Java設(shè)計(jì)模式之原型模式詳解
一、前言
原型模式是一種比較簡(jiǎn)單的模式,也非常容易理解,實(shí)現(xiàn)一個(gè)接口,重寫(xiě)一個(gè)方法即完成了原型模式。在實(shí)際應(yīng)用中,原型模式很少單獨(dú)出現(xiàn)。經(jīng)常與其他模式混用,他的原型類Prototype也常用抽象類來(lái)替代。
該模式的思想就是將一個(gè)對(duì)象作為原型,對(duì)其進(jìn)行復(fù)制、克隆,產(chǎn)生一個(gè)和原對(duì)象類似的新對(duì)象。在Java中,復(fù)制對(duì)象是通過(guò)clone()實(shí)現(xiàn)的,先創(chuàng)建一個(gè)原型類,通過(guò)實(shí)現(xiàn)Cloneable 接口
public class Prototype implements Cloneable { public Object clone() throws CloneNotSupportedException { Prototype proto = (Prototype) super.clone(); return proto; } }
只需要實(shí)現(xiàn)Cloneable接口,覆寫(xiě)clone方法,此處clone方法可以改成任意的名稱,因?yàn)镃loneable接口是個(gè)空接口,你可以任意定義實(shí)現(xiàn)類的方法名,如cloneA或者cloneB,因?yàn)榇颂幍闹攸c(diǎn)是super.clone()這句話,super.clone()調(diào)用的是Object的clone()方法,而在Object類中,clone()是native的,說(shuō)明這個(gè)方法實(shí)現(xiàn)并不是使用java語(yǔ)言,是底層C實(shí)現(xiàn)阿達(dá)
至于cloneA或者cloneB名字可以任意取,是因?yàn)橐阒鲃?dòng)去調(diào)用的,所以你名字取成什么,你調(diào)用的時(shí)候就調(diào)用該名字就可以了
二、優(yōu)點(diǎn)及適用場(chǎng)景
使用原型模式創(chuàng)建對(duì)象比直接new一個(gè)對(duì)象在性能上要好的多,因?yàn)樯厦嫖乙蔡岬竭^(guò),Object類的clone()是native的,它直接操作內(nèi)存中的二進(jìn)制流,特別是復(fù)制大對(duì)象時(shí),性能的差別非常明顯。
使用原型模式的另一個(gè)好處是簡(jiǎn)化對(duì)象的創(chuàng)建,使得創(chuàng)建對(duì)象就像我們?cè)诰庉嬑臋n時(shí)的復(fù)制粘貼一樣簡(jiǎn)單。
因?yàn)橐陨蟽?yōu)點(diǎn),所以在需要重復(fù)地創(chuàng)建相似對(duì)象時(shí)可以考慮使用原型模式。比如需要在一個(gè)循環(huán)體內(nèi)創(chuàng)建對(duì)象,假如對(duì)象創(chuàng)建過(guò)程比較復(fù)雜或者循環(huán)次數(shù)很多的話,使用原型模式不但可以簡(jiǎn)化創(chuàng)建過(guò)程,而且可以使系統(tǒng)的整體性能提高很多。
三、原型模式的注意事項(xiàng)
使用原型模式復(fù)制對(duì)象不會(huì)調(diào)用類的構(gòu)造方法。因?yàn)閷?duì)象的復(fù)制是通過(guò)調(diào)用Object類的clone()來(lái)完成的,它直接在內(nèi)存中復(fù)制數(shù)據(jù),因此不會(huì)調(diào)用到類的構(gòu)造方法。不但構(gòu)造方法中的代碼不會(huì)執(zhí)行,甚至連訪問(wèn)權(quán)限都對(duì)原型模式無(wú)效。
說(shuō)到這里,就得順便提一下單例模式,在單例模式中,只要將構(gòu)造方法的訪問(wèn)權(quán)限設(shè)置為private型,就可以實(shí)現(xiàn)單例。但是clone方法直接無(wú)視構(gòu)造方法的權(quán)限,所以,單例模式與原型模式是沖突的,在使用時(shí)要特別注意。
四、淺復(fù)制和深復(fù)制
另外還得知道兩個(gè)特別重要的概念 : 淺復(fù)制 深復(fù)制
淺復(fù)制:將一個(gè)對(duì)象復(fù)制后,基本數(shù)據(jù)類型的變量都會(huì)重新創(chuàng)建,而數(shù)組、容器對(duì)象、引用對(duì)象等都不會(huì)拷貝,指向的還是原對(duì)象所指向的地址。淺拷貝實(shí)現(xiàn) Cloneable,重寫(xiě)clone方法
深復(fù)制:將一個(gè)對(duì)象復(fù)制后,不論是基本數(shù)據(jù)類型還有引用類型,都是重新創(chuàng)建的。簡(jiǎn)單來(lái)說(shuō),就是深復(fù)制進(jìn)行了完全徹底的復(fù)制,而淺復(fù)制不徹底。深拷貝是通過(guò)實(shí)現(xiàn) Serializable 讀取二進(jìn)制流
五、淺復(fù)制demo演示
首先我們創(chuàng)建一個(gè)抽象原型類 Animal.class,實(shí)現(xiàn)了Cloneable接口,并且重寫(xiě)了clone方法
package cn.zygxsq.design.module.prototypePattern; /** * Created by yjl on 2021/4/30. * 動(dòng)物類 * 原型模式:博文介紹鏈接:https://blog.csdn.net/qq_27471405/article/details/116309878 */ public abstract class Animal implements Cloneable{ private String id; public String name; abstract void shout(); public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 淺復(fù)制 */ public Object clone() throws CloneNotSupportedException { Animal clone = (Animal) super.clone(); return clone; } }
再創(chuàng)建兩個(gè)實(shí)現(xiàn)類
Dog.class 和Cat.class
package cn.zygxsq.design.module.prototypePattern; /** * Created by yjl on 2021/4/30. */ public class Dog extends Animal { public Dog(){ name = "狗狗"; } @Override public void shout() { System.out.println("我的叫聲是:汪汪汪"); } }
package cn.zygxsq.design.module.prototypePattern; /** * Created by yjl on 2021/4/30. */ public class Cat extends Animal { public Cat(){ name = "貓貓"; } @Override public void shout() { System.out.println("我的叫聲是:喵喵喵"); } }
然后創(chuàng)建一個(gè)數(shù)據(jù)緩存類,用戶存儲(chǔ)從數(shù)據(jù)庫(kù)中獲取到的大對(duì)象數(shù)據(jù)或者曾經(jīng)使用過(guò)的大對(duì)象數(shù)據(jù)
以后下一次想要再次對(duì)這個(gè)對(duì)象數(shù)據(jù)操作的時(shí)候,直接從緩存里獲取并且clone一個(gè)
package cn.zygxsq.design.module.prototypePattern; import com.google.common.collect.Maps; import org.springframework.beans.factory.InitializingBean; import java.util.HashMap; import java.util.Hashtable; import java.util.concurrent.ConcurrentMap; /** * Created by yjl on 2021/4/30. * 緩存類 用于加載一些數(shù)據(jù)庫(kù)的緩存的數(shù)據(jù) */ public class DataCache /*implements InitializingBean*/{ //正常的情況是 實(shí)現(xiàn) InitializingBean ,用于web服務(wù)啟動(dòng)的時(shí)候加載數(shù)據(jù) // 這里測(cè)試由于不是web服務(wù),所以就模擬加載數(shù)據(jù) private static ConcurrentMap<String, Animal> animalCache = Maps.newConcurrentMap(); public static Animal getAnimal(String id) throws Exception{ Animal cache = animalCache.get(id); return (Animal) cache.clone(); } public static void init(){ Dog dog = new Dog(); String dogid = "111"; dog.setId(dogid); animalCache.put(dogid,dog); Dog dog2 = new Dog(); String dogid2 = "222"; dog2.setId(dogid2); animalCache.put(dogid2,dog2); Cat cat = new Cat(); String catid = "333"; cat.setId(catid); animalCache.put(catid,cat); } }
最后咱們開(kāi)始測(cè)試
先是從數(shù)據(jù)庫(kù)里加載緩存,然后要從緩存里獲取數(shù)據(jù),并且的到的是一個(gè)個(gè)clone出來(lái)的對(duì)象
package cn.zygxsq.design.module.prototypePattern; import com.alibaba.fastjson.JSON; /** * Created by yjl on 2021/4/30. * 測(cè)試主類 淺復(fù)制 * 原型模式:博文介紹鏈接:https://blog.csdn.net/qq_27471405/article/details/116309878 */ public class TestPrototype { public static void main(String[] args) { DataCache.init(); // 模擬加載數(shù)據(jù)到緩存中 try { Animal animal = DataCache.getAnimal("111"); System.out.println(animal.getName()+"---"+JSON.toJSONString(animal)); Animal animal222 = DataCache.getAnimal("222"); System.out.println(animal222.getName()+"---"+JSON.toJSONString(animal222)); Animal animal333 = DataCache.getAnimal("333"); System.out.println(animal333.getName()+"---"+JSON.toJSONString(animal333)); animal.shout(); animal222.shout(); animal333.shout(); } catch (Exception e) { e.printStackTrace(); } } }
運(yùn)行結(jié)果:
小伙伴們看的時(shí)候,好像沒(méi)什么問(wèn)題,確實(shí)沒(méi)什么問(wèn)題,但是細(xì)細(xì)一看,還是有一定的問(wèn)題的
package cn.zygxsq.design.module.prototypePattern; import com.alibaba.fastjson.JSON; /** * Created by yjl on 2021/4/30. * 淺復(fù)制遇到的問(wèn)題 */ public class TestCloneProblem { public static void main(String[] args) { //做完TestPrototype的main方法后,好像覺(jué)得淺復(fù)制沒(méi)有什么問(wèn)題 //那么可以看一下下面的a1的name 和 克隆后的name指向的是同一個(gè)地址 DataCache.init(); // 模擬加載數(shù)據(jù)到緩存中 try { Animal a1 = DataCache.getAnimal("111"); Animal a2 = (Animal)a1.clone(); System.out.println(a1==a2); System.out.println(a1.name == a2.name); } catch (Exception e) { e.printStackTrace(); } } }
運(yùn)行結(jié)果:
大家踩一下 a1的name 和 克隆后的name是什么樣的關(guān)系呢
大家可以看到,a1的name和a2的name是一樣的,由于他們的類型是String,所以他們指向的是同一個(gè)地址,名稱為“狗狗”的引用地址,大概的樣子可以看下圖
那怎么樣才能不讓a1.name 和a2.name不相同呢,也就是完完全全的復(fù)制,這個(gè)就得用到深復(fù)制了
深復(fù)制其實(shí)用到的就是流復(fù)制
可以在clone()的方法定義一個(gè)深復(fù)制的方法,比如deepClone()
六、深復(fù)制demo演示
記住,深復(fù)制的時(shí)候,方法一定得實(shí)現(xiàn)可序列化,Serializable
package cn.zygxsq.design.module.prototypePattern; import java.io.*; /** * Created by yjl on 2021/4/30. * 動(dòng)物類 * 原型模式:博文介紹鏈接:https://blog.csdn.net/qq_27471405/article/details/116309878 */ public abstract class Animal implements Cloneable, Serializable{ private String id; public String name; abstract void shout(); public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 淺復(fù)制 */ public Object clone() throws CloneNotSupportedException { Animal clone = (Animal) super.clone(); return clone; } /** * 深復(fù)制 */ public Object deepClone() { ByteArrayOutputStream byteArrayOutputStream = null; ObjectOutputStream objectOutputStream = null; ByteArrayInputStream byteArrayInputStream = null; ObjectInputStream objectInputStream = null; try { // 序列化 byteArrayOutputStream = new ByteArrayOutputStream(); objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(this);/*將當(dāng)前對(duì)象以對(duì)象流的方式輸出*/ //反序列化 byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); objectInputStream = new ObjectInputStream(byteArrayInputStream); Animal deepProtoType = (Animal) objectInputStream.readObject(); return deepProtoType; } catch (Exception e) { e.printStackTrace(); return null; } finally { try { byteArrayOutputStream.close(); objectOutputStream.close(); byteArrayInputStream.close(); objectInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
測(cè)試一下結(jié)果
package cn.zygxsq.design.module.prototypePattern; import com.alibaba.fastjson.JSON; /** * Created by yjl on 2021/4/30. * 測(cè)試主類 深復(fù)制 * 原型模式:博文介紹鏈接:https://blog.csdn.net/qq_27471405/article/details/116309878 */ public class TestPrototypeDeepClone { public static void main(String[] args) { DataCache.init(); // 模擬加載數(shù)據(jù)到緩存中 try { Animal a1 = DataCache.getAnimal("111"); Animal a2 = (Animal)a1.deepClone(); System.out.println(a1==a2); System.out.println(a1.name == a2.name); System.out.println(a1.name); } catch (Exception e) { e.printStackTrace(); } } }
運(yùn)行結(jié)果:
這就是深復(fù)制和淺復(fù)制以及原型模式的使用
到此這篇關(guān)于Java設(shè)計(jì)模式之原型模式詳解的文章就介紹到這了,更多相關(guān)Java原型模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring+SpringMVC+MyBatis整合詳細(xì)教程(SSM)
Spring是一個(gè)開(kāi)源框架,Spring是于2003 年興起的一個(gè)輕量級(jí)的Java 開(kāi)發(fā)框架。這篇文章主要介紹了Spring+SpringMVC+MyBatis整合詳細(xì)教程(SSM),需要的朋友可以參考下2017-10-10Mybatis中xml的動(dòng)態(tài)sql實(shí)現(xiàn)示例
本文主要介紹了Mybatis中xml的動(dòng)態(tài)sql實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06SpringBoot項(xiàng)目部署到服務(wù)器的兩種方式
目前,前后端分離的架構(gòu)已成主流,而使用SpringBoot構(gòu)建Web應(yīng)用是非??焖俚?項(xiàng)目發(fā)布到服務(wù)器上的時(shí)候,只需要打成一個(gè)jar包,然后通過(guò)命令 : java -jar jar包名稱即可啟動(dòng)服務(wù)了,本文介紹了SpringBoot項(xiàng)目部署到服務(wù)器的兩種方式,需要的朋友可以參考下2024-10-10idea中Java實(shí)體類怎樣生成序列化的版本號(hào)的方法
這篇文章主要介紹了idea中Java實(shí)體類怎樣生成序列化的版本號(hào)的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11詳解如何使用tldb數(shù)據(jù)庫(kù)的java客戶端
這篇文章主要為大家介紹了如何使用tldb數(shù)據(jù)庫(kù)的java客戶端過(guò)程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09java比較兩個(gè)json文件的差異及說(shuō)明
這篇文章主要介紹了java比較兩個(gè)json文件的差異及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10Java學(xué)習(xí)教程之定時(shí)任務(wù)全家桶
這篇文章主要給大家介紹了關(guān)于Java學(xué)習(xí)教程之定時(shí)任務(wù)全家桶的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11IDEA 2020.2 部署JSF項(xiàng)目的詳細(xì)過(guò)程
本文通過(guò)圖文并茂的形式教大家如何在IDEA中創(chuàng)建一個(gè)JSF項(xiàng)目及遇到問(wèn)題的解決方法,感興趣的朋友跟隨小編一起看看吧2021-09-09