ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

Linux系统安装JDK全攻略:版本选择、环境变量配置与多版本切换 1. 装 JDK 之前先把版本、发行版和安装方式这三件事定下来很多人搜“Linux系统安装JDK”一上来就开始复制命令结果装到一半发现装出来的版本不对或者装好了找不到 java最难受的是服务器上已经有一套 JDK 8项目又要求 JDK 17死活不敢动。其实 JDK 安装本身非常简单真正麻烦的是“装之前没想清楚装哪个”“装完之后不会切版本”。我就按平时帮同事处理问题的思路把这几个环节拆开讲。1.1 版本选择优先 LTS新项目闭眼选 17 或 21JDK 版本不是越新越好生产环境的核心原则是“选 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先更新索引然后安装 JDKsudo 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 还在用 yumCentOS 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-alternativesapt 和 dnf 安装 JDK 后系统会自动把java、javac注册到 alternatives 机制里。你可能会在系统里同时装多个版本比如先装了 openjdk-11再装 openjdk-17那默认java命令指向哪个版本由 alternatives 的优先级决定一般新装的包优先级更高。查看当前所有已注册的 JDKsudo 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 的选x64ARM 服务器的选aarch64。云上现在很多机器是 ARM 架构下错包会直接报cannot execute binary file: Exec format error。用下面这条命令确认架构uname -m3.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 与非登录 shellLinux 里有几个环境变量文件各自的生效范围完全不同/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 foundsudo: 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-17sudo 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-17current指向当前默认版本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 选的是 17Maven 的 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 17java -version也显示 17但某些项目跑起来还是老版本文本特征。原因通常是进程启动时没有用 PATH 里的 java而是用了另一个绝对路径。比如 Tomcat 的catalina.sh里能设JAVA_HOME会优先用你在这个脚本里指定的 JDKsystemd 服务里也可以写EnvironmentJAVA_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 环境问题都会从容很多。
返回列表