第31章 Java final关键字

掌握final修饰类、方法和变量的特性与应用场景

final关键字概述

什么是final关键字?

final是Java中的一个重要关键字,用于限制继承、重写和重新赋值。它可以修饰类、方法和变量,确保被修饰的元素具有"最终"的特性,不能被改变。

final类

final修饰的类不能被继承,如String类、包装类等。确保类的设计不被破坏。

final方法

final修饰的方法不能被子类重写,但可以被继承。保护核心算法不被修改。

final变量

final修饰的变量只能赋值一次,成为常量。提高代码安全性和性能。

final修饰类

final修饰的类不能被继承,这是final关键字最重要的特性之一。Java中的String类就是一个典型的final类。

// final修饰的类不能被继承
final class FinalClass {
    private String name;
    
    public FinalClass(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public void displayInfo() {
        System.out.println("这是一个final类: " + name);
    }
}

// 以下代码会编译错误,因为不能继承final类
/*
class SubClass extends FinalClass {
    // 编译错误:Cannot inherit from final 'FinalClass'
}
*/

public class FinalClassExample {
    public static void main(String[] args) {
        // 可以正常创建final类的对象
        FinalClass finalObj = new FinalClass("Final类示例");
        finalObj.displayInfo();
        
        // 演示String类也是final类
        String str = "Hello World";
        System.out.println("String类是final类: " + str);
    }
}

final类的特点

  • 不能被继承:final类不能有子类
  • 可以正常使用:可以创建对象、调用方法
  • 常见例子:String、Integer、Double等包装类
  • 设计目的:保护类的完整性和安全性

final修饰方法

final修饰的方法不能被子类重写,但可以被继承和调用。这确保了方法的行为不会被改变。

class ParentClass {
    // 普通方法,可以被重写
    public void normalMethod() {
        System.out.println("父类的普通方法");
    }
    
    // final方法,不能被重写
    public final void finalMethod(String message) {
        System.out.println("父类的final方法: " + message);
        System.out.println("这个方法不能被子类重写");
    }
    
    // final方法示例:计算圆的面积
    public final double calculateCircleArea(double radius) {
        return Math.PI * radius * radius;
    }
}

class ChildClass extends ParentClass {
    // 重写父类的普通方法
    @Override
    public void normalMethod() {
        System.out.println("子类重写的普通方法");
    }
    
    // 以下代码会编译错误,因为不能重写final方法
    /*
    @Override
    public final void finalMethod(String message) {
        // 编译错误:Cannot override the final method
    }
    */
    
    public void childMethod() {
        System.out.println("子类特有的方法");
        // 可以调用父类的final方法
        finalMethod("从子类调用");
    }
}

final方法的应用场景

  • 核心算法:保护重要的计算逻辑不被修改
  • 安全方法:确保安全相关的方法行为不变
  • 模板方法:在模板方法模式中保护关键步骤
  • 性能优化:编译器可以对final方法进行内联优化

final修饰变量

final修饰的变量只能赋值一次,一旦初始化后就不能再修改。包括final实例变量、final局部变量和final静态变量。

public class FinalVariableExample {
    // final静态变量(类常量)
    public static final String COMPANY_NAME = "嘎嘎Plus科技有限公司";
    public static final int MAX_USERS = 1000;
    public static final double PI = 3.14159;
    
    // final实例变量(必须在构造函数中初始化)
    private final String name;
    private final int id;
    
    // final实例变量(声明时初始化)
    private final String version = "1.0.0";
    
    public FinalVariableExample(String name, int id) {
        this.name = name;  // final变量只能赋值一次
        this.id = id;      // final变量只能赋值一次
    }
    
    public void demonstrateFinalLocalVariables() {
        // final局部变量
        final int localConstant = 100;
        final String localMessage = "这是final局部变量";
        
        System.out.println("final局部常量: " + localConstant);
        System.out.println("final局部消息: " + localMessage);
        
        // final变量可以延迟初始化
        final int delayedInit;
        if (localConstant > 50) {
            delayedInit = 999;
        } else {
            delayedInit = 111;
        }
        System.out.println("延迟初始化的final变量: " + delayedInit);
    }
    
    public void demonstrateFinalReference() {
        // final引用变量
        final StringBuilder sb = new StringBuilder("Hello");
        
        // 可以修改对象的内容
        sb.append(" World");
        sb.append("!");
        System.out.println("StringBuilder内容: " + sb.toString());
        
        // 但不能重新赋值引用
        // sb = new StringBuilder("New"); // 编译错误
        
        // final数组示例
        final int[] numbers = {1, 2, 3, 4, 5};
        
        // 可以修改数组元素
        numbers[0] = 10;
        numbers[4] = 50;
        
        System.out.print("final数组内容: ");
        for (int num : numbers) {
            System.out.print(num + " ");
        }
        System.out.println();
    }
}
final变量类型 初始化时机 特点 应用场景
final静态变量 声明时或静态代码块 类级别常量,所有实例共享 配置常量、数学常数
final实例变量 声明时或构造函数 实例级别常量,每个对象独有 不可变对象的字段
final局部变量 使用前任意时机 方法内部常量 临时常量、lambda表达式
final方法参数 方法调用时 参数不可修改 防止意外修改参数

完整代码示例

以下是一个综合展示final关键字各种用法的完整示例:

// 演示final关键字的综合使用
public class FinalComprehensiveExample {
    // final静态常量
    public static final String APP_NAME = "Java学习平台";
    public static final double TAX_RATE = 0.08;
    
    // final实例变量
    private final String username;
    private final long createTime;
    
    public FinalComprehensiveExample(String username) {
        this.username = username;
        this.createTime = System.currentTimeMillis();
    }
    
    // final方法
    public final double calculateTotalPrice(double price) {
        return price * (1 + TAX_RATE);
    }
    
    // 演示final参数
    public void processData(final String data) {
        // data = "新数据"; // 编译错误
        System.out.println("处理数据: " + data);
        
        // final局部变量
        final String processedData = data.toUpperCase();
        System.out.println("处理后的数据: " + processedData);
    }
    
    public static void main(String[] args) {
        FinalComprehensiveExample example = 
            new FinalComprehensiveExample("张三");
        
        System.out.println("应用名称: " + APP_NAME);
        System.out.println("用户名: " + example.username);
        
        double price = 100.0;
        double total = example.calculateTotalPrice(price);
        System.out.printf("总价: %.2f\n", total);
        
        example.processData("hello world");
    }
}
💻 查看完整代码 - 在线IDE体验

final关键字最佳实践

✅ 推荐做法

使用final声明常量:用static final声明类级别常量,提高代码可读性。

✅ 不可变类设计

final类和字段:设计不可变类时,将类和所有字段都声明为final。

✅ 保护核心方法

final方法:对于核心算法和安全相关的方法使用final修饰。

✅ 方法参数final

防止意外修改:对重要的方法参数使用final,防止意外修改。

❌ 避免过度使用

适度原则:不要过度使用final,简单的getter方法通常不需要final。

❌ 设计过于严格

灵活性考虑:如果类可能需要扩展,不要轻易使用final修饰类。

性能和安全考虑

性能优化

编译器可以对final方法进行内联优化,final变量可以被优化为常量,提高运行效率。

线程安全

final字段天然线程安全,不需要额外的同步机制,简化并发编程。

代码安全

防止意外的继承、重写和修改,提高代码的安全性和可维护性。

常见问题解答

常见问题

Q: final变量和常量有什么区别?

A: final变量是语法概念,常量是语义概念。通常用static final组合声明常量。

Q: final方法可以被重载吗?

A: 可以。final只是防止重写(override),不影响重载(overload)。

Q: 为什么String类是final的?

A: 为了安全性、性能优化和字符串池的实现。

Q: final变量一定要在声明时初始化吗?

A: 不一定。实例final变量可以在构造函数中初始化,局部final变量可以延迟初始化。