How the string pool works, literals vs new String(), when interning hurts, String vs StringBuilder vs StringBuffer, and exactly why String is immutable.
Published September 25, 2026
String questions are practically guaranteed in a Java interview, and they often come with a "what does this print?" snippet. Know the pool rules precisely, and be ready to say why String is immutable in at least three ways.
Short answer: The string pool (the "intern pool") is a JVM-managed table of unique String instances. String literals, and strings you explicitly intern(), are stored there once and reused. Two literals with the same text share a single object.
String a = "order"; // goes to (or is found in) the pool
String b = "order"; // same pooled object
String c = new String("order"); // a NEW object on the heap, outside the pool
String d = c.intern(); // returns the pooled "order"
System.out.println(a == b); // true
System.out.println(a == c); // false
System.out.println(a == d); // true
Key points to cover:
substring, reading input) don't, unless you intern them."ord" + "er" is the same pooled object as "order". But var + "er" creates a new string at runtime.Learn it in depth → String Manipulation
Short answer: When strings are mostly unique, or short-lived. Interning costs a hash lookup and keeps an entry in the pool table, so doing it on unique values (request IDs, UUIDs, user input) wastes CPU and memory, and saves nothing.
Key points to cover:
-XX:+UseStringDeduplication shares the underlying character arrays of equal strings automatically, without changing identity semantics.new String("literal"). It creates a pointless extra object.String and StringBuffer?Short answer: String is immutable. Every "modification" creates a new object. StringBuffer is a mutable sequence of characters that you change in place. Its methods are synchronized, which makes it thread-safe but slower.
String s = "a";
s = s + "b"; // creates a new String; the old "a" is unchanged
StringBuffer sb = new StringBuffer("a");
sb.append("b"); // modifies the same object
Learn it in depth → String Manipulation
StringBuilder different from StringBuffer, and when should you use each?Short answer: They have the same API. StringBuilder (Java 5) is not synchronized, so it's faster, and it's the right choice almost always, because string building usually happens inside one method on one thread. StringBuffer is a legacy class, kept for code that genuinely shares one buffer between threads.
String | StringBuilder | StringBuffer | |
|---|---|---|---|
| Mutable | No | Yes | Yes |
| Thread-safe | Yes (immutable) | No | Yes (synchronised) |
| Speed for repeated edits | Slow (new objects) | Fastest | Slower (locking) |
| Use for | Values, keys, constants | Building strings in loops | Rare legacy/shared cases |
StringBuilder csv = new StringBuilder();
for (Order o : orders) {
csv.append(o.id()).append(',').append(o.total()).append('\n'); // one buffer, no garbage
}
Key points to cover:
a + b + c expressions (using invokedynamic/StringConcatFactory since Java 9). Use StringBuilder explicitly for loops, where += creates a new string on every iteration.StringBuffer is better than String.Short answer: When one character buffer must be modified by several threads. For example, several worker threads appending fragments to a shared diagnostic report, where each append must be atomic.
Key points to cover:
StringBuilder, and then combine the results. Or use a thread-safe logger or queue. Shared mutable buffers are a concurrency smell.String: any scenario with many modifications (in loops) favours a mutable builder over repeated concatenation.Short answer: A literal ("java") is pooled: equal literals share one instance. new String("java") always creates a new, separate object, even though an equal string already exists in the pool.
Key points to cover:
new String("java") create?" The answer: up to two. The literal "java" is pooled (if it isn't already there, when the class is loaded), plus the new heap object.equals(), never with ==.String immutable in Java?Short answer: For security, safe sharing (pooling), thread safety and hash caching.
Key points to cover:
hashCode() is computed once and cached, which makes strings fast and reliable HashMap keys.Common trap: saying "String is immutable because it's final". final on the class only prevents subclassing, which protects the immutability. The immutability itself comes from a private internal array that's never modified or exposed.
Q: How is a String stored internally in modern Java?
A: Since Java 9 (compact strings), a String holds a byte[] plus a coder flag. Latin-1 text uses one byte per character, and anything else uses UTF-16. This roughly halves memory for mostly-ASCII text.
Q: Why is char[] preferred over String for passwords?
A: A String can't be wiped, and it may linger in memory, in the pool, or in heap dumps until it's collected. A char[] can be overwritten with zeros (Arrays.fill(pw, '\0')) as soon as you've used it.
Q: What's the difference between isEmpty() and isBlank()?
A: isEmpty() is true only for a length of 0. isBlank() (Java 11) is also true for strings containing only whitespace.
Q: How do you reverse a string efficiently?
A: new StringBuilder(s).reverse().toString(). It runs in linear time, and it handles surrogate pairs correctly.
Q: What does String.join do?
A: It concatenates elements with a delimiter: String.join(", ", List.of("a", "b")) → "a, b". For streams, use Collectors.joining(", ").