class文件中的fields_count和fields
fields_count描述的是當前的類中定義的字段的個數, 注意, 這里包括靜態字段, 但不包括從父類繼承的字段。 如果當前class文件是由一個接口生成的, 那么這里的fields_count描述的是接口中定義的字段, 我們知道, 接口中定義的字段默認都是靜態的。此外要說明的是, 編譯器可能會自動生成字段, 也就是說, class文件中的字段的數量可能多于源文件中定義的字段的數量。 舉例來說, 編譯器會為內部類增加一個字段, 這個字段是指向外圍類的對象的引用。
位于fields_count下面的數據叫做fields, 可以把它看做一個數組, 數組中的每一項是一個field_info 。這個數組中一共有fields_count個field_info , 每個field_info都是對一個字段的描述。 下面我們詳細講解field_info的結構。 每個field_info的結構如下:
(1)access_flags
其中access_flags占兩個字節, 描述的是字段的訪問標志信息。 這里就不在詳細介紹了, 下面給出一張表格(該表格來自《深入java虛擬機》):
(2)name_index
access_flags下面的兩個字節是name_index, 這是一個指向常量池的索引, 它描述的是當前字段的字段名。 這個索引指向常量池中的一個constant_utf8_info數據項。 這個constant_utf8_info數據項中存放的字符串就是當前字段的字段名。
(3)descriptor_index
name_index下面的兩個字節叫做descriptor_index , 它同樣是一個指向常量池的索引, 它描述的是當前字段的描述符。 這個索引指向常量池中的一個constant_utf8_info數據項。 這個constant_utf8_info數據項中存放的字符串就是當前字段的描述符。
(4)attributes_count和attributes
descriptor_index 下面是attributes_count和attributes 。 這是對當前字段所具有的屬性的描述。 這里的屬性和源文件中的屬性不是同一個概念, 在源文件測層面中, 屬性是字段的另一種叫法, 希望讀者不要疑惑。讀者也不要輕視class文件中的屬性, 這些屬性可以描述很多的信息。 我們會在后面的文章中進行介紹。
attributes_count表示這個字段有幾個屬性。attributes 可以看成一個數組, 數組中的每一項都是一個attribute_info , 每個attribute_info 表示一個屬性, 數組中一共有attributes_count個屬性。可以出現在filed_info中的屬性有三種, 分別是constantvalue, deprecated, 和 synthetic。 這些屬性會在后面的文章中進行介紹。
下面我們以代碼的形式進行解釋, 源碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.bjpowernode.test; public class programer extends person{ private computer computer; public programer(computer computer){ this .computer = computer; } public void dowork(){ computer.calculate(); } } |
反編譯之后, 常量池中會有如下信息(這里省略了大部分無關信息):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
constant pool: ......... ......... # 5 = utf8 computer # 6 = utf8 lcom/jg/zhang/computer; ......... ......... { private com.jg.zhang.computer computer; flags: acc_private ......... ......... } |
從反編譯的結果可以看出, 源文件中定義了一個computer類型的字段computer, 并且是private的。 然后常量池中有這個字段的字段名和描述符。 其中常量池第五項的constant_utf8_info是字段名, 第六項的constant_utf8_info是該字段的描述符。這里有一點需要說明, 在反編譯programer.class時,由于computer是私有的, 要加- private選項, 否則的話, 雖然常量池中有字段引用信息, 但是不會輸出字段信息, 即下面這兩行不會輸出:
1
2
|
private com.bjpowernode.test.computer computer; flags: acc_private |
如果在javap中加入 - private選項, 那么就會有上面兩行的輸出。 使用的命令如下:
1
2
|
javap -c -v - private - classpath . com.bjpowernode.test.programer |
根據反編譯的結果,可以下面給出示意圖, 該圖說明了與computer相對應的field_info是不合引用常量池的 ( 其中虛線范圍內表示常量池):
class文件中的methods_count和methods
fields下面的信息是methods_count和methods 。 methods_count描述的是當前的類中定義的方法的個數, 注意, 這里包括靜態方法, 但不包括從父類繼承的方法。 如果當前class文件是由一個接口生成的, 那么這里的methods_count描述的是接口中定義的抽象方法的數量, 我們知道, 接口中定義的方法默認都是公有的。此外需要說明的是, 編譯器可能會在編譯時向class文件增加額外的方法, 也就是說, class文件中的方法的數量可能多于源文件中由用戶定義的方法。 舉例來說: 如果當前類沒有定義構造方法, 那么編譯器會增加一個無參數的構造函數<init>; 如果當前類或接口中定義了靜態變量, 并且使用初始化表達式為其賦值, 或者定義了static靜態代碼塊, 那么編譯器在編譯的時候會默認增加一個靜態初始化方法<clinit> 。
位于methods_count下面的數據叫做methods , 可以把它看做一個數組, 數組中的每一項是一個method_info 。這個數組中一共有methods_count個method_info , 每個method_info 都是對一個方法的描述。 下面我們詳細講解method_info 的結構。 每個method_info 的結構如下, 幾乎和field_info的結構是一樣的:
(1)access_flags
其中access_flags占兩個字節, 描述的是方法的訪問標志信息。 這里就不在詳細介紹了, 下面給出一張表格(該表格來自《深入java虛擬機》):
(2)name_index
access_flags下面的兩個字節是name_index, 這是一個指向常量池的索引, 它描述的是當前方法的方法名。 這個索引指向常量池中的一個constant_utf8_info數據項。 這個constant_utf8_info數據項中存放的字符串就是當前方法的方法名。
(3)descriptor_index
name_index下面的兩個字節叫做descriptor_index , 它同樣是一個指向常量池的索引, 它描述的是當前方法的描述符。 這個索引指向常量池中的一個constant_utf8_info數據項。 這個constant_utf8_info數據項中存放的字符串就是當前方法的描述符。
(4)attributes_count和attributes
descriptor_index 下面是attributes_count和attributes 。 這是對當前方法所具有的屬性的描述。 這里的屬性和源文件中的屬性不是同一個概念, 在源文件測層面中, 屬性是字段的另一種叫法, 希望讀者不要疑惑。讀者也不要輕視class文件中的屬性, 這些屬性可以描述很多的信息。 我們會在后面的文章中進行介紹。
attributes_count表示這個字段有幾個屬性。attributes 可以看成一個數組, 數組中的每一項都是一個attribute_info , 每個attribute_info 表示一個屬性, 數組中一共有attributes_count個屬性。可以出現在method_info 中的屬性有三種, 分別是code, deprecated, exceptions 和synthetic。 在這幾個屬性中, 尤其是code和exceptions 非常重要, 這兩個屬性對于在class文件中完整描述一個方法起著至關重要的作用, 其中code屬性中存放方法的字節面指令,exceptions 屬性是對方法聲明中拋出的異常的描述 。 這兩屬性以及其他一些屬性, 會在下一篇文章中詳細介紹, 敬請關注。
介紹完了每個method_info的結構, 下面我們以代碼來說明, 還是使用上面的源碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.jg.zhang; public class programer extends person{ private computer computer; public programer(computer computer){ this .computer = computer; } public void dowork(){ computer.calculate(); } } |
反編譯之后, 常量池中會有如下信息(這里省略了大部分無關信息):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
constant pool: ......... # 7 = utf8 <init> # 8 = utf8 (lcom/jg/zhang/computer;)v ......... # 12 = utf8 ()v ......... # 19 = utf8 dowork { ......... public com.jg.zhang.programer(com.jg.zhang.computer); flags: acc_public ......... public void dowork(); flags: acc_public ......... } |
由反編譯結果可以看出, 該類中定義了兩個方法, 其中一個是構造方法, 一個是dowork方法, 且這兩個方法都是public的。 這兩個方法的描述信息都存放在常量池。 其中第7項的constant_utf8_info為構造方法的方法名, 第8項的constant_utf8_info為構造方法的方法描述符, 第19項的constant_utf8_info為dowork方法的方法名, 第12項的constant_utf8_info為dowork方法的方法描述符。
根據常量池中的信息, 可以得出如下的示意圖, 該示意圖形象的說明了class文件中的method_info是如何引用常量池中的數據項來描述當前類中定義的方法的。 圖中虛線范圍內表示常量池所在的區域:
總結
到此為止, 我們就介紹完了class文件中的fields和methods, 進行一下總結。
fields是對當前類中定義的字段的描述, 其中每個字段使用一個field_info表示, fields中有fields_count個field_info。
methods是對當前類或者接口中聲明的方法的描述, 其中每個方法使用一個method_info表示, methods中有methods_count個method_info。
在下一篇博客中, 將會介紹class文件中的各個屬性, 敬請關注。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。