类文件结构

class文件结构

  • Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件中,中间没有添加任何分隔符。
  • 根据Java虚拟机规范的规定,Class文件格式采用一种类似于C语言结构体的伪结构来存储,这种伪结构中只有两种数据类型:无符号数和表。无符号数属于基本数据类型,以u1、u2、u4、u8来分别代表1、2、4、8个字节的无符号数。表是由多个无符号数或其他表作为数据项构成的符合数据类型,所有的表都习惯性地以“_info”结尾。
ClassFile{
magic                        u4,
minor_version                u2,
major_version                u2,
constant_pool_count            u2,
constant_pool                cp_info*constant_pool_count,
access_flags                 u2,
this_class                   u2,
super_class                  u2,
interface_count              u2,
interfaces                   u2 * interface_count,
fields_count                 u2,
fields                      field_info * fields_count,
methods_count                u2,
methods                      method_info * methods_count,
attributes_count             u2,
attributes                   attributes_info * attributes_count
}

magic(魔数) u4

  • 值为0xcafebabe,没有特别的意义,放在文件头并选取用来标记该文件是一个class文件。

minor_version/major_version(次版本号和主版本号) u2 u2

  • 高版本jdK可以向下兼容以前版本的class文件

constant_pool_count/constant_pool(常量池数量和常量池) u2

  • 计数器从1开始,第0位有特殊含义,表示指向常量池的索引值数据不引用任何一个常量池项目
  • 常量池保存了文件中类或接口相关的一切常量,字面常量(直接量),如文字字符串、final变量值,以及符号引用,如类或接口的全限定名、方法或字段的简单名称和描述符。


    类文件结构_第1张图片

access_flags(访问标志) u2

  • 标识这个类或接口的访问信息。是类还是接口,是否public,是否abstract等等。两个字节一共16个标志位,目前只定义了8个,没有使用的一律为0。
类文件结构_第2张图片

类索引、父类索引与接口索引集合 u2 u2 u2表

  • 各自指向一个类型为Constant_class_info的类描述符常量,通过它的索引值找到一个Constant_utf8_info类型的常量的全限定名字符串。

字段表集合

  • 描述接口或者类中声明的类变量以及实例变量,不包括方法中的局部变量。
  • 从父类继承来的字段不会出现在字段表中。但有可能出现类中不存在的字段,如内部类中自动添加指向外部类实例的字段。
field_info
{ 
    u2      access_flags; 
    u2      name_index;     //字段的简单名称
    u2      descriptor_index;     //字段和方法的描述符
    u2      attributes_count; 
    attributes_info * attributes_cout      attributes               
} 
类文件结构_第3张图片
字段访问标志
类文件结构_第4张图片
描述符标识字符含义

方法表集合

  • 字段表集合结束后便是方法表集合。
  • 和字段表一样,使用一个u2类型的方法计数器,记录该类中方法的个数。
  • 方法的定义可以和字段表一样表达,方法的代码以字节码形式存放在Code属性里。
  • 如果父类方法没有被重写,方法表集合中就不会出现来自父类的方法信息。但有可能出现编译器自动添加的方法,如类构造器和实例构造器
类文件结构_第5张图片
方法表的结构

属性表集合

code属性
  • 该属性里主要存放由javac编译器处理后得到的字节码指令。


    类文件结构_第6张图片
    code属性结构
  1. attribute_name_index是一项指向CONSTANT_Utf8_info型常量的索引,常量值固定为“Code”,它代表了该属性的名称。
  2. attribute_length指示了属性值的长度,由于属性名称索引与属性长度一共是6个字节,所以属性值的长度固定为整个属性表的长度减去6个字节。
  3. max_stack代表了操作数栈深度的最大值。
  4. max_locals代表了局部变量表所需的存储空间,它的单位是Slot,并不是在方法中用到了多少个局部变量,就把这些局部变量所占Slot之和作为max_locals的值,原因是局部变量表中的Slot可以重用。
  5. code_length和code用来存储Java源程序编译后生成的字节码指令。code用于存储字节码指令的一系列字节流,它是u1类型的单字节,因此取值范围为0×00到0xFF,那么一共可以表达256条指令,目前,Java虚拟机规范已经定义了其中200条编码值对应的指令含义。code_length虽然是一个u4类型的长度值,理论上可以达到2^32-1,但是虚拟机规范中限制了一个方法不允许超过65535条字节码指令,如果超过了这个限制,Javac编译器将会拒绝编译。
  6. 异常表: 如果字节码从第start_pc行到第end_pc行之间(不含end_pc行)出现了类型为catch_type或其子类的异常(catch_type为指向一个CONSTANT_Class_info型常量的索引),则转到第handler_pc行继续处理,当catch_pc的值为0时,代表任何的异常情况都要转到handler_pc处进行处理。
类文件结构_第7张图片
异常表
expections属性
  • 与code平级,描述方法可能抛出的受查异常,及throws关键字后的异常。注意与上面异常表区分。
LineNumberTable属性
  • 描述Java源码行号和字节码行号的对应关系,非必须,javac时可以取消,取消的话,抛出异常时,堆栈不显示出错的行号。
LocalVariableTable属性
  • 描述栈帧中局部变量表中的变量和Java源码中定义的变量之间的关系,非必须,可以和上面一样取消。
SourceFile属性
  • 记录这个Class文件的源码文件名称,可以取消。
ConstantVaule属性
  • 通知虚拟机自动为静态变量赋值。

【final、static、static final修饰的字段赋值的区别】

  • static修饰的字段在类加载过程中的准备阶段被初始化为0或null等默认值,而后在初始化阶段(触发类构造器)才会被赋予代码中设定的值,如果没有设定值,那么它的值就为默认值。
  • final修饰的字段在运行时被初始化(可以直接赋值,也可以在实例构造器中赋值),一旦赋值便不可更改;
  • static final修饰的字段在Javac时生成ConstantValue属性,在类加载的准备阶段根据ConstantValue的值为该字段赋值,它没有默认值,必须显式地赋值,否则Javac时会报错。可以理解为在编译期即把结果放入了常量池中。
InnerClasses属性
  • 该属性用于记录内部类与宿主类之间的关联。如果一个类中定义了内部类,那么编译器将会为它及它所包含的内部类生成InnerClasses属性。

你可能感兴趣的:(类文件结构)