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
1995 • Java Launches with == and .equals()
2004 • Autoboxing Deepens the Confusion
2017 • Modern IDEs Warn Developers
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.
Reference (Identity)
== operator checks if two variables hold the exact same address.Content (Equality)
.equals() method compares the internal state of objects. Two different houses can have identical furniture — same content, different references.Primitives Are Different
int, double, and boolean store actual values — not references. For primitives, == compares values directly, and .equals() does not apply.The String Pool Trick
"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
.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
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
"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
int, double) while object types are capitalized (String, Integer).| Scenario | Use == | 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
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
Step 2 — Evaluate a == b
Bothaandbare 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
Step 3 — Evaluate a == c
Variableapoints to the pooled "cat" object, whilecwas created withnew 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
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
Step 5 — Evaluate c == d
Variabledwas created by assigningd = 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
| Pitfall | What Happens | Best Practice |
|---|---|---|
Using == to compare Strings | Works with literals (String pool), fails with new String(), user input, or concatenation results. | Always use .equals() for String content comparison. |
Calling .equals() on null | Throws 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 wrappers | Java 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 classes | The 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
== 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 Lesson | Advanced Topic |
|---|---|
.equals() compares content | .hashCode() must return the same integer for equal objects — needed by HashMap and HashSet |
Default .equals() uses reference comparison | Overriding .equals() requires implementing reflexivity, symmetry, transitivity, consistency, and non-null checks |
| String pool reuses immutable objects | The concept of immutability — objects that can't change after creation — is key to thread safety and caching |
== checks reference identity | The 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
== 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
== 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
String x = "java";
String y = new String("java");
System.out.println(x == y);
System.out.println(x.equals(y));Show answer
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
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
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
== 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
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
.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().