final关键字约束变量引用不可重绑定,而非对象内容不可变;修饰基本类型时值固定,修饰引用类型时仅锁定地址;final方法禁止重写以保障契约;final类禁止继承;final字段在构造器中初始化可提供happens-before保证。

final 关键字不是为了“让对象不可变”,而是为了禁止重绑定——它约束的是变量(引用)本身,不是所指向的对象内容。
final 修饰变量:只允许赋值一次,但不等于对象不可变
这是最容易误解的一点。声明 final List 后,list 引用不能再指向别的 ArrayList,但 list.add("x") 完全合法。
-
final变量在初始化后无法再被重新赋值(编译期检查) - 如果变量类型是基本类型,值确实固定;如果是引用类型,仅锁定引用地址,不冻结堆上对象的状态
- 想真正实现不可变容器,得用
Collections.unmodifiableList()或ImmutableList.of()(Guava)这类封装逻辑
final 方法:防止子类覆盖,保障契约一致性
当一个方法被标记为 final,比如 public final String toString(),子类无法重写它。这不是为了“性能优化”(JVM 早期可能有内联收益,现在已不依赖此),而是明确表达设计意图:该行为是类协议的一部分,不容篡改。
- 常见于模板方法模式中的钩子方法被禁用时,或安全敏感操作(如
java.lang.String.hashCode()) - 注意:
final方法仍可被子类继承和调用,只是不能重定义逻辑 - 若父类方法本就
private或static,加final没有意义(语法允许但无实际效果)
final 类:彻底封死继承链,强制组合优于继承
声明 final class StringUtils 后,任何类都无法 extends StringUtils。Java 标准库大量使用这一设计,如 String、Integer、LocalDateTime。
立即学习“Java免费学习笔记(深入)”;
- 核心动机是避免子类破坏不可变性或线程安全性——例如,若
String可被继承,子类可能通过重写方法偷偷修改内部char[] - 不影响该类的使用方式:你依然可以
new String("abc")或调用其所有public方法 - 与
sealed(Java 17+)不同:final是“一刀切”禁止所有继承,而sealed允许显式列出可继承的子类
final 字段与线程安全:happens-before 语义的关键支点
只有 final 字段(且在构造器中完成初始化)才能保证:一旦对象构造完成并发布给其他线程,该字段的值对所有线程可见,无需额外同步。
- 这是 JVM 内存模型的硬性保证,不是“建议”——比如
final int id;在构造器里赋值后,其他线程读到的一定是那个值 - 但仅限于
final字段本身;若字段是引用类型(如final List),其内部元素仍可能被并发修改,除非该引用指向的是真正不可变对象 - 反例:
final List—— 构造器里新建了可变列表,data; { data = new ArrayList(); } final只锁住引用,不锁住列表内容
真正需要不可变性时,别只靠 final;它只是拼图的第一块——字段不可重赋、类不可继承、方法不可覆盖,都得配合对象状态本身的封闭性(私有字段 + 无修改方法 + 防御性拷贝)才能落地。









