Rumah  >  Artikel  >  Java  >  Apakah kaedah penyalinan mendalam di Jawa?

Apakah kaedah penyalinan mendalam di Jawa?

PHPz
PHPzke hadapan
2023-04-17 20:34:011424semak imbas

Kata Pengantar

Di Java, apabila kita perlu menyalin objek, terdapat dua jenis salinan: Salinan cetek dan salinan dalam.

  • Salinan cetek hanya menyalin alamat objek sumber, jadi apabila nilai objek sumber berubah, nilai objek yang disalin juga akan berubah.

  • Salinan dalam menyalin semua nilai objek sumber, jadi walaupun nilai objek sumber berubah, nilai objek yang disalin tidak akan berubah.

Apakah kaedah penyalinan mendalam di Jawa?

Kaedah 1: Salinan dalam Pembina

Kita boleh memanggil pembina untuk melakukan salinan dalam Jika parameter formal adalah jenis asas dan Rentetan diberikan secara langsung Jika ia adalah objek, yang baharu dicipta.

Kes ujian

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;
    }
}

Kaedah 2: Salinan dalam kaedah Klon() berlebihan

Kelas induk Objek mempunyai salinan klon( ) kaedah, tetapi ia adalah jenis yang dilindungi Kita perlu menulis semula dan menukarnya kepada jenis awam Selain itu, subkelas juga perlu melaksanakan antara muka Boleh Diklon untuk memberitahu JVM bahawa kelas ini boleh disalin.

Kes ujian

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()));
    }
}

Perlu diambil perhatian bahawa super.clone() sebenarnya adalah salinan cetek, jadi apabila menulis semula kaedah klon() Pengguna class , objek alamat perlu ditetapkan semula dengan memanggil address.clone().

Kaedah 3: salinan dalam kaedah penyirian Apache Commons Lang

Java menyediakan keupayaan penyirian objek sumber dahulu, dan kemudian nyahsirikannya untuk menjana objek salinan. Walau bagaimanapun, prasyarat untuk menggunakan serialisasi ialah kelas yang disalin (termasuk pembolehubah ahlinya) perlu melaksanakan antara muka Serializable.

Pakej Apache Commons Lang merangkumi siri Java dan kami boleh menggunakannya secara langsung.

Kes ujian

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()));
    }
}

Kaedah 4: Salin dalam kaedah siri Gson

Gson boleh mensiri objek ke dalam JSON, atau JSON Deserialize menjadi objek supaya kita boleh menggunakannya untuk membuat salinan yang mendalam.

Kes ujian

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()));
    }
}

Kaedah 5: Kaedah siri Jackson

Jackson serupa dengan Gson dan boleh mensiri objek ke dalam JSON, yang jelas sekali berbeza Intinya ialah kelas yang disalin (termasuk pembolehubah ahlinya) perlu mempunyai pembina tanpa hujah lalai.

Kes Ujian

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()));
    }
}

Ringkasan

深拷贝方法 优点 缺点
构造函数 1. 底层实现简单 2. 不需要引入第三方包 3. 系统开销小 4. 对拷贝类没有要求,不需要实现额外接口和方法 1. 可用性差,每次新增成员变量都需要新增新的拷贝构造函数
重载clone()方法 1. 底层实现较简单 2. 不需要引入第三方包 3. 系统开销小 1. 可用性较差,每次新增成员变量可能需要修改clone()方法 2. 拷贝类(包括其成员变量)需要实现Cloneable接口
Apache Commons Lang序列化 1. 可用性强,新增成员变量不需要修改拷贝方法 1. 底层实现较复杂 2. 需要引入Apache Commons Lang第三方JAR包 3. 拷贝类(包括其成员变量)需要实现Serializable接口 4. 序列化与反序列化存在一定的系统开销
Gson序列化 1. 可用性强,新增成员变量不需要修改拷贝方法 2. 对拷贝类没有要求,不需要实现额外接口和方法 1. 底层实现复杂 2. 需要引入Gson第三方JAR包 3. 序列化与反序列化存在一定的系统开销
Jackson序列化 1. 可用性强,新增成员变量不需要修改拷贝方法 1. 底层实现复杂 2. 需要引入Jackson第三方JAR包 3. 拷贝类(包括其成员变量)需要实现默认的无参构造函数 4. 序列化与反序列化存在一定的系统开销

Atas ialah kandungan terperinci Apakah kaedah penyalinan mendalam di Jawa?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam