Antes de ingresar oficialmente al tema, primero comprendamos los conceptos de copia profunda y pre-copia:
Copia ligera:
Se creará un nuevo objeto, que tiene una copia exacta del valor de propiedad del objeto original. Si el atributo es un tipo básico, se copia el valor del tipo básico; Si el atributo es una dirección de memoria, se copia la dirección de memoria, por lo que si un objeto cambia esta dirección, afectará a otro objeto;
Copia profunda:
No solo desea copiar todos los valores variables de miembros no referenciados del objeto, sino que también debe crear una nueva instancia para las variables de miembro del tipo de referencia e inicializarlo al valor de instancia de parámetro formal;
Después de comprender el concepto, probemos si las operaciones de asignación de objetos ordinarios son una copia profunda o una copia superficial:
Código de prueba:
Public Class ProfundCopy {public static void main (string [] args) {copy first = new Copy ("Hzw", 24); Copiar segundo = primero; segundo.name = "shanxi"; System.out.println (first.name); // output shanxi}} class Copy {public String Name; Public int Age; Copia pública (name de cadena, int age) {this.name = name; this.age = edad; }} Se puede encontrar que después de la segunda modificación del valor del atributo de nombre a Shanxi, el valor del atributo de primer nombre también se convierte en shanxi. Esto muestra que las asignaciones de objetos ordinarios pertenecen a copias superficiales;
Después de comprender si la asignación entre objetos es una copia superficial, veamos si la clonación es una copia profunda o una copia superficial. El código de prueba es habilitar el objeto de copia anterior para implementar el método clon en la interfaz clonable:
Public Class ProfundCopy {public static void main (string [] args) {copy first = new Copy ("Hzw", 24); Copiar segundo = nulo; intente {segundo = (copy) first.clone (); } Catch (ClonenotsupportedException e) {E.PrintStackTrace (); } segundo.name = "shanxi"; System.out.println (first.name); // salida: hzw system.out.println (first); // output: com.hzw.day33.copy@7f39ebdb system.out.println (segundo); // output :hzw.day33.copy@33abb81e}}}}} Copy Copy clonable {public string name; Public int Age; Copia pública (name de cadena, int age) {this.name = name; this.age = edad; } @Override Protected Object clone () lanza ClonenOtsupportedException {return super.clone (); }}Se puede ver que el objeto creado originalmente y el objeto clonado segundo son dos instancias, por lo que la modificación del atributo de nombre en segundo no afectará el atributo de nombre en primer lugar; Sin embargo, no podemos pensar simplemente que la clonación es una copia profunda, como el siguiente ejemplo:
Public Class ProfundCopy {public static void main (string [] args) {estudiante estudiante = nuevo estudiante (95); Copia primero = nueva copia ("Hzw", 24, estudiante); Copiar segundo = nulo; intente {segundo = (copy) first.clone (); } Catch (ClonenotsupportedException e) {E.PrintStackTrace (); } segundo.name = "shanxi"; segundo.student.score = 60; System.out.println (primero == segundo); // false system.out.println (first.student == Second.student); // True System.out.println (first.student.score); // 60}} Copiar clase implementa clonable {nombre de cadena pública; Public int Age; estudiante público; Copia pública (nombre de cadena, int Age, Student Student) {this.name = name; this.age = edad; this.student = estudiante; } @Override Protected Object clone () lanza ClonenOtsupportedException {return super.clone (); }} Estudiante de clase {puntaje public int; Estudiante público (intesctualidad int) {this.score = stork; }}¿Lo has visto? Creamos en segundo lugar a través de la clonación, y es obvio que primero y el segundo son dos instancias, porque la salida del primer == segundo es falso, pero los objetos del alumno en primer y segundo son los mismos. Después de modificar el valor de puntaje del estudiante en segundo lugar, la puntuación del estudiante en primer lugar también cambió. Esto significa que el estudiante en primer y segundo es el mismo. Esto significa que la clonación es una copia superficial. Si queremos implementar una copia profunda de la clonación, debemos permitir que el objeto del alumno en el objeto de copia también implementa el método de clon en la interfaz clonable, y el método de clon en copia devuelve un clon de alumno, para que el estudiante pueda ser único. El código modificado es el siguiente:
Public Class ProfundCopy {public static void main (string [] args) {estudiante estudiante = nuevo estudiante (95); Copia primero = nueva copia ("Hzw", 24, estudiante); Copiar segundo = nulo; intente {segundo = (copy) first.clone (); } Catch (ClonenotsupportedException e) {E.PrintStackTrace (); } segundo.name = "shanxi"; segundo.student.score = 60; System.out.println (primero == segundo); // false System.out.println (first.student == Second.student); // false System.out.println (first.student.score); // 95 System.out.println (Second.Student.Score); // 60} Copie Copy Copy Clonable {Nombre de cadena pública; Public int Age; estudiante público; Copia pública (nombre de cadena, int Age, Student Student) {this.name = name; this.age = edad; this.student = estudiante; } @Override Protected Object clone () lanza ClonenOtsupportedException {Copy Copy = (Copy) Super.Clone (); copy.student = (Student) Student.Clone (); devolver copia; }} El estudiante de clase implementa clonable {public int score; Estudiante público (intesctualidad int) {this.score = stork; } @Override Protected Object clone () lanza ClonenOtsupportedException {return super.clone (); }} Puede ver que primero y segundo, primero. Estudente y segundo. Los estudiantes no son lo mismo en este momento. Por lo tanto, después de modificar la puntuación del segundo estudiante, no afecta el valor de puntaje del estudiante en el primero, logrando el propósito de una copia profunda;
Sin embargo, si lo piensa cuidadosamente, surgirá el problema. Si también hay atributos de tipos de referencia en la clase de alumnos en nuestro ejemplo anterior, como la clase universitaria, entonces debemos dejar que la clase universitaria implementa la interfaz clonable y luego llame al método de clon de clase universitaria en el método de clon en la clase de estudiantes, y llame al método de clonos de clase de estudiante en el método de clonos de la clase de copia. Descubrí que se había ido. Este proceso es muy complicado. Todos los tipos de referencia relevantes en la clase deben implementar la interfaz clonable. Siento que es tan problemático, está bien, lo siguiente es ser increíble.
La mejor manera de resolver el problema de copia profunda es usar la serialización, para que todas las clases no necesiten implementar la interfaz clonable, solo serializar y deserializar directamente. Echemos un vistazo.
import java.io.file; import java.io.fileInputStream; import java.io.fileOutputStream; import java.io.objectInputStream; import java.io.objectOutputStream; import java.io.serializable; Public Class ProfundCopy {public static void main (String [] args) {College School = New College ("Nongda"); Estudiante estudiante = nuevo estudiante (95, escuela); Copia copia = nueva copia ("Hzw", 23, estudiante); Copiar otro = null; // indica la instancia de clase deserializada // serializa la operación de serialización prueba {fileOutputStream fos = new FileOutputStream (nuevo archivo ("d: /copy.txt")); ObjectOutputStream oos = new ObjectOutputStream (FOS); oos.writeObject (copia); } catch (Exception e) {E.PrintStackTrace (); } // Serializa la operación de deserialización fileInputStream FIS; intente {fis = new FileInputStream (nuevo archivo ("d: /copy.txt")); ObjectInputStream OIS = new ObjectInputStream (FIS); otro = (Copy) OIS.ReadObject (); } catch (Exception e) {E.PrintStackTrace (); } System.out.println (copy == otro); // false system.out.println (copy.student == OTRO.student); // false System.out.println (copy.student.school == OTRO.Student.School); // False otro.student.school.schoolname = "Wuda";; System.out.println (copy.student.school.schoolname); // nongda}} clase copia implementa serializable {public string name; Public int Age; estudiante público; Copia pública (nombre de cadena, int Age, Student Student) {this.name = name; this.age = edad; this.student = estudiante; }} El estudiante de clase implementa serializable {public int score; escuela universitaria pública; Estudiante público (Int Score, College School) {this.score = stork; this.school = escuela; }} Class College implementa serializable {public String SchoolName; Public College (String SchoolName) {this.schoolName = SchoolName; }} Desde la salida, podemos ver que el objeto generado después de la deserialización es una copia del objeto original, y no tiene ninguna relación con el objeto original, excepto el mismo valor de atributo. Por lo tanto, cuando modificamos el nombre de escuela del objeto generado por la deserialización a "wuda", no modificamos el nombre de escuela de la instancia original, y aún obtenemos "nogda", por lo que logramos el efecto de copia profunda real. Sin embargo, para lograr la serialización, todas las clases relevantes deben implementar la interfaz serializable, que siempre es más conveniente que implementar tanto la interfaz clonable como el método clon.
Lo anterior es una explicación detallada de las copias profundas y superficiales de Java. Si lo necesita, consultelo.