原创

Java类文件结构

Class 类文件

Class 类文件是一组以单字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在 Class 文件中,没有任何分隔符。对于大于 8 位的数据均采用大头方式储存,文件中只有无符号数和表两种结构

无符号数

无符号数有 u1,u2,u4,u8,即 1,2,4,8 位字节,可以用来描述数字,索引引用,数量值或按照 utf8 编码的字符串值

表是由一组无符号数组成的结构,所有表均以_info 结尾,整个 Class 文件本质上就是一张表,

Class 类文件数据项依次排列:

Class类文件排列
Class类文件排列

magic → minor_version → major_version → constant_pool_count → constant_pool → access_flags → this_class → super_class → interfaces_count → interfaces → fields_count → fields → methods_count → methods → attributes_count → attributes

Class 类文件结构

1.magic u4

魔数,Class 文件以 0xCAFEBABE 开头

2.minor_version u2    major_version u2

次版本号和主版本号 jdk1.8 的主版本号位 52

3.constant_pool_count u2    constant_pool

常量池,constant_pool_count 代表常量池计数器,从 1 开始计数,所以常量池中常量个数等于 constant_pool_count - 1,用 0 来表示“不引用任何常量池项目”

常量池主要存放字面量(Literal)和符号引用

字面量:字符串,被声明为 final 的值等

符号引用:包含以下三类常量

1)类和接口的全限定名

2)字段的名称和描述符

3)方法的名称和描述符

常量池中每一项都是一个表,一共有 11 中结构不同的数据表,以下是每个表所对应的标志(tag,类型为 u1)

标志 类型(CONSTANT_xxx_info) 描述 结构(第一个字节均为对应的 tag)
1 Utf8 UTF-8 编码的字符串 length(u2) + bytes(length)
3 Integer 整型字面量 bytes(u2)
4 Float 浮点型字面量 bytes(u4)
5 Long 长整型字面量 bytes(u8)
6 Double 双精度浮点型字面量 bytes(u8)
7 Class 类或接口的符号引用 index(u2)
8 String 字符串类型字面量 index(u2)
9 Fieldref 字段的符号引用 index(u2) + index(u2)
10 Methodref 类中方法的符号引用 index(u2) + index(u2)
11 InterfaceMethodref 接口中的符号引用 index(u2) + index(u2)
12 NameAndType 字段或方法的部分符号引用 index(u2) + index(u2)

4.access_flags u2

访问标志,识别类或接口的访问信息,目前有以下 8 个标志:

标志名称 标志值 含义
ACC_PUBLIC 0X0001 是否是 public
ACC_FINAL 0X0010 是否是 final,只有类才可能有
ACC_SUPER 0X0020 jdk1.2 之后都有
ACC_INTERFACE 0X0200 标志这是一个接口
ACC_ABSTRACT 0X0400 对于接口或抽象类为真,其他类为假
ACC_SYNTHETIC 0X1000 标志该类并非由用户代码产生
ACC_ANNOTATION 0X2000 标识这是一个注解
ACC_ENUM 0X4000 标识这是一个枚举

access_flag 的值等于标志为真的标志值相与,例如 0x0001|0x0010 = 0x0011 表示是 public final 的

5.this_class u2    super_class u2

类索引和父类索引,因为一个类只有一个父类,所以均用 u2 表示(与 interface 相对比),依据该值可以在常量池找到一个全限定名字符串

6.interfaces_count u2    interfaces

接口索引,以 u2 类型开头表示该类有 interfaces_count 个接口,然后依次是 interfaces_count 个 u2 类型值,依据该值可以在常量池找到一个全限定名字符串

7.fields_count u2    fields

字段表,用于描述接口或者类中声明的变量,包括以下几个项目:

1)access_flags:

字段修饰符,各种表示对应的标志值

标志名称 标志值
ACC_PUBLIC 0X0001
ACC_PRIVATE 0X0002
ACC_PRITECTED 0X0004
ACC_STATIC 0X0008
ACC_FINAL 0X0010
ACC_VOLATILE 0X0400
ACC_TRANSIENT 0X0800
ACC_SYNTHETIC 0X1000
ACC_ENUM 0X4000

2)name_index    descriptor_index

字段的简单名称及字段和方法的描述符

几个名词解释:

全限定符——如 com/xiao/helloworld/Test

简单名称——定义变量 i,i 为简单名称

字段和方法的描述符——描述字段的数据类型,方法的参数列表(包括数量,类型及顺序),返回值

数据类型对应规则为:基本类型以及 void 用一个大写字母来表示(类型的第一个字母大写,除了 long 为 J,boolean 为 Z);对于对象类型,使用字母 L 加上对象的全限定类名表示;对于数组,每一维度使用一个前置的[

描述方法时:按照先参数列表后返回值描述,参数列表严格按顺序放置在()中,如(IIFZ)V 表示 void xxx(int x,int x,float x,boolean x)

3)attributes_count    attributes

可能有的属性,例如 final static int m = 456;就会存放一个 ConstantValue,其指向常量 456;

8.methods_count    methods

方法表集合,与字段表的结构基本一样的,除了 access_flags 标志值有变化,变化如下:

标志名称 标志值
ACC_PUBLIC 0X0001
ACC_PRIVATE 0X0002
ACC_PRITECTED 0X0004
ACC_STATIC 0X0008
ACC_FINAL 0X0010
ACC_SYNCHRONIZED 0X0020
ACC_BRIDGE 0X0040
ACC_VARARGS 0X0080
ACC_NATIVE 0X0100
ACC_ABSTRACT 0X0400
ACC_STRICT 0X0800
ACC_SYNTHETIC 0X1000

如果父类方法没有在子类中被重写,那么方法表中不会出现父类的方法信息;另外也有可能会出现编译器自动添加的方法,如(类的构造方法)(实例构造方法)

方法具体的代码被编译成字节码指令,存放在方法属性表内一个名为 Code 的属性里面

9.attributes_count    attributes

属性表,包括很多个属性,例如 Code,constantValue,Exceptions(可能抛出的异常种类和数量 ,throws)等等

正文到此结束