Java continues to evolve, releasing new features in each version. In Java 24, we’ve seen the consolidation of several previews introduced in previous versions: more powerful primitive patterns and a continued push toward modern, clean, and powerful code.
If you're still using Java 8, 11, or even Java 17 and haven’t moved to 21 yet, it’s time to upgrade before the leap becomes too great.
What’s New in Java 24
It’s important to remember that some of the features we’re discussing are still in preview. This means you’ll need to enable them using the –enable-preview flag for the Java 24 compiler to recognize them.
1 Primitive Patterns (preview): primitives deserve love too (JEP 488)
Java 24 moves forward with primitive patterns, delivering a second preview (you can read about the first preview here and the updates in this second preview). It expands the capabilities of instanceof and switch by enabling pattern matching with primitive types, such as int, boolean, and double, without the need to convert them to Integer or Double.
How does this look in code? Previously, you'd write something like:
Object o = 42;
if (o instanceof Integer i && i > 10) {
System.out.println("Whole number greater than 10");
}
Now, in Java 24, we can perform that comparison directly using the primitive type:
Object o = 42;
if (o instanceof int i && i > 10) {
System.out.println("Primitive integer greater than 10");
}
2 Stream Gatherers: Streams, assemble! JEP 485
Streams and Java have gone hand in hand since Java 8, and Java 24 is no exception. With the (now final) implementation of the Gatherers API, Java 24 enables more advanced operations than those offered by Collectors, such as custom groupings, sliding windows, or batching. Let’s illustrate this with an example:
Stream.of(1, 2, 3, 4, 5, 6)
.gather(Gatherers.windowFixed(3))
.forEach(System.out::println);
[1, 2, 3], [4, 5, 6]
A new gather method is introduced, coming from the java.util.stream.Gatherer interface. It provides a set of methods that we’ll be able to use in future implementations as intermediate operations within the stream pipeline:
- windowFixed(int size): groups elements into fixed-size windows of the given size.
- windowSliding(int size, int step): creates sliding windows, with defined size and step.
- scan(seed, accumulator): returns a sequence of partial accumulations, similar to reduce, but emitting the result at each step:
import java.util.stream.Stream;
import java.util.stream.Gatherers;
Stream.of(1, 2, 3, 4)
.gather(Gatherers.scan(0, Integer::sum))
.forEach(System.out::println);
The output would be as follows:
1
3
6
10
- fold(seed, accumulator, combiner): performs an accumulative reduction, similar to the previously mentioned reduce, but with more control over the process. Here's an example:
import java.util.stream.Stream;
import java.util.stream.Gatherers;
Stream.of("Java", "24", "es", "potente")
.gather(Gatherers.fold(
"", // seed
(acc, s) -> acc.isEmpty() ? s : acc + " - " + s, // accumulator
(a, b) -> a + " - " + b // parallel merge combinator
))
.forEach(System.out::println);
The output would be as follows:
Java - 24 - is - powerful
These are some of the most important methods from the library, but they are not the only ones available. Since we are still in preview versions, it’s important to get familiar with them gradually. That’s why we invite you to experiment and take a look directly at the official Java 24 documentation.
4 Scoped Values (preview). A modern replacement for ThreadLocal. JEP487
As the fourth preview, Java 24 continues advancing with Scoped Values — a new, safer, immutable, and lightweight way to pass data across virtual threads.
In previous versions, these threads could be modified — that is, they lacked immutability, could lead to memory leaks, and didn’t always offer optimal performance. Here's an example of how it used to be and how it evolves:
ThreadLocal<String> userId = new ThreadLocal<>();
userId.set("abc-123");
//It must be cleaned (removed) manually, which ends up being dangerous.
Now with Java 24, we will have the following:
ScopedValue<String> USER_ID = ScopedValue.newInstance();
ScopedValue.where(USER_ID, "abc-123").run(() -> {
System.out.println("Current user: " + USER_ID.get());
});
We can see that now the value is immutable, which provides greater safety and is automatically scoped, without needing to call .remove() as we had to do before to free up memory.
This improvement can be helpful to pass information like user ID, session tokens, request configuration, etc. But remember, it is still a preview feature (don’t forget: –enable-preview to make it work).
4 Other changes: it’s not all about coding
Java 24 also brings other enhancements and updates beyond programming style. Some of the most noteworthy are:
- Ahead-of-Time Class Loading. With JEP 483, JVM startup is improved by allowing class loading and linking to happen before execution instead of at runtime as before. This leads to faster startups, which is especially useful for microservices or containers requiring immediate response times.
- New official (and final!) API for .class files. JEP 484 introduces the ability to work directly and natively with class files using the JDK, eliminating the need for external tools like ASM or BCEL. While this might not be directly used by most dev teams, it’s a huge step forward for frameworks like Spring or Hibernate that depend on those tools.
- Virtual Threads without pinning. JEP 491 proposes a new mechanism for synchronizing virtual threads without pinning (e.g. when using synchronized), meaning they don’t have to be bound to a specific platform thread. This allows Java libraries that use synchronized to scale more efficiently and free up threads more easily.
5 Java 24 embraces transformation
It’s evident that, starting from a few versions ago (especially from Java 14), Java is moving towards a more declarative, safer, and expressive programming model, evolving into a more modern language — all while staying true to its roots.
As developers, we can’t really complain. Java continues to evolve and adapt to modern development practices, paving the way for Java 25 and beyond in terms of style, clean coding practices, and—most importantly in today’s world—code security. We’ll keep a close eye on upcoming updates and news about future Java releases. What does the future hold?
References
Comments are moderated and will only be visible if they add to the discussion in a constructive way. If you disagree with a point, please, be polite.
Tell us what you think.