Java1.5发行版本中增加了新的引用类型--枚举类型(enum type)。枚举类型是指由一组固定的常量组成合法值的类型。在Java虚拟机中,枚举类是以用语法糖实现的,在进行编译时会转变成普通的Java类。
创建枚举类型要使用enum关键字,隐含了所创建的类型都是java.lang.Enum类的子类(java.lang.Enum是一个抽象类)。枚举类型的通用格式为Class Enum<E extends Enum<E>>,而E表示枚举类型的名称。枚举类型的每一个值都会映射到protected Enum(String name,int ordinal)构造函数中,在这里,每个值的名称都被转换成一个字符串,并且序数设置表示了此设置被创建的顺序。
/**
* This is the common base class of all Java language enumeration types.
*/
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
private final String name;
private final int ordinal;
/**
* Sole constructor. Programmers cannot invoke this constructor.
* It is for use by code emitted by the compiler in response to enum type declarations.
*/
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
}
Java的枚举类型是功能十分齐全的类,其本质上是int值。Java枚举类型背后的基本思想是:通过公有的静态final域为每个枚举常量导出实例的类。因为没有可以访问的构造器,枚举类型是真正的final,他们是单例的泛型化,本质上是单元素枚举。枚类型是类型安全的枚举模式。
包含同名常量的多个枚举类型可以在一个系统中和平共处,因为每个类型都有自己的命名空间。你可以增加或重新排列枚举类型中的常量,而无需重新编译它的客户端代码,因为导出常量的域在枚举类型和它的客户端之间提供了一个隔离层:常量值并没有被编译到客户端代码中,而是在int枚举模式之中。
除了完善int枚举模式的不足之外,枚举类型还允许添加任意的方法和域,并实现任意接口,他们提供了所有Object方法、Comparable接口、Serializable接口的高级实现,并针对枚举类型的可任意改变设计了序列化方式。
接下讲解下枚举类型的用途:
用法1:常量
在JDK1.5以前,我们定义一个常量通常是 public static final xxx。在JDK1.5以后,就可以用枚举类型来定义常量。
//定义季节
public enum Season{
SPRING,SUMMER,AUTUMN,WINTER;
}
用法2:switch
JDK1.6之前的switch语句只支持byte,short,char,int,enum类型,使用枚举,能让我们的代码可读性更强。
public class EnumTest{
Season season = Season.SPRING;
switch(season){
case SPRING:
system.out.println("SPRING");
break;
case SUMMER:
system.out.println("SUMMER");
break;
case AUTUMN:
system.out.println("AUTUMN");
break;
case WINTER:
system.out.println("WINTER");
break;
}
}
enum Season{
SPRING,SUMMER,AUTUMN,WINTER;
}
用法3:向枚举中添加新方法
如果自己定义方法,那么就必须在enum实例序列的最后添加一个分号,而且Java要求必须先定义enum实例。
public class EnumTest {
public static void main(String[] args) {
// 1.value()
// 返回season所有的枚举类对象,以数组的形式返回
Season[] s = Season.values();
for (int i = 0; i < s.length; i++) {
System.out.println(s[i]);
}
}
}
enum Season {
// 创建枚举类的对象
SPRING("spring", "春暖花开"), SUMMER("summer", "夏日炎炎"), AUTUMN("autumn", "秋高气爽"), WINTER("winter", "白雪皑皑");
// 1.提供类的属性,声明为private final
private final String seasonName;
private final String seasonDesc;
// 2.声明为final的属性,在构造器中初始化
private Season(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
}
用法4:覆盖枚举方法
enum Season {
// 创建枚举类的对象
SPRING("spring", "春暖花开"),SUMMER("summer", "夏日炎炎"), AUTUMN("autumn", "秋高气爽"), WINTER("winter", "白雪皑皑");
// 1.提供类的属性,声明为private final
private final String seasonName;
private final String seasonDesc;
// 2.声明为final的属性,在构造器中初始化
private Season(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
@Override
public String toString() {
return "Season [seasonName=" + seasonName + ", seasonDesc="
+ seasonDesc + "]";
}
}
用法5:实现接口
所有的枚举都继承自ajva.lang.Enum类,由于Java不支持多继承,所以枚举对象不能再继承其他类。
interface Info {
void show();
}
enum Season implements Info {
// 创建枚举类的对象
SPRING("spring", "春暖花开") {
public void show() {
System.out.println("春天");
}
},
SUMMER("summer", "夏日炎炎") {
public void show() {
System.out.println("夏天");
}
},
AUTUMN("autumn", "秋高气爽") {
public void show() {
System.out.println("秋天");
}
},
WINTER("winter", "白雪皑皑") {
public void show() {
System.out.println("冬天");
}
};
// 1.提供类的属性,声明为private final
private final String seasonName;
private final String seasonDesc;
// 2.声明为final的属性,在构造器中初始化
private Season(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
@Override
public String toString() {
return "Season [seasonName=" + seasonName + ", seasonDesc="
+ seasonDesc + "]";
}
}
用法6:枚举集合(EnumSet,EnumMap)的使用
java.util.EnumSet和java.util.EnumMap是两个枚举集合。
EnumSet保证集合中的元素不重复,有效地表示从单个枚举类型中提取的多个值的多个集合,这个类实现了Set接口。在内部具体的实现上,每个EnumSet内容都表示为位矢量。如果底层的枚举类型有64个或更少的元素,整个EnumSet就是用单个long来表示,因此它的性能比得上位域(位域是让你用OR运算将几个常量合并到一个集合中,如:text.applySeason(SPRING | SUMMER))的的性能。即用EnumSet代替位域。
public class EnumSetTest{
private enum Season{SPRING,SUMMER,AUTUMN,WINTER};
public void applySeason(Set<Season> seasons){
...
}
public static void main(){
EnumSetTest enumSetTest = new EnumSetTest();
enumSetTest.applySeason(EnumSet.of(Season.SPRING,Season.WINTER));
}
}
EnumSet类集位域的简洁和性能优势于一身,所以没有理由用位域来表示了。但EnumSet有个缺点,就是无法创建不可变的EnumSet。可以用Collections.unmodifiableSet将EnumSet封装起来,但简洁性和性能会受到影响。
EnumMap中的key是enum类型,而value可以是任意类型。EnumMap在运行速度方面上能与通过序数索引的数组相媲美,是因为EnumMap在内部使用了这种数组。但是他对程序员隐藏了这种实现细节,集Map的丰富功能和类型安全与数组的快速于一身。注意EnumMap构造器采用键类型的Class对象。总之,最好不要用序数来索引数组,而要使用EnumMap。如果你所表示的这种关系是多维的,就使用EnumMap<...,EnumMap<...>>。EnumMap具体实现与用法参考源码以及JDK文档。
enum对象常用方法介绍:
int compareTo(E e): 比较此枚举与指定对象的顺序。
Class<E> getDeclaringClass():返回与此枚举常量的枚举类型对应的Class对象。 String name():返回此枚举常量的名称
int ordinal():返回枚举常量的序数
String toString():返回枚举常量的名称,它包含在声明中。
static <T extends Enum<T>> T valueOf(Class<T> enumType,String name):返回带指定名称的指定枚举类型的枚举常量。
什么时候应该使用枚举呢?当需要一组固定常量或者包括“天然的枚举类型”,例如行星。季节等。但它也包括你在编译时就知道其所有可能值的其他集合,例如菜单的选项等。枚举类型中的常量集并不一定要始终保持不变,专门设计枚举特性是考虑到枚举类型的二进制兼容演变。
总之,枚举天生是不可变的,所有的域都应该为final,他们可以是公有的,也可以是私有的,但最好是私有的。与int常量相比,枚举有个小小的性能缺点,即装载和初始化时会有空间和时间的成本,但枚举类易读,也更加安全,功能更加强大。枚举类型对象的值可以直接使用==进行比较,无需使用equals方法。