1. 程式人生 > 其它 >三種單例模式的終極寫法(包括反射攻擊)

三種單例模式的終極寫法(包括反射攻擊)

技術標籤:Java-基礎

一、餓漢式的反射攻擊及解決方法

public class HungrySingleton {

	//直接宣告一個需要被單例的物件,在靜態塊中初始化物件
    private final static HungrySingleton hungrySingleton;

    static {
        hungrySingleton=new HungrySingleton();
    }
    //私有的構造器,不被外部所訪問
    private HungrySingleton(){
    	// 防止反射攻擊
    	if(hungrySingleton !=null ){
    		throw new RuntimeException("單例構造器進位制通過反射呼叫。。。。");
    	}
    	// 防止反射攻擊
    }
    //提供一個外部獲取HungrySingleton物件的靜態方法
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}
public class HungryTest {
	public static void main(String[] args) {
        try {
            /** 
            * @Description: 通過反射獲取物件
            * @Author: xz
            * @Date: 2020/5/31 21:06
            */ 
            //獲取class物件
            Class<HungrySingleton> hungrySingletonClass = HungrySingleton.class;
            //通過獲取的class物件,獲取宣告的構造器
            Constructor<HungrySingleton> declaredConstructor =  hungrySingletonClass.getDeclaredConstructor();
            //去除構造器的私有許可權
            declaredConstructor.setAccessible(true);
            //通過構造器建立物件
            
            HungrySingleton newInstance = declaredConstructor.newInstance();
            /** 
            * @Description:  直接通過類名.getInstance方法獲取物件
            * @Author: xz
            * @Date: 2020/5/31 21:07
            */ 
            HungrySingleton instance=HungrySingleton.getInstance();

            //輸出2個物件,並判斷是否相等
            System.out.println(newInstance);
            System.out.println(instance);
            System.out.println(newInstance==instance);

        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }  catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

二、靜態內部類的反射攻擊及解決方法

public class StaticInnerClassSingleton {
	
	    //私有的構造器,不被外部所訪問
	    private StaticInnerClassSingleton(){
	    	// 方式反射攻擊
	    	 if(InnerClass.staticInnerClassSingleton !=null){
	    	      throw new RuntimeException("單例構造器進位制反射呼叫");
	    	 }
	    	// 方式反射攻擊
	    }
	    //私有的靜態內部類
	    private static class InnerClass{
	        //new一個私有的靜態的StaticInnerClassSingleton物件
	        private static StaticInnerClassSingleton staticInnerClassSingleton=new StaticInnerClassSingleton();
	    }
	    //提供一個公用的對外暴露的方法
	    public static StaticInnerClassSingleton getInstance(){
	        //返回值為通過靜態內部內呼叫靜態成員
	        return InnerClass.staticInnerClassSingleton;
	    }
}
public static void main(String[] args) {
        try {
            /**
             * @Description: 通過反射獲取物件
             * @Author: xz
             * @Date: 2020/5/31 21:06
             */
            //獲取class物件
            Class<StaticInnerClassSingleton> staticInnerClassSingletonClass = StaticInnerClassSingleton.class;
            //通過獲取的class物件,獲取宣告的構造器
            Constructor<StaticInnerClassSingleton> declaredConstructor = staticInnerClassSingletonClass.getDeclaredConstructor();
            //去除構造器的私有許可權
            declaredConstructor.setAccessible(true);
            //通過構造器建立物件

            StaticInnerClassSingleton newInstance = declaredConstructor.newInstance();
            /**
             * @Description:  直接通過類名.getInstance方法獲取物件
             * @Author: xz
             * @Date: 2020/5/31 21:07
             */
            StaticInnerClassSingleton instance=StaticInnerClassSingleton.getInstance();

            //輸出2個物件,並判斷是否相等
            System.out.println(newInstance);
            System.out.println(instance);
            System.out.println(newInstance==instance);

        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }  catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

三、懶漢式的反射攻擊及解決方法

無法防止反射攻擊

public class LazyDoubleCheckSingleton {

	//宣告一個需要被單例的物件,初始化時沒有被建立所以設定null
    /** volatile修飾的成員變數在每次被執行緒訪問時,都強迫從共享記憶體中重讀該成員變數的值。
     *  而且,當成員變數發生變化時,強迫執行緒將變化值回寫到共享記憶體。
     *  這樣在任何時刻,兩個不同的執行緒總是看到某個成員變數的同一個值。
     */
    private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton=null;
    
    private static boolean flag =true;
    
    //私有的構造器,不被外部所訪問
    private LazyDoubleCheckSingleton(){
    	if(flag){
            flag=false;
        }else{
            throw new RuntimeException("單例構造器進位制反射呼叫");
        }
    }

    //提供一個外部獲取LazyDoubleCheckSingleton物件的靜態方法
    public  static LazyDoubleCheckSingleton getInstance(){
        if (lazyDoubleCheckSingleton==null){
            synchronized (LazyDoubleCheckSingleton.class){
                if(lazyDoubleCheckSingleton==null){
                    lazyDoubleCheckSingleton=new LazyDoubleCheckSingleton();
                }
            }
        }
        return  lazyDoubleCheckSingleton;
    }
}
public class LazyDoubleCheckTest {
	public static void main(String[] args) {
        try {
            /**
             * @Description: 通過反射獲取物件
             * @Author: xz
             * @Date: 2020/5/31 21:06
             */
            //獲取class物件
            Class<LazyDoubleCheckSingleton> lazySingletonClass = LazyDoubleCheckSingleton.class;
            //通過獲取的class物件,獲取宣告的構造器
            Constructor<LazyDoubleCheckSingleton> declaredConstructor =  lazySingletonClass.getDeclaredConstructor();
            //去除構造器的私有許可權
            declaredConstructor.setAccessible(true);
            
            /**
             * @Description:  直接通過類名.getInstance方法獲取物件
             * @Author: xz
             * @Date: 2020/5/31 21:07
             */
            LazyDoubleCheckSingleton instance=LazyDoubleCheckSingleton.getInstance();

            //修改懶漢式的私有屬性
            Field  flag = instance.getClass().getDeclaredField("flag");
            flag.setAccessible(true);
            flag.set(instance,true);
            
            //通過構造器建立物件
            LazyDoubleCheckSingleton newInstance = declaredConstructor.newInstance();
            
            //輸出2個物件,並判斷是否相等
            System.out.println(newInstance);
            System.out.println(instance);
            System.out.println(newInstance==instance);

        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }  catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
}