JAVA 基礎 - clone淺克隆與深克隆

再見理想2017發表於2017-02-23

介紹

一直以來只知道Java有clone方法,該方法屬於Object的,對於什麼是淺克隆與深克隆就比較模糊了,現在就來補充學習一下。

概念

淺拷貝(淺克隆)
複製出來的物件的所有變數都含有與原來的物件相同的值,而所有的對其他物件的引用仍然指向原來的物件。

深拷貝(深克隆)
複製出來的所有變數都含有與原來的物件相同的值,那些引用其他物件的變數將指向複製出來的新物件,而不再是原有的那些被引用的物件。換言之,深複製把要複製的物件所引用的物件都複製了一遍。

實現

Student.java

public class Student implements Cloneable {

    private String name;
    private int age;
    private Teacher teacher;

    public Student(String name, int age, Teacher teacher) {
        this.name = name;
        this.age = age;
        this.teacher = teacher;
    }
}複製程式碼

Teacher.java

public class Teacher implements Cloneable {

    private String name;
    private String course;

    public Teacher(String name, String course) {
        this.name = name;
        this.course = course;
    }
}複製程式碼

Student類中包含有name,age和Teacher物件。

淺克隆

克隆物件實現Cloneable介面(該介面是一個標記介面),在克隆的方法裡面呼叫super.clone(),就會返回克隆後的物件。

public class Student implements Cloneable {

    private String name;
    private int age;
    private Teacher teacher;

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

    public Student clone() {
        Student student = null;
        try {
            student = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;
    }
}複製程式碼

測試

 public static void main(String args[]) throws IOException, ClassNotFoundException {
        Teacher teacher = new Teacher("王老師", "英語");
        Student student = new Student("小明", 11, teacher);

        Student clone = student.clone();
        clone.setName("小強");
        clone.setAge(20);
        clone.getTeacher().setName("李老師");

        System.out.println(student.getName() + " " + student.getAge());
        System.out.println(clone.getName() + " " + clone.getAge());
        System.out.println(clone.getTeacher() == student.getTeacher());
    }複製程式碼

輸出:

小明 11
小強 20
true複製程式碼

從上面結果可知,克隆出來的Student物件裡的name和age是新的,但是teacher是和原來的共享的,這就是淺克隆。

深克隆

Student.java

public class Student implements Cloneable {

    private String name;
    private int age;
    private Teacher teacher;

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

    public Student clone() {

        Student student = null;
        try {
            student = (Student) super.clone();
            Teacher teacher = this.teacher.clone();
            student.setTeacher(teacher);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;
    }
}複製程式碼

Teacher.java

public class Teacher implements Cloneable {

    private String name;
    private String course;

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

    public Teacher clone() {

        Teacher clone = null;
        try {
            clone = (Teacher) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
}複製程式碼

要實現深克隆的話,克隆物件裡面的物件型別也必須實現Cloneable介面並呼叫clone()。

測試:

public static void main(String args[]) throws IOException, ClassNotFoundException {
    Teacher teacher = new Teacher("王老師", "英語");
    Student student = new Student("小明", 11, teacher);

    Student clone = student.clone();
    clone.setName("小強");
    clone.setAge(20);
    clone.getTeacher().setName("李老師");

    System.out.println(student.getName() + " " + student.getAge());
    System.out.println(clone.getName() + " " + clone.getAge());
    System.out.println(clone.getTeacher() == student.getTeacher());
}複製程式碼

輸出:

小明 11
小強 20
false複製程式碼

這時,兩個物件的中的Teacher就不是同一個物件了,實現了深克隆,但是如果要克隆的物件繼承鏈比較長的話要實現深克隆,就必須逐層地實現Cloneable,這個過程是比較麻煩的,不過還有一種方法可以簡便地實現深克隆。

serializable克隆

大家知道,Java可以把物件序列化寫進一個流裡面,反之也可以把物件從序列化流裡面讀取出來,但這一進一出,這個物件就不再是原來的物件了,就達到了克隆的要求。

public class Student implements  Serializable {

    private String name;
    private int age;
    private Teacher teacher;

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

    public Student serializableClone() throws IOException, ClassNotFoundException {
        Student clone;

        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(bo);
        oo.writeObject(this);
        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);
        clone = (Student) oi.readObject();

        return clone;
    }
}複製程式碼

通過把物件寫進ByteArrayOutputStream裡,再把它讀取出來。注意這個過程中所有涉及的物件都必須實現Serializable介面,由於涉及IO操作,這種方式的效率會比前面的低。

以上就是本人對Java克隆的一點理解
如有不對的地方,歡迎指正。

相關文章