保护性拷贝是Java封装中防止外部篡改内部状态的关键手段:在getter中返回可变对象(如Date、ArrayList)的副本;在构造器和setter中接收前先拷贝;优先使用LocalDate、List.copyOf等不可变类型;数组和自定义可变类需警惕并做深拷贝。

保护性拷贝(Defensive Copy)是 Java 封装中防止外部篡改对象内部状态的关键手段。核心思路是:当类的字段是可变对象(如 ArrayList、Date、自定义可变类等),且该字段通过 getter 返回或通过构造器/ setter 接收时,不直接暴露引用,而是返回其副本,从而切断外部与内部数据的引用关联。
在 getter 中返回不可变副本
如果类持有可变对象(例如 java.util.Date、ArrayList),getter 方法不能直接返回原始引用,否则调用方可能修改它,破坏封装性。
- 对
Date等旧式可变类型:用new Date(originalDate.getTime())创建新实例 - 对
List、Map等集合:用new ArrayList(originalList)或Collections.unmodifiableList(...)(后者更侧重只读语义,但需配合构造时的保护性拷贝) - 示例:
private Date birthDate; public Date getBirthDate() { return (birthDate != null) ? new Date(birthDate.getTime()) : null; }
在构造器和 setter 中接收前做拷贝
若外部传入可变对象作为参数初始化或修改内部状态,必须先拷贝再保存,避免后续外部修改影响内部一致性。
- 构造器中:不直接赋值
this.dates = dates;,而写this.dates = new ArrayList(dates); - setter 中:同样先校验非空,再拷贝;若允许 null,也要统一处理
- 注意:拷贝应深度到业务所需层级——若集合元素本身可变(如自定义类),还需对每个元素做保护性拷贝(即深拷贝),否则仍存在风险
优先使用不可变类型替代可变类型
比手动拷贝更安全、更简洁的方式是尽量选用天然不可变的类型:
立即学习“Java免费学习笔记(深入)”;
- 用
java.time.LocalDate/Instant替代Date - 用
ImmutableList.copyOf(list)(来自 Guava)或List.copyOf(list)(Java 10+)替代ArrayList存储只读集合 - 自定义类声明为
final,所有字段private final,不提供修改方法,并在构造时确保字段值自身也不可变
警惕数组和自定义可变类的陷阱
数组是可变对象,即使声明为 private final String[] names,外部拿到引用后仍可修改元素内容。
- 构造器接收数组参数时:用
Arrays.copyOf(src, src.length) - getter 返回数组时:同样用
Arrays.copyOf(internalArray, internalArray.length) - 对于自定义可变类(如
Person含 public setter),若作为字段存在,其 getter/setter 都需做保护性拷贝(创建新Person实例并复制字段值)


















