注意,枚举类values(),valueof()这两个静态方法既不是子类我们自己写的方法,也不是父类的方法,而是jVM自动生成的两个方法

package com.ydlclass.enumTest;

public enum SeasonConstant {
    //存在这样的需求,比如季节的四个常量,在以往我们可以使用静态常量的方式来定义并区分这四个季节的常量。
    //但是我们假设需要在这几个常量的基础上增加更多的信息,这时后就需要封装。
    //我们可以为每个季节创建一个单例,也就是SeasonConstant这个类的字段中都有几个字段,以此达到存储多个信息的目的。


    //从现在开始,我们可以使用枚举的方式来实现;在使用静态常量的时候可以发现重复的代码太多,不同的季节的生成只是引用的名称不同。
    //所以我们使用枚举的方式,这个枚举在最初就是将重复的代码删除,但是删除完后没明显不符合类的定义方式我们就把class改写为enum

    SPRING,SUMMER,AUTUMN,WINTER;//这些枚举相当与public static final SeasonConstant SPRING = new SeasonConstant();
    //本质上春夏秋冬是四个对象,即new了四个对象,这就是枚举类的本质,可以查看其字节码文件
    public static void main(String[] args) {

        //分析字节码文件之后可以明确的知道enum到底是一个什么?
        //枚举是一个类,这是一个继承至java.lang.Enum类的一个类,java.lang.Enum这个类存在着一个含有两个参数的构造器,第一个参数是这个Enum对象的
        //名字,另一个ordinal则是枚举的序号。

        System.out.println(SeasonConstant.values());//values是子类的静态方法,所以是SeasonConstant来直接调用
        
        System.out.println(SeasonConstant.WINTER.ordinal());//这个方法是父类的成员方法,由于子类继承父类,所以是由子类对象负责调用
        System.out.println(SeasonConstant.SUMMER.name());//这个方法是父类的成员方法,由于子类继承父类,所以是由子类实例负责调用
        System.out.println(SeasonConstant.SUMMER.getDeclaringClass());//这个方法是父类的成员方法,由于子类继承父类,所以是由子类实例负责调用
        //getDeclaringClass()方法是查看此实例对象的所属的枚举类的全限定名称
        SeasonConstant[] values = SeasonConstant.values();

        for (int i = 0; i < values.length; i++) {
            System.out.println(values[i]);
        }
        
        
        System.out.println(SeasonConstant.valueOf("SPRING"));


        //注意,枚举类values(),valueof()这两个静态方法既不是子类我们自己写的方法,也不是父类的方法,而是jVM自动生成的两个方法


    }

}

上述类去掉方法调用后的字节码文件,可以看清楚枚举的本质是一个什么,我们定义一个枚举时明明没有创建子类的成员方法,这些方法是哪里来的?你知道常见的枚举类的方法有哪些?这些方法的来源是什么?哪些是静态的方法?哪些是父类对象的方法?

答:在日常的使用过程中我们使用枚举时都比较想当然的使用枚举的一些方法,比如values(),valueOf(),getDeclaringClass(),name(),ordinal()方法,这些方法在调用时的方式不同,那么是什么造成了这些方法的不同呢?

查看下面的字节码文件可以知道,枚举的本质实际上是一个类,这个类时继承java.lang.Enum类的,这时一个抽象类。并且这个类提供了一个含有两个参数的构造方法,第一个参数是String类型的,其实就是枚举的名字,第二个是Int类型的参数名ordinal,并且是从0开始的。用于表示这个枚举实例是在枚举类中的第几个对象。

此外,我们知道,这五个方法我们在定义枚举的时候是没有定义的,所以我们推测父类中存在这样的方法,可能全有,也可能不是全都包含。查看父类的源码可以知道包含name(),ordinal(),getDeclareClass(),这三个方法,并且是成员方法。所以比较明显的就是这三个方法是子类的实例对象负责调用的。

但是我们查看源码却并没有values(),valueof()方法,但是通过子类,也就是我们定义的泛型类的字节码文件可以知道,jvm为我们自动创建了两个,枚举的方法,values(),valueOf()静态方法。使用public final static来修饰这两个方法。顾这两个方法的调用是我们创建的枚举类来直接调用。

以下为字节码文件:

Classfile /D:/code/MyReviewer/out/production/api/com/ydlclass/enumTest/SeasonConstant.class
  Last modified 2022-4-4; size 1252 bytes
  MD5 checksum 597b46dd7989b7edf21c49795df2ec22
  Compiled from "SeasonConstant.java"
public final class com.ydlclass.enumTest.SeasonConstant extends java.lang.Enum<com.ydlclass.enumTest.SeasonConstant>
//可以发现,我们使用的一个enum关键字创建的泛型类,其实被编译器改编为了这样的一个类的文件,使用final修饰的类,说明不能被继承,这个类继承了java.lang.enum类,并且还使用了泛型。
  minor version: 0
  major version: 55
  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
Constant pool:
   #1 = Fieldref           #4.#46         // com/ydlclass/enumTest/SeasonConstant.$VALUES:[Lcom/ydlclass/enumTest/SeasonConstant;
   #2 = Methodref          #47.#48        // "[Lcom/ydlclass/enumTest/SeasonConstant;".clone:()Ljava/lang/Object;
   #3 = Class              #23            // "[Lcom/ydlclass/enumTest/SeasonConstant;"
   #4 = Class              #49            // com/ydlclass/enumTest/SeasonConstant
   #5 = Methodref          #16.#50        // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
   #6 = Methodref          #16.#51        // java/lang/Enum."<init>":(Ljava/lang/String;I)V
   #7 = String             #17            // SPRING
   #8 = Methodref          #4.#51         // com/ydlclass/enumTest/SeasonConstant."<init>":(Ljava/lang/String;I)V
   #9 = Fieldref           #4.#52         // com/ydlclass/enumTest/SeasonConstant.SPRING:Lcom/ydlclass/enumTest/SeasonConstant;
  #10 = String             #19            // SUMMER
  #11 = Fieldref           #4.#53         // com/ydlclass/enumTest/SeasonConstant.SUMMER:Lcom/ydlclass/enumTest/SeasonConstant;
  #12 = String             #20            // AUTUMN
  #13 = Fieldref           #4.#54         // com/ydlclass/enumTest/SeasonConstant.AUTUMN:Lcom/ydlclass/enumTest/SeasonConstant;
  #14 = String             #21            // WINTER
  #15 = Fieldref           #4.#55         // com/ydlclass/enumTest/SeasonConstant.WINTER:Lcom/ydlclass/enumTest/SeasonConstant;
  #16 = Class              #56            // java/lang/Enum
  #17 = Utf8               SPRING
  #18 = Utf8               Lcom/ydlclass/enumTest/SeasonConstant;
  #19 = Utf8               SUMMER
  #20 = Utf8               AUTUMN
  #21 = Utf8               WINTER
  #22 = Utf8               $VALUES
  #23 = Utf8               [Lcom/ydlclass/enumTest/SeasonConstant;
  #24 = Utf8               values
  #25 = Utf8               ()[Lcom/ydlclass/enumTest/SeasonConstant;
  #26 = Utf8               Code
  #27 = Utf8               LineNumberTable
  #28 = Utf8               valueOf
  #29 = Utf8               (Ljava/lang/String;)Lcom/ydlclass/enumTest/SeasonConstant;
  #30 = Utf8               LocalVariableTable
  #31 = Utf8               name
  #32 = Utf8               Ljava/lang/String;
  #33 = Utf8               <init>
  #34 = Utf8               (Ljava/lang/String;I)V
  #35 = Utf8               this
  #36 = Utf8               Signature
  #37 = Utf8               ()V
  #38 = Utf8               main
  #39 = Utf8               ([Ljava/lang/String;)V
  #40 = Utf8               args
  #41 = Utf8               [Ljava/lang/String;
  #42 = Utf8               <clinit>
  #43 = Utf8               Ljava/lang/Enum<Lcom/ydlclass/enumTest/SeasonConstant;>;
  #44 = Utf8               SourceFile
  #45 = Utf8               SeasonConstant.java
  #46 = NameAndType        #22:#23        // $VALUES:[Lcom/ydlclass/enumTest/SeasonConstant;
  #47 = Class              #23            // "[Lcom/ydlclass/enumTest/SeasonConstant;"
  #48 = NameAndType        #57:#58        // clone:()Ljava/lang/Object;
  #49 = Utf8               com/ydlclass/enumTest/SeasonConstant
  #50 = NameAndType        #28:#59        // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
  #51 = NameAndType        #33:#34        // "<init>":(Ljava/lang/String;I)V
  #52 = NameAndType        #17:#18        // SPRING:Lcom/ydlclass/enumTest/SeasonConstant;
  #53 = NameAndType        #19:#18        // SUMMER:Lcom/ydlclass/enumTest/SeasonConstant;
  #54 = NameAndType        #20:#18        // AUTUMN:Lcom/ydlclass/enumTest/SeasonConstant;
  #55 = NameAndType        #21:#18        // WINTER:Lcom/ydlclass/enumTest/SeasonConstant;
  #56 = Utf8               java/lang/Enum
  #57 = Utf8               clone
  #58 = Utf8               ()Ljava/lang/Object;
  #59 = Utf8               (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
{
  public static final com.ydlclass.enumTest.SeasonConstant SPRING;
//定义常量SPRING
    descriptor: Lcom/ydlclass/enumTest/SeasonConstant;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
//定义常量SUMMER

  public static final com.ydlclass.enumTest.SeasonConstant SUMMER;
    descriptor: Lcom/ydlclass/enumTest/SeasonConstant;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
//定义常量AUTUMN

  public static final com.ydlclass.enumTest.SeasonConstant AUTUMN;
    descriptor: Lcom/ydlclass/enumTest/SeasonConstant;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
//定义常量WINTER
  public static final com.ydlclass.enumTest.SeasonConstant WINTER;
    descriptor: Lcom/ydlclass/enumTest/SeasonConstant;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static com.ydlclass.enumTest.SeasonConstant[] values();
//可以发现一个不是我们写的方法,说明这个方法是父类中的方法,我们可以去java.lang.enum中来寻找。
    descriptor: ()[Lcom/ydlclass/enumTest/SeasonConstant;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #1                  // Field $VALUES:[Lcom/ydlclass/enumTest/SeasonConstant;
         3: invokevirtual #2                  // Method "[Lcom/ydlclass/enumTest/SeasonConstant;".clone:()Ljava/lang/Object;
         6: checkcast     #3                  // class "[Lcom/ydlclass/enumTest/SeasonConstant;"
         9: areturn
      LineNumberTable:
        line 3: 0

  public static com.ydlclass.enumTest.SeasonConstant valueOf(java.lang.String);
    descriptor: (Ljava/lang/String;)Lcom/ydlclass/enumTest/SeasonConstant;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: ldc           #4                  // class com/ydlclass/enumTest/SeasonConstant
         2: aload_0
         3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
         6: checkcast     #4                  // class com/ydlclass/enumTest/SeasonConstant
         9: areturn
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  name   Ljava/lang/String;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 17: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  args   [Ljava/lang/String;

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=4, locals=0, args_size=0
         0: new           #4                  // class com/ydlclass/enumTest/SeasonConstant
         3: dup
         4: ldc           #7                  // String SPRING压入了这个字符串

         6: iconst_0//又将字符串压入了,其实每个Enum的构造其都需要两个参数,一个是那么,另一个是ordinal,这个有序的数字其实就是enum的索引。

         7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V               //调用方法,这个构造器,是两个参数的构造器,说明是父类的构造器。

        10: putstatic     #9                  // Field SPRING:Lcom/ydlclass/enumTest/SeasonConstant;
        13: new           #4                  // class com/ydlclass/enumTest/SeasonConstant
        16: dup
        17: ldc           #10                 // String SUMMER
        19: iconst_1
        20: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        23: putstatic     #11                 // Field SUMMER:Lcom/ydlclass/enumTest/SeasonConstant;
        26: new           #4                  // class com/ydlclass/enumTest/SeasonConstant
        29: dup
        30: ldc           #12                 // String AUTUMN
        32: iconst_2
        33: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        36: putstatic     #13                 // Field AUTUMN:Lcom/ydlclass/enumTest/SeasonConstant;
        39: new           #4                  // class com/ydlclass/enumTest/SeasonConstant
        42: dup
        43: ldc           #14                 // String WINTER
        45: iconst_3
        46: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        49: putstatic     #15                 // Field WINTER:Lcom/ydlclass/enumTest/SeasonConstant;
        52: iconst_4
        53: anewarray     #4                  // class com/ydlclass/enumTest/SeasonConstant
        56: dup
        57: iconst_0
        58: getstatic     #9                  // Field SPRING:Lcom/ydlclass/enumTest/SeasonConstant;
        61: aastore
        62: dup
        63: iconst_1
        64: getstatic     #11                 // Field SUMMER:Lcom/ydlclass/enumTest/SeasonConstant;
        67: aastore
        68: dup
        69: iconst_2
        70: getstatic     #13                 // Field AUTUMN:Lcom/ydlclass/enumTest/SeasonConstant;
        73: aastore
        74: dup
        75: iconst_3
        76: getstatic     #15                 // Field WINTER:Lcom/ydlclass/enumTest/SeasonConstant;
        79: aastore
        80: putstatic     #1                  // Field $VALUES:[Lcom/ydlclass/enumTest/SeasonConstant;
        83: return
      LineNumberTable:
        line 12: 0
        line 3: 52
}
Signature: #43                          // Ljava/lang/Enum<Lcom/ydlclass/enumTest/SeasonConstant;>;
SourceFile: "SeasonConstant.java"