亚洲国产成人精品久久_欧美日韩国产综合一区二区_亚洲精品理论电影_色综合久久中文字幕

首頁 » 早鳥快報 » 咨詢 » 正文

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

放大字體  縮小字體 發布日期:2019-12-30  來源:來自互聯網  作者:來自互聯網  瀏覽次數:828
導讀

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

作者 | 靜幽水

責編 | Elle

問題背景

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

publicclassGirlFriend{

privateString name;

privateInteger age;

publicGirlFriend(String name, Integer age){

this.name = name;

this.age = age;

}

publicString getName{

returnname;

}

publicvoidsetName(String name){

this.name = name;

}

publicInteger getAge{

returnage;

}

publicvoidsetAge(Integer age){

this.age = age;

}

@Override

publicString toString{

return"GirlFriend{"+

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

", age="+ age +

'}';

}

}

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

publicclassProgrammer{

publicstaticvoidmain( String[] args) {

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

System. out.println(girlFriend.toString);

}

}

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

有何問題

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

publicclassProgrammer{

publicstaticvoidmain( String[] args) {

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

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

GirlFriend girlFriend3 = newGirlFriend( "小麗", 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}

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

解決方法

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

來看看代碼如何實現:

publicclassGirlFriend{

privateString name;

privateInteger age;

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

privatestaticGirlFriend girlFriend = null;

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

privateGirlFriend(String name, Integer age){

this.name = name;

this.age = age;

}

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

publicstaticGirlFriend getGirlFriend{

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

if(girlFriend== null){

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

girlFriend = newGirlFriend( "小美", 28);

}

returngirlFriend;

}

publicString getName{

returnname;

}

publicvoidsetName(String name){

this.name = name;

}

publicInteger getAge{

returnage;

}

publicvoidsetAge(Integer age){

this.age = age;

}

@Override

publicString toString{

return"GirlFriend{"+

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

", age="+ age +

'}';

}

}

主要的核心思想有三點:

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

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

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

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

publicclassProgrammer{

publicstaticvoidmain( String[] args) {

GirlFriend girlFriend = GirlFriend.getGirlFriend;

System. out.println(girlFriend.toString);

}

}

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

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

模式講解

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

通用類圖如下:

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

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

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

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

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

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

完整代碼:

publicclassSingleton{

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

privatestaticSingleton uniqueInstance = null;

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

privateSingleton{

}

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

publicstaticsynchronizedSingleton getInstance{

//判斷是否已經有實例

if(uniqueInstance == null){

uniqueInstance = newSingleton;

}

//有就直接用

returnuniqueInstance;

}

}

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

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

代碼實現:

publicclassSingleton{

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

privatevolatilestaticSingleton instance = null;

privateSingleton{

}

publicstaticSingleton getInstance{

//第一次檢查

if(instance == null){

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

synchronized(Singleton.class){

//第二次檢查

if(instance== null){

instance = newSingleton;

}

}

}

returninstance;

}

}

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

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

publicclassSingleton{

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

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

privatestaticSingleton uniqueInstance = newSingleton;

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

privateSiingleton{

}

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

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

publicstaticSingleton getInstance{

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

returnuniqueInstance;

}

}

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

單例模式優缺點:

1.節約內存資源;

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

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

新的問題

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

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

importjava.util.HashMap;

importjava.util.Map;

publicclassGirlFriend{

privateString name;

privateInteger age;

privatestaticintmaxNumsOfGirlFriends = 2; //最大數量

privatestaticintnumber = 1; //當前編號

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

privatestaticMap<String,GirlFriend> girlFriendMap = newHashMap<String, GirlFriend>;

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

privateGirlFriend(String name, Integer age){

this.name = name;

this.age = age;

}

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

publicstaticGirlFriend getGirlFriend{

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

if(girlFriend== null){

//new一個新實例,并放到map中,用number當做key,實例是value

girlFriend = newGirlFriend( "小美", 28);

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

}

number++;

if(number>maxNumsOfGirlFriends){

number = 1;

}

returngirlFriend;

}

publicString getName{

returnname;

}

publicvoidsetName(String name){

this.name = name;

}

publicInteger getAge{

returnage;

}

publicvoidsetAge(Integer age){

this.age = age;

}

}

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

publicclassProgrammer{

publicstaticvoidmain( 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。

相關擴展

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

類級內部類:

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

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

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

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

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

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

2.訪問final字段時

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

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

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

代碼:

publicclassSingleton{

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

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

privatestaticclassSingletonHolder{

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

privatestaticSingleton instance = newSingleton;

}

privateSingleton{

}

publicstaticSingleton getInstance{

returnSingletonHolder.instance;

}

}

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

枚舉實現單例:

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

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

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

代碼:

publicenumSingleton{

uniqueInstance;

//單例自己的操作函數

publicvoidsingletonOperation{

//功能處理

}

}

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

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

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

聲明:本文為作者投稿,版權歸作者個人所有。

 
 
免責聲明
本文為會員免費發布,僅代表發布者個人觀點,本站未對其內容進行核實,請讀者僅做參考,如若文中涉及有違公德、觸犯法律的內容,一經發現,立即刪除,作者需自行承擔相應責任。涉及到版權或其他問題,請及時聯系我們刪除處理。
 
 
主站蜘蛛池模板: 91精品国产91久久久久久吃药| 蜜臀久久99精品久久久酒店新书 | 欧美xxxx综合视频| 久久99久国产精品黄毛片入口| 91精品国产91久久久久| 精品国产中文字幕| 日本成人中文字幕在线| 国产激情视频一区| 国产美女精彩久久| 久久久久久成人精品| 色综合久久久久久中文网| 豆国产97在线| 国产精品久久久久久亚洲影视| 久久久国产在线视频| 日本欧美一二三区V| 日韩中文字幕三区| 亚洲成人午夜在线| 亚洲精品一区二区毛豆| 91精品国产亚洲| 国产精品久久久av| 久久99国产精品久久久久久久久| 欧美一级中文字幕| 人人妻人人澡人人爽欧美一区| 亚洲欧美国产不卡| 婷婷四房综合激情五月| 色天天综合狠狠色| 日本国产高清不卡| 久久亚洲国产精品成人av秋霞| 欧美一区二区三区精美影视 | 蜜桃视频一区二区在线观看| 欧美高清视频一区| 蜜桃视频成人在线观看| 欧美在线观看日本一区| 日韩欧美精品免费| 欧洲精品视频在线| 久久亚洲国产成人| 国产一区视频在线| 国产精品毛片一区视频| 成人精品网站在线观看| 一区二区在线观| 欧洲中文字幕国产精品|