实现语言无关性的基础仍然是虚拟机和字节码存储格式,java虚拟机不和包括java语言在内的任何语言绑定,它只与“Class文件”这种特定的二进制文件所关联。虚拟机不关心Class文件来源于何种语言。
Java语言中的各种变量,关键字,运算符号是由字节码组成的。,因此字节码所提供的提供的语义表述能力比java要强。
任意一个Class文件都对应着一个类或借口的信息,但反过来说类和接口并不一定都定义在文件里。(比如类和接口可以直接通过类加载器生成。)
Class文件是一个8为字节为基础的二进制流(当遇到占用8位字节以上空间的数据项时大端的字节存储顺序。指的是最高位字节在地址最低位)
无符号数属于基本数据类型,无符号数可以用来描述数字,索引引用,数量值,或者按照UTF-8编码构成的字符串值。
表:是由多个无符号数或这他表作为数据项构成的符合数据类型。所有的表都习惯以“_info”结尾。表用来描述具有层次关系的符合数据结构。整个Class文件本质上就是一张表。
(1)魔数(Magic Number) 与 Class文件的版本
每个Class文件的头四个字节成为魔数,它的唯一作用就是确定这个文件能否作为一个被虚拟机接受的Class文件(很多文件都是以魔数进行身份识别的,gif ,jpeg等文件头中都存有魔数,使用魔数而不是用扩展名的原因是,因为文件的扩展名可以随意改动,基于安全考虑。)
紧接着魔数的的四个字节是存储的主版本号(Major Version)。Java的版本号是从45开始的。高版本的JDK能向下兼容以前版本的Class文件,但是不能运行以后版本的Class文件。即时文件格式没有发生任何变化,虚拟机也必须执行超过其版本的Class文件。
(2)常量池,紧接着版本号之后
常量池常常可以理解为Class文件中的资源仓库。是Class文件中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据类型。同时他还是在Class文件中第一个出现表类型的数据项目。
常量池入口是U2(两字节)类型的数据,代表常量池容量计数值。从1开始,0空置。22代表有21个常量。0具有特殊含义,表示满足后面某些指向常量池的索引值的数据在特定情况下需要表达不引用任何一个常量池项目的含义。
常量池主要存放字面量和符号引用
Class文件不会保存各个方法、字段的最终内存布局信息,因此这些字段,方法的符号引用不经过运行期转换的话无法得到真正的内存入口地址。也就是无法直接被虚拟机使用。
常量池中每一个常量都是一个表
(3)在常量池结束之后,紧接着两个字节代表访问标志(access flag),
这个标志位用来识别一些类或者接口层次的信息。(包括着Class是类还是接口,访问类型,以及如果是类是否被声明为final)
详细表介绍 见 --表6-7(p173页)
(4)Class文件中确定类的继承关系
索引类,父类索引,都是一个u2类型的数据,而接口索引集合是一组u2类型的数据的集合。Class文件由这三类数据来确定这个类的继承关系。
类索引用来确定类的全限定名,父类索引用来确定父类索引的全限定名,由于父类索引只有一个,所以java不允许多继承。除了Object外,所有的类都有父类,所以除了java.lang.object外,所有java类父类索引都不为0.接口同理。
三个索引按照顺序排列在访问标志之后。
其中前两个索引给指向一个CONSTANT_Class_info,通过它找到可以找到定义在CONSTANT_Utf-8_info类型的常量中的类的全限定名。
...........................
(5)字段表集合
字段表(field_info)用于描述类或者接口中的变量。字段包括类级变量以及实例变量,但不包括局部变量。
字段修饰符放在access_flags项目中。与类中的access_flag非常类似。
紧跟随 access_flags后面的是name_index,与descriptor_index他们都是对常量池的引用。分别代表字段的简单名称以及字段和方法的描述符,
字段表所包含的固定的固定数据项目到descriptor_index为止就结束了,不过在其之后跟着一个属性表集合用于存储一些额外的信息。字段都可以在属性表中描述零至多项的额外信息。
字段表中不会列出从超类或者接口中继承来的字段但有可能列出java代码中原本不存在的自断,比如内部类为了保持对外部类的访问性,会自动添加指向外部类的实例字段。
others,在java语言中,字段是无法重载的,两个字段的数据类型,修饰符不管是否相同,都必须使用不一样的名称。但是对于字节码来讲,如果两个字段的描述符不一致,那么字段重名就是合法的。