天天看點

面試官:說說你了解Class檔案結構?

本文思維導圖:

面試官:說說你了解Class檔案結構?

Class類檔案結構

為什麼Java可以一次編譯到處運作?JVM無關性

與平台無關性是建立在作業系統上,虛拟機廠商提供了許多可以運作在各種不同平台的虛拟機,它們都可以載入和執行位元組碼,進而實作程式的“一次編寫,到處運作”。​​https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html​​

各種不同平台的虛拟機與所有平台都統一使用的程式存儲格式——位元組碼(Byte Code)是構成平台無關性的基石,也是語言無關性的基礎。Java 虛拟機不和包括 Java 在内的任何語言綁定,它隻與“Class 檔案”這種特定的二進制檔案格式所關聯,Class 檔案中包含了 Java 虛拟機指令集和符号表以及若幹其他輔助資訊。

面試官:說說你了解Class檔案結構?

Class  類檔案

Java 技術能夠一直保持非常好的向後相容性,這點 Class 檔案結構的穩定性功不可沒。Java 已經發展到 14 版本,但是 class 檔案結構的内容,絕大部分在JDK1.2 時代就已經定義好了。雖然 JDK1.2 的内容比較古老,但是 java 發展經曆了十餘個大版本,但是每次基本上知識在原有結構基礎上新增内容、擴充功能,并未對定義的内容做修改。

任何一個 Class 檔案都對應着唯一一個類或接口的定義資訊,但反過來說,Class 檔案實際上它并不一定以磁盤檔案的形式存在(比如可以動态生成、或者直接送入類加載器中)。

Class 檔案是一組以 8 位位元組為基礎機關的二進制流。

工具介紹

Sublime:檢視 16 進制的編輯器

javap:javap 是 JDK 自帶的反解析工具。它的作用是将 .class 位元組碼檔案解析成可讀的檔案格式。

在使用 javap 時我一般會添加 -v 參數,盡量多列印一些資訊。同時,我也會使用 -p 參數,列印一些私有的字段和方法。

jclasslib:如果你不太習慣使用指令行的操作,還可以使用 jclasslib,jclasslib 是一個圖形化的工具,能夠更加直覺的檢視位元組碼中的内容。它還分門别類的對類中的各個部分進行了整理,非常的人性化。同時,它還提供了 Idea 的插件,你可以從 plugins 中搜尋到它。jclasslib 的下載下傳位址:https://github.com/ingokegel/jclasslib

Class  檔案格式

從一個 Class 檔案開始,整個 Class 檔案的格式就是一個二進制的位元組流。各個資料項目嚴格按照順序緊湊地排列在 Class 檔案之中,中間沒有添加任何分隔符,這使得整個 Class 檔案中存儲的内容幾乎全部是程式運作的必要資料,沒有空隙存在。

Class 檔案格式采用一種類似于 C 語言結構體的僞結構來存儲資料,這種僞結構中隻有兩種資料類型:無符号數和表。

無符号數屬于基本的資料類型,以 u1、u2、u4、u8 來分别代表 1 個位元組(一個位元組是由兩位 16 進制數組成)、2 個位元組、4 個位元組和 8 個位元組的無符号數,無符号數可以用來描述數字、索引引用、數量值或者按照 UTF-8 編碼構成字元串值。

表是由多個無符号數或者其他表作為資料項構成的複合資料類型,所有表都習慣性地以“_info”結尾。表用于描述有層次關系的複合結構的資料,整個Class 檔案本質上就是一張表。

Class  檔案格式詳解

Class 的結構不像 XML 等描述語言,由于它沒有任何分隔符号,是以在其中的資料項,無論是順序還是數量,都是被嚴格限定的,哪個位元組代表什麼含義,長度是多少,先後順序如何,都不允許改變。

面試官:說說你了解Class檔案結構?

按順序包括:

魔數與 Class  檔案的版本

每個 Class 檔案的頭 4 個位元組稱為魔數(Magic Number),它的唯一作用是确定這個檔案是否為一個能被虛拟機接受的 Class 檔案。使用魔數而不是擴充名來進行識别主要是基于安全方面的考慮,因為檔案擴充名可以随意地改動。檔案格式的制定者可以自由地選擇魔數值,隻要這個魔數值還沒有被廣泛采用過同時又不會引起混淆即可。(

面試官:說說你了解Class檔案結構?

緊接着魔數的 4 個位元組存儲的是 Class 檔案  的版本号:第 5 和第 6 個位元組是次版本号(MinorVersion),第 7 和第 8 個位元組是主版本号(Major Version)。

Java 的版本号是從 45 開始的,JDK 1.1 之後的每個 JDK 大版本釋出主版本号向上加 1 高版本的 JDK 能向下相容以前版本的 Class 檔案,但不能運作以後版本的 Class 檔案,即使檔案格式并未發生任何變化,虛拟機也必須拒絕執行超過其版本号的 Class 檔案。

面試官:說說你了解Class檔案結構?

代表 JDK1.8(16 進制的 34,換成 10 進制就是 52)

常量池

常量池中常量的數量是不固定的,是以在常量池的入口需要放置一項 u2 類型的資料,代表常量池容量計數值(constant_pool_count)。與 Java 中語言習慣不一樣的是,這個容量計數是從 1 而不是 0 開始的

面試官:說說你了解Class檔案結構?

常量池中主要存放兩大類常量:字面量(Literal)和符号引用(Symbolic References)。

字面量比較接近于 Java 語言層面的常量概念,如文本字元串、聲明為 final 的常量值等。

符号引用則屬于編譯原理方面的概念,包括了下面三類常量:類和接口的全限定名(Fully Qualified Name)、字段的名稱和描述符(Descriptor)、方法的名稱和描述符

通路标志

用于識别一些類或者接口層次的通路資訊,包括:這個 Class 是類還是接口;是否定義為 public 類型;是否定義為 abstract 類型;如果是類的話,是否被聲明為 final 等

類索引、父類索引與接口索引集合

這三項資料來确定類的繼承關系。類索引用于确定這個類的全限定名,父類索引用于确定這個類的父類的全限定名。由于 Java 語言不允許多重繼承,是以父類索引隻有一個,除了 java.lang.Object 之外,所有的 Java 類都有父類,是以除了java.lang.Object 外,所有 Java 類的父類索引都不為 0。接口索引集合就用來描述這個類實作了哪些接口,這些被實作的接口将按 implements 語句(如果這個類本身是一個接口,則應當是 extends 語句)後的接口順序從左到右排列在接口索引集合中

字段表集合

描述接口或者類中聲明的變量。字段(field)包括類級變量以及執行個體級變量。

而字段叫什麼名字、字段被定義為什麼資料類型,這些都是無法固定的,隻能引用常量池中的常量來描述。字段表集合中不會列出從超類或者父接口中繼承而來的字段,但有可能列出原本 Java 代碼之中不存在的字段,譬如在内部類中為了保持對外部類的通路性,會自動添加指向外部類執行個體的字段。

方法表集合

描述了方法的定義,但是方法裡的 Java 代碼,經過編譯器編譯成位元組碼指令後,存放在屬性表集合中的方法屬性表集合中一個名為“Code”的屬性裡面。

屬性表集合