枚举

作者: 墨白历险记 | 来源:发表于2018-12-20 11:34 被阅读4次

定义枚举类

//定义加减乘除四个枚举常量
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()方法.

    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_ENUM
Constant 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.java

package 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等.

如果本文对您有帮助,请您帮忙点个喜欢.谢谢~

相关文章

  • C#枚举及与枚举常数的转换

    1、枚举的定义 2、枚举绑定到comboBox 3、枚举常数转换为枚举string转枚举 数字值转枚举

  • Swift 基础笔记 - 枚举

    枚举 OC定义和使用枚举 Swift定义枚举类型 Swift判断枚举类型 枚举成员类型

  • 枚举类

    1.枚举类型的定义: 枚举类型定义的一般形式为 enum 枚举名{//枚举值表枚举值1;枚举值2;...} 在枚举...

  • 10、枚举与闭包

    枚举 枚举和普通类相比有什么优势 枚举类型、枚举名称与枚举值 枚举的比较运算 两个枚举之间可以使用等值比较(==)...

  • Swift与OC的语法简单对比(常用语法二)

    20- 枚举,枚举原始值,枚举相关值,switch提取枚举关联值 Swift枚举: Swift中的枚举比OC中的枚...

  • Swift 2 学习笔记 10.枚举

    课程来自慕课网liuyubobobo老师 枚举 枚举基础 枚举之原始值 枚举之关联值 枚举递归

  • swift3语法(八)

    枚举/结构体 枚举 定义形式enum 枚举名 { 枚举值} // 例如enum CompassPoint {...

  • C语言基础 之 枚举类型

    枚举类型 枚举类型: 列出所有可能的值 枚举类型的定义 枚举类型定义的一般格式:enum 枚举类型名 {枚举值表}...

  • 枚举

    枚举 Why:为什么需要枚举 What:枚举是什么; How:枚举怎么实现 When:枚举什么时候使用 Where...

  • 枚举的概念及应用

    一、枚举的概念 二、枚举类型的定义 三、枚举变量的定义 四、枚举使用的注意 五、枚举变量的基本操作 五、枚举变量的...

网友评论

    本文标题:枚举

    本文链接:https://www.haomeiwen.com/subject/wqfykqtx.html