Class类文件时一组以单字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在Class文件中,没有任何分隔符。对于大于8位的数据均采用大头方式储存,文件中只有无符号数和表两种结构
无符号数有u1,u2,u4,u8,即1,2,4,8位字节,可以用来描述数字,索引引用,数量值或按照utf8编码的字符串值
表是由一组无符号数组成的结构,所有表均以_info结尾,整个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
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(ur) |
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 |
如果父类方法没有在子类中被重写,那么方法表中不会出现父类的方法信息;另外也有可能会出现编译器自动添加的方法,如<clinit>(类的构造方法)<init>(实例构造方法)
方法具体的代码被编译成字节码指令,存放在方法属性表内一个名为Code的属性里面
9.attributes_count attributes
属性表,包括很多个属性,例如Code,constantValue,Exceptions(可能抛出的异常种类和数量 ,throws)等等