空手套白狼,硬阅java字节码class文件

时间:2023-12-14 08:34:08

如下,是一些java字节码也就是原始的class文件,当应用部署到线上之后,我们能够看到的也就是这样的字样了。那么怎样解呢?就让我们一起,来解读解读字节码吧!

Offset                           A  B  C  D  E  F

   CA FE BA BE       6A 0A  1C   0A   漱壕    j
3A 3B 3C 3D : ; < =
3E 0A 3F 0A > ? @ A
0A 0A 0A B C D
0A 0A 0A 0A E F G H I
0A 0A 4A 0B 4B 4C 4D 0A J K L M
0A 4E 4F 0A N O
0A 0A 0A P Q R
0B S T userServi
4C 6F 6D 2F 6F 2F ce $Lcom/youge/
000000A0 2F 2F service/user/Use
000000B0 3B 6E rService; init
000000C0 6C 5A 3C 6E ialed Z <ini
000000D0 3E 6F t> ()V Code

原始类源码如下:

package com.youge.api;

import com.youge.pojo.user.UserInfo;
import com.youge.service.user.UserService;
import com.youge.service.user.UserServiceImpl; public class ByteCodeClassKen { private UserService userService; private boolean initialed; public ByteCodeClassKen() {
init();
} private void init() {
if(!initialed) {
System.out.println("init...");
userService = new UserServiceImpl();
initialed = true;
}
} public Integer addUser() throws Exception {
UserInfo userInfo = new UserInfo();
userInfo.setName("lilei");
userInfo.setAge(12);
userInfo.setAddress("new road.");
return userService.addUser(userInfo);
} public static void main(String[] args) throws Exception {
// test
ByteCodeClassKen classKen = new ByteCodeClassKen();
Integer affect = classKen.addUser();
System.out.println("affect: " + affect);
}
}

完整字节码文件,如有兴趣请展开:

Offset                           A  B  C  D  E  F

   CA FE BA BE       6A 0A  1C   0A   漱壕    j
3A 3B 3C 3D : ; < =
3E 0A 3F 0A > ? @ A
0A 0A 0A B C D
0A 0A 0A 0A E F G H I
0A 0A 4A 0B 4B 4C 4D 0A J K L M
0A 4E 4F 0A N O
0A 0A 0A P Q R
0B S T userServi
4C 6F 6D 2F 6F 2F ce $Lcom/youge/
000000A0 2F 2F service/user/Use
000000B0 3B 6E rService; init
000000C0 6C 5A 3C 6E ialed Z <ini
000000D0 3E 6F t> ()V Code
000000E0 0F 4C 6E 4E 6D 6C LineNumberTabl
000000F0 4C 6F 6C 6C e LocalVariabl
6C eTable this
4C 6F 6D 2F 6F 2F 2F Lcom/youge/api/B
6F 6C 4B 6E 3B yteCodeClassKen;
6E 0D 6B 4D init StackM
6C apTable addUse
4C 6A 2F 6C 6E r ()Ljava/lang
2F 6E 3B /Integer; user
6E 6F 1E 4C 6F 6D 2F 6F Info Lcom/youg
2F 6F 6A 6F 2F 2F e/pojo/user/User
6E 6F 3B 0A 6F Info; Exceptio
000001A0 6E 6D 6E ns U main (
000001B0 5B 4C 6A 2F 6C 6E 2F [Ljava/lang/Stri
000001C0 6E 3B 5B ng;)V args [
000001D0 4C 6A 2F 6C 6E 2F 6E Ljava/lang/Strin
000001E0 3B 6C 4B 6E g; classKen
000001F0 4C 6A 2F 6C affect Ljava/l
6E 2F 6E 3B 0A ang/Integer; S
6F 6C ourceFile Byte
6F 6C 4B 6E 2E 6A CodeClassKen.jav
0C 0C 0C 1F a ! " ( "
0C 6E 2E V W X init.
2E 2E 0C 5A 5B 6F 6D .. Y Z [ &com
2F 6F 2F 2F /youge/service/u
2F ser/UserServiceI
6D 6C 0C 1D 1E 1C 6F 6D 2F mpl com/y
6F 2F 6F 6A 6F 2F 2F ouge/pojo/user/U
000002A0 6E 6F 6C 6C 0C serInfo lilei
000002B0 5C 5B 5D 0C 5E 5F 0C \ [ ] ^ _ `
000002C0 6E 6F 2E 0C a new road. b
000002D0 5B 0C 2A 1E 6F 6D [ c * d com
000002E0 2F 6F 2F 2F /youge/api/ByteC
000002F0 6F 6C 4B 6E 0C 2A 2B odeClassKen * +
6A 2F 6C 6E 2F java/lang/Str
6E 6C ingBuilder aff
3A 0C 0C 0C ect: e f e g
6A 2F 6C 6E h i java/lang
2F 4F 6A 6A 2F 6C /Object java/l
6E 2F 6F 6E ang/Exception
6A 2F 6C 6E 2F 6D java/lang/System
6F 4C 6A 2F out Ljava/i
6F 2F 6E 6D 3B o/PrintStream;
6A 2F 6F 2F 6E java/io/PrintSt
000003A0 6D 6E 6C 6E ream println
000003B0 4C 6A 2F 6C 6E 2F (Ljava/lang/Str
000003C0 6E 3B 4E 6D ing;)V setName
000003D0 6A 2F 6C 6E 2F 6E java/lang/Int
000003E0 6C 4F eger valueOf
000003F0 4C 6A 2F 6C 6E 2F (I)Ljava/lang/I
6E 3B nteger; setAge
4C 6A 2F 6C 6E 2F (Ljava/lang/I
6E 3B 0A nteger;)V setA
6F 6D 2F 6F ddress "com/you
2F 2F 2F ge/service/user/
4C UserService (L
6F 6D 2F 6F 2F 6F 6A 6F 2F com/youge/pojo/u
2F 6E 6F 3B 4C 6A ser/UserInfo;)Lj
2F 6C 6E 2F 6E ava/lang/Integer
3B 6E 2D 4C 6A ; append -(Lj
000004A0 2F 6C 6E 2F 6E 3B ava/lang/String;
000004B0 4C 6A 2F 6C 6E 2F )Ljava/lang/Stri
000004C0 6E 6C 3B 2D 4C 6A ngBuilder; -(Lj
000004D0 2F 6C 6E 2F 4F 6A 3B ava/lang/Object;
000004E0 4C 6A 2F 6C 6E 2F )Ljava/lang/Stri
000004F0 6E 6C 3B 6F ngBuilder; toS
6E 4C 6A 2F tring ()Ljava/
6C 6E 2F 6E 3B lang/String; !
1C 1D 1E
1F ! "
3B 2A B7 # ; *?
2A B7 B1 *? ? $
0E
0C % & '
( " # c
2A B4 9A 1B B2 *? ? ?
000005A0 B6 2A BB B7 B5 ? *? Y? ?
000005B0 2A B5 B1 * ? ? $
000005C0 0F
000005D0 1A 1A 1F 1C 0C %
000005E0 & ' )
000005F0 1F 2A 2B 6C * + # l
BB 0A B7 0B 4C (? Y? L
2B 0C B6 0D 2B 0C B8 0E B6 0F 2B + ? + ? ? +
B6 2A B4 2B B9 B0 ? *? +? ?
1F $
0E 1D ! " #
% ( & '
2C 2D 2E , - .
2F / #
BB q '? Y
B7 4C 2B B6 4D B2 BB ? L+? M? ? Y
000006A0 B7 B6 2C B6 1A B6 1B B6 ? ? ,? ? ?
000006B0 B1 ? $
000006C0 0D 2A 2B ( ) * & +
000006D0 % ' 2 3
000006E0 1F 0D 1A '
000006F0 2E 2F . /

0. class文件的整体架构格式如下:

type    descriptor                                remark
u4 magic 0xCAFEBABE
u2 minor_version
u2 major_version
u2 constant_pool_count
cp_info constant_pool[cosntant_pool_count – ] index  is invalid
u2 access_flags
u2 this_class
u2 super_class
u2 interfaces_count
u2 interfaces[interfaces_count]
u2 fields_count
field_info fields[fields_count]
u2 methods_count
method_info methods[methods_count]
u2 attributes_count
attribute_info attributes[attributes_count]

图表class文件整体架构格式如下:

空手套白狼,硬阅java字节码class文件

1. 文件头

0xCAFE BABE: 前四个字节,魔数,代表文件类型,java的class文件固定为 0xCAFEBABE. 助记词: 咖啡宝贝

0x0000 0034: 接下来要知道版本号。版本号含主版本号和次版本号,都是各占2个字节。在此Demo种为0X0000 0034。其中前面的0000是次版本号,后面的0034是主版本号。通过进制转换得到的是次版本号为0,主版本号为52。

从oracle官方网站我们能够知道,52对应的正式jdk1.8,而其次版本为0,所以该文件的版本为1.8.0。如果需要验证,可以在用java –version命令输出版本号,或者修改编译目标版本–target重新编译,查看编译后的字节码文件版本号是否做了相应的修改。

2. 常量池

至此,我们共了解了前8字节的含义,下面讲讲常量池相关内容。

紧接着主版本号之后的就是常量池入口。常量池是Class文件中的资源仓库,在接下来的内容中我们会发现很多地方会涉及,如Class Name,Interfaces等。常量池中主要存储2大类常量:字面量和符号引用。字面量如文本字符串,java中声明为final的常量值等等,而符号引用如类和接口的全局限定名,字段的名称和描述符,方法的名称和描述符。

为什么需要类和接口的全局限定名呢?系统引用类或者接口的时候不是通过内存地址进行操作吗?这里大家仔细想想,java虚拟机在没有将类加载到内存的时候根本都没有分配内存地址,也就不存在对内存的操作,所以java虚拟机首先需要将类加载到虚拟机中,那么这个过程设计对类的定位(需要加载A包下的B类,不能加载到别的包下面的别的类中),所以需要通过全局限定名来判别唯一性。这就是为什么叫做全局,限定的意思,也就是唯一性。

在进行具体常量池分析之前,我们先来了解一下常量池的项目类型表:

这里tag用来表示当前常量池不同类型的项。info中存放常量池项中存放的数据。

tag中表示的数据类型:

tag名称                                remark            tag值
CONSTANT_Utf8_info 记录字符串的值
CONSTANT_Integer_info 用于记录int类型的常量值
CONSTANT_Float_info 用于记录float类型的常量值
CONSTANT_Long_info 用于记录long类型的常量值   
CONSTANT_Double_info 用于记录double类型的常量值   
CONSTANT_Class_info 用于记录类或接口名       
CONSTANT_String_info 用于记录常量字符串的值     
CONSTANT_Fieldref_info 用于记录字段信息(包括类或接口中定义的字段以及代码中使用到的字段) 
CONSTANT_Methodref_info 用于记录方法信息(包括类中定义的方法以及代码中使用到的方法)    
CONSTANT_InterfaceMethodref_info 用于记录接口中的方法信息(包括接口中定义的方法以及代码中使用到的方法)
CONSTANT_NameAndType_info 记录方法或字段的名称(name)和描述符(descriptor)         
CONSTANT_MethodHandle_info 方法句柄表,.7增加       
CONSTANT_MethodType_info 方法类型表,.7增加      
CONSTANT_InvokeDynamic_info 动态方法调用点,.7增加    

上面的表中描述了14种数据类型的结构。接下来我们按照Demo的字节码进行逐一翻译。

0x006A: 由于常量池的数量不固定(n+2),所以需要在常量池的入口处放置一项u2类型的数据代表常量池数量。因此该6A进制是106,表示有105项常量,索引范围为1~105。Class文件格式规定,设计者就讲第0项保留出来了,以备后患。从这里我们知道接下来我们需要翻译出105项常量。

Constant #1 (一共有105个常量,这是第一个,以此类推…)

0x0A:从常量类型表中我们发现,第一个数据均是u1类型的tag,16进制的0a是十进制的10,对应表中的MethodRef_info。

0x001C: 指向方法描述符 CONSTANT_Class_info 的索引项, 为 #28, 查找得: #84           // java/lang/Object

0x0039: 指向名称及类型描述符 CONSTANT_NameAndType_info 的索引项, 为 #57, 查找得 #33:#34       // "<init>":()V

如上,依次计算出每个常量池的范围,直到 105项全部计算完成。

3 Access_Flag 访问标志

Flag Name        Value        Remarks
ACC_PUBLIC 0x0001 pubilc,包外可访问。
ACC_FINAL 0x0010 final,不能有子类。
ACC_SUPER 0x0020 用于兼容早期编译器,新编译器都设置该标记,以在使用 invokespecial指令时对子类方法做特定处理。
ACC_INTERFACE 0x0200 接口,同时需要设置:ACC_ABSTRACT。不可同时设置:ACC_FINAL、ACC_SUPER、ACC_ENUM
ACC_ABSTRACT 0x0400 抽象类,无法实例化。不可和ACC_FINAL同时设置。
ACC_SYNTHETIC 0x1000 synthetic,由编译器产生,不存在于源代码中。 ACC_ANNOTATION 0x2000 注解类型(annotation),需同时设置:ACC_INTERFACE、ACC_ABSTRACT
ACC_ENUM 0x4000 枚举类型

访问标志信息包括该Class文件是类还是接口,是否被定义成public,是否是abstract,如果是类,是否被声明成final。通过上面的源代码,我们知道该文件是类并且是public。

4  this_class

0x0013: this_class是指向constant pool的索引值,该值必须是CONSTANT_Class_info类型,指定当前字节码定义的类或接口。

5 父类索引 super_class

0x001C: 同理:#28(Class #84 java/lang/Object)

6 接口索引 interfaces

0x0000: 通过java_byte.jpeg图我们知道,这个接口有2+n个字节,前两个字节表示的是接口数量,后面跟着就是接口的表。偏移量为: 4(magic)+4(version)+2+106*(constant)+2(access_flags)+2(this)+2(super)=122*,我们这个类没有任何接口,所以应该是0000。常量池是动态变化的,有点难算,不过算出来就是这个值。此处可以先找到super

7 字段表集合

字段表用于描述类和接口中声明的变量。这里的字段包含了类级别变量以及实例变量,但是不包括方法内部声明的局部变量。

同样,接下来就是2+n个字段属性。我们只有一个属性a,按道理应该是0001。查找文件果不其然是0001。

那么接下来我们要针对这样的字段进行解析。附上字段表如下:

type        descriptor            remark
u2 access_flags 记录字段的访问权限。
u2 name_index constant_pool中的索引,CONSTANT_Utf8_info类型。指定字段的名称。
u2 descriptor_index constant_pool中的索引,CONSTANT_Utf8_info类型,指定字段的描述符(见附录C)。
u2 attributes_count attributes包含的项目数。
attribute_info attributes[attributes_count]

0x00 02: 访问标志为private(自行搜索字段访问标志)

0x00 02: 字段名称索引为#2,对应的是 Methodref          #19.#58       // com/youge/api/ByteCodeClassKen.init:()V

0x001D: 描述符索引为#29,对应的是 Utf8 userService

0x001E :描述符索引为#30,对应的是 Utf8 Lcom/youge/service/user/UserService;

0x00 00 :属性表数量为0,因此没有属性表。

8 方法

我们只有一个方法testMethod,按照道理应该前2个字节是0001。通过查找发现是0x00 02。这是什么原因,这代表着有2个方法呢?且继续看……

上图是一张方法表结构图,按照这个图我们分析下面的字节码:

type        descriptor            remark
u2 access_flags 记录方法的访问权限。见2.9.1
u2 name_index constant_pool中的索引,CONSTANT_Utf8_info类型。指定方法名称。
u2 descriptor_index constant_pool中的索引,CONSTANT_Utf8_info类型,指定方法的描述符(见附录C)。
u2 attributes_count attributes包含的项目数。
attribute_info attributes[attributes_count] 字段中包含的Attribute集合。

第1个方法:

0x00 01: 访问标志 ACC_PUBLIC,表明该方法是public。(可自行搜索方法访问标志表)

0x00 07: 方法名索引为#7,对应的是"<init>"

0x00 08: 方法描述符索引为#8,对应的是"()V"

0x00 01: 属性表数量为1(一个属性表)

那么这里涉及到了属性表。什么是属性表呢?可以这么理解,它是为了描述一些专有信息的,上面的方法带有一张属性表。所有属性表的结构如下图:

一个u2的属性名称索引,一个u2的属性长度加上属性长度的info。

虚拟机规范预定义的属性有很多,比如Code,LineNumberTable,LocalVariableTable,SourceFile等等,这个网上可以搜索到。

按照上面的表结构解析得到下面信息:

Offset                               

        3B          2A B7    #   ;        *?

     2A B7   B1               *? ?    $   

   0E                                   

       0C               %           & ' 

                          ( "   #   c 

          2A  B4   9A  1B B2           *? ? ?

      B6   2A BB     B7   B5       ? *? Y? ?

    2A  B5   B1              * ? ?    $   

                0F                      

   1A  1A  1F  1C       0C              %       

                          & '   )     

    1F    2A  2B         6C        * +   #   l

            BB  0A  B7  0B 4C          (? Y? L

   2B  0C B6  0D 2B   0C B8  0E B6  0F 2B   +  ? +  ? ? +

     B6   2A B4    2B B9     B0     ? *? +?   ?

0x0023:名称索引为#35("Code")。

0x0000 003B: 属性长度为59字节。

那么接下来解析一个Code属性表,按照下图解析 Code Attribute:

type    descriptor    remark
u2 attribute_name_index constant_pool中的索引,CONSTANT_Utf8_info类型。指定Attribute的名称("Code")。
u4 attribute_length 该Attribute内容的字节长度。
u2 max_stack 该方法操作栈的最大深度。
u2 max_locals 该方法调用时需要分配的局部变量的最大个数,包括该方法的参数。
u4 code_length 该方法字节码长度(以字节为单位)
u1 code[code_length] 存放字节码数组(字节码如何解析?)。
u2 exception_table_length 异常表的长度。
exception_table_info 每个表项记录一段异常处理代码信息和范围。
u2 start_pc 记录应用该项异常处理的起始字节码。在字节码数组中的起始索引号[start_pc, end_pc)。索引号必须是opcode(一条指令的开始位置)对应的位置。
u2 end_pc u2
handler_pc 记录该项异常处理代码的开始地址。在字节码数组中的开始索引号。索引号必须是opcode对应的位置。
u2 catch_type constant_pool中的索引,CONSTANT_Class_info类型。指定该项能捕获的异常类(或其子类)。或0用于实现finally语法(即不管什么类型都会捕获。)
exception_table[exception_table_length]
u2 attributes_count attributes包含的项目数。
attribute_info attributes[attributes_count]

前面6个字节(名称索引2字节+属性长度4字节)已经解析过了,所以接下来就是解析剩下的59-6=53字节即可。

0x00 01: max_stack=1

0x00 01: max_locals=1

0x0000 0009: code_length=9

0x2A B7 00 01 2A B7 00 02 B1: 这是code代码,可以通过虚拟机字节码指令进行查找。

2a=aload_0(将第一个引用变量推送到栈顶)

b7=invokespecial(调用父类构造方法)

00=什么都不做

01=将 #1 常量池推送到栈顶,即默认"<init>":()V 构造方法

2a=同上

b7=同上

00=什么都不做

02=将#2常量池推送栈顶,即 init() 方法

b1=return 从当前方法返回void

整理,去除无动作指令得到下面

0: aload_0

1: invokespecial #1                  // Method java/lang/Object."<init>":()V

4: aload_0

5: invokespecial #2                  // Method init:()V

8: return

接下来顺着Code属性表继续解析下去:

0x00 00 : exception_table_length=0

0x00 02 : attributes_count=2(Code属性表内部还含有2个属性表)

0x00 24: 第一个属性表是"LineNumberTable"

0x00 00 00 0E : "属性长度为14″

0x00 03 :line_number_table_length=3

line_number_table是一个数量为line_number_table_length,类型为line_number_info的集合,line_number_info表包括了start_pc和line_number两个u2类型的数据项,前者是字节码行号,后者是Java源码行号

0x00 00 : start_pc=0

0x00 12 : end_pc=18

0x00 04 : start_pc=4

0x00 13 : end_pc=19

0x00 25 第二个属性表是:"LocalVariableTable"

0x00 0000 0c:属性长度为12

0x00 01 : local_variable_table_length=1

然后按照local_variable_info表结构进行解析:

0x00 00 : start_pc=0

0x00 09:length=9

0x0026 : #38 name_index="this"

0x0027 : #39 descriptor_index #13 ("Lcom/youge/api/ByteCodeClassKen;")

0000 index=0

-----------到这里第一个方法就解析完成了--------------------

Method(<init>)–1个属性Code表-2个属性表(LineNumberTable ,LocalVariableTable)

10 Attribute

attributes数组记录了和类或接口相关的所有Attribute项(和字段相关的Attribute在field_info的attributes中,和方法相关的Attribute在method_info的attrubutes中,和字节码相关的Attribute在Code Attribute的attributes中)。attributes数组中的每项都是attribute_info类型,它描述了Attribute的名称、详细信息等。该attributes数组描述了ClassFile的一些额外信息。JVM必须忽略它不能识别的Attribute,而且那些JVM不能识别的的Attribute也不能影响class文件的语义。

当前定义的Attribute有:

Code Attribute、Constant Value Attibute、Deprecated Attribute、Enclosing Method Attribute、Exceptions Attribute、Inner Classes Attribute、Line Number Table Attribute、Local Variable Table Attribute、Local Variable Type Table Attribute、Runtime Visible Annotations Attribute、Runtime Invisible Annotation Attribute、Runtime Visible Parameter Annotation Attribute、Runtime Invisible Parameter Annotation Attribute、Signature Attribute、Source Debug Extension Attribute、Source File Attribute、Stack Map Table Attribute、Synthetic Attribute、Annotation Default Attribute等。

它们有些只存在于field_info中,有些只存在method_info中,有些只存在ClassFile中,有些只存在于Code Attribute中,还有些可以同时存在于field_info、method_info、classfile中。

Attribute结构只存在与ClassFile、method_info、field_info、Code Attribute结构中。

0x0002 :同样的,表示有2个Attributes了。

0x0037 : #15("SourceFile")

0x0000 0002 attribute_length=2

0x0038 : sourcefile_index = #56("ByteCodeClassKen.java")

SourceFile属性用来记录生成该Class文件的源码文件名称。

11. 直接使用 javap 反编译成可读的文本型字节码

javap -verbose xxx.class

Classfile /D:/www/test/target/classes/com/youge/api/ByteCodeClassKen.class
Last modified 2018-9-26; size 1801 bytes
MD5 checksum 76e207e502c3b096346480457d98cdef
Compiled from "ByteCodeClassKen.java"
public class com.youge.api.ByteCodeClassKen
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #28.#57 // java/lang/Object."<init>":()V
#2 = Methodref #19.#58 // com/youge/api/ByteCodeClassKen.init:()V
#3 = Fieldref #19.#59 // com/youge/api/ByteCodeClassKen.initialed:Z
#4 = Fieldref #60.#61 // java/lang/System.out:Ljava/io/PrintStream;
#5 = String #62 // init...
#6 = Methodref #63.#64 // java/io/PrintStream.println:(Ljava/lang/String;)V
#7 = Class #65 // com/youge/service/user/UserServiceImpl
#8 = Methodref #7.#57 // com/youge/service/user/UserServiceImpl."<init>":()V
#9 = Fieldref #19.#66 // com/youge/api/ByteCodeClassKen.userService:Lcom/youge/service/user/UserService;
#10 = Class #67 // com/youge/pojo/user/UserInfo
#11 = Methodref #10.#57 // com/youge/pojo/user/UserInfo."<init>":()V
#12 = String #68 // lilei
#13 = Methodref #10.#69 // com/youge/pojo/user/UserInfo.setName:(Ljava/lang/String;)V
#14 = Methodref #70.#71 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#15 = Methodref #10.#72 // com/youge/pojo/user/UserInfo.setAge:(Ljava/lang/Integer;)V
#16 = String #73 // new road.
#17 = Methodref #10.#74 // com/youge/pojo/user/UserInfo.setAddress:(Ljava/lang/String;)V
#18 = InterfaceMethodref #75.#76 // com/youge/service/user/UserService.addUser:(Lcom/youge/pojo/user/UserInfo;)Ljava/lang/Integer;
#19 = Class #77 // com/youge/api/ByteCodeClassKen
#20 = Methodref #19.#57 // com/youge/api/ByteCodeClassKen."<init>":()V
#21 = Methodref #19.#78 // com/youge/api/ByteCodeClassKen.addUser:()Ljava/lang/Integer;
#22 = Class #79 // java/lang/StringBuilder
#23 = Methodref #22.#57 // java/lang/StringBuilder."<init>":()V
#24 = String #80 // affect:
#25 = Methodref #22.#81 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#26 = Methodref #22.#82 // java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
#27 = Methodref #22.#83 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#28 = Class #84 // java/lang/Object
#29 = Utf8 userService
#30 = Utf8 Lcom/youge/service/user/UserService;
#31 = Utf8 initialed
#32 = Utf8 Z
#33 = Utf8 <init>
#34 = Utf8 ()V
#35 = Utf8 Code
#36 = Utf8 LineNumberTable
#37 = Utf8 LocalVariableTable
#38 = Utf8 this
#39 = Utf8 Lcom/youge/api/ByteCodeClassKen;
#40 = Utf8 init
#41 = Utf8 StackMapTable
#42 = Utf8 addUser
#43 = Utf8 ()Ljava/lang/Integer;
#44 = Utf8 userInfo
#45 = Utf8 Lcom/youge/pojo/user/UserInfo;
#46 = Utf8 Exceptions
#47 = Class #85 // java/lang/Exception
#48 = Utf8 main
#49 = Utf8 ([Ljava/lang/String;)V
#50 = Utf8 args
#51 = Utf8 [Ljava/lang/String;
#52 = Utf8 classKen
#53 = Utf8 affect
#54 = Utf8 Ljava/lang/Integer;
#55 = Utf8 SourceFile
#56 = Utf8 ByteCodeClassKen.java
#57 = NameAndType #33:#34 // "<init>":()V
#58 = NameAndType #40:#34 // init:()V
#59 = NameAndType #31:#32 // initialed:Z
#60 = Class #86 // java/lang/System
#61 = NameAndType #87:#88 // out:Ljava/io/PrintStream;
#62 = Utf8 init...
#63 = Class #89 // java/io/PrintStream
#64 = NameAndType #90:#91 // println:(Ljava/lang/String;)V
#65 = Utf8 com/youge/service/user/UserServiceImpl
#66 = NameAndType #29:#30 // userService:Lcom/youge/service/user/UserService;
#67 = Utf8 com/youge/pojo/user/UserInfo
#68 = Utf8 lilei
#69 = NameAndType #92:#91 // setName:(Ljava/lang/String;)V
#70 = Class #93 // java/lang/Integer
#71 = NameAndType #94:#95 // valueOf:(I)Ljava/lang/Integer;
#72 = NameAndType #96:#97 // setAge:(Ljava/lang/Integer;)V
#73 = Utf8 new road.
#74 = NameAndType #98:#91 // setAddress:(Ljava/lang/String;)V
#75 = Class #99 // com/youge/service/user/UserService
#76 = NameAndType #42:#100 // addUser:(Lcom/youge/pojo/user/UserInfo;)Ljava/lang/Integer;
#77 = Utf8 com/youge/api/ByteCodeClassKen
#78 = NameAndType #42:#43 // addUser:()Ljava/lang/Integer;
#79 = Utf8 java/lang/StringBuilder
#80 = Utf8 affect:
#81 = NameAndType #101:#102 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#82 = NameAndType #101:#103 // append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
#83 = NameAndType #104:#105 // toString:()Ljava/lang/String;
#84 = Utf8 java/lang/Object
#85 = Utf8 java/lang/Exception
#86 = Utf8 java/lang/System
#87 = Utf8 out
#88 = Utf8 Ljava/io/PrintStream;
#89 = Utf8 java/io/PrintStream
#90 = Utf8 println
#91 = Utf8 (Ljava/lang/String;)V
#92 = Utf8 setName
#93 = Utf8 java/lang/Integer
#94 = Utf8 valueOf
#95 = Utf8 (I)Ljava/lang/Integer;
#96 = Utf8 setAge
#97 = Utf8 (Ljava/lang/Integer;)V
#98 = Utf8 setAddress
#99 = Utf8 com/youge/service/user/UserService
#100 = Utf8 (Lcom/youge/pojo/user/UserInfo;)Ljava/lang/Integer;
#101 = Utf8 append
#102 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#103 = Utf8 (Ljava/lang/Object;)Ljava/lang/StringBuilder;
#104 = Utf8 toString
#105 = Utf8 ()Ljava/lang/String;
{
public com.youge.api.ByteCodeClassKen();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: invokespecial #2 // Method init:()V
8: return
LineNumberTable:
line 18: 0
line 19: 4
line 20: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcom/youge/api/ByteCodeClassKen; public java.lang.Integer addUser() throws java.lang.Exception;
descriptor: ()Ljava/lang/Integer;
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=1
0: new #10 // class com/youge/pojo/user/UserInfo
3: dup
4: invokespecial #11 // Method com/youge/pojo/user/UserInfo."<init>":()V
7: astore_1
8: aload_1
9: ldc #12 // String lilei
11: invokevirtual #13 // Method com/youge/pojo/user/UserInfo.setName:(Ljava/lang/String;)V
14: aload_1
15: bipush 12
17: invokestatic #14 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
20: invokevirtual #15 // Method com/youge/pojo/user/UserInfo.setAge:(Ljava/lang/Integer;)V
23: aload_1
24: ldc #16 // String new road.
26: invokevirtual #17 // Method com/youge/pojo/user/UserInfo.setAddress:(Ljava/lang/String;)V
29: aload_0
30: getfield #9 // Field userService:Lcom/youge/service/user/UserService;
33: aload_1
34: invokeinterface #18, 2 // InterfaceMethod com/youge/service/user/UserService.addUser:(Lcom/youge/pojo/user/UserInfo;)Ljava/lang/Integer;
39: areturn
LineNumberTable:
line 31: 0
line 32: 8
line 33: 14
line 34: 23
line 35: 29
LocalVariableTable:
Start Length Slot Name Signature
0 40 0 this Lcom/youge/api/ByteCodeClassKen;
8 32 1 userInfo Lcom/youge/pojo/user/UserInfo;
Exceptions:
throws java.lang.Exception public static void main(java.lang.String[]) throws java.lang.Exception;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=3, args_size=1
0: new #19 // class com/youge/api/ByteCodeClassKen
3: dup
4: invokespecial #20 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #21 // Method addUser:()Ljava/lang/Integer;
12: astore_2
13: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
16: new #22 // class java/lang/StringBuilder
19: dup
20: invokespecial #23 // Method java/lang/StringBuilder."<init>":()V
23: ldc #24 // String affect:
25: invokevirtual #25 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: aload_2
29: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
32: invokevirtual #27 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
35: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
38: return
LineNumberTable:
line 40: 0
line 41: 8
line 42: 13
line 43: 38
LocalVariableTable:
Start Length Slot Name Signature
0 39 0 args [Ljava/lang/String;
8 31 1 classKen Lcom/youge/api/ByteCodeClassKen;
13 26 2 affect Ljava/lang/Integer;
Exceptions:
throws java.lang.Exception
}
SourceFile: "ByteCodeClassKen.java"

字节码阅读或者简单反编译的字节码阅读能力,不是排查问题所必须的,但是,如果能够顺手读懂这些语言,无疑会给自己带来莫大的方便。另外,也能够更深层理解你写的代码本来的样子,从而从另一个角度发现不一样的自己。

后记,java字节码指令表速查:

### java字节码指令列表

字节码    助记符    指令含义
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 将单字节的常量值(-~)推送至栈顶
0x11 sipush 将一个短整型常量值(-~)推送至栈顶
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+=)
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型数值大小,并将结果(,,-)压入栈顶
0x95 fcmpl 比较栈顶两float型数值大小,并将结果(,,-)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
0x96 fcmpg 比较栈顶两float型数值大小,并将结果(,,-)压入栈顶;当其中一个数值为NaN时,将1压入栈顶
0x97 dcmpl 比较栈顶两do le型数值大小,并将结果(,,-)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
0x98 dcmpg 比较栈顶两do le型数值大小,并将结果(,,-)压入栈顶;当其中一个数值为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下一条指令地址压入栈顶

分类助词符:

JVM指令助记符
变量到操作数栈:iload,iload_,lload,lload_,fload,fload_,dload,dload_,aload,aload_
操作数栈到变量:istore,istore_,lstore,lstore_,fstore,fstore_,dstore,dstor_,astore,astore_
常数到操作数栈:bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_ml,iconst_,lconst_,fconst_,dconst_
加:iadd,ladd,fadd,dadd
减:is ,ls ,fs ,ds
乘:imul,lmul,fmul,dmul
除:idiv,ldiv,fdiv,ddiv
余数:irem,lrem,frem,drem
取负:ineg,lneg,fneg,dneg
移位:ishl,lshr,iushr,lshl,lshr,lushr
按位或:ior,lor
按位与:iand,land
按位异或:ixor,lxor
类型转换:i2l,i2f,i2d,l2f,l2d,f2d(放宽数值转换)
i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l,d2f(缩窄数值转换) 创建类实便:new
创建新数组:newarray,anewarray,multianwarray
访问类的域和类实例域:getfield,putfield,getstatic,putstatic
把数据装载到操作数栈:baload,caload,saload,iaload,laload,faload,daload,aaload
从操作数栈存存储到数组:bastore,castore,sastore,iastore,lastore,fastore,dastore,aastore
获取数组长度:arraylength
检相类实例或数组属性:instanceof,checkcast
操作数栈管理:pop,pop2,dup,dup2,dup_xl,dup2_xl,dup_x2,dup2_x2,swap
有条件转移:ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnonnull,if_icmpeq,if_icmpene,
if_icmplt,if_icmpgt,if_icmple,if_icmpge,if_acmpeq,if_acmpne,lcmp,fcmpl
fcmpg,dcmpl,dcmpg
复合条件转移:tableswitch,lookupswitch
无条件转移:goto,goto_w,jsr,jsr_w,ret
调度对象的实便方法:invokevirt l
调用由接口实现的方法:invokeinterface
调用需要特殊处理的实例方法:invokespecial
调用命名类中的静态方法:invokestatic
方法返回:ireturn,lreturn,freturn,dreturn,areturn,return
异常:athrow
finally关键字的实现使用:jsr,jsr_w,ret