Wie erstelle ich eine tiefe Kopie eines Objekts?

einführen

Wenn wir ein Objekt kopieren müssen, gibt es in der Java-Sprache zwei Arten von Kopien: flache Kopie und tiefe Kopie. Flaches Kopieren kopiert nur die Adresse des Quellobjekts. Wenn sich also der Wert des Quellobjekts ändert, ändert sich auch der Wert des kopierten Objekts. Eine tiefe Kopie kopiert alle Werte des Quellobjekts, sodass sich der Wert des kopierten Objekts nicht ändert, selbst wenn sich der Wert des Quellobjekts ändert.

Nachdem Sie den Unterschied zwischen flacher Kopie und tiefer Kopie verstanden haben, werden Sie in diesem Blog verschiedene Methoden der tiefen Kopie lernen.

Objekt kopieren

Lassen Sie uns zunächst ein einfaches Objekt definieren, das kopiert werden muss.

/**  
 * 用户  
 */  
public class User {
    
      

    private String name;  
    private Address address;  

    // constructors, getters and setters  

}  

/**  
 * 地址  
 */  
public class Address {
    
      

    private String city;  
    private String country;  

    // constructors, getters and setters  

}  

Wie im obigen Code gezeigt, definieren wir eine User-Benutzerklasse, die Name und Adresse enthält, wobei Adresse keine Zeichenfolge ist, sondern eine andere Adressklasse, die Land und Stadt enthält. Die Methoden get() und set() von Konstruktoren und Member-Variablen entfallen hier. Als Nächstes beschreiben wir detailliert, wie das Benutzerobjekt tief kopiert wird.

Methodenkonstruktor

Durch den Aufruf des Konstruktors können wir eine tiefe Kopie erstellen: Ist der formale Parameter ein Basistyp und ein String, wird er direkt zugewiesen, ist es ein Objekt, wird ein neuer angelegt.

Testfall

@Test  
public void constructorCopy() {
    
      

    Address address = new Address("杭州", "中国");  
    User user = new User("大山", address);  

    // 调用构造函数时进行深拷贝  
    User copyUser = new User(user.getName(), new Address(address.getCity(), address.getCountry()));  

    // 修改源对象的值  
    user.getAddress().setCity("深圳");  

    // 检查两个对象的值不同  
    assertNotSame(user.getAddress().getCity(), copyUser.getAddress().getCity());  

}  

Methode zwei überlädt die Methode clone()

Die Object-Elternklasse hat eine clone()-Kopiermethode, aber es ist ein geschützter Typ, wir müssen ihn überschreiben und in einen öffentlichen Typ ändern. Darüber hinaus müssen Unterklassen auch die Cloneable-Schnittstelle implementieren, um der JVM mitzuteilen, dass die Klasse kopiert werden kann.

den Code umschreiben

Lassen Sie uns die User-Klasse und die Address-Klasse ändern, um die Cloneable-Schnittstelle zu implementieren, um tiefes Kopieren zu unterstützen.

/**  
 * 地址  
 */  
public class Address implements Cloneable {
    
      

    private String city;  
    private String country;  

    // constructors, getters and setters  

    @Override  
    public Address clone() throws CloneNotSupportedException {
    
      
        return (Address) super.clone();  
    }  

}  
/**  
 * 用户  
 */  
public class User implements Cloneable {
    
      

    private String name;  
    private Address address;  

    // constructors, getters and setters  

    @Override  
    public User clone() throws CloneNotSupportedException {
    
      
        User user = (User) super.clone();  
        user.setAddress(this.address.clone());  
        return user;  
    }  

}  

Es ist zu beachten, dass super.clone() eigentlich eine flache Kopie ist, sodass beim Umschreiben der Methode clone() der Klasse User das Adressobjekt durch Aufrufen von address.clone() neu zugewiesen werden muss.

Testfall

@Test  
public void cloneCopy() throws CloneNotSupportedException {
    
      

    Address address = new Address("杭州", "中国");  
    User user = new User("大山", address);  

    // 调用clone()方法进行深拷贝  
    User copyUser = user.clone();  

    // 修改源对象的值  
    user.getAddress().setCity("深圳");  

    // 检查两个对象的值不同  
    assertNotSame(user.getAddress().getCity(), copyUser.getAddress().getCity());  

}  

Methode drei Apache Commons Lang-Serialisierung

Java bietet die Möglichkeit zur Serialisierung, wir können zuerst das Quellobjekt serialisieren und dann deserialisieren, um ein Kopierobjekt zu generieren. Voraussetzung für die Verwendung der Serialisierung ist jedoch, dass die kopierte Klasse (einschließlich ihrer Mitgliedsvariablen) die Serializable-Schnittstelle implementieren muss. Das Apache Commons Lang-Paket kapselt die Java-Serialisierung und wir können sie direkt verwenden.

den Code umschreiben

Ändern wir die User-Klasse und die Address-Klasse, um die Serializable-Schnittstelle zur Unterstützung der Serialisierung zu implementieren.

/**  
 * 地址  
 */  
public class Address implements Serializable {
    
      

    private String city;  
    private String country;  

    // constructors, getters and setters  

}  
/**  
 * 用户  
 */  
public class User implements Serializable {
    
      

    private String name;  
    private Address address;  

    // constructors, getters and setters  

} 

Testfall

@Test  
public void serializableCopy() {
    
      

    Address address = new Address("杭州", "中国");  
    User user = new User("大山", address);  

    // 使用Apache Commons Lang序列化进行深拷贝  
    User copyUser = (User) SerializationUtils.clone(user);  

    // 修改源对象的值  
    user.getAddress().setCity("深圳");  

    // 检查两个对象的值不同  
    assertNotSame(user.getAddress().getCity(), copyUser.getAddress().getCity());  

} 

Methode vier Gson-Serialisierung

Gson kann Objekte in JSON serialisieren und JSON auch in Objekte deserialisieren, sodass wir es für Deep Copying verwenden können.

Testfall

@Test  
public void gsonCopy() {
    
      

    Address address = new Address("杭州", "中国");  
    User user = new User("大山", address);  

    // 使用Gson序列化进行深拷贝  
    Gson gson = new Gson();  
    User copyUser = gson.fromJson(gson.toJson(user), User.class);  

    // 修改源对象的值  
    user.getAddress().setCity("深圳");  

    // 检查两个对象的值不同  
    assertNotSame(user.getAddress().getCity(), copyUser.getAddress().getCity());  

}  

Methode fünf Jackson-Serialisierung

Jackson ähnelt Gson darin, dass es Objekte in JSON serialisieren kann. Der offensichtliche Unterschied besteht darin, dass die kopierte Klasse (einschließlich ihrer Mitgliedsvariablen) einen Standardkonstruktor ohne Argumente haben muss.

den Code umschreiben

Lassen Sie uns die User-Klasse und die Address-Klasse ändern und den Standardkonstruktor ohne Argumente implementieren, um Jackson zu unterstützen.

/**  
 * 用户  
 */  
public class User {
    
      

    private String name;  
    private Address address;  

    // constructors, getters and setters  

    public User() {
    
      
    }  

}  
/**  
 * 地址  
 */  
public class Address {
    
      

    private String city;  
    private String country;  

    // constructors, getters and setters  

    public Address() {
    
      
    }  

}  

Testfall

@Test  
public void jacksonCopy() throws IOException {
    
      

    Address address = new Address("杭州", "中国");  
    User user = new User("大山", address);  

    // 使用Jackson序列化进行深拷贝  
    ObjectMapper objectMapper = new ObjectMapper();  
    User copyUser = objectMapper.readValue(objectMapper.writeValueAsString(user), User.class);  

    // 修改源对象的值  
    user.getAddress().setCity("深圳");  

    // 检查两个对象的值不同  
    assertNotSame(user.getAddress().getCity(), copyUser.getAddress().getCity());  

}  

Zusammenfassen

Nach so vielen Deep-Copy-Implementierungsmethoden, welche Methode ist die beste? Die einfachste Beurteilung ist, danach zu wählen, ob die kopierte Klasse (einschließlich ihrer Member-Variablen) einen Deep-Copy-Konstruktor bereitstellt, ob sie die Cloneable-Schnittstelle implementiert, ob sie die Serializable-Schnittstelle implementiert und ob sie den Standardkonstruktor ohne Argumente implementiert. Für eine detaillierte Betrachtung können Sie sich auf die folgende Tabelle beziehen:

Guess you like

Origin blog.csdn.net/qq_43842093/article/details/123763063