实现深度对象复制函数有点困难。您采取什么步骤来确保原始对象和克隆对象没有共享引用?


当前回答

您可以使用具有简单API的库,并使用反射执行相对快速的克隆(应该比序列化方法快)。

Cloner cloner = new Cloner();

MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o

其他回答

实现深度复制的一种方法是向每个相关类添加复制构造函数。复制构造函数将'this'的实例作为其单个参数,并从该实例复制所有值。工作量很大,但很简单也很安全。

编辑:请注意,您不需要使用访问器方法来读取字段。您可以直接访问所有字段,因为源实例总是与具有复制构造函数的实例具有相同的类型。显而易见,但可能会被忽视。

例子:

public class Order {

    private long number;

    public Order() {
    }

    /**
     * Copy constructor
     */
    public Order(Order source) {
        number = source.number;
    }
}


public class Customer {

    private String name;
    private List<Order> orders = new ArrayList<Order>();

    public Customer() {
    }

    /**
     * Copy constructor
     */
    public Customer(Customer source) {
        name = source.name;
        for (Order sourceOrder : source.orders) {
            orders.add(new Order(sourceOrder));
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

编辑:注意,复制构造函数不考虑继承。例如:如果您将OnlineOrder (Order的子类)传递给一个复制构造函数,则将在副本中创建一个常规的Order实例,除非您显式地解决这个问题。可以使用反射在实参的运行时类型中查找复制构造函数。但是我建议不要走这条路,如果需要以一般的方式讨论继承,就寻找另一种解决方案。

1)

public static Object deepClone(Object object) {
   try {
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     ObjectOutputStream oos = new ObjectOutputStream(baos);
     oos.writeObject(object);
     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
     ObjectInputStream ois = new ObjectInputStream(bais);
     return ois.readObject();
   }
   catch (Exception e) {
     e.printStackTrace();
     return null;
   }
 }

2)

    // (1) create a MyPerson object named Al
    MyAddress address = new MyAddress("Vishrantwadi ", "Pune", "India");
    MyPerson al = new MyPerson("Al", "Arun", address);

    // (2) make a deep clone of Al
    MyPerson neighbor = (MyPerson)deepClone(al);

这里,您的MyPerson和MyAddress类必须实现可序列化接口

下面是一种通用的深度克隆方法,使用字节数组流进行对象序列化和反序列化(以避免写入文件)。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepClone(T t) {
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);) {
        oos.writeObject(t);
        byte[] bytes = baos.toByteArray();
        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
            return (T) ois.readObject();
        }
    } catch (IOException | ClassNotFoundException e) {
        throw new RuntimeException(e);
    }
}

这是一个关于如何深度克隆任何对象的简单示例: 首先实现serializable

public class CSVTable implements Serializable{
    Table<Integer, Integer, String> table; 
    public CSVTable() {
        this.table = HashBasedTable.create();
    }
    
    public CSVTable deepClone() {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            return (CSVTable) ois.readObject();
        } catch (IOException e) {
            return null;
        } catch (ClassNotFoundException e) {
            return null;
        }
    }

}

然后

CSVTable table = new CSVTable();
CSVTable tempTable = table.deepClone();

才能得到克隆体。

一个安全的方法是序列化对象,然后反序列化。这确保了所有内容都是全新的参考。

这里有一篇关于如何有效地做到这一点的文章。

注意:类可以重写序列化,这样就不会创建新的实例,例如单例。当然,如果你的类不是序列化的,这也行不通。