反射如何破壞單例模式

一個單例類:

public class Singleton {
    private static Singleton instance = new Singleton();  
 
    private Singleton() {}
 
    public static Singleton getInstance() {
        return instance;
    }
}

通過反射破壞單例模式:

public class Test {
    public static void main(String[] args) throws Exception{
        Singleton s1 = Singleton.getInstance();
 
        Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton s2 = constructor.newInstance();
 
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());
 
    }
}
 
輸出結果:
671631440
935563443

結果表明s1和s2是兩個不同的實例了。

通過反射獲得單例類的構造函數,由於該構造函數是private的,通過setAccessible(true)指示反射的對象在使用時應該取消 Java 語言訪問檢查,使得私有的構造函數能夠被訪問,這樣使得單例模式失效。

如果要抵禦這種攻擊,要防止構造函數被成功調用兩次。需要在構造函數中對實例化次數進行統計,大於一次就拋出異常。

public class Singleton {
    private static int count = 0;
 
    private static Singleton instance = null;
 
    private Singleton(){
        synchronized (Singleton.class) {
            if(count > 0){
                throw new RuntimeException("創建了兩個實例");
            }
            count++;
        }
 
    }
 
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
 
    public static void main(String[] args) throws Exception {
 
        Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton s1 = constructor.newInstance();
        Singleton s2 = constructor.newInstance();
    }
 
}

執行結果:
Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at com.yzz.reflect.Singleton.main(Singleton.java:33)
Caused by: java.lang.RuntimeException: 創建了兩個實例
    at com.yzz.reflect.Singleton.<init>(Singleton.java:14)
    ... 5 more

在通過反射創建第二個實例時拋出異常,防止實例化多個對象。構造函數中的synchronized是爲了防止多線程情況下實例化多個對象。

轉載出自於:http://www.cnblogs.com/wyb628/p/6371827.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章