Clon profundo y clon superficial-JAVA

Clon superficial

Copie todos los atributos cuyas variables miembro en el objeto prototipo sean tipos de valor en el objeto clonado

Asignar la dirección de referencia de la variable miembro en el objeto prototipo como un tipo de referencia para el objeto clonado

Es decir, si hay una variable miembro en el objeto prototipo que es un objeto de referencia, la dirección del objeto de referencia se comparte con el objeto prototipo y el objeto clon.

Diagrama esquemático

Clon superficial

Clon profundo

La clonación profunda copiará todos los tipos del objeto prototipo, ya sea un tipo de valor o un tipo de referencia, al objeto clonado.

Clon profundo

Código para clonar en java

El objeto implementado debe implementar la interfaz Cloneable e implementar 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

análisis de código fuente de clonación

 /**
     * 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;

Convención del objeto sobre la clonación (correspondiente a la parte del código fuente anterior con la marca correspondiente)

1.x.clone ()! = X devuelve verdadero porque el objeto clonado y el objeto original no son el mismo objeto, así que devuelve verdadero

2.x.clone (). GetClass () == x.getClass () devuelve verdadero El tipo de objeto clonado y el objeto prototipo son el mismo.

3.x.clone (). Equals (x) devuelve verdadero Los valores del objeto clonado y el objeto prototipo son iguales.

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

Los resultados son todos verdaderos

El método clone () está decorado con nativo y el rendimiento de ejecución será muy alto. El valor de retorno es objeto, por lo que el objeto debe forzarse al tipo de destino después de la clonación.

Arrays.copyof () es un clon superficial

El código de verificación es el siguiente

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());

Devolver resultado:

2 Caballo Zaha
2 Caballo Zaha

Como resultado, se puede ver que los dos comparten una dirección de referencia.

Debido a que la matriz especial es un tipo de referencia, cuando se usa Arrays.copyOf (), simplemente copia la dirección de referencia al objeto clonado. Si se modifica el objeto de referencia, entonces todos los objetos que apuntan a él (dirección de referencia) serán Cambios, por lo que el resultado es que se modifica el primer elemento del objeto clonado y también se modifica el objeto prototipo.

Resumen de los métodos de implementación de la clonación profunda

Todos los objetos implementan el método de clonación;

La operación del código es la siguiente:

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();
        }
    }
}

Resultados de la demostración:

NanJing
Beijing

Realice la clonación profunda a través del método de construcción;

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 方法

    }

}

Utilice el flujo de bytes que viene con el JDK para implementar la clonación profunda;

La forma de implementar la clonación profunda a través del flujo de bytes que viene con el JDK es escribir primero el objeto prototipo en el flujo de bytes en la memoria y luego leer la información recién almacenada del flujo de bytes como un nuevo objeto Retorno, luego el nuevo objeto y el objeto prototipo no comparte ninguna dirección

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 方法

    }

}

Utilice herramientas de terceros para implementar la clonación profunda, como Apache Commons Lang;

Se puede ver que este método es similar al tercer método de implementación, y ambos necesitan implementar la interfaz serializable, que se implementa por flujo de bytes, pero este método de implementación es un método listo para usar proporcionado por un tercero, por lo que puede llamar directamente.

Utilice clases de herramientas JSON para implementar la clonación profunda, como Gson, FastJSON, etc.

El uso de la clase de herramienta JSON primero convertirá el objeto en una cadena y luego convertirá la cadena en un nuevo objeto, porque el nuevo objeto se convierte de una cadena, por lo que no tendrá ninguna relación con el objeto prototipo, por lo que se logra La clonación profunda, otras clases de herramientas JSON similares se implementan de la misma manera.

Entonces, ¿por qué agregar un método clone () a Object?

Debido a la semántica especial del método clone (), es mejor tener el soporte directo de la JVM. Dado que la JVM lo soporta directamente, es necesario encontrar una API para exponer este método. La forma más directa es poner en una clase base Objeto de todas las clases, de modo que todas las clases puedan llamarse fácilmente.

Supongo que te gusta

Origin blog.csdn.net/tangshuai96/article/details/111319073
Recomendado
Clasificación