1. 枚举的本质
Java枚举是一种特殊的类,它表示一组固定常量。每个枚举常量都是枚举类的实例。
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
2. 枚举的优势
- 类型安全:避免使用魔法数字或字符串
- 代码可读性:使用有意义的名称
- 编译时检查:避免无效值
- 内置方法:values(), valueOf()等
二、枚举在项目中的实际应用
1. 状态管理(最常用场景)
订单状态管理
public enum OrderStatus {
// 定义订单状态
CREATED("已创建", 1),
PAID("已支付", 2),
SHIPPED("已发货", 3),
DELIVERED("已送达", 4),
COMPLETED("已完成", 5),
CANCELLED("已取消", 0);
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;
}
// 根据code获取枚举
public static OrderStatus fromCode(int code) {
for (OrderStatus status : values()) {
if (status.code == code) {
return status;
}
}
throw new IllegalArgumentException("无效状态码: " + code);
}
}
在业务中使用
public class Order {
private OrderStatus status;
public void cancelOrder() {
if (status == OrderStatus.PAID || status == OrderStatus.CREATED) {
status = OrderStatus.CANCELLED;
} else {
throw new IllegalStateException("当前状态不可取消订单");
}
}
public void updateStatus(OrderStatus newStatus) {
// 状态转换验证逻辑
status = newStatus;
}
}
2. 策略模式实现
支付方式处理
public enum PaymentMethod {
ALIPAY {
@Override
public void processPayment(BigDecimal amount) {
System.out.println("支付宝支付: " + amount);
// 调用支付宝API
}
},
WECHAT {
@Override
public void processPayment(BigDecimal amount) {
System.out.println("微信支付: " + amount);
// 调用微信支付API
}
},
BANK_TRANSFER {
@Override
public void processPayment(BigDecimal amount) {
System.out.println("银行转账: " + amount);
// 生成银行账户信息
}
};
public abstract void processPayment(BigDecimal amount);
}
在支付服务中使用
public class PaymentService {
public void processOrderPayment(Order order, PaymentMethod method) {
try {
method.processPayment(order.getTotalAmount());
order.setStatus(OrderStatus.PAID);
} catch (Exception e) {
order.setStatus(OrderStatus.PAYMENT_FAILED);
throw new PaymentException("支付失败", e);
}
}
}
3. 配置管理
系统配置项
public enum SystemConfig {
MAX_LOGIN_ATTEMPTS("security.max_login_attempts", "5"),
SESSION_TIMEOUT("session.timeout_minutes", "30"),
CACHE_ENABLED("cache.enabled", "true");
private final String key;
private final String defaultValue;
SystemConfig(String key, String defaultValue) {
this.key = key;
this.defaultValue = defaultValue;
}
public String getValue() {
// 从配置中心或配置文件中获取值
String value = ConfigService.getProperty(key);
return value != null ? value : defaultValue;
}
public int getIntValue() {
return Integer.parseInt(getValue());
}
public boolean getBooleanValue() {
return Boolean.parseBoolean(getValue());
}
}
在业务中使用
public class LoginService {
public void authenticate(String username, String password) {
int maxAttempts = SystemConfig.MAX_LOGIN_ATTEMPTS.getIntValue();
// 认证逻辑...
}
}
4. 错误码管理
统一错误码
public enum ErrorCode {
// 通用错误
SUCCESS(0, "成功"),
UNKNOWN_ERROR(1000, "未知错误"),
INVALID_PARAM(1001, "参数无效"),
// 用户相关
USER_NOT_FOUND(2001, "用户不存在"),
USER_DISABLED(2002, "用户已禁用"),
// 订单相关
ORDER_NOT_FOUND(3001, "订单不存在"),
ORDER_CANCEL_FAILED(3002, "订单取消失败");
private final int code;
private final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
public static ErrorCode fromCode(int code) {
for (ErrorCode error : values()) {
if (error.code == code) {
return error;
}
}
return UNKNOWN_ERROR;
}
}
在全局异常处理中使用
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
@ResponseBody
public ResponseEntity<ErrorResponse> handleBusinessEx(BusinessException ex) {
ErrorCode errorCode = ex.getErrorCode();
ErrorResponse response = new ErrorResponse(
errorCode.getCode(),
errorCode.getMessage(),
ex.getAdditionalInfo()
);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
}
}
public class ErrorResponse {
private int code;
private String message;
private Map<String, Object> details;
// 构造方法、getter等
}
5. 单例模式实现
线程安全的单例
public enum DatabaseConnection {
INSTANCE;
private Connection connection;
private DatabaseConnection() {
try {
// 初始化数据库连接
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "root";
String password = "password";
connection = DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
throw new RuntimeException("数据库连接失败", e);
}
}
public Connection getConnection() {
return connection;
}
public void executeQuery(String sql) {
// 执行SQL逻辑
}
}
在DAO层使用
public class UserDao {
public User findById(int id) {
Connection conn = DatabaseConnection.INSTANCE.getConnection();
try (PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?")) {
stmt.setInt(1, id);
ResultSet rs = stmt.executeQuery();
// 处理结果集
} catch (SQLException e) {
throw new DataAccessException("查询失败", e);
}
}
}
三、枚举的高级特性应用
1. 枚举与接口结合
状态转换接口
public interface StateTransition {
boolean canTransitionTo(OrderStatus nextStatus);
}
public enum OrderStatus implements StateTransition {
// 枚举常量...
@Override
public boolean canTransitionTo(OrderStatus nextStatus) {
// 实现状态转换规则
switch (this) {
case CREATED:
return nextStatus == PAID || nextStatus == CANCELLED;
case PAID:
return nextStatus == SHIPPED || nextStatus == CANCELLED;
// 其他状态转换规则...
default:
return false;
}
}
}
2. 枚举与Stream API结合
动态生成下拉选项
public class EnumUtils {
public static <E extends Enum<E>> List<Map<String, Object>> toOptionList(Class<E> enumClass) {
return Arrays.stream(enumClass.getEnumConstants())
.map(enumValue -> {
Map<String, Object> option = new HashMap<>();
option.put("value", enumValue.name());
// 如果枚举实现了Descriptive接口
if (enumValue instanceof Descriptive) {
option.put("label", ((Descriptive) enumValue).getDescription());
} else {
option.put("label", enumValue.name());
}
return option;
})
.collect(Collectors.toList());
}
}
// 前端使用
List<Map<String, Object>> statusOptions = EnumUtils.toOptionList(OrderStatus.class);
代码解析
public class EnumUtils {
1. 工具类定义
- 定义了一个名为
EnumUtils的公共工具类 - 工具类通常包含静态方法,不需要实例化
public static <E extends Enum<E>> List<Map<String, Object>> toOptionList(Class<E> enumClass) {
2. 方法签名
public static:公共静态方法,可通过类名直接调用<E extends Enum<E>>:泛型约束,确保E必须是枚举类型List<Map<String, Object>>:返回值类型,是一个列表,每个元素是键值对集合Class<E> enumClass:参数,传入一个枚举类的Class对象
作用:将枚举转换为前端可用的选项列表
return Arrays.stream(enumClass.getEnumConstants())
3. 获取枚举值并转为流
enumClass.getEnumConstants():获取该枚举类的所有枚举值(实例)Arrays.stream():将枚举值数组转为Java 8 Stream流,便于链式处理
.map(enumValue -> {
Map<String, Object> option = new HashMap<>();
4. 创建选项Map
map():流操作,将每个枚举值转换为新对象enumValue:当前处理的枚举实例new HashMap<>():创建空Map,用于存储选项数据
option.put("value", enumValue.name());
5. 设置选项值
enumValue.name():获取枚举常量的名称(如"PAID")- 存入Map,键为
"value",前端可通过此值识别选项
if (enumValue instanceof Descriptive) {
option.put("label", ((Descriptive) enumValue).getDescription());
} else {
option.put("label", enumValue.name());
}
6. 智能设置显示文本
- 检查枚举是否实现
Descriptive接口:- 如果实现:调用
getDescription()获取友好名称(如"已支付") - 未实现:使用枚举名称作为显示文本
- 如果实现:调用
label:前端显示用的文本
Descriptive接口示例:
public interface Descriptive {
String getDescription();
}
// 枚举实现
public enum OrderStatus implements Descriptive {
PAID("已支付"),
UNPAID("未支付");
private String description;
OrderStatus(String desc) {
this.description = desc;
}
@Override
public String getDescription() {
return description;
}
}
return option;
})
7. 返回构建的选项
- 每个枚举值转换为一个Map对象
- Map结构:
{ "value": "PAID", "label": "已支付" }
.collect(Collectors.toList());
}
8. 收集结果
collect(Collectors.toList()):将流中的Map收集到List中- 最终返回:
List<Map<String, Object>>
3. 枚举与注解结合
权限控制
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresRole {
UserRole[] value();
}
public enum UserRole {
ADMIN, MANAGER, USER, GUEST
}
// 在服务方法上使用
public class UserService {
@RequiresRole({UserRole.ADMIN, UserRole.MANAGER})
public void deleteUser(int userId) {
// 删除用户逻辑
}
}
// 权限切面
@Aspect
@Component
public class AuthorizationAspect {
@Before("@annotation(requiresRole)")
public void checkRole(JoinPoint jp, RequiresRole requiresRole) {
UserRole[] allowedRoles = requiresRole.value();
User currentUser = SecurityContext.getCurrentUser();
if (!Arrays.asList(allowedRoles).contains(currentUser.getRole())) {
throw new AccessDeniedException("权限不足");
}
}
}
1414

被折叠的 条评论
为什么被折叠?



