Q12 of 40 · Core Java
What is the purpose of the `final` keyword in Java?
Short answer
Short answer: `final` means different things depending on where it's applied: a final variable cannot be reassigned; a final method cannot be overridden; a final class cannot be subclassed. It communicates intent ('this is fixed'), enables JVM optimisations, and is required to make effective immutable objects safe for sharing.
Detail
Final variable: the binding cannot be reassigned after initialisation. For primitives this means the value is constant. For references, it means the reference can't point to a different object — but the object itself can still be mutated. final List<String> tags = new ArrayList<>() allows tags.add("x") but not tags = new ArrayList<>().
This is important in lambda and anonymous class contexts: local variables captured by a lambda must be effectively final — either declared final or never reassigned after their point of use. The compiler enforces this.
Final method: cannot be overridden in a subclass. Use it to protect invariants — if login() must always log audit events before delegating to doLogin(), mark login() final so subclasses can only override doLogin().
Final class: cannot be extended. String, Integer, and all other wrapper classes are final. For test automation, value objects in test data builders are good candidates for final — there's rarely a reason to subclass TestUser or ApiRequest.
Immutability: combining final fields with no mutating methods and defensive copies produces a truly immutable object that is inherently thread-safe. Java Records (Java 16+) bake this in: all component fields are implicitly private final.
JVM optimisation: the JIT compiler can inline final method calls because it knows no dynamic dispatch is needed. This is a micro-optimisation in most code, but it's part of why String is fast.
// EXAMPLE
FinalKeyword.java
// final variable — binding cannot change
final int MAX_RETRIES = 3;
// MAX_RETRIES = 5; // ❌ compile error
// final reference — reference fixed, object mutable
final List<String> tags = new ArrayList<>();
tags.add("smoke"); // ✅ mutates the object
// tags = new ArrayList<>(); // ❌ reassignment not allowed
// Effectively final — required for lambda capture
String baseUrl = "https://api.example.com";
Runnable r = () -> System.out.println(baseUrl); // ✅ never reassigned
// baseUrl = "http://..."; // would break the lambda capture
// final class — immutable value object (Java 17+ record is cleaner)
final class TestCredential {
private final String username;
private final String password;
TestCredential(String username, String password) {
this.username = username;
this.password = password;
}
// No setters — immutable by design
}
// Java 16+ record: implicitly final, all fields final
record TestCredential(String username, String password) {}