What does the default Object.equals() method compare, and how does that differ from the == operator?
answer
- Default equals == identity == ==
- == compares primitives by value, references by identity
- Override equals to get value/content comparison
- new String("a") == new String("a") is false
- Integer cache -128..127 trap
basics
~10 sBy default, equals() checks whether two references point to the exact same object in memory — the same as ==. To compare by value (content), you must override equals() yourself.
solid answer
~40 sObject.equals() is inherited by every class and, unless overridden, does exactly what == does for references: it returns true only when both references point to the same object instance (reference identity). The == operator compares primitives by value and references by identity. So for objects, default equals() and == behave identically. The difference matters when you override equals() to compare logical content (e.g., two Strings with the same characters): then equals() returns true for distinct-but-equal objects, while == is still false because they are separate instances. Value-like classes (String, Integer, your own DTOs) override equals() to compare fields; the result is that you compare meaning with equals() and identity with ==. Forgetting this is why new String("a") == new String("a") is false but .equals() is true.
code
java · 12 lines// Object's default equals — just identity:
// public boolean equals(Object o) { return this == o; }
String a = new String("hi");
String b = new String("hi");
System.out.println(a == b); // false -> different objects
System.out.println(a.equals(b)); // true -> String overrides equals (compares chars)
Object p = new Object();
Object q = new Object();
System.out.println(p.equals(q)); // false -> Object.equals == identitygo deeper
Knows == is identity for objects and that you override equals() for value comparison; can recite the new String example.
Explains the literal pool, the Integer cache pitfall, and that default equals() is literally this == obj.
Connects identity vs. value equality to defensive coding (always .equals() for wrappers/strings) and to the equals/hashCode pairing.
Frames identity-vs-value as a design choice — when value semantics are wrong (entities with identity, mutable keys) and the maintainability cost of overriding equals across a domain model.
## The setup: references vs. objects In Java, a variable of a class type holds a **reference** — essentially the address of an object on the heap, not the object itself. Two variables can hold the *same* reference (point to one object) or two *different* references that happen to point to objects with identical contents. ## The `==` operator `==` has two meanings: - For **primitives** (`int`, `double`, `char`, `boolean`, …) it compares the actual values: `3 == 3` is true. - For **references** (any object type) it compares the references themselves — i.e. *identity*: it is true only if both sides point to the very same object in memory. So `a == b` for objects asks "are these the same object?", never "do these have the same contents?". ## `Object.equals()` — the default Every class in Java implicitly extends `java.lang.Object`, which defines: ```java public boolean equals(Object obj) { return (this == obj); } ``` That is literally it. The **default** `equals()` is just `==` under the hood: reference identity. So if you never override `equals()`, calling `a.equals(b)` returns true only when `a` and `b` are the same object — exactly what `==` would tell you. ## Why override it Often you want **logical (value) equality**: two `Point` objects with the same `x` and `y` should be "equal" even though they are separate instances. To get that, you **override** `equals()` to compare the relevant fields. The standard library does this for you in `String`, the wrapper types (`Integer`, `Long`, …), `BigDecimal`, collections, etc. Classic demonstration: ```java String a = new String("hi"); String b = new String("hi"); a == b; // false — two distinct objects a.equals(b); // true — String overrides equals to compare characters ``` `new String(...)` forces a fresh object, so identity differs, but content is equal. ## The mental model - Use `==` to ask **"same object?"** (identity). - Use `.equals()` to ask **"same value/meaning?"** (logical equality) — *but only if the class overrides it*; otherwise `.equals()` falls back to identity. ## A common trap: caching Small `Integer` values (-128..127) are cached, so `Integer x = 100; Integer y = 100; x == y` is true, but `Integer x = 200; Integer y = 200; x == y` is false. This is *not* a special equals rule — it's autoboxing reusing cached objects. Always use `.equals()` (or `.intValue()`) for wrapper comparison to avoid depending on the cache. ## Takeaway The default `equals()` is reference identity, equivalent to `==`. Value comparison is something a class opts into by overriding `equals()` (and, by contract, `hashCode()`).
- Why is new String("a") == new String("a") false but "a" == "a" true?String literals are interned into a shared pool, so two identical literals refer to the same cached object (identity true). new String(...) explicitly allocates a fresh object outside the pool, so the two references differ.
- If a class does not override equals(), what does HashSet use to detect duplicates?It falls back to Object.equals (identity) plus Object.hashCode (identity hash), so only the exact same instance is treated as a duplicate; two value-equal-but-distinct objects are both stored.
Two photocopies of the same letter: == asks 'is it the literal same sheet of paper?' (identity); a value-aware equals asks 'do they say the same thing?' (content). Default equals only checks the sheet of paper.
saying these in an interview costs you the question
- Saying == compares object contents — it compares identity for references.
- Believing equals() always does value comparison even without an override.
- Claiming Integer == Integer always works because of caching (it breaks outside -128..127).
- Confusing the String literal pool with overriding equals.