Why Is String Immutable in Java?
Fast recall
Quick revision
Core answer
replace() return a result while the original remains unchanged. Stable content supports safe sharing, interning, and reliable map keys.Interview-ready guide
Interview answer
String immutability means that an existing String object's text cannot change after construction. It does not mean a variable can never change. In String name = "alice", the object holds alice; later assigning another String to name only changes which object the variable refers to.
Java protects the guarantee in several ways. String is final, its stored state is private, and its public API does not expose a mutator. A call such as String upper = name.toUpperCase() returns a value while the original stays alice. Final alone would not make a class immutable; callers also need no path to alter its state.
A stable text value can be passed to another method or thread without that receiver changing it for everyone else. The JVM can safely share interned strings, and a String used as a HashMap key keeps the same content after insertion. These benefits all come from the value staying fixed, not from memorizing one internal field declaration.
Immutability does not promise a fresh object for every call. If an operation makes no change, a method may return the same String instance. It also should not be defined as 'a private final char array': OpenJDK has changed its storage representation. The reliable contract is that no caller can observe the existing String's value changing.
Several threads can read the same String value without a lock because none can mutate that object. However, a shared field such as currentName may still be reassigned from one String to another. That update needs suitable publication, such as a final field at construction, volatile, or locking, when other threads must see it.
Why Is String Immutable in Java
Direct Answer
String is immutable because Java exposes it as a fixed value: the class is final, its state is private, and its API has no operation that changes an existing object's text. Methods such as replace() return a result while the original remains unchanged. Stable content supports safe sharing, interning, and reliable map keys.
Deep Dive & Architecture
The class closes every mutation route
An immutable class must prevent callers from changing the state that defines its value. String is final, so a subclass cannot replace its behavior with a mutable version. Its representation is not exposed for callers to edit, and the API provides operations that calculate String results instead of editing the receiver. Constructors that accept a mutable array copy the relevant content, so later changes to that input array do not alter the String.
No single keyword is enough. A final class could still have a method that changes a field, and a final array reference could still point to an array whose elements are modified. The whole design—construction, encapsulation, and every operation—creates the immutability guarantee.
Stable values remove aliasing surprises
Suppose two components receive the same String reference as a file name or customer ID. Neither component can change that shared object's characters after the other has validated it. The same stability allows literal and interned strings to be shared, and it keeps content-based equality and hashing dependable after a String becomes a map key.
This does not make every surrounding object immutable. A Customer can hold a String name and later replace that field with another String. The old and new String values are both immutable, while the Customer is still mutable.
Result objects and implementation details
Ignoring a returned value is a common practical bug: name.toUpperCase() alone does not update name. Assign the returned result when it is needed. At the same time, do not infer that every apparently changing method must allocate. Some operations can return this when the requested transformation changes nothing.
The private representation has evolved across JDK versions, so explanations based on a particular char[] or byte[] are fragile. The public promise is simpler and stronger: once constructed, a String's character sequence never changes.
Code Example
class StringImmutabilityDemo {
public static void main(String[] args) {
String original = "java";
String changed = original.replace('j', 'J');
System.out.println(original); // java
System.out.println(changed); // Java
String ignored = "hello";
ignored.toUpperCase();
System.out.println(ignored); // hello
ignored = ignored.toUpperCase();
System.out.println(ignored); // HELLO
if (!original.equals("java")) {
throw new AssertionError("The original String changed");
}
}
}
The first transformation keeps both values. The second call shows why code must use the returned value when it wants the transformed text.
Check your understanding
Answer these without looking back at the article.
- Why is final by itself not enough to make a class immutable?
- How does String immutability help when a String is used as a HashMap key?
- Why does ignoring the result of replace() or toUpperCase() cause a bug?
- Does String immutability make a reassigned shared field automatically thread-safe?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.