反射如何破壞單例模式


一個單例類:

 
        
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是為了防止多線程情況下實例化多個對象。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM