Java 26 New Features With Examples Core Java java Java 26 by devs5003 - April 12, 2026September 26, 20262 Last Updated on September 26th, 2026 Java 26 New Features With Examples – What’s New In JDK 26 After Java 25, Java 26 arrives as the next release in Oracle’s six-month cadence. Unlike Java 25, it is not an LTS version, but it still ships ten JEPs that touch the language, the standard library, security, and the JVM itself. In this article, we will walk through every Java 26 new feature with practical, runnable examples. You may also go through Java 21 New Features With Examples if you want to catch up on the last LTS release first. 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:Java 25 and Earlier vs. Java 26JEP 500: Prepare to Make Final Mean FinalOutput (default warn mode in Java 26):Comparison: Java 25 and Earlier vs. Java 26JEP 526: Lazy Constants (Second Preview)What Are Lazy Constants?Manual Lazy Initialization vs. LazyConstantKey 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?JEP 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 FeaturesCommon Mistakes to AvoidConclusionRelated 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. Java 25 and Earlier vs. Java 26 Feature Java 25 and Earlier Java 26 (JEP 530, preview) Primitive patterns in instanceof Not supported Supported, with safety checks Primitive catch-all in switch Only default case int i style catch-all Dominance checking Looser Tighter, catches more compile errors Requires flag N/A –enable-preview JEP 500: Prepare to Make Final Mean Final Since Java 5, code has been able to reach into a class and mutate a final field using deep reflection, Field.setAccessible(true) followed by Field.set(…). This quietly broke the promise that final means “never changes after construction.” JEP 500 is the first step in fixing that. In JDK 26, mutating a final instance field through reflection still works, but the JVM now prints a runtime warning the first time it happens per module. A future release will turn that warning into a thrown IllegalAccessException by default. public class Account { private final double balance; public Account(double balance) { this.balance = balance; } public double getBalance() { return balance; } } import java.lang.reflect.Field; public class FinalFieldMutationDemo { public static void main(String[] args) throws Exception { Account account = new Account(500.0); System.out.println("Before: " + account.getBalance()); Field balanceField = Account.class.getDeclaredField("balance"); balanceField.setAccessible(true); balanceField.set(account, 999999.0); System.out.println("After: " + account.getBalance()); } } Output (default warn mode in Java 26): Before: 500.0 WARNING: A final field of class Account has been mutated reflectively by class FinalFieldMutationDemo WARNING: Use --enable-final-field-mutation to avoid this warning WARNING: Mutating final fields will be blocked in a future release unless final field mutation is enabled After: 999999.0 You get three levels of control over this behavior: –illegal-final-field-mutation=warn (the JDK 26 default): mutation succeeds, a warning is printed once per module. –illegal-final-field-mutation=debug: same as warn, plus a full stack trace for every mutation. –illegal-final-field-mutation=deny: throws IllegalAccessException immediately, useful for CI pipelines that want to catch this early. –enable-final-field-mutation=ALL-UNNAMED (or a specific module name): explicitly allow reflective final-field mutation for that module, silencing the warning. Comparison: Java 25 and Earlier vs. Java 26 Behavior Java 25 and Earlier Java 26 Reflective mutation of final field Silently succeeds Succeeds, but warns (default warn mode) Ability to suppress via –add-opens N/A, mutation just worked Does not bypass the warning by itself CI enforcement Not possible –illegal-final-field-mutation=deny throws immediately Long-term direction final was advisory final is moving toward being enforced 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. import java.lang.LazyConstant; public class LazyConstantDemo { private static final LazyConstant<String> GREETING = LazyConstant.of(LazyConstantDemo::loadGreeting); private static String loadGreeting() { System.out.println("Computing the greeting only once..."); return "Hello from Java 26!"; } public static void main(String[] args) { System.out.println("Before first access"); System.out.println(GREETING.get()); System.out.println(GREETING.get()); } } Output: Before first access Computing the greeting only once... Hello from Java 26! Hello from Java 26! Notice the supplier only ran once, even though get() was called twice. Lazy lists and maps are also available, moved onto List and Map themselves for discoverability: import java.util.List; public class LazyListDemo { public static void main(String[] args) { List<Integer> squares = List.ofLazy(5, i -> { System.out.println("Computing square for index " + i); return i * i; }); System.out.println(squares.get(2)); System.out.println(squares.get(2)); } } Output: Computing square for index 2 4 4 This is a preview feature, so it needs the –enable-preview flag (see the section on enabling preview features below). Manual Lazy Initialization vs. LazyConstant Aspect Manual (double-checked locking) LazyConstant (Java 26) Thread safety You write it yourself Built in Boilerplate High (volatile field, null checks) None JVM constant-folding Not guaranteed Supported when held in a final field Risk of bugs Real, easy to get wrong Minimal 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.IntVector; import jdk.incubator.vector.VectorSpecies; public class VectorApiDemo { static final VectorSpecies<Integer> SPECIES = IntVector.SPECIES_256; public static void main(String[] args) { int[] a = {1, 2, 3, 4, 5, 6, 7, 8}; int[] b = {10, 20, 30, 40, 50, 60, 70, 80}; int[] result = new int[a.length]; IntVector va = IntVector.fromArray(SPECIES, a, 0); IntVector vb = IntVector.fromArray(SPECIES, b, 0); va.add(vb).intoArray(result, 0); System.out.println(java.util.Arrays.toString(result)); } } To compile and run: javac --add-modules jdk.incubator.vector -d out VectorApiDemo.java java --add-modules jdk.incubator.vector -cp out VectorApiDemo Output [11, 22, 33, 44, 55, 66, 77, 88] Because this is still an incubator module, it lives outside the java.base module and needs –add-modules jdk.incubator.vector at both compile time and run time. JEP 516: Ahead-of-Time Object Caching with Any GC Java 24 introduced an ahead-of-time (AOT) object cache to speed up JVM startup by pre-initializing objects during a training run and reusing them on later runs. The catch was that the cache stored objects in a format tied to a specific garbage collector’s memory layout, which made it incompatible with ZGC. JEP 516 fixes that by storing cached objects as GC-neutral logical indices instead of raw memory addresses, so the same AOT cache now works with Serial, Parallel, G1, and ZGC alike. # Step 1: record a training run java -XX:AOTCacheOutput=app.aot -XX:+AutoCreateSharedArchive -jar your-application.jar # Step 2: run again using the cache, even with ZGC java -XX:+UseZGC -XX:AOTCache=app.aot -jar your-application.jar This means teams that wanted ZGC’s sub-millisecond pause times previously had to give up AOT-cache startup gains, and vice versa. Java 26 removes that trade-off entirely. 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. 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 Common Mistakes to Avoid Assuming Java 26 is an LTS release and planning multi-year production support around it. It is not; support ends after six months. Forgetting –enable-preview on both the javac and java commands. Preview code fails at run time with a UnsupportedClassVersionError-style message if only one side has the flag. Treating the Vector API’s package name (jdk.incubator.vector) as stable. Incubator modules can be renamed or restructured before they graduate. Ignoring JEP 500’s warnings in logs. They are quiet today, but they are a direct signal that a future JDK will throw instead of warn. Deploying code that still imports java.applet and expecting it to keep compiling; JEP 504 removes it completely. 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