1. 程式人生 > >Class類檔案的結構

Class類檔案的結構

        JAVA實現平臺無關性的基礎是虛擬機器和位元組碼儲存格式,使用Java編譯器可以把Java程式碼編譯為儲存位元組碼的Class檔案,使用JRuby等其他語言的編譯器一樣可以把程式程式碼編譯成Class檔案,虛擬機器並不關心Class的來源是什麼語言,只要它符合Class檔案應有的結構就可以在Java虛擬機器中執行。

        Java語言中的各種變數、關鍵字和運算子號的語義最終都是由多條位元組碼命令組合而成的,因此位元組碼命令所能提供的語義描述能力肯定比Java語言本身更強大。Class檔案是一組以8位位元組為基礎單位的二進位制流,各個資料專案嚴格按照順序緊湊地排列在Class檔案之中,中間沒有新增任何分隔符,這使得整個Class檔案儲存的內容幾乎全部都是程式執行的必要資料,沒有空隙。當遇到需要佔用8位位元組以上空間的資料項時,則會按照高位在前的方式分割成若干個8位位元組進行儲存。

        根據Java虛擬機器規範的規定,Class檔案格式採用一種類似於C語言結構體的偽結構表示。與C語言結構體的域不同,連續的項Java Class檔案中順序儲存,不進行填充或者對齊。在《JVM Specification》中式這樣定義Class檔案的結構:

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];
   }

        接下來我們一起看看這個表中各個資料項的具體含義。使用下面的測試類進行詳細說明:

package com.igood;

public class TestClass {
	private int member;//int型別欄位
	public static String HOME_URL="http://www.baidu.com";//static字元欄位
	public final float PI = 3.14159F;//final欄位
	//建構函式
	public TestClass(){
		
	}
	//getMember函式
	public int getMember() {
		return member;
	}	
}
        編譯完成的類檔案如下:

1、magic

        每個Class檔案的頭4個位元組稱為魔數,它的唯一作用是用於確定這個檔案是否為一個能被虛擬機器接受的Class檔案,它的值為0xCAFEBABE。


2、minor_version和major_version         緊接著魔數的4個位元組儲存的是Class檔案的版本號:第5、6個位元組是次版本號(minor_version),第7、8位元組是主版本號(major_version)。Java的版本號從45開始。


3、常量池

        緊接著主次版本號之後的是常量池入口,由於常量池中常量的數量是不固定的,所以再常量池的入口需要放置一項u2的型別的資料,代表常量池容量計數值(constant_pool_count),這個容量計數是從1而不是從0開始的。常量池(constant_pool)儲存了諸如符號常量、final常量值、基本資料型別的字面值等內容。在jVM的標頭檔案jvm/vmcommon/h/pool.h中,有以下對常量池項型別的定義:

#define CONSTANT_Utf8                       1 //UTF-8編碼的Unicode字串
#define CONSTANT_Integer                    3 //int型別的字面值
#define CONSTANT_Float                      4 //float型別的字面值
#define CONSTANT_Long                       5 //long型別的字面值
#define CONSTANT_Double                     6 //double型別的字面值
#define CONSTANT_Class                      7 //對一個類或介面的符號引用
#define CONSTANT_String                     8 //String型別字面值的引用
#define CONSTANT_Fieldref                   9 //對一個欄位的符號引用
#define CONSTANT_Methodref                  10 //對一個類中方法的符號引用
#define CONSTANT_InterfaceMethodref         11 //對一個介面中方法的符號引用
#define CONSTANT_NameAndType                12 //對一個欄位或方法的部分符號引用
constant_pool:表型別資料集合,即常量池中每一項常量都是一個表,共有11種結構各不相同的表結構資料。這11種表都有一個共同的特點,即均由一個u1型別的標誌位開始,可以通過這個標誌位來判斷這個常量屬於哪種常量型別,常量型別及其資料結構如下表所示:

型別

簡介

專案

型別

描述

CONSTANT_Utf8_info

utf-8縮略編碼字串

tag

u1

值為1

length

u2

utf-8縮略編碼字串佔用位元組數

bytes

u1

長度為length的utf-8縮略編碼字串

CONSTANT_Integer_info

整形字面量

tag

u1

值為3

bytes

u4

按照高位在前儲存的int值

CONSTANT_Float_info

浮點型字面量

tag

u1

值為4

bytes

u4

按照高位在前儲存的float值

CONSTANT_Long_info

長整型字面量

tag

u1

值為5

bytes

u8

按照高位在前儲存的long值

CONSTANT_Double_info

雙精度浮點型字面量

tag

u1

值為6

bytes

u8

按照高位在前儲存的double值

CONSTANT_Class_info

類或介面的符號引用

tag

u1

值為7

index

u2

指向全限定名,常量項的索引

CONSTANT_String_info

字串型別字面量

tag

u1

值為8

index

u2

指向字串字面量的索引

CONSTANT_Fieldref_info

欄位的符號引用

tag

u1

值為9

index

u2

指向宣告欄位的類或介面描述符CONSTANT_Class_info的索引項

index

u2

指向欄位描述符CONSTANT_NameAndType_info的索引項

CONSTANT_Methodref_info

類中方法的符號引用

tag

u1

值為10

index

u2

指向宣告方法的類描述符CONSTANT_Class_info的索引項

index

u2

指向名稱及型別描述符CONSTANT_NameAndType_info的索引項

CONSTANT_InterfaceMethodref_info

介面中方法的符號引用

tag

u1

值為11

index

u2

指向宣告方法的介面描述符CONSTANT_Class_info的索引項

index

u2

指向名稱及型別描述符CONSTANT_NameAndType_info的索引項

CONSTANT_NameAndType_info

欄位或方法的部分符號引用

tag

u1

值為12

index

u2

指向該欄位或方法名稱常量項的索引

index

u2

指向該欄位或方法描述符常量項的索引


4、access_flags(訪問標誌) 佔用2個位元組。用來表明該class檔案中定義的是類還是介面,訪問修飾符是否定義為public;是否定義為abstract型別。類是否是final的。

標誌名稱

標誌值

含義

ACC_PUBLIC

0x0001

是否為public型別

ACC_FINAL

0x0010

是否被宣告為final,只有類可設定

ACC_SUPER

0x0020

是否允許使用invokespecial位元組碼指令,JDK1.2以後編譯出來的類這個標誌為真

ACC_INTERFACE

0x0200

標識這是一個介面

ACC_ABSTRACT

0x0400

是否為abstract型別,對於介面和抽象類,此標誌為真,其它類為假

ACC_SYNTHETIC

0x1000

標識別這個類並非由使用者程式碼產生

ACC_ANNOTATION

0x2000

標識這是一個註解

ACC_ENUM

0x4000

標識這是一個列舉



         根據上面的表格,TestClass類的訪問標誌為0x0021 = ACC_PUBLIC | ACC_SUPER = 0x0001 | 0x0020

5、this_class 佔用2個位元組。 它是一個對常量池的索引。指向的是常量池中儲存類名符號引用的CONSTANT_Class_info常量表(見下面常量池具體結構)。比如this_class=0x0001。則表示指向常量池中的第一個常量表。通常這個表是指向當前class檔案所定義的類名。


6、super_class 佔用2個位元組 與this_class類似,指向存放當前class檔案所定義類的超類名字的索引的CONSTANT_Class_info常量表。


7、inteface_count、interfaces 

        interface_count是class檔案所定義的類直接實現的介面或父類實現的介面的數量。佔2個位元組。intefaces包含了對每個介面的CONSTANT_Class_info常量表的索引。


由於interface_count為0,所以介面索引集合interfaces大小為0,即在編譯後的二進位制檔案中不存在interfaces這項內容。

8、fields_count、fields

        fields_count欄位計數器,表明了類中欄位的數量 。fields是不同長度的field_info表的序列。這些field_info表中並不包含超類或父介面繼承而來的欄位。field_info表展示了一個欄位的資訊,包括欄位的名字,描述符和修飾符。如果該欄位是final的,那麼還會展示其常量值。注意,這些資訊有些存放在field_info裡面,有些則存放在field_info所指向的常量池中。fields:欄位表集合,一組欄位表型別資料的集合,欄位表包括access_flags,name_index,descriptor_index,attributes_count和attributes[attributes_count]


9、method_count、methods

        與欄位類似,method_count表明類中方法的數量和每個方法的常量表的索引。methods表明了不同長度的method_info表的序列。methods:方法表集合,一組方法表型別資料的集合。方法表結構和欄位表結構一樣包括access_flags,name_index,descriptor_index,attributes_count和attributes[attributes_count]

標誌名稱

標誌值

含義

ACC_PUBLIC

0x0001

欄位是否為public

ACC_PRIVATE

0x0002

欄位是否為private

ACC_PROTECTED

0x0004

欄位是否為protected

ACC_STATIC

0x0008

欄位是否為static

ACC_FINAL

0x0010

欄位是否為final

ACC_SYNCHRONIZED

0x0020

欄位是否為synchronized

ACC_BRIDGE

0x0040

方法是否是由編譯器產生的橋接方法

ACC_VARARGS

0x0080

方法是否接受不定引數

ACC_NATIVE

0x0100

欄位是否為native

ACC_ABSTRACT

0x0400

欄位是否為abstract

ACC_STRICTFP

0x0800

欄位是否為strictfp

ACC_SYNTHETIC

0x1000

欄位是否為編譯器自動產生




10、attributes_count 和 attributes屬性表

         在Class檔案、屬性表、方法表中都可以包含自己的屬性表集合,用於描述某些場景的專有資訊與Class檔案中其它資料項對長度、順序、格式的嚴格要求不同,屬性表集合不要求其中包含的屬性表具有嚴格的順序,並且只要屬性的名稱不與已有的屬性名稱重複,任何人實現的編譯器可以向屬性表中寫入自己定義的屬性資訊。