Strings in Java are objects that never change once they are created. This immutability means that if you try to modify a string, a new string object is formed instead of altering the old one. The design choice gives strings safety, consistency, and efficiency in the runtime environment.
The reason strings stay immutable is tied to how the String
class is built. The class is marked final
so it cannot be extended, and in modern Java its internal byte[]
value is final
, which prevents that reference from being reassigned after construction. No method ever alters the stored data, so any change you request produces a new object. This structure makes it possible to share string objects in the string pool, avoid accidental changes across threads, and rely on strings as stable values throughout the program.
What Makes Strings Immutable
None of the public methods ever provide a path to overwrite the stored characters. Instead, calls like substring
, replace
, or toLowerCase
return a new String
when the content would change and if nothing changes they can return the original instance. This contract guarantees that the internal data set at construction time never changes, no matter how much you transform or reuse the original reference.
When you call a method that looks like it should change the text, such as concat
, a new string object is created with the new contents, leaving the original untouched.
The first variable never changes, which is why it still holds the original text. That pattern repeats for every operation on strings, which means the internal data always stays safe from modification.
A different layer comes from the fact that strings cache their hash code. Because the characters never change after creation, the cached hash can be reused, making string lookups in hash-based collections like HashMap
faster.
The reason this works well is that the hash code is cached after it’s computed the first time, so later lookups reuse it.
How The String Pool Works
The string pool is a JVM-wide table of interned strings on the heap. When a literal like \"hello\"
is loaded with its class, the VM looks it up in the pool and reuses it if present. If not, it creates and interns a new one. Non literal strings can be added by calling intern()
.
The pooled literals a
and b
share a single reference, while the new String
call forces a fresh object outside the pool.
The pool’s safety depends on immutability. If pooled strings could change, altering one would affect every reference pointing to it, which would make sharing impossible. Because strings never change, the JVM can freely share references without risk.
Why Immutability Matters For Safety
Thread safety is one of the biggest reasons immutability matters. Multiple threads can work with the same string at the same time without the risk of one thread changing the data while another relies on it. This makes strings a safe building block for concurrent applications.
Both threads print the same value without conflict because the shared string can’t be altered.
Another benefit is predictability for things like database URLs and file paths. For secrets such as passwords, prefer char[]
so you can clear it, because a String
can’t be wiped. It also makes them reliable as identifiers. At the Java level, class and method names are represented as String
values and treated as stable identifiers, even though the VM also keeps internal symbol data. Their immutability guarantees that once a class name is linked, it won’t suddenly shift and break execution.
A simple look at HashMap
again helps explain why. If the text in a string key could change, the stored location would no longer match the key’s hash, and retrieval would fail. Because strings don’t change, keys remain consistent for the lifetime of the map.
The stability of the string key makes this retrieval dependable every time.