对.java文件进行编译后,就生成了.class文件。
那么class文件是什么样的呢?
在Java最初设计的时候,设计者们就卡率过并实现了让其他语言运行在java虚拟机上的可能性。
所以,刻意将Java的规范拆分成了Java语言规范和jav虚拟机规范。
实现了java虚拟机的语言无关性。
其实就是最后统一成存储为字节码的Class文件,
使用JRuby等其他语言可以编译成Class文件,使用java也可以编译成Class文件。
有一些java语言本身无法有效支持的语言特性,并不代表字节码本身不支持。
类文件结构
Class文件就是个很被规范的二进制文件。
其实就是要规范,那个class文件的解释器才能认得它,
而且要够精炼,才能高效运行。
和C语言编译成汇编一样的道理。就是一个规范的文件而已。
做一下规定:
class的文件格式:
* magic:魔数,用于确定这个文件是否为一个能被虚拟机接收的Class文件,一种标识。
Class文件的魔数:0xCAFEBABE
* Minor_version,Major_version:java的版本号,用于该class文件表示可以支持的java语言版本。
* Constant_pool:常量池
* Access_flags:访问标识,用于识别一些类或是接口层次的访问信息
占用两个字节,所以应该有16个标志位,标志位为1表示true,为0表示false。
目前只有其中8个标识为被使用,没有使用一律为0;
如:0x0021则后面八个二进制为:00100001,
所以倒数第1位为public,倒数第6位为Super。这个两个为true。
*This_class:类索引,用于其额定这个类的 权限定名
*super_class:父类索引,用于确定这个类的父类的权限定名。
*interfaces:接口索引集合,用于描述这个类实现了哪些接口
*fields:字段表集合
*methods:方法表集合
*attributes:属性表集合
Constant_pool:常量池
常量池是Class文件中与其他项目关联最多的数据类型,也是占用class文件空间最大的数据项目之一。 常量池中主要存放:字面量(Literal)、符号引用(Symbolic References)
```
如上图所示,常量池中存放着各种了类型的数据,共count个,如果count=0,就后面什么都没有了。 每个数据都有一个字节的标志tag来识别数据的类型,从而获得数据结构,以解析。
前面提到的
类索引和父类索引,他们就是指向了一个 CONSTANT_Class_info 的类描述符常量```
attributes:属性表集合
先说attributes ,因为后面的会依赖这个
其中attribute_name_index中放置了一个引用常量池中的
索引
,这个
索引
会指向一个常量池中的CONSTANT_Utf8_info型的常量,这个字符常量就会指出attribute的类型。
##虚拟机规范预定义的属性
属性名称 | 使用位置 | 含义 |
---|---|---|
Code | 方法表 | Java代码编译成的字节码指令 |
Constant | 字段表 | final关键字定义的常量值 |
Deprecated | 类、放发表、字段表 | 被声明为deprecated的方法和字段 |
Exception | 方法表 | 方法抛出的异常 |
InnerClasses | 类文件 | 内部类列表 |
LineNumberTable | Code属性 | Java源码的行号与字节码指令对应关系 |
LocalVarialeTable | Code属性 | 方法的局部变量描述 |
SourceFile | 类文件 | 源文件名称 |
Synthetic | 类、方法表、字段表 | 标识方法或字段为编译器自动生成的 |
Java虚拟机运行是会忽略它不认识的属性。
而每个属性,都有自己的格式,比如
- ConstantValue
类型 | 名称 | 数量 |
---|---|---|
2个字节 | attribute_name_index | 1 |
4个字节 | attribute_length | 1 |
2个字节 | constantvalue_index | 1 |
这里ConstantValue的attribute_length
比较特殊,必须是2,因为后面的constantvalue_index
是2个字节,但其实也不特殊。
其他的也有自己相应的格式。
interface:接口集合
一堆接口的索引
fields:字段表集合
用于描述接口或类中声明的变量。
字段包括了{类级变量
,实例级变量
},但不包含方法内部声明的变量
Access_flags:字段访问标志
name_index:字段名索引
descriptor_index:字段数据类型索引
属性中则是字段额外的信息,比如:
final static int m =123;
那么会有一项名为ConstantValue的属性,其值指向常量123
描述符标识字符含义
|标识字符|含义|
|:-:|:-:|
|B|基本类型 byte|
|C|基本类型 char|
|D|基本类型 double|
|F|基本类型 float|
|I|基本类型 int|
|J|基本类型 long|
|S|基本类型 short|
|Z|基本类型 boolean|
|V|特殊类型 void|
|L|对象类型,如Ljava/lang/Object |
* ```而
对于同样数组类型,每一维度将使用前置的 "[" 字符来描述
举例子 如
"java.lang.String[][]" => "[[Ljava/lang/String"
"int[]"=>"[I"
methods:方法表集合
用于描述接口或类中声明的变量。
字段包括了{类级变量
,实例级变量
},但不包含方法内部声明的变量
- ``
Access_flags:方法访问标志
name_index:方法名索引
descriptor_index:方法描述符
按照先参数列表,后返回值的顺序描述,参数列表按参数的严格顺序放在一组小括号“()”之内。
例子:
void inc() 描述符:()V
java.lang.String toString() =>()Ljava/lang/String
int indexOf(char[] source,int sourceOffset,char[] target,int targetOffset,int targetCount,int fromIndex)
=>([CII[CIII)I
attributes:比如Code,就是二进制运行代码