一.Class文件结构
1. Class文件是一组以8位字节为基础的二进制流,采用一种类似C语言结构体的伪代码类存储。它只有两种数据类型:无符号数和表。
2. 无符号数属于基本的数据类型,以u1,u2,u4,u8分表代表1个,2个,4个,8个字节。他可以用来描述数字,索引引用,数量值,UTF-8编码的字符串。
3. 表有多个无符号数或其他表作为数据项构成的符合数据类型,所有的表都已_info结尾。
4. Class文件格式
类型 |
名称 |
数量 |
U4 |
Magic(魔数) |
1 |
U2 |
Minor_version(次版本号) |
1 |
U2 |
Major_version(主版本号) |
1 |
U2 |
Constant_pool_length |
1 |
Cp_info |
Constant_pool(常量池) |
Constant_pool_length-1 |
U2 |
Access_flags |
1 |
U2 |
This_class |
1 |
U2 |
Super_class |
1 |
U2 |
Interfaces_count |
1 |
U2 |
Interfaces |
Interfaces_count |
U2 |
Fields_count |
1 |
Field_info |
fields |
Fields_count |
U2 |
Methods_count |
1 |
Method_info |
methods |
Methods_count |
U2 |
Attributes_count |
1 |
Attribute_info |
attributes |
Attribute_count |
5. 魔数和class文件的版本
a) 魔数:确定这个文件是否为一个能被虚拟机接受的class文件。
b) 版本号:每个JDK版本对应一个版本号段,低版本的JDK不能执行标识高版本JDK的版本号class文件,但是高版本的可以执行低版本的(向下兼容的特性)。
c) 常量池:(注意常量池使用1计数的,0表示常量池为空)常量池主要存放两类常量:字面量(Literal)和符号引用(Symbolic Reference)。
字面量包括文本字符串,被声明为final的常量等。
符号引用包括:类和接口的全限定名;字段名称和描述符;方法的名称和描述符。
常量池中的每一项都是一个表。
类型 |
标志 |
描述 |
CONSTANT_UTF-8_info |
1 |
UTF-8编码的字符串 |
CONSTANT_Integer_info |
3 |
整形字面量 |
CONSTANT_Float_info |
4 |
浮点型字面量 |
CONSTANT_Long_info |
5 |
长整型字面量 |
CONSTANT_Double_info |
6 |
双精度浮点型字面量 |
CONSTANT_Class_info |
7 |
类或接口的符号引用 |
CONSTANT_String_info |
8 |
字符串类型字面量 |
CONSTANT_Fieldref_info |
9 |
字段的符号引用 |
CONSTANT_Methodref_info |
10 |
类中方法的引用 |
CONSTANT_InterfaceMethodref_info |
11 |
接口中方法的引用 |
CONSTANT_NameAnd_info |
12 |
字段或方法的部分符号引用 |
d) 访问标志:用于标识一些类或借口层次的访问信息,包括:这个class是接口还是类,是否定义为public类型等
标志名称 |
标志值 |
含义 |
ACC_PUBLIC |
0x0001 |
是否为public类型 |
ACC_FINAL |
0x0010 |
是否声明为final,只有类可以设置 |
ACC_SUPER |
0x0020 |
是否允许使用invokespcial字节码指令 |
ACCC_INTERFACE |
0x0200 |
标识这是一个接口 |
ACC_ABSTRACT |
0x0400 |
是否为abstract类型,对于接口和类这个标识为真,其他类为假 |
ACC_AYNTHETIC |
0x1000 |
标识这个类并非由用户代码产生 |
ACC_ANNOTATION |
0x2000 |
标识这是一个注解 |
ACC_ENUM |
0x4000 |
标识这是一个枚举 |
6. 类索引、父类索引和接口索引集合:类索引,父类索引都是一个u2类型的数据,但是接口索引集合是一组u2类型的集合,class文件由这三项确定继承关系。
7. 字段表集合:用于描述接口或类中声明的变量。字段包括类变量和实例变量,但是不包括方法内的变量。
a) 字段访问标识
标识名称 |
标志值 |
ACC_PUBLIC |
0x0001 |
ACC_PRIVATE |
0x0002 |
ACC_PROTECTED |
0x0004 |
ACC_STATIC |
0x0008 |
ACC_FINAL |
0x0010 |
ACC_VOLTATILE |
0x0040 |
ACC_TRANSIENT |
0x0080 |
ACC_SYNTHETIC |
0x1000 |
ACC_ENUM |
0x4000 |
8. 方法表集合:类似字段表集合。
a) 访问标志
标识名称 |
标志值 |
ACC_PUBLIC |
0x0001 |
ACC_PRIVATE |
0x0002 |
ACC_PROTECTED |
0x0004 |
ACC_STATIC |
0x0008 |
ACC_FINAL |
0x0010 |
ACC_SYNCHRONIZED |
|
ACC_BRIDGE |
0x0040 |
ACC_VARARGS |
0x0080 |
ACC_NATIVE |
0x0100 |
ACC_ABSTRACT |
0x0400 |
ACC_STRICT |
0x0800 |
ACC_SYNTHETIC |
0x1000 |
b) 方法的重载(overload)和重写(override)
重载:除了要与原来的方法具备相同的名称之外,还要具备不同的特征签名,特征签名就是一个方法中各个参数在常量池中的字段符号引用的集合。(java代码的方法特征签名只包括了方法名称,参数顺序,即参数类型,而字节码的特征签名还包括方法返回值及受检异常表)。
重写: 拥有相同的方法名和特征签名但有不同的方法体。
9. 属性集合:在class文件,字段表,方法表中都可以携带自己的属性表集合,以用于描述某些场景专有的信息。
a) 属性表
属性名称 |
使用位置 |
含义 |
Code |
方法表 |
Java代码编译成字节码指令 |
ConstantValue |
字段表 |
Final关键字定义的常量值 |
Deprecated |
类、方法表、字段表 |
被声明为deprecated的方法和字段 |
Exceptions |
方法表 |
方法抛出异常 |
InnerClass |
类文件 |
内部类列表 |
LineNumberTable |
Code属性 |
Java源码的行号与字节码指令的对应关系 |
LocalVariableTable |
Code属性 |
方法的局部变量描述 |
SourceFile |
类文件 |
源文件名称 |
Synthetic |
类、方法表、字段表 |
标识方法或字段为编译器自动生成 |
b) Javac在编译的时候会把对this关键字的访问转变为一个普通方法参数的访问,然后在虚拟机调用实例方法时自动传入此参数即可。
c) Java异常实际上是java代码的一部分不是简单地跳转。