深拷贝和浅拷贝的区别?
发表于更新于
字数总计:1.7k阅读时长:6分钟阅读量: 长沙
面试考察点
- 基础概念理解:面试官不仅仅是想知道 “一个是复制引用,一个是复制对象”,更是想考察你是否理解 Java 对象在内存中的存储结构,以及引用类型和基本类型的区别。
- 实现方式掌握:考察你是否知道如何实现深拷贝(
Cloneable、序列化、第三方工具),以及各种方式的优缺点。
- 实战经验:实际开发中何时需要深拷贝?哪些场景下浅拷贝会导致 bug?这反映你的工程经验。
核心答案
浅拷贝只复制对象本身,内部的引用类型成员变量仍然指向原对象;深拷贝会递归复制所有层级的对象,创建完全独立的新对象。
| 对比维度 |
浅拷贝 |
深拷贝 |
| 对象本身 |
✅ 复制 |
✅ 复制 |
| 基本类型成员 |
✅ 复制值 |
✅ 复制值 |
| 引用类型成员 |
❌ 只复制引用(共享) |
✅ 递归复制对象 |
| 修改影响 |
修改引用成员会影响原对象 |
完全独立,互不影响 |
| 实现复杂度 |
简单(clone()) |
复杂(递归/序列化/工具) |
深度解析
一、内存结构对比图
flowchart TB
subgraph SHALLOW["浅拷贝"]
P1["原始对象 Person<br/>name:张三,age:25"] -->|"引用"| A1["Address:北京"]
C1["拷贝对象 Person<br/>name:张三,age:25(基本类型:值复制)"] -->|"引用复制"| A1
end
subgraph DEEP["深拷贝"]
P2["原始对象 Person<br/>name:张三,age:25"] -->|"引用"| A2["Address:北京"]
C2["拷贝对象 Person<br/>name:张三,age:25"] -->|"递归拷贝引用对象"| A3["全新 Address:北京"]
end
上图清晰展示了浅拷贝和深拷贝在内存中的区别。关键要点:
- 浅拷贝的 “坑”:虽然
Person 对象复制了一份,但 address 引用指向的是同一个 Address 对象。修改拷贝对象的地址,原对象的地址也会跟着变。
- 深拷贝的 “彻底”:不仅复制
Person,连 Address 对象也一起复制,两个对象完全独立,互不影响。
二、代码示例:浅拷贝翻车现场
class Address implements Cloneable { String city;
public Address(String city) { this.city = city; }
@Override protected Address clone() throws CloneNotSupportedException { return (Address) super.clone(); } }
class Person implements Cloneable { String name; int age; Address address;
public Person(String name, int age, Address address) { this.name = name; this.age = age; this.address = address; }
@Override protected Person clone() throws CloneNotSupportedException { return (Person) super.clone(); } }
public class ShallowCopyDemo { public static void main(String[] args) throws Exception { Person original = new Person("张三", 25, new Address("北京"));
Person copy = original.clone();
copy.age = 30; System.out.println(original.age);
copy.address.city = "上海"; System.out.println(original.address.city); } }
|
看到了吗?修改 copy.address.city 后,original.address.city 也变成了 “上海”,这就是浅拷贝的 “坑”。
三、深拷贝的三种实现方式
方式一:递归调用 clone()(手动实现)
class Person implements Cloneable { String name; int age; Address address;
@Override protected Person clone() throws CloneNotSupportedException { Person cloned = (Person) super.clone(); cloned.address = address.clone(); return cloned; } }
class Address implements Cloneable { String city;
@Override protected Address clone() throws CloneNotSupportedException { return (Address) super.clone(); } }
|
缺点:如果对象层级很深,需要每层都实现 Cloneable 并重写 clone(),代码冗余且容易遗漏。
方式二:序列化/反序列化(推荐)
import java.io.*;
class Person implements Serializable { String name; int age; Address address;
public Person deepCopy() { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (Person) ois.readObject(); } catch (Exception e) { throw new RuntimeException("深拷贝失败", e); } } }
class Address implements Serializable { String city; }
|
优点:一行代码搞定,无需手动递归,支持任意深度的对象层级。 缺点:性能相对较差,所有类必须实现 Serializable。
方式三:第三方工具库(生产推荐)
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.clone.CloneSupport;
Person copy = BeanUtil.copyProperties(original, Person.class);
import com.fasterxml.jackson.databind.ObjectMapper; ObjectMapper mapper = new ObjectMapper(); Person deepCopy = mapper.readValue(mapper.writeValueAsString(original), Person.class);
import com.google.gson.Gson; Gson gson = new Gson(); Person deepCopy = gson.fromJson(gson.toJson(original), Person.class);
|
优点:代码简洁,无需实现接口,性能和可维护性都较好。
四、三种方式对比
| 实现方式 |
代码复杂度 |
性能 |
适用场景 |
| 递归 clone() |
高(每层都要实现) |
好 |
对象层级浅且固定 |
| 序列化/反序列化 |
低(需 Serializable) |
较差 |
通用场景 |
| JSON 工具 |
低(无需接口) |
中等 |
生产推荐 |
面试高频追问
- Java 的
Object.clone() 是浅拷贝还是深拷贝? 默认是浅拷贝。只复制当前对象的基本类型和引用,不会递归复制引用指向的对象。要实现深拷贝,需要在 clone() 方法中手动调用引用类型成员的 clone()。
- 为什么 Java 设计成默认浅拷贝? 性能考虑。深拷贝需要递归复制整个对象图,如果对象层级很深或包含循环引用,可能导致性能问题或栈溢出。浅拷贝作为默认行为更安全、可控。
- 数组的
clone() 是深拷贝还是浅拷贝? 对于基本类型数组是深拷贝(值复制),对于引用类型数组是浅拷贝(引用复制)。
常见面试变体
- 变体一:”如何实现一个对象的深拷贝?”
- 变体二:”
clone() 方法为什么需要实现 Cloneable 接口?”
- 变体三:”序列化实现深拷贝的原理是什么?”
记忆口诀
浅拷贝:壳子新,芯子旧,引用共享出 bug; 深拷贝:里外新,彻底分,序列化来一键搞。
总结
浅拷贝只复制对象本身,引用类型成员仍然共享;深拷贝递归复制所有层级对象,完全独立。生产环境推荐使用 JSON 工具(Jackson/Gson)或序列化方式实现深拷贝,避免手动递归 clone() 的繁琐和遗漏。
声明:本文转载自小哈的博客 《深拷贝和浅拷贝的区别?》,版权归原文作者所有。转载请保留本声明与原文链接。


zhangchangsheng
眼太浅,饶恕我对世界一无所知。放过我,反正看来看去不外如是。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 边走边爱,四海为家!