欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java設(shè)計(jì)模式之單例和原型

 更新時(shí)間:2022年10月11日 09:06:27   作者:tianClassmate  
這篇文章介紹了Java設(shè)計(jì)模式之單例和原型,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

今天這篇文章我們來(lái)學(xué)習(xí)創(chuàng)建型設(shè)計(jì)模式的另外兩個(gè)孿生兄弟,單例和原型,其中原型設(shè)計(jì)模式中我們深入到JVM的內(nèi)存模型,最后順便談?wù)凧ava中的值傳遞和引用傳遞。

上篇文章老王買(mǎi)產(chǎn)品 我們從最原始的基本實(shí)現(xiàn)方法,到簡(jiǎn)單(靜態(tài))工廠,然后使用工廠方法設(shè)計(jì)模式進(jìn)行改造,最后考慮產(chǎn)品會(huì)產(chǎn)生變體,我們又?jǐn)U展到了抽象工廠。

設(shè)計(jì)模式所有的相關(guān)代碼均已上傳到碼云 讀者可以自行下載學(xué)習(xí)測(cè)試,本地源碼下載。

一、引出問(wèn)題

今天老王又來(lái)了,還是想買(mǎi)我們的產(chǎn)品,今天老王上老就提出來(lái)一個(gè)要求,當(dāng)他購(gòu)買(mǎi)產(chǎn)品的時(shí)候,每次都要從貨架上給他拿相同的一個(gè)。

如果用傳統(tǒng)實(shí)現(xiàn)方式,當(dāng)老王拿到產(chǎn)品以后,直接和上一個(gè)比對(duì)一下就行了,如果不一致老王就還回來(lái)。

但通過(guò)我們查閱軟件的七大設(shè)計(jì)原則 ,這很明顯違反了依賴(lài)倒置原則,為了避免耦合和讓代碼更易于維護(hù),老王是不能依賴(lài)具體產(chǎn)品的。

二、單例

我們就需要將產(chǎn)品比對(duì)在創(chuàng)建產(chǎn)品的時(shí)候進(jìn)行判斷,老王就只管拿。

老王來(lái)之前應(yīng)該還有兩種情況,一種就是老王還沒(méi)來(lái),產(chǎn)品就準(zhǔn)備好了,也即餓漢式。第二種就是老王什么時(shí)候來(lái),什么時(shí)候給他準(zhǔn)備產(chǎn)品,也即懶漢式。

我們看具體的實(shí)現(xiàn)代碼:

懶漢式:

/**
 * 懶漢式
 * @author tcy
 * @Date 29-07-2022
 */
public class LazySingletonProduct {

    private static volatile LazySingletonProduct instance=null;

    private LazySingletonProduct(){}

    public static synchronized LazySingletonProduct getInstance(){
        if (instance==null){
            instance=new LazySingletonProduct();

        }
        return instance;
    }

餓漢式:

/**
 * 餓漢式
 * @author tcy
 * @Date 29-07-2022
 */
public class HungrySingletonProduct {
    private static volatile HungrySingletonProduct instance=new HungrySingletonProduct();

    private HungrySingletonProduct(){};

    public static synchronized HungrySingletonProduct getInstance(){
        if (instance==null){
            instance=new HungrySingletonProduct();
        }
        return instance;
    }
}

老王類(lèi):

/**
 * @author tcy
 * @Date 29-07-2022
 */
public class Client {
    public static void main(String[] args) {
        HungrySingletonProduct instance1 = HungrySingletonProduct.getInstance();
        HungrySingletonProduct instance2 = HungrySingletonProduct.getInstance();

        if (instance1==instance2){
            System.out.println("我倆一樣...");
        }else {
            System.out.println("我倆不一樣...");
        }
    }
}

以上就是單例設(shè)計(jì)模式中的懶漢式和餓漢式,應(yīng)該是設(shè)計(jì)模式中最簡(jiǎn)單的一個(gè),理解起來(lái)難度也不大。

為了克服老王和他兒子小王一起來(lái)拿錯(cuò)的尷尬,我們?cè)诜椒ㄉ霞觭ynchronized鎖,對(duì)象引用上加volatile共享變量,但這樣會(huì)帶來(lái)效率問(wèn)題,如果不考慮多線(xiàn)程需求,讀者可自行去掉。

三、原型

老王今天很明顯是找茬,他繼續(xù)說(shuō),如果我不想要一個(gè)了,我要每次買(mǎi)都要不同的,你看著辦。

每次創(chuàng)建產(chǎn)品都要不同的,傳統(tǒng)的方式肯定就是重新new一個(gè)對(duì)象,但每創(chuàng)建一個(gè)對(duì)象都是一個(gè)復(fù)雜的過(guò)程,而且這樣還會(huì)帶來(lái)一定的代碼冗余。

這就需要用到創(chuàng)建型設(shè)計(jì)模式中的原型模式中的拷貝,其中又分為淺拷貝和深拷貝。

我們先看基本概念。

  • 淺克?。簞?chuàng)建一個(gè)新對(duì)象,對(duì)象種屬性和原來(lái)對(duì)象的屬性完全相同,對(duì)于非基本類(lèi)型屬性仍指向原有屬性所指向的內(nèi)存地址
  • 深克?。簞?chuàng)建一個(gè)新對(duì)象,屬性中引用類(lèi)型也會(huì)被克隆,不再指向原來(lái)屬性所指向的內(nèi)存地址

這段意思也就是,老王購(gòu)買(mǎi)產(chǎn)品的時(shí)候,如果產(chǎn)品都是基本數(shù)據(jù)類(lèi)型(byte(位)、short(短整數(shù))、int(整數(shù))、long(長(zhǎng)整數(shù))、float(單精度)、double(雙精度)、char(字符)和boolean(布爾值))和String,那么我們就使用淺拷貝。

如果產(chǎn)品包括別的產(chǎn)品(對(duì)象)的引用類(lèi)型就要使用深拷貝。

如果想搞明白,為什么造成深拷貝和淺拷貝這個(gè)問(wèn)題,我們就要重點(diǎn)說(shuō)說(shuō)JVM的內(nèi)存模型。

我們聲明一個(gè)基本數(shù)據(jù)類(lèi)型的變量a=2,實(shí)際上是在棧中直接存儲(chǔ)了一個(gè)a=2,當(dāng)拷貝的時(shí)候直接把值拷貝過(guò)去,也就是直接有了一份a的副本。

當(dāng)我們創(chuàng)建一個(gè)對(duì)象時(shí)Student stu=new Student(),實(shí)際上對(duì)象的值存儲(chǔ)在堆中,在棧中只存放了stu="對(duì)象地址",stu指向了堆中的地址,jvm拷貝的時(shí)候只復(fù)制了棧中的地址,實(shí)際上他們堆中的對(duì)象還是一個(gè)。

我們?cè)賮?lái)看String類(lèi)型。String 存在于堆內(nèi)存、常量池;這種比較特殊, 傳遞是引用地址;由本身的final性, 每次賦值都是一個(gè)新的引用地址,原對(duì)象的引用和副本的引用互不影響。因此String就和基本數(shù)據(jù)類(lèi)型一樣,表現(xiàn)出了"深拷貝"特性。

我們具體看實(shí)現(xiàn)代碼:

淺拷貝類(lèi):

/**
 * @author tcy
 * @Date 29-07-2022
 */
public class ShallowProduct implements Cloneable{

    private String name;

    private int num;

    public void show(){
        System.out.println("這是淺產(chǎn)品..."+name+"數(shù)量:"+num);
    }

    public String getName() {
        return name;
    }

    public ShallowProduct setName(String name) {
        this.name = name;
        return this;
    }

    public int getNum() {
        return num;
    }

    public ShallowProduct setNum(int num) {
        this.num = num;
        return this;
    }

    @Override
    public ShallowProduct clone() throws CloneNotSupportedException {
        return (ShallowProduct) super.clone();
    }
}

如果需要哪個(gè)對(duì)象淺拷貝,需要該對(duì)象實(shí)現(xiàn)Cloneable接口,并重寫(xiě)clone()方法。

public void shallowTest()throws CloneNotSupportedException{
    ShallowProduct product1=new ShallowProduct();
    ShallowProduct product2 = product1.clone();
    product1.setName("老王");
    product2.setName("老李");

    product1.setNum(1);
    product2.setNum(2);

    product1.show();

    product2.show();
}

調(diào)用時(shí)輸出的對(duì)象中的值直接就是兩個(gè)不同的對(duì)象,實(shí)現(xiàn)了對(duì)象的淺拷貝。

如果該對(duì)象中包括引用類(lèi)型呢?那怎么實(shí)現(xiàn)呢。

其實(shí)原理上也是很簡(jiǎn)單的,只需要將非基本數(shù)據(jù)類(lèi)型也像淺拷貝那樣操做就行了,然后在當(dāng)前clone()方法中,調(diào)用非基本數(shù)據(jù)類(lèi)型的clone()方法

深拷貝引用類(lèi):

/**
 * @author tcy
 * @Date 29-07-2022
 */
public class Child implements Cloneable{

    private String childName;

    public String getChildName() {
        return childName;
    }

    public Child setChildName(String childName) {
        this.childName = childName;
        return this;
    }

    @Override
    protected Child clone() throws CloneNotSupportedException {
        return (Child) super.clone();
    }
}

深拷貝類(lèi):

/**
 * @author tcy
 * @Date 29-07-2022
 */
public class DeepProduct implements Cloneable{

    private String name;

    private Integer num;

    private Child child;
    public String getName() {
        return name;
    }

    public DeepProduct setName(String name) {
        this.name = name;
        return this;
    }

    public Integer getNum() {
        return num;
    }

    public DeepProduct setNum(Integer num) {
        this.num = num;
        return this;
    }

    public void show(){
        System.out.println("這是深產(chǎn)品..."+name+"數(shù)量:"+num+"包括child:"+child.getChildName());
    }


    @Override
    public DeepProduct clone() throws CloneNotSupportedException {
        DeepProduct clone = (DeepProduct) super.clone();
        clone.child=child.clone();
        return clone;
    }

    public Child getChild() {
        return child;
    }

    public DeepProduct setChild(Child child) {
        this.child = child;
        return this;
    }
}

我們測(cè)試一下對(duì)象中的值是否發(fā)生了改變。

public void deepTest() throws CloneNotSupportedException {
    DeepProduct product1=new DeepProduct();
    Child child=new Child();
    child.setChildName("老王child");

    product1.setName("老王");
    product1.setNum(1);
    product1.setChild(child);

    //--------------
    DeepProduct product2=product1.clone();
    product2.setName("老李");
    product2.setNum(2);
    product2.getChild().setChildName("老李child");

    product1.show();
    product2.show();
}

老李、老王都正確的輸出了,說(shuō)明實(shí)現(xiàn)沒(méi)有問(wèn)題。

這樣就符合了老王的要求。

既然說(shuō)到了jvm的內(nèi)存模型,就有必要說(shuō)一下java中的值傳遞和引用傳遞。

實(shí)際上java應(yīng)該就是值傳遞,在調(diào)用方法的時(shí)候,如果參數(shù)是基本數(shù)據(jù)類(lèi)型,那么傳遞的就是副本,我們?cè)诜椒ㄖ袩o(wú)論怎么給他賦值,他原本的值都不會(huì)有變化。

在調(diào)用方法的時(shí)候,如果參數(shù)是引用數(shù)據(jù)類(lèi)型,那么傳遞的就是這個(gè)對(duì)象的地址,我們?cè)诜椒ㄖ行薷倪@個(gè)對(duì)象都會(huì)影響他原本的對(duì)象。

造成這個(gè)現(xiàn)象的原因其實(shí)是和淺拷貝、深拷貝的原理是一樣的,都是棧、堆內(nèi)存的結(jié)構(gòu)導(dǎo)致的。

老王看他的要求都滿(mǎn)足了,最后心滿(mǎn)意足的拿著產(chǎn)品走了。

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接

相關(guān)文章

  • Java StringTokenizer分隔符拆分字符串

    Java StringTokenizer分隔符拆分字符串

    Java中的StringTokenizer類(lèi)用于將一個(gè)字符串分解成標(biāo)記,本文主要介紹了Java StringTokenizer分隔符拆分字符串,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-08-08
  • Gauva使用ListenableFuture介紹說(shuō)明

    Gauva使用ListenableFuture介紹說(shuō)明

    并發(fā)是一個(gè)困難問(wèn)題,但是通過(guò)強(qiáng)大和強(qiáng)大的抽象能夠顯著的簡(jiǎn)化工作。為了簡(jiǎn)化問(wèn)題,Gauva使用ListenableFuture擴(kuò)展了JDK的Future接口,這篇文章主要介紹了Gauva使用ListenableFuture
    2023-01-01
  • Java接口的簡(jiǎn)單定義與實(shí)現(xiàn)方法示例

    Java接口的簡(jiǎn)單定義與實(shí)現(xiàn)方法示例

    這篇文章主要介紹了Java接口的簡(jiǎn)單定義與實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了java面向?qū)ο蟪绦蛟O(shè)計(jì)中接口的概念、功能、定義及使用技巧,需要的朋友可以參考下
    2019-01-01
  • 盤(pán)點(diǎn)總結(jié)SpringBoot自帶工具類(lèi)使用提升開(kāi)發(fā)效率

    盤(pán)點(diǎn)總結(jié)SpringBoot自帶工具類(lèi)使用提升開(kāi)發(fā)效率

    這篇文章主要為大家介紹了盤(pán)點(diǎn)總結(jié)SpringBoot自帶工具類(lèi)使用提升開(kāi)發(fā)效率,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Spring+EHcache緩存實(shí)例詳解

    Spring+EHcache緩存實(shí)例詳解

    這篇文章主要為大家詳細(xì)介紹了Spring+EHcache緩存實(shí)例,EhCache是一個(gè)純Java的進(jìn)程內(nèi)緩存框架,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • spring boot 加載web容器tomcat流程源碼分析

    spring boot 加載web容器tomcat流程源碼分析

    本文章主要描述spring boot加載web容器 tomcat的部分,為了避免文章知識(shí)點(diǎn)過(guò)于分散,其他相關(guān)的如bean的加載,tomcat內(nèi)部流程等不做深入討論,具體內(nèi)容詳情跟隨小編一起看看吧
    2021-06-06
  • java hasNextInt判斷是否為數(shù)字的方法

    java hasNextInt判斷是否為數(shù)字的方法

    今天小編就為大家分享一篇java hasNextInt判斷是否為數(shù)字的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • java實(shí)現(xiàn)大文本文件拆分

    java實(shí)現(xiàn)大文本文件拆分

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)大文本文件拆分,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • Java中的數(shù)組流ByteArrayOutputStream用法

    Java中的數(shù)組流ByteArrayOutputStream用法

    Java中的ByteArrayOutputStream是java.io包中的一個(gè)類(lèi),用于在內(nèi)存中創(chuàng)建字節(jié)數(shù)組緩沖區(qū),支持動(dòng)態(tài)擴(kuò)展,它繼承自O(shè)utputStream,允許以字節(jié)形式寫(xiě)入數(shù)據(jù),無(wú)需與外部設(shè)備交互,常用方法包括write()、toByteArray()、toString()等
    2024-09-09
  • 一文帶你掌握J(rèn)PA實(shí)體類(lèi)注解

    一文帶你掌握J(rèn)PA實(shí)體類(lèi)注解

    這篇文章主要給大家詳細(xì)介紹一下?JPA?實(shí)體類(lèi)中的注解,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java有一定的幫助,感興趣的小伙伴可以了解一下
    2023-01-01

最新評(píng)論