站内搜索: 请输入搜索关键词
当前页面: 在线文档首页 > Java Tutorial 5.0 英文版

Being a Descendent of Object - Java Tutorial 5.0 英文版

The JavaTM Tutorial
Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search
Feedback Form

Trail: Learning the Java Language
Lesson: Classes and Inheritance

Being a Descendent of Object

The Object (in the API reference documentation) class sits at the top of the class hierarchy tree. Every class is a descendent, direct or indirect, of the Object class. This class defines the basic state and behavior that all objects might use, such as the ability to compare oneself to another object, to convert to a string, to wait on a condition variable, to notify other objects that a condition variable has changed, and to return the class of the object.

The Object class provides several useful methods that may need to be overridden by a well-behaved subclass.

  • equals and hashCode
  • toString
In addition, the Object class provides the following handy methods:
  • getClass
  • notify, notifyAll, and wait
And then there is finalize, a special method, called by the garbage collector. It shouldn't be called from regular programs.

With the exception of notify, notifyAll, and wait, these methods are covered in the sections that follow. The notify, notifyAll, and wait methods all play a part in synchronizing the activities of independently running threads in a program, which is discussed in Threads: Doing Two or More Tasks at Once (in the Learning the Java Language trail).

The clone Method


Note: Use great care when implementing a clone method. Implementing a clone method properly can be tricky and has some non-trivial consequences. This section covers the method briefly, but you can find more information about the ins and outs of implementing clone in the book Effective Java (outside of the tutorial) by Josh Bloch.
You use the clone method to create an object from an existing object. To create a clone, you write:
aCloneableObject.clone();
Object's implementation of this method checks to see whether the object on which clone was invoked implements the Cloneable interface. If the object does not, the method throws a CloneNotSupportedException. Even though Object implements the clone method, the Object class is not declared to implement the Cloneable interface, so classes that don't explicitly implement the interface are not cloneable. If the object on which clone was invoked does implement the Cloneable interface, Object's implementation of the clone method creates an object of the same class as the original object and initializes the new object's member variables to have the same values as the original object's corresponding member variables.

The simplest way to make your class cloneable, then, is to add implements Cloneable to your class's declaration. For some classes, the default behavior of Object's clone method works just fine. Other classes need to override clone to get correct behavior.

Consider a Stack class that contains an ArrayList and a member variable referencing its top element. If Stack relies on Object's implementation of clone, the original stack and its clone refer to the same list. Changing one stack changes the other, which is undesirable behavior.

Here is an appropriate implementation of clone for our Stack class, which clones the list to ensure that the original stack and its clone do not refer to the same list:

public class Stack implements Cloneable {
    private ArrayList<Object> items;
    ...
    //Code for Stack's methods and constructor
    //not shown.
    protected Stack clone() {
        try {
            //Clone the stack.
            Stack s = (Stack)super.clone();
            //Clone the list.
            s.items = (ArrayList)items.clone();
            return s; //Return the clone.
        } catch (CloneNotSupportedException e) {
            //This shouldn't happen because Stack and
            //ArrayList implement Cloneable.
            throw new AssertionError();
        }
    }
}
The implementation for Stack's clone method is relatively simple. First, it calls Object's implementation of the clone method by calling super.clone, which creates and initializes a Stack object. At this point, the original stack and its clone refer to the same list. Next, the method clones the list.

Note: The clone method should never use new to create the clone and should not call constructors. Instead, the method should call super.clone, which creates an object of the correct type and allows the hierarchy of superclasses to perform the copying necessary to get a proper clone.

The equals and hashCode Methods

The equals method compares two objects for equality and returns true if they are equal. The equals method provided in the Object class uses the identity operator (==) to determine whether two objects are equal. If the objects compared are the exact same object, the method returns true.

However, for some classes, two distinct objects of that type might be considered equal if they contain the same information. Here is an example of a Book class that, like a good citizen, overrides equals:

public class Book {
    ...
    public boolean equals(Object obj) {
        if (obj instanceof Book) {
            if (((Book)obj).getISBN().equals(this.ISBN)) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
}
Consider this code that tests two instances of the Book class for equality:
Book firstBook  = new Book("0201914670"); //Swing Tutorial, 2nd edition
Book secondBook = new Book("0201914670");
if (firstBook.equals(secondBook)) {
    System.out.format("objects are equal%n");
} else {
    System.out.format("objects are not equal%n");
}
This program displays objects are equal even though firstBook and secondBook reference two distinct objects. They are considered equal because the objects compared contain the same value.

You should always override the equals method if the identity operator is not appropriate for your class. If you override equals, override hashCode as well.

The value returned by hashCode is an int that maps an object into a bucket in a hash table. An object must always produce the same hash code. However, objects can share hash codes (they aren't necessarily unique). Writing a "correct" hashing function is easy — always return the same hash code for the same object. Writing an "efficient" hashing function — one that provides a sufficient distribution of objects over the buckets — is difficult and is outside the scope of this tutorial.

Even so, the hashing function for some classes is relatively obvious. For example, an obvious hash code for an Integer object is its integer value.

For more information on writing correct implementations of equals and hashcode, see Effective Java (outside of the tutorial) by Josh Bloch.

The finalize Method

The Object class provides a callback method, finalize, that allows you to clean up an object before it is garbage collected. The finalize method may be called automatically by the system, and this is where you put any necessary cleanup code.

Note: You should not employ sloppy programming practices and then rely on this method to do your cleanup for you. For example, say you forget to close a file descriptor after performing some I/O. You don't know when or even if garbage collection will occur (when this method would be called) and you may run out of file descriptors before you run out of memory.

For more information the dangers and subtleties of finalize, see Effective Java (outside of the tutorial) by Josh Bloch.


The toString Method

The Object's toString method returns a String representation of the object. You can use toString along with System.out.format to display a text representation of an object, such as an instance of Book:
System.out.format("%s%n", firstBook.toString());
which would, hopefully, print something useful, like this:
0201914670: The JFC Swing Tutorial: A Guide to Constructing GUIs, 2nd Edition
Authors: Kathy Walrath, Mary Campione, Alison Huml, Sharon Zakhour
The String representation for an object depends entirely on the object. The toString method is very useful for debugging. You should override this method in all your classes.

The getClass Method

The getClass method returns a runtime representation of the class of an object. This method returns a Class object, which you can query for information about the class, such as its name, its superclass, and the interfaces it implements. You cannot override getClass. The following method gets and displays the class name of an object:
void printClassName(Object obj) {
    //The getSimpleName method was introduced in J2SE 5.0.
    //Prior to that, you can use getName, which returns
    //the fully qualified name, rather than just the 
    //class name.
    System.out.format("The object's class is %s.%n"
                      obj.getClass().getSimpleName());
}
One easy way to get a Class object is from its class name. Here are two ways to get the Class object for the String class:
String.class
Class.forName("java.lang.String")
String.class is preferred for performance reasons because it does the lookup only once. Class.forName performs the lookup each time, but can be used even when the class name is not known at compile time.

Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search
Feedback Form

Copyright 1995-2005 Sun Microsystems, Inc. All rights reserved.