2.9 methods
methods數(shù)組記錄了類或接口中的所有方法,包括實例方法、靜態(tài)方法、實例初始化方法和類初始化方法,但不包括父類或父接口中定義的方法。methods數(shù)組中每項都是method_info類型值,它描述了方法的詳細(xì)信息,如名稱、描述符、方法中的attribute(如Code Attribute記錄了方法的字節(jié)碼)等。
method_info |
type | descriptor | remark |
u2 | access_flags | 記錄方法的訪問權(quán)限。見2.9.1 |
u2 | name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定方法名稱。 |
u2 | descriptor_index | constant_pool中的索引,CONSTANT_Utf8_info類型,指定方法的描述符(見附錄C)。 |
u2 | attributes_count | attributes包含的項目數(shù)。 |
attribute_info | attributes[attributes_count] | 字段中包含的Attribute集合。見2.9.2-2.9.11 |
注:methods數(shù)組同樣有和fields數(shù)組一樣的問題,包括fields中項和CONSTANT_Methodref_info以及CONSTANT_InterfaceMethodref_info中的區(qū)別。以及設(shè)計上的問題。詳見field_info中的注。
2.9.1 方法訪問權(quán)限
方法的訪問權(quán)限 |
Flag Name | Value | Remarks |
ACC_PUBLIC | 0x0001 | pubilc,包外可訪問。 |
ACC_PRIVATE | 0x0002 | private,只可在類內(nèi)訪問。 |
ACC_PROTECTED | 0x0004 | protected,類內(nèi)和子類中可訪問。 |
ACC_STATIC | 0x0008 | static,靜態(tài)。 |
ACC_FINAL | 0x0010 | final,不可被重寫。 |
ACC_SYNCHRONIZED | 0x0020 | synchronized,同步方法。 |
ACC_BRIDGE | 0x0040 | bridge方法,由編譯器生成。(什么是bridge方法?) |
ACC_VARARGS | 0x0080 | 包含不定參數(shù)個數(shù)的方法。 |
ACC_NATIVE | 0x0100 | native,非Java語言實現(xiàn)的方法。(如何實現(xiàn)Native方法?) |
ACC_ABSTRACT | 0x0400 | abstract,抽象方法。 |
ACC_STRICT | 0x0800 | strictfp,設(shè)置floating-point模式為FP-strict。(什么是FP-strict模式?) |
ACC_SYNTHETIC | 0x1000 | synthetic,由編譯器產(chǎn)生,不存在于源代碼中。 |
注:接口中的方法必須同時設(shè)置:ACC_PUBLIC、ACC_ABSTRACT。設(shè)置了ACC_ABSTRACT后,不可以再設(shè)置ACC_FINAL、ACC_STATIC、ACC_PRIVATE、ACC_NATIVE、ACC_SYNCHRONIZED 、ACC_STRICT。
2.9.2 Code Attribute (JVM識別)
每個非abstract、非native方法的attributes集合都包含有且僅有一項Code Attribute。它包含了一個方法的棧、局部變量、字節(jié)碼以及和代碼相關(guān)的Attribute信息。
Code Attribute |
type | descriptor | remark |
u2 | attribute_name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute的名稱(“Code”)。 |
u4 | attribute_length | 該Attribute內(nèi)容的字節(jié)長度。 |
u2 | max_stack | 該方法操作棧的最大深度。 |
u2 | max_locals | 該方法調(diào)用時需要分配的局部變量的最大個數(shù),包括該方法的參數(shù)。 |
u4 | code_length | 該方法字節(jié)碼長度(以字節(jié)為單位) |
u1 | code[code_length] | 存放字節(jié)碼數(shù)組(字節(jié)碼如何解析?)。 |
u2 | exception_table_length | 異常表的長度。 |
exception_table_info | 每個表項記錄一段異常處理代碼信息和范圍。 | u2 | start_pc | 記錄應(yīng)用該項異常處理的起始字節(jié)碼。在字節(jié)碼數(shù)組中的起始索引號[start_pc, end_pc)。索引號必須是opcode(一條指令的開始位置)對應(yīng)的位置。 | u2 | end_pc | u2 | handler_pc | 記錄該項異常處理代碼的開始地址。在字節(jié)碼數(shù)組中的開始索引號。索引號必須是opcode對應(yīng)的位置。 | u2 | catch_type | constant_pool中的索引,CONSTANT_Class_info類型。指定該項能捕獲的異常類(或其子類)。或0用于實現(xiàn)finally語法(即不管什么類型都會捕獲。) | exception_table[exception_table_length] | |
u2 | attributes_count | attributes包含的項目數(shù)。 |
attribute_info | attributes[attributes_count] | 字段中包含的Attribute集合。見2.9.2.1-2.9.2.4 |
2.9.2.1 StackMapTable Attribute (JVM識別)
StackMapTable Attribute在J2SE 6中引入,記錄了類型檢查時需要用到的信息,如字節(jié)碼的偏移量、局部變量的驗證類型、操作棧中的驗證類型,用于類型檢查過程。在Code Attribute只能包含一項StackMapTable Attribute,記錄所有當(dāng)前Code Attribute中的驗證信息。
一項StackMapTable Attribute中包含多項stack_map_frame。每項stack_map_frame顯式或隱式得記錄了字節(jié)碼的偏移量、局部變量驗證類型和操作棧的驗證類型。驗證器就是通過獲取局部變量類型和操作棧類型進(jìn)行驗證的(具體如何驗證呢?)。
在stack_map_frame中,并不是直接記錄了字節(jié)碼的索引值,而是記錄了offset_delta的值。stack_map_frame中的每一項都通過前一項的值+1+offset_delta計算出當(dāng)前項對應(yīng)的真正的字節(jié)碼的位置,只有當(dāng)當(dāng)前stack_map_frame的前一項是當(dāng)前方法的初始幀(initial frame of the method,什么是方法的初始幀?)的時候,offset_delta的值才直接表示字節(jié)碼位置。
(為什么不直接記錄字節(jié)碼的索引值?為了保證stack_map_frame被正確的排序了。為什么要加1再加offset_delta?為了避免重復(fù)出現(xiàn)stack_map_frame項。)
StackMapTable Attribute |
type | descriptor | remark |
u2 | attribute_name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute的名稱(“StackMapTable”)。 |
u4 | attribute_length | 該Attribute內(nèi)容的字節(jié)長度。 |
u2 | number_of_entries | stack_map_frame項的數(shù)目。 |
stack_map_frame(union,聯(lián)合體類型) | 每一項顯式或隱式得記錄了字節(jié)碼的偏移量、局部變量驗證類型和操作棧的驗證類型。每一項第一個字節(jié)指定當(dāng)前stack_map_frame的類型(tag) | same_frame | same_frame { u1 frame_type = SAME; /* 0-63 */ } tag的值為[0-63]。offset_delta = frame_type。表示當(dāng)前幀和前一幀有相同的局部變量,并且當(dāng)前操作數(shù)棧為空。 | same_locals_1_stack _item_frame | same_locals_1_stack_item_frame { u1 frame_type = SAME_LOCALS_1_STACK_ITEM; /* 64-127 */ verification_type_info stack[1] } tag值為[64-127]。offset_delta = frame_type – 64。表示當(dāng)前幀和前一幀有相同的局部變量,并且操作棧內(nèi)的操作數(shù)條目數(shù)為1,因而它為該操作數(shù)棧內(nèi)的操作數(shù)保存了一項verification_type_info(詳見下表)。([128-246]的tag值保留) | same_locals_1_stack _item_frame_extended | same_locals_1_stack_item_frame_extended { u1 frame_type = SAM_LOCALS_1_STACK_ITEM_EXTENEDED; /* 247 */ u2 offset_delta; verification_type_info stack[1]; } tag值為247。offset_delta = offset_delta。表示當(dāng)前值幀和前一幀有相同的局部變量,并且操作棧內(nèi)的操作數(shù)條目數(shù)為1,因而它為該操作數(shù)棧內(nèi)的操作數(shù)保存了一項verification_type_info(詳見下表)。和上一類型的區(qū)別是這里的offset_delta是直接給出的。 | chop_frame | chop_frame { u1 frame_type = CHOP; /* 248 – 250 */ u2 offset_delta; } tag值為[248, 250]。offset_delta = offset_delta。表示當(dāng)前操作棧為空,而當(dāng)前局部變量比前一幀的局部變量少后面的251 – frame_type個局部變量。 | same_frame_extended | same_frame_extended { u1 frame_type = SAME_FRAME_EXTENDED; /* 251 */ u2 offset_delta; } tag值為251。offset_delta = offset_delta。表示當(dāng)前幀和前一幀有相同的局部變量,并且操作數(shù)棧為空。和samep_frame的區(qū)別是same_frame_extended中的offset_delta值直接給出。 | append_frame | append_frame { u1 frame_type = APPEND; /* 252 – 254 */ u2 offset_delta; verification_type_info locals[frame_type – 251]; } tag值為[252-254]。offset_delta = offset_delta。表示操作數(shù)棧為空,而當(dāng)前幀的局部變量比前一幀的局部變量多frame_type – 251個。因而它也定義了frame_type – 251項的verification_type_info類型。詳見下表。 | full_frame | full_frame { u1 frame_type = FULL_FRAME; /* 255 */ u2 offset_delta; u2 number_of_locals; verification_type_info locals[number_of_locals]; u2 number_of_stack_items; verification_type_info stack[number_of_stack_items]; } tag值255。offset_delta = offset_delta。full_frame則定義了所有的信息,包括offset_delta的值,以及當(dāng)前幀和前一幀不同的所有局部變量和操作數(shù)。locals[0]表示0號局部變量;stack[0]表示棧底操作數(shù)。 | entries[number_of_entries] | |
verification_type_info(union,聯(lián)合體類型) |
記錄字節(jié)碼的驗證類型。每一項第一個字節(jié)指定驗證類型(tag)。 |
Top_variable_info | Top_variable_info { u1 tag = ITEM_Top; /* 0 */ } 指定局部變量的驗證類型為Top。 |
Integer_variable_info | Integer_variable_info { u1 tag = ITEM_Integer; /* 1 */ } 指定驗證類型為int。 |
Float_variable_info | Float_variable_info { u1 tag = ITEM_Float; /* 2 */ } 指定驗證類型為float。 |
Long_variable_info | Long_variable_info { u1 tag = ITEM_Long; /* 4 */ } 指定驗證類型為long。 |
Double_variable_info | Double_variable_info { u1 tag = ITEM_Double; /* 3 */ } 指定驗證類型為double。 |
Null_variable_info | Null_variable_info { u1 tag = ITEM_Null; /* 5 */ } 指定驗證類型為null。 |
UninitializedThis_variable_info | UninitializedThis_variable_info { u1 tag = ITEM_UninitializedThis; /* 6 */ } 指定驗證類型為uninitializedThis(什么是uninitializedThis?)。 |
Object_variable_info | Object_variable_info { u1 tag = ITEM_Object; /* 7 */ u2 cpool_index; //constant_pool索引,CONSTANT_Class_info類型。 } 指定驗證類型為cpool_index中指定的類型實例。 |
Uninitialized_variable_info | Uninitiated_variable_info { u1 tag = ITEM_Uninitialized; /* 8 */ u2 offset; } 指定驗證類型為uninitialized(這種類型是指什么?)。offset記錄了用于創(chuàng)建實例的new指令的偏移量。(The offset item indicates the offset of the new instruction that created the object being stored in the location.這段話是什么意思?) |
注:對驗證過程不太了解,因而StackMapTable Attribute的一些描述也沒能理解。
2.9.2.2 LineNumberTable Attribute (調(diào)試信息)
LineNumberTable Attribute用于調(diào)試器,以獲取某條指令對應(yīng)的源代碼中的行號。多條指令可以對應(yīng)相同的行號。
LineNumberTable Attribute |
type | descriptor | remark |
u2 | attribute_name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute的名稱(“LineNumberTable”)。 |
u4 | attribute_length | 該Attribute內(nèi)容的字節(jié)長度。 |
u2 | line_number_table_length | 異常表的長度。 |
line_number_table | 一條指令和源代碼行號的映射關(guān)系表。 | u2 | start_pc | 一條指令的開始索引(字節(jié)碼數(shù)組中的索引號) | u2 | line_number | 源代碼中的行號。 | line_number_table[line_number_table_length] | |
2.9.2.3 LocalVariableTable Attribute (調(diào)試信息)
LocalVariableTable Attribute用于調(diào)試器,以獲取在方法運(yùn)行時局部變量的信息。在一個Code Attribute中只包含1或0項LocalVariableTable Attribute。
LocalVariableTable Attribute |
type | descriptor | remark |
u2 | attribute_name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute的名稱(“LocalVariableTable”)。 |
u4 | attribute_length | 該Attribute內(nèi)容的字節(jié)長度。 |
u2 | local_variable_table_length | 局部變量表的長度。 |
local_variable_table | 每項記錄了一個局部變量有值的范圍和該局部變量在局部變量數(shù)組中的索引值。 | u2 | start_pc | 記錄該局部變量的有值范圍,字節(jié)碼數(shù)組中的索引范圍[start_pc, start_pc+length)。 | u2 | length | u2 | name_index | constant_pool中索引,CONSTANT_Utf8_info類型,記錄該項代表的局部變量名。 | u2 | descriptor_index | constant_pool中索引,CONSTANT_Utf8_info類型,記錄該項代表的局部變量的字段描述符(見附錄C)。 | u2 | index | 記錄該項代表的局部變量在方法的局部變量數(shù)組中的索引。 | local_variable_table[local_variable_table_length] | |
2.9.2.4 LocalVariableTypeTable Attribute (調(diào)試信息)
LocalVariableTypeTable Attribute用于調(diào)試器,以獲取在方法運(yùn)行時泛型局部變量的信息。在一個Code Attribute中只包含1或0項LocalVariableTypeTable Attribute。
LocalVariableTable Attribute和LocalVariableTypeTable Attribute表達(dá)的信息是類似的,他們的區(qū)別是對泛型類型的局部變量,需要用Signature的形式表達(dá),而不能僅僅用Descriptor的形式表達(dá),因而對泛型類型的局部變量,需要在LocalVariableTable Attribute和LocalVariableTypeTable Attribute中同時存在一項;而對非泛型類型的局部變量來說,只要在LocalVariableTable Attribute存在表項就可以了。
從這里我們也可以看出泛型是后期才被字節(jié)碼所支持的痕跡。我感覺很奇怪的是Java在設(shè)計的時候,泛型應(yīng)該已經(jīng)開始流行了,為什么它在設(shè)計之初沒有把它考慮進(jìn)去,而要到后期加入,然后讓這種修補(bǔ)的設(shè)計做的那么糟糕呢,Java的設(shè)計者如果能在設(shè)計的時候把它作為擴(kuò)展考慮,然后再后期去實現(xiàn),不是更好嗎?
LocalVariableTypeTable Attribute |
type | descriptor | remark |
u2 | attribute_name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute的名稱(“LocalVariableTypeTable”)。 |
u4 | attribute_length | 該Attribute內(nèi)容的字節(jié)長度。 |
u2 | local_variable_type_table_length | 泛型局部變量表的長度。 |
local_variable_type_table | 每項記錄了一個泛型局部變量有值的范圍和該局部變量在局部變量數(shù)組中的索引值。 | u2 | start_pc | 記錄該局部變量的有值范圍,字節(jié)碼數(shù)組中的索引范圍[start_pc, start_pc+length)。 | u2 | length | u2 | name_index | constant_pool中索引,CONSTANT_Utf8_info類型,記錄該項代表的局部變量名。 | u2 | signature_index | constant_pool中索引,CONSTANT_Utf8_info類型,記錄該項代表的局部變量的字段簽名(見附錄D)。 | u2 | index | 記錄該項代表的局部變量在方法的局部變量數(shù)組中的索引。 | local_variable_type_table[local_variable_table_length] | |
2.9.3 Exceptions Attribute (JVM識別)
Exceptions Attribute記錄了一個方法需要檢驗的異常類型。一個method_info的attributes中只能包含一項Exceptions Attribute。即記錄一個方法可以拋出的異常類型。
一個方法可以拋出的異常類型遵循三點:
1. 拋出的異常是RuntimeException類型或其子類。
2. 拋出的異常是Error類型或其子類。
3. 拋出的異常是Exceptions Attribute中記錄的類型或它們的子類。
Exceptions Attribute |
type | descriptor | remark |
u2 | attribute_name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute的名稱(“Exceptions”)。 |
u4 | attribute_length | 該Attribute內(nèi)容的字節(jié)長度。 |
u2 | number_of_exceptions | exception_index_table表項長度 |
u2 | exception_index_table[number_of_exceptions] | 每項為constant_pool中的索引,CONSTANT_Class_info類型。記錄該方法可拋出的異常類型。 |
2.9.4 RuntimeVisibleParameterAnnotations Attribute
RuntimeVisibleParameterAnnotations Attribute記錄該方法在運(yùn)行時可見的修飾該方法參數(shù)的Annotation,從而Java程序可以通過反射機(jī)制獲取這些Annotation中的值。一個method_info中的attributes中只能包含一項RuntimeVisibleAnnotations Attribute。
RuntimeVisibleAnnotations Attribute |
type | descriptor | remark |
u2 | attribute_name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute的名稱(“RuntimeVisibleAnnotations”)。 |
u4 | attribute_length | 該Attribute內(nèi)容的字節(jié)長度。 |
u2 | num_parameters | 記錄該方法中參數(shù)個數(shù) |
parameter_annotations | 每個參數(shù)和在它之上定義的annotations的映射表。順序和源碼定義順序一致。 | u2 | num_annotations | 記錄該參數(shù)定義的annotation的個數(shù) | annotation | annotations[num_annotations] | 記錄該項對應(yīng)的參數(shù)中所有運(yùn)行時可見的annotaion項,順序和源碼定義順序一致。(annotation類型見附件E) | parameter_annotations[num_parameters] | |
注:從該數(shù)據(jù)結(jié)構(gòu)的定義中可以看到,Java對Parameter Annotation存放的信息是很少的,我們只能依賴于定義的順序來獲取這些Annotation,而不能通過參數(shù)名或者參數(shù)類型來獲取相應(yīng)的Annotation。事實上,
1. 由于參數(shù)名的信息只在調(diào)試時才有,如LocalVariableTable Attribute或LocalVariableTypeTable Attribute中;
2. 而一個方法中不同參數(shù)的類型極有可能是相同的;
因而從邏輯上來說,通過參數(shù)名或者參數(shù)類型返回相應(yīng)的Annotation信息的方式也是不合理的。由于這個原因,在java.lang.reflect.Method類的方法中也只是給出了:
Annotation[][] getParameterAnnotations()
的方法,獲得所有參數(shù)中的Annotation,這里的二維數(shù)組一維代表參數(shù),一維代表多個Annotation,它們的順序和源碼定義時順序相同。
2.9.5 RuntimeInvisibleParameterAnnotations Attribute
RuntimeInvisibleParameterAnnotations Attribute記錄該方法在運(yùn)行時不可見的修飾該方法參數(shù)的Annotation。一個method_info中的attributes中只能包含一項RuntimeInvisibleAnnotations Attribute。
RuntimeInvisibleParameterAnotations Attribute和RuntimeVisibleAnnotations Attribute的區(qū)別在于:
后者中的Annotation默認(rèn)情況下,可以通過Java提供的反射函數(shù)獲取相應(yīng)的Annotation,而前者的Annotation在默認(rèn)情況下是無法通過Java提供的反射函數(shù)被獲取的,而需要通過特定的機(jī)制(如設(shè)置JVM的特定參數(shù),該機(jī)制由不同的JVM實現(xiàn)來決定)才能通過Java提供的反射函數(shù)獲取內(nèi)部的Annotation。
然而這樣就又有一個問題了,RuntimeInvisibleParameterAnotations Attribute是如何被填入值的呢?通過什么機(jī)制讓源碼中方法參數(shù)的Annotation是默認(rèn)不可見的呢?我感覺這個也可能也是由不同編譯器提供不同的機(jī)制來實現(xiàn)的,不知道sun提供的編譯器有沒有什么機(jī)制支持它了??
RuntimeInvisibleAnnotations Attribute |
type | descriptor | remark |
u2 | attribute_name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute的名稱(“RuntimeInvisibleAnnotations”)。 |
u4 | attribute_length | 該Attribute內(nèi)容的字節(jié)長度。 |
u2 | num_parameters | 記錄該方法中參數(shù)個數(shù) |
parameter_annotations | 每個參數(shù)和在它之上定義的annotations的映射表。順序和源碼定義順序一致。 | u2 | num_annotations | 記錄該參數(shù)定義的annotation的個數(shù) | annotation | annotations[num_annotations] | 記錄該項對應(yīng)的參數(shù)中所有運(yùn)行時不可見的annotaion項,順序和源碼定義順序一致。(annotation類型見附件E) | parameter_annotations[num_parameters] | |
2.9.6 AnnotationDefault Attribute
AnnotationDefault Attribute用于Annotation類型方法中,以記錄該方法所代表的Annotation類型的默認(rèn)值。每個Annotation類型的method_info中的attributes中只能包含一個AnnotationDefault Attribute項。如:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.PARAMETER})
public @interface Test {
public int id() default -1;
public String description() default "no description";
}
該Annotation類產(chǎn)生的class二進(jìn)制文件中的id方法和description方法的attributes數(shù)組中都會包含一項AnnotationDefault Attribute,它們的默認(rèn)值分別為-1(CONSTANT_Integer_info類型)和”no description”(CONSTANT_String_info類型)。
AnnotationDefault Attribute |
type | descriptor | remark |
u2 | attribute_name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute的名稱(“AnnotationDefault”)。 |
u4 | attribute_length | 該Attribute內(nèi)容的字節(jié)長度。 |
element_value | default_value | 記錄該方法表示的Annotation類型的默認(rèn)值。(element_value結(jié)構(gòu)詳見附錄E) |
2.9.7 Synthetic Attribute
參見2.11.1
2.9.8 Signature Attribute
參見2.11.2
2.9.9 Deprecated Attribute
參見2.11.3
2.9.10 RuntimeVisibleAnnotations Attribute
參見2.11.4
2.9.11 RuntimeInvisibleAnnotations Attribute
參見2.11.5
2.10 attributes
attributes數(shù)組記錄了和類或接口相關(guān)的所有Attribute項(和字段相關(guān)的Attribute在field_info的attributes中,和方法相關(guān)的Attribute在method_info的attrubutes中,和字節(jié)碼相關(guān)的Attribute在Code Attribute的attributes中)。attributes數(shù)組中的每項都是attribute_info類型,它描述了Attribute的名稱、詳細(xì)信息等。該attributes數(shù)組描述了ClassFile的一些額外信息。JVM必須忽略它不能識別的Attribute,而且那些JVM不能識別的的Attribute也不能影響class文件的語義。
當(dāng)前定義的Attribute有:Code Attribute、Constant Value Attibute、Deprecated Attribute、Enclosing Method Attribute、Exceptions Attribute、Inner Classes Attribute、Line Number Table Attribute、Local Variable Table Attribute、Local Variable Type Table Attribute、Runtime Visible Annotations Attribute、Runtime Invisible Annotation Attribute、Runtime Visible Parameter Annotation Attribute、Runtime Invisible Parameter Annotation Attribute、Signature Attribute、Source Debug Extension Attribute、Source File Attribute、Stack Map Table Attribute、Synthetic Attribute、Annotation Default Attribute等。它們有些只存在于field_info中,有些只存在method_info中,有些只存在ClassFile中,有些只存在于Code Attribute中,還有些可以同時存在于field_info、method_info、classfile中。
Attribute結(jié)構(gòu)只存在與ClassFile、method_info、field_info、Code Attribute結(jié)構(gòu)中。
attribute_info(Attribute的基本數(shù)據(jù)結(jié)構(gòu)) |
type | descriptor | remark |
u2 | attribute_name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute的名稱。 |
u4 | attribute_length | 該Attribute內(nèi)容的字節(jié)長度。 |
u1 | info[attribute_length] | 記錄Attribute的內(nèi)容字節(jié)數(shù)據(jù)。 |
在用戶自定義的編譯器或者Java虛擬機(jī)中可以擴(kuò)展ClassFile中的Attribute表(即自定義新的Attribute)。但是對自定義的Attribute必須遵循一些規(guī)則:
1. 用戶自定義的新的Attribute(非sun定義的Attribute)命名必須是遵循Java命名規(guī)則,即加入公司的包名信息,如:“com.levin.new_attribute”
2. 新增的Attribute只可以作為輔助的信息,如增加和自定義調(diào)試器相關(guān)的調(diào)試信息,但是它們不可以改變ClassFile的語義。什么叫改變ClassFile的語義呢?我現(xiàn)在的理解,比如在自定義的Java虛擬機(jī)中,為某個方法新增一個Attribute,用以標(biāo)記該方法在運(yùn)行是不可以被調(diào)用。不知道這個例子合適不合適。
3. 對于自定義的Java虛擬機(jī),禁止因為某些它不識別的Attribute存在而拋出異常或者直接報錯。但是這一層限制可以加載自定義編譯器中。Java虛擬機(jī)必須忽略它不識別的Attribute。
以下是定義在ClassFile中的Attribute。
2.10.1 InnerClasses Attribute
InnerClasses記錄當(dāng)前類的所有內(nèi)部類。當(dāng)前類需要記錄的內(nèi)部類的算法如下:
1. 當(dāng)前類中定義的內(nèi)部類,包括方法中定義的類。
2. 如果當(dāng)前類本身是內(nèi)部類,則還要記錄當(dāng)前類的外部類,直到外部類不是一個內(nèi)部類。
如:
class Outer {
public class Inner {
public void getMethod() {
class Inner3 {
class Inner4 {
}
}
}
public class Inner2 {
public class Inner5 {
}
}
}
public class Inner_1 {
}
}
Outer中的InnerClasses Attribute:
[inner class info: #17 org/levin/insidejvm/miscs/instructions/Outer$Inner, outer class info: #1 org/levin/insidejvm/miscs/instructions/Outer
inner name: #19 Inner, accessflags: 1 public],
[inner class info: #20 org/levin/insidejvm/miscs/instructions/Outer$Inner_1, outer class info: #1 org/levin/insidejvm/miscs/instructions/Outer
inner name: #22 Inner_1, accessflags: 1 public]
Inner中的InnerClasses Attribute:
[inner class info: #1 org/levin/insidejvm/miscs/instructions/Outer$Inner, outer class info: #23 org/levin/insidejvm/miscs/instructions/Outer
inner name: #25 Inner, accessflags: 1 public],
[inner class info: #26 org/levin/insidejvm/miscs/instructions/Outer$Inner$1Inner3, outer class info: #0
inner name: #28 Inner3, accessflags: 16 final],
[inner class info: #29 org/levin/insidejvm/miscs/instructions/Outer$Inner$Inner2, outer class info: #1 org/levin/insidejvm/miscs/instructions/Outer$Inner
inner name: #31 Inner2, accessflags: 1 public]
Inner5中的InnerClasses Attribute:
[inner class info: #22 org/levin/insidejvm/miscs/instructions/Outer$Inner, outer class info: #24 org/levin/insidejvm/miscs/instructions/Outer
inner name: #26 Inner, accessflags: 1 public],
[inner class info: #27 org/levin/insidejvm/miscs/instructions/Outer$Inner$Inner2, outer class info: #22 org/levin/insidejvm/miscs/instructions/Outer$Inner
inner name: #29 Inner2, accessflags: 1 public],
[inner class info: #1 org/levin/insidejvm/miscs/instructions/Outer$Inner$Inner2$Inner5, outer class info: #27 org/levin/insidejvm/miscs/instructions/Outer$Inner$Inner2
inner name: #30 Inner5, accessflags: 1 public]
InnerClasses Attribute |
type | descriptor | remark |
u2 | attribute_name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute的名稱(“InnerClasses”)。 |
u4 | attribute_length | 該Attribute內(nèi)容的字節(jié)長度。 |
u2 | number_of_classes | 記錄內(nèi)部類的數(shù)量。 |
classes | 定義每項Inner Class的信息。 | u2 | inner_class_info_index | constant_pool中的索引,CONSTANT_Class_info類型。記錄內(nèi)部類名。 | u2 | outer_class_info_index | 若當(dāng)前項內(nèi)部類不是包含它的類的成員類,則該值為0;否則該值為constant_pool中的索引,CONSTANT_Class_info類型,記錄外部類名。 | u2 | inner_name_index | 若當(dāng)前項的內(nèi)部類為匿名類,則該值為0;否則該值為constant_pool中的索引,CONSTANT_Utf8_info類型,記錄該內(nèi)部類的簡單名(沒有前綴)。 | u2 | inner_class_access_flags | 定義當(dāng)前項的內(nèi)部類的訪問屬性。見下表。 | classess[number_of_classes] | |
注:為什么需要為內(nèi)部類保留那么多的信息呢?是為了在反射的時候獲取必要的信息或者在反編譯的時候可以更好的還原源代碼的結(jié)構(gòu)嗎?還是有其他的作用?
內(nèi)部類的訪問權(quán)限 |
Flag Name | Value | Remarks |
ACC_PUBLIC | 0x0001 | pubilc,類外可訪問。 |
ACC_PRIVATE | 0x0002 | private,類內(nèi)才可訪問。 |
ACC_PROTECTED | 0x0004 | protected,類和其子類可訪問。 |
ACC_STATIC | 0x0008 | static,靜態(tài)內(nèi)部類。 |
ACC_FINAL | 0x0010 | final,不能有子類。 |
ACC_INTERFACE | 0x0200 | 接口,同時需要設(shè)置:ACC_ABSTRACT。不可同時設(shè)置:ACC_FINAL、ACC_SUPER、ACC_Enum |
ACC_ABSTRACT | 0x0400 | 抽象類,無法實例化。不可和ACC_FINAL同時設(shè)置。 |
ACC_SYNTHETIC | 0x1000 | synthetic,由編譯器產(chǎn)生,不存在于源代碼中。 |
ACC_ANNOTATION | 0x2000 | 注解類型(annotation),需同時設(shè)置:ACC_INTERFACE、ACC_ABSTRACT |
ACC_ENUM | 0x4000 | 枚舉類型 |
2.10.2 EnclosingMethod Attribute
當(dāng)且僅當(dāng)一個類是匿名類或者本地類(local class),該類才會包含一項且僅有一項EnclosingMethod Attribute。
然而什么是本地類(local class)呢?我的理解,所謂本地類就是在方法內(nèi)部定義的類,如以下類的定義:
class A {
public Iterator getIterator() {
class LocalClass {
}
return new Iterator() {
public boolean hasNext() { return false; }
public Object next() { return null; }
public void remove() { }
};
}
}
匿名類A$1.class中的EnclosingMethod Attribute:
Enclosing Method: #29 #31 org/levin/insidejvm/miscs/instructions/A.getIterator()Ljava/util/Iterator;
本地類A$1LocalClass.class中的EnclosingMethod Attribute:
Enclosing Method: #22 #24 org/levin/insidejvm/miscs/instructions/A.getIterator()Ljava/util/Iterator;
EnclosingMethod Attribute |
type | descriptor | remark |
u2 | attribute_name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute的名稱(“EnclosingMethod”)。 |
u4 | attribute_length | 該Attribute內(nèi)容的字節(jié)長度(4)。 |
u2 | class_index | constant_pool中的索引,CONSTANT_Class_info類型。記錄定義當(dāng)前類所在方法的宿主類。如上例中,A$1.class和A$1LocalClass.class中的class_index都指向類A。 |
u2 | method_index | 若當(dāng)前類沒有被方法方法包含,如當(dāng)前類是賦值給類成員的匿名類,則method_index值為0,否則該method_index的值為constant_pool中的索引,CONSTANT_NameAndType_info類型。記錄了class_index指定的類中定義的包含當(dāng)前類的方法名和類型信息。 |
注:這里同樣也有一個問題,就是EnclosingMethod Attribute存在的目的問題。我現(xiàn)在的理解,該Attribute的存在也應(yīng)該只是為了用于反射信息和反編譯時可以更好的還原原來代碼的結(jié)構(gòu),在虛擬機(jī)運(yùn)行該程序的時候,由于所有的指令已經(jīng)編譯好了,虛擬機(jī)應(yīng)該不需要這些信息。但是事實是這樣的嗎?有待考證。
2.10.3 SourceFile Attribute
SourceFile Attribute用于記錄和當(dāng)前字節(jié)碼對應(yīng)的源代碼的文件(由編譯器產(chǎn)生,該Attribute只是記錄相應(yīng)源代碼的文件名,而不記錄和路徑相關(guān)的信息)。一個ClassFile中只能包含一項SourceFile Attribute。
SourceFile Attribute |
type | descriptor | remark |
u2 | attribute_name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute的名稱(“SourceFile”)。 |
u4 | attribute_length | 該Attribute內(nèi)容的字節(jié)長度(2)。 |
u2 | sourcefile_index | constant_pool中的索引,CONSTANT_Utf8_info類型。記錄相應(yīng)的源代碼文件名。 |
2.10.4 SourceDebugExtension Attribute
SourceDebugExctension Attribute是Java為調(diào)試時提供的擴(kuò)展信息,主要用于自定義(擴(kuò)展)的編譯器和調(diào)試器。一個ClassFile中只能包含一項SourceFile Attribute。
SourceDebugExtension Attribute |
type | descriptor | remark |
u2 | attribute_name_index | constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute名稱(“SourceDebugExtension”)。 |
u4 | attribute_length | 該Attribute內(nèi)容的字節(jié)長度。 |
u1 | debug_extension[attribute_length] | Utf-8格式的字符串記錄擴(kuò)展調(diào)試信息,不以0結(jié)尾。 |
2.10.5 Synthetic Attribute
參見2.11.1
2.10.6 Signature Attribute
參見2.11.2
2.10.7 Deprecated Attribute
參見2.11.3
2.10.8 RuntimeVisibleAnnotations Attribute
參見2.11.4
2.10.9 RuntimeInvisibleAnnotations Attribute
參見2.11.5
于2010-12-19日