注意,枚举类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"