← 返回题目列表

数组的浅拷贝和深拷贝有什么区别?

高频 中等 第 15 / 30 题 更新于 2026/07/29
数组浅拷贝深拷贝引用类型

简化版

浅拷贝只复制数组容器本身,里面如果是对象引用,引用指向的对象仍然共享;深拷贝会把数组里的对象也复制出独立副本。基本类型数组浅拷贝看起来像完全独立,对象数组才是最容易踩坑的地方。

详细版

例如对象数组 [user1, user2] 做浅拷贝后,新旧数组不是同一个数组,但 copy[0]origin[0] 指向同一个 user1。修改 copy[0].name 会影响 origin[0].name。如果要彻底隔离,需要逐个复制元素对象,嵌套对象还要继续递归复制。

面试要强调:数组拷贝有两层,第一层是数组结构,第二层是元素对象。clonesliceArrays.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 会复制所有行。 很多语言只复制外层,内层数组仍共享。
  • 追问:怎么判断需要深拷贝? 看元素是否可变、是否会被双方修改、是否需要隔离对象图。
  • 追问:深拷贝一定好吗? 不一定,深拷贝成本高,还可能破坏共享语义。
  • 追问:不可变对象对拷贝有什么帮助? 不可变对象可安全共享,减少深拷贝需求。

八、加强记忆

数组拷贝记成“两层结构”:第一层是数组壳,第二层是元素对象。浅拷贝复制数组壳和引用,深拷贝复制元素对象甚至嵌套对象。基本类型数组不容易出事,对象数组和二维数组最容易共享内部对象;是否深拷贝,要看可变性、隔离需求和性能成本。