Java versions
I include only features that interest me.
Quick summary:
-
JDK 26 (in development)
-
JEP 517 HTTP/3 support in HttpClient
-
-
JDK 25 (September 2025) LTS
-
JDK 24 (March 2025)
-
JDK 23 (September 2024)
-
JEP 467 Markdown Documentation Comments using
///
-
-
JDK 22 (March 2024)
-
JDK 21 (September 2023) LTS
-
JDK 18 (March 2022)
-
JDK 17 (September 2021) LTS
-
JEP 409 Sealed Classes & Interfaces
-
-
JDK 16 (March 2021)
-
JDK 15 (September 2020)
-
JEP 378 Text Blocks using
"""
-
-
JDK 14 (March 2020)
-
JEP 358 Helpful NullPointerExceptions
-
JDK 26
In development
JEP 517: HTTP/3 for the HTTP Client API
Update the HTTP Client API to support the HTTP/3 protocol, so that libraries and applications can interact with HTTP/3 servers with minimal code change.
To send a request using HTTP/3, you must opt-in to using it.
var client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build();
JDK 25 LTS
September 2025
JEP 520: JFR Method Timing & Tracing
Extend the JDK Flight Recorder (JFR) with facilities for method timing and tracing via bytecode instrumentation.
It introduces two new JFR event: jdk.MethodTiming and jdk.MethodTrace.
$ java -XX:StartFlightRecording:jdk.MethodTrace#filter=java.util.HashMap::resize,filename=recording.jfr ...
$ jfr print --events jdk.MethodTrace --stack-depth 20 recording.jfr
In practice, configuration files are rather used.
$ java '-XX:StartFlightRecording:method-timing=::<clinit>,filename=clinit.jfr' ...
$ jfr view method-timing clinit.jfr
A filter can also name an annotation (including custom ones).
$ jcmd <pid> JFR.start method-timing=@jakarta.ws.rs.GET
JEP 514: Ahead-of-Time Command-Line Ergonomics
Make it easier to create ahead-of-time caches, which accelerate the startup of Java applications, by simplifying the commands required for common use cases.
Simplifies JEP 483: Ahead-of-Time Class Loading & Linking the two-step workflow into a single step:
$ java -XX:AOTCacheOutput=app.aot -cp app.jar com.example.App ...
A production run that uses the AOT cache is started the same way as before:
$ java -XX:AOTCache=app.aot -cp app.jar com.example.App ...
JEP 513: Flexible Constructor Bodies
In the body of a constructor, allow statements to appear before an explicit constructor invocation, i.e.,
super(…)orthis(…). Such statements cannot reference the object under construction, but they can initialize its fields and perform other safe computations. This change allows many constructors to be expressed more naturally. It also allows fields to be initialized before they become visible to other code in the class, such as methods called from a superclass constructor, thereby improving safety.
class Employee extends Person {
String officeID;
Employee(..., int age, String officeID) {
if (age < 18 || age > 67)
// Now fails fast!
throw new IllegalArgumentException(...);
super(..., age);
this.officeID = officeID;
}
}
JEP 512: Compact Source Files and Instance Main Methods
Evolve the Java programming language so that beginners can write their first programs without needing to understand language features designed for large programs.
-
Allow main methods to omit the infamous boilerplate of
public static void main(String[] args). -
Introduce a compact form of source file that lets developers get straight to the code.
-
Add a new class in the java.lang package that provides basic line-oriented I/O methods for beginners.
void main() {
IO.println("Hello, World!");
}
JEP 506: Scoped Values
Introduce scoped values, which enable a method to share immutable data both with its callees within a thread, and with child threads. Scoped values are easier to reason about than thread-local variables. They also have lower space and time costs, especially when used together with virtual threads (JEP 444) and structured concurrency (JEP 505).
Problems with thread-local variables:
-
Unconstrained mutability — Every thread-local variable is mutable
-
Unbounded lifetime — Once a thread’s copy of a thread-local variable is set via the set method, the value to which it was set is retained for the lifetime of the thread, or until code in the thread calls the
removemethod. -
Expensive inheritance — The overhead of thread-local variables may be worse when using large numbers of threads, because thread-local variables of a parent thread can be inherited by child threads. (A thread-local variable is not, in fact, local to one thread.)
class Framework {
private static final ScopedValue<FrameworkContext> CONTEXT
= ScopedValue.newInstance(); // (1)
void serve(Request request, Response response) {
var context = createContext(request);
where(CONTEXT, context) // (2)
.run(() -> Application.handle(request, response));
}
public PersistedObject readKey(String key) {
var context = CONTEXT.get(); // (3)
var db = getDBConnection(context);
db.readKey(key);
}
}
JDK 24
March 2025
JEP 485: Stream Gatherers
Enhance the Stream API to support custom intermediate operations. This will allow stream pipelines to transform data in ways that are not easily achievable with the existing built-in intermediate operations.
jshell> Stream.of(1,2,3,4,5,6,7,8,9).gather(new WindowFixed(3)).toList()
$1 ==> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Built-in gatherers in the java.util.stream.Gatherers class:
-
foldis a stateful many-to-one gatherer which constructs an aggregate incrementally and emits that aggregate when no more input elements exist. -
mapConcurrentis a stateful one-to-one gatherer which invokes a supplied function for each input element concurrently, up to a supplied limit. -
scanis a stateful one-to-one gatherer which applies a supplied function to the current state and the current element to produce the next element, which it passes downstream. -
windowFixedis a stateful many-to-many gatherer which groups input elements into lists of a supplied size, emitting the windows downstream when they are full. -
windowSlidingis a stateful many-to-many gatherer which groups input elements into lists of a supplied size. After the first window, each subsequent window is created from a copy of its predecessor by dropping the first element and appending the next element from the input stream.
JEP 483: Ahead-of-Time Class Loading & Linking
Improve startup time by making the classes of an application instantly available, in a loaded and linked state, when the HotSpot Java Virtual Machine starts. Achieve this by monitoring the application during one run and storing the loaded and linked forms of all classes in a cache for use in subsequent runs. Lay a foundation for future improvements to both startup and warmup time.
To create a cache takes two steps. First, run the application once, in a training run, to record its AOT configuration, in this case into the file app.aotconf:
$ java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconf \
-cp app.jar com.example.App ...
Second, use the configuration to create the cache, in the file app.aot:
$ java -XX:AOTMode=create -XX:AOTConfiguration=app.aotconf \
-XX:AOTCache=app.aot -cp app.jar
To check if your JVM is correctly configured to use the AOT cache, you can add the option -XX:AOTMode=on to the command line:
$ java -XX:AOTCache=app.aot -XX:AOTMode=on \
-cp app.jar com.example.App ...
JDK 23
September 2024
JEP 467: Markdown Documentation Comments
Enable JavaDoc documentation comments to be written in Markdown rather than solely in a mixture of HTML and JavaDoc @-tags.
/// Returns a hash code value for the object. This method is
/// supported for the benefit of hash tables such as those provided by
/// [java.util.HashMap].
///
/// The general contract of `hashCode` is:
///
/// - Whenever...
/// - ..
///
/// @implSpec
/// As far as is reasonably practical, the `hashCode` method defined
/// by class `Object` returns distinct integers for distinct objects.
///
/// @return a hash code value for this object.
/// @see java.lang.Object#equals(java.lang.Object)
/// @see java.lang.System#identityHashCode
JDK 22
March 2024
JEP 458: Launch Multi-File Source-Code Programs
Enhance the java application launcher to be able to run a program supplied as multiple files of Java source code. This will make the transition from small programs to larger ones more gradual, enabling developers to choose whether and when to go to the trouble of configuring a build tool.
// file MainApplication.java
public class MainApplication {
public static void main(String[] args) {
Person p = new Person("Billy", "Korando");
System.out.println("Hello, " + p.toString() + "!");
}
}
// file Person.java
record Person(String fName, String lName) {
public String toString(){
return fName + " " + lName;
}
}
$ java MainApplication.java
Hello Billy Korando!
JEP 456: Unnamed Variables & Patterns
Enhance the Java programming language with unnamed variables and unnamed patterns, which can be used when variable declarations or nested patterns are required but never used. Both are denoted by the underscore character,
_.
try (var _ = ScopedContext.acquire()) { // Unnamed variable
... no use of acquired resource ...
} catch (Exception _) { ... }
...stream.collect(Collectors.toMap(
String::toUpperCase,
_ -> "NODATA")) // Unnamed variable
JDK 21 LTS
JEP 444: Virtual Threads
-
Preview: 19
Introduce virtual threads to the Java Platform. Virtual threads are lightweight threads that dramatically reduce the effort of writing, maintaining, and observing high-throughput concurrent applications.
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return i;
});
});
} // executor.close() is called implicitly, and waits
JEP 441: Pattern Matching for switch
-
Preview: 17
Enhance the Java programming language with pattern matching for
switchexpressions and statements. Extending pattern matching toswitchallows an expression to be tested against a number of patterns, each with a specific action, so that complex data-oriented queries can be expressed concisely and safely.
Improved enum constant case labels:
sealed interface Currency permits Coin {}
enum Coin implements Currency { HEADS, TAILS }
static void goodEnumSwitch1(Currency c) {
switch (c) {
case Coin.HEADS -> { // Qualified name of enum constant as a label
System.out.println("Heads");
}
case Coin.TAILS -> {
System.out.println("Tails");
}
}
}
static void goodEnumSwitch2(Coin c) {
switch (c) {
case HEADS -> {
System.out.println("Heads");
}
case Coin.TAILS -> { // Unnecessary qualification but allowed
System.out.println("Tails");
}
}
}
Patterns in switch labels:
static void patternSwitchTest(Object obj) {
String formatted = switch (obj) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> obj.toString();
};
}
static void testNew(Object obj) {
switch (obj) {
case String s when s.length() == 1 -> ...
case String s -> ...
...
}
}
Enhanced type checking:
record Point(int i, int j) {}
enum Color { RED, GREEN, BLUE; }
static void typeTester(Object obj) {
switch (obj) {
case null -> System.out.println("null");
// Beware of dominance of String over CharSequence!
case CharSequence cs -> System.out.println("CharSequence");
case String s -> System.out.println("String");
case Color c -> System.out.println("Color: " + c.toString());
case Point p -> System.out.println("Record class: " + p.toString());
case int[] ia -> System.out.println("Array of ints of length" + ia.length);
default -> System.out.println("Something else");
}
}
JEP 440: Record Patterns
-
Preview: 19
Enhance the Java programming language with record patterns to deconstruct record values. Record patterns and type patterns can be nested to enable a powerful, declarative, and composable form of data navigation and processing.
Pattern matching and records:
static void printSum(Object obj) {
if (obj instanceof Point(int x, int y)) {
System.out.println(x+y);
}
}
Nested record patterns:
static void printColorOfUpperLeftPoint(Rectangle r) {
if (r instanceof Rectangle(ColoredPoint(Point p, Color c),
ColoredPoint lr)) {
System.out.println(c);
}
}
static void printXCoordOfUpperLeftPointWithPatterns(Rectangle r) {
if (r instanceof Rectangle(ColoredPoint(Point(var x, var y), var c),
var lr)) {
System.out.println("Upper-left corner: " + x);
}
}
JEP 431: Sequenced Collections
Introduce new interfaces to represent collections with a defined encounter order. Each such collection has a well-defined first element, second element, and so forth, up to the last element. It also provides uniform APIs for accessing its first and last elements, and for processing its elements in reverse order.
SequencedCollection:
interface SequencedCollection<E> extends Collection<E> {
// new method
SequencedCollection<E> reversed();
// methods promoted from Deque
void addFirst(E);
void addLast(E);
E getFirst();
E getLast();
E removeFirst();
E removeLast();
}
SequencedSet:
interface SequencedSet<E> extends Set<E>, SequencedCollection<E> {
SequencedSet<E> reversed(); // covariant override
}
SequencedMap:
interface SequencedMap<K,V> extends Map<K,V> {
// new methods
SequencedMap<K,V> reversed();
SequencedSet<K> sequencedKeySet();
SequencedCollection<V> sequencedValues();
SequencedSet<Entry<K,V>> sequencedEntrySet();
V putFirst(K, V);
V putLast(K, V);
// methods promoted from NavigableMap
Entry<K, V> firstEntry();
Entry<K, V> lastEntry();
Entry<K, V> pollFirstEntry();
Entry<K, V> pollLastEntry();
}
JDK 20
March 2023
JDK 19
September 2022
JDK 18
March 2022
JEP 413: Code Snippets in Java API Documentation
Introduce an
@snippettag for JavaDoc’s Standard Doclet, to simplify the inclusion of example source code in API documentation.
Markup tags define regions within the content of a snippet: @start, @end, @highlight, @replace, and @link.
Inline snippets: An inline snippet contains the content of the snippet within the tag itself.
/**
* The following code shows how to use {@code Optional.isPresent}:
* {@snippet :
* if (v.isPresent()) {
* System.out.println("v: " + v.get());
* }
* }
*/
External snippets: An external snippet refers to a separate file that contains the content of the snippet.
/**
* The following code shows how to use {@code Optional.isPresent}:
* {@snippet file="ShowOptional.java" region="example"}
*/
public class ShowOptional {
void show(Optional<String> v) {
// @start region="example"
if (v.isPresent()) {
System.out.println("v: " + v.get());
}
// @end
}
}
JEP 400: UTF-8 by Default
Specify UTF-8 as the default charset of the standard Java APIs. With this change, APIs that depend upon the default charset will behave consistently across all implementations, operating systems, locales, and configurations.
JDK 17 LTS
September 2021
JEP 409: Sealed Classes
-
Project Amber
-
Release: 17
-
Preview: 15
Enhance the Java programming language with sealed classes and interfaces. Sealed classes and interfaces restrict which other classes or interfaces may extend or implement them.
Exactly one of the modifiers final, sealed, and non-sealed must be used by each permitted subclass.
sealed interface Celestial
permits Planet, Star, Comet { ... }
final class Planet implements Celestial { ... }
final class Star implements Celestial { ... }
final class Comet implements Celestial { ... }
JDK 16
March 2021
JEP 395: Records
-
Project Amber
-
Release: 16
Enhance the Java programming language with records, which are classes that act as transparent carriers for immutable data. Records can be thought of as nominal tuples.
record Range(int lo, int hi) {
// Compact canonical validating constructor
Range {
if (lo > hi) // referring here to the implicit constructor .parameters
throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi));
}
}
record Rational(int num, int denom) {
// Compact canonical normalizing constructor.
Rational {
int gcd = gcd(num, denom);
num /= gcd;
denom /= gcd;
}
}
JEP 394: Pattern Matching for instanceof
-
Project Amber
-
Preview: 14
Enhance the Java programming language with pattern matching for the
instanceofoperator. Pattern matching allows common logic in a program, namely the conditional extraction of components from objects, to be expressed more concisely and safely.
if (obj instanceof String s) {
// `String s` is visible here.
}
// `String s` is NOT visible here.
if (!(obj instanceof String s)) {
// `String s` is NOT visible here.
} else {
// `String s` is visible here.
}
JEP 392: Packaging Tool
Provide the jpackage tool, for packaging self-contained Java applications.
The supported platform-specific package formats are:
-
Linux:
debandrpm -
macOS:
pkganddmg -
Windows:
msiandexe
jpackage --name myapp --input lib --main-jar main.jar --type pkg
JDK 15
September 2020
JEP 378: Text Blocks
-
Preview: 13
Add text blocks to the Java language. A text block is a multi-line string literal that avoids the need for most escape sequences, automatically formats the string in a predictable way, and gives the developer control over the format when desired.
final String html = """
{
"message": "Hello world"
}
""";
JDK 14
March 2020
JEP 358: Helpful NullPointerExceptions
Improve the usability of
NullPointerExceptionsgenerated by the JVM by describing precisely which variable was null.
Exception in thread "main" java.lang.NullPointerException:
Cannot assign field "i" because "a" is null
at Prog.main(Prog.java:5)
JDK 13
September 2019
JDK 12
March 2019