1016 字
约 3 分钟
5
S04-B01 · Java 的定位、版本与"一次编写"到底怎么实现

适用读者:刚上手 Java、或"用了几年但说不清字节码"的人。本系列第一篇。
前置:S01-B02(JDK 与环境变量)、S02-B01(分层思维)。
实测环境:OpenJDK 21.0.12.1 / Ubuntu 24.04.1(javac、javap 实测,2026-11-05)。
覆盖旧文:A-1836(176) A-1893(188) A-1640(40) = 404 点;A-1831(IDEA 安装)→ S01 duplicate_ref。
覆盖扩展:E-S04-001(JVM/字节码/编译期)、E-S04-002(LTS 与版本矩阵)。
下一篇:S04-B02 · 基本类型与装箱。


1. 一句话结论

"一次编写,到处运行"不是靠解释执行,而是靠"编译成中间语言(字节码)+ 每个平台各有一个 JVM"。

源码 .java ──javac──► 字节码 .class ──JVM(各平台各一份)──► 机器码(JIT 编译)
                                   │
                                   └─ 同一份 .class 在 Linux/Windows/macOS 都能跑
阶段 谁来做 产物
编译期 javac .class(字节码 + 常量池 + 符号引用)
类加载 ClassLoader 方法区中的运行时结构
运行期 JVM(解释 + JIT) 机器码(热点代码被编译)

2. 编译期发生了什么(实测)

$ javac Box.java
$ javap -c Box | head -20

实测输出(节选):

public static void main(java.lang.String[]);
    Code:
       0: bipush        127
       2: invokestatic  #7    // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       5: astore_1
       6: bipush        127
       8: invokestatic  #7    // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      12: sipush        128
      15: invokestatic  #7    // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

三条可以直接读出来的信息:

字节码 含义
bipush 127 / sipush 128 常量入栈(127 用单字节指令,128 需要双字节)
invokestatic Integer.valueOf 自动装箱发生在编译期插入的 valueOf 调用(B02 的关键)
astore_1 存到局部变量槽 1

这说明:"装箱"不是运行时的魔法,是编译器替你插了一段 valueOf。看懂这一点,B02 的 == 问题就不再玄学。


3. 版本:为什么必须谈 LTS(E-S04-002)

版本 状态 关键特性
8 老项目主力 Lambda、Stream、Optional
11 LTS var、标准 HTTP Client
17 LTS(推荐基线) sealed、record(预览转正)、强封装 JDK 内部
21 LTS(新项目推荐) 虚拟线程、switch 模式匹配、分代 ZGC
非 LTS(如 18/19/20) 6 个月即止 不要用于生产

判据:团队基线选 17 或 21;升不升看 §A01 的检查单(依赖兼容、构建工具、容器镜像)。


4. 一个最小可运行例子

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, S04");
    }
}
$ javac Hello.java && java Hello
Hello, S04
命令 作用
javac 编译(需要 JDK)
java 启动 JVM 并运行(只需要 JRE)
javap 反汇编字节码(排障神器)

"javac: command not found" 说明只有 JRE;"UnsupportedClassVersionError" 说明编译用高版本、运行用低版本(S01-B02 §2)。


5. 五个误区

误区 真相
"Java 是解释型语言" 先编译成字节码,热点再 JIT 编译成机器码
"字节码等于机器码" 字节码是 JVM 指令集,与 CPU 无关
"装 JRE 就能编译" 编译需要 JDK(含 javac)
"版本越高越好" 生产用 LTS;非 LTS 半年停更
"javap 只是炫技" 排查"为什么装箱/为什么这里有同步"时最直接

6. 自检

  1. "一次编写到处运行"依赖哪两件事?
  2. javap -c 里 invokestatic Integer.valueOf 说明了什么?
  3. JDK 与 JRE 的区别?只装 JRE 会缺什么?
  4. 为什么生产环境不建议用非 LTS 版本?
  5. UnsupportedClassVersionError 的根因是什么?

7. 下一篇

S04-B02 · 基本类型与装箱:== 什么时候会骗你

下一篇用实测解释"127 相等、128 不相等"的原因——以及 Integer.valueOf 的缓存在哪里。


本篇属于系列《S04 · Java 语言核心》。旧文来源:A-1836/A-1893/A-1640(A-1831 → S01 duplicate_ref);扩展点 E-S04-001/002。字节码输出为 OpenJDK 21 实测。

S04-B01 · Java 的定位、版本与"一次编写"到底怎么实现
https://www.clxhxhhr.top/posts/5216/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。