Object Class in Java

The Object class is a fundamental class in Java that serves as the root of the class hierarchy. Inheriting from the Object class provides all classes in Java with several important methods that can be used to manipulate objects. In this tutorial, we’ll explore some of the most commonly used methods that are inherited from the Object class, including toString(), equals(), getClass(), and clone(). By the end of this tutorial, you’ll have a solid understanding of how to use these methods to work with objects in Java.

All Classes are Instances of Object Class

The Object class is the ultimate parent class in Java. All classes in Java directly or indirectly inherit the Object class. Therefore, all Java classes are considered to be instances of the Object class. Consider the following example:

class Vehicle { // ... }
class Car extends Vehicle { // ... }

class Test {
  public static void main(String[] args) {
    Vehicle vehicle = new Vehicle();
    Car car = new Car();
    System.out.println(vehicle instanceof Object);
    System.out.println(car instanceof Vehicle);
    System.out.println(car instanceof Car);
  }
}
Output:
true 
true 
true

As you can see, the Car class inherits the Vehicle class, which is itself an instance of the Object class. Similarly, the Car class is an instance of both the Vehicle and the Object class.

In Java, all classes inherit some useful methods from the Object class, which we will discuss in the next section.

Inheriting Methods from the Object Class

Method Description
public final Class getClass() Returns the class object of the object it is called on, it is used to obtain information about the class of an object at runtime.
public int hashCode() Returns a unique integer value (called hash code) that represents the object.
public boolean equals(Object obj) Used to compare whether two objects are equal or not. It returns true if the two objects are equal, and false otherwise.
protected Object clone() throws CloneNotSupportedException Creates and returns a copy of the current object. It throws a CloneNotSupportedException if the object being cloned does not implement the Cloneable interface.
public String toString() Convert an object into a human-readable string representation, this method can be overridden by a subclass to provide a more appropriate string representation of the object.
public final void notify() Used in Java to wake up a single thread that is waiting on the object’s monitor.
public final void notifyAll() Used to wake up all threads that are waiting on a particular object.
public final void wait() throws InterruptedException Causes the current thread to pause execution and enter a waiting state until another thread notifies it to continue. If the thread is interrupted while waiting, it throws an InterruptedException.
public final void wait(long timeout) throws InterruptedException Causes the current thread to pause its execution and wait for a certain amount of time, specified by the timeout parameter. If the specified timeout elapses or the thread is interrupted, the method throws an InterruptedException.
public final void wait(long timeout, int nanos) throws InterruptedException Causes the current thread to wait for a specified amount of time (given in milliseconds and nanoseconds) until it is awakened by another thread or until the specified time has elapsed.
protected void finalize() throws Throwable Called by the garbage collector before an object is removed from memory. It can be overridden in a subclass to perform cleanup operations such as releasing system resources or closing files.

We will take a closer look at some of the most interesting and useful methods inherited from the Object class in Java. We will explore their functionality and usage, and provide examples of how to implement them in our own Java programs:

The toString() Method

The toString() method is a method of the Object class in Java that returns a string representation of the object. By default, the toString() method returns a string that contains the name of the object’s class and its hash code. For example, if you create an instance of the Vehicle class and call its toString() method, you might get a string like this:

Vehicle@3b192d32

This string consists of the name of the class (Vehicle) followed by the @ symbol and the hash code of the object in hexadecimal notation. This default implementation of toString() is not very useful for debugging or logging, as it doesn’t provide any information about the state of the object.

However, you can override the toString() method in your own classes to provide a more useful string representation of the object. For example, you might override toString() in the Vehicle class to return a string that contains information about the vehicle’s make, model, and year:

class Vehicle {
    private String make;
    private String model;
    private int year;

    public Vehicle(String make, String model, int year) {
        this.make = make;
        this.model = model;
        this.year = year;
    }

    @Override
    public String toString() {
        return year + " " + make + " " + model;
    }
}

Now, if you create an instance of Vehicle and call its toString() method, you’ll get a string that contains information about the vehicle:

2010 Toyota Camry

The toString() method is very useful for debugging and logging, as it allows you to quickly see the state of an object in a human-readable format. Many Java classes, such as collections and exceptions, override toString() to provide useful string representations of their contents. When creating your own classes, you should always consider overriding toString() to provide a useful string representation of the object’s state.

The equals() Method

One of the most important methods inherited by Java classes from the Object class is the equals() method. The equals() method is used to compare two objects for equality. By default, the equals() method compares objects based on their memory addresses. This means that two objects will be considered equal only if they are the same instance of the same class.

For example:

String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");

System.out.println(str1.equals(str2)); // true
System.out.println(str1.equals(str3)); // false

In the above example, str1 and str2 are both instances of the String class and they contain the same value “hello”. Therefore, the equals() method returns true when comparing them. On the other hand, str3 is also an instance of the String class and it contains the same value “hello”, but it is a different instance than str1 and str2. Therefore, the equals() method returns false when comparing them.

Sometimes, we want to compare objects based on their content instead of their memory addresses. In such cases, we can override the equals() method in our class to provide our own implementation for comparing objects. To override the equals() method, we need to follow these rules:

  1. The equals() method must be public.
  2. The equals() method must take an Object parameter (i.e., public boolean equals(Object obj)).
  3. The equals() method must return a boolean value (true if the objects are equal, false otherwise).
  4. The equals() method must be consistent: if two objects are equal according to the equals() method, then they must remain equal for the lifetime of the objects.
  5. The equals() method must be reflexive: an object must be equal to itself.
  6. The equals() method must be symmetric: if a.equals(b) returns true, then b.equals(a) must also return true.
  7. The equals() method must be transitive: if a.equals(b) returns true and b.equals(c) returns true, then a.equals(c) must also return true.
  8. The equals() method must not throw an exception.

Here is an example of a class that overrides the equals() method:

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof Person)) {
            return false;
        }
        Person other = (Person) obj;
        return name.equals(other.name) && age == other.age;
    }
}

In the above example, the equals() method compares two Person objects based on their name and age attributes.

The equals() method VS the == operators

In Java, there are two ways to compare objects: using the equals() method and the == operator. While both are used for comparison, they differ in their functionality.

The == operator is used to compare the references of two objects. It checks whether two objects are the same or not, i.e., if they refer to the same memory location. For example, if we have two objects obj1 and obj2, obj1 == obj2 will return true only if obj1 and obj2 refer to the same object in memory.

On the other hand, the equals() method is used to compare the values of two objects. It checks whether two objects have the same values or not. By default, the equals() method is inherited from the Object class and compares the references of two objects. However, most classes override this method to compare the values of two objects.

When to use the == operator:

  • Use the == operator when you want to check if two objects are the same instance.
  • Use the == operator when comparing primitive types.

When to use the equals() method:

  • Use the equals() method when you want to check if two objects have the same values.
  • Use the equals() method when comparing objects of non-primitive types.

It is important to note that not all classes override the equals() method, and some may provide their own custom comparison methods. In such cases, it is important to understand the specific comparison logic provided by the class and use the appropriate method for comparison.

It is also important to note that when implementing the equals() method, it is recommended to follow the guidelines laid out in the Java documentation to ensure consistency and correctness of the comparison logic.

The getClass() Method

The getClass() method is a non-static method of the Object class that returns the runtime class of an object. The method is commonly used to determine the class of an object at runtime, which can be useful for a variety of purposes, such as debugging and logging.

Here’s an example that demonstrates how to use the getClass() method to determine an object’s class:

Object obj = new String("Hello, world!");
Class<?> clazz = obj.getClass();
System.out.println("The class of obj is " + clazz.getName());

In this example, we create a new String object and assign it to a variable of type Object. We then use the getClass() method to get the runtime class of the obj variable, which is a String. Finally, we print the name of the class using the getName() method of the Class class.

The output of this program will be:

The class of obj is java.lang.String

This example shows how the getClass() method can be used to determine the class of an object at runtime. Note that the getClass() method returns an object of type Class<?>, which is a generic type that represents the runtime class of an object.

It’s worth noting that the getClass() method is inherited by all classes in Java, so you can use it on any object to get its runtime class. However, the getClass() method can be overridden by subclasses, so it’s possible that the method might not return the expected result in some cases.

Overall, the getClass() method is a useful tool for determining the runtime class of an object, which can be useful for a variety of purposes.

The clone() method

The clone() method is a protected method defined in the Object class that allows objects to be copied. The method creates a new object of the same class as the original object and initializes its fields with the same values as the original object.

Here’s an example of how to use the clone() method:

class Person implements Cloneable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }

        if (!(obj instanceof Person)) {
            return false;
        }

        Person other = (Person) obj;

        return this.name.equals(other.name) && this.age == other.age;
    }
}

class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person("John", 25);
        Person person2 = (Person) person1.clone();

        System.out.println(person1 == person2); // false
        System.out.println(person1.equals(person2)); // true
    }
}

In this example, we create a Person class that implements the Cloneable interface. This is necessary because the clone() method can only be called on objects that implement this interface.

Then we create an instance of Person called person1. We use the clone() method to create a new instance of Person called person2. We cast the result of clone() to the Person class so that we can assign it to the person2 variable.

Finally, we compare the two instances of Person. Since person1 and person2 are different objects (even though they have the same values for their fields), the comparison person1 == person2 returns false. However, since the Person class overrides the equals() method, the comparison person1.equals(person2) returns true.

Note that the clone() method creates a shallow copy of the object, meaning that any objects referenced by the original object are not duplicated. If you need a deep copy, you’ll need to implement it yourself.

Conclusion

In conclusion, the Object class is an essential class in Java, as it serves as the ultimate parent class for all other classes. All classes in Java directly or indirectly inherit from the Object class and inherit a set of useful methods such as toString(), equals(), getClass(), clone(), and more. By understanding these methods and how they are inherited, you can improve the quality and efficiency of your Java programs. Remember to always check the Java documentation for more information on these methods and how to use them effectively in your code.

Frequently asked questions

  • Can I use the toString() method to convert an object to a string and vice versa?
    The toString() method can be used to convert an object to a string representation in Java, but it cannot be used to convert a string back to an object. To create an object from a string, you will typically need to use a constructor or a factory method that accepts a string parameter.
  • How does the Object class handle memory management in Java?
    The Object class in Java doesn’t directly handle memory management, but it does provide a method called finalize() that can be used to release any resources that an object might be holding onto before it is garbage collected. In general, memory management in Java is handled by the JVM’s garbage collector, which automatically frees up memory for objects that are no longer being used by the program.
  • What are immutable objects in Java, and why are they important?
    Immutable objects are objects in Java whose state cannot be modified once they are created. In other words, their values cannot be changed after creation. Examples of immutable objects in Java include String, Integer, and other wrapper classes.

Leave a Reply

Your email address will not be published. Required fields are marked *