Java final Keyword
Make Java variables constant, prevent method overriding, and forbid class inheritance with the final keyword.
final means "set this once, then don't change it." It applies to four things, and the effect depends on which one:
- On a variable, the binding cannot be reassigned.
- On a method, the method cannot be overridden by a subclass.
- On a class, the class cannot be extended.
- On a parameter, the parameter cannot be reassigned inside the method.
The common thread is "no further modification after the initial binding." What that exactly forbids depends on what's being marked.
Final local variables
The simplest form: a final local variable cannot be reassigned after its first assignment.
void demo() {
final int max = 100;
max = 50; // ERROR: cannot assign a value to final variable max
}You don't have to initialize it at the declaration — you can defer the first assignment, as long as you assign exactly once before any read:
final int x;
if (condition) x = 1;
else x = 2;
System.out.println(x); // ok — x was definitely assignedfinal on a local is mostly a documentation tool — it tells a future reader (and the compiler) "this won't change after this point." Lambda and inner-class captures require it: any local variable used inside one must be final or effectively final (never reassigned).
Final parameters
A final parameter cannot be reassigned inside the method body:
void greet(final String name) {
name = name.toUpperCase(); // ERROR
}Some teams require final on every parameter; others find it visual noise. Either is fine — what matters is consistency within a codebase. The mutating-parameter habit it discourages is a real source of bugs.
Final fields
A final field is assigned exactly once — either at declaration, in an instance initializer, or in the constructor — and then never again:
public class Point {
private final int x, y;
public Point(int x, int y) {
this.x = x; // assigned once
this.y = y;
}
// no setX or setY — there's no way to change the values
}This is the core of an immutable class. Every field is final; nothing can mutate the object after construction.
The compiler checks that every final field is assigned exactly once on every constructor path. Miss a path, get a compile error. Assign twice, get a compile error.
static final — constants
The standard form for a constant is public static final plus an UPPER_SNAKE name:
public static final double PI = 3.141592653589793;
public static final int MAX_RETRY = 3;Static, because there's no reason to keep one copy per instance; final, because it's a constant. For primitive and String constants, the compiler inlines the value at every call site.
Final and references
A common misunderstanding: final freezes the reference, not the object it points to. With a final array or list, you can still mutate the contents:
final int[] nums = {1, 2, 3};
nums[0] = 99; // ok — mutating the array, not reassigning the reference
nums = new int[5]; // ERROR — reassigning the reference
final List<String> names = new ArrayList<>();
names.add("Rex"); // ok — mutates the list
names = List.of(); // ERRORIf you want a list whose contents are also frozen, wrap it with List.copyOf(...) or Collections.unmodifiableList(...).
Final methods
final on a method means no subclass may override it:
public class Shape {
public final String describe() { // can never be overridden
return getClass().getSimpleName() + " area=" + area();
}
public double area() { return 0; }
}
public class Circle extends Shape {
public String describe() { return "circle"; } // ERROR
}You'd mark a method final when overriding it would break invariants the class relies on. It's an opinionated design choice — most methods aren't final in real codebases — but for templates that mustn't change, it's the right tool.
Final classes
final on a class means no class may extend it:
public final class Money { ... }
public class CryptoMoney extends Money { } // ERRORUse this when extension would defeat the design. The standard library does it routinely: String, Integer, Long, Double, and the rest of the primitive wrappers are all final — extending them would let you sneak mutable behavior into a type the language treats as immutable.
Final isn't immutability
A final field alone doesn't make an object immutable; it just prevents the reference from changing. Real immutability requires:
- Every field
final. - The class itself
final, or constructed with care to prevent subclasses from adding mutable state. - No method that exposes a mutable internal object (defensive copies on the way out).
- No method that lets a caller mutate state through it.
Records (covered in records) bundle most of this automatically.
A worked example
What's next
private fields and final immutability are the building blocks of the next idea: hiding state behind a class's methods entirely. The encapsulation chapter pulls those threads together.
Practice
A field declared final int[] xs = {1, 2, 3}; — which assignment is rejected by the compiler?