詳解Java單例模式的實現(xiàn)與原理剖析
一、什么是單例模式
單例模式(Singleton Pattern)是 Java 中最簡單的設(shè)計模式之一。這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。
這種模式涉及到一個單一的類,該類負(fù)責(zé)創(chuàng)建自己的對象,同時確保只有單個對象被創(chuàng)建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
注意
- 單例模式只能由一個實例對象
- 單例類必須自己創(chuàng)建自己的唯一實例。
- 單例類必須給所有其他對象提供這一實例。
二、哪些地方用到了單例模式
單例模式經(jīng)常用在需要一個實例的程序中,例如
1.Spring框架IOC容器就使用到了單例模式,默認(rèn)創(chuàng)建對象的時候為單例模式
2.ResultBean 后端統(tǒng)一返回給前端的封裝類,這個在項目中是唯一的,只用一個對象進行返回JSON給前端進行渲染
JDK中也有單例模式的身影,例
- Runtime 體現(xiàn)了餓漢式單例
- Console 體現(xiàn)了雙檢鎖懶漢式單例
- Collections 中的 EmptyNavigableSet 內(nèi)部類懶漢式單例
- ReverseComparator.REVERSE_ORDER 內(nèi)部類懶漢式單例
- Comparators.NaturalOrderComparator.INSTANCE 枚舉餓漢式單例
三、單例模式的優(yōu)缺點
優(yōu)點
提供了對唯一實例的訪問
可以節(jié)約系統(tǒng)資源,提高系統(tǒng)的性能,減少不必要的內(nèi)存開銷
允許可變數(shù)目的實例(多例類)
缺點
擴展困難(缺少抽象層)
單例類的職責(zé)過重
由于自動垃圾回收機制,可能會導(dǎo)致共享的單例對象的狀態(tài)丟失
四、手寫單例模式
餓漢式
package com.wanshi.single;
//餓漢式單例
public class Hungry {
//會造成資源浪費,占用CPU
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private byte[] data4 = new byte[1024*1024];
private Hungry() {
System.out.println("Hungry init...");
}
private static Hungry hungry = new Hungry();
public static Hungry getInstance() {
return hungry;
}
}
class Test {
public static void main(String[] args) {
Hungry hungry = Hungry.getInstance();
Hungry hungry2 = Hungry.getInstance();
System.out.println(hungry);
System.out.println(hungry2);
}
}
枚舉餓漢式
package com.wanshi.single;
import java.lang.reflect.Constructor;
// enum 是一個class類
public enum EnumSingle {
INSTANCE;
public static EnumSingle getInstance() {
return INSTANCE;
}
}
class Test {
public static void main(String[] args) throws Exception{
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
在這里自行下載jad編譯工具即可

枚舉類最后反編譯源碼
jad工具反編譯
jad -sjava EnumSingle.class
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumSingle.java
package com.wanshi.single;
public final class EnumSingle extends Enum
{
public static EnumSingle[] values()
{
return (EnumSingle[])$VALUES.clone();
}
public static EnumSingle valueOf(String name)
{
return (EnumSingle)Enum.valueOf(com/wanshi/single/EnumSingle, name);
}
private EnumSingle(String s, int i)
{
super(s, i);
}
public static EnumSingle getInstance()
{
return INSTANCE;
}
public static final EnumSingle INSTANCE;
private static final EnumSingle $VALUES[];
static
{
INSTANCE = new EnumSingle("INSTANCE", 0);
$VALUES = (new EnumSingle[] {
INSTANCE
});
}
}
DCL懶漢式
package com.wanshi.single;
public class Lazy {
private static Lazy lazy;
public static Lazy getInterface() {
synchronized (Lazy.class) {
if (lazy == null) {
lazy = new Lazy();
}
}
return lazy;
}
public static void main(String[] args) {
Lazy lazy = Lazy.getInterface();
Lazy lazy2 = Lazy.getInterface();
System.out.println(lazy);
System.out.println(lazy2);
}
}
雙檢鎖懶漢式
package com.wanshi.single;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class LazyMan {
private static boolean flag = false;
private LazyMan() {
synchronized (this) {
if (!flag) {
flag = true;
} else {
throw new RuntimeException("不要試圖通過反射破壞對象");
}
}
}
private volatile static LazyMan lazyMan;
//雙重檢查鎖,懶漢式(DCL懶漢式)
public static LazyMan getInstance() {
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
//不是原子性操作,1.分配內(nèi)存空間,2.執(zhí)行構(gòu)造方法,3.把對象指向這個空間 指令重排可能會發(fā)生 加上volatile關(guān)閉指令重排
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
public static void main(String[] args) throws Exception {
// LazyMan lazyMan1 = LazyMan.getInstance();
Field flag = LazyMan.class.getDeclaredField("flag");
flag.setAccessible(true);
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan lazyMan1 = declaredConstructor.newInstance();
flag.set(lazyMan1, false);
LazyMan lazyMan2 = declaredConstructor.newInstance();
System.out.println(lazyMan1);
System.out.println(lazyMan2);
}
}
為什么要使用 volatile 關(guān)鍵字呢
不是原子性操作
1.分配內(nèi)存空間
2.執(zhí)行構(gòu)造方法
3.把對象指向這個空間
指令重排可能會發(fā)生 加上volatile關(guān)閉指令重排
內(nèi)部類懶漢式
package com.wanshi.single;
public class Holder {
private Holder() {
}
public static class InnerClass {
private static final Holder HOLDER = new Holder();
}
}
案例全部通過測試!
小結(jié)
單例模式共有5種創(chuàng)建方式,分別為餓漢式、DCL懶漢式、雙檢鎖懶漢式、Enum枚舉餓漢式,內(nèi)部類懶漢式,這幾種方式要掌握,項目中對于全局唯一的對象將其封裝為單例模式,開箱即用,非常方便,以及面試中,會讓手寫單例模式,可謂是大廠必備!
以上就是詳解Java單例模式的實現(xiàn)與原理剖析的詳細(xì)內(nèi)容,更多關(guān)于Java單例模式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
一文帶你徹底了解Java8中的Lambda,函數(shù)式接口和Stream
這篇文章主要為大家詳細(xì)介紹了解Java8中的Lambda,函數(shù)式接口和Stream的用法和原理,文中的示例代碼簡潔易懂,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-08-08
Springboot+Spring Security實現(xiàn)前后端分離登錄認(rèn)證及權(quán)限控制的示例代碼
本文主要介紹了Springboot+Spring Security實現(xiàn)前后端分離登錄認(rèn)證及權(quán)限控制的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11
使用Java快速將Web中表格轉(zhuǎn)換成Excel的方法
在平時做系統(tǒng)項目時,經(jīng)常會需要做導(dǎo)出功能,下面這篇文章主要給大家介紹了關(guān)于使用Java快速將Web中表格轉(zhuǎn)換成Excel的相關(guān)資料,需要的朋友可以參考下2023-06-06
restTemplate發(fā)送get與post請求并且?guī)?shù)問題
這篇文章主要介紹了restTemplate發(fā)送get與post請求并且?guī)?shù)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
java web監(jiān)聽器統(tǒng)計在線用戶及人數(shù)
本文主要介紹了java web監(jiān)聽器統(tǒng)計在線用戶及人數(shù)的方法解析。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04
java讀取excel圖片導(dǎo)入代碼示例(親測有效)
在日常工作中,我們經(jīng)常要將一些照片插入到Excel表格中,這篇文章主要給大家介紹了關(guān)于java讀取excel圖片導(dǎo)入的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10

