定義:
- 用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象
通用類圖:
原型模式通用類圖
/*
* 實現Cloneable接口,復習父類中的clone方法
* */
public class Prototype implements Cloneable {
@Override
public Prototype clone(){
// TODO Auto-generated method stub
Prototype prototype = null;
try {
prototype = (Prototype) super.clone();
} catch (Exception e) {
// TODO Auto-generated catch block
// 相關的異常處理
e.printStackTrace();
}
return prototype;
}
}
原型模式的優點(通常是和工廠方法模式一起出現,通過clone的方法創建一個對象,然后由工廠方法提供給調用者):
- 性能優良,原型模式是對內存中二進制流的拷貝,要比直接new一個對象的性能好很多
- 逃避構造函數的約束,直接在內存中拷貝,構造函數是不會執行的。
原型模式的使用場景:
- 資源優化場景,類初始化需要消耗非常多的資源,數據、硬件資源等
- 性能和安全要求的場景,通過new產生一個對象需要非常繁瑣的數據準備或訪問權限
- 一個對象多個修改者
原型模式的注意事項
- 構造函數不會被執行(new 會執行構造函數中的方法,通過clone則不會執行構造函數中的方法)
- 淺拷貝和深拷貝
/*
* 淺拷貝代碼
* */
public class ThingSimple implements Cloneable {
// 定義一個私有變量
private ArrayList<String> arrayList = new ArrayList();
@Override
public ThingSimple clone() {
// TODO Auto-generated method stub
ThingSimple thingSimple = null;
try {
thingSimple = (ThingSimple) super.clone();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return thingSimple;
}
public void setValue(String value) {
this.arrayList.add(value);
}
public ArrayList<String> getValue() {
return this.arrayList;
}
}
public class ThingDeep implements Cloneable {
// 定義一個私有變量
private ArrayList<String> arrayList = new ArrayList();
@Override
public ThingDeep clone() {
// TODO Auto-generated method stub
ThingDeep thingDeep = null;
try {
thingDeep = (ThingDeep) super.clone();
thingDeep.arrayList = (ArrayList<String>) this.arrayList.clone();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return thingDeep;
}
public void setValue(String value) {
this.arrayList.add(value);
}
public ArrayList<String> getValue() {
return this.arrayList;
}
}
public class Client {
public static void main(String[] args) {
// 測試淺拷貝
ThingSimple thingSimple = new ThingSimple();
thingSimple.setValue("1");
ThingSimple copySimple = thingSimple.clone();
copySimple.setValue("2");
System.out.println("原數據-->" + thingSimple.getValue());
System.out.println("拷貝數據-->" + copySimple.getValue());
System.out.println(">>>>>>>>>>>>>>>");
// 測試深拷貝
ThingDeep thingDeep = new ThingDeep();
thingDeep.setValue("3");
ThingDeep copyDeep = thingDeep.clone();
copyDeep.setValue("4");
System.out.println("原數據-->" + thingDeep.getValue());
System.out.println("拷貝數據-->" + copyDeep.getValue());
}
}
原數據-->[1, 2]
拷貝數據-->[1, 2]
>>>>>>>>>>>>>>>
原數據-->[3]
拷貝數據-->[3, 4]
解析:java做了一個偷懶的拷貝動作,Object類提供的方法clone只是拷貝對象本身,其對象內部的數組、
引用對象等都不拷貝,還是指向原生對象的內部元素地址,這種拷貝叫做淺拷貝,
多個對象共享一個變量,是一種非常不安全的方式。內部的數組和引用對象不拷貝,
其他原始類型,比如int、long、char等都會被拷貝,但是對于string類型,
java希望你認為它是基本類型,它是沒有clone方法的,并且處理機制也比較特殊,
通過字符串池(stringpool)在需要的時候才在內存中創建新的字符串。
注:在使用原型模式的時候,引用的成員變量必須滿足,一是類的成員變量,
不是方法內變量,二必須是可變的引用對象,而不是一個原始的類型或不可變對象。
對于深拷貝不能使用final進行修飾,因為要重新賦值,對于淺拷貝因為指向的是
內存中同一個地址,所以無影響。