一、簡介
原型模式是一種創(chuàng)建型設(shè)計模式,允許使用已有的實例對象作為原型,創(chuàng)建新的對象,無需知道任何如何創(chuàng)建的細(xì)節(jié),也就是“克隆指定對象”。
實現(xiàn)原型模式的思想是讓需要拷貝的原型類必須實現(xiàn)"java.lang.Cloneable"接口,然后重寫Object類中的clone方法。
Cloneable是一個“標(biāo)記接口”,所謂的標(biāo)記接口就是該接口中沒有任何內(nèi)容,但只有實現(xiàn)了Cloneable接口的類才有資格調(diào)用Object類中的clone方法(該方法提供了淺拷貝的功能),否則會拋出“CloneNotSupportedException”異常。
原型模式主要有兩類角色:
1.抽象原型角色((Prototype):
實現(xiàn)Cloneable接口重寫clone方法(可定義為抽象,也可具體實現(xiàn),視情況而定),來說明它有被克隆功能。
2.具體原型(Concrete Prototype)角色
繼承抽象原型角色,則具體原型角色自動具有拷貝功能,這樣節(jié)省代碼量,當(dāng)然也可以讓具體原型角色直接實現(xiàn)實現(xiàn)Cloneable接口。
原型模式的類圖如下:

二、使用場景
當(dāng)對象的創(chuàng)建非常復(fù)雜,可以使用原型模式快捷的創(chuàng)建對象?;蛘咴谶\(yùn)行過程中不知道對象的具體類型,可使用原型模式創(chuàng)建一個相同類型的對象。
使用原型模式的優(yōu)點在于可以基于原型,快速的創(chuàng)建一個對象,而無需知道創(chuàng)建的細(xì)節(jié)。另外,由于clone方法是由虛擬機(jī)直接復(fù)制內(nèi)存塊執(zhí)行,不會執(zhí)行構(gòu)造方法,避免了初始化需要的時間
三、舉例
public class 原型模式{
public static void main(String[] args) {
Car1ConcretePrototype car1_1=new Car1ConcretePrototype("car1",100,new ArrayList<String>(){{add("hello1");}});
Car1ConcretePrototype car1_2=(Car1ConcretePrototype)car1_1.clone();
Car2ConcretePrototype car2_1=new Car2ConcretePrototype("car2",200,new ArrayList<String>(){{add("hello2");}});
Car2ConcretePrototype car2_2=(Car2ConcretePrototype)car2_1.clone2();
car1_1.show();car1_2.show();
car2_1.show();car2_2.show();
}
}
class ICarPrototype implements Cloneable,Serializable{
private static final long serialVersionUID = 1L;
private String name;
private int price;
private ArrayList<String>otherInfo;
public ICarPrototype(String name, int price, ArrayList<String> otherInfo) {
this.name = name;
this.price = price;
this.otherInfo = otherInfo;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
public ArrayList<String> getOtherInfo() {
return otherInfo;
}
//深度克隆方式1
@Override
protected Object clone() {
ICarPrototype cloneResult=null;
try {
//可以對基本數(shù)據(jù)類型進(jìn)行復(fù)制,即Object中的clone方法實現(xiàn)的只是淺拷貝
cloneResult=(ICarPrototype)super.clone();
//下面對每一個復(fù)雜成員對象(如數(shù)組、容器、引用對象等)分別進(jìn)行拷貝,以實現(xiàn)對象的深拷貝
cloneResult.otherInfo=(ArrayList<String>)this.otherInfo.clone();
} catch (CloneNotSupportedException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
return cloneResult;
}
//深度克隆方式2,通過序列化對象實現(xiàn)深拷貝,需要實現(xiàn)Serializable接口
protected Object clone2() {
//聲明流對象
ByteArrayOutputStream bos=null;
ByteArrayInputStream bis=null;
ObjectOutputStream oos=null;
ObjectInputStream ois=null;
try{
//創(chuàng)建序列化流
bos=new ByteArrayOutputStream();
oos=new ObjectOutputStream(bos);
//將當(dāng)前對象以對象流的形式輸出
oos.writeObject(this);
//創(chuàng)建反序列化流
bis=new ByteArrayInputStream(bos.toByteArray());
ois=new ObjectInputStream(bis);
//將流對象反序列化,從而實現(xiàn)對象的深度拷貝
return ois.readObject();
}catch(Exception e){
e.printStackTrace();
return null;
}finally{
//釋放資源
try {
bos.close();bis.close();
oos.close();ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class Car1ConcretePrototype extends ICarPrototype{
private static final long serialVersionUID = 2L;
public Car1ConcretePrototype(String name, int price,ArrayList<String> otherInfo) {
super(name, price, otherInfo);
}
public void show(){
System.out.println("**1類型車的信息:**");
System.out.println("車名:"+getName());
System.out.println("車價:"+getPrice());
System.out.println("其他信息:"+getOtherInfo().toString());
}
}
class Car2ConcretePrototype extends ICarPrototype{
private static final long serialVersionUID =3L;
public Car2ConcretePrototype(String name, int price,ArrayList<String> otherInfo) {
super(name, price, otherInfo);
}
public void show(){
System.out.println("##2類型車的信息:##");
System.out.println("車名:"+getName());
System.out.println("車價:"+getPrice());
System.out.println("其他信息:"+getOtherInfo().toString());
}
}
四、擴(kuò)展
- 需要注意的是調(diào)用clone方法克隆java對象的時候不會調(diào)用構(gòu)造器。
這是因為執(zhí)行clone方法的時候是直接從內(nèi)存中去獲取數(shù)據(jù)的,在第一次創(chuàng)建對象的時候就會把數(shù)據(jù)在內(nèi)存保留一份,克隆的時候直接使用就好了 - 訪問權(quán)限對原型模式無效
由于數(shù)據(jù)從內(nèi)存中直接復(fù)制的,所以克隆起來也會直接無視數(shù)據(jù)的訪問權(quán)限,即使對于私有的數(shù)據(jù)也可以獲取復(fù)制過來。
五、參考
原型模式
深度好文:設(shè)計模式之——原型模式
Java Object對象之clone方法
設(shè)計模式之原型模式