【macOS开发环境:Java配置】—— Mac上的Java版本管理-SDKMAN安装、多版本切换与项目实战

一文搞定 Mac 上的 Java 版本管理:SDKMAN 安装、多版本切换与项目实战

装过几次 Java 的人,大概率都被 JDK 版本折腾过:这个项目要 JDK 8,那个要 21,新拉的代码又点名要 25;JAVA_HOME 手动改来改去,终端一关就忘。说白了,JDK 不是装一个就完事,而是要能在多个版本之间随手切。

本文用一套从安装到项目实战的完整流程,讲清楚怎么用 SDKMAN 把 Java(以及 Maven、Gradle)的版本彻底管起来。覆盖:为什么选 SDKMAN、安装、多 JDK 并存、日常命令速查、拿到项目后的推荐用法,以及常见坑。


一、先厘清:为什么需要一个 Java 版本管理工具

直接从官网下载安装包、或者用 Homebrew 装一个 JDK,单看都能用。问题出在"多个版本"和"切换"上:

  • 系统里装了多个 JDK,java 命令到底用哪个,取决于 PATH 里谁排在前面,靠肉眼很难理清;
  • JAVA_HOME 是构建工具(Maven、Gradle)和 IDE 认的关键变量,手动 export 容易漏、容易忘;
  • 不同项目要求不同版本,你不可能每切一个项目就重装一遍 JDK。

版本管理工具就是来解决"装在哪、用哪个、怎么切"这件事的。JVM 生态里常见的有三个,定位并不一样:

工具 定位 下载安装 JDK 管理 Maven / Gradle JAVA_HOME
Homebrew 直装 通用包管理器 能,但多版本切换麻烦 能装,但各管各的 需手动配置
jenv 纯版本切换器 不能,JDK 要另装 不能 需额外开 export 插件
SDKMAN JVM 生态一站式管理器 能,多家发行版可选 能,统一安装切换 自动设置

一句话区分:jenv 只管"切换",SDKMAN 管"下载 + 安装 + 切换 + 构建工具"。

如果你只是偶尔切版本、并且习惯用 Homebrew 管理一切,jenv 够用;但只要你认真做 Java 开发------要装多个 JDK、用 Maven 或 Gradle、还想让 JAVA_HOME 自动跟着走------SDKMAN 明显更省心,也是 Java 开发者的主流选择。本文就以 SDKMAN 为准。


二、环境说明与前置条件

本文环境如下,命令在 macOS(Apple Silicon)上实测通过:

组件 版本 / 说明
操作系统 macOS(Apple Silicon,M 系列,aarch64)
Shell zsh(SDKMAN 同时支持 bash、zsh)
SDKMAN core 5.24.0 / native 0.8.0
已装 JDK Temurin 21.0.13、Temurin 25.0.5
系统依赖 curl、zip、unzip(macOS 自带)

SDKMAN 安装前会用到 curl、zip、unzip,Mac 上默认都有,不用额外装。不确定的话可以先确认:

bash 复制代码
for c in curl zip unzip; do which "$c"; done

三、安装 SDKMAN

3.1 一条命令安装

打开终端,执行官方安装脚本:

bash 复制代码
curl -s "https://get.sdkman.io" | bash

脚本会把 SDKMAN 装到 ~/.sdkman 目录,并自动在你的 ~/.zshrc(或 ~/.bash_profile)末尾追加初始化代码。

3.2 让当前终端立即生效

安装脚本只会影响"之后新开的"终端。要让当前这个窗口立刻生效,执行:

bash 复制代码
source ~/.sdkman/bin/sdkman-init.sh

或者直接关掉终端、重新开一个,效果一样。

3.3 验证安装

bash 复制代码
sdk version

正常会输出 core 和 native 两个版本号,类似:

复制代码
SDKMAN!
core:   5.24.0
native: 0.8.0 (macos aarch64)

✅ 看到版本号,说明 SDKMAN 装好了。

3.4 初始化代码长什么样

安装后,~/.zshrc 末尾会多出这么几行,作用是每次开终端时自动加载 SDKMAN:

bash 复制代码
#THIS MUST BE AT THE END OF THE FILE FOR SDKMAN TO WORK!!!
export SDKMAN_DIR="$HOME/.sdkman"
[[ -s "$HOME/.sdkman/bin/sdkman-init.sh" ]] && source "$HOME/.sdkman/bin/sdkman-init.sh"

⚠️ 注意注释里的提示:这段初始化建议放在 .zshrc 的靠后位置。如果你 .zshrc 里还有别的工具(比如 Starship 提示符),不用慌,SDKMAN 靠 source 加载,和这些工具通常不冲突。


四、安装与管理多个 JDK

4.1 先看有哪些 JDK 可装

SDKMAN 本身不发行 JDK,它聚合了多家厂商的构建版本。先列出所有可装的 Java:

bash 复制代码
sdk list java

列表会给出每个版本的标识符(Identifier),长这样:

复制代码
21.0.13.0+9-tem
25.0.5.0+8-tem
25.0.5.0+8-zulu
25.0.5.0+9-amzn

标识符的规律是 版本号+构建号-发行版后缀。常见后缀:

后缀 发行版 说明
-tem Eclipse Temurin(Adoptium) 免费、社区主流,本文推荐
-zulu Azul Zulu 老牌、覆盖面广
-amzn Amazon Corretto AWS 出品
-ms Microsoft Build 微软出品
-oracle Oracle JDK 注意商用授权条款
-graalce GraalVM CE 适合 AOT 编译、Native Image 场景

为什么推荐 Temurin:它是 Eclipse 基金会 Adoptium 工作组维护的 OpenJDK 构建,免费开源、更新及时、被广泛使用,作为日常开发和学习的默认版本最稳妥。

Apple Silicon(M 系列)用户不用手动挑架构,SDKMAN 会自动选择 aarch64 版本。

4.2 安装指定版本的 JDK

复制 sdk list java 里的准确标识符来安装。以 Temurin 21 为例:

bash 复制代码
sdk install java 21.0.13.0+9-tem

安装结尾会问你要不要把它设为默认:

复制代码
Do you want java 21.0.13.0+9-tem to be set as default? (Y/n):

回车(或输入 Y)会把它设为所有新终端的默认版本;输入 n 则只安装、不改变当前默认。

4.3 再装一个 JDK,多版本并存

SDKMAN 的价值就在这------多个版本可以同时装、互不影响。再装一个 JDK 25:

bash 复制代码
sdk install java 25.0.5.0+8-tem

🔴 关于版本选择:JDK 21(2023 年 9 月发布)和 JDK 25(2025 年 9 月发布)都是 LTS(长期支持)版本,Oracle 对 LTS 提供多年支持。学习和新项目可以用 25,企业存量项目大量还在 21,更老的项目可能要 17 甚至 8。装哪些、默认用哪个,取决于你手头的项目。

4.4 JDK 装在哪、怎么切换的

所有通过 SDKMAN 安装的 JDK 都在 ~/.sdkman/candidates/java/ 下,每个版本一个独立目录,另有一个 current 软链指向当前默认版本:

复制代码
~/.sdkman/candidates/java/
├── 21.0.13.0+9-tem/
├── 25.0.5.0+8-tem/
└── current -> 21.0.13.0+9-tem

JAVA_HOME 会自动指向这个 current,这正是 SDKMAN 省心的地方------不用你手动 export。


五、日常操作命令速查

SDKMAN 的命令都以 sdk 开头,子命令不多。先给一张集中的全表,后面再分场景逐条说明。

5.0 SDK 常用命令全表(速查)

基础查询

命令 作用
sdk version 查看 SDKMAN 版本
sdk help [命令] 查看帮助
sdk config 打开配置文件 ~/.sdkman/etc/config
sdk list 列出所有可管理的 SDK
sdk list <sdk> 列出某 SDK 的全部版本
sdk current 查看所有 SDK 当前版本
sdk current <sdk> 查看某 SDK 当前版本
sdk home <sdk> <version> 查看某版本的安装路径

安装与卸载

命令 作用
sdk install <sdk> 安装最新稳定版
sdk install <sdk> <version> 安装指定版本
sdk install <sdk> <name> <path> 注册已有的本地版本
sdk uninstall <sdk> <version> 卸载某版本

版本切换

命令 作用范围
sdk default <sdk> <version> 全局默认,所有新终端生效
sdk use <sdk> <version> 仅当前终端临时切换

项目级环境(.sdkmanrc)

命令 作用
sdk env init 在项目根目录生成 .sdkmanrc
sdk env 应用当前目录的 .sdkmanrc
sdk env clear 退出项目,恢复默认版本
sdk env install 安装 .sdkmanrc 里缺失的 SDK

升级与维护

命令 作用
sdk upgrade 查看所有 SDK 可升级项
sdk upgrade <sdk> 升级某 SDK
sdk selfupdate 升级 SDKMAN 本身
sdk update 刷新候选版本缓存
sdk flush 清理临时文件和缓存

5.1 版本切换

操作 命令 作用范围
设全局默认 sdk default java 21.0.13.0+9-tem 所有之后新开的终端
当前终端临时切换 sdk use java 25.0.5.0+8-tem 仅当前窗口,关掉即恢复
查看当前版本 sdk current java 单个 SDK
查看全部 SDK 版本 sdk current 所有已安装的 SDK

5.2 安装、卸载、升级

bash 复制代码
# 查看可装版本
sdk list java

# 安装最新稳定版(不指定版本时)
sdk install java

# 安装指定版本
sdk install java 25.0.5.0+8-tem

# 卸载某个版本
sdk uninstall java 25.0.5.0+8-tem

# 查看哪些 SDK 有新版本可升级
sdk upgrade

# 只升级某一类
sdk upgrade java

# 升级 SDKMAN 自身
sdk selfupdate          # 有新版本才更新
sdk selfupdate force    # 强制重装

# 刷新候选版本缓存(当提示 SDKMAN 过期时)
sdk update

5.3 清理缓存

bash 复制代码
sdk flush

SDKMAN 安装过程会留下临时文件和缓存,sdk flush 用来清理它们。

⚠️ 千万别手动删 ~/.sdkman/tmp 这类目录 ,官方明确警告手动删除会破坏 SDKMAN。要清理,一律用 sdk flush。

5.4 顺带管理 Maven、Gradle

SDKMAN 能管的不只是 Java,整个 JVM 生态的工具都能装,版本同样可以多版本切换:

bash 复制代码
sdk list maven
sdk install maven

sdk list gradle
sdk install gradle

这样 Maven、Gradle 也统一由 SDKMAN 管理,升级切换都是同一套命令。

5.5 注册一个已有的本地 JDK

如果你已经有一个手动安装的 JDK(比如某些厂商的安装包),也可以把它注册进 SDKMAN,而不重新下载:

bash 复制代码
sdk install java my-21 /Library/Java/JavaVirtualMachines/some-jdk.jdk/Contents/Home

my-21 是你给它起的唯一名字,不能和已有标识符重名。注意:卸载这种本地版本时,SDKMAN 只会移除注册,不会删你原来的目录。


六、拿到一个项目后怎么用(推荐方法,重点)

这一节是日常最高频的场景。先说一个最容易混淆的点:项目里其实存在两套独立的版本控制,分别由 SDKMAN 和 Maven 各管一件事。

6.1 两套版本控制,别搞混

控制对象 配置文件 决定什么
工具链 项目根目录 .sdkmanrc 用哪个 JDK 运行 Maven(即 JAVA_HOME)、用哪个 Maven 版本
编译目标 项目的 pom.xml 编译出的字节码是哪个 Java 版本

说白了:

  • .sdkmanrc 决定"用什么跑";
  • pom.xml 里的 release 决定"编译成什么版本"。

这两件事是分开的,理解了这一点,后面的配置就不会乱。

6.2 推荐流程(一个项目从头到尾)

第 1 步:安装 Maven(只需一次)

bash 复制代码
sdk install maven

第 2 步:进入项目,生成 .sdkmanrc

bash 复制代码
cd my-project
sdk env init

这会在项目根目录生成一个 .sdkmanrc,并预填当前使用的版本:

properties 复制代码
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=21.0.13.0+9-tem

第 3 步:编辑 .sdkmanrc,锁定项目需要的版本

需要几个 SDK 就写几行,Java 和 Maven 都能锁:

properties 复制代码
java=25.0.5.0+8-tem
maven=3.9.11

Maven 具体版本号以 sdk list maven 为准;只想用最新版、不锁 Maven,也可以不写 maven 这行。

第 4 步:应用配置

bash 复制代码
sdk env

成功会提示:

复制代码
Using java version 25.0.5.0+8-tem in this shell.

想离开项目、恢复默认版本,执行:

bash 复制代码
sdk env clear

第 5 步:开启进目录自动切换(强烈推荐)

每次手动 sdk env 还是有点烦。在 SDKMAN 配置文件 ~/.sdkman/etc/config 里设置:

properties 复制代码
sdkman_auto_env=true

之后你 cd 进含 .sdkmanrc 的项目目录,会自动切到项目指定的版本;离开目录,自动恢复默认。这一步配置一次,永久省心。

第 6 步:在 pom.xml 里指定编译目标版本

用 maven-compiler-plugin 的 release 选项(JDK 9+ 推荐,等价于同时设定 source、target 和引导类路径):

xml 复制代码
<properties>
    <maven.compiler.release>25</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

或者显式配置插件:

xml 复制代码
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <release>25</release>
            </configuration>
        </plugin>
    </plugins>
</build>

第 7 步:验证

bash 复制代码
mvn -version

这是确认环境最关键的一条命令,它会同时显示 Maven 版本、运行 Maven 的 Java 版本 和 JAVA_HOME。三个信息都对得上,环境就没问题。

6.3 一条铁律:运行 Maven 的 JDK 必须 ≥ 编译目标

release 设定的编译目标,不能高于运行 Maven 的那个 JDK 版本,因为低版本 JDK 不认识高版本的 API:

运行 Maven 的 JDK 编译目标 release 结果
JDK 25 25 ✅ 可以,能用 25 新特性
JDK 25 21 ✅ 可以,高版本 JDK 编译低目标(常见、推荐)
JDK 21 25 ❌ 不行,JDK 21 无法编译成 25

所以:

  • 项目要用 Java 25 的特性 → .sdkmanrc 里 java 用 25,pom.xml 里 release 设 25;
  • 项目只需要 Java 21 → .sdkmanrc 用 21(或 25),release 设 21 即可。

6.4 克隆新项目,缺版本一键补齐

从 Git 拉下来一个带 .sdkmanrc 的项目,如果你本机还没装它指定的版本,不用一个个手动装,直接:

bash 复制代码
sdk env install

SDKMAN 会读取 .sdkmanrc,把里面缺的 SDK 全部下载安装好。这在换电脑、拉新项目时特别省事。

6.5 旧项目里的 .java-version 怎么处理

如果项目里有 .java-version 文件(那是 jenv 的项目级配置,内容通常是一个版本号),SDKMAN 不识别它。处理方式:

bash 复制代码
# 进入项目,改用 SDKMAN 的配置
sdk env init
# 确认 .sdkmanrc 内容无误后,删掉 jenv 旧文件
rm .java-version

七、常见问题排查

7.1 非交互环境报 "Unable to locate a Java Runtime"

在脚本、CI 或某些非交互方式下调用 java,可能报:

复制代码
The operation couldn't be completed. Unable to locate a Java Runtime.

原因:SDKMAN 是在交互式 shell 启动时通过 sdkman-init.sh 接管命令的,非交互环境不会自动加载。

解决:在脚本开头先加载初始化,或直接用绝对路径、显式设置 JAVA_HOME:

bash 复制代码
source "$HOME/.sdkman/bin/sdkman-init.sh"
# 或
export JAVA_HOME="$HOME/.sdkman/candidates/java/current"

7.2 JAVA_HOME 为空

先确认 ~/.zshrc 里有 SDKMAN 的初始化片段,并且当前终端已经 source 过或重新打开。正常情况下 SDKMAN 会自动设置 JAVA_HOME,不需要手动 export。

7.3 下载慢、启动卡顿

SDKMAN 启动时会做一次网络健康检查(healthcheck),网络不稳时可能拖慢启动。可以在 ~/.sdkman/etc/config 里关掉:

properties 复制代码
sdkman_healthcheck_enable=false

关掉后启动不再等检查,真没网时命令依然会正常报错。如果是下载 JDK 本身慢,可考虑配置代理,或用 5.5 节的本地安装方式,先手动下载再注册。

7.4 想知道某个 SDK 具体装在哪

写脚本时需要某个 SDK 的绝对路径,用 sdk home(类似 macOS 的 java_home):

bash 复制代码
sdk home java 21.0.13.0+9-tem
# /Users/you/.sdkman/candidates/java/21.0.13.0+9-tem

八、总结:真正需要记住的几件事

  1. SDKMAN 管"装和切" :JDK、Maven、Gradle 等整个 JVM 工具链都能统一管理,JAVA_HOME 自动设置。
  2. 三级切换 :sdk default(全局)、sdk use(当前终端)、.sdkmanrc(项目级,推荐)。
  3. 项目里两套版本控制 :.sdkmanrc 定"用什么跑",pom.xml 的 release 定"编译成什么版本"。
  4. 铁律:运行 Maven 的 JDK 版本 ≥ 编译目标版本。
  5. 清理用 sdk flush,别手动删 ~/.sdkman 下的目录 ;升级用 sdk upgrade / sdk selfupdate。
  6. 开启 sdkman_auto_env=true,进项目自动切版本,最省心。
  7. 克隆带 .sdkmanrc 的项目 ,用 sdk env install 一键补齐缺失版本。

验证清单

  • sdk version 能正常输出版本号
  • sdk list java 中已安装版本带有 * 标记
  • 新开终端执行 java -version,与预期默认版本一致
  • echo $JAVA_HOME 指向 ~/.sdkman/candidates/java/current
  • 项目根目录有 .sdkmanrc,mvn -version 显示的 Java 版本正确
  • sdkman_auto_env=true,进入 / 离开项目目录时版本自动切换
  • 旧的 .java-version 文件已替换为 .sdkmanrc

参考资源

相关推荐
骑着蜗牛撵大象3271 小时前
大模型 SDK 编译就崩?用 CMakeLists 把 httplib、JSON 与 OpenSSL 装配成可复用的 ChatServer
android·java·c++·http·json·cmake·大模型sdk
天远API1 小时前
零信任架构实战:基于天远企业股权变更构建自动化私募股权投后控制权穿透与异动预警网关
大数据·人工智能·架构·自动化
码智社1 小时前
Redis生产环境高频踩坑总结(Java后端视角)
java·数据库·redis
江苏汉软1 小时前
制造企业为什么需要MES
java·数据库·制造·mes系统·mes软件
deviant-ART1 小时前
java注解简写规则
java·后端
邪修king1 小时前
Re:Linux系统篇(三十八):进程间通信ChapterSystem V 信号量深度解析|同步互斥、内核源码、C 指针多态、实战代码
java·linux·c语言
文丑颜不良啊1 小时前
LeetCode1181~1200题目解析
java·leetcode
释厄6231 小时前
矢量词汇向下不向上公理即词汇归位定律——肤浅即表面
java·人工智能·算法·去中心化·1024程序员节