final关键字本身不保证初始化安全,而是通过JMM的两条重排序规则(禁止final写重排序到引用发布之后、禁止读引用与读final字段重排序)和正确构造前提(无this逸出、所有final字段已赋值),确保其他线程无需同步即可看到final字段的初始化值;普通字段因无此约束,可能被重排序导致读到默认值。

final 关键字本身不“保证”初始化安全,而是通过 Java 内存模型(JMM)赋予它的特殊语义,在对象**正确构造**的前提下,让 final 字段的初始化值对其他线程天然可见——无需同步、无需 volatile。
final 如何约束重排序以保障初始化顺序
JMM 对 final 字段施加了两条硬性重排序规则:
- 构造函数内对 final 字段的写操作,不能被重排序到“把 this 引用赋值给外部变量”之后;
- 线程首次读取对象引用后,再读该对象的 final 字段,这两步也不能被重排序。
这意味着:只要对象没在构造中逸出,JVM 就会确保其他线程看到的对象引用,一定伴随着所有 final 字段已写入的最终值,而不是默认值(0、null、false)。
为什么普通字段做不到这点
普通字段没有重排序限制。例如:
立即学习“Java免费学习笔记(深入)”;
class Point {
int x, y;
Point(int x, int y) {
this.x = x; // 可能被 CPU 或编译器延后执行
this.y = y; // 可能先完成
}
}
若线程 A 执行 shared = new Point(1, 2),线程 B 看到 shared != null 后读 shared.x,可能读到 0 —— 因为 x 的写入还没真正发生或还没刷新到主内存。
而 final 字段杜绝了这种“半初始化可见”的风险,是 JMM 层面的语义保障,不是靠开发者加锁实现的。
安全的前提:对象必须正确构造
final 的初始化安全只在以下条件全部满足时生效:
- 所有 final 字段都在构造函数中完成赋值(或声明时直接初始化);
- 构造函数执行完毕前,
this引用没有泄露出去(即无 this 逸出); - 对象引用的发布(如赋给 static 变量、放入 ConcurrentHashMap、返回给调用方)发生在构造函数 return 之后。
一旦发生逸出(比如在构造器里启动线程、注册监听器、赋值给 public static 字段),JMM 对 final 的保障立即失效,其他线程仍可能看到未初始化的值。
final 是安全发布的常用手段之一
它常用于以下典型场景,实现“构造即发布”的安全模式:
- 静态工厂方法返回对象(如
LocalDate.of(2026, 8, 6)); - 对象创建后直接赋值给
static final字段; - 放入线程安全容器(如
ConcurrentHashMap); - 作为不可变类的核心字段(配合 private + no setter + 防御性拷贝)。
注意:final 仅保证字段本身不可再赋值,不阻止对象内部状态改变。要真正不可变,还需类设计配合 —— 字段全 final、类 final、无修改方法、可变组件做防御性拷贝。


















