標籤:method gravity not 注意 tar 另一個 抽象類別 方法表 ast
轉載:http://blog.csdn.net/zhangjg_blog/article/details/21888943
經過前幾篇文章, 終於將常量池介紹完了, 之所以花這麼大的功夫介紹常量池, 是因為對於理解class檔案格式,常量池是必須要瞭解的, 因為class檔案中其他地方,大量引用了常量池中的資料項目。 對於還不瞭解常量池的讀者, 如果想要深入瞭解class檔案格式, 或者想繼續讀這篇部落格和本專欄以後的部落格, 那麼我建議先把我前面的幾篇部落格讀一下,把常量池的結構熟悉一下, 對於理解後面的內容很有協助。
雖然介紹完了常量池, 但是class檔案中位於常量池下面的內容還有很多呢。 接下來, 我們就分析class檔案中位於常量池下面的內容, 不用擔心, 只要把常量池搞明白了, 這些內容就會很容易理解。
在開始進入本文之前, 在這裡再次給出class檔案的整體格式。 這個表格曾出現在 深入理解Java Class檔案格式(一) 這篇文章中。 之所以把這個表格列在這裡, 是想再次給讀者一個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 |
attribute_count |
1 |
| attribute_info |
attributes |
attributes_count |
下面我們就開始介紹class檔案中的其他內容。
class檔案中的訪問標誌資訊 從上面的表格可以看出, 位於常量池下面的2個位元組是access_flags 。 access_flags 描述的是當前類(或者介面)的存取修飾詞, 如public, private等, 此外, 這裡面還存在一個標誌位, 標誌當前的額這個class描述的是類, 還是介面。access_flags 的資訊比較簡單, 下面列出access_flags 中的各個標誌位的資訊。本來寫這個系列部落格參考的是《深入java虛擬機器》, 但是這本書比較老了, 關於java 5以後的新特性沒有進行解釋,這本書中指列出了5個標誌值, 而最新的JVM規範是針對java 7 的, 其中加入了額外的三個標誌位。 分別是ACC_SYNTHETIC, ACC_ANNOTATION 和 ACC_ENUM 。
| 標誌名 |
標誌值 |
標誌含義 |
針對的對像 |
| ACC_PUBLIC |
0x0001 |
public類型 |
所有類型 |
| ACC_FINAL |
0x0010 |
final類型 |
類 |
| ACC_SUPER |
0x0020 |
使用新的invokespecial語義 |
類和介面 |
| ACC_INTERFACE |
0x0200 |
介面類型 |
介面 |
| ACC_ABSTRACT |
0x0400 |
抽象類別型 |
類和介面 |
| ACC_SYNTHETIC |
0x1000 |
該類不由使用者代碼產生 |
所有類型 |
| ACC_ANNOTATION |
0x2000 |
註解類型 |
註解 |
| ACC_ENUM |
0x4000 |
枚舉類型 |
枚舉 |
其他標誌就不做介紹了, 這些標誌都很簡單。 讀者感覺比較陌生的可能是ACC_SUPER這個標誌。 讀者會想, 類型不能被super關鍵字修飾啊, 那這個ACC_SUPER是做什麼的呢?表中可以看出, 它的含義是:使用新的invokespecial語義 。 invokespecial是一個位元組碼指令, 用於調用一個方法, 一般情況下, 調用構造方法或者使用super關鍵字顯示調用父類的方法時, 會使用這條位元組碼指令。 這正是ACC_SUPER這個名字的由來。 在java 1.2之前, invokespecial對方法的調用都是靜態繫結的, 而ACC_SUPER這個標誌位在java 1.2的時候加入到class檔案中, 它為invokespecial這條指令增加了動態綁定的功能。 這裡可能有幾個概念讀者不是很明白, 如靜態繫結, 動態綁定等, 這些概念會在以後的部落格中詳細介紹。 還有一點需要說明, 既然access_flags 出現在class檔案中的類的層面上, 那麼它只能描述類型的修飾符, 而不能描述欄位或方法的修飾符, 希望讀者不要將這裡的access_flags 和後面要介紹的方法表和欄位表中的存取修飾詞相混淆。 此外, 在Java 5 的中, 引入和註解和枚舉的新特性, 那麼可以推測, ACC_ANNOTATION 和 ACC_ENUM是在Java 5版本中加入的。 class檔案雖然總體上保持前後一致性, 但他也不是一成不變的, 也會跟著Java版本的提升而有所改變, 但是總體來說, class檔案格式還是相對穩定的, 變動的地方不是很多。
class檔案中的this_class
訪問標誌access_flags 下面的兩個位元組叫做this_class, 它是對當前類的描述。 它的兩個位元組的資料是對常量池中的一個CONSTANT_Class_info資料項目的一個索引。 CONSTANT_Class_info在上面的文章中已經介紹過了。 CONSTANT_Class_info中有一個欄位叫做name_index , 指向一個CONSTANT_Utf8_info , 在這個CONSTANT_Utf8_info 中存放著當前類的全限定名。 如果當前類為Person:
- package com.jg.zhang;
-
- public class Person {
-
- int age;
-
- int getAge(){
- return age;
- }
- }
將Person.class反編譯後, 可以在常量池中看到如下兩項:
- Constant pool:
- #1 = Class #2 // com/jg/zhang/Person
- #2 = Utf8 com/jg/zhang/Person
-
- .........
- .........
這兩項就是當前類的資訊。 其中索引為1的CONSTANT_Class_info會被class檔案中的this_class所引用。 下面給出樣本圖(其中虛線範圍內表示常量池的地區): class檔案中的super_class super_class緊跟在this_class之後。 它和this_class一樣是一個指向常量池資料項目的索引。 它指向一個CONSTANT_Class_info, 這個CONSTANT_Class_info資料項目描述的是當前類的超類的資訊。CONSTANT_Class_info中的name_index指向常量池中的一個CONSTANT_Utf8_info ,CONSTANT_Utf8_info 中存放的是當前類的超類的全限定名。 如果沒有顯式的繼承一個,也就是說如果當前類是直接繼承Object的, 那麼super_class值為0 。 我們在前面的文章中提到過, 如果一個索引值為0, 那麼就說明這個索引不引用任何常量池中的資料項目, 因為常量池中的資料項目是從1開始的。 也就是說, 如果一個類的class檔案中的super_class為0 , 那麼就代表該類直接繼承Object類。 下面以代碼來說明:
- package com.jg.zhang;
-
- public class Programer extends Person{
-
- Computer computer;
-
- public Programer(Computer computer){
- this.computer = computer;
- }
-
- public void doWork(){
- computer.calculate();
- }
- }
上面的Programer類繼承自Person類。 那麼反編譯Programer .class , 它的常量池中會存在如下資訊:
- Constant pool:
-
- .........
- .........
-
- #3 = Class #4 // com/jg/zhang/Person
- #4 = Utf8 com/jg/zhang/Person
這兩項就是當前類的父類的資訊。 其中索引為3的CONSTANT_Class_info會被class檔案中的super_class引用。 下面給出樣本圖(其中虛線範圍內表示常量池的地區): class檔案中的interfaces_count和interfaces 緊接著super_class的是interfaces_count, 表示當前類所實現的介面的數量或者當前介面所繼承的超介面的數量。 注意, 只有當前類直接實現的介面才會被統計, 如果當前類繼承了另一個類, 而另一個類又實現了一個介面, 那麼這個介面不會統計在當前類的interfaces_count中。 在interfaces_count後面是interfaces, 他可以看做是一個數組, 其中的每個數組項是一個索引, 指向常量池中的一個CONSTANT_Class_info, 這個CONSTANT_Class_info又會引用常量池中的一個CONSTANT_Utf8_info , 這個CONSTANT_Utf8_info 中存放著有當前類型直接實現或繼承的介面的全限定名。 當前類型實現或繼承了幾個介面, 在interfaces數組中就會有幾個數項與之相對應。 下面看程式碼範例:
- package com.jg.zhang;
-
- public class Plane implements IFlyable, Cloneable{
-
- @Override
- public void fly() {
-
- }
- }
Plane類實現了一個自訂的IFlyable介面, 還實現了一個JDK中的Cloneable介面, 那麼它的常量池中會有如下資訊:
- Constant pool:
-
- .........
- .........
-
- #5 = Class #6 // com/jg/zhang/IFlyable
- #6 = Utf8 com/jg/zhang/IFlyable
- #7 = Class #8 // java/lang/Cloneable
- #8 = Utf8 java/lang/Cloneable
-
- .........
- .........
這四項資料就是當前的Plane類所實現的介面的資訊。 第五項和第六項描述了Plane所實現的IFlyable介面, 第七項和第八項描述了Plane所實現的介面Cloneable介面。 下面是(其中虛線範圍內表示常量池的地區):
總結 在本篇部落格中, 繼續講解了class檔案中常量池以下的部分。 主要講解了三個部分, 分別是this_class , super_class , interfaces_count和interfaces 。 這三個資料項目分別描述了當前類(就是當前class檔案所在的類), 當前類所繼承的超類, 和當前類所實現的介面(如果當前class檔案代表的是一個介面, 那麼 interfaces_count和interfaces描述的是當前介面所繼承的超介面)。 這幾個資料項目都持有指向常量池的索引。 真實的資訊都是存放在常量池中的, 只不過常量池中的這些資訊會被this_class , super_class , interfaces_count和interfaces 引用。 通過本篇部落格我們可以知道源檔案中的當前類, 當前類的超類以及當前類的超介面在class檔案中是如何被描述的。 在下一篇部落格中, 將會講解源檔案中的定義的欄位, 聲明的方法在class檔案中是如何描述的。
(6) 深入瞭解Java Class檔案格式(六)