一、前言
Java的枚举类型相对C#来说具有更灵活可配置性,Java的枚举类型可以携带更多的信息。
//C#
enumMyColor{
RED= 0,
BLUE= 1}
Console.Write(MyColor.RED);//Java
enumMyColor{
RED("Hot", 4), BLUE("SAD",8);privateString mood;publicString getMood{returnmood;
}private intindex;public intgetIndex(){returnindex;
}private MyColor(String mood, intindex){this.mood =mood;this.index =index;
}
}
System.out.println(MyColor.RED.getMood());
本文将对枚举类型进行较为详细的叙述,以便日后查阅。
二、最简单的用法——常量
/*定义*/
//形式1
enumMyColor{
RED,BLUE
}//形式2
enumMyColor{
RED,BLUE;
}/*使用*/System.out.println(MyColor.RED.name()); //显示RED
System.out.println(MyColor.RED.ordinal()); //显示0
System.out.println(MyColor.BLUE.name()); //显示BLUE
System.out.println(MyColor.BLUE.ordinal()); //显示1
枚举值的name()会返回枚举值的字面量,而ordinal()为返回枚举值的索引,而索引是以枚举值定义时的位置来确定,并在编译时设置的。下面我们来看看到底编译器为我们做了什么?
final class MyColor extends java.lang.Enum{
public staticfinal MyColor RED;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUMpublic staticfinal MyColor BLUE;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUMpublic staticMyColor[] values();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: getstatic #1 //Field $VALUES:[LMyColor;
3: invokevirtual #2 //Method "[LMyColor;".clone:()Ljava/lang/Object;
6: checkcast #3 //class "[LMyColor;"
9: areturn
LineNumberTable:
line1: 0
public staticMyClass valueOf(java.lang.String);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: ldc_w #4 //class MyColor
3: aload_04: invokestatic #5 //Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
7: checkcast #4 //class MyColor
10: areturn
LineNumberTable:
line1: 0
static{};
flags: ACC_STATIC
Code:
stack=4, locals=0, args_size=0
0: new #4 //class MyColor
3: dup4: ldc #7 //String RED
6: iconst_07: invokespecial #8 //Method "":(Ljava/lang/String;I)V
10: putstatic #9 //Field RED:LMyColor;
13: new #4 //class MyColor
16: dup17: ldc #10 //String BLUE
19: iconst_120: invokespecial #8 //Method "":(Ljava/lang/String;I)V
23: putstatic #11 //Field BLUE:LMyColor;
26: iconst_227: anewarray #4 //class MyColor
30: dup31: iconst_032: getstatic #9 //Field RED:LMyColor;
35: aastore36: dup37: iconst_138: getstatic #11 //Field BLUE:LMyColor;
41: aastore42: putstatic #1 //Field $VALUES:[LMyColor;
45: returnLineNumberTable:
line2: 0line1: 26
}
可以看到编译器将enum MyColor编译为一个继承Enum并且带修饰符final的MyColor类。
而枚举值RED和BLUE则被编译为MyColor的类常量,并且在类加载的初始化阶段实例化。MyColor默认的构造函数会调用父类Enum的构造函数Enum(String name, int ordinal)来设置私有字段name和ordinal的值。其中iconst_0和iconst_1分别表示将0和1压栈,invokespecial #8则是调用构造函数Enum(String name, int ordinal)。
0: new #4 //class MyColor
3: dup4: ldc #7 //String RED
6: iconst_0 //int 0
7: invokespecial #8 //Method "":(Ljava/lang/String;I)V
10: putstatic #9 //Field RED:LMyColor;
13: new #4 //class MyColor
16: dup17: ldc #10 //String BLUE
19: iconst_1 //int 1
20: invokespecial #8 //Method "":(Ljava/lang/String;I)V
23: putstatic #11 //Field BLUE:LMyColor;
另外在类加载的初始化阶段会生成一个私有的$VALUE数组用于存放常量RED和BLUE,而在调用MyColor.values()返回的正是这个$VALUE数组的复制品。
26: iconst_227: anewarray #4 //class MyColor
30: dup31: iconst_032: getstatic #9 //Field RED:LMyColor;
35: aastore36: dup37: iconst_138: getstatic #11 //Field BLUE:LMyColor;
41: aastore42: putstatic #1 //Field $VALUES:[LMyColor;
小结:
1. 定义枚举类型本质上就是在定义带final修饰符的Enum的子类;
2. 枚举值本质为第1点所定义的类的类常量;
3. 枚举值的ordinal值由其定义时的排序决定,并且在编译时已经被设置好了。
三、枚举类型的抽象父类Enum
其实我们大多数情况下都是调用父类Enum的方法来操作自定义的枚举值,下面一起看看父类Enum吧!
1. 它为抽象类且继承了Comparable和Serializable两个类。
2. 内含私有字段name和ordinal和对应的公有get方法name()和ordinal()。
3. 重写了equals方法,通过==比较两个枚举值的内存地址来判断两者是否相同。
4. 实现compareTo方法,通过比较两个枚举值的ordinal值来做判断。
5. getDeclaringClass方法,用于返回枚举的Class对象。
四、携带更多信息——自定义构造函数
由于枚举最终被编译为类,因此我们通过自定义构造函数、自定义字段和方法来让枚举值携带更多信息
public enumMyColor{
RED("Hot", 2), BLUE("SAD",5);privateString mood;private intindex;private MyColor(String mood, intindex){this.mood =mood;this.index =index;
}
}
注意:
1. 自定义的构造函数必须是私有的;
2. 构造函数内不能显式调用父类的构造函数;
3. RED、BLUE的ordinal值依然是0和1,那么值依然是RED和BLUE。
上述3点规定和结果的原因是编译器会对我们的自定义构造函数进行加工变为
private MyColor(String name, String ordinal, String mood, intindex){
super(name, ordinal);this.mood =mood;this.index =index;
}
五、让相同枚举类型下的枚举值具有不同的行为——重写枚举值的方法
public enumMyColor{
RED, BLUE(){
@Overridepublicboolean getFlag(){return false;
}
};publicboolean getFlag(){return true;
}
}//调用
System.out.println(MyColor.RED.getFlag()); //显示true
System.out.println(MyColor.BLUE.getFlag()); //显示false
可以看到枚举值RED和BLUE同一个方法具有不同的行为。其实这是通过匿名内部类的方式实现的,BLUE的类型为MyColor$1 extends MyColor,而RED的类型为MyColor。
六、使用接口组织枚举
public interfaceFood {enumCoffee implements Food {
BLACK_COFFEE, DECAF_COFFEE, LATTE, CAPPUCCINO
}enumDessert implements Food {
FRUIT, CAKE, GELATO
}
}
七、总结
若有纰漏请大家指正,谢谢。
八、参考
http://www.tuicool.com/articles/YvQZFf
http://www.cnblogs.com/hemingwang0902/archive/2011/12/29/2306263.html
http://www.cnblogs.com/frankliiu-java/archive/2010/12/07/1898721.html