ends Enum
{
public static EnumSingleton[] values()
{
return (EnumSingleton[])$VALUES.clone();
}
public static EnumSingleton valueOf(String name)
{
return (EnumSingleton)Enum.valueOf(com/eamon/javadesignpatterns/singleton/enums/EnumSingleton, name);
}
private EnumSingleton(String s, int i)
{
super(s, i);
instance = new EnumResource();
}
public Object getInstance()
{
return instance;
}
public static final EnumSingleton INSTANCE;
private Object instance;
private static final EnumSingleton $VALUES[];
static
{
INSTANCE = new EnumSingleton("INSTANCE", 0);
$VALUES = (new EnumSingleton[] {
INSTANCE
});
}
}
请注意这段代码:
static
{
INSTANCE = new EnumSingleton("INSTANCE", 0);
$VALUES = (new EnumSingleton[] {
INSTANCE
});
}
原来枚举类单例在静态代码块中就给INSTANCE
赋了值,是饿汉式单例的实现方式。那么同样的,我们能否通过反射和序列化方式进行破坏呢?
先分析通过序列化方式:
我们还是回到JDK
源码:在 ObjectInputStream
的 readObject0()
方法中有如下代码:
private Object readObject0(boolean unshared) throws IOException {
...
case 126:
var4 = this.checkResolve(this.readEnum(unshared));
...
return var4;
}
我们看到 readObject0()
中调用了readEnum()
方法,跟进该方法:
private Enum<?> readEnum(boolean unshared) throws IOException {
if (this.bin.readByte() != 126) {
throw new InternalError();
} else {
ObjectStreamClass desc = this.readClassDesc(false);
if (!desc.isEnum()) {
throw new InvalidClassException("non-enum class: " + desc);
} else {
int enumHandle = this.handles.assign(unshared ? unsharedMarker : null);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
this.handles.markException(enumHandle, resolveEx);
}
String name = this.readString(false);
Enum<?> result = null;
Class<?> cl = desc.forClass();
if (cl != null) {
try {
Enum<?> en = Enum.valueOf(cl, name);
result = en;
} catch (IllegalArgumentException var9) {
throw (IOException)(new InvalidObjectException("enum constant " + name + " does not exist in " + cl)).initCause(var9);
}
if (!unshared) {
this.handles.setObject(enumHandle, result);
}
}
this.handles.finish(enumHandle);
this.passHandle = enumHandle;
return result;
}
}
}
我们发现枚举类型其实通过类名和 Class 对象类找到一个唯一的枚举对象。因此,枚举对象不可能被类加载器加载多次。
那么是否可以通过反射进行破坏呢?我们先来执行以下反射破坏枚举类的测试代码:
@Test
public void testEnum(){
try {
// 很无聊的情况下,进行破坏
Class<EnumSingleton> clazz = EnumSingleton.class;
// 通过反射拿到私有的构造方法
Constructor<EnumSingleton> c = clazz.getDeclaredConstructor(null);
// 设置访问属性,强制访问
c.setAccessible(true);
// 暴力初始化两次,这就相当于调用了两次构造方法
EnumSingleton o1 = c.newInstance();
EnumSingleton o2 = c.newInstance();
// 只要 o1和o2 地址不相等,就可以说明这是两个不同的对象,也就是违背了单例模式的初衷
System.out.println(o1 == o2);
} catch (Exception e) {
e.printStackTrace();
}
}
执行结果:

报的是 java.lang.NoSuchMethodException
异常,意思是没找到无参的构造方法。
那么我们来看一下 java.lang.Enum
的源码,我们发现它只有一个protected
的构造方法:
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
那我们来做一个这样的测试:
@Test
public void testEnum1() {
try {
Class clazz = EnumSingleton.class;
Constructor c = clazz.getDeclaredConstructor(String.class, int.class);
c.setAccessible(true);
EnumSingleton enumSingleton = (EnumSingleton) c.newInstance("Eamon", 666);
} catch (Exception e) {
e.printStackTrace();
}
}
发现控制台输出如下错误:

意思就是不能用反射来创建枚举类型。至于为什么,我们还是来