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: ```java 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).
-
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: ```java / 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 of `String` is frequently used in hash-based collections like `HashMap`, `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).
-
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 `String` class were not `final`, someone could subclass it and override its methods to make it mutable. ```java / 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 of `MyMutableString`.
- By making the `String` class `final`, the Java designers ensured that any object that is an `instanceof String` is guaranteed to behave according to the immutable contract of the `java.lang.String` class. This upholds all the benefits of immutability (security, pooling, thread safety, hashing).
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`.