Ich glaube, jeder hat davon gehört. Die ersten geklonten Schafe der Welt, Dolly, nutzt die Kerntransplantationstechnologie, um neue Personen in somatischen Zellen der Säugetiere zu kultivieren, was sehr magisch ist. Tatsächlich gibt es auch das Konzept des Klonen in Java, das heißt, das Kopieren von Objekten zu erkennen.
In diesem Artikel wird versucht, einige eingehende Fragen zum Klonen in Java zu stellen, in der Hoffnung, Ihnen zu helfen, das Klonen besser zu verstehen.
Angenommen, Sie möchten eine einfache Variable kopieren. Sehr einfach:
int apples = 5; int Pearls = Äpfel;
Nicht nur der Int -Typ, die anderen sieben primitiven Datentypen (boolean, char, byte, Short, Float, Double.long) sind ebenfalls für diese Art von Situation anwendbar.
Wenn Sie jedoch ein Objekt kopieren, ist die Situation etwas kompliziert.
Angenommen, ich bin ein Anfänger, ich würde Folgendes schreiben:
Klassenstudent {private int nummer; public int getNumber () {Rückgabenummer; } public void setNumber (int nummer) {this.number = number; }} public class Test {public static void main (String args []) {Student stu1 = new Student (); stu1.setNumber (12345); Student stu2 = stu1; System.out.println ("Student 1:" + stu1.getNumber ()); System.out.println ("Student 2:" + stu2.getNumber ()); }}Ergebnis:
Student 1: 12345
Schüler 2: 12345
Hier haben wir eine Schülerklasse angepasst, die nur ein Feld hat.
Wir erstellen eine neue Studenteninstanz und weisen den Wert der STU2 -Instanz zu. (Schüler stu2 = stu1;)
Schauen wir uns die Druckergebnisse an. Als Anfänger tätschelte ich meine Brust und meinen Bauch, aber das Objekt wurde so kopiert.
Ist das wirklich der Fall?
Wir versuchen, das Zahlenfeld der STU2 -Instanz zu ändern und dann das Ergebnis zu drucken und zu sehen:
stu2.setNumber (54321); System.out.println ("Student 1:" + stu1.getNumber ()); System.out.println ("Student 2:" + stu2.getNumber ());Ergebnis:
Student 1: 54321
Schüler 2: 54321
Das ist seltsam. Warum änderte sich die Schülerzahl von Studenten 2 und die Schülerzahl von Studenten 1 auch?
Der Grund liegt im Satz (stu2 = stu1). Der Zweck dieser Erklärung ist es, Stu2 die Referenz von Stu1 zuzuweisen.
Auf diese Weise verweisen Stu1 und Stu2 auf dasselbe Objekt im Speicherhaufen. Wie im Bild gezeigt:
Wie können wir also das Kopieren eines Objekts erreichen?
Erinnerst du dich an den König aller Altersgruppen? Es hat 11 Methoden, und es gibt zwei geschützte Methoden, von denen eine die Klonmethode ist.
In Java werden alle Klassen standardmäßig aus der Objektklasse im Java -Sprachpaket geerbt. Überprüfen Sie seinen Quellcode. Sie können src.zip in Ihr JDK -Verzeichnis an einen anderen Ort kopieren und es dekomprimieren, und der gesamte Quellcode ist enthalten. Ich fand heraus, dass es eine Methode mit dem von Access Qualifikator Protected Clone () gibt:
/*Erstellt und gibt eine Kopie dieses Objekts zurück. Die Präzisionsbedeutung von "Kopie" kann von der Klasse des Objekts abhängen. Die allgemeine Absicht ist, dass für jedes Objekt X der Ausdruck: 1) x.clone ()! wirft ClonenotsupportedException aus;
Wenn Sie genau hinschauen, ist es immer noch eine native Methode. Jeder weiß, dass native Methoden Code in Nicht-Java-Sprachen implementiert werden und für den Anruf von Java-Programmen dienen. Da Java-Programme auf JVM-Virtual-Maschinen durchgeführt werden, gibt es keine Möglichkeit, auf die zugrunde liegenden Betriebssysteme zuzugreifen, und sie können nur durch Sprachen in der Nähe des Betriebssystems implementiert werden.
Die erste Erklärung stellt sicher, dass das geklonte Objekt über eine separate Speicheradressenzuweisung verfügt.
Die zweite Erklärung zeigt, dass die ursprünglichen und geklonten Objekte den gleichen Klassentyp haben sollten, aber nicht obligatorisch ist.
Die dritte Aussage zeigt, dass die ursprünglichen und geklonten Objekte nach der Equals () -Methode gleichermaßen verwendet werden sollten, aber nicht obligatorisch ist.
Da die übergeordnete Klasse jeder Klasse ein Objekt ist, enthalten sie alle eine Clone () -Methode, aber weil die Methode geschützt ist, kann auf keiner von ihnen außerhalb der Klasse zugegriffen werden.
Um ein Objekt zu kopieren, müssen Sie die Klonmethode überschreiben.
Warum klonen?
Denken wir zuerst über eine Frage nach, warum Sie Objekte klonen müssen? Ist es nicht in Ordnung, nur ein Objekt neu zu sein?
Die Antwort lautet: Das klonierte Objekt kann einige geänderte Eigenschaften enthalten, und die Eigenschaften des neuen Objekts sind zum Zeitpunkt der Initialisierung immer noch die Werte. Wenn also ein neues Objekt benötigt wird, um den "Status" des aktuellen Objekts zu speichern, hängt die Klonmethode von der Klonmethode ab. Ist es also nicht in Ordnung für mich, die temporären Eigenschaften dieses Objekts meinem neuen Objekt einzeln zuzuweisen? Es ist in Ordnung, aber lassen Sie uns nicht zuerst darüber sprechen. Zweitens hat jeder durch den obigen Quellcode festgestellt, dass Clone eine native Methode ist, die schnell und unten implementiert ist.
Lassen Sie mich Sie daran erinnern, dass unser gemeinsames Objekt a = neues Objekt (); Objekt b; b = a; Diese Form von Code kopiert Referenzen, dh die Adresse des Objekts im Speicher, und die A- und B -Objekte weisen immer noch auf dasselbe Objekt hin.
Das über die Klonmethode zugewiesene Objekt existiert unabhängig vom ursprünglichen Objekt.
So implementieren Sie das Klonen
Lassen Sie mich zunächst zwei verschiedene Klonenmethoden einführen, flacher und tiefes Klonen.
In der Java -Sprache werden Datentypen in Werttypen (grundlegende Datentypen) und Referenztypen unterteilt. Zu den Werttypen gehören einfache Datentypen wie int, doppelte, byte, boolean, char usw., und Referenztypen umfassen komplexe Typen wie Klassen, Schnittstellen, Arrays usw. Der Hauptunterschied zwischen flachem Klonen und tiefem Klonen besteht darin, ob das Kopieren von Mitgliedsvariablen von Referenztypen unterstützt wird. Die beiden werden nachstehend ausführlich vorgestellt.
Die allgemeinen Schritte sind (flaches Klonen):
1. Die kopierte Klasse muss die klonenbare Schnittstelle implementieren (wenn Sie sie nicht implementieren, wird eine klonenotsupportedException beim Aufrufen der Klonmethode geworfen). Diese Schnittstelle ist eine Tag -Schnittstelle (ohne Methode)
2. Überschreiben Sie die Clone () -Methode und setzen Sie den Zugriffsmodifikator auf die Öffentlichkeit. Rufen Sie die Methode Super.clone () in der Methode auf, um das erforderliche Kopierobjekt zu erhalten. (Native ist eine lokale Methode)
Das Folgende ist eine Modifikation der obigen Methode:
Klassenstudenten implementiert klonbar {private int nummer; public int getNumber () {return number;} public void setNumber (int nummer) {this.number = number;}@override public Object () {student stu = null; try {stu = (student) Super.clone (); stu;}} public class test {public static void main (String args []) {Student stu1 = new student (); stu1.setNumber (12345); Student stu2 = (student) stu1.clone (); System.out.println ("student1:" + stu1.getnumber (); stu2.getNumber ()); stu2.setNumber (54321); System.out.println ("student1:" + stu1.getNumber ()); System.out.println ("student2:" + stu2.getNumber ());}}Ergebnis:
Student 1: 12345
Schüler 2: 12345
Student 1: 12345
Schüler 2: 54321
Wenn Sie nicht glauben, dass diese beiden Objekte nicht dasselbe Objekt sind, können Sie sich diesen Satz ansehen:
System.out.println (STU1 == STU2); // FALSCH
Die obige Kopie wird als flaches Klonen bezeichnet.
Es gibt auch eine etwas komplexere tiefe Kopie:
Fügen wir der Schülerklasse eine Adresskurs hinzu.
Klasse Adresse {private String add; public String getAdd () {return add;} public void setAdd (String add) {this.add = add;}} Klasse Schüler implementiert klonable {private int nummer; setNumber (int number) {this.number = number;}@override public Object Clone () {student stu = null; try {stu = (student) super.clone ();} catch (clonenotsupportedException E) {E. printstocktrace (); Address (); addr.setAdd ("Hangzhou City"); Student stu1 = neuer Schüler (); stu1.setNumber (123); stu1.setaddr (addr); Student stu2 = (student) stu1.clone (); stu1.getaddr (). getAdd ());Ergebnis:
Schüler 1: 123, Adresse: Hangzhou Student 2: 123, Adresse: Hangzhou
Auf den ersten Blick gibt es kein Problem, ist das wirklich der Fall?
Wir versuchen, die Adresse der ADDR -Instanz in der Hauptmethode zu ändern.
addr.setAdd ("Xihu District"); System.out.println ("Student 1:" + stu1.getNumber () + ", fügen Sie:" + stu1.getAddr (). GetAdd ()); System.out.println ("Student 2:" + stu2.getNumber () + ", fügen Sie:" + stu2.getAddr (). GetAdd ());Ergebnis:
Schüler 1: 123, Adresse: Hangzhou Student 2: 123, Adresse: Hangzhou Student 1: 123, Adresse: Xihu Distrikt Student 2: 123, Adresse: Xihu Distrikt
Das ist seltsam, warum haben sich die Adressen beider Schüler verändert?
Der Grund dafür ist, dass das flache Kopieren nur die Referenz der ADDR -Variablen kopiert und nicht wirklich ein anderes Stück Platz öffnet. Geben Sie nach dem Kopieren des Werts den Verweis auf das neue Objekt zurück.
Um also echte Kopierobjekte zu erreichen, nicht nur Referenzkopien. Wir müssen die Adressklasse kopieren und die Klonmethode ändern. Der vollständige Code lautet wie folgt:
Paket ABC; Klassenadresse implementiert klonbar {private String add; public String getAdd () {return add; } public void setAdd (String add) {this.add = add; } @Override öffentliches Objekt Clone () {Adresse adDr = null; try {adDr = (Adresse) Super.clone (); } catch (clonenotsUsPortedexception e) {e.printstacktrace (); } return adDr; }} Klasse Schüler implementiert klonbar {private int nummer; private Adresse adDr; öffentliche Adresse getAddr () {return adDr; } public void setAddr (Adresse addr) {this.addr = adDr; } public int getNumber () {Rückgabenummer; } public void setNumber (int nummer) {this.number = number; } @Override öffentliches Objekt Clone () {Student stu = null; try {stu = (student) super.clone (); // flache Kopie} catch (clonenotsuptedEdException e) {e.printstacktrace (); } stu.addr = (Adresse) addr.clone (); // Deep Copy Return Stu; }} public class test {public static void main (String args []) {Adresse adDr = new address (); addr.setAdd ("Hangzhou City"); Student stu1 = neuer Schüler (); stu1.setNumber (123); stu1.setaddr (adDR); Student stu2 = (Student) stu1.clone (); System.out.println ("Student 1:" + stu1.getNumber () + ", Adresse:" + stu1.getAddr (). GetAdd ()); System.out.println ("Student 2:" + stu2.getNumber () + ", Adresse:" + stu2.getAddr (). GetAdd ()); addr.setAdd ("Xihu District"); System.out.println ("Student 1:" + stu1.getNumber () + ", Adresse:" + stu1.getAddr (). GetAdd ()); adDr.SetAdd ()); System.out.println ("Student 2:" + stu2.getNumber () + ", Adresse:" + stu2.getAddr (). GetAdd ()); }}Ergebnis:
Schüler 1: 123, Adresse: Hangzhou Student 2: 123, Adresse: Hangzhou Student 1: 123, Adresse: Xihu Distrikt Student 2: 123, Adresse: Hangzhou Stadt
Dieses Ergebnis entspricht unseren Ideen.
Schließlich können wir uns einen der Klassen in der API ansehen, die die Klonmethode implementiert:
java.util.date:
/*** Gibt eine Kopie dieses Objekts zurück. */ public Object Clone () {Datum d = null; try {d = (Datum) super.clone (); if (cdate! = null) {d.cdate = (Basecalendar.date) cdate.clone (); }} catch (clonenotsupportededexception e) {} // wird nicht geschehen, dass d; }Diese Kategorie ist eigentlich eine tiefe Kopie.
Flaches Klonen und tiefes Klonen
1. flaches Klonen
Bei der flachen Klonierung wird eine Kopie an das geklonte Objekt kopiert, wenn die Mitgliedsvariable des Prototypobjekts einen Werttyp hat. Wenn die Mitgliedsvariable des Prototyp -Objekts einen Referenztyp hat, wird die Adresse des Referenzobjekts in das geklonte Objekt kopiert, dh die Mitgliedsvariable des Prototyp -Objekts und das geklonte Objekt verweist auf dieselbe Speicheradresse.
Einfach ausgedrückt, wenn das Objekt kopiert wird, wird nur die Mitgliedsvariable des Werttyps des Objekts kopiert und das Elementobjekt des Referenztyps nicht kopiert.
In der Java -Sprache kann das flache Klonen durch Überschreiben der Clone () -Methode der Objektklasse implementiert werden.
2. Tiefes Klonen
Beim tiefen Klonen wird eine Kopie an das geklonte Objekt kopiert, unabhängig davon, ob die Mitgliedsvariable des Prototyp -Objekts ein Werttyp oder ein Referenztyp ist. Tiefes Klonen kopiert auch alle referenzierten Objekte des Prototypobjekts in das klonierte Objekt.
Einfach ausgedrückt, in tiefem Klonen, mit Ausnahme des Objekts selbst, das selbst kopiert wird, werden alle im Objekt enthaltenen Mitgliedsvariablen ebenfalls kopiert.
In der Java -Sprache kann es implementiert werden, wenn Sie eine tiefe Klonen implementieren müssen, indem Sie die Clone () -Methode der Objektklasse überschreiben oder durch Serialisierung implementiert werden können, etc.
(Wenn der Referenztyp viele Referenztypen enthält oder die innere Referenztypklasse Referenztypen enthält, ist es sehr problematisch, die Klonmethode zu verwenden. Zu diesem Zeitpunkt können wir eine Serialisierung verwenden, um die tiefe Klone des Objekts zu implementieren.)
Serialisierung ist der Prozess des Schreibens von Objekten in einen Stream. Das in den Stream geschriebene Objekt ist eine Kopie des ursprünglichen Objekts, und das ursprüngliche Objekt existiert immer noch im Speicher. Die durch Serialisierung implementierte Kopie kann nicht nur das Objekt selbst kopieren, sondern auch die IT -Referenzen der Elementobjekte kopieren. Durch die Serialisierung des Objekts mit einem Strom und dann kann es aus dem Strom gelesen werden, kann ein tiefes Klonen erreicht werden. Es ist zu beachten, dass die Klasse eines Objekts, das eine Serialisierung implementieren kann, die serialisierbare Schnittstelle implementieren muss, andernfalls kann der Serialisierungsvorgang nicht implementiert werden.
Erweitert
Der Code der klonbaren Schnittstelle und der serialisierbaren Schnittstelle, die von der Java -Sprache bereitgestellt wird, ist sehr einfach. Sie sind beide leere Schnittstellen. Diese leere Schnittstelle wird auch als Identifikationsschnittstelle bezeichnet. Es gibt keine Definition einer Methode in der Identifikationsschnittstelle. Seine Funktion besteht darin, JRE zu sagen, ob die Implementierungsklassen dieser Schnittstellen eine bestimmte Funktion haben, z. B. ob sie das Klonen unterstützen, ob sie die Serialisierung unterstützen usw.
Lösen Sie mehrschichtige Klonierungsprobleme
Wenn der Referenztyp viele Referenztypen oder die innere Referenztypklasse enthält, ist es sehr problematisch, die Klonmethode zu verwenden. Zu diesem Zeitpunkt können wir eine Serialisierung verwenden, um das tiefgreifende Klonen des Objekts zu implementieren.
öffentliche Klasse äußere implementiert serialisierbar {private statische endgültige long serialversionuid = 369285298572941l; // Es ist am besten, die ID öffentlich innere innere innere zu deklarieren. // Beschreibung: [Deep Copy -Methode erfordert, dass das Objekt und alle Objekteigenschaften serialisiert werden] public outer myclone () {outer outer = null; Versuchen Sie es mit {// das Objekt in einen Stream serialisieren, da das, was im Stream geschrieben ist, eine Kopie des Objekts ist und das ursprüngliche Objekt im JVM weiterhin vorhanden ist. Mit dieser Funktion können Sie daher eine tiefe Kopie des ObjektbytearrayoutputStream baos = new bytearrayoutputStream () erreichen. ObjectOutputStream OOS = new ObjectOutputStream (BAOS); oos.writeObject (this); // den Stream in ein Objekt bytearrayInputStream bais = neuer BytearrayInputStream (baos.tobytearray ()) serialisieren; ObjectInputStream OIS = New ObjectInputStream (BAIS); äußere = (äußere) ois.readObject (); } catch (ioException e) {e.printstacktrace (); } catch (classNotFoundException e) {e.printstacktrace (); } return out; }}Innere muss auch serialisierbar implementieren, andernfalls kann es nicht serialisiert werden:
Öffentliche Klasse Innere implementiert serialisierbar {private statische endgültige lange Serialversionuid = 872390113109l; // Es ist am besten, die ID public String name = "" explizit zu deklarieren; public inner (String name) {this.name = name; } @Override public String toString () {return "Der Name des inneren Werts lautet:" + name; }}Dies kann es auch ermöglichen, dass die beiden Objekte im Speicherraum völlig unabhängig existieren, ohne die Werte des anderen zu beeinflussen.
Zusammenfassen
Es gibt zwei Möglichkeiten, das Klonen von Objektklonen zu implementieren:
1). Implementieren Sie die klonbare Schnittstelle und überschreiben Sie die Clone () -Methode in der Objektklasse.
2). Implementieren Sie die serialisierbare Schnittstelle und implementieren Sie das Klonen durch Objektserialisierung und Deserialisierung, die das echte Tiefenklonen realisieren können.
Hinweis: Klonen basierend auf Serialisierung und Deserialisierung ist nicht nur ein tiefes Klonen, sondern vor allem, sondern durch generische Einschränkung kann überprüft werden, ob das zu klonende Objekt die Serialisierung unterstützt. Diese Überprüfung erfolgt vom Compiler und wirft keine Ausnahmen zur Laufzeit aus. Diese Lösung ist offensichtlich besser als das Klonieren von Objekten unter Verwendung der Klonmethode der Objektklasse. Es ist immer besser, das Problem der Laufzeit zu überlassen, indem Sie es beim Kompilieren freilegen.
Das obige ist die detaillierte Erläuterung des Codes (Java -Programmierungsimplementierungsobjektkloning). Ich hoffe, er wird für alle hilfreich sein. Interessierte Freunde können weiterhin auf andere verwandte Themen auf dieser Website verweisen. Wenn es Mängel gibt, hinterlassen Sie bitte eine Nachricht, um darauf hinzuweisen.