Copiar objetos es una operación común en proyectos empresariales. Al copiar un objeto, debemos asegurarnos de terminar con una nueva instancia que contenga los valores que queremos. Los objetos de dominio suelen ser complejos. Hacer una copia con el objeto raíz y los objetos compuestos tampoco es trivial. Exploremos las formas más efectivas de copiar un objeto usando técnicas de copia superficial y profunda. Referencias a objetos Para realizar correctamente una copia superficial o profunda de un objeto, primero debemos saber qué no hacer. Comprender las referencias a objetos es esencial para utilizar técnicas de copia superficial y profunda. Al realizar una copia de un objeto, es importante evitar utilizar la misma referencia de objeto. Es un error fácil, como muestra este ejemplo. Para comenzar, aquí está el objeto Producto que usaremos en nuestros ejemplos: clase pública Producto { nombre de cadena privada; precio doble privado; Producto público (nombre de cadena, precio doble) { this.name = nombre; this.price = precio; } public String getName() { devolver nombre; } public double getPrice() { precio de retorno; } public void setName (nombre de cadena) { this.name = nombre; } public void setPrice(precio doble) { this.price = precio; } } Ahora, creemos y asignemos una referencia de objeto Producto a otra variable. Parece ser una copia, pero en realidad es el mismo objeto: public static void main(String[] args) { Producto producto = nuevo Producto(«Macbook Pro», 3000); Producto copyOfProduct = producto; nombre.producto = «Alienware»; System.out.println(nombre.delproducto); System.out.println(copiaDeProducto.nombre); } El resultado de este código es Alienware Alienware Aviso en el código anterior que asignamos el valor del objeto a una variable local diferente, pero esta variable apunta a la misma referencia de objeto. Si cambiamos los objetos producto o copyOfProduct, el resultado será un cambio en el objeto Producto original. Esto se debe a que cada vez que creamos un objeto en Java, se crea una referencia de objeto en el montón de memoria de Java. Esto nos permite modificar objetos usando sus variables de referencia. Copia superficial La técnica de copia superficial nos permite copiar valores de objetos simples a un nuevo objeto sin incluir los valores internos del objeto. Como ejemplo, aquí se explica cómo utilizar la técnica de copia superficial para copiar el objeto Producto sin utilizar su referencia de objeto: // Se omitió el objeto Producto public class ShallowCopyPassingValues ​​{ public static void main(String[] args) { Producto producto = nuevo Producto(«Macbook Pro», 3000); Producto copyOfProduct = nuevo Producto(product.getName(), product.getPrice()); producto.setName(«Alienware»); System.out.println(product.getName()); System.out.println(copyOfProduct.getName()); } } El resultado es Alienware Macbook Pro. Observe en este código que cuando pasamos los valores de un objeto a otro, se crean dos objetos diferentes en el montón de memoria. Cuando cambiamos uno de los valores en el nuevo objeto, los valores seguirán siendo los mismos en el objeto original. Esto prueba que los objetos son diferentes y que hemos ejecutado con éxito la copia superficial. Nota: El patrón de diseño Builder es otra forma de realizar la misma acción. Copia superficial con Cloneable Desde Java 7, tenemos la interfaz Cloneable en Java. Esta interfaz proporciona otra forma de copiar objetos. En lugar de implementar la lógica de copia manualmente, como acabamos de hacer, podemos implementar la interfaz Cloneable y luego implementar el método clone(). El uso de Cloneable y el método clone() da como resultado automáticamente una copia superficial. No me gusta esta técnica porque arroja una excepción marcada y tenemos que convertir manualmente un tipo de clase, lo que hace que el código sea detallado. Pero usar Cloneable podría simplificar el código si tenemos un objeto de dominio enorme con muchos atributos. Esto es lo que sucede si implementamos la interfaz Cloneable en un objeto de dominio y luego anulamos el método clone(): public class Product implements Cloneable { // Atributos omitidos , métodos y constructor @Override protected Object clone() lanza CloneNotSupportedException { return super.clone(); } } Ahora, aquí está el método de copia en acción nuevamente: public class ShallowCopyWithCopyMethod { public static void main(String[] args) lanza CloneNotSupportedException { Producto producto = nuevo Producto(«Macbook Pro», 3000); Producto copyOfProduct = (Producto) product.clone(); producto.setName(«Alienware»); System.out.println(product.getName()); System.out.println(copyOfProduct.getName()); } } Como puedes ver, el método de copia funciona perfectamente para hacer una copia superficial de un objeto. Usarlo significa que no necesitamos copiar cada atributo manualmente. Copia profunda La técnica de copia profunda es la capacidad de copiar los valores de un objeto compuesto a otro objeto nuevo. Si el objeto Producto contiene el objeto Categoría, por ejemplo, se espera que todos los valores de ambos objetos se copien en un nuevo objeto. ¿Qué sucede si el objeto Producto tiene un objeto compuesto? ¿Funcionará la técnica de copia superficial? Veamos qué sucede si intentamos usar solo el método copy(). Para comenzar, componemos la clase Producto con el objeto Orden: clase pública Producto implementa Cloneable { // Omitimos otros atributos, constructor, captadores y definidores categoría categoría privada; Categoría pública getCategory() { categoría de retorno; } } Ahora, hagamos lo mismo usando el método super.clone(): public class TryDeepCopyWithClone { public static void main(String[] args) lanza CloneNotSupportedException { Categoría categoría = nueva Categoría(«Laptop», «Computadoras portátiles»); Producto producto = nuevo Producto(«Macbook Pro», 3000, categoría); Producto copyOfProduct = (Producto) product.clone(); Categoría copiadaCategory = copyOfProduct.getCategory(); System.out.println(copiedCategory.getName()); } } El resultado es Laptop. Observe que, aunque el resultado es «Laptop», la operación de copia profunda no se realizó. En cambio, lo que sucedió es que tenemos la misma referencia de objeto de categoría. Aquí está la prueba: public class TryDeepCopyWithClone { public static void main(String[] args) throws CloneNotSupportedException { // Mismo código que el ejemplo anterior copyCategory.setName(«Phone»); System.out.println(copiedCategory.getName()); System.out.println(categoría.getName()); } } Salida: Teléfono portátil Teléfono Observe en este código que no se realizó una copia cuando cambiamos el objeto Categoría. En cambio, sólo había una asignación de objeto a una variable diferente. Por lo tanto, cambiaremos el objeto que creamos en el montón de memoria cada vez que cambiemos la variable de referencia. Copia profunda con el método clone() Ahora sabemos que el método clone() no funcionará para una copia profunda si tenemos una simple anular. Veamos cómo podemos hacer que funcione. Primero, implementamos Cloneable en la clase Categoría: clase pública Categoría implementa Cloneable { // Atributos, constructor, captadores y definidores omitidos @Override protected Object clone() throws CloneNotSupportedException { return super.clone( ); } } Ahora, tenemos que cambiar la implementación del método de clonación del Producto también para clonar el objeto Categoría: public class ProductWithDeepCopy implements Cloneable { // Atributos, constructor, captadores y definidores omitidos @Override protected Object clone() throws CloneNotSupportedException { this. categoría = (Categoría) categoría.clon(); devolver super.clon(); } } Si intentamos realizar la copia profunda con el mismo ejemplo de código anterior, obtendremos una copia real de los valores del objeto en un nuevo objeto, como se muestra aquí: public class TryDeepCopyWithClone { public static void main(String[] args) lanza CloneNotSupportedException { Categoría categoría = nueva Categoría(«Laptop», «Computadoras portátiles»); Producto producto = nuevo Producto(«Macbook Pro», 3000, categoría); Producto copyOfProduct = (Producto) product.clone(); Categoría copiadaCategory = copyOfProduct.getCategory(); System.out.println(copiedCategory.getName()); copyCategory.setName(«Teléfono»); System.out.println(copiedCategory.getName()); System.out.println(categoría.getName()); } } El resultado es Laptop Phone Laptop. Dado que copiamos manualmente el método de categoría en el método copy() de Producto, finalmente funciona. Obtendremos una copia de Producto y Categoría utilizando el método copy() de Producto. Este código demuestra que la copia profunda funcionó. Los valores de los objetos originales y copiados son diferentes. Por tanto, no es la misma instancia; es un objeto copiado. Copia superficial con serialización: a veces es necesario serializar un objeto para transformarlo en bytes y pasarlo a través de una red. Esta operación puede ser peligrosa porque si no se valida correctamente, el objeto serializado podría verse explotado. La seguridad de la serialización de Java está fuera del alcance de este artículo, pero veamos cómo funciona con el código. Usaremos la misma clase del ejemplo anterior, pero esta vez implementaremos la interfaz serializable: clase pública Implementos del producto Serializable, Cloneable { // Atributos omitidos, constructor, captadores, definidores y método de clonación } Tenga en cuenta que solo el Producto será serializado ya que solo el Producto implementa Serializable. El objeto Categoría no se serializará. Aquí hay un ejemplo: clase pública ShallowCopySerializable { public static void main(String[] args) { intentar { ByteArrayOutputStream bos = nuevo ByteArrayOutputStream(); ObjectOutputStream out = nuevo ObjectOutputStream(bos); Producto producto = nuevo Producto(«Macbook Pro», 3000); out.writeObject(producto); salida.flush(); fuera.cerrar(); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream en = nuevo ObjectInputStream(bis); Producto clonadoProducto = (Producto) in.readObject(); cercar(); System.out.println(clonedProduct.getName()); Categoría clonedCategory = clonedProduct.getCategory(); System.out.println(categoría clonada); } captura (IOException | ClassNotFoundException e) { e.printStackTrace(); } } } La salida es Macbook Pro null Ahora, si intentáramos llenar el objeto Categoría en el objeto Producto, se generaría la excepción java.io.NotSerializableException. Esto se debe a que el objeto Categoría no implementa Serializable. Copia profunda con serialización. Ahora, veamos qué sucede si usamos el mismo código anterior pero agregamos lo siguiente en la clase Categoría: clase pública Categoría implementa Serializable, Clonable { // Atributos omitidos, constructor , getters, setters y método de clonación // Agregando toString para una buena descripción del objeto @Override public String toString() { return «Category{» + «name=»» + nombre + «\» + «, descripción='» + descripción + ‘\» + ‘}’; } } Al ejecutar el mismo código que la copia superficial serializable, también obtendremos el resultado de Categoría, y el resultado debería ser el siguiente: Macbook Pro Categoría{name=»Laptop», descripción=’Computadoras portátiles’ } Nota: Para explorar más a fondo la serialización de Java, pruebe el desafío del código Java aquí. Conclusión A veces, la técnica de copia superficial es todo lo que necesita para clonar un objeto superficialmente. Pero cuando quieras copiar tanto el objeto como sus objetos internos, debes implementar una copia profunda manualmente. Estas son las conclusiones clave de estas importantes técnicas. Qué recordar acerca de la copia superficial Una copia superficial crea un nuevo objeto pero comparte las referencias de los objetos internos con el objeto original. Los objetos copiados y originales se refieren a los mismos objetos en la memoria. Los cambios realizados en los objetos internos a través de una referencia se reflejarán tanto en los objetos copiados como en los originales. La copia superficial es un proceso simple y eficiente. Java proporciona una implementación predeterminada de copia superficial mediante el método clone(). Qué recordar sobre la copia profunda Una copia profunda crea un nuevo objeto y también crea nuevas copias de sus objetos internos. Los objetos copiados y originales tienen copias independientes de los objetos internos. Los cambios realizados en los objetos internos a través de una referencia no afectarán a la otra. La copia profunda es un proceso más complejo, especialmente cuando se trata de gráficos de objetos o referencias anidadas. La copia profunda debe implementarse explícitamente, ya sea manualmente o mediante bibliotecas o marcos. Esta historia, «Cómo copiar objetos en Java: copia superficial y copia profunda», fue publicada originalmente por JavaWorld. Copyright © 2024 IDG Communications, Inc.

Source link