Java 枚举(Enum)从入门到进阶:定义、用法与最佳实践
1. 认识 Java 枚举枚举Enum是 JDK 1.5 引入的一种特殊类用于表示一组固定的常量。相比使用public static final定义常量枚举提供了类型安全、可读性更强、功能更丰富的解决方案。1.1 最简单的枚举// 定义一个季节枚举enumSeason{SPRING,SUMMER,AUTUMN,WINTER}publicclassTest{publicstaticvoidmain(String[]args){System.out.println(Season.SPRING);System.out.println(Season.SUMMER);System.out.println(Season.AUTUMN);System.out.println(Season.WINTER);}}运行结果SPRING SUMMER AUTUMN WINTER枚举常量默认以字符串形式输出其名称避免了硬编码魔数的问题增强了代码的可维护性。下图展示了枚举与普通类的关系以及枚举常量的本质继承Enumabstractname() : Stringordinal() : intvalues() : T[]valueOf(String) : TSeasonenumerationSPRINGSUMMERAUTUMNWINTER每个常量都是 Season 的静态实例2. 枚举常用方法每个枚举都继承自java.lang.Enum因此自带一些实用方法。2.1 values()遍历所有常量enumSeason{SPRING,SUMMER,AUTUMN,WINTER}for(Seasons:Season.values()){System.out.println(s);}2.2 ordinal()获取常量序号序号从 0 开始按照声明顺序递增。注意序号仅代表声明顺序不建议在业务逻辑中过度依赖。System.out.println(Season.SPRING.ordinal());// 0System.out.println(Season.AUTUMN.ordinal());// 22.3 valueOf()通过名称获取枚举实例SeasonsummerSeason.valueOf(SUMMER);System.out.println(summer);// SUMMER若传入不存在的名称会抛出IllegalArgumentException。2.4 name() 与 toString()name()返回枚举常量在定义时的名称且不能被重写。toString()默认返回名称但可以被重写以提供更友好的描述。Season.SPRING.name();// SPRINGSeason.SPRING.toString();// SPRING默认3. 在 switch 中使用枚举枚举使得switch语句更加清晰和安全编译器会检查是否覆盖了所有可能的分支。enumDay{MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY}publicclassSwitchDemo{publicstaticvoidmain(String[]args){DaytodayDay.FRIDAY;switch(today){caseMONDAY:System.out.println(周一综合征);break;caseFRIDAY:System.out.println(终于周五了);break;caseSATURDAY:caseSUNDAY:System.out.println(周末愉快);break;default:System.out.println(工作日加油);}}}4. 枚举进阶添加属性与方法枚举并不是只能存储名称它本质上是“有限个实例的类”。我们可以为它定义字段、构造器和自定义方法。4.1 带构造参数的枚举enumColor{RED(红色,#FF0000),GREEN(绿色,#00FF00),BLUE(蓝色,#0000FF);privatefinalStringchineseName;privatefinalStringhex;// 构造器只能是 private 或默认无需显式声明Color(StringchineseName,Stringhex){this.chineseNamechineseName;this.hexhex;}publicStringgetChineseName(){returnchineseName;}publicStringgetHex(){returnhex;}OverridepublicStringtoString(){returnString.format(%s(%s),chineseName,hex);}}// 使用System.out.println(Color.RED.getChineseName());// 红色System.out.println(Color.BLUE.getHex());// #0000FF4.2 枚举实现接口枚举可以实现一个或多个接口从而让每个常量根据需要提供不同的行为。interfaceGreetable{voidgreet();}enumEmployeeimplementsGreetable{MANAGER{Overridepublicvoidgreet(){System.out.println(经理你好有什么需要汇报的吗);}},DEVELOPER{Overridepublicvoidgreet(){System.out.println(开发代码已提交请 review。);}}}// 使用Employee.MANAGER.greet();// 经理你好有什么需要汇报的吗Employee.DEVELOPER.greet();// 开发代码已提交请 review。4.3 枚举抽象方法可以在枚举内部定义抽象方法让每个常量强制实现适合定义“策略模式”等场景。enumOperation{PLUS{Overridepublicdoubleapply(doublex,doubley){returnxy;}},MINUS{Overridepublicdoubleapply(doublex,doubley){returnx-y;}},TIMES{Overridepublicdoubleapply(doublex,doubley){returnx*y;}},DIVIDE{Overridepublicdoubleapply(doublex,doubley){if(y0)thrownewIllegalArgumentException(除数不能为0);returnx/y;}};publicabstractdoubleapply(doublex,doubley);}// 使用doubleresultOperation.PLUS.apply(10,5);// 15.0枚举进阶特性知识图谱枚举进阶构造器与属性定义字段private 构造器每个常量传入参数getter 方法实现接口统一接口约束每个常量独立实现多态行为抽象方法枚举内部定义 abstract每个常量强制实现策略模式四则运算示例5. 枚举与集合java.util.EnumSet和java.util.EnumMap是专为枚举设计的高效集合实现相比普通集合它们内部使用位向量实现性能更优。importjava.util.EnumSet;importjava.util.EnumMap;// EnumSet所有工作日EnumSetDayweekdaysEnumSet.range(Day.MONDAY,Day.FRIDAY);// EnumMap统计每个季节对应的月份数EnumMapSeason,IntegermonthCountnewEnumMap(Season.class);monthCount.put(Season.SPRING,3);monthCount.put(Season.SUMMER,3);// ...6. 枚举单例在《Effective Java》中单元素枚举被推荐为实现单例的最佳方式因为它天生防反射、防序列化攻击。publicenumSingleton{INSTANCE;publicvoiddoSomething(){System.out.println(单例方法执行);}}// 使用Singleton.INSTANCE.doSomething();7. 小结特性说明类型安全避免使用int常量带来的类型混淆自带方法values()、ordinal()、valueOf()等switch 支持更清晰的流程控制可带属性与方法像正常类一样定义字段、构造器、方法实现接口 / 抽象方法每个实例可拥有不同行为适合策略模式专用集合EnumSet/EnumMap提供极致性能单例最佳实践天生的序列化安全与反射安全合理使用枚举能让代码更优雅、健壮和易于维护。你可以从简单常量开始逐步引入属性和方法让枚举承担更多业务逻辑而不是仅作为标签存在。