>  기사  >  Java  >  Java에서 딥 카피(deep copy) 방법은 무엇입니까?

Java에서 딥 카피(deep copy) 방법은 무엇입니까?

PHPz
PHPz앞으로
2023-04-17 20:34:011424검색

머리말

Java에서는 객체를 복사해야 할 때 두 가지 유형의 복사본이 있습니다. Shallow Copy와 Deep Copy입니다.

  • 얕은 복사는 원본 개체의 주소만 복사하므로 원본 개체의 값이 변경되면 복사된 개체의 값도 변경됩니다.

  • Deep Copy는 원본 객체의 값을 모두 복사하므로 원본 객체의 값이 변경되더라도 복사된 객체의 값은 변경되지 않습니다.

Java에서 딥 카피(deep copy) 방법은 무엇입니까?

방법 1: 생성자 딥 카피

생성자를 호출하여 딥 카피를 수행할 수 있습니다. 형식 매개 변수가 기본 유형 또는 문자열이면 직접 할당됩니다. .

테스트 사례

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() 메서드 오버로드

객체 상위 클래스에는 clone() 복사 메서드가 있지만 보호 유형이므로 다시 작성하고 변경해야 합니다. 또한 하위 클래스는 이 클래스를 복사할 수 있음을 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.clone()을 호출하여 그 값을 다시 할당하십시오.

방법 3: Apache Commons Lang 직렬화 방법 깊은 복사

Java는 직렬화 기능을 제공합니다. 먼저 소스 개체를 직렬화한 다음 역직렬화하여 복사 개체를 생성할 수 있습니다. 그러나 직렬화를 사용하기 위한 전제 조건은 복사된 클래스(해당 멤버 변수 포함)가 직렬화 가능 인터페이스를 구현해야 한다는 것입니다.

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으로 직렬화할 수 있습니다. 분명한 차이점은 복사된 클래스(멤버 변수 포함)에 매개 변수가 없는 기본 값이 있어야 한다는 것입니다. 건설자.

테스트 사례 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()));
    }
}
요약

전체 복사 방법 장점 단점 구성 기능 1. 간단한 기본 구현 2. 타사 패키지 도입 필요 없음 3. 시스템. 낮은 오버헤드 4. 복사 클래스에 대한 요구 사항이 없으며 추가 인터페이스 및 메서드를 구현할 필요가 없습니다1. 사용성이 좋지 않아 새 멤버 변수가 추가될 때마다 새 복사 생성자를 추가해야 합니다클론 오버로드( ) 방법1. 기본 구현이 비교적 간단합니다. 2. 타사 패키지를 도입할 필요가 없습니다. 3. 시스템 오버헤드가 적습니다. 1. 새로운 멤버 변수가 추가될 때마다 clone( ) 메소드 수정이 필요할 수 있음 2. 클래스 복사(멤버 변수 포함) Cloneable 인터페이스 구현 필요 Apache Commons Lang 직렬화 1. 강력한 유용성, 새 멤버 변수는 복사 메소드를 수정할 필요가 없음 1. 기본 구현이 복잡합니다. 2. Apache Commons Lang 타사 JAR 패키지를 도입해야 합니다. 3. 클래스(멤버 변수 포함)를 복사하려면 직렬화 가능 인터페이스를 구현해야 합니다. 4. 직렬화에 특정 시스템 오버헤드가 있습니다. 및 역직렬화 Gson 직렬화 1. 높은 가용성, 새 멤버 변수 추가 시 복사 방법 수정이 필요하지 않음 2 복사 클래스에 대한 요구 사항이 없으며 추가 인터페이스 및 방법을 구현할 필요가 없습니다1. 기본 구현이 복잡합니다. 2. Gson 타사 JAR 패키지를 도입해야 합니다. 3. 직렬화 및 역직렬화에 특정 시스템 오버헤드가 있습니다Jackson 직렬화 1. 강력한 유용성, 새 멤버 변수를 수정할 필요가 없습니다. 복사 방법1. 기본 구현이 복잡합니다. 2. Jackson 타사 JAR 패키지를 도입해야 합니다. 3. 복사 클래스(멤버 변수 포함)는 인수가 없는 기본 생성자를 구현해야 합니다. 직렬화 및 역직렬화의 시스템 오버헤드

위 내용은 Java에서 딥 카피(deep copy) 방법은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제