skip to content

getting a JDK

which JDK to install (there are like ten), JAVA_HOME, and an editor.

chapter
chapter 01.02
reading time
6 min read

install one JDK (if nobody's told you otherwise: temurin 25), check that java -version and javac -version both say 25, and point JAVA_HOME at it. that's the whole setup.

the picture

you go to download java and there are like ten of them. temurin, corretto, zulu, liberica, oracle, graalvm, microsoft... which one is "real"?

all of them. they're all built from the same OpenJDK source code, and the ones worth using pass the same official compatibility test suite (the TCK). think of the same recipe baked by different bakeries: same bread. what differs is who sells it, how long they keep fixing it, and the license on the bag.

for =
os =
install =
eclipse temurin 25by eclipse adoptium

free, tested against the java spec, builds for everything, no strings attached. the boring default, which is what you want from a JDK.

$ sdk install java # no version = sdkman's default, temurin 25
$ java -version
pick what you're installing it for, your OS and how you like to install things. the command is the one you'd actually run. version numbers move every quarter, so sdk list java shows what's current.

if you install a lot of JDKs (you will, eventually), use sdkman. it installs any vendor's JDK into your home folder, switches between them with one command, and sets JAVA_HOME for you:

terminal
$ curl -s "https://get.sdkman.io" | bash
$ sdk install java          # no version: sdkman's default, temurin 25
$ sdk list java             # every build of every vendor, with its id
$ sdk use java <id>         # switch, in this terminal only

check it worked

open a new terminal (old ones don't see the changes) and ask both the runtime and the compiler:

terminal
$ java -version
openjdk version "25.0.4.1" 2026-08-18 LTS
OpenJDK Runtime Environment Temurin-25.0.4.1+1 (build 25.0.4.1+1-LTS)
OpenJDK 64-Bit Server VM Temurin-25.0.4.1+1 (build 25.0.4.1+1-LTS, mixed mode, sharing)
 
$ javac -version
javac 25.0.4.1

both should say 25. read the first line for the version, the second for who built it (temurin, corretto...), the third for the JVM (HotSpot's "Server VM", in "mixed mode": interpreter plus JIT, like in what java actually is).

JAVA_HOME and PATH

two environment variables, two different jobs, and most "java is broken on my machine" problems are these two disagreeing.

  • PATH is how your shell finds a program when you type java. it's a list of folders, checked in order. first match wins.
  • JAVA_HOME is the folder of one JDK. maven, gradle, IntelliJ and most scripts read it to decide which java to use. your shell never looks at it.
PATH =
JAVA_HOME =
you type java -version. the shell tries each PATH entry, in order:
  1. 1/usr/local/binno java here
  2. 2/usr/lib/jvm/java-17-openjdk/bininstalled years agofound java 17, runs this one
  3. 3/usr/lib/jvm/temurin-25-jdk/bin$JAVA_HOME/binjava 25, never reached
  4. 4/usr/binnever checked
your terminalfirst java on PATH
java 17
maven, gradle, your IDEread $JAVA_HOME
java 25

they disagree. the terminal compiles with 17, the build uses 25. errors like "release version 25 not supported" come from exactly this.

flip the PATH order and unset JAVA_HOME. the terminal and your build tools only agree when the first java on PATH is the one inside $JAVA_HOME. green is a match, red is the setup that wastes your afternoon.

the fix is the same everywhere: set JAVA_HOME, then put $JAVA_HOME/bin at the front of PATH, so they can't drift apart.

~/.zshrc
export JAVA_HOME=$(/usr/libexec/java_home -v 25)
export PATH="$JAVA_HOME/bin:$PATH"

/usr/libexec/java_home -V (capital V) lists every JDK the mac knows about.

quick check
you run java -version and it says 25. but your build fails with error: release version 25 not supported. what's going on?

what's in the box

a JDK is just a folder. nothing gets "installed into the system" beyond what PATH and JAVA_HOME point at, which is why having five of them side by side is totally fine.

  • ├── binjava, javac, jshell, jar, javap, jdb... 30 programs
  • ├── confsecurity settings, logging defaults
  • ├── includeC headers, for calling native code
  • ├── legallicenses, one folder per module
  • ├──
  • │ ├── modulesthe entire class library: 69 modules, 138 MB
  • │ └──
  • │ └── libjvm.dylibthe JVM itself, 19 MB
  • ├── manmanual pages
  • └── releasea text file: version, vendor, module list
temurin 25 on a mac. click a folder to fold it. the two purple files are the actual runtime: the whole class library in one file, and the JVM.
terminal
$ cat "$JAVA_HOME/release" | head -3
IMPLEMENTOR="Eclipse Adoptium"
IMPLEMENTOR_VERSION="Temurin-25.0.4.1+1"
JAVA_RUNTIME_VERSION="25.0.4.1+1-LTS"

an editor

anything works for this site, because every example is one file you can run with java Main.java (more on that in compile and run). for real projects:

editorwhen
IntelliJ IDEAthe java IDE. understands your whole project, refactors safely, best debugger. the free tier is plenty.
VS Code + the "Extension Pack for Java"lighter. good if you already live in VS Code.
any text editor + a terminalhonestly great while learning. you see every step the IDE would hide.

whichever you pick, tell it which JDK to use (IntelliJ calls it the "project SDK"). editors keep their own setting and don't always read JAVA_HOME.

one level deeper:why java -version prints to stderr+

try java -version > out.txt: the version still shows up in your terminal and out.txt is empty. -version has always printed to stderr (that's just how it was written in the 90s). java 9 added --version with two dashes, which prints the same thing to stdout, so scripts can capture it. same story for -help vs --help.

keylines
  • every JDK worth installing is built from the same OpenJDK code. pick temurin 25 unless you have a reason not to.
  • java -version and javac -version should both say 25.
  • PATH decides what your terminal runs. JAVA_HOME decides what your tools use. make them point at the same JDK.
  • a JDK is just a folder. having several is normal, sdkman makes switching painless.