Class文件結構、反射的實現原理與使用

參考:
https://blog.csdn.net/IT_GJW/article/details/80447947
https://zhuanlan.zhihu.com/p/24789506

Class文件在JVM底層的實現

索引:


Class文件.png

Class文件是一組以8位字節為基礎單位的二進制流,
各個數據項目按嚴格的順序緊湊的排列在Class文件中,
里面的信息主要描述以下信息:

1. 魔數和版本號

1.1. 魔數:0xCAFEBABE,確定這個文件是否為一個能被虛擬機接收的Class文件
1.2. Class文件的版本號:第5和第6個字節是次版本號(Minor Version),第7和第8個字節是主版本號(Major Version)。高版本的JDK能向下兼容以前版本的Class文件,但不能運行以后版本的Class文件。

2. 常量池

主要存放字面量(Literal)和符號引用(references)

  1. 字面量:文本字符串、final 類型的常量值 等
  2. 符號引用:
    a. 類和接口的全限定名
    b. 字段描述和描述符
    c. 方法的名稱和描述

Java代碼在進行Javac編譯時,并不像C和C++那樣有“連接”的步驟,而是在虛擬機加載Class文件的時候進行動態連接。
也就是說,
在Class文件中不會保存各個方法、字段的最終內存布局信息,
當虛擬機運行時,需要從常量池獲得對應的符號引用,再在類創建時或運行時解析、翻譯到具體的內存地址之中。
JDK1.7中,總共有14種類型的常量,每種常量都是表類型的數據項。
這14種表都有一個共同的特點,就是表開始的第一位是一個u1類型的標志位,代表當前常量屬于哪種常量類型。

14種常量類型代表的具體含義見下表
類型 標志 描述
Constant_Utf8_info 1 UTF-編碼的字符串
Constant_Integer_info 3 整型字面量
Constant_Float_info 4 浮點型字面量
Constant_Long_info 5 長整型字面量
Constant_Double_info 6 雙精度浮點型字面量
Constant_Class_info 7 類或接口的符號引用
Constant_String_info 8 字符串類型字面量
Constant_Fieldref_info 9 字段的符號引用
Constant_Methodref_info 10 類中方法的符號引用
Constant_InterfaceMethodref_info 11 接口中方法的符號引用
Constant_NameAndType_info 12 字段或方法的部分符號引用
Constant_MethodHandle_info 15 表示方法句柄
Constant_MethodType_info 16 標識方法類型
Constant_InvodeDynamic_info 18 表示一個動態方法調用點

可以通過用命令javap -verbose TestClass.class命令查看class文件的字節碼內容,如


TestClass
例:Constant_Methodref_info的結構
tag index index
u1 u2 u2
標志位,值如上表,10 指向生命方法的類描述符Constant_Class_info的索引項 指向名稱及類型描述符Constant_NameAndType_info的索引項
10 例:0x0004 例:0x000f
例:Constant_Class_info的結構
tag index
u1 u2
標志位,值如上表,7 指向全限定名常量項的索引項
7 例:0x0011
例:Constant_Utf8_info的結構
tag length bytes
u1 u2 u1
標志位,值如上表,1 UTF-8編碼的字符串占用的字節數 長度為length的UTF-8編碼的字符串
1 例:java/lang/Object

3. 當前類的訪問標志:

常量池結束之后,緊接著的兩個字節代表訪問標志,這個標志用于識別一些類或者接口層次的訪問信息
a.這個Class是類還是接口
b.這個Class是否是public 等類型
c.這個Class是否是abstract ,是否被聲明為final 等標志

4. 類索引、父類索引和接口索引集合

a.類索引:確定這個類的全限定名
b.父類索引:確定父類的全限定名
c.接口索引集合:描述這個類實現了哪些接口,它是一組u2類型的數據的集合,集合中的第一項是接口計數器,表示索引表的容量。如果一個類沒有實現任何接口,則該計數器值為0。

5. 字段表集合(Fileds)

用于描述接口或者類中聲明的變量。
包括信息有

類型 名稱 數量 說明
u2 access_flags 1 字段作用域(public,private等修飾符)
是實例變量還是類變量(static)
可變性 (final)
并發可見性(volatile)
可否被序列化(transient)等信息
u2 name_index 1 對常量池的引用
代表字段的簡單名稱
u2 descriptor_index 1 對常量池的引用
代表字段的描述符
描述字段的數據類型
u2 attribute_count 1 計數器
如果其值為0:字段沒有額外信息
如果其值不為0:則attribute_count后面會緊跟著attribute_count個attribute數據項。
attribute_info attributes attributes_count

6. 方法表集合:

包括

類型 名稱 數量 說明
u2 access_flags 1 訪問標志
u2 name_index 1 名稱索引
u2 descriptor_index 1 描述符索引
u2 attributes_count 1 屬性表集合
方法里的Java代碼經過編譯器編譯成字節碼指令后,存放在方法屬性表集合中一個名為“Code”的屬性表中。屬性表作為class文件格式中最具有擴展性的一種數據項目,將在隨后介紹。
attribute_info attributes attributes_count

7. 其他:包括屬性表集合、Code 屬性(指令) 等。

屬性表(attribute_info)在前面的講解中已經出現過多次了,字段表方法表都可以攜帶自己的屬性表集合,以用于描述某些場景專有的信息。

與Class文件中其他的數據項目要求嚴格的順序、長度和內容不同,
**屬性表集合的限制稍微寬松了一些,不再要求各個屬性表具有嚴格的順序。
為了能夠正確解析Class文件,《Java虛擬機規范(Java SE 7)》中,預定義了21項屬性表。下文將對一些常用的屬性表進行講解。

屬性名稱 使用位置 含義
Code 方法表 Java代碼編譯成的字節碼指令
ConstantValue 字段表 final關鍵字定義的常量值
Deprecated 類、方法表、字段表 被聲明為deprecated的方法和字段
Exceptions 方法表 方法拋出的異常
EnclosingMethod 類文件 僅當一個類為局部類或者匿名類時才能擁有這個屬性
這個屬性用于標識這個類所在的外圍方法
InnerClasses 類文件 內部類列表
LineNumberTable Code屬性 Java源碼的行號與字節碼指令的對應關系
LineVariableTable Code屬性 方法的局部變量描述
StackMapTable Code屬性
Signature 類、方法表、字段表
SourceFile 類文件 記錄源文件名稱
SourceDebugExtension 類文件
Synthetic 類、方法表、字段表
LocalVariableTypeTable
RuntimeVisibleAnnotations 類、方法表、字段表
RuntimeInvisibleAnnotations 類、方法表、字段表
RuntimeVisibleParameter 方法表
RuntimeInvisibleParameter 方法表
AnnotationDefault 方法表
BootstrapMethod 類文件
Code屬性

Code屬性存儲編譯后的字節碼指令,它出現在方法表的屬性集合中,
但并非所有方法都必須存在這個屬性,譬如抽象方法就不存在Code屬性。
Code屬性的結構如下表所示

類型 名稱 數量
u2 attribute_name_index 1 指向CONSTANT_Utf8_info型常量的索引
值固定為“Code”
代表該屬性的屬性名稱
attribute_length指示了屬性值的長度
u4 attribute_length 1
u2 max_stack 1 操作數棧的最大值
在方法執行的任意時刻
操作數棧都不會超過這個深度
虛擬機運行時需要根據這個值來分配棧幀中操作棧深度
u2 max_local 1 局部變量表所需的存儲空間
它的單位是Slot
u4 code_length 1 字節碼長度
u1 code code_length 存儲字節碼指令的一系列字節流
每個指令就是一個u1類型的單字節
u1類型的取值范圍是0x00~0xFF
一共可以表達256條指令
u2 exception_table_length 1
exception_info exception_table exception_table_length
u2 attributes_count 1
attribute_info attributes attributes_count

可以通過用命令javap -verbose TestClass.class命令查看一個class文件中方法的Code屬性,如


TestClass

反射

Java反射機制就是在運行狀態中,
對于任意一個類,都能夠知道這個類的屬性和方法。
對于任意一個對象能夠調用它的任意一個屬性和方法。
這種動態獲取的信息和動態調用對象的方法的功能稱為Java語言的反射機制

反射機制就是通過Class類實現的。
在Java中,Object 類是所有類的根類,而Class類就是描述Java類的類。

在Java中,每一個class都有一個相應的Class對象,
在將Java源碼編譯成.class文件中就會生成一個Class對象,
Class對象表示這個類的類型信息,你也可以理解成Class是類的類型

注意:因為Class類也是類,所以Object也包括Class類

我們創建對象一般是通過new關鍵字創建,
但是new是靜態加載類,一旦找不到類就會編譯不通過。
但是通過反射機制創建對象一旦找不到類則拋出java.lang.ClassNotFoundException異常。

Class對象的常用方法:

Constructor[] getConstructors():返回此Class對象所表示的類的所有public構造方法
Method[] getMethods():返回此Class對象所表示的類的所有public方法
Method[] getDeclaredMethods():返回此Class對象所表示的類的所有方法,與方法的訪問級別無關
Field[] getFields():返回此Class對象所表示的類的所有public屬性
Field[] getDecalaredDields():返回此Class對象所表示的類的所有屬性,與屬性訪問級別無關
Object get(Object obj):得到引用類型屬性值
void set(Object obj,Object val):將obj對象的該屬性設置成val值。針對引用類型賦值

Object invoke(Object obj,Object args):調用類的方法,obj是執行該方法的對象,args是執行該方法時傳入該方法的參數

通過Class類得到指定的對象:

public interface Office {
    /**
     * 描述
     */
    public void describe();
}
public class Word implements Office {
    @Override
    public void describe() {
        System.out.println("大家好,我是Word");
    }
}
public class Test {
    public static void main(String[] string) throws ClassNotFoundException {
        try {
            @SuppressWarnings("rawtypes")
            //傳入接口實現類的路徑
            Class office = Class.forName("com.Word");           
            try {
                //創建該類的對象
                Office word = (Office)office.newInstance();     
                //調用方法
                word.describe();                                
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

通過對象得到Class類

public class Test {
    @SuppressWarnings("rawtypes")
    public static void main(String[] string) throws ClassNotFoundException {
        Word word = new Word();
        Class word1 = word.getClass();              // 通過對象的getClass()方法獲取Class
        Class word2 = Word.class;                   // 通過類.class獲取Class
        Class word3 = Class.forName("com.Word");    // 通過路徑獲取Class
        System.out.println(word1 == word2);
        System.out.println(word2 == word3);
    }

}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容