java - java如何复制一个对象?

  显示原文与译文双语对照的内容

考虑以下代码:


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'

所以,我想将'dum'复制到 dumtwo,并且我想改变'dum'而不影响'dumtwo'。 但是上面的代码并没有这么做。 在'dum'中更改某些内容时,'dumtwo'也发生了同样的变化。

我想,当我说 dumtwo = dum时,Java只复制引用。 那么,是否有办法创建一个新的'dum'副本并将它的分配给'dumtwo'?

时间:

创建一个复制构造函数:


class DummyBean {
 private String dummy;

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

每个对象都有一个可以用于复制对象的克隆方法,但不使用它。 创建类和做不正确的克隆方法太容易了。 如果你要这样做,至少读约书亚·布洛赫所说在 Effective Java

基本:在Java中复制对象。

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

浅复制:
浅表复制创建一个新的类的instance 并将所有字段复制到新实例并返回它。 对象类提供了一个 clone 方法并提供了对浅表复制的支持。
enter image description here

深度复制:
深复制发生在复制对象以及对象,它涉及 。 下图显示了在对它的执行深层复制后的obj1 。 不仅复制了 obj1,还复制了其中包含的对象。 我们可以使用 Java Object Serialization 进行深层复制。 不幸的是,这个方法有一些问题 too( detailed examples ) 。
enter image description here

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

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

例如 org.apache.commons.lang.SerializationUtils 将使用 serialization( Source ) 进行深层克隆。 如果我们需要克隆 Bean,那么 org.apache.commons.beanutils ( ) 中有几个工具方法。

  • cloneBean 将基于可用的属性getter和setter克隆一个 bean,即使bean类本身没有实现可以克隆。
  • copyProperties 将在属性名称相同的情况下将属性值从原始bean复制到目标 bean 。

如下所示:


public class Deletable implements Cloneable{

 private String str;
 public Deletable(){
 }
 public void setStr(String str){
 this.str = str;
 }
 public void display(){
 System.out.println("The String is"+str);
 }
 protected Object clone() throws CloneNotSupportedException {
 return super.clone();
 }
}

无论你想获取其他对象,简单执行克隆。 例如


Deletable del = new Deletable();
Deletable delTemp = (Deletable ) del.clone();//this line will return you an independent
//object, the changes made to this object will
//not be reflected to other object

在软件包中 import org.apache.commons.lang.SerializationUtils; 有一个方法:


SerializationUtils.clone(Object);

例如:


this.myObjectCloned = SerializationUtils.clone(this.object);

是的,你只是在对对象进行引用。 如果对象实现了 Cloneable,你可以克隆它。

查看关于复制对象的维基文章。

http://en.wikipedia.org/wiki/Object_copy#Copying_in_Java

为什么没有使用反射API的答案?


private static Object cloneObject(Object obj){
 try{
 Object clone = obj.getClass().newInstance();
 for (Field field : obj.getClass().getDeclaredFields()) {
 field.setAccessible(true);
 field.set(clone, field.get(obj));
 }
 return clone;
 }catch(Exception e){
 return null;
 }
 }

非常简单。

编辑:通过递归包含子对象


private static Object cloneObject(Object obj){
 try{
 Object clone = obj.getClass().newInstance();
 for (Field field : obj.getClass().getDeclaredFields()) {
 field.setAccessible(true);
 if(field.get(obj) == null || Modifier.isFinal(field.getModifiers())){
 continue;
 }
 if(field.getType().isPrimitive() || field.getType().equals(String.class)
 || field.getType().getSuperclass().equals(Number.class)
 || field.getType().equals(Boolean.class)){
 field.set(clone, field.get(obj));
 }else{
 Object childObj = field.get(obj);
 if(childObj == obj){
 field.set(clone, clone);
 }else{
 field.set(clone, cloneObject(field.get(obj)));
 }
 }
 }
 return clone;
 }catch(Exception e){
 return null;
 }
 }

要做到这一点,你必须以某种方式克隆对象。 尽管Java有一个克隆机制,但如果你不需要。 创建复制工作的副本方法,然后执行以下操作:

 
dumtwo = dum.copy();

 

这里的是关于完成复制的不同技巧的更多建议。

Cloneable 和下面代码添加到你的类


public Object clone() throws CloneNotSupportedException {
 return super.clone();
 }

使用这里选项 clonedObject = (YourClass) yourClassObject.clone();

你可以使用XStream自动复制。

将它添加到项目中( 如果使用 Maven )


<dependency>
 <groupId>com.thoughtworks.xstream</groupId>
 <artifactId>xstream</artifactId>
 <version>1.3.1</version> 
</dependency>

然后


DummyBean dum = new DummyBean();
dum.setDummy("foo");
DummyBean dumCopy = (DummyBean) XSTREAM.fromXML(XSTREAM.toXML(dum));

有了这个,你就有了一个不需要实现任何克隆接口的副本

...