1. 引言
在编程中,我们经常需要操作一组预先已知且有限的选项。例如:一周中的天数、红绿灯的颜色、游戏难度级别、订单状态。每个选项通常由一个固定的值表示。由此我们就引出了今天的主题——枚举(enum)。
枚举是一种特殊的数据类型,用于声明一组具名常量。
enum就像一个把标签分好格子的盒子,每个标签都是与数字关联的唯一名称。
enum 的主要优点:
- 你的代码将变得更易读且更自文档化;
- 不再充斥神秘的“魔法数字”,如 0、1、2,而是有表达力的名字:DayOfWeek.MONDAY 或 TrafficLight.RED;
- 如果你尝试赋予非法值,编译器会报错;
- 每个元素背后其实都对应一个普通的整数!(后面我们还会提到。)
enum 的声明语法
// 枚举声明的通用模板
public enum Name
{
CONSTANT1 ,
CONSTANT2 ,
CONSTANT3
}
在 Java 中声明枚举非常简单。下面是一个基础示例:
// 文件 DayOfWeek.java
public enum DayOfWeek {
MONDAY, // 0
TUESDAY, // 1
WEDNESDAY, // 2
THURSDAY, // 3
FRIDAY, // 4
SATURDAY, // 5
SUNDAY // 6
}
说明:
- 关键字 enum 告诉编译器:“这是一个枚举!”
- 在花括号内用逗号分隔列出各个选项(常量)。
- 通常使用 UPPER_SNAKE_CASE 命名(全大写,单词间用下划线)。
在哪里可以声明 enum?
- 在单独的文件中(DayOfWeek.java)。
- 在类内部(如果只想让该类私用一个私有枚举)。
- 甚至在方法内部(不过较少这样做)。
2. 在代码中使用 enum
声明一个 enum 变量:
DayOfWeek today = DayOfWeek.MONDAY;
现在,today 只能取 DayOfWeek 中声明的那些值之一。
在 switch 中的使用示例
switch (today)
{
case MONDAY:
System.out.println("唉,星期一……");
break;
case FRIDAY:
System.out.println("万岁,星期五!");
break;
default:
System.out.println("普通的一天。");
}
重要区别:在 case 中直接写常量名(不加前缀),前提是编译器已经知道变量的类型。
遍历 enum 的所有值
每个枚举都有一个静态方法 values(),它返回包含其所有值的数组:
for (DayOfWeek day : DayOfWeek.values())
{
System.out.println(day);
}
这对于输出菜单、校验、生成选项等非常方便。
3. enum 的方法:name()、ordinal()、valueOf()
Java 中的枚举不仅仅是常量列表,它们还是带有方法的完整类!
name() 方法
返回常量在代码中的名字(字符串,大小写与声明一致):
DayOfWeek day = DayOfWeek.FRIDAY;
System.out.println(day.name()); // "FRIDAY"
ordinal() 方法
返回常量的序号(从 0 开始):
System.out.println(DayOfWeek.MONDAY.ordinal()); // 0
System.out.println(DayOfWeek.FRIDAY.ordinal()); // 4
注意:如果将来顺序可能变化,不要用 ordinal() 来存储或比较枚举!更好的做法是使用名称。
valueOf(String name) 方法
将字符串转换为对应的枚举值(如果该常量存在):
DayOfWeek day = DayOfWeek.valueOf("MONDAY");
System.out.println(day); // MONDAY
如果字符串不匹配——会抛出 IllegalArgumentException 异常。
values() 方法
返回该枚举的所有值数组(上面已有示例):
DayOfWeek[] days = DayOfWeek.values();
4. 带字段、构造器和方法的枚举
Java 中的枚举可以扩展:添加字段、构造器、方法!没错,它不仅是名字列表,更是一个迷你类。
示例:带本地化名称的星期
public enum DayOfWeek
{
MONDAY("星期一"),
TUESDAY("星期二"),
WEDNESDAY("星期三"),
THURSDAY("星期四"),
FRIDAY("星期五"),
SATURDAY("星期六"),
SUNDAY("星期日");
private final String russianName;
// 构造器(默认是 private)
DayOfWeek(String russianName)
{
this.russianName = russianName;
}
public String getRussianName()
{
return russianName;
}
}
关于其工作原理的更多细节,我们会在 27+ 级开始学习 OOP 时讲到 :P
现在可以这样用:
DayOfWeek day = DayOfWeek.WEDNESDAY;
System.out.println(day.getRussianName()); // "星期三"
小技巧:每个枚举值都可以有自己的数据(本例中是名为 russianName 的“俄语名称”字段)。
使用 enum 的优势
- 类型安全:无法把列表之外的值意外赋给枚举变量。
- 可读性:代码自带文档(TrafficLight.RED 替代 1 或 "red")。
- 维护方便:需要新增或删除选项时,只需在一个地方修改。
- 可与 switch 配合:enum 非常适合按值分支。
- 可扩展:可以添加字段、方法,实现接口。
- 更少错误:编译器会帮助你不要漏掉分支(尤其是在 switch 结构中)。
6. 实战:在我们的应用中使用 enum
让我们在这个迷你应用中通过 enum 添加对星期的支持。
步骤 1. 声明枚举
public enum DayOfWeek
{
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
步骤 2. 在代码中使用
System.out.println("请输入星期(例如,MONDAY):");
Scanner console = new Scanner(System.in);
String input = console.nextLine().toUpperCase();
DayOfWeek day = DayOfWeek.valueOf(input);
switch (day)
{
case MONDAY:
System.out.println("工作周开始了!");
break;
case FRIDAY:
System.out.println("周末就要到了!");
break;
case SATURDAY:
case SUNDAY:
System.out.println("太好了,周末!");
break;
default:
System.out.println("普通的工作日。");
}
发生了什么:
- 用户输入星期(例如,"monday" 或 "MONDAY")。
- 将字符串转为大写(toUpperCase()),以避免大小写差异的影响。
- 通过 valueOf 将字符串转换为枚举值。
- 使用 switch 输出不同的消息。
- 如果输入不正确——捕获错误并给出警告。
7. 使用 enum 的常见错误
错误 №1:用字符串或数字代替 enum。 初学者常常还会这样写:
int day = 1; // 1 是星期一吗?
或者
String status = "DELIVERED";
这样不好:容易拼写出错,代码难以维护。请使用 enum!
错误 №2:用 == 与 .equals() 比较枚举。 在 Java 中可以用 == 比较 enum 的值——这是安全的,因为在同一个枚举类中每个值都是唯一的:
if (day == DayOfWeek.MONDAY) { ... } // OK!
尽管 .equals() 也能用,但更常见的是 ==。
错误 №3:糟糕的枚举命名。 不要把元素命名成 VALUE1、VALUE2、VALUE3——过一个月谁也看不懂!请使用有意义的名称。
错误 №4:在逻辑中使用 ordinal()。 不要这样写:
if (day.ordinal() == 0) { ... } // 别这样!
元素顺序可能会改变,逻辑就会崩。请使用名称!
GO TO FULL VERSION