Java中设计不可变对象需满足四点:1.所有字段private final;2.不提供修改状态的public方法,返回新对象;3.构造时对可变参数深拷贝或不可变包装;4.类声明为final以防继承破坏不可变性。

在Java中设计不可变对象,核心是确保对象一旦创建,其状态(即所有字段)在生命周期内无法被修改。这并非仅靠final修饰变量就能实现,而需从封装性、引用安全性和构造过程三方面协同保障。
1. 所有字段必须声明为 private final
这是不可变性的基础约束。任何可变字段都必须禁止外部直接访问和内部意外重赋值。
- 字段私有化:防止子类或外部代码通过继承或反射绕过控制(虽反射仍可突破,但属于非正常场景)
- 使用 final 修饰:保证字段只能在构造器中初始化一次,避免后续被重新赋值
- 基本类型(如 int、boolean)和不可变引用类型(如 String、Integer)天然安全;但像
ArrayList、StringBuilder等可变容器需额外防护
2. 不提供任何修改状态的 public 方法
包括 setter、add/remove 操作、clear、reverse 等会改变内部数据的方法。若需“更新”,应返回一个新对象。
- 例如
String.replace()返回新字符串,而非修改原字符串 - 若类包含集合字段,不要暴露其原始引用:
return new ArrayList(this.items);而非return this.items; - 避免在 getter 中返回可变对象的原始引用;必要时做防御性拷贝(defensive copy)
3. 构造过程需确保内部状态不被泄露
尤其当构造参数本身是可变对象时,若直接赋值,外部仍可通过原引用修改内部状态。
立即学习“Java免费学习笔记(深入)”;
- 对传入的可变参数(如数组、List、Date),应在构造器内进行深拷贝或不可变包装
- 示例:
this.dates = Collections.unmodifiableList(new ArrayList(inputDates)); - 避免在构造器中调用可被子类重写的方法(防止 this 引用逸出)
4. 类自身必须是 final 或严格控制继承
若允许继承,子类可能通过添加可变字段或重写方法破坏不可变契约。
- 最稳妥方式:将类声明为
final,杜绝继承带来的不确定性 - 若必须支持继承,需确保所有字段和关键逻辑受保护,且文档明确说明“子类不得破坏不可变性”——但实践中难以保证,不推荐
String、LocalDateTime、BigInteger 和所有基本类型的包装类。它们的设计已验证了上述原则的有效性。自定义不可变类时,可参考其源码中对参数校验、拷贝逻辑和防御性封装的处理方式。










