Java 5之前没有提供枚举类型,尽管可以通过声明静态常量(final static变量)替代枚举,但是仍然很多Java程序员期待能有类似其他语言中的枚举类型。Java 5之后提供了枚举类型,Java枚举类型本质上是一种继承java.lang.Enum类,是引用数据类型,因此也称为“枚举类”。本章介绍Java枚举类。
在C和Objective-C等其他语言中,枚举用来管理一组相关常量的集合,使用枚举可以提高程序的可读性,使代码更清晰且更易于维护。
在Java 5之前没有提供枚举类型,可以通过声明静态常量(final static变量)替代枚举常量,例如想声明一组常量表示一周中的5个工作日,那么Java 5之前实现代码如下:
//WeekDays.java文件
package com.a51work6;
public interface WeekDays{
// 枚举常量列表
int MONDAY = 0; //星期一
int TUESDAY = 1; //星期二
int WEDNESDAY = 2; //星期三
int THURSDAY = 3; //星期四
int FRIDAY =4; //星期五
}
通常在接口中声明一组静态常量,当然也可以在一般类中声明一组静态常量。这些常量往往都是int类型,这是为了以后方便使用switch语句进行判断。
调用代码如下:
//HelloWorld.java文件
package com.a51work6;
public class HelloWorld {
public static void main(String[] args)
//day工作日变量
int day = WeekDays.FRIDAY; ①
switch (day) {
case WeekDays.MONDAY:
System.out.println("星期一");
break;
case WeekDays.TUESDAY:
System.out.println("星期二");
break;
case WeekDays.WEDNESDAY:
System.out.println("星期三");
break;
case WeekDays.THURSDAY:
System.out.println("星期四");
break;
case WeekDays.FRIDAY:
System.out.println("星期五");
break;
}
}
}
从上述代码中直接使用这些常量。但这种方式还存在一些问题:
1.类型不安全。代码第①行是声明工作日变量day,day是整数类型,程序执行过程中很有可能给day变量传入一个任意的整数值,可能导致程序出现错误。
2.程序不方便调试。在程序调试时,如果通过日志输出day值,那么只能看到0~4之间的数值,程序员需要比较这些数值代表的含义,才能知道输出的结果是什么。
枚举类型可以避免直接使用常量所导致的问题。Java 5之后可以使用枚举类型了,Java中枚举类型的作用已经不仅仅是定义一组常量提高程序的可读性了,还具有如下特性:
1.Java枚举类型是一种类,是引用类型,具有了面向对象特性,可以添加方法和成员变量等。
2. Java枚举类型父类是java.lang.Enum,不需要显式声明。
3. Java枚举类型可以实现接口,与类实现接口类似。
4.Java枚举类型不能被继承,不存在子类。
先来看Java中的枚举类声明。Java中是使用enum关键词声明枚举类,具体定义放在一对大括号内,枚举的语法格式如下:
[public] enum 枚举名 {
枚举常量列表
}
enum前面的修饰符是[public]表示public或省略。public是公有访问级别,可以在任何地方访问。省略是默认访问级别,只能在当前包中访问。
“枚举名”是该枚举类的名称。它首先应该是有效的标识符,其次应该遵守Java命名规范。它应该是一个名称,如果采用英文单词命名,首字母应该大写,且应尽量用一个英文单词。“枚举常量列表”是枚举的核心,它由一组相关常量组成。
15.2.1 最简单形式的枚举类
如果采用枚举类来表示工作日,最简单枚举类WeekDays具体代码如下:
//WeekDays.java文件
package com.a51work6;
public enum WeekDays {
// 枚举常量列表
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
}
在枚举类WeekDays中定义了5个常量,使用枚举类WeekDays代码如下:
//HelloWorld.java文件
package com.a51work6;
public class HelloWorld {
public static void main(String[] args) {
// day工作日变量
WeekDays day = WeekDays.FRIDAY; ①
System.out.println(day); ②
switch (day) { ③
case MONDAY: ④
System.out.println("星期一");
break;
case TUESDAY:
System.out.println("星期二");
break;
case WEDNESDAY:
System.out.println("星期三");
break;
case THURSDAY:
System.out.println("星期四");
break;
default: //case FRIDAY: ⑤
System.out.println("星期五");
}
}
}
输出结果:
FRIDAY
星期五
上述代码第①行是声明工作日变量day,day是WeekDays枚举类型,取值是WeekDays.FRIDAY,是枚举类中定义的枚举常量。day = WeekDays.FRIDAY赋值过程中实例化WeekDays枚举类对象,并初始化为WeekDays.FRIDAY。注意赋值表达式是“枚举类型名.枚举常量”的形式。
代码第②行day对象日志输出结果不是整数,而是FRIDAY。
枚举类与switch语句能够很好地配合使用,代码第③行switch表达式直接使用day枚举对象,case常量直接使用枚举常量,见代码第④行,而且不需要枚举类名作为前缀,使用起来比较简洁。
15.2.2 枚举类中成员变量和成员方法
枚举类可以像类一样包含成员变量和成员方法,成员变量可以是实例变量也可以是静态变量,成员方法可以是实例方法,也可以是静态方法,但不能是抽象方法。
示例代码如下:
//WeekDays.java文件
package com.a51work6;
public enum WeekDays {
// 枚举常量列表
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY; ①
// 实例变量
private String name;
private int index;
// 静态变量
private static int staticVar = 100;
// 覆盖父类中的toString()方法
@Override
public String toString() { ②
StringBuilder sb = new StringBuilder();
sb.append(name);
sb.append('-');
sb.append(index);
return sb.toString();
}
// 实例方法
public String getInfo() {
// 调用父类中toString()方法
return super.toString();
}
// 静态方法
public static int getStaticVar() {
return staticVar;
}
}
上述代码第①行在枚举类WeekDays中添加了一些成员变量和成员方法,这些方法还可以覆盖枚举父类(java.lang.Enum)中的方法,见代码第②行的toString()方法。
添加的其他成员的枚举类需要注意,“枚举常量列表”语句必须是枚举类中的第一行代码。而且“枚举常量列表”语句后面要加分号(;)表示语句的结束,见代码第①行所示。
使用枚举类WeekDays代码如下:
//HelloWorld.java文件
package com.a51work6;
public class HelloWorld {
public static void main(String[] args) {
// day工作日变量
WeekDays day = WeekDays.FRIDAY;
//打印day默认调用枚举toString()方法
System.out.println(day);
//调用枚举实例方法
System.out.println(day.getInfo());
//调用枚举静态方法
System.out.println(WeekDays.getStaticVar());
}
}
上述代码比较简单这里不再赘述。
15.2.3 枚举类构造方法
在15.2.2节示例中实例变量name和index,都是没有初始化,在类中成员变量的初始化是通过构造方法实现的,而在枚举类中也是通过构造方法初始化成员变量的。
为15.2.2节示例添加构造方法,代码如下:
//WeekDays.java文件
package com.a51work6;
public enum WeekDays {
// 枚举常量列表
MONDAY(“星期一”, 0), TUESDAY(“星期二”, 1), WEDNESDAY(“星期三”, 2),
THURSDAY(“星期四”, 3), FRIDAY(“星期五”, 4); ①
// 实例变量
private String name;
private int index;
// 静态变量
private static int staticVar = 100;
private WeekDays(String name, int index) { ②
this.name = name;
this.index = index;
}
// 覆盖父类中的toString()方法
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name);
sb.append('-');
sb.append(index);
return sb.toString();
}
// 实例方法
public String getInfo() {
// 调用父类中toString()方法
return super.toString();
}
// 静态方法
public static int getStaticVar() {
return staticVar;
}
}
上述代码第②行是添加的构造方法,注意枚举类的中的构造方法只能是私有访问级别,构造方法可以省略private关键字,但它仍然是私有的构造方法。这也说明了枚举类不允许在外部创建对象。
一旦添加了有参数的构造方法,那么“枚举常量列表”也需要修改,见代码第①行,每一个枚举常量都是一个实例,都会调用构造方法进行初始化成员变量,(“星期一”, 0)是调用构造方法。
所有枚举类都继承java.lang.Enum类,Enum中定义了一些枚举中常用的方法:
o int ordinal():返回枚举常量的顺序。这个顺序根据枚举常量声明的顺序而定,顺序从零开始。
o 枚举类型[] values():静态方法,返回一个包含全部枚举常量的数组。
o 枚举类型 valueOf(String str):静态方法,str是枚举常量对应的字符串,返回一个包含枚举类型实例。
WeekDays枚举类代码如下:
//WeekDays.java文件
package com.a51work6;
public enum WeekDays {
// 枚举常量列表
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
}
使用枚举常用方法示例代码如下:
//HelloWorld.java文件
package com.a51work6;
public class HelloWorld {
public static void main(String[] args) {
// 返回一个包含全部枚举常量的数组
WeekDays[] allValues = WeekDays.values(); ①
// 遍历枚举常量数值
for (WeekDays value : allValues) {
System.out.printf("%d - %s\n",value.ordinal(), value); ②
}
// 创建WeekDays对象
WeekDays day1 = WeekDays.FRIDAY;
WeekDays day2 = WeekDays.valueOf("FRIDAY"); ③
System.out.println(day1 == WeekDays.FRIDAY); ④
System.out.println(day1.equals(WeekDays.FRIDAY)); ⑤
System.out.println(day1 == day2); ⑥
}
}
上述代码第①行是通过values方法获得所有枚举常量的数组,代码第②行是获得枚举常量value,其中value.ordinal()获得当前枚举常量的顺序。
代码第③行是通过valueOf方法获得枚举对象WeekDays.FRIDAY,参数是枚举常量对应的字符串。
代码第④行~第⑥行是比较枚举对象,它们比较的结果都是true。
通过对本章的学习,读者可以了解到Java中枚举的作用、特点和常用方法。重点是声明和使用枚举类。