一、概述
任何一個Class文件都對應唯一一個類或接口的定義信息,但是不是所有的類或接口都得定義在文件中(它們也可以通過類加載器直接生成)。
Class文件是一組以8位字節為基礎單位的二進制流,各個數據項嚴格按順序排列,沒有任何分隔符。Class文件格式采用一種類似於C語言結構體的偽結構來存儲數據,這種偽結構只有兩種數據類型:無符號數和表。
無符號數:是基本數據類型,以u1、u2、u4、u8分別代表1個字節、2個字節、4個字節、8個字節的無符號數,可以用來描述數字、索引引用、數量值或者按照UTF-8編碼構成的字符串值。
表:由多個無符號數或者其他表作為數據項構成的復合數據類型,所有表都習慣性地以“_info”結尾。整個Class文件本質上就是一張表,如下所示:
類型 |
名稱 |
數量 |
u4 |
magic |
1 |
u2 |
minor_version |
1 |
u2 |
major_version |
1 |
u2 |
constant_pool_count |
1 |
cp_info |
constant_pool |
constant_pool_count-1 |
u2 |
access_flags |
1 |
u2 |
this_class |
1 |
u2 |
super_class |
1 |
u2 |
interfaces_count |
1 |
u2 |
interfaces |
interfaces_count |
u2 |
fields_count |
1 |
field_info |
fields |
fields_count |
u2 |
methods_count |
1 |
method_info |
methods |
methods_count |
u2 |
attributes_count |
1 |
attribute_info |
attributes |
attributes_count |
二、各個字段詳解
- package com.test;
- public class Test {
- private int m;
- public int getM(){
- return m + 1;
- }
- }

1.魔數

2.版本號
3.常量池

類型 |
簡介 |
項目 |
類型 |
描述 |
CONSTANT_Utf8_info |
utf-8縮略編碼字符串 |
tag |
u1 |
值為1 |
length |
u2 |
utf-8縮略編碼字符串占用字節數 |
||
bytes |
u1 |
長度為length的utf-8縮略編碼字符串 |
||
CONSTANT_Integer_info |
整形字面量 |
tag |
u1 |
值為3 |
bytes |
u4 |
按照高位在前儲存的int值 |
||
CONSTANT_Float_info |
浮點型字面量 |
tag |
u1 |
值為4 |
bytes |
u4 |
按照高位在前儲存的float值 |
||
CONSTANT_Long_info |
長整型字面量 |
tag |
u1 |
值為5 |
bytes |
u8 |
按照高位在前儲存的long值 |
||
CONSTANT_Double_info |
雙精度浮點型字面量 |
tag |
u1 |
值為6 |
bytes |
u8 |
按照高位在前儲存的double值 |
||
CONSTANT_Class_info |
類或接口的符號引用 |
tag |
u1 |
值為7 |
index |
u2 |
指向全限定名常量項的索引 |
||
CONSTANT_String_info |
字符串類型字面量 |
tag |
u1 |
值為8 |
index |
u2 |
指向字符串字面量的索引 |
||
CONSTANT_Fieldref_info |
字段的符號引用 |
tag |
u1 |
值為9 |
index |
u2 |
指向聲明字段的類或接口描述符CONSTANT_Class_info的索引項 |
||
index |
u2 |
指向字段描述符CONSTANT_NameAndType_info的索引項 |
||
CONSTANT_Methodref_info |
類中方法的符號引用 |
tag |
u1 |
值為10 |
index |
u2 |
指向聲明方法的類描述符CONSTANT_Class_info的索引項 |
||
index |
u2 |
指向名稱及類型描述符CONSTANT_NameAndType_info的索引項 |
||
CONSTANT_InterfaceMethodref_info |
接口中方法的符號引用 |
tag |
u1 |
值為11 |
index |
u2 |
指向聲明方法的接口描述符CONSTANT_Class_info的索引項 |
||
index |
u2 |
指向名稱及類型描述符CONSTANT_NameAndType_info的索引項 |
||
CONSTANT_NameAndType_info |
字段或方法的部分符號引用 |
tag |
u1 |
值為12 |
index |
u2 |
指向該字段或方法名稱常量項的索引 |
||
index |
u2 |
指向該字段或方法描述符常量項的索引 |
首先來看常量池中的第一項常量,其標志位為0x07,是一個CONSTANT_Class_info類型常量,此類型常量代表一個類或接口的符號引用。根據其數據結構,接下來2位字節用來保存一個索引值,它指向常量池中一個CONSTANT_Utf8_info類型的常量,此常量代表了這個類或接口的全限定名,索引值為0x0002,即指向了常量池中的第二項常量。
第二項常量標志位為0x01,確實是一個CONSTANT_Utf8_info類型的常量。根據其數據結構,接下來2個字節用來保存utf-8縮略編碼字符串長度,其值為0x000D,轉化為十進制為13,即接下來的13個字節為一個utf-8縮略編碼的字符串,為com/test/Test,可以看到正好是測試類的全限定名。
4.訪問標志

志名稱 |
標志值 |
含義 |
ACC_PUBLIC |
0x0001 |
是否為public類型 |
ACC_FINAL |
0x0010 |
是否被聲明為final,只有類可設置 |
ACC_SUPER |
0x0020 |
是否允許使用invokespecial字節碼指令,JDK1.2以后編譯出來的類這個標志為真 |
ACC_INTERFACE |
0x0200 |
標識這是一個接口 |
ACC_ABSTRACT |
0x0400 |
是否為abstract類型,對於接口和抽象類,此標志為真,其它類為假 |
ACC_SYNTHETIC |
0x1000 |
標識別這個類並非由用戶代碼產生 |
ACC_ANNOTATION |
0x2000 |
標識這是一個注解 |
ACC_ENUM |
0x4000 |
標識這是一個枚舉 |
根據上面的表格,測試類的訪問標志0x0021= 0x0001 | 0x0020 =ACC_PUBLIC | ACC_SUPER
5.類索引、父類索引和接口索引集合

Class文件中由這3項數據來確定這個類的繼承關系
this_class:類索引,用於確定這個類的全限定名,占2字節
super_class:父類索引,用於確定這個類父類的全限定名(Java語言不允許多重繼承,故父類索引只有一個。除了java.lang.Object類之外所有類都有父類,故除了java.lang.Object類之外,所有類該字段值都不為0),占2字節
interfaces_count:接口索引計數器,占2字節。如果該類沒有實現任何接口,則該計數器值為0,並且后面的接口的索引集合將不占用任何字節,
interfaces:接口索引集合,一組u2類型數據的集合。用來描述這個類實現了哪些接口,這些被實現的接口將按implements語句(如果該類本身為接口,則為extends語句)后的接口順序從左至右排列在接口的索引集合中
this_class、super_class與interfaces中保存的索引值均指向常量池中一個CONSTANT_Class_info類型的常量,通過這個常量中保存的索引值可以找到定義在CONSTANT_Utf8_info類型的常量中的全限定名字符串
this_class的值為0x0001,即常量池中第一個常量,super_class的值為0x0003,即常量池中的第三個常量,interfaces_counts的值為0x0000,故接口索引集合大小為0
6.字段表集合

fields_count:字段表計數器,即字段表集合中的字段表數據個數。占2字節,其值為0x0001,即只有一個字段表數據,也就是測試類中只包含一個變量(不算方法內部變量)
fields:字段表集合,一組字段表類型數據的集合。字段表用於描述接口或類中聲明的變量,包括類級別(static)和實例級別變量,不包括在方法內部聲明的變量
在Java中一般通過如下幾項描述一個字段:字段作用域(public、protected、private修飾符)、是類級別變量還是實例級別變量(static修飾符)、可變性(final修飾符)、並發可見性(volatile修飾符)、可序列化與否(transient修飾符)、字段數據類型(基本類型、對象、數組)以及字段名稱。在字段表中,變量修飾符使用標志位表示,字段數據類型和字段名稱則引用常量池中常量表示,字段表格式如下表所示:
類型 |
名稱 |
數量 |
u2 |
access_flags |
1 |
u2 |
name_index |
1 |
u2 |
descriptor_index |
1 |
u2 |
attributes_count |
1 |
attribute_info |
attributes |
attributes_count |
字段修飾符放在access_flags中,占2字節,其值為0x0002,可見這個字段由private修飾,與訪問標志位十分相似
標志名稱 |
標志值 |
含義 |
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 |
7.方法表集合

methods_count:方法表計數器,即方法表集合中的方法表數據個數。占2字節,其值為0x0002,即測試類中有2個方法(還自動增加了一個構造函數)
methods:方法表集合,一組方法表類型數據的集合。方法表結構和字段表結構一樣:
類型 |
名稱 |
數量 |
u2 |
access_flags |
1 |
u2 |
name_index |
1 |
u2 |
descriptor_index |
1 |
u2 |
attributes_count |
1 |
attribute_info |
attributes |
attributes_count |
數據項的含義非常相似,僅在訪問標志位和屬性表集合中的可選項上有略微不同
由於ACC_VOLATILE標志和ACC_TRANSIENT標志不能修飾方法,所以access_flags中不包含這兩項,同時增加ACC_SYNCHRONIZED標志、ACC_NATIVE標志、ACC_STRICTFP標志和ACC_ABSTRACT標志
標志名稱 |
標志值 |
含義 |
ACC_PUBLIC |
0x0001 |
字段是否為public |
ACC_PRIVATE |
0x0002 |
字段是否為private |
ACC_PROTECTED |
0x0004 |
字段是否為protected |
ACC_STATIC |
0x0008 |
字段是否為static |
ACC_FINAL |
0x0010 |
字段是否為final |
ACC_SYNCHRONIZED |
0x0020 |
字段是否為synchronized |
ACC_BRIDGE |
0x0040 |
方法是否是由編譯器產生的橋接方法 |
ACC_VARARGS |
0x0080 |
方法是否接受不定參數 |
ACC_NATIVE |
0x0100 |
字段是否為native |
ACC_ABSTRACT |
0x0400 |
字段是否為abstract |
ACC_STRICTFP |
0x0800 |
字段是否為strictfp |
ACC_SYNTHETIC |
0x1000 |
字段是否為編譯器自動產生 |
第一個方法(由編譯器自動添加的默認構造方法):

access_flags為0x0001,即public;name_index為0x0007,即常量池中第7個常量;descriptor_index為0x0008,即常量池中第8個常量
- const #7 = Asciz <init>;
- const #8 = Asciz ()V;

接下來2個字節為屬性計數器,其值為0x0001,說明這個方法的屬性表集合中有一個屬性,屬性名稱為接下來2位0x0009,指向常量池中第9個常量:Code。接下來4位為0x0000002F,表示Code屬性值的字節長度為47。接下來2位為0x0001,表示該方法的操作數棧的深度最大值為1。接下來2位依然為0x0001,表示該方法的局部變量占用空間為1。接下來4位為0x0000005,則緊接着的5個字節0x2AB7000AB1為該方法編譯后生成的字節碼指令(各字節對應的指令不介紹了,可查詢虛擬機字節碼指令表)。接下來2個字節為0x0000,說明Code屬性異常表集合為空。

接下來2個字節為0x0002,說明Code屬性帶有2個屬性,那么接下來2位0x000C即為Code屬性第一個屬性的屬性名稱,指向常量池中第12個常量:LineNumberTable。接下來4位為0x00000006,表示LineNumberTable屬性值所占字節長度為6。接下來2位為0x0001,即該line_number_table中只有一個line_number_info表,start_pc為0x0000,line_number為0x0003,LineNumberTable屬性結束。

接下來2位0x000D為Code屬性第二個屬性的屬性名,指向常量池中第13個常量:LocalVariableTable。該屬性值所占的字節長度為0x0000000C=12。接下來2位為0x0001,說明local_variable_table中只有一個local_variable_info表,按照local_variable_info表結構,start_pc為0x0000,length為0x0005,name_index為0x000E,指向常量池中第14個常量:this,descriptor_index為0x000F,指向常量池中第15個常量:Lcom/test/Test;,index為0x0000。第一個方法結束
第二個方法:

access_flags為0x0001,即public;name_index為0x0010,即常量池中第16個常量;descriptor_index為0x0011,即常量池中第17個常量
- const #16 = Asciz getM;
- const #17 = Asciz ()I;

接下來2個字節為屬性計數器,其值為0x0001,說明這個方法有一個方法屬性,屬性名稱為接下來2位0x0009,指向常量池中第9個常量:Code。接下來4位為0x00000031,表示Code屬性值的字節長度為49。接下來2位為0x0002,表示該方法的操作數棧的深度最大值為2。接下來2位為0x0001,表示該方法的局部變量占用空間為1。接下來4位為0x0000007,則緊接着的7個字節0x2AB400120460AC為該方法編譯后生成的字節碼指令。接下來2個字節為0x0000,說明Code屬性異常表集合為空。

接下來2個字節為0x0002,說明Code屬性帶有2個屬性,那么接下來2位0x000C即為Code屬性第一個屬性的屬性名稱,指向常量池中第12個常量:LineNumberTable。接下來4位為0x00000006,表示LineNumberTable屬性值所占字節長度為6。接下來2位為0x0001,即該line_number_table中只有一個line_number_info表,start_pc為0x0000,line_number為0x0007,LineNumberTable屬性結束。

和第一個方法的LocalVariableTable屬性基本相同,唯一的區別是局部變量this的作用范圍覆蓋的長度為7而不是5,第二個方法結束
如果子類沒有重寫父類的方法,方法表集合中就不會出現父類方法的信息;有可能會出現由編譯器自動添加的方法(如:<init>,實例類構造器)
在Java語言中,重載一個方法除了要求和原方法擁有相同的簡單名稱外,還要求必須擁有一個與原方法不同的特征簽名(方法參數集合),由於特征簽名不包含返回值,故Java語言中不能僅僅依靠返回值的不同對一個已有的方法重載;但是在Class文件格式中,特征簽名即為方法描述符,只要是描述符不完全相同的2個方法也可以合法共存,即2個除了返回值不同之外完全相同的方法在Class文件中也可以合法共存
javap工具在后半部分會列出分析完成的方法(可以看到和我們的分析結果是一樣的):
- d:\>javap -verbose Test
- ......
- {
- public com.test.Test();
- Code:
- Stack=1, Locals=1, Args_size=1
- 0: aload_0
- 1: invokespecial #10; //Method java/lang/Object."<init>":()V
- 4: return
- LineNumberTable:
- line 3: 0
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this Lcom/test/Test;
- public int getM();
- Code:
- Stack=2, Locals=1, Args_size=1
- 0: aload_0
- 1: getfield #18; //Field m:I
- 4: iconst_1
- 5: iadd
- 6: ireturn
- LineNumberTable:
- line 7: 0
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 7 0 this Lcom/test/Test;
- }
8.屬性表集合
在Class文件、屬性表、方法表中都可以包含自己的屬性表集合,用於描述某些場景的專有信息
與Class文件中其它數據項對長度、順序、格式的嚴格要求不同,屬性表集合不要求其中包含的屬性表具有嚴格的順序,並且只要屬性的名稱不與已有的屬性名稱重復,任何人實現的編譯器可以向屬性表中寫入自己定義的屬性信息。虛擬機在運行時會忽略不能識別的屬性,為了能正確解析Class文件,虛擬機規范中預定義了虛擬機實現必須能夠識別的9項屬性:
屬性名稱 |
使用位置 |
含義 |
Code |
方法表 |
Java代碼編譯成的字節碼指令 |
ConstantValue |
字段表 |
final關鍵字定義的常量值 |
Deprecated |
類文件、字段表、方法表 |
被聲明為deprecated的方法和字段 |
Exceptions |
方法表 |
方法拋出的異常 |
InnerClasses |
類文件 |
內部類列表 |
LineNumberTale |
Code屬性 |
Java源碼的行號與字節碼指令的對應關系 |
LocalVariableTable |
Code屬性 |
方法的局部變量描述 |
SourceFile |
類文件 |
源文件名稱 |
Synthetic |
類文件、方法表、字段表 |
標識方法或字段是由編譯器自動生成的 |
每種屬性均有各自的表結構。這9種表結構有一個共同的特點,即均由一個u2類型的屬性名稱開始,可以通過這個屬性名稱來判段屬性的類型
Code屬性:Java程序方法體中的代碼經過Javac編譯器處理后,最終變為字節碼指令存儲在Code屬性中。當然不是所有的方法都必須有這個屬性(接口中的方法或抽象方法就不存在Code屬性),Code屬性表結構如下:
類型 |
名稱 |
數量 |
u2 |
attribute_name_index |
1 |
u4 |
attribute_length |
1 |
u2 |
max_stack |
1 |
u2 |
max_locals |
1 |
u4 |
code_length |
1 |
u1 |
code |
code_length |
u2 |
exception_table_length |
1 |
exception_info |
exception_table |
exception_table_length |
u2 |
attributes_count |
1 |
attribute_info |
attributes |
attributes_count |
max_stack:操作數棧深度最大值,在方法執行的任何時刻,操作數棧深度都不會超過這個值。虛擬機運行時根據這個值來分配棧幀的操作數棧深度
max_locals:局部變量表所需存儲空間,單位為Slot(參見備注四)。並不是所有局部變量占用的Slot之和,當一個局部變量的生命周期結束后,其所占用的Slot將分配給其它依然存活的局部變量使用,按此方式計算出方法運行時局部變量表所需的存儲空間
code_length和code:用來存放Java源程序編譯后生成的字節碼指令。code_length代表字節碼長度,code是用於存儲字節碼指令的一系列字節流。
每一個指令是一個u1類型的單字節,當虛擬機讀到code中的一個字節碼(一個字節能表示256種指令,Java虛擬機規范定義了其中約200個編碼對應的指令),就可以判斷出該字節碼代表的指令,指令后面是否帶有參數,參數該如何解釋,雖然code_length占4個字節,但是Java虛擬機規范中限制一個方法不能超過65535條字節碼指令,如果超過,Javac將拒絕編譯
ConstantValue屬性:通知虛擬機自動為靜態變量賦值,只有被static關鍵字修飾的變量(類變量)才可以使用這項屬性。其結構如下:
類型 |
名稱 |
數量 |
u2 |
attribute_name_index |
1 |
u4 |
attribute_length |
1 |
u2 |
constantvalue_index |
1 |
可以看出ConstantValue屬性是一個定長屬性,其中attribute_length的值固定為0x00000002,constantvalue_index為一常量池字面量類型常量索引(Class文件格式的常量類型中只有與基本類型和字符串類型相對應的字面量常量,所以ConstantValue屬性只支持基本類型和字符串類型)
對非static類型變量(實例變量,如:int a = 123;)的賦值是在實例構造器<init>方法中進行的
對類變量(如:static int a = 123;)的賦值有2種選擇,在類構造器<clinit>方法中或使用ConstantValue屬性。當前Javac編譯器的選擇是:如果變量同時被static和final修飾(虛擬機規范只要求有ConstantValue屬性的字段必須設置ACC_STATIC標志,對final關鍵字的要求是Javac編譯器自己加入的要求),並且該變量的數據類型為基本類型或字符串類型,就生成ConstantValue屬性進行初始化;否則在類構造器<clinit>方法中進行初始化
Exceptions屬性:列舉出方法中可能拋出的受查異常(即方法描述時throws關鍵字后列出的異常),與Code屬性平級,與Code屬性包含的異常表不同,其結構為:
類型 |
名稱 |
數量 |
u2 |
attribute_name_index |
1 |
u4 |
attribute_length |
1 |
u2 |
number_of_exceptions |
1 |
u2 |
exception_index_table |
number_of_exceptions |
number_of_exceptions表示可能拋出number_of_exceptions種受查異常
exception_index_table為異常索引集合,一組u2類型exception_index的集合,每一個exception_index為一個指向常量池中一CONSTANT_Class_info型常量的索引,代表該受查異常的類型
InnerClasses屬性:該屬性用於記錄內部類和宿主類之間的關系。如果一個類中定義了內部類,編譯器將會為這個類與這個類包含的內部類生成InnerClasses屬性,結構為:
類型 |
名稱 |
數量 |
u2 |
attribute_name_index |
1 |
u4 |
attribute_length |
1 |
u2 |
number_of_classes |
1 |
inner_classes_info |
inner_classes |
number_of_classes |
inner_classes為內部類表集合,一組內部類表類型數據的集合,number_of_classes即為集合中內部類表類型數據的個數
每一個內部類的信息都由一個inner_classes_info表來描述,inner_classes_info表結構如下:
類型 |
名稱 |
數量 |
u2 |
inner_class_info_index |
1 |
u2 |
outer_class_info_index |
1 |
u2 |
inner_name_index |
1 |
u2 |
inner_name_access_flags |
1 |
inner_class_info_index和outer_class_info_index指向常量池中CONSTANT_Class_info類型常量索引,該CONSTANT_Class_info類型常量指向常量池中CONSTANT_Utf8_info類型常量,分別為內部類的全限定名和宿主類的全限定名
inner_name_index指向常量池中CONSTANT_Utf8_info類型常量的索引,為內部類名稱,如果為匿名內部類,則該值為0
inner_name_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_INTERFACE |
0x0020 |
內部類是否為一個接口 |
ACC_ABSTRACT |
0x0400 |
內部類是否為abstract |
ACC_SYNTHETIC |
0x1000 |
內部類是否為編譯器自動產生 |
ACC_ANNOTATION |
0x4000 |
內部類是否是一個注解 |
ACC_ENUM |
0x4000 |
內部類是否是一個枚舉 |
LineNumberTale屬性:用於描述Java源碼的行號與字節碼行號之間的對應關系,非運行時必需屬性,會默認生成至Class文件中,可以使用Javac的-g:none或-g:lines關閉或要求生成該項屬性信息,其結構如下:
類型 |
名稱 |
數量 |
u2 |
attribute_name_index |
1 |
u4 |
attribute_length |
1 |
u2 |
line_number_table_length |
1 |
line_number_info |
line_number_table |
line_number_table_length |
line_number_table是一組line_number_info類型數據的集合,其所包含的line_number_info類型數據的數量為line_number_table_length,line_number_info結構如下:
類型 |
名稱 |
數量 |
說明 |
u2 |
start_pc |
1 |
字節碼行號 |
u2 |
line_number |
1 |
Java源碼行號 |
不生成該屬性的最大影響是:1,拋出異常時,堆棧將不會顯示出錯的行號;2,調試程序時無法按照源碼設置斷點
LocalVariableTable屬性:用於描述棧幀中局部變量表中的變量與Java源碼中定義的變量之間的關系,非運行時必需屬性,默認不會生成至Class文件中,可以使用Javac的-g:none或-g:vars關閉或要求生成該項屬性信息,其結構如下:
類型 |
名稱 |
數量 |
u2 |
attribute_name_index |
1 |
u4 |
attribute_length |
1 |
u2 |
local_variable_table_length |
1 |
local_variable_info |
local_variable_table |
local_variable_table_length |
local_variable_table是一組local_variable_info類型數據的集合,其所包含的local_variable_info類型數據的數量為local_variable_table_length,local_variable_info結構如下:
類型 |
名稱 |
數量 |
說明 |
u2 |
start_pc |
1 |
局部變量的生命周期開始的字節碼偏移量 |
u2 |
length |
1 |
局部變量作用范圍覆蓋的長度 |
u2 |
name_index |
1 |
指向常量池中CONSTANT_Utf8_info類型常量的索引,局部變量名稱 |
u2 |
descriptor_index |
1 |
指向常量池中CONSTANT_Utf8_info類型常量的索引,局部變量描述符 |
u2 |
index |
1 |
局部變量在棧幀局部變量表中Slot的位置,如果這個變量的數據類型為64位類型(long或double), 它占用的Slot為index和index+1這2個位置 |
start_pc + length即為該局部變量在字節碼中的作用域范圍
不生成該屬性的最大影響是:1,當其他人引用這個方法時,所有的參數名稱都將丟失,IDE可能會使用諸如arg0、arg1之類的占位符代替原有的參數名稱,對代碼運行無影響,會給代碼的編寫帶來不便;2,調試時調試器無法根據參數名稱從運行上下文中獲取參數值
SourceFile屬性:用於記錄生成這個Class文件的源碼文件名稱,為可選項,可以使用Javac的-g:none或-g:source關閉或要求生成該項屬性信息,其結構如下:
型 |
名稱 |
數量 |
u2 |
attribute_name_index |
1 |
u4 |
attribute_length |
1 |
u2 |
sourcefile_index |
1 |
可以看出SourceFile屬性是一個定長屬性,sourcefile_index是指向常量池中一CONSTANT_Utf8_info類型常量的索引,常量的值為源碼文件的文件名
對大多數文件,類名和文件名是一致的,少數特殊類除外(如:內部類),此時如果不生成這項屬性,當拋出異常時,堆棧中將不會顯示出錯誤代碼所屬的文件名
Deprecated屬性和Synthetic屬性:這兩個屬性都屬於標志類型的布爾屬性,只存在有和沒有的區別,沒有屬性值的概念
Deprecated屬性表示某個類、字段或方法已經被程序作者定為不再推薦使用,可在代碼中使用@Deprecated注解進行設置
Synthetic屬性表示該字段或方法不是由Java源碼直接產生的,而是由編譯器自行添加的(當然也可設置訪問標志中的ACC_SYNTHETIC標志,所有由非用戶代碼產生的類、方法和字段都應當至少設置Synthetic屬性和ACC_SYNTHETIC標志位中的一項,唯一的例外是實例構造器<init>和類構造器<clinit>方法)
這兩項屬性的結構為(當然attribute_length的值必須為0x00000000):
類型 |
名稱 |
數量 |
u2 |
attribute_name_index |
1 |
u4 |
attribute_length |
1 |

起始2位為0x0001,說明有一個類屬性。接下來2位為屬性的名稱,0x0014,指向常量池中第20個常量:SourceFile。接下來4位為0x00000002,說明屬性體長度為2字節。最后2個字節為0x0014,指向常量池中第21個常量:Test.java,即這個Class文件的源碼文件名為Test.java
PS:
1,全限定名:將類全名中的“.”替換為“/”,為了保證多個連續的全限定名之間不產生混淆,在最后加上“;”表示全限定名結束。例如:"com.test.Test"類的全限定名為"com/test/Test;"
2,簡單名稱:沒有類型和參數修飾的方法或字段名稱。例如:"public void add(int a,int b){...}"該方法的簡單名稱為"add","int a = 123;"該字段的簡單名稱為"a"
3,描述符:描述字段的數據類型、方法的參數列表(包括數量、類型和順序)和返回值。根據描述符規則,基本數據類型和代表無返回值的void類型都用一個大寫字符表示,而對象類型則用字符L加對象全限定名表示
標識字符 |
含義 |
B |
基本類型byte |
C |
基本類型char |
D |
基本類型double |
F |
基本類型float |
I |
基本類型int |
J |
基本類型long |
S |
基本類型short |
Z |
基本類型boolean |
V |
特殊類型void |
L |
對象類型,如:Ljava/lang/Object; |
對於數組類型,每一維將使用一個前置的“[”字符來描述,如:"int[]"將被記錄為"[I","String[][]"將被記錄為"[[Ljava/lang/String;"
用描述符描述方法時,按照先參數列表,后返回值的順序描述,參數列表按照參數的嚴格順序放在一組"()"之內,如:方法"String getAll(int id,String name)"的描述符為"(I,Ljava/lang/String;)Ljava/lang/String;"
4,Slot,虛擬機為局部變量分配內存所使用的最小單位,長度不超過32位的數據類型占用1個Slot,64位的數據類型(long和double)占用2個Slot