如下,是一些java字节码也就是原始的class文件,当应用部署到线上之后,我们能够看到的也就是这样的字样了。那么怎样解呢?就让我们一起,来解读解读字节码吧!
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F 00000000 CA FE BA BE 00 00 00 34 00 6A 0A 00 1C 00 39 0A 漱壕 4 j 9 00000010 00 13 00 3A 09 00 13 00 3B 09 00 3C 00 3D 08 00 : ; < = 00000020 3E 0A 00 3F 00 40 07 00 41 0A 00 07 00 39 09 00 > ? @ A 9 00000030 13 00 42 07 00 43 0A 00 0A 00 39 08 00 44 0A 00 B C 9 D 00000040 0A 00 45 0A 00 46 00 47 0A 00 0A 00 48 08 00 49 E F G H I 00000050 0A 00 0A 00 4A 0B 00 4B 00 4C 07 00 4D 0A 00 13 J K L M 00000060 00 39 0A 00 13 00 4E 07 00 4F 0A 00 16 00 39 08 9 N O 9 00000070 00 50 0A 00 16 00 51 0A 00 16 00 52 0A 00 16 00 P Q R 00000080 53 07 00 54 01 00 0B 75 73 65 72 53 65 72 76 69 S T userServi 00000090 63 65 01 00 24 4C 63 6F 6D 2F 79 6F 75 67 65 2F ce $Lcom/youge/ 000000A0 73 65 72 76 69 63 65 2F 75 73 65 72 2F 55 73 65 service/user/Use 000000B0 72 53 65 72 76 69 63 65 3B 01 00 09 69 6E 69 74 rService; init 000000C0 69 61 6C 65 64 01 00 01 5A 01 00 06 3C 69 6E 69 ialed Z <ini 000000D0 74 3E 01 00 03 28 29 56 01 00 04 43 6F 64 65 01 t> ()V Code
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 – 1]index 0 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]
1. 文件头
CA FE BA BE: 前四个字节,魔数,代表文件类型,java的class文件固定为 0xCAFEBABE. 助记词: 咖啡宝贝
00 00 00 34: 接下来要知道版本号。版本号含主版本号和次版本号,都是各占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
CONSTANT_Class_info 用于记录类或接口名
CONSTANT_Integer_info 用于记录int类型的常量值
CONSTANT_Long_info 用于记录long类型的常量值
CONSTANT_Float_info 用于记录float类型的常量值
CONSTANT_Double_info 用于记录double类型的常量值
CONSTANT_String_info用于记录常量字符串的值
CONSTANT_Fieldref_info 用于记录字段信息(包括类或接口中定义的字段以及代码中使用到的字段)
CONSTANT_Methodref_info 用于记录方法信息(包括类中定义的方法以及代码中使用到的方法)
CONSTANT_InterfaceMethodref_info 用于记录接口中的方法信息(包括接口中定义的方法以及代码中使用到的方法)
CONSTANT_NameAndType_info 记录方法或字段的名称(name)和描述符(descriptor)
CONSTANT_Utf8_info 记录字符串的值
上面的表中描述了11中数据类型的结构,其实在jdk1.7之后又增加了3种( CONSTANT_MethodHandle_info ,CONSTANT_MethodType_info 以及 CONSTANT_InvokeDynamic_info )。这样算起来一共是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 NameValueRemarks
ACC_PUBLIC0x0001pubilc,包外可访问。
ACC_FINAL0x0010final,不能有子类。
ACC_SUPER0x0020用于兼容早期编译器,新编译器都设置该标记,以在使用 invokespecial指令时对子类方法做特定处理。
ACC_INTERFACE0x0200接口,同时需要设置:ACC_ABSTRACT。不可同时设置:ACC_FINAL、ACC_SUPER、ACC_ENUM
ACC_ABSTRACT0x0400抽象类,无法实例化。不可和ACC_FINAL同时设置。
ACC_SYNTHETIC 0x1000synthetic,由编译器产生,不存在于源代码中。 ACC_ANNOTATION 0x2000注解类型(annotation),需同时设置:ACC_INTERFACE、ACC_ABSTRACT
ACC_ENUM0x4000枚举类型
访问标志信息包括该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。
那么接下来我们要针对这样的字段进行解析。附上字段表如下:
typedescriptorremark
u2access_flags记录字段的访问权限。
u2name_indexconstant_pool中的索引,CONSTANT_Utf8_info类型。指定字段的名称。
u2descriptor_indexconstant_pool中的索引,CONSTANT_Utf8_info类型,指定字段的描述符(见附录C)。
u2attributes_countattributes包含的项目数。
attribute_infoattributes[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个方法呢?且继续看……
上图是一张方法表结构图,按照这个图我们分析下面的字节码:
typedescriptorremark
u2access_flags记录方法的访问权限。见2.9.1
u2name_indexconstant_pool中的索引,CONSTANT_Utf8_info类型。指定方法名称。
u2descriptor_indexconstant_pool中的索引,CONSTANT_Utf8_info类型,指定方法的描述符(见附录C)。
u2attributes_countattributes包含的项目数。
attribute_infoattributes[attributes_count]字段中包含的Attribute集合。见2.9.2-2.9.11
第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 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 00001344 00 23 00 00 00 3B 00 01 00 01 00 00 00 09 2A B7 # ; *? 00001360 00 01 2A B7 00 02 B1 00 00 00 02 00 24 00 00 00 *? ? $ 00001376 0E 00 03 00 00 00 12 00 04 00 13 00 08 00 14 00 00001392 25 00 00 00 0C 00 01 00 00 00 09 00 26 00 27 00 % & ' 00001408 00 00 02 00 28 00 22 00 01 00 23 00 00 00 63 00 ( " # c 00001424 03 00 01 00 00 00 20 2A B4 00 03 9A 00 1B B2 00 *? ? ? 00001440 04 12 05 B6 00 06 2A BB 00 07 59 B7 00 08 B5 00 ? *? Y? ? 00001456 09 2A 04 B5 00 03 B1 00 00 00 03 00 24 00 00 00 * ? ? $ 00001472 16 00 05 00 00 00 17 00 07 00 18 00 0F 00 19 00 00001488 1A 00 1A 00 1F 00 1C 00 25 00 00 00 0C 00 01 00 % 00001504 00 00 20 00 26 00 27 00 00 00 29 00 00 00 03 00 & ' ) 00001520 01 1F 00 01 00 2A 00 2B 00 02 00 23 00 00 00 6C * + # l 00001536 00 02 00 02 00 00 00 28 BB 00 0A 59 B7 00 0B 4C (? Y? L 00001552 2B 12 0C B6 00 0D 2B 10 0C B8 00 0E B6 00 0F 2B + ? + ? ? + 00001568 12 10 B6 00 11 2A B4 00 09 2B B9 00 12 02 00 B0 ? *? +? ?
0x0023:名称索引为#35("Code")。
0x0000 003B: 属性长度为59字节。
那么接下来解析一个Code属性表,按照下图解析
Code Attribute
typedescriptorremark
u2attribute_name_indexconstant_pool中的索引,CONSTANT_Utf8_info类型。指定Attribute的名称("Code")。
u4attribute_length该Attribute内容的字节长度。
u2max_stack该方法操作栈的最大深度。
u2max_locals该方法调用时需要分配的局部变量的最大个数,包括该方法的参数。
u4code_length该方法字节码长度(以字节为单位)
u1code[code_length]存放字节码数组(字节码如何解析?)。
u2exception_table_length异常表的长度。
exception_table_info每个表项记录一段异常处理代码信息和范围。
u2start_pc记录应用该项异常处理的起始字节码。在字节码数组中的起始索引号[start_pc, end_pc)。索引号必须是opcode(一条指令的开始位置)对应的位置。
u2end_pcu2
handler_pc记录该项异常处理代码的开始地址。在字节码数组中的开始索引号。索引号必须是opcode对应的位置。
u2catch_typeconstant_pool中的索引,CONSTANT_Class_info类型。指定该项能捕获的异常类(或其子类)。或0用于实现finally语法(即不管什么类型都会捕获。)
exception_table[exception_table_length]
u2attributes_countattributes包含的项目数。
attribute_infoattributes[attributes_count]
xxx
前面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"
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 CA FE BA BE 00 00 00 34 00 6A 0A 00 1C 00 39 0A 漱壕 4 j 9 00000010 00 13 00 3A 09 00 13 00 3B 09 00 3C 00 3D 08 00 : ; < = 00000020 3E 0A 00 3F 00 40 07 00 41 0A 00 07 00 39 09 00 > ? @ A 9 00000030 13 00 42 07 00 43 0A 00 0A 00 39 08 00 44 0A 00 B C 9 D 00000040 0A 00 45 0A 00 46 00 47 0A 00 0A 00 48 08 00 49 E F G H I00000050 0A 00 0A 00 4A 0B 00 4B 00 4C 07 00 4D 0A 00 13 J K L M 00000060 00 39 0A 00 13 00 4E 07 00 4F 0A 00 16 00 39 08 9 N O 9 00000070 00 50 0A 00 16 00 51 0A 00 16 00 52 0A 00 16 00 P Q R 00000080 53 07 00 54 01 00 0B 75 73 65 72 53 65 72 76 69 S T userServi00000090 63 65 01 00 24 4C 63 6F 6D 2F 79 6F 75 67 65 2F ce $Lcom/youge/000000A0 73 65 72 76 69 63 65 2F 75 73 65 72 2F 55 73 65 service/user/Use000000B0 72 53 65 72 76 69 63 65 3B 01 00 09 69 6E 69 74 rService; init000000C0 69 61 6C 65 64 01 00 01 5A 01 00 06 3C 69 6E 69 ialed Z <ini000000D0 74 3E 01 00 03 28 29 56 01 00 04 43 6F 64 65 01 t> ()V Code
如上,是一些java字节码也就是原始的class文件,当应用部署到线上之后,我们能够看到的也就是这样的字样了。那么怎样解呢?
0. class文件的整体格式typedescriptorremarku4magic0xCAFEBABEu2minor_versionu2major_versionu2constant_pool_countcp_infoconstant_pool[cosntant_pool_count – 1]index 0 is invalidu2access_flagsu2this_classu2super_classu2interfaces_countu2interfaces[interfaces_count]u2fields_countfield_infofields[fields_count]u2methods_countmethod_infomethods[methods_count]u2attributes_countattribute_infoattributes[attributes_count]
1. 文件头CA FE BA BE: 前四个字节,魔数,代表文件类型,java的class文件固定为 0xCAFEBABE. 助记词: 咖啡宝贝00 00 00 34: 接下来要知道版本号。版本号含主版本号和次版本号,都是各占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名称remarkCONSTANT_Class_info用于记录类或接口名CONSTANT_Integer_info 用于记录int类型的常量值CONSTANT_Long_info用于记录long类型的常量值CONSTANT_Float_info用于记录float类型的常量值CONSTANT_Double_info用于记录double类型的常量值CONSTANT_String_info用于记录常量字符串的值CONSTANT_Fieldref_info用于记录字段信息(包括类或接口中定义的字段以及代码中使用到的字段)CONSTANT_Methodref_info用于记录方法信息(包括类中定义的方法以及代码中使用到的方法)CONSTANT_InterfaceMethodref_info用于记录接口中的方法信息(包括接口中定义的方法以及代码中使用到的方法)CONSTANT_NameAndType_info 记录方法或字段的名称(name)和描述符(descriptor)CONSTANT_Utf8_info记录字符串的值
上面的表中描述了11中数据类型的结构,其实在jdk1.7之后又增加了3种( CONSTANT_MethodHandle_info ,CONSTANT_MethodType_info 以及 CONSTANT_InvokeDynamic_info )。这样算起来一共是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/Object0x0039: 指向名称及类型描述符 CONSTANT_NameAndType_info 的索引项, 为 #57, 查找得 #33:#34 // "<init>":()V如上,依次计算出每个常量池的范围,直到 105项全部计算完成。
3.4 Access_Flag 访问标志
类或接口的访问权限Flag NameValueRemarksACC_PUBLIC0x0001pubilc,包外可访问。ACC_FINAL0x0010final,不能有子类。ACC_SUPER0x0020用于兼容早期编译器,新编译器都设置该标记,以在使用 invokespecial指令时对子类方法做特定处理。ACC_INTERFACE0x0200接口,同时需要设置:ACC_ABSTRACT。不可同时设置:ACC_FINAL、ACC_SUPER、ACC_ENUMACC_ABSTRACT0x0400抽象类,无法实例化。不可和ACC_FINAL同时设置。ACC_SYNTHETIC 0x1000synthetic,由编译器产生,不存在于源代码中。 ACC_ANNOTATION 0x2000注解类型(annotation),需同时设置:ACC_INTERFACE、ACC_ABSTRACTACC_ENUM0x4000枚举类型
访问标志信息包括该Class文件是类还是接口,是否被定义成public,是否是abstract,如果是类,是否被声明成final。通过上面的源代码,我们知道该文件是类并且是public。
3.5 this_class0x0013: this_class是指向constant pool的索引值,该值必须是CONSTANT_Class_info类型,指定当前字节码定义的类或接口。
3.6父类索引 super_class0x001C: 同理:#28(Class #84 java/lang/Object)
3.7 接口索引 interfaces0x0000: 通过java_byte.jpeg图我们知道,这个接口有2+n个字节,前两个字节表示的是接口数量,后面跟着就是接口的表。偏移量为: 4(magic)+4(version)+2+106*(constant)+2(access_flags)+2(this)+2(super)=122*,我们这个类没有任何接口,所以应该是0000。常量池是动态变化的,有点难算,不过算出来就是这个值。此处可以先找到super
3.8 字段表集合字段表用于描述类和接口中声明的变量。这里的字段包含了类级别变量以及实例变量,但是不包括方法内部声明的局部变量。同样,接下来就是2+n个字段属性。我们只有一个属性a,按道理应该是0001。查找文件果不其然是0001。那么接下来我们要针对这样的字段进行解析。附上字段表如下:
typedescriptorremarku2access_flags记录字段的访问权限。u2name_indexconstant_pool中的索引,CONSTANT_Utf8_info类型。指定字段的名称。u2descriptor_indexconstant_pool中的索引,CONSTANT_Utf8_info类型,指定字段的描述符(见附录C)。u2attributes_countattributes包含的项目数。attribute_infoattributes[attributes_count]
0x00 02: 访问标志为private(自行搜索字段访问标志)0x00 02: 字段名称索引为#2,对应的是 Methodref #19.#58 // com/youge/api/ByteCodeClassKen.init:()V0x001D: 描述符索引为#29,对应的是 Utf8 userService0x001E :描述符索引为#30,对应的是 Utf8 Lcom/youge/service/user/UserService;0x00 00 :属性表数量为0,因此没有属性表。
3.9 方法我们只有一个方法testMethod,按照道理应该前2个字节是0001。通过查找发现是0x00 02。这是什么原因,这代表着有2个方法呢?且继续看……上图是一张方法表结构图,按照这个图我们分析下面的字节码:
typedescriptorremarku2access_flags记录方法的访问权限。见2.9.1u2name_indexconstant_pool中的索引,CONSTANT_Utf8_info类型。指定方法名称。u2descriptor_indexconstant_pool中的索引,CONSTANT_Utf8_info类型,指定方法的描述符(见附录C)。u2attributes_countattributes包含的项目数。attribute_infoattributes[attributes_count]字段中包含的Attribute集合。见2.9.2-2.9.11
第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 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00001344 00 23 00 00 00 3B 00 01 00 01 00 00 00 09 2A B7 # ; *?00001360 00 01 2A B7 00 02 B1 00 00 00 02 00 24 00 00 00 *? ? $ 00001376 0E 00 03 00 00 00 12 00 04 00 13 00 08 00 14 00 00001392 25 00 00 00 0C 00 01 00 00 00 09 00 26 00 27 00 % & ' 00001408 00 00 02 00 28 00 22 00 01 00 23 00 00 00 63 00 ( " # c 00001424 03 00 01 00 00 00 20 2A B4 00 03 9A 00 1B B2 00 *? ? ?00001440 04 12 05 B6 00 06 2A BB 00 07 59 B7 00 08 B5 00 ? *? Y? ?00001456 09 2A 04 B5 00 03 B1 00 00 00 03 00 24 00 00 00 * ? ? $ 00001472 16 00 05 00 00 00 17 00 07 00 18 00 0F 00 19 00 00001488 1A 00 1A 00 1F 00 1C 00 25 00 00 00 0C 00 01 00 % 00001504 00 00 20 00 26 00 27 00 00 00 29 00 00 00 03 00 & ' ) 00001520 01 1F 00 01 00 2A 00 2B 00 02 00 23 00 00 00 6C * + # l00001536 00 02 00 02 00 00 00 28 BB 00 0A 59 B7 00 0B 4C (? Y? L00001552 2B 12 0C B6 00 0D 2B 10 0C B8 00 0E B6 00 0F 2B + ? + ? ? +00001568 12 10 B6 00 11 2A B4 00 09 2B B9 00 12 02 00 B0 ? *? +? ?
0x0023:名称索引为#35("Code")。0x0000 003B: 属性长度为59字节。那么接下来解析一个Code属性表,按照下图解析Code Attributetypedescriptorremarku2attribute_name_indexconstant_pool中的索引,CONSTANT_Utf8_info类型。指定Attribute的名称("Code")。u4attribute_length该Attribute内容的字节长度。u2max_stack该方法操作栈的最大深度。u2max_locals该方法调用时需要分配的局部变量的最大个数,包括该方法的参数。u4code_length该方法字节码长度(以字节为单位)u1code[code_length]存放字节码数组(字节码如何解析?)。u2exception_table_length异常表的长度。exception_table_info每个表项记录一段异常处理代码信息和范围。u2start_pc记录应用该项异常处理的起始字节码。在字节码数组中的起始索引号[start_pc, end_pc)。索引号必须是opcode(一条指令的开始位置)对应的位置。u2end_pcu2handler_pc记录该项异常处理代码的开始地址。在字节码数组中的开始索引号。索引号必须是opcode对应的位置。u2catch_typeconstant_pool中的索引,CONSTANT_Class_info类型。指定该项能捕获的异常类(或其子类)。或0用于实现finally语法(即不管什么类型都会捕获。)exception_table[exception_table_length]u2attributes_countattributes包含的项目数。attribute_infoattributes[attributes_count]xxx前面6个字节(名称索引2字节+属性长度4字节)已经解析过了,所以接下来就是解析剩下的59-6=53字节即可。0x00 01: max_stack=10x00 01: max_locals=10x0000 0009: code_length=90x2A 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_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: aload_05: invokespecial #2 // Method init:()V8: return
接下来顺着Code属性表继续解析下去:0x00 00 : exception_table_length=00x00 02 : attributes_count=2(Code属性表内部还含有2个属性表)0x00 24: 第一个属性表是"LineNumberTable"
0x00 00 00 0E : "属性长度为14″0x00 03 :line_number_table_length=3line_number_table是一个数量为line_number_table_length,类型为line_number_info的集合,line_number_info表包括了start_pc和line_number两个u2类型的数据项,前者是字节码行号,后者是Java源码行号0x00 00 : start_pc=00x00 12 : end_pc=180x00 04 : start_pc=40x00 13 : end_pc=19
0x00 25 第二个属性表是:"LocalVariableTable"
0x00 0000 0c:属性长度为120x00 01 : local_variable_table_length=1然后按照local_variable_info表结构进行解析:0x00 00 : start_pc=00x00 09:length=90x0026 : #38 name_index="this"0x0027 : #39 descriptor_index #13 ("Lcom/youge/api/ByteCodeClassKen;")0000 index=0-----------到这里第一个方法就解析完成了--------------------Method(<init>)–1个属性Code表-2个属性表(LineNumberTable ,LocalVariableTable)
3.10 Attributeattributes数组记录了和类或接口相关的所有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=20x0038 : sourcefile_index = #56("ByteCodeClassKen.java")SourceFile属性用来记录生成该Class文件的源码文件名称。
4. 直接使用 javap 反编译成可读的文本型字节码javap -verbose xxx.class
Classfile /D:/xampp/htdocs/java/mvn-local-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_SUPERConstant 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"