在java中,实现深层对象复制功能有点困难.您采取了哪些步骤来确保原始对象和克隆的对象没有共享?
一种安全的方法是序列化对象,然后反序列化.这确保了一切都是全新的参考.
这是一篇关于如何有效地做到这一点的文章.
注意事项:类可以覆盖序列化,以便不创建新实例,例如单个实例.如果您的类不是Serializable,当然这也行不通.
有些人提到过使用或覆盖Object.clone()
.不要这样做.Object.clone()
有一些重大问题,在大多数情况下不鼓励使用它.请参阅Joshua Bloch的" Effective Java "中的第11项,以获得完整的答案.我相信你可以安全地使用Object.clone()
原始类型的数组,但除此之外,你需要明智地正确使用和重写克隆.
依赖于序列化(XML或其他)的方案是kludgy.
这里没有简单的答案.如果要深度复制对象,则必须遍历对象图并通过对象的复制构造函数或静态工厂方法显式复制每个子对象,而静态工厂方法又会深层复制子对象.不可变的(例如String
s)不需要复制.顺便说一句,出于这个原因,你应该支持不变性.
您可以使用序列化进行深层复制而无需创建文件.
您希望深层复制的对象需要implement serializable
.如果该类不是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 = (Object) new ObjectInputStream(bais).readObject();
您可以org.apache.commons.lang3.SerializationUtils.clone(T)
在Apache Commons Lang中使用基于序列化的深度克隆,但要小心 - 性能非常糟糕.
通常,最佳做法是在需要克隆的对象图中为对象的每个类编写自己的克隆方法.
实现深层复制的一种方法是将复制构造函数添加到每个关联的类.复制构造函数将"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 Listorders = new ArrayList (); 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; } }
编辑:请注意,使用复制构造函数时,您需要知道要复制的对象的运行时类型.使用上述方法,您无法轻松复制混合列表(您可以使用某些反射代码进行复制).
Apache commons提供了一种快速克隆对象的方法.
My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
您可以使用具有简单API 的库,并使用反射执行相对快速的克隆(应该比序列化方法更快).
Cloner cloner = new Cloner(); MyClass clone = cloner.deepClone(o); // clone is a deep-clone of o
XStream在这种情况下非常有用.这是一个简单的代码来进行克隆
private static final XStream XSTREAM = new XStream(); ... Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
一种非常简单和简单的方法是使用Jackson JSON将复杂的Java Object序列化为JSON并将其读回.
http://wiki.fasterxml.com/JacksonInFiveMinutes
对于复杂的对象,当性能不重要时,我使用json库,如gson 将对象序列化为json文本,然后反序列化文本以获取新对象.
基于反射的gson在大多数情况下都可以工作,除了transient
不会复制字段和带有循环引用的对象StackOverflowError
.
public staticT copy(T anObject, Class 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); }
使用XStream(http://x-stream.github.io/).您甚至可以通过注释控制可以忽略的属性,或者显式指定XStream类的属性名称.此外,您不需要实现可克隆的接口.
对于Spring Framework用户.使用课程org.springframework.util.SerializationUtils
:
@SuppressWarnings("unchecked") public staticT clone(T object) { return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object)); }
深度复制只能在每个班级的同意下完成.如果您可以控制类层次结构,则可以实现clonable接口并实现Clone方法.否则,执行深层复制是不可能安全的,因为该对象也可能共享非数据资源(例如数据库连接).一般而言,深度复制在Java环境中被认为是不好的做法,应该通过适当的设计实践来避免.
import com.thoughtworks.xstream.XStream; public class deepCopy { private static XStream xstream = new XStream(); //serialize with Xstream them deserialize ... public static Object deepCopy(Object obj){ return xstream.fromXML(xstream.toXML(obj)); } }