如何在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

1427663 次浏览

为此,您必须以某种方式克隆对象。虽然Java有克隆机制,但如果没有必要,请不要使用它。创建一个为您执行复制工作的复制方法,然后执行:

dumtwo = dum.copy();

这里是关于完成副本的不同技术的更多建议。

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

这里:克隆(Java方法)

创建一个复制构造函数:

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

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

是的,您只是在引用该对象。如果对象实现了Cloneable,您可以克隆该对象。

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

参考这里:对象复制

除了显式复制之外,另一种方法是使对象不可变(没有set或其他修改器方法)。通过这种方式,问题永远不会出现。对于较大的对象,不可变性变得更加困难,但另一方面,它将你推向分裂成连贯的小对象和复合对象的方向。

您可以尝试实现Cloneable并使用clone()方法;但是,如果您使用clone方法,您应该-按照标准-始终覆盖Objectpublic Object clone()方法。

只需按照以下方式操作:

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

您可以使用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));

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

使用深度克隆实用程序:

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

这将深度复制任何java对象,在https://github.com/kostaskougios/cloning处检查它

基础:对象复制Java。

让我们假设一个对象-obj1,包含两个对象,包含对象包含对象
在此处输入图片描述

浅层复制:
浅复制创建同一个类的新instance,并将所有字段复制到新实例并返回它。对象类提供了clone方法并为浅复制提供支持。
在此处输入图片描述

深度复制:
一个对象与它所引用的对象一起被复制时发生深度复制。下图显示obj1在对其执行深度复制后。不仅#0被复制,但其中包含的对象也已被复制。我们可以使用Java Object Serialization进行深度复制。不幸的是,这种方法也有一些问题(详细的例子)。
在此处输入图片描述

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

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

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

  • cloneBean将根据可用的属性getter和setter克隆bean,即使bean类本身没有实现Clonable。
  • 对于属性名称相同的所有情况,copyProperties将属性值从源bean复制到目标bean。

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

SerializationUtils.clone(Object);

示例:

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

我使用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;}}

Cloneable及以下代码添加到您的类中

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

使用此clonedObject = (YourClass) yourClassObject.clone();

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

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

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

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

为什么没有使用反射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;}}

传递你想要复制的对象并获取你想要的对象:

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解析为所需的对象。

编码快乐!

这个也行假设模特

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关键字来排除使用字段。

备注:这是不好的做法。也不建议使用CloneableJavaSerialization它既慢又坏。编写复制构造函数以获得最佳性能参考

就像

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

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

UserAccount clone = new UserAccount(aO);取小于1ms

结论:如果你的老板疯了,你更喜欢速度,请使用gson。如果你更喜欢质量,请使用第二个复制构造函数。

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

如果您可以向源文件添加注释,则可以使用注释处理器或代码生成器(如这一个)。

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

将生成一个类DummyBeanBuilders,它有一个静态方法dummyBeanUpdater来创建浅拷贝,与您手动执行的方式相同。

DummyBean bean = new DummyBean();// Call some setters ...// Now make a copyDummyBean copy = DummyBeanBuilders.dummyBeanUpdater(bean).done();
public class MyClass implements Cloneable {
private boolean myField= false;// and other fields or objects
public MyClass (){}
@Overridepublic MyClass clone() throws CloneNotSupportedException {try{MyClass clonedMyClass = (MyClass)super.clone();// if you have custom object, then you need create a new one in herereturn clonedMyClass ;} catch (CloneNotSupportedException e) {e.printStackTrace();return new MyClass();}
}}

在你的代码中:

MyClass myClass = new MyClass();// do some work with this objectMyClass clonedMyClass = myClass.clone();

复制的egaga的构造函数方法的替代品。您可能已经有了一个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 bean1bean1.setDummyStr("koos");bean1.setDummyInt(88);
System.out.println("bean1: " + bean1.getDummyStr() + " " + bean1.getDummyInt());System.out.println("bean2: " + bean2.getDummyStr() + " " + bean2.getDummyInt());

输出:

bean1: peet 2bean2: peet 2
bean1: koos 88bean2: peet 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);

备注:性能要慢得多。

使用静态编程语言扩展函数

fun <T : Any?> T.duplicate(): T? {var copyObject: T? = nulltry {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()

Java

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

==============================================

静态编程语言更新

如果您使用数据类,那么您将拥有复制静态编程语言数据类的复制方法。很酷的是,您还可以传递一些值来使用新副本修改对象。我会推荐这种方式。

示例:

//类

data class TestModel(val title: String, var subtitle: String)

用例

val testClass = TestModel("Test title", "Test subtitle")
val newInstance = testClass.copy(subtitle = "new subtitle for copy instance")