第一部分:枚举基础

从传统常量到枚举类型的演进

学习目标

  • 理解枚举类型的必要性
  • 掌握枚举的基本语法
  • 了解枚举的底层实现
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处理状态转换
  • ✅ 演示多态使用