Java continues to actively evolve! The new Java 25 is just around the corner. This release brings changes to boilerplate code and constructors, and discontinues support for deprecated systems. You can read about these updates and other new features in this article.
How often do you use ThreadLocal
? No no no, another question: have you even heard of it? Shortly, ThreadLocal
is a container that stores a unique variable value for each thread.
At first glance, it's hard to tell what problems might arise, but they do exist and are quite serious:
In this version, Java introduces a new mechanism, ScopedValue
, that can help solve these problems.
Here's the example:
ScopedValue<FrameworkContext> CONTEXT = ScopedValue.newInstance();
void serve(Request request, Response response) {
var context = createContext(request);
ScopedValue.where(CONTEXT, context)
.run(() -> handle(request, response));
}
void handle(Request request, Response response) {
// ....
readKey(key);
// ....
}
Key readKey(String key) {
var context = CONTEXT.get();
var connection = getConnection(context);
return connection.readKey(key);
}
Here, ScopedValue#where
sets up the environment, and #run
executes Application#handle
with this context. The context is cleared immediately after the method exits.
Now, Java brings a unified, built-in approach for handling KDF algorithms.
KDF safely generates cryptographic algorithms from primary secrets (passwords and master keys). You can learn more here.
Previously, different algorithms required different approaches, but now everything is simple:
KDF hkdf = KDF.getInstance("HKDF-SHA256");
AlgorithmParameterSpec params =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial)
.addSalt(salt)
.thenExpand(info, 32);
SecretKey key = hkdf.deriveKey("AES", params);
All the dancing around with SecretKeyFactory
and PBEKeySpec
for PBKDF2 or fussing with HKDFParameterSpec
is now a relic of the past. One API to rule them all.
New syntax features
Tada! It's a perfect gift for devs tired of endless import lists. Now you can import an entire module with a single line. Such an elegant feature is a good combination with JEP 512 that allows writing succinct, almost script-like scenarios without losing strong typing.
Here's how it looks when combined with compact main
methods:
import module org.slf4j;
Logger log = LoggerFactory.getLogger("MyAppLogger");
void main() {
Stream<String> stringStream = ThreadLocalRandom.current().longs(10)
.mapToObj(String::valueOf);
for (var string : (Iterable<String>) (stringStream::iterator)) {
log.info(string);
}
}
Perhaps this is one of the most noticeable changes for every developer. The fight against boilerplate has reached a new level.
Finally, you can forget about the mandatory public static void main(String[] args)
for simple scripts and utilities. The language feels friendlier for beginners and more pleasant for experienced developers who value concise code. Under the hood, this works by implicitly declaring the class and auto-importing the java.base
module. For extra convenience, the release adds a new IO
class to java.lang
with basic input/output methods.
Boilerplate code is repetitive code that has to be written manually to solve standard tasks, but it adds little to no unique logic.
Life hack! Do you have a headache?
void main() {
IO.println("I'm still java. For now");
}
Just do it twice a week... and your headaches vanish.
Here's the link to JEP
This is what many have dreamed of for years: now developers can write code before calling super()
or this()
. And this isn't just syntactic sugar—it's a real simplification of object initialization logic. After its preview in Java 22, the feature has been enhanced to not only validate arguments but also safely initialize fields, which solves classic execution order issues. By the way, one of PVS-Studio diagnostic rules was designed to detect such issues :)
Let's not point fingers, but it has always been a language limitation, not a problem with bytecode, JVM, or anything else.
Sandbox(String key) {
if (!key.startsWith("sandbox:"))
throw new IllegalArgumentException(key);
super(key);
}
Java platform changes
Here's the link to JEP
Let's officially say bye to 32-bit x86 systems.
To recap: support for 32-bit Windows has been discontinued in the previous release, and now the remaining platforms (Linux x86 and macOS) are gone as well. The reason is simple: maintaining ancient hardware requires significant costs too much, while almost nobody uses it anymore.
If you're still using a 32-bit JVM (wow!), it's time to think about upgrading.
Here's the link to JEP
Shrink not allocate
Every object in a Java heap has a hidden header—like a passport with metadata. Until Java 25, it was 12 bytes on 64-bit JVMs. Now it can be shrunk to 8 bytes using the -XX:+UseCompactObjectHeaders
flag.
Here's the link to JEP
Shenandoah is a low-pause parallel garbage collector that runs concurrently with the application. Its main task is to minimize stop-the-world pauses (<10 ms) even on terabyte-sized heap sizes. Instead of the usual generations, it uses regions of fixed size.
Shenandoah now officially supports generations (previously an experimental feature), with regions that can be young or old. Why?
Ahead-of-Time optimizations
Here's the link to JEP
Java keeps simplifying itself, especially its CLI. Now, devs need a single flag to create a new-fashioned AOT cache: -XX:AOTCacheOutput=<file>
.
You can read more about Ahead-of-Time caching in our previous article on Java's new features.
Here's the link to JEP
AOT cache is evolving. This Java release introduces a new feature to save method execution profiles. That means the JVM can apply JIT optimizations immediately, without waiting for runtime statistics. As a result, production apps hit peak performance faster.
JFR enhancements
Here's the link to JEP
Performance profiling becomes more accurate! Previously, JFR used thread interruptions to collect data, which could distort results under high load.
Now, Java 25 introduces cooperative sampling: threads pause at safepoints to report statistics. The data remains consistent, and the impact on performance is minimal.
Here's the link to JEP
Want to know not only where the code is slowing down, but why? New JFR features provide the answer:
MethodTiming
records method execution times with nanosecond precision;MethodTrace
builds call chains for critical sections.Beyond this list of new features, some changes remain in the Preview:
You can explore the full list of JEPs here.
Java continues to evolve, and the 25th release is excellent proof of that. The language is moving steadily toward better performance (AOT, Shenandoah), less boilerplate (compact syntax), and enhanced security (KDF). At the same time, the Java devs keep a balance without breaking backward compatibility. Everything is as usual: step-by-step enhancements and no revolutions, just making the tools we love even better.
0