一份字节码,怎样跑遍三种系统?

把同一个 PlatformProbe.class 交给不同平台的 JVM,再逐项加入版本、JNI 与路径条件,观察“跨平台”在哪一步成立、又会在哪一步失效。

字节码转接台 bytecode lab

先做预测:如果 .class 文件完全相同,只更换目标操作系统,它一定能运行吗?答案取决于 JVM 版本,以及程序是否越过了 Java 世界与本机环境的边界。

源码 → 字节码 → 本机执行 同一 class 指纹:7E2A…C91F
  1. 01 · 源代码 PlatformProbe.java System.out.println(System.getProperty("os.name")); 文本
  2. 02 · JAVAC 编译与校验 javac --release 17 PlatformProbe.java 已编译
  3. 03 · CLASS 字节码 PlatformProbe.class major version 61 平台中立
  4. 04 · 目标 JVM Linux JVM · Java 21 验证 class 版本,解释或 JIT 编译字节码。 版本兼容
  5. 05 · 本机执行 Linux 机器指令 标准 API 与本机环境检查通过。 可以运行
关键转接:class 文件没有写死 Linux 指令;Linux JVM 把同一份字节码转换为当前机器能执行的形式。 Linux JVM

✓ 可以运行

目标 JVM 足够新,且没有不匹配的平台依赖。

    把同一份 class 交给三个 JVM

    字节码指纹与编译目标保持不变,只替换平台 JVM;依赖条件会按各平台重新判定。

    目标平台 收到的字节码 平台 JVM 的工作 最终结果

    相同的是字节码

    .class 遵循 JVM 规范,不直接保存 Windows、macOS 或 Linux 的机器指令。平台差异主要由各自的 JVM 实现承接。

    版本必须向后兼容

    较新的 JVM 通常能运行较旧的 class;较旧 JVM 读到更新的 class 版本,会在程序启动前抛出 UnsupportedClassVersionError

    Java 8
    52
    Java 11
    55
    Java 17
    61
    Java 21
    65

    依赖会越过边界

    JNI 库、固定绝对路径、外部命令、默认编码与文件权限都可能重新引入平台差异。“一次编译,到处运行”是一组条件,而不是无条件承诺。

    三问自检

    1. Java 21 编译出的 class,能直接交给 Java 17 JVM 吗?

    默认不能。Java 21 对应 class 主版本 65,而 Java 17 JVM 最高识别 61。可用支持的编译器通过 --release 17 生成较低目标版本,但代码也必须遵守 Java 17 的 API 与语言边界。

    2. 同一个 class 在三个平台都能启动,是否说明 JNI 库也能共用?

    不能。class 可以相同,但 JNI 背后的原生库必须分别编译为 Windows 的 .dll、macOS 的 .dylib 或 Linux 的 .so,并匹配 CPU 架构。

    3. 使用相对路径就一定不会出错吗?

    相对路径减少了路径语法和盘符绑定,但仍取决于当前工作目录、文件是否部署、大小写规则与权限。它提高可移植性,不等于资源必然存在。