類文件結構
????????各種不同平臺的虛擬機與所有平臺都統(tǒng)一使用的程序存儲格式— 字節(jié)碼( ByteCode ) 是構成平臺無關性的基石.實現(xiàn)語言無關性的基礎仍然是虛擬機和字節(jié)碼存儲格式。Java虛擬機不和包括Java在內的任何語言綁定,它只與“Class文件”這種特定的二進制文件格式所關聯(lián),Class文件中包含了Java虛擬機指令集和符號表以及若干其他輔助信息。基于安全方面的考慮,Java虛擬機規(guī)范要求在Class文件中使用許多強制性的語法和結構化約束,但任一門功能性語言都可以表示 為一個能被Java虛擬機所接受的有效的Class文件。作為一個通用的、機器無關的執(zhí)行平臺, 任何其他語言的實現(xiàn)者都可以將Java虛擬機作為語言的產品交付媒介。
? ??????Java語言中的各種變量、關鍵字和運算符號的語義最終都是由多條字節(jié)碼命令組合而成的 ,因此字節(jié)碼命令所能提供的語義描述能力肯定會比Java語言本身更加強大。因此,有一些Java語言本身無法有效支持的語言特性不代表字節(jié)碼本身無法有效支持,這也為其他語言實現(xiàn)一些有別于Java的語言特性提供了基礎。
Class類文件結構
? ? ? ? Class文件是一組以8位字節(jié)為基礎單位的二進制流,各個數(shù)據(jù)項目嚴格按照順序緊湊地排列在Class文件之中,中間沒有添加任何分隔符,這使得整個Class文件中存儲的內容幾乎全部是程序運行的必要數(shù)據(jù),沒有空隙存在。當遇到需要占用8位字節(jié)以上空間的數(shù)據(jù)項時,則會按照高位在前的方式分割成若干8位字節(jié)進行存儲。這種順序稱為“Big-Endian”,具體是指最高位字節(jié)在地址最低位.最低位字節(jié)在地址最高位的順序來存儲一數(shù)據(jù)。它是SPARC.PowerPc等處理器的默認多字節(jié)存儲順序,而x86等處理器則是使用了相反的“Littlt-Endian”順序來存儲數(shù)據(jù)。
? ? ? ? 根據(jù)Java虛擬機規(guī)范的規(guī)定,Class文件格式采用了一種類似于C語言結構體的偽結構來存儲數(shù)據(jù),這種偽結構中只有兩種數(shù)據(jù)類型:無符號數(shù)和表,后面的解析都要以這兩種數(shù)據(jù)類型為基礎,所以這里要介紹這兩個概念。
? ??????無符號數(shù)屬于基本的數(shù)據(jù)類型, 以 u1 、 u2、 u4、 u8 來分別代表 1 個字節(jié)、 2 個字節(jié)、 4 個字節(jié)和8個字節(jié)的無符號數(shù), 無符號數(shù)可以用來描述數(shù)字、 索引引用、 數(shù)量值或者按照UTF-8編碼構成字符串值。
????????表是由多個無符號數(shù)或者其他表作為數(shù)據(jù)項構成的復合數(shù)據(jù)類型, 所有表都習慣性地以 "_info" 結尾。 表用于描述有層次關系的復合結構的數(shù)據(jù), 整個 Class 文件本質上就是一張“表”它由表 6-1 所示的數(shù)據(jù)項構成。
? ??????無論是無符號數(shù)還是表, 當需要描述同一類型但數(shù)量不定的多個數(shù)據(jù)時, 經常會使用一個前置的容量計數(shù)器加若干個連續(xù)的數(shù)據(jù)項的形式, 這時稱這一系列連續(xù)的某一類型的數(shù)據(jù) 為某一類型的集合。
????????本節(jié)結束之前, 筆者需要再重復講一下, Class 的結構不像XML等描述語言, 由于它沒 有任何分隔符號, 所以在表 6-1 中的數(shù)據(jù)項, 無論是順序還是數(shù)扯, 甚至于數(shù)據(jù)存儲的字節(jié)序 (Byte Ordering, Class 文件中字節(jié)序為 Big-Endian) 這樣的細節(jié), 都是被嚴格限定的, 哪 個字節(jié)代表什么含義, 長度是多少, 先后順序如何, 都不允許改變。 接下來我們將一起看看
魔數(shù)與Class文件的版本
????????每個Class文件的頭四個字節(jié)稱為魔數(shù)(Magic Number),其值為:0xCAFEBABE,它的唯一作用是用于確定這個文件是否為一個能被虛擬機接受的class文件。使用魔數(shù)而不是擴展名來進行識別主要是基于安全的考慮,因為文件的擴展名可以隨意地被改動。緊接著魔數(shù)的四個字節(jié)存儲的是Class文件的版本號,第5和第6個字節(jié)是次版本號(Minor Version),第7和第8個字節(jié)是主版本號(Major Version)。高版本的JDK能向下兼容以前版本的Class文件,不能執(zhí)行以后版本的,即使文件格式未發(fā)生任何變化,虛擬機也必須拒絕執(zhí)行。
常量池
? ? ? ? 緊接著主次版本號之后的是常量池入口,常量池是class文件結構中與其它項目關聯(lián)最多的數(shù)據(jù)類型,也是占用class文件空間最大的數(shù)據(jù)項目之一,同時它還是class文件中第一個出現(xiàn)的表類型數(shù)據(jù)項目。
? ??????由于常量池中常量的數(shù)據(jù)是不固定的,所以在常量池的入口需要放置一項u2類型的數(shù)據(jù),代表常量池容量計算值(constant_pool_count)。與Java語言習慣不一樣的是,這個容量計數(shù)是從1而不是0開始的。將第0項常量出來的目的是為了滿足后面某些指向常量池的索引值的數(shù)據(jù)在特定情況下需要表達“不引用任何一個常量池項目”的意思。class文件結構中只有常量池的容量計數(shù)是從1開始,對于其它集合類型,包括接口索引集合,字段表集合,方法表集合的容量計算都是從0開始的。
????????常量池中主要存放兩大類常量:字面量(Literal)和符號引用(Symbolic References)。字面量比較接近于Java語言層面的常量概念,如文本字符串,被聲明為final的常量值等。而符號引用則屬性編譯原理方面的概念,包含了下面三類常量:
a.類和接口的全限定名(Fully Qualified Name)
b.字段的名稱和描述符(Descriptor)
c.方法的名稱和描述
? ? ? ? Java代碼在進行Javac編譯的時候,在虛擬機加載Class文件的時候進行動態(tài)鏈接。也就是說,在Class文件中不會保存?zhèn)€個方法、字段的最終內存布局信息。因此這些字段、方法的符號引用不經過運行期轉換的話無法得到真正的內存入口地址,也就無法直接被虛擬機使用。當虛擬機運行時,需要從常量池獲得對應的符號引用,再在類創(chuàng)建時或運行時解析、翻譯到具體的內存地址之中。
? ??????常量池中的每一項常量都是一個表,共有11種結構各不相同的表結構數(shù)據(jù),這11種表都有一個共同的特點,就是表開始的第一位是一個u1類型的標志位,代表當前這個常量屬性哪種常量類型,14種常量類型具體含義如下:
各常量項結構:
訪問標志
????????在常量池結束之后, 緊接著的兩個字節(jié)代表訪問標志(access_flags), 這個標志用于識別一些類或者接口層次的訪問信息, 包括:這個 Class 是類還是接口;是否定義為 public 類型;是否定義 為 abstract 類型;如果是類的話, 是否被聲明為 final 等。
類索引、父類索引與接口索引集合
? ? ? ? 類索引(this_class)和父類索引(super_class)都是u2類型的數(shù)據(jù),而接口索引(interfaces)是一組u2類型的數(shù)據(jù)集合,class文件中由這三項數(shù)據(jù)來確定這個類的繼承關系。類索引用于確定這個類的全限定名,父類索引用于確定這個類的父類的全限定名。由于Java語言不允許多繼承,所以父類索引只有一個,除了java.lang.Object之外,所有的Java類都有父類,因了除了java.lang.Object之外,所有Java類的父類索引都不為0。接口索引集合用來描述這個實現(xiàn)實現(xiàn)了哪些接口,這些被實現(xiàn)的接口將按照implements語句后的接口順序從左到右排列在接口的索引集合中。
????????類索引,父類索引和接口索引集合都按順序排列在訪問標志之后,類索引和父類索引用兩個u2類型的索引值表示,它們各自指向一個類型為CONSTANT_Class_info的類描述符常量,通過CONSTANT_Class_info類型的常量中的索引可以找到定義在CONSTANT_Utf8_info類型的常量中的全限定名稱字符串。
????????對于接口索引集合,入口的第一項為u2類型的數(shù)據(jù),表示接口計數(shù)器(interfaces_countD),表示索引表的容量。如果該類沒有實現(xiàn)任何接口,那么該計數(shù)器值為0,后面接口的索引表不再占用任何字節(jié)。
字段表集合
? ? ????字段表(field_info)用于描述接口或類中聲明的變量。字段(field)包括了類級變量或實例變量,但不包括方法內部聲明的變量。描述一個字段的信息有:字段的作用域(public,private,protected修飾符),是類級變量還是實例級變量(static修飾符),可變性(final),并發(fā)可見性(volatile修飾符,是否強制從主內存讀寫),是否可序列化(transient修飾符),字段數(shù)據(jù)類型(基本數(shù)據(jù)類型,對象,數(shù)組),字段名稱。這些信息中,各個修改符都是布爾值,要么有某個修飾符,要么沒有,很適合使用標志位來表示。而字段叫什么名字,字段被定義為什么數(shù)據(jù)類型,這些都是無法固定的,只能引用常量池中的常量來描述。下面是字段表的最終格式。
????????字段修飾符放在access_flags項目中,它與類的access_flags項目是非常相似的,都是一個u2的數(shù)據(jù)類型,其中可以設置的標志位和含義如下表:
????????跟隨access_flags標志的是兩項索引值:name_index和descriptor_index。它們都是對常量池的引用,分別代表著字段的簡單名稱及字段的描述符。現(xiàn)在需要解釋一下“簡單名稱”,“描述符”及前面出現(xiàn)過多次的“全限定名”這三種特殊字符串的概念。
????????全限制名稱和簡單名稱很好理解,如“org/fenixsoft/clazz/TestClass"就是一個類全限制名,僅僅是把類名中的”.“替換成了”/“而已,為了使連續(xù)的多個全限定名之間不產生混淆,在使用時最后一般會加上一個“;”號表示全限定名結束。簡單名稱就是指沒有類型和參數(shù)修飾的方法或字段名稱。
????????相對于全限定名和簡單名稱來說,方法和字段的描述符就要復雜一些。描述符的作用是來用描述字段的數(shù)據(jù)類型,方法的參數(shù)列表(包括數(shù)量,類型及順序)和返回值。根據(jù)描述符規(guī)則,基本數(shù)據(jù)類型(byte,char,double,float,int,long,short,boolean)及代表無返回值的void類型都使用一個大寫字符來表示,而對象類型則用字符L加對象全限定名來表示,如下圖:
????????對于數(shù)組類型,每一維度使用一個前置的“[”字符來描述,如一定義為“java.lang.String[][]”類型的二維數(shù)組,將被記錄為:“[[java/lang/String;”,一個整型數(shù)組“int[]”將被記錄為“[I”。
????????用描述符來描述方法時,按照先參數(shù)列表,后返回值的順序描述,參數(shù)列表按照參數(shù)的嚴格順序在一組小括號“()”之內。如方法void int()描述符為:”()V“,方法java.lang.String toString()描述符為:“()java/lang/String;”
????????字段表都包含的固定數(shù)據(jù)項目到descriptor_index為止就結束了,但是在descriptor_index之后跟隨著一個屬性表集合用于存儲一些額外的信息,字段都可以在屬性表中描述0至多項額外的信息。
????????字段表集合中不會列出超類或父接口中繼承而來的字段,但有可能列表出原來Java代碼中不存在的字段,譬如在內部類中為了保持對外部類的訪問性,會自動添加指向外部類實例的字段。另外,在Java語言中字段是無法重載的,兩個字段的數(shù)據(jù)類型,修飾符不管是否相同,都必須使用不一樣的名稱,但是對于字段碼來講,如果兩個字段的描述符不一致,那字段重名就是合法的。
方法表集合
????????方法表的結構與字段表一樣,依次包含了訪問標志(access_flags),名稱索引(name_index),描述符索引(descriptor_index),屬性表集合(attributes)幾項,如下表所示:
????????因為volatile關鍵字和transient關鍵字不能修改方法,所以方法表的訪問標志中沒有了ACC_VOLATILE與ACC_TRANSIENT標志。與之相對的,synchronized, native, strictfp和abstract關鍵字可以修飾方法,所以方法表的訪問標志中增加了ACC_SYNCHRONIZED,ACC_NATIVE,ACC_STRICTFP,ACC_ABSTRACT標志。對于方法表,所有標志位及取值如下表:
????????方法里面的Java代碼,經過編譯器編譯成字節(jié)碼指令后,存放在方法屬性表集合中一個名為“Cocde”的屬性表中,屬性表是class文件橋口中最具擴展性的一種數(shù)據(jù)項目。
????????與字段表集合相對應的,如果父類方法在子類中沒有被重寫(Override),方法表集合中就不會出現(xiàn)父類的方法。但同樣的,可能會出現(xiàn)由編譯器自動添加的方法,最典型的便是類構造器“”方法和缺省實例構造器“”方法。在Java語言中,要重(Override)一個方法,除了要與原方法具有相同的簡單名稱之外,還要求必須擁有一個與原方法不同的特征簽名,特征簽名是一個方法中各個參數(shù)在常量池中的字段符號引用的集合,也就是因為返回值不會包在特征簽名之中,因為Java語言里是無法僅僅依靠返回值的不同來對一個已有的方法進行重載的。但在Class文件格式中,特征簽名的范圍更大一些,只要描述符不是完全一致的兩個方法就可以共存。也就是說,如果兩個方法有相同的名稱和特征簽名,但返回值不同,那么也是可以合法共存于同一個class文件中。
屬性表集合
? ???????在Class文件,字段表,方法表中都可以攜帶自己的屬性表集合,以用于描述某些場景專有的信息。與Class文件中其它的數(shù)據(jù)項目要求的順序、長度和內容不同,屬性表集合的限制稍微寬松一些,不再要求各個屬性表具有嚴格的順序,并且只要不與已有的屬性名重復,任何人實現(xiàn)的編譯器都可以向屬性表中寫入自己定義的屬性信息,Java虛擬機運行時會忽略掉它不認識的屬性。為了能正確地解析Class文件,《Java虛擬機規(guī)范(第二版)》中預定義了9薦虛擬機實現(xiàn)應當能識別的屬性,具體如下表所示:
對于每個屬性,它的名稱需要從常量池中引用一個CONSTANT_Utf8_info類型的常量表來表示,而屬性值的結構則是完全自定義的,只要說明屬性值所占用的位數(shù)長度即可。一個符合規(guī)則的屬性表應該滿足如下表定義的結構:
1.Code屬性
Java程序方法體里的代碼經過Javac編譯器處理之后,最終變?yōu)樽止?jié)碼指令存儲在Code屬性內。Code屬性出現(xiàn)在方法表的屬性集合中,但并非所有方法都必須存在這個屬性表,譬如接口或抽象類中的抽象方法就不存在Code屬性,如果方法有Code屬性表存在,那么它的結構如下表:
attribute_name_index是一項指向CONSTANT_Utf8_info常量表的索引,常量值固定為“Code”,它代表了該屬性的屬性名稱,attribute_length指示了屬性值的長度,由于屬性名稱索引與屬性長度一共是6個字節(jié),所以屬性值的長度固定為整個屬性表的長度減去6個字節(jié)。
max_stack代表了操作數(shù)棧(Operand Stacks)的最大深度。在方法執(zhí)行的任意時刻,操作數(shù)棧都不會超過這個深度。虛擬機運行的時候需要根據(jù)這個值來分配棧幀(Frame)中的操作數(shù)棧深度。
max_locals代表了局部變量表所需的存儲空間。在這里,max_locals的單位是Slot,Slot是虛擬機為局部變量表分配內存所使用的最小單位。對于byte,char,float,int,shot,boolean,reference和returnAddress等長度不超過32位的數(shù)據(jù)類型,每個局部變量占1個Slot,而double與long這兩種64位的數(shù)據(jù)類型而需要2個Slot來存放。方法參數(shù)(包括實例方法中的隱藏參數(shù)“this”),顯示異常處理器的參數(shù)(Exception Handler Parameter,即try-catch語句中catch塊所定義的異常),方法體中定義的局部變量都需要使用局部表來存放。另外,并不是在方法中使用了多個局部變量,就把這些局部變量所占的Slot之和作為max_locals的值,原因是局部變量表中的Slot可以重用,當代碼執(zhí)行超出一個局部變量的作用域時,這個局部變量所在的Slot就可以被其他局部變量所使用,編譯器會根據(jù)變量的作用域來分類Slot并分配給各個變量使用,然后計算出max_locals的大小。
code_length和code用來存儲Java源程序編譯后生成的字節(jié)碼指令。code_length代表字節(jié)碼長度,code是用于存儲字節(jié)碼指令的一系列字節(jié)流。既然名為字節(jié)碼指令,那么每個指令就是一個u1類型的單字節(jié),當虛擬機讀取到code中的一個字節(jié)碼時,就可相應地找出這個字節(jié)碼代表的是什么指令,并且可以知道這條指令后面是否需要跟隨參數(shù),以及參數(shù)應該如何理解。
關于code_length還有一件值得注意的事情,雖然它是一個u4類型的長度值,理論上最大值可以達到2的32次方減1,但虛擬機規(guī)范中限制了一個方法不允許超過65535條字節(jié)碼指令,如果超過這個限制,Javac編譯器就會拒絕編譯。一般來講,只要我們寫Java代碼時不是刻意地編寫超長的方法,就不會超過這個最大值限制。但是,在編譯復雜的JSP文件中,可以會因為這個原因導致編譯失敗。
Code屬性是Class文件中最重要的一個屬性,如果表一個Java程序中的信息分為代碼(Code,方法體里的Java代碼)和元數(shù)據(jù)(Metadata,包括類、字段、方法定義及其它信息)兩部分,那么在整個Class文件里,Code屬性用于描述代碼,其它的所有數(shù)據(jù)項目就都用于描述元數(shù)據(jù)。
在字節(jié)碼指令之后的是這個方法的顯示異常處理表,異常表對于Code屬性表來說不是必須存在的。異常表的格式如下表:
異常表它包含4個字段,這些字段的含義為:如果字節(jié)碼從第start_pc到end_pc行之間(不包含第end_pc)行出現(xiàn)了類型為catch_type或其子類的異常(catch_type為指向一個CONSTANT_Class_info型常量的索引),則轉到第handler_pc行繼續(xù)處理。當catch_type的值為0時,代表任何的異常情況都需要轉向到handler_pc行行進行處理。異常表實際上是Java代碼的一部分,編譯器使用異常表而不是簡單的跳轉命令來實現(xiàn)Java異常及finally處理機制。注:字節(jié)碼的“行”是一種形象的描述,指的是字節(jié)碼相對于方法體開始的偏移量,而不是Java源代碼的行號。
2.Exceptions屬性
這里的Exceptions屬性是在方法表中與Code屬性平級的一項屬性,而不是Code屬性表中的異常屬性表。Exceptions屬性表的作是列舉出方法中可能拋出的受查檢(Checked Exception),也就是在方法描述時在throws關鍵字后面列舉的異常。它的結構如下表:
此屬性表中的number_of_exceptions項表示訪求可能拋出number_of_exceptions種受檢查異常,每一種受檢查異常使用一個exception_index_table項表示,為指向常量池中CONSTANT_Class_info型常量表的索引,代表了該受檢查異常的類型。
3.LineNumberTable屬性
LineNumberTable屬性用于描述Java源代碼行號與字節(jié)碼行號(字節(jié)碼偏移量)之間的對應關系。它并不是運行時必須的屬性,但默認會生成到Class文件之中,可以在Javac中使用-g:none或-g:lines選項來取消或要求生成這項信息。如果選擇不生成LineNumberTable屬性表,對程序運行產生的最主要的影響就是在拋出異常時,堆棧中將不會顯示出錯的行號,并且在調試程序的時候無法按照源碼來設置斷點。LineNumberTable屬性表結構如下表:
line_number_table是一個數(shù)量為line_number_table_length,類型為line_number_info的集合,line_number_info表包括了start_pc和line_number兩個u2類型的數(shù)據(jù)項,前者是字節(jié)碼行號,后者是Java源碼行號。
4.LocalVariableTable屬性
LocalVariableTable屬性表用于描述棧幀中局部變量表中的變量與Java源碼中定義的變量之間的關系,它不是運行時必須的屬性,默認也不會生成到Class文件之中,可以使用-g:none或-g:vars選項來取消或要求生成這項信息。如果沒有生成這項屬性,最大的影響就是當其它人引用這個方法時,所有參數(shù)名稱都丟失,IDE可能會使用諸如arg0、arg1之類的占位符來替換原有的參數(shù)名稱,這對程序運行沒有影響,但是會給代碼編寫帶來較大的不便,而且在調試期間無法根據(jù)參數(shù)名稱從運行上下文件中獲取參數(shù)值。LocalVariableTable屬性表結構如下:
其中l(wèi)ocal_variable_info項目代表了一個棧幀與源碼中的局部變量的關聯(lián),結構如下:
index是這個局部變量在棧幀局部變量表中的Slot位置。當這個變量的數(shù)據(jù)類型是64位時(double和long),它占用的Slot為index和index+1兩個位置。
在JDK1.5引入了泛型之后,LocalVariableTable屬性增加了一個“姐妹”屬性:LocalVaiableTypeTable,這個新增加的屬性結構與LocalVariableTable屬性非常相似,僅僅是把記錄字段描述符的descript_index替換成了字段的特征簽名(Singnature),對于非泛型類型來說,描述符的參數(shù)化類型被擦除掉了,描述符就不能準確地描述泛型類型了,因此出現(xiàn)了LocalVariableTypeTable屬性。
5.SourceFile屬性
SourceFile屬性用于記錄這生成這個Class文件的源碼文件名稱。這個屬性也是可選的,可以使用-g:none或-g:source選項來取消或要求生成這項信息。在Java中,對于大多數(shù)的類來說,類名和文件是一致的,但有一些特殊情況(如內部類)例外。如果不生成這項屬性,當招聘異常時,堆棧中半不會顯示出錯誤代碼所屬性文件名。這個屬性是一個室長的屬性,結構如下:
sourcefile_index數(shù)據(jù)項是指向常量池中CONSTANT_Utf8_info型常量的索引,常量值是源文件的文件名。
6.ConstantValue屬性
ConstantValue屬性的作用是通知虛擬機自動為靜態(tài)變量賦值。只有被static關鍵字修飾的變量才可以使用這項屬性。在Java程序里類類似“int x = 123“和”static int x = 123”這樣的變量定義非常常見,但虛擬機對這兩種變量賦值的方法和時刻有所不同。對于非static類型的變量(也就是實例變量)的賦值是在實例構造器方法中進行的;對于類變量,則有兩種式可以選擇:賦值在類構造器方法中進行,或者使用ConstantValue屬性來賦值。目前Sun Javac編譯器的選擇是:如果同時使用final和static來修改一個變量,并且這個變量的數(shù)據(jù)類型是基本類型或java.lang.String的話,就生成ConstantValue屬性來進行初始化,如果這個變量沒有被final修飾,或者并非基本類型或字符串,則選擇在類構造器中進行初始化。ConstantValue屬性表結構如下:
ConstantValue屬性是一個定長屬性,它的attribute_length數(shù)據(jù)項值必須為2。constantvalue_index數(shù)據(jù)項代表了常量池中一個字面常量的引用,根據(jù)字段類型不同,字面量可以是CONSTANT_Long_info,CONSTANT_Float_info,CONSTANT_Double_info,CONSTANT_Integer_info和CONSTANT_String_info常量中的一種。
7.InnerClasses屬性
InnerClasses屬性表用于記錄內部類與宿主類之間的關聯(lián)。如果一個類中定義了內部類,那么編譯器將會為它及它所包含的內部類生成InnerClasses屬性表。表結構如下:
數(shù)據(jù)項number_of_classes代表需要記錄多少個內部類信息,每一個內部類的類的信息都由一個inner_class_info表進行描述。inner_class_info表結構如下:
inner_class_info_index和outer_class_info_index都是指向常量池中CONSTANT_Class_infon常量的索引,分別代表了內部類和宿主類的符號引用。inner_name_index是指向常量池中CONSTANT_Utf8_info型常量的索引,代表這個內部類的名稱,如果是匿名內部類,則這項值為0。inner_class_access_flags是內部類的訪問標志,類型于類的access_flags,它的取值范圍如下表:
8.Deprecated及Synthetic屬性
Deprecated及Synthetic屬性都屬性于標志類型的布爾值屬性,只存在有和沒有的區(qū)別,沒有屬性值的概念。
Deprecated屬性用于表示某個類,字段或方法,已經被程序作者定為不再推薦使用,它可以通過代碼中使用@Deprecated注解進行設置。
Synthetic屬代表此字段或方法并不是由Java源碼直接產生的,而是由編譯器自行添加的,在JDK1.5之后,標識一個類,字段或方法是編譯器自動產生的,也可以設置它們訪問標志中的ACC_SYNTHETIC標志位,其中最典型的就是Bridge Method。所有非用戶代碼生產的類,方法及字段都應當至少設置Synthetic屬性和ACC_SYNTHETIC標志位中的一項,唯一的例外是實例構造器“”方法和類構造器“
Deprecated及Synthetic屬性表結構如下:
其中attribute_length數(shù)據(jù)項的值必須為0,因為沒有任何屬性值需要設置。
?? 在JDK1.5和JDK1.6中一共增加了10項屬性,具體如下: