Object Class in Java and Its Methods
The Object class (java.lang.Object) is the root of the Java class hierarchy. Every class implicitly extends Object, even if you never write extends Object, and Object is the only class in Java with no superclass. As a result, every class inherits the methods of the Object class: equals(), hashCode(), toString(), getClass(), clone(), wait(), notify(), and the rest. Arrays inherit them too. Primitive types (int, double, and so on) are not objects and do not have these methods.
All methods of the Object class: a table
The Object class declares 11 public and protected methods. You can override the ones that aren't marked final. The six final methods can't be overridden.
| Method | Modifiers | Purpose | Can it be overridden? |
|---|---|---|---|
boolean equals(Object obj) | public | Checks whether another object is equal to this one | Yes, often |
int hashCode() | public | Returns the object's hash code. HashMap, HashSet, and other hash-based collections use it | Yes, always together with equals() |
String toString() | public | Returns a string representation of the object | Yes, often |
Class<?> getClass() | public final | Returns the runtime class of the object | No |
Object clone() | protected | Creates a shallow copy of the object. The class must implement Cloneable, or a CloneNotSupportedException is thrown | Yes, rarely |
void finalize() | protected, deprecated for removal | The garbage collector may call it before reclaiming the object. There is no guarantee when, or whether, it runs | Not recommended |
void notify() | public final | Wakes up one thread waiting on this object's monitor | No |
void notifyAll() | public final | Wakes up all threads waiting on this object's monitor | No |
void wait() | public final | Makes the current thread wait until another thread calls notify()/notifyAll() or the thread is interrupted | No |
void wait(long timeout) | public final | Same, but waits at most timeout milliseconds | No |
void wait(long timeout, int nanos) | public final | Same, with an extra timeout in nanoseconds | No |
finalize() is on its way out
finalize() has been @Deprecated since Java 9 and deprecated for removal since Java 18 (JEP 421). JDK 18+ also lets you switch finalization off entirely with --finalization=disabled. The garbage collector never guarantees when, or whether, it runs. To release resources, use try-with-resources with AutoCloseable, or java.lang.ref.Cleaner for rare edge cases. See Garbage Collection in Java to learn how the collector decides when to reclaim an object.
Object as a universal reference type
Every class is a subclass of Object, so a variable of type Object can hold a reference to any object: a string, an array, a boxed number, or an instance of your own class. Primitives get autoboxed when you assign them.
Object o1 = "Hello"; // String
Object o2 = new int[]{1, 2, 3}; // arrays are objects too
Object o3 = 42; // autoboxed to Integer
Object[] mixed = {o1, o2, o3};
System.out.println(o3.getClass().getName()); // java.lang.Integer You can only call Object's own methods through an Object reference. To use the methods of the real class, you need to cast (for example, with instanceof pattern matching). Object is also a regular, non-abstract class, so new Object() is valid. Its most common use is as a private lock object for synchronized.
Which Object methods you actually override
In everyday development you usually override three methods of the Object class:
- equals() to compare objects by their field values instead of by reference;
- hashCode(), always together with
equals(), or objects will misbehave inHashMapandHashSet; - toString() to get a readable representation in logs, the debugger, and print statements.
getClass(), wait(), notify(), and notifyAll() are declared final, so you can't override them. clone() is rarely overridden. A copy constructor or a static factory method is usually a cleaner choice.
How default equals, hashCode, and toString work
A class that doesn't override these methods uses the implementation it inherits from Object:
equals()compares references, so it behaves exactly like==;hashCode()returns an identity hash code tied to the specific instance, so two different objects with equal fields almost always get different hash codes;toString()returnsgetClass().getName() + "@" + Integer.toHexString(hashCode()).
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public static void main(String[] args) {
Point a = new Point(1, 2);
Point b = new Point(1, 2);
System.out.println(a.equals(b)); // false - references are compared
System.out.println(a); // Point@1b6d3586 (hash will differ)
System.out.println(a.getClass()); // class Point
}
} Now override the methods so that points with the same coordinates count as equal:
import java.util.Objects;
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point other = (Point) o;
return x == other.x && y == other.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
@Override
public String toString() {
return "Point{x=" + x + ", y=" + y + "}";
}
} Now new Point(1, 2).equals(new Point(1, 2)) returns true, a HashSet finds an equal point, and System.out.println prints Point{x=1, y=2}. This is the core pattern of classes and objects in Java: fields hold the state, and the methods you override decide how instances compare and print. For the basics, see Classes and Objects in Java.
A modern alternative
Since Java 16, you can declare a simple data-carrier class as record Point(int x, int y) {}. The compiler then generates equals(), hashCode(), and toString() from all the components automatically.
The equals() and hashCode() contract
The Object documentation defines rules that every equals() override must follow. For any non-null references x, y, and z:
- Reflexive:
x.equals(x)istrue. - Symmetric:
x.equals(y)istrueif and only ify.equals(x)istrue. - Transitive: if
x.equals(y)andy.equals(z), thenx.equals(z). - Consistent: repeated calls return the same result as long as the objects don't change.
- Null:
x.equals(null)is alwaysfalseand never throws.
The hashCode() contract adds one key rule: equal objects must have equal hash codes. The reverse doesn't have to hold. Two unequal objects may share a hash code (a collision). That only hurts performance, not correctness.
The wait, notify, and notifyAll methods
wait(), notify(), and notifyAll() are used for inter-thread communication. They live in Object because any Java object can act as a monitor (a lock object). You can only call them inside a synchronized block or method on that same object. Otherwise, you get an IllegalMonitorStateException.
synchronized (lock) {
while (!ready) {
lock.wait(); // releases the monitor and waits; throws InterruptedException
}
}
// in another thread
synchronized (lock) {
ready = true;
lock.notifyAll(); // wakes up all waiting threads
} Always call wait() inside a while loop that re-checks the condition, not inside an if. A thread can wake up without any notify call (a spurious wakeup), or another thread can change the condition before this one gets the monitor back. In new code, higher-level tools from java.util.concurrent (BlockingQueue, CountDownLatch, Condition) usually replace hand-written wait/notify.
Where developers get tripped up
- Overriding equals() without hashCode(). Equal objects end up in different buckets of the hash table, so
HashSet.contains()orHashMap.get()can't find them. - Overloading instead of overriding. A method
equals(Point p)is an overload, not an override, and collections always callequals(Object). The@Overrideannotation catches this at compile time. - Using mutable fields in hashCode(). If a field changes after the object is placed in a
HashSetor used as aHashMapkey, the object gets "lost" in its old bucket. - getClass() vs instanceof in equals().
getClass()treats a subclass instance as never equal to a parent instance.instanceofallows it, but that can break symmetry if the subclass adds fields to the comparison. Pick one approach on purpose. - Calling wait() outside synchronized. You get an
IllegalMonitorStateExceptionat runtime. - Relying on finalize() to release resources. The call is never guaranteed. Use
try-with-resourcesinstead. - Calling clone() on a class that doesn't implement Cloneable. The default implementation throws
CloneNotSupportedException. Even when it works, the copy is shallow, so reference fields are shared with the original.
Related topics
- How fields and behavior come together in a class: Classes and Objects in Java.
- Why every class, including yours, inherits from Object: Java Inheritance and the extends Keyword.
- How the JVM decides when to reclaim objects: Garbage Collection in Java.
Frequently Asked Questions
How many methods does the Object class have in Java?
The Object class has 11 methods that subclasses inherit: equals(), hashCode(), toString(), getClass(), clone(), finalize(), notify(), notifyAll(), and three overloads of wait(). Six of them (getClass, notify, notifyAll, and the three wait() overloads) are final and can't be overridden.
What is the difference between == and equals() in Java?
For objects, == checks whether two references point to the same instance. equals() checks logical equality as the class defines it. Object's default equals() just uses ==, so the two only differ once a class overrides equals(), as String, Integer, and records do.
Why do you need to override hashCode() when you override equals()?
The contract says objects that are equal according to equals() must have the same hashCode(). Hash-based collections find a bucket by hash code first and only then compare with equals(). Without a matching hashCode(), a HashMap or HashSet won't find an equal object.
Can you create an instance of the Object class?
Yes. Object isn't abstract and has a public no-argument constructor, so new Object() is valid. Such an instance has no state of its own and is mostly used as a private lock for synchronized blocks.
Do interfaces inherit the Object class?
No, an interface can't extend a class. However, the Java Language Specification says an interface with no superinterfaces implicitly declares abstract versions of Object's public methods, so you can still call equals(), hashCode(), or toString() on a variable of an interface type. You can't provide them as default methods in an interface.
Why are wait() and notify() in Object instead of Thread?
In Java, any object can act as a monitor, and waiting and notifying are tied to the monitor, not to a thread. A thread waits on a specific lock object, and another thread wakes it up through that same object.
Comments