Java虚拟机不与包含Java在内的任何语言绑定,只和Class文件这种二进制文件格式关联。
Class文件包含了Java虚拟机指令集和符号表以及其他辅助信息。
1.Class文件结构
任何一个Class文件都对应着唯一一个类或接口的定义信息。
Class文件是以8位字节为基础的二进制流,数据按照顺序紧密排列在Class文件中,中间没有任何分隔符。
遇到需要占用8字节以上空间的数据项时,会按照高位在前的方式分成若干个8位字节进行存储
最高位字节在地址最低位,最低位字节在地址最高位(Big-Endian)
Class文件采用蕾西C语言结构体的结构来存储数据,其中有两种类型:
1.无符号数
u1、u2、u4、u8代表1、2、4、8个字节的符号引用
2.表
由无符号数或者其他表构成的,整个Class文件本质上是一张表
2.各个数据项
1.魔数 -----Class文件前四个字节
唯一作用是确定文件是否为一个被虚拟机接受的Class文件。
一些文件比如jpeg或者gif文件头中使用魔数,是基于安全方面考虑,因为文件扩展名可以随意改动。
2.版本号----魔数后的四个字节
第5、6个字节是次版本号(Minor Version),第7、8个字节是主版本号(Major Version)
高版本JDK能向下兼容低版本Classs文件,低版本不能运行高版本Class文件
3.常量池 使用javap命令可以计算出常量池
1. 常量池入口放置u2类型数据(常量池容量计数值)----该计数值从1开始计数,第0项给不引用任何常量池项目的索引数据
2.常量池存放两大类常量:
1.字面量
类似常量(文本字符串、声明为final的常量值等)
2.符号引用
1.类和接口的全限定名
2.字段的名称和描述符
3.方法的名称和描述符
3.常量池中每一项常量都是一个表
表结构的共同特点:表开始的第一位是u1类型的标志位(tag),代表该常量属于哪种常量类型
例如:标志位为0x07,查表可知常量属于CONSTANT_Class_info类型,代表一个类或者接口的符号引用。
CONSTANT_Class_info的结构为
类型 | 名称 | 数量 |
u1 | tag | 1 |
u2 | name_index | 1 |
tag是标志位,name_index为索引值,这个索引值指向常量池中 CONSTANT_Utf8_info类型常量, CONSTANT_Utf8_info代表类或者接口的全限定名
查name_index值为0x0002,指向常量池第二项,第二项标志位为0x01,也就是一个 CONSTANT_Utf8_info常量
CONSTANT_Utf8_info类型
类型 | 名称 | 数量 |
u1 | tag | 1 |
u2 | length | 1 |
u1 | bytes | length |
length为UTF-8编码字符串字节数,后面跟着length长度的数据为UTF-8缩略编码格式的字符串。
UTF-8缩略编码和普通UTF-8编码区别:
从'\u0001'到'\u007f'之间字符缩略编码用一个字节表示。
从'\u0080'到'\u07ff'之间字符缩略编码用两个字符表示。
从'\u0800'到'\uffff'之间字符缩略用普通UTF-8编码规则三个字符表示。
Class文件中所有的方法、字段都用 CONSTANT_Utf8_info型常量描述, CONSTANT_Utf8_info中最大长度就是Class文件中方法、字段
名的最大长度,最大长度即length的值,length用两个字节表示,即方法、字段名超过64KB就无法编译。
4.访问标志(access_flags)
常量池结束后,是访问标志,用来识别类或接口层次的访问信息。
Class是类还是接口、是否定义为public类型、是否定义为abstarct类型;若为类是否被声明为final
access_flags一共有16个标志位可用,没有使用到的标志位要求一律为0。
access_flags的标志为所有标志位的或(|)运算的和
例:普通Java类,则ACC_PUBLIC,ACC_SUPER为真,ACC_FINAL、ACC_INTERFACE ACC_ABSTRACT ACC_SYNTHETIC ACC_ANNOTATION ACC_ENUM为
假
5.类索引、父类索引、接口索引集合
类的继承关系由三项数据组成:
类索引:确定类的全限定名
父类索引:确定类的父类的全限定名
除了java.lang.Object,所有的父类索引都不为0(因为除了java.lang.Object所有的类都有父类)
接口索引集合:描述类实现了哪些接口
类索引、父类索引、接口索引集合按顺序排列在访问标志之后。
类索引和父类索引用两个u2类型的索引值表示,各自指向CANSTANT_Class_info的描述符常量,通过CANSTANT_Class_info的常量中的索引值找到定义在
CANSTANT_Utf8_info类型的全限定名字符串
接口索引集合:入口第一项----u2类型的接口计数器,表示索引表的容量,如果该类没有实现任何接口,则计数器为0
6.字段表集合
描述接口或者类中声明的变量。字段包含类级变量以及实例级变量,不包括方法内部声明的变量。
字段包含信息:作用域(private、protected、public),实例变量还是类变量(static修饰),可变性(final),并发可见性(volatile),是否可序列化
(transient)
修饰符存在与否可以使用布尔值来表示,就可以用标志位来标识。字段名称、字段数据类型用常量池中常量表示。
全限定名:类名中"."换成"/",名称结尾用";"隔开
简单名称:没有类型和参数修饰的方法或者字段名称
描述符:描述字段的数据类型、方法、返回值。
基本数据类型用大写字母表示,对象类型用L加全限定名表示
数组类型加"["
参数先参数列表,后返回值,如void类型"()V",java.lang.String.toString()描述符为"()Ljava/lang/String"
7.方法表集合
方法表结构和字段表类似,包括访问标志、名称索引、描述索引和属性表集合。
方法里面的代码经过编译生成字节码指令后,放入属性表中"Code"属性中
8.属性表集合
Class文件、字段表、属性表都可以携带属性表集合。