前言概述
本文旨在講解class文件的整體結構信息,閱讀本文后應該可以完整的了解class文件的格式以及各個部分的邏輯組成含義
class文件包含了java虛擬機指令集 和 符號表 以及若干其他輔助信息.
class文件是一組以8位字節為基礎單位的二進制字節流
各個數據項按照順序緊湊的排列在Class文件中,中間沒有任何分隔符號
class文件采用類似 c結構體的格式存儲數據
數據類型只有兩種
無符號數 和 類c結構體的 表 表是由無符號數或者其他的表構成的
整個class文件就是一張表
無論無符號數還是表,當需要描述同一類型但數量不定的多個數據時,經常會使用一個前置的容量計數器用於指示接下來的數據個數,然后是若干個連續的數據項
class文件主要內容為: 類本身的信息 字段 方法 常量池 以及方法中的Code屬性 再就是一些相關的輔助信息
類本身的信息類本身有一些必備的描述信息,比如類名 訪問修飾符 繼承關系等
字段用於描述接口或者類中聲明的變量
字段包括類變量以及實例變量,不包括局部變量 他有訪問標志 名稱 描述符信息
方法用於描述方法表信息 類似字段 也有訪問標志 名稱 描述符信息
常量池可以理解為Class文件的資源倉庫,所以他是與其他項目關聯最多的數據類型
主要是兩大類: 字面量 以及符號引用
字面量接近java語言層面的常量概念 比如文本字符串 聲明為final常量的值
符號引用包括:
類和接口的全限定名
字段的名稱和描述符
方法的名稱和描述符
虛擬機加載class文件的時候動態鏈接,所以class文件中不會保存方法的最終內存布局, 還需要轉換
虛擬機運行時從常量池中獲得對應的符號引用,然后在創建或者運行時解析翻譯到具體的內存地址中
Code屬性存放的Java方法的內容,位於方法method_info 內
存放的是java方法經過編譯器編譯成的字節碼指令 說白了存放的就是代碼的編譯后形式
概述:
class文件作為JVM的"機器語言" 主要包括兩部分的信息,基礎信息以及附加信息
基礎信息為源代碼中呈現的信息
類自身信息/字段/方法用於描述源代碼中的類/字段/方法
常量池中保存了資源信息,比如字段的名字 方法的描述符等
方法中的code屬性保存了方法中的代碼的執行邏輯
額外信息為虛擬機執行過程中或者字節碼指令執行所需要的信息
為了保證虛擬機能夠正確的加載class文件
另外虛擬機加載類還需要做一些額外的工作比如校驗信息等
字節碼指令的執行可能還需要一些額外的信息
這些額外的信息通常也是保存在常量池中或者以屬性的形式出現
|
所以想要理解class文件的內容,就需要從這兩個方面出發
基礎信息以及額外附加信息
基礎信息是對於源代碼的映射
給出任意一段java代碼,源代碼中到底有哪些信息呢?
比如
類的全限定名 類的屬性信息 訪問權限等
字段的名稱 類型信息 訪問權限等
方法的名稱 方法簽名 返回值類型 訪問權限等
類或者方法或者字段 有沒有注解?
類的父類是什么?
類繼承了哪些接口? 等等等等
其實換句話說你所有使用依賴的功能,都需要有底層的數據來支撐
額外的附加信息主要涉及到字節碼指令以及虛擬機加載相關原理,額外的附加信息是附屬於基本信息的,他們滲透在基本信息里面
所以下面的說明中,我們以基礎信息為綱要,涉及到的附加信息會說明或者從功能上看出來是作為附加信息
class文件的數據格式了解
class文件包含了虛擬機所需要知道的,關於類或者接口的所有信息
結構體
他的基本數據類型為無符號數,以及表 表 是 數據組織結構類似於C語言中的結構體的的一種形式
為了更好地理解這種形式的邏輯,不了解C語言的,可以稍微了解一點結構體的形式,更有利於理解class文件的數據形式
struct 結構體名
{
類型名1 成員名1;
類型名2 成員名2;
.....
類型名n 成員名n;
};
|
比如
struct student { char name[10]; char sex; int age; float score; };
他的每個元素的數據類型都可以不相同
而且每個字段本身也可以是指向另外一個數據項的地址
也類似與數據庫中的關聯字段ID,這個ID在另一個表中有代表一整條的記錄
比如學生表有addressId字段,用於關聯地址信息
地址是一條完整的記錄,其中可能包括 國家地區 省市 鄉鎮等等字段值
class文件中的數據類型
每一個class文件都是由字節流組成
一個字節8位
所有的16位 32位 和 64位數據長度都可以通過構造成2個 4個或者8個字節來表示
多字節的數據將會大端排序(高位字節在地址最低位 )
對於字節的描述,java虛擬機規范中使用u1 u2 u4 u8來分別表示1,2,4和8 個字節的無符號數
基本數據類型為u1,u2,u4,u8
復雜的數據類型由類似結構體的形式來組織無符號數或者是類結構體的形式 可以稱之為 表 也就是說表 里面可以有表
比如常量池表中的數據結構為
cp_info{
u1 tag;
u1 info[ ]
}
所以說
class文件的形式是一張巨大的表,是一個二進制字節流
只有兩種數據表示形式 無符號數 以及 表(結構體 復合的數據結構)
各個數據項嚴格的按照順序存放,之間沒有填充或者對齊,這也是為何編譯后代碼如此緊湊的原因之一
基本數據類型為: u1 u2 u4 u8
|
class文件的數據組織格式解讀
ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count]; }
class文件是一張表
這張表里面 記錄了class文件本身的相關信息 比如magic 以及版本號
class文件中類索引 this_class 父類索引 super_class 以及接口集合 interfaces 三個數據項確定了類的繼承關系
同時又記錄了字段 方法 以及屬性信息
還是以之前的例子為例
源代碼
public class HelloWorld { private int x; private String y; public void fun() { } public static void main(String[] args) { System.out.println("hello world"); } }
使用WinHex打開.class 文件 的部分結果
我們對照着class文件的結構進行解析
注意:
上圖中一列是一個字節 8位 也就是相當於u1 4位表示一個十六進制數
所以一列表示兩個十六進制數
第四項 u2 constant_pool_count![]() 十六機制27 十進制39 可以看到javap解析后的Constant pool:中總共有從#1 到 #38 常量池計數器constant_pool_count的值等於常量表中的成員數加1 常量池標的索引值只有大於0 且小於constant_pool_count時才有效 所以此處解析也是對的 |
不在繼續一一比對,你可以使用javap 進行查看
這是官方提供的class文件的解析工具
總結:
class文件的存儲形式就是一個二進制字節流
使用類似結構體的形式
將源代碼映射的基礎信息以及運行時必要的輔助信息,而這些基礎信息也都已經被分割為最小的數據單位
進行合理緊湊的組織起來
class文件詳解
classFile文件格式
ClassFile { u4 magic;//唯一作用是確定這個文件是否為一個能被虛擬機所接受的class文件。魔數值固定為0xCAFEBABE,不會改變 u2 minor_version;//唯一作用是確定這個文件是否為一個能被虛擬機所接受的class文件。魔數值固定為0xCAFEBABE,不會改變 u2 major_version;//主版本號 u2 constant_pool_count;//常量池計數 值等於常量池表中的成員個數加1 cp_info constant_pool[constant_pool_count-1];//常量池 1~ constant_pool_count-1 為索引 u2 access_flags;//訪問標志以及類型信息 u2 this_class;//當前類索引 指向常量池中一個CONSTANT_Class_info u2 super_class;//父類索引 0 或者指向常量池中一個CONSTANT_Class_info u2 interfaces_count;//直接超接口數量 u2 interfaces[interfaces_count];//接口表 u2 fields_count;//字段個數 static類變量或者非sttic的實例變量 不包括繼承的 field_info fields[fields_count];//字段表 u2 methods_count;//方法個數 所有方法 但不包括繼承而來的 method_info methods[methods_count];//方法表 u2 attributes_count;//屬性個數 attribute_info attributes[attributes_count];/屬性表 }
從class文件的數據結構上來看,主要有下面幾部分信息內容
class文件本身的信息 magic minor_version minor_version
類本身的信息 access_flags this_class super_class interfaces_count interfaces[interfaces_count]
常量信息 constant_pool_count constant_pool[constant_pool_count-1]
字段 fields_count fields[fields_count]
方法 methods_count methods[methods_count]
屬性 attributes_count attributes[attributes_count]
各種名稱的內部表示形式
在進入更加詳細的說明之前,有一部分內容必須提前說一下
那就是一些內部名稱數據的表示形式
就好像編碼一樣,亦或者理解成書寫格式與規范,比如我們會把姓寫在名的前面
對於class文件中描述的一些信息,我們有固定的格式的信息描述符號
比如下面會提到的我們用 D表示是double類型
主要涉及兩部分內容 名稱 和描述符
名稱描述
類和接口的二進制名稱
class文件中的類和接口的二進制名稱 是通過全限定名稱來進行表示的
稱之為二進制名稱
注意,全限定名的分割形式不再是 英文句號 . 而是 / 比如 java/lang/System
非限定名
方法名 字段名 局部變量名以及形式參數的名都采用非限定名的形式
描述符
分為字段描述符/方法描述符
字段描述符
上面截圖自The Java Virtual Machine Specification, Java SE 8 Edition
他表示字段描述符使用FieldType來進行表述
FieldType有包括基本類型/對象類型/數組類型
形式是
FieldType
|
B | byte | [基本類型] 有符號的字節數組 |
C | char | [基本類型] 基本多語種平面中的Unicode代碼點 UTF-16 |
D | double | [基本類型] 雙精度浮點數 |
F | float | [基本類型] 單精度浮點數 |
I | int | [基本類型] 整型數 |
J | long | [基本類型] 長整數 |
S | short | [基本類型] 有符號短整數 |
Z | boolean | [基本類型] 布爾值true/false |
L ClassName; | L ClassName; | [對象類型] ClassName類的實例 |
[ | reference | [數組類型] 一維數組 |
比如int 為I Object 為 L java/lang/Object; double[][][] d 為 [[[D
方法描述符
上面截圖自The Java Virtual Machine Specification, Java SE 8 Edition
他表示一個方法描述符由一個參數描述符ParameterDescriptor 和一個返回類型描述符ReturnDescriptor組成
參數類型描述符是: FieldType 類型
返回類型描述符是: FieldType類型或者Void類型VoidDescriptor
Void類型描述符 使用的是V來進行表示
形式是
( {ParameterDescriptor} ) ReturnDescriptor
注意: {} 不是一部分,是想表達和數組似的,也可能是多個
|
比如
Object m(int i, double d, Thread t) {...}
他的描述符為:
(IDLjava/lang/Thread;)Ljava/lang/Object;
class文件詳解之類本身信息
類本身的信息
access_flags
this_class super_class interfaces_count interfaces[interfaces_count]
名稱 | 值 | |
ACC_PUBLIC | 0x0001 | 聲明為public 包外訪問 |
ACC_FINAL | 0x0010 | final 不允許子類 |
ACC_SUPER | 0x0020 | 調用invokespecial 需要處理父類 |
ACC_INTERFACE | 0x0200 | 這是一個接口 |
ACC_ABSTRACT | 0x0400 | abstract 抽象的不能被實例化 |
ACC_SYNTHETIC | 0x1000 | class文件並非由java源代碼生成 |
ACC_ANNOTATION | 0x2000 | 注解 |
ACC_ENUM | 0x4000 | 枚舉 |
access_flag 字段為類的訪問權限標志以及類型值
this_class super_class interfaces[interfaces_count] 構成了類的繼承關系 指向的都是常量池中的CONSTANT_Class_info
對於super_class來說,可能為0 因為Object沒有父類,其他所有都需要有對應的值存在
class文件詳解之常量池
主要分為兩類 字面量 符號引用
字面量類似java語言層面的含義 文本字符串 聲明為final 的常量值
符號引用包括:
類和接口的全限定名
字段的名稱和描述符
方法的名稱和描述符
常量池包含了class文件結構及其子結構中引用的所有的,字符串常量,類或者接口名,字段名,以及其他常量
|
class文件由無符號數和表結構兩種類型組成
關於名稱的書寫規范格式上面已經進行了描述
但是怎么表述這些名稱的"字符串" 形式呢? 比如className = "Helloworld" 怎么保存Helloworld?
另外一些基本的數據類型的數據在class文件中又將是如何存放呢?比如 int類型的x=5 這個5又怎么保存?
字符串以及不同數值類型也都不就是一個u1 所以也需要組織形式也就是數據結構 也就是表
常量池中的表結構的類型可以分為三種類型
基本數據類型,比如 int long的描述形式,
雖然class文件是二進制字節流,最小為u1 但是這些基本數據類型在邏輯意義上來說,才是最小的描述單位
|
用於表述, 用於描述各個部分包含的邏輯內容的表 "結構體" 復合形式的數據類型結構
|
中間的映射結構表 相當於數據庫中的中間關系表
|
另外所有的常量池中的數據都是cp_info形式,所有的常量池中的數據都完全遵循這個格式
cp_info{
u1 tag;
u1 info[];
}
只不過info[]具體的格式,由tag來進行決定
也就是說不同的tag 那一塊區域的大小以及表示的含義自然是不同的
其實tag 跟我們平時寫代碼時候的類型是一個道理 就是接下來這塊區域的內容的類型標記
tag值如下:
也就是總共有下面這些類型的常量池信息
CONSTANT_Class | 7 |
CONSTANT_Fieldref | 9 |
CONSTANT_Methodref | 10 |
CONSTANT_InterfaceMethodref | 11 |
CONSTANT_String | 8 |
CONSTANT_Integer | 3 |
CONSTANT_Float | 4 |
CONSTANT_Long | 5 |
CONSTANT_Double | 6 |
CONSTANT_NameAndType | 12 |
CONSTANT_Utf8 | 1 |
CONSTANT_MethodHandle | 15 |
CONSTANT_MethodType | 16 |
CONSTANT_InvokeDynamic | 18 |
常量池中的基礎數據類型部分
我們先說下常量池中的封裝好的數據類型部分
字符串常量 |
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
|
tag是CONSTANT_Utf8 1 字符串采用改進過的UTF-8編碼表示 接下來是編碼后的字符串占用字節數以及字符串 class文件中的方法字段名稱都是此類型 |
int整型 4字節 |
CONSTANT_Integer_info {
u1 tag;
u4 bytes;
}
|
tag為CONSTANT_Integer 3 大端排序的int值 |
單精度浮點型 float 4字節 |
CONSTANT_Float_info {
u1 tag;
u4 bytes;
}
|
tag為CONSTANT_Float 4 大端排序 IEEE754單精度格式 的floa值 |
long與double 是64位 8個字節
分為4個高字節和4個低字節
long 長整型 8字節 |
CONSTANT_Long_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
|
tag為CONSTANT_Long 5 大端排序的long值 |
雙精度浮點型 double 8字節 |
CONSTANT_Double_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
|
tag為CONSTANT_Double 6 大端排序的 double值 |
除了基本數據類型其他的就是復合數據類型
所有的復合數據類型基本上都會包含其他的數據結構
這種包含方式使用的就是索引 #xxx的形式, 指向常量池中的另外一項數據
常量池中的中間關系映射類型部分
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index;
u2 descriptor_index;
}
|
tag為 CONSTANT_NameAndType (12) NameAndType 就是名稱和類型的意思 對於方法 / 字段 來說, 他們都有變量名稱或者方法名稱 他們也都有變量類型和方法簽名(方法的類型) NameAndType 是作為一個中間表形式的數據結構 字段/方法中都有一個索引指向他,他又指向了實際的名稱和類型 不管是方法名稱還是字段名稱 不管是方法簽名還是字段類型都是字符常量的形式 name_index 和 descriptor_index 指向的都是CONSTANT_Utf8_info |
常量池中的復合數據類型部分
String類型的常量對象
|
CONSTANT_String_info {
u1 tag;
u2 string_index;
}
|
tag為CONSTANT_String 8
他表示的是String類型的數據,我們知道String是常量
字符串常量是用CONSTANT_Utf8_info進行表示的
所以 String_index指向的就是對應的CONSTANT_Utf8_info的"行號"
|
方法類型 |
CONSTANT_MethodType_info {
u1 tag;
u2 descriptor_index;
}
|
CONSTANT_MethodType 16 CONSTANT_NameAndType_info 是一個用於字段或者方法結構中的中間結構,包含了名稱和類型 CONSTANT_MethodType_info 僅僅表示的就是方法簽名 方法簽名對應的是CONSTANT_Utf8_info 所以descriptor_index 指向 方法類型描述符的CONSTANT_Utf8_info |
類或接口 |
CONSTANT_Class_info {
u1 tag;
u2 name_index;
}
|
tag 為CONSTANT_Class 7 名稱自然是字符串常量也就是CONSTANT_Utf8_info 所以 name_index指向常量池中的 CONSTANT_Utf8_info |
字段 |
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
|
CONSTANT_Fieldref 9 class_index 表示當前字段 對應或者說所屬的 類或者接口 類和接口都可能 class_index指向CONSTANT_Class_info name_and_type_index 表示當前字段的名稱和類型 name_and_type_index指向CONSTANT_NameAndType_info |
方法 |
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
|
CONSTANT_Methodref 10 class_index 表示當前方法 對應或者說所屬的類,必須是類,不能是接口 class_index指向CONSTANT_Class_info
name_and_type_index 表示當前方法的名稱和方法簽名
name_and_type_index指向CONSTANT_NameAndType_info
|
接口方法 |
CONSTANT_InterfaceMethodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
|
CONSTANT_InterfaceMethodref 11 class_index 表示當前方法 對應或者說所屬的接口,必須是接口 不能是類 class_index指向CONSTANT_Class_info
name_and_type_index 表示當前方法的名稱和方法簽名
name_and_type_index指向CONSTANT_NameAndType_info
|
方法調用 |
CONSTANT_MethodHandle_info {
u1 tag;
u1 reference_kind;
u2 reference_index;
}
|
CONSTANT_MethodHandle 15 方法調用,顧名思義也就是描述 方法的調用 對於一個方法調用來說,方法可能有不同的類型,不同的類型有不同的操作對象 reference_kind 正是描述方法的調用類型 reference_index 描述的是方法的操作目標 reference_kind 的值為1~9 他的類型決定了方法句柄的類型 句柄類型的值表示方法句柄中字節碼行為 |
用於表示invokedynamic指令 |
CONSTANT_InvokeDynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
|
tag為CONSTANT_InvokeDynamic 18 CONSTANT_InvokeDynamic_info是為了字節碼指令 invokedynamic 使用的 invokedynamic是為了更好的支持動態類型語言,Java7通過JSR292給JVM增加的一條新的字節碼指令 bootstrap_method_attr_index 的值必須是對當前Class文件中引導方法表的bootstrap_methods[] 數組的有效索引 name_and_type_index 指向CONSTANT_NameAndType 表示方法名和方法描述符 |
class文件詳解之字段
字段表field_info 用於描述接口或者類中聲明的變量
包括類變量 以及 實例變量 不包括方法內部聲明的局部變量
可以包括的信息包括
- 字段的作用域 public private protected
- 字段類型 類變量還是實例變量 是否有static修飾
- 是否為常量 final
- 並發可見性 volatile
- 是否可以被序列化 transient
- 字段的數據類型 基本類型 對象 數組
- 字段名稱
字段 |
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
|
每個字段都由field_info結構定義 同一個class文件中不會有兩個字段同時具有相同的字段名和描述符 access_flags 表示字段訪問權限和基本屬性 name_index指向字段的名字 CONSTANT_utf8_info descriptor_index 指向字段描述符CONSTANT_utf8_info 字段field 包含屬性表,屬性表結構的情況稍后介紹 |
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 |
如同源代碼中abstract不能和final同時使用,此處的標志位規則也是如此,有些標志是互斥的
一個字段最多只能設置ACC_PUBLIC ACC_PRIVATE ACC_PROTECTED 的一種
不能同時設置ACC_FINAL 和 ACC_VOLATILE
接口中所有字段都具有 ACC_PUBLIC ACC_STATIC ACC_FINAL 也可以設置ACC_SYNTHETIC 其他的都不行了
class文件詳解之方法
方法 |
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
|
所有方法,包括實例初始化方法以及類或者接口初始化方法 一個class文件中不會有兩個方法具有相同的方法名和描述符 name_index 指向方法名字 CONSTANT_Utf8_info descriptor_index 表示方法描述符 指向 CONSTANT_Utf8_info 方法也有屬性表 |
access_flag 標志
基本也同方法的修飾符
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 | 方法是否為 編譯器為了字節碼兼容自動生成的bridge方法 |
ACC_VARARGS | 0x0080 | 方法是否為變長參數 |
ACC_NATIVE | 0x0100 | 方法是否為native 本地方法 |
ACC_ABSTRACT | 0x0400 | 方法是否為abstract 無實現代碼 |
ACC_STRICT | 0x0800 | 方法是否為strictfp 使用FP-strict浮點模式 |
ACC_SYNTHETIC | 0x1000 | 方法是否為編譯器自動產生而不是由源代碼編譯而來 |
volatile關鍵字和transient關鍵字不能修飾方法,所以自然也沒有這些個標志
而synchronized native strictfp 和 abstract 關鍵字可以修飾方法 所以相對於字段中的標志新增了對應的標志
類似字段,有些方法修飾符標志也是互斥的
一個方法只能設置ACC_PUBLIC ACC_PRIVATE ACC_PROTECTED 中的一種
接口方法可以設置 除了ACC_PROTECTED ACC_FINAL ACC_SYNCHRONIZED ACC_NATIVE 以外的,以外的
版本號小於52 每個方法必須設置 ACC_PUBLIC ACC_ABSTRACT
大於等於52 每個方法必須設置ACC_PUBLIC 或者 ACC_PRIVATE 中的一個
ps: 52 可以理解為jdk1.8
如果設置了ACC_ABSTRACT 不能再設置 ACC_FINAL ACC_NATIVE ACC_PRIVATE ACC_STATIC ACC_STRICT ACC_SYNCHRONIZED
實例初始化方法只能被ACC_PUBLIC ACC_PROTECTED ACC_PRIVATE 其中之一修飾
還可以設置 ACC_STRICT ACC_VARARGS ACC_SYNTHETIC 其他的都不能再設置
類或者接口的初始化方法 由虛擬機自動調用 除了ACC_STRICT以外,其它標志全部都會被忽略
class文件詳解之屬性
通過類 常量池 字段 方法的結構,已經塑造完成了 class文件的基本概念
他們是class文件的基礎骨架
骨架之上還有其他很多的附屬信息以及比如運行時需要的額外的信息
這些信息大多數不能歸結於一大類,邏輯架構上可能比較散亂,也可以理解為雜項信息
這些雜項就都是屬性表的范疇
不過Code屬性比較特殊,他其實也算作是一個骨架部分,或者說一個重要"器官" 他是作為方法中的代碼編譯后的字節碼形式存在的
只不過因為邏輯上 方法內的代碼字節碼指令顯然是歸屬於某個方法的,所以Code作為屬性表也可以理解
class文件的ClassFile結構
字段的field_info 結構
方法的method_info結構
另外還有Code屬性
以上四類都包含屬性結構信息
所有屬性表的梗概結構為
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
attribute_name_index 表示屬性的名字索引 指向 CONSTANT_Utf8_info
attribute_length就是屬性的長度
info[attribute_length] 是屬性的具體數據信息
所有的屬性按照用途,可以划分為三類
1.對於JVM 正確解讀class文件起關鍵作用的5個屬性
• ConstantValue
• Code
• StackMapTable
• Exceptions
• BootstrapMethods
|
2.對JavaSE 平台類庫正確解讀class文件起關鍵作用的12個屬性
• InnerClasses
• EnclosingMethod
• Synthetic
• Signature
• RuntimeVisibleAnnotations
• RuntimeInvisibleAnnotations
• RuntimeVisibleParameterAnnotations
• RuntimeInvisibleParameterAnnotations
• RuntimeVisibleTypeAnnotations
• RuntimeInvisibleTypeAnnotations
• AnnotationDefault
• MethodParameters
|
對JVM或者JavaSE平台類庫能夠正確解讀class文件 雖然不起關鍵作用,但是卻可以作為實用工具來使用的6個屬性
• SourceFile
• SourceDebugExtension
• LineNumberTable
• LocalVariableTable
• LocalVariableTypeTable
• Deprecated
|
我們已經知道 屬性出現於 classFile field_info method_info code 中
所有屬性按照位置划分
屬性 | 位置 | 備注 | 首次出現版本號 |
SourceFile
|
ClassFile | 表示class文件的源文件名稱 類獨有屬性 |
45.3 |
InnerClasses
|
ClassFile | 內部類相關信息 類獨有屬性 |
45.3 |
EnclosingMethod
|
ClassFile | class為局部類或者匿名類才具有 類獨有屬性 |
49.0 |
SourceDebugExtension
|
ClassFile | 可選/保存擴展調試信息/最多一個 類獨有屬性 |
49.0 |
BootstrapMethods | ClassFile |
與 invokedynamic指令
常量池中CONSTANT_InvokeDynamic_info
相關
類獨有屬性
|
51.0 |
ConstantValue | field_info | fina修飾的字段的常量值 字段獨有屬性 |
45.3 |
Code | method_info | java程序方法體中的代碼經過javac編譯器處理后 最終變為字節碼指令存儲在Code屬性內 Code屬性出現在方法表的屬性集合中 抽象類和接口不存在code屬性 包含了方法的java虛擬機指令及相關輔助信息 方法獨有屬性 |
45.3 |
Exceptions | method_info | 方法可能拋出的已檢查異常列表 方法獨有屬性 |
45.3 |
RuntimeVisibleParameterAnnotations,
RuntimeInvisibleParameterAnnotations
|
method_info | 形參上的運行時的注解信息類型 分為可見和不可見兩種類型 方法獨有屬性 |
49.0 |
AnnotationDefault | method_info | method_info表示注解類型中的元素時 記錄這個元素的默認值 方法獨有屬性 |
49.0 |
MethodParameters | method_info | 形參相關信息,比如參數名稱 方法獨有屬性 |
52.0 |
Synthetic | classFile field_info method_info |
Synthetic 標志編譯器生成 類 字段 方法都可能由編譯器生成 所以三種都有此屬性 |
45.3 |
Deprecated | classFile field_info method_info |
語義同@Deprecated 顯然可以標注在類/接口/字段/方法上 所以三種都有此屬性 |
45.3 |
Signature |
classFile
field_info
method_info
|
泛型信息 類接口 字段 方法 都有可能有類型參數 所以三種都有此屬性 |
49.0 |
RuntimeVisibleAnnotations,
RuntimeInvisibleAnnotations
|
classFile
field_info
method_info
|
類 方法 字段上 運行時注解的可見性 分為可見不可見兩種類型 三種都有此屬性 |
49.0 |
LineNumberTable | Code | 調試用信息 用於調試器確定源文件中給定行號所表示的內容,對應於虛擬機中code[]數組中的哪一部分 也就是行號與字節碼指令的對應關系 |
45.3 |
LocalVariableTable | Code | 調試用信息 調試器執行方法過程中可以用它來確定某個局部變量的值 |
45.3 |
LocalVariableTypeTable | Code |
調試用信息
調試器執行方法過程中可以用它來確定某個局部變量的值
|
49.0 |
StackMapTable | Code | 虛擬機類型檢查驗證使用信息 | 50.0 |
RuntimeVisibleTypeAnnotations,
RuntimeInvisibleTypeAnnotations
|
classFile
field_info
method_info
|
類/方法/字段聲明所使用的類型上面的運行時注解可見性 分為可見/不可見兩種 三種都有此屬性 |
52.0 |
變換一種組織形式
我們以位置為綱 可以很清晰的看到四個位置處都有那些信息
classFile |
SourceFile
InnerClasses
EnclosingMethod
SourceDebugExtension
BootstrapMethods
Synthetic
Deprecated
Signature
RuntimeVisibleAnnotations,
RuntimeInvisibleAnnotations
RuntimeVisibleTypeAnnotations,
RuntimeInvisibleTypeAnnotations
|
field_info |
ConstantValue
Synthetic
Deprecated
Signature
RuntimeVisibleAnnotations,
RuntimeInvisibleAnnotations
RuntimeVisibleTypeAnnotations,
RuntimeInvisibleTypeAnnotations
|
method_info |
Code
Exceptions
RuntimeVisibleParameterAnnotations,
RuntimeInvisibleParameterAnnotations
AnnotationDefault
MethodParameters
Synthetic
Deprecated
Signature
RuntimeVisibleAnnotations,
RuntimeInvisibleAnnotations
RuntimeVisibleTypeAnnotations,
RuntimeInvisibleTypeAnnotations
|
Code |
LineNumberTable
LocalVariableTable
LocalVariableTypeTable
StackMapTable
|
屬性表中的attribute_name_index 都是對應的屬性名稱 指向CONSTANT_Utf8_info 比如Code屬性值為Code ConstantValue屬性為ConstantValue
ConstantValue 屬性
通知虛擬機為靜態變量賦值
只有被static關鍵字修飾的變量才可以使用這個屬性 也就是只有類變量才可以使用
非static類型的變量也就是實例變量的賦值在構造方法中<init>
類變量可以再<clinit>方法中 也可以使用ConstantValue 屬性
目前編譯器的做法是 如果同時使用final和static來修飾,也就是常量了
如果變量的數據類型是基本類型或者java.lang.String的話,就生成ConstantValue
如果沒有final 或者並非基本類型或者字符串 選擇在<clinit>中
ConstantValue_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 constantvalue_index;
}
|
Code屬性
Code屬性是方法體中的代碼經過編譯處理后,最終的字節碼指令
既然是方法體的內容,如果沒有方法體自然沒有code屬性 比如 接口或者抽象類中就不存在Code屬性 native也不存在
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
|
attribute_name_index 指向CONSTANT_Utf8_info 常量值 固定為"Code" 表示屬性的名稱
attribute_length 屬性值長度 屬性表前面6個字節 u2 + u4 再加上attribute_length 就是整個表的長度了
max_stack 操作數棧的最大深度 方法執行任意時刻不會超過這個值,根據值來分配棧幀
ps:
虛擬機棧是線程私有的,每創建一個線程,虛擬機就會為這個線程創建一個虛擬機棧
虛擬機棧表示Java方法執行的內存模型,每調用一個方法就會為每個方法生成一個棧幀(Stack Frame),用來存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。
每個方法被調用和完成的過程,都對應一個棧幀從虛擬機棧上入棧和出棧的過程。
虛擬機棧的生命周期和線程是相同的
|
max_locals 局部變量表所需要的空間單位個數,可以理解為座位 這個位置叫做 Slot 可以查看javap后的信息
long double占用兩個單位 其他類型一個單位
並不是有多少局部變量就是求和計算,因為變量有作用域的生命周期,所以空間可以復用
編譯器會自動分配 ,並且計算出來這個值, 可以理解為 任意時刻最大的所需要空間個數
code_length 和code 才是真正的存儲字節碼的,雖然我們說code屬性就是存儲編譯后的字節碼
code_length指明了字節碼指令的長度 字節碼指令為u1
讀取一個指令后虛擬機就可以知道這個字節碼的含義以及這條指令后面是否還有參數
以及參數如何理解
exception_table 異常表表示的是可能出現的代碼執行路徑
表示如果字節碼start_pc行 到 end_pc行,包含頭不包含為 也就是不包含end_pc
出現了類型為catch_type 或者他的子類的異常 catch_type 指向一個CONSTANT_Class_info
轉到handler_pc中處理
u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
上面的代碼除了正常執行以外
如果try出現Exception或者子類的異常 轉到catch
如果出現不屬於Exception或者子類的異常 轉到finally
如果catch中出現任何異常,轉到finally
StackMapTable屬性
用於虛擬機類型檢查的驗證階段
是為了一種新的類型檢查驗證器而設置的,新的驗證器在編譯階段將一系列的驗證類型直接記錄在class文件中,
通過檢查這些驗證類型代替了類型推導過程
Code屬性表里最多可以包含一個StackMapTable
StackMapTable包含0個或者多個棧幀映射
用於表示 執行到該字節碼時局部變量表和操作數棧的驗證類型
類型檢查驗證器會通過檢查目標方法的局部變量和操作數棧所需要的類型來確定一段字節碼指令是否符合邏輯約束
版本號大於等於50的class文件中如果方法的code屬性中沒有附帶StackMapTable屬性,意味着他有一個 隱式的棧幀映射屬性
隱式的棧映射屬性的作用等同於number_of_entries 值為0的StackMapTable
StackMapTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_entries;
stack_map_frame entries[number_of_entries];
}
|
number_of_entries給出來的是stack_map_frame的個數
union stack_map_frame {
same_frame;
same_locals_1_stack_item_frame;
same_locals_1_stack_item_frame_extended;
chop_frame;
same_frame_extended;
append_frame;
full_frame;
}
|
Exceptions屬性
不是Code中的exception_table 注意區分
列舉出方法中可能拋出的已檢查的異常
也就是方法聲明throws后面的內容
Exceptions_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_exceptions;
u2 exception_index_table[number_of_exceptions];
}
|
attribute_name_index 指向名字
attribute_length表示長度
number_of_exceptions 表示個數
exception_index_table 指向常量池中的CONSTANT_Class_info 表示異常類型
BootstrapMethods 屬性
保存invokedynamic 指令引用的引導方法限定符
如果某個classFile文件中常量池中至少有一個CONSTANT_InvokeDynamic_info
那么就必須包含且只能包含一個BootstrapMethods
與invokedynamic指令和java.lang.invoke包關系密切
BootstrapMethods_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_bootstrap_methods;
{ u2 bootstrap_method_ref;
u2 num_bootstrap_arguments;
u2 bootstrap_arguments[num_bootstrap_arguments];
} bootstrap_methods[num_bootstrap_methods];
}
|
InnerClasses 屬性
記錄內部類與宿主類之間的關聯
如果一個類內部定義了內部類,編譯器就會為以及他所包含的內部類生成InnerClasses屬性
InnerClasses_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes;
{ u2 inner_class_info_index;
u2 outer_class_info_index;
u2 inner_name_index;
u2 inner_class_access_flags;
} classes[number_of_classes];
}
|
inner_class_info_index outer_class_info_index 都是指向常量池中CONSTANT_Utf8_info
分別代表內部類和宿主類的內部引用
inner_name_index指向常量池中CONSTANT_Utf8_info 表示內部類的名稱 匿名內部類 為0
inner_class_access_flags 內部類的訪問標志
ACC_PUBLIC | 0x0001 | 內部類是否為public |
ACC_PRIVATE | 0x0002 | 內部類是否為private |
ACC_PROTECTED | 0x0004 | 內部類是否為protected |
ACC_STATIC | 0x0008 | 內部類是否為static |
ACC_FINAL | 0x0010 | 內部類是否為final |
ACC_INTERFACE | 0x0200 | 內部類是否為interface |
ACC_ABSTRACT | 0x0400 | 內部類是否為abstract |
ACC_SYNTHETIC | 0x1000 | 內部類是否為編譯器自動生成 |
ACC_ANNOTATION | 0x2000 | 內部類是否為注解 |
ACC_ENUM | 0x4000 | 內部類是否為枚舉 |
Synthetic 屬性
標志是否有編譯器自動生成 沒有具體的值 只有存在和不存在的說法
Synthetic_attribute {
u2 attribute_name_index;
u4 attribute_length;
}
|
attribute_name_index 指向CONSTANT_Utf8_info 表示 synthetic
attribute_length 固定為0 也就是么有值
Signature 屬性
可選的屬性 1.5之后 任何 類 接口 初始化方法 或者成員 的泛型簽名如果包含了類型變量 或者 參數化類型
那么signature 屬性記錄泛型簽名信息
之所以需要是因為泛型擦除機制
反射機制獲取泛型類型 依賴數據就是這個屬性
Signature_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 signature_index;
}
|
EnclosingMethod 屬性
位於classFile結果的屬性 當且僅當class為局部類和匿名內部類時 才具有這個屬性
class_index 表示包含當前類的最內層類
method_index表示當前類是否在某個方法或者構造器中,如果不是 值為0
EnclosingMethod_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 class_index;
u2 method_index;
}
|
RuntimeVisibleAnnotations 屬性 RuntimeInvisibleAnnotations 屬性
添加在類聲明 字段聲明 方法聲明上面的注解 在運行時的可見情況
Visible 可見 Invisible不可見
ClassFile field_info method_info中最多只能有一個RuntimeVisibleAnnotations 或者 RuntimeInvisibleAnnotations
RuntimeVisibleAnnotations 和 RuntimeInvisibleAnnotations 基本一致, 但是 RuntimeInvisibleAnnotations 標志的不能被反射API訪問
除非虛擬機通過與實現相關的特殊方式保留這些注解,否則,虛擬機將忽略Invisible的注解
num_annotations表示注解的數量
annotations 每個元素都表示一個注解
RuntimeVisibleAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_annotations;
annotation annotations[num_annotations];
}
|
RuntimeInvisibleAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_annotations;
annotation annotations[num_annotations];
}
|
annotation {
u2 type_index;
u2 num_element_value_pairs;
{ u2 element_name_index;
element_value value;
} element_value_pairs[num_element_value_pairs];
}
type_index 用來表示一個字段描述符,字段描述符表示一個注解類型
和當前annotation 結構所表示的注解一致
num_element_value_pairs 表示注解中的鍵值對 (注解中的參數都是鍵值對) 的個數
element_value_pairs 代表真正的鍵值對
它包括
element_name_index表示鍵
element_value 表示值
element_value {
u1 tag;
union {
u2 const_value_index;
{ u2 type_name_index;
u2 const_name_index;
} enum_const_value;
u2 class_info_index;
annotation annotation_value;
{ u2 num_values;
element_value values[num_values];
} array_value;
} value;
}
element_value 表示一個聯合體
tag 使用u1 來表示鍵值對中的值是什么類型
也就是決定了鍵值對中 值的格式與value 中的哪一項相符合
聯合體總共有五種
const_value_index
enum_const_value
class_info_index
annotation_value
array_value
|
tag 值表示的類型
B | byte | const_value_index | CONSTANT_Integer |
C | char | const_value_index | CONSTANT_Integer |
D | double | const_value_index | CONSTANT_Double |
F | float | const_value_index | CONSTANT_Float |
I | int | const_value_index | CONSTANT_Integer |
J | long | const_value_index | CONSTANT_Long |
S | short | const_value_index | CONSTANT_Integer |
Z | boolean | const_value_index | CONSTANT_Integer |
s | String | const_value_index | CONSTANT_Utf8 |
e | Enum類型 | enum_const_value | 不適用 |
c | Class | class_info_index | 不適用 |
@ | Annotation類型 | annotation_value | 不適用 |
[ | Array 類型 | array_value | 不適用 |
const_value_index 表示原始類型的常量值 或者String類型的字面量 |
enum_const_value 表示一個枚舉常量
type_name_index 指向CONSTANT_Utf8_info 枚舉常量類型的二進制名稱的內部形式
const_name_index 指向CONSTANT_Utf8_info 枚舉常量的簡單名稱
|
class_info_index 表示類字面量 CONSTANT_Utf8_info 用於表示返回描述符 返回描述符給出了與該element_value結構所表示的類字面量相對應的類型
如果類字面量是C. class,且C是類、接口或數組類型的名字,那么對應的類型就是C。常量池中的返回描述符會是ObjectType 或者ArrayType
如果類字面量是p. class,且p是原始類型的名稱,那么對應的類型就是p 常量池中的返回描述符會是一個BaseType
如果類字面量是void. class,那么對應的類型就是void。常量池中的返回描述符會是V.
比如Object.class 對應於類型Object 所以常量池中就是Ljava/lang/Object; 而 int.class對應於類型int 常量池中就是I(大寫的i )
|
annotation_value 表示鍵值對中里面的值本身又是一個注解 |
array_value 表示鍵值對的 值 是一個數組
num_values 給出了當前element_value結構所表示的數組的成員數量
values 每個成員對應了當前element_value 結構所表示的數組中的一個元素
|
RuntimeVisibleTypeAnnotations 屬性 RuntimeInvisibleTypeAnnotations 屬性
classFile field_info method_info 或者code屬性中都有
比如對於某個類聲明implements后面的類型所加的注解 記錄在classFile結構體的這個屬性里
比如某個字段聲明中的類型 所加的全部注解記錄在字段的對應屬性里
記錄了標注在對應類聲明 字段聲明 或者方法聲明所使用的類型上面的注解 在運行時的可見情況
分為可見和不可見兩種
也記錄了標注在對應方法體重某個表達式所使用的類型上面的運行時可見注解
此外還記錄了標注在泛型類 接口 方法 以及構造器的類型參數聲明上面的注解
Java虛擬機必須使這些注解可供取用
最多只能有一個
num_annotations 表示注解個數
annotations 表示每一個注解 類型為type_annotation
RuntimeVisibleTypeAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_annotations;
type_annotation annotations[num_annotations];
}
|
RuntimeInvisibleTypeAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_annotations;
type_annotation annotations[num_annotations];
}
|
type_annotation {
u1 target_type;
union {
type_parameter_target;
supertype_target;
type_parameter_bound_target;
empty_target;
method_formal_parameter_target;
throws_target;
localvar_target;
catch_target;
offset_target;
type_argument_target;
} target_info;
type_path target_path;
u2 type_index;
u2 num_element_value_pairs;
{ u2 element_name_index;
element_value value;
} element_value_pairs[num_element_value_pairs];
}
|
前三項 target_type target_info 以及 target_path 指出了帶注解的類型所在的精確位置 target表示那個類型
后面type_index num_element_value_pairs element_value_pairs指出了注解本身的類型以及鍵值對
RuntimeVisibleParameterAnnotations 屬性 RuntimeInvisibleParameterAnnotations 屬性
保存標注在對應方法的形式參數聲明上面的注解的運行時可見狀態
分為可見和不可見兩種
num_parameters 形參個數
parameter_annotations 每個元素表示一個形式參數的運行時注解 第 n項,表示方法描述符中的第 n 個形式參數
RuntimeVisibleParameterAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u1 num_parameters;
{ u2 num_annotations;
annotation annotations[num_annotations];
} parameter_annotations[num_parameters];
}
|
RuntimeInvisibleParameterAnnotations_attribute {
u2 attribute_name_index;
u4 attribute_length;
u1 num_parameters;
{ u2 num_annotations;
annotation annotations[num_annotations];
} parameter_annotations[num_parameters];
}
|
AnnotationDefault 屬性
如果一個method_info 是用來表述注解類型中的元素的
該結構體的屬性表中最多只能有一個AnnotationDefault
AnnotationDefault屬性記錄了由method_info 結構所表示的那個元素的默認值
default_value 表示由AnnotationDefault屬性外圍的method_info結構所描述的那個注解類型元素的默認值
說白了這個結構有用的才是default_value
AnnotationDefault_attribute {
u2 attribute_name_index;
u4 attribute_length;
element_value default_value;
}
|
MethodParameters 屬性
形參相關的一些信息 比如參數名稱
parameters_count 表示本屬性外圍method_info 結構里面的descriptor_index所引用的那個方法描述符中,有多少個參數描述符
parameters 表示實際的參數
name_index 要么是0要么指向CONSTANT_Utf8_info 表示一個有效的非限定名 用來指代某個形式參數
access_flags ACC_FINAL 0x0010 形參為final
ACC_SYNTHETIC 0x1000 形參沒有顯式或者隱式的在源代碼中聲明,由編譯器生成
ACC_MANDATED 0x8000 形參是隱式聲明,也就是編程語言規范對所有的編譯器的要求必須生成
MethodParameters_attribute {
u2 attribute_name_index;
u4 attribute_length;
u1 parameters_count;
{ u2 name_index;
u2 access_flags;
} parameters[parameters_count];
}
|
sourceFile 屬性
class文件的源文件名,屬性是可選的可以關閉
但是一旦關閉,當拋出異常時 不會顯示出錯代碼所歸屬的文件名稱
SourceFile_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 sourcefile_index;
}
|
sourcefile_index 指向的是常量池中的CONSTANT_Utf8_info 表示源文件的文件名
SourceDebugExtension 屬性
一個classFile只能包含一個屬性
debug_extension 用於保存擴展調試信息 擴展調試信息對於java虛擬機來說沒有實際的語義
擴展信息使用改進版的UTF8 編碼的字符串 也可以說這個就算是另一種格式的用於表示字符串的結構
不過他比String類的實例所能表示的字符串更長
SourceDebugExtension_attribute {
u2 attribute_name_index;
u4 attribute_length;
u1 debug_extension[attribute_length];
}
|
LineNumberTable 屬性
Code屬性的屬性表中
源文件中給定的行號表示的內容對應字節碼指令的行號(偏移量)之間的關系
並不是運行時必須信息,但是會默認生成到Class文件中
可以通過參數設置不生成,但是程序運行產生的最主要影響就是當拋出異常時,堆棧中不會顯示出錯的行號
調試時也無法設置斷點
LineNumberTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{ u2 start_pc;
u2 line_number;
} line_number_table[line_number_table_length];
}
|
LocalVariableTable 屬性
用於描述棧幀中的局部變量表中的變量與java源代碼中定義的變量之間的關系
也可以不生成,但是可能會導致別人引用方法時,參數名稱丟失
LocalVariableTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_table_length;
{ u2 start_pc;
u2 length;
u2 name_index;
u2 descriptor_index;
u2 index;
} local_variable_table[local_variable_table_length];
}
|
LocalVariableTypeTable 屬性
與LocalVariableTable類似,就是descriptor_index 被替換為了signature_index
主要針對泛型場景
非泛型簽名與描述符基本一致,引入泛型后實際的泛型信息會被擦除 描述符就不足夠准確了
LocalVariableTypeTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_type_table_length;
{ u2 start_pc;
u2 length;
u2 name_index;
u2 signature_index;
u2 index;
} local_variable_type_table[local_variable_type_table_length];
}
|
Deprecated 屬性
語義同語法中的@Deprecated 形式類似Synthetic 標志屬性 有和沒有的區別
attribute_length 固定為0
Deprecated_attribute {
u2 attribute_name_index;
u4 attribute_length;
}
|
class文件的最詳細的介紹
自然是官方文檔
https://docs.oracle.com/javase/specs/index.html
包含多個版本的JDK
以及兩種格式
另外也有中文版