it-swarm-eu.dev

Wie macht man eine tiefe Kopie eines Objekts in Java?

In Java ist es etwas schwierig, eine Deep Object Copy-Funktion zu implementieren. Welche Schritte müssen Sie ausführen, um sicherzustellen, dass das ursprüngliche Objekt und das geklonte Objekt keine Referenz gemeinsam haben?

277
Andrei Savu

Ein sicherer Weg ist, das Objekt zu serialisieren und dann zu deserialisieren. Dies stellt sicher, dass alles eine brandneue Referenz ist.

Hier ist ein Artikel wie man das effizient macht.

Vorsichtsmaßnahmen: Klassen können die Serialisierung überschreiben, sodass keine neuen Instanzen erstellt werden , z. für Singletons. Dies funktioniert natürlich auch nicht, wenn Ihre Klassen nicht serialisierbar sind.

161
Jason Cohen

Einige Leute haben erwähnt, dass sie Object.clone() verwenden oder überschreiben. Tu es nicht. Object.clone() weist einige schwerwiegende Probleme auf, und von seiner Verwendung wird in den meisten Fällen abgeraten. Eine vollständige Antwort finden Sie in Punkt 11 aus " Effective Java " von Joshua Bloch. Ich glaube, Sie können Object.clone() sicher auf primitiven Arrays verwenden, aber ansonsten müssen Sie überlegt sein, ob Sie Klone richtig verwenden und überschreiben.

Die Schemata, die auf Serialisierung (XML oder anders) basieren, sind kludgy.

Hier gibt es keine einfache Antwort. Wenn Sie ein Objekt tief kopieren möchten, müssen Sie das Objektdiagramm durchlaufen und jedes untergeordnete Objekt explizit über den Kopierkonstruktor des Objekts oder eine statische Factory-Methode kopieren, mit der das untergeordnete Objekt tief kopiert wird. Immutables (z. B. Strings) müssen nicht kopiert werden. Abgesehen davon sollten Sie Unveränderlichkeit aus diesem Grund bevorzugen.

67
Julien Chastang

Sie können eine umfangreiche Kopie mit Serialisierung erstellen, ohne Dateien zu erstellen.

Ihr Objekt, das Sie vertiefen möchten, muss implement serializable. Wenn die Klasse nicht endgültig ist oder nicht geändert werden kann, erweitern Sie die Klasse und implementieren Sie serializable.

Konvertieren Sie Ihre Klasse in einen Stream von Bytes:

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

Stellen Sie Ihre Klasse aus einem Datenstrom von Bytes wieder her:

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

Sie können mit org.Apache.commons.lang3.SerializationUtils.clone(T) in Apache Commons Lang einen auf der Serialisierung basierenden Deep Clone ausführen, aber seien Sie vorsichtig - die Leistung ist miserabel.

Im Allgemeinen empfiehlt es sich, für jede Klasse eines Objekts im Objektdiagramm, das geklont werden muss, eigene Klonmethoden zu schreiben.

37
user8690

Eine Möglichkeit, Deep Copy zu implementieren, besteht darin, jeder zugeordneten Klasse Kopierkonstruktoren hinzuzufügen. Ein Kopierkonstruktor verwendet eine Instanz von 'this' als einzelnes Argument und kopiert alle Werte daraus. Ziemlich viel Arbeit, aber ziemlich unkompliziert und sicher.

BEARBEITEN: Beachten Sie, dass Sie zum Lesen von Feldern keine Zugriffsmethoden verwenden müssen. Sie können direkt auf alle Felder zugreifen, da die Quellinstanz immer vom selben Typ ist wie die Instanz mit dem Kopierkonstruktor. Offensichtlich, könnte aber übersehen werden.

Beispiel:

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

Bearbeiten: Beachten Sie, dass Sie bei Verwendung von Kopierkonstruktoren den Laufzeit-Typ des zu kopierenden Objekts kennen müssen. Mit dem obigen Ansatz können Sie eine gemischte Liste nicht einfach kopieren (möglicherweise können Sie dies mit einem Reflektionscode tun).

23
Adriaan Koster

Apache Commons bietet eine schnelle Möglichkeit, ein Objekt tief zu klonen.

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

Sie können verwenden Sie eine Bibliothek mit einer einfachen API und führen ein relativ schnelles Klonen mit Reflektion durch (sollte schneller sein als Serialisierungsmethoden).

Cloner cloner = new Cloner();

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

XStream ist in solchen Fällen sehr nützlich. Hier ist ein einfacher Code zum Klonen

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

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

Ein sehr einfacher Ansatz besteht darin, mit Jackson JSON komplexe Java Object to JSON zu serialisieren und zurückzulesen.

http://wiki.fasterxml.com/JacksonInFiveMinutes

9
Ravi Chinoy

Für Spring Framework Benutzer. Mit Klasse 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

Verwenden Sie XStream ( http://x-stream.github.io/ ). Sie können sogar steuern, welche Eigenschaften Sie ignorieren können, indem Sie Anmerkungen hinzufügen oder den Eigenschaftsnamen für die XStream-Klasse explizit angeben. Darüber hinaus müssen Sie keine klonbare Schnittstelle implementieren.

8
Adi

Bei komplizierten Objekten und wenn die Leistung nicht wesentlich ist, verwende ich eine JSON-Bibliothek wie gson , um das Objekt in JSON-Text zu serialisieren, und deserialisiere dann den Text, um ein neues Objekt zu erhalten.

gson, das auf Reflexion basiert, funktioniert in den meisten Fällen, außer dass transient Felder nicht kopiert werden und Objekte mit Zirkelbezug mit Ursache 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);
}
7
tiboo

Tiefes Kopieren ist nur mit Zustimmung der jeweiligen Klasse möglich. Wenn Sie die Kontrolle über die Klassenhierarchie haben, können Sie die klonbare Schnittstelle implementieren und die Clone-Methode implementieren. Andernfalls ist das sichere Erstellen einer tiefen Kopie nicht möglich, da das Objekt möglicherweise auch Nicht-Datenressourcen (z. B. Datenbankverbindungen) gemeinsam nutzt. Im Allgemeinen wird jedoch tiefes Kopieren in der Java) - Umgebung als schlechte Praxis angesehen und sollte über die entsprechenden Entwurfspraktiken vermieden werden.

7
Orion Adrian
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

Ich habe Dozer zum Klonen von Java -Objekten verwendet, und das ist großartig. Kryo Bibliothek ist eine weitere großartige Alternative.

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

Hier müssen Ihre MyPerson- und MyAddress-Klasse eine serilazable Schnittstelle implementieren

2
Arun

BeanUtils macht wirklich gute Arbeit beim Klonen von Bohnen.

BeanUtils.cloneBean(obj);
1
Alfergon