java中getter需对可变引用类型做防御性拷贝以保障封装性,包括可变集合、java.util.date、自定义可变对象和数组;string、localdatetime等不可变类型可直接返回引用。

Java中getter方法返回引用类型属性的副本,是为了防止外部代码通过返回的对象引用直接修改对象内部状态,从而破坏封装性。核心思路是:不返回原始引用,而是返回一个内容相同但独立的新对象(即“防御性拷贝”)。
哪些引用类型需要做防御性拷贝?
以下类型的属性在getter中通常需要返回副本:
-
可变集合类:如
ArrayList、HashMap、HashSet等(Arrays.asList()返回的列表也属于可变视图) -
可变日期类:如
java.util.Date、java.time.LocalDateTime(注意:LocalDateTime本身不可变,但若字段是Date就必须拷贝) -
自定义可变对象:比如你自己写的
Person、Address类,且其字段可被外部修改 -
数组:无论元素类型如何,数组本身是可变的,需复制(用
clone()或Arrays.copyOf())
常见防御性拷贝写法示例
以一个含集合和日期的类为例:
public class User {
private String name;
private Date birthDate; // java.util.Date 是可变的
private List<string> hobbies; // 可变列表
private int[] scores; // 原始数组
// 构造时也建议做防御性拷贝(略)
public Date getBirthDate() {
return (birthDate != null) ? new Date(birthDate.getTime()) : null;
}
public List<string> getHobbies() {
return (hobbies != null)
? new ArrayList(hobbies)
: Collections.emptyList();
}
public int[] getScores() {
return (scores != null) ? scores.clone() : new int[0];
}
}</string></string>
说明:
-
Date使用new Date(original.getTime())创建新实例,避免共享同一时间点对象 -
List用new ArrayList(original)复制内容;若原列表可能为null,需判空并返回不可变空集合(Collections.emptyList()) -
int[]用clone()最简洁安全;也可用Arrays.copyOf(scores, scores.length)
不可变类型无需拷贝
以下类型本身不可变,getter中可直接返回引用,无需额外拷贝:
- String:字符串不可变,即使返回引用也无法修改其内容
-
JDK 8+ 的时间类:如
LocalDateTime、ZonedDateTime、Instant,都是不可变对象 -
包装类:如
Integer、Boolean等,也是不可变的
例如:public String getName() { return name; } 安全,无需 new String(name)。
进阶建议:用不可变容器替代可变集合
更彻底的方案是,在字段声明和初始化阶段就使用不可变集合,这样getter可直接返回,既安全又高效:
- 使用
java.util.Collections.unmodifiableList()包装(注意:仅对外不可变,原始引用仍可改) - 推荐用 Guava:
ImmutableList.copyOf(list),或ImmutableSet.of(...) - JDK 10+ 可用
List.copyOf(list)(要求输入不可修改,且返回不可变列表)
例如:private final List<string> hobbies = List.copyOf(originalList);</string> → getter 中直接 return hobbies; 即可。
关键不是“所有引用都要拷贝”,而是识别哪些对象状态可能被意外修改,并在暴露接口时切断修改通道。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











