Java中深淺克隆的區別

目錄

深克隆與淺克隆區別

淺克隆demo

深克隆demo

文末思考


克隆與淺克隆區別

深淺克隆的區別在於,能否支持引用類型(包括類、接口、數組等)的成員變量的複製。
淺克隆:對象只複製了它本身和其中包含的值類型的成員變量,引用類型的成員對象並沒有複製。
深克隆:對象本身以及包含的所有成員變量都會被複制。

淺克隆demo

關鍵點:Student類,實現Cloneable接口。

Student中數值類型屬性 name sex,引用類型teacher

public class Student implements Cloneable {
    String name;
    String sex;
    Teacher teacher;

    public Student(String name, String sex, Teacher teacher) {
        this.name = name;
        this.sex = sex;
        this.teacher = teacher;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", teacher=" + teacher +
                '}';
    }
}
/**
 * 教師類
 */
public class Teacher {
    String name;
    String sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }

    public Teacher(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

}

測試類:

public class LightCloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher = new Teacher("laoshi", "男");
        Student a = new Student("zhangsan", "男", teacher);
        Student b = (Student) a.clone();
        System.out.println(a);
        System.out.println(b);
        System.out.println(a.equals(b));
        System.out.println("b爲a克隆的一個對象,兩者引用不相等");

        System.out.println("對克隆的的值屬性修改,a的性別屬性修改爲女");
        b.setSex("女");
        System.out.println(a);
        System.out.println(b);
        System.out.println("對克隆的的值屬性修改,被克隆的對象中對應值屬性無變化了");

        // 對a的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi
        System.out.println("對a的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi");
        a.getTeacher().setName("laosi");
        System.out.println(a);
        System.out.println(b);
        System.out.println("對被克隆的的對象進行屬性修改,克隆的對象中對應屬性也變化了");

        // 對b的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi
        System.out.println("對克隆的的對象進行屬性修改,將teacher的name屬性由改爲laowu\"");
        b.getTeacher().setName("laowu");
        System.out.println(a);
        System.out.println(b);
        System.out.println("對克隆的的對象進行屬性修改,被克隆的對象中對應屬性也變化了");

    }
}

運行結果:

Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
false
b爲a克隆的一個對象,兩者引用不相等
對克隆的的值屬性修改,a的性別屬性修改爲女
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laoshi', sex='男'}}
對克隆的的值屬性修改,被克隆的對象中對應值屬性無變化了
對a的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi
Student{name='zhangsan', sex='男', teacher=Teacher{name='laosi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laosi', sex='男'}}
對被克隆的的對象進行屬性修改,克隆的對象中對應屬性也變化了
對克隆的的對象進行屬性修改,將teacher的name屬性由改爲laowu"
Student{name='zhangsan', sex='男', teacher=Teacher{name='laowu', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laowu', sex='男'}}
對克隆的的對象進行屬性修改,被克隆的對象中對應屬性也變化了

可以看出:對象只複製了它本身和其中包含的值類型的成員變量,修改克隆對象的值屬性,不會影響被克隆對象屬性的值,但修改引用會影響被克隆的引用屬性。

深克隆demo

關鍵點:Student類,實現Cloneable接口 重寫clone方法,引用屬性teacher類也需要實現Cloneable接口。

如:Student的重寫clone方法:

    @Override
    public Object clone() throws CloneNotSupportedException {
        Student student = (Student)super.clone();
        student.setTeacher((Teacher)student.getTeacher().clone());
        return student;
    }

引用屬性teacher類需要實現Cloneable接口。

測試類:

public class DeepCloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher = new Teacher("laoshi", "男");
        Student a = new Student("zhangsan", "男", teacher);
        Student b = (Student) a.clone();
        System.out.println(a);
        System.out.println(b);
        System.out.println(a.equals(b));
        System.out.println("b爲a克隆的一個對象,兩者引用不相等");

        System.out.println("對克隆的的值屬性修改,a的性別屬性修改爲女");
        b.setSex("女");
        System.out.println(a);
        System.out.println(b);
        System.out.println("對克隆的的值屬性修改,被克隆的對象中對應值屬性未變化");

        // 對a的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi
        System.out.println("對a的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi");
        a.getTeacher().setName("laosi");
        System.out.println(a);
        System.out.println(b);
        System.out.println("對被克隆的的對象進行屬性修改,克隆的對象中對應屬性未變化");

        // 對b的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi
        System.out.println("對克隆的的對象進行屬性修改,將teacher的name屬性由改爲laowu\"");
        b.getTeacher().setName("laowu");
        System.out.println(a);
        System.out.println(b);
        System.out.println("對克隆的的對象進行屬性修改,被克隆的對象中對應屬性未變化");

    }
}

運行結果:

Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
false
b爲a克隆的一個對象,兩者引用不相等
對克隆的的值屬性修改,a的性別屬性修改爲女
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laoshi', sex='男'}}
對克隆的的值屬性修改,被克隆的對象中對應值屬性未變化
對a的引用類型成員變量teacher進行修改,將teacher的name屬性由laoshi改爲laosi
Student{name='zhangsan', sex='男', teacher=Teacher{name='laosi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laoshi', sex='男'}}
對被克隆的的對象進行屬性修改,克隆的對象中對應屬性未變化
對克隆的的對象進行屬性修改,將teacher的name屬性由改爲laowu"
Student{name='zhangsan', sex='男', teacher=Teacher{name='laosi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laowu', sex='男'}}
對克隆的的對象進行屬性修改,被克隆的對象中對應屬性未變化

可以看到手動實現clone方法對引用對象進行克隆,如果引用對象還有引用類型屬性,那麼只能繼續低引用對象進行clone實現。

那麼有沒有更好的方法實現深度克隆呢?

當然有,使用序列化,之需要將Student和Teacher類都實現Serializable接口,Student重寫clone方法。

如下:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Student implements Serializable {
    String name;
    String sex;
    Teacher teacher;

    public Student(String name, String sex, Teacher teacher) {
        this.name = name;
        this.sex = sex;
        this.teacher = teacher;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        Student student = null;
        try{
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            // 將流序列化成對象
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            student = (Student) ois.readObject();
        }catch (Exception e){
            System.out.println(e.toString());
        }
        return student;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", teacher=" + teacher +
                '}';
    }
}


import java.io.Serializable;

/**
 * 教師類
 */
public class Teacher implements Serializable {
    String name;
    String sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }

    public Teacher(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

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

使用Serializable接口序列化,引用對象也必須實現Serializable接口。

文末思考

除了使用Serializable接口序列化實現深克隆,還有其他方法嗎?

提示:使用使用Json。

微信掃碼關注更精彩
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章