1 新建案例代码
基础环境,jdk为jdk11
java version "11.0.14" 2022-01-18 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.14+8-LTS-263)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.14+8-LTS-263, mixed mode)
编写案例类TestClass,一个基本的属性num,一个静态属性STAIC_NUM,另外连个个的方法add,一个add方法有参数,另外一个没有参数。
public class TestClass {
private int num = 1;
private int STATIC_NUM = 2;
public int add(){
return num + 1;
}
public int add(int addNum) {
return num + addNum;
}
}
代码编译后生成class文件,反编译后,多了一个构造器,另外方法里面属性多了this引用,代码如下:
public class TestClass {
private int num = 1;
private int STATIC_NUM = 2;
//编译器生成
public TestClass() {
}
//多了this引用
public int add() {
return this.num + 1;
}
//多了this引用
public int add(int addNum) {
return this.num + addNum;
}
}
2 查看字节码
macos 查看class文件16进制,参考这里
3 字节码结构
class文件是一组以8个字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑排列在文件之中,中间没有添加任何分隔符。class文件格式有两种数据类型:无符号数和表。
- 无符号数 无符号数属于基本数据类型,以u1、u2、u4、u8分别代表1个字节、2个字节、4个字节、8个字节。无符号数用来描述数字、索引引用、数量值、字符串值。
- 表 表是由多个无符号数或者表座位数据项构成的复合数据类型,所有表的命名都习惯性以“_info”结尾。 Java字节码总的结构表如下所示,解析class文件信息的按照结构顺序逐一进行解读。
| 类型 | 名称 | 说明 | 数量 | 备注 |
|---|---|---|---|---|
| u4 | magic | 魔数 | 1 | 识别文件类型,4个字节 |
| u2 | minor_version | 副版本号 | 1 | 2个字节 |
| u2 | major_version | 主版本号 | 1 | 2个字节 |
| u2 | constant_pool_count | 常量池数量 | 1 | 2个字节 |
| cp_info | constant_pool | 常量池信息 | constant_pool_count | n个字节 |
| u2 | access_flags | 访问标志 | 1 | 2个字节 |
| u2 | this_class | 类索引 | 1 | 2个字节 |
| u2 | super_class | 父类索引 | 1 | 2个字节 |
| u2 | interface_count | 接口数量 | 1 | 2个字节 |
| u2 | interfaces | 接口索引集合 | interface_count | n个字节 |
| u2 | fields_count | 字段数量 | 1 | 2个字节 |
| field_info | fields | 字段信息 | fields_count | n个字节 |
| u2 | methods_count | 方法数量 | 1 | 2个字节 |
| method_info | methods | 方法信息集合 | methods_count | n个字节 |
| u2 | attributes_count | 附加属性计数器 | 1 | 2个字节 |
| attribute_info | attributes | 附加属性集合 | attributes_count | n个字节 |
4 解析class文件
4.1 magic解析
| 类型 | 名称 | 说明 | 数量 | 备注 |
|---|---|---|---|---|
| u4 | magic | 魔数 | 1 | 识别文件类型,4个字节 |
解析magic,魔数作用是确定这个文件是否为一个能被虚拟机接收的class文件。魔数解析完为 0xCAFEBABE(咖啡宝贝???)。
4.2 minor_version 解析
| 类型 | 名称 | 说明 | 数量 | 备注 |
|---|---|---|---|---|
| u4 | magic | 魔数 | 1 | 识别文件类型,4个字节 |
| u2 | minor_version | 副版本号 | 1 | 2个字节 |
副版本号解析完0x0000。
4.3 major_version 解析
| 类型 | 名称 | 说明 | 数量 | 备注 |
|---|---|---|---|---|
| u4 | magic | 魔数 | 1 | 识别文件类型,4个字节 |
| u2 | minor_version | 副版本号 | 1 | 2个字节 |
| u2 | major_version | 主版本号 | 1 | 2个字节 |
主版本号解析完0x0037,3*16+7=55。
4.4 constant_pool_count 解析
| 类型 | 名称 | 说明 | 数量 | 备注 |
|---|---|---|---|---|
| u4 | magic | 魔数 | 1 | 识别文件类型,4个字节 |
| u2 | minor_version | 副版本号 | 1 | 2个字节 |
| u2 | major_version | 主版本号 | 1 | 2个字节 |
| u2 | constant_pool_count | 常量池数量 | 1 | 2个字节 |
常量池数量解析完0x001B,16+11=27。常量池计数从1开始,不是从0开始。27代表常量池数量为26。第0项常量空出来,目的后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”的含义。
4.5 cp_info 解析
| 类型 | 名称 | 说明 | 数量 | 备注 |
|---|---|---|---|---|
| u4 | magic | 魔数 | 1 | 识别文件类型,4个字节 |
| u2 | minor_version | 副版本号 | 1 | 2个字节 |
| u2 | major_version | 主版本号 | 1 | 2个字节 |
| u2 | constant_pool_count | 常量池数量 | 1 | 2个字节 |
| cp_info | constant_pool | 常量池信息 | constant_pool_count | n个字节 |
常量池中主要存放两大类常量:字面量和符号引用。字面量比较接近于java常量的概念,比如文本字符串、被声明为final的常量值等;符号引用则是主要是类和接口全限定名、字段的名称和描述符、方法的名称和描述符、方法句柄和方法类型、动态调用点和动态常量。
常量池中每一项都是一个表,不同的JDK版本,常量表中分别有不同的常量类型。每个常量类型有tag标志,标志位都是u1,占用一个字节;然后每个常量类型有对应的结构体。
| 类型 | 标志 | 描述 | 结构 |
|---|---|---|---|
| CONSTANT_utf8_info | 1 | UTF-8编码字符串 | tag: u1 值为1 length: u2 bytes长度 bytes:u1 长度为length的utf-8字符串 |
| CONSTANT_Integer_info | 3 | 整形字面量 | tag: u1 值为3 bytes:u4 存储int值 |
| CONSTANT_Float_info | 4 | 浮点型字面量 | tag: u1 值为4 bytes:u4 存储float值 |
| CONSTANT_Long_info | 5 | 长整型字面量 | tag: u1 值为5 bytes:u8 存储long值 |
| CONSTANT_Double_info | 6 | 双精度浮点型字面量 | tag: u1 值为3 bytes:u8 存储double值 |
| CONSTANT_Class_info | 7 | 类或接口的符号引用 | tag: u1 值为7 index:u2 存储常量池索引号 |
| CONSTANT_String_info | 8 | 字符串类型字面量 | tag: u1 值为8 index:u2 存储常量池索引号 |
| CONSTANT_Fieldref_info | 9 | 字段的符号引用 | tag: u1 值为9 index:u2 CONSTANT_Class_info索引号 index:u2 CONSTANT_NameAndType_info 索引号 |
| CONSTANT_Methodref_info | 10 | 类中方法的符号引用 | tag: u1 值为10 index:u2 CONSTANT_Class_info索引号 index:u2 CONSTANT_NameAndType_info 索引号 |
| CONSTANT_InterfaceMethodref_info | 11 | 接口中方法的符号引用 | tag: u1 值为11 index:u2 CONSTANT_Class_info索引号 index:u2 CONSTANT_NameAndType_info 索引号 |
| CONSTANT_NameAndType_info | 12 | 字段或方法的符号引用 | tag: u1 值为12 index:u2 字段名或者方法名常量池索引号 index:u2 字段或者方法描述符常量池索引号 |
| CONSTANT_MethodHandle_info | 15 |表示方法句柄 |tag: u1 值为15<br/>reference_kind:值必须在1-9之间,决定了方法句柄的类型<br/>reference_index: u2 常量池索引|
| CONSTANT_MothodType_info | 16 | 标志方法类型|tag: u1 值为16
index:u2 常量池索引 |
| CONSTANT_Dynamic_info | 17 |动态计算常量 |tag: u1 值为17
method_index:u2 索引
index:u2 常量池索引|
| CONSTANT_InvokeDynamic_info | 18 |表示一个方法调用点 |tag: u1 值为18
method_index:u2 索引
index:u2 常量池索引|
| CONSTANT_Module_info | 19 |表示一个模块 |tag: u1 值为19
index:u2 常量池索引|
| CONSTANT_Package_info | 20 |表示一个模块中开放或者导出的包 |tag: u1 值为20
index:常量池索引|
描述符的作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。根据描述符规则,基本数据类型(byte、char、double、float、int、long、short、boolean)以及代表无返回值的void类型都用一个大写字符来表示,而对象类型则用字符L加对象的全限定名来表示。对于数组类型,每一维度将使用一个前置的[字符来描述,如一个定义为java.lang.String[][]类型的二维数组,将被记录为:[[Ljava/lang/String;,,一个整型数组int[]被记录为[I。用描述符来描述方法时,按照先参数列表,后返回值的顺序描述,参数列表按照参数的严格顺序放在一组小括号“( )”之内。如方法java.lang.String toString()的描述符为( ) LJava/lang/String;,方法int abc(int[] x, int y)的描述符为([II) I。
数据类型标志符定义如下:
| 标志符 | 含义 |
|---|---|
| B | 基本数据类型byte |
| C | 基本数据类型char |
| D | 基本数据类型double |
| F | 基本数据类型float |
| I | 基本数据类型int |
| J | 基本数据类型long |
| S | 基本数据类型short |
| Z | 基本数据类型boolean |
| V | 基本数据类型void |
| L | 对象类型,如Ljava/lang/Object |
java提供javap查看,命令javap -verbose TestClass.class,显示常量池结构
4.5.1 常量解析-1
解析常量池tag=0x0A ,为10,查询常量池结构tag=10,为CONSTANT_Methodref_info。
CONSTANT_Methodref_info为类中方法的符号引用。tag: u1 值为10;index:u2 CONSTANT_Class_info索引号;index:u2 CONSTANT_NameAndType_info 索引号。tag后面是记录两个索引。一个是CONSTANT_Class_info索引号,u2;一个是CONSTANT_NameAndType_info 索引号,u2。 CONSTANT_Class_info索引号=0x0005(5),CONSTANT_NameAndType_info 索引号=0x0016(22)
解析后为**#1 #5 #22** 对应如下
4.5.2 常量解析-2
解析常量池tag=0x09,为9,查询常量池结构tag=9,为CONSTANT_Fieldref_info。
CONSTANT_Fieldref_info为字段的符号引用。tag: u1 值为9;index:u2 CONSTANT_Class_info索引号;index:u2 CONSTANT_NameAndType_info 索引号。tag后面是记录两个索引。一个是CONSTANT_Class_info索引号,u2;一个是CONSTANT_NameAndType_info 索引号,u2。 CONSTANT_Class_info索引号=0x0004(4),CONSTANT_NameAndType_info 索引号=0x0017(23)
解析后为**#2 #4 #23** 对应如下
4.5.3 常量解析-3
解析常量池tag=0x09,为9,查询常量池结构tag=9,为CONSTANT_Fieldref_info。
CONSTANT_Fieldref_info为字段的符号引用。tag: u1 值为9;index:u2 CONSTANT_Class_info索引号;index:u2 CONSTANT_NameAndType_info 索引号。tag后面是记录两个索引。一个是CONSTANT_Class_info索引号,u2;一个是CONSTANT_NameAndType_info 索引号,u2。
CONSTANT_Class_info索引号=0x0004(4),CONSTANT_NameAndType_info 索引号=0x0018(24)
解析后为**#3 #4 #24** 对应如下
4.5.4 常量解析-4
解析常量池tag=0x07,为7,查询常量池结构tag=7,为CONSTANT_Class_info。
CONSTANT_Class_info为类或接口的符号引用。tag: u1 值为7;index:u2 存储常量池索引号。
解析后为**#4 #25** 对应如下
4.5.5 常量解析-5
解析常量池tag=0x07,为7,查询常量池结构tag=7,为CONSTANT_Class_info。
CONSTANT_Class_info为类或接口的符号引用。tag: u1 值为7;index:u2 存储常量池索引号。
解析后为**#5 #26** 对应如下
4.5.6 常量解析-6
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为0x0003代表字符串的长度,那再往后面取3个字节,0x6E756D代表对应字符串内容“num”。 解析后为**#6 num** 对应如下
4.5.7 常量解析-7
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为0x0001代表字符串的长度,那再往后面取1个字节,0x49代表对应字符串内容“I”。 解析后为**#7 I** 对应如下
4.5.8 常量解析-8
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为0x000A代表字符串的长度,那再往后面取10个字节,0x53544154 49435F4E 554D代表对应字符串内容“STATIC_NUM”。 解析后为**#8 STATIC_NUM** 对应如下
4.5.9 常量解析-9
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为0x0006代表字符串的长度,那再往后面取6个字节,0x3C696E6 9743E代表对应字符串内容“”。 解析后为**#9 ** 对应如下
4.5.10 常量解析-10
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为0x0003代表字符串的长度,那再往后面取3个字节,0x282956代表对应字符串内容“()V”。 解析后为**#10 ()V** 对应如下
4.5.11 常量解析-11
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为0x0004代表字符串的长度,那再往后面取4个字节,0x436F6465代表对应字符串内容“code”。 解析后为**#11 code** 对应如下
4.5.12 常量解析-12
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为0x000F代表字符串的长度,那再往后面取16个字节,0x4C696E65 4E756D62 65725461 626C65代表对应字符串内容“LineNumberTable”。 解析后为**#12 LineNumberTable** 对应如下
4.5.13 常量解析-13
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为0x0012代表字符串的长度,那再往后面取18个字节,0x4C6F6361 6C566172 6961626C 65546162 6C65代表对应字符串内容“LocalVariableTable”。 解析后为**#13 LocalVariableTable** 对应如下
4.5.14 常量解析-14
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为0x0004代表字符串的长度,那再往后面取4个字节,0x74686973代表对应字符串内容“this”。 解析后为**#14 this** 对应如下
4.5.15 常量解析-15
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为**#15 Lcom/youlangta/study/TestClass;** 对应如下
4.5.16 常量解析-16
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为**#16 add** 对应如下
4.5.17 常量解析-17
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为**#17 ()I** 对应如下
4.5.18 常量解析-18
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为**#18 (I)I** 对应如下
4.5.19 常量解析-19
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为**#19 addNum** 对应如下
4.5.20 常量解析-20
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为**#20 SourceFile** 对应如下
4.5.21 常量解析-21
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为**#21 TestClass.java** 对应如下
4.5.22 常量解析-22
解析常量池tag=0x0C,为12,查询常量池结构tag=12,为 CONSTANT_NameAndType_info。
CONSTANT_NameAndType_info为字段或方法的符号引用。tag: u1 值为12;index:u2 字段名或者方法名常量池索引号;index:u2 字段或者方法描述符常量池索引号。
解析后为**#22 #9 #10** 对应如下
4.5.23 常量解析-23
解析常量池tag=0x0C,为12,查询常量池结构tag=12,为 CONSTANT_NameAndType_info。
CONSTANT_NameAndType_info为字段或方法的符号引用。tag: u1 值为12;index:u2 字段名或者方法名常量池索引号;index:u2 字段或者方法描述符常量池索引号。
解析后为**#23 #6 #7** 对应如下
4.5.24 常量解析-24
解析常量池tag=0x0C,为12,查询常量池结构tag=12,为 CONSTANT_NameAndType_info。
CONSTANT_NameAndType_info为字段或方法的符号引用。tag: u1 值为12;index:u2 字段名或者方法名常量池索引号;index:u2 字段或者方法描述符常量池索引号。
解析后为**#24 #8 #7** 对应如下
4.5.25 常量解析-25
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为**#25 com/youlangta/study/TestClass** 对应如下
4.5.26 常量解析-26
解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。
CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。
解析后为**#26 java/lang/Object** 对应如下
4.6 access_flags 访问标志解析
访问标志标识了泪或接口的访问权限信息。各种访问标识如下所示:
| 标志名称 | 标志值 | 含义 |
|---|---|---|
| ACC_PUBLIC | 0x0001 | 是否为Public类型 |
| ACC_FINAL | 0x0010 | 是否被声明为final,只有类可以设置 |
| ACC_SUPER | 0x0020 | 是否允许使用invokespecial字节码指令的新语义,JDK1.0.2之后编译出来的类的这个标志默认为真 |
| ACC_INTERFACE | 0x0200 | 标志这是一个接口 |
| ACC_ABSTRACT | 0x0400 | 是否为abstract类型,对于接口或者抽象类来说,次标志值为真,其他类型为假 |
| ACC_SYNTHETIC | 0x1000 | 标志这个类并非由用户代码产生 |
| ACC_ANNOTATION | 0x2000 | 标志这是一个注解 |
| ACC_ENUM | x4000 | 标志这是一个枚举 |
解析值为0x0021,public类。
4.7 this_class 类索引解析
类索引。0x0004,对应常量池#4。
4.8 super_class 父类索引解析
父类索引。0x0005,对应常量池#5。
4.9 interface_count 接口数量解析
接口数量。0x0000,说明没有实现接口。
4.10 interfaces 接口索引集合解析
案例没有实现接口,索引这里不需要解析。
4.11 fields_count 字段数量解析
解析类定义的字段数量,包括类级别变量以及实例变量。解析结果为2.
4.12 fields 字段信息解析
字段信息解析即为字段表解析。字段表结构如下:
| 类型 | 名称 | 含义 | 数量 |
|---|---|---|---|
| u2 | access_flags | 访问标志 | 1 |
| u2 | name_index | 字段名索引 | 1 |
| u2 | descriptor_index | 描述符索引 | 1 |
| u2 | attributes_count | 属性计数器 | 1 |
| attribute_info | attributes | 属性集合 | attributes_count |
访问标志定义如下:
| 标志名称 | 标志值 | 含义 |
|---|---|---|
| ACC_PUBLIC | 0x0001 | 字段是否为public |
| ACC_PRIVATE | 0x0002 | 字段是否为private |
| ACC_PROTECTED | 0x0004 | 字段是否为protected |
| ACC_STATIC | 0x0008 | 字段是否为static |
| ACC_FINAL | 0x0010 | 字段是否为final |
| ACC_VOLATILE | 0x0040 | 字段是否为volatile |
| ACC_TRANSTENT | 0x0080 | 字段是否为transient |
| ACC_SYNCHETIC | 0x1000 | 字段是否为由编译器自动产生 |
| ACC_ENUM | 0x4000 | 字段是否为enum |
第一个为 access_flags=0x0002,name_index=0x0006,descriptor_index=0x0007, attributes_count=0x0000,即为“private #6 #7”,对应如下
第二个为 access_flags=0x0002,name_index=0x0008,descriptor_index=0x0007, attributes_count=0x0000,即为“private #8 #7”,对应如下
4.13 methods_count 方法数量解析
方法数量解析,数量为3。其中一个是编译生成的方法。
4.14 methods 方法信息集合解析
方法信息集合即为方法表解析。方法表结构和属性表结构一致,如下:
| 类型 | 名称 | 含义 | 数量 |
|---|---|---|---|
| u2 | access_flags | 访问标志 | 1 |
| u2 | name_index | 方法名索引 | 1 |
| u2 | descriptor_index | 描述符索引 | 1 |
| u2 | attributes_count | 属性计数器 | 1 |
| attribute_info | attributes | 属性集合 | attributes_count |
访问标志如下:
| 标志名称 | 标志值 | 含义 |
|---|---|---|
| ACC_PUBLIC | 0x0001 | 方法是否为public |
| ACC_PRIVATE | 0x0002 | 方法是否为private |
| ACC_PROTECTED | 0x0004 | 方法是否为protected |
| ACC_STATIC | 0x0008 | 方法是否为static |
| ACC_FINAL | 0x0010 | 方法是否为final |
| ACC_SYHCHRONRIZED | 0x0020 | 方法是否为synchronized| |
| ACC_BRIDGE | 0x0040 | 方法是否是有编译器产生的方法 |
| ACC_VARARGS | 0x0080 | 方法是否接受参数 |
| ACC_NATIVE | 0x0100 | 方法是否为native |
| ACC_ABSTRACT | 0x0400 | 方法是否为abstract |
| ACC_STRICTFP | 0x0800 | 方法是否为strictfp |
| ACC_SYNTHETIC | 0x1000 | 方法是否是有编译器自动产生的| |
属性类型如下:
| 属性名称 | 使用位置 | 含义 |
|---|---|---|
| Code | 方法表 | Java代码编译成的字节码指令 |
| ConstantValue | 字段表 | final关键字定义的常量池 |
| Deprecated | 类,方法,字段表 | 被声明为deprecated的方法和字段 |
| Exceptions | 方法表 | 方法抛出的异常 |
| EnclosingMethod | 类文件 | 仅当一个类为局部类或者匿名类是才能拥有这个属性,这个属性用于标识这个类所在的外围方法 |
| :-------------: | :----: | :----------------------------------------: |
| InnerClass | 类文件 | 内部类列表 |
| LineNumberTable | Code属性 | Java源码的行号与字节码指令的对应关系 |
| LocalVariableTable | Code属性 | 方法的局部变量描述 |
| StackMapTable | Code属性 | JDK1.6中新增的属性,供新的类型检查检验器检查和处理目标方法的局部变量和操作数有所需要的类是否匹配 |
| Signature | 类,方法表,字段表 | 用于支持泛型情况下的方法签名| |
| SourceFile | 类文件 | 记录源文件名称 |
| SourceDebugExtension | 类文件 | 用于存储额外的调试信息 |
| Synthetic | 类,方法表,字段表 | 标志方法或字段为编译器自动生成的| |
| LocalVariableTypeTable | 类 | 使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加 |
| RuntimeVisibleAnnotations | 类,方法表,字段表 | 为动态注解提供支持 |
| RuntimeInvisibleAnnotations | 表,方法表,字段表 | 用于指明哪些注解是运行时不可见的 |
| RuntimeVisibleParameterAnnotation | 方法表 | 作用与RuntimeVisibleAnnotations属性类似,只不过作用对象为方法 |
| RuntimeInvisibleParameterAnnotation | 方法表 | 作用与RuntimeInvisibleAnnotations属性类似,作用对象哪个为方法参数 |
| AnnotationDefault | 方法表 | 用于记录注解类元素的默认值 |
| BootstrapMethods | 类文件 | 用于保存invokeddynamic指令引用的引导方式限定符 |
第一个方法解析,“public #9 #10 #11” ,对应"public ()V Code",结果为方法访问标志为public,方法名称为**,方法描述符为()V**,属性数量为1,对应为Code
这里涉及到属性解析,虚拟机规范定义的属性表如下所示:
| 属性名称 | 使用位置 | 含义 |
|---|---|---|
| Code | 方法表 | Java代码编译成的字节码指令 |
| ConstantValue | 字段表 | final关键字定义的常量池 |
| Deprecated | 类,方法,字段表 | 被声明为deprecated的方法和字段 |
| Exceptions | 方法表 | 方法抛出的异常 |
| EnclosingMethod | 类文件 | 仅当一个类为局部类或者匿名类是才能拥有这个属性,这个属性用于标识这个类所在的外围方法 |
| InnerClass | 类文件 | 内部类列表 |
| LineNumberTable | Code属性 | Java源码的行号与字节码指令的对应关系 |
| LocalVariableTable | Code属性 | 方法的局部变量描述 |
| StackMapTable | Code属性 | JDK1.6中新增的属性,供新的类型检查检验器检查和处理目标方法的局部变量和操作数有所需要的类是否匹配 |
| Signature | 类,方法表,字段表 | 用于支持泛型情况下的方法签名 |
| SourceFile | 类文件 | 记录源文件名称 |
| SourceDebugExtension | 类文件 | 用于存储额外的调试信息 |
| Synthetic | 类,方法表,字段表 | 标志方法或字段为编译器自动生成的 |
| LocalVariableTypeTable | 类 | 使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加 |
| RuntimeVisibleAnnotations | 类,方法表,字段表 | 为动态注解提供支持 |
| RuntimeInvisibleAnnotations | 表,方法表,字段表 | 用于指明哪些注解是运行时不可见的 |
| RuntimeVisibleParameterAnnotation | 方法表 | 作用与RuntimeVisibleAnnotations属性类似,只不过作用对象为方法 |
| RuntimeInvisibleParameterAnnotation | 方法表 | 作用与RuntimeInvisibleAnnotations属性类似,作用对象哪个为方法参数 |
| AnnotationDefault | 方法表 | 用于记录注解类元素的默认值 |
| BootstrapMethods | 类文件 | 用于保存invokeddynamic指令引用的引导方式限定符 |
对于每一个属性,它的名称都要从常量池中引用一个常量来表示,而属性值的结构则是完全自定义,只需要通过一个u4的长度属性去说明属性值内容所占用的位数。
| 类型 | 名称 | 数量 |
|---|---|---|
| u2 | attribute_name_index | 1 |
| u4 | attribute_length | 1 |
| u1 | info | attribute_length |
- Code 属性 Java程序方法里面的代码经过Javac编译器处理之后,最终变为字节码指令存储在Code属性内。Code属性出现在方法表的属性集合中,但是并非所有的方法都必须存在这个属性,譬如接口或者抽象类中的方法就不存在Code属性。Code属性结构如下: 类型 | 名称 | 数量 | 含义 | | :-: | :------------------: | :-: | :--------: | | u2 | attribute_name_index | 1 | 属性名索引 | | u4 | attribute_length | 1 | 属性长度 | | u2 | max_stack | 1 | 操作数栈深度的最大值| |u2 | max_locals | 1 | 局部变量表所需的存续空间 | | u4 | code_length | 1 | 字节码指令的长度 | | u1 | code | code_length | 存储字节码指令 | | u2 | exception_table_length | 1 | 异常表长度| |exception_info | exception_table | exception_length | 异常表 | | u2 | attributes_count | 1 | 属性集合计数器 | | attribute_info | attributes | attributes_count | 属性集合|
000B0000 00410002 00010000 000F2AB7 00012A04 B500022A 05B50003 B1000000 02000C00 00000E00 03000000 03000400 04000900 05000D00 00000C00 01000000 0F000E00 0F0000为方法的属性字节码信息。000B属性名称索引, 00000041代表属性长度 为65个字节,0002操作数栈深度的最大值为2,0001局部变量表存储需要的连续空间为1,0000000F字节码指令长度为15,2AB7 00012A04 B500022A 05B50003 B1为字节码指令,0000为异常表长度,0002属性集合计数器为2,000C为属性索引号,对应“#12 = Utf8 LineNumberTable”,00 00000E表示LineNumberTable字节码长度为14,00 03000000 03000400 04000900 05为LineNumberTable的字节码内容,000D为属性索引号,对应“#13 = Utf8 LocalVariableTable”,00 00000C为表示LineNumberTable字节码长度为12,00 01000000 0F000E00 0F0000为LocalVariableTable字节码内容
对应一下内容:
LineNumberTable属性用于描述Java源码行号与字节码行号之间的对应关系。LineNumberTable结构如下所示:
| 类型 | 名称 | 数量 | 含义 |
|---|---|---|---|
| u2 | attribute_name_index | 1 | 属性名索引 |
| u4 | attribute_length | 1 | 属性长度 |
| u2 | line_number_table_length | 1 | 行号表长度| |
| line_number_info | line_number_table | line_number_table_length | 行号表 |
00 03000000 03000400 04000900 05,00 03line_number_table_length的值为0x0003,即其行号表长度长度为3,即有三个行号表;line_number_info表包含start_pc和line_number两个u2数据项,前者是字节码行号,后者是Java源码行号,分别为000000 03即0对应3,000400 04即4对应4,000900 05即9对应5。
LocalVariableTable 属性用于描述栈桢中局部变量表的变量与Java源码定义的变量之间的关系。这个不是运行时必须的属性,可以用-g:none或者-g:vars选项来取消或者要求生成这项信息。LocalVariableTable结构如下所示:
| 类型 | 名称 | 数量 | 含义 |
|---|---|---|---|
| u2 | attribute_name_index | 1 | 属性名索引 |
| u4 | attribute_length | 1 | 属性长度 |
| u2 | local_variable_table_length | 1 | 局部变量关联信息长度| |
| u1 | local_variable_table | 1 | 局部变量关联信息| |
| 局部变量关联信息结构如下: | |||
| 类型 | 名称 | 数量 | 含义 |
| :-: | :----------------------: | :-: | :---: |
| u2 | start_pc | 1 | 字节码偏移量 |
| u2 | length | 1 | 长度 |
| u2 | name_index | 1 | 名称索引号 |
| u2 | descriptor_index | 1 | 描述符 |
| u2 | index | 1 | 局部变量表变量槽位置 |
start_pc和length代表了这个局部变量的生命周期开始的字节码偏移量及其作用范围覆盖的长度,两者结合起来就是这个局部变量在字节码之中的作用域范围。name_index和descriptor_index指向常量池索引。index是这个局部变量表在变量槽位置。
00 01000000 0F000E00 0F0000,00 01代表局部变量关联长度为1,0000start_pc为0,00 0Flength为15,000Ename_index为14,即“#14 = Utf8 this”,00 0F描述符为15,即“#15 = Utf8 Lcom/youlangta/study/TestClass;”,0000index为0。
剩下的按照此方法一一解析,这里就不枚举了。
4.15 attributes_count 附加属性计数器解析
4.16 attributes 附加属性集合解析
5 字节码指令简介
| 字节码 | 助记符 | 指令含义 |
|---|---|---|
| 0x00 | nop | 什么都不做 |
| 0x01 | aconst_null | 将null推送至栈顶 |
| 0x02 | iconst_m1 | 将int型-1推送至栈顶 |
| 0x03 | iconst_0 | 将int型0推送至栈顶 |
| 0x04 | iconst_1 | 将int型1推送至栈顶 |
| 0x05 | iconst_2 | 将int型2推送至栈顶 |
| 0x06 | iconst_3 | 将int型3推送至栈顶 |
| 0x07 | iconst_4 | 将int型4推送至栈顶 |
| 0x08 | iconst_5 | 将int型5推送至栈顶 |
| 0x09 | lconst_0 | 将long型0推送至栈顶 |
| 0x0a | lconst_1 | 将long型1推送至栈顶 |
| 0x0b | fconst_0 | 将float型0推送至栈顶 |
| 0x0c | fconst_1 | 将float型1推送至栈顶 |
| 0x0d | fconst_2 | 将float型2推送至栈顶 |
| 0x0e | dconst_0 | 将do le型0推送至栈顶 |
| 0x0f | dconst_1 | 将do le型1推送至栈顶 |
| 0x10 | bipush | 将单字节的常量值(-128~127)推送至栈顶 |
| 0x11 | sipush | 将一个短整型常量值(-32768~32767)推送至栈顶 |
| 0x12 | ldc | 将int, float或String型常量值从常量池中推送至栈顶 |
| 0x13 | ldc_w | 将int, float或String型常量值从常量池中推送至栈顶(宽索引) |
| 0x14 | ldc2_w | 将long或do le型常量值从常量池中推送至栈顶(宽索引) |
| 0x15 | iload | 将指定的int型本地变量 |
| 0x16 | lload | 将指定的long型本地变量 |
| 0x17 | fload | 将指定的float型本地变量 |
| 0x18 | dload | 将指定的do le型本地变量 |
| 0x19 | aload | 将指定的引用类型本地变量 |
| 0x1a | iload_0 | 将第一个int型本地变量 |
| 0x1b | iload_1 | 将第二个int型本地变量 |
| 0x1c | iload_2 | 将第三个int型本地变量 |
| 0x1d | iload_3 | 将第四个int型本地变量 |
| 0x1e | lload_0 | 将第一个long型本地变量 |
| 0x1f | lload_1 | 将第二个long型本地变量 |
| 0x20 | lload_2 | 将第三个long型本地变量 |
| 0x21 | lload_3 | 将第四个long型本地变量 |
| 0x22 | fload_0 | 将第一个float型本地变量 |
| 0x23 | fload_1 | 将第二个float型本地变量 |
| 0x24 | fload_2 | 将第三个float型本地变量 |
| 0x25 | fload_3 | 将第四个float型本地变量 |
| 0x26 | dload_0 | 将第一个do le型本地变量 |
| 0x27 | dload_1 | 将第二个do le型本地变量 |
| 0x28 | dload_2 | 将第三个do le型本地变量 |
| 0x29 | dload_3 | 将第四个do le型本地变量 |
| 0x2a | aload_0 | 将第一个引用类型本地变量 |
| 0x2b | aload_1 | 将第二个引用类型本地变量 |
| 0x2c | aload_2 | 将第三个引用类型本地变量 |
| 0x2d | aload_3 | 将第四个引用类型本地变量 |
| 0x2e | iaload | 将int型数组指定索引的值推送至栈顶 |
| 0x2f | laload | 将long型数组指定索引的值推送至栈顶 |
| 0x30 | faload | 将float型数组指定索引的值推送至栈顶 |
| 0x31 | daload | 将do le型数组指定索引的值推送至栈顶 |
| 0x32 | aaload | 将引用型数组指定索引的值推送至栈顶 |
| 0x33 | baload | 将boolean或byte型数组指定索引的值推送至栈顶 |
| 0x34 | caload | 将char型数组指定索引的值推送至栈顶 |
| 0x35 | saload | 将short型数组指定索引的值推送至栈顶 |
| 0x36 | istore | 将栈顶int型数值存入指定本地变量 |
| 0x37 | lstore | 将栈顶long型数值存入指定本地变量 |
| 0x38 | fstore | 将栈顶float型数值存入指定本地变量 |
| 0x39 | dstore | 将栈顶do le型数值存入指定本地变量 |
| 0x3a | astore | 将栈顶引用型数值存入指定本地变量 |
| 0x3b | istore_0 | 将栈顶int型数值存入第一个本地变量 |
| 0x3c | istore_1 | 将栈顶int型数值存入第二个本地变量 |
| 0x3d | istore_2 | 将栈顶int型数值存入第三个本地变量 |
| 0x3e | istore_3 | 将栈顶int型数值存入第四个本地变量 |
| 0x3f | lstore_0 | 将栈顶long型数值存入第一个本地变量 |
| 0x40 | lstore_1 | 将栈顶long型数值存入第二个本地变量 |
| 0x41 | lstore_2 | 将栈顶long型数值存入第三个本地变量 |
| 0x42 | lstore_3 | 将栈顶long型数值存入第四个本地变量 |
| 0x43 | fstore_0 | 将栈顶float型数值存入第一个本地变量 |
| 0x44 | fstore_1 | 将栈顶float型数值存入第二个本地变量 |
| 0x45 | fstore_2 | 将栈顶float型数值存入第三个本地变量 |
| 0x46 | fstore_3 | 将栈顶float型数值存入第四个本地变量 |
| 0x47 | dstore_0 | 将栈顶do le型数值存入第一个本地变量 |
| 0x48 | dstore_1 | 将栈顶do le型数值存入第二个本地变量 |
| 0x49 | dstore_2 | 将栈顶do le型数值存入第三个本地变量 |
| 0x4a | dstore_3 | 将栈顶do le型数值存入第四个本地变量 |
| 0x4b | astore_0 | 将栈顶引用型数值存入第一个本地变量 |
| 0x4c | astore_1 | 将栈顶引用型数值存入第二个本地变量 |
| 0x4d | astore_2 | 将栈顶引用型数值存入第三个本地变量 |
| 0x4e | astore_3 | 将栈顶引用型数值存入第四个本地变量 |
| 0x4f | iastore | 将栈顶int型数值存入指定数组的指定索引位置 |
| 0x50 | lastore | 将栈顶long型数值存入指定数组的指定索引位置 |
| 0x51 | fastore | 将栈顶float型数值存入指定数组的指定索引位置 |
| 0x52 | dastore | 将栈顶do le型数值存入指定数组的指定索引位置 |
| 0x53 | aastore | 将栈顶引用型数值存入指定数组的指定索引位置 |
| 0x54 | bastore | 将栈顶boolean或byte型数值存入指定数组的指定索引位置 |
| 0x55 | castore | 将栈顶char型数值存入指定数组的指定索引位置 |
| 0x56 | sastore | 将栈顶short型数值存入指定数组的指定索引位置 |
| 0x57 | pop | 将栈顶数值弹出 (数值不能是long或do le类型的) |
| 0x58 | pop2 | 将栈顶的一个(long或do le类型的)或两个数值弹出(其它) |
| 0x59 | dup | 复制栈顶数值并将复制值压入栈顶 |
| 0x5a | dup_x1 | 复制栈顶数值并将两个复制值压入栈顶 |
| 0x5b | dup_x2 | 复制栈顶数值并将三个(或两个)复制值压入栈顶 |
| 0x5c | dup2 | 复制栈顶一个(long或do le类型的)或两个(其它)数值并将复制值压入栈顶 |
| 0x5d | dup2_x1 | dup_x1 指令的双倍版本 |
| 0x5e | dup2_x2 | dup_x2 指令的双倍版本 |
| 0x5f | swap | 将栈最顶端的两个数值互换(数值不能是long或do le类型的) |
| 0x60 | iadd | 将栈顶两int型数值相加并将结果压入栈顶 |
| 0x61 | ladd | 将栈顶两long型数值相加并将结果压入栈顶 |
| 0x62 | fadd | 将栈顶两float型数值相加并将结果压入栈顶 |
| 0x63 | dadd | 将栈顶两do le型数值相加并将结果压入栈顶 |
| 0x64 | is | 将栈顶两int型数值相减并将结果压入栈顶 |
| 0x65 | ls | 将栈顶两long型数值相减并将结果压入栈顶 |
| 0x66 | fs | 将栈顶两float型数值相减并将结果压入栈顶 |
| 0x67 | ds | 将栈顶两do le型数值相减并将结果压入栈顶 |
| 0x68 | imul | 将栈顶两int型数值相乘并将结果压入栈顶 |
| 0x69 | lmul | 将栈顶两long型数值相乘并将结果压入栈顶 |
| 0x6a | fmul | 将栈顶两float型数值相乘并将结果压入栈顶 |
| 0x6b | dmul | 将栈顶两do le型数值相乘并将结果压入栈顶 |
| 0x6c | idiv | 将栈顶两int型数值相除并将结果压入栈顶 |
| 0x6d | ldiv | 将栈顶两long型数值相除并将结果压入栈顶 |
| 0x6e | fdiv | 将栈顶两float型数值相除并将结果压入栈顶 |
| 0x6f | ddiv | 将栈顶两do le型数值相除并将结果压入栈顶 |
| 0x70 | irem | 将栈顶两int型数值作取模运算并将结果压入栈顶 |
| 0x71 | lrem | 将栈顶两long型数值作取模运算并将结果压入栈顶 |
| 0x72 | frem | 将栈顶两float型数值作取模运算并将结果压入栈顶 |
| 0x73 | drem | 将栈顶两do le型数值作取模运算并将结果压入栈顶 |
| 0x74 | ineg | 将栈顶int型数值取负并将结果压入栈顶 |
| 0x75 | lneg | 将栈顶long型数值取负并将结果压入栈顶 |
| 0x76 | fneg | 将栈顶float型数值取负并将结果压入栈顶 |
| 0x77 | dneg | 将栈顶do le型数值取负并将结果压入栈顶 |
| 0x78 | ishl | 将int型数值左移位指定位数并将结果压入栈顶 |
| 0x79 | lshl | 将long型数值左移位指定位数并将结果压入栈顶 |
| 0x7a | ishr | 将int型数值右(符号)移位指定位数并将结果压入栈顶 |
| 0x7b | lshr | 将long型数值右(符号)移位指定位数并将结果压入栈顶 |
| 0x7c | iushr | 将int型数值右(无符号)移位指定位数并将结果压入栈顶 |
| 0x7d | lushr | 将long型数值右(无符号)移位指定位数并将结果压入栈顶 |
| 0x7e | iand | 将栈顶两int型数值作“按位与”并将结果压入栈顶 |
| 0x7f | land | 将栈顶两long型数值作“按位与”并将结果压入栈顶 |
| 0x80 | ior | 将栈顶两int型数值作“按位或”并将结果压入栈顶 |
| 0x81 | lor | 将栈顶两long型数值作“按位或”并将结果压入栈顶 |
| 0x82 | ixor | 将栈顶两int型数值作“按位异或”并将结果压入栈顶 |
| 0x83 | lxor | 将栈顶两long型数值作“按位异或”并将结果压入栈顶 |
| 0x84 | iinc | 将指定int型变量增加指定值(i++, i–, i+=2) |
| 0x85 | i2l | 将栈顶int型数值强制转换成long型数值并将结果压入栈顶 |
| 0x86 | i2f | 将栈顶int型数值强制转换成float型数值并将结果压入栈顶 |
| 0x87 | i2d | 将栈顶int型数值强制转换成do le型数值并将结果压入栈顶 |
| 0x88 | l2i | 将栈顶long型数值强制转换成int型数值并将结果压入栈顶 |
| 0x89 | l2f | 将栈顶long型数值强制转换成float型数值并将结果压入栈顶 |
| 0x8a | l2d | 将栈顶long型数值强制转换成do le型数值并将结果压入栈顶 |
| 0x8b | f2i | 将栈顶float型数值强制转换成int型数值并将结果压入栈顶 |
| 0x8c | f2l | 将栈顶float型数值强制转换成long型数值并将结果压入栈顶 |
| 0x8d | f2d | 将栈顶float型数值强制转换成do le型数值并将结果压入栈顶 |
| 0x8e | d2i | 将栈顶do le型数值强制转换成int型数值并将结果压入栈顶 |
| 0x8f | d2l | 将栈顶do le型数值强制转换成long型数值并将结果压入栈顶 |
| 0x90 | d2f | 将栈顶do le型数值强制转换成float型数值并将结果压入栈顶 |
| 0x91 | i2b | 将栈顶int型数值强制转换成byte型数值并将结果压入栈顶 |
| 0x92 | i2c | 将栈顶int型数值强制转换成char型数值并将结果压入栈顶 |
| 0x93 | i2s | 将栈顶int型数值强制转换成short型数值并将结果压入栈顶 |
| 0x94 | lcmp | 比较栈顶两long型数值大小,并将结果(1,0,-1)压入栈顶 |
| 0x95 | fcmpl | 比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶 |
| 0x96 | fcmpg | 比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶 |
| 0x97 | dcmpl | 比较栈顶两do le型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶 |
| 0x98 | dcmpg | 比较栈顶两do le型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶 |
| 0x99 | ifeq | 当栈顶int型数值等于0时跳转 |
| 0x9a | ifne | 当栈顶int型数值不等于0时跳转 |
| 0x9b | iflt | 当栈顶int型数值小于0时跳转 |
| 0x9c | ifge | 当栈顶int型数值大于等于0时跳转 |
| 0x9d | ifgt | 当栈顶int型数值大于0时跳转 |
| 0x9e | ifle | 当栈顶int型数值小于等于0时跳转 |
| 0x9f | if_icmpeq | 比较栈顶两int型数值大小,当结果等于0时跳转 |
| 0xa0 | if_icmpne | 比较栈顶两int型数值大小,当结果不等于0时跳转 |
| 0xa1 | if_icmplt | 比较栈顶两int型数值大小,当结果小于0时跳转 |
| 0xa2 | if_icmpge | 比较栈顶两int型数值大小,当结果大于等于0时跳转 |
| 0xa3 | if_icmpgt | 比较栈顶两int型数值大小,当结果大于0时跳转 |
| 0xa4 | if_icmple | 比较栈顶两int型数值大小,当结果小于等于0时跳转 |
| 0xa5 | if_acmpeq | 比较栈顶两引用型数值,当结果相等时跳转 |
| 0xa6 | if_acmpne | 比较栈顶两引用型数值,当结果不相等时跳转 |
| 0xa7 | goto | 无条件跳转 |
| 0xa8 | jsr | 跳转至指定16位offset位置,并将jsr下一条指令地址压入栈顶 |
| 0xa9 | ret | 返回至本地变量 |
| 0xaa | tableswitch | 用于switch条件跳转,case值连续(可变长度指令) |
| 0xab | lookupswitch | 用于switch条件跳转,case值不连续(可变长度指令) |
| 0xac | ireturn | 从当前方法返回int |
| 0xad | lreturn | 从当前方法返回long |
| 0xae | freturn | 从当前方法返回float |
| 0xaf | dreturn | 从当前方法返回do le |
| 0xb0 | areturn | 从当前方法返回对象引用 |
| 0xb1 | return | 从当前方法返回void |
| 0xb2 | getstatic | 获取指定类的静态域,并将其值压入栈顶 |
| 0xb3 | putstatic | 为指定的类的静态域赋值 |
| 0xb4 | getfield | 获取指定类的实例域,并将其值压入栈顶 |
| 0xb5 | putfield | 为指定的类的实例域赋值 |
| 0xb6 | invokevirtual | 调用实例方法 |
| 0xb7 | invokespecial | 调用超类构造方法,实例初始化方法,私有方法 |
| 0xb8 | invokestatic | 调用静态方法 |
| 0xb9 | invokeinterface | 调用接口方法 |
| 0xba | – | 无此指令 |
| 0xbb | new | 创建一个对象,并将其引用值压入栈顶 |
| 0xbc | newarray | 创建一个指定原始类型(如int, float, char…)的数组,并将其引用值压入栈顶 |
| 0xbd | anewarray | 创建一个引用型(如类,接口,数组)的数组,并将其引用值压入栈顶 |
| 0xbe | arraylength | 获得数组的长度值并压入栈顶 |
| 0xbf | athrow | 将栈顶的异常抛出 |
| 0xc0 | checkcast | 检验类型转换,检验未通过将抛出ClassCastException |
| 0xc1 | instanceof | 检验对象是否是指定的类的实例,如果是将1压入栈顶,否则将0压入栈顶 |
| 0xc2 | monitorenter | 获得对象的锁,用于同步方法或同步块 |
| 0xc3 | monitorexit | 释放对象的锁,用于同步方法或同步块 |
| 0xc4 | wide | <待补充> |
| 0xc5 | multianewarray | 创建指定类型和指定维度的多维数组(执行该指令时,操作栈中必须包含各维度的长度值),并将其引用值压入栈顶 |
| 0xc6 | ifnull | 为null时跳转 |
| 0xc7 | ifnonnull | 不为null时跳转 |
| 0xc8 | goto_w | 无条件跳转(宽索引) |
| 0xc9 | jsr_w | 跳转至指定32位offset位置,并将jsr_w下一条指令地址压入栈顶 |