Skip to content

What's New in Java 26? Complete Guide to All 10 JEPs

The Problem

I wanted to understand what Java 26 brings to the table. But when I looked at the OpenJDK page, I saw 10 JEPs with cryptic titles. Which ones matter? Which are just previews? Should I care about a non-LTS release?

Here’s what I found: Java 26 is a stepping stone release. It’s not an LTS version, so production workloads shouldn’t upgrade. But it shows where Java is heading. Five JEPs are still in preview or incubator stages, meaning they’re not ready for prime time. The other five are final, including some significant changes like HTTP/3 support and the long-awaited Applet API removal.

The 10 JEPs at a Glance

Here’s the complete list:

JEPNameStatusCategory
500Prepare to Make Final Mean FinalFinalLibrary
504Remove the Applet APIFinalCleanup
516Ahead-of-Time Object Caching with Any GCFinalPerformance
517HTTP/3 for the HTTP Client APIFinalLibrary
522G1 GC: Improve Throughput by Reducing SynchronizationFinalPerformance
524PEM Encodings of Cryptographic ObjectsSecond PreviewSecurity
525Structured ConcurrencySixth PreviewLibrary
526Lazy ConstantsSecond PreviewLibrary
529Vector APIEleventh IncubatorPerformance
530Primitive Types in Patterns, instanceof, and switchFourth PreviewLanguage

Half are finalized, half are still in progress. Let me walk through each one.

JEP 517: HTTP/3 Support (Final)

This is the headline feature. The HTTP Client API now supports HTTP/3.

HTTP/3 uses QUIC instead of TCP. Why does this matter?

  • Faster connection establishment (0-RTT or 1-RTT)
  • Better performance on unreliable networks
  • No head-of-line blocking

Here’s how to use it:

HttpClientHttp3Example.java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class HttpClientHttp3Example {
public static void main(String[] args) throws Exception {
// HTTP/3 is opt-in, not default
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3) // Enable HTTP/3
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com"))
.GET()
.build();
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println("Status: " + response.statusCode());
System.out.println("Version: " + response.version());
}
}

Important: HTTP/2 remains the default. You must explicitly opt in to HTTP/3. This is intentional - let developers test before making it default.

JEP 504: Applet API Removal (Final)

Goodbye, Applet API. It’s been deprecated since Java 9, deprecated for removal since Java 17. Now it’s gone.

These classes and interfaces are removed:

  • java.applet.Applet
  • java.applet.AppletStub
  • java.applet.AppletContext
  • java.applet.AudioClip
  • javax.swing.JApplet
  • java.beans.AppletInitializer

If your code still references these, it won’t compile on Java 26. But let’s be honest: who’s still using Applets in 2026? Browser plugins died years ago.

JEP 516: AOT Object Caching with Any GC (Final)

This is significant for startup performance. AOT (Ahead-of-Time) caching was introduced in JDK 24, but it only worked with specific garbage collectors. Now it works with any GC, including ZGC.

What does this mean?

AOT Cache GC Support Comparison
Before Java 26:
AOT Cache -> Only works with G1 or Serial GC
After Java 26:
AOT Cache -> Works with G1, ZGC, Shenandoah, Serial, Parallel

The key change: cached objects are stored in a GC-agnostic format, then loaded sequentially into memory at startup. This improves:

  • Application startup time
  • Warmup time
  • Cloud-native scenarios where fast scaling matters

If you’re using microservices that scale up and down frequently, this matters.

JEP 522: G1 GC Throughput Improvements (Final)

G1 gets a throughput boost by reducing synchronization overhead.

The G1 garbage collector uses synchronization barriers during evacuation. JEP 522 optimizes these barriers, reducing the synchronization cost during garbage collection cycles.

The result: better throughput without sacrificing pause time goals. This is a low-level optimization that doesn’t require code changes.

JEP 500: Prepare to Make Final Mean Final (Final)

This JEP is a warning shot. It doesn’t change behavior yet - it warns about future restrictions.

Final fields in Java can technically be modified through reflection:

ReflectionWarning.java
import java.lang.reflect.Field;
class Example {
final int value = 42;
}
public class ReflectionWarning {
public static void main(String[] args) throws Exception {
Example ex = new Example();
Field field = Example.class.getDeclaredField("value");
field.setAccessible(true);
field.set(ex, 100); // Modifying a final field!
System.out.println(ex.value); // Prints 100
}
}

This works today. But it undermines the whole point of final. Java 26 issues warnings when this happens. Future versions will restrict this capability.

If your code relies on modifying final fields via reflection, start planning alternatives.

JEP 530: Primitive Types in Patterns (Fourth Preview)

This is the most exciting language feature. It lets you use primitive types in pattern matching:

PrimitivePatternExample.java
public class PrimitivePatternExample {
public static void main(String[] args) {
Object value = 42;
// Before: needed to handle wrapper types
if (value instanceof Integer i) {
System.out.println("Integer: " + i);
}
// After: can match primitive types directly
if (value instanceof int i) {
System.out.println("int: " + i);
}
// Works in switch too
switch (value) {
case int i -> System.out.println("Got int: " + i);
case double d -> System.out.println("Got double: " + d);
case long l -> System.out.println("Got long: " + l);
default -> System.out.println("Got something else");
}
}
}

This is the fourth preview. It started in JDK 23 and keeps getting refined. The fourth preview adds stricter dominance checking in switch statements, helping the compiler catch more errors.

Why preview? The team wants feedback before finalizing the design.

JEP 525: Structured Concurrency (Sixth Preview)

Structured Concurrency treats multiple tasks as a single unit of work. This is the sixth preview since JDK 21.

StructuredConcurrencyExample.java
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.Future;
public class StructuredConcurrencyExample {
public static void main(String[] args) throws Exception {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> user = scope.fork(() -> fetchUser());
Future<String> orders = scope.fork(() -> fetchOrders());
Future<String> recommendations = scope.fork(() -> fetchRecommendations());
scope.join(); // Wait for all tasks
scope.throwIfFailed(); // Handle any errors
// All tasks succeeded
String result = user.resultNow() + orders.resultNow() + recommendations.resultNow();
System.out.println(result);
}
}
static String fetchUser() { return "User data"; }
static String fetchOrders() { return "Order data"; }
static String fetchRecommendations() { return "Recommendations"; }
}

The key benefit: if one task fails, the others are automatically cancelled. No orphaned threads. No resource leaks.

Sixth preview means it’s mature but not quite ready. Expect it to stabilize soon.

JEP 524: PEM Encodings (Second Preview)

PEM (Privacy-Enhanced Mail) is a common format for cryptographic objects like certificates and keys. JEP 524 adds standard API support for PEM encoding/decoding.

PemExample.java
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.cert.X509Certificate;
import java.util.Base64;
public class PemExample {
// Before Java 26: manual PEM handling
public static String toPemOld(String type, byte[] data) {
StringBuilder sb = new StringBuilder();
sb.append("-----BEGIN ").append(type).append("-----\n");
sb.append(Base64.getMimeEncoder().encodeToString(data));
sb.append("\n-----END ").append(type).append("-----\n");
return sb.toString();
}
// After: standard API (preview in JDK 26)
// java.security.PemEncoder and PemDecoder classes
}

This is second preview. It simplifies working with certificates and keys without third-party libraries.

JEP 526: Lazy Constants (Second Preview)

Lazy constants are initialized on first use, not at class load time:

LazyConstantExample.java
public class LazyConstantExample {
// Regular constant: initialized immediately
static final String IMMEDIATE = expensiveInit();
// Lazy constant (preview): initialized on first access
// static final lazy String LAZY = expensiveInit();
static String expensiveInit() {
System.out.println("Initializing...");
return "value";
}
}

This is second preview. The benefit: faster class loading when constants are expensive to compute.

JEP 529: Vector API (Eleventh Incubator)

Vector API enables SIMD (Single Instruction, Multiple Data) operations:

VectorApiExample.java
import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.VectorSpecies;
public class VectorApiExample {
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_256;
static void vectorComputation(float[] a, float[] b, float[] c) {
int i = 0;
int upperBound = SPECIES.loopBound(a.length);
for (; i < upperBound; i += SPECIES.length()) {
var va = FloatVector.fromArray(SPECIES, a, i);
var vb = FloatVector.fromArray(SPECIES, b, i);
var vc = va.mul(va).add(vb.mul(vb)).neg();
vc.intoArray(c, i);
}
// Handle remaining elements
for (; i < a.length; i++) {
c[i] = -(a[i] * a[i] + b[i] * b[i]);
}
}
}

Eleventh incubator means it’s been cooking for a while. Still not ready for production use, but maturing.

Should You Upgrade?

Java 26 is a non-LTS release with 6 months of support. Here’s my take:

Do upgrade for:

  • Testing new features before they land in LTS
  • Evaluating HTTP/3 for your applications
  • Experimenting with AOT caching on ZGC

Do NOT upgrade for:

  • Production workloads (use Java 21 LTS)
  • Stability-critical systems
  • Long-term maintenance

The 6-month release cadence means LTS versions (Java 21, next likely Java 29 or 30) are for production. Non-LTS releases are for developers who want to see what’s coming.

What’s Coming Next?

Looking at the JEPs, I see preparation for bigger things:

  • JEP 500 prepares for Project Valhalla (value types)
  • JEP 516 expands Leyden (AOT compilation) capabilities
  • JEP 530 moves toward unified type system

The non-LTS releases are stepping stones. Java 26 sets the stage for what’s coming in the next LTS.

Summary

In this post, I covered all 10 JEPs in Java 26. The key highlights are:

  • HTTP/3 support (JEP 517): Modern HTTP client with QUIC
  • Applet API removal (JEP 504): Finally dead after years of deprecation
  • AOT caching for any GC (JEP 516): Better startup performance with ZGC support
  • G1 throughput improvements (JEP 522): Reduced synchronization overhead
  • Final field warning (JEP 500): Preparing for future restrictions

The preview/incubator features continue maturing:

  • Primitive type patterns (fourth preview)
  • Structured Concurrency (sixth preview)
  • PEM encodings, Lazy constants (second preview)
  • Vector API (eleventh incubator)

Java 26 isn’t for production. But it shows where Java is heading. Test the features now, prepare for the next LTS.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments