Why can comparing strings with == sometimes return true and sometimes false, and what role does the string pool (interning) play?
answer
- literals are interned → shared pooled object
- new String() = fresh object, == false
- constant folding: "a"+"b" pooled; var+"b" not
- intern() returns the pooled canonical instance
- strings immutable → safe to share → pool
basics
~20 sString literals are stored once in a shared pool, so two equal literals point to the same object and == is true. Strings made with new (or computed at runtime) are separate objects, so == is false. Always use equals() to compare string contents.
solid answer
~50 sThe JVM keeps a string constant pool: each distinct compile-time String literal is interned, so all occurrences of "abc" refer to one shared object — hence "abc" == "abc" is true. But new String("abc") explicitly allocates a fresh object outside the pool, so it == is false even though contents match. Likewise, strings built at runtime (concatenation of non-constants, substring, etc.) are new objects and won't be == to a literal. The compiler does fold constant expressions: "a" + "b" becomes the literal "ab" at compile time and is pooled, but "a" + someVar is computed at runtime and is not. You can force pooling with String.intern(). The takeaway: == on strings tests object identity, which is an implementation detail of pooling, not content — so it's unreliable for value comparison. Always use equals() (or Objects.equals for null safety).
code
java · 13 linesString a = "abc";
String b = "abc";
String c = new String("abc");
String d = "ab" + "c"; // compile-time constant -> pooled
String part = "ab";
String e = part + "c"; // runtime concat -> new object
System.out.println(a == b); // true
System.out.println(a == c); // false
System.out.println(a == d); // true
System.out.println(a == e); // false
System.out.println(a == e.intern()); // true
System.out.println(a.equals(c)); // true (always use this for content)go deeper
Knows that == on strings can be unreliable and that equals() is the safe choice.
Explains the constant pool, interning of literals, why new String differs, and constant folding; can predict == results.
Relates interning to immutability and memory, knows intern() trade-offs, and ties it to the Integer cache as the same identity-vs-equality pitfall.
Discusses when identity guarantees are part of an API contract (interned strings, flyweights), pool sizing/metaspace history, and why exposing identity semantics through == is a design smell.
## The puzzle ```java "abc" == "abc"; // true new String("abc") == "abc"; // false ``` Both compare strings with the same characters, yet give different answers. The reason is **string interning** and the **string constant pool**. ## What `==` actually tests Recall: for objects, `==` compares **references** — *are these the same object in memory?* It never looks at characters. So whether `==` is true depends entirely on whether the two expressions yield the *same* `String` object. ## The string constant pool A `String` is **immutable** (its characters never change after construction). Immutability makes it safe to *share* one object for every occurrence of the same literal. The JVM exploits this with a **string constant pool**: a table of canonical `String` instances. - Every **string literal** in your source (`"abc"`) is **interned**: the first time it's seen, one `String` object is created and placed in the pool; every other identical literal reuses that same object. ```java String a = "abc"; String b = "abc"; a == b; // true — both are the one pooled "abc" ``` ## `new String(...)` opts out `new String("abc")` is an explicit instruction to **allocate a brand-new object** on the heap, separate from the pool — even though it copies the same characters. ```java String c = new String("abc"); c == a; // false — different object c.equals(a); // true — same value ``` ## Compile-time constant folding The Java compiler evaluates **constant expressions** at compile time. If every operand is a compile-time constant (literals or `final` constants), the result is itself folded into a literal and pooled: ```java String d = "ab" + "c"; // folded to the literal "abc" at compile time → pooled d == a; // true ``` But if any operand is a **runtime value**, the concatenation happens at runtime and produces a **new** object: ```java String part = "ab"; // not final / not a constant in this scope String e = part + "c"; // computed at runtime → new object e == a; // false e.equals(a); // true ``` The same applies to methods like `substring`, `toLowerCase`, reading input, etc. — their results are new objects. ## `String.intern()` You can ask the JVM to return the pooled canonical instance: ```java String f = new String("abc").intern(); f == a; // true — intern() returns the pooled "abc" ``` Interning can save memory for many duplicate strings, but overusing it pressures the pool; it's rarely needed in application code. ## The wrapper-cache analogue The same surprise exists for boxed integers: `Integer.valueOf` caches small values (−128..127), so `Integer.valueOf(100) == Integer.valueOf(100)` is true but `... (1000) == ... (1000)` is false. Both cases reduce to the same lesson. ## The lesson Whether `==` is true for strings depends on **pooling**, which is an implementation/optimization detail — *not* on the characters. Therefore: - **Never** use `==` to compare string **content**. - Use `equals()` for value comparison (`Objects.equals(x, y)` if either may be null). - Reserve `==` for the rare case where you genuinely care about object identity.
- Does "a" + variable get pooled like "a" + "b"?No. "a" + "b" is a compile-time constant expression, folded to the literal "ab" and pooled. "a" + variable involves a runtime value, so it's computed at runtime into a new String object that is not pooled (unless you call intern()).
- Why is String being immutable a prerequisite for the pool?Because the pool shares one object across many references. If a String could be mutated, changing it through one reference would corrupt every other use of the literal. Immutability makes sharing safe.
saying these in an interview costs you the question
- Concluding == is fine for strings because it 'worked' on literals
- Thinking the pool compares contents (it shares identical literals)
- Believing all concatenation is folded at compile time
- Forgetting new String always makes a distinct object
- Assuming intern() is needed for correctness (equals() is)