The <code>final</code> Keyword in Java

final means "assign exactly once" or "cannot be changed" depending on what it modifies. A final variable can't be reassigned. A final method can't be overridden. A final class can't be subclassed.

Final variables

final int MAX = 100;
MAX = 200;                         // ❌ compile error

final User u = new User("Alice");
u.setAge(30);                       // βœ… the REFERENCE is final, the OBJECT isn't
u = new User("Bob");                // ❌ can't reassign

Crucial distinction: final prevents reassignment of the variable. It does not make the referenced object immutable.

Final fields

public class Order {
    private final long id;            // assigned once β€” in the constructor
    private final Instant createdAt;

    public Order(long id) {
        this.id = id;
        this.createdAt = Instant.now();
    }
}

Always prefer final fields. They make the class thread-safe by accident β€” a properly constructed object with only final fields is safely visible to every thread without synchronisation.

Final methods β€” can't be overridden

public class Shape {
    public final String id() { return ... }   // subclasses must not redefine
    public String describe() { ... }           // can be overridden
}

Final classes β€” can't be subclassed

public final class String { ... }           // JDK β€” designed to be immutable
public final class Money { ... }             // value type β€” no subclasses allowed

Effectively final β€” for lambdas and inner classes

String prefix = "hello ";
list.forEach(s -> System.out.println(prefix + s));   // βœ… prefix never reassigned

String prefix = "hello ";
prefix = "hi ";                                       // ❌ now referenced in lambda below:
list.forEach(s -> System.out.println(prefix + s));   // compile error

A variable captured by a lambda or inner class must be effectively final β€” unchanged after initialisation, even without the keyword.

Records and final

Records are implicitly final and their components are implicitly final fields. You can't make them non-final.

Common mistakes

  • Confusing final with immutable β€” final List<X> doesn't prevent list.add(...). Use List.copyOf for an immutable list.
  • Not using final on fields β€” every field that doesn't need to change should be final. Make immutability the default.
  • Using final on method parameters reflexively β€” opinions vary; it reduces accidental reassignment but adds noise. Some teams enable it by convention.

Related

Pillar: Java keywords. See also final variables, records, static.