精彩文章免费看

C05 单例模式 Enum枚举单例(二) 抗反射攻击分析

枚举单例抗反射攻击演示(一)

NoSuchMethodException 报的是枚举类 EnumInstance 中没有
getDeclaredConstructor() 这个方法;
  • 意思是 EnumInstance 枚举类中没有无参构造器;
  • public enum EnumInstance {
        INSTANCE;
        private Object data;
        public Object getData() {
            return data;
        public void setData(Object data) {
            this.data = data;
        public static EnumInstance getInstance() {
            return INSTANCE;
    import java.lang.reflect.Constructor;
    public class Test {
        public static void main(String[] args) throws Exception {
            reflectionAttack();
        public static void reflectionAttack() throws Exception {
            Class objectClass = EnumInstance.class;
            Constructor constructor = objectClass.getDeclaredConstructor();
            constructor.setAccessible(true);
    

    Exception in thread "main" java.lang.NoSuchMethodException: designpattern.creational.singleton.enuminstance.EnumInstance.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.getDeclaredConstructor(Class.java:2178)
    at designpattern.creational.singleton.enuminstance.Test.reflectionAttack(Test.java:14)
    at designpattern.creational.singleton.enuminstance.Test.main(Test.java:9)

    攻击失败源码分析(一)

    Enum 类中只有一个构造器,该构造器有 2 个参数,所以在用 objectClass.getDeclaredConstructor() 获取 Enum 的无参构造器时是无法获得的;
  • 获取到了 Enum 类的构造器还是无法用反射创建出EnumInstance 类的实例;
  • 这次报的错不一样,是 IllegalArgumentException: Cannot reflectively create enum objects,说得很明显,不允许以反射的方式创建 enum 实例;
  • public class Test {
        public static void main(String[] args) throws Exception {
            reflectionAttack2();
        public static void reflectionAttack2() throws Exception {
            Class objectClass = EnumInstance.class;
            Constructor constructor = objectClass.getDeclaredConstructor(String.class, int.class);
            constructor.setAccessible(true);
            EnumInstance instance = (EnumInstance)constructor.newInstance("", 0);
    

    Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
    at designpattern.creational.singleton.enuminstance.Test.reflectionAttack2(Test.java:16)
    at designpattern.creational.singleton.enuminstance.Test.main(Test.java:9)

    攻击失败源码分析(二)

    java.lang.reflect.Constructor 类在 newInstance(Object ... initargs) 方法中针对需要被反射创建的对象的类型,专门针对 Enum 做了判断,在 Java 层面禁止了反射创建枚举实例的可能;
    getInstance() 方法;
  • public static final EnumInstance INSTANCE;
  • 在静态代码块中初始化,饿汉模式(可用防御代码抵御反射攻击);
  • // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) // Source File Name: EnumInstance.java package designpattern.creational.singleton.enuminstance; public final class EnumInstance extends Enum public static EnumInstance[] values() return (EnumInstance[])$VALUES.clone(); public static EnumInstance valueOf(String name) return (EnumInstance)Enum.valueOf(designpattern/creational/singleton/enuminstance/EnumInstance, name); private EnumInstance(String s, int i) super(s, i); public Object getData() return data; public void setData(Object data) this.data = data; public static EnumInstance getInstance() return INSTANCE; public static final EnumInstance INSTANCE; private Object data; private static final EnumInstance $VALUES[]; static INSTANCE = new EnumInstance("INSTANCE", 0); $VALUES = (new EnumInstance[] { INSTANCE

    在枚举单例中定义方法

  • 定义完的方法要在 EnumInstance 中定义抽象方法;
  • public enum EnumInstance {
        INSTANCE {
            protected void printTest() {
                System.out.println("EmumInstance.INSTANCE.printTest()...");
        protected abstract void printTest();
        private Object data;
        public Object getData() {
            return data;
        public void setData(Object data) {
            this.data = data;
        public static EnumInstance getInstance() {
            return INSTANCE;
    public class Test {
        public static void main(String[] args) throws Exception {
            EnumInstance instance = EnumInstance.getInstance();
            instance.printTest();
    

    EmumInstance.INSTANCE.printTest()...

    反编译带方法的EnumInstance

  • 变成抽象类了,所以要把定义好的方法声明成抽象方法;
  • // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) // Source File Name: EnumInstance.java package designpattern.creational.singleton.enuminstance.addmethod; import java.io.PrintStream; public abstract class EnumInstance extends Enum public static EnumInstance[] values() return (EnumInstance[])$VALUES.clone(); public static EnumInstance valueOf(String name) return (EnumInstance)Enum.valueOf(designpattern/creational/singleton/enuminstance/addmethod/EnumInstance, name); private EnumInstance(String s, int i) super(s, i); protected abstract void printTest(); public Object getData() return data; public void setData(Object data) this.data = data; public static EnumInstance getInstance() return INSTANCE; public static final EnumInstance INSTANCE; private Object data; private static final EnumInstance $VALUES[]; static INSTANCE = new EnumInstance("INSTANCE", 0) { protected void printTest() System.out.println("EmumInstance.INSTANCE.printTest()..."); $VALUES = (new EnumInstance[] { INSTANCE