Comprensión profunda de la copia superficial y la copia profunda de Java

La copia de objetos en Java es un concepto importante, especialmente en la programación orientada a objetos. Java proporciona dos tipos de métodos de copia: copia superficial y copia profunda.

Una copia superficial solo copia la referencia, en lugar de crear un nuevo objeto, por lo que el nuevo objeto hace referencia a los mismos datos que el objeto original. La copia profunda crea un nuevo objeto, que es completamente independiente del objeto original y tiene su propio espacio de memoria y valores.

Este artículo explorará en profundidad los conceptos de copia superficial y copia profunda de Java, explicará sus respectivos usos, escenarios de uso y técnicas, y ayudará a los lectores a comprender mejor el significado de estos dos métodos de copia a través de casos prácticos.

Copia superficial

¿Qué es una copia superficial?

La copia superficial de Java significa copiar solo la referencia del objeto en lugar de crear un nuevo objeto. Esto significa que modificar el nuevo objeto afectará al objeto original. La copia superficial generalmente se implementa mediante el método clone() de la clase Object. Solo tiene efecto para ocho tipos de datos básicos y tipos de referencia de objetos. La copia superficial no se puede implementar para otros tipos de datos.

¿Cómo implementar una copia superficial?

La clase Object en Java proporciona un método de clonación clone (). Si queremos hacer una copia superficial, solo necesitamos implementar la interfaz Cloneable y reescribir el método clone (). He aquí un ejemplo sencillo:

public class Employee implements Cloneable {
    private String name;
    private int age;
    private Address address;

    public Employee(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }

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

public class Address {
    private String street;
    private String city;

    public Address(String street, String city) {
        this.street = street;
        this.city = city;
    }

    public String getStreet() {
        return street;
    }
    public void setStreet(String street) {
        this.street = street;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
}

// 测试代码
Employee emp1 = new Employee("Tom", 28, new Address("1234", "New York"));
Employee emp2 = (Employee)emp1.clone(); // 浅拷贝
System.out.println(emp1 == emp2); // false,引用不同
System.out.println(emp1.getAddress() == emp2.getAddress()); // true,地址相同

Como se puede ver en el código anterior, implementamos una copia superficial implementando la interfaz Cloneable y anulando el método clone(). Luego creamos un objeto Empleado emp1 y le asignamos los valores de atributo correspondientes. A continuación, realizamos una copia superficial a través de emp1.clone() y generamos otro objeto Empleado emp2. Como puede ver, las referencias de emp1 y emp2 son diferentes, pero comparten el mismo objeto de dirección.

Desventajas de la copia superficial

La copia superficial tiene algunas desventajas obvias en comparación con la copia profunda:

1. Influenciado por el objeto original.

Dado que la copia superficial solo copia la referencia del objeto, las modificaciones al objeto de copia superficial tendrán un impacto en el objeto original. Esto significa que cuando modificamos el objeto de copia superficial, podemos modificar accidentalmente el objeto original.

// 测试代码
Employee emp1 = new Employee("Tom", 28, new Address("1234", "New York"));
Employee emp2 = (Employee)emp1.clone(); // 浅拷贝
emp2.getAddress().setStreet("5678");

System.out.println(emp1.getAddress().getStreet()); // 5678,原始对象被修改

Como puede ver en el código anterior, modificamos la dirección del objeto Dirección al que hace referencia el objeto emp2, y luego accedimos al objeto Dirección del objeto emp1 nuevamente y descubrimos que había cambiado, lo que indica que el objeto original se vio afectado. por el objeto de copia superficial.

2. Los miembros de datos de tipos no básicos no se pueden copiar.

La copia superficial solo puede copiar tipos básicos de datos, y la copia profunda no se puede completar para tipos complejos de estructuras de datos, como matrices, objetos, etc. Porque una copia superficial simplemente copia la referencia en lugar de crear un nuevo objeto.

copia profunda

¿Qué es una copia profunda?

En comparación con la copia superficial, la copia profunda creará un nuevo objeto y copiará todas las propiedades del objeto original al objeto recién creado. Esto significa que modificar el nuevo objeto no afectará al objeto original. Las copias profundas generalmente se implementan mediante la API de serialización de Java, pero también se pueden implementar mediante transmisión, ObjectInputStream/ObjectOutputStream.

¿Cómo implementar una copia profunda?

Java proporciona varias formas de implementar una copia profunda, que se presentan a continuación:

1. Serialización para lograr una copia profunda

Java proporciona un mecanismo de serialización y deserialización predeterminado a través del cual podemos implementar una copia profunda. Este método es muy simple, solo necesita implementar la interfaz Serializable en el objeto a copiar. El proceso de copia serializada es el siguiente:

// 将对象序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);

// 反序列化对象
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();

 

Aquí hay un ejemplo específico:

public class Employee implements Serializable {
    private String name;
    private int age;
    private Address address;

    public Employee(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
}

public class Address implements Serializable {
    private String street;
    private String city;

    public Address(String street, String city) {
        this.street = street;
        this.city = city;
    }

    public String getStreet() {
        return street;
    }
    public void setStreet(String street) {
        this.street = street;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
}

// 测试代码
Employee emp1 = new Employee("Tom", 28, new Address("1234", "New York"));
Employee emp2 = (Employee) deepCopy(emp1); // 序列化实现深拷贝
System.out.println(emp1 == emp2); // false,引用不同
System.out.println(emp1.getAddress() == emp2.getAddress()); // false,地址不同

private static Object deepCopy(Object obj) throws IOException, ClassNotFoundException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(obj);
    ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
    return ois.readObject();
}

Como puede ver en el código anterior, implementamos una copia profunda implementando la interfaz Serializable y serializando y deserializando. Luego creamos un objeto Empleado emp1 y le asignamos los valores de atributo correspondientes. A continuación, realizamos una copia profunda mediante el método deepCopy() y generamos otro objeto Empleado emp2. Se puede ver que las referencias de emp1 y emp2 son diferentes, y sus direcciones también son diferentes, lo que indica que la copia profunda se ha completado.

2. Procesamiento de transmisión para lograr una copia profunda

El procesamiento de flujo también puede implementar copias profundas. La API Streams agregada en Java 8 se puede utilizar para procesar elementos de colecciones u otras fuentes de datos de forma rápida y sencilla. Un patrón común con Streams es recopilar todos los elementos de una fuente de datos en una nueva colección. Este proceso se llama conversión y también se puede utilizar para copias profundas.

Aquí hay un ejemplo específico:

public class Employee implements Cloneable {
    private String name;
    private int age;
    private Address address;
    // 省略构造器、get/set方法等

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Employee empClone = (Employee)super.clone();
        empClone.setAddress(new Address(address.getStreet(), address.getCity())); // 注意深拷贝
        return empClone;
    }
}

public class Address {
    private String street;
    private String city;
    // 省略构造器、get/set方法等
}

// 测试代码
Employee emp1 = new Employee("Tom", 28, new Address("1234", "New York"));
Employee emp2 = (Employee)emp1.clone(); // 流式处理实现深拷贝
System.out.println(emp1 == emp2); // false,引用不同
System.out.println(emp1.getAddress() == emp2.getAddress()); // false,地址不同

Como puede ver en el código anterior, implementamos una copia profunda reescribiendo el método clone() y usando el procesamiento de transmisión. Luego creamos un objeto Empleado emp1 y le asignamos los valores de atributo correspondientes.

A continuación, realizamos una copia profunda a través de emp1.clone() y generamos otro objeto Empleado emp2. Se puede ver que las referencias de emp1 y emp2 son diferentes, y sus direcciones también son diferentes, lo que indica que la copia profunda se ha completado.

Ventajas de la copia profunda

En comparación con la copia superficial, la copia profunda tiene las siguientes ventajas:

1. Copia completa de una estructura de datos compleja.

La copia profunda copiará todos los valores de los atributos, por lo que se pueden copiar por completo una serie de estructuras de datos complejas, como matrices de objetos, objetos anidados, anidamientos de varios niveles, etc.

2. Completamente independiente

Una copia profunda no se ve afectada por el objeto original y, por lo tanto, funciona de forma completamente independiente del objeto original.

3. Diferentes direcciones de referencia

Una copia profunda crea un nuevo objeto con una dirección de referencia diferente a la del objeto original. Esto significa que la copia profunda no cambia el estado del objeto original, lo que lo hace más seguro y confiable.

La diferencia entre copia superficial y copia profunda

La copia superficial simplemente copia la referencia en lugar de crear un nuevo objeto. Comparten los mismos datos.

La copia profunda creará un nuevo objeto, todos los valores de propiedad del objeto se copiarán por completo y se podrán operar de forma completamente independiente del objeto original.

La principal diferencia entre copia superficial y copia profunda es que la copia superficial solo se centra en la dirección de memoria del objeto, mientras que la copia profunda se centra en el objeto mismo.

¿Cómo elegir entre copia superficial y copia profunda?

En aplicaciones prácticas, debemos optar por utilizar copia superficial o copia profunda según las necesidades específicas. Hay algunos puntos a considerar:

Estructura y complejidad del objeto.

Si la estructura del objeto es simple y los valores de los atributos son básicamente tipos básicos, puede utilizar una copia superficial; por el contrario, la copia profunda es más segura.

tamaño del objeto

Si el objeto copiado es muy grande, el uso de una copia superficial puede consumir mucho tiempo y recursos de memoria, en este caso la copia profunda puede reducir el consumo de tiempo y memoria.

Seguridad operativa

Si la operación requiere seguridad de los datos, se debe utilizar una copia profunda. Porque cuando se utiliza una copia superficial, el objeto copiado puede modificarse accidentalmente.

Expande tu pensamiento

En Java, ¿cómo se pueden implementar la copia superficial y la copia profunda del tipo Lista?

Copia superficial

Se puede lograr una copia superficial de List llamando al método clone() proporcionado por la interfaz List. Este método devuelve un nuevo objeto que tiene los mismos elementos que el objeto original, pero el nuevo objeto comparte referencias a los mismos elementos que el objeto original.

El código de muestra es el siguiente:

Cabe señalar que al realizar una copia profunda, el objeto copiado debe implementar la interfaz clonable o serializable; de ​​lo contrario, se generará una excepción. Al mismo tiempo, cuando se utiliza la serialización para una copia profunda, el objeto copiado debe garantizar que todas sus variables miembro sean serializables.

En general, utilizar una copia profunda es más seguro, pero también consume más tiempo y recursos de memoria. Por lo tanto, al elegir utilizar copia superficial o copia profunda, es necesario sopesar los requisitos y el rendimiento.

List<Object> originalList = new ArrayList<>();
// 假设 originalList 中已经存在一些元素
List<Object> shallowCopyList = (ArrayList<Object>) originalList.clone();

copia profunda

Una copia profunda de una Lista requiere una copia recursiva de cada elemento de la colección, lo que garantiza que todos los elementos del nuevo objeto sean objetos recién creados y no referencias a los objetos originales. Esto generalmente requiere el uso de un bucle para recorrer la colección y juzgar y copiar cada elemento.

El código de muestra es el siguiente:

List<Object> originalList = new ArrayList<>();
// 假设 originalList 中已经存在一些元素
List<Object> deepCopyList = new ArrayList<>();
for (Object element : originalList) {
    if (element instanceof Cloneable) { // 可以被克隆
        try {
            Method cloneMethod = element.getClass().getMethod("clone");
            Object copiedElement = cloneMethod.invoke(element);
            deepCopyList.add(copiedElement);
        } catch (Exception e) {
            // 处理异常
        }
    } else if (element instanceof Serializable) { // 可以被序列化
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(element);
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            Object copiedElement = ois.readObject();
            deepCopyList.add(copiedElement);
        } catch (Exception e) {
            // 处理异常
        }
    } else { // 不可复制
        deepCopyList.add(element);
    }
}

Cabe señalar que al realizar una copia profunda, el objeto copiado debe implementar la interfaz clonable o serializable; de ​​lo contrario, se generará una excepción. Al mismo tiempo, cuando se utiliza la serialización para una copia profunda, el objeto copiado debe garantizar que todas sus variables miembro sean serializables.

En general, utilizar una copia profunda es más seguro, pero también consume más tiempo y recursos de memoria. Por lo tanto, al elegir utilizar copia superficial o copia profunda, es necesario sopesar los requisitos y el rendimiento.

Finalmente : El video tutorial completo de prueba de software a continuación ha sido compilado y subido. Los amigos que lo necesiten pueden obtenerlo ellos mismos [garantizado 100% gratis]

Documento de entrevista de prueba de software

Debemos estudiar para encontrar un trabajo bien remunerado. Las siguientes preguntas de la entrevista son los últimos materiales de entrevista de empresas de Internet de primer nivel como Alibaba, Tencent, Byte, etc., y algunos jefes de Byte han dado respuestas autorizadas. Después de terminar este conjunto Creo que todos pueden encontrar un trabajo satisfactorio según la información de la entrevista.

Supongo que te gusta

Origin blog.csdn.net/wx17343624830/article/details/132859060
Recomendado
Clasificación