日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

java主类与源代码名称_Java高级编程基础:类文件结构解析,看穿Class代码背后的秘密...

發(fā)布時間:2025/3/19 java 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java主类与源代码名称_Java高级编程基础:类文件结构解析,看穿Class代码背后的秘密... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

類文件結(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。