單例模式
- 定義:確保一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。
- 使用場景:確保一個(gè)類有且只有一個(gè)對(duì)象的場景,避免產(chǎn)生多個(gè)對(duì)象消耗過多的資源,或者某種類型的對(duì)象只應(yīng)該有且只有一個(gè)。
- 關(guān)鍵點(diǎn):
- 構(gòu)造函數(shù)不對(duì)外開放,一般為 Private。
- 通過一個(gè)靜態(tài)方法或者枚舉返回單例類對(duì)象 。
- 確保單例類的對(duì)象有且只有一個(gè),尤其在多線程環(huán)境下。
- 確保單例類對(duì)象在反序列化時(shí)不會(huì)重新構(gòu)建對(duì)象。
- 實(shí)現(xiàn)方式:
- 懶漢模式:
- 定義:懶漢模式是聲明一個(gè)靜態(tài)對(duì)象,并且在用戶第一次調(diào)用個(gè)體Instance時(shí)進(jìn)行初始化。
- 代碼實(shí)現(xiàn):
public class SingleCode { //懶漢模式 private static SingleCode instance; private SingleCode(){} public static synchronized SingleCode getInstance(){ if(instance == null ) { instance = new SingleCode(); } return instance; } } - 優(yōu)點(diǎn):單例只有在使用時(shí)才會(huì)被實(shí)例化,在一定程度上節(jié)約資源。
- 缺點(diǎn):第一次加載時(shí)需要即使進(jìn)行實(shí)例化,反應(yīng)稍慢,最大的問題時(shí)每次調(diào)用getInstance都進(jìn)行同步,造成不必要的同步開銷。
- Double CheckLock(DCL)實(shí)現(xiàn)單利:
- 定義:DCL方式實(shí)現(xiàn)單例模式的優(yōu)點(diǎn)是既能夠在需要時(shí)才初始化單利,又能夠保證線程安全,且單例對(duì)象初始化后調(diào)用getInstance不進(jìn)行同步鎖;
- 代碼實(shí)現(xiàn):
public class SingleCode { //DCL 實(shí)現(xiàn)單例模式 private static SingleCode instance = null; private static Object obj = new Object(); private SingleCode(){} public static SingleCode getInstance(){ if(instance == null) { //判斷是否為空避免不需要的同步 synchronized (obj) { if(instance == null ) { //為了在null 的情況下創(chuàng)建實(shí)例 instance = new SingleCode(); } } } return instance; } } - 優(yōu)點(diǎn):資源利用率高,第一次執(zhí)行g(shù)etInstance時(shí)單例對(duì)象才會(huì)被實(shí)例化,效率高。
- 缺點(diǎn):第一次加載時(shí)反應(yīng)稍慢,也由于Java內(nèi)存模型的原因偶爾會(huì)失敗。
- 靜態(tài)內(nèi)部類單例模式:
- 代碼實(shí)現(xiàn):
public class SingleCode { //靜態(tài)內(nèi)部類實(shí)現(xiàn)單例模式 private SingleCode(){} public static SingleCode getInstance(){ return SingleHolder.instance; } /**靜態(tài)內(nèi)部類*/ private static class SingleHolder{ private static final SingleCode instance = new SingleCode(); } } - 優(yōu)點(diǎn):第一次調(diào)用getInstance方法會(huì)導(dǎo)致虛擬機(jī)加載SingleHolder類,這種方法不僅能夠確保線程安全,也能夠保證單例對(duì)象的唯一性,同時(shí)也延遲里單例的實(shí)例化。
- 推薦使用的單例模式實(shí)現(xiàn)方式。
- 代碼實(shí)現(xiàn):
- 枚舉單例:
- 代碼實(shí)現(xiàn):
public enum SingleEnum{ INSTANCE; public void doSomething(){ System.out.put("do sth."); } } - 優(yōu)點(diǎn):寫法簡單,枚舉實(shí)例的創(chuàng)建是線程安全的,并且任何情況下它都是一個(gè)單例。
- 代碼實(shí)現(xiàn):
- 使用容器實(shí)現(xiàn)單例模式:
- 代碼實(shí)現(xiàn):
public class SingleManager { private static Map<String,Object> objMap = new HashMap<String, Object>(); private SingleManager(){} public static void registerService(String key,Object instance){ if(!objMap.containsKey(key)) { objMap.put(key, instance); } } public static Object getService(String key) { return objMap.get(key); } } - 優(yōu)點(diǎn) 容器實(shí)現(xiàn)單例模式可以管理多種類型的單例,并且在使用時(shí)可以通過統(tǒng)一接口進(jìn)行獲取操作,降低用戶使用成本,有對(duì)用戶隱藏了具體實(shí)現(xiàn),降低了耦合度。
- 代碼實(shí)現(xiàn):
- 懶漢模式:
總結(jié)
- 優(yōu)點(diǎn):
- 由于單例模式在內(nèi)存中只有偶一個(gè)實(shí)例,減少內(nèi)存開支,特別是一個(gè)對(duì)象需要頻繁地創(chuàng)建、銷毀時(shí),而且創(chuàng)建或 銷毀時(shí)性能又無法優(yōu)化。
- 由于單例模式只生成一個(gè)實(shí)例,所以減少系統(tǒng)的性能開銷。
- 單例模式可以避免對(duì)資源的多重占用。
- 單例模式可以在系統(tǒng)設(shè)置全局的訪問點(diǎn),優(yōu)化和共享資源訪問。
- 缺點(diǎn):
- 單例模式一般沒有接口,擴(kuò)展很困難,若要擴(kuò)展除了修改代碼基本上沒有第二種途徑實(shí)現(xiàn)。
- 單例對(duì)象如果持有Context,那么很容引發(fā)內(nèi)存泄露,此時(shí)需要注意傳遞給單例對(duì)象的Context最好是Application Context。