Java诞生之初就有的口号,Write Once,Run Anywhere
Java规范分为Java语言规范和Java虚拟机规范,Java实现平台无关性的基础是虚拟机和字节码存储格式,Java虚拟机不与包括Java在内的任何语言绑定,它只与Class文件这种特定的二进制文件格式关联
时至今日,已经有很多语言运行在Java虚拟机之上,包括Groovy,JRuby,Jython,Scala等,只要他们通过编译器,得到虚拟机可以识别的Class文件,那么就可以在虚拟机上运行。
Class类文件的结构
任何一个Class文件都对应唯一的一个类或者接口的定义信息,反之不然,类和接口不一定定义在文件里(可以通过Classloader生成)。
Class文件是一组以8Byte为基础单位的二进制流(高位在前分割)
Java虚拟机规定,Class文件格式采用类似C语言的伪结构来存储数据,只有两种数据类型,无符号数和表。
无符号数
无符号数属于基本数据类型,如
无符号数 | 含义 |
---|---|
u1 | 1个字节 |
u2 | 2个字节 |
u4 | 4个字节 |
u8 | 8个字节 |
表
由多个无符号数或者其他表作为数据项构成的复合数据类型
所有的表都习惯的以_info结尾
表用来描述有层次关系的复合结构数据
整个Class文件本质上就是一张表,Class文件结构如:
类型 | 名称 | 数量 | 说明 |
---|---|---|---|
u4 | magic | 1 | 魔数,头4个字节,确定这个Class文件是否能被虚拟机接受,值为0xCAFEBABE,出于安全不用文件名后缀而用魔数 |
u2 | minor_version | 1 | 5-6字节,次版本号,JDK版本向下兼容,JDK版本从45(1.1)开始,以后每个大版本发布,主版本号加1,如JDK1.6是50,最新的1.7生成的Class最大值为51 |
u2 | major_version | 1 | 7-8字节,主版本号,同上 |
u2 | constant_pool_count | 1 | 常量池入口,从1开始计算(其它均从0开始),代表常量池中有多少项常量, |
cp_info | constant_pool | constant_pool_count-1 | 常量池,字面量(文本字符串,final常量值等)和符号引用(类和接口的全限定名,字段的名称和描述符,方法的名称和描述符),常量池中的 每一项常量都是一个表,JDK1.7之前有11种表结构,之后有14种;详细见下节 |
u2 | access_flag | 1 | 访问标志,表示这个是类还是接口,是否为public,是否为abstract,是否为final等 |
u2 | this_class | 1 | 该列的全限定名 |
u2 | super_class | 1 | 父类的全限定名 |
u2 | interfaces_count | 1 | 接口计数器 |
u2 | interfaces | interfaces_count | 接口索引表 |
u2 | fileds_count | 1 | 字段计数器,接口或类中声明的变量 |
filed_info | fileds | fileds_count | 字段表 |
u2 | methods_count | 1 | |
method_info | methods | methods_count | |
u2 | attributes_count | 1 | |
attribute_info | attributes | attributes_count |
常量池的项目类型有
标志 | 描述 |
---|---|
1 | UTF-8编码的字符串 |
3 | 整型字面量 |
4 | 浮点型字面量 |
5 | 长整型字面 |
6 | 双精度浮点型字面量 |
7 | 类或接口的符号引用 |
8 | 字符串类型字面量 |
9 | 字段的符号引用 |
10 | 类中方法的符号引用 |
11 | 接口中方法的符号引用 |
12 | 字段或方法的部分符号引用 |
15 | 表示方法句柄 |
16 | 标志方法类型 |
18 | 表示一个动态方法调用点 |
可以通过javap工具的-verbose参数输出字节码内容,可以观察到引用常量池中的常量