Command Palette

Search for a command to run...

PHASE 5Intermediate ~28 min· topic 10 of 13

Topic 5.10

The final Keyword

In one line

final means 'can't be changed after it's set': a final variable can be assigned only once, a final method can't be overridden, and a final class can't be extended. A final reference can't point elsewhere, but the object it points to can still change.

Think of it like this

Writing in pen versus pencil. Something written in pen (a final variable) can't be erased and rewritten. A sealed exam paper (a final method) can't be edited by anyone else. A finished, published book (a final class) can't have new chapters added by readers. But writing a friend's *address* in pen doesn't stop your friend from repainting the house: a final reference doesn't freeze the object.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

final
A modifier meaning 'cannot change after being set'. Applies to variables, fields, parameters, methods and classes.
Blank final
A final field that is declared without a value and must be assigned exactly once in every constructor.
Constant
A static final field whose value never changes, like static final int MAX_USERS = 100;.
Compile-time constant
A final primitive or String variable initialised with a value the compiler can work out, like 60 * 60. Its value is copied into the code that uses it.
Effectively final
A local variable that is never reassigned, so the compiler treats it as final even without the keyword.
Immutable
An object whose state can never change after construction. final fields help, but don't guarantee it alone.

Step by step

01final local variables and parameters

final int max = 10; and then max = 20; is a compile error. A final local can also be assigned later, once: final String label; if (vip) label = "VIP"; else label = "Regular";. The compiler's definite assignment analysis checks that every path assigns it exactly once.

void f(final int x) makes the parameter unassignable inside the method. It's a style choice; many teams rely on linters instead.

terminal
$ javac Main.java
── expected output ──
Main.java:1: error: cannot assign a value to final variable x
public class Main { public static void main(String[] a) { final int x = 1; x = 2; } }
^
1 error

02A final reference vs the object behind it

The variable holds a reference (an arrow). final nails the arrow in place; it says nothing about the box the arrow points to.

This is the most common misunderstanding about final in interviews.

A final reference vs the object behind itdiagram
Rendering diagram…

03Blank final fields

private final int id; without a value is a blank final. Every constructor must assign it exactly once; otherwise the compiler reports variable id might not have been initialized (or that it may already have been assigned). After construction it can never change.

Final fields are the building blocks of immutable objects (Topic 4.11) and of records, whose components are all private final fields.

04final methods and classes

A final method keeps its behaviour in every subclass. Object.getClass() is final, so no class can lie about its own type. Template methods are often final.

A final class can't be extended. String is final for security and correctness: if you could subclass it, a 'String' passed to a security check could change its value afterwards. A class with only private constructors is effectively un-extendable too (subclasses couldn't call super(...)), but declaring it final states the intent and lets the compiler reject casts that could never succeed (Topic 5.5).

terminal
$ javac Main.java
── expected output ──
Main.java:2: error: m() in B cannot override m() in A
class B extends A { void m() { } }
^
overridden method is final
1 error

05Constants and inlining

static final int MAX_USERS = 100; is a compile-time constant. When another class uses Limits.MAX_USERS, javac copies the number 100 into that class's bytecode; at run time it never reads the field. Constants can also be used where Java demands a constant, such as case labels in a switch.

The catch: if you change MAX_USERS to 200 in a library and don't recompile the classes that use it, they still have 100 baked in. Values that may change between releases should not be compile-time constants (compute them, e.g. static final int MAX = Integer.parseInt("200");, or read them from configuration).

06Effectively final and capturing

When an anonymous class or lambda uses a local variable from the surrounding method, it gets a copy of the value. If the variable could change later, the copy and the original would disagree, so Java requires captured locals to be final or effectively final.

This is a rule about local variables only. Fields can be read and changed from lambdas freely (which is a separate source of concurrency bugs).

terminal
$ javac Main.java
── expected output ──
Main.java:1: error: local variables referenced from a lambda expression must be final or effectively final
public class Main { public static void main(String[] a) { int n = 0; n++; Runnable r = () -> System.out.println(n); } }
^
1 error

Try it yourself

  1. 1

    Forget a blank final

    In the Order example, comment out this.amount = amount; and compile. Read variable amount might not have been initialized. Put it back.

  2. 2

    Try to override receipt()

    Add @Override String receipt() { return "hacked"; } to DiscountOrder. Read the 'overridden method is final' error.

  3. 3

    Turn a constant into a non-constant

    Change static final int SMALL = 1, LARGE = 2; to static int SMALL = 1, LARGE = 2; (not final) and compile. case SMALL: now fails with constant expression required, because a non-final field could change.

Code & diagrams

A final reference to a mutable object New tab
Sign in to run this example in your browser.

Expected output

[Asha, Ravi]
[99, 2, 3]
frozen list refused add()
label = VIP
Blank finals, constants and a final template New tab
Sign in to run this example in your browser.

Expected output

Order 1: 100.00 + tax = 118.00
Order 2: 100.00 + tax = 106.20
large
Which JDK classes are final? New tab

Records are implicitly final. ArrayList is deliberately extendable.

Sign in to run this example in your browser.

Expected output

String final? true
Integer final? true
Math final? true
Point final? true
ArrayList final? false
Object final? false
Object.getClass() final? true

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Extend a final class

Declare final class Animal and then class Dog extends Animal.

Main.javawhole filejava
final class Animal { }
class Dog extends Animal { }
terminal
$ javac Main.java
── what you'll see ──
Main.java:2: error: cannot inherit from final Animal
class Dog extends Animal { }
^
1 error

Break #2

Leave a blank final unassigned

Declare final String name; and a constructor that doesn't set it.

Main.javawhole filejava
class User { final String name; User() { } }
terminal
$ javac Main.java
── what you'll see ──
Main.java:1: error: variable name might not have been initialized
class User { final String name; User() { } }
^
1 error

Break #3

Reassign a final parameter

Mark a parameter final and assign to it.

Main.javawhole filejava
public class Main { static void f(final int x) { x = 3; } public static void main(String[] a) { } }
terminal
$ javac Main.java
── what you'll see ──
Main.java:1: error: final parameter x may not be assigned
public class Main { static void f(final int x) { x = 3; } public static void main(String[] a) { } }
^
1 error

Myth vs fact

Myth

A final variable holding an object makes the object immutable.

Fact

It only fixes which object the variable refers to. The object's fields can still change unless the class itself is immutable.

Myth

Making methods final makes them faster.

Fact

Modern JITs devirtualise and inline non-final methods using class hierarchy analysis. final is for design and safety, not speed.

Myth

final, finally and finalize are related.

Fact

They're unrelated. finally is the always-runs block of try (Topic 7.2). finalize() is a deprecated Object method for GC clean-up (deprecated for removal since Java 18). final is this modifier.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Final fields have a special guarantee in the Java Memory Model (JLS 17.5): once a constructor finishes, any thread that sees the object reference also sees the correctly initialised values of its final fields, without synchronisation, as long as this didn't escape during construction. That's why immutable objects with final fields are thread-safe to share (Topic 13.4).

  • ▸

    javac inlines compile-time constants, but the JIT treats ordinary final instance fields as potentially changeable because reflection (Field.setAccessible(true) + set) and deserialisation can modify them. static final fields are trusted as true constants by the JIT; instance final fields are trusted only in special cases (records, hidden classes, and some JDK internals). JEP 500 ('Prepare to Make Final Mean Final') starts warning about reflective mutation of final fields so that, eventually, the JIT can trust them everywhere.

  • ▸

    final on a class lets the JIT bind calls statically without needing CHA dependencies, and lets the compiler reject impossible casts. sealed (Topic 5.11) gives a middle ground: a fixed, known set of subclasses.

Remember this

  1. 1

    A final variable can be assigned exactly once. For a local variable or parameter, any second assignment is a compile error. For a final field, the compiler checks that every constructor assigns it exactly once (a blank final) or that it's assigned in its declaration.

  2. 2

    final reference ≠ immutable object. final List<String> names = new ArrayList<>(); stops you from writing names = otherList, but names.add("x") works fine. To freeze the contents, use an unmodifiable list (List.of, List.copyOf) and build immutable classes (Topic 4.11).

  3. 3

    A final method can't be overridden by subclasses. Use it to protect behaviour that other methods rely on, like a template method (Topic 5.6) or a security check.

  4. 4

    A final class can't be extended at all. String, Integer and the other wrappers, Math, and every record are final. This guarantees no subclass can change their behaviour, which is essential for immutability and security.

  5. 5

    static final fields with a constant value are constants, named in UPPER_SNAKE_CASE. If the value is a compile-time constant (primitive or String literal), javac inlines it into every class that uses it.

  6. 6

    Effectively final (Java 8): a local variable that is never reassigned after initialisation counts as final even without the keyword. Lambdas and anonymous/local classes may only capture final or effectively final locals (Topic 5.13, Phase 10).

Explain it without notes

01

What does final mean on a variable, a method and a class?

02

Does final List<String> list make the list immutable? Explain.

03

What is a blank final, and what does the compiler check?

04

What is 'effectively final' and where does it matter?

Practice

01

Write an immutable Money class: final class, private final amount (long paise) and currency, no setters, a plus(Money) that returns a new object. Show the original is unchanged.

02

Show with code that a final StringBuilder can still be appended to, but not reassigned.

03

Write a class with a blank final id that has two constructors, both assigning it, and print both objects.

Trade-offs

  • ↔

    Final classes are safer and easier to reason about, but they block legitimate extension and some mocking libraries (Mockito can mock final classes with its inline mock maker, which is the default since Mockito 5).

  • ↔

    Marking every local final documents intent but adds noise; most teams mark fields final and let 'effectively final' cover locals.

  • ↔

    Public compile-time constants get inlined into callers; changing them requires recompiling every user. Expose values that may change through methods instead.

Done when you can

  • Done when you can explain final on variables, methods and classes.

  • Done when you can show that a final reference doesn't make its object immutable.

  • Done when you can write blank final fields and satisfy the compiler in every constructor.

  • Done when you can explain constant inlining and its versioning pitfall.

  • Done when you know what 'effectively final' means and why lambdas need it.