java怎编写么解析一个类型_DAY3:你必须知道的java虚拟机之类篇——类文件的结构...
馬上過年啦,不知道大家今年有沒有投資基金股票呢?是賺的盆滿缽滿還是拍斷大腿,可以評論區一起交流交流,秀一秀哈哈,反正我是沒來得及上車。
暴富西不可能暴富的啦,打工人嘛幾能寫寫文章啦~記得點贊?關注呀
上節回顧
1.創建對象的7個步驟
2.java對象的內存布局了解多少
3.內存分配的并發問題怎么解決
一點說不上來的同學注意啊!你離暴富又遠了一步,好在我這兒有本秘籍,暴富秘籍
寫在前面
從本章開始,我打算用2章的篇幅來講述一個類文件結構和類的加載過程。很多工作了幾年的同學都會有這種感受:幾個月不學技術,不去接觸新的東西,漸漸的就會跟不上潮流了。就像前幾年一直鼓吹中臺,到現在又要拆中臺,這都是種種原因所導致的,可理解為是一種潮流。但不管潮流怎么變,最底層的知識永遠都不會輕易動搖和變化的,我們現在學的可能未來5年、10年都不會有所改變,對于短短的編碼生涯來說基本屬于終生受益了。九九乘法表雖然枯燥難背,但現在不也深入每個人的生活,并終生受用。
本章內容
本章我們主要學習class類文件的結構,要想更好的理解和掌握本章的知識點的話,需要同學們也上手一起來操作,同時針對一些常用的字節碼指令簡單進行一些介紹。
一、Class類文件的結構
Class文件是一組以8個字節為基礎單位的二進制流,各個數據項目嚴格按照順序緊湊地排列在文
件之中,中間沒有添加任何分隔符,這使得整個Class文件中存儲的內容幾乎全部是程序運行的必要數
據,沒有空隙存在。當遇到需要占用8個字節以上空間的數據項時,則會按照高位在前的方式分割
成若干個8個字節進行存儲。
Class文件有兩種數據結構:無符號數和表
無符號數:屬于基本的數據類型,以u1、u2、u4、u8來分別代表1個字節、2個字節、4個字節和8個
字節的無符號數,無符號數可以用來描述數字、索引引用、數量值或者按照UTF-8編碼構成字符串
值。
表:是由多個無符號數或者其他表作為數據項構成的復合數據類型,為了便于區分,所有表的命名
都習慣性地以“_info”結尾。表用于描述有層次關系的復合結構的數據,整個Class文件本質上也可以視
作是一張表。
在解析之前我們需要下載一個能查看class文件內容的工具,我這邊使用的是Hex Fiend,windows系統的同學也可以下載WinHex都是一樣的。
希望同學們也能自己下載一個也嘗試著來解析一下,這樣就不至于隔天就忘記了。
使用javac指令生成對應的class文件,并把文件拖入Hex Fiend中打開
好了,今天我們要將的就是這些個16進制數字組合。
1、魔數
每個Class文件的頭4個字節被稱為魔數(Magic Number),它的唯一作用是確定這個文件是否為
一個能被虛擬機接受的Class文件。
java使用的魔數是CAFEBABE,這個壓根不用記住,看一眼java圖標就顯而易見了
以前一直不清楚這圖標到底是咖啡還是茶呢,本人喝茶比較多一點,當然老外的話一定是咖啡比較多。現在相信大家都能確定這就是一杯咖啡。
2、文件版本號
緊接著魔數的4個字節存儲的是Class文件的版本號:第5和第6個字節是次版本號(Minor
Version),第7和第8個字節是主版本號(Major Version)。
至于版本號的作用相信大家都能想到,畢竟java從JDK1.1開始,到現在JDK16新特性都已經出來了,大版本都已經有十幾個了,這還是不計算各個小版本的情況下。為了保持非常良好的向后兼容性,這個版本號必不可少,所謂向后兼容就是說低版本的JDK下開發的產品能在高版本的JDK中跑。
Java的版本號是從45開始的,JDK 1.1之后的每個JDK大版本發布主版本號向上加1(JDK 1.0~1.1使用了45.0~45.3的版本號)。這邊我們看到主版本號的16進制是34,對應的10進制就是52,也就是說我們從這個就能推斷出,我們是用JDK1.8來編譯生成這個class文件的。
關于次版本號,曾經在現代Java(即Java 2)出現前被短暫使用過,JDK 1.0.2支持的版本45.0~
45.3(包括45.0~45.3)。JDK 1.1支持版本45.0~45.65535,從JDK 1.2以后,直到JDK 12之前次版本號均未使用,全部固定為零。而到了JDK 12時期,由于JDK提供的功能集已經非常龐大,有一些復雜的新特性需要以“公測”的形式放出,所以設計者重新啟用了副版本號,將它用于標識“技術預覽版”功能特性的支持。如果Class文件中使用了該版本JDK尚未列入正式特性清單中的預覽功能,則必須把次版本號標識為65535,以便Java虛擬機在加載類文件時能夠區分出來。
3、常量池
同學們應該都還記得在DAY1中我們曾經講過方法區中的運行時常量池,我們這邊講的常量池指的是class文件中的常量池,這個大家要區分開。兩者的區別就是運行時常量池具備動態性,這里我們就不展開講了。
緊接著主、次版本號之后的是常量池入口,由于常量池中常量的數量是不固定的,所以在常量池的入口需要放置一項u2類型的數據,代表常量池容量計數值(constant_pool_count)。
這里我們看到這個16進制值是0013,對應的10進制就是19。與Java中語言習慣不同,這個容量計數是從1而不是0開始的,所以記錄的是#1到#18的常量。
這個可能不大好理解。我們通過事實來證明一下,使用javap -c -v指令來看一下我們的class文件,眼見為實
顯而易見這里一共是18個常量。
對應到我們Hex Fiend里面的字段的話就是這大段
大家先別慌,這些其實都是死的,只要參照著字典表一個個對照來查找就很簡單了,下面我們也一起來走一遍。
常量池解析實戰
首先拿出我們的字典表,先放著不急著看
每一種類型都是以_info結尾的,證明這是幾個常量都是表,籠統地去看不難發現,每一個常量的開頭都是一個u1字節長度的tag,這就好比是一個id號,大家可以這樣來理解。
1.第一個常量
看圖中,緊接著常量池入口的第一個字節就是我們第一個常量的tag,他的值為0A,也就是10,根據這個id查找字典,找到
也就是說這個常量是一個u1和兩個u2組成的,一共5個字節,很快哦,啪~一下我們就把第一個常量找到了
其他的都是類似的,除了tag = 01的utf_8比較特殊
2.常量CONSTANT_Utf8_info
這個常量的第二個項目的含義是lenth也就是第三個項目有多少個位數的含義。
我們用第一個CONSTANT_Utf8_info常量來作為例子
tag = 01表明他是一個CONSTANT_Utf8_info常量,0001表示這個常量的長度是1,6D就是長度為1的UTF-8編碼的字符串
這樣分開來講應該很容易理解來把。同學們能自己把這18個常量一一找出來嗎?
評論區找答案哦~
4.訪問標志
在常量池結束之后,緊接著的2個字節代表訪問標志(access_flags),這個標志用于識別一些類或
者接口層次的訪問信息。
可以看到這邊的16進制值是0x0021
access_flags中一共有16個標志位可以使用,當前只定義了其中9個,沒有使用到的標志位要求一
律為零。
每一個訪問標志占用一位目前一共又9位,還有6位還未使用,全部記為0。
下面我們看一下這個訪問標志是怎么算出來的
首先把0x0021轉換為2進制
0000000000100001
也就是說我們第0位和第5位為1的兩個訪問標志位為1,其他全部為0,對應到上面表格中很容易就能查出,這邊除了ACC_PUBLIC和ACC_SUPER為真,其他全部為假。
最后我們通過javap -c -v指令來驗證一下
5.類索引、父類索引與接口索引集合
類索引(this_class)和父類索引(super_class)都是一個u2類型的數據,而接口索引集合
(interfaces)是一組u2類型的數據的集合,Class文件中由這三項數據來確定該類型的繼承關系。
java是不允許多重繼承,但是允許實現多個接口,所以除了Object類的父類索引是0x0000,其他類的父類索引都不為0,父類索引后面緊跟著的是一個u2長度的接口索引集合計數,可以類比常量池計數器,接口索引集合按照代碼中接口實現的順序從左到右記錄索引位置。
對應到我們的Hex Fiend工具里面是這3個u2字符,分別表示
類索引: 0x0003 代表常量池的#3號常量
父類索引:0x0004 代表常量池的#4號常量
接口索引集合計數器: 0x0000 代表沒有實現任何接口
考慮到本章講的內容相對來說比較多,而且看到到這里跟著我一起一邊學一邊操作的同學應該已經對類文件的結構的解析套路有了一個大致的了解了,所以正文的類文件結構就告一段落。剩余的字段表集合、方法表集合和屬性表集合這三個部分我另外開一章來寫,感興趣的同學可以繼續進入進階學習
最后附上常量池解析的結果
全部正確的同學恭喜你已經小有所成啦,點贊👍+關注,下一篇的主題是類加載~
總結
以上是生活随笔為你收集整理的java怎编写么解析一个类型_DAY3:你必须知道的java虚拟机之类篇——类文件的结构...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php mysql 分行执行,php执行
- 下一篇: c语言深度解剖 pdf,c语言深度解剖