final 关键字详解:特性与内存语义
一、基本特性
final
关键字用于声明不可变的实体,可修饰变量、方法和类,具体规则如下:
1. 修饰变量
-
基本类型:值不可变。
-
引用类型:引用不可变(不能指向其他对象),但对象内部状态可能可变。
final List<String> list = new ArrayList<>(); list.add("item"); // 允许修改对象内容 // list = new ArrayList<>(); // 编译错误:引用不可变
2. 修饰方法
-
不可重写:子类不能覆盖父类的
final
方法。class Parent { public final void method() {} } class Child extends Parent { // public void method() {} // 编译错误 }
3. 修饰类
-
不可继承:类不能被其他类继承。
final class FinalClass {} // class SubClass extends FinalClass {} // 编译错误
二、初始化规则
1. 实例变量
-
必须在声明时或构造函数中初始化。
class Example { final int a = 1; // 声明时初始化 final int b; Example(int b) { this.b = b; } // 构造函数中初始化 }
2. 静态变量(常量)
-
必须在声明时或静态初始化块中初始化。
class Constants { static final int MAX_VALUE = 100; // 声明时初始化 static final double PI; static { PI = 3.14; } // 静态初始化块中初始化 }
三、内存语义
1. 可见性保证
-
构造函数中初始化安全:正确构造的对象,其
final
域的值对其他线程可见,无需同步。class SafePublication { private final int value; public SafePublication(int value) { this.value = value; // 初始化完成后,value 对其他线程可见 } }
2. 禁止重排序
- 写操作屏障:JVM 在
final
域写入后插入StoreStore
屏障,确保构造函数返回前完成写入。 - 读操作屏障:读取
final
域时,JVM 插入LoadLoad
屏障,确保获取最新值。
3. 安全发布
- 条件:对象引用未在构造函数中逸出(
this
未泄露)。 - 效果:即使无同步,其他线程也能看到
final
域的正确初始值。
四、并发编程中的使用
1. 安全发布对象
-
无逸出构造:确保
this
不在构造函数中被其他线程访问。public class SafeFinalExample { private final int x; public SafeFinalExample(int x) { this.x = x; // 正确初始化,无逸出 } }
2. 不可变对象
-
所有域均为
final
:对象状态完全不可变,天然线程安全。public final class ImmutablePoint { private final int x; private final int y; public ImmutablePoint(int x, int y) { this.x = x; this.y = y; } }
3. 注意事项
-
引用类型内部状态:
final
不保证引用对象内部状态的线程安全,需额外同步。final List<String> list = Collections.synchronizedList(new ArrayList<>());
五、与 static
的结合
1. 静态常量
-
编译时常量:若
static final
基本类型/字符串在编译时可确定值,会内联优化。static final int MAX = 100; // 编译时常量
2. 延迟初始化
-
静态初始化块:复杂初始化逻辑。
static final Map<String, Integer> MAP; static { MAP = new HashMap<>(); MAP.put("key", 1); }
六、对比其他关键字
特性 | final | volatile | synchronized |
---|---|---|---|
作用对象 | 变量、方法、类 | 变量 | 代码块、方法 |
可见性 | 初始化安全(构造函数内) | 强制主内存读写 | 锁释放时刷新主内存 |
原子性 | 不提供(引用对象需同步) | 不提供 | 提供 |
有序性 | 禁止构造函数内重排序 | 禁止重排序(内存屏障) | 禁止重排序 |
性能开销 | 无 | 低 | 高(锁竞争) |
七、总结
-
核心作用:通过不可变性简化并发编程,保障初始化安全。
-
适用场景:
- 定义常量(基本类型或不可变对象)。
- 构建线程安全的不可变类。
- 防止方法或类被修改(增强封装性)。
-
注意事项:
final
引用对象的内部状态仍需同步。- 避免构造函数中
this
逸出,破坏初始化安全。
正确使用 final
能显著提升代码健壮性和可维护性,尤其在多线程环境中,结合 JMM 的内存语义,可有效减少同步开销。