枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。
1.定义枚举及应用场景
例如季节,只有春夏秋冬。如果你创建了一个season类,你当然有义务维护这个类的实例对象只能是春(SPRING)、夏(SUMMER)、秋(AUTUMN)、冬(WINTER)这四个。这个时候就体现出枚举类的作用了,java中枚举类型就是针对这样的场景需求所设计的。在jdk1.5以前我们是这样定义的:
public class SeasonConstant {
private final static String SPRING = "SPRING";
private final static String SUMMER = "SUMMER";
private final static String AUTUMN = "AUTUMN";
private final static String WINTER = "WINTER";
}
上述代码中使我们常用的常量枚举类,该类这样定义本没有什么错误,但它存在许多不足,如在类型安全和使用方便性上并没有多少好处,如果存在定义int值相同的变量,混淆的几率还是很大的,编译器也不会提出任何警告。因此在jdk1.5以后便不提倡我们这样定义枚举,如下定义四季的常量:
/**
* 枚举类的后缀建议为Enum,枚举类型的实例对象建议全大写(这样做符合JAVA的规范)
*/
public enum SeasonEnum{
SPRING,SUMMER,AUTUMN,WINTER
}
如上代码定义枚举,比起使用class定义枚举代码更加简洁,与class关键字类似,只不过前者是定义枚举类型,后者是定义类类型。注意枚举中变量一般字母都大写多个值之间以逗号分隔。枚举可以像class类型一样定义在其他类的内部,同时也可以作为一个单独的文件存在即enum。
1.1枚举使用场景
public class EnumDemo {
public static void main(String[] args) {
SeasonEnum spring = SeasonEnum.SPRING;
System.out.println(spring);
}
public enum SeasonEnum{
SPRING,SUMMER,AUTUMN,WINTER
}
}
如上述直接引用枚举即可。
1.2枚举实现原理
既然可以像使用普通的类一样使用枚举,编译器究竟为我们做了些什么事呢?要想知道这其中的秘密,最有效的途径就是查看生成的字节码。通过javac反编译我们的类:
通过编译后生成EnumDemo.class以及EnumDemo$SeasonEnum.class文件,查看文件我们可以知道EnumDemo.class为我们的枚举类,这也就验证前面所说的使用关键字enum定义枚举类型并编译后,编译器会自动帮助我们生成一个与枚举相关的类。通过反编译我们EnumDemo.class可以知道:
final class SeasonEnum extends Enum
{
//编译器为我们添加的静态的values()方法
public static SeasonEnum[] values()
{
return (SeasonEnum[])$VALUES.clone();
}
//编译器为我们添加的静态的valueOf()方法,注意间接调用了Enum也类的valueOf方法
public static SeasonEnum valueOf(String s)
{
return (SeasonEnum)Enum.valueOf(com/dylan/SeasonEnum, s);
}
//私有构造函数
private SeasonEnum(String s, int i)
{
super(s, i);
}
//前面定义的4种枚举实例
public static final SeasonEnum SPRING;
public static final SeasonEnum SUMMER;
public static final SeasonEnum AUTUMN;
public static final SeasonEnum WINTER;
private static final SeasonEnum $VALUES[];
static
{
//实例化枚举实例
SPRING = new SeasonEnum("SPRING", 0);
SUMMER = new SeasonEnum("SUMMER", 1);
AUTUMN = new SeasonEnum("AUTUMN", 2);
WINTER = new SeasonEnum("WINTER", 3);
$VALUES = (new SeasonEnum[] {
SPRING,
SUMMER,
AUTUMN,
WINTER
});
}
}
可以看到,一个枚举在经过编译器编译过后,变成了一个抽象类,它继承了java.lang.Enum;而枚举中定义的枚举常量,变成了相应的public static final属性,而且其类型就抽象类的类型,除此之外,编译器还帮助我们生成了4个SeasonEnum类型的实例对象分别对应枚举中定义的4个季节,这也充分说明了我们前面使用关键字enum定义的SeasonEnum类型中的每种季节枚举常量也是实实在在的SeasonEnum实例对象,只不过代表的内容不一样而已。注意编译器还为我们生成了两个静态方法,分别是values()和 valueOf(),稍后会分析它们的用法,到此我们也就明白了,使用关键字enum定义的枚举类型,在编译期后,也将转换成为一个实实在在的类,而在该类中,会存在每个在枚举类型中定义好变量的对应实例对象,如上述的SPRING枚举类型对应public static final SeasonEnum SPRING;,同时编译器会为该类创建两个方法,分别是values()和valueOf()。