Java是與平臺無關的語言,「一次編寫,處處運行」,
這一方面依賴於Java源代碼編譯後生成的存儲字節碼的文件,即Class文件是語言和平臺無關的;html
另外一方面依賴於Java虛擬機的實現。java
Java虛擬機並不關心Class的來源是什麼語言,只要它符合必定的結構,就能夠在Java中運行。
Java語言中的各類變量、關鍵字和運算符的語義最終都是由多條字節碼命令組合而成的,
所以字節碼命令所能提供的語義描述能力確定會比Java語言自己更強大,
這便爲其餘語言實現一些有別於Java的語言特性提供了基礎,並且這也正是在類加載時要進行安全驗證的緣由。安全
class文件是一種8位字節的二進制流文件, 各個數據項按順序緊密的從前向後排列, 相鄰的項之間沒有間隙, 這樣可使得class文件很是緊湊, 體積輕巧, 能夠被JVM快速的加載至內存, 而且佔據較少的內存空間。
咱們的Java源文件, 在被編譯以後, 每一個類(或者接口)都單獨佔據一個class文件, 而且類中的全部信息都會在class文件中有相應的描述, 因爲class文件很靈活, 它甚至比Java源文件有着更強的描述能力。this
class文件中存在如下數據項:spa
類型 | 名稱 | 數量 |
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 | attribute_count | 1 |
attribute_info | attributes | attributes_count |
不管是無符號數仍是表,當須要描述同一類型但數量不定的多個數據時,常常會在其前面使用一個前置的容量計數器來記錄其數量,而便跟着若干個連續的數據項,稱這一系列連續的某一類型的數據爲某一類型的集合,如:fields_count個field_info表數據便組成了方法表集合。htm
(1)魔數和版本號 magic minor_version&major_version
每一個Class文件的頭4個字節稱爲魔數(magic),它的惟一做用是判斷該文件是否爲一個能被虛擬機接受的Class文件。它的值固定爲0xCAFEBABE。
緊接着magic的4個字節存儲的是Class文件的次版本號和主版本號,通常狀況下, 高版本的JVM能識別低版本的javac編譯器編譯的class文件, 而低版本的JVM不能識別高版本的javac編譯器編譯的class文件。 若是使用低版本的JVM執行高版本的class文件, JVM會拋出java.lang.UnsupportedClassVersionError 。blog
(2)常量池計數和常量池 constant_pool_count&constant_pool
常量池是class文件中的一項很是重要的數據。 常量池中存放了文字字符串, 常量值, 當前類的類名, 字段名, 方法名, 各個字段和方法的描述符, 對當前類的字段和方法的引用信息, 當前類中對其餘類的引用信息等等。
常量池中主要存放兩大類常量:字面量和符號引用。字面量比較接近於Java層面的常量概念,如文本字符串、被聲明爲final的常量值等。而符號引用總結起來則包括了下面三類常量:繼承
類和接口的全限定名(即帶有包名的Class名)
字段的名稱和描述符(private、static等描述符)
方法的名稱和描述符(private、static等描述符)索引
(3) 訪問標誌 access_flag 接口
在常量池結束以後,緊接着的2個字節表明訪問標誌(access_flag),這個標誌用於識別一些類或接口層次的訪問信息,包括:這個Class是類仍是接口,是否認義爲public類型,abstract類型,若是是類的話,是否聲明爲final,等等。每種訪問信息都由一個十六進制的標誌值表示,若是同時具備多種訪問信息,則獲得的標誌值爲這幾種訪問信息的標誌值的邏輯或。
(4) 類索引和父類索引 接口索引集合 this_class、super_class、interfaces
類索引(this_class)和父類索引(super_class)都是一個u2類型的數據,而接口索引集合(interfaces)則是一組u2類型的數據集合,Class文件中由這三項數據來肯定這個類的繼承關係。類索引、父類索引和接口索引集合都按照順序排列在訪問標誌以後,類索引和父類索引兩個u2類型的索引值表示,它們各自指向一個類型爲COMNSTANT_Class_info的類描述符常量,經過該常量中的索引值找到定義在COMNSTANT_Utf8_info類型的常量中的全限定名字符串。而接口索引集合就用來描述這個類實現了哪些接口,這些被實現的接口將按implements語句(若是這個類自己是個接口,則應當是extend語句)後的接口順序從左到右排列在接口的索引集合中。
(5)字段表 fields
字段表(field_info)用於描述接口或類中聲明的變量。字段包括了類級變量或實例級變量,但不包括在方法內聲明的變量。字段的名字、數據類型、修飾符等都是沒法固定的,只能引用常量池中的常量來描述。
(6)方法表 methods
方法表(method_info)的結構與屬性表的結構相同,方法裏的Java代碼,通過編譯器編譯成字節碼指令後,存放在方法屬性表集合中一個名爲「Code」的屬性裏。
與字段表集合相對應,若是父類方法在子類中沒有被覆寫,方法表集合中就不會出現來自父類的方法信息。但一樣,有可能會出現由編譯器自動添加的方法,最典型的即是類構造器「<clinit>」方法和實例構造器「<init>」方法。
在Java語言中,要重載一個方法,除了要與原方法具備相同的簡單名稱外,還要求必須擁有一個與原方法不一樣的特徵簽名,特徵簽名就是一個方法中各個參數在常量池中的字段符號引用的集合,也就是由於返回值不會包含在特徵簽名之中,所以Java語言裏沒法僅僅依靠返回值的不一樣來對一個已有方法進行重載。
(7)屬性表 attributes
屬性表(attribute_info)在前面已經出現過多系,在Class文件、字段表、方法表中均可以攜帶本身的屬性表集合,以用於描述某些場景專有的信息。
static修飾的字段在類加載過程當中的準備階段被初始化爲0或null等默認值,然後在初始化階段(觸發類構造器<clinit>)纔會被賦予代碼中設定的值,若是沒有設定值,那麼它的值就爲默認值。
final修飾的字段在運行時被初始化(能夠直接賦值,也能夠在實例構造器中賦值),一旦賦值便不可更改;
static final修飾的字段在Javac時生成ConstantValue屬性,在類加載的準備階段根據ConstantValue的值爲該字段賦值,它沒有默認值,必須顯式地賦值,不然Javac時會報錯。能夠理解爲在編譯期即把結果放入了常量池中。
轉:http://www.cnblogs.com/binyue/p/4560484.html