1. ClassFile 基本定義
Classfile 是一個連續的8 位字節二進制流,數據項按照順序存儲在class 文件中,相鄰項沒有間隔,佔多字節空間的項時,高位在前。
ClassFile 文件格式是固定的,按照順序
名稱 |
長度 |
描述 |
備註 |
majic |
4 個字節 |
魔數 :0xCAFEBABE |
Od –x 命令可以看到。這樣保證了 Java 虛擬機能很輕鬆的分辨出 Java 文件和非 Java 文件 |
Minor_version 和 major_version |
分別 2 字節 |
主次版本號: Class 文件格式一旦發生變化,版本號也會隨之變化。 |
如果 class 文件版本號超出了處理範圍, java 虛擬機將不會處理該文件。 |
Constantpool_count,constanpool |
不固定 |
常量池:包含了文件中類和接口相 關的常量。文字字符串、 final 變量值、類名和方法名的常量。常量池的大小平均佔到了整個類大小的 60% 左右。 |
入口列表的形式來存儲。每個常量 池入口都從一個長度爲一個字節的標誌開始。除了字面常量還可以容納字段名稱、方法名稱和類的全限名等。 |
Access_flags |
2 字節 |
訪問標誌 : 定義了類或接口 |
指明瞭是類還是接口、是抽象還是 具體。公共、 final 等修飾符。 |
This_class |
2 字節 |
本身是一個常量池的索引,指向了 常量池中該類全限定名的常量池入口 |
|
Super_class |
2 字節 |
指向父類全限定名 |
|
Interface_count 和 interfaces |
不固定 |
該類實現的接口數量, interfaces 包含了由該類實現的接口的常量池引用。 |
|
FiledsCount 和 fileds |
不固定 |
字段數量和字段的信息表。描述了 字段的類型、描述符等。 |
|
Methods_count 和 Mechods |
不固定 |
方法總數和方法本身。使用 ASM 進行 AOP 編程,通常是通過調整 Method 中的指令來實現的。 |
每一個方法都會有一個 Mechod_info 表,改表記錄了方法的方法名、描述符、返回類型。局部變量表,字節碼序列等。 |
Attributes_count 和 Attributes |
不固定 |
屬性總數和屬性本身。寫出了 |
|
2.class文件的結構
The ClassFile Structure
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 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[attrributes_count];
}
說明:u2表示無符號2字節量,u4表示無符號4字節量。
各字段的說明:
magic
一般二進制文件中都有magic number這一項,這個數說明該文件是一個class文件,它的值是0xcAFEBABE。
minor_version,major_version
class文件的版本號。
constant_pool_count
常量池表中項目數加1得到。從後面的cp_info constant_pool[constant_pool_count-1];也容易理解這個值的意思。
cp_info constant_pool[]
存儲該class文件中使用到的常量信息。
access_flag
說明該class文件的訪問權限和相關屬性,通過位掩碼來設定。
Flag Name |
值 |
解釋 |
ACC_PUBLIC |
0x0001 |
public |
ACC_FINAL |
0x0010 |
final |
ACC_SUPER |
0x0020 |
跟調用invokespecial指令時的處理有關 |
ACC_INTERFACE |
0x0200 |
這是個接口 |
ACC_ABSTRACT |
0x0400 |
abastract |
ACC_SYNTHETIC |
0x1000 |
沒有在源代碼中出現,該類是編譯器生成的 |
ACC_ANNOTATION |
0x2000 |
註解類型 |
ACC_ENUM |
0x4000 |
枚舉類型 |
這些屬性都很容易理解,比如我們定義一個類public final Test{},則Test.class的access_flag中,ACC_PUBLIC和ACC_FINAL是置位的,而ACC_INTERFACE一 項肯定是0。很多類似的組合規則倒是容易自己總結出來,比如如果ACC_INTERFACE置位了,那肯定ACC_ABSTRACT也是置位的,因爲接口 肯定是抽象的;如果ACC_ANNOTATION置位了,那ACC_ANNOTATION肯定同時置位,等等,諸如此類。
畫一個具體的位示意圖表示:
this_class
一個下標,指向常量池表中代表當前類的Constant_Class_info。
super_class
爲0,或者爲一個下標,指向常量池中代表其父類的Constant_Class_info。如果這個值爲0,那麼當前類肯定是Object類。如果 當前類是一個接口,該下標指向的是Object類的描述信息。
interface_count
該類實現的接口數,或者該接口實現的超接口數。
interfaces[]
具體的接口信息,每一項都是常量池表中的一個下標,指向表示接口的Constant_Class_info。
後面幾個依次是字段(field),方法(methods)和屬性的描述,不再贅述,
下面具體的分析一個class文件。源文件很簡單:
public class Test{
public static void main(String[] args){
System.out.println("Hello World!");
}
}
編譯,得到Test.class文件,使用WinHex打開。
前4個字節是magic部分,內容爲0xCAFEBABE;隨後的4個字節是class文件的版本號,這裏主版本號是0x0032,也就是50,這 是使用jdk1.6編譯出的class文件。再後面的2個字節是常量池表的大小信息,0x001D,也就是29,說明常量池表中一共有28項。爲了方便觀 察,可以使用javap -verbose Test得到的結果和當前的二進制文件進行對比。