实现一个深度对象复制函数有一些困难。 你需要采取哪些步骤来确保原始对象和克隆对象没有共享引用?
一种安全的方法是将对象序列化,然后反序列化。这可以确保所有内容都是全新的引用。
这里有一篇文章介绍如何高效地实现此操作。
注意:某些类可能会覆盖序列化,使得不会创建新实例,例如对于单例。另外,如果您的类不可序列化,则当然无法使用此方法。
Object.clone()
。不要这样做。 Object.clone()
存在一些主要问题,在大多数情况下不建议使用。请参见Joshua Bloch的《Effective Java》中的第11项,以获得完整答案。我相信您可以安全地在原始类型数组上使用Object.clone()
,但除此之外,您需要审慎地正确使用和覆盖clone。String
)。顺便说一句,出于这个原因,您应该倾向于使用不可变性。Object.clone()
? 请在回答中至少添加一个简短的解释,不需要购买书籍。 - jazzpi您可以通过序列化而不创建文件来进行深度复制。
您希望进行深层复制的对象将需要实现可序列化接口
。如果该类不是final或无法修改,则可以扩展该类并实现serializable接口。
将您的类转换为一系列字节:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();
从字节流中恢复您的类:
ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
Object object = new ObjectInputStream(bais).readObject();
instance
呢? - FaridObjectOutputStream.writeObject()
对于大对象来说非常慢,不幸的是。 - Happyorg.apache.commons.lang3.SerializationUtils.clone(T)
进行基于序列化的深度克隆,但要小心——性能极差。org.apache.commons.lang.SerializationUtils
中找到。 - Pino实现深拷贝的一种方法是为每个相关类添加复制构造函数。复制构造函数以“this”实例作为其唯一参数,并从中复制所有值。这需要一些工作,但非常直观和安全。
注意:您不需要使用访问器方法来读取字段。您可以直接访问所有字段,因为源实例始终与具有复制构造函数的实例相同类型。显而易见,但可能会被忽视。
示例:
public class Order {
private long number;
public Order() {
}
/**
* Copy constructor
*/
public Order(Order source) {
number = source.number;
}
}
public class Customer {
private String name;
private List<Order> orders = new ArrayList<Order>();
public Customer() {
}
/**
* Copy constructor
*/
public Customer(Customer source) {
name = source.name;
for (Order sourceOrder : source.orders) {
orders.add(new Order(sourceOrder));
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
编辑:请注意,复制构造函数不考虑继承。例如:如果您将OnlineOrder(Order的子类)传递给复制构造函数,则在副本中将创建常规Order实例,除非您明确解决此问题。您可以使用反射在参数的运行时类型中查找复制构造函数。但我建议如果需要通用地涵盖继承,请不要走这条路并寻找另一种解决方案。
Toyota
,您的代码将在目标列表中放置一个Car
。适当的克隆通常要求该类提供一个虚拟工厂方法,其合同规定它将返回自己类的新对象;复制构造函数本身应该是protected
,以确保它只用于构造其精确类型与被复制对象相匹配的对象。 - supercat你可以使用一个库,它具有简单的 API,并使用反射执行相对快速的克隆(应该比序列化方法更快)。
Cloner cloner = new Cloner();
MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
Apache Commons提供了一种快速深度克隆对象的方法。
My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
对于使用Spring Framework的用户,可以使用org.springframework.util.SerializationUtils
类:
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
对于复杂对象且性能不是很重要的情况下,我会使用一个json库,比如gson将对象序列化为json文本,然后再反序列化该文本以获取新对象。
基于反射的gson在大多数情况下都可以正常工作,但transient
字段将不会被复制,并且具有循环引用的对象将导致StackOverflowError
。
public static <T> T copy(T anObject, Class<T> classInfo) {
Gson gson = new GsonBuilder().create();
String text = gson.toJson(anObject);
T newObject = gson.fromJson(text, classInfo);
return newObject;
}
public static void main(String[] args) {
String originalObject = "hello";
String copiedObject = copy(originalObject, String.class);
}
private static final XStream XSTREAM = new XStream();
...
Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));