Java 代码经过 javac
编译后,会生成一个 .class
文件,其中包含 Java 字节码。字节码文件由 JVM 规范定义,由多个部分组成,每个部分都有特定的作用。
1. .class
文件的基本结构
Java 字节码文件采用 严格的二进制格式,其结构如下:
ClassFile {
u4 magic; // 魔数,标识该文件是一个 Java 字节码文件
u2 minor_version; // 次版本号
u2 major_version; // 主版本号
u2 constant_pool_count; // 常量池大小
cp_info constant_pool[]; // 常量池
u2 access_flags; // 访问标志(如 public, abstract, final 等)
u2 this_class; // 当前类的索引
u2 super_class; // 父类的索引
u2 interfaces_count; // 实现的接口数量
u2 interfaces[]; // 接口表
u2 fields_count; // 成员变量数量
field_info fields[]; // 字段表(成员变量)
u2 methods_count; // 方法数量
method_info methods[]; // 方法表
u2 attributes_count; // 属性数量
attribute_info attributes[]; // 其他属性表
}
其中:
- u1(1 字节): 无符号 8 位整数
- u2(2 字节): 无符号 16 位整数
- u4(4 字节): 无符号 32 位整数
2. 详细解析 .class
文件的各部分
(1) 魔数(Magic Number)
- 作用:用于标识该文件是 Java 字节码文件
- 固定值:
0xCAFEBABE
- 例如:
CA FE BA BE
(16 进制),用于防止错误地将其他文件误识别为 Java 类文件。
(2) 版本号(Minor & Major Version)
- 作用:指示 Java 编译器的版本,以便 JVM 确定是否支持该
.class
文件 - 主版本号(Major Version):如
60
代表 JDK 16 - 次版本号(Minor Version):通常为
0
Java 版本 | Major Version |
---|---|
Java 8 | 52 |
Java 11 | 55 |
Java 17 | 61 |
(3) 常量池(Constant Pool)
- 作用:存储类名、方法名、字符串、数值等信息
- 每个
.class
文件包含一个 常量池(Constant Pool),类似于符号表 - 常量池包含 字面量(如
int
,String
)和 符号引用(类名、方法、字段)
示例:
Constant pool:
#1 = Methodref #2.#3 // 调用某个方法
#2 = Class #4 // 类名
#3 = NameAndType #5:#6 // 方法名称和类型
#4 = Utf8 "HelloWorld" // 类名字符串
#5 = Utf8 "main" // 方法名字符串
(4) 访问标志(Access Flags)
- 作用:描述类的访问权限和属性
- 取值(可组合):
0x0001
(ACC_PUBLIC
):类是public
0x0010
(ACC_FINAL
):类是final
0x0200
(ACC_INTERFACE
):类是接口0x0400
(ACC_ABSTRACT
):类是抽象类
(5) 当前类和父类(This Class & Super Class)
this_class
:当前类在常量池的索引super_class
:父类在常量池的索引- 示例:
this_class = #2
(表示HelloWorld
类)super_class = #3
(表示java/lang/Object
)
(6) 接口表(Interfaces)
- 作用:存储当前类实现的所有接口
- 例如:
public class MyClass implements Serializable, Cloneable {}
interfaces_count = 2
interfaces[] = { #4, #5 }
(索引指向Serializable
和Cloneable
)
(7) 字段表(Fields Table)
- 作用:存储类的成员变量
- 结构:
field_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[]; }
- 示例:
可能会被表示为:private int num;
#6 = Utf8 "num" #7 = Utf8 "I" // int 类型
(8) 方法表(Methods Table)
- 作用:存储类的方法,包括
main()
和构造方法 - 结构:
method_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[]; }
- 例如:
可能会被转换成:public void sayHello() { System.out.println("Hello!"); }
#8 = Utf8 "sayHello" #9 = Utf8 "()V" // 方法签名,V 代表 void
(9) 属性表(Attributes Table)
- 作用:存储额外的信息,如代码段、异常表、调试信息等
- 主要属性:
- Code:存储字节码指令
- LineNumberTable:存储 Java 代码行号映射(用于调试)
- SourceFile:存储源文件名称
- Exceptions:异常表
3. 示例:简单 Java 类的 .class
解析
假设有以下 Java 代码:
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, JVM!"); } }
使用 javap -verbose HelloWorld.class
反编译后,可以看到:
Classfile HelloWorld.class
Last modified ...; size ...
public class HelloWorld
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #2.#3 // Method java/lang/Object."<init>":()V
#2 = Class #4 // HelloWorld
#3 = NameAndType #5:#6 // main:([Ljava/lang/String;)V
#4 = Utf8 "HelloWorld"
#5 = Utf8 "main"
#6 = Utf8 "([Ljava/lang/String;)V"
...
可以看到 .class
文件存储了 常量池、方法表、类信息等内容。
4. 总结
.class
文件是 JVM 运行 Java 程序的核心,它包含:
- 魔数(Magic Number)确保文件格式正确
- 版本号(Java 版本兼容性)
- 常量池(存储类名、方法名、字符串等)
- 访问标志(类的访问权限)
- 类信息(当前类和父类)
- 接口表(存储实现的接口)
- 字段表(存储成员变量)
- 方法表(存储方法定义)
- 属性表(存储字节码指令等)