程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java class文件格式之屬性_動力節點Java學院整理

Java class文件格式之屬性_動力節點Java學院整理

編輯:關於JAVA

class文件中的attributes_count和attributes

attributes_count位於class文件中methods的下面。 它占兩個字節, 存儲的是一個整數值, 表示class文件中屬性的個數。 
attributes_count下面的是attributes, 可以把它看做一個數組, 每個數組項是一個attribute_info , 每個attribute_info 表示一個屬性。attributes中有 attributes_count個attribute_info 。

需要說明的是, 屬性會出現在多個地方, 不僅僅出現在頂層的ClassFile中, 也會出現在class文件中的數據項中, 如出現在field_info中, 用來描述特定字段的一些信息, 還可以出現在method_info中, 用來描述特定方法的一些信息。

屬性(attribute_info)的大概格式是這樣的:

其中attribute_name_index占兩個字節, 它是一個指向常量池數據項的索引。 它指向一個CONSTANT_Utf8_info , 這個CONSTANT_Utf8_info 中存放的是當前屬性的名字。

attribute_name_index下面的四個字節叫做attribute_length, 它表示當前屬性的長度, 這個長度不包括前6個字節, 也就是說只包括屬性真實信息(也就是info)的長度。

attribute_length下面的數據是info, 它的長度由上面提到的attribute_length指定, 它存放的是真實的屬性數據。

下面我們會依次介紹一些重要屬性, 相對不是很重要的屬性會一筆帶過。

ClassFile中的SourceFile屬性

首先介紹一個比較簡單的屬性:SourceFile。 該屬性出現在頂層的class文件中。 它描述了該類是從哪個源文件中編譯來的, 注意, 描述的是源文件, 而不是類, 一個源文件中可以存在多個類。 它的格式如下:

前面說過, attribute_name_index指向常量池中的一個CONSTANT_Utf8_info , 這個CONSTANT_Utf8_info 中存放的是這個屬性的名字字符串, 即“SourceFile” 。 

attribute_length是屬性信息的長度, 這裡是2, 因為這個屬性的info就兩個字節。

sourcefile_index占兩個字節, 這也是為什麼attribute_length是2的原因。 sourcefile_index指向常量池中的一個CONSTANT_Utf8_info , 這個CONSTANT_Utf8_info 中存放的是生成該類的源文件的文件名, 這裡的文件名不包括路徑部分。

下面舉例說明, 示例代碼:

package combjpowernodetest; 
 
public class Person { 
 
  int age; 
 
  int getAge(){ 
    return age; 
  } 
} 

反編譯後的相關信息:

public class combjpowernodetestPerson 
 
 SourceFile: "Personjava" 
 
Constant pool: 
 
......... 
 
 #20 = Utf8        SourceFile 
 #21 = Utf8        Personjava 
 
......... 

反編譯結果中的  SourceFile: "Person.java"  一行是SourceFile屬性的簡單表示形式。 可以把它看做一個可讀的attribute_info 。 下面常量池中的第20項的CONSTANT_Utf8_info是對這個屬性的屬性名(attribute_name_index)的描述 , 第21項的CONSTANT_Utf8_info是對源文件的文件名的描述。

下面是圖例, 注意, 虛線范圍內表示常量池區域:

ClassFile中的InnerClasses屬性

InnerClasses是一個存在於頂層class文件中的屬性, 它描述的是內部類和外圍類的關系。  這是一個相對來說比較復雜的屬性, 因為每個類可能有多個內部類, 而這些內部類中可能還有內部類, 多層嵌套。外圍類中的InnerClasses屬性必須描述它的所有內部類, 而內部類中的InnerClasses也必須描述它的外圍類。 

由於這個屬性相對較為復雜, 而對於我們理解class文件又不具有很大的意義, 所以我們只是簡單的介紹一下。 如果想深入理解這個屬性, 請參考 《深入Java虛擬機》 第144到166頁。 

下面是這個屬性的結構:

attribute_name_index和attribute_length就不過多介紹了, 和上面介紹的是一樣的。

number_of_classes描述的是內部類的個數。

classes可以看做是一個數組, 這個數組中的每一項是一個inner_class_info, 而每個inner_class_info是對一個內部類的描述。每個 inner_class_info的結構如下:

Synthetic屬性

Synthetic屬性可以出現在filed_info中, method_info中和頂層的ClassFile中, 分別表示這個字段, 方法或類不是有用戶代碼生成的(即不存在與源文件中), 而是由編譯器自動添加的。 例如, 編譯器會為內部類增加一個字段, 該字段是對外部類對象的引用; 如果一個不定義構造方法, 那麼編譯器會自動添加一個無參數的構造方法<init>, 如果定義了靜態字段或靜態代碼塊, 還會根據具體情況, 增加靜態初始化方法<clinit> 。 此外, 有些機制, 如動態代理, 會在運行時自動生成字節碼文件, 由於這些類不是由源文件中編譯來的, 所以這些類的class文件中會有一個Synthetic屬性。 

它的結構如下:

可以看到, 它沒有真正的屬性數據info, 它只是一個標志性的屬性, 用來表示它所在的字段, 方法或類是由編譯器自動添加的 。 

下面以實例代碼來說明, 源碼如下:

package combjpowernodetest; 
 
public class Person { 
   
  static{ 
    Systemoutprintln("static"); 
     
  } 
   
  int age; 
 
  int getAge(){ 
    return age; 
  } 
} 

反編譯後的相關信息如下:

{ 
 int age; 
  flags: 
 
 
 static {}; 
 
  ......... 
 
 public combjpowernodetestPerson(); 
   
  ......... 
 
 int getAge(); 
 
  ......... 
} 

由反編譯結果可以看出, 編譯器自動生成了靜態初始化方法和構造方法。 可能是因為Synthetic屬性是可選的(也就是說某個版本的編譯器可以選擇不加入Synthetic屬性) ,所以在反編譯後的結果中沒有發現Synthetic屬性。 

ConstantValue屬性

ConstantValue屬性出現在class文件中的field_info中, 也就是說它是一個和字段相關的屬性。 每個field_info中最多只能出現一個ConstantValue屬性。 此外, 要注意的是, 必須是靜態字段才可以有ConstantValue屬性。 這個靜態字段可以是final的, 也可以不是final的。 

這個屬性為靜態變量提供了另一種初始化的方式。 靜態變量初始化的方式有兩種, 一種就是現在要講得ConstantValue屬性, 另一種就是靜態初始化方法<clinit> 不同的編譯器和虛擬機可以有不同的實現方式。 但是如果虛擬機決定使用ConstantValue屬性為靜態變量賦值, 那麼為這個變量的賦值動作, 必須位於執行<clinit>方法之前。 

此外, 只有基本數據類型或String類型的靜態變量才可以存在ConstantValue屬性, 原因在下面會有說明。 

下面介紹它的結構:

attribute_name_index和attribute_length就不過多介紹了, 和上面介紹的是一樣的。這裡的attribute_length為2 。 

位於attribute_length之下的是constantvalue_index , 這是一個指向常量池中某個數據項的索引。這個常量池數據項中存放的就是當前字段的值。

  這個常量池中的數據項,根據field_info描述的字段的不同, 可以是不同類型的數據項, 如果當前字段是byte, short, char, int, boolean類型, 那麼這個被指向的常量池數據項就會是一個CONSTANT_Integer_info , 如果當前字段是一個long類型的字段, 那麼這個被指向的常量池數據項就會是一個CONSTANT_Long_info 。 如果當前字段是是一個String類型的字段 , 那麼這個被指向的常量池數據項就是一個CONSTANT_String_info 。 這裡有一點需要說明, 雖然java語言支持byte, short, char, boolean類型, 但是JVM卻不支持這幾種類型, 表現在class文件中就是, class文件中的常量池中沒有和這幾個數據類型相對應的數據項, 這幾中類型都被JVM在執行時當做int來對待, 表現在class文件中就是, 這幾種類型都對應常量池中的CONSTANT_Integer_info 數據項。 

這也說明了, 為什麼只有基本數據類型和String類型的靜態常量才會存在ConstantValue屬性 。 因為constantvalue_index只是一個指向常量池的索引, 而其他引用類型的常量不會存在於常量池中。

下面以實例來說明, 實例代碼如下:

package combjpowernodetest; 
 
public class Person { 
   
  static final int a = 1; 
   
  int age; 
 
  int getAge(){ 
    return age; 
  } 
} 

反編譯後的相關結果如下:

...... 
 
Constant pool: 
  
  #7 = Utf8        ConstantValue 
  #8 = Integer      1 
 
  
{ 
 static final int a; 
  flags: ACC_STATIC, ACC_FINAL 
  ConstantValue: int 1 
 
  ......... 
} 

可以看到, 源文件中的a字段, 是static final 的, 所以編譯器為這個字段的filed_info生成了ConstantValue屬性。 這個屬性的示意圖如下所示, 注意, 虛線范圍內表示常量池區域:

Deprecated屬性

Deprecated屬性可以存在於filed_info中, method_info中和頂層的ClassFile中, 分別表示這個字段, 方法或類已經過時。 這個屬性用來支持源文件中的@deprecated注解。 也就是說, 如果在源文件中為一個字段, 方法或類標注了@deprecated注解, 那麼編譯器就會在class文件中為這個字段, 方法或類生成一個Deprecated屬性 。

Deprecated屬性的格式如下:

和上面的屬性一樣, attribute_name_index屬性指向一個常量池中的CONSTANT_Utf8_info 。 這個CONSTANT_Utf8_info中存放著該屬性的名字 “Deprecated” 。 

attribute_length永遠為0 , 因為這個屬性只是一個標志信息, 用來表示字段, 方法, 類已經過時, 而不具有任何實質性的屬性信息。

下面以代碼示例來說明, 代碼如下:

package combjpowernodetest; 
 
public class Person { 
   
  int age; 
 
  @Deprecated 
  int getAge(){ 
    return age; 
  } 
} 

在getAge方法上使用了@deprecated 。 下面是反編譯之後的相關信息:

...... 
 
nstant pool: 
...... 
 
#18 = Utf8        Deprecated 
 
...... 
 
 
 
...... 
 
int getAge(); 
 flags: 
 Deprecated: true 
 
 ...... 

可以看到, 在getAge方法相關的信息中, 有一行 Deprecated: true , 這說明編譯器在getAge方法的method_info中加入了Deprecated屬性。 常量池第18項的CONSTANT_Utf8_info中存放的是Deprecated屬性的屬性名“Deprecated” 。 

下面是示意圖, 虛線范圍內表示常量池區域:

總結

本文就到此為止。 在本文中, 主要講解了class文件中的一些屬性。 這些屬性可以出現在class文件中的對個地方, 用來描述一些其他信息。 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved