1. Einführung
Die Objektkopie besteht darin, die Eigenschaften eines Objekts mit demselben Klassentyp in ein anderes Objekt zu kopieren. Es ist üblich, Objekte in Programmen zu kopieren, hauptsächlich, um einige oder alle Daten des Objekts in einem neuen Kontext wiederzuverwenden. In Java gibt es drei Arten von Objektkopien: Flache Kopie, Deep Copy und Delay Copy.
2. Leichte Kopie
1. Was ist eine flache Kopie
Eine flache Kopie ist ein Bitgewise Copy -Objekt, das ein neues Objekt mit einer genauen Kopie des Attributwerts des ursprünglichen Objekts erstellt. Wenn das Attribut ein Grundtyp ist, wird der Wert des Grundtyps kopiert. Anderes Objekt.
In der Abbildung hat SourceObject ein Int -Attribut "Feld1" und ein Referenztyp -Attribut "Refobj" (referenziert ein Objekt vom Typ Typ enthüllt. Bei einer flachen Kopie von SourceObject wird ein CopieDObject erstellt, der eine Eigenschaft "Feld2" enthält, die den Kopienwert von "Feld1" enthält, und eine Referenz, die noch auf Refan selbst zeigt. Da "field1" ein primitiver Typ ist, kopieren Sie einfach seinen Wert in "field2", aber da "Refobj" ein Referenztyp ist, verweist CopiedObject auf dieselbe Adresse wie "Refobj". Daher wirkt sich alle Änderungen an "Refobj" in SourceObject auf das CopiedObject aus.
2. wie man eine flache Kopie erreicht
Hier ist ein Beispiel für die Implementierung einer flachen Kopie
Die Codekopie lautet wie folgt:
Fach der öffentlichen Klasse {
privater Zeichenfolge Name;
öffentliches Thema (String s) {
name = s;
}
public String getName () {
Rückgabename;
}
public void setName (String s) {
name = s;
}
}
Schüler der öffentlichen Klasse implementiert Klonable {
// Objektreferenz
privates Subj;
privater Zeichenfolge Name;
public student (String s, String sub) {
name = s;
subJ = neues Subjekt (sub);
}
öffentliches Thema getubj () {
Subj zurückgeben;
}
public String getName () {
Rückgabename;
}
public void setName (String s) {
name = s;
}
/**
* Schreiben Sie die Clone () -Methode neu
* @zurückkehren
*/
öffentliches Objekt Clone () {
// flache Kopie
versuchen {
// Rufen Sie direkt die Clone () -Methode der übergeordneten Klasse auf
return Super.clone ();
} catch (clonenotsuptedEdException e) {
null zurückkehren;
}
}
}
öffentliche Klasse CopyTest {
public static void main (String [] args) {
// Originalobjekt
Student Stud = New Student ("John", "Algebra");
System.out.println ("Originalobjekt:" + stud.getName () + " -" + stud.getSubj (). GetName ());
// das Objekt kopieren
Student klonedstud = (student) stud.clone ();
System.out.println ("kloniertes Objekt:" + klonedstud.getName () + " -" + klonedstud.getSubj (). GetName ());
// sind das ursprüngliche Objekt und das gleiche Kopieobjekt gleich:
System.out.println ("ist das ursprüngliche Objekt mit geklonter Objekt:" + (stud == klonedstud));
// sind die Namenseigenschaften des ursprünglichen Objekts und das gleiche Kopierobjekt gleich
System.out.println ("ist das Feld des Originalobjekts mit geklonter Objekt gleich:" +
(stud.getName () == klonedstud.getName ()));
// sind die SubJ -Eigenschaften des ursprünglichen Objekts und das Kopierobjekt gleich
System.out.println ("ist das Feld des Originalobjekts, das das gleiche mit geklonter Objekt ist:" +
(stud.getSubj () == klonedstud.getSubj ()));
Stud.SetName ("Dan");
stud.getSubj (). setName ("Physik");
System.out.println ("Originalobjekt nach dem Aktualisieren:" + stud.getName () + " -" + +
stud.getSubj (). getName ());
System.out.println ("Kloned Objekt nach Aktualisierung des Originalobjekts:" + klonedstud.getname () +
" -" + klonedstud.getSubj (). getName ());
}
}
Das Ausgabeergebnis ist wie folgt:
Originalobjekt: John - Algebra
Geklontes Objekt: John - Algebra
Ist das ursprüngliche Objekt mit geklonter Objekt gleich: Falsch
Ist das Feld des ursprünglichen Objekts mit geklontem Objekt dasselbe: TRUE
Ist das Feld des ursprünglichen Objekts mit geklontem Objekt das gleiche
Originalobjekt nach der Aktualisierung: Dan - Physik
Geklonte Objekt nach Aktualisierung des Originalsobjekts: John - Physik
In diesem Beispiel lasse ich die Schülerklasse kopieren, um die klonbare Schnittstelle zu implementieren und die Clone () -Methode der Objektklasse zu überschreiben und dann die Super.Clone () -Methode innerhalb der Methode aufzurufen. Aus den Ausgabergebnissen können wir feststellen, dass die Änderungen an der Eigenschaft "Name" des ursprünglichen Objektstuds nicht auf das klonierte Kopieobjekt ausgewirkt wurden, sondern die Änderungen an der Eigenschaft "Name" des Referenzobjekts SubJ das Kopierobjekt beeinflussten klonedstud.
3. Deep Copy
1. Was ist eine tiefe Kopie
Die Deep Copy kopiert alle Attribute und kopiert den dynamisch zugewiesenen Speicher, auf das die Attribute hingewiesen werden. Eine tiefe Kopie tritt auf, wenn ein Objekt zusammen mit dem Objekt kopiert wird, auf das es sich bezieht. Tiefe Kopie ist langsamer und teurer als flache Kopie.
In der obigen Abbildung hat SourceObject ein Int -Attribut "Feld1" und ein Referenztyp -Attribut "Refobj1" (in Bezug auf ein Objekt vom Typ des Typs enthalten). Beim Erstellen einer tiefen Kopie von SourceObject wird ein CopiedObject erstellt, das ein Attribut "Feld2" enthält, das den Kopierwert "Feld1" und ein Referenztyp -Attribut "RefOBJ2" enthält, das den Kopierwert "Refobj1" enthält. Daher wirkt sich alle Änderungen,
2. wie man eine tiefe Kopie erreicht
Hier ist ein Beispiel für die Implementierung einer tiefen Kopie. Im Beispiel des flachen Kopiens wurde nur eine kleine Änderung vorgenommen, und weder das Thema noch die Kopie -Test -Klasse änderten sich.
Die Codekopie lautet wie folgt:
Schüler der öffentlichen Klasse implementiert Klonable {
// Objektreferenz
privates Subj;
privater Zeichenfolge Name;
public student (String s, String sub) {
name = s;
subJ = neues Subjekt (sub);
}
öffentliches Thema getubj () {
Subj zurückgeben;
}
public String getName () {
Rückgabename;
}
public void setName (String s) {
name = s;
}
/**
* Schreiben Sie die Clone () -Methode neu
*
* @zurückkehren
*/
öffentliches Objekt Clone () {
// Deep Copy erstellen Sie ein neues Objekt der Kopierklasse, damit es unabhängig vom ursprünglichen Objekt ist
Schüler S = neuer Schüler (Name, subj.getName ());
Rückkehr s;
}
}
Das Ausgabeergebnis ist wie folgt:
Originalobjekt: John - Algebra
Geklontes Objekt: John - Algebra
Ist das ursprüngliche Objekt mit geklonter Objekt gleich: Falsch
Ist das Feld des ursprünglichen Objekts mit geklontem Objekt dasselbe: TRUE
Ist das Feld des ursprünglichen Objekts mit geklontem Objekt dasselbe: Falsch
Originalobjekt nach der Aktualisierung: Dan - Physik
Geklonte Objekt nach Aktualisierung des Originalsobjekts: John - Algebra
Es ist leicht, eine kleine Änderung der Clone () -Methode zu finden. Da es sich um eine tiefe Kopie handelt, müssen Sie ein Objekt der Kopierklasse erstellen. Da es in der Schülerklasse Objektreferenzen gibt, müssen Sie die klonbare Schnittstelle in der Schülerklasse implementieren und die Klonmethode überschreiben.
3.. Tiefe Kopieren wird durch Serialisierung erreicht
Tiefe Kopie kann auch durch Serialisierung erreicht werden. Was macht die Serialisierung? Das brauchen Sie wirklich, wenn Sie ein Objekt tief kopieren. Beachten Sie, dass Sie beim Tiefenkopieren durch Serialisierung sicherstellen müssen, dass alle Klassen im Objektdiagramm serialisierbar sind.
Die Codekopie lautet wie folgt:
Public Class ColoredCircle implementiert serialisierbar {
Privat int x;
privat int y;
public ColoredCircle (int x, int y) {
this.x = x;
this.y = y;
}
public int getX () {
Rückkehr x;
}
public void setx (int x) {
this.x = x;
}
public int gety () {
kehre y zurück;
}
public void sety (int y) {
this.y = y;
}
@Override
public String toString () {
return "x =" + x + ", y =" + y;
}
}
öffentliche Klasse DeepCopy {
public static void main (String [] args) löst ioException {aus
ObjectOutputStream OOS = NULL;
ObjectInputStream ois = null;
versuchen {
// Erstellen Sie originelle serialisierbare Objekte
ColoredCircle C1 = neuer Farbtöne (100, 100);
System.out.println ("Original =" + C1);
Farbigcircle c2 = null;
// Tiefes Kopieren wird durch Serialisierung erreicht
BytearrayoutputStream bos = new bytearrayoutputStream ();
OOS = New ObjectOutputStream (BOS);
// dieses Objekt serialisieren und übergeben
OOS.WriteObject (C1);
oos.flush ();
BytearrayInputStream bin = new bytearrayInputStream (bos.tobytearray ());
OIS = New ObjectInputStream (Bin);
// ein neues Objekt zurückgeben
C2 = (farbig) ois.readObject ();
// Überprüfen Sie, ob der Inhalt gleich ist
System.out.println ("Copied =" + C2);
// Ändern Sie den Inhalt des ursprünglichen Objekts
c1.setx (200);
C1.Sety (200);
// jeden aktuellen Inhalt anzeigen
System.out.println ("Original =" + C1);
System.out.println ("Copied =" + C2);
} catch (Ausnahme e) {
System.out.println ("Ausnahme in main =" + e);
} Endlich {
OOS.CLOSE ();
ois.close ();
}
}
}
Das Ausgabeergebnis ist wie folgt:
Original = x = 100, y = 100
Kopiert = x = 100, y = 100
Original = x = 200, y = 200
Kopiert = x = 100, y = 100
Hier müssen Sie nur Folgendes tun:
(1) Stellen Sie sicher, dass alle Klassen im Objektdiagramm serialisierbar sind
(2) Erstellen Sie Eingangs- und Ausgabestream
(3) Verwenden Sie diesen Eingangs- und Ausgangsstrom, um Objekteingangs- und Objektausgabestrom zu erstellen
(4) Übergeben Sie das Objekt, das Sie an den Objektausgabestrom kopieren möchten
(5) Lesen Sie neue Objekte aus dem Objekteingangsstrom und konvertieren Sie sie in die Klasse des von Ihnen gesendeten Objekts zurück
In diesem Beispiel erstelle ich ein ColoredCircle -Objekt C1 und serialisiere es dann (schreibe es in BytearrayoutputStream). Dann habe ich das ursprüngliche Objekt C1 geändert. Dann ist das Ergebnis, wie Sie sehen können, dass sich C1 von C2 unterscheidet, und alle Änderungen an C1 betreffen C2 nicht.
Beachten Sie, dass die Serialisierung eigene Einschränkungen und Probleme hat:
Da die transiente Variable nicht serialisiert werden kann, ist es unmöglich, die transiente Variable mit dieser Methode zu kopieren.
Dann gibt es das Leistungsproblem. Erstellen Sie einen Socket, serialisieren Sie ein Objekt, übertragen Sie es durch den Sockel und deserialisieren Sie es dann langsamer als die Aufrufmethoden vorhandener Objekte. Es wird also einen großen Unterschied in der Leistung geben. Wenn die Leistung für Ihren Code von entscheidender Bedeutung ist, wird empfohlen, diese Methode nicht zu verwenden. Es dauert fast 100 Mal mehr als ein tiefes Kopieren, indem die klonbare Schnittstelle implementiert wird.
4. Verspätete Kopie
Eine verzögerte Kopie ist eine Kombination aus flacher Kopie und tiefer Kopie und wird tatsächlich selten verwendet. Beim Kopieren eines Objekts am Anfang wird eine schnellere flache Kopie verwendet, und ein Zähler wird verwendet, um festzustellen, wie viele Objekte diese Daten teilen. Wenn das Programm das ursprüngliche Objekt ändern möchte, entscheidet es, ob die Daten (durch Überprüfen des Zählers) und bei Bedarf ein tiefes Kopieren geteilt werden.
Die verzögerte Kopie sieht von außen wie eine tiefe Kopie aus, nutzt jedoch die Geschwindigkeit der flachen Kopie, wann immer möglich. Eine verzögerte Kopie kann verwendet werden, wenn Referenzen im ursprünglichen Objekt nicht häufig geändert werden. Aufgrund des Vorhandenseins von Zählern ist der Effizienzabfall sehr hoch, aber nur der Overhead des konstanten Niveaus. In einigen Fällen können kreisförmige Referenzen einige Probleme verursachen.
5. wie man wählen
Wenn die Eigenschaften eines Objekts von grundlegender Typ sind, können Sie eine flache Kopie verwenden. Wenn das Objekt jedoch Referenzeigenschaften hat, müssen Sie wählen, ob Sie eine flache Kopie oder eine tiefe Kopie basierend auf bestimmten Anforderungen auswählen können. Ich meine, wenn die Objektreferenz zu keinem Zeitpunkt geändert wird, müssen keine tiefe Kopie verwendet werden. Verwenden Sie einfach eine flache Kopie. Wenn sich Objektreferenzen häufig ändern, verwenden Sie Deep Copy. Es gibt keine unveränderlichen Regeln, alles hängt von bestimmten Bedürfnissen ab.