通過前幾篇文章, 終於將常量池介紹完了, 之因此花這麼大的功夫介紹常量池, 是由於對於理解class文件格式,常量池是必需要了解的, 由於class文件中其餘地方,大量引用了常量池中的數據項。 對於還不瞭解常量池的讀者, 若是想要深刻了解class文件格式, 或者想繼續讀這篇博客和本專欄之後的博客, 那麼我建議先把我前面的幾篇博客讀一下,把常量池的結構熟悉一下, 對於理解後面的內容頗有幫助。 java
雖然介紹完了常量池, 可是class文件中位於常量池下面的內容還有不少呢。 接下來, 咱們就分析class文件中位於常量池下面的內容, 不用擔憂, 只要把常量池搞明白了, 這些內容就會很容易理解。 數組
在開始進入正文以前, 在這裏再次給出class文件的總體格式。 這個表格曾出如今 深刻理解Java Class文件格式(一) 這篇文章中。 之因此把這個表格列在這裏, 是想再次給讀者一個class文件的整體概覽。表格的內容以下:ide
類型 | 名稱 | 數量 |
---|---|---|
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文件中的其餘內容。this
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 。spa
標誌名 | 標誌值 | 標誌含義 | 針對的對像 |
---|---|---|---|
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這條指令增長了動態綁定的功能。 這裏可能有幾個概念讀者不是很明白, 如靜態綁定, 動態綁定等, 這些概念會在之後的博客中詳細介紹。code
還有一點須要說明, 既然access_flags 出如今class文件中的類的層面上, 那麼它只能描述類型的修飾符, 而不能描述字段或方法的修飾符, 但願讀者不要將這裏的access_flags 和後面要介紹的方法表和字段表中的訪問修飾符相混淆。blog
此外, 在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文件中是如何描述的。