天天看點

01二十三種設計模式之單例模式

二十三種設計模式之單例模式

核心作用

保證一個類隻有一個執行個體,并且對外提供一個通路該執行個體的全局通路點。

優點

  • 由于單例模式隻生成一個執行個體,減少系統性能開銷
  • 單例模式可以在系統設定全局通路點,優化環境共享資源通路

常用應用場景

  • windows 系統的任務管理器、資源回收筒;
  • 項目中讀取配置檔案的類,一般配置檔案隻需要讀取一次,是以隻需要一個讀取配置檔案的類;
  • 網站的計數器
  • 應用程式中日志應用,一般都是單例模式實作,由于共享的日志檔案一直處于打開狀态,隻能有一個執行個體操作,否則不好追加
  • 資料庫的連接配接池設計
  • 作業系統的檔案系統
  • 在 Spring 中,每個 bean 預設是單例,這樣做的優點是 Spring 容器很好的管理
  • 在 Servlet 程式設計中,每個 Servlet 也是單例
  • 在 SpringMVC 中,控制器對象也是單例

步驟

  • 私有化構造方法
  • 提供私有的靜态的對象的執行個體屬性
  • 提供對外通路的靜态方法

五種單例模式

  • 餓漢式(線程安全,調用效率高,但是不能延時加載)
  • 懶漢式(線程安全,調用效率不高,但是可以延時加載)
  • 雙重檢測鎖式(由于 JVM 底層内部模型原因,偶爾會出現問題,不建議使用)
  • 靜态内部類式(線程安全,調用效率高,可以延時加載)
  • 枚舉單例(線程安全,調用效率高,不能延時加載,實作簡單。由于 JVM 從根本上提供保障,避免通過反射和反序列化的漏洞)

餓漢式

package singleton;
/**
 * 餓漢式單例模式
 * 類加載時,立即加載這個對象。加載類是,天然的線程安全
 *
 * @author SIMBA1949
 * @date 2019/6/5 21:20
 */
public class Singleton01 {
   
    private static Singleton01 instance = new Singleton01(); 
    private Singleton01() {
    }
    public static Singleton01 getInstance(){
  return instance;
    }
}      

懶漢式

package singleton;
/**
 * 懶漢式單例模式
 * 延時加載,資源使用率高,并發情況下 synchronized 效率低
 * 
 * @author SIMBA1949
 * @date 2019/6/5 21:39
 */
public class Singleton02 {
   
    private static Singleton02 instance;
    private Singleton02() {
    }
    public static synchronized Singleton02 getInstance(){
  if (null == instance){
    instance = new Singleton02();
  }
  return instance;
    }
}      

雙重檢測鎖單例模式

package singleton;
/**
 * 雙重檢測鎖單例模式
 * 
 * @author SIMBA1949
 * @date 2019/6/5 21:44
 */
public class Singleton03 {
    private static Singleton03 instance;
    private Singleton03() {
    }
    public static Singleton03 getInstance(){
  if (null == instance){
    synchronized (Singleton03.class){
    if (null == instance){
        instance = new Singleton03();
    }
    }
  }
  return instance;
    }
}      

靜态内部類單例模式

package singleton;
/**
 * 靜态内部類單例模式
 * 類初始化的時候不會将内部類一起初始化,隻有調用的時候才會加載靜态内部類,加載類時線程時安全的
 * INSTANCE 是 static final 修飾的,保證記憶體中隻有一個執行個體存在
 * 兼備并發高效調用和延時加載的優勢
 * 
 * @author SIMBA1949
 * @date 2019/6/5 21:54
 */
public class Singleton04 {
    private Singleton04() {
    }
    private static class Singleton04Inter{
  public static final Singleton04 INSTANCE = new Singleton04();
    }
    public static Singleton04 getInstance(){
  return Singleton04Inter.INSTANCE;
    }
}      

枚舉單例模式

package singleton;
/**
 * @author SIMBA1949
 * @date 2019/6/5 22:01
 */
public enum  Singleton05 {
    /**
  * 這個枚舉元素 INSTANCE 本身就是單例對象
  */
    INSTANCE
}      

避免反射建立多個對象

package singleton;
/**
 * 避免反射建立多個對象,在構造方法中判斷如果執行個體屬性不為 null 時,抛出異常即可
 *
 * @author SIMBA1949
 * @date 2019/6/5 22:08
 */
public class Singleton064Reflect {
    private static Singleton064Reflect instance = new Singleton064Reflect();
    private Singleton064Reflect() {
  if (null != instance){
    throw new RuntimeException();
  }
    }
    public static Singleton064Reflect getInstance(){
  return instance;
    }
}      

避免反序列化建立多個對象

package singleton;
import java.io.*;
/**
 * 添加 readResolve() 方法可以避免反序列化建立多個對象
 * 
 * @author SIMBA1949
 * @date 2019/6/5 22:12
 */
public class Singleton074Serializable implements Serializable{
    private static final long serialVersionUID = -3093202203157151493L;
    private static Singleton074Serializable instance = new Singleton074Serializable();
    private Singleton074Serializable() {
    }
    public static Singleton074Serializable getInstance(){
  return instance;
    }
    /**
  * 添加 readResolve() 方法可以避免反序列化建立多個對象
  * @return
  */
    private Object readResolve(){
  return instance;
    }
}
class DeserializableTest{
    public static void main(String[] args) throws IOException, ClassNotFoundException {
  Singleton074Serializable instance = Singleton074Serializable.getInstance();
  // 序列化
  FileOutputStream fis = new FileOutputStream(new File("T:/s.java"));
  ObjectOutputStream oos = new ObjectOutputStream(fis);
  oos.writeObject(instance);
  // 反序列化
  ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("T:/s.java")));
  Singleton074Serializable deInstance = (Singleton074Serializable) ois.readObject();
  System.out.println(instance == deInstance);
    }
}