Why should an immutable class be final?
An immutable class must be final so that no one can subclass it and add mutable fields or methods.
Junior Level
An immutable class must be final so that no one can subclass it and add mutable fields or methods.
public final class Point { // final — cannot be inherited
private final int x;
private final int y;
// ...
}
If you remove final, someone can write:
public class MutablePoint extends Point {
private int z; // new field
public void setZ(int z) { this.z = z; } // mutation!
}
Middle Level
Protection against behavior substitution
Without final, a subclass can:
- Add mutable state — new fields with setters
- Override getters — return different data
- Override equals/hashCode — break operation in
HashMap
Attack scenario
public void process(ImmutablePoint point) {
validate(point); // check coordinates
// here a subclass can change the data
saveToDatabase(point); // save already changed data
}
Examples from JDK
public final class String { ... }
public final class Integer { ... }
public final class BigDecimal { ... }
public final class LocalDate { ... }
What if you need inheritance?
Use Sealed classes (Java 17+) to control heirs, or composition.
When final can be a problem
- Mocking in tests: Mockito cannot mock final classes without bytebuddy-agent
- Proxy frameworks: some require inheritance (Spring AOP via CGLIB)
- Solution: sealed classes or Mockito inline-mock-maker
Senior Level
JMM and JIT guarantees
final class + final fields creates an “impenetrable” shell:
- JMM:
finalfields ensure safe publication with memory barrier - JIT: compiler can aggressively inline methods and cache values, knowing the class won’t be overridden
Problems without final
- HashMap corruption — a subclass can override
hashCode(), returning different values - Security vulnerabilities — TOCTOU attacks through getter substitution
- Thread-safety loss — subclass accesses non-
finalfields without synchronization
Summary for Senior
finalclass prevents logic changes through method overriding- Without
final, you cannot guarantee thread-safety or hash code stability final class— a signal to other developers and the compiler about an unchanging contract- Use
sealedclasses for controlled inheritance (Java 17+)
Interview Cheat Sheet
Must know:
finalprohibits inheritance — subclass cannot add mutability- Without
final, a subclass can: add mutable fields, override getters, break equals/hashCode - HashMap corruption — subclass overrides
hashCode(), object gets lost - Security vulnerabilities — TOCTOU attacks through getter substitution
- JMM:
final class + final fields= impenetrable shell with safe publication - When final interferes: mocking in tests, proxy frameworks (CGLIB); solution: sealed classes or inline-mock-maker
Frequent follow-up questions:
- What happens without final? — Subclass adds mutable fields, breaks hashCode and thread-safety
- Mockito can’t mock final classes? — Need inline-mock-maker or bytebuddy-agent
- Sealed classes as an alternative? — Yes, Java 17+: limit the circle of heirs
- Does JIT benefit from final? — Yes: aggressive inlining, caching, since class won’t be overridden
Red flags (do NOT say):
- “Immutability works without final” — a subclass can break everything
- “Final is just for style” — it’s a guarantee of security and JIT optimizations
- “Mockito doesn’t work with final” — it works with inline-mock-maker
- “Sealed classes = final” — sealed allows inheritance from specific classes
Related topics:
- [[7. What is the final keyword and how does it help create immutable classes]]
- [[15. Can you inherit from an immutable class]]
- [[17. What happens if you override getter in subclass of immutable class]]
- [[1. What is an immutable object]]