深拷贝和浅拷贝的区别?

2025年 阅读约 7 分钟 面试指南 · Java面试

深入解析Java中深拷贝和浅拷贝的区别,从Cloneable接口到序列化方案,分初级、中级、高级三个层次全面讲解。

一句话总结

浅拷贝只复制引用(基本类型复制值,引用类型共享对象)→ 深拷贝递归复制整个对象图(修改互不影响)。实现方案: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 设计差且容易出错。