Javaオブジェクトとコレクションのコピー/クローン/コピー

8053 ワード

昨日、同僚はJavaBeanをコピーし、新しく作成したBeanを操作する必要があるという奇妙な問題に遭遇しました.しかし、彼が新しいBeanを操作すると、古いBeanの値に影響します.この問題を聞いたとき、私の最初の反応は彼のコピー方法に問題があり、aBeanの内容をbBeanにコピーしただけで、メモリの中で同じアドレスを指しています.ここでは,浅いコピーと深いコピーの2つのキーワードを引き出した.

浅いコピー(浅いクローン)


コピーされたオブジェクトのすべての変数値は元のオブジェクトの値と同じですが、コピーされたオブジェクトの参照は元のオブジェクトを指します.簡単に言えば、AをコピーしてBを生成し、Aの値をBにコピーするだけで、メモリの中で同じアドレスを指しており、影響はBを変えると同時に、Aの値も変わる.

深いコピー(深いクローン)


コピーされたオブジェクトのすべての変数値は、元のオブジェクトの値と同じであり、変数の値が変更されるだけでなく、新しい変数を指すアドレスも作成されます.ソースオブジェクトAとコピー後のBは,内容は同じであるが,それぞれの指向するアドレスが異なるため,変更は互いに影響を受けない.
その問題は、JavaのBeanオブジェクトがどのように深いコピーを行うかを知っています.

一、Javaオブジェクトクローン


Java内のオブジェクトの深いクローン


①Objectクラスのclone()メソッドを利用する.②派生クラスでベースクラスのcloneメソッドを書き換えpublicと宣言する.③派生クラスのclone()メソッドでsuperを呼び出す.clone(). ④派生クラスでCloneableインタフェースを実現する.
これはStudentのクラスで、clone()メソッドを書き換えました.
public class Student implements Cloneable {
    private String name;
    private int age;

    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
    public Object clone() {
        Object o = null;
        try {
            o = (Student)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        
        return o;
    }
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
}

テストclone
public static void main(String[] args) { 
    Student s1=new Student(" ",18);
    System.out.println(" s1 :" + s1.toString());
    
    Student s2=(Student)s1.clone(); 
    s2.setName(" ");
    s2.setAge(20);
    
    // 2 , 1 。
    System.out.println(" s1 :" + s1.toString());
    System.out.println("s2 :" + s2.toString());
}

コンソール出力の結果は次のとおりです.
 s1 :Student [name= , age=18]
 s1 :Student [name= , age=18]
s2 :Student [name= , age=20]

上の例のオブジェクトのプロパティは基本タイプですが、非基本タイプが含まれている場合は、結果はどうなりますか、上コード、Studioクラスを変更し、Courseクラスを追加します.Studentクラス
public class Student implements Cloneable {
    
    private String name;
    private int age;
    private Course course;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Course getCourse() {
        return course;
    }
    public void setCourse(Course course) {
        this.course = course;
    }
    
    public Student(String name, int age, Course course) {
        super();
        this.name = name;
        this.age = age;
        this.course = course;
    }
    
    public Student() {
    }
    
    public Object clone() {
        Object o = null;
        try {
            o = (Student)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        
        return o;
    }
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ", course=" + course + "]";
    }
    
}

Courseクラス

public class Course {
    
    private String name;
    private int value;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getValue() {
        return value;
    }
    public void setValue(int value) {
        this.value = value;
    }
    
    public Course(String name, int value) {
        super();
        this.name = name;
        this.value = value;
    }
    public Course() {
        
    }
    @Override
    public String toString() {
        return "Course [name=" + name + ", value=" + value + "]";
    }
    
}

クローンのテスト
public static void main(String[] args) { 
    
    Student s1 = new Student();
    s1.setName(" ");
    s1.setAge(24);
    
    Course c = new Course();
    c.setName(" ");
    c.setValue(80);
    
    s1.setCourse(c);
    System.out.println(" s1 :" + s1.toString());
    
    Student s2 = (Student)s1.clone();
    s2.setName(" ");
    s2.setAge(20);
    
    Course c2 = s2.getCourse();
    c2.setName(" ");
    c2.setValue(90);
    
    // 2 Course , 1 Course。
    System.out.println(" s1 :" + s1.toString());
    System.out.println("s2 :" + s2.toString());
}

コンソール出力結果
 s1 :Student [name= , age=24, course=Course [name= , value=80]]
 s1 :Student [name= , age=24, course=Course [name= , value=90]]
s2 :Student [name= , age=20, course=Course [name= , value=90]]

上記の例では、cloneメソッドを呼び出し、元のオブジェクトの内容をコピーすることを示しています.このような操作は基本データ型では問題ありませんが、非基本タイプでは、オブジェクトの参照のみが保存されます.
上記の問題を解決するために、深度クローンスキームを使用する必要があります.修正後のStudentクラスとCourseクラスは次のようになります.
public class Student implements Cloneable {
    
    private String name;
    private int age;
    private Course course;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Course getCourse() {
        return course;
    }
    public void setCourse(Course course) {
        this.course = course;
    }
    
    public Student(String name, int age, Course course) {
        super();
        this.name = name;
        this.age = age;
        this.course = course;
    }
    
    public Student() {
    }
    
    public Student clone() {
        Student s = null;
        try {
            s = (Student)super.clone();
            s.course = course.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        
        return s;
    }
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ", course=" + course + "]";
    }
    
}

Courseクラス

public class Course implements Cloneable{
    
    private String name;
    private int value;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getValue() {
        return value;
    }
    public void setValue(int value) {
        this.value = value;
    }
    
    public Course(String name, int value) {
        super();
        this.name = name;
        this.value = value;
    }
    public Course() {
        
    }
    
    public Course clone() {
        Course c = null;
        try {
            c = (Course)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        
        return c;
    }
    @Override
    public String toString() {
        return "Course [name=" + name + ", value=" + value + "]";
    }
    
}

テストメソッドは、前のメソッド、次の実行後の結果を使用します.
 s1 :Student [name= , age=24, course=Course [name= , value=80]]
 s1 :Student [name= , age=24, course=Course [name= , value=80]]
s2 :Student [name= , age=20, course=Course [name= , value=90]]

その結果,深さクローンによりclone後のオブジェクトの非基本クラスの変数が修正され,元のオブジェクトに影響を及ぼさないことが分かった.

集合のClone操作についても,集合の属性が基本データ型であればループ割り当て,addAll()メソッドを用いても元の集合には影響しない.集合に非基本データ型が格納されている場合、上記のようなStudioオブジェクトには、Studioオブジェクトに書き換えclone()メソッドを追加する必要があります。