Java Tutorial
🔍

final Keyword in Java

The final keyword in Java restricts changes to variables, methods, and classes. Once something is marked final, it becomes unchangeable - a variable can't be reassigned, a method can't be overridden, and a class can't be extended.

Java
1final int MAX_USERS = 100; // Cannot change this value 2MAX_USERS = 200; // Compile error!

Think of final as Java's way of saying: "This value is locked. Nobody should accidentally change it later."

One of the most practical reasons developers use final is readability - when you see a final variable, you instantly know that value will never change throughout the code. This makes debugging and code reviews significantly easier.

Real-World Usage

Production systems use final for configuration that must never change:

Java
1final String DATABASE_URL = "prod-db.company.com"; 2final String API_KEY = "sk_live_abc123xyz";

Once the application loads these values, no method can accidentally modify them. This prevents entire categories of production bugs where URLs or API keys get corrupted during request processing.

What is the final Keyword?

The final keyword is a non-access modifier that prevents modification:

ContextEffect
VariableCannot be reassigned after initialization
MethodCannot be overridden by subclasses
ClassCannot be extended (inherited)

Java includes final to prevent accidental modifications, create constants, enforce design decisions, and enable compiler optimizations.

Final Variables

A final variable becomes constant after one assignment.

Basic Final Variable

Java
1public class Constants { 2 public static void main(String[] args) { 3 final int MAX_LOGIN_ATTEMPTS = 3; 4 5 System.out.println("Max attempts: " + MAX_LOGIN_ATTEMPTS); 6 7 // This would cause compile error: 8 // MAX_LOGIN_ATTEMPTS = 5; // Error! 9 } 10}

Output:

Max attempts: 3

Once initialized, any attempt to reassign triggers a compile-time error. Final constants typically use UPPER_CASE_WITH_UNDERSCORES naming.

Types of Final Variables

1. Regular Final Variable

Java
1final int SESSION_TIMEOUT = 1800; // 30 minutes in seconds

2. Blank Final Variable

Java
1final int userId; // Declared but not initialized 2userId = 12345; // Must be initialized before use

3. Static Final Variable (Class Constant)

Java
1public class Config { 2 static final String APP_VERSION = "2.1.0"; 3 static final int MAX_CONNECTIONS = 100; 4}

4. Static Blank Final Variable

Java
1public class Database { 2 static final String CONNECTION_URL; 3 4 static { 5 // Must initialize in static block 6 CONNECTION_URL = "jdbc:mysql://localhost:3306/app"; 7 } 8}

Final Reference Variables - Critical Understanding

This is where most beginners get confused. When you mark a reference variable as final, you're locking the reference (the memory address), not the object's state (its contents).

Java
1class ReferenceExample { 2 public static void main(String[] args) { 3 final StringBuilder message = new StringBuilder("Hello"); 4 5 System.out.println(message); 6 7 // This works - modifying object contents 8 message.append(" World"); 9 System.out.println(message); 10 11 // This fails - trying to change the reference 12 // message = new StringBuilder("New"); // Compile error! 13 } 14}

Output:

Hello Hello World

Think of it this way: final locks which box you're pointing to, but doesn't lock what's inside the box. You can still change the contents of a StringBuilder, ArrayList, or any mutable object. You just can't make the variable point to a different object.

final StringBuilder sb = new StringBuilder("Hello"); ↓ [Reference] ────→ [Object: "Hello"] LOCKED CAN CHANGE

Common Mistake: Thinking final makes objects immutable. It doesn't - it only prevents reassigning the reference.

Real Industry Example

Java
1public class PaymentProcessor { 2 private static final String GATEWAY_URL = "https://api.stripe.com"; 3 private static final double TRANSACTION_FEE = 0.029; // 2.9% 4 5 private final String transactionId; 6 private final double amount; 7 8 public PaymentProcessor(String transactionId, double amount) { 9 this.transactionId = transactionId; 10 this.amount = amount; 11 } 12 13 public void processPayment() { 14 System.out.println("Processing: " + transactionId); 15 System.out.println("Amount: $" + amount); 16 System.out.println("Fee: $" + (amount * TRANSACTION_FEE)); 17 } 18}

Constants prevent accidental URL changes in production. Transaction details remain immutable for audit trails. This design is thread-safe and makes bugs impossible to introduce through reassignment.

So far, final has protected values. Java can also use final to protect behavior itself - that's where final methods become useful.

Final Methods

A final method cannot be overridden by subclasses. This ensures the implementation stays consistent across inheritance hierarchies.

Java
1class SecurityModule { 2 public final boolean authenticate(String username, String password) { 3 return username.equals("admin") && password.equals("secret"); 4 } 5} 6 7class CustomSecurity extends SecurityModule { 8 // Compile error! Cannot override final method 9 // public boolean authenticate(String u, String p) { 10 // return true; // Would bypass security! 11 // } 12}

Use final methods to protect critical business logic, prevent security bypasses, and enforce API contracts.

Template Method Pattern

A common use case is locking down the overall algorithm while allowing customization of individual steps:

Java
1public abstract class DataProcessor { 2 // Template method - cannot be changed by subclasses 3 public final void processData() { 4 loadData(); 5 validateData(); 6 transformData(); 7 saveData(); 8 } 9 10 protected abstract void loadData(); 11 protected abstract void validateData(); 12 protected abstract void transformData(); 13 protected abstract void saveData(); 14}

The processing flow is guaranteed - subclasses can customize individual steps but can't break the overall algorithm structure.

Beyond protecting individual methods, Java also lets you protect entire classes from extension.

Final Classes

A final class cannot be extended. No other class can inherit from it.

Java
1public final class ImmutableUser { 2 private final String userId; 3 private final String email; 4 5 public ImmutableUser(String userId, String email) { 6 this.userId = userId; 7 this.email = email; 8 } 9 10 public String getUserId() { return userId; } 11 public String getEmail() { return email; } 12} 13 14// Compile error! Cannot extend final class 15// class AdminUser extends ImmutableUser { }

Java's standard library uses final classes extensively: String, Integer, Double, Math, and System are all final.

Why String is final (common interview question):

  • Security: Prevents malicious subclasses from changing behavior
  • Thread Safety: Immutable strings are thread-safe
  • String Pool: Allows efficient memory caching
  • Hash Codes: Once calculated, never changes (critical for HashMap keys)

Code Review Insight: Senior developers often mark constructor-initialized fields as final because it makes object state more predictable during debugging. When you see private final String userId, you know that value is set once and never changes.

Common Beginner Mistakes

Mistake 1: Confusing Reference Locking with Object Immutability

Remember: final locks the reference, not the object state (we covered this earlier with StringBuilder).

Java
1final List<String> items = new ArrayList<>(); 2items.add("Item 1"); // Works - modifying object 3items = new ArrayList<>(); // Error! Cannot reassign reference

Mistake 2: Forgetting to Initialize Blank Final Variables

Java
1class BankAccount { 2 final String accountNumber; 3 4 public BankAccount() { 5 // Compile error! Must initialize accountNumber 6 } 7}

Fix by initializing in the constructor:

Java
1public BankAccount(String accountNumber) { 2 this.accountNumber = accountNumber; 3}

Mistake 3: Trying to Override Final Methods

Java
1class Parent { 2 public final void criticalMethod() { } 3} 4 5class Child extends Parent { 6 public void criticalMethod() { } // Compile error! 7}

Best Practices

Use UPPER_CASE_WITH_UNDERSCORES for constants and normal camelCase for final instance variables:

Java
1public static final int MAX_CONNECTIONS = 100; 2private final String userId;

Mark method parameters final when you won't modify them - it prevents accidental reassignment bugs:

Java
1public void processOrder(final String orderId, final double amount) { 2 // Cannot accidentally reassign orderId or amount 3}

For truly immutable collections, use Collections.unmodifiableList():

Java
1private static final List<String> STATUSES = 2 Collections.unmodifiableList(Arrays.asList("PENDING", "APPROVED"));

This protects both the reference (via final) and the contents (via unmodifiable).

Mini Quiz

Test your understanding:

Question 1: What happens with this code?

Java
1final int x = 10; 2x = 20;

A) Works normally
B) Runtime error
C) Compile-time error
D) Warning only

Question 2: What about this code?

Java
1final List<String> list = new ArrayList<>(); 2list.add("Item");

A) Compile error
B) Runtime error
C) Works fine
D) List becomes immutable

Question 3: Can this code compile?

Java
1class A { 2 final void display() { } 3} 4class B extends A { 5 void display() { } 6}

A) Yes
B) No - compile error
C) Runtime error
D) Depends on JVM

Answers

1: C) Final variables cannot be reassigned - the compiler catches this immediately.

2: C) Works fine. Remember: final locks the reference, not the object. You can modify list contents, just can't reassign list to a different ArrayList.

3: B) Compile error. Final methods cannot be overridden in subclasses.

FAQs

Can a final variable be changed?

No, once initialized it cannot be reassigned. However, if it's a reference to a mutable object, the object's contents can still be modified.

What's the difference between final and const?

Java uses final for constants. Some languages have const, but Java doesn't. Use static final for class-level constants.

Can we override a final method?

No. This ensures the method's implementation remains unchanged across all subclasses.

Can we inherit a final class?

No. Final classes cannot be extended, which is used when a class's design should not be changed.

Why can't constructors be final?

Constructors are never inherited, so there's no need for the final keyword.

Is it mandatory to initialize a final variable?

Yes, exactly once - either at declaration, in a constructor, or in an initializer block.

Can a final method be private?

Yes, but it's redundant since private methods cannot be overridden anyway.

Performance benefits of final?

Modern JVMs are sophisticated - use final for correctness and clarity, not performance optimization.

final Keyword in Java | DevStackFlow