Java class檔案說明

來源:互聯網
上載者:User

一、Java Class檔案是什麼 
《The JavaTM Virtual Machine Specification》(Second Edtion)中有表述:Java Class檔案由8位位元組流組成,所有的16位、32位和64位元據分別通過讀入2個、4個和8個位元組來構造,多位元組資料總是按照Big-endian順序來存放,即高位位元組在前(放在低地址)。每個Class檔案都包含且僅包含一個Java類型(類或者介面)。 

或許,《The JavaTM Virtual Machine Specification》中的表述不夠明確,那麼我們可以參考一下《Inside the Java Virtual Machine》(Second Edtion)中的表述:Java Class檔案特指以.class為尾碼名的Java虛擬機器可裝載的檔案。 

分析一下兩者的表述,我覺得都不夠全面、不夠明確。我是這麼定義的:Java Class檔案就是指符合特定格式的位元組流組成的二進位檔案。這個特定的格式就是指第二節要討論的Class檔案格式,亦即在《The JavaTM Virtual Machine Specification》中定義的Class檔案格式。從另一個角度來說,這個特定格式就是指JVM能夠識別、能夠裝載的格式。為什麼這麼說呢?因為JVM在裝載class檔案時,要進行class檔案驗證,以保證裝載的class檔案內容符合正確的內部結構。這個內部結構指的就是這個特定格式,只要是符合這個特定格式的Class檔案都是合法的、規範的Class檔案,都是JVM能夠裝載的Class檔案。如果覺得這樣的表述還是不夠明確,我只能建議你讀完這篇文章之後再回頭來理解看看了J 

為了討論方便,在下文中將對這兩個參考資料做個簡記: 

1)《The Java Virtual Machine Specification》(Second Edtion)簡記為《JVM Spec》(2nded)。 

2)《Inside the Java Virtual Machine》(Second Edtion) 簡記為《Inside JVM》(2nded)。 

二、Java Class檔案的格式 

在講Class檔案的格式之前,要介紹三個概念: 

1)資料類型:《JVM Spec》(2nded)中指出,Java Class檔案的資料用自己定義的一個資料類型集來表示,即u1,u2,u4,分別用於表示一個無符號類型的、佔1,2,4個位元組的資料。在《Inside JVM》(2nded)一書中,作者把這個資料類型集稱之為Class檔案的基本類型,本人覺得比較形象,便於理解。所以,在本文中,我們也用基本類型來表示Java Class檔案的資料。 

2)表:根據《JVM Spec》(2nded)中的定義,表(table)由項(定義見3)組成,用於幾種Class檔案結構中。《JVM Spec》(2nded)中指出,Java Class檔案格式用一個類似於C結構的記號編寫的偽結構來表示。這個偽結構指的就是這裡的表,例如下面的ClassFile表就是這種偽結構的一個典型例子,下文中所有的表都是指這種偽結構的表。表的大小是可變的,這是因為它的組成部分項是可變的。注意;這裡的可變是針對Class層次而言的,即在不同的Class檔案中該項的大小可能不一樣的,但是對於每一個具體的Class檔案來說,這個項的大小又是一定的,因而這個表的大小也是一定的。那麼,項為什麼是可變的呢?請看下面的分析。 

3)項:描述Java Class檔案格式的結構的內容稱為項(items)。每個項都有自己的類型和名稱。項的類型可能是基本類型,也可能是一個表的名字,這種項都是一些數組項。數組項的每一個元素都是一個表,這個表同頂層的ClassFile表一樣,也都是一種偽結構,也都是由一些項構成的,而且這些表不一定是同一種格式的,因此數組項也可以看作一個可變大小的結構流J。這些表對於該數組項來說就是子項,當然子項可能還有子項(目前子項的深度最多就兩層)。項的名稱,沒有什麼好說的,就是《JVM Spec》(2nded)中指定的一些名稱。另外,項也是有大小的,對於沒有子項的項來說,其大小是固定的;對於有子項的項來說,其大小是可變的。在一個具體的Class檔案中,一個可變項(數組)的大小都會在其前一項中指定,為什麼會是這樣的呢?因為《JVM Spec》(2nded)中就是這麼定義的!在Class檔案中,每個項按規範中定義好的順序儲存在Class檔案中,相鄰的項之間沒有任何間隔,連續的項(數組)也是按順序儲存,不進行填充或者對齊,這樣可以使Class檔案緊湊。 

好了,我想這三個概念我已經解釋地比較清楚了,下面開始正式解析Class檔案的格式。 

首先要來解析一下ClassFile表結構,這是《JVM Spec》(2nded)中定義的Class檔案最外層的結構,換言之,就是Class檔案的格式。 

ClassFile表結構 
    ClassFile { 
        u4 magic; 

        u2 minor_version; 

        u2 major_version; 

        u2 constant_pool_count; 

        cp_info constant_pool[constant_pool_count-1]; 

        u2 access_flags; 

        u2 this_Class; 

        u2 super_Class; 

        u2 interfaces_count; 

        u2 interfaces[interfaces_count]; 

        u2 fields_count; 

        field_info fields[fields_count]; 

        u2 methods_count; 

        method_info methods[methods_count]; 

        u2 attributes_count; 

        attribute_info attributes[attributes_count]; 

    } 

ClassFile表結構由16個不同的項組成,其中的各項可以簡要地分析如下: 

(1) magic 

每個Class檔案的前4個位元組被稱為它的魔數(magic number): 0xCAFEBABE。魔數的作用在於:可以輕鬆地分辨出Java Class檔案和非Java Class檔案。(如果一個檔案不是以0xCAFEBABE開頭,它就肯定不是Java Class檔案,因為它不符合規範J)。當Java還稱為“Oak”的時候,這個魔數就已經定下來了,它預示了Java這個名字的出現。魔數的來曆請大家自己查閱J 

(2) minor_version和major_version 

Class檔案的下面4個位元組包含了次、主要版本號。通常只有給定主要版本號和一系列次版本號碼後,Java虛擬機器才能夠讀取Class檔案。如果Class檔案的版本號碼超出了Java虛擬機器所能夠處理的有效範圍,Java虛擬機器將不會處理該Class檔案。例如J2SE5.0版本的虛擬機器就不能執行由J2SE6.0版本的編譯器編譯出來的Class檔案。 

(3) constant_pool_count 

版本號碼後面的項是constant_pool_count即常量池計數項,該項的值必須大於零,它給出該Class檔案中常量池清單項目的元素個數,這個計數項包括了索引為0的constant_pool表項,但是該表項不出現在Class檔案的constant_pool列表中,因為它被保留為Java虛擬機器內部實現使用了,因此常量池列表的元素個數constant_pool_count-1,各個常量池表項的索引值分別為1到constant_pool_count-1。 

註:在這裡,有幾個術語需要解釋一下,常量池即為constant_pool,常量池列表就是指constant_pool[ ],常量池表項即指常量池列表中的某一個具體的表項(元素)。這些常量池表項的可能類型如下述的cp_type表所示: 

cp_type 
入口類型                                標誌值 

CONSTANT_Class                           7 

CONSTANT_Fieldref                        9 

CONSTANT_Methodref                       10 

CONSTANT_InterfaceMethodref              11 

CONSTANT_String                           8 

CONSTANT_Integer                          3 

CONSTANT_Float                            4 

CONSTANT_Long                             5 

CONSTANT_Double                           6 

CONSTANT_NameAndType                      12 

CONSTANT_Utf8                              1 

(4) constant_pool[ ] 

constant_pool_count項下面是constant_pool[ ]項,即常量池列表,其中儲存了該ClassFile結構及其子結構中引用的各種常量,諸如文字字串、final變數值、類名和方法名等等。在Java Class檔案中,常量池表項是用一個cp_info結構來描述的,常量池列表就是由constant_pool_count-1個連續的、可變長度的cp_info表結構構成的constant_pool[ ]數組。為什麼是constant_pool_count-1個constant_pool的原因,在上面已經解釋了。每一個常量池表項都是一個變長結構,其通常格式如下所示: 

cp_info 
cp_info { 
        u1 tag; 
        u1 info[]; 

cp_info表的tag項是一個無符號的byte類型值,它表明了cp_info表的類型和格式,具體的tag類型見上表。 

需要說明的是,cp_info只是一個抽象的概念,在Class檔案中,它表現為一系列具體的、形如CONSTANT_Xxxx_info的constant_pool結構,其具體的格式由cp_info表的tag項(即第一個位元組)來確定。不同的cp_info表,其info[]項也是不一樣的,例如,CONSTANT_Class_info表的info[]項為“u2 name_index”,而CONSTANT_Utf8_info表的info[]項為“u2 length; u1 bytes[length];”,顯然,這兩個cp_info表是不一樣的,大小更是不一樣的,因而常量池表項的大小是可變的。由於常量池列表中的每個常量池表項的結構是不一樣,因此常量池列表的大小也是可變的。在Class檔案中,常量池清單項目是一個可變長度的結構流。 

由cp_info表以及cp_type表我們可以知道,若cp_info表中tag(標誌)項的值為1時,當前的cp_info就是一個CONSTANT_Utf8_info表結構,若cp_info表中tag項的值為3,當前的cp_info就是一個CONSTANT_Integer_info表結構,其它情況類推。這些表的結構可以查閱《JVM Spec》(2nded)的第四章或者《Inside JVM》(2nded)的第六章。 

(5) access_flags 

緊接常量池後的兩個位元組稱為access_flags,access_flags項描述了該Java類型的一些訪問標誌資訊。例如,訪問標誌指明檔案中定義的是類還是介面;訪問標誌還定義了在類或介面的聲明中,使用了哪些修飾符;類和介面是抽象的還是公用的等等。實際上,access_flags項的值是Java型別宣告中使用的訪問標誌符的掩碼(mask,這裡掩碼指的是access_flags的值是所有訪問標誌值的總和,當然,未被使用的標誌位在Class檔案中都被設定為0。例如,若access_flags的值就是0x0001,就表示該Java類型的訪問標誌符是ACC_PUBLIC;若access_flags的值是0x0011,就表示該Java類型的訪問標誌符是ACC_PUBLIC和ACC_FINAL,因為只有這兩個標誌位的和才可能是0x0011;其它情況類推)。 

一個Java類型的所有access_flags標誌符如下表所示: 

access_flags 
標誌名稱         值           含義 

ACC_PUBLIC     0x0001   聲明為public,可以從它的包外訪問 

ACC_FINAL      0x0010   聲明為final,不允許有子類 

ACC_SUPER      0x0020   用invokespecial指令處理超類的調用 

ACC_INTERFACE  0x0200   表明是一個介面,而不是一個類 

ACC_ABSTRACT   0x0400   聲明為abstract,不能被執行個體化 

需要說明的是,這是針對一個Java類型的訪問標誌符列表,有的標誌符只有類可以使用,有的標誌符只有介面才可以使用,詳情請查閱《JVM Spec》(2nded)。 

(6) this_class 

接下來的兩個位元組為this_class項,其值為一個對常量池表項的索引,即它指向一個常量池表項,而且該常量池表項必須為CONSTANT_Class_info表的結構。該表有一個name_index項,該項將指向另一個常量池表項,該表項包含了該類或者介面的完全限定名稱。 

(7) super_class 

緊接著this_class之後的兩個位元組是super_class項,該項必須是對常量池表項的一個有效索引或者值為0。如果super_class項的值為0,則該Class檔案必須表示java.lang.Object類。如果super_class項的值不為0,則又分為兩種情況,若該Class檔案表示一個類,則super_class項必須是對常量池中該類的超類的CONSTANT_Class_info表項的索引,這個超類和它的任何超類都不能是一個final類;若該Class檔案表示一個介面,則super_class項必須是對常量池中表示java.lang.Object類的一個CONSTANT_Class_info表項的索引。 

(8) interfaces_count和interfaces[ ] 

緊接著super_class項後面的兩個位元組是interfaces_count項,此項表示由該類直接實現或者由該介面所擴充的超介面的數量。 

緊接著interfaces_count項後面的是interfaces清單項目,它包含了由該類直接實現或者由該介面所擴充的超介面的常量池索引,共計interfaces_count個索引。interfaces列表中的常量池索引按照該類型在原始碼中給定的從左至右的順序排列。 

(9) fields_count和fields[ ] 

接下來的是fields_count項,該項的值給出了fields清單項目中的field_info表結構的數量,即表示了該Java型別宣告的類變數和執行個體變數的個數總和。 

fields清單項目包含了在該Java類型中聲明的所有欄位的完整描述。fields列表中的每個field_info表項都完整地表示了一個欄位的資訊,包括該欄位的名稱、描述符和修飾符等。這些資訊有的放在field_info表中,如修飾符;有的則放在field_info表所指向的常量池中,如名字和描述符。同前面的分析,fields清單項目也是一個變長結構。    

需要說明的是,只有在該Java類型中聲明的欄位才可能在fields列表中列出,fields列表中不包括從超類或者超介面中繼承而來的欄位資訊。 

(10) methods_count和methods[ ] 

在Class檔案中,緊接著fields後面的是對在該Java類型中所聲明的方法的描述。首先是methods_count項,它佔兩個位元組長度,它的值表示對該Java類型中聲明的所有方法的總計數。methods_count項後面是methods清單項目,它由methods_count個連續的method_info表構成。每個method_info表都包含了與一個方法相關的資訊,如方法名、描述符(即方法的傳回值及參數類型)以及一些其它資訊。如果一個方法既非abstract也非native,那麼該method_info表將包含該方法局部變數所需的棧空間長度、為方法所捕獲的異常表、位元組碼序列以及可選的行號表和局部變數表等資訊。 

需要說明的是,只有在該Java類型中顯式定義的方法才可能在fields列表中列出,fields列表中不包括從超類或者超介面中繼承而來的方法資訊。 

(11) attributes_count和attributes[ ] 

Class檔案中最後的部分是屬性(attribute),它給出了在該Java類型中所定義的屬性的基本資料。首先是attributes_count項,它佔兩個位元組長度,它的值表示在後續的attributes列表中的attributes_info表的總個數。每個attributes_info表的第一項都是對常量池中CONSTANT_Utf8_info表項的一個索引,該表給出了此屬性的名稱。 

需要說明的是,屬性有很多種,在Class檔案中的很多地方都出現了屬性這一項,在頂層ClassFile表中有attributes屬性項,在field_info表中也有attributes屬性項,在method_info中也有attributes屬性項,但是它們各有各的功能,詳見上述分析。在《JVM Spec》(2nded)中,為ClassFile表結構的attributes清單項目定義的唯一屬性是SourceFile屬性,為field_info表結構的attributes清單項目定義的唯一屬性是ConstantValue屬性,為method_info表結構的attributes清單項目定義的屬性是Code屬性和Exceptions屬性。 

總而言之,Class檔案格式是一個規範性的格式。這個規範指的就是,上面提到的這些表結構本身的規範性,以及這些表結構之間的內含項目關聯性的規範性。實際上,《JVM Spec》(2nded)中就是通過表和項這兩個概念來組織Class檔案的格式的。首先,ClassFile表就是Class檔案最外層的結構,換言之,這就是Class檔案的格式。其次,ClassFile表又是一些項組成的,這些項的內容都要符合《JVM Spec》(2nded)中定義的規範,具體來說,若這個項的類型是基本類型,該項的值要符合規範,例如magic項一定要是0xCAFEBABE,access_flags項的值一定要是有效標誌值等等;若這個項的類型是一個表名,即該項是一個數組項,那麼該數組項列表中的每一個表項都要是一個合法的、規範的表,不能是一個規範中沒有定義的新表,這就是內含項目關聯性的規範性,同樣,清單項目中的每個表項本身也都要是符合其規範定義的表項,例如常量池列表中的某個CONSTANT_Class_info表的name_index項不是對一個CONSTANT_Utf8_info表結構的索引,那麼這個常量池的表項就不是一個合法的表項,因而這個常量池清單項目就是不符合規範的,因而整個檔案就是不符合規範的。 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.