【设计模式】学习笔记---单例模式
概述
學習設計模式,死記硬背是沒用的,還要從實踐中理解
日常應用中,設計模式從來都不是單個設計模式獨立使用的。在實際應用中,通常多個設計模式混合使用,你中有我,我中有你。下圖完整地描述了設計模式之間的混用關系,希望對大家有所幫助。
單例模式
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。
這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
注意:
1、單例類只能有一個實例。
2、單例類必須自己創建自己的唯一實例。
3、單例類必須給所有其他對象提供這一實例。
單例模式的應用場景
對于 Java 來說,單例模式可以保證在一個 JVM 中只存在單一實例。單例模式的應用場景主要有以下幾個方面。
懶漢模式
延遲加載, 只有在真正使用的時候,才開始實例化。
1)線程安全問題
2)double check 加鎖優化
3)編譯器(JIT),CPU 有可能對指令進行重排序,導致使用到尚未初始化 的實例,可以通過添加volatile 關鍵字進行修飾, 對于volatile 修飾的字段,可以防止指令重排。
class LazySingleton{ // java5版本之前 關鍵字volatile 不能解決指令重排的問題private volatile static LazySingleton instance; // 讓構造函數為 private,這樣該類就不會被實例化private LazySingleton(){ } // 這種方式的懶加載只適用于單線程情況下public static LazySingleton getInstance() { if (instance==null){ instance=new LazySingleton(); } return instance; } // 同步,線程安全,但是效率太低// 但是單例模式只有在第一次使用的時候才會創建// 下面這種寫法就不適合了public static synchronized LazySingleton getInstance() { if (instance==null){ instance=new LazySingleton(); } return instance; } // 雙重檢驗鎖// 下面代碼看是完美,但是JVM編譯器存在指令重排的優化(有坑)public static LazySingleton getInstance() { if (instance==null){ synchronized (LazySingleton.class){ if (instance==null){ instance=new LazySingleton(); } } } return instance; } }餓漢模式
類加載的 初始化階段就完成了 實例的初始化 。本質上就是借助于jvm 類加載機制,保證實例的唯一性(初始化過程只會執行一次)及線程安全(JVM以同步的形式來完成類加載的整個過程)。
類加載過程
1,加載二進制數據到內存中, 生成對應的Class數據結構,
2,連接: a. 驗證, b.準備(給類的靜態成員變量賦默認值),c.解析
3,初始化: 給類的靜態變量賦初值
只有在真正使用對應的類時,才會觸發初始化 如( 當前類是啟動類即 main函數所在類,直接進行new 操作,訪問靜態屬性、訪問靜態方 法,用反射訪問類,初始化一個類的子類等.)
class HungrySingleton{ private static HungrySingleton instance=new HungrySingleton(); //讓構造函數為 private,這樣該類就不會被實例化private HungrySingleton(){ } public static HungrySingleton getInstance() { return instance;} }靜態內部類
1).本質上是利用類的加載機制來保證線程安全
2).只有在實際使用的時候,才會觸發類的初始化,所以也是懶加載的一 種形式。
class InnerClassSingleton{ private static class InnerClassHolder{private static InnerClassSingleton instance= new InnerClassSingleton(); } private InnerClassSingleton(){} public static InnerClassSingleton getInstance(){ return InnerClassHolder.instance;} }反射攻擊實例
Constructor<InnerClassSingleton> declaredConstructor=InnerClassSingleton.c lass.getDeclaredConstructor(); declaredConstructor.setAccessible( true ); InnerClassSingleton innerClassSingleton=declaredConstructor.newInstance(); InnerClassSingleton instance=InnerClassSingleton.getInstance(); System.out.println(innerClassSingleton==instance);靜態內部類防止反射破壞
class InnerClassSingleton { private static class InnerClassHolder{ private static InnerClassSingleton instance= new InnerClassSingleton(); } private InnerClassSingleton(){ if (InnerClassHolder.instance!=null){ throw new RuntimeException( " 單例不允許多個實例 " ); } } public static InnerClassSingleton getInstance(){ return InnerClassHolder.instance; } }枚舉類型
1)天然不支持反射創建對應的實例,且有自己的反序列化機制
2)利用類加載機制保證線程安全
public enum EnumSingleton { INSTANCE; public void print(){ System.out.println(this.hashCode()); } }序列化
1)可以利用 指定方法來替換從反序列化流中的數據 如下 1
ANY‐ACCESS‐MODIFIER Object readResolve() throws ObjectStreamException; class InnerClassSingleton implements Serializable{ static final long serialVersionUID = 42L; private static class InnerClassHolder{ private static InnerClassSingleton instance= new InnerClassSingleton(); } private InnerClassSingleton(){ if (InnerClassHolder.instance!=null){ throw new RuntimeException( " 單例不允許多個實例 " );}} public static InnerClassSingleton getInstance(){ return InnerClassHolder.instance; } Object readResolve() throws ObjectStreamException{ return InnerClassHolder.instance; } }總結
以上是生活随笔為你收集整理的【设计模式】学习笔记---单例模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【MaxCompute】学习笔记常用查询
- 下一篇: 【学习笔记】Linux 命令万字解析(超