macOS 如何定位 JAVA_HOME

多数的 Java 入门教程都是要求同时设置 JAVA_HOME 和 PATH(包含 $JAVA_HOME/bin) 两个环境变量,反正两个都有了就保险。其实一般情况下系统能在 PATH 中找到 java 程序时就知道 JAVA_HOME, 基本上只要配置 PATH 就行,而 JAVA_HOME 环境变量是可选的。但也有例外,比如 TOMCAT 就可能要求有 JAVA_HOME 环境变量。

在 macOS 下,JAVA_HOME 与 PATH 的关系又显得有点微妙了。一个新的 macOS 系统,它自带有 java 命令
$ which java
/usr/bin/java
你要直接执行它的话
$ java
The operation couldn’t be completed. Unable to locate a Java Runtime.
Please visit http://www.java.com for information on installing Java.
所以它实际上只是执行 java 的辅助入口,没有实际的 JDK 或 JRE 是没用的。

用 PATH 覆盖 /usr/bin/java

当我们把 JDK 安装在非默认目录,如 /Users/yanbin/amazon-corretto-11.jdk,我们可以配置 PATH 来使用这个 JDK
export PATH=/Users/yanbin/amazon-correto-11.jdk/Contents/Home/bin:$PATH
然后可以执行

$ java -version
openjdk version "11.0.14.1" 2022-02-08 LTS
注意自己的路径要放在 $PATH 之前,否则仍然会执行 /usr/bin/java, 从而找不到 Java

macOS 定位 JAVA_HOME 有一个自己的命令 /usr/libexec/java_home, /usr/bin/java 就是仰赖于它来定位 JAVA_HOME 的。如果 JDK 在非默认目录中,/usr/libexec/java_home 是不知道的
$ /usr/libexec/java_home
The operation couldn’t be completed. Unable to locate a Java Runtime.
Please visit http://www.java.com for information on installing Java.
虽然依据 PATH 是可以执行 java 程序的。

/usr/libexec/java_home 定位

前面提到 macOS 的 JDK 安装默认目录,那就是 /Library/Java/JavaVirtualMachines, JDK 安装程序会安装 JDK 到该目录中,如
/Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk
这时候我们不需要配置 PATH 环境变量(把 PATH 环境变量恢复成默认,或打开一个新的终端),再执行 /usr/libexec/java_home 就能找到该默认位置下的 JDK11
$ /usr/libexec/java_home
/Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk/Contents/Home
执行 java
$ java -version
openjdk version "11.0.14.1" 2022-02-08 LTS
不光是执行 JDK 安装程序的方式,就是我们直接解压 JDK 到目录也行。例如我们解压 JDK 8 到该目录中
$ ls /Library/Java/JavaVirtualMachines
amazon-corretto-11.jdk amazon-corretto-8.jdk
看看此时 macOS 会选择哪个 JDK
 1$ /usr/libexec/java_home
 2/Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk/Contents/Home
 3$
 4$ /usr/libexec/java_home -V
 5Matching Java Virtual Machines (2):
 611.0.14.1 (x86_64) "Amazon.com Inc." - "Amazon Corretto 11" /Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk/Contents/Home
 71.8.0_322 (x86_64) "Amazon" - "Amazon Corretto 8" /Library/Java/JavaVirtualMachines/amazon-corretto-8.jdk/Contents/Home
 8/Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk/Contents/Home
 9$
10$java -version
11openjdk version "11.0.14.1" 2022-02-08 LTS

列出来两个 JDK, 但选择了 JDK 11

再来一个 JDk
$ ls /Library/Java/JavaVirtualMachines
amazon-corretto-11.jdk amazon-corretto-17.jdk amazon-corretto-8.jdk
1$ /usr/libexec/java_home -V
2Matching Java Virtual Machines (3):
3    17.0.2 (x86_64) "Amazon.com Inc." - "Amazon Corretto 17" /Library/Java/JavaVirtualMachines/amazon-corretto-17.jdk/Contents/Home
4    11.0.14.1 (x86_64) "Amazon.com Inc." - "Amazon Corretto 11" /Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk/Contents/Home
5    1.8.0_322 (x86_64) "Amazon" - "Amazon Corretto 8" /Library/Java/JavaVirtualMachines/amazon-corretto-8.jdk/Contents/Home
6/Library/Java/JavaVirtualMachines/amazon-corretto-17.jdk/Contents/Home<br/><br/>
7$ java -version
8openjdk version "17.0.2" 2022-01-18 LTS

看来在 /Library/Java/JavaVirtualMachines 中有多个 JDK 版本时,/usr/libexec/java_home 会选择最高版本。

用 JAVA_HOME 选择 JDK 版本

那么如何让 /usr/bin/java 选择自己想要的 JDK 版本呢,设定 JAVA_HOME, 而不是让 /usr/libexec/java_home 自主选择最高的版本。

比如我们在 /Library/Java/JavaVirtualMachines 中有三个 JDK 版本时,想用 JDK 8

export JAVA_HOME=/Library/Java/JavaVirtualMachines/amazon-corretto-8.jdk/Contents/Home
$ java -version
openjdk version "1.8.0_322"

/usr/libexec/java_home 的高级用法

命令帮助
 1$ /usr/libexec/java_home --help
 2Usage: java_home [options...]
 3    Returns the path to a Java home directory from the current user's settings.<br/><br/>
 4Options:
 5    [-v/--version   <version>]       Filter versions (as if JAVA_VERSION had been set in the environment).
 6    [-a/--arch      <architecture>]  Filter architecture (as if JAVA_ARCH had been set in the environment).
 7    [-F/--failfast]                  Fail when filters return no JVMs, do not continue with default.
 8    [   --exec      <command> ...]   Execute the $JAVA_HOME/bin/<command> with the remaining arguments.
 9    [-X/--xml]                       Print full JVM list and additional data as XML plist.
10    [-V/--verbose]                   Print full JVM list with architectures.
11    [-h/--help]                      This usage information.

除了 /usr/libexec/java_home -V 可列出 /Library/Java/JavaVirtualMachines 中的所有 JDK 版本及路径,以及首选的 JDK 外,用 /usr/libexec/java_home -X 能以 plist XML 格式展示所有 JDK
$ /usr/libexec/java_home -X
 1<?xml version="1.0" encoding="UTF-8"?>
 2<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 3<plist version="1.0">
 4<array>
 5    <dict>
 6        <key>JVMArch</key>
 7        <string>x86_64</string>
 8        <key>JVMBundleID</key>
 9        <string>com.amazon.corretto.18</string>
10        <key>JVMEnabled</key>
11        <true/>
12        <key>JVMHomePath</key>
13        <string>/Library/Java/JavaVirtualMachines/amazon-corretto-18.jdk/Contents/Home</string>
14        <key>JVMName</key>
15        <string>Amazon Corretto 18</string>
16        <key>JVMPlatformVersion</key>
17        <string>18.0.0</string>
18        <key>JVMVendor</key>
19        <string>Amazon.com Inc.</string>
20        <key>JVMVersion</key>
21        <string>18.0.0</string>
22    </dict>
23    <dict>
24        <key>JVMArch</key>
25        <string>x86_64</string>
26        <key>JVMBundleID</key>
27        <string>com.amazon.corretto.17</string>
28        <key>JVMEnabled</key>
29        <true/>
30        <key>JVMHomePath</key>
31        <string>/Library/Java/JavaVirtualMachines/amazon-corretto-17.jdk/Contents/Home</string>
32        <key>JVMName</key>
33        <string>Amazon Corretto 17</string>
34        <key>JVMPlatformVersion</key>
35        <string>17.0.2</string>
36        <key>JVMVendor</key>
37        <string>Amazon.com Inc.</string>
38        <key>JVMVersion</key>
39        <string>17.0.2</string>
40    </dict>
41    <dict>
42        <key>JVMArch</key>
43        <string>x86_64</string>
44        <key>JVMBundleID</key>
45        <string>com.amazon.corretto.11</string>
46        <key>JVMEnabled</key>
47        <true/>
48        <key>JVMHomePath</key>
49        <string>/Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk/Contents/Home</string>
50        <key>JVMName</key>
51        <string>Amazon Corretto 11</string>
52        <key>JVMPlatformVersion</key>
53        <string>11.0.14</string>
54        <key>JVMVendor</key>
55        <string>Amazon.com Inc.</string>
56        <key>JVMVersion</key>
57        <string>11.0.14.1</string>
58    </dict>
59    <dict>
60        <key>JVMArch</key>
61        <string>x86_64</string>
62        <key>JVMBundleID</key>
63        <string>com.amazon.corretto.8</string>
64        <key>JVMEnabled</key>
65        <true/>
66        <key>JVMHomePath</key>
67        <string>/Library/Java/JavaVirtualMachines/amazon-corretto-8.jdk/Contents/Home</string>
68        <key>JVMName</key>
69        <string>Amazon Corretto 8</string>
70        <key>JVMPlatformVersion</key>
71        <string>1.8</string>
72        <key>JVMVendor</key>
73        <string>Amazon</string>
74        <key>JVMVersion</key>
75        <string>1.8.0_322</string>
76    </dict>
77</array>
78</plist>

还能用 /usr/libexec/java_home -v xxx 智能的显示所对应版本的 JAVA_HOME
$ /usr/libexec/java_home -v 1.8
/Library/Java/JavaVirtualMachines/amazon-corretto-8.jdk/Contents/Home
$ /usr/libexec/java_home -v 11
/Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk/Contents/Home
$ /usr/libexec/java_home -v 8
/Library/Java/JavaVirtualMachines/amazon-corretto-18.jdk/Contents/Home
结合 /usr/libexec/java_home -v xxx 选项,我们就可以用一个脚本来切换 JAVA_HOME,核心就是
export JAVA_HOME=$(/usr/libexec/java_home -v $1)
根据所使用 shell 的不同,比如我们可以在 ~/.bashrc 或 ~/.zshrc 文件中定义一个函数
1switch_java() {
2  JAVA_HOME=$(/usr/libexec/java_home -v $1)
3  if [ -z $2 ]; then
4    echo "switch JAVA_HOME to $JAVA_HOME"
5  fi
6  export JAVA_HOME
7}<br/><br/>
8# set default JAVA_HOME
9switch_java 11 XX

然后执行 switch_java 来切换
$ switch_java 11
$ switch_java 1.8
$ switch_java 17
Demo
$  java -version
openjdk version "11.0.14.1" 2022-02-08 LTS
OpenJDK Runtime Environment Corretto-11.0.14.10.1 (build 11.0.14.1+10-LTS)
OpenJDK 64-Bit Server VM Corretto-11.0.14.10.1 (build 11.0.14.1+10-LTS, mixed mode)
$  switch_java 1.8
switch JAVA_HOME to /Library/Java/JavaVirtualMachines/amazon-corretto-8.jdk/Contents/Home
$  java -version
openjdk version "1.8.0_322"
OpenJDK Runtime Environment Corretto-8.322.06.1 (build 1.8.0_322-b06)
OpenJDK 64-Bit Server VM Corretto-8.322.06.1 (build 25.322-b06, mixed mode)
如果把 export JAVA_HOME=$(/usr/libexec/java_home -v $1) 定义在别的脚本文件中要用 source switch_java.sh 1.8 来切换

最后注意在用 /usr/libexec/java_home 定位 JAVA_HOME 时不需要为 JAVA 定制 PATH,要以 /usr/bin/java 作为入口。

javac 也一样
$ which javac
/usr/bin/javac
更多 /usr/bin 下的 java 命令
1$ ls /usr/bin/j*
2/usr/bin/jar          /usr/bin/javah        /usr/bin/jconsole     /usr/bin/jhsdb        /usr/bin/jmap         /usr/bin/jpackage     /usr/bin/json_pp      /usr/bin/jstack
3/usr/bin/jarsigner    /usr/bin/javap        /usr/bin/jcontrol     /usr/bin/jimage       /usr/bin/jmc          /usr/bin/jps          /usr/bin/json_pp5.18  /usr/bin/jstat
4/usr/bin/java         /usr/bin/javapackager /usr/bin/jdb          /usr/bin/jinfo        /usr/bin/jobs         /usr/bin/jrunscript   /usr/bin/json_pp5.30  /usr/bin/jstatd
5/usr/bin/javac        /usr/bin/javaws       /usr/bin/jdeps        /usr/bin/jjs          /usr/bin/join         /usr/bin/jsadebugd    /usr/bin/json_xs      /usr/bin/jvisualvm
6/usr/bin/javadoc      /usr/bin/jcmd         /usr/bin/jhat         /usr/bin/jlink        /usr/bin/jot          /usr/bin/jshell       /usr/bin/json_xs5.30

如果有 JDK 中无法被 /usr/bin 下 j* 覆盖的命令,就要从 $JAVA_HOME/bin 中找,所以必要时在修改 JAVA_HOME 环境变量后,也应更新 PATH 环境变量。

关于 Linux 下如何找到 JAVA_HOME, 有两个命令
1sh-4.2# dirname $(dirname $(readlink -f $(which javac)))
2/usr/lib/jvm/java-11-openjdk-11.0.14.1.1-1.el7_9.x86_64
3sh-4.2# java -XshowSettings:properties -version 2>&1 > /dev/null | grep 'java.home'
4    java.home = /usr/lib/jvm/java-11-openjdk-11.0.14.1.1-1.el7_9.x86_64

链接:

  1. How to Find JAVA_HOME
永久链接 https://yanbin.blog/macos-how-to-locatate-java_home/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。