詳解Java單例模式的實(shí)現(xiàn)與原理剖析
一、什么是單例模式
單例模式(Singleton Pattern)是 Java 中最簡(jiǎn)單的設(shè)計(jì)模式之一。這種類(lèi)型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式。
這種模式涉及到一個(gè)單一的類(lèi),該類(lèi)負(fù)責(zé)創(chuàng)建自己的對(duì)象,同時(shí)確保只有單個(gè)對(duì)象被創(chuàng)建。這個(gè)類(lèi)提供了一種訪問(wèn)其唯一的對(duì)象的方式,可以直接訪問(wèn),不需要實(shí)例化該類(lèi)的對(duì)象。
注意
- 單例模式只能由一個(gè)實(shí)例對(duì)象
- 單例類(lèi)必須自己創(chuàng)建自己的唯一實(shí)例。
- 單例類(lèi)必須給所有其他對(duì)象提供這一實(shí)例。
二、哪些地方用到了單例模式
單例模式經(jīng)常用在需要一個(gè)實(shí)例的程序中,例如
1.Spring框架IOC容器就使用到了單例模式,默認(rèn)創(chuàng)建對(duì)象的時(shí)候?yàn)閱卫J?/p>
2.ResultBean 后端統(tǒng)一返回給前端的封裝類(lèi),這個(gè)在項(xiàng)目中是唯一的,只用一個(gè)對(duì)象進(jìn)行返回JSON給前端進(jìn)行渲染
JDK中也有單例模式的身影,例
- Runtime 體現(xiàn)了餓漢式單例
- Console 體現(xiàn)了雙檢鎖懶漢式單例
- Collections 中的 EmptyNavigableSet 內(nèi)部類(lèi)懶漢式單例
- ReverseComparator.REVERSE_ORDER 內(nèi)部類(lèi)懶漢式單例
- Comparators.NaturalOrderComparator.INSTANCE 枚舉餓漢式單例
三、單例模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
提供了對(duì)唯一實(shí)例的訪問(wèn)
可以節(jié)約系統(tǒng)資源,提高系統(tǒng)的性能,減少不必要的內(nèi)存開(kāi)銷(xiāo)
允許可變數(shù)目的實(shí)例(多例類(lèi))
缺點(diǎn)
擴(kuò)展困難(缺少抽象層)
單例類(lèi)的職責(zé)過(guò)重
由于自動(dòng)垃圾回收機(jī)制,可能會(huì)導(dǎo)致共享的單例對(duì)象的狀態(tài)丟失
四、手寫(xiě)單例模式
餓漢式
package com.wanshi.single; //餓漢式單例 public class Hungry { //會(huì)造成資源浪費(fèi),占用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 是一個(gè)class類(lèi) 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編譯工具即可
枚舉類(lèi)最后反編譯源碼
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("不要試圖通過(guò)反射破壞對(duì)象"); } } } 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.把對(duì)象指向這個(gè)空間 指令重排可能會(huì)發(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.把對(duì)象指向這個(gè)空間
指令重排可能會(huì)發(fā)生 加上volatile關(guān)閉指令重排
內(nèi)部類(lèi)懶漢式
package com.wanshi.single; public class Holder { private Holder() { } public static class InnerClass { private static final Holder HOLDER = new Holder(); } }
案例全部通過(guò)測(cè)試!
小結(jié)
單例模式共有5種創(chuàng)建方式,分別為餓漢式、DCL懶漢式、雙檢鎖懶漢式、Enum枚舉餓漢式,內(nèi)部類(lèi)懶漢式,這幾種方式要掌握,項(xiàng)目中對(duì)于全局唯一的對(duì)象將其封裝為單例模式,開(kāi)箱即用,非常方便,以及面試中,會(huì)讓手寫(xiě)單例模式,可謂是大廠必備!
以上就是詳解Java單例模式的實(shí)現(xiàn)與原理剖析的詳細(xì)內(nèi)容,更多關(guān)于Java單例模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot+jwt+springSecurity微信小程序授權(quán)登錄問(wèn)題
這篇文章主要介紹了springboot+jwt+springSecurity微信小程序授權(quán)登錄問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01淺談Spring Cloud Eureka 自我保護(hù)機(jī)制
這篇文章主要介紹了淺談Spring Cloud Eureka 自我保護(hù)機(jī)制,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06JDK動(dòng)態(tài)代理提高代碼可維護(hù)性和復(fù)用性利器
這篇文章主要為大家介紹了JDK動(dòng)態(tài)代理提高代碼可維護(hù)性和復(fù)用性利器,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10Idea中maven無(wú)法下載依賴包問(wèn)題解決
用過(guò)idea開(kāi)發(fā)過(guò)項(xiàng)目的同學(xué),偶爾會(huì)遇到項(xiàng)目中有一些依賴沒(méi)法下載,或者依賴包已經(jīng)有項(xiàng)目卻無(wú)法掃到的問(wèn)題,本文就詳細(xì)的介紹了解決方法,感興趣的可以了解一下2020-08-08java面試散列表及樹(shù)所對(duì)應(yīng)容器類(lèi)及HashMap沖突解決全面分析
這篇文章主要介紹了java面試中的java散列表及樹(shù)所對(duì)應(yīng)容器類(lèi)與HashMap沖突解決的問(wèn)題總結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-10-10