为了账号安全,请及时绑定邮箱和手机立即绑定

枚举

标签:
Java

定义枚举类

//定义加减乘除四个枚举常量public enum Operator {
    ADD,SUBTRACT,MULTIPLY,DIVIDE
}

可以在枚举中定义普通方法抽象方法.

public enum Operator {

    ADD {        @Override
        public int calculate(int a, int b) {            return a + b;
        }
    }, SUBTRACT {        @Override
        public int calculate(int a, int b) {            return a - b;
        }
    }, MULTIPLY {        @Override
        public int calculate(int a, int b) {            return a * b;
        }
    }, DIVIDE {        @Override
        public int calculate(int a, int b) {            if (b == 0) throw new IllegalArgumentException("divisor must be not zero");            return a / b;
        }
    };    public abstract int calculate(int a, int b);
}

也同样可在枚举类中定义属性,构造方法.

public enum Operator {

    ADD("+") {        @Override
        public int calculate(int a, int b) {            return a + b;
        }
    }, SUBTRACT("-") {        @Override
        public int calculate(int a, int b) {            return a - b;
        }
    }, MULTIPLY("*") {        @Override
        public int calculate(int a, int b) {            return a * b;
        }
    }, DIVIDE("/") {        @Override
        public int calculate(int a, int b) {            if (b == 0) throw new IllegalArgumentException("divisor must be not zero");            return a / b;
        }
    };    private String operator;

    Operator(String operator) {        this.operator = operator;
    }    public abstract int calculate(int a, int b);    public String getOperator() {        return operator;
    }
}

实现原理

Java文件在编译后会生成.class字节码文件.
对该文件使用javap xxx.class命令可以进行查看.

Compiled from "Operator.java"public abstract class com.inkio.onepro.Operator extends java.lang.Enum<com.inkio.onepro.Operator> {  public static final com.inkio.onepro.Operator ADD;  public static final com.inkio.onepro.Operator SUBTRACT;  public static final com.inkio.onepro.Operator MULTIPLY;  public static final com.inkio.onepro.Operator DIVIDE;  public static com.inkio.onepro.Operator[] values();  public static com.inkio.onepro.Operator valueOf(java.lang.String);  public abstract int calculate(int, int);  public java.lang.String getOperator();
  com.inkio.onepro.Operator(java.lang.String, int, java.lang.String, com.inkio.onepro.Operator$1);  static {};
}
  • 可以看到,枚举类在经过编译后变成了一个抽象类,继承了java.lang.Enum类;

  • 我们所定义的枚举常量也变成了public static final属性,而且其类型为这个抽象类的类型,名字则还是枚举常量的名字.

  • 并且同时我们可以看见在我们的编译路径下看见四个内部类的.class文件.以及values()valutOf()方法.

    https://img1.sycdn.imooc.com//5d2c98300001cb4d04750075.jpg

    image.png


  • 构造方法由我们定义的两个参数变成三个参数.

  • 生成一个静态代码块.

我们可以通过javap -c -v Operator.class指令对文件进行反编译如下.

Classfile /Users/sean/Desktop/Enum/Operator.class
  Last modified Dec 20, 2018; size 1587 bytes
  MD5 checksum 0b06630621a6a49772e53495294f72cf
  Compiled from "Operator.java"public abstract class com.inkio.onepro.Operator extends java.lang.Enum<com.inkio.onepro.Operator>  minor version: 0  major version: 52  flags: ACC_PUBLIC, ACC_SUPER, ACC_ABSTRACT, ACC_ENUMConstant pool:
   #1 = Methodref          #5.#60         // com/inkio/onepro/Operator."<init>":(Ljava/lang/String;ILjava/lang/String;)V
   #2 = Fieldref           #5.#61         // com/inkio/onepro/Operator.$VALUES:[Lcom/inkio/onepro/Operator;
   #3 = Methodref          #62.#63        // "[Lcom/inkio/onepro/Operator;".clone:()Ljava/lang/Object;
   #4 = Class              #39            // "[Lcom/inkio/onepro/Operator;"
   #5 = Class              #64            // com/inkio/onepro/Operator
   #6 = Methodref          #29.#65        // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
   #7 = Methodref          #29.#66        // java/lang/Enum."<init>":(Ljava/lang/String;I)V
   #8 = Fieldref           #5.#67         // com/inkio/onepro/Operator.operator:Ljava/lang/String;
   #9 = Class              #68            // com/inkio/onepro/Operator$1
  #10 = String             #31            // ADD
  #11 = String             #69            // +
  #12 = Methodref          #9.#60         // com/inkio/onepro/Operator$1."<init>":(Ljava/lang/String;ILjava/lang/String;)V
  #13 = Fieldref           #5.#70         // com/inkio/onepro/Operator.ADD:Lcom/inkio/onepro/Operator;
  #14 = Class              #71            // com/inkio/onepro/Operator$2
  #15 = String             #33            // SUBTRACT
  #16 = String             #72            // -
  #17 = Methodref          #14.#60        // com/inkio/onepro/Operator$2."<init>":(Ljava/lang/String;ILjava/lang/String;)V
  #18 = Fieldref           #5.#73         // com/inkio/onepro/Operator.SUBTRACT:Lcom/inkio/onepro/Operator;
  #19 = Class              #74            // com/inkio/onepro/Operator$3
  #20 = String             #34            // MULTIPLY
  #21 = String             #75            // *
  #22 = Methodref          #19.#60        // com/inkio/onepro/Operator$3."<init>":(Ljava/lang/String;ILjava/lang/String;)V
  #23 = Fieldref           #5.#76         // com/inkio/onepro/Operator.MULTIPLY:Lcom/inkio/onepro/Operator;
  #24 = Class              #77            // com/inkio/onepro/Operator$4
  #25 = String             #35            // DIVIDE
  #26 = String             #78            // /
  #27 = Methodref          #24.#60        // com/inkio/onepro/Operator$4."<init>":(Ljava/lang/String;ILjava/lang/String;)V
  #28 = Fieldref           #5.#79         // com/inkio/onepro/Operator.DIVIDE:Lcom/inkio/onepro/Operator;
  #29 = Class              #80            // java/lang/Enum
  #30 = Utf8               InnerClasses
  #31 = Utf8               ADD
  #32 = Utf8               Lcom/inkio/onepro/Operator;
  #33 = Utf8               SUBTRACT
  #34 = Utf8               MULTIPLY
  #35 = Utf8               DIVIDE
  #36 = Utf8               operator
  #37 = Utf8               Ljava/lang/String;
  #38 = Utf8               $VALUES
  #39 = Utf8               [Lcom/inkio/onepro/Operator;
  #40 = Utf8               values
  #41 = Utf8               ()[Lcom/inkio/onepro/Operator;
  #42 = Utf8               Code
  #43 = Utf8               LineNumberTable
  #44 = Utf8               valueOf
  #45 = Utf8               (Ljava/lang/String;)Lcom/inkio/onepro/Operator;
  #46 = Utf8               <init>
  #47 = Utf8               (Ljava/lang/String;ILjava/lang/String;)V
  #48 = Utf8               Signature
  #49 = Utf8               (Ljava/lang/String;)V
  #50 = Utf8               calculate
  #51 = Utf8               (II)I
  #52 = Utf8               getOperator
  #53 = Utf8               ()Ljava/lang/String;
  #54 = Utf8               (Ljava/lang/String;ILjava/lang/String;Lcom/inkio/onepro/Operator$1;)V
  #55 = Utf8               <clinit>
  #56 = Utf8               ()V
  #57 = Utf8               Ljava/lang/Enum<Lcom/inkio/onepro/Operator;>;
  #58 = Utf8               SourceFile
  #59 = Utf8               Operator.java
  #60 = NameAndType        #46:#47        // "<init>":(Ljava/lang/String;ILjava/lang/String;)V
  #61 = NameAndType        #38:#39        // $VALUES:[Lcom/inkio/onepro/Operator;
  #62 = Class              #39            // "[Lcom/inkio/onepro/Operator;"
  #63 = NameAndType        #81:#82        // clone:()Ljava/lang/Object;
  #64 = Utf8               com/inkio/onepro/Operator
  #65 = NameAndType        #44:#83        // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
  #66 = NameAndType        #46:#84        // "<init>":(Ljava/lang/String;I)V
  #67 = NameAndType        #36:#37        // operator:Ljava/lang/String;
  #68 = Utf8               com/inkio/onepro/Operator$1
  #69 = Utf8               +
  #70 = NameAndType        #31:#32        // ADD:Lcom/inkio/onepro/Operator;
  #71 = Utf8               com/inkio/onepro/Operator$2
  #72 = Utf8               -
  #73 = NameAndType        #33:#32        // SUBTRACT:Lcom/inkio/onepro/Operator;
  #74 = Utf8               com/inkio/onepro/Operator$3
  #75 = Utf8               *
  #76 = NameAndType        #34:#32        // MULTIPLY:Lcom/inkio/onepro/Operator;
  #77 = Utf8               com/inkio/onepro/Operator$4
  #78 = Utf8               /
  #79 = NameAndType        #35:#32        // DIVIDE:Lcom/inkio/onepro/Operator;
  #80 = Utf8               java/lang/Enum
  #81 = Utf8               clone
  #82 = Utf8               ()Ljava/lang/Object;
  #83 = Utf8               (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
  #84 = Utf8               (Ljava/lang/String;I)V
{  public static final com.inkio.onepro.Operator ADD;
    descriptor: Lcom/inkio/onepro/Operator;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM  public static final com.inkio.onepro.Operator SUBTRACT;
    descriptor: Lcom/inkio/onepro/Operator;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM  public static final com.inkio.onepro.Operator MULTIPLY;
    descriptor: Lcom/inkio/onepro/Operator;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM  public static final com.inkio.onepro.Operator DIVIDE;
    descriptor: Lcom/inkio/onepro/Operator;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM  public static com.inkio.onepro.Operator[] values();
    descriptor: ()[Lcom/inkio/onepro/Operator;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #2                  // Field $VALUES:[Lcom/inkio/onepro/Operator;
         3: invokevirtual #3                  // Method "[Lcom/inkio/onepro/Operator;".clone:()Ljava/lang/Object;
         6: checkcast     #4                  // class "[Lcom/inkio/onepro/Operator;"
         9: areturn
      LineNumberTable:
        line 3: 0

  public static com.inkio.onepro.Operator valueOf(java.lang.String);
    descriptor: (Ljava/lang/String;)Lcom/inkio/onepro/Operator;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: ldc           #5                  // class com/inkio/onepro/Operator
         2: aload_0         3: invokestatic  #6                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
         6: checkcast     #5                  // class com/inkio/onepro/Operator
         9: areturn
      LineNumberTable:
        line 3: 0

  public abstract int calculate(int, int);
    descriptor: (II)I
    flags: ACC_PUBLIC, ACC_ABSTRACT  public java.lang.String getOperator();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0         1: getfield      #8                  // Field operator:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 37: 0

  com.inkio.onepro.Operator(java.lang.String, int, java.lang.String, com.inkio.onepro.Operator$1);
    descriptor: (Ljava/lang/String;ILjava/lang/String;Lcom/inkio/onepro/Operator$1;)V
    flags: ACC_SYNTHETIC
    Code:
      stack=4, locals=5, args_size=5
         0: aload_0         1: aload_1         2: iload_2         3: aload_3         4: invokespecial #1                  // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
         7: return
      LineNumberTable:
        line 3: 0

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=5, locals=0, args_size=0
         0: new           #9                  // class com/inkio/onepro/Operator$1
         3: dup         4: ldc           #10                 // String ADD
         6: iconst_0         7: ldc           #11                 // String +
         9: invokespecial #12                 // Method com/inkio/onepro/Operator$1."<init>":(Ljava/lang/String;ILjava/lang/String;)V
        12: putstatic     #13                 // Field ADD:Lcom/inkio/onepro/Operator;
        15: new           #14                 // class com/inkio/onepro/Operator$2
        18: dup        19: ldc           #15                 // String SUBTRACT
        21: iconst_1        22: ldc           #16                 // String -
        24: invokespecial #17                 // Method com/inkio/onepro/Operator$2."<init>":(Ljava/lang/String;ILjava/lang/String;)V
        27: putstatic     #18                 // Field SUBTRACT:Lcom/inkio/onepro/Operator;
        30: new           #19                 // class com/inkio/onepro/Operator$3
        33: dup        34: ldc           #20                 // String MULTIPLY
        36: iconst_2        37: ldc           #21                 // String *
        39: invokespecial #22                 // Method com/inkio/onepro/Operator$3."<init>":(Ljava/lang/String;ILjava/lang/String;)V
        42: putstatic     #23                 // Field MULTIPLY:Lcom/inkio/onepro/Operator;
        45: new           #24                 // class com/inkio/onepro/Operator$4
        48: dup        49: ldc           #25                 // String DIVIDE
        51: iconst_3        52: ldc           #26                 // String /
        54: invokespecial #27                 // Method com/inkio/onepro/Operator$4."<init>":(Ljava/lang/String;ILjava/lang/String;)V
        57: putstatic     #28                 // Field DIVIDE:Lcom/inkio/onepro/Operator;
        60: iconst_4        61: anewarray     #5                  // class com/inkio/onepro/Operator
        64: dup        65: iconst_0        66: getstatic     #13                 // Field ADD:Lcom/inkio/onepro/Operator;
        69: aastore        70: dup        71: iconst_1        72: getstatic     #18                 // Field SUBTRACT:Lcom/inkio/onepro/Operator;
        75: aastore        76: dup        77: iconst_2        78: getstatic     #23                 // Field MULTIPLY:Lcom/inkio/onepro/Operator;
        81: aastore        82: dup        83: iconst_3        84: getstatic     #28                 // Field DIVIDE:Lcom/inkio/onepro/Operator;
        87: aastore        88: putstatic     #2                  // Field $VALUES:[Lcom/inkio/onepro/Operator;
        91: return
      LineNumberTable:
        line 5: 0
        line 10: 15
        line 15: 30
        line 20: 45
        line 3: 60}
Signature: #57                          // Ljava/lang/Enum<Lcom/inkio/onepro/Operator;>;SourceFile: "Operator.java"InnerClasses:     static #24; //class com/inkio/onepro/Operator$4
     static #19; //class com/inkio/onepro/Operator$3
     static #14; //class com/inkio/onepro/Operator$2
     static #9; //class com/inkio/onepro/Operator$1

代码那么多,看起来比较繁琐,那么我们翻译一下,使用jad将这个文件转成java的.执行指令jad -sjava Operator.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:   Operator.javapackage com.inkio.onepro;public abstract class Operator extends Enum{    public static Operator[] values()
    {        return (Operator[])$VALUES.clone();
    }    public static Operator valueOf(String s)
    {        return (Operator)Enum.valueOf(com/inkio/onepro/Operator, s);
    }    private Operator(String s, int i, String s1)
    {        super(s, i);
        operator = s1;
    }    public abstract int calculate(int i, int j);    public String getOperator()
    {        return operator;
    }    public static final Operator ADD;    public static final Operator SUBTRACT;    public static final Operator MULTIPLY;    public static final Operator DIVIDE;    private String operator;    private static final Operator $VALUES[];    static 
    {
        ADD = new Operator("ADD", 0, "+") {            public int calculate(int i, int j)
            {                return i + j;
            }

        }
;
        SUBTRACT = new Operator("SUBTRACT", 1, "-") {            public int calculate(int i, int j)
            {                return i - j;
            }

        }
;
        MULTIPLY = new Operator("MULTIPLY", 2, "*") {            public int calculate(int i, int j)
            {                return i * j;
            }

        }
;
        DIVIDE = new Operator("DIVIDE", 3, "/") {            public int calculate(int i, int j)
            {                if(j == 0)                    throw new IllegalArgumentException("divisor must be not zero");                else
                    return i / j;
            }

        }
;
        $VALUES = (new Operator[] {
            ADD, SUBTRACT, MULTIPLY, DIVIDE
        });
    }
}

这下代码是不是变得高端大次上档气了,嘿嘿,那么继续分析~
首先,声明了四个public static final常量,并且在静态代码块中为其赋值,且静态代码块中还生成了一个$VALUES字段,并初始化为定义的枚举类型的所有枚举常量的数组.
并且声明了一个枚举类型数组的静态方法values()以及一个我们定义的枚举类型的静态方法valueOf(),我们可以通过values()方法获取这个枚举值的数组,它的实现就是在静态代码块中生成的$VALUES并初始化的过程,同样也可以直接通过valueOf方法得到定义的枚举类型常量,是通过强转实现的.
可以这样使用:

  Operator operator = Operator.valueOf(Operator.ADD.name());
        System.out.println(operator);

>>> ADD

再说说构造方法,明明我们定义的时候只有一个参数,为什么现在变成三个了呢?
其实我们在使用时可以发现,每个枚举类其实都定义了两个属性,一个name一个ordinal,

Operator.ADD.name()Operator.ADD.ordinal()

name表示我们定义枚举常量的名称,如ADDSUBTRACT.
ordinal表示的是一个顺序号,按照我们定义常量的顺序自动分配,从0开始,在枚举常量初始化时,会自动初始化这两个字段设置相应的值,再加上我们为构造函数设置的字段,所以构造方法就有三个参数了.

我们可以从Enum类的代码中看到,定义的name和ordinal属性都是final的,而且大部分方法也都是final的,特别是clone、readObject、writeObject这三个方法,这三个方法和枚举通过静态代码块来进行初始化一起,它保证了枚举类型的不可变性,不能通过克隆,不能通过序列化和反序列化来复制枚举,这能保证一个枚举常量只是一个实例,即是单例的,所以在effective java中推荐使用枚举来实现单例.

对于Android开发,枚举还有一个面试相关的问题,在Android中为什么不推荐使用枚举,说出原因.通过本文其实我们就可以知道,枚举占用的内存会多很多.所以推荐使用注解来代替枚举.@IntDef@StringDef等.



作者:Inkio
链接:https://www.jianshu.com/p/fe7891daeb36


点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消