一、class 类文件
class文件是一组以8位字节为基础的二进制流,中间没有任何分隔符,所以数据的含义和顺序都被严格限定。
class 文件使用两种数据类型保存数据:
- 无符号数(有符号数有正负之分)
- 表(由无符号数和ITA表构成)
class文件本质就是一张表
二、class类文件结构
- magic num 和class文件版本
- 常量池
主要存放字面量和符号引用:
字面量:字符串、final常量等
符号引用:类和接口全限定名、字段名称和描述符、方法名称和描述符
常量池中每一个常量都是一个表,共14中类型
image.png
const #7 = Asciz <init>;
const #8 = Asciz ()V;
const #9 = Asciz Code;
const #10 = Method #3.#11; // java/lang/Object."<init>":()V
const #11 = NameAndType #7:#8;// "<init>":()V
其中,第11常量,引用了常量7、8组成了NameAndType常量,
常量7是方法名称,常量8是方法描述符
- 访问标志
标识类或接口的访问信息(如public、final、annotation等)
各种访问标志求和后,可得出总访问标志 - 类索引、父类索引与接口索引的集合(用以确定类的继承关系)
通过常量池中索引表示 -
字段表集合
描述接口或类中声明的变量(不包括继承来的字段和局部变量)
image.png
字段和方法描述符:
描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)
image.png
对于数组类型,每一维度使用一个前置“[”字符描述,如String[][]二维数组,被记录为"[[Ljava/lang/String";int[]被记录为"[I"
-
方法表集合
image.png描述符用来描述方法时,按照先参数列表,后返回值的顺序描述,参数列表严格按照顺序放在一组小括号()中,如:
void inc() 描述为 ()V
toString() 描述为 ()Ljava/lang/String
请反推 ([CII[CIII)I的方法
方法里的java代码,经编译后成为字节码指令,存放在方法属性表集合中,名为Code的属性中
字段名、方法名等,都是通过索引表示,索引指向常量池 -
属性表集合
在Class文件、字段表、方法表都可以携带自己的属性表集合,用以描述某些场景专有的信息。
Code属性:
image.png
code指令执行过程中的数据交换、方法调用等操作都是基于操作栈的,如invokespecial,这条指令作用是以栈顶的reference类型的数据所指向的对象作为方法接收者,调用此对象的实例构造器方法、父类方法、private方法。
注意点:
- java中,overload除与原方法同名之外,还要求与原方法特征签名不同,特征签名就是一个方法中各个参数在常量池中的字段符号引用的集合,而返回值不包含在特征签名中。
- this关键字
编译时把this关键字的访问,转换为普通方法参数的访问,调用时自动传入当前实例。局部变量表中也会预留第一个slot来存放对象实例的引用。 - LineNumberTable属性
使用位置:Code属性中
用于描述java源代码行号和字节码行号(字节码的偏移量)之间的对应关系。由-g:lines控制,如果选择不生成,堆栈中将不会显示出错的行号(源代码行号)。 - LocalVariableTable属性
使用位置:Code属性
用以描述栈帧中局部变量表中变量与java源码定义变量的关系。由-g:vars控制,如果选择不生成,其他人引用这个方法时,所有参数名称都会丢失。
三、字节码指令
一个字节长度,由操作码+操作数构成
大部分指令都没有支持byte、char、short等,因为编译器在编译期间扩展为int类型数据
- 方法调用和返回指令
invokevirtual:调用对象实例方法
invokeinterface:调用接口方法
invokespecial:调用需要特殊处理的实例方法
包括实例初始化方法、私有方法和父类方法等
invokestatic:调用类方法 - 异常处理指令
- 类型转换指令
i2b、i2c、d2f等 - 对象创建与访问指令
new、newarray等
getfield、putfield、getstatic、putstatic - 同步指令
monitorenter和monitorexit
网友评论