如何制作对象的深度副本?

ev7lccsx  于 2021-06-30  发布在  Java
关注(0)|答案(20)|浏览(518)

实现深度对象复制功能有点困难。您采取了哪些步骤来确保原始对象和克隆对象不共享引用?

rjee0c15

rjee0c151#

对于spring框架用户。使用类 org.springframework.util.SerializationUtils :

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
     return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
w51jfk4q

w51jfk4q2#

我用dozer克隆java对象,这很好,kryo库是另一个很好的选择。

slmsl1lt

slmsl1lt3#

深度复制只能在每个班级同意的情况下进行。如果可以控制类层次结构,则可以实现clonable接口和clone方法。否则,由于对象也可能共享非数据资源(例如数据库连接),因此无法安全地执行深度复制。但是,在java环境中,深度复制通常被认为是不好的做法,应该通过适当的设计实践来避免。

nzrxty8p

nzrxty8p4#

apachecommons提供了一种深度克隆对象的快速方法。

My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
nkkqxpd9

nkkqxpd95#

实现深度复制的一种方法是向每个关联的类添加复制构造函数。复制构造函数将“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;
    }
}

编辑:请注意,当使用复制构造函数时,您需要知道要复制的对象的运行时类型。使用上述方法,您无法轻松地复制混合列表(您可以使用一些反射代码来实现这一点)。

lsmd5eda

lsmd5eda6#

小豆子在克隆豆子方面做得很好。

BeanUtils.cloneBean(obj);
ou6hu8tu

ou6hu8tu7#

xstream在这种情况下非常有用。下面是一个简单的克隆代码

private static final XStream XSTREAM = new XStream();
...

Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
mnowg1ta

mnowg1ta8#

对于复杂的对象,当性能不显著时,我使用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);
}
gzjq41n4

gzjq41n49#

下面是一个通用的深度克隆方法,它使用对象序列化和字节数组流的反序列化(以避免写入文件)。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepClone(T t) {
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);) {
        oos.writeObject(t);
        byte[] bytes = baos.toByteArray();
        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
            return (T) ois.readObject();
        }
    } catch (IOException | ClassNotFoundException e) {
        throw new RuntimeException(e);
    }
}
brgchamk

brgchamk10#

使用xstream(http://x-stream.github.io/). 甚至可以通过注解或显式指定xstream类的属性名来控制可以忽略哪些属性。此外,您不需要实现可关闭的接口。

qojgxg4l

qojgxg4l11#

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类必须实现serializable接口

a5g8bdjr

a5g8bdjr12#

您可以使用具有简单api的库,并使用反射执行相对快速的克隆(应该比序列化方法快)。

Cloner cloner = new Cloner();

MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
gajydyqb

gajydyqb13#

一种非常简单的方法是使用jacksonjson将复杂的java对象序列化为json并读回。
从https://github.com/fasterxml/jackson-databind/#5-分钟教程流解析生成器:

JsonFactory f = mapper.getFactory(); // may alternatively construct directly too

// First: write simple JSON output
File jsonFile = new File("test.json");
JsonGenerator g = f.createGenerator(jsonFile);
// write JSON: { "message" : "Hello world!" }
g.writeStartObject();
g.writeStringField("message", "Hello world!");
g.writeEndObject();
g.close();

// Second: read file back
JsonParser p = f.createParser(jsonFile);

JsonToken t = p.nextToken(); // Should be JsonToken.START_OBJECT
t = p.nextToken(); // JsonToken.FIELD_NAME
if ((t != JsonToken.FIELD_NAME) || !"message".equals(p.getCurrentName())) {
   // handle error
}
t = p.nextToken();
if (t != JsonToken.VALUE_STRING) {
   // similarly
}
String msg = p.getText();
System.out.printf("My message to you is: %s!\n", msg);
p.close();
0yycz8jy

0yycz8jy14#

使用jackson序列化和反序列化对象。此实现不需要对象来实现可序列化类。

<T> T clone(T object, Class<T> clazzType) throws IOException {

    final ObjectMapper objMapper = new ObjectMapper();
    String jsonStr= objMapper.writeValueAsString(object);

    return objMapper.readValue(jsonStr, clazzType);

  }
hwazgwia

hwazgwia15#

有几个人提到使用或重写 Object.clone() . 别这么做。 Object.clone() 有一些主要问题,在大多数情况下不鼓励使用。请参阅joshua bloch的“effective java”中的第11项,以获得完整的答案。我相信你可以安全使用 Object.clone() 但除此之外,您还需要明智地正确使用和重写克隆。
依赖于序列化(xml或其他)的方案是笨拙的。
这里没有简单的答案。如果要深度复制对象,则必须遍历对象图并通过对象的复制构造函数或静态工厂方法显式复制每个子对象,而静态工厂方法反过来又深度复制子对象。不可变(例如。 String s) 不需要复制。另外,出于这个原因,您应该支持不变性。

相关问题