Java String Pool

In the last lesson you learned an introduction to Strings. Now let’s clear up something that confuses almost every learner. Why does == on two equal-looking Strings sometimes say true and sometimes false? The answer is a memory trick called the String pool.

πŸ€” The puzzle

Here is the pain:

  • You compare two Strings that clearly hold the same words.
  • Sometimes == says they match, sometimes it says they do not.
  • You cannot trust your own eyes.

Try to guess the output before reading on.

String a = "hello";
String b = "hello";
String c = new String("hello");
System.out.println(a == b); // ?
System.out.println(a == c); // ?
System.out.println(a.equals(c)); // ?

All three hold β€œhello”, yet the results differ. To see why, you need two ideas: what == really checks, then what the String pool does.

Output

true
false
true

πŸ“ What == really checks

For objects like Strings, == does not look at the text. Think of it like home addresses:

  • == asks β€œdo you both live at the same address?” It compares references (the memory address a variable points to). Same object means true.
  • .equals() asks β€œdo your houses look the same inside?” It compares the actual characters. Same text means true.

So when you want to know β€œis the text the same?”, reach for .equals(). But why would two separate "hello" Strings ever be the same object? That is the pool.

🏊 What is the String pool?

The String pool is a special area inside heap memory that stores String literals (text you write in double quotes, like "hello"). Java keeps one copy of each unique literal, so writing "hello" again hands you the existing one.

Why Java does this:

  • It saves memory. One shared copy of "yes" or "error" instead of thousands adds up fast.
  • It is safe to share. Strings are immutable, so the shared copy can never change. Nothing can edit "hello" and break another part of the program.

That second point is the quiet hero: pooling is only safe because nothing can mutate the shared value.

String a = "hello"; // creates "hello" in the pool
String b = "hello"; // βœ… reuses the SAME pooled "hello"
System.out.println(a == b); // true: same object

Both a and b point to the one shared "hello". So == is true. Not luck. The pool deliberately shares one copy.

Output

true

πŸ—ΊοΈ A picture of the memory

A map makes it click. The heap holds your objects. Inside it sits a fenced-off section, the String pool, that holds the literals.

Heap

StringPool

a variable

hello in pool

b variable

c variable

hello new object

Follow the arrows:

  • a and b point to the same "hello" inside the pool, so a == b is true.
  • c points to a different box outside the pool, so a == c is false.
  • The text matches in all three, so .equals() is true for every pair.

πŸ†• What new String() does differently

new String("hello") is a direct order: build a brand new object, separate from the pooled one, even though the text is identical. The word new always means β€œbuild a fresh object right now”.

String a = "hello"; // βœ… from the pool
String c = new String("hello"); // ❌ a new, separate object
System.out.println(a == c); // false: different objects
System.out.println(a.equals(c)); // true: same text

Line by line:

  • a points to the pooled "hello", the shared one.
  • c points to a fresh object new created in the heap.
  • a == c is false because they sit at different addresses.
  • a.equals(c) is true because the characters are identical.

The lesson: always use .equals() for text. With == the answer depends on which object you happen to hold.

Output

false
true

One more sneaky case. Strings built at runtime, not as plain literals, also skip the pool.

String a = "hello";
String d = "hel";
String e = d + "lo"; // built at runtime from a variable
System.out.println(a == e); // ❌ false: e is a new runtime object
System.out.println(a.equals(e)); // βœ… true: same text

Even though e spells β€œhello”, Java joined pieces while running, so the result is a new object, not the pooled literal:

  • == says false here too.
  • This is the trap with user input and concatenation. The text looks right, but == still fails.

Output

false
true

Never compare String text with ==

Comparing String text with == is a classic bug. It may work by accident with pooled literals, then break the moment you use new, read user input, or join Strings at runtime. Always use .equals() (or equalsIgnoreCase) when you care about the text.

πŸ”§ The intern() method

Literals go into the pool for free. But what about a String that is not pooled, like one from new or user input? The intern() method returns the pooled copy of a String’s text, adding it first if needed.

String c = new String("hello");
String pooled = c.intern(); // get the pooled "hello"
String a = "hello";
System.out.println(c == pooled); // false: c is the new object, pooled is the shared one
System.out.println(a == pooled); // true: both are the pooled object

Walk through it:

  • c is the fresh object from new, outside the pool.
  • c.intern() returns the shared pooled copy, stored in pooled.
  • a is the literal, already pointing to that same pooled copy.
  • a == pooled is true; both point to the one pooled object.
  • c == pooled is false; intern() does not change c, it just returns the pooled one.

You rarely need intern() in everyday code. It is for special cases with huge numbers of repeated Strings.

Output

false
true

⚠️ Common Mistakes

A few String-pool slip-ups trip up almost everyone:

  • Using == to compare text. It checks object identity, not the characters.
  • Assuming new String() gives the pooled object. It does not.
  • Thinking the pool covers runtime-built Strings. Strings joined with + from variables, or read from input, are usually not pooled.

Wrong way and right way side by side:

String name = getUserName(); // text comes from input
// ❌ works by luck sometimes, fails often
if (name == "Alex") { ... }
// βœ… compares the actual text every time
if (name.equals("Alex")) { ... }

βœ… Best Practices

Good habits around the String pool keep these bugs out for good:

  • Always compare text with .equals(). Never depend on == for String contents.
  • Put the literal first to dodge null crashes. Write "Alex".equals(name) so a null name does not crash.
  • Prefer literals over new String(). Literals use the pool; new String("hello") wastes memory for no benefit.
  • Use equalsIgnoreCase when capital letters should not matter. It treats β€œYES” and β€œyes” as the same.
  • Do not reach for intern() by habit. Use it only after measuring a real memory problem from repeated Strings.

🧩 What You’ve Learned

The == mystery is solved. Recap:

  • βœ… The String pool is a special area inside heap memory that stores String literals and reuses one shared copy of each unique text to save memory.
  • βœ… Sharing is safe because Strings are immutable, so the shared copy can never change.
  • βœ… == compares object identity (same memory address), while .equals() compares the actual text.
  • βœ… Two identical literals share the pooled object, so == is often true for them.
  • βœ… new String("...") and runtime-built Strings make separate objects, so == is false even when the text matches.
  • βœ… intern() returns the pooled copy of a String on demand.
  • βœ… Always compare String text with .equals(), never ==.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What does == check when used on two String variables?

    Why: For objects, == checks reference identity, not the text. Use .equals() for text.

  2. 2

    Why is a == b often true for two String literals with the same text?

    Why: The String pool stores one copy of each literal, so identical literals point to the same object.

  3. 3

    What does new String("hello") create compared to the literal "hello"?

    Why: new String forces a new, distinct object even though the text is identical, so == is false.

  4. 4

    How should you always compare the text of two Strings?

    Why: .equals() compares the actual characters and is reliable regardless of pooling.

πŸš€ What’s Next?

You now understand how Strings live in memory and why == behaves the way it does. Up next we explore the many built-in methods Strings give you for searching, changing, and slicing text.

Java String Methods

Share & Connect