java枚举

📅 2026/6/26 16:38:11
java枚举
什么是枚举用一句话概括枚举就是“固定选项的集合”。为什么需要枚举类型安全编译期就能发现错误不会传入非法值可读性强OrderStatus.PAID比1或paid清晰得多防止非法值方法参数限定了取值范围调用方无法随意传值基础篇最简单的枚举publicenumOrderStatus{CREATED,// 已创建PAID,// 已支付SHIPPED,// 已发货COMPLETED// 已完成}枚举值之间用逗号隔开最后一个可以有分号。使用方式publicclassOrder{privateOrderStatusstatus;// ✅ 类型安全publicvoidpay(){if(statusOrderStatus.CREATED){statusOrderStatus.PAID;}}}进阶篇带字段和方法的枚举实际项目中枚举通常需要携带额外属性如编码、描述等publicenumOrderStatus{CREATED(0,已创建),PAID(1,已支付),SHIPPED(2,已发货),COMPLETED(3,已完成);// 成员变量privatefinalStringcode;// 用于数据库存储privatefinalStringdesc;// 用于前端展示// 构造方法必须是 privateprivateOrderStatus(Stringcode,Stringdesc){this.codecode;this.descdesc;}// getter 方法publicStringgetCode(){returncode;}publicStringgetDesc(){returndesc;}}关键点构造方法必须是private这是枚举的语法规定字段推荐用final枚举值应该是不可变的后面会重点讲枚举的内置方法Java 为所有枚举自动提供了几个实用方法// 1. name()返回枚举常量的名称OrderStatus.PAID.name();// 返回 PAID// 2. ordinal()返回声明顺序从 0 开始OrderStatus.CREATED.ordinal();// 返回 0OrderStatus.PAID.ordinal();// 返回 1// 3. valueOf(String)字符串转枚举OrderStatusstatusOrderStatus.valueOf(PAID);// 相当于 OrderStatus.PAID// 4. values()获取所有枚举常量for(OrderStatusstatus:OrderStatus.values()){System.out.println(status);}// 输出CREATED PAID SHIPPED COMPLETED应用场景1. 在实体类中作为字段publicclassOrderEntity{privateStringorderId;privateStringuserId;privateOrderStatusstatus;// ✅ 用枚举类型// 判断订单是否可支付publicbooleancanPay(){returnstatusOrderStatus.CREATED;}}2. 作为方法的参数或返回值publicclassOrderService{// 参数限制只能传 OrderStatus 中定义的值publicvoidupdateStatus(StringorderId,OrderStatusnewStatus){// 业务逻辑...}}3. 配合 switch 做多分支逻辑publicStringgetStatusDesc(OrderStatusstatus){switch(status){caseCREATED:return订单已创建等待支付;casePAID:return已支付正在准备发货;caseSHIPPED:return已发货请注意查收;caseCOMPLETED:return订单已完成;default:return未知状态;// 理论上不执行保留健壮性}}Spring Boot 中的枚举映射Enumerated在 Spring Boot JPA 项目中枚举需要存到数据库这时要用Enumerated注解EntityTable(nameorders)publicclassOrder{IdprivateStringorderId;privateStringuserId;// 方式一存枚举名称字符串Enumerated(EnumType.STRING)privateOrderStatusstatus;// 存 PAID 到数据库// 方式二存枚举序号数字⚠️ 不推荐// Enumerated(EnumType.ORDINAL)// private OrderStatus status; // 存 0, 1, 2... 到数据库}EnumType.STRINGvsEnumType.ORDINAL对比维度EnumType.STRING✅EnumType.ORDINAL❌存什么枚举名称如PAID序号如1可读性数据库里一眼看懂需要查文档才知道1代表什么顺序依赖不依赖声明顺序❌ 新增枚举插在中间会乱套推荐✅ 推荐使用❌ 不推荐有坑配合 MyBatis 使用另一种方式如果用 MyBatis可以自定义 TypeHandler 或直接用code字段存// 存 code 到数据库order.setStatus(OrderStatus.PAID.getCode());// 存 1枚举 vs 结构体对比初学时有个对比会清晰一些。核心区别在于实例数量对比维度枚举结构体实例数量固定有限编译期确定无限运行时随意 new能否伪造不能类型安全可以绕开常量定义适用场景固定选项状态、类型等数据容器用户、商品等一句话记住枚举是选一个结构体是装一堆。枚举必须不可变枚举值是全局单例的这意味着如果你修改了某个枚举值的属性会影响所有使用它的地方。❌ 反面示例在CSDN上看到的错误示例publicenumRole{WARRIOR(战士,100),// ❌ 没有 final 的字段MAGE(法师,80);privateStringname;privateinthp;privateRole(Stringname,inthp){this.namename;this.hphp;}publicvoidsetHp(inthp){// ❌ 提供了 setterthis.hphp;}}// 使用方Role.MAGE.setHp(50);// ❌ 修改了枚举值本身System.out.println(Role.MAGE.getHp());// 输出 50不是 80 了问题所有法师的血量都被改成了 50✅ 正确写法publicenumRole{WARRIOR(战士,100),MAGE(法师,80);privatefinalStringname;// ✅ finalprivatefinalinthp;// ✅ finalprivateRole(Stringname,inthp){this.namename;this.hphp;}publicStringgetName(){returnname;}publicintgetHp(){returnhp;}// ✅ 没有 setter}如果每个玩家的血量要独立变化怎么办血量应该存在玩家对象里而不是枚举里publicclassPlayer{privateRolerole;// 职业模板privateintcurrentHp;// 当前血量实例状态privateintcurrentAttack;// 当前攻击力privateintcurrentDefense;// 当前防御力publicPlayer(Rolerole){this.rolerole;this.currentHprole.getHp();// 从模板复制this.currentAttackrole.getAttack();this.currentDefenserole.getDefense();}publicvoidattack(Playertarget){intdamagethis.currentAttack-target.currentDefense;if(damage0){target.currentHp-damage;// ✅ 只修改玩家的血量}}}总结核心要点说明枚举是什么固定选项的集合用于表示有限的、不变的值为什么用枚举类型安全、可读性强、防止非法值基本语法public enum Xxx { A, B, C }带字段写法私有构造方法 final字段 getterSpring Boot 映射使用Enumerated(EnumType.STRING)存到数据库与结构体区别枚举实例数固定结构体可无限创建最重要原则枚举必须不可变所有字段都应该是final的