Este artículo discutirá los siguientes 4 temas
1. Java La interfaz clonable implementa una copia profunda
2. La serialización de Java implementa una copia profunda
3. El análisis del código fuente de clonación de la biblioteca binaria de copia profunda más rápida
4. Comparación de velocidades de varios métodos de copia
No hablaré sobre el concepto de copia profunda en este artículo. Implementación de una copia profunda en C ++. En general, sobrecarga el operador de asignación "=" para implementar una copia profunda entre objetos de la misma clase. Por lo tanto, es natural que en Java también podamos definir una función de copia para asignar cada propiedad del objeto dentro de la función. Este método es simple y natural, pero hay un problema fatal: si algún día se agrega un nuevo atributo que requiere una copia profunda a la clase, la función de copia correspondiente también debe modificarse. Este método trae grandes inconvenientes a la extensibilidad de la clase. Cómo resolver este problema, veamos el método de implementación de los capítulos 1, 2 y 3 y la prueba de velocidad de la Sección 4.
1. La interfaz clonable Java implementa una copia profunda <Br /> De esta manera, la clase necesita implementar la función de clon de interfaz colnable y llamar a Super.Clone en la función clon. Esta copia profunda del método también trae otro problema. Si hay objetos de otras clases como propiedades en la clase, otras clases también deben ser sobrecargadas e implementadas en la interfaz clonable. Aquí hay un ejemplo. En el siguiente ejemplo, ComplexDO contiene objetos simpledo. Para implementar complejo de copia profunda, primero debe implementar la interfaz clon de Simpledo:
La clase pública Simpledo implementa clonable, serializable {private int x = 1; cadena privada s = "simpledo"; @Override Protected Object clone () lanza clonenotsupportedException {simpledo newclass = (simpledo) super.clone (); devolver newclass; }} Class Public COMPLECTDO implementa clonable, serializable {private int x = 1; cadena privada s = "complejo"; entero privado a = 123; entero privado b = 1234; entero privado C = 1334455; cadena privada s2 = "jehehe"; cadena privada s3 = "jajaja"; ID de largo privado = 1233245L; Private ArrayList <Simpledo> L = New ArrayList <Simpledo> (); @Override Public Object clone () lanza ClonenOtsupportedException {complejDo newclass = (complejo) super.clone (); newclass.l = new ArrayList <Simpledo> (); para (simpledo simple: this.l) {newclass.l.add ((simpledo) simple.clone ()); } return newClass; }} Cabe señalar que muchos artículos dicen que el operador de asignación del tipo de cadena es una copia profunda, pero de hecho, aquellos que usan operadores de asignación en Java son copias superficiales, pero ¿por qué los artículos con errores tan obvios tienen que decir que esta es una copia profunda? Entiendo que los atributos de cadena y tipo son tipos básicos, y el método proporcionado será nuevo objetos siempre que los cambios de datos internos estén diseñados. Por lo tanto, una operación de cadena no afectará la memoria a la que señaló originalmente. Por lo tanto, en términos generales, las operaciones de asignación de clases básicas como la cadena son copias profundas.
Por esta razón, cuando se usa el empalme de cadena de cadenas, se debe abrir una nueva memoria, por lo que muchas personas recomiendan usar StringBuilder en lugar de una cadena para empalme, porque StringBuilder solo vuelve a aplicar memoria más grande cuando la gama de matriz de char incorporado no es suficiente (para los JVM modernos, el código se ajustará, y String+String se optimizará en instrucciones similares para StringBuUrid. Para recortar opuesto al empalme, hay una función de subcadena en la cadena. Cuando se usa la función de subcadena, ¿la matriz de caracteres interno de la nueva cadena es igual a la cadena original? Esto es más interesante. Si está interesado, puede comparar y verificar la implementación de JDK1.6 y JKD1.7.
2. La serialización de Java implementa una copia profunda
El principio de este método es usar la serialización de Java para serializar un objeto en una corriente de byte binaria, y luego deserializar y asignar el valor a un objeto. Ejemplo de código:
Object public seircopy (Object src) {try {byteArRayOutputStream byteout = new ByteArRayOutputStream (); ObjectOutputStream out = new ObjectOutputStream (byteout); out.writeObject (SRC); ByteArrayInputStream bytein = new byteArrayInputStream (byteout.tobytearray ()); ObjectInputStream in = new ObjectInputStream (bytein); Objeto dest = in.readObject (); regresar des; } Catch (Exception e) {// Haz un retorno de controlador de errores NULL; }} Por supuesto, también puede usar JSON y otras bibliotecas serializadas para completar la serialización. Este método evita de manera efectiva las deficiencias extensibles de la interfaz CloneAbel. Una función básicamente puede ser adecuada para todas las clases. La desventaja es que es una copia de memoria relativa. La serialización requiere primero convertir el objeto en una corriente de byte binaria, y luego deserializar la recopilación de la corriente de byte binaria a una pieza de memoria del objeto, que es relativamente lento.
3. El análisis del código fuente de clonación de la biblioteca binaria de copia profunda más rápida
En el código fuente, la lógica de procesamiento central está en la clase Cloner.
Hay dos enlaces recursivos:
En (1), FastClone completa los objetos heredados de la clase de interfaz ifastClone, es decir, son todas copias de operaciones de recolección;
En (2), CloneObject completa el proceso de obtención de cada propiedad del objeto normal a través del mecanismo de reflexión y luego asigna valores a las propiedades del objeto recién generado usando objénesis.
Este método es altamente extensible. No solo puede confiar en su código existente para completar la copia profunda, sino que también puede definir algunos métodos y tipos de clonación que no requieren clonación, lo cual es altamente flexible.
4. Comparación de velocidades de varios métodos de copia
Los tres modos anteriores se pueden usar para completar la copia profunda, y el método de copia más rápido es lo que nos importa.
Primero, prueba el código:
public void testClonecomplex () lanza ClonenotsupportedException {final int ciptount = 1; List <COPPLEPDO> ComplexDolist = new ArrayList <POXCPELDO> (COPIECOUNT * 3); complejo final de complejo = new ComplexDo (); // Calculando la biblioteca de dos partes Long Start = System.CurrentTimemillis (); for (int i = 0; i <copyCount; ++ i) {final compleDo profundo = clner.deepclone (complejo); Complexdolist.add (DeepClone); } Long End = System.CurrentTimemillis (); System.out.println ("DeepClone Costo Time =" + (End-start)); // llamando a la función clon implementada por la interfaz clonable start = system.currentTimemillis (); for (int i = 0; i <copyCount; ++ i) {final compleDO interfaceclone = (complejDo) comple.clone (); Complexdolist.add (interfaceclone); } end = System.CurrentTimemillis (); System.out.println ("Interfaceclone cost time =" + (end-start)); // La serialización y la deserialización generan un nuevo objeto start = System.CurrentTimemillis (); for (int i = 0; i <copyCount; ++ i) {final complejo seirclone = seircopy (complejo); Complexdolist.add (Seirclone); } end = System.CurrentTimemillis (); System.out.println ("SeirClone Costo Time =" + (End-start)); }La unidad de resultados de ejecución es milisegundos (estos datos se ignoran y no calculan los puntos de acceso Java y posibles GC).
De esta tabla, podemos sacar la conclusión:
1. La copia de la interfaz clonable es la más rápida, porque solo involucra copias de memoria, pero si los atributos involucrados son objetos más comunes, es un poco problemático escribir.
2. La copia de serialización/deserialización es la más lenta
3. Usando la biblioteca de clonación, la copia del mecanismo de recursión y reflexión es más lento que la implementación de la interfaz clonable, pero más rápido que el método de serialización.
Lo anterior se trata de este artículo, espero que sea útil para el aprendizaje de todos.