class文件结构
结构
java虚拟机用u1、u2、u4表示1、2、4个字节的无符号整数。
classFile {
u4 magic; \\魔数
u2 minor_version; \\副版本号
u2 major_version; \\主版本号
u2 constant_pool_count; \\常量池大小
cp constant_pool[constant_pool_count - 1] \\常量池,不定长
u2 access_flags; \\访问标记
u2 this_class; \\当前类索引
u2 super_class; \\直接父类索引
u2 interfaes_count; \\直接接口数
u2 interfaces[interfaes_count]; \\直接接口索引
u2 field_count; \\成员变量数
field_info fields[filed_count]; \\成员变量
}
常量池
数据结构
cp_info {
u1 tag; \\类型
u1 info[]; \\数据
}
- CONSTANT_Integer_Info和CONSTANT_Float_Info
两种结构相似,用4个字节来表示具体的数值常量。boolean、byte、short、char类型在常量池都当做int作处理。
CONSTANT_Integer_Info {
u1 tag;
u4 bytes;
}
CONSTANT_Float_Info {
u1 tag;
u4 bytes;
}
- CONSTANT_Long_Info和CONSTANT_Double_Info
二者都用8个字节表示具体数值,占用常量池的2个位置。
CONSTANT_Long_Info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
CONSTANT_Double_Info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
- CONSTANT_Utf8_Info
存储字符串的内容
CONSTANT_Long_Info {
u1 tag;
u2 length; \\第三部分byte数组的长度
u1 bytes[length]; \\内容数组,每个byte都是MUTF-8编码
}
- CONSTANT_String_Info
用来表示java.lang.String类型的常量对象,仅仅包含一个指向常量池中CONSTANT_Utf8_Info常量类型的索引。
CONSTANT_String_Info {
u1 tag;
u2 string_index; \\表示指向CONSTANT_Utf8_Info的索引
}
- CONSTANT_Class_Info
与CONSTANT_String_Info类型,name_index表示指向CONSTANT_Utf8_Info的索引,这个字符串存储的是类或接口的全限定名。
CONSTANT_Class_Info {
u1 tag;
u2 name_index;
}
- CONSTANT_NameAndType_Info
用来表示字段或者方法,name_index和descriptor_index都指向CONSTANT_Utf8_Info的索引。
CONSTANT_NameAndType_Info {
u1 tag;
u2 name_index; \\字段或方法名的指向索引
u2 descriptor_index; \\字段或方法的描述符(签名)的指向索引
}
- CONSTANT_Fieldref_Info、CONSTANT_Methodref_Info和CONSTANT_InterfaceMethodref_Info
这三种常量类型比较类似,class_index表示指向类信息的索引,name_and_type_index表示方法或字段指向的类型的索引。
CONSTANT_Fieldref_Info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Methodref_Info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_InterfaceMethodref_Info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
Access flags(访问标记)
表示一个类的访问标志。
访问标记名 | 十六进制值 | 描述 |
---|---|---|
ACC_PUBLIC | 1 | 标识是否是public |
ACC_FINAL | 10 | 标识是否是final |
ACC_SUPER | 20 | 不再使用 |
ACC_INTERFACE | 200 | 标识接口 |
ACC_ABSTRACT | 400 | 标识是否是abstract |
ACC_SYNTHETIC | 1000 | 编译器自动生成,不是用户源代码编译生成 |
ACC_ANNOTATION | 2000 | 标识是否是注解类 |
ACC_ENUM | 4000 | 标识是否是枚举类 |
this_class、super_class、interfaces
用于确定类的继承关系,this_class表示类索引,super_class表示直接父类索引,interfaces表示当前类或接口的直接父接口
如this_class存储的是指向CONSTANTS_Class_Info的索引。
字段表
类中定义的字段都会被存储到这个集合中,包括静态和非静态的字段。
{
u2 field_count; \\字段数量
filed_info fileds[filed_count]; \\字段集合
}
字段表也是个变长的结构。field_info结构如下:
filed_info {
u2 access_flag;
u2 name_index; \\指向CONSTANTS_Utf8_Info的索引
u2 descriptor_index; \\指向CONSTANTS_Utf8_Info的索引
u2 attributes_count;
attribute_info attributes[attribute_count];
}
- access_flag
访问标记名|十六进制值|描述
—|:—|:—:
ACC_PUBLIC|0x0001|声明为public
ACC_PRIVATE|0x0002|声明为private
ACC_PROTECTED|0x0004|声明为protected
ACC_STATIC|0x0008|声明为static
ACC_FINAL|0x0010|声明为final
ACC_VOLATILE|0x0040|声明为volatile
ACC_ENUM|0x4000|声明为枚举类型
如果类中定义了字段public static final int SIZE = 10
,编译后在类文件中存储的访问标记值为0x0019,访问标志位ACC_PUBLIC|ACC_STATIC|ACC_FINAL
-
字段描述符
访问标记名|十六进制值
—|:—:
B | byte类型
C | char类型
D | double类型
F | float类型
I | int类型
J | long类型
S | short类型
Z | boolean类型
L ClassName;| 引用类型, “L” + 类的全限定名 + “;”
[ | 一维数组
[[ | 二维数组 -
字段属性
以ConstantValue为例
ConstantValue属性出现在字段field_info中,用来表示静态变量的初始值,结构如下:
ConstantValue_attribute {
u2 attribute_name_index; \\指向常量池中值为“ConstantValue”的字符串常量项
u4 attribute_length; \\固定值为2
u2 constantvalue_index; \\根据变量类型不同指向不同的常量项
}
constantvalue_index根据变量类型不同指向不同的常量项,如变量是long类型,则constantvalue_index执行CONSTANT_Long_Info类型的常量项。
方法表
类中定义的方法会被存储在方法表里,结构如下:
{
u2 methods_count; \\方法的数量
method_info methods[methods_count]; \\方法的集合
}
方法method_info的结构如下:
method_info {
u2 access_flags; \\访问标志
u2 name_index; \\方法名索引
u2 descriptor_index; \\描述(签名)索引
u2 attributes_count; \\方法属性个数
attribute_info attributes[attributes_count]; \\方法属性
}
- 方法属性
Code属性
Code_attribute {
u2 attribute_name_index; \\属性名,对应“Code”的字符串常量索引
u4 attribute_length; \\属性长度
u2 max_stack; \\操作数栈的最大深度
u2 max_locals; \\局部变量表的大小
u4 code_length; \\方法的字节码长度
u1 code[code_lenght]; \\字节码
u2 exception_table_length; \\异常数量
{
u2 start_pc; \\异常处理器覆盖的code数组开始位置
u2 end_pc; \\异常处理器覆盖的code数组结束位置
u2 handler_pc; \\异常处理handler在code字节数组的起始位
u2 catch_type; \\catch的异常类型
} exception_table[exception_table_length]; \\异常数组
u2 attributes_count;
attribute_info attributes[attributes_count];
}
- max_stack
表示操作数栈的最大深度,方法执行的任意期间操作数栈的深度都不会超过这个值。计算规则是:有入栈的指令stack增加,有出栈的指令stack减少,stack的最大值就是max_stack。一般增加和减少都是1,但LONG和DOUBLE相关指令入栈会增加2,void指令则为0。 - max_locals
表示局部变量表的大小,它的值并不等于方法中所有局部变量的数量之和。当一个局部作用域结束,它内部的局部变量占用的位置就可以被接下来的局部变量复用。 - exception_table
表示try-catch相关。start_pc、end_pc、handler_pc都是指向code字节数组的索引值,start_pc和end_pc表示异常处理器覆盖的字节码开始和结束的位置,是左闭右开区间[start_pc, end_pc]。handler_pc表示异常处理handler在code字节数组的起始位置,表示异常捕获后该跳转至何处处理。 - attributes
用于标识Code属性相关附属属性,java规定只包含四种可选属性:LineNumberTable、LocalVariableTable、LocalVariableTypeTable、StackMapTable。- LineNumberTable用来存放源代码行号和字节码偏移量之间的对应关系属于调试信息,不是类文件运行的必需属性,默认情况下都会生成。如果没有该属性,调试时就无法在源码中设置断点,也没办法在抛出异常的堆栈中显示行号。
javap命令
- -p 显示private信息
- -s 显示签名信息
- -c 反编译,显示方法的字节码
- -v 显示详细信息,包括版本号、类访问权限、常量池等相关信息。
- -l 显示行号