Object Class in Java

In Java, the Object class is the root or superclass of all classes. Every predefined and user-defined class is a subclass of the Object class. It contains the most commonly required methods for every Java class.

The most commonly required methods for every Java class (whether it is a predefined class or customized class) are defined in a separate class which is nothing but an Object class.

This class name is chosen as Object, not some other name because as per coding standards, generally name will be chosen based on the operations it is performing. This class has methods to perform operations related to the object of a class, so its name is chosen as an Object.

All classes in Java are inherited from which class

All classes in java are inherited from java.lang.Object class. Every class in Java is the child class of java.lang.Object class either directly or indirectly. Therefore, Object class methods are directly available for every Java class. Hence Object class is considered as the root of all Java classes.

class A {}
class B extends A {}

Here class A is a direct subclass of Object class. Class B is a subclass of A, so indirectly B class is also the subclass of Object.

If our class doesn’t extend any class then only, our class is the direct child class of Object class. For example, in our case, A class is the direct subclass of Object class. But if our class extends any other class then our class is an indirect child class of Object class. For example in our case, B is the subclass of A, and class A is the subclass of Object class. Hence it is an indirect child class of the Object class. It is called a multi-level inheritance.

class A {}
class A extends Object {}
class A extends java.lang.Object {}

These three are the same class declaration, there is no difference between them.

Reasons to create Object class as super class

Object class is created as super class for all java classes due to below 2 reasons,

1. To achieve reusability to subclasses:- Every object will have some common behaviours. These common behaviours must be implemented in every class with the same signature by every class developer. So for informing these methods signature and to reduce the burden in implementing these methods, SUN developed a class called Object by implementing all these common behaviours with some methods common to all classes. All these methods have generic logic common for all subclasses. If this logic is not satisfying to subclass requirement then subclass should override these methods.

2. To achieve loose coupling and runtime polymorphism in user class:- If we want to define a method receive and to return any type of object, and further to invoke and execute from this passed object class, we must have one common superclass to all classes.

Object class methods in Java

Every object will contain some common operations. To perform these operations seperate methods are given.

OperationMethods
Retrieving the runtime class object referencegetClass()
Retrieving object identity i.e. hascodehashCode()
Comparing two objects of a classequals(Object obj)
Retrieving object information in String format for printing purposetoString()
Object Cloningclone()
Executing object clean-up code just before object is being destroyedfinalize()
Notifying about object lock availability to waiting threadsnotify()
notifyAll()
Releasing object lock and sending thread to waiting statewait()
wait(long mills)
wait(long mills, int Nanos)

These methods are discussed in detail in their own topic, all links are given. The finalize() method is related to garbage collection features of Java. The notify(), notifyAll(), and wait() methods are related to multi-threading feature of Java OOP.

The finalize() method is deprecated since Java 9. The finalization mechanism is inherently problematic. Finalization can lead to performance issues, deadlocks, and hangs. Errors in finalizers can lead to resource leaks; there is no way to cancel finalization if it is no longer necessary, and no ordering is specified among calls to finalize() methods of different objects. Furthermore, there are no guarantees regarding the timing of finalization. The finalize method might be called on a finalizable object only after an indefinite delay, if at all.

  • public final native Class<?> getClass();
  • public native int hashCode();
  • public boolean equals(Object obj)
  • public String toString()
  • protected native Object clone() throws CloneNotSupportedException;
  • protected void finalize() throws Throwable { }
  • public final native void notify();
  • public final native void notifyAll();
  • public final void wait() throws InterruptedException
  • public final native void wait(long timeoutMillis) throws InterruptedException;
  • public final void wait(long timeoutMillis, int nanos) throws InterruptedException

Methods Demonstration

  • The getClasss() method is used to get class information.
  • In the Object class hashCode() method is implemented to return the reference of the object as an integer number format.
  • The equals() method is implemented to compare two objects based on their reference. If two objects are having the same reference then the equals() method returns true else it returns false.
  • The toString() method of java.lang.Object class internally calling getClass() and hashCode() method of Object class to return getClass().getName() + "@" + Integer.toHexString(hashCode()) value.
  • The clone() method is used to create duplicate objects.

Demonstrating methods of java.lang.Object class.

class Student {
   int id;
   Student(int id) {
      this.id = id;
   }
}

public class Test {
  public static void main(String[] args) {

     Student s1 = new Student(1001);
     Student s2 = new Student(1002);
     Student s3 = new Student(1001);
     Student s4 = s1;

     // getClass() method
     Class cls = s1.getClass();
     String name = cls.getName();
     System.out.println("Class name of s1:: " 
                       + name );

     // invoke hashCode() method
     System.out.println("hashCode():: " + 
                    s1.hashCode() + " " + 
                    s2.hashCode() + " " + 
                    s3.hashCode() + " " +
                    s4.hashCode() );

     // invoke equals() method
     System.out.println("equals():: " +
                  s1.equals(s2) + " " + 
                  s1.equals(s3) + " " + 
                  s1.equals(s4) );

     // toString() method
     System.out.println("toString():: " +
                    s1.toString() + " " +
                    s2.toString() + " " +
                    s3.toString() );
  }
}

Output:-

Class name of s1:: Student
hashCode():: 1259475182 1300109446 1020371697 1259475182
equals():: false false true
toString():: [email protected] [email protected] [email protected]

Demonstrating clone() method of java.lang.Object class. To invoke the clone() method on an object, the class must implement the Cloneable interface. Since it is a protected method so we can call it only with the subclass, not in the user class.

class Student implements Cloneable {

   int id;
   Student(int id) {
      this.id = id;
   }

   public static void main(String[] args) 
          throws CloneNotSupportedException {

      Student s1 = new Student(1001);
      // clone() method
      Student s2 = (Student) s1.clone();
      
      System.out.println(s1 + " " + s2);
      System.out.println(s1.id + " " + s2.id);
   }
}

Output:-

[email protected] [email protected]
1001 1001

Overriding Object class Methods

In Object class, all the above methods logic is implemented based on the object’s reference common to all subclasses. If we want these methods to perform operations based on the object’s state, we must override these methods in subclasses. For example, consider the toString() method, this method has logic to display current objects [email protected] and output will be displayed based on the object’s reference. Rather than displaying [email protected], if we want to display data of the object then we must override the toString() method in the subclass.

We can override only non-final methods. Among these 11 methods, 6 methods are final methods. Therefore we can override only five methods they are not given as the final methods. Those methods are,
1) equals
2) hashcode
3) toString
4) clone
5) finalize

Overriding equals and hashCode() methods

When should we override equals and hashcode methods in subclass?

To store subclass objects in Set or Map collections we must override equals and hashcode methods. If these two methods are not overridden then we don’t get any compile-time or runtime errors but those objects are not found for retrieving or removing from collection objects because these methods are executed from java.lang.Object class based on their references.

class Student {
   private int id;
   Student(int id) {
      this.id = id;
   }

   @Override
   public boolean equals(Object obj) {
      if(this == obj) return true;
      if(obj instanceof Student) {
         Student s = (Student) obj;
         return this.id == s.id;
      }
      return false;
   }

   @Override 
   public int hashCode() {
      return id;
   }
}

public class Test {
  public static void main(String[] args) {

      Student s1 = new Student(1001);
      Student s2 = new Student(1002);
      Student s3 = new Student(1001);
      
      // invoke hashCode() method
      System.out.println("hashCode():: " +
                     s1.hashCode() + " " +
                     s2.hashCode() + " " +
                     s3.hashCode() );

      // invoke equals() method
      System.out.println("equals():: " + 
                   s1.equals(s2) + " " +
                   s1.equals(s3) + " " +
                   s2.equals(s3) );
  }
}

Output:-

hashCode():: 1001 1002 1001
equals():: false true false

Overriding toString() method

When should we override toString() method?

To print object data we must override the toString() method by returning the object’s non-static variables value (state). This method is called form PrintStream class println(Object) parameter method to get the argument object’s information for printing it on the console.

class Student {
   private int id;
   private String name;
   private double mark;

   Student(int id, String name, double mark) {
      this.id = id;
      this.name = name;
      this.mark = mark;
   }

   @Override
   public String toString() {
      return "["+ id + ", " + name + ", " + mark + "]";
   }
}

public class Test {
   public static void main(String[] args) {

      Student s1 = new Student(1001, "Alora", 75);
      Student s2 = new Student(1002, "Jessica", 80);
      
      // invoke toString() method using println()
      // println() internally calls toString() method
      System.out.println(s1);
      System.out.println(s2);

      // invoking toString()
      System.out.println(s1.toString());
      System.out.println(s2.toString());
   }
}

Output:-

[1001, Alora, 75.0]
[1002, Jessica, 80.0]
[1001, Alora, 75.0]
[1002, Jessica, 80.0]

Overriding clone() method

Generally, we override methods for two reasons,
1) To change implementation logic. Examples:- Overriding toString(), hashCode(), and equals() method.
2) To change the accessibility modifiers. Example:- Overriding clone() method.

The clone() method of Object class is protected and because of this we can access it only in the sub class, not in the user class (like Test class). To overcome this problem we must override the clone() method of java.lang.Object class. Since we are overriding clone() method just to change the accessiblity modifier not to change the implementaion logic so while overriding we must call Object class clone() as super.clone().

class Student implements Cloneable {
   int id;
   Student(int id) {
      this.id = id;
   }
   public Object clone() 
            throws CloneNotSupportedException {
      // downcast the return value
      return super.clone();
   }
}

public class Test {
   public static void main(String[] args) 
             throws CloneNotSupportedException {
      Student s1 = new Student(1001);
      // invoke clone() method
      Student s2 = (Student) s1.clone();
      
      System.out.println(s1 + " " + s2);
      System.out.println(s1.id + " " + s2.id);
   }
}

Output:-

[email protected] [email protected]
1001 1001

If you enjoyed this post, share it with your friends. Do you want to share more information about the topic discussed above or you find anything incorrect? Let us know in the comments. Thank you!

Leave a Reply