静态内部类式
private Singleton(){if(InnerClass.singleton!=null){throw new RuntimeException("单例模式下 , 禁止使用反射创建新实例");}}
这两种方式通过更改构造器的代码都可以有效防止反射创建新实例
我们接下来对用双重检查锁式实现的单例模式进行测试 。为了展示懒汉式使用(改动构造函数来防止反射攻击)方法的漏洞 。我将类引用变量设置成了,方便测试代码获取打印到控制台
public class Singleton {public static volatile Singleton singleton;private Singleton() {if(singleton!=null){throw new RuntimeException("单例模式下 , 禁止使用反射创建新实例");}}public static Singleton getInstance() {if (singleton == null) {synchronized (Singleton.class){if(singleton==null){singleton = new Singleton();}}}return singleton;}}
测试代码如下 , 有详细的注释
public class ReflectTest {public static void main(String[] args) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException {//得到该类在内存中的字节码对象Class objectClass = Class.forName("com.xt.designmode.creational.singleton.doubleCheckClass.Singleton");//获取这个对象的构造器Constructor constructor = objectClass.getDeclaredConstructor();//暴力反射 , 解除私有限定constructor.setAccessible(true);//先使用反射获取的构造器创建一个实例Singleton reflectInstance = (Singleton) constructor.newInstance();//打印反射机制创造的实例System.out.println(reflectInstance);//打印此时单例类中的Singleton类引用变量singletonSystem.out.println(Singleton.singleton);//使用常规方式获取实例Singleton instance = Singleton.getInstance();//打印常规方式生成的实例System.out.println(instance);//对象类型 , == 比较的是地址if(instance != reflectInstance){System.out.printf("创建了两个实例\n");}else{System.out.printf("只创建了一个实例\n");}}}
如下图所示 , 创建了两个实例 , 是因为正常的双重检查锁式只是想实例化单例类中的一个引用变量 , 如果在单例类还没有实例化这个引用变量之前 , 反射就可以通过使用构造器创建无数个实例 , 直到有人使用常规的方式来将单例类中的引用变量实例化 。
本来懒汉式只是为了节约资源 , 等到真正有人使用他的时候才创建 , 但是没想到给反射钻了空子 。
既然他钻这个空子 , 我们就想别的办法 , 因为我们只需要一个实例 , 而懒汉式的实例都是通过构造函数来创建的 , 那我们只需要保证构造函数只会被调用一次就好了 。我们加个标记flag
public class Singleton {private static volatile Singleton singleton;private static boolean flag = true;private Singleton() {if(flag == true){flag = false;}else{throw new RuntimeException("单例模式下 , 禁止使用反射创建新实例");}}public static Singleton getInstance() {if (singleton == null) {synchronized (Singleton.class){if(singleton==null){singleton = new Singleton();}}}return singleton;}}
这样就限制了构造函数的使用次数 , 可以有效防止反射通过获取构造器来创建新实例 。
但是反射是可以获取指定类的一切属性和方法 , 所以也可以通过反射不断将flag置为true来破解这一限制
public class ReflectTest {public static void main(String[] args) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException, NoSuchFieldException {//得到该类在内存中的字节码对象Class objectClass = Class.forName("com.xt.designmode.creational.singleton.doubleCheckClass.Singleton");//获取这个对象的构造器Constructor constructor = objectClass.getDeclaredConstructor();//暴力反射 , 解除私有限定constructor.setAccessible(true);for(int i=0;i
- 8种 单例设计模式
- 钱某、韩某,破坏军婚,双双获刑! 中国十大军人犯罪案例
- 大渣男32年结婚105次,创下吉尼斯世界纪录 男人破坏吉尼斯记录
- 全民奇迹5转可以拿几阶,全民奇迹剑士五转多少级可以拿破坏
- 人类历史上曾出现过这样一次灾难,破坏力远胜瘟疫、海啸和战争 历史之最自然灾害
- 破坏神是什么?
- 什么是破坏版,暗黑破坏神2单机版死灵法师加点
- 内爆夺命5人,死者中惊现泰坦尼克当年遇难者的玄孙女丈夫 男子破坏吉尼斯记录
- 泰坦“魔咒” 破坏吉尼斯记录
- 一代天骄成吉思汗是怎样从破坏者到庇护者的