What is the final keyword and how does it help create immutable classes?
The final keyword in Java has three meanings depending on context:
Basic Level
The final keyword in Java has three meanings depending on context:
- final for a variable = cannot be reassigned
- final for a method = cannot be overridden in a subclass
- final for a class = cannot be inherited from
1. final variable
final int MAX = 100;
// MAX = 200; // ERROR! Cannot reassign
2. final method
public final void doSomething() {
// This method cannot be overridden in a subclass
}
3. final class
public final class MyImmutableClass {
// This class cannot be subclassed
}
For immutable classes
Use final for the class and for all fields:
public final class Point {
private final int x;
private final int y;
// ...
}
When final on a field gets in the way
final is incompatible with lazy initialization — if the value is computed on first access, the field cannot be final. Use the holder pattern or AtomicReference.
Intermediate Level
final at the class level
Declaring a class as final prohibits inheritance. This is critical for immutability: a subclass cannot add mutable fields or override getters.
final at the field level
A final field can only be initialized once — at declaration or in the constructor:
public final class Config {
private final String url; // initialized in the constructor
private final int timeout = 30; // or immediately at declaration
public Config(String url) {
this.url = url;
}
}
final doesn’t protect contents
private final List<String> list = new ArrayList<>();
list.add("New Item"); // ALLOWED! The reference is the same, contents changed
That’s why collections need additional defensive copying.
final methods
If the class is not final, but individual methods are final — this doesn’t ensure full immutability, but protects specific methods from being overridden.
Advanced Level
Safe Publication and JMM Guarantees
final ensures a “freeze” of the value at the end of the constructor. Any thread that sees a reference to the object is guaranteed to see its final fields in an initialized state. Without final, a situation is possible where a thread sees the object but its fields still contain default values (null or 0) due to instruction reordering by the processor.
JIT optimizations
The JIT compiler can more aggressively optimize code:
- Inlining — substituting
finalfield values directly - Constant folding — computing expressions at compile time
- Hoisting — lifting
finalfields out of loops, since they don’t change
final and the module system (Java 9+)
Starting with Java 16 (JEP 396 — Strong Encapsulation), reflection access to final fields of java.base is blocked by default. In Java 17 (JEP 403), this was strengthened for all JDK internals. --add-opens is required.
Summary for Advanced
finalclass prevents breaking immutability through inheritancefinalfields ensure atomic data visibility in multi-threading (JMM freeze)finalreference ≠ immutable data at the reference — defensive copy is needed- JIT uses
finalfor aggressive optimizations
Interview Cheat Sheet
Must know:
finalfor variable = cannot reassign, for method = cannot override, for class = cannot inherit- For immutability:
finalclass +finalfields = safe publication (JMM freeze) finaldoesn’t protect contents at the reference —final Listcan be modified, a copy is needed- JIT optimizations: inlining, constant folding, hoisting — all thanks to
final - Safe publication: after construction, all
finalfields are visible to any thread - Starting with Java 16, reflection to final fields of java.base is blocked (JEP 396)
Frequent follow-up questions:
- Where can final fields be initialized? — At declaration or in the constructor
- Can a final class be mocked in tests? — Mockito requires inline-mock-maker or bytebuddy agent
- final reference = immutable data? — No,
final Listallows add/remove — a copy is needed - Why a final method? — So a subclass doesn’t override and break immutability
Red flags (DO NOT say):
- “Final fields are enough for immutability” — without final class and defensive copy, not enough
- “final = const” — final cannot be reassigned, but the object at the reference can change
- “final method makes the class immutable” — only the specific method is protected, not the entire class
- “Reflection can change a final field” — in Java 16+ for java.base this is impossible
Related topics:
- [[1. What is an immutable object]]
- [[3. How to create an immutable class in Java]]
- [[8. Is it enough to make all fields final for immutability]]
- [[16. Why must an immutable class be final]]