Understanding String Immutability in Java
In Java, a string is considered immutable, meaning its value cannot be changed after creation. This might seem counterintuitive, especially when you append or concatenate strings. Let’s break down what actually happens.
The String Constant Pool
When you create a string literal, Java stores it in a special area of memory called the String Constant Pool. This pool is designed to optimize memory usage by storing identical strings only once. If you create multiple strings with the same value, they will all point to the same object in the pool.
String s1 = "Java";
String s2 = "Java";When we create these two strings, we might initially think that two separate objects are being created in memory. However, due to Java’s string interning mechanism, only one object is actually created.
String Interning:
- String Constant Pool: Java maintains a String Constant Pool in the heap memory. This pool stores unique string literals.
- Object Reuse: When a string literal is encountered, the JVM checks if it already exists in the pool. If it does, it reuses the existing object. If not, it creates a new object and adds it to the pool.
In the given example:
- The first string
s1 = "Java"is created and added to the String Constant Pool. - When the second string
s2 = "Java"is created, the JVM finds that "Java" already exists in the pool and reuses the same object.
Both s1 and s2 are references to the same string object in the String Constant Pool. Therefore, comparing them using == will result in true, as they point to the same memory location.
String s1 = "Java";
String s2 = "Java";
System.out.println(s1 == s2); // returns trueConcatenation and New Objects
When you concatenate strings, Java doesn’t modify the existing strings. Instead, it creates a new string that is the combination of the original strings. This new string is then stored in the String Constant Pool.
String str = "Hello";
str += " World";
System.out.println(str); // Creates a new string "Hello World"In this code, we’re initially creating a string str with the value "Hello". When we concatenate " World" to it, we're not modifying the original "Hello" string. Instead, Java creates a new string, "Hello World," and assigns it to the str variable.
Get CodeBook’s stories in your inbox
Join Medium for free to get updates from this writer.
Why is this happening?
- String Constant Pool: As discussed earlier, strings are stored in the String Constant Pool, a special area in the heap. This pool ensures that identical strings are only stored once, improving memory efficiency.
- New Object Creation: When you concatenate strings, Java creates a new string object in the String Constant Pool. This new object contains the combined content of the original strings.
- Reassignment: The
strvariable is then reassigned to reference this new string object. The original "Hello" string remains unchanged in the pool. And if “Hello” string no longer referenced by any active part of the program, it will eventually reclaimed by the garbage collector.
Visualizing the Process:
Before concatenation:
- str: "Hello" (points to the "Hello" string in the String Constant Pool)
After concatenation:
- str: "Hello World" (points to the newly created "Hello World" string in the String Constant Pool)Mutable Alternatives: StringBuilder and StringBuffer
If you need to modify strings frequently, you can use the StringBuilder or StringBuffer classes. These classes are mutable, meaning you can change their contents without creating new objects.
StringBuilder vs. StringBuffer: A Closer Look
Both StringBuilder and StringBuffer classes in Java are designed for efficient mutable string manipulation. However, they differ in their underlying implementation and thread safety.
Thread Safety:
- StringBuffer: Synchronized methods are used in
StringBufferto ensure thread safety. This means that multiple threads can safely modify the sameStringBufferobject without causing data corruption. However, synchronization comes at the cost of performance. - StringBuilder:
StringBuilderis not synchronized. It is faster thanStringBufferbecause it avoids the overhead of synchronization. However, it is not thread-safe. If multiple threads need to modify the sameStringBuilderobject, explicit synchronization must be implemented.
// StringBuilder (not thread-safe)
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");
System.out.println(sb.toString()); // Output: Hello World
// StringBuffer (thread-safe)
StringBuffer sb2 = new StringBuffer("Hello");
sb2.append(" World");
System.out.println(sb2.toString()); // Output: Hello WorldUse Cases:
- StringBuffer: Use
StringBufferwhen thread safety is a concern. For example, if multiple threads are concurrently modifying a string,StringBuffercan prevent race conditions. - StringBuilder: Use
StringBuilderwhen thread safety is not required and performance is a priority. For example, in single-threaded applications or when modifications are performed within a synchronized block.
Practical Considerations
Concatenation Performance
While immutability is generally efficient, excessive string concatenation can lead to performance issues due to the creation of multiple new objects. In such cases, consider using StringBuilder or StringBuffer for more efficient mutable string operations.
String Comparison
When comparing strings for equality, use the equals() method instead of the == operator. The == operator compares references, while equals() compares the actual string content.









