JVM 面试炸裂题:深拷贝 VS 浅拷贝,99% 的人答不清!

0 阅读5分钟



一次令人窒息的面试体验

事情发生在我上一份工作离职后。那段时间信心爆棚,觉得自己对JVM烂熟于心,GC啊、内存模型啊,轻轻松松就能应对。

结果到了美团面试,面试官一开口:“你能说说深拷贝和浅拷贝的区别吗?”

我心里一乐,这不送分题嘛!

“浅拷贝就是只拷贝对象的第一层,深拷贝是连引用的对象也都复制一份。”

说完我就等着夸奖,结果面试官一挑眉:“那如果对象里嵌套了多个引用,浅拷贝和深拷贝在内存上分别会是怎样的结构?”

……

完了,脑子顿时嗡嗡响,我竟然说不出具体场景,只能干巴巴地扯了点 clone() 的话题,最后草草结束。

回家路上我一直在想:这么简单的知识点,为什么我在关键时刻说不清楚?

那晚我重新捡起了笔记,从JVM内存模型、对象结构到实际代码实现,一个点一个点抠,才发现“深拷贝 vs 浅拷贝”远没有我们想象中那么简单。

什么是浅拷贝?

我们先从“浅拷贝”聊起。

浅拷贝指的是:只复制当前对象本身的字段,对于引用类型的字段,只复制引用地址,不复制其指向的对象本身。

说人话就是——表面复制一份,看起来和原来一样,其实底层的引用指向的还是同一块内存区域。

举个小例子:

当你用 clone() 创建了一个新对象,Person 对象本体是复制了,但里面的 Address 是引用,还是指向原来的地址。

所以,当你修改 clone 出来的 address 的城市名,原对象也跟着变了。这就是浅拷贝“表面忠诚”的真面目。

深拷贝又是什么?

深拷贝的定义相对复杂一些。

它不仅复制当前对象本身的字段,而且对引用类型字段也会“递归”地复制一份新的对象出来,整个对象链条都脱离了原始对象。

形象点说,浅拷贝是拍了张照片,外形一模一样但连着同一条神经;而深拷贝是另造了一个灵魂,也有自己的脑袋、手脚。

用代码实现深拷贝,通常有两种方式:

方式一:实现 Cloneable 接口并在每一层都重写 clone() 方法

方式二:使用序列化(推荐用于大型复杂结构)

缺点是效率不如 clone(),但胜在稳定、通用。

JVM视角下的拷贝到底发生了什么?

这时,我们来切换一下视角,用 JVM 的眼睛看拷贝到底干了啥。

当一个对象被创建时,它会被分配在 堆内存 中,所有引用类型字段也同样如此。

浅拷贝时,新对象只复制了第一层结构,其引用字段指向的地址与原对象相同,因此依赖了相同的堆空间。

而深拷贝则是为引用字段也申请新的堆内存地址,并将其内容一并复制。因此,在 JVM 内存中,深拷贝会产生更多对象实例,但带来了真正的独立性。

这也是为什么深拷贝开销更大,性能要慎重考虑的原因。

一道经典社招题:你能画出深浅拷贝的结构图吗?

在字节跳动面试中,我就被要求“在纸上画出一段对象的浅拷贝和深拷贝的内存图”,还要解释 JVM 中的堆和栈的作用。

当时我画了这么一个结构:

  • 原始对象 Order,有一个指向 products 的引用
  • 浅拷贝的 OrderCopy 也有一个指向相同 products 的引用(箭头指向同一块地址)
  • 深拷贝的 OrderCopy 拥有自己独立的 products,箭头各自指向新地址

面试官点点头,说:“这个图比你嘴上说十分钟都有用。”

从那以后,我记住了:面试,不光要懂,更要表达得清楚。

拷贝时的那些坑:你踩过几个?

1. 没有实现 Cloneable 却调用 clone()

这是 Java 的一个“坑设定”。如果你不显式实现 Cloneable 接口,调用 clone() 会抛出 CloneNotSupportedException,即便你写了 clone() 方法。

2. 忘记递归 clone 引用字段

很多同学以为 clone() 一调用万事大吉,殊不知你得自己去处理嵌套对象,否则默认还是浅拷贝。

3. 用 BeanUtils.copyProperties() 以为是深拷贝

这个常见在 Spring 项目中,实际上它复制的是“值”,但依旧是浅拷贝。引用字段还是共用的,改了一个变两个。

工作中我们该怎么选?

这其实没有绝对标准,要看你的业务需求:

  • 如果对象结构简单,或你明确知道不会修改引用字段,浅拷贝足矣。
  • 如果需要真正隔离两个对象,防止互相影响,务必用深拷贝
  • 如果是高性能场景(比如游戏逻辑、交易撮合引擎),要避免深拷贝带来的GC压力,可能考虑对象池或写时复制(COW)策略。

写在最后

那次美团面试我没通过,但我很感谢那个面试官,因为他让我重新认识了“拷贝”这个在 Java 世界中最基础却最容易被忽略的知识点。

现在我每次准备面试,都会写写画画,把脑中知识真正“物化”,就像做一次深拷贝。

最后送大家一句话:

技术不是记忆的堆叠,而是理解和表达的艺术。

会用,更要说得明白、画得出来。

你最近有没有被“拷贝”题难住过?欢迎在留言区聊聊你的故事呀!

END

如果你觉得这篇文章对你有帮助,记得 转发 + 在看,支持小米继续更新更多“JVM 社招面试题故事集”系列喔!

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!

我们下期见~