¿Cómo se hace una copia profunda de un objeto en Java?

En Java, es un poco difícil implementar una función de copia profunda de objetos. ¿Qué pasos debe seguir para garantizar que el objeto original y el clonado no compartan ninguna referencia?

Una forma segura es serializar el objeto, luego deserializarlo. Esto asegura que todo sea una referencia nueva.

Aquí hay un artículo sobre cómo hacer esto de manera eficiente.

Advertencias: Es posible que las clases anulen la serialización de manera que no se creen nuevas instancias, por ejemplo, para singletons. También esto, por supuesto, no funciona si tus clases no son serializables.

Algunas personas han mencionado usar o reemplazar Object.clone() . No lo hagas Object.clone() tiene algunos problemas importantes, y su uso se desaconseja en la mayoría de los casos. Consulte el Ítem 11, de ” Java efectivo ” de Joshua Bloch para obtener una respuesta completa. Creo que puedes usar Object.clone() forma segura en matrices de tipo primitivo, pero aparte de eso, debes ser prudente sobre el uso y sobreescritura del clon.

Los esquemas que se basan en la serialización (XML o de otro tipo) son kludgy.

No hay una respuesta fácil aquí. Si quiere copiar profundamente un objeto, tendrá que recorrer el gráfico del objeto y copiar cada objeto secundario explícitamente a través del constructor de copia del objeto o un método de fábrica estático que a su vez copia profundamente el objeto secundario. Inmutables (por ejemplo, String s) no necesitan ser copiados. Como un aparte, debes favorecer la inmutabilidad por este motivo.

Puede hacer una copia profunda con serialización sin crear archivos.

Su objeto que desea copiar profundamente tendrá que implement serializable . Si la clase no es definitiva o no se puede modificar, amplíe la clase e implemente serializable.

Convierte tu clase a una secuencia de bytes:

 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(object); oos.flush(); oos.close(); bos.close(); byte[] byteData = bos.toByteArray(); 

Restaure su clase de una secuencia de bytes:

 ByteArrayInputStream bais = new ByteArrayInputStream(byteData); (Object) object = (Object) new ObjectInputStream(bais).readObject(); 

Puedes hacer una clonación profunda basada en serialización utilizando org.apache.commons.lang3.SerializationUtils.clone(T) en Apache Commons Lang, pero ten cuidado: el rendimiento es abismal.

En general, es una buena práctica escribir sus propios métodos de clonación para cada clase de un objeto en el gráfico de objetos que necesita clonar.

Una forma de implementar una copia profunda es agregar constructores de copia a cada clase asociada. Un constructor de copia toma una instancia de ‘this’ como su único argumento y copia todos los valores de la misma. Un poco de trabajo, pero bastante sencillo y seguro.

EDITAR: tenga en cuenta que no necesita utilizar métodos de acceso para leer campos. Puede acceder a todos los campos directamente porque la instancia de origen siempre es del mismo tipo que la instancia con el constructor de copias. Obvio, pero podría ser pasado por alto.

Ejemplo:

 public class Order { private long number; public Order() { } /** * Copy constructor */ public Order(Order source) { number = source.number; } } public class Customer { private String name; private List orders = new ArrayList(); public Customer() { } /** * Copy constructor */ public Customer(Customer source) { name = source.name; for (Order sourceOrder : source.orders) { orders.add(new Order(sourceOrder)); } } public String getName() { return name; } public void setName(String name) { this.name = name; } } 

Editar: Tenga en cuenta que al usar constructores de copia, necesita saber el tipo de tiempo de ejecución del objeto que está copiando. Con el enfoque anterior, no puede copiar fácilmente una lista mixta (es posible que pueda hacerlo con un código de reflexión).

Apache commons ofrece una forma rápida de clonar un objeto en profundidad.

 My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1); 

Puede usar una biblioteca que tenga una API simple y realice una clonación relativamente rápida con reflexión (debería ser más rápido que los métodos de serialización).

 Cloner cloner = new Cloner(); MyClass clone = cloner.deepClone(o); // clone is a deep-clone of o 

XStream es realmente útil en tales casos. Aquí hay un código simple para hacer clonación

 private static final XStream XSTREAM = new XStream(); ... Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj)); 

Un enfoque muy fácil y simple es usar Jackson JSON para serializar objetos complejos de Java a JSON y leerlos de nuevo.

http://wiki.fasterxml.com/JacksonInFiveMinutes

Use XStream ( http://x-stream.github.io/ ). Incluso puede controlar qué propiedades puede ignorar a través de anotaciones o especificando explícitamente el nombre de propiedad a la clase XStream. Además, no es necesario implementar una interfaz clonable.

La copia profunda solo puede hacerse con el consentimiento de cada clase. Si tiene control sobre la jerarquía de clases, entonces puede implementar la interfaz clonable e implementar el método Clone. De lo contrario, es imposible hacer una copia en profundidad porque el objeto también puede estar compartiendo recursos que no son de datos (por ejemplo, conexiones de bases de datos). En general, sin embargo, la copia profunda se considera una mala práctica en el entorno de Java y debe evitarse mediante las prácticas de diseño apropiadas.

 import com.thoughtworks.xstream.XStream; public class deepCopy { private static XStream xstream = new XStream(); //serialize with Xstream them deserialize ... public static Object deepCopy(Object obj){ return xstream.fromXML(xstream.toXML(obj)); } } 

Utilicé Dozer para clonar objetos Java y es genial, la biblioteca Kryo es otra gran alternativa.

Para los usuarios de Spring Framework . Usando la clase org.springframework.util.SerializationUtils :

 @SuppressWarnings("unchecked") public static  T clone(T object) { return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object)); } 

Para objetos complicados y cuando el rendimiento no es significativo, uso una biblioteca json, como gson, para serializar el objeto en texto json, luego deserializar el texto para obtener un nuevo objeto.

gson que, en función de la reflexión, funcionará en la mayoría de los casos, excepto que transient campos transient no se copiarán y los objetos con referencia circular con la causa StackOverflowError .

 public static  T Copy(T AnObject, Class ClassInfo) { Gson gson = new GsonBuilder().create(); String text = gson.toJson(AnObject); T newObject = gson.fromJson(text, ClassInfo); return newObject; } public static void main(String[] args) { String originalObject = "hello"; String copiedObject = Copy(originalObject, String.class); } 

1)

 public static Object deepClone(Object object) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(object); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); } catch (Exception e) { e.printStackTrace(); return null; } } 2) // (1) create a MyPerson object named Al MyAddress address = new MyAddress("Vishrantwadi ", "Pune", "India"); MyPerson al = new MyPerson("Al", "Arun", address); // (2) make a deep clone of Al MyPerson neighbor = (MyPerson)deepClone(al); 

Aquí su clase MyPerson y MyAddress debe implementar una interfaz serilazable

BeanUtils hace un muy buen trabajo de clonación profunda de frijoles.

 BeanUtils.cloneBean(obj);