主题
JDK 从 0 到 1 完整学习手册
学习目标:能用一句话向产品经理讲清楚「JDK 到底是个什么玩意儿」;能向面试官讲清楚「JDK / JRE / JVM 三者到底啥区别」;理解一份
.java源码是怎么被编译、装载、执行起来的;遇到JAVA_HOME 环境变量没配、版本不兼容、内存溢出这类常见问题不再两眼一抹黑。
0. 一张图看懂全文
你写的代码 JDK 帮你做的事 最终的样子
───────────── ───────────────── ─────────────
Hello.java ──── javac ────► Hello.class ──── java ────► 屏幕打印 Hello
(人能看懂) 编译器 (JVM 能看懂) 虚拟机 (程序运行起来)
字节码 bytecode📌 生活化类比:你写菜谱(
.java)是给中国厨师看的中文,但餐厅的机器人厨师(JVM)只认识一套国际通用的「图形菜谱」(.class字节码)。JDK 就是那本"中文菜谱 → 图形菜谱"的翻译手册 + 机器人厨师 + 一整套厨房工具的合集。
1. JDK 是什么:一句话讲清
JDK(Java Development Kit,Java 开发工具包)是 Oracle / OpenJDK 等组织发行的一整套"开发 + 运行 Java 程序"所需要的全家桶,包含编译器、运行时(JRE)、调试工具、文档工具和标准类库。
这句话拆开看:
| 词 | 含义 | 通俗类比 |
|---|---|---|
| Development Kit | 开发工具包 | 像「装修工具箱」,里面有锤子、电钻、卷尺、说明书 |
| 编译器(javac) | 把 .java 翻译成 .class | 「翻译官」 |
| 运行时(JRE + JVM) | 真正跑程序的环境 | 「执行命令的厨房」 |
| 调试工具(jdb / jps / jstack) | 排查问题用 | 「显微镜 + 听诊器」 |
| 标准类库(rt.jar / java.base 等) | 一堆别人写好的代码(String、List、HashMap……) | 「半成品冷柜」,拿来就能用 |
2. JDK / JRE / JVM 三者关系(面试高频)
这是 Java 入门第一个让人懵的概念。一张图一辈子记住:
┌────────────────────────────────────────────────────────┐
│ JDK │
│ (开发 + 运行的全家桶) │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ JRE │ │
│ │ (只能"运行"程序) │ │
│ │ │ │
│ │ ┌──────────────────────────────────┐ │ │
│ │ │ JVM │ │ │
│ │ │ (执行字节码的虚拟机) │ │ │
│ │ └──────────────────────────────────┘ │ │
│ │ │ │
│ │ + Java 标准类库(String/List/HashMap..) │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ + 开发工具:javac / javadoc / jar / jdb / jps ... │
└────────────────────────────────────────────────────────┘2.1 用一个外卖的故事讲明白
| 角色 | 对应 | 干什么 |
|---|---|---|
| JVM | 厨房的灶台 | 真正"做菜"的地方,把字节码烹熟变成程序运行 |
| JRE | 厨房(灶台 + 食材冷柜) | 灶台 + 一冷柜半成品(标准类库),能直接做现成的菜,但不能开发新菜 |
| JDK | 整个餐厅(厨房 + 研发部 + 工具间) | 既能做菜也能研发新菜,给开发者用 |
2.2 那我电脑上到底装哪个?
你的角色 需要装什么
───────────── ─────────────────
普通用户 JRE 就够(很多年前 Java 小程序时代)
(现在已经基本不再单独发行 JRE 了)
Java 开发者 JDK 必装
服务器跑 Java 程序 JDK 或 JRE 都行,但实际工作中
一律装 JDK(方便排查问题用 jstack/jstat)📌 重要事实:从 JDK 11 开始,Oracle 不再单独发布 JRE,JDK 自带运行能力。所以现在你只需要装 JDK 就够了。
3. JDK 简史与版本选择
3.1 30 秒版编程史
1995 ── Java 1.0 (Sun Microsystems) "一次编写,到处运行" ★ 时代开端
1998 ── Java 1.2 引入 Swing/Collections,史称 J2SE 1.2
2004 ── Java 5 ★ 泛型、注解、自动装箱、枚举、增强 for(划时代)
2006 ── Sun 把 JDK 开源,叫 OpenJDK
2010 ── Oracle 收购 Sun,Java 易主
2014 ── Java 8 ★★★ Lambda、Stream、Optional(至今最经典版本)
2017 ── Java 9 模块化 (JPMS),砍掉了一些祖传 API
2018 ── ★ Oracle 调整商业策略:Oracle JDK 商用收费,OpenJDK 免费
2018 ── Java 11 ★★ 第一个 LTS(长期支持),公司大量从 8 迁过来
2021 ── Java 17 ★★ LTS,Record / Sealed / 模式匹配 全面成熟
2023 ── Java 21 ★★★ LTS,虚拟线程(Loom)、模式匹配 GA,"现代 Java" 起点
2025 ── Java 25 LTS(每 2 年一个 LTS)3.2 LTS 是什么?
LTS = Long Term Support 长期支持版,可以理解为「官方承诺免费修 BUG 好多年」的稳定版。每 6 个月发一个新版本,但只有特定的几个版本是 LTS。
非 LTS 版本: 用了就像追新番,过半年就停更,bug 没人修
LTS 版本: 用了就像追长篇连载,连续好几年都有人维护
主流 LTS:8 → 11 → 17 → 21 → 25
(8 已停止免费商用支持,但是 2030 年前 OpenJDK 8 还有社区维护)📌 生活化类比:买手机你愿意买年度旗舰还是季度试验机?LTS 就是「年度旗舰」,公司生产环境一定挑 LTS 用。
3.3 该选哪个版本?给小白的「无脑公式」
新项目从零开始: 用 JDK 21(最新 LTS,未来 5 年不操心)
维护老项目: 看老项目本来用啥就用啥(一般是 8 / 11 / 17)
学 Android: JDK 17(Android Studio 现在自带 JBR 17)
学 Spring Boot 3.x: 必须 JDK 17+
公司服务器: JDK 17 或 JDK 21(看公司基础设施)3.4 JDK 厂商有哪些?该装哪个发行版?
很多人不知道:JDK 不止 Oracle 一家做,OpenJDK 是开源标准,每家公司基于它都可以编译自己的发行版。
| 发行版 | 厂商 | 收费? | 推荐场景 |
|---|---|---|---|
| Oracle JDK | Oracle | 商用要钱(JDK 17 以后又部分免费了) | 老项目存量 |
| OpenJDK | OpenJDK 社区 | 完全免费 | 通用 |
| Eclipse Temurin(前 AdoptOpenJDK) | Eclipse 基金会 | 完全免费 | 个人 / 公司首选 |
| Amazon Corretto | 亚马逊 | 完全免费 | AWS 用户 |
| Azul Zulu | Azul | 免费版 + 商用版 | 性能敏感场景 |
| JetBrains Runtime (JBR) | JetBrains | 免费 | IntelliJ / Android Studio 内置 |
| GraalVM | Oracle | 免费 | 想编译成本地可执行文件 |
📌 新手无脑选:直接装 Eclipse Temurin 最新 LTS(21)。不用纠结。
4. JDK 内部到底有些啥?
下载完一个 JDK,解压后是一坨文件夹。我们解剖一下(以 JDK 17+ 为例):
jdk-21/
├── bin/ ★ 所有可执行命令(javac、java、jar、jps...)
├── conf/ 配置文件(安全策略、日志)
├── include/ C/C++ 头文件(写 JNI 时要)
├── jmods/ 模块文件(JDK 9 模块化后的产物)
├── lib/ 底层依赖库(含一些 .so / .dll)
├── legal/ 各种开源协议
└── release 版本信息最有用的就是 bin/ 这一坨,挑几个真的会用到的命令讲讲:
| 命令 | 干啥的 | 通俗类比 |
|---|---|---|
javac | Java 编译器:.java → .class | 翻译官 |
java | 启动 JVM 运行字节码 | 厨师按菜谱做菜 |
jar | 把一堆 .class 打包成 .jar | 装快递箱 |
javadoc | 从注释生成 API 文档 | 自动写说明书 |
jps | 查看本机所有 Java 进程 | ps -ef | grep java 的 Java 专用版 |
jstack | 打印线程堆栈 | 给程序拍 X 光,查"卡在哪了" |
jmap | 导出堆内存快照 | 拍内存的 CT 片 |
jstat | 查看 GC 统计 | 看程序的"心电图" |
jcmd | 万能瑞士军刀(替代上面好多命令) | 多功能遥控器 |
jdb | 命令行调试器 | 老派调试工具,IDE 出来后基本不用 |
jshell | JDK 9 加的交互式 REPL | 像 Python 那样可以一行一行试代码 |
4.1 一定要学会的 3 个命令
① java -version —— 查当前用的什么版本
bash
$ java -version
openjdk version "21.0.2" 2024-01-16 LTS
OpenJDK Runtime Environment Temurin-21.0.2+13 (build 21.0.2+13-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.2+13 (build 21.0.2+13-LTS, mixed mode, sharing)② jps —— 看有哪些 Java 程序在跑
bash
$ jps -lv
12345 org.apache.catalina.startup.Bootstrap -Xmx2g
67890 com.example.MyApp -Xms512m③ jshell —— 不用建工程也能写 Java
bash
$ jshell
jshell> 1 + 1
$1 ==> 2
jshell> "hello".toUpperCase()
$2 ==> "HELLO"
jshell> /exit📌 生活化类比:以前学 Java 必须先写一个
class HelloWorld { public static void main... }框架代码才能跑一行。jshell出来后,终于跟 Python 一样想到啥就试啥,对小白超友好。
5. 装 JDK + 配环境变量
5.1 三步装好(以 macOS / Linux 为例)
bash
# 1. 下载(推荐 Temurin)
# 去 https://adoptium.net/ 下对应平台的 .tar.gz / .pkg / .msi
# 2. 解压(举例 Linux)
sudo tar -zxvf OpenJDK21U-jdk_x64_linux_hotspot.tar.gz -C /opt/
# 3. 配环境变量(写到 ~/.bashrc 或 ~/.zshrc)
export JAVA_HOME=/opt/jdk-21.0.2+13
export PATH=$JAVA_HOME/bin:$PATH
# 4. 立刻生效
source ~/.zshrc
# 5. 验证
java -version
javac -versionWindows 用户:装个 .msi 安装包,安装时勾选「设置 JAVA_HOME」就行;或者手动到「系统属性 → 环境变量」加 JAVA_HOME 和 PATH。
5.2 JAVA_HOME 到底是个啥?为什么要配?
JAVA_HOME = JDK 装在哪个文件夹的"地址簿"
PATH = 系统去哪儿找命令的"路径列表"📌 生活化类比:
JAVA_HOME像「Java 大本营在 X 路 Y 号」这块路牌PATH像「外卖小哥的送货地图」,告诉系统:「用户敲java命令时,到$JAVA_HOME/bin这个路径里去找」不配
JAVA_HOME,很多软件(Tomcat、Maven、Gradle、Android Studio)找不到 Java 装在哪里,就罢工。
5.3 多版本共存:jenv / sdkman(高级技巧)
工作中你可能同时维护 JDK 8 的老项目和 JDK 21 的新项目,怎么切换?
Linux / macOS 推荐 sdkman:
bash
# 装 sdkman
curl -s "https://get.sdkman.io" | bash
# 列出所有可装的 JDK
sdk list java
# 装一个
sdk install java 21.0.2-tem
sdk install java 17.0.10-tem
sdk install java 8.0.402-tem
# 切换
sdk use java 17.0.10-tem # 仅当前终端
sdk default java 21.0.2-tem # 全局默认📌 生活化类比:
sdkman像「JDK 版本的 nvm / pyenv」,手电筒一按就切灯泡,不用每次手改JAVA_HOME。
6. 一段代码完整跑起来:从源码到屏幕
让我们手把手把一个程序从代码到运行全程跟一遍,不用 IDE,纯命令行,这样你能看清 JDK 每一步在干嘛。
6.1 写代码
新建一个文件 Hello.java:
java
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, JDK!");
}
}6.2 编译
bash
$ javac Hello.java
# 没报错 = 成功,当前目录会多一个 Hello.class 文件
$ ls
Hello.java Hello.classHello.class 长这样(截取片段)—— 不是给人看的,是给 JVM 看的字节码:
ca fe ba be 00 00 00 41 00 1d 0a 00 02 00 03 07
00 04 0c 00 05 00 06 01 00 10 6a 61 76 61 2f 6c
...注意开头四个字节 CA FE BA BE —— 这是 Java class 文件的"魔数",类似身份证号,JVM 看到这四个字节才认它是合法字节码。
📌 生活化类比:
CA FE BA BE拼读起来像 "Cafe Babe(咖啡宝贝)",因为 Java 的 logo 就是一杯咖啡。这是 Sun 工程师当年埋的彩蛋,至今没改过。
6.3 运行
bash
$ java Hello # 注意没有 .class 后缀!
Hello, JDK!幕后发生了什么?三大步:
┌─────────────────────────────────────────────────────────────┐
│ Step 1:类加载器(ClassLoader)找到 Hello.class,读进内存 │
│ │
│ Step 2:字节码校验器(Verifier)检查这是合法字节码吗? │
│ 有没有"非法访问"、"栈溢出"等可疑动作? │
│ │
│ Step 3:执行引擎运行 main 方法 │
│ → 解释器逐条执行 / 热代码 JIT 编译为机器码 │
│ → CPU 执行机器码,屏幕上看到 "Hello, JDK!" │
└─────────────────────────────────────────────────────────────┘6.4 反编译看看 .class 里到底是啥(拓展)
JDK 自带 javap 工具:
bash
$ javap -c Hello
Compiled from "Hello.java"
public class Hello {
public Hello();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #13 // String Hello, JDK!
5: invokevirtual #15 // Method java/io/PrintStream.println:(...)V
8: return
}这些 aload_0 / invokevirtual 就是字节码指令,类似汇编但跨平台 —— 这正是 Java「Write Once, Run Anywhere(一次编写,到处运行)」的秘密。
📌 生活化类比:Windows 的
.exe像粤语,只有广东人听得懂;Linux 的 ELF 像川话,只有四川人听得懂。Java 字节码像「普通话」,全世界 JVM 都能听懂,所以你的程序拿到任何系统装上 JVM 就能跑。
7. JVM 工作原理(小白版)
JVM 是 JDK 里最神秘也最重要的部分。我们用「餐厅运营」的类比来理解。
7.1 JVM 的三大组件
┌────────────────────────────────────────────────────────┐
│ JVM │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 类加载子系统 │ │ 运行时数据区 │ │ 执行引擎 │ │
│ │ ClassLoader │ │ Runtime Data │ │ Execution │ │
│ │ │ │ Areas │ │ Engine │ │
│ └─────────────┘ └──────────────┘ └──────────────┘ │
│ 把 .class 装入 程序运行时的"内存" 解释 + JIT 编译 + GC│
└────────────────────────────────────────────────────────┘| 组件 | 干啥的 | 餐厅类比 |
|---|---|---|
| 类加载器 | 把 .class 从磁盘读进内存 | 「采购员」从仓库取食材 |
| 运行时数据区 | 划分各种内存区域 | 「厨房工作台」分蔬菜区、肉区、酱料区 |
| 执行引擎 | 真正执行字节码 | 「厨师」按菜谱做菜 |
7.2 运行时数据区:内存怎么划分
这是面试经典问题:「JVM 内存模型有哪几块?」
┌──────────────────────────────────────────────────────────┐
│ JVM 内存 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────────────┐ │
│ │ 程序计数器 │ │ Java 栈 │ │ Java 堆 │ │
│ │ 每线程私有 │ │ 每线程私有 │ │ 所有线程共享 │ │
│ │ │ │ │ │ ★ 对象都在这里 │ │
│ └──────────┘ └──────────┘ │ ★ GC 的主战场 │ │
│ ┌──────────┐ ┌──────────┐ │ │ │
│ │ 本地方法栈 │ │ 元空间 │ │ │ │
│ │ 每线程私有 │ │(方法区) │ │ │ │
│ │ │ │ 类元数据 │ │ │ │
│ └──────────┘ └──────────┘ └──────────────────────────┘ │
└──────────────────────────────────────────────────────────┘| 区域 | 存什么 | 通俗类比 | 出问题会咋样 |
|---|---|---|---|
| 程序计数器 PC | 当前线程执行到第几行字节码 | 看书时的"书签" | 几乎不会出错 |
| Java 栈 | 方法调用、局部变量 | 厨师做菜的"工序笔记本",做完一道菜撕一页 | StackOverflowError(递归太深) |
| 本地方法栈 | 调 C/C++ 代码(JNI)时用 | 餐厅请的"外援大厨"用的工作台 | 同上 |
| Java 堆 | 所有 new 出来的对象 | 餐厅的"成品冷柜",菜做好都放这 | OutOfMemoryError: Java heap space |
| 方法区 / 元空间 | 类的元信息、常量池、静态变量 | "菜谱图书馆" | OutOfMemoryError: Metaspace(动态加载太多类) |
📌 关键记忆:「对象在堆上,引用在栈上」。
比如
User u = new User();这行:
new User()创建的对象在堆里u这个变量名(指向那个对象的"快递单号")在栈里
7.3 GC:垃圾回收(最神奇的部分)
C/C++ 程序员要手动 malloc / free 内存,写错就内存泄漏。Java 不用,因为 JVM 会自动收垃圾。
啥是「垃圾」?
没有任何变量再指向它的对象 = 垃圾
java
User u = new User(); // 此时堆上有一个 User 对象,u 指向它,不是垃圾
u = null; // 没人指向它了 → 变成垃圾,下次 GC 时被回收GC 的两种基本算法
算法 1:标记-清除
──────────────────
先扫一遍,标记所有"还有人用"的对象 → 把没标记的扫掉
缺点:内存碎片化,像房间打扫完家具东一块西一块
算法 2:复制(Copying)
──────────────────
把内存切成 A / B 两半,活对象从 A 复制到 B,A 整个清空
缺点:浪费一半内存
现代 JVM 都是组合拳:分代 + 复制 + 标记整理「分代收集」是什么?
JVM 的工程师发现一个统计规律:「绝大部分对象创建后很快就死了」,比如临时变量。所以他们把堆分两块:
┌─────────────────────────────────────────────────────────┐
│ Java 堆 │
│ │
│ ┌────────────────────────┐ ┌──────────────────────┐ │
│ │ 新生代 (Young) │ │ 老年代 (Old) │ │
│ │ 小、扫得快、扫得勤 │ │ 大、扫得慢、扫得少 │ │
│ │ │ │ │ │
│ │ Eden + S0 + S1 │ │ 长寿对象的家 │ │
│ └────────────────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────┘
生命周期:
1. new 出来的对象先放 Eden 区
2. 第 1 次 GC 没死 → 挪到 S0
3. 第 2 次 GC 没死 → 挪到 S1
4. 反复 N 次都不死 → 升级到老年代("老人区")
5. 老年代满了 → Full GC(重量级回收,会"暂停整个程序")📌 生活化类比:JVM 的 GC 像幼儿园 + 养老院两套机制:
- 幼儿园(新生代)的小朋友很多,但流动性大,老师每天都要清点(Minor GC)
- 养老院(老年代)的老人少而稳定,几个月才大检查一次(Full GC)
把人按年龄分开管,效率比"全市统一普查"高得多。
常见垃圾收集器(了解即可)
| 收集器 | 出现年代 | 特点 | 适用 |
|---|---|---|---|
| Serial | 远古 | 单线程,简单 | 小程序 |
| Parallel | JDK 5 默认 | 多线程,吞吐优先 | 后台批处理 |
| CMS | JDK 5(已废弃) | 并发标记清除,低延迟 | 已退役 |
| G1 | JDK 7 引入 / JDK 9 默认 | 分区、可预测停顿 | 大堆通用首选 |
| ZGC | JDK 11 引入 | 超大堆 + 几乎不停顿(< 1ms) | TB 级堆 |
| Shenandoah | RedHat 出品 | 类似 ZGC | 同上 |
📌 小白不用记:现代 JDK 默认 G1,绝大部分情况不用调;遇到性能问题再说。
8. 类加载机制(面试常考)
8.1 加载过程
一个 .class 文件要变成 JVM 能跑的样子,要走 5 步:
┌───────┐ ┌───────┐ ┌───────┐ ┌──────────┐ ┌────────┐
│ 加载 │ → │ 验证 │ → │ 准备 │ → │ 解析 │ → │ 初始化 │
└───────┘ └───────┘ └───────┘ └──────────┘ └────────┘
读入字节 合法吗? 给静态变量 符号引用 执行
分配默认值 转直接引用 static 块8.2 双亲委派模型
JVM 不是"看到一个 .class 就直接加载",它有一套等级森严的加载顺序:
┌───────────────────────┐
│ 启动类加载器 │ ★ 最权威,只加载 java.* 这些核心
│ Bootstrap ClassLoader │
└───────────┬───────────┘
│
┌───────────▼───────────┐
│ 扩展类加载器 │ 加载 javax.* 等扩展库
│ Extension/Platform │
└───────────┬───────────┘
│
┌───────────▼───────────┐
│ 应用类加载器 │ 加载你写的代码(classpath)
│ Application │
└───────────────────────┘
工作流程:你想加载一个类
→ 先问爹(应用 → 扩展 → 启动):「你能加载吗?」
→ 爹不能加载,自己再尝试
→ 这样保证 java.lang.String 永远是 JDK 自带那个,
不会被你写的同名类替换掉(防恶意代码)📌 生活化类比:这套机制像家族式公司的请示流程 —— 任何决策先逐级上报到董事长(Bootstrap),董事长说"我管不了"才轮到下面办。这样保证核心规则永远由最高层制定,下面没法偷偷篡改。
9. 实战:常见问题排查工具箱
学这一节,你下次看到线上报错就不慌了。
9.1 程序卡死了,怎么看「卡在哪」?
bash
# 第 1 步:找到 Java 进程的 PID
$ jps -l
12345 com.example.MyApp
# 第 2 步:打印所有线程的堆栈
$ jstack 12345 > stack.txt
# 第 3 步:打开 stack.txt 找 BLOCKED / WAITING 的线程
# 通常一眼能看出"哪个线程卡在哪个方法"9.2 内存爆了:OutOfMemoryError
bash
# 第 1 步:启动 Java 时加参数,让它崩的时候自动 dump 内存
$ java -Xmx2g \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/tmp/heap.hprof \
MyApp
# 第 2 步:用 Eclipse MAT 或 VisualVM 打开 .hprof 文件
# 看「哪个对象占了大头」「谁在持有它不让 GC 收」9.3 想看 GC 日志
bash
# JDK 9+ 统一日志格式
$ java -Xlog:gc*:file=gc.log:time,uptime,level,tags MyApp
# 然后用 GCEasy.io(在线工具)上传 gc.log,自动出可视化报告9.4 常用启动参数速记表
| 参数 | 作用 | 例子 |
|---|---|---|
-Xms<n> | 初始堆大小 | -Xms512m |
-Xmx<n> | 最大堆大小 | -Xmx2g |
-Xss<n> | 单线程栈大小 | -Xss1m |
-XX:+PrintGCDetails | 打印 GC 详情 | (JDK 9 后用 -Xlog:gc*) |
-XX:+HeapDumpOnOutOfMemoryError | OOM 时自动 dump | 必加 |
-Dprop=val | 设系统属性 | -Dfile.encoding=UTF-8 |
📌 生产环境通用模板:
bashjava -Xms2g -Xmx2g \ -XX:+UseG1GC \ -XX:+HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath=/data/logs/heap.hprof \ -Xlog:gc*:file=/data/logs/gc.log:time,level,tags \ -jar myapp.jar
10. 常见疑惑 FAQ
Q1:我电脑上装了好几个 JDK,到底用的哪个?
bash
$ which java # 看 java 命令实际指向哪
$ java -version # 看版本
$ echo $JAVA_HOME # 看 JAVA_HOME 配的哪个如果 JAVA_HOME 配的版本和 java -version 不一样,是因为 PATH 里有别的版本排在前面。
Q2:JDK 8 和 JDK 17 能装在同一台电脑吗?
完全可以。 装在不同目录,用 sdkman / jenv 切换,或者每个项目用 JAVA_HOME 指向不同 JDK。
Q3:Maven / Gradle 用哪个 JDK?
它们用的是启动它们时的那个 JDK(即 java 命令对应的版本)。 项目可以单独指定编译版本(maven-compiler-plugin 的 source/target 或 Gradle 的 sourceCompatibility),但这只影响编译产物的字节码版本,不影响运行 Maven/Gradle 本身的 JDK。
Q4:为啥同一个 .class 拿去 JDK 8 跑就报 UnsupportedClassVersionError?
因为字节码有版本号。JDK 11 编译的 .class 默认版本是 55,JDK 8 的 JVM 只认 52 及以下。
解决:编译时降级
javac --release 8 Hello.java # JDK 9+ 推荐
javac -source 8 -target 8 Hello.java # 老写法📌 生活化类比:你用 Word 2021 存了
.docx,拿到 Word 2003 打不开。一样的道理。
Q5:为啥学 Kotlin / Scala / Groovy 也要装 JDK?
因为它们都编译成 JVM 字节码,最终都跑在 JVM 上,所以离不开 JDK。
Q6:JDK 和 OpenJDK 是同一个东西吗?
OpenJDK 是开源标准实现,所有发行版(Oracle JDK / Temurin / Corretto)都基于 OpenJDK,差别只在打包、商业支持、补丁速度。功能上 99.9% 一致。
11. 学习路线图
第 1 周:环境 + 基本概念(本文 1~6 章)
├─ 装好 Temurin JDK 21
├─ 跑通 Hello.java 命令行编译运行
└─ 理解 JDK / JRE / JVM 关系
第 2~4 周:Java 基础语法
├─ 变量、控制流、面向对象、集合、IO
└─ 推荐书:《On Java 8》中文叫《Java 编程思想 第 5 版》
第 5~8 周:Java 进阶
├─ 多线程、JUC 并发包
├─ 反射、注解、泛型
├─ Stream / Lambda / Optional
└─ 推荐:《Effective Java 第 3 版》
第 9~12 周:JVM 深度
├─ 内存模型、GC、类加载(本文 7~9 章 + 深挖)
├─ 用 JFR (Java Flight Recorder) 实战分析
└─ 推荐:《深入理解 Java 虚拟机 第 3 版》(周志明)
第 13 周以后:根据方向选择
├─ 后端 → Spring Boot / Spring Cloud
├─ Android → 跟 Kotlin 一起学
├─ 大数据 → Hadoop / Flink / Spark
└─ 中间件 → Netty / Dubbo12. 必读参考资源
| 类型 | 推荐 |
|---|---|
| 官网 | https://openjdk.org |
| 下载 | https://adoptium.net (Temurin) |
| 在线 API 文档 | https://docs.oracle.com/en/java/javase/21/docs/api/ |
| 入门书 | 《Head First Java》《On Java 8》 |
| JVM 圣经 | 《深入理解 Java 虚拟机》周志明 |
| 代码风格 | 《Effective Java 第 3 版》Joshua Bloch |
| JEP 索引(看新版本加了啥) | https://openjdk.org/jeps/0 |
13. 一句话总结
JDK 是 Java 世界的全套装备:JVM 是发动机,JRE 是发动机 + 油箱 + 标配工具,JDK 是 JRE + 整套维修工具。 普通用户用 JRE,开发者必装 JDK;想跑 Kotlin / Scala / Spring 的,统统离不开它。
学 JDK 不是学一门语言,是学一台「跨平台运行机器」是怎么诞生、怎么工作、怎么被排查问题的。理解了它,你写的每一行 Java / Kotlin 代码背后是什么样子,就再也不是黑盒了。