类文件结构
1. Java一次编写,到处执行的基石:
Java编译产生的是字节码(bytecode)。sun公司和其它虚拟机提供商公布各个平台上的虚拟机。这些虚拟机能够加载和执行这些与平台无关的字节码。
2. Class类文件结构:
Class文件是一组以字节为基础单位的二进制字节流,各个数据项目严格依照顺序紧凑的排列在class文件之中。中间没有不论什么分隔符。
Class文件使用无符号数和表来存储全部的数据。
无符号数属于基本数据类型,用u1、u2、u4、u8来代表1个字节、2个字节、4个字节和8个字节的无符号数;表是由多个无符号数和其它表构成的符合数据类型。
整个class文件本质上就是一张表。它的数据项例如以下:
Class文件的格式
不管是无符号数还是表。当要描写叙述同一类型但数量不定的多个数据的时候,常常会使用一个前置的容量计数器加若干连续的数据项的形式。称为一个集合。
这里须要重点声明一下的是,Class文件没有不论什么的分隔符,所以上图中的全部数据项,不管是顺序还是数量,甚至数据存储的字节序,都被严格限定,不同意改变。
Class文件里的常量池:常量池是class文件的资源仓库。存放两类常量:字面量和符号引用。字面量包含文本字符串、声明为final的常量。符号引用包含:①类和接口的全限定名、②字段的名称和描写叙述符、③方法的名称和描写叙述符。常量池的每一项常量都是一个表,一共14中表结构。比如:
Constant_Class_info型常量的结构
常量池并没有固定的结构,它仅仅是一个资源仓库,想查找它里面的内容仅仅需提供索引值就可以。
Class文件里的类索引、父类索引、接口索引集合:类索引和父类索引都是一个u2类型的数据,它们都有且仅仅有一个(由于java不同意多继承),他们的值都是指向常量池中对应项的一个索引值。接口索引集合入口的第一项是接口索引的数量。后面是一系列u2类型的索引指向常量池中的对应值的索引。
字段表集合:字段表用于描写叙述类或接口中声明的变量。
字段表的结构例如以下:
字段表结构
这些属性按access_flags、name_index、descriptor_index、attributes_count、attributes的顺序排列。
⑴access_flags存储字段修饰符,跟类中的access_flags很相似,都是一个u2的数据类型,当中能够设置的标志位和含义例如以下:
字段訪问标志
⑵name_index存储对常量池的索引。代表字段的简单名称。
⑶descriptor_index存储对常量池的索引,存储字段的描写叙述符。关于简单名称、描写叙述符、全限定名,这里做一个介绍例如以下:
①简单名称就是没有类型和參数修饰的方法或字段的名称。如inc()方法和m字段的简单名称就是inc和m。
②全限定名很好理解。类的全限定名就是包1.包2….类名。在class文件里不用.的ascii码而是用的/作分隔符。也就是包1/包2/…/类名;
③描写叙述符比較复杂,描写叙述符的作用是描写叙述字段的数据类型、方法的參数列表(包含数量、类型和顺序)和返回值,基本数据类型及void用一个大写字符来表示。对象类型用L加对象的全限定名来表示,详细例如以下:
描写叙述符标识字符含义
对于数组类型,每个维度用一个前置的[字符来描写叙述。比如java.lang.String[][]类型的二维数组会被记录为[[Ljava/lang/String;,int[]型数组会被记录为[I。用描写叙述符描写叙述方法时,依照先參数列表、后返回值的顺序描写叙述,參数列表依照參数的严格顺序存放在()之内,比如void inc()描写叙述符为()V。java.lang.String toString()描写叙述符为()Ljava/lang/String;,intindexOf(char[] source, int sourceOffset, int sourceCount, char[] target, inttargetOffset, int targetCount, int fromIndex)的描写叙述符为([CII[CIII)I。
⑷在descriptor_index之后尾随着一个属性表集合用来存储一些额外的信息。
字段表集合不会列出从超类或者父接口中继承而来的字段。但有可能列出原Java代码中不存在的字段。如内部类为了保持对外部类的訪问性会自己主动加入指向外部类实例的字段。
方法表集合:方法表的结构和字段表相似,例如以下:
方法表结构
这些数据项目的含义也很相似,仅在訪问标志和属性表集合的可选项中有所区别。
方法訪问标志
方法的定义能够通过訪问标志、名称索引、描写叙述符索引表达清楚。那么方法的代码那里去了呢?方法里的Java代码。经过编译器编译成字节码指令后,存放在方法属性表中一个名为Code的属性里面。假设父类的方法没有被子类覆盖,则方法表集合中不会出现父类的方法信息。但相同的,有可能出现编译器自己主动加入的方法,如类构造器<cinit>方法和实例构造器<init>方法。
属性表集合:属性表用来描写叙述场景专有的信息。
属性表不须要具有严格的顺序,对于每个属性。它的名称须要从常量池中引用一个Constant_Utf8_info类型的常量来表示,而属性值的结构则是全然自己定义的,仅仅须要用一个u4类型的数据去说明属性值所占用的位数就可以,属性表结构例如以下:
属性表结构:
典型的属性。如Code,其结构例如以下:
3. 字节码指令:
Java虚拟机的指令由一字节长度的操作码加零至多个操作数组成。