Java Object Class
Last modified: April 13, 2025
The java.lang.Object
class is the root of the Java class hierarchy.
Every class in Java is directly or indirectly derived from the Object class.
Understanding Object class methods is fundamental to Java programming as they
provide basic functionality to all objects.
The Object class defines several important methods that are inherited by all other classes. These include methods for object comparison, hash code generation, string representation, and object cloning. Knowing how to properly override these methods is crucial for writing correct and efficient Java code.
Object Class Methods
The Object class provides several methods that are common to all Java objects.
These methods can be overridden by subclasses to provide specific behavior.
The main methods include toString
, equals
,
hashCode
, clone
, and getClass
.
public class Object { public final Class<?> getClass() {...} public int hashCode() {...} public boolean equals(Object obj) {...} protected Object clone() throws CloneNotSupportedException {...} public String toString() {...} public final void notify() {...} public final void notifyAll() {...} public final void wait() throws InterruptedException {...} protected void finalize() throws Throwable {...} }
The code above shows the main methods provided by the Object class. These methods form the foundation of object behavior in Java and are available to all objects.
toString Method
The toString
method returns a string representation of the object.
By default, it returns the class name followed by '@' and the object's hash code.
This method is often overridden to provide more meaningful information.
package com.zetcode; class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person[name=" + name + ", age=" + age + "]"; } } public class Main { public static void main(String[] args) { Person person = new Person("John Doe", 30); System.out.println(person.toString()); System.out.println(person); // println automatically calls toString } }
In this example, we override the toString
method in the Person
class to return a meaningful string representation. When we print the object,
this custom string is displayed instead of the default implementation.
equals Method
The equals
method compares two objects for equality. The default
implementation simply checks if two references point to the same object.
For meaningful comparison, this method should be overridden.
package com.zetcode; class Book { private String title; private String author; public Book(String title, String author) { this.title = title; this.author = author; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Book book = (Book) obj; return title.equals(book.title) && author.equals(book.author); } } public class Main { public static void main(String[] args) { Book book1 = new Book("Java Basics", "John Smith"); Book book2 = new Book("Java Basics", "John Smith"); Book book3 = new Book("Advanced Java", "Jane Doe"); System.out.println("book1 equals book2: " + book1.equals(book2)); System.out.println("book1 equals book3: " + book1.equals(book3)); } }
This example demonstrates how to properly override the equals
method. We compare Book objects based on their title and author fields rather
than memory addresses. The method first checks for reference equality, null, and
class type before comparing fields.
hashCode Method
The hashCode
method returns an integer hash code value for the
object. This method must be overridden whenever equals
is
overridden to maintain the general contract that equal objects must have equal
hash codes.
package com.zetcode; class Student { private int id; private String name; public Student(int id, String name) { this.id = id; this.name = name; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Student student = (Student) obj; return id == student.id && name.equals(student.name); } @Override public int hashCode() { int result = 17; result = 31 * result + id; result = 31 * result + (name != null ? name.hashCode() : 0); return result; } } public class Main { public static void main(String[] args) { Student s1 = new Student(101, "Alice"); Student s2 = new Student(101, "Alice"); System.out.println("s1 hash: " + s1.hashCode()); System.out.println("s2 hash: " + s2.hashCode()); System.out.println("Equal objects same hash? " + (s1.hashCode() == s2.hashCode())); } }
This example shows a proper implementation of hashCode
that
matches our equals
implementation. We use a common algorithm that
combines hash codes of individual fields using prime numbers to reduce
collisions.
clone Method
The clone
method creates and returns a copy of the object. To make
a class cloneable, it must implement the Cloneable
interface and
override the clone
method. The default implementation performs a
shallow copy.
package com.zetcode; class Address implements Cloneable { private String city; private String street; public Address(String city, String street) { this.city = city; this.street = street; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public void setStreet(String street) { this.street = street; } @Override public String toString() { return city + ", " + street; } } public class Main { public static void main(String[] args) throws CloneNotSupportedException { Address addr1 = new Address("New York", "5th Avenue"); Address addr2 = (Address) addr1.clone(); System.out.println("Original: " + addr1); System.out.println("Clone: " + addr2); addr2.setStreet("Broadway"); System.out.println("\nAfter modification:"); System.out.println("Original: " + addr1); System.out.println("Clone: " + addr2); } }
This example demonstrates how to implement cloning. The Address class implements
Cloneable
and overrides clone
. After cloning,
modifying the clone's street doesn't affect the original, showing a proper
shallow copy implementation.
getClass Method
The getClass
method returns the runtime class of an object. This
method is final and cannot be overridden. It's useful for reflection and runtime
type checking.
package com.zetcode; class Animal {} class Dog extends Animal {} public class Main { public static void main(String[] args) { Animal animal = new Animal(); Dog dog = new Dog(); System.out.println("animal class: " + animal.getClass()); System.out.println("dog class: " + dog.getClass()); System.out.println("dog superclass: " + dog.getClass().getSuperclass()); if (dog instanceof Animal) { System.out.println("dog is an Animal"); } } }
This example shows how getClass
returns the actual runtime class
of an object. Even though dog is declared as Animal, getClass
returns Dog. We also demonstrate the instanceof operator for type checking.
wait, notify, notifyAll Methods
The wait
, notify
, and notifyAll
methods are used for thread synchronization. They must be called from within a
synchronized context and are fundamental to Java's inter-thread communication.
package com.zetcode; class Message { private String content; private boolean empty = true; public synchronized String read() { while (empty) { try { wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; } } empty = true; notifyAll(); return content; } public synchronized void write(String content) { while (!empty) { try { wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } empty = false; this.content = content; notifyAll(); } } public class Main { public static void main(String[] args) { Message message = new Message(); // Writer thread new Thread(() -> { String[] messages = {"First", "Second", "Third"}; for (String msg : messages) { message.write(msg); System.out.println("Written: " + msg); try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } message.write("DONE"); }).start(); // Reader thread new Thread(() -> { String msg; while (!"DONE".equals(msg = message.read())) { if (msg != null) { System.out.println("Read: " + msg); } } System.out.println("Reader finished."); }).start(); } }
This example demonstrates thread communication using wait
and
notifyAll
. The Message class coordinates between writer and reader
threads. The writer sends messages and the reader consumes them until receiving
"DONE". Proper synchronization ensures thread safety and correct communication.
Source
Java Object Class Documentation
In this article, we've covered all major methods of the Java Object class with practical examples. Understanding these methods is essential for proper Java development as they form the foundation of object behavior in the language.
Author
List all Java tutorials.