一 class 属性计数器
attributes_count 的值表示当前 class 文件属性表的成员个数。属性表中每一项都是一个 attribute_info 结构。
二 属性表
attributes[](属性表)的每个项的值必须是 attribute_info 结构。属性表的结构比较灵活,各种不同的属性只要满足以下结构即可。
1 属性的通用格式
类型 |
名称 |
数量 |
含义 |
u2 |
attribute_name_index |
1 |
属性名索引 |
u4 |
attribute_length |
1 |
属性长度 |
u1 |
info |
attribute_length |
属性表 |
2 属性类型
属性表实际上可以有很多类型,上面看到的 Code 属性只是其中一种,Java8 里面定义了 23 种属性。下面这些是虚拟机中预定义的属性。
属性名称 |
使用位置 |
含义 |
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 指令引用的引导方法限定符 |
或者(查看官网)
三 部分属性详解
1 ConstantValue 属性
ConstantValue 属性表示一个常量字段的值。位于 field_info 结构的属性表中。
ConstantValue_attribute{
u2 attribute_name_index;
u4 attribute_length;
u2 constantvalue_index; // 字段值在常量池中的索引,常量池在该索引处的项给出该属性表示的常量值。(例如,值是 1ong 型的,在常量池中便是CONSTANT_Long)
}
2 Deprecated 属性
Deprecated 属性是在 JDK1.1 为了支持注解中的关键词 @deprecated 而引入的。
Deprecated_attribute{
u2 attribute_name_index;
u4 attribute_length;
}
3 Code 属性
Code 属性就是存放方法体里面的代码。但是,并非所有方法表都有 Code 属性。像接口或者抽象方法,他们没有具体的方法体,因此也就不会有 Code 属性了。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_lenth |
存储字节码指令 |
u2 |
exception_table_length |
1 |
异常表长度 |
exception_info |
exception_table |
exception_length |
异常表 |
u2 |
attributes_count |
1 |
属性集合计数器 |
attribute_info |
attributes |
attributes_count |
属性集合 |
可以看到:Code 属性表的前两项跟属性表是一致的,即 Code 属性表遵循属性表的结构,后面那些则是他自定义的结构。
4 InnerClasses 属性
为了方便说明特别定义一个表示类或接口的 Class 格式为 C。如果 C 的常量池中包含某个 CONSTANT_Class_info 成员,且这个成员所表示的类或接口不属于任何一个包,那么 C 的 ClassFile 结构的属性表中就必须含有对应的 InnerClasses 属性。InnerClasses 属性是在 JDK1.1 中为了支持内部类和内部接口而引入的,位于 ClassFile 结构的属性表。
5 LineNumberTable 属性
LineNumberTable 属性是可选变长属性,位于 Code 结构的属性表。
LineNumberTable 属性是用来描述 Java 源码行号与字节码行号之间的对应关系。这个属性可以用来在调试的时候定位代码执行的行数。
-
start_pc:即字节码行号;
-
line_number:即 Java 源代码行号。
在 Code 属性的属性表中,LineNumberTable 属性可以按照任意顺序出现,此外,多个 LineNumberTable 属性可以共同表示一个行号在源文件中表示的内容,即 LineNumberTable 属性不需要与源文件的行一一对应。
// LineNumberTable属性表结构:
LineNumberTable_attribute{
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{
u2 start_pc;
u2 line_number;
} line_number_table[line_number_table_length];
}
6 LocalVariableTable 属性
LocalVariableTable 是可选变长属性,位于 Code 属性的属性表中。它被调试器用于确定方法在执行过程中局部变量的信息。在 Code 属性的属性表中,LocalVariableTable 属性可以按照任意顺序出现。Code 属性中的每个局部变量最多只能有一个 LocalVariableTable 属性。
-
start pc + length 表示这个变量在字节码中的生命周期起始和结束的偏移位置(this 生命周期从头 0 到结尾 )
-
index 就是这个变量在局部变量表中的槽位(槽位可复用)
-
name 就是变量名
-
Descriptor 表示局部变量类型描述
// LocalVariableTable属性表结构:
LocalVariableTable_attribute{
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_table_length;
{
u2 start_pc;
u2 length;
u2 name_index;
u2 descriptor_index;
u2 index;
} local_variable_table[local_variable_table_length];
}
7 Signature 属性
Signature 属性是可选的定长属性,位于 ClassFile,field_info 或 method_info 结构的属性表中。在 Java 语言中,任何类、接口、初始化方法或成员的泛型签名如果包含了类型变量(Type Variables)或参数化类型(Parameterized Types),则 Signature 属性会为它记录泛型签名信息。
8 SourceFile 属性
SourceFile 属性结构
类型 |
名称 |
数量 |
含义 |
u2 |
attribute_name_index |
1 |
属性名索引 |
u4 |
attribute_length |
1 |
属性长度 |
u2 |
sourcefile index |
1 |
源码文件素引 |
可以看到,其长度总是固定的 8 个字节。
9 其他属性
Java 虚拟机中预定义的属性有 20 多个,这里就不一一介绍了,通过上面几个属性的介绍,只要领会其精髓,其他属性的解读也是易如反掌。
四 实战
1 代码
package chapter01.java1;
/**
* 全类名:chapter01.java1.Demo
* 全限定名:chapter01/java1/Demo
*/
public class Demo {
private int num = 1;
public int add() {
num = num + 2;
return num;
}
}
2 class 文件解析
3 IDEA 解析