Object.clone() 默认浅拷贝:基本类型值复制,引用类型只复制地址;String和包装类因不可变性表现类似深拷贝;基础类型本身无引用,故深/浅拷贝无区别。

浅拷贝的 clone() 方法到底复制了什么
Java 中的 Object.clone() 默认就是浅拷贝:基础类型字段(如 int、boolean)值被完整复制,互不影响;但引用类型字段(如 List、StringBuffer、自定义对象)只复制了地址,新旧对象仍指向堆中同一块内存。
常见错误现象:dictionary1.clone() 后修改 dictionary2.words.add("新词"),dictionary1.words 也跟着变——这不是 bug,是浅拷贝的必然行为。
- 必须实现
Cloneable接口,否则调用clone()会抛CloneNotSupportedException -
super.clone()是 native 实现,不走构造函数,字段复制是逐位(bit-wise)的,不触发任何初始化逻辑 -
String和基本类型的包装类(Integer、Boolean)虽是引用类型,但因不可变性,在浅拷贝中“表现得像深拷贝”——改一个不会影响另一个
基础数据类型在深/浅拷贝中为何“没区别”
因为基础类型(byte、short、int、long、float、double、char、boolean)本身不存引用,只存值。无论浅拷贝还是深拷贝,它们都是直接复制数值,天然隔离。
所以你不需要为 int age 或 double salary 做任何特殊处理——clone()、构造器传参、甚至手动赋值,效果完全一致。
立即学习“Java免费学习笔记(深入)”;
- 别被“深拷贝要递归复制所有引用”带偏:基础类型根本不在递归路径上
- 如果类里只有基础类型字段,那
clone()就是事实上的深拷贝,无需额外操作 - 混淆点常来自误把
Integer当作基础类型:它本质是对象,但因缓存和不可变,行为接近int
为什么不能靠 = 赋值来理解拷贝语义
Person p2 = p1; 不是拷贝,是引用传递——p1 和 p2 指向同一个堆对象。这既不是浅拷贝,也不是深拷贝,连新对象都没创建。
真实开发中,新手常把这种写法当成“复制”,结果改 p2.name 发现 p1.name 也变了,然后困惑“为啥 clone 没生效”。其实根本没走到 clone 那步。
-
==比较的是引用地址,equals()才可能比内容(取决于是否重写) - 验证是否真拷贝成功,第一反应应是
System.out.println(p1 == p2):浅拷贝返回false,引用赋值永远是true - IDE 调试时注意看变量右侧的内存地址(如
@621be5d1),相同就说明没拷贝
深拷贝对基础类型字段毫无额外开销
深拷贝的代价全在引用类型上:要 new 新对象、递归调用其 clone() 或构造器、可能触发序列化等。而基础类型字段不管在哪种拷贝策略下,都只是几个字节的内存复制,纳秒级,可忽略。
所以当你设计深拷贝逻辑(比如重写 clone())时,对 int、long 等字段,照抄就行,不用绕弯子。
- 别为了“统一风格”给基础类型字段加
new Integer(x)——这是画蛇添足,还可能触发自动装箱/拆箱开销 - 如果用 JSON 序列化(如 Jackson)做深拷贝,基础类型会被原样写出再读入,仍是值复制,无副作用
- 真正要警惕的是嵌套引用:比如
private List<address> addresses</address>,这里List本身和每个Address都得单独深拷贝,漏一层就退化成浅拷贝


















