原型模式
用原型實例指定創建對象的,拷貝這些對象生成新的對象進行使用。
也可以直接進行new一個對象,但是當對象的構造複雜時,new的效率會很低,使用clone更好
new適用於簡單的構造
clone適用於複雜的構造
應用場景
- 資源消耗大的對象
- 節省資源,比如在for循環內創建相同的對象
- 一個對象要讓其他人使用,並且使用過程中要改變這個對象的某些屬性,可考慮將原有對象拷貝,並且修改需要改變的屬性,在進行使用
- 對象結構複雜的情況使用拷貝
淺拷貝代碼實現
原型對象實現Cloneable接口(默認是淺拷貝)
class Prototype implements Cloneable {
public Prototype clone() {
Prototype prototype = null;
try {
prototype = (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return prototype;
}
}
原型對象具體的內容實現
class ConcretePrototype extends Prototype {
public String name;
public ArrayList<String> list = new ArrayList<>();
public ConcretePrototype() {
System.out.println("執行了ConcretePrototype構造函數");
}
@Override
public String toString() {
return "ConcretePrototype{" +
"name='" + name + '\'' +
", list=" + list +
'}';
}
public void show() {
System.out.println("原型模式實現類");
}
}
使用方式
ConcretePrototype cp = new ConcretePrototype();
cp.name = "原始數據";
for (int i = 0; i < 10; i++) {
cp.list.add(i, String.valueOf(i));//填充數據
}
ConcretePrototype cloneConcretePrototype = (ConcretePrototype) cp.clone();//實現拷貝
cloneConcretePrototype.name = "拷貝數據";
System.out.println(cp.toString());
System.out.println(cloneConcretePrototype.toString());
顯示結果:
執行了ConcretePrototype構造函數
ConcretePrototype{name='原始數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
ConcretePrototype{name='拷貝數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
原型對象不實現Cloneable接口(默認是淺拷貝)
其實就是把cloneable接口的內容手動實現一次
class ConcretePrototype2 {
public String name;
public ArrayList<String> list = new ArrayList<>();
public ConcretePrototype2() {
System.out.println("執行了ConcretePrototype2構造函數");
}
public ConcretePrototype2 clone() {
//手動new對象進行賦值
concretePrototype2 concretePrototype2 = new ConcretePrototype2();
concretePrototype2.name = this.name;
concretePrototype2.list = this.list;
return concretePrototype2;
}
@Override
public String toString() {
return "ConcretePrototype2{" +
"name='" + name + '\'' +
", list=" + list +
'}';
}
public void show() {
System.out.println("原型模式實現類");
}
}
調用方式
ConcretePrototype2 cp = new ConcretePrototype2();
cp.name = "原始數據";
for (int i = 0; i < 10; i++) {
cp.list.add(i, String.valueOf(i));//填充數據
}
ConcretePrototype2 cloneConcretePrototype = (ConcretePrototype2) cp.clone();//實現拷貝
cloneConcretePrototype.name = "拷貝數據";
System.out.println(cp.toString());
System.out.println(cloneConcretePrototype.toString());
顯示結果
執行了ConcretePrototype2構造函數
執行了ConcretePrototype2構造函數
ConcretePrototype2{name='原始數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
ConcretePrototype2{name='拷貝數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
構造函數執行了兩次,說明了cloneable接口的複製是不會執行2次構造的,直接new對象則會調用2次構造。
測試
修改複製後的數據
ConcretePrototype2 cp = new ConcretePrototype2();
cp.name = "原始數據";
for (int i = 0; i < 10; i++) {
cp.list.add(i, String.valueOf(i));//填充數據
}
ConcretePrototype2 cloneConcretePrototype = (ConcretePrototype2) cp.clone();//實現拷貝
cloneConcretePrototype.name = "拷貝數據";
cloneConcretePrototype.list.add("追加測試數據");
System.out.println(cp.toString());
System.out.println(cloneConcretePrototype.toString());
顯示結果
執行了ConcretePrototype2構造函數
執行了ConcretePrototype2構造函數
ConcretePrototype2{name='原始數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 追加測試數據]}
ConcretePrototype2{name='拷貝數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 追加測試數據]}
【追加測試數據無論是原型還是拷貝後的對象都有這個數據】
問題原因:深拷貝-淺拷貝
Cloneable接口默認實現的是淺拷貝,對於基本類型會進行拷貝,但是對於引用類型(集合,數據,對象)等clone僅僅代表指向了同一個內存地址,所以修改一個兩個都會變化。
解決方案:在引用類型的具體數據類型在進行一次clone,將淺拷貝處理爲深拷貝
深拷貝代碼實現
代碼如下
public ConcretePrototype2 clone() {
//手動new對象進行賦值
// ConcretePrototype2 concretePrototype2 = new ConcretePrototype2();
// concretePrototype2.name = this.name;
// concretePrototype2.list = this.list;
// return concretePrototype2;
//將具體的集合修改爲深拷貝
ConcretePrototype2 concretePrototype2 = new ConcretePrototype2();
concretePrototype2.name = this.name;
concretePrototype2.list = (ArrayList<String>) this.list.clone();
return concretePrototype2;
}
顯示結果
執行了ConcretePrototype2構造函數
執行了ConcretePrototype2構造函數
ConcretePrototype2{name='原始數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
ConcretePrototype2{name='拷貝數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 追加測試數據]}
應證我們的想法,對於集合,數組,對象等,如果要新增數據,需要對其重寫更改具體的數據類型。
總結
原型模式可以避免構造複雜的對象時的資源消耗問題,提升創建對象的效率。
可以保護原始對象的數據安全性
clone是二進制流,對於複雜的構造對象,性能提升明顯,但是對於簡單的構造沒必要使用clone