Object Class in Java – The Root of Every Class
java.lang.Object is the root of the Java class hierarchy. Every class extends Object directly or indirectly, so every object, including arrays, inherits its methods. That is why you can call toString(), equals() or hashCode() on any object, and why a variable of type Object can refer to anything.
This tutorial explains each Object method, how and when to override toString, equals and hashCode, the contract between them, and the helper class java.util.Objects.
1. Methods of the Object Class
| Method | Purpose | Override? |
|---|---|---|
String toString() | Text representation; default is ClassName@hexHash | Usually |
boolean equals(Object o) | Logical equality; default is identity (==) | For value objects |
int hashCode() | Hash for hash-based collections | Whenever equals is overridden |
Class<?> getClass() | Runtime class of the object | No (final) |
Object clone() | Field-by-field copy (needs Cloneable) | Rarely |
wait(), notify(), notifyAll() | Thread coordination on the object's monitor | No (final) |
void finalize() | Old pre-GC hook | Never (deprecated for removal) |
2. Overriding toString, equals and hashCode
import java.util.*;
public class ObjectMethods {
static class Plain { int id = 7; }
static final class Product {
private final String sku;
private final String name;
Product(String sku, String name) { this.sku = sku; this.name = name; }
@Override public String toString() { return "Product[" + sku + ", " + name + "]"; }
@Override public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Product other)) return false;
return sku.equals(other.sku); // identity by business key
}
@Override public int hashCode() { return Objects.hash(sku); }
}
public static void main(String[] args) {
Plain p = new Plain();
System.out.println("default toString starts with class name: " + p.toString().startsWith("ObjectMethods$Plain@"));
System.out.println("default equals is identity: " + p.equals(new Plain()));
Product a = new Product("SKU-1", "Keyboard");
Product b = new Product("SKU-1", "Keyboard (new box)");
System.out.println(a);
System.out.println("a.equals(b): " + a.equals(b));
System.out.println("same hashCode: " + (a.hashCode() == b.hashCode()));
System.out.println("HashSet size: " + new HashSet<>(List.of(a, b)).size());
System.out.println("getClass(): " + a.getClass().getSimpleName());
}
}
default toString starts with class name: true default equals is identity: false Product[SKU-1, Keyboard] a.equals(b): true same hashCode: true HashSet size: 1 getClass(): Product
- The default
toString()output is rarely useful; override it for logging and debugging. - The default
equals()compares references. Override it when two different objects can represent the same value, here products with the same SKU. - Because
equalsandhashCodeagree, theHashSetcorrectly keeps only one product.
3. The equals and hashCode Contract
equals must be:
- Reflexive:
x.equals(x)is true. - Symmetric:
x.equals(y)equalsy.equals(x). - Transitive: if x equals y and y equals z, then x equals z.
- Consistent, and
x.equals(null)is false.
And: equal objects must have equal hash codes. Breaking this makes hash-based collections fail:
import java.util.HashSet;
import java.util.Set;
public class EqualsHashCodeBug {
static class Point {
final int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
@Override public boolean equals(Object o) {
return o instanceof Point p && x == p.x && y == p.y;
}
// hashCode NOT overridden: equal points get different hash codes
}
record FixedPoint(int x, int y) { } // records generate equals AND hashCode
public static void main(String[] args) {
Set<Point> points = new HashSet<>();
points.add(new Point(1, 2));
System.out.println("broken contains: " + points.contains(new Point(1, 2)));
Set<FixedPoint> fixed = new HashSet<>();
fixed.add(new FixedPoint(1, 2));
System.out.println("record contains: " + fixed.contains(new FixedPoint(1, 2)));
}
}
broken contains: false record contains: true
Records, IDEs and Lombok generate correct implementations. Unequal objects may share a hash code (a collision), but fewer collisions mean faster lookups.
4. getClass()
getClass() returns the Class object describing the object's runtime type, used for reflection, logging and type checks: obj.getClass().getName(), getSimpleName() or getSuperclass(). It is final and native.
5. clone(), wait/notify and finalize()
clone()is protected and works only withCloneable; copy constructors are usually better. See Object Cloning.wait(),notify(),notifyAll()coordinate threads using the monitor every object has. See Synchronization.finalize()is deprecated for removal; use try-with-resources orCleaner. See Garbage Collection.
6. The java.util.Objects Helper Class
Objects (Java 7) provides null-safe static helpers for the Object methods and argument checking:
import java.util.Objects;
public class ObjectsUtility {
public static void main(String[] args) {
String a = null, b = "java";
System.out.println("Objects.equals(null, \"java\") : " + Objects.equals(a, b));
System.out.println("Objects.hash(\"a\", 1) equal : " + (Objects.hash("a", 1) == Objects.hash("a", 1)));
System.out.println("Objects.toString(null, \"n/a\") : " + Objects.toString(a, "n/a"));
System.out.println("requireNonNullElse : " + Objects.requireNonNullElse(a, "default"));
System.out.println("isNull / nonNull : " + Objects.isNull(a) + " / " + Objects.nonNull(b));
try {
Objects.requireNonNull(a, "name must not be null");
} catch (NullPointerException e) {
System.out.println("requireNonNull : " + e.getMessage());
}
}
}
Objects.equals(null, "java") : false
Objects.hash("a", 1) equal : true
Objects.toString(null, "n/a") : n/a
requireNonNullElse : default
isNull / nonNull : true / true
requireNonNull : name must not be null
7. Why Have a Common Root Class?
- Collections and other generic code can store and handle any object.
- Every object can be printed, compared and hashed in a uniform way.
- Every object can serve as a lock for
synchronized.
8. Summary
Objectis the superclass of every Java class and array.- Override
toStringfor readable output, andequalswithhashCodefor value equality. - Equal objects must have equal hash codes, or hash-based collections break.
getClass,waitandnotifyare final;finalizeis deprecated.java.util.Objectsoffers null-safe helpers.
Related Tutorials
Frequently Asked Questions (FAQ)
Q1. What is the Object class in Java?
The class at the top of the hierarchy, java.lang.Object, which every class extends directly or indirectly.
Q2. Which methods does the Object class have?
toString, equals, hashCode, getClass, clone, wait (three overloads), notify, notifyAll and the deprecated finalize.
Q3. Why should I override toString()?
The default prints the class name and hash code. A custom toString makes logs, debugging and error messages meaningful.
Q4. What does the default equals() do?
It compares references, returning true only for the same object.
Q5. Why must hashCode be overridden with equals?
Hash-based collections use hashCode to find buckets. If equal objects have different hash codes, lookups fail.
Q6. Can two unequal objects have the same hash code?
Yes. That is a collision and is allowed, although good hash functions minimise it.
Q7. Is Object the superclass of arrays?
Yes. Arrays are objects and inherit Object's methods; they also implement Cloneable and Serializable.
Q8. Which Object methods are final?
getClass, notify, notifyAll and all wait methods.
Q9. Why are wait and notify in Object and not Thread?
Because every object has a monitor that threads lock on; waiting and notifying are tied to the lock, not to a thread.
Q10. What does getClass() return?
The runtime Class object of the instance, even when the reference has a more general type.
Q11. Should equals use instanceof or getClass()?
instanceof allows subclasses to be equal and is fine for final classes; getClass() requires exactly the same class and keeps symmetry in class hierarchies.
Q12. Do records override Object methods?
Yes. Records automatically implement equals, hashCode and toString from their components.
Q13. What is Objects.requireNonNull used for?
Validating that an argument is not null, throwing a NullPointerException with a helpful message immediately.
Q14. What is the difference between Objects.equals and a.equals(b)?
Objects.equals(a, b) is null-safe; a.equals(b) throws a NullPointerException if a is null.
Q15. Does Object have a constructor?
Yes, a public no-argument constructor, which runs at the top of every constructor chain.
Q16. What does the default hashCode return?
An identity-based value chosen by the JVM, stable for the object's lifetime but not related to its memory address in modern JVMs.
Q17. Is finalize() still called?
It may be, but it is deprecated for removal and can be disabled. Do not rely on or override it.
Q18. Can I use Object as a generic type?
Yes, List<Object> holds anything, but you lose type safety; prefer specific types or wildcards.
Q19. What happens if equals is not symmetric?
Collections behave unpredictably, for example contains may give different answers depending on argument order.
Q20. Which package contains the Object class?
java.lang, which is imported automatically.