Java中的單例模式

1.概述

        Java中單例模式,即一個類有且僅有一個對象實例。並且自行實例化,向整個系統提供。
        一旦該類存在一個對象之後,無法再重新創建第二個類對象。

2.作用

        對於系統中的某些類來說,只有一個實例很重要。比如:一個系統中可存在多個打印任務,但是只能有一個正在工作的任務;Windows中同時只能打開一個任務管理器,即窗口對象唯一化。一些資源管理器常常被設計爲單例模式
如何保證一個類只有一個實例並且這個實例方便被訪問呢?
        定義一個全局變量可以確保對象隨時都可以被訪問,但不能防止實例化多個對象。而且在程序開發中,全局變量一般不會輕易創建。一個更好的解決辦法是讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例被創建,並且可以提供一個訪問該實例的辦法。

3.創建方式

        懶漢方式
                指全局的實例對象在第一次被使用時創建
        餓漢方式
                指全局的實例對象在類加載時被創建。

4.優缺點

優點:
        1.實例控制
                單例模式會阻止其他對象實例化其自己的單例對象的副本,從而確保所有對象都訪問唯一實例。
        2.靈活性
                因爲類控制了實例化過程,所以類可以靈活更改實例化過程。
缺點:
        1.開銷
                雖然數量很少,但如果每次對象請求引用時都要檢查是否存在類的實例,將仍然需要一些開銷。可以通過使用靜態初始化解決此問題。
        2.可能的開發混淆
                使用單例對象(尤其在類庫中定義的對象)時,開發人員必須記住自己不能使用new關鍵字實例化對象。

5.單例模式推導

5.1懶漢方式

package com.a_single;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * SingleDog要求是一個單例類,整個代碼運行週期內有且只有一個類對象
 *
 * 不可取方式:
 * @全體成員 SingleDog類使用一個單例類,我創建過對象了,你們不要搞事情
 *          紅包
 * 【期望】
 *      從語法角度約束一些行爲。
 *      目前創建對象過於簡單,new + 構成方法組合非常easy
 * 【解決方案】
 *      private修飾構造方法
 *
 * 【問題】
 *      構造方法私有化之後,類外沒有操作構造方法的權限,沒辦法創建對象
 * 【解決方案】
 *      1. 暴力反射
 *          暴力反射,給予構造方法操作權限,這裏和沒有使用private有什麼區別
 *          【不合適】
 *      2. 期望
 *          類外最起碼有一個對象。
 *          a. 類外沒有SingleDog類對象
 *          b. 期望獲取到一個SingleDog對象
 *
 *          需要方法來完成
 *              1. 該方法要求靜態成員方法,沒有對象,需要通過類名調用
 *              2. 類外要求使用public修飾
 *              3. 該方法需要得到一個SingleDog對象,返回值類型是SingleDog
 *              4. 方法參數爲無參數,構造方法也是無參
 *              5. 方法名 getInstance
 *              public static SingleDog getInstance()
 *
 * 【問題】
 *      依然是不同對象
 *
 * 【期望】
 *      1. 調用該方法每一次調用都是new新的對象。
 *      2. 這個new得有限制,如果之前創建過對象,你就不能再new了,沒有對象可以new
 *      3. 存在一個變量可以保存之前創建對象的空間首地址,並且可以持久性保存。
 *      分析:
 *          a. 需要保存SingleDog類對象空間首地址
 *                 SingleDog類型
 *          b. 該數據不能類外輕鬆獲取
 *                 private
 *          c.
 *              (1) 靜態方法可以使用
 *              (2) 有一定的持久性
 *                  static修飾成員變量
 *          private static SingleDog sd = null;
 * 【getInstance方法】
 *      需要判斷
 *          判斷SingleDog類型的靜態成員變量 sd是否保存有之前創建的空間首地址
 *
 * 【問題】
 *      多線程情況下,存在線程搶佔問題,極有可能導致當前方法被兩個線程同時執行,同時
 *      創建對象
 *      com.qfedu.a_single.SingleDog@22673956
 *      com.qfedu.a_single.SingleDog@6223c513
 *
 * 【期望】
 *      在多線程情況下,也是安全的
 * 【解決方案】
 *      加鎖
 *          【牆裂推薦】synchronized同步代碼塊
 *              使用同步代碼塊,用什麼做鎖對象?
 *              最合適的鎖對象依然是Single.class
 *
 *          【牆裂推薦】synchronized同步方法
 *              static修飾靜態方法的情況下,什麼是鎖對象?
 *                  當前SingleDog類.class字節碼文件
 *                  
 *          【不推薦】Lock鎖對象
 *              和以上兩個方式對比?
 *              new ==> lock ==> 多出一個Lock鎖空間
 *              (1) 在getInstance方法new lock是不合適,多個線程情況下
 *              可能會出現不同鎖情況
 *              (2) Lock做成靜態成員變量
 *                  浪費空間了!!!
 *
 *
 * @author Anonymous 2020/3/13 10:53
 */
public class SingleDog {
    private static SingleDog sd = null;

    private SingleDog() {}
	
    // 同步方法
    public static synchronized SingleDog getInstance() {
        if (null == sd) {
            sd = new SingleDog();
        }

        return sd;
    }
}

public class SingleDog1 {
    private static SingleDog1 sd = null;

    private SingleDog1() {}

    // 同步代碼塊
    public static  SingleDog1 getInstance() {
        synchronized (SingleDog1.class) {
        	if (null == sd) {
        	    sd = new SingleDog1();
        	}
    	}
    
        return sd;
    }
}

5.2餓漢方式

package com.a_single;

/**
 * 另一個單例模式
 *
 * @author Anonymous 2020/3/13 11:44
 */
public class SingleCat {
    /*
    static修飾,在代碼的加載階段創建完成
    並且使用final修飾,保存當前指向不可以改變
    private修飾類外無法直接獲取,不能修改
     */
    private static final SingleCat sc = new SingleCat();

    private SingleCat() {}

    /*
    使用方法做的統一
     */
    public static SingleCat getInstance() {
        return sc;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章