it-swarm.it

Come si fa una copia profonda di un oggetto in Java?

In Java è un po 'difficile implementare una funzione di copia di oggetti profondi. Quali passi fate per garantire che l'oggetto originale e quello clonato non abbiano alcun riferimento?

273
Andrei Savu

Un modo sicuro è serializzare l'oggetto, quindi deserializzare. Questo assicura che tutto sia un riferimento nuovo di zecca.

Ecco un articolo su come farlo in modo efficiente.

Avvertenze: è possibile che le classi sovrascrivano la serializzazione in modo tale che le nuove istanze siano non create, ad es. per singleton. Anche questo naturalmente non funziona se le tue classi non sono serializzabili.

160
Jason Cohen

Alcune persone hanno menzionato l'uso o l'override di Object.clone(). Non farlo Object.clone() ha alcuni problemi importanti e il suo uso è scoraggiato nella maggior parte dei casi. Per favore, vedi l'Articolo 11, da " Effective Java " di Joshua Bloch per una risposta completa. Credo che si possa usare tranquillamente Object.clone() su array di tipi primitivi, ma a parte questo è necessario essere giudiziosi sull'uso corretto e sull'override del clone.

Gli schemi che si basano sulla serializzazione (XML o altro) sono kludgy.

Non c'è una risposta facile qui. Se si desidera copiare in profondità un oggetto, è necessario attraversare il grafico dell'oggetto e copiare esplicitamente ciascun oggetto figlio tramite il costruttore di copie dell'oggetto o un metodo factory statico che a sua volta copia in profondità l'oggetto figlio. Immutables (ad esempio Strings) non ha bisogno di essere copiato. Per inciso, dovresti favorire l'immutabilità per questo motivo.

67
Julien Chastang

È possibile eseguire una copia approfondita con la serializzazione senza creare file.

Il tuo oggetto che desideri copiare in profondità avrà bisogno di implement serializable. Se la classe non è definitiva o non può essere modificata, estendi la classe e implementa serializzabile.

Converti la tua classe in un flusso di byte:

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

Ripristina la tua classe da un flusso di byte:

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

Puoi fare un clone profondo basato sulla serializzazione usando org.Apache.commons.lang3.SerializationUtils.clone(T) in Apache Commons Lang, ma fai attenzione: le prestazioni sono pessime.

In generale, è consigliabile scrivere i propri metodi di clonazione per ogni classe di un oggetto nel grafico degli oggetti che richiede la clonazione.

36
user8690

Un modo per implementare la copia profonda è aggiungere i costruttori di copia a ciascuna classe associata. Un costruttore di copie prende un'istanza di "this" come argomento singolo e copia tutti i valori da esso. Un bel po 'di lavoro, ma piuttosto semplice e sicuro.

EDIT: si noti che non è necessario utilizzare i metodi Accessor per leggere i campi. È possibile accedere direttamente a tutti i campi poiché l'istanza di origine è sempre dello stesso tipo dell'istanza con il costruttore di copie. Ovvio ma potrebbe essere trascurato.

Esempio:

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;
    }
}

Modifica: si noti che quando si usano i costruttori di copie è necessario conoscere il tipo di runtime dell'oggetto che si sta copiando. Con l'approccio di cui sopra non è possibile copiare facilmente un elenco misto (potrebbe essere possibile farlo con un codice di riflessione).

23
Adriaan Koster

Apache commons offre un modo rapido per clonare in profondità un oggetto.

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

È possibile utilizzare una libreria che ha una semplice API ed esegue una clonazione relativamente veloce con reflection (dovrebbe essere più veloce dei metodi di serializzazione).

Cloner cloner = new Cloner();

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

XStream è davvero utile in questi casi. Ecco un semplice codice per fare la clonazione

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

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

Un approccio molto semplice e semplice è quello di utilizzare Jackson JSON per serializzare complessi oggetti Java su JSON e leggerli di nuovo.

http://wiki.fasterxml.com/JacksonInFiveMinutes

9
Ravi Chinoy

Per Spring Framework utenti. Utilizzando la classe 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

Usa XStream ( http://x-stream.github.io/ ). È anche possibile controllare quali proprietà è possibile ignorare tramite annotazioni o specificare esplicitamente il nome della proprietà nella classe XStream. Inoltre non è necessario implementare l'interfaccia clonabile.

8
Adi

La copia profonda può essere effettuata solo con il consenso di ogni classe. Se si ha il controllo sulla gerarchia delle classi, è possibile implementare l'interfaccia clonabile e implementare il metodo Clone. Altrimenti fare una copia profonda è impossibile da fare in sicurezza perché l'oggetto potrebbe condividere anche risorse non di dati (ad esempio connessioni a database). In generale, tuttavia, la copia profonda è considerata una cattiva pratica nell'ambiente Java e dovrebbe essere evitata tramite le pratiche di progettazione appropriate.

7
Orion Adrian

Per oggetti complicati e quando le prestazioni non sono significative io uso una libreria json, come gson per serializzare l'oggetto in testo json, quindi deserializzare il testo per ottenere nuovo oggetto.

gson, che in base alla riflessione funziona nella maggior parte dei casi, con la differenza che i campi transient non verranno copiati e gli oggetti con riferimento circolare 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);
}
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

Ho usato Dozer per clonare oggetti Java ed è fantastico, Kryo la biblioteca è un'altra grande 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);

Qui tu sei la mia persona e la classe MyAddress deve implementare un'interfaccia serializzabile

2
Arun

BeanUtils fa un buon lavoro di clonazione dei fagioli.

BeanUtils.cloneBean(obj);
1
Alfergon