Class文件结构梳理(持续更新)

  发布日期:   2017-07-03
  最新修改:   2020-03-27
  阅读次数:   43 次
  • 每一个 Class 文件对应于一个如下所示的 ClassFile 结构体。

      ClassFile {
         u4 magic; 
         u2 minor_version;
         u2 major_version;
         u2 constant_pool_count;
         cp_info constant_pool[constant_pool_count-1];
         u2 access_flags;
         u2 this_class;
         u2 super_class;
         u2 interfaces_count;
         u2 interfaces[interfaces_count];
         u2 fields_count;
         field_info fields[fields_count];
         u2 methods_count;
         method_info methods[methods_count];
         u2 attributes_count;
         attribute_info attributes[attributes_count]; 
    
      }
  • ClassFile 结构体中,各项的含义描述如下:  magic 魔数,魔数的唯一作用是确定这个文件是否为一个能被虚拟机所接受的 Class 文件。魔数值固定为 0xCAFEBABE,不会改变。 

  • minor_version、major_version 副版本号和主版本号,minor_version 和 major_version 的值分别表示 Class 文件 的副、主版本。它们共同构成了 Class 文件的格式版本号。譬如某个 Class 文件的主版 本号为 M,副版本号为 m,那么这个 Class 文件的格式版本号就确定为 M.m。Class 文件格式版本号大小的顺序为:1.5 < 2.0 < 2.1。 一个 Java 虚拟机实例只能支持特定范围内的主版本号(Mi 至 Mj)和 0 至特定范围 内(0 至 m)的副版本号。假设一个 Class 文件的格式版本号为 V,仅当Mi.0 ≤ v ≤Mj.m 成立时,这个 Class 文件才可以被此 Java 虚拟机支持。不同版本的 Java 虚拟机实现支持的版本号也不同,高版本号的 Java 虚拟机实现可以支持低版本号的 Class 文件,反之则不成立1。

  • constant_pool_count 常量池计数器,constant_pool_count 的值等于 constant_pool 表中的成员数加 1。 constant_pool 表的索引值只有在大于 0 且小于 constant_pool_count 时才会被 认为是有效的2,对于 long 和 double 类型有例外情况,可参见§4.4.5。

  • constant_pool[] 常量池,constant_pool 是一种表结构(§4.4),它包含 Class 文件结构及其子结构 中引用的所有字符串常量、类或接口名、字段名和其它常量。常量池中的每一项都具备相 同的格式特征——第一个字节作为类型标记用于识别该项是哪种类型的常量,称为“tag byte”。常量池的索引范围是 1 至 constant_pool_count−1。

  • access_flags 访问标志,access_flags 是一种掩码标志,用于表示某个类或者接口的访问权限及基础属性。access_flags 的取值范围和相应含义见表 4.1 所示。

表 4.1 访问和修饰符标志

标记名 含义
ACC_PUBLIC 0x0001 可以被包的类外访问。
ACC_FINAL 0x0010 不允许有子类。
ACC_SUPER 0x0020 当用到 invokespecial 指令时,需要特殊处理3的 父类方法。
ACC_INTERFACE 0x0200 标识定义的是接口而不是类。
ACCABSTRACT 0x0400 不能被实例化。
ACC_SYNTHETIC 0x1000 标识并非 Java 源码生成的代码。
ACC_ANNOTATION 0x2000 标识注解类型
ACC_ENUM 0x4000 标识枚举类型
1 Oracle 的 JDK 在 1.0.2 版本时,支持的 Class 格式版本号范围是 45.0 至 45.3;JDK 版本在 1.1.x 时,支持的 Class 格式版本号范围扩展至 45.0 至 45.65535;JDK 版本为 1. k 时(k ≥2)时,对应的 Class 文件格式版本号的范围是 45.0 至 44+k.0

2 译者注:虽然值为 0 的 constant_pool 索引是无效的,但其他用到常量池的数据结构可以使用索引 0 来 表示“不引用任何一个常量池项”的意思。

3 译者注:此处“特殊处理”是相对于 JDK 1.0.2 之前的 Class 文件而言,invokespecial 的语义和处 理方式在 JDK 1.0.2 时发生了变化,为避免二义性,在 JDK 1.0.2 之后编译出的 Class 文件,都带有 ACC_SUPER 标志用以区分。

  • 带有 ACC_SYNTHETIC 标志的类,意味着它是由编译器自己产生的而不是由程序员 编写的源代码生成的。
  •  带有 ACC_ENUM 标志的类,意味着它或它的父类被声明为枚举类型。
  •  带有 ACC_INTERFACE 标志的类,意味着它是接口而不是类,反之是类而不是接口。 如果一个 Class 文件被设置了 ACC_INTERFACE 标志,那么同时也得设置 ACC_ABSTRACT标志(JLS §9.1.1.1)。同时它不能再设置ACC_FINAL、 ACC_SUPER 和 ACC_ENUM 标志。
  •  注解类型必定带有 ACC_ANNOTATION 标记,如果设置了 ANNOTATION 标记, ACC_INTERFACE 也必须被同时设置。如果没有同时设置 ACC_INTERFACE 标记, 那么这个 Class 文件可以具有表 4.1 中的除 ACC_ANNOTATION 外的所有其它标记。 当然ACC_FINAL和ACC_ABSTRACT这类互斥的标记除外(JLS §8.1.1.2)。
  •  ACC_SUPER 标志用于确定该 Class 文件里面的 invokespecial 指令使用的是哪 一种执行语义。目前 Java 虚拟机的编译器都应当设置这个标志。ACC_SUPER 标记 是为了向后兼容旧编译器编译的 Class 文件而存在的,在 JDK1.0.2 版本以前的编 译器产生的 Class 文件中,access_flag 里面没有 ACC_SUPER 标志。同时, JDK1.0.2 前的 Java 虚拟机遇到 ACC_SUPER 标记会自动忽略它。
  •  在表 4.1 中没有使用的 access_flags 标志位是为未来扩充而预留的,这些预留的 标志为在编译器中会被设置为 0, Java 虚拟机实现也会自动忽略它们。
  • this_class 类索引,this_class 的值必须是对 constant_pool 表中项目的一个有效索引值。 constant_pool 表在这个索引处的项必须为 CONSTANT_Class_info 类型常量 (§4.4.1),表示这个 Class 文件所定义的类或接口

  • super_class 父类索引,对于类来说,super_class 的值必须为 0 或者是对 constant_pool 表中 项目的一个有效索引值。如果它的值不为 0,那 constant_pool 表在这个索引处的项 必须为 CONSTANT_Class_info 类型常量(§4.4.1),表示这个 Class 文件所定义的 类的直接父类。当前类的直接父类,以及它所有间接父类的 access_flag 中都不能带有 ACC_FINAL 标记。对于接口来说,它的 Class 文件的 super_class 项的值必须是 对 constant_pool 表中项目的一个有效索引值。constant_pool 表在这个索引处的 项必须为代表 java.lang.Object 的 CONSTANT_Class_info 类型常量(§4.4.1)。 如果 Class 文件的 super_class 的值为 0,那这个 Class 文件只可能是定义的是 java.lang.Object 类,只有它是唯一没有父类的类。

  • interfaces_count 接口计数器,interfaces_count 的值表示当前类或接口的直接父接口数量。

  • interfaces[] 接口表,interfaces[]数组中的每个成员的值必须是一个对 constant_pool 表中项 目的一个有效索引值,它的长度为 interfaces_count。每个成员 interfaces[i] 必 须为CONSTANT_Class_info类型常量(§4.4.1),其中0 ≤ i < interfaces_count。在 interfaces[]数组中,成员所表示的接口顺序和对应的源 代码中给定的接口顺序(从左至右)一样,即 interfaces[0]对应的是源代码中最左 边的接口。

  • fields_count 字段计数器,fields_count 的值表示当前 Class 文件 fields[]数组的成员个数。 fields[]数组中每一项都是一个 field_info 结构(§4.5)的数据项,它用于表示 该类或接口声明的类字段或者实例字段1。

  • fields[] 字段表,fields[]数组中的每个成员都必须是一个 fields_info 结构(§4.5)的数 据项,用于表示当前类或接口中某个字段的完整描述。fields[]数组描述当前类或接口 声明的所有字段,但不包括从父类或父接口继承的部分。

  • methods_count 方法计数器,methods_count 的值表示当前 Class 文件 methods[]数组的成员个数。 Methods[]数组中每一项都是一个 method_info 结构(§4.5)的数据项。

  • methods[]方法表,methods[]数组中的每个成员都必须是一个 method_info 结构(§4.6)的 数据项,用于表示当前类或接口中某个方法的完整描述。如果某个 method_info 结构 的 access_flags 项既没有设置 ACC_NATIVE 标志也没有设置 ACC_ABSTRACT 标志, 那么它所对应的方法体就应当可以被 Java 虚拟机直接从当前类加载,而不需要引用其它 类。method_info 结构可以表示类和接口中定义的所有方法,包括实例方法、类方法、 实例初始化方法方法(§2.9)和类或接口初始化方法方法(§2.9)。methods[]数组 只描述当前类或接口中声明的方法,不包括从父类或父接口继承的方法。

  • attributes_count 属性计数器,attributes_count 的值表示当前 Class 文件 attributes 表的成员个 数。attributes 表中每一项都是一个 attribute_info 结构(§4.7)的数据项。

  • attributes[] 属性表,attributes 表的每个项的值必须是 attribute_info 结构(§4.7)。在本 规范里,Class 文件结构中的 attributes 表的项包括下列定义的属性: InnerClasses(§4.7.6)、EnclosingMethod(§4.7.7)、Synthetic(§4.7.8)、 Signature(§4.7.9)、SourceFile(§4.7.10),SourceDebugExtension (§4.7.11)、Deprecated(§4.7.15)、RuntimeVisibleAnnotations (§4.7.16)、RuntimeInvisibleAnnotations(§4.7.17)以及 BootstrapMethods(§4.7.21)属性。对于支持 Class 文件格式版本号为 49.0 或 更高的 Java 虚拟机实现,必须正确识别并读取 attributes 表中的 Signature (§4.7.9)、RuntimeVisibleAnnotations(§4.7.16)和 RuntimeInvisibleAnnotations(§4.7.17)属性。对于支持 Class 文件格式版 本号为 51.0 或更高的 Java 虚拟机实现,必须正确识别并读取 attributes 表中的 BootstrapMethods(§4.7.21)属性。本规范要求任一 Java 虚拟机实现可以自动 忽略 Class 文件的 attributes 表中的若干(甚至全部)它不可识别的属性项。任何本 规范未定义的属性不能影响 Class 文件的语义,只能提供附加的描述信息(§4.7.1)。


   转载规则

《Class文件结构梳理(持续更新)字》GajAngels 采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。