Cloning, creo que todos han oído hablar de eso. La primera oveja clonada del mundo, Dolly, utiliza tecnología de trasplante nuclear para cultivar nuevas personas en células somáticas adultas de mamíferos, lo cual es muy mágico. De hecho, también existe el concepto de clonación en Java, es decir, realizar la copia de los objetos.
Este artículo intentará introducir algunas preguntas en profundidad sobre la clonación en Java, con la esperanza de ayudarlo a comprender mejor la clonación.
Supongamos que desea copiar una variable simple. Muy simple:
int manzanas = 5; int perlas = manzanas;
No solo el tipo int, los otros siete tipos de datos primitivos (booleano, char, byte, corto, flotante, doble. Long) también son aplicables a este tipo de situación.
Pero si copia un objeto, la situación es un poco complicada.
Supongamos que soy un principiante, escribiría esto:
Estudiante de clase {Número privado int; public int getNumber () {número de retorno; } public void setNumber (int número) {this.number = number; }} prueba de clase pública {public static void main (String args []) {Student stu1 = new Student (); stu1.setNumber (12345); Estudiante stu2 = stu1; System.out.println ("Estudiante 1:" + stu1.getNumber ()); System.out.println ("Estudiante 2:" + stu2.getNumber ()); }}resultado:
Estudiante 1: 12345
Estudiante 2: 12345
Aquí hemos personalizado una clase de estudiante, que tiene solo un campo de número.
Creamos una nueva instancia de estudiante y asignamos el valor a la instancia STU2. (Estudiante stu2 = stu1;)
Veamos los resultados de la impresión. Como novato, me dio unas palmaditas en el pecho y el abdomen, pero el objeto fue copiado así.
¿Es este realmente el caso?
Intentamos cambiar el campo Número de la instancia STU2, y luego imprimir el resultado y ver:
stu2.setNumber (54321); System.out.println ("Estudiante 1:" + stu1.getNumber ()); System.out.println ("Estudiante 2:" + stu2.getNumber ());resultado:
Estudiante 1: 54321
Estudiante 2: 54321
Esto es extraño. ¿Por qué cambia el número de estudiante del estudiante 2 y el número de estudiante del estudiante 1 también cambian?
La razón radica en la oración (stu2 = stu1). El propósito de esta declaración es asignar la referencia de STU1 a STU2.
De esta manera, Stu1 y Stu2 apuntan al mismo objeto en el montón de memoria. Como se muestra en la imagen:
Entonces, ¿cómo podemos lograr copiar un objeto?
¿Recuerdas el objeto del rey de todas las edades? Tiene 11 métodos, y hay dos métodos protegidos, uno de los cuales es el método clon.
En Java, todas las clases se heredan de la clase de objetos en el paquete de idioma Java de forma predeterminada. Verifique su código fuente. Puede copiar src.zip en su directorio JDK a otro lugar y descomprimirlo, y todo el código fuente está incluido. Descubrí que hay un método con el clon protegido del calificador de acceso ()::
/*Crea y devuelve una copia de este objeto. El significado de precisión de "copia" puede depender de la clase del objeto. La intención general es que, para cualquier objeto x, la expresión: 1) x.clone ()! = X será true2) x.clone (). GetClass () == x.getClass () será verdadero, pero estos no son requisitos absolutos.3) x.Clone (). Útil (x) será verdadero, esto no es un requisito absoluto.* arroja clonenotsupportedException;
Si miras de cerca, sigue siendo un método nativo. Todos saben que los métodos nativos son código implementado en idiomas que no son Java y son para los programas de Java. Debido a que los programas Java se ejecutan en máquinas virtuales JVM, no hay forma de acceder a los subyacentes relacionados con el sistema operativo, y solo pueden ser implementados por idiomas cercanos al sistema operativo.
La primera declaración asegura que el objeto clonado tenga una asignación de dirección de memoria separada.
La segunda declaración muestra que los objetos originales y clonados deben tener el mismo tipo de clase, pero no es obligatorio.
La tercera declaración muestra que los objetos originales y clonados deben ser utilizados igualmente por el método Equals (), pero no es obligatorio.
Debido a que la clase principal de cada clase es un objeto, todos contienen un método clon (), pero debido a que el método está protegido, no se puede acceder a ninguno de ellos fuera de la clase.
Para copiar un objeto, debe anular el método clon.
¿Por qué clon?
Pensemos primero en una pregunta, ¿por qué necesitas clonar objetos? ¿No está bien solo un objeto nuevo?
La respuesta es: el objeto clonado puede contener algunas propiedades modificadas, y las propiedades del nuevo objeto siguen siendo los valores en el momento de la inicialización, por lo que cuando se necesita un nuevo objeto para guardar el "estado" del objeto actual, el método clon depende del método de clon. Entonces, ¿no está bien que asigne las propiedades temporales de este objeto a mi nuevo objeto uno por uno? Está bien, pero no hablemos de eso primero. En segundo lugar, a través del código fuente anterior, todos han descubierto que Clone es un método nativo, que es rápido e implementado en la parte inferior.
Déjame recordarte que nuestro objeto común a = nuevo objeto (); objeto b; b = a; Esta forma de código copia las referencias, es decir, la dirección del objeto en la memoria, y los objetos A y B aún apuntan al mismo objeto.
El objeto asignado a través del método clon existe independientemente del objeto original.
Cómo implementar la clonación
Permítanme introducir primero dos métodos de clonación diferentes, clonación blando y clonación profunda.
En el lenguaje Java, los tipos de datos se dividen en tipos de valor (tipos de datos básicos) y tipos de referencia. Los tipos de valor incluyen tipos de datos simples como INT, Double, Byte, Boolean, Char, etc., y los tipos de referencia incluyen tipos complejos como clases, interfaces, matrices, etc. La principal diferencia entre la clonación superficial y la clonación profunda es si admite la copia de variables miembros de tipos de referencia. Los dos se introducirán en detalle a continuación.
Los pasos generales son (clonación superficial):
1. La clase copiada necesita implementar la interfaz clonable (si no la implementa, se lanzará una clonenotsupportedException al llamar al método clon). Esta interfaz es una interfaz de etiqueta (sin ningún método)
2. Anule el método Clone () y establezca el modificador de acceso en público. Llame al método super.clone () en el método para obtener el objeto de copia requerido. (Nativo es un método local)
La siguiente es una modificación del método anterior:
class Student implementa clonable {private int número; public int getNumber () {número de retorno;} public void setNumber (int number) {this.number = number;}@anular public object clone () {student stu = null; try {stu = (student) super.clone ();} capt (clonEnnEntotsUpportedException e) {E.PrintststArttAtraTe ();} stu;}} prueba de clase pública {public static void main (string args []) {estudiante stu1 = new student (); stu1.setNumber (12345); estudiante stu2 = (student) stu1.clone (); system.out.println ("student1:" + stu1.getnumber (); stu2.getNumber ()); stu2.setNumber (54321); system.out.println ("student1:" + stu1.getNumber ()); system.out.println ("student2:" + stu2.getNumber ());}}}}}resultado:
Estudiante 1: 12345
Estudiante 2: 12345
Estudiante 1: 12345
Estudiante 2: 54321
Si no cree que estos dos objetos no sean el mismo objeto, entonces puede echar un vistazo a esta oración:
System.out.println (stu1 == stu2); // FALSO
La copia anterior se llama clonación superficial.
También hay una copia profunda un poco más compleja:
Agreguemos una clase de dirección a la clase de estudiante.
dirección de clase {private string add; public string getAdd () {return add;} public void setAdd (string add) {this.add = add = add;}} clase estudiante de clase implementa clonable {private int numin; private direcciones addr; public dirección pública getAddr () {return addr;} public void setDDR (dirección addr) {this.addr = addDr;} getnumber ()} number;} setNumber (int number) {this.number = number;}@anular el objeto público clone () {student stu = null; try {stu = (student) super.clone ();} catch (cloneNotsupportedException e) {e.PrintStacktrace ();} return;} public class test {public static void main (string (string (string strings [] {{{dirección) Dirección (); addr.setadd ("Hangzhou City"); Student stu1 = new Student (); stu1.setNumber (123); stu1.setaddr (addr); estudiante stu2 = (estudiante) stu1.clone (); system.out.println ("Student 1:" + stu1.getnumber () + ", add:" + stu1.getaddr (). getAdd ()); system.out.println ("Student 2:" + stu2.getNumber () + ", add:" + stu2.getaddr (). getAdd ());}}resultado:
Estudiante 1: 123, Dirección: Hangzhou Estudiante 2: 123, Dirección: Hangzhou
A primera vista, no hay problema, ¿es realmente el caso?
Intentamos cambiar la dirección de la instancia de ADDR en el método principal.
AddR.SetAdd ("Distrito Xihu"); System.out.println ("Student 1:" + stu1.getNumber () + ", add:" + stu1.getaddr (). GetAdd ()); System.out.println ("Student 2:" + stu2.getNumber () + ", add:" + stu2.getaddr (). GetAdd ());resultado:
Estudiante 1: 123, Dirección: Hangzhou Estudiante 2: 123, Dirección: Hangzhou Estudiante 1: 123, Dirección: Xihu Estudiante 2: 123, Dirección: Distrito de Xihu
Esto es extraño, ¿por qué cambian las direcciones de ambos estudiantes?
La razón es que la copia superficial solo copia la referencia de la variable ADDR, y realmente no abre otra pieza de espacio. Después de copiar el valor, devuelva la referencia al nuevo objeto.
Entonces, para lograr los verdaderos objetos de copia, no la copia de referencia puramente. Necesitamos copiar la clase de dirección y modificar el método de clon. El código completo es el siguiente:
paquete ABC; La dirección de clase implementa clonable {String private String add; public String getAdd () {return add; } public void setAdd (string add) {this.add = add; } @Override public Object clone () {dirección addr = null; intente {ADDR = (Dirección) Super.Clone (); } Catch (ClonenotsupportedException e) {E.PrintStackTrace (); } return addr; }} El estudiante de clase implementa clonable {Número privado int; dirección de dirección privada; dirección pública getAddr () {return addr; } public void setAddr (dirección adr) {this.addr = addr; } public int getNumber () {número de retorno; } public void setNumber (int número) {this.number = number; } @Override public Object clone () {Student stu = null; intente {stu = (estudiante) super.clone (); // Copia poco profunda} Catch (clonenotsupportedException e) {E.PrintStackTrace (); } stu.addr = (dirección) addr.clone (); // Copia profunda return stu; }} prueba de clase pública {public static void main (string args []) {dirección addr = new dirección (); AddR.SetAdd ("Ciudad de Hangzhou"); Estudiante stu1 = nuevo estudiante (); stu1.setNumber (123); stu1.setaddr (addr); Estudiante stu2 = (estudiante) stu1.clone (); System.out.println ("Estudiante 1:" + stu1.getNumber () + ", dirección:" + stu1.getaddr (). GetAdd ()); System.out.println ("Student 2:" + stu2.getNumber () + ", dirección:" + stu2.getaddr (). GetAdd ()); AddR.SetAdd ("Distrito Xihu"); System.out.println ("Estudiante 1:" + stu1.getNumber () + ", dirección:" + stu1.getaddr (). GetAdd ()); AddR.SetAdd ()); System.out.println ("Student 2:" + stu2.getNumber () + ", dirección:" + stu2.getaddr (). GetAdd ()); }}resultado:
Estudiante 1: 123, Dirección: Hangzhou Estudiante 2: 123, Dirección: Hangzhou Estudiante 1: 123, Dirección: Xihu Estudiante 2: 123, Dirección: Hangzhou City
Este resultado está en línea con nuestras ideas.
Finalmente, podemos echar un vistazo a una de las clases en la API que implementa el método de clonos:
java.util.date:
/*** Devuelve una copia de este objeto. */ public object clone () {date d = null; intente {d = (date) super.clone (); if (cdate! = null) {d.cdate = (basecalendar.date) cdate.clone (); }} catch (clonenotsupportedException e) {} // no sucederá return d; }Esta categoría es en realidad una copia profunda.
Clonación poco profunda y clonación profunda
1. Clonación poco profunda
En la clonación superficial, si la variable miembro del objeto prototipo es de un tipo de valor, se copiará una copia al objeto clonado; Si la variable miembro del objeto prototipo es de un tipo de referencia, la dirección del objeto de referencia se copiará al objeto clonado, es decir, la variable de miembro del objeto prototipo y el objeto clonado apuntando a la misma dirección de memoria.
En pocas palabras, en clonación superficial, cuando se copia el objeto, solo se copia la variable de valor del tipo de valor del objeto y el objeto miembro del tipo de referencia no se copia.
En el lenguaje Java, la clonación superficial se puede implementar sobrescribiendo el método Clone () de la clase de objeto.
2. Clonación profunda
En la clonación profunda, sin importar si la variable miembro del objeto prototipo es un tipo de valor o un tipo de referencia, se copiará una copia al objeto clonado. La clonación profunda también copia todos los objetos referenciados del objeto prototipo al objeto clonado.
En pocas palabras, en una clonación profunda, excepto por el objeto en sí que se copia, todas las variables miembros contenidas en el objeto también se copiarán.
En el lenguaje Java, si necesita implementar una clonación profunda, puede implementarse sobrescribiendo el método clone () de la clase de objetos, o puede implementarse mediante serialización, etc.
(Si el tipo de referencia contiene muchos tipos de referencia, o la clase de tipo de referencia interna contiene tipos de referencia, será muy problemático usar el método de clon. En este momento, podemos usar la serialización para implementar una clonación profunda del objeto).
La serialización es el proceso de escribir objetos a una secuencia. El objeto escrito en la transmisión es una copia del objeto original, y el objeto original todavía existe en la memoria. La copia implementada por serialización no solo puede copiar el objeto en sí, sino también copiar los objetos del miembro que hace referencia. Por lo tanto, al serializar el objeto a una transmisión y luego al leerlo fuera de la corriente, se puede lograr una clonación profunda. Cabe señalar que la clase de un objeto que puede implementar la serialización debe implementar la interfaz serializable, de lo contrario, la operación de serialización no se puede implementar.
Extendido
El código de la interfaz clonable y la interfaz serializable proporcionada por el lenguaje Java es muy simple. Ambas son interfaces vacías. Esta interfaz vacía también se llama interfaz de identificación. No hay definición de ningún método en la interfaz de identificación. Su función es decirle a JRE si las clases de implementación de estas interfaces tienen una determinada función, como si admiten la clonación, si apoyan la serialización, etc.
Resolver problemas de clonación de múltiples capas
Si el tipo de referencia contiene muchos tipos de referencia, o la clase de tipo de referencia interna contiene tipos de referencia, será muy problemático usar el método Clone. En este momento, podemos usar la serialización para implementar una clonación profunda del objeto.
implementos externos de clase pública serializable {private estático final de serie Long SerialUduid = 369285298572941l; // es mejor declarar explícitamente la identificación pública interna interna; // Descripción: [Método de copia profunda, requiere que el objeto y todas las propiedades del objeto se serializen] public myclone externo () {externo externo = null; Pruebe {// Serialice el objeto en una transmisión, porque lo que está escrito en la transmisión es una copia del objeto, y el objeto original todavía existe en el JVM. Por lo tanto, utilizando esta función, puede lograr una copia profunda del objeto bytearRayOutputStream Baos = new ByteArRayOutputStream (); ObjectOutputStream oos = new ObjectOutputStream (baos); oos.writeObject (esto); // Serialice la transmisión en un objeto bytearrayInputStream bais = new ByteArrayInputStream (baos.tobytearray ()); ObjectInputStream OIS = new ObjectInputStream (BAIS); externo = (exterior) ois.readObject (); } catch (ioException e) {E.PrintStackTrace (); } catch (ClassNotFoundException e) {E.PrintStackTrace (); } return externo; }}Inner también debe implementar serializable, de lo contrario no se puede serializar:
Implementos internos de clase pública Serializable {privado estático final Long SerialVersionUid = 872390113109l; // es mejor declarar explícitamente el nombre de cadena pública de ID = ""; Public Inner (nombre de cadena) {this.name = name; } @Override public string toString () {return "El valor de nombre del interior es:" + nombre; }}Esto también puede permitir que los dos objetos existan de forma completamente independiente en el espacio de la memoria sin afectar los valores de los demás.
Resumir
Hay dos formas de implementar la clonación de objetos:
1). Implemente la interfaz clonable y anule el método clone () en la clase de objeto;
2). Implemente la interfaz serializable e implementa la clonación a través de la serialización y la deserialización de los objetos, lo que puede realizar una verdadera clonación profunda.
Nota: La clonación basada en la serialización y la deserialización no es solo una clonación profunda, sino lo más importante, a través de la limitación genérica, se puede verificar si el objeto para clonarse admite la serialización. Esta verificación lo realiza el compilador y no arroja excepciones en tiempo de ejecución. Esta solución es obviamente mejor que los objetos de clonación utilizando el método clon de la clase de objeto. Siempre es mejor dejar que el problema se ejecute al exponerlo al compilarlo.
Lo anterior es toda la explicación detallada del código de clonación de objetos de implementación de programación Java (copia), espero que sea útil para todos. Los amigos interesados pueden continuar referiéndose a otros temas relacionados en este sitio. Si hay alguna deficiencia, deje un mensaje para señalarlo.