By WebNest Studio Engineering Team
Java - Core Tutorial
Object Class and Its Methods
Every class in Java, whether you write extends explicitly or not, ultimately extends java.lang.Object. This makes Object the root of the entire class hierarchy, and it guarantees that every Java object automatically inherits a small set of universally useful methods.
Why it matters
Every class inherits equals(), hashCode() and toString() from Object, and the default versions are rarely what you want: two objects with the same data are "not equal", and printing one shows a class name and a number. Overriding these correctly is what makes your objects work in a HashSet, as HashMap keys, and in log output.
Watch: Java Object Class Explained 🧬 toString, equals, hashCode, clone & More | Java Core Tutorial
A detailed guide to java.lang.Object and its methods, including toString(), equals(), hashCode(), clone(), getClass(), and the wait/notify methods.
Core Methods Inherited from Object
A handful of Object methods are overridden constantly in everyday Java code, because their default behavior is rarely what a class actually needs.
toString()— returns a String representation of the object; the default prints the class name plus a hash code in hex, which is rarely useful and almost always worth overriding.equals(Object obj)— compares two objects for logical equality; the default implementation only checks reference identity (the same object in memory).hashCode()— returns an integer used by hash-based collections like HashMap and HashSet to bucket objects efficiently.getClass()— returns the runtime Class object representing the object's actual class, useful for reflection and type checks.
The equals() and hashCode() Contract
Java requires a strict rule: if two objects are equal according to equals(), they must return the same value from hashCode(). The reverse is not required — equal hash codes do not imply the objects are equal (that is simply a hash collision). Breaking this contract by overriding one method but not the other causes subtle bugs where an object that "equals" another cannot be found in a HashMap or HashSet, because it lands in the wrong bucket.
Overriding These Methods Correctly
When you override equals(), you should also override hashCode() using the same fields that participate in the equality check, so the contract holds. Most IDEs and the java.util.Objects helper class (Objects.equals(), Objects.hash()) make this straightforward and less error-prone than writing it by hand.
Example
Overriding toString, equals, and hashCode consistently on a Point class
import java.util.Objects;
class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Point(" + x + ", " + y + ")";
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Point)) return false;
Point other = (Point) obj;
return x == other.x && y == other.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
public class ObjectMethodsDemo {
public static void main(String[] args) {
Point p1 = new Point(1, 2);
Point p2 = new Point(1, 2);
System.out.println(p1);
System.out.println(p1.equals(p2));
System.out.println(p1.hashCode() == p2.hashCode());
System.out.println(p1.getClass().getName());
}
}
Point(1, 2)
true
true
Point
Common Mistakes
- Overriding equals() without also overriding hashCode(), which silently breaks HashMap and HashSet lookups.
- Using "==" to compare object content instead of equals() — "==" checks reference identity, not logical equality.
- Forgetting to check "obj instanceof Point" (or a null check) inside equals(), risking a ClassCastException or NullPointerException.
- Relying on the default toString() output in logs or debugging instead of overriding it with meaningful field values.
Exercise
Two Email objects should be equal when their addresses match, ignoring upper and lower case. At the moment the program prints false. Override equals() and hashCode() so it prints true.
import java.util.HashSet;
import java.util.Set;
class Email {
private final String address;
Email(String address) {
this.address = address;
}
// TODO: override equals() to compare addresses ignoring case
// TODO: override hashCode() so equal emails have the same hash code
}
public class EmailDemo {
public static void main(String[] args) {
Set<Email> seen = new HashSet<>();
seen.add(new Email("Asha@Example.com"));
System.out.println(seen.contains(new Email("asha@example.com")));
}
}
Show hints
- In
equals, check that the other object is anEmail, then compare withequalsIgnoreCase. - The hash code must ignore case too: use
address.toLowerCase().hashCode().
Show solution
import java.util.HashSet;
import java.util.Set;
class Email {
private final String address;
Email(String address) {
this.address = address;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof Email)) {
return false;
}
return address.equalsIgnoreCase(((Email) other).address);
}
@Override
public int hashCode() {
return address.toLowerCase().hashCode();
}
}
public class EmailDemo {
public static void main(String[] args) {
Set<Email> seen = new HashSet<>();
seen.add(new Email("Asha@Example.com"));
System.out.println(seen.contains(new Email("asha@example.com"))); // true
}
}
Quick quiz
1. What does the default equals() inherited from Object compare?
- A. The values of all fields
- B. Whether both references point to the same object
- C. The class names
- D. The hash codes only
Show answer
B. Object.equals() behaves like ==. Two separate objects are not equal until the class overrides equals().
2. What does the default toString() return?
- A. The field values
- B. An empty string
- C. The class name, an @ sign and the hash code in hexadecimal
- D. null
Show answer
C. For example Point@1b6d3586. Override toString() to return something meaningful.
3. If two objects are equal according to equals(), what must be true of their hash codes?
- A. They must be different
- B. There is no requirement
- C. They must both be zero
- D. They must be equal
Show answer
D. Equal objects must return the same hash code. Unequal objects may share one, although that makes hash tables slower.
Interview questions
What is the contract between equals() and hashCode()?
Show answer
If two objects are equal according to equals(), they must return the same hashCode(). The reverse is not required: two unequal objects may have the same hash code. The hash code must also stay the same for as long as the fields used in equals() do not change.
What goes wrong if you override equals() but not hashCode()?
Show answer
Hash-based collections stop working for that class. A HashSet or HashMap uses the hash code to choose a bucket before it ever calls equals(). Two equal objects with different default hash codes end up in different buckets, so contains() returns false and a set can hold duplicates.
Key Points to Remember
- Every class implicitly extends Object and inherits toString(), equals(), hashCode(), and getClass().
- The default equals() checks reference identity only; override it for logical equality.
- Equal objects (by equals()) must produce equal hashCode() values.
- Objects.equals() and Objects.hash() simplify writing correct, contract-respecting overrides.
Go deeper
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering