Maison >Java >javaDidacticiel >Quelles sont les méthodes de copie profonde en Java ?

Quelles sont les méthodes de copie profonde en Java ?

PHPz
PHPzavant
2023-04-17 20:34:011499parcourir

Avant-propos

En Java, lorsque nous devons copier un objet, il existe deux types de copies : Copie superficielle et copie profonde.

  • La copie superficielle copie uniquement l'adresse de l'objet source, donc lorsque la valeur de l'objet source change, la valeur de l'objet copié changera également.

  • La copie approfondie copie toutes les valeurs de l'objet source, donc même si la valeur de l'objet source change, la valeur de l'objet copié ne changera pas.

Quelles sont les méthodes de copie profonde en Java ?

Méthode 1 : Copie profonde du constructeur

Nous pouvons appeler le constructeur pour effectuer une copie profonde Si le paramètre formel est un type de base ou une chaîne, il est attribué directement. S'il s'agit d'un objet, il est nouveau. .

Cas de test

package com.lyj.demo.pojo.cloneTest;
import lombok.Getter;
/**
 * @author 凌兮
 * @date 2021/4/15 14:28
 * 通过构造器进行深拷贝测试
 */
@Getter
public class UserConstruct {
    private String userName;
    private AddressConstruct address;
    public UserConstruct() {
    }
    public UserConstruct(String userName, AddressConstruct address) {
        this.userName = userName;
        this.address = address;
    }
    public static void main(String[] args) {
        AddressConstruct address = new AddressConstruct("小区1", "小区2");
        UserConstruct user = new UserConstruct("小李", address);
        // 调用构造函数进行深拷贝
        UserConstruct copyUser = new UserConstruct(user.getUserName(), new AddressConstruct(address.getAddress1(), address.getAddress2()));
        // 修改源对象的值
        user.getAddress().setAddress1("小区3");
        // false
        System.out.println(user == copyUser);
        // false
        System.out.println(user.getAddress().getAddress1() == copyUser.getAddress().getAddress1());
        // false
        System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));
        // true
        System.out.println(user.getAddress().getAddress2().equals(copyUser.getAddress().getAddress2()));
    }
}
package com.lyj.demo.pojo.cloneTest;
import lombok.Getter;
import lombok.Setter;
/**
 * @author 凌兮
 * @date 2021/4/15 14:28
 */
@Getter
@Setter
public class AddressConstruct {
    private String address1;
    private String address2;
    public AddressConstruct() {
    }
    public AddressConstruct(String address1, String address2) {
        this.address1 = address1;
        this.address2 = address2;
    }
}

Méthode 2 : Surcharger la méthode Clone() pour la copie profonde

La classe parent Object a une méthode de copie clone(), mais elle est de type protégé, nous devons la réécrire et la modifier au type public, en outre, la sous-classe doit également implémenter l'interface Cloneable pour indiquer à la JVM que cette classe peut être copiée.

Cas de test

package com.lyj.demo.pojo.cloneTest;
import lombok.Getter;
import lombok.Setter;
/**
 * @author 凌兮
 * @date 2021/4/15 14:49
 *
 */
@Setter
@Getter
public class AddressClone implements Cloneable{
    private String address1;
    private String address2;
    public AddressClone() {
    }
    public AddressClone(String address1, String address2) {
        this.address1 = address1;
        this.address2 = address2;
    }
    @Override
    protected AddressClone clone() throws CloneNotSupportedException {
        return (AddressClone) super.clone();
    }
}
package com.lyj.demo.pojo.cloneTest;
import lombok.Getter;
import lombok.Setter;
/**
 * @author 凌兮
 * @date 2021/4/15 14:48
 * 通过实现Clone接口实现深拷贝
 */
@Setter
@Getter
public class UserClone implements Cloneable{
    private String userName;
    private AddressClone address;
    public UserClone() {
    }
    public UserClone(String userName, AddressClone address) {
        this.userName = userName;
        this.address = address;
    }
    /**
     * Object父类有个clone()的拷贝方法,不过它是protected类型的,
     * 我们需要重写它并修改为public类型。除此之外,
     * 子类还需要实现Cloneable接口来告诉JVM这个类是可以拷贝的。
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected UserClone clone() throws CloneNotSupportedException {
        // 需要注意的是,super.clone()其实是浅拷贝,
        // 所以在重写UserClone类的clone()方法时,address对象需要调用address.clone()重新赋值
        UserClone userClone = (UserClone) super.clone();
        userClone.setAddress(this.address.clone());
        return userClone;
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        AddressClone address = new AddressClone("小区1", "小区2");
        UserClone user = new UserClone("小李", address);
        UserClone copyUser = user.clone();
        user.getAddress().setAddress1("小区3");
        // false
        System.out.println(user == copyUser);
        // false
        System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));
    }
}

Il convient de noter que super.clone() est en fait une copie superficielle, donc lors du remplacement de la méthode clone() de la classe User, l'objet d'adresse doit appeler adresse.clone() pour réaffecter sa valeur.

Méthode 3 : copie approfondie de la méthode de sérialisation Apache Commons Lang

Java fournit des capacités de sérialisation. Nous pouvons d'abord sérialiser l'objet source, puis le désérialiser pour générer l'objet de copie. Cependant, la condition préalable à l'utilisation de la sérialisation est que la classe copiée (y compris ses variables membres) doit implémenter l'interface Serialisable.

Le package Apache Commons Lang encapsule la sérialisation Java et nous pouvons l'utiliser directement.

Cas de test

package com.lyj.demo.pojo.cloneTest;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
/**
 * @author 凌兮
 * @date 2021/4/15 15:11
 */
@Getter
@Setter
public class AddressSerializable implements Serializable {
    private String address1;
    private String address2;
    public AddressSerializable() {
    }
    public AddressSerializable(String address1, String address2) {
        this.address1 = address1;
        this.address2 = address2;
    }
}
package com.lyj.demo.pojo.cloneTest;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.SerializationUtils;
import java.io.Serializable;
/**
 * @author 凌兮
 * @date 2021/4/15 15:10
 * 通过Apache Commons Lang 序列化方式深拷贝
 * Java提供了序列化的能力,我们可以先将源对象进行序列化,再反序列化生成拷贝对象。
 * 但是,使用序列化的前提是拷贝的类(包括其成员变量)需要实现Serializable接口。
 * Apache Commons Lang包对Java序列化进行了封装,我们可以直接使用它。
 */
@Getter
@Setter
public class UserSerializable implements Serializable {
    private String userName;
    private AddressSerializable address;
    public UserSerializable() {
    }
    public UserSerializable(String userName, AddressSerializable address) {
        this.userName = userName;
        this.address = address;
    }
    public static void main(String[] args) {
        AddressSerializable address = new AddressSerializable("小区1", "小区2");
        UserSerializable user = new UserSerializable("小李", address);
        UserSerializable copyUser = SerializationUtils.clone(user);
        user.getAddress().setAddress1("小区3");
        // false
        System.out.println(user == copyUser);
        // false
        System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));
    }
}

Méthode 4 : méthode de sérialisation Gson copie profonde

Gson peut sérialiser des objets en JSON et désérialiser JSON en objets, afin que nous puissions l'utiliser pour la copie profonde.

Cas de test

package com.lyj.demo.pojo.cloneTest;
import lombok.Data;
/**
 * @author 凌兮
 * @date 2021/4/15 15:31
 */
@Data
public class AddressGson {
    private String address1;
    private String address2;
    public AddressGson() {
    }
    public AddressGson(String address1, String address2) {
        this.address1 = address1;
        this.address2 = address2;
    }
}
package com.lyj.demo.pojo.cloneTest;
import com.google.gson.Gson;
import lombok.Data;
/**
 * @author 凌兮
 * @date 2021/4/15 15:30
 * 使用Gson序列化方式进行深拷贝
 * Gson可以将对象序列化成JSON,也可以将JSON反序列化成对象,所以我们可以用它进行深拷贝
 */
@Data
public class UserGson {
    private String userName;
    private AddressGson address;
    public UserGson() {
    }
    public UserGson(String userName, AddressGson address) {
        this.userName = userName;
        this.address = address;
    }
    public static void main(String[] args) {
        AddressGson address = new AddressGson("小区1", "小区2");
        UserGson user = new UserGson("小李", address);
        // 使用Gson序列化进行深拷贝
        Gson gson = new Gson();
        UserGson copyUser = gson.fromJson(gson.toJson(user), UserGson.class);
        user.getAddress().setAddress1("小区3");
        // false
        System.out.println(user == copyUser);
        // false
        System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));
    }
}

Méthode 5 : méthode de sérialisation de Jackson

Jackson est similaire à Gson et peut sérialiser des objets en JSON. La différence évidente est que la classe copiée (y compris ses variables membres) doit avoir un paramètre sans paramètre par défaut. Constructeur.

Cas de test re

package com.lyj.demo.pojo.cloneTest;
import lombok.Data;
/**
 * @author 凌兮
 * @date 2021/4/15 15:41
 */
@Data
public class AddressJackson {
    private String address1;
    private String address2;
    public AddressJackson() {
    }
    public AddressJackson(String address1, String address2) {
        this.address1 = address1;
        this.address2 = address2;
    }
}
package com.lyj.demo.pojo.cloneTest;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
/**
 * @author 凌兮
 * @date 2021/4/15 15:40
 * 通过Jackson方式实现深拷贝
 * Jackson与Gson相似,可以将对象序列化成JSON,明显不同的地方是拷贝的类(包括其成员变量)需要有默认的无参构造函数。
 */
@Data
public class UserJackson {
    private String userName;
    private AddressJackson address;
    public UserJackson() {
    }
    public UserJackson(String userName, AddressJackson address) {
        this.userName = userName;
        this.address = address;
    }
    public static void main(String[] args) throws JsonProcessingException {
        AddressJackson address = new AddressJackson("小区1", "小区2");
        UserJackson user = new UserJackson("小李", address);
        // 使用Jackson序列化进行深拷贝
        ObjectMapper objectMapper = new ObjectMapper();
        UserJackson copyUser = objectMapper.readValue(objectMapper.writeValueAsString(user), UserJackson.class);
        user.getAddress().setAddress1("小区3");
        // false
        System.out.println(user == copyUser);
        // false
        System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));
    }
}
Résumé

Méthode de copie approfondie Avantages Inconvénients Fonction de construction 1. Implémentation sous-jacente simple 2. Pas besoin d'introduire des packages tiers 3. Systèmes. Faible surcharge 4. Aucune exigence pour les classes de copie, pas besoin d'implémenter d'interfaces et de méthodes supplémentaires1 Mauvaise convivialité, chaque fois qu'une nouvelle variable membre est ajoutée, un nouveau constructeur de copie doit être ajoutéSurcharge du clone (. ) méthode1. L'implémentation sous-jacente est relativement simple 2. Pas besoin d'introduire de packages tiers 3. La surcharge du système est faible 1. La convivialité est médiocre, chaque fois qu'une nouvelle variable membre est ajoutée, le clone( ) La méthode devra peut-être être modifiée 2. Copiez la classe (y compris ses variables membres) Nécessité d'implémenter l'interface Cloneable Sérialisation Apache Commons Lang 1 Forte convivialité, les nouvelles variables membres n'ont pas besoin de modifier la méthode de copie. 1. L'implémentation sous-jacente est complexe 2. Le package JAR tiers Apache Commons Lang doit être introduit 3 . La copie d'une classe (y compris ses variables membres) doit implémenter l'interface Serialisable 4. Il y a une certaine surcharge système dans la sérialisation. et désérialisation Sérialisation Gson 1. Haute convivialité, l'ajout de nouvelles variables membres ne nécessite pas de modification de la méthode de copie 2 . Il n'y a aucune exigence de classes de copie et pas besoin d'implémenter des interfaces et des méthodes supplémentaires1. l'implémentation sous-jacente est complexe 2. Le package JAR tiers Gson doit être introduit 3. Il y a une certaine surcharge système dans la sérialisation et la désérialisationSérialisation Jackson 1. méthode de copie1. L'implémentation sous-jacente est complexe 2. Le package JAR tiers Jackson doit être introduit 3. La copie des classes (y compris leurs variables membres) doit implémenter le constructeur par défaut sans argument 4. Il y a une certaine surcharge système en sérialisation et désérialisation

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer