To clone the Address
object, it suffices to create a new Address
object and initialize it to a duplicate of the object referenced from the city
field. The new Address
object is then returned.
Compile Listing 6 (javac CloneDemo.java
) and run this application (java CloneDemo
). You should observe the following output:
John Doe: 49: Denver
John Doe: 49: Denver
John Doe: 49: Chicago
John Doe: 49: Denver
Cloning arrays
Array types have access to the clone()
method, which lets you shallowly clone an array. When used in an array context, you don't have to cast clone()
's return value to the array type. Listing 7 demonstrates array cloning.
Listing 7. Shallowly cloning a pair of arrays
class City
{
private String name;
City(String name)
{
this.name = name;
}
String getName()
{
return name;
}
void setName(String name)
{
this.name = name;
}
}
class CloneDemo
{
public static void main(String[] args)
{
double[] temps = { 98.6, 32.0, 100.0, 212.0, 53.5 };
for (int i = 0; i < temps.length; i++)
System.out.print(temps[i] + " ");
System.out.println();
double[] temps2 = temps.clone();
for (int i = 0; i < temps2.length; i++)
System.out.print(temps2[i] + " ");
System.out.println();
System.out.println();
City[] cities = { new City("Denver"), new City("Chicago") };
for (int i = 0; i < cities.length; i++)
System.out.print(cities[i].getName() + " ");
System.out.println();
City[] cities2 = cities.clone();
for (int i = 0; i < cities2.length; i++)
System.out.print(cities2[i].getName() + " ");
System.out.println();
cities[0].setName("Dallas");
for (int i = 0; i < cities2.length; i++)
System.out.print(cities2[i].getName() + " ");
System.out.println();
}
}
Listing 7 declares a City
class that stores the name and (eventually) other details about a city, such as its population. The CloneDemo
class provides a main()
method to demonstrate array cloning.
main()
first declares an array of double precision floating-point values that denote temperatures. After outputting this array's values, it clones the array -- note the absence of a cast operator. Next, it outputs the clone's identical temperature values.
Continuing, main()
creates an array of City
objects, outputs the city names, clones this array, and outputs the cloned array's city names. As a proof that shallow cloning was used, note that main()
changes the name of the first City
object in the original array and then outputs all of the city names in the second array. The second array reflects the changed name.
Compile Listing 7 (javac CloneDemo.java
) and run this application (java CloneDemo
). You should observe the following output:
98.6 32.0 100.0 212.0 53.5
98.6 32.0 100.0 212.0 53.5
Denver Chicago
Denver Chicago
Dallas Chicago
Comparing objects: equals()
The equals()
method lets you compare the contents of two objects to see if they are equal. This form of equality is known as content equality.
Although the ==
operator compares two primitive values for content equality, it doesn't work the way you might expect (for performance reasons) when used in an object-comparison context. In this context, ==
compares two object references to determine whether or not they refer to the same object. This form of equality is known as reference equality.
Object
's implementation of the equals()
method compares the reference of the object on which equals()
is called with the reference passed as an argument to the method. In other words, the default implementation of equals()
performs a reference equality check. If the two references are the same, equals()
returns true; otherwise, it returns false.
You must override equals()
to perform content equality. The rules for overriding this method are stated in Oracle's official documentation for the Object
class. They're worth repeating below:
- Be reflexive: For any non-null reference value
x
,x.equals(x)
should return true. - Be symmetric: For any non-null reference values
x
andy
,x.equals(y)
should return true if and only ify.equals(x)
returns true. - Be transitive: For any non-null reference values
x
,y
, andz
, ifx.equals(y)
returns true andy.equals(z)
returns true, thenx.equals(z)
should return true. - Be consistent: For any non-null reference values
x
andy
, multiple invocations ofx.equals(y)
consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
Additionally, for any non-null reference value x
, x.equals(null)
should return false.
Content equality
The small application in Listing 8 demonstrates how to properly override equals()
to perform content equality.
Listing 8. Comparing objects for content equality
class Employee
{
private String name;
private int age;
Employee(String name, int age)
{
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o)
{
if (!(o instanceof Employee))
return false;
Employee e = (Employee) o;
return e.getName().equals(name) && e.getAge() == age;
}
String getName()
{
return name;
}
int getAge()
{
return age;
}
}
class EqualityDemo
{
public static void main(String[] args)
{
Employee e1 = new Employee("John Doe", 29);
Employee e2 = new Employee("Jane Doe", 33);
Employee e3 = new Employee("John Doe", 29);
Employee e4 = new Employee("John Doe", 27 + 2);
// Demonstrate reflexivity.
System.out.println("Demonstrating reflexivity...");
System.out.println();
System.out.println("e1.equals(e1): " + e1.equals(e1));
System.out.println();
// Demonstrate symmetry.
System.out.println("Demonstrating symmetry...");
System.out.println();
System.out.println("e1.equals(e2): " + e1.equals(e2));
System.out.println("e2.equals(e1): " + e2.equals(e1));
System.out.println("e1.equals(e3): " + e1.equals(e3));
System.out.println("e3.equals(e1): " + e3.equals(e1));
System.out.println("e2.equals(e3): " + e2.equals(e3));
System.out.println("e3.equals(e2): " + e3.equals(e2));
System.out.println();
// Demonstrate transitivity.
System.out.println("Demonstrating transitivity...");
System.out.println();
System.out.println("e1.equals(e3): " + e1.equals(e3));
System.out.println("e3.equals(e4): " + e3.equals(e4));
System.out.println("e1.equals(e4): " + e1.equals(e4));
System.out.println();
// Demonstrate consistency.
System.out.println("Demonstrating consistency...");
System.out.println();
for (int i = 0; i < 5; i++)
{
System.out.println("e1.equals(e2): " + e1.equals(e2));
System.out.println("e1.equals(e3): " + e1.equals(e3));
}
System.out.println();
// Demonstrate the null check.
System.out.println("Demonstrating null check...");
System.out.println();
System.out.println("e1.equals(null): " + e1.equals(null));
}
}
Listing 8 declares an Employee
class that describes employees as combinations of names and ages. This class also overrides equals()
to properly compare two Employee
objects.
The equals()
method first verifies that an Employee
object has been passed. If not, it returns false. This check relies on the instanceof
operator, which also evaluates to false when null
is passed as an argument. Note that doing this satisfies the final rule above: "for any non-null reference value x
, x.equals(null)
should return false."
Continuing, the object argument is cast to Employee
. You don't have to worry about a possible ClassCastException
because the previous instanceof
test guarantees that the argument has Employee
type. Following the cast, the two name
fields are compared, which relies on String
's equals()
method, and the two age
fields are compared.
Compile Listing 8 (javac EqualityDemo.java
) and run the application (java EqualityDemo
). You should observe the following output:
Demonstrating reflexivity...
e1.equals(e1): true
Demonstrating symmetry...
e1.equals(e2): false
e2.equals(e1): false
e1.equals(e3): true
e3.equals(e1): true
e2.equals(e3): false
e3.equals(e2): false
Demonstrating transitivity...
e1.equals(e3): true
e3.equals(e4): true
e1.equals(e4): true
Demonstrating consistency...
e1.equals(e2): false
e1.equals(e3): true
e1.equals(e2): false
e1.equals(e3): true
e1.equals(e2): false
e1.equals(e3): true
e1.equals(e2): false
e1.equals(e3): true
e1.equals(e2): false
e1.equals(e3): true
Demonstrating null check...
e1.equals(null): false
Calling equals() on an array
You can call equals()
on an array object reference, as shown in Listing 9, but you shouldn't. Because equals()
performs reference equality in an array context, and because equals()
cannot be overridden in this context, this capability isn't useful.
Listing 9. An attempt to compare arrays
class EqualityDemo
{
public static void main(String[] args)
{
int x[] = { 1, 2, 3 };
int y[] = { 1, 2, 3 };
System.out.println("x.equals(x): " + x.equals(x));
System.out.println("x.equals(y): " + x.equals(y));
}
}
Listing 9's main()
method declares a pair of arrays with identical types and contents. It then attempts to compare the first array with itself and the first array with the second array. However, because of reference equality, only the array object references are being compared; the contents are not compared. Therefore, x.equals(x)
returns true (because of reflexivity -- an object is equal to itself), but x.equals(y)
returns false.
Compile Listing 9 (javac EqualityDemo.java
) and run the application (java EqualityDemo
). You should observe the following output:
x.equals(x): true
x.equals(y): false
Cleaning up objects: finalize()
Suppose you've created a Truck
object and have assigned its reference to Truck
variable t
. Now suppose that this reference is the only reference to that object (that is, you haven't assigned t
's reference to another variable). By assigning null
to t
, as in t = null;
, you remove the only reference to the recently created t
object, and make the object available for garbage collection.
The finalize()
method lets an object whose class overrides this method (which is known as a finalizer) perform cleanup tasks when called by the garbage collector. This cleanup activity is known as finalization.
The default finalize()
method does nothing; it returns when called. You must provide code that performs some kind of cleanup task. Here's the pattern for finalize()
:
class someClass
{
// ...
@Override
protected void finalize() throws Throwable
{
try
{
// Finalize the subclass state.
// ...
}
finally
{
super.finalize();
}
}
}