1. 程式人生 > >java設計模式學習筆記--原型模式(淺克隆和深克隆)

java設計模式學習筆記--原型模式(淺克隆和深克隆)

1. 什麼是原型模式

原型模式屬於物件的建立模式。。原型模式允許你通過複製現有的例項來建立新的例項。
這個模式的重點在於,客戶端的程式碼在不知道要例項化何種特定類的情況下,可以製造出新的例項。在java中,一般使用clone()的方法,或者序列化。

2. 原型模式的實現

在java中,由於Object類中有一個clone()方法,所以要使用原型模式非常簡單,只要實現Cloneable的介面就可以了。
Java語言提供的Cloneable介面只起一個作用,就是在執行時期通知Java虛擬機器可以安全地在這個類上使用clone()方法。通過呼叫這個clone()方法可以得到一個物件的複製。由於Object類本身並不實現Cloneable介面,因此如果所考慮的類沒有實現Cloneable介面時,呼叫clone()方法會丟擲CloneNotSupportedException異常。
具體程式碼如下:

public class Student implements Cloneable ,Serializable{

    private String name;
    private transient int age;
    private Book book;

    //淺克隆
    public Student clone(){
        Student stu = null;
        try {
            stu = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return
stu; } //深克隆 public Student deepClone() throws Exception { Student stu = null; ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bo); oos.writeObject(this); ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi = new
ObjectInputStream(bi); stu = (Student) oi.readObject(); return stu; } public Student(String name, int age, Book book) { this.name = name; this.age = age; this.book = book; } 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 Book getBook() { return book; } public void setBook(Book book) { this.book = book; } }
public class Book implements Serializable{

    private String name;

    public Book(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Client {
    public static void main(String[] args){
        Book book = new Book("Chinese");
        Student s = new Student("Tom",12,book);
        Student s1 = s.clone();

        System.out.println(s==s1);
        System.out.println(s.getClass()==s1.getClass());
        System.out.println(s.getBook()==s1.getBook());

        System.out.println("------------------------");

        try {
            Student s2 = s.deepClone();

            System.out.println(s==s2);
            System.out.println(s.getClass()==s2.getClass());
            System.out.println(s.getBook()==s2.getBook());

            System.out.println("-------------------");
            System.out.println("s.age:"+s.getAge()+" s2.age:"+s2.getAge());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

執行結果如下:
這裡寫圖片描述

在例子中可以看到,使用Clone()方法,我們可以很容易的複製一個新的例項。
但是,這樣的複製,使用的是淺克隆,那麼什麼是淺克隆哪?

3. 淺克隆與深克隆

淺克隆:
只負責克隆按值傳遞的資料(比如基本資料型別、String型別),而不復制它所引用的物件,換言之,所有的對其他物件的引用都仍然指向原來的物件。
也就是說如果被克隆的物件中,有對其他物件的引用,那麼就只複製那個物件的引用,而不是重新複製一個新的物件。
在上面的例子中,Student物件中具有對Book的引用,那麼在使用淺克隆時(使用Clone()方法),複製的s1的book和原來的book是同一個物件,所以s.getBook() ==s1.getBook() 返回 true。

深克隆:
除了淺度克隆要克隆的值外,還負責克隆引用型別的資料。那些引用其他物件的變數將指向被複制過的新物件,而不再是原有的那些被引用的物件。換言之,深度克隆把要複製的物件所引用的物件都複製了一遍,而這種對被引用到的物件的複製叫做間接複製。
深克隆的方法有,反序列化,重寫Clone()方法等。在上述例子中,就使用了序列化和反序列化的手段。由此,s.getBook() ==s1.getBook() 返回 false。

利用序列化實現深度克隆
把物件寫到流裡的過程是序列化(Serialization)過程;而把物件從流中讀出來的過程則叫反序列化(Deserialization)過程。應當指出的是,寫到流裡的是物件的一個拷貝,而原物件仍然存在於JVM裡面。
在序列化一個物件時,應該先實現Serializable介面,然後把物件(實際是物件的拷貝)寫到流裡面,然後再從流裡面反序列化出來,這樣就重新建立了一個物件。
使用這個方法有一個前提,那就是要克隆的物件所引用的所有物件都應該是可序列化的,那麼如果遇到不可序列化的物件時應該怎麼辦哪?
這時就要用到transient關鍵字了(注意:transient只能修飾變數),被transient修飾的變數在序列化時不會被序列化。在上述的例子中,Student的age變數使用了transient關鍵字修飾,所以我們可以看到s.getAge()的值為12,而s2.getAge()的值為0(int型別預設值為0)。

4.原型模式的優缺點

優點:
1、向客戶隱藏製造新例項的複雜性。
2、提供讓客戶能夠產生未知型別物件的選項。
3、在某些環境下,複製物件比建立物件更加有效。

缺點:
1、物件的複製有時相當的複雜。特別是當一個類引用不支援序列化的間接物件,或者引用含有迴圈結構的時候。

用途:
1、在一個複雜的類層次中,當系統必須從其中的許多型別建立新物件時,可以考慮原型