
In this article, I’ve outlined the most important syntax and API updates introduced in JDK 25.
JDK 25 is a Long-Term Support (LTS) release, meaning it will be the standard for production environments for the next 4 years to come, succeeding JDK 21.
I’ve also written an article summarizing JDK 24 features which was released just before JDK 25 introducing massive performance improvements and new language features, check it out here.
⚠️ Some code examples provided here may still be preview features (like Stable Values). To run them, you might need to enable preview mode using the --enable-preview flag.
Here is a guide for symbols used in the articles:
🔗 — Link to JEP
⚠️ — Pay attention
❌ — Limitation
👉 — Reasoning
1. Module Import Declarations
Module Import declarations have moved from the preview in JDK 24 to being finalized in JDK 25.
❌ Until now, if you wanted to use List, Map, Stream, and Function, you had to clutter your file with multiple import statements or use wildcard imports that could lead to ambiguity.
Java
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.function.Function;
public class MyClass { ... }👉 With JDK 25, you can simply import the java.base module, which automatically imports all the commonly used packages (java.util, java.io, java.math, etc.) on demand.
Java
import module java.base; // Module Import
void main() {
List<String> list = List.of("Java", "25");
println(list);
}This dramatically reduces the “ceremony” at the top of your Java files.
🔗 Module Import Declarations are finalized in JEP 511.
2. Compact Source Files & Instance Main Methods
The removal of boilerplate initiative is finally complete now! 🥳
You can now write simple Java programs without the verbose class declarations and public static void main.
❌ Previously, even a simple script required understanding classes, static methods, and array arguments.
Java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World");
}
}👉 With JDK 25, the syntax is streamlined. The implicit class feature is now standard, and you can use void main() as a valid entry point.
Java
void main() {
println("Hello, World");
}Methods like println are now available via the new java.lang.IO class, which is implicitly imported in compact source files.
🔗 Compact Source Files and Instance Main Methods are finalized in JEP 512.
3. Flexible Constructor Bodies
This JEP has also moved from preview to finalized in the JDK 25.
👉 Developers can finally execute logic in a constructor before calling super().
❌ In previous JDK versions, the call to super() had to be the very first statement in the constructor.
This forced developers to use awkward static helper methods if they needed to validate arguments or transform data before passing it to the parent constructor.
Now, the constructor body allows a Prologue (statements before super) and an Epilogue (statements after super).
Java
public class Employee extends Person {
public Employee(String name) {
// Prologue: Validate BEFORE super()
if (name == null) throw new IllegalArgumentException("Name cannot be null");
super(name); // Initialize Parent
// Epilogue
System.out.println("Employee created");
}
}⚠️ You still cannot access this (the instance being created) in the prologue, but you can perform calculations and validations freely.
🔗 Flexible Constructor Bodies are finalized in JEP 513.
4. Scoped Values
Scoped Values are now a permanent feature, officially replacing ThreadLocal as the modern standard for sharing data safely across threads.
👉 Scoped Values solve the critical issues of ThreadLocal:
- Immutability: Data is immutable, preventing accidental side-effects.
- Bounded Lifetime: The value is automatically cleaned up when the scope ends, preventing memory leaks.
- Efficiency: Designed to work seamlessly with Virtual Threads, allowing millions of threads to share data without memory bloat.
Java
static final ScopedValue<String> REQUEST_ID = ScopedValue.newInstance();
void main() {
ScopedValue.where(REQUEST_ID, "REQ-12345").run(() -> {
processRequest();
});
}
void processRequest() {
// Access the value anywhere in the call stack
System.out.println("Processing: " + REQUEST_ID.get());
}🔗 Scoped Values are finalized in JEP 506.
5. Compact Object Headers
This is a massive under-the-hood performance upgrade.
❌ Historically, every Java object carried a header of 96 to 128 bits (12-16 bytes) to store metadata like hash codes, GC state, and locking information.
This overhead added up significantly in memory-intensive applications.
👉 With JDK 25, the object header has been compressed to just 64 bits (8 bytes).
This change:
- Reduces heap memory footprint by 10% to 20% automatically.
- Improves CPU cache locality.
You don’t need to change a single line of code to benefit from this!
🔗 Compact Object Headers are finalized in JEP 519.
6. Stable Values
Talking about some preview features, comes the Stable Values API that bridges the gap between final fields and mutable variables.
❌ Currently, final fields must be initialized when the object is constructed.
If you want lazy initialization, you have to use mutable fields, which the JVM cannot optimize as effectively as constants.
👉 StableValue allows you to have a field that is set once (lazily) and then treated as a constant by the JVM for superior performance.
Java
import java.lang.StableValue;
public class Config {
private static final StableValue<String> CONFIG_DATA = StableValue.of();
public static String getData() {
// Initializes lazily on first use
return CONFIG_DATA.orElseSet(() -> loadHeavyData());
}
}🔗 Stable Values are introduced in JEP 502.
7. AOT Enhancements & Profiling
JDK 25 makes huge strides in Ahead-of-Time (AOT) compilation to fix Java’s slow startup reputation, which was already discussed in JDK 24.
JDK 25 introduces two key JEPs to improve the AOT cache:
- JEP 514 (Ergonomics): Simplifies the command-line commands to create AOT caches.
- JEP 515 (Method Profiling): Allows the AOT cache to store method profiles from a training run.
This means when your application restarts, the JIT compiler already knows which methods are hot and how to optimize them, leading to near-instant peak performance.
Other Features
There are other notable features like Generational Shenandoah GC (JEP 521) and the removal of the 32-bit x86 port (JEP 503).
Apart from these, the two more important features that are still in preview carried from JDK 24 are:
You can read about these two features in my JDK 24 article here.
Subscribe to my newsletter today!