防御性拷贝不是为了提速,而是断开外部与内部引用链;构造器接收数组/List/Set时须用Arrays.copyOf、new ArrayList或new HashSet拷贝,getter返回前也需拷贝,不可变类型(如String、LocalDateTime)可直接返回,自定义可变对象需深拷贝,优先选用不可变类型替代拷贝。

防御性拷贝不是为了提速,而是为了断开外部与内部的引用链——只要对方拿到的是你字段的原始引用,就等于把修改权交了出去。
构造器里接收外部数组或集合时必须拷贝
Java中传入数组或List、Set等可变容器,若直接赋值给private字段,外部后续修改会立刻反映到对象内部。
- 错例:
this.items = items;→items.add(x)会污染内部状态 - 正确做法:对数组用
Arrays.copyOf(items, items.length);对List用new ArrayList(items);对Set用new HashSet(items) - 注意:
Collections.unmodifiableList(items)不是防御性拷贝,它只阻止add/remove,不防items.get(0).setName("x")
getter方法返回内部可变对象前必须拷贝
返回 return this.dates; 等同于公开内部引用,调用方调用 dates.clear() 就能清空你的私有数据。
- 基础类型包装类(Integer、String、LocalDate)可直接返回,它们不可变
- 自定义可变对象(如Person、Config)需返回新实例:
return new Person(this.person); - 集合类统一返回副本:
return Collections.unmodifiableList(new ArrayList(this.items));或 Java 10+ 的List.copyOf(this.items)(仅当元素全不可变时安全)
Set.copyOf 和 List.copyOf 的真实能力边界
它们只做浅拷贝 + 不可变包装,不递归复制元素内容。
- 安全场景:
Set.copyOf(Set.of("a", "b"))、List.copyOf(Arrays.asList(1, 2, 3))—— 元素本身不可变,引用复制即隔离 - 危险场景:
Set.copyOf(personSet)中 person 是普通类 → 外部仍可通过任一 person 实例改名、改年龄 - 需深拷贝时:用 stream 映射构造新对象,例如
Set.copyOf(originalSet.stream().map(p -> new Person(p)).collect(Collectors.toSet()))
优先用不可变类型替代防御性拷贝
防御性拷贝是补救手段,设计阶段选对类型更省心。
- 时间用
LocalDateTime替代Date,避免被setTime()修改 - 集合字段声明为
private final List<string> items;</string>,构造时用new ArrayList(src)初始化 - 自定义数据类考虑用
record(JDK 14+),天然 final + 不可变语义,且字段类型也应选不可变 - 对外暴露只读接口,如返回
Collection<? extends T>而非具体可变实现类

















