用户目录多版本 JDK 的部署与切换
在 Linux 开发环境中,不同仓库与构建链路常对 JDK 主版本提出互斥约束:既有仍绑定 8 或 11 的遗留工程,亦有以 17、21 乃至更新主版本为基线的服务。若完全依赖发行版软件源中的 OpenJDK,并借助 update-alternatives 做全局切换,则版本边界与系统升级周期耦合,且难以在同一用户会话策略下稳定复现「与 CI 或上游构建矩阵一致」的具体发行构建。
本文给出一种将多套 Oracle JDK(linux-x64)并列部署于用户目录、并以环境变量选择默认运行时的方案:各主版本解压至 ~/workspace/sdks/jdk-<主版本>,默认 JAVA_HOME 指向 JDK 17。该方法不修改系统级 Java 包,也不将 JDK 注册为系统 alternatives,从而把运行时生命周期收敛到可备份、可替换的目录树。
路径一律以 ~/workspace/sdks 或 $HOME/workspace/sdks 表述;将家目录根替换为其它前缀时,只需同步修改配置文件中的对应字段。安装包须选用 64 位发行物;误用 i586/i386 包时,在 amd64 主机上常表现为动态链接器缺失或「required file not found」,此类故障与配置语法无关,应在解压前以文件名与 file 输出加以排除。
方法
目录命名仅保留主版本号,不把补丁版本写入路径。如此,小版本升级可通过覆盖同一目录完成,而指向该路径的 JAVA_HOME 与文档表述保持稳定。当前采用的布局为:
text
~/workspace/sdks/jdk-8
~/workspace/sdks/jdk-17
~/workspace/sdks/jdk-21
~/workspace/sdks/jdk-25

解压前建议用 tar -tzf ... | head 确认包内顶层目录名,以免 mv 目标与实际不符。JDK 8 的顶层目录通常呈 jdk1.8.0_* 形态,与 17 及以后的 jdk-17.* 命名习惯不同,需按归档实际名称映射到 jdk-8。以 JDK 17 为例,完整过程如下(其它主版本仅替换压缩包、顶层目录与目标路径):
bash
mkdir -p ~/workspace/sdks
TMP=$(mktemp -d)
tar -xzf ~/Downloads/jdk-17.0.20.1_linux-x64_bin.tar.gz -C "$TMP"
mv "$TMP"/jdk-17.0.20.1 ~/workspace/sdks/jdk-17
rm -rf "$TMP"
默认运行时通过用户级环境片段声明。将下列内容写入 ~/.config/environment.d/java.conf,使图形登录会话能够读取默认 JAVA_HOME;同时在 ~/.profile 中 source 同一文件,使登录 shell 与终端环境与之对齐:
bash
JAVA_HOME=$HOME/workspace/sdks/jdk-17
PATH=${JAVA_HOME}/bin:${PATH}
bash
if [ -f "$HOME/.config/environment.d/java.conf" ]; then
set -a
. "$HOME/.config/environment.d/java.conf"
set +a
fi
其中 set -a 使随后载入的赋值自动导出,子进程可继承。PATH 将 ${JAVA_HOME}/bin 置于前部,以降低被 /usr/bin/java 抢先解析的概率。持久切换主版本时,仅修改 java.conf 中的目录后缀(例如改为 jdk-21),然后新开终端;若需桌面应用同步感知,通常需要重新登录。仅对当前 shell 生效的临时切换可采用 export JAVA_HOME=... 与相应的 PATH 调整,不写回配置文件。
验证
在新的登录上下文中执行:
bash
bash -l -c 'echo $JAVA_HOME; which java; java -version; javac -version'

判定标准包括:JAVA_HOME 以所选 jdk-* 目录结尾;which java 与 which javac 均落在该目录的 bin 下;版本字符串与主版本预期一致。若 java 可用而 javac 缺失,则表明解压内容不完整或误用了仅含 JRE 的布局,应重新核验归档。对多版本并存场景,可直接调用绝对路径(如 ~/workspace/sdks/jdk-8/bin/java -version)确认各树彼此独立、互不覆盖。
End
补丁级升级:在确认归档完整后,删除或覆盖对应 jdk-N 目录再解压,无需改动 JAVA_HOME。新增主版本:解压至新的 jdk-<N>,需要成为默认运行时时再改 java.conf。卸载某一主版本:删除对应目录,并确保任何仍引用该路径的配置或 IDE 设置已迁移。备份与迁移时,拷贝整个 ~/workspace/sdks/jdk-* 树及 java.conf、.profile 中的加载片段即可在另一主机复现相同默认值。
本方案将「默认 JDK」定义为环境变量语义,而非系统唯一安装。因此 Maven、Gradle 等尊重 JAVA_HOME 的构建工具会跟随会话配置变化;若工具链内部另行指定 toolchain,则以工具自身配置为准。已打开的终端不会自动重读 environment.d;部分由桌面启动的进程只在会话初始化时读取环境,修改后需新开会话。若存在 ~/.bash_profile 且未引用 ~/.profile,bash 登录可能跳过上述加载逻辑,应在 .bash_profile 中显式 source ~/.profile。此外,发行版仍可能通过软件包提供 /usr/bin/java;在 PATH 顺序正确的前提下其影响可被屏蔽,但在排查「版本不符合预期」时仍应同时检查 which -a java 与 JAVA_HOME。