定義: 指原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。不需要知道任何創建的細節,不調用構造函數適用場景: 詳解: 接下來我們分下麵幾部分講解: 1.原型模式的核心 其實很簡單,就是實現Cloneable介面,然後重寫clone()方法。上面我們已經說過 ,當你在上面的適用場景中的時 ...
定義:
指原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。不需要知道任何創建的細節,不調用構造函數
適用場景:
- 類初始化的時候消耗較多資源
- new產生的對象需要非常繁瑣的過程
- 構造函數比較複雜
- 迴圈體中產生大量對象
詳解:
接下來我們分下麵幾部分講解:
- 原型模式的核心
- 深克隆和淺克隆
- JDK源碼分析
1.原型模式的核心
其實很簡單,就是實現Cloneable介面,然後重寫clone()方法。上面我們已經說過 ,當你在上面的適用場景中的時候,按照我們平常的辦法來說肯定是直接new對象出來,但是new對象特別多的時候就會消耗很多資源,並且效率也是比較緩慢的。所以我們引入原型模式的情況,其實我們只需要創建出一個原型來 ,剩下的完全可以通過克隆來達到創建新對象的目的。克隆是底層直接拿二進位流來克隆出新對象,然後對新對象進行特別的操作。
2.深克隆和淺克隆
這時候你心裡可能會有疑惑,克隆不就克隆就行了嗎?怎麼還分淺克隆和深克隆。事實上在一個類中如果有另外的類的實例作為屬性的話,正常使用Object.clone()方法,這個對象成員是無法被克隆的,也就是淺克隆。所以你怎麼改原型中的對象成員,後面克隆的版本中這個對象成員就會一直跟原型一樣。但是我們的目標是創建新對象來進行特定的操作,也就是希望每個對象裡面的值不會跟他人共用。新對象是新對象,原型是原型。所以我們需要深克隆。下麵我舉幾個例子來看看
public class Student implements Cloneable{ private int age; private Date date; private String name; public void setAge(int age) { this.age = age; } public void setDate(Date date) { this.date = date; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student{" + "age=" + age + ", date=" + date + ", name='" + name + '\'' + '}'; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
然後我們寫一下測試類
class Test { public static void main(String[] args) throws CloneNotSupportedException { Student stu=new Student(); Student stu1= (Student) stu.clone(); Date date=new Date(0L); int i=10; stu.setDate(date); stu1.setDate(date); stu.setAge(i); stu1.setAge(i); System.out.println(stu); System.out.println(stu1); date.setTime(666666666L); stu.setDate(date); i=11; stu.setAge(i); System.out.println(stu); System.out.println(stu1); } }
最終輸出結果就是
分析一下
註意看這4行,我們上面是兩個都調用了set方法,下麵只有stu原型調用了set方法,但是最終卻兩個對象中的值一起改了。可能細心的的註意到Age只有stu改了,這是因為int類型基本數據類型。而Date類型的對象成員就不行了,實際上只有Student這個類進行了克隆,但是Student裡面的對象成員變數沒有進行克隆。所以那個對象還是那個對象。
上面就是淺克隆。要想做到深克隆,可以在clone方法裡面clone出對應的對象成員結果及代碼如下
上面我重寫了clone()方法,實現了深克隆
3.JDK源碼解析
其實我們主要理解拷貝原型來創建新的對象,拷貝是比new更快的一個創建對象的方法,當你需要大批量創建新對象而且都是同一個類的對象的時候可以考慮用原型模式。但是千萬千萬註意就是一般的克隆只是淺克隆(淺克隆:只是對象的hash值不一樣,但是對象裡面的對象成員變數的hash值是一樣的)有些場景可能是需要我們深克隆的,這時候就需要我們重寫Object.clone()方法。就拿ArrayList中的clone()方法來看
public Object clone() { try { ArrayList<?> v = (ArrayList<?>) super.clone();//先克隆出一個ArrayList v.elementData = Arrays.copyOf(elementData, size);//將原型中的數據拷貝到新的ArrayList中 v.modCount = 0;//把修改次數改為0 return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); } }
相信看懂了上面的實例,你也能理解ArrayList中這段代碼到底是做什麼
總結:
儘管我們會用了原型,知道拷貝比new快,知道深克隆,但是具體的還是看業務場景的需求,希望能多理解適用場景的那幾種情況。