在某些情况,一个类的对象是有限而且是固定的,例如季节类,只有春夏秋冬四个对象,这种实例有限且固定的类,成为枚举类

枚举类概念和作用

在JAVA5之前的枚举用法是这样的:

public static final int SEASON_SPRING = 1;
public static final int SEASON_SUMMER = 2;
public static final int SEASON_FALL = 3;
public static final int SEASON_WINTER = 4;
public class Season
{
// 把Season类定义成不可变的,将其成员变量也定义成final的
private final String name;
private final String desc;
public static final Season SPRING
= new Season("春天", "趁春踏青");
public static final Season SUMMER
= new Season("夏天", "夏日炎炎");
public static final Season FALL
= new Season("秋天", "秋高气爽");
public static final Season WINTER
= new Season("冬天", "围炉赏雪");
public static Season getSeason(int seasonNum)
{
switch (seasonNum)
{
case 1 :
return SPRING;
case 2 :
return SUMMER;
case 3 :
return FALL;
case 4 :
return WINTER;
default :
return null;
}
}
// 将构造器定义成private访问权限
private Season(String name, String desc)
{
this.name = name;
this.desc = desc;
}
// 只为name和desc提供getter方法
public String getName()
{
return this.name;
}
public String getDesc()
{
return this.desc;
}
}

在JAVA5之后新增了enum关键字,它与class,interface关键字的地位相同,用于定义枚举类,枚举类一样可以有自己的成员变量、方法,可以实现一个或多个接口,也可以定义自己的构造器
一个Java源文件只能有一个public访问权限的枚举类,并且这个Java源文件的文件名必须与该枚举类的类名相同

枚举类与普通类的不同

  • 枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类而不是Object类,枚举类不能显示的继承其父类,java.lang.Enum实现了java.lang.Serializable和java.lang.Comparable接口
  • 使用enum定义的非抽象的枚举类默认会使用final修饰
  • 枚举类的构造器只能使用private访问控制符,如果省略了则系统默认会加上private修饰,因此枚举类不能派生子类
  • 枚举类所有实例必须在枚举类的第一行显示的列出,否则这个枚举类永远不能产生实例,列出这些实例,系统会自动添加public static final修饰,无需显示添加
public enum SeasonEnum
{ // 显示的列出4个枚举类
SPRING, SUMMER, FALL, WINTER;
}

显示的使用枚举类的实例EnumClass.variable,例如SeasonEnum.SPRING

public class EnumTest
{
public 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 (var s : SeasonEnum.values())
{
System.out.println(s);
}
// 使用枚举实例时,可通过EnumClass.variable形式来访问
new EnumTest().judge(SeasonEnum.SPRING);
}
}

switch表达式使用了SeasonEnum对象作为表达式(JDK1.5增加了枚举后对switch进行了扩展),switch的控制表达式可以是任何枚举类型,并且case表达式中的值直接使用枚举值的名字,无需添加枚举类作为限定

java.lang.Enum类中的几个方法

  • int compareTo(E o):
  • String name():
  • int ordinal():
  • String toString():
  • public static <T extends Enum>T valueOf(ClassenumType, String name):

枚举类的成员变量、方法和构造器

public enum Gender
{
MALE, FEMALE;
public String name;
}

public class GenderTest
{
public static void main(String[] args)
{
// 通过Enum的valueOf()方法来获取指定枚举类的枚举值
Gender g = Enum.valueOf(Gender.class, "FEMALE");
// 直接为枚举值的name实例变量赋值
g.name = "女";
// 直接访问枚举值的name实例变量
System.out.println(g + "代表:" + g.name);
}
}

枚举类用起来根普通类区别不大,只是创建对象的方式不同,枚举类实例只能是枚举值而不是通过new来创建枚举类对象
然而为了更好的封装性,不应该允许直接访问到类的成员变量,而是用方法来控制,枚举类也不例外

public enum Gender
{
MALE, FEMALE;
private String name;
public void setName(String name)
{
switch (this)
{
case MALE:
if (name.equals("男"))
{
this.name = name;
}
else
{
System.out.println("参数错误");
return;
}
break;
case FEMALE:
if (name.equals("女"))
{
this.name = name;
}
else
{
System.out.println("参数错误");
return;
}
break;
}
}
public String getName()
{
return this.name;
}
}
public class GenderTest
{
public static void main(String[] args)
{
Gender g = Gender.valueOf("FEMALE");
g.setName("女");
System.out.println(g + "代表:" + g.getName());
// 此时设置name值时将会提示参数错误。
g.setName("男");
System.out.println(g + "代表:" + g.getName());
}
}

枚举类通常设计成不可变类,他的成员变量值不应该允许改变,因此建议使用private final修饰,同时如果所有的成员变量都使用了private final修饰,则必须在构造器为这些成员变量指定初始值(或者在定义成员变量时指定默认值,或者在初始化块中指定初始值)

public enum Gender
{
// 此处的枚举值必须调用对应构造器来创建
MALE("男"), FEMALE("女");
private final String name;
// 枚举类的构造器只能使用private修饰
private Gender(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
}

当为枚举类创建了一个Gender(String name)构造器后,列出的枚举值就应该如代码所示来完成,实际上枚举类中列出枚举值时,就是调用构造器创建枚举对象,只是不需要new,不需要显示调用构造器

实现接口的枚举类

枚举类可以实现一个或者多个接口,与普通类实现一个或多个接口一样,枚举类实现接口时,也需要实现该接口所包含的方法

public interface GenderDesc
{
void info();
}
public enum Gender implements GenderDesc
{
// 此处的枚举值必须调用对应构造器来创建
MALE("男"), FEMALE("女");
// 此处的枚举值必须调用对应构造器来创建
private final String name;
// 枚举类的构造器只能使用private修饰
private Gender(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
// 增加下面的info()方法,实现GenderDesc接口必须实现的方法
public void info()
{
System.out.println(
"这是一个用于用于定义性别的枚举类");
}
}

如果要每个枚举值在调用该方法时呈现出不同的行为则需要让每个枚举值分别来实现该方法

MALE("男")
// 花括号部分实际上是一个类体部分
// 枚举类的匿名内部子类
{
public void info()
{
System.out.println("这个枚举值代表男性");
}
},
FEMALE("女")
{
public void info()
{
System.out.println("这个枚举值代表女性");
}
};

编译时会生成三个文件​​Gender.class/Gender$1.class/Gender$2.class​

包含抽象方法的枚举类

并不是所有的枚举类都使用final修饰,非抽象的枚举类才默认使用final修饰,对于一个抽象枚举类来说,只要它包含了抽象抽象方法,它就是个抽象枚举类,系统默认使用abstract修饰

public enum Operation
{
// 定义枚举值时为抽象方法提供实现,否则报错
// 枚举类里定义抽象方法,不需要把枚举类也用abstract修饰,系统会自动加上
PLUS
{
public double eval(double x, double y)
{
return x + y;
}
},
MINUS
{
public double eval(double x, double y)
{
return x - y;
}
},
TIMES
{
public double eval(double x, double y)
{
return x * y;
}
},
DIVIDE
{
public double eval(double x, double y)
{
return x / y;
}
};
// 为枚举类定义一个抽象方法
// 这个抽象方法由不同的枚举值提供不同的实现
public abstract double eval(double x, double y);
public static void main(String[] args)
{
System.out.println(Operation.PLUS.eval(3, 4));
System.out.println(Operation.MINUS.eval(5, 4));
System.out.println(Operation.TIMES.eval(5, 4));
System.out.println(Operation.DIVIDE.eval(5, 4));
}
}