La diferencia entre copia profunda, copia superficial y copia de referencia

Tabla de contenido

Autor: Xiaoniu Hululu |  https://xiaoniuhululu.com
En la cuenta pública "Xiaoniuhululu" se encuentran disponibles más artículos interesantes sobre habilidades internas informáticas, capa inferior de JAVA, información relacionada con entrevistas, etc.

copia por referencia

Copia de referencia: la copia de referencia no creará un nuevo objeto en el montón, sino que solo generará una nueva dirección de referencia en la pila, y eventualmente apuntará al mismo objeto que aún está en el montón .

//实体类
public class Person{
    public String name;//姓名
    public int height;//身高
    public StringBuilder something;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public StringBuilder getSomething() {
        return something;
    }

    public void setSomething(StringBuilder something) {
        this.something = something;
    }

    public Person(String name, int height, StringBuilder something) {
        this.name = name;
        this.height = height;
        this.something = something;
    }

}

//测试类
public class copyTest {
    public static void main(String[] args) {
        Person p1 = new Person("小张", 180, new StringBuilder("今天天气很好"));
        Person p2 = p1;

        System.out.println("对象是否相等:"+ (p1 == p2));
        System.out.println("p1 属性值=" + p1.getName()+ ","+ p1.getHeight() + ","+ p1.getSomething());
        System.out.println("p2 属性值=" + p2.getName()+ ","+ p2.getHeight() + ","+ p2.getSomething());


        // change
        p1.name="小王";
        p1.height = 200;
        p1.something.append(",适合出去玩");
        System.out.println("...after p1 change....");

        System.out.println("p1 属性值=" + p1.getName()+ ","+ p1.getHeight() + ","+ p1.getSomething());
        System.out.println("p2 属性值=" + p2.getName()+ ","+ p2.getHeight() + ","+ p2.getSomething());

    }
}

resultado:

Si los objetos son iguales: verdadero
valor de atributo p1 = Xiao Zhang, 180, el clima es bueno hoy
valor de atributo p2 = Xiao Zhang, 180, el clima es bueno hoy
...después del cambio p1....
valor de atributo p1 = Xiao Wang, 200, Hace muy buen tiempo hoy, adecuado para salir a jugar
p2 valor de atributo = Xiao Wang, 200, Hace muy buen tiempo hoy, adecuado para salir a jugar

antes del cambio:


después del cambio:

Podemos ver que dado que las dos referencias p1 y p2 apuntan al mismo objeto en el montón, los dos objetos son iguales. Modificar el objeto p1 afectará al objeto p2, que necesita atención
.

  1. El atributo de nombre, aunque es un tipo de referencia, también es un tipo de cadena, que es inmutable.Si se modifica, la JVM creará un nuevo espacio de memoria en el montón de forma predeterminada y luego reasignará el valor.
  2. int weight=180; Sí成员变量,存放在堆中,不是所有的基本类型变量 都存放在JVM栈中

Tenga en cuenta que es diferente de este artículo ¿  Por qué solo se pasa valor en Java? ,  int num1 = 10;基本类型的局部变量, almacenado en la pila

copia superficial

Copia superficial: la copia superficial crea un nuevo objeto en el montón. El nuevo objeto no es igual al objeto original, pero las propiedades del nuevo objeto son las mismas que las del objeto anterior .
en:

  • Si el atributo es de tipo básico (int, double, long, boolean, etc.), se copia el valor del tipo básico.
  • Si el atributo es un tipo de referencia (salvo que los tipos básicos sean tipos de referencia), lo que se copia es el valor de dirección de la variable de tipo de datos de referencia y el objeto del montón al que apunta la variable de tipo de referencia no se copiará.

¿Cómo implementar una copia superficial? También es muy simple, es decir, implemente la interfaz Cloneable en la clase que necesita ser copiada y reescriba su método clone() .

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

Cuando lo use, simplemente llame al método clone () de la clase directamente

//实体类 继承Cloneable
public class Person implements Cloneable{
    public String name;//姓名
    public int height;//身高
    public StringBuilder something;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public StringBuilder getSomething() {
        return something;
    }

    public void setSomething(StringBuilder something) {
        this.something = something;
    }

    public Person(String name, int height, StringBuilder something) {
        this.name = name;
        this.height = height;
        this.something = something;
    }



    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }

}

//测试类
public class shallowCopyTest {

    public static void main(String[] args) throws CloneNotSupportedException {
        Person p1 = new Person("小张", 180, new StringBuilder("今天天气很好"));
        Person p2 = p1.clone();

        System.out.println("对象是否相等:"+ (p1 == p2));
        System.out.println("p1 属性值=" + p1.getName()+ ","+ p1.getHeight() + ","+ p1.getSomething());
        System.out.println("p2 属性值=" + p2.getName()+ ","+ p2.getHeight() + ","+ p2.getSomething());


        // change
        p1.setName("小王");
        p1.setHeight(200);
        p1.getSomething().append(",适合出去玩");
        System.out.println("...after p1 change....");

        System.out.println("p1 属性值=" + p1.getName()+ ","+ p1.getHeight() + ","+ p1.getSomething());
        System.out.println("p2 属性值=" + p2.getName()+ ","+ p2.getHeight() + ","+ p2.getSomething());

    }
}

resultado:

Si los objetos son iguales: falso
valor de atributo p1 = Xiao Zhang, 180, el tiempo está bien hoy
valor de atributo p2 = Xiao Zhang, 180, el tiempo está bien hoy
...después del cambio de p1....
valor de atributo p1 = Xiao Wang, 200, El clima es muy bueno hoy, adecuado para salir a jugar
valor de atributo p2 = Xiao Zhang, 180, El clima es muy bueno hoy, adecuado para salir a jugar

antes del cambio:


después del cambio:

Podemos ver que:

  1. Cuando modificamos la propiedad de peso del objeto p1, dado que la propiedad de altura de p2 copia directamente la propiedad de altura de p1 antes de la modificación, sigue siendo 180.
  2. Cuando modificamos el atributo de nombre del objeto p1, el nombre de la cadena apunta a un nuevo espacio de memoria, pero el nombre del objeto p2 aún apunta al antiguo espacio de memoria, por lo que el atributo de nombre del objeto p2 sigue siendo "Xiao Zhang". .
  3. Dado que la propiedad algo del objeto p1 y la propiedad algo del objeto p2 apuntan al mismo espacio de memoria, cuando modificamos la propiedad algo del objeto p1, afectará la propiedad algo del objeto p2, por lo que la propiedad algo del objeto p2 se convierte en " hace buen tiempo hoy, propicio para pasar el rato".

copia profunda

Copia profunda: copie un objeto por completo, cree un nuevo objeto en el montón, copie el valor de la variable miembro del objeto copiado y copie el objeto en el montón al mismo tiempo.
Necesidad de anular el método de clonación

    @Override
    public Person clone() throws CloneNotSupportedException {
        //return (Person) super.clone();
        Person person = (Person) super.clone();
        person.setSomething( new StringBuilder(person.getSomething()));//单独为引用类型clone
        return person;
    }

Los resultados de la clase de prueba de prueba de copia superficial:

Si los objetos son iguales: falso
valor de atributo p1 = Xiao Zhang, 180, el tiempo está bien hoy
valor de atributo p2 = Xiao Zhang, 180, el tiempo está bien hoy
...después del cambio de p1....
valor de atributo p1 = Xiao Wang, 200, Hace muy buen tiempo hoy, adecuado para salir a jugar
valor de atributo p2 = Xiao Zhang, 180, Hace muy buen tiempo hoy

En este momento, el objeto p1 y el objeto p2 no interfieren entre sí.

antes del cambio:


Después del cambio:

Pero también hay un pequeño problema. Cada vez que un objeto tiene un tipo de referencia, tenemos que reescribir su método de clonación, lo que será muy problemático, por lo que también podemos usar la serialización para lograr una copia profunda de los objetos.

//实体类 继承Cloneable
public class Person implements Serializable{
    public String name;//姓名
    public int height;//身高
    public StringBuilder something;

...//省略 getter setter


    public Object deepClone() throws Exception{
        // 序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
    
        oos.writeObject(this);
    
        // 反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
    
        return ois.readObject();
    }

}

//测试类,这边类名笔者就不换了,在之前的基础上改改
public class shallowCopyTest {

    public static void main(String[] args) throws Exception {
        Person p1 = new Person("小张", 180, new StringBuilder("今天天气很好"));
        Person p2 = (Person)p1.deepClone();

        System.out.println("对象是否相等:"+ (p1 == p2));
        System.out.println("p1 属性值=" + p1.getName()+ ","+ p1.getHeight() + ","+ p1.getSomething());
        System.out.println("p2 属性值=" + p2.getName()+ ","+ p2.getHeight() + ","+ p2.getSomething());


        // change
        p1.setName("小王");
        p1.setHeight(200);
        p1.getSomething().append(",适合出去玩");
        System.out.println("...after p1 change....");

        System.out.println("p1 属性值=" + p1.getName()+ ","+ p1.getHeight() + ","+ p1.getSomething());
        System.out.println("p2 属性值=" + p2.getName()+ ","+ p2.getHeight() + ","+ p2.getSomething());

    }
}

Esto también resultará en una copia profunda

resumen

  1. Copia de referencia: la copia de referencia no creará un nuevo objeto en el montón, sino que solo generará una nueva dirección de referencia en la pila, y eventualmente apuntará al mismo objeto que aún está en el montón .
  2. Copia superficial: la copia superficial crea un nuevo objeto en el montón. El nuevo objeto no es igual al objeto original, pero las propiedades del nuevo objeto son las mismas que las del objeto anterior .

en:

  • Si el atributo es de tipo básico (int, double, long, boolean, etc.), se copia el valor del tipo básico.
  • Si el atributo es un tipo de referencia (salvo que los tipos básicos sean tipos de referencia), lo que se copia es el valor de dirección de la variable de tipo de datos de referencia y el objeto del montón al que apunta la variable de tipo de referencia no se copiará.
  1. Copia profunda: copie un objeto por completo, cree un nuevo objeto en el montón, copie el valor de la variable miembro del objeto copiado y copie el objeto en el montón al mismo tiempo.

Materiales de referencia:
donde int a = 1 es el blog CSDN-blog de store_ly_dsjing ¿
En qué área de memoria del jvm se colocan las variables miembro del tipo de datos básico? - huliangbin - Blog Garden
Muchas gracias por leer hasta el final. Si te gusta, sigue, dale me gusta, recopila y reenvía, ¡gracias! Más artículos geniales

Supongo que te gusta

Origin blog.csdn.net/weixin_45499836/article/details/126482694
Recomendado
Clasificación