Java 26 New Features With Examples Core Java java Java 26 by devs5003 - April 12, 2026April 12, 20260 Java 26 New Features With Examples – What’s New In JDK 26 Java 26 was officially released on March 17, 2026. It is a non-LTS (short-term support) release and delivers 10 JEPs (JDK Enhancement Proposals) spanning language improvements, security enhancements, performance/runtime advancements, library improvements, and platform clean-up. Oracle will provide six months of Premier-level support for Java 26, after which developers are encouraged to migrate to Java 27. 10 JEPs of Java 26 span five categories: Core Java Library, HotSpot, Java Language Specification, Security Library, and Client Library. All new features are introduced via JDK Enhancement Proposals (JEPs), so each section lists the JEP, summarizes the upgrade, and provides practical code samples or use cases. Table of Contents Toggle JEP 530: Primitive Types in Patterns, instanceof and switch (Fourth Preview)Example#1: switch over an Object with primitive patterns:Example#2: switch over an int with range guards:Example#3: instanceof with primitive types:What’s new in this Fourth Preview (Java 26)?JEP 500: Prepare to Make Final Mean FinalWhat happens in Java 26?Example: Testing the new warning:JEP 526: Lazy Constants (Second Preview)What Are Lazy Constants?Why Not Traditional Approaches?With Lazy Constants (Java 26)Key Changes from Java 25 (Stable Values → Lazy Constants)JEP 525: Structured Concurrency (Sixth Preview)What Is Structured Concurrency?Key Changes in Java 26 (JEP 525)Example#1: All-or-Nothing Parallel FetchExample#2: Return First Successful ResultExample#3: Timeout Handling (New in Java 26)JEP 524: PEM Encodings of Cryptographic Objects (Second Preview)Example of a PEMBefore (Old Approach: Very Verbose)After (With JEP 524: Clean and Concise)JEP 529: Vector API (Eleventh Incubator)Example: Vectorized Array AdditionJEP 516: Ahead-of-Time Object Caching with Any GCWhat Changed in Java 26?Example: AOT Cache with ZGCJEP 522: G1 GC: Improve Throughput by Reducing SynchronizationThe Improvement in Java 26Example: Measure the ImprovementJEP 517: HTTP/3 for the HTTP Client APIExample#1: Default HTTP/2 (unchanged behavior)Example#2: Opt-in to HTTP/3Example#3: Stream a Large File Upload (ofFileChannel: new in Java 26)JEP 504: Remove the Applet APIHistory of RemovalWhat Was Removed?Impact on DevelopersAlso Removed: Thread.stop()Other Notable Changes in Java 26Dark Theme in JavadocDefault Initial Heap Size Trimmed DownVirtual Threads ImprovementsUnicode 17.0 SupportComplete Table of JEPs in Java 26 New FeaturesConclusionRelated JEP 530: Primitive Types in Patterns, instanceof and switch (Fourth Preview) This feature was originally proposed by JEP 455 (JDK 23), re-previewed in JDK 24 (JEP 488) and JDK 25 (JEP 507). In JDK 26, it is proposed as a fourth preview with two specific improvements: An enhanced definition of unconditional exactness for safer type conversions. Tighter dominance checks in switch constructs, improving correctness guarantees and helping the compiler catch more coding errors at compile time. Java’s pattern matching now supports all primitive types, streamlining code and reducing errors. This enables direct, type-safe matching of primitives without unnecessary boxing or verbose casting . Example#1: switch over an Object with primitive patterns: Object price = 250; switch (price) { case int p when p < 100 -> System.out.println("BUDGET: " + p); case int p when p < 500 -> System.out.println("MID: " + p); case int p -> System.out.println("PREMIUM: " + p); case double d -> System.out.println("Double price: " + d); default -> System.out.println("Unknown type"); } Example#2: switch over an int with range guards: int code = 404; switch (code) { case int i when i >= 100 && i < 200 -> System.out.println("Informational"); case int i when i >= 200 && i < 300 -> System.out.println("Success"); case int i when i >= 300 && i < 400 -> System.out.println("Redirection"); case int i when i >= 400 && i < 500 -> System.out.println("Client Error"); case int i when i >= 500 && i < 600 -> System.out.println("Server Error"); default -> throw new IllegalArgumentException("Unknown code: " + code); } Example#3: instanceof with primitive types: Object obj = 42; if (obj instanceof int val) { System.out.println("Primitive int value: " + val); } else if (obj instanceof double val) { System.out.println("Primitive double value: " + val); } What’s new in this Fourth Preview (Java 26)? The dominance check was tightened. The following code that compiled without error in Java 25 now produces compiler errors in Java 26 : int i1 = 100; switch (i1) { case float f -> {} // ERROR in Java 26: constant below is dominated case 16_777_216 -> {} // dominated by float f — unreachable default -> {} } In Java 26, 16_777_216 can be precisely represented as a float, so the constant is dominated by case float f. Java 26 catches such unreachable case labels at compile time. JEP 500: Prepare to Make Final Mean Final Most developers assume that a final field is truly immutable. In practice, however, “Deep Reflection” (Field.setAccessible(true) followed by Field.set(…)) has always allowed bypassing the restriction from outside the class, even from third-party libraries. Java 26 begins the journey to make final truly mean final . The Problem: public class Box { private final Object value; public Box(Object value) { this.value = value; } @Override public String toString() { return "Box{value=" + value + "}"; } } Box box = new Box("Rubic's Cube"); System.out.println(box); // Box{value=Rubic's Cube} Field valueField = Box.class.getDeclaredField("value"); valueField.setAccessible(true); valueField.set(box, "Magic Wand"); // Deep Reflection bypasses final! System.out.println(box); // Box{value=Magic Wand} What happens in Java 26? In Java 26, the above code still runs but now prints a warning : WARNING: Final field value in class Box has been mutated reflectively WARNING: Use --enable-final-field-mutation=ALL-UNNAMED to avoid a warning WARNING: Mutating final fields will be blocked in a future release Controlling the behavior with VM options: VM Option Behavior –illegal-final-field-mutation=warn (default) Issues a warning (Java 26 default) –illegal-final-field-mutation=allow Allows silently (backward-compatible) –illegal-final-field-mutation=deny Throws exception (future default) Example: Testing the new warning: import java.lang.reflect.Field; public class FinalTest { static class Config { final String env = "prod"; } public static void main(String[] args) throws Exception { Config cfg = new Config(); System.out.println("Before: " + cfg.env); // prod Field f = Config.class.getDeclaredField("env"); f.setAccessible(true); f.set(cfg, "dev"); // WARNING issued in Java 26 System.out.println("After: " + cfg.env); // dev } } Impact on Developers: Review your logs after upgrading to Java 26. If using frameworks (like serialization or ORM libraries) that mutate final fields via reflection, test carefully. In a future Java version, this will throw an InaccessibleObjectException by default. Plan your migration accordingly. JEP 526: Lazy Constants (Second Preview) Lazy Constants were introduced in Java 25 as Stable Values (JEP 502). In Java 26, the API has been significantly simplified and renamed to Lazy Constants . What Are Lazy Constants? Lazy Constants are objects that hold unmodifiable data but can be initialized lazily (deferred to first use). Once initialized, the JVM treats them as true constants enabling the same JVM optimizations (like Constant Folding) as final fields, while allowing initialization at any time. Why Not Traditional Approaches? Eager Initialization (traditional final static): // Initialized at class-load time, even if never used private static final Logger LOGGER = Logger.getLogger("com.app"); Double-Checked Locking (verbose, error-prone): private static volatile Logger logger; static Logger getLogger() { if (logger == null) { synchronized (Application.class) { if (logger == null) { logger = Logger.getLogger("com.app"); } } } return logger; } With Lazy Constants (Java 26) Example#1: Basic LazyConstant: import java.lang.LazyConstant; public class Application { // Supplier called only on first access; thread-safe, constant-folded after private static final LazyConstant<Logger> LOGGER = LazyConstant.of(() -> Logger.getLogger("com.app")); static void logInfo(String msg) { LOGGER.get().info(msg); // First call initializes; subsequent calls are instant } public static void main(String[] args) { logInfo("App started"); // Logger created here (lazy) logInfo("Processing.."); // Instant (already initialized) } } Example#2: Lazy List: // Each element is initialized on first access List<String> items = List.ofLazy(100, index -> "item-" + index); System.out.println(items.get(0)); // Initializes index 0 only System.out.println(items.get(0)); // Returns cached constant Example#3: Lazy Map: Set<Locale> locales = getSupportedLocales(); Map<Locale, ResourceBundle> bundles = Map.ofLazy(locales, this::loadResourceBundle); // Bundle for ENGLISH loaded only when first accessed bundles.get(Locale.ENGLISH).getString("greeting"); Key Changes from Java 25 (Stable Values → Lazy Constants) Aspect Java 25 (Stable Values) Java 26 (Lazy Constants) Class Name StableValue<T> LazyConstant<T> Creation StableValue.of() LazyConstant.of(supplier) List Factory StableValue.list(…) List.ofLazy(…) Map Factory StableValue.map(…) Map.ofLazy(…) Low-level methods orElseSet(), setOrThrow(), trySet() Removed Null values Allowed Throws NullPointerException JEP 525: Structured Concurrency (Sixth Preview) The Structured Concurrency API was first previewed in JDK 21 and has evolved through each subsequent release. In JDK 26, it continues as a sixth preview with several refinements to the Joiner interface and StructuredTaskScope . What Is Structured Concurrency? Structured Concurrency treats related concurrent tasks as a single unit of work. All subtasks begin and end inside a single code block, errors from any subtask cancel siblings automatically, and no thread leaks occur. Key Changes in Java 26 (JEP 525) The Joiner interface now has an onTimeout() method (alongside onFork() and onComplete()). This method is called by StructuredTaskScope.join() on timeout and throws a TimeoutException by default, but can be overridden. Joiner.allSuccessfulOrThrow() now returns a list of results instead of a stream of subtasks. Joiner.allUntil(…) returns a list of subtasks instead of a stream. anySuccessfulResultOrThrow() was renamed to anySuccessfulOrThrow(). The configuration parameter type was changed from Function<Configuration, Configuration> to UnaryOperator<Configuration>. Example#1: All-or-Nothing Parallel Fetch import java.util.concurrent.StructuredTaskScope; import java.util.concurrent.StructuredTaskScope.Joiner; public class OrderService { record OrderData(String customer, List<String> items, String address) {} static OrderData fetchOrder() throws Exception { try (var scope = StructuredTaskScope.open(Joiner.allSuccessfulOrThrow())) { var customerTask = scope.fork(() -> findCustomer()); var itemsTask = scope.fork(() -> fetchItems()); var addressTask = scope.fork(() -> resolveAddress()); scope.join(); // Waits for all; auto-cancels others if any fails return new OrderData( customerTask.get(), itemsTask.get(), addressTask.get() ); } } } Example#2: Return First Successful Result AddressVerificationResponse verify(Address address) throws Exception { try (var scope = StructuredTaskScope.open( Joiner.<AddressVerificationResponse>anySuccessfulOrThrow())) {scope.fork(() -> serviceA.verify(address)); scope.fork(() -> serviceB.verify(address)); scope.fork(() -> serviceC.verify(address));return scope.join(); // Returns first success; cancels the rest } } Example#3: Timeout Handling (New in Java 26) try (var scope = StructuredTaskScope.open(Joiner.allSuccessfulOrThrow())) { var task1 = scope.fork(() -> slowService.call()); var task2 = scope.fork(() -> fastService.call()); scope.join(Duration.ofSeconds(5)); // TimeoutException if not done in 5s return new Result(task1.get(), task2.get()); } JEP 524: PEM Encodings of Cryptographic Objects (Second Preview) PEM (Privacy-Enhanced Mail) is a widely-used text-based format for encoding cryptographic objects like certificates, private keys, and public keys. This JEP was first introduced as JEP 470 in Java 25. In Java 26, it is re-proposed as a second preview with the following changes : PEMRecord was renamed to PEM. PEMEncoder and PEMDecoder now support encryption/decryption of KeyPair and PKCS8EncodedKeySpec objects. Example of a PEM -----BEGIN CERTIFICATE----- MIIDtzCCAz2gAwIBAgISBUCeYELtjMmr4FAIqHapebbFMAoGCCqGSM49BAMDMDIx ... -----END CERTIFICATE----- Before (Old Approach: Very Verbose) // Reading an encrypted private key before JEP 524 String encryptedPem = ...; String passphrase = ...; String base64 = encryptedPem .replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "") .replace("-----END ENCRYPTED PRIVATE KEY-----", "") .replaceAll("[\\r\\n]", ""); byte[] bytes = Base64.getDecoder().decode(base64); EncryptedPrivateKeyInfo info = new EncryptedPrivateKeyInfo(bytes); SecretKeyFactory skf = SecretKeyFactory.getInstance(info.getAlgName()); PBEKeySpec spec = new PBEKeySpec(passphrase.toCharArray()); Key pbeKey = skf.generateSecret(spec); Cipher cipher = Cipher.getInstance(info.getAlgName()); cipher.init(Cipher.DECRYPT_MODE, pbeKey, info.getAlgParameters()); PrivateKey privateKey = KeyFactory.getInstance("RSA") .generatePrivate(info.getKeySpec(cipher)); After (With JEP 524: Clean and Concise) Example#1: Decode an encrypted private key: PrivateKey privateKey = PEMDecoder.of() .withDecryption(passphrase.toCharArray()) .decode(encryptedPem, PrivateKey.class); Example#2: Encode a KeyPair to PEM (new in Java 26): KeyPair kp = KeyPairGenerator.getInstance("RSA").generateKeyPair(); // Encode to PEM string String pem = PEMEncoder.of().encodeToString(kp); System.out.println(pem); Example#3: Encrypt a private key into PEM with password: String encryptedPem = PEMEncoder.of() .withEncryption("mySecret".toCharArray()) .encodeToString(kp.getPrivate()); // Decrypt back PrivateKey restored = PEMDecoder.of() .withDecryption("mySecret".toCharArray()) .decode(encryptedPem, PrivateKey.class); System.out.println("Algorithm: " + restored.getAlgorithm()); // RSA JEP 529: Vector API (Eleventh Incubator) The Vector API allows Java developers to write high-performance, portable, vectorized code that leverages SIMD (Single Instruction, Multiple Data) hardware capabilities on modern CPUs (SSE, AVX, AVX-512, NEON, SVE). It has been incubating since JDK 16 . In JDK 26, the Vector API continues its incubation as its eleventh version with no substantial changes. It is waiting for Project Valhalla’s value classes before moving to preview. The first Early-Access Build of Project Valhalla was made available on October 10, 2025 . Example: Vectorized Array Addition import jdk.incubator.vector.*; public class VectorDemo { static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED; static float[] addVectors(float[] a, float[] b) { float[] c = new float[a.length]; int i = 0; // Vectorized loop — processed in SIMD batches int upperBound = SPECIES.loopBound(a.length); for (; i < upperBound; i += SPECIES.length()) { FloatVector va = FloatVector.fromArray(SPECIES, a, i); FloatVector vb = FloatVector.fromArray(SPECIES, b, i); va.add(vb).intoArray(c, i); } // Scalar tail for remaining elements for (; i < a.length; i++) { c[i] = a[i] + b[i]; } return c; } public static void main(String[] args) { float[] a = {1.0f, 2.0f, 3.0f, 4.0f}; float[] b = {5.0f, 6.0f, 7.0f, 8.0f}; float[] result = addVectors(a, b); // [6.0, 8.0, 10.0, 12.0] for (float v : result) System.out.println(v); } } To compile and run: javac --add-modules jdk.incubator.vector -d out VectorDemo.java java --add-modules jdk.incubator.vector -cp out VectorDemo JEP 516: Ahead-of-Time Object Caching with Any GC In Java 24, Ahead-of-Time (AOT) Class Loading & Linking was introduced, allowing classes to be stored in a binary cache for faster startup (up to 42% improvement in tests). However, the cache was previously only compatible with G1, Serial GC, or Parallel GC and could not be used with ZGC, or if Compressed OOPs were disabled . What Changed in Java 26? JEP 516 introduces a GC-independent (“streamable”) cache format. Instead of mapping objects directly to heap in a GC-specific binary layout, objects are stored by logical index and streamed onto the heap at startup in GC-specific format . The streamable format is used when: ZGC is active. Compressed OOPs are disabled (-XX:-UseCompressedOops). Heap size exceeds 32 GB. -XX:+AOTStreamableObjects is explicitly set. Otherwise (G1/Serial/Parallel, heap ≤ 32 GB), the faster GC-specific format is used automatically. Example: AOT Cache with ZGC # Training run (generate cache) java -XX:AOTCacheOutput=app.aot -jar yourapp.jar # Production run with ZGC (now supported in Java 26!) java -XX:+UseZGC -XX:AOTCache=app.aot -jar yourapp.jar # Production run with G1 (the default GC) java -XX:AOTCache=app.aot -jar yourapp.jar Note: Tests on Spring Boot’s PetClinic with ~21,000 pre-loaded classes showed ~41% startup improvement. JEP 522: G1 GC: Improve Throughput by Reducing Synchronization G1 is the default Garbage Collector of the JVM, designed to balance high throughput with low latency. G1 uses Write Barriers to track references between heap regions via a Card Table. Continuously optimizing the Card Table while application threads write to it required synchronization — a significant overhead . The Improvement in Java 26 A second Card Table is introduced. Application threads write to one table while the GC’s optimization thread works on the other. When the active table becomes too large, the tables are swapped. This eliminates the need for synchronization between application and GC threads . Throughput gain: 5–15% on workloads with heavy object-reference modifications. Memory overhead: Only 0.2% additional native memory (equal to the size of one Card Table). No code changes required — improvement is automatic after upgrading to Java 26. Example: Measure the Improvement # Before Java 26 java -XX:+UseG1GC -XX:+PrintGCDetails -jar your-app.jar # After upgrading to Java 26 (same flags, fewer GC pauses) java -XX:+UseG1GC -XX:+PrintGCDetails -jar your-app.jar # Observe: higher throughput, lower synchronization overhead JEP 517: HTTP/3 for the HTTP Client API The HttpClient API (available since Java 11) now supports HTTP/3, the latest version of the HTTP protocol introduced in 2022. HTTP/3 uses the QUIC transport protocol for faster handshakes, no head-of-line blocking, and improved performance in high-packet-loss networks . By default, HttpClient still uses HTTP/2 for compatibility as only about one-third of all websites currently support HTTP/3. HTTP/3 must be explicitly opted-in . Example#1: Default HTTP/2 (unchanged behavior) HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://www.example.com/api")) .build(); HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); System.out.println(response.body()); Example#2: Opt-in to HTTP/3 HttpClient h3Client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_3) .build(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://www.example.com/api")) .build(); HttpResponse<String> response = h3Client.send(request, BodyHandlers.ofString()); // Falls back to HTTP/2 transparently if server doesn't support HTTP/3 System.out.println("Status: " + response.statusCode()); System.out.println("Body: " + response.body()); Example#3: Stream a Large File Upload (ofFileChannel: new in Java 26) Previously, sending a large file required loading it entirely into memory. Java 26 adds BodyPublishers.ofFileChannel() to stream files directly without RAM bloat : try (FileChannel channel = FileChannel.open(Path.of("large-file.bin"), READ); HttpClient client = HttpClient.newHttpClient()) { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.example.com/upload")) .POST(BodyPublishers.ofFileChannel(channel, 0, channel.size())) .build(); HttpResponse<Void> response = client.send(request, BodyHandlers.discarding()); System.out.println("Upload status: " + response.statusCode());} JEP 504: Remove the Applet API The Applet API (the java.applet package, javax.swing.JApplet, java.beans.AppletInitializer, etc.) has been completely removed in Java 26 . History of Removal Java Version Action Java 9 (2017) Applet API marked as @Deprecated Java 17 (2021) Marked as @Deprecated(forRemoval = true) Java 24 (2024) Security Manager (related to Applets) disabled Java 26 (2026) Applet API fully removed What Was Removed? java.applet.Applet java.applet.AppletStub java.applet.AppletContext java.applet.AudioClip javax.swing.JApplet java.beans.AppletInitializer Impact on Developers Applications running on modern Java (no applets) are unaffected. If you have legacy applet code, migrate as follows: UI containers: Use AWT components or Swing directly. Audio playback: Use javax.sound.SoundClip (added in Java 25). Compile errors after upgrade? That’s a sign of successful modernization! Also Removed: Thread.stop() In addition to JEP 504, Thread.stop() deprecated since Java 1.2 in December 1998, and throwing UnsupportedOperationException since Java 20, is completely removed in Java 26 . // Java 26: This no longer compiles thread.stop(); // Compile error: cannot find symbol Use thread.interrupt() and cooperative interruption patterns instead. Other Notable Changes in Java 26 Dark Theme in Javadoc Java 26 adds a dark mode toggle (☀/🌙 icon) to Javadoc-generated API documentation. No code change needed. It affects all newly generated Javadoc output . Default Initial Heap Size Trimmed Down Previously, the JVM allocated 1/64th (≈ 1.5%) of physical RAM as the initial heap. On a 64 GB machine, this was 1 GB, even for a Hello World app. Java 26 reduces the default to 0.2% (1/500) of physical RAM, giving 128 MB on a 64 GB machine. This speeds up startup for most small and medium applications . Virtual Threads Improvements Virtual threads no longer pin their carrier thread when waiting for a class initializer to complete (a scenario where another thread is currently initializing the class). This resolves the last major pinning scenario: the previous synchronized pinning was already fixed in Java 24. Unicode 17.0 Support Java 26 upgrades Unicode support to version 17.0, ensuring that character-processing classes like String and Character handle all newly introduced characters and code blocks . Complete Table of JEPs in Java 26 New Features JEP Feature Status Category 500 Prepare to Make Final Mean Final Final (Warning) Language / Security 504 Remove the Applet API Final (Removal) Clean-up 516 Ahead-of-Time Object Caching with Any GC Final Performance 517 HTTP/3 for the HTTP Client API Final Client Library 522 Improve Throughput by Reducing Synchronization Final GC / Performance 524 PEM Encodings of Cryptographic Objects Second Preview Security 525 Structured Concurrency Sixth Preview Concurrency 526 Lazy Constants Second Preview Core Libs 529 Vector API Eleventh Incubator Performance 530 Primitive Types in Patterns, instanceof, and switch Fourth Preview Syntax Conclusion Java 26 is a focused, incremental release that polishes and progresses the major investments made in Java 25. The promotion of AOT Object Caching to work with any GC (including ZGC), HTTP/3 support, and G1 GC throughput gains are production-ready highlights. Lazy Constants (formerly Stable Values) has received a much cleaner API, and the final-field mutation warnings pave the road toward true immutability. For every feature, refer to its JEP if deeper technical details are needed. Sources: https://openjdk.org/projects/jdk/26/ https://www.oracle.com/news/announcement/oracle-releases-java-26-2026-03-17/ For other version’s features, kindly go through Java Features After Java 8. Kindly visit Java 24 new Features With Examples Kindly visit Java 25 new Features With Examples Also check ‘How to Configure JDK specific version in Eclipse or STS?‘ Related