Class文件中,会用两个字节表示访问标志

比如是接口吗,是否是public类型

图片

将其简化为了两个字节

其用法就是 0x0001|0x0020 = 0x0021

表明这个为public的一个普通类

然后是各类索引,可以细分为类索引 父类的索引 接口的索引类

类索引:表示这个类的全限定的类名

父类索引:由于Java是单继承的,所以只会有一个

接口索引集合,描述实现了什么接口,是一个接口索引的集合

排在了访问标示之后,都是u2类型的数据,都会指向一个CONSTANT_CLASS_info 类描述符变量

内部的字段表的集合

用于描述接口或者类中生命的变量,也是一个info类型

例如

public static final String INFO = “is”

只是类变量,不包括方法中的局部变量,包括的信息有,字段的作用域,包括但不限于public private protected等

类变量还是实例变量 static  是否为静态 final ,并发安全 volatile

图片

然后是这个字段表的具体内容

图片

在标示位后面是两个索引值

都是对常量池的引用

name_index 用于描述 全限定类名或者说是简化的简化名字

比如说一个叫addOne()的方法,简单名称就是addOne

descriptor_index 是描述符,用于描述字段的数据类型 参数类型,返回值

属性信息,表明的是字段的数据类型,参数类型,返回值

图片

数组比较特殊,需要使用 [ 来描述 ,假设一个二维数组 String[][] 就将记录为了[[String

一个方法概述为

int addOne(int a, int[] b)那就是

(I[I[I) i

对于类中的方法

方法表和之前的字段表一致,依次包括了访问标志 名称索引 描述符索引和属性表集合

图片

只是对应的字段不一样

图片

那么在方法表之前的,是一个u2类型的计数器,说明其中有几个方法

然后就是方法表用于描述方法详情(业务代码除外),

顺便提一下,如果父类方法在子类中没有重写,方法表的集合中是不会显示出现的

但是可能出现像 构造器这种由编译器书写的方法

对于类中,还有一个属性表

专门用于描述某些场景下的信息,也是前面很多表的最后一项

可以被用于 Class 文件 字段表 方法表使用

图片

图片

其中我们主要说一下其中的code表

将Java方法体中的代码进行编译之后,最终变为字节码文件放在Code中,但不是所有的方法表都存在,抽象方法就可以没有

其常见的结构如下

图片

attribute_name_inde指向一个常量,值恒定为 Code

attribute_length 指示了属性值的长度

max_stack 操作栈的最大深度,执行任意方法都不会超过深度,这个交由java虚拟机在运行时候,分配栈帧深度

max_locals 存储局部变量所需的存储空间,计算单位为Slot

对于32位的类型,为1个Slot,对于64位的数据类型为2个Slot

而这些分配出来的Slot,可以给各个变量使用,而且支持复用,也就是在代码执行过程中,有些变量作用域一旦被结束了,就可以分配给其他变量使用

code_length是指字节码长度

虽然其是一个u4的长度,但是一旦超过65535个字节码,也无法被编译,一般来说,JSP中如果将页面代码放进去,可能导致过长而编译失败

code为真正的代码

是Java程序中真正重要的属性,我们将其中涉及到的字节码指令放在下一节来讲

其中的执行流程可以概述为,读取到字节码区域的长度后,将以此读取后面的字节码指令,然后推送到操作栈栈顶,这也是由于Java在执行过程中选择了栈这种数据结构而决定的

code指令之后,是异常处理表

在之后是异常处理表,这是一个可选择的项,因为不是必要存在的

其的基本结构很简单

图片

可以理解为,在start_pc到end_pc行之间如果出现了catch_type的异常,那么会转到handler_pc行去处理

这就是try-catch-finally的处理机制

然后是Exceptions属性,这个是配合上面的异常表使用的,用于列举方法中可能抛出的受检查异常,也就是throws关键字后的异常,其结构如下

图片

这个表说明了,可能抛出number_of_exceptions个异常,并指向了可能出现异常的表(一个CONSTANT_Class_indo)

剩下还有些非必要的属性

我们一概而述

LineNumberTable属性

描述Java源代码和字节码行数之间的对应关系,并非必要属性,可以通过-g:none或者-g:lines来设置

但取消之后,抛出异常时候,不会显示出错的行号

图片

line_number_table之中有着start_pc和line_number两个数据项

为一种key-value的映射关系

LocalVariableTable属性

用于描述栈帧中局部变量表的变量和源码中定义的变量之间关系,不是默认的信息,也可以通过-g:none或者-g:vars来设置

单取消后,别人引用了参数名称会消失,会用默认的arg0,arg1来代替

其表结构如下

图片

local_variable_info是一个栈帧和源码中局部变量的对应关系

图片

start_pc length表示这个变量的起始地点和偏移量,组合起来就是覆盖范围

index是所在的Slot位置

而且现在对于泛型,会使用

LocalVariableTypeTable这个表,将descriptor_index替换称为字段的特征签名

SourceFile属性

记录生成这个Class文件的源码文件名称,也是可选择的,通过-g:none或者-g:source来设置

如果不生成这个类,会在抛出异常的时候,不显示代码所属的文件名,结构如下

图片

ConstantValue

通知虚拟机及为静态变量赋值的,

这是因为虚拟机对变量赋值的方式不同,对于非static变量,是在实例构造器<init>进行的

类变量则可以通过<clint>或者 ConstantValue来修饰变量,

现在来说,使用ConstantValue属性值修饰的,只限于基本数据类型和String

表结构为

图片

InnnerClasses属性

内部类和宿主类之间的关系

为了描述内部类的,也就是这个类文件生成的时候查看是否有内部类,有的话为其生成内部类属性

表结构为

图片

number_of_classes,记录存在多少个内部类

每一个都是用inner_classes_info来描述

inner_classes_info可以描述为

图片

inner_classes_info_index 指向了内部类的符号引用

outer_class_info_index 指向了诉诸来的符号引用

name_index指的是内部类的名称,匿名内部类为0

access_flags 表示访问标志

Depreacted属性和Synthetic属性

其实就是@deprecated注解,标记为不建议使用

Synthetic表示这个方法或者字段不是由Java源码产生的,是编译器自行添加的

其表结构㘝

图片

StackMapTABLE属性

是一个类型校验器

在过去版本的校验器是通过数据流来分析的类型推导验证器

现在啊来说编译阶段通过一系列的验证类型直接记录在Class文件中

其包括了多个映射帧,标记了多个偏移量,表示执行到该字节码时候局部变量表和操作数栈的验证类型

通过这个来进行运行时候检测

表结构为

图片

Signature属性

为了记录泛型类型,因为Java提供的是伪泛型

在编译后,统统擦除,使用擦除法易于实现,可以节省内存空间

但是就不如像C#,那样真泛型支持的语言那样,泛型类型也是普通类型

其表结构为

图片

这个表结构可以适用于类签名 方法类型签名 字段类型签名

发表评论

邮箱地址不会被公开。 必填项已用*标注