Ubuntu 20.04 apt安装Java:JDK 17安装与JAVA_HOME配置指南
1. 为什么在 Ubuntu 20.04 上用 apt 装 Java 不是“点几下鼠标”那么简单
很多人第一次在 Ubuntu 20.04 上装 Java,心里想的是:“不就是 sudo apt install default-jre 一行命令搞定?”结果敲完回车,系统报错 E: Unable to locate package default-jre ,或者装完一查 java -version ,显示的是 OpenJDK 11,而项目文档明确要求 JDK 17;又或者 javac 命令根本不存在,只装了 JRE 没装 JDK;更常见的是, JAVA_HOME 环境变量压根没设,后续跑 Maven、Gradle 或 Tomcat 全部报错——不是找不到 tools.jar ,就是提示 No Java runtime present 。
这些不是偶然,而是 Ubuntu 20.04 的 Java 生态设计逻辑决定的。它不像 Windows 那样提供一个“Java 官方安装包.exe”,也不像 macOS 用 Homebrew 默认拉最新版;Ubuntu 的 apt 仓库对 Java 的管理是 分版本、分用途、分维护主体 的:OpenJDK 是上游社区维护的开源实现,Canonical(Ubuntu 官方)只打包其中经过长期测试、满足 LTS(长期支持)策略的版本;而 Oracle JDK 自 2019 年起已停止免费商用,不再进入任何主流 Linux 发行版的默认仓库。所以当你执行 apt list --installed | grep java ,看到的不是“Java”,而是一长串带版本号和后缀的包名: openjdk-11-jre-headless 、 openjdk-17-jdk 、 java-common 、 ca-certificates-java ……它们各自承担不同职责,缺一不可,也绝不能随意混装。
我刚接手一个遗留 Spring Boot 2.5 项目时就踩过这个坑:运维同事说“Java 已装好”,我连上服务器一查, java -version 显示 11.0.11,但 mvn clean package 直接失败,报错 Unsupported class file major version 61 ——这是 JDK 17 编译的字节码,而运行环境只有 JDK 11。后来发现他只装了 default-jre ,连 JDK 都没装,更别说配置 JAVA_HOME 。这种“看似装了,实则废柴”的状态,在 Ubuntu 20.04 的 Java 部署中太常见了。它不是 apt 不好用,而是你没理解 apt 在这个发行版里对 Java 的“分治逻辑”:它把 Java 拆成了 运行时(JRE)、开发套件(JDK)、头文件(headless)、证书支持(ca-certificates-java)、基础元数据(java-common) 五个独立可选组件。你必须按需组合,而不是指望一个“万能包”。
这也是为什么网络热搜里总出现 sudo: apt: command not found 和 apt --fix-broken install ——前者往往是因为误删了 apt 本身(极罕见),更多是用户把 apt 和 apt-get 混淆,或在非 root 用户下漏了 sudo ;后者则几乎全是 Java 包依赖冲突导致的,比如你先装了 openjdk-11-jdk ,又手动下载 .deb 包强行装 openjdk-17-jdk ,两个版本的 java-common 元数据打架,apt 就会拒绝继续操作,直到你运行 sudo apt --fix-broken install 强制修复依赖树。这不是 bug,是 apt 的自我保护机制在起作用。
所以,这篇内容不教你怎么“一键安装”,而是带你 看清 Ubuntu 20.04 的 apt 仓库里 Java 的真实地图 :哪些包是必装的,哪些是可选的,哪些是陷阱,以及每一步操作背后,apt 究竟在帮你做什么、又在防止你做什么。你不需要记住所有命令,但需要建立一个判断框架:当项目要求“JDK 17”时,你知道该搜 openjdk-17-jdk 而不是 java-17-openjdk ;当 CI 流水线报 JAVA_HOME not set 时,你知道该去 /usr/lib/jvm/ 下找路径,而不是瞎猜 /opt/java ;当 sudo apt update 报 404 Not Found 时,你知道该检查 /etc/apt/sources.list 里是否还残留着已废弃的 old-releases.ubuntu.com 镜像源。这才是真正能让你在 Ubuntu 20.04 上稳定交付 Java 应用的底层能力。
1.1 Ubuntu 20.04 的 Java 仓库结构:三个层级与两个生命周期
要真正用好 apt 装 Java,你得先看懂它的仓库布局。Ubuntu 20.04(Focal Fossa)的官方软件源分为三个主要层级: main 、 universe 和 multiverse 。Java 相关包全部位于 universe 层级,这意味着它们 由社区维护,而非 Canonical 官方直接支持 。这直接决定了两点:第一,更新频率低于 main 中的核心系统组件;第二,版本选择更保守——只收录经过充分测试、满足 LTS 要求的 OpenJDK 版本。
具体到 Java,20.04 的 universe 仓库中,长期支持(LTS)版本只有两个: OpenJDK 11 和 OpenJDK 17 。前者是 Ubuntu 20.04 发布时的默认 JDK,后者是在 2021 年 10 月随 OpenJDK 17 GA(General Availability)发布后,被同步进 focal-updates 仓库的。注意,这里没有 OpenJDK 13、14、15、16——它们属于短期支持(STS)版本,Ubuntu 官方不会打包进任何 LTS 发行版的仓库,因为其生命周期太短(仅 6 个月),无法匹配 Ubuntu 20.04 长达 5 年的桌面支持期和 10 年的服务器支持期。
我们来实际验证一下。打开终端,执行:
apt update && apt list -a openjdk-*
你会看到类似这样的输出(截取关键部分):
Listing... Done
openjdk-11-jdk/focal-updates,now 11.0.22+8-0ubuntu1~20.04.1 amd64 [installed]
openjdk-11-jdk-headless/focal-updates,now 11.0.22+8-0ubuntu1~20.04.1 amd64 [installed,automatic]
openjdk-11-jre/focal-updates,now 11.0.22+8-0ubuntu1~20.04.1 amd64 [installed,automatic]
openjdk-11-jre-headless/focal-updates,now 11.0.22+8-0ubuntu1~20.04.1 amd64 [installed,automatic]
openjdk-17-jdk/focal-updates,now 17.0.10+7-0ubuntu1~20.04.1 amd64
openjdk-17-jdk-headless/focal-updates,now 17.0.10+7-0ubuntu1~20.04.1 amd64
openjdk-17-jre/focal-updates,now 17.0.10+7-0ubuntu1~20.04.1 amd64
openjdk-17-jre-headless/focal-updates,now 17.0.10+7-0ubuntu1~20.04.1 amd64
注意观察包名后缀:
-jdk:完整的 Java 开发工具包,包含javac、javadoc、jdb等编译和调试工具, 开发必备 。-jdk-headless:无图形界面的 JDK,不含 AWT/Swing 等 GUI 类库,体积更小, 服务器部署首选 。-jre:Java 运行时环境,仅含java命令和核心类库, 纯运行应用够用 。-jre-headless:无 GUI 的 JRE,最精简, 容器化或嵌入式场景常用 。
而 java-common 这个包,虽然名字不起眼,却是整个 Java 生态的“地基”。它不提供任何可执行命令,只负责管理 /usr/lib/jvm/ 目录结构、符号链接(如 /usr/bin/java 指向哪个具体版本)、以及 update-alternatives 的注册信息。如果你手动删除了 java-common ,再装任何 JDK, java -version 可能依然报错,因为符号链接断了。这就是为什么 apt --fix-broken install 经常能修好 Java 问题——它本质上是在重建 java-common 所维护的这套元数据关系。
再来看生命周期。OpenJDK 11 在 Ubuntu 20.04 中的生命周期,与其上游一致:Oracle 提供的 OpenJDK 11 LTS 支持到 2026 年 10 月,因此 Ubuntu 会持续为其提供安全更新,直到 2026 年。而 OpenJDK 17 的支持期到 2029 年 9 月,Ubuntu 也会同步跟进。这意味着,如果你今天在 20.04 上装 OpenJDK 17,它在未来 5 年内都会收到 Canonical 提供的安全补丁,无需升级操作系统。这正是 LTS 发行版的核心价值: 稳定,可预测,免于频繁迁移 。
提示:不要试图用
apt install oracle-java17-installer。这个包早已从universe仓库移除,因为它依赖的 Oracle JDK 二进制文件不再提供免费下载链接,apt 无法自动获取。强行添加第三方 PPA(如webupd8team/java)不仅违反 Ubuntu 官方安全策略,还会导致系统包管理器混乱,是生产环境的大忌。
1.2 “default-jre” 和 “default-jdk”:一个被严重误解的“快捷方式”
在大量中文教程里,你一定会看到这两行命令:
sudo apt install default-jre
sudo apt install default-jdk
它们看起来是“最简单、最官方”的安装方式。但真相是: default-* 系列包,是 Ubuntu 为新手准备的“概念性入口”,而非生产环境推荐方案 。它们的作用,是提供一个指向当前“默认”版本的符号链接,这个“默认”由 Canonical 的包维护者手动设定,并非技术最优解。
我们来拆解 default-jdk 到底是什么。执行:
apt show default-jdk
输出中关键一行是:
Depends: openjdk-11-jdk (>= 11.0.22+8-0ubuntu1~20.04.1)
看到了吗? default-jdk 本身不包含任何 Java 代码,它只是一个 元包(metapackage) ,唯一作用就是强制依赖 openjdk-11-jdk 。也就是说, sudo apt install default-jdk 的效果,等价于 sudo apt install openjdk-11-jdk 。它只是给你一个“不用记版本号”的心理安慰。
但问题来了:如果你的项目需要 JDK 17,而你装了 default-jdk ,那你就永远卡在 JDK 11。 default-jdk 不会自动升级到 JDK 17,除非 Canonical 的维护者手动修改其 Depends 字段,并发布新版本的 default-jdk 包——而这通常发生在下一个 Ubuntu LTS 版本(如 22.04)发布时。在 20.04 的生命周期内, default-jdk 永远指向 JDK 11。
更隐蔽的坑在于 update-alternatives 的优先级机制。当你同时装了 openjdk-11-jdk 和 openjdk-17-jdk ,系统会通过 update-alternatives --config java 来让你选择默认 java 命令指向哪个版本。但 default-jdk 的存在,会让 update-alternatives 的自动模式(auto mode)产生歧义。因为 default-jdk 注册的替代项(alternative)优先级是 1000,而 openjdk-11-jdk 是 1101, openjdk-17-jdk 是 1701。理论上,数字越大优先级越高, openjdk-17-jdk 应该胜出。但如果你先装 default-jdk (它拉了 JDK 11),再装 openjdk-17-jdk , update-alternatives 有时会因为元数据缓存问题,不自动切换,导致 java -version 仍显示 11。
我遇到过最典型的案例:一个团队的 CI 服务器,管理员为了“省事”统一装了 default-jdk ,结果所有构建都用 JDK 11。半年后项目升级到 Spring Boot 3,要求 JDK 17,他们花了两天时间排查,才发现 default-jdk 是罪魁祸首。最后解决方案不是卸载它,而是 显式指定版本安装,并手动配置 JAVA_HOME 。
所以,我的建议非常明确: 在生产环境、CI/CD 流水线、或任何需要版本确定性的场景,永远使用 openjdk-XX-jdk 的全名安装,彻底抛弃 default-* 系列包 。它带来的那点“便捷”,远不如版本失控带来的风险大。 default-jdk 的唯一合理使用场景,是给完全不懂 Java 的新用户做一次性的、临时的演示环境搭建——比如你在咖啡馆帮朋友快速跑一个 Hello World,用完即删。
注意:
default-jre同理,它只依赖openjdk-11-jre。如果你只需要运行一个.jar文件,且确认该文件是用 JDK 11 编译的,那它没问题;但一旦涉及编译、调试、或版本敏感的运行时(如某些 JNI 库),就必须用具体版本的 JDK。
2. 从零开始:四步完成 JDK 17 的完整安装与验证
现在,我们抛开所有“快捷方式”,用最清晰、最可控的方式,在 Ubuntu 20.04 上完成 OpenJDK 17 的安装。整个过程分为四个原子步骤: 环境预检 → 包安装 → 环境变量配置 → 全面验证 。每一步都不可跳过,且每一步都有其不可替代的技术目的。
2.1 步骤一:环境预检——为什么 sudo apt update 必须成功?
很多教程把 sudo apt update 当作一句“例行公事”的命令,一笔带过。但在 Java 安装前,这一步是 整个流程的基石和守门员 。它的作用,远不止是“刷新软件包列表”。
apt update 的核心任务,是下载并解析 /var/lib/apt/lists/ 目录下的所有 Packages 文件。这些文件是 Ubuntu 官方镜像站(如 archive.ubuntu.com )生成的索引数据库,里面精确记录了每个包的名称、版本、依赖关系、校验和(SHA256)、以及它在仓库中的物理路径( .deb 文件 URL)。当你执行 apt install openjdk-17-jdk 时,apt 不是凭空下载,而是先查这个索引,确认 openjdk-17-jdk 这个包是否存在、有哪些可用版本、它依赖哪些其他包(如 openjdk-17-jre-headless , ca-certificates-java ),然后才去镜像站拉取对应的 .deb 文件。
所以,如果 apt update 失败,原因只有一个: 你的系统无法访问或解析这些索引文件 。常见原因有三类:
- 网络连接问题 :公司防火墙屏蔽了
archive.ubuntu.com,或 DNS 解析失败。此时ping archive.ubuntu.com会超时,curl -I http://archive.ubuntu.com/ubuntu/dists/focal-updates/会返回Connection refused。 - 源地址失效 :Ubuntu 20.04 发布初期,部分国内镜像源(如
mirrors.ustc.edu.cn)的focal-updates分支尚未同步完成,导致apt update报404 Not Found。这在 2020 年很常见,现在基本已解决。 -
sources.list配置错误 :最致命的是,你可能手动修改过/etc/apt/sources.list,把focal-updates写成了focal-security,或者误加了old-releases.ubuntu.com(这是为已 EOL 的旧版 Ubuntu 准备的,20.04 还在支持期内,不该用)。
我曾在一个客户现场遇到过一个诡异问题: apt update 总是卡在 Reading package lists... 99% ,持续十分钟不动。排查发现,他们的 sources.list 里有一行:
deb http://archive.ubuntu.com/ubuntu/ focal main restricted universe multiverse
但缺少了 focal-updates 和 focal-security 这两行。 focal 主仓库只包含 20.04 发布时的初始版本(如 OpenJDK 11.0.7),而 openjdk-17-jdk 是后来发布的,必须在 focal-updates 仓库中才能找到。没有这行, apt update 虽然能完成,但 apt list openjdk-17-jdk 会返回 No packages found ,因为索引里根本没有这个包。
因此,预检的正确姿势是:
# 1. 检查网络连通性
ping -c 3 archive.ubuntu.com
# 2. 检查 sources.list 是否完整(关键!)
grep -E "focal-updates|focal-security" /etc/apt/sources.list
# 你应该看到类似这样的两行:
# deb http://archive.ubuntu.com/ubuntu/ focal-updates main restricted universe multiverse
# deb http://archive.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse
# 3. 执行 update 并观察输出
sudo apt update 2>&1 | grep -E "(openjdk|Hit|Ign|Err)"
如果输出中出现 Err:... 或 Failed to fetch ,说明源有问题,必须先修复。如果一切正常,最后一行应该是 Reading package lists... Done ,且中间有 Hit:... focal-updates 的提示,这就意味着 focal-updates 仓库已成功加载, openjdk-17-jdk 就在那里等着你。
提示:不要迷信“国内镜像源一定更快”。有些小众镜像源同步延迟高达 24 小时,可能导致你
apt update成功,却apt install失败(因为索引里有包名,但镜像站物理文件还没同步)。生产环境建议优先使用archive.ubuntu.com官方源,或security.ubuntu.com(专用于安全更新)。
2.2 步骤二:包安装—— openjdk-17-jdk 与 openjdk-17-jdk-headless 的抉择
现在, apt update 已确认成功,我们可以正式安装 JDK 了。命令很简单:
sudo apt install openjdk-17-jdk
但这里有一个关键决策点: 你是否需要 openjdk-17-jdk-headless ?
答案取决于你的使用场景。 -headless 后缀,直译是“无头”,在 Java 语境中特指 不包含图形用户界面(GUI)子系统的运行时 。它移除了 java.awt 、 javax.swing 、 java.applet 等所有与屏幕绘制、事件处理相关的类库。这带来了两个显著优势:
- 体积更小 :
openjdk-17-jdk完整包约 350MB,而openjdk-17-jdk-headless仅约 220MB。对于磁盘空间紧张的服务器或 Docker 镜像,这 130MB 的节省很可观。 - 启动更快、内存占用更低 :JVM 启动时无需初始化 AWT Toolkit、X11 连接等 GUI 相关模块,冷启动时间平均快 15%-20%,堆外内存(off-heap)占用减少约 8%。
那么,什么情况下你 不能 用 -headless ?只有两种:
- 你的 Java 应用本身需要 GUI :比如一个 Swing 桌面程序,或一个用 JavaFX 做前端的工具。但请注意,Ubuntu 20.04 的
openjdk-17-jdk默认 不包含 JavaFX ,它已被 Oracle 移出 OpenJDK 标准,需单独下载gluonhq/javafxSDK。所以即使你装了完整版 JDK,也未必能跑 JavaFX。 - 你依赖的某个第三方库,内部调用了 AWT 的 headful 功能 :最典型的是
java.awt.Toolkit.getDefaultToolkit().getScreenSize()这类方法。如果库作者没做headless兼容(即没加if (!GraphicsEnvironment.isHeadless()) { ... }判断),在-headless环境下会抛HeadlessException。
绝大多数后端服务、Web 应用(Spring Boot, Tomcat)、构建工具(Maven, Gradle)、数据处理(Spark, Flink)都 完全兼容 -headless 。它们从不碰 GUI API,只用 java.lang , java.util , java.net , java.nio 等核心包。
因此,我的生产环境推荐是: 无脑选择 openjdk-17-jdk-headless 。命令如下:
sudo apt install openjdk-17-jdk-headless
它会自动拉取所有必要依赖,包括 openjdk-17-jre-headless 、 ca-certificates-java 和 java-common 。你不需要单独装它们,apt 的依赖解析器会帮你搞定。
安装过程中,apt 会显示类似这样的依赖树:
The following additional packages will be installed:
ca-certificates-java java-common libasound2 libasound2-data libavahi-client3
libavahi-common3 libdbus-1-3 libfontconfig1 libfreetype6 libgcc-s1 libglib2.0-0
libgraphite2-3 libharfbuzz0b libjpeg-turbo8 libjpeg8 liblcms2-2 libnspr4 libnss3
libpcsclite1 libpng16-16 libstdc++6 libx11-6 libx11-data libxau6 libxcb1 libxdmcp6
libxext6 libxi6 libxrender1 libxtst6 openjdk-17-jre-headless tzdata-java
注意,这里没有 libx11-xcb1 、 libxrandr2 等典型的 X11 图形库——这正是 -headless 的体现。如果你看到这些包被列为“将被安装”,那说明你误装了完整版 openjdk-17-jdk 。
安装完成后,JDK 的文件被放在 /usr/lib/jvm/ 目录下。执行 ls -l /usr/lib/jvm/ ,你会看到:
drwxr-xr-x 7 root root 4096 Oct 12 10:23 java-1.17.0-openjdk-amd64/
lrwxrwxrwx 1 root root 25 Oct 12 10:23 java-17-openjdk-amd64 -> java-1.17.0-openjdk-amd64/
java-1.17.0-openjdk-amd64 是实际安装目录, java-17-openjdk-amd64 是一个符号链接,方便记忆。这个路径,就是下一步配置 JAVA_HOME 的关键。
提示:不要用
which java的结果来反推JAVA_HOME。which java返回的是/usr/bin/java,这是一个由update-alternatives管理的符号链接,它最终指向/etc/alternatives/java,再指向/usr/lib/jvm/java-1.17.0-openjdk-amd64/bin/java。绕这么多层,不如直接认准/usr/lib/jvm/java-1.17.0-openjdk-amd64这个物理路径,绝对可靠。
2.3 步骤三:环境变量配置—— JAVA_HOME 与 PATH 的黄金搭档
安装完 JDK, java 和 javac 命令已经可以全局使用了,但这只是“表面功夫”。真正的挑战在于 JAVA_HOME 环境变量的配置。它是 Java 生态的“心脏起搏器”,几乎所有基于 Java 的工具都依赖它:
- Maven :用它定位
tools.jar(虽然 JDK 9+ 已移除,但插件仍会读取)和jre/lib/rt.jar。 - Gradle :用它确定默认的 JVM 启动参数(如
-Xmx)和编译目标版本。 - Tomcat :用它设置
CATALINA_HOME和JRE_HOME,并加载tomcat-juli.jar。 - IDEA/VS Code :用它识别项目 SDK 和语言级别。
- Docker 构建 :
Dockerfile中ENV JAVA_HOME是构建多阶段镜像的基础。
所以,配置 JAVA_HOME 不是“可选项”,而是“必选项”。而且,它必须与 PATH 协同工作,形成闭环。
标准做法是编辑 /etc/environment (系统级,对所有用户生效)或 ~/.profile (用户级)。我强烈推荐 系统级配置 /etc/environment ,因为:
- 它在用户登录前就被 shell 读取,确保所有进程(包括 systemd 服务、cron 任务、GUI 应用)都能继承。
- 它不执行任何 shell 脚本,只做纯变量赋值,避免了
~/.bashrc中可能存在的语法错误导致整个 shell 启动失败的风险。
操作步骤:
# 1. 用 nano 编辑(或你喜欢的编辑器)
sudo nano /etc/environment
# 2. 在文件末尾添加两行(注意:不要加 export,/etc/environment 不是 shell 脚本)
JAVA_HOME="/usr/lib/jvm/java-1.17.0-openjdk-amd64"
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:$JAVA_HOME/bin"
# 3. 保存退出,然后让新环境变量立即生效(对当前会话)
source /etc/environment
# 4. 验证
echo $JAVA_HOME
# 输出应为:/usr/lib/jvm/java-1.17.0-openjdk-amd64
echo $PATH | tr ':' '\n' | grep jvm
# 输出应包含:/usr/lib/jvm/java-1.17.0-openjdk-amd64/bin
这里有个极易被忽略的细节: PATH 的赋值。很多教程写成 PATH="$JAVA_HOME/bin:$PATH" ,这看似正确,但会导致一个问题: /usr/bin 等系统路径被挤到后面,万一 JAVA_HOME/bin 下有个叫 ls 的恶意脚本(当然不可能,但原理如此),它就会被优先执行。所以, 最佳实践是把 $JAVA_HOME/bin 加在 PATH 的末尾 ,确保系统命令优先级最高,Java 工具次之。上面的写法 PATH="...:$JAVA_HOME/bin" 就是遵循此原则。
配置完成后,重启一个新终端,执行 env | grep JAVA ,你应该看到:
JAVA_HOME=/usr/lib/jvm/java-1.17.0-openjdk-amd64
如果没看到,说明 /etc/environment 没生效。常见原因是:你用的是 ~/.bashrc ,而 ~/.bashrc 默认不读取 /etc/environment (它只读取 /etc/profile )。此时,你需要在 ~/.bashrc 末尾添加:
# Load system environment variables
if [ -f /etc/environment ]; then
. /etc/environment
fi
然后 source ~/.bashrc 。
注意:
JAVA_HOME的值必须是 绝对路径,且不能有尾部斜杠 。写成/usr/lib/jvm/java-1.17.0-openjdk-amd64/(带/)会导致某些工具(如旧版 Ant)解析失败,报Invalid JAVA_HOME。
2.4 步骤四:全面验证——不只是 java -version
安装和配置完成后,别急着庆祝。真正的验证,是模拟一个真实的应用场景,用一套组合拳,确保每个环节都坚如磐石。我把它总结为“四验法”:
验一:基础命令与版本一致性
# 1. 检查 java 命令
java -version
# 输出应为:openjdk version "17.0.10" ...
# 2. 检查 javac 命令(证明 JDK 而非仅 JRE)
javac -version
# 输出应为:javac 17.0.10
# 3. 检查 JAVA_HOME 是否被正确读取
echo $JAVA_HOME
# 输出应为:/usr/lib/jvm/java-1.17.0-openjdk-amd64
# 4. 检查 java 命令的实际路径
readlink -f $(which java)
# 输出应为:/usr/lib/jvm/java-1.17.0-openjdk-amd64/bin/java
这四条命令,必须全部通过。特别是第 4 条 readlink -f ,它能穿透所有符号链接,直达物理文件。如果这里显示的是 /usr/lib/jvm/java-11-openjdk-amd64/bin/java ,说明 update-alternatives 没切过来,或者 PATH 里有别的 java 在前面。
验二: update-alternatives 状态检查
# 查看 java 的替代项配置
sudo update-alternatives --config java
# 查看 javac 的替代项配置
sudo update-alternatives --config javac
你应该看到类似这样的输出:
There are 2 choices for the alternative java (providing /usr/bin/java).
Selection Path Priority Status
------------------------------------------------------------
* 0 /usr/lib/jvm/java-17-openjdk-amd64/jre/bin/java 1701 auto mode
1 /usr/lib/jvm/java-11-openjdk-amd64/jre/bin/java 1101 manual mode
2 /usr/lib/jvm/java-17-openjdk-amd64/jre/bin/java 1701 manual mode
Press <enter> to keep the current choice[*], or type selection number:
注意 Priority 列: 1701 > 1101 ,所以 auto mode 会自动选 17。 * 号表示当前生效项。如果 * 在 11 那行,说明你需要手动输入 0 或 2 来切换。
验三:编译与运行一个真实 Java 类
这是最硬核的验证。创建一个最小可运行单元:
# 1. 创建测试目录
mkdir -p ~/java-test && cd ~/java-test
# 2. 创建 HelloWorld.java
cat > HelloWorld.java << 'EOF'
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello from OpenJDK 17 on Ubuntu 20.04!");
System.out.println("JAVA_HOME: " + System.getenv("JAVA_HOME"));
System.out.println("Java Version: " + System.getProperty("java.version"));
}
}
EOF
# 3. 编译
javac HelloWorld.java
# 4. 运行
java HelloWorld
预期输出:
Hello from OpenJDK 17 on Ubuntu 20.04!
JAVA_HOME: /usr/lib/jvm/java-1.17.0-openjdk-amd64
Java Version: 17.0.10
这个测试同时验证了:
javac能正常工作(JDK 安装正确)java能加载并执行 class 文件(JRE 运行时正确)JAVA_HOME环境变量在 JVM 进程内可被System.getenv()读取(环境变量透传成功)
验四:Maven/Gradle 兼容性(可选但强烈推荐)
如果你的项目要用 Maven,再加一验:
# 安装 Maven(如果还没装)
sudo apt install maven
# 创建一个最小 pom.xml
cat > pom.xml << 'EOF'
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>hello-world</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>
EOF
# 运行 mvn compile(它会触发下载依赖,但至少能走到编译阶段)
mvn compile -q 2>/dev/null && echo "✅ Maven compilation successful" || echo "❌ Maven failed"
如果输出 ✅ Maven compilation successful ,恭喜,你的 JDK 17 已经可以投入生产使用了。
3. 常见故障全景图:从 apt --fix-broken install 到 JAVA_HOME not set
在 Ubuntu 20.04 上装 Java,90% 的问题都集中在几个经典故障点。它们不是随机发生的,而是有清晰的因果链。下面,我以一个真实的排错日志为线索,带你走一遍完整的“故障定位-分析-修复”全流程。这个日志,来自一个 CI 服务器的构建失败报告:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project my-app: Fatal error compiling: invalid target release: 17 -> [Help 1]
...
[INFO] -------------------------------------------------------
[INFO更多推荐
所有评论(0)