搞Java的人估计没有谁敢说自己从没被JDK安装折腾过。尤其是在Linux服务器上没有图形界面没有一键安装向导全靠命令行很多新手第一次部署Java应用就卡在了“环境装不上”。其实Linux下安装JDK远没有想象中那么玄乎核心就三件事选对版本、选对安装方式、把环境变量配好。这篇文章我打算用一套从零开始的完整流程把Linux环境下JDK安装的细节全部展开一遍。不管你是在Ubuntu上学习Java编程还是要在一台CentOS服务器上部署生产项目或者被公司要求在一台机器上同时维护多套JDK版本这篇文章都能给你一个可以照搬的操作路径。有一点先说明白Linux环境下的JDK安装不是“能敲完命令就行”的事。工作中很多坑都是因为没搞清楚系统版本、CPU架构、发行版包管理机制之间的差异导致装完以后java -version报错或者编译时就找不到javac。我下面会把不同场景下的选择逻辑和操作细节都讲透然后给出一套我自己在生产环境常用的手动安装流程外加多版本切换和排错实录尽量让你少走弯路。1. 安装前的准备工作别急着敲命令1.1 先搞清楚你要装哪个JDK我见过太多人一上来就执行apt install default-jdk装完才发现版本不对然后整个环境被改得乱七八糟。JDK的发行版非常多OpenJDK、Oracle JDK、Eclipse Temurin、Azul Zulu、Amazon Corretto、Alibaba Dragonwell光名字就能看花眼。但对大多数人来说只需要在Oracle JDK和OpenJDK之间做选择。Oracle JDK从Java 11开始修改了许可协议在生产环境商用需要购买授权除非你确定符合官网免费条款。OpenJDK则是完全免费的社区开源版本绝大部分Linux发行版内置的就是OpenJDK功能和Oracle JDK几乎一样。对普通开发者和中小团队来说直接用OpenJDK完全够用没必要给自己惹授权上的麻烦。至于Eclipse Temurin、Zulu、Corretto这些本质都是OpenJDK的下游构建版本区别在于由谁维护、更新节奏不同。你可以把它们理解成同样的菜谱不同的厨师炒出来营养差不多口味略有差别。如果公司没有指定任意选一个靠谱的长期支持版就行。版本选择上我的建议是优先用LTSLong Term Support版本。目前Java社区最常见的LTS是Java 8、11、17和21。Java 8虽然是老古董但很多历史遗留项目还在用它Java 17是当前主流Spring Boot 3和很多新框架都要求17起步Java 21是最新一代LTS引入了虚拟线程等特性想尝鲜也可以。如果你拿不定主意就选JDK 17这是目前兼容性和性能平衡最好的选择。1.2 确认你的Linux版本和CPU架构同一套JDK的安装包x86_64平台的版本和ARM平台aarch64的版本是不通用的。当年我在树莓派上装了个x86的JDK包解压后执行java直接提示No such file or directory最后才发现原因。所以在下载之前先用下面两条命令确认系统基础信息cat /etc/os-release uname -mcat /etc/os-release会告诉你这是Debian系、RHEL系还是其他发行版uname -m会输出机器的CPU架构。如果是x86_64就选linux-x64的包如果是aarch64就选linux-aarch64的包。搞清楚发行版同样重要因为这会决定你用什么包管理器、环境变量文件从哪里加载。Debian/Ubuntu/Kali用aptRed Hat/CentOS/Fedora用yum或dnfopenSUSE用zypperArch用pacman。你如果用的是国内常见的国产Linux发行版比如基于Debian或RHEL的那些操作命令基本和对应系一致。这个判断直接影响下一步选择安装方式所以别偷懒。1.3 检查系统里有没有装过JDK在动手之前先看看系统里有没有已经存在的Java环境。特别是接手一台二手服务器时很多人可能已经装过其他版本了。用这几条命令检查which java java -version echo $JAVA_HOME如果which java有输出说明系统里已经有Java命令了如果java -version报错可能是命令存在但依赖损坏也可能是环境变量没配置好。这时候别急着卸载先看它是什么版本。有些系统软件包比如LibreOffice或者一些系统工具会依赖默认的OpenJDK你直接卸载它可能把别的软件也带崩。正确的做法是保留系统自带的版本自己再安装一份需要的JDK然后用环境变量或alternatives机制指定使用哪一份。这样互不干扰出现也能随时回滚。2. JDK安装的几种主流方式及对比2.1 包管理器安装最快但最不自由用系统包管理器安装JDK是很多Linux新手的第一反应。操作确实简单Debian系一行命令sudo apt update sudo apt install openjdk-17-jdk -yRHEL系则用sudo yum install java-17-openjdk-devel -y包管理器会自动处理依赖把JDK安装到/usr/lib/jvm目录下并帮你注册好基本的命令链接。这种方式的优点是快、稳、卸载时也比较干净一个apt remove就完事。但缺点同样明显你能装到的版本受系统软件源限制比如Ubuntu 20.04默认源里的openjdk-17版本号可能不是最新的甚至有可能只有openjdk-11。如果你想在同一台机器上放JDK 8和JDK 17两套环境并随意切换用包管理器就会很别扭因为每次切版本都要动全局配置。所以我一般只在临时测试环境或者单纯为了跑一个脚本时用包管理器方案。2.2 手动解压TAR包安装最推荐的生产环境方案所谓TAR包安装就是从官方下载tar.gz压缩包手动解压到指定目录再自己配置环境变量。这个流程看起来比包管理器多几步但自由度是最高的也是我在生产环境中最常用的方式。原因很简单版本完全由自己选目录由自己定想装几个版本就装几个版本互不干扰。卸载时更爽直接rm -rf目录、删掉环境变量配置系统里不留任何包管理器的痕迹。而且tar包安装不会污染系统的包管理器。很多人担心“手动装是不是不专业”其实恰恰相反在需要精细控制的服务器环境中手动解压安装反而更容易管理。比如可以把JDK统一放到/usr/local/java下以后升级、切换版本、排查问题都有迹可循。缺点也很明确环境变量需要自己配置下载时要注意包架构以及解压后的目录名里带版本号方便识别。2.3 使用deb或rpm安装包介于两者之间很多JDK发行商除了提供tar包还会提供deb和rpm格式的安装包。安装命令如下Debian系用dpkgRHEL系用rpmsudo dpkg -i jdk-17_linux-x64_bin.deb sudo rpm -ivh jdk-17_linux-x64_bin.rpm安装之后系统会把请求注册到alternatives机制Debian系叫update-alternativesRHEL系叫alternatives之后你可以用update-alternatives --config java在已安装的JDK之间切换。这种方式体验上和包管理器很接近但版本一般比系统软件源中的新安装包也保留了官方构建时的目录结构。缺点是路径不可控你很难自定义JDK安装位置而且deb/rpm包里的安装路径在不同发行商之间可能不一致排查时看它实际装到哪里会有一定的迷惑性。通常适用于公司要求用标准包分发软件的场景。2.4 三种方式对比速查表为了方便决策我把三种方式放在一起对比安装方式推荐场景优点缺点难易程度包管理器临时环境、快速验证命令简单、卸载干净版本受限、路径不可控低TAR压缩包生产环境、多版本并存灵活可控、版本任选需要手动配置环境变量中deb/rpm包系统级标准分发自动注册alternatives路径固定、打包版本依赖中如果是个人开发机哪种方式顺手就用哪种如果是公司服务器我建议团队内部统一选一种方式并约定好JDK安装目录避免不同人用不同方式装了一堆版本最后互相干扰。3. 一步步实操从下载到部署JDK 173.1 下载正式版JDK并校验文件完整性这一步是整个流程的起点也是很多人懒得认真对待的地方。推荐从官方或可信镜像下载我用的是AdoptiumEclipse Temurin的版本因为它是纯开源的LTS版本齐全下载比较稳定。当然你也可以从Oracle官网的Java Downloads页面下载只是有些版本需要注册或登录。这里以JDK 17的tar.gz包为例cd /tmp wget https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.12%2B7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.12_7.tar.gz请务必根据你的CPU架构替换x64为aarch64。下载完成后优先做一件事校验SHA256。命令如下sha256sum OpenJDK17U-jdk_x64_linux_hotspot_17.0.12_7.tar.gz将输出的哈希值和你下载页面上的SHA256摘要比对一致再继续。别觉得这一步多余我至少碰到过两次因网络问题导致压缩包下载不完整或者文件损坏的情况解压的时候各种报错回头排查了半天才发现是包的问题。先校验能省掉后面一大堆麻烦。如果你在国内觉得GitHub下载太慢可以换国内镜像。阿里云、华为云、清华源都提供JDK的镜像把下载地址换成镜像对应路径就行。无论从哪下校验这一步不能少。3.2 解压并移动到统一安装目录下载完成且校验通过后开始解压tar -xzf OpenJDK17U-jdk_x64_linux_hotspot_17.0.12_7.tar.gz解压后会得到一个jdk-17.0.127目录。我习惯把JDK统一放在/usr/local/java目录下这样以后所有JDK都在同一个地方环境变量好写管理也清晰sudo mkdir -p /usr/local/java sudo mv jdk-17.0.127 /usr/local/java/接着确认一下目录权限。tar包解压出来的文件权限一般是对的但为了保证所有用户都能执行可以顺手加一下sudo chmod -R 755 /usr/local/java/jdk-17.0.127这里有个需要特别注意的细节千万别把JDK解压到带空格或中文的路径下比如/home/user/我的 java。JDK里很多脚本和工具在解析路径时遇到空格和中文极容易出现诡异的报错这种问题很隐蔽排查起来特别花时间。统一放/usr/local/java是最稳的选择。3.3 配置JAVA_HOME与PATH环境变量环境变量配置是JDK安装过程中最容易出错的环节。网上很多教程直接让你打开/etc/profile去改但改动系统全局文件风险很高一个手误就可能让整个shell瘫痪。我的建议是在/etc/profile.d/目录下新建一个独立的脚本文件比如java.shsudo vim /etc/profile.d/java.sh写入以下内容export JAVA_HOME/usr/local/java/jdk-17.0.127 export PATH$JAVA_HOME/bin:$PATH保存退出后让当前会话立即生效source /etc/profile.d/java.sh为什么推荐写在这里/etc/profile.d/下的所有.sh文件会在系统用户登录时被/etc/profile循环加载把JDK配置单独放一个文件不但不会干扰原有系统配置排错时还能单独检查、单独移除。PATH那句的含义很好理解把$JAVA_HOME/bin放在PATH的最前面这样Linux执行java命令时会优先从JDK目录中找到它而不是去/usr/bin里找系统自带的旧版本。如果你在网上看到一些教程让你配置CLASSPATH变量那多半是Java 8之前的做法了。现代Java编译器会自动寻找依赖手动设置CLASSPATH反而容易因路径写错引起冲突新项目尽量不写了。3.4 验证安装结果配置完环境变量最后一步就是验证。用这三条命令java -version javac -version echo $JAVA_HOME正常输出大致如下openjdk version 17.0.12 2024-07-16 OpenJDK Runtime Environment Temurin-17.0.127 (build 17.0.127) OpenJDK 64-Bit Server VM Temurin-17.0.127 (build 17.0.127, mixed mode, sharing)如果你看到输出的是java version 1.8.0_xxx说明当前PATH中生效的还是系统里的老JDK。不要慌先检查/etc/profile.d/java.sh里的路径是否正确然后重新执行source。如果which java显示的路径不是你刚设置的JDK路径可以手动清理PATH里的旧目录或者把JAVA_HOME/bin的顺序提到最前面。总之只要java -version的版本号和javac -version一致JAVA_HOME也输出正确这一步就算完成了。4. 多版本JDK共存与切换4.1 为什么要装多个JDK怎么共存很多公司现在的常态是一个老项目还跑在JDK 8上新项目却要求JDK 17。你不可能经常把服务器系统重装一遍所以一台Linux机器上同时存在多个JDK版本是很务实的需求。多版本共存的核心思路就四个字目录隔离。把每个版本的JDK解压到不同目录用环境变量或系统工具去决定当前默认用哪个。比如我在/usr/local/java目录下同时放了两套/usr/local/java/ ├── jdk-17.0.127 └── jdk8显然JAVA_HOME只有一个默认指向17。如果某个终端想临时使用8直接在当前shell里执行export JAVA_HOME/usr/local/java/jdk8 export PATH$JAVA_HOME/bin:$PATH java -version这样当前终端就会变成JDK 8但其他终端不受影响。不过这种生效方式只对当前shell有效新开终端又会回到17。若要让切换“记住”就需要借助工具或脚本。4.2 使用update-alternatives管理默认版本Debian/Ubuntu系统自带update-alternatives工具专门用于维护命令链接的切换。你手动解压的JDK不会被自动注册但我们可以手动注册。假设要注册JDK 8和JDK 17的java、javac命令可以这样执行sudo update-alternatives --install /usr/bin/java java /usr/local/java/jdk8/bin/java 1 sudo update-alternatives --install /usr/bin/java java /usr/local/java/jdk-17.0.127/bin/java 2 sudo update-alternatives --install /usr/bin/javac javac /usr/local/java/jdk8/bin/javac 1 sudo update-alternatives --install /usr/bin/javac javac /usr/local/java/jdk-17.0.127/bin/javac 2命令末尾的1和2是优先级数字越大优先级越高。配置好之后随时运行交互式切换sudo update-alternatives --config java系统会列出所有已注册的Java版本输入编号即可切换。如果你只想让某个命令固定指向特定版本也可以用--set参数sudo update-alternatives --set java /usr/local/java/jdk-17.0.127/bin/java注意CentOS/RHEL系统上这个命令叫alternatives不带update-前缀用法基本一致。这种方式适合系统级别的默认版本切换因为它会把/usr/bin/java软链接到指定JDK的bin/java系统上下所有用户都会生效。4.3 用脚本实现一键切换开发机偷懒方案update-alternatives虽然正规但切一次要sudo还要输入一大串路径开发机上用有点繁琐。我个人的习惯是在~/.bashrc里加一个函数实现一键切换。比如我常用的写法function jdk() { case $1 in 8) export JAVA_HOME/usr/local/java/jdk8 ;; 17) export JAVA_HOME/usr/local/java/jdk-17.0.127 ;; *) echo Usage: jdk {8|17} return 1 ;; esac export PATH$JAVA_HOME/bin:$PATH java -version }保存后执行source ~/.bashrc之后想切换时直接输入jdk 8 jdk 17这种方式只修改当前终端的环境变量不需要root权限也不会影响系统里其他人的环境。非常适合个人开发机或者需要频繁切换版本测试Java代码的场景。缺点是新开终端需要再次切换但反正一行命令就能搞定我觉得这不算什么缺点。5. 环境变量配置失败与常见报错排查5.1 为什么java -version提示command not found这是最让新手抓狂的问题之一明明JDK装了环境变量也写了运行java -version还是报找不到命令。出现这个问题的原因有以下几种逐个排查就行。首先检查PATH变量里到底有没有JDK的bin目录。执行echo $PATH看看输出里是否包含/usr/local/java/jdk-17.0.127/bin。如果没有说明环境变量文件没被正确加载或者你写入的路径有问题。最直接的做法是重新source /etc/profile.d/java.sh然后再次echo $PATH确认。其次注意shell的加载机制。如果你是在图形终端里直接执行命令新的环境变量可能不会立即加载需要先关掉终端重新打开或者手动source。如果你在SSH会话中安装每次登录都会重新加载profile通常不会有问题。最后检查一个常见乌龙只下载了tar包根本没解压或者解压出来的目录里没有bin/java。这时候执行ls /usr/local/java/jdk-17.0.127/bin看看发现里面空荡荡那就回到解压步骤重新操作。5.2 误改了/etc/profile导致shell无法登录怎么办这是Linux新手安装JDK时的经典事故现场。你直接把export PATH写进/etc/profile结果不小心把原有PATH覆盖掉了保存后发现自己连ls、vi都用不了因为命令都找不到了。这时候别慌系统功能还在只是你的PATH被破坏了用绝对路径依然可以执行命令。如果你会用vi用绝对路径打开文件修复/usr/bin/vi /etc/profile如果你不熟悉vi也可以用sed命令直接把写错的那行注释掉/bin/sed -i s|^export PATH.*|# | /etc/profile然后重新登录PATH就会恢复默认。这种事故的本质是直接改全局文件导致的风险。我的建议从头到尾就不该碰/etc/profile一律使用/etc/profile.d/java.sh你改错了只需要删文件不会把自己锁死。5.3 JDK下载慢或者下不动怎么办GitHub被某些网络环境拖慢或者公司网络限制外网都是下载JDK时经常会遇到的问题。最实用的解决办法就是换国内镜像源。阿里云、华为云、清华大学的开源镜像站都提供了OpenJDK的镜像文件。你只需要把wget下载链接里的域名部分替换成镜像站的域名或者直接到镜像站的网页目录里找到对应版本的文件即可。下载时还有个小技巧wget加上-c参数可以断点续传wget -c https://mirrors.huaweicloud.com/openjdk/...这样文件下载到一半断了再次执行wget会自动从断点继续不用重头开始。无论从哪里下载SHA256校验都不能省这是避免文件损坏的唯一保险。5.4 JDK降级到17的注意事项从高版本切过来热搜词里出现了“jdk降级到17”说明不少人正在做从更高版本往回切换的事。从JDK 21降回17大部分项目没问题但前提是你的代码没有用到21的新特性比如虚拟线程、record pattern这些。如果用了编译和运行都会直接失败。所以降级前先扫一遍项目代码尤其确认构建工具和依赖库的兼容性。降级并不等于把高版本卸干净而是切换JAVA_HOME、PATH以及update-alternatives的默认指向。我的建议是保留高版本目录只把当前默认版本切到17。因为先切回17跑确认没问题之后再删高版本万一有遗漏还能切回去。另外如果你在高版本下编译过class文件切到17后运行时可能会报UnsupportedClassVersionError这表示class文件版本比17高。解决方法是回到高版本重新用--release 17参数编译javac --release 17 Main.java这个参数可以让编译器按Java 17的规范生成字节码即使你用高版本JDK编译产出的class低版本JDK也能跑。如果老项目从8升到17还要注意Java EE相关模块比如javax.xml.bind在17里已经被移除需要额外引入Maven依赖不能直接升完再抱怨环境有问题。6. 实用建议与踩坑记录6.1 装JDK这件事我踩过的几个坑这些坑都是我自己实战中遇到过的写出来给大家提个醒。第一个坑是装错了包。某次在CentOS上急着装Java直接yum install java装完发现只有jre没有javac项目根本编译不了。后来才知道应该装java-17-openjdk-develdevel后缀才有完整的开发工具链。Debian系同理要装带-jdk后缀的包而不是jre。第二个坑是使用临时目录放JDK。有一次我图省事把JDK解压到/tmp下系统运行了几天都没问题结果某次重启后/tmp被系统清理了整个Java环境直接失效生产服务跟着挂掉。所以JDK必须放在持久化目录/usr/local、/opt都可以千万别放/tmp。第三个坑是直接修改/etc/profile写错了PATH把自己锁在门外。我至今记得那次用/bin/sed把错误配置注释掉的场景。后来固定用/etc/profile.d/java.sh再也没出过这种事故。第四个坑是环境变量只配了PATH没有配JAVA_HOME。很多脚本和框架比如Maven、Gradle、Tomcat都需要用JAVA_HOME定位JDK只配PATH虽然能用java命令但跑构建工具时会找不到环境。所以JAVA_HOME必须一并配置。6.2 生产环境里的一个优雅小技巧软链接管理版本如果你经常升级JDK每次改环境变量里的目录名很烦。我建议在/usr/local/java下面维护一个名为current的软链接让它指向当前要使用的版本sudo ln -s /usr/local/java/jdk-17.0.127 /usr/local/java/current环境变量里只写export JAVA_HOME/usr/local/java/current export PATH$JAVA_HOME/bin:$PATH以后要升级JDK只需要把新版本解压到/usr/local/java下然后修改current软链接的指向sudo ln -sfn /usr/local/java/jdk-18 /usr/local/java/current环境变量完全不用动。这个技巧在生产环境中非常实用尤其当你需要对多个应用服务器做统一JDK升级时只需改软链接比逐台改环境变量高效得多。6.3 最后分享一点个人体会如果是在自己的开发机上其实还可以试试SDKMAN这个工具它能自动下载、切换多个JDK版本省去手动管理的麻烦。但对服务器生产环境我更推荐手动安装加软链接管理的方式因为生产环境要的是稳定和可控过度依赖自动化工具可能带来额外的不确定性。你只需要理解版本怎么选、路径怎么放、环境变量怎么写、链接怎么切Linux下JDK安装这件事就彻底不求人了。别把问题想复杂跟着流程走一遍十分钟内搞定然后就把精力放到真正重要的Java代码上去吧。