Skip to content

在Java中,浅克隆(Shallow Clone)和深克隆(Deep Clone)是两种不同的对象复制方式,主要区别在于对引用类型字段的处理:

一、核心区别

特性浅克隆深克隆
引用类型字段复制引用地址(原对象和克隆对象共享引用对象)递归复制整个对象图(创建完全独立的副本)
内存关系引用类型字段指向同一内存地址引用类型字段指向不同的内存地址
数据安全性修改引用对象会影响原对象修改引用对象不影响原对象
实现复杂度简单复杂
性能开销

二、实现方式

1. 浅克隆实现步骤

java
// 1. 实现Cloneable接口(标记接口)
class Person implements Cloneable {
    private String name;
    private Address address; // 引用类型字段

    @Override
    public Object clone() throws CloneNotSupportedException {
        // 2. 调用Object.clone()实现浅克隆
        return super.clone(); // 仅复制基本类型和引用地址
    }
}

// 测试
Person original = new Person("Tom", new Address("北京"));
Person cloned = (Person) original.clone();

// 修改克隆对象的引用类型字段会影响原对象
cloned.getAddress().setCity("上海");
System.out.println(original.getAddress().getCity()); // 输出"上海"(共享对象)

2. 深克隆实现方式

(1) 递归重写clone()方法
java
class Address implements Cloneable {
    private String city;
    
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Person implements Cloneable {
    private String name;
    private Address address;

    @Override
    public Object clone() throws CloneNotSupportedException {
        Person cloned = (Person) super.clone();
        cloned.address = (Address) this.address.clone(); // 递归克隆引用对象
        return cloned;
    }
}

// 测试
Person original = new Person("Tom", new Address("北京"));
Person cloned = (Person) original.clone();

cloned.getAddress().setCity("上海");
System.out.println(original.getAddress().getCity()); // 输出"北京"(独立对象)
(2) 通过序列化实现(推荐)
java
import java.io.*;

class Person implements Serializable {
    private String name;
    private Address address; // Address也需实现Serializable

    public Person deepClone() {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(bos)) {
            
            oos.writeObject(this); // 序列化对象
            
            try (ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
                 ObjectInputStream ois = new ObjectInputStream(bis)) {
                
                return (Person) ois.readObject(); // 反序列化生成新对象
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

// 测试
Person cloned = original.deepClone();

三、关键注意事项

  1. Cloneable接口

    • 是标记接口(无方法),不实现会抛CloneNotSupportedException
    • 必须重写Object.clone()(原生方法是protected)
  2. 深克隆的挑战

    • 需要递归处理所有引用类型字段
    • 循环引用可能导致栈溢出(需特殊处理)
    • 序列化方式要求所有对象实现Serializable
  3. 最佳实践

    java
    // 在深克隆中处理null引用避免NPE
    if (this.address != null) {
        cloned.address = (Address) this.address.clone();
    }
  4. 替代方案

    • 使用第三方库(如Apache Commons Lang的SerializationUtils.clone()
    • 手动复制构造器(推荐):
      java
      public Person(Person source) {
          this.name = source.name;
          this.address = new Address(source.address); // 深拷贝
      }

四、总结

场景推荐方式
无引用类型字段浅克隆(简单高效)
嵌套引用对象深克隆(数据安全)
复杂对象图序列化深克隆
高性能要求手动实现复制构造器

📌 设计建议:优先考虑使用复制构造器工厂方法实现深拷贝,避免clone()方法的复杂性。Java的clone()机制存在设计缺陷(详见《Effective Java》第13条),许多现代框架已弃用该方式。