载体
- 二进制流
- 字节为单位,没有分隔符,大端编码(高位在前)
- 由无符号数(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 | 运行时不可见的注解 |