下面我們接著為訪問標(biāo)志及舍,類索引未辆,父類索引,接口索引集合击纬,字段集合鼎姐,方法表集合
訪問標(biāo)志:
常量池結(jié)束后緊接著的兩個(gè)字節(jié)代表訪問標(biāo)志,用來標(biāo)識一些類或接口的訪問信息更振,包括:這個(gè)Class是類還是接口炕桨;是否定義為public;是否定義為abstract肯腕;如果是類的話献宫,是否被聲明為final等。具體的標(biāo)志位以及含義如下表:
標(biāo)志名稱 | 標(biāo)志值 | 含義 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否是public |
ACC_FINAL | 0x0010 | 是否被聲明為final实撒,只有類可以設(shè)置 |
ACC_SUPER | 0x0020 | 是否允許使用invokespecial字節(jié)碼指令的新語義姊途,JDK1.0.2之后編譯出來的類的這個(gè)標(biāo)志默認(rèn)為真 |
ACC_INTERFACE | 0x0200 | 標(biāo)識是一個(gè)接口 |
ACC_ABSTRACT | 0x0400 | 是否是abstract,對于接口和抽象類來說為真知态,其他類都為假 |
ACC_SYNITHETIC | 0x1000 | 標(biāo)識這個(gè)類并非由用戶代碼產(chǎn)生 |
ACC_ANNOTATION | 0x2000 | 標(biāo)識這是一個(gè)注解 |
ACC_ENUM | 0x4000 | 標(biāo)識這是一個(gè)枚舉類 |
類索引(2個(gè)字節(jié))捷兰、父類索引(2個(gè)字節(jié))與接口索引(2個(gè)字節(jié)接口數(shù)+接口)集合:
在訪問標(biāo)志access_flags后接下來就是類索引(this_class)和父類索引(super_class),這兩個(gè)數(shù)據(jù)都是u2類型的负敏,而接下來的接口索引集合是一個(gè)u2類型的集合贡茅,class文件由這三個(gè)數(shù)據(jù)項(xiàng)來確定類的繼承關(guān)系。由于Java中是單繼承其做,所以父類索引只有一個(gè)顶考;但Java類可以實(shí)現(xiàn)多個(gè)接口,所以接口索引是一個(gè)集合妖泄。
類索引用來確定這個(gè)類的全限定名驹沿,這個(gè)全限定名就是說一個(gè)類的類名包含所有的包名,然后使用"/"代替"."蹈胡。比如Object的全限定名是java.lang.Object渊季。父類索引確定這個(gè)類的父類的全限定名朋蔫,除了Object之外,所有的類都有父類梭域,所以除了Object之外所有類的父類索引都不為0.接口索引集合存儲了implements語句后面按照從左到右的順序的接口斑举。
類索引和父類索引都是一個(gè)索引搅轿,這個(gè)索引指向常量池中的CONSTANT_Class_info類型的常量病涨。然后再CONSTANT_Class_info常量中的索引就可以找到常量池中類型為CONSTANT_Utf8_info的常量,而這個(gè)常量保存著類的全限定名璧坟。
字段表集合:
字段表用來描述接口或類中聲明的變量既穆。字段包括類級變量和實(shí)例級變量,但不包括方法內(nèi)變量雀鹃。所謂的類級變量就是靜態(tài)變量幻工,這個(gè)變量不屬于這個(gè)類的任何實(shí)例,可以不用定義類實(shí)例就可以使用黎茎;實(shí)例級變量不是靜態(tài)變量囊颅,是和類實(shí)例相關(guān)聯(lián)的,需要定義類實(shí)例才能使用傅瞻。
那么踢代,聲明一個(gè)變量需要哪些信息呢?
有:字段的作用域(public嗅骄、private和protected修飾符)胳挎、是實(shí)例變量還是類變量(static修飾符)、可變性(final修飾符)溺森、并發(fā)可見性(volatile修飾符)慕爬、是否可被序列化(transient修飾符)、字段的數(shù)據(jù)類型(基本類型屏积、對象医窿、數(shù)組)以及字段名稱。包含的信息有點(diǎn)多炊林,不過不需要的可以不寫姥卢。這些信息中,各個(gè)修飾符可以用布爾值表示铛铁。而字段叫什么名字隔显、字段被定義為什么類型數(shù)據(jù)都是無法固定的,只能用常量池中的常量來表示饵逐。
下面是字段表的格式:
類型 | 名稱 | 數(shù)量 |
---|---|---|
U2 | access_flags | 1 |
U2 | name_index | 1 |
U2 | descriptor_index | 1 |
U2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
其中的字段修飾符access_flags括眠,和類中的access_flags類似,對于字段來說可以設(shè)置的標(biāo)志位及含義如下:
標(biāo)志名稱 | 標(biāo)志值 | 含義 |
---|---|---|
ACC_PUBLIC | 0x0001 | 字段是否是public |
ACC_PRIVATE | 0x0002 | 字段是否是private |
ACC_PROTECTED | 0x0004 | 字段是否是protected |
ACC_STATIC | 0x0008 | 字段是否是static |
ACC_FINAL | 0x0010 | 字段是否是final |
ACC_VOLATILE | 0x0040 | 字段是否是volatile |
ACC_TRANSIENT | 0x0080 | 字段是否是transient |
ACC_SYNTHETIC | 0x1000 | 字段是否是由編譯器自動產(chǎn)生的 |
ACC_ENUM | 0x4000 | 字段是否是enum |
顯然倍权,ACC_PUBLIC掷豺、ACC_PRIVATE和ACC_PROTECTED只能選擇一個(gè)捞烟,ACC_FINAL和ACC_VOLATILE不能同時(shí)選擇。接口中的字段必須有ACC_PUBLIC当船、ACC_STATIC和ACC_FINAL標(biāo)志题画,這是Java語言本身的規(guī)則決定的。
access_flags給出了字段中所有可以用布爾值表示的修飾符德频,剩下的信息就是字段的名字苍息、變量類型等信息。access_flags后面的是name_index和descriptor_index壹置,前者是字段名的常量池索引竞思,后者是字段描述符的常量池索引。name_index可以描述字段的名字钞护,descriptor_index可以描述字段的數(shù)據(jù)類型盖喷。不過,對于方法的描述符來說就要復(fù)雜一些难咕,因?yàn)橐粋€(gè)方法除了返回值類型课梳,還有參數(shù)類型,而且參數(shù)的個(gè)數(shù)還不確定余佃。根據(jù)描述符規(guī)則暮刃,這些類型都使用一個(gè)大寫字母來表示,如下表:
標(biāo)志名稱 | 含義 | 標(biāo)志名稱 | 含義 |
---|---|---|---|
B | byte | J | long |
C | char | S | short |
D | double | Z | boolean |
F | float | V | void |
I | int | L | 對象類型咙冗,如Ljava/lang/Object |
對于數(shù)組類型沾歪,每一個(gè)維度將使用一個(gè)前置的“[”字符來描述。比如定義一個(gè)java.lang.String[][]類型的二維數(shù)組雾消,將記錄為"[[Ljava/lang/String"灾搏,一個(gè)double數(shù)組"double[]"將標(biāo)記為"[D"。
當(dāng)描述符用來描述方法時(shí)立润,按照先參數(shù)列表狂窑,后返回值的順序描述,參數(shù)列表按照參數(shù)的嚴(yán)格順序放在一組小括號"()"內(nèi)桑腮。比如方法void inc()的描述符是:()V泉哈。方法java.lang.String toString()的描述符是:()Ljava/lang/String。方法int indexOf(char[] source,int sourceOffset,int sourceCount,char[] target,int targetOffset,int targetCount,int fromIndex)的描述符是:([CII[CIII)I破讨。
descriptor_info后面是屬性信息丛晦,這會在后面屬性表集合中介紹。
方法表集合:
在字段表集合中介紹了字段的描述符和方法的描述符提陶,對于理解方法表有很大幫助烫沙。class文件存儲格式中對方法的描述和對字段的描述幾乎相同,方法表的結(jié)構(gòu)也和字段表相同隙笆,這里就不再列出锌蓄。不過升筏,方法表的訪問標(biāo)志和字段的不同,列出如下:
標(biāo)志名稱 | 標(biāo)志值 | 含義 |
---|---|---|
ACC_PUBLIC | 0x0001 | 方法是否是public |
ACC_PRIVATE | 0x0002 | 方法是否是private |
ACC_PUBLICPROTECTED | 0x0004 | 方法是否是protected |
ACC_STATIC | 0x0008 | 方法是否是static |
ACC_FINAL | 0x0010 | 方法是否是final |
ACC_SYNCHRONIZED | 0x0020 | 方法是否是synchronized |
ACC_BRIDGE | 0x0040 | 方法是否是由編譯器產(chǎn)生的橋接方法 |
ACC_VARARGS | 0x0080 | 方法是否接受不定參數(shù) |
ACC_NATIVE | 0x0100 | 方法是否是native |
ACC_ABSTRACT | 0x0400 | 方法是否是abstract |
ACC_STRICTFP | 0x0800 | 方法是否是strictfp |
ACC_SYNTHETIC | 0x1000 | 方法是否是由編譯器自動產(chǎn)生的 |
下面我們來一個(gè)例子
JavaCode
public class Test{
public int a;
private String b;
private int add(int arg1,int arg2){
return arg1+arg2;
}
}
二進(jìn)制解析
Javap解析
C:\Users\GH\Desktop>javap -verbose Test.class
Classfile /C:/Users/GH/Desktop/Test.class
Last modified 2018-8-8; size 287 bytes
MD5 checksum 430dad91948d95b5532953a32356174c
Compiled from "Test.java"
public class Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#16 // java/lang/Object."<init>":()V
#2 = Class #17 // Test
#3 = Class #18 // java/lang/Object
#4 = Utf8 a
#5 = Utf8 I
#6 = Utf8 b
#7 = Utf8 Ljava/lang/String;
#8 = Utf8 <init>
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Utf8 LineNumberTable
#12 = Utf8 add
#13 = Utf8 (II)I
#14 = Utf8 SourceFile
#15 = Utf8 Test.java
#16 = NameAndType #8:#9 // "<init>":()V
#17 = Utf8 Test
#18 = Utf8 java/lang/Object
{
public int a;
descriptor: I
flags: ACC_PUBLIC
public Test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
}
SourceFile: "Test.java"
屬性表集合:
屬性表(attribute_info)瘸爽,在Class文件您访、字段表、方法表都可以攜帶字節(jié)的屬性表集合剪决,用于描述某些場景專有信息灵汪。
屬性名稱 | 使用位置 | 含義 |
---|---|---|
Code | 方法表 | Java代碼編譯成的字節(jié)碼指令 |
ConstantValue | 字段表 | final關(guān)鍵字定義的常量池 |
Deprecated | 類,方法昼捍,字段表 | 被聲明為deprecated的方法和字段 |
Exceptions | 方法表 | 方法拋出的異常 |
EnclosingMethod | 類文件 | 僅當(dāng)一個(gè)類為局部類或者匿名類是才能擁有這個(gè)屬性识虚,這個(gè)屬性用于標(biāo)識這個(gè)類所在的外圍方法 |
InnerClass | 類文件 | 內(nèi)部類列表 |
LineNumberTable | Code屬性 | Java源碼的行號與字節(jié)碼指令的對應(yīng)關(guān)系 |
LocalVariableTable | Code屬性 | 方法的局部便狼描述 |
StackMapTable | Code屬性 | JDK1.6中新增的屬性,供新的類型檢查檢驗(yàn)器檢查和處理目標(biāo)方法的局部變量和操作數(shù)有所需要的類是否匹配 |
Signature | 類妒茬,方法表,字段表 | 用于支持泛型情況下的方法簽名 |
SourceFile | 類文件 | 記錄源文件名稱 |
SourceDebugExtension | 類文件 | 用于存儲額外的調(diào)試信息 |
Synthetic | 類蔚晨,方法表乍钻,字段表 | 標(biāo)志方法或字段為編譯器自動生成的 |
LocalVariableTypeTable | 類 | 使用特征簽名代替描述符,是為了引入泛型語法之后能描述泛型參數(shù)化類型而添加 |
RuntimeVisibleAnnotations | 類铭腕,方法表银择,字段表 | 為動態(tài)注解提供支持 |
RuntimeInvisibleAnnotations | 表,方法表累舷,字段表 | 用于指明哪些注解是運(yùn)行時(shí)不可見的 |
RuntimeVisibleParameterAnnotation | 方法表 | 作用與RuntimeVisibleAnnotations屬性類似浩考,只不過作用對象為方法 |
RuntimeInvisibleParameterAnnotation | 方法表 | 作用與RuntimeInvisibleAnnotations屬性類似,作用對象哪個(gè)為方法參數(shù) |
AnnotationDefault | 方法表 | 用于記錄注解類元素的默認(rèn)值 |
BootstrapMethods | 類文件 | 用于保存invokeddynamic指令引用的引導(dǎo)方式限定符 |
對于每個(gè)屬性被盈,它的名稱需要從常量池中引用一個(gè)CONSTANT_utf8_info類型的常量類表示析孽,而屬性值的結(jié)構(gòu)則是完全自定義的,只需要通過一個(gè)u4的長度屬性區(qū)說明屬性值所占用的位數(shù)即可.
屬性表定義的結(jié)構(gòu):
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u2 | attribute_length | 1 |
u1 | info | attribute_length |
1.CODE屬性
Java程序方法體中的代碼經(jīng)過Javac編譯處理后只怎,最終變?yōu)樽止?jié)碼指令存儲在Code屬性中.Code屬性出現(xiàn)在方法表的屬性集合中袜瞬,但是并非所有的方法表都有這個(gè)屬性.例如接口或類中的方法就不存在Code屬性了.
在字節(jié)碼指令之后的是方法的是方法的顯式異常處理表集合,異常表對于Code屬性來說并不是必須的.
結(jié)構(gòu):
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | max_stack | 1 |
u2 | max_locals | 1 |
u4 | code_length | 1 |
u1 | code | code_length |
u2 | exception_table_length | 1 |
exception_info | exception_table | exception_length |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
2.EXCEPTIONS屬性
- Exception屬性的作用是列出方法中能拋出的受查異常Check Exceptions身堡,也就是方法描述時(shí)在throws關(guān)鍵字之后列舉的異常
- Exception屬性中的number_of_exceptions項(xiàng)表示方法可能拋出的number_of_exceptions種受查異常邓尤,每一種受查異常使用一個(gè)exception_index_table項(xiàng)表示,exception_index_table是一個(gè)指向常量池中CONSTANT_Class_info型常量的索引贴谎,代表了該受查異常的類型.
結(jié)構(gòu):
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u2 | attribute_lrngth | 1 |
u2 | attribute_of_exception | 1 |
u2 | exception_index_tsble | number_of_exceptions |
3.LINENUMBERTABLE屬性
line_number_table是一個(gè)數(shù)量為line_number_table_length汞扎,類型為line_number_info的集合,line_number_info表包括了start_PC和line_number兩個(gè)u2類型的數(shù)據(jù)項(xiàng)擅这,前者是字節(jié)碼行號澈魄,后者是Java源代碼行號.
結(jié)構(gòu):
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | line_number_table_length | 1 |
line_number_info | line_number_table | line_number_table_length |
虛擬機(jī)預(yù)定義的屬性有20多個(gè),就不意一一介紹蕾哟,基本上和上述的幾個(gè)屬性差不多.