Class文件規(guī)范
?根據(jù)《JAVA虛擬機(jī)規(guī)范》的規(guī)定,Class文件格式采用一種類(lèi)似于C語(yǔ)言結(jié)構(gòu)體的偽結(jié)構(gòu)來(lái)存儲(chǔ)數(shù)據(jù),這種偽結(jié)構(gòu)中(Class文件格式中)只有兩種數(shù)據(jù)類(lèi)型:“無(wú)符號(hào)數(shù)”和“表”。
?
?本篇文章圖中貼出的類(lèi)型這一列也都是這兩個(gè)數(shù)據(jù)類(lèi)型,如果類(lèi)型是u開(kāi)頭的表示為無(wú)符號(hào)數(shù)的數(shù)據(jù)類(lèi)型,如果是_info結(jié)尾的表示是表類(lèi)型。
?
- 無(wú)符號(hào)數(shù)就是正常的數(shù)字,以u(píng)1,u2,u4,u8來(lái)表示一個(gè),兩個(gè),四個(gè),八個(gè)字節(jié),通常用來(lái)描述數(shù)字,索引引用,數(shù)量值或者用來(lái)表達(dá)字符串值。
?u1在Class文件中為兩個(gè)數(shù)字或字母,比如00,01,0A,6F ; u2在Class文件中為四個(gè)數(shù)字或字母,比如0001,000A,6C61;
?
- 表是用來(lái)描述復(fù)合結(jié)構(gòu)的數(shù)據(jù),它由無(wú)符號(hào)數(shù)或者其他表構(gòu)成,并在結(jié)尾處用“_info”表示。
?比如:cp_info為常量池表,field_info為字段表,method_info為方法表,attribute_info為屬性表。本篇文章會(huì)講解除了屬性表之外的其他表結(jié)構(gòu),屬性表單獨(dú)寫(xiě)一篇文章進(jìn)行講解
?
- 本質(zhì)上Class結(jié)構(gòu)也是將表和無(wú)符號(hào)數(shù)組合起來(lái)的一張表。
Class文件大結(jié)構(gòu)總覽
后面講的所有結(jié)構(gòu)都是按照這個(gè)Class文件表講的,所以這張圖先看下。
magic
Class文件的 「頭四個(gè)字節(jié)」 表示魔數(shù),這個(gè)值存在于各個(gè)文件中,在之前的這篇文章里面講過(guò)這個(gè)字段:驗(yàn)證apk文件的magic,magic是用來(lái)驗(yàn)證是不是屬于某種結(jié)構(gòu)的,即使用戶更改了文件的后綴名也沒(méi)事,通過(guò)這個(gè)字段就可以看出他是什么類(lèi)型的文件。「因此每一種文件格式的魔數(shù)magic都不一樣,因?yàn)槭怯脕?lái)表示其屬于哪種文件格式的。」
?Class文件的magic為0XCAFFBABE(咖啡寶貝)
?
Class文件版本號(hào)
接下來(lái)的兩個(gè)字節(jié)表達(dá)的Class文件的 「次版本號(hào)(Minor Version)」 ,再接著兩個(gè)字節(jié)表達(dá)的是 「主版本號(hào)(Major Version)」 。
?高版本的JDK能夠兼容低版本的Class文件,但是低版本的JDK不能運(yùn)行高版本的Class文件(規(guī)范中Class文件校驗(yàn)必須要求的!)
?
Constant_Pool常量池
1、Class文件中第一個(gè)出現(xiàn)的表結(jié)構(gòu)的數(shù)據(jù),主要存放兩種常量:「字面量和符號(hào)引用。」
?字面量:Java語(yǔ)言層面常量的概念。eg:字符串,被聲明為final的常量等
?
?符號(hào)引用:編譯原理方面的概念。eg:被模塊導(dǎo)出或者開(kāi)放的包(Package) 類(lèi)和接口的全限定名(也就是類(lèi)所屬的包名和類(lèi)的名字) 字段的名稱(chēng)和描述符(名稱(chēng)簡(jiǎn)單理解就是字段名,描述符為字段所屬的類(lèi)型) 方法的名稱(chēng)和描述符(名稱(chēng)簡(jiǎn)單理解就是方法名,描述符為方法參數(shù)和返回值) 方法句柄和方法類(lèi)型(Method Handle,Method Type,Invoke Dynamic) 動(dòng)態(tài)調(diào)用點(diǎn)和動(dòng)態(tài)常量
?
2、符號(hào)引用,由于Class文件是在編譯后生成的,因此運(yùn)行的時(shí)候需要 「將符號(hào)引用轉(zhuǎn)換為對(duì)應(yīng)數(shù)據(jù)在內(nèi)存中的地址」 ,否則使用的時(shí)候會(huì)找不到真正的內(nèi)存入口地址(也就是不能被虛擬機(jī)直接使用)。
?當(dāng)虛擬機(jī)做類(lèi)加載的時(shí)候,會(huì)從常量池中拿出對(duì)應(yīng)的符號(hào)引用,在類(lèi)創(chuàng)建或者運(yùn)行時(shí)解析,翻譯到具體的內(nèi)存地址之中 比如class文件的類(lèi)名,當(dāng)類(lèi)被加載到虛擬機(jī)中時(shí),需要將這個(gè)符號(hào)引用轉(zhuǎn)換為類(lèi)真實(shí)所在的內(nèi)存地址才能被正常訪問(wèn)。
?
3.常量池中存儲(chǔ)的數(shù)據(jù)數(shù)量 「從1開(kāi)始計(jì)數(shù)」 ,第0項(xiàng)常量空出來(lái)是因?yàn)橛行?shù)據(jù)并不需要引用常量池中的數(shù)據(jù)時(shí),就可以用第0項(xiàng)常量來(lái)表達(dá)。
4.常量池的數(shù)據(jù)數(shù)量會(huì)在版本號(hào)之后用兩個(gè)字節(jié)來(lái)表達(dá),之后才是常量池中的數(shù)據(jù)
?這樣做是因?yàn)闆](méi)有Class文件中 「沒(méi)有分隔符」 來(lái)表示一組數(shù)據(jù)結(jié)束,因此Class文件中的數(shù)據(jù)必須嚴(yán)格按照固定長(zhǎng)度,固定數(shù)量去存放,不允許改變。
?
5.常量池中 「每一項(xiàng)常量數(shù)據(jù)都是一張表」 ,每個(gè)表結(jié)構(gòu)中都有一個(gè)u1類(lèi)型的標(biāo)志位標(biāo)識(shí)自己屬于哪種類(lèi)型的表。
各個(gè)表對(duì)應(yīng)的含義
這些表有著各自完全獨(dú)立的結(jié)構(gòu),之間沒(méi)有任何關(guān)聯(lián)。
反編譯代碼示例
C:\\>javap -verbose TestClass
Compiled from "TestClass.java"
public class org.fenixsoft.clazz.TestClass extends java.lang.Object SourceFile: "TestClass.java"
minor version: 0
major version: 50
Constant pool:
const #1 = class #2; // org/fenixsoft/clazz/TestClass
const #2 = Asciz org/fenixsoft/clazz/TestClass;
const #3 = class #4; // java/lang/Object
const #4 = Asciz java/lang/Object;
const #5 = Asciz m;
const #6 = Asciz I;
const #7 = Asciz ;
const #8 = Asciz ()V;
const #9 = Asciz Code;
const #10 = Method #3.#11; // java/lang/Object."":()V
const #11 = NameAndType #7:#8;// "":()V
const #12 = Asciz LineNumberTable;
const #13 = Asciz LocalVariableTable;
const #14 = Asciz this;
const #15 = Asciz Lorg/fenixsoft/clazz/TestClass;;
const #16 = Asciz inc;
const #17 = Asciz ()I;
const #18 = Field #1.#19; // org/fenixsoft/clazz/TestClass.m:I
const #19 = NameAndType #5:#6; // m:I
const #20 = Asciz SourceFile;
const #21 = Asciz TestClass.java;
代碼塊中Asciz就是之前說(shuō)的Constant_UTF8_info常量(也就是字符串常量),可以看到這些數(shù)據(jù)都是由這個(gè)常量來(lái)組成的,
常量池中常量表對(duì)應(yīng)數(shù)據(jù)結(jié)構(gòu)內(nèi)容
第一列常量為之前 「各個(gè)表對(duì)應(yīng)的含義」 中提到的表結(jié)構(gòu)類(lèi)型也就是表的名稱(chēng);
第二列中的項(xiàng)目為各個(gè)表中存放的不同的數(shù)據(jù)內(nèi)容有哪些,這些表的數(shù)據(jù)中都有一個(gè)標(biāo)識(shí)位tag用來(lái)標(biāo)識(shí)不同的表,在之前 「各個(gè)表對(duì)應(yīng)的含義」 中也提到過(guò);
第三列為描述不同于之前講的表的描述,這列說(shuō)明的是**「表中存儲(chǔ)的不同數(shù)據(jù)的描述」**
圖例:
-
JAVA
+關(guān)注
關(guān)注
19文章
2974瀏覽量
105018 -
C語(yǔ)言
+關(guān)注
關(guān)注
180文章
7614瀏覽量
137499 -
Class
+關(guān)注
關(guān)注
0文章
53瀏覽量
19769 -
存儲(chǔ)數(shù)據(jù)
+關(guān)注
關(guān)注
0文章
89瀏覽量
14145
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論