Why is String
Immutable and Final in Java?
The String
class in Java is immutable (cannot be modified) and final (cannot be subclassed). This design choice was made for security, performance, and thread safety reasons.
1. Security
If String
were mutable, it could be changed unintentionally or maliciously, leading to security vulnerabilities.
Example:
public class SecurityExample {
public static void main(String[] args) {
String password = "mySecret";
// If String were mutable, a malicious function could change its value!
authenticate(password);
System.out.println(password); // Still "mySecret", ensuring security
}
static void authenticate(String password) {
// If String were mutable, an attacker could modify `password`
}
}
🛑 Problem if String were mutable: A hacker could modify password
while being processed.
✅ Solution: Since String
is immutable, it remains unchanged, ensuring safe authentication.
Security Example: Preventing URL Tampering
String url = "https://secure-site.com/login";
connectToServer(url);
If url
were mutable, a malicious program could change "secure-site.com"
to "fake-site.com"
.
2. Memory Optimization (String Pool)
Since String
is immutable, Java reuses strings efficiently using the String Pool.
Example:
String s1 = "Hello";
String s2 = "Hello"; // Same reference as s1
System.out.println(s1 == s2); // true
✅ Benefit: Java reuses existing string literals, reducing memory consumption.
If String
were mutable, two references (s1
and s2
) could point to the same object, leading to unexpected changes.
3. Thread Safety
Since String
objects cannot be modified, they are inherently thread-safe.
Example:
class SharedResource {
static String message = "Hello";
}
public class ThreadSafetyExample {
public static void main(String[] args) {
new Thread(() -> System.out.println(SharedResource.message)).start();
new Thread(() -> System.out.println(SharedResource.message)).start();
}
}
✅ Benefit: No need for synchronization—multiple threads can safely share a String
object.
If String
were mutable, one thread could change it while another is reading, causing race conditions.
4. HashCode Caching for Performance
- Since
String
is immutable, its hash code is computed only once and cached. - This improves performance when using
String
in hash-based collections (HashMap
,HashSet
).
Example:
public class HashCodeExample {
public static void main(String[] args) {
String s = "Java";
System.out.println(s.hashCode()); // Computed once and cached
System.out.println(s.hashCode()); // Fast lookup
}
}
✅ Benefit: HashMap
lookups are faster because the hash code does not change.
If String
were mutable, the hash code could change, making it unusable in hash-based collections.
5. Preventing Unintended Changes
If String
were mutable, a single modification could affect multiple references.
Example:
String s1 = "Java";
String s2 = s1; // s1 and s2 point to the same object
s1 = s1 + " Rocks!"; // Creates a new object, s2 remains unchanged
System.out.println(s1); // Java Rocks!
System.out.println(s2); // Java
✅ Benefit: Modifications create new objects, preventing accidental changes.
If String
were mutable, modifying s1
would also modify s2
, leading to unpredictable behavior.
6. final
Prevents Subclassing
The final
keyword prevents inheritance of the String
class.
public final class String {
// Implementation
}
✅ Why?
- Prevents overriding methods that could break immutability.
- Protects security-sensitive operations (e.g., hashing, pooling).
If String
were not final, malicious subclasses could modify its behavior.
Key Takeaways
Reason | Benefit |
---|---|
Security | Prevents modification of sensitive data (e.g., passwords, URLs). |
Memory Efficiency | Enables String Pooling (string reuse, less memory). |
Thread Safety | No need for synchronization—safe for multithreading. |
Performance Boost | Hash code is cached, making hash-based collections faster. |
Predictability | Prevents unintended modifications when passing strings around. |
Prevent Subclassing (final ) | Ensures immutability and prevents security risks. |
Conclusion
✅ Java String
is immutable to ensure security, performance, and thread safety.
✅ Being final
prevents modification through subclassing.
✅ This makes Java’s string handling highly efficient and reliable.