如何在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 复制参考 。 那么,有什么办法可以创build“dum”的新副本并将其分配给“dumtwo”?

创build一个拷贝构造函数:

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

每个对象也有一个克隆方法可以用来复制对象,但不要使用它。 创build一个类和做不正确的克隆方法太容易了。 如果你打算这样做的话,至less应该读一下Joshua Bloch在Effective Java中所说的。

基本: Java中的对象复制。

让我们假设一个object- obj1 ,它包含两个对象, 包含 obj1containedObj2
在这里输入图像描述

浅拷贝:
浅拷贝创build相同类的新实例,并将所有字段复制到新实例并返回。 对象类提供了一种clone方法,并为浅拷贝提供支持。
在这里输入图像描述

深拷贝:
一个对象与它引用的对象一起复制时,会发生深度复制。 在下面的图像显示obj1深层复制后已经执行。 obj1不仅被复制 ,而且其中包含的对象也被复制。 我们可以使用Java Object Serialization来进行深层复制。 不幸的是,这种方法也有一些问题( 详细的例子 )。
在这里输入图像描述

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

  1. 如果你有一个对象,你知道有一个公共的clone()方法,但是你不知道在编译时对象的types,那么你有问题。 Java有一个名为Cloneable的接口。 在实践中,我们应该实现这个接口,如果我们想要一个对象CloneableObject.clone受保护的 ,所以我们必须用一个公共方法来覆盖它,以便它可以被访问。
  2. 当我们尝试复制一个复杂对象时,会出现另一个问题。 假设所有成员对象variables的clone()方法也做了深度拷贝,这太冒险了。 你必须控制所有类的代码。

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

  • cloneBean会根据可用的属性getter和setter克隆一个bean,即使这个bean类本身没有实现Cloneable。
  • 对于属性名称相同的所有情况, 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); 

为什么使用Reflection 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; } } 

这很简单。

编辑:通过recursion包含子对象

 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; } } 

是的,你只是提到对象的引用。 如果实现Cloneable则可以克隆该对象。

看看这篇关于复制对象的wiki文章。

请参阅此处:对象复制

我使用Google的JSON库对其进行序列化,然后创build一个序列化对象的新实例。 它具有一些限制的深层复制:

  • 不能有任何recursion引用

  • 它不会复制不同types的数组

  • 数组和列表应该被键入,否则它不会find要实例化的类

  • 你可能需要在你自己声明的类中封装string

我也使用这个类来保存用户的偏好,窗口和什么都不要在运行时重新加载。 这是非常容易使用和有效的。

 import com.google.gson.*; public class SerialUtils { //___________________________________________________________________________________ public static String serializeObject(Object o) { Gson gson = new Gson(); String serializedObject = gson.toJson(o); return serializedObject; } //___________________________________________________________________________________ public static Object unserializeObject(String s, Object o){ Gson gson = new Gson(); Object object = gson.fromJson(s, o.getClass()); return object; } //___________________________________________________________________________________ public static Object cloneObject(Object o){ String s = serializeObject(o); Object object = unserializeObject(s,o); return object; } } 

是。 您需要深度复制您的对象。

Cloneable和下面的代码添加到您的类中

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

使用这个clonedObject = (YourClass) yourClassObject.clone();

要做到这一点,你必须以某种方式克隆对象。 虽然Java有一个克隆机制,但是如果你不需要的话,不要使用它。 创build一个复制方法,为您做复制工作,然后执行:

 dumtwo = dum.copy(); 

以下是关于完成副本的不同技巧的更多build议。

如果你最终需要它,这里有一个clone()的体面的解释。

这里:clone(Java方法)

使用深层克隆工具:

 SomeObjectType copy = new Cloner().deepClone(someObject); 

这将深入复制任何Java对象,请查看https://github.com/kostaskougios/cloning

除了明确复制,另一种方法是使对象不可变(没有set或其他增变器方法)。 这样,问题就不会出现。 较大物体的不可变性变得更加困难,但是另一方面,它会推动你分裂成连贯的小物体和复合物。

深入克隆是您的答案,它需要实现Cloneable接口并重写clone()方法。

 public class DummyBean implements Cloneable { private String dummy; public void setDummy(String dummy) { this.dummy = dummy; } public String getDummy() { return dummy; } @Override public Object clone() throws CloneNotSupportedException { DummyBean cloned = (DummyBean)super.clone(); cloned.setDummy(cloned.getDummy()); // the above is applicable in case of primitive member types, // however, in case of non primitive types // cloned.setNonPrimitiveType(cloned.getNonPrimitiveType().clone()); return cloned; } } 

你会这样称呼它DummyBean dumtwo = dum.clone();

这也起作用。 假设模型

 class UserAccount{ public int id; public String name; } 

首先将compile 'com.google.code.gson:gson:2.8.1'到您的应用> gradle&sync。 然后

 Gson gson = new Gson(); updateUser = gson.fromJson(gson.toJson(mUser),UserAccount.class); 

访问修饰符后,可以使用transient关键字排除字段。

注意:这是不好的做法。 也不build议使用CloneableJavaSerialization这是缓慢和破碎。 写复制构造函数以获得最佳性能

就像是

 class UserAccount{ public int id; public String name; //empty constructor public UserAccount(){} //parameterize constructor public UserAccount(int id, String name) { this.id = id; this.name = name; } //copy constructor public UserAccount(UserAccount in){ this(in.id,in.name); } } 

90000次迭代testing统计:
Line UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class); 需要808ms

Line UserAccount clone = new UserAccount(aO); 需要不到1ms

结论:如果你的老板疯了,而且你更喜欢速度,那就用gson吧。 如果你喜欢质量,使用第二个拷贝构造函数。

您也可以在Android Studio中使用复制构造函数代码生成器插件 。

 class DB { private String dummy; public DB(DB one) { this.dummy = one.dummy; } } 

您可以使用XStream从http://x-stream.github.io/自动深度复制:;

XStream是一个将对象序列化为XML并返回的简单库。

将它添加到你的项目(如果使用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)); 

有了这个,你有一个副本,而不需要实现任何克隆界面。

通过你想复制的对象,并获得你想要的对象,

 private Object copyObject(Object objSource) { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(objSource); oos.flush(); oos.close(); bos.close(); byte[] byteData = bos.toByteArray(); ByteArrayInputStream bais = new ByteArrayInputStream(byteData); try { objDest = new ObjectInputStream(bais).readObject(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } return objDest; } 

现在parsingobjDest去欺骗对象。

快乐的编码

您可以尝试实现Cloneable并使用clone()方法; 然而,如果你使用clone方法,你应该 – 通过标准 – 总是重写Objectpublic Object clone()方法。

如果您可以将注释添加到源文件,则可以使用像这样的注释处理器或代码生成器。

 import net.zerobuilder.BeanBuilder @BeanBuilder public class DummyBean { // bean stuff } 

将生成一个类DummyBeanBuilders ,它具有一个静态方法dummyBeanUpdater来创build浅拷贝,就像手动操作一样。

 DummyBean bean = new DummyBean(); // Call some setters ... // Now make a copy DummyBean copy = DummyBeanBuilders.dummyBeanUpdater(bean).done();