ホームページ  >  記事  >  Java  >  Java でのディープ コピーの方法にはどのようなものがありますか?

Java でのディープ コピーの方法にはどのようなものがありますか?

PHPz
PHPz転載
2023-04-17 20:34:011423ブラウズ

はじめに

Java では、オブジェクトをコピーする必要がある場合、浅いコピーと深いコピーの 2 種類のコピーがあります。

  • 浅いコピーはソース オブジェクトのアドレスのみをコピーするため、ソース オブジェクトの値が変更されると、コピーされたオブジェクトの値も変更されます。

  • ディープコピーはソースオブジェクトの値をすべてコピーするため、ソースオブジェクトの値が変更されても、コピーされたオブジェクトの値は変わりません。

Java でのディープ コピーの方法にはどのようなものがありますか?

#方法 1: コンストラクターのディープ コピー

コンストラクターを呼び出してディープ コピーを実行できます。 type と String は直接割り当てられ、オブジェクトの場合は新しいものになります。

テスト ケース

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

方法 2: Clone() メソッドのディープ コピーのオーバーロード

Object 親クラスには clone() copy メソッドがあります。ただし、これはプロテクト型なので、書き換えてパブリック型に変更する必要があります。また、サブクラスは、このクラスがコピーできることを JVM に伝えるために Cloneable インターフェイスを実装する必要もあります。

テスト ケース

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

super.clone() は実際には浅いコピーであるため、User クラスの clone() メソッドをオーバーライドするときは、 address オブジェクトは、address.clone() を呼び出して再割り当てする必要があります。

方法 3: Apache Commons Lang シリアル化メソッドのディープ コピー

Java にはシリアル化機能が備わっており、最初にソース オブジェクトをシリアル化し、次にそれを逆シリアル化してコピー オブジェクトを生成できます。ただし、シリアル化を使用するための前提条件は、コピーされたクラス (そのメンバー変数を含む) が Serializable インターフェイスを実装する必要があることです。

Apache Commons Lang パッケージは Java シリアル化をカプセル化しており、それを直接使用できます。

テスト ケース

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

方法 4: Gson シリアル化メソッドのディープ コピー

Gson はオブジェクトを JSON にシリアル化し、JSON をオブジェクトに逆シリアル化できるため、これを使用してディープコピーを作成できます。

テスト ケース

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

方法 5: Jackson シリアル化メソッド

Jackson は Gson に似ており、オブジェクトを JSON にシリアル化できますが、明らかな違いがあります。クラス (そのメンバー変数を含む) には、デフォルトの引数なしのコンストラクターが必要です。

#テストケース

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

##ディープコピー方式利点欠点コンストラクター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. シリアル化および逆シリアル化には一定のシステム オーバーヘッドがあります
##

以上がJava でのディープ コピーの方法にはどのようなものがありますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。