news 2026/10/3 2:58:29

Linux系统安装JDK全攻略:版本选择、环境变量配置与多版本切换

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux系统安装JDK全攻略:版本选择、环境变量配置与多版本切换

1. 装 JDK 之前,先把版本、发行版和安装方式这三件事定下来

很多人搜“Linux系统安装JDK”,一上来就开始复制命令,结果装到一半发现装出来的版本不对,或者装好了找不到 java,最难受的是服务器上已经有一套 JDK 8,项目又要求 JDK 17,死活不敢动。其实 JDK 安装本身非常简单,真正麻烦的是“装之前没想清楚装哪个”“装完之后不会切版本”。我就按平时帮同事处理问题的思路,把这几个环节拆开讲。

1.1 版本选择:优先 LTS,新项目闭眼选 17 或 21

JDK 版本不是越新越好,生产环境的核心原则是“选 LTS(长期支持)版本”。目前主流 LTS 是 JDK 8、11、17、21,短期版本像 18、19、20、22、23、24 这些不建议在生产环境用,因为官方支持周期太短,补丁跟不上,出了问题只能自己扛。

很多团队现在还踩在 JDK 8 上,这很正常,老项目依赖的第三方库不一定兼容新版,强行升版风险很大。但如果是新项目,我一般建议直接上 JDK 17 或者 21。JDK 17 是目前兼容性最均衡的版本,Spring Boot 3、主流中间件全都支持,而且比 8 和 11 多了很多语法和性能上的改进;JDK 21 则把虚拟线程做成了正式特性,适合做高并发服务端。我自己的服务器上现在默认就是 17,个别老项目单独锁 8,互不干扰。

这里也想回应一下热搜里那个“jdk降级到17”。不少朋友装了 JDK 25 甚至更高,结果项目一编译就报错,要么语法不兼容,要么字节码版本不被构建工具识别,最后又灰溜溜降回来。这不是你操作有问题,而是你用了一个还没被生态完全接纳的新版本。生产中不用追求最新,稳定优先,这一点一定要想明白。

1.2 发行版选择:OpenJDK、Temurin、Corretto 还是 Oracle JDK

第二个常见困惑是“到底该下哪个”。你打开搜索,出来的有 Oracle JDK、OpenJDK、Eclipse Temurin、Amazon Corretto、Zulu、Liberica 等等,名字一堆,其实绝大多数都是同一套源码编译出来的。Oracle JDK 在许可政策调整后,到了 JDK 17 其实也允许免费用于生产环境,但它的更新策略和商业支持方式仍然让很多人谨慎;OpenJDK 是各个发行版的基础,但“OpenJDK 官网”本身并不直接发放通用安装包,它只是一个代码仓库,所以新手在官网找不到下载入口,就会觉得“java 的 JDK 怎么那么难下载”。

这里我直接给出结论:服务器上不用纠结,选 Eclipse Temurin 就好,它就是原来的 AdoptOpenJDK,由 Eclipse 基金会维护,免费、开源、更新及时,社区用得最广。Amazon Corretto 也不错,特别是你跑在 AWS 上时,它经过 AWS 生产环境大量验证。Oracle JDK 适合需要 Oracle 官方技术支持的企业。你装的 OpenJDK 和你官网下载的 Oracle JDK,在日常业务开发上基本不会有感知差异。

1.3 安装方式:包管理器、tar.gz 压缩包、SDKMAN 各适合什么场景

Linux 下装 JDK 就三条路:

  • 包管理器安装(apt / dnf / yum):一条命令装完,系统自动帮你管好路径、注册到 alternatives,适合快速搭环境。缺点是仓库里的版本通常偏老,比如 Ubuntu 默认源里可能还是 openjdk-8 / openjdk-11,装不了最新,或者你想要的某个小版本根本不在源里。
  • tar.gz 手动解压安装:自己去下载 JDK 压缩包,解压到指定目录,配置环境变量。听起来麻烦,但这是最可控的方式,生产环境我基本都是这么装。因为你可以精确锁定版本,可以统一规划 JDK 放在哪个目录,以后升级、回滚都清楚。
  • SDKMAN 安装:类似 Node 的 nvm,一条命令装任意版本,随时切换。这个在开发机或个人服务器上非常舒服,缺点是不能完全自定义 JDK 目录,不适合需要严格管控的生产服务器。

我在下面的内容里会把这三条路都走一遍,重点讲讲为什么手动解压反而是最值得熟练掌握的方法,以及环境变量配置失败时应该怎么排查。

2. 包管理器安装实操:apt 和 dnf 两条完整命令链条

先说大多数人都能马上上手的包管理器方式。不管你是 Ubuntu / Debian 还是 CentOS / Rocky / Fedora,只要选对命令,几分钟就能装好。

2.1 Ubuntu / Debian 系列用 apt 安装 OpenJDK

先更新索引,然后安装 JDK:

sudo apt update sudo apt install openjdk-17-jdk -y

装完验证三件事:java 版本、javac 版本、java 实际所在的路径。

java -version javac -version readlink -f $(which java)

readlink -f $(which java)很关键,因为你直接用which java看到的往往是一个符号链接,并不是 JDK 真实路径。Ubuntu 上 apt 安装的 JDK 默认放在/usr/lib/jvm/下面,systemd 和很多脚本都会默认去这个目录找 JVM,这也是很多人配环境变量时 get 不到的点——手动解压时我通常不用这个目录,但 apt 装的时候系统已经帮你规划好了。

2.2 CentOS / Rocky / Fedora 系列用 dnf / yum 安装

CentOS 7 还在用 yum,CentOS 8 以上、Rocky Linux、AlmaLinux、Fedora 都用 dnf,命令参数基本一样:

sudo dnf install java-17-openjdk-devel -y

这里有个特别容易踩的坑:CentOS 上有个包叫java-17-openjdk,还有个包叫java-17-openjdk-devel。前者只带 JRE 和基础运行环境,没有 javac 编译器。如果你只是跑别人打好的 jar 包,装前者够了;但你本地要编译代码、要配 IDE、要看javac -version,必须装带-devel后缀的那个。我在给同事排查“java 能用,javac 不存在”这类问题时,十次里有九次是只装了不带 devel 的包。

2.3 包管理器安装的隐藏机制:update-alternatives

apt 和 dnf 安装 JDK 后,系统会自动把java、javac注册到 alternatives 机制里。你可能会在系统里同时装多个版本,比如先装了 openjdk-11,再装 openjdk-17,那默认java命令指向哪个版本?由 alternatives 的优先级决定,一般新装的包优先级更高。

查看当前所有已注册的 JDK:

sudo update-alternatives --config java sudo update-alternatives --config javac

进入交互界面,输入对应数字就能切换。这个机制很省心,但它只管java、javac这些系统命令,不会帮你改JAVA_HOME。很多 IDE 和构建工具(Maven、Gradle)启动时认的是JAVA_HOME,不是你 PATH 里的java。所以包管理器装完之后,你依然需要单独配置JAVA_HOME,这点别漏了。

包管理器方式的优点是好上手、卸载干净(直接sudo apt remove openjdk-17-jdk),缺点也非常明显:源里的版本滞后。Ubuntu 20.04 默认源里只有 JDK 8 和 11,你想装 17 得先加 PPA 或者换用手动安装;企业内网如果用的还是老版本系统,这个问题会更突出。所以真正靠谱的,还是手动解压安装。

3. 手动解压 tar.gz 安装,生产环境我最推荐的方案

手动安装步骤不多,但每一步都有讲究。下面我按生产环境标准来写。

3.1 JDK 安装包去哪里下载,以及为什么很多人觉得“下载难”

说句实话,JDK 下载之所以让很多人头疼,主要有三个原因:第一,Oracle JDK 的下载页面动不动要你登录账号,界面交互还绕;第二,OpenJDK 官网不直接提供安装包,你得找对构建商;第三,国内直连某些国外源速度很慢,下载到一半就断了。

我的习惯是去Adoptium(也就是 Eclipse Temurin 的官方发布站)下载,地址就是模型里能直接搜到的那几个常见镜像站之一。如果你是国内服务器,直接用华为云、阿里云、腾讯云的 JDK 镜像,速度快很多,文件名也都清晰标明了版本和架构。

下载前最重要的动作是确认架构:x86_64 的选x64,ARM 服务器的选aarch64。云上现在很多机器是 ARM 架构,下错包会直接报cannot execute binary file: Exec format error。用下面这条命令确认架构:

uname -m

3.2 解压与目录规划:为什么统一放 /usr/local/java

我习惯把所有 JDK 放在/usr/local/java下,比如:

sudo mkdir -p /usr/local/java sudo tar -zxvf jdk-17.0.10_linux-x64_bin.tar.gz -C /usr/local/java

解压完会生成一个类似jdk-17.0.10的目录。很多教程到这里就配环境变量了,但我还会多做一步——创建一个不带版本号的软链接:

sudo ln -s /usr/local/java/jdk-17.0.10 /usr/local/java/jdk-17

这样做的道理很简单:以后升级 JDK 小版本,只要解压新包,改一下软链接的指向,JAVA_HOME和 PATH 都不用动。如果你直接把JAVA_HOME写死到jdk-17.0.10,下次升到jdk-17.0.11就要改一堆配置文件,非常容易漏。这也是手动安装比包管理器更适合生产环境的原因——目录结构完全可控,升级回滚都靠软链接解决。

3.3 手动安装后的环境变量写入方式

这一步是全流程里最值得写清楚的部分。我见太多人直接改/etc/profile,然后 source 一下发现生效了,但换一个终端、换一个用户,又失效了,或者 SSH 登录后环境变量就是不对。

正确做法是在/etc/profile.d/下新建一个独立脚本。/etc/profile在被读取时会遍历/etc/profile.d/*.sh并执行,所以你在那里放脚本,效果等同于直接改/etc/profile,但互相隔离、便于管理:

sudo tee /etc/profile.d/java17.sh > /dev/null <<'EOF' export JAVA_HOME=/usr/local/java/jdk-17 export PATH=$JAVA_HOME/bin:$PATH EOF

写完后重新加载:

source /etc/profile.d/java17.sh

验证:

echo $JAVA_HOME java -version which java

这里有两个容易犯的错误。第一个:写完文件后直接在当前终端source /etc/profile,但当前终端不一定加载/etc/profile.d/java17.sh,要看你当前 shell 是谁、当时加载了哪些文件。第二个:有人把 PATH 配成了export PATH=$JAVA_HOME/bin,把原来 PATH 里的/usr/bin、/usr/local/bin全丢掉了,结果ls、vim等基础命令全部失效,看着像系统崩了一样。记住一定要在后面追加:$PATH。

4. JAVA_HOME 与环境变量配置失败,完整排查链路与根源分析

“jdk环境变量配置失败”能成为热搜,说明这个问题坑人太普遍了。我之前带过一个刚转行做运维的同事,他照着教程在/etc/profile里加了JAVA_HOME,然后自己开了一个新终端,echo $JAVA_HOME居然还是空的,整个人就懵了。这不是他操作不对,而是他弄混了这几个配置文件的加载顺序。

4.1 不同配置文件的生效范围:登录 shell 与非登录 shell

Linux 里有几个环境变量文件,各自的生效范围完全不同:

  • /etc/profile:系统级配置,登录 shell启动时读取。
  • /etc/profile.d/*.sh:被/etc/profile统一加载,同样只在登录 shell 生效。
  • /etc/environment:系统级环境变量,PAM 登录时就会读取,不区分 shell 类型,连图形界面登录也生效。
  • ~/.bashrc:非登录 shell(比如你在终端里再敲 bash 开一个子 shell,或者用 gnome-terminal 开的终端)读取。
  • ~/.bash_profile或~/.bash_login:交互式登录 shell读取,内容通常会在结尾去 source.bashrc。

看到没有,坑就藏在“登录”和“非登录”这两个词里。你用 SSH 登录服务器,是登录 shell,会读/etc/profile和~/.bash_profile;你在桌面系统里打开一个终端,这个终端默认是非登录 shell 模式,读的是~/.bashrc。所以你只改/etc/profile,在桌面终端里 source 一下,当前窗口可能有了,但重新打开终端又没了;反过来,你只改了~/.bashrc,用 SSH 登录时又看不到。

我的建议是:系统级 JDK 配置统一放/etc/profile.d/java.sh,用户级配置统一放~/.bashrc。这样不管哪种 shell 都能识别。生产服务器上我基本只用/etc/profile.d/这一种。

4.2 配置完成后不生效,按这个顺序逐层排查

如果你配置完了,echo $JAVA_HOME还是空的,按下面的顺序检查,基本上两三分钟就能定位:

第一步,确认文件内容写对了没有。直接查看文件:

cat /etc/profile.d/java17.sh

注意看有没有写错路径、有没有拼错 JAVA_HOME、PATH 那行有没有漏掉$PATH。顺便ls -l /usr/local/java/jdk-17确认软链接存在。

第二步,确认当前 shell 是否重新读取了配置。如果新开的终端还是不行,说明问题大概率在加载环节。手动执行一次:

source /etc/profile.d/java17.sh echo $JAVA_HOME

执行完有值,说明配置文件没问题,是 shell 没有自动加载。这种情况一般是你改的是/etc/profile但当前用户的 shell 是/bin/sh而不是 bash,或者你登录方式非常规(比如通过某些工具直接执行命令,没有走完整的 login shell 流程)。

第三步,排查是否被其他脚本覆盖。有时候你明明配好了,但~/.bashrc、~/.bash_profile或者系统其他初始化脚本里又有一个export JAVA_HOME=...指向旧路径,后执行的文件会把前面的覆盖掉。特别常见的是安转了一些软件后,软件的安装脚本自动往.bashrc里写了自己的 JAVA_HOME。对策是把你需要优先生效的配置放在最后面执行。

4.3 配置导致系统命令全部失效:PATH 覆盖事故处理

这个我亲自经历过的场景值得单独写一下。有人配 JDK 环境变量时写成了:

export PATH=$JAVA_HOME/bin

然后 source,结果终端突然什么都干不了:ls: command not found,sudo: command not found。原理很简单,PATH 被替换成了 JDK bin 目录,而那里没有ls、没有cat、没有sudo,系统命令全部找不到。

这时候千万别慌,也别去重启终端,因为你必须用绝对路径修复。/usr/bin/ls这种绝对路径不依赖 PATH:

/usr/bin/export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$JAVA_HOME/bin

然后把配置文件改回去就好。日常配置记得加上$PATH前缀,写成export PATH=$JAVA_HOME/bin:$PATH,把 JDK 的命令放在前面,这样java、javac会优先命中你自己装的版本。

5. 多版本 JDK 共存与降级切换:update-alternatives、软链接与项目层面的三板斧

环境变量配好只是第一步。真实工作中,一台服务器同时跑多个项目、多个 JDK 版本的情况太常见了。这里把我的切换思路完整讲一遍。

5.1 项目要求降级到 JDK 17,先判读这台机器属于哪种安装方式

“jdk降级到17”这个搜索词背后通常有两种场景:一种是用包管理器装了 JDK 21,项目报错想换 17;另一种是手动装了新版本,但构建工具还在找旧版本。处理方式取决于你最初是怎么装的。

  • 如果是包管理器安装的,直接使用update-alternatives --config java切换。
  • 如果是手动解压安装的,切换软链接指向就行。
  • 如果是 SDKMAN 管理的,直接sdk use java 17.0.10-tem,一条命令搞定。

所以我说安装方式直接影响后续管理成本,这也是为什么我在生产环境坚持手动安装并配合软链接。

5.2 包管理器场景:update-alternatives 切换默认 java 和 javac

假设系统里装了 openjdk-11 和 openjdk-17:

sudo update-alternatives --config java sudo update-alternatives --config javac

交互界面里选择你想要的版本编号即可。但这里要特别提醒,update-alternatives 不会同步改 JAVA_HOME。你切了 java 命令,可 Maven 里的 JAVA_HOME 还指向旧的,那就等于白切。正确姿势是两条腿走路:java命令用 alternatives 管,JAVA_HOME用/etc/profile.d/下的脚本管,两边的版本必须对得上。

5.3 手动安装场景:用软链接做版本切换,一分钟切完

手动安装多版本时,我建议这样规划目录:

/usr/local/java/jdk-8 /usr/local/java/jdk-11 /usr/local/java/jdk-17 /usr/local/java/jdk-21 /usr/local/java/current --> jdk-17

current指向当前默认版本,JAVA_HOME统一写成/usr/local/java/current。切换版本时,只需要:

sudo rm -f /usr/local/java/current sudo ln -s /usr/local/java/jdk-17 /usr/local/java/current

然后重新加载配置:

source /etc/profile.d/java17.sh java -version

这样 PATH 和 JAVA_HOME 永远不用改,切来切去就是一两条命令的事。项目要临时用 JDK 8 跑一个 jar,就先切到 8,跑完切回来,干净利落。

5.4 IDE 和构建工具层面的版本:IDEA、Maven、项目 pom 三个地方别打架

服务器配置搞定不等于开发机没问题。很多人配置 IDEA 时,Project SDK 选的是 17,Maven 的 JDK for importer 却是 11,项目编译就一直在报UnsupportedClassVersionError。这类报错的本质是:编译生成的字节码版本高于运行时 JVM 能识别的版本。class 文件有版本号,JVM 只能加载等于或低于自身版本号的文件。

开发机上我建议按这个顺序统一:先项目pom.xml里确认<java.version>,再 IDEA 的 Project Structure 里把 SDK 设为对应版本,最后 Maven Settings 里的 JDK 选同一套。三个地方不一致,构建工具会优先用 Maven 运行时指定的 JDK,而不是你在 IDEA 里选的 SDK。

6. 安装完必做的验证清单与几个高频报错速查

很多人装完java -version能出来就认为搞定了,其实还差得远。我列一个自己每次装 JDK 后都会跑的验证流程,照着走一遍能提前排除掉绝大多数坑。

6.1 从环境变量到实际编译,逐项确认

# 1. 确认 JAVA_HOME 指向有效路径 echo $JAVA_HOME # 2. 确认 PATH 里的 java 是你预期的版本 which java java -version # 3. 确认 javac 也正常 javac -version # 4. 写一个最简单的 HelloWorld,确认编译和运行链路都没问题 cd /tmp cat > Hello.java <<'EOF' public class Hello { public static void main(String[] args) { System.out.println("Hello JDK"); } } EOF javac Hello.java java Hello # 5. 确认 jps、jmap 等 JVM 工具可访问(排查问题时要经常用) jps # 6. 确认当前运行中的 Java 进程启动用的 JDK ps -ef | grep java

第 4 步是最容易被忽略的。有人 java 和 javac 都存在,但编译一个带中文注释的文件老是乱码,或者编译报“编码 GBK 的不可映射字符”,这时候还得注意代码文件的编码和编译器参数,纯命令行环境建议用-encoding UTF-8显式指定。

6.2 “java: command not found”与“Could not find or load main class”的区别

很多新手分不清这两个报错,排查方向会跑偏。

java: command not found是 shell 在 PATH 里根本没找到 java 可执行文件。原因要么是 PATH 没配好,要么是java所在目录不在 PATH 中。用echo $PATH看一眼前面 JDK 路径在不在里面;再用ls -l $JAVA_HOME/bin/java确认这个文件是否真实存在且可执行。有时/usr/local/java/jdk-17/bin/java的文件权限不对,执行位没有给到当前用户,也会出现奇怪的行为。

“Could not find or load main class Hello”则是 Java 运行时找不到你要执行的那个类。它跟 JDK 没任何关系,是你编译产物位置不对,或者 classpath 没指对。自己手动跑类时,正好当前目录运行java Hello,如果当前目录不在 classpath 里,把.加进去即可:java -cp . Hello。

6.3 装机常见的“两个版本”幻觉问题

还有一种情况,你明明切到了 JDK 17,java -version也显示 17,但某些项目跑起来还是老版本文本特征。原因通常是进程启动时没有用 PATH 里的 java,而是用了另一个绝对路径。比如 Tomcat 的catalina.sh里能设JAVA_HOME,会优先用你在这个脚本里指定的 JDK;systemd 服务里也可以写Environment=JAVA_HOME=。这种问“java 显示 17,项目为什么还是 JDK 8”的情况,十有八九就是启动脚本里写死了 JDK 路径。碰到时不要光盯着系统环境变量,把服务的启动脚本、systemd unit 文件都翻一遍。

最后分享点我自己的安装习惯

装了这么多年 JDK,我现在最常和同事重申的一点就是:安装本身不是重点,重点是你对它的管理方式。手动安装 + 软链接统一目录 +/etc/profile.d/隔离配置,这套组合让我在几十台服务器上从来没有为 JDK 切换发过愁。别人问我 JDK 装在哪里,我只有一个答案:看/usr/local/java/current指向哪里。别人问我怎么把 JDK 从 25 降到 17,我让他们看一眼软链接三秒钟内就解决了。

如果你刚开始接触 Linux 上的 JDK 安装,建议先把包管理器方式和手动解压方式各实践一遍,然后自己亲手制造一次环境变量配置失败,再按我上面的排查链路把它修好。这个过程走完,你再去应对任何一台 Linux 服务器上的 Java 环境问题,都会从容很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 2:58:11

DzzOffice集成OnlyOffice报错排查:从JWT到回调的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/3 2:56:58

为什么大厂API设计都在放弃PUT和DELETE?REST与POST之争

我第一次独立设计 API 的时候&#xff0c;是个教科书级信徒&#xff1a;用户更新用PUT /users/{id}&#xff0c;删除用DELETE /users/{id}&#xff0c;还在接口文档里煞有介事地标注了幂等性。结果联调第一天就被网关打回来了——运维丢给我一句话&#xff1a;"我们这只放…

作者头像 李华
网站建设 2026/10/3 2:55:08

课程答疑系统设计实战:SpringBoot+Vue+MyBatis全栈踩坑与优化

先说一个我观察到的现象&#xff1a;市面上的“课程答疑系统”绝大多数是拿论坛源码或工单系统改的&#xff0c;把发帖叫“提问”&#xff0c;把回帖叫“回答”&#xff0c;角色换一下就交差了。这东西不是不能用&#xff0c;但离真实的课堂场景差得远——没有课程归属、没有教…

作者头像 李华
网站建设 2026/10/3 2:54:49

SpringBoot酒店客房预订系统毕设:从选题到部署答辩的完整指南

本身是做毕设带学生的&#xff0c;每年springboot类题目占一半还多&#xff0c;酒店客房预订系统又是其中被点率最高的一个。这个题目看着简单&#xff0c;但真到答辩时能讲清楚的人不多——大部分人卡在同一个地方&#xff1a;系统能跑&#xff0c;但说不明白"为什么这么…

作者头像 李华
网站建设 2026/10/3 2:53:57

从零手写SysY编译器:西工大编译原理试点班大作业实战指南

简介&#xff1a;这份资源是西北工业大学编译原理试点班的大作业完整交付物&#xff0c;面向计算机、人工智能、通信工程等专业需要完成课程设计或毕业设计的学生&#xff0c;也适合想深入理解编译器构造的进阶学习者。核心内容是一个能够正常工作的Sysy语法编译器&#xff0c;…

作者头像 李华
网站建设 2026/10/3 2:53:48

华为云计算HCIE笔试v3.5变题解读与高效备考路线

华为云计算HCIE笔试升级v3.5的消息&#xff0c;这几天在备考群里确实把不少人炸出来了。各机构“变题通知”刷了一波又一波&#xff0c;但真正把“到底变什么、现在怎么备考、题库v3.0到底更新了啥”说清楚的内容并不多。我这段时间把新旧考纲、近期考生回忆、官方材料重新捋了…

作者头像 李华