Prefacio
La clonación de Java (clon) es una de las características del idioma Java, pero es raro usarlo en la práctica. Pero a veces la clonación es más conveniente y eficiente.
Para los clones, Java tiene algunas limitaciones:
1. La clase clonada debe implementar la interfaz clonable en sí misma para indicar que Object.clone() puede copiar legalmente la instancia de esta clase por campo. La interfaz clonable es en realidad una interfaz de identificación y no tiene un método de interfaz.
2. Las clases que implementan la interfaz clonable deben anular Object.clone (está protegido) utilizando métodos públicos. Es imposible que un objeto lo clone después de implementar esta interfaz. Incluso si el método de clon se llama reflexivamente, no hay garantía de que tenga éxito.
3. El método de clonación en Java.lang.Object se define de la siguiente manera:
Objeto protegido Clone () arroja clonenotsupportedException
Crea y devuelve una copia de este objeto. Indica que es un método protegido, visible en el mismo paquete.
Por convención, el objeto devuelto debe obtenerse llamando super.clone .
Asignación en Java
En Java, la asignación se usa muy comúnmente, una asignación simple es la siguiente.
// El tipo original int a = 1; int b = a; // Tipo de referencia String [] Weekdays = New String [5]; String [] gongzuori = weekdays; // Copiar solo referencias referencias
En el código anterior.
1. Si es el tipo de datos original, el valor aprobado por la asignación es el valor real.
2. Si es un tipo de datos de referencia, la asignación pasa la referencia al objeto, no al objeto.
Comprender la diferencia entre los tipos de datos y los tipos de referencia nos facilita comprender el clon.
Clon
En Java, Clone es el proceso de copiar un objeto existente en la memoria a otro objeto que es el mismo que. La clonación en Java es una copia de dominio por dominio.
Si desea admitir métodos clonados en Java, primero debe implementar la interfaz clonable
Clonable es en realidad un poco extraño. Es diferente de la interfaz que a menudo usamos. No contiene ningún método en el interior, es solo una interfaz de marcado.
El código fuente es el siguiente
interfaz pública clonable {}Sobre clonable, a qué se debe prestar atención
1. Si desea admitir Clone, debe implementar la interfaz clonable
2. Si el método de clon no se llama a la interfaz clonable, se lanzará una clonenotsupportedException.
Luego reescribe el método de clon y modifíelo al nivel de acceso público
Clase estática clonable Implementa clonable {public int Count; niño público niño; @Override public Object clone () lanza ClonenOtsupportedException {return super.clone (); }}Llame al método clon para copiar el objeto
ClonableIm Imp1 = new ClonableImp (); Imp1.Child = New Child ("Andy"); Try {Object obj = Imp1.Clone (); ClonableIm Imp2 = (ClonableIm) obj; System.out.println ("Main imp2.child.name =" + imp2.child.name);} catch (cloneSupportedException e) {E.PrintStackTrace ();}Copia ligera
El clon implementado en el código anterior es en realidad una copia superficial.
Lo que debe saber sobre la copia superficial
1. Use el método clon predeterminado
2. Haga una copia de valor del campo de datos original
3. Solo copia referencias para tipos de referencia
4. Ejecución rápida y alta eficiencia
5. Los datos no se pueden separar en el 100%.
6. Si un objeto solo contiene el dominio de datos original o el dominio del objeto inmutable, se recomienda usar una copia superficial.
Con respecto a la incapacidad de separar los datos, podemos usar este código para verificarlo.
ClonableIm Imp1 = new ClonableImp (); Imp1.Child = New Child ("Andy"); Try {Object obj = Imp1.Clone (); ClonableIm Imp2 = (ClonableIm) obj; imp2.child.name = "bob"; System.out.println ("Main imp1.child.name =" + imp1.child.name);} catch (clonenotsupportedException e) {e.printstacktrace ();} En el código anterior, utilizamos el método clon de Imp1 a clon Imp2, luego modificó imp2.child.name a Bob, e imprimimos imp1.child.name para obtener el resultado.
principal imp1.child.name = bob
La razón es que la copia superficial no alcanza el 100% de separación de los datos. Imp1 e Imp2 comparten el mismo objeto infantil, por lo que una modificación afectará al otro.
Copia profunda
La copia profunda puede resolver el problema de la separación del 100% de los datos. Solo haga algunas modificaciones al código anterior.
1. El niño implementa la interfaz clonable.
Class pública El niño implementa clonable {nombre de cadena pública; Public Child (nombre de cadena) {this.name = name; } @Override public string toString () {return "child [name =" + name + "]"; } @Override Protected Object clone () lanza ClonenOtsupportedException {return super.clone (); }}2. Reescribe el método clon y llame al método clon del dominio de datos.
Clase estática clonable Implementa clonable {public int Count; niño público niño; @Override public Object Clone () lanza ClonenotsupportedException {ClonableImp obj = (ClonableImp) Super.Clone (); obj.child = (niño) child.clone (); regresar obj; }} Cuando modificamos imp2.child.name nuevamente, no afectará el valor de imp1.child.name , porque Imp1 e Imp2 tienen sus propios objetos hijos, porque los datos están 100% aislados.
Algunas características de la copia profunda
1. Debe anular el método clon, no solo llamar al método de la clase principal, sino también llamar al método de clon del atributo.
2. 100% Se alcanza la separación de datos entre el objeto original y el objeto clonado
3. Si el objeto tiene un atributo de tipo de referencia, se recomienda usar una copia profunda.
4. La copia profunda lleva más tiempo y es menos eficiente que la copia superficial
Por qué usar la clonación
Es muy importante y común: una API necesita proporcionar una recopilación de listas, pero no quiere que la modificación de la persona que llame afecte sus propios cambios, por lo que debe clonar un objeto para lograr el propósito del aislamiento de datos.
Cloone debe evitarse tanto como sea posible
1. Por lo general, la interfaz se implementa para mostrar lo que la clase puede hacer para sus clientes, mientras que clonable es solo una interfaz de etiqueta, y también cambia el comportamiento del método protegido a mano en la superclase. Es un uso extremadamente atípico de la interfaz y no es digno de imitación.
2. La descripción de Javadoc de la convención del método de clonos y su método de clon frágil es un poco ambiguo, como sigue: la convención Java SE8
El método clon crea y devuelve una copia del objeto. El significado exacto de la copia depende de la clase del objeto. El significado general es que para cualquier objeto x, la expresión
x.clone() != x 为true x.clone().getClass() == x.getClass() también devuelve verdadero, pero no requerido x.clone().equals(x) también devuelve verdadero, pero no necesario, pero no necesario
La segunda y la tercera expresiones anteriores devuelven fácilmente False. Por lo tanto, lo único que puede garantizar verdadero verdadero es la expresión uno, es decir, los dos objetos son objetos independientes.
3. El dominio final del objeto variable. En el método de clonación, si necesitamos copiar el dominio final del objeto variable, en realidad es imposible compilar y pasar debido a restricciones finales. Por lo tanto, para implementar la clonación, debemos considerar abandonar la palabra clave final del dominio de objeto mutable.
4. Seguridad de los hilos Si decide usar clases seguras para hilos para implementar la interfaz clonable, debe asegurarse de que su método de clon se sincronice. El método predeterminado Object.clone no está sincronizado.
En general, el método clon en Java no es realmente perfecto, por lo que se recomienda evitar usarlo tanto como sea posible. Aquí hay algunas alternativas.
Constructores de copias
La copia de los objetos también se puede implementar utilizando un constructor de copias.
1. El constructor de copias también es un tipo de constructor
2. Solo se acepta un parámetro, el tipo de parámetro es la clase actual
3. El propósito es generar un nuevo objeto con los mismos parámetros que
4. La ventaja del constructor de copias sobre el método de clon es que es simple y fácil de implementar.
Un código de ejemplo que usa un constructor de copias
auto de clase pública {rueda rueda; Fabricante de cuerdas; Public Car (Wheel Wheel, Fabricante de cadenas) {this.wheel = Wheel; this.Manufacturer = fabricante; } // copiar constructor de auto público (coche de automóvil) {this (car.wheel, car.manufacturer); } Rueda de clase estática pública {marca String Brand; }}Tenga en cuenta que el código anterior se implementa como una copia superficial. Si desea implementar una copia profunda, consulte el siguiente código.
// Copiar el automóvil ConstructorPublic (coche de automóvil) {Wheel Wheel = New Wheel (); Wheel.Brand = Car.Wheel.Brand; this.wheel = Wheel; this.Manufacturer = Car.Manufacturer;}Para más conveniencia, también podemos agregar un método estático a la clase anterior
Public Static Car Newinstance (coche de automóvil) {return New Car (automóvil);}Use serializable para lograr una copia profunda
De hecho, se puede lograr una copia profunda de los objetos utilizando la serialización. El código breve es el siguiente
clase pública DeepCopyExample Implementa serializable {privado estático final Long SerialVersionUid = 6098694917984051357l; niño público niño; public DeepCopyExample Copy () {DeepCopyExample copy = null; intente {byteArRaReOutputStream Baos = new ByteArRaReOutputStream (); ObjectOutputStream oos = new ObjectOutputStream (baos); oos.writeObject (esto); ByteArrayInputStream Bais = new ByteArrayInputStream (Baos.TobyTearray ()); ObjectInputStream OIS = new ObjectInputStream (BAIS); copy = (DeepCopyExample) OIS.ReadObject (); } catch (ioException e) {E.PrintStackTrace (); } catch (ClassNotFoundException e) {E.PrintStackTrace (); } return Copy; }}Entre ellos, el niño debe implementar la interfaz serializable
La clase pública implementa serializable {private estático final de serie Long SerialVeUdUid = 6832122780722711261l; Nombre de cadena pública = ""; Public Child (nombre de cadena) {this.name = name; } @Override public string toString () {return "child [name =" + name + "]"; }}Use ejemplos y código de prueba
DeepCopyExample Ejemplo = new DeepCopyExample (); ejemplo.child = nuevo niño ("ejemplo"); DeepCopyExample Copy = ejemplo.Copy (); if (copy! = null) {copy.child.name = "copied"; System.out.println ("Ejemplo.child =" + ejemplo.child + "; copy.child =" + copy.child);} // resultado de salida: ejemplo.child = child [name = ejemplo]; copy.child = child [name = copied]A juzgar por los resultados de la salida, modificar el valor infantil del objeto de copia no afecta el valor infantil del objeto de ejemplo, es decir, usar serialización para lograr una copia profunda del objeto.
Resumir
Lo anterior es todo el contenido de la clonación en Java. Espero que este artículo sea útil para que todos aprendan Java.