首页 > 编程语言 >java中的enum-java中特殊的class;通过字节码来分析enum构成

java中的enum-java中特殊的class;通过字节码来分析enum构成

时间:2024-08-29 20:37:11浏览次数:13  
标签:lang 码来 String service enum sms Weekday java class

§1 先思考一个问题

先思考一个问题:我们在enum类里,可以直接使用 values() 或 valueOf(String name) 方法,我们也没有在enum类里定义这两个方法,怎么就能直接使用呢?

 

这里先按下不表。

下面是正文。

§2 enum类及其编译后的字节码

在java编程中,我们经常会定义和使用枚举。

简单的enum类及其字节码概览

一个简单的 enum 类定义如下:

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

 

 

Java Enum类型的语法结构尽管和java类的语法不一样,应该说差别比较大。但是经过编译器编译之后产生的是一个class文件。我们反编译该class文件后,可以看到实际上是生成了一个class类,该类继承了 java.lang.Enum<E>,其中包括了常量和方法。

 

首先,编译 Weekday.java 文件:

javac Weekday.java

经过 javac 编译后得到一个名字为Weekday.class的class字节码文件。然后,使用 javap 命令查看生成的字节码文件源码:

javap -c Weekday.class

生成的字节码文件源码可能会类似于下面的内容(这是经过简化和部分省略的例子):

Compiled from "Weekday.java"
public final class Weekday extends java.lang.Enum<Weekday> {
  public static final Weekday MONDAY;
  public static final Weekday TUESDAY;
  public static final Weekday WEDNESDAY;
  public static final Weekday THURSDAY;
  public static final Weekday FRIDAY;
  public static final Weekday SATURDAY;
  public static final Weekday SUNDAY;
   
  static {};
  private Weekday();
  public static Weekday[] values();
  public static Weekday valueOf(java.lang.String);
}

 

这段字节码文件源码显示了 Weekday 枚举类中的常量以及一些生成的方法,例如 values() 和 valueOf(String) 等。实际的字节码文件源码会更加复杂,其中包含了更多的细节和信息。

所以实际上Enum类型就是以Java类来实现的,没有什么新的特点,只不过java编译器帮我们做了语法的解析和编译。

 

附图:在IDEA Terminal窗口中所执行的命令

 

 

 

功能丰富的enum类 及其字节码详解

我们知道,enum 类型可以包含除了默认的 name() 和 ordinal() 方法之外的扩展信息。我们可以为每个枚举常量添加字段、构造函数、方法等,使得枚举类型更加灵活和功能丰富。

我们看下面的 Weekday枚举。

public enum Weekday {
    MONDAY(1, "星期一"), TUESDAY(2, "星期二"), WEDNESDAY(3, "星期三"), THURSDAY(4, "星期四"), FRIDAY(5, "星期五"), SATURDAY(6, "星期六"), SUNDAY(7, "星期日");
 
    private final int dayNo;
    private final String name;
 
    private Weekday(int dayNo, String name) {
        this.dayNo = dayNo;
        this.name = name;
    }
 
    public static Weekday getEnum(int dayNo) {
        for (Weekday d : values()) {
            if (d.dayNo == dayNo)
                return d;
        }
        return null;
    }
}

 

下面是javap打印出来的完整的字节码源码:

Compiled from "Weekday.java"
public final class com.sms.service.Weekday extends java.lang.Enum<com.sms.service.Weekday> {
  public static final com.sms.service.Weekday MONDAY;
  public static final com.sms.service.Weekday TUESDAY;
  public static final com.sms.service.Weekday WEDNESDAY;
  public static final com.sms.service.Weekday THURSDAY;
  public static final com.sms.service.Weekday FRIDAY;
  public static final com.sms.service.Weekday SATURDAY;
  public static final com.sms.service.Weekday SUNDAY;
 
  public static com.sms.service.Weekday[] values();
    Code:
       0: getstatic     #25                 // Field $VALUES:[Lcom/sms/service/Weekday;
       3: invokevirtual #29                 // Method "[Lcom/sms/service/Weekday;".clone:()Ljava/lang/Object;
       6: checkcast     #30                 // class "[Lcom/sms/service/Weekday;"
       9: areturn
 
  public static com.sms.service.Weekday valueOf(java.lang.String);
    Code:
       0: ldc           #1                  // class com/sms/service/Weekday
       2: aload_0
       3: invokestatic  #34                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
       6: checkcast     #1                  // class com/sms/service/Weekday
       9: areturn
 
  public static com.sms.service.Weekday getEnum(int);
    Code:
       0: invokestatic  #52                 // Method values:()[Lcom/sms/service/Weekday;
       3: astore_1
       4: aload_1
       5: arraylength
       6: istore_2
       7: iconst_0
       8: istore_3
       9: iload_3
      10: iload_2
      11: if_icmpge     37
      14: aload_1
      15: iload_3
      16: aaload
      17: astore        4
      19: aload         4
      21: getfield      #44                 // Field dayNo:I
      24: iload_0
      25: if_icmpne     31
      28: aload         4
      30: areturn
      31: iinc          3, 1
      34: goto          9
      37: aconst_null
      38: areturn
 
  static {};
    Code:
       0: new           #1                  // class com/sms/service/Weekday
       3: dup
       4: ldc           #56                 // String MONDAY
       6: iconst_0
       7: iconst_1
       8: ldc           #57                 // String 星期一
      10: invokespecial #59                 // Method "<init>":(Ljava/lang/String;IILjava/lang/String;)V
      13: putstatic     #3                  // Field MONDAY:Lcom/sms/service/Weekday;
      16: new           #1                  // class com/sms/service/Weekday
      19: dup
      20: ldc           #62                 // String TUESDAY
      22: iconst_1
      23: iconst_2
      24: ldc           #63                 // String 星期二
      26: invokespecial #59                 // Method "<init>":(Ljava/lang/String;IILjava/lang/String;)V
      29: putstatic     #7                  // Field TUESDAY:Lcom/sms/service/Weekday;
      32: new           #1                  // class com/sms/service/Weekday
      35: dup
      36: ldc           #65                 // String WEDNESDAY
      38: iconst_2
      39: iconst_3
      40: ldc           #66                 // String 星期三
      42: invokespecial #59                 // Method "<init>":(Ljava/lang/String;IILjava/lang/String;)V
      45: putstatic     #10                 // Field WEDNESDAY:Lcom/sms/service/Weekday;
      48: new           #1                  // class com/sms/service/Weekday
      51: dup
      52: ldc           #68                 // String THURSDAY
      54: iconst_3
      55: iconst_4
      56: ldc           #69                 // String 星期四
      58: invokespecial #59                 // Method "<init>":(Ljava/lang/String;IILjava/lang/String;)V
      61: putstatic     #13                 // Field THURSDAY:Lcom/sms/service/Weekday;
      64: new           #1                  // class com/sms/service/Weekday
      67: dup
      68: ldc           #71                 // String FRIDAY
      70: iconst_4
      71: iconst_5
      72: ldc           #72                 // String 星期五
      74: invokespecial #59                 // Method "<init>":(Ljava/lang/String;IILjava/lang/String;)V
      77: putstatic     #16                 // Field FRIDAY:Lcom/sms/service/Weekday;
      80: new           #1                  // class com/sms/service/Weekday
      83: dup
      84: ldc           #74                 // String SATURDAY
      86: iconst_5
      87: bipush        6
      89: ldc           #75                 // String 星期六
      91: invokespecial #59                 // Method "<init>":(Ljava/lang/String;IILjava/lang/String;)V
      94: putstatic     #19                 // Field SATURDAY:Lcom/sms/service/Weekday;
      97: new           #1                  // class com/sms/service/Weekday
     100: dup
     101: ldc           #77                 // String SUNDAY
     103: bipush        6
     105: bipush        7
     107: ldc           #78                 // String 星期日
     109: invokespecial #59                 // Method "<init>":(Ljava/lang/String;IILjava/lang/String;)V
     112: putstatic     #22                 // Field SUNDAY:Lcom/sms/service/Weekday;
     115: invokestatic  #80                 // Method $values:()[Lcom/sms/service/Weekday;
     118: putstatic     #25                 // Field $VALUES:[Lcom/sms/service/Weekday;
     121: return
}
View Code

 

由字节码可见,javac为这个class生成了7个Weekday类型的公共静态常量MODAY、TUESDAY、...、SUNDAY,它们在这个class的static代码块中得到初始化。

static代码块的大致逻辑是:

1. 创建类型为Weekday的实例 new Weekday("MONDAY", 0, 1, "星期一"),赋值给静态变量MONDAY。见下方字节码片段。

 0: new           #1                  // class com/sms/service/Weekday
 3: dup
 4: ldc           #56                 // String MONDAY
 6: iconst_0
 7: iconst_1
 8: ldc           #57                 // String 星期一
10: invokespecial #59                 // Method "<init>":(Ljava/lang/String;IILjava/lang/String;)V
13: putstatic     #3                  // Field MONDAY:Lcom/sms/service/Weekday;
View Code

2. 同第一步,依次创建实例、给静态变量TUESDAY、...、SUNDAY赋值。

3. 调用静态方法 values()。

4. 给静态变量 $VALUES 赋值。

 

§3 java.lang.Enum类介绍

javadoc及类声明

/*
This is the common base class of all Java language enumeration types. More information about enums, including descriptions of the implicitly declared methods synthesized by the compiler, can be found in section 8.9 of The Java™ Language Specification.
Note that when using an enumeration type as the type of a set or as the type of the keys in a map, specialized and efficient set and map implementations are available.
Since: 1.5
See Also: Class.getEnumConstants(), java.util.EnumSet, java.util.EnumMap
*/
public abstract class Enum<E extends Enum<E>>
     implements Comparable<E>, Serializable {

 

Enum类的成员

 

从中我们get到:

① Enum是所有Java枚举类型的公共基类。

② Enum是一个抽象类,我们不能new一个Enum实例。

Enum的默认受保护构造器Enum(String, int),第一个参数是我们定义的枚举常量的名称,对应的field是Enum#name,第二个参数指的是枚举的顺序,对应的field是Enum#ordinal。

Enum定义了valueOf方法,用以根据枚举常量的name来获取枚举常量。

 

枚举虽然是class,但是并不支持继承

  • 尝试继承自枚举时,会提示: Cannot inherit from enum 'jstudy.enumsinterface.MyEnum'
  • 尝试继承自class类时,会提示: No extends clause allowed for enum

这大概也佐证了enum是一种特殊的class。

不过,枚举支持实现接口。我们项目中,几乎所有enum都实现了EnumAbility<T>这个interface。这体现了java对面向接口编程理论的最强大的支持。

 

§4 回到文章开头的问题

回到文章开头的问题。为什么我们在创建的enum类里可以直接访问 values() 或 valueOf(String) 方法呢?

从上面的字节码源码可以看出来,这2个方法是 java编译器 给加上的。换句话说,在 Java 中,所有的枚举类型都是继承自 java.lang.Enum 类。Java编译器在编译Enum 类时,会生成一些方法,其中包括 values() 和 valueOf(String name) 方法。因为这些方法是在编译时由编译器自动生成并添加到枚举类中的,所以,我们在任何枚举类中都可以直接使用这两个方法,而无需显式地在枚举类中定义它们。

就像我们在model类上添加 lombok工具的 @Data 注解,就可以使用这个model对象的getter和setter。

就像我们在枚举类上添加 enumgen工具的 @GetByEnum 注解,就可以使用 getBeanByCode(code)。

 

标签:lang,码来,String,service,enum,sms,Weekday,java,class
From: https://www.cnblogs.com/buguge/p/18387517

相关文章

  • 计算机毕业设计选题推荐-在线音乐网站-音乐专辑商城-Java/Python项目实战
    ✨作者主页:IT研究室✨个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。☑文末获取源码☑精彩专栏推荐⬇⬇⬇Java项目Python项目安卓项目微信小程序项目......
  • 计算机毕业设计选题推荐-中药材进存销管理系统-Java/Python项目实战
    ✨作者主页:IT研究室✨个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。☑文末获取源码☑精彩专栏推荐⬇⬇⬇Java项目Python项目安卓项目微信小程序项目......
  • Java——Stream 流的使用详解
    Stream 是一个可以用于操作集合、数组等数据源的API,主要进行数据的转换、筛选、聚合等操作这样做可以避免显式地使用迭代器或者循环来操作集合,提高代码的可读性和简洁性特点: 1、无存储性:是基于数据源的对象,本身不存储元素,而是通过管道将数据源元素传递给操作2......
  • Java中super关键字的学习
    super关键字目录super关键字1.访问父类的成员变量2.调用父类的方法3.调用父类的构造方法4.在实现接口的类中调用接口的默认方法注意事项在Java中,super是一个关键字,它主要用于在子类中引用父类的成员(包括字段、方法和构造方法),或者用于解决子类和父类之间的命名冲突。以下是s......
  • java毕业设计-基于springboot+vue的高校自习室预约系统设计和实现,基于springboot+vue
    文章目录前言演示视频项目架构和内容获取(文末获取)项目相关文件系统功能部分实现截图架构设计MVC的设计模式基于B/S的架构技术栈具体功能模块设计系统需求分析可行性分析系统测试为什么我?关于我我自己的网站项目开发案例前言博主介绍:✌️码农一枚,专注于大学生项目......
  • Java面试题--1基础篇-03 __八股文 备战春招,秋招
    八、泛型Java中的泛型是什么?泛型是JDK1.5的一个新特性,**泛型就是将类型参数化,其在编译时才确定具体的参数。**这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。使用泛型的好处是什么?远在JDK1.4版本的时候,那时候是没有泛型的概......
  • Java面试题--2集合篇-01 __八股文 备战春招,秋招
    1.常见的集合有哪些?Java集合类主要由两个根接口Collection和Map派生出来的,Collection派生出了三个子接口:List、Set、Queue(Java5新增的队列),因此Java集合大致也可分成List、Set、Queue、Map四种接口体系。注意:Collection是一个接口,Collections是一个工具类,Map不是Collectio......
  • Java算法之Gnome 排序
    简介Gnome排序,又称为双向插入排序或鸡尾酒排序,是一种改进的插入排序算法。它在每次迭代中不仅将最小的元素移动到前面,同时也将最大的元素移动到后面。这种排序算法在每次迭代中同时向两个方向进行移动,因此得名。算法步骤从数组的两端开始,向中间进行扫描。如果左侧元素大于......
  • Java算法之基数排序(Radix Sort)
    简介基数排序是一种非比较型整数排序算法,其原理是按照低位先排序,然后收集,再按照高位排序,再收集,依次类推,直到最高位。这种方法可以视为对每个位上的数字进行稳定的排序。算法步骤确定最大数的位数。对每一位进行排序:从最低位开始,使用稳定的排序算法(如计数排序)对当前位进......
  • 基于Java的民宿管理系统
    系统包含:源码+论文所用技术:SpringBoot+Vue+SSM+Mybatis+Mysql免费提供给大家参考或者学习,获取资料请私聊我目录第1章绪论 11.1选题动因 11.2目的和意义 11.3论文结构安排 2第2章开发环境与技术 32.1MYSQL数据库 32.2Tomcat介绍 32.3vue技术 42.4Sp......