1.Class文件概述
Class文件是一組以8位字節(jié)為基礎(chǔ)的二進(jìn)制流声离,各個(gè)數(shù)據(jù)項(xiàng)目嚴(yán)格按照順序緊湊地排列在Class文件之中吠裆,中間沒(méi)有添加任何分隔符养篓,這使得整個(gè)Class文件中存儲(chǔ)的內(nèi)容幾乎全部是程序的必要數(shù)據(jù)涂炎,沒(méi)有空隙存在忠聚。當(dāng)遇到需要占用8位字節(jié)的以上的空間的數(shù)據(jù)項(xiàng)時(shí)设哗,則會(huì)按照高位在前的方式分隔成若干個(gè)8位字節(jié)進(jìn)行存儲(chǔ)。
根據(jù)Java虛擬機(jī)規(guī)范的規(guī)定两蟀,Class文件格式采用一種類似于C語(yǔ)言結(jié)構(gòu)體的偽結(jié)構(gòu)來(lái)存儲(chǔ)數(shù)據(jù)网梢,這種偽結(jié)構(gòu)中只有兩種數(shù)據(jù)類型:
無(wú)符號(hào)和表,后面的解析都要以這兩種數(shù)據(jù)類型為基礎(chǔ)赂毯,所以這里要先介紹這兩個(gè)概念战虏。
1.1 無(wú)符號(hào)數(shù)和表
無(wú)符號(hào)數(shù)屬于基本的數(shù)據(jù)類型,以u(píng)1党涕、u2烦感、u4、u8來(lái)分別代表1個(gè)字節(jié)膛堤、2個(gè)字節(jié)手趣、4個(gè)字節(jié)和8個(gè)字節(jié)的無(wú)符號(hào)數(shù),
無(wú)符號(hào)數(shù)可以用來(lái)描述數(shù)字肥荔、索引引用绿渣、數(shù)量值或者按照UTF-8編碼構(gòu)成字符串值。表是由多個(gè)無(wú)符號(hào)數(shù)或者其他表作為數(shù)據(jù)項(xiàng)構(gòu)成的復(fù)合數(shù)據(jù)類型燕耿,所有表都習(xí)慣性的以"_info"結(jié)尾中符。表用于描述
有層次關(guān)系的復(fù)合結(jié)構(gòu)的數(shù)據(jù),整個(gè)Class文件本質(zhì)上就是一張表誉帅。它由表6-1所示的數(shù)據(jù)項(xiàng)構(gòu)成淀散。
1.2 魔數(shù)與Class文件的版本
每個(gè)Class文件的頭4個(gè)字節(jié)稱為魔數(shù)(Magic Number),它的唯一作用是確定這個(gè)文件是否為一個(gè)能被虛擬機(jī)接受的Class文件蚜锨。
Class文件的魔數(shù)很有“浪漫氣息”吧凉,值為0xCAFEBABY
(咖啡寶貝).
緊接著魔數(shù)的4個(gè)字節(jié)值是Class文件的版本號(hào):第5和第6個(gè)字節(jié)是次版本號(hào)(Minor Version),第7和第8個(gè)字節(jié)是主版本號(hào)
(Major Version)。Java的版本號(hào)是從45開始的踏志。
為了方便講解阀捅,現(xiàn)準(zhǔn)備了一段最簡(jiǎn)單的Java代碼(見(jiàn)代碼清單1-1)
代碼清單1-1
package org.fenixsoft.clazz;
public class TestClass {
private int m;
public int inc() {
return m+1;
}
}
下面是部分class文件中的內(nèi)容:
1.3 常量池
緊接著主次版本號(hào)之后的是常量池入口,常量池可以理解為Class文件之中的資源倉(cāng)庫(kù)针余,它是Class文件結(jié)構(gòu)中與其他項(xiàng)目關(guān)聯(lián)
最多的數(shù)據(jù)類型饲鄙,也是占用Class文件空間最大的數(shù)據(jù)項(xiàng)目之一,同時(shí)它還是在Class文件中第一個(gè)出現(xiàn)的表類型數(shù)據(jù)項(xiàng)目圆雁。
由于常量池常量的數(shù)量是不固定的忍级,所以在常量池的入口需要放置一項(xiàng)u2類型的數(shù)據(jù),代表常量池容量計(jì)數(shù)值(constant_pool_count)伪朽。
如下圖所示
常量池容量(偏移地址:0x00000008)為十六進(jìn)制數(shù)0x0016轴咱,即二進(jìn)制的22.這就代表常量池中有21項(xiàng)常量,索引值范圍為1-21.
在Class文件格式規(guī)范制定之時(shí),設(shè)計(jì)者將第0項(xiàng)常量空出來(lái)在于滿足后面某些指向常量池的索引值的數(shù)據(jù)在特定情況下需要表達(dá)
“不引用任何一個(gè)常量池項(xiàng)目”的含義朴肺。
常量池主要存放兩大類常量:字面量(Literal)和符號(hào)引用(Symbolic References)窖剑。字面量比較接近
Java語(yǔ)言層面的常量概念,如文本字符串,聲明為final的常量值等戈稿。而符號(hào)引用則屬于編譯原理方面的概念西土,
包括了下面三類常量:
1.類和接口的全限定名(Full Qualified Name)
2.字段的名稱和描述符(Descriptor)
3.方法的名稱和描述符
常量池的每一項(xiàng)常量都是一個(gè)表,在JDK1.7之前共有11種結(jié)構(gòu)各不相同的表結(jié)構(gòu)數(shù)據(jù)鞍盗,在JDK1.7中為了更好支持動(dòng)態(tài)語(yǔ)言調(diào)用需了,
又額外增加了3種(CONSTANT_MethodHandle_info、CONSTANT_MethodType_info般甲、CONSTANT_InvokeDynamic_info)肋乍。
這14種表都有一個(gè)共同特點(diǎn),就是表開始的第一位是一個(gè)u1類型的標(biāo)志位(tag敷存,取值見(jiàn)下表中標(biāo)志列)墓造,代表當(dāng)前常量屬于哪種
常量類型.
1.3.1 javap的使用
在JDK的bin目錄中,有一個(gè)專門用于分析Class文件字節(jié)碼的工具:javap历帚,下圖列出了使用javap工具的-verbose參數(shù)輸出的
TestClass.class文件字節(jié)碼內(nèi)容滔岳。
1.3.2 常量池中的14種常量項(xiàng)的結(jié)構(gòu)總表
1.4 訪問(wèn)標(biāo)志
在常量池結(jié)束之后杠娱,緊接著的兩個(gè)字節(jié)代表訪問(wèn)標(biāo)志(access_flags)挽牢,這個(gè)標(biāo)志用于識(shí)別一些類或者接口層次的訪問(wèn)信息,
包括:這個(gè)Class是類還是接口摊求;是否定義為public類型禽拔;是否定義為abstract類型;如果是類的話室叉,是否被聲明為final等睹栖。
具體的標(biāo)志位以及標(biāo)志的含義見(jiàn)下圖
access_flags中一共有16個(gè)標(biāo)志位可以使用,上圖只定義了其中8個(gè)茧痕,未使用的標(biāo)志位要求一律為0.
1.5 類索引野来、父類索引和接口索引集合
類索引(this_class)和父類索引(super_class)都是一個(gè)u2類型的數(shù)據(jù),而接口索引集合(interfaces)是一組u2類型的數(shù)據(jù)的集合
踪旷,Class文件中由這三項(xiàng)數(shù)據(jù)來(lái)確定這個(gè)類的繼承關(guān)系曼氛。
1.6 字段表集合
字段表(field_info)用于描述接口或者類中聲明的變量。字段(field)包括類級(jí)變量和實(shí)例級(jí)變量令野,但不包括在方法內(nèi)部聲明的
局部變量舀患。
字段表結(jié)構(gòu):
字段修飾符放在access_flags項(xiàng)目中,它與類中的access_flags項(xiàng)目非常類似的气破,都是一個(gè)u2的數(shù)據(jù)類型聊浅,其中可以設(shè)置的標(biāo)志
位的含義見(jiàn)下表:
跟隨access_flags標(biāo)志的兩項(xiàng)索引值:name_index和descriptor_index。它們都是對(duì)常量池的引用,分別代表著字段的簡(jiǎn)單名稱
以及字段和方法的描述符低匙。
名詞解釋
全限定名:將類名中的'.'替換成'/'旷痕,例如:java.lang.Object的全限定名為java/lang/Object
簡(jiǎn)單名稱:指沒(méi)有類型和參數(shù)修飾的方法和字段名稱,例如Object的equals()方法的簡(jiǎn)單名稱為equals
描述符:用來(lái)描述字段的數(shù)據(jù)類型努咐,方法的參數(shù)列表(包括數(shù)量苦蒿、類型以及順序)和返回值。根據(jù)描述符規(guī)則
基本數(shù)據(jù)類型以及代表無(wú)返回值的void類型都用一個(gè)大寫字符來(lái)表示渗稍,而對(duì)象類型則用字符L加對(duì)象的全限
定名來(lái)表示佩迟。描述數(shù)組類型時(shí),每一維度將使用一個(gè)前置的'['字符來(lái)描述竿屹。
描述符標(biāo)識(shí)字符含義
用描述符來(lái)描述方法時(shí)报强,按照先參數(shù)列表,后返回值的順序描述拱燃,參數(shù)列表按照參數(shù)的嚴(yán)格順序放在一組小括號(hào)`()`
之內(nèi)秉溉。如方法void inc()的描述符為`()V`,方法java.lang.String()的描述符為`()Ljava/lang/String;`,
方法int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target,int targetOffset,int targetCount, int frmoIndex)的描述符為`([CII[CIII)I`
1.7 方法表集合
方法表的結(jié)構(gòu)與字段表的結(jié)構(gòu)相同,依次包括了訪問(wèn)標(biāo)志(access_flags)碗誉、名稱索引(name_index)召嘶、描述符索引(descriptor_index)
、屬性表集合(arrtibutes)幾項(xiàng)哮缺。
方法訪問(wèn)標(biāo)志
注意事項(xiàng):
如果父類方法在子類中沒(méi)有被重寫(Override)弄跌,方法表集合中就不會(huì)出現(xiàn)來(lái)自父類的方法信息。但同樣的
尝苇,有可能會(huì)出現(xiàn)編譯器自動(dòng)添加的方法铛只,最典型的便是類構(gòu)造器`<clinit>`方法和實(shí)例構(gòu)造器`<init>`方法。
1.8 屬性表集合
在Class文件糠溜、字段表淳玩、方法表都可以攜帶自己的屬性表集合,以用于描述某些場(chǎng)景專有的信息非竿。
具體屬性表在此不作講解蜕着,請(qǐng)參考《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐(最新第二版)》的第六章
文件提取碼是2f30
聲明
本文摘自《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐(最新第二版)》一書,為了更好的理解java的字節(jié)碼文件红柱。