由于计算机只认识0和1,因此,编写的Java程序要经编译器编译成二进制字节码,才能被机器执行。之前,二进制的本地机器码是唯一的选择,但是,随着计算机的发展
,越来越多的程序语言选择与操作系统和机器指令无关的,平台独立的格式作为编译后的存储格式,这也是Java倡导的,一次编写,到处运行的理念。Java之所以是跨平台的,因为它没有将代码编译成平台相关的二进制文件,而是编译成平台无关的字节码class文件,该文件能够被JVM解析运行。
Class类文件结构
实现语言无关性的基础仍是虚拟机和字节码的存储格式,使用,使用Java编译器将Java代码编译成Class文件,使用Scala编译器将Scala代码编译成Class文件等,只要其符合Class文件应有的格式,就可以在Java虚拟机中运行。Java中各种变量,关键字和运算符会最终由多条字节码命令组合而成。
Class文件时一组以8位字节为基础单位的二进制流,其采用类似C语言结构体的伪结构来存储,它包含两种数据结构:无符号数和表。
无符号数是使用u1,u2,u4,u8来分别表示1个字节,2个字节,4个字节和8个字节的无符号数,可以用数字索引引用,数量值或由UTF-8表示的字符串值。
表是由多个无符号数或其他表作为数据项构成的复合数据类型,习惯都以”_info”结尾。
|
|
class文件的具体结构
前4个字节:0xCAFEBABE, Java的魔数,表征它是一个Java编译后的.class文件
minor_version(2个字节): 0x0000 次版本号
major_version(2个字节): 0x0034 主版本号 –> java 1.8
constant_pool_count(2个字节):0x001D 常量池容量计数值 –> 十进制数29,表示有28个常量,索引从1开始[1,28],第0项保留,有特殊意义
常量池中存放两大类常量:字面量和符号引用。
字面量就是我们理解的一般常量,包括文本字符串,final修饰的常量值等;而符号引用则是编译方面的概念
- cp_info(多个字节): 是一个可变长的常量数组,如上,有28个常量,则这个数组可表示个常量,一共有12种常量类型,其中,每个常量的第一个字节(u1)表示该常量是属于什么类型的,例如0x07表示CONSTANT_Class, 0x08表示CONSTANT_String等,由于每个常量类型的结构不同,根据不同的结构,去决定读取之后多少个字节来表示这个常量的具体信息。例如:如果单字节标记表示是一个字符串字面值,就会读取后两个字节,表示字符串字面值的长度,根据长度再从后面读取对应长度的字符串的实际值。
所有这个cp_info的数组的字节数是根据不同的常量类型而不同的,即数组长度不确定。
access flags(2个字节):访问标记,表示该文件定义的是类还是接口,如果是类,表示其是控制权限
该类(This Class):当前类的名称
父类(Super Class):父类的名称
接口(Interfaces):该类的所有接口
字段(Fields):该类的所有字段,Fileds 部分的前两个字节也是一个计数,表示字段的数目。接下来是一个表示每个字段的一个数组。每个数组元素是一个可变长度的结构体。该字段的一些信息保存在这个结构体中,也有一些信息保存在常量池中
方法(Methods):该类的所有方法,Methods 部分包括了该类显式定义的方法,不包括从父类或父接口中继承来的方法,头两个字节表示方法的数目。剩下的又是一个可变长度数组,其中保存了每个方法的信息。方法结构体保存了方法的多个信息,例如参数列表、返回值、保存局部变量和操作数需要的堆栈数目、异常表、字节码系列等。
属性(Attributes):该类的所有属性(例如源文件名称,等等)