深入理解 JVM Class文件格式(六)
經(jīng)過前幾篇文章, 終于將常量池介紹完了, 之所以花這么大的功夫介紹常量池, 是因為對于理解class文件格式,常量池是必須要了解的, 因為class文件中其他地方,大量引用了常量池中的數(shù)據(jù)項。 對于還不了解常量池的讀者, 如果想要深入了解class文件格式, 或者想繼續(xù)讀這篇博客和本專欄以后的博客, 那么我建議先把我前面的幾篇博客讀一下,把常量池的結(jié)構(gòu)熟悉一下, 對于理解后面的內(nèi)容很有幫助。
雖然介紹完了常量池, 但是class文件中位于常量池下面的內(nèi)容還有很多呢。 接下來, 我們就分析class文件中位于常量池下面的內(nèi)容, 不用擔(dān)心, 只要把常量池搞明白了, 這些內(nèi)容就會很容易理解。
在開始進(jìn)入正文之前, 在這里再次給出class文件的整體格式。 這個表格曾出現(xiàn)在 深入理解Java Class文件格式(一) 這篇文章中。 之所以把這個表格列在這里, 是想再次給讀者一個class文件的總體概覽。表格的內(nèi)容如下:
下面我們就開始介紹class文件中的其他內(nèi)容。
class文件中的訪問標(biāo)志信息
從上面的表格可以看出, 位于常量池下面的2個字節(jié)是access_flags 。 access_flags 描述的是當(dāng)前類(或者接口)的訪問修飾符, 如public, private等, 此外, 這里面還存在一個標(biāo)志位, 標(biāo)志當(dāng)前的額這個class描述的是類, 還是接口。access_flags 的信息比較簡單, 下面列出access_flags 中的各個標(biāo)志位的信息。
其他標(biāo)志就不做介紹了, 這些標(biāo)志都很簡單。 讀者感覺比較陌生的可能是ACC_SUPER這個標(biāo)志。 讀者會想, 類型不能被super關(guān)鍵字修飾啊, 那這個ACC_SUPER是做什么的呢?表中可以看出, 它的含義是:使用新的invokespecial語義 。 invokespecial是一個字節(jié)碼指令, 用于調(diào)用一個方法, 一般情況下, 調(diào)用構(gòu)造方法或者使用super關(guān)鍵字顯示調(diào)用父類的方法時, 會使用這條字節(jié)碼指令。 這正是ACC_SUPER這個名字的由來。 在java 1.2之前, invokespecial對方法的調(diào)用都是靜態(tài)綁定的, 而ACC_SUPER這個標(biāo)志位在java 1.2的時候加入到class文件中, 它為invokespecial這條指令增加了動態(tài)綁定的功能。 這里可能有幾個概念讀者不是很明白, 如靜態(tài)綁定, 動態(tài)綁定等, 這些概念會在以后的博客中詳細(xì)介紹。
還有一點需要說明, 既然access_flags 出現(xiàn)在class文件中的類的層面上, 那么它只能描述類型的修飾符, 而不能描述字段或方法的修飾符, 希望讀者不要將這里的access_flags 和后面要介紹的方法表和字段表中的訪問修飾符相混淆。
此外, 在Java 5 的中, 引入和注解和枚舉的新特性, 那么可以推測, ACC_ANNOTATION 和 ACC_ENUM是在Java 5版本中加入的。 class文件雖然總體上保持前后一致性, 但他也不是一成不變的, 也會跟著Java版本的提升而有所改變, 但是總體來說, class文件格式還是相對穩(wěn)定的, 變動的地方不是很多。
class文件中的this_class
訪問標(biāo)志access_flags 下面的兩個字節(jié)叫做this_class, 它是對當(dāng)前類的描述。 它的兩個字節(jié)的數(shù)據(jù)是對常量池中的一個CONSTANT_Class_info數(shù)據(jù)項的一個索引。 CONSTANT_Class_info在上面的文章中已經(jīng)介紹過了。 CONSTANT_Class_info中有一個字段叫做name_index , 指向一個CONSTANT_Utf8_info , 在這個CONSTANT_Utf8_info 中存放著當(dāng)前類的全限定名。
如果當(dāng)前類為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......... .........這兩項就是當(dāng)前類的信息。 其中索引為1的CONSTANT_Class_info會被class文件中的this_class所引用。 下面給出示例圖(其中虛線范圍內(nèi)表示常量池的區(qū)域):
class文件中的super_class
super_class緊跟在this_class之后。 它和this_class一樣是一個指向常量池數(shù)據(jù)項的索引。 它指向一個CONSTANT_Class_info, 這個CONSTANT_Class_info數(shù)據(jù)項描述的是當(dāng)前類的超類的信息。CONSTANT_Class_info中的name_index指向常量池中的一個CONSTANT_Utf8_info ,CONSTANT_Utf8_info 中存放的是當(dāng)前類的超類的全限定名。 如果沒有顯式的繼承一個,也就是說如果當(dāng)前類是直接繼承Object的, 那么super_class值為0 。 我們在前面的文章中提到過, 如果一個索引值為0, 那么就說明這個索引不引用任何常量池中的數(shù)據(jù)項, 因為常量池中的數(shù)據(jù)項是從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這兩項就是當(dāng)前類的父類的信息。 其中索引為3的CONSTANT_Class_info會被class文件中的super_class引用。 下面給出示例圖(其中虛線范圍內(nèi)表示常量池的區(qū)域):
class文件中的interfaces_count和interfaces
緊接著super_class的是interfaces_count, 表示當(dāng)前類所實現(xiàn)的接口的數(shù)量或者當(dāng)前接口所繼承的超接口的數(shù)量。 注意, 只有當(dāng)前類直接實現(xiàn)的接口才會被統(tǒng)計, 如果當(dāng)前類繼承了另一個類, 而另一個類又實現(xiàn)了一個接口, 那么這個接口不會統(tǒng)計在當(dāng)前類的interfaces_count中。 在interfaces_count后面是interfaces, 他可以看做是一個數(shù)組, 其中的每個數(shù)組項是一個索引, 指向常量池中的一個CONSTANT_Class_info, 這個CONSTANT_Class_info又會引用常量池中的一個CONSTANT_Utf8_info , 這個CONSTANT_Utf8_info 中存放著有當(dāng)前類型直接實現(xiàn)或繼承的接口的全限定名。 當(dāng)前類型實現(xiàn)或繼承了幾個接口, 在interfaces數(shù)組中就會有幾個數(shù)項與之相對應(yīng)。
下面看代碼示例:
package com.jg.zhang;public class Plane implements IFlyable, Cloneable{@Overridepublic void fly() {} }Plane類實現(xiàn)了一個自定義的IFlyable接口, 還實現(xiàn)了一個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......... .........這四項數(shù)據(jù)就是當(dāng)前的Plane類所實現(xiàn)的接口的信息。 第五項和第六項描述了Plane所實現(xiàn)的IFlyable接口, 第七項和第八項描述了Plane所實現(xiàn)的接口Cloneable接口。 下面是示意圖(其中虛線范圍內(nèi)表示常量池的區(qū)域):
總結(jié)
在本篇博客中, 繼續(xù)講解了class文件中常量池以下的部分。 主要講解了三個部分, 分別是this_class , super_class , interfaces_count和interfaces 。 這三個數(shù)據(jù)項分別描述了當(dāng)前類(就是當(dāng)前class文件所在的類), 當(dāng)前類所繼承的超類, 和當(dāng)前類所實現(xiàn)的接口(如果當(dāng)前class文件代表的是一個接口, 那么 interfaces_count和interfaces描述的是當(dāng)前接口所繼承的超接口)。
這幾個數(shù)據(jù)項都持有指向常量池的索引。 真實的信息都是存放在常量池中的, 只不過常量池中的這些信息會被this_class , super_class , interfaces_count和interfaces 引用。
通過本篇博客我們可以知道源文件中的當(dāng)前類, 當(dāng)前類的超類以及當(dāng)前類的超接口在class文件中是如何被描述的。 在下一篇博客中, 將會講解源文件中的定義的字段, 聲明的方法在class文件中是如何描述的。
總結(jié)
以上是生活随笔為你收集整理的深入理解 JVM Class文件格式(六)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最快的懒人减肥方法
- 下一篇: 每天消耗300卡路里能减肥吗