Java Notes
Complete guide to the java.lang.Object class — toString(), equals(), hashCode(), clone(), getClass(), finalize(), and how every Java class inherits from Object.
java.lang.Object is the root class of the entire Java class hierarchy. Every class in Java — whether you write extends Object or not — inherits from Object. It provides essential methods that every object needs.
// These are EQUIVALENT:
public class MyClass { }
public class MyClass extends Object { }Methods of Object Class
| Method | Purpose | Override? |
|---|---|---|
toString() | String representation | Always |
equals(Object) | Content equality | When needed |
hashCode() | Hash for collections | With equals() |
getClass() | Runtime type info | Never (final) |
clone() | Object copying | When needed |
finalize() | Pre-GC cleanup | Never (deprecated) |
wait() | Thread synchronization | Rarely |
notify() | Thread synchronization | Rarely |
notifyAll() | Thread synchronization | Rarely |
toString() — String Representation
Default toString() returns ClassName@hexHashCode, which is useless:
public class Student {
private String name;
private int age;
private double gpa;
public Student(String name, int age, double gpa) {
this.name = name;
this.age = age;
this.gpa = gpa;
}
}
public class Main {
public static void main(String[] args) {
Student s = new Student("Alice", 20, 3.8);
System.out.println(s); // Default: Student@1b6d3586 (useless!)
}
}Always override toString():
public class Student {
private String name;
private int age;
private double gpa;
public Student(String name, int age, double gpa) {
this.name = name;
this.age = age;
this.gpa = gpa;
}
@Override
public String toString() {
return String.format("Student{name='%s', age=%d, gpa=%.1f}", name, age, gpa);
}
}
public class Main {
public static void main(String[] args) {
Student s = new Student("Alice", 20, 3.8);
System.out.println(s); // Student{name='Alice', age=20, gpa=3.8}
// toString() is called automatically in:
System.out.println("Student: " + s); // String concatenation
String.format("Data: %s", s); // Format
System.out.printf("Info: %s%n", s); // Printf
}
}Student{name='Alice', age=20, gpa=3.8}
Student: Student{name='Alice', age=20, gpa=3.8}equals() — Content Equality
Default equals() uses == (reference comparison). Override for meaningful comparison:
p1 == p2: false p1.equals(p2): true p1.equals(p3): false p1 == p4: true
The equals() Contract
Your equals() must be:
- Reflexive:
x.equals(x)is always true - Symmetric: if
x.equals(y)theny.equals(x) - Transitive: if
x.equals(y)andy.equals(z)thenx.equals(z) - Consistent: multiple calls return same result (if objects unchanged)
- Null-safe:
x.equals(null)is always false
hashCode() — Hash for Collections
Rule: If you override equals(), you MUST override hashCode().
Equal objects must have the same hash code. This is critical for HashMap, HashSet, etc.
Team size: 2 E001: Alice (Engineering) E002: Bob (Marketing) Alice's salary: $95000.0
getClass() — Runtime Type Information
public class Main {
public static void main(String[] args) {
Object obj = "Hello";
System.out.println(obj.getClass()); // class java.lang.String
System.out.println(obj.getClass().getName()); // java.lang.String
System.out.println(obj.getClass().getSimpleName()); // String
// Useful for type checking
Animal animal = new Dog("Rex", 3);
System.out.println(animal.getClass().getSimpleName()); // Dog
// getClass() vs instanceof
System.out.println(animal instanceof Animal); // true
System.out.println(animal instanceof Dog); // true
System.out.println(animal.getClass() == Animal.class); // false (exact class is Dog)
System.out.println(animal.getClass() == Dog.class); // true
}
}class java.lang.String java.lang.String String Dog true true false true
clone() — Object Copying
To use clone(), your class must implement Cloneable and override clone():
public class Address implements Cloneable {
private String city;
private String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
@Override
public Address clone() {
try {
return (Address) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() { return street + ", " + city; }
public void setCity(String city) { this.city = city; }
}
public class Person implements Cloneable {
private String name;
private int age;
private Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// Shallow clone — address is shared!
@Override
public Person clone() {
try {
Person cloned = (Person) super.clone();
cloned.address = this.address.clone(); // Deep clone the address!
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() {
return name + ", " + age + ", " + address;
}
}
public class Main {
public static void main(String[] args) {
Person original = new Person("Alice", 30, new Address("NYC", "123 Main St"));
Person copy = original.clone();
System.out.println("Original: " + original);
System.out.println("Copy: " + copy);
// Modify copy's address
copy.address.setCity("LA");
System.out.println("After modifying copy:");
System.out.println("Original: " + original); // Unchanged (deep clone)
System.out.println("Copy: " + copy);
}
}Original: Alice, 30, 123 Main St, NYC Copy: Alice, 30, 123 Main St, NYC After modifying copy: Original: Alice, 30, 123 Main St, NYC Copy: Alice, 30, 123 Main St, LA
Complete Example: Well-Designed Class
Common Mistakes
- Overriding equals() without hashCode():
``java // ❌ HashMap/HashSet will BREAK @Override public boolean equals(Object o) { ... } // hashCode() still returns default — different for equal objects! ``
- Using == instead of equals() for objects:
``java String a = new String("test"); String b = new String("test"); if (a == b) { } // ❌ false — different references if (a.equals(b)) { } // ✅ true — same content ``
- Wrong equals() signature:
``java // ❌ This OVERLOADS (not overrides) Object.equals(Object)! public boolean equals(Point other) { ... } // ✅ Correct signature @Override public boolean equals(Object obj) { ... } ``
- toString() not overridden:
``java System.out.println(myObject); // "MyClass@7852e922" — useless for debugging! ``
Best Practices
- Always override
toString()— it helps with debugging immensely - Override
equals()when objects have logical equality (same ID, same content) - Always override
hashCode()when you overrideequals() - Use
Objects.hash()andObjects.equals()for clean implementations - Consider making classes
finalor documenting the equals() contract for subclasses - Prefer copy constructor over
clone()for object copying - Use Java records (Java 16+) for simple data classes — they auto-generate these methods
Interview Questions
Q1: Why must hashCode() be overridden when equals() is overridden? Collections like HashMap use hashCode to find the bucket, then equals to confirm identity. If equal objects have different hash codes, HashMap won't find them in the right bucket.
Q2: Can two unequal objects have the same hashCode()? Yes. This is called a hash collision and is normal. But equal objects MUST have the same hash code.
Q3: What does getClass() return? The runtime Class<?> object representing the actual type. Useful for reflection and exact type checking.
Q4: Why is finalize() deprecated? It's unreliable (GC timing is unpredictable), causes performance issues, and can resurrect objects. Use try-with-resources and Cleaner instead.
Q5: What is the difference between shallow and deep cloning? Shallow: copies fields directly (references still point to same objects). Deep: recursively clones all referenced objects too.
Q6: Why does Object have wait() and notify()? For thread synchronization. Any object can serve as a monitor/lock. These methods enable threads to coordinate via shared objects.
Q7: Can we prevent a class from being used as a HashMap key? Technically no, but if equals/hashCode aren't properly overridden, it won't work correctly as a key. The default identity-based behavior might not be what you want.
Q8: What is the contract between equals() and hashCode()? If two objects are equal via equals(), they MUST have the same hashCode(). If hashCodes differ, objects MUST be unequal. Equal hashCodes don't guarantee equality (collisions allowed). Breaking this contract causes HashMap/HashSet to malfunction.
Q9: What does the finalize() method do? Why is it deprecated? finalize() was called by GC before reclaiming memory. It's deprecated (Java 9+) because: (1) no guarantee when/if it runs, (2) performance overhead, (3) can resurrect objects, (4) runs on a single thread causing bottlenecks. Use try-with-resources or Cleaner instead.
Q10: Explain wait(), notify(), and notifyAll() from Object class. These are for inter-thread communication. wait() releases the lock and suspends the thread until notified. notify() wakes one waiting thread. notifyAll() wakes all waiting threads. All three must be called from synchronized context. They enable producer-consumer patterns.
Q11: What is the getClass() method used for? getClass() returns the runtime class (Class<?> object) of the instance. Used for reflection, type checking, and logging. Unlike instanceof, it checks exact type: obj.getClass() == MyClass.class is true only for MyClass, not subclasses.
Q12: Why is clone() method in Object class protected? It's protected to prevent arbitrary cloning. Classes must explicitly opt-in by: (1) implementing Cloneable marker interface, and (2) overriding clone() with public access. This design ensures classes control their own cloning behavior.
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Object Class in Java.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Java Master Course topic.
Search Terms
java-master-course, java master course, java, master, course, oops, object, class
Related Java Master Course Topics