前言:為什麼要學習類文件結構呢?
有些人說了類文件結構我們知道或者不知道沒有什麼作用,如果你持有這種觀點,我覺得需要矯正一下,類文件結構是沒有多大的作用,但是它是我們代碼和java虛擬機之間的橋梁,如果研究透了類文件結構,那麼會對我們研究虛擬機的運行起到至關重要的作用,同樣對於我們研究jvm內存調優能夠帶來很大的幫助。
定義: Class文件是一組以8個字節為基礎的單位的二進位流。
項目嚴格按照順序緊湊的排列在文件中,沒有添加任何分隔符,這使得整個Class文件中的存儲的內容幾乎全部是程序運行的必要數據,沒有空隙存在。
數據類型: 無符號數和表
通過以上定義,我們可知,其實我們編寫的java文件,經過javac編譯之後的class文件會以一種二進位流的方式存在,這樣就方便java虛擬機來讀取我們編寫的java文件。
也就是說class文件只是一種中介,但是這種中介的格式或者順序都是被嚴格限定的,那麼,我們的java文件變成class文件之後,會變成一種什麼樣的數據結構呢?下面我們通過一張圖來看看。
通過這張圖,我們可以從宏觀的意義上了解了一個class文件具體是怎樣組成的,那麼,下面我們分別看看,每一個組成的部分又代表了什麼?
1 魔數和JDK版本
這個不是我們代碼中已經有的,而是java虛擬機為了兼容所有的語言運行的一種設計方式,比如 我們的java版本的魔數為0xCAFEBABE,他就是固定代表著java語言,如果改了就是不行,至於JDK的版本號,裡面包括大版本號和小版本號,這樣能夠檢測我們的JDK是否支持這個class文件。
2 常量池
常量池是一種表類型的數據項目,如類級別的字面量,final String a ="123",另外還包含符號引用,比如包名,類的全名,欄位的名稱和描述,方法的名稱和描述。
常量池可以存放22種數據類型,但是第0項被設計者作為特殊的使用,所以常量池中只能有21項常量,目前設計者已經使用了17種數據類型的常量。如CONSTANT_Utf8_info代表utf-8編碼的字符串,CONSTANT_Integer_info 代表整型字面量等。
3 訪問標誌
訪問標誌用於識別一些類或者接口的訪問信息,包括這個class是類還是接口,是否被定義為public類,是否定義為abstract類型,如果是類的話,是否被聲明final。
4 類索引、父類索引和接口索引集合
這個顧名思義就是表示類的全名,父類的全名以及接口集合的全名
5 欄位表的集合
即不包括方法內部聲明的局部變量的欄位是否為public 、private、protected、static、final等。
對於數組類型,每一維將使用一個前置的 [ 字符來描述。如一個整型數組int[] 將被記錄為[I
6 方法表的集合
方法表的集合基本上和欄位表的集合一樣,描述方法是否為ublic 、private、protected、static等屬性,但是多了synchronized等屬性。
寫到這裡,大家就開始有疑問了,方法裡的java代碼跑哪裡去了呢? 這個就要說說屬性表集合了。
從使用位置來說,屬性表是一個比較雜亂的集合,包括方法表、類以及欄位表中都使用了屬性表。
比如方法表中的Code屬性,它是java編譯成的字節碼指令,如iadd等表示相加的功能,值得注意的是這些都是在棧中操作的指令,因而這種被歸屬於方法表的集合,再比如LineNumberTable屬於code屬性,其代表的含義為java源碼與字節碼指令的對應關係,這個就可以解釋為什麼異常能夠發現哪一行報錯了,再比如ContantValue屬於欄位表,他代表由final修飾的常量值。
通過以上探討,我們從宏觀上總結了java的類文件結構,相信看過的朋友能夠對類文件有一個大致的了解,進而對比如字節碼指令等問題能夠進行更深層次的探討。