如何在 Java 中复制对象?

2022-08-31 04:01:38

请考虑以下代码:

DummyBean dum = new DummyBean();
dum.setDummy("foo");
System.out.println(dum.getDummy()); // prints 'foo'

DummyBean dumtwo = dum;
System.out.println(dumtwo.getDummy()); // prints 'foo'

dum.setDummy("bar");
System.out.println(dumtwo.getDummy()); // prints 'bar' but it should print 'foo'

因此,我想在不影响 .但是上面的代码没有这样做。当我在 中改变某些东西时,同样的变化也发生在 中。dumdumtwodumdumtwodumdumtwo

我想,当我说,Java只复制引用。那么,有没有办法创建一个新的副本并将其分配给?dumtwo = dumdumdumtwo


答案 1

创建复制构造函数:

class DummyBean {
  private String dummy;

  public DummyBean(DummyBean another) {
    this.dummy = another.dummy; // you can access  
  }
}

每个对象还有一个克隆方法,可用于复制对象,但不要使用它。创建类并执行不正确的克隆方法太容易了。如果你打算这样做,至少要阅读Joshua Bloch在 Effective Java 中对它的看法。


答案 2

基本:Java 中的对象复制。

让我们假设一个 object- ,它包含两个对象,containObj1containedObj2obj1
enter image description here

浅层复制:
浅层复制创建同一类的新字段,并将所有字段复制到新实例并返回。对象类提供方法并为浅层复制提供支持。instanceclone
enter image description here

深度复制:
对象与其引用的对象一起复制时,将发生深度复制。下图显示了在其上执行深度复制后。不仅 obj1 被复制了,其中包含的对象也被复制了。我们可以用它来制作深度复制。不幸的是,这种方法也有一些问题(详细的例子)。obj1Java Object Serialization
enter image description here

可能的问题:
正确实现很棘手。
最好使用防御性复制复制构造函数(如@egaga回复)或静态工厂方法clone

  1. 如果你有一个对象,你知道有一个公共方法,但你不知道编译时对象的类型,那么你就有问题了。Java 有一个名为 的接口。在实践中,如果我们想做一个对象,我们应该实现这个接口。 受保护,因此我们必须使用公共方法覆盖它,以便可以访问它。clone()CloneableCloneableObject.clone
  2. 当我们尝试对复杂对象进行深度复制时,会出现另一个问题。假设所有成员对象变量的方法也做深度复制,这是一个太大的假设风险。您必须控制所有类中的代码。clone()

例如,org.apache.commons.lang.SerializationUtils将有使用序列化(Source)进行深度克隆的方法。如果我们需要克隆Bean,那么org.apache.commons.beanutilsSource)中有几个实用程序方法。

  • cloneBean将基于可用的属性 getters 和 setters 克隆 Bean,即使 Bean 类本身没有实现 Cloneable。
  • copyProperties将属性值从源 Bean 复制到目标 Bean,适用于所有属性名称相同的情况。