Java虚拟机规范 Java SE 8版 - class文件格式(一)
-
- 4.1 ClassFile 结构
- 4.2 各种名称的内部表示形式
- 4.3 描述符
- 4.4 常量池
-
- 4.4.1 CONSTANT_Class_info 结构
- 4.4.2 CONSTANT_Fieldref_info、CONSTANT_Methodref_info和CONSTANT_InterfaceMethodref_info 结构
- 4.4.3 CONSTANT_String_info 结构
- 4.4.4 CONSTANT_Integer_info 和 CONSTANT_Float_info 结构
- 4.4.5 CONSTANT_Long_info 和 CONSTANT_Double_info 结构
- 4.4.6 CONSTANT_NameAndType_info 结构
- 4.4.7 CONSTANT_Utf8_info 结构
- 4.4.8 CONSTANT_MethodHandle_info 结构
- 4.4.9 CONSTANT_MethodType_info 结构
- 4.4.10 CONSTANT_InvokeDynamic_info 结构
本章将描述Java虚拟机中定义的class文件格式。每一个class文件都对应着唯一一个类或接口的定义信息,但是相对地,类或接口并不一定都必须定义在文件里(比如类或接口也可以通过类加载器直接生成)。在本章中,我们只是通俗地将任意一个有效的类或接口所应当满足的格式称为 “class文件格式”,即使它不一定以磁盘文件的形式存在。
每个class文件都由字节流组成,每个字节含有8个二进制位,所有16位、32位和64位长度的数据将通过构造成2个、4个和8个连续的8位字节来表示。多字节数据项总是按照big-endian(大端在前)㊀的顺序进行存储。在Java SDK中,可以使用 java.io.Datalnput、java.io.DataOutput 等接口和 java.io.DataInputStream 和 java.io.DataOutputStream 等类来访问这种格式的数据。
㊀big-endian顺序是指按高位字节在地址最低位,低位字节在地址最高位来存储数据,它是SPARC、
PowerPC等处理器的默认多字节存储顺序,而x86等处理器则使用了相反的little-endian顺序来存
储数据。为了保证class文件在不同硬件上具备同样的含义,因此,在Java虚拟机规范中有必要严格规
定数据存储顺序。一译者注
本章还定义了一组专用的数据类型来表示class文件的内容,它们包括ul、u2 和 u4,分别代表1、2和4个字节的无符号数。在Java SE平台中,这些类型的数据可以通过 java.io.Datalnput 接口中的 readUnsignedByte、 readUnsignedShort 和 readlnt 方法进行读取。
本章将采用类似C语言结构体的伪结构来描述class文件格式。为了避免与类的字段、 类的实例等概念产生混淆,我们用项(item)来称呼class文件格式各结构体中的内容。在 class文件中,各项按照严格顺序连续存放的,它们之间没有用任何填充或对齐作为各项间的分隔符号。
表(table)由任意数量的可变长度的项组成,用于表示class文件内容的一系列复合结构。尽管我们采用类似C语言的数组语法来表示表中的项,但是你应当清楚意识到,表是由可变长数据组成的复合结构(表中每项的长度不固定),因此无法直接把表格索引转换为偏移量,并以此来访问表中的项。
而把一个数据结构描述为数组时,就意味着它含有0至多个长度固定的项,此时可以采用数组索引的方式访问它。
在本章中出现的所有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结构中,各项的含义描述如下:
- Magic (魔数)
Magic的唯一作用是确定这个文件是否为一个能被虚拟机所接受的class文件。魔数值固定为OxCAFEBABE,不会改变。 - minor_version(副版本号)、major_version(主版本号)
minor_version 和 major_version 的值分别表示class文件的副、主版本。 它们共同构成了 class文件的格式版本号。比如,某个class文件的主版本号为M,副版本号为m,那么这个class文件的格式版本号就确定为M.m。class文件格式版本号可按字面顺序来排序,例如:1.5 < 2.0 < 2.1。
假设一个class文件的格式版本号为v,那么,当且仅当 M i . 0 ≤ v ≤ M j . m Mi.0 \leq v \leq Mj.m Mi.0≤v≤Mj.m 成立时,这个class文件才可以被此Java虚拟机支持。Java虚拟机实现会遵从Java SE平台的某个发行版级别(the release level of the Java SE platform)㊀,而这个发行版级别决定了本虚拟机所能支持的版本范围。
㊀ 所谓“JavaSE平台的某个发行版级别”,可以大致对应于JDK的版本号。例如,Java SE 8相当于JDK
1.8.0。————译者注
Oracle的JDK在1.0.2版本时,Java虚拟机所支持的class格式版本号范围为 45.0(含)~ 45.3(含);JDK版本在1.1.x时,支持的
class格式版本号范围扩展至 45.0~45.65535 (含两端);JDK版本为l.k($k \geq 2$)时,对应的class文件格式版本号的范围为
45.0~44+k.0(含两端)。
- constant_pool_count (常量池计数器)
constant_pool_count 的值等于常量池表中的成员数加1。常量池表的索引值只有在大于0且小于constant_pool_count时才会认为是有效的㊀,对于 long 和 double 类型有例外情况,参见4.4.5小节。
㊀ 虽然值为0的 constant_pool 索引是无效的,但其他用到常量池的数据结构可以使用索引0来表示
“不引用任何一个常量池项”的意思。——译者注
- constant_pool [ ](常量池)
constant_pool 是一种表结构(见4.4节),它包含class文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其他常量。常量池中的每一项都具备相同的特征——第1个字节作为类型标记,用于确定该项的格式,这个字节称为tag byte(标记字节、标签字节)。
常量池以 1 ~ constant_pool_count-1 为索引。 - access_flags(访问标志)
access_flags 是一种由标志所构成的掩码,用于表示某个类或者接口的访问权限及属性。每个标志的取值及其含义如表4-1所示。
表4-1 类访问和属性修饰符标志
标志名 | 值 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | 声明为public,可以从包外访问 |
ACC_FINAL | 0x0010 | 声明为final,不允许有子类 |
ACC_SUPER | 0x0020 | 当用到invokespecial指令时,需要对父类方法做特殊处理① |
ACC_INTERFACE | 0x0200 | 该class文件定义的是接口而不是类 |
ACC_ABSTRACT | 0x0400 | 声明为abstract,不能被实例化 |
ACC_SYNTHETIC | 0x1000 | 声明为synthetic,表示该class文件并非由Java源代码所生成 |
ACC_ANNOTATION | 0x2000 | 标识注解类型 |
ACC_ENUM | 0x4000 | 标识枚举类型 |
① 此处“特殊处理”是相对于JDK 1.0.2 之前的class文件而言的,invokespecial的语义、处理方式在JDK
1.0.2 时发生了改变,为避免二义性,在JDK 1.0.2之后编译出的class文件都带有ACC_SUPER标志用以
区分。 ——译者注
带有ACC_INTERFACE 标志的class文件表示的是接口而不是类,反之则表示的是类而不是接口。
如果一个class文件被设置了 ACC_INTERFACE 标志,那么同时也得设置 ACC_ ABSTRACT 标志(JLS § 9.1.1.1 )。同时它不能再设置 ACC_FINAL、ACC_SUPER 或 ACC_ENUM 标志。
如果没有设置 ACC_INTERFACE 标志,那么这个class文件可以具有表4-1中除 ACC_ANNOTATION 外的其他所有标志。当然,ACC_FINAL 和ACC_ABSTRACT 这类互斥的标志除外(JLS § 8.1.1.2),这两个标志不得同时设置。
ACC_SUPER 标志用于确定类或接口里面的 invokespecial 指令使用的是哪一种执行语义。针对Java虚拟机指令集的编译器都应当设置这个标志。对于Java SE 8及后续版本来说,无论class文件中这个标志的实际值是什么,也不管class文件的版本号是多少,Java虚拟机都认为每个class文件均设置了 ACC_SUPER 标志。
ACC_SUPER 标志是为了向后兼容由旧Java编译器所编译的代码而设计的。目前的
ACC_SUPER 标志在由JDK 1.0.2 之前的编译器所生成的 access_flags 中是没有确
定含义的,如果设置了该标志,那么Oracle的Java虚拟机实现会将其忽略。
ACC_SYNTHETIC 标志意味着该类或接口是由编译器生成的,而不是由源代码生成的。 注解类型必须设置ACC_ANNOTATION 标志。如果设置了 ACC_ANNOTATION 标志, 那么也必须设置 ACC_INTERFACE 标志。
ACC_ENUM标志表明该类或其父类为枚举类型。
表4-1中没有使用的 access_flags 标志是为未来扩充而预留的,这些预留的标志在编译器中应该设置为0,Java虚拟机实现也应该忽略它们。
- this_class(类索引)
this_class 的值必须是对常量池表中某项的一个有效索引值。常量池在这个索引处的成员必须为CONSTANT_Class_info 类型结构体(见4.4.1小节),该结构体表示这个class文件所定义的类或接口。 - super_class(父类索引)
对于类来说,super_class的值要么是0,要么是对常量池表中某项的一个有效索引值。如果它的值不为0,那么常量池在这个索引处的成员必须为CONSTANT_Class_info 类型常量(见4.4.1小节),它表示这个class文件所定义的类的直接超类。在当前类的直接超类,以及它所有间接超类的ClassFile 结构体中, access_flags里面均不能带有ACC_FINAL 标志。
如果class文件的super_class的值为0,那这个class文件只可能用来表示Object类,因为它是唯一没有父类的类。
对于接口来说,它的class文件的super_class项必须是对常量池表中某项的一个有效索引值。常量池在这个索引处的成员必须为代表Object类的CONSTANT_Class_info 结构。
- interf