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


当前回答

对于复杂的对象,当性能不重要时,我使用json库,如gson 要将对象序列化为json文本,然后反序列化文本以获得新对象。

gson,基于反射将工作在大多数情况下,除了瞬态字段将不会被复制和对象的循环引用与原因StackOverflowError。

public static <T> T copy(T anObject, Class<T> classInfo) {
    Gson gson = new GsonBuilder().create();
    String text = gson.toJson(anObject);
    T newObject = gson.fromJson(text, classInfo);
    return newObject;
}
public static void main(String[] args) {
    String originalObject = "hello";
    String copiedObject = copy(originalObject, String.class);
}

其他回答

你可以在Apache Commons Lang中使用org.apache.commons.lang3.SerializationUtils.clone(T)来做一个基于序列化的深度克隆,但是要小心——性能非常糟糕。

一般来说,最好的做法是为需要克隆的对象图中对象的每个类编写自己的克隆方法。

使用XStream (http://x-stream.github.io/)。您甚至可以通过注释或显式地指定XStream类的属性名来控制可以忽略哪些属性。此外,您不需要实现可克隆的接口。

使用Jackson序列化和反序列化对象。此实现不需要对象实现Serializable类。

  <T> T clone(T object, Class<T> clazzType) throws IOException {

    final ObjectMapper objMapper = new ObjectMapper();
    String jsonStr= objMapper.writeValueAsString(object);

    return objMapper.readValue(jsonStr, clazzType);

  }

一个非常快速和简单的一行程序解决方案是使用Jackson。

看一下示例代码片段:

ObjectMapper objectMapper = new ObjectMapper();

MyClass deepCopyObject = objectMapper
  .readValue(objectMapper.writeValueAsString(originalObject), MyClass.class);

在上面的例子中:“MyClass”指的是你想要复制的对象的类。

解释:我们只是试图将原始对象序列化为字符串,然后将字符串反序列化回对象,从而获得一个深度副本。 了解更多关于ObjectMapper的信息,请访问:https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/ObjectMapper.html

实现深度复制的一种方法是向每个相关类添加复制构造函数。复制构造函数将'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实例,除非您显式地解决这个问题。可以使用反射在实参的运行时类型中查找复制构造函数。但是我建议不要走这条路,如果需要以一般的方式讨论继承,就寻找另一种解决方案。