Java虚拟机规范 Java SE 8版 - class文件格式(一)

本文详细介绍了Java虚拟机的ClassFile结构,包括类和接口的二进制名称、描述符、常量池、字段、方法、属性等各个组成部分,以及其在class文件中的表示方式和格式规定。通过对这些内容的理解,读者可以更好地掌握Java类文件的底层原理。
摘要由CSDN通过智能技术生成

本章将描述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.0vMj.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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值