Klonen ist der Prozess, bei dem eine Kopie eines Objekts erstellt wird. Die Java-Object-Klasse wird mit der nativen clone()
-Methode geliefert, die eine Kopie der vorhandenen Instanz zurückgibt. Da Object die Basisklasse in Java ist, unterstützen standardmäßig alle Objekte das Klonen.
Java-Objektklonen
Wenn Sie die Java-Objekt-
clone()
-Methode verwenden möchten, müssen Sie das Markerinterface java.lang.Cloneable
implementieren. Andernfalls wird zur Laufzeit eine CloneNotSupportedException
ausgelöst. Außerdem ist das Object-Clone eine geschützte Methode, daher müssen Sie sie überschreiben. Schauen wir uns das Objektklonen in Java anhand eines Beispielprogramms an.
package com.journaldev.cloning;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Employee implements Cloneable {
private int id;
private String name;
private Map<String, String> props;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, String> getProps() {
return props;
}
public void setProps(Map<String, String> p) {
this.props = p;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Wir verwenden die Object-clone()
-Methode, daher haben wir das Cloneable
Interface implementiert. Wir rufen die Superklassen-clone()
-Methode auf, d. h. die Object-clone()
-Methode.
Verwendung der Object-clone() Methode
Lassen Sie uns ein Testprogramm erstellen, um die Methode `clone()` des Objekts zu verwenden, um eine Kopie der Instanz zu erstellen.
package com.journaldev.cloning;
import java.util.HashMap;
import java.util.Map;
public class CloningTest {
public static void main(String[] args) throws CloneNotSupportedException {
Employee emp = new Employee();
emp.setId(1);
emp.setName("Pankaj");
Map props = new HashMap<>();
props.put("salary", "10000");
props.put("city", "Bangalore");
emp.setProps(props);
Employee clonedEmp = (Employee) emp.clone();
// Überprüfen Sie, ob die Attribute emp und clonedEmp gleich oder unterschiedlich sind
System.out.println("emp and clonedEmp == test: " + (emp == clonedEmp));
System.out.println("emp and clonedEmp HashMap == test: " + (emp.getProps() == clonedEmp.getProps()));
// Schauen wir uns die Auswirkungen der Verwendung des Standard-Klonens an
// ändern Sie die Eigenschaften von emp
emp.getProps().put("title", "CEO");
emp.getProps().put("city", "New York");
System.out.println("clonedEmp props:" + clonedEmp.getProps());
// ändern Sie den Namen von emp
emp.setName("new");
System.out.println("clonedEmp name:" + clonedEmp.getName());
}
}
Ergebnis:
emp and clonedEmp == test: false
emp and clonedEmp HashMap == test: true
clonedEmp props:{city=New York, salary=10000, title=CEO}
clonedEmp name:Pankaj
CloneNotSupportedException zur Laufzeit
Wenn unsere Employee-Klasse das Interface `Cloneable` nicht implementiert, wird das obige Programm eine `CloneNotSupportedException` zur Laufzeit Ausnahme auslösen.
Exception in thread "main" java.lang.CloneNotSupportedException: com.journaldev.cloning.Employee
at java.lang.Object.clone(Native Method)
at com.journaldev.cloning.Employee.clone(Employee.java:41)
at com.journaldev.cloning.CloningTest.main(CloningTest.java:19)
Verständnis der Objektklone
Lassen Sie uns das obige Ergebnis betrachten und verstehen, was mit der Methode `clone()` des Objekts passiert.
emp und clonedEmp == test: false
: Es bedeutet, dass emp und clonedEmp zwei verschiedene Objekte sind, die nicht auf dasselbe Objekt verweisen. Dies entspricht den Anforderungen des Java-Objektklonens.emp und clonedEmp HashMap == test: true
: Sowohl die Variablen emp als auch clonedEmp verweisen auf dasselbe Objekt. Dies kann ein ernsthaftes Problem für die Datenintegrität sein, wenn wir den Wert des zugrunde liegenden Objekts ändern. Jede Änderung im Wert könnte sich auch auf die geklonte Instanz auswirken.clonedEmp Eigenschaften:{Stadt=New York, Gehalt=10000, Titel=CEO}
: Wir haben keine Änderung an den Eigenschaften von clonedEmp vorgenommen, dennoch wurden sie geändert, weil sowohl die Variablen emp als auch clonedEmp auf dasselbe Objekt verweisen. Dies geschieht, weil die Standardmethode Object clone() eine oberflächliche Kopie erstellt. Dies kann ein Problem sein, wenn Sie völlig unabhängige Objekte durch den Klonprozess erstellen möchten. Dies kann zu unerwünschten Ergebnissen führen, daher die Notwendigkeit, die Methode Object clone() ordnungsgemäß zu überschreiben.clonedEmp Name:Pankaj
: Was ist hier passiert? Wir haben den Namen von emp geändert, aber der Name von clonedEmp hat sich nicht geändert. Das liegt daran, dass String unveränderlich ist. Wenn wir also den Namen von emp setzen, wird ein neuer String erstellt und die Referenz auf den Namen von emp wird inthis.name = name;
geändert. Daher bleibt der Name von clonedEmp unverändert. Sie werden ein ähnliches Verhalten auch für beliebige primitive Variablentypen feststellen. Wir sind also gut mit der Standardklonung von Java-Objekten, solange wir nur primitive und unveränderliche Variablen im Objekt haben.
Arten der Objektklonung
Es gibt zwei Arten von Objektklonen – flaches Klonen und tiefes Klonen. Lassen Sie uns jede davon verstehen und herausfinden, wie wir das Klonen in unseren Java-Programmen am besten implementieren können.
1. Flaches Klonen
Die Standardimplementierung der Java Object clone() Methode verwendet flache Kopien. Es verwendet die Reflexions-API, um die Kopie der Instanz zu erstellen. Der folgende Codeausschnitt zeigt die Implementierung des flachen Klonens.
@Override
public Object clone() throws CloneNotSupportedException {
Employee e = new Employee();
e.setId(this.id);
e.setName(this.name);
e.setProps(this.props);
return e;
}
2. Tiefes Klonen
Beim tiefen Klonen müssen wir Felder einzeln kopieren. Wenn wir ein Feld mit verschachtelten Objekten wie List, Map usw. haben, müssen wir den Code schreiben, um sie ebenfalls einzeln zu kopieren. Deshalb wird es als tiefes Klonen oder tiefe Kopie bezeichnet. Wir können die Methode Employee clone wie folgt für tiefes Klonen überschreiben.
public Object clone() throws CloneNotSupportedException {
Object obj = super.clone(); //utilize clone Object method
Employee emp = (Employee) obj;
// tiefes Klonen für unveränderliche Felder
emp.setProps(null);
Map hm = new HashMap<>();
String key;
Iterator it = this.props.keySet().iterator();
// Tiefe Kopie von Feld zu Feld
while (it.hasNext()) {
key = it.next();
hm.put(key, this.props.get(key));
}
emp.setProps(hm);
return emp;
}
Mit dieser clone() Methodenimplementierung wird unser Testprogramm folgende Ausgabe erzeugen.
emp and clonedEmp == test: false
emp and clonedEmp HashMap == test: false
clonedEmp props:{city=Bangalore, salary=10000}
clonedEmp name:Pankaj
In den meisten Fällen ist dies das, was wir wollen. Die Methode clone() sollte ein völlig vom Originalobjekt getrenntes neues Objekt zurückgeben. Wenn Sie beabsichtigen, Object clone und das Klonen in Ihrem Programm zu verwenden, tun Sie dies klug und überschreiben Sie es ordnungsgemäß, indem Sie auf veränderliche Felder achten. Es kann eine mühsame Aufgabe sein, wenn Ihre Klasse von einer anderen Klasse erbt, die wiederum von einer anderen Klasse erbt und so weiter. Sie müssen den gesamten Vererbungshierarchiepfad von Object durchlaufen, um eine Tiefenkopie aller veränderlichen Felder zu erstellen.
Klonen mit Serialisierung?
Eine Möglichkeit, eine Tiefenkopie einfach durchzuführen, besteht darin, die Serialisierung zu verwenden. Aber Serialisierung ist ein teurer Vorgang und Ihre Klasse sollte das Interface Serializable implementieren. Alle Felder und Superklassen müssen ebenfalls Serializable implementieren.
Verwenden von Apache Commons Util
Wenn Sie bereits Apache Commons Util-Klassen in Ihrem Projekt verwenden und Ihre Klasse serialisierbar ist, verwenden Sie die folgende Methode.
Employee clonedEmp = org.apache.commons.lang3.SerializationUtils.clone(emp);
Kopierkonstruktor für das Klonen
Wir können einen Kopierkonstruktor definieren, um eine Kopie des Objekts zu erstellen. Warum sollte man sich überhaupt auf die Methode Object clone() verlassen? Zum Beispiel können wir einen Mitarbeiter-Kopierkonstruktor wie den folgenden Code haben.
public Employee(Employee emp) {
this.setId(emp.getId());
this.setName(emp.getName());
Map hm = new HashMap<>();
String key;
Iterator it = emp.getProps().keySet().iterator();
// Tiefe Kopie von Feld zu Feld
while (it.hasNext()) {
key = it.next();
hm.put(key, emp.getProps().get(key));
}
this.setProps(hm);
}
Immer wenn wir eine Kopie eines Mitarbeiterobjekts benötigen, können wir sie mit Employee clonedEmp = new Employee(emp);
erhalten. Das Schreiben eines Kopierkonstruktors kann jedoch eine mühsame Aufgabe sein, wenn Ihre Klasse viele Variablen hat, insbesondere primitive und unveränderliche Variablen.
Best Practices für das Klonen von Java-Objekten
-
Verwenden Sie die Standardmethode Object clone() nur, wenn Ihre Klasse Primitive und unveränderliche Variablen enthält oder Sie eine flache Kopie wünschen. Im Falle der Vererbung müssen Sie alle Klassen überprüfen, von denen Sie erben, bis zur Object-Ebene.
-
Sie können auch den Kopierkonstruktor definieren, wenn Ihre Klasse hauptsächlich veränderliche Eigenschaften hat.
-
Verwenden Sie die Methode Object clone(), indem Sie
super.clone()
in der überschriebenen Klonmethode aufrufen, und nehmen Sie dann die notwendigen Änderungen für das tiefe Kopieren von veränderlichen Feldern vor. -
Wenn Ihre Klasse serialisierbar ist, können Sie die Serialisierung zum Klonen verwenden. Es wird jedoch mit einer Leistungseinbuße einhergehen, also führen Sie einige Benchmarks durch, bevor Sie die Serialisierung für das Klonen verwenden.
-
Wenn Sie eine Klasse erweitern und sie die clone-Methode ordnungsgemäß mit Deep Copy definiert hat, können Sie die Standard-Clone-Methode verwenden. Zum Beispiel haben wir die clone()-Methode in der Employee-Klasse wie folgt ordnungsgemäß definiert.
@Override public Object clone() throws CloneNotSupportedException { Object obj = super.clone(); Employee emp = (Employee) obj; // Deep Cloning für unveränderliche Felder emp.setProps(null); Map<String, String> hm = new HashMap<>(); String key; Iterator<String> it = this.props.keySet().iterator(); // Deep Copy Feld für Feld while (it.hasNext()) { key = it.next(); hm.put(key, this.props.get(key)); } emp.setProps(hm); return emp; }
Wir können eine Unterklasse erstellen und die Überklasse Deep Cloning wie folgt nutzen.
package com.journaldev.cloning; public class EmployeeWrap extends Employee implements Cloneable { private String title; public String getTitle() { return title; } public void setTitle(String t) { this.title = t; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }
Die Klasse
EmployeeWrap
hat keine veränderlichen Eigenschaften und nutzt die Implementierung der Überklasse clone()-Methode. Wenn es veränderliche Felder gibt, müssen Sie sich um die tiefe Kopie nur dieser Felder kümmern. Hier ist ein einfaches Programm, um zu testen, ob diese Art des Klonens funktioniert oder nicht.package com.journaldev.cloning; import java.util.HashMap; import java.util.Map; public class CloningTest { public static void main(String[] args) throws CloneNotSupportedException { EmployeeWrap empWrap = new EmployeeWrap(); empWrap.setId(1); empWrap.setName("Pankaj"); empWrap.setTitle("CEO"); Map<String, String> props = new HashMap<>(); props.put("salary", "10000"); props.put("city", "Bangalore"); empWrap.setProps(props); EmployeeWrap clonedEmpWrap = (EmployeeWrap) empWrap.clone(); empWrap.getProps().put("1", "1"); System.out.println("empWrap mutable property value = "+empWrap.getProps()); System.out.println("clonedEmpWrap mutable property value = "+clonedEmpWrap.getProps()); } }
Ausgabe:
empWrap mutable property value = {1=1, city=Bangalore, salary=10000} clonedEmpWrap mutable property value = {city=Bangalore, salary=10000}
Es hat also perfekt funktioniert, wie wir es erwartet haben.
Das ist alles zum Thema Objektklonen in Java. Ich hoffe, Sie haben eine Vorstellung davon bekommen, wie die Java-Objekt-clone() Methode ordnungsgemäß überschrieben wird, ohne negative Auswirkungen zu haben.
Sie können das Projekt von meinem GitHub-Repository herunterladen.
Referenz: API-Dokumentation für Object clone
Source:
https://www.digitalocean.com/community/tutorials/java-clone-object-cloning-java