http://c.biancheng.net/view/1343.html
https://www.cnblogs.com/shakinghead/p/7651502.html
原型(Prototype)模式的定義
用一個已經(jīng)創(chuàng)建的實例作為原型圣拄,通過復(fù)制該原型對象來創(chuàng)建一個和原型相同或相似的新對象轧拄。
原型模式的優(yōu)點:
- java自帶的原型模式基于內(nèi)存二進制流的復(fù)制,在性能上比直接 new 一個對象更加優(yōu)良洲胖。
- 可以使用深克隆方式保存對象的狀態(tài),使用原型模式將對象復(fù)制一份,并將其狀態(tài)保存起來,簡化了創(chuàng)建對象的過程咨演,以便在需要的時候使用(例如恢復(fù)到歷史某一狀態(tài)),可輔助實現(xiàn)撤銷操作蚯斯。
原型模式的缺點:
- 需要為每一個類都配置一個 clone 方法
- clone 方法位于類的內(nèi)部薄风,當(dāng)對已有類進行改造的時候,需要修改代碼拍嵌,違背了開閉原則遭赂。
- 當(dāng)實現(xiàn)深克隆時,需要編寫較為復(fù)雜的代碼撰茎,而且當(dāng)對象之間存在多重嵌套引用時嵌牺,為了實現(xiàn)深克隆,每一層對象對應(yīng)的類都必須支持深克隆,實現(xiàn)起來會比較麻煩逆粹。因此募疮,深克隆、淺克隆需要運用得當(dāng)僻弹。
原型模式的結(jié)構(gòu)與實現(xiàn)
由于 Java 提供了對象的 clone() 方法阿浓,所以用 Java 實現(xiàn)原型模式很簡單。
1. 模式的結(jié)構(gòu)
原型模式包含以下主要角色蹋绽。
- 抽象原型類:規(guī)定了具體原型對象必須實現(xiàn)的接口芭毙。
- 具體原型類:實現(xiàn)抽象原型類的 clone() 方法,它是可被復(fù)制的對象卸耘。
- 訪問類:使用具體原型類中的 clone() 方法來復(fù)制新的對象退敦。
2.實現(xiàn)
/**
* 原型模式Prototype
* 通過復(fù)制已有實例快速創(chuàng)建一個新實例
* 原型模式有淺克隆、深克隆的區(qū)別:淺克隆的結(jié)果
*/
public class Realizetype implements Cloneable {
private String name;
private Age age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Age getAge() {
return age;
}
public void setAge(Age age) {
this.age = age;
}
public Realizetype(){
System.out.println("帥氣的我出現(xiàn)了");
}
public Realizetype clone() throws CloneNotSupportedException {
System.out.println("又一個帥氣的我出現(xiàn)了");
return (Realizetype) super.clone();
}
public static void main(String[] args) {
try {
Age age = new Age();
age.setAge(2);
Realizetype realizetype = new Realizetype();
realizetype.setName("張三");
realizetype.setAge(age);
System.out.println("我是,name=" + realizetype.getName());
System.out.println("年齡,age=" + realizetype.getAge().getAge());
System.out.println("##########");
Realizetype realizetype1 = realizetype.clone();
System.out.println("張三的復(fù)制體,name=" + realizetype1.getName());
System.out.println("張三的復(fù)制體,age=" + realizetype1.getAge().getAge());
System.out.println("##########");
realizetype.setName("李四");
age.setAge(9);
realizetype.setAge(age);
System.out.println("張三改名為李四蚣抗,年齡變?yōu)?");
System.out.println("張三的新,name=" + realizetype.getName());
System.out.println("張三的新,age=" + realizetype.getAge().getAge());
System.out.println("##########");
System.out.println("張三的復(fù)制體,name=" + realizetype1.getName());
System.out.println("張三的復(fù)制體,age=" + realizetype1.getAge().getAge());
System.out.println("##########");
System.out.println(realizetype == realizetype1);
System.out.println(realizetype.age == realizetype1.age);
} catch (Exception e){
System.out.println("無礙");
}
}
}
運行結(jié)果
帥氣的我出現(xiàn)了
我是,name=張三
年齡,age=2
##########
又一個帥氣的我出現(xiàn)了
張三的復(fù)制體,name=張三
張三的復(fù)制體,age=2
##########
張三改名為李四侈百,年齡變?yōu)?
張三的新,name=李四
張三的新,age=9
##########
張三的復(fù)制體,name=張三
張三的復(fù)制體,age=9
##########
false
true
可以發(fā)現(xiàn)當(dāng)張三的姓名年齡變更后,張三的復(fù)制體姓名不變翰铡、年齡變了钝域,其原因是clone()
方法為淺拷貝,拷貝的引用數(shù)據(jù)指向同一內(nèi)存地址所以System.out.println(realizetype.age == realizetype1.age);
為true
锭魔。
String是一個例外例证,雖然String是引用數(shù)據(jù),但是String類型的數(shù)據(jù)是存放在常量池中的迷捧。
深拷貝
實現(xiàn)方式1-每個類重寫clone方法
- 修改Realizetype的clone方法
public Realizetype clone() throws CloneNotSupportedException {
System.out.println("又一個帥氣的我出現(xiàn)了");
Realizetype r = (Realizetype) super.clone();
//克隆Age實例
r.setAge(getAge().clone());
}
- 修改Age類织咧,使他繼承Cloneable,重寫clone方法
public Age clone(){
Age a = null;
try {
a = (Age) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return a;
}
修改后執(zhí)行結(jié)果
帥氣的我出現(xiàn)了
我是,name=張三
年齡,age=2
##########
又一個帥氣的我出現(xiàn)了
張三的復(fù)制體,name=張三
張三的復(fù)制體,age=2
##########
張三改名為李四党涕,年齡變?yōu)?
張三的新,name=李四
張三的新,age=9
##########
張三的復(fù)制體,name=張三
張三的復(fù)制體,age=2
##########
false
false
可以看到深克隆的實例不受本體影響烦感。
這種深克隆的實現(xiàn)方式較為復(fù)雜巡社,特別對于屬性數(shù)量比較多膛堤、層次比較深的類而言,每個類都要重寫clone方法太過繁瑣