【深入理解Java虚拟机】【06】类文件结构

本文详细介绍了Java Class文件的结构,包括魔数、版本号、常量池、访问标志、类索引、父类索引、接口索引、字段表、方法表和属性表等,探讨了字节码指令的基础知识,展示了Java虚拟机如何实现平台无关性和语言无关性,为理解Java运行机制提供了关键信息。
摘要由CSDN通过智能技术生成

[TOC]

无关性的基石

代码编译的结果从本地机器码编程字节码,是存储格式发展的一小步,却是编程语言发展的一大步。Java 在诞生之初,曾经有一个著名的口号“一次编译,到处运行”,这句话充分表达了软件开发人员对冲破平台界限的渴求。

各种不同平台的虚拟机都统一使用的存储格式 —— 字节码,是构成平台无关性的基石。虚拟机的另外一种特性是语言无关性,目前已经出现了一大批在 Java 虚拟机之上运行的语言,比如 Scala、Clojure、Groovy 等。实现语言无关特性的基础仍然是虚拟机和字节码存储格式,Java 虚拟机不和包括 Java 在内的任何语言绑定,它只与 class 文件这种特定的二进制文件格式关联。

Class文件的结构

Class 文件是一组以 8 位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在 Class 文件中,中间没有添加任何分隔符。

Class 文件格式采用一种类似于 C 语言结构体的伪结构来存储数据,这种伪结构只有两种数据类型:无符号数和表。无符号数属于基本的数据类型,以 u1、u2、u4、u8 分别代表 1 个字节、2 个字节、4 个字节、8 个字节的无符号数,可以用来描述数字、索引引用、数量值或者 UTF8 编码构成的字符串。

表是由多个无符号数或其他表作为数据项构成的复合数据类型,所有表都习惯性地以 _info 结尾。表用于描述有层次关系的复合结构的数据,整个 Class 文件本质上就是一张表,它由以下数据项构成:

image-20190829104509907

image-20190829113916895

魔数和版本号

Class 文件的头 4 个字节称为魔数,其唯一作用是确定该文件是否是一个能被虚拟机接受的 Class 文件,其值固定为 0xCAFEBABE。

紧接着魔数的 4 个字节是 Class 文件的版本号,第 5、6 字节是次版本号 minor version,第 7、8 字节是主版本号 major version。jdk7 的主版本号是 51。

常量池

紧接着主次版本号之后的是常量池,常量池可以认为是 Class 文件中的资源仓库,它是 Class 文件中与其他项目关联最多的数据类型。

常量池的入库放置一个 u2 类型的数据,表示常量池容量计数值。与 Java 中语言习惯不同的是,这个容量计数从 1 而非从 0 开始。如果常量池容量为十六进制的 0x0016,即十进制的 22,那么表示常量池里有 21 项数据,索引值范围是 1 到 21。设计者将第 0 项常量空出来,是由特殊考虑的,方便在特定情况下表达“不引用任何一个常量池项目”的含义。Class 文件结构中,只有常量池的容量计数从 1 开始,其他都是从 0 开始。

常量池中主要存放两类数据:字面量和符号引用。字面量比较接近于 Java 语言层面的常量概念,如文本字符串、声明为 final 类型的常量值等。而符号引用则属于编译原理方面的概念,包括以下三类常量:

  1. 类和接口的全限定名;

  2. 字段的名称和描述符;

  3. 方法的名称和描述符。

常量池中的每一项常量都是一个表,JDK1.7 一共支持 14 种常量类型:

image-20190829114433102

其中每一项常量的结构组成为:

image-20190829114726127

image-20190829114746273

访问标志

在常量池结束之后,紧接着的 2 个字节代表访问标志 access flags,用于识别一些类或接口级别的访问信息,比如:这个 Class 是类还是接口?是否 public?如果是类的话,是否被声明为 final?具体标志见下表:

image-20190829114845381

类索引、父类索引和接口索引集合

Class 文件中由这三项数据来确定这个类的继承关系。由于 Java 不允许多重继承,因此父类索引也只有一个,类索引和父类索引使用两个 u2 类型的索引值表示,各自执行一个类型为 CONSTANT_Class_info 的类描述符常量。对于接口索引集合,入口的第一项 u2 类型数据表示索引表的容量,也就是接口数量,其他 u2 类型数据同样指向类描述符常量。

字段表集合

字段表结构

字段表 field_info 用于描述接口或类中声明的变量,包括类级变量和实例级变量,但不包括方法内部声明的局部变量。在 Java 中描述一个字段的信息有哪些呢?

  1. 字段的作用域:public、private、protected

  2. 实例级变量还是类级变量:static

  3. 可变性:final

  4. 可见性:volatile

  5. 可否序列化:transient

  6. 字段数据类型

  7. 字段名称

字段表结构如下图所示,第一项数据是 access_flags,与类中的访问标志类似。紧随其后的是两个索引值:name_index、descriptor_index,分别表示字段的简单名称以及字段和方法的描述符。

image-20190829115602734

image-20190829143850626

描述符

这里解释一下,什么是简单名称、描述符,以及前面提到的“全限定名”?全限定名和简单名称好理解,比如 com/apache/util/TestClass 是类的全限定名,就是把类全名里的 . 换成了 /,结尾加上分号 ; 用于分割多个连续的全限定名。简单名称是没有类型和参数修饰的方法或字段名称,比如 int m(),其简单名称就是 m。

相对于全限定名和简单名称来说,方法和字段的描述符更复杂些,因为是包含的信息更多:字段类型、方法参数和返回值类型。基本数据类型以及 Void 类型都用一个大写字符标识,对象类型用 L 加对象的全限定名标识,对于数组每个维度使用一个 [ 来表示,二维数组就是 [[,一个整型数组 int[] 会被记录为 [I。

当使用描述符来描述方法的时候,按照先参数列表后

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值