载体
- 二进制流
- 字节为单位,没有分隔符,大端编码(高位在前)
- 由无符号数(u1|u2|u4|u8)和表(*_info)构成
| 类型 | 名称 | 数量 |
|---|---|---|
| u4 | magic | 1 |
| u2 | minor_version | 1 |
| u2 | major_version | 1 |
| u2 | constant_pool_count | 1 |
| cp_info | constant_pool | constant_pool_count - 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 |
| attributes_info | attributes | attributes_count |
魔数与版本号
u4: 0xCAFEBABE
版本号以主版本号为主(45开始),只能向下兼容
JDK1.1支持45.0-45.65535
JDK1.2支持45.0-46.65535
JDK1.7支持45.0-51.65535
但使用编译器输出的Class文件的版本号,除了和编译器本身的版本相关外,还可以使用-target参数向前指定
常量池
常量池第0项保留,所以数量为常量池容量+1
表项内容详见《深入理解Java虚拟机》p172
访问标志
0x0001:ACC_PUBLIC
0x0010:ACC_FINAL
0x0020:ACC_SUPER(JDK1.0.2后为真)
0x0200:ACC_INTERFACE
0x0400:ACC_ABSTRACT
0x1000:ACC_SYNTHETIC(不由代码产生)
0x2000:ACC_ANNOTATION
0x4000:ACC_ENUM
类索引、父类索引、接口索引集合
类索引:u2指向常量池中一个类型为Constant_Class_info的类描述符,描述了类的全限定名
父类索引:u2指向常量池中一个类型为Constant_Class_info的类描述符,描述了父类的全限定名
接口索引:u2的接口计数器,计数器个u2类型的类描述符
字段表集合
field_info
一个字段项包括如下部分:
- u2: access_flags 字段修饰符
- u2: name_index 简单名称
- u2: desciptor_index 描述符
- u2: attribute_count 属性表计数器
- attribute_info: attributes 属性表集合
name_index, descriptor_index 字段名称和描述符
分别指向了常量池中的名称和类型,相当于常量池中的nameAndType
描述符标识字符含义
| 字符 | 含义 | 字符 | 含义 |
|---|---|---|---|
| B | byte | J | long |
| C | char | S | short |
| D | double | Z | boolean |
| F | float | V | void |
| I | int | L | 对象,Ljava/lang/Object |
数组类型前置一个[,java.lang.String[][]为[[Ljava/lang/String
attributes 属性表集合
attributes用来描述额外信息,如初始值等
注意
不会包括继承字段,但可能会包括未定义字段,如内部类保持一个外部类的引用
方法表集合
结构与字段表一样
descriptor_index 描述符
先参数列表,按顺序放在()之中,然后跟着返回值类型
void inc() --> ()V
String toString() --> ()Ljava/lang/String;
注意
不会包括没有重写的父类方法;
如果没有显示定义的构造函数,会添加一个名为"<init>"的实例构造器;
方法中也可能会有类构造器"<clinit>";
Class文件中的方法重载,只要描述符不完全一样即可,也就是可以通过返回值区分;
方法中的代码会放在方法属性表集合中一个名为"Code"的属性里。
属性表集合
字段,方法,Class文件,Code属性都可以包括各自的属性表集合。
属性表元素结构
u2:attribute_name_index 指向常量池一个utf8
u4:attribute_length 属性值的长度
u1:info
方法属性表
| 名称 | 含义 |
|---|---|
| Code | Java代码编译成的字节码指令 |
| Exceptions | 方法抛出的异常 |
| Deprecated | 被D的方法(boolean) |
| Signature | 用于泛型情况下的方法签名,避免擦除后签名混乱 |
| Synthetic | 标识自动生成的方法(boolean) |
| RuntimeVisibleAnnotations | 指明运行时可见的注解 |
| RuntimeInvisibleAnnotations | 运行时不可见的注解 |
| RuntimeVisibleParameterAnnotations | 运行时可见的方法参数注解 |
| RuntimeInvisibleParameterAnnotations | 运行时不可见的方法参数注解 |
| AnnotationDefault | 注解类元素的默认值 |
Code属性
| 类型 | 名称 | 含义 |
|---|---|---|
| u2 | attribute_name_index | Code |
| u4 | attribute_length | 属性值的长度 |
| u2 | max_stack | 操作数栈最大深度(用于栈帧中分配) |
| u2 | max_locals | 局部变量表需要的空间,32位以下的数据类型一个Slot,以上的两个Slot,包括了方法参数、局部变量、this、异常参数(catch)等,但由于作用域的问题,Slot可以复用 |
| u4 | code_length | 字节码长度,最多65535条 |
| u1 | code | 一条字节码指令一个字节 |
| u2 | exception_table_length | 异常表长度 |
| exception_info | exception_table | 异常表(深入理解Java虚拟机P186) |
| u2 | attributes_count | 属性表计数器 |
| attribute_info | attributes | 属性表 |
Code的属性表中包括了三个个属性
LineNumberTable用于描述Java源码与字节码行号的对应关系
LocalVariableTable用于描述栈帧中局部变量表中的变量与Java源码定义的变量之间的关系
StackMapTable在虚拟机加载字节码的验证阶段被新类型检查验证器使用
字段属性表
| 名称 | 含义 |
|---|---|
| ConstantValue | final关键字定义的常量值 |
| Signature | 泛型 |
| Deprecated | 被D字段 |
| Synthetic | 自动生成 |
| RuntimeVisibleAnnotations | 指明运行时可见的注解 |
| RuntimeInvisibleAnnotations | 运行时不可见的注解 |
Class文件属性表
| 名称 | 含义 |
|---|---|
| EnclosingMethod | 局部类或者匿名类标识外围方法 |
| InnerClasses | 内部类列表 |
| SourceFile | 源文件名称 |
| SourceDebugExtension | 额外的调试信息 |
| LocalVariableTypeTable | 用特征签名代替描述符,描述泛型参数化类型 |
| BootstrapMethods | invokedynamic引用的引导方法限定符 |
| Signature | 泛型 |
| Deprecated | 被D字段 |
| Synthetic | 自动生成 |
| RuntimeVisibleAnnotations | 指明运行时可见的注解 |
| RuntimeInvisibleAnnotations | 运行时不可见的注解 |