请稍等 ...
×

采纳答案成功!

向帮助你的同学说点啥吧!感谢那些助人为乐的人

关于老师讲到的单例模式的一些问题

我看另一个提问当中,老师回答说,加装了枚举的饿汉模式中,真正的单例是指内部枚举持有的单例,在后面的验证代码中也有所体现

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

3回答

orzzzz 2020-04-14 21:57:44
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);
0 回复 有任何疑惑可以回复我~
翔仔 2020-04-06 12:00:21

同学好,太棒了,还配合了自己的画图:)针对同学的问题,对于普通饿汉模式来讲,如果我通过反射去获取到类里面的这个私有成员变量instance,然后替换掉它的值,之后别人通过getInstance方法获取的时候值也会改变呢

0 回复 有任何疑惑可以回复我~
  • orzzzz #1
    老师,我发现通过上面的代码也可以修改加了枚举的饿汉模式中instance的值(因为回复里面不能加代码格式,就写在上面的)
    回复 有任何疑惑可以回复我~ 2020-04-14 22:00:46
  • 翔仔 回复 orzzzz #2
    同学好,咱们这里主要是保证修改不了枚举值就可以了
    回复 有任何疑惑可以回复我~ 2020-04-14 22:24:04
  • 老师这个私有的instance是final的 通过反射能够修改他的值吗
    回复 有任何疑惑可以回复我~ 2020-12-06 18:14:59
orzzzz 2020-04-05 17:05:28

我看的时候也有类似的疑问,后面在网上看了一下,发现他们是直接用枚举实现的,感觉这样是可以无视反射的

public enum Singleton {
    INSTANCE;    
    public void doSomething() {
        System.out.println("doSomething");
    }
}
0 回复 有任何疑惑可以回复我~
问题已解决,确定采纳
还有疑问,暂不采纳
意见反馈 帮助中心 APP下载
官方微信