skip to content

Object Identity vs Equality

The difference between == comparing references and equals comparing logical value. This is the source of the classic string-comparison bug, so almost every Java screen touches it in some form.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

In Java, what is the difference between == and the equals() method when comparing two object references?

level: juniorimportance: must knowfreq 90%

answer

  1. == = identity (same heap object); equals() = value
  2. Object.equals default IS ==
  3. String/Integer/collections override equals()
  4. new String("a") == new String("a") is false, equals true
  5. primitives: == compares values, no equals()

basics

~10 s

== checks if two references point to the exact same object in memory (identity). equals() checks whether two objects are logically equal in value. They can disagree: two different objects can hold equal data.

solid answer

~40 s

For object references, == compares identity: it returns true only when both variables point to the very same object on the heap. equals() compares logical equality and is meant to answer "do these represent the same value?". The default Object.equals() just does ==, so unless a class overrides equals(), the two are identical. Value-based classes like String, Integer, and the collection classes override equals() so distinct objects with the same contents compare equal. Classic gotcha: new String("a") == new String("a") is false (two objects), but .equals() is true. For primitives (int, double, etc.) there are no references, so == compares the actual values and equals() doesn't apply. Rule of thumb: use == for identity and primitives, use equals() for value comparison of objects.

code

java · 10 lines
java
String a = new String("hi");
String b = new String("hi");
String c = a;

System.out.println(a == b);       // false  (different objects)
System.out.println(a == c);       // true   (same object)
System.out.println(a.equals(b));  // true   (same value)

int x = 5, y = 5;
System.out.println(x == y);       // true   (primitive value compare)

go deeper

for a junior

States the core distinction: == is same object, equals() is same value; knows to use equals() for Strings.

for a middle

Knows the default Object.equals() is identity, that value classes override it, and explains the new String gotcha; mentions Objects.equals for null safety.

for a senior

Connects to the equals/hashCode contract, string interning and the Integer cache as reasons == surprises, and when identity comparison is actually the right tool.

for a principal

Frames identity vs equality as a domain-modeling choice (entities have identity, value objects compare by state), and discusses how == leaks identity semantics into APIs (e.g. enum == vs equals, sentinel singletons).

## The two kinds of "same" When you ask whether two things are "the same", you can mean two different things: 1. **Identity** — "are these literally the *same* object?" (the same box in memory). 2. **Equality** — "do these two (possibly different) objects *represent the same value*?" Java gives you a separate tool for each. ## References vs objects (the foundation) In Java, every non-primitive variable holds a **reference** — essentially a pointer to an object that lives on the **heap** (the region of memory where objects are allocated). The variable is *not* the object; it's an address-like handle to it. ```java String a = new String("hi"); // object #1 on the heap; a points to it String b = new String("hi"); // object #2 on the heap; b points to it String c = a; // c points to the SAME object as a (#1) ``` ## `==` compares references (identity) For object types, `==` asks: *do these two variables hold the same reference?* — i.e. point to the same heap object. ```java a == b // false: different objects (#1 vs #2) a == c // true: same object (#1) ``` This is called **reference identity**. ## `equals()` compares value (logical equality) `equals(Object)` is a method declared on `java.lang.Object`. Its job is to answer *do these represent the same value?* The **default** implementation in `Object` is literally: ```java public boolean equals(Object o) { return this == o; } ``` So for a class that does **not** override `equals()`, `equals()` and `==` behave identically — both test identity. Many standard classes **override** `equals()` to compare contents instead. `String`, the wrapper types (`Integer`, `Long`, `Double`…), and the collection classes (`ArrayList`, `HashMap`…) all do this: ```java a.equals(b) // true: same characters "hi", even though different objects ``` ## Primitives are different Primitive types (`int`, `long`, `double`, `boolean`, `char`…) are **not** objects and hold no reference — the variable holds the value directly. So for primitives, `==` compares the actual values, and `equals()` doesn't apply (you can't call a method on a primitive). ```java int x = 5, y = 5; x == y; // true: compares the values 5 and 5 ``` ## The canonical gotcha ```java String s1 = new String("a"); String s2 = new String("a"); s1 == s2; // false (two distinct objects) s1.equals(s2); // true (same value) ``` Because `String` overrides `equals()`, comparing string *contents* must use `.equals()`. Using `==` on strings is a frequent bug. ## Practical rule - Comparing **object values** → use `equals()` (or `Objects.equals(x, y)` to be null-safe). - Checking whether two variables are the **same object** (rare, e.g. cache identity) → use `==`. - Comparing **primitives** → use `==`. The two only coincide when a class hasn't overridden `equals()`.

  • Why does new String("a") == new String("a") return false but "a" == "a" return true?
    new String(...) always creates a fresh object, so two news are distinct references (== false). String literals are interned into a shared pool, so "a" == "a" points to the same pooled object (== true). .equals() returns true in both cases.
  • What does the default Object.equals() do?
    It returns this == o — pure reference identity. So unless a class overrides equals(), equals() and == behave identically.

saying these in an interview costs you the question

  • Claiming == always compares values for objects
  • Saying equals() compares memory addresses (it compares value, when overridden)
  • Forgetting that the DEFAULT equals() is just == (identity)
  • Using == to compare String contents
  • Thinking equals() applies to primitives

context

open as a page

How do you compare two object references for value equality safely when either might be null, and why is x.equals(y) risky?

level: juniorimportance: should knowfreq 60%

basics

~10 s

Calling x.equals(y) throws NullPointerException if x is null. Use Objects.equals(x, y) instead — it handles nulls (both null returns true, one null returns false) and otherwise calls x.equals(y).

open as a page

Why can comparing strings with == sometimes return true and sometimes false, and what role does the string pool (interning) play?

level: middleimportance: should knowfreq 75%

basics

~20 s

String literals are stored once in a shared pool, so two equal literals point to the same object and == is true. Strings made with new (or computed at runtime) are separate objects, so == is false. Always use equals() to compare string contents.

open as a page

When should a class be compared by identity (==) versus by logical equality (equals()), and how does that map to entity vs value object modeling?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Use identity (==) for objects that represent a unique thing with a lifecycle (entities) — two are the same only if they're literally the same object. Use equals() (value comparison) for objects defined purely by their data (value objects), like money or a date.

open as a page

If a class overrides equals() and hashCode() to compare by value, how can you still test for true object identity, and where does System.identityHashCode fit in?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

Use == to test whether two references point to the same object — overriding equals() never changes what == does. System.identityHashCode(x) gives the original identity-based hash even when hashCode() is overridden, and IdentityHashMap compares keys by ==.

open as a page