不可变对象指创建后状态无法被修改的设计,如String通过final类、private final字段和防御性拷贝实现;适用于HashMap键、多线程共享等场景,但不适合高频修改。

不可变对象(Immutable Object)在 Java 中指创建后状态无法被修改的对象——不是“不能改”,而是设计上**禁止任何方法改变其内部字段的值**。
为什么 String 是典型的不可变对象
每次对 String 调用 concat、substring、toUpperCase 等方法,返回的都是新对象,原对象内容始终不变:
String s1 = "hello"; String s2 = s1.toUpperCase(); // s1 仍是 "hello",s2 是 "HELLO"
这背后依赖三个关键设计:
-
final类:防止子类覆盖方法破坏不可变性 -
private final字段:如value字节数组,外部不可访问、不可重赋值 - 所有 getter 不返回可变引用(如不直接暴露内部
char[])
自己写不可变类要注意什么
手动实现不可变对象时,最容易翻车的地方不是忘了加 final,而是忽略了“防御性拷贝”:
立即学习“Java免费学习笔记(深入)”;
- 构造器中若接收了可变对象(如
ArrayList、Date),必须做深拷贝,不能直接赋值给final字段 - getter 方法返回集合时,应返回
Collections.unmodifiableList(...)或新副本,而非原始引用 - 避免提供任何 setter、
add、clear等修改状态的方法 - 如果类有子类需求,就不能声明为
final,但需把所有方法设为final防止重写
不可变对象不是万能的,别硬套
它适合用在以下场景:
- 作为
HashMap的 key(保证 hash 值稳定,不会因修改导致查找失败) - 多线程共享数据(天然线程安全,无需同步)
- 需要历史快照或回滚能力(比如配置快照、命令模式中的命令对象)
但不适合高频修改的场景:比如拼接大量字符串用 StringBuilder,而不是反复 new String;又比如 DTO 对象若需多次 set 字段,强行不可变只会催生一堆 builder 和中间对象。
真正难的不是加 final,而是判断哪些字段该不可变、哪些不该,以及如何安全地与可变世界(如数据库、IO、第三方库)交接——那才是设计思想落地时最常被跳过的一步。










