JDK 17 — The Third LTS
Java 17, released in September 2021, is the third LTS (Long-Term Support) release. In the LTS lineage of JDK 8 -> 11 -> 17, many enterprises are migrating from JDK 11 to 17, making it a key target version. It includes finalized Sealed Classes, a switch pattern matching preview, random generator improvements, and more.
| JEP | Feature | Status |
|---|---|---|
| JEP 409 | Sealed Classes | Finalized |
| JEP 406 | Pattern Matching for switch | Preview |
| JEP 356 | Enhanced Pseudo-Random Number Generators | Final |
| JEP 412 | Foreign Function & Memory API | Incubator |
| JEP 391 | macOS/AArch64 Port | Final |
| JEP 403 | Strong Encapsulation of Internal APIs | Strengthened |
| JEP 398 | Applet API Deprecation for Removal | Deprecated |
Sealed Classes — Restricting Inheritance (JEP 409)
Sealed Classes explicitly declare which classes can extend them. Using the permits keyword to list allowed subclasses, the compiler blocks any other inheritance.
Think of a library lending system: “This book can only be borrowed by members, librarians, and administrators” — it’s like specifying an explicit allow list.
// Sealed interface: only permitted implementations can exist
sealed interface Shape permits Circle, Rectangle, Triangle {}
// Each implementation must declare final, sealed, or non-sealed
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
// non-sealed: allows further inheritance
non-sealed class Triangle implements Shape {
private final double base;
private final double height;
Triangle(double base, double height) {
this.base = base;
this.height = height;
}
double base() { return base; }
double height() { return height; }
}
public class SealedClassExample {
// Sealed + Record combination for area calculation
static double area(Shape shape) {
if (shape instanceof Circle c) {
return Math.PI * c.radius() * c.radius();
} else if (shape instanceof Rectangle r) {
return r.width() * r.height();
} else if (shape instanceof Triangle t) {
return 0.5 * t.base() * t.height();
}
throw new IllegalArgumentException("Unknown shape");
}
public static void main(String[] args) {
Shape circle = new Circle(5.0);
Shape rect = new Rectangle(4.0, 6.0);
Shape tri = new Triangle(3.0, 8.0);
System.out.printf("Circle area: %.2f%n", area(circle)); // Circle area: 78.54
System.out.printf("Rectangle area: %.2f%n", area(rect)); // Rectangle area: 24.00
System.out.printf("Triangle area: %.2f%n", area(tri)); // Triangle area: 12.00
}
}
Subclasses of a Sealed Class must choose one of the following:
| Modifier | Meaning |
|---|---|
final | No further inheritance allowed |
sealed | Restricts subclasses again with permits |
non-sealed | Removes inheritance restriction (free inheritance like regular classes) |
Records are implicitly final, so they can be used without additional declaration. When switch pattern matching is finalized in JDK 21, combining it with Sealed Classes enables exhaustive switch (guaranteed complete branching).
Pattern Matching for switch — Preview (JEP 406)
JDK 17 introduced pattern matching in switch statements as a preview. It extends instanceof to switch statements, allowing clean expression of multiple type branches.
public class SwitchPatternPreview {
// JDK 17 preview feature — requires --enable-preview at compile time
static String format(Object obj) {
return switch (obj) {
case Integer i -> "Integer: " + i;
case Long l -> "Long: " + l;
case Double d -> String.format("Double: %.2f", d);
case String s -> "String(length=" + s.length() + "): " + s;
case int[] arr -> "int array, size=" + arr.length;
case null -> "null value";
default -> "Other: " + obj.getClass().getSimpleName();
};
}
public static void main(String[] args) {
System.out.println(format(42)); // Integer: 42
System.out.println(format(3.14)); // Double: 3.14
System.out.println(format("Java 17")); // String(length=7): Java 17
System.out.println(format(new int[]{1, 2})); // int array, size=2
System.out.println(format(null)); // null value
}
}
Since this is a preview feature, the --enable-preview flag is required for both compilation and execution.
javac --enable-preview --release 17 SwitchPatternPreview.java
java --enable-preview SwitchPatternPreview
This feature goes through previews in JDK 18-20 and is finalized in JDK 21.
Enhanced Pseudo-Random Number Generators (JEP 356)
The existing java.util.Random class had barely changed since 1995. JDK 17 introduces the RandomGenerator interface, enabling various algorithms through a unified API.
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import java.util.stream.Collectors;
public class RandomGeneratorExample {
public static void main(String[] args) {
// Using the default RandomGenerator
RandomGenerator rng = RandomGenerator.getDefault();
System.out.println("Default random: " + rng.nextInt(100));
// Selecting a specific algorithm
RandomGenerator xoshiro = RandomGenerator.of("Xoshiro256PlusPlus");
System.out.println("Xoshiro256++: " + xoshiro.nextInt(100));
// List available algorithms
String algorithms = RandomGeneratorFactory.all()
.map(RandomGeneratorFactory::name)
.sorted()
.collect(Collectors.joining(", "));
System.out.println("Algorithm list: " + algorithms);
// Algorithm list: L128X1024MixRandom, L128X128MixRandom, ...
// Xoshiro256PlusPlus, ...
// Combined with Stream API — generate 5 random numbers between 1-100
String randomNumbers = xoshiro.ints(5, 1, 101)
.mapToObj(String::valueOf)
.collect(Collectors.joining(", "));
System.out.println("5 random numbers: " + randomNumbers);
// 5 random numbers: 72, 15, 88, 43, 61 (varies per run)
}
}
| Algorithm | Characteristics | Use Case |
|---|---|---|
| L64X128MixRandom | Fast, sufficient quality | General simulation |
| Xoshiro256PlusPlus | Very fast, high quality | Games, scientific computing |
| L128X1024MixRandom | Very long period | Large-scale parallel simulation |
| SecureRandom | Cryptographic safety | Security, token generation |
Major Changes: Removals and Deprecations
JDK 17 removed or deprecated several legacy features.
Applet API Deprecation for Removal (JEP 398): java.applet.Applet was marked as forRemoval. Since all major browsers had already dropped plugin support, there is virtually no real-world impact.
Security Manager Deprecation for Removal (JEP 411): SecurityManager was marked as forRemoval. Its complex policy configuration saw very low actual usage.
RMI Activation Removal (JEP 407): The RMI Activation mechanism was completely removed. RMI itself remains.
AOT and Graal JIT Compiler Removal (JEP 410): The experimental AOT/JIT compiler built into the JDK was removed. GraalVM can be used separately.
macOS/AArch64 Port (JEP 391)
Official JDK builds that run natively on Apple Silicon (M1, M2) Macs were included. Running directly without Rosetta 2 emulation means Apple Silicon Mac users can experience significant performance improvements.
Migration Points from JDK 11 to 17
Key items to check when upgrading from JDK 11 LTS to JDK 17 LTS:
| Area | Change | Action |
|---|---|---|
| Internal APIs | --illegal-access option removed | Explicitly open with --add-opens |
| Text Blocks | JDK 13-15 preview -> final | Use multi-line strings |
| Records | JDK 14-16 preview -> final | Replace DTO/VO classes |
| Sealed Classes | JDK 15-17 preview -> final | Use for type hierarchy restriction |
| instanceof patterns | JDK 14-16 preview -> final | Eliminate casting boilerplate |
| NullPointerException | Improved messages since JDK 14 | Easier debugging |
| GC | ZGC/Shenandoah production-ready | Consider low-latency GC |
The most common migration issue is internal API access blocking. javax.xml.bind, javax.annotation, etc. were already removed in JDK 11, so separate dependencies must be added.
<!-- Migrate javax.xml.bind -> jakarta.xml.bind -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.0</version>
</dependency>
Summary
JDK 17 is a version that combines LTS stability with modern features.
- Sealed Classes: Explicitly restrict inheritance hierarchies with
permits. Combined with Records, enables modeling close to algebraic data types (ADT) - Switch pattern matching (preview): Replace
instanceofif-else chains with switch. Finalized in JDK 21 - RandomGenerator: Use various random algorithms through a unified interface
- Apple Silicon native: Native performance on macOS ARM64
- Legacy cleanup: Applet, Security Manager, RMI Activation removed or deprecated for removal
Migration from JDK 11 to 17 is mostly smooth, but internal API direct access and Java EE module removal impacts should be checked in advance. Modern frameworks like Spring Boot 3.x and Jakarta EE 10 require JDK 17 as the minimum version, so if you haven’t migrated yet, it’s highly recommended to start planning.