Java 21 Other Improvements

August 3, 20264 min readUpdated 8/20/2026

Virtual threads dominated the Java 21 release notes, and several smaller additions arrived alongside them. These are the ones worth knowing about.

String templates — and why you cannot use them

Worth addressing first because you will find articles recommending them. String templates were previewed in Java 21 as a typed alternative to concatenation, then withdrawn — removed entirely in Java 23 after the design was reconsidered.

So: do not adopt them. Java still has no string interpolation, and formatted remains the answer:

class Demo {
    String message(String name, int count) {
        return "Hello %s, you have %d orders".formatted(name, count);
    }
}

This is a useful illustration of what preview features are for. They ship so that real use can expose design problems before the feature becomes permanent — and occasionally the answer is that it should not.

Sequenced collections

The other substantial API addition, covered in its own post: getFirst(), getLast() and reversed() on every ordered collection.

Key encapsulation and better randomness

class Demo {
    void run() {
        // A stream of random numbers, from a named algorithm
        RandomGenerator generator = RandomGenerator.of("Xoshiro256PlusPlus");
        System.out.println(generator.nextInt(100));

        // Still fine, and now one implementation among many
        RandomGenerator legacy = new Random(42);
        System.out.println(legacy.ints(5, 0, 10).boxed().toList());
    }
}

Java 17 introduced the RandomGenerator interface and Java 21 rounded it out. Random, SecureRandom and ThreadLocalRandom all implement it, so code can now take the interface and let the caller choose the algorithm — including several modern generators that are faster and have better statistical properties than the 1995 Random.

For anything security-related, SecureRandom remains the only correct choice; this change does not alter that.

Generational ZGC

ZGC, the low-latency collector, gained generational support. Practically: it now handles the very common case of many short-lived objects far more efficiently, which was its main weakness.

java -XX:+UseZGC -XX:+ZGenerational -jar myapp.jar

G1 is still the right default for almost everything. ZGC is worth investigating if you have a large heap and a measured pause-time problem — and "measured" is doing real work in that sentence, because changing collectors on a hunch usually makes things worse.

The foreign function and memory API

Previewing in 21 and standard in 22, this replaces JNI for calling native code and ByteBuffer for working with off-heap memory. It matters to a narrow audience and it is worth recognising:

// java.lang.foreign — allocate memory outside the Java heap, deterministically freed
// try (Arena arena = Arena.ofConfined()) {
//     MemorySegment segment = arena.allocate(1024);
//     segment.set(ValueLayout.JAVA_INT, 0, 42);
//     System.out.println(segment.get(ValueLayout.JAVA_INT, 0));   // 42
// }   // memory released here, not whenever the GC gets round to it

The two problems it solves are real ones. JNI required writing C glue code and was easy to get catastrophically wrong; ByteBuffer capped out at 2GB and freed its memory whenever the collector felt like it. The new API is pure Java, bounded by an Arena whose lifetime you control, and checked at runtime rather than trusting you.

If you have never needed to call a C library from Java, you can ignore this entirely. If you have, it is a substantial improvement over what came before.

Deprecations worth noticing

  • Finalization is deprecated for removal. Object.finalize() has been discouraged for years; Java 18 formally deprecated it and it can now be disabled with --finalization=disabled. If any of your classes override finalize, replace it with AutoCloseable and try-with-resources — the exception handling post covers the pattern.
  • The security manager is disabled by default. Java 17 deprecated it; from 18 it must be explicitly re-enabled. Almost nothing uses it.
  • Dynamic agent loading now warns. Tools that attach to a running JVM — some profilers and mocking libraries — print a warning, and a future release will require an explicit flag.

Unnamed classes and instance main

Also previewed in 21: the ability to write a program without a class declaration or a static main. It was refined over several releases and finalised in Java 25, so it is covered in Module Imports and Simple Source Files rather than here — mentioned only so you know the Java 21 preview version is not the final syntax.

Smaller API additions

class Demo {
    void run() {
        // Splitting on a character, without regex overhead
        System.out.println("a,b,c".split(",").length);        // 3

        // Emoji and code point awareness
        System.out.println("hello".codePoints().count());     // 5

        // Character.isEmoji and friends, added in 21
        System.out.println(Character.isLetter('a'));          // true
    }
}

Java 21 also added Math.clamp, which replaces a very common two-call idiom:

class Demo {
    void run() {
        int value = 150;

        int old = Math.max(0, Math.min(100, value));    // the idiom everyone wrote
        int now = Math.clamp(value, 0, 100);            // Java 21

        System.out.println(old + " " + now);            // 100 100
    }
}

It also throws if the bounds are the wrong way round, which the nested version silently does not — Math.max(100, Math.min(0, value)) returns nonsense rather than complaining.

What Java 21 is, overall

It is the LTS that made Java competitive on concurrency again. Everything else here is small; virtual threads are the reason the release matters, and they are a genuine change in how server-side Java is written rather than a syntax convenience.

Alongside that, pattern matching for switch and record patterns completed the data-modelling story that records and sealed classes began in 17. Those four features are designed to be used together, which is why they read oddly in isolation and well in combination.

Next

The Java 21 migration guide is next — what to check when moving from 17.