Why is String immutable or final in Java?
The String class in Java is designed to be immutable (its state cannot be changed after creation) and the class itself is declared final (it cannot be subclassed). These two design choices are closely related and offer several significant advantages:
Why String is Immutable:
-
The absolutely most important reason that String is immutable is that it is used by the class loading mechanism, and thus have profound and fundamental security aspects.
Had String been mutable, a request to load “java.io.Writer” could have been changed to load “mil.vogoon.DiskErasingWriter”
-
String Pool / Interning Efficiency:
- Java maintains a special memory area called the “String pool” (or “String constant pool”). When you create a String literal (e.g.,
String s1 = "Java";), the JVM checks if an identical string already exists in the pool. - If it exists, the existing String object’s reference is returned. If not, a new String object is created in the pool, and its reference is returned.
- Immutability is crucial here: If Strings were mutable, one reference changing the String’s value would affect all other references pointing to the same object in the pool. This would lead to unpredictable behavior and data corruption. For example:\
String s1 = "Hello"; String s2 = "Hello"; // s2 points to the same object as s1 in the pool // If String were mutable and s1 could change its value: // s1.changeValueTo("World"); // Hypothetical mutable operation // Then s2 would also become "World", which is likely not intended. - This pooling mechanism saves memory by avoiding redundant String objects and can speed up comparisons (though
equals()should always be used for content comparison, not==for pooled strings).
- Java maintains a special memory area called the “String pool” (or “String constant pool”). When you create a String literal (e.g.,
-
Security:
- Strings are widely used as parameters for sensitive operations, such as opening files, network connections, database queries, or storing usernames/passwords.
- If Strings were mutable, a malicious party could change the String’s value after it has been validated but before it’s used. For example:
// Hypothetical scenario if String were mutable void accessResource(MutableString path) { if (!isValidPath(path)) { throw new SecurityException("Invalid path"); } // At this point, another thread or even the calling code // could change 'path' to something malicious like "/etc/passwd" // before the actual file access happens. File f = new File(path.toString()); // ... access f } - Immutability ensures that once a String is created and validated, its value cannot be changed, preventing such security vulnerabilities.
-
Thread Safety:
- Immutable objects are inherently thread-safe. Since their state cannot be changed after creation, multiple threads can share and access String objects concurrently without any risk of race conditions or data corruption, and without the need for explicit synchronization. This simplifies multi-threaded programming.
-
Caching HashCode:
- The
hashCode()method ofStringis frequently used in hash-based collections likeHashMap,HashSet, etc. - Because a String is immutable, its hash code can be calculated once when the String is created and then cached. Subsequent calls to
hashCode()simply return the cached value, which improves performance. - If Strings were mutable, the hash code would have to be recalculated every time, or if it changed, the String would become “lost” in a
HashMap(as it would be stored in a bucket based on its old hash code).
- The
-
Performance Optimizations:
- The JVM and JIT compiler can perform various optimizations knowing that String objects won’t change. For example, certain string concatenations can be optimized at compile time.
Why the String Class is final:
- To Guarantee Immutability:
- If the
Stringclass were notfinal, someone could subclass it and override its methods to make it mutable.// Hypothetical if String were not final class MyMutableString extends String { // ... override methods to allow mutation ... } - If you received an object of type
String, you could no longer be certain it was truly immutable; it might be an instance ofMyMutableString. - By making the
Stringclassfinal, the Java designers ensured that any object that is aninstanceof Stringis guaranteed to behave according to the immutable contract of thejava.lang.Stringclass. This upholds all the benefits of immutability (security, pooling, thread safety, hashing).
- If the
In summary, making String immutable provides security, efficiency, and thread-safety. Making the String class final is a crucial step to enforce and guarantee this immutability. When you need a mutable sequence of characters, Java provides StringBuilder and StringBuffer.