第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");
}
}
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变量可以延迟初始化。