https://blog.csdn.net/u012422829/article/details/46633515#
任何一个Class文件都对应唯一一个类或接口的定义信息,但是不是所有的类或接口都得定义在文件中(它们也可以通过类加载器直接生成)。
Class文件是一组以8位字节为基础单位的二进制流,各个数据项严格按顺序排列,没有任何分隔符,非常的工整!Class文件格式采用一种类似于C语言结构体的伪结构来存储数据,这种伪结构只有两种数据类型:无符号数
和表
。
在了解class文件格式之前我们需要先了解一些基础概念:
1. 无符号数
是基本数据类型,以u1、u2、u4、u8分别代表1个字节、2个字节、4个字节、8个字节的无符号数,可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成的字符串值。
**
2. 表
由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以“_info
”结尾。整个Class文件本质上就是一张表,因此class文件整体结构如下所示:
名称 | 类型 | 数量 | 说明 |
---|---|---|---|
magic | u4 | 1 | class文件身份标识,写死的CA FE BA BE |
minor_version | u2 | 1 | calss文件版本号 |
major_version | u2 | 1 | calss文件版本号 |
constant_pool_count | u2 | 1 | 常量池元素数量 |
constant_pool | cp_info | constant_pool_count-1 | 常量池列表 |
access_flags | u2 | 1 | 访问标记 |
this_class | u2 | 1 | 指向自己的class |
super_class | u2 | 1 | 指向父类的class |
interfaces_count | u2 | 1 | 接口数量 |
interfaces | u2 | interfaces_count | 接口列表 |
fields_count | u2 | 1 | 非常量属性数量 |
fields | field_info | fields_count | 非常量属列表 |
methods_count | u2 | 1 | 方法数量 |
methods | method_info | methods_count | 方发表 |
attributes_count | u2 | 1 | 属性数量 |
attributes | attribute_info | attributes_count | 属性表 |
3. class文件各个字段详解
我们通过一个这样的示例来解释class文件
public class Dog {
private int m;
public int getM(){
return m + 1;
}
}
使用javac命令编译后长这样:
使用java自带的命令行查看一下统计数据:
javap -verbose Dog.class
结果如下:
3.1 魔数
每个class文件的头4个字节称为魔数,它唯一的作用是确定这个文件是否为一个能被虚拟机接受的Class文件。很多文件存储标准中都使用魔数来进行身份识别,譬如图片格式gif、jpeg等。使用魔数而不是拓展名来进行识别主要是基于安全方面的考虑,因为文件拓展格式可以随意改动。
Class文件的魔数为: 0xCAFEBABE(咖啡宝贝?),这个魔数似乎也预示着日后JAVA这个商标名称的出现。
3.2 版本号
第五六个字节是次版本号(Minor Version),第7和第8个字节是主版本号(Major Version)。
高版本的JDK可以向下兼容以前版本的Class文件,但是无法运行以后版本的Class文件,即使文件格式并未发生变化,虚拟机也必须拒绝执行超过其版本号的Class文件。
3.3 常量池
常量池可以理解为Class文件之中的资源仓库,是Class文件结构中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时也是在Class文件中第一个出现的表类型数据项目。
由于常量池中常量的数目是不固定的, 所以在常量池入口需要放置一个2字节长的无符号数constatn_pool_count来代表常量池容量计数值。这个容量计数从1而不是0开始。
constant_pool_count:占2字节,0x0016,转化为十进制为22,即说明常量池中有21个常量(只有常量池的计数是从1开始的,其它集合类型均从0开始),索引值为1~22。 第0项常量具有特殊意义,如果某些指向常量池索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”的含义,这种情况可以将索引值置为0来表示。
常量池中主要存放两大类常量:字面量和符号引用。字面量如文本字符串、声明为final的常量值等。符号引用包括三类常量:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。
常量池中的每一项常量都是一个表,在JDK1.7之前共有11种结构各不相同的表数据结构。这些表数据结构在表开始的第一位是一个u1类型的标志位,代表当前这个常量属于那种常量类型。如下表所示:
类型 | 简介 | 项目 | 类型 | 描述 |
---|---|---|---|---|
CONSTANT_Utf8_info | utf-8缩略编码字符串 | tag | u1 | 值为1 |
length | u2 | utf-8缩略编码字符串占用字节数 | ||
bytes | u1 | 长度为length的utf-8缩略编码字符串 | ||
CONSTANT_Integer_info | 整形字面量 | tag | u1 | 值为3 |
bytes | u4 | 按照高位在前储存的int值 | ||
CONSTANT_Float_info | 浮点型字面量 | tag | u1 | 值为4 |
bytes | u4 | 按照高位在前储存的float值 | ||
CONSTANT_Long_info | 长整型字面量 | tag | u1 | 值为5 |
bytes | u8 | 按照高位在前储存的long值 | ||
CONSTANT_Double_info | 双精度浮点型字面量 | tag | u1 | 值为6 |
bytes | u8 | 按照高位在前储存的double值 | ||
CONSTANT_Class_info | 类或接口的符号引用 | tag | u1 | 值为7 |
index | u2 | 指向全限定名常量项的索引 | ||
CONSTANT_String_info | 字符串类型字面量 | tag | u1 | 值为8 |
index | u2 | 指向字符串字面量的索引 | ||
CONSTANT_Fieldref_info | 字段的符号引用 | tag | u1 | 值为9 |
index | u2 | 指向声明字段的类或接口描述符CONSTANT_Class_info的索引项 | ||
index | u2 | 指向字段描述符CONSTANT_NameAndType_info的索引项 | ||
CONSTANT_Methodref_info | 类中方法的符号引用 | tag | u1 | 值为10 |
index | u2 | 指向声明方法的类描述符CONSTANT_Class_info的索引项 | ||
index | u2 | 指向名称及类型描述符CONSTANT_NameAndType_info的索引项 | ||
CONSTANT_InterfaceMethodref_info | 接口中方法的符号引用 | tag | u1 | 值为11 |
index | u2 | 指向声明方法的接口描述符CONSTANT_Class_info的索引项 | ||
index | u2 | 指向名称及类型描述符CONSTANT_NameAndType_info的索引项 | ||
CONSTANT_NameAndType_info | 字段或方法的部分符号引用 | tag | u1 | 值为12 |
index | u2 | 指向该字段或方法名称常量项的索引 | ||
index | u2 | 指向该字段或方法描述符常量项的索引 |
首先来看常量池中的第一项常量,其标志位tag为0x0A,它的十进制是10,从常量表中可以看出是一个CONSTANT_Methodref_info
类型常量,此类型常量代表类中方法的符号引用。根据其数据结构,接下来2位字节用来保存一个索引值,它指向常量池中一个CONSTANT_Class_info
类型的常量,此常量代表了指向声明方法的类描述符的索引项,索引值为0x0004,即指向了常量池中的第4项常量。
从常量表中可以看出,第4项常量标志位为0x01,确实是一个CONSTANT_Utf8_info类型的常量。根据其数据结构,接下来2个字节用来保存utf-8缩略编码字符串长度,其值为0x000D,转化为十进制为13,即接下来的13个字节为一个utf-8缩略编码的字符串,为com/test/Test,可以看到正好是测试类的全限定名。
笔记待整理、、