Home | Send Feedback | Share on Bluesky |

Small changes in Java 26

Published: 17. March 2026  •  java

In this blog post, I take a look at the smaller changes in Java 26. By a smaller change, I mean, for example, a new method added to an existing class or a platform change that affects day-to-day development. This article does not cover preview or incubator features.

Java 26 ships with a few noteworthy runtime improvements such as JEP 516: Ahead-of-Time Object Caching with Any GC and JEP 522: G1 GC: Improve Throughput by Reducing Synchronization.

Java 26 (March 2026)

If you ignore preview and incubator work, Java 26 is a fairly focused release. There is no new language syntax for everyday code, but there are still a few interesting platform changes. The most visible features are JEP 500: Prepare to Make Final Mean Final, JEP 504: Remove the Applet API, and JEP 517: HTTP/3 for the HTTP Client API. Java 26 also upgrades the platform to Unicode 17.0.


JEP 500: Prepare to Make Final Mean Final

Java has allowed code to mutate final fields through deep reflection for a long time. That has always felt a bit wrong because final is supposed to communicate immutability, but reflective code could still break that assumption.

Java 26 starts tightening this up. Code that mutates final fields with reflection now triggers warnings by default, and a future release is expected to deny this by default.

  final class Configuration {
    final int port = 8080;
  }

  Configuration configuration = new Configuration();

  Field field = Configuration.class.getDeclaredField("port");
  field.setAccessible(true);
  field.set(configuration, 9090);

  System.out.println(configuration.port);
  // warning in Java 26
  // WARNING: Use --enable-final-field-mutation=ALL-UNNAMED to avoid a warning
  // WARNING: Mutating final fields will be blocked in a future release unless final field mutation is enabled

If you want to find problematic code paths early, Java 26 adds useful runtime switches such as --illegal-final-field-mutation=deny and --illegal-final-field-mutation=debug. This is especially relevant if your application depends on serialization libraries, mocking tools, or dependency injection frameworks that still try to write into final fields reflectively.


JEP 517: HTTP/3 for the HTTP Client API

The java.net.http.HttpClient API now supports HTTP/3. This is an opt-in feature that you can enable by setting the client version to HTTP_3. The client will then use HTTP/3 if the server supports it and otherwise fall back to HTTP/2 or HTTP/1.1. By default, the client still uses HTTP/2 or HTTP/1.1, so existing applications are not affected by this change until they explicitly opt in.

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

  HttpRequest request = HttpRequest.newBuilder(URI.create("https://openjdk.org/"))
      .GET()
      .build();

  HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

  System.out.println(response.version());
  System.out.println(response.statusCode());

Check out this blog post for more detailed information about the HTTP/3 implementation.

Java 26 also adds some supporting HTTP client APIs, for example HttpRequest.Builder.setOption for request options.


Another small addition is HttpRequest.BodyPublishers.ofFileChannel(FileChannel, long, long). It lets you upload only a region of a file without first copying it into memory yourself.

  try (FileChannel channel = FileChannel.open(Path.of("video.mp4"), StandardOpenOption.READ)) {
    HttpRequest request = HttpRequest.newBuilder(URI.create("https://example.com/upload"))
        .POST(HttpRequest.BodyPublishers.ofFileChannel(channel, 0, 1024 * 1024))
        .build();

    client.send(request, HttpResponse.BodyHandlers.discarding());
  }

That is handy for chunked or resumable uploads.


UUID version 7 support

Java 26 adds UUID.ofEpochMillis(long), which creates a version 7 UUID from a Unix epoch timestamp.

  UUID id = UUID.ofEpochMillis(System.currentTimeMillis());

  System.out.println(id);
  System.out.println(id.version()); // 7

Process is now AutoCloseable

Process now implements AutoCloseable and gets a new close() method.

That means process handling fits much better into try-with-resources than before.

  try (Process process = new ProcessBuilder("java", "-version")
        .redirectErrorStream(true)
        .start()) {
    String output = process.inputReader().lines().collect(Collectors.joining(System.lineSeparator()));
    System.out.println(output);
  }

Unicode case folding for String

String.compareToFoldCase(String) and String.equalsFoldCase(String) add Unicode case-folding-aware comparison to String.

  String left = "Straße";
  String right = "STRASSE";

  System.out.println(left.equalsFoldCase(right));   // true
  System.out.println(left.compareToFoldCase(right)); // 0

Case folding is locale-independent and language-neutral, unlike locale-sensitive transformations such as toLowerCase() or toUpperCase(). It is intended for caseless matching, searching, and indexing.


BigInteger nth root methods

BigInteger.rootn(int) and BigInteger.rootnAndRemainder(int) are two new methods that compute the integer nth root of a BigInteger and optionally also return the remainder.

  BigInteger thousand = new BigInteger("1000");
  System.out.println(thousand.rootn(3)); // 10

  BigInteger value = new BigInteger("1001");
  BigInteger[] result = value.rootnAndRemainder(3);

  System.out.println(result[0]); // 10
  System.out.println(result[1]); // 1

This is useful in number theory code, cryptography-related utilities, and anywhere you need exact integer arithmetic without going through double.


Comparator.max and Comparator.min

Comparator.max(U, U) and Comparator.min(U, U) are tiny additions, but they make code a bit cleaner.

Instead of writing comparator.compare(a, b) >= 0 ? a : b, you can now express the intent directly.

  Comparator<String> byLength = Comparator.comparingInt(String::length);

  System.out.println(byLength.max("cat", "giraffe")); // giraffe
  System.out.println(byLength.min("cat", "giraffe")); // cat

If you want to see the complete list of changes, check out the Java 26 release notes and the API documentation.


If you want to see the small changes in previous Java releases, check out the other parts of this series: