6.3.1 魔数与Class文件的版本

6.3.1 魔数与Class文件的版本

每个Class文件的头4个字节被称为魔数(Magic Number),它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件。不仅是Class文件,很多文件格式标准中都有使用魔数来进行身份识别的习惯,譬如图片格式,如GIF或者JPEG等在文件头中都存有魔数。使用魔数而不是扩展名来进行识别主要是基于安全考虑,因为文件扩展名可以随意改动。文件格式的制定者可以自由地选择魔数值,只要这个魔数值还没有被广泛采用过而且不会引起混淆。Class文件的魔数取得很有“浪漫气息”, 值为0xCAFEBABE(咖啡宝贝?)。这个魔数值在Java还被称作“Oak”语言的时候(大约是1991年前后)就已经确定下来了。它还有一段很有趣的历史,据Java开发小组最初的关键成员Patrick Naughton 所说:“我们一直在寻找一些好玩的、容易记忆的东西,选择0xCAFEBABE是因为它象征着著名咖啡品牌Peet’s Coffee深受欢迎的Baristas咖啡。”[^1]这个魔数似乎也预示着日后“Java”这个商标名称的出现。

紧接着魔数的4个字节存储的是Class文件的版本号:第5和第6个字节是次版本号(Minor Version),第7和第8个字节是主版本号(Major Version)。Java的版本号是从45开始的,JDK 1.1之后的每个JDK大版本发布主版本号向上加1(JDK 1.0~1.1使用了45.0~45.3的版本号),高版本的JDK能向下兼容以前版本的Class文件,但不能运行以后版本的Class文件,因为《Java虚拟机规范》在Class文件校验部分明确要求了即使文件格式并未发生任何变化,虚拟机也必须拒绝执行超过其版本号的Class 文件。

例如,JDK 1.1能支持版本号为45.0~45.65535的Class文件,无法执行版本号为46.0以上的Class文件,而JDK 1.2则能支持45.0~46.65535的Class文件。目前最新的JDK版本为13,可生成的Class文件主版本号最大值为57.0。

为了讲解方便,笔者准备了一段最简单的Java代码(如代码清单6-1所示),本章后面的内容都将以这段程序使用JDK 6编译输出的Class文件为基础来进行讲解,建议读者不妨用较新版本的JDK跟随本章的实验流程自己动手测试一遍。

代码清单6-1 简单的Java代码

1
2
3
4
5
6
7
package org.fenixsoft.clazz;
public class TestClass {
private int m;
public int inc() {
return m + 1;
}
}

图6-2显示的是使用十六进制编辑器WinHex打开这个Class文件的结果,可以清楚地看见开头4个字节的十六进制表示是0xCAFEBABE,代表次版本号的第5个和第6个字节值为0x0000,而主版本号的值为0x0032,也即是十进制的50,该版本号说明这个是可以被JDK 6或以上版本虚拟机执行的Class文件。

image-20211118120945322

图6-2 Java Class文件的结构

表6-2列出了从JDK 1.1到13之间,主流JDK版本编译器输出的默认的和可支持的Class文件版本号。

表6-2 Class文件版本号

image-20211118124632741

注:从JDK 9开始,Javac编译器不再支持使用-source参数编译版本号小于1.5的源码。

关于次版本号,曾经在现代Java(即Java 2)出现前被短暂使用过,JDK 1.0.2支持的版本45.0~ 45.3(包括45.0~45.3)。JDK 1.1支持版本45.0~45.65535,从JDK 1.2以后,直到JDK 12之前次版本号均未使用,全部固定为零。而到了JDK 12时期,由于JDK提供的功能集已经非常庞大,有一些复杂的新特性需要以“公测”的形式放出,所以设计者重新启用了副版本号,将它用于标识“技术预览版”功能特性的支持。如果Class文件中使用了该版本JDK尚未列入正式特性清单中的预览功能,则必须把次版本号标识为65535,以便Java虚拟机在加载类文件时能够区分出来。

[^1]: 根据Java之父James Gosling的解释,当时还定义了“CAFEDEAD”用作另一种对象持久化文件格式的 魔数,只是后来该格式被废弃掉了,所以并未流传开来。