数组的浅拷贝和深拷贝有什么区别?
简化版
浅拷贝只复制数组容器本身,里面如果是对象引用,引用指向的对象仍然共享;深拷贝会把数组里的对象也复制出独立副本。基本类型数组浅拷贝看起来像完全独立,对象数组才是最容易踩坑的地方。
详细版
例如对象数组 [user1, user2] 做浅拷贝后,新旧数组不是同一个数组,但 copy[0] 和 origin[0] 指向同一个 user1。修改 copy[0].name 会影响 origin[0].name。如果要彻底隔离,需要逐个复制元素对象,嵌套对象还要继续递归复制。
面试要强调:数组拷贝有两层,第一层是数组结构,第二层是元素对象。clone、slice、Arrays.copyOf 这类常见 API 多数只复制第一层。
完整版教学
一、数组拷贝要分容器和元素
数组本身是一个容器,容器里存的可能是基本值,也可能是对象引用。讨论浅拷贝和深拷贝时,关键是看复制到了哪一层。
array container
slot0 -> object A
slot1 -> object B
浅拷贝复制数组槽位,但槽位里的引用仍指向原对象。深拷贝不仅复制槽位,还复制对象本身。
记忆钩子:浅拷贝换数组壳,深拷贝连里面对象一起换。
二、基本类型数组为什么不容易暴露问题
如果数组里存的是数字、布尔、字符这类值,浅拷贝后的修改通常不会互相影响。
int[] a = {1, 2, 3};
int[] b = a.clone();
b[0] = 99;
// a[0] 仍是 1
因为 int 这种元素值直接存在数组槽里。复制槽位就复制了值。
但这容易让人误以为所有数组 clone 都是深拷贝。对象数组的情况完全不同。
三、对象数组浅拷贝会共享元素对象
对象数组槽位里存的是引用。
User[] a = { new User("Alice"), new User("Bob") };
User[] b = a.clone();
b[0].name = "Alicia";
此时 a[0].name 也会变成 "Alicia",因为 a[0] 和 b[0] 指向同一个 User 对象。
ASCII 图更直观:
a[0] ----\
-> User("Alice")
b[0] ----/
浅拷贝只断开了数组容器共享,没有断开元素对象共享。
四、深拷贝需要逐个复制元素
深拷贝要创建新数组,还要为每个元素创建独立对象。
User[] b = new User[a.length];
for (int i = 0; i < a.length; i++) {
b[i] = new User(a[i].name);
}
如果元素对象里还有嵌套对象,例如 User 里有 Address,也要决定 Address 是否复制。真正的深拷贝不是一个 API 名字,而是一套对象图复制策略。
User -> Address -> City
深拷贝要想清楚复制到哪一层
五、多维数组也常是浅拷贝坑
很多语言里的二维数组其实是“数组的数组”。只复制外层数组时,内层数组仍共享。
int[][] a = { {1, 2}, {3, 4} };
int[][] b = a.clone();
b[0][0] = 99;
// a[0][0] 也变成 99
图示:
a -> [ row0, row1 ]
b -> [ row0, row1 ]
row0 -> [1, 2]
要深拷贝二维数组,至少要逐行复制。
六、性能和语义要一起考虑
深拷贝更安全,但成本更高。数组有 100 万个对象时,深拷贝可能创建 100 万个新对象,带来 CPU、内存和 GC 压力。
| 拷贝方式 | 优点 | 风险 |
|---|---|---|
| 浅拷贝 | 快,内存省 | 元素对象共享 |
| 深拷贝 | 隔离强 | 成本高,规则复杂 |
| 不可变对象 | 可安全共享 | 需要对象设计支持 |
如果元素对象不可变,浅拷贝通常也很安全,因为共享对象不能被修改。
七、常见误区与追问
- 误区:数组 clone 一定是深拷贝。 对基本类型数组像深拷贝,对对象数组通常只是复制引用。
- 误区:新旧数组不是同一个对象就完全独立。 数组容器独立,不代表元素对象独立。
- 误区:二维数组 clone 会复制所有行。 很多语言只复制外层,内层数组仍共享。
- 追问:怎么判断需要深拷贝? 看元素是否可变、是否会被双方修改、是否需要隔离对象图。
- 追问:深拷贝一定好吗? 不一定,深拷贝成本高,还可能破坏共享语义。
- 追问:不可变对象对拷贝有什么帮助? 不可变对象可安全共享,减少深拷贝需求。
八、加强记忆
数组拷贝记成“两层结构”:第一层是数组壳,第二层是元素对象。浅拷贝复制数组壳和引用,深拷贝复制元素对象甚至嵌套对象。基本类型数组不容易出事,对象数组和二维数组最容易共享内部对象;是否深拷贝,要看可变性、隔离需求和性能成本。