简介:面向macOS x64架构开发者的Java 17长期支持版完整JDK二进制包,解决在苹果Intel芯片Mac上搭建稳定Java开发与运行环境的问题,适合本地开发、生产部署、离线安装及构建镜像等场景。依据Oracle免费条款,JDK 17二进制文件可在生产中免费使用并可免费再分发。压缩包共392个文件,大小约169.24MB,文件类型覆盖jmod模块文件、license与copyright授权说明、dylib动态链接库、properties配置和policy安全策略;同时包含javac、jshell、jcmd、jpackage等常用开发诊断工具,以及源码模块和说明文档,结构清晰,便于按需查阅。目前已有296人学习下载。解压后即可调用核心命令,无需额外联网;也可结合模块文件、源码与文档,深入理解JDK的模块化组织方式和内置工具链。对于需要统一布置团队开发环境、制作离线工具包或搭建CI/CD基础镜像的运维与架构人员,这份资源同样适用。
1. 从 jdk-17_macos-x64_bin.tar.gz 说起:一份能直接解压的 Java 17 LTS
“jdk-17_macos-x64_bin.tar.gz”这个文件名,熟手一眼就能看出门道:Oracle 官方发布的 JDK 17(Java 17 LTS)macOS x64 平台压缩包版。我身边好几个同事第一次看到它都愣一下——JDK 不是都双击 dmg 安装吗?但真正在 macOS 上手工管理过多个 JDK 版本的人,反而更愿意要这份 tar.gz:不需要安装器弹窗,不会在系统目录里留一堆卸载不干净的残留,解压之后改一下环境变量就能用。
它解决的是三类问题:给开发机快速装一套干净的 Java 17 LTS;在已经装了 JDK 8 或 JDK 21 的机器上降级、切换到 17;给 DBeaver、Jmeter 这类需要指定 JDK 的工具提供一个不污染系统的可用运行环境。适合已经过了“点下一步装 Java”阶段、愿意自己掌控目录和版本的人。接下来我按实际动手顺序,把解压、配环境变量、多版本切换和踩坑记录完整过一遍。
2. 为什么这份 tar.gz 值得保留:安装方式选型与目录规划
2.1 tar.gz、dmg、Homebrew 三种装法的差别
在 macOS 上装 JDK,主流路径无非三种:Oracle 官方 dmg 安装器、Homebrew 的 openjdk@17、以及这份 tar.gz 解压版。它们的本质差别不在版本号,而在“安装之后你还能不能控制它”。
dmg 安装器会把 JDK 写进 /Library/Java/JavaVirtualMachines/ 并注册系统 Java 目录,好处是全局可用、IDEA 能自动识别,坏处是版本切换时你会陷入“系统里同时存在好几份 JDK、但 java -version 不知道听谁的”的尴尬。我见过不止一次 dmg 装完 17 后执行 java -version 显示的还是 8——PATH 被某个老配置抢先了,卸载脚本还未必删得干净。
Homebrew 的 openjdk@17 是另一种常见选择,装完还要手动 sudo ln -sfn 把目录链接进系统 Java 目录,否则 IDEA 同样找不到。而且 Homebrew 管理的是 OpenJDK 构建版,与 Oracle 官方构建在个别参数默认值上有细微差异;brew upgrade 的时候版本还可能被顺带升掉,你必须花精力 pin 住版本。
tar.gz 解压版的价值在于:JDK 就是一个普通目录,目录里就是完整 SDK。没有任何系统目录写入、没有注册动作。它放在哪、叫什么名字,完全由你决定。这正是后续多版本切换和“绿色软件式管理”的基础,也是我把这份资源长期留在硬盘里的原因。
2.2 解压与目录规划:一份可复制的 JDK 根目录
动手前先说我的习惯:先把下载好的 tar.gz 放到稳定位置,通常先留在 ~/Downloads,解压时再落到 /usr/local/java 下,这样后续配 JAVA_HOME 时路径固定,不会因为清理下载目录而失效。
# 确认文件完整,建议校验 SHA-256 shasum -a 256 ~/Downloads/jdk-17_macos-x64_bin.tar.gz # 创建 JDK 目录并解压 sudo mkdir -p /usr/local/java sudo tar -xzf ~/Downloads/jdk-17_macos-x64_bin.tar.gz -C /usr/local/java # 查看解压后的结构 ls /usr/local/java/ # 输出一般是 jdk-17.0.x.jdk/Contents/Homeshasum 这步值得养成习惯:官网页面上会给出对应包的 SHA-256 值,尤其是从镜像站拿包时,先校验再解压能避开损坏包和改名包。tar -xzf 的意思分别对应解压 gzip 压缩、显示解压过程、指定目标目录,-C 后面跟的是落盘位置。解压出来的 jdk-17.0.x.jdk 本质是一个 .jdk bundle,自带 Info.plist,真实 SDK 内容在 jdk-17.0.x.jdk/Contents/Home 这一层,后续所有 JAVA_HOME 配置都要指到这里,指错一层就只能看到空目录。
注意:这份 JDK 对系统是“不可见”的。dmg 装完的 JDK 系统会自动识别,而 tar.gz 解压版在 IntelliJ IDEA 里如果不手动指定路径是搜不到的,所以它天然适合本来就打算手工管理 JDK 的使用方式。
2.3 配置 JAVA_HOME 与 PATH:以 zsh 为例
macOS 从 Catalina 起默认 shell 是 zsh,所以绝大多数情况下你改的是 ~/.zshrc。还在用 bash 的机器就改 ~/.bash_profile,逻辑一样。
# 编辑 zsh 配置,把下面内容加进去 export JAVA_HOME="/usr/local/java/jdk-17.0.x.jdk/Contents/Home" export PATH="$JAVA_HOME/bin:$PATH"这里为什么不写死 /Library/Java/JavaVirtualMachines?因为那份 JDK 根本不在系统目录里。JAVA_HOME 必须完整指到 Contents/Home 这一层,少了 jdk-17.0.x.jdk 前缀或少了 Contents/Home,java 命令都跑不起来。PATH 里把 $JAVA_HOME/bin 放在最前面,是为了让 shell 优先找到这份 17 而不是系统自带的其它 java。
配置完后执行 source ~/.zshrc,再跑 echo $JAVA_HOME 确认路径已生效。这里有个很容易被忽略的点:如果机器上已经通过 dmg 装过其它 JDK,/usr/libexec/java_home -V 会列出系统识别的所有 JDK,但这份 tar.gz 解压版不在列表里——这是正常的,它没有走系统注册机制,别拿这条来判断安装失败。
# 快速验证 echo $JAVA_HOME # 期望输出 /usr/local/java/jdk-17.0.x.jdk/Contents/Home到这里,一份干净、可控、路径明确的 JDK 17 就绪了。接下来看它怎么跟机器上可能存在的其它 JDK 和平共处。
3. 多版本共存与降级到 17:同一台 Mac 的版本切换实战
3.1 先想清楚:你要的是“切换”还是“降级”
很多人的真实场景是:电脑里已经装了 JDK 8 或 JDK 21,突然项目要求必须 JDK 17,于是到处搜“jdk降级到17”。这里有个概念要先理清:JDK 8 在老项目里往往仍然有用,你不需要删掉它,你要做的是把当前生效的版本切到 17,也就是“多版本共存 + 切换”,而不是真的把机器降级成一个裸的 JDK 17。
干净的做法是:把 dmg 装的旧 JDK 留在系统目录 /Library/Java/JavaVirtualMachines/,把 JDK 17 的 tar.gz 解压版放到自己的 /usr/local/java/ 下。两者互不干扰:dmg 装的会向系统注册,tar.gz 的不会。哪个生效,取决于 PATH 里谁的 bin 目录先被搜到。这也是 tar.gz 版本在多版本管理上的核心优势——你可以完全不碰系统目录,纯靠用户级配置完成版本控制。
顺带提一句,DBeaver 改 JDK 版本、Jmeter 找不到 JDK 之类的问题,也基本都是这套机制没理清:GUI 工具有自己的 JDK 路径配置,不读 shell 的 PATH,后面避坑章节会展开。
3.2 手动切换:直接改 .zshrc 的朴素方式
最直白的方案是在 .zshrc 里维护两组变量,要用哪个版本就取消哪一组的注释:
# JDK 8(dmg 安装,保留在系统目录) export JAVA_HOME="/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home" export PATH="$JAVA_HOME/bin:$PATH" # JDK 17(tar.gz 解压,自定义目录) # export JAVA_HOME="/usr/local/java/jdk-17.0.x.jdk/Contents/Home" # export PATH="$JAVA_HOME/bin:$PATH"每次切换版本就是改注释、source 一下。这个方式我在早期用过一段时间,最大的问题是容易翻车:两组变量都忘了注释时,PATH 会以最后写的为准,很容易出现“我以为切到了 17,java -version 却还是 8”的错觉。而且 source ~/.zshrc 只在当前终端窗口有效,新开一个窗口又得重新来。如果你的项目频繁在 8 和 17 之间横跳,下面 jenv 的方式更适合。
3.3 用 jenv 做带状态的版本切换
jenv 是 macOS 上管理多个 JDK 的常用工具。它本身不安装 JDK,只负责把你机器上已有的 JDK 登记起来,按目录或全局来切换版本。
# 安装 jenv(Homebrew 方式) brew install jenv # 把两份 JDK 注册进 jenv jenv add /Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home jenv add /usr/local/java/jdk-17.0.x.jdk/Contents/Home # 设置全局默认版本为 17 jenv global 17.0.13 # 在某个老项目目录里单独指定 8 cd ~/work/legacy-project jenv local 1.8jenv 的原理是在你的 PATH 前面插入一个 shim,java、javac 这类命令先经过 jenv,由它根据当前目录或全局配置转发到对应 JDK。这意味着你不必反复改注释,到哪个目录就自动用哪个版本。但有一个边界必须知道:jenv 不会自动改写 JAVA_HOME 环境变量,而 Maven 这类构建工具恰恰优先读 JAVA_HOME。所以用 jenv 时要在 .zshrc 里完成 jenv 的初始化配置,官方文档里有固定一段,照抄即可,否则你切版本只切了一半。
3.4 验证降级是否成功:三层确认法
切完版本后别急着开写,先按下面这套流程确认。我实际踩过的坑是:java -version 明明显示 17,mvn -v 里 Java version 却还是 1.8,原因是 Maven 读的是 JAVA_HOME 而不是 PATH 里的 java 指向。
| 验证层 | 命令 | 看什么 |
|---|---|---|
| 运行时版本 | java -version | openjdk version "17.0.x" |
| 编译工具链 | javac -version | javac 17.0.x |
| 构建工具读取 | mvn -v | 第二行 Java version: 17.0.x |
同一台机器上存在多份 JDK 时,这四个命令可能给出四个不同版本,这不算玄学,而是每层工具读取 JDK 的方式不同。只要 java、javac、mvn 三者显示一致,说明这份 JDK 17 真正接管了你的开发链路。
4. 避坑:macOS 上安装和配置 JDK 17 的五个真实翻车现场
4.1 Terminal 提示 Operation not permitted,连 java 都跑不起来
现象:解压 tar.gz 或执行 java -version 时报 Operation not permitted,有时候连 ls /usr/local/java 都没权限。
原因:macOS 的 TCC 权限限制。从“下载”或“桌面”这类目录解压出来的可执行文件,常常带 com.apple.quarantine 扩展属性,Gatekeeper 会拦截执行;另一个常见情况是解压目标目录权限被改乱,系统不允许当前用户写读。
解决:先去掉 quarantine 属性,再检查目录归属。命令参考:
# 去掉隔离属性 xattr -dr com.apple.quarantine /usr/local/java/jdk-17.0.x.jdk # 修正目录归属(当前用户) sudo chown -R $(whoami) /usr/local/java去掉属性后建议关掉终端重开再验证。如果还不行,去“系统设置-隐私与安全性”里给终端加上“完全磁盘访问权限”。这一步解决了 80% 的“解压了但跑不动”问题。
4.2 source 之后能跑 java,新开终端就 command not found
现象:当前终端里 source ~/.zshrc 后一切正常,关掉终端重新开一个,java 直接找不到了,或者版本跳回旧的。
原因:新终端不会自动加载你改的那份配置文件。你很可能改的是 ~/.bash_profile,但当前 shell 是 zsh;或者 ~/.zshrc 里 PATH 变量被 export PATH="$JAVA_HOME/bin" 直接覆盖,没拼接原有 PATH。
解决:先确认 shell 类型,再纠正配置。执行 echo $SHELL,输出 /bin/zsh 就改 ~/.zshrc;同时检查配置里有没有把 PATH 写丢:
# 正确写法,$PATH 要保留在后面 export PATH="$JAVA_HOME/bin:$PATH"把这两处改好后,新开终端应该能直接跑到 java -version。
4.3 java -version 显示 17,Maven 和 IDEA 却还是旧版本
现象:命令行验证一切正常,但 mvn -v 显示 Java version 1.8,IDEA 的 Project SDK 里也找不到 17。
原因:Maven 读的是 JAVA_HOME 环境变量,IDEA 默认扫描的是系统 JavaVirtualMachines 注册表,这两个都不依赖当前终端的 PATH。tar.gz 解压的 JDK 没有注册进系统目录,IDEA 自然搜不到;JAVA_HOME 如果没配或指错,Maven 就退回自己找的版本。
解决:Maven 的执行环境先确认 JAVA_HOME 输出:
echo $JAVA_HOME # 必须输出 /usr/local/java/jdk-17.0.x.jdk/Contents/HomeIDEA 里到 Project Structure → Project SDK → Add JDK,手动选择 /usr/local/java/jdk-17.0.x.jdk/Contents/Home 目录。如果是 Gradle 项目,还可以在 build.gradle 里声明 toolchain,让构建工具强制使用 17,这比依赖全局配置更稳。
4.4 镜像站下载的包解压后结构不对或版本对不上
现象:下载的文件名写着 jdk-17_macos-x64_bin.tar.gz,解压后找不到 Contents/Home 目录,或者 javac 命令不存在。
原因:部分第三方镜像站会对官方包二次打包,外壳是 tar.gz、内容却是 zip 结构;也有极少数情况是下载到了 renamed 的 Linux 版,在 macOS 上自然跑不了。
解决:解压后先看第一层目录名,合法结构应该是这样:
jdk-17.0.x.jdk/ └── Contents/ └── Home/ ├── bin/ ├── lib/ └── jmods/再拿官方 SHA-256 校验值对一遍。这一步虽然麻烦,但能省掉后面排查问题的半天时间。我现在每次从镜像站拿包都强制走 shasum 校验流程,再急也不跳过。
4.5 GUI 工具报找不到 JDK:DBeaver、Jmeter 各自维护自己的路径
现象:DBeaver 首选项里 Java 路径是红的,Jmeter 启动时弹窗提示没有 JRE。
原因:这些 GUI 工具在 Finder 里双击启动时,根本不经过你的 shell 环境变量。它们各自维护一份 JDK 路径配置,DBeaver 在首选项里读,Jmeter 在启动脚本里找 java。
解决:直接手动指定路径。DBeaver 在 Preferences → General → Java 里填 /usr/local/java/jdk-17.0.x.jdk/Contents/Home;Jmeter 在 jmeter 脚本里设置 JVM_HOME,或者直接在系统里保留一份 dmg 安装的 JDK 兜底。说实话这里 tar.gz 方案反而更好给 GUI 工具指路径——路径固定、纯粹,填进去就能认,不像系统目录里有好几个版本还要猜。
5. 验证安装是否可用:用命令行、Maven 和 IDEA 一次性确认
5.1 命令行验证:别只看 java -version
配置完成后我有一套固定验证流程,按顺序执行,每一步看一个层面。
# 看 java 实际来自哪个路径 which java # 期望输出 /usr/local/java/jdk-17.0.x.jdk/Contents/Home/bin/java java -version # 期望输出 openjdk version "17.0.x" 2024-xx-xx LTS javac -version # 期望输出 javac 17.0.xwhich java 是最该先看的一条:它直接告诉你命令解析顺序里谁的 bin 排在前面。如果输出 /usr/bin/java,说明你的 PATH 配置没生效,或是系统 Java 目录优先级更高。java -version 确认运行时版本,javac -version 确认编译工具链,两者必须一致。版本不一致的项目,写代码时 IDE 提示正常,一编译就报错,这类问题最难排查。
5.2 用 hello world 验证编译与运行链路
命令看起来是通的,不等于 javac 和 java 真的能配合。我习惯写一个最小 Java 文件走完整编译运行链路:
mkdir -p ~/tmp/jdk17-test && cd ~/tmp/jdk17-test cat > Hello.java <<'EOF' public class Hello { public static void main(String[] args) { System.out.println("JDK17 on macOS OK"); } } EOF javac Hello.java && java Hello # 期望输出 JDK17 on macOS OK这段的操作逻辑是:javac 把 Hello.java 编译成 Hello.class,java 再启动 JVM 运行它。&& 保证编译成功才执行运行,哪一步失败都会停在原地。如果 javac 编译报错,大概率是 PATH 里 JDK bin 路径没对上;如果 java 运行报错,常见原因是 CLASSPATH 被污染或 JDK 目录被移动过导致 lib 相对路径失效。趁 JDK 刚装好时走一遍这条链路,能确认环境是整体可用的,而不是只活了半条命。
5.3 Maven 项目验证:确认构建工具读的是这份 JDK
建立一个空的 pom.xml,让 compiler 插件按 17 编译:
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>jdk17-check</artifactId> <version>1.0</version> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> </project>在项目根目录执行 mvn compiler:compile。如果错误信息里有 invalid target release: 17,说明 Maven 正在运行在一个低于 17 的 JVM 上,编译目标版本超过了运行版本。这时用临时指定 JAVA_HOME 的方式排查:
JAVA_HOME=/usr/local/java/jdk-17.0.x.jdk/Contents/Home mvn -v | grep "Java version"注意这里 JVM_HOME 只对这一次命令生效,不会污染终端配置。如果输出 Java version: 17.0.x,说明 Maven 本身能识别这份 JDK,问题大概率出在 maven-compiler-plugin 版本太旧。升级到 3.11.0 以上再跑一次即可。
5.4 IDEA 里指定 JDK 17:Project SDK 与 Gradle toolchain
IDEA 打开已有项目后,到 File → Project Structure → Project SDK 点 Add JDK,选目录时指向 /usr/local/java/jdk-17.0.x.jdk/Contents/Home,IDEA 会自动识别出版本号 17。然后看 SDK 旁边的 Language level 下拉菜单里有没有 17 这个选项,有就代表 SDK 配对了。
Gradle 项目还有更稳的方式,在 build.gradle 里声明 toolchain:
java { toolchain { languageVersion = JavaLanguageVersion.of(17) } }toolchain 的意思是构建时 Gradle 会去找能运行 17 的 JDK,找不到会尝试自动下载。这个声明写进项目文件后,换机器、换人开发都不会因为各自全局 JDK 版本不同而翻车。我把这个配置写进模板项目里之后,新同事加入再也没问过“为什么我这边编译报错”。
6. 一个技巧:把 JDK 17 当“绿色软件”管理,一劳永逸
最后一个技巧,也是我目前每台 Mac 上都在用的方式:利用符号链接,把 JDK 17 做成一个可随时切换的“绿色软件”,而不是绑定在某个具体版本号上。
思路是这样的:新建一个不带版本号的链接名 current,让 JAVA_HOME 永远指向 current,而不是具体版本号。以后 17.0.x 升级到新 patch 版本时,只要把链接改到新目录,所有配置都不用动。
# 将当前使用的 JDK 链接到 current ln -sfn /usr/local/java/jdk-17.0.x.jdk /usr/local/java/current # 环境变量里只写 current,不写死具体版本号 export JAVA_HOME="/usr/local/java/current/Contents/Home" export PATH="$JAVA_HOME/bin:$PATH"配合 alias,切换 JDK 就变成一条短命令:
alias jdk17='export JAVA_HOME=/usr/local/java/current/Contents/Home; export PATH="$JAVA_HOME/bin:$PATH"' alias jdk8='export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home; export PATH="$JAVA_HOME/bin:$PATH"'注意 alias 里必须有 export PATH 那半句,因为只改 JAVA_HOME 不改 PATH,java 命令仍会落到旧版本的 bin 目录。这两个 alias 放进 .zshrc 后,命令行切换版本就是敲一个单词的事。
这套管理方式还有另一个好处:整个 jdk-17.0.x.jdk 目录可以打包拷到另一台同架构 Mac 上,解压到相同路径,配置逻辑完全一致,没有任何系统级注册步骤。我帮同事在三台 Mac 上搭过同样的环境,全部复用这一套。从那以后我每次在新 Mac 上配 Java 环境,都强制走一遍同样的流程——解压、ln -sfn 建链接、验证 which java——三分钟完事,再也没翻过车。希望帮到你。
本文还有配套的精品资源,点击获取