三種單例模式的終極寫法(包括反射攻擊)
阿新 • • 發佈:2021-02-08
技術標籤:Java-基礎
一、餓漢式的反射攻擊及解決方法
public class HungrySingleton { //直接宣告一個需要被單例的物件,在靜態塊中初始化物件 private final static HungrySingleton hungrySingleton; static { hungrySingleton=new HungrySingleton(); } //私有的構造器,不被外部所訪問 private HungrySingleton(){ // 防止反射攻擊 if(hungrySingleton !=null ){ throw new RuntimeException("單例構造器進位制通過反射呼叫。。。。"); } // 防止反射攻擊 } //提供一個外部獲取HungrySingleton物件的靜態方法 public static HungrySingleton getInstance(){ return hungrySingleton; } }
public class HungryTest { public static void main(String[] args) { try { /** * @Description: 通過反射獲取物件 * @Author: xz * @Date: 2020/5/31 21:06 */ //獲取class物件 Class<HungrySingleton> hungrySingletonClass = HungrySingleton.class; //通過獲取的class物件,獲取宣告的構造器 Constructor<HungrySingleton> declaredConstructor = hungrySingletonClass.getDeclaredConstructor(); //去除構造器的私有許可權 declaredConstructor.setAccessible(true); //通過構造器建立物件 HungrySingleton newInstance = declaredConstructor.newInstance(); /** * @Description: 直接通過類名.getInstance方法獲取物件 * @Author: xz * @Date: 2020/5/31 21:07 */ HungrySingleton instance=HungrySingleton.getInstance(); //輸出2個物件,並判斷是否相等 System.out.println(newInstance); System.out.println(instance); System.out.println(newInstance==instance); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }
二、靜態內部類的反射攻擊及解決方法
public class StaticInnerClassSingleton { //私有的構造器,不被外部所訪問 private StaticInnerClassSingleton(){ // 方式反射攻擊 if(InnerClass.staticInnerClassSingleton !=null){ throw new RuntimeException("單例構造器進位制反射呼叫"); } // 方式反射攻擊 } //私有的靜態內部類 private static class InnerClass{ //new一個私有的靜態的StaticInnerClassSingleton物件 private static StaticInnerClassSingleton staticInnerClassSingleton=new StaticInnerClassSingleton(); } //提供一個公用的對外暴露的方法 public static StaticInnerClassSingleton getInstance(){ //返回值為通過靜態內部內呼叫靜態成員 return InnerClass.staticInnerClassSingleton; } }
public static void main(String[] args) {
try {
/**
* @Description: 通過反射獲取物件
* @Author: xz
* @Date: 2020/5/31 21:06
*/
//獲取class物件
Class<StaticInnerClassSingleton> staticInnerClassSingletonClass = StaticInnerClassSingleton.class;
//通過獲取的class物件,獲取宣告的構造器
Constructor<StaticInnerClassSingleton> declaredConstructor = staticInnerClassSingletonClass.getDeclaredConstructor();
//去除構造器的私有許可權
declaredConstructor.setAccessible(true);
//通過構造器建立物件
StaticInnerClassSingleton newInstance = declaredConstructor.newInstance();
/**
* @Description: 直接通過類名.getInstance方法獲取物件
* @Author: xz
* @Date: 2020/5/31 21:07
*/
StaticInnerClassSingleton instance=StaticInnerClassSingleton.getInstance();
//輸出2個物件,並判斷是否相等
System.out.println(newInstance);
System.out.println(instance);
System.out.println(newInstance==instance);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
三、懶漢式的反射攻擊及解決方法
無法防止反射攻擊
public class LazyDoubleCheckSingleton {
//宣告一個需要被單例的物件,初始化時沒有被建立所以設定null
/** volatile修飾的成員變數在每次被執行緒訪問時,都強迫從共享記憶體中重讀該成員變數的值。
* 而且,當成員變數發生變化時,強迫執行緒將變化值回寫到共享記憶體。
* 這樣在任何時刻,兩個不同的執行緒總是看到某個成員變數的同一個值。
*/
private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton=null;
private static boolean flag =true;
//私有的構造器,不被外部所訪問
private LazyDoubleCheckSingleton(){
if(flag){
flag=false;
}else{
throw new RuntimeException("單例構造器進位制反射呼叫");
}
}
//提供一個外部獲取LazyDoubleCheckSingleton物件的靜態方法
public static LazyDoubleCheckSingleton getInstance(){
if (lazyDoubleCheckSingleton==null){
synchronized (LazyDoubleCheckSingleton.class){
if(lazyDoubleCheckSingleton==null){
lazyDoubleCheckSingleton=new LazyDoubleCheckSingleton();
}
}
}
return lazyDoubleCheckSingleton;
}
}
public class LazyDoubleCheckTest {
public static void main(String[] args) {
try {
/**
* @Description: 通過反射獲取物件
* @Author: xz
* @Date: 2020/5/31 21:06
*/
//獲取class物件
Class<LazyDoubleCheckSingleton> lazySingletonClass = LazyDoubleCheckSingleton.class;
//通過獲取的class物件,獲取宣告的構造器
Constructor<LazyDoubleCheckSingleton> declaredConstructor = lazySingletonClass.getDeclaredConstructor();
//去除構造器的私有許可權
declaredConstructor.setAccessible(true);
/**
* @Description: 直接通過類名.getInstance方法獲取物件
* @Author: xz
* @Date: 2020/5/31 21:07
*/
LazyDoubleCheckSingleton instance=LazyDoubleCheckSingleton.getInstance();
//修改懶漢式的私有屬性
Field flag = instance.getClass().getDeclaredField("flag");
flag.setAccessible(true);
flag.set(instance,true);
//通過構造器建立物件
LazyDoubleCheckSingleton newInstance = declaredConstructor.newInstance();
//輸出2個物件,並判斷是否相等
System.out.println(newInstance);
System.out.println(instance);
System.out.println(newInstance==instance);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}