*.class 是 *.java 編譯後的產物,我們來用一個最小的例子看一下class的文件結構
源代碼
package sample.asm.readclass;
public class Test {
private String name;
}
反編譯
class文件結構(The Java Virtual Machine Specification Java SE 7 Edition)
注意,比如cp_info,cp_info表示常量池,上圖中用 constant_pool[constant_pool_count-1]的方式來表示常量池有constant_pool_count-1個常量,它 這裏是採用數組的表現形式,但是大家不要誤以爲所有的常量池的常量長度都是一樣的,其實這個地方只是爲了方便描述採用了數組的方式,但是這裏並不像編程語 言那裏,一個int型的數組,每個int長度都一樣。
(1)魔數:CAFEBABE
(2)minor_version:0000
(3)major_version:0034
(4)常量池的數量000F (十進制:15)
常量池的數量是constant_pool_count-1,爲什麼減一,是因爲索引0表示class中的數據項不引用任何常量池中的常 量。
(5)常量池
每個常量都有一個u1類型的tag標識來表示 常量的類型,第一個常量的類型是0x0A,轉換成二級制是10,有上面的關於常量類型的描述可知tag爲10的常量是Constant_Methodref_info
tag | class_index | name_and_type_index |
---|---|---|
0A | 0003 | 000C |
--- | #3(#14 java/lang/Object) | #12(#6#7 <init>()V) |
(6)u2 access_flags 0021
表示類或者接口方面的訪問信息,比如Class表示的是類還是接口,是否爲public,static,final等。0x0021=0x0001|0x0020 也即ACC_PUBLIC 和 ACC_SUPER爲真,其中ACC_PUBLIC大家好理解,ACC
_SUPER是jdk1.2之後編譯的類都會帶有的標誌。
(7)u2 this_class 0002
表示類的索引值,用來表示類的全限定名稱 sample/asm/readclass/Test
(8)u2 super_class:0003
表示當前類的父類的索引值 java/lang/Object
(9)interfaces_count和 interfaces[interfaces_count]表示接口數量以及具體的每一個接口
(10)fields_count 和 field_info
fields_count:1 (0001)
field_info:0002 0004 0005 0000
access_flag | name_index | descriptor_index | attribute_count |
---|---|---|---|
0002 | 0004 | 0005 | 0000 |
--- | name | Ljava/lang/String; | 0 |
(11)methods_count 和 method_info:
access_flag | name_index | descriptor_index | attribute_count | attribute_info |
---|---|---|---|---|
0001 | 0006 | 0007 | 0001 | --- |
--- | <init> | ()V | 1 | --- |
接下來是attribute_info
0008對應的常量池中的常量爲Code,表示的方法的Code屬性,所以到這裏大家應該明白方法的那些代碼是存儲在Class文件方法表中的屬性表中的Code屬性中。接下來我們在分析一下Code屬性,Code屬性的結構如下
attribute_name_index | attribute_length | max_stack | max_locals | code_length | code[code_length] |
---|---|---|---|---|---|
0008 | 0000001D | 0001 | 0001 | 00000005 | 2AB70001B1 |
exception_table_length | attribute_count | attribute_info |
---|---|---|
0000 | 0001 | --- |
0009,它表示常量池中屬性的名稱,查看常量池得知第9個常量的值爲 LineNumberTable,LineNumberTable用於描述java源代碼的行號和字節碼行號的對應關係,它不是運行時必需的屬性,如果通 過-g:none的編譯器參數來取消生成這項信息的話,最大的影響就是異常發生的時候,堆棧中不能顯示出出錯的行號,調試的時候也不能按照源代碼來設置斷點,接下來我們再看一下LineNumberTable的結構如下圖所示:
attribute_name_index | attribute_length | 字節流 |
---|---|---|
0009 | 00000006 | 0001(有1個行號表) 0000(字節碼行號0) 0003(源碼行號3) |
Class文件的屬性
屬性數量:0001
attribute_name_index | attribute_length | sourcefile_index |
---|---|---|
000A | 00000002 | 000B |
SourceFile | 00000002 | Test.java |