== vs equals() in Java — What's the difference?
Understanding the difference between the == operator and equals() method in Java, and why using the wrong one leads to subtle bugs.
Java has two ways to check equality: the == operator and the equals() method. They do different things, and confusing them is one of the most common bugs in Java.
The short version
==compares references — are these two variables pointing to the same object in memory?equals()compares values — do these two objects contain the same data?
For primitives (int, boolean, char, etc.), == compares values directly. The distinction only matters for objects.
See it in action
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s1.equals(s2)); // true
System.out.println(s1.equals(s3)); // true
Why does s1 == s2 return true but s1 == s3 return false? They all contain "hello".
What’s happening in memory
When you write String s1 = "hello", Java checks the String Constant Pool. If "hello" already exists there, it reuses the same reference. So both s1 and s2 point to the exact same object in memory.
s1 ──┐
├──▶ "hello" (in String Constant Pool)
s2 ──┘
s3 ──────▶ "hello" (new object on heap)
new String("hello") forces Java to create a new object on the heap, even though an identical string exists in the pool. So s3 holds a different reference.
s1 == s2→true— same reference (both point to the pooled string)s1 == s3→false— different references (pool vs heap)s1.equals(s3)→true— same value (both contain “hello”)
How equals() works in String
The String class overrides equals() from Object to compare character-by-character:
- If both references are the same (
==), returntrueimmediately - Check if the other object is a
String - Compare lengths
- Compare each character
This is why equals() returns true for s1 and s3 — it doesn’t care where the objects live in memory, only what they contain.
The null trap
String s1 = null;
s1.equals("hello"); // NullPointerException
"hello".equals(s1); // false — safe
If there’s any chance a variable could be null, either put the known non-null value first, or use Objects.equals():
Objects.equals(s1, s2); // null-safe, returns false if either is null
Objects.equals() has been available since Java 7. Use it when either side could be null.
Beyond String
Every class can override equals(). The Object class default implementation just uses == (reference comparison). Most standard library classes override it to compare values:
| Class | equals() compares |
|---|---|
String | Character sequence |
Integer, Long, etc. | Numeric value |
List | Elements in order |
Set | Same elements |
Map | Same key-value pairs |
| Custom classes | Whatever you implement |
If you write your own class and don’t override equals(), it falls back to ==.
Quick rules
- Primitives — use
==(it’s the only option) - Objects — use
equals()to compare values - Null-safe comparison — use
Objects.equals(a, b) - Never use
==to compare Strings — it works sometimes (pooled literals) and fails other times (new String), which is worse than always failing
// Don't
if (userInput == "admin") { ... }
// Do
if ("admin".equals(userInput)) { ... }
// Or
if (Objects.equals(userInput, "admin")) { ... }