Java 8 and beyond ยท 2. Java 9 to 25, lesson 1 of 4

Java 10 to 25: the language features that matter

Intermediate3 min read@since 25Code runs on your Java 25
Explain it forThe essentials plus production detail and pitfalls.

Modern Java reads very differently from Java 8. The features you'll use most, in order:

  • var (10): local type inference, var list = new ArrayList<String>();
  • HTTP client (11): java.net.http.HttpClient for HTTP/1.1 and HTTP/2.
  • Switch expressions (14): case X -> value; a switch that returns a value, with no fall-through.
  • Text blocks (15): multi-line strings between triple quotes, for JSON and SQL.
  • Records (16): immutable data classes in one line.
  • Pattern matching for instanceof (16): if (o instanceof String s) ...
  • Sealed classes and interfaces (17): a closed set of subtypes the compiler knows about.
  • Pattern matching for switch and record patterns (21): switch over types and deconstruct records.
  • Virtual threads (21): millions of cheap threads for blocking I/O.
  • Sequenced collections (21): getFirst(), getLast(), reversed() on lists, deques and ordered sets and maps.
  • Unnamed variables (22): _ for values you don't need.
  • Stream gatherers (24): custom intermediate stream operations.
  • Java 25: compact source files with an instance main method (void main()), scoped values, flexible constructor bodies and module import declarations.

The long-term-support releases (11, 17, 21, 25) are what most companies run. The example below combines several of these features.

Diagram

Example

Java
sealed interface Payment permits Card, Upi, Wallet {}            // Java 17: closed hierarchy
record Card(String last4, long amountPaise) implements Payment {}  // Java 16: records
record Upi(String vpa, long amountPaise) implements Payment {}
record Wallet(String provider, long amountPaise) implements Payment {}

static String describe(Payment p) {
    return switch (p) {                                            // Java 21: pattern matching for switch
        case Card(var last4, var amount) -> "Card ****" + last4 + ": " + amount / 100;   // record patterns
        case Upi(String vpa, long amount) when amount > 100_000_00 -> "Large UPI payment from " + vpa;
        case Upi u -> "UPI " + u.vpa();
        case Wallet(var provider, _) -> "Wallet: " + provider;    // Java 22: unnamed pattern
    };                                                             // no default: the compiler knows every case
}

var receipt = """
        {
          "status": "paid",
          "summary": "%s"
        }
        """.formatted(describe(new Card("4242", 249900)));         // Java 15: text blocks
Java 25: a complete program in a compact source file (run it with: java Hello.java)
void main() {
    String name = IO.readln("Your name? ");
    IO.println("Hello, " + name + "!");
}

Common mistake

Using var everywhere, including where the type isn't obvious (var data = service.load();). Use it when the right-hand side makes the type clear.

Under the hood

Upgrade paths usually go LTS to LTS: the biggest jumps are 8 to 11 (modules, removed Java EE packages) and anything to 21 (virtual threads change how you size thread pools). Preview features need --enable-preview and can change between releases, so keep them out of production code. Use the "Java versions" pages on this site for a release-by-release list.

Check yourself

Which Java version made virtual threads a standard feature?

How this connects

Where this leads

You've reached the end of this thread. Try a learning path for what's next.

Was this lesson helpful?

Finished reading? Mark it complete to track your progress.