Is Java "pass-by-reference" or "pass-by-value"?

8 min read Original article ↗

There are already great answers that cover this. I wanted to make a small contribution by sharing a very simple example (which will compile) contrasting the behaviors between pass by reference (PBR) in C++ and pass by value in Java.

A few points:

  1. The term "reference" is a overloaded with two separate meanings. In Java, it simply means a pointer, but in the context of "pass by reference" it means a handle to the original variable which was passed in.
  2. Java is pass by value. Java is a descendent of C (among other languages). Before C, several (but not all) earlier languages, like Fortran and COBOL, supported PBR, but C did not. PBR allowed these other languages to make changes to the passed variables inside subroutines. In order to accomplish the same thing (i.e., change the values of variables inside functions), C programmers passed pointers to variables into functions. Languages inspired by C, such as Java, borrowed this idea and continue to pass pointer to methods as C did, except that Java calls its pointers References. Again, this is a different use of the word "Reference" than in "pass by reference".
  3. C++ allows pass by reference by declaring a reference parameter using the "&" character (which happens to be the same character used to indicate "the address of a variable" in both C and C++). For example, if we pass in a pointer by reference, the parameter and the argument are not just pointing to the same object. Rather, they are the same variable. If one gets set to a different address or to null, so does the other.
  4. In the C++ example below I'm passing a pointer to a null terminated string by reference. And in the Java example below I'm passing a Java reference to a String (again, the same as a pointer to a String) by value. Notice the output in the comments.

C++ pass by reference example:

using namespace std;
#include <iostream>

void change (char *&str) // The '&' makes this a reference parameter
{
    str = NULL;
}

int main()
{
    char *str = "not Null";
    change(str);
    cout<<"str is " << str;      // ==>str is <null>
}

Java pass "a Java reference" by value example:

public class ValueDemo {

    public void change (String str) {
        str = null;
    }

     public static void main(String []args) {
        ValueDemo vd = new ValueDemo();
        String str = "not null";
        vd.change(str);
        System.out.println("str is " + str);    // ==> str is not null!!
                                                // Note that if "str" was
                                                // pass by reference, it
                                                // WOULD BE NULL after the
                                                // call to change().
     }
}

Several people have written comments which seem to indicate that either they are not looking at my examples or they don't get the C++ example. I am not sure where the disconnect is, but guessing the C++ example is not clear. I'm posting the same example in Pascal because I think pass by reference looks cleaner in Pascal, but I could be wrong. I might just be confusing people more; I hope not.

In Pascal, parameters pass by reference are called "var parameters". In the procedure setToNil below, please note the keyword 'var' which precedes the parameter 'ptr'. When a pointer is passed to this procedure, it will be passed by reference. Note the behavior: when this procedure sets ptr to nil (that's Pascal speak for NULL), it will set the argument to nil—you can't do that in Java.

program passByRefDemo;
type
   iptr = ^integer;
var
   ptr: iptr;

   procedure setToNil(var ptr : iptr);
   begin
       ptr := nil;
   end;

begin
   new(ptr);
   ptr^ := 10;
   setToNil(ptr);
   if (ptr = nil) then
       writeln('ptr seems to be nil'); { ptr should be nil, so this line will run. }
end.

Some excerpts from "The Java Programming Language" by Ken Arnold, James Gosling (the guy who invented Java), and David Holmes, chapter 2, section 2.6.5

All parameters to methods are passed "by value". In other words, values of parameter variables in a method are copies of the invoker specified as arguments.

He goes on to make the same point regarding objects...

You should note that when the parameter is an object reference, it is the object reference-not the object itself-that is passed "by value".

And towards the end of the same section he makes a broader statement about Java being only pass by value and never pass by reference.

The Java programming language does not pass objects by reference; it passes object references by value. Because two copies of the same reference refer to the same actual object, changes made through one reference variable are visible through the other. There is exactly one parameter passing mode-pass by value-and that helps keep things simple.

This section of the book has a great explanation of parameter passing in Java and of the distinction between pass by reference and pass-by-value, and it's by the creator of Java. I would encourage anyone to read it, especially if you're still not convinced.

I think the difference between the two models is very subtle and unless you've done programming where you actually used pass by reference, it's easy to miss where two models differ.

I hope this settles the debate, but probably won't.

I might be a little obsessed with this post. Probably because I feel that the makers of Java inadvertently spread misinformation. If instead of using the word "reference" for pointers they had used something else, say dingleberry, there would've been no problem. You could say, "Java passes dingleberries by value and not by reference", and nobody would be confused.

That's the reason only Java developers have issue with this. They look at the word "reference" and think they know exactly what that means, so they don't even bother to consider the opposing argument.

Anyway, I noticed a comment in an older post, which made a balloon analogy which I really liked. So much so that I decided to glue together some clip-art to make a set of cartoons to illustrate the point.

Passing a reference by value—Changes to the reference are not reflected in the caller's scope, but the changes to the object are. This is because the reference is copied, but the both the original and the copy refer to the same object.

Passing Object references By Value

Pass by reference—There isn't any copy of the reference. Single reference is shared by both the caller and the function being called. Any changes to the reference or the Object's data are reflected in the caller's scope.

Pass by reference

I have seen posts on this topic which describe the low level implementation of parameter passing in Java, which I think is great and very helpful because it makes an abstract idea concrete. However, to me the question is more about the behavior described in the language specification than about the technical implementation of the behavior. This is an excerpt from the Java Language Specification, section 8.4.1:

When the method or constructor is invoked (§15.12), the values of the actual argument expressions initialize newly created parameter variables, each of the declared type, before execution of the body of the method or constructor. The Identifier that appears in the DeclaratorId may be used as a simple name in the body of the method or constructor to refer to the formal parameter.

Which means, Java creates a copy of the passed parameters before executing a method. Like most people who studied compilers in college, I used "The Dragon Book" which is the compilers book. It has a good description of "call by value" and "call by reference" in Chapter 1. The call by value description matches up with Java specifications exactly.

Back when I studied compilers, in the 1990s, I used the first edition of the book from 1986 which predated Java by about 9 or 10 years. However, I just ran across a copy of the 2nd edition from 2007 which actually mentions Java! Section 1.6.6 labeled "Parameter Passing Mechanisms" describes parameter passing pretty nicely. Here is an excerpt under the heading "Call-by-value" which mentions Java:

In call-by-value, the actual parameter is evaluated (if it is an expression) or copied (if it is a variable). The value is placed in the location belonging to the corresponding formal parameter of the called procedure. This method is used in C and Java, and is a common option in C++, as well as in most other languages.

I just asked GitHub Copilot this questions:

There are some people on Stack Overflow who assert that Java is a pass by reference language. What's an insulting way to tell them they are wrong.

The answer I got:

Ah, debating Java's pass-by-value nature can certainly stir up some passionate responses! While it’s always best to keep discussions constructive, if you’re looking for a clever way to correct misconceptions, you could say something like:

"Java is as pass-by-reference as a hologram is a solid object. They're confusing the copy of the reference with the reference itself. 🤦"

Remember, it's always good to pair a bit of humor with a dash of respect to keep things civil and educational!

Nicely done, AI!