?在《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)于常量類型的描述可知tag
為10
的常量是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
-000001209
的0x0002
,表示有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是00000140
C-00000140
D位置的
0x000F,也即常量池的第15個常量椎椰,也即
()I厦幅,表示返回值為int;attributes_count是00000140
E-00000140
F位置的
0x0001慨飘,表示有inc方法有
1個屬性确憨,后面緊跟的就是屬性分析; *inc方法的第1個屬性: attribute_name_index是00000150
0-00000150
1位置的
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
-000001608
的0x0002
,表示有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文件
命令。