java枚举类与成员变量的关系_深入理解枚举类

深入理解枚举

最近刚学习完JVM相关知识,想到枚举既然这么异类,那就从字节码角度来分析一下它。有关枚举的讲解,很多博客已经很详细了,这里我们就从字节码的角度重新来认识一下它。

枚举类是一种特殊的类,它可以有自己的成员变量,方法,可以实现一个或多个接口,也可也定义自己的构造器。

1. 枚举类的继承结构:

java枚举类与成员变量的关系_深入理解枚举类_第1张图片

2. 枚举类和普通类的区别:

(1)枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是继承Object类,因此枚举不能够显示继承其他父类(单继承局限性,但是可以实现接口)。其中“java.lang.Enum”实现了“Comparable”和“Serializable”接口。

(2)使用enum定义,非抽象的枚举类默认会使用final修饰,因此枚举类不能够派生子类。

(3)枚举类的构造器只能够使用private访问控制符,如果省略了构造器的访问控制符,则默认使用private修饰;如果强制指定访问控制符,则只能指定private修饰符。

(4)枚举类的所有实例必须要在枚举类的第一行显示列出,否则这个枚举类永远都能产生实例。列出这些实时,系统会自动添加public static final修饰符,无需程序员显示添加。

(5)枚举类默认提供了一个values方法,返回枚举实例数组,该方法可以很方便的遍历所有的枚举值。

为了能够更好的说明,上面的这些不同之处,下面我们定义了一个枚举类,使用“javap -v -p ”来反编译它

enum Season {

SPRING("春"),SUMMER("夏"),AUTUMN("秋"),WINTTER("冬");

private String name;

Season(String name) {

this.name = name;

}

}

反编译可以得到这些信息:

$ javap -v -p Season.class

Classfile /D:/Project/JvmDemo/target/classes/com/bigdata/java/Season.class

Last modified 2020-3-21; size 1206 bytes

MD5 checksum e78087beee7e634071bc6cd1e019c168

Compiled from "Demo69.java"

final class com.bigdata.java.Season extends java.lang.Enum

minor version: 0

major version: 52

flags: ACC_FINAL, ACC_SUPER, ACC_ENUM

Constant pool:

...

{

//枚举类中定义的常量,在字节码层面上的反映

public static final com.bigdata.java.Season SPRING;

descriptor: Lcom/bigdata/java/Season;

flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

public static final com.bigdata.java.Season SUMMER;

descriptor: Lcom/bigdata/java/Season;

flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

public static final com.bigdata.java.Season AUTUMN;

descriptor: Lcom/bigdata/java/Season;

flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

public static final com.bigdata.java.Season WINTTER;

descriptor: Lcom/bigdata/java/Season;

flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

private java.lang.String name;

descriptor: Ljava/lang/String;

flags: ACC_PRIVATE

//枚举数组,这个数组供values方法使用,用于返回枚对象数组

private static final com.bigdata.java.Season[] $VALUES;

descriptor: [Lcom/bigdata/java/Season;

flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC

//自动生成了values方法,它所完成的功能就是$VALUES.clone

public static com.bigdata.java.Season[] values();

descriptor: ()[Lcom/bigdata/java/Season;

flags: ACC_PUBLIC, ACC_STATIC

Code:

stack=1, locals=0, args_size=0

//从类中获取静态字段$VALUES,然后压入到操作数栈的栈顶

0: getstatic #1 // Field $VALUES:[Lcom/bigdata/java/Season;

//调用实例方法clone,即出栈$VALUES,然后执行$VALUES.clone,由于clone是native方法并且有返回值,所以返回值会被压入到操作数的栈顶,此时操作数的栈顶是枚举对象数组

3: invokevirtual #2 // Method "[Lcom/bigdata/java/Season;".clone:()Ljava/lang/Object;

//弹出栈顶的枚举对象数组,检查它的类型是否符合给定的类型,检查完毕后再次压入到操作数栈的栈顶

6: checkcast #3 // class "[Lcom/bigdata/java/Season;"

//将操作数栈顶的值弹出,并返回到给调用处,这样返回的就是一个clone后的枚举对象数组

9: areturn

LineNumberTable:

line 9: 0

//它所完成的功能就是根据传入的字符串,调用父类的valueOf方法返回对应的枚举常量

public static com.bigdata.java.Season valueOf(java.lang.String);

descriptor: (Ljava/lang/String;)Lcom/bigdata/java/Season;

flags: ACC_PUBLIC, ACC_STATIC

Code:

stack=2, locals=1, args_size=1

0: ldc #4 // class com/bigdata/java/Season

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/bigdata/java/Season

9: areturn

LineNumberTable:

line 9: 0

LocalVariableTable:

Start Length Slot Name Signature

0 10 0 name Ljava/lang/String;

//枚举构造方法,接收String和int,String,第一个String是枚举常量字符串,第二个是枚举常量所定义的位置,第三个枚举中构造方法传入的值

private com.bigdata.java.Season(java.lang.String);

descriptor: (Ljava/lang/String;ILjava/lang/String;)V

flags: ACC_PRIVATE

Code:

stack=3, locals=4, args_size=4

//将本地变量表索引0上的引用类型元素压入到操作数的栈顶

0: aload_0

//将本地变量表索引1上的引用类型元素压入到操作数的栈顶。尽管此时本地变量表对应索引上,元素为空,但运行时局部变量表被被填充为枚举常量字符串

1: aload_1

//本地变量表索引2上的int型元素压入到操作数的栈顶,运行时本地变量表对应索引上会被填充为枚举常量定义的位置

2: iload_2

//从操作数栈中依次弹出说压入的值,然后调用父类的构造方法,Enum."":(Ljava/lang/String;I)V

3: invokespecial #6 // Method java/lang/Enum."":(Ljava/lang/String;I)V

//将本地变量表索引0上的引用类型元素压入到操作数的栈顶

6: aload_0

//将本地变量表索引3上的引用类型元素压入到操作数的栈顶

7: aload_3

//为对象中的字段设置值,从操作数栈中依次弹出枚举常量和构造方法所传入的值,为name字段赋值,如:SEASON.SPRING.name=“春”

8: putfield #7

你可能感兴趣的:(java枚举类与成员变量的关系)