Java在誕生時(shí)就以一次編寫赞警,到處運(yùn)行特點(diǎn)在各個(gè)平臺(tái)都可以進(jìn)行運(yùn)行。其實(shí)就是通過不同的編譯器(Javac編譯器,jrubyc編譯器,groovyc編譯器等等)將代碼編譯成規(guī)范的class文件虏两,虛擬機(jī)只要接收到claas文件而并不關(guān)心是class文件時(shí)哪一種編譯器編譯的愧旦,這樣就到達(dá)了(write one,run anywhere)定罢。所以要想更好的了解虛擬機(jī)笤虫,下面我們走進(jìn)class文件中!祖凫!
Class文件是一組以8bit為基礎(chǔ)單位的二進(jìn)制流琼蚯,各個(gè)數(shù)據(jù)項(xiàng)目嚴(yán)格按順序緊湊地排列在class文件中,中間無任何添割符惠况。Class文件格式采用一種類似于C語(yǔ)言結(jié)構(gòu)體的偽結(jié)構(gòu)來儲(chǔ)存數(shù)據(jù)遭庶,這種偽結(jié)構(gòu)只有兩種數(shù)據(jù):無符號(hào)數(shù)和表。
無符號(hào)數(shù)屬于基本的數(shù)據(jù)類型稠屠,以u(píng)1,u2,u3,u4,u8表示1個(gè)字節(jié),2個(gè)字節(jié),3個(gè)字節(jié),4個(gè)字節(jié),8個(gè)字節(jié)峦睡,無符號(hào)數(shù)可以用來描述數(shù)字,索引引用,數(shù)量值或者按照UTF-8編碼構(gòu)成字符串。
表是由多個(gè)無符號(hào)數(shù)或者其它表作為數(shù)據(jù)項(xiàng)構(gòu)成的復(fù)合數(shù)據(jù)類型权埠,所有表都習(xí)慣地以"_info"結(jié)尾榨了。表用于描述由層次關(guān)系的復(fù)合結(jié)構(gòu)的數(shù)據(jù),整個(gè)class文件本質(zhì)上就是一張表攘蔽。
class文件格式
類型 | 名稱 | 數(shù)量 |
---|---|---|
u4 | magic(魔數(shù)) | 1 |
u2 | minor_version(JDK次版本號(hào)) | 1 |
u2 | major_version(JDK主版本號(hào)) | 1 |
u2 | constant_pool_count(常量池?cái)?shù)量) | 1 |
cp_info | constan_pool(常量表) | constant_pool_count-1 |
u2 | access_flags(訪問標(biāo)志) | 1 |
u2 | this_class(類引用) | 1 |
u2 | super_class(父類引用) | 1 |
u2 | interfaces_count(接口數(shù)量) | 1 |
u2 | interfaces(接口數(shù)組) | interfaces_count |
u2 | fields_count(字段數(shù)量) | 1 |
field_info | fields(屬性表) | fields_count |
u2 | methods_count(方法數(shù)量) | 1 |
method_info | methods(方法表) | methods_count |
u2 | attributes_count(屬性數(shù)量) | 1 |
attribute_info | attributes(屬性表) | attributes_count |
由于方便龙屉,先講概念,最后翻譯一個(gè)class文件的形式進(jìn)行講解
一:魔數(shù):每個(gè)class文件的頭4個(gè)字節(jié)稱為魔數(shù)满俗,它的唯一作用就是確定這個(gè)文件是否為一個(gè)能被虛擬機(jī)接收的class文件转捕。值為CAFEBABE,緊接著就是4個(gè)字節(jié)的版本號(hào),其中前兩個(gè)為次版本號(hào)漫雷,后兩個(gè)為主版本號(hào)瓜富。到目前前8個(gè)字節(jié)就確定了。
二:版本號(hào):一個(gè)4個(gè)字節(jié)降盹,前兩字節(jié)表示次版本號(hào)与柑,后兩字節(jié)表示主版本號(hào)。
三:常量池:緊接著主次版本號(hào)就是常量池了蓄坏,第一個(gè)是常量池?cái)?shù)量(占兩個(gè)字節(jié))价捧,接下來就是常量池表,我以表的方式展現(xiàn)涡戳。
常量池表結(jié)構(gòu)
類型 | 名稱 | 數(shù)量 |
---|---|---|
u1 | tag(常量池的項(xiàng)目類型號(hào)) | 1 |
u2 | name_index(索引) | 1 |
那么tag對(duì)應(yīng)的就是常量池的項(xiàng)目類型结蟋,下面我們來看看有哪些
讀取常量池的時(shí)候首先讀取標(biāo)志位,判斷常量類型渔彰,就可以知道對(duì)應(yīng)的結(jié)構(gòu)嵌屎,獲取對(duì)應(yīng)的信息了推正。
下面我們來一個(gè)簡(jiǎn)單的代碼,并編譯成class文件宝惰,以二進(jìn)制形式打開植榕,人為來解析一下。
Java代碼
public class Test{
}
class文件
使用javap指令反編譯
C:\Users\GH\Desktop>javap -verbose Test.class
Classfile /C:/Users/GH/Desktop/Test.class
Last modified 2018-8-8; size 182 bytes
MD5 checksum f22f52551c287057ed6d62d392d5647e
Compiled from "Test.java"
public class Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#10 // java/lang/Object."<init>":()V
#2 = Class #11 // Test
#3 = Class #12 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 SourceFile
#9 = Utf8 Test.java
#10 = NameAndType #4:#5 // "<init>":()V
#11 = Utf8 Test
#12 = Utf8 java/lang/Object
{
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"
參考:
https://blog.csdn.net/weixin_40234548/article/details/81507125