今天老王來聊一聊原型模式,內容相對簡單,相信一看就懂。
一、引言
原型模式是什麼?爲什麼要學原型模式?原型模式到底怎麼做?還是按照這個學習套路來。
首先原型模式有幾個關鍵詞(克隆,拷貝,複製),故名思意,對於一個對象,我們將對他用原型模式進行克隆,拷貝,複製,相當於細胞分裂。
首先,在沒有原型模式之前,我們複製對象是這樣複製的(要一個,new一個):
//這是老王類
public class Laowang {
private String name;
private String city;
private School school;
public Laowang(String name, String city, School school) {
this.name = name;
this.city = city;
this.school = school;
}
}
//Main方法
public static void main(String[] args) {
Laowang laowang=new Laowang("老王", "廣東廣州", new School("廣州某大學"));
Laowang laowang1=new Laowang("老王", "廣東廣州", new School("廣州某大學"));
Laowang laowang2=new Laowang("老王", "廣東廣州", new School("廣州某大學"));
}
這樣複製對象,有什麼缺陷呢?比如每次我們都要整齊輸入構造對象的參數“老王”,“廣東廣州”,“廣州某大學”。這樣顯然過於笨重,因此原型模式出現了。接下來聊聊怎麼實現。
二、實現原型模式
接下來介紹原型模式的2種實現方式(部分構造方法,set方法沒放出來)
1、淺克隆
淺克隆實現方式很簡單
第一步:讓Laowang類 實現Cloneable接口
第二步:重寫父類clone方法(任何一個對象都是繼承於Object,因此都有clone方法)。
代碼如下:
//這是Laowang類
public class Laowang implements Cloneable{
private String name;
private String city;
private School school;
//重寫父類clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//這是Main方法
public static void main(String[] args) throws CloneNotSupportedException {
//原型模式淺克隆
Laowang laowang3= (Laowang) laowang.clone();
System.out.println("-------------------淺克隆------------------------");
System.out.println("本體的信息"+laowang.toString());
System.out.println("淺克隆的信息"+laowang3.toString());
}
運行結果如下:
可以看到,克隆是克隆了,但是屬性 School並沒有完全克隆,因爲始終指向和本體同一個School對象。
在有屬性是對象引用的情況下,這是淺克隆的劣勢。
2、深克隆
深克隆的意思就會把原來的對象引用也同時克隆。
深克隆有2種實現方式:
- 修改重寫的clone方法方式。
- 利用對象流方式深克隆
我們先來看第一種:改重寫的clone方法方式
直接上代碼
讓School類也實現Cloneable接口,重寫父類clone方法
//這是School類
public class School implements Cloneable{
private String schoolName;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Laowang類重寫的clone方法進行修改:
//這是Laowang類
public class Laowang implements Cloneable{
private String name;
private String city;
private School school;
//重寫的clone方法進行修改
@Override
protected Object clone() throws CloneNotSupportedException {
School s1 = (School) school.clone();//讓school自己先複製一個對象
Laowang l1 = (Laowang) super.clone();//讓自己也複製一個對象
l1.setSchool(s1);//把剛纔School複製後的對象賦值給l1
return l1;//返回l1
}
}
//這是Main方法
public static void main(String[] args) throws CloneNotSupportedException {
//原型模式深克隆1
Laowang laowang3= (Laowang) laowang.clone();
System.out.println("-------------------深克隆1------------------------");
System.out.println("本體的信息"+laowang.toString());
System.out.println("深克隆的信息"+laowang3.toString());
}
運行結果如下:
很明顯,對象引用地址不同,已經發生了深度克隆。
這種做法其實不太被推薦,爲什麼?因爲如果屬性有一個對象引用,至少就
要修改對象引用類繼承Cloneable接口。
因此更推薦下面做法:通過對象流進行深度克隆
代碼如下:
讓所有類都實現Serializabel接口(支持序列化和反序列化)
public class School implements Serializable {
private String schoolName;
public School(String schoolName) {
this.schoolName = schoolName;
}
}
寫一個深度克隆deepClone方法如下
//這是Laowang類
public class Laowang implements Cloneable{
private String name;
private String city;
private School school;
//深度克隆
public Laowang deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bao=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bao);
oos.writeObject(this);//將當前對象寫入對象流
ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());//從對象流中取出
ObjectInputStream ois=new ObjectInputStream(bis);
return (Laowang) ois.readObject();
}
}
//這是Main方法
public static void main(String[] args) throws IOException, ClassNotFoundException {
//模式克隆2
Laowang laowang3= (Laowang) laowang.deepClone();
System.out.println("-------------------深克隆2------------------------");
System.out.println("本體的信息"+laowang.toString());
System.out.println("深克隆的信息"+laowang3.toString());
}
運行結果如下圖:
可以看到,也是有進行了對象引用的深度克隆。
三、結束語
好了,原型模式也就這樣,實現起來相對簡單。而且很好理解,就是一個對象的複製。只不過複製的方法有所不同。
原型模式在java底層也有用到,例如map,set之類的都有實現該方式進行克隆。