Java 25 landed on September 16, 2025 as the new Long-Term Support release, succeeding Java 21. After four years of six-month releases, we finally get another LTS with a solid set of finalized features. Here's what's worth your attention.
Scoped Values Are Finally Done
After five preview rounds, Scoped Values (JEP 487) are now a permanent feature. They replace ThreadLocal for sharing immutable data within a thread and its children — particularly important for virtual threads where ThreadLocal gets expensive.
private static final ScopedValue<User> CURRENT_USER = ScopedValue.newInstance();
void handleRequest(User user) {
ScopedValue.runWhere(CURRENT_USER, user, () -> {
// All code in this scope sees CURRENT_USER
processOrder();
sendNotification();
});
}
void processOrder() {
User user = CURRENT_USER.get(); // No parameter passing needed
}
Unlike ThreadLocal, scoped values are immutable and have bounded lifetime — they exist only within the runWhere block. Cleaner mental model, better performance with virtual threads.
Stable Values for Lazy Initialization
Stable Values (JEP 502, preview) solve the lazy initialization problem properly. They're variables that can be set once and stay unchanged, with full JVM optimization like final fields.
private static final StableValue<DatabaseConnection> DB = StableValue.of();
DatabaseConnection getConnection() {
return DB.orElseSet(() -> createExpensiveConnection());
}
No more double-checked locking patterns. The JVM treats these like constants after first assignment.
Primitive Types in Pattern Matching
Pattern matching now handles primitives in instanceof and switch (JEP 507, third preview):
static String describe(Object obj) {
return switch (obj) {
case int i when i > 0 -> "positive int: " + i;
case int i -> "non-positive int: " + i;
case double d -> "double: " + d;
case String s -> "string: " + s;
default -> "something else";
};
}
No more manual unboxing and casting. The compiler handles it safely.
Compact Object Headers
Object headers shrink from 96-128 bits down to 64 bits (JEP 519). This was experimental in JDK 24, now it's production-ready.
For applications creating millions of small objects, this means measurable memory savings. Enable with -XX:+UseCompactObjectHeaders.
Flexible Constructor Bodies
You can now write statements before super() or this() calls (JEP 513):
public class Order extends Entity {
private final String orderId;
public Order(String rawId) {
String validated = validate(rawId); // Before super()!
super(validated);
this.orderId = validated;
}
}
No more workarounds with static helper methods just to validate constructor arguments.
Structured Concurrency (Still Preview)
Structured Concurrency (JEP 505, fifth preview) keeps evolving. It treats groups of concurrent tasks as a single unit:
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Supplier<User> user = scope.fork(() -> fetchUser(id));
Supplier<Order> order = scope.fork(() -> fetchOrder(id));
scope.join().throwIfFailed();
return new UserWithOrder(user.get(), order.get());
}
If one task fails, all others cancel automatically. No more orphaned threads or forgotten futures.
JFR Improvements
Java Flight Recorder gets three upgrades:
- Method timing and tracing (JEP 520) — record execution times for specific methods without code changes
- CPU-time profiling on Linux (JEP 525) — more accurate CPU profiles using kernel timers
- Cooperative safepoint sampling (JEP 518) — more stable stack sampling
For anyone doing production profiling, these are meaningful improvements.
Should You Upgrade?
If you're on Java 21, there's no rush — it's supported until 2029. But Java 25 brings enough finalized features (Scoped Values, compact headers, flexible constructors) that it's worth planning a migration, especially for new projects.
If you're still on Java 17 or earlier, now's a good time to jump — you get four years of LTS improvements in one upgrade.
The full list of JEPs is at openjdk.org/projects/jdk/25.