实现深度对象复制函数有点困难。您采取什么步骤来确保原始对象和克隆对象没有共享引用?
当前回答
实现深度复制的一种方法是向每个相关类添加复制构造函数。复制构造函数将'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实例,除非您显式地解决这个问题。可以使用反射在实参的运行时类型中查找复制构造函数。但是我建议不要走这条路,如果需要以一般的方式讨论继承,就寻找另一种解决方案。
其他回答
一些人提到了使用或重写Object.clone()。不要这样做。Object.clone()存在一些主要问题,在大多数情况下不鼓励使用它。请参见Joshua Bloch的“Effective Java”中的第11项,以获得完整的答案。我相信您可以安全地在基本类型数组上使用Object.clone(),但除此之外,您还需要明智地正确使用和重写clone。
依赖于序列化(XML或其他方式)的方案是拙劣的。
这个问题没有简单的答案。如果你想要深度复制一个对象,你必须遍历对象图,并通过对象的复制构造函数或静态工厂方法显式复制每个子对象,然后再深度复制子对象。不需要复制不可变对象(例如字符串)。顺便说一句,出于这个原因,您应该支持不可变性。
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类必须实现可序列化接口
一个非常快速和简单的一行程序解决方案是使用Jackson。
看一下示例代码片段:
ObjectMapper objectMapper = new ObjectMapper();
MyClass deepCopyObject = objectMapper
.readValue(objectMapper.writeValueAsString(originalObject), MyClass.class);
在上面的例子中:“MyClass”指的是你想要复制的对象的类。
解释:我们只是试图将原始对象序列化为字符串,然后将字符串反序列化回对象,从而获得一个深度副本。 了解更多关于ObjectMapper的信息,请访问:https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/ObjectMapper.html
Apache commons提供了一种快速的深度克隆对象的方法。
My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
实现深度复制的一种方法是向每个相关类添加复制构造函数。复制构造函数将'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实例,除非您显式地解决这个问题。可以使用反射在实参的运行时类型中查找复制构造函数。但是我建议不要走这条路,如果需要以一般的方式讨论继承,就寻找另一种解决方案。
推荐文章
- 使用Enum实现单例(Java)
- RabbitMQ与通道和连接之间的关系
- buildSessionFactory()配置方法在Hibernate中已弃用?
- Spring MVC -如何获得所有的请求参数在一个地图在Spring控制器?
- 如何在Java中按两个字段排序?
- 文件之间的差异。路径中的分隔符和斜杠
- 在方法参数中使用NotNull注释
- Spring MVC中处理可选参数的@RequestParam
- Tomcat:如何查找正在运行的Tomcat版本?
- “java”、“javaw”和“javaws”之间有什么区别?
- 将Date对象转换为日历对象
- 在Java中保存最后N个元素的大小有限的队列
- 如何运行一个类从Jar不是主类在其清单文件
- 使arrayList.toArray()返回更具体的类型
- 在Eclipse中粘贴多行Java字符串