25.java枚举

1、概念

enum 的全称为 enumeration, 是 JDK 1.5 中引入的新特性。

枚举是一种语法糖,编译到字节码后是一个类继承 java.lang.Enum,而枚举值是静态final成员变量

在Java中,被 enum 关键字修饰的类型就是枚举类型。形式如下:

 enum Color { RED, GREEN, BLUE }

如果枚举不添加任何方法,枚举值默认为从0开始的有序数值。以 Color 枚举类型举例,它的枚举常量依次为 RED:0,GREEN:1,BLUE:2

枚举的好处:可以将常量组织起来,统一进行管理。

枚举的典型应用场景:错误码、状态机等。

  • 枚举类型的本质:除了不能继承,基本上可以将 enum 看做一个常规的类。

2、Enum抽象类

尽管 enum 看起来像是一种新的数据类型,事实上,enum是一种受限制的类,并且具有自己的方法

创建enum时,编译器会为你生成一个相关的类,这个类继承自 java.lang.Enum

Enum是所有 Java 语言枚举类型的公共基本类(注意Enum是抽象类),以下是它的常见方法:

返回类型 方法名称 方法说明
int compareTo(E o) 比较此枚举与指定对象的顺序
boolean equals(Object other) 当指定对象等于此枚举常量时,返回 true。
Class<?> getDeclaringClass() 返回与此枚举常量的枚举类型相对应的 Class 对象
String name() 返回此枚举常量的名称,在其枚举声明中对其进行声明
int ordinal() 返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)
String toString() 返回枚举常量的名称,它包含在声明中
static<T extends Enum<T>> T static valueOf(Class<T> enumType, String name) 返回带指定名称的指定枚举类型的枚举常量。

构造方法:

Modifier Constructor and Description
protected Enum(String name, int ordinal) 唯一的构造函数。

3、原始的接口定义常量

在JDK1.5 之前,在Java中定义常量都是public static final TYPE A;

或者接口中定义常量:

 public interface DayConstant {
     int MONDAY = 1;
     int TUESDAY = 2;
     int WEDNESDAY = 3;
     int THURSDAY = 4;
     int FRIDAY = 5;
     int SATURDAY = 6;
     int SUNDAY = 7;
 }

有了枚举,你可以将有关联关系的常量组织起来,使代码更加易读、安全,并且还可以使用枚举提供的方法

4、语法

创建枚举类型要使用 enum 关键字,隐含了所创建的类型都是 java.lang.Enum 类的子类(java.lang.Enum 是一个抽象类)。枚举类型符合通用模式 Class Enum<E extends Enum<E>>,而 E 表示枚举类型的名称。

枚举类型的每一个值都将映射到 protected Enum(String name, int ordinal) 构造函数中,在这里,每个值的名称都被转换成一个字符串,并且序数设置表示了此设置被创建的顺序。

定义:

 public enum DayEnum {
     MONDAY,
     TUESDAY,
     WEDNESDAY,
     THURSDAY,
     FRIDAY,
     SATURDAY,
     SUNDAY
 }

注意:

1、值一般是大写的字母,多个值之间以逗号分隔。

2、枚举类型可以像类(class)类型一样,定义为一个单独的文件,当然也可以定义在其他类内部

使用:

 public class App {
     public static void main(String[] args) {
         DayEnum[] values = DayEnum.values();
         for (DayEnum value : values) {
             System.out.println(value.ordinal()+":"+value.name());
         }
     }
 }

values()方法的作用就是获取枚举类中的所有变量,并作为数组返回

valueOf(String name)方法与Enum类中的valueOf方法的作用类似根据名称获取枚举变量,编译器生成的valueOf方法最终还是调用了Enum类的valueOf方法

结果:

 0:MONDAY
 1:TUESDAY
 2:WEDNESDAY
 3:THURSDAY
 4:FRIDAY
 5:SATURDAY
 6:SUNDAY

反编译查看如下:

javap DayEnum

 public final class com.woniuxy.DayEnum extends java.lang.Enum<com.woniuxy.DayEnum> {
   public static final com.woniuxy.DayEnum MONDAY;
   public static final com.woniuxy.DayEnum TUESDAY;
   public static final com.woniuxy.DayEnum WEDNESDAY;
   public static final com.woniuxy.DayEnum THURSDAY;
   public static final com.woniuxy.DayEnum FRIDAY;
   public static final com.woniuxy.DayEnum SATURDAY;
   public static final com.woniuxy.DayEnum SUNDAY;
   public static com.woniuxy.DayEnum[] values();
   public static com.woniuxy.DayEnum valueOf(java.lang.String);
   static {};
 }

1、创建enum时,编译器会为你生成一个相关的类DayEnum,这个类继承自 java.lang.Enum。

2、每个枚举变量都是枚举类DayEnum的实例,相当于MONDAY = new DayEnum(“MONDAY”, 0),按序号来。

3、每个成员变量都是final static修饰,也就是常量,常量命令规则要求全部大写

CFR反编译(了解)

 java -jar cfr-0.152.jar D:\workspace\idea\day0910\target\classes\com\woniuxy\DayEnum.class --sugarenums false
 参数--sugerenums false表示会解析枚举语法糖

反编译参考

CFR使用说明

参考2

 //反编译DayEnum.class
 final class DayEnum extends Enum
 {
     //编译器为我们添加的静态的values()方法
     public static DayEnum[] values()
     {
         return (DayEnum[])$VALUES.clone();
     }
     //编译器为我们添加的静态的valueOf()方法,注意间接调用了Enum也类的valueOf方法
     public static DayEnum valueOf(String s)
     {
         return (DayEnum)Enum.valueOf(com/woniuxy/enumdemo/DayEnum, s);
     }
     //私有构造函数
     private DayEnum(String s, int i)
     {
         //调用父类Enum的构造方法
         super(s, i);
     }
      //前面定义的7种枚举实例
     public static final DayEnum MONDAY;
     public static final DayEnum TUESDAY;
     public static final DayEnum WEDNESDAY;
     public static final DayEnum THURSDAY;
     public static final DayEnum FRIDAY;
     public static final DayEnum SATURDAY;
     public static final DayEnum SUNDAY;
     private static final DayEnum $VALUES[];
 
     static
     {
         //实例化枚举实例
         MONDAY = new DayEnum("MONDAY", 0);
         TUESDAY = new DayEnum("TUESDAY", 1);
         WEDNESDAY = new DDayEnumay("WEDNESDAY", 2);
         THURSDAY = new DayEnum("THURSDAY", 3);
         FRIDAY = new DayEnum("FRIDAY", 4);
         SATURDAY = new DayEnum("SATURDAY", 5);
         SUNDAY = new DayEnum("SUNDAY", 6);
         $VALUES = (new DayEnum[] {
             MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
         });
     }
 }

4、枚举的进阶用法

在前面的分析中,我们都是基于简单枚举类型的定义,也就是在定义枚举时只定义了枚举实例类型,并没定义方法或者成员变量,实际上使用关键字enum定义的枚举类,除了不能使用继承(因为编译器会自动为我们继承Enum抽象类而Java只支持单继承,因此枚举类是无法手动实现继承的),可以把enum类当成常规类,也就是说我们可以向enum类中添加方法和变量,甚至是mian方法。

Java 语法中却不允许使用赋值符号 = 为枚举常量赋值,但枚举可以添加普通方法、静态方法、抽象方法、构造方法

 public enum DayEnum {
     MONDAY(1001, "monday"),
     TUESDAY(1002, "tuesday"),
     WEDNESDAY(1003, "wednesday"),
     THURSDAY(1004, "thursday"),
     FRIDAY(1005, "friday"),
     SATURDAY(1006, "saturday"),
     SUNDAY(1007, "sunday");//加上分号,否则报错
     private int code;
     private String message;
 
     DayEnum(int code, String message) {
         this.code = code;
         this.message = message;
     }
 
     public int getCode() {
         return code;
     }
 
     public String getMessage() {
         return message;
     }
 
     @Override
     public String toString() {
         return "DayEnum{" +
                 "code=" + code +
                 ", message='" + message + '\'' +
                 '}';
     }
 }
 

注意:

1、如果要为enum定义方法,那么必须在enum的最后一个实例尾部添加一个分号。

2、在enum中,必须先定义实例,不能将字段或方法定义在实例前面。否则,编译器会报错。

测试调用:

 public class App {
     public static void main(String[] args) {
         DayEnum[] values = DayEnum.values();
         for (DayEnum day : values) {
             System.out.println(day.getCode()+":"+day.getMessage());
         }
     }
 }

打印结果:

 1001:monday
 1002:tuesday
 1003:wednesday
 1004:thursday
 1005:friday
 1006:saturday
 1007:sunday

5、应用案例:

错误码(常用):

 package com.woniuxy.wms.util;
 
 /**
  * @author :fengSir
  * @date :Created By 2023-06-30 10:40
  * @description :服务状态码
  */
 public enum ServerCode {
 
     //自定义的状态码
     ERR_BAD_REQUEST(400, "请求参数格式错误"),
     ERR_UNAUTHORIZED(401, "未授权登录"),
     ERR_FORBIDDEN(403, "无权限访问"),
     ERR_NOT_FOUND(404, "相关资源或数据不存在"),
     ERR_CONFLICT(409, "数据冲突"),
     ERR_INSERT(5001, "插入数据时的错误"),
     ERR_DELETE(5002, "删除数据时的错误"),
     ERR_UPDATE(5003, "修改数据时的错误"),
     ERR_INTERNAL_SERVER_ERROR(500, "服务器内部错误"),
     ERR_JWT_PARSE(6000, "解析jwt失败:格式错误,或签名错误"),
     ERR_JWT_EXPIRED(6001, "解析jwt失败:过期"),
     MONITOR_EXCEPTION(304, "文件监控异常");
     //错误码
     private final Integer code;
     //提示信息
     private final String message;
 
     //构造函数
     ServerCode(Integer code, String message) {
         this.code = code;
         this.message = message;
     }
 
     //获取状态码
     public Integer getCode() {
         return code;
     }
 
     //获取提示信息
     public String getMessage() {
         return message;
     }
 
 }
 

自定义属性声明为final类型的,因为枚举定义完之后不能修改

不要提供setter方法

服务器返回前端

 Result.error(ServiceCode.ERR_FORBIDDEN)

更多参考

状态机(了解):

我们经常使用switch语句来写状态机。JDK7以后,switch已经支持 intcharStringenum 类型的参数。这几种类型的参数比较起来,使用枚举的switch代码更具有可读性。

 enum Signal {RED, YELLOW, GREEN}
 
 public static String getTrafficInstruct(Signal signal) {
     String instruct = "信号灯故障";
     switch (signal) {
         case RED:
             instruct = "红灯停";
             break;
         case YELLOW:
             instruct = "黄灯请注意";
             break;
         case GREEN:
             instruct = "绿灯行";
             break;
         default:
             break;
     }
     return instruct;
 }

注意:不能在switch()括号中直接写Signal类名,必须是Signal的变量名称signal

6、总结

  • enum是一种受限制的类,并且具有自己的方法
  • 声明使用enum关键字,声明的枚举默认继承自 java.lang.Enum
  • 枚举可以声明一个独立的文件(常见),也可以声明在一个类中(不常见)
  • 枚举成员全部大写,每个成员其实是枚举对应类的实例,每个实例调用java.lang.Enum中的Enum(String name, int ordinal) 唯一的构造函数,并且该实例为public static final修饰,因为是常量,所以要求全部大写。
  • 枚举中可以添加属性,并可以生成set方法,(get方法一般用不到,因为枚举成员是常量,只能被赋值一次,并在创建时就赋值了)
  • 枚举可以添加普通方法、静态方法、抽象方法、构造方法,
  • 在添加属性或方法时,枚举成员必须是首先声明,且枚举成员最后一位加上分号
  • 开发过程用使用比较多的场景就是作为错误码传值返回给前台
posted @ 2025-05-12 16:42  icui4cu  阅读(30)  评论(0)    收藏  举报