Java class文件結(jié)構(gòu)(實例篇)

?在《JAVA CLASS的文件結(jié)構(gòu)(規(guī)范篇)》中我們基本完整的描述了class的文件結(jié)構(gòu)規(guī)范浪漠,這一篇我們以一個實際的例子,來逐行分析class的二進(jìn)制格式藻肄,從而深入了解class文件結(jié)構(gòu)木柬。
?分析代碼如下:

package com.sunny.jdk.classfile;

/**
 * <Description> <br>
 *
 * @author Sunny<br>
 * @version 1.0<br>
 * @taskId: <br>
 * @createDate 2018/09/17 15:52 <br>
 * @see com.sunny.jdk.classfile <br>
 */
public class TestClass {
    private int m;

    public int inc() {
        return m + 1;
    }
}

?class文件使用winhex打開的二進(jìn)制結(jié)構(gòu)如下:



下面我們就根據(jù)前篇所說的Class的文件結(jié)構(gòu)來解析以下上圖中字節(jié)流:

1. 魔數(shù)

?從Class的文件結(jié)構(gòu)我們知道缰雇,剛開始的4個字節(jié)是魔數(shù),上圖中從地址000000000-000000003的內(nèi)容就是魔數(shù)追驴,從上圖可知Class的文件的魔數(shù)是0xCAFEBABE械哟。

2. 主副版本號

?接下來的4個字節(jié)是主副版本號,有上圖可知從000000004-000000005對應(yīng)的是0x0000,因此Class的minor_version 為0x0000,從000000006-000000007對應(yīng)的內(nèi)容為0x0034,因此Class文件的major_version版本為 0x0034,這正好就是jdk1.8編譯后的Class對應(yīng)的主次版本殿雪。

3. 常量池數(shù)量

?接下來的2個字節(jié)從000000008-000000009表示常量池的數(shù)量暇咆,由上圖可以知道其值為0x0016,十進(jìn)制為22個,但是對于常量池的數(shù)量 需要明確一點丙曙,常量池的數(shù)量是constant_pool_count-1爸业,為什么減一,是因為索引0表示class中的數(shù)據(jù)項不引用任何常量池中的常 量亏镰。所以常量池的個數(shù)應(yīng)該是21個扯旷;

4. 常量池

?前篇已經(jīng)說過了常量池中有不同類型的常量,下面就來看看TestClass.class的第一個常量索抓,每個常量都有一個u1類型的tag標(biāo)識來表示 常量的類型钧忽,上圖中00000000A處的內(nèi)容為0x0A,轉(zhuǎn)換成二級制是10逼肯,有上面的關(guān)于常量類型的描述可知tag10的常量是Constant_Methodref_info,而Constant_Methodref_info的結(jié)構(gòu)如下:

CONSTANT_Methodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

?其中tag對應(yīng)上圖位置00000000A處的內(nèi)容為0x0A耸黑;class_index是接下來兩個字節(jié)的數(shù)據(jù),對應(yīng)的上圖位置的00000000B-00000000C篮幢,其值為0x0004大刊,也就是說指向第四個常量泉沾;name_and_type_index指向常量池中類型為CONSTANT_NameAndType_info常量捎稚。從上圖可以看出name_and_type_index的值為0x0012,對應(yīng)上圖的位置是00000000D-00000000E表示指向常量池中的第18個常量紊册。接下來我們可以找到常量池中所有的常量:

  • 第2個常量:
    tag對應(yīng)00000000F位置的內(nèi)容是0x09赋续,表示CONSTANT_Fieldref_info男翰,其結(jié)構(gòu)如下:
CONSTANT_Fieldref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

?其中tag對應(yīng)上圖位置00000000F處的內(nèi)容為0x09;class_index是接下來兩個字節(jié)的數(shù)據(jù)纽乱,對應(yīng)的上圖位置的000000100-000000101蛾绎,其值為0x0003,也就是說指向第3個常量鸦列;name_and_type_index指向常量池中類型為CONSTANT_NameAndType_info常量租冠。從上圖可以看出name_and_type_index的值為0x0013,對應(yīng)上圖的位置是000000102-000000103表示指向常量池中的第19個常量薯嗤。

  • 第3個常量:
    tag對應(yīng)000000104位置的內(nèi)容是0x07顽爹,表示CONSTANT_Class_info,其結(jié)構(gòu)如下:
CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}

?其中tag對應(yīng)上圖位置000000104處的內(nèi)容為0x07骆姐;name_index是接下來兩個字節(jié)的數(shù)據(jù)镜粤,對應(yīng)的上圖位置的000000105-000000106捏题,其值為0x0014,也就是說指向第20個常量肉渴;

  • 第4個常量:
    tag對應(yīng)000000107位置的內(nèi)容是0x07公荧,表示CONSTANT_Class_info,其結(jié)構(gòu)如下:
CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}

?其中tag對應(yīng)上圖位置000000107處的內(nèi)容為0x07同规;name_index是接下來兩個字節(jié)的數(shù)據(jù)循狰,對應(yīng)的上圖位置的000000108-000000109,其值為0x0015券勺,也就是說指向第21個常量绪钥;

  • 第5個常量:
    tag對應(yīng)00000010A位置的內(nèi)容是0x01,表示CONSTANT_Utf8_info朱灿,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置00000010A處的內(nèi)容為0x01昧识;length是接下來兩個字節(jié)的數(shù)據(jù),對應(yīng)的上圖位置的00000010B-00000010C盗扒,其值為0x0001跪楞,也就是length為1;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù)侣灶,因為length=1甸祭,所以00000010D處的數(shù)據(jù)0x6D表示的即為該數(shù)據(jù),轉(zhuǎn)換成ASCII即為m褥影;

  • 第6個常量:
    tag對應(yīng)00000010E位置的內(nèi)容是0x01池户,表示CONSTANT_Utf8_info,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置00000010E處的內(nèi)容為0x01凡怎;length是接下來兩個字節(jié)的數(shù)據(jù)校焦,對應(yīng)的上圖位置的00000010F-000000200,其值為0x0001统倒,也就是length為1寨典;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù),因為length=1房匆,所以000000201處的數(shù)據(jù)0x49表示的即為該數(shù)據(jù)耸成,轉(zhuǎn)換成ASCII即為I

  • 第7個常量:
    tag對應(yīng)000000202位置的內(nèi)容是0x01浴鸿,表示CONSTANT_Utf8_info井氢,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置000000202處的內(nèi)容為0x01;length是接下來兩個字節(jié)的數(shù)據(jù)岳链,對應(yīng)的上圖位置的000000203-000000204花竞,其值為0x0006,也就是length為6掸哑;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù)约急,因為length=6寇仓,所以000000205-00000020A處的數(shù)據(jù)0x3C, 0x69,0x6E,0x69, 0x74, 0x3E表示的即為該數(shù)據(jù),轉(zhuǎn)換成ASCII即為<init>烤宙;

  • 第8個常量:
    tag對應(yīng)00000020B位置的內(nèi)容是0x01,表示CONSTANT_Utf8_info俭嘁,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置00000020B處的內(nèi)容為0x01躺枕;length是接下來兩個字節(jié)的數(shù)據(jù),對應(yīng)的上圖位置的00000020C-00000020D供填,其值為0x0003拐云,也就是length為3;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù)近她,因為length=3叉瘩,所以00000020E-000000300處的數(shù)據(jù)0x28, 0x29,0x56表示的即為該數(shù)據(jù),轉(zhuǎn)換成ASCII即為()V粘捎;

  • 第9個常量:
    tag對應(yīng)000000301位置的內(nèi)容是0x01薇缅,表示CONSTANT_Utf8_info,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置000000301處的內(nèi)容為0x01攒磨;length是接下來兩個字節(jié)的數(shù)據(jù)泳桦,對應(yīng)的上圖位置的000000302-000000303,其值為0x0004娩缰,也就是length為4灸撰;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù),因為length=4拼坎,所以000000304-000000307處的數(shù)據(jù)0x43, 0x6F,0x64,0x65表示的即為該數(shù)據(jù)浮毯,轉(zhuǎn)換成ASCII即為Code

  • 第10個常量:
    tag對應(yīng)000000308位置的內(nèi)容是0x01泰鸡,表示CONSTANT_Utf8_info债蓝,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置000000308處的內(nèi)容為0x01;length是接下來兩個字節(jié)的數(shù)據(jù)鸟顺,對應(yīng)的上圖位置的000000309-00000030A惦蚊,其值為0x000F,也就是length為15讯嫂;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù)蹦锋,因為length=15,所以00000030B-000000409處的數(shù)據(jù)0x4C, 0x69,0x6E,0x65,0x4E,0x75,0x6D,0x62,0x65,0x72,0x54,0x61,0x62,0x6C,0x65表示的即為該數(shù)據(jù)欧芽,轉(zhuǎn)換成ASCII即為LineNumberTable莉掂;

  • 第11個常量:
    tag對應(yīng)00000040A位置的內(nèi)容是0x01,表示CONSTANT_Utf8_info千扔,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置00000040A處的內(nèi)容為0x01憎妙;length是接下來兩個字節(jié)的數(shù)據(jù)库正,對應(yīng)的上圖位置的000000409-00000040A,其值為0x0012厘唾,也就是length為18褥符;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù),因為length=18抚垃,所以00000040B-00000050E處的數(shù)據(jù)0x4C,0x6F,0x63,0x61,0x6C,0x56,0x61,0x72,0x69,0x61,0x62,0x6C,0x65,0x54,0x61,0x62,0x6C,0x65表示的即為該數(shù)據(jù)喷楣,轉(zhuǎn)換成ASCII即為LocalVariableTable

  • 第12個常量:
    tag對應(yīng)00000050F位置的內(nèi)容是0x01鹤树,表示CONSTANT_Utf8_info铣焊,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置00000050F處的內(nèi)容為0x01;length是接下來兩個字節(jié)的數(shù)據(jù)罕伯,對應(yīng)的上圖位置的000000600-000000601曲伊,其值為0x0004,也就是length為4追他;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù)坟募,因為length=4,所以000000602-000000605處的數(shù)據(jù)0x74,0x68,0x69,0x73表示的即為該數(shù)據(jù)邑狸,轉(zhuǎn)換成ASCII即為this婿屹;

  • 第13個常量:
    tag對應(yīng)000000606位置的內(nèi)容是0x01,表示CONSTANT_Utf8_info推溃,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置000000606處的內(nèi)容為0x01昂利;length是接下來兩個字節(jié)的數(shù)據(jù),對應(yīng)的上圖位置的000000607-000000608铁坎,其值為0x0023蜂奸,也就是length為35;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù)硬萍,因為length=35扩所,所以000000609-00000080B處的數(shù)據(jù)如圖:


,轉(zhuǎn)換成ASCII即為Lcom/sunny/jdk/classfile/TestClass;朴乖;

  • 第14個常量:
    tag對應(yīng)00000080C位置的內(nèi)容是0x01祖屏,表示CONSTANT_Utf8_info,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置00000080C處的內(nèi)容為0x01买羞;length是接下來兩個字節(jié)的數(shù)據(jù)袁勺,對應(yīng)的上圖位置的00000080D-00000080E,其值為0x0003畜普,也就是length為3期丰;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù),因為length=3,所以00000080F-000000901處的數(shù)據(jù)0x69,0x6E,0x63表示的即為該數(shù)據(jù)钝荡,轉(zhuǎn)換成ASCII即為inc街立;

  • 第15個常量:
    tag對應(yīng)000000902位置的內(nèi)容是0x01,表示CONSTANT_Utf8_info埠通,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置000000902處的內(nèi)容為0x01赎离;length是接下來兩個字節(jié)的數(shù)據(jù),對應(yīng)的上圖位置的000000903-000000904端辱,其值為0x0003蟹瘾,也就是length為3;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù)掠手,因為length=3,所以000000905-000000907處的數(shù)據(jù)0x28,0x29,0x49表示的即為該數(shù)據(jù)狸捕,轉(zhuǎn)換成ASCII即為()I喷鸽;

  • 第16個常量:
    tag對應(yīng)000000908位置的內(nèi)容是0x01,表示CONSTANT_Utf8_info灸拍,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置000000908處的內(nèi)容為0x01做祝;length是接下來兩個字節(jié)的數(shù)據(jù),對應(yīng)的上圖位置的000000909-00000090A鸡岗,其值為0x000A混槐,也就是length為10;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù)轩性,因為length=10声登,所以00000090B-000000A04處的數(shù)據(jù)如圖:


,轉(zhuǎn)換成ASCII即為SourceFile揣苏;

  • 第17個常量:
    tag對應(yīng)000000A05位置的內(nèi)容是0x01悯嗓,表示CONSTANT_Utf8_info,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置000000A05處的內(nèi)容為0x01卸察;length是接下來兩個字節(jié)的數(shù)據(jù)脯厨,對應(yīng)的上圖位置的000000A06-000000A07,其值為0x000E坑质,也就是length為14合武;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù),因為length=14涡扼,所以000000A08-000000B05處的數(shù)據(jù)如圖:


稼跳,轉(zhuǎn)換成ASCII即為TestClass.java

  • 第18個常量:
    tag對應(yīng)000000B06位置的內(nèi)容是0x0C吃沪,表示CONSTANT_NameAndType_info岂贩,其結(jié)構(gòu)如下:
CONSTANT_NameAndType_info {
    u1 tag;
    u2 name_index; 
    u2 descriptor_index;
}

?其中tag對應(yīng)上圖位置000000B06處的內(nèi)容為0x0C;name_index是接下來兩個字節(jié)的數(shù)據(jù),對應(yīng)的上圖位置的000000B07-000000B08萎津,其值為0x0007卸伞,也就是說指向第7個常量;descriptor_index是接下來的2個字節(jié)锉屈,從上圖可以看出descriptor_index的值為0x0008荤傲,對應(yīng)上圖的位置是000000B09-000000B0A表示指向常量池中的第8個常量。

  • 第19個常量:
    tag對應(yīng)000000B0B位置的內(nèi)容是0x0C颈渊,表示CONSTANT_NameAndType_info遂黍,其結(jié)構(gòu)如下:
CONSTANT_NameAndType_info {
    u1 tag;
    u2 name_index; 
    u2 descriptor_index;
}

?其中tag對應(yīng)上圖位置000000B0B處的內(nèi)容為0x0C;name_index是接下來兩個字節(jié)的數(shù)據(jù)俊嗽,對應(yīng)的上圖位置的000000B0C-000000B0D雾家,其值為0x0005,也就是說指向第5個常量绍豁;descriptor_index是接下來的2個字節(jié)芯咧,從上圖可以看出descriptor_index的值為0x0006,對應(yīng)上圖的位置是000000B0E-000000B0F表示指向常量池中的第6個常量竹揍。

  • 第20個常量:
    tag對應(yīng)000000C00位置的內(nèi)容是0x01敬飒,表示CONSTANT_Utf8_info,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置000000C00處的內(nèi)容為0x01芬位;length是接下來兩個字節(jié)的數(shù)據(jù)无拗,對應(yīng)的上圖位置的000000C01-000000C02,其值為0x0021昧碉,也就是length為33英染;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù),因為length=33被饿,所以000000C03-000000E03處的數(shù)據(jù)如圖:


税迷,轉(zhuǎn)換成ASCII即為com/sunny/jdk/classfile/TestClass

  • 第21個常量:
    tag對應(yīng)000000E04位置的內(nèi)容是0x01锹漱,表示CONSTANT_Utf8_info箭养,其結(jié)構(gòu)如下:
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

?其中tag對應(yīng)上圖位置000000E04處的內(nèi)容為0x01;length是接下來兩個字節(jié)的數(shù)據(jù)哥牍,對應(yīng)的上圖位置的000000E05-000000E06毕泌,其值為0x0010,也就是length為16嗅辣;緊接著后面length個字節(jié)表示的數(shù)據(jù)即為該數(shù)據(jù)撼泛,因為length=16,所以000000E07-000000F06處的數(shù)據(jù)如圖:


澡谭,轉(zhuǎn)換成ASCII即為java/lang/Object愿题;

?這樣常量池的21個常量全部分析完畢,不過JDK提供了一個方便的工具可以讓我們查看常量池中所包含的常量。通過javap -verbose TestClass 即可得到所有常量池中的常量潘酗,如下所示:

C:\ProgramFiles\Java\jdk1.8.0_144\bin>javap -verbose E:\workspace\workspace_java_tool\workspace_sunny_project\java-honey-collection\target\classes\com\sunny\jdk\classfile\TestClass.class
Classfile /E:/workspace/workspace_java_tool/workspace_sunny_project/java-honey-collection/target/classes/com/sunny/jdk/classfile/TestClass.class
  Last modified 2018-10-23; size 401 bytes
  MD5 checksum b02768ba5213ac8bcfdcd0865e8d7374
  Compiled from "TestClass.java"
public class com.sunny.jdk.classfile.TestClass
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#18         // java/lang/Object."<init>":()V
   #2 = Fieldref           #3.#19         // com/sunny/jdk/classfile/TestClass.m:I
   #3 = Class              #20            // com/sunny/jdk/classfile/TestClass
   #4 = Class              #21            // java/lang/Object
   #5 = Utf8               m
   #6 = Utf8               I
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcom/sunny/jdk/classfile/TestClass;
  #14 = Utf8               inc
  #15 = Utf8               ()I
  #16 = Utf8               SourceFile
  #17 = Utf8               TestClass.java
  #18 = NameAndType        #7:#8          // "<init>":()V
  #19 = NameAndType        #5:#6          // m:I
  #20 = Utf8               com/sunny/jdk/classfile/TestClass
  #21 = Utf8               java/lang/Object
{
  public com.sunny.jdk.classfile.TestClass();
    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: return
      LineNumberTable:
        line 12: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/sunny/jdk/classfile/TestClass;

  public int inc();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field m:I
         4: iconst_1
         5: iadd
         6: ireturn
      LineNumberTable:
        line 16: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   Lcom/sunny/jdk/classfile/TestClass;
}
SourceFile: "TestClass.java"

?由此常量池已經(jīng)全部分析完畢杆兵,接下來我們即將分access_flags。

5. u2 access_flags

?access_flags表示類或者接口方面的訪問信息仔夺,比如Class表示的是類還是接口琐脏,是否為public,static,final等缸兔。具體訪問標(biāo)示的含義之前已經(jīng)說過 了日裙,下面我們就來看看TestClass的訪問標(biāo)示。Class的訪問標(biāo)示是000000F07-000000F08惰蜜,期值為0x0021昂拂,根據(jù)前面說的 各種訪問標(biāo)示的標(biāo)志位,我們可以知道:0x0021=0x0001|0x0020 也即ACC_PUBLIC 和 ACC_SUPER為真抛猖,其中ACC_PUBLIC大家好理解格侯,ACC_SUPER是jdk1.0.2之后編譯的類都會帶有的標(biāo)志。

6. u2 this_class 和 u2 super_class

?this_class表示當(dāng)前類的索引值樟结,super_class 表示當(dāng)前類的父類的索引值;索引值所指向的常量池中類型為CONSTANT_Class_info的常量精算;例子中類索引值的是000000F09-000000F0A所表示的值0x0003瓢宦,也即常量池的第3個常量,從而可知灰羽,this_class的全限定名為com/sunny/jdk/classfile/TestClass驮履;同理,super_class是000000F0B-000000F0C所表示的值0x0004廉嚼,也即常量池的第4個常量玫镐,從而可知,super_class的全限定名為java/lang/Object怠噪;

6. u2 interfaces_count和 interfaces[interfaces_count]

?interfaces_count和 interfaces[interfaces_count]表示接口數(shù)量以及具體的每一個接口恐似;TestClass的接口數(shù)量是000000F0D-000000F0E所表示的值0x0000,也即沒有實現(xiàn)任何接口傍念;

7. u2 fields_count 和 field_info

? fields_count表示類中field_info表的數(shù)量矫夷,而field_info表示類的實例變量和類變量,這里需要注意的是 field_info不包含從父類繼承過來的字段憋槐,field_info的結(jié)構(gòu)如下:

field_info {
    u2 access_flags;
    u2 name_index;
    u2 descriptor_index;
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

?例子中接口后面緊接著兩個字節(jié)000000F0F-000001000中的0x0001即為字段的個數(shù)双藕,從而可知字段的個數(shù)為1,與代碼樣例相符阳仔;后面即為字段的詳細(xì)信息忧陪;
?access_flags 是000001001-000001002中0x0002,對照上一篇的字段修飾符表格可知字段的修飾符為ACC_PRIVATE(0x0002);name_index是000001003-000001004表達(dá)的0x0005嘶摊,表示常量池中的第5個常量延蟹,也即是m,表示字段名為m更卒;descriptor_index是000001005-000001006表達(dá)的0x0006等孵,表示常量池中的第6個常量,也I蹂空,表示int類型俯萌;attributes_count是000001007-000001008位置的0x0000,表示沒有屬性上枕;沒有屬性的話咐熙,后面就是methods_count和method_info;

8. u2 methods_count 和 method_info

?其中methods_count表示方法的數(shù)量辨萍,而method_info表示的方法表棋恼,其中方法表的結(jié)構(gòu)如下所示:

method_info {
    u2 access_flags;
    u2 name_index;
    u2 descriptor_index;
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

?methods_count是000001009-00000100A位置的0x0002,表示有2個method锈玉;

  • 第1個方法:
    access_flags是00000100B-00000100C位置的0x0001爪飘,表示ACC_PUBLIC(0x0001);name_index是00000100D-00000100E位置的0x0007拉背,表示常量池的第7個常量师崎,也即方法名為<init>;descriptor_index是00000100F-000001100位置的0x0008椅棺,也即常量池的第8個常量犁罩,也即()V;attributes_count是000001101-000001102位置的0x0001两疚,表示有1個屬性床估,后面緊跟的就是屬性,屬性通用結(jié)構(gòu)如下诱渤,不同的屬性結(jié)構(gòu)也不完全一樣:
attribute_info {
    u2 attribute_name_index;   //屬性名索引
    u4 attribute_length;       //屬性長度
    u1 info[attribute_length]; //屬性的具體內(nèi)容
}
  • <init>方法的第1個屬性:
    attribute_name_index是000001103-000001104位置的0x0009丐巫,表示常量池的第9個常量,也即Code勺美,表示該屬性是Code屬性鞋吉,Code的屬性結(jié)構(gòu)如下:
Code_attribute {
    u2 attribute_name_index; 
    u4 attribute_length;
    u2 max_stack;   
    u2 max_locals;  
    u4 code_length; 
    u1 code[code_length]; 
    u2 exception_table_length;
    {   u2 start_pc;
        u2 end_pc;
        u2 handler_pc; 
        u2 catch_type; 
    } exception_table[exception_table_length]; 
    u2 attributes_count;     
    attribute_info attributes[attributes_count]; 
}

?attribute_length(attribute_length不包括attribute_name_index和attribute_length的6個字節(jié)的長度)為000001105-000001108位置的0x0000002F,表示屬性長度為47励烦;max_stack(max_stack表示最大棧深度谓着,虛擬機(jī)在運行時根據(jù)這個值來分配棧幀中操作數(shù)的深度)為000001109-00000110A位置的0x0001,表示最大棧幀深度為1坛掠;max_locals(max_locals代表了局部變量表的存儲空間)為00000110B-00000110C位置的0x0001赊锚,表示局部變量的存儲空間為1治筒;code_length(code_length代表了字節(jié)碼指令的數(shù)量,而code表示的是字節(jié)碼指令舷蒲,從Code屬性結(jié)構(gòu)中可以知道code的類型為u1耸袜,一個u1類型的取值為0x00-0xFF,對應(yīng)的十進(jìn)制為0-255,目前虛擬機(jī)規(guī)范已經(jīng)定義了200多條指令)為00000110D-000001200位置的0x00000005牲平,表示Code字節(jié)碼指令的數(shù)量為5堤框;后面的5個字節(jié)即為字節(jié)碼,位置000001201-000001205的數(shù)據(jù)為“2A B7 00 01 B1”纵柿,翻譯如下:

  • 讀入2A蜈抓,查虛擬機(jī)規(guī)范1.8知道指令操作碼0x2A對應(yīng)的指令為aload_0,這個指令的含義是講第0個Slot中為reference類型的本地變量推送到操作數(shù)棧頂昂儒;
  • 讀入B7沟使,查表得0xB7對應(yīng)的指令為invokespecial,這條指令的作用是以棧頂?shù)膔eference類型的數(shù)據(jù)所指向的對象作為方法接受者渊跋,調(diào)用此對象的實例構(gòu)造器方法腊嗡、private方法或者它的父類的方法。這個方法有一個u2類型的參數(shù)說明具體調(diào)用哪一個方法拾酝,它指向常量池中的一個CONSTANT_Methodref_info類型常量燕少,即此方法的符號引用。
  • 讀入00 01蒿囤,這是invokespecial的參數(shù)客们,查常量池得0x0001對應(yīng)的常量為實例構(gòu)造器<init>方法的符號引用;
  • 讀入B1蟋软,查表得0xB1對應(yīng)的指令為return镶摘,含義是返回此方法嗽桩,并且返回值為void岳守,這條指令執(zhí)行后,當(dāng)前方法結(jié)束碌冶;

?exception_table_length為000001206-000001207位置的0x0000湿痢,表示exception_table_length為0,后面沒有exception相關(guān)數(shù)據(jù)扑庞;緊接著兩個字節(jié)表示attributes_count譬重,即位置000001208-0000012090x0002,表示有2個attribute罐氨;分析Code屬性結(jié)構(gòu)中的兩個屬性如下:

  • 第1個屬性:
    ?attribute_name_index為00000120A-00000120B位置的0x000A臀规,表示引用常量池的第10個常量,也即LineNumberTable,LineNumberTable的屬性結(jié)構(gòu)如下:
LineNumberTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 line_number_table_length;
    {   u2 start_pc;
        u2 line_number;
      } line_number_table[line_number_table_length];
}

?attribute_length為00000120C-00000120F位置的0x00000006栅隐,表示屬性長度為6塔嬉;line_number_table_length為000001300-000001301位置的0x0001,也即擁有一個line_number_table玩徊;后面緊跟著line_number_table,其中start_pc為000001302-000001303位置的0x0000谨究,line_number為000001304-000001305位置0x000C恩袱;

  • 第2個屬性:
    ?attribute_name_index為000001306-000001307位置的0x000B,表示引用常量池的第11個常量胶哲,也即LocalVariableTable,LocalVariableTable的屬性結(jié)構(gòu)如下:
LocalVariableTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 local_variable_table_length
    { u2 start_pc;
      u2 length;
      u2 name_index;
      u2 descriptor_index;
      u2 index;
    } local_variable_table[local_variable_table_length];
}

?attribute_length為000001308-00000130B位置的0x0000000C畔塔,表示屬性長度為12;local_variable_table_length為00000130C-00000130D位置的0x0001鸯屿,表示local_variable_table的長度為1澈吨;緊接著local_variable_table的分析,其中start_pc為00000130E-00000130F位置的0x0000碾盟,length為000001400-000001401位置的0x0005棚辽,name_index為000001402-000001403位置的0x000C,表示name為常量池的第12個屬性冰肴,也即this屈藐,descriptor_index為000001404-000001405位置的0x000D,也即常量池的第13個屬性熙尉,也即Lcom/sunny/jdk/classfile/TestClass;联逻,index為000001406-000001407位置的0x0000

  • 第2個方法:

?access_flags為000001408-000001409位置的0x0001检痰,表示ACC_PUBLIC(0x0001)包归;name_index是00000140A-00000140B位置的0x000E,表示常量池的第14個常量铅歼,也即方法名為inc公壤;descriptor_index是00000140C-00000140D位置的0x000F,也即常量池的第15個常量椎椰,也即()I厦幅,表示返回值為int;attributes_count是00000140E-00000140F位置的0x0001慨飘,表示有inc方法有1個屬性确憨,后面緊跟的就是屬性分析; *inc方法的第1個屬性: attribute_name_index是000001500-000001501位置的0x0009瓤的,表示常量池的第9個常量休弃,也即Code`,表示該屬性是Code屬性圈膏,Code的屬性結(jié)構(gòu)如下:

Code_attribute {
    u2 attribute_name_index; 
    u4 attribute_length;
    u2 max_stack;   
    u2 max_locals;  
    u4 code_length; 
    u1 code[code_length]; 
    u2 exception_table_length;
    {   u2 start_pc;
        u2 end_pc;
        u2 handler_pc; 
        u2 catch_type; 
    } exception_table[exception_table_length]; 
    u2 attributes_count;     
    attribute_info attributes[attributes_count]; 
}

?attribute_length為000001502-000001505位置的0x00000031塔猾,表示屬性長度為49;max_stack(max_stack表示最大棧深度稽坤,虛擬機(jī)在運行時根據(jù)這個值來分配棧幀中操作數(shù)的深度)為000001506-000001507位置的0x0002丈甸,表示最大棧幀深度為2医增;max_locals(max_locals代表了局部變量表的存儲空間)為000001508-000001509位置的0x0001,表示局部變量的存儲空間為1老虫;code_length(code_length代表了字節(jié)碼指令的數(shù)量叶骨,而code表示的是字節(jié)碼指令,從Code屬性結(jié)構(gòu)中可以知道code的類型為u1祈匙,一個u1類型的取值為0x00-0xFF,對應(yīng)的十進(jìn)制為0-255忽刽,目前虛擬機(jī)規(guī)范已經(jīng)定義了200多條指令)為00000150A-00000150D位置的0x00000007,表示Code字節(jié)碼指令的數(shù)量為7夺欲;后面的7個字節(jié)即為字節(jié)碼跪帝,位置00000150E-000001604的數(shù)據(jù)為“2A B4 00 02 04 60 AC”,翻譯如下:

  • 讀入2A些阅,查虛擬機(jī)規(guī)范1.8知道指令操作碼0x2A對應(yīng)的指令為aload_0伞剑,這個指令的含義是講第0個Slot中為reference類型的本地變量推送到操作數(shù)棧頂;

  • 讀入B4市埋,查表得0xB4對應(yīng)的指令為getfield黎泣,這條指令的作用是獲取指定類的實例字段,并將其壓入棧頂缤谎;這個方法有一個u2類型的參數(shù)說明具體調(diào)用哪一個類的什么屬性抒倚,它指向常量池中的一個CONSTANT_Fieldref_info類型常量,即此屬性的符號引用坷澡。

  • 讀入00 02托呕,這是getfield的參數(shù),查常量池得0x0002對應(yīng)的常量為com/sunny/jdk/classfile/TestClass.m:I频敛;

  • 讀入04项郊,查表得0x04對應(yīng)的指令為iconst_1,含義是將int類型2推送至棧頂斟赚;

  • 讀入60着降,查表得0x60對應(yīng)的指令為iadd,含義是將棧頂兩int類型的元素相加汁展,并將相加的結(jié)果壓入棧頂鹊碍,得到的是1厌殉;

  • 讀入AC食绿,查表得0xAC對應(yīng)的指令為ireturn,含義是將當(dāng)前方法的結(jié)果1公罕,返回器紧;

?exception_table_length為000001605-000001606位置的0x0000,表示exception_table_length為0楼眷,后面沒有exception相關(guān)數(shù)據(jù)铲汪;緊接著兩個字節(jié)表示attributes_count熊尉,即位置000001607-0000016080x0002,表示有2個attribute掌腰;分析Code屬性結(jié)構(gòu)中的兩個屬性如下:

  • 第1個屬性:
    ?attribute_name_index為000001609-00000160A位置的0x000A狰住,表示引用常量池的第10個常量,也即LineNumberTable,LineNumberTable的屬性結(jié)構(gòu)如下:
LineNumberTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 line_number_table_length;
    {   u2 start_pc;
        u2 line_number;
      } line_number_table[line_number_table_length];
}

?attribute_length為00000160B-00000160E位置的0x00000006齿梁,表示屬性長度為6催植;line_number_table_length為00000160F-000001700位置的0x0001,也即擁有一個line_number_table;后面緊跟著line_number_table勺择,其中start_pc為000001701-000001702位置的0x0000创南,line_number為000001703-000001704位置0x0010;

  • 第2個屬性:
    ?attribute_name_index為000001705-000001706位置的0x000B省核,表示引用常量池的第11個常量稿辙,也即LocalVariableTable,LocalVariableTable的屬性結(jié)構(gòu)如下:
LocalVariableTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 local_variable_table_length
    { u2 start_pc;
      u2 length;
      u2 name_index;
      u2 descriptor_index;
      u2 index;
    } local_variable_table[local_variable_table_length];
}

?attribute_length為000001707-00000170A位置的0x0000000C,表示屬性長度為12气忠;local_variable_table_length為00000170B-00000170C位置的0x0001邻储,表示local_variable_table的長度為1;緊接著local_variable_table的分析旧噪,其中start_pc為00000170D-00000170E位置的0x0000芥备,length為00000170F-000001800位置的0x0007,name_index為000001801-000001802位置的0x000C舌菜,表示name為常量池的第12個屬性萌壳,也即this,descriptor_index為000001803-000001804位置的0x000D日月,也即常量池的第13個屬性袱瓮,也即Lcom/sunny/jdk/classfile/TestClass;,index為000001805-000001806位置的0x0000爱咬;最后我們來分析下class的文件屬性尺借;

8. class文件屬性:u2 attributes_count 和 attributes[attributes_count]

?attributes_count 為000001807-000001808位置的0x0001,表示有一個屬性精拟;attribute_name_index為000001809-00000180A位置的0x0010燎斩,表示常量池中的第16個屬性,也SourceFile屬性蜂绎;SourceFile屬性結(jié)構(gòu)如下:

SourceFile_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 sourcefile_index;
}

?attribute_length為位置的00000180B-00000180E位置的0x00000002栅表,表示屬性長度為2;sourcefile_index為00000180F-000001900位置的0x0011师枣,表示常量池的第17個常量怪瓶,也即TestClass.java
?至此践美,整個class的二進(jìn)制碼全部分析完畢洗贰;但是JDK也給我們提供了更方便的分析工具找岖,也即文中提到的javap -verbose class文件命令。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敛滋,一起剝皮案震驚了整個濱河市许布,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绎晃,老刑警劉巖爹脾,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異箕昭,居然都是意外死亡灵妨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進(jìn)店門落竹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泌霍,“玉大人,你說我怎么就攤上這事述召≈熳” “怎么了祭务?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵螟左,是天一觀的道長监右。 經(jīng)常有香客問我议泵,道長,這世上最難降的妖魔是什么谢翎? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任唆铐,我火速辦了婚禮淮菠,結(jié)果婚禮上遍愿,老公的妹妹穿的比我還像新娘存淫。我一直安慰自己,他們只是感情好沼填,可當(dāng)我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布桅咆。 她就那樣靜靜地躺著,像睡著了一般坞笙。 火紅的嫁衣襯著肌膚如雪岩饼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天薛夜,我揣著相機(jī)與錄音籍茧,去河邊找鬼。 笑死却邓,一個胖子當(dāng)著我的面吹牛硕糊,可吹牛的內(nèi)容都是我干的院水。 我是一名探鬼主播腊徙,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼简十,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了撬腾?” 一聲冷哼從身側(cè)響起螟蝙,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎民傻,沒想到半個月后胰默,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡漓踢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年牵署,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喧半。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡奴迅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出挺据,到底是詐尸還是另有隱情取具,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布扁耐,位于F島的核電站暇检,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏婉称。R本人自食惡果不足惜块仆,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望王暗。 院中可真熱鬧榨乎,春花似錦、人聲如沸瘫筐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽策肝。三九已至肛捍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間之众,已是汗流浹背拙毫。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留棺禾,地道東北人缀蹄。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親缺前。 傳聞我的和親對象是個殘疾皇子蛀醉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,562評論 2 349