Skip to content

Class 文件内部数据类型/魔数/版本号

Class 文件的数据类型

Class 文件是一种紧凑的二进制格式,使用两种基本数据类型:无符号数

基本数据类型

无符号数

无符号数是 Class 文件的基本数据单位:

类型大小说明
u11 字节无符号单字节数
u22 字节无符号双字节数(big-endian)
u44 字节无符号四字节数

Java 使用 Big-Endian(大端序) 存储多字节数据:

u2 值 0x1234 在文件中存储为:
  高字节 0x12 → 低字节 0x34

读取时先读高位,再读低位

表是由多个无符号数或其他表组成的复合结构:

java
// 伪代码描述 Class 文件结构
ClassFile {
    u4              magic;              // 魔数
    u2              minor_version;       // 次版本号
    u2              major_version;       // 主版本号
    cp_info         constant_pool[];     // 常量池(表)
    u2              access_flags;        // 访问标识
    u2              this_class;          // 当前类索引
    u2              super_class;         // 父类索引
    // ... 更多字段
}

魔数(Magic Number)

魔数是 Class 文件开头的 4 字节固定值:0xCAFEBABE

Class 文件开头:
┌────────┬────────┬────────┬────────┐
│ CA     │ FE     │ BA     │ BE     │
│ 0xCA   │ 0xFE   │ 0xBA   │ 0xBE   │
└────────┴────────┴────────┴────────┘
  magic = 0xCAFEBABE

魔数的来历

这个神奇的数字来自 Café Babe——James Gosling 团队在开发 Java 时的代码命名惯例。当时咖啡是程序员的标配,所以用了咖啡相关的名字。

魔数的作用

魔数用于验证文件是否是一个合法的 Class 文件:

java
// 验证 Class 文件合法性
public class MagicValidator {
    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("HelloWorld.class");
        byte[] header = new byte[4];
        fis.read(header);

        // 验证魔数
        if (header[0] == (byte) 0xCA &&
            header[1] == (byte) 0xFE &&
            header[2] == (byte) 0xBA &&
            header[3] == (byte) 0xBE) {
            System.out.println("合法的 Class 文件");
        } else {
            System.out.println("不是合法的 Class 文件");
        }
    }
}

字节码查看魔数

bash
# 查看 Class 文件的魔数(十六进制)
hexdump -C HelloWorld.class | head -n 1

# 输出:
# 00000000  ca fe ba be 00 00 00 37  00 3d 0a 00 0b 00 27 0a
#           └─────┘ └───────────────────────── major version: 55 = JDK 11

版本号(Class File Version)

魔数之后是 4 字节版本号:

┌────────┬────────┬────────┬────────┐
│ minor_version  │  major_version │
│   (2字节)      │    (2字节)    │
└────────┴────────┴────────┴────────┘

JDK 版本与主版本号对照

主版本号JDK 版本说明
0x30 = 48JDK 1.4-
0x31 = 49JDK 5引入泛型
0x32 = 50JDK 6-
0x33 = 51JDK 7动态语言支持
0x34 = 52JDK 8Lambda 表达式
0x35 = 53JDK 9模块化
0x36 = 54JDK 10-
0x37 = 55JDK 11-
0x38 = 56JDK 12-
0x39 = 57JDK 13-
0x3A = 58JDK 14-
0x3B = 59JDK 15-
0x3C = 60JDK 16-
0x3D = 61JDK 17-
0x3E = 62JDK 18-
0x3F = 63JDK 19-
0x40 = 64JDK 20-
0x41 = 65JDK 21-
bash
# 查看 Class 文件版本
javap -verbose HelloWorld.class | grep "major version"

# 输出:
# major version: 55
# → JDK 11 编译

# 或者用 hexdump
hexdump -C HelloWorld.class | head -n 1
# 00000000  ca fe ba be 00 00 00 37
#                            └─── 0x37 = 55 = JDK 11

版本号的意义

JVM 通过主版本号判断 Class 文件是否可以被当前 JVM 加载:

java
// 如果加载的 Class 文件版本高于 JVM 支持的版本,会报错:
// UnsupportedClassVersionError
UnsupportedClassVersionError: Unsupported major.minor version 55.0
    // 55 = JDK 11
    // 当前 JVM 可能只支持到 JDK 10(版本 54)

Class 文件结构总览

完整的 Class 文件结构:

ClassFile {
    u4              magic;                 // 0xCAFEBABE
    u2              minor_version;         // 次版本号(通常 0)
    u2              major_version;         // 主版本号

    u2              constant_pool_count;    // 常量池计数
    cp_info         constant_pool[constant_pool_count - 1];

    u2              access_flags;          // 访问标识
    u2              this_class;            // 当前类索引
    u2              super_class;           // 父类索引

    u2              interfaces_count;      // 接口计数
    u2              interfaces[interfaces_count];

    u2              fields_count;          // 字段计数
    field_info      fields[fields_count];

    u2              methods_count;         // 方法计数
    method_info     methods[methods_count];

    u2              attributes_count;      // 属性计数
    attribute_info  attributes[attributes_count];
}

访问标识(Access Flags)

access_flags 是一个 16 位的掩码,表示类和接口的访问权限:

标识说明
ACC_PUBLIC0x0001public
ACC_FINAL0x0010final,不能被继承
ACC_SUPER0x0020使用新的 invokespecial 语义
ACC_INTERFACE0x0200接口
ACC_ABSTRACT0x0400abstract
ACC_SYNTHETIC0x1000编译器生成,非源码
ACC_ANNOTATION0x2000注解类型
ACC_ENUM0x4000枚举类型
ACC_MODULE0x8000模块(JDK 9+)

常见的访问标识组合

类型访问标识
public classACC_PUBLIC | ACC_SUPER = 0x0021
class(默认)ACC_SUPER = 0x0020
public abstract interfaceACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT
public final enumACC_PUBLIC | ACC_FINAL | ACC_SUPER | ACC_ENUM

本节小结

Class 文件基础数据类型速查:

元素类型说明
魔数u4 = 0xCAFEBABE文件合法性验证
版本号u2 + u2major:minor,JDK 版本对应
无符号数u1/u2/u4Class 文件基本数据类型
复合结构由无符号数组成
access_flagsu2类/接口的访问权限

理解 Class 文件的基本结构,是深入分析字节码的基础。

下一节,我们来看 常量池(计数器/字面量/符号引用)

基于 VitePress 构建