DEV Community

Cover image for How to switch Java versions on Windows with one command (no more editing JAVA_HOME)
captivator
captivator

Posted on

How to switch Java versions on Windows with one command (no more editing JAVA_HOME)

I kept juggling JDK 8, 21 and 25 on Windows. Every time a project needed a different one, I was back in the environment variables dialog, editing JAVA_HOME and then checking PATH to make sure the right java.exe won.

So I made jfs (see what it stands for at the end), a Windows Java version manager for Java fans.

GitHub logo qaptivator / jfs

Windows Java version manager for Java fans. Switch JDKs with one command. No shims, no shell hooks, no background service.

jfs

jfs logo, a blue coffee cup with orange steam

Stop editing JAVA_HOME.

jfs (Java Fucking Sucks) is a Windows Java version manager. Point it at JDKs you already have, then switch with one command. Your actual JAVA_HOME environment variable stays unchanged. Behind the scenes, a directory junction moves. No shims, no shell hooks, no background service.

irm https://jfs.acdev.app/install.ps1 | iex
Enter fullscreen mode Exit fullscreen mode

Git Bash:

curl -fsSL https://jfs.acdev.app/install.ps1 | powershell.exe -NoProfile -ExecutionPolicy Bypass -Command -
Enter fullscreen mode Exit fullscreen mode

cmd:

powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://jfs.acdev.app/install.ps1 | iex"
Enter fullscreen mode Exit fullscreen mode

Or run install.bat.

PS C:\Users\you> 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)
PS C:\Users\you> jfs
…
Enter fullscreen mode Exit fullscreen mode

The trick

JAVA_HOME is set once, to %LOCALAPPDATA%\jfs\current. That path is an NTFS directory junction. When you run jfs use 21, jfs retargets the junction to the JDK you picked. JAVA_HOME itself never changes.

Because of that:

  • every new java process follows the JDK you picked
  • already-open terminals don't need a restart (a java process that is already running keeps its old JDK, of course)
  • there are no shims, no shell hooks, and no background service
  • it doesn't need admin rights or Developer Mode, because junctions (mklink /J) don't, unlike symlinks

Usage

irm https://jfs.acdev.app/install.ps1 | iex

jfs add 21 "C:\path\to\jdk-21"
jfs add 8 "C:\path\to\jdk8"
jfs list
jfs use 21
java -version
jfs current
jfs rm 8
Enter fullscreen mode Exit fullscreen mode

jfs add checks that bin\java.exe exists in the directory and records the path in a small config.json. The first JDK you add becomes active. jfs rm only forgets a version and never touches files on disk.

It does not download JDKs

This is on purpose, at least for now. Many of us already have several JDKs lying around: Gradle's cached toolchains, the ones bundled with IDEs, Adoptium installs. jfs lets you reuse those instead of downloading yet another copy. I was inspired by nvm, but jfs only switches, it doesn't install.

Things worth knowing

  • Windows only. The switching mechanism is an NTFS junction.
  • Local NTFS paths only. A junction can't target a network share.
  • System PATH beats user PATH. Windows searches the system PATH first, so if a system directory contains its own java.exe, that one wins. The installer prints those directories when it finds them. An entry that is literally %JAVA_HOME%\bin is fine, since it follows jfs.
  • Uninstall carefully. Remove-Item -Recurse follows the current junction and can delete the JDK it points at. Remove the junction first with cmd /c rmdir "%LOCALAPPDATA%\jfs\current".

Under the hood

It's written in Rust and MIT licensed. All state is one config.json plus the junction, living in %LOCALAPPDATA%\jfs (or wherever JFS_DIR points).

Feedback

I'd especially like to hear about:

  • PATH edge cases I haven't hit
  • how IDEs and Gradle daemons behave after a switch, since some tools cache JAVA_HOME at startup

Source, releases and issues: github.com/qaptivator/jfs
Landing page: jfs.acdev.app

(and, yes, it stands for "Java Fucking Sucks")

Top comments (0)