以下内容主要还是参考《Inside JVM》
什么是java类文件
Java类文件是一个精确定义为Java程序二进制文件格式。每个Java类文件是一个Java类或接口的完整描述。没有办法把多个类或接口到单个类文件。精确定义的类文件格式可以确保任何Java类文件可以加载和正确解释任何Java虚拟机,无论什么系统产生的类文件或系统主机的虚拟机。
虽然类文件与Java语言架构,它不是Java语言密不可分。你可以用其他语言编写程序并编译类文件,或者你可以编译Java程序不同的二进制文件格式。然而,大多数Java程序员会使用类文件作为主要工具提供Java虚拟机的程序。
java类文件是一个8位字节的二进制流。数据顺序项存储在没有填充的类文件的相邻的项目之间。没有填充有助于保持紧凑的类文件,超过一个字节的数据是分成几个连续的字节,出现在高位字节顺序中。
就像java类可以包含不同数字的字段,方法,方法参数,局部变量等,java类文件也可以许多大小不同的项目或者数量从一个类文件到另一个地方。在类文件中,类文件的大小或者长度变长项先于实际的数据项,这允许类文件流解析从头到尾,先是读项目的大小然后再读数据。
幻数
每个java类文件的前四个字节都是幻数,以0xCAFEBABE开头。幻数使非java类文件更容易识别。如果一个文件不是以0xCAFEBABE开头,那它绝不是一个java类文件。设计任意没有在广泛使用的任意数字作为一个幻数。当“java”被称为“Oak”时,java类文件的幻数可以选择过去式。帕特里克•诺顿表示 最初的Java团队的关键成员,之前曾说过关于这门语言幻数选择Java名称时。我们正在寻找一些有趣,独特,容易记住。这只是一个巧合0 xcafebabe开头,暗指可爱的咖啡师在皮特的咖啡,预示了Java名称。”
主次版本号
第二个类文件的四个直接包含主次版本号。 随着java技术的发展,新功能偶尔会被添加到java类文件的格式中。每次类文件格式的改变,版本号也会随之改变。对java虚拟机而言,版本号的标示的格式需要遵循一个特定的类文件。Java虚拟机通常能够负载类文件与给定的主版本号和一系列的次要版本号。Java虚拟机必须拒绝类超出它们的有效范围的文件版本号。
常量池数和常量池
类文件中的幻数和版本号都是常量池。常量池包含常量定的类或者接口文件。如这些常量:字符串,final类型的变量,类名,方法名都存储在常量池中。常量池被组织成一个条目列表。在一个计数条目列表中,常量池数优先于实际的列表,常量池。
在常量池中许多元素引用其它元素,和许多类文件中的常量池引用常量池中的元素。整个类文件,常量池条里的元素被整数索引,它们的位置在常量池列表中。列表中的第一个元素的索引,第二个索引,等等。尽管没有在常量池列表中的元素但是它的索引为0,包含在常量池数的常量池数为0 的元素中。例如,如果一个常量池列表包含14个元素(1到14的索引),那么下一个常量池数为15。
每个常量池条目从一个字节标记开始,表明该类型的常量在那个列表中标记这个起始位置。一旦一个java迅即抓住并解析这个标记后,它就知道会发生什么。
上面的表中,是展示的为每个标签对应的表。表的名称是由附加“_info”标签的名字。例如表CONSTANT_Class标签对应的表名称是CONSTANT_Class_info。CONSTANT_Utf8_info表存储压缩的Unicode字符串形式。
在java程序动态链中 常量池扮演着重要的角色。除了字面常量,常量池也包含下面的类型的符号引用。
- 类和接口的全限定名
- 字段的名称和描述符
- 方法名称和描述符
一个字段是一个类或者接口的实例或者类变量。一个字段描述是一个字符,表示字段类型。方法描述是一个字符,表示该方法返回的类型的参数。常量池是完全限定的名称和使用方法和字段描述符在运行时链接代码在这个类或接口与其他类和接口的代码和数据。类文件不包含信息的最终的内存布局组件,因此类、字段和方法不能直接引用类的字节码文件。Java虚拟机解决任何引用的项目的实际地址在运行时常量池中的象征性参考。例如,字节码指令,调用一个方法给常量池指数的符号引用调用的方法。
访问标志
前两个字节在常量池后,访问标志,揭示几项信息关于类文件中定义的类或者接口。首先,访问标志标志是否是一个类或者接口。访问标志也表明修饰符所使用的类或者接口的声明。也可以是抽象或者公共的类和接口。类可以是final类型的,尽管final类型的类不能是抽象的。接口不能是final来修饰。
this_class
接下来的两个字节是this_class项,一个在常量池的索引。常量池的入口位置this_class必须是是表CONSTANT_Class_info,它有两部分:一个标签,一个名字索引。这个标签值是CONSTANT_Class。常量池项入口位置的名字索引将是包含类或者接口的完全限定名的表CONSTANT_Utf8_info。
this_class项提供类一个如何使用常量池。this_class项本身就是一个在常量池中的索引。当java虚拟机查找this_class常量池入口的位置时,它将发现通过标签标示本身的条目CONSTANT_Class_info。java虚拟机知道CONSTANT_Class_info条目索引到常量池中,称为名字索引,在它们的标签后面。所以虚拟机查找name_index常量池入口的位置,应该在哪里找到CONSTANT_Utf8_info条目包含类或接口的完全限定名称
super_class
在类文件在this_class 是super_class的项目,另一个两字节的索引到常量池中。常量池入口位置super_class将CONSTANT_Class_info条目指该类超类的完全限定名称。在java程序中所有对象的基类都是java.lang.Object class,super_class常量池索引对每个类有效除了Object。对于Object类而言,它没有super_class。接口的常量池入口位置的super_class是java.lang.Object。
接口数量和接口
组件super_class始于interfaces_count,超接口的数量计数直接在实现的类或者接口中定义该文件。立即计数接口是一个数组,其中包含一个索引到常量池中为每个超接口直接由这个类或者接口实现。每个超接口由常量池中的一个CONSTANT_Class_info项表示,指的是接口的完全限定名称。唯一的直接超接口,那些出现在类的实现或者接口的扩展,出现在这个数组中。数组中的超接口出现在它们出现的顺序(从左到右)实现或扩展。
字段数和字段
类文件中的接口组件是描述声明这个类或者接口的字段。这个组件是从fields_count开始,一个数量计数的字段,包括类和实例变量。数是表variable-length field_info的列表,对应着每个字段。唯一的字段出现在字段列表中是那些被定义的类或接口中定义的文件。没有字段从超类继承或它的超接口出现在字段列表。
每个 field_info表显示一个字段信息。表中包含字段名称,描述符和修饰符。如果字段声明为final类型,field_info表也揭示该字段的常量值。它的一些信息包含在field_info表中,常量池中包含的位置被称为表。
方法数和方法
通过类或者接口声明的方法描述在类文件中的字段。这个组件从methods_count开始,类或者接口中的双字节方法数量。数只包括那些明确定义的类或接口的方法。