373 Stimmen

Wie erstellt man eine Tiefenkopie eines Objekts?

Es ist etwas schwierig, eine tiefe Objektkopierfunktion zu implementieren. Wie stellen Sie sicher, dass das Originalobjekt und das geklonte Objekt keine gemeinsame Referenz haben?

192voto

Jason Cohen Punkte 78227

Ein sicherer Weg ist, das Objekt zu serialisieren und dann zu deserialisieren. Dadurch wird sichergestellt, dass alles eine brandneue Referenz ist.

Hier ist ein Artikel darüber, wie man dies effizient tun kann.

Vorbehalte: Es ist möglich, dass Klassen die Serialisierung außer Kraft setzen, so dass neue Instanzen no erstellt, z.B. für Singletons. Auch dies funktioniert natürlich nicht, wenn Ihre Klassen nicht serialisierbar sind.

81voto

Julien Chastang Punkte 17256

Einige Leute haben erwähnt, dass die Verwendung oder Aufhebung von Object.clone() . Tun Sie es nicht. Object.clone() hat einige große Probleme, und in den meisten Fällen wird von seiner Verwendung abgeraten. Siehe Punkt 11, aus " Leistungsfähiges Java " von Joshua Bloch für eine vollständige Antwort. Ich glaube, Sie können sicher verwenden Object.clone() auf Arrays des primitiven Typs, aber abgesehen davon müssen Sie bei der korrekten Verwendung und Überschreibung von clone vorsichtig sein.

Die Schemata, die sich auf Serialisierung (XML oder anderweitig) stützen, sind unübersichtlich.

Hier gibt es keine einfache Antwort. Wenn Sie ein Objekt tief kopieren wollen, müssen Sie den Objektgraphen durchlaufen und jedes Kindobjekt explizit über den Kopierkonstruktor des Objekts oder eine statische Fabrikmethode kopieren, die wiederum das Kindobjekt tief kopiert. Immutables (z.B.. String s) müssen nicht kopiert werden. Nebenbei bemerkt, sollten Sie aus diesem Grund die Unveränderbarkeit bevorzugen.

63voto

Thargor Punkte 1862

Sie können mit der Serialisierung eine Deep Copy erstellen, ohne Dateien zu erzeugen.

Ihr Objekt, das Sie tief kopieren 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 Strom 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 Strom von Bytes wieder her:

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

49voto

user8690 Punkte 451

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

Im Allgemeinen ist es am besten, für jede Klasse eines Objekts im Objektgraphen, das geklont werden muss, eigene Klonmethoden zu schreiben.

32voto

Adriaan Koster Punkte 15224

Eine Möglichkeit, Deep Copy zu implementieren, besteht darin, jeder zugehörigen Klasse Kopierkonstruktoren hinzuzufügen. Ein Kopierkonstruktor nimmt eine Instanz von "this" als einziges Argument und kopiert alle Werte daraus. Das ist ziemlich viel Arbeit, aber ziemlich einfach und sicher.

EDIT: Beachten Sie, dass Sie zum Lesen von Feldern keine Accessor-Methoden verwenden müssen. Sie können auf alle Felder direkt zugreifen, da die Quellinstanz immer vom gleichen Typ ist wie die Instanz mit dem Kopierkonstruktor. Offensichtlich, wird aber vielleicht übersehen.

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 Kopierkonstruktoren die Vererbung nicht berücksichtigen. Zum Beispiel: Wenn Sie eine OnlineOrder (eine Unterklasse von Order) an einen Kopierkonstruktor übergeben, wird eine reguläre Order-Instanz in der Kopie erstellt, es sei denn, Sie lösen dies explizit. Sie könnten Reflection verwenden, um einen Kopierkonstruktor im Laufzeittyp des Arguments nachzuschlagen. Ich würde jedoch vorschlagen, diesen Weg nicht zu gehen und nach einer anderen Lösung zu suchen, wenn die Vererbung allgemein abgedeckt werden soll.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X