final修饰引用类型变量时,不可变的是引用本身而非对象内容;即变量不能重新指向新对象,但所指对象的内部状态仍可修改。

final 修饰引用类型变量时,不可变的是“引用本身”,不是“对象内容”。也就是说,变量不能再指向另一个对象,但对象内部的属性、状态仍可被修改。
引用不可变,对象可变
声明为 final 的引用类型变量,其存储的内存地址固定不变。一旦初始化完成,就不能再用 = 赋值给它新的对象。但只要该对象自身不是不可变类(如 String、LocalDate),它的字段、集合元素、内部状态依然可以被修改。
-
合法操作:调用对象的方法修改其内部字段(如
user.setName("李四")、list.add("item")、sb.append("more")) -
非法操作:重新赋值引用(如
user = new User("王五", 30)、list = new ArrayList())——编译直接报错
常见例子对比
以下代码片段能清晰体现这个区别:
final StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // ✅ 合法:修改对象内容
// sb = new StringBuilder("Hi"); // ❌ 编译错误:不能改变引用
final List<string> names = new ArrayList();
names.add("Alice"); // ✅ 合法:List 内容可变
// names = new ArrayList(); // ❌ 编译错误:引用不可重赋值
final User user = new User("张三", 25);
user.setAge(26); // ✅ 合法:User 对象非 final,字段可改
// user = new User("李四", 28); // ❌ 编译错误</string>
如何真正实现对象不可变?
仅靠 final 无法让对象内容不可变。要达到“完全不可变”,需配合其他设计:
- 类本身用 final 修饰(防止被继承后破坏封装)
- 所有字段用 private final 声明
- 不提供修改字段的 setter 方法
- 构造器完成全部初始化,避免外部访问可变内部状态(如返回集合副本而非原始引用)
- 若字段是可变对象(如 Date、ArrayList),在 getter 中返回防御性拷贝
为什么这样设计?
Java 的 final 关注的是“变量绑定关系”的稳定性,而非对象语义上的只读。这种分层控制更灵活:既保证了引用安全(比如确保某个配置对象不会被意外替换),又保留了对象自身的可操作性(比如允许 builder 模式逐步构建)。JVM 也正依赖这种确定性做优化,比如将 final 字段视为编译期常量或进行锁消除。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











