Immutability in Java — How and Why
Fast recall
Quick revision
Core answer
Whether you understand the full recipe for creating immutable objects and the thread-safety benefits
Declaring fields final but forgetting defensive copies — if a final field holds a reference to a mutable List, the object is NOT truly immutable
Mention Java records as a modern shortcut, explain the connection between immutability and thread safety, and note that String is the most famous immutable class
Interview-ready guide
Interview answer
The trap with immutable classes is that final prevents reassignment, not mutation — a final List can still have elements added or removed.
Four requirements for true immutability:
- Make the class
final— prevents mutable subclasses from adding state or overriding methods - All fields
private final— prevents direct access and reassignment after construction - No setters or mutation methods — prevents internal state changes
- Defensive copies for mutable components — the step most people miss
Defensive copying:
this.tags = tagsmeans the caller's list and your internal field share the same object — mutating one mutates boththis.tags = List.copyOf(tags)creates an independent unmodifiable copy — modifications from neither side affect the otherCollections.unmodifiableList(tags)is insufficient — it wraps the original, so changes to the original still leak through
Records and immutability:
- Records handle
finalclass,finalfields, and no setters automatically - Records do not defensively copy mutable components — add
Order { items = List.copyOf(items); }in the compact constructor
I recommend List.copyOf() over Collections.unmodifiableList() for defensive copies — it provides true independence from the original.
Immutability in Java — How and Why
Direct Answer
An immutable class is one whose state cannot change after construction. To create one: make the class final, make all fields private and final, don't provide setters, perform defensive copies of mutable fields in the constructor and getters, and initialize all fields via the constructor. Immutability matters because immutable objects are inherently thread-safe, safe as HashMap keys, and eliminate entire categories of bugs.
Deep Dive & Architecture
The diamond problem is the ambiguity that appears when one type inherits the same method from two paths — which version runs?
Java forbids multiple inheritance of classes precisely to avoid this.
With Java 8 default methods in interfaces, the diamond can re-appear, so the compiler forces you to resolve it explicitly: if a class inherits conflicting default methods from two interfaces, it must override the method and can call a specific parent version via InterfaceName.super.method(). The rule order: class wins over interface, more specific interface wins over less specific, and explicit override is required otherwise (compile error).
A common mistake is confusing default methods with abstract methods, or not knowing the resolution rules when two interfaces provide conflicting default methods
Common Mistakes & Pitfalls
Declaring fields final but forgetting defensive copies — if a final field holds a reference to a mutable List, the object is NOT truly immutable
Check your understanding
Answer these without looking back at the article.
- What are default and static methods in interfaces, and why were they added in Java 8?
- Enums in Java — More Than Constants
- Association vs Aggregation vs Composition in Java
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.