JavaClass文件是什么-創(chuàng)新互聯(lián)

本篇內(nèi)容主要講解“Java Class文件是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Java Class文件是什么”吧!

畢節(jié)網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)建站,畢節(jié)網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為畢節(jié)1000多家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)網(wǎng)站建設(shè)要多少錢,請找那個售后服務(wù)好的畢節(jié)做網(wǎng)站的公司定做!

Class文件

什么是Class文件?

java之所以能夠?qū)崿F(xiàn)跨平臺,便在于其編譯階段不是將代碼直接編譯為平臺相關(guān)的機(jī)器語言,而是先編譯成二進(jìn)制形式的java字節(jié)碼,放在Class文件之中,虛擬機(jī)再加載Class文件,解析出程序運(yùn)行所需的內(nèi)容。每個類都會被編譯成一個單獨(dú)的class文件,內(nèi)部類也會作為一個獨(dú)立的類,生成自己的class。

基本結(jié)構(gòu)

隨便找到一個class文件,用Sublime Text打開是這樣的:

是不是一臉懵逼,不過java虛擬機(jī)規(guī)范中給出了class文件的基本格式,只要按照這個格式去解析就可以了:

ClassFile {u4 magic;u2 minor_version;u2 major_version;u2 constant_pool_count;cp_info constant_pool[constant_pool_count-1];u2 access_flags;u2 this_class;u2 super_class;u2 interfaces_count;u2 interfaces[interfaces_count];u2 fields_count;field_info fields[fields_count];u2 methods_count;method_info methods[methods_count];u2 attributes_count;attribute_info attributes[attributes_count];}

ClassFile中的字段類型有u1、u2、u4,這是什么類型呢?其實(shí)很簡單,就是分別表示1個字節(jié),2個字節(jié)和4個字節(jié)。

開頭四個字節(jié)為:Magic,是用來標(biāo)識文件格式的,一般被稱作magic number(魔數(shù)),這樣虛擬機(jī)才能識別出所加載的文件是否是class格式,class文件的魔數(shù)為cafebabe。不只是class文件,基本上大部分文件都有魔數(shù),用來標(biāo)識自己的格式。

接下來的部分主要是class文件的一些信息,如常量池、類訪問標(biāo)志、父類、接口信息、字段、方法等。

解析

字段類型

上面說到ClassFile中的字段類型有u1、u2、u4,分別表示1個字節(jié),2個字節(jié)和4個字節(jié)的無符號整數(shù)。java中short、int、long分別為2、4、8個字節(jié)的有符號整數(shù),去掉符號位,剛好可以用來表示u1、u2、u4。

public class U1 {public static short read(InputStream inputStream) {byte[] bytes = new byte[1];try {inputStream.read(bytes);} catch (IOException e) {e.printStackTrace();}short value = (short) (bytes[0] & 0xFF);return value;}}public class U2 {public static int read(InputStream inputStream) {byte[] bytes = new byte[2];try {inputStream.read(bytes);} catch (IOException e) {e.printStackTrace();}int num = 0;for (int i= 0; i < bytes.length; i++) {num <<= 8;num |= (bytes[i] & 0xff);}return num;}}public class U4 {public static long read(InputStream inputStream) {byte[] bytes = new byte[4];try {inputStream.read(bytes);} catch (IOException e) {e.printStackTrace();}long num = 0;for (int i= 0; i < bytes.length; i++) {num <<= 8;num |= (bytes[i] & 0xff);}return num;}}

常量池

定義好字段類型后,我們就可以讀取class文件了,首先是讀取魔數(shù)之類的基本信息,這部分很簡單:

FileInputStream inputStream = new FileInputStream(file);ClassFile classFile = new ClassFile();classFile.magic = U4.read(inputStream);classFile.minorVersion = U2.read(inputStream);classFile.majorVersion = U2.read(inputStream);

這部分只是熱熱身,接下來的大頭在于常量池。解析常量池之前,我們先來解釋一下常量池是什么。

常量池,顧名思義,存放常量的資源池,這里的常量指的是字面量和符號引用。字面量指的是一些字符串資源,而符號引用分為三類:類符號引用、方法符號引用和字段符號引用。通過將資源放在常量池中,其他項(xiàng)就可以直接定義成常量池中的索引了,避免了空間的浪費(fèi),不只是class文件,Android可執(zhí)行文件dex也是同樣如此,將字符串資源等放在DexData中,其他項(xiàng)通過索引定位資源。java虛擬機(jī)規(guī)范給出了常量池中每一項(xiàng)的格式:

cp_info {u1 tag;u1 info[]; }

上面的這個格式只是一個通用格式,常量池中真正包含的數(shù)據(jù)有14種格式,每種格式的tag值不同,具體如下所示:

由于格式太多,文章中只挑選一部分講解:

這里首先讀取常量池的大小,初始化常量池:

//解析常量池int constant_pool_count = U2.read(inputStream);ConstantPool constantPool = new ConstantPool(constant_pool_count);constantPool.read(inputStream);

接下來再逐個讀取每項(xiàng)內(nèi)容,并存儲到數(shù)組cpInfo中,這里需要注意的是,cpInfo[]下標(biāo)從1開始,0無效,且真正的常量池大小為constant_pool_count-1。

public class ConstantPool {public int constant_pool_count;public ConstantInfo[] cpInfo;public ConstantPool(int count) {constant_pool_count = count;cpInfo = new ConstantInfo[constant_pool_count];}public void read(InputStream inputStream) {for (int i = 1; i < constant_pool_count; i++) {short tag = U1.read(inputStream);ConstantInfo constantInfo = ConstantInfo.getConstantInfo(tag);constantInfo.read(inputStream);cpInfo[i] = constantInfo;if (tag == ConstantInfo.CONSTANT_Double || tag == ConstantInfo.CONSTANT_Long) {i++;}}}}

我們先來看看CONSTANT_Utf8格式,這一項(xiàng)里面存放的是MUTF-8編碼的字符串:

CONSTANT_Utf8_info { u1 tag;u2 length;u1 bytes[length]; }

那么如何讀取這一項(xiàng)呢?

public class ConstantUtf8 extends ConstantInfo {public String value;@Overridepublic void read(InputStream inputStream) {int length = U2.read(inputStream);byte[] bytes = new byte[length];try {inputStream.read(bytes);} catch (IOException e) {e.printStackTrace();}try {value = readUtf8(bytes);} catch (UTFDataFormatException e) {e.printStackTrace();}}private String readUtf8(byte[] bytearr) throws UTFDataFormatException {//copy from java.io.DataInputStream.readUTF()}}

很簡單,首先讀取這一項(xiàng)的字節(jié)數(shù)組長度,接著調(diào)用readUtf8(),將字節(jié)數(shù)組轉(zhuǎn)化為String字符串。

再來看看CONSTANT_Class這一項(xiàng),這一項(xiàng)存儲的是類或者接口的符號引用:

CONSTANT_Class_info {u1 tag;u2 name_index;}

注意這里的name_index并不是直接的字符串,而是指向常量池中cpInfo數(shù)組的name_index項(xiàng),且cpInfo[name_index]一定是CONSTANT_Utf8格式。

public class ConstantClass extends ConstantInfo {public int nameIndex;@Overridepublic void read(InputStream inputStream) {nameIndex = U2.read(inputStream);}}

常量池解析完畢后,就可以供后面的數(shù)據(jù)使用了,比方說ClassFile中的this_class指向的就是常量池中格式為CONSTANT_Class的某一項(xiàng),那么我們就可以讀取出類名:

int classIndex = U2.read(inputStream);ConstantClass clazz = (ConstantClass) constantPool.cpInfo[classIndex];ConstantUtf8 className = (ConstantUtf8) constantPool.cpInfo[clazz.nameIndex];classFile.className = className.value;System.out.print("classname:" + classFile.className + "\n");

字節(jié)碼指令

解析常量池之后還需要接著解析一些類信息,如父類、接口類、字段等,但是相信大家好奇的還是java指令的存儲,大家都知道,我們平時(shí)寫的java代碼會被編譯成java字節(jié)碼,那么這些字節(jié)碼到底存儲在哪呢?別急,講解指令之前,我們先來了解下ClassFile中的method_info,其格式如下:

method_info {u2 access_flags;u2 name_index;u2 descriptor_index;u2 attributes_count;attribute_info attributes[attributes_count];}

method_info里主要是一些方法信息:如訪問標(biāo)志、方法名索引、方法描述符索引及屬性數(shù)組。這里要強(qiáng)調(diào)的是屬性數(shù)組,因?yàn)樽止?jié)碼指令就存儲在這個屬性數(shù)組里。屬性有很多種,比如說異常表就是一個屬性,而存儲字節(jié)碼指令的屬性為CODE屬性,看這名字也知道是用來存儲代碼的了。屬性的通用格式為:

attribute_info {u2 attribute_name_index;u4 attribute_length;u1 info[attribute_length];}

根據(jù)attribute_name_index可以從常量池中拿到屬性名,再根據(jù)屬性名就可以判斷屬性種類了。

Code屬性的具體格式為:

Code_attribute {u2 attribute_name_index; u4 attribute_length;u2 max_stack;u2 max_locals;u4 code_length;u1 code[code_length];u2 exception_table_length; {u2 start_pc;u2 end_pc;u2 handler_pc;u2 catch_type;} exception_table[exception_table_length];u2 attributes_count;attribute_info attributes[attributes_count];}

其中code數(shù)組里存儲就是字節(jié)碼指令,那么如何解析呢?每條指令在code[]中都是一個字節(jié),我們平時(shí)javap命令反編譯看到的指令其實(shí)是助記符,只是方便閱讀字節(jié)碼使用的,jvm有一張字節(jié)碼與助記符的對照表,根據(jù)對照表,就可以將指令翻譯為可讀的助記符了。這里我也是在網(wǎng)上隨便找了一個對照表,保存到本地txt文件中,并在使用時(shí)解析成HashMap。代碼很簡單,就不貼了,可以參考我代碼中InstructionTable.java。

接下來我們就可以解析字節(jié)碼了:

for (int j = 0; j < methodInfo.attributesCount; j++) {if (methodInfo.attributes[j] instanceof CodeAttribute) {CodeAttribute codeAttribute = (CodeAttribute) methodInfo.attributes[j];for (int m = 0; m < codeAttribute.codeLength; m++) {short code = codeAttribute.code[m];System.out.print(InstructionTable.getInstruction(code) + "\n");}}}

到此,相信大家對“Java Class文件是什么”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)建站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

網(wǎng)頁題目:JavaClass文件是什么-創(chuàng)新互聯(lián)
網(wǎng)址分享:http://muchs.cn/article34/dsidpe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站改版微信公眾號、網(wǎng)站策劃、企業(yè)網(wǎng)站制作、品牌網(wǎng)站建設(shè)做網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化