戲說設計模式 只有一個中國 單例模式
戲說設計模式 - 單例模式
[toc]
簡介
Singleton
保證類有且僅有一個實例,並提供一個訪問它的全局站點
類型
餓漢模式
懶漢模式
懶漢模式 PK 餓漢模式
優缺點
優點
由於單例模式在內存中只有一個實例,
減少了內存開支
,特別是一個對象需要頻繁地創建、銷毀時,而且創建或銷毀時性能又無法優化,單例模式的優勢就非常明顯。
由於單例模式只生成一個實例,所以
減少了系統的性能開銷
,當一個對象的產生需要比較多的資源時,如讀取配置、產生其他依賴對象時,則可以通過在應用啟動時直接產生一個單例對象,然後用永久駐留內存的方式來解決(在Java EE中採用單例模式時需要注意JVM垃圾回收機制)。
單例模式可以
避免對資源的多重佔用
,例如一個寫文件動作,由於只有一個實例存在內存中,避免對同一個資源文件的同時寫操作。
單例模式可以在系統設置全局的訪問點,
優化和共享資源訪問
,例如可以設計一個單例類,負責所有數據表的映射處理。
缺點
單例模式一般沒有介面,
擴展很困難
,若要擴展,除了修改代碼基本上沒有第二種途徑可以實現。單例模式為什麼不能增加介面呢?因為介面對單例模式是沒有任何意義的,它要求「自行實例化」,並且提供單一實例、介面或抽象類是不可能被實例化的。當然,在特殊情況下,單例模式可以實現介面、被繼承等,需要在系統開發中根據環境判斷。
單例模式
對測試是不利的
。在並行開發環境中,如果單例模式沒有完成,是不能進行測試的,沒有介面也不能使用mock的方式虛擬一個對象。
單例模式
與單一職責原則有衝突
。一個類應該只實現一個邏輯,而不關心它是否是單例的,是不是要單例取決於環境,單例模式把「要單例」和業務邏輯融合在一個類中。
適用場景
應用程序中的該對象有且只有一個即可
創建一個對象需要消耗的資源過多,如要訪問IO和資料庫等資源;
需要定義大量的靜態常量和靜態方法(如工具類)的環境,可以採用單例模式(當然,也可以直接聲明為static的方式)。
注意事項
在高並發情況下,請注意單例模式的線程同步問題。
案例
只有一個中國
小鳥:「該死的日本鬼子,...」
大牛:「小鳥,你突然罵日本人幹啥?」
小鳥:「連一個懷孕六個月的孕婦都不放過,居然將刺刀插進孕婦的腹部,挑出一個還沒出世的孩子,好好的一個南京城,硬是被他們弄的血流成河.」
大牛:「你在看《南京大屠殺》吧!在那個貧窮積弱的年代,又何止是一個南京,整個華夏大地都被他們弄的烏煙瘴氣,幸好,咱們的前輩們站起來了,打贏了那一場硬仗。」
小鳥:「是啊!勝利來之不易,所以我們要堅持只有一個中國的原則。到現在了,居然還有人鬧藏獨,台獨,真想化身成歐特曼,鋼鐵俠,誰敢不服,誰敢鬧分裂,就揍誰!」
大牛:「哈哈,想不到我們小鳥還是一個這麼有血性的人,不過大象是不會去計較螞蟻的挑釁的。」
大牛:「香港回歸,你能用程序實現嗎?」
小鳥:「我編寫一個中國類,定義一個城市返回的方法,這裡定義為帶參的(參數:城市),因為我們都知道,香港回歸後,有澳門回歸,我更期待不久的將來,由台灣回歸.這是事實的角度出發,同時也是從系統的擴展性出發」
小鳥:「客戶端調用,香港回歸」
China china = new China(); china.returnChina("香港");
大牛:「小鳥,說的不錯,擴展性你已經考慮到了,但是就像你前面說的勝利來之不易,我們要堅持只有一個中國的原則,而你這裡有體現沒?」
小鳥:「沒有,我的中國類是new出來的,如果我不new的話,我就沒法創建對象了啊」
大牛:「你可以將它的構造方法私有化啊。」
懶漢模式實現
私有化構造方法
:限制產生多個對象
小鳥:「
構造方法私有化
,是將構造方法的修飾符改成private嗎?」
private China() { }
大牛:「是的。」
創建類的唯一實例
:創建中國類的唯一實例,並使用private static 修飾
小鳥:「這樣改了,我外部就沒法實例化了哦,不要說台灣回歸,就是已經回歸了的香港,澳門,都回歸不了啊?」
大牛:「private的作用域是?」
小鳥:「在其類的內部是可以使用的,我明白了,你是說讓我在中國類裡面聲明一個自己的對象,然後通過get方法讓它自行實例化.同時也是為了提供給外部使用」
大牛:「不錯不錯,一點就通,不過能將代碼寫出來,才表示真正理解了哦!」
小鳥:「這還不容易」
單例對象:China
類中其它方法盡量是static
public class China { // 自行實例化 private static China china; private China() { } // 返回我自己的對象 public static China getChina() { if (china == null) { china = new China(); } return china; } public void returnChina(String city) { System.out.println(city + "回歸"); } }
客戶端
China china = China.getChina(); china.returnChina("香港");
大牛:「不錯,這裡還有一點需要知道的就是,
類中的其它方法,盡量是static
」
小鳥:「ok,我馬上把returnChina改成static修飾.」
餓漢模式實現
大牛:「這裡還有一個問題需要說明一下,就是關於
並發
,會導致線程不安全。」
小鳥:「並發?」
大牛:「換句話說吧!假設我們的香港和澳門是同一天同一時刻回歸的,那麼也就代表會同時去訪問China類 ---> 由於開始並沒有任何城市訪問過China類,所以會導致它們都進入getChina()方法中的if語句裡面 ---> 這樣的話就會造成我們的china會new兩次。」
小鳥:「我明白了,我們這個單例模式,就是為了保證整個應用程序中有且只有一個該對象,像開始那種低並發的情況,我那樣寫是沒什麼問題的,但是如果要考慮高並發的話,就不能這麼寫了,如果不這麼寫,我又該如何寫呢?」
大牛:「不錯我們的小鳥還挺善於總結的,關於並發導致線程不安全,我們這裡有幾種解決方案。」
第1種:synchronized
在getChina方法前加synchronize關鍵字
synchronize作用:用於解決線程同步的問題, eg:A,B同時訪問方法c, 如果不加synchronize,則同時訪問,如果加了B會等A訪問完在進行訪問
大牛:「第1種,就是在getChina方法前加synchronized,它的作用就是香港,澳門同一時刻回歸時,它們種任意一個獲取完中國對象,另一個才會去獲取對象,也就解決了會同時產生兩個中國對象的問題.」
public static synchronized China getChina() { if (china == null) { china = new China(); } return china; }
第2種:雙重鎖定
Double-Check Locking
小鳥:「如果是這樣的話,確實是能解決同時產生兩個中國對象的問題,但是每次獲取China對象時,都會進行鎖定,這樣不好吧!會影響性能吧?」
大牛:「確實,分析的不錯,所以這裡我就給你講一下第2種實現方式:雙重鎖定,具體代碼如下:」
public static China getChina() { if (china == null) { synchronized (new Object()) { if (china == null) { china = new China(); } } } return china; }
大牛:「我們這裡在加鎖前面進行了一次判斷,所以就解決了我們每次都要進行加鎖操作的問題,我們再加鎖的代碼塊中再次進行判斷,就解決了我們會產生兩個對象的問題.」
第3種:餓漢模式
小鳥:「如果是這樣的話,確實是能解決同時產生兩個中國對象的問題,但是我們假設當前第1個回歸的是香港,香港正在獲取中國這個對象,但出了意外,大不列顛及北愛爾蘭聯合王國政府欠揍,突然反悔,那麼我們的香港就會一直處於查找中國的階段,由於我們加了synchronized關鍵字,它會解決並發的問題,對線程進行一個排序,從而也就導致我們的香港如果不回歸的話,我們的澳門也就不能回歸了,這樣是不是太虧了啊!」
大牛:「分析的不錯,確實會有這個問題,所以我們這裡推薦第3種實現方式,餓漢模式,即在類進行裝載的時候,就對其進行實例化.」
public class China { // 自行實例化 private static final China china = new China(); private China() { } // 返回我自己的對象 public static China getChina() { return china; } // 類中其它方法,建議用static修飾 public static void returnChina(String city) { System.out.println(city + "回歸"); } }
小鳥:「這樣就完美了!」
大牛:「非也,非也,我們這3種方式都是從線程安全的角度來考慮,第1種:線程安全,但是嚴重影響性能;第2種:線程安全,性能優於第1種;第3種:線程安全,性能優於前面兩種,但是由於類裝載的時候,就進行了唯一實例的初始化,會導致內存的浪費。」
小鳥:「那我採用哪種比較優呢?」
大牛:「我們一般是採用第3種,內存浪費點就浪費點,現在設備都不差這點內存,最主要的是運行時,由於一開始已經載入,所以運行速度會快一點。」
※java學習 JavaScript學習心得
※高級java程序員必備的面試官常問的知識
※Spring MVC異常處理詳解
※Java 小白比較關心的幾個問題
TAG:java學習吧 |
※一個有趣的鉤針模式
※單例模式的特點
※遊戲玩了二十年,只有兩種模式,而這種新模式讓虛擬遊戲堪比現實
※單例模式介紹
※除了故事模式,《靈魂能力6》還包含另一個單人模式
※設計模式概念
※共享工作模式這個噱頭,了解一下
※穿越火線裡面一直存在這樣的一個模式,但大多數人沒辦法玩!
※契約模式不管其中一個英雄選誰,另外一個是這五個之一就能贏!
※這是買手店設計,更是一種商業模式!
※介紹一種訓練模式——IFT模型
※24種設計模式及案例
※刺激戰場:玩膩了經典模式,你有沒有玩過這幾種模式!
※圖層調色法,調色的另一種模式,這個教程就兩個字:簡單
※一切的婚外情,只有這一種模式
※一個新的模式你一定不要錯過
※第三方影像中心是一個好的商業模式嗎?
※最強設計模式全攻略,看這一篇文章就夠了
※來了解一下單反的幾種模式
※設計模式之代理模式