Java 10 to 25: the language features that matter
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.
Example
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 blocksvoid 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
Know these first
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.