反射如何破壞單例模式

一個單例類:java

 
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 語言訪問檢查,使得私有的構造函數可以被訪問,這樣使得單例模式失效。spa

 

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

 
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是爲了防止多線程狀況下實例化多個對象。
相關文章
相關標籤/搜索