詳解Java單例模式的實現(xiàn)與原理剖析
一、什么是單例模式
單例模式(Singleton Pattern)是 Java 中最簡單的設(shè)計模式之一。這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。
這種模式涉及到一個單一的類,該類負責(zé)創(chuàng)建自己的對象,同時確保只有單個對象被創(chuàng)建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
注意
- 單例模式只能由一個實例對象
- 單例類必須自己創(chuàng)建自己的唯一實例。
- 單例類必須給所有其他對象提供這一實例。
二、哪些地方用到了單例模式
單例模式經(jīng)常用在需要一個實例的程序中,例如
1.Spring框架IOC容器就使用到了單例模式,默認創(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)與原理剖析的詳細內(nèi)容,更多關(guān)于Java單例模式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot+jwt+springSecurity微信小程序授權(quán)登錄問題
這篇文章主要介紹了springboot+jwt+springSecurity微信小程序授權(quán)登錄問題,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01
JDK動態(tài)代理提高代碼可維護性和復(fù)用性利器
這篇文章主要為大家介紹了JDK動態(tài)代理提高代碼可維護性和復(fù)用性利器,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10
java面試散列表及樹所對應(yīng)容器類及HashMap沖突解決全面分析
這篇文章主要介紹了java面試中的java散列表及樹所對應(yīng)容器類與HashMap沖突解決的問題總結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-10-10

