Class类文件的结构
类型 | 名称 | 数量 |
---|---|---|
u4 | magic(魔数) | 1 |
u2 | minor_version(此版本号) | 1 |
u2 | major_version(主版本号) | 1 |
u2 | constant_pool_count(常量池容量计数值) | 1 |
cp_info | constant_pool(常量池表) | constant_pool_count-1 |
u2 | access_flags(访问标志) | 1 |
u2 | this_class(类索引) | 1 |
u2 | super_class(父类索引) | 1 |
u2 | interfaces_count(接口计数值) | 1 |
u2 | interfaces(接口索引表) | interfaces_count |
u2 | fields_count(字段表计数值) | 1 |
field_info | fields(字段表) | fields_count |
u2 | methods_count(方法计数值) | 1 |
method_info | method(方法表) | methods_count |
u2 | attributes_count(属性表计数值) | 1 |
attribute_info | attributes(属性表) | attributes_count |
实例解析:
创建一个接口以便后面查看接口索引表
public interface Hello {
void function();
}
创建用例TestClassFile类
public class TestClassFile implements Hello{
public int a=1;
static int b=2;
@Override
public void function()
{
int x=a+b;
}
public int method(){
int y=b;
return y;
}
public static void main(String[]args){
Hello hello=new TestClassFile();
hello.function();
}
}
编译后得到字节码class文件,用Win-Hex
打开
开始逐个分析:
前4个字节,魔数:0xCAFEBABE
所有的class文件都必须包含它,它的作用是确定这个文件是一个能被虚拟机接收的class文件。JDK主版本号从45(JDK1.1)开始每个大版本发布主版本号加1.
这里给出一个常量池中的14中常量项的结构表:
常量 | 项目 | 类型 | 描述 |
---|---|---|---|
CONSTANT_Utf8_info | 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的索引项 | |
CONSTANT_Methodref_info | tag | u1 | 10 |
index | u2 | 指向声明方法的类描述符CONSTANT_Class_info的索引项 | |
index | u2 | 指向名称及类型描述符CONSTANT_NameAndType的索引项 | |
CONSTANT_Interface-Methodref_info | tag | u1 | 11 |
index | u2 | 指向声明方法接口描述符CONSTANT_Class_info的索引项 | |
index | u2 | 指向名称及类型描述符CONSTANT_NameAndType的索引项 | |
CONSTANT_NameAndType_info | tag | u1 | 12 |
index | u2 | 指向该字段或方法名称常量项的索引 | |
index | u2 | 指向该字段或方法描述符常量项的索引 | |
CONSTANT_MethodHandle_info | tag | u1 | 15 |
reference_kind | u1 | 该值必须在1-9之间(包括1和9),它决定了方法句柄的类型。方法句柄类型的值表示方法句柄的字节码行为 | |
reference_index | u2 | 必须是对常量池的有效索引 | |
CONSTANT_MethodType_info | tag | u1 | 16 |
descriptor_index | u2 | 该值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf-8_info结构,表示方法的描述符 | |
CONSTANT_InvokeDynamic_info | tag | u1 | 18 |
bootstrap_method_attr_index | u2 | 该值必须是对当前class文件中引导方法表的bootstrap_methods[]数组的有效索引 | |
name_and_type_index | u2 | 该值必须是对当前常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info结构,表示方法名和方法描述符 |
表Constant.info
命令:javap -verbose 类名.class
图2
常量池:
从常量计数值开始后面紧跟着39个常量项,第一个常量的偏移量为0x0000000000A
每一个常量项都是一个表,表头是一个tag标识,可以通过tag号查上面的Constant.info
表。
得知 tag:0x0A
查表tag=10的项,为COSTANT_Methodref_info
,根据该常量项的结构tag后面是两个2字节的索引项:0x0007,0x0021
分别是第7项常量和第33项常量,查看上面图2,第一个常量确实是Methodref且指向7和33号常量。同理接下来的是第2个常量。
暂停!!