Back to Java Course

Java Course

Java releases follow a 6-month cadence since Java 9 (2017). A Long-Term Support (LTS) release is made periodically: Java 11 (Sep 2018), Java 17 (Sep 2021), Java 21 (Sep 2023), Java 25 (Sep 2025). Note the cadence itself changed along the way — the gap between Java 11 and 17 was 3 years, but Oracle shortened it to 2 years starting with Java 21, so Java 21→25 was 2 years, and the next LTS (Java 29) is expected around September 2027. For production systems, always prefer an LTS version.

Features preview in one release, optionally mature over subsequent releases, then become standard. The version numbers below reflect when features became standard (non-preview) unless noted otherwise.


Java 9 (2017)

Module System (Project Jigsaw — JEP 261)

The most significant Java 9 feature. Introduces the module-info.java descriptor to explicitly declare what packages a module exports and what modules it requires. Allows the JDK itself to be split into modules so you can create smaller runtime images.

module com.myapp.service { requires java.sql; exports com.myapp.service.api; }

JShell (JEP 222) — The first official Java REPL (Read-Eval-Print Loop). Run Java expressions interactively without compiling a full class. Useful for quick experimentation.

HTTP/2 Client (incubating) — A new HTTP client API (finalized in Java 11).

Collection Factory Methods — Concise, immutable collection creation:

List<String> list = List.of("a", "b", "c"); Set<Integer> set = Set.of(1, 2, 3); Map<String, Integer> map = Map.of("a", 1, "b", 2);

Java 10 (2018)

Local Variable Type Inference — var (JEP 286)

Java 10 introduced var for local variable declarations, letting the compiler infer the type from the initializer. This is purely a compile-time feature — the bytecode is identical to explicitly typed code. var cannot be used for method parameters, fields, or return types.

Before Java 10:

List<String> numsList = new ArrayList<>();

From Java 10:

var numsList = new ArrayList<String>();

Java 11 (2018) — LTS

HTTP Client (Standard — JEP 321)

The HTTP/2-capable HTTP client introduced as incubating in Java 9 was finalized in Java 11. Supports HTTP/1.1 and HTTP/2, synchronous and asynchronous calls.

HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://example.com")) .build(); HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); System.out.println(response.body());

New String Methods: isBlank(), strip() (Unicode-aware trim), lines(), repeat(n).

Running Single-File Programs: java Hello.java compiles and runs a single .java file in one step — no separate javac step needed for simple scripts.


Java 12–13 (2019): Preview Features

Switch Expressions — introduced as preview in Java 12, second preview in Java 13. The -> arrow syntax eliminates fall-through and break:

// Preview in Java 12, 13 String result = switch (day) { case MON, FRI, SUN -> "6 letters"; case TUE -> "7 letters"; default -> "unknown"; };

Text Blocks — introduced as preview in Java 13, second preview in Java 14.


Java 14 (2020)

Switch Expressions (Standard — JEP 361)

Switch expressions became standard in Java 14 (not Java 17 as sometimes stated). The -> arrow syntax and yield for returning values from a block are both finalized here.

int day = 4; String dayName = switch (day) { case 1, 8 -> "Monday"; case 2 -> "Tuesday"; case 3 -> "Wednesday"; case 4 -> "Thursday"; case 5 -> "Friday"; case 6 -> "Saturday"; case 7 -> "Sunday"; default -> "Not Applicable"; };

Helpful NullPointerExceptions (JEP 358) — JVM now produces messages identifying exactly which variable was null: Cannot invoke "String.length()" because "str" is null instead of just NullPointerException.


Java 15 (2020)

Text Blocks (Standard — JEP 378)

Text Blocks became standard in Java 15 (previewed in Java 13 and 14). A text block is a multi-line string literal enclosed in """. Leading indentation is stripped intelligently based on the least-indented line.

Before Java 15:

String json = "{\n \"name\": \"Praveen\",\n \"city\": \"Hyderabad\"\n}";

From Java 15:

String json = """ { "name": "Praveen", "city": "Hyderabad" } """;

Java 16 (2021)

Record Classes (Standard — JEP 395)

Records are immutable data carriers. The compiler automatically generates a canonical constructor, equals(), hashCode(), and toString() from the record components. Records cannot extend other classes (they implicitly extend java.lang.Record) and all fields are final.

Before Java 16:

public final class Book { private final String title; private final String author; private final String isbn; public Book(String title, String author, String isbn) { this.title = title; this.author = author; this.isbn = isbn; } // equals, hashCode, toString... }

From Java 16:

public record Book(String title, String author, String isbn) {}

Records integrate naturally with sealed classes, pattern matching, and instanceof — they're designed to work as transparent, immutable data carriers in modern Java data models.

Pattern Matching for instanceof (Standard — JEP 394)

Eliminates the redundant cast after an instanceof check:

Before Java 16:

if (obj instanceof String) { String s = (String) obj; System.out.println(s.length()); }

From Java 16:

if (obj instanceof String s) { System.out.println(s.length()); }

Java 17 (2021) — LTS

Sealed Classes (Standard — JEP 409)

Sealed classes and interfaces restrict which classes can extend or implement them, making class hierarchies explicit and exhaustive. Each permitted subclass must be either final, sealed, or non-sealed.

public abstract sealed class Shape permits Circle, Rectangle, Triangle {} public final class Circle extends Shape { ... } public final class Rectangle extends Shape { ... } public non-sealed class Triangle extends Shape { ... } // can be further extended

Sealed classes pair powerfully with pattern matching — the compiler can verify that a switch covers all permitted subtypes exhaustively.


Java 19–20 (2022–2023): Preview Features

Virtual Threads (Preview — JEP 425 in Java 19, JEP 436 in Java 20)

Virtual threads are extremely lightweight, JVM-managed threads (Project Loom). Unlike platform threads (which map 1:1 to OS threads), virtual threads are cheap enough to create millions of them. They are mounted on platform threads when running and unmounted when blocking (e.g., on I/O), allowing the platform thread to serve other virtual threads.


Java 21 (2023) — LTS

Virtual Threads (Standard — JEP 444)

Virtual threads became standard in Java 21. They enable high-concurrency server applications without reactive/async programming — you write simple, blocking code and the JVM makes it scalable.

// Create a virtual thread Thread.ofVirtual().start(() -> { System.out.println("Running in virtual thread: " + Thread.currentThread().isVirtual()); }); // Virtual thread per task executor (best practice for web servers) try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { executor.submit(() -> handleRequest()); }

Platform threads are expensive (typically ~1MB stack, OS resource). Virtual threads are tiny (~few KB, JVM-managed). A server that could handle ~500 concurrent platform threads can handle millions of virtual threads. This makes Tomcat's thread-per-request model viable for very high concurrency without switching to reactive frameworks like WebFlux.

Pattern Matching for switch (Standard — JEP 441)

Switch statements can now match on type patterns:

switch (shape) { case Circle c -> System.out.println("Circle radius: " + c.radius()); case Rectangle r -> System.out.println("Rect " + r.width() + "x" + r.height()); default -> System.out.println("Unknown shape"); }

Combined with sealed classes, the compiler can verify exhaustiveness — no default needed if all permitted subtypes are covered.

Record Patterns (Standard — JEP 440)

Destructure record components directly in pattern matching (previewed in Java 19 and 20):

if (obj instanceof Point(int x, int y)) { System.out.println("x=" + x + ", y=" + y); }

Sequenced Collections (JEP 431)

New interfaces SequencedCollection, SequencedSet, SequencedMap add getFirst(), getLast(), addFirst(), addLast(), and reversed() to ordered collections. Fixes the historical inconsistency where LinkedList and ArrayDeque had different APIs for first/last access.


Java 22 (2024)

Foreign Function & Memory API (Standard — JEP 454)

Finalized in Java 22, after previewing across Java 19–21. Provides a safe, performant alternative to JNI for calling native libraries and accessing off-heap memory. Replaces the error-prone sun.misc.Unsafe patterns.

try (Arena arena = Arena.ofConfined()) { MemorySegment segment = arena.allocate(100); // use segment for native memory access }

Unnamed Variables and Patterns (JEP 456) — Use _ for unused variables in patterns and catch blocks:

try { // ... } catch (IOException _) { // exception variable unused — use _ log.warn("IO error occurred"); }

Java 23 (2024)

Primitive Types in Patterns (Preview — JEP 455)

Allows primitive types in instanceof and switch patterns, completing the pattern matching story:

switch (value) { case int i when i < 0 -> "negative"; case int i -> "non-negative int"; case long l -> "long"; default -> "other"; }

Module Import Declarations (Preview — JEP 476) — Import all public packages of a module with a single declaration: import module java.base;