枚举类
在某些情况下,一个累的对象是有限而且固定的,比如季节类,它只有4个对象(春、夏、秋、冬);再比如交通灯类,只有3个对象(红、绿、黄)。这种实例有限而且固定的类,在Java里被称为枚举类。
以前与现在的枚举类的对比
在早期代码中,可能会直接使用简单的静态常量来表示枚举,例如以下代码:
public class State {
public static final int ON = 1;
public static final Int OFF= 0;
}
这种方法虽然简单简单明了,但存在如下几个问题:
1. 类型不安全:因为上面的每个state实际上是一个int整数,因此完全可以把一个季节当成一个int整数使用,例如进行加法运算 ON+OFF,这样的代码完全正常。
2. 打印输出的意义不明确:当输出某个state时,例如输出 ON时,实际输出的是1,这个1很难猜测它代表了ON。
但枚举又确实有存在的意义,因此早期也可以通过定义类的方式来实现,可以采用如下设计方式:
1. 通过private将构造器隐藏起来。
2. 把这个类的所有可能实例都使用public static final修饰的类变量来保存。
3. 如果有必要,可以提供一些静态方法,允许其他程序根据特定参数来获取与之匹配的实例。
4. 使用枚举类可以使程序更加健壮,避免创建对象的随意性。
但通过定义类来实现枚举的代码量比较大,实现起来也比较麻烦,Java从JDK1.5后就增加了对枚举类的支持。
枚举类入门
Java 5新增了一个enum关键字(它与class、interface关键字的地位相同),用于定义枚举类。正如前面看到的,枚举类是一种特殊的类,它一样可以有自己的成员变量、方法,可以实现一个或者多个接口,也可以定义自己的构造器。一个Java源文件中最多只能定义一个public访问权限的枚举类,且该Java源文件也必须和该枚举类的类名相同。
但是枚举类终究不是普通类,它与普通类有如下简单的区别:
1. 枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是默认继承Object类,因此枚举类不能显式继承其它类。其中java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable两个接口。
2.使用enum定义、非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类。
3. 枚举类的所有实例必须在枚举类的第一行显式列出,否则这个枚举类永远都不能产生实例。列出这些实例的时候,系统会自动添加public static final修饰,无需程序员显式添加。
4.枚举类的构造器只能使用private访问控制符,如果省略的默认的构造器的访问控制符,则默认使用private修饰;如果强制指定访问控制符,则只能指定private修饰符。
枚举类默认提供一个values()方法,该方法可以很方便的遍历所有的枚举值。
package knowledge;
enum SeasonEnum{
Spring,Summer,Fall,Winter;
}
public class enum1 {
public static void judge(SeasonEnum s){
// switch 与距离的表达式可以是枚举值
switch (s)
{
case Spring :
System.out.println("春暖花开");
break;
case Summer :
System.out.println("夏日炎炎");
break;
case Fall :
System.out.println("秋高气爽");
break;
case Winter :
System.out.println("冬日雪飘");
break;
}
}
public static void main(String[] args) {
// 枚举类默认有一个values()方法,返回该枚举类的所有实例
for(SeasonEnum s:SeasonEnum.values())
{
System.out.println(s);
}
//使用枚举实例时,可通过EnumClass.variable形式来访问
enum1.judge(SeasonEnum.Spring);
}
}
java.lang.Enum类中提供了几个常用的如下的方法:。
1. String name():返回此枚举实例的名称,这个名称就是定义枚举时列出的所有枚举值之一。与此方法相比,大多数程序员应该优先考虑使用toString()方法,因为toString方法返回更加友好的名称。
2. int ordinal():返回枚举值在枚举类中的索引值(就是枚举值在声明中的位置,第一个枚举值的索引值为零)。
3. String toString():返回枚举常量的名称,与name方法相似,单toString()方法更常用。
枚举类型的完整演示代码如下:
package knowledge;
import java.util.EnumMap;
import java.util.EnumSet;
public class enumtest {
// 1. 定义枚举类型
public enum Light {
// 利用构造函数传参
RED (10), GREEN (3), YELLOW (2);
// 定义私有变量
private int nCode ;
// 构造函数,枚举类型只能为私有
private Light( int _nCode) {
this . nCode = _nCode;
}
@Override
public String toString() {
return String.valueOf ( this . nCode );
}
}
/**
* @param args
*/
public static void main(String[] args ) {
// 1. 遍历枚举类型
System. out .println( " 演示枚举类型的遍历 ......" );
testTraversalEnum ();
// 2. 演示 EnumMap 对象的使用
System. out .println( " 演示 EnmuMap 对象的使用和遍历 ....." );
testEnumMap ();
// 3. 演示 EnmuSet 的使用
System. out .println( " 演示 EnmuSet 对象的使用和遍历 ....." );
testEnumSet ();
System.out.println();
}
/**
* 演示枚举类型的遍历
*/
private static void testTraversalEnum() {
Light[] allLight = Light.values ();
for (Light aLight : allLight) {
System. out .println( " 当前灯 name : " + aLight.name());
System. out .println( " 当前灯 ordinal : " + aLight.ordinal());
System. out .println( " 当前灯: " + aLight);
}
}
/**
* 演示 EnumMap 的使用, EnumMap 跟 HashMap 的使用差不多,只不过 key 要是枚举类型
*/
private static void testEnumMap() {
// 1. 演示定义 EnumMap 对象, EnumMap 对象的构造函数需要参数传入 , 默认是 key 的类的类型
EnumMap<Light, String> currEnumMap = new EnumMap<Light, String>(
Light. class );
currEnumMap.put(Light. RED , " 红灯 " );
currEnumMap.put(Light. GREEN , " 绿灯 " );
currEnumMap.put(Light. YELLOW , " 黄灯 " );
// 2. 遍历对象
for (Light aLight : Light.values ()) {
System. out .println( "[key=" + aLight.name() + ",value="
+ currEnumMap.get(aLight) + "]" );
}
}
/**
* 演示 EnumSet 如何使用, EnumSet 是一个抽象类,获取一个类型的枚举类型内容 <BR/>
* 可以使用 allOf 方法
*/
private static void testEnumSet() {
EnumSet<Light> currEnumSet = EnumSet.allOf (Light. class );
for (Light aLightSetElement : currEnumSet) {
System. out .println( " 当前 EnumSet 中数据为: " + aLightSetElement);
}
}
}