Is Java "pass-by-reference" or "pass-by-value"?
Java is always pass-by-value. Unfortunately, when we deal with objects we are really dealing with object-handles called references which are passed-by-value as well. This terminology and semantics easily confuse many beginners.
It goes like this:
public static void main(String[] args) {
Dog aDog = new Dog("Max");
Dog oldDog = aDog;
// we pass the object to foo
foo(aDog);
// aDog variable is still pointing to the "Max" dog when foo(...) returns
aDog.getName().equals("Max"); // true
aDog.getName().equals("Fifi"); // false
aDog == oldDog; // true
}
public static void foo(Dog d) {
d.getName().equals("Max"); // true
// change d inside of foo() to point to a new Dog instance "Fifi"
d = new Dog("Fifi");
d.getName().equals("Fifi"); // true
}
In the example above aDog.getName()
will still return "Max"
. The value aDog
within main
is not changed in the function foo
with the Dog
"Fifi"
as the object reference is passed by value. If it were passed by reference, then the aDog.getName()
in main
would return "Fifi"
after the call to foo
.
Likewise:
public static void main(String[] args) {
Dog aDog = new Dog("Max");
Dog oldDog = aDog;
foo(aDog);
// when foo(...) returns, the name of the dog has been changed to "Fifi"
aDog.getName().equals("Fifi"); // true
// but it is still the same dog:
aDog == oldDog; // true
}
public static void foo(Dog d) {
d.getName().equals("Max"); // true
// this changes the name of d to be "Fifi"
d.setName("Fifi");
}
In the above example, Fifi
is the dog's name after call to foo(aDog)
because the object's name was set inside of foo(...)
. Any operations that foo
performs on d
are such that, for all practical purposes, they are performed on aDog
, but it is not possible to change the value of the variable aDog
itself.
For more information on pass by reference and pass by value, consult the following SO answer: https://stackoverflow.com/a/430958/6005228. This explains more thoroughly the semantics and history behind the two and also explains why Java and many other modern languages appear to do both in certain cases.
I just noticed you referenced my article.
The Java Spec says that everything in Java is pass-by-value. There is no such thing as "pass-by-reference" in Java.
The key to understanding this is that something like
Dog myDog;
is not a Dog; it's actually a pointer to a Dog. The use of the term "reference" in Java is very misleading and is what causes most of the confusion here. What they call "references" act/feel more like what we'd call "pointers" in most other languages.
What that means, is when you have
Dog myDog = new Dog("Rover");
foo(myDog);
you're essentially passing the address of the created Dog
object to the foo
method.
(I say essentially because Java pointers/references aren't direct addresses, but it's easiest to think of them that way.)
Suppose the Dog
object resides at memory address 42. This means we pass 42 to the method.
if the Method were defined as
public void foo(Dog someDog) {
someDog.setName("Max"); // AAA
someDog = new Dog("Fifi"); // BBB
someDog.setName("Rowlf"); // CCC
}
let's look at what's happening.
- the parameter
someDog
is set to the value 42 - at line "AAA"
-
someDog
is followed to theDog
it points to (theDog
object at address 42) - that
Dog
(the one at address 42) is asked to change his name to Max
-
- at line "BBB"
- a new
Dog
is created. Let's say he's at address 74 - we assign the parameter
someDog
to 74
- a new
- at line "CCC"
- someDog is followed to the
Dog
it points to (theDog
object at address 74) - that
Dog
(the one at address 74) is asked to change his name to Rowlf
- someDog is followed to the
- then, we return
Now let's think about what happens outside the method:
Did myDog
change?
There's the key.
Keeping in mind that myDog
is a pointer, and not an actual Dog
, the answer is NO. myDog
still has the value 42; it's still pointing to the original Dog
(but note that because of line "AAA", its name is now "Max" - still the same Dog; myDog
's value has not changed.)
It's perfectly valid to follow an address and change what's at the end of it; that does not change the variable, however.
Java works exactly like C. You can assign a pointer, pass the pointer to a method, follow the pointer in the method and change the data that was pointed to. However, the caller will not see any changes you make to where that pointer points. (In a language with pass-by-reference semantics, the method function can change the pointer and the caller will see that change.)
In C++, Ada, Pascal and other languages that support pass-by-reference, you can actually change the variable that was passed.
If Java had pass-by-reference semantics, the foo
method we defined above would have changed where myDog
was pointing when it assigned someDog
on line BBB.
Think of reference parameters as being aliases for the variable passed in. When that alias is assigned, so is the variable that was passed in.
Java always passes arguments by value, NOT by reference.
Let me explain this through an example:
public class Main {
public static void main(String[] args) {
Foo f = new Foo("f");
changeReference(f); // It won't change the reference!
modifyReference(f); // It will modify the object that the reference variable "f" refers to!
}
public static void changeReference(Foo a) {
Foo b = new Foo("b");
a = b;
}
public static void modifyReference(Foo c) {
c.setAttribute("c");
}
}
I will explain this in steps:
-
Declaring a reference named
f
of typeFoo
and assign it a new object of typeFoo
with an attribute"f"
.Foo f = new Foo("f");
-
From the method side, a reference of type
Foo
with a namea
is declared and it's initially assignednull
.public static void changeReference(Foo a)
-
As you call the method
changeReference
, the referencea
will be assigned the object which is passed as an argument.changeReference(f);
-
Declaring a reference named
b
of typeFoo
and assign it a new object of typeFoo
with an attribute"b"
.Foo b = new Foo("b");
-
a = b
makes a new assignment to the referencea
, notf
, of the object whose attribute is"b"
. -
As you call
modifyReference(Foo c)
method, a referencec
is created and assigned the object with attribute"f"
. -
c.setAttribute("c");
will change the attribute of the object that referencec
points to it, and it's the same object that referencef
points to it.
I hope you understand now how passing objects as arguments works in Java :)
Java is always pass by value, with no exceptions, ever.
So how is it that anyone can be at all confused by this, and believe that Java is pass by reference, or think they have an example of Java acting as pass by reference? The key point is that Java never provides direct access to the values of objects themselves, in any circumstances. The only access to objects is through a reference to that object. Because Java objects are always accessed through a reference, rather than directly, it is common to talk about fields and variables and method arguments as being objects, when pedantically they are only references to objects. The confusion stems from this (strictly speaking, incorrect) change in nomenclature.
So, when calling a method
- For primitive arguments (
int
,long
, etc.), the pass by value is the actual value of the primitive (for example, 3). - For objects, the pass by value is the value of the reference to the object.
So if you have doSomething(foo)
and public void doSomething(Foo foo) { .. }
the two Foos have copied references that point to the same objects.
Naturally, passing by value a reference to an object looks very much like (and is indistinguishable in practice from) passing an object by reference.