请考虑以下代码:
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
?
答案 0 :(得分:574)
创建一个复制构造函数:
class DummyBean {
private String dummy;
public DummyBean(DummyBean another) {
this.dummy = another.dummy; // you can access
}
}
每个对象都有一个克隆方法,可以用来复制对象,但不要使用它。创建类并执行不正确的克隆方法太容易了。如果您打算这样做,请至少阅读Joshua Bloch在 Effective Java 中对此所说的内容。
答案 1 :(得分:381)
基本:用Java复制对象。
让我们假设一个对象 - obj1
,它包含两个对象, containedObj1 和 containedObj2 。
浅层复制:
浅复制创建同一个类的新instance
,并将所有字段复制到新实例并返回它。 对象类提供了clone
方法,并为浅层复制提供支持
深层复制:
当对象与其引用的对象一起复制时,会发生深层复制。下图显示了对其进行深层复制后的obj1
。 不仅已复制obj1
,而且其中包含的对象也已被复制。我们可以使用Java Object Serialization
制作深层副本。不幸的是,这种方法也存在一些问题(detailed examples)。
可能出现的问题:
clone
正确实施很棘手
最好使用Defensive copying,copy constructors(作为@egaga回复)或static factory methods。
clone()
方法,但是你在编译时不知道对象的类型,那么你就有问题了。 Java有一个名为Cloneable
的接口。实际上,如果我们想要创建一个对象Cloneable
,我们应该实现这个接口。 Object.clone
受保护,因此我们必须使用公共方法覆盖,以便可以访问它。clone()
方法也进行深度复制,这对假设来说风险太大。您必须控制所有类中的代码。例如,org.apache.commons.lang.SerializationUtils将使用序列化(Source)的深度克隆方法。如果我们需要克隆Bean,那么org.apache.commons.beanutils(Source)中有几种实用方法。
cloneBean
将根据可用的属性getter和setter克隆bean,即使bean类本身没有实现Cloneable。copyProperties
会将属性值从原始bean复制到目标bean。答案 2 :(得分:109)
在包import org.apache.commons.lang.SerializationUtils;
中有一个方法:
SerializationUtils.clone(Object);
示例:
this.myObjectCloned = SerializationUtils.clone(this.object);
答案 3 :(得分:96)
请按以下步骤操作:
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
答案 4 :(得分:36)
为什么没有使用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;
}
}
这很简单。
编辑:通过递归包含子对象
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;
}
}
答案 5 :(得分:26)
我使用Google的JSON库对其进行序列化,然后创建序列化对象的新实例。它通过一些限制进行深度复制:
不能有任何递归引用
它不会复制不同类型的数组
应该键入数组和列表,否则它将找不到要实例化的类
您可能需要在自己声明的类中封装字符串
我还使用此类来保存用户首选项,窗口以及在运行时重新加载的内容。它非常易于使用和有效。
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;
}
}
答案 6 :(得分:22)
答案 7 :(得分:13)
是。您需要Deep Copy您的对象。
答案 8 :(得分:12)
将Cloneable
及以下代码添加到您的班级
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
使用此clonedObject = (YourClass) yourClassObject.clone();
答案 9 :(得分:10)
如果您最终需要clone()
,这是一个不错的解释......
答案 10 :(得分:9)
这也有效。假设模型
class UserAccount{
public int id;
public String name;
}
首先添加
compile 'com.google.code.gson:gson:2.8.1'
到您的应用> gradle&同步。然后
Gson gson = new Gson();
updateUser = gson.fromJson(gson.toJson(mUser),UserAccount.class);
您可以在访问修饰符后使用transient
关键字排除字段。
注意:这是不好的做法。另外,不建议使用Cloneable
或JavaSerialization
这是缓慢而破碎的。编写复制构造函数以获得最佳性能ref。
像
这样的东西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次迭代的统计数据:
第UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class);
行需要 808毫秒
行UserAccount clone = new UserAccount(aO);
需要少于 1ms
结论:如果你的老板疯了,你更喜欢速度,请使用gson。如果您更喜欢质量,请使用第二个复制构造函数。
您还可以在Android Studio中使用复制构造函数代码generator plugin。
答案 11 :(得分:8)
使用深度克隆实用程序:
SomeObjectType copy = new Cloner().deepClone(someObject);
这将深层复制任何java对象,请在https://github.com/kostaskougios/cloning
查看答案 12 :(得分:7)
深度克隆是您的答案,需要实施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();
答案 13 :(得分:6)
要做到这一点,你必须以某种方式克隆对象。尽管Java具有克隆机制,但如果您不需要,请不要使用它。创建一个复制方法,使复制工作为您,然后执行:
dumtwo = dum.copy();
Here是关于完成副本的不同技术的更多建议。
答案 14 :(得分:5)
除了显式复制之外,另一种方法是使对象不可变(无set
或其他mutator方法)。这样就不会出现问题。对于较大的物体,不变性变得更加困难,但另一方面是它将你推向分裂成连贯的小物体和复合物的方向。
答案 15 :(得分:3)
class DB {
private String dummy;
public DB(DB one) {
this.dummy = one.dummy;
}
}
答案 16 :(得分:2)
您可以使用来自http://x-stream.github.io/的XStream自动进行深度复制:
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));
有了这个,您就可以获得一个副本而无需实现任何克隆接口。
答案 17 :(得分:2)
传递要复制的对象并获取所需的对象:
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;
}
现在将objDest
解析为所需的对象。
快乐的编码!
答案 18 :(得分:2)
public class MyClass implements Cloneable {
private boolean myField= false;
// and other fields or objects
public MyClass (){}
@Override
public MyClass clone() throws CloneNotSupportedException {
try
{
MyClass clonedMyClass = (MyClass)super.clone();
// if you have custom object, then you need create a new one in here
return clonedMyClass ;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return new MyClass();
}
}
}
并在您的代码中:
MyClass myClass = new MyClass();
// do some work with this object
MyClass clonedMyClass = myClass.clone();
答案 19 :(得分:2)
使用gson
复制对象。
public static <T>T copyObject(Object object){
Gson gson = new Gson();
JsonObject jsonObject = gson.toJsonTree(object).getAsJsonObject();
return gson.fromJson(jsonObject,(Type) object.getClass());
}
假设我有一个对象person
。所以
Person copyPerson = copyObject(person);
注意:性能要慢得多。
答案 20 :(得分:1)
您可以尝试实施Cloneable
并使用clone()
方法;但是,如果您使用克隆方法,您应该 - 通过标准 - 始终覆盖Object
的{{1}}方法。
答案 21 :(得分:1)
如果您可以为源文件添加注释,可以使用注释处理器或代码生成器,如this one。
import net.zerobuilder.BeanBuilder
@BeanBuilder
public class DummyBean {
// bean stuff
}
将生成一个类DummyBeanBuilders
,它有一个静态方法dummyBeanUpdater
来创建浅拷贝,就像你手动操作一样。
DummyBean bean = new DummyBean();
// Call some setters ...
// Now make a copy
DummyBean copy = DummyBeanBuilders.dummyBeanUpdater(bean).done();
答案 22 :(得分:1)
替代egaga's复制的构造方法。您可能已经有一个POJO,所以只需添加另一个方法copy()
,该方法将返回已初始化对象的副本。
class DummyBean {
private String dummyStr;
private int dummyInt;
public DummyBean(String dummyStr, int dummyInt) {
this.dummyStr = dummyStr;
this.dummyInt = dummyInt;
}
public DummyBean copy() {
return new DummyBean(dummyStr, dummyInt);
}
//... Getters & Setters
}
如果您已经有DummyBean
并想要一份副本:
DummyBean bean1 = new DummyBean("peet", 2);
DummyBean bean2 = bean1.copy(); // <-- Create copy of bean1
System.out.println("bean1: " + bean1.getDummyStr() + " " + bean1.getDummyInt());
System.out.println("bean2: " + bean2.getDummyStr() + " " + bean2.getDummyInt());
//Change bean1
bean1.setDummyStr("koos");
bean1.setDummyInt(88);
System.out.println("bean1: " + bean1.getDummyStr() + " " + bean1.getDummyInt());
System.out.println("bean2: " + bean2.getDummyStr() + " " + bean2.getDummyInt());
输出:
bean1: peet 2 bean2: peet 2 bean1: koos 88 bean2: peet 2
但是两者都很好,最终取决于您...
答案 23 :(得分:0)
fun <T : Any?> T.duplicate(): T? {
var copyObject: T? = null
try {
val byteArrayOutputStream = ByteArrayOutputStream()
val objectOutputStream = ObjectOutputStream(byteArrayOutputStream)
objectOutputStream.writeObject(this)
objectOutputStream.flush()
objectOutputStream.close()
byteArrayOutputStream.close()
val byteData = byteArrayOutputStream.toByteArray()
val byteArrayInputStream = ByteArrayInputStream(byteData)
try {
copyObject = ObjectInputStream(byteArrayInputStream).readObject() as T
} catch (e: ClassNotFoundException) {
e.printStackTrace()
}
} catch (e: IOException) {
e.printStackTrace()
}
return copyObject
}
var object = Any()
var duplicateObject = object.duplicate()
<T extends Object> T copyObject(T sourceObject) {
T copyObject = null;
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(sourceObject);
objectOutputStream.flush();
objectOutputStream.close();
byteArrayOutputStream.close();
byte[] byteData = byteArrayOutputStream.toByteArray();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteData);
try {
copyObject = (T) new ObjectInputStream(byteArrayInputStream).readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return copyObject;
}
Object object = new Object();
Object duplicateObject = copyObject(object);