天天看點

【一】Java設計模式GOF23之單例模式:餓漢模式、懶漢模式、靜态内部類模式、雙重同步鎖單例模式、枚舉單例代碼

單例簡介:

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

UML圖

【一】Java設計模式GOF23之單例模式:餓漢模式、懶漢模式、靜态内部類模式、雙重同步鎖單例模式、枚舉單例代碼

常見應用場景:

1.全局計數器采用單例模式,不然不好同步。

2.應用程式的日志應用,共享日志檔案一直處于打開狀态,是以隻能有一個執行個體去操作,否則内容不好追加。

3.資料庫連接配接池的設計也用單例,否則浪費資源。

4.spring中的bean預設都是單例。

5.servlet是單例。

6.spring mvc/ structs1,控制器對象是單例。

7.項目中工具類,一般都用單例,沒必要浪費資源。

常見實作方式

1.餓漢模式(線程安全、調用效率高、不能延時加載)

/**
 * 單例模式
 * 餓漢模式
 * */
public class SingletonDemo1 {
	
	//私有靜态屬性  類初始化的時候就new了對象
	private static /*final*/ SingletonDemo1 instance = new SingletonDemo1();
	
	//私有化構造器
	private SingletonDemo1(){}
	
	//隻能從這裡取對象  方法沒有同步 調用效率高
	public static /*synchronized*/ SingletonDemo1 getInstance(){
		return instance;
	}
}
           

static在類裝載的時候就初始化了(第一句 new),JVM保證隻會new一次SingletonDemo,是以getInstance方法不會有并發問題,得到的都是同一個對象,可以省略synchronized。

2.懶漢模式(線程安全、調用效率不高(getInstance方法用了synchronized同步方法)、可以延時加載)

/*
 * 單例模式
 * 懶漢模式
 * 延遲加載
 * */
public class SingletonDemo2 {
	
	//這裡不用new 延遲加載 等到有人要用的時候調用getinstance方法時才new
	private static SingletonDemo2 s;
	
	//私有化構造器
	private SingletonDemo2(){}
	
	//需要加synchronized,同步化該方法,不然多線程的時候可能會new很多個對象
	public static synchronized SingletonDemo2 getInstance(){
		if(s == null){
			s =  new SingletonDemo2();
		}
		return s;
	}
}
           

3.靜态内部類模式(線程安全、調用率高、可以延時加載)

/*
 * 單例模式
 * 靜态内部類模式
 * 懶加載 線程安全 效率高
 * */

public class SingletonDemo3 {

	//靜态内部類  初始化SingletonDemo3的時候并不會立即初始化這個靜态内部類
	private static class singletonClassInstance{
		//在靜态内部類中定義單例對象
		private static final SingletonDemo3 instance = new SingletonDemo3();
	}
	
	public static SingletonDemo3 getInstance(){
		//調用的時候才初始化這個單例類  靜态内部類初始化的時候是天然線程安全的,jvm隻會初始化一次靜态内部類
		return singletonClassInstance.instance;
	}
	
	//私有化構造器
	private SingletonDemo3(){}
	
           

4.懶漢模式 -》 雙重同步鎖單例模式

public class SingletonExample5 {

    // 私有構造函數
    private SingletonExample5() {

    }

    // 1、memory = allocate() 配置設定對象的記憶體空間
    // 2、ctorInstance() 初始化對象
    // 3、instance = memory 設定instance指向剛配置設定的記憶體

    // 單例對象 volatile + 雙重檢測機制 -> 禁止指令重排
    private volatile static SingletonExample5 instance = null;

    // 靜态的工廠方法
    public static SingletonExample5 getInstance() {
        if (instance == null) { // 雙重檢測機制        // B
            synchronized (SingletonExample5.class) { // 同步鎖
                if (instance == null) {
                    instance = new SingletonExample5(); // A - 3
                }
            }
        }
        return instance;
    }
}
           

5.枚舉單例(線程安全、調用率高、不能延時加載、不會被反射反序列化生成多個執行個體)

/*
 * 單例模式
 * 
 * 枚舉模式
 * 
 * 無延遲加載
 * 避免通過反射和反序列化的漏洞建立新的對象
 */

//枚舉類天然單例
public enum SingletonDemo4 {
	
	//定義一個枚舉元素,它就代表了SingletonDemo4的一個執行個體
	INSTANCE;
	
	//單例可以有自己的操作
	public void singletonOperation(){
		//功能處理
	}
}
           

避免通過反射和反序列化的漏洞建立新的對象

枚舉類天然單例

無延遲加載

通過反射漏洞建立多個對象

import java.lang.reflect.Constructor;

/**
 * @author liyijie
 * @date 2018年8月13日下午2:41:23
 * @email [email protected]
 * @remark 通過反射、反序列化破解單例(枚舉除外)
 * @version 
 */
public class client2 {
	public static void main(String[] args) throws Exception {
		SingletonDemo3 s1 = SingletonDemo3.getInstance();
		SingletonDemo3 s2 = SingletonDemo3.getInstance();
		System.out.println(s1);
		System.out.println(s2);
		
		//得到class
		Class<SingletonDemo6> class6 = (Class<SingletonDemo6>)Class.forName("com.sid.singleton.SingletonDemo6");
		//得到構造器
		Constructor<SingletonDemo6> c =class6.getDeclaredConstructor(null);
		//跳過權限檢查,不然不能通路私有方法
		c.setAccessible(true);
		SingletonDemo6 s3 = c.newInstance();
		SingletonDemo6 s4 = c.newInstance();
		
		System.out.println(s3);
		System.out.println(s4);
	}
}
           
【一】Java設計模式GOF23之單例模式:餓漢模式、懶漢模式、靜态内部類模式、雙重同步鎖單例模式、枚舉單例代碼

防止反射漏洞建立多個對象需要修改單例類的私有化構造器,第二次建立對象時報錯

import java.io.ObjectStreamException;
import java.io.Serializable;

/*
 * 防止反射、反序列化破解單例

 * implements Serializable是用來測試反序列化漏洞的,實際寫單例不需要實作這個接口
 * */
public class SingletonDemo6 implements Serializable{
	//這裡不用new 延遲加載 等到有人要用的時候調用getinstance方法時才new
		private static SingletonDemo6 s;
		
		//私有化構造器
		private SingletonDemo6(){
			//第二次建立這個對象報錯、防止利用反射漏洞
			if(s!=null){
				throw new RuntimeException();
			}
		}
		
		//需要加synchronized,同步化該方法,不然多線程的時候可能會new很多個對象
		public static synchronized SingletonDemo6 getInstance(){
			if(s == null){
				s =  new SingletonDemo6();
			}
			return s;
		}
		
		//反序列化拿對象時傳回這個s對象,防止利用反序列化漏洞建立多個執行個體
		private Object readResolve() throws ObjectStreamException{
			return s;
		}

}
           

繼續閱讀