类文件结构包括
- 魔数与Class文件的版本
- 常量池
- 访问标志
- 类索引,父类索引与接口索引集合
- 字段表集合
- 方法表集合
- 属性表集合
第二节 无关性的基石
Java虚拟机不和包括Java在内的任何语言绑定,它只与“Class文件”这种特定的二进制文件格式所关联,Class文件中包含了Java虚拟机指令集和符号表以及若干其他辅助信息。
基于安全方面考虑,Java虚拟机规范要求在Class文件中使用许多强制性语法和结构化约束,但任一门功能性语言都可以表示为一个能被Java虚拟机所接受的有效的Class文件,Class的来源是何种语言
第三节 Class类文件的结构(*****本章重点)
ps:任何一个Class文件都对应着唯一一个类或接口的定义信息,但反过来类或接口并不一定都得定义在文件里。
Class文件时一组以8位字节为基础单位的二进制流,各个数据项目都严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符,这使得整个Class文件中存储的内容几乎全部是程序运行的必要数据,没有空隙存在。
Class文件格式采用伪结构来存储数据,伪结构中只有两种数据类型:无符号数和表
一 魔数与Class文件的版本
1 每个Class文件的头四个字节称为魔数,它的唯一作用是确定这份文件是否为一个能被虚拟机接受的Class文件
2 魔数的后四个字节存储的是Class文件的版本号:高版本的JDK能向下兼容以前的Class文件,但不能运行以后版本的Class文件
二 常量池
1 常量池可以理解为Class文件之中的资源仓库,它是Class文件结构中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时它还是在Class文件中第一个出现的表类型的数据项目
2 由于常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项u2类型的数据,代表的是常量池容量计数值,计数是从1开始的(这是一个特例要记得)
3 常量池主要存放两大类常量:字面量和符号引用
字面量:文本字符串,声明为final的常量值等
符号引用包括:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
当虚拟机运行时,需要从常量池获取对应的符号引用,再在类创建时或者运行时解析,翻译到具体的内存地址中
4 常量池中每一项常量都是一个表,一共有14种结构各不相同的表结构数据,它们的共同点是表开始的第一位是一个u1类型的标志位,代表当前这个常量属于哪种常量类型
5 UTF-8缩略编码与普通UTF-8编码的区别
三 访问标志
常量池结束之后,紧接着两个字节代表访问标志,这个标志用来识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口,是否定义为public类型,是否定义为abstract类型,如果是类是否被声明为final等
四 类索引,父类索引与接口索引集合
类索引用于确定这个类的全限定名,父类索引用于确定这个父类的全限定名(只能有一个),接口索引集合用来描述这个类实现了哪些接口
五 字段表集合
1 字段表用于描述接口或者类中声明的变量,字段包括类级变量以及实例级变量,但不包括方法内声明的局部变量
2 区分全限定名,简单名称,方法和字段的描述符
全限定名是把类全名中的“.”替换为“/”
简单名称是指没有类型和参数修饰的方法或者字段名称
方法和字段描述符用来描述字段的数据类型,方法的参数列表和返回值
3 用描述符来描述方法时,按照先参数列表,后返回值的顺序来描述
4 对于字节码来讲,如果两个字段的描述符不一致,那字段重名也就是合法的
六 方法表集合
1 方法表包括了访问标志,名称索引,描述符索引,属性表集合
2 方法里面的代码去哪了?
方法里面额Java代码,经过编译器变异成字节码指令后,存放在方法属性表集合中一个名为“Code”的属性里面
3 特征签名:是一个方法中各个参数在常量池中的字段符号引用的集合,也就是返回值不会包含在特征签名中
4如果要重载一个方法,除了要与原方法具有相同的简单名称之外,还要求必须拥有一个与原方法不用的特征签名
七 属性表集合
在Class文件,字段表,方法表都可以携带自己的属性集合,以用于描述某些场景的专有信息
1 Code属性(****):上文已提到,Java程序方法体总的代码经过javac编译器处理后,最终变为字节码指令存储在Code属性中
Code属性表结构中的code用于存储字节码指令的一系列字节流,
code_length:编写Java代码时只要不是可以编写一个超长的方法来为难编译器,是不大可能超过这个最大值的限制
Code属性是Class文件中最重要的一个属性,Code属性用于描述代码
2 Java虚拟机执行字节码是基于栈的体系结构
3 在任何实例方法中里面,都可以通过“this”关键字访问到此方法所属对象,所以Args_size = 1;而声明为static的方法(静态方法或类方法)该参数值为0
4 字节码指令之后是这个方法的显式异常处理表集合,但对于Code属性表来说不是必须存在的
5 Exceptions属性的作用是列举出方法中可能抛出的受查异常
6 LineNumberTable属性:用于描述Java源码行号与字节码行号之间的对应关系,虽然不是运行时必要的属性,但默认会生成到Class文件之中(没有该属性的最主要影响就是当抛出异常时,堆栈中将不会显示出错的行号,并且在调试程序时,也无法按照源码行来设置断点)