前言:
在前面的文章中,我们依次学习了类与对象、继承与方法重写、访问限定符与抽象类,以及接口、函数式接口与lambda表达式。今天,我们将学习Java面向对象编程中的另一个重要特性------枚举类型(enum)。
考虑这样一种应用:定义某种类型的变量,并且这个变量只能被赋予某些指定的值,如果所赋予的值不是事先规定的特定值,那么Java编译器就能直接指出这个错误。Java提供了这种机制,这种机制就是枚举类型 。采用枚举类型 ,能克服程序员在编程时的粗心所导致的低级错误。当然,枚举类型 能解决的问题远不止于此,例如,可以使用枚举类型表示程序的状态码或者错误码等。
1 枚举类型入门:一个表示四季的枚举类型
一年有四个季节:春、夏、秋、冬。能不能定义一个类表示"春夏秋冬"四季,并且,之后所定义的这个类的变量只能赋予规定的"春夏秋冬"这四个值呢?
答案是可以。Java提供的枚举类型能完美地满足这种要求。
可以定义一个包含"春夏秋冬"四个取值的枚举类Season。为此,新建一个名称为ch07-01的Java工程,并在其下新建名为com.ttt.enums的程序包,然后新建名称为Season的类。

1.1 Season枚举类型的定义
Season枚举类型的代码如下:
java
package com.ttt.enums;
public enum Season {
SPRING, SUMMER, AUTUMN, WINTER;
}
定义简单枚举类型的一般形式是:
java
访问限定符 enum 枚举类型名称 {
//可用的值,之间用逗号","分隔,最后一个值的后面加上分号";"
}
可以看到,使用enum关键字声明枚举类型,枚举值之间用逗号","分隔,最后一个值的后面加上分号";"。
1.2 创建并使用枚举对象
Main类的代码如下:
java
package org.example;
import com.ttt.enums.Season;
public class Main {
public static void main(String[] args) {
//只能为枚举变量赋值预先设定地枚举值,否则编译报错
Season s1 = Season.WINTER;
System.out.println(s1.toString());
System.out.println(s1.ordinal());
System.out.println(s1.name());
System.out.println("------------------");
//其中的参数只是是枚举值对应的字符串常量或变量,否则运行时错误
Season s2 = Season.valueOf("SPRING");
System.out.println(s2.toString());
System.out.println(s2.ordinal());
System.out.println(s2.name());
System.out.println("------------------");
//遍历枚举类型
for(Season s : Season.values())
System.out.println(s.toString());
}
}
输出结果:

上述代码演示了枚举类型的几种典型用法:
- 通过Season.WINTER直接引用枚举值,如果赋予的不是预先设定的枚举值,编译阶段就会报错;
- 通过valueOf()方法将字符串转换为枚举实例,参数必须是枚举值对应的字符串常量或变量,否则会产生运行时错误;
- 通过values()方法遍历枚举类型中的所有成员。
1.3 Enum类中的常用方法
所有枚举实例都可以调用Enum类 的方法。Enum中常用的方法如下表所示:
|-----------------------------|---------------------------------------------|
| 方法名称 | 功能 |
| String name() | 返回枚举值名称 |
| int ordinal() | 获取枚举成员的索引位置,每个枚举值在枚举类型中都有一个唯一的序号,从0开始 |
| Enum valueOf(String name) | 将普通字符串转换为枚举实例 |
| int compareTo(Enum another) | 比较两个枚举成员在定义时的顺序,也就是比较两个枚举值的序号大小,返回两个枚举值的序号差 |
| String\[\] values() | 以数组形式返回枚举类型的所有成员 |
2 深入枚举类型
在定义枚举类型 的枚举值时,除了可以定义简单的枚举值之外,还可以为每个枚举值设定需要的属性,也就是说,可以通过为枚举值设定属性进一步地对枚举值进行附加说明。不仅如此,还可以为枚举类型定义方法。
下面通过一个例子说明如何为枚举类型 的枚举值设定属性,以及如何为枚举类型定义方法。
在com.ttt.enums包下新建一个名称为WeekDay的枚举类型,用以表示星期的名称。在定义该枚举类型时,为每个枚举值设定两个属性:code属性,为每个枚举值设定一个编码;cn属性,为每个枚举值设定中文名称。
2.1 WeekDay枚举类型
WeekDay枚举类型的源代码如下:
java
package com.ttt.enums;
public enum WeekDay {
Monday(1, "星期一"), Tuesday(2, "星期二"), Wednesday(3, "星期三"),
Thursday(4, "星期四"), Friday(5, "星期五"), Saturday(6, "星期六"), Sunday(7, "星期日");
private int code;
private String cn;
private WeekDay(int code, String cn) {
this.code = code;
this.cn = cn;
}
public int getCode() {
return code;
}
public String getCn() {
return cn;
}
@Override
public String toString() {
return "WeekDay{" +
"code=" + code +
", cn='" + cn + '\'' +
'}';
}
}
从上面的代码可以看出:
- 枚举值后面可以跟上括号,为枚举值的构造方法传递参数;
- 枚举类型中可以定义成员变量 (如code和cn)、构造方法 以及成员方法(如getCode()、getCn()、toString());
- 枚举类型还可以通过@Override注解重写从Object类继承来的方法。
2.2 Main测试类及运行结果
Main类代码如下:
java
package org.example;
import com.ttt.enums.WeekDay;
public class Main {
public static void main(String[] args) {
// 创建WeekDay枚举实例wd1,值为Saturday
WeekDay wd1 = WeekDay.Saturday;
// 输出wd1的英文名称(name()方法返回枚举常量名)
System.out.print(wd1.name() + ", ");
// 输出wd1的序号(ordinal()返回枚举常量的序数,从0开始)
System.out.print(wd1.ordinal() + ", ");
// 输出wd1的中文名称(假设getCn()是自定义方法)
System.out.println(wd1.getCn());
System.out.println("--------------------");
// 通过字符串"Thursday"获取对应的枚举实例wd2
WeekDay wd2 = WeekDay.valueOf("Thursday");
// 输出wd2的英文名称、序号和中文名称
System.out.print(wd2.name() + ", ");
System.out.print(wd2.ordinal() + ", ");
System.out.println(wd2.getCn());
System.out.println("--------------------");
// 创建另一个WeekDay实例wd3,同样指向Saturday
WeekDay wd3 = WeekDay.Saturday;
// 比较wd1和wd2是否相同(false)
System.out.println(wd1.equals(wd2));
// 比较wd1和wd3是否相同(true)
System.out.println(wd1.equals(wd3));
System.out.println("--------------------");
for(WeekDay w : WeekDay.values()) {
System.out.print(w.getCn() + " ");
}
}
}
运行结果如下:
Saturday, 5, 星期六
Thursday, 3, 星期四
false
true
星期一 星期二 星期三 星期四 星期五 星期六 星期日
3 枚举类型应用最佳实践
使用枚举类型 可以改善程序的可读性,这是枚举类型最常见的应用场景。
经常会遇到这样的场景:一个方法需要根据方法的执行结果返回特定的某些值。举个例子,程序中定义了一个电梯类,该类有一个控制电梯开门的方法openDoor()。这个方法的返回值需要明确告知调用者该方法的执行结果。该方法可能的执行结果包括:门被正常打开、门未打开--电机故障、门未打开--左门被异物卡住、门未打开--右门被异物卡住、门未打开--控制皮带未卡紧、门未打开--电梯箱未到位、门未打开--其他未知错误。
处理这个场景的最专业、最优雅的方式是使用枚举类型:定义一个枚举类型表示openDoor()方法的返回值。
3.1 ErrorCode枚举类型
例如,可以定义如下的名称为ErrorCode的枚举类型,代码如下:
java
package com.ttt.enums;
public enum ErrorCode {
Success(0, "门被正常打开"),
Failure1(1, "电机故障"), Failure2(2, "左门被异物卡住"),
Failure3(3, "右门被异物卡住"), Failure4(4, "控制皮带未卡紧"),
Failure5(5, "电梯箱未到位"), Failure6(6, "其他未知错误");
private final int code;
private final String description;
private ErrorCode(int code, String description) {
this.code = code;
this.description = description;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return "ErrorCode{" +
"code=" + code +
", description='" + description + '\'' +
'}';
}
}
通过这种方式,openDoor()方法可以直接返回ErrorCode枚举值,调用者通过getCode()和getDescription()方法即可获取错误编码和错误描述,程序的可读性和可维护性都得到了极大的提升。
4 案例:优雅的员工Staff类
本节结合类的定义和枚举类型 ,介绍如何在类的属性中使用枚举类型限定属性的取值。
4.1 案例任务
每个员工都是有性别的,每个员工也都是有工作时间的。定义一个优雅的Staff类,在表示员工基本信息的同时,要求定义使用枚举类型表示员工的性别和工作时间,例如,一个星期的哪几天是需要工作的。
4.2 任务分析
这个案例要求定义一个Staff类表示员工信息,同时,要求使用枚举类型表示员工的性别和员工的工作时间。因此,在定义Staff类的同时,需要定义表示性别的枚举类型和表示工作时间的枚举类型。总结起来,该案例任务需要定义如下的枚举类型和类:
- 定义表示性别的名称为Gender的枚举类型;
- 定义表示工作时间的名称为WeekDay的枚举类型;
- 定义表示员工的Staff类。
4.3 任务实施
在ch07-01工程下新建一个名为com.ttt.staff的包。在该包下新建Gender枚举类型和Staff类。由于在7.2节已经定义了WeekDay类,这里可以直接沿用这个枚举类型即可。
Gender枚举类型的代码如下:
java
package com.ttt.staff;
public enum Gender {
Male, Female;
}
Staff类的代码如下:
package com.ttt.staff;
import com.ttt.enums.WeekDay;
public class Staff {
private String name;
private Gender gender;
private float salary;
private WeekDay[] workdays;
public Staff(String name, Gender gender, float salary, WeekDay... workdays) {
this.name = name;
this.gender = gender;
this.salary = salary;
this.workdays = workdays;
}
public String getName() {
return name;
}
public Gender getGender() {
return gender;
}
public float getSalary() {
return salary;
}
public WeekDay[] getWorkdays() {
return workdays;
}
}
可以看到,Staff类的gender属性使用Gender枚举类型、workdays属性使用WeekDay枚举类型的数组,从而将属性的取值严格限定在枚举值的范围内。
Main类的代码如下:
java
package org.example;
import com.ttt.enums.WeekDay;
import com.ttt.staff.Gender;
import com.ttt.staff.Staff;
public class Main {
public static void main(String[] args) {
Staff staff1 = new Staff("张三", Gender.Male, 5050.5f,
WeekDay.Tuesday, WeekDay.Friday, WeekDay.Saturday);
System.out.print(staff1.getName() + "性别是:");
System.out.println(staff1.getGender().toString());
System.out.println(staff1.getName() + "工作时间是:");
for(WeekDay wd : staff1.getWorkdays()) {
System.out.println(wd);
}
}
}
运行结果如下:
张三性别是:Male
张三工作时间是:
WeekDay{code=2, cn='星期二'}
WeekDay{code=5, cn='星期五'}
WeekDay{code=6, cn='星期六'}
5 练习:水果的成熟季节
大家知道,水果都有名称、形状和成熟季节。编写一个Java程序,首先设计一个表示水果的类,其中,水果的成熟季节用枚举类型表示。最后编写一个测试类,用于测试程序的正确性。
总结
本文结合的内容,系统介绍了Java中枚举类型的定义与使用,核心知识点归纳如下:
- 枚举类型 使用enum关键字声明,变量的取值被限定在预先定义的枚举值范围内,能在编译期发现取值错误;
- 定义简单枚举类型的一般形式为:访问限定符 enum 枚举类型名称 { 枚举值列表 };
- 所有枚举实例都可以调用Enum类 的常用方法:name() 、ordinal() 、valueOf() 、compareTo() 、values();
- 枚举类型 中可以为枚举值设定属性,可以定义成员变量 、构造方法 和成员方法,还可以重写toString()等方法;
- 使用枚举类型 表示方法的返回状态码(如ErrorCode),可以显著改善程序的可读性,是枚举类型最常见的应用场景;
- 在类的属性中使用枚举类型(如Staff类中的Gender和WeekDay),可以优雅地限定属性的取值范围。
希望本文能帮助大家深入理解和掌握Java的枚举类型!