第一部分:枚举基础
从传统常量到枚举类型的演进
学习目标
- 理解枚举类型的必要性
- 掌握枚举的基本语法
- 了解枚举的底层实现
1
传统常量的痛点
传统方式的问题
// 问题1:类型不安全
public static final int RED = 0;
public static final int GREEN = 1;
// 可以传入任意int值,编译器不检查
setColor(100); // 编译通过,但逻辑错误
枚举的解决方案
public enum Color {
RED, GREEN, BLUE
}
// 只能传入Color枚举值,类型安全
setColor(Color.RED); // 编译器检查
2
枚举基本定义
// 最简单的枚举定义
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
// 使用枚举
Day today = Day.MONDAY;
System.out.println(today); // 输出: MONDAY
枚举类型
编译时类型安全
命名常量
可读性更强
方法支持
可以包含字段和方法
第二部分:枚举高级特性
枚举可以像类一样拥有字段、方法和构造器
3
带字段和构造器的枚举
public enum Planet {
// 枚举常量必须在最前面
MERCURY(3.303e+23, 2.4397e6),
VENUS(4.869e+24, 6.0518e6),
EARTH(5.976e+24, 6.37814e6),
MARS(6.421e+23, 3.3972e6);
private final double mass; // 质量(kg)
private final double radius; // 半径(m)
// 构造器必须是private或package-private
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
public double surfaceGravity() {
return 6.67300E-11 * mass / (radius * radius);
}
}
// 使用
double earthGravity = Planet.EARTH.surfaceGravity();
4
枚举方法详解
方法 | 说明 | 示例 |
---|---|---|
values() | 返回所有枚举值数组 | Day.values() |
valueOf(String) | 将字符串转为枚举 | Day.valueOf("MONDAY") |
name() | 返回枚举名称 | day.name() |
ordinal() | 返回枚举序号(从0开始) | day.ordinal() |
第三部分:枚举与switch语句
枚举在switch语句中的完美应用
5
枚举switch示例
public enum Operation {
PLUS, MINUS, TIMES, DIVIDE
}
public static double calculate(double x, double y, Operation op) {
switch (op) {
case PLUS:
return x + y;
case MINUS:
return x - y;
case TIMES:
return x * y;
case DIVIDE:
return x / y;
default:
throw new AssertionError("Unknown operation: " + op);
}
}
第四部分:枚举实现单例模式
枚举是实现单例的最佳方式
6
线程安全的单例实现
传统单例的问题
public class DatabaseConnection {
private static DatabaseConnection instance;
private DatabaseConnection() {}
public static synchronized DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
}
枚举单例
public enum DatabaseConnection {
INSTANCE;
public void connect() {
System.out.println("连接数据库...");
}
}
// 使用
DatabaseConnection.INSTANCE.connect();
枚举单例优势
- ✅ 线程安全(JVM保证)
- ✅ 防止反序列化创建新实例
- ✅ 防止反射攻击
- ✅ 代码简洁
第五部分:常见错误与最佳实践
避免枚举使用中的常见陷阱
常见错误1:使用==比较字符串
// 错误:用==比较枚举字符串
if (day == "MONDAY") { // 错误!
// ...
}
// 正确:直接比较枚举
if (day == Day.MONDAY) { // 正确!
// ...
}
常见错误2:空指针异常
Day day = null;
// 错误:switch语句中的空指针
switch (day) { // NullPointerException!
case MONDAY:
// ...
}
// 正确:先检查null
if (day != null) {
switch (day) {
case MONDAY:
// ...
}
}
最佳实践
- 命名规范: 枚举名使用大驼峰,常量名全大写
- 不可变性: 枚举字段应声明为final
- 工具方法: 在枚举中定义相关工具方法
- 文档注释: 为每个枚举值添加JavaDoc
第六部分:实战练习
通过完整项目巩固枚举知识
练习:订单状态管理系统
步骤1:定义订单状态枚举
public enum OrderStatus {
PENDING("待付款", 1),
PAID("已付款", 2),
SHIPPED("已发货", 3),
DELIVERED("已送达", 4),
CANCELLED("已取消", 5);
private final String description;
private final int code;
OrderStatus(String description, int code) {
this.description = description;
this.code = code;
}
public String getDescription() {
return description;
}
public int getCode() {
return code;
}
public static OrderStatus fromCode(int code) {
for (OrderStatus status : values()) {
if (status.code == code) {
return status;
}
}
throw new IllegalArgumentException("Invalid status code: " + code);
}
}
步骤2:使用订单状态
public class Order {
private String orderId;
private OrderStatus status;
public Order(String orderId) {
this.orderId = orderId;
this.status = OrderStatus.PENDING;
}
public void updateStatus(OrderStatus newStatus) {
if (canTransitionTo(newStatus)) {
this.status = newStatus;
System.out.println("订单" + orderId + "状态更新为: " + newStatus.getDescription());
}
}
private boolean canTransitionTo(OrderStatus newStatus) {
switch (status) {
case PENDING:
return newStatus == OrderStatus.PAID || newStatus == OrderStatus.CANCELLED;
case PAID:
return newStatus == OrderStatus.SHIPPED;
case SHIPPED:
return newStatus == OrderStatus.DELIVERED;
default:
return false;
}
}
}
完成检查清单
- ✅ 枚举定义包含字段和构造器
- ✅ 提供获取描述的方法
- ✅ 实现根据code获取枚举
- ✅ 使用switch处理状态转换
- ✅ 演示多态使用