一個單例類: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是爲了防止多線程狀況下實例化多個對象。