Web Analytics

The Object Class

Intermediate ~30 min read

The Object class in java.lang is the root of the class hierarchy. Every class in Java implicitly extends Object, making its methods available to all objects.

The Root of All Classes

Every class in Java, whether you explicitly extend it or not, is a descendant of Object:

// These two declarations are equivalent:
class MyClass { }
class MyClass extends Object { }

// Even classes that extend other classes ultimately extend Object
class Animal { }           // Implicitly extends Object
class Dog extends Animal { } // Dog -> Animal -> Object
Key Methods of Object Class:
  • toString() - String representation of the object
  • equals(Object obj) - Check if two objects are equal
  • hashCode() - Return a hash code value
  • getClass() - Return the runtime class
  • clone() - Create a copy of the object
  • finalize() - Called before garbage collection (deprecated)

The toString() Method

Returns a string representation of the object. The default implementation returns the class name followed by @ and the hash code in hexadecimal.

class Person {
    String name;
    int age;

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

Person p = new Person("Alice", 25);
System.out.println(p);  // Output: Person@1a2b3c4d (unhelpful!)

// Override toString() for meaningful output
class Person {
    String name;
    int age;

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

System.out.println(p);  // Output: Person{name='Alice', age=25}
Best Practice: Always override toString() in your classes for easier debugging and logging.

The equals() Method

The default equals() compares object references (same as ==). Override it to compare object content instead.

class Person {
    String name;
    int age;

    // Default equals() compares references
    Person p1 = new Person("Alice", 25);
    Person p2 = new Person("Alice", 25);

    System.out.println(p1 == p2);       // false (different objects)
    System.out.println(p1.equals(p2));  // false (default equals = ==)

    // Override equals() to compare content
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;  // Same reference
        if (obj == null || getClass() != obj.getClass()) return false;

        Person person = (Person) obj;
        return age == person.age && name.equals(person.name);
    }
}

The hashCode() Method

Returns an integer hash code value for the object. If you override equals(), you must override hashCode() too.

The Contract:
  • If a.equals(b) is true, then a.hashCode() == b.hashCode() must be true
  • If hashCode() values are different, equals() must return false
  • Two unequal objects may have the same hash code (collision)
class Person {
    String name;
    int age;

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    // Or use Objects.hash() (Java 7+)
    @Override
    public int hashCode() {
        return java.util.Objects.hash(name, age);
    }
}

The getClass() Method

Returns the runtime class of the object. This method is final and cannot be overridden.

Animal animal = new Dog();

Class<?> cls = animal.getClass();
System.out.println(cls.getName());        // "Dog"
System.out.println(cls.getSimpleName());  // "Dog"
System.out.println(cls.getSuperclass());  // "class Animal"
Output
Click Run to execute your code

The clone() Method

Creates and returns a copy of the object. To use it, your class must implement Cloneable interface.

class Person implements Cloneable {
    String name;
    int age;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();  // Shallow copy
    }
}

Person original = new Person("Alice", 25);
Person copy = (Person) original.clone();

System.out.println(original == copy);         // false (different objects)
System.out.println(original.name == copy.name); // true (shallow copy!)
Shallow vs Deep Copy:
  • Shallow copy: Copies references (default clone behavior)
  • Deep copy: Creates new instances of referenced objects too

Complete Example: Proper equals/hashCode/toString

import java.util.Objects;

class Student {
    private String name;
    private int id;

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

    @Override
    public String toString() {
        return "Student{name='" + name + "', id=" + id + "}";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return id == student.id && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, id);
    }
}

Common Mistakes

  • Overriding equals() without hashCode(): Breaks HashMap, HashSet functionality
  • Using == instead of equals() for strings: Always use .equals() for object comparison
  • Not checking for null in equals(): Can cause NullPointerException
  • Forgetting to override toString(): Makes debugging difficult

Summary

  • Object is the root class - every class extends it
  • Override toString() for meaningful string representation
  • Override equals() to compare object content (not references)
  • If you override equals(), always override hashCode()
  • getClass() returns the runtime class (cannot be overridden)
  • clone() creates a copy (requires Cloneable interface)