it-swarm-es.tech

¿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 objeto profunda. ¿Qué pasos toma para asegurarse de que el objeto original y el clonado no comparten ninguna referencia?

271
Andrei Savu

Una forma segura es serializar el objeto, luego deserializar. Esto asegura que todo es 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 manera tal que las nuevas instancias sean no creadas, por ejemplo. para singletons. Además, esto, por supuesto, no funciona si sus clases no son serializables.

159
Jason Cohen

Algunas personas han mencionado usar o anular 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 Artículo 11, de " Effective Java " por Joshua Bloch para una respuesta completa. Creo que puede usar Object.clone() de forma segura en arreglos de tipo primitivo, pero aparte de eso, debe ser juicioso sobre el uso adecuado y sobreescribir el 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 desea copiar en profundidad 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 en profundidad el objeto secundario. No es necesario copiar los inmutables (por ejemplo, Strings). Como un aparte, debe favorecer la inmutabilidad por esta razón.

67
Julien Chastang

Puede hacer una copia en profundidad con la serialización sin crear archivos.

El objeto que desea copiar en profundidad deberá implement serializable. Si la clase no es definitiva 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 a partir de una secuencia de bytes:

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

Puede hacer un clon profundo basado en serialización usando org.Apache.commons.lang3.SerializationUtils.clone(T) en Apache Commons Lang, pero tenga cuidado, el rendimiento es pésimo.

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 objeto que necesita clonación.

36
user8690

Una forma de implementar 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 utilizar métodos de acceso para leer los 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 copia. Obvio pero puede pasarse 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<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 los constructores de copia, debe conocer 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).

23
Adriaan Koster

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

My_Object object2= org.Apache.commons.lang.SerializationUtils.clone(object1);
18
TheByeByeMan

Puede usar una biblioteca que tiene una API simple y realiza 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
17
CorayThan

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

private static final XStream XSTREAM = new XStream();
...

Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
11
sankara

Un enfoque muy fácil y simple es usar Jackson JSON para serializar el objeto Java complejo a JSON y leerlo.

http://wiki.fasterxml.com/JacksonInFiveMinutes

9
Ravi Chinoy

Para Spring Framework usuarios. 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));
}
8
Igor Rybak

Utilice 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
Adi

Copia profunda solo puede hacerse 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 de clonación. De lo contrario, es imposible hacer una copia profunda de manera segura porque el objeto también puede compartir 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 mediante las prácticas de diseño apropiadas.

7
Orion Adrian

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

gson, que basado en la reflexión funcionará en la mayoría de los casos, excepto que los campos transient no se copiarán y los objetos con referencia circular con la 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);
}
6
tiboo
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
user946968

Usé Dozer para clonar objetos Java y es genial, Kryo library es otra gran alternativa.

4
supernova

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

2
Arun

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

BeanUtils.cloneBean(obj);
1
Alfergon