第4章 Class 文件格式
提示
来自deepseek解释
原文链接:https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html
本章描述了Java虚拟机的class文件格式。每个class文件包含单个类、接口或模块的定义。尽管类、接口或模块不一定需要在文件中存在字面上的外部表示(例如,因为该类是由类加载器生成的),但我们仍将通俗地将类、接口或模块的任何有效表示称为处于class文件格式中。
一个class文件由8位字节流组成。16位和32位的量分别通过读取连续的2个和4个8位字节来构造。多字节数据项始终按大端序存储,其中高位字节在前。本章定义了数据类型u1、u2和u4,分别表示无符号的1字节、2字节或4字节量。
在Java SE平台API中,class文件格式由接口java.io.DataInput和java.io.DataOutput以及类(如java.io.DataInputStream和java.io.DataOutputStream)支持。例如,类型u1、u2和u4的值可以通过接口java.io.DataInput中的readUnsignedByte、readUnsignedShort和readInt等方法读取。
本章使用类似C的结构表示法,以伪结构的形式呈现class文件格式。为避免与类及类实例的字段等混淆,描述class文件格式的结构中的内容被称为项。连续的项按顺序存储在class文件中,没有填充或对齐。
在几个class文件结构中使用了由零个或多个可变大小项组成的表。尽管我们使用类似C的数组语法来引用表项,但表是由可变大小结构组成的流这一事实意味着无法直接将表索引转换为表中的字节偏移量。
当我们引用数据结构为数组时,它由零个或多个连续的固定大小的项组成,并且可以像数组一样被索引。
本章中对ASCII字符的引用应解释为与该ASCII字符对应的Unicode码点。
4.1 ClassFile结构
一个class文件由单个ClassFile结构组成:
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}ClassFile结构中的项如下:
magicmagic项提供了标识class文件格式的魔数,其值为0xCAFEBABE。
minor_version, major_versionminor_version和major_version项的值分别是此class文件的次版本号和主版本号。主版本号和次版本号共同决定了class文件格式的版本。如果一个class文件的主版本号为M,次版本号为m,我们将其class文件格式版本表示为M.m。
符合Java SE N规范的Java虚拟机实现必须完全支持表4.1-A第四列中指定的class文件格式主版本号。表示法A .. B表示从A到B(包括A和B)的主版本号。第三列“Major”显示了每个Java SE版本引入的主版本号,即第一个能够接受包含该major_version项的class文件的版本。对于非常早期的版本,显示的是JDK版本而不是Java SE版本。
表4.1-A. class文件格式主版本号
| Java SE版本 | 发布年份 | 引入的主版本 | 支持的主版本 |
|---|---|---|---|
| 1.0.2 | 1996年5月 | 45 | 45 |
| 1.1 | 1997年2月 | 45 | 45 |
| 1.2 | 1998年12月 | 46 | 45..46 |
| 1.3 | 2000年5月 | 47 | 45..47 |
| 1.4 | 2002年2月 | 48 | 45..48 |
| 5.0 | 2004年9月 | 49 | 45..49 |
| 6 | 2006年12月 | 50 | 45..50 |
| 7 | 2011年7月 | 51 | 45..51 |
| 8 | 2014年3月 | 52 | 45..52 |
| 9 | 2017年9月 | 53 | 45..53 |
| 10 | 2018年3月 | 54 | 45..54 |
| 11 | 2018年9月 | 55 | 45..55 |
| 12 | 2019年3月 | 56 | 45..56 |
| 13 | 2019年9月 | 57 | 45..57 |
| 14 | 2020年3月 | 58 | 45..58 |
| 15 | 2020年9月 | 59 | 45..59 |
| 16 | 2021年3月 | 60 | 45..60 |
| 17 | 2021年9月 | 61 | 45..61 |
对于主版本号为56或以上的class文件,次版本号必须为0或65535。
对于主版本号在45到55之间(包含端点)的class文件,次版本号可以是任何值。
关于JDK对class文件格式版本的支持,有必要回顾一下历史。JDK 1.0.2支持版本45.0到45.3(包含端点)。JDK 1.1支持版本45.0到45.65535(包含端点)。当JDK 1.2引入对主版本46的支持时,该主版本下支持的次版本仅为0。后来的JDK延续了引入新主版本(47、48等)的做法,但在新主版本下仅支持次版本0。最后,Java SE 12中预览功能的引入(见下文)促使次版本在class文件格式版本中发挥了标准作用。后续的JDK引入对N.0和N.65535的支持,其中N是所实现的Java SE平台对应的主版本号。例如,JDK 13支持57.0和57.65535。
Java SE平台可以定义预览功能。符合Java SE N(N ≥ 12)规范的Java虚拟机实现必须支持Java SE N的所有预览功能,而不支持任何其他Java SE版本的预览功能。该实现必须默认禁用所支持的预览功能,并且必须提供一种启用所有这些功能的方法,且不得提供仅启用其中一部分功能的方法。
如果一个class文件的主版本号对应于Java SE N(根据表4.1-A),且次版本号为65535,则称该class文件依赖于Java SE N(N ≥ 12)的预览功能。
符合Java SE N(N ≥ 12)规范的Java虚拟机实现必须按如下方式运行:
- 仅当Java SE
N的预览功能被启用时,才能加载依赖于Java SEN预览功能的class文件。 - 依赖于其他Java SE版本预览功能的
class文件绝不能被加载。 - 不依赖于任何Java SE版本预览功能的
class文件,无论Java SEN的预览功能是否被启用,都可以被加载。
constant_pool_countconstant_pool_count项的值等于constant_pool表中的条目数加一。如果常量池索引大于零且小于constant_pool_count,则该索引被认为是有效的,但§4.4.5中提到的long和double类型的常量除外。
constant_pool[]constant_pool是一个由结构(§4.4)组成的表,表示各种字符串常量、类和接口名称、字段名称以及在ClassFile结构及其子结构中引用的其他常量。每个constant_pool表项的格式由其第一个“标签”字节指示。
constant_pool表从1索引到constant_pool_count - 1。
access_flagsaccess_flags项的值是一个标志掩码,用于表示该类或接口的访问权限和属性。每个标志(当被设置时)的解释在表4.1-B中指定。
表4.1-B. 类访问和属性修饰符
| 标志名称 | 值 | 解释 |
|---|---|---|
ACC_PUBLIC | 0x0001 | 声明为public;可从其包外部访问。 |
ACC_FINAL | 0x0010 | 声明为final;不允许有子类。 |
ACC_SUPER | 0x0020 | 当由invokespecial指令调用时,特殊处理超类方法。 |
ACC_INTERFACE | 0x0200 | 是接口,不是类。 |
ACC_ABSTRACT | 0x0400 | 声明为abstract;不能被实例化。 |
ACC_SYNTHETIC | 0x1000 | 声明为synthetic;不存在于源代码中。 |
ACC_ANNOTATION | 0x2000 | 声明为注解接口。 |
ACC_ENUM | 0x4000 | 声明为枚举类。 |
ACC_MODULE | 0x8000 | 是模块,不是类或接口。 |
ACC_MODULE标志指示此class文件定义了一个模块,而不是类或接口。如果设置了ACC_MODULE标志,则适用于该class文件的特殊规则在本节末尾给出。如果未设置ACC_MODULE标志,则当前段落下方的规则适用于该class文件。
接口通过设置ACC_INTERFACE标志来区分。如果未设置ACC_INTERFACE标志,则此class文件定义了一个类,而不是接口或模块。
如果设置了ACC_INTERFACE标志,则还必须设置ACC_ABSTRACT标志,并且不得设置ACC_FINAL、ACC_SUPER、ACC_ENUM和ACC_MODULE标志。
如果未设置ACC_INTERFACE标志,则可以设置表4.1-B中的任何其他标志,但ACC_ANNOTATION和ACC_MODULE除外。然而,这样的class文件不得同时设置其ACC_FINAL和ACC_ABSTRACT标志(JLS §8.1.1.2)。
ACC_SUPER标志指示如果此类或接口中出现invokespecial指令(§invokespecial),则该指令应表达两种替代语义中的哪一种。面向Java虚拟机指令集的编译器应设置ACC_SUPER标志。在Java SE 8及更高版本中,Java虚拟机认为ACC_SUPER标志在每个class文件中都被设置,无论该标志在class文件中的实际值和class文件的版本如何。
ACC_SUPER标志的存在是为了与旧版Java编程语言编译器编译的代码向后兼容。在JDK 1.0.2之前,编译器生成的access_flags中,现在代表ACC_SUPER的标志没有分配含义,Oracle的Java虚拟机实现在该标志被设置时忽略它。
ACC_SYNTHETIC标志指示此类或接口是由编译器生成的,且不存在于源代码中。
注解接口(JLS §9.6)必须设置其ACC_ANNOTATION标志。如果设置了ACC_ANNOTATION标志,则还必须设置ACC_INTERFACE标志。
ACC_ENUM标志指示此类或其超类被声明为枚举类(JLS §8.9)。
表4.1-B中未分配的所有access_flags项中的位都保留供将来使用。它们在生成的class文件中应设置为零,Java虚拟机实现应忽略它们。
this_classthis_class项的值必须是constant_pool表中的有效索引。该索引处的constant_pool条目必须是一个CONSTANT_Class_info结构(§4.4.1),表示由此class文件定义的类或接口。
super_class 对于类,super_class项的值要么必须为零,要么必须是constant_pool表中的有效索引。如果super_class项的值非零,则该索引处的constant_pool条目必须是一个CONSTANT_Class_info结构,表示由此class文件定义的类的直接超类。直接超类及其任何超类都不得在其ClassFile结构的access_flags项中设置ACC_FINAL标志。
如果super_class项的值为零,则此class文件必须表示Object类,这是唯一没有直接超类的类或接口。
对于接口,super_class项的值必须始终是constant_pool表中的有效索引。该索引处的constant_pool条目必须是一个表示Object类的CONSTANT_Class_info结构。
interfaces_countinterfaces_count项的值给出了此类或接口类型的直接超接口的数量。
interfaces[]interfaces数组中的每个值必须是constant_pool表中的有效索引。interfaces[i](其中0 ≤ i < interfaces_count)的每个值处的constant_pool条目必须是一个CONSTANT_Class_info结构,表示一个接口,该接口是该类或接口类型的直接超接口,顺序按源代码中为类型给定的从左到右的顺序。
fields_countfields_count项的值给出了fields表中field_info结构的数量。field_info结构表示此类或接口类型声明的所有字段,包括类变量和实例变量。
fields[]fields表中的每个值必须是一个field_info结构(§4.5),完整描述此类或接口中的字段。fields表仅包括此类或接口声明的字段。它不包括表示从超类或超接口继承的字段的项。
methods_countmethods_count项的值给出了methods表中method_info结构的数量。
methods[]methods表中的每个值必须是一个method_info结构(§4.6),完整描述此类或接口中的方法。如果method_info结构的access_flags项中既未设置ACC_NATIVE标志也未设置ACC_ABSTRACT标志,则还会提供实现该方法的Java虚拟机指令。
method_info结构表示此类或接口类型声明的所有方法,包括实例方法、类方法、实例初始化方法(§2.9.1)以及任何类或接口初始化方法(§2.9.2)。methods表不包括表示从超类或超接口继承的方法的项。
attributes_countattributes_count项的值给出了此类的attributes表中的属性数量。
attributes[]attributes表中的每个值必须是一个attribute_info结构(§4.7)。
本规范定义为出现在ClassFile结构的attributes表中的属性列于表4.7-C中。
关于定义出现在ClassFile结构的attributes表中的属性的规则在§4.7中给出。
关于ClassFile结构的attributes表中非预定义属性的规则在§4.7.1中给出。
如果在access_flags项中设置了ACC_MODULE标志,则access_flags项中不得设置任何其他标志,并且以下规则适用于ClassFile结构的其余部分:
major_version,minor_version:≥ 53.0(即Java SE 9及以上)this_class:module-infosuper_class,interfaces_count,fields_count,methods_count:零attributes:必须存在一个Module属性。除Module、ModulePackages、ModuleMainClass、InnerClasses、SourceFile、SourceDebugExtension、RuntimeVisibleAnnotations和RuntimeInvisibleAnnotations外,预定义属性(§4.7)均不得出现。
4.2 名称
4.2.1 二进制类名与接口名
出现在class文件结构中的类和接口名称始终以完全限定形式表示,即所谓的二进制名称(JLS §13.1)。此类名称始终表示为CONSTANT_Utf8_info结构(§4.4.7),因此,在没有进一步约束的情况下,可以从整个Unicode代码空间中选取。类和接口名称从那些在其描述符(§4.3)中具有此类名称的CONSTANT_NameAndType_info结构(§4.4.6)以及所有CONSTANT_Class_info结构(§4.4.1)中被引用。
由于历史原因,class文件结构中出现的二进制名称的语法与JLS §13.1中记录的二进制名称的语法不同。在这种内部形式中,通常分隔组成二进制名称的标识符的ASCII句点(.)被ASCII正斜杠(/)替换。标识符本身必须是非限定名称(§4.2.2)。
例如,Thread类的普通二进制名称是java.lang.Thread。在class文件格式的描述符中使用的内部形式中,对Thread类名称的引用是使用表示字符串java/lang/Thread的CONSTANT_Utf8_info结构来实现的。
4.2.2 非限定名称
方法、字段、局部变量和形参的名称存储为非限定名称。非限定名称必须至少包含一个Unicode码点,并且不得包含任何ASCII字符 . ; [ /(即句点、分号、左方括号或正斜杠)。
方法名称受到进一步约束,除了特殊方法名称<init>和<clinit>(§2.9)之外,它们不得包含ASCII字符 < 或 >(即左尖括号或右尖括号)。
注意,字段名称或接口方法名称可以是<init>或<clinit>,但没有方法调用指令可以引用<clinit>,且只有invokespecial指令(§invokespecial)可以引用<init>。
4.2.3 模块名称与包名称
从Module属性引用的模块名称存储在常量池(§4.4.11)的CONSTANT_Module_info结构中。CONSTANT_Module_info结构包装了一个表示模块名称的CONSTANT_Utf8_info结构。模块名称不像类和接口名称那样以“内部形式”编码,即分隔模块名称中标识符的ASCII句点(.)不会被ASCII正斜杠(/)替换。
模块名称可以从整个Unicode代码空间中选取,但须遵守以下约束:
- 模块名称不得包含范围
'\u0000'到'\u001F'(含)内的任何码点。 - ASCII反斜杠(
\)保留用作模块名称中的转义字符。它不得出现在模块名称中,除非后面紧跟ASCII反斜杠、ASCII冒号(:)或ASCII at符号(@)。ASCII字符序列\\可用于在模块名称中编码反斜杠。 - ASCII冒号(
:)和at符号(@)保留供将来在模块名称中使用。除非被转义,否则它们不得出现在模块名称中。ASCII字符序列\:和\@可用于在模块名称中编码冒号和at符号。
从Module属性引用的包名称存储在常量池(§4.4.12)的CONSTANT_Package_info结构中。CONSTANT_Package_info结构包装了一个CONSTANT_Utf8_info结构,该结构表示以内部形式编码的包名称。
4.3 描述符
描述符是表示字段或方法类型的字符串。描述符在class文件格式中使用修改过的UTF-8字符串(§4.4.7)表示,因此,在没有进一步约束的情况下,可以从整个Unicode代码空间中选取。
4.3.1 语法符号
描述符使用文法来指定。该文法是一组产生式,描述字符序列如何能够形成各种类型的语法正确的描述符。文法的终结符号以等宽字体显示。非终结符号以斜体显示。非终结符的定义由被定义的非终结符名称后跟冒号引入。随后在后续行中给出该非终结符的一个或多个替代定义。
产生式右侧的语法 {x} 表示零个或多个 x 的出现。
产生式右侧的短语 (one of) 表示以下一行或多行中的每个终结符号都是一个替代定义。
4.3.2 字段描述符
字段描述符表示类、实例或局部变量的类型。
FieldDescriptor:
FieldType
FieldType:
BaseType
ObjectType
ArrayType
BaseType:
(one of)
B C D F I J S Z
ObjectType:
L ClassName ;
ArrayType:
[ ComponentType
ComponentType:
FieldTypeBaseType的字符、ObjectType的L和;,以及ArrayType的[都是ASCII字符。
ClassName表示以内部形式(§4.2.1)编码的二进制类或接口名称。
字段描述符作为类型的解释如表4.3-A所示。
表示数组类型的字段描述符仅当它表示的类型的维度不超过255时才有效。
表4.3-A. 字段描述符的解释
| 字段类型术语 | 类型 | 解释 |
|---|---|---|
B | byte | 有符号字节 |
C | char | Unicode字符码点(基本多语言平面),使用UTF-16编码 |
D | double | 双精度浮点值 |
F | float | 单精度浮点值 |
I | int | 整数 |
J | long | 长整数 |
L ClassName ; | reference | 类ClassName的实例 |
S | short | 有符号短整数 |
Z | boolean | true或false |
[ | reference | 一个数组维度 |
int类型实例变量的字段描述符就是简单的I。
Object类型实例变量的字段描述符是Ljava/lang/Object;。注意,类Object的二进制名称的内部形式被使用。
多维数组类型double[][][]的实例变量的字段描述符是[[[D。
4.3.3 方法描述符
方法描述符包含零个或多个参数描述符(表示方法所取参数的类型)和一个返回描述符(表示方法返回的值(如果有)的类型)。
MethodDescriptor:
{ ParameterDescriptor } ReturnDescriptor
ParameterDescriptor:
FieldType
ReturnDescriptor:
FieldType
VoidDescriptor
VoidDescriptor:
V字符V表示方法不返回值(其结果类型为void)。
方法的方法描述符:
Object m(int i, double d, Thread t) { ... }是:
(IDLjava/lang/Thread;)Ljava/lang/Object;注意,Thread和Object的二进制名称的内部形式被使用。
仅当方法描述符表示的方法参数总长度不超过255时,该描述符才有效,该长度包括在实例或接口方法调用情况下this的贡献。总长度通过计算各个参数的贡献之和得出,其中long或double类型的参数贡献两个单位,任何其他类型的参数贡献一个单位。
无论所描述的方法是类方法还是实例方法,方法描述符都是相同的。尽管实例方法除了其预期参数外还传递了this(对正在调用该方法的对象的引用),但该事实并未反映在方法描述符中。对this的引用由调用实例方法的Java虚拟机指令隐式传递(§2.6.1,§4.11)。
4.4 常量池
Java虚拟机指令不依赖于类、接口、类实例或数组的运行时布局。相反,指令引用constant_pool表中的符号信息。
所有constant_pool表项都具有以下通用格式:
cp_info {
u1 tag;
u1 info[];
}constant_pool表中的每个条目必须以一个1字节的标签开头,指示该条目所表示的常量种类。共有17种常量,列于表4.4-A中,并附有相应的标签,并按本章节编号排序。每个标签字节后必须跟有两个或多个字节,给出关于特定常量的信息。附加信息的格式取决于标签字节,即info数组的内容随tag的值而变化。
表4.4-A. 常量池标签(按节)
| 常量种类 | 标签 | 节 |
|---|---|---|
CONSTANT_Class | 7 | §4.4.1 |
CONSTANT_Fieldref | 9 | §4.4.2 |
CONSTANT_Methodref | 10 | §4.4.2 |
CONSTANT_InterfaceMethodref | 11 | §4.4.2 |
CONSTANT_String | 8 | §4.4.3 |
CONSTANT_Integer | 3 | §4.4.4 |
CONSTANT_Float | 4 | §4.4.4 |
CONSTANT_Long | 5 | §4.4.5 |
CONSTANT_Double | 6 | §4.4.5 |
CONSTANT_NameAndType | 12 | §4.4.6 |
CONSTANT_Utf8 | 1 | §4.4.7 |
CONSTANT_MethodHandle | 15 | §4.4.8 |
CONSTANT_MethodType | 16 | §4.4.9 |
CONSTANT_Dynamic | 17 | §4.4.10 |
CONSTANT_InvokeDynamic | 18 | §4.4.10 |
CONSTANT_Module | 19 | §4.4.11 |
CONSTANT_Package | 20 | §4.4.12 |
在版本号为v的class文件中,constant_pool表中的每个条目必须具有在class文件格式版本v或更早版本中首次定义的标签(§4.1)。也就是说,每个条目必须表示一种被批准在该class文件中使用的常量。表4.4-B列出了每个标签及其首次定义的class文件格式版本。同时还显示了引入该class文件格式版本的Java SE平台版本。
表4.4-B. 常量池标签(按标签)
| 常量种类 | 标签 | class文件格式版本 | Java SE版本 |
|---|---|---|---|
CONSTANT_Utf8 | 1 | 45.3 | 1.0.2 |
CONSTANT_Integer | 3 | 45.3 | 1.0.2 |
CONSTANT_Float | 4 | 45.3 | 1.0.2 |
CONSTANT_Long | 5 | 45.3 | 1.0.2 |
CONSTANT_Double | 6 | 45.3 | 1.0.2 |
CONSTANT_Class | 7 | 45.3 | 1.0.2 |
CONSTANT_String | 8 | 45.3 | 1.0.2 |
CONSTANT_Fieldref | 9 | 45.3 | 1.0.2 |
CONSTANT_Methodref | 10 | 45.3 | 1.0.2 |
CONSTANT_InterfaceMethodref | 11 | 45.3 | 1.0.2 |
CONSTANT_NameAndType | 12 | 45.3 | 1.0.2 |
CONSTANT_MethodHandle | 15 | 51.0 | 7 |
CONSTANT_MethodType | 16 | 51.0 | 7 |
CONSTANT_Dynamic | 17 | 55.0 | 11 |
CONSTANT_InvokeDynamic | 18 | 51.0 | 7 |
CONSTANT_Module | 19 | 53.0 | 9 |
CONSTANT_Package | 20 | 53.0 | 9 |
常量池中的某些条目是可加载的,因为它们表示可以在运行时压入栈以支持进一步计算的实体。在版本号为v的class文件中,如果constant_pool表中的条目具有在class文件格式版本v或更早版本中首次被视为可加载的标签,则该条目是可加载的。表4.4-C列出了每个标签及其首次被视为可加载的class文件格式版本。同时还显示了引入该class文件格式版本的Java SE平台版本。
在每种情况下,除了CONSTANT_Class之外,一个标签首次被视为可加载的版本与其首次定义的class文件格式版本相同。
表4.4-C. 可加载常量池标签
| 常量种类 | 标签 | class文件格式版本 | Java SE版本 |
|---|---|---|---|
CONSTANT_Integer | 3 | 45.3 | 1.0.2 |
CONSTANT_Float | 4 | 45.3 | 1.0.2 |
CONSTANT_Long | 5 | 45.3 | 1.0.2 |
CONSTANT_Double | 6 | 45.3 | 1.0.2 |
CONSTANT_Class | 7 | 49.0 | 5.0 |
CONSTANT_String | 8 | 45.3 | 1.0.2 |
CONSTANT_MethodHandle | 15 | 51.0 | 7 |
CONSTANT_MethodType | 16 | 51.0 | 7 |
CONSTANT_Dynamic | 17 | 55.0 | 11 |
4.4.1 CONSTANT_Class_info结构
CONSTANT_Class_info结构用于表示一个类或接口:
CONSTANT_Class_info {
u1 tag;
u2 name_index;
}CONSTANT_Class_info结构的项如下:
tagtag项的值为CONSTANT_Class(7)。
name_indexname_index项的值必须是constant_pool表中的有效索引。该索引处的constant_pool条目必须是一个CONSTANT_Utf8_info结构(§4.4.7),表示以内部形式(§4.2.1)编码的有效二进制类或接口名称。
因为数组是对象,所以操作码anewarray和multianewarray——但不是操作码new——可以通过constant_pool表中的CONSTANT_Class_info结构引用数组“类”。对于此类数组类,类的名称是数组类型的描述符(§4.3.2)。
例如,表示二维数组类型int[][]的类名称是[[I,而表示类型Thread[]的类名称是[Ljava/lang/Thread;。
4.4.2 CONSTANT_Fieldref_info、CONSTANT_Methodref_info和CONSTANT_InterfaceMethodref_info结构
字段、方法和接口方法由类似的结构表示:
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_InterfaceMethodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}这些结构的项如下:
tagCONSTANT_Fieldref_info结构的tag项值为CONSTANT_Fieldref(9)。 CONSTANT_Methodref_info结构的tag项值为CONSTANT_Methodref(10)。 CONSTANT_InterfaceMethodref_info结构的tag项值为CONSTANT_InterfaceMethodref(11)。
class_indexclass_index项的值必须是constant_pool表中的有效索引。该索引处的constant_pool条目必须是一个CONSTANT_Class_info结构(§4.4.1),表示具有该字段或方法作为成员的类或接口类型。
在CONSTANT_Fieldref_info结构中,class_index项可以是类类型或接口类型。
在CONSTANT_Methodref_info结构中,class_index项必须是类类型,不能是接口类型。
在CONSTANT_InterfaceMethodref_info结构中,class_index项必须是接口类型,不能是类类型。
name_and_type_indexname_and_type_index项的值必须是constant_pool表中的有效索引。该索引处的constant_pool条目必须是一个CONSTANT_NameAndType_info结构(§4.4.6)。此constant_pool条目指示字段或方法的名称和描述符。
在CONSTANT_Fieldref_info结构中,所指示的描述符必须是字段描述符(§4.3.2)。否则,所指示的描述符必须是方法描述符(§4.3.3)。
如果CONSTANT_Methodref_info结构中方法的名称以'<'('\u003c')开头,则该名称必须是特殊名称<init>,表示实例初始化方法(§2.9.1)。此类方法的返回类型必须为void。
4.4.3 CONSTANT_String_info结构
CONSTANT_String_info结构用于表示String类型的常量对象:
CONSTANT_String_info {
u1 tag;
u2 string_index;
}CONSTANT_String_info结构的项如下:
tagtag项的值为CONSTANT_String(8)。
string_indexstring_index项的值必须是constant_pool表中的有效索引。该索引处的constant_pool条目必须是一个CONSTANT_Utf8_info结构(§4.4.7),表示要初始化的String对象的Unicode码点序列。
4.4.4 CONSTANT_Integer_info和CONSTANT_Float_info结构
CONSTANT_Integer_info和CONSTANT_Float_info结构表示4字节数值(int和float)常量:
CONSTANT_Integer_info {
u1 tag;
u4 bytes;
}
CONSTANT_Float_info {
u1 tag;
u4 bytes;
}这些结构的项如下:
tagCONSTANT_Integer_info结构的tag项值为CONSTANT_Integer(3)。 CONSTANT_Float_info结构的tag项值为CONSTANT_Float(4)。
bytesCONSTANT_Integer_info结构的bytes项表示int常量的值。bytes项按大端序(高位字节在前)存储。
CONSTANT_Float_info结构的bytes项表示IEEE 754 binary32格式的float值(§2.3.2)。bytes项按大端序(高位字节在前)存储。其值通过将bytes转换为int常量bits来确定,然后:
- 如果
bits为0x7f800000,则float值为正无穷大。 - 如果
bits为0xff800000,则float值为负无穷大。 - 如果
bits在0x7f800001到0x7fffffff或0xff800001到0xffffffff范围内,则float值为NaN。 - 否则,令
s、e和m为从bits计算出的三个值:则浮点值等于数学表达式int s = ((bits >> 31) == 0) ? 1 : -1; int e = ((bits >> 23) & 0xff); int m = (e == 0) ? (bits & 0x7fffff) << 1 : (bits & 0x7fffff) | 0x800000;s · m · 2^(e-150)的float值。
4.4.5 CONSTANT_Long_info和CONSTANT_Double_info结构
CONSTANT_Long_info和CONSTANT_Double_info结构表示8字节数值(long和double)常量:
CONSTANT_Long_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
CONSTANT_Double_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}所有8字节常量在class文件的constant_pool表中占用两个条目。如果CONSTANT_Long_info或CONSTANT_Double_info结构是constant_pool表中索引n处的条目,则表中下一个可用的条目位于索引n+2。常量池索引n+1必须有效但被视为不可用。
事后看来,让8字节常量占用两个常量池条目是一个糟糕的选择。
这些结构的项如下:
tagCONSTANT_Long_info结构的tag项值为CONSTANT_Long(5)。 CONSTANT_Double_info结构的tag项值为CONSTANT_Double(6)。
high_bytes, low_bytesCONSTANT_Long_info结构的无符号high_bytes和low_bytes项共同表示long常量的值:
((long) high_bytes << 32) + low_bytes其中high_bytes和low_bytes的每个字节按大端序(高位字节在前)存储。
CONSTANT_Double_info结构的high_bytes和low_bytes项共同表示IEEE 754 binary64格式的double值(§2.3.2)。每个项的字节按大端序(高位字节在前)存储。
该结构所表示的值确定如下:将high_bytes和low_bytes转换为long常量bits,等于:
((long) high_bytes << 32) + low_bytes然后:
- 如果
bits为0x7ff0000000000000L,则double值为正无穷大。 - 如果
bits为0xfff0000000000000L,则double值为负无穷大。 - 如果
bits在0x7ff0000000000001L到0x7fffffffffffffffL或0xfff0000000000001L到0xffffffffffffffffL范围内,则double值为NaN。 - 否则,令
s、e和m为从bits计算出的三个值:则浮点值等于数学表达式int s = ((bits >> 63) == 0) ? 1 : -1; int e = (int)((bits >> 52) & 0x7ffL); long m = (e == 0) ? (bits & 0xfffffffffffffL) << 1 : (bits & 0xfffffffffffffL) | 0x10000000000000L;s · m · 2^(e-1075)的double值。
4.4.6 CONSTANT_NameAndType_info结构
CONSTANT_NameAndType_info结构用于表示字段或方法,而不指明它属于哪个类或接口类型:
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index;
u2 descriptor_index;
}CONSTANT_NameAndType_info结构的项如下:
tagtag项的值为CONSTANT_NameAndType(12)。
name_indexname_index项的值必须是constant_pool表中的有效索引。该索引处的constant_pool条目必须是一个CONSTANT_Utf8_info结构(§4.4.7),表示表示字段或方法的有效非限定名称(§4.2.2),或特殊方法名称<init>(§2.9.1)。
descriptor_indexdescriptor_index项的值必须是constant_pool表中的有效索引。该索引处的constant_pool条目必须是一个CONSTANT_Utf8_info结构(§4.4.7),表示有效的字段描述符或方法描述符(§4.3.2,§4.3.3)。
4.4.7 CONSTANT_Utf8_info结构
CONSTANT_Utf8_info结构用于表示常量字符串值:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}CONSTANT_Utf8_info结构的项如下:
tagtag项的值为CONSTANT_Utf8(1)。
lengthlength项的值给出bytes数组中的字节数(不是结果字符串的长度)。
bytes[]bytes数组包含字符串的字节。
任何字节都不得有值(byte)0。
任何字节都不得在范围(byte)0xf0到(byte)0xff内。
字符串内容以修改过的UTF-8编码。修改过的UTF-8字符串的编码方式使得仅包含非空ASCII字符的码点序列可以用每个码点1个字节表示,但Unicode代码空间中的所有码点都可以表示。修改过的UTF-8字符串不以空字符终止。编码如下:
范围
'\u0001'到'\u007F'内的码点由单个字节表示:0 bits 6-0字节中的7位数据给出所表示码点的值。
空码点(
'\u0000')和范围'\u0080'到'\u07FF'内的码点由一对字节x和y表示:x: 1 1 0 bits 10-6 y: 1 0 bits 5-0这两个字节表示值为
((x & 0x1f) << 6) + (y & 0x3f)的码点。范围
'\u0800'到'\uFFFF'内的码点由3个字节x、y和z表示:x: 1 1 1 0 bits 15-12 y: 1 0 bits 11-6 z: 1 0 bits 5-0这三个字节表示值为
((x & 0x0f) << 12) + ((y & 0x3f) << 6) + (z & 0x3f)的码点。码点高于U+FFFF(所谓的增补字符)通过分别编码其UTF-16表示的两个代理码元来表示。每个代理码元由3个字节表示。这意味着增补字符由6个字节
u、v、w、x、y和z表示:u: 1 1 1 0 1 1 0 1 v: 1 0 (bits 20-16)-1 w: 1 0 bits 15-10 x: 1 1 1 0 1 1 0 1 y: 1 0 1 1 bits 9-6 z: 1 0 bits 5-0这六个字节表示值为
0x10000 + ((v & 0x0f) << 16) + ((w & 0x3f) << 10) + ((y & 0x0f) << 6) + (z & 0x3f)的码点。
多字节字符的字节按大端序(高位字节在前)存储在class文件中。
此格式与“标准”UTF-8格式有两个区别。首先,空字符(char)0使用2字节格式而不是1字节格式编码,因此修改过的UTF-8字符串永远不会包含嵌入的空值。其次,仅使用标准UTF-8的1字节、2字节和3字节格式。Java虚拟机不识别标准UTF-8的4字节格式;它使用自己的两次三字节格式代替。
有关标准UTF-8格式的更多信息,请参见《Unicode标准13.0版》第3.9节“Unicode编码形式”。
4.4.8 CONSTANT_MethodHandle_info结构
CONSTANT_MethodHandle_info结构用于表示方法句柄:
CONSTANT_MethodHandle_info {
u1 tag;
u1 reference_kind;
u2 reference_index;
}CONSTANT_MethodHandle_info结构的项如下:
tagtag项的值为CONSTANT_MethodHandle(15)。
reference_kindreference_kind项的值必须在1到9的范围内。该值表示此方法句柄的种类,它表征其字节码行为(§5.4.3.5)。
reference_indexreference_index项的值必须是constant_pool表中的有效索引。该索引处的constant_pool条目必须如下:
- 如果
reference_kind项的值是1(REF_getField)、2(REF_getStatic)、3(REF_putField)或4(REF_putStatic),则该索引处的constant_pool条目必须是一个CONSTANT_Fieldref_info结构(§4.4.2),表示要为其创建方法句柄的字段。 - 如果
reference_kind项的值是6(REF_invokeStatic)或7(REF_invokeSpecial),则如果class文件版本号小于52.0,该索引处的constant_pool条目必须是一个CONSTANT_Methodref_info结构,表示要为其创建方法句柄的类的方法;如果class文件版本号为52.0或以上,则该条目必须是CONSTANT_Methodref_info结构或CONSTANT_InterfaceMethodref_info结构(§4.4.2),表示要为其创建方法句柄的类或接口的方法。 - 如果
reference_kind项的值是9(REF_invokeInterface),则该索引处的constant_pool条目必须是一个CONSTANT_InterfaceMethodref_info结构,表示要为其创建方法句柄的接口的方法。 - 如果
reference_kind项的值是5(REF_invokeVirtual)、6(REF_invokeStatic)、7(REF_invokeSpecial)或9(REF_invokeInterface),则由CONSTANT_Methodref_info结构或CONSTANT_InterfaceMethodref_info结构表示的方法名称不得为<init>或<clinit>。如果值为8(REF_newInvokeSpecial),则由CONSTANT_Methodref_info结构表示的方法名称必须为<init>。
4.4.9 CONSTANT_MethodType_info结构
CONSTANT_MethodType_info结构用于表示方法类型:
CONSTANT_MethodType_info {
u1 tag;
u2 descriptor_index;
}CONSTANT_MethodType_info结构的项如下:
tagtag项的值为CONSTANT_MethodType(16)。
descriptor_indexdescriptor_index项的值必须是constant_pool表中的有效索引。该索引处的constant_pool条目必须是一个CONSTANT_Utf8_info结构(§4.4.7),表示一个方法描述符(§4.3.3)。
4.4.10 CONSTANT_Dynamic_info和CONSTANT_InvokeDynamic_info结构
constant_pool表中的大多数结构通过组合静态记录在表中的名称、描述符和值来直接表示实体。相反,CONSTANT_Dynamic_info和CONSTANT_InvokeDynamic_info结构通过指向动态计算实体的代码来间接表示实体。该代码称为引导方法,由Java虚拟机在解析派生自这些结构的符号引用期间调用(§5.1,§5.4.3.6)。每个结构指定一个引导方法以及一个辅助名称和类型,用于表征要计算的实体。更详细地说:
CONSTANT_Dynamic_info结构用于表示动态计算常量,这是一个任意值,由引导方法在ldc指令(§ldc)等过程中调用产生。该结构指定的辅助类型约束动态计算常量的类型。
CONSTANT_InvokeDynamic_info结构用于表示动态计算调用点,即java.lang.invoke.CallSite的实例,由引导方法在invokedynamic指令(§invokedynamic)过程中调用产生。该结构指定的辅助类型约束动态计算调用点的方法类型。
CONSTANT_Dynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
CONSTANT_InvokeDynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}这些结构的项如下:
tagCONSTANT_Dynamic_info结构的tag项值为CONSTANT_Dynamic(17)。 CONSTANT_InvokeDynamic_info结构的tag项值为CONSTANT_InvokeDynamic(18)。
bootstrap_method_attr_indexbootstrap_method_attr_index项的值必须是此class文件的引导方法表(§4.7.23)的bootstrap_methods数组中的有效索引。
CONSTANT_Dynamic_info结构是独特的,因为它们在语法上允许通过引导方法表引用自身。我们允许循环,但要求在解析时失败(§5.4.3.6),而不是强制在加载类时检测此类循环(这可能是一项昂贵的检查)。
name_and_type_indexname_and_type_index项的值必须是constant_pool表中的有效索引。该索引处的constant_pool条目必须是一个CONSTANT_NameAndType_info结构(§4.4.6)。此constant_pool条目指示一个名称和描述符。
在CONSTANT_Dynamic_info结构中,所指示的描述符必须是字段描述符(§4.3.2)。
在CONSTANT_InvokeDynamic_info结构中,所指示的描述符必须是方法描述符(§4.3.3)。
4.4.11 CONSTANT_Module_info结构
CONSTANT_Module_info结构用于表示一个模块:
CONSTANT_Module_info {
u1 tag;
u2 name_index;
}CONSTANT_Module_info结构的项如下:
tagtag项的值为CONSTANT_Module(19)。
name_indexname_index项的值必须是constant_pool表中的有效索引。该索引处的constant_pool条目必须是一个CONSTANT_Utf8_info结构(§4.4.7),表示一个有效的模块名称(§4.2.3)。
CONSTANT_Module_info结构仅允许在声明模块的class文件的常量池中出现,即access_flags项设置了ACC_MODULE标志的ClassFile结构。在所有其他class文件中,CONSTANT_Module_info结构是非法的。
4.4.12 CONSTANT_Package_info结构
CONSTANT_Package_info结构用于表示模块导出或开放的包:
CONSTANT_Package_info {
u1 tag;
u2 name_index;
}CONSTANT_Package_info结构的项如下:
tagtag项的值为CONSTANT_Package(20)。
name_indexname_index项的值必须是constant_pool表中的有效索引。该索引处的constant_pool条目必须是一个CONSTANT_Utf8_info结构(§4.4.7),表示以内部形式(§4.2.3)编码的有效包名称。
CONSTANT_Package_info结构仅允许在声明模块的class文件的常量池中出现,即access_flags项设置了ACC_MODULE标志的ClassFile结构。在所有其他class文件中,CONSTANT_Package_info结构是非法的。
4.5 字段
每个字段由一个field_info结构描述。
一个class文件中不能有两个字段具有相同的名称和描述符(§4.3.2)。
该结构具有以下格式:
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}field_info结构的项如下:
access_flagsaccess_flags项的值是一个标志掩码,用于表示该字段的访问权限和属性。每个标志(当被设置时)的解释在表4.5-A中指定。
表4.5-A. 字段访问和属性标志
| 标志名称 | 值 | 解释 |
|---|---|---|
ACC_PUBLIC | 0x0001 | 声明为public;可从其包外部访问。 |
ACC_PRIVATE | 0x0002 | 声明为private;仅在定义类及同一nest(§5.4.4)内的其他类中可访问。 |
ACC_PROTECTED | 0x0004 | 声明为protected;可在子类中访问。 |
ACC_STATIC | 0x0008 | 声明为static。 |
ACC_FINAL | 0x0010 | 声明为final;对象构造后永远不会被直接赋值(JLS §17.5)。 |
ACC_VOLATILE | 0x0040 | 声明为volatile;不能被缓存。 |
ACC_TRANSIENT | 0x0080 | 声明为transient;不被持久对象管理器写入或读取。 |
ACC_SYNTHETIC | 0x1000 | 声明为synthetic;不存在于源代码中。 |
ACC_ENUM | 0x4000 | 声明为枚举类的一个元素。 |
类的字段可以设置表4.5-A中的任何标志。但是,类的每个字段最多只能设置ACC_PUBLIC、ACC_PRIVATE和ACC_PROTECTED标志中的一个(JLS §8.3.1),并且不得同时设置ACC_FINAL和ACC_VOLATILE标志(JLS §8.3.1.4)。
接口的字段必须设置其ACC_PUBLIC、ACC_STATIC和ACC_FINAL标志;它们可以设置其ACC_SYNTHETIC标志,但不得设置表4.5-A中的任何其他标志(JLS §9.3)。
ACC_SYNTHETIC标志指示此字段是由编译器生成的,不存在于源代码中。
ACC_ENUM标志指示此字段用于保存枚举类的一个元素(JLS §8.9)。
表4.5-A中未分配的所有access_flags项中的位都保留供将来使用。它们在生成的class文件中应设置为零,Java虚拟机实现应忽略它们。
name_indexname_index项的值必须是constant_pool表中的有效索引。该索引处的constant_pool条目必须是一个CONSTANT_Utf8_info结构(§4.4.7),表示表示字段的有效非限定名称(§4.2.2)。
descriptor_indexdescriptor_index项的值必须是constant_pool表中的有效索引。该索引处的constant_pool条目必须是一个CONSTANT_Utf8_info结构(§4.4.7),表示有效的字段描述符(§4.3.2)。
attributes_countattributes_count项的值指示此字段的附加属性数量。
attributes[]attributes表中的每个值必须是一个attribute_info结构(§4.7)。
一个字段可以具有任意数量的与其关联的可选属性。
本规范定义为出现在field_info结构的attributes表中的属性列于表4.7-C中。
关于定义出现在field_info结构的attributes表中的属性的规则在§4.7中给出。
关于field_info结构的attributes表中非预定义属性的规则在§4.7.1中给出。