1、Java编译期
Java文件(.java文件)通过Java编译器转化成Java字节码文件(.class文件)。通过类装载器将class文件装载进Java虚拟机中进行后续操作。见下图:
2、class文件概述
Class文件格式中只包含两种类型:无符号数、表
2.1 无符号数
基本数据类型,以u1(1个字节)、u2(2个字节)、u4(4个字节)、u8(8个字节)来代表几个字节的无符号数
2.2 表
由多个无符号数和其他表构成的复合数据类型,通常以“_info”结尾
3、ClassFile结构
3.1 魔数u4(magic)
每个class文件头四个字节被称为魔数(CAFEBABE),它的唯一作用是确定该class文件是否能被JVM虚拟机接受。
3.2 副版本号u2(minor_version)、主版本号u2(major_version)
紧跟魔数后的第五、六个字节代表存储class文件的副版本号
第七、八个字节代表存储class文件的主版本号
3.3 常量池数量u2(constant_pool_count)
第九、十个字节表示常量池中的常量个数,因为它的计数是从1开始的,所以实际中常量池中的容量比十六进制表示的值要小1
3.4 常量池信息(cp_info)
常量池中的每一项常量都是一个表,截止JDK13时,常量表中共有17个常量类型。
常量表中都有如下特点:
(1)表结构的头一个字节是u1类型的标志位(tag);
(2)info[]中的内容由前面tag对应的类型来决定
3.4.1 tag对应的常量类型见下图:
3.4.2 常量池中17种数据类型的结构总表
3.5 访问标记u2(access_flags)
常量池结束之后,紧跟着的两个字节代表访问标记(access_flags),用于表示类或接口的访问信息。具体的标记值及其含义见下图:
3.6 类索引u2(this_class)、父类索引u2(super_class)
访问标记结束之后,后面的四个字节头两个代表当前类索引,及其父类索引,类索引用于确定当前类的全限定名,父类索引用于确定当前类父类的全限定名。由于Java中所有类的父类都为Object类,所以除了Object类以外,父类索引都不应为0。
类索引和父类索引各自指向一个类型为CONSTANT_Class_info的类描述符常量,通过CONSTANT_Class_info类型的常量中的索引值可以找到定义在CONSTANT_Utf8_info类型的常量中的全限定名字符串。
3.7 接口数量u2(interfaces_count)、接口索引u2(interfaces[interfaces_count])
接口索引是一组u2类型的数据的集合,它和上面的类索引以及父类所以用来确定该类型的继承关系。如果接口数量为0,name后面的接口所以将不会显示。
3.8 字段数量u2(field_count)
3.8.1 字段结构格式
3.8.2 字段访问标志(access_flags所对应的)
跟随access_flags的是两项索引值name_index和descriptor_index,他们都是对常量池的引用,分别代表了字段的简单名称和字段以及方法的描述符。
后续的attribute_info指的是属性表的集合,是一些辅助信息。
3.9 方法数量u2(method_count)
方法和上面的字段的相关描述几乎一致,头两个字节表示方法的数量,方法表的结构中同样包含访问标志(access_flags)、名称索引(name_index)、描述符索引(descriptor_index)、属性表集合(attribute_info)几项内容,方法表结构以及方法访问标志见下图:
在方法定义的过程中,除了Java文件中自己定义的方法以外,JVM中还会自动给我们定义类初始化方法“<clinit>()”和实例初始化方法“<init>()”,根据Java代码中所写的内容来确定。
同样的,名称索引(name_index)、描述符索引(descriptor_index)也是在常量池当中去找对应的信息。
3.10 属性表集合
所有属性的通用格式见下图: