Back to String & String Pool
Q1Question 1 of 12

Why is String immutable in Java?

Key Concepts
  • String is final (cannot be subclassed) and its internal char array is never exposed
  • Security: connection URLs, file paths, class names cannot be altered after creation
  • String Pool: immutability allows safe sharing of one object for identical literals
  • Thread safety: immutable objects are inherently thread-safe without synchronization
  • HashMap key safety: String's hashCode is cached and never changes
  • Performance: hashCode computed once and cached, safe for constant folding by the compiler

Definition

String is immutable for security (credentials/URLs cannot be tampered), String Pool sharing (one object for identical literals), thread safety (no locks needed), and HashMap key stability.

Five reasons String is immutable:

  • Security. Database URLs, file paths, and class names stored as Strings cannot be modified after creation.
  • String Pool. The JVM reuses one String object for all identical literals. This is safe only because Strings never change.
  • Thread safety. Immutable objects are inherently safe to share across threads.
  • HashMap keys. String caches its hashCode. If the String could change, the cached hash would be wrong and HashMap would break.
  • Compiler optimization. The compiler can fold constant String expressions at compile time because the value never changes.

A bank's printed account number on a cheque. Once printed, it cannot be altered. This makes it safe to photocopy (String Pool sharing), use as a filing index (HashMap key), and pass to multiple departments simultaneously (thread safety).

Understanding the Concepts

How is String immutable internally?

The String class is designed with multiple layers of protection to ensure its contents can never change after construction.

JAVA
public final class String {  // final: cannot be subclassed
    private final byte[] value;  // final: reference never changes
    private final byte coder;    // LATIN1 or UTF16 encoding flag
    private int hash;            // cached, computed on first hashCode() call

    // No method modifies value[]. Methods like toUpperCase() return a NEW String.
    // The byte[] is never exposed to outside code.
}

Three mechanisms work together: the class is final so no subclass can add mutation methods, the internal array is private final so the reference cannot be reassigned, and no method ever modifies the array contents or returns a reference to it.

What happens when you "modify" a String?

Every method that appears to modify a String actually creates and returns a new String object. The original is untouched.

JAVA
String account = "SAV2001";
String upper = account.toUpperCase();  // creates a NEW String

System.out.println(account);  // SAV2001 (unchanged!)
System.out.println(upper);    // SAV2001 (new object, same content here)
System.out.println(account == upper);  // false (different objects)

The same applies to concat(), replace(), substring(), trim(), and every other String method.

Why does String Pool need immutability?

The String Pool allows the JVM to reuse a single String object for all identical string literals. This saves significant heap memory. But sharing is safe only because Strings cannot change.

JAVA
String a = "SAV2001";  // goes to the String Pool
String b = "SAV2001";  // reuses the SAME pooled object

System.out.println(a == b);  // true – same object in the pool

// If String were mutable:
// a.setValue("HACKED");  // would change b's value too!
// Both a and b would show "HACKED" – a catastrophic security breach
// Immutability prevents this – the shared object is safe

Why does HashMap need immutable keys?

String caches its hashCode on first computation and returns the cached value on subsequent calls. This is safe because the value never changes. If String were mutable and someone changed the value, the cached hashCode would be wrong and HashMap would look in the wrong bucket.

JAVA
package com.bank.service;

import java.util.HashMap;
import java.util.Map;
import java.math.BigDecimal;

public class AccountCache {
    private final Map<String, BigDecimal> balances = new HashMap<>();

    public void cache(String accountId, BigDecimal balance) {
        // String's hashCode is computed once and cached
        // Since accountId never changes, the cached hash stays valid
        balances.put(accountId, balance);
    }

    public BigDecimal getBalance(String accountId) {
        // HashMap finds the entry using the cached hashCode
        return balances.get(accountId);
    }
}

What about security?

In banking applications, Strings hold security-sensitive data: database connection URLs, file paths, class names loaded by the class loader, and user credentials. Immutability ensures that once a String passes a security check, it cannot be modified afterward.

JAVA
// Database connection URL
String dbUrl = "jdbc:postgresql://prod-db:5432/bank";
// If someone could mutate this after validation:
// dbUrl.replace("prod-db", "hacker-db");  // would redirect silently
// Immutability prevents this – the validated URL stays valid

// Class loading
Class<?> cls = Class.forName("com.bank.TransferService");
// If the class name String could be mutated after the security check,
// a different class could be loaded

What are common mistakes?

  • Thinking += modifies the String. s += "x" creates a new String and reassigns the reference. The original String object is unchanged.
  • Using String in a loop for concatenation. Each += creates a new object. Use StringBuilder instead.
  • Expecting a.toUpperCase() to modify a. It returns a new String; a is unchanged.

What should a senior engineer know?

  • Compact Strings (Java 9+). String uses byte[] with LATIN1 encoding for ASCII strings (1 byte per char) and UTF16 for non-ASCII (2 bytes). This halves memory for ASCII-heavy workloads, which describes most banking system data.
  • String deduplication (G1 GC). -XX:+UseStringDeduplication makes G1 identify Strings with the same content and share the underlying byte[]. Reduces heap for applications with many duplicate Strings.
  • Text Blocks (Java 15+). Triple-quoted strings for multi-line: """SELECT * FROM accounts WHERE id = ?""". Still immutable.
  • Strings and sensitive data. For passwords, prefer char[] over String because you can explicitly zero out the array after use. Strings linger in the pool and on the heap until GC collects them.

Visual Overview

DIAGRAM
  String is immutable because:
    1. Security:    URLs, paths, class names cannot be tampered
    2. String Pool: safe sharing of identical literals
    3. Thread safe: no locks needed
    4. HashMap key: cached hashCode stays valid
    5. Performance: compiler constant folding, hashCode caching

  Internally:
    public final class String {       ← final: no subclass
        private final byte[] value;   ← final: never reassigned
        // No method modifies value[]
        // toUpperCase() → NEW String
    }

Key Takeaway

String is immutable for security, String Pool sharing, thread safety, and HashMap key stability. The class is final, the internal byte array is never exposed, and every "modification" method returns a new String.

This enables the String Pool (one object for identical literals), safe caching of hashCode for HashMap, and inherent thread safety without synchronization.

One sentence to remember: String is immutable so the Pool can share, HashMap keys stay stable, threads are safe, and security-sensitive values cannot be tampered.

---

Share
Use ← → arrow keys to navigate