6.类文件结构(Class结构)
结构图
常量池
主要存放两大常量
字面量:如文本字符串,被声明未final的常量值等。
符号引用:
被模块导出或开放的包(Package)
类和接口的全限定名(Fully Qualified Name)
字段的名称和描述符(Descriptor)
方法的名称和描述符
方法句柄和方法类型(Meathod Handle 、Method Type 、Invoke Dynamic)
动态调用点和动态常量 (Dynamically-Computed Call Site,Dynamically-Computed Constant)
常量池项目类型
常量池结构表
访问标志:用于识别一些类或接口层次的访问信息,是类还是接口,是否定义未Public,abstract,final等。共有16个标志位可用,目前只定义了9个,其他为0
类索引、父类索引与接口索引集合
其中类索引和父类索引都是一个u2类型的数据,而接口索引集合是一组u2类型的数据。在Java中由于不允许多继承,所以父类索引是唯一的,但是一个类可以实现多个接口,所以得到的接口索引是一个集合,表示这个类实现了哪些接口,对于接口索引集合,入口第一项u2为接口计数器。
字段表集合
描述一个字段的信息包括:字段的作用域(public、protected、private)、实例变量与否(static)、可变性(final)、并发可见性(volatile)、可否被序列化(transient)、字段数据类型(基本数据类型、对象、数组)、字段名称。字段的信息也被存放在一张表中,其字段表包括三种类型:
u2类型访问标志(access_flags),其访问标志在access_flags中
u2类型的name_index(字段的简单名称)
u2类型的描述符(descriptor_index)
还有u2类型的attributes_count(1个),attributes_count个attribute_info类型的attributes。
方法表集合
结构与字段表一样,仅访问标志(没有volatile,transient有锁,native,strictfp,abstract)和属性表集合的可选项有所区别。
代码经Javac编译成字节码指令后,存放在属性表集合名为Code的属性中
类构造器,实例构造器可能会有编译器自动添加
重载:要与元方法具有相同的简单名称还要有不同的特征签名
特征签名:一个方法中各个参数在常量池中的字段符号引用的集合(所以无法根据反回值不同对方法进行重载)
属性表集合(详见https://blog.csdn.net/en_joker/article/details/79868865)
虚拟机中预定义的属性:
属性名称 | 使用位置 | 含义 |
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指令引用的引导方式限定符 |
对于每个属性,它的名称需要从常量池中引用一个CONSTANT_utf8_info类型的常量类表示,而属性值的结构则是完全自定义的,只需要通过一个u4的长度属性区说明属性值所占用的位数即可.
属性表定义的结构:
类型 | 名称 | 数量 |
u2 | attribute_name_index | 1 |
u2 | attribute_length | 1 |
u1 | info | attribute_length |
CODE属性
Java程序方法体中的代码经过Javac编译处理后,最终变为字节码指令存储在Code属性中.Code属性出现在方法表的属性集合中,但是并非所有的方法表都有这个属性.例如接口或类中的方法就不存在Code属性了.
在字节码指令之后的是方法的是方法的显式异常处理表集合,异常表对于Code属性来说并不是必须参在的.
max_stack操作数栈,max_locals局部变量表所需空间,
结构:
类型 | 名称 | 数量 |
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 |
异常表的格式如下表所示,他包含4个字段,这些字段的含义为:如果当字节码在第start_pc行到end_pc行之间(不含第end_pc行)出现了类型为catch_type或者其子类的异常(catch_type为指向一个CONSTANT_Class_info型常量的索引),则转到第handler_pc行继续处理。当catch_type的值为0时,代表任意异常情况都需要转向到handler_pc处进行处理。
类型 名称 数量 类型 名称 数量 u2 start_pc 1 u2 handler_pc 1 u2 end_pc 1 u2 catch_type 1
EXCEPTIONS属性
Exception属性的作用是列出方法中能抛出的受查异常Check Exceptions,也就是方法描述时在throws关键字之后列举的异常
结构:
类型 | 名称 | 数量 |
u2 | attribute_name_index | 1 |
u2 | attribute_lrngth | 1 |
u2 | attribute_of_exception | 1 |
u2 | exception_index_tsble | number_of_exceptions |
Exception属性中的number_of_exceptions项表示方法可能抛出的number_of_exceptions种受查异常,每一种受查异常使用一个exception_index_tsble项表示,exception_index_tsble是一个指向常量池中CONSTANT_Class_info型常量的索引,代表了该受查异常的类型.
LINENUMBERTABLE属性
结构:
类型 | 名称 | 数量 |
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 |
line_number_table是一个数量为line_number_table_length,类型为line_number_info的集合,line_number_info表包括了start_PC和line_number两个u2类型的数据项,前者是字节码行号,后者是Java源代码行号.
BoottstrapMethods(以后再看)
字节码指令简介:加载和存储指令、运算指令、类型转换指令、对象创建与访问指令、操作数栈管理指令、控制转移指令、方法调用和返回指、异常处理指令、同步指令