Class文件结构
说明:本文中,通俗地将任意一个有效的类或接口所应当满足的格式称为 “Class 文件格式”,实际上它并不一定以磁盘文件的形式存在。
Class文件是一组以8位字节(对应2位16进制编码)为基础单位的二进制流,各个项目严格按照顺序紧凑地排列在Class文件之中(保证了解析地准确性),当遇到8位字节以上空间的数据项时,则会按照高位在前的方式(Big-Endian)分割成若干个8位字节进行存储。
根据Java虚拟机规范,Class文件格式采用一种类似C语言结构体的形式来组织数据,这种伪结构体中只有两种数据类型:无符号数 和 表,后面的解析以这两种数据类型为基础。
无符号数属于基本的数据类型,以 u1、u2、u4、u8 来分别表示1个字节、两个字节etc。无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。表是由多个无符号数或者其它表作为数据项构成的复合数据类型,表都习惯地以_info 结尾。类似于ViewGroup与View的关系。
Class本身就是一个表,它其中各个数据的顺序与各项数据所占的大小如下图.无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干个连续的数据项的形式,这时称这一连续的某一类型的数据为某一类型的集合
下面我们将根据这张表来详细讲解每一个数据项的大小以及含义
魔数与Class文件的版本 [magic/minor_version/major_version]
u4表明了魔数所占大小为4个字节(32位,8个16进制编码位),magic number 的唯一作用是确定这个文件是否为一个能被虚拟机接受的 Class 文件。
紧接着魔数的4个字节存储的是Class文件的版本号: minor_version & & major_version
常量池
紧接着版本号的是 常量池入口。常量池可以理解为 Class 文件中的资源仓库,它也是 Class 文件结构中与其它项目关联最多的数据类型,也是Class 文件空间中最大的数据项目之一,同时它还是 Class 文件中第一个出现的表类型数据结构
u2 | constant_pool_count | 1 |
---|---|---|
cp_info | constant_pool | constant_pool_count-1 |
u2就是常量池的入口的数据类型,代表常量池容量计数值(constant_pool_count),这是因为常量池(cp_info)中常量的数量不是固定的。与Java中不同的是,这个容量计数是从1开始而非0. 这样做也是有目的的,是为了表达 “满足后面某些指向常量池的索引值的数据在特定情况下需要表达’不引用任何一个常量值项目’”的含义
常量池中主要存放两大类常量:
字面量(Literal)
字面量比较接近 Java 语言层的常量概念,例如文本字符串、声明为final的常量值等
符号引用(Symbolic References)
Java代码在进行Javac编译的时候,并不像C和C++那样有”连接”这一步骤,而是在虚拟机加载Class文件的时候进行动态链接。也就是说,在Class文件中不会保存各个方法、字段的最终内存布局信息,因此这些字段、方法的符号引用不经过运行期转换的话无法得到真正的内存如扣地址,也就无法直接被虚拟机使用。
符号引用属于编译原理方面的概念,包含以下三类常量:
- 类和接口的全限定名( Fully Qualified Name)
- 字段的名称和描述符(Descriptor)
- 方法的名称和描述符
常量池中的每一项常量都是一个表(_info),这14种表都有一个特点,就是表开始的第一位是一个 u1 类型的标志位(tag),代表当前这个常量属于哪种类型,这14种常量类型所代表的具体含义如下图
下图是上图的详尽版,不仅包含tag 类型信息,而且包含了常量的具体信息(比如UTF-8b字符串的length和bytes)
访问标志 [Access_flags]
紧接着常量池的两个字节表示访问标志,用于识别一些类或者接口层次的访问信息。包括:这个Class是类还是接口,是否定义为 public 类型是否定义为 abstract 类型,如果是类的话,是否被声明为 final 等等。
类索引、父类索引 与 接口索引集合
this_class / super_class 都是u2类型的数据,interfaces 是一组 u2 类型的数据的集合。Class 文件由这三项数据来确定该类的继承关系。
- 类索引用于确定这个类的全限定名
- 父类索引用于确定这个类的父类的全限定名(除了 java.lang.Object 之外,有且仅有一个父类)
- 接口索引集合用来描述这个类实现了哪些接口,这些被实现的接口将按 implements (如果该类本身就是一个接口,则是extends )语句顺序从左到右排列在接口索引集合中。
如图
this_class | super_class | interfacecount | interface | |||
---|---|---|---|---|---|---|
u2 | u2 | u2 | u2 (0) | u2 | ……… | u2 (interface_count) |
字段表集合 [field_info]
field_info 用于描述接口或者类中声明的变量。字段(field)包括类级变量和实例级变量,但不包括在方法中声明的局部变量(临时变量)。
我们可以想象一下在Java中描述一个字段可以包含什么信息
下图是他的最终形式以及表格表示:
看到上图中索引值一般都以 _index 结尾,表示引用别的地方
现在我们需要区分你几个概念:
简单名称 | 没有类型或者参数修饰的方法或者字段名称 | |
---|---|---|
全限定名 | 包含类型和参数修饰的方法或者字段名称 | |
描述符 (descriptor ) | 描述字段的数据类型、方法的参数列表和返回值 | |
方法表集合
理解了字段表集合,就很容易理解方法表集合。
概念回顾:
表:由多个无符号数或其它表作为数据项构成的复合数据类型,习惯以 _info 结尾
集合:无论是无符号数,还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干个连续的数据项的形式,这时称这一系列连续的某一个类型的数据为某一类型的集合