目錄java
任何一個Class文件都對應着惟一一個類或者接口的定義信息,可是類或者接口並不必定都要定義在文件裏(例如類也能夠經過類加載器直接生成)。Class文件是一組以8位字節爲基礎單位的二進制流,各項數據項目嚴格按照順序緊湊地排列在Class文件中。Class文件格式採用相似C語言結構體的僞結構來存儲數據,包括兩種數據類型:無符號數和表。
無符號數屬於基本的數據類型,以u1,u2,u4,u8來表明1,2,4,8個字節的無符號數。能夠用來描述數字、索引引用、數量值或者按照utf-8編碼構成字符串值。
表是由多個無符號數或者其餘表做爲數據項構成的複合數據類型,全部表都習慣性的以「_info」結尾。表用於描述有層次關係的符合結構數據。sql
Class文件的頭4個字節稱之爲魔數(Magic Number),用於確認這個文件是否爲一個能被虛擬機接受的Class文件。Class文件的魔數值爲:0xCAFEBABE(咖啡寶貝?),這個魔數在Java仍是「Oak」的時候就被確認了。
以後4個字節存儲的是Class文件的版本號,第5第6個字節是次版本號(Minor Version),第7第8是主版本號(Major Version)。Java的版本號是從45開始的,1.1以後大版本發佈主版本號向上加1,高版本JDK能向下兼容但不能運行之後版本的Class文件。性能優化
主次版本號以後時常量池,Class文件之中的資源倉庫,關聯其餘項目最多的數據類型,也是佔用Class文件空間最大的項目之一。因爲常量數量不固定,因此常量池的入口須要防止一項u2類型數據,表明常量池容量計數值(constant_pool_count)。
常量池中主要存放兩大類常量:字面量(Literal)和符號引用(Symbolic References)。字面量比較接近與Java語言層面的常量概念,如文本字符串、聲明爲final的常量值等。符號引用則屬於編譯原理方面的概念,包括下面三類常量:性能
Java代碼在Javac編譯的時候,不像C或C++有鏈接這一步驟,而是在虛擬機加載Class文件的時候進行動態鏈接。虛擬機運行時,從常量池得到對應的符號引用,在類建立時或運行時解析、翻譯到具體的內存地址之中。學習
常量池以後緊接着兩個字節表明訪問標誌(access_flag),用於識別一些類或者接口層次的訪問信息。包括:是類仍是接口,是否public,是否abstract,是否final等。測試
標誌名稱 | 標誌值 | 含義 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否爲public |
ACC_FINAL | 0x0010 | 是否爲final |
ACC_SUPER | 0x0020 | 是否容許使用invokespecial字節碼指令的新語意,因爲該語意在JDK1.0.2發生過改變,因此以後編譯出來的類這個標誌必須爲真 |
ACC_INTERFACE | 0x0200 | 是否爲接口 |
ACC_ABSTRACT | 0x0400 | 是否爲抽象類 |
ACC_SYNTHETIC | 0x1000 | 是否由編譯器自動產生的 |
ACC_ANNOTATION | 0x2000 | 是否爲註解類 |
ACC_ENUM | 0x4000 | 是否爲enum |
例如public的普通類,因此ACC_PUBLIC ACC_SUPE標誌爲真,其餘標誌爲假。所以access_flags的值爲: 0x0001|0x0020=0x0021
前5個是Java虛擬機規範定義標誌,1.5以後增長了後面的三種。優化
類索引(this_class)父類索引(super_class)是u2類型數據,接口索引集合(interfaces)時一組u2類型的數據集合。按照順序排在訪問標誌以後。this
類型 | 名稱 | 數量 |
---|---|---|
u2 | 類索引 | 1 |
u2 | 父類索引 | 1 |
u2[n] | 接口索引 | n |
字段表(field_info)用於描述接口或者類中聲明的變量。字段包括類級變量以及實例級別變量,但不包括方法內部聲明的局部變量。修飾符使用標誌量表示,字段名稱類型使用常量池中的常量來描述。
access_flag的含義編碼
標誌名稱 | 標誌值 | 含義 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否爲public |
ACC_PRIVATE | 0x0002 | 是否爲private |
ACC_PROTECTED | 0x0004 | 是否爲protected |
ACC_STATIC | 0x0008 | 是否爲static |
ACC_FINAL | 0x0010 | 是否爲final |
ACC_VOLATILE | 0x0040 | 是否爲volatile |
ACC_TRANSIENT | 0x0080 | 是否爲transient |
ACC_SYNTHETIC | 0x1000 | 是否由編譯器自動產生的 |
ACC_ENUM | 0x4000 | 是否爲enum |
類型 | 名稱 | 數量 |
---|---|---|
u2 | access_flag | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
方法表(method_info)用於描述接口或者類中聲明的方法。
由於volatile關鍵字和transient關鍵字不能修飾方法,因此方法表的訪問標誌中沒有了 ACC_VOLATILE標誌和ACC_TRANSIENT標誌。與之相對的,synchronized、native、strictfp 和abstract關鍵字能夠修飾方法,因此方法表的訪問標誌中增長了ACC_SYNCHRONIZED、 ACC_NATIVE、ACC_STRICTFP和ACC_ABSTRACT標誌。翻譯
標誌名稱 | 標誌值 | 含義 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否爲public |
ACC_PRIVATE | 0x0002 | 是否爲private |
ACC_PROTECTED | 0x0004 | 是否爲protected |
ACC_STATIC | 0x0008 | 是否爲static |
ACC_FINAL | 0x0010 | 是否爲final |
ACC_SYNCHRONIZED | 0x0020 | 是否爲synchronized |
ACC_BRIDGE | 0x0040 | 是否爲編譯器產生的橋接方法 |
ACC_VARARGS | 0x0080 | 是否接受不定參數 |
ACC_NATIVE | 0x0100 | 是否爲native |
ACC_ABSTRACT | 0x0400 | 是否爲abstract |
ACC_STRICTFP | 0x0800 | 是否爲strictfp |
ACC_SYNTHETIC | 0x1000 | 是否由編譯器自動產生的 |
類型 | 名稱 | 數量 |
---|---|---|
u2 | access_flag | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
屬性表(attribute_info)以前反覆出現,在Class文件、字段表、方法表均可以攜帶字節的屬性表集合,用於描述某些場景專有信息。
順序、長度、內容的要求不像前面那麼嚴格。
不要求各個屬性表具備嚴格順序,而且只要不與已有屬性名重複,任何人 實現的編譯器均可以向屬性表中寫入本身定義的屬性信息,Java虛擬機運行時會忽略掉它不 認識的屬性。
屬性名稱 | 使用位置 | 含義 |
---|---|---|
Code | 方法表 | Java代碼編譯成的字節碼指令 |
ConstantValue | 字段表 | final關鍵字定義的常量值 |
Deprecated | 類、方法表、字段表 | 聲明爲deprecated的方法和字段 |
Exceptions | 方法表 | 方法拋出的異常 |
EnclosingMethod | 類文件 | 僅當一個類爲局部類或者匿名類纔有這個屬性,用於標識這個類所在的外圍方法 |
...
屬性表結構:
類型 | 名稱 | 數量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u1 | info | attribute_length |
表用於描述有層次關係的符合結構的數據,整個Class文件本質上就是一張表,其構成成分就是以下的數據項:
數據類型 | 名稱 | 數量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | max_stack | 1 |
u2 | max_locals | 1 |
u4 | code_length | 1 |
u1 | code | code_length |
u2 | exception_table_length | 1 |
exception_info | exception_table | exception_table_length |
u2 | attribute_count | 1 |
attribute_info | attributes | attribute_count |
爲了可以更好的理解,咱們拿一個實際的類文件來分析一下。
package com.software5000.base.jsql; public class TestClass { public String strField; public int intField; public String getStrField() { return strField; } public void setStrField(String strField) { this.strField = strField; } public int getIntField() { return intField; } public void setIntField(int intField) { this.intField = intField; } }
這是一個很簡單的測試類,兩個屬性,以及對應的getter/setter方法。
針對類文件以及前面的概念對比分析
CAFEBABE - 魔數
00000034 - 版本 52 :jdk 1.8
0020 - 32-1 長度31個常量項
01——
0A tag表示 constant_methodref_info
0005 index no. 5 contant_class_info
001B index no. 27 constant_nameandtype
02——
09 tag constant_fieldref_info
0004 index no.4 constant_class_info
001C index no.28 constatn_nameandtype
03——
09 tag constant_fieldref_info
0004 index no.4 constant_class_info
001D index no.29 constatn_nameandtype
04——
07 tag constant_class_info
001E index no.30
05——
07 tag constant_class_info
001F index no.31
06——
01 tag constant_utf8_info
0008 length 8 byte
7374 7246 6965 6C64 strField
07——
01 tag constant_utf8_info
0012 length 18 byte
4C6A 6176 612F 6C61 6E67 2F53 7472 696E 673B Ljava/lang/String;
08——
01 tag constant_utf8_info
0008 length 8 byte
696E 7446 6965 6C64 intField
09——
01 tag constant_utf8_info
0001 length 1 byte
49 I
10——
01 tag constant_utf8_info
0006 length 6 byte
3C69 6E69 743E
11——
01 tag constant_utf8_info
0003 length 3 byte
2829 56 ()V
12——
01 tag constant_utf8_info
0004 length 4 byte
436F 6465 Code
13——
01 tag constant_utf8_info
000F length 15 byte
4C69 6E65 4E75 6D62 6572 5461 626C 65 LineNumberTable
14——
01 tag constant_utf8_info
0012 length 18 byte
4C6F 6361 6C56 6172 6961 626C 6554 6162 6C65 LocalVariableTable
15——
01 tag constant_utf8_info
0004 length 4 byte
7468 6973 this
16——
01 tag constant_utf8_info
0026 length 38 byte
4C63 6F6D 2F73 6F66 7477 6172 6535 3030 302F 6261 7365 2F6A 7371 6C2F 5465 7374 436C 6173 733B Lcom/software5000/base/jsql/TestClass;
17——
01 tag constant_utf8_info
000B length 11 byte
67 6574 5374 7246 6965 6C64 getStrField
18——
01 tag constant_utf8_info
0014 length 20 byte
2829 4C6A 6176 612F 6C61 6E67 2F53 7472 696E 673B ()Ljava/lang/String;
19——
01 tag constant_utf8_info
000B length 11 byte
7365 7453 7472 4669 656C 64 setStrField
20——
01 tag constant_utf8_info
0015 length 21 byte
284C 6A61 7661 2F6C 616E 672F 5374 7269 6E67 3B29 56 (Ljava/lang/String;)V
21——
01 tag constant_utf8_info
000B length 11 byte
6765 7449 6E74 4669 656C 64 getIntField
22——
01 tag constant_utf8_info
0003 length 3 byte
2829 49 ()I
23——
01 tag constant_utf8_info
000B length 11 byte
7365 7449 6E74 4669 656C 64 setIntField
24——
01 tag constant_utf8_info
0004 length 4 byte
2849 2956 (I)V
25——
01 tag constant_utf8_info
000A length 10 byte
536F 7572 6365 4669 6C65 SourceFile
26——
01 tag constant_utf8_info
000E length 14 byte
5465 7374 436C 6173 732E 6A61 7661 TestClass.java
27——
0C tag constant_Name-andtype-info
000A index no.10
000B index no.11
28——
0C tag constant_Name-andtype-info
0006 index no.6
0007 index no.7
29——
0C tag constant_Name-andtype-info
0008 index no.8
0009 index no.9
30——
01 tag constant_utf8_info
0024 length 36 byte
5636F 6D2F 736F 6674 7761 7265 3530 3030 2F62 6173 652F 6A73 716C 2F54 6573 7443 6C61 7373 com/software5000/base/jsql/TestClass
31——
01 tag constant_utf8_info
0010 length 16 byte
6A61 7661 2F6C 616E 672F 4F62 6A65 6374 java/lang/Object
0021 訪問標誌 (ACC_PUBLIC,ACC_SUPER兩個標誌位爲真)
0004 類索引 no.4
0005 父類索引 no.5
0000 接口索引集合 0
0002 fields_counts 2個字段表數據
No.1 field
0001 access_flag public
0006 index no.6 name_index
0007 index no.7 descriptor_index
0000 attributes_count
No.2 field
0001 access_flag public
0008 index no.8 name_index
0009 index no.9 descriptor_index
0000 attributes_count
0005 methods_count 5個方法
No.1 method
0001 access_flag public
000A index no.10 name_index
000B index no.11 descriptor_index ()V
0001 attributes_count 1個
000C attributes_info LineNumberTable
剩餘的待進一步補充。。。
Class文件的結構理解,說實話對於編碼或者性能優化等方面沒有什麼特別大的幫助,可是可以幫咱們更好的理解Java及其設計的思想。咱們從Class文件的結構設計中也可以學習到一些模式,這些東西可能會在後續的研發過程提供一些解決問題或者設計方案的思路。 這纔是最重要的。