我们知道一个Class文件对应着一个接口或者注解的类,但是他们并不一定定义在文件里,也可以直接由类加载器生成。
Java虚拟机定义了专门的数据类型来表示class文件的内容,他们包括u1,u2,u4表示1,2,4个无符号数
一 Class文件结构
在Class文件中,各个项按照严格顺序连续存放的,他们之间没有任何填充或者对齐做为分隔符。
表由任意数量的可变长度的项组成,表示Class文件内容一系列复合结构,如图所示:
二 文件结构-魔数 和 版本号
每一个class文件头4个字节叫做魔数,他的作用就是确定这个文件是不是一个能被虚拟机接收的Class文件。
很多格式的文件都使用魔数而不是扩展名来识别,比如图片格式JPG 或者 PNG等。使用魔数而不使用扩展名是基于安全的考虑,因为文件扩展名是可以随意更改的。
Class文件的魔数值就是CAFEBABE.
然后接着4个字节就是Class文件版本号:
5-6字节时小版本号(u2),7-8字节时主版本号(u2),JDK能向下兼容低版本的class文件,但是不能执行高于其版本的class文件。
通过16进制转换成10进制,得出最小版本好0 主版本号52(JDK1.8)
三 文件结构-常量池
3.1 常量池计数器 constant_pool_count
常量池计数器 = 常量池表中的成员数 + 1. 常量池的索引值index,只有在index > 0 && index <constant_pool_count时才会认为是有效的。
3.2 常量池constant_pool
常量池是一种表结构,它包含了Class文件结构及其及其子结构中引用的所有字符串常量,类或者接口名、字段名或者其他常量。
3.2.1 常量池表中类型的共同特点
# 第一个字节作为类型标记(tag值),用于确定该项的格式,表明是什么类型的常量,比如tag值为3表明是CONSTANT_INTEGER类型。
# 如果是字符串,还有length项表示字符串长度,然后bytes项表示字符串内容
# 有的是index,index就是当前不是具体的常量值,他们一般是引用其他常量类型。比如CONSTANT_STRING{
u1:tag,
u2: string_index(指向CONSTANT_UTF8)
}
#3 = String #20
#20 = Utf8 Static Type Convert
3.2.2 常量池表里的常量类型
基本数据类型
CONSTANT_STRING {
u1 tag;
u2string_index;
}
tag 指的就是8
string_index 指的就是对CONSTANT_UTF8的索引值
CONSTANT_INTEGER {
u1 tag;
u4bytes;
}
tag 指的就是3
bytes指的是int类型的常量值
CONSTANT_FLOAT {
u1 tag;
u4bytes;
}
tag 指的就是4
bytes指的是float类型的常量值
CONSTANT_LONG {
u1 tag;
u4high_bytes;
u4low_bytes;
}
tag 指的就是5
high_bytes和low_bytes共同表示LONG类型常量
CONSTANT_DOUBLE{
u1 tag;
u4high_bytes;
u4low_bytes;
}
tag 指的就是6
high_bytes和low_bytes共同表示DOUBLE类型常量
CONSTANT_UTF8 {
u1 tag;
u2length;
u1bytes[length];
}
tag 指的就是1
length指明了byte数组的长度
其他类型
字段或者方法名字和描述符类型
CONSTANT_NameAndType {
u1 tag;
u2name_index;
u2descriptor_index;
}
tag 值为12
name_index必须是对CONSTANT_UTF8的引用,即名字
descriptor_index 必须是对CONSTANT_UTF8的引用,即字段或者方法描述符
类或者接口类型
COSNTANT_CLASS {
u1 tag;
u2name_index;
}
tag 值为7
name_index必须是对CONSTANT_UTF8的引用,即类或者接口名字
字段类型
COSNTANT_Fieldref{
u1 tag;
u2class_index;
u2 name_and_type_index;
}
tag值为9
class_index 索引值必须指向CONSTANT_CLASS的,即这是哪一个类型上的属性
name_and_type_index 索引值必须指向CONSTANT_NameAndType,即当前字段名字和描述符
方法类型
COSNTANT_Methodref{
u1 tag;
u2class_index;
u2name_and_type_index;
}
tag值为10
class_index 索引值必须指向CONSTANT_CLASS的,即这是哪一个类型上的方法
name_and_type_index 索引值必须指向CONSTANT_NameAndType,即当前方法名字和描述符
接口方法类型
CONSTANT_InterfaceMethodref{
u1 tag;
u2class_index;
u2name_and_type_index;
}
tag值为11
class_index 索引值必须指向CONSTANT_CLASS的,即这是哪一个接口的方法
name_and_type_index 索引值必须指向CONSTANT_NameAndType,即当前方法名字和描述符
方法句柄类型
CONSTANT_MethodHandle {
u1 tag;
u2reference_kind;
u2reference_index;
}
tag值为15
reference_kind [1-9]范围内,表示方法句柄类型
name_and_type_index 索引值必须指向CONSTANT_NameAndType,即当前方法名字和描述符
若reference_kind 为1,2,3,4 即REF_getField,REF_getStatic,REF_putFiled,REF_putStatic,那么此处成员必须是CONSTANT_Fieldref结构,表示某个字段.
若reference_kind 为5,8 即REF_invokeVirtual或者REF_newInvokeSpecial,那么此处成员必须是CONSTANT_Methodref结构,表示某个方法或者构造器,如果是8名称必须是<init>
若reference_kind 为6,7 即REF_invokeStatic或者REF_invokeSpecial,且JDK版本1.8以下,那么此处成员必须是CONSTANT_Methodref或者CONSTANT_InterfaceMethodref结构,表示接口中某个方法
若reference_kind 为9 即REF_invokeInterface那么此处成员必须是CONSTANT_InterfaceMethodref结构,表示接口中某个方法
方法类型
CONSTANT_MethodType{
u1 tag;
u2descriptor_index;
}
tag值为16
descriptor_index 必须是对CONSTANT_UTF8的引用,即字段或者方法描述符
动态调用类型
CONSTANT_InvokeDynamic{
u1 tag;
u2bootstrap_method_attr_index;
u2name_and_type_index;
}
tag值为18
bootstrap_method_attr_index: 必须是当前class文件中引导方法表
name_and_type_index 索引值必须指向CONSTANT_NameAndType,即当前字段名字和描述符
四 文件结构-访问标志
在常量池结束后,紧接着2个字节代表访问标志access_flags,这个标志用于识别一些类或者接口层次的访问信息。
包括这个Class是接口还是类,是否定义为public类型,是否定义为abstract类型,如果是类的话是否声明为final.
五 文件结构-字段
field {
u2access_flags;
u2name_index;
u2descriptor_index;
u2attributes_count;
attribute_infoattributes[attributes_count];
}
access_flags: 表示字段的访问权限和基本属性,比如是public 是不是 final 是不是抽象等
name_index: 字段名字,必须指向一个CONSTANT-UTF8类型的结构
descriptor_index:字段描述符,必须指向一个CONSTANT-UTF8类型的结构
attributes_count:表示附加属性的数量
attribute_info:附加属性
六 文件结构-方法
method {
u2 access_flags;
u2name_index;
u2descriptor_index;
u2attributes_count;
attribute_infoattributes[attributes_count];
}
access_flags: 表示方法的访问权限和基本属性,比如是public 是不是 final 是不是抽象等
name_index: 方法名字,必须指向一个CONSTANT_UTF8类型的结构
descriptor_index:方法描述符,必须指向一个CONSTANT_UTF8类型的结构
attributes_count:表示附加属性的数量
attribute_info:附加属性
七 文件结构-描述符
描述符是一个描述字段或者方法类型的字符串比如方法返回类型是什么,接收参数类型是什么等
public void add(int a, int b) {
}
descriptor: (II)V
flags: ACC_PUBLIC
public List<String> add(String[] arr) {
List<String>list = new ArrayList<String>();
if (arr!= null || arr.length > 0) {
for(int i =0; i < arr.length; i++) {
list.add(arr[i]);
}
}
returnlist;
}
descriptor: ([Ljava/lang/String;)Ljava/util/List;
flags: ACC_PUBLIC