Deep clone and shallow clone-JAVA

Shallow clone

Copy all attributes whose member variables in the prototype object are value types to the cloned object

Assign the reference address of the member variable in the prototype object as a reference type to the cloned object

That is, if there is a member variable in the prototype object that is a reference object, the address of the reference object is shared with the prototype object and the clone object

Schematic diagram

Shallow clone

Deep clone

Deep cloning will copy all types in the prototype object, whether it is a value type or a reference type, to the cloned object

Deep clone

Code for cloning in java

The implemented object must implement the Cloneable interface and implement clone()

import lombok.Data;

public class CloneLearn {
    
    
    public static void main(String[] args) throws CloneNotSupportedException {
    
    
      People p1=new People();
      p1.setId(1);
      p1.setName("古力娜扎");
      People p2 = (People)p1.clone();
        System.out.println(p2.getName());

    }
    @Data
    static class People implements Cloneable{
    
    
     private Integer id;
     private String name;

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

Gulinaza

clone source code analysis

 /**
     * Creates and returns a copy of this object.  The precise meaning
     * of "copy" may depend on the class of the object. The general
     * intent is that, for any object {@code x}, the expression:
     * <blockquote>
     * <pre>
     * 1:x.clone() != x</pre></blockquote>
     * will be true, and that the expression:
     * <blockquote>
     * <pre>
     * 2:x.clone().getClass() == x.getClass()</pre></blockquote>
     * will be {@code true}, but 11111equirements.
     * While it is typically the case that:
     * <blockquote>
     * <pre>
     * 3:x.clone().equals(x)</pre></blockquote>
     * will be {@code true}, this is not an absolute requirement.
     * <p>
     * By convention, the returned object should be obtained by calling
     * {@code super.clone}.  If a class and all of its superclasses (except
     * {@code Object}) obey this convention, it will be the case that
     * {@code x.clone().getClass() == x.getClass()}.
     * <p>
     * By convention, the object returned by this method should be independent
     * of this object (which is being cloned).  To achieve this independence,
     * it may be necessary to modify one or more fields of the object returned
     * by {@code super.clone} before returning it.  Typically, this means
     * copying any mutable objects that comprise the internal "deep structure"
     * of the object being cloned and replacing the references to these
     * objects with references to the copies.  If a class contains only
     * primitive fields or references to immutable objects, then it is usually
     * the case that no fields in the object returned by {@code super.clone}
     * need to be modified.
     * <p>
     * The method {@code clone} for class {@code Object} performs a
     * specific cloning operation. First, if the class of this object does
     * not implement the interface {@code Cloneable}, then a
     * {@code CloneNotSupportedException} is thrown. Note that all arrays
     * are considered to implement the interface {@code Cloneable} and that
     * the return type of the {@code clone} method of an array type {@code T[]}
     * is {@code T[]} where T is any reference or primitive type.
     * Otherwise, this method creates a new instance of the class of this
     * object and initializes all its fields with exactly the contents of
     * the corresponding fields of this object, as if by assignment; the
     * contents of the fields are not themselves cloned. Thus, this method
     * performs a "shallow copy" of this object, not a "deep copy" operation.
     * <p>
     * The class {@code Object} does not itself implement the interface
     * {@code Cloneable}, so calling the {@code clone} method on an object
     * whose class is {@code Object} will result in throwing an
     * exception at run time.
     *
     * @return     a clone of this instance.
     * @throws  CloneNotSupportedException  if the object's class does not
     *               support the {@code Cloneable} interface. Subclasses
     *               that override the {@code clone} method can also
     *               throw this exception to indicate that an instance cannot
     *               be cloned.
     * @see java.lang.Cloneable
     */
    protected native Object clone() throws CloneNotSupportedException;

Object's convention on clone (corresponding to the above source code part with corresponding mark)

1.x.clone()!=x returns true because the cloned object and the original object are not the same object, so return true

2.x.clone().getClass()==x.getClass() returns true The type of the cloned object and the prototype object are the same.

3.x.clone().equals(x) returns true The values ​​of the cloned object and the prototype object are equal.

System.out.println(p1!=p2);
System.out.println(p1.getClass()==p2.getClass());
System.out.println(p1.equals(p2));

The results are all true

The clone() method is decorated with native, and the execution performance will be very high. The return value is object, so the object must be forced to the target type after cloning.

Arrays.copyof() is a shallow clone

The verification code is as follows

People[] people={
    
    new People(1,"马儿扎哈")};
People[] people1 = Arrays.copyOf(people, people.length);
people[0].setId(2);
System.out.println(people[0].getId()+people[0].getName());
System.out.println(people1[0].getId()+people1[0].getName());

Return result:

2 Horse Zaha
2 Horse Zaha

As a result, it can be seen that the two share a reference address.

Because the special array is a reference type, when using Arrays.copyOf(), it just copies the reference address to the cloned object. If the reference object is modified, then all objects pointing to it (reference address) will be Change occurs, so the result is that the first element of the cloned object is modified, and the prototype object is also modified.

Summary of deep cloning implementation methods

All objects implement the clone method;

The code operation is as follows:

public class CloneLearn {
    
    
    public static void main(String[] args) throws CloneNotSupportedException {
    
    
        Address address=new Address(110,"BeiJing");
        People p1=new People(1,"古力娜扎",address);
        People p2 = p1.clone();
        p1.getAddress().setCity("NanJing");
        System.out.println(p1.getAddress().city);
        System.out.println(p2.getAddress().city);

    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class People implements Cloneable{
    
    
     private Integer id;
     private String name;
     private Address address;

     @Override
     protected People clone() throws CloneNotSupportedException{
    
    
         People people=(People) super.clone();
         people.setAddress( this.address.clone());
         return people;
     }
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class Address implements Cloneable{
    
    
        private Integer id;
        private String city;

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

Demonstration results:

NanJing
BeiJing

Realize deep cloning through construction method;

public class SecondExample {
    
    

    public static void main(String[] args) throws CloneNotSupportedException {
    
    

        // 创建对象

        Address address = new Address(110, "北京");

        People p1 = new People(1, "Java", address);

        // 调用构造函数克隆对象

        People p2 = new People(p1.getId(), p1.getName(),

                new Address(p1.getAddress().getId(), p1.getAddress().getCity()));

        // 修改原型对象

        p1.getAddress().setCity("西安");

        // 输出 p1 和 p2 地址信息

        System.out.println("p1:" + p1.getAddress().getCity() +

                " p2:" + p2.getAddress().getCity());

    }

    /**

     * 用户类

     */

    static class People {
    
    

        private Integer id;

        private String name;

        private Address address;

        // 忽略构造方法、set、get 方法

    }

    /**

     * 地址类

     */

    static class Address {
    
    

        private Integer id;

        private String city;

        // 忽略构造方法、set、get 方法

    }

}

Use the byte stream that comes with the JDK to implement deep cloning;

The way to implement deep cloning through the byte stream that comes with the JDK is to first write the prototype object to the byte stream in memory, and then read the information just stored from the byte stream as a new object Return, then the new object and the prototype object do not share any address

import java.io.*;

public class ThirdExample {

    public static void main(String[] args) throws CloneNotSupportedException {

        // 创建对象

        Address address = new Address(110, "北京");

        People p1 = new People(1, "Java", address);

        // 通过字节流实现克隆

        People p2 = (People) StreamClone.clone(p1);

        // 修改原型对象

        p1.getAddress().setCity("西安");

        // 输出 p1 和 p2 地址信息

        System.out.println("p1:" + p1.getAddress().getCity() +

                " p2:" + p2.getAddress().getCity());

    }

    /**

     * 通过字节流实现克隆

     */

    static class StreamClone {

        public static <T extends Serializable> T clone(People obj) {

            T cloneObj = null;

            try {

                // 写入字节流

                ByteArrayOutputStream bo = new ByteArrayOutputStream();

                ObjectOutputStream oos = new ObjectOutputStream(bo);

                oos.writeObject(obj);

                oos.close();

                // 分配内存,写入原始对象,生成新对象

                ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());//获取上面的输出字节流

                ObjectInputStream oi = new ObjectInputStream(bi);

                // 返回生成的新对象

                cloneObj = (T) oi.readObject();

                oi.close();

            } catch (Exception e) {

                e.printStackTrace();

            }

            return cloneObj;

        }

    }

    /**

     * 用户类

     */

    static class People implements Serializable {

        private Integer id;

        private String name;

        private Address address;

        // 忽略构造方法、set、get 方法

    }

    /**

     * 地址类

     */

    static class Address implements Serializable {

        private Integer id;

        private String city;

        // 忽略构造方法、set、get 方法

    }

}

Use third-party tools to implement deep cloning, such as Apache Commons Lang;

It can be seen that this method is similar to the third implementation method, and both need to implement the Serializable interface, which is implemented by byte stream, but this implementation method is a ready-made method provided by a third party, so that we can directly call .

Use JSON tool classes to implement deep cloning, such as Gson, FastJSON, etc.

Using the JSON tool class will first convert the object into a string, and then convert the string into a new object, because the new object is converted from a string, so it will not have any relationship with the prototype object, so it is achieved Deep cloning, other similar JSON tool classes are implemented in the same way.

So why add a clone() method to Object?

Because of the special semantics of the clone() method, it is best to have the direct support of the JVM. Since the JVM directly supports it, it is necessary to find an API to expose this method. The most direct way is to put it in A base class Object of all classes, so that all classes can be easily called.

Guess you like

Origin blog.csdn.net/tangshuai96/article/details/111319073