1. 程式人生 > >Java位元組碼結構剖析二:欄位表

Java位元組碼結構剖析二:欄位表

access_flags

訪問標誌資訊包括該class檔案是類還是介面,是否定義成public,是否是abstract,如果是類,是否被申明為final。access_flags 的取值範圍和相應含義見下表。

7867f444437e53d4717059fe73573b58af937948

我們的位元組碼裡該位置的16進製表示是0×0021。0×0021=0×0001 ^ 0×0020。即代表該類的訪問修飾是public的。ACC_SUPER這裡不做介紹,看看JVM規範對他的描述,瞭解即可。

ACC_SUPER 標誌用於確定該 Class 檔案裡面的 invokespecial 指令使用的是哪一種執行語義。目前 Java 虛擬機器的編譯器都應當設定這個標誌。ACC_SUPER 標記是為了向後相容舊編譯器編譯的 Class 檔案而存在的,在 JDK1.0.2 版本以前的編譯器產生的 Class 檔案中,access_flag 裡面沒有 ACC_SUPER 標誌。同時,JDK1.0.2 前的 Java 虛擬機器遇到 ACC_SUPER 標記會自動忽略它。

this_class_name

類索引,this_class 的值必須是對 constant_pool 表中專案的一個有效索引值。constant_pool 表在這個索引處的項必須為 CONSTANT_Class_info 型別常量,表示這個 Class 檔案所定義的類或介面。

在我的位元組碼檔案中,該16進位制值為0×0005=5。通過常量池資訊,最終他指向的是一個utf-8字串,com/shengsiyuan/jvm/bytecode/MyTest2。即類的全限定名。

1

2

#5 = Class #38 // com/shengsiyuan/jvm/bytecode/MyTest2

#38 = Utf8 com/shengsiyuan/jvm/bytecode/MyTest2

super_class_name

父類索引,對於類來說,super_class 的值必須為 0 或者是對 constant_pool 表中專案的一個有效索引值。

在位元組碼檔案中,父類索引為0x000A=10。即父類是 java/lang/Object

1

2

#10 = Class #43 // java/lang/Object

#43 = Utf8 java/lang/Object

interfaces_count

介面計數器,interfaces_count 的值表示當前類或介面的直接父介面數量。

我們的程式碼沒有實現任何介面,所以該項值為0,即0×0000。

interfaces[]

介面表,interfaces[]陣列中的每個成員的值必須是一個對 constant_pool 表中專案的一個有效索引值,它的長度為 interfaces_count。

我們程式碼沒有介面,所以我們的位元組碼檔案裡沒有這項了。所以 interfaces_count 後面就直接是欄位計數器和欄位表。

fields_count

欄位計數器,fields_count 的值表示當前 Class 檔案 fields[]陣列的成員個數。也就是當前類的類欄位和例項欄位的個數。

我們原始碼裡定義了3個欄位,1個類欄位,2個例項欄位。所以fields_count為3。檢視對應位元組碼檔案的16進製表示0×0003=3。

fields[]

欄位表用於描述類和介面中宣告的變數。這裡的欄位包含了類級別變數以及例項變數,但是不包括方法內部宣告的區域性變數。

field_info結構格式如下:

1

2

3

4

5

6

7

field_info {

u2 access_flags;

u2 name_index;

u2 descriptor_index;

u2 attributes_count;

attribute_info attributes[attributes_count];

}

access_flags 項的值是用於定義欄位被訪問許可權和基礎屬性的掩碼標誌。access_flags 的取值範圍和相應含義見如下表:

16de3fd3c71da41f4b33157176bdef21c68a88da

看在我位元組碼中的16進製表示,0×0000=0。0代表沒有修飾符的意思。看我們的原始碼:String str = “Welcome”;,即預設修飾符。

 ●  name_index 項的值必須是對常量池的一個有效索引。常量池在該索引處的項必須是CONSTANT_Utf8_info結構,表示一個有效的欄位的非全限定名。

在位元組碼裡是0x000B=11。常量池11處:

1

#11 = Utf8 str

表示欄位的名稱為“str”。

 ●  descriptor_index 項的值必須是對常量池的一個有效索引。常量池在該索引處的項必須是CONSTANT_Utf8_info結構,表示一個有效的欄位的描述符。

位元組碼中,0x000C=12。看常量池:

1

#12 = Utf8 Ljava/lang/String;

表示該欄位是String型別。

在JVM規範中,每個變數/欄位都有描述資訊,描述資訊主要作用是描述欄位的資料型別、方法的引數列表(包括數量,型別與順序)與返回值。根據描述符規則,基本資料型別和代表無返回值的void型別都用一個大寫字元來表示,物件型別則使用字元L加物件的全限定名稱來表示。為了壓縮位元組碼檔案的體積,對於基本資料型別,JVM都只使用一個大寫字母表示,如下所示:B-byte、C-char、D-double、F-float、I-int、J-long、S-short、Z-boolean、V-void、L-物件型別,如Ljava/lang/String。
對於陣列型別來說,每一個緯度使一個前置的[表示,如int[]被記錄為[I,String[][]被記錄為[[Ljava/lang/String;。

 ●  attributes_count的項的值表示當前欄位的附加屬性的數量。

在位元組碼裡,0x0000=0。即該欄位沒有附加屬性。

 ●  attributes[]

attributes 表的每一個成員的值必須是 attribute結構,一個欄位可以有任意個關聯屬性。

因為該欄位沒有附加屬性,所以這項資料沒有。

以上就是欄位表裡的第1個欄位的完整位元組碼資訊描述。也就是我們定義的『str』欄位的資訊。我們程式碼裡還有2個欄位 private int x = 5public static Integer in = 10。我便不再描述了,大家可以緊接著我後面把這兩個欄位的資訊解析出來。


原文釋出時間為:2018-11-22

本文來自雲棲社群合作伙伴“Java雜記”,瞭解相關資訊可以關注“Java雜記”。