參考:
https://blog.csdn.net/IT_GJW/article/details/80447947
https://zhuanlan.zhihu.com/p/24789506
Class文件在JVM底層的實(shí)現(xiàn)
索引:
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)
- 字面量:文本字符串泥从、final 類型的常量值 等
- 符號(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)容,如
例: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屬性装盯,如
反射
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);
}
}