String · 4 of 43
Why strings are immutable in Java
A Java String is like a word carved in stone: nobody can change it, so everyone can safely share it, and a change always makes a new stone.
- Simple way
- time O(n^2)
room O(n) - Clever way
- time O(n)
room O(n)
The problem, simply
This is a "why" question that comes up in almost every Java interview. In Java, once a String object is made, its letters can never change. Methods like toUpperCase(), replace() or + never touch the old string. They make a brand new one.
You need to explain why Java was designed this way, and what it means for your code. The code part is the classic trap: building a long string with + in a loop is slow, and StringBuilder is the fix.
A story to remember it
Picture it
s1 and s2 both point to the one shared hi in the string pool. s3 used new, so it points to its own copy. Nobody can change either object, so sharing is safe.
- seen
- done
Examples
A copy keeps the old value
- In
String s = 'hi'; String t = s; s = s + '!';- Out
t is 'hi', s is 'hi!'
s + "!" made a new object and moved s to it. t still points to the old, unchanged hi.
Calling a method without saving it
- In
String u = 'cat'; u.toUpperCase();- Out
u is still 'cat'
toUpperCase() returned a new string, CAT, and nobody kept it. Write u = u.toUpperCase();.
Same letters, different objects
- In
'hi' == 'hi', new String('hi') == 'hi', new String('hi').equals('hi')- Out
true, false, true
Two literals share one pool object, so == is true. new makes a separate object, so == is false. equals compares letters and is true.
Building a long string
- In
s = s + 'a', repeated 100000 times- Out
slow: billions of letter copies
Each + copies the whole string so far. With a StringBuilder the same job copies each letter about once or twice.
Way 1: the simple way
Build a string of n letters with + in a loop. Because strings cannot change, every step carves a new stone.
- Start with
s = "". - Each time,
s + "a"makes a new string, copies all old letters into it, and adds one more. snow points to the new string. The old one is garbage.
Every + carves a new stone
01 / 04
s starts at the empty string.
- letters copied
- 0
- looking now
- answer
- rejected
- just added
- ignored
class Solution {
public String buildString(int n) {
String s = "";
for (int i = 0; i < n; i++) {
s = s + "a"; // a new String every time, old letters copied again
}
return s;
}
}How long it takes: step k copies k letters, so the total is 1 + 2 + ... + n, which is O(n^2). Extra room: only the newest string and the one being built are alive, O(n), but the loop throws away about n^2 / 2 letters of garbage for the garbage collector to clean up. Modern Java compiles a single a + b + c well, but inside a loop it still makes a new string each time.
Way 2: the clever way
Use a whiteboard that can change: StringBuilder. It keeps a box of letters with spare room at the end.
appendwrites the next letter into the next free box. No copying.- When the boxes are full, it makes a bigger box (about twice as big) and copies once.
- At the end,
toString()carves the final stone once.
Because the box doubles, the total copying is a small multiple of n.
The whiteboard writes in place and grows by doubling
01 / 06
new StringBuilder(2) makes a whiteboard with room for 2 letters. It is empty.
- length
- 0
- capacity
- 2
- seen
- done
- answer
- just added
class Solution {
public String buildString(int n) {
StringBuilder sb = new StringBuilder(n); // room for n letters up front
for (int i = 0; i < n; i++) {
sb.append('a'); // write in place, no new String
}
return sb.toString(); // carve the stone once
}
}How long it takes: each append is O(1) on average, so the loop is O(n). Extra room: the buffer and the final string, O(n). Giving the size up front, new StringBuilder(n), skips the growing step entirely.
Another way: make your own immutable class
Interviewers often follow up with "write an immutable class". These are the same rules Java uses for String.
final class Point { // final: nobody can subclass it and add changes
private final int x; // private final: set once, never again
private final int y;
private final List<Integer> tags;
Point(int x, int y, List<Integer> tags) {
this.x = x;
this.y = y;
this.tags = new ArrayList<>(tags); // copy in, so the caller cannot change it later
}
int getX() { return x; }
int getY() { return y; }
List<Integer> getTags() {
return Collections.unmodifiableList(tags); // read only view out
}
Point withX(int newX) {
return new Point(newX, y, tags); // a "change" makes a new object, like String
}
}Watch out for
Why Java made strings immutable, the four reasons to say in an interview:
- The string pool. Literals are shared by everyone. Sharing is only safe because nobody can change them.
- Security. File paths, URLs, class names and database usernames are checked once and then used. If they could change after the check, an attacker could swap them.
- Threads. Many threads can read the same string with no locks, because nothing can change under them.
- Hashing. A string caches its
hashCode(). That works because the letters never change, which makes strings fast and reliable asHashMapkeys.
Common slips:
- Calling
s.trim()ors.replace(...)and not saving the result. - Passwords in a
String. They stay in memory until garbage collection and may land in the pool. Achar[]can be wiped with zeros right after use. - Using
StringBufferby habit. It locks on every call. UseStringBuilderunless several threads write to the same builder.
What interviewers ask next
- Is a String really impossible to change? Normal code cannot change it. Reflection or native code can break into the private array, but that is a hack and recent Java versions block it by default.
- What does
intern()do? It returns the pool copy of a string, adding it to the pool if needed. Then==works between interned strings. - How is a String stored? Since Java 9, as a
byte[]plus a flag that says Latin-1 (one byte per letter) or UTF-16 (two bytes). This is called compact strings. - Why is
Stringafinalclass? So no subclass can add a method that changes the letters and break all the guarantees above. String,StringBuilder,StringBuffer: when to use which?Stringfor values you keep and share,StringBuilderto build text in one thread,StringBufferonly when threads share one builder.
Remember this
Grown-up words: the stone tag is an immutable object, the shared tag room is the string pool (interning), the whiteboard is a mutable buffer, StringBuilder, and its doubling growth gives amortized O(1) appends.