首先我們還是拿餓漢模式作為栗子進行測試,餓漢模式的代碼如下:
public class HungrySingleton implements Serializable { private static final HungrySingleton instance; static { instance = new HungrySingleton(); } private HungrySingleton(){ } public static HungrySingleton getInstance(){ return instance; } private Object readResolve(){ return instance; }
1、先寫一個利用反射獲取實例的方法和直接獲取實例的方法,將兩者的返回值進行比較,看返回的是否是一個實例。
代碼如下:
public class Testreflection { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class object = HungrySingleton.class; Constructor constructor = object.getDeclaredConstructor(); constructor.setAccessible(true); HungrySingleton instance = HungrySingleton.getInstance(); HungrySingleton newInstance = (HungrySingleton) constructor.newInstance(); System.out.println(instance); System.out.println(newInstance); System.out.println(instance == newInstance); } }
運行后的結果為:可見,兩者的結果並不是一個對象,則餓漢模式毅然受到了反射的攻擊。
2、那么改如何解決這反射攻擊呢?我們知道是在類加載的時候就加載了這個實例的,因為是在類加載的時候就生成了詞實例,那么我們可以在構造器里面加一個判斷,進行反射防御。代碼如下:
測試結果為:
這種方式有一個特點,也就是它對類加載這個時刻就把對象創建好的這種類是ok的,靜態內部類的單例也可以用。
對於不是靜態類的也需要解決下,要根據創建實例的順序進行解決。但是無論如何反射都可以訪問到類的方法和變量,進行修改,所以非
類加載這個時刻就把對象創建好的這種類,是不能防止反射攻擊的。