1. 程式人生 > 實用技巧 >Singleton單例模式

Singleton單例模式

Singleton單例模式

Singleton:在Java中指單例模式,它是軟體開發中常用的設計模式之一。

單例模式:即某個類在整個系統中只能有一個例項物件可被獲取和使用的程式碼模式。

例如:代表JVM執行環境的Runtime類

要點

  • 一個類只能有一個例項;
    • 構造器私有化
  • 它必須自行建立這個例項;
    • 含有一個該類的靜態變數來儲存這個唯一的例項
  • 必須自行向整個系統提供這個例項;
    • 對外提供獲取該例項物件的方法
    • ①直接暴露 ②用靜態變數的get方法獲取

常見形式

餓漢式:在類初始化時,直接建立物件,不存線上程安全問題

  • 直接例項化餓漢式(簡潔直觀)

    /**
     * 餓漢式
     * 在類初始化時,直接建立例項物件,不管你是否需要這個物件都會建立
     * (1)構造器私有化
     * (2)自行建立,並且用靜態變數儲存
     * (3)向外提供這個例項
     * (4)強調這是一個單例, 我們可以用final修改
     */
    public class Singleton1 {
        public static final Singleton1 INSTANCE = new Singleton1();
    
        private Singleton1() {
        }
    }
    
    /**
     * 直接例項化餓漢式建立物件
     */
    @Test
    public void singleton1Test() {
        Singleton1 instance = Singleton1.INSTANCE;
        System.out.println(instance);  //com.zt.Singleton1@51521cc1  //輸出的是物件的類名+hashcode
    }
    
  • 列舉式(最簡潔)

    /**
     * 列舉型別。表示該型別的物件是有限的幾個
     * 我們可以限定為一個,就成了單例
     */
    public enum Singleton2 {
        INSTANCE
    }
    
    /**
     * 列舉式餓漢式建立物件
     */
    @Test
    public void singleton2Test() {
        Singleton2 instance = Singleton2.INSTANCE;
        System.out.println(instance);  //INSTANCE  輸出的是例項的名字
     }
    
  • 靜態程式碼塊餓漢式(適合複雜例項化)

    public class Singleton3 {
        public static final Singleton3 INSTANCE;
        private String info;
    
        public Singleton3(String info){
            this.info = info;
        }
    
        static {
    
            try {
                //建立一個properties物件,用來獲取properties配置檔案中的屬性
                Properties pro = new Properties();
                //載入配置檔案  路徑時從當前類的類載入器中獲取的
                pro.load(Singleton3.class.getClassLoader().getResourceAsStream("single.properties"));
                INSTANCE = new Singleton3(pro.getProperty("info"));
            } catch (IOException e) {
                //丟擲執行時異常
                throw new RuntimeException(e);
            }
    
        }
    
        public String getInfo() {
            return info;
        }
    
        public void setInfo(String info) {
            this.info = info;
        }
    
        @Override
        public String toString() {
            return "Singleton3{" +
                    "info='" + info + '\'' +
                    '}';
        }
    }
    

    在src目錄下建立一個配置檔案single.properties

    info = hello
    
    /**
     * 靜態程式碼塊餓漢式建立物件
     */
    @Test
    public void singleton3Test() {
        Singleton3 instance = Singleton3.INSTANCE;
        System.out.println(instance);  //Singleton3{info='hello'}
     }
    

懶漢式:延遲建立物件

  • 執行緒不安全(適用於單執行緒)

    /**
     * 懶漢式:延遲建立這個例項物件
     * (1)構造器私有化
     * (2)用一個靜態變數儲存這個唯一的例項
     * (3)提供一個靜態方法,獲取這個例項物件
     */
    public class Singleton4 {
        private static Singleton4 instance;
    
        private Singleton4() {
        }
    
        public static Singleton4 getInstance(){
            if(instance==null){
                instance = new Singleton4();
            }
            return instance;
        }
    }
    
  • 執行緒安全(適用於多執行緒)

    public class Singleton5 {
        private static Singleton5 instance;
    
        private Singleton5() {
        }
    
        public static Singleton5 getInstance() {
    
            if (instance == null) {
    
                synchronized (Singleton5.class) {
    
                    if (instance == null) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        instance = new Singleton5();
                    }
                }
            }
            return instance;
        }
    }
    
    
  • 靜態內部類形式(適用於多執行緒)

    /**
     * 在內部類被載入和初始化時,才建立INSTANCE例項物件
     * 靜態內部類不會自動隨著外部類的載入和初始化而初始化,它是要單獨去載入和初始化的。
     * 因為是在內部類載入和初始化時,建立的,因此是執行緒安全的
     * /
     */
    public class Singleten6 {
        private Singleten6() {
        }
    
        private  static class Inner{
    
            private static final Singleten6 INSTANCE = new Singleten6();
        }
    
        public static Singleten6 getInstance(){
            return Inner.INSTANCE;
        }
    }
    

小結:如果時餓漢式,列舉形式最簡單,如果是懶漢式,靜態內部類形式最簡單;

兩者最根本的區別時建立物件的時機,餓漢式不考慮執行緒安全(安全),懶漢式可以使用靜態內部類形式或者用synchronized程式碼塊實現執行緒安全。