类文件结构
代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步
1 概述
越来越多的程序语言选择了与操作系统和机器指令集无关的、平台中立的格式作为程序编译后的存储格式
2 无关性的基石
在Java虚拟机之上运行的语言,如Clojure、Groovy、JRuby、Jython、Scala
3 Class类文件的结构
Class文件是一组以8位字节为基础单位的二进制流,中间没有添加任何分隔符,8位以上的按照高位在前的方式分割成若干个8位字节进行存储
Class文件格式采用伪结构存储,只有两种数据类型:无符号数和表;
无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值,或者按照UTF-8编码构成字符串值;
表习惯性地以“_info”结尾。表用于描述有层次关系的复合结构的数据
3.1 魔数与Class文件的版本
Class文件的头4个字节称为魔数,值为:0xCAFEBABE,它的唯一作用是用于确定这个文件是否为一个能被虚拟机接受的Class文件。
第5和第6个字节是次版本号(Minor Version),第7个和第8个字节是主版本号(Major Version)
使用十六进制编辑器WinHex打开这个Class文件
表6-2列举了从JDK 1.1到1.7之间,主流JDK版本编译器输出的默认和可支持的Class文件版本号
3.2 常量池
与其他项目关联最多的,占空间最大的表类型数据项目
值为22,表示池中有21项常量,索引为1-21,第0项常量空出来,是为了满足后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”的意思
常量池之中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)
如文本字符串、被声明为final的常量值等
符号引用包括类和接口的全限定名(Fully Qualified Name)□ 字段的名称和描述符(Descriptor)□ 方法的名称和描述符。当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析并翻译到具体的内存地址之中。
常量池中的每一项常量都是一个表,共有11种结构,数据结构相同(起始都是一位的u1类型标志tag)
07 00 02
0d代表name_index是一个索引值,它指向常量池中一个CONSTANT_Utf8_info类型的常量,此常量代表了这个类(或者接口)的全限定名,这里的name_index值为0x0002,即指向了常量池中的第二项常量
javap工具的-verbose参数输出TestClass.class文件的字节码内容(省略了常量池以外的信息)
3.3 访问标志
2字节,用于识别一些类或接口层次的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final,等等
3.4 类索引、父类索引与接口索引集合
类索引u2用于确定这个类的全限定名,父类索引u2用于确定这个类的父类的全限定名,接口索引(u2集合)集合就用来描述这个类实现了哪些接口,将按implements语句从左到右排在接口索引集合中。
类索引,父类索引通过CONSTANT_Class_info类型的常量中的索引值可以找到定义在CONSTANT_Utf8_info类型的常量中的全限定名字符串;
3.5 字段表集合
描述接口或类中声明的变量,第一个为U2类型代表容量计数,字段(field)包括了类级变量或实例级变量。
一个字段可以包含什么信息?可以包括的信息有:字段的作用域(public、private、protected修饰符)、是类级变量还是实例级变量(static修饰符)、可变性(final)、并发可见性(volatile修饰符,是否强制从主内存读写)、可否序列化(transient修饰符)、字段数据类型(基本类型、对象、数组)、字段名称
字段表结构
字段访问标志
name_index和descriptor_index:代表着字段的简单名称及字段和方法的描述符
描述符标识字符含义,void为VoidDescriptor;
综上:0001 0002 0005 0006代表private int m ;
字段表集合中不会列出从超类或父接口中继承而来的字段,但有可能列出原本Java代码之中不存在的字段,譬如在内部类中为了保持对外部类的访问性,会自动添加指向外部类实例的字段
3.6 方法表集合
方法表结构
方法访问标志,比属性多了synchronized、native、strictfp和abstract,少了volatile关键字和transient
方法里的Java代码,经过编译器编译成字节码指令之后,存放在方法属性表集合中一个名为“Code”的属性里面
父类中没有被重写的不会出现,类构造喝实例构造器会有;
3.7 属性表集合s
虚拟机规范预定义的属性
1.Code属性
Java程序方法体里面的代码经过Javac编译器处理之后,最终变为字节码指令存储在Code属性内
2.Exceptions属性
列举出方法中可能抛出的受查异常
3.LineNumberTable属性
用于描述Java源码行号与字节码行号(字节码的偏移量)之间的对应关系
4.LocalVariableTable属性
用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系
5.SourceFile属性
用于记录生成这个Class文件的源码文件名称
6.ConstantValue属性
作用是通知虚拟机自动为静态变量赋值
7.InnerClasses属性
用于记录内部类与宿主类之间的关联
8.Deprecated及Synthetic属性
Deprecated属性用于表示某个类、字段或方法,已经被程序作者定为不再推荐使用,在代码中使用@deprecated注释进行设置
inner_class_access_flags标志
标识一个类、字段或方法是编译器自动产生的,也可以设置它们访问标志中的ACC_SYNTHETIC标志位