Java單例模式的6種實(shí)現(xiàn)方式詳解
為什么使用單例模式
需要確保某個(gè)類只要一個(gè)對(duì)象,或創(chuàng)建一個(gè)類需要消耗的資源過多,如訪問IO和數(shù)據(jù)庫操作等,這時(shí)就需要考慮使用單例模式了。
使用單例模式需要注意的關(guān)鍵點(diǎn)
- 將構(gòu)造函數(shù)訪問修飾符設(shè)置為private
- 通過一個(gè)靜態(tài)方法或者枚舉返回單例類對(duì)象
- 確保單例類的對(duì)象有且只有一個(gè),特別是在多線程環(huán)境下
- 確保單例類對(duì)象在反序列化時(shí)不會(huì)重新構(gòu)建對(duì)象
單例模式的幾種寫法
1. 餓漢式
/** * 餓漢式實(shí)現(xiàn)單例模式 */ public class Singleton { private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } }
2. 懶漢式
/** * 懶漢式實(shí)現(xiàn)單例模式 */ public class Singleton { private static Singleton instance; private Singleton() { } // synchronized方法,多線程情況下保證單例對(duì)象唯一 public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
getInstance()方法中添加了synchronized關(guān)鍵字,使其變成一個(gè)同步方法,目的是為了在多線程環(huán)境下保證單例對(duì)象唯一。
優(yōu)點(diǎn): 只有在使用時(shí)才會(huì)實(shí)例化單例,一定程度上節(jié)約了資源。
缺點(diǎn): 第一次加載時(shí)要立即實(shí)例化,反應(yīng)稍慢。每次調(diào)用getInstance()方法都會(huì)進(jìn)行同步,這樣會(huì)消耗不必要的資源。這種模式一般不建議使用。
3. DCL(Double CheckLock)實(shí)現(xiàn)單例
/** * DCL實(shí)現(xiàn)單例模式 */ public class Singleton { private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { // 兩層判空,第一層是為了避免不必要的同步 // 第二層是為了在null的情況下創(chuàng)建實(shí)例 if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
優(yōu)點(diǎn): 資源利用率高,既能夠在需要的時(shí)候才初始化實(shí)例,又能保證線程安全,同時(shí)調(diào)用getInstance()方法不進(jìn)行同步鎖,效率高。
缺點(diǎn): 第一次加載時(shí)稍慢,由于Java內(nèi)存模型的原因偶爾會(huì)失敗。在高并發(fā)環(huán)境下也有一定的缺陷,雖然發(fā)生概率很小。
DCL模式是使用最多的單例模式實(shí)現(xiàn)方式,除非代碼在并發(fā)場(chǎng)景比較復(fù)雜或者JDK 6以下版本使用,否則,這種方式基本都能滿足需求。
4. 靜態(tài)內(nèi)部類
/** * 靜態(tài)內(nèi)部類實(shí)現(xiàn)單例模式 */ public class Singleton { private Singleton() { } public static Singleton getInstance() { return SingletonHolder.instance; } /** * 靜態(tài)內(nèi)部類 */ private static class SingletonHolder { private static Singleton instance = new Singleton(); } }
第一次加載Singleton類時(shí)不會(huì)初始化instance,只有在第一次調(diào)用getInstance()方法時(shí),虛擬機(jī)會(huì)加載SingletonHolder類,初始化instance。
這方式既保證線程安全,單例對(duì)象的唯一,也延遲了單例的初始化,推薦使用這種方式來實(shí)現(xiàn)單例模式。
5. 枚舉單例
/** * 枚舉實(shí)現(xiàn)單例模式 */ public enum SingletonEnum { INSTANCE; public void doSomething() { System.out.println("do something"); } }
默認(rèn)枚舉實(shí)例的創(chuàng)建是線程安全的,即使反序列化也不會(huì)生成新的實(shí)例,任何情況下都是一個(gè)單例。
優(yōu)點(diǎn): 簡(jiǎn)單!
6. 容器實(shí)現(xiàn)單例
import java.util.HashMap; import java.util.Map; /** * 容器類實(shí)現(xiàn)單例模式 */ public class SingletonManager { private static Map<String, Object> objMap = new HashMap<String, Object>(); public static void regsiterService(String key, Object instance) { if (!objMap.containsKey(key)) { objMap.put(key, instance); } } public static Object getService(String key) { return objMap.get(key); } }
SingletonManager可以管理多個(gè)單例類型,使用時(shí)根據(jù)key獲取對(duì)象對(duì)應(yīng)類型的對(duì)象。這種方式可以通過統(tǒng)一的接口獲取操作,隱藏了具體實(shí)現(xiàn),降低了耦合度。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Java使用TCP協(xié)議發(fā)送和接收數(shù)據(jù)方式
這篇文章詳細(xì)介紹了Java中使用TCP進(jìn)行數(shù)據(jù)傳輸?shù)牟襟E,包括創(chuàng)建Socket對(duì)象、獲取輸入輸出流、讀寫數(shù)據(jù)以及釋放資源,通過兩個(gè)示例代碼TCPTest01.java和TCPTest02.java,展示了如何在客戶端和服務(wù)器端進(jìn)行數(shù)據(jù)交換2024-12-12Java中Object toString方法簡(jiǎn)介_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
Object類在Java里面是一個(gè)比較特殊的類,JAVA為了組織這個(gè)類組織得比較方便,它提供了一個(gè)最根上的類,相當(dāng)于所有的類都是從這個(gè)類繼承,這個(gè)類就叫Object。接下來通過本文給大家介紹Object toString方法,需要的的朋友參考下吧2017-05-05Java文件處理之使用itextpdf實(shí)現(xiàn)excel轉(zhuǎn)pdf
在文件處理中,經(jīng)常有文件類型轉(zhuǎn)換的使用場(chǎng)景,本文主要介紹了如何使用poi以及itextpdf完成excel轉(zhuǎn)pdf的操作,需要的小伙伴可以參考一下2024-02-02SpringBoot同一接口多個(gè)實(shí)現(xiàn)類配置的實(shí)例詳解
這篇文章主要介紹了SpringBoot同一接口多個(gè)實(shí)現(xiàn)類配置的實(shí)例詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11java結(jié)束當(dāng)前循環(huán)常用代碼
在?Java中,當(dāng)我們要結(jié)束一個(gè)循環(huán)時(shí),通常會(huì)使用循環(huán)變量的實(shí)現(xiàn)類來結(jié)束,但在實(shí)際開發(fā)中,我們經(jīng)常會(huì)遇到某個(gè)循環(huán)結(jié)束后需要進(jìn)行其他的操作的情況,在本文中給大家分享java結(jié)束當(dāng)前循環(huán)常用代碼,感興趣的朋友跟隨小編一起看看吧2023-06-06