Java 枚举的 3 个实战场景:从状态码到策略模式
枚举不只是"定义常量",用好了能让代码干净一半。
适合人群:刚学完 Enum 语法、想知道"实际项目中怎么用"的同学。
阅读时间:约 8 分钟。
目录
- 场景 1:状态 / 类型定义(最常用)
- 场景 2:策略模式(替换大量 if/else)
- 场景 3:统一返回码(后端接口必备)
- 完整可运行代码
- 总结
场景 1:状态 / 类型定义(最常用)
问题:魔法数字与魔法字符串
// 烂代码示例
if (order.getStatus() == 1) {// 待支付
} else if (order.getStatus() == 2) {// 已支付
}
// 1 和 2 是什么意思?新同事一脸懵,数据库里也是裸数字
枚举重构
文件:OrderStatus.java
public enum OrderStatus {PENDING_PAYMENT("待支付", 1),PAID("已支付", 2),SHIPPED("已发货", 3),COMPLETED("已完成", 4),CANCELLED("已取消", 5);private final String desc;private final int code;OrderStatus(String desc, int code) {this.desc = desc;this.code = code;}public String getDesc() { return desc; }public int getCode() { return code; }// 根据 code 反查枚举(数据库里存的是 code)public static OrderStatus of(int code) {for (OrderStatus status : values()) {if (status.code == code) return status;}throw new IllegalArgumentException("未知状态码: " + code);}
}
文件:OrderStatusDemo.java
public class OrderStatusDemo {public static void main(String[] args) {// 1. 基本使用OrderStatus status = OrderStatus.PAID;System.out.println("当前状态: " + status.getDesc());System.out.println("状态码: " + status.getCode());// 2. 遍历所有状态(适合展示枚举的规范性)System.out.println("\n=== 所有订单状态 ===");for (OrderStatus s : OrderStatus.values()) {System.out.printf("code=%d, desc=%s%n", s.getCode(), s.getDesc());}// 3. 根据 code 反查(数据库场景)OrderStatus fromDb = OrderStatus.of(3);System.out.println("\n从数据库 code=3 反查: " + fromDb);}
}
运行截图

对比
| 维度 | 魔法数字 | 枚举 |
|---|---|---|
| 可读性 | 需要查文档或注释 | 自描述 |
| 类型安全 | 任意 int 都能传 | 编译期检查 |
| 维护性 | 改一个值要全局搜索 | 改枚举定义一处即可 |
| 数据库映射 | 手动维护 | 通过 getCode() / of() 统一映射 |
场景 2:策略模式(替换大量 if/else)
问题:臃肿的分支判断
public BigDecimal calculatePrice(String role, BigDecimal amount) {if ("VIP".equals(role)) {return amount.multiply(new BigDecimal("0.9"));} else if ("SVIP".equals(role)) {return amount.multiply(new BigDecimal("0.75"));} else if ("NORMAL".equals(role)) {return amount;}// 每新增一个角色,就要改这里,违反开闭原则throw new IllegalArgumentException("未知角色: " + role);
}
痛点:新增角色要改核心方法,容易出错,且逻辑和配置混在一起。
枚举策略重构
文件:DiscountStrategy.java
import java.math.BigDecimal;public enum DiscountStrategy {NORMAL("普通会员") {@Overridepublic BigDecimal apply(BigDecimal amount) {return amount; // 无折扣}},VIP("VIP会员") {@Overridepublic BigDecimal apply(BigDecimal amount) {return amount.multiply(new BigDecimal("0.9")); // 9折}},SVIP("SVIP会员") {@Overridepublic BigDecimal apply(BigDecimal amount) {return amount.multiply(new BigDecimal("0.75")); // 75折}};private final String roleName;DiscountStrategy(String roleName) {this.roleName = roleName;}public abstract BigDecimal apply(BigDecimal amount);public String getRoleName() {return roleName;}
}
文件:StrategyDemo.java
import java.math.BigDecimal;public class StrategyDemo {public static void main(String[] args) {BigDecimal amount = new BigDecimal("1000");System.out.println("=== 不同角色折扣计算 ===");for (DiscountStrategy strategy : DiscountStrategy.values()) {BigDecimal finalPrice = strategy.apply(amount);System.out.printf("%s: 原价 %s → 实付 %s%n", strategy.getRoleName(), amount, finalPrice);}}
}
运行截图

对比
| 维度 | if/else 版本 | 枚举策略版本 |
|---|---|---|
| 新增角色 | 修改核心类 | 新增枚举常量即可 |
| 单元测试 | 测一个长方法 | 每个策略独立测试 |
| 可读性 | 逻辑堆在一起 | 策略自描述 |
| 扩展性 | 违反开闭原则 | 符合开闭原则 |
如果策略逻辑很复杂,也可以在枚举里只存一个
Supplier<Strategy>或函数接口,把具体实现类延迟加载。
场景 3:统一返回码(后端接口必备)
问题:错误码散落各处
// Controller A
return new Result(500, "服务器内部错误", null);// Controller B
return new Result(500, "系统错误", null);
// 同样的 500,消息不一样,前端无法统一处理
枚举统一封装
文件:ResultCode.java
public enum ResultCode {SUCCESS(200, "操作成功"),BAD_REQUEST(400, "请求参数错误"),UNAUTHORIZED(401, "未登录或 token 过期"),FORBIDDEN(403, "无权限访问"),NOT_FOUND(404, "资源不存在"),INTERNAL_ERROR(500, "服务器内部错误"),SERVICE_BUSY(503, "服务繁忙,请稍后重试");private final int code;private final String message;ResultCode(int code, String message) {this.code = code;this.message = message;}public int getCode() { return code; }public String getMessage() { return message; }// 统一包装成 Result 对象public <T> Result<T> toResult() {return new Result<>(this.code, this.message, null);}public <T> Result<T> toResult(T data) {return new Result<>(this.code, this.message, data);}
}
文件:Result.java
public class Result<T> {private int code;private String message;private T data;public Result(int code, String message, T data) {this.code = code;this.message = message;this.data = data;}// 快速成功public static <T> Result<T> ok(T data) {return ResultCode.SUCCESS.toResult(data);}// 快速失败public static <T> Result<T> error(ResultCode resultCode) {return resultCode.toResult();}public int getCode() { return code; }public String getMessage() { return message; }public T getData() { return data; }
}
文件:ResultCodeDemo.java
public class ResultCodeDemo {public static void main(String[] args) {// 模拟成功响应Result<String> success = Result.ok("用户数据");System.out.println("成功响应:");System.out.println(" code: " + success.getCode());System.out.println(" message: " + success.getMessage());System.out.println(" data: " + success.getData());System.out.println();// 模拟失败响应Result<String> error = Result.error(ResultCode.NOT_FOUND);System.out.println("失败响应:");System.out.println(" code: " + error.getCode());System.out.println(" message: " + error.getMessage());System.out.println(" data: " + error.getData());}
}
运行截图

对比
| 维度 | 硬编码返回 | 枚举统一返回 |
|---|---|---|
| 语义一致性 | 各处写法可能不同 | 一处定义,全局复用 |
| 维护成本 | 改消息要全局搜索 | 改枚举一处即可 |
| 国际化 | 难以扩展 | 可在枚举里扩展 getMessage(Locale) |
| 前端协作 | 错误码语义不清 | 前后端按枚举约定对接 |
完整可运行代码
把以下 7 个文件放在同一个包(或同一个文件夹)下:
| 文件名 | 类型 | 说明 |
|---|---|---|
OrderStatus.java |
enum |
场景 1 枚举定义 |
OrderStatusDemo.java |
class |
场景 1 运行入口(右键运行) |
DiscountStrategy.java |
enum |
场景 2 枚举定义 |
StrategyDemo.java |
class |
场景 2 运行入口(右键运行) |
ResultCode.java |
enum |
场景 3 枚举定义 |
Result.java |
class |
场景 3 泛型包装类 |
ResultCodeDemo.java |
class |
场景 3 运行入口(右键运行) |
注意:只有带 main 方法的 *Demo.java 才能直接运行,枚举文件本身没有 main 方法,无法直接运行。
总结
三个场景速查表
| 场景 | 判断标准 | 核心收益 |
|---|---|---|
| 状态/类型定义 | 一组固定值,且需要附带描述或码值 | 类型安全、可读性强、数据库映射方便 |
| 策略模式 | 多分支逻辑,且分支可能新增 | 消灭 if/else,符合开闭原则 |
| 统一返回码 | 前后端协作,需要集中管理错误语义 | 错误码标准化,降低维护成本 |
如果对你有帮助,欢迎点赞和收藏。有任何问题或更好的实践,欢迎在评论区交流。
