题
在java中实现深度对象复制功能有点困难。您采取哪些步骤来确保原始对象和克隆对象不共享引用?
解决方案
一种安全的方法是序列化对象,然后反序列化。这确保一切都是全新的参考。
这是一篇文章 关于如何有效地做到这一点。
注意事项:类可以覆盖序列化,以便新实例 不是 创建,例如对于单身人士。如果您的类不可序列化,这当然不起作用。
其他提示
有几个人提到使用或覆盖 Object.clone()
. 。不要这样做。 Object.clone()
存在一些重大问题,并且在大多数情况下不鼓励使用它。请参阅第 11 条,来自“有效的Java”约书亚·布洛赫(Joshua Bloch)的完整答案。相信您可以放心使用 Object.clone()
在原始类型数组上,但除此之外,您还需要明智地正确使用和覆盖克隆。
依赖序列化(XML 或其他方式)的方案很混乱。
这里没有简单的答案。如果要深度复制一个对象,则必须遍历对象图并通过对象的复制构造函数或静态工厂方法显式复制每个子对象,然后再深度复制子对象。不可变的(例如 String
s) 不需要复制。顺便说一句,出于这个原因,你应该支持不变性。
您可以通过序列化进行深层复制,而无需创建文件。
您想要深度复制的对象需要 implement 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 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;
}
}
编辑:请注意,使用复制构造函数时,您需要知道要复制的对象的运行时类型。使用上述方法,您无法轻松复制混合列表(您也许可以使用一些反射代码来做到这一点)。
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 对象序列化为 JSON 并将其读回。
使用 XStream(http://x-stream.github.io/)。您甚至可以通过注释或显式指定 XStream 类的属性名称来控制可以忽略哪些属性。此外,您不需要实现可克隆接口。
为了 Spring框架 用户。使用类 org.springframework.util.SerializationUtils
:
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
深度复制只有在每个类都同意的情况下才能进行。如果您可以控制类层次结构,那么您可以实现可克隆接口并实现 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));
}
}
对于复杂的对象并且当性能不重要时,我使用 json 库,例如 格森将对象序列化为 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);
}
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 类必须实现可序列化接口
BeanUtils 深度克隆 beans 做得非常好。
BeanUtils.cloneBean(obj);