Preventing Inheritance with Final

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

ItemWithout finalWith final
ClassCan be inheritedCannot be inherited
MethodCan be overriddenCannot be overridden
VariableCan be changedCannot be changed
PerformanceNormalSlightly better
SecurityLowerHigher

When to Use final

Use final Class:

  1. Utility classes (Math, Arrays)
  2. Security-sensitive classes
  3. Immutable classes (String)
  4. Complete implementation (no extension needed)

Use final Method:

  1. Critical algorithms (security, calculations)
  2. Template methods (fixed behavior)
  3. Callback methods (shouldn’t change)

Use final Variable:

  1. Constants
  2. Configuration values
  3. 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:

  • abstract requires inheritance/overriding
  • final prevents 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:

  1. final class cannot be inherited
  2. final method cannot be overridden
  3. final variable cannot be changed
  4. Used for security and immutability
  5. String is final class
  6. Cannot be abstract and final together
  7. Performance benefit (slight)
  8. Wrapper classes are final
  9. Use for constants and utility classes
  10. 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?