Class文件結(jié)構(gòu)高职、反射的實(shí)現(xiàn)原理與使用

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

Class文件在JVM底層的實(shí)現(xiàn)

索引:


Class文件.png

Class文件是一組以8位字節(jié)為基礎(chǔ)單位的二進(jìn)制流疼约,
各個(gè)數(shù)據(jù)項(xiàng)目按嚴(yán)格的順序緊湊的排列在Class文件中,
里面的信息主要描述以下信息:

1. 魔數(shù)和版本號(hào)

1.1. 魔數(shù):0xCAFEBABE,確定這個(gè)文件是否為一個(gè)能被虛擬機(jī)接收的Class文件
1.2. Class文件的版本號(hào):第5和第6個(gè)字節(jié)是次版本號(hào)(Minor Version),第7和第8個(gè)字節(jié)是主版本號(hào)(Major Version)谣辞。高版本的JDK能向下兼容以前版本的Class文件迫摔,但不能運(yùn)行以后版本的Class文件。

2. 常量池

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

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

Java代碼在進(jìn)行Javac編譯時(shí)句占,并不像C和C++那樣有“連接”的步驟,而是在虛擬機(jī)加載Class文件的時(shí)候進(jìn)行動(dòng)態(tài)連接躯嫉。
也就是說纱烘,
在Class文件中不會(huì)保存各個(gè)方法、字段的最終內(nèi)存布局信息祈餐,
當(dāng)虛擬機(jī)運(yùn)行時(shí)擂啥,需要從常量池獲得對(duì)應(yīng)的符號(hào)引用,再在類創(chuàng)建時(shí)或運(yùn)行時(shí)解析帆阳、翻譯到具體的內(nèi)存地址之中哺壶。
JDK1.7中,總共有14種類型的常量蜒谤,每種常量都是表類型的數(shù)據(jù)項(xiàng)山宾。
這14種表都有一個(gè)共同的特點(diǎn),就是表開始的第一位是一個(gè)u1類型的標(biāo)志位鳍徽,代表當(dāng)前常量屬于哪種常量類型资锰。

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

可以通過用命令javap -verbose TestClass.class命令查看class文件的字節(jié)碼內(nèi)容,如


TestClass
例:Constant_Methodref_info的結(jié)構(gòu)
tag index index
u1 u2 u2
標(biāo)志位阶祭,值如上表绷杜,10 指向生命方法的類描述符Constant_Class_info的索引項(xiàng) 指向名稱及類型描述符Constant_NameAndType_info的索引項(xiàng)
10 例:0x0004 例:0x000f
例:Constant_Class_info的結(jié)構(gòu)
tag index
u1 u2
標(biāo)志位,值如上表濒募,7 指向全限定名常量項(xiàng)的索引項(xiàng)
7 例:0x0011
例:Constant_Utf8_info的結(jié)構(gòu)
tag length bytes
u1 u2 u1
標(biāo)志位接剩,值如上表,1 UTF-8編碼的字符串占用的字節(jié)數(shù) 長度為length的UTF-8編碼的字符串
1 例:java/lang/Object

3. 當(dāng)前類的訪問標(biāo)志:

常量池結(jié)束之后萨咳,緊接著的兩個(gè)字節(jié)代表訪問標(biāo)志懊缺,這個(gè)標(biāo)志用于識(shí)別一些類或者接口層次的訪問信息
a.這個(gè)Class是類還是接口
b.這個(gè)Class是否是public 等類型
c.這個(gè)Class是否是abstract ,是否被聲明為final 等標(biāo)志

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

a.類索引:確定這個(gè)類的全限定名
b.父類索引:確定父類的全限定名
c.接口索引集合:描述這個(gè)類實(shí)現(xiàn)了哪些接口培他,它是一組u2類型的數(shù)據(jù)的集合鹃两,集合中的第一項(xiàng)是接口計(jì)數(shù)器,表示索引表的容量舀凛。如果一個(gè)類沒有實(shí)現(xiàn)任何接口俊扳,則該計(jì)數(shù)器值為0。

5. 字段表集合(Fileds)

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

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

6. 方法表集合:

包括

類型 名稱 數(shù)量 說明
u2 access_flags 1 訪問標(biāo)志
u2 name_index 1 名稱索引
u2 descriptor_index 1 描述符索引
u2 attributes_count 1 屬性表集合
方法里的Java代碼經(jīng)過編譯器編譯成字節(jié)碼指令后号坡,存放在方法屬性表集合中一個(gè)名為“Code”的屬性表中。屬性表作為class文件格式中最具有擴(kuò)展性的一種數(shù)據(jù)項(xiàng)目梯醒,將在隨后介紹宽堆。
attribute_info attributes attributes_count

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

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

與Class文件中其他的數(shù)據(jù)項(xiàng)目要求嚴(yán)格的順序、長度和內(nèi)容不同猫胁,
**屬性表集合的限制稍微寬松了一些箱亿,不再要求各個(gè)屬性表具有嚴(yán)格的順序。
為了能夠正確解析Class文件弃秆,《Java虛擬機(jī)規(guī)范(Java SE 7)》中极景,預(yù)定義了21項(xiàng)屬性表。下文將對(duì)一些常用的屬性表進(jìn)行講解驾茴。

屬性名稱 使用位置 含義
Code 方法表 Java代碼編譯成的字節(jié)碼指令
ConstantValue 字段表 final關(guān)鍵字定義的常量值
Deprecated 類、方法表氢卡、字段表 被聲明為deprecated的方法和字段
Exceptions 方法表 方法拋出的異常
EnclosingMethod 類文件 僅當(dāng)一個(gè)類為局部類或者匿名類時(shí)才能擁有這個(gè)屬性
這個(gè)屬性用于標(biāo)識(shí)這個(gè)類所在的外圍方法
InnerClasses 類文件 內(nèi)部類列表
LineNumberTable Code屬性 Java源碼的行號(hào)與字節(jié)碼指令的對(duì)應(yīng)關(guān)系
LineVariableTable Code屬性 方法的局部變量描述
StackMapTable Code屬性
Signature 類锈至、方法表、字段表
SourceFile 類文件 記錄源文件名稱
SourceDebugExtension 類文件
Synthetic 類译秦、方法表峡捡、字段表
LocalVariableTypeTable
RuntimeVisibleAnnotations 類、方法表筑悴、字段表
RuntimeInvisibleAnnotations 類们拙、方法表、字段表
RuntimeVisibleParameter 方法表
RuntimeInvisibleParameter 方法表
AnnotationDefault 方法表
BootstrapMethod 類文件
Code屬性

Code屬性存儲(chǔ)編譯后的字節(jié)碼指令阁吝,它出現(xiàn)在方法表的屬性集合中砚婆,
但并非所有方法都必須存在這個(gè)屬性,譬如抽象方法就不存在Code屬性突勇。
Code屬性的結(jié)構(gòu)如下表所示

類型 名稱 數(shù)量
u2 attribute_name_index 1 指向CONSTANT_Utf8_info型常量的索引
值固定為“Code”
代表該屬性的屬性名稱
attribute_length指示了屬性值的長度
u4 attribute_length 1
u2 max_stack 1 操作數(shù)棧的最大值
在方法執(zhí)行的任意時(shí)刻
操作數(shù)棧都不會(huì)超過這個(gè)深度
虛擬機(jī)運(yùn)行時(shí)需要根據(jù)這個(gè)值來分配棧幀中操作棧深度
u2 max_local 1 局部變量表所需的存儲(chǔ)空間
它的單位是Slot
u4 code_length 1 字節(jié)碼長度
u1 code code_length 存儲(chǔ)字節(jié)碼指令的一系列字節(jié)流
每個(gè)指令就是一個(gè)u1類型的單字節(jié)
u1類型的取值范圍是0x00~0xFF
一共可以表達(dá)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命令查看一個(gè)class文件中方法的Code屬性装盯,如


TestClass

反射

Java反射機(jī)制就是在運(yùn)行狀態(tài)中,
對(duì)于任意一個(gè)類甲馋,都能夠知道這個(gè)類的屬性和方法埂奈。
對(duì)于任意一個(gè)對(duì)象能夠調(diào)用它的任意一個(gè)屬性和方法。
這種動(dòng)態(tài)獲取的信息和動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為Java語言的反射機(jī)制

反射機(jī)制就是通過Class類實(shí)現(xiàn)的定躏。
在Java中账磺,Object 類是所有類的根類芹敌,而Class類就是描述Java類的類。

在Java中垮抗,每一個(gè)class都有一個(gè)相應(yīng)的Class對(duì)象氏捞,
在將Java源碼編譯成.class文件中就會(huì)生成一個(gè)Class對(duì)象,
Class對(duì)象表示這個(gè)類的類型信息借宵,你也可以理解成Class是類的類型

注意:因?yàn)镃lass類也是類幌衣,所以O(shè)bject也包括Class類

我們創(chuàng)建對(duì)象一般是通過new關(guān)鍵字創(chuàng)建,
但是new是靜態(tài)加載類壤玫,一旦找不到類就會(huì)編譯不通過豁护。
但是通過反射機(jī)制創(chuàng)建對(duì)象一旦找不到類則拋出java.lang.ClassNotFoundException異常。

Class對(duì)象的常用方法:

Constructor[] getConstructors():返回此Class對(duì)象所表示的類的所有public構(gòu)造方法
Method[] getMethods():返回此Class對(duì)象所表示的類的所有public方法
Method[] getDeclaredMethods():返回此Class對(duì)象所表示的類的所有方法欲间,與方法的訪問級(jí)別無關(guān)
Field[] getFields():返回此Class對(duì)象所表示的類的所有public屬性
Field[] getDecalaredDields():返回此Class對(duì)象所表示的類的所有屬性楚里,與屬性訪問級(jí)別無關(guān)
Object get(Object obj):得到引用類型屬性值
void set(Object obj,Object val):將obj對(duì)象的該屬性設(shè)置成val值。針對(duì)引用類型賦值

Object invoke(Object obj,Object args):調(diào)用類的方法猎贴,obj是執(zhí)行該方法的對(duì)象班缎,args是執(zhí)行該方法時(shí)傳入該方法的參數(shù)

通過Class類得到指定的對(duì)象:

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")
            //傳入接口實(shí)現(xiàn)類的路徑
            Class office = Class.forName("com.Word");           
            try {
                //創(chuàng)建該類的對(duì)象
                Office word = (Office)office.newInstance();     
                //調(diào)用方法
                word.describe();                                
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

通過對(duì)象得到Class類

public class Test {
    @SuppressWarnings("rawtypes")
    public static void main(String[] string) throws ClassNotFoundException {
        Word word = new Word();
        Class word1 = word.getClass();              // 通過對(duì)象的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);
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末她渴,一起剝皮案震驚了整個(gè)濱河市达址,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌趁耗,老刑警劉巖沉唠,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異苛败,居然都是意外死亡满葛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人揪阶,你說我怎么就攤上這事〕” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵曼月,是天一觀的道長肃叶。 經(jīng)常有香客問我,道長十嘿,這世上最難降的妖魔是什么因惭? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮绩衷,結(jié)果婚禮上蹦魔,老公的妹妹穿的比我還像新娘激率。我一直安慰自己,他們只是感情好勿决,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布乒躺。 她就那樣靜靜地躺著,像睡著了一般低缩。 火紅的嫁衣襯著肌膚如雪嘉冒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天咆繁,我揣著相機(jī)與錄音讳推,去河邊找鬼。 笑死玩般,一個(gè)胖子當(dāng)著我的面吹牛银觅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播坏为,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼究驴,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了匀伏?” 一聲冷哼從身側(cè)響起洒忧,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎够颠,沒想到半個(gè)月后熙侍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡摧找,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了牢硅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹬耘。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖减余,靈堂內(nèi)的尸體忽然破棺而出综苔,到底是詐尸還是另有隱情,我是刑警寧澤位岔,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布如筛,位于F島的核電站,受9級(jí)特大地震影響抒抬,放射性物質(zhì)發(fā)生泄漏杨刨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一擦剑、第九天 我趴在偏房一處隱蔽的房頂上張望妖胀。 院中可真熱鬧芥颈,春花似錦、人聲如沸赚抡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涂臣。三九已至盾计,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赁遗,已是汗流浹背署辉。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吼和,地道東北人涨薪。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像炫乓,于是被迫代替她去往敵國和親刚夺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容