In diesem Artikel aus meinem kostenlosen Java 8-Kurs werde ich den Unterschied zwischen einer tiefen und einer flachen Kopie diskutieren. Hier können Sie die Folien und den Artikel als PDF herunterladen.
Was Ist eine Kopie?
Zunächst möchte ich hervorheben, was eine Kopie in Java ist. Unterscheiden wir zunächst zwischen einer Referenzkopie und einer Objektkopie. Eine Referenzkopie erstellt, wie der Name schon sagt, eine Kopie einer Referenzvariablen, die auf ein Objekt verweist., Wenn wir ein Car-Objekt haben, auf das eine MyCar-Variable zeigt, und wir eine Referenzkopie erstellen, haben wir jetzt zwei MyCar-Variablen, aber immer noch ein Objekt.
Beispiel 1
Eine Objektkopie erstellt eine Kopie des Objekts selbst. Wenn wir also unser Autoobjekt erneut kopieren würden, würden wir eine Kopie des Objekts selbst sowie eine zweite Referenzvariable erstellen, die auf dieses kopierte Objekt verweist.
Beispiel 2
Was Ist ein Objekt?,
Sowohl eine tiefe Kopie als auch eine flache Kopie sind Objektkopiertypen, aber was ist wirklich ein Objekt? Wenn wir über ein Objekt sprechen, sprechen wir oft von einer einzigen Einheit, die nicht weiter abgebaut werden kann, wie eine bescheidene Kaffeebohne. Das ist jedoch zu vereinfacht.
Beispiel 3
Sagen, wir haben ein Person-Objekt. Unser Person-Objekt besteht tatsächlich aus anderen Objekten, wie Sie in Beispiel 4 sehen können. Unsere Person enthält ein Namensobjekt und ein Adressobjekt., Der Name wiederum enthält ein Vorname-und ein NachName-Objekt; Das Adressobjekt besteht aus einem Straßenobjekt und einem Stadtobjekt. Wenn ich also in diesem Artikel über Person spreche, spreche ich eigentlich über dieses gesamte Netzwerk von Objekten.
Beispiel 4
Warum sollten wir dieses Personenobjekt kopieren? Eine Objektkopie, normalerweise als Klon bezeichnet, wird erstellt, wenn ein Objekt geändert oder verschoben werden soll, während das ursprüngliche Objekt beibehalten wird. Es gibt viele verschiedene Möglichkeiten, ein Objekt zu kopieren, das Sie in einem anderen Artikel erfahren können., In diesem Artikel verwenden wir speziell einen Kopierkonstruktor, um unsere Kopien zu erstellen.
Flache Kopie
Sprechen wir zuerst über die flache Kopie. Eine flache Kopie eines Objekts kopiert das‘ Haupt ‚ – Objekt, kopiert jedoch nicht die inneren Objekte. Die „inneren Objekte“ werden zwischen dem ursprünglichen Objekt und seiner Kopie geteilt. In unserem Personenobjekt würden wir beispielsweise eine zweite Person erstellen, aber beide Objekte würden denselben Namen und dieselben Adressobjekte haben.
Schauen wir uns ein Codierungsbeispiel an. In Beispiel 5 haben wir unsere Klasse Person, die einen Namen und ein Adressobjekt enthält., Der Kopierkonstruktor nimmt das originalPerson-Objekt und kopiert seine Referenzvariablen.
Beispiel 5
Das Problem mit der flachen Kopie ist, dass die beiden Objekte nicht unabhängig sind. Wenn Sie das Namensobjekt einer Person ändern, wird die Änderung im Objekt der anderen Person wiedergegeben.
Wenden wir dies auf ein Beispiel an. Angenommen, wir haben ein Personenobjekt mit einer Referenzvariablen mother; Dann erstellen wir eine Kopie von mother und erstellen ein Objekt der zweiten Person, son. Wenn später im Code der Sohn versucht, sich zu bewegenout () Durch Ändern seines Adressobjekts bewegt sich die Mutter mit ihm!,
Person mother = new Person(new Name(…), new Address(…));Person son = new Person(mother);son.moveOut(new Street(…), new City(…));
Beispiel 6
Dies geschieht, weil unsere Mutter-Sohn-Objekte dasselbe Adressobjekt teilen, wie Sie in Beispiel 7 veranschaulicht sehen können. Wenn wir die Adresse in einem Objekt ändern, ändert es sich in beiden!
Beispiel 7
Deep Copy
Im Gegensatz zur flachen Kopie ist eine Deep copy eine völlig unabhängige Kopie eines Objekts. Wenn wir unser Person-Objekt kopieren würden, würden wir die gesamte Objektstruktur kopieren.,
Beispiel 8
Eine Änderung des Adressobjekts einer Person würde sich nicht im anderen Objekt widerspiegeln, wie Sie im Diagramm in Beispiel 8 sehen können. Wenn wir uns den Code in Beispiel 9 ansehen, können Sie sehen, dass wir nicht nur einen Kopierkonstruktor für unser Personenobjekt verwenden, sondern auch Kopierkonstruktoren für die inneren Objekte.
Beispiel 9
Mit dieser tiefen Kopie können wir das Mutter-Sohn-Beispiel aus Beispiel 6 wiederholen. Jetzt kann der Sohn erfolgreich ausziehen!,
Das ist jedoch nicht das Ende der Geschichte. Um eine echte Deep copy zu erstellen, müssen wir weiterhin alle verschachtelten Elemente des Person-Objekts kopieren, bis nur noch primitive Typen und „Immutables“ übrig sind. Schauen wir uns die Street Klasse an, um dies besser zu veranschaulichen:
Beispiel 10
Das Street Objekt besteht aus zwei Instanzvariablen – String name und int number. int number ist ein primitiver Wert und kein Objekt. Es ist nur ein einfacher Wert, der nicht geteilt werden kann, also erstellen wir durch Erstellen einer zweiten Instanzvariablen automatisch eine unabhängige Kopie., String ist eine unveränderliche. Kurz gesagt, ein unveränderliches Objekt ist ein Objekt, das, sobald es erstellt wurde, nie wieder geändert werden kann. Daher können Sie es teilen, ohne eine tiefe Kopie davon erstellen zu müssen.
Schlussfolgerung
Abschließend möchte ich über einige Codierungstechniken sprechen, die wir in unserem Mutter-Sohn-Beispiel verwendet haben. Nur weil Sie mit einer tiefen Kopie die internen Details eines Objekts ändern können, z. B. das Adressobjekt, bedeutet dies nicht, dass Sie dies tun sollten., Dies würde die Codequalität verringern, da dadurch die Personenklasse anfälliger für Änderungen wird – wenn die Adressklasse geändert wird, müssen Sie (möglicherweise) auch Änderungen an der Personenklasse vornehmen. Wenn die Address-Klasse beispielsweise kein Street-Objekt mehr enthält, müssen wir die moveOut () – Methode in der Person-Klasse zusätzlich zu den Änderungen ändern, die wir bereits an der Address-Klasse vorgenommen haben.
In Beispiel 6 dieses Artikels habe ich mich nur für ein neues Straßen-und Stadtobjekt entschieden, um den Unterschied zwischen einer flachen und einer tiefen Kopie besser zu veranschaulichen., Stattdessen würde ich empfehlen, stattdessen ein neues Adressobjekt zuzuweisen und effektiv in eine Mischung aus einer flachen und einer tiefen Kopie zu konvertieren, wie Sie in Beispiel 10 sehen können:
Person mother = new Person(new Name(…), new Address(…));Person son = new Person(mother);son.moveOut(new Address(...));
Beispiel 11
In objektorientierten Begriffen verstößt dies gegen die Kapselung und sollte daher vermieden werden. Kapselung ist einer der wichtigsten Aspekte der objektorientierten Programmierung. In diesem Fall hatte ich die Kapselung verletzt, indem ich auf die internen Details des Adressobjekts in unserer Person-Klasse zugegriffen hatte., Dies schadet unserem Code, da wir jetzt die Person-Klasse in die Adressklasse verwickelt haben und wenn wir Änderungen an der Adressklasse vornehmen, könnte dies der Person-Klasse schaden, wie ich oben erklärt habe. Während Sie offensichtlich Ihre verschiedenen Klassen miteinander verbinden müssen, um ein Codierungsprojekt zu haben, müssen Sie bei jeder Verbindung zweier Klassen die Kosten und den Nutzen analysieren.