空手套白狼,硬閱java位元組碼class檔案
如下,是一些java位元組碼也就是原始的class檔案,當應用部署到線上之後,我們能夠看到的也就是這樣的字樣了。那麼怎樣解呢?就讓我們一起,來解讀解讀位元組碼吧!
Offset0123456789ABCDEF 00000000CA FE BA BE 00 00 00 3400 6A 0A 00 1C 00 39 0A漱壕4 j9 0000001000 13 00 3A 09 00 13 003B 09 00 3C 00 3D 08 00:;< = 000000203E 0A 00 3F 00 40 07 0041 0A 00 07 00 39 09 00>? @A9 0000003013 00 42 07 00 43 0A 000A 00 39 08 00 44 0A 00BC9D 000000400A 00 45 0A 00 46 00 470A 00 0A 00 48 08 00 49EF GHI 000000500A 00 0A 00 4A 0B 00 4B00 4C 07 00 4D 0A 00 13JK LM 0000006000 39 0A 00 13 00 4E 0700 4F 0A 00 16 00 39 089NO9 0000007000 50 0A 00 16 00 51 0A00 16 00 52 0A 00 16 00PQR 0000008053 07 00 54 01 00 0B 7573 65 72 53 65 72 76 69STuserServi 0000009063 65 01 00 24 4C 63 6F6D 2F 79 6F 75 67 65 2Fce$Lcom/youge/ 000000A073 65 72 76 69 63 65 2F75 73 65 72 2F 55 73 65service/user/Use 000000B072 53 65 72 76 69 63 653B 01 00 09 69 6E 69 74rService;init 000000C069 61 6C 65 64 01 00 015A 01 00 06 3C 69 6E 69ialedZ<ini 000000D074 3E 01 00 03 28 29 5601 00 04 43 6F 64 65 01t>()VCode
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等等,這個網上可以搜尋到。
按照上面的表結構解析得到下面資訊:
Offset0123456789 10 11 12 13 14 15 0000134400 23 00 00 00 3B 00 0100 01 00 00 00 09 2A B7#;*? 0000136000 01 2A B7 00 02 B1 0000 00 02 00 24 00 00 00*? ?$ 000013760E 00 03 00 00 00 12 0004 00 13 00 08 00 14 00 0000139225 00 00 00 0C 00 01 0000 00 09 00 26 00 27 00%& ' 0000140800 00 02 00 28 00 22 0001 00 23 00 00 00 63 00( "#c 0000142403 00 01 00 00 00 20 2AB4 00 03 9A 00 1B B2 00*? ? ? 0000144004 12 05 B6 00 06 2A BB00 07 59 B7 00 08 B5 00? *? Y? ? 0000145609 2A 04 B5 00 03 B1 0000 00 03 00 24 00 00 00* ? ?$ 0000147216 00 05 00 00 00 17 0007 00 18 00 0F 00 19 00 000014881A 00 1A 00 1F 00 1C 0025 00 00 00 0C 00 01 00% 0000150400 00 20 00 26 00 27 0000 00 29 00 00 00 03 00& ') 0000152001 1F 00 01 00 2A 00 2B00 02 00 23 00 00 00 6C* +#l 0000153600 02 00 02 00 00 00 28BB 00 0A 59 B7 00 0B 4C(? Y? L 000015522B 12 0C B6 00 0D 2B 100C B8 00 0E B6 00 0F 2B+? +? ? + 0000156812 10 B6 00 11 2A B4 0009 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 = Utf8userService #30 = Utf8Lcom/youge/service/user/UserService; #31 = Utf8initialed #32 = Utf8Z #33 = Utf8<init> #34 = Utf8()V #35 = Utf8Code #36 = Utf8LineNumberTable #37 = Utf8LocalVariableTable #38 = Utf8this #39 = Utf8Lcom/youge/api/ByteCodeClassKen; #40 = Utf8init #41 = Utf8StackMapTable #42 = Utf8addUser #43 = Utf8()Ljava/lang/Integer; #44 = Utf8userInfo #45 = Utf8Lcom/youge/pojo/user/UserInfo; #46 = Utf8Exceptions #47 = Class#85// java/lang/Exception #48 = Utf8main #49 = Utf8([Ljava/lang/String;)V #50 = Utf8args #51 = Utf8[Ljava/lang/String; #52 = Utf8classKen #53 = Utf8affect #54 = Utf8Ljava/lang/Integer; #55 = Utf8SourceFile #56 = Utf8ByteCodeClassKen.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 = Utf8init... #63 = Class#89// java/io/PrintStream #64 = NameAndType#90:#91// println:(Ljava/lang/String;)V #65 = Utf8com/youge/service/user/UserServiceImpl #66 = NameAndType#29:#30// userService:Lcom/youge/service/user/UserService; #67 = Utf8com/youge/pojo/user/UserInfo #68 = Utf8lilei #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 = Utf8new 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 = Utf8com/youge/api/ByteCodeClassKen #78 = NameAndType#42:#43// addUser:()Ljava/lang/Integer; #79 = Utf8java/lang/StringBuilder #80 = Utf8affect: #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 = Utf8java/lang/Object #85 = Utf8java/lang/Exception #86 = Utf8java/lang/System #87 = Utf8out #88 = Utf8Ljava/io/PrintStream; #89 = Utf8java/io/PrintStream #90 = Utf8println #91 = Utf8(Ljava/lang/String;)V #92 = Utf8setName #93 = Utf8java/lang/Integer #94 = Utf8valueOf #95 = Utf8(I)Ljava/lang/Integer; #96 = Utf8setAge #97 = Utf8(Ljava/lang/Integer;)V #98 = Utf8setAddress #99 = Utf8com/youge/service/user/UserService #100 = Utf8(Lcom/youge/pojo/user/UserInfo;)Ljava/lang/Integer; #101 = Utf8append #102 = Utf8(Ljava/lang/String;)Ljava/lang/StringBuilder; #103 = Utf8(Ljava/lang/Object;)Ljava/lang/StringBuilder; #104 = Utf8toString #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: StartLengthSlotNameSignature 090thisLcom/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: bipush12 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: StartLengthSlotNameSignature 0400thisLcom/youge/api/ByteCodeClassKen; 8321 userInfoLcom/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: StartLengthSlotNameSignature 0390args[Ljava/lang/String; 8311 classKenLcom/youge/api/ByteCodeClassKen; 13262 affectLjava/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"