No Java variable represents an object.
String foo; // NOT AN OBJECT
Neither does any Java array contain objects.
String bar[] = new String[100]; // No member is an object.
If you mistakenly think of variables as objects, the actual behavior of the Java language will surprise you.
For Java variables which have a primitive type (such as int
or float
) the variable holds a copy of the value. All copies of a primitive value are indistinguishable; i.e. there is only one int
value for the number one. Primitive values are not objects and they do not behave like objects.
For Java variables which have a reference type (either a class or an array type) the variable holds a reference. All copies of a reference are indistinguishable. References may point to objects, or they may be null
which means that they point to no object. However, they are not objects and they don't behave like objects.
Variables are not objects in either case, and they don't contain objects in either case. They may contain references to objects, but that is saying something different.
The examples that follow use this class, which represents a point in 2D space.
public final class MutableLocation {
public int x;
public int y;
public MutableLocation(int x, int y) {
this.x = x;
this.y = y;
}
public boolean equals(Object other) {
if (!(other instanceof MutableLocation) {
return false;
}
MutableLocation that = (MutableLocation) other;
return this.x == that.x && this.y == that.y;
}
}
An instance of this class is an object that has two fields x
and y
which have the type int
.
We can have many instances of the MutableLocation
class. Some will represent the same locations in 2D space; i.e. the respective values of x
and y
will match. Others will represent different locations.
MutableLocation here = new MutableLocation(1, 2);
MutableLocation there = here;
MutableLocation elsewhere = new MutableLocation(1, 2);
In the above, we have declared three variables here
, there
and elsewhere
that can hold references to MutableLocation
objects.
If you (incorrectly) think of these variables as being objects, then you are likely to misread the statements as saying:
- Copy the location "[1, 2]" to
here
- Copy the location "[1, 2]" to
there
- Copy the location "[1, 2]" to
elsewhere
From that, you are likely to infer we have three independent objects in the three variables. In fact there are only two objects created by the above. The variables here
and there
actually refer to the same object.
We can demonstrate this. Assuming the variable declarations as above:
System.out.println("BEFORE: here.x is " + here.x + ", there.x is " + there.x +
"elsewhere.x is " + elsewhere.x);
here.x = 42;
System.out.println("AFTER: here.x is " + here.x + ", there.x is " + there.x +
"elsewhere.x is " + elsewhere.x);
This will output the following:
BEFORE: here.x is 1, there.x is 1, elsewhere.x is 1
AFTER: here.x is 42, there.x is 42, elsewhere.x is 1
We assigned a new value to here.x
and it changed the value that we see via there.x
. They are referring to the same object. But the value that we see via elsewhere.x
has not changed, so elsewhere
must refer to a different object.
If a variable was an object, then the assignment here.x = 42
would not change there.x
.
Applying the equality (==
) operator to reference values tests if the values refer to the same object. It does not test whether two (different) objects are "equal" in the intuitive sense.
MutableLocation here = new MutableLocation(1, 2);
MutableLocation there = here;
MutableLocation elsewhere = new MutableLocation(1, 2);
if (here == there) {
System.out.println("here is there");
}
if (here == elsewhere) {
System.out.println("here is elsewhere");
}
This will print "here is there", but it won't print "here is elsewhere". (The references in here
and elsewhere
are for two distinct objects.)
By contrast, if we call the equals(Object)
method that we implemented above, we are going to test if two MutableLocation
instances have an equal location.
if (here.equals(there)) {
System.out.println("here equals there");
}
if (here.equals(elsewhere)) {
System.out.println("here equals elsewhere");
}
This will print both messages. In particular, here.equals(elsewhere)
returns true
because the semantic criteria we chose for equality of two MutableLocation
objects has been satisfied.
Java method calls use pass by value1 to pass arguments and return a result.
When you pass a reference value to a method, you're actually passing a reference to an object by value, which means that it is creating a copy of the object reference.
As long as both object references are still pointing to the same object, you can modify that object from either reference, and this is what causes confusion for some.
However, you are not passing an object by reference2. The distinction is that if the object reference copy is modified to point to another object, the original object reference will still point to the original object.
void f(MutableLocation foo) {
foo = new MutableLocation(3, 4); // Point local foo at a different object.
}
void g() {
MutableLocation foo = MutableLocation(1, 2);
f(foo);
System.out.println("foo.x is " + foo.x); // Prints "foo.x is 1".
}
Neither are you passing a copy of the object.
void f(MutableLocation foo) {
foo.x = 42;
}
void g() {
MutableLocation foo = new MutableLocation(0, 0);
f(foo);
System.out.println("foo.x is " + foo.x); // Prints "foo.x is 42"
}
1 - In languages like Python and Ruby, the term "pass by sharing" is preferred for "pass by value" of an object / reference.
2 - The term "pass by reference" or "call by reference" has a very specific meaning in programming language terminology. In effect, it means that you pass the address of a variable or an array element, so that when the called method assigns a new value to the formal argument, it changes the value in the original variable. Java does not support this. For a more fulsome description of different mechanisms for passing parameters, please refer to https://en.wikipedia.org/wiki/Evaluation_strategy.