1.枚举类型是做了特殊限制的类,在Class文件格式中的变化是添加了值为0x4000的属性标记ACC_ENUM,同时添加的还有值为0x2000的ACC_ANNOTATION。这些变化是[JVMS:99]之后的05年做出的,这里是对[JVMS:99]的官方修改说明。
2.枚举类型不能由程序员来显式实例化,这来自使用枚举的初衷(即语义上不允许)。而语法上的不允许,则由如下4点保证:a.编译器强制其构制造函数一定是private修饰符,以保证其不被类外实例化。类内的实例化亦不被允许,实例化的尝试会导致编译器报错。b.继承自Enum的final修饰的clone()方法是固定写好了的。c.序列化技术队枚举做了特殊处理。d.反射中对枚举类型的实例化会得到运行时错误。翻译自[JLS:05],Page249。
3.试图为枚举类型的写finalize()方法将导致编译器错误,因为枚举常量永远不会被垃圾回收。翻译自[JLS:05],Page250。
4.任何枚举类型的(隐式)直接父类都是java.lang.Enum<E>,E是由程序员自己定义的枚举类型。可以注意到Enum类自身的声明:
public abstract class Enum<E extends Enum<E>> extends Object implements Comparable<E>, Serializable
除此以外,我们注意到其声明了以下方法(忽略从Object类继承来的众多方法):
Class<E> getDeclaringClass(); // Returns the Class object corresponding to this enum constant's enum type. String name(); // Returns the name of this enum constant, exactly as declared in its enum declaration. int ordinal(); // Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial constant is assigned an ordinal of zero). static <T extends Enum<T>> T valueOf(Class<T> enumType, String name); // Returns the enum constant of the specified enum type with the specified name.
参考自:[JDK API:05]
5.自己定义的枚举类型E,编译器会为程序员生成以下方法,并且不允许程序员自己写一个方法签名相同的方法,从而保证了以下方法行为的正确:
public static E[] values(); public static E valueOf(String name);
而且,其方法实现及与Enum类之联系由如下示例说明:
public enum Season{ SPRING,SUMMER, AUTUMN,WINTER; }
上述定义的枚举类Season实际被javac修改如下:
public enum Season extends Enum<Season> { public static final SPRING = new Season("SPRING", 0); public static final SUMMER = new Season("SUMMER", 1); public static final AUTUMN = new Season("AUTUMN", 2); public static final WINTER = new Season("WINTER", 3); /*synthetic*/ private static final Season[] $VALUES = new Season[]{Season.SPRING , Season.SUMMER , Season.AUTUMN , Season.WINTER }; public static Season[] values() { return (Season[])$VALUES.clone(); } public static Season valueOf(String name) { return (Season)Enum.valueOf(Season.class, name); } private Season(/*synthetic*/ String $enum$name, /*synthetic*/ int $enum$ordinal) { super($enum$name, $enum$ordinal); } }
示例参考自How is values() implemented for Java 6 enums
6.switch语句对于枚举类型的支持在下面做了说明。对于这样的代码:
public class Test { static enum Type { A, B; } public static void main(String[] args) { Type t = Type.valueOf(args[0]); switch (t) { case A: System.out.println("We've got A"); break; case B: System.out.println("We've got B"); break; } } }
将被javac改写为如下代码:
public class Test { static enum Type { A, B; } private static int[] $SWITCH_TABLE$Test$Type; static int[] $SWITCH_TABLE$Test$Type() { if (Type.$SWITCH_TABLE$Test$Type != null) return $SWITCH_TABLE$Test$Type; else { int[] is = new int[Type.values().length]; is[Type.A.ordinal()] = 1; is[Type.B.ordinal()] = 2; $SWITCH_TABLE$Test$Type = is; return is; } } public static void main(String[] args) { Type t = Type.valueOf(args[0]); int[] is = $SWITCH_TABLE$Test$Type(); int i = is[t.ordinal()]; switch (i) { case 1: System.out.println("We've got A"); break; case 2: System.out.println("We've got B"); break; } } }
其中我们需要注意两点:
a.既然字节码采用的是tableswitch,那直接用Type.ordinal()返回的0~n-1作为tableswitch的下界和上界似乎更合理。但是,因为switch语句中的case标签的顺序可以不按照ordinal()的顺序写出,而为了case中的具体语句(上例为输出"We've got A"和"We've got B")的顺序不变,所以采用了int[]类型的$SWITCH_TABLE$Test$Type作为中间过渡。有意思的是,javac为这个field和另一个method使用了相同的名称,不要看的迷迷糊糊,:)
b.虽然这里是final static的常量,但是在case标签里面却不能像我们使用其他常量那样使用全额限定名:Test.Type.A或者Test.Type.B,而必须简写为A或者B。注意,这不是一个错误或者失误,以后亦不会做出改变。这个是有意思的一个讨论,原因请参考官方的回应。