一、final概述
final
是Java中的一个修饰符,可以用来修饰类
、方法
和变量
。它的核心含义是"不可改变的"
- final变量:一旦初始化就
不能重新赋值
- final方法:
不能被子类重写
- final类:
不能被继承
- final变量:一旦初始化就
二、final修饰变量
1、final基本类型变量
- 当final修饰基本类型变量时,该变量的值一旦被初始化后就不能再改变
java
final int MAX_VALUE = 100;
// MAX_VALUE = 200; // 编译错误,不能修改final变量的值
特点:
- 必须在声明时或构造方法/静态块中初始化
- 一旦赋值后不能再修改
- 对于基本类型,值本身不可变
2、final引用类型变量
- 当final修饰引用类型变量时,
引用本身不可变
(即不能指向其他对象),但对象的内容可以改变
java
final List<String> names = new ArrayList<>();
names.add("Alice"); // 允许,修改对象内容
// names = new ArrayList<>(); // 编译错误,不能改变引用
特点:
- 引用不可变 ≠ 对象不可变
- final只保证引用不变,不保证被引用对象的内容不变
3、final变量的初始化时机
- 对于实例变量:可以在
声明时
、构造代码块
或构造方法
中初始化 - 对于静态变量:可以在
声明时
或静态初始化块
中初始化
java
public class Example {
// 实例变量
final int instanceVar1 = 10; // 声明时初始化
final int instanceVar2;
final int instanceVar3;
{
instanceVar2 = 20; // 实例初始化块(构造代码块)初始化
}
Example() {
instanceVar3 = 30; // 构造方法中初始化
}
// 静态变量
static final int STATIC_VAR1 = 10; // 声明时初始化
static final int STATIC_VAR2;
static {
STATIC_VAR2 = 20; // 静态初始化块中初始化
}
}
三、final修饰方法
- 当final修饰方法时,该方法不能被子类重写(override)
java
class Parent {
public final void forbiddenMethod() {
System.out.println("我不能被重写");
}
public void normalMethod() {
System.out.println("我可以被重写");
}
}
class Child extends Parent {
// @Override
// public void forbiddenMethod() { } // 编译错误:不能重写final方法
@Override
public void normalMethod() {
System.out.println("父类方法被重写了");
}
}
设计考虑:
- 防止子类修改关键方法:确保方法的行为不会被子类改变
- 常将框架中的关键方法声明为final
- 当不希望子类改变特定行为时使用
- JVM可以对final方法进行
内联优化
:直接将方法体插入调用处,减少方法调用的开销
四、final修饰类
- 当final修饰类时,该类不能被继承
java
final class StringUtils {
public static boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
}
// class ExtendedUtils extends StringUtils { } // 编译错误:不能继承final类
常见的final类:
- String
- Integer
- Double
- Math
- System
设计考虑:
不可变类
:如String类,确保对象状态不会被改变安全性
:防止通过继承破坏类的安全性或不变性设计意图
:明确表示该类不应有子类工具类
:如Math,只包含静态方法,不需要实例化或继承
五、final修饰参数
java
public void process(final int param1, final List<String> param2) {
// param1 = 10; // 编译错误
// param2 = new ArrayList<>(); // 编译错误
param2.add("value"); // 允许,修改对象内容
}
设计考虑:
- 防止方法内部意外修改参数引用
- 提高代码可读性,明确参数不应被重新赋值
六、final在并发编程中的应用
final
字段在并发编程中非常重要,因为JVM保证final
字段的初始化安全
java
class SafePublication {
final int safeValue;
int unsafeValue;
public SafePublication(int value) {
this.safeValue = value; // 正确初始化
this.unsafeValue = value; // 可能发生重排序
// 在构造函数返回后,safeValue对所有线程立即可见
}
public int getSafeValue() {
return safeValue; // 总是能看到正确初始化的值
}
public int getUnsafeValue() {
return unsafeValue; // 可能看到默认值0
}
}
Java内存模型对final字段有特殊规定:
- 在构造函数中对final字段的写入,与随后将被构造对象的引用赋值给一个引用变量,这两个操作
不能重排序
- 初次读包含final字段的对象的引用,与随后初次读这个final字段,这两个操作
不能重排序
这意味着:正确构造的对象的final字段,对于所有线程都是可见的,无需额外的同步措施