學習札記:設計模式之單例模式(如何應對在反射,反序列化時單例失效)

一.什麼是單例模式(Singleton)

保證一個類僅有一個實例,並提供一個訪問他的全局訪問穩點。

二.單例模式的幾種寫法

1.餓漢式單例

public class SingletonInstance{
	private static SingletonInstance instance=new SingleInstance;
	
	private SingletonInstance(){
	
	}
	public static SingletonInstance instance(){
		 return instance;
	}

} 

線程安全的餓漢式單例

public class SingletonInstance{
	private static SingletonInstance instance=new SingleInstance;
	
	private SingletonInstance(){
	
	}
	public  static  synchronized SingletonInstance instance(){
		 return instance;
	}

} 

2.懶漢式單例

public class SingletonInstance{
	private static SingletonInstance instance;
	private SingletonInstance instance;
	public static SingletonInstance instance(){
	    
	    if(null==instance){
	    	instance = new SingletonInstance();
	    }
		return instance;
	}

}

線程安全的懶漢式單例

public class SingletonInstance{
	private static SingletonInstance instance;
	private SingletonInstance instance;
	public static  synchronize SingletonInstance instance(){
	    
	    if(null==instance){
	    	instance = new SingletonInstance();
	    }
		return instance;
	}

}

3.雙重檢測單例(DCL double check lock)

public class SingletonInstance{
	private staic SingletonInstance instance;
 	private SingletonInstance (){
	}
 	public static SingletonInstance(){
		if(null==instance){
			synchornize(SingletonInstance.class){
				if(null==instance){
					instance=new SingletonInstance();
				}
			}
		}
		return instance;
	}

}

4.枚舉單列

public enum SingletonInstance{
	INSTANCE
}


5.單列管理類創建單例

public class SingletonInstanceManager {
	
	private static Map<String, Object> objMap = new HashMap<>();
	

	public static void registerService(String key,Object instance) {
		
		if(objMap.containsKey(key)) {
			objMap.put(key, instance);
		}
			
	}
	
	public static Object getService(String key) {
		
		return objMap.get(key);
	}

}

6.通過靜態內部類實現單例

public class SingletonInstance{
    private SingletonInstance (){

    }
    
    private static class Builder{
      
        private static SingletonInstance singletonInstance = new SingletonInstance();
    }
 
    public static SingletonInstance instance(){
        return Builder.singletonInstance ;
    }
}

三.序列化和反射造成的單例失敗及解決辦法

1.反射造成的單例失敗

public class client {

	
	public static void main(String[] args) {
		
		System.out.println("反射前:");
		System.out.println(SingletonInstance.instance());
		System.out.println(SingletonInstance.instance());
		
		try {
			//反射破壞了單例
			Class clz=Class.forName("com.seven.singleton.SingletonInstance");
			Constructor constru=clz.getDeclaredConstructor();	
			System.err.println(constru);
			constru.setAccessible(true);
			SingletonInstance singletonInstance=(SingletonInstance) constru.newInstance();
			System.out.println("反射後:");
			System.out.println("singleton="+singletonInstance);
			singletonInstance.print();
			SingletonInstance singletonInstance1=(SingletonInstance) constru.newInstance();
			System.out.println("singleton="+singletonInstance1);
			singletonInstance1.print();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

我們把控制檯的打印展示出來
反射前:
com.seven.singleton.SingletonInstance@15db9742
com.seven.singleton.SingletonInstance@15db9742
反射後:
private com.seven.singleton.SingletonInstance()
singleton=com.seven.singleton.SingletonInstance@6d06d69c
this is a single
singleton=com.seven.singleton.SingletonInstance@7852e922
this is a single
我們發現通過反射後,會有兩個不同的對象了,所以我們可以通過在私有構造方法裏面加判斷,來解除這個問題,

 private SingletonInstance (){
    	 if(null!=Builder.singletonInstance){
             throw new RuntimeException("單例模式不允許外界使用構造器!");
         }
    }
    
    private static class Builder{
      
        private static SingletonInstance singletonInstance = new SingletonInstance();
    }
 
    public static SingletonInstance instance(){
        return Builder.singletonInstance ;
    }
    
    public void print() {
    	System.out.println("this is a single");
    }

這樣就解決了

2.序列化造成的單例問題

public class client {

	
	public static void main(String[] args) {
		System.out.println("反射前:");
		System.out.println(SingletonInstance.instance());
		System.out.println(SingletonInstance.instance());
        FileOutputStream fileOutputStream = null;
        ObjectOutputStream objectOutputStream = null;
        FileInputStream fileInputStream = null;
        ObjectInputStream objectInputStream = null;
 
		
		 try {
	            fileOutputStream = new FileOutputStream("SingletonInstance.txt");
	            objectOutputStream = new ObjectOutputStream(fileOutputStream);
	            objectOutputStream.writeObject(SingletonInstance.instance());
	            System.out.println("序列化對象成功!");
	        } catch (IOException e) {
	            e.printStackTrace();
	        } finally {
	            try {
	                objectOutputStream.close();
	            } catch (IOException e1) {
	                e1.printStackTrace();
	            }
	            try {
	                fileOutputStream.close();
	            } catch (IOException e) {
	                e.printStackTrace();
	            }
	        }
	        // 2.通過剛纔生成的序列化對象文件反序列化生成對象
	        try {
	            fileInputStream = new FileInputStream("SingletonInstance.txt");
	            objectInputStream = new ObjectInputStream(fileInputStream);
	            SingletonInstance singletonInstance3 = (SingletonInstance) objectInputStream.readObject();
	            System.out.println(singletonInstance3);
	        } catch (FileNotFoundException e) {
	            e.printStackTrace();
	        } catch (ClassNotFoundException e) {
	            e.printStackTrace();
	        } catch (IOException e) {
	            e.printStackTrace();
	        } finally {
	            try {
	                objectInputStream.close();
	            } catch (IOException e) {
	                e.printStackTrace();
	            }
	            try {
	                fileInputStream.close();
	            } catch (IOException e) {
	                e.printStackTrace();
	            }
	        }

	}

我們把控制檯的打印展示出來
反射前:
com.seven.singleton.SingletonInstance@22f71333
com.seven.singleton.SingletonInstance@22f71333
序列化對象成功!
com.seven.singleton.SingletonInstance@685cb137
我們發現,反序列化之後,單例就失效了,那怎麼解決呢

   
    /**
     * 反序列化時通過該默認方法返回對象
     * @return
     */
    private  Object readResolve(){
        return Builder.singletonInstance;
    }

加上這句即可

四.總結

本文介紹了什麼是單例模式以及單例模式的寫法,並介紹了單例模式在反射和反序列化失效時的原因和解決辦法,如果本文對你有用,可以點個贊,如果有不正之處,歡迎留下你的評論,謝謝!

發佈了57 篇原創文章 · 獲贊 8 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章