>  기사  >  Java  >  Java 개체의 얕은 복제 및 심층 복제에 대한 자세한 분석(예제 포함)

Java 개체의 얕은 복제 및 심층 복제에 대한 자세한 분석(예제 포함)

不言
不言앞으로
2018-10-10 11:19:301997검색

이 기사는 Java 객체의 얕은 복제와 심층 복제에 대한 자세한 분석을 제공합니다. 이는 특정 참고 가치가 있으므로 도움이 될 수 있습니다.

소개:

Object 기본 클래스에는 이전 개체의 복제본을 생성하는 clone이라는 메서드가 있습니다. 복제된 개체는 원본 개체의 복사본입니다. 참조 유형이 존재하기 때문에 복제된 객체에 참조 유형 속성이 있는 경우 심층 복제는 이 속성의 전체 복사본을 만드는 반면 얕은 복제는 이에 대한 참조만 복사합니다. 기인하다. 먼저 쉽게 만들 수 있는 몇 가지 사소한 문제를 살펴보겠습니다.

Cloneable 인터페이스가 아닌 Object 클래스에 복제 방법이 표시 인터페이스가 사용됩니다. 클래스에는 Serialized, Cloneable 및 RandomAccess의 세 가지 일반적인 표시 인터페이스가 있습니다. Cloneable 인터페이스가 구현되지 않으면 clone 메서드가 호출될 때 CloneNotSupportedException이 발생합니다.

Object 클래스의 clone 메서드는 protected로 수정됩니다. 즉, 하위 클래스에서 이 메서드를 재정의하지 않으면 하위 클래스 외부에서 액세스할 수 없습니다. 객체에서 사용 가능 패키지와 해당 패키지가 포함된 하위 클래스에서 액세스할 수 있습니다. 이는 또한 상위 클래스 메서드를 재정의하는 하위 클래스의 권한 한정자가 더 커질 수 있지만 더 작아질 수는 없다는 설명도 확인합니다.

protected native Object clone() throws CloneNotSupportedException;

내부적으로 clone 메서드를 재정의하면 상위 클래스의 clone 메서드만 호출됩니다. 사실 접근 권한을 public으로 변경해도 되지만 그럴 필요는 없습니다. 미래에 상속받는다면 다시 작성하세요. 물론, 얕은 클로닝을 위한 클론 기능일 뿐이고, 딥 클로닝은 수정이 필요합니다.

    @Override    
    protected Object clone() throws CloneNotSupportedException {       
     return super.clone();
    }

속성이 String이고 String도 클래스인 경우 String은 참조 유형인가요? String은 기본 유형처럼 동작합니다. 결론은 String을 변경할 수 없다는 것입니다. 복제 후에는 두 참조가 동일한 String을 가리킵니다. 그러나 둘 중 하나가 수정되면 String의 값은 변경되지 않습니다. 문자열이 생성됩니다. 수정된 참조는 새 문자열을 가리킵니다. 외형은 기본형과 같습니다.

Shallow clone: ​​​​

Shallow clone은 User 클래스에 grade 속성 Mark가 포함되어 있어 참조 유형 속성을 완전히 복사할 수 없음을 의미합니다. , etc.,shallow clone 복제 실패의 예

class Mark{
    private int chinese;
    private int math;
    public Mark(int chinese, int math) {
        this.chinese = chinese;
        this.math = math;
    }

    public void setChinese(int chinese) {
        this.chinese = chinese;
    }

    public void setMath(int math) {
        this.math = math;
    }

    @Override
    public String toString() {
        return "Mark{" +
                "chinese=" + chinese +
                ", math=" + math +
                '}';
    }
}
public class User implements Cloneable{
    private String name;
    private int age;
    private Mark mark;

    public User(String name, int age,Mark mark) {
        this.name = name;
        this.age = age;
        this.mark = mark;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", mark=" + mark +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Mark mark = new Mark(100,99);
        User user = new User("user",22,mark);
        User userClone = (User) user.clone();
        System.out.println("原user:"+user);
        System.out.println("克隆的user:"+userClone);
        //修改引用类型的mark属性
        user.mark.setMath(60);
        System.out.println("修改后的原user:"+user);
        System.out.println("修改后的克隆user:"+userClone);
    }
}

출력 결과는 다음과 같습니다:  

원래 사용자: User{name='user', age=22, mark=Mark {chinese=100, math= 99}}
복제 사용자: User{name='user', age=22, mark=Mark{chinese=100, math=99}}
수정된 원래 사용자: 사용자{name= 'user', age=22, mark=Mark{chinese=100, math=60}}
수정된 복제 사용자: User{name='user', age=22, mark=Mark{chinese =100 , math=60}}

사용자 표시가 변경된 후에는 복제된 사용자도 수정되는 것이 분명합니다. 그리고 영향을 받지 않으려면 심층 복제가 필요합니다.

심층 복제:

방법 1: 복제 함수의 중첩 호출

참조 유형을 완전히 복제할 수 없으므로 참조 유형도 다음과 같습니다. 구현됨 Cloneable 인터페이스는 복제 메소드를 다시 작성합니다. User 클래스는 메소드의 중첩 호출인 속성의 복제 메소드를 호출합니다.

class Mark implements Cloneable{
    private int chinese;
    private int math;
    public Mark(int chinese, int math) {
        this.chinese = chinese;
        this.math = math;
    }
    public void setChinese(int chinese) {
        this.chinese = chinese;
    }
    public void setMath(int math) {
        this.math = math;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    @Override
    public String toString() {
        return "Mark{" +
                "chinese=" + chinese +
                ", math=" + math +
                '}';
    }
}
public class User implements Cloneable{
    private String name;
    private int age;
    private Mark mark;

    public User(String name, int age,Mark mark) {
        this.name = name;
        this.age = age;
        this.mark = mark;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", mark=" + mark +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        User user = (User) super.clone();
        user.mark = (Mark) this.mark.clone();
        return user;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Mark mark = new Mark(100,99);
        User user = new User("user",22,mark);
        User userClone = (User) user.clone();
        System.out.println("原user:"+user);
        System.out.println("克隆的user:"+userClone);
        //修改引用类型的mark属性
        user.mark.setMath(60);
        System.out.println("修改后的原user:"+user);
        System.out.println("修改后的克隆user:"+userClone);
    }
}

출력 결과는 다음과 같습니다. name='user', age=22, mark=Mark{chinese=100, math=99}}

  복제 사용자: User{name='user', age=22, mark=Mark{ Chinese=100, math=99}}

수정된 원래 사용자: User{name='user', age=22, mark=Mark{chinese=100, math=60}}
수정 후 복제 사용자: User{ name='user', age=22, mark=Mark{chinese=100, math=99}}

방법 2: 직렬화

이전 방법으로도 충분합니다. 우리의 요구사항이지만 클래스 사이에 관계가 많거나 일부 속성이 배열인 경우 배열은 Cloneable 인터페이스를 구현할 수 없지만(clone 메소드에서 수동으로 배열을 복사할 수 있음) 매번 clone 메소드를 직접 작성해야 합니다. 그러나 직렬화 방법은 마크 인터페이스이기도 한 각 클래스에 대해 직렬화 가능 인터페이스만 구현하면 됩니다. 마지막으로 직렬화 및 역직렬화를 통해 복제 목적(배열 복사 포함)을 달성합니다. 운영. 직렬화 및 역직렬화에 대한 자세한 내용은 다음 문서를 참조하세요.

import java.io.*;
class Mark implements Serializable {
    private int chinese;
    private int math;
    public Mark(int chinese, int math) {
        this.chinese = chinese;
        this.math = math;
}
    public void setChinese(int chinese) {
        this.chinese = chinese;
    }
    public void setMath(int math) {
        this.math = math;
    }
    @Override
    public String toString() {
        return "Mark{" +
                "chinese=" + chinese +
                ", math=" + math +
                '}';
    }
}
public class User implements Serializable{
    private String name;
    private int age;
    private Mark mark;

    public User(String name, int age,Mark mark) {
        this.name = name;
        this.age = age;
        this.mark = mark;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", mark=" + mark +
                '}';
    }
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Mark mark = new Mark(100,99);
        User user = new User("user",22,mark);

        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(bo);
        oo.writeObject(user);//序列化
        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);
        User userClone = (User) oi.readObject();//反序列化

        System.out.println("原user:"+user);
        System.out.println("克隆的user:"+userClone);
        user.mark.setMath(59);
        System.out.println("修改后的原user:"+user);
        System.out.println("修改后的克隆user:"+userClone);
    }
}

출력 결과:

원래 사용자: User{name='user', age=22, mark= Mark{chinese=100, math=99}}

 복제 사용자: User{name='user', age=22, mark=Mark{chinese=100, math=99}}

 수정 후 원래 사용자: User{name='user', age=22, mark=Mark{chinese=100, math=60}}
  수정된 복제 사용자: User{name='user', age=22 , mark =Mark{중국어=100, 수학=99}}

 배열 속성으로 복제

import java.io.*;
import java.util.Arrays;

public class User implements Serializable{
    private String name;
    private int age;
    private int[] arr;

    public User(String name, int age, int[] arr) {
        this.name = name;
        this.age = age;
        this.arr = arr;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", arr=" + Arrays.toString(arr) +
                '}';
    }
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        int[] arr = {1,2,3,4,5,6};
        User user = new User("user",22,arr);

        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(bo);
        oo.writeObject(user);//序列化
        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);
        User userClone = (User) oi.readObject();//反序列化

        System.out.println("原user:"+user);
        System.out.println("克隆的user:"+userClone);
        user.arr[1] = 9;
        System.out.println("修改后的原user:"+user);
        System.out.println("修改后的克隆user:"+userClone);
    }
}

위 내용은 Java 개체의 얕은 복제 및 심층 복제에 대한 자세한 분석(예제 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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