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


En java es un poco difícil implementar una función de copia de objetos profundos. ¿Qué pasos toma para asegurarse de que el objeto original y el clonado no comparten ninguna referencia?

Author: Lazer, 2008-09-15

17 answers

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

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

Advertencias: Es posible que las clases anulen la serialización de tal manera que las nuevas instancias sean no creadas, por ejemplo, para singletons. También esto, por supuesto, no funciona si sus clases no son Serializables.

 151
Author: Jason Cohen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-15 15:42:23

Algunas personas han mencionado usar o sobreescribir Object.clone(). No lo hagas. Object.clone() tiene algunos problemas importantes, y su uso se desaconseja en la mayoría de los casos. Por favor vea el Ítem 11, de "Java efectivo" por Joshua Bloch para una respuesta completa. Creo que puede usar Object.clone() de forma segura en matrices de tipo primitivo, pero aparte de eso, debe ser juicioso sobre el uso correcto y la anulación de clones.

Los esquemas que dependen de la serialización (XML u otros) son kludgy.

No hay fácil responde aquí. Si desea copiar en profundidad un objeto, tendrá que recorrer el gráfico de objetos 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 en profundidad el objeto secundario. Los inmutables (por ejemplo, String s) no necesitan ser copiados. Como un aparte, usted debe favorecer la inmutabilidad por esta razón.

 65
Author: Julien Chastang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-13 07:50:46

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

El objeto que desea copiar en profundidad necesitará implement serializable. Si la clase no es final o no se puede modificar, extienda la clase e implemente serializable.

Convierte tu clase a un flujo de bytes:

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

Restaura tu clase desde un flujo de bytes:

ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
(Object) object = (Object) new ObjectInputStream(bais).readObject();
 50
Author: Thargor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-07 13:22:57

Puede hacer un clon profundo basado en serialización usando org.apache.commons.lang3.SerializationUtils.clone(T) en Apache Commons Lang, pero tenga cuidado - la actuación es abismal.

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

 33
Author: user8690,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-09 14:59:52

Una forma de implementar la 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 él. Bastante trabajo, pero bastante sencillo y seguro.

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

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<Order> orders = new ArrayList<Order>();

    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 algún código de reflexión).

 22
Author: Adriaan Koster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-27 07:50:30

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

My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
 17
Author: TheByeByeMan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-28 14:53:24

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ápida que los métodos de serialización).

Cloner cloner = new Cloner();

MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
 16
Author: CorayThan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-13 07:51:08

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));
 11
Author: sankara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-23 08:03:08

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

Http://wiki.fasterxml.com/JacksonInFiveMinutes

 9
Author: Ravi Chinoy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-07 03:51:31

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 la propiedad a la clase XStream. Además, no es necesario implementar la interfaz clonable.

 8
Author: Adi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-02 11:35:00

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

 6
Author: Orion Adrian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-15 15:44:32
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));
    }
}
 6
Author: Eric Leschinski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-21 00:58:35

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

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

public static <T> T Copy(T AnObject, Class<T> 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);

}
 5
Author: tiboo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-19 10:30:33

Usé Dozerpara clonar objetos java y es genial , la biblioteca Kryo es otra gran alternativa.

 4
Author: supernova,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-17 02:08:59

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

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
     return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
 4
Author: Igor Rybak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-19 22:15:18

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 deben implementar la interfaz serilazable

 2
Author: Arun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-11 14:51:53

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

BeanUtils.cloneBean(obj);
 1
Author: Alfergon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-16 10:24:08