最近買了一本設計模式的書籍看了看,發現自己對設計模式沒有什麼概念,同時,在看某些設計模式的時候發現在項目中應用了不少,但是是哪個設計模式卻說不明白!!
不扯皮,今天記錄的是原型模式,java中天然支援原型模式,也就是在jdk層面就支援這個了(clone),代理模式也是(Proxy)。
概念
用原型執行個體制定建立對象的種類,并通過拷貝這些原型并建立新的對象,相當于是拷貝一份副本
看到這個概念的時候我不禁想起了java的Object類的clone方法麼? 難怪java天然支援拷貝模型。話說回來,在java中如果實作克隆對象,要有兩個标準
- 必須要實作人家自帶的Cloneable接口
- 必須要重寫clone方法。
文字太幹燥了,直接上栗子:
import lombok.Data;
@Data
public class User implements Cloneable{
private String name ;
private int age;
private int sex;
@Override
protected Object clone() {
try {
// 這裡是重寫clone方法的邏輯
User user = (User) super.clone();
return user;
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return null;
}
}
複制
再來上一個測試類:
public class Client {
public static void main(String[] args) {
User user = new User();
user.setAge();
User cloneUser = (User)user.clone();
System.out.println(cloneUser);
}
}
####得到結果 ::User(name=null, age=18, sex=0)
複制
結果: 拷貝成功
原理: 因為需要實作cloneable接口,這個接口它是一個标記接口,沒有任何方法(跟Serializable一樣),jvm用來對标記的對象執行它的clone方法,然後進行記憶體二進制流的拷貝,是以性能會比new好很多。
這裡問題又來了,如果你克隆的對象裡面引用了其他對象呢??試試?
@Data
public class User implements Cloneable{
//很多根頭發
private ArrayList hairs = new ArrayList();
private String name ;
private int age;
private int sex;
@Override
protected Object clone() {
try {
User user = (User) super.clone();
return user;
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return null;
}
}
public class Client {
public static void main(String[] args) {
User user = new User();
user.setAge();
user.getHairs().add("aaaa");
User cloneUser = (User)user.clone();
cloneUser.getHairs().add("abc");
System.out.println(cloneUser);
System.out.println(user);
}
}
### User(hairs=[aaaa, abc], name=null, age=18, sex=0)
###User(hairs=[aaaa, abc], name=null, age=18, sex=0)
複制
翻車了? 沒錯,當對象裡面引用另外一個對象的時候,它這裡其實是引用這個對象的位址,這種拷貝也就是我們常說的淺拷貝,有人說了,String不就是引用的對象麼?注意:String它是jdk提供的不可變對象,它内部是通過final類型的char數組實作的,并沒有實作clone 方法,是以它是可以直接被克隆的,與此相同的,比如int Integer,long Long …等等都是可以直接被拷貝的。
那諸如上面的情況,應該如何修改呢?也就是如何修改為深拷貝呢?
so essy!!!直接對内部的對象引用也執行clone方法
@Override protected Object clone() { try { User user = (User) super.clone(); user.hairs = (ArrayList) hairs.clone(); return user; }catch (CloneNotSupportedException e){ e.printStackTrace(); } return null; }
複制
對,沒錯,這裡我們在點到ArrayList源碼看一下,它是有實作clone方法的
至此,原型模式的栗子就舉完畢了 !
原型模式使用場景
- 在使用多個對象但又不便于建立多個對象所承擔的消耗的時候,可以使用克隆
- 通過new一個對象的時候需要很繁瑣的進行資料準備,可以使用原型模式對目前對象進行重複修改(一個對象多個修改者的場景)