前言:前段時間讀《深入java虛擬機》介紹到class文件的時候,由于理論知識較多,人總感覺疲憊不堪,就泛泛閱讀了一下。在工作中使用起來知識點知道,但是總是需要查閱各種資料。今天有時間,繼續(xù)整理常量池后面的相關(guān)知識。
源碼
public class Sample {
public String m1;
public String m2;
public Object [] arr;
public static void main(String[] args) {
Sample sample = new Sample();
sample.m1="22";
sample.arr=new Object[12];
System.out.println(sample.m1);
}
}
編譯之后的javap文件
Last modified 2023-6-2; size 708 bytes
MD5 checksum fc8bb4833223a10b68449d42080b1695
Compiled from "Sample.java"
public class com.company.jvm.Sample
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#29 // java/lang/Object."<init>":()V
#2 = Class #30 // com/company/jvm/Sample
#3 = Methodref #2.#29 // com/company/jvm/Sample."<init>":()V
#4 = String #31 // 22
#5 = Fieldref #2.#32 // com/company/jvm/Sample.m1:Ljava/lang/String;
#6 = Class #33 // java/lang/Object
#7 = Fieldref #2.#34 // com/company/jvm/Sample.arr:[Ljava/lang/Object;
#8 = Fieldref #35.#36 // java/lang/System.out:Ljava/io/PrintStream;
#9 = Methodref #37.#38 // java/io/PrintStream.println:(Ljava/lang/String;)V
#10 = Utf8 m1
#11 = Utf8 Ljava/lang/String;
#12 = Utf8 m2
#13 = Utf8 arr
#14 = Utf8 [Ljava/lang/Object;
#15 = Utf8 <init>
#16 = Utf8 ()V
#17 = Utf8 Code
#18 = Utf8 LineNumberTable
#19 = Utf8 LocalVariableTable
#20 = Utf8 this
#21 = Utf8 Lcom/company/jvm/Sample;
#22 = Utf8 main
#23 = Utf8 ([Ljava/lang/String;)V
#24 = Utf8 args
#25 = Utf8 [Ljava/lang/String;
#26 = Utf8 sample
#27 = Utf8 SourceFile
#28 = Utf8 Sample.java
#29 = NameAndType #15:#16 // "<init>":()V
#30 = Utf8 com/company/jvm/Sample
#31 = Utf8 22
#32 = NameAndType #10:#11 // m1:Ljava/lang/String;
#33 = Utf8 java/lang/Object
#34 = NameAndType #13:#14 // arr:[Ljava/lang/Object;
#35 = Class #39 // java/lang/System
#36 = NameAndType #40:#41 // out:Ljava/io/PrintStream;
#37 = Class #42 // java/io/PrintStream
#38 = NameAndType #43:#44 // println:(Ljava/lang/String;)V
#39 = Utf8 java/lang/System
#40 = Utf8 out
#41 = Utf8 Ljava/io/PrintStream;
#42 = Utf8 java/io/PrintStream
#43 = Utf8 println
#44 = Utf8 (Ljava/lang/String;)V
{
public java.lang.String m1;
descriptor: Ljava/lang/String;
flags: ACC_PUBLIC
public java.lang.String m2;
descriptor: Ljava/lang/String;
flags: ACC_PUBLIC
public java.lang.Object[] arr;
descriptor: [Ljava/lang/Object;
flags: ACC_PUBLIC
public com.company.jvm.Sample();
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 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/company/jvm/Sample;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class com/company/jvm/Sample
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String 22
11: putfield #5 // Field m1:Ljava/lang/String;
14: aload_1
15: bipush 12
17: anewarray #6 // class java/lang/Object
20: putfield #7 // Field arr:[Ljava/lang/Object;
23: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_1
27: getfield #5 // Field m1:Ljava/lang/String;
30: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
33: return
LineNumberTable:
line 9: 0
line 10: 8
line 11: 14
line 12: 23
line 13: 33
LocalVariableTable:
Start Length Slot Name Signature
0 34 0 args [Ljava/lang/String;
8 26 1 sample Lcom/company/jvm/Sample;
}
SourceFile: "Sample.java"
1、訪問標(biāo)志
class文件中,我們可以通過背或記也好,或者通過查閱對照表??梢詫⒊A砍刂械臄?shù)據(jù)整理出來。常量池的數(shù)據(jù),之后又是什么呢?緊接著的就是訪問標(biāo)志。
標(biāo)志名稱 | 標(biāo)志值 | 標(biāo)志意義 |
---|---|---|
ACC_PUBLIC | 0X0001 | 是否為public類型 |
ACC_FINAL | 0X0010 | 是否被聲明為final,只有類可設(shè)置 |
ACC_SUPER | 0x0020 | 在jdk1.0.2之后編譯出來的類的這個標(biāo)志都為真 |
ACC_INTERFACE | 0x0200 | 標(biāo)識是一個接口 |
ACC_ABSTRACT | 0x4000 | 是否為abstract類型,如為真,其他類型均為假,如INTERFACE |
ACC_SYNTHETIC | 0x1000 | 標(biāo)識這個類并非由用戶產(chǎn)生 |
ACC_ANNOTATION | 0x2000 | 標(biāo)識這是一個注解 |
ACC_ENUM | 0x4000 | 標(biāo)識這是一個枚舉 |
ACC_MODULE | 0x8000 | 標(biāo)識這是一個模塊 |
這里先上一張二進制的圖:
下面是常量池之后的class文件截取的部分:
00 21
訪問標(biāo)志,占用空間:U2,這里占用了十六進制的4個位,則是0x0021。這里我啰嗦一下,一個字節(jié)為8個位,對應(yīng)到十六機制來說【兩個位】代表一個字節(jié)。eg:0xF標(biāo)識二進制的0000 1111,去除高位的0,就是1111。
在表格中我們說過,ACC_SUPER標(biāo)志在jdk1.0.2之后的版本其值都為真,則是0x0020,說明其訪問標(biāo)志ACC_PUBLIC為真!0x0001|0x0020=0x0021
2、類索引、父類索引、接口索引
訪問標(biāo)志結(jié)束之后,就來到了我們所聲明的類例,如下偽代碼
public class dog extend cat implement animal{
}
00 02 00 06 00 00
__類索引、父類索引、接口索引占用的內(nèi)存均為u2。
u2 | 索引 | 說明 |
---|---|---|
00 02 | #2 | 代表當(dāng)前類的索引,通過查找為com/company/jvm/Sample |
00 08 | #8 | 代表當(dāng)前父類索引,通過查找為Object |
00 00 | 0 | 代表當(dāng)前文件沒有接口 |
字段表集合
通過字面意,就能得知這里將要介紹的是類或接口成員字段。
//這里寫個偽代碼
public final static int AGE=10;
1、字段表結(jié)構(gòu)
類型 | 名稱 | 數(shù)量 | 說明 |
---|---|---|---|
u2 | filed_count | 1 | 字段數(shù)量 |
u2 | access_flag | 1 | 訪問標(biāo)志 |
u2 | name_index | 1 | 名稱索引 |
u2 | descriptor_index | 1 | 類型索引 |
u2 | attribute_count | 1 | 屬性計數(shù)器 |
u2 | attributes | attribute_count | 屬性值集合 |
2、字段表的訪問標(biāo)識
標(biāo)志名稱 | 標(biāo)志值 | 含義 |
---|---|---|
ACC_PUBLIC | 0x0001 | 字段是否為公開 |
ACC_PRIVATE | 0x0002 | 字段是否為私有 |
ACC_PROTECTED | 0x0004 | 字段是否為保護 |
ACC_STATIC | 0x0008 | 字段是否為靜態(tài) |
ACC_FINAL | 0x0010 | 字段是否為Final |
ACC_VOLATILE | 0x0040 | 字段是否在并發(fā)時可見 |
ACC_TRANSIENT | 0x0080 | 字段是否序列化 |
ACC_SYNTHETIC | 0x1000 | 字段是否由編譯器自己決定 |
ACC_ENUM | 0x4000 | 字段是否為枚舉 |
00 03 00 01 00 0a 00 0b 00 00 00 01 00 0c 00 0b 00 00 00 01 00 0d 00 0e 00 00
位值 | 說明 |
---|---|
第一個成員字段 | |
00 03 | 代表字段數(shù)量有3個 |
00 01 | 代表字段訪問標(biāo)志位public |
00 0a | 代表名稱索引為10 名稱為 m1 |
00 0b | 代表descriptor的索引值為11 對象類型為Ljava/lang/String;【分號;全限定名】 |
00 00 | 代表沒有屬性數(shù)量 |
第二個成員字段 | |
00 01 | 代表字段訪問標(biāo)志位為public |
00 0c | 代表字段名稱索引為12,名稱為m2 |
00 0b | 代表類型的索引11,類型為 Ljava/lang/String;同上 |
00 00 | 代表沒有屬性 |
第三個成員字段 | |
00 01 | 同上 |
00 0d | 名稱索引為13,經(jīng)查找為arr |
00 0e | 類型索引為14,經(jīng)查找為 [Ljava/lang/Object;全限定名,其中“[”代表為數(shù)組 |
00 00 | 代表沒有屬性,數(shù)量為0 |
方法表集合
嘮嗑時間開始,寫到這里花了三個多小時。從排版到書寫上面確實有很大的提升。此時的我確實有點疲憊。仔細一想,沒啥子疲憊不疲憊的,路雖遠,但始終在路上,總會到達終點。突然想到書上說過這樣的一句:當(dāng)你在解決一個問題的時候,你會感到很疲憊,這時候千萬別放棄。因為大部分的人就此放棄了,而你還在路上行走。當(dāng)你解決之后,你又比別人強了不少!
寫到這里【字段表集合】之后,這里就會很輕松。這里再啰嗦一下,字段表分為:成員字段數(shù)量、字段名索引、字段類型索引、字段屬性數(shù)量、字段屬性集合。
1、方法表結(jié)構(gòu)
類型 | 名稱 | 數(shù)量 | 說明 |
---|---|---|---|
method_count | 方法數(shù)量 | ||
u2 | access_flag | 1 | 訪問標(biāo)志 |
u2 | name_index | 1 | 名稱索引 |
u2 | descriptor_index | 1 | 類型索引 |
u2 | attributes_count | 1 | 屬性數(shù)量 |
attributes_count | attributes_[attributes_count] | 1 | 屬性表 |
2、方法表的訪問標(biāo)識
標(biāo)志名稱 | 標(biāo)志值 | 含義 |
---|---|---|
ACC_PUBLIC | 0x0001 | 方法是否為公開 |
ACC_PRIVATE | 0x0002 | 方法是否為私有 |
ACC_PROTECTED | 0x0004 | 方法是否為保護 |
ACC_STATIC | 0x0008 | 方法是否為靜態(tài) |
ACC_FINAL | 0x0010 | 方法是否為Final |
ACC_SYNCHRONIZED | 0x0020 | 方法是否在并發(fā)時可見 |
ACC_BRIDGE | 0x0040 | 方法是不是由編譯器產(chǎn)生的橋接方法 |
ACC_VARCHAR | 0x0080 | 方法是否接收不可定參數(shù) |
ACC_NATIVE | 0x0100 | 方法是否為Native |
ACC_ABSTRACT | 0x0400 | 方法是否為abstract |
ACC_STRICT | 0x0800 | 方法是否為strictfp【修飾在接口和類,對精確率類型較高且跨平臺的計算結(jié)果要求比較嚴格的清醒的話,建議使用該strictfp關(guān)鍵詞?!?/td> |
ACC_SYNTHETIC | 0x1000 | 方法是否由編譯器自動產(chǎn)生 |
code屬性表
類型 | 名稱 | 數(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_table_length |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
繼續(xù)上方法表的字節(jié)碼:
00 02 00 01 00 0f 00 10 00 01 00 11 00 00 00 2f 00 00 00 01 00 00 00 05 2a b7 00 01 b1 00 00
現(xiàn)在對照上面的說明,現(xiàn)在逐一說明:
對照 | 位碼 | 說明 |
---|---|---|
methods_count | 00 02 | 存在2個方法 |
acc_flags | 00 01 | 訪問標(biāo)識 public |
name_index | 00 0f | 方法名索引為15,經(jīng)查為< init > |
descriptor_index | 00 10 | 方法描述索引為16,經(jīng)常為()v |
attributes_count | 00 01 | 屬性只有一個 |
attributes_name_index | 00 11 | 屬性名索引為17,經(jīng)查為code |
attributes_length | 00 00 00 2f | 屬性值長度為47 |
max_stack | 00 00 | 最大棧深為0 |
max_locals | 00 01 | 需要分配1個變量槽,根據(jù)同時生存的最大局部變量數(shù)和類型計算 |
code_length | 00 00 00 05 | 字節(jié)碼長度 |
code | 2a | 查看指令表為aload_0 |
code | b7 | 同上~,invokespecial,調(diào)用超類構(gòu)造方法,實例初始化方法,私有方法 |
code | 00 | 同上~,nop,什么都不做 |
code | 01 | 同上~,acoust_null,將null推送至棧頂 |
code | b1 | 同上~,return ,從當(dāng)前方法返回void |
exception_table_lenght | 00 00 | 當(dāng)前沒有發(fā)現(xiàn)異常信息 |
attributes_count | 00 02 | 該方法的附加屬性共有2個 |
attribute_name_index | 0012 | 屬性名索引為18,經(jīng)查為 LineNumberTable |
attribute_length | 00 00 00 06 | 屬性長度為6 |
line_number_table_length | 00 01 | 字節(jié)碼行號共1行 |
star_pc | 00 00 | 從字節(jié)碼第0行開始。此處說的行數(shù)是一種抽象的,指的是相對于方法體的偏移 |
line_number | 00 03java行號為3 | |
attribute_name_index | 00 13 | 屬性名稱索引為19,經(jīng)查為 LocalVariableTable |
attribute_length | 00 00 00 0c | 屬性長度為12 |
local_variable_table_length | 00 01 | 局部變量表長度為1 |
star_pc | 00 00 | 局部變量的生命周期開始的字節(jié)碼偏移量 |
length | 00 05 | 往后偏移5個地址的長度,star_pc和length的配合使用就是局部變量在字節(jié)碼中的作用域范圍 |
name_index | 00 14 | 名字索引為20,經(jīng)查為 this |
descriptor_index | 00 15 | 描述索引為21,經(jīng)查為Lcom/company/jvm/Sample; |
index | 00 00 | 這個局部變量在棧幀的局部變量表中變量槽的為之為0 |
acc_flag | 00 09 | public的標(biāo)志0x0001,static的標(biāo)志0x0008,0x0001 |
name_index | 00 16 | 名字為索引22,經(jīng)查為 main |
descriptor_index | 00 17 | 描述的索引為23,經(jīng)查([Ljava/lang/String;)V |
attributes_count | 00 01 | 當(dāng)前方法的屬性長度為1 |
attribute_name_index | 00 11 | 當(dāng)前屬性名稱索引值為17,經(jīng)查為Code |
attribute_length | 00 00 00 66 | 當(dāng)前屬性的長度為102 |
attribute_name_index | 00 02 | 屬性名稱索引為2,經(jīng)查為com/company/jvm/Sample |
max_stack | 00 02 | 棧幀最大深度為2 |
max_local | 00 02 | 最大局部變量槽數(shù)為2 |
code_lenght | 00 00 00 22 | 字節(jié)碼長度為34 |
code | bb | 經(jīng)查字節(jié)碼指令,0xbb為new |
code | 00 | Nop,什么事都不做 |
code | 02 | 同上經(jīng)查,為將int型-1推送至棧頂 |
code | 59 | 0x59,經(jīng)查為dump,賦值棧頂數(shù)值并壓入棧頂 |
code | b7 | 0xb7,invokespecial,調(diào)用超類構(gòu)造方法,實例化初始方法,私有方法 |
code | 00 | Nop 不做任何事 |
code | 03 | 0x03,將int的0推送至棧頂 |
code | 0x27e | 一直都這個位都是code值 |
exception_table_lenght | 00 00 | 說明沒有任何異常信息 |
attributes_count | 00 02 | 說明有兩條 |
同上面分析~ | 同上面分析~ | 同上面分析~ |
到最后8位為Source文章來源:http://www.zghlxwxcb.cn/news/detail-469025.html
標(biāo)識 | 位碼 | 說明 |
---|---|---|
attribute_name_index | 00 1b | 名稱索引為27,經(jīng)查為SourceFile |
attribute_length | 00 00 00 02 | 對應(yīng)值為2 |
sourcefile_index | 00 1c | 對應(yīng)的文件索引值為28, 經(jīng)查為Sample.java |
至此一個簡單的文件就翻譯完成了,對照著javap和字節(jié)碼整理之后,確實有一番收獲。但是還處于道可道非常道的過程,仍然需要透徹一些!明天繼續(xù)干~文章來源地址http://www.zghlxwxcb.cn/news/detail-469025.html
到了這里,關(guān)于class文件中,常量池之后的相關(guān)數(shù)據(jù)解析!【class二進制文件分析】的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!