Stage 2: Object-oriented programming, lesson 10 of 10

Immutable objects

Intermediate3 min read@since 16Code runs on your Java 25
Explain it forThe essentials plus production detail and pitfalls.

An immutable object can't change after it's created. String, Integer, LocalDate and BigDecimal are all immutable.

Why it's worth it:

  • Thread-safe without locks: nothing changes, so nothing can race.
  • Safe as HashMap keys and in sets, because the hash never changes.
  • Easier to reason about: nobody can modify it behind your back.

How to make a class immutable:

  1. Make fields private final and set them in the constructor.
  2. Provide no setters; "changes" return a new object, such as withPrice(…).
  3. Make the class final, or use a record.
  4. Make defensive copies of mutable inputs and outputs (List.copyOf), or a caller can still change your list.

Example

Java
public record Cart(String owner, List<String> items) {
    public Cart {
        items = List.copyOf(items);                 // defensive copy, unmodifiable
    }

    public Cart add(String item) {                  // returns a new Cart
        var next = new ArrayList<>(items);
        next.add(item);
        return new Cart(owner, next);
    }
}

List<String> source = new ArrayList<>(List.of("Java book"));
Cart cart = new Cart("Asha", source);
source.add("Hack!");                                // doesn't affect cart
Cart bigger = cart.add("Spring book");              // the original stays the same
System.out.println(cart.items());                   // [Java book]
// cart.items().add("x");                           // UnsupportedOperationException

Common mistake

Returning an internal mutable list from a getter. Callers can then change your object's state; return an unmodifiable copy.

Under the hood

Immutability trades a little allocation for a lot of simplicity. Modern JVMs make short-lived objects cheap, so this rarely matters outside hot loops, where a mutable builder such as StringBuilder is the escape hatch. Collections.unmodifiableList is only a read-only view, and changes to the underlying list still show through; List.copyOf makes a real copy.

Check yourself

Which of these is not immutable?

How this connects

Where this leads

You've reached the end of this thread. Try a learning path for what's next.

Part of Crack the Java interview, Upgrade from Java 8 to Java 25.

Was this lesson helpful?

Finished reading? Mark it complete to track your progress.