最近做android开发,需要用到枚举值,这样可以连续赋值,我按之前c++那样书写,如下所示:
- public enum ColorSelect {
- RED_BAGE = 0,
- GREEN_BAGE,
- BLUE_BAGE;
- }
编译不过。
我将赋值语句修改了以后,如下所示:
- public enum ColorSelect {
- RED_BAGE ,
- GREEN_BAGE,
- BLUE_BAGE;
- }
编译通过。说明C++那样的赋值方法不适用java。所以,我需要弄清楚的是:
1. 在java平台上,如何初始化枚举值。
2.像上述那样的枚举类型ColorSelect,没有赋值,为什么switch(ColorSelect) 可以运行?它是通过字符串还是整数值来匹配枚举类型。
为了弄清楚上述问题,我自己就写了个示范程序,将该程序编译后,看看结果,是否可以找到答案。
一. 如何初始化枚举值
下面是我的示范程序:
自己定义的枚举类:
- public enum ColorSelect {
- RED_BAGE ,
- GREEN_BAGE,
- BLUE_BAGE;
- }
通过反编译,这段代码反编译后的程序是:
- public final class ColorSelect extends Enum
- {
-
- public static ColorSelect valueOf(String s)
- {
- return (ColorSelect)Enum.valueOf(test/Enum/TestEnum$ColorSelect, s);
- }
-
- public static ColorSelect[] values()
- {
- ColorSelect acolorselect[] = ENUM$VALUES;
- int i = acolorselect.length;
- ColorSelect acolorselect1[] = new ColorSelect[i];
- System.arraycopy(acolorselect, 0, acolorselect1, 0, i);
- return acolorselect1;
- }
-
- public static final ColorSelect BLUE_BAGE;
- private static final ColorSelect ENUM$VALUES[];
- public static final ColorSelect GREEN_BAGE;
- public static final ColorSelect RED_BAGE;
- static
- {
- RED_BAGE = new ColorSelect("RED_BAGE", 0);
- GREEN_BAGE = new ColorSelect("GREEN_BAGE", 1);
- BLUE_BAGE = new ColorSelect("BLUE_BAGE", 2);
- ColorSelect acolorselect[] = new ColorSelect[3];
- ColorSelect colorselect = RED_BAGE;
- acolorselect[0] = colorselect;
- ColorSelect colorselect1 = GREEN_BAGE;
- acolorselect[1] = colorselect1;
- ColorSelect colorselect2 = BLUE_BAGE;
- acolorselect[2] = colorselect2;
- ENUM$VALUES = acolorselect;
- }
-
- private ColorSelect(String s, int i)
- {
- super(s, i);
- }
- }
从该反编译的代码看,有两个公有成员函数:
public static ColorSelect valueOf(String s); 该方法通过字符串获取对应的ColorSelect 对象
public static ColorSelect[] values(); 该方法获取所有的ColorSelect 对象
这两种方法,在java里面可以直接调用。
下面的是ColorSelect的数据成员。
- public static final ColorSelect BLUE_BAGE;
- private static final ColorSelect ENUM$VALUES[];
- public static final ColorSelect GREEN_BAGE;
- public static final ColorSelect RED_BAGE;
公有数据就是BLUE_BAGE,GREEN_BAGE,RED_BAGE,它们本身属性就是ColorSelect类。所以,从这里可以看到枚举类定义的成员变量也是类。后续的代码:
- RED_BAGE = new ColorSelect("RED_BAGE", 0);
- GREEN_BAGE = new ColorSelect("GREEN_BAGE", 1);
- BLUE_BAGE = new ColorSelect("BLUE_BAGE", 2);
这三行相当于初始化这三个类,并且每个类分配唯一的序号,按定义时的顺序从0递增。我们在java代码,可以调用ColorSelect.BLUE_BAGE.ordinal()来得到对应的序号值。
通过以上分析,java枚举是一个类,不是语言本身实现的,而是编译器实现的,我们可以直接调用里面的方法。Enum 本身就是个普通的 class, 可以有很多自定义方法用来实现不同的功能。如果我们不自定义里面的方法,编译器就能初始化,默认顺序从0递增。我们也可以自定义方法,这样就能随便赋值。
下面是我们自定义的java枚举类型,可以赋值。
- <span style="font-weight: normal;"> public enum Temp {
-
-
-
- absoluteZero(-459), freezing(32),boiling(212), paperBurns(451);
-
- private final int value;
- public int getValue() {
- return value;
- }
-
- Temp(int value) {
- this.value = value;
- }
- }</span>
这就是一个枚举类,自定义方法来赋值。java不像C++那样简便,初始第一个值,后续值递增。赋值必须是都赋值或都不赋值,不能一部分赋值一部分不赋值。如果不赋值则不能写构造器,赋值编译也出错。
- Temp temp = null;
- Log.i("Temp##",temp.freezing.getValue()+"");
在主函数中,我们调用temp.freezing.getValue(),得到对应的值32。
如果大家感觉自定义枚举类麻烦,其实也可以用别的方法来代替自定义枚举类。如下所示:
- public class ColorSelect {
- private static final int RED_BAGE = 1;
- private static final int GREEN_BAGE = 3;
- private static final int BLUE_BAGE = 5;
- }
这种方法也比较方便。
二. 第一个疑问搞清楚了,我们现在要解决第二个问题。Switch到底通过字符串,还是整型来分辨枚举变量。
下面是java代码段:
- ColorSelect test = ColorSelect.BLUE_BAGE;
-
- switch(test){
- case RED_BAGE:
- Log.i("TEST####1","a");
- break;
- case GREEN_BAGE:
- Log.i("TEST####2","b");
- break;
- case BLUE_BAGE:
- Log.i("TEST####3","c");
- break;
- default:
- Log.i("TEST####4","d");
- }
-
- Log.i("TEST####ret", "e");
反编译后的代码:
- {
- ColorSelect colorselect = ColorSelect.BLUE_BAGE;
- ai = $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect();
- i = colorselect.ordinal();
- ai[i];
- JVM INSTR tableswitch 1 3: default 56
-
-
-
- goto _L1 _L2 _L3 _L4
- _L1:
- Log.i("TEST####4", "d");
- _L6:
- Log.i("TEST####ret", "e");
- return;
- _L2:
- Log.i("TEST####1", "a");
- continue;
- _L3:
- Log.i("TEST####2", "b");
- continue;
- _L4:
- Log.i("TEST####3", "c");
- if(true) goto _L6; else goto _L5
- _L5:
- }
-
- private static int $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect[];
- }
-
-
- static int[] $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect()
- {
- int ai[] = $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect;
- if(ai == null)
- {
- ai = new int[ColorSelect.values().length];
- try
- {
- int i = ColorSelect.BLUE_BAGE.ordinal();
- ai[i] = 3;
- }
- catch(NoSuchFieldError nosuchfielderror2) { }
- try
- {
- int j = ColorSelect.GREEN_BAGE.ordinal();
- ai[j] = 2;
- }
- catch(NoSuchFieldError nosuchfielderror1) { }
- try
- {
- int k = ColorSelect.RED_BAGE.ordinal();
- ai[k] = 1;
- }
- catch(NoSuchFieldError nosuchfielderror) { }
- $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect = ai;
- }
- return ai;
- }
子函数$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:
- JVM INSTR tableswitch 1 3: default 56
-
-
-
- goto _L1 _L2 _L3 _L4
反编译代码2:
- JVM INSTR tableswitch 0 2: default 60
-
-
-
- goto _L1 _L2 _L3 _L4
这两个,标明 1, 2,3相互之间数值都相差11。第二个也是。这个搞不懂?同时,这些数值代表什么?
如果以上哪些不对的地方,请大家指正。