學習Java的朋友應該都知道Java從剛開始的時候就打著平台無關性的旗號,說“一次編寫,到處運行”,其實說到無關性,Java平台還有另外一個無關性那就是語言無關性,要實現語言無關性,那麼Java體系中的class的檔案結構或者說是字節碼就顯得相當重要了,其實Java從剛開始的時候就有兩套規範,一個是Java語言規範,另外一個是Java虛擬機規範,Java語言規範只是規定了Java語言相關的約束以及規則,而虛擬機規範則才是真正從跨平台的角度去設計的。
或許大部分程式設計師都認為Java虛擬機器執行Java程式是理所當然且天經地義的事,但時至今日,商業機構和開源機構已經在Java語言之外發展出一大批在Java虛擬機器之上運作的語言,如Clojure、Groovy、JRuby、Jython、Scale等。使用Java編譯器可以把Java程式碼編譯為儲存字節碼的Class文件,使用JRuby等其它語言的編譯器一樣可以把程式碼編譯成Class文件,Java之所以能夠跨平台運行,是因為Java虛擬機可以載入和執行同一種平台無關的字節碼。也就是說,實現語言平台無關性的基礎是虛擬機器和字節碼儲存格式,虛擬機器並不關心Class的來源是什麼語言,只要它符合Class檔案應有的結構就可以在Java虛擬機器中運行。
1)Class檔案是由byte(8bit)為基礎的位元組流構成的,這些位元組流之間都嚴格按照規定的順序排列,並且位元組之間不存在任何空隙,對於超過8個位元組的數據,將按照Big-Endian的順序存儲的,也就是說高位元組儲存在低的位址上面,而低位元組儲存到高位址上面,其實這也是class檔案要跨平台的關鍵,因為PowerPC架構的處理採用Big- Endian的儲存順序,而x86系列的處理器則採用Little-Endian的儲存順序,因此為了Class檔案在各中處理器架構下維持統一的儲存順序,虛擬機器規格必須對其進行統一。
2) Class檔案結構採用類似C語言的結構體來儲存資料的,主要有兩類資料項,無符號數和表,無符號數用來表述數字,索引引用以及字串等,例如u1,u2,u4,u8分別代表1個位元組,2個位元組,4個位元組,8個位元組的無符號數,而表是有多個無符號數以及其它的表組成的複合結構。可能大家看到這裡 對無符號數和表到底是上面也不是很清楚,不過不要緊,等下面實例的時候,我會再以實例來解釋。
明確了上面的兩點以後,我們接下來後來看看Class檔案中按照嚴格的順序排列的位元組流都具體包含些什麼資料:
在看上圖的時候,有一點我們需要注意,例如cp_info表示常數池,上圖中用constant_pool[constant_pool_count-1]的方式來表示常數池有constant_pool_count-1個常數,它這裡是採用數組的表現形式,但是大家不要誤以為所有的常數池的常數長度都是一樣的,其實這個地方只是為了方便描述採用了數組的方式,但是這裡並不像編程語言那裡,一個int型的數組,每個int長度都一樣。
1)u4 magic 表示魔數,而魔數佔用了4個位元組,魔數到底是做什麼的呢?它其實就是表示這個文件的類型是一個Class文件,而不是一張JPG圖片,或是AVI的電影。而Class檔案對應的魔數是0xCAFEBABE.
2)u2 minor_version 表示Class檔案的次版本號,且此版本號是u2型別的無符號數表示。
3)u2 major_version 表示Class檔案的主版本號,且主版本號是u2類型的無符號數表示。 major_version和minor_version主要用來表示目前的虛擬機器是否接受目前這種版本的Class檔案。不同版本的Java編譯器編譯的Class檔案對應的版本是不一樣的。 高版本的虛擬機器支援低版本的編譯器編譯的Class檔案結構。例如Java SE 6.0對應的虛擬機器支援Java SE 5.0的編譯器編譯的Class檔案結構,反之則不行。
4)u2 constant_pool_count 表示常數池的數量。這裡我們需要重點來說一下常數池是什麼東西,請大家不要與Jvm內存模型中的運行時常量池混淆了,Class文件中常量池主要存儲了字面量以及符號引用,其中字面量主要包括字串,final常數的值或某個屬性的初始值等等,而符號引用主要儲存類別和介面的全限定名稱,欄位的名稱以及描述符,方法的名稱以及描述符,這裡名稱可能大家都很容易理解,至於描述詞的概念,放到下面說字段表以及方法表的時候再說。另外大家都知道Jvm的記憶體模型中有堆,棧,方法區,程式計數器構成,而方法區中又存在一塊區域叫運行時常數池,運行時常量池中存放的東西其實也就是編譯器產生的各種字面量以及符號引用, 只不過運行時常量池具有動態性,它可以在運行的時候向其中增加其它的常量進去,最具代表性的就是String的intern方法。
5)cp_info 表示常數池,這裡面就存在了上面說的各種各樣的字面量和符號引用。放到常數池的中資料項目一共有14個常數,每一種常數都是一個表,並且每種常數都用一個公共的部分tag來表示是哪種類型的常數。
#Constant Type | Value |
---|---|
|
|
|
|
|
|
|
CONSTANT_Class |
|
#CONSTANT_Fieldref |
|
CONSTANT_Methodref |
|
CONSTANT_InterfaceMethodref |
|
CONSTANT_String |
|
CONSTANT_Integer |
|
CONSTANT_Float |
|
CONSTANT_Long |
|
CONSTANT_Double |
|
CONSTANT_NameAndType |
|
CONSTANT_Utf8 | 1
6)u2 access_flags 表示类或者接口的访问信息,具体如下图所示:
Flag Name | Value | Interpretation |
---|---|---|
ACC_PUBLIC |
0x0001 | Declared public ; may be accessed from outside its package. |
ACC_FINAL |
0x0010 | Declared final ; no subclasses allowed. |
ACC_SUPER |
0x0020 | Treat superclass methods specially when invoked by the invokespecial instruction. |
ACC_INTERFACE |
0x0200 | Is an interface, not a class. |
ACC_ABSTRACT |
0x0400 | Declared abstract ; must not be instantiated. |
ACC_SYNTHETIC |
0x1000 | Declared synthetic; not present in the source code. |
ACC_ANNOTATION |
0x2000 | Declared as an annotation type. |
ACC_ENUM |
0x4000 | Declared as an enum type. |
7)u2 this_class 表示类的常量池索引,指向常量池中CONSTANT_Class的常量
8)u2 super_class 表示超类的索引,指向常量池中CONSTANT_Class的常量
9)u2 interface_counts 表示接口的数量
10)u2 interface[interface_counts]表示接口表,它里面每一项都指向常量池中CONSTANT_Class常量
11)u2 fields_count 表示类的实例变量和类变量的数量
12) field_info fields[fields_count]表示字段表的信息,其中字段表的结构如下图所示:
field_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; }
上图中access_flags表示字段的访问表示,比如字段是public、private、protect 等,name_index表示字段名称,指向常量池中类型是CONSTANT_UTF8的常量,descriptor_index表示字段的描述符,它也指向常量池中类型为 CONSTANT_UTF8的常量,attributes_count表示字段表中的属性表的数量,而属性表是则是一种用与描述字段,方法以及 类的属性的可扩展的结构,不同版本的Java虚拟机所支持的属性表的数量是不同的。
13) u2 methods_count表示方法表的数量
14)method_info 表示方法表,方法表的具体结构如下图所示:
method_info { u2 access_flags; u2 name_index;, u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; }
其中access_flags表示方法的访问表示,name_index表示名称的索引,descriptor_index表示方法的描述符,attributes_count以及attribute_info类似字段表中的属性表,只不过字段表和方法表中属性表中的属性是不同的,比如方法表中就有Code属性,表示方法的代码,而字段表中就没有Code属性。
15) attribute_count表示属性表的数量,说到属性表,我们需要明确以下几点:
属性表存在于Class文件结构的最后,字段表,方法表以及Code属性中,也就是说属性表中也可以存在属性表,属性表的长度是不固定的,不同的属性,属性表的长度是不同的
以上是java虛擬機器之class文件結構介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!