java中的枚举类型解析

来源:互联网 发布:中国联通3g网络制式 编辑:程序博客网 时间:2024/04/28 15:12

背景

学过c/c++的同学都知道,c/c++里面的枚举变量在编译的时候,都会用相应的值替换掉枚举常量。
java里面也有枚举类型,那么java里面的枚举是否和c/c++里面的枚举类型一样呢?

分析过程

为了分析java里面的枚举类型,最好的方法也就是反汇编的class文件,来分析java的枚举类型到底是个什么东西。

将下面列子编译为class文件

public class Main {    enum Ops {        ADD, SUB    }    public static void main(String[] args) {        System.out.println(Ops.ADD.name());    }}

在bin下面发现有两个class文件:Main.classMain$Ops.class文件,可以知道枚举类型Ops编译之后也是一个类。

反编译Main$Ops.class,查看它的指令,可以看到下面内容:

chous-MacBook-Pro:TestJava chaohuang$ javap -c -p Main\$Ops.class Compiled from "Main.java"final class com.kevin.TestJava.Main$Ops extends java.lang.Enum<com.kevin.TestJava.Main$Ops> {  public static final com.kevin.TestJava.Main$Ops ADD;  public static final com.kevin.TestJava.Main$Ops SUB;  private static final com.kevin.TestJava.Main$Ops[] ENUM$VALUES;  static {};    Code:        // 省略    private com.kevin.TestJava.Main$Ops(java.lang.String, int);    Code:        // 省略   public static com.kevin.TestJava.Main$Ops[] values();    Code:        // 省略      public static com.kevin.TestJava.Main$Ops valueOf(java.lang.String);    Code:        // 省略 }

可以看到Ops类是继承于java.lang.Enum,而且有两个成员变量:ADDSUB(这正是Ops里面定义的两个枚举值)。并且有三个成员方法,先分别分析下这三个方法的作用。

  private com.kevin.TestJava.Main$Ops(java.lang.String, int);    Code:       0: aload_0               // 将#0局部变量压到栈上       1: aload_1               // 将#1局部变量压到栈上       2: iload_2               // 将#2局部变量压到栈上       3: invokespecial #27     // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V       6: return        

可以看到这个构造方法有两个参数,调用父类的构造方法(父类的构造见Enum定义)。

  public static com.kevin.TestJava.Main$Ops[] values();    Code:       0: getstatic     #23    // Field ENUM$VALUES:[Lcom/kevin/TestJava/Main$Ops; 将静态变量压到栈上       3: dup                  // 复制一份静态变量的引用压到栈上       4: astore_0             // 将栈顶元素(静态变量ENUM$VALUES)保存到#0局部变量中,并弹出栈顶元素       5: iconst_0             // 将常量0压到栈上       6: aload_0              // 将#0局部变量的内容压到栈上       7: arraylength          // 计算当前栈顶变量的数组长度,弹出栈顶元素,压入数组长度       8: dup                  // 复制栈顶元素,压到栈顶       9: istore_1             // 将栈顶元素保存到#1局部变量      10: anewarray     #1     // class com/kevin/TestJava/Main$Ops,新建一个长度为当前栈顶元素值的数组,弹出栈顶元素,将新创建的数组引用压到栈上      13: dup                  // 复制栈顶元素,压到栈顶      14: astore_2             // 将栈顶元素保存到#2局部变量,并弹出      15: iconst_0             // 将常量0压到栈上      16: iload_1              // 将#1局部变量的值压到栈上      17: invokestatic  #31    // Method java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V,调用arraycopy方法      20: aload_2              // 将#2局部变量压到栈上      21: areturn       

分析完这段汇编,可以发现values方法的作用是将类的静态变量ENUM$VALUES复制一份,然后返回给调用者。

  public static com.kevin.TestJava.Main$Ops valueOf(java.lang.String);    Code:       0: ldc           #1     // class com/kevin/TestJava/Main$Ops,将class引用压到栈上       2: aload_0              // 将#0局部变量压到栈上(由于是静态方法,所以#0就是第一个参数)       3: invokestatic  #39    // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;,调用父类的valueOf方法       6: checkcast     #1     // class com/kevin/TestJava/Main$Ops,检查下返回值的类型       9: areturn       

由上面可知valueOf的作用就是传入一个字符串,然后返回对应的枚举类型值。

  static {};    Code:       0: new           #1     // class com/kevin/TestJava/Main$Ops       3: dup                  4: ldc           #13    // String ADD       6: iconst_0             7: invokespecial #14    // Method "<init>":(Ljava/lang/String;I)V      10: putstatic     #18    // Field ADD:Lcom/kevin/TestJava/Main$Ops;      13: new           #1     // class com/kevin/TestJava/Main$Ops      16: dup                 17: ldc           #20    // String SUB      19: iconst_1            20: invokespecial #14    // Method "<init>":(Ljava/lang/String;I)V      23: putstatic     #21    // Field SUB:Lcom/kevin/TestJava/Main$Ops;      26: iconst_2            27: anewarray     #1     // class com/kevin/TestJava/Main$Ops      30: dup                 31: iconst_0            32: getstatic     #18    // Field ADD:Lcom/kevin/TestJava/Main$Ops;      35: aastore             36: dup                 37: iconst_1            38: getstatic     #21    // Field SUB:Lcom/kevin/TestJava/Main$Ops;      41: aastore             42: putstatic     #23    // Field ENUM$VALUES:[Lcom/kevin/TestJava/Main$Ops;      45: return        

这段静态代码块好理解,分别给静态变量ADDSUB构建类对象,并且创建一个数组,将两个对象的引用保存到数组中。

总结

经过分析可以发现:

  • 枚举类型是一个类,继承java.lang.Enum
  • 枚举类型中的每个值都是类对象,并且是枚举类型的静态成员
  • 枚举类型中有个数组类型的静态成员,保存了所有的枚举值,并且可以通过成员方法values获取一个复制的数组
  • 可以通过枚举值对应的字符串获取对应的枚举对象
0 0
原创粉丝点击