Object.clone()默认浅拷贝,因底层按内存字节复制,引用类型仅复制地址;深拷贝需手动重写clone()并递归克隆引用字段,或用序列化等替代方案。

Java 中 Object.clone() 默认只做浅拷贝,不会自动递归复制引用对象。要实现深拷贝,必须手动干预——在重写的 clone() 方法里,对每个可变的引用类型字段,显式调用其自身的 clone() 方法(前提是它也支持克隆),或通过其他方式创建独立副本。
为什么 clone() 不自动深拷贝?
Object.clone() 是 native 方法,底层按内存字节复制:
- 基本类型(如
int,boolean)→ 复制值; - 引用类型(如
Address,List<String>)→ 复制的是地址,不是对象本身。
所以super.clone()返回的对象,其引用字段仍指向原对象里的同一实例。
递归调用 clone() 实现深拷贝的关键步骤
- 所有涉及的类(包括嵌套类)都必须实现
Cloneable接口; - 每个类都要重写
public Object clone(),并确保调用super.clone()后,对引用字段单独克隆; - 克隆顺序从内到外:先保证底层对象可克隆,再逐层向上处理。
class Address implements Cloneable {
private String city;
private String street;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // Address 无嵌套引用,浅拷贝即够用
}
}
class Person implements Cloneable {
private String name;
private int age;
private Address address; // 引用类型,需深拷贝
private List<String> hobbies; // 可变集合,也要深拷贝
@Override
public Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
// 手动克隆引用字段
cloned.address = (Address) this.address.clone(); // 递归调用
cloned.hobbies = new ArrayList<>(this.hobbies); // 构造新集合(不可变元素时可行)
return cloned;
}
}⚠️ 注意:
ArrayList.clone()本身是浅拷贝,只复制列表结构,不复制元素。如果hobbies里存的是自定义可变对象(比如Hobby类),就得遍历并克隆每个元素。
常见引用类型字段的深拷贝处理方式
-
自定义对象(如
Address):调用其clone()方法(需已实现Cloneable); -
数组:用
Arrays.copyOf()或循环克隆每个元素; -
ArrayList/LinkedList:- 元素是不可变类型(
String,Integer)→new ArrayList<>(original)即可; - 元素是可变对象 → 遍历 +
element.clone();
- 元素是不可变类型(
-
HashMap等 Map:新建 map,遍历 entry 并克隆 key 和 value(若需完全独立); - 包含循环引用的对象图:手动递归易出错,建议改用序列化或专用库。
替代方案:比手动递归更稳妥的选择
-
序列化/反序列化(要求所有类实现
Serializable):public static <T> T deepClone(T obj) throws IOException, ClassNotFoundException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(obj); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (T) ois.readObject(); } -
Apache Commons Lang 的
SerializationUtils.clone():封装了上述逻辑,一行搞定; - JSON 序列化(如 Jackson):适用于 POJO,但会丢失方法、静态字段、非标准构造逻辑。
不复杂但容易忽略:深拷贝不是“开个开关”,而是根据对象结构逐层决定复制策略。
立即学习“Java免费学习笔记(深入)”;


















