Java String Pool Explained: Why == Sometimes Lies

Java String Pool Explained: Why == Sometimes Lies
Quick Guides is a series of short, practical explanations of core software engineering concepts. Each guide focuses on one topic, explains how it works, why it exists, and the common pitfalls every developer should know.
Java String Pool Explained
If you've ever seen this in Java:
String a = "hello";
String b = "hello";
System.out.println(a == b); // true
...and then this:
String c = new String("hello");
System.out.println(a == c); // false
you've already encountered the String Pool.
Understanding how it works can help you write more efficient code and avoid one of Java's most common interview questions.
What problem does the String Pool solve?
Strings are everywhere.
"GET"
"POST"
"ERROR"
"SUCCESS"
"userId"
Applications often create the same string values thousands—or even millions—of times.
Without any optimisation:
"hello"
"hello"
"hello"
"hello"
would create four separate objects in memory, all containing identical data.
That's wasteful.
Instead, Java keeps a shared cache of string literals called the String Pool.
How it works
Whenever Java encounters a string literal:
String s = "hello";
it first checks the String Pool.
String Pool
+-------------+
| "hello" || "world" || "GET" |
+-------------+
If "hello" already exists, Java simply returns a reference to the existing object.
If it doesn't exist, Java creates it once and stores it in the pool.
Every future occurrence of "hello" reuses that same object.
We'll trace one running example — variables a, b, c, and d — through the rest of this guide, so each new concept builds on the one before it.
Example: two literals, one object
String a = "hello";
String b = "hello";
System.out.println(a == b);
Memory looks like this:
String Pool
+-----------+
| "hello" |
+-----------+
^ ^
| |
a b
Both variables point to the exact same object.
Output:
true
Using new String()
Now bring in a third variable, building on the same a and b above:
String c = new String("hello");
This call explicitly creates a brand-new object on the heap, bypassing the pool entirely.
String Pool Heap
+-----------+ +-----------+
| "hello" | | "hello" |
+-----------+ +-----------+
^ ^ ^
| | |
a b c
System.out.println(a == c);
Output:
false
Even though the text is identical, c points to a different object than a and b.
== vs .equals()
This is where many developers get caught.
== compares object references.
a == b // true — both point at the pooled object
a == c // false — c is a separate object on the heap
asks:
Are these the exact same object?
.equals() compares the contents.
a.equals(c)
asks:
Do these strings contain the same characters?
Output:
true
As a rule:
Use == only if you intentionally want to compare references.
Use .equals() to compare string values.
What does intern() do?
Java also provides:
String d = c.intern();
intern() checks whether an identical string already exists in the String Pool.
If it does, it returns the pooled instance.
Otherwise, it adds the string to the pool and returns that pooled reference.
Since "hello" is already in the pool — thanks to a — calling c.intern() doesn't create anything new. It just hands back the pooled reference:
System.out.println(d == a);
System.out.println(d == c);
Output:
true
false
d now points at the same pooled object as a and b. c still refers to its own separate object on the heap — intern() doesn't change c itself, it only returns a different reference.
Why this improves performance
Pooling provides several benefits:
Less memory usage
Fewer duplicate objects
Reduced garbage collection pressure
Faster reference comparisons when appropriate
Imagine parsing an HTTP server.
Without pooling:
GET
GET
GET
GET
POST
POST
might create millions of identical string objects.
With pooling:
Pool
GET
POST
PUT
DELETE
Only one copy of each literal needs to exist.
Common interview question
What does this print?
String a = "Java";
String b = "Ja" + "va";
String c = new String("Java");
System.out.println(a == b);
System.out.println(a == c);
System.out.println(a.equals(c));
Answer:
true
false
true
Why?
"Ja" + "va" is a compile-time constant, so Java places "Java" in the String Pool.
new String() always creates a new object.
.equals() compares the characters rather than the object reference.
When should you care?
Most of the time, you don't need to think about the String Pool—Java manages it automatically.
However, it's useful to understand when:
debugging unexpected == comparisons
optimising memory usage in high-throughput applications
analysing heap dumps
preparing for Java interviews
working on performance-sensitive systems
Key takeaways
Java stores string literals in the String Pool.
Identical literals share the same object.
new String() always creates a new object.
== compares references.
.equals() compares string contents.
intern() returns the pooled version of a string.
The String Pool reduces memory usage and improves performance by avoiding duplicate string objects.
Final thoughts
The String Pool is one of Java's oldest and most effective optimisations. It's largely invisible during day-to-day development, but understanding it explains why two strings that look identical can behave differently when compared.
Whenever you're comparing strings, remember:
Compare values with .equals(), not references with ==.
That one habit will save you from countless subtle bugs.



