JVM系列文章(三):Class文件內容解析


作為一個程序猿,只知道怎么用是遠遠不夠的。起碼,你須要知道為什么能夠這么用。即我們所謂底層的東西。

那究竟什么是底層呢?我認為這不能一概而論。以我如今的知識水平而言:對於Web開發人員,TCP/IP、HTTP等等協議可能就是底層;對於C、C++程序猿。內存、指針等等可能就是底層的東西。那對於Java開發人員。你的Java代碼執行所在的JVM可能就是你所須要去了解、理解的東西。

我會在接下來的一段時間,和讀者您一起去學習JVM。全部內容均參考自《深入理解Java虛擬機:JVM高級特性與最佳實踐》(第二版),感謝作者。

本文是系列文章第三篇。講述的是 類文件結構文中的部分內容來自http://blog.csdn.net/a19881029/article/details/16117251(也是此書閱讀筆記),感謝先行者。

系列文章第一篇:JVM系列文章(一):Java內存區域分析

系列文章第二篇:JVM系列文章(二):垃圾回收機制


一、概述

不論什么一個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;
	}
}

編譯后的class文件例如以下:



1.魔數

每一個class文件的頭4個字節稱為魔數,它唯一的作用是確定這個文件是否為一個能被虛擬機接受的Class文件。

非常多文件存儲標准中都使用魔數來進行身份識別。譬如圖片格式gif、jpeg等。使用魔數而不是拓展名來進行識別主要是基於安全方面的考慮,由於文件拓展格式能夠任意修改。


Class文件的魔數為: 0xCAFEBABE(咖啡寶貝?)這個魔數似乎也預示着日后JAVA這個商標名稱的出現。




2.版本

第五六個字節是次版本(Minor Version)。第7和第8個字節是主版本(Major Version)。
高版本號的JDK能夠向下兼容曾經版本號的Class文件,可是無法執行以后版本號的Class文件,即使文件格式並未發生變化,虛擬機也必須拒絕執行超過其版本號號的Class文件。

3.常量池



常量池能夠理解為Class文件之中的資源倉庫,是Class文件結構中與其它項目關聯最多的數據類型,也是占用Class文件空間最大的數據項目之中的一個。同一時候也是在Class文件里第一個出現的表類型數據項目。
因為常量池中常量的數目是不固定的所以在常量池入口須要放置一個2字節長的無符號數constatn_pool_count來代表常量池容量計數值。這個容量計數從1而不是0開始。

constant_pool_count:占2字節。0x0016。轉化為十進制為22,即說明常量池中有21個常量(僅僅有常量池的計數是從1開始的,其他集合類型均從0開始),索引值為1~22。 第0項常量具有特殊意義。假設某些指向常量池索引值的數據在特定情況下須要表達“不引用不論什么一個常量池項目”的含義,這樣的情況能夠將索引值置為0來表示

常量池中主要存放兩大類常量:字面量和符號引用。字面量如文本字符串、聲明為final的常量值等。符號引用包含三類常量:類和接口的全限定名、字段的名稱和描寫敘述符、方法的名稱和描寫敘述符。

常量池中的每一項常量都是一個表,在JDK1.7之前共同擁有11種結構各不同樣的表數據結構。這些表數據結構在表開始的第一位是一個u1類型的標志位,代表當前這個常量屬於那種常量類型。例如以下表所看到的:

類型

簡單介紹

項目

類型

描寫敘述

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.字段表集合

字段表用於描寫敘述接口或者類中聲明的變量,包含類級變量和實例級變量(是否是static)。但不包含在方法內部聲明的局部變量。

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個常量

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. const #7 = Asciz        <init>;  
  2. 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個常量

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. const #16 = Asciz       getM;  
  2. 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工具在后半部分會列出分析完畢的方法(能夠看到和我們的分析結果是一樣的):

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. d:\>javap -verbose Test  
  2. ......  
  3. {  
  4. public com.test.Test();  
  5.   Code:  
  6.    Stack=1, Locals=1, Args_size=1  
  7.    0:   aload_0  
  8.    1:   invokespecial   #10; //Method java/lang/Object."<init>":()V  
  9.    4:   return  
  10.   LineNumberTable:  
  11.    line 3: 0  
  12.   
  13.   LocalVariableTable:  
  14.    Start  Length  Slot  Name   Signature  
  15.    0      5      0    this       Lcom/test/Test;  
  16.   
  17. public int getM();  
  18.   Code:  
  19.    Stack=2, Locals=1, Args_size=1  
  20.    0:   aload_0  
  21.    1:   getfield        #18; //Field m:I  
  22.    4:   iconst_1  
  23.    5:   iadd  
  24.    6:   ireturn  
  25.   LineNumberTable:  
  26.    line 7: 0  
  27.   
  28.   LocalVariableTable:  
  29.    Start  Length  Slot  Name   Signature  
  30.    0      7      0    this       Lcom/test/Test;  
  31. }  

 

8.屬性表集合

在Class文件、屬性表、方法表中都能夠包括自己的屬性表集合。用於描寫敘述某些場景的專有信息

與Class文件里其他數據項對長度、順序、格式的嚴格要求不同,屬性表集合不要求當中包括的屬性表具有嚴格的順序,而且僅僅要屬性的名稱不與已有的屬性名稱反復。不論什么人實現的編譯器可以向屬性表中寫入自定義的屬性信息。虛擬機在執行時會忽略不能識別的屬性,為了能正確解析Class文件,虛擬機規范中提前定義了虛擬機實現必須可以識別的9項屬性:

屬性名稱

使用位置

含義

Code

方法表

Java代碼編譯成的字節碼指令

ConstantValue

字段表

finalkeyword定義的常量值

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屬性:通知虛擬機自己主動為靜態變量賦值,僅僅有被statickeyword修飾的變量(類變量)才干夠使用這項屬性。

其結構例如以下:

類型

名稱

數量

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標志,對finalkeyword的要求是Javac編譯器自己增加的要求),而且該變量的數據類型為基本類型或字符串類型。就生成ConstantValue屬性進行初始化;否則在類構造器<clinit>方法中進行初始化

Exceptions屬性:列舉出方法中可能拋出的受查異常(即方法描寫敘述時throwskeyword后列出的異常),與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



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM