4、原型模式(設計模式筆記)

一、實質

  • 通過new產生一個對象有時需要非常頻繁的數組準備或訪問權限,則可以使用原型模式。就是java中的克隆技術,以某個對象為原型,復制出新的對象。顯然,新的對象具備原型對象的特點。
  • 優勢:效率高(直接克隆,避免了重新執行構造過程步驟)
  • 克隆類似于new,但是不同于newnew創建新的對象屬性采用的是默認值。克隆出的對象的屬性值完全和原型對象相同。并且克隆出的新對象改變不會影響原型對象。然后,再修改克隆對象的值。

二、原型模式的實現

  • 實現Cloneable接口和覆寫clone方法
  • Prototype模式中實現起來最困難的地方就是內存復制操作,所幸在java中提供了clone()方法替我們做了絕大部分事情。

三、應用場景

springbean的創建實際就兩種:單例模式和原型模式(當然原型模式需要和工廠模式搭配起來)

Sheep.java

package cn.itcast.day233.prototype;
import java.util.Date;
//克隆羊多利,實現Cloneable接口,此接口是一個空接口
public class Sheep implements Cloneable {
    
    private String name;
    private Date birthday;
    
    public Sheep() {
    }

    public Sheep(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj =  super.clone();//直接使用父類的克隆方法
        
        //添加如下代碼實現深復制
        Sheep s = (Sheep) obj;
        s.birthday = (Date) this.birthday.clone();//將屬性中的對象(非基本數據類型)也進行拷貝
        return obj;
    }

    public String getName() {
        return name;
    }

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

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

說明:上面我們給出了淺克隆和深克隆的兩種情況,下面進行測試。

Client.java

package cn.itcast.day233.prototype;
import java.util.Date;
//測試原型模式(淺克隆或者深克隆,兩者有一些區別)
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Date date = new Date(1241341651L);
        Sheep s1 = new Sheep("多利", date);
        System.out.println(s1);
        System.out.println(s1.getName() + "," + s1.getBirthday());
        
        //通過s1克隆一個對象,可以看到這是兩個不同的對象,但是它們屬性的值是相同的,我們可以自己修改
        Sheep s2 = (Sheep) s1.clone();
        System.out.println(s2);
        System.out.println(s2.getName() + "," + s2.getBirthday());
        
        date.setTime(44444444L);
        //下面在淺克隆的情況下輸出為:Thu Jan 01 20:20:44 CST 1970,Thu Jan 01 20:20:44 CST 1970
        //在深克隆的情況下輸出為:Thu Jan 01 20:20:44 CST 1970,Thu Jan 15 16:49:01 CST 1970
        System.out.println(s1.getBirthday() + "," + s2.getBirthday());
    }
}

說明:當一個對象中的屬性有其他對象類型時,淺克隆和深克隆就會表現出區別。在測試例子中我們可以看到當改變第一個對象的日期屬性的時候,如果是淺復制,那么原對象和克隆對象的日期屬性都會發生改變,而如果是深復制,那么只會影響原對象,而不會影響克隆對象。上面是通過克隆的方式來達到復制的目的的,下面我們看通過序列化和反序列化實現對象的復制。

Sheep1.java

package cn.itcast.day233.prototype;
import java.io.Serializable;
import java.util.Date;
//克隆羊多利,實現Cloneable接口,此接口是一個空接口
public class Sheep1 implements Cloneable, Serializable {
    
    private String name;
    private Date birthday;
    
    public Sheep1() {
    }

    public Sheep1(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }

    //此類自己本身沒有實現深克隆
    protected Object clone() throws CloneNotSupportedException {
        Object obj =  super.clone();//直接使用父類的克隆方法
        return obj;
    }

    public String getName() {
        return name;
    }

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

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

說明:上述對象本身并沒有實現深復制,下面我們通過序列化和反序列化實現深復制。

Client1.java

package cn.itcast.day233.prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
//利用序列化和反序列化技術實現深克隆
public class Client1 {
    public static void main(String[] args) throws Exception {
        Date date = new Date(1241341651L);
        Sheep1 s1 = new Sheep1("多利", date);
        System.out.println(s1.getName() + "," + s1.getBirthday());
        
        //使用序列化和反序列化實現
        //序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(s1);
        byte[] bytes = bos.toByteArray();
        //反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bis);
        Sheep1 s2 = (Sheep1) ois.readObject();
        System.out.println(s2.getName() + "," + s2.getBirthday());
        
        date.setTime(44444444L);
        System.out.println(s1.getBirthday() + "," + s2.getBirthday());
    }
}

說明:可以看到和之前的深復制的測試結果是一致的。下面我們測試比較new和克隆的效率。

Client2.java

package cn.itcast.day233.prototype;
//測試克隆比new的效率高
public class Client2 {
    
    public static void testNew(int size){
        long start = System.currentTimeMillis();
        for(int i = 0; i < size; i++){
            Laptop t = new Laptop();
        }
        long end = System.currentTimeMillis();
        System.out.println("new耗時" + (end - start));
    }
    
    public static void testClone(int size) throws Exception{
        long start = System.currentTimeMillis();
        Laptop t = new Laptop();
        Laptop[] arr = new Laptop[size];
        for(int i = 0; i < size; i++){
            arr[i] = (Laptop) t.clone();
        }
        long end = System.currentTimeMillis();
        System.out.println("clone耗時" + (end - start));
    }
    
    public static void main(String[] args) throws Exception {
        testClone(1000);
        testNew(1000);
    }
}

class Laptop implements Cloneable{
    public Laptop(){
        try {
            Thread.sleep(10);//假設一個筆記本對象的創建過程很耗時
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

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

說明:測試結果為:

clone耗時12
new耗時10483

可以看到差距非常明顯,當然這只是在構造一個對象非常耗時的情況下的結果,如果一個類本身構造就很便捷,那么就不必使用原型模式了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容