关于老师讲到的单例模式的一些问题
来源:5-6 无视反射和序列化攻击的单例

qq_慕前端3164363
2020-04-04
我看另一个提问当中,老师回答说,加装了枚举的饿汉模式中,真正的单例是指内部枚举持有的单例,在后面的验证代码中也有所体现
System.out.println(EnumStarvingSingleton.getInstance());
Class clazz = EnumStarvingSingleton.class;
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
EnumStarvingSingleton enumStarvingSingleton = (EnumStarvingSingleton)constructor.newInstance();
System.out.println(enumStarvingSingleton.getInstance());
无论是EnumStarvingSingleton本身,还是通过反射创建enumStarvingSingleton实例,都是通过调用getInstance()方法来获取枚举中持有的实例的。
但是我也就产生了针对上一节课中普通饿汉模式的一些疑问。在上一节课中,对于普通饿汉模式的单例遭到破坏的演示代码如下:
System.out.println(StarvingSingleton.getInstance());
Class clazz = StarvingSingleton.class;
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
System.out.println(constructor.newInstance());
显然输出的两个实例是不同的,因为第一个实例是由StarvingSingleton类中的成员变量instance所指向的一个实例,第二个实例是通过反射创建出来的新对象。
但是我发现,如果再次调用通过反射创建出来的新对象的getInstance()方法,同样可以保证单例“不被破坏”,只需要将上面代码中的
System.out.println(constructor.newInstance());
修改为
System.out.println(constructor.newInstance().getInstance());
即可,这样子输出的两个实例就有是同一个对象了,我也简单梳理了一下内存中的实际情况
第一次单例遭到破坏的代码中,对比的是实例A和实例B,显然不是同一个对象,但是如果实例B调用也getInstace方法,返回的又是实例A,两者就又会相同。
所以这样子是不是和加装了枚举的饿汉模式会有一些相似性?
- 普通的饿汉模式中,真正的单例是成员变量instance持有的单例
- 加装了枚举的饿汉模式中,真正的单例是指内部枚举持有的单例
普通的饿汉模式中,我可以调用getInstance()方法(类似于加装了枚举的饿汉模式)得到唯一的实例,虽然它的外层依然无法防御反射的攻击,但是内部的成员变量instance是无法修改的(类似于加装了枚举的饿汉模式中的内部枚举),这样子看来普通的饿汉模式似乎也是可以“无视反射”的了?
3回答
-
orzzzz
2020-04-14
Class enumClass = Class.forName("org.simpleframework.core.BeanContainer$ContainerHolder"); Field holderField = enumClass.getField("HOLDER"); holderField.setAccessible(true); Object enumValue = holderField.get(null); Field enumField = enumClass.getDeclaredField("instance"); enumField.setAccessible(true); enumField.set(enumValue, null);
00 -
翔仔
2020-04-06
同学好,太棒了,还配合了自己的画图:)针对同学的问题,对于普通饿汉模式来讲,如果我通过反射去获取到类里面的这个私有成员变量instance,然后替换掉它的值,之后别人通过getInstance方法获取的时候值也会改变呢
042020-12-06 -
orzzzz
2020-04-05
我看的时候也有类似的疑问,后面在网上看了一下,发现他们是直接用枚举实现的,感觉这样是可以无视反射的
public enum Singleton { INSTANCE; public void doSomething() { System.out.println("doSomething"); } }
00
相似问题