Recall that a variable holds exactly one thing. So if you have
int x = 5;
you can visualize this variable definition as follows:
This is a simple story for primitive types.
What if the type is an array type or a class? In this case, the box for the variable contains a reference to an object. These types are therefore called reference types.
You can think of a reference as an arrow pointing to the object. For example, if you
int[] x = {6, 2, 5};
then you have:
The reference points to the entire object, not any particular part of it.
The =
assignment operator copies the contents of one box into another. For primitive types, this is easy to understand. If
you
int x = 4;
int y = x;
then the value 4 is copied from x
's box into y
's:
With references, an assignment still copies the contents of the box, but now that means copying the reference, not the object on the other end. For example, if you
int[] a = {1, 2, 3};
int[] b = a;
then you get two references to the same array:
a
and b
are said to be aliases because they refer to the same array. A consequence of this is that, if you modify the array to which a
refers, you are also modify the array to which b
refers (because they're the same one).
Methods cannot modify their arguments, but references provide a loophole.
If a method has a primitive parameter, like
static void f(int x) {
x = 100;
}
then doing something like
int y = 7;
f(y);
has no effect on y
. The parameter x
gets a copy of the value stored in y
, so no matter what happens to x
, y
is unchanged.
If, on the other hand, a method has a parameter of an object type, like
static void g(int[] a) {
a[0] = 3;
}
and then you
int[] b = new int[5];
g(b);
then the array to which b
refers has been modified, because the parameter a
is an alias for the argument b
.
References make the notion of equality more complex.
The ==
operator asks whether two boxes contain the same thing: the same primitive value or references to the same object. For example, suppose you have defined a Point
class
class Point {
int x;
int y;
}
and then:
Point p = new Point();
p.x = 3;
p.y = 4;
Point q = p;
Point r = new Point();
r.x = 3;
r.y = 4;
Memory now looks like this:
then p == q
but p != r
because p
and r
do not refer to the same object.
We are often interested in whether two objects have the same contents. In this sense, p
and r
are equal. For many built-in classes (including String), the expression p.equals(q)
is true if p
and q
have the same contents. This is the preferred way to compare objects.
You can also write equals
methods for your own classes.
The literal value null
is a reference to nothing. If you
String s = null;
then s
doesn't refer to anything. I draw this as a line connected to a dot rather than to an arrowhead:
Trying to do anything with s
, such as asking for s.length()
, results in a NullPointerException
because there is no String object on the other end.
null
is the default value for object types.
Every object takes up memory. As soon as an object is unreachable (that is, there are no more references to it), the Java system reclaims that memory through a process called garbage collection.
This feature of Java is in contrast to C, where it is the programmer's responsibility to free objects when they are no longer needed. A C programmer who does not manage memory with extreme care can end up with a memory leak, where unreachable objects take up more and more memory until the system crashes, or a dangling pointer, where a reference points to a location in memory that does not contain the intended object. Either of these bugs can be exceedingly difficult to detect and fix.
- Sedgewick and Wayne, Introduction to Programming in Java, Section 3.1
- Horstmann, Core Java, Volume I: Fundamentals, 11th Edition, Section 4.2.1 and 4.3.6
- Draper, The Worst Mistake of Computer Science
- ⭐ What is the term for two references to the same object?
- ⭐ Which types use references?
- ⭐ Is it possible to create a dangling pointer in Java?
- ⭐⭐ Is it possible to create a memory leak in Java?
- ⭐⭐ If you define
String s = null;
, iss == null
? - ⭐⭐ Suppose you execute the following code (assuming Point is a class with a well-defined
equals
method):Point a = new Point(); a.x = 1; a.y = 2; Point b = new Point(); b.x = 1; b.y = 2; Point c = b;
- Is
a == b
? - Is
a.equals(b)
? - Is
b == c
? - Is
b.equals(c)
?
- Is
- ⭐⭐ If you
then what is the value of
int[] a = {1, 2, 3}; int[] b = a; a[1] = 5;
b[1]
? - ⭐⭐ Suppose you have defined
and then you:
static void alter1(int[] a) { a[0] = 5; }
What are the elements ofint[] b = {1, 2, 3, 4}; alter1(b);
b
afterward? - ⭐⭐ Suppose you have defined
and then you:
static void alter2(int[] a) { a = new int[] {5, 6, 7, 8}; }
What are the elements ofint[] b = {1, 2, 3, 4}; alter2(b);
b
afterward? - ⭐⭐⭐ What's the difference between a reference and a pointer?
- ⭐⭐⭐ How can you tell if two arrays
a
andb
have the same contents? - ⭐⭐⭐ What type is the literal value
null
?
- Aliases.
- All non-primitive types, that is, array types and types defined by classes and interfaces.
- No. The only way to get a reference is to create a new object using new
new
(or special syntax for arrays and Strings). The only way to get rid of an object is to set the last reference to it to refer tonull
or another object. - Not strictly speaking; you can't have an object that is truly unreachable. If, however, you stored extra references to objects in an array and then changed the original references, the objects would still be reachable and they would therefore not be garbage collected.
- Yes.
==
compares the contents of the box fors
to the literal valuenull
and determines that they're the same. It doesn't cause a NullPointerException because this check doesn't look at what's on the other end of the reference. -
- No (
a
andb
refer to different objects) - Yes
- Yes (
b
andc
refer to the same object) - Yes
- No (
- 5, because
a
andb
are aliases. There is only one array, which can be accessed via either reference. - 5, 2, 3, 4. Since
a
andb
refer to the same array, alterations to the array on the other end of the reference are visible from either variable. - 1, 2, 3, 4. Making the local variable
a
point to a new array does not affect the external variableb
. - Some sources will use these two words interchangeably. Indeed, a pointer (which is the address of a location in memory) is one way for a compiler to implement a reference. Other sources will insist that it's only a pointer if you have access to the address and can perform arithmetic on it to, for example, find the next consecutive location in memory. This is what people mean when they say, "C has pointers but Java does not." The designers of Java felt that the few additional shortcuts you can take with "true" pointers aren't worth the subtle and frustrating bugs that can result from messing with addresses directly.
a == b
only checks if the two references are to the same array anda.equals(b)
is not defined for arrays. One solution is to iterate through the elements using a (possibly nested) loop. Another is to usejava.util.Arrays.equals(a, b)
(for one-dimensional arrays) orjava.util.Arrays.deepEquals(a, b)
(for multidimensional arrays).null
has its own type, which has no name. It can be cast to any other object type. While many programming languages have such a special value, some computer scientists consider it a bad idea.