Massimo Caliman
by Massimo Caliman
3 min read

Categories

  • Java

Tags

  • code
  • en
  • java
  • oop

One of the questions you may be asked at an interview, certification, or a simple test concerning the Java language is the following: What are the methods of the Object class?

clone() - This method helps us create and return a copy of the object on which the method is called. equals() - Helps us make a comparison finalize() - Called by the garbage collector on an object when it is being finalised getClass() - Returns the class at runtime of the object. hashCode() - Returns the hash code value for the object. toString() - returns a string representation of the object. notify(), notifyAll(), and wait() - Helps synchronize the activities of independently running threads in the program

If you look at this code

public class TClass extends Object {

    public TClass() {
        System.out.println("call constructor TClass()!");
    }

    public static void main(String[] args) {
        TClass instance = new TClass();
    }
}

clone() - This method helps us create and return a copy of the object on which the method is called. equals() - Helps us make a comparison. finalize() - Called by the garbage collector on an object when it is being finalized. getClass() - Returns the class at runtime of the object. hashCode() - Returns the hash code value for the object. toString() - Returns a string representation of the object. notify(), notifyAll(), and wait() - Help synchronize the activities of independently running threads in the program.

/*
 * Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package java.lang;

public class Object {

    private static native void registerNatives();
    static {
        registerNatives();
    }

    public final native Class<?> getClass();
    public native int hashCode();
    public boolean equals(Object obj) {
        return (this == obj);
    }
    protected native Object clone() throws CloneNotSupportedException;
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    public final native void notify();
    public final native void notifyAll();
    public final native void wait(long timeout) throws InterruptedException;
    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }
    public final void wait() throws InterruptedException {
        wait(0);
    }

    protected void finalize() throws Throwable { }
}

At a quick glance we can say that

public final native Class<?> getClass();

has an native implementation, it is final and so you can’t override it, the reasons are obvious, think what it would mean from a security point of view if you could do that.

By contrast hashCode, also native, provides an implementation but you can override it and create your own mechanism for generating the hash code:

public native int hashCode();

The equals() method

public boolean equals(Object obj) {
        return (this == obj);
 }

provides a very simple default implementation, it checks if the object is the same or not (not if its properties have the same values, which is much more useful, but that’s normal, it’s up to us to implement such behaviour, it’s not necessarily the case that two objects are the same only if they have the same attributes).

We immediately notice that the method has to be handled. For example, if we want to use the default native implementation, we must make our class implement the Cloneable interface.

public class TClass implements Cloneable {

Since all classes extend Object, we have omitted extends Object. The toString() method is one of my favorites; if handled properly, it makes development and testing convenient:


 public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

It is evident that its default implementation is trivial:

The notify(), notifyAll(), and wait() methods in their different overloaded versions deserve separate treatment.

The finalize() method has an empty implementation

protected void finalize() throws Throwable { }

Beware, you are unlikely to override finalize(), and it is the JVM or rather the GC that decides whether and how much to call it anyway.