How does String immutability enable the string pool, and how do literals differ from new String(...)?
answer
- Pool = one shared copy per literal
- Sharing safe only because immutable
- == compares identity, equals() compares contents
- new String(literal) = wasteful distinct object
- intern() returns the pooled canonical instance
basics
~20 sBecause strings never change, Java can safely store one shared copy of each literal in a pool and reuse it. Two equal literals are the same object. But new String("x") forces a separate object on the heap, even though its text is equal.
solid answer
~50 sThe string pool (intern pool) is a JVM-managed cache of unique String objects. When the compiler sees a literal like "hello", it ensures the pool holds exactly one such object and every "hello" literal references it. This is only safe because Strings are immutable — a shared object can't be corrupted by one user. So two literals with equal text are the *same* reference, and == returns true for them. In contrast, new String("hello") explicitly allocates a fresh object on the heap with equal contents but a different identity, so == against the literal is false while equals() is true. You can add a runtime-built string to the pool with intern(), which returns the pooled canonical instance. The practical rules: always compare string contents with equals(), never ==; and avoid new String(literal), which just wastes memory.
code
java · 8 linesString a = "hello";
String b = "hello";
String c = new String("hello");
System.out.println(a == b); // true - same pooled literal
System.out.println(a == c); // false - new String() = distinct object
System.out.println(a.equals(c)); // true - equal contents
System.out.println(a == c.intern()); // true - intern() returns pooled copygo deeper
Knows to compare strings with equals(), not ==, and that new String makes a separate object.
Explains the pool, why literals share identity, and why new String(literal) is wasteful.
Connects pooling to immutability as its precondition, explains intern() semantics and constant folding of literal concatenation.
Reasons about intern() memory/CPU trade-offs at scale, pool sizing, and when string de-duplication (or the JVM's automatic dedup) is the right tool.
## The string pool, defined The **string pool** (also called the **intern pool**) is a special table the JVM keeps of distinct `String` objects. Its job is de-duplication: if the same text appears many times in your code as a literal, the JVM stores it once and shares that single object everywhere. ## Why immutability is the precondition Sharing one object across the whole program is only safe if nobody can change it. If `String` were mutable, code in one class could edit the shared `"hello"` and silently change it for every other class. Because `String` is immutable, the JVM can hand the same instance to thousands of call sites without risk. **Immutability is what makes pooling possible.** ## Literals are pooled automatically ```java String a = "hello"; String b = "hello"; System.out.println(a == b); // true — same pooled object ``` `==` compares **references** (object identity), not contents. Both variables point at the one pooled `"hello"`, so identity comparison is true. Compile-time-constant concatenations like `"hel" + "lo"` are also folded into a single pooled literal. ## new String(...) opts out of pooling ```java String c = new String("hello"); System.out.println(a == c); // false — different object System.out.println(a.equals(c)); // true — equal contents ``` `new String("hello")` is an explicit instruction to allocate a *brand-new* object on the heap. Its characters equal the literal's, but its identity is different, so `==` is false. This is almost always a mistake — it wastes a heap object and the pooled literal already exists. Hence the common advice: **never write `new String("literal")`**. ## intern() — add a runtime string to the pool Strings built at runtime (from concatenation, input, etc.) are *not* automatically pooled: ```java String d = new StringBuilder("hel").append("lo").toString(); System.out.println(a == d); // false — d is a fresh runtime object System.out.println(a == d.intern()); // true — intern() returns the pooled canonical copy ``` `intern()` looks up the pool: if an equal string is already there it returns that one; otherwise it adds this string and returns it. It's a tool for memory de-duplication of many equal runtime strings, but overusing it can pressure the pool — use it deliberately. ## The take-away rule Because identity (`==`) depends on these subtle pooling rules, you must **never compare string contents with `==`**. Always use `equals()` (or `equalsIgnoreCase`). `==` only tells you whether two references point at the same object, which is an implementation detail of pooling — not what you usually mean.
- Why is == sometimes true and sometimes false for equal strings?== is reference identity. Equal literals share one pooled object (true), but a new String(...) or a runtime-built string is a distinct object (false) unless interned. Always use equals() for contents.
- When might intern() be worth using?When you load huge numbers of equal runtime strings (e.g. parsing) and want to collapse the duplicates to one shared instance to save heap, accepting the pool/CPU cost of interning.
saying these in an interview costs you the question
- Using == to compare string contents
- Believing new String("x") == "x"
- Thinking all equal strings are automatically the same object
- Assuming runtime-built strings are pooled without intern()