深入理解Java虛擬機讀書筆記之:第6章 Java class文件

    Java class文件是對Java程序二進制文件格式的精確定義。每一個Java class文件都對一個Java類或者Java接口作出了全面描述。一個class文件中只能包含一個類或者接口。
    儘管class文件與Java語言結構相關,但它並不一定必須與Java語言相關。如下圖,可以使用其他語言來編寫程序,然後將其編譯爲class文件,或者把Java程序編譯爲另一種不同的二進制文件格式。


 
(注:如Scala、Groovy、JRuby等基於JVM的語言)
 
    Java class文件是8位字節的二進制流。數據項按順序存儲在class文件中,相鄰的項之間沒有任何間隔,這樣可以使class文件緊湊。佔據多個字節空間的項按照高位在前的順序分爲幾個連續的字節存放。
    在class文件中,可變長度項的大小和長度位於其實際數據之前。這個特性使得class文件流可以從頭到尾被順序解析,首先讀出項的大小,然後讀出項的數據。
 
class文件的內容
    Java class文件中包含了Java虛擬機所需知道的、關於類或接口的所有信息。

class文件“基本類型”

類 型

描 述

u1

1個字節,無符號類型

u2

2個字節,無符號類型

u4

4個字節,無符號類型

u8

8個字節,無符號類型

 
    可變長度的ClassFile表中的項,按照它們在class文件中出現的順序列出了主要部分。

ClassFile表的格式

 

 

 

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

 
1)magic(魔數)
    每個Java class文件的前4個字節被稱爲它的魔數(magic number):0xCAFEBABE。魔數的作用在於,可以輕鬆地分辨出Java class文件和非Java class文件。
 
2)minor_version和major_version
    class文件的下面4個字節包含了主、次版本號。對於Java虛擬機來說,版本號確定了特定的class文件格式,通常只有給定主版本號和一系列次版本號後,Java虛擬機才能夠讀取class文件。
 
3)constant_pool_count和constant_pool
    魔數和版本號後面的是常量池。常量池包含了與文件中類和接口相關的常量。常量池中存儲了諸如文字字符串、final變量值、類名和方法名的常量。Java虛擬機把常量池組織爲入口列表的形式。在實際列表constant_pool之前,是入口在列表中的計數constant_pool_count。
    每個常量池入口都從一個長度爲一個字節的標誌開始,這個標誌指出了列表中該位置的常量類型。
    每一個標誌都有一個相對應的表,表名通過在標誌名後加上“_info”後綴來產生。

常量池標誌

入 口 類 型

標 志 值

描 述

CONSTANT_Utf8

1

UTF-8編碼的Unicode字符串

CONSTANT_Integer

3

int類型字面值

CONSTANT_Float

4

float類型字面值

CONSTANT_Long

5

long類型字面值

CONSTANT_Double

6

double類型字面值

CONSTANT_Class

7

對一個類或接口的符號引用

CONSTANT_String

8

String類型字面值

CONSTANT_Fieldref

9

對一個字段的符號引用

CONSTANT_Methodref

10

對一個類中聲明的方法的符號引用

CONSTANT_InterfaceMethodref

11

對一個接口中聲明的方法的符號引用

CONSTANT_NameAndType

12

對一個字段或方法的部分符號引用

 
    除了字面常量(或者說直接量)值以外,常量池還可以容納下面幾種符號引用:
  • 類和接口的全限定名
  • 字段的名稱和描述符
  • 方法的名稱和描述符
4)access_flags
    緊接常量池後的兩個字節稱爲access_flags,它展示了文件中定義的類或接口的幾段信息。

access_flags項的標誌位

  

設置後的含義

  

ACC_PUBLIC

0x0001

public類型

類和接口

ACC_FINAL

0x0010

類爲final類型

只有類

ACC_SUPER

0x0020

使用新型的invokespecial語義

類和接口

ACC_INTERFACE

0x0200

接口類型,不是類類型

所有的接口,沒有類

ACC_ABSTRACT

0x0400

abstract類型

所有的接口,部分類

    在access_flags中所有未使用的位都必須由編譯器置0,而且Java虛擬機必須忽略它。
 
5)this_class
    接下來的兩個字節爲this_class項,它是一個對常量池的索引。在this_class位置的常量池入口必須爲CONSTANT_Class_info表。該表由兩個部分組成——標籤和name_index。標籤部分是一個具有CONSTANT_Class值的常量,在name_index位置的常量池入口爲一個包含了類或接口全限定名的CONSTANT_Utf8_info表。


 
6)super_class
    緊接在this_class之後的是super_class項,它是一個兩個字節的常量池索引。在super_class位置的常量池入口是一個指向該類超類全限定名的CONSTANT_Class_info入口。
 
7)interfaces_count和interfaces
    緊接着super_class是interfaces_count。此項的含義爲:在文件中由該類直接實現或者由接口所擴展的父接口的數量。在這個計數的後面,是名爲interfaces的數組,它包含了對每個由該類或者接口直接實現(注:只包含直接出現在implements、extends子句中的父接口)的父接口的常量池索引。
 
8)fields_count和fields
    緊接在interfaces後面的是對在該類或者接口中所聲明的字段的描述。首先是名爲fields_count的計數,它是類變量和實例變量的字段的數量總和。在這個計數後面的是不同長度的field_info表的序列(fields_count指出了序列中有多少個field_info表)。在fields列表中,不列出從超類或者父接口繼承而來的字段。
 
9)methods_count和methods
    緊接着fields後面的是對在該類或者接口中所聲明的方法的描述。只包括在該類或者接口中顯式定義的方法。
 
10)attributes_count和attributes
    class文件中最後的部分是屬性(attribute),它給出了在該文件中類或者接口所定義的屬性的基本信息。
 
特殊字符串
    常量池中容納的符號引用包括三種特殊的字符串:全限定名、簡單名稱和描述符。
 
全限定名
    當常量池入口指向類或者接口時,它們給出該類或者接口的全限定名。在class文件中,全限定名中的點用斜線取代了。
 
簡單名稱
    字段名和方法名以簡單名稱(非全限定名)形式出現在常量池入口中。
 
描述符
    字段的描述符給出了字段的類型;方法描述符給出了方法的返回值和方法參數的數量、類型以及順序。
    字段和方法的描述符由如下所示的上下文無關語法定義。該語法中非終結符號用斜體字標出,如FieldType;終結符號使用等寬度字體標出,如BV;星號代表緊接在它前面的符號(中間沒有空格)將會出現0次或者多次。
 
FieldDescriptor:
        FieldType
ComponentType:
        FieldType
FieldType:
        BaseType
        ObjectType
        ArrayType
BaseType:
        B
        C
        D
        F
        I
        J
        S
        Z
ObjectType:
        L<classname>;
ArrayType:
        [ComponentType
MethodDescriptor:
        (ParameterDescriptor*)ReturnDescriptor
ParameterDescriptor:
        FieldType
ReturnDescriptor:
        FieldType
        V
 
V        表示方法返回值爲void類型
L;      對象類型終結符
[         數組類型終結符
()      方法描述符終結符
 

基本類型終結符

  

 

B

byte

C

char

D

double

F

float

I

int

J

long

S

short

Z

boolean


字段描述符示例

描 述 符

字 段 聲 明

I

int i;

[[J

long[][] windingRoad;

[Ljava/lang/Object;

java.lang.Object[] stuff;

Ljava/util/Hashtable;

java.util.Hashtable ht;

[[[Z

boolean[][][] isReady;

 

方法描述符示例

  

   

()I

int getSize();

()Ljava/lang/String;

String toString();

([Ljava/lang/String;)V

void main(String[] args);

()V

void wait();

(JI)V

void wait(long timeout, int nanos);

(ZILjava/lang/String;II)Z

boolean regionMatches(boolean ignoreCase, int toOffset, String other, int offset, int len);

([BII)I

int read(byte[] b, int off, int len);



常量池
    常量池是一個可變長度cp_info表的有序序列。cp_info表一共有11種類型。

cp_info表的通常形式

 

 

 

 

u1

tag

1

表的類型和格式

u1

info

根據tag值決定

 


1)CONSTANT_Utf8_info表
    可變長度的CONSTANT_Utf8_info表使用一種UTF-8格式的變體來存儲一個常量字符串。這種類型的表可以存儲多種字符串,包括:
  • 文字字符串,如String對象。
  • 被定義的類和接口的全限定名。
  • 被定義的類的超類(如果有的話)的全限定名。
  • 被定義的類和接口的父接口的全限定名。
  • 由類或者接口聲明的任意字段的簡單名稱和描述符。
  • 由類或者接口聲明的任意方法的簡單名稱和描述符。
  • 任何引用的類和接口的全限定名。
  • 任何引用的字段的簡單名稱和描述符。
  • 任何引用的方法的簡單名稱和描述符。
  • 與屬性相關的字符串。

CONSTANT_Utf8_info表的格式

 

 

 

 

u1

tag

1

值爲CONSTANT_Utf81

u2

length

1

bytes項的長度(字節數)

u1

bytes

length

按照變體UTF-8格式存儲的字符串中的字符


2)CONSTANT_Integer_info表的格式

CONSTANT_Integer_info表的格式

 

 

 

 

u1

tag

1

值爲CONSTANT_Integer3

u4

bytes

1

按照高位在前的格式存儲int類型值


3)CONSTANT_Float_info表的格式

CONSTANT_Float_info表的格式

 

 

 

 

u1

tag

1

值爲CONSTANT_Float4

u4

bytes

1

按照高位在前的格式存儲float類型值


4)CONSTANT_Long_info表的格式

CONSTANT_Long_info表的格式

 

 

 

 

u1

tag

1

值爲CONSTANT_Long5

u8

bytes

1

按照高位在前的格式存儲long類型值


5)CONSTANT_Double_info表的格式

CONSTANT_Double_info表的格式

 

 

 

 

u1

tag

1

值爲CONSTANT_Double6

u8

bytes

1

按照高位在前的格式存儲double類型值


6)CONSTANT_Class_info表的格式

CONSTANT_Class_info表的格式

 

 

 

 

u1

tag

1

值爲CONSTANT_Class7

u2

name_index

1

包含類或者接口全限定名的CONSTANT_Utf8_info表的索引


7)CONSTANT_String_info表的格式

CONSTANT_String_info表的格式

 

 

 

 

u1

tag

1

值爲CONSTANT_String8

u2

string_index

1

包含文字字符串值的CONSTANT_Utf8_info表的索引


8)CONSTANT_Fieldref_info表的格式

CONSTANT_Fieldref_info表的格式

 

 

 

 

u1

tag

1

值爲CONSTANT_Fieldref9

u2

class_index

1

聲明被引用字段的類或者接口的CONSTANT_Class_info入口的索引

u2

name_and_type_index

1

提供了CONSTANT_NameAndType_info入口的索引,該入口提供了字段的簡單名稱以及描述符


9)CONSTANT_Methodref_info表的格式

CONSTANT_Methodref_info表的格式

 

 

 

 

u1

tag

1

值爲CONSTANT_Methodref10

u2

class_index

1

聲明被引用方法的類的CONSTANT_Class_info入口的索引

u2

name_and_type_index

1

提供了CONSTANT_NameAndType_info入口的索引,該入口提供了方法的簡單名稱以及描述符


10)CONSTANT_InterfaceMethodref_info表的格式

CONSTANT_InterfaceMethodref_info表的格式

 

 

 

 

u1

tag

1

值爲CONSTANT_InterfaceMethodref11

u2

class_index

1

聲明被引用方法的接口的CONSTANT_Class_info入口的索引

u2

name_and_type_index

1

提供了CONSTANT_NameAndType_info入口的索引,該入口提供了方法的簡單名稱以及描述符


11)CONSTANT_NameAndType_info表的格式

CONSTANT_NameAndType_info表的格式

 

 

 

 

u1

tag

1

值爲CONSTANT_NameAndType12

u2

name_index

1

給出了CONSTANT_Utf8_info入口的索引,該入口給出了字段或者方法的名稱

u2

descriptor_index

1

提供了CONSTANT_Utf8_info入口的索引,該入口提供了字段或者方法的描述符

 
字段
    在類或者接口中聲明的每一個字段(類變量或者實例變量)都由class文件中的一個名爲field_info的可變長度的表進行描述。

field_info表的格式

 

 

 

 

u2

access_flags

1

見下方的表

u2

name_index

1

提供了給出字段簡單名稱(不是全限定名)的CONSTANT_Utf8_info入口的索引

u2

descriptor_index

1

提供了給出字段描述符的CONSTANT_Utf8_info入口的索引

u2

atrributes_count

1

attributes_count指出列表中attribute_info表的數量

attribute_info

atrributes

atrributes_count

由多個attribute_info表組成的列表


field_info表中access_flags項的標誌

   

   

  

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_PUBLIC、ACC_PRIVATE、ACC_PROTECTED這三個標誌中的一個。ACC_FINAL和ACC_VOLATILE不能同時設置。所有接口中聲明的字段必須有且只能有ACC_PUBLIC、ACC_STATIC和ACC_FINAL這三種標誌。
 
方法
    在class文件中,每個在類和接口中聲明的方法,或者由編譯器產生的方法,都由一個可變長度的method_info表來描述。

method_info表的格式

 

 

 

 

u2

access_flags

1

見下方的表

u2

name_index

1

提供了給出方法簡單名稱(不是全限定名)的CONSTANT_Utf8_info入口的索引

u2

descriptor_index

1

提供了給出方法描述符的CONSTANT_Utf8_info入口的索引

u2

atrributes_count

1

attributes_count指出列表中attribute_info表的數量

attribute_info

atrributes

atrributes_count

由多個attribute_info表組成的列表


method_info表中access_flags項的標誌

   

   

  

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_NATIVE

0x0100

方法設爲native

只有類

ACC_ABSTRACT

0x0400

方法設爲abstract

類和所有的接口方法

ACC_STRICT

0x0800

方法設爲strictFP

類和接口的<clinit>方法


 
屬性
    屬性在Java class文件中多處出現。它們可以出現在ClassFile、field_info、method_info和Code_attribute表中。Code_attribute表本身即爲一個屬性。
    Java虛擬機規範定義了9種屬性。爲了正確地解釋Java class文件,所有Java虛擬機實現都必須能夠識別下列三種屬性:Code,ConstantValue和Exception。爲了正確地實現Java和Java 2平臺類庫,虛擬機實現必須能夠識別InnerClasses和Synthetic屬性,但可以自主選擇究竟是識別還是忽略其他一些預定義的屬性。
 

由規範定義的attribute_info表的類型

 

使  

 

Code

method_info

方法的字節碼和其他數據

ConstantValue

field_info

final變量的值

Deprecated

field_infomethod_info

字段或者方法被禁用的指示符

Exceptions

method_info

方法可能拋出的可被檢測的異常

InnerClasses

ClassFile

內部、外部類的列表

LineNumberTable

Code_attribute

方法的行號與字節碼的映射

LocalVariableTable

Code_attribute

方法的局部變量的描述

SourceFile

ClassFile

源文件名

Synthetic

field_infomethod_info

編譯器產生的字段或者方法的指示符


attribute_info表的格式

 

 

 

 

u2

attribute_name_index

1

給出了包含屬性名稱的CONSTANT_Utf8入口的常量池中的索引

u4

attribute_length

1

給出了屬性數據的長度(以字節計)

u1

info

attribute_length

包含屬性數據


 
最後,簡單總結一下Java class文件:
  • 一個class文件中只能包含一個類或者接口
  • 儘管class文件與Java語言結構相關,但它並不一定必須與Java語言相關
  • Java class文件是8位字節的二進制流
  • 數據項按順序存儲
  • 文件緊湊
  • 多字節的項按照高位在前的格式存儲
  • 常量池至關重要,包含了與class文件中類和接口相關的常量
    
    在此說明,本系列文章的內容均出自《深入理解Java虛擬機》一書,除了極少數的“注”或對內容的裁剪整理外,內容原則上與原書保持一致。由於這是一本原理性的書籍,本人不想因爲自己能力與理解的問題對大家造成誤解,所以除了對原書內容的裁剪整理之外,基本不做任何內容的延伸思考與擴展。
    另外,如果您對本系列文章的內容感興趣,建議您去閱讀原版書籍,謝謝!
 
(轉載請註明來源:http://zhanjia.iteye.com/blog/1857709)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章