MacOS 下安装多个版本的 JDK 并统一管理

现在的 MacOS 都不自带 JDK 了,如果你在未安装 JDK 的 MacOS 中执行 Java 命令会出现如下提示:

AigeStudio@aige$ java -version
No Java runtime present, requesting install.

然后 MacOS 会提示你 JDK 未安装:

JDK 未安装提示 —— From AigeStudio

点击更多信息的话就会去到 Oracle 的 JDK 下载页让你下载安装,在稍微早一点的 MacOS 版本中点击更多信息会让你跳转至 Apple 的 JDK 6 下载页,不管怎么个方式,MacOS 就是要你自己去安装 JDK,自行下载安装包安装 JDK 虽然可行,但是 JDK 的包并非是 app 而是 pkg 的形式,管理、升级和卸载起来都极不方便,因此这里爱哥是很不推荐下载安装包安装 JDK 的,相较而言,使用 brew 提供自动安装和卸载的功能,管理起来也比自行安装方便轻松。如果你还没用过 brew 的话可以参阅 MacOS 安装 brew 并配置 cask

使用 brew 安装 JDK 也非常简单,如果你只是安装最新的 JDK,那么直接执行下述命令即可:

AigeStudio@aige$ brew cask install java

当然很多时候我们开发环境并不一定只局限于最新版的 JDK,还需要一些低版本的 JDK,如果使用 brew 安装其他版本的 JDK 还需要使用 “homebrew-cask-versions”,该命令行工具用作于安装应用的历史版本,如果你未曾使用过它那么则需要先使用下述命令配置:

AigeStudio@aige$ brew tap caskroom/versions

然后执行 brew 的 search 命令看看有哪些 JDK 版本可供安装:

AigeStudio@aige$ brew search java

这里爱哥已经安装了 JDK 的最新版 12 以及 8 和 6,可以看到执行搜索命令后会出现已安装的状态标识(该功能实现参阅 巧用 iTerm2 & zsh & oh-my-zsh 打造炫酷的 MacOS 终端环境)系列文章:

brew 显示已安装的 JDK 版本 —— From AigeStudio

安装完所有你需要的 JDK 版本后执行下述命令就可以查看所有已安装的 JDK 版本路径地址:

AigeStudio@aige$ /usr/libexec/java_home -V

比如这里爱哥安装了三个版本的 JDK (其中 JDK6 区分了 32bit 和 64bit 版本):

本机显示已安装的 JDK 版本 —— From AigeStudio

得到每个不同的 JDK 路径后我们就可以着手在这些 JDK 之间进行切换,在默认情况下 MacOS 中 Java 使用的是最新的 JDK 版本,可以通过命令:

AigeStudio@aige$ java -version

查看,比如这里爱哥使用的最新版本就是 Open JDK 12:

本机安装的 Open JDK 12 —— From AigeStudio

我们可以通过设置系统的环境变量 JAVA_HOME 来在不同的 JDK 版本之间切换,最简单的方式就是通过 “export” 命令手动修改它的值:

AigeStudio@aige$ export JAVA_HOME=/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home

不过每次都输入这么一长串命令显得太过繁琐不够智能,我们可以在 shell 的配置文件(如果是 bash 则在 ~/.bash_profile;如果是 zsh,则在 ~/.zshrc )中以指定 alias 的方式简化切换命令:

# JDK 6、JDK 8、JDK 12 的 export 命令
export JDK6_HOME="/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home"
export JDK8_HOME="/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home"
export JDK12_HOME="/Library/Java/JavaVirtualMachines/openjdk-12.0.1.jdk/Contents/Home"

# alias 命令链接到 export 命令
alias jdk6="export JAVA_HOME=$JDK6_HOME"
alias jdk8="export JAVA_HOME=$JDK8_HOME"
alias jdk12="export JAVA_HOME=$JDK12_HOME"

修改完 Shell 配置文件后保存并执行 source 命令实时更新配置文件:

AigeStudio@aige$ source 配置文件路径(如果是bash则为~/.bash_profile;如果是zsh,则为~/.zshrc)

最后我们就可以在 Shell 中使用 jdk6、jdk8、jdk12 命令切换不同的 JDK 版本了。

使用 export & alias 的方式管理 JDK 固然没问题,但是当我们遇到复杂的场景时这种方式就显得不中用了。比如我们又很多不同的 Java 项目,而这些 Java 项目之间又互相依赖,并且这些 Java 项目都不是使用统一的 JDK 版本。如果我们使用 export & alias 的方式切换 JDK,那么就有可能会导致一些项目可以编译成功一些不能。一种更好的思路是限制 export & alias 的作用范围,即可以将其对 JDK 环境变量的设置作用于某个具体的项目,即作用到具体的目录。这个思路虽好,但是使用 export & alias 的方式实现起来就比较困难。这里爱哥给大家推荐 JEnv 这个小工具,它可以帮我们更好地管理 & 切换不同版本的 JDK。

JEnv 的安装很简单,你可以直接参考其官方网站。JEnv 目前支持 Linux 和 MacOS 两种操作系统,在 MacOS 下你可以直接使用 brew 进行安装(注:如果你还没用过 brew 的话可以参阅 MacOS 安装 brew 并配置 cask):

AigeStudio@aige$ brew install jenv

然后配置 Shell 添加如下配置参数

# 如果你是用的 bash 则执行下述命令
AigeStudio@aige$ echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.bash_profile
AigeStudio@aige$ echo 'eval "$(jenv init -)"' >> ~/.bash_profile

# 如果你是用的 zsh 则执行下述命令
AigeStudio@aige$ echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.zshrc
AigeStudio@aige$ echo 'eval "$(jenv init -)"' >> ~/.zshrc

最后我们使用上述使用到的 /usr/libexec/java_home -V 命令罗列出已经安装的 JDK 路径:

AigeStudio@aige$ /usr/libexec/java_home -V
Matching Java Virtual Machines (4):
    12.0.1, x86_64: "OpenJDK 12.0.1"    /Library/Java/JavaVirtualMachines/openjdk-12.0.1.jdk/Contents/Home
    1.8.0_212, x86_64:  "AdoptOpenJDK 8"    /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
    1.6.0_65-b14-468, x86_64:   "Java SE 6" /Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
    1.6.0_65-b14-468, i386: "Java SE 6" /Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home

再使用 jenv add 命令挨个将上述路径添加即可:

AigeStudio@aige$ jenv add /Library/Java/JavaVirtualMachines/openjdk-12.0.1.jdk/Contents/Home
AigeStudio@aige$ jenv add /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
AigeStudio@aige$ jenv add /Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home

完成上述配置后使用 JEnv 的 versions 命令查看已经被添加至 JEnv 管理的 JDK 版本:

AigeStudio@aige$ jenv versions
  system
  1.6
  1.6.0.65
  1.8
  1.8.0.212
  12.0
* 12.0.1 (set by /Users/aige/.jenv/version)
  openjdk64-1.8.0.212
  openjdk64-12.0.1
  oracle64-1.6.0.65

如果看到我们添加的 JDK 6、JDK 8 和 JDK 12 被添加了则表明配置成功。这里需要注意的是,JEnv 会读取 JDK 的不同别名来作为显示。比如上面命令行中罗列出的 1.8、1.8.0.212 以及 openjdk64-1.8.0.212 其实指代的都是 JDK 8。而版本号左边带星号 “*” 的则表示当前执行 Java 命令后会使用到的 JDK 版本。比如上述命令行中 12.0.1 左边有星号,这时我们执行一个 java version 命令:

AigeStudio@aige$ java -version
openjdk version "12.0.1" 2019-04-16
OpenJDK Runtime Environment (build 12.0.1+12)
OpenJDK 64-Bit Server VM (build 12.0.1+12, mixed mode, sharing)

显示的就会是 JDK 12 的版本。

JEnv 的精髓是 shell、local 和 global 三个参数命令。shell 用于设置终端窗口生命周期内使用的 JDK 版本;local 用于设置当前目录下使用的 JDK 版本;而 global 用于设置全局使用的 JDK 版本。这三个命令的使用方式都一样:

AigeStudio@aige$ jenv shell/local/global 1.6

上述命令最后的 “1.6” 即我们在 JEnv 中设置的不同 JDK 版本的别名。这里如果我们想在当前终端窗口中使用 JDK 8,我们只需执行:

AigeStudio@aige$ jenv shell 1.8

此时我们再执行 java version 命令就会显示使用的是 JDK 8:

AigeStudio@aige$ jenv shell 1.8
AigeStudio@aige$ java -version
openjdk version "1.8.0_212"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_212-b04)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.212-b04, mixed mode)

shell 命令设置的 JDK 是终端窗口生命周期内有效的,即你一旦退出终端再次进入则会失效。而 local 命令则可以让你在某个目录下执行某个具体版本的 JDK:

JEnv 参数命令 local 的使用 —— From AigeStudio

local 命令不像 shell 那样有生命周期,一旦在某个目录下使用 local 设置了 JDK 版本,就会在该目录下生成一个 “.java-version” 文件,该文件中记录了当前目录所使用的 JDK 版本,只要你不删除该文件,则设置会一直有效。最后的 global 命令就不说了很好理解。

shell、local 和 global 命令几乎就是 JEnv 的全部,其它的一些小功能不太常用,你可以参考其官方文档或者直接查看 jenv 的命令帮助:

AigeStudio@aige$ jenv
jenv 0.5.2
Usage: jenv <command> [<args>]

Some useful jenv commands are:
   commands    List all available jenv commands
   local       Set or show the local application-specific Java version
   global      Set or show the global Java version
   shell       Set or show the shell-specific Java version
   rehash      Rehash jenv shims (run this after installing executables)
   version     Show the current Java version and its origin
   versions    List all Java versions available to jenv
   which       Display the full path to an executable
   whence      List all Java versions that contain the given executable

See jenv help <command> for information on a specific command.
For full documentation, see: https://github.com/hikage/jenv#readme
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容