926 Stimmen

Wie kopiere ich ein Objekt in Java?

Betrachten Sie den folgenden Code:

DummyBean dum = new DummyBean();
dum.setDummy("foo");
System.out.println(dum.getDummy()); // gibt 'foo' aus

DummyBean dumtwo = dum;
System.out.println(dumtwo.getDummy()); // gibt 'foo' aus

dum.setDummy("bar");
System.out.println(dumtwo.getDummy()); // gibt 'bar' aus, aber es sollte 'foo' ausgeben

Also möchte ich dum nach dumtwo kopieren und dum ändern, ohne dumtwo zu beeinflussen. Aber der obige Code tut das nicht. Wenn ich etwas in dum ändere, geschieht die gleiche Änderung auch in dumtwo.

Ich vermute, wenn ich dumtwo = dum sage, kopiert Java nur die Referenz. Gibt es also einen Weg, eine frische Kopie von dum zu erstellen und sie dumtwo zuzuweisen?

704voto

egaga Punkte 20134

Erstellen Sie einen Kopierkonstruktor:

class DummyBean {
  private String dummy;

  public DummyBean(DummyBean another) {
    this.dummy = another.dummy; // Sie können darauf zugreifen  
  }
}

Jedes Objekt hat auch eine clone-Methode, die verwendet werden kann, um das Objekt zu kopieren, aber verwenden Sie sie nicht. Es ist viel zu einfach, eine Klasse zu erstellen und eine falsche clone-Methode zu erstellen. Wenn Sie das tun werden, lesen Sie zumindest, was Joshua Bloch dazu in _Effective Java_ zu sagen hat.

46 Stimmen

Aber dann müsste er seinen Code ändern zu DummyBean two = new DummyBean(one); Richtig?

13 Stimmen

Erfüllt diese Methode effektiv das gleiche wie eine Deep-Copy?

140 Stimmen

@MatthewPiziak, für mich wäre dies kein Deep-Clone, da alle verschachtelten Objekte immer noch auf die Originalquellinstanz verweisen würden, nicht auf eine Duplikation, es sei denn, jedes Referenzobjekt (nicht Werttyp) liefert dieselbe Konstruktorvorlage wie oben.

457voto

Chandra Sekhar Punkte 15788

Grundlegend: Objektkopieren in Java.

Angenommen, ein Objekt - obj1, das zwei Objekte enthält, containedObj1 und containedObj2.
Bildbeschreibung hier eingeben

flaches Kopieren:
Flaches Kopieren erstellt eine neue Instanz derselben Klasse und kopiert alle Felder in die neue Instanz und gibt sie zurück. Die Object-Klasse bietet eine clone-Methode und unterstützt das flache Kopieren.
Bildbeschreibung hier eingeben

Tiefes Kopieren:
Ein tiefes Kopieren erfolgt, wenn ein Objekt zusammen mit den Objekten, auf die es verweist, kopiert wird. Im folgenden Bild wird obj1 nach einem tiefen Kopieren gezeigt. Nicht nur wurde obj1 kopiert, sondern auch die Objekte darin wurden kopiert. Wir können Java Object Serialization verwenden, um ein tiefes Kopieren durchzuführen. Leider hat dieser Ansatz auch einige Probleme (detaillierte Beispiele).
Bildbeschreibung hier eingeben

Mögliche Probleme:
clone ist schwierig korrekt zu implementieren.
Es ist besser, Defensive Copying, Kopierkonstruktoren (wie in der Antwort von @egaga) oder statische Fabrikmethoden zu verwenden.

  1. Wenn Sie ein Objekt haben, von dem Sie wissen, dass es eine öffentliche clone()-Methode hat, aber Sie den Typ des Objekts zur Kompilierzeit nicht kennen, dann haben Sie ein Problem. Java hat ein Interface namens Cloneable. Praktisch gesehen sollten wir dieses Interface implementieren, wenn wir ein Objekt Cloneable machen wollen. Object.clone ist protected, daher müssen wir es mit einer öffentlichen Methode überschreiben, damit es zugänglich ist.

  2. Ein weiteres Problem entsteht, wenn wir versuchen, ein tiefes Kopieren eines komplexen Objekts durchzuführen. Angenommen, die clone()-Methode aller Objektvariablen für Mitglieder führt ebenfalls ein tiefes Kopieren durch, dies ist eine zu riskante Annahme. Sie müssen den Code in allen Klassen kontrollieren.

Zum Beispiel wird org.apache.commons.lang.SerializationUtils eine Methode für das Tiefe Klonen mit Serialisierung haben (Quelle). Wenn wir ein Bean klonen müssen, gibt es ein paar Hilfsmethoden in org.apache.commons.beanutils (Quelle).

  • cloneBean wird ein Bean auf der Grundlage der verfügbaren Eigenschaften-Getter und -Setter klonen, auch wenn die Bean-Klasse selbst nicht Cloneable implementiert.
  • copyProperties wird Eigenschaftswerte vom Ursprungs-Bean auf das Ziel-Bean kopieren, wenn die Eigenschaftsnamen übereinstimmen.

1 Stimmen

Kannst du bitte erklären, was ein Objekt innerhalb eines anderen enthält?

1 Stimmen

@Chandra Sekhar "Flaches Kopieren erstellt eine neue Instanz derselben Klasse und kopiert alle Felder in die neue Instanz und gibt sie zurück", das stimmt nicht, um alle Felder zu erwähnen, weil Objekte nicht kopiert werden, nur die Verweise werden kopiert, die auf dasselbe Objekt verweisen, auf das das alte (ursprüngliche) zeigt.

4 Stimmen

@sunny - Die Beschreibung von Chandra ist richtig. Und auch deine Beschreibung dessen, was passiert, ist richtig; ich sage, dass du ein falsches Verständnis davon hast, was "kopiert alle Felder" bedeutet. Das Feld ist die Referenz, es ist nicht das Objekt, auf das verwiesen wird. "alle Felder kopieren" bedeutet "alle diese Verweise kopieren". Es ist gut, dass du genau darauf hingewiesen hast, was das genau bedeutet, für alle anderen, die die gleiche Fehlinterpretation wie du haben, in Bezug auf die Aussage "alle Felder kopieren". :)

166voto

pacheco Punkte 1527

Im Paket import org.apache.commons.lang.SerializationUtils; gibt es eine Methode:

SerializationUtils.clone(Object);

Beispiel:

this.myObjectCloned = SerializationUtils.clone(this.object);

84 Stimmen

Solange das Objekt Serializable implementiert

2 Stimmen

In diesem Fall hat das geklonte Objekt keine Referenz zum Original, wenn letzteres statisch ist.

12 Stimmen

Eine Bibliothek von Drittanbietern nur zum Klonen von Objekten!

113voto

Bhasker Tiwari Punkte 1099

Einfach wie folgt vorgehen:

public class Deletable implements Cloneable{

    private String str;
    public Deletable(){
    }
    public void setStr(String str){
        this.str = str;
    }
    public void display(){
        System.out.println("Der String ist "+str);
    }
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

und überall dort, wo Sie ein weiteres Objekt erhalten möchten, einfach eine Klonung durchführen. z. B.:

Deletable del = new Deletable();
Deletable delTemp = (Deletable ) del.clone(); // Diese Zeile liefert Ihnen ein unabhängiges
                                 // Objekt, Änderungen an diesem Objekt werden
                                 // nicht auf das andere Objekt übertragen

2 Stimmen

Hast du das getestet? Ich könnte das für mein Projekt gebrauchen und es ist wichtig, dass es korrekt ist.

3 Stimmen

@misty Ich habe es getestet. Funktioniert einwandfrei auf meiner Produktions-App

2 Stimmen

Nach dem Klonen, wenn Sie das Originalobjekt ändern, ändert es auch den Klon.

47voto

WillingLearner Punkte 729

Warum gibt es keine Antwort auf die Verwendung der Reflection API?

private static Object cloneObject(Object obj){
        try{
            Object clone = obj.getClass().newInstance();
            for (Field field : obj.getClass().getDeclaredFields()) {
                field.setAccessible(true);
                field.set(clone, field.get(obj));
            }
            return clone;
        }catch(Exception e){
            return null;
        }
    }

Es ist wirklich einfach.

BEARBEITEN: Kindobjekt über Rekursion einbeziehen

private static Object cloneObject(Object obj){
        try{
            Object clone = obj.getClass().newInstance();
            for (Field field : obj.getClass().getDeclaredFields()) {
                field.setAccessible(true);
                if(field.get(obj) == null || Modifier.isFinal(field.getModifiers())){
                    continue;
                }
                if(field.getType().isPrimitive() || field.getType().equals(String.class)
                        || field.getType().getSuperclass().equals(Number.class)
                        || field.getType().equals(Boolean.class)){
                    field.set(clone, field.get(obj));
                }else{
                    Object childObj = field.get(obj);
                    if(childObj == obj){
                        field.set(clone, clone);
                    }else{
                        field.set(clone, cloneObject(field.get(obj)));
                    }
                }
            }
            return clone;
        }catch(Exception e){
            return null;
        }
    }

0 Stimmen

Dies sieht viel besser aus, aber Sie müssen nur die finalen Felder in Betracht ziehen, da setAccessible(true) fehlschlagen könnte. Vielleicht müssen Sie die Ausnahme IllegalAccessException separat behandeln, die auftritt, wenn field.set(clone, field.get(obj)) aufgerufen wird.

1 Stimmen

Ich mochte es so sehr, aber konnen Sie es umgestalten, um Generika zu verwenden? private static T cloneObject(T obj) { .... }

2 Stimmen

Ich denke, es handelt sich um ein Problem, wenn wir Referenzen von Eigenschaften auf deren Eltern haben: Klasse A { B child; } Klasse B{ A parent; }

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