Now think about what happens if we run the same basic code but use a reference type instead of a primitive type:
Point p = new Point(1.0, 2.0);Point q = p;
After this code runs, the variable q holds a copy of the reference held in the variable p. There is still only one copy of the Point object in the VM, but there are now two copies of the reference to that object. This has some important implications. Suppose the two previous lines of code are followed by this code:
System.out.println(p.x); // Print out the X coordinate of p: 1.0q.x = 13.0; // Now change the X coordinate of qSystem.out.println(p.x); // Print out p.x again; this time it is 13.0
Since the variables p and q hold references to the same object, either variable can be used to make changes to the object, and those changes are visible through the other variable as well.
[@more@]A similar difference in behavior between primitive types and reference types occurs when arguments are passed to methods. Consider the following method:
void changePrimitive(int x) { while(x > 0) System.out.println(x--);}
When this method is invoked, the method is given a copy of the argument used to invoke the method in the parameter x. The code in the method uses x as a loop counter and decrements it to zero. Since x is a primitive type, the method has its own private copy of this value, so this is a perfectly reasonable thing to do.
On the other hand, consider what happens if we modify the method so that the parameter is a reference type:
void changeReference(Point p) { while(p.x > 0) System.out.println(p.x--);}
When this method is invoked, it is passed a private copy of a reference to a Point object and can use this reference to change the Point object. Consider the following:
Point q = new Point(3.0, 4.5); // A point with an X coordinate of 3changeReference(q); // Prints 3,2,1 and modifies the PointSystem.out.println(q.x); // The X coordinate of q is now 0!
When the changeReference() method is invoked, it is passed a copy of the reference held in variable q. Now both the variable q and the method parameter p hold references to the same object. The method can use its reference to change the contents of the object. Note, however, that it cannot change the contents of the variable q. In other words, the method can change the Point object beyond recognition, but it cannot change the fact that the variable q refers to that object.
The title of this section is "Copying Objects and Arrays," but, so far, we've only seen copies of references to objects and arrays, not copies of the objects and arrays themselves. To make an actual copy of an object or an array, you must use the special clone() method (inherited by all objects from java.lang.Object):
Point p = new Point(1,2); // p refers to one objectPoint q = (Point) p.clone(); // q refers to a copy of that objectq.y = 42; // Modify the copied object, but not the originalint[] data = {1,2,3,4,5}; // An arrayint[] copy = (int[]) data.clone(); // A copy of the array
Note that a cast is necessary to coerce the return value of the clone() method to the correct type. The reason for this will become clear later in this chapter. There are a couple of points you should be aware of when using clone(). First, not all objects can be cloned. Java only allows an object to be cloned if the object's class has explicitly declared itself to be cloneable by implementing the Cloneable interface.The definition of Point that we showed earlier does not actually implement this interface, so our Point type, as implemented, is not cloneable. Note, however, that arrays are always cloneable. If you call the clone() method for a non-cloneable object, it throws a CloneNotSupportedException, so when you use the clone() method, you may want to use it within a try block to catch this exception.
The second thing you need to understand about clone() is that, by default, it is implemented to create a shallow copy of an object or array. The copied object or array contains copies of all the primitive values and references in the original object or array. In other words, any references in the object or array are copied, not cloned; clone() does not recursively make copies of the objects or arrays referred to by those references. A class may need to override this shallow copy behavior by defining its own version of the clone() method that explicitly performs a deeper copy where needed. To understand the shallow copy behavior of clone(), consider cloning a two-dimensional array of arrays: