Copy java object details and instances

  • 2020-07-21 08:07:24
  • OfStack

Copy java object details and instances

Java assignment is a copy object reference, if we want to get a copy of an object, we cannot use the assignment operation:


@Test
public void testassign(){
 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");

 Person p2=p1;
 System.out.println(p1==p2);//true
}

If you create a new copy of an object, that is, their original state is exactly the same, but you can change their state later without affecting each other, you need to use a copy of the object in java, such as the native clone() method.

How to do object cloning

The Object object has an clone() method, which implements the replication of various attributes in the object, but its visibility scope is protected, so the entity class USES the premise of cloning:

Implementation of Cloneable interface, which is a tag interface, its own no method.

Overrides the clone() method and increases visibility to public.


@Data
public class Person implements Cloneable {
  private String name;
  private Integer age;
  private Address address;
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

@Test
public void testShallowCopy() throws Exception{
 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");

 Person p2=(Person) p1.clone();
 System.out.println(p1==p2);//false
 p2.setName("Jacky");
 System.out.println("p1="+p1);//p1=Person [name=Peter, age=31]
 System.out.println("p2="+p2);//p2=Person [name=Jacky, age=31]
}

The test case has only two basic types of members, and the test does its job.

It doesn't seem that simple. Add 1 member of Address class to Person:


@Data
public class Address {
  private String type;
  private String value;
}

Let's test it again. Here's the problem.


@Test
public void testShallowCopy() throws Exception{
 Address address=new Address();
 address.setType("Home");
 address.setValue(" Beijing ");

 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");
 p1.setAddress(address);

 Person p2=(Person) p1.clone();
 System.out.println(p1==p2);//false

 p2.getAddress().setType("Office");
 System.out.println("p1="+p1);
 System.out.println("p2="+p2);
}

View the output:


false
p1=Person(name=Peter, age=31, address=Address(type=Office, value= Beijing ))
p2=Person(name=Peter, age=31, address=Address(type=Office, value= Beijing ))

We ran into some trouble and only changed the address type of p2, both of which became Office.

Shallow copy and deep copy

The previous example is a typical use case of shallow copy and deep copy.

Shallow copy: All value attributes of the copied object contain the same as the original object, and all object reference attributes still point to the original object.

Deep copy: Based on the shallow copy, all variables referring to other objects are clone and point to new objects that have been copied.

That is, the default clone() method implementation mechanism is still an assignment.

If one of the copied properties is a primitive type, you only need to implement the cloneable mechanism of the current class, which is a shallow copy.

If the property of the copied object contains references to other entity class objects, these entity class objects need to implement the cloneable interface and override the clone() method.


@Data
public class Address implements Cloneable {
  private String type;
  private String value;

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

This is not enough; clone() of Person needs to explicitly clone its reference member.


@Data
public class Person implements Cloneable {
  private String name;
  private Integer age;
  private Address address;
  @Override
  protected Object clone() throws CloneNotSupportedException {
    Object obj=super.clone();
    Address a=((Person)obj).getAddress();
    ((Person)obj).setAddress((Address) a.clone());
    return obj;
  }
}

Rerun the previous test case:


false
p1=Person(name=Peter, age=31, address=Address(type=Home, value= Beijing ))
p2=Person(name=Peter, age=31, address=Address(type=Office, value= Beijing ))

clone mode deep copy summary

If you have a non-native member, such as a member of a custom object, then you need:

The member implements the Cloneable interface and overrides the clone() method, and don't forget to elevate to public visibility.
At the same time, modify the clone() method of the copied class to add the cloning logic for the member.

If the copied object does not directly inherit from Object, and there are other inheritance levels in the middle, each layer of the super class needs to implement the Cloneable interface and override the clone() method.

Unlike object members, clone in an inheritance relationship does not require clone() of the copied class to do any extra work.

In a word, if a full deep copy is implemented, it is necessary to implement the cloning mechanism for every object in the inheritance chain and reference chain of the copied object.

The previous instance is acceptable, but if you have N object members and an M layer inheritance relationship, you're going to be in trouble.

Implement deep copy with serialization

The clone mechanism is not strongly typed; for example, the implementation of Cloneable does not force objects on the inheritance chain to be implemented; Nor is it mandatory to override the clone() method. Therefore, it is easy to ignore one of the links in the coding process, and it is difficult to check the complex projects.

Serialization is one way to find a reliable and simple method.

1. Each object in the inheritance chain and reference chain of the copied object implements java. io. Serializable interface. This one is relatively simple, does not need to implement any methods, serialVersionID requirements are not mandatory, and is fine for deep copy.

2. Implement your own deepClone method. Write this into the stream and read it out. Commonly known as: freeze-thaw.


@Data
public class Person implements Serializable {
  private String name;
  private Integer age;
  private Address address;
  public Person deepClone() {
    Person p2=null;
    Person p1=this;
    PipedOutputStream out=new PipedOutputStream();
    PipedInputStream in=new PipedInputStream();
    try {
      in.connect(out);
    } catch (IOException e) {
      e.printStackTrace();
    }

    try(ObjectOutputStream bo=new ObjectOutputStream(out);
        ObjectInputStream bi=new ObjectInputStream(in);) {
      bo.writeObject(p1);
      p2=(Person) bi.readObject();

    } catch (Exception e) {
      e.printStackTrace();
    }
    return p2;
  }
}

Prototype factory class

To facilitate testing and save space, encapsulate a factory class.

To be fair, avoid using caching mechanisms in some tool libraries and use prototype factories instead.


public class PersonFactory{
  public static Person newPrototypeInstance(){
    Address address = new Address();
    address.setType("Home");
    address.setValue(" Beijing ");

    Person p1 = new Person();
    p1.setAddress(address);
    p1.setAge(31);
    p1.setName("Peter");
    return p1;
  }
}

Copy objects using Dozer

Dozer is an Bean processing class library.

maven rely on


@Data
public class Person implements Cloneable {
  private String name;
  private Integer age;
  private Address address;
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

@Test
public void testShallowCopy() throws Exception{
 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");

 Person p2=(Person) p1.clone();
 System.out.println(p1==p2);//false
 p2.setName("Jacky");
 System.out.println("p1="+p1);//p1=Person [name=Peter, age=31]
 System.out.println("p2="+p2);//p2=Person [name=Jacky, age=31]
}

0

Test cases:


@Data
public class Person {
  private String name;
  private Integer age;
  private Address address;

  @Test
  public void testDozer() {
  Person p1=PersonFactory.newPrototypeInstance();
    Mapper mapper = new DozerBeanMapper();
    Person p2 = mapper.map(p1, Person.class);
    p2.getAddress().setType("Office");
    System.out.println("p1=" + p1);
    System.out.println("p2=" + p2);
  }
}

@Data
public class Address {
  private String type;
  private String value;
}

Output:


p1=Person(name=Peter, age=31, address=Address(type=Home, value= Beijing ))
p2=Person(name=Peter, age=31, address=Address(type=Office, value= Beijing ))

Note: There was a serious problem with dozer in 10,000 tests. If the DozerBeanMapper object was created in the for loop, the efficiency (dozer:7358) was reduced by nearly 10 times. Since DozerBeanMapper is thread-safe, you should not create a new instance every time. The singleton factory DozerBeanMapperSingletonWrapper can be shipped to create mapper or integrated into spring.

Even more violent, create an People class:


@Data
public class Person implements Cloneable {
  private String name;
  private Integer age;
  private Address address;
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

@Test
public void testShallowCopy() throws Exception{
 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");

 Person p2=(Person) p1.clone();
 System.out.println(p1==p2);//false
 p2.setName("Jacky");
 System.out.println("p1="+p1);//p1=Person [name=Peter, age=31]
 System.out.println("p2="+p2);//p2=Person [name=Jacky, age=31]
}

3

As long as the property name is the same, dry ~

Continue to ravage:


@Data
public class Person implements Cloneable {
  private String name;
  private Integer age;
  private Address address;
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

@Test
public void testShallowCopy() throws Exception{
 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");

 Person p2=(Person) p1.clone();
 System.out.println(p1==p2);//false
 p2.setName("Jacky");
 System.out.println("p1="+p1);//p1=Person [name=Peter, age=31]
 System.out.println("p2="+p2);//p2=Person [name=Jacky, age=31]
}

4

Copy objects using ES163en-BeanUtils

maven rely on


@Data
public class Person implements Cloneable {
  private String name;
  private Integer age;
  private Address address;
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

@Test
public void testShallowCopy() throws Exception{
 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");

 Person p2=(Person) p1.clone();
 System.out.println(p1==p2);//false
 p2.setName("Jacky");
 System.out.println("p1="+p1);//p1=Person [name=Peter, age=31]
 System.out.println("p2="+p2);//p2=Person [name=Jacky, age=31]
}

5

Test cases:


@Data
public class Person implements Cloneable {
  private String name;
  private Integer age;
  private Address address;
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

@Test
public void testShallowCopy() throws Exception{
 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");

 Person p2=(Person) p1.clone();
 System.out.println(p1==p2);//false
 p2.setName("Jacky");
 System.out.println("p1="+p1);//p1=Person [name=Peter, age=31]
 System.out.println("p2="+p2);//p2=Person [name=Jacky, age=31]
}

6

Copy objects using cglib

maven depends on:


<dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib</artifactId>
 <version>3.2.4</version>
</dependency>

Test cases:


@Test
public void testCglib(){
 Person p1=PersonFactory.newPrototypeInstance();
 BeanCopier beanCopier=BeanCopier.create(Person.class, Person.class, false);
 Person p2=new Person();
 beanCopier.copy(p1, p2,null);
 p2.getAddress().setType("Office");
 System.out.println("p1=" + p1);
 System.out.println("p2=" + p2);
}

Surprisingly, cglib was a shallow copy of x. But cglib offers extended capabilities:


@Data
public class Person implements Cloneable {
  private String name;
  private Integer age;
  private Address address;
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

@Test
public void testShallowCopy() throws Exception{
 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");

 Person p2=(Person) p1.clone();
 System.out.println(p1==p2);//false
 p2.setName("Jacky");
 System.out.println("p1="+p1);//p1=Person [name=Peter, age=31]
 System.out.println("p2="+p2);//p2=Person [name=Jacky, age=31]
}

9

Orika copies objects

The role of orika is not only to handle bean copies, but also to be better at converting between types.

maven depends on:


<dependency>
 <groupId>ma.glasnost.orika</groupId>
 <artifactId>orika-core</artifactId>
 <version>1.5.0</version>
</dependency>
</dependencies>

Test cases:


@Test
public void testOrika() {
 MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

 mapperFactory.classMap(Person.class, Person.class)
 .byDefault()
 .register();
 ConverterFactory converterFactory = mapperFactory.getConverterFactory();
 MapperFacade mapper = mapperFactory.getMapperFacade();

 Person p1=PersonFactory.newPrototypeInstance();
 Person p2 = mapper.map(p1, Person.class);
 System.out.println("p1=" + p1);
 p2.getAddress().setType("Office");
 System.out.println("p2=" + p2);
}

Spring BeanUtils copy object

Give Spring a face, it doesn't seem to support deep copy.


Person p1=PersonFactory.newPrototypeInstance();
Person p2 = new Person();
Person p2 = (Person) BeanUtils.cloneBean(p1);
//BeanUtils.copyProperties(p2, p1);// This is even less likely 

Deep copy performance comparison


@Test
public void testBatchDozer(){
 Long start=System.currentTimeMillis();
 Mapper mapper = new DozerBeanMapper();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  Person p2 = mapper.map(p1, Person.class);
 }
 System.out.println("dozer:"+(System.currentTimeMillis()-start));
 //dozer:721
}
@Test
public void testBatchBeanUtils(){
 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  try {
   Person p2=(Person) BeanUtils.cloneBean(p1);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 System.out.println("commons-beanutils:"+(System.currentTimeMillis()-start));
 //commons-beanutils : 229
}
@Test
public void testBatchCglib(){
 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  BeanCopier beanCopier=BeanCopier.create(Person.class, Person.class, true);
  Person p2=new Person();
  beanCopier.copy(p1, p2, new Converter(){
   @Override
   public Object convert(Object value, Class target, Object context) {
    if(target.isSynthetic()){
     BeanCopier.create(target, target, true).copy(value, value, this);
    }
    return value;
   }
  });
 }
 System.out.println("cglib:"+(System.currentTimeMillis()-start));
 //cglib:133
}
@Test
public void testBatchSerial(){
 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  Person p2=p1.deepClone();
 }
 System.out.println("serializable:"+(System.currentTimeMillis()-start));
 //serializable:687
}
@Test
public void testBatchOrika() {
 MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

 mapperFactory.classMap(Person.class, Person.class)
 .field("name", "name")
 .byDefault()
 .register();
 ConverterFactory converterFactory = mapperFactory.getConverterFactory();
 MapperFacade mapper = mapperFactory.getMapperFacade();

 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  Person p2 = mapper.map(p1, Person.class);
 }
 System.out.println("orika:"+(System.currentTimeMillis()-start));
 //orika:83
}

@Test
public void testBatchClone(){
 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  try {
   Person p2=(Person) p1.clone();
  } catch (CloneNotSupportedException e) {
   e.printStackTrace();
  }
 }
 System.out.println("clone:"+(System.currentTimeMillis()-start));
 //clone:8
}

(10k) Performance Comparison:


//dozer:721
//commons-beanutils : 229
//cglib:133
//serializable:687
//orika:83
//clone:8

Deep copy summary

The native clone is far and away the most efficient, even on your toes.

Once in a while, it's not a problem with either one.

cglib and orika are perfectly acceptable for applications with slightly higher general performance requirements.

Another consideration, if the project has introduced a dependency, use that dependency. There is no need to introduce a third party dependency.

Thank you for reading, I hope to help you, thank you for your support to this site!


Related articles: