Linux 安装 Oracle JDK
摘要
- 本文写 Oracle JDK 的发布节奏、LTS 的 NFTC / OTN 许可,以及 class 文件主版本号和 JDK 版本的对照。后半部分在 Linux 上用 tar.gz 安装 Oracle JDK,并把
JAVA_HOME指到/usr/local/jvm/jdk。 - 许可和支持期限按 2026-10-20 更新的许可 FAQ,以及 Oracle Java SE Support Roadmap。安装步骤按 Oracle JDK 25.0.5 的 Linux x64 Compressed Archive。
- Oracle Java SE Support Roadmap,Oracle Java SE Licensing FAQ,Java Downloads,Archived Oracle Java SE Releases。
发布节奏
正式环境用 LTS。本文安装的 25.0.5 仍在 NFTC 免费商用窗口内,这个窗口到 2028 年 9 月。
Oracle 当前的节奏:
-
每 6 个月一个功能版本,3 月和 9 月。功能版本同时带上当季的安全修复。
-
每 3 个月一次更新版本,1 月、4 月、7 月、10 月。
-
每 2 年一个 LTS,落在 9 月。已经发布的 LTS 是 8、11、17、21、25。下一个计划是 2027 年 9 月的 JDK 29,再下一个是 2029 年 9 月的 JDK 33。29 和 33 的日期 Oracle 标明可能调整。
非 LTS 在下一个功能版本发布后即被取代。它的 Premier Support 只覆盖到那时,没有 Extended Support。Oracle JDK 的非 LTS 在这大约 6 个月里按 NFTC。
发布日历见 Java Releases。
许可证
从 2021 年 9 月的 JDK 17 起,Oracle 对当时的当前 LTS 使用 NFTC(No-Fee Terms and Conditions)。NFTC 允许任何用户使用,包括商业和生产;可以再分发,再分发不能收费。
免费窗口收到下一个 LTS 发布之后的一年。按现在每两年一个 LTS 的节奏,这段时间是 3 年。窗口结束后的更新改为 OTN(Oracle Technology Network License)。OTN 允许个人使用、开发、测试、原型和演示。生产环境里的商业使用需要 Java SE Universal Subscription。
已经下载的那一版,继续按下载当时的许可证使用。换上窗口结束后的更新包,才适用 OTN。
Premier Support 和 Extended Support 是订阅用户还能拿到补丁的期限。它和 NFTC 的免费商用窗口是两件事。JDK 17 的免费商用收到 17.0.12,Extended Support 到 2029 年 9 月;到 2026 年 11 月,JDK 17 已经过了 Premier Support,后续补丁落在 Extended Support。
| 版本 | 发布 | NFTC 免费商用截止 | Premier Support | Extended Support |
|---|---|---|---|---|
| 8 | 2014-03 | 无。8u202(2019-01)及更早按当时的 BCL;8u211(2019-04)起按 OTN | 2022-03 | 2030-12 |
| 11 | 2018-09 | 无。Oracle JDK 11 全系列按 OTN。Oracle 构建的 OpenJDK 提供到 11.0.2(2019-01),许可证是 GPLv2+CPE | 2023-09 | 2032-01 |
| 17 | 2021-09 | 17.0.12 及更早(窗口到 2024-09)。17.0.13 起按 OTN | 2026-09 | 2029-09 |
| 21 | 2023-09 | 21.0.12 及更早(窗口到 2026-09)。21.0.13 起按 OTN | 2028-09 | 2031-09 |
| 25 | 2025-09 | 2028-09。按季度节奏,2028-07 的 25.0.12 仍在窗口内,2028-10 的更新起按 OTN | 2030-09 | 2033-09 |
| 29 | 2027-09(计划) | 2030-09(计划,下一个 LTS 是 2029-09 的 JDK 33) | 2032-09(计划) | 2035-09(计划) |
下载
当前版本和历史版本都在 Oracle 的下载页。本文用的是 JDK 25 的 Linux x64 Compressed Archive,文件名是 jdk-25.0.5_linux-x64_bin.tar.gz。aarch64 把文件名里的 x64 换成 aarch64,解压后的目录名不变。
同页上的 Oracle OpenJDK 是另一套构建,许可证是 GPLv2+CPE,不在下面的安装步骤里。
从 class 文件看编译版本
class 文件的主版本号等于 44 + 功能版本号。JDK 8 是 52,JDK 11 是 55,JDK 25 是 69。这个号是编译目标(--release / -target),记录的是这份字节码要求的最低 JDK。
本机有任意一个 JDK 的 javap 就能读。它只打印文件里的版本,不执行这个类。
1 | # 读一个 class 的主版本号 |
jar 里的 class 可以彼此版本不同。下面只查你点名的那一个类,类名换成 jar tf 列出的路径,并去掉 .class。
1 | jar tf app.jar | grep '\.class$' |
MANIFEST.MF 里的 Build-Jdk 或 Created-By 是打包时用的 JDK,和 class 的编译目标可以不同。war 的清单在包根上的 META-INF/MANIFEST.MF。
1 | unzip -p app.jar META-INF/MANIFEST.MF | grep -E '^(Build-Jdk|Created-By)' |
| 主版本号 | JDK |
|---|---|
| 45 | 1.1 |
| 46 | 1.2 |
| 47 | 1.3 |
| 48 | 1.4 |
| 49 | 1.5 |
| 50 | 1.6 |
| 51 | 1.7 |
| 52 | 8,LTS |
| 53 | 9 |
| 54 | 10 |
| 55 | 11,LTS |
| 56 | 12 |
| 57 | 13 |
| 58 | 14 |
| 59 | 15 |
| 60 | 16 |
| 61 | 17,LTS |
| 62 | 18 |
| 63 | 19 |
| 64 | 20 |
| 65 | 21,LTS |
| 66 | 22 |
| 67 | 23 |
| 68 | 24 |
| 69 | 25,LTS |
| 70 | 26 |
| 71 | 27 |
| 72 | 28 |
| 73 | 29,LTS,计划于 2027-09 |
安装
下面把发行目录固定在 /usr/local/jvm/jdk。tar.gz 解压出来的是带版本号的一层,例如 jdk-25.0.5。用符号链接指过去之后,升级时只改链接,JAVA_HOME 不用改。
1 | # 解压到 /usr/local/jvm。顶层目录名以 tar 实际解出的为准 |
/usr/local/jvm/jdk 已经是普通目录、并且里面直接有 bin/java 时,环境变量就用这个目录,下面的链接不用做。
解压出来的是 jdk-25.0.5 这一层时,再用符号链接固定路径。升级时只改链接。ln -sfn 的目标如果已经是普通目录,链接会进到该目录里面,所以这一步只在 /usr/local/jvm/jdk 不存在、或它本身已是符号链接时执行。
1 | # 目录名不同时,改成上面 ls 看到的那一层 |
能输出版本号,说明这个路径上的 java 可以执行。当前 shell 要等后面 source 之后,才会从 PATH 里找到它。
1 | /usr/local/jvm/jdk/bin/java -version |
25.0.5 的第一行类似 java version "25.0.5"。
环境变量
JAVA_HOME 指向发行目录本身,也就是里面直接有 bin/java 的那一层。PATH 把 $JAVA_HOME/bin 放在最前面,当前 shell 会先找到这一份。
$JAVA_HOME/bin 放在 PATH 最前面之后,会盖住系统里已经安装的 java。包管理器装的那一份还在,当前 shell 只是先用到 /usr/local/jvm/jdk。
所有用户的登录 shell
/etc/profile 在登录时 source /etc/profile.d/*.sh。这个文件被 source,需要可读。ssh host 命令、systemd、cron 不读它。
下面的 tee 会覆盖已有的 /etc/profile.d/jdk.sh。文件里如果写过别的变量,先把内容拷出来。
1 | # 单引号 EOF,避免当前 shell 把 $JAVA_HOME 提前展开 |
写入文件之后,已经打开的 shell 不会变。让当前这个登录 shell 读入:
1 | source /etc/profile.d/jdk.sh |
新开的登录 shell 会自己读 /etc/profile,不用再 source。
只给当前用户
交互式 bash 读 ~/.bashrc。只影响这一个用户时,把同样两行追加到这个文件。已经有这段时不要再追加,否则每次打开 shell,PATH 都会再被加上一遍。
1 | grep -q 'JAVA_HOME=/usr/local/jvm/jdk' ~/.bashrc || cat >> ~/.bashrc <<'EOF' |
/etc/profile.d/jdk.sh 和 ~/.bashrc 都设置了 JAVA_HOME 时,后被 source 的那个生效。登录 shell 先读 /etc/profile,再读用户的 bashrc。两处要写同一个路径。
确认已经生效
1 | echo "$JAVA_HOME" |
三者分别应是:
1 | /usr/local/jvm/jdk |
java -version 能跑,只说明 PATH 上有一个 java。echo "$JAVA_HOME" 为空时,依赖 JAVA_HOME 的程序仍然找不到安装目录。两条都要对。
升级
下一次更新把版本号换成新包的版本。新的 java -version 正确之后,再删上一版的目录。
1 | # 文件名和目录名换成这次下载的版本。25.0.6 是 25.0.5 的下一档更新 |
JAVA_HOME 仍是 /usr/local/jvm/jdk。已经打开的 shell 要重新 source,或者新开一个终端。hash -r 清掉 bash 记住的 java 路径。
换上 2028 年 10 月及以后的 JDK 25 更新之前,先看上一节的 OTN 窗口。那之后的包做商业生产,需要订阅。
卸掉
从 ~/.bashrc 里删掉 JAVA_HOME 和对应的 PATH 两行,再删系统文件、符号链接和解压目录。
1 | sudo rm -f /etc/profile.d/jdk.sh |
/usr/local/jvm/jdk 本身就是解压出来的目录时,先 ls -ld /usr/local/jvm/jdk 确认,再 sudo rm -rf /usr/local/jvm/jdk。
新开一个 shell,确认当前用户已经不再落到这份 JDK 上:
1 | hash -r |
JAVA_HOME 应为空。type -a java 如果还有输出,那是系统里另一份 JDK,和这次解压的目录无关。/usr/local/jvm 已经空了的话,可以 sudo rmdir /usr/local/jvm。
排错
版本仍是旧的时,先看当前 shell 到底找到哪一个文件:
1 | type -a java |
type -a java 的第一行应是 /usr/local/jvm/jdk/bin/java。第一行是别的路径时,$JAVA_HOME/bin 不在 PATH 前面,或者这个 shell 还没 source。bash 如果记住了旧路径,执行 hash -r 后再看 java -version。
ls 报没有 bin/java 时,JAVA_HOME 多指了一层,或少指了一层。它必须是 bin 的上一级。符号链接用 ls -ld /usr/local/jvm/jdk 看它指向哪里。
ls -l 里这个文件没有执行位时,当前用户跑不了它。发行包解压后,bin/java 一般是所有用户可执行。
systemd 服务、桌面启动器不读 /etc/profile.d。单元里写绝对路径,并单独给进程 JAVA_HOME:
1 | [Service] |