java主类与源代码名称_Java高级编程基础:类文件结构解析,看穿Class代码背后的秘密...
類文件結(jié)構(gòu)
在說完了JVM內(nèi)部結(jié)構(gòu)后,接下來我們需要說一下另外一個非常重要的基礎(chǔ)概念Class類結(jié)構(gòu)。
我們既然知道了開發(fā)的Java源代碼會首先被編譯成字節(jié)碼文件保存,JVM的類加載器會讀取這些文件內(nèi)容,然后將其轉(zhuǎn)換為Class類對象保存到JVM管理的運行數(shù)據(jù)區(qū)里。那么這個編譯后的Class類文件的結(jié)構(gòu)如何呢?我們今天來簡單說一下。
其實JVM的規(guī)范里有詳細(xì)的定義和說明,不過它涉及了很多專業(yè)的名稱和術(shù)語,還有就是說的比較簡潔,理解起來還是有些難度。
類文件結(jié)構(gòu)
簡單說來從存儲字節(jié)碼的.class文件中讀取的內(nèi)容由10個基本組成部分構(gòu)成:
簡略結(jié)構(gòu)
- magic: 0xCAFEBABE
- minor_version和major_version: 包括類文件的主次版本號
- constant_pool: 當(dāng)前類的常量池
- access_flags: 當(dāng)前類訪問標(biāo)志
- this_class: 當(dāng)前類名字
- super_class: 其父類名字
- interfaces: 當(dāng)前類實現(xiàn)的任何接口
- fields: 當(dāng)前類定義的任何字段
- methods: 當(dāng)前類包含的方法
- attributes: 類的任何屬性(例如源文件的名稱等)
一般情況下,該文件是由8位字節(jié)基本單位長度字節(jié)流構(gòu)成,對于那些16位,32位,甚至64位的它們將分別讀取2個,4個和8個基本單位來表示。
這類多字節(jié)數(shù)據(jù)項都將是以大端順序存儲的,也就是說高位字節(jié)在前。
我們在Java開發(fā)時,常用的讀寫這類數(shù)據(jù)的接口是java.io.DataInput和java.io.DataOutput,對應(yīng)的類有java.io.DataInputStream和java.io.DataOutputStream。
類文件結(jié)構(gòu)
截圖中的u1,u2,u4 它們分別表示無符號的1字節(jié),2字節(jié)和4字節(jié)類型,它們分別對應(yīng)著我們Java的基礎(chǔ)數(shù)據(jù)類型中的byte,short,int等。我們完全可以使用java.io.DataInput接口定義的readUnsignByte,readUnsignShort, readUnsignInt等讀取其值。
另外一類就是長度在加載之前是未知的,有可變長度的部分,如常量池,方法,屬性等。這些部分的組織方式是以它們的大小或長度作為前綴的。
通過這種方式,JVM在實際加載可變長度區(qū)段之前就知道它們的大小。
在Java ClassFile中不同部分的順序是嚴(yán)格定義的,這樣JVM就知道每一步應(yīng)該要加載什么,以及加載不同組件的順序。
關(guān)于魔幻數(shù)字的故事
詹姆斯·高斯林(James Gosling)曾經(jīng)解釋過這個神奇數(shù)字的歷史:
他說“我們過去常去一個叫 St Michael’s Alley的地方吃午飯。據(jù)當(dāng)?shù)貍髡f,在黑暗的過去,Grateful Dead樂隊在成名之前經(jīng)常在那里演出。這是一個非常時髦的地方,絕對是一個‘Grateful Dead Kinda Place’。Jerry死后,他們甚至建了一座佛教風(fēng)格的小神龕。當(dāng)我們過去常去那里,我們把那個地方稱為‘Cafe Dead’。
沿著這思路,有人注意到這是一個十六進制數(shù)。當(dāng)時我重新整理了一些文件格式代碼,需要幾個神奇的數(shù)字,一個用于持久對象文件,一個用于類文件。所以我就選用CAFEDEAD作為對象文件格式,并在查詢中添加了4個字符的十六進制單詞,它們與“CAFE”匹配。我意外獲得了BABE,所以就決定使用CAFEBABE。在那個時候,它似乎并不是那么重要,或者注定要去任何地方,除了歷史的垃圾桶。
因此CAFEBABE成為類文件格式,而CAFEDEAD則是持久對象格式。但持久性對象設(shè)施消失了,隨之消失的還有CAFEDEAD的使用——它最終被RMI取代。”
類文件版本
類文件的下4個字節(jié)包含主版本號和次版本號。我們的JVM會通過這組數(shù)字驗證和標(biāo)識類文件。
如果這個數(shù)量大于JVM可以裝載的數(shù)量,那么類文件將被錯誤拒絕,并提示java.lang.UnsupportedClassVersionError。
我們通常可以使用javap命令行來查看任何Java類文件的類版本。
先定義一個MyClass.java文件,然后編譯成MyClass.class
public class Main {public static void main(String [] args) {int my_integer = 0xFEEDED;}}我們使用javap命令查看:
$ javap -verbose MyClass在上面的主類上執(zhí)行javap,我們得到如下的符號表。
javap 顯示類文件內(nèi)容
關(guān)于常量池
我們知道所有的類或者接口的定義在應(yīng)用程序運行過程中都不會發(fā)生變化的,因此所有與類或接口相關(guān)的常量都將存儲在常量池中。
它們包括類名、變量名、接口名、方法名和簽名、最終變量值、字符串字面量等。
這些常量在常量池中被存儲為可變長度數(shù)組元素。
這些常量可變數(shù)組的前面是它的數(shù)組大小,因此JVM知道在加載類文件時需要多少常量。
而在每個數(shù)組元素中,第一個字節(jié)表示一個標(biāo)記位,該標(biāo)記指定數(shù)組中該位置的常量類型,JVM通過讀取這個字節(jié)的標(biāo)記來標(biāo)識常量的類型。
因此,如果這個字節(jié)標(biāo)記表示一個字符串文字,那么JVM知道接下來的兩個字節(jié)表示字符串文字的長度,而條目的其余部分是字符串文字本身。
我們同樣可以使用javap命令分析任何類文件的常量池。
常量池內(nèi)容
上圖中常量池總共有15個條目。
#1是方法public static void main;
#2用于整數(shù)值 0xFEEDED(16707053)。
#3和#4分別是我們類的this和super類。
剩余其它都是存儲字符串文本的符號表。
關(guān)于訪問標(biāo)識
它是一個2個字節(jié)的條目,用類指示文件是定義的類還是接口,如果是一個類,它是公共的、抽象的還是final的。
這些訪問標(biāo)志包括ACC_PUBLIC,ACC_FINAL,ACC_SUPER,ACC_INTERFACE,ACC_ABSTRACT,ACC_SYNTHETIC,ACC_ANNOTATION,ACC_ENUM等等。
關(guān)于this Class
this Class類是一個2個字節(jié)的條目,其內(nèi)容是指向常量池中的索引。
圖片來自網(wǎng)絡(luò)
比如在上面的圖中,this類的值0x0007是常量池中的索引。
this 所指向的對應(yīng)項Constant_pool[this_class]在常量池中有兩部分,第一部分是一個字節(jié)的標(biāo)記類表示在常量池中的條目類型。
這種情況下是類或者接口類型。在上面的圖表中,這是用橙色表示的。第二個條目部分是兩個字節(jié),同樣在常量池中有索引。
在上面的圖中,兩個字節(jié)包含值0x0004。因此它指向Constant_pool[0x0004],它的值是具有接口或類名稱的字符串文字。
關(guān)于super Class和其它集合組件
排在this Class后面那個字節(jié)就是Super Class,跟this Class類似,它是2個字節(jié)的值,也是一個指向常量池的指針,該常量池具有類的超類的條目。
接口集合
包含由該文件中定義的類或接口實現(xiàn)的所有接口。
接口部分開始的兩個字節(jié)是提供有關(guān)正在實現(xiàn)的接口總數(shù)的信息的計數(shù),緊接著是一個數(shù)組,該數(shù)組包含了被該類實現(xiàn)的接口在常量池里的索引。
字段集合
字段是類或接口的實例或類級變量或?qū)傩源鎯Φ牡胤健?/p>
字段部分只包含由文件的類或接口定義的字段,而不包含從超類或超接口繼承的字段。
它的前兩個字節(jié)表示計數(shù),是該部分中包含的所有字段數(shù),跟在其后的是每個字段的可變長度數(shù)組。數(shù)組中每個元素都表示一個字段。
有些信息存儲在這個結(jié)構(gòu)中,有些像字段名等信息存儲在常量池中。
方法集合
方法組件托管由該類顯式定義的方法,而不包括從超類繼承的任何其他方法。
同樣,其開始的前兩個字節(jié)是該類或者接口中包含的方法計數(shù),其余的是包含每個方法結(jié)構(gòu)的可變長度數(shù)組。
每個方法結(jié)構(gòu)包含了有關(guān)方法的參數(shù)列表,返回值類型,方法的局部變量所需的堆棧單詞數(shù),方法操作數(shù)堆棧所需的堆棧單詞,異常表,字節(jié)碼序列等信息。
屬性集合
屬性部分包含有關(guān)類文件的幾個屬性,比如其中一個屬性是源代碼屬性,它顯示編譯該類文件的源文件的名稱。
同樣屬性部分的前兩個字節(jié)是屬性數(shù)量的計數(shù),然后是屬性本身。jvm將忽略它們不理解的任何屬性。
總結(jié)一下
這里我簡單的參照J(rèn)VM規(guī)范說明了一下我們編寫Java源代碼后編譯成的.class文件內(nèi)容,它被JVM的類加載模塊加載后,會形成上面的數(shù)據(jù)結(jié)構(gòu),了解這個結(jié)構(gòu),主要是為以后我們在Java編程中如何去理解其處理過程,代碼優(yōu)化,性能調(diào)優(yōu),包括反射機制如何讀取等都有幫助。
本文是我的《Java高級編程基礎(chǔ)》系列的第四篇,由于Java高級編程主要是以對底層特別是Java虛擬機的理解為基礎(chǔ)的,雖然網(wǎng)上有很多專業(yè)的文章已經(jīng)針對這一塊講的非常好了,我還是想從另外一個角度去重復(fù)串聯(lián)一下它們,希望能夠為想學(xué)習(xí)Java開發(fā)的小伙伴們提供另外一個理解它們的思路和學(xué)習(xí)路線圖。期待與大家一起共同探索一條Java學(xué)習(xí)的高效之道,下面是前面幾篇相關(guān)的文章,歡迎同行評論交流!
《Java高級編程基礎(chǔ):深入理解虛擬機的共享堆內(nèi)存和方法區(qū)》
《Java高級編程基礎(chǔ):詳細(xì)解讀虛擬機底層棧幀線程模型》
《Java高級編程基礎(chǔ):可以從這個思路去理解JVM和性能調(diào)優(yōu)》
總結(jié)
以上是生活随笔為你收集整理的java主类与源代码名称_Java高级编程基础:类文件结构解析,看穿Class代码背后的秘密...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 10的多少次方 oracle_初中数学:
- 下一篇: java单链表节点翻转_单链表Java实