JAVA -- cópia profunda do objeto

Herdar a interface Cloneable e reimplementar o método clone

A classe de entidade precisa herdar a interface Cloneable e reimplementar o método clone() .
Deve-se observar que os elementos do tipo ponteiro referenciados na classe de entidade também herdam a interface Cloneable e reimplementam o método clone() ou criam um novo objeto e o atribuem ao elemento.

@Data
public class CloneableEntity implements Cloneable{
    
    
    private String stringValue;
    private PointerEntity pointerEntity;
    private List<String> listValue=  new ArrayList<>();

    //填充列表 用来比较克隆效率
    public void initListValue() {
    
    
        for (int i = 0; i < 10000; i++) {
    
    
            this.listValue.add(UUID.randomUUID().toString());
        }
    }

    @Override
    public CloneableEntity clone() throws CloneNotSupportedException {
    
    
        CloneableEntity clone = (CloneableEntity)super.clone();
		// 指针类型的元素要做clone一份新的
		if(null!=this.pointerEntity) {
    
    
        	clone.setPointerEntity(this.pointerEntity.clone());
        }
        // 创建一个新的对象给元素
        clone.setListValue(new ArrayList<>(listValue));
        return clone;
    }
}
@Data
public class PointerEntity implements Cloneable{
    
    
    private String uuid;

    public PointerEntity(){
    
    
    }

    public PointerEntity(String uuid){
    
    
        this.uuid = uuid;
    }

    @Override
    protected PointerEntity clone() throws CloneNotSupportedException {
    
    
        return (PointerEntity)super.clone();
    }
}

clonar instância

Qualquer modificação nos elementos do novo objeto clonado não afetará o valor dos elementos no objeto original.

Gson gson = new Gson();//用来打印对象
//        Cloneable 深拷贝
CloneableEntity entity1 = new CloneableEntity();
entity1.setStringValue("1");
entity1.setPointerEntity(new PointerEntity(UUID.randomUUID().toString()));
entity1.setListValue(new ArrayList<String>(){
    
    {
    
    add("1");add("2");}});
CloneableEntity clone1 = entity1.clone();
//对克隆后的对象进行修改
clone1.setStringValue("1--1");
clone1.getListValue().add("3");
clone1.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity1: "+gson.toJson(entity1));
System.out.println("clone1: "+gson.toJson(clone1));
-------------------------------
entity1: {
    
    "stringValue":"1","pointerEntity":{
    
    "uuid":"27bb47c6-1ad3-4b79-a408-509f2163b5fa"},"listValue":["1","2"]}
clone1: {
    
    "stringValue":"1--1","pointerEntity":{
    
    "uuid":"a3495d1d-e32c-4dee-be57-0a8a3f30678c"},"listValue":["1","2","3"]}

Serialização e desserialização usando streams

A classe clonada deve oferecer suporte à serialização e os elementos da classe também devem oferecer suporte à serialização. Caso contrário, uma exceção que não oferece suporte à serialização aparecerá durante a clonagem.

@Data
public class Serialzable2Entity implements Serializable {
    
    
    private String stringValue;
    private SerialzablePointerEntity pointerEntity;
    private List<String> listValue=  new ArrayList<>();

    //填充列表 用来比较克隆效率
    public void initListValue() {
    
    
        for (int i = 0; i < 10000; i++) {
    
    
            this.listValue.add(UUID.randomUUID().toString());
        }
    }
}
@Data
public class SerialzablePointerEntity implements Serializable {
    
    
    private String uuid;
    public SerialzablePointerEntity(){
    
    
    }

    public SerialzablePointerEntity(String uuid){
    
    
        this.uuid = uuid;
    }

}

clonar instância

Gson gson = new Gson();//用来打印对象
//流深拷贝
Serialzable2Entity entity4 = new Serialzable2Entity();
entity4.setStringValue("41");
entity4.setPointerEntity(new SerialzablePointerEntity(UUID.randomUUID().toString()));
entity4.setListValue(new ArrayList<String>(){
    
    {
    
    add("41");add("42");}});
Serialzable2Entity clone4 ;
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
  ObjectOutputStream oos = new ObjectOutputStream(bos);
) {
    
    
 oos.writeObject(entity4);
 oos.flush();
 try (
      ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
      ObjectInputStream ois = new ObjectInputStream(bis);
 ) {
    
    
     clone4 = (Serialzable2Entity) ois.readObject();
 }
}
clone4.setStringValue("41--1");
clone4.getListValue().add("43");
clone4.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity4: " + gson.toJson(entity4));
System.out.println("clone4: " + gson.toJson(clone4));
---------------------------------------
entity4: {
    
    "stringValue":"41","pointerEntity":{
    
    "uuid":"af3c287f-1289-4fb3-947f-f5b7684ef07e"},"listValue":["41","42"]}
clone4: {
    
    "stringValue":"41--1","pointerEntity":{
    
    "uuid":"0549e071-d7df-469f-baae-578e9aa89194"},"listValue":["41","42","43"]}

Usando o Serializador Apache Commons Lang

O Apache Commons Lang vem com uma classe de ferramenta de serialização SerializationUtils , na qual o método clone() nos permite fazer uma cópia profunda do objeto.
Na verdade, seu princípio é o mesmo da serialização e desserialização do stream acima, exceto que há mais conversões de tipo e outras operações nele. Portanto,
também requer que a classe clonada ofereça suporte à serialização e os elementos da classe também ofereçam suporte à serialização. Caso contrário, uma exceção que não oferece suporte à serialização aparecerá durante a clonagem.

clonar instância

Gson gson = new Gson();//用来打印对象
// Apache Commons Lang序列化深拷贝 拷贝对象和对象中所有涉及到的指针类型的对象都要实现序列化
Serialzable2Entity entity2 = new Serialzable2Entity();
entity2.setStringValue("11");
entity2.setPointerEntity(new SerialzablePointerEntity(UUID.randomUUID().toString()));
entity2.setListValue(new ArrayList<String>(){
    
    {
    
    add("11");add("12");}});
Serialzable2Entity clone2 = SerializationUtils.clone(entity2);
//对克隆后的对象进行修改
clone2.setStringValue("11--1");
clone2.getListValue().add("13--1");
clone2.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity2: "+gson.toJson(entity2));
System.out.println("clone2: "+gson.toJson(clone2));
------------------------------------------
entity2: {
    
    "stringValue":"11","pointerEntity":{
    
    "uuid":"303e0bff-ad81-4b56-a1db-311cf894b189"},"listValue":["11","12"]}
clone2: {
    
    "stringValue":"11--1","pointerEntity":{
    
    "uuid":"bb42fd5b-d0ce-483a-9f34-d32e1b0eab5c"},"listValue":["11","12","13--1"]}

Use Gson para serializar e desserializar

Use serialização e desserialização Gson para clonar, e o objeto clonado não precisa de nenhuma extensão.

@Data
public class NormalEntity{
    
    
    private String stringValue;
    private NormalPointerEntity pointerEntity;
    private List<String> listValue=  new ArrayList<>();

    /**
     * 初始化列表
     */
    public void initListValue() {
    
    
        for (int i = 0; i < 10000; i++) {
    
    
            this.listValue.add(UUID.randomUUID().toString());
        }
    }
}

@Data
public class NormalPointerEntity{
    
    
    private String uuid;

    public NormalPointerEntity(){
    
    
    }

    public NormalPointerEntity(String uuid){
    
    
        this.uuid = uuid;
    }
}
Gson gson = new Gson();
// Gson 序列化反序列化深拷贝 对对象没有要求
NormalEntity entity3 = new NormalEntity();
entity3.setStringValue("21");
entity3.setPointerEntity(new NormalPointerEntity(UUID.randomUUID().toString()));
entity3.setListValue(new ArrayList<String>(){
    
    {
    
    add("21");add("22");}});
NormalEntity clone3 = gson.fromJson(gson.toJson(entity3 ), NormalEntity.class);
//修改克隆后的对象
clone3.setStringValue("21--1");
clone3.getListValue().add("23");
clone3.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity3: "+gson.toJson(entity3));
System.out.println("clone3: "+gson.toJson(clone3));
---------------------------
entity3: {
    
    "stringValue":"21","pointerEntity":{
    
    "uuid":"b81d7ee9-54db-4d51-b09d-0a61adce4518"},"listValue":["21","22"]}
clone3: {
    
    "stringValue":"21--1","pointerEntity":{
    
    "uuid":"276de4f8-0b23-4435-9225-dfc836311064"},"listValue":["21","22","23"]}

Compare os prós e contras

Em primeiro lugar, comparamos a eficiência


StopWatch stopWatch = new StopWatch();
//        Cloneable 深拷贝
CloneableEntity entity1 = new CloneableEntity();
entity1.initListValue();
stopWatch.start("Cloneable 深拷贝");
CloneableEntity clone1 = entity1.clone();
stopWatch.stop();

//        流深拷贝
Serialzable2Entity entity4 = new Serialzable2Entity();
entity4.initListValue();
Serialzable2Entity clone4 ;
stopWatch.start(" 流深拷贝");
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
     ObjectOutputStream oos = new ObjectOutputStream(bos);
) {
    
    
    oos.writeObject(entity4);
    oos.flush();
    try (
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
    ) {
    
    
        clone4 = (Serialzable2Entity) ois.readObject();
    }
}
stopWatch.stop();

//        Apache Commons Lang序列化深拷贝 拷贝对象和对象中所有涉及到的指针类型的对象都要实现序列化
Serialzable2Entity entity2 = new Serialzable2Entity();
entity2.initListValue();
stopWatch.start("Apache Commons Lang序列化深拷贝");
Serialzable2Entity clone2 = SerializationUtils.clone(entity2);
stopWatch.stop();

//        Gson 序列化反序列化深拷贝 对对象没有要求
NormalEntity entity3 = new NormalEntity();
entity3.initListValue();
stopWatch.start(" Gson 序列化反序列化深拷贝");
Gson gson = new Gson();//用来打印对象
NormalEntity clone3 = gson.fromJson(gson.toJson(entity3 ), NormalEntity.class);
stopWatch.stop();

System.out.println(stopWatch.prettyPrint());
-----------------------------------
---------------------------------------------
ns         %     Task name
---------------------------------------------
000029100  000%  Cloneable 深拷贝
048922700  023%  流深拷贝
017047500  008%  Apache Commons Lang序列化深拷贝
150727000  070%  Gson 序列化反序列化深拷贝

De acordo com o consumo de tempo, pode-se observar que a clonagem por herança da interface Cloneable é a mais eficiente, seguida pela serialização de stream e SerializationUtils , sendo a serialização Gson a mais lenta.
Deve-se observar que a eficiência da cópia profunda de streaming e do SerializationUtils deve ser semelhante. Porque essencialmente todos os fluxos são usados ​​para serialização e desserialização. No entanto, como uma parte do tempo é consumida quando a classe IO é carregada pela primeira vez após o programa ser iniciado, ao executar a clonagem SerializationUtils , o consumo de tempo é menor que a serialização do fluxo.
Quando trocamos a ordem de execução de serialização de fluxo e SerializationUtils , descobriremos que o tempo consumido pela serialização de fluxo é menor que o de SerializationUtils .

Resumo das vantagens e desvantagens

método Vantagem desvantagem
Herdar a interface Cloneable Alta eficiência, sem necessidade de consultar outros pacotes jar e baixa sobrecarga do sistema A classe clonada deve reescrever a interface Cloneable, optar por clonar manualmente os elementos de ponteiro na classe e, quando os campos da classe forem atualizados, também precisamos atualizar o método de clonagem a tempo de evitar falhas de cópia profunda
serialização de fluxo Não há necessidade de se referir a outros pacotes jar e não há necessidade de ajustar o código para adicionar e modificar campos A eficiência é média, a classe deve oferecer suporte à serialização e o processo de serialização e desserialização tem uma grande sobrecarga do sistema
SerializationUtils.clone() O código é conciso e fácil de usar, adicionar e modificar campos não precisa ajustar o código A eficiência é média, o pacote apache common jar precisa ser introduzido, a classe deve suportar a serialização e o processo de serialização e desserialização tem uma grande sobrecarga do sistema
Serialização Gson Não requer nenhum pré-processamento da classe A eficiência é muito baixa, o pacote jar precisa ser introduzido e o processo de serialização e desserialização tem uma grande sobrecarga do sistema

Acho que você gosta

Origin blog.csdn.net/qq_40096897/article/details/130700014
Recomendado
Clasificación