Introduction
final keyword prevents inheritance and method overriding. It creates unchangeable classes and methods.
final with Classes
final class cannot be inherited.
Syntax:
final class ClassName {
// Class members
}
Example:
final class FinalClass {
void display() {
System.out.println("This is final class");
}
}
// class SubClass extends FinalClass { } // ✗ Error: Cannot inherit
public class Main {
public static void main(String[] args) {
FinalClass obj = new FinalClass();
obj.display();
}
}
final with Methods
final method cannot be overridden.
class Parent {
final void display() {
System.out.println("Parent display - cannot be overridden");
}
void show() {
System.out.println("Parent show - can be overridden");
}
}
class Child extends Parent {
// @Override
// void display() { } // ✗ Error: Cannot override final method
@Override
void show() { // ✓ OK: Not final
System.out.println("Child show");
}
}
public class Main {
public static void main(String[] args) {
Child c = new Child();
c.display(); // Parent's version
c.show(); // Child's version
}
}
Why Use final?
1. Security:
final class SecurityManager {
private String password;
final void authenticate() {
// Critical security logic
// Cannot be changed by subclass
}
}
2. Prevent Modification:
class Parent {
final void calculate() {
// Important calculation
// Should not be changed
}
}
3. Performance:
// final methods can be inlined by compiler
final void fastMethod() {
// Optimized execution
}
Real-World Examples
String Class:
// String is final class
public final class String {
// Cannot be inherited
}
// class MyString extends String { } // ✗ Error
Wrapper Classes:
public final class Integer { }
public final class Double { }
public final class Boolean { }
// All wrapper classes are final
Math Class:
public final class Math {
// Utility class, should not be inherited
}
Complete Example
class BankAccount {
protected double balance;
// final method - secure calculation
final double calculateInterest(double rate) {
return balance * rate / 100;
}
// Can be overridden
void deposit(double amount) {
balance += amount;
}
}
class SavingsAccount extends BankAccount {
// @Override
// double calculateInterest(double rate) { } // ✗ Error
@Override
void deposit(double amount) { // ✓ OK
// Add minimum balance check
if (balance + amount >= 1000) {
super.deposit(amount);
} else {
System.out.println("Minimum balance required");
}
}
}
public class Main {
public static void main(String[] args) {
SavingsAccount acc = new SavingsAccount();
acc.balance = 5000;
System.out.println("Interest: " + acc.calculateInterest(5));
acc.deposit(1000);
}
}
final with Variables
Constants:
class Constants {
final int MAX_SIZE = 100; // Instance constant
static final double PI = 3.14159; // Class constant
void display() {
// MAX_SIZE = 200; // ✗ Error: Cannot change
System.out.println("Max size: " + MAX_SIZE);
}
}
Final Parameters:
class Example {
void method(final int x) {
// x = 20; // ✗ Error: Cannot change final parameter
System.out.println("x = " + x);
}
}
Immutable Class Pattern
final class ImmutablePerson {
private final String name;
private final int age;
ImmutablePerson(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
// No setters - immutable
}
public class Main {
public static void main(String[] args) {
ImmutablePerson person = new ImmutablePerson("John", 25);
System.out.println(person.getName());
System.out.println(person.getAge());
// Cannot change values
}
}
final Method in Inheritance Chain
class GrandParent {
final void method() {
System.out.println("GrandParent - final");
}
}
class Parent extends GrandParent {
// Cannot override
// void method() { } // ✗ Error
}
class Child extends Parent {
// Still cannot override (inherited as final)
// void method() { } // ✗ Error
}
public class Main {
public static void main(String[] args) {
Child c = new Child();
c.method(); // Calls GrandParent's method
}
}
Comparison Table
| Item | Without final | With final |
|---|---|---|
| Class | Can be inherited | Cannot be inherited |
| Method | Can be overridden | Cannot be overridden |
| Variable | Can be changed | Cannot be changed |
| Performance | Normal | Slightly better |
| Security | Lower | Higher |
When to Use final
Use final Class:
- Utility classes (Math, Arrays)
- Security-sensitive classes
- Immutable classes (String)
- Complete implementation (no extension needed)
Use final Method:
- Critical algorithms (security, calculations)
- Template methods (fixed behavior)
- Callback methods (shouldn’t change)
Use final Variable:
- Constants
- Configuration values
- Prevent accidental changes
final vs abstract
// ✗ Cannot be both final and abstract
// final abstract class Wrong { } // Error
// ✗ Cannot have final abstract method
abstract class Wrong2 {
// final abstract void method(); // Error
}
Reason:
abstractrequires inheritance/overridingfinalprevents inheritance/overriding- Contradictory!
Singleton Pattern with final
final class Singleton {
private static Singleton instance;
private Singleton() {
// Private constructor
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public void showMessage() {
System.out.println("Singleton instance");
}
}
public class Main {
public static void main(String[] args) {
Singleton obj = Singleton.getInstance();
obj.showMessage();
}
}
Design Considerations
Good Use:
// Utility class - final is good
final class StringUtils {
static String reverse(String str) {
return new StringBuilder(str).reverse().toString();
}
}
Bad Use:
// Making everything final - bad practice
final class BadDesign {
final void method1() { }
final void method2() { }
// Reduces flexibility
}
Real Example: Configuration
final class Configuration {
private final String dbUrl;
private final String dbUser;
private final int maxConnections;
Configuration(String url, String user, int max) {
this.dbUrl = url;
this.dbUser = user;
this.maxConnections = max;
}
// Only getters, no setters
public String getDbUrl() {
return dbUrl;
}
public String getDbUser() {
return dbUser;
}
public int getMaxConnections() {
return maxConnections;
}
}
public class Main {
public static void main(String[] args) {
Configuration config = new Configuration(
"jdbc:mysql://localhost:3306/db",
"admin",
10
);
System.out.println("DB URL: " + config.getDbUrl());
System.out.println("Max Connections: " + config.getMaxConnections());
}
}
Quick Reference
// final class
final class FinalClass { }
// Cannot inherit: class Sub extends FinalClass { }
// final method
class Parent {
final void method() { }
}
class Child extends Parent {
// Cannot override: void method() { }
}
// final variable
final int x = 10;
// Cannot change: x = 20;
// final parameter
void method(final int x) {
// Cannot change: x = 20;
}
Exam Tips
Remember:
- final class cannot be inherited
- final method cannot be overridden
- final variable cannot be changed
- Used for security and immutability
- String is final class
- Cannot be abstract and final together
- Performance benefit (slight)
- Wrapper classes are final
- Use for constants and utility classes
- Part of immutable design
Common Questions:
- What is final keyword?
- final class vs final method?
- Why is String final?
- Can we override final method?
- Can we inherit final class?
- final vs abstract?
- When to use final?
- Examples of final classes?