Part 2 of 14

Setting Up Java 17: JDK Options, Tooling, and IDE Configuration

JDK Distribution Options

Multiple vendors ship Java 17 JDK builds. All pass the TCK (Technology Compatibility Kit) — they are functionally equivalent for development.

DistributionProviderNotes
Eclipse TemurinAdoptium / EclipseRecommended default; fully open-source
Amazon CorrettoAWSFree; optimized for AWS Lambda and EC2
Microsoft Build of OpenJDKMicrosoftWindows and Azure optimized
Oracle JDK 17OracleFree for development; commercial license for production
Azul ZuluAzulCommercial support available; free binaries
GraalVM CE 22.3+OracleAdds native-image; based on Java 17

Recommendation: Use Eclipse Temurin 17 for development and CI. It is the most widely used OpenJDK build, fully open-source, and supported by the Eclipse Foundation.


Installing Java 17

SDKMAN lets you install and switch between multiple JDK versions with a single command.

Install SDKMAN:

curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"

List available Java 17 builds:

sdk list java | grep "17"

Install Eclipse Temurin 17:

sdk install java 17.0.11-tem

Set as default:

sdk default java 17.0.11-tem

Switch to Java 17 for the current shell only:

sdk use java 17.0.11-tem

Switch back to another version:

sdk use java 21.0.3-tem

Option 2: Homebrew (macOS)

brew install --cask temurin@17

# Set JAVA_HOME
export JAVA_HOME=$(/usr/libexec/java_home -v 17)
echo 'export JAVA_HOME=$(/usr/libexec/java_home -v 17)' >> ~/.zshrc

Option 3: Direct Download

Download from adoptium.net — select Java 17, your OS, and architecture. Extract and set JAVA_HOME.

Verify Installation

java -version
# openjdk version "17.0.11" 2024-04-16
# OpenJDK Runtime Environment Temurin-17.0.11+9 (build 17.0.11+9)
# OpenJDK 64-Bit Server VM Temurin-17.0.11+9 (build 17.0.11+9, mixed mode, sharing)

javac -version
# javac 17.0.11

Maven Configuration

Set Compiler to Java 17

<!-- pom.xml -->
<properties>
  <java.version>17</java.version>
  <maven.compiler.release>17</maven.compiler.release>
</properties>

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.12.1</version>
      <configuration>
        <release>17</release>
      </configuration>
    </plugin>
  </plugins>
</build>

Use <release> (not <source> and <target>) — it correctly prevents using APIs from newer JDKs and newer class file formats simultaneously.

Enabling Preview Features in Maven

Article 8 uses Pattern Matching for switch, which is a preview feature in Java 17. To compile and test preview features:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.12.1</version>
      <configuration>
        <release>17</release>
        <compilerArgs>
          <arg>--enable-preview</arg>
        </compilerArgs>
      </configuration>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>3.2.5</version>
      <configuration>
        <argLine>--enable-preview</argLine>
      </configuration>
    </plugin>
  </plugins>
</build>

Preview features also require --enable-preview at runtime:

java --enable-preview -jar myapp.jar

Minimum Maven Version

Use Maven 3.8.1 or later with Java 17. Earlier versions have issues with the module system and strong encapsulation.

mvn -version
# Apache Maven 3.9.6

Gradle Configuration

Kotlin DSL (build.gradle.kts)

plugins {
    java
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

tasks.withType<JavaCompile> {
    options.release = 17
}

Using a toolchain is preferred over setting sourceCompatibility — it ensures the correct JDK is used for compilation even if your local default JDK is different.

Enabling Preview Features in Gradle

tasks.withType<JavaCompile> {
    options.release = 17
    options.compilerArgs.add("--enable-preview")
}

tasks.withType<Test> {
    jvmArgs("--enable-preview")
}

tasks.withType<JavaExec> {
    jvmArgs("--enable-preview")
}

Groovy DSL (build.gradle)

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

compileJava {
    options.release = 17
}

Minimum Gradle Version

Use Gradle 7.3 or later for Java 17 support. Gradle 8.x is recommended.


IntelliJ IDEA Configuration

Set Project SDK

  1. File → Project Structure → Project
  2. Set SDK to Java 17 (add it with + if not listed)
  3. Set Language level to 17 - Sealed classes, always-strict floating-point semantics

For preview features, set Language level to 17 (Preview) — Pattern matching for switch, ….

Set Module SDK

  1. File → Project Structure → Modules
  2. For each module, set Language level to match the project level

Maven / Gradle Auto-Sync

IntelliJ reads the release setting from your pom.xml or build.gradle.kts and configures the project automatically after a sync:

  • Maven: Maven tool window → Reload All Maven Projects (or Cmd+Shift+O)
  • Gradle: Gradle tool window → Reload All Gradle Projects

Enable Preview Features in IntelliJ

Set the language level to 17 (Preview) in Project Structure. IntelliJ will then recognize preview feature syntax without compilation errors in the editor.

PluginPurpose
SonarLintStatic analysis
CheckStyle-IDEACode style enforcement
LombokIf still using Lombok alongside records

VS Code Configuration

Install the Extension Pack

Install Extension Pack for Java by Microsoft:

code --install-extension vscjava.vscode-java-pack

This includes Language Support for Java, Debugger for Java, Test Runner, Maven for Java, and Gradle for Java.

Configure Java Home

In .vscode/settings.json (project-level):

{
  "java.jdt.ls.java.home": "/Users/yourname/.sdkman/candidates/java/17.0.11-tem",
  "java.configuration.runtimes": [
    {
      "name": "JavaSE-17",
      "path": "/Users/yourname/.sdkman/candidates/java/17.0.11-tem",
      "default": true
    }
  ]
}

Or globally in settings.json (Cmd+Shift+P → “Open User Settings (JSON)”):

{
  "java.home": "/Users/yourname/.sdkman/candidates/java/17.0.11-tem"
}

Enable Preview Features in VS Code

Add to .vscode/settings.json:

{
  "java.compile.nullAnalysis.mode": "automatic",
  "java.configuration.updateBuildConfiguration": "automatic"
}

Preview syntax is handled by the language server when your pom.xml or build.gradle.kts has --enable-preview configured.


Running Java 17 Programs

Compile and Run

# Compile
javac --release 17 Hello.java

# Run
java Hello

# With preview features
javac --release 17 --enable-preview Hello.java
java --enable-preview Hello

Compile a Single-File Program

Java 11+ supports running a single .java file directly:

java Hello.java

With preview features:

java --enable-preview --source 17 Hello.java

Using jshell

JShell (Java 9+) supports Java 17 features interactively:

jshell --enable-preview
|  Welcome to JShell -- Version 17.0.11

jshell> record Point(int x, int y) {}
|  created record Point

jshell> var p = new Point(3, 4)
p ==> Point[x=3, y=4]

jshell> p.x()
$3 ==> 3

JShell is the fastest way to experiment with new Java features without creating a full project.


Verifying the Setup

Create a file Java17Check.java that exercises several Java 17 features:

import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;

public class Java17Check {

    sealed interface Shape permits Circle, Rect {}
    record Circle(double radius) implements Shape {}
    record Rect(double w, double h) implements Shape {}

    public static void main(String[] args) {
        // Records
        var p = new Circle(5.0);
        System.out.println("Record: " + p);

        // Text block
        String json = """
                {"shape": "circle", "radius": 5.0}
                """;
        System.out.println("Text block: " + json.strip());

        // Pattern matching for instanceof
        Object obj = "hello";
        if (obj instanceof String s && s.length() > 3) {
            System.out.println("Pattern match: " + s.toUpperCase());
        }

        // Switch expression
        Shape shape = new Rect(3, 4);
        double area = switch (shape) {
            case Circle c -> Math.PI * c.radius() * c.radius();
            case Rect r   -> r.w() * r.h();
        };
        System.out.println("Switch expression area: " + area);

        // Enhanced PRNG
        RandomGenerator rng = RandomGeneratorFactory.of("L64X128MixRandom").create();
        System.out.println("Random (LXM): " + rng.nextInt(100));
    }
}

Compile and run:

javac --release 17 Java17Check.java
java Java17Check

Expected output:

Record: Circle[radius=5.0]
Text block: {"shape": "circle", "radius": 5.0}
Pattern match: HELLO
Switch expression area: 12.0
Random (LXM): 47

If you see this output, your Java 17 environment is correctly configured.


Common Setup Issues

error: --release: target release 17 conflicts with default source release 11

Your Maven/Gradle is still using an old JDK. Set JAVA_HOME correctly:

export JAVA_HOME=$(sdk home java 17.0.11-tem)
mvn clean compile

--add-opens Errors

If you see InaccessibleObjectException during tests, a dependency is using internal JDK APIs. Add to Surefire configuration:

<configuration>
  <argLine>--add-opens java.base/java.util=ALL-UNNAMED</argLine>
</configuration>

See Article 11 for a complete guide to strong encapsulation and how to fix these errors.

Preview Feature Compile Error

error: preview feature is disabled by default

Add --enable-preview to both compilerArgs (for javac) and argLine (for JUnit/Surefire at runtime). Both flags are required.


What’s Next

Article 3: Text Blocks (JEP 378) covers Java’s multiline string literal — how to write clean, readable embedded JSON, SQL, HTML, and YAML in Java code.