最安全的单例模式-枚举

时间:2021-1-18 作者:admin

枚举实现

首先我们先看一下枚举实现单例模式


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;
    }

所以各位小伙伴明白为什么枚举是最安全的了吧,反射机制的底层就规定了我们的枚举没法进行反射获取!

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。