【Java 课程作业】枚举类型(Enum)三大核心应用场景详解
本文是 Java 面向对象程序设计课程的课后作业,详细讲解枚举类型在实际开发中的三个最常用场景:状态/类型定义、策略模式替换 if/else、统一返回码。每个场景都包含问题分析、传统写法对比和完整的枚举实现代码。
文章目录
一、为什么要用枚举类型?
在 Java 5.0 之前,我们通常使用 public static final 常量来表示固定的一组值,比如:
public static final int ORDER_STATUS_CREATED = 0;
public static final int ORDER_STATUS_PAID = 1;
public static final int ORDER_STATUS_SHIPPED = 2;
public static final int ORDER_STATUS_COMPLETED = 3;
这种写法存在很多致命缺点:
- 类型不安全:可以传入任意 int 值,编译器不会报错。
- 可读性差:打印出来只是数字,不知道代表什么意思。
- 代码脆弱:如果常量值改变,所有使用的地方都要修改。
- 无法携带附加信息:只能表示一个值,不能关联其他数据。
枚举类型(Enum)的出现完美解决了这些问题。它是一种特殊的类,用于表示一组固定的常量,具有类型安全、可读性强、功能强大等优点。
二、场景一:状态/类型定义(最常用)
这是枚举类型最基础也是最常用的场景,用于定义系统中所有固定的状态和类型。
2.1 问题引入
在电商系统中,订单有创建、已支付、已发货、已完成、已取消等多个状态。如果用传统的 int 常量来表示,很容易出现传入非法值的问题。
2.2 枚举实现
/**
* 订单状态枚举
* 例1:状态定义
*/
public enum OrderStatus {
// 枚举常量,每个常量都是 OrderStatus 类的一个实例
CREATED(0, "订单已创建"),
PAID(1, "订单已支付"),
SHIPPED(2, "订单已发货"),
COMPLETED(3, "订单已完成"),
CANCELLED(4, "订单已取消");
// 枚举可以携带多个字段
private final int code;
private final String description;
// 枚举的构造方法必须是 private 的
private OrderStatus(int code, String description) {
this.code = code;
this.description = description;
}
// 提供 getter 方法获取字段值
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
// 提供静态方法,根据 code 获取对应的枚举值
public static OrderStatus getByCode(int code) {
for (OrderStatus status : OrderStatus.values()) {
if (status.getCode() == code) {
return status;
}
}
throw new IllegalArgumentException("无效的订单状态码:" + code);
}
}
2.3 使用示例
public class Order {
private Long orderId;
private OrderStatus status; // 使用枚举作为字段类型,类型安全
public void updateStatus(OrderStatus newStatus) {
// 只能传入 OrderStatus 枚举的实例,不能传入任意 int 值
this.status = newStatus;
System.out.println("订单" + orderId + "状态更新为:" + newStatus.getDescription());
}
public static void main(String[] args) {
Order order = new Order();
order.setOrderId(1001L);
// 正确用法
order.updateStatus(OrderStatus.CREATED);
order.updateStatus(OrderStatus.PAID);
// 错误用法:编译报错,类型不匹配
// order.updateStatus(1);
// 根据 code 获取枚举
OrderStatus status = OrderStatus.getByCode(3);
System.out.println("状态码 3 对应的状态是:" + status.getDescription());
}
}
2.4 优点总结
- 类型安全:编译器会检查类型,不能传入非法值。
- 可读性强:代码中直接使用
OrderStatus.PAID,一目了然。 - 可扩展:可以轻松添加新的状态,不需要修改原有代码。
- 功能丰富:可以携带 code、description 等多个附加信息。
三、场景二:策略模式(替换大量 if/else)
这是枚举类型最强大的应用场景之一,可以优雅地替换代码中大量的 if-else 或 switch 语句。
3.1 问题引入
假设我们要实现一个计算器,支持加减乘除四种运算。传统写法会是这样的:
public class Calculator {
public double calculate(double a, double b, String operator) {
if ("+".equals(operator)) {
return a + b;
} else if ("-".equals(operator)) {
return a - b;
} else if ("*".equals(operator)) {
return a * b;
} else if ("/".equals(operator)) {
return a / b;
} else {
throw new IllegalArgumentException("不支持的运算符:" + operator);
}
}
}
这种写法的问题:
- 代码臃肿,每增加一种运算就要加一个 if-else。
- 违反开闭原则:新增功能需要修改原有代码。
- 可读性差,逻辑分散。
3.2 枚举 + 策略模式实现
/**
* 运算策略枚举
* 例2:策略模式替换 if/else
*/
public enum Operation {
// 每个枚举常量实现自己的运算逻辑
ADD("+") {
@Override
public double apply(double a, double b) {
return a + b;
}
},
SUBTRACT("-") {
@Override
public double apply(double a, double b) {
return a - b;
}
},
MULTIPLY("*") {
@Override
public double apply(double a, double b) {
return a * b;
}
},
DIVIDE("/") {
@Override
public double apply(double a, double b) {
if (b == 0) {
throw new ArithmeticException("除数不能为 0");
}
return a / b;
}
};
private final String symbol;
private Operation(String symbol) {
this.symbol = symbol;
}
// 抽象方法,每个枚举常量必须实现
public abstract double apply(double a, double b);
// 根据运算符获取对应的枚举
public static Operation getBySymbol(String symbol) {
for (Operation op : Operation.values()) {
if (op.symbol.equals(symbol)) {
return op;
}
}
throw new IllegalArgumentException("不支持的运算符:" + symbol);
}
}
3.3 使用示例
public class Calculator {
public double calculate(double a, double b, String operator) {
Operation op = Operation.getBySymbol(operator);
return op.apply(a, b);
}
public static void main(String[] args) {
Calculator calculator = new Calculator();
System.out.println("10 + 5 = " + calculator.calculate(10, 5, "+"));
System.out.println("10 - 5 = " + calculator.calculate(10, 5, "-"));
System.out.println("10 * 5 = " + calculator.calculate(10, 5, "*"));
System.out.println("10 / 5 = " + calculator.calculate(10, 5, "/"));
}
}
3.4 优点总结
- 消除 if/else:代码变得非常简洁优雅。
- 符合开闭原则:新增运算只需要添加一个枚举常量,不需要修改原有代码。
- 逻辑内聚:每种运算的逻辑都封装在对应的枚举常量中。
- 易于测试:每个运算逻辑都是独立的,可以单独测试。
四、场景三:统一返回码(后端接口必备)
在后端开发中,所有接口都需要返回统一格式的响应结果,枚举类型是定义统一返回码的最佳实践。
4.1 问题引入
如果没有统一的返回码规范,不同的开发人员可能会使用不同的错误码和错误信息,导致前端无法统一处理。
4.2 枚举实现统一返回码
/**
* 统一返回码枚举
* 例3:后端接口统一返回码
*/
public enum ResultCode {
// 通用成功
SUCCESS(200, "操作成功"),
// 客户端错误 4xx
BAD_REQUEST(400, "请求参数错误"),
UNAUTHORIZED(401, "未登录或 token 已过期"),
FORBIDDEN(403, "没有权限访问"),
NOT_FOUND(404, "请求的资源不存在"),
// 服务器错误 5xx
INTERNAL_SERVER_ERROR(500, "服务器内部错误"),
SERVICE_UNAVAILABLE(503, "服务暂时不可用"),
// 业务错误 1000xx
USER_NOT_FOUND(100001, "用户不存在"),
USERNAME_OR_PASSWORD_ERROR(100002, "用户名或密码错误"),
ORDER_NOT_FOUND(100003, "订单不存在");
private final int code;
private final String message;
private ResultCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
4.3 统一返回结果类
/**
* 统一返回结果类
*/
public class Result<T> {
private int code;
private String message;
private T data;
// 成功返回(带数据)
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(ResultCode.SUCCESS.getCode());
result.setMessage(ResultCode.SUCCESS.getMessage());
result.setData(data);
return result;
}
// 成功返回(不带数据)
public static <T> Result<T> success() {
return success(null);
}
// 失败返回(使用枚举)
public static <T> Result<T> error(ResultCode resultCode) {
Result<T> result = new Result<>();
result.setCode(resultCode.getCode());
result.setMessage(resultCode.getMessage());
return result;
}
// 失败返回(自定义错误信息)
public static <T> Result<T> error(int code, String message) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMessage(message);
return result;
}
// getter 和 setter 方法
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
4.4 接口使用示例
/**
* 用户控制器
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 根据 ID 查询用户
*/
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable Long id) {
if (id == null || id <= 0) {
// 参数错误
return Result.error(ResultCode.BAD_REQUEST);
}
User user = userService.getUserById(id);
if (user == null) {
// 用户不存在
return Result.error(ResultCode.USER_NOT_FOUND);
}
// 成功返回
return Result.success(user);
}
/**
* 用户登录
*/
@PostMapping("/login")
public Result<String> login(String username, String password) {
boolean success = userService.login(username, password);
if (success) {
return Result.success("登录成功");
} else {
return Result.error(ResultCode.USERNAME_OR_PASSWORD_ERROR);
}
}
}
4.5 优点总结
- 规范统一:所有接口返回格式一致,便于前端统一处理。
- 便于维护:所有返回码集中管理,修改时只需要改一个地方。
- 可读性强:使用
ResultCode.USER_NOT_FOUND比直接写 100001 清晰得多。 - 避免硬编码:消除了代码中大量的魔法数字和魔法字符串。
五、学习总结
通过这次作业,我对 Java 枚举类型的应用有了全面深入的理解。枚举不仅仅是用来定义常量的简单工具,它实际上是一个功能强大的类,可以实现很多复杂的设计模式。
我总结了枚举类型的三个核心优势:
- 类型安全:从根本上解决了传统常量的类型不安全问题。
- 代码简洁:可以优雅地替换大量的 if-else 和 switch 语句。
- 功能强大:可以携带多个字段、实现抽象方法、定义构造函数等。
在这三个应用场景中,我觉得策略模式替换 if/else 是最实用的。以前写代码总是会写出很多冗长的 if-else 语句,现在学会了用枚举来实现策略模式,代码变得非常简洁优雅,而且扩展性也更好。
作为一名大二的 Java 学习者,我之前对枚举的理解只停留在定义常量的层面。通过这次作业,我意识到枚举是 Java 面向对象思想的重要体现,掌握枚举的正确使用方法,对于写出高质量的 Java 代码至关重要。
更多推荐

所有评论(0)