The Object SuperclassS2C Home « The Object Superclass

We finish the section by looking at the Object superclass, the 'daddy' of all classes. To explain what we mean by the last sentence, we need to first look back to the first Java class we wrote on the site, the Hello.java class:


public class Hello {
   public static void main (String[] args) {
      System.out.println("Hello World");
   }
}

Unbeknown to us, this simple program was implicitly extended by the compiler, in fact every program we write has an implicit superclass called Object. How can this be though? Looking back to earlier lessons in the section how can our Garbage class extend Truck and Object, we know we can only extend one class. Well the compiler just travels up the inheritance tree and tries to extend our Truck class. The Truck class extends the Vehicle class so the compiler travels up the tree again. The Vehicle class extends nothing so the compiler implicitly extends the Vehicle class with Object. What this means for us is that we get to inherit the methods of Object which we will talk about as we go through this lesson. For now, we just need to know that we always inherit from Object at the top of any hierarchy we create. Before we look at the methods of the Object class lets list some points about Object:

  • Any class that doesn't implicitly extend another class, is implicitly extended by the compiler to extend Object.
  • Object is a concrete class so we can create objects of type Object.
  • Every class inherits the non-final methods of Object and these methods can be overridden.
  • We can use Object as a polymorphic type for methods that need to work with objects of any type.
    • So if we can use Object as a polymorphic type, why not use Object with all our methods, parameters and return types? There are two very good reasons not to do this:
      1. Java is a strongly typed language and as such cares about the type of the variables we declare. As we know whenever we declare a variable in Java, we have to give it a type, or the compiler complains when we come to compile the code. If we made every parameter and return type Object bang goes our type-safety. Instead of catching errors at compile time, we may run code for months before we get a runtime error, or even worse not notice an error at all, not a pleasant prospect.
      2. Inheritance, remember this only works up the inheritance tree as explained in inheritance method invocation. So if we made everything reference Object, what happens when we try to use the load() method from our Vehicle classes on the Object reference type? The compiler doesn't allow it as Object knows nothing of subclass methods unless they have been overridden. Therefore the only methods that can be called on Object are its own methods, or overrides thereof.
    So although it can be very useful to use the Object reference type polymorphically, and on occasions this is exactly what we want to do, it comes with a price.

Object Methods OverviewTop

The table below shows the declarations of all the methods in the Object class:

Method Declaration Description
Discussed in this lesson
public boolean equals(Object obj)Indicates whether another object is equal to the invoking object.
protected void finalize()Called by the garbage collector on an object before that object is discarded.
public final Class<? extends Object> getClass()Obtains and returns the runtime class of an object.
The <? extends Object> syntax is an example of a generic type which is discussed in the Generics section.
public int hashCode()Returns the hash code value associated with the object.
public String toString()Returns a string description of the object.
Discussed in the Concurrency Section
public final void notify()Wakes up a single thread that is waiting on the invoking object's lock.
public final void notifyAll() Wakes up all threads that are waiting on the invoking object's lock.
public final void wait()Current thread is put into a wait state until another thread invokes the notify()or notifyAll() method for this object.
public final void wait(long timeout)Current thread is put into a wait state until either another thread invokes the notify() or notifyAll() method for this object, or a specified amount of time has elapsed.
public final void wait(long timeout, int nanos)Current thread is put into a wait state until another thread invokes the notify() or notifyAll() method for this object, or a specified amount of time has elapsed, or a certain amount of real time has elapsed.
Not discussed
protected Object clone()Creates and returns a shallow copy of this object.

We will go through the first four methods in much greater detail as we go through this lesson. Use the links in the table above to go the Concurrency section for more information on the other Object methods we discuss on the site. For more information on the clone() method follow the link below.

Java DocumentationTop

As mentioned in the first two sections Java comes with very rich documentation. The following link will take you to the online version of documentation for the JavaTM 2 Platform Standard Edition 5.0 API Specification. Take a look at the documentation for the Object class which you can find by scrolling down the lower left pane and clicking on Object. You will go back to this documentation time and time again so if you haven't done so already I suggest adding this link to your browser's favourites toolbar for fast access.

The finalize() MethodTop


  • The finalize() method is only ever invoked once, for any given object, by the JVM.

The finalize() method of the Object class is called by the garbage collector on an object before that object is discarded. The finalize method in the Object class doesn't execute any specific tasks, but gives us the opportunity to override. We may want to do this to clear up any resources held by an object such as closing a file used by it.

We have no control over when the garbage collector runs as this is in the control of the JVM. The garbage collector runs its collection efficiently and this means it usually runs when there are a lot of objects to discard.

Overriding finalize()Top

To see an overridden finalize() method in action we are going to have to instantiate a lot of objects and then make these objects eligible for garbage collection:


/*
  Class to show an overridden finalize() method in action
*/ 
public class Finalize {
    private int i;
    
    Finalize(int i) {
        this.i = i;
    }
    /*
      Instantiate a Finalize object and immediately descope it
    */ 
    public void CreateFinalize(int i) {
        Finalize f = new Finalize(i);
    }
    /*
      Overridden finalize() method
    */ 
    protected void finalize() {
        System.out.println("The finalize() method called on object: " + i);
    }
    /*
      You may need to change the iterator to get this to work!
    */ 
    public static void main (String[] args) {
        Finalize f = new Finalize(0);
        for (int i=1; i < 300000; i++) {
            f.CreateFinalize(i);
        }
    }
}

Save, compile and run the Finalize class in directory   c:\_OOConcepts in the usual way.

run finalize()

The above screenshot shows the output of running our Finalize class on my machine. You may have to increase the iterator within the for loop to get it to work.

The getClass() MethodTop

The getClass() method of the Object class obtains and returns the runtime class of an object, or put another way the object that was instantiated.


/*
  Test Class for the getClass() method
*/ 
public class GetClassTest {
    public static void main (String[] args) {
        Vehicle car = new Car();
        System.out.println(car.getClass());
        Bus bus = new Bus();
        System.out.println(bus.getClass());
    }
}

Save, compile and run the file in directory   c:\_OOConcepts in the usual way.

run getClass()

The above screenshot shows the output of running our ATest test class. As you can see the method returns the class of the instantiated object.

The toString() MethodTop

The toString() method of the Object class returns a string description of the object which consists of the name of the class, the @ symbol and the unsigned hexadecimal representation of the hash code of the object. The official documentation from Oracle encourages us to override this method in our subclasses, which as we now know, are all subclasses of Object. Before we create an override lets look at the output of the toString() method we inherit from Object:


/*
  Test Class for the Object toString() method
*/ 
public class ToStringTest {
    public static void main (String[] args) {
        Carrier carrier = new Carrier(10, 20);
        System.out.println(carrier.toString());
    }
}

Save, compile and run the file in directory   c:\_OOConcepts in the usual way.

run toString()

The above screenshot shows the output of running our ToStringTest test class. As you can see the method returns the class of the instantiated object, the @ symbol and a number, not very meaningful.

Overriding toString()Top

What we need to do is override the Object toString() method in our Carrier class to give us some more information about the object state of instances we create:


/*
  A Carrier class
*/ 
public class Carrier {
    ...
    /*
      A Carrier class 
    */    
    public String toString() {
        String strVal = "[" + this.people + ", " + this.cost + "]";
    }
}

After adding the toString override to the Carrier class, save and compile it and rerun the ToStringTest test class in directory   c:\_OOConcepts in the usual way.

run toString() 2

The above screenshot shows the output of running our ToStringTest test class. We have overridden the Object toString() method in our Carrier class with something more meaningful to the class in question.

Checking Object EqualityTop

The equals() method of the Object class indicates whether another object is equal to the invoking object by using the == relational operator. What the method is actually doing is checking the bits in both reference variables and returning a boolean value. Therefore the equals() method of the Object class only ever returns true when the reference variables refer to the same object. Following is the contract specified in the Oracle documentation for the equals() method of the Object class:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

The equals() MethodTop

To illustrate usage of the equals() method of the Object class we will make a new TestEquals class:


/*
  A TestEquals class
*/ 
public class TestEquals {
    public static void main(String[] args) {
        Carrier a = new Carrier(10, 20);
        Carrier b = new Carrier(10, 20);
        Carrier c = a;
        System.out.println("Is a = b? " + a.equals(b));
        System.out.println("Is a = c? " + a.equals(c));
        System.out.println("Is b = c? " + b.equals(c));
    }
}

Save, compile and run the TestEquals class in directory   c:\_OOConcepts in the usual way.

run Object equals()

The above screenshot shows the output of running our TestEquals test class. Although the instances referenced by a and b have exactly the same object state they are not the same object and so the equals() method of the Object class returns false. The c reference is initialized to refer to a and therefore refers to the same object reference and as such the equals() method of the Object class returns true.

Overriding equals()Top

But what if want to think of two objects as equal if part of their object state is shared? Well then we need to override the equals() method of the Object class in our own classes. To demonstrate how to do this we will make a new Contractor class where we will consider objects equal if they have the same name and location as part of their object state:


/*
 A Contractor class
*/ 
public class Contractor {
    private String name = "";      // The Contractor Name.
    private String location = "";  // City Location.
    private String owner = "";     // The Customer Id.
    
    Contractor() {
    } 
    Contractor(String name, String location, String owner) {
        this.name = name;
        this.location = location;
        this.owner = owner;
    } 
    public boolean equals(Object obj) {
        if (this==obj) { // Same reference variable so equal
            return true;
        }
        if (!(obj instanceof Contractor)) // Make sure we have a Contractor instance
            return false;
        Contractor other = (Contractor) obj; // We need to cast to Contractor for comparison
        if (location == null) { // Compare locations
            if (other.location != null)
                return false;
        } else if (!location.equals(other.location))
            return false;
        if (name == null) { // Compare names
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

Lets go through our equals() override. The first thing we do is check for the same object using the == relational operator, if they are, we don't need to do anymore and return true. The object we want to check against is passed in polymorphically as type Object and so we need to make sure it is indeed a Contractor object and we use the instanceof operator to do this. Once we know we have the correct object type coming in we can cast it to the Contractor type for comparison. We then do checks on each property to make sure they are equal using the equals() method of String which is overridden in that class already. If all tests pass for both, then we know these two objects have the same state for the fields we are interested in and return true.

Save and compile the Contractor class in directory   c:\_OOConcepts in the usual way.

We should test our class to make sure the equals() override is working as intended:


/*
  A ContractorTest class
*/ 
public class ContractorTest {
    public static void main(String[] args) {
        Contractor a = new Contractor();
        Contractor b = new Contractor();
        Contractor c = new Contractor("Contractor A", "Essex", "1234");
        Contractor d = new Contractor("Contractor B", "Essex", "1234");
        Contractor e = new Contractor("Contractor A", "Essex", "5678");
        System.out.println("Is a = b? " + a.equals(b));
        System.out.println("Is a = c? " + a.equals(c));
        System.out.println("Is c = d? " + c.equals(d));
        System.out.println("Is c = e? " + c.equals(e));
        System.out.println("Is d = e? " + d.equals(e));
    }
}

Save, compile and run the ContractorTest class in directory   c:\_OOConcepts in the usual way.

run Object equals() override

The above screenshot shows the output of running our ContractorTest test class. Although the instances referenced by a and b have exactly the same object state they are not the same object and so the equals() method of the Object class returns false. The first test returns true as we haven't initialized any instance variables in a and b and so name and location are both set to null in both objects and so are indeed equal. The only other test that returns true is the fourth test where we compare c and e which both have the same name and location in both objects and so are equal. All other tests return false which is what we want so our override works correctly.

The hashCode() MethodTop

The hashCode() method returns the hash code value associated with the object. Following is the contract specified in the Oracle documentation for the hashCode() method of the Object class:

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode() method must consistently return the same integer, provided no information used in equals() comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode() method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(Object) method, then calling the hashCode() method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.

From this contract we can see that the hashCode() method of the Object class, relies on the equals() method of the Object class. What this means to us is whenever we override the equals method of the Object class, we should also override the hashCode() method to keep the general contract for the hashCode() method, which states that equal objects must have equal hash codes. We can tabularize the association between the equals and hashCode() contracts to make understanding easier:

Condition Mandatory Not Mandatory But Possible
From the equals() viewpoint
if x.equals(y) returns truex.hashCode() == y.hashCode()
if x.equals(y) returns falseNo hashCode() requirement
From the hashCode() viewpoint
x.hashCode() == y.hashCode()x.equals(y) returns true
x.hashCode() != y.hashCode()x.equals(y) returns false

So it looks like our Contractor class is breaking this contract. To see this we need to update our ContractorTest class to output the hashcodes for each object and check them against this table:


/*
  A ContractorTest class
*/ 
public class ContractorTest {
    public static void main(String[] args) {
        Contractor a = new Contractor();
        Contractor b = new Contractor();
        Contractor c = new Contractor("Contractor A", "Essex", "1234");
        Contractor d = new Contractor("Contractor B", "Essex", "1234");
        Contractor e = new Contractor("Contractor A", "Essex", "5678");
        System.out.println("Is a = b? " + a.equals(b));
        System.out.println("Is a = c? " + a.equals(c));
        System.out.println("Is c = d? " + c.equals(d));
        System.out.println("Is c = e? " + c.equals(e));
        System.out.println("Is d = e? " + d.equals(e));
        System.out.println("The hashcode of a: = " + a.hashCode());
        System.out.println("The hashcode of b: = " + b.hashCode());
        System.out.println("The hashcode of c: = " + c.hashCode());
        System.out.println("The hashcode of d: = " + d.hashCode());
        System.out.println("The hashcode of e: = " + e.hashCode());
    }
}

Save, compile and run the ContractorTest class in directory   c:\_OOConcepts in the usual way.

run Object hashCode()

The above screenshot shows the output of running our ContractorTest test class. We can clearly see from this that we have broken the hashCode contract. The hash codes of a and b should be the same as should those for c and e.

Overriding hashCode()Top

Because we have overridden the equals() method of Object, we also need to override the hashCode() method. Add the new hashCode() override below to the Contractor class:


/*
 A Contractor class
*/ 
public class Contractor {
     
     ...
     
	public int hashCode() {
        String hashKey = name + location;
	    return hashKey.hashCode();
	}
}

Lets go through our hashCode() override. We are concatenating the name and location instance variables we used for our equals() override into a String object. We then use the hashCode() method of the String class to convert the string to a hashcode and return it. By using the same variables we can be assured that the returned hashcode will be equal, when our equals() override returns true.

Add our hashCode() override to the Contractor class and save and compile the Contractor class in directory   c:\_OOConcepts in the usual way.

We should test our class to make sure the hashCode() override now honours its contract and is working as intended. So rerun the ContractorTest test class.

run hashCode() override

The above screenshot shows the output of running our ContractorTest test class. We can see that the hashcodes for a and b are the same, as are those for c and e. The contract for our hashCode() override is now fine.

Lesson 8 Complete

We finished the section on OO Concepts by looking at the Object object, the daddy of all classes.

What's Next?

In the next section we finish our studies of flow control by looking at exceptions and assertions.

go to home page Homepage go to top of page Top