-
Notifications
You must be signed in to change notification settings - Fork 0
Java 22
Release Date: March 19, 2024 | Type: Standard (non-LTS) | Status:
⚠️ End of Life
Java 22 standardizes the Foreign Function & Memory API (replacing JNI), previews unnamed variables/patterns for cleaner code, and adds Stream Gatherers — a way to write custom intermediate stream operations.
flowchart LR
A["Java 17: FFM Incubator"] --> B["Java 18-20: refined previews"] --> C["Java 22: FFM STANDARD ✅"]
D["JNI - complex, unsafe,\nboilerplate-heavy"] -->|replaced by| C
What: Call native (C) libraries and manage off-heap memory directly from Java — no JNI boilerplate, no native glue code compilation.
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
// Call the C standard library's strlen() function directly — no JNI!
try (Arena arena = Arena.ofConfined()) {
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
MethodHandle strlen = linker.downcallHandle(
stdlib.find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
);
MemorySegment cString = arena.allocateUtf8String("Hello, Panama!");
long length = (long) strlen.invoke(cString);
System.out.println("Length: " + length); // 14
}
// Off-heap memory management — safer than sun.misc.Unsafe
try (Arena arena = Arena.ofConfined()) {
MemorySegment segment = arena.allocate(1024); // 1KB off-heap
segment.set(ValueLayout.JAVA_INT, 0, 42);
int value = segment.get(ValueLayout.JAVA_INT, 0);
System.out.println(value); // 42
} // memory automatically freed when Arena closesflowchart TD
A[Java Application] -->|"FFM API\n(safe, no glue code)"| B[Native C/C++ Library]
A -.->|"Old way: JNI\n(complex, unsafe, boilerplate)"| B
C[Arena] -->|manages lifecycle of| D[MemorySegment - off-heap memory]
D -->|automatically freed| E[Arena.close]
Why it matters: This is a huge deal for performance-critical libraries (compression, crypto, ML inference) that need native code — no more hand-written JNI headers, no unsafe memory access, deterministic cleanup via Arena.
What: Use _ for variables/pattern bindings you don't actually need — reduces noise and unused-variable warnings.
// Before: forced to name a variable you don't use
for (Order order : orders) {
count++; // order itself isn't used
}
// Java 22 Preview: unnamed variable
for (Order _ : orders) {
count++;
}
// In pattern matching — ignore parts of a record you don't need
record Point3D(int x, int y, int z) {}
if (obj instanceof Point3D(var x, var y, var _)) {
// z is discarded — we only care about x and y
process(x, y);
}
// In catch blocks
try {
riskyOperation();
} catch (IOException _) {
// exception details irrelevant, just handle the failure case
fallback();
}What: A new Stream.gather() operation letting you write custom intermediate operations — something the Streams API famously couldn't do cleanly before (only Collectors handled custom terminal logic).
// Custom gatherer: sliding window of size 3
List<List<Integer>> windows = Stream.of(1, 2, 3, 4, 5)
.gather(Gatherers.windowSliding(3))
.toList();
// [[1,2,3], [2,3,4], [3,4,5]]
// Fixed-size batching — great for chunked API calls
List<List<String>> batches = ids.stream()
.gather(Gatherers.windowFixed(100))
.toList();
// Process 100 IDs at a time
// fold() — a stateful reduction that produces intermediate results, not just a final one
List<Integer> runningTotal = Stream.of(1, 2, 3, 4)
.gather(Gatherers.fold(() -> 0, (acc, val) -> acc + val))
.toList();flowchart LR
A[Stream: 1,2,3,4,5] -->|"gather(windowSliding(3))"| B["[1,2,3]"]
A -->|window 2| C["[2,3,4]"]
A -->|window 3| D["[3,4,5]"]
public class Order {
private final double total;
public Order(List<Item> items) {
// Before Java 22: validation had to happen AFTER super(), awkwardly
// Java 22 Preview: statements allowed before the constructor call
if (items == null || items.isEmpty()) {
throw new IllegalArgumentException("Items required");
}
super();
this.total = items.stream().mapToDouble(Item::price).sum();
}
}A standard API for parsing, generating, and transforming Java .class files — replacing the JDK's internal dependence on third-party bytecode libraries like ASM for tools that need it.
# Java 11 let you run a SINGLE .java file directly.
# Java 22 extends this to multiple files in one directory — still no compile step needed!
java src/Main.java
# Main.java can reference other .java files in the same directory,
# and they're compiled in-memory automatically✅ Migrate JNI-based native integrations to the FFM API — safer, no native glue code to maintain
✅ Use _ for genuinely unused loop variables, catch blocks, and pattern components — improves readability once out of preview
✅ Try Stream Gatherers for windowing/batching logic you'd otherwise hand-roll with imperative loops
❌ Don't rewrite stable, working JNI code just because FFM exists — migrate opportunistically, not urgently