在了解了Java內存的散布、HotSpot虛擬機對Java對象的管理和Java垃圾搜集機制以后,我們大致了解了Java自動內存管理的部份。接下來,就應當看看Java的類加載機制,看看虛擬機是如何將Java代碼文件編譯后的class文件加載到Java內存中的。
Java是1門平臺無關語言,只要有Java的運行環境,編寫的代碼可以運行在各種機器上,做到了“1次編碼、處處運行”的目的。為了到達平臺無關,Sun公司和其它虛擬機提供商發布了許多可以運行不同平臺上的虛擬機,這些虛擬機都可以載入和履行同1種平臺無關的字節碼,到達了平臺無關的目的,以下圖:
不過,java虛擬機不但可以運行Java程序,在設計之初就實現了讓其他語言運行在Java虛擬機上的可能性,只要程序編譯以后能生成符合虛擬機規范的class文件便可。現在,已有很多語言可以運行在java虛擬機上了,比如Clojure、Groovy、JRuby、Jython、Scala等。這樣,java虛擬機也實現了語言無關性。上面的圖就變成了這個模樣:
可以看出,語言無關性和平臺無關性的關鍵在于class字節碼文件。Java虛擬機規范要求class文件中使用許多強迫性和若干其他輔助信息。接下來就詳細了解1下class字節碼文件的結構,固然,這里主要以Java語言為主。
class文件是1組以8位字節為基礎的2進制流,各個數據項目嚴格依照順序緊湊地排列在class文件中,中間沒有任何分隔符,這點和png、jpg等圖片文件格式類似。當遇到需要占用8位字節以上空間的數據項時,則會依照1定的字節順序分隔為若干個8位字節進行存儲。
Java虛擬機規范規定class文件格式采取1種類似于C語言結構體的偽結構來存儲數據,這類偽結構只有兩種數據類型:無符號數和表。其中無符號數屬于基本的數據類型,以u1、u2、u4、u8來分別代表1個字節、2個字節、4個字節和8個字節。無符號數可以用來描寫數字、索引援用、數量值或依照utf⑻編碼構成的字符串值。而表是由多個無符號數或其他表構成的復合數據結構,所有的表都以“_info”結尾。表用于描寫有層次關系的復合結構的數據,其實,全部class文件就是1張表。它的整體結構以下圖:
在這張圖中,每行表示兩個字節長度,依照從上到下、從左到右的順序描寫了class文件的結構。其中,淺色彩的部份是無符號數,深色彩的部份是表。下面以表格的情勢詳細描寫1下具體的信息:
類型 |
名稱 |
數量 |
U4 |
magic |
1 |
U2 |
minor_version |
1 |
U2 |
major_version |
1 |
U2 |
constant_pool_count |
1 |
cp_info |
constant_pool |
constant_pool_count⑴ |
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 |
不管是無符號數還是表,當需要描寫同1類型但數量不定的多個數據時,常常會用到1個前置的容量計數器(表中以“_count”結尾的項)加上若干個連續的數據項的情勢,這時候稱這1系列連續的某1類型的數據為某1類型的集合。
為了介紹各個數據項的含義,這里以1個簡單的Java程序為例,代碼以下:
接下來看看各項的具體含義。
這部份的2進制流內容:
class文件的頭4個字節稱為魔數,它的唯1作用就是肯定這個文件時候是1個能被虛擬機接受的class文件。很多圖片格式都用1個魔數來標識文件類型,比如png和jpg等。在java的class文件中,這個數是0xcafebabe。
接下來就是class文件的版本號,第5、6個字節是次版本號,第7、8個字節是主版本號。在這里,次版本號是0,主版本號是52,(106進制是34)。Java的版本號是從45開始的,JDK1.1以后的每個JDK大版本發布主版本號向上加1,高版本的JDK能向下兼容低版本的JDK。
緊接著主版本號的就是常量池,常量池可以理解為class文件的資源倉庫,它是class文件結構中與其它項目關聯最多的數據類型,也是占用class文件空間最大的數據項目之1,也是class文件中第1個出現的表類型數據項目。
由于常量池中常量的數量不是固定的,所以常量池入口需要放置1項u2類型的數據,代表常量池中的容量計數。不過,這里需要注意的是,這個容器計數是從1開始的而不是從0開始,也就是說,常量池中常量的個數是這個容器計數⑴。將0空出來的目的是滿足后面某些指向常量池的索引值的數據在特定情況下需要表達“不援用任何1個常量池項目”的含義。class文件中只有常量池的容量計數是從1開始的,對其它集合類型,比如接口索引集合、字段表集合、方法表集合等的容量計數都是從0開始的。
常量池中主要寄存兩大類常量:字面量和符號援用。字面量比較接近Java語言的常量概念,如文本字符串、聲明為final的常量等。而符號援用則屬于編譯原理方面的概念,它包括3方面的內容:
Java代碼在進行javac編譯的時候其實不像C和C++那樣有連接這1步,而是在虛擬機加載class文件的時候進行動態連接。也就是說,在class文件中不會保存各個方法、字段的終究內存布局信息,因此這些字段、方法的符號援用不經過運行期轉換的話沒法得到真實的內存入口地址,虛擬機也就沒法使用。當虛擬機運行時,需要從常量池取得對應的符號援用,再在類創建時或運行時解析、翻譯到具體的內存地址中。
常量池中的每項都是1個表,在JDK1.7之前有11中結構不同的表結構,在JDK1.7中為了更好的支持動態語言調用,又增加了3種(CONSTANT_MethodHandle_info、CONSTANT_MethodType_info和CONSTANT_InvokeDynamic_info)。不過這里不會介紹這3種表數據結構。
這14個表的開始第1個字節是1個u1類型的tag,用來標識是哪種常量類型。這14種常量類型所代表的含義以下:
類型 |
標志 |
含義 |
CONSTANT_Utf8_info |
1 |
UTF⑻編碼的字符串 |
CONSTANT_Integer_info |
3 |
整型字面量 |
CONSTANT_Float_info |
4 |
浮點型字面量 |
CONSTANT_Long_info |
5 |
長整形字面量 |
CONSTANT_Double_info |
6 |
雙精度浮點型字面量 |
CONSTANT_Class_info |
7 |
類或接口的符號援用 |
CONSTANT_String_info |
8 |
字符串類型字面量 |
CONSTANT_Fieldref_info |
9 |
字段的符號援用 |
CONSTANT_Methodref_info |
10 |
類中方法的符號援用 |
CONSTANT_InterfaceMethod_info |
11 |
接口中方法的符號援用 |
CONSTANT_NameAndType_info |
12 |
字段或方法的部份符號援用 |
CONSTANT_MethodHandle_info |
15 |
表示方法句柄 |
CONSTANT_MethodType_info |
16 |
標識方法類型 |
CONSTANT_InvokeDynamic_info |
18 |
表示1個動態方法調用點 |
本例的常量池部份以下:
藍色彩覆蓋的是常量池部份,可以看到這部份的內容非常多。由于常量池中的常量比較多,每中常量還有自己的結構,致使常量池的結構非常復雜,這里也僅僅是簡單解析兩個例子。
由class文件結構圖可知,常量池的開頭兩個字節0x001A是常量池的容量計數,這里是26,也就是說,這個常量池中有25個常量項。看看這個例子的第1項,容量計數后面的第1個字節標識這個常量的類型,是0A,即10,查表可知是類方法的符號援用,這個常量表的結構以下:
類型 |
名稱 |
數量 |
U1 |
tag |
1 |
U2 |
name_index |
1 |
U2 |
descriptor_index |
1 |
依照這個結構,可以知道name_index是7(0x0007),descriptor_index是21(0x0015)。這都是1個索引,指向常量池中的其他常量,其中name描寫了這個方法的名稱,descriptor描寫了這個方法的訪問標志(比如public、private等)、參數類型和返回類型。
接下來的tag是9,可知是1個字段的符號援用,它的結構和方法的結構類似,只不過接下來的兩個字節表示的是聲明這個字段的類或接口的索引,最后的兩個字節表示的是這個字段的類型和名字CONSTANT_NameAndType索引,這兩個索引分別是6和22,在后面會驗證這幾個索引。
根據這兩個例子可以看出,要準確的描寫1個類中所聲明的字段和方法的所有信息,僅僅1個符號援用是不夠的,還需要繼續援用其他的常量池項目。
常量池中接下來的內容也能夠這樣解析,不過,JDK已提供了1個工具可以自動計算這些內容,使用javap -verbose命令可以快速的計算出class文件結構的內容,比如這樣:
首先是1個方法符號援用,內容是7.21,查看結果,可以看到索引為7的常量是1個類符號援用,這個類符號援用的索引是25,然后看看索引是25的常量,是1個Utf8編碼的字符串,內容是java/lang/Object。然后看看索引是21的常量,是1個NameAndType類型,這個常量的內容是12:13,索引是12的內容是<init>,索引是13的內容是()V,這表示了1個方法的名稱、參數類型和返回類型,具體的含義在后面的方法表中介紹。這樣,這個方法的內容就是java/lang/Object."<init>":()V。
看起來這個<init>并沒有在Java程序中出現,還有1些內容也沒有在Java程序中出現,比如“I”、“V”、“LineNumberTable”等。這是自動生成的常量,但它們會被后面行將介紹到的字段表、方法表和屬性表援用到,用來描寫1些不方便使用固定字節表示的內容。
最后,給出14種常量項的結構:
常量 |
項目 |
類型 |
含義 |
CONSTANT_Utf8_info |
tag |
U1 |
1 |
length |
U2 |
UTF⑻編碼的字符串的長度 |
|
bytes |
U1 |
長度為length的UTF⑻編碼的字符串 |
|
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_InterfaceMethod_info |
tag |
U1 |
11 |
index |
U2 |
指向聲明方法的接口描寫符COSNTANT_Class_info的索引項 |
|
index |
U2 |
指向名稱及類描寫符CONSTANT_NameAndType_info的索引項 |
|
CONSTANT_NameAndType_info |
tag |
U1 |
12 |
index |
U2 |
指向該字段或方法名稱常量池的索引 |
|
index |
U2 |
指向該字段或方法描寫符常量池的索引 |
|
CONSTANT_MethodHandle_info |
tag |
U1 |
15 |
reference_kind |
U2 |
值必須在1⑼之間,決定了方法句柄的類型,方法句柄累心的值表示方法句柄的字節碼行動 |
|
reference_ index |
U2 |
值必須是對常量池的有效索引 |
|
CONSTANT_MethodType_info |
tag |
U1 |
16 |
descriptor_index |
U2 |
值必須是對常量池的有效索引,常量池在改索引處的項必須是CONSTANT_Utf8_info結構,表示方法的描寫符 |
|
CONSTANT_InvokeDynamic_info |
tag |
U1 |
18 |
bootstrap_method_attrindex |
U2 |
值必須是對當前Class文件中引導方法表的bootstrap_methods[]數組的有效索引 |
|
name_and_type_index |
U2 |
值必須是對當前常量池的有效索引,常量池在該索引處的項必須是COSTANT_NameAndType_info結構,表示方法名和方法描寫符 |
常量池結束后緊接著的兩個字節代表訪問標志,用來標識1些類或接口的訪問信息,包括:這個Class是類還是接口;是不是定義為public;是不是定義為abstract;如果是類的話,是不是被聲明為final等。具體的標志位和含義以下表:
標志名稱 |
標志值 |
含義 |
ACC_PUBLIC |
0x0001 |
是不是是public |
ACC_FINAL |
0x0010 |
是不是被聲明為final,只有類可以設置 |
ACC_SUPER |
0x0020 |
是不是允許使用invokespecial字節碼指令的新語義,JDK1.0.2以后編譯出來的類的這個標志默許為真 |
ACC_INTERFACE |
0x0200 |
標識是1個接口 |
ACC_ABSTRACT |
0x0400 |
是不是是abstract,對接口和抽象類來講為真,其他類都為假 |
ACC_SYNITHETIC |
0x1000 |
標識這個類并不是由用戶代碼產生 |
ACC_ANNOTATION |
0x2000 |
標識這是1個注解 |
ACC_ENUM |
0x4000 |
標識這是1個枚舉類 |
這部份的2進制流內容以下:
由于access_flags是兩個字節大小,1共有106個標志位可使用,當前僅僅定義了8個,沒有用到的標志位都是0。對1個類來講,可能會有多個訪問標志,這時候就能夠對比上表中的標志值取或運算的值。拿上面那個例子來講,它的訪問標志值是0x0021,查表可知,這是ACC_PUBLIC和ACC_SUPER值取或運算的結果。所以Test這個類的訪問標志就是ACC_PUBLIC和ACC_SUPER,這1點我們可以在javap得到的結果中驗證。
在訪問標志access_flags后接下來就是類索引(this_class)和父類索引(super_class),這兩個數據都是u2類型的,而接下來的接口索引集合是1個u2類型的集合,class文件由這3個數據項來肯定類的繼承關系。由于Java中是單繼承,所以父類索引只有1個;但Java類可以實現多個接口,所以接口索引是1個集合。
類索援用來肯定這個類的全限定名,這個全限定名就是說1個類的類名包括所有的包名,然后使用"/"代替"."。比如Object的全限定名是java.lang.Object。父類索引肯定這個類的父類的全限定名,除Object以外,所有的類都有父類,所以除Object以外所有類的父類索引都不為0.接口索引集合存儲了implements語句后面依照從左到右的順序的接口。
類索引和父類索引都是1個索引,這個索引指向常量池中的CONSTANT_Class_info類型的常量。然后再CONSTANT_Class_info常量中的索引就能夠找到常量池中類型為CONSTANT_Utf8_info的常量,而這個常量保存著類的全限定名。
這部份的2進制流內容以下:
以上面的例子來講,this_class的值是0x0006,即10進制的6,指向的CONSTANT_Class_info中的索引是24,常量池中索引是24的CONSTANT_Utf8_info的常量是1個長度為4的字符串,值是“Test”。這樣就解析到了這個類的全限定名,類的父類的全限定名也能夠這樣解析。下圖是解析進程:
由于這個類沒有實現接口,所以接口索引集合的容量計數是0。如果容量計數是0,就不需要存儲接口的信息。
字段表用來描寫接口或類中聲明的變量。字段包括類級變量和實例級變量,但不包括方法內變量。所謂的類級變量就是靜態變量,這個變量不屬于這個類的任何實例,可以不用定義類實例就能夠使用;實例級變量不是靜態變量,是和類實例相干聯的,需要定義類實例才能使用。
那末,聲明1個變量需要哪些信息呢?有:字段的作用域(public、private和protected修飾符)、是實例變量還是類變量(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,和類中的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 |
明顯,ACC_PUBLIC、ACC_PRIVATE和ACC_PROTECTED只能選擇1個,ACC_FINAL和ACC_VOLATILE不能同時選擇。接口中的字段必須有ACC_PUBLIC、ACC_STATIC和ACC_FINAL標志,這是Java語言本身的規則決定的。
access_flags給出了字段中所有可以用布爾值表示的修飾符,剩下的信息就是字段的名字、變量類型等信息。access_flags后面的是name_index和descriptor_index,前者是字段名的常量池索引,后者是字段描寫符的常量池索引。name_index可以描寫字段的名字,descriptor_index可以描寫字段的數據類型。不過,對方法的描寫符來講就要復雜1些,由于1個方法除返回值類型,還有參數類型,而且參數的個數還不肯定。根據描寫符規則,這些類型都使用1個大寫字母來表示,以下表:
標識字符 |
含義 |
標識字符 |
含義 |
B |
byte |
J |
long |
C |
char |
S |
short |
D |
double |
Z |
boolean |
F |
float |
V |
void |
I |
int |
L |
對象類型,如Ljava/lang/Object |
對數組類型,每個維度將使用1個前置的“[”字符來描寫。比如定義1個java.lang.String[][]類型的2維數組,將記錄為"[[Ljava/lang/String",1個double數組"double[]"將標記為"[D"。
當描寫符用來描寫方法時,依照先參數列表,后返回值的順序描寫,參數列表依照參數的嚴格順序放在1組小括號"()"內。比如方法void inc()的描寫符是:()V。方法java.lang.String toString()的描寫符是:()Ljava/lang/String。方法int indexOf(char[] source,int sourceOffset,int sourceCount,char[] target,int targetOffset,int targetCount,int fromIndex)的描寫符是:([CII[CIII)I。
descriptor_info后面是屬性信息,這會在后面屬性表集合中介紹。
本部份的2進制流內容:
以上面的例子為例,前兩個字節代表字段的個數,這里是2個。接下來就是具體的字段信息。第1個字段表內容是:0009 0008 0009 0000,首先訪問標志是9,可以看出是ACC_PUBLIC和ACC_STATIC,是1個靜態常量,name_index是8,指向的常量項是CONSTANT_Utf8_info,內容是PI,描寫符是8,常量池中的常量是CONSTANT_Utf8_info,內容是D,即double類型。所以這個常量是:public static double PI。和我們聲明的1樣,不過還有1點就是,我們聲明的PI還有1個值:3.14,這個數在常量池中可以找到,索引是3的常量,不過這個值是如何與PI關聯起來的,后面會介紹。
一樣的道理也能解析出第2個字段是:private int m。
字段表集合中不會列出從父類或接口中繼承來的字段,但有可能會出現本來Java程序中沒有的字段。比較典型的例子是內部類,為了在內部類中保持對外部類的訪問性,會增加1個指向外部類實例的字段。另外,在Java語言中字段沒法重載,也就是字段名不能重復,即便兩個字段的數據類型、修飾符都不相同。不過對字節碼來講,如果兩個字段的描寫符不1致,那末就能夠有重復的字段名。
在字段表集合中介紹了字段的描寫符和方法的描寫符,對理解方法表有很大幫助。class文件存儲格式中對方法的描寫和對字段的描寫幾近相同,方法表的結構也和字段表相同,這里就不再列出。不過,方法表的訪問標志和字段的不同,列出以下:
標識名稱 |
標志值 |
含義 |
ACC_PUBLIC |
0x0001 |
方法是不是是public |
ACC_PRIVATE |
0x0002 |
方法是不是是private |
ACC_PUBLICPROTECTED |
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 |
方法是不是是由編譯器自動產生的 |
本部份2進制流內容:
從這里可以看到,方法表集合中1共有3個方法,依照字段的解析方法,可以得到每一個方法的定義。分別是:
public void <init>();
public int inc();
static void <clinit>();
可是我們的代碼里只定義了1個inc方法,怎樣會多出來兩個方法?
其實,Java類都要有1個構造方法,如果沒有的話編譯器會自動構造1個無參的構造方法,就是上面的第1個名叫<init>的方法;同時,如果1個類中含有靜態代碼塊或靜態變量,那末就需要首先履行類的構造方法,來履行靜態代碼塊和初始化靜態變量,這就是上面的第3個名為<clinit>的方法。
不過,方法比字段還多了方法體呢,那方法體中的代碼哪去了?
在每個方法表中descriptor_index后描寫屬性的時候,0001表明屬性的個數為1,再后面的000E是指向常量池中的CONSTANT_Utf8_info常量,內容是Code,說明后面屬性中寄存的就是方法體里面編譯后的字節碼指令。
在Java中,要重載1個方法,除要與原方法具有相同的方法名稱以外,還要求必須具有1個與原方法不同的特點簽名,特點簽名就是1個方法中各個參數在常量池中的字段符號援用的集合,也就是特點簽名只包括參數個數和類型,其實不包括返回值類型,所以Java語言中是沒法僅僅依托返回值的不同來對1個方法重載的。但是在class文件格式中,特點簽名還包括返回值類型,也就是說只有返回值類型不同的兩個方法也能夠存在。這1點在泛型中編譯后類型擦除后生成的橋接方法上有所體現。不過這里就不過量介紹了。
屬性表在前面出現了屢次,在class文件、字段表和方法表都可以攜帶自己的屬性表集合,來描寫某些場景專有的信息。
與class文件中其他的數據項目要求嚴格的順序、長度和內容不同,屬性表集合的限制比較少,不要求嚴格的順序,只要不與已有的屬性名重復,任何人實現的編譯器都可以向屬性表中寫入自定義的屬性信息,Java虛擬機會在運行時疏忽掉那些不認識的信息。為了能正確解析class文件,《Java虛擬機規范(第2版)》中預定義了9項虛擬機應當辨認的屬性。現在,屬性已到達了21項。具體信息以下表,這里僅對常見的屬性做介紹:
屬性名稱 |
使用位置 |
含義 |
Since |
Code |
方法表 |
Java代碼編譯后的字節碼指令 |
《Java虛擬機規范(第2版)》 |
ConstantValue |
字段表 |
final關鍵字定義的常量值 |
《Java虛擬機規范(第2版)》 |
Deprecated |
類、方法表、字段表 |
被聲明為deprecated的方法和字段 |
《Java虛擬機規范(第2版)》 |
Exception |
方法表 |
方法拋出的異常 |
《Java虛擬機規范(第2版)》 |
EnclosingMethod |
類文件 |
僅當1個類為局部類或匿名類時才能具有這個屬性,來標識這個類所在的外圍方法 |
《Java虛擬機規范(第2版)》 |
InnerClasses |
類文件 |
內部類列表 |
《Java虛擬機規范(第2版)》 |
LineNumberTable |
Code屬性 |
Java源碼的行號與字節碼指令的對應關系 |
《Java虛擬機規范(第2版)》 |
LocalVariableTable |
Code屬性 |
方法的局部變量描寫 |
《Java虛擬機規范(第2版)》 |
StackMapTable |
Code屬性 |
供新的類型檢查驗證器檢查和處理目標方法的局部變量和操作數棧所需要的類型檢查 |
JDK 1.6 |
Signature |
類、方法表、字段表 |
用于保存泛型中的類型信息 |
JDK 1.5 |
SourceFile |
類文件 |
記錄源文件名稱 |
《Java虛擬機規范(第2版)》 |
SourceDebugExtension |
類文件 |
用于存儲額外的調試信息 |
《Java虛擬機規范(第2版)》 |
Synthetic |
類、方法表、字段表 |
標識方法或字段為編譯器自動生成 |
《Java虛擬機規范(第2版)》 |
LocalVariableTypeTable |
類 |
使用特點簽名代替描寫符,為了描寫泛型參數化類型 |
JDK 1.5 |
RuntimeVisibleAnnotations |
類、方法表、字段表 |
為動態注解提供支持 |
JDK 1.5 |
RuntimeInvisibleAnnotations |
類、方法表、字段表 |
與RuntimeVisibleAnnotations作用相反 |
<
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈 ![]()
------分隔線----------------------------
分享到:
------分隔線----------------------------
|