【发布时间】:2020-02-27 15:44:12
【问题描述】:
我的直截了当的问题是:由于反射现在受到限制,考虑将 Enum 用于单例实现是否仍然有意义?
通过单例实现的抛出枚举,我的意思是一些实现,例如:
public enum SingletonEnum {
INSTANCE;
int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
如果我们对比answer related to scope package access“... Jigsaw 的可访问性规则现在仅限制对公共元素(类型、方法、字段)的访问”以及由枚举修复的反射问题,我们可能想知道为什么仍然将单例编码为枚举。
尽管它很简单,但在序列化枚举时,字段变量不会被序列化。除此之外,枚举不支持延迟加载。
总而言之,假设我上面没有说任何愚蠢的话,因为将枚举用于单例的主要优点是防止反射风险,我会得出结论,将单例编码为枚举并不比围绕这样的静态方法的简单实现:
当需要序列化时
public class DemoSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private DemoSingleton() {
// private constructor
}
private static class DemoSingletonHolder {
public static final DemoSingleton INSTANCE = new DemoSingleton();
}
public static DemoSingleton getInstance() {
return DemoSingletonHolder.INSTANCE;
}
protected Object readResolve() {
return getInstance();
}
}
当不涉及序列化时,复杂对象也不需要延迟加载
public class Singleton {
public static final Singleton INSTANCE = new Singleton();
private Singleton() {}
}
*** 已编辑:在@Holger 评论关于序列化之后添加
public class DemoSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private DemoSingleton() {
// private constructor
}
private static class DemoSingletonHolder {
public static final DemoSingleton INSTANCE = new DemoSingleton();
}
public static DemoSingleton getInstance() {
return DemoSingletonHolder.INSTANCE;
}
protected Object readResolve() {
return getInstance();
}
private int i = 10;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
public class DemoSingleton implements Serializable {
private volatile static DemoSingleton instance = null;
public static DemoSingleton getInstance() {
if (instance == null) {
instance = new DemoSingleton();
}
return instance;
}
private int i = 10;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
【问题讨论】:
-
“将枚举用于单例的主要优点是防止反射风险”但是,如果您将单例反序列化两次,这意味着什么?你怎么能停止这样做,并以多个实例结束?
-
据我了解,序列化场景中的枚举毫无价值(我在上面写了“......当序列化枚举时,字段变量没有被序列化......”)。假设我是对的,你的问题在这里不适用,因为它不是枚举的 PRO
-
我的观点是可变单例与序列化不兼容,因为您无法协调单例的当前状态与反序列化时加载的状态之间的差异。要么踩住当前状态(对于不希望它改变的人感到惊讶),放弃反序列化状态(为什么还要序列化状态),或者创建另一个实例(它不是单例)。
-
首先,
enum类型和其他单例示例一样懒惰。没有区别。此外,您的可序列化单例示例毫无意义。它将反序列化存储的值,然后将反序列化的对象替换为已经存在的对象,就像enum一开始会做的那样。除了临时替换是脆弱的,你的“单身”有两个对象存在。 -
在调用
readResolve()时,您有两个DemoSingleton实例,一个调用readResolve(),另一个存储在DemoSingletonHolder.INSTANCE中。这本身就是单例不变量的矛盾。正如@AndyTurner 所说,无论您将使用谁的对象状态,您都会打破某人的期望。更不用说如果两个线程同时反序列化两个不同版本的DemoSingleton状态会发生什么。
标签: java enums singleton java-module java-platform-module-system