An Overview of New Features in Java 26: A Robust Iteration Paving the Way for the Future
Java 26 has arrived. While it doesn't bring the kind of groundbreaking, language-level changes we've seen in some past versions, it feels more like the work of a meticulous engineer — focused on performance optimization, modernization, and paving the way for more ambitious future projects (hello, Valhalla!).
This update includes 10 JEPs (JDK Enhancement Proposals), which can be grouped into three categories: 4 finalized features, 5 preview features, and 1 important preparation for deprecation. Let's dive into these changes and see how they will impact our daily development work.
Finalized Features: Production-Ready Tools
These are features that, after multiple rounds of preview and feedback, are finally ready for confident use in production environments.
JEP 516: Ahead-of-Time Object Caching with Any GC
What is it? The AOT object caching feature introduced in Java 24 is now freed from its garbage collector constraints and can work with any GC, including ZGC and Shenandoah.
What problem does it solve?
The previous AOT cache implementation was tightly coupled with the object format of specific GCs. This prevented it from being enabled when using ZGC, large heaps (>32GB), or with compressed object pointers disabled (-XX:-UseCompressedOops), limiting its scope of application.
How it works:
The new approach no longer uses hardcoded memory addresses to reference cached objects. Instead, it employs a logical indexing system. This layer of abstraction decouples the object cache from the GC's memory layout, achieving universal compatibility. It activates automatically when using ZGC, heaps over 32GB, or -XX:-UseCompressedOops, and can be forced with -XX:+AOTStreamableObjects.
My Take: This is a classic "unsung hero" type of improvement. For applications pursuing extreme startup performance (e.g., Serverless, CLI tools), AOT caching is key. JEP 516 extends this benefit from G1/Parallel GC to all modern GCs, allowing more applications that adopt newer technologies to benefit. This is a crucial step in making Java more competitive in the cloud-native era.
JEP 522: G1 GC: Improve Throughput by Reducing Synchronization
What is it? Through a clever design, this JEP reduces the synchronization overhead between application threads (mutators) and GC threads in the G1 garbage collector.
What are the benefits? A direct performance boost! The official expectations are:
- A 5-15% throughput improvement for reference-intensive applications
- An additional ~5% improvement on x64 architecture due to reduced register pressure
All this comes at the small cost of about 2MB (0.2%) of extra overhead per GB of heap memory — a clear net win.
How it works: G1 uses a Card Table to track which memory regions (Cards) have been modified, so they can be processed during the concurrent marking phase. Previously, application threads and GC threads would contend for the same Card Table, creating a synchronization bottleneck. JEP 522 introduces a second Card Table, allowing application threads to write to one while GC threads process the other, skillfully avoiding direct conflict.
My Take: This is another excellent example of a "free lunch" on the Java platform. As the default GC, any performance improvement to G1 benefits a massive number of applications. Developers don't need to change a single line of code — just upgrade the JDK version to enjoy higher throughput. This perfectly demonstrates the value of the JVM platform's continuous self-optimization.
JEP 517: HTTP/3 for the HTTP Client API
What is it?
The standardized java.net.http.HttpClient, introduced in Java 11, finally gets native support for the HTTP/3 protocol.
Why is it important? HTTP/3 is based on the QUIC protocol (built on UDP). Compared to HTTP/2 (based on TCP), it solves the head-of-line blocking problem, establishes connections faster, and performs better in unstable network conditions (like mobile networks). Approximately one-third of websites currently support HTTP/3.
How to use it: You need to explicitly opt in to HTTP/3, with four protocol negotiation strategies available:
// You need to explicitly request HTTP/3
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com"))
.build();
// The client will attempt to negotiate HTTP/3 via ALPN or an HTTPS record
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Response protocol version: " + response.version());
Four negotiation strategies:
- Try HTTP/3 first with timeout fallback to HTTP/2/1.1
- Race HTTP/3 against older protocols simultaneously
- Start with HTTP/2/1.1 and switch upon server discovery
- Force HTTP/3 only (fails if unsupported)
My Take: This is a necessary upgrade to keep pace with the times. While many high-performance scenarios might already be using third-party libraries like Netty, having out-of-the-box HTTP/3 support in the standard library is significant for common applications and for those looking to reduce external dependencies. It ensures Java remains modern in the network programming space.
JEP 504: Remove the Applet API
The ancient Applet API has been officially removed from the JDK.
A look back:
- Java 9: Marked as Deprecated
- Java 17: Marked as Deprecated for Removal
- Java 26: Officially removed
Removed elements include the entire java.applet package, java.beans.AppletInitializer, and javax.swing.JApplet.
My Take: A relic of a bygone era — time to say goodbye. Removing the Applet API is the right decision for cleaning up historical baggage, making the JDK lighter and more secure. For the vast majority of developers, this will have zero impact — unless you're maintaining museum-grade code.
Preview Features: Shaping the Future of Java
These features are available but must be enabled with the --enable-preview flag. They represent the Java team's invitation to the community to provide feedback and collectively refine the future of the language.
JEP 530: Primitive Types in Patterns, instanceof, and switch (Fourth Preview)
What is it?
Pattern matching can now be used directly with primitive types like int, double, and boolean, no longer being restricted to reference types. Both instanceof and switch now work with primitive type patterns.
Code Example:
// Preview feature, requires --enable-preview
static String formatPrimitive(Object obj) {
return switch (obj) {
// Match primitive types directly!
case int 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();
};
}
// instanceof also supports primitive types
if (roomSize instanceof byte r) {
System.out.println("Room size fits in a byte: " + r);
}
My Take: This is a feature I've been personally looking forward to. It completes a major piece of the pattern matching puzzle, significantly enhancing its expressiveness and consistency when dealing with mixed-type data (e.g., parsing JSON, processing database records). A solid step in Java's language modernization.
JEP 525: Structured Concurrency (Sixth Preview)
What is it? A new concurrency model designed to simplify writing, debugging, and maintaining concurrent programs by tying the lifecycle of concurrent tasks to the syntactic structure of the code.
Core idea: If a task splits into multiple subtasks, it must wait for all subtasks to complete before continuing. This creates a clear parent-child task hierarchy, eliminates thread leaks, and greatly simplifies error handling and task cancellation.
What's new in this round:
The introduction of an onTimeout() method; allSuccessfulOrThrow() now returns a List instead of a Stream.
Code Example:
// Preview feature, requires --enable-preview
void handleOrder() throws InterruptedException, ExecutionException, TimeoutException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> userFuture = scope.fork(this::findUser);
Future<Integer> orderFuture = scope.fork(this::fetchOrder);
scope.joinUntil(Instant.now().plusSeconds(5));
scope.throwIfFailed();
String user = userFuture.resultNow();
int order = orderFuture.resultNow();
System.out.println("Order " + order + " for user " + user);
}
}
My Take:
Structured Concurrency is the most significant evolution in Java's concurrency landscape since the java.util.concurrent package. It fundamentally shifts how we think about concurrency — from "managing threads" to "managing tasks." Although it has gone through six rounds of preview, this level of caution is warranted. It's getting more mature with each iteration and isn't far from finalization.
JEP 526: Lazy Constants (Second Preview)
What is it? Provides a standard, safe, and efficient way to define lazily initialized, immutable values. Renamed from "Stable Values" to "Lazy Constants" — a name that more intuitively reflects its core properties: lazy loading + immutability.
The new LazyConstant<T> class guarantees single initialization even under concurrent access and disallows null as computed values. It also supports collection factory methods like List.ofLazy() and Map.ofLazy().
My Take: This might seem like a niche feature, but it's incredibly practical. The classic "lazy initialization singleton" pattern is full of pitfalls (thread safety, double-checked locking failures, etc.). Lazy Constants elevates this common, error-prone design pattern into a simple and reliable language construct — especially valuable for building high-performance, robust libraries and frameworks.
Other Preview/Incubator Features
- JEP 524: PEM-Encoded Cryptographic Objects (Second Preview): Provides a standard API for parsing and generating PEM-formatted keys and certificates, introducing
PEMEncoder,PEMDecoder,PEMrecord, and more — reducing reliance on third-party libraries like Bouncy Castle. - JEP 529: Vector API (Eleventh Incubator): This long-running incubator feature leverages CPU SIMD instruction sets to accelerate vectorized computations — key for Java's ambitions in high-performance computing and machine learning. Its final form is highly dependent on Project Valhalla's progress, with no changes in this round.
A Deprecation to Watch
JEP 500: Prepare to Make Final Mean Final
What is it?
The JDK will now issue a warning when code attempts to modify a final field via reflection.
Why does this matter?
The final keyword is an important contract in Java, guaranteeing field immutability. The JVM and JIT compiler perform extensive optimizations based on this contract. Breaking it through reflection is dangerous "dark magic" that can cause unpredictable behavior and hinder future performance optimizations.
Impact:
Currently just a warning, but this is a clear signal: in a future Java version, this behavior will likely be prohibited entirely, throwing an IllegalAccessException. You can explicitly allow final field mutation for specific modules with --enable-final-field-mutation=module1,module2. Frameworks relying on this technique (such as some older serialization libraries) need to adjust their implementations soon.
My Take: This is about restoring order and correctness — a move crucial for the long-term health of the Java platform. It strengthens the semantic consistency of the language and opens up greater optimization opportunities for the JIT compiler. Developers should check their codebases now and eliminate dependencies on this unsafe behavior.
Summary and Outlook
Java 26 is a classic "bridging" release.
It builds on the past by finalizing features like AOT caching, G1 GC optimizations, and HTTP/3 support, delivering tangible performance and modernization benefits to existing applications.
It lays the groundwork for the future by continuing to refine heavyweight features like Structured Concurrency and Pattern Matching through multiple preview rounds — features that will profoundly change Java programming paradigms.
More importantly, we can clearly see the shadow of Project Valhalla. Whether it's the long-incubating Vector API or the novel Lazy Constants, they are all waiting for Valhalla's Value Types and Primitive Objects to unlock their full potential. Valhalla will be the most profound transformation of the Java platform since generics, and many features in Java 26 are laying the solid foundation for its arrival.
All in all, upgrading to Java 26 will be a smooth and beneficial experience. You'll get free performance improvements and modern network support, while gaining early access to powerful features that will define the next generation of Java programming. The evolution of Java continues — steady and determined.
References
- Java 26 is here! — Hanno's Blog