你如何在Java中做一个对象的深层拷贝?
在java中,实现深层对象复制function有点困难。 你采取了哪些步骤来确保原始对象和克隆的对象不共享?
一个安全的方法是序列化对象,然后反序列化。 这确保了一切都是一个全新的参考。
这里有一篇关于如何有效地做到这一点的文章 。
注意事项:类可能会覆盖序列化,因此不会创build新的实例,例如单例。 当然,如果你的类不是可序列化的,这当然是行不通的。
有几个人提到使用或重写Object.clone()
。 不要这样做。 Object.clone()
有一些主要的问题,在大多数情况下,它的使用是不鼓励的。 请参阅Joshua Bloch的“ Effective Java ”中的第11项,作为完整答案。 我相信你可以在原始types数组上安全地使用Object.clone()
,但除此之外,你需要明智的使用和覆盖克隆。
依赖于序列化(XML或其他)的scheme很笨拙。
这里没有简单的答案。 如果要深度复制对象,则必须遍历对象图并通过对象的复制构造函数或静态工厂方法显式复制每个子对象,然后再深层复制子对象。 不可变(例如String
)不需要被复制。 顺便说一下,你应该为此支持不变性。
您可以使用序列化进行深层复制而不创build文件。
您希望深度复制的对象需要implement serializable
。 如果该类不是最终的或者不能被修改的,则扩展该类并实现可序列化。
将您的类转换为字节stream:
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(object); oos.flush(); oos.close(); bos.close(); byte[] byteData = bos.toByteArray();
从字节stream中恢复你的类:
ByteArrayInputStream bais = new ByteArrayInputStream(byteData); (Object) object = (Object) new ObjectInputStream(bais).readObject();
您可以使用Commons Lang中的org.apache.commons.lang3.SerializationUtils.clone(T)
来执行基于序列化的深度克隆,但要小心 – 性能是糟糕的。
通常,最好的做法是为需要克隆的对象图中的每个对象类编写自己的克隆方法。
实现深层复制的一种方法是将复制构造函数添加到每个关联的类。 复制构造函数将“this”的实例作为其单个参数,并从中复制所有值。 相当一些工作,但非常简单和安全。
编辑:请注意,您不需要使用访问器方法来读取字段。 您可以直接访问所有字段,因为源实例始终与具有复制构造函数的实例具有相同的types。 明显但可能被忽视。
例:
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; } }
编辑:请注意,使用复制构造函数时,您需要知道您正在复制的对象的运行时types。 用上面的方法,你不能轻易地复制一个混合列表(你可以用一些reflection代码来完成)。
Apache commons提供了一种快速克隆对象的方法。
My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
XStream在这种情况下非常有用。 这是一个简单的克隆代码
private static final XStream XSTREAM = new XStream(); ... Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
您可以使用一个具有简单API 的库 ,并使用reflection执行相对快速的克隆(应该比序列化方法快)。
Cloner cloner = new Cloner(); MyClass clone = cloner.deepClone(o); // clone is a deep-clone of o
一个非常简单和简单的方法是使用Jackson JSON将复杂的Java对象序列化为JSON并将其读回。
使用XStream( http://x-stream.github.io/ )。 您甚至可以控制哪些属性可以通过注释忽略,或者显式指定XStream类的属性名称。 而且你不需要实现可复制的接口。
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)); } }
只有在每个class级的同意下才能进行深度复制。 如果你能控制类层次结构,那么你可以实现可克隆接口并实现Clone方法。 否则,执行深度复制是不可能的,因为对象也可能共享非数据资源(例如数据库连接)。 但是,通常情况下,深度复制在Java环境中被认为是不好的做法,应该通过适当的devise实践来避免。
我用Dozer来克隆java对象,这非常棒, Kryo库是另一个很好的select。
BeanUtils在克隆bean方面做得非常好。
BeanUtils.cloneBean(obj);
对于Spring Framework用户。 使用class org.springframework.util.SerializationUtils
:
@SuppressWarnings("unchecked") public static <T extends Serializable> T clone(T object) { return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object)); }
1)
public static Object deepClone(Object object) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(object); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); } catch (Exception e) { e.printStackTrace(); return null; } } 2) // (1) create a MyPerson object named Al MyAddress address = new MyAddress("Vishrantwadi ", "Pune", "India"); MyPerson al = new MyPerson("Al", "Arun", address); // (2) make a deep clone of Al MyPerson neighbor = (MyPerson)deepClone(al);
这里你的MyPerson和MyAddress类必须实现可串行接口
对于复杂的对象,当性能不显着时,我使用json库,比如gson将对象序列化为json文本,然后反序列化文本以获取新对象。
基于reflection的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); }