linux服务器上jdk多版本共存需同时管控命令路径与java_home:用alternatives注册java和javac并同步设置java_home,确保java -version与$java_home版本一致,否则maven编译成功但运行报unsupportedclassversionerror。

Linux服务器上JDK多版本共存不是靠改JAVA_HOME硬切就能稳住的——java和javac命令本身可能仍指向旧版本,尤其在systemd服务、CI/CD脚本或后台进程里会出错。真正可靠的切换必须同时控制命令路径和环境变量,且要区分“系统级默认”和“当前会话临时切换”两种场景。
用alternatives注册所有JDK并统一管理命令
这是最接近“系统级生效”的方式,java和javac命令由/etc/alternatives/统一调度,不依赖JAVA_HOME,适合运维和部署环境。
- 每个JDK必须分别注册
java和javac两个替代项,路径不能写错:/usr/lib/jvm/jdk-17.0.3/bin/java≠/usr/lib/jvm/jdk-17.0.3/jre/bin/java - 优先级数字只影响
--auto模式下的默认选择,手动--config时无效;但建议按版本号升序设(如8→100、11→110、17→120),避免冲突 - 注册后必须执行
sudo alternatives --config java和sudo alternatives --config javac各选一次,否则/usr/bin/java仍可能软链到旧版本 - 如果之前用
apt装过OpenJDK,先运行sudo alternatives --remove-all java清空残留,否则--install会失败
JAVA_HOME必须与alternatives实际选中的版本严格一致
很多构建工具(Maven、Gradle)和Java服务(Tomcat、Spring Boot jar)直接读JAVA_HOME,它和java命令指向不同版本时,编译成功但运行报UnsupportedClassVersionError是高频问题。
- 不要在
/etc/environment里硬编码路径,改用动态赋值:在/etc/profile.d/java.sh里加export JAVA_HOME=$(readlink -f /usr/bin/java | sed 's:/bin/java$::') - 验证是否同步:
java -version和echo $JAVA_HOME输出的版本号必须完全一致(包括u号,如17.0.3) - systemd服务默认不加载用户profile,需在service文件里显式设置
Environment="JAVA_HOME=/usr/lib/jvm/jdk-17.0.3"
避免update-alternatives被包管理器覆盖
用apt或dnf升级OpenJDK时,系统可能自动重置alternatives配置,导致切换失效。
- 安装新版本后,立即重新运行
sudo alternatives --install注册,别依赖自动注册 - 禁用自动配置:Ubuntu/Debian下可
echo "java java 0 auto" | sudo tee /var/lib/dpkg/info/java.default,防止apt篡改 - 检查是否被覆盖:
sudo alternatives --display java输出里若出现auto字样,说明处于自动模式,应改为manual
手动解压版JDK的路径权限常被忽略
从Adoptium下载的tar.gz解压后,bin/目录默认无执行权限,alternatives注册会静默失败,java -version报Permission denied。
- 解压后立刻修复:
sudo chmod -R +x /usr/lib/jvm/jdk-21.0.1+12/bin/ - 确认所有者:普通用户解压的目录,
alternatives注册时需sudo,但最终运行时java进程应能以非root身份执行 - 路径中含空格或特殊字符(如
+、%)会导致alternatives解析失败,优先用短路径如/usr/lib/jvm/jdk21软链到真实目录
真正麻烦的不是注册几个版本,而是确保java命令、JAVA_HOME、构建工具、服务进程四者始终对齐——任何一环脱节,都会在凌晨三点的CI流水线里突然爆发。











