Learn why String is immutable in Java and how immutability improves security, String pooling, thread safety, hashCode caching and performance.
Focus Keywords
String immutable in Java, why String is immutable in Java, Java String immutability, String immutability, immutable String Java, Java String
Tags
Java, Core Java, Java String, String Immutability, Java Programming, Java Concepts, JVM, Java Interview, Java Developers, Java Performance, String Pool, Java Security, Java Beginners
Why Is String Immutable in Java? The Real Reason Behind It
If you've worked with Java, you've probably noticed something unusual about String.
Consider this code:
String name = "Java";
name = name + " Programming";
System.out.println(name);
It looks like the existing String has been modified.
But that's not actually what happens.
The original "Java" String is not changed. Instead, Java creates another String containing "Java Programming" and makes the variable name refer to the new object.
This behavior is known as String immutability.
But why did Java designers make String immutable?
It's not simply because "immutable objects are safer."
String immutability plays an important role in several parts of Java, including:
- String pooling
- Security
- Thread safety
- Hash-based collections
- Performance
- Class loading
- Predictable program behavior
Let's understand what really happens.
What Does Immutable Mean?
An immutable object is an object whose state cannot be changed after it has been created.
For example:
String text = "Hello";
Once the String object representing "Hello" exists, its contents cannot be changed.
If you write:
text = "World";
you haven't changed "Hello" into "World".
Instead, text now refers to another String.
Conceptually:
Before:
text
↓
"Hello"
After:
text
↓
"World"
The original "Hello" object remains unchanged.
Strings Are Objects in Java
One important thing to understand is that String is not a primitive data type.
For example:
int age = 25;
int is a primitive type.
But:
String name = "Pavan";
String is a class.
Conceptually, you can think of:
String
↓
Object
↓
Immutable value
The String class is designed so that its character sequence cannot be modified after construction.
What Happens When You "Change" a String?
Consider:
String s = "Hello";
s = s + " World";
Many beginners think Java does something like:
"Hello"
↓
"Hello World"
But the original object isn't modified.
Conceptually, the process is:
String object 1
↓
"Hello"
+
String object 2
↓
" World"
↓
New String object
↓
"Hello World"
↓
s refers to new object
The variable changes its reference.
The original String remains immutable.
Why Did Java Make String Immutable?
There isn't one single reason.
String immutability provides several important advantages.
The major reasons are:
- Security
- String Pool
- Thread Safety
- HashCode Caching
- Performance and Predictability
- Safe Use as HashMap Keys
- Class Loading and System-Level Operations
Let's examine each one.
1. Security
Security is one of the most important reasons String immutability is useful.
Strings are frequently used for sensitive or security-related information such as:
File paths
URLs
Database connection information
Class names
Usernames
Network addresses
File permissions
Configuration values
Imagine a Java API receives a String representing a file path.
void openFile(String path) {
// validate path
// open file
}
Suppose the String could be modified after validation.
The program might perform:
1. Receive path
2. Validate path
3. Open path
If the value could change between steps 2 and 3, security checks could become unreliable.
Because Strings are immutable, once the value has been created, its contents cannot be altered.
This makes Strings safer to use when values need to remain stable.
2. String Pool
This is one of the most important reasons String immutability matters.
Java maintains a special area commonly called the String Pool for interned String values.
Consider:
String a = "Java";
String b = "Java";
Java can allow both references to point to the same pooled String object.
Conceptually:
String Pool
"Java"
/ \
/ \
a b
Both a and b can refer to the same immutable object.
Now imagine Strings were mutable.
Suppose:
a.changeTo("Python");
If a and b referred to the same object, changing the object through a could unexpectedly change what b observes.
You could end up with:
a → "Python"
b → "Python"
even though b was originally created as:
String b = "Java";
That would be disastrous.
Because Strings are immutable, Java can safely share identical String objects.
3. String Pool Saves Memory
String pooling also helps reduce unnecessary duplicate String objects.
Consider:
String a = "Java";
String b = "Java";
String c = "Java";
Instead of necessarily creating three separate objects for the same literal value, Java can reuse the same pooled String.
Conceptually:
a ─┐
b ─┼──→ "Java"
c ─┘
This can reduce memory usage.
But this optimization is safe precisely because the shared String cannot be modified.
4. Thread Safety
Immutable objects are naturally easier to share between threads.
Suppose multiple threads have access to:
String message = "Hello Java";
Because nobody can modify the String itself, threads can safely read it without needing synchronization just to protect the String's contents.
Conceptually:
"Hello Java"
/ | \
/ | \
Thread 1 Thread 2 Thread 3
All threads can safely read the same immutable object.
This doesn't mean every operation involving a String or every surrounding data structure is automatically thread-safe. It means the String's own state cannot be mutated.
5. HashCode Caching
Here's another interesting reason.
Strings are frequently used as keys in hash-based collections.
For example:
Map<String, Integer> employees = new HashMap<>();
employees.put("Java", 100);
A HashMap uses the key's hash value to determine where the entry belongs.
If the contents of a String could change after it was inserted, its hash code could also change.
That could cause a serious problem.
Imagine:
Map<String, String> map = new HashMap<>();
String key = "Java";
map.put(key, "Programming");
Suppose the key could later change:
"Java"
↓
"Python"
Its hash code would potentially change.
The HashMap might have stored the entry based on the original hash.
Now the map could have difficulty finding the entry using the modified key.
Because String is immutable, its value remains stable.
6. Strings Work Reliably as HashMap Keys
This is why code like this works so reliably:
Map<String, Integer> scores = new HashMap<>();
scores.put("Java", 90);
scores.put("Python", 85);
System.out.println(scores.get("Java"));
The key's contents don't unexpectedly change after insertion.
This is an important property of immutable objects used as keys.
7. String Hash Codes Can Be Cached
There is another performance benefit.
Because a String cannot change, its hash code remains the same throughout its lifetime.
Java can therefore cache the computed hash value in the String implementation.
Conceptually:
First hashCode() call
↓
Calculate hash
↓
Cache result
Later hashCode() calls
↓
Reuse cached result
This can avoid repeatedly calculating the same hash value.
This is particularly useful because Strings are commonly used in hash-based collections.
8. Predictable Behavior
Immutability makes Strings easier to reason about.
Consider:
String username = "admin";
You can confidently pass this String to another method:
validateUser(username);
The method cannot modify the contents of the String object itself.
For example:
void validateUser(String username) {
// username itself cannot be modified
}
The method could assign its local parameter to another String, but that doesn't modify the original String object.
This makes APIs easier to reason about.
9. Strings Are Used Everywhere in Java
Another reason String immutability is valuable is simply how fundamental Strings are.
Strings are used throughout Java applications.
For example:
String username;
String password;
String url;
String filename;
String className;
String sql;
String json;
String email;
They are also used extensively by Java APIs and frameworks.
Having a stable, immutable text type makes the entire ecosystem easier to design.
A Simple Example That Demonstrates Immutability
Consider:
String original = "Hello";
String modified = original.concat(" World");
System.out.println(original);
System.out.println(modified);
Output:
Hello
Hello World
Notice that original is still:
Hello
The concat() operation didn't modify the original String.
It produced another String.
What About toUpperCase()?
Consider:
String name = "java";
String result = name.toUpperCase();
System.out.println(name);
System.out.println(result);
Output:
java
JAVA
Again, the original String wasn't modified.
What About replace()?
Consider:
String language = "Java";
String result = language.replace("Java", "Python");
System.out.println(language);
System.out.println(result);
Output:
Java
Python
The original String remains unchanged.
Why Does This Code Look Like String Is Changing?
Consider:
String name = "Java";
name = name.concat(" Developer");
It may look like name has been modified.
But what's actually happening?
Conceptually:
Before:
name
↓
"Java"
concat()
↓
Creates new object
↓
"Java Developer"
name
↓
"Java Developer"
The reference stored in name changed.
The original String did not.
This distinction is extremely important.
String vs StringBuilder
If Strings are immutable, what should you use when you need to modify text repeatedly?
That's where StringBuilder can be useful.
For example:
StringBuilder builder = new StringBuilder();
builder.append("Java");
builder.append(" ");
builder.append("Developer");
System.out.println(builder);
StringBuilder is mutable.
Conceptually:
String
"Java"
↓
new object
↓
"Java Developer"
StringBuilder
"Java"
↓
same object modified
↓
"Java Developer"
For repeated string modifications, StringBuilder is often more appropriate.
String vs StringBuffer
Java also provides StringBuffer.
FeatureStringStringBuilderStringBuffer
Mutable
No
Yes
Yes
Thread synchronization
Immutable state
Not synchronized
Synchronized methods
Best use
Text values
Frequent modifications
Certain synchronized/multithreaded use cases
Performance for repeated modifications
Usually not ideal
Generally preferred
Generally slower than StringBuilder
For most ordinary single-threaded string-building tasks, StringBuilder is the usual choice.
Is Immutability the Only Reason for String Pooling?
No.
This is an important interview distinction.
You may hear:
"String is immutable because Java has a String Pool."
That's incomplete.
A better explanation is:
String immutability makes sharing pooled String objects safe and practical.
The String Pool and immutability are closely related, but one isn't simply the sole cause of the other.
Why Can't We Just Make String Mutable?
You could theoretically design a programming language with mutable strings.
But Java would lose several useful properties.
A mutable String could cause problems with:
Shared objects
a ─┐
├──→ "Java"
b ─┘
Changing it through a could affect b.
HashMap keys
Changing a key after insertion could invalidate its expected hash-based location.
Security
Values could change after validation.
Thread safety
Shared Strings would require more synchronization.
Caching
Cached hash values would become more complicated.
API predictability
Methods receiving Strings could potentially modify values unexpectedly.
Making String immutable avoids these problems.
Is String Truly Immutable?
From a Java programmer's perspective, yes: the observable character sequence of a String cannot be changed after the String object is created.
There are implementation details inside the JDK, but application code is not supposed to mutate a String's internal state.
Modern JDK implementations have also changed how String data is represented internally over time. For example, modern Java implementations can use compact representations for certain strings. These implementation details don't change the fundamental immutability contract exposed by the String API.
Can Reflection Change a String?
This is a common advanced question.
Historically, developers have found ways to use reflection or unsafe/internal mechanisms to manipulate private implementation details.
However, such techniques are not normal Java programming and should not be used to argue that String is mutable.
The Java API contract treats String as immutable.
For application development, the correct assumption is:
String = Immutable
Why Is String final?
Another interesting detail is that String is declared as a final class:
public final class String
This means other classes cannot extend it.
Why is this useful?
Imagine if someone could create a subclass of String and override behavior related to its value.
That could undermine assumptions made throughout the Java platform about Strings being immutable and predictable.
Making the class final prevents subclass-based alteration of String behavior.
So two important characteristics work together:
String
├── final class
└── immutable objects
String Immutability and Security: A Real-World Example
Imagine an application checks whether a user has permission to access:
/admin/reports
The application validates the path and then uses it.
If the value could change between validation and use, you could potentially have a time-of-check/time-of-use style problem.
With immutable Strings, the value being passed around cannot simply be changed by another piece of code.
This is one reason immutable values are particularly useful for security-sensitive operations.
Why Is This Important in Interviews?
String immutability is one of the most common Core Java interview topics.
An interviewer might ask:
"Why is String immutable?"
Don't answer only:
"For security."
That's incomplete.
A stronger answer is:
String is immutable because immutability provides several important benefits in Java. It makes String pooling safe, allows Strings to be shared between threads without modifying their contents, makes them reliable as HashMap keys, allows hash codes to be cached, and improves predictability and security when Strings represent sensitive or system-level values.
That's a much stronger interview answer.
