我看另一个提问当中,老师回答说,加装了枚举的饿汉模式中,真正的单例是指内部枚举持有的单例,在后面的验证代码中也有所体现
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,两者就又会相同。
所以这样子是不是和加装了枚举的饿汉模式会有一些相似性?
普通的饿汉模式中,我可以调用getInstance()方法(类似于加装了枚举的饿汉模式)得到唯一的实例,虽然它的外层依然无法防御反射的攻击,但是内部的成员变量instance是无法修改的(类似于加装了枚举的饿汉模式中的内部枚举),这样子看来普通的饿汉模式似乎也是可以“无视反射”的了?