JDK 26 is a non-LTS release, but it doesn’t make it less important! It is always interesting to see where Java is heading, and for the teams planning to upgrade to the next LTS version — to try out new features in advance.
This release includes 10 JEPs with new, improved, or removed functionality. Let’s look at all of them.
Table of Contents
- New Features
- Improved Features
- JEP 516: Ahead-of-Time Object Caching with Any GC
- JEP 524: PEM Encodings of Cryptographic Objects (Second Preview)
- JEP 525: Structured Concurrency (Sixth Preview)
- JEP 526: Lazy Constants (Second Preview)
- JEP 529: Vector API (Eleventh Incubator)
- JEP 530: Primitive Types in Patterns, instanceof, and switch (Fourth Preview)
- Removed Features
- Conclusion
New Features
JEP 500: Prepare to Make Final Mean Final
JEP 500 aims to prepare the developers for the restriction of mutation of final fields in future releases.
Final fields in Java represent an immutable state. After they are assigned in a constructor or in a class initializer, a final field cannot be reassigned. The expectation that a final field cannot be reassigned is important for reliability and even app performance. However, there are several APIs in Java that allow final fields to be reassigned at any time by any code. That undermines the whole point of final fields.
// A normal class with a final field
class C {
final int x;
C() { x = 100; }
}
// 1. Perform deep reflection over the final field in C
java.lang.reflect.Field f = C.class.getDeclaredField("x");
f.setAccessible(true); // Make C's final field mutable
// 2. Create an instance of C
C obj = new C();
System.out.println(obj.x); // Prints 100
// 3. Mutate the final field in the object
f.set(obj, 200);
System.out.println(obj.x); // Prints 200
f.set(obj, 300);
System.out.println(obj.x); // Prints 300
With JEP 500, developers will receive warnings about uses of deep reflection to mutate final fields. To avoid current warnings and future restrictions, developers will have to explicitly and selectively enable the ability to mutate them.
JEP 517: HTTP/3 for the HTTP Client API
JEP 517 updates Java's HTTP Client API to support the HTTP/3 protocol, which was standardized in 2022 and is supported by most web browsers. Java applications using HTTP/3 can benefit from a more reliable transport and potentially faster handshakes.
HTTP/2 remains the default version of the HTTP Client API, so developers can use the new version by setting the protocol version of an HttpClient object to HTTP/3. If the target server does not support HTTP/3 then the request will be transparently downgraded to HTTP/2 or HTTP/1.1.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Http3Demo {
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://openjdk.org/"))
.version(HttpClient.Version.HTTP_3)
.GET()
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.version()); // HTTP_3, HTTP_2, or HTTP_1_1
System.out.println(response.statusCode());
}
}
JEP 522: G1 GC: Improve Throughput by Reducing Synchronization
JEP 522 aims to improve the throughput of applications that use G1 GC by reducing the G1 synchronization overhead.
G1GC keeps track of object references so it can update them efficiently after moving objects. But some apps update references so often that G1 needs background optimizer threads, and those threads have to synchronize with application threads to avoid being on each other’s way. That coordination makes G1’s write barriers slow.
The JEP addresses this problem by letting the application threads and the GC optimizer threads work on separate copies of the tracking data and swapping them when needed. This will boost throughput without changing how users interact with G1.
Improved Features
JEP 516: Ahead-of-Time Object Caching with Any GC
JEP 516 further improves the AOT Cache feature introduced as part of the Project Leyden integration in JDK 24. AOT Cache enables you to reduce the startup and warmup time of Java applications by creating a cache with loaded and linked classes and method profiles. Previously, this cache was incompatible with ZGC, a low-latency Java garbage collector.
But now, it can be used with all garbage collectors, including ZGC, so you don’t have to choose between low latency and fast startup.
// trial run of the application
java -XX:AOTCacheOutput=app.aot -cp app.jar com.example.App
// production run of the application
java -XX:AOTCache=app.aot -cp app.jar com.example.App
JEP 524: PEM Encodings of Cryptographic Objects (Second Preview)
PEM Encodings of Cryptographic Objects is an API for encoding objects representing cryptographic keys, certificates, and certificate revocation lists into the Privacy-Enhanced Mail (PEM) transport format, and for decoding from that format back into objects.
JEP 524 re-previews this feature with several changes, including some renamed classes and methods.
JEP 525: Structured Concurrency (Sixth Preview)
Structured concurrency was first introduced in JDK 19. Its goal is to improve the experience of working with concurrency in Java and help the developers write more readable, maintainable, and reliable code.
The secret ingredient of the feature is tying the lifetime of subtasks to a specific scope with clearly defined entry and exit points, namely the task’s code block. This way, errors, cancellation, and observability all follow a clear, enforced parent-child structure. If one subtask fails, for instance, others are automatically cancelled.
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.StructuredTaskScope.Subtask;
record Response(String user, int order) {}
class Service {
Response handle() throws InterruptedException {
try (var scope = StructuredTaskScope.open()) {
Subtask<String> user = scope.fork(this::findUser);
Subtask<Integer> order = scope.fork(this::fetchOrder);
scope.join(); // Join subtasks, propagating exceptions
// Both subtasks have succeeded, so compose their results
return new Response(user.get(), order.get());
}
}
}
JEP 525 re-previews the feature with minor changes to gain additional feedback.
JEP 526: Lazy Constants (Second Preview)
Lazy constants are objects holding immutable data. Like final fields, they are treated as constants by the JVM and allow for the same optimizations. But unlike finals, they don’t have to be initialized at the moment of creation. This allows the application to initialize them incrementally and start faster.
Lazy constants were first introduced in JDK 25 as Stable Values. JEP 526 gives them a new name and shifts their purpose towards high-level use cases, removing low-level methods.
In addition, lazy collection helpers are now exposed as List.ofLazy and Map.ofLazy. Also, some extra factories were removed, and computed null values are no longer allowed.
import java.lang.LazyConstant;
import java.util.List;
public class LazyConstantsDemo {
// A lazy constant stored in a final field for best JVM optimizations
private static final LazyConstant<Logger> LOGGER =
LazyConstant.of(() -> Logger.create(LazyConstantsDemo.class));
// Lazy list: each element is initialized on first access
private static final List<Worker> workers =
List.ofLazy(3, i -> new Worker("worker-" + i));
public static void main(String[] args) {
System.out.println("App started (without eagerly creating everything).");
// First access triggers initialization for element 0
workers.get(0).doWork();
// Second access reuses the same already-initialized element
workers.get(0).doWork();
// Logger is also initialized on first use
LOGGER.get().info("Lazy constants are doing their job.");
}
static final class Worker {
private final String name;
Worker(String name) { this.name = name; }
void doWork() {
System.out.println(name + " working");
}
}
}
JEP 529: Vector API (Eleventh Incubator)
JEP 529 re-incubates the Vector API without significant changes.
The Vector API was first introduced in JDK 16 for expressing vector computations that reliably compile at runtime to optimal vector instructions.
This API will incubate until necessary features of Project Valhalla become available as preview features. After that, the Vector API will be promoted from incubation to preview.
JEP 530: Primitive Types in Patterns, instanceof, and switch (Fourth Preview)
JEP 530 brings two important changes to the feature that allows using primitive types in Patterns, instanceof, and switch.
Firstly, the JEP tightens the rules the compiler uses to decide when a conversion is guaranteed to not lose information. The feature now clearly covers both always-safe type conversions (like byte to int, int to double) and constants that are safe to convert (like the literal 42 to byte).
The second change brings tighter dominance checks to primitive types in switch. It means that switch now rejects case labels that can never be reached because an earlier case already matches everything that the following case could match.
int j = 16_777_216;
String s = switch (j) {
case float f -> "float: " + f;
case 16_777_216 -> "never reached now"; // error: dominated since 16_777_216 can be
// converted unconditionally exactly to float
default -> "other";
};
Removed Features
JEP 504: Remove the Applet API
JEP 504 removes the Applet API: the whole java.applet package, classes related to applets, and also any remaining elements that reference this API. Modern web browsers don’t support applets anymore, so there is no reason to keep this API in the Java platform.
Conclusion
Early-access builds of JDK 26 are available so you can experiment with them now or wait for the GA of your favorite distribution due on March 17th.





