Java class文件格式之屬性_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
class文件中的attributes_count和attributes
attributes_count位于class文件中methods的下面。 它占兩個(gè)字節(jié), 存儲(chǔ)的是一個(gè)整數(shù)值, 表示class文件中屬性的個(gè)數(shù)。
attributes_count下面的是attributes, 可以把它看做一個(gè)數(shù)組, 每個(gè)數(shù)組項(xiàng)是一個(gè)attribute_info , 每個(gè)attribute_info 表示一個(gè)屬性。attributes中有 attributes_count個(gè)attribute_info 。
需要說明的是, 屬性會(huì)出現(xiàn)在多個(gè)地方, 不僅僅出現(xiàn)在頂層的ClassFile中, 也會(huì)出現(xiàn)在class文件中的數(shù)據(jù)項(xiàng)中, 如出現(xiàn)在field_info中, 用來描述特定字段的一些信息, 還可以出現(xiàn)在method_info中, 用來描述特定方法的一些信息。
屬性(attribute_info)的大概格式是這樣的:
其中attribute_name_index占兩個(gè)字節(jié), 它是一個(gè)指向常量池?cái)?shù)據(jù)項(xiàng)的索引。 它指向一個(gè)CONSTANT_Utf8_info , 這個(gè)CONSTANT_Utf8_info 中存放的是當(dāng)前屬性的名字。
attribute_name_index下面的四個(gè)字節(jié)叫做attribute_length, 它表示當(dāng)前屬性的長(zhǎng)度, 這個(gè)長(zhǎng)度不包括前6個(gè)字節(jié), 也就是說只包括屬性真實(shí)信息(也就是info)的長(zhǎng)度。
attribute_length下面的數(shù)據(jù)是info, 它的長(zhǎng)度由上面提到的attribute_length指定, 它存放的是真實(shí)的屬性數(shù)據(jù)。
下面我們會(huì)依次介紹一些重要屬性, 相對(duì)不是很重要的屬性會(huì)一筆帶過。
ClassFile中的SourceFile屬性
首先介紹一個(gè)比較簡(jiǎn)單的屬性:SourceFile。 該屬性出現(xiàn)在頂層的class文件中。 它描述了該類是從哪個(gè)源文件中編譯來的, 注意, 描述的是源文件, 而不是類, 一個(gè)源文件中可以存在多個(gè)類。 它的格式如下:
前面說過, attribute_name_index指向常量池中的一個(gè)CONSTANT_Utf8_info , 這個(gè)CONSTANT_Utf8_info 中存放的是這個(gè)屬性的名字字符串, 即“SourceFile” 。
attribute_length是屬性信息的長(zhǎng)度, 這里是2, 因?yàn)檫@個(gè)屬性的info就兩個(gè)字節(jié)。
sourcefile_index占兩個(gè)字節(jié), 這也是為什么attribute_length是2的原因。 sourcefile_index指向常量池中的一個(gè)CONSTANT_Utf8_info , 這個(gè)CONSTANT_Utf8_info 中存放的是生成該類的源文件的文件名, 這里的文件名不包括路徑部分。
下面舉例說明, 示例代碼:
package combjpowernodetest; public class Person { int age; int getAge(){ return age; } }
反編譯后的相關(guān)信息:
public class combjpowernodetestPerson SourceFile: "Personjava" Constant pool: ......... #20 = Utf8 SourceFile #21 = Utf8 Personjava .........
反編譯結(jié)果中的 SourceFile: "Person.java" 一行是SourceFile屬性的簡(jiǎn)單表示形式。 可以把它看做一個(gè)可讀的attribute_info 。 下面常量池中的第20項(xiàng)的CONSTANT_Utf8_info是對(duì)這個(gè)屬性的屬性名(attribute_name_index)的描述 , 第21項(xiàng)的CONSTANT_Utf8_info是對(duì)源文件的文件名的描述。
下面是圖例, 注意, 虛線范圍內(nèi)表示常量池區(qū)域:
ClassFile中的InnerClasses屬性
InnerClasses是一個(gè)存在于頂層class文件中的屬性, 它描述的是內(nèi)部類和外圍類的關(guān)系。 這是一個(gè)相對(duì)來說比較復(fù)雜的屬性, 因?yàn)槊總€(gè)類可能有多個(gè)內(nèi)部類, 而這些內(nèi)部類中可能還有內(nèi)部類, 多層嵌套。外圍類中的InnerClasses屬性必須描述它的所有內(nèi)部類, 而內(nèi)部類中的InnerClasses也必須描述它的外圍類。
由于這個(gè)屬性相對(duì)較為復(fù)雜, 而對(duì)于我們理解class文件又不具有很大的意義, 所以我們只是簡(jiǎn)單的介紹一下。 如果想深入理解這個(gè)屬性, 請(qǐng)參考 《深入Java虛擬機(jī)》 第144到166頁。
下面是這個(gè)屬性的結(jié)構(gòu):
attribute_name_index和attribute_length就不過多介紹了, 和上面介紹的是一樣的。
number_of_classes描述的是內(nèi)部類的個(gè)數(shù)。
classes可以看做是一個(gè)數(shù)組, 這個(gè)數(shù)組中的每一項(xiàng)是一個(gè)inner_class_info, 而每個(gè)inner_class_info是對(duì)一個(gè)內(nèi)部類的描述。每個(gè) inner_class_info的結(jié)構(gòu)如下:
Synthetic屬性
Synthetic屬性可以出現(xiàn)在filed_info中, method_info中和頂層的ClassFile中, 分別表示這個(gè)字段, 方法或類不是有用戶代碼生成的(即不存在與源文件中), 而是由編譯器自動(dòng)添加的。 例如, 編譯器會(huì)為內(nèi)部類增加一個(gè)字段, 該字段是對(duì)外部類對(duì)象的引用; 如果一個(gè)不定義構(gòu)造方法, 那么編譯器會(huì)自動(dòng)添加一個(gè)無參數(shù)的構(gòu)造方法<init>, 如果定義了靜態(tài)字段或靜態(tài)代碼塊, 還會(huì)根據(jù)具體情況, 增加靜態(tài)初始化方法<clinit> 。 此外, 有些機(jī)制, 如動(dòng)態(tài)代理, 會(huì)在運(yùn)行時(shí)自動(dòng)生成字節(jié)碼文件, 由于這些類不是由源文件中編譯來的, 所以這些類的class文件中會(huì)有一個(gè)Synthetic屬性。
它的結(jié)構(gòu)如下:
可以看到, 它沒有真正的屬性數(shù)據(jù)info, 它只是一個(gè)標(biāo)志性的屬性, 用來表示它所在的字段, 方法或類是由編譯器自動(dòng)添加的 。
下面以實(shí)例代碼來說明, 源碼如下:
package combjpowernodetest; public class Person { static{ Systemoutprintln("static"); } int age; int getAge(){ return age; } }
反編譯后的相關(guān)信息如下:
{ int age; flags: static {}; ......... public combjpowernodetestPerson(); ......... int getAge(); ......... }
由反編譯結(jié)果可以看出, 編譯器自動(dòng)生成了靜態(tài)初始化方法和構(gòu)造方法。 可能是因?yàn)镾ynthetic屬性是可選的(也就是說某個(gè)版本的編譯器可以選擇不加入Synthetic屬性) ,所以在反編譯后的結(jié)果中沒有發(fā)現(xiàn)Synthetic屬性。
ConstantValue屬性
ConstantValue屬性出現(xiàn)在class文件中的field_info中, 也就是說它是一個(gè)和字段相關(guān)的屬性。 每個(gè)field_info中最多只能出現(xiàn)一個(gè)ConstantValue屬性。 此外, 要注意的是, 必須是靜態(tài)字段才可以有ConstantValue屬性。 這個(gè)靜態(tài)字段可以是final的, 也可以不是final的。
這個(gè)屬性為靜態(tài)變量提供了另一種初始化的方式。 靜態(tài)變量初始化的方式有兩種, 一種就是現(xiàn)在要講得ConstantValue屬性, 另一種就是靜態(tài)初始化方法<clinit> 不同的編譯器和虛擬機(jī)可以有不同的實(shí)現(xiàn)方式。 但是如果虛擬機(jī)決定使用ConstantValue屬性為靜態(tài)變量賦值, 那么為這個(gè)變量的賦值動(dòng)作, 必須位于執(zhí)行<clinit>方法之前。
此外, 只有基本數(shù)據(jù)類型或String類型的靜態(tài)變量才可以存在ConstantValue屬性, 原因在下面會(huì)有說明。
下面介紹它的結(jié)構(gòu):
attribute_name_index和attribute_length就不過多介紹了, 和上面介紹的是一樣的。這里的attribute_length為2 。
位于attribute_length之下的是constantvalue_index , 這是一個(gè)指向常量池中某個(gè)數(shù)據(jù)項(xiàng)的索引。這個(gè)常量池?cái)?shù)據(jù)項(xiàng)中存放的就是當(dāng)前字段的值。
這個(gè)常量池中的數(shù)據(jù)項(xiàng),根據(jù)field_info描述的字段的不同, 可以是不同類型的數(shù)據(jù)項(xiàng), 如果當(dāng)前字段是byte, short, char, int, boolean類型, 那么這個(gè)被指向的常量池?cái)?shù)據(jù)項(xiàng)就會(huì)是一個(gè)CONSTANT_Integer_info , 如果當(dāng)前字段是一個(gè)long類型的字段, 那么這個(gè)被指向的常量池?cái)?shù)據(jù)項(xiàng)就會(huì)是一個(gè)CONSTANT_Long_info 。 如果當(dāng)前字段是是一個(gè)String類型的字段 , 那么這個(gè)被指向的常量池?cái)?shù)據(jù)項(xiàng)就是一個(gè)CONSTANT_String_info 。 這里有一點(diǎn)需要說明, 雖然java語言支持byte, short, char, boolean類型, 但是JVM卻不支持這幾種類型, 表現(xiàn)在class文件中就是, class文件中的常量池中沒有和這幾個(gè)數(shù)據(jù)類型相對(duì)應(yīng)的數(shù)據(jù)項(xiàng), 這幾中類型都被JVM在執(zhí)行時(shí)當(dāng)做int來對(duì)待, 表現(xiàn)在class文件中就是, 這幾種類型都對(duì)應(yīng)常量池中的CONSTANT_Integer_info 數(shù)據(jù)項(xiàng)。
這也說明了, 為什么只有基本數(shù)據(jù)類型和String類型的靜態(tài)常量才會(huì)存在ConstantValue屬性 。 因?yàn)閏onstantvalue_index只是一個(gè)指向常量池的索引, 而其他引用類型的常量不會(huì)存在于常量池中。
下面以實(shí)例來說明, 實(shí)例代碼如下:
package combjpowernodetest; public class Person { static final int a = 1; int age; int getAge(){ return age; } }
反編譯后的相關(guān)結(jié)果如下:
...... Constant pool: #7 = Utf8 ConstantValue #8 = Integer 1 { static final int a; flags: ACC_STATIC, ACC_FINAL ConstantValue: int 1 ......... }
可以看到, 源文件中的a字段, 是static final 的, 所以編譯器為這個(gè)字段的filed_info生成了ConstantValue屬性。 這個(gè)屬性的示意圖如下所示, 注意, 虛線范圍內(nèi)表示常量池區(qū)域:
Deprecated屬性
Deprecated屬性可以存在于filed_info中, method_info中和頂層的ClassFile中, 分別表示這個(gè)字段, 方法或類已經(jīng)過時(shí)。 這個(gè)屬性用來支持源文件中的@deprecated注解。 也就是說, 如果在源文件中為一個(gè)字段, 方法或類標(biāo)注了@deprecated注解, 那么編譯器就會(huì)在class文件中為這個(gè)字段, 方法或類生成一個(gè)Deprecated屬性 。
Deprecated屬性的格式如下:
和上面的屬性一樣, attribute_name_index屬性指向一個(gè)常量池中的CONSTANT_Utf8_info 。 這個(gè)CONSTANT_Utf8_info中存放著該屬性的名字 “Deprecated” 。
attribute_length永遠(yuǎn)為0 , 因?yàn)檫@個(gè)屬性只是一個(gè)標(biāo)志信息, 用來表示字段, 方法, 類已經(jīng)過時(shí), 而不具有任何實(shí)質(zhì)性的屬性信息。
下面以代碼示例來說明, 代碼如下:
package combjpowernodetest; public class Person { int age; @Deprecated int getAge(){ return age; } }
在getAge方法上使用了@deprecated 。 下面是反編譯之后的相關(guān)信息:
...... nstant pool: ...... #18 = Utf8 Deprecated ...... ...... int getAge(); flags: Deprecated: true ......
可以看到, 在getAge方法相關(guān)的信息中, 有一行 Deprecated: true , 這說明編譯器在getAge方法的method_info中加入了Deprecated屬性。 常量池第18項(xiàng)的CONSTANT_Utf8_info中存放的是Deprecated屬性的屬性名“Deprecated” 。
下面是示意圖, 虛線范圍內(nèi)表示常量池區(qū)域:
總結(jié)
本文就到此為止。 在本文中, 主要講解了class文件中的一些屬性。 這些屬性可以出現(xiàn)在class文件中的對(duì)個(gè)地方, 用來描述一些其他信息。
相關(guān)文章
mybatis-plus 處理大數(shù)據(jù)插入太慢的解決
這篇文章主要介紹了mybatis-plus 處理大數(shù)據(jù)插入太慢的解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Spring-boot結(jié)合Shrio實(shí)現(xiàn)JWT的方法
這篇文章主要介紹了Spring-boot結(jié)合Shrio實(shí)現(xiàn)JWT的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05詳解Http請(qǐng)求中Content-Type講解以及在Spring MVC中的應(yīng)用
這篇文章主要介紹了Http請(qǐng)求中Content-Type講解以及在Spring MVC中的應(yīng)用的相關(guān)資料,需要的朋友可以參考下2017-02-02基于struts2和hibernate實(shí)現(xiàn)登錄和注冊(cè)功能
這篇文章主要為大家詳細(xì)介紹了基于struts2和hibernate實(shí)現(xiàn)登錄和注冊(cè)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10springboot mybatis手動(dòng)事務(wù)的實(shí)現(xiàn)
本文主要介紹了springboot mybatis手動(dòng)事務(wù)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12在Ubuntu系統(tǒng)下安裝JDK和Tomcat的教程
這篇文章主要介紹了在Ubuntu系統(tǒng)下安裝JDK和Tomcat的教程,這樣便是在Linux系統(tǒng)下搭建完整的Java和JSP開發(fā)環(huán)境,需要的朋友可以參考下2015-08-08