类文件结构
- 3.1 魔数与版本号
- 3.2 常量池
-
- 常量池数量描述
- 常量类型
- Javap
- 字面量
- 符号引用
- 3.3 访问标志
- 3.4 类索引、父类索引与接口索引集合
- 3.5 字段表集合
- 3.7 属性表集合
-
- 预定义属性
- Code属性表的结构
- Exceptions属性
- LocalVariableTable属性
- SourceFile属性
- 其它
以下面的例子做讲解。
JAVA代码:
CLASS文件:
3.1 魔数与版本号
Class文件的前4个字节为魔数,十六进制:CAFEBABE (00000000:0-3)
接下来的4个字节为版本号:00 00 00 31 (0000000:4-7)代表JDK1.6.0_01 –targer1.5的版本。
3.2 常量池
常量池数量描述
前2个字节代表常量数量,如:00 16(00000000:8-9),代表有21个常量(从1开始,0是空出来的)。
常量类型
常量池的每一项常量都是一个表,常量类型如下表所示。
类型 | 标志 | 描述 |
---|---|---|
CONSTANT_utf8_info | 1 | UTF-8编码的字符串 |
CONSTANT_Integer_info | 3 | 整形字面量 |
CONSTANT_Float_info | 4 | 浮点型字面量 |
CONSTANT_Long_info | 5 | 长整型字面量 |
CONSTANT_Double_info | 6 | 双精度浮点型字面量 |
CONSTANT_Class_info | 7 | 类或接口的符号引用 |
CONSTANT_String_info | 8 | 字符串类型字面量 |
CONSTANT_Fieldref_info | 9 | 字段的符号引用 |
CONSTANT_Methodref_info | 10 | 类中方法的符号引用 |
CONSTANT_InterfaceMethodref_info | 11 | 接口中方法的符号引用 |
CONSTANT_NameAndType_info | 12 | 字段或方法的符号引用 |
CONSTANT_MothodType_info | 16 | 标志方法类型 |
CONSTANT_MethodHandle_info | 15 | 表示方法句柄 |
CONSTANT_InvokeDynamic_info | 18 | 表示一个动态方法调用点 |
每一个类型对应不同结构,但是有一个共同点就是每一个常量的第一个字节描述的就是类型。
下面描述2个类型的结构表。
CONSTANT_utf8_info
类型 | 名称 | 数量 |
---|---|---|
u1 | tag | 1 |
u2 | Length | 1 |
u1 | bytes | length |
例子:图中01(00000000:d)代表类型CONSTANT_utf8_info,那么接下来00 11(00000000:e-f)代表长度,长度为十进制17,那么接下来(00000010:0 - 00000020:0)代表这常量的值为:com/lin/TestClass。
CONSTANT_Class_info
类型 | 名称 | 数量 |
---|---|---|
u1 | tag | 1 |
u2 | name_index | 1 |
例子:图中07(00000000:a)代表类型CONSTANT_Class_info,接下来00 02(00000000:b-c)代表引用的是第二个常量的值。
常量池中的14中常量项的结构总表如下:
常量 | 项目 | 类型 | 描述 |
---|---|---|---|
CONSTANT_Utf8_info | tag | u1 | 值为1 |
CONSTANT_Utf8_info | length | u2 | UF-8编码的字符串占用的字节数 |
CONSTANT_Utf8_info | bytes | u1 | 长度为length的UTF-8编码的字符串 |
CONSTANT_Integer_info | tag | u1 | 值为3 |
CONSTANT_Integer_info | bytes | u4 | 按照高位在前存储的int值 |
CONSTANT_Float_info | tag | u1 | 值为4 |
CONSTANT_Float_info | bytes | u4 | 按照高位在前存储的float值 |
CONSTANT_Long_info | tag | u1 | 值为5 |
CONSTANT_Long_info | bytes | u8 | 按照高位在前存储的long值 |
CONSTANT_Double_info | tag | u1 | 值为6 |
CONSTANT_Double_info | bytes | u8 | 按照高位在前存储的double值 |
CONSTANT_Class_info | tag | u1 | 值为7 |
CONSTANT_Class_info | index | u2 | 指向全限定名常量项的索引 |
CONSTANT_String_info | tag | u1 | 值为8 |
CONSTANT_String_info | index | u2 | 指向字符串字面量的索引 |
CONSTANT_Fieldref_info | tag | u1 | 值为9 |
CONSTANT_Fieldref_info | index | u2 | 指向声明字段的类或接口描述符CONSTANT_Class_info的索引项 |
CONSTANT_Fieldref_info | index | u2 | 指向字段名称及类型描述符CONSTANT_NameAndType_info的索引项 |
CONSTANT_Methodref_info | tag | u1 | 值为10 |
CONSTANT_Methodref_info | index | u2 | 指向声明方法的类描述符CONSTANT_Class_info的索引项 |
CONSTANT_Methodref_info | index | u2 | 指向方法名称及类型描述符CONSTANT_NameAndType_info的索引项 |
CONSTANT_InrerfaceMethodref_info | tag | u1 | 值为11 |
CONSTANT_InrerfaceMethodref_info | index | u2 | 指向声明方法的接口描述符CONSTANT_Class_info的索引项 |
CONSTANT_InrerfaceMethodref_info | index | u2 | 指向方法名称及类型描述符CONSTANT_NameAndType_info的索引项 |
CONSTANT_NameAndType_info | tag | u1 | 值为12 |
CONSTANT_NameAndType_info | index | u2 | 指向字段或方法名称常量项目的索引 |
CONSTANT_NameAndType_info | index | u2 | 指向该字段或方法描述符常量项的索引 |
CONSTANT_MethodHandle_info | tag | u1 | 值为15 |
CONSTANT_MethodHandle_info | reference_kind | u1 | 值必须在1~9之间,它决定方法句柄的类型。 |
CONSTANT_MethodHandle_info | reference_index | u2 | 值必须是对常量池的有效索引 |
CONSTANT_MethodType_info | tag | u1 | 值为16 |
CONSTANT_MethodType_info | descriptor_index | u2 | 值必须是对常量池的有效索引 |
CONSTANT_InvokeDynamic_info | tag | u1 | 值为18 |
CONSTANT_InvokeDynamic_info | bootstrap_method_attr_index | u2 | 值必须是当前Class文件中引导方法表的bootstrap_methods[]数组的有效索引 |
CONSTANT_InvokeDynamic_info | name_and_type_index | u2 | 值必须是当前常量池的有效索引。 |
Javap
我们可以利用javap命令反编译出class,常量池如下:
字面量
类似于java中的常量,比如String,或者修饰final的常量值。
符号引用
包括下面三种常量:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
3.3 访问标志
常量池结束后,紧接着的2个字节代表访问标志位(access_flags),这个标志位识别这个class是类还是接口;是否定义为public类型;是否定义abstract类型;是否声明为final等。标志位的含义如下表:
标志名称 | 标志值 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否为public类型 |
ACC_FINAL | 0x0010 | 是否被声明为final |
ACC_SUPER | 0x0020 | 是否使用invokespecial字节码指令,JDK1.0.2之后为真 |
ACC_INTERFACE | 0x0200 | 标示为一个接口 |
ACC_ABSTRACT | 0x0400 | 是否为abstract:接口和抽象类为真 |
ACC_SYNTHETIC | 0x1000 | 表示这个类并非用户代码产生的 |
ACC_ANNOTATION | 0x2000 | 表示这是一个注解 |
ACC_ENUM | 0x4000 | 表示这是一个枚举 |
例子:00 21(000000d0:7-8)代表此类为public类。
3.4 类索引、父类索引与接口索引集合
类索引:u2类型的数据。00 01(000000d0:9-a),引用第1个常量。即:com.lin.TestClass
父类索引:u2类型的数据。00 03(000000d0:b-c),引用第3个常量。即:java.lang.Object
接口索引:一组u2类型的数据。00 00(000000d0:d-e),为0代表没有实现接口。如果有实现接口,那么这个数代表实现多少个接口,接下来的多少个u2指向对于接口。
3.5 字段表集合
用于描述接口或者类中声明的变量。包括类级变量以及实例级变量,但不包括方法内部变量。
Class字段表集合第一个u2字节代表变量数量,之后开始描述各个变量。
例子:00 01(000000d0:f – 000000e0:0)代表变量数量为1。
变量的描述结构如下:
类型 | 名称 | 数量 |
---|---|---|
u2 | access_flags 修饰位 | 1 |
u2 | name_index 变量名称 | 1 |
u2 | descriptor_index 变量类型 | 1 |
u2 | attributes_count 属性表集合数量 | 1 |
attribute_info | Attributes 属性表 | attributes_count |
其中修饰位对应表如下
标志位名称 | 标志值 | 含义 |
---|---|---|
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_TRANSIENT | 0x0080 | Transient |
ACC_SYNTHETIC | 0x1000 | 是否有编译器自动产生 |
ACC_ENUM | 0x4000 | 是否为enum |
其中变量类型对应表如下
标识字符 | 含义 | 标识字符 | 含义 |
---|---|---|---|
B | 基本类型byte | J | 基本类型long |
C | 基本类型char | S | 基本类型short |
D | 基本类型double | Z | 基本类型boolean |
F | 基本类型float | V | 特殊类型void |
I | 基本类型int | L | 对象类型 |
例子1: 00 02(000000e0:1-2)代表修饰符,private
00 05(000000e0:3-4)代表名称,映射到常量第5个。
00 06(000000e0:5-6)代表变量类型,6代表映射到常量第6个。
00 00(000000e0:7-8)代表属性表数量,这里为0。
注:属性表将在3.7中的属性表集合介绍。
#3.6 方法表集合
存储对方法的描述。第一个u2是描述方法数量。
例子:00 02(000000e0:9-a)代表存在2个方法
结构与字段表集合类似,结构如下
类型 | 名称 | 数量 |
---|---|---|
u2 | access_flags 修饰位 | 1 |
u2 | name_index 方法名称 | 1 |
u2 | descriptor_index 方法描述符 | 1 |
u2 | attributes_count 属性表集合数量 | 1 |
attribute_info | Attributes 属性表 | attributes_count |
其中修饰位对应表如下
标志位名称 | 标志值 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | Public |
ACC_PRIVATE | 0x0002 | Private |
ACC_PROTECTED | 0x0004 | Protected |
ACC_STATIC | 0x0008 | Static |
ACC_FINAL | 0x0010 | Final |
ACC_SYNCHRONIZED | 0x0020 | synchronized |
ACC_BRIDGE | 0x0040 | 方法是否有编译器产生的桥接方法 |
ACC_VARARGS | 0x0080 | 方法是否接受不定参数 |
ACC_NATIVE | 0x0100 | native |
ACC_ABSTRACT | 0x0400 | abstract |
ACC_STRICTFP | 0x0800 | strictifp |
ACC_ENUM | 0x1000 | native |
例子1:00 01(000000e0:b-c)代表修饰符,public
00 07(000000e0:d-e)代表方法名称,常量第7个:
00 08(000000e0:f-000000f0:0)代表方法描述符,常量第8个:()V。
00 01(000000f0:1-2)代表方法在属性表集合存在1个属性。
00 09(000000f0:3-4)代表属性对应第9个常量:Code
例子2:00 01(00000120:8-9)代表修饰符,public
00 10(00000120:a-b)代表方法名称,常量第16个:inc
00 11(00000120:c-d)代表方法描述符,常量第17个:()I。
00 01(00000120:c-d)代表方法在属性表集合存在1个熟悉。
00 09(00000130:0-1)代表属性对应第9个常量:Code
3.7 属性表集合
存在于字段表集合和方法表集合里面,用于描述某些场景专有的信息。
预定义属性
预定义的属性表如下21项:
属性名称 | 使用位置 | 含义 |
---|---|---|
Code | 方法表 | Java代码编译成的字节码指令 |
ConstantValue | 字段表 | final关键字定义的常量池 |
Deprecated | 类,方法,字段表 | 被声明为deprecated的方法和字段 |
Exceptions | 方法表 | 方法抛出的异常 |
EnclosingMethod | 类文件 | 仅当一个类为局部类或者匿名类是才能拥有这个属性,这个属性用于标识这个类所在的外围方法 |
InnerClass | 类文件 | 内部类列表 |
LineNumberTable | Code属性 | Java源码的行号与字节码指令的对应关系 |
LocalVariableTable | Code属性 | 方法的局部便狼描述 |
StackMapTable | Code属性 | JDK1.6中新增的属性,供新的类型检查检验器检查和处理目标方法的局部变量和操作数有所需要的类是否匹配 |
Signature | 类,方法表,字段表 | 用于支持泛型情况下的方法签名 |
SourceFile | 类文件 | 记录源文件名称 |
SourceDebugExtension | 类文件 | 用于存储额外的调试信息 |
Synthetic | 类,方法表,字段表 | 标志方法或字段为编译器自动生成的 |
LocalVariableTypeTable | 类 | 使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加 |
RuntimeVisibleAnnotations | 类,方法表,字段表 | 为动态注解提供支持 |
RuntimeInvisibleAnnotations | 表,方法表,字段表 | 用于指明哪些注解是运行时不可见的 |
RuntimeVisibleParameterAnnotation | 方法表 | 作用与RuntimeVisibleAnnotations属性类似,只不过作用对象为方法 |
RuntimeInvisibleParameterAnnotation | 方法表 | 作用与RuntimeInvisibleAnnotations属性类似,作用对象哪个为方法参数 |
AnnotationDefault | 方法表 | 用于记录注解类元素的默认值 |
BootstrapMethods | 类文件 | 用于保存invokeddynamic指令引用的引导方式限定符 |
Code属性表的结构
类型 | 名称 | 数量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | max_stack | 1 |
u2 | max_locals | 1 |
u4 | code_length | 1 |
u1 | code | code_length |
u2 | exception_table_length | 1 |
exception_info | exception_table | exception_length |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
例子:00 09(000000f0:3-4)代表属性对应第9个常量:Code
00 00 00 2F(000000f0:5-8)代表属性长度为十进制47。
00 01(000000f0:9-a)代表操作数栈为1。
00 01(000000f0:b-c)代表局部变量存储空间,单位是slot。
00 00 00 05(000000f0:d-00000100:0)代表字节码指令数。
2A B7 00 0A B1(00000100:1-5)代表5个指令,请查看指令表对应。
00 00(00000100:6-7)代表异常属性表长度,0代表不存在。
00 02(00000100:8-9)代表存在2个附加属性。
Exceptions属性
与Code属性平级的属性。与异常表不一样,异常表在于属性里面。
与Code属性平级的属性。与异常表不一样,异常表在于属性里面。
类型 | 名称 | 数量 |
---|---|---|
u2 | attribute_name_index | 1 |
u2 | attribute_lrngth | 1 |
u2 | attribute_of_exception | 1 |
u2 | exception_index_tsble | number_of_exceptions |
LineNumberTable属性
用于描述java源码行号与字节码行号对应关系,结构如下:
类型 | 名称 | 数量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | line_number_table_length | 1 |
line_number_info | line_number_table | line_number_table_length |
例子:00 0C(00000100:a-b)代表属性名称,对应常量第12个,即:LineNumberTable。
00 00 00 06(00000100:c-f)代表属性长度,为6。
00 01(00000110:0-1)代表1个集合的对应表。
00 00 00 03(00000110:2-5)前面一个u2代表字节码行号,后面一个u2代表java源码行号。
LocalVariableTable属性
类型 | 名称 | 数量 |
---|---|---|
U2 | Attribute_name_index | 1 |
U4 | Attribute_length | 1 |
U2 | local_variable_table_length | 1 |
Local_variable_info | local_variable_table | Local_variable_table_length |
其中local_variable_info的结构如下
类型 | 名称 | 数量 |
---|---|---|
u2 | Start_pc | 1 |
u2 | Length | 1 |
u2 | Name_index | 1 |
u2 | Descriptor_index | 1 |
u2 | index | 1 |
例子:00 0D(00000110:6-7)代表代表属性名称,对应常量第13个,即:LocalVariableTable。
00 00 00 0C(00000110:8-b)代表属性长度为十进制12。
00 01(00000110:c-d)代表localtable长度为1。
00 00 00 05 00 0E 00 0F 00 00(00000110:e-00000120:7)代表local_variable_info。第一个u2(00 00)代表局部变量生命周期开始字节码偏移量;第二个u2(00 05)代表作用范围长度;第三个u2(00 0E)代表局部变量名,对应常量表。第四个u2(00 0F)代表局部变量描述符,对应常量表。第五个u2(00 00)代表局部变量的slot位置。
SourceFile属性
用于记录生成这个Class文件的源码文件名称。在方法表集合后面。结构如下
类型 | 名称 | 数量 |
---|---|---|
U2 | Attribute_name_index | 1 |
U4 | Attribute_length | 1 |
U2 | Sourcefile_index | 1 |
例子:00 14(00000160:9-a)代表属性名称,对应常量表十进制20,即:SourceFile
00 00 00 02(00000160:b-e)代表属性长度,为2。
00 15(00000160:f-00000170:0)代表属性数据,对应常量表十进制21,即:TestClass.java
其它
其它属性就不在一一列举。
另外可以使用javap –verbose命令反编译等到结果,便于查看方法内容。