Java 25
Java 25 is a Long-Term Support (LTS) release and has been made generally available on 16 September 2025.
Here are some features
Flexible Constructor Bodies
Allows code before the super(...) or this(...) call in constructors.
Enables validation or computations before delegation to the superclass constructor.
Before
public class Elephant extends Animal {
public Elephant(String name) {
super(validate(name));
}
private static String validate(String name) {
if (name == null) {
throw new IllegalArgumentException();
}
return name;
}
}
After
public class Elephant extends Animal {
public Elephant(String name) {
if (name == null) {
throw new IllegalArgumentException();
}
super(name);
}
}
Primitive Types in Patterns
Pattern matching can handle primitive types in switch and instanceof statements.
instanceof
Before
Object o = 42;
if (o instanceof Integer i) { // autoboxed to Integer
System.out.println("Integer with value: " + i);
} else {
System.out.println("Not an Integer");
}
You could not write if (o instanceof int i)
The only option was to rely on wrapper classes (Integer, Double, etc.), because instanceof only worked with reference types.
After
Now, instanceof supports primitive patterns:
Object o = 42;
if (o instanceof int i) { // primitive pattern
System.out.println("Primitive int with value: " + i);
} else {
System.out.println("Not an int");
}
switch
Before
If you had a primitive like int x = 42;, and passed it as Object o = x;, then autoboxing turned it into an Integer, and the pattern case Integer i matched.
You could not directly write case int i or use primitive patterns in switch.
public String test(Object obj) {
return switch (obj) {
case String s -> "String: " + s;
case Integer i -> "Integer: " + i;
default -> "Other: " + obj;
};
}
After
You can now use patterns like int, long, double, etc. directly
public String test(Object obj) {
return switch (obj) {
case int i -> "int: " + i;
case long l -> "long: " + l;
case double d -> "double: " + d;
default -> "Other: " + obj;
};
}
Compact Source Files and Instance Main Methods
Before
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
After
void main() {
System.out.println("Hello, world!");
}
No need for public class, public static void main, etc. → easier for small programs, teaching, scripting.
Module import
Before
import java.util.List;
import java.util.Map;
import java.util.HashMap;
public class Example {
public static void main(String[] args) {
List<String> list = List.of("a", "b");
Map<String, Integer> map = new HashMap<>();
}
}
After
import module java.util;
void main() {
var list = List.of("a", "b");
var map = new HashMap<String, Integer>();
}
Instead of importing every class or package, you can pull in the whole module (java.util in this case).
Cleaner for quick apps and educational snippets.
Scoped Values
Scoped values are a lightweight and safe alternative to thread-locals.
Before
static final ThreadLocal<String> USER = new ThreadLocal<>();
public static void main(String[] args) {
USER.set("John");
doWork();
USER.remove();
}
static void doWork() {
System.out.println("Working as " + USER.get());
}
Issues: manual set/remove, risk of memory leaks, not structured.
After
import java.lang.ScopedValue;
public class Example {
static final ScopedValue<String> USER = ScopedValue.newInstance();
public static void main(String[] args) {
ScopedValue.where(USER, "Alice").run(() -> {
doWork();
});
}
static void doWork() {
System.out.println("Working as " + USER.get());
}
}
- No risk of accidental value leakage across threads.
- Automatically bound to the structured scope.
- Plays nicely with virtual threads.
Other
- Compact Object Headers : the header size is reduced -> more efficient memory usage
- GC / Startup / Profiling : Previous versions required extra flags and tools for performance tuning and observability. Java 25 improves this with faster startup times
- Crypto Updates : modernized cryptography libraries