一句话总结
浅拷贝只复制引用(基本类型复制值,引用类型共享对象)→ 深拷贝递归复制整个对象图(修改互不影响)。实现方案:Cloneable(逐层重写 clone,繁琐)、序列化(自动递归,需 Serializable)、JSON 序列化(无需 Serializable,可能丢类型)、手动复制(性能最优但代码多)。
初级理解
浅拷贝(Shallow Copy):只复制对象本身,不复制对象内部引用的其他对象。基本类型字段复制值,引用类型字段复制引用(指向同一个对象)。
深拷贝(Deep Copy):复制对象本身以及对象内部引用的所有对象,递归复制整个对象图。修改拷贝不会影响原对象。
class Address {
String city;
Address(String city) { this.city = city; }
}
class Person implements Cloneable {
String name;
Address address; // 引用类型
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝:address 共享
}
}
Person p1 = new Person();
p1.address = new Address("北京");
Person p2 = (Person) p1.clone();
System.out.println(p1.address == p2.address); // true,浅拷贝共享
p2.address.city = "上海";
System.out.println(p1.address.city); // "上海",原对象也被修改了!
中级深入
Cloneable 接口实现深拷贝:Cloneable 是一个标记接口(没有方法),需要重写 clone() 方法并递归克隆引用对象。
class Address implements Cloneable {
String city;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable {
String name;
Address address;
@Override
protected Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
cloned.address = (Address) this.address.clone(); // 递归克隆
return cloned;
}
}
Cloneable 的缺点:需要逐层实现 Cloneable 和重写 clone(),嵌套深时非常繁琐;clone() 是 protected 的,使用不便;是浅拷贝的默认行为,容易出错。
高级拓展
序列化实现深拷贝(推荐):通过将对象序列化再反序列化,可以方便地实现深拷贝,无需逐层实现 Cloneable。要求所有涉及的类都实现 Serializable。
public static <T> T deepCopy(T obj) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (T) ois.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
其他深拷贝方案对比:
| 方案 | 优点 | 缺点 |
| Cloneable | 原生支持 | 逐层实现,容易出错 |
| 序列化 | 自动递归,代码简洁 | 性能较差,需要 Serializable |
| JSON 序列化 | 不要求 Serializable | 依赖第三方库,类型可能丢失 |
| 手动复制/Builder | 性能最好 | 代码量大,容易遗漏 |
面试加分项:能说出多种深拷贝方案及其适用场景,说明你有实际工程经验。
实战场景
场景:DTO 转换中的深拷贝
// 反例:浅拷贝导致原对象被污染
UserDTO dto = new UserDTO();
BeanUtils.copyProperties(user, dto);
dto.getAddress().setCity("上海"); // user 的 address 也被改了!
// 正例:用 MapStruct 生成深拷贝代码
@Mapper
public interface UserConverter {
UserConverter INSTANCE = Mappers.getMapper(UserConverter.class);
@Mapping(target = "address", source = "address") // 自动深拷贝
UserDTO toDTO(User user);
}
场景:JSON 序列化深拷贝(最常用)
// 使用 Jackson 实现深拷贝
public static <T> T deepCopyByJson(T obj, Class<T> clazz) {
try {
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(obj);
return mapper.readValue(json, clazz);
} catch (JsonProcessingException e) {
throw new RuntimeException("深拷贝失败", e);
}
}
// 使用
User copy = deepCopyByJson(original, User.class);
面试模拟
Q:Cloneable 的 clone() 是浅拷贝还是深拷贝?
A:Object.clone() 默认是浅拷贝。基本类型字段复制值,引用类型字段复制引用。要实现深拷贝,需要重写 clone() 并递归克隆所有引用对象。这也是 Cloneable 容易出错的原因。
Q:实际项目中推荐哪种深拷贝方案?
A:1) JSON 序列化最常用(Jackson/Gson),代码简洁,不要求 Cloneable;2) MapStruct/BeanUtils 适合 DTO 转换场景;3) 序列化适合需要完整对象图的场景;4) 手动复制适合性能敏感场景。不推荐 Cloneable,API 设计差且容易出错。