深入 JVM 核心:一文读懂 Class 文件结构(附 Hex 实战解析)

3 阅读5分钟

源码之前,了无秘密。——《编程珠玑》

作为 Java 开发者,我们每天编写 .java 文件,却很少关心编译后的 .class 文件里到底藏了什么。本文将带你从 Hex 层面逐字节拆解 Class 文件,彻底搞懂 JVM 的类加载基石。


一、Class 文件概览

Class 文件是 Java 虚拟机执行引擎的"食材",它是一组以 8 位字节为基础单位的二进制流,各数据项严格按照顺序紧凑排列,没有任何分隔符。

1.1 整体结构

数据类型名称数量
u4magic(魔数)1
u2minor_version(次版本号)1
u2major_version(主版本号)1
u2constant_pool_count(常量池容量)1
cp_infoconstant_pool(常量池)constant_pool_count - 1
u2access_flags(访问标志)1
u2this_class(类索引)1
u2super_class(父类索引)1
u2interfaces_count(接口数)1
u2interfaces(接口索引集合)interfaces_count
u2fields_count(字段数)1
field_infofields(字段表)fields_count
u2methods_count(方法数)1
method_infomethods(方法表)methods_count
u2attributes_count(属性数)1
attribute_infoattributes(属性表)attributes_count

💡 u1u2u4 分别代表 1、2、4 个字节的无符号数


二、Hex 实战:写一个最简单的类

先创建一个最基础的 Java 类:

// Hello.java
public class Hello {
    private static final int MAGIC = 0xCAFEBABE;
    
    public static void main(String[] args) {
        System.out.println("Hello, Class File!");
    }
}

编译后,用 xxd 或 Hex 编辑器打开 Hello.class

javac Hello.java
xxd Hello.class | head -20

输出:

00000000: cafe babe 0000 0034 0013 0a00 0400 0f09  .......4........
00000010: 0003 0010 0700 1107 0012 0100 054d 4147  .............MAG
00000020: 4943 0100 0149 0100 0d43 6f6e 7374 616e  IC...I...Constan
00000030: 7456 616c 7565 0300 0000 0001 0006 3c69  tValue.......<i

三、逐字节解析

3.1 魔数(Magic Number)- 4 bytes

 cafe babe
  • 位置:第 0-3 字节
  • 0xCAFEBABE(咖啡宝贝 ☕)
  • 作用:标识这是一个合法的 Class 文件

很多文件格式都有魔数:JPEG 是 FF D8 FF,PNG 是 89 50 4E 47

3.2 版本号 - 4 bytes

 0000 0034
  • 次版本号(minor_version):0x0000 → 0
  • 主版本号(major_version):0x0034 → 52

版本对照表

JDK 版本十进制十六进制
JDK 1.1450x2D
JDK 1.2460x2E
.........
JDK 8520x34
JDK 11550x37
JDK 17610x3D
JDK 21650x41

所以 0x34 = JDK 8 编译的类文件。

3.3 常量池(Constant Pool)

 0013
  • constant_pool_count0x0013 = 19
  • 实际常量数:19 - 1 = 18 个(索引 0 保留)

常量池是 Class 文件的 "资源仓库",存放:

  • 字面量(字符串、final 常量值)
  • 符号引用(类名、方法名、字段名)

每个常量以 tag(1 byte)开头,标识类型:

Tag类型说明
1CONSTANT_Utf8UTF-8 字符串
3CONSTANT_Integer整型字面量
4CONSTANT_Float浮点字面量
7CONSTANT_Class类/接口符号引用
8CONSTANT_String字符串字面量
9CONSTANT_Fieldref字段符号引用
10CONSTANT_Methodref方法符号引用
12CONSTANT_NameAndType名称和类型

3.4 访问标志(Access Flags)- 2 bytes

 0021

0x0021 = 0x0001 | 0x0020 = ACC_PUBLIC | ACC_SUPER

常见标志位:

标志名含义
ACC_PUBLIC0x0001public
ACC_FINAL0x0010final
ACC_SUPER0x0020允许 invokespecial 字节码指令
ACC_INTERFACE0x0200接口
ACC_ABSTRACT0x0400抽象类

3.5 类索引与父类索引

 0003  // this_class  指向常量池 #3
 0004  // super_class  指向常量池 #4(java/lang/Object)

3.6 字段表与方法表

字段表描述类中声明的变量(实例变量 + 类变量),方法表描述方法定义。两者结构类似:

// 字段表结构
field_info {
    u2 access_flags;      // 访问标志
    u2 name_index;        // 名称索引(指向常量池)
    u2 descriptor_index;  // 描述符索引
    u2 attributes_count;  // 属性数量
    attribute_info attributes[attributes_count];
}

描述符规则

标识字符含义
Bbyte
Cchar
Ddouble
Ffloat
Iint
Jlong
L对象类型(如 Ljava/lang/Object;)
Sshort
Zboolean
[数组(如 [I 表示 int[])
Vvoid

四、实用工具推荐

4.1 javap - 官方反编译器

# 查看基本信息
javap -v Hello.class

# 查看字节码
javap -c Hello.class

-v 参数输出的详细信息,其实就是对 Class 文件结构的"人类可读版"翻译。

4.2 jclasslib 插件

IDEA 插件,可视化查看 Class 文件结构,比 Hex 编辑器友好太多:

Settings → Plugins → 搜索 "jclasslib" → 安装

4.3 Hex Fiend / 010 Editor

专业 Hex 编辑器,配合 Class 文件模板,解析一目了然。


五、总结

知识点要点
魔数0xCAFEBABE,Class 文件身份证
版本号主版本号 52 = JDK 8
常量池资源仓库,从索引 1 开始
访问标志按位或运算组合
描述符字段/方法的类型签名

理解 Class 文件结构,是深入 JVM 的必经之路。无论是排查类加载问题、分析字节码增强,还是手写 Class 文件,这些知识都将成为你的利器。


📌 文末思考:你能用 Hex 编辑器手动修改 Class 文件的版本号,让它在低版本 JDK 上运行吗?有什么风险?欢迎在评论区讨论!