偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

一篇帶給你 Java Class 詳解

系統(tǒng) OpenHarmony
class文件的結(jié)構(gòu)分析就到這里了,通過(guò)一個(gè)簡(jiǎn)單的類(lèi)去探索編譯器如何實(shí)現(xiàn)類(lèi)的編寫(xiě),那么再?gòu)?fù)雜的類(lèi)我們也能一步一步分析出來(lái),只是需要我們更加細(xì)心。

??想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):??

??51CTO OpenHarmony技術(shù)社區(qū)??

??https://ost.51cto.com??

  • 基于棧和基于寄存器指令區(qū)別?
  • 什么是直接引用和間接引用?
  • class文件怎么來(lái)的?
  • apt與AMS字節(jié)碼插樁?

第一節(jié) Class 文件介紹

1、 背景

“計(jì)算機(jī)只認(rèn)識(shí)0和1,所以我們寫(xiě)的程序需要被編譯 器翻譯成由0和1構(gòu)成的二進(jìn)制格式才能被計(jì)算機(jī)執(zhí)行?!笔嗄赀^(guò)去了,今天的計(jì)算機(jī)仍然只能識(shí)別0和1,但由于最近十年內(nèi)虛擬機(jī)以及大量建立在虛擬機(jī)之上的程序語(yǔ)言如雨后春筍般出現(xiàn)并蓬勃發(fā)展,把我們編寫(xiě)的程序編譯成二進(jìn)制本地機(jī)器碼(Native Code)已不再是唯一的選擇,越來(lái)越多的程序語(yǔ)言選擇了與操作系統(tǒng)和機(jī)器指令集無(wú)關(guān)的、平臺(tái)中立的格式作為程序編譯后的存儲(chǔ)格式。

Java 語(yǔ)言之所以能實(shí)現(xiàn)一次編譯到處運(yùn)行,就是因?yàn)槭褂盟衅脚_(tái)都支持的字節(jié)碼格式

第二節(jié) Class類(lèi)文件的結(jié)構(gòu)

1、class文件格式

一個(gè)class文件是由下圖描述出來(lái)的。我們可以按這張表的格式去解釋一個(gè)class文件。

以u(píng)1、u2、u4、u8來(lái)分別代表1個(gè)字節(jié)、2個(gè)字節(jié)、4個(gè)字節(jié)和8個(gè)字節(jié)的無(wú)符號(hào)數(shù),無(wú)符號(hào)數(shù)可以用來(lái)描述數(shù)字、索引引用、數(shù)量值或者按照UTF-8編碼構(gòu)成字符串值。

接下來(lái)我們用這一小段樣本代碼來(lái)說(shuō)明class文件的具體內(nèi)容。再?gòu)?fù)雜的java源文件都是可以通過(guò)這樣的方式分析出來(lái)。

public class TestClass {
private int m;
public int inc() {
return m + 1;
}
}

我們將上面的代碼用編譯器進(jìn)行編譯得到一個(gè)TestClass.class文件。通過(guò)Windows工具“010Editor”對(duì)這個(gè)class文件進(jìn)行閱讀。

下面是010Editor上面class二進(jìn)制內(nèi)容:

0A FE BA BE : 魔數(shù)(它的唯一作用是確定這個(gè)文件是否為一個(gè)能被虛擬機(jī)接受的Class文件)。

00 00 00 34 : 次版本號(hào)與主版本號(hào) 次版本號(hào)為0,主版本號(hào)為52(只能被jdk1.1~1.8 識(shí)別)。

class主版本與jdk版本關(guān)系(部分)。

2、 常量池

00 16 : 常量池?cái)?shù)量 22,索引是1-21。

為什么常量池的索引不從0開(kāi)始?

如果后面某些指向常量池的索引值的數(shù)據(jù)在特定情況下需要表達(dá)“不引用任何一個(gè)常量池項(xiàng)目”的含義,可以把索引值設(shè)置為0來(lái)表示。

0A 00 04 00 12( 常量索引:1):

0A: -> 10 通過(guò)查表 表示一個(gè)Methodref_info。

04: 找到索引為4的常量 -> java/lang/Object。

12 轉(zhuǎn)十進(jìn)制得到18 , 這里找到常量池里18的常量代表 ()V。

得到結(jié)果: java/lang/Object () V。

09 00 03 00 13( 常量索引:2):

09: -> 09 表示一個(gè)Fieldref_info。

最終得到:com/havefun/javaapitest/TestClass 和 m i。

07 00 14( 常量索引:3):

最終結(jié)果 :com/havefun/javaapitest/TestClass。

07 00 15( 常量索引:4):

07 表示類(lèi)信息。

15-> 21 是在常量的索引 -> java/lang/Object。

01 00 01 6D( 常量索引:5): m。

01 00 01 49( 常量索引:6): I。

01 00 06 3C 69 6E 69 74 3E( 常量索引:7):

01 00 03 28 29 56( 常量索引:8): ()V。

01 00 04 43 6F 64 65( 常量索引:9): Code。

01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65( 常量索引:10): LineNumberTable。

01 00 12 4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65( 常量索引:11):

LocalVariableTable

01 00 04 74 68 69 73( 常量索引:12): ----> this

01 00 23 4C 63 6F 6D 2F 68 61 76 65 66 75 6E 2F 6A 61 \

76 61 61 70 6974 65 73 74 2F 54 65 73 74 43 6C 61 73 73 3B( 常量索引:13):

Lcom/havefun/javaapitest/TestClass;

01 00 03 69 6E 63( 常量索引:14): inc

01 00 03 28 29 49( 常量索引:15): ()I

01 00 0A 53 6F 75 72 63 65 46 69 6C 65( 常量索引:16): SourceFile

01 00 0E 54 65 73 74 43 6C 61 73 73 2E 6A 61 76 61( 常量索引:17): TestClass.java

0C 00 07 00 08( 常量索引:18):

0C 表示字段或方法的部分引用。

07 ->

05 -> ()V

最終得到: // “”: ()V。

0C 00 05 00 06( 常量索引:19): 最終得到: // m:I

01 00 21 63 6F 6D 2F 68 61 76 65 66 75 6E 2F 6A 61 \

76 61 61 70 69 74 95 73 74 2F 54 65 73 76 43 6C 61 73 73( 常量索引:20):

最終得到:com/havefun/javaapitest/TestClass。

01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74( 常量索引:21):

最終得到:java/lang/Object。

Javap -v 生成的內(nèi)容,通過(guò)上面的分析就很容易看懂這個(gè)反編譯過(guò)后的常量池要表達(dá)的內(nèi)容了!

Constant pool:
#1 = Methodref #4.#18 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#19 // com/havefun/javaapitest/TestClass.m:I
#3 = Class #20 // com/havefun/javaapitest/TestClass
#4 = Class #21 // java/lang/Object
#5 = Utf8 m
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/havefun/javaapitest/TestClass;
#14 = Utf8 inc
#15 = Utf8 ()I
#16 = Utf8 SourceFile
#17 = Utf8 TestClass.java
#18 = NameAndType #7:#8 // "<init>":()V
#19 = NameAndType #5:#6 // m:I
#20 = Utf8 com/havefun/javaapitest/TestClass
#21 = Utf8 java/lang/Object

**訪問(wèn)標(biāo)識(shí)符、類(lèi)索引、父類(lèi)索引與接口索引集合 **。

下圖是class文件結(jié)構(gòu)表里面的一部分,描述了訪問(wèn)標(biāo)識(shí),類(lèi)索引,父類(lèi)索引與接口集合等。

00 21: ACC_PUBLIC | ACC_SUPER

下面是截取的常量池部分內(nèi)容,類(lèi)索引和父類(lèi)索引都能在上面找到。

#1 = Methodref          #4.#18         // java/lang/Object."<init>":()V
#2 = Fieldref #3.#19 // com/havefun/javaapitest/TestClass.m:I
#3 = Class #20 // com/havefun/javaapitest/TestClass
#4 = Class #21 // java/lang/Object

00 03: 類(lèi)索引-> 常量池索引3

00 04: 父類(lèi)索引-> 常量池索引4

00 00: 接口數(shù)量0

3、 字段信息

字段表結(jié)構(gòu)如下:

字段訪問(wèn)標(biāo)志:

字段表信息:

00 01: 字段數(shù)量 1

通過(guò)字段表結(jié)構(gòu)讀取6個(gè)字節(jié):00 02 00 05 00 06 00 00

00 02 訪問(wèn)描述符:代表了private

00 05 字段名稱在常量池的索引:m

00 06 描述符在常量池的索引:I

00 00 屬性數(shù)量為0

結(jié)合起來(lái)字段就很容易知道這個(gè)是 private 的int類(lèi)型的字段m。

4、方法表信息

繼續(xù)讀class文件后面的內(nèi)容:00 02 表示有兩個(gè)方法。

方法表的結(jié)構(gòu):

向后讀方法表第一個(gè)方法:

00 01: 代表public方法 00 07:方法名 00 08:方法簽名()V

上面這小部分可以得到如下信息:

public com.havefun.javaapitest.TestClass();
descriptor: ()V
flags: ACC_PUBLIC

00 01: 表示屬性表有一個(gè)屬性

屬性表結(jié)構(gòu):

00 09 00 00 00 2F: 通過(guò)常量池09表示Code(Code 的含義是Java代碼編譯成字節(jié)碼的指令), 后面4個(gè)字節(jié)表示接下來(lái)的屬性長(zhǎng)度,2F轉(zhuǎn)十進(jìn)制等于47。

Code對(duì)應(yīng)的結(jié)構(gòu):

接下來(lái)的字節(jié)碼是:00 01 00 01 表示操作數(shù)棧最大深度為1;max_locals代表了局部變量表所需的存儲(chǔ)空間。

再接下來(lái)4個(gè)字節(jié):00 00 00 05(表示代碼長(zhǎng)度)。

再向后讀5個(gè)字節(jié)表示代碼:2A B7 00 01 B1;。

  • 2A:對(duì)應(yīng)指令aload_0。是將第0個(gè)變量槽中為reference類(lèi)型的本地變量推送到操作數(shù)棧頂。
  • B7:指令為invokespecial。指令的作用是以棧頂?shù)膔eference類(lèi)型的數(shù)據(jù)所指向的對(duì)象作為方法接收者,調(diào)用此對(duì)象的實(shí)例構(gòu)造器方法、private方法或者它的父類(lèi)的方法。這個(gè)方法有一個(gè)u2類(lèi)型的參數(shù)說(shuō)明具體調(diào)用哪一個(gè)方法,它指向常量池中的一個(gè)CONSTANT_Methodref_info類(lèi)型常量,即此方法的符號(hào)引用。

這里 00 01 也就是代表了常量池里面#1號(hào)常量 =>(// java/lang/Object.“”: ()V)這是一個(gè)構(gòu)造方法。

因?yàn)镴ava默認(rèn)在每個(gè)方法插入一個(gè)默認(rèn)參數(shù)this,并且放在變量槽0的位置。上面兩條指令可以理解為 this = new Object(); 把這個(gè)this給實(shí)例化了。

  • B1:對(duì)應(yīng)指令為return。

說(shuō)明:這里一個(gè)字節(jié)表示一條指令操作,那么也就說(shuō)明Java虛擬機(jī)最多不會(huì)超過(guò)256條指令;

00 00 :異常表長(zhǎng)度為0。

00 02:屬性列表數(shù)量為2。

那么上面可以得到如下信息:

Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return

(1) 屬性表信息

00 0A 00 00 00 06 00 01 00 00 00 03:

通過(guò)查表0A對(duì)應(yīng)的常量池里面的:LineNumberTable;LineNumberTable屬性用于描述Java源碼行號(hào)與字節(jié)碼行號(hào)(字節(jié)碼的偏移量)之間的對(duì)應(yīng)關(guān)系。00 00 00 06 表示屬性長(zhǎng)度為6個(gè)字節(jié);00 01表示有一個(gè)line_number_table;00 00表示是字節(jié)碼行號(hào),00 03表示是Java源碼行號(hào).

LineNumberTable對(duì)應(yīng)的結(jié)構(gòu):

那么這可以得到如下信息:

LineNumberTable:
line 3: 0

00 0B 00 00 00 0C 00 01 00 00 00 05 00 0C 00 0D 00 00:

通過(guò)常量池得到0B代表的是 LocalVariableTable。

LocalVariableTable的屬性結(jié)構(gòu):

local_variable_info結(jié)構(gòu)。

屬性長(zhǎng)度0C轉(zhuǎn)十進(jìn)制為12;00 01局部變量表長(zhǎng)度為1。

00 00 00 05:表示start_pc和length屬性分別代表了這個(gè)局部變量的生命周期開(kāi)始的字節(jié)碼偏移量及其作用范圍覆蓋的長(zhǎng)度,兩者結(jié)合起來(lái)就是這個(gè)局部變量在字節(jié)碼之中的作用域范圍。

0C:在常量池查詢是表示 this;0D:是這個(gè)變量的描述符對(duì)應(yīng)的:Lcom/havefun/javaapitest/TestClass。

最后的00 00表示:index是這個(gè)局部變量在棧幀的局部變量表中變量槽的位置。

通過(guò)上面這一小節(jié)可以得到如下信息:

LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/havefun/javaapitest/TestClass;

5、 屬性信息

SourceFile屬性結(jié)構(gòu)。

00 10:對(duì)應(yīng)常量池的SourceFile 00 00 00 02:對(duì)應(yīng)的屬性長(zhǎng)度為2。

作用:如果不生成這項(xiàng)屬性,當(dāng)拋出異常時(shí),堆棧中將不會(huì)顯示出錯(cuò)代碼所屬的文件名。

11:轉(zhuǎn)十進(jìn)制得到17,sourcefile_index數(shù)據(jù)項(xiàng)是指向常量池中CONSTANT_Utf8_info型常量的索引,常量值是源碼文件的文件名。通過(guò)常量池得知17對(duì)應(yīng)常量為:TestClass.java。

第三節(jié) 基于棧指令簡(jiǎn)介

1、 基于棧的解釋器執(zhí)行過(guò)程

以一段代碼作為例子說(shuō)明演示字節(jié)碼執(zhí)行過(guò)程。

public int calc() {
int a = 100;
int b = 200;
int c = 300;
return (a + b) * c;
}

編譯成字節(jié)碼指令如下:

 public int calc();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: bipush 100 // 將100 推到操作數(shù)棧
2: istore_1 // 將操作數(shù)棧頂?shù)恼椭党鰲2⒋娣诺降?個(gè)局部變量槽中
3: sipush 200 // 將200 推到操作數(shù)棧
6: istore_2 // 將操作數(shù)棧頂?shù)恼椭党鰲2⒋娣诺降?個(gè)局部變量槽中
7: sipush 300 // 將300 推到操作數(shù)棧
10: istore_3 // 將操作數(shù)棧頂?shù)恼椭党鰲2⒋娣诺降?個(gè)局部變量槽中
11: iload_1 // 將局部變量槽1的變量放入操作數(shù)棧
12: iload_2 // 將局部變量槽2的變量放入操作數(shù)棧
13: iadd // 將操作數(shù)棧中頭兩個(gè)棧頂元素出棧,做整型加法,然后把結(jié)果重新入棧
14: iload_3 // 將局部變量槽3的變量放入操作數(shù)棧
15: imul // 將操作數(shù)棧中頭兩個(gè)棧頂元素出棧,做整型乘法,然后把結(jié)果重新入棧
16: ireturn // 將結(jié)束方法執(zhí)行并將操作數(shù)棧頂 的整型值返回給該方法的調(diào)用者

2、 基于棧與基于寄存器指令集區(qū)別?

以同樣的1+1這個(gè)計(jì)算來(lái)進(jìn)行舉例。

基于棧的指令集如下:

iconst_1
iconst_1
iadd
istore_0

基于寄存器指令集如下:

mov eax, 1
add eax, 1

這兩種指令集的優(yōu)勢(shì)與劣勢(shì):

  • 基于棧的指令集主要優(yōu)點(diǎn)是可移植。
  • 基于寄存器的指令會(huì)比基于棧的指令少,但是每條指令會(huì)邊長(zhǎng)。
  • 基于棧指令集的主要缺點(diǎn)是理論上執(zhí)行速度相對(duì)來(lái)說(shuō)會(huì)稍慢一些。

個(gè)人總結(jié)

class文件的結(jié)構(gòu)分析就到這里了,通過(guò)一個(gè)簡(jiǎn)單的類(lèi)去探索編譯器如何實(shí)現(xiàn)類(lèi)的編寫(xiě),那么再?gòu)?fù)雜的類(lèi)我們也能一步一步分析出來(lái),只是需要我們更加細(xì)心。我們了解了這些文件的生成過(guò)程,個(gè)人認(rèn)為有如下好處:

  • 知道javap -v 反編譯class文件的輸出內(nèi)容到底是怎么來(lái)的。
  • class文件怎么描述一個(gè)Java方法或者一個(gè)變量。運(yùn)用方向比如字節(jié)碼增強(qiáng),動(dòng)態(tài)修改或者生成等都是能夠?qū)崿F(xiàn)的。

??想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):??

??51CTO OpenHarmony技術(shù)社區(qū)??

??https://ost.51cto.com??

責(zé)任編輯:jianghua 來(lái)源: 鴻蒙社區(qū)
相關(guān)推薦

2021-04-14 14:16:58

HttpHttp協(xié)議網(wǎng)絡(luò)協(xié)議

2022-03-03 09:05:17

索引MySQL數(shù)據(jù)查詢

2023-03-09 07:47:56

BeanFactorSpring框架

2021-08-06 17:47:46

Kotin高階函數(shù)函數(shù)

2021-03-28 09:12:58

多線程死鎖技術(shù)熱點(diǎn)

2024-04-15 08:17:21

Spring依賴注入循環(huán)依賴

2020-12-18 07:55:47

JavaScriptClassMyClass

2021-07-12 06:11:14

SkyWalking 儀表板UI篇

2021-06-21 14:36:46

Vite 前端工程化工具

2021-01-28 08:55:48

Elasticsear數(shù)據(jù)庫(kù)數(shù)據(jù)存儲(chǔ)

2023-03-29 07:45:58

VS編輯區(qū)編程工具

2021-04-08 11:00:56

CountDownLaJava進(jìn)階開(kāi)發(fā)

2022-03-22 09:09:17

HookReact前端

2021-07-21 09:48:20

etcd-wal模塊解析數(shù)據(jù)庫(kù)

2024-06-13 08:34:48

2021-03-12 09:21:31

MySQL數(shù)據(jù)庫(kù)邏輯架構(gòu)

2021-04-01 10:51:55

MySQL鎖機(jī)制數(shù)據(jù)庫(kù)

2022-02-17 08:53:38

ElasticSea集群部署

2022-02-25 15:50:05

OpenHarmonToggle組件鴻蒙

2021-07-08 07:30:13

Webpack 前端Tree shakin
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)