如何在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
,它包含两个对象, 包含 obj1
和containedObj2 。
浅拷贝:
浅拷贝创build相同类的新实例,并将所有字段复制到新实例并返回。 对象类提供了一种clone
方法,并为浅拷贝提供支持。
深拷贝:
当一个对象与它引用的对象一起复制时,会发生深度复制。 在下面的图像显示obj1
深层复制后已经执行。 obj1
不仅被复制 ,而且其中包含的对象也被复制。 我们可以使用Java Object Serialization
来进行深层复制。 不幸的是,这种方法也有一些问题( 详细的例子 )。
可能的问题:
clone
很难正确实施。
最好使用防御性复制 , 复制构造函数 (如@egaga回复)或静态工厂方法 。
- 如果你有一个对象,你知道有一个公共的
clone()
方法,但是你不知道在编译时对象的types,那么你有问题。 Java有一个名为Cloneable
的接口。 在实践中,我们应该实现这个接口,如果我们想要一个对象Cloneable
。Object.clone
是受保护的 ,所以我们必须用一个公共方法来覆盖它,以便它可以被访问。 - 当我们尝试复制一个复杂对象时,会出现另一个问题。 假设所有成员对象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议使用Cloneable
或JavaSerialization
这是缓慢和破碎。 写复制构造函数以获得最佳性能
就像是
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方法,你应该 – 通过标准 – 总是重写Object
的public 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();