1. 程式人生 > 其它 >.class檔案結構解析

.class檔案結構解析

工具

我們可以使用javap -verbose,配合記事本來對照著看位元組碼檔案

.class檔案結構

class檔案通過記事本開啟,可以得到16進位制的一個檔案,其中結構如下:

魔數

4個位元組,魔數固定為cafe baby

副版本號

2個位元組,表示jdk的次版本號

主版本號

主版本號兩個位元組,假設為00 34,它的10進製為52,52代表的是1.8,51代表的是1.7,依此類推

常量池計數器

記錄常量池資料區中常量的數量

常量池資料區

常量池資料區的資料數量是常量池計數器的數量-1。例如:常量池計數器為00 19轉成10進位制即為25,這個時候常量池資料區應該有24個常量。原因是第一個常量池JVM存了null

常量池結構

u1,u2,u4,u8分別代表1個位元組,2個位元組,4個位元組,8個位元組的無符號數


常量池一般存放兩種常量,字面量和符號引用

描述資訊

在JVM規範中,每個欄位或者變數都有描述資訊,描述資訊的主要作用是 資料型別,方法引數列表,返回值型別等.
1)基本引數型別和void型別都是用一個大寫的字元來表示,物件型別是通過一個大寫L加全類
名錶示,這麼做的好處就是在保證jvm能讀懂class檔案的情況下儘量的壓縮class檔案體積.
基本資料型別表示:
B---->byte
C---->char
D---->double
F----->float
I------>int
J------>long
S------>short
Z------>boolean
V------->void
物件型別:前面加一個L,然後把.改為/
String------>Ljava/lang/String;(後面有一個分號)
對於陣列型別: 每一個唯獨都是用一個前置 [ 來表示
比如: int[] ------>[ I,
String [][]------>[[Ljava.lang.String;
用描述符來描述方法的,先引數列表,後返回值的格式,引數列表按照嚴格的順序放在()中
比如原始碼 String getUserInfoByIdAndName(int id,String name) 的方法描述符號
(I,Ljava/lang/String;)Ljava/lang/String;

訪問標識

解析我們的class檔案是類還是介面,是否定義為public的,是否是abstract,是否被final修飾

兩個訪問修飾符直接通過位運算來實現的。
例如現在有一個class檔案訪問標識為00 21。那麼實際它代表的是0x0021 = 0x0020 位運算 0x0001,即它代表這個class的訪問許可權是ACC_PUBLIC和ACC_SUPER

類索引

描述當前所屬的類,它儲存著常量池中的位置,例如00 03,就代表這個索引指著常量池中第三個常量,那麼第三個常量的名稱就是這個類的名稱。

父類索引

當前類的父類名字,同類索引,存的也是常量池中的位置。

介面計數器

計數,記錄實現了幾個介面

介面資訊計數區

會存位於常量池中的索引,同類索引和父類索引

欄位資訊

同樣的,前兩位為欄位的數量,後面就是n個欄位,它的作用是描述類和介面中宣告的變數,包括類變數和例項變數,但是不包括方法的區域性變數
欄位的結構如下:

假設有個欄位在class檔案中為00 02 00 05 00 06 00 00
所以00 02 表示訪問修飾符號為ACC_PRIVATE
所以00 05 表示的是欄位的名稱 指向的是常量池中第五個常量
所以00 06是我們的欄位的描述符: 指向的是常量池中第六個常量
00 00 表示是屬性表的個數 這裡為0表示後面是沒有屬性表集合

方法資訊

方法與欄位類似,其結構如下:

假設有個方法在class檔案中為 00 01 00 07 00 08 00 01
00 01:表示的是方法的修飾符 表示的是acc_public
00 07:表示的是方法的名稱 表示指向常量池中第7個常量,表示方法的名稱
00 08:方法的描述符號,表示指向常量池第八個常量
00 01表示有一個方法屬性的個數
注意:表示構造方法;為()V 表示的是無參無返回值

方法表中的屬性表attribute_info結構圖