代碼編譯的結果從本地機器碼轉變為位元組碼,是存盤格式發展的一小步,卻是編程語言發展的一大步,
Java 虛擬機使用位元組碼實作了跨平臺的愿景,不僅針對Java語言,實作了write once,run anywhere的愿景;隨著發展,越來越多其他語言可以在Java虛擬機之上運行,如Kotlin、Clojure、Groovy、JRbuy、JPython、Scala等,
Java虛擬機不與包括Java語言在內的任何程式語言系結,它只與“Class檔案”這種特定的二進制檔案格式所關聯,Class檔案中包含了Java虛擬機指令集、符號表以及若干其他輔助資訊,

1. Class類檔案的結構
Class檔案是一組以位元組為基礎單位的二進制流,各個資料專案嚴格按照順序緊湊的排列在檔案之中,中間沒有任何分隔符,Class檔案格式采用一種類似于C語言結構體的偽結構來存盤資料,這種偽結構只有兩種資料型別:無符號數和表,
- 無符號數屬于基本的資料型別,以u1、u2、u4、u8來分別代表1位元組、2位元組、4位元組和8位元組的無符號數,無符號數可以用來描述數字、索引參考、數量值或者按照UTF-8編碼構成的字串值,
- 表是由多個無符號數或其他表作為資料項構成的復合資料型別,所有的表習慣性以“_info”結尾,表用于描述有層次關系的復合結構資料,整個Class檔案本質上可以視作一張表,這張表嚴格按照下表1-1所列順序排列,
表1-1 Class 檔案格式

為了方便講解,準備了如下的Java代碼,
package org.fenixsoft.clazz; public class TestClass{ private int m; public int inc(){ return m + 1; } }
相應的類檔案結構如下圖所示:

1.1 魔數與Class檔案版本
每個Class檔案的頭四個位元組被稱為魔數,用以確定這個檔案是否為一個能被虛擬機接受的Class檔案,值為0x CA FE BA BE,第五六位元組 0x 00 00代表次版本號,主版本號值為0x 00 32,即十進制的50,說明是JDK6或以上版本虛擬機執行的Class檔案,
1.2 常量池
由表1-1可知,主次版本號后,是常量池入口,常量池被比喻為Class檔案里面的資源倉庫,是Class檔案結構中與其他專案關聯最多的資料,
首先是u2型別的資料代表常量池容量計數值(constant_pool_count),這個容量技術從1開始,如果不參考任何常量池專案,則把索引值設定為0,由下圖0x 00 16,即十進制的22,代表常量池中有21項常量,索引范圍為1~21,

常量池中主要存放兩大類常量:字面量(Literal)和符號參考(Symbolic References),字面量比較接近于Java語言層面的常量概念,如文本字串,被宣告為final常量值等,而符號參考則屬于編譯原理方面的概念,主要包括以下幾類常量:
- 被模塊匯出或者開放的包(Package)
- 類和介面的全限定名(Fully Qualified Name)
- 欄位的名稱和描述符(Descriptor)
- 方法的名稱和描述符
- 方法句柄和方法型別(Method Handle、Method Type、Invoke Dynamic)
- 動態呼叫點和動態常量(Dynamically-Computed Call Site、Dynamically-Computed Constant)
常量池中的每一項常量都是一個表,常量表中有17種不同型別的常量,所有表結構起始的第一位是個u1型別的標志位,代表當前常量屬于哪種常量型別,

圖6-3常量池結構中,常量池容量0x 00 16后緊跟的u1標志位0x 07查表6-3可知,是一個CONSTANT_Class_info型別,此型別的常量代表一個類或者介面的符號參考,

name_index是常量池的索引值,它指向常量池中一個CONSTANT_Utf8_info型別常量,此常量代表了這個類的全限定名,本例中的name_index值為0x 00 02,指向了常量池中的第二項常量,第二項常量的標志位是0x 01,查表6-3可知,是CONSTANT_Uff8_info型別的常量,其結構表如表6-5所示,

length值說明了UTF-8字串長度是多少個位元組,后面緊跟長度為length位元組的,UTF-8縮略編碼表示的字串,
UTF-8縮略編碼與普通UTF-8編碼的區別是:從'\u0001'到'\u007f'之間的字符(相當于1~127的ASCII碼) 的縮略編碼使用一個位元組表示,從'\u0080'到'\u07ff'之間的所有字符的縮略編碼用兩個位元組表示, 從'\u0800'開始到'\uffff'之間的所有字符的縮略編碼就按照普通UTF-8編碼規則使用三個位元組表示,
本例中length值長度為0x 00 1D, 長度是29個位元組,往后的29個位元組都在1~127的ASCII碼范圍以內,內容為“org/fenixsoft/clazz/TestClass”
剩下的19個常量與上面討論的類似,可以通過javap -verbose 類檔案名查看位元組碼內容,如下圖所示,

不難發現,常量池中存在“I”“V”“<init>”“LineNumberTable”“LocalVariableTable”等, 這些看起來在源代碼中不存在的常量,它們是編譯器自動生成的,會被欄位表(field_info)、方法表(method_info)、屬性表(attribute_info)所參考,用于表示方法的回傳值,有幾個引數,每個引數的型別等資訊,
以下是常量池中的17種資料型別的結構總表


1.3 訪問標志
由表6-1,常量池結束后,緊接著的2個位元組代表訪問標志(access_flags),這個標志用于識別一些類或者介面層次的訪問資訊,具體標志值及其含義見下表,

沒有用到的標志位一律為0,由標志位為0x 00 21可知,ACC_PUBLIC 和 ACC_SUPER標志為真,
