- 概述
- Class文件結(jié)構(gòu)總結(jié)
1.概述
在 Java 中,JVM 可以理解的代碼就叫做字節(jié)碼(即擴(kuò)展名為 .class 的文件),它不面向任何特定的處理器,只面向虛擬機(jī)。Java 語言通過字節(jié)碼的方式,在一定程度上解決了傳統(tǒng)解釋型語言執(zhí)行效率低的問題,同時(shí)又保留了解釋型語言可移植的特點(diǎn)。所以 Java 程序運(yùn)行時(shí)比較高效,而且,由于字節(jié)碼并不針對(duì)一種特定的機(jī)器,因此,Java 程序無須重新編譯便可在多種不同操作系統(tǒng)的計(jì)算機(jī)上運(yùn)行。
.class文件是不同的語言在 Java 虛擬機(jī)之間的重要橋梁,同時(shí)也是支持 Java 跨平臺(tái)很重要的一個(gè)原因。

2. Class文件結(jié)構(gòu)總結(jié)
根據(jù) Java 虛擬機(jī)規(guī)范,類文件由單個(gè) ClassFile結(jié)構(gòu)組成:
單個(gè)ClassFile的結(jié)構(gòu)為:
ClassFile {
u4 magic; //Class 文件的標(biāo)志
u2 minor_version;//Class 的小版本號(hào)
u2 major_version;//Class 的大版本號(hào)
u2 constant_pool_count;//常量池的數(shù)量
cp_info constant_pool[constant_pool_count-1];//常量池
u2 access_flags;//Class 的訪問標(biāo)記
u2 this_class;//當(dāng)前類
u2 super_class;//父類
u2 interfaces_count;//接口
u2 interfaces[interfaces_count];//一個(gè)類可以實(shí)現(xiàn)多個(gè)接口
u2 fields_count;//Class 文件的字段屬性
field_info fields[fields_count];//一個(gè)類會(huì)可以有個(gè)字段
u2 methods_count;//Class 文件的方法數(shù)量
method_info methods[methods_count];//一個(gè)類可以有個(gè)多個(gè)方法
u2 attributes_count;//此類的屬性表中的屬性數(shù)
attribute_info attributes[attributes_count];//屬性表集合
}
Class文件字節(jié)碼結(jié)構(gòu)示意圖

2.1 magic number
u4 magic; //Class 文件的標(biāo)志
每個(gè) Class 文件的頭四個(gè)字節(jié)稱為魔數(shù)(Magic Number),它的唯一作用是確定這個(gè)文件是否為一個(gè)能被虛擬機(jī)接收的 Class 文件。
2.2 Class文件版本
u2 minor_version;//Class 的小版本號(hào)
u2 major_version;//Class 的大版本號(hào)
緊接著魔數(shù)的四個(gè)字節(jié)存儲(chǔ)的是 Class 文件的版本號(hào):第五和第六是次版本號(hào),第七和第八是主版本號(hào)。
高版本的 Java 虛擬機(jī)可以執(zhí)行低版本編譯器生成的 Class 文件,但是低版本的 Java 虛擬機(jī)不能執(zhí)行高版本編譯器生成的 Class 文件。所以,我們?cè)趯?shí)際開發(fā)的時(shí)候要確保開發(fā)的的 JDK 版本和生產(chǎn)環(huán)境的 JDK 版本保持一致。
2.3 常量池
u2 constant_pool_count;//常量池的數(shù)量
cp_info constant_pool[constant_pool_count-1];//常量池
緊接著主次版本號(hào)之后的是常量池,常量池的數(shù)量是 constant_pool_count-1(常量池計(jì)數(shù)器是從1開始計(jì)數(shù)的,將第0項(xiàng)常量空出來是有特殊考慮的,索引值為0代表“不引用任何一個(gè)常量池項(xiàng)”)。
常量池主要存放兩大常量:
- 字面量(字面量比較接近于 Java 語言層面的的常量概念)
- 文本字符串
- 聲明為 final 的常量值
- 符號(hào)引用(編譯方面的概念)
- 類和接口的全限定名
- 字段的名稱和描述符
- 方法的名稱和描述符
常量池中每一項(xiàng)常量都是一個(gè)表,這14種表有一個(gè)共同的特點(diǎn):開始的第一位是一個(gè) u1 類型的標(biāo)志位 -tag 來標(biāo)識(shí)常量的類型,代表當(dāng)前這個(gè)常量屬于哪種常量類型.

2.4 訪問標(biāo)志
在常量池結(jié)束之后,緊接著的兩個(gè)字節(jié)代表訪問標(biāo)志,這個(gè)標(biāo)志用于識(shí)別一些類或者接口層次的訪問信息。例如:
- 這個(gè)Class是類還是接口
- 是否為 public 或者 abstract 類型
- 是類的話是否聲明為 final
- ......

2.5 當(dāng)前類索引,父類索引與接口索引集合
u2 this_class;//當(dāng)前類
u2 super_class;//父類
u2 interfaces_count;//接口
u2 interfaces[interfaces_count];//一個(gè)類可以實(shí)現(xiàn)多個(gè)接口
類索引用于確定這個(gè)類的全限定名,父類索引用于確定這個(gè)類的父類的全限定名,由于 Java 語言的單繼承,所以父類索引只有一個(gè),除了 java.lang.Object 之外,所有的 java 類都有父類,因此除了 java.lang.Object 外,所有 Java 類的父類索引都不為 0。
接口索引集合用來描述這個(gè)類實(shí)現(xiàn)了那些接口,這些被實(shí)現(xiàn)的接口將按implents(如果這個(gè)類本身是接口的話則是extends) 后的接口順序從左到右排列在接口索引集合中.
2.6 字段表集合
u2 fields_count;//Class 文件的字段的個(gè)數(shù)
field_info fields[fields_count];//一個(gè)類會(huì)可以有多個(gè)字段
字段表(field info)用于描述接口或類中聲明的變量。字段包括類級(jí)變量以及實(shí)例變量,但不包括在方法內(nèi)部聲明的局部變量。
field info(字段表) 的結(jié)構(gòu):

- access_flags: 字段的作用域(public ,private,protected修飾符),是實(shí)例變量還是類變量(static修飾符),可否被序列化(transient 修飾符),可變性(final),可見性(volatile 修飾符,是否強(qiáng)制從主內(nèi)存讀寫)。
- name_index: 對(duì)常量池的引用,表示的字段的名稱;
- descriptor_index: 對(duì)常量池的引用,表示字段和方法的描述符;
- attributes_count: 一個(gè)字段還會(huì)擁有一些額外的屬性,attributes_count 存放屬性的個(gè)數(shù);
- attributes[attributes_count]: 存放具體屬性具體內(nèi)容。
上述這些信息中,各個(gè)修飾符都是布爾值,要么有某個(gè)修飾符,要么沒有,很適合使用標(biāo)志位來表示。而字段叫什么名字、字段被定義為什么數(shù)據(jù)類型這些都是無法固定的,只能引用常量池中常量來描述。

2.7 方法表集合
u2 methods_count;//Class 文件的方法的數(shù)量
method_info methods[methods_count];//一個(gè)類可以有個(gè)多個(gè)方法
methods_count 表示方法的數(shù)量,而 method_info 表示的方法表。
Class 文件存儲(chǔ)格式中對(duì)方法的描述與對(duì)字段的描述幾乎采用了完全一致的方式。方法表的結(jié)構(gòu)如同字段表一樣,依次包括了訪問標(biāo)志、名稱索引、描述符索引、屬性表集合幾項(xiàng)。

2.8 屬性表集合
u2 attributes_count;//此類的屬性表中的屬性數(shù)
attribute_info attributes[attributes_count];//屬性表集合
在 Class 文件,字段表,方法表中都可以攜帶自己的屬性表集合,以用于描述某些場(chǎng)景專有的信息。與 Class 文件中其它的數(shù)據(jù)項(xiàng)目要求的順序、長(zhǎng)度和內(nèi)容不同,屬性表集合的限制稍微寬松一些,不再要求各個(gè)屬性表具有嚴(yán)格的順序,并且只要不與已有的屬性名重復(fù),任何人實(shí)現(xiàn)的編譯器都可以向?qū)傩员碇袑?入自己定義的屬性信息,Java 虛擬機(jī)運(yùn)行時(shí)會(huì)忽略掉它不認(rèn)識(shí)的屬性。