Java?深入理解創(chuàng)建型設(shè)計模式之原型模式
1.思考問題
現(xiàn)在有一只羊 tom,姓名為: tom,年齡為:1,顏色為:白色,請編寫程序創(chuàng)建和 tom羊?qū)傩酝耆嗤?0只羊。
按照傳統(tǒng)的思路來,我們可能會按照下面的方式去寫。

那么這種寫法的優(yōu)缺點自然而然就出來了:
- 優(yōu)點是比較好理解,簡單易操作。
- 缺點是在創(chuàng)建新的對象時,總是需要重新獲取原始對象的屬性,如果創(chuàng)建的對象比較復(fù)雜時,效率較低。總是需要重新初始化對象,而不是動態(tài)地獲得對象運行時的狀態(tài),不夠靈活。
- 改進的思路分析:Java中Object類是所有類的根類,Object類提供了一個 clone()方法,該方法可以將一個Java對象復(fù)制一份,但是需要實現(xiàn)clone的Java類必須要實現(xiàn)一個接口Cloneable,該接口表示該類能夠復(fù)制且具有復(fù)制的能力 => 原型模式。
2.什么是原型模式?
- 原型模式(Prototype模式)是指: 用原型實例指定創(chuàng)建對象的種類,并且通過拷貝這些原型,創(chuàng)建新的對象。
- 原型模式是一種創(chuàng)建型設(shè)計模式,允許一個對象再創(chuàng)建另外一個可定制的對象,無需知道如何創(chuàng)建的細(xì)節(jié)。
- 工作原理是: 通過將一個原型對象傳給那個要發(fā)動創(chuàng)建的對象,這個要發(fā)動創(chuàng)建的對象通過請求原型對象拷貝它們自己來實施創(chuàng)建,即對象.clone()。
原型模式相關(guān)的類圖如下:

3.克隆羊多莉案例代碼(淺拷貝)
這里由于我是新建的普通Java項目,并不是maven項目,所以沒法加lombok依賴。那么這里的構(gòu)造器、setter/getter方法顯得這個類的篇幅比較長。。。
package com.szh.prototype.shallowclone;
public class Sheep implements Cloneable {
private String name;
private Integer age;
private String color;
private Sheep friend;
public Sheep(String name, Integer age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public Sheep(String name, Integer age, String color, Sheep friend) {
this.name = name;
this.age = age;
this.color = color;
this.friend = friend;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
", friend=" + friend +
'}';
}
//克隆該實例,使用默認(rèn)的clone方法來完成
@Override
protected Object clone() {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println(e.getMessage());
}
return sheep;
}
}
package com.szh.prototype.shallowclone;
public class MainTest {
public static void main(String[] args) {
Sheep sheep = new Sheep("多莉",5,"黑白相間",new Sheep("喜羊羊",1,"白色"));
Sheep sheep2 = (Sheep) sheep.clone();
Sheep sheep3 = (Sheep) sheep.clone();
Sheep sheep4 = (Sheep) sheep.clone();
Sheep sheep5 = (Sheep) sheep.clone();
System.out.println(sheep + " sheep.friend.hashCode = " + sheep.getFriend().hashCode());
System.out.println(sheep2 + " sheep2.friend.hashCode = " + sheep2.getFriend().hashCode());
System.out.println(sheep3 + " sheep3.friend.hashCode = " + sheep3.getFriend().hashCode());
System.out.println(sheep4 + " sheep4.friend.hashCode = " + sheep4.getFriend().hashCode());
System.out.println(sheep5 + " sheep5.friend.hashCode = " + sheep5.getFriend().hashCode());
}
}
從上面的運行結(jié)果中可以看到,Sheep類中的前三個成員屬性都可以成功的拷貝,但是最后一個friend,它表示羊的朋友(也是Sheep類型,就是引用類型了)。而當(dāng)我們拷貝完成之后,應(yīng)該來說都是不一樣的新的對象,但是它們中的friend屬性的hashCode居然是一樣的?。。?nbsp; 這里我們就要來聊一聊深拷貝和淺拷貝了。
淺拷貝:
- 對于數(shù)據(jù)類型是基本數(shù)據(jù)類型的成員變量,淺拷貝會直接進行值傳遞,也就是將該屬性值復(fù)制一份給新的對象。
- 對于數(shù)據(jù)類型是引用數(shù)據(jù)類型的成員變量,比如說成員變量是某個數(shù)組、某個類對象等,那么淺拷貝會進行引用傳遞,也就是只是將該成員變量的引用值(內(nèi)存地址〉復(fù)制一份給新的對象。因為實際上兩個對象的該成員變量都指向同一個實例。在這種情況下,在一個對象中修改該成員變量會影響到另一個對象的該成員變量值。
- 前面我們克隆羊就是淺拷貝。淺拷貝是使用默認(rèn)的clone()方法來實現(xiàn):
sheep= (Sheep) super.clone();
深拷貝:
- 復(fù)制對象的所有基本數(shù)據(jù)類型的成員變量值。
- 為所有引用數(shù)據(jù)類型的成員變量申請存儲空間,并復(fù)制每個引用數(shù)據(jù)類型成員變量所引用的對象,直到該對象可達(dá)的所有對象。也就是說,對象進行深拷貝要對整個對象(包括對象的引用類型)進行拷貝。
- 深拷貝實現(xiàn)方式1: 重寫clone方法來實現(xiàn)深拷貝。
- 深拷貝實現(xiàn)方式2: 通過對象序列化實現(xiàn)深拷貝(推薦)。
4.深拷貝代碼案例
package com.szh.prototype.deepclone;
import java.io.Serializable;
public class DeepCloneableTarget implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
@Override
public String toString() {
return "DeepCloneableTarget{" +
"cloneName='" + cloneName + '\'' +
", cloneClass='" + cloneClass + '\'' +
'}';
}
//因為該類的屬性,都是String , 因此我們這里使用默認(rèn)的clone完成即可
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package com.szh.prototype.deepclone;
import java.io.*;
public class DeepPrototype implements Serializable, Cloneable {
public String name; //String 屬性
public DeepCloneableTarget deepCloneableTarget;// 引用類型
public DeepPrototype() {
super();
}
@Override
public String toString() {
return "DeepPrototype{" +
"name='" + name + '\'' +
", deepCloneableTarget=" + deepCloneableTarget +
'}';
}
//深拷貝 - 方式 1 使用clone 方法
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
deep = super.clone();
DeepPrototype deepPrototype = (DeepPrototype) deep;
deepPrototype.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
return deepPrototype;
}
//深拷貝 - 方式2 通過對象的序列化實現(xiàn) (推薦)
public Object deepClone() {
//創(chuàng)建流對象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepPrototype copyObj = (DeepPrototype) ois.readObject();
return copyObj;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
//關(guān)閉流
try {
ois.close();
bis.close();
oos.close();
bos.close();
} catch (Exception e2) {
System.out.println(e2.getMessage());
}
}
}
}
package com.szh.prototype.deepclone;
public class MainTest {
public static void main(String[] args) throws Exception {
DeepPrototype prototype = new DeepPrototype();
prototype.name = "張起靈";
prototype.deepCloneableTarget = new DeepCloneableTarget("小哥","悶油瓶");
//方式1 完成深拷貝
DeepPrototype prototype2 = (DeepPrototype) prototype.clone();
System.out.println("方式1 完成深拷貝");
System.out.println("prototype.name = " + prototype.name + ", prototype.deepCloneableTarget = " + prototype.deepCloneableTarget);
System.out.println("prototype.deepCloneableTarget.hashCode = " + prototype.deepCloneableTarget.hashCode());
System.out.println("-------------------------------------------------------");
System.out.println("prototype2.name = " + prototype2.name + ", prototype2.deepCloneableTarget = " + prototype2.deepCloneableTarget);
System.out.println("prototype2.deepCloneableTarget.hashCode = " + prototype2.deepCloneableTarget.hashCode());
System.out.println("==============================================================================");
//方式2 完成深拷貝
DeepPrototype prototype3 = (DeepPrototype) prototype.deepClone();
System.out.println("方式2 完成深拷貝");
System.out.println("prototype.name = " + prototype.name + ", prototype.deepCloneableTarget = " + prototype.deepCloneableTarget);
System.out.println("prototype.deepCloneableTarget.hashCode = " + prototype.deepCloneableTarget.hashCode());
System.out.println("-------------------------------------------------------");
System.out.println("prototype3.name = " + prototype3.name + ", prototype3.deepCloneableTarget = " + prototype3.deepCloneableTarget);
System.out.println("prototype3.deepCloneableTarget.hashCode = " + prototype3.deepCloneableTarget.hashCode());
}
}
5.原型模式總結(jié)
- 創(chuàng)建新的對象比較復(fù)雜時,可以利用原型模式簡化對象的創(chuàng)建過程,同時也能夠提高效率。
- 不用重新初始化對象,而是動態(tài)地獲得對象運行時的狀態(tài)。
- 如果原始對象發(fā)生變化(增加或者減少屬性),其它克隆對象的也會發(fā)生相應(yīng)的變化,無需修改代碼。
- 在實現(xiàn)深克隆的時候可能需要比較復(fù)雜的代碼。
- 缺點: 需要為每一個類配備一個克隆方法,這對全新的類來說不是很難,但對已有的類進行改造時,需要修改其源代碼,違背了ocp原則。
到此這篇關(guān)于Java 深入理解創(chuàng)建型設(shè)計模式之原型模式的文章就介紹到這了,更多相關(guān)Java 原型模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot 自定義權(quán)限標(biāo)簽(tld),在freemarker引用操作
這篇文章主要介紹了springboot 自定義權(quán)限標(biāo)簽(tld),在freemarker引用操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
Java將word文件轉(zhuǎn)成pdf文件的操作方法
這篇文章主要介紹了Java將word文件轉(zhuǎn)成pdf文件的操作方法,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-09-09
Java面試題之HashMap 的 hash 方法原理是什么
那天,小二去蔚來面試,面試官老王一上來就問他:HashMap 的 hash 方法的原理是什么?當(dāng)時就把裸面的小二給蚌埠住了,這篇文章將詳細(xì)解答該題目2021-11-11
SpringBoot中整合Shiro實現(xiàn)權(quán)限管理的示例代碼
這篇文章主要介紹了SpringBoot中整合Shiro實現(xiàn)權(quán)限管理的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

