Engineering With Java

Engineering With Java

Java 26 — All New Features, Removals & Previews

Suraj Mishra's avatar
Suraj Mishra
Dec 05, 2025
∙ Paid

📢 Get actionable Java/Spring Boot insights every week — from practical code tips to real-world use-case based interview questions.

Join 5000+ subscribers and level up your Spring & backend skills with hand-crafted content — no fluff.

First 100 paid subscribers will get the annual membership at $50/year ( 60 already converted to paid, 40 remaining )

So far we have covered 44+ real world based interview questions and will add up to 100 by end of this year.

Still not convinced? Check out the details of the past work


  1. HTTP/3 support in HttpClient

    1. Java 26 adds a new version enum:

      HttpClient.Version.HTTP_3
    2. This allows HttpClient to negotiate and use HTTP/3. HTTP/3 is not an evolution of HTTP/2 over TCP.It uses a completely new transport.

    3. HTTP/3 uses QUIC over UDP → not upgradable from HTTP/1.1 or HTTP/2.

    4. Connection-level version negotiation is non-trivial.

    5. API surface change is tiny; complexity is under the hood.

      HttpClient client = HttpClient.newBuilder()
          .version(HttpClient.Version.HTTP_3)
          .build();

    When to use

    • Latency-sensitive apps.

    • Packet-loss-heavy networks (QUIC handles loss better).

    • Services that already support HTTP/3 (e.g., Cloudflare, Google).

  2. Prepare to Make Final Mean Final

    1. Its main goal is to restore the “immutability guarantee” of final fields by default. In other words, make it so that once a final field is assigned (in a constructor or static initializer), it can’t be overwritten via reflection and related mechanisms.

    2. In Java, since JDK 5, “deep reflection” — using java.lang.reflect.Field.setAccessible(true) + Field::set(...) — has allowed code to override the final guarantee: even a final field can be changed at runtime.

    3. That undermines the semantic meaning of final: for correctness, reasoning about immutability, thread-safety, etc. Also undermines certain optimizations (like constant-folding) the JVM might perform under the assumption that final fields don’t change.

    4. JEP 500 considers the fact that allowing such unconstrained mutation was a “poor choice” and wants to align with the stricter immutability model already used for newer constructs (e.g. records, hidden classes, etc.)

    5. From JDK 26, by default, if code uses reflection (or other deep reflection APIs) to mutate a final field, the JVM will now emit a warning at runtime (instead of silently allowing it).

    6. The warning is intended to notify developers of code paths that mutate final fields, to give time to migrate.

    7. In future JDK versions, the plan is for such mutation to be disallowed by default — i.e. Field::set(...) on a final field will throw an exception rather than succeed (unless explicitly allowed)

  3. Remove the Applet API

    1. JEP 504 proposes to remove the entire java.applet API from the standard Java SE platform.

    2. It was marked Completed and targeted for JDK 26.

    3. The Applet API was deprecated for removal back in JDK 17 (2021).

    4. Web browsers no longer support Java applets — the plugin model that allowed applets to run in browsers is largely dead.

  4. Lazy Constants (Second Preview)

    1. JEP 526 introduces a new API: java.lang.LazyConstant<T> — a holder for a value that is computed lazily (on first demand) but then becomes immutable (“constant”) thereafter.

    2. Lazy constants are treated by the JVM as “true constants,” enabling many of the same performance and optimization benefits that final fields enjoy (constant-folding, inlining, etc.) once initialized.

    3. Compared to a plain final field, lazy constants give flexibility in when initialization happens — instead of requiring initialization at construction or class-load time.

    4. Using final fields give immutability — but final forces eager initialization (at object creation or class initialization).
      In many real-world cases (e.g., heavy resources: loggers, configs, caches, service clients,) you may want to delay initialization until first use to save startup costs.

      private static final LazyConstant<Logger> LOG =
          LazyConstant.of(() -> Logger.create(MyService.class));
    5. JEP 526 is a refinement / second preview of what had been previously proposed under JEP 502 (“Stable Values”). Changes include:

      1. Renamed the API from StableValue → LazyConstant — a name better aligned with the high-level use case.

      2. Removed lower-level methods (orElseSet, setOrThrow, trySet) from the API — simplifying it to just “factory + get” style.

  5. Primitive Types in Patterns, instanceof, and switch (Fourth Preview)

    1. JEP 530 makes primitive types (int, long, float, double, boolean, etc.) first‑class participants in Java’s pattern matching, instanceof, and switch

    2. It’s a preview language feature in JDK 26.

    3. Historically, pattern matching, instanceof, and switch supported only reference types (objects), or a limited subset of primitive-like types; this JEP removes that restriction.

    4. Primitive Type Patterns in instanceof, switch, Pattern Matching

      You can now write:

      if (x instanceof int i) {
          // i is bound as int
      }
      
      switch (someLongValue) {
        case 1L -> ...
        case long l -> ...
      }
    5. instanceof Supports Primitive Types

      Instead of only instanceof SomeClass, you can test primitive‑to‑primitive safe conversions:

      int i = ...;
      if (i instanceof byte b) {  // tests if i can be losslessly cast to byte
          // b is byte
      }
    6. switch Works on All Primitive Types

User's avatar

Continue reading this post for free, courtesy of Suraj Mishra.

Or purchase a paid subscription.
© 2026 Suraj Mishra · Privacy ∙ Terms ∙ Collection notice
Start your SubstackGet the app
Substack is the home for great culture