首先我们先看一下枚举实现单例模式
public enum EnumSingleton {
INSTANCE;
// 枚举能够绝对有效的防止实例化多次,和防止反射和序列化破解
public void add() {
System.out.println("add方法...");
}
}
Test类
public class Test {
public static void main(String[] args) {
EnumSingleton instance1 = EnumSingleton.INSTANCE;
EnumSingleton instance2 = EnumSingleton.INSTANCE;
System.out.println(instance1==instance2);
}
}
1、上一篇博客中提到了我们可以通过JAVA的反射机制去破解我们的单例模式
https://blog.csdn.net/weixin_45498245/article/details/112742030
2、我们可以用反射去破解一下枚举
public class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException {
Class<?> aClass = Class.forName("单例模式.枚举实现单例.EnumSingleton");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);//设置构造权限为可以访问
EnumSingleton enumSingleton= (EnumSingleton) declaredConstructor.newInstance();//Singleton2 with modifiers "private"
System.out.println(enumSingleton);
}
}
Exception in thread “main” java.lang.NoSuchMethodException: 单例模式.枚举实现单例.EnumSingleton.()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
at 单例模式.破解单例.Test.main(Test.java:26)
这是为什么呢?我们接着往下看枚举的源码分析
结果如下,这是为什么呢 我们通过反编译工具可以看一下枚举的代码(这是通过反编译工具去反编译了一下我们上边的EnumSingleton.class)
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumSingleton.java
package 53554F8B6A215F0F.679A4E3E5B9E73B053554F8B;
import java.io.PrintStream;
public final class EnumSingleton extends Enum//可以看出我们的枚举无非也是一个类去实现了一个Enum父类
{
public static EnumSingleton[] values()
{
return (EnumSingleton[])$VALUES.clone();
}
//根据我们枚举中定义的名字去拿类
public static EnumSingleton valueOf(String name)
{
return (EnumSingleton)Enum.valueOf(53554F8B6A215F0F/679A4E3E5B9E73B053554F8B/EnumSingleton, name);
}
//有参构造,这里可以发现枚举是没有默认的无参构造的 i是一个序号
private EnumSingleton(String s, int i)
{
super(s, i);
}
public void add()
{
System.out.println("add\u65B9\u6CD5...");
}
private static final EnumSingleton $VALUES[]; //存放我们对象的数组,然后去初始化
//可以看到枚举的话是在静态代码块中去初始化的
static
{
INSTANCE = new EnumSingleton("INSTANCE", 0);
$VALUES = (new EnumSingleton[] {
INSTANCE
});
}
}
看完源码之后那么我们通过反射去测试加载有参构造(因为通过上边我们看出来枚举是没有无参构造的,只有一个有参构造,那么我们就通过反射对有参构造下手)
//有参构造去破解枚举的单例
public class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException {
Class<?> aClass = Class.forName("单例模式.枚举实现单例.EnumSingleton");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class,int.class);//这次我们测试去破解有参构造
declaredConstructor.setAccessible(true);//设置构造权限为可以访问
EnumSingleton enumSingleton= (EnumSingleton) declaredConstructor.newInstance("123",1);//Singleton2 with modifiers "private"
System.out.println(enumSingleton);
}
}
结果如何呢?
Cannot reflectively create enum objects
会给我们报错,意思是没法去反射枚举类
为什么呢?我们看一下反射中的newInstance源码:
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
// !!!这里我们看到如果反射的类是枚举的话,会直接抛出异常!!!
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
所以各位小伙伴明白为什么枚举是最安全的了吧,反射机制的底层就规定了我们的枚举没法进行反射获取!