Class文件是一组以8位字节为基础单位的二进制字节流,各个数据项目严格按照顺序排列在Class文件中,中间没有任何分隔符。
如果一个数据需要8位以上的空间,则会按照高位在前(最高为字节在地址最低位,最低位字节在地址最高位)的方式分割成若干个8位字节进行存储。
Class文件只有两种数据类型:无符号数和表。
每个Class文件的头4个字节称为魔数,它唯一的作用是确定这个文件是否是一个能被虚拟机接受的Class文件。Class文件的魔数是:0xCAFEBABE。
紧接着魔数的4个字节存储的是Class文件的版本号:第5和第6个字节是次版本号,第7和第8字节是主版本号。
紧接着主次版本号之后的是常量池入口,常量池可以理解为Class文件之中的资源仓库,它是Class文件结构中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一。
由于常量池中常量的数量不固定,所以在常量池的入口需要放置一项u2类型的数据,代表常量容器计数值。与Java语言习惯不同,它是以计数值1开始的。
常量池中主要存放两大类常量:字面量和符号引用。
字面量:接近Java语言层面的常量的概念,如文本字符串、声明为final的常量值等。
符号引用:属于编译原理方面的概念,包括三类常量:
常量池中每一项常量都是一个表,表开始的第一位是一个u1类型的标置位,代表当前这个常量属于哪种常量类型。
常量池结束后,紧接着的两个字节代表访问标志,这个标志用于识别一些类或接口的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final等。
类索引和父索引都是一个u2类型的数据,而接口索引集合是一组u2类型数据的集合,Class文件中由这三项数据确定类的继承关系。
类索引用于确定这个类的全限定名,父索引用于确定这个类的父类的全限定名。接口索引集合用来描述这个类实现的接口,这些被实现的接口按照implements语句(如果本身是接口,则应该是extends语句)的顺序排列。
字段表用于描述接口或类中声明的变量。字段包括类级变量和实例级变量,但不包括方法内部声明的局部变量。
一个字段需要保存哪些信息?字段的作用域(public、protected、private)、是实例变量还是类变量(static修饰符)、可变性(final)、并发可见性(volatile)、可否被序列化(transient)、字段的数据类型(基本类型、对象、数组)、字段名称。
字段表结构依次包括:访问标志(access_flags)、名称索引(name_index)、描述符索引(descriptor)、属性表集合(attribute_info)。除了属性表集合是表,其他类型都是一个u2类型。
方法表结构同字段表一样,依次包括了:访问标志、名称索引、描述符索引、属性表集合这几项。除了属性表集合是表,其他类型都是一个u2类型。
方法表中没有保存方法体,方法体存放在方法属性表中的“Code”属性里面。属性表是Class文件最具扩展性的一种数据项目。
Class文件、字段表、方法表都可以携带自己的属性表集合,以用于描述某些场景的专有信息。《Java虚拟机规范(Java SE7)》中预定义了21项属性。
Java方法体中的代码经过javac编译器处理后,最终变为字节码指令存储在Code属性内。
列举出方法中可能抛出的受查异常,也就是方法描述时在throws关键字后面列举的异常。
用于描述Java源码行号和字节码行号(字节码偏移量)之间的对应关系。该属性不是必须的,如果选择不生成该属性,对程序运行时最主要的影响是抛出异常时不会显示出错行号,也无法按照源码行设置断点。
用于描述栈帧中局部变量表中的变量与Java源码中定义变量之间的关系,它不是必须的属性。如果选择不生成该属性,在调试期间无法根据参数名称从上下文获得参数值。
用于记录生成这个class文件的源文件名称。该属性也是可选的。如果不生成该属性,当抛出异常时堆栈中不会显示出错代码所属的文件名。
通知虚拟机自动为静态变量赋值。只有被static关键字修饰的变量才可以使用这项属性。
变量赋值问题: 对于非static的变量(即实例变量)的赋值实在实例构造器<init>方法中进行的; 对于static变量(类变量),有两种方式选择:在类构造器<clinit>方法中或者使用ConstantValue属性。目前Sun Javac编译器的选择是:如果同时使用final和static来修饰一个变量(这里称为常量更贴切),并且这个变量的类型是基本数据类型或String时,就生成ConstantValue属性来初始化;如果这个变量没有被final修饰,或者并非基本类型及字符串,则在<clinit>方法中进行初始化。
用于记录内部类和宿主类之间的关联。如果一个类中定义了内部类,那编译器就会为它和它的内部类生成InnerClasses属性。