【java基础】枚举

枚举的定义

public enum Color {
    Red,Blue,Green;
}

枚举的使用

Color red = Color.Red;//枚举的其中一个类型
Color[] values = Color.values();//获取所有的枚举类型
String name = red.name();//可以获得枚举值的名称
int ordinal = red.ordinal();//可以获得枚举值的编号

原理

那我们定义枚举类型后,到底发生了什么呢?我们对枚举的实现原理进行探究。
我们来解析下Color.class文件,命令javap Color

public final class Color extends java.lang.Enum<Color> {
  public static final Color Red;
  public static final Color Blue;
  public static final Color Green;
  public static final Color[] $VALUES;
  public static Color[] values();
  public static Color valueOf(java.lang.String);
  static {};
}

从解析后的文件中我们可以知道

  1. 枚举类是final的,不能被继承
  2. 枚举类在经过编译后生成一个继承java.lang.Enum的类Color
  3. 编译后的枚举值,是该类的Color类型的成员变量,如Red、Bule、Green,并且是final static修饰
  4. 枚举编译后的类添加了静态的values()valueOf(java.lang.String)方法
  5. 静态代码块static {}

进一步细化Color.class
命令javap -c Color

public final class Color extends java.lang.Enum<Color> {
  public static final Color Red;
  public static final Color Blue;
  public static final Color Green;
  public static Color[] values();
    Code:
       0: getstatic     #1                  // Field $VALUES:[LColor;
       3: invokevirtual #2                  // Method "[LColor;".clone:()Ljava/lang/Object;
       6: checkcast     #3                  // class "[LColor;"
       9: areturn

  public static Color valueOf(java.lang.String);
    Code:
       0: ldc           #4                  // class Color
       2: aload_0
       3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljav
a/lang/Enum;
       6: checkcast     #4                  // class Color
       9: areturn

  static {};
    Code:
       0: new           #4                  // class Color
       3: dup
       4: ldc           #7                  // String Red
       6: iconst_0
       7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      10: putstatic     #9                  // Field Red:LColor;
      13: new           #4                  // class Color
      16: dup
      17: ldc           #10                 // String Blue
      19: iconst_1
      20: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      23: putstatic     #11                 // Field Blue:LColor;
      26: new           #4                  // class Color
      29: dup
      30: ldc           #12                 // String Green
      32: iconst_2
      33: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      36: putstatic     #13                 // Field Green:LColor;
      
      39: iconst_3
      40: anewarray     #4                  // class Color
      43: dup
      
      44: iconst_0
      45: getstatic     #9                  // Field Red:LColor;
      48: aastore
      49: dup
      50: iconst_1
      51: getstatic     #11                 // Field Blue:LColor;
      54: aastore
      55: dup
      56: iconst_2
      57: getstatic     #13                 // Field Green:LColor;
      60: aastore
      61: putstatic     #1                  // Field $VALUES:[LColor;
      64: return
}

还原后的代码如下:

public final class Color extends java.lang.Enum<Color> {
    //定义的枚举成员
    public static final Color Red;
    public static final Color Blue;
    public static final Color Green;
    //编译器自动生成的 javap -c 还查不出来,疑惑
    public static final /* synthetic */ Color[] $VALUES;//编译器自动生成的
    public static Color[] values(){
        /**
         * 0: getstatic     #1                  // Field $VALUES:[LColor;
         * 3: invokevirtual #2                  // Method "[LColor;".clone:()Ljava/lang/Object;
         * 6: checkcast     #3                  // class "[LColor;"
         * 9: areturn
         */
        return $VALUES.clone();
    }
    public static Color valueOf(java.lang.String s){
        /**
         * 0: ldc           #4                  // class Color
         * 2: aload_0
         * 3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljav
         * a/lang/Enum;
         * 6: checkcast     #4                  // class Color
         * 9: areturn
         */
        return Enum.valueOf(Color.class,s);
    }
    protected Color(String name, int ordinal) {
        super(name, ordinal);
    }
    static {
        /**
         * 0: new           #4                  // class Color
         * 3: dup
         * 4: ldc           #7                  // String Red
         * 6: iconst_0
         * 7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
         * 10: putstatic     #9                  // Field Red:LColor;
         */
        Red=new Color("Red",0);
        /**
         * 13: new           #4                  // class Color
         * 16: dup
         * 17: ldc           #10                 // String Blue
         * 19: iconst_1
         * 20: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
         * 23: putstatic     #11                 // Field Blue:LColor;
         */
        Blue=new Color("Blue",1);
        /**
         * 26: new           #4                  // class Color
         * 29: dup
         * 30: ldc           #12                 // String Green
         * 32: iconst_2
         * 33: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
         * 36: putstatic     #13                 // Field Green:LColor;
         */
        Green=new Color("Green",2);
        /**
         * 39: iconst_3
         * 40: anewarray     #4                  // class Color
         * 43: dup
         * 44: iconst_0
         * 45: getstatic     #9                  // Field Red:LColor;
         * 48: aastore
         * 49: dup
         * 50: iconst_1
         * 51: getstatic     #11                 // Field Blue:LColor;
         * 54: aastore
         * 55: dup
         * 56: iconst_2
         * 57: getstatic     #13                 // Field Green:LColor;
         * 60: aastore
         * 61: putstatic     #1                  // Field $VALUES:[LColor;
         */
        $VALUES=new Color[]{Red,Blue,Green};
    };
}

通过上面还原后的代码可知,在类的static代码块中编译器帮我们生成枚举中的每个成员,实际上就是生成对象并赋值给静态变量。

枚举的扩展

单例模式

枚举其实就是编译帮我们在静态代码块中创建一个或多个枚举成员对象,如果我们只定义一个枚举成员,这样就是一个单例对象

public enum Singleton {
    INSTANCE;
    public void doSometing(){
        System.out.println("doing");
    }
}

代码如此简单,不仅如此,这样的方式还可以解决反射攻击反序列化导致的单例失败的问题。

避免反射攻击

在获取实例时进行了枚举判断,如果是枚举类型的则直接抛出异常。

Jdk1.8 Class.clss 文件的416..417

public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException{
        ....
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        .....
        return inst;
    }

反序列化

枚举的序列时仅降枚举对象的name属性输出到结果中,在反序列化时则是通过java.lang.EnumvalueOf方法来根据名字在jvm中查找对象的。

同时编译器还禁止对枚举的定制化序列机制,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。

策略模式

一种行为,多种结果,不同结果的实现,名曰策略。

public enum Calculator {
    //加法运算
    ADD("+"){
        public int exec(int a,int b){
            return a+b;
        }
    },
    //减法运算
    SUB("-"){
        public int exec(int a,int b){
            return a - b;
        }
    };
    String value = "";
    //定义成员值类型
    Calculator(String _value){
        this.value = _value;
    }
    //获得枚举成员的值
    public String getValue(){
        return this.value;
    }
    //声明一个抽象函数
    public abstract int exec(int a,int b);
}

总结

  1. 枚举其实就是编写代码方面的语法糖,仍然是一个完整的类,暴露了几个静态变量,隐藏掉了大部分细节
  2. 枚举的成员变量是在编译后的静态代码块逐个创建实例来实现的
  3. 枚举的values()valueOf()方法都是编译器帮我们创建出来的
  4. 在调用values()方法时,返回的对象是克隆出来的一份新的,修改对原对象没有影响
  5. 枚举可以实现单例模式,能避免反射攻击和反序列化问题
  6. 避免反射攻击是因为枚举类不允许通过反射实例化
  7. 反序列化是通过Enum.valueOf到jvm中根据class,name去查找的
  8. 枚举可以实现策略模式,优化代码,避免大量的if...else的出现
posted @ 2021-08-05 19:13  Hitechr  阅读(178)  评论(0编辑  收藏  举报