1. 程式人生 > >Java中的類檔案結構之一:如何分析一個.class檔案的二進位制碼內容

Java中的類檔案結構之一:如何分析一個.class檔案的二進位制碼內容

該文為作者原創,請轉載者註明出處

以下為一個Java類--Temp4Test

package com.demo;



public class Temp4Test extends Temp3Test {



    private int i = 1;

    public float f;

    public static String thisstr = "";



    public Temp4Test(int ii, String str, float ff) {

        i = ii;

        thisstr = str;

        f = ff;

    }



    public static void main(String[] args) {
    
        Temp4Test t4 = new Temp4Test(100, "hello", 5.5f);

        System.out.println(t4);

    }



    @Override

    public String toString() {

        return "[" + i + " , " + thisstr + " , " + f + "]";

    }

}

其父類Temp3Test.java的實現為:

package com.demo;



public class Temp3Test implements Cloneable {



    private int ii;

    public static String str = "hi";



    public static void main(String[] args) {

        int i = 123;

    }

}

所生成的Temp4Test.class的二進位制檔案為:

感興趣的同學可以自己編譯下就可以看到上述結果。

下面逐位分析一下二進位制檔案中各位的含義

a.象徵是.class檔案的魔數:頭4個Byte,看到這4個Byte就可以基本確認為一個.class檔案,固定值:0xCAFEBABE。

b.class檔案版本號,第5、6個位元組是次版本號,第7、8個位元組為主版本號,在上述二進位制碼為0x00000034,代表版本號為52.0即JDK1.8.0

c.常量池,從0x0052開始(含0x0052)

    c.1 首先的兩個位元組為常量池中所含常量的數量,本例中即為0x0052,所代表的數為:0x0052==5*16+2-1==81(換為十進位制為81個常量)

    c.2 從0x07開始為1-81個常量的二進位制位表,開始位如下圖所示:

    

    常量池共有14種類型的常量,如下圖所示,截圖來源於《深入理解Java虛擬機器》

                       

    每一個常量,第一位均為該常量型別,即上述表中14項之一,以第一個常量為例0x07表示類或介面的符號引用,即CONSTANT_Class_info,此型別結構為:

    

因此第一個常量即為0x070002,0x0002是一個索引,表示指向第二個常量,第二個常量型別為0x01,為下圖所示二進位制位

    

0x01表示(上面有表)CONSTANT_Utf8_info,即字串,CONSTANT_Utf8_info型別的常量結構為下圖:

    

可知,第二個常量對應的二進位制碼為:0x010012(共計1*16+2=18位)636F6D2F64656D6F2F54656D703454657374,後面的字串所對應的文字為:com/demo/Temp4Test,以此類推,可以推出上述二進位制檔案中的所有常量池常量,以下是我手寫的一個推導圖,有點粗漏,^_^

以下為14種常量項的結構總表

d.訪問標誌,緊挨著常量池的兩個位元組,含義如下表:

在上述示例中為:

0x0021==0x0001|0x0020表示是一個由使用者定義的public的類

e.接下來的二進位制表示該類的繼承關係,共有三項內容:

    e.1 類索引,兩個位元組,常量池中的對本類的描述

    e.2 父類索引,兩個位元組,常量池中對父類的描述

    e.3 介面索引,頭兩個位元組表示介面數,然後,緊跟進介面列表

    本例中的二進位制碼為:

    

    0x0001表示常量池中第一個常量為類索引,0x0003為表示常量池中第三個常量為父類索引,0x0000表示實現的介面個數為0個

f.接著的二進位制表示欄位表,頭兩位為個數,從上圖可以看出,有三個欄位(0x0003),每個欄位規則如下圖

    

    f.1 access_flags的取值如下圖

    

    f.2 name_index的含義,同上,對映至常量池中的常量索引

    f.3 descriptor_index,描述符,表示該變數的型別,此處插一下描述符的表示規則:

        f.3.* 描述符中的字元含義如下:

        

        f.3.** 表示陣列時,每一個維度前用“[”表示

        f.3.*** 描述方法,先參後返回值

        下面舉一個描述符的例子:

        上面的例子中,Temp4Test.java類中的建構函式的返回值及引數描述為:(ILjava/lang/String;F)V

    本例中的欄位表,三個欄位分別為:

    0x0002(private)00050006(檢視第五常量、第六常量)0000

    0x0001(public)00070008(檢視第七常量、第八常量)0000

    0x0009(0x0001|0x0008 public static)0009000A(檢視第九常量、第十常量)0000

g.接著的二進位制表示方法表,頭兩位為個數,從上圖可以看出,有四個欄位(0x0004),每個方法表述規則如下圖

    

    g.1 access_flags的含義與欄位表有所差別,具體如下圖:

    

    其他的name_index、descriptor_index同欄位表

    g.2 在本例中,方法1-方法4的二進位制塊如下圖:

    方法1:

    

    兩個紅色豎線中間的部分

    方法2:

    

    方法3:

    

    方法4:

    

    g.3 下面找一個方法來說明一下方法的二進位制怎麼看,以方法2為例

    0x0001 -- public

    0x0014 -- 第二十個常量,方法名

    0x0015 -- 第二十一個常常,方法描述

    0x0001 -- 含有一個屬性表

    後面一直到方法結束均為屬性表的二進位制內容

    g.4 屬性是一個特別複雜的二進位制規則,之後會寫一篇文章專門說述一下屬性表的讀法,在此不一一展開描述,只說一下當前例子方法(即方法2)中的屬性表讀取規則

        0x000D -- 第十三個常量,查常量表,可知,表示該屬性為Code,Code的屬性規則為:

        

        attribute_name_index,即0x000D,表示Code

    attribute_length,表示該屬性所佔位元組數(不包括attribute_name_index和attribute_length),方法2中為0x00000074==7*16+4==116    

        max_stack,最大堆疊數為0x0002

        max_locals,最大臨時變數數為0x0004

        code_length,位元組碼數量為0x00000018 == 16+8 == 24Byte

        code,如下圖:

        

        位元組碼的閱讀不在該文中討論

        exception_table_length,0x0000,沒有

        attributes_count,0x0002,下面內嵌了兩個屬性表,內嵌的屬性表的讀取方式同上面所述,和外層的讀取規則是一樣的,只是屬性不為Code了。因屬性表的全部分類並未羅列,也就不再讀了從0x0012開始直到方法2結束,均為這兩個屬性表的二進位制碼。

h.方法表的二進位制碼結束後,對於本例來講,基本二進位制碼就進入最後了,還剩一點是SourceFile的屬性,讀法,如下:

        

    對應的二進位制為:

    0x0050,第八十位常量--SourceFile

    0x00000002,後面還有兩位,這個值在SourceFile型別中是定值,只有2位,原因就是上圖的規則定義

    0x0051,第八十一位常量

結語:終於把類的二進位制檔案讀完了,當然,上述例子比較簡單,但麻雀雖小五臟俱全,複雜的檔案只是多了一些其他類別,讀法是和上述例子一致的。上面有一些內容將在後續文章中繼續詳述,會有一篇文章專門講述文字化的常量池是如何讀的,還會有一篇文章講述屬性表,再有一篇文章詳述位元組碼,文中的截圖來源於《深入理解Java虛擬機器》,在此向作者致以最深的敬意,同時,圖片若侵權,請聯絡我,將第一時間刪除。

比二進位制更方便的是文字形態的閱讀方法,詳見第二篇:Java中的類檔案結構之二:分析一個.class檔案的文字化閱讀