Java • Object-Oriented Programming

.equals vs. == — Use .equals vs == correctly for objects

Learn why two objects can look identical yet fail an == check, and how .equals() saves the day.

Java programming emblem with a coffee cup, code braces, and terminal
Inside this lesson 10 sections 5 questions 5 figures

SECTION 1

Historical Context & Motivation

When programming languages were first designed, comparing two values was straightforward — you just checked if two numbers were the same. But as languages evolved to support object-oriented programming, a tricky question emerged: what does it mean for two objects to be "equal"? Are they the same physical thing in memory, or do they just happen to contain the same data? This distinction between identity and equality became one of the most important — and most confusing — ideas in Java.

1972 • Smalltalk Introduces Object Identity

Smalltalk, one of the first object-oriented languages, distinguishes between asking if two variables point to the same object versus if they hold equivalent data.

1995 • Java Launches with == and .equals()

Java's creators at Sun Microsystems build two comparison mechanisms into the language: the == operator for reference comparison and the .equals() method on every object for content comparison.

2004 • Autoboxing Deepens the Confusion

Java 5 introduces autoboxing, which automatically converts primitives to wrapper objects. This makes the == vs .equals() distinction even more critical, as Integer objects can silently replace int primitives.

2017 • Modern IDEs Warn Developers

Tools like IntelliJ IDEA and Eclipse start flagging == comparisons on objects as likely bugs, underscoring how frequently this mistake occurs — even among professionals.

The central question this lesson addresses is deceptively simple: if you create two String objects that both contain the text "hello", are they equal? The answer depends entirely on whether you use == or .equals() — and getting it wrong is one of the most common bugs in Java.

SECTION 2

Core Principles & Definitions

To understand the difference between == and .equals(), you first need to understand how Java stores objects in memory. When you create an object using new, Java allocates space on the heap (a region of memory for objects) and gives you a reference — essentially a memory address pointing to that object. Variables don't hold the object itself; they hold the reference to it.

1

Reference (Identity)

A reference is like an address on a house. Two variables can point to the same house (same reference) or to different houses (different references). The == operator checks if two variables hold the exact same address.
2

Content (Equality)

Content equality asks: do two objects store the same data inside? The .equals() method compares the internal state of objects. Two different houses can have identical furniture — same content, different references.
3

Primitives Are Different

Primitive types like int, double, and boolean store actual values — not references. For primitives, == compares values directly, and .equals() does not apply.
4

The String Pool Trick

Java optimizes memory by reusing String literals in a special area called the String pool. Writing "hello" twice may point to the same object, making == return true — but this is an optimization, not a guarantee you should rely on.
✦

KEY TAKEAWAY

Think of it like phone contacts. Two friends might both be named "Alex" — they have the same name (content equality, checked by .equals()), but they're clearly two different people (different identity, detected by ==). When you want to know if two Strings say the same thing, you need .equals(). Using == would be like asking if your two contacts are literally the same human being.

SECTION 3

Visual Explanation — Memory Model

Java Memory: References vs. ObjectsStack (Variables)s1ref: 0x100s2ref: 0x200s3ref: 0x100Heap (Objects)0x100String: "hello"0x200String: "hello"Results:s1 == s2 → false(different addresses: 0x100 ≠ 0x200)s1.equals(s2) → true(same content: "hello" = "hello")s1 == s3 → true(same address: 0x100 = 0x100)Code that created these variables:String s1 = new String("hello");String s2 = new String("hello");String s3 = s1;
This diagram shows the Java memory model with three variables on the stack. Variables s1 and s2 each point to separate String objects on the heap (both containing "hello"), while s3 is assigned the same reference as s1, so they point to the exact same object. The == operator compares the arrows (references), while .equals() compares what's inside the boxes (content).

In the diagram above, notice that s1 and s2 both store the text "hello", but they live at different addresses in the heap. The == operator only cares about the address — since 0x100 and 0x200 are different, it returns false. Meanwhile, .equals() opens up both objects and compares their characters one by one, finding that they match perfectly. The variable s3 is a copy of the reference stored in s1, so both variables point to the exact same object — making == return true.

SECTION 4

How It Works Under the Hood

The == Operator for Objects

When you write a == b where a and b are object references, Java compares the memory addresses stored in those variables. It does not look inside the objects at all. This is called a reference comparison or identity check. The result is true only when both variables point to the exact same object in memory.

The .equals() Method

Every class in Java inherits from Object, and Object provides a default .equals() method. Here's the catch: the default version in Object actually behaves the same as == — it just checks references. Classes like String, Integer, and ArrayList override this method to compare the actual content instead. When you write your own classes, you must override .equals() yourself if you want content-based comparison.

The String Pool — A Special Case

Java has a performance optimization called the String pool (also called the String intern pool). When you write a String literal like "hello", Java first checks if that exact text already exists in the pool. If it does, Java reuses the existing object instead of creating a new one. This means String a = "hello"; String b = "hello"; will make a == b return true because both point to the same pooled object. However, using new String("hello") forces Java to create a brand-new object outside the pool, so == would return false. This inconsistency is exactly why you should always use .equals() for Strings.

⚠

Common Mistake Alert

Many beginners test their String code with literals like "hello" == "hello" and see true, convincing themselves that == works fine for Strings. It works only because of the String pool optimization — it will fail the moment Strings come from user input, file reading, or new String(). Always use .equals() for String comparison.

SECTION 5

Detailed Breakdown — Where Each Belongs

Decision Flowchart: == or .equals()?Are you comparing…Primitive types?int, double, boolean, char…Object types?String, Integer, custom…Use ==Compares actual valuesWhat do you want to check?Same content?Same object?Use .equals()Compares content insideUse ==Compares referencesQuick Rule of ThumbIf the variable type starts with a CAPITAL letter → use .equals()If the variable type starts with a lowercase letter → use ==
This decision flowchart helps you decide which comparison to use. Start at the top and follow the path that matches your situation. The quick rule at the bottom leverages the fact that primitive types are lowercase (int, double) while object types are capitalized (String, Integer).
ScenarioUse ==Use .equals()
Comparing two int values✅ Yes — primitives store values directly❌ Not available for primitives
Comparing two Strings for same text❌ Unreliable — depends on String pool✅ Yes — compares character by character
Checking if a reference is null✅ Yes — use obj == null❌ Causes NullPointerException!
Comparing two Integer wrapper objects❌ Unreliable — caching varies by value✅ Yes — compares the numeric values
Verifying two variables point to same object✅ Yes — that's exactly what == does⚠️ Works but may be too permissive
Comparing enum values✅ Yes — each enum constant is a singleton✅ Also works, but == is idiomatic

When to use == vs .equals() in common Java scenarios

SECTION 6

Worked Example — Tracing Object Comparisons

Let's trace through a complete code example step by step, predicting the output of each comparison before running it.

Predicting == and .equals() Output

  1. 1

    Step 1 — Create the Variables

    We declare four String variables: String a = "cat"; — String literal, goes into the String pool. String b = "cat"; — Same literal, Java reuses the pooled object. String c = new String("cat"); — Forces a new object on the heap. String d = c; — Copies the reference from c.
    a and b → same pooled object; c → new separate object; d → same object as c
  2. 2

    Step 2 — Evaluate a == b

    Both a and b are String literals with the same text. Java's String pool reuses the same object for both. Since they share the same reference, == compares two identical memory addresses.
    a == b → true (same pooled object)
  3. 3

    Step 3 — Evaluate a == c

    Variable a points to the pooled "cat" object, while c was created with new String("cat"), which created a brand-new object at a different memory address. Even though the content is the same, the addresses differ.
    a == c → false (different objects in memory)
  4. 4

    Step 4 — Evaluate a.equals(c)

    The .equals() method in the String class is overridden to compare character sequences. It checks: does 'c' equal 'c'? Does 'a' equal 'a'? Does 't' equal 't'? All characters match, and the lengths are the same.
    a.equals(c) → true (same content: "cat")
  5. 5

    Step 5 — Evaluate c == d

    Variable d was created by assigning d = c, which copies the reference (memory address) from c into d. Both variables now hold the exact same address, pointing to the same object.
    c == d → true (d is a copy of c's reference)

SECTION 7

Common Pitfalls & Best Practices

PitfallWhat HappensBest Practice
Using == to compare StringsWorks with literals (String pool), fails with new String(), user input, or concatenation results.Always use .equals() for String content comparison.
Calling .equals() on nullThrows a NullPointerException because you can't call methods on null.Put the known non-null value first: "hello".equals(myVar) instead of myVar.equals("hello")
Using == with Integer wrappersJava caches Integer values −128 to 127, so == works for small numbers but fails for large ones.Use .equals() for all wrapper comparisons (Integer, Double, etc.).
Forgetting to override .equals() in custom classesThe default Object.equals() uses ==, so .equals() behaves like reference comparison.Override .equals() (and .hashCode()) in any class where content equality matters.

Common pitfalls when comparing objects in Java

✦

KEY TAKEAWAY

Think of == like comparing house addresses on two envelopes — it only tells you if they're going to the same physical house. Think of .equals() like walking inside both houses and checking if the furniture, paint, and layout are identical. For almost every real-world Java task involving objects, you care about what's inside the house, not its street address — so use .equals().

SECTION 8

Connection to Advanced Concepts

Understanding == versus .equals() is a stepping stone to several deeper topics in Java. Once you're comfortable with this distinction, you'll be better prepared to tackle concepts like hash codes, the equals-hashCode contract, and immutability.

This LessonAdvanced Topic
.equals() compares content.hashCode() must return the same integer for equal objects — needed by HashMap and HashSet
Default .equals() uses reference comparisonOverriding .equals() requires implementing reflexivity, symmetry, transitivity, consistency, and non-null checks
String pool reuses immutable objectsThe concept of immutability — objects that can't change after creation — is key to thread safety and caching
== checks reference identityThe Comparable interface and compareTo() method extend equality to ordering (less than, greater than)

How this lesson connects to advanced Java concepts

In AP Computer Science A, you'll frequently encounter questions that test your understanding of reference vs. content equality. The College Board expects you to know that == checks whether two references point to the same object and that .equals() checks whether two objects have the same content. Mastering this distinction now will give you a significant advantage on exams and in building reliable programs.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

In your own words, explain the difference between what == checks and what .equals() checks when used on two String variables. Why could == return false even when the two Strings contain identical text?
Show answer
The == operator compares memory addresses (references), checking whether two variables point to the exact same object in memory. The .equals() method compares the actual content (the sequence of characters) stored inside the String objects. Two Strings can contain identical text yet live at different memory addresses — for example, if one was created with new String("hello") and the other from user input. In those cases, == returns false because the addresses differ, but .equals() returns true because the characters match.

Question 2: BASIC

What is the output of the following code? String x = "java"; String y = new String("java"); System.out.println(x == y); System.out.println(x.equals(y));
Show answer
The output is false on the first line and true on the second. Variable x references the pooled String literal "java", while y references a new String object created on the heap. Since they are different objects, == returns false. However, .equals() compares the character sequences, which are identical, so it returns true.

Question 3: INTERMEDIATE

Consider this code: Integer a = 127; Integer b = 127; Integer c = 200; Integer d = 200; What does a == b return? What does c == d return? Explain the difference.
Show answer
a == b returns true, but c == d returns false. Java caches Integer objects for values between −128 and 127 through autoboxing. When you assign 127 to both a and b, Java reuses the same cached Integer object, so == returns true. For 200, which is outside the cache range, Java creates two separate Integer objects, and == compares different references. Using c.equals(d) would return true for both pairs.

Question 4: APPLIED

You are writing a login system. A user types their password into a text field, and you need to compare it against the stored password. Your code reads: if (inputPassword == storedPassword) A tester reports that correct passwords are sometimes rejected. Explain the bug and write the corrected line of code. Also explain how you would make the comparison null-safe.
Show answer
The bug is that == compares whether inputPassword and storedPassword are the same object in memory, not whether they contain the same text. User input creates a new String object, so it will almost never be the same object as the stored password. The corrected line is: if (storedPassword.equals(inputPassword)). To make it null-safe (in case storedPassword could be null), you could use if ("expectedPassword".equals(inputPassword)) by putting the known non-null literal first, or use Objects.equals(storedPassword, inputPassword) which handles null safely.

Question 5: CRITICAL THINKING

A student creates a Student class with a name field and an id field. They create two Student objects with the same name and id, then call .equals() on them and get false. They haven't overridden any methods. Explain why this happens, and sketch out what an overridden .equals() method should check. Why should they also override .hashCode()?
Show answer
Without an overridden .equals() method, the Student class inherits the default implementation from Object, which simply uses == (reference comparison). Since the two Student objects were created with new, they are different objects and the result is false. The overridden .equals() method should: (1) check if the other object is null, (2) check if it's the same type using instanceof, (3) cast the other object to Student, and (4) compare the name and id fields using their own .equals() methods. They must also override .hashCode() because Java's contract requires that objects that are equal (via .equals()) must produce the same hash code. If they don't, data structures like HashMap and HashSet will malfunction — a Student could be added to a HashSet and then not be found when searched for.

SUMMARY

Lesson Summary

Summary

In Java, the == operator performs a reference comparison, checking whether two variables point to the exact same object in memory. The .equals() method performs a content comparison, checking whether two objects store the same data. For primitive types like int and double, use == because they store values directly. For objects like String and Integer, almost always use .equals().

Be aware of Java's String pool and Integer caching, which can make == appear to work in some cases but fail in others. Guard against NullPointerException by placing the known non-null value first when calling .equals(). When creating your own classes, remember to override .equals() and .hashCode() if you want content-based equality. The quick rule: if the type starts with a capital letter, reach for .equals().

Varsity Tutors • Java • .equals vs. == — Use .equals vs == correctly for objects