The non-primitive data types in Java are objects and arrays. These non-primitive types are often called "reference types" because they are handled "by reference"--in other words, the address of the object or array is stored in a variable, passed to methods, and so on. By comparison, primitive types are handled "by value"--the actual primitive values are stored in variables and passed to methods.
In C, you can manipulate a value by reference by taking its address with the & operator, and you can "dereference" an address with the * and -> operators. These operators do not exist in Java: primitive types are always passed by value; arrays and objects are always passed by reference.
Because objects are passed by reference, two different variables may refer to the same object:
Button p, q; p = new Button(); // p refers to a Button object. q = p; // q refers to the same Button. p.setLabel("Ok"); // A change to the object through p... String s = q.getLabel(); // ...is also visible through q. // s now contains "Ok."
This is not true of primitive types, however:
int i = 3; // i contains the value 3. int j = i; // j contains a copy of the value in i. i = 2; // Changing i doesn't change j. // Now, i == 2 and j == 3.
The statement that Java manipulates objects "by reference" causes confusion for some programmers, because there are several different meanings of "by reference" in common use. Regardless of what we call it, it is important to understand what Java does. Java works with references to objects. A Java variable holds only a reference to an object, not the object itself. When an object is passed to a method, only a reference to the object is actually passed, not the entire object. It is in this sense that Java manipulates objects "by reference."
Some people use the term "pass by reference" to mean that a reference to a variable is passed to a method. Java does not do this. For example, it is not possible to write a working swap() function like the following in Java:
public void swap(Object a, Object b) { Object temp = a; a = b; b = temp; }
The method parameters a and b contain references to objects, not addresses of variables. Thus, while this swap() function does compile and run, it has no effect except on its own local variables and arguments.
To solve this terminology problem, perhaps we should say that Java manipulates objects "by reference," but it passes object references to methods "by value."
Because reference types are not passed by value, assigning one object to another in Java does not copy the value of the object. It merely assigns a reference to the object. Consider the following code:
Button a = new Button("Okay"); Button b = new Button("Cancel"); a = b;
After these lines are executed, the variable a contains a reference to the object that b refers to. The object that a used to refer to is lost.
To copy the data of one object into another object, use the clone() method:
Vector b = new Vector; c = b.clone();
After these lines run, the variable c refers to an object that is a duplicate of the object referred to by b. Note that not all types support the clone() method. Only classes that implement the Cloneable interface may be cloned. For more information on cloning objects, look up java.lang.Cloneable and java.lang.Object.clone() in Chapter 25, The java.lang Package.
Arrays are also reference types, and assigning an array simply copies a reference to the array. To actually copy the values stored in an array, you must assign each of the values individually or use the System.arraycopy() method.
Another implication of passing objects by reference is that the == operator tests whether two variables refer to the same object, not whether two objects contain the same values. To actually test whether two separate objects are the same, you must use a specially written method for that object type (just as you might use strcmp() to compare C strings for equality). In Java, a number of classes define an equals() method that you can use to perform this test.
The referencing and dereferencing of objects is handled for you automatically by Java. Java does not allow you to manipulate pointers or memory addresses of any kind:
There are two reasons for these restrictions:
To a C programmer, the lack of pointers and pointer arithmetic may seem an odious restriction in Java. But once you get used to the Java object-oriented programming model, it no longer seems like a serious restriction at all. The lack of pointers does mean that you probably can't do things like write UNIX device drivers in Java (at least not without using native methods written in C). But big deal--most of us never have to do this kind of low-level programming anyway.
The default value for variables of all reference types is null. null is a reserved value that indicates "an absence of reference"--i.e., that a variable does not refer to any object or array.
In Java, null is a reserved keyword, unlike NULL in C, where it is just a constant defined to be 0. null is an exception to the strong typing rules of Java--it may be assigned to any variable of reference type (i.e., any variable which has a class, interface, or array as its type).
null can't be cast to any primitive type, including integral types and boolean. It shouldn't be considered equal to zero (although it may be implemented this way).
The distinction between primitive types passed by value, and objects and arrays passed by reference is a crucial one in Java. Be sure you understand the following: