免费在线a视频-免费在线观看a视频-免费在线观看大片影视大全-免费在线观看的视频-色播丁香-色播基地

女朋友:你能給我講講單例模式嗎?

:2020年01月01日 腳本之家
分享到:

某公司老板在招程序員時(shí)承諾幫助解決單身問題,給程序員分配一個(gè)女朋友,于是單身的小強(qiáng)毫不猶豫就去應(yīng)聘了,并被順利錄用。那么我們怎么用代碼來模擬一下呢?首先定義一個(gè)女朋友的類,擁有兩個(gè)屬性,姓...

本文經(jīng)授權(quán)轉(zhuǎn)自公眾號 程序員修煉(ID:lixing2457)

作者:靜幽水 

如若轉(zhuǎn)載請聯(lián)系原公眾號

01

問題背景

某公司老板在招程序員時(shí)承諾幫助解決單身問題,給程序員分配一個(gè)女朋友,于是單身的小強(qiáng)毫不猶豫就去應(yīng)聘了,并被順利錄用。那么我們怎么用代碼來模擬一下呢?首先定義一個(gè)女朋友的類,擁有兩個(gè)屬性,姓名和年齡:

public class GirlFriend {

private String name;

private Integer age;

public GirlFriend(String name, Integer age) {

this.name = name;

this.age = age;

    }

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;

    }

@Override

public String toString() {

return "GirlFriend{" +

"name='" + name + '\'' +

", age=" + age +

'}';

    }

}

接著程序員小強(qiáng)就可以new出來一個(gè)女朋友的實(shí)例了,只需要傳進(jìn)去姓名和年齡就可以了,如下:

public class Programmer {

public static void main(String[] args){

        GirlFriend girlFriend = new GirlFriend("小美",20);

        System.out.println(girlFriend.toString());

    }

}

打印出的結(jié)果是GirlFriend{name='小美', age=20}

02

有何問題

突然有一天,程序員小強(qiáng)已經(jīng)不滿足只有一個(gè)女朋友了,于是他私自new出了多個(gè)女朋友對象出來,如下:

public class Programmer {

public static void main(String[] args){

        GirlFriend girlFriend = new GirlFriend("小美",20);

        GirlFriend girlFriend2 = new GirlFriend("小紅",18);

        GirlFriend girlFriend3 = new GirlFriend("小麗",19);

        System.out.println(girlFriend.toString());

        System.out.println(girlFriend2.toString());

        System.out.println(girlFriend3.toString());

    }

}

打印結(jié)果如下:

GirlFriend{name='小美', age=20}

GirlFriend{name='小紅', age=18}

GirlFriend{name='小麗', age=19}

但是不久就被老板發(fā)現(xiàn)了,因?yàn)閮?nèi)存中存在多個(gè)女朋友實(shí)例對象,嚴(yán)重浪費(fèi)了公司的資源,老板決定只能給小強(qiáng)分配一個(gè)女朋友,老板絞盡腦汁,終于想出了應(yīng)對方法。

03

解決方法

老板發(fā)現(xiàn),問題的根源就是不能把創(chuàng)造女朋友的權(quán)限交給小強(qiáng),應(yīng)該給他一個(gè)創(chuàng)造好的對象,并且姓名和年齡也不能由小強(qiáng)來決定,不然他肯定只要18歲的。而是要把創(chuàng)建實(shí)例的權(quán)限收回,讓類自身負(fù)責(zé)自己類實(shí)例的創(chuàng)建。

來看看代碼如何實(shí)現(xiàn):

public class GirlFriend {

private String name;

private Integer age;

//定義一個(gè)變量來存儲(chǔ)創(chuàng)建好的類實(shí)例

private static GirlFriend girlFriend = null;

//私有化構(gòu)造方法,防止外部調(diào)用

private GirlFriend(String name, Integer age) {

this.name = name;

this.age = age;

    }

//定義一個(gè)方法為程序員類提供女朋友實(shí)例

public static GirlFriend getGirlFriend(){

//判斷存儲(chǔ)實(shí)例是否為空

if(girlFriend==null){

//如果沒值,就new出一個(gè)實(shí)例,并賦值給存儲(chǔ)實(shí)例的變量

          girlFriend = new GirlFriend("小美",28);

      }

return girlFriend;

    }

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;

    }

@Override

public String toString() {

return "GirlFriend{" +

"name='" + name + '\'' +

", age=" + age +

'}';

    }

}

主要的核心思想有三點(diǎn):

1.定義一個(gè)變量來存儲(chǔ)創(chuàng)建好的類實(shí)例;

2.私有化構(gòu)造函數(shù),防止外部new該對象;

3.對外提供一個(gè)能獲取到該對象的方法。

程序員小強(qiáng)該如何獲取呢:

public class Programmer {

public static void main(String[] args){

       GirlFriend girlFriend = GirlFriend.getGirlFriend();

        System.out.println(girlFriend.toString());

    }

}

直接使用GirlFriend類調(diào)用獲取對象的getGirlFriend方法獲取到實(shí)例對象,打印結(jié)果如下:

GirlFriend{name='小美', age=28}

04

模式講解

上面這種解決方案就是單例模式(Singleton),單例模式定義:保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。

通用類圖如下:

Singleton:負(fù)責(zé)創(chuàng)建單例類自己的唯一實(shí)例,并提供一個(gè)getInstance的方法讓外部來訪問這個(gè)類的唯一實(shí)例。

單例模式功能:保證類在運(yùn)行期間只允許被創(chuàng)建一個(gè)實(shí)例。有懶漢式和餓漢式兩種實(shí)現(xiàn)方式。

懶漢式:上面的代碼就是懶漢式的實(shí)現(xiàn)方式,顧名思義,懶漢式指只有當(dāng)該實(shí)例被使用到的時(shí)候才會(huì)創(chuàng)建,通過三個(gè)步驟就可以實(shí)現(xiàn)懶漢式:

1.私有化構(gòu)造方法:防止外部使用。

2.提供獲取實(shí)例的方法:全局唯一的類實(shí)例訪問點(diǎn)。

3.把獲取實(shí)例的方法改為靜態(tài):因?yàn)橹挥徐o態(tài)的方法才能直接通過類名來調(diào)用,否則就要通過實(shí)例調(diào)用,這就陷入了死循環(huán)。

完整代碼:

public class Singleton{

//定義變量存放創(chuàng)建好的實(shí)例,因?yàn)橐陟o態(tài)方法中使用,所以變量也必須是靜態(tài)的

private static Singleton uniqueInstance = null;

//私有化構(gòu)造方法,可以在內(nèi)部控制創(chuàng)建實(shí)例的數(shù)目

private Singleton(){

    }

//定義一個(gè)方法為客戶端提供類實(shí)例,synchronized同步保證線程安全

public static synchronized Singleton getInstance(){

//判斷是否已經(jīng)有實(shí)例

if(uniqueInstance == null){

            uniqueInstance = new Singleton();

        }

//有就直接用

return uniqueInstance;

    }

}

這里使用到了synchronized用來保證線程安全,如果不加會(huì)帶來什么問題呢?比如兩個(gè)線程A和B,就有可能導(dǎo)致并發(fā)的問題,如圖所示:

這種情況就會(huì)創(chuàng)建出兩個(gè)實(shí)例出來,單例模式也就失效了。加上synchronized雖然能保證線程安全,但是卻降低了訪問速度,影響了性能,可以考慮使用雙重檢查加鎖來解決這個(gè)問題,雙重檢查加鎖意思是并不是每次進(jìn)入getInstance方法都需要同步,而是先不同步,進(jìn)入方法之后先檢查實(shí)例是否存在,如果不存在才進(jìn)入同步塊。這是第一重檢查。進(jìn)入同步塊之后再檢查實(shí)例是否存在,如果不存在,就在同步的情況下創(chuàng)建一個(gè)實(shí)例,這是第二重檢查。

代碼實(shí)現(xiàn):

public class Singleton{

//對保存實(shí)例的變量添加volatile修飾

private volatile static Singleton instance = null;

private Singleton(){

    }

public static Singleton getInstance(){

//第一次檢查

if(instance == null){

//同步塊,線程安全的創(chuàng)建實(shí)例

synchronized (Singleton.class){

//第二次檢查

if(instance==null){

                    instance = new Singleton();

                }

            }

        }

return instance;

    }

}

這種方式即可以安全的創(chuàng)建線程,又不會(huì)對性能造成太大的影響。

餓漢式:所謂餓漢式也就是在類加載的時(shí)候直接new出一個(gè)對象來,不管以后用不用得到,是一種以空間換取時(shí)間的策略。代碼也非常簡單:

public class Singleton{

//定義一個(gè)變量來存儲(chǔ)創(chuàng)建好的實(shí)例,直接在這里創(chuàng)建實(shí)例,只能創(chuàng)建一次

//static變量在類加載時(shí)進(jìn)行初始化,并且只被初始化一次。

private static Singleton uniqueInstance = new Singleton();

//私有化構(gòu)造方法,可以在內(nèi)部控制創(chuàng)建實(shí)例的數(shù)目,防止在外部創(chuàng)建

private Siingleton(){

    }

//定義一個(gè)方法為客戶端提供類實(shí)例,方法上加static將該方法變?yōu)殪o態(tài)

//目的是不需要對象實(shí)例就可以在外部直接通過類來調(diào)用

public static Singleton getInstance(){

//直接使用已經(jīng)創(chuàng)建好的實(shí)例

return uniqueInstance;

    }

}

單例模式作用范圍:目前Java里面實(shí)現(xiàn)的單例是一個(gè)虛擬機(jī)的范圍,虛擬機(jī)在通過自己的`ClassLoader`裝載餓漢式實(shí)現(xiàn)的單例類時(shí)就會(huì)創(chuàng)建一個(gè)類實(shí)例。如果一個(gè)虛擬機(jī)中有多個(gè)類加載器或者一個(gè)機(jī)器中有多個(gè)虛擬機(jī),那么單例就不再起作用了。

單例模式優(yōu)缺點(diǎn):

1.節(jié)約內(nèi)存資源;

2.時(shí)間和空間:懶漢式是以時(shí)間換空間,餓漢式是以空間換時(shí)間;

3.線程安全:不加同步synchronized的懶漢式是線程不安全的,而餓漢式是線程安全的,因?yàn)樘摂M機(jī)只會(huì)裝載一次,并且在裝載的時(shí)候是不會(huì)發(fā)生并發(fā)的。加上synchronized和雙重檢查加鎖也能保證懶漢式的線程安全。

05

新的問題

由于小強(qiáng)工作很賣命,公司業(yè)績發(fā)展的不錯(cuò),老板決定再招一名程序員,應(yīng)聘者小華也是一個(gè)單身漢,老板也承諾會(huì)給他分配女朋友,單是問題來了,之前的GirlFriend只能new出一個(gè)對象,總不能讓小強(qiáng)和小華共用一個(gè)對象吧。于是要想辦法實(shí)現(xiàn)一個(gè)可以提供兩個(gè)實(shí)例的GirlFriend類。

其實(shí)思路很簡單,只需要通過Map來緩存實(shí)例即可,代碼如下:

import java.util.HashMap;

import java.util.Map;

public class GirlFriend {

private String name;

private Integer age;

private static int maxNumsOfGirlFriends = 2;//最大數(shù)量

private static int number = 1;//當(dāng)前編號

//定義一個(gè)變量來存儲(chǔ)創(chuàng)建好的類實(shí)例

private static Map girlFriendMap = new HashMap();

//私有化構(gòu)造方法,防止外部調(diào)用

private GirlFriend(String name, Integer age) {

this.name = name;

this.age = age;

    }

//定義一個(gè)方法為程序員類提供女朋友實(shí)例

public static GirlFriend getGirlFriend(){

        GirlFriend girlFriend = girlFriendMap.get(number+"");

if(girlFriend==null){

//new一個(gè)新實(shí)例,并放到map中,用number當(dāng)做key,實(shí)例是value

          girlFriend = new GirlFriend("小美",28);

          girlFriendMap.put(number+"",girlFriend);

        }

        number++;

if(number>maxNumsOfGirlFriends){

            number = 1;

        }

return girlFriend;

    }

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;

    }

}

程序員類進(jìn)行獲取女朋友實(shí)例,如下:

public class Programmer {

public static void main(String[] args){

       GirlFriend girlFriend1 = GirlFriend.getGirlFriend();

        System.out.println(girlFriend1);

        GirlFriend girlFriend2 = GirlFriend.getGirlFriend();

        System.out.println(girlFriend2);

        GirlFriend girlFriend3 = GirlFriend.getGirlFriend();

        System.out.println(girlFriend3);

        GirlFriend girlFriend4 = GirlFriend.getGirlFriend();

        System.out.println(girlFriend4);

    }

}

上面代碼獲取了四次,看看打印的結(jié)果如何:

GirlFriend@6e0be858

GirlFriend@61bbe9ba

GirlFriend@6e0be858

GirlFriend@61bbe9ba

可以看出,第一次和第三次是一樣的,第二次和第四次是一樣的,一共就只有兩個(gè)對象,解決了這個(gè)問題。但是如何判斷哪個(gè)女朋友實(shí)例是小強(qiáng)的哪個(gè)是小華的呢?一種簡單的方法是通過給獲取實(shí)例的函數(shù)getGirlFriend傳參,比如小強(qiáng)獲取的時(shí)候傳如number = 1,小華的number = 2。

06

相關(guān)擴(kuò)展

在Java中還用一種更好的單例實(shí)現(xiàn)方式,既能夠?qū)崿F(xiàn)延遲加載,又能夠?qū)崿F(xiàn)線程安全。這種解決方案被稱為Lazy initialization holder class模式,這個(gè)模式綜合使用了Java的類級內(nèi)部類和多線程缺省同步鎖的知識(shí),很巧妙地同時(shí)實(shí)現(xiàn)了延遲加載和線程安全。

類級內(nèi)部類:

1.類級內(nèi)部類指的是有static修飾的成員式內(nèi)部類。如果沒有static修飾的成員式內(nèi)部類被稱為對象級內(nèi)部類。

2.類級內(nèi)部類相當(dāng)于其外部類的static成分,它的對象與外部類對象間不存在依賴關(guān)系,因此可以直接創(chuàng)建。

3.類級內(nèi)部類中可以定義靜態(tài)方法。在靜態(tài)方法中能夠引用外部類中的靜態(tài)成員方法或者成員變量。

4.類級內(nèi)部類相當(dāng)于其外部類的成員,只有在第一次被使用的時(shí)候才會(huì)被裝載。

缺省同步鎖:在某些情況下,JVM已經(jīng)隱含地執(zhí)行了同步,不需要自己進(jìn)行同步控制了,這些情況包括:

1.由靜態(tài)初始化器初始化數(shù)據(jù)時(shí)。

2.訪問final字段時(shí)

3.在創(chuàng)建線程之前創(chuàng)建對象時(shí)

4.線程可以看見它將要處理的對象時(shí)。

思路:使用靜態(tài)初始化器的方式,由jvm保證線程安全。但是這樣就像餓漢式的實(shí)現(xiàn)方式了,浪費(fèi)一定的空間。采用類級內(nèi)部類,在這個(gè)類級內(nèi)部類里面創(chuàng)建對象實(shí)例,只要不使用這個(gè)類級內(nèi)部類,就不會(huì)創(chuàng)建實(shí)例對象。

代碼:

public class Singleton{

//類級內(nèi)部類,該內(nèi)部類的實(shí)例與外部類的實(shí)例沒有綁定關(guān)系,

// 而且只有被調(diào)用到時(shí)才會(huì)裝載,從而實(shí)現(xiàn)延遲加載

private static class SingletonHolder{

//靜態(tài)初始化器,由jvm保證線程安全

private static Singleton instance = new Singleton();

    }

private Singleton(){

    }

public static Singleton getInstance(){

return SingletonHolder.instance;

    }

}

當(dāng)getInstance方法第一次被調(diào)用的時(shí)候,它第一次讀取 SingletonHolder.instance導(dǎo)致SingletonHolder類得到初始化,從而創(chuàng)建Singleton實(shí)例。

枚舉實(shí)現(xiàn)單例:

1.Java的枚舉類型實(shí)質(zhì)上是功能齊全的類,因此可以有自己的屬性和方法。

2.Java枚舉類型的基本思想是通過共有的靜態(tài)fianl域?yàn)槊總€(gè)枚舉常量導(dǎo)出實(shí)例的類。

3.從某種角度將,枚舉是單例的泛型化,本質(zhì)上是單元素的枚舉。

代碼:

public enum Singleton{

    uniqueInstance;

//單例自己的操作函數(shù)

public void singletonOperation(){

//功能處理

    }

}

使用枚舉來實(shí)現(xiàn)單例控制更加簡潔,而且無償?shù)靥峁┝诵蛄谢臋C(jī)制,并由JVM從根本上提供保障,絕對防止多次實(shí)例化。

在Spring中,每個(gè)Bean默認(rèn)就是單例的,這樣的優(yōu)點(diǎn)是Spring容器可以管理這些Bean的生命周期,決定什么時(shí)候創(chuàng)建出來,什么時(shí)候銷毀,銷毀的時(shí)候如何處理等等。

使用單例模式需要注意的就是JVM的垃圾回收機(jī)制,如果我們的一個(gè)單例對象在內(nèi)存中長久不使用,JVM就認(rèn)為這個(gè)對象是一個(gè)垃圾,在CPU資源空閑的情況下該對象會(huì)被清理掉,下次再調(diào)用時(shí)就需要重新產(chǎn)生一個(gè)對象。

[我要糾錯(cuò)]
[ 編輯:王振袢 &發(fā)表于江蘇 ]
關(guān)鍵詞: 本文 授權(quán) 轉(zhuǎn)自 公眾 單例模式

來源:本文內(nèi)容搜集或轉(zhuǎn)自各大網(wǎng)絡(luò)平臺(tái),并已注明來源、出處,如果轉(zhuǎn)載侵犯您的版權(quán)或非授權(quán)發(fā)布,請聯(lián)系小編,我們會(huì)及時(shí)審核處理。
聲明:江蘇教育黃頁對文中觀點(diǎn)保持中立,對所包含內(nèi)容的準(zhǔn)確性、可靠性或者完整性不提供任何明示或暗示的保證,不對文章觀點(diǎn)負(fù)責(zé),僅作分享之用,文章版權(quán)及插圖屬于原作者。

點(diǎn)個(gè)贊
0
踩一腳
0

您在閱讀:女朋友:你能給我講講單例模式嗎?

Copyright©2013-2025 ?JSedu114 All Rights Reserved. 江蘇教育信息綜合發(fā)布查詢平臺(tái)保留所有權(quán)利

蘇公網(wǎng)安備32010402000125 蘇ICP備14051488號-3技術(shù)支持:南京博盛藍(lán)睿網(wǎng)絡(luò)科技有限公司

南京思必達(dá)教育科技有限公司版權(quán)所有   百度統(tǒng)計(jì)

主站蜘蛛池模板: 天天草草| 国产特黄一级毛片特黄 | 日韩国产在线观看 | 国产在线精品成人一区二区三区 | 免费国产综合视频在线看 | 草草视频在线 | 精品日韩二区三区精品视频 | 欧美日韩视频二区三区 | 99精品欧美一区 | 亚洲青青草 | 一级成人毛片免费观看 | 最新欧美精品一区二区三区 | 亚洲一区二区免费 | 成人免费网站 | 99ri在线视频网 | 日本高清三区 | 国产在线一区二区三区在线 | 骚色视频| 日韩国产欧美精品综合二区 | 看全黄大色黄大片老人做 | 中文字幕日韩欧美一区二区三区 | 好男人社区神马www在线观看 | 亚洲国产婷婷综合在线精品 | 一级片在线观看视频 | 国产在线欧美日韩一区二区 | 亚欧毛片基地国产毛片基地 | a级毛片免费高清视频 | 国产精品资源在线播放 | 久草视频免费 | 福利所导航导航导航导航 | 国产精品456 | 97国产伦子在线观看 | 福利久草 | 亚洲精品成人av在线 | 免费看黄的视频网站 | 国产成人精品视频在放 | 日本天堂在线播放 | 亚洲精品视频免费看 | 三国同人h文啪啪高黄 | 1204国产成人精品视频 | 2048国产精品原创综合在线 |
最熱文章
最新文章
  • 阿里云上云鉅惠,云產(chǎn)品享最低成本,有需要聯(lián)系,
  • 卡爾蔡司鏡片優(yōu)惠店,鏡片價(jià)格低
  • 蘋果原裝手機(jī)殼