定義
原型模式屬于對(duì)象的創(chuàng)建模式腔呜。通過(guò)給出一個(gè)原型對(duì)象來(lái)指明所有創(chuàng)建的對(duì)象的類(lèi)型,然后用復(fù)制這個(gè)原型對(duì)象的辦法創(chuàng)建出更多同類(lèi)型的對(duì)象。這就是原型模式的用意
原型模式的結(jié)構(gòu)
原型模式要求對(duì)象實(shí)現(xiàn)同一個(gè)可以“克隆”自身的接口榄鉴,遮掩個(gè)就可以通過(guò)賦值一個(gè)實(shí)例對(duì)象本身來(lái)創(chuàng)建一個(gè)新的實(shí)例苍碟。
這樣一來(lái)赞季,通過(guò)原型實(shí)例創(chuàng)建新的對(duì)象沈跨,就不再需要關(guān)心這個(gè)實(shí)例本身的類(lèi)型篮奄,只要實(shí)現(xiàn)了克隆自身的方法,就可以通過(guò)這個(gè)方法獲取新的對(duì)象穆趴,而無(wú)需再去通過(guò)new
來(lái)創(chuàng)建脸爱。
原型對(duì)象有兩種表現(xiàn)形式:
- 簡(jiǎn)單形式
- 登記形式
這兩種形式僅僅是原型模式的不同實(shí)現(xiàn)。
簡(jiǎn)單形式的原型模式
原型模式涉及三個(gè)角色:
- 客戶(Client)角色:客戶類(lèi)提出創(chuàng)建對(duì)象的請(qǐng)求未妹。
- 抽象原型(Prototype)角色:這是一個(gè)抽象角色阅羹,通常由一個(gè)Java接口或者Java抽象類(lèi)實(shí)現(xiàn)勺疼。此角色給出所有的具體原型類(lèi)所需的接口教寂。
- 具體原型(Concrete Prototype)角色:被復(fù)制的對(duì)象捏鱼。此角色需要實(shí)現(xiàn)抽象原型角色要求的接口。
示例代碼
抽象原型角色
/**
* 抽象原型角色
*/
public abstract class Prototype {
private String id;
public Prototype(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
/**
* 克隆自身的方法
* @return 一個(gè)從自身克隆出來(lái)的對(duì)象酪耕。
*/
public abstract Prototype clone();
}
具體原型角色
public class ConcreteProtype1 extends Prototype {
public ConcreteProtype1(String id) {
super(id);
}
public Prototype clone() {
Prototype prototype = new ConcreteProtype1(this.getId());
return prototype;
}
}
public class ConcreteProtype2 extends Prototype {
public ConcreteProtype2(String id) {
super(id);
}
public Prototype clone() {
Prototype prototype = new ConcreteProtype2(this.getId());
return prototype;
}
}
客戶端
public class Client {
public static void main(String[] args) {
ConcreteProtype1 protype1 = new ConcreteProtype1("Protype1");
ConcreteProtype1 protypeCopy1 = (ConcreteProtype1)protype1.clone();
System.out.println(protypeCopy1.getId());
System.err.println(protype1.toString());
System.err.println(protypeCopy1.toString());
ConcreteProtype2 protype2 = new ConcreteProtype2("Protype2");
ConcreteProtype2 protypeCopy2 = (ConcreteProtype2)protype2.clone();
System.out.println(protypeCopy2.getId());
System.err.println(protype2.toString());
System.err.println(protypeCopy2.toString());
}
}
輸出結(jié)果:
Protype1
com.sschen.prototype.ConcreteProtype1@2a139a55
com.sschen.prototype.ConcreteProtype1@15db9742
Protype2
com.sschen.prototype.ConcreteProtype2@6d06d69c
com.sschen.prototype.ConcreteProtype2@7852e922
還有另外一種對(duì)象的復(fù)制方式导梆,如下:
ConcreteProtype1 protype3 = new ConcreteProtype1("Protype3");
ConcreteProtype1 protypeCopy3 = protype3;
System.out.println(protypeCopy3.getId());
System.err.println(protype3.toString());
System.err.println(protypeCopy3.toString());
這種方式的輸出結(jié)果為:
Protype3
com.sschen.prototype.ConcreteProtype1@2a139a55
com.sschen.prototype.ConcreteProtype1@2a139a55
這種方式同上面的原型模式的克隆模式比較,可以看見(jiàn)的是:原型模式生成的兩個(gè)對(duì)象迂烁,內(nèi)存地址是不一樣的看尼。
之前在java面試 內(nèi)存中堆和棧的區(qū)別文章中說(shuō)明過(guò):對(duì)象的值存放在堆中,在棧中存儲(chǔ)指向堆中內(nèi)存位置的引用盟步。
第一種方式中藏斩,我們是重新創(chuàng)建了新的對(duì)象,對(duì)象的值和引用都是新的却盘,對(duì)于克隆對(duì)象的任何變更狰域,都不會(huì)影響到原對(duì)象的值。如下圖:
另一種方式黄橘,我們只是在棧中新創(chuàng)建了一個(gè)引用兆览,指向的還是被復(fù)制對(duì)象對(duì)應(yīng)的堆地址,對(duì)于復(fù)制對(duì)象的變更塞关,都會(huì)同時(shí)改變?cè)瓕?duì)象的值抬探。如下圖:
登記形式的原型模型
作為原型模式的第二種形式,它多了一個(gè)原型管理器(PrototypeManager)角色帆赢。該角色的作用是:創(chuàng)建具體有原型類(lèi)的對(duì)象小压,并記錄每一個(gè)被創(chuàng)建的對(duì)象。
示例代碼
抽象原型角色
public interface Prototype {
public Prototype clone();
public String getName();
public void setName(String name);
}
具體原型角色
public class ConcretePrototype1 implements Prototype {
private String name;
@Override
public Prototype clone() {
ConcretePrototype1 prototype1 = new ConcretePrototype1();
prototype1.setName(this.name);
return prototype1;
}
@Override
public String getName() {
return this.name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "ConcretePrototype1 [name=" + name + "]";
}
}
public class ConcretePrototype2 implements Prototype {
private String name;
@Override
public Prototype clone() {
ConcretePrototype2 prototype2 = new ConcretePrototype2();
prototype2.setName(this.name);
return prototype2;
}
@Override
public String getName() {
return this.name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "ConcretePrototype2 [name=" + name + "]";
}
}
原型管理器角色保持一個(gè)聚集椰于,作為對(duì)所有原型對(duì)象的登記怠益,這個(gè)角色提供必要的方法,供外界增加新的原型對(duì)象和取得已經(jīng)登記過(guò)的原型對(duì)象廉羔。
public class PrototypeManager {
/**
* 用來(lái)記錄原型的編號(hào)同原型實(shí)例的對(duì)象關(guān)系
*/
private static Map<String, Prototype> map = new HashMap<String, Prototype>();
/**
* 私有化構(gòu)造方法溉痢,避免從外部創(chuàng)建實(shí)例
*/
private PrototypeManager() {}
/**
* 向原型管理器里面添加或者修改原型實(shí)例
* @param prototypeId 原型編號(hào)
* @param prototype 原型實(shí)例
*/
public synchronized static void setPrototype(String prototypeId, Prototype prototype) {
map.put(prototypeId, prototype);
}
/**
* 根據(jù)原型編號(hào)從原型管理器里面移除原型實(shí)例
* @param prototypeId 原型編號(hào)
*/
public synchronized static void removePrototype(String prototypeId) {
map.remove(prototypeId);
}
/**
* 根據(jù)原型編號(hào)獲取原型實(shí)例
* @param prototypeId 原型編號(hào)
* @return 原型實(shí)例對(duì)象
* @throws Exception 如果根據(jù)原型編號(hào)無(wú)法獲取對(duì)應(yīng)實(shí)例,則提示異潮锼“您希望獲取的原型還沒(méi)有注冊(cè)或已被銷(xiāo)毀”
*/
public synchronized static Prototype getPrototype(String prototypeId) throws Exception {
Prototype prototype = map.get(prototypeId);
if (prototype == null) {
throw new Exception("您希望獲取的原型還沒(méi)有注冊(cè)或已被銷(xiāo)毀");
}
return prototype;
}
}
客戶端角色
public class Client {
public static void main(String[] args) {
try {
Prototype p1 = new ConcretePrototype1();
PrototypeManager.setPrototype("p1", p1);
//獲取原型來(lái)創(chuàng)建對(duì)象
Prototype p3 = PrototypeManager.getPrototype("p1").clone();
p3.setName("張三");
System.out.println("第一個(gè)實(shí)例:" + p3);
//有人動(dòng)態(tài)的切換了實(shí)現(xiàn)
Prototype p2 = new ConcretePrototype2();
PrototypeManager.setPrototype("p1", p2);
//重新獲取原型來(lái)創(chuàng)建對(duì)象
Prototype p4 = PrototypeManager.getPrototype("p1").clone();
p4.setName("李四");
System.out.println("第二個(gè)實(shí)例:" + p4);
//有人注銷(xiāo)了這個(gè)原型
PrototypeManager.removePrototype("p1");
//再次獲取原型來(lái)創(chuàng)建對(duì)象
Prototype p5 = PrototypeManager.getPrototype("p1").clone();
p5.setName("王五");
System.out.println("第三個(gè)實(shí)例:" + p5);
} catch (Exception e) {
e.printStackTrace();
}
}
}
輸出結(jié)果為:
第一個(gè)實(shí)例:ConcretePrototype1 [name=張三]
第二個(gè)實(shí)例:ConcretePrototype2 [name=李四]
java.lang.Exception: 您希望獲取的原型還沒(méi)有注冊(cè)或已被銷(xiāo)毀
at com.sschen.prototyperegist.PrototypeManager.getPrototype(PrototypeManager.java:44)
at com.sschen.prototyperegist.Client.main(Client.java:26)
兩種形式的比較
簡(jiǎn)單形式和登記形式的原型模式各有其長(zhǎng)處和短處孩饼。
- 如果需要?jiǎng)?chuàng)建的原型對(duì)象數(shù)據(jù)較少而且比較固定的話,可以采用第一種形式竹挡。在這種情況下镀娶,原型對(duì)象的引用可以由客戶端自己保存。
- 如果要?jiǎng)?chuàng)建的原型對(duì)象數(shù)據(jù)不固定的話揪罕,可以采用第二種形式梯码。在這種情況下宝泵,客戶端不保存對(duì)原型對(duì)象的引用,這個(gè)任務(wù)被交給原型管理器角色轩娶。在克隆一個(gè)對(duì)象之前儿奶,客戶端可以查看管理員對(duì)象是否已經(jīng)有一個(gè)滿足要求的原型對(duì)象。如果有鳄抒,可以從原型管理器角色中取得這個(gè)對(duì)象引用闯捎;如果沒(méi)有,客戶端就需要自行復(fù)制此原型對(duì)象许溅。
Java中的克隆方法
Java中的所有類(lèi)都是從java.lang.Object
類(lèi)繼承而來(lái)的瓤鼻,而Object
類(lèi)提供protected Object clone()
方法對(duì)對(duì)象進(jìn)行克隆復(fù)制,子類(lèi)當(dāng)然也可以把這個(gè)方法置換掉贤重,提供滿足自己需要的復(fù)制方法茬祷。對(duì)象的復(fù)制有一個(gè)基本問(wèn)題,就是對(duì)象通常都有對(duì)其他對(duì)象的引用并蝗。當(dāng)使用Object
類(lèi)的clone()
方法來(lái)復(fù)制一個(gè)對(duì)象時(shí)祭犯,此對(duì)象對(duì)其他對(duì)象的引用也同時(shí)會(huì)被復(fù)制一份。
java語(yǔ)言提供的Cloneable
接口只起一個(gè)作用借卧,就是在運(yùn)行時(shí)期通知Java虛擬機(jī)可以安全的在這個(gè)類(lèi)上使用clone()
方法盹憎。通過(guò)調(diào)用這個(gè)clone()
方法可以得到一個(gè)對(duì)象的復(fù)制。由于Object
類(lèi)本身不實(shí)現(xiàn)Cloneable
接口铐刘,因此如果所考慮的類(lèi)沒(méi)有實(shí)現(xiàn)Cloneable
接口時(shí)陪每,調(diào)用clone()
方法會(huì)拋出CloneNotSupportedException
異常。
克隆滿足的條件
clone()
方法將對(duì)象復(fù)制了一份并返還給了調(diào)用者镰吵。所謂“復(fù)制”的含義于clone()
方法是怎么實(shí)現(xiàn)的含義是一樣的檩禾。一般而言,clone()
方法滿足以下的描述:
- 對(duì)任何的對(duì)象
x
疤祭,都有x.clone() != x
盼产。換而言之,克隆對(duì)象和原對(duì)象不是同一個(gè)對(duì)象勺馆。 - 對(duì)任何的對(duì)象
x
戏售,都有x.clone().getClass() == x.getClass()
。換而言之草穆,克隆對(duì)象同原對(duì)象的類(lèi)型一致灌灾。 - 如果對(duì)象
x
的equals()
方法定義其恰當(dāng)?shù)脑挘敲?code>x.clone().equals(x)應(yīng)當(dāng)是成立的悲柱。
在Java語(yǔ)言的API中锋喜,凡是提供了clone()
方法的類(lèi),都滿足上面的這些條件。Java語(yǔ)言的設(shè)計(jì)師再設(shè)計(jì)自己的clone()
方法時(shí)嘿般,也應(yīng)當(dāng)遵守這三個(gè)條件段标。一般來(lái)說(shuō),上面的三個(gè)條件中的前兩個(gè)是必需的炉奴,而第三個(gè)是可選的逼庞。
淺克隆和深克隆
無(wú)論你是自己實(shí)現(xiàn)克隆方法,還是采用Java提供的克隆方法盆佣,都存在一個(gè)淺度克隆和深度克隆的問(wèn)題往堡。
-
淺度克隆:只負(fù)責(zé)克隆按值傳遞的數(shù)據(jù)(比如基本數(shù)據(jù)類(lèi)型,
String
類(lèi)型)共耍,而不是復(fù)制它所引用的對(duì)象。換而言之吨瞎,所有對(duì)其他對(duì)象的引用都仍然指向原來(lái)的對(duì)象痹兜。 -
深度克隆:除了淺度克隆需要克隆的值外,還負(fù)責(zé)克隆引用類(lèi)型的數(shù)據(jù)颤诀。那些引用其他對(duì)象的變量將指向被復(fù)制過(guò)的新對(duì)象字旭,而不再是原有的那些被引用的對(duì)象。換而言之崖叫,深度克隆要把復(fù)制的對(duì)象所引用的對(duì)象都復(fù)制一遍遗淳,而這種對(duì)被引用到的對(duì)象的復(fù)制叫做簡(jiǎn)間接復(fù)制。
深度克隆要深入到多少層心傀,是一個(gè)不易確定的問(wèn)題屈暗。在決定以深度克隆的方式復(fù)制一個(gè)對(duì)象的時(shí)候,必須決定對(duì)間接復(fù)制的對(duì)象是采取淺度克隆還是繼續(xù)采用深度克隆脂男。因此养叛,在采用深度克隆時(shí),需要決定多深才算深宰翅。
此外弃甥,在深度克隆的過(guò)程中,很可能會(huì)出現(xiàn)循環(huán)引用的問(wèn)題汁讼,必須小心處理淆攻。
使用序列化實(shí)現(xiàn)深度克隆
把對(duì)象寫(xiě)到流里的過(guò)程是序列化Serialization
的過(guò)程;而把對(duì)象從流中讀出來(lái)的過(guò)程叫反序列化Deserialization
過(guò)程嘿架。應(yīng)當(dāng)指出的是瓶珊,寫(xiě)到流里的是對(duì)象的一個(gè)拷貝,原對(duì)象仍然存在于JVM中眶明。
在Java語(yǔ)言里深度克隆一個(gè)對(duì)象艰毒,常常可以先使對(duì)象實(shí)現(xiàn)Serializable
接口搜囱,然后把對(duì)象(實(shí)際上對(duì)象的拷貝)寫(xiě)到一個(gè)流里(序列化過(guò)程)丑瞧,再?gòu)牧骼镒x出來(lái)(反序列化過(guò)程)柑土,便可以重建對(duì)象。
public Object deepClone() throws IOException, ClassNotFoundException{
//將對(duì)象寫(xiě)到流里
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//從流里讀回來(lái)
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
這樣做的前提就是對(duì)象及對(duì)象內(nèi)部所有引用到的對(duì)象都是可序列化的绊汹,否則稽屏,就需要仔細(xì)考察那些不可序列化的對(duì)象是否可以設(shè)置成transient
,從而將至排除在復(fù)制過(guò)程之外西乖。
淺度克隆顯然比深度克隆更容易實(shí)現(xiàn)狐榔,因?yàn)镴ava語(yǔ)言的所有類(lèi)都會(huì)繼承一個(gè)clone()
方法,而這個(gè)clone()
方法所做的正是淺度克隆获雕。
有一些對(duì)象薄腻,比如線程Thread
對(duì)象或者Socket
對(duì)象,是不能簡(jiǎn)單復(fù)制或者共享的届案。不管是使用淺度克隆還是使用深度克隆庵楷,只要涉及這樣的間接對(duì)象,就必須把簡(jiǎn)介對(duì)象射程transient
而不予復(fù)制楣颠;或者由程序自行創(chuàng)建出相當(dāng)?shù)耐葘?duì)象尽纽,權(quán)且當(dāng)做復(fù)制件使用。
孫大圣的身外身法術(shù)
孫大圣的身外身本領(lǐng)如果在Java語(yǔ)言里使用原型模式來(lái)實(shí)現(xiàn)的話童漩,會(huì)怎么樣呢弄贿?
首先,齊天大圣The Greatest Sage
矫膨,即TheGreatestSage類(lèi)扮演客戶角色差凹。齊天大圣持有一個(gè)猢猻Monkey
的實(shí)例,而猢猻就是大圣本尊豆拨。Monkey
類(lèi)具有繼承自java.lang.Object
的clone()
方法直奋,因此,可以通過(guò)調(diào)用這個(gè)克隆方法來(lái)復(fù)制一個(gè)Monkey
實(shí)例施禾。
示例代碼
大圣持有金箍棒的實(shí)例脚线,因此這里定義了一個(gè)金箍棒的類(lèi)GoldRingedStaff
/**
* 金箍棒對(duì)象
*/
public class GoldRingedStaff {
/**
* 高度
*/
private float height = 100.00f;
/**
* 半徑
*/
private float radius = 10.00f;
/**
* 金箍棒變大方法
*/
public void grow() {
this.radius *= 2;
this.height *= 2;
}
/**
* 金箍棒縮小方法
*/
public void shrink() {
this.radius /= 2;
this.height /= 2;
}
}
大圣本尊使用Monkey
類(lèi)來(lái)表示,這個(gè)類(lèi)來(lái)扮演具體的原型角色:
/**
* 獼猴類(lèi)弥搞,大圣本尊由獼猴類(lèi)來(lái)表示
*/
public class Monkey implements Cloneable {
/**
* 身高
*/
private int height;
/**
* 體重
*/
private int weight;
/**
* 出生日期
*/
private Date birthDay;
/**
* 金箍棒
*/
private GoldRingedStaff staff;
/**
* 構(gòu)造函數(shù)邮绿,指定創(chuàng)建事件和給定金箍棒
*/
public Monkey() {
this.birthDay = new Date();
this.staff = new GoldRingedStaff();
}
/**
* 克隆方法,直接調(diào)用接口的克隆方法
*/
public Object clone() {
Monkey temp = null;
try {
temp = (Monkey)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
} finally {
return temp;
}
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public Date getBirthDay() {
return birthDay;
}
public void setBirthDay(Date birthDay) {
this.birthDay = birthDay;
}
public GoldRingedStaff getStaff() {
return staff;
}
public void setStaff(GoldRingedStaff staff) {
this.staff = staff;
}
}
孫大圣本尊則使用類(lèi)TheGreatestSage
來(lái)表示:
public class TheGreatestSage {
private Monkey monkey = new Monkey();
public void change() {
Monkey copyMonkey = (Monkey) monkey.clone();
System.out.println("大圣本尊的生日是:" + monkey.getBirthDay());
System.out.println("克隆大圣的生日是:" + copyMonkey.getBirthDay());
System.out.println("大圣本尊同克隆大圣是否為同一個(gè)對(duì)象:" + (monkey == copyMonkey));
System.out.println("大圣本尊持有的金箍棒 同 克隆大圣持有的金箍棒是否為同一個(gè)對(duì)象:" + (monkey.getStaff() == copyMonkey.getStaff()));
}
public static void main(String[] args) {
TheGreatestSage sage = new TheGreatestSage();
sage.change();
}
}
當(dāng)運(yùn)行TheGreatestSage
類(lèi)時(shí)攀例,首先創(chuàng)建大圣本尊對(duì)象船逮,然后淺度克隆大圣本尊對(duì)象。程序在運(yùn)行時(shí)輸出的信息如下:
大圣本尊的生日是:Wed Jun 28 10:19:53 CST 2017
克隆大圣的生日是:Wed Jun 28 10:19:53 CST 2017
大圣本尊同克隆大圣是否為同一個(gè)對(duì)象:false
大圣本尊持有的金箍棒 同 克隆大圣持有的金箍棒是否為同一個(gè)對(duì)象:true
可以看出粤铭,首先挖胃,復(fù)制的大圣本尊具有和原始的大圣本尊一樣的birthDay
,而本尊對(duì)象不相等,這表明他們二者是克隆關(guān)系酱鸭;其次吗垮,復(fù)制的大圣本尊持有的金箍棒和原始大圣持有的金箍棒為同一個(gè)對(duì)象,這表明二者所持有的金箍棒為同一根凹髓,而非兩根烁登。
正如前面所述,繼承自java.lang.Object
類(lèi)的clone()
方法是淺度克隆蔚舀。換而言之饵沧,齊天大圣所有化身所持有的金箍棒引用全都是指向一個(gè)對(duì)象的,這與《西游記》中描寫(xiě)并不一致赌躺。要想要糾正這一點(diǎn)狼牺,就需要考慮使用深度克隆
想要做到深度克隆,就要求所有需要復(fù)制的對(duì)象都實(shí)現(xiàn)java.io.Serializable
接口寿谴。
實(shí)例代碼锁右,修改為深度克隆后
孫大圣的源代碼
public class TheGreatestSage {
private Monkey monkey = new Monkey();
public void change() {
Monkey copyMonkey = null;
try {
copyMonkey = (Monkey) monkey.deepClone();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("大圣本尊的生日是:" + monkey.getBirthDay());
System.out.println("克隆大圣的生日是:" + copyMonkey.getBirthDay());
System.out.println("大圣本尊同克隆大圣是否為同一個(gè)對(duì)象:" + (monkey == copyMonkey));
System.out.println("大圣本尊持有的金箍棒 同 克隆大圣持有的金箍棒是否為同一個(gè)對(duì)象:" + (monkey.getStaff() == copyMonkey.getStaff()));
}
public static void main(String[] args) {
TheGreatestSage sage = new TheGreatestSage();
sage.change();
}
}
在大圣本尊Monkey
類(lèi)中,原有一個(gè)克隆方法clone()
讶泰,為淺度克隆,因此新增一個(gè)方法deepClone()
拂到,為深度克隆痪署。在deepClone()
方法中,大圣本尊對(duì)象(一個(gè)拷貝)被序列化兄旬,然后又被反序列化狼犯。反序列化后的對(duì)象也就成了一個(gè)深度克隆后的對(duì)象。deepClone()
方法如下:
/**
* 獼猴類(lèi)领铐,大圣本尊由獼猴類(lèi)來(lái)表示
*/
public class Monkey implements Cloneable,Serializable {
//………………
/**
* 克隆方法悯森,直接調(diào)用接口的克隆方法
*/
public Object clone() {
Monkey temp = null;
try {
temp = (Monkey)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
} finally {
return temp;
}
}
public Object deepClone() throws IOException, ClassNotFoundException {
//將對(duì)象寫(xiě)入流中
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
//將對(duì)象從流中讀取回來(lái)
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return objectInputStream.readObject();
}
//……………………
}
對(duì)于金箍棒GoldRingedStaff
類(lèi),也讓其實(shí)現(xiàn)java.io.Serializable
接口:
public class GoldRingedStaff implements Serializable {
//………………
}
修改后的代碼運(yùn)行結(jié)果為:
大圣本尊的生日是:Wed Jun 28 11:31:01 CST 2017
克隆大圣的生日是:Wed Jun 28 11:31:01 CST 2017
大圣本尊同克隆大圣是否為同一個(gè)對(duì)象:false
大圣本尊持有的金箍棒 同 克隆大圣持有的金箍棒是否為同一個(gè)對(duì)象:false
從運(yùn)行結(jié)果可以看出绪撵,大圣的金箍棒同克隆大圣的金箍棒是不同的對(duì)象瓢姻。這是因?yàn)槭褂昧?strong>深度克隆,從而將大圣本尊所引用的對(duì)象也都復(fù)制了一遍音诈,其中也包括金箍棒幻碱。
原型模式優(yōu)缺點(diǎn)總結(jié)
原型模式的優(yōu)點(diǎn)
原型模式允許在運(yùn)行時(shí)動(dòng)態(tài)的改變具體的實(shí)現(xiàn)類(lèi)型。原型模式可以在運(yùn)行期間细溅,有客戶來(lái)注冊(cè)符合原型接口的實(shí)現(xiàn)類(lèi)型褥傍,也可以動(dòng)態(tài)的改變具體的實(shí)現(xiàn)類(lèi)型,看起來(lái)接口沒(méi)有任何變化喇聊,但是其實(shí)運(yùn)行的已經(jīng)是另外一個(gè)類(lèi)實(shí)體了恍风。因?yàn)榭寺∫粋€(gè)原型對(duì)象就類(lèi)似于實(shí)例化一個(gè)類(lèi)。
原型模式的缺點(diǎn)
原型模式最主要的缺點(diǎn)是每一個(gè)類(lèi)都必須要配備一個(gè)克隆方法。配備克隆方法需要對(duì)類(lèi)的功能進(jìn)行通盤(pán)考慮朋贬,這對(duì)于全新的類(lèi)來(lái)說(shuō)并不是很難凯楔,但是對(duì)于已有的類(lèi)來(lái)說(shuō)并不容易,可別是當(dāng)一個(gè)類(lèi)引用不支持序列化的間接對(duì)象兄世,或者引用含有循環(huán)結(jié)構(gòu)的時(shí)候啼辣。