Interviewers love this one because a tiny design choice in String quietly powers security, caching, and thread safety everywhere in Java.
Question: Why is String immutable?
Answer: String is immutable because once a String object is created, its text cannot change. Any operation that looks like a change, such as concatenation or replacement, creates a new String instead. This makes Strings safe to share, safe to use as map keys, and easy for the JVM to cache.
Interview-Ready Answer: I’d say String is immutable so Java can safely share text across the runtime without worrying that some code will change it later. That gives us thread safety, stable hash codes for hash-based collections, and efficient pooling of repeated literals. When I need frequent edits, I use StringBuilder instead of trying to mutate a String.
A String is immutable when its visible text cannot be changed after construction. In plain words: you can point a variable at a different String, but you cannot edit the characters inside an existing String object.
byte[] plus a small encoding flag; older Java versions used char[].final, so nobody can subclass it and sneak in mutating behavior. That helps the JVM trust that String behaves consistently.char[], later changes to that array do not affect the String.setCharAt or append on String. Methods such as concat, replace, or substring return a new String value.HashMap and HashSet.String literal like "java" can be shared instead of copied many times, which saves memory and speeds up comparisons.| Type | Mutable? | Thread-safe? | Best use |
|---|---|---|---|
| String | No | Yes | Read-only text |
| StringBuilder | Yes | No | Fast single-threaded building |
| StringBuffer | Yes | Yes | Legacy synchronized building |
+ in a loop can become expensive, often close to O(n^2) overall, because many temporary Strings are created.StringBuilder.append is amortized O(1) per append, and the final toString() is O(n). That is why builders are preferred for heavy concatenation.intern() does not make String mutable; it only asks the JVM to return a shared pooled instance for equal text.Memory hook: think of a String like a sealed envelope. You can write a new envelope with updated text, but you cannot erase and rewrite the one already sent.
Imagine a checkout service for an e-commerce app. It uses order IDs, coupon codes, and request IDs as keys in caches, logs, and audit trails. Because Strings are immutable, one request can validate an ID and another thread can safely log the exact same value without fear that some later code will quietly change it.
What goes wrong when people misunderstand this: a team uses a mutable text holder for cache keys and then keeps appending trace data to it. Suddenly the cache starts missing entries, logs stop matching orders, and the payment service begins hammering the database for the same lookups. In production, this shows up as rising latency, duplicate processing, and confusing log lines like "order not found" even though the order was just created.
With String, that class of bug is much harder to create because the key you inserted into the cache will always stay the same.
import java.util.HashMap;
import java.util.Map;
public class StringImmutabilityDemo {
public static void main(String[] args) {
// The String constructor copies the source characters.
// Changing the original array later does not change the String.
char[] letters = {'J', 'a', 'v', 'a'};
String text = new String(letters);
letters[0] = 'X';
System.out.println("String after source array change: " + text);
// A String is safe to use as a map key because its content never changes.
Map<String, Integer> cache = new HashMap<>();
cache.put(text, 1);
// Equal text finds the same key.
System.out.println("Lookup with equal text: " + cache.get("Java"));
// Concatenation does not mutate the old String.
// It creates a brand-new String, so the variable now points somewhere else.
text = text + "!";
System.out.println("Variable after concat: " + text);
// This lookup fails because the key text is now different.
System.out.println("Lookup with changed text: " + cache.get(text));
System.out.println("Original key still works: " + cache.get("Java"));
// StringBuilder is the mutable alternative when you need repeated edits.
StringBuilder builder = new StringBuilder("Java");
builder.append("!");
builder.append(" Rocks");
System.out.println("StringBuilder result: " + builder);
}
}Follow-up & Tricky Questions:
final helps, but immutability mainly comes from private internal storage, no mutator methods, and defensive copying of input data.intern() for performance everywhere? No. It can help when you have many repeated identical strings, but overusing it can add complexity and memory pressure. Use it only when you have a clear reason.s = s + "x", did String mutate? No. The old String still exists unchanged; the variable s is just reassigned to a new object.intern(). In general, compare Strings with equals(), not ==.Common Mistakes:
+ in big loops. Correction: use StringBuilder for repeated concatenation, then call toString() once at the end.==. Correction: == checks object identity, not text equality; use equals().Memory Hook: A String is a sealed envelope: you can write a new one, but you cannot rewrite the one already sealed.
Cheat Sheet:
Practice Tasks:
+ versus StringBuilder.