Java語言從誕生之時就宣稱一次編寫,處處運行的跨平臺特性,其實現原理是源碼文件並無直接編譯成機器指令,而是編譯成Java虛擬機能夠識別和運行的字節碼文件(Class類文件,*.class),字節碼文件是一種平臺無關的中間編譯結果,字節碼文件由java虛擬機讀取,解析和執行,java虛擬機屏蔽了不一樣操做系統和硬件平臺的差別性。java
現在的java虛擬機已經稱爲一種通用平臺,不但可以運行java語言,Groovy,JRuby,Jython等一大批動態語言也能夠直接在Java虛擬機上運行,其原理也是這些動態語言的編譯器將源碼文件編譯爲和Java相同的字節碼文件,這樣Java虛擬機就能夠像執行java語言同樣執行這些動態語言了。web
字節碼class類文件是由一系列字節碼命令組成,用於表示程序中各類常量、變量、關鍵字和運算符號的語義等等。Java的Class類文件是一組以8爲字節爲單位的二進制流,各個數據項嚴格按照順序緊湊地排列在Class類文件之中,中間沒有添加任何分隔符,當遇到須要佔用8位字節以上空間的數據項時,按照高位在前的方式分割成若干個8位字節進行存儲。安全
Java虛擬機規定,Class類文件格式採用相似C語言結構體的僞結構來存儲,這種僞結構中只有兩種數據類型:無符號數和表:併發
(1).無符號數:app
屬於基本類型的數據,以u1, u2, u4, u8來分別表明1個字節,2個字節,4個字節和8個字節的無符號數,無符號數能夠用來描述數字、索引引用、數量值或者按照UTF-8編碼的字符串值。編輯器
(2).表:ide
由多個無符號數或其餘表做爲數據項構成的複合數據類型,因此表都習慣性地以「_info「結尾。表用於描述有層次關係的複合結構數據,整個Class文件本質就是一張表。函數
Java Class類文件結構以下:工具
類型oop |
名稱 |
數量 |
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字節碼文件時,一般會報相似以下的異常:
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來存儲接口數量。
(6). field:
field_info字段表用於描述接口或者類中聲明的變量,field字段包括了類級變量(靜態變量)和實例級變量(成員變量),但不包括方法內部的局部變量。
fields_count字段數目表示Class文件中的類和實例變量總數,字段存放的信息包括:字段訪問標誌、是否靜態、是否final、是否併發可見volatile、是否可序列化transient、數據類型、字段名稱等等。
注意:字段表中不包含從父類或者接口中繼承而來的字段,可是會添加本來代碼中不存在的字段,例如this,以及內部類對外部類訪問而自動添加的外部類實例字段等。
(7).method:
method_info方法表用於描述類或者接口中聲明的方法,methods_count用於表示Class文件中方法總數,method方法存儲了方法的訪問標識、是否靜態、是否final、是否同步synchronized、是否本地方法native、是否抽象方法abstract、方法返回值類型、方法名稱、方法參數列表等信息。
方法的代碼指令並無直接存放在方法表中,而是存放着屬性表中的方法表Code中。
注意:若是父類的方法在子類沒有被重寫,方法表中不會出現來自父類的方法信息,可是編譯器會自動添加類構造器」<clinit>」方法和實例構造器」<init>」方法。
Java編譯器的方法特徵簽名只包括:方法名稱、參數順序和參數類型,不包括方法返回值類型,所以java的方法重載不能經過方法的返回值類區別,可是在Class文件中,方法特徵簽名包括方法的返回值類型,所以Class文件中能夠共存兩個名稱和參數徹底相同而返回值類型不一樣的方法。
(8). attribute:
attribute_info屬性表是Class文件格式中最具擴展性的一種數據項目,用於存放field_info字段表、method_info方法表以及Class文件的專有信息,屬性表不要求各個屬性有嚴格順序,只要求不與已有的屬性名字重複便可,屬性表中存放的經常使用信息以下:
屬性名稱 |
使用位置 |
含義 |
Code |
方法表 |
Java代碼編譯後的字節碼指令 |
ConstantValue |
字段表 |
final關鍵字定義的常量值 |
Deprecated |
類、方法表、字段表 |
被聲明爲Deprecated的字段或方法 |
Exception |
方法表 |
方法拋出的異常 |
InnerClasses |
類文件 |
內部類列表 |
LineNumberTable |
Code屬性 |
java源碼行號和字節碼指令的對應關係 |
LocalVariableTable |
Code屬性 |
方法的局部變量描述 |
SourceFile |
類文件 |
源文件名稱 |
Synthetic |
類、方法表、字段表 |
標識方法或字段爲編譯器自動生成 |
Class文件是二進制文件,使用支持二進制的文本編輯器打開以後顯示的全是二進制數據,很是的不便於閱讀和理解,使用JDK提供的javap工具能夠簡單將Class反編譯,編譯理解Class文件的結構,例子以下:
源碼:
javap反編譯以後的字節碼文件: