The final keyword in Java is a small word with big impact. It lets you lock down values, methods, and even entire classes so they cannot be changed in ways you don’t want. In this tutorial for students and beginners, we’ll explore the final keyword in Java with examples. You will learn what final does for variables, methods, and classes, why it’s useful, how to use it, and common mistakes to avoid. If you’re just starting your Java journey, also check our main Java tutorials for more basics.
What is final in Java?
In Java, final prevents modification:
- final variable in Java: can be assigned only once.
- final method in Java: cannot be overridden (or hidden if static).
- final class in Java: cannot be subclassed.
These three uses help you express intent (single assignment), protect code from accidental changes (prevent overriding), and design reliable APIs (no subclassing where it doesn’t make sense). Let’s go through each with simple examples and best practices.
- Variable: single assignment only.
final int LIMIT = 100; - Method: cannot be overridden.
public final void validate() { ... } - Class: cannot be subclassed.
public final class Token { ... }
final variable in Java
Syntax
final dataType variableName = value; // assign exactly oncefinal keyword in Java with simple examples
class="cd-keyword cd-access">public class FinalVariableBasics {
class="cd-keyword cd-access">public static void main(String[] args) {
final int maxStudents = 30;
System.out.println("Max = " + maxStudents);
// maxStudents = 31; // Compile-time error: cannot assign a value to final variable
}
}Max = 30Once you assign a final variable, you cannot reassign it.
final on object references (important!)
When a reference variable is final, the reference cannot change, but the object it points to can still change (if it’s mutable):
class="cd-package">import java.util.ArrayList;
class="cd-package">import java.util.List;
class="cd-keyword cd-access">public class FinalReferenceDemo {
class="cd-keyword cd-access">public static void main(String[] args) {
final List<String> ids = new ArrayList<>();
ids.add("A"); // OK: modifying the object
ids.add("B"); // OK
System.out.println(ids);
// ids = new ArrayList<>(); // Compile-time error: cannot reassign final reference
}
}[A, B]Blank final fields (assign later, once)
A blank final is declared without an initializer and must be assigned exactly once before use—commonly in a constructor. This is how you create immutable fields for each object instance.
class="cd-keyword cd-access">public class Student {
class="cd-keyword cd-access">private final String rollNo; // blank final
class="cd-keyword cd-access">private final String name; // blank final
class="cd-keyword cd-access">public Student(String rollNo, String name) {
this.rollNo = rollNo; // assigned once here
this.name = name; // assigned once here
}
class="cd-annotation">@Override
class="cd-keyword cd-access">public String toString() {
return rollNo + ": " + name;
}
class="cd-keyword cd-access">public static void main(String[] args) {
Student s = new Student("R001", "Asha");
System.out.println(s);
}
}R001: AshaConstants with static final
Use static final for constants (especially primitives and Strings). By convention, constant names are uppercase with underscores:
class="cd-keyword cd-access">public class MathUtil {
class="cd-keyword cd-access">public static final double PI = 3.141592653589793;
class="cd-keyword cd-access">public static void main(String[] args) {
System.out.println(2 * PI);
}
}6.283185307179586- If a static final primitive or String is initialized with a compile-time constant, compilers may inline it. Prefer private static final constants inside classes to avoid accidental coupling.
- All interface fields are implicitly public static final.
Effectively final and lambdas (beginner tip)
Local variables used inside lambdas or inner classes must be final or effectively final (assigned once). You don’t need the keyword if you never reassign the variable.
class="cd-keyword cd-access">public class LambdaFinalDemo {
class="cd-keyword cd-access">public static void main(String[] args) {
int base = 10; // effectively final (assigned once)
Runnable r = () -> System.out.println("Base = " + base);
r.run();
// base++; // Uncommenting this makes ___CDPHSTR1___ not effectively final -> compile-time error
}
}Base = 10From Java 10 onward, you can combine final with var for single-assignment with type inference:
class="cd-keyword cd-access">public class FinalVarDemo {
class="cd-keyword cd-access">public static void main(String[] args) {
final var msg = "Hello"; // type inferred as String
System.out.println(msg);
// msg = ___CDPHSTR1___; // error: cannot assign a value to final variable
}
}Hellofinal method in Java
A final method cannot be overridden in subclasses. This is useful when you want to lock down behavior that must not change, e.g., validation logic, security checks, or lifecycle hooks.
Why a final method cannot be overridden (example)
class Base {
class="cd-keyword cd-access">public final void greet() {
System.out.println("Hello from Base");
}
}
class Child extends Base {
// @Override
// public void greet() { } // Compile-time error: cannot override final method
class="cd-keyword cd-access">public void wave() {
System.out.println("Child waves");
}
}
class="cd-keyword cd-access">public class FinalMethodDemo {
class="cd-keyword cd-access">public static void main(String[] args) {
Child c = new Child();
c.greet(); // calls Base.greet()
c.wave();
}
}Hello from Base
Child wavesStatic final methods and method hiding
static methods can also be final. A subclass cannot hide a final static method:
class A {
class="cd-keyword cd-access">public static final void ping() {
System.out.println("A.ping");
}
}
class B extends A {
// public static void ping() {} // Compile-time error: cannot hide final method
}
class="cd-keyword cd-access">public class StaticFinalMethodDemo {
class="cd-keyword cd-access">public static void main(String[] args) {
B.ping(); // resolves to A.ping()
}
}A.pingfinal class in Java
A final class cannot be subclassed. Use it when you want to prevent inheritance—for example, to keep invariants safe or to avoid misuse of an API. java.lang.String is a famous final class.
How to use a final class in Java (real-world idea)
- Utility or helper classes whose behavior shouldn’t be extended.
- Security-sensitive types to prevent tampering via subclassing.
- Immutable value types where subclassing could break invariants.
final class ApiClient {
class="cd-keyword cd-access">public void request() {
System.out.println("Requesting...");
}
}
// class MockClient extends ApiClient {} // Compile-time error: cannot subclass final class
class="cd-keyword cd-access">public class FinalClassDemo {
class="cd-keyword cd-access">public static void main(String[] args) {
ApiClient client = new ApiClient();
client.request();
}
}Requesting...Immutability and concurrency: final fields matter
final fields help you build immutable objects and improve thread-safety. The Java Memory Model gives special guarantees to final fields: after the constructor finishes, properly published objects make their final fields safely visible to other threads. This is one reason immutable classes use private final fields and no setters.
Note: final alone does not make an object immutable. You must design for immutability (final fields, no setters, avoid exposing mutable internals).
class="cd-keyword cd-access">public final class Point {
class="cd-keyword cd-access">private final int x;
class="cd-keyword cd-access">private final int y;
class="cd-keyword cd-access">public Point(int x, int y) {
// assign all final fields in the constructor
this.x = x;
this.y = y;
}
class="cd-keyword cd-access">public int x() { return x; }
class="cd-keyword cd-access">public int y() { return y; }
class="cd-annotation">@Override
class="cd-keyword cd-access">public String toString() {
return "(" + x + "," + y + ")";
}
class="cd-keyword cd-access">public static void main(String[] args) {
Point p = new Point(2, 3);
System.out.println(p);
// p.x = 5; // error: x has private access, and it's final
}
}(2,3)Records are implicitly final
Java records are concise data carriers. Every record class is implicitly final, and each component becomes a private final field with an accessor. This makes records great for immutable data.
class="cd-keyword cd-access">public record Person(String name, int age) {
class="cd-keyword cd-access">public static void main(String[] args) {
Person p = new Person("Ria", 20);
System.out.println(p.name() + " " + p.age());
// p.age = 21; // error: fields are final
}
}Ria 20Common mistakes and quick tips
- Thinking final makes objects immutable: It doesn’t by itself. A final reference can still point to a mutable object. Design for immutability using private final fields and no mutators.
- Forgetting to initialize blank finals: Every code path in a constructor must assign all blank final fields exactly once.
- Expecting performance boosts: final is a semantic constraint, not a performance promise. Don’t use it for micro-optimizations.
- Reassigning effectively final locals used in lambdas: If you change a local variable after using it in a lambda, compilation fails.
- Changing public static final constants later: Compilers may inline constant values. Keep constants private where possible to avoid compatibility issues.
- Overusing inheritance: If a class wasn’t designed for extension, make it final to avoid fragile hierarchies.
- final with var: You can write final var x = … for single-assignment with type inference (Java 10+).
FAQ: People Also Ask
What does the final keyword do in Java?
It prevents change at three levels: a final variable can be assigned only once, a final method cannot be overridden (or hidden if static), and a final class cannot be subclassed. Use it to express invariants and protect APIs.
How do I create a final variable in Java?
Declare with the final keyword and assign exactly once:
final int limit = 100; // assigned at declaration
// or
final String id; // blank final
id = "U101"; // assigned later, exactly once (before use)Can a final method be overridden in Java?
No. A final method cannot be overridden in a subclass. Attempting to do so is a compile-time error. For static methods, final prevents method hiding as well.
When should I use a final class in Java?
Use a final class when subclassing would break invariants or complicate maintenance, such as:
- Immutable value objects (e.g., Money, Point).
- Security-sensitive or correctness-critical types.
- Utilities or API endpoints where behavior must not vary.
What is the difference between final, finally, and finalize in Java?
| Term | Where it applies | Prevents / Guarantees | Quick example | Notes |
|---|---|---|---|---|
| final (keyword) | Variables, methods, classes | Reassignment, overriding, subclassing | final int N=5;final void f(){}final class C{} |
Core to immutability and API safety |
| finally (block) | try-catch-finally | Cleanup code always runs | try{...} finally {close();} |
Use try-with-resources when possible |
| finalize (method) | Object cleanup hook | Legacy GC callback (deprecated) | protected void finalize(){} |
Deprecated; avoid using |
- final: a keyword for variables, methods, and classes (prevents change).
- finally: a block in try-catch-finally that always executes (resource cleanup).
- finalize(): a method once used for cleanup by the GC; it’s deprecated and should be avoided. Use try-with-resources or explicit close methods instead.
When to use final in your code
private final String id;
public final void checkAccess() { ... }
public final class Credentials { ... }
- Model constants: private static final int MAX_RETRIES = 3;
- Immutable domain objects: private final fields set in the constructor.
- Method-level guarantees: final method to lock down critical behavior.
- API design: final class to prevent unsafe or unintended extension.
- Functional style: final/effectively final locals used in lambdas.
Practice and next steps
Try converting a mutable class you wrote into an immutable one by using private final fields and no setters. Add one final method that validates state and cannot be overridden. If you’re exploring more Java basics and OOP topics, head back to our Java section on CodDesire.
Sources / Further reading
- JLS §4.12.4 — final Variables
- JLS §8.4.3.3 — final Methods
- JLS §8.1.1.2 — final Classes
- JLS §8.10 — Record Classes and §8.10.3 — Record Components
- JLS §17.5 — final Field Semantics
- JLS §13.4.9 — final Fields and Binary Compatibility
- JEP 286 — Local-Variable Type Inference (var)
- java.lang.String (final class)


