《深入理解java虛擬機器》學習筆記5——Java Class類檔案結構
Java語言從誕生之時就宣稱一次編寫,到處執行的跨平臺特性,其實現原理是原始碼檔案並沒有直接編譯成機器指令,而是編譯成Java虛擬機器可以識別和執行的位元組碼檔案(Class類檔案,*.class),位元組碼檔案是一種平臺無關的中間編譯結果,位元組碼檔案由java虛擬機器讀取,解析和執行,java虛擬機器遮蔽了不同作業系統和硬體平臺的差異性。
如今的java虛擬機器已經稱為一種通用平臺,不但能夠執行java語言,Groovy,JRuby,Jython等一大批動態語言也可以直接在Java虛擬機器上執行,其原理也是這些動態語言的編譯器將原始碼檔案編譯為和Java相同的位元組碼檔案,這樣Java虛擬機器就可以像執行java語言一樣執行這些動態語言了。
位元組碼class類檔案是由一系列位元組碼命令組成,用於表示程式中各種常量、變數、關鍵字和運算子號的語義等等。Java的Class類檔案是一組以8為位元組為單位的二進位制流,各個資料項嚴格按照順序緊湊地排列在Class類檔案之中,中間沒有新增任何分隔符,當遇到需要佔用8位位元組以上空間的資料項時,按照高位在前的方式分割成若干個8位位元組進行儲存。
Java虛擬機器規定,Class類檔案格式採用類似C語言結構體的偽結構來儲存,這種偽結構中只有兩種資料型別:無符號數和表:
(1).無符號數:
屬於基本型別的資料,以u1, u2, u4, u8來分別代表1個位元組,2個位元組,4個位元組和8個位元組的無符號數,無符號數可以用來描述數字、索引引用、數量值或者按照UTF-8編碼的字串值。
(2).表:
由多個無符號數或其他表作為資料項構成的複合資料型別,所以表都習慣性地以“_info“結尾。表用於描述有層次關係的複合結構資料,整個Class檔案本質就是一張表。
Java Class類檔案結構如下:
型別 |
名稱 |
數量 |
u4 |
magic |
1 |
u2 |
minor_version |
1 |
u2 |
major_version |
1 |
u2 |
constant_pool_count |
1 |
cp_info |
constant_pool |
constant_pool_count-1 |
u2 |
access_flags |
1 |
u2 |
this_class |
1 |
u2 |
super_class |
1 |
u2 |
interfaces_count |
1 |
u2 |
interfaces |
interfaces_count |
u2 |
fields_count |
1 |
field_info |
fields |
fields_count |
u2 |
methods_count |
1 |
method_info |
methods |
methods_count |
u2 |
attributes_count |
1 |
attribute_info |
attributes |
attributes_count |
Class類檔案沒有任何分隔符,是嚴格按照這個結構表順序排列,下面具體介紹各個名稱含義:
(1).magic:
每個Class檔案的頭4個位元組被稱為魔數,它的唯一作用是用於確定這個檔案是否為一個能被java虛擬機器所接收的Class類檔案,即用於判定檔案是否是符合規範的java Class檔案。雖然說字尾名“.class”可以表明檔案是一個Class檔案,但是檔案字尾名是可以隨意被改動的,基於安全的考慮,很多檔案都通過魔數值來唯一確定檔案型別,java的Class檔案魔數是:0xCAFEBABE.
(2).minor_version和major_version:
每個Class檔案的第5和第6個位元組代表Class檔案的次版本號,第7和第8個位元組代表Class檔案的主版本號。
Class檔案的主、次版本號是由JDK決定的,JDK1.0~JDK1.1使用了45.0~45.3的版本號(45是主版本好,點”.“之後的是次版本號),從JDK1.1開始,每個大版本的JDK主版本號加1.
Class主、次版本號的一個作用時,高版本的Java虛擬機器可以向前相容,執行低版本JDK編譯的Class位元組碼檔案,而低版本的java虛擬機器不能執行高版本JDK編譯的Class位元組碼檔案。當低版本的java虛擬機器執行高版本JDK編譯的Class位元組碼檔案時,通常會報類似如下的異常:
- Exception in thread "main" java.lang.UnsupportedClassVersionError: a (Unsupporte
- d major.minor version 49.0)
JDK1.0~JDK1.1使用了45.0~45.3的版本號,JDK1.2使用了46.0~46.65535的版本號,JDK1.3使用了47.0~47.65535的版本號,JDK1.4使用了48.0~48.65535的版本號,JDK1.5使用了49.0~49.65535的版本號,JDK1.6使用了50.0~50.65535的版本號,JDK1.7使用51.0~51.65535的版本號。
在編譯時可以通過指定-target引數來改變主版本號,如JDK1.6編譯時如果沒有給定target引數,則編譯出來的Class檔案的主版本號是50,如果給定”-target 1.4 -source 1.4”引數之後,則主版本將變為48,如果給定”-target 1.5 ”引數之後,則主版本將變為49。
(3). constant_pool_count和constant_pool:
constant_pool_count代表Class檔案中常量池的數目,由於常量池的計數從1開始,因此常量池的容量是constant_pool_count-1。
第0項常量空出做特殊考慮,為了滿足一些指向常量池的索引值在某些特定情況下需要表達“不指向任何一個常量池”的意思。
constant_pool常量池是Class類檔案中出現的第一個表型別資料,常量池主要存放兩大類常量:
a.字面量(Literal):包括文字字串、final型別常量值。
b.符號引用(SymbolicReferences):包括類和介面的全限定名、欄位的名稱和描述符、方 法的名稱和描述符。
(4). access_flags:
用於表示Class或介面層次的訪問標誌,即類或介面層面的訪問控制資訊,通常儲存的資訊包括:Class類檔案是類、介面、列舉或是註解;是否定義為public型別;是否定義為abstract型別;類是否被定義為final等等。
(5). this_class、super_class和interfaces:
this_class類索引用於確定類的全限定名,super_class父類索引用於確定父類的全限定名,interfaces介面索引用於確定介面的全限定名,由於java中可以實現多個介面,因此使用interfaces_count來儲存介面數量。
(6). field:
field_info欄位表用於描述介面或者類中宣告的變數,field欄位包括了類級變數(靜態變數)和例項級變數(成員變數),但不包括方法內部的區域性變數。
fields_count欄位數目表示Class檔案中的類和例項變數總數,欄位存放的資訊包括:欄位訪問標誌、是否靜態、是否final、是否併發可見volatile、是否可序列化transient、資料型別、欄位名稱等等。
注意:欄位表中不包含從父類或者介面中繼承而來的欄位,但是會新增原本程式碼中不存在的欄位,例如this,以及內部類對外部類訪問而自動新增的外部類例項欄位等。
(7).method:
method_info方法表用於描述類或者介面中宣告的方法,methods_count用於表示Class檔案中方法總數,method方法儲存了方法的訪問標識、是否靜態、是否final、是否同步synchronized、是否本地方法native、是否抽象方法abstract、方法返回值型別、方法名稱、方法引數列表等資訊。
方法的程式碼指令並沒有直接存放在方法表中,而是存放著屬性表中的方法表Code中。
注意:如果父類的方法在子類沒有被重寫,方法表中不會出現來自父類的方法資訊,但是編譯器會自動新增類構造器”<clinit>”方法和例項構造器”<init>”方法。
Java編譯器的方法特徵簽名只包括:方法名稱、引數順序和引數型別,不包括方法返回值型別,因此java的方法過載不能通過方法的返回值類區別,但是在Class檔案中,方法特徵簽名包括方法的返回值型別,因此Class檔案中可以共存兩個名稱和引數完全相同而返回值型別不同的方法。
(8). attribute:
attribute_info屬性表是Class檔案格式中最具擴充套件性的一種資料專案,用於存放field_info欄位表、method_info方法表以及Class檔案的專有資訊,屬性表不要求各個屬性有嚴格順序,只要求不與已有的屬性名字重複即可,屬性表中存放的常用資訊如下:
屬性名稱 |
使用位置 |
含義 |
Code |
方法表 |
Java程式碼編譯後的位元組碼指令 |
ConstantValue |
欄位表 |
final關鍵字定義的常量值 |
Deprecated |
類、方法表、欄位表 |
被宣告為Deprecated的欄位或方法 |
Exception |
方法表 |
方法丟擲的異常 |
InnerClasses |
類檔案 |
內部類列表 |
LineNumberTable |
Code屬性 |
java原始碼行號和位元組碼指令的對應關係 |
LocalVariableTable |
Code屬性 |
方法的區域性變數描述 |
SourceFile |
類檔案 |
原始檔名稱 |
Synthetic |
類、方法表、欄位表 |
標識方法或欄位為編譯器自動生成 |
Class檔案是二進位制檔案,使用支援二進位制的文字編輯器開啟之後顯示的全是二進位制資料,非常的不便於閱讀和理解,使用JDK提供的javap工具可以簡單將Class反編譯,編譯理解Class檔案的結構,例子如下:
原始碼:
- public class Test {
- public int getNum(int i) {
- return i + 1;
- }
- }
javap反編譯之後的位元組碼檔案:
- public class Test extends java.lang.Object
- SourceFile: "Test.java"
- minor version: 0
- major version: 50
- //常量池
- Constant pool:
- const #1 = class #2;
- const #2 = Asciz Test;
- const #3 = class #4;
- const #4 = Asciz java/lang/Object;
- const #5 = Asciz <init>; //例項構造器
- const #6 = Asciz ()V; //void返回型別
- const #7 = Asciz Code; //屬性表Code屬性
- const #8 = Method #3.#9; //方法特徵簽名 java/lang/Object."<init>":()V
- const #9 = NameAndType #5:#6;// 方法名稱和返回值"<init>":()V
- const #10 = Asciz LineNumberTable; //屬性表原始碼行號和位元組碼指令對應表
- const #11 = Asciz LocalVariableTable; //屬性表方法區域性變數表
- const #12 = Asciz this; //Test類例項物件本身
- const #13 = Asciz LTest;; //物件型別,Test類
- const #14 = Asciz getNum; //方法名稱
- const #15 = Asciz (I)I; //方法引數列表為一個int型別和返回值為int型別
- const #16 = Asciz i; //引數名稱i
- const #17 = Asciz I; //引數型別int
- const #18 = Asciz SourceFile;
- const #19 = Asciz Test.java;
- //方法表
- {
- //建構函式(預設構造方法)
- public Test();
- Code: //屬性表Code屬性
- Stack=1, Locals=1, Args_size=1
- 0: aload_0
- 1: invokespecial #8; //Method java/lang/Object."<init>":()V
- 4: return
- LineNumberTable:
- line 2: 0
- LocalVariableTable: //屬性表方法區域性變數表
- Start Length Slot Name Signature
- 0 5 0 this LTest;
- //自定義方法
- public int getNum(int);
- Code:
- Stack=2, Locals=2, Args_size=2
- 0: iload_1
- 1: iconst_1
- 2: iadd
- 3: ireturn
- LineNumberTable:
- line 4: 0
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 4 0 this LTest;
- 0 4 1 i I
- }
相關文章
- 類檔案結構——深入理解Java虛擬機器 筆記三Java虛擬機筆記
- 深入學習Java虛擬機器——類檔案結構Java虛擬機
- 深入理解Java虛擬機器(類檔案結構)Java虛擬機
- 深入理解Java虛擬機器 – 類檔案結構Java虛擬機
- Java虛擬機器之Class類檔案結構Java虛擬機
- 《深入理解Java虛擬機器》-(實戰)練習修改class檔案Java虛擬機
- Java虛擬機器——類檔案結構Java虛擬機
- 深入理解虛擬機器之類檔案結構虛擬機
- Java虛擬機器,類檔案結構深度解析Java虛擬機
- 《深入理解 Java 虛擬機器》筆記整理Java虛擬機筆記
- 《深入理解Java虛擬機器》個人筆記Java虛擬機筆記
- JAVA虛擬機器學習筆記Java虛擬機機器學習筆記
- 深入理解 python 虛擬機器:pyc 檔案結構Python虛擬機
- 深入理解java虛擬機器——讀書筆記1Java虛擬機筆記
- 深入理解Java虛擬機器筆記1: OOM實戰Java虛擬機筆記OOM
- 《深入理解java虛擬機器》第3版筆記3Java虛擬機筆記
- 《深入理解java虛擬機器》第七章讀書筆記——虛擬機器類載入機制Java虛擬機筆記
- 深入理解java虛擬機器Java虛擬機
- 深入理解Java虛擬機器 - 類載入機制Java虛擬機
- 深入理解Java虛擬機器(類載入機制)Java虛擬機
- 深入理解Java虛擬機器 --- 類載入機制Java虛擬機
- 深入理解Java虛擬機器 – 閱讀class檔案的三種姿勢(連載2)Java虛擬機
- 深入理解Java虛擬機器筆記之二關於物件Java虛擬機筆記物件
- Java 虛擬機器之五:Java位元組碼檔案結構Java虛擬機
- Java虛擬機器記憶體模型學習筆記Java虛擬機記憶體模型筆記
- 深入理解Java虛擬機器(一)Java虛擬機
- 深入理解Java虛擬機器(二)Java虛擬機
- 深入java虛擬機器學習–類的載入機制(四)Java虛擬機機器學習
- Java 虛擬機器之三:Java虛擬機器的記憶體結構Java虛擬機記憶體
- 深入理解JAVA虛擬機器學習筆記18——位元組碼指令2(運算指令)Java虛擬機機器學習筆記
- 深入理解Java虛擬機器筆記-自動記憶體管理機制Java虛擬機筆記記憶體
- 《深入理解java虛擬機器》筆記3——7種垃圾收集器Java虛擬機筆記
- (深入理解 Java虛擬機器)一篇文章帶你深入瞭解Java 虛擬機器類載入器Java虛擬機
- 深入理解JVM(五)Class類的檔案結構JVM
- JVM虛擬機器Class類檔案研究分析JVM虛擬機
- 深入理解Java虛擬機器筆記之一Java執行時資料區Java虛擬機筆記
- 【深入理解Java虛擬機器】垃圾回收Java虛擬機
- JVM學習--Class類檔案結構JVM
- 深入理解Java虛擬機器 --- 垃圾回收器Java虛擬機