Java設(shè)計模式之原型模式詳細(xì)解讀
一、介紹
原型模式屬于創(chuàng)建型設(shè)計模式,用于創(chuàng)建重復(fù)的對象,且同時又保證了性能。
該設(shè)計模式的好處是將對象的創(chuàng)建與調(diào)用方分離。
其目的就是**根據(jù)一個對象(稱為原型)創(chuàng)建一個與其完全相同的對象(當(dāng)然內(nèi)存地址不同)。**原對象被認(rèn)為是新對象的原型。
二、實現(xiàn)步驟
原型模式的實現(xiàn)步驟如下:
- 應(yīng)用原型模式的類要實現(xiàn)Cloneable接口。
- 應(yīng)用原型模式的類要重寫Object類定義的clone()方法。
- 通過調(diào)用對象的clone()方法,獲得一個與該對象相同的對象。
在重寫Object類定義的clone()方法時,直接通過調(diào)用super.clone()即可得到一個新對象,如下所示
@Override
public Object clone() {
try {
Object obj = super.clone();
return obj;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}而super.clone()調(diào)用的其實就是在Object類中定義的方法
public class Object {
// ...
protected native Object clone() throws CloneNotSupportedException;
// ...
}該方法由native關(guān)鍵字修飾,表明其具體實現(xiàn)邏輯在JVM中已經(jīng)完成了,我們無需知道其細(xì)節(jié),只需知道調(diào)用該方法后將返回一個具有相同屬性的對象即可。也正因此,該方式要比我們創(chuàng)建實例再初始化實例的性能好。
另外,該方法顯式地拋出了CloneNotSupportedException異常,要求我們在調(diào)用clone()方法的對象必須實現(xiàn)Cloneable接口,否則將拋出該異常。
注意:調(diào)用super.clone()時,只能夠滿足淺拷貝,如果要實現(xiàn)深拷貝,則需要我們根據(jù)實際情況重寫clone()的具體邏輯而不是調(diào)用super.clone()。
三、案例
我們創(chuàng)建一個Person類,實現(xiàn)Cloneable接口,并重寫clone()方法。
public class Person implements Cloneable{
private String name;
private Integer age;
private String sex;
// 省略get、set方法
@Override
public Person clone() {
try {
Person person = (Person) super.clone();
return person;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}然后進(jìn)行測試
public class PrototypeTest {
public static void main(String[] args) {
Person person = new Person();
person.setName("name");
person.setAge(1);
person.setSex("男");
Person clone = person.clone();
System.out.println("獲得的新對象:" + clone);
}
}得到的輸出如下,可以發(fā)現(xiàn),通過clone()方法可以獲得一個與原對象具有相同屬性的新對象。

四、應(yīng)用
原型模式在實際應(yīng)用中不是很廣泛,因為絕大多數(shù)實例要么是有狀態(tài)的(例如持有文件、遠(yuǎn)程鏈接等),則無法應(yīng)用原型模式;要么是無狀態(tài)的,此時應(yīng)用單例模式更合適。
思來想去,基于原型模式創(chuàng)建重復(fù)對象的作用,我們可以利用該模式模擬細(xì)胞分裂。
五、細(xì)胞分裂
按照上面案例,我們新建一個細(xì)胞類Cell
public class Cell implements Cloneable{
// 當(dāng)前細(xì)胞為第num次分裂所得,細(xì)胞分裂是一個1變2,2變4,4變8的過程,
private Integer num = 0;
@Override
public Cell clone() {
try {
// 每當(dāng)克隆一次,num+1
num++;
Cell clone = (Cell) super.clone();
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}演示細(xì)胞分裂過程
public static void cellDivision() {
// 第一個細(xì)胞
Cell cell1 = new Cell();
System.out.println("cell1:" + cell1);
// cell1 通過自我復(fù)制, 產(chǎn)生一個新細(xì)胞 cell2
Cell cell2 = cell1.clone();
System.out.println("第一次分裂后:");
System.out.println("cell1:" + cell1);
System.out.println("cell2:" + cell2);
// cell1、cell2 通過自我復(fù)制, 產(chǎn)生新細(xì)胞 cell3、cell4
Cell cell3 = cell1.clone();
Cell cell4 = cell2.clone();
System.out.println("第二次分裂后:");
System.out.println("cell1:" + cell1);
System.out.println("cell2:" + cell2);
System.out.println("cell3:" + cell3);
System.out.println("cell4:" + cell4);
}輸出如下:

此時如果要計算當(dāng)前一共有多少個細(xì)胞,就可以通過 2 n u m 2^{num} 2num得到結(jié)果。
六、改造細(xì)胞分裂邏輯
我們將N個細(xì)胞(無論分裂多少次)作為一個整體,假設(shè)該整體中所有細(xì)胞同時分裂,則可以將該細(xì)胞整體進(jìn)行抽象。如下所示,新建一個細(xì)胞整體類Cells。
- 實現(xiàn)Cloneable接口,表示該細(xì)胞整體可進(jìn)行復(fù)制分裂。
- 重寫clone()方法,該細(xì)胞整體分裂的過程,其實就是所有個體在分裂,最后將其整合即可
public class Cells implements Cloneable{
// 該細(xì)胞整體中的所有細(xì)胞個體
private List<Cell> cellList = new ArrayList<>();
// 添加一個細(xì)胞
public void addCell(Cell cell) {
cellList.add(cell);
}
// 該細(xì)胞整體復(fù)制分裂的過程
@Override
public Cells clone() {
try {
System.out.println("第" + (cellList.get(0).getNum()+1) + "次分裂...");
// 新增的細(xì)胞數(shù)量和原細(xì)胞數(shù)量相同
List<Cell> clonedCellList = new ArrayList<>(cellList.size());
for (Cell cell : cellList) {
// 將每一個細(xì)胞分裂得到的新細(xì)胞添加到clonedCellList集合中
clonedCellList.add(cell.clone());
}
// 新細(xì)胞與原細(xì)胞合并
cellList.addAll(clonedCellList);
// 復(fù)制該細(xì)胞整體
Cells clone = (Cells) super.clone();
clone.setCellList(cellList);
System.out.println("分裂后得到細(xì)胞數(shù):" + cellList.size());
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}下面我們對該細(xì)胞整體的分裂過程進(jìn)行演示
public static void cellDivision() {
// 第一個細(xì)胞
Cell cell = new Cell();
// 向該細(xì)胞整體中添加第一個細(xì)胞
Cells cells = new Cells();
cells.addCell(cell);
// 細(xì)胞分裂10次
for (int i = 0; i < 10; i++) {
cells = cells.clone();
}
}輸出如下:

七、總結(jié)
- 原型模式用于創(chuàng)建具有相同屬性的重復(fù)對象。
- 原型模式的實現(xiàn)就是實現(xiàn)Cloneable接口 + 重寫clone()方法實現(xiàn)的。
- Object類的clone()方法實現(xiàn)由JVM實現(xiàn),性能較好。但僅能實現(xiàn)淺拷貝。
到此這篇關(guān)于Java設(shè)計模式之原型模式詳細(xì)解讀的文章就介紹到這了,更多相關(guān)Java原型模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring注解@Qualifier的詳細(xì)用法你知道幾種
本文給大家分享Spring注解@Qualifier的詳細(xì)用法,包括@Autowired和@Resource區(qū)別介紹,本文通過示例代碼給大家詳細(xì)介紹,感興趣的朋友跟隨小編一起看看吧2021-07-07
如何解決報錯:java.net.BindException:無法指定被請求的地址問題
在Linux虛擬機(jī)上安裝并啟動Tomcat時遇到啟動失敗的問題,通過檢查端口及配置文件未發(fā)現(xiàn)異常,后發(fā)現(xiàn)/etc/hosts文件中缺少localhost的映射,添加后重啟Tomcat成功,Tomcat啟動時會檢查localhost的IP映射,缺失或錯誤都可能導(dǎo)致啟動失敗2024-10-10
java?stream使用指南之sorted使用及進(jìn)階方式
這篇文章主要介紹了java?stream使用指南之sorted使用及進(jìn)階方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05
詳解Spring Boot最新版優(yōu)雅停機(jī)的方法
這篇文章主要介紹了Spring Boot最新版優(yōu)雅停機(jī)的相關(guān)知識,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10

