Java 22
Release Information
- Release Date: March 19, 2024
- Support Type: Non-LTS (6-month support)
- End of Support: September 2024 (superseded by Java 23)
- JEPs: 12 enhancements
Highlights: 4 final features, 7 preview features, 1 incubator
Final Features
Foreign Function & Memory API (JEP 454) - FINAL 🎉
After 8 rounds of incubation/preview (Java 14-21), FFM API is now standard.
Why this matters: Safe, efficient alternative to JNI for native code integration.
Call C library functions:
import java.lang.foreign.*;
// Load C standard library
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
// Get strlen function
MethodHandle strlen = linker.downcallHandle(
stdlib.find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
);
// Call strlen
try (Arena arena = Arena.ofConfined()) {
MemorySegment str = arena.allocateFrom("Hello, Java 22!");
long length = (long) strlen.invoke(str);
System.out.println("Length: " + length); // 16
}Access off-heap memory:
// Allocate 1 MB of native memory
try (Arena arena = Arena.ofConfined()) {
MemorySegment segment = arena.allocate(1024 * 1024);
// Write data
segment.setAtIndex(ValueLayout.JAVA_INT, 0, 42);
segment.setAtIndex(ValueLayout.JAVA_INT, 1, 100);
// Read data
int first = segment.getAtIndex(ValueLayout.JAVA_INT, 0);
int second = segment.getAtIndex(ValueLayout.JAVA_INT, 1);
System.out.printf("Values: %d, %d%n", first, second);
} // Memory automatically freedBenefits:
- 90% less code than JNI
- 4-5x better performance than JNI
- Type-safe (compile-time checks)
- Automatic memory management (try-with-resources)
- No security warnings (unlike sun.misc.Unsafe)
Unnamed Variables & Patterns (JEP 456) - FINAL
Use _ for unused variables - improves code readability.
Unnamed variables:
// Old: Unused variable warning
try {
processFile();
} catch (IOException e) { // Warning: e is never used
System.out.println("Failed to process file");
}
// New: Explicitly mark as unused
try {
processFile();
} catch (IOException _) { // No warning
System.out.println("Failed to process file");
}Multiple unused variables:
// Connecting with credentials (ignoring password)
var connection = connect(username, _, hostname);
// Reading tuple (ignoring second value)
var (first, _, third) = readTuple();Unnamed patterns:
record Point(int x, int y) {}
// Old: Must name unused components
if (obj instanceof Point(int x, int y)) {
System.out.println("X coordinate: " + x); // y unused
}
// New: Use _ for unused components
if (obj instanceof Point(int x, _)) {
System.out.println("X coordinate: " + x);
}Switch patterns:
switch (shape) {
case Circle(_, double radius) -> // Ignore center
System.out.println("Radius: " + radius);
case Rectangle(Point(int x, _), _) -> // Ignore y and second point
System.out.println("Top-left X: " + x);
}Launch Multi-File Source-Code Programs (JEP 458) - FINAL
Run Java programs with multiple files without compilation.
Before (Java 21):
# Single file: Works
java HelloWorld.java
# Multiple files: Must compile first
javac Main.java Helper.java
java MainAfter (Java 22):
# Multiple files: Works directly!
java Main.java
# Automatically compiles and runs Main.java and dependenciesExample:
Main.java:
public class Main {
public static void main(String[] args) {
Helper.greet("Java 22");
}
}Helper.java:
public class Helper {
public static void greet(String name) {
System.out.println("Hello, " + name + "!");
}
}Run:
java Main.java
# Output: Hello, Java 22!Use cases:
- Scripts and utilities
- Learning and prototyping
- Quick experiments
- CI/CD scripts
Region Pinning for G1 (JEP 423) - FINAL
G1 GC can now perform garbage collection even when JNI code holds references.
Problem (Java 21):
- JNI code locks entire heap regions
- GC must wait for JNI to complete
- Can cause long pause times
Solution (Java 22):
- Pin individual objects instead of regions
- GC continues on other regions
- Reduced pause times
Impact: Lower latency for applications using JNI
Preview Features
String Templates (JEP 459) - Second Preview
Embed expressions in strings safely.
Example:
int x = 10, y = 20;
// Old: Concatenation
String message = "Sum of " + x + " and " + y + " is " + (x + y);
// Old: String.format
String message = String.format("Sum of %d and %d is %d", x, y, x + y);
// New: String templates
String message = STR."Sum of \{x} and \{y} is \{x + y}";Multi-line templates:
String json = STR."""
{
"name": "\{user.name()}",
"age": \{user.age()},
"active": \{user.isActive()}
}
""";Custom processors:
// JSON template
var json = JSON."""
{
"id": \{id},
"value": "\{value}"
}
""";
// SQL template (with escaping)
ResultSet rs = DB."""
SELECT * FROM users
WHERE name = \{userName}
""";Benefits:
- Type-safe interpolation
- Protection against injection attacks
- Better readability than concatenation
Stream Gatherers (JEP 461) - Preview
Custom intermediate stream operations.
Built-in gatherers:
// Window: Group elements into fixed-size windows
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
List<List<Integer>> windows = numbers.stream()
.gather(Gatherers.windowFixed(3))
.toList();
// [[1, 2, 3], [4, 5, 6]]
// Scan: Running accumulation
List<Integer> runningSum = numbers.stream()
.gather(Gatherers.scan(() -> 0, (sum, num) -> sum + num))
.toList();
// [1, 3, 6, 10, 15, 21]Custom gatherer:
// Group consecutive duplicates
Gatherer<String, ?, List<String>> deduplicateConsecutive = /* ... */;
List<String> items = List.of("a", "a", "b", "b", "b", "c");
List<String> deduplicated = items.stream()
.gather(deduplicateConsecutive)
.toList();
// ["a", "b", "c"]Implicitly Declared Classes (JEP 463) - Second Preview
Simplified entry point for beginners.
Old (verbose for beginners):
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}New (simplified):
void main() {
System.out.println("Hello, World!");
}Benefits:
- No class declaration
- No String[] args
- No public/static modifiers
- Perfect for learning
Statements before super() (JEP 447) - Preview
Execute code before calling super constructor.
Old:
class SubClass extends BaseClass {
public SubClass(int value) {
// ❌ Can't do anything before super()
super(value * 2);
}
}New:
class SubClass extends BaseClass {
public SubClass(int value) {
// ✅ Can validate/transform before super()
if (value < 0) {
throw new IllegalArgumentException("Value must be positive");
}
int transformed = value * 2;
super(transformed);
}
}Structured Concurrency (JEP 462) & Scoped Values (JEP 464) - Second Preview
Continued refinement from Java 21.
Incubator Features
Vector API (JEP 460) - Seventh Incubator
Continued SIMD API refinement with minor improvements.
Migration Considerations
Upgrading from Java 21 LTS:
- FFM API now standard: Remove
--enable-previewfor FFM code - Unnamed patterns: Refactor unused variable suppression
- Multi-file launch: Simplify development scripts
- String Templates: Consider for safer string composition
- G1 region pinning: May see reduced pause times with JNI
Compatibility: Binary compatible with Java 21
Related Topics
- Java 21 LTS - Previous LTS
- Java 23 - Next release
- Foreign Function & Memory API - In-the-field guide
References
Sources:
- The Arrival of Java 22! – Inside.java
- Oracle Releases Java 22
- Java 22 Features (with Examples)
- Java 22 Delivers Foreign Memory & Memory API, Unnamed Variables & Patterns - InfoQ
Official OpenJDK JEPs: