Java字節碼(.class文件)格式詳解(三) – JAVA編程語言程序開發技術文章

 

2.11 ClassFilemethod_infofield_info中同時存在的Attribute

2.11.1     Synthetic Attribute

Synthetic Attribute用於指示當前類、接口、方法或字段由編譯器生成,而不在源代碼中存在(不包含類初始函數和實例初始函數)。相同的功能還有一種方式就是在類、接口、方法或字段的訪問權限中設置ACC_SYNTHETIC標記。

 

Synthetic Attribute由JDK1.1中引入,以支持內嵌類和接口(nested classes and interfaces)。但是以我現在所知,這些功能都是可以通過ACC_SYNTHETIC標記來表達的,為什麼還需要存在Synthetic Attribute呢?在什麼樣的情況下會生成Synthetic Attribute項呢?我還沒有找到,需要繼續研究。

Synthetic Attribute

type

descriptor

remark

u2

attribute_name_index

constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute名稱(“Synthetic”)。

u4

attribute_length

該Attribute內容的字節長度(0)。

 

2.11.2     Signature Attribute

Signature Attribute

type

descriptor

remark

u2

attribute_name_index

constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute名稱(“Signature”)。

u4

attribute_length

該Attribute內容的字節長度(2)。

u2

signature_index

constant_pool中的索引,CONSTANT_Utf8_info類型。記錄當前類型的簽名(類簽名、字段簽名、方法簽名)。

JVM規范中沒有指定什麼情況下需要生成Signature Attribute。但是從Signature的目的是用於泛型類型,可以推測Signature Attribute存在於當前Signature Attribute所在類型是泛型(泛型類、泛型方法、泛型字段)的時候。它和field_info、method_info、this_class一起對應於局部變量中的LocalVariableTable Attribute和LocalVariableTypeTable Attribute,他們同時都有descriptor版本和signature版本。

 

2.11.3     Deprecated Attribute

Deprecated Attribute指示當前類、方法、字段已經過時瞭,一些工具,如編譯器可以根據該Attribute提示用戶他們使用的類、方法、字段已經過時瞭,最好使用最新版本的類、方法、字段。

Deprecated Attribute

type

descriptor

remark

u2

attribute_name_index

constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute名稱(“Deprecated”)。

u4

attribute_length

該Attribute內容的字節長度(0)。

 

2.11.4     RuntimeVisibleAnnotations Attribute

RuntimeVisibleAnnotations Attribute記錄瞭當前類、方法、字段在源代碼中定義的、在運行時可見的Annotation。Java程序可以通過反射函數獲取這些Annotation。一個attributes集合中隻能包含一項RuntimeVisibleAnnotations Attribute,記錄所有運行時可見的Annotation。

RuntimeVisibleAnnotations Attribute

type

descriptor

remark

u2

attribute_name_index

constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute名稱(“RuntimeVisibleAnnotations”)。

u4

attribute_length

該Attribute內容的字節長度。

u2

num_annotations

annotations集合長度。

annotation

annotations[num_annotations]

記錄所有運行時可見的annotation的集合。annotation類型詳見附錄E

 

2.11.5     RuntimeInvisibleParameterAnotations Attribute

RuntimeInvisibleAnnotations Attribute記錄瞭當前類、方法、字段在源代碼中定義的、在運行時不可見的Annotation。默認情況下,這些Annotation是不可被Java提供的反射函數獲取的,需要通過和實現相關的機制來獲取這些Annotation。一個attributes集合中隻能包含一項RuntimeInvisibleAnnotations Attribute,記錄所有運行時不可見的Annotation。

RuntimeInvisibleAnnotations Attribute

type

descriptor

remark

u2

attribute_name_index

constant_pool中的索引,CONSTANT_Utf8_info類型。指定Attribute名稱(“RuntimeInvisibleAnnotations”)。

u4

attribute_length

該Attribute內容的字節長度。

u2

num_annotations

annotations集合長度。

annotation

annotations[num_annotations]

記錄所有運行時不可見的annotation的集合。annotation類型詳見附錄E

 

 

總體格式

magic(0xCAFEBABE)

version(major.minor)

constant pool

CONSTANT_Utf8_info(1)

CONSTANT_Integer_info(3)

CONSTANT_Float_info(4)

CONSTANT_Long_info(5)

CONSTANT_Double_info(6)

CONSTANT_Class_info(7)

CONSTANT_String_info(8)

CONSTANT_Fieldref_info(9)

CONSTANT_Methodref_info(10)

CONSTANT_InterfaceMethodref_info(11)

CONSTANT_NameAndType_info(12)

 

access_flags

this_class

super_class

interfaces

fields

access_flags

name

descriptor

attributes

ConstantValue Attribute

Synthetic Attribute

Signature Attribute

Deprecated Attribute

RuntimeVisibleAnnotations Attribute

RuntimeInvisibleAnnotations Attribute

 

 

methods

access_flags

name

descriptor

attributes

Code Attribute

StackMapTable Attribute

LineNumberTable Attribute

LocalVariableTable Attribute

LocalVariableTypeTable Attribute

 

Exceptions Attribute

RuntimeVisibleParameterAnnotations Attribute

RuntimeInvisibleParameterAnnotations Attribute

AnnotationDefault Attribute

Synthetic Attribute

Signature Attribute

Deprecated Attribute

RuntimeVisibleAnnotations Attribute

RuntimeInvisibleAnnotations Attribute

 

 

attributes

InnerClasses Attribute

EnclosingMethod Attribute

SourceFile Attribute

SourceDebugExtension Attribute

Synthetic Attribute

Signature Attribute

Deprecated Attribute

RuntimeVisibleAnnotations Attribute

RuntimeInvisibleAnnotations Attribute

 

 

 

附件A Java字節碼中的類和接口名

 

在Java字節碼中類和接口名主要表現以下幾點:

1.       類和接口名都是以全限定名的方式存放(包名加類或接口名)。

2.       在源代碼中的點分隔符”.”在字節碼中以斜杠”/”代替。如:“java.lang.Object”-> “java/lang/Object

3.       數組類型名做瞭特殊處理。如:“int[][]”-> “[[I”、“Thread[]”->“[Ljava/lang/Thread”。詳見附錄BJava字節碼中的數組類型名

 

 

附件B Java字節碼中的數組類型名

 

在Java中,數組被認為是類,因而它也有對應的類名表示,而Java字節碼為數組名指定瞭特定的格式:

1. 所有數組名都以“[”開頭,n維數組有n個“[”。

2. 對引用類型的數組,在“[”後加“L後加引用類型的全限定名。

3. 對基本類型,在“[”後加基本類型的對應字符

基本類型對應字符表

基本類型

對應字符

byte

B

char

C

double

D

float

F

int

I

long

J

short

S

boolean

Z

 

附件C 描述符(Descriptor

 

描述符(Descriptor)定義瞭字段或方法的類型(A descriptor is a string representing the type of a field or method.這段描述感覺不怎麼精確)。它存放在constant pool中的CONSTANT_Utf8_info類型項中。

 

1.       字段描述符(Field Descriptor

字段描述符是定義瞭字段、局部變量、參數等類型的字符串。即附錄A中的類或接口名。

語法定義:

FieldDescrptor :

FieldType

 

BaseType:B、C、D、F、I、J、S、Z(參考附錄B中的基本類型對應字符表)

ObjectType:LfullClassName;

ArrayType:[+BaseType | [+ObjectType

FieldType:BaseType | ObjectType | ArrayType

如:[[D -> double[][]、[Ljava/lang/Thread; -> Thread[]、I->int、Ljava/lang/Object; -> Object

 

2.       方法描述符(Method Descriptor

方法描述符是定義瞭方法參數、方法返回等信息的字符串。

語法定義:

MethodDescriptor:

         ParameterDescriptor*ReturnDescriptor

 

ParameterDescriptor :FieldType

ReturnDescriptor :FieldType | VoidDescriptor

VoidDescriptor :V

如:void method(int i, Object obj)-> (ILjava/lang/Object;)V

Object getValue()-> ( )Ljava/lang/Object;

Object mymethod(int i, double d, Object o) -> (IDLjava/lang/Object;)Ljava/lang/Object;

 

附件D 簽名(Signature

 

簽名(Signature)定義瞭類、字段或方法的泛型類型信息(A signature is a string representing the generic type of a field or method, or generic type information for a class declaration. 這段描述感覺不怎麼精確)。它也存放在constant pool中的CONSTANT_Utf8_info類型項中。

它存在於Signature Attribute中,隻有包含泛型的類、字段、方法才會產生Signature Attribute

 

簽名信息並不是給JVM用的,而是用於編譯、調試、反射。

 

1.       類簽名

語法定義:

ClassSignature:

FormalTypeParametersopt SuperclassSignature SuperinterfaceSignature*

 

FormalTypeParameters:

<FormalTypeParameter+>

FormalTypeParameter:

Identifier ClassBound InterfaceBound*

ClassBound:

: FieldTypeSignatureopt

InterfaceBound:

: FieldTypeSignature

SuperclassSignature:

ClassTypeSignature

SuperinterfaceSignature:

ClassTypeSignature

FieldTypeSignature:

ClassTypeSignature

ArrayTypeSignature

TypeVariableSignature

ClassTypeSignature:

L PackageSpecifier* SimpleClassTypeSignature

ClassTypeSignatureSuffix* ;

PackageSpecifier:

Identifier / PackageSpecifier*

SimpleClassTypeSignature:

Identifier TypeArgumentsopt

ClassTypeSignatureSuffix:

. SimpleClassTypeSignature

TypeVariableSignature:

T Identifier ;

TypeArguments:

<TypeArgument+>

TypeArgument:

WildcardIndicatoropt FieldTypeSignature

*

WildcardIndicator:

+

ArrayTypeSignature:

[TypeSignature

TypeSignature:

FieldTypeSignature

BaseType

以上定義沒有看懂??例子如:

對class MyClass<T> { } 定義的類,產生如下的簽名:

<T:Ljava/lang/Object;>Ljava/lang/Object;

而對以下類定義:

classMyClass<T1, T2> extendsClassFileParser implementsIndexParser {

}

則產生如下簽名:

<T1:Ljava/lang/Object;T2:Ljava/lang/Object;>Lorg/levin/classfilereader/ClassFileParser;Lorg/levin/classfilereader/IndexParser;

 

2.       字段簽名

語法定義如上,沒能看懂。從Tomcat代碼中的Digester.class文件中可以解析得到如下的例子:

Ljava/util/HashMap<Ljava/lang/String;Ljava/util/Stack<Ljava/lang/String;>;>;(對應的descriptor:“Ljava/util/HashMap;”)

Ljava/util/Stack<Ljava/lang/Object;>;(對應的descriptor:“Ljava/util/Stack;”)

 

3.       方法簽名

語法定義:

MethodTypeSignature:

FormalTypeParametersopt (TypeSignature*) ReturnType

ThrowsSignature*

ReturnType:

TypeSignature

VoidDescriptor

ThrowsSignature:

^ClassTypeSignature

^TypeVariableSignature

也沒能看懂。同樣從Tomcat代碼中的Digester.class文件中可以解析得到如下例子:

(Ljava/lang/String;Ljava/lang/Class<*>;Ljava/lang/String;)V(對應descriptor:“(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;)V”)

(Ljava/lang/String;Ljava/lang/Class<*>;Ljava/lang/String;Z)V(對應descriptor:“(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Z)V”)

()Ljava/util/Map<Ljava/lang/String;Ljava/net/URL;>;(對應descriptor:“()Ljava/util/Map;”)

 

附錄Eannotation結構和element_value結構

 

1.      annotation結構

每一項annotation結構記錄一項用戶定義的annotation的值。如:

    @Test(id = 4, description = "description",useCase = @UseCase())

    @UseCase()

    voidtestExecute(inta) {

    }

編譯器會為該方法生成兩項annotation。每項annotation指定瞭annotation的類型和鍵值對。

annotation結構

type

descriptor

remark

u2

type_index

constant_pool中的索引。CONSTANT_Utf8_info類型。以字段描述符(field descriptor)方式記錄當前結構表示的annotation類型。

u2

num_element_value_pairs

記錄當前annotation中的鍵值對數。

element_value_pair

記錄每項annotation中的鍵值對表。

u2

element_name_index

constant_pool中的索引。CONSTANT_Utf8_info類型。記錄當前annotation中當前鍵值對的鍵名。如上例的“id”、“description”等。

element_value

value

當前annotation中當前鍵值對的值。詳見element_value結構一節。

element_value_pairs[num_element_value_pairs]

 

 

2.      element_value結構

element_value結構記錄瞭所有annotation類型的鍵值對中的值。它是一個聯合類型,可以表示多種類型的值。

element_value結構

type

descriptor

remark

u1

tag

tag記錄瞭當前annotation鍵值對中值的類型,’B’、’C’、’D’、’F’、’I’、’J’、’S’、’Z’表示基本類型(見附錄B中的基本類型對應表);其他的合法值有:

’s’ -> String

‘e’ -> enum constant

‘c’ -> class

‘@’ -> annotation type

‘[‘ -> array

value 聯合體類型(union)

union類型,記錄當前annotaion鍵值對中的值。

u2

constant_value_index

constant_pool中的索引,索引項必須是常量類型。當tag中的值為’B’ ‘C’ ‘D’ ‘F’ ‘I’ ‘J’ ‘S’ ‘Z’ ‘s’時該項有效。

enum_const_value

當tag值為’e’時,該項有效。記錄枚舉類型值。

u2

type_name_index

constant_pool中的索引,CONSTANT_Utf8_info類型。記錄當前枚舉類型二進制名(binary name,好像就是類型名,以descriptor的形式表示)。

u2

const_name_index

constant_pool中的索引,CONSTANT_Utf8_info類型。記錄當前枚舉類型的值(枚舉類型內部成員字符串)。

enum_const_value

u2

class_info_index

constant_pool中的索引,CONSTANT_Utf8_info類型。以descriptor記錄當前值所表達的Class類型。當tag值為’c’時,該項有效。

annotation

annotation_value

當tag值為’@’時,該項有效。記錄當前annotation鍵值對中的值為內嵌的annotation。

array_value

當tag值為’[‘時,該項有效。記錄當前annotation鍵值對中的值為數組類型。

u2

num_values

數組的長度。

element_value

values[num_values]

每一項記錄數組中的值。

array_value

value

 

註:從這個結構中,我們也可以得出annotation中可以設置的值類型:

1.       基本類型值(byte、char、double、float、int、long、short、boolean)

2.       字符串(String)

3.       枚舉(enum)

4.       類實例(Class)

5.       嵌套註解類型(annotation)

6.       以上所有以上類型的一維數組。

作者“上善若水”

發佈留言