类文件结构
class文件是一组8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在class文件中,中间没有添加任何分隔符。遇到需要占用8位字节以上空间的数据项时,则会按照高位在前的方式分割成若干个8位字节进行存储。
class文件格式只有两种数据类型:无符号数和表(info后缀),每种表都有自己的数据结构。
无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来表述数字、索引引用、数量值或者按照utf-8编码构成字符串值。
类文件由下列项目依次构成
类型 | 名称 | 数量 |
---|---|---|
u4 | magic 魔数 | 1 |
u2 | minor_version 次版本号 | 1 |
u2 | major_version 主版本号 | 1 |
u2 | constant_pool_count 常量数量 | 1 |
cp_info | constant_pool 常量池 | constant_pool_count |
u2 | access_flags 访问标志 | 1 |
u2 | this_class 类索引 | 1 |
u2 | super_class 父类索引 | 1 |
u2 | interface_count 接口索引集合数量 | 1 |
u2 | interfaces 接口索引集合 | interface_count |
u2 | fields_count 字段表数量 | 1 |
field_info | fields 字段表 | fields_count |
u2 | methods_count 方法表数量 | 1 |
method_info | methods 方法表 | methods_count |
u2 | attribute_count 属性表数量 | 1 |
attribute_info | attributes 属性表 | attribute_count |
关于各项内容
魔数
每个class文件的头四个文件称为魔数,唯一作用就是确定这个文件是否为一个能被虚拟机接受的class文件。和后缀的性质是一样的,只是后缀容易被人修改掉。
class文件的魔数为:0xCAFEBABE(咖啡宝贝?)
版本号
高版本的JDK能向下兼容以前版本的class文件,但不能运行以后版本的class文件,即使文件格式并未发生任何变化。
常量池
常量池的数量是从1开始的,比如说常量池数量为22,则代表常量池中有21项常量,索引范围为1~21。
常量池中存放两大类常量:字面量和符号引用。字面量就是文本字符串、常量值等;符号引用包括类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。
访问标志
访问标志用于识别一些类或者接口层次的访问信息,包括:这个class是类还是接口;是否定义位publi类型;是否定义位abstract类型等。
类索引、父类索引与接口索引集合
- 类索引确定这个类的全限定名
- 父类索引确定这个类的父类的权限定名
- 接口索引集合确定它实现所有接口的全限定名的集合,顺序是按照implements后的接口顺序从左到右
字段表集合
字段表用于描述接口或者类中声明的变量。字段包括类级变量以及实例变量。可以包括的信息有:字段的作用域(public、private、protected)、可变性(final)、并发可见性(volatile)、可否被序列化(transient)、字段基本类型(基本型、对象、数组)、字段名称等。
方法表集合
包括访问标志、方法名称、方法参数、方法返回类型等。
属性表
在class文件、字段表、方法表都可以携带自己的属性表集合,以用于描述某些场景专有的信息。
比如说class文件中的InnerClasses属性用来描述内部类列表
比如说字段表中的ConstantValue属性用于存放static final修饰的字段
比如说方法表中的Code用于属性用来存储字节码指令
参考
- 深入理解Java虚拟机[书籍]