Java in a Nutshell

Previous Chapter 2
How Java Differs from C
Next
 

2.7 Reference Data Types

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.

Terminology: Pass by Reference

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."

Copying Objects

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.

Checking Objects for Equality

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.

Java Has No Pointers

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:

  • It does not allow you to cast object or array references into integers or vice-versa.

  • It does not allow you to do pointer arithmetic.

  • It does not allow you to compute the size in bytes of any primitive type or object.

There are two reasons for these restrictions:

  • Pointers are a notorious source of bugs. Eliminating them simplifies the language and eliminates many potential bugs.

  • Pointers and pointer arithmetic could be used to sidestep Java's run-time checks and security mechanisms. Removing pointers allows Java to provide the security guarantees that it does.

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.

null

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).

Reference Type Summary

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:

  • All objects and arrays are handled by reference in Java. (Those object references are passed-by-value to methods, however.)

  • The = and == operators assign and test references to objects. Use clone() and equals() to actually copy or test the objects themselves.

  • The necessary referencing and dereferencing of objects and arrays is handled automatically by Java.

  • A reference type can never be cast to a primitive type.

  • A primitive type can never be cast to a reference type.

  • There is no pointer arithmetic in Java.

  • There is no sizeof operator in Java.

  • null is a special value that means "no object" or indicates an absence of reference.


Previous Home Next
Primitive Data Types Book Index Objects

Java in a Nutshell Java Language Reference Java AWT Java Fundamental Classes Exploring Java