java 枚举类型分析

最近做android开发,需要用到枚举值,这样可以连续赋值,我按之前c++那样书写,如下所示:

[java]  view plain copy
  1. public enum ColorSelect {  
  2.            RED_BAGE = 0,  
  3.            GREEN_BAGE,  
  4.            BLUE_BAGE;  
  5.     }  

编译不过。

我将赋值语句修改了以后,如下所示:

[java]  view plain copy
  1. public enum ColorSelect {  
  2.            RED_BAGE ,  
  3.            GREEN_BAGE,  
  4.            BLUE_BAGE;  
  5.     }  


编译通过。说明C++那样的赋值方法不适用java。所以,我需要弄清楚的是:

1. 在java平台上,如何初始化枚举值。

2.像上述那样的枚举类型ColorSelect,没有赋值,为什么switch(ColorSelect) 可以运行?它是通过字符串还是整数值来匹配枚举类型。


为了弄清楚上述问题,我自己就写了个示范程序,将该程序编译后,看看结果,是否可以找到答案。

一. 如何初始化枚举值

下面是我的示范程序:

自己定义的枚举类:

[java]  view plain copy
  1. public enum ColorSelect {   //定义枚举类ColorSelect  
  2.            RED_BAGE ,   //定义三个枚举对象  
  3.            GREEN_BAGE,  
  4.            BLUE_BAGE;  
  5.     }  

  通过反编译,这段代码反编译后的程序是:

[java]  view plain copy
  1. public final class ColorSelect extends Enum  
  2.     {  
  3.   
  4.         public static ColorSelect valueOf(String s)  //公有函数,该方法通过字符串获取对应的ColorSelect 对象  
  5.         {  
  6.             return (ColorSelect)Enum.valueOf(test/Enum/TestEnum$ColorSelect, s);  
  7.         }  
  8.   
  9.         public static ColorSelect[] values()  //公有函数该方法获取所有的ColorSelect对象  
  10.         {  
  11.             ColorSelect acolorselect[] = ENUM$VALUES; //ENUM$VALUES是ColorSelect数组,下面的代码会将其进行初始化  
  12.             int i = acolorselect.length;  
  13.             ColorSelect acolorselect1[] = new ColorSelect[i];  
  14.             System.arraycopy(acolorselect, 0, acolorselect1, 0, i);  
  15.             return acolorselect1;  
  16.         }  
  17.   
  18.         public static final ColorSelect BLUE_BAGE; // 定义公共成员BLUE_BAGE,属于ColorSelect对象  
  19.         private static final ColorSelect ENUM$VALUES[]; //定义私有成员ENUM$VALUES[],属于ColorSelect数组  
  20.         public static final ColorSelect GREEN_BAGE; // 定义公共成员GREEN_BAGE,属于ColorSelect对象  
  21.         public static final ColorSelect RED_BAGE;  // 定义公共成员RED_BAGE,属于ColorSelect对象  
  22.          static  
  23.         {//static里面这段代码,将上面定义的是三个公共成员ColorSelect对象进行初始化,然后将他们全部赋值给ENUM$VALUES  
  24.             RED_BAGE = new ColorSelect("RED_BAGE"0);  //初始化BLUE_BAGE  
  25.             GREEN_BAGE = new ColorSelect("GREEN_BAGE"1);  //初始化GREEN_BAGE  
  26.             BLUE_BAGE = new ColorSelect("BLUE_BAGE"2); //初始化BLUE_BAGE  
  27.             ColorSelect acolorselect[] = new ColorSelect[3];  
  28.             ColorSelect colorselect = RED_BAGE;  
  29.             acolorselect[0] = colorselect;  
  30.             ColorSelect colorselect1 = GREEN_BAGE;  
  31.             acolorselect[1] = colorselect1;  
  32.             ColorSelect colorselect2 = BLUE_BAGE;  
  33.             acolorselect[2] = colorselect2;  
  34.             ENUM$VALUES = acolorselect;  
  35.         }  
  36.   
  37.         private ColorSelect(String s, int i) //私有函数,调用父类进行初始化ColorSelect对象  
  38.         {  
  39.             super(s, i);  
  40.         }  
  41.     }  
从该反编译的代码看,有两个公有成员函数:


public static ColorSelect valueOf(String s);  该方法通过字符串获取对应的ColorSelect 对象
public static ColorSelect[] values();  该方法获取所有的ColorSelect 对象
这两种方法,在java里面可以直接调用。

下面的是ColorSelect的数据成员。 

[java]  view plain copy
  1. public static final ColorSelect BLUE_BAGE;  
  2. private static final ColorSelect ENUM$VALUES[];  
  3. public static final ColorSelect GREEN_BAGE;  
  4. public static final ColorSelect RED_BAGE;  
公有数据就是BLUE_BAGE,GREEN_BAGE,RED_BAGE,它们本身属性就是ColorSelect类。所以,从这里可以看到枚举类定义的成员变量也是类。后续的代码:

[java]  view plain copy
  1. RED_BAGE = new ColorSelect("RED_BAGE"0);  
  2. GREEN_BAGE = new ColorSelect("GREEN_BAGE"1);  
  3. BLUE_BAGE = new ColorSelect("BLUE_BAGE"2);  
这三行相当于初始化这三个类,并且每个类分配唯一的序号,按定义时的顺序从0递增。我们在java代码,可以调用ColorSelect.BLUE_BAGE.ordinal()来得到对应的序号值。


通过以上分析,java枚举是一个类,不是语言本身实现的,而是编译器实现的,我们可以直接调用里面的方法。Enum 本身就是个普通的 class, 可以有很多自定义方法用来实现不同的功能。如果我们不自定义里面的方法,编译器就能初始化,默认顺序从0递增。我们也可以自定义方法,这样就能随便赋值。

下面是我们自定义的java枚举类型,可以赋值。

[java]  view plain copy
  1. <span style="font-weight: normal;">  public enum Temp {  
  2.         /*通过括号赋值,而且必须有带参构造器和一属性跟方法,否则编译出错 
  3.          * 赋值必须是都赋值或都不赋值,不能一部分赋值一部分不赋值 
  4.          * 如果不赋值则不能写构造器,赋值编译也出错*/  
  5.         absoluteZero(-459), freezing(32),boiling(212), paperBurns(451);  
  6.           
  7.         private final int value;  
  8.         public int getValue() {  
  9.             return value;  
  10.         }  
  11.         //构造器默认也只能是private, 从而保证构造函数只能在内部使用  
  12.         Temp(int value) {  
  13.             this.value = value;  
  14.         }  
  15.     }</span>  

这就是一个枚举类,自定义方法来赋值。java不像C++那样简便,初始第一个值,后续值递增。赋值必须是都赋值或都不赋值,不能一部分赋值一部分不赋值。如果不赋值则不能写构造器,赋值编译也出错。

[java]  view plain copy
  1. Temp temp = null;  
  2.       Log.i("Temp##",temp.freezing.getValue()+"");   
在主函数中,我们调用temp.freezing.getValue(),得到对应的值32。

如果大家感觉自定义枚举类麻烦,其实也可以用别的方法来代替自定义枚举类。如下所示:

[java]  view plain copy
  1. public class ColorSelect {  
  2.         private static final int  RED_BAGE = 1;  
  3.         private static final int  GREEN_BAGE = 3;  
  4.         private static final int  BLUE_BAGE = 5;  
  5.    }  

这种方法也比较方便。

二. 第一个疑问搞清楚了,我们现在要解决第二个问题。Switch到底通过字符串,还是整型来分辨枚举变量。

下面是java代码段:

[java]  view plain copy
  1. ColorSelect test = ColorSelect.BLUE_BAGE;  
  2.   
  3.      switch(test){  
  4.      case RED_BAGE:  
  5.         Log.i("TEST####1","a");  
  6.         break;  
  7.      case GREEN_BAGE:  
  8.         Log.i("TEST####2","b");  
  9.         break;  
  10.      case BLUE_BAGE:  
  11.         Log.i("TEST####3","c");  
  12.         break;  
  13.         default:  
  14.          Log.i("TEST####4","d");  
  15.      }  
  16.   
  17.     Log.i("TEST####ret""e");  


反编译后的代码:

[java]  view plain copy
  1. {  //主函数  
  2.  ColorSelect colorselect = ColorSelect.BLUE_BAGE;  
  3.         ai = $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect();  //调用子函数$SWITCH_TABLE$test$Enum$TestEnum$ColorSelect(),返回数组赋值给ai  
  4.         i = colorselect.ordinal();  //得到该枚举类型BLUE_BAGE的序号  
  5.         ai[i]; //switch根据ai[i]值来进行判断  
  6.         JVM INSTR tableswitch 1 3default 56 // 1 3是ai[i]的取值范围  
  7.     //                   1 73  
  8.     //                   2 84  
  9.     //                   3 95;  
  10.            goto _L1 _L2 _L3 _L4  
  11. _L1:  
  12.         Log.i("TEST####4""d");  
  13. _L6:  
  14.         Log.i("TEST####ret""e");  
  15.         return;  
  16. _L2:  
  17.         Log.i("TEST####1""a");  
  18.         continue/* Loop/switch isn't completed */  
  19. _L3:  
  20.         Log.i("TEST####2""b");  
  21.         continue/* Loop/switch isn't completed */  
  22. _L4:  
  23.         Log.i("TEST####3""c");  
  24.         if(truegoto _L6; else goto _L5  
  25. _L5:  
  26.     }  
  27.   
  28.     private static int $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect[];  
  29. }  
  30.   
  31. //子函数  
  32.  static int[] $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect()  //  
  33.     {  
  34.         int ai[] = $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect;  
  35.         if(ai == null)  
  36.         {  
  37.             ai = new int[ColorSelect.values().length];    
  38.             try  //下面的代码,将每一个枚举类型的序号值作为数组ai的下标,然后赋一个整数值  
  39.             {  
  40.                 int i = ColorSelect.BLUE_BAGE.ordinal();    
  41.                 ai[i] = 3;  
  42.             }  
  43.             catch(NoSuchFieldError nosuchfielderror2) { }  
  44.             try  
  45.             {  
  46.                 int j = ColorSelect.GREEN_BAGE.ordinal();  
  47.                 ai[j] = 2;  
  48.             }  
  49.             catch(NoSuchFieldError nosuchfielderror1) { }  
  50.             try  
  51.             {  
  52.                 int k = ColorSelect.RED_BAGE.ordinal();  
  53.                 ai[k] = 1;  
  54.             }  
  55.             catch(NoSuchFieldError nosuchfielderror) { }  
  56.             $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect = ai;  
  57.         }  
  58.         return ai;  
  59.     }  

子函数$SWITCH_TABLE$test$Enum$TestEnum$ColorSelect()的功能:

通过int i = ColorSelect.BLUE_BAGE.ordinal()得到该类的唯一序号,作为数组下标,并且赋值给ai[i]。这样,每一个枚举类对应都有唯一的整数对应。然后返回数组ai[]。这个功能是编译器实现的。我感觉,子函数的功能有些多余,其实可以直接用之前初始化枚举类的时候的下标号,直接作为标识符也行,就不用再给每个枚举类重新赋整数值。

主函数里面,调用子函数$SWITCH_TABLE$test$Enum$TestEnum$ColorSelect(),switch()里面的参数是ai[i],根据这个整数值来判断分支。Java里面枚举值通过编译器给每个枚举类型赋整型数值,然后Switch通过识别这些整数来进行判断分支,这就解决了第二个疑问。
同时我们通过对比switch的java代码和反编译的代码,可以得到大概下面的结论。
1.每个标志位的"continue"说明该分支break。
2.最后一个分支的标志是if (true) goto _Lx,else goto _x。

同时我们通过对比switch的java代码和反编译的代码,可以得到大概下面的结论。
1.每个标志位的"continue"说明该分支break。
2.最后一个分支的标志是if (true) goto _Lx,else goto _x。

同时,还有些疑问,没弄明白?

我写了好几个switch的例子,反编译出来的代码有一个共同规律。

反编译代码1:

[java]  view plain copy
  1. JVM INSTR tableswitch 1 3default 56  
  2.   //                   1 73  
  3.   //                   2 84  
  4.   //                   3 95;  
  5.          goto _L1 _L2 _L3 _L4  

反编译代码2:

[java]  view plain copy
  1. JVM INSTR tableswitch 0 2default 60  
  2.     //                   0 77  
  3.     //                   1 88  
  4.     //                   2 99;  
  5.            goto _L1 _L2 _L3 _L4  

这两个,标明 1, 2,3相互之间数值都相差11。第二个也是。这个搞不懂?同时,这些数值代表什么?

如果以上哪些不对的地方,请大家指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值