Java 單例類詳解:從基礎到高級,掌握線程安全與高效設計

作為一名Java開發工程師,你一定對**單例模式(Singleton Pattern)**不陌生。它是23種經典設計模式中最簡單也是最常用的一種,用于確保一個類在整個應用程序中只有一個實例存在。

單例廣泛應用于系統配置、數據庫連接池、日志管理器、緩存服務等場景。本文將帶你全面掌握 Java中實現單例的多種方式、線程安全性、懶加載機制、反射攻擊防范、序列化處理以及在Spring中的應用


🧱 一、什么是單例模式?

單例模式(Singleton Pattern) 是一種創建型設計模式,其核心思想是:

? 確保一個類在整個程序運行期間只被初始化一次,并提供一個全局訪問點。

單例的核心特點:

特性描述
私有構造方法防止外部通過?new?創建實例
靜態私有實例指向自己唯一的實例對象
公共靜態獲取方法提供對外訪問該實例的方法

📦 二、單例的基本實現方式

1. 餓漢式(Eager Initialization)

public class Singleton {// 類加載時就初始化private static final Singleton INSTANCE = new Singleton();// 構造方法私有private Singleton() {}// 提供唯一訪問方法public static Singleton getInstance() {return INSTANCE;}
}

? 線程安全
?? 類加載即初始化,浪費資源(非懶加載)


2. 懶漢式(Lazy Initialization)

public class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

?? 非線程安全,在多線程下可能創建多個實例


3. 懶漢式 + 同步方法(線程安全)

public class Singleton {private static Singleton instance;private Singleton() {}public synchronized static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

? 線程安全
?? 性能差,每次調用都要加鎖


4. 雙重檢查鎖定(Double-Checked Lockin

public class Singleton {private static volatile Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

? 線程安全、懶加載、性能較好
? 必須使用 volatile 防止指令重排序


5. 靜態內部類(IoDH,Initialization on Demand Holder)

public class Singleton {private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}

? 線程安全
? 懶加載
? 推薦寫法之一


6. 枚舉(Enum)實現單例(《Effective Java》推薦)

public enum Singleton {INSTANCE;public void doSomething() {System.out.println("執行單例操作");}
}

調用方式:

Singleton.INSTANCE.doSomething();

? 線程安全
? 天然支持序列化/反序列化
? 防止反射攻擊
? 推薦寫法之一


🔐 三、單例的安全性問題

1. 如何防止反射破壞單例?

默認情況下,通過反射可以調用私有構造函數,從而創建多個實例。

解決辦法:添加構造函數檢測

private Singleton() {if (INSTANCE != null) {throw new RuntimeException("單例已被初始化");}
}

2. 如何防止序列化/反序列化破壞單例?

如果實現了 Serializable 接口,反序列化會生成新對象。

解決辦法:添加 readResolve() 方法

protected Object readResolve() {return INSTANCE;
}

🔄 四、單例模式的應用場景

場景示例
日志記錄器記錄整個系統的日志
數據庫連接池統一管理數據庫連接
緩存服務如本地緩存、Redis客戶端
配置管理器加載并讀取配置文件
Spring Bean默認就是單例作用域
線程池統一管理線程資源
ID生成器保證ID全局唯一

🧩 五、單例模式的優缺點

優點缺點
節省內存資源生命周期長,可能造成內存泄漏
提供全局訪問點違背單一職責原則(若邏輯復雜)
易于維護和控制不利于測試(依賴隱藏)
線程安全(部分實現)擴展困難(不符合開閉原則)

📦 六、Spring 中的單例 Bean

在Spring框架中,默認所有Bean都是單例的(@Scope("singleton")),但它的“單例”含義略有不同:

? 在Spring容器中,每個Bean定義只會有一個實例
? 與傳統單例不同的是,它不是JVM級別的單例,而是Spring上下文內的單例

示例

@Component
public class MyService {public void sayHello() {System.out.println("Hello from singleton bean");}
}

注入使用:

@RestController
public class MyController {@Autowiredprivate MyService myService;@GetMapping("/hello")public String hello() {myService.sayHello();return "OK";}
}

🚫 七、常見誤區與注意事項

錯誤正確做法
使用懶漢式未同步導致并發問題使用雙重檢查或靜態內部類
忘記?volatile?導致指令重排添加?volatile?關鍵字
忽略反射攻擊添加構造函數檢查
忽略序列化破壞添加?readResolve()?方法
將單例用于可變狀態應保持不可變性或加鎖處理
單例中包含大量業務邏輯應拆分職責,避免違反單一職責原則

📊 八、六種常見單例實現對比表

實現方式是否線程安全是否懶加載是否推薦
餓漢式???
懶漢式???
同步方法懶漢式???
雙重檢查鎖定???
靜態內部類???
枚舉?????(最佳實踐)

📎 九、附錄:單例相關工具與框架速查表

工具/框架用途
Lombok 的?@UtilityClass幫助構建無實例的工具類
Spring 的?@Component?/?@Service自動注冊為單例Bean
Guice / DaggerDI框架中的單例支持
MapStruct / Dozer單例工具類轉換數據
Jackson / Gson單例對象的序列化控制
Mockito單例測試時需使用 Spy 或注入方式
ThreadLocal若誤用可能導致偽單例問題
Flyweight 模式與單例類似,但允許多個共享實例

? 十、總結:Java 單例類關鍵知識點一覽表

內容說明
定義整個程序中僅允許存在一個實例
核心結構私有構造器 + 私有靜態實例 + 公共靜態訪問方法
實現方式餓漢式、懶漢式、雙重檢查、靜態內部類、枚舉
線程安全枚舉、雙重檢查、靜態內部類是線程安全的
懶加載懶漢式、雙重檢查、靜態內部類支持懶加載
Spring 中的單例容器內單例,非JVM級別
注意事項防止反射、序列化破壞,合理使用
推薦寫法枚舉 > 靜態內部類 > 雙重檢查

如果你正在準備一篇面向初學者的技術博客,或者希望系統回顧Java基礎知識,這篇文章將為你提供完整的知識體系和實用的編程技巧。

歡迎點贊、收藏、轉發,也歡迎留言交流你在實際項目中遇到的單例類相關問題。我們下期再見 👋

📌 關注我,獲取更多Java核心技術深度解析!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/88221.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/88221.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/88221.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

面向對象設計

你列出的這些屬于 C 高級開發中面向對象設計與架構設計的核心知識,也是面試高級工程師崗位必問的內容。下面我按順序,深入講解每一項概念、原理、用途,并穿插 C 示例。? 1. 設計原則(SOLID)SOLID 是面向對象設計的五大…

IntelliJ IDEA讓我的開發效率翻倍:從新手到高效開發者的進階之路

IntelliJ IDEA讓我的開發效率翻倍:從新手到高效開發者的進階之路 🌟 嗨,我是IRpickstars! 🌌 總有一行代碼,能點亮萬千星辰。 🔍 在技術的宇宙中,我愿做永不停歇的探索者。 ? 用…

css sprites使用

CSS Sprites 是一種將多個小圖標或背景圖像合并到一個大圖中的技術。通過減少HTTP請求次數,可以顯著提高頁面加載速度。其核心原理是:通過設置元素的背景圖(background-image)為這個大圖,然后調整背景位置(…

分布式爬蟲在電商平臺商品數據大規模采集中的技術應用

在電商平臺商品數據大規模采集場景中,分布式爬蟲憑借其高效、可擴展、抗風險的特性,成為突破單節點爬蟲性能瓶頸的核心技術方案。以下從技術架構、關鍵技術點、電商場景適配及挑戰應對四個維度,解析其具體應用:一、分布式爬蟲的核…

Linux的`if test`和`if [ ]中括號`的取反語法比較 筆記250709

Linux的if test和if 中括號的取反語法比較 筆記250709 Linux的 test命令(或等價中括號寫法 [空格expression空格])的用法詳解. 筆記250709 四種取反語法: if ! test -e xxx ;then... 和 if test ! -e xxx ;then... 和 if ! [ -e xxx ] ;then... 和 if …

記錄使用ubuntu16.04編譯aosp(android8.1與10)遇到的問題

一、前言: 本來打算用wsl來編譯AOSP,但是折騰了好幾天,以失敗告終。后來使用vmware反而成功了。 本篇同樣會把wsl遇到的問題與嘗試記錄下來。 環境:vmware ubuntu16.04。 為什么會使用ubuntu16.04呢,因為在公司有一…

hiredis window之RFDMap

簡介 RFDMap用于將socket分配映射成連續的文件描述符,同時管理回收的文件描述符,因為ae構架中管理fd與對應事件處理器使用的是數據,fd作為數組下標 結構 #mermaid-svg-zQz2LTrKRi0LQTII {font-family:"trebuchet ms",verdana,arial…

RustFS一款Rust 驅動的 高性能 分布式存儲系統

演示地址:https://play.rustfs.com/browser 訪問賬號(默認 rustfsadmin)。 訪問密鑰(默認 rustfsadmin)。 下載mc https://dl.min.io/client/mc/release可以直接在 Linux 系統上安裝 mc(,然后訪…

微軟 Bluetooth LE Explorer 實用工具的詳細使用分析

微軟 Bluetooth LE Explorer 實用工具的詳細使用分析 文章目錄 微軟 **Bluetooth LE Explorer** 實用工具的詳細使用分析1. **工具定位與核心功能**2. **關鍵特性與更新**3. **使用場景示例**4. **系統要求與依賴**5. **與專業工具對比**6. **局限性**7. **實踐建議**結論以下是…

centos 7.6安裝mysql8

在 CentOS 7.6 上安裝 MySQL 8.0.42 的步驟如下,基于搜索結果中的最新信息: 下載 MySQL 8.0.42 安裝包 https://dev.mysql.com/downloads/mysql/從 MySQL 官方網站下載 mysql-8.0.42-1.el7.x86_64.rpm-bundle.tar 文件: 官方下載地址&#xf…

CentOS7更換阿里云yum源

問題:剛剛在本地安裝了CentOS7虛擬機,使用yum安裝vim軟件時(最小化安裝只有vi沒有vim)出現下面的報錯原因 :CentOS7 已于2024-6-30停止維護,官方鏡像源已不可用,可以更換為阿里云鏡像源解決&…

UE5內置插件 AnimToTexture 簡單入門

開啟插件 首先安裝插件,然后重啟。打開顯示插件內容我們就可以找到插件自帶的轉換內容將骨骼網格體轉換為頂點動畫有兩種方式: 最簡單的記錄每個頂點的位置然后通過切換拾取顏色偏移實現記錄骨骼的變換,然后通過貼圖去修改骨骼位置計算 這兩種…

如何搭建Appium環境?

🍅 點擊文末小卡片,免費獲取軟件測試全套資料,資料在手,漲薪更快1、安裝Java Development Kit(JDK)前往Oracle官網下載JDK。在https://www.oracle.com/java/technologies/javase-jdk11-downloads.html 找到…

Android kotlin 協程的詳細使用指南

Android Kotlin 協程的詳細使用指南,結合核心概念、實戰場景和最佳實踐:一、協程基礎概念?協程本質?協程是輕量級線程,通過掛起/恢復機制實現并發,相比線程節省90%以上的內存開銷。其核心優勢在于結構化并發和掛起函數的協作式調…

什么是 AMR 格式?簡鹿音頻轉換器輕松批量轉換 AMR 為 MP3

AMR 格式是一種比較特殊但又常見的音頻格式,而MP3 格式則是大家耳熟能詳的通用音頻格式。那么,它們之間有什么區別?又該如何把 AMR 文件轉換成更常用的 MP3 呢?下面我們就來通俗地了解一下。一、什么是 AMR 格式?AMR&a…

C++11 std::move與std::move_backward深度解析

文章目錄移動語義的革命性意義std::move:正向范圍移動函數原型與核心功能關鍵特性與實現原理適用場景與代碼示例危險區域:重疊范圍的未定義行為std::move_backward:反向安全移動函數原型與核心功能關鍵特性與實現原理適用場景與代碼示例重疊范…

訂單初版—2.生單鏈路中的技術問題說明文檔

大綱1.生單鏈路的業務代碼2.生單鏈路中可能會出現數據不一致的問題3.Seata AT模式下的分布式事務的原理4.Seata AT模式下的分布式事務的讀寫隔離原理5.Seata AT模式下的死鎖問題以及超時機制6.Seata AT模式下的讀寫隔離機制的影響7.生單鏈路使用Seata AT模式的具體步驟8.生單鏈…

跨平臺ROS2視覺數據流:服務器運行IsaacSim+Foxglove本地可視化全攻略

任務目標 本教程將完整實現: 在服務器無頭模式下運行IsaacSim,并在本地顯示GUI界面 通過IsaacSim的ROS2 Bridge發布圖像數據 在本地Foxglove中實時可視化服務器端的ROS2數據流 實現步驟 1. 服務器無頭運行IsaacSim 本地GUI顯示 在服務器端執行&am…

【機器學習筆記Ⅰ】 8 多元梯度下降法

多元線性回歸的梯度下降法詳解 多元線性回歸(Multiple Linear Regression)是多個自變量(特征)與一個因變量(目標)之間的線性關系建模,梯度下降法用于優化模型參數(權重和偏置&#x…

C++——從結構體到類與對象

C 類與對象詳解:從結構體到面向對象編程C 的面向對象編程(OOP)核心是 類(Class) 和 對象(Object)。類是用戶自定義的數據類型,用于封裝數據(屬性)和操作數據的…