Java位元組碼結構剖析三:方法表
這裡給大家介紹一款位元組碼分析小工具——jclasslib bytecode viewer。它可以將位元組碼檔案結構化的展現給我們看。
緊接著上篇『欄位表』的分析。後面的分析輪到了『方法表』。
方法表結構
- u2 method_count:方法計數器,methods_count 的值表示當前 class 檔案 methods[]陣列的成員個數。
- method_info methods[methods_count]: 方法表,methods[]陣列中的每個成員都必須是一個 method_info 結構的資料項,用於表示當前類或介面中某個方法的完整描述。
- method_info 結構:
1 2 3 4 5 6 7 |
attribute_info attributes[attribute_count]
|
方法表的具體解析
知道方法表的組成結構,我們就可以直接對照著位元組碼檔案去解析了。依然是前文用的java程式碼示例產生的位元組碼檔案。緊接在『欄位表』後面的16進位制是0×0004=4。即該類有4個成員方法!看原始碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
原始碼中,我們只定義了2個成員方法!但是位元組碼卻說有4個方法。我們用jclasslib小工具開啟看一下。展示如下:
這樣就一目瞭然了,其實位元組碼裡除了我們自己顯示定義的2個方法 main
和 setX
。Java編譯器生成位元組碼的時候預設又幫我們生成了2個方法 <init>
和 <clinit>
。
<init>方法就是預設的構造方法。我們知道,一個類必須要有至少一個構造方法,用來完成類的例項化過程。當我們沒有顯示去給一個類定義一個構造方法時,Java編譯器在為生成位元組碼檔案時,會預設給它生成一個預設的構造方法。
<clinit>方法是類的構造器。是類初始化階段要執行的方法,它的職責就是為類的靜態變數賦初始值(由程式設計師定義的那個初始值,在我們的原始碼中就是靜態變數in的初始值10),或者如果類中有靜態程式碼塊,那就並按順序執行靜態程式碼塊的程式碼。
所以,當一個類有靜態變數或者靜態程式碼塊的時候,Java編譯器會為這個類的位元組碼裡生成一個<clinit>方法,在類初始化階段去執行!
這就是為什麼程式碼中我們只定義了2個方法,但生成的位元組碼裡卻有4個方法的原因了。
分析第一個方法。先看看方法表的部分16進位制的資訊,如下:從0×004開始。
首先是方法的access_flag(訪問標誌位),即0×0001。說明此方法是public。接著是方法的name_index(指向常量池的索引,代表方法的全限定名稱),0×0011=17。我藉助jclasslib小工具可以查到方法名稱是<init>,就是Java編譯器預設生成的構造方法。然後,是該方法的描述符資訊descriptor_index,0×0012=18,同樣可以查到()V。這個描述符說明我們的方法是無參的『()』。且無返回值『V』。完美符合我們構造方法的定義。
屬性表分析
我們再看看它的屬性個數,attributes_count的項的值表示這個方法的附加屬性的數量。0×0001=1,說明這個方法只有一個附加屬性。那後面就是對屬性表的分析了。我們先看一下屬性表的結構:
1 2 3 4 5 |
|
所以,0×0013=19。表示的就是這個屬性的名字在常量池中的索引。查閱得,該屬性的名字是『Code』。Code屬性很重要,因為Java程式方法中的程式碼經過javac編譯之後形成位元組碼存在了Code屬性內。在這裡,我們通過jclasslib先檢視一下Code屬性裡有什麼。
紅框裡的助記符就是<init>
方法裡要執行的程式碼邏輯!
Code屬性
- code屬性的作用是儲存該方法的結構,如所對應的位元組碼。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
- Java程式方法體中的程式碼經過Java編譯處理後,最終變成位元組碼指令儲存在Code屬性中。Code屬性出現在方法表的屬性集合中,但是並非所有方法都有這個屬性。例如介面或者類中的抽象方法就不存在Code屬性。
Code屬性其實是一個結構比較複雜的屬性表。這裡就不做過多描述,打算後面抽個時間用一篇部落格來說說它。其實方法表的<init>方法到此,分析得差不多了。接下來,大家可以再對照一遍,自己去把每個方法都分析一遍,加深印象。
歡迎工作一到五年的Java工程師朋友們加入Java程式設計師開發: 854393687
群內提供免費的Java架構學習資料(裡面有高可用、高併發、高效能及分散式、Jvm效能調優、Spring原始碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!