Chapter 4. Reference Types
Reference types hold references to objects and provide a means to access those objects
stored somewhere in memory. The memory locations are irrelevant to programmers. All
reference types are a subclass of type java.lang.Object
.
Table 4-1 lists the five Java reference types.
Reference types |
Brief description |
Provides a way to associate metadata (data about data) with program elements. | |
Provides a fixed-size data structure that stores data elements of the same type. | |
Designed to provide inheritance, polymorphism, and encapsulation. Usually models something in the real world and consists of a set of values that holds data and a set of methods that operates on the data. | |
A reference for a set of objects that represents a related set of choices. | |
Provides a public API and is “implemented” by Java classes. |
Comparing Reference Types to Primitive Types
There are two categories of types in Java:reference types and primitive types. Table 4-2 shows some of the key comparisons between them. See Chapter 3 for more details.
Reference types |
Primitive types |
Unlimited number of reference types, as they are user-defined. |
Consists of |
Memory location stores a reference to the data. |
Memory location stores actual data held by the primitive type. |
When a reference type is assigned to another reference type, both will point to the same object. |
When a value of a primitive is assigned to another variable of the same type, a copy is made. |
When an object is passed into a method, the called method can change the contents of the object passed to it but not the address of the object. |
When a primitive is passed into a method, only a copy of the primitive is passed. The called method does not have access to the original primitive value and therefore cannot change it. The called method can change the copied value. |
Default Values
Instance and Local Variable Objects
Instance variable objects (objects declared at the class level) have a
default value of null. null
references
nothing.
Local variable objects (objects declared within a method) do not have a
default value, not even a value of null
. Always
initialize local objects because they are not given a default value. Checking an
uninitialized local variable object for a value (including a value of null
) will result in a compile-time error.
Although object references with a value of null
do not refer to any object on the heap, objects set to null
can be referenced in code without receiving
compile-time or runtime errors.
Date dateOfParty = null; // This is ok if (dateOfParty == null) { ... }
Invoking a method on a reference variable that is null
or using the dot operator on the object will result in a java.lang.NullPointerException
.
String theme = null; // This will result in an exception // if theme is still set to null if (theme.getLength( ) > MAX_LENGTH) { ... }
Arrays
Arrays are always given a default value whether they are declared as instance variables or local
variables. Arrays that are declared but not initialized are given a default value of
null
.
In the code below, the gameList
array is
initialized but not the individual values, meaning that the object references will have
a value of null
. Objects have to be added to the
array.
// This declared array named gameList // is initialized to null by default Game[] gameList; // This array has been initialized but // the object references are still null // since the array contains no objects gameList = new Game[10]; // Add a Game object to the list // Now the list has one object gameList[0] = new Game( );
Conversion of Reference Types
An object can be converted to the type of its superclass (widening) or any of its subclasses (narrowing).
The compiler checks conversions at compile time and the JVM checks conversions at runtime.
Widening Conversions
Narrowing Conversions
Narrowing converts a more general type into a more specific type.
Narrowing is a conversion of a superclass to a subclass.
An explicit cast is required. To cast an object to another object, place the type of object you are casting to in parentheses immediately before the object you are casting.
Object a = new Object; String b = (String)a; // Cast to String
Illegitimate narrowing results in a
ClassCastException
.Narrowing may result in a loss of data/precision.
Objects cannot be converted to an unrelated type—that is, a type other than one of
its subclasses or superclasses. Doing so will generate an inconvertible types
error at compile time. The following is an example of a
conversion that will result in a compile-time error due to inconvertible types
:
Object c = "balloons"; int d = (int) c; // compile-time error
Converting Between Primitives and Reference Types
The automatic conversion of primitive types to reference types and vice versa is called autoboxing and unboxing, respectively. For more information, see Chapter 3.
Passing Reference Types into Methods
When an object is passed into a method as a variable:
A copy of the reference variable is passed, not the actual object.
The caller and the called methods have identical copies of the reference.
The caller will also see any changes the called method makes to the object. Passing a copy of the object to the called method will prevent it from making changes to the original object.
The called method cannot change the address of the object, but it can change the contents of the object.
The following example illustrates passing reference types and primitive types into methods and the effects on those types when changed by the called method:
void roomSetup( ) { // Reference passing Table table = new Table( ); table.setLength(72); // Length will be changed modTableLength(table); // Primitive passing // Value of chairs not changed int chairs = 8; modChairCount(chairs); } void modTableLength(Table t) { t.setLength(36); } void modChairCount(int 1) { 1 = 10; } }
Comparing Reference Types
Using the Equality Operators ==
The != and == equality operators:!= and
Are used to compare the memory locations of two objects. If the memory addresses of the objects being compared are the same, the objects are considered equal.
Are not used to compare the contents of the two objects.
In the following example, guest1
and guest2
have the same memory address, so the statement
"They are equal"
will be output.
Guest guest1 = new Guest("name"); Guest guest2 = guest1; if (guest1 == guest2) System.out.println("They are equal")
In the following example, the memory addresses are not equal, so the statement
"They are not equal"
will be output.
Guest guest3 = new Guest("name"); Guest guest4 = new Guest("name"); if (guest3 == guest4) System.out.println("They are equal.") else System.out.println("They are not equal")
Using the equals( ) method
To compare the contents of two class objects, the equals(
)
method from class Object
can be used
or overridden.
Tip
By default, the equals( )
method uses only
the == operator for comparisons. This method has to be overridden to really be
useful.
For example, if you want to compare values contained in two instances of the same
class, you should use a programmerdefined equals( )
method.
Comparing strings
There are two ways to check whether strings are equal in Java, but the definition of “equal” for each of them
is different. Typically, if the goal is to compare character sequences contained in
two strings, the equals( )
method should be
used.
The
equals( )
method compares two strings, character by character, to determine equality. This is not the default implementation of theequals( )
method provided by theclass Object
. This is the overridden implementation provided byclass String
.The == operator checks to see whether two object references refer to the same instance of an object.
Below is a program that shows how strings are evaluated using the equals( )
method and the == operator. For more information
on how strings are evaluated, see the “String Literals” section in Chapter 2.
class MyComparisons { // Add string to pool String first = "chairs"; // Use string from pool String second = "chairs"; // Create a new string String third = new String ("chairs"); void myMethod( ) { // Contrary to popular belief, this evaluates // to true. Try it! if (first == second) { System.out.println("first == second"); } // This evaluates to true if (first.equals(second)) { System.out.println("first equals second"); } // This evaluates to false if (first == third) { System.out.println("first == third"); } // This evaluates to true if (first.equals(third)) { System.out.println("first equals third"); } } // End myMethod( ) } //end class
Copying Reference Types
When reference types are copied, either a copy of the reference to an object is made, or an actual copy of the object is made, creating a new object. The latter is referred to as cloning in Java.
Copying a Reference to an Object
When copying a reference to an object, the result is one object with two references.
In the example below, closingSong
is assigned a
reference to the object pointed to by lastSong
. Any
changes made to lastSong
will be reflected in
closingSong
and vice versa.
Song lastSong = new Song( ); Song closingSong = lastSong;
Cloning Objects
Cloning results in another copy of the object, not just a copy of a reference to an object. Cloning is not available to classes by default. Note that cloning is usually very complex, so you should consider a copy constructor instead.
For a class to be cloneable, it must implement the interface
Cloneable
.The protected method
clone( )
allows for objects to clone themselves.For an object to clone an object other than itself, the
clone( )
method must be overridden and made public bythe object being cloned.When cloning, a cast must be used because
clone( )
returns typeobject
.Cloning can throw a
CloneNotSupportedException
.
Shallow and deep cloning
Shallow and deep cloning are the two types of cloning in Java.
Primitive values and the references in the object being cloned are copied.
Copies of the objects referred to by those references are not made.
In the example below, leadingSong
will be
assigned the values in length
and year
, as they are primitive types, and references to
title
and artist
, as they are reference types.
Class Song { String title; Artist artist; float length; int year; void setData( ) {...} } Song firstSong = new Song( ); try { // Make an actual copy by cloning Song leadingSong = (Song)firstSong.clone( ); } catch (CloneNotSupportedException cnse) cnse.printStackTrace( ); } // end
In deep cloning:
The cloned object makes a copy of each of its object’s fields, recursing through all other objects referenced by it.
A deep-clone method must be programmer-defined, as the Java API does not provide one.
Alternatives to deep cloning are serialization and copy constructors. (Copy constructors are often preferred over serialization.)
Memory Allocation and Garbage Collection of Reference Types
When a new object is created, memory is allocated. When there are no references to an object, the memory that object used can be reclaimed during the garbage collection process. For more information on this topic, see Chapter 15.
Get Java Pocket Guide now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.