返回可变对象时须做防御性拷贝以维护封装性,包括Date、数组、ArrayList、HashMap及自定义可变类;getter和setter均需拷贝,深拷贝适用于嵌套可变对象。

在 Java 封装中,返回可变对象时若直接返回内部引用,会破坏封装性——外部可随意修改对象状态,导致类的不变量被破坏。正确做法是返回该对象的防御性拷贝(Defensive Copy),确保内部状态不被外部篡改。
哪些对象需要防御性拷贝?
只要对象是可变的(mutable)且被用作类的私有字段,且通过 getter 方法对外暴露,就需考虑防御性拷贝。常见类型包括:
- java.util.Date(已过时但仍在大量遗留代码中使用)
- java.time.LocalDateTime 等不可变类除外,但 java.util.Calendar 是可变的
- 数组(如 int[], String[])
- ArrayList、HashMap 等集合类
- 自定义的可变业务对象(如 Address、PersonDetail)
如何安全返回可变对象?
核心原则:不返回原始引用,而是创建并返回一个独立副本。具体方式依对象类型而异:
-
Date 类型:用
new Date(original.getTime())构造新实例(不能用clone(),因 Date.clone() 不是 public) -
数组:用
Arrays.copyOf(arr, arr.length)或arr.clone()(注意浅拷贝) -
ArrayList/LinkedList:用
new ArrayList(originalList)或originalList.stream().collect(Collectors.toList()) -
HashMap/HashSet:用
new HashMap(originalMap)或new HashSet(originalSet) -
自定义可变类:提供拷贝构造器或
copy()方法,并确保深拷贝(若含嵌套可变对象)
setter 中也要做防御性拷贝
只在 getter 做防御性拷贝不够。如果 setter 接收外部传入的可变对象并直接赋值,同样会引入风险。例如:
立即学习“Java免费学习笔记(深入)”;
public void setBirthDate(Date date) { this.birthDate = date; } // ❌ 危险!
应改为:
public void setBirthDate(Date date) { this.birthDate = (date == null) ? null : new Date(date.getTime()); } // ✅ 安全
同理,对集合类 setter 也需拷贝后再存入。
注意深拷贝 vs 浅拷贝
多数场景下浅拷贝足够(如集合中元素本身不可变),但若集合内含可变对象(如 List<Address>),则需深拷贝:
- 浅拷贝:复制容器本身(如 new ArrayList),但元素仍指向原对象
- 深拷贝:递归复制每个可变元素(如对每个 Address 调用其 copy() 方法)
- 判断依据:看被包含对象是否可能被外部修改;若会,则必须深拷贝


















