天天看點

Class檔案結構全面解析(下)

上一篇文章分享了Class檔案的主要構成,同時也詳細分析了魔數、次版本号、主版本号、常量池集合、通路标志的構造,接下來繼續詳細分析類索引、父類索引、接口索引集合、字段表集合、方法表集合和屬性表集合。

接上回書

書接上一回,分享了Class檔案的主要構成,同時也詳細分析了魔數、次版本号、主版本号、常量池集合、通路标志的構造,接下來我們就繼續學習。

歡迎關注微信公衆号:

萬貓學社

,每周一分享Java技術幹貨。

類索引和父類索引

類索引(this_class)和父類索引(super_class)都是一個u2類型的資料,類索引用于确定這個類的全限定名,父類索引用于确定這個類的父類全限定名。由于java語言不允許多重繼承,是以父類索引隻有一個。

類索引和父類索引各自指向常量池中類型為CONSTANT_Class_info的類描述符,再通過類描述符中的索引值找到常量池中類型為CONSTANT_Utf8_info的字元串。再來看一下之前的Class檔案例子:

Class檔案結構全面解析(下)

結合之前javap分析出來的常量池内容:

#3 = Class         #17        // OneMoreStudy
   #4 = Class         #18        // java/lang/Object
  #17 = Utf8          OneMoreStudy
  #18 = Utf8          java/lang/Object
           

類索引為0x0003,去常量池裡找索引為3的類描述符,類描述符中的索引為17,再去找索引為17的字元串,就是“OneMoreStudy”。

父類索引為0x0004,去常量池裡找索引為4的類描述符,類描述符中的索引為18,再去常量池裡找索引為18的字元串,就是“java/lang/Object”。

接口索引集合

接口索引集合(interface)是一組u2類型的資料的集合,由于java語言允許實作多個接口,是以接口索引也有多個,它們按照implements語句後的接口順序從左到右依次排列在接口索引集合中。接口索引集合的第一項資料是接口集合計數值(interfaces_count),表示有多少接口索引。如果該類沒有實作任何接口,那麼該計數值為0,後面的接口索引表不占任何位元組。之前的例子OneMoreStudy類沒有實作任何接口,是以接口集合計數值就是0,如下圖:

Class檔案結構全面解析(下)

字段表集合

字段表(field_info)是用來描述接口或類中聲明的變量。包括類級變量(靜态變量)和執行個體級變量(成員變量),但是不包括在方法内部聲明的局部變量。具體結構如下表:

類型 名稱 數量 描述
u2 access_flags 1 字段的通路标志
name_index 字段的簡單名稱索引
descriptor_index 字段的描述符索引
attributes_count 字段的屬性計數值
attribute_info attributes 字段的屬性

字段表中的access_flags,和類的access_flags是非常類似的,但是辨別和含義是不一樣的。具體如下表:

标志名稱 标志值 含義
ACC_PUBLIC 0x0001 字段是否public
ACC_PRIVATE 0x0002 字段是否private
ACC_PROTECTED 0x0004 字段是否protected
ACC_STATIC 0x0008 字段是否static
ACC_FINAL 0x0010 字段是否為final
ACC_VOLATILE 0x0040 字段是否volatile
ACC_TRANSIENT 0x0080 字段是否transient
ACC_SYNTHETIC 0x1000 字段是否由編譯器自動産生的
ACC_ENUM 0x4000 字段是否enum

這裡提到了簡單名稱、描述符,和全限定名有什麼差別呢?稍微說一下。

簡單名稱是沒有類型和參數修飾的方法或字段名稱,比如OneMoreStudy類中的number字段和plusOne()方法的簡單名稱分别是“number”和“plusOne”。

全限定名是把類全名中的“.”替換成“/”就可以了,比如java.lang.Object類的全限定名就是“java/lang/Object”。

描述符是用來描述字段的資料類型、方法的參數清單(包括數量、類型以及順序)和傳回值。基礎資料類型和無傳回的void類型都有一個大寫字母表示,對象類型用字元L加對象的全限定名來表示,如下表:

辨別字元
B 基本類型byte
C 基本類型char
D 基本類型double
F 基本類型float
I 基本類型int
J 基本類型long
S 基本類型short
Z 基本類型boolean
V 特殊類型void
L 對象類型 如 Ljava/lang/Object

對于數組類型,每一次元使用一個前置的“[”字元來描述,比如java.lang.Object[][]的二維資料,就是“[[Ljava/lang/Object”。在描述方法時,按照先參數清單,後傳回值的順序描述,參數清單按照嚴格順序放在“()”值中,比如boolean equals(Object anObject),就是“(Ljava/lang/Object)B”。

再來看一下之前的Class檔案例子:

Class檔案結構全面解析(下)

OneMoreStudy類中隻有一個字段number,是以字段計數值為0x0001。字段number隻被private修飾,沒有其他修飾,是以字段的通路标志位為0x0002。字段的簡單名稱索引是0x0005,去常量池中找索引為5的字元串,為“number”。字段的描述符索引為0x0006,去常量池中找索引為6的字元串,為“I”,是基本類型int。以下是常量池相關内容:

#5 = Utf8          number
   #6 = Utf8          I
           

字段number的屬性計數值為0x0000,也就是沒有需要額外描述的資訊。

字段表集合中不會列出從父類或者父接口中繼承而來的字段,但有可能列出原版Java代碼中沒有的字段,比如在内部類中為了保持對外部類的通路性,會自動添加指向外部類執行個體的字段。

方法表集合

方法表的結構和字段表的是一樣的,也是依次包括了通路标志(access_flags)、名稱索引(name_index)、描述符索引(descriptor_index)和屬性表集合(attributes)。具體如下表:

方法的通路标志
方法的簡單名稱索引
方法的描述符索引
方法的屬性計數值
方法的屬性

對于方法的通路标志,所有标志位和取值如下表:

方法是否public
方法是否private
方法是否protected
方法是否static
方法是否為final
ACC_SYNCHRONIZED 0x0020 方法是否sychronized
ACC_BRIDGE 方法是否是由編譯器産生的橋接方法
ACC_VARARGS 方法是否接受不定參數
ACC_NATIVE 0x0100 方法是否為native
ACC_ABSTRACT 0x0400 方法是否為abstract
ACC_STRICT 0x0800 方法是否為strictfp
方法是否由編譯器自動産生

方法中的Java代碼,經過編譯器程式設計成位元組碼指令後,放在方法屬性表集合中一個名為“Code”的屬性裡,後面會有更多分享。

Class檔案結構全面解析(下)

方法計算值為0x0003,表示集合中有兩個方法(編譯器自動添加的無參構造方法和源碼中的plusOne方法)。第一個方法的通路标志是0x0001,表示隻有ACC_PUBLIC标志為true。

名稱索引為0x0007,在常量池中為索引為7的字元串為“”,這就是編譯器自動添加的無參構造方法。描述符索引為0x0008,在常量池中為索引為7的字元串為“()V”,方法的屬性計數值為0x0001,表示該方法有1個屬性,屬性名稱索引為0x0009,在常量池中為索引為7的字元串為“Code”。以下是常量池相關内容:

#7 = Utf8          <init>
   #8 = Utf8          ()V
   #9 = Utf8          Code
           

屬性表集合

屬性表(attribute_info)在前面的分享中出現了幾次,在Class檔案、字段表、方法表都可以有自己的屬性表集合,用來描述某些場景下特有的資訊。

屬性表不在要求具有嚴格的順序,并且隻要不與已有的屬性名重複,任何人實作的編譯器都可以寫入自己定義的屬性資訊,Java虛拟機在運作時會忽略掉它不認識的屬性。

我總結了一些比較常見的屬性,如下表:

屬性名稱 使用位置
Code 方法表 Java代碼編譯成的位元組碼指令
ConstantValue 字段表 final關鍵字定義的常量值
Exceptions 方法抛出的異常
InnerClasses 類檔案 内部類清單
LineNumberTable Code屬性 Java源碼的行号與位元組碼指定的對應關系
LocalVariableTable 方法的局部變量描述
SourceFile 記錄源檔案名稱

對于每個屬性,它的名稱都從常量池中引用一個CONSTANT_Utf8_info類型的常量,而屬性值的結構則是完全自定義的,隻需要用一個u4類型來說明屬性值所占的位數就可以了。具體結構如下:

attribute_name_index 屬性名稱索引
attribute_length 屬性值所占的位數
u1 info 屬性值

總結

Class檔案主要由魔數、次版本号、主版本号、常量池集合、通路标志、類索引、父類索引、接口索引集合、字段表集合、方法表集合和屬性表集合組成。随着JDK版本的不斷更新,Class檔案結構也在不斷更新,學習之路,永不止步。

作者:萬貓學社

出處:http://www.cnblogs.com/heihaozi/

版權聲明:本文遵循 CC 4.0 BY-NC-SA 版權協定,轉載請附上原文出處連結和本聲明。

微信掃描二維碼,關注

,回複「

電子書

」,免費擷取12本Java必讀技術書籍。