java語言是跨平台的,所謂一次編寫,到處運行。之所以是跨平台的,就是 java定義了一套與作業系統,硬體無關的位元組碼格式,這個位元組碼就是用 java class檔案來表示的, java class檔案內部定義了虛擬機器可以識別的位元組碼格式,這個格式是平台無關性的,在 linux系統或者在 windows系統上都是一致的。這個就好比 html檔案,我們定義好規範,這個系統只要去按照規範顯示出來裡面的內容就好了。好比 html就是 class檔案,瀏覽器就是虛擬機器一樣,通過瀏覽器去執行 html的渲染過程,我們無論是用手機, Windows系統,蘋果系統上網,顯示出來的內容都是一樣。 java虛擬機器可以從 class檔案中載入預定義的位元組碼,也可以從網路,資料庫,訊息檔案中載入位元組碼。
如今的java虛擬機器已經稱為一種通用平台,不但能夠運行java語言,Groovy,JRuby,Jython等一大批動態語言也可以直接在Java虛擬機器上運行,其原理也是這些動態語言的編譯器將源碼檔案編譯為和Java相同的位元組碼檔案,這樣Java虛擬機器就可以像執行java語言一樣執行這些動態語言了。
位元組碼class類檔案是由一系列位元組碼命令組成,用於表示程式中各種常量、變數、關鍵字和運算子號的語義等等。
Java的Class類檔案是一組以8為位元組為單位的二進位流,各個資料項目嚴格按照順序緊湊地排列在Class類檔案之中,中間沒有添加任何分隔字元,
當遇到需要佔用8位位元組以上空間的資料項目時,按照高位在前的方式分割成若干個8位位元組進行儲存。
Java虛擬機器規定,Class類檔案格式採用類似C語言結構體的偽結構來儲存,這種偽結構中只有兩種資料類型:無符號數和表:
(1).無符號數:
屬於基本類型的資料,以u1, u2, u4, u8來分別代表1個位元組,2個位元組,4個位元組和8個位元組的無符號數,無符號數可以用來描述數字、索引引用、數量值或者按照UTF-8編碼的字串值。
(2).表:
由多個無符號數或其他表作為資料項目構成的複合資料型別,所以表都習慣性地以“_info“結尾。表用於描述有層次關係的複合結構資料,整個Class檔案本質就是一張表。
Java Class類檔案結構如下:
| 類型 |
名稱 |
數量 |
| u4 |
magic |
1 |
| u2 |
minor_version |
1 |
| u2 |
major_version |
1 |
| u2 |
constant_pool_count |
1 |
| cp_info |
constant_pool |
constant_pool_count-1 |
| u2 |
access_flags |
1 |
| u2 |
this_class |
1 |
| u2 |
super_class |
1 |
| u2 |
interfaces_count |
1 |
| u2 |
interfaces |
interfaces_count |
| u2 |
fields_count |
1 |
| field_info |
fields |
fields_count |
| u2 |
methods_count |
1 |
| method_info |
methods |
methods_count |
| u2 |
attributes_count |
1 |
| attribute_info |
attributes |
attributes_count |
Class類檔案沒有任何分隔字元,是嚴格按照這個結構表順序排列,下面具體介紹各個名稱含義:
(1).magic:
每個Class檔案的頭4個位元組被稱為魔數,它的唯一作用是用於確定這個檔案是否為一個能被java虛擬機器所接收的Class類檔案,即用於判定檔案是否是符合規範的java Class檔案。雖然說尾碼名“.class”可以表明檔案是一個Class檔案,但是檔案尾碼名是可以隨意被改動的,基於安全的考慮,很多檔案都通過魔數值來唯一確定檔案類型,java的Class檔案魔數是:0xCAFEBABE.
(2).minor_version和major_version:
每個Class檔案的第5和第6個位元組代表Class檔案的次版本號碼,第7和第8個位元組代表Class檔案的主要版本號。Class檔案的主、次版本號碼是由JDK決定的,JDK1.0~JDK1.1使用了45.0~45.3的版本號碼(45是主要版本好,點”.“之後的是次版本號碼),從JDK1.1開始,每個大版本的JDK主要版本號加1.
Class主、次版本號碼的一個作用時,高版本的Java虛擬機器可以向前相容,運行低版本JDK編譯的Class位元組碼檔案,而低版本的java虛擬機器不能運行高版本JDK編譯的Class位元組碼檔案。當低版本的java虛擬機器運行高版本JDK編譯的Class位元組碼檔案時,通常會報類似如下的異常:
1. Exception in thread "main" java.lang.UnsupportedClassVersionError: a (Unsupporte
2. d major.minor version 49.0)
JDK1.0~JDK1.1使用了45.0~45.3的版本號碼,JDK1.2使用了46.0~46.65535的版本號碼,JDK1.3使用了47.0~47.65535的版本號碼,JDK1.4使用了48.0~48.65535的版本號碼,JDK1.5使用了49.0~49.65535的版本號碼,JDK1.6使用了50.0~50.65535的版本號碼,JDK1.7使用51.0~51.65535的版本號碼。
在編譯時間可以通過指定-target參數來改變主要版本號,如JDK1.6編譯時間如果沒有給定target參數,則編譯出來的Class檔案的主要版本號是50,如果給定”-target 1.4 -source 1.4”參數之後,則主要版本將變為48,如果給定”-target 1.5 ”參數之後,則主要版本將變為49。
(3). constant_pool_count和constant_pool:
constant_pool_count代表Class檔案中常量池的數目,由於常量池的計數從1開始,因此常量池的容量是constant_pool_count-1。
第0項常量空出做特殊考慮,為了滿足一些指向常量池的索引值在某些特定情況下需要表達“不指向任何一個常量池”的意思。
constant_pool常量池是Class類檔案中出現的第一個表類型資料,常量池主要存放兩大類常量:
a.字面量(Literal):包括文本字串、final類型常量值。
b.符號引用(SymbolicReferences):包括類和介面的全限定名、欄位的名稱和描述符、方法的名稱和描述符。
(4). access_flags:用於表示Class或介面層次的訪問標誌,即類或介面層面的存取控制資訊,通常儲存的資訊包括:Class類檔案是類、介面、枚舉或是註解;是否定義為public類型;是否定義為abstract類型;類是否被定義為final等等。
(5). this_class、super_class和interfaces:this_class類索引用於確定類的全限定名,super_class父類索引用於確定父類的全限定名,interfaces介面索引用於確定介面的全限定名,由於java中可以實現多個介面,因此使用interfaces_count來儲存介面數量。