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文件的文本化閱讀

   https://blog.csdn.net/kcstrong/article/details/81233672

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章