- Class文件核心结构剖析
- 魔数
- 次版本号
- 主版本号
- 常量池(Constant Pool)
- 访问标志
- 类索引,父类索引和接口索引集合
- 字段表集合
- 方法表集合
- 访问修饰符
- 方法名称索引
- 方法描述符
- 方法属性表
- class文件详解之字段
- class文件详解之方法
- class文件详解之属性
- 所有的属性按照用途,可以划分为三类
- 所有属性按照位置划分
- 变换一种组织形式
- ConstantValue 属性
- Code属性
- StackMapTable属性
- Exceptions属性
- BootstrapMethods 属性
- InnerClasses 属性
- Synthetic 属性
- Signature 属性
- EnclosingMethod 属性
- RuntimeVisibleAnnotations 属性 **RuntimeInvisibleAnnotations 属性**
- RuntimeVisibleTypeAnnotations 属性 **RuntimeInvisibleTypeAnnotations 属性**
- RuntimeVisibleParameterAnnotations 属性 **RuntimeInvisibleParameterAnnotations 属性**
- AnnotationDefault 属性
- MethodParameters 属性
- sourceFile 属性
- SourceDebugExtension 属性
- LineNumberTable 属性
- LocalVariableTable 属性
- LocalVariableTypeTable 属性
- Deprecated 属性
Class文件核心结构剖析
魔数
次版本号
主版本号
00 34 (52) 对应jdk1.8版本 占两个字节 根据主次版本号就可以分析使用的jdk的版本
常量池(Constant Pool)
常量个数
00 19(25-1) 占两个字节 减1是因为常量个数从1开始计数 第0号常量 被jvm占用 表示的是什么都不引用
如图 常量从1开始 24结束
常量类型
常量池中共有11项结构各不相同的表数据结构,分为字面量和
符号引用。
常量的第一个字节为tag标志位,标志这个常量属于哪一种常量类型(tag取值为1~12,缺少标志为2的数据类型)
第一个常量
//java/lang/object.”
0A 00 04 00 15 Methodref_info class_index 00 04 (#4) nameAndType_index 00 15 (#21)
第二个常量
09 00 03 00 16 field_info class_index 00 03(#3) nameAndType 00 16(#22)
第三个常量
07 00 17 class_info name_index 00 17 (#23)
访问标志
access_flag 类的权限描述符 (2个字节) 00 21 对应权限为 public super
表中没有 00 21这一项 是根据super项 和 public项 位运算的结果
类索引,父类索引和接口索引集合
this class name
super class name
00 04(#4)两个字节 对应到常量池第4号常量 java/lang/Object 也就是该类的父类
实现接口的个数
00 00(0) ——-> FF FF(65535) 类中实现接口的个数 所以类实现的最大接口数为65535
字段表集合
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
00 01 (1) 字段的个数为1
所以有一个field_info
根据访问修饰符,字段名称,字段类型可以唯一确定我们的字段field_info
访问修饰符
字段名称的索引
字段描述索引
00 06 #6————————java/lang/String
字段属性表
如果字段表被final这样额外的属性修饰, attributes_count;
attribute_info attributes[attributes_count]就不会为空
方法表集合
访问修饰符
方法名称索引
方法描述符
方法属性表
属性表计数器
属性名称
00 09 #9 指向code 此属性是方法的字节码描述
class文件详解之字段
字段表field_info 用于描述接口或者类中声明的变量
包括类变量 以及 实例变量 不包括方法内部声明的局部变量
可以包括的信息包括
- 字段的作用域 public private protected
- 字段类型 类变量还是实例变量 是否有static修饰
- 是否为常量 final
- 并发可见性 volatile
- 是否可以被序列化 transient
- 字段的数据类型 基本类型 对象 数组
字段名称 | 字段 | | —- | | field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
} | | 每个字段都由field_info结构定义
同一个class文件中不会有两个字段同时具有相同的字段名和描述符
access_flags 表示字段访问权限和基本属性
name_index指向字段的名字 CONSTANT_utf8_info
descriptor_index 指向字段描述符CONSTANT_utf8_info
字段field 包含属性表,属性表结构的情况稍后介绍 |
access_flags字段类型
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 |
如同源代码中abstract不能和final同时使用,此处的标志位规则也是如此,有些标志是互斥的
一个字段最多只能设置ACC_PUBLIC ACC_PRIVATE ACC_PROTECTED 的一种
不能同时设置ACC_FINAL 和 ACC_VOLATILE
接口中所有字段都具有 ACC_PUBLIC ACC_STATIC ACC_FINAL 也可以设置ACC_SYNTHETIC 其他的都不行了
class文件详解之方法
方法 |
---|
method_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; } |
所有方法,包括实例初始化方法以及类或者接口初始化方法 一个class文件中不会有两个方法具有相同的方法名和描述符 name_index 指向方法名字 CONSTANT_Utf8_info descriptor_index 表示方法描述符 指向 CONSTANT_Utf8_info 方法也有属性表 |
access_flag 标志
基本也同方法的修饰符
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 | 方法是否为 编译器为了字节码兼容自动生成的bridge方法 |
ACC_VARARGS | 0x0080 | 方法是否为变长参数 |
ACC_NATIVE | 0x0100 | 方法是否为native 本地方法 |
ACC_ABSTRACT | 0x0400 | 方法是否为abstract 无实现代码 |
ACC_STRICT | 0x0800 | 方法是否为strictfp 使用FP-strict浮点模式 |
ACC_SYNTHETIC | 0x1000 | 方法是否为编译器自动产生而不是由源代码编译而来 |
volatile关键字和transient关键字不能修饰方法,所以自然也没有这些个标志
而synchronized native strictfp 和 abstract 关键字可以修饰方法 所以相对于字段中的标志新增了对应的标志
类似字段,有些方法修饰符标志也是互斥的
一个方法只能设置ACC_PUBLIC ACC_PRIVATE ACC_PROTECTED 中的一种
接口方法可以设置 除了ACC_PROTECTED ACC_FINAL ACC_SYNCHRONIZED ACC_NATIVE 以外的,以外的
版本号小于52 每个方法必须设置 ACC_PUBLIC ACC_ABSTRACT
大于等于52 每个方法必须设置ACC_PUBLIC 或者 ACC_PRIVATE 中的一个
ps: 52 可以理解为jdk1.8
如果设置了ACC_ABSTRACT 不能再设置 ACC_FINAL ACC_NATIVE ACC_PRIVATE ACC_STATIC ACC_STRICT ACC_SYNCHRONIZED
实例初始化方法只能被ACC_PUBLIC ACC_PROTECTED ACC_PRIVATE 其中之一修饰
还可以设置 ACC_STRICT ACC_VARARGS ACC_SYNTHETIC 其他的都不能再设置
类或者接口的初始化方法 由虚拟机自动调用 除了ACC_STRICT以外,其它标志全部都会被忽略
class文件详解之属性
通过类 常量池 字段 方法的结构,已经塑造完成了 class文件的基本概念
他们是class文件的基础骨架
骨架之上还有其他很多的附属信息以及比如运行时需要的额外的信息
这些信息大多数不能归结于一大类,逻辑架构上可能比较散乱,也可以理解为杂项信息
这些杂项就都是属性表的范畴
不过Code属性比较特殊,他其实也算作是一个骨架部分,或者说一个重要”器官” 他是作为方法中的代码编译后的字节码形式存在的
只不过因为逻辑上 方法内的代码字节码指令显然是归属于某个方法的,所以Code作为属性表也可以理解
class文件的ClassFile结构
字段的field_info 结构
方法的method_info结构
另外还有Code属性
以上四类都包含属性结构信息
所有属性表的梗概结构为
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
attribute_name_index 表示属性的名字索引 指向 CONSTANT_Utf8_info
attribute_length就是属性的长度
info[attribute_length] 是属性的具体数据信息
所有的属性按照用途,可以划分为三类
1.对于JVM 正确解读class文件起关键作用的5个属性 • ConstantValue • Code • StackMapTable • Exceptions • BootstrapMethods |
---|
2.对JavaSE 平台类库正确解读class文件起关键作用的12个属性 • InnerClasses • EnclosingMethod • Synthetic • Signature • RuntimeVisibleAnnotations • RuntimeInvisibleAnnotations • RuntimeVisibleParameterAnnotations • RuntimeInvisibleParameterAnnotations • RuntimeVisibleTypeAnnotations • RuntimeInvisibleTypeAnnotations • AnnotationDefault • MethodParameters |
对JVM或者JavaSE平台类库能够正确解读class文件 虽然不起关键作用,但是却可以作为实用工具来使用的6个属性 • SourceFile • SourceDebugExtension • LineNumberTable • LocalVariableTable • LocalVariableTypeTable • Deprecated |
我们已经知道 属性出现于 classFile field_info method_info code 中
所有属性按照位置划分
属性 | 位置 | 备注 | 首次出现版本号 |
---|---|---|---|
SourceFile | ClassFile | 表示class文件的源文件名称 类独有属性 |
45.3 |
InnerClasses | ClassFile | 内部类相关信息 类独有属性 |
45.3 |
EnclosingMethod | ClassFile | class为局部类或者匿名类才具有 类独有属性 |
49.0 |
SourceDebugExtension | ClassFile | 可选/保存扩展调试信息/最多一个 类独有属性 |
49.0 |
BootstrapMethods | ClassFile | 与 invokedynamic指令 常量池中CONSTANT_InvokeDynamic_info 相关 类独有属性 |
51.0 |
ConstantValue | field_info | fina修饰的字段的常量值 字段独有属性 |
45.3 |
Code | method_info | java程序方法体中的代码经过javac编译器处理后 最终变为字节码指令存储在Code属性内 Code属性出现在方法表的属性集合中 抽象类和接口不存在code属性 包含了方法的java虚拟机指令及相关辅助信息 方法独有属性 |
45.3 |
Exceptions | method_info | 方法可能抛出的已检查异常列表 方法独有属性 |
45.3 |
RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations |
method_info | 形参上的运行时的注解信息类型 分为可见和不可见两种类型 方法独有属性 |
49.0 |
AnnotationDefault | method_info | method_info表示注解类型中的元素时 记录这个元素的默认值 方法独有属性 |
49.0 |
MethodParameters | method_info | 形参相关信息,比如参数名称 方法独有属性 |
52.0 |
Synthetic | classFile field_info method_info |
Synthetic 标志编译器生成 类 字段 方法都可能由编译器生成 所以三种都有此属性 |
45.3 |
Deprecated | classFile field_info method_info |
语义同@Deprecated 显然可以标注在类/接口/字段/方法上 所以三种都有此属性 |
45.3 |
Signature | classFile field_info method_info |
泛型信息 类接口 字段 方法 都有可能有类型参数 所以三种都有此属性 |
49.0 |
RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations |
classFile field_info method_info |
类 方法 字段上 运行时注解的可见性 分为可见不可见两种类型 三种都有此属性 |
49.0 |
LineNumberTable | Code | 调试用信息 用于调试器确定源文件中给定行号所表示的内容,对应于虚拟机中code[]数组中的哪一部分 也就是行号与字节码指令的对应关系 |
45.3 |
LocalVariableTable | Code | 调试用信息 调试器执行方法过程中可以用它来确定某个局部变量的值 |
45.3 |
LocalVariableTypeTable | Code | 调试用信息 调试器执行方法过程中可以用它来确定某个局部变量的值 |
49.0 |
StackMapTable | Code | 虚拟机类型检查验证使用信息 | 50.0 |
RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations |
classFile field_info method_info |
类/方法/字段声明所使用的类型上面的运行时注解可见性 分为可见/不可见两种 三种都有此属性 |
52.0 |
变换一种组织形式
我们以位置为纲 可以很清晰的看到四个位置处都有那些信息
classFile | SourceFile InnerClasses EnclosingMethod SourceDebugExtension BootstrapMethods Synthetic Deprecated Signature RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations |
---|---|
field_info | ConstantValue Synthetic Deprecated Signature RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations |
method_info | Code Exceptions RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations AnnotationDefault MethodParameters Synthetic Deprecated Signature RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations |
Code | LineNumberTable LocalVariableTable LocalVariableTypeTable StackMapTable |
属性表中的attribute_name_index 都是对应的属性名称 指向CONSTANT_Utf8_info 比如Code属性值为Code ConstantValue属性为ConstantValue
ConstantValue 属性
通知虚拟机为静态变量赋值
只有被static关键字修饰的变量才可以使用这个属性 也就是只有类变量才可以使用
非static类型的变量也就是实例变量的赋值在构造方法中
类变量可以再
目前编译器的做法是 如果同时使用final和static来修饰,也就是常量了
如果变量的数据类型是基本类型或者java.lang.String的话,就生成ConstantValue
如果没有final 或者并非基本类型或者字符串 选择在
ConstantValue_attribute { u2 attribute_name_index; u4 attribute_length; u2 constantvalue_index; } |
---|
Code属性
Code属性是方法体中的代码经过编译处理后,最终的字节码指令
既然是方法体的内容,如果没有方法体自然没有code属性 比如 接口或者抽象类中就不存在Code属性 native也不存在
Code_attribute { u2 attribute_name_index; u4 attribute_length; u2 max_stack; u2 max_locals; u4 code_length; u1 code[code_length]; u2 exception_table_length; { u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; } exception_table[exception_table_length]; u2 attributes_count; attribute_info attributes[attributes_count]; } |
---|
attribute_name_index 指向CONSTANT_Utf8_info 常量值 固定为”Code” 表示属性的名称
attribute_length 属性值长度 属性表前面6个字节 u2 + u4 再加上attribute_length 就是整个表的长度了
max_stack 操作数栈的最大深度 方法执行任意时刻不会超过这个值,根据值来分配栈帧
ps: 虚拟机栈是线程私有的,每创建一个线程,虚拟机就会为这个线程创建一个虚拟机栈 虚拟机栈表示Java方法执行的内存模型,每调用一个方法就会为每个方法生成一个栈帧(Stack Frame),用来存储局部变量表、操作数栈、动态链接、方法出口等信息。 每个方法被调用和完成的过程,都对应一个栈帧从虚拟机栈上入栈和出栈的过程。 虚拟机栈的生命周期和线程是相同的 |
---|
max_locals 局部变量表所需要的空间单位个数,可以理解为座位 这个位置叫做 Slot 可以查看javap后的信息
long double占用两个单位 其他类型一个单位
并不是有多少局部变量就是求和计算,因为变量有作用域的生命周期,所以空间可以复用
编译器会自动分配 ,并且计算出来这个值, 可以理解为 任意时刻最大的所需要空间个数
code_length 和code 才是真正的存储字节码的,虽然我们说code属性就是存储编译后的字节码
code_length指明了字节码指令的长度 字节码指令为u1
读取一个指令后虚拟机就可以知道这个字节码的含义以及这条指令后面是否还有参数
以及参数如何理解
exception_table 异常表表示的是可能出现的代码执行路径
表示如果字节码start_pc行 到 end_pc行,包含头不包含为 也就是不包含end_pc
出现了类型为catch_type 或者他的子类的异常 catch_type 指向一个CONSTANT_Class_info
转到handler_pc中处理
u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
上面的代码除了正常执行以外
如果try出现Exception或者子类的异常 转到catch
如果出现不属于Exception或者子类的异常 转到finally
如果catch中出现任何异常,转到finally
StackMapTable属性
用于虚拟机类型检查的验证阶段
是为了一种新的类型检查验证器而设置的,新的验证器在编译阶段将一系列的验证类型直接记录在class文件中,
通过检查这些验证类型代替了类型推导过程
Code属性表里最多可以包含一个StackMapTable
StackMapTable包含0个或者多个栈帧映射
用于表示 执行到该字节码时局部变量表和操作数栈的验证类型
类型检查验证器会通过检查目标方法的局部变量和操作数栈所需要的类型来确定一段字节码指令是否符合逻辑约束
版本号大于等于50的class文件中如果方法的code属性中没有附带StackMapTable属性,意味着他有一个 隐式的栈帧映射属性
隐式的栈映射属性的作用等同于number_of_entries 值为0的StackMapTable
StackMapTable_attribute { u2 attribute_name_index; u4 attribute_length; u2 number_of_entries; stack_map_frame entries[number_of_entries]; } |
---|
number_of_entries给出来的是stack_map_frame的个数
union stack_map_frame { same_frame; same_locals_1_stack_item_frame; same_locals_1_stack_item_frame_extended; chop_frame; same_frame_extended; append_frame; full_frame; } |
---|
Exceptions属性
不是Code中的exception_table 注意区分
列举出方法中可能抛出的已检查的异常
也就是方法声明throws后面的内容
Exceptions_attribute { u2 attribute_name_index; u4 attribute_length; u2 number_of_exceptions; u2 exception_index_table[number_of_exceptions]; } |
---|
attribute_name_index 指向名字
attribute_length表示长度
number_of_exceptions 表示个数
exception_index_table 指向常量池中的CONSTANT_Class_info 表示异常类型
BootstrapMethods 属性
保存invokedynamic 指令引用的引导方法限定符
如果某个classFile文件中常量池中至少有一个CONSTANT_InvokeDynamic_info
那么就必须包含且只能包含一个BootstrapMethods
与invokedynamic指令和java.lang.invoke包关系密切
BootstrapMethods_attribute { u2 attribute_name_index; u4 attribute_length; u2 num_bootstrap_methods; { u2 bootstrap_method_ref; u2 num_bootstrap_arguments; u2 bootstrap_arguments[num_bootstrap_arguments]; } bootstrap_methods[num_bootstrap_methods]; } |
---|
InnerClasses 属性
记录内部类与宿主类之间的关联
如果一个类内部定义了内部类,编译器就会为以及他所包含的内部类生成InnerClasses属性
InnerClasses_attribute { u2 attribute_name_index; u4 attribute_length; u2 number_of_classes; { u2 inner_class_info_index; u2 outer_class_info_index; u2 inner_name_index; u2 inner_class_access_flags; } classes[number_of_classes]; } |
---|
inner_class_info_index outer_class_info_index 都是指向常量池中CONSTANT_Utf8_info
分别代表内部类和宿主类的内部引用
inner_name_index指向常量池中CONSTANT_Utf8_info 表示内部类的名称 匿名内部类 为0
inner_class_access_flags 内部类的访问标志
ACC_PUBLIC | 0x0001 | 内部类是否为public |
---|---|---|
ACC_PRIVATE | 0x0002 | 内部类是否为private |
ACC_PROTECTED | 0x0004 | 内部类是否为protected |
ACC_STATIC | 0x0008 | 内部类是否为static |
ACC_FINAL | 0x0010 | 内部类是否为final |
ACC_INTERFACE | 0x0200 | 内部类是否为interface |
ACC_ABSTRACT | 0x0400 | 内部类是否为abstract |
ACC_SYNTHETIC | 0x1000 | 内部类是否为编译器自动生成 |
ACC_ANNOTATION | 0x2000 | 内部类是否为注解 |
ACC_ENUM | 0x4000 | 内部类是否为枚举 |
Synthetic 属性
标志是否有编译器自动生成 没有具体的值 只有存在和不存在的说法
Synthetic_attribute { u2 attribute_name_index; u4 attribute_length; } |
---|
attribute_name_index 指向CONSTANT_Utf8_info 表示 synthetic
attribute_length 固定为0 也就是么有值
Signature 属性
可选的属性 1.5之后 任何 类 接口 初始化方法 或者成员 的泛型签名如果包含了类型变量 或者 参数化类型
那么signature 属性记录泛型签名信息
之所以需要是因为泛型擦除机制
反射机制获取泛型类型 依赖数据就是这个属性
Signature_attribute { u2 attribute_name_index; u4 attribute_length; u2 signature_index; } |
---|
EnclosingMethod 属性
位于classFile结果的属性 当且仅当class为局部类和匿名内部类时 才具有这个属性
class_index 表示包含当前类的最内层类
method_index表示当前类是否在某个方法或者构造器中,如果不是 值为0
EnclosingMethod_attribute { u2 attribute_name_index; u4 attribute_length; u2 class_index; u2 method_index; } |
---|
RuntimeVisibleAnnotations 属性 **RuntimeInvisibleAnnotations 属性**
添加在类声明 字段声明 方法声明上面的注解 在运行时的可见情况
Visible 可见 Invisible不可见
ClassFile field_info method_info中最多只能有一个RuntimeVisibleAnnotations 或者 RuntimeInvisibleAnnotations
RuntimeVisibleAnnotations 和 RuntimeInvisibleAnnotations 基本一致, 但是 RuntimeInvisibleAnnotations 标志的不能被反射API访问
除非虚拟机通过与实现相关的特殊方式保留这些注解,否则,虚拟机将忽略Invisible的注解
num_annotations表示注解的数量
annotations 每个元素都表示一个注解
RuntimeVisibleAnnotations_attribute { u2 attribute_name_index; u4 attribute_length; u2 num_annotations; annotation annotations[num_annotations]; } |
---|
RuntimeInvisibleAnnotations_attribute { u2 attribute_name_index; u4 attribute_length; u2 num_annotations; annotation annotations[num_annotations]; } |
---|
annotation { u2 type_index; u2 num_element_value_pairs; { u2 element_name_index; element_value value; } element_value_pairs[num_element_value_pairs]; } type_index 用来表示一个字段描述符,字段描述符表示一个注解类型 和当前annotation 结构所表示的注解一致 num_element_value_pairs 表示注解中的键值对 (注解中的参数都是键值对) 的个数 element_value_pairs 代表真正的键值对 它包括 element_name_index表示键 element_value 表示值 element_value { u1 tag; union { u2 const_value_index; { u2 type_name_index; u2 const_name_index; } enum_const_value; u2 class_info_index; annotation annotation_value; { u2 num_values; element_value values[num_values]; } array_value; } value; } element_value 表示一个联合体 tag 使用u1 来表示键值对中的值是什么类型 也就是决定了键值对中 值的格式与value 中的哪一项相符合 联合体总共有五种 const_value_index enum_const_value class_info_index annotation_value array_value |
---|
tag 值表示的类型
B | byte | const_value_index | CONSTANT_Integer |
---|---|---|---|
C | char | const_value_index | CONSTANT_Integer |
D | double | const_value_index | CONSTANT_Double |
F | float | const_value_index | CONSTANT_Float |
I | int | const_value_index | CONSTANT_Integer |
J | long | const_value_index | CONSTANT_Long |
S | short | const_value_index | CONSTANT_Integer |
Z | boolean | const_value_index | CONSTANT_Integer |
s | String | const_value_index | CONSTANT_Utf8 |
e | Enum类型 | enum_const_value | 不适用 |
c | Class | class_info_index | 不适用 |
@ | Annotation类型 | annotation_value | 不适用 |
[ | Array 类型 | array_value | 不适用 |
const_value_index 表示原始类型的常量值 或者String类型的字面量 |
---|
enum_const_value 表示一个枚举常量 type_name_index 指向CONSTANT_Utf8_info 枚举常量类型的二进制名称的内部形式 const_name_index 指向CONSTANT_Utf8_info 枚举常量的简单名称 |
class_info_index 表示类字面量 CONSTANT_Utf8_info 用于表示返回描述符 返回描述符给出了与该element_value结构所表示的类字面量相对应的类型 如果类字面量是C. class,且C是类、接口或数组类型的名字,那么对应的类型就是C。常量池中的返回描述符会是ObjectType 或者ArrayType 如果类字面量是p. class,且p是原始类型的名称,那么对应的类型就是p 常量池中的返回描述符会是一个BaseType 如果类字面量是void. class,那么对应的类型就是void。常量池中的返回描述符会是V. 比如Object.class 对应于类型Object 所以常量池中就是Ljava/lang/Object; 而 int.class对应于类型int 常量池中就是I(大写的i ) |
annotation_value 表示键值对中里面的值本身又是一个注解 |
array_value 表示键值对的 值 是一个数组 num_values 给出了当前element_value结构所表示的数组的成员数量 values 每个成员对应了当前element_value 结构所表示的数组中的一个元素 |
RuntimeVisibleTypeAnnotations 属性 **RuntimeInvisibleTypeAnnotations 属性**
classFile field_info method_info 或者code属性中都有
比如对于某个类声明implements后面的类型所加的注解 记录在classFile结构体的这个属性里
比如某个字段声明中的类型 所加的全部注解记录在字段的对应属性里
记录了标注在对应类声明 字段声明 或者方法声明所使用的类型上面的注解 在运行时的可见情况
分为可见和不可见两种
也记录了标注在对应方法体重某个表达式所使用的类型上面的运行时可见注解
此外还记录了标注在泛型类 接口 方法 以及构造器的类型参数声明上面的注解
Java虚拟机必须使这些注解可供取用
最多只能有一个
num_annotations 表示注解个数
annotations 表示每一个注解 类型为type_annotation
RuntimeVisibleTypeAnnotations_attribute { u2 attribute_name_index; u4 attribute_length; u2 num_annotations; type_annotation annotations[num_annotations]; } |
---|
RuntimeInvisibleTypeAnnotations_attribute { u2 attribute_name_index; u4 attribute_length; u2 num_annotations; type_annotation annotations[num_annotations]; } |
---|
type_annotation { u1 target_type; union { type_parameter_target; supertype_target; type_parameter_bound_target; empty_target; method_formal_parameter_target; throws_target; localvar_target; catch_target; offset_target; type_argument_target; } target_info; type_path target_path; u2 type_index; u2 num_element_value_pairs; { u2 element_name_index; element_value value; } element_value_pairs[num_element_value_pairs]; } |
---|
前三项 target_type target_info 以及 target_path 指出了带注解的类型所在的精确位置 target表示那个类型
后面type_index num_element_value_pairs element_value_pairs指出了注解本身的类型以及键值对
RuntimeVisibleParameterAnnotations 属性 **RuntimeInvisibleParameterAnnotations 属性**
保存标注在对应方法的形式参数声明上面的注解的运行时可见状态
分为可见和不可见两种
num_parameters 形参个数
parameter_annotations 每个元素表示一个形式参数的运行时注解 第 n项,表示方法描述符中的第 n 个形式参数
RuntimeVisibleParameterAnnotations_attribute { u2 attribute_name_index; u4 attribute_length; u1 num_parameters; { u2 num_annotations; annotation annotations[num_annotations]; } parameter_annotations[num_parameters]; } |
---|
RuntimeInvisibleParameterAnnotations_attribute { u2 attribute_name_index; u4 attribute_length; u1 num_parameters; { u2 num_annotations; annotation annotations[num_annotations]; } parameter_annotations[num_parameters]; } |
---|
AnnotationDefault 属性
如果一个method_info 是用来表述注解类型中的元素的
该结构体的属性表中最多只能有一个AnnotationDefault
AnnotationDefault属性记录了由method_info 结构所表示的那个元素的默认值
default_value 表示由AnnotationDefault属性外围的method_info结构所描述的那个注解类型元素的默认值
说白了这个结构有用的才是default_value
AnnotationDefault_attribute { u2 attribute_name_index; u4 attribute_length; element_value default_value; } |
---|
MethodParameters 属性
形参相关的一些信息 比如参数名称
parameters_count 表示本属性外围method_info 结构里面的descriptor_index所引用的那个方法描述符中,有多少个参数描述符
parameters 表示实际的参数
name_index 要么是0要么指向CONSTANT_Utf8_info 表示一个有效的非限定名 用来指代某个形式参数
access_flags ACC_FINAL 0x0010 形参为final
ACC_SYNTHETIC 0x1000 形参没有显式或者隐式的在源代码中声明,由编译器生成
ACC_MANDATED 0x8000 形参是隐式声明,也就是编程语言规范对所有的编译器的要求必须生成
MethodParameters_attribute { u2 attribute_name_index; u4 attribute_length; u1 parameters_count; { u2 name_index; u2 access_flags; } parameters[parameters_count]; } |
---|
sourceFile 属性
class文件的源文件名,属性是可选的可以关闭
但是一旦关闭,当抛出异常时 不会显示出错代码所归属的文件名称
SourceFile_attribute { u2 attribute_name_index; u4 attribute_length; u2 sourcefile_index; } |
---|
sourcefile_index 指向的是常量池中的CONSTANT_Utf8_info 表示源文件的文件名
SourceDebugExtension 属性
一个classFile只能包含一个属性
debug_extension 用于保存扩展调试信息 扩展调试信息对于java虚拟机来说没有实际的语义
扩展信息使用改进版的UTF8 编码的字符串 也可以说这个就算是另一种格式的用于表示字符串的结构
不过他比String类的实例所能表示的字符串更长
SourceDebugExtension_attribute { u2 attribute_name_index; u4 attribute_length; u1 debug_extension[attribute_length]; } |
---|
LineNumberTable 属性
Code属性的属性表中
源文件中给定的行号表示的内容对应字节码指令的行号(偏移量)之间的关系
并不是运行时必须信息,但是会默认生成到Class文件中
可以通过参数设置不生成,但是程序运行产生的最主要影响就是当抛出异常时,堆栈中不会显示出错的行号
调试时也无法设置断点
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]; } |
---|
LocalVariableTable 属性
用于描述栈帧中的局部变量表中的变量与java源代码中定义的变量之间的关系
也可以不生成,但是可能会导致别人引用方法时,参数名称丢失
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]; } |
---|
LocalVariableTypeTable 属性
与LocalVariableTable类似,就是descriptor_index 被替换为了signature_index
主要针对泛型场景
非泛型签名与描述符基本一致,引入泛型后实际的泛型信息会被擦除 描述符就不足够准确了
LocalVariableTypeTable_attribute { u2 attribute_name_index; u4 attribute_length; u2 local_variable_type_table_length; { u2 start_pc; u2 length; u2 name_index; u2 signature_index; u2 index; } local_variable_type_table[local_variable_type_table_length]; } |
---|
Deprecated 属性
语义同语法中的@Deprecated 形式类似Synthetic 标志属性 有和没有的区别
attribute_length 固定为0
Deprecated_attribute { u2 attribute_name_index; u4 attribute_length; } |
---|