天天看點

結合JDK源碼看設計模式——原型模式

定義:

  指原型執行個體指定建立對象的種類,并且通過拷貝這些原型建立新的對象。不需要知道任何建立的細節,不調用構造函數

适用場景:

  1. 類初始化的時候消耗較多資源
  2. new産生的對象需要非常繁瑣的過程
  3. 構造函數比較複雜
  4. 循環體中産生大量對象

詳解:

  接下來我們分下面幾部分講解:

  1. 原型模式的核心
  2. 深克隆和淺克隆
  3. 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);
}
}      

最終輸出結果就是

結合JDK源碼看設計模式——原型模式

分析一下

結合JDK源碼看設計模式——原型模式

注意看這4行,我們上面是兩個都調用了set方法,下面隻有stu原型調用了set方法,但是最終卻兩個對象中的值一起改了。可能細心的的注意到Age隻有stu改了,這是因為int類型基本資料類型。而Date類型的對象成員就不行了,實際上隻有Student這個類進行了克隆,但是Student裡面的對象成員變量沒有進行克隆。是以那個對象還是那個對象。

結合JDK源碼看設計模式——原型模式

上面就是淺克隆。要想做到深克隆,可以在clone方法裡面clone出對應的對象成員結果及代碼如下

結合JDK源碼看設計模式——原型模式
結合JDK源碼看設計模式——原型模式

  上面我重寫了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快,知道深克隆,但是具體的還是看業務場景的需求,希望能多了解适用場景的那幾種情況。