Question 7 · Section 13

What is the final keyword and how does it help create immutable classes?

The final keyword in Java has three meanings depending on context:

Language versions: English Russian Ukrainian

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 final field values directly
  • Constant folding — computing expressions at compile time
  • Hoisting — lifting final fields 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

  • final class prevents breaking immutability through inheritance
  • final fields ensure atomic data visibility in multi-threading (JMM freeze)
  • final reference ≠ immutable data at the reference — defensive copy is needed
  • JIT uses final for aggressive optimizations

Interview Cheat Sheet

Must know:

  • final for variable = cannot reassign, for method = cannot override, for class = cannot inherit
  • For immutability: final class + final fields = safe publication (JMM freeze)
  • final doesn’t protect contents at the reference — final List can be modified, a copy is needed
  • JIT optimizations: inlining, constant folding, hoisting — all thanks to final
  • Safe publication: after construction, all final fields 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 List allows 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]]