反编译详解Java枚举类Enum

从字节码指令分析Java枚举类的运行过程。 目录 一、创建的枚举类 二、反编译字节码 1)getCode()方…

从字节码指令分析Java枚举类的运行过程。

目录

一、创建的枚举类

二、反编译字节码

1)getCode()方法分析

2)setCode()方法分析

三、枚举常量给枚举类成员变量赋值过程分析


一、创建的枚举类

二、反编译字节码

1)getCode()方法分析

字节码各指令含义在这里不再阐述,主要讲一下字节码运行之后会产生什么效果。

了解过JVM内存模型的都知道,JVM中每一个方法都会存在一个方法栈。而字节码执行中,就会存在一个操作数栈,一个局部表量表,对应图片中stack就是操作数栈,局部变量可以在LocalVariableTable中看到;其中,Slot就是字节码中操作的一个下标。

在大概了解了执行中存在哪些资源后,就可以开始分析了。

首先public java.lang.String getCode()就是定义的方法声明,下面的descriptor也是描述方法的;()表示这个方法,并且没有参数,Ljava/lang/String,其中L表示这个一个引用类,后面表示这个引用类是什么类型,意思就是这个方法有返回值,返回值类型为字符串类型。

从Code:下面开始依次分析每一行字节码含义。

这里的stack表示操作栈的深度,为1,locals是局部变量表的变量个数,args_size表示参数个数,这里getCode()没有参数,但是显示为1,是因为每个实例方法都会有一个隐藏参数this。

aload_0表示将Slot0这个变量压入操作栈栈顶,即将this压入栈

getfield很好理解,就是获取this对应的code的这个变量的值,并且会把此时位于栈顶的this弹出栈。

areturn就是返回了。局部变量表和栈都会清零。

2)setCode()方法分析

和getCode()相同的地方就不再阐述,这里说一下descriptor,(Ljava/lang/String;)表示这个方法有一个参数,参数为String类型,V表示该方法没有返回值,为Void

aload_0,将这个对象this压入栈

aload_1,将code这个参数的值压入栈,此时,code在栈顶

putfield,将栈顶的code的值赋值给栈顶下一个this对象对应的属性,此时,this的code成员属性的值就是参数code的值了

return。局部变量表和栈都会清零。

三、枚举常量给枚举类成员变量赋值过程分析

通过上面getCode()和setCode(),可以大致熟悉一下字节码指令做的事了,下面就分析,枚举常量是如何通过构造函数给枚举类成员变量赋值的。

所有的枚举类都继承自Enum这个公共基类,在这个公共基类中,有一个构造方法,如下:

Enum(String name, int ordinal),其中name就是我们平常写在枚举类中的常量,在这个例子中就是SUCCESS和FAIL,ordinal则是枚举常量的顺序,这里SUCCESS就是0,FAIL就是1。

构造函数字节码如下:

构造函数也比较简单,主要就是前面4行,前面4行就是在执行公共基类的构造函数。aload_0和aload_1会在后面讲到。

第三行invokespecial执行的方法,通过注释可以看到那个方法是没有返回值的,参数是String和int,和公共基类的方法一致。

接下来就是枚举常量的执行字节码:

new,运行构造函数,创建一个枚举类实例,并压入栈顶

dup,复制栈顶数据,并压入栈,此时操作数栈有两个值,都是new的实例

ldc,从常量池中取出SUCCESS并压入栈

iconst_0,将基本类型int的0压入栈

ldc,从常量池中取出SUCCESS中的code的值(这里是0000)并压入栈

ldc,从常量池中取出SUCCESS中的success的值(这里是success)并压入栈

invokespecial,执行构造函数,这里通过注释,可以分析出这个方法是 method(String, int, String, String),公共基类Enum的构造函数和自定义的枚举类的构造函数的合并。

putstatic,为枚举类的SUCCESS常量赋值。

一个枚举类大致就会经历这几个过程,从字节码我们可以看到,枚举类的常量都是static的,虽然我们没有显示的指定。

本文来自网络,不代表软粉网立场,转载请注明出处:https://www.rfff.net/p/8448.html

作者: HUI

发表评论

您的电子邮箱地址不会被公开。

返回顶部