类文件结构
本文为《深入理解Java虚拟机_第三版 周志明》学习笔记
Class类文件的结构
概念
任何一个Class文件都对应着唯一的一个类或接口的定义信息。class文件是一组以8个byte为基础的二进制流,存储的内容几乎全部是程序运行的必要数据。只包含俩种数据类型:“无符号数”和“表”。
class文件中不会保存各个方法,字段最终在内存中的布局信息,这些字段,方法的符号引用不经过虚拟机在运行期间转换的话是无法得到真正的内存入口地址。
魔术与Class文件的版本
每个class文件的前4个字节放的是0xCAFEBABY。5~ 6字节是次版本号,7~ 8是主版本号。Java的版本号是从45开始的,每次遇到大版本发布,主版本号向上+1。
常量池
constant_pool_count的数量是0是占位的,从1开始计数。
常量池中主要存放两大类常量:字面量和符号引用。
- 字面量:文本字符串,被声明为final的常量值等;
- 符号引用则属于编译原理方面的概念:
- 被模块导出或者开放的包(Package)
- 类和接口的全限定名(Full Qualified Name)
- 字段的名称和描述符(Descriptor)
- 方法的名称和描述符
- 方法句柄和方法类型(Method Handle, Method Type, Invoke Dynamic)
- 动态调用点和动态常量
常量池中每一项常量都是一个表,截至JDK13,常量表中分别有17种不同类型的常量。可用 javap -verbose XXXClass
查看常量表。
访问标志
用于识别一些类或者接口层次的访问信息。访问标志一共有16个标志位可以使用,没有用到的标志位一律置为0。
类索引、父类索引、接口索引集合
class文件由类索引,父类索引,与接口类型集合来确定该类的继承关系。
类索引用于确定这个类的全限定名,父索引用于确定父类的全限定名(除了Object所有的类都有父类,所有的类父索引都不为0)。接口索引集合就是用来描述这个类实现了哪些接口,这些被实现的接口将按implements关键字后的接口顺序从左向右排列在接口索引集合中。
类名: cn/ixp/test/TestClass
全限定名: cn.ixp.test.TestClass;
字段表集合
用于描述接口或者类中声明的变量。不会列出从父类或者父接口中继承来的字段,但会出现原本Java代码中不存在的字段(编译器会根据代码进行优化)。
Java语言字段是无法重载的,两个字段的数据类型、修饰符不管是否相同,都必须使用不一样的名称,但对于Class文件格式来讲,只要两个字段的描述不是完全相同重名也无所谓。
紧随access_flags后的name_index和descriptor_index代表字段的简单名称以及字段和方法的描述。都是对常量池项的引用。
简单名称:就是没有类型和参数修饰的方法或者字段名称。
public int m;
// 简单名称 m
public void inc();
// 简单名称 inc
描述符:作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。如下:
用描述符描述方法时,按照先参数,后返回值的顺序。对于数组来说,每一维度将使用前置的[表示;
// ()V
void inc();
// ()Ljava/lang/String
java.lang.String toString();
// ([CII[C)I
int indexOf(char[] source, int off, int count, char[] target);
方法表集合
对方法的描述与对字段的描述几乎一致,在访问标志和属性表集合中的可选项有区别。方法中的代码通过Javac编译器编译成字节码指令,存放在方法属性表集合中一个Code属性里面。