點(diǎn)贊關(guān)注锚烦,不再迷路扩然,你的支持對(duì)我意義重大艘儒!
?? Hi,我是丑丑夫偶。本文 「Java 路線」| 導(dǎo)讀 —— 他山之石界睁,可以攻玉 已收錄,這里有 Android 進(jìn)階成長(zhǎng)路線筆記 & 博客兵拢,歡迎跟著彭丑丑一起成長(zhǎng)翻斟。(聯(lián)系方式在 GitHub)
前言
-
Class 文件
是Java
技術(shù)體系的重要組成部分,在學(xué)習(xí)整個(gè)虛擬機(jī)的執(zhí)行引擎之前说铃,應(yīng)該清楚Class 文件
的結(jié)構(gòu)访惜; - 這篇文章將帶你理解
Class 文件
的基本結(jié)構(gòu),希望能幫上忙腻扇。
延伸文章
對(duì)于
Java
編譯過程不了解债热,請(qǐng)閱讀:《Java | 聊一聊編譯過程(編譯前端 & 編譯后端)》對(duì)于
類加載
的流程不太了解,請(qǐng)閱讀:《Java | 談?wù)勀銓?duì)類加載過程的理解》
目錄
1. 什么是 Class 文件幼苛?
定義:或稱字節(jié)碼窒篱,可以看作
Java 虛擬機(jī)
的可執(zhí)行文件作用:對(duì)應(yīng)于一個(gè)類 / 抽象類 / 接口的定義信息
意義:
Java 虛擬機(jī) & Class 文件
共同構(gòu)成了Java
無關(guān)性的基礎(chǔ)-
來源
- 由
Java 源碼
經(jīng)過Java 編譯器
編譯后得到,以磁盤文件形式存在
- 由
- 由字節(jié)碼生成技術(shù)
(如javassist / CGLib / ASM)
生成,以內(nèi)存中二進(jìn)制流形式存在
- 由字節(jié)碼生成技術(shù)
# 咬文嚼字 #
雖然字節(jié)碼不一定是以 “磁盤文件” 的形式存在墙杯,但是通常在很多文獻(xiàn) & 資料中會(huì)籠統(tǒng)地將字節(jié)碼表述為
Class 文件
济锄,這里不必鉆牛角尖。
更多信息請(qǐng)務(wù)必閱讀:《Java | 為什么 Java 實(shí)現(xiàn)了平臺(tái)無關(guān)性》
2. Class 文件的大致結(jié)構(gòu)
-
Class 文件
是一種強(qiáng)協(xié)議的緊湊型結(jié)構(gòu)(遵循《Java 虛擬機(jī)規(guī)范》) -
Class 文件
有三種數(shù)據(jù)結(jié)構(gòu):無符號(hào)數(shù)霍转、TVL、表一汽,具體如下:
-
Class 文件
本質(zhì)上也是一個(gè)表避消,大致結(jié)構(gòu)如下圖:
-
魔數(shù):固定值
0xCAFEBABE
,用于鑒別為合法的Class 文件
-
版本號(hào):表示
Class 文件
的目標(biāo)版本號(hào)召夹,高版本的虛擬機(jī)可以向前兼容舊版本Class 文件
-
訪問標(biāo)志:一個(gè)
u2
無符號(hào)數(shù)岩喷,用于表示本類 / 接口的訪問信息。其中每個(gè)標(biāo)志位的值與java.lang.reflect.Modifier
中的常量一一對(duì)應(yīng):
本類索引 & 父類索引 & 接口索引集:是一個(gè)索引值监憎,(共經(jīng)過 2 次索引后)指向常量池中一個(gè)
utf-8
編碼的 全限定名纱意,例如:java/lang/Object;
字段表集合:用于描述類或接口中聲明的變量(包括類變量與成員變量)
方法表集合:用于描述類或接口中聲明的方法(包括類方法與成員方法)
屬性表集合:用于描述 Class 文件、字段表鲸阔、方法表中攜帶的屬性
下面偷霉,我將概括表中各個(gè)重點(diǎn)數(shù)據(jù)項(xiàng)的具體含義!
3. 常量池(const pool)
- 常量池中的每一項(xiàng)常量都是一個(gè)表
- 存放兩種類常量:字面量(Literal)和 符號(hào)引用(Symbolic References)
符號(hào)引用(Symbolic References)是一個(gè)字符串類型的字面量(存儲(chǔ)在常量池)褐筛,它的作用是唯一地標(biāo)示一個(gè)實(shí)體类少,最重要的特點(diǎn)如下:
平臺(tái)無關(guān)性
這一點(diǎn)與Java
的特性是一脈相承的。符號(hào)引用與具體虛擬機(jī)實(shí)現(xiàn)內(nèi)存布局無關(guān)渔扎,需要在運(yùn)行期將符號(hào)引用轉(zhuǎn)換為直接引用(Direct Reference)硫狞,這個(gè)直接引用才是符號(hào)引用在虛擬機(jī)中的真實(shí)存在。唯一性
無歧義地標(biāo)示一個(gè)目標(biāo)晃痴,以方法為例残吩,如果是本類中聲明的方法,不需要添加類名(如:Method func:()V
)倘核;如果是其他類中聲明的方法泣侮, 需要添加類名前綴(如:Method com/Base.func:()V
)。
常量池表結(jié)構(gòu)有一個(gè)共同的特點(diǎn)笤虫,就是表結(jié)構(gòu)的首元素是u1
的標(biāo)志位旁瘫,代表當(dāng)前的常量類型,截止到Java 13
總共有 20 種常量:
4. 本類索引 & 父類索引 & 接口索引集
- 本類索引肯定存在琼蚯,只有一個(gè)
-
Java
是類單繼承酬凳,所以父類索引只有一個(gè)(特例:Object
的父類索引為 0) -
Java
是接口多繼承,所以接口索引有零或多個(gè)
這三個(gè)索引值均指向常量池中CONSTANT_Class_info
常量遭庶,而CONSTANT_Class_info
常量本質(zhì)上也是一個(gè)索引值宁仔,指向CONSTANT_Utf8_info
常量。經(jīng)過 2次 索引峦睡,這三個(gè)索引最終指向?qū)?yīng) 類 / 接口的全限定名
5. 字段表(field_info)
字段表用于描述類字段與實(shí)例字段翎苫,但只包括在本類中聲明的字段权埠,既不包括父類中聲明的字段,也不包括方法內(nèi)部聲明的局部變量煎谍。要注意的是攘蔽,編譯器生成的字段是包括的,例如編譯器會(huì)為非靜態(tài)內(nèi)部類添加外部類的引用字段呐粘。字段表的基本結(jié)構(gòu)如下:
- access_flags:字段的訪問標(biāo)志位
- name_index:常量池索引满俗,最終指向字段的簡(jiǎn)單名稱,見第 8 節(jié)
- descriptor_index:常量池索引作岖,最終指向字段的描述符唆垃,見第 8 節(jié)
- attributes_count & attributes:字段屬性,為字段的附加信息痘儡,見第 8 節(jié)
6. 方法表(method_info)
方法表和字段表的設(shè)計(jì)是很相似的辕万。方法表用于描述類方法與實(shí)例方法,但只包括在本類中聲明的方法或者重寫的方法沉删,不包括父類或父接口中聲明的方法渐尿。需要注意的是,編譯器生成的方法是包括的矾瑰,例如類構(gòu)造器<clinit>()
與實(shí)例構(gòu)造器<init>()
涡戳。方法表的基本結(jié)構(gòu)如下:
可以看到,方法表和字段表的基本結(jié)構(gòu)是完全一致的脯倚,此處不再贅述渔彰。需要特別指出的是,方法里面的代碼在方法的Code屬性
推正,方法的受檢異常聲明在Exception屬性
恍涂。
7. 屬性表(attribute_info)
- 屬性相當(dāng)于字段表或方法表的附加信息
- 每一項(xiàng)屬性都是一個(gè)表,基本結(jié)構(gòu)如下圖所示:
-
attribute_name_index:常量池索引植榕,最終指向一個(gè)屬性名再沧。
Class 文件
使用屬性名來區(qū)分每一種屬性,截止到Java 12
尊残,總共有 29 種預(yù)定義屬性炒瘸。 - attribute_length:不同屬性的屬性信息是不同的,因此需要一個(gè)長(zhǎng)度表表示屬性信息占用的長(zhǎng)度
- info:屬性信息
# 你覺得呢寝衫?#
市面上你能找到的介紹虛擬機(jī)的書籍顷扩,普遍都會(huì)按順序羅列出每種屬性的信息。筆者并不是說這種方式不好慰毅,因?yàn)樽鳛闀年U述方式需要考慮到讀者可接受度 & 參考性的問題隘截。但是如果以博客的闡述方式也采用同樣地方式,豈非成為知識(shí)搬運(yùn)工?因此婶芭,我將從不同的問題域出發(fā)东臀,在每個(gè)問題域中介紹每種屬性。
Code 屬性
Exceptions 屬性
LocalVariableTable 屬性
LocalVariableTypeTable 屬性
Signature 屬性
泛型中所謂的類型擦除犀农,其實(shí)只是擦除Code 屬性
中的泛型信息惰赋,在類常量池屬性中其實(shí)還保留著泛型信息,這也是在運(yùn)行時(shí)可以反射獲取泛型信息的根本依據(jù)呵哨。在這篇文章里谤逼,我們?cè)敿?xì)討論:《Java | 關(guān)于泛型能問的都在這里了(含Kotlin)》,請(qǐng)關(guān)注仇穗!
RuntimeVisibleAnnotations 屬性
RuntimeInvisibleAnnotations 屬性
RuntimeVisibleParameterAnnotations 屬性
RuntimeInvisibleParameterAnnotations 屬性
注解在編譯后擦除,如果注解的保留級(jí)別為CLASS & RUNTIME
戚绕,在 Class 文件中還會(huì)生成對(duì)應(yīng)的注解屬性纹坐。在這篇文章里,我們?cè)敿?xì)討論:《Java | 這是一篇全面的注解使用攻略(含 Kotlin)》舞丛,請(qǐng)關(guān)注耘子!
InnerClasses 屬性
8. 信息描述規(guī)則
Editting...
參考資料
- 《深入理解Java虛擬機(jī)(第3版本)》(第6章)—— 周志明 著
- 《深入理解Android:Java虛擬機(jī) ART》(第2章) —— 鄧凡平 著
- 《深入理解 JVM 字節(jié)碼》(第2、3球切、4章)—— 張亞 著
創(chuàng)作不易谷誓,你的「三連」是丑丑最大的動(dòng)力,我們下次見吨凑!