skip to content

What is the difference between == and .equals() when comparing objects in Java?

level: middleimportance: must knowfreq 95%

answer

  1. == = identity (same object); .equals() = value/content
  2. String == is a trap; literals are interned but new/runtime strings aren't
  3. Object.equals defaults to ==; override needs hashCode too
  4. x.equals(null) is false; z.equals(...) on null z throws NPE
  5. Objects.equals(a,b) is null-safe; use == for enums and null checks

basics

~20 s

For objects, == checks whether two variables point to the very same object (identity). .equals() checks whether two objects are meaningfully equal in content. Use .equals() for value comparison, like comparing the text of two Strings.

solid answer

~40 s

When the operands are object references, == compares the references themselves — it is true only if both variables refer to the exact same object in memory (identity). It does not look inside the objects. .equals() is a regular method that, when properly overridden, compares the contents (value equality). The classic bug is comparing strings with ==: two distinct String objects with the same characters are not == but are .equals(). For value types you almost always want .equals(). To compare safely against a possibly-null reference, call equals on the known-non-null side or use Objects.equals(a, b), which handles nulls. Note Object's default .equals() falls back to identity (==), so a class only gets meaningful value equality if it overrides equals (and, by contract, hashCode).

code

java · 11 lines
java
String x = "hi";
String y = new String("hi");

System.out.println(x == y);              // false: different objects
System.out.println(x.equals(y));         // true:  same content
System.out.println(java.util.Objects.equals(x, y)); // true, and null-safe

String z = null;
System.out.println(z == null);           // true,  safe
// System.out.println(z.equals("hi"));   // would throw NullPointerException
System.out.println("hi".equals(z));      // false, no exception

go deeper

for a junior

States that == is identity and .equals() is content, and that Strings need .equals().

for a middle

Explains interning, the default Object.equals = identity, and null-safety idioms.

for a senior

Knows the full equals/hashCode contract and the consequences for hash-based collections; uses == correctly for enums.

for a principal

Reasons about value-based classes (records), equality design across inheritance (symmetry/Liskov pitfalls), and identity-vs-value as an API design decision.

## The core split: identity vs value An **object** lives on the heap; a variable of a class type holds a **reference** (a handle pointing to that object), not the object itself. - **`==` on references** compares the *references*: it is `true` only when both sides point to the **same single object**. This is **identity** (also called reference equality). - **`.equals(Object)`** is an ordinary **method** (not an operator). When a class overrides it, it compares the objects' **contents** — this is **value equality**. ```java String a = new String("hi"); String b = new String("hi"); System.out.println(a == b); // false — two distinct objects System.out.println(a.equals(b)); // true — same characters ``` ## Why the String trap is so common String literals are **interned** (pooled): `"hi" == "hi"` is often `true` because both refer to the one pooled instance. But the moment a String comes from `new`, user input, concatenation at runtime, or a substring, you get a fresh object and `==` breaks. Relying on interning is a latent bug — **always use `.equals()` (or `equals` /`Objects.equals`) for String content**. ## The default equals is identity The `equals` defined on `java.lang.Object` is literally `this == that`. So if a class does **not override** `equals`, calling `.equals()` gives you the same answer as `==` (identity). A class earns value equality only by **overriding `equals`** — and the contract requires you to override **`hashCode`** consistently too, or hash-based collections (`HashMap`, `HashSet`) will misbehave. ## The equals contract (what a correct override guarantees) A correct `equals` is: **reflexive** (`x.equals(x)`), **symmetric** (`x.equals(y) == y.equals(x)`), **transitive**, **consistent** (same result on repeated calls), and `x.equals(null)` is **false**. ## Null safety `==` is null-safe: `x == null` never throws. But `x.equals(y)` throws `NullPointerException` if `x` is null. Two safe idioms: ```java "CONST".equals(userValue); // call on the known-non-null literal Objects.equals(a, b); // null-tolerant: true if both null, else a.equals(b) ``` ## When you actually want == `==` is the right tool for: comparing against `null`, comparing **enum** constants (each enum value is a singleton, so `==` is safe and even preferred), and deliberate identity checks (e.g. "is this the same cache instance?"). Everything else value-like wants `.equals()`. ## Why it matters This is the single most common Java interview question and a top source of real bugs: a `==` on Strings that passes in tests (interned literals) but fails in production (runtime-built strings).

  • Should you compare enum constants with == or .equals()?
    Prefer ==. Each enum constant is a singleton, so == is correct, null-safe, and fails fast at compile time on type mismatch; equals just delegates to == anyway.
  • Why must you override hashCode whenever you override equals?
    The contract says equal objects must have equal hash codes. If you break that, HashMap/HashSet may store duplicates or fail to find entries, because they bucket by hashCode before checking equals.
  • How does Objects.equals(a, b) handle nulls?
    It returns true if both are null, false if exactly one is null, otherwise a.equals(b). This avoids NPEs without writing manual null checks.

saying these in an interview costs you the question

  • Using == to compare String contents
  • Forgetting to override hashCode when overriding equals
  • Calling equals on a possibly-null reference without guarding
  • Believing .equals() always compares contents even when not overridden

context