Java 22 to 25 Features
When a school redesigns its first-day welcome for new students, it doesn't change the subjects; it removes the confusing forms and long speeches that scare newcomers away. Several of the changes from Java 22 to Java 25 do exactly that for Java: a first program no longer needs public class and public static void main(String[] args), and a single import line can bring in a whole module. Alongside these friendlier changes came serious power tools: calling C libraries safely, custom stream operations, and faster startup. Java 25 (September 2025) is the LTS that collects them all, and it is the version this course is written for. In this lesson, you will get a quick reference to each release and a tour program that uses the Java 25 features.
What Each Release Added
| Release | Final features | Read more |
|---|---|---|
| Java 22 (Mar 2024) | Unnamed variables and patterns (_); Foreign Function and Memory API; launching programs made of several source files | "Unnamed Variables and Patterns", "Foreign Function and Memory API in Java" |
| Java 23 (Sep 2024) | Markdown documentation comments (///); generational mode becomes ZGC's default | "Markdown Documentation Comments in Java" |
| Java 24 (Mar 2025) | Stream gatherers; Class-File API; AOT class loading and linking; virtual threads no longer pinned by synchronized; quantum-resistant ML-KEM and ML-DSA; Security Manager permanently disabled | "Stream Gatherers in Java", "Class-File API in Java", "AOT Class Loading and Caching in Java" |
| Java 25 (Sep 2025, LTS) | Compact source files and instance main methods; module import declarations; flexible constructor bodies; scoped values; compact object headers; AOT one-step cache creation and method profiling; Key Derivation Function API | see below |
Java 25 in Detail
| Feature | What it gives you | Read more |
|---|---|---|
| Compact source files and instance main methods | void main() { IO.println("Hi"); } is a complete program | "Compact Source Files and Instance Main Methods" |
| Module import declarations | import module java.base; imports every exported package of a module | "Module Import Declarations (Java 25)" |
| Flexible constructor bodies | Statements (validation, preparing arguments) before super(...) or this(...) | "Flexible Constructor Bodies" |
| Scoped values | Share an immutable value with a call tree and its threads; a safer ThreadLocal | "Scoped Values in Java" |
| Compact object headers | Object headers shrink from 12 to 8 bytes, saving heap (opt in with -XX:+UseCompactObjectHeaders) | "Compact Object Headers in Java 25" |
| AOT cache in one step | -XX:AOTCacheOutput trains and writes a startup cache in one run | "AOT Class Loading and Caching in Java" |
Still in preview or incubation in Java 25: structured concurrency, primitive types in patterns, stable values and the Vector API.
A Java 22 to 25 Tour
Let's see a compact source file that uses features from every release in this range. There is no class declaration around main: the file itself is the program.
import module java.base; // 25: module import
record Fee(String student, int amount) {}
class LateFee extends RuntimeException {
LateFee(int days) {
String why = "late by " + days + " days"; // 25: before super
super(why);
}
}
static final ScopedValue<String> TERM = ScopedValue.newInstance();
void main() { // 25: compact source
List<Fee> fees = List.of(new Fee("Navika", 15000),
new Fee("Srihitha", 14000), new Fee("Advitha", 16000));
int count = 0;
for (Fee _ : fees) count++; // 22: unnamed variable
IO.println("fees: " + count);
List<List<Fee>> batches = fees.stream()
.gather(Gatherers.windowFixed(2)) // 24: gatherers
.toList();
IO.println("batches: " + batches.size());
ScopedValue.where(TERM, "Term 2") // 25: scoped values
.run(() -> IO.println("billing " + TERM.get()));
try {
throw new LateFee(3);
} catch (LateFee e) {
IO.println(e.getMessage());
}
}fees: 3
batches: 2
billing Term 2
late by 3 daysExplanation:
import module java.base;makesList,Gatherersand every otherjava.baseclass available without separate imports.void main()with no class around it is a compact source file.IO.printlnis the new, simpler console helper injava.lang.for (Fee _ : fees)says "I don't need the element", so no unused variable is named.Gatherers.windowFixed(2)groups the three fees into batches of two and one.ScopedValue.where(...).run(...)makesTERMreadable only while that code runs.- The
LateFeeconstructor computes its message before callingsuper(why), which was a compile error before Java 25.
The LTS Releases at a Glance
| LTS | Released | Three features to remember |
|---|---|---|
| Java 8 | Mar 2014 | Lambdas, streams, java.time |
| Java 11 | Sep 2018 | HttpClient, var in lambdas, String.strip/isBlank |
| Java 17 | Sep 2021 | Records, sealed classes, text blocks |
| Java 21 | Sep 2023 | Virtual threads, pattern matching for switch, sequenced collections |
| Java 25 | Sep 2025 | Compact source files, module imports, scoped values |
Practice
- Easy: Write a compact source file
Hello.javacontaining onlyvoid main() { IO.println("Hello, Navika Anamaneni"); }and run it withjava Hello.java. - Harder: From memory, write the LTS table above: release, year and three features each. Then check it against this lesson.
Key Takeaways
Was this tutorial helpful?