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

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

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

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

本文經授權轉自公眾號 程序員修煉(ID:lixing2457)

作者:靜幽水 

如若轉載請聯系原公眾號

01

問題背景

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

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 +

'}';

    }

}

接著程序員小強就可以new出來一個女朋友的實例了,只需要傳進去姓名和年齡就可以了,如下:

public class Programmer {

public static void main(String[] args){

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

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

    }

}

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

02

有何問題

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

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());

    }

}

打印結果如下:

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

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

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

但是不久就被老板發現了,因為內存中存在多個女朋友實例對象,嚴重浪費了公司的資源,老板決定只能給小強分配一個女朋友,老板絞盡腦汁,終于想出了應對方法。

03

解決方法

老板發現,問題的根源就是不能把創造女朋友的權限交給小強,應該給他一個創造好的對象,并且姓名和年齡也不能由小強來決定,不然他肯定只要18歲的。而是要把創建實例的權限收回,讓類自身負責自己類實例的創建。

來看看代碼如何實現:

public class GirlFriend {

private String name;

private Integer age;

//定義一個變量來存儲創建好的類實例

private static GirlFriend girlFriend = null;

//私有化構造方法,防止外部調用

private GirlFriend(String name, Integer age) {

this.name = name;

this.age = age;

    }

//定義一個方法為程序員類提供女朋友實例

public static GirlFriend getGirlFriend(){

//判斷存儲實例是否為空

if(girlFriend==null){

//如果沒值,就new出一個實例,并賦值給存儲實例的變量

          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 +

'}';

    }

}

主要的核心思想有三點:

1.定義一個變量來存儲創建好的類實例;

2.私有化構造函數,防止外部new該對象;

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

程序員小強該如何獲取呢:

public class Programmer {

public static void main(String[] args){

       GirlFriend girlFriend = GirlFriend.getGirlFriend();

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

    }

}

直接使用GirlFriend類調用獲取對象的getGirlFriend方法獲取到實例對象,打印結果如下:

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

04

模式講解

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

通用類圖如下:

Singleton:負責創建單例類自己的唯一實例,并提供一個getInstance的方法讓外部來訪問這個類的唯一實例。

單例模式功能:保證類在運行期間只允許被創建一個實例。有懶漢式和餓漢式兩種實現方式。

懶漢式:上面的代碼就是懶漢式的實現方式,顧名思義,懶漢式指只有當該實例被使用到的時候才會創建,通過三個步驟就可以實現懶漢式:

1.私有化構造方法:防止外部使用。

2.提供獲取實例的方法:全局唯一的類實例訪問點。

3.把獲取實例的方法改為靜態:因為只有靜態的方法才能直接通過類名來調用,否則就要通過實例調用,這就陷入了死循環。

完整代碼:

public class Singleton{

//定義變量存放創建好的實例,因為要在靜態方法中使用,所以變量也必須是靜態的

private static Singleton uniqueInstance = null;

//私有化構造方法,可以在內部控制創建實例的數目

private Singleton(){

    }

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

public static synchronized Singleton getInstance(){

//判斷是否已經有實例

if(uniqueInstance == null){

            uniqueInstance = new Singleton();

        }

//有就直接用

return uniqueInstance;

    }

}

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

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

代碼實現:

public class Singleton{

//對保存實例的變量添加volatile修飾

private volatile static Singleton instance = null;

private Singleton(){

    }

public static Singleton getInstance(){

//第一次檢查

if(instance == null){

//同步塊,線程安全的創建實例

synchronized (Singleton.class){

//第二次檢查

if(instance==null){

                    instance = new Singleton();

                }

            }

        }

return instance;

    }

}

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

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

public class Singleton{

//定義一個變量來存儲創建好的實例,直接在這里創建實例,只能創建一次

//static變量在類加載時進行初始化,并且只被初始化一次。

private static Singleton uniqueInstance = new Singleton();

//私有化構造方法,可以在內部控制創建實例的數目,防止在外部創建

private Siingleton(){

    }

//定義一個方法為客戶端提供類實例,方法上加static將該方法變為靜態

//目的是不需要對象實例就可以在外部直接通過類來調用

public static Singleton getInstance(){

//直接使用已經創建好的實例

return uniqueInstance;

    }

}

單例模式作用范圍:目前Java里面實現的單例是一個虛擬機的范圍,虛擬機在通過自己的`ClassLoader`裝載餓漢式實現的單例類時就會創建一個類實例。如果一個虛擬機中有多個類加載器或者一個機器中有多個虛擬機,那么單例就不再起作用了。

單例模式優缺點:

1.節約內存資源;

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

3.線程安全:不加同步synchronized的懶漢式是線程不安全的,而餓漢式是線程安全的,因為虛擬機只會裝載一次,并且在裝載的時候是不會發生并發的。加上synchronized和雙重檢查加鎖也能保證懶漢式的線程安全。

05

新的問題

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

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

import java.util.HashMap;

import java.util.Map;

public class GirlFriend {

private String name;

private Integer age;

private static int maxNumsOfGirlFriends = 2;//最大數量

private static int number = 1;//當前編號

//定義一個變量來存儲創建好的類實例

private static Map girlFriendMap = new HashMap();

//私有化構造方法,防止外部調用

private GirlFriend(String name, Integer age) {

this.name = name;

this.age = age;

    }

//定義一個方法為程序員類提供女朋友實例

public static GirlFriend getGirlFriend(){

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

if(girlFriend==null){

//new一個新實例,并放到map中,用number當做key,實例是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;

    }

}

程序員類進行獲取女朋友實例,如下:

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);

    }

}

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

GirlFriend@6e0be858

GirlFriend@61bbe9ba

GirlFriend@6e0be858

GirlFriend@61bbe9ba

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

06

相關擴展

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

類級內部類:

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

2.類級內部類相當于其外部類的static成分,它的對象與外部類對象間不存在依賴關系,因此可以直接創建。

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

4.類級內部類相當于其外部類的成員,只有在第一次被使用的時候才會被裝載。

缺省同步鎖:在某些情況下,JVM已經隱含地執行了同步,不需要自己進行同步控制了,這些情況包括:

1.由靜態初始化器初始化數據時。

2.訪問final字段時

3.在創建線程之前創建對象時

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

思路:使用靜態初始化器的方式,由jvm保證線程安全。但是這樣就像餓漢式的實現方式了,浪費一定的空間。采用類級內部類,在這個類級內部類里面創建對象實例,只要不使用這個類級內部類,就不會創建實例對象。

代碼:

public class Singleton{

//類級內部類,該內部類的實例與外部類的實例沒有綁定關系,

// 而且只有被調用到時才會裝載,從而實現延遲加載

private static class SingletonHolder{

//靜態初始化器,由jvm保證線程安全

private static Singleton instance = new Singleton();

    }

private Singleton(){

    }

public static Singleton getInstance(){

return SingletonHolder.instance;

    }

}

當getInstance方法第一次被調用的時候,它第一次讀取 SingletonHolder.instance導致SingletonHolder類得到初始化,從而創建Singleton實例。

枚舉實現單例:

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

2.Java枚舉類型的基本思想是通過共有的靜態fianl域為每個枚舉常量導出實例的類。

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

代碼:

public enum Singleton{

    uniqueInstance;

//單例自己的操作函數

public void singletonOperation(){

//功能處理

    }

}

使用枚舉來實現單例控制更加簡潔,而且無償地提供了序列化的機制,并由JVM從根本上提供保障,絕對防止多次實例化。

在Spring中,每個Bean默認就是單例的,這樣的優點是Spring容器可以管理這些Bean的生命周期,決定什么時候創建出來,什么時候銷毀,銷毀的時候如何處理等等。

使用單例模式需要注意的就是JVM的垃圾回收機制,如果我們的一個單例對象在內存中長久不使用,JVM就認為這個對象是一個垃圾,在CPU資源空閑的情況下該對象會被清理掉,下次再調用時就需要重新產生一個對象。

[我要糾錯]
[編輯:王振袢 &發表于江蘇]
關鍵詞: 本文 授權 轉自 公眾 單例模式

來源:本文內容搜集或轉自各大網絡平臺,并已注明來源、出處,如果轉載侵犯您的版權或非授權發布,請聯系小編,我們會及時審核處理。
聲明:江蘇教育黃頁對文中觀點保持中立,對所包含內容的準確性、可靠性或者完整性不提供任何明示或暗示的保證,不對文章觀點負責,僅作分享之用,文章版權及插圖屬于原作者。

點個贊
0
踩一腳
0

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

Copyright©2013-2025 ?JSedu114 All Rights Reserved. 江蘇教育信息綜合發布查詢平臺保留所有權利

蘇公網安備32010402000125 蘇ICP備14051488號-3技術支持:南京博盛藍睿網絡科技有限公司

南京思必達教育科技有限公司版權所有   百度統計

主站蜘蛛池模板: a级特黄一级毛片七仙女思春 | 欧美一区二区日韩一区二区 | 美女日批视频在线观看 | 国产大学生一级毛片绿象 | 亚洲人成图片小说网站 | a级大片免费观看 | 黄网站在线观看 | 欧美一级视频在线观看欧美 | 国产精品13页 | 国产成人精品一区二区视频 | 日韩美一区二区 | 波多野结衣中文字幕在线播放 | 57pao国产成视频免费播放 | 日本中文字幕视频 | 97国产免费全部免费观看 | 成年美女黄网站色大片免费看 | 免费观看欧美一区二区三区 | 国产成人精品久久一区二区三区 | 亚洲一区二区三区首页 | 黄色成人在线播放 | 一级韩国aa毛片免费观看 | 午夜羞羞视频在线观看 | 欧美人善交vides0 | 日本三级黄在线观看 | 成人免费专区 | 色中色在线视频 | 亚洲福利在线观看 | 日韩色视频一区二区三区亚洲 | 羞羞视频网站免费 | 国产一级一片免费播放i | 国产精品久久二区三区色裕 | 亚洲精品高清在线 | 欧美最新的精品videoss | 男女扒开双腿猛进入免费看污 | 欧美一卡二卡科技有限公司 | 老司机成人免费精品视频 | eeuss免费鲁丝片 | 午夜性刺激在线观看视频 | 波多野结衣四虎精品影库 | 在线观看国产欧美 | 青春久草 |
最熱文章
最新文章
  • 阿里云上云鉅惠,云產品享最低成本,有需要聯系,
  • 卡爾蔡司鏡片優惠店,鏡片價格低
  • 蘋果原裝手機殼