ホームページ  >  記事  >  Java  >  Java オブジェクトのシャロー クローン作成とディープ クローン作成の詳細な分析 (例付き)

Java オブジェクトのシャロー クローン作成とディープ クローン作成の詳細な分析 (例付き)

不言
不言転載
2018-10-10 11:19:301997ブラウズ

この記事では、Java オブジェクトのシャロー クローン作成とディープ クローン作成について詳しく説明します。必要な方は参考にしてください。

はじめに:

オブジェクト基本クラスには、初期オブジェクトのクローンを生成する clone というメソッドがあります。参照タイプが存在する場合は、ディープ クローン作成が行われます。シャロー クローン作成とシャロー クローン作成の違いは、クローンされたオブジェクトに参照タイプの属性がある場合、ディープ クローン作成ではその属性の完全なコピーが作成されるのに対し、シャロー クローン作成では参照のみがコピーされることです。属性に。まず、簡単にできるいくつかの小さな問題を見てみましょう。

clone メソッドは Object クラスに属し、Cloneable インターフェイスは単なるマーク インターフェイスです。マーク インターフェイスはユーザー マークを使用します。クラスには、ある種のインターフェイス マーキング機能があります。Serializable、Cloneable、および RandomAccess という 3 つの一般的なマーキング インターフェイスがあります。Cloneable インターフェイスが実装されていない場合、cloneNotSupportedException 例外が発生します。

Object クラスの clone メソッドは protected で変更されます。つまり、このメソッドをサブクラスでオーバーライドしないと、この保護されたアクセス許可はサブクラス外でのみアクセスできなくなります。これにより、サブクラスは親クラスのメソッドをオーバーライドするときに権限修飾子を大きくできるが、小さくはできないというステートメントも検証されます。

protected native Object clone() throws CloneNotSupportedException;

clone メソッドを内部的にオーバーライドするのは、実際にはアクセス権を拡張するためだけであり、protected を public に変更することもできます。将来相続する場合はそうします。もちろん、これは浅いクローンのクローン機能のみであり、深いクローンの場合は変更が必要です。

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

属性が String で、String もクラスである場合、String は参照型ですか? String は基本的な型と同様に動作します。つまり、複製後に 2 つの参照は同じ String を指しますが、どちらかが変更されると、String の値は変更されません。文字列が生成され、変更された参照は新しい文字列を指します。見た目はベーシックタイプと変わりません。

浅いクローン:

浅いクローンは、クラス User には、成績属性 Mark が含まれており、中国語、数学などで構成されています。浅いクローンの失敗

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}}
変更された元のユーザー: User{name='user', age=22, mark = Mark{chinese=100, math=60}}
変更されたクローン ユーザー: User{name='user', age=22, mark=Mark{chinese=100, math=60}}

#ユーザーのマークが変更された後、複製されたユーザーも変更されることは明らかです。影響を受けたくない場合は、ディープクローンを作成する必要があります。

ディープ クローン作成:

方法 1: クローン関数のネストされた呼び出し

参照型は完全にクローン化できないため、参照型も Cloneable インターフェイスを実装し、クローンを書き換えます。メソッドの場合、User クラスの clone メソッドは属性の clone メソッドを呼び出します。つまり、メソッド

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

のネストされた呼び出しです。出力結果は次のようになります。

元のユーザー: 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{chinese=100, math=99}}

方法 2: シリアル化

前の方法で十分にニーズを満たすことができますが、多くの関係があるか、一部の属性が配列である場合、配列は Cloneable インターフェイスを実装できません (clone メソッドで配列を手動でコピーできます)。ただし、毎回 clone メソッドを手動で記述する必要があり、これは非常に面倒です。メソッドは、各クラスに Serializable インターフェイスを実装するだけで済みます。これはマーク インターフェイスでもあります。最後に、シリアル化および逆シリアル化操作は、クローン作成 (配列のコピーを含む) の目的を達成するために使用されます。シリアル化と逆シリアル化に関する知識については、次の記事を参照してください。

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{chinese= 100, math =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 中国語 Web サイトの他の関連記事を参照してください。

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