Java中的深拷贝与浅拷贝

什么是拷贝

在Java中,拷贝是指创建一个对象的副本。拷贝主要分为两种类型:浅拷贝(Shallow Copy)和深拷贝(Deep Copy)。理解这两种拷贝的区别对于编写正确的Java程序非常重要,特别是在处理对象引用时。

浅拷贝(Shallow Copy)

浅拷贝是指创建一个新对象,然后将原始对象的所有字段值复制到新对象中。如果字段是基本类型,则直接复制其值;如果字段是引用类型,则复制引用但不复制引用的对象。因此,原始对象和副本对象将共享引用类型的字段。

浅拷贝的实现方式

在Java中,实现浅拷贝有几种方式:

  1. 使用clone()方法:Object类提供了clone()方法,可以实现浅拷贝

  2. 使用构造方法:通过构造方法复制每个字段

  3. 使用拷贝工厂方法

class Department implements Cloneable {
    private String name;
    
    public Department(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Employee implements Cloneable {
    private String name;
    private Department department;
    
    public Employee(String name, Department department) {
        this.name = name;
        this.department = department;
    }
    
    public String getName() {
        return name;
    }
    
    public Department getDepartment() {
        return department;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public void setDepartment(Department department) {
        this.department = department;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class ShallowCopyExample {
    public static void main(String[] args) throws CloneNotSupportedException {
        Department department = new Department("IT");
        Employee original = new Employee("John", department);
        Employee cloned = (Employee) original.clone();
        
        System.out.println("Original: " + original.getName() + " - " + original.getDepartment().getName());
        System.out.println("Cloned: " + cloned.getName() + " - " + cloned.getDepartment().getName());
        
        // 修改克隆对象的department
        cloned.getDepartment().setName("HR");
        
        System.out.println("\nAfter modifying cloned object:");
        System.out.println("Original: " + original.getName() + " - " + original.getDepartment().getName());
        System.out.println("Cloned: " + cloned.getName() + " - " + cloned.getDepartment().getName());
    }
}

 输出结果:

Original: John - IT
Cloned: John - IT

After modifying cloned object:
Original: John - HR
Cloned: John - HR

可以看到,修改克隆对象的department后,原始对象的department也被修改了,这是因为它们引用的是同一个Department对象。

深拷贝(Deep Copy)

深拷贝是指创建一个新对象,然后递归地复制原始对象的所有字段。对于引用类型的字段,不仅复制引用,还会创建引用对象的新副本。因此,原始对象和副本对象完全独立,互不影响。

深拷贝的实现方式

实现深拷贝有几种常见方法:

  1. 重写clone()方法:在clone()方法中手动复制所有引用类型的字段

  2. 使用序列化:将对象序列化为字节流,然后再反序列化为新对象

  3. 使用拷贝构造方法:创建接受原对象并复制所有字段的构造方法

  4. 使用第三方库:如Apache Commons Lang的SerializationUtils

方法1:重写clone()方法
class Department implements Cloneable {
    private String name;
    
    public Department(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Employee implements Cloneable {
    private String name;
    private Department department;
    
    public Employee(String name, Department department) {
        this.name = name;
        this.department = department;
    }
    
    public String getName() {
        return name;
    }
    
    public Department getDepartment() {
        return department;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public void setDepartment(Department department) {
        this.department = department;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Employee cloned = (Employee) super.clone();
        cloned.department = (Department) department.clone(); // 深度拷贝department
        return cloned;
    }
}

public class DeepCopyExample1 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Department department = new Department("IT");
        Employee original = new Employee("John", department);
        Employee cloned = (Employee) original.clone();
        
        System.out.println("Original: " + original.getName() + " - " + original.getDepartment().getName());
        System.out.println("Cloned: " + cloned.getName() + " - " + cloned.getDepartment().getName());
        
        // 修改克隆对象的department
        cloned.getDepartment().setName("HR");
        
        System.out.println("\nAfter modifying cloned object:");
        System.out.println("Original: " + original.getName() + " - " + original.getDepartment().getName());
        System.out.println("Cloned: " + cloned.getName() + " - " + cloned.getDepartment().getName());
    }
}

 输出结果:

Original: John - IT
Cloned: John - IT

After modifying cloned object:
Original: John - IT
Cloned: John - HR

 方法2:使用序列化实现深拷贝

import java.io.*;

class Department implements Serializable {
    private String name;
    
    public Department(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}

class Employee implements Serializable {
    private String name;
    private Department department;
    
    public Employee(String name, Department department) {
        this.name = name;
        this.department = department;
    }
    
    public String getName() {
        return name;
    }
    
    public Department getDepartment() {
        return department;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public void setDepartment(Department department) {
        this.department = department;
    }
    
    public Employee deepCopy() throws IOException, ClassNotFoundException {
        // 将对象写入字节数组
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        oos.flush();
        
        // 从字节数组读取对象
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (Employee) ois.readObject();
    }
}

public class DeepCopyExample2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Department department = new Department("IT");
        Employee original = new Employee("John", department);
        Employee cloned = original.deepCopy();
        
        System.out.println("Original: " + original.getName() + " - " + original.getDepartment().getName());
        System.out.println("Cloned: " + cloned.getName() + " - " + cloned.getDepartment().getName());
        
        // 修改克隆对象的department
        cloned.getDepartment().setName("HR");
        
        System.out.println("\nAfter modifying cloned object:");
        System.out.println("Original: " + original.getName() + " - " + original.getDepartment().getName());
        System.out.println("Cloned: " + cloned.getName() + " - " + cloned.getDepartment().getName());
    }
}

 输出结果与上一个相同:

Original: John - IT
Cloned: John - IT

After modifying cloned object:
Original: John - IT
Cloned: John - HR

浅拷贝与深拷贝的比较

特性浅拷贝深拷贝
基本类型字段复制值复制值
引用类型字段复制引用(共享对象)递归复制对象(不共享对象)
实现复杂度简单(通常只需调用super.clone)复杂(需要处理所有引用类型字段)
性能较高(复制较少)较低(需要复制更多对象)
对象独立性低(引用类型字段会相互影响)高(完全独立)

如何选择拷贝方式

选择浅拷贝还是深拷贝取决于具体需求:

  1. 使用浅拷贝的情况

    • 对象的所有字段都是基本类型

    • 引用类型字段是不可变的(如String)

    • 你确实需要共享某些对象的状态

  2. 使用深拷贝的情况

    • 对象包含可变引用类型字段

    • 你需要完全独立的副本

    • 副本的修改不应影响原始对象

注意事项

  1. clone()方法的限制

    • 需要实现Cloneable接口,否则会抛出CloneNotSupportedException

    • clone()方法是protected的,如果要在其他包中调用,需要重写为public

    • 深拷贝实现需要确保所有相关类都正确实现了clone()

  2. 序列化的限制

    • 所有相关类都必须实现Serializable接口

    • 序列化性能较低,不适合频繁拷贝的场景

    • 某些特殊对象(如线程)无法通过序列化正确拷贝

  3. 循环引用问题

    • 在深拷贝时,如果对象图中有循环引用,需要特别处理,否则可能导致栈溢出

最佳实践

1.考虑使用拷贝构造方法拷贝工厂方法替代clone():

public Employee(Employee other) {
    this.name = other.name;
    this.department = new Department(other.department);
}

2.对于复杂的对象图,考虑使用第三方库如Apache Commons Lang的SerializationUtils.clone():

Employee cloned = SerializationUtils.clone(original);

3.明确文档化你的拷贝行为,让使用者知道是浅拷贝还是深拷贝。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值