Java 22 wurde am 19. März 2024 veröffentlicht. Du kannst es hier herunterladen.
Die Highlights von Java 22:
- Die Finalisierung von Unnamed Variables & Patterns.
- Launch Multi-File Source-Code Programs – starte Programme, die aus mehreren .java-Dateien bestehen, ohne sie vorab zu kompilieren.
- Statements before super(...) – wir dürfen nun in Konstruktoren auch vor dem Aufruf von
super(...)
oderthis(...)
Code auszuführen (mit einigen Einschränkungen). - Schreibe intermediären Stream-Operationen mit Stream Gatherers.
- Die Finalisierung der Foreign Function & Memory API nach sage und schreibe 8 (!) Incubator- und Preview-Runden.
Zudem gehen die in Java 21 als Preview eingeführten Features Structured Concurrency, Scoped Values, String Templates ohne Änderungen in eine zweite Preview-Runde. Und die ebenfalls in Java 21 als Preview eingeführten „Unnamed Classes and Instance Main Methods” wurde noch einmal überarbeitet und umbenannt in Implicitly Declared Classes and Instance Main Methods.
Für alle JEPs und sonstigen Änderungen verwende ich wie immer die originalen, englischen Bezeichnungen.
Unnamed Variables & Patterns – JEP 456
Oft müssen wir Variablen definieren, die wir gar nicht benötigen. Gängige Beispiele sind z. B. Exceptions, Lambda-Parameter, und Patterns.
Im folgenden Beispiel verwenden wir die Exception-Variable e
nicht:
try {
int number = Integer.parseInt(string);
} catch (NumberFormatException e) {
System.err.println("Not a number");
}
Code-Sprache: Java (java)
Hier verwenden wir den Lambda-Paraketer k
nicht:
map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
Code-Sprache: Java (java)
Und im folgenden Record-Pattern verwenden wir die Pattern-Variable position2
nicht:
if (object instanceof Path(Position(int x1, int y1), Position position2)) {
System.out.printf("object is a path starting at x = %d, y = %d%n", x1, y1));
}
Code-Sprache: Java (java)
Alle drei Code-Beispiele können in Java 22 mit unbenannten Variablen und unbenannten Patterns ausdrucksstärker formuliert werden, indem die Namen der Variablen oder das komplette Pattern durch den Unterstrich (_) ersetzt werden:
Die Exception-Variable e
können wir durch _
ersetzen:
try {
int number = Integer.parseInt(string);
} catch (NumberFormatException _) {
System.err.println("Not a number");
}
Code-Sprache: Java (java)
Den Lambda-Parameter k
können wir durch _
ersetzen:
map.computeIfAbsent(key, _ -> new ArrayList<>()).add(value);
Code-Sprache: Java (java)
Und das komplette Teil-Pattern Position position2
können wir durch _ ersetzen:
if (object instanceof Path(Position(int x1, int y1), _)) {
System.out.printf("object is a path starting at x = %d, y = %d%n", x1, y1));
}
Code-Sprache: Java (java)
Unnamed Variables & Patterns wurde in Java 21 als Preview-Feature unter dem Namen „Unnamed Patterns and Variables” veröffentlicht und wird in Java 22 durch JDK Enhancement Proposal 456 ohne Änderungen finalisiert.
Eine ausführlichere Beschreibung findest du im Hauptartikel über unbenannte Variablen und Patterns.
Launch Multi-File Source-Code Programs – JEP 458
Seit Java 11 können wir Java-Programme, die aus nur einer Datei bestehen, direkt ausführen, ohne sie vorher kompilieren zu müssen.
Speichere z. B. einmal den folgenden Java-Code in der Datei Hello.java:
public class Hello {
public static void main(String[] args) {
System.out.printf("Hello %s!%n", args[0]);
}
}
Code-Sprache: Java (java)
Du brauchst dieses Programm nicht, wie vor Java 11, erst mit javac
zu kompilieren, sondern kannst es direkt ausführen:
$ java Hello.java World
Hello World!
Code-Sprache: Klartext (plaintext)
Wir können in der Datei Hello.java auch mehrere Klassen definieren, doch wenn unser Programm wächst, wird das schnell unübersichtlich; die anderen Klassen sollten in eigenen Dateien definiert werden und in einer sinnvollen Paketstruktur organisiert werden.
Sobald wir aber weitere Java-Dateien hinzufügen, funktioniert der sogennante „Launch Single-File Source-Code”-Mechanismus aus Java 11 nicht mehr.
Lass uns einmal die Berechnung der Begrüßung in eine andere Klasse auslagern und diese in der Datei Greetings.java speichern:
public class Greetings {
public static String greet(String name) {
return "Hello %s!%n".formatted(name);
}
}
Code-Sprache: Java (java)
Die Hello
-Klasse lassen wir wie folgt die Greetings
-Klasse verwenden:
public class Hello {
public static void main(String[] args) {
System.out.println(Greetings.greet(args[0]));
}
}
Code-Sprache: Java (java)
Wenn wir das Programm nun direkt mit java starten wollen, passierte – jedenfalls bis Java 21 – folgendes:
$ java Hello.java World
Hello.java:5: error: cannot find symbol
System.out.println(Greetings.greet(args[0]));
^
symbol: variable Greetings
location: class Hello
1 error
error: compilation failed
Code-Sprache: Klartext (plaintext)
Die Greetings
-Klasse wurde nicht gefunden.
Versuchen wir es noch einmal mit Java 22:
$ java Hello.java World
Hello World!
Code-Sprache: Klartext (plaintext)
Aus dem „Launch Single-File Source-Code”-Feature wurde in Java 22 ein „Launch Multi-File Source-Code Programs”-Feature. Der Code darf nun in beliebig vielen Java-Dateien strukturiert sein.
Folgende Besonderheiten musst du dabei beachten:
- Würde die
Greetings
-Klasse nicht nur in der Datei Greetings.java, sondern ebenfalls in der Datei Hello.java definiert sein, dann würde dasjava
-Kommando die in der DateiHello.java
definierte Klasse verwenden. Es würde die Datei Greetings.java gar nicht erst suchen und somit auch keine Fehlermeldung ausgeben, dass die Klasse doppelt definiert ist. - Wenn die Datei Hello.java in einem Paket liegen würde, beispielsweise in eu.happycoders.java22, dann muss sie auch im entsprechenden Verzeichnis eu.happycoders.java22 liegen, und du musst das
java
-Kommando im Root-Verzeichis wie folgt aufrufen:java eu/happycoders/java22/Hello.java World
- Möchtest du Code aus JAR-Dateien verwenden, kannst du diese z. B. in ein Verzeichnis libs legen und das
java
-Kommando dann mit der VM-Option aufrufen.--class-path 'libs/*'
Definiert ist dieses Feature in JDK Enhancement Proposal 458. Dort wird auch erklärt, wie das Feature bei der Verwendung von Modulen funktioniert, und wie einige Sonderfälle, die theoretisch eintreten könnten, behandelt werden. Ich denke aber, dass für die meisten Anwendungsfälle das hier beschriebene ausreichend sein sollte.
Foreign Function & Memory API – JEP 454
Nach vielen Jahren der Entwicklung in Project Panama und nach insgesamt acht Incubator- und Preview-Versionen wird die Foreign Function & Memory API in Java 22 durch JDK Enhancement Proposal 454 endlich finalisiert.
Die Foreign Function & Memory API (oder kurz: FFM API) ermöglicht es, aus Java heraus auf Code außerhalb der JVM (z. B. auf Funktionen in Bibliotheken, die in anderen Programmiersprachen implementiert wurden) sowie auf nativen Speicher (also Speicher, der nicht von der JVM im Heap verwaltet wird) zuzugreifen.
Die FFM API soll das extrem komplizierte, fehleranfällige und langsame Java Native Interface (JNI) ablösen und verspricht im direkten Vergleich 90 % weniger Implementierungsaufwand und die vier- bis fünffache Performance.
Ich zeige dir hier an einem einfachen Beispiel, wie die API funktioniert – eine umfangreichere Einführung in das Thema findest du im Hauptartikel über die Foreign Function & Memory API.
Der folgende Code ruft die strlen()
-Funktion der Standard-C-Library auf, um die Länge des Strings „Happy Coding!” zu bestimmen:
public class FFMTest22 {
public static void main(String[] args) throws Throwable {
// 1. Get a lookup object for commonly used libraries
SymbolLookup stdlib = Linker.nativeLinker().defaultLookup();
// 2. Get a handle to the "strlen" function in the C standard library
MethodHandle strlen =
Linker.nativeLinker()
.downcallHandle(
stdlib.find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
// 3. Get a confined memory area (one that we can close explicitly)
try (Arena offHeap = Arena.ofConfined()) {
// 4. Convert the Java String to a C string and store it in off-heap memory
MemorySegment str = offHeap.allocateFrom("Happy Coding!");
// 5. Invoke the foreign function
long len = (long) strlen.invoke(str);
System.out.println("len = " + len);
}
// 6. Off-heap memory is deallocated at end of try-with-resources
}
}
Code-Sprache: Java (java)
Die einzelnen Schritte der Vorgehensweise sind durch Kommentare direkt im Quellcode beschrieben.
Der Code unterscheidet sich von der im Java-21-Artikel vorgestellten Version in nur einem Detail: Die Arena.allocateFrom(...)
-Methode hieß zuvor allocateUtf8String(...)
.
Du kannst das Programm mit Java 22 wie folgt starten:
$ java --enable-native-access=ALL-UNNAMED FFMTest22.java
len = 13
Code-Sprache: Klartext (plaintext)
Weiter will ich in die Details der FFM API an dieser Stelle nicht eingehen. Du findest eine ausführliche Beschreibung der API und all ihrer Komponenten im Hauptartikel über die FFM API.
Locale-Dependent List Patterns
Mit der neuen Klasse ListFormat (← Link zur Dokumentation) lassen sich Listen als Aufzählung formatieren, so wie wir sie in Fließtext formulieren würden.
Hier ist ein Beispiel:
List<String> list = List.of("Earth", "Wind", "Fire");
ListFormat formatter = ListFormat.getInstance(Locale.GERMAN, Type.STANDARD, Style.FULL);
System.out.println(formatter.format(list));
Code-Sprache: Java (java)
Der Code gibt folgendes aus:
Earth, Wind und Fire
Code-Sprache: Klartext (plaintext)
Wenn wir den locale
-Parameter auf Locale.US
ändern, kommt folgendes heraus:
Earth, Wind, and Fire
Code-Sprache: Klartext (plaintext)
Und die Einstellung Locale.FRANCE
führt zu:
Earth, Wind et Fire
Code-Sprache: Klartext (plaintext)
Neben locale
hat die ListFormat.getInstance(...)
-Methode noch zwei weitere Parameter:
type
– der Typ der Aufzählung – hier gibt es drei Varianten:
STANDARD
für eine Auflistung mit „und”,OR
für eine Auflistung mit „oder”,UNIT
für eine Auflistung von Einheiten, entspricht je nach Locale einer Auflistung mit „und” oder einer mit nur Kommas.
style
– der Stil der Aufzählung – auch hier gibt es drei Varianten:
FULL
– die Bindewörter wie „und” und „oder” werden ausgeschrieben.SHORT
– die Bindewörter werden je nach Locale ausgeschrieben oder abgekürzt.NARROW
– die Bindewörter werden je nach Locale ausgeschrieben oder weggelassen, ggf. werden auch Kommas weggelassen.
In der folgenden Tabelle siehst du alle Kombinationen für Locale.GERMANY
:
FULL | SHORT | NARROW | |
STANDARD | Earth, Wind und Fire | Earth, Wind und Fire | Earth, Wind und Fire |
OR | Earth, Wind oder Fire | Earth, Wind oder Fire | Earth, Wind oder Fire |
UNIT | Earth, Wind und Fire | Earth, Wind und Fire | Earth, Wind und Fire |
Wie du siehst, gibt es im deutschen keine Unterschiede zwischen den Typen STANDARD
und UNIT
, und der Stil spielt überhaupt keine Rolle.
Schauen wir uns einmal das Locale.US
-Format an:
FULL | SHORT | NARROW | |
STANDARD | Earth, Wind, and Fire | Earth, Wind, & Fire | Earth, Wind, Fire |
OR | Earth, Wind, or Fire | Earth, Wind, or Fire | Earth, Wind, or Fire |
UNIT | Earth, Wind, Fire | Earth, Wind, Fire | Earth Wind Fire |
Hier lassen sich hingegen mehr Unterschiede ausmachen: Beim Typ UNIT wird vor dem letzten Element kein Verbindungswort eingefügt, und das „and” wird beim Stil SHORT
zu „&” und fällt beim Stil NARROW
komplett weg.
Die parameterlose Methode ListFormat.getInstance()
liefert ein Listenformat für die Standard-Locale, den Typ STANDARD
und den Stil FULL
.
Für diese Änderung gibt es keinen JEP, sie ist im Bug-Tracker unter JDK-8041488 zu finden.
Neue Preview-Features in Java 22
Java 22 bringt uns drei neue Features im Preview Stadium. Da sich Preview-Features noch ändern können, solltest du sie nicht in Produktivcode einsetzen. Du musst sie sowohl im javac
- als auch im java
-Kommando explizit über die VM-Optionen --enable-preview --source 22
freischalten.
Statements before super(…) (Preview) – JEP 447
Hast du dich auch schon mal geärgert, dass du vor dem Aufruf eines Super-Konstruktors mit super(...)
oder vor dem Aufruf eines alternativen Konstruktors mit this(...)
keinen anderen Code aufrufen darfst?
Musstest du auch schon mal ein Ungetüm wie das folgende schreiben, nur um das Argument für einen Super-Konstruktor zu berechnen oder zu validieren?
public class Square extends Rectangle {
public Square(Color color, int area) {
this(color, Math.sqrt(validateArea(area)));
}
private static double validateArea(int area) {
if (area < 0) throw new IllegalArgumentException();
return area;
}
private Square(Color color, double sideLength) {
super(color, sideLength, sideLength);
}
}
Code-Sprache: Java (java)
Es ist nicht leicht zu erkennen, was dieser Code tut, um warum er das auf so komplizierte Weise macht.
Wäre es nicht viel schöner, man könnte einfach folgendes schreiben?
public class Square extends Rectangle
public Square(Color color, int area) {
if (area < 0) throw new IllegalArgumentException();
double sideLength = Math.sqrt(area);
super(color, sideLength, sideLength);
}
}
Code-Sprache: Java (java)
Hier ist sofort ersichtlich, was der Konstruktor tut (selbst ohne die Rectangle
-Elternklasse zu kennen):
- Er validiert, dass
area
nicht negativ ist. - Er berechnet die Seitenlänge des Quadrats als Wurzel der Fläche.
- Er ruft den Super-Konstruktor von
Rectangle
auf und übergibt als Breite und Höhe die Seitenlänge des Quadrats.
Doch bisher war das nicht möglich. Bisher musste der Aufruf von super(...)
das erste Statement in einem Konstruktor sein. Code für die Validierungen von Parametern und die Berechnung von Argumenten für die super(...)
-Methode musste kompliziert in separate Methoden oder alternative Konstruktoren ausgelagert werden, wie im ersten Code-Beispiel.
Mit Java 22 (mit aktivierten Preview-Features) kannst du den Code nun so schreiben, wie im zweiten Beispiel: mit Validierungs- und Berechnungslogik vor dem Aufruf von super(...)
!
Das Preview-Feature „Statements before super(…)” wird in JDK Enhancement Proposal 447 definiert.
Eine tiefergehende Betrachtung und einige Besonderheiten, die du beim Schreiben von Code vor super(...)
oder this(...)
beachten musst, findest du im Hauptartikel über Flexible Constructor Bodies (wie das Feature ab Java 23 heißt).
Stream Gatherers (Preview) – JEP 461
Seit Jahren stößt die begrenzte Zahl an intermediären Stream-Operationen der Java-Stream-API auf Kritik. Neben den bestehenden Operationen filter, map, flatMap, mapMulti, distinct, sorted, peak, limit, skip, takeWhile und dropWhile wünscht sich die Java-Community Methoden wie window und fold und zahlreiche mehr.
Doch anstatt all diese Methoden ins JDK zu integrieren, entschieden sich die JDK-Entwickler, stattdessen eine API zu entwickeln, die es sowohl den JDK-Entwicklern als auch der Java-Community ermöglicht, beliebige intermediäre Stream-Operationen zu schreiben.
Die neue API heißt „Stream Gatherers“ und wird durch JDK Enhancement Proposal 461 in Java 22 erstmals als Preview-Feature veröffentlicht.
Zusammen mit der API wird eine ganze Reihe vordefinierter Gatherer mitgeliefert, so zum Beispiel die gewünschten Window- und Fold-Operationen.
Mit der „Fixed Window“-Operation z. B. kannst du Stream-Elemente in Listen vorgegebener Größen gruppieren:
List<String> words = List.of("the", "be", "two", "of", "and", "a", "in", "that");
List<List<String>> fixedWindows = words.stream()
.gather(Gatherers.windowFixed(3))
.toList();
System.out.println(fixedWindows);
Code-Sprache: Java (java)
Dieses kleine Beispielprogramm gibt folgendes aus:
[[the, be, two], [of, and, a], [in, that]]
Code-Sprache: Klartext (plaintext)
Mit der „Sliding Window“-Operation kannst du Stream-Elemente ebenfalls gruppieren, allerdings überlappen die erzeugten Listen und sind um jeweils ein Element verschoben:
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
List<List<Integer>> slidingWindows = numbers.stream()
.gather(Gatherers.windowSliding(3))
.toList();
System.out.println(slidingWindows);
Code-Sprache: Java (java)
Dieses Programm gibt folgendes aus:
[[1, 2, 3], [2, 3, 4], [3, 4, 5]]
Code-Sprache: Klartext (plaintext)
Welche weiteren vordefinierten Stream-Gatherer Java 22 bereithält und wie du solch einen Gatherer selbst implementierst, erfährst du im Hauptartikel über Stream Gatherer.
Class-File API (Preview) – JEP 457
Die Java Class-File API ist eine Schnittstelle zum Lesen und Schreiben von .class-Dateien, also von kompiliertem Java-Bytecode. Die neue API soll das im JDK intensiv genutzte Bytecode-Manipulations-Framework ASM ablösen.
Der Grund für die Entwicklung einer eigenen API liegt darin, dass eine Library benötigt wird, die zum einen mit dem sechsmonatigen Veröffentlichungszyklus des JDK Schritt halten kann und zum anderen nicht immer eine Version hinterherhinkt. Denn erst wenn ein JDK veröffentlicht ist, kann ASM auf diese Version angepasst werden – die neue ASM-Version kann dann aber wiederum erst in der nächsten JDK-Version eingesetzt werden.
Da die meisten Java-Programmiererinnen und -Programmierer wahrscheinlich nie direkt mit der Class-File-API in Berührung kommen werden, werde ich sie an dieser Stelle nicht detailliert beschreiben.
Sollte die Class-File-API für dich von Interesse sein, findest du alle Einzelheiten in JDK Enhancement Proposal 457.
Wiedervorgelegte Preview und Incubator-Features
Fünf Preview- und Incubator-Features werden in Java 22 wiedervorgelegt, drei davon ohne Änderungen gegenüber Java 21:
Structured Concurrency (Second Preview) – JEP 462
Structured Concurrency ermöglicht eine einfache Koordination von nebenläufigen Aufgaben und führt mit StructuredTaskScope
eine Kontrollstruktur ein, die den Anfang und das Ende nebenläufiger Tasks klar definiert, eine saubere Fehlerbehandlung ermöglicht und Teilaufgaben, deren Ergebnisse nicht mehr benötigt werden, geordnet abbrechen kann.
So lässt sich z. B. sehr leicht eine race()
-Methode implementieren, die zwei Tasks startet, das Ergebnis des zuerst beendeten Tasks zurückliefert und den zweiten Task abbricht:
public static <R> R race(Callable<R> task1, Callable<R> task2)
throws InterruptedException, ExecutionException {
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<R>()) {
scope.fork(task1);
scope.fork(task2);
scope.join();
return scope.result();
}
}
Code-Sprache: Java (java)
Structured Concurrency wurde in Java 21 als Preview-Feature vorgestellt. Eine ausführlichere Behandlung und zahlreiche weitere Beispiele findest du im Hauptartikel über Structured Concurrency.
Durch JDK Enhancement Proposal 462 geht dieses Feature in Java 22 ohne Änderungen in eine zweite Preview-Phase, um der Java-Community weitere Gelegenheit zum Testen und zur Einreichung von Feedback zu ermöglichen.
Scoped Values (Second Preview) – JEP 464
Scoped Values erlauben es, einen oder mehrere Werte an eine oder mehrere Methoden zu übergeben, ohne sie als explizite Parameter zu definieren und von einer Methode zur nächsten weiterreichen zu müssen.
Das folgende Beispiel zeigt, wie ein Webserver den eingeloggten User als Scoped Value definiert und in dessen Scope den Request weiterverarbeitet:
public class Server {
public final static ScopedValue<User> LOGGED_IN_USER = ScopedValue.newInstance();
. . .
private void serve(Request request) {
. . .
User loggedInUser = authenticateUser(request);
ScopedValue.where(LOGGED_IN_USER, loggedInUser)
.run(() -> restAdapter.processRequest(request));
. . .
}
}
Code-Sprache: Java (java)
Nehmen wir an, der durch den Server aufgerufene REST-Adapter ruft einen Service auf und dieser Service wiederum ein Repository. In diesem Repository könnten wir dann z. B. wie folgt auf den eingeloggten User zugreifen:
public class Repository {
. . .
public Data getData(UUID id) {
Data data = findById(id);
User loggedInUser = Server.LOGGED_IN_USER.get();
if (loggedInUser.isAdmin()) {
enrichDataWithAdminInfos(data);
}
return data;
}
. . .
}
Code-Sprache: Java (java)
Scoped Values wurden gemeinsam mit Structured Concurrency in Java 21 als Preview-Feature eingeführt. Eine ausführliche Einführung und einen Vergleich mit ThreadLocal
-Variablen findest du im Hauptartikel über Scoped Values.
Auch Scoped Values gehen in Java 22 ohne Änderungen in eine zweite Preview-Runde, spezifiziert durch JDK Enhancement Proposal 464.
String Templates (Second Preview) – JEP 459
Mit String Templates können Strings zur Laufzeit durch sogenannte String-Interpolation anhand von Variablen und berechneten Werten zusammengesetzt werden:
int a = ...;
int b = ...;
String interpolated = STR."\{a} times \{b} = \{Math.multiplyExact(a, b)}";
Code-Sprache: Java (java)
Zur Laufzeit werden hier folgende Ersetzungen vorgenommen:
\{a}
wird durch den Wert der Variablena
ersetzt.\{b}
wird durch den Wert der Variablenb
ersetzt.\{Math.multiplyExact(a, b)}
wird durch das Ergebnis des Aufrufes der MethodeMath.multiplyExact(a, b)
ersetzt.
String Templates wurden in Java 21 als Preview-Feature eingeführt. Eine ausführlichere Beschreibung findest du im Hauptartikel über String Templates.
Und auch String Templates gehen – durch JDK Enhancement Proposal 459 spezifiziert – als drittes Preview-Feature in Java 22 ohne Änderungen in eine zweite Preview-Runde.
Implicitly Declared Classes and Instance Main Methods (Second Preview) – JEP 463
Java-Anfängerinnen und -Anfänger beginnen oft mit sehr einfachen Programmen, wie z. B. dem folgenden:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
Code-Sprache: Java (java)
Für Java-Neulinge kann dieses Beispiel trotz seiner Einfachheit extrem verwirrend sein, denn sie sind in der Regel noch nicht vertraut mit Konzepten wie Sichtbarkeitsmodifikatoren, Klassenstrukturen und statischen Methoden. Der ungenutzte args
-Parameter und das schwergewichtige System.out.println(...)
tun ihr Übriges.
Wäre es nicht viel einfacher, wenn man all diese überflüssigen Elemente entfernen könnte? Zum Beispiel so:
JDK Enhancement Proposal 463 macht dies möglich. Der folgende, nach der Löschaktion verbleibende Code ist somit ein gültiges und vollständiges Java-Programm:
void main() {
System.out.println("Hello world!");
}
Code-Sprache: Java (java)
Damit können Anfänger langsam als bisher an die Sprache herangeführt werden. Konzepte, die für größere Programme relevant werden, wie Klassen, die Unterscheidung in statische und Instanzmethoden, Sichtbarkeitsmodifikatoren wie public
, protected
und private
sowie grobgranulare Strukturen wie Pakete und Module können so nach und nach gelehrt werden.
Beachte, dass dieses Feature in Java 22 noch im Preview-Modus ist. Wenn du das Programm unter HelloWorld.java speicherst, kannst du es wie folgt starten:
$ java --enable-preview --source 22 HelloWorld.java
Note: Hello.java uses preview features of Java SE 22.
Note: Recompile with -Xlint:preview for details.
Hello world!
Code-Sprache: Klartext (plaintext)
Mehr Details über die Bestandteile dieses Features – einfache Quelldateien, implizit deklarierte Klassen und Instanz-Main-Methoden – kannst du im Abschnitt Simple Source Files and Instance Main Methods (so wird das Feature ab Java 24 heißen) des Artikels über die Java-main-Methode nachlesen.
Rückblick
Das Feature wurde in Java 21 bereits unter dem Namen Unnamed Classes and Instance Main Methods vorgestellt. Das Konzept der unbenannten Klasse wurde für Java 22 in das deutlich simplere Konzept der implizit deklarierten Klasse abgeändert, und das Launch-Protokoll der Main-Methode wurde vereinfacht.
Vector API (Seventh Incubator) – JEP 460
Die seit nunmehr über drei Jahren entwickelte neue Vector API geht mit JDK Enhancement Proposal 460 in die siebte Inkubator-Runde.
Die Vector API ermöglicht es, Vektor-Operationen durchzuführen, wie z. B. die folgende Vektor-Addition:
Das besondere dabei ist, dass die JVM diese Berechnungen auf möglichst effiziente Art auf die Vektor-Operationen moderner CPUs abbildet, so dass solche Berechnungen (bis zu einer bestimmten Vektorgröße) in einem einzigen CPU-Zyklus ausgeführt werden können.
Da sich das Feature noch immer im Incubator-Stadium befindet, also wesentliche Änderungen nicht ausgeschlossen sind, werde ich in diesem Artikel noch nicht weiter auf die Details eingehen. Ich werde die neue API detailliert vorstellen, sobald sie das Preview-Stadium erreicht.
Deprecations und Löschungen
Auch in Java 22 wurden wieder einige Funktionen als „deprecated” markiert bzw. zuvor als „deprecated for removal” markierte Funktionen aus dem JDK entfernt.
Thread.countStackFrames has been removed
Die Methode Thread.countStackFrames()
wurde bereits in Java 1.2 im Jahr 1998 als „deprecated” markiert, in Java 9 wurde sie als „deprecated for removal” markiert, und seit Java 14 wirft sie eine UnsupportedOperationException
.
In Java 22 wird die Methode nun entfernt.
Zur Untersuchung des aktuellen Stacks wurde als Alternative bereits in Java 9 die StackWalker-API eingeführt.
Für diese Änderung gibt es keinen JEP, sie ist im Bug-Tracker unter JDK-8309196 zu finden.
The Old Core Reflection Implementation Has Been Removed
In Java 18 wurde der Core Reflection Mechanismus auf Basis von Method Handles neu implementiert. Die alte Funktionalität konnte allerdings bisher über die VM-Option -Djdk.reflect.useDirectMethodHandle=false
In Java 22 wird die alte Funktionalität komplett entfernt, und die o. g. VM-Option wird ignoriert.
Für diese Änderung gibt es keinen JEP, sie ist im Bug-Tracker unter JDK-8305104 registriert.
Deprecations und Löschungen in sun.misc.Unsafe
Diese Klasse sun.misc.Unsafe
stellt Low-Level-Funktionalitäten zur Verfügung, die eigentlich nur von der Java-Kernbibliothek und nicht von anderen Java-Programmen genutzt werden sollen. Das hat allerdings viele Java-Entwickler nicht davon abgehalten, Unsafe
dennoch zu benutzen.
Für viele Methoden in Unsafe
wurden im Laufe der Zeit öffentliche APIs im JDK zur Verfügung gestellt. Diese Methoden werden zunächst als „deprecated” markiert, dann als „deprecated for removal”, und schließlich werden sie komplett entfernt.
In Java 22 sind folgende Methoden von der Aufräumaktion betroffen:
Unsafe.park()
undunpark()
wurden in Java 5 durchjava.util.concurrent.LockSupport.park()
undunpark()
ersetzt und werden in Java 22 als „deprecated for removal” markiert.Unsafe.getLoadAverage()
wurde in Java 6 durchjava.lang.management.OperatingSystemMXBean.getSystemLoadAverage()
ersetzt und wird nun ebenfalls als „deprecated for removal” markiert.Unsafe.loadFence()
,storeFence()
undfullFence()
wurden in Java 9 durch gleichnamige Methoden injava.lang.invoke.VarHandle
ersetzt und werden ebenfalls als „deprecated for removal” markiert.Unsafe.shouldBeInitialized()
undensureClassInitialized()
wurden in Java 15 durchjava.lang.invoke.MethodHandles.Lookup.ensureInitialized()
ersetzt und in derselben Java-Version als „deprecated for removal” markiert. Die Methoden werden in Java 22 entfernt.
Für diese Änderungen gibt es keinen JEP, sie sind im Bug-Tracker unter JDK-8315938 und JDK-8316160 zu finden.
Sonstige Änderungen in Java 22
In diesem Abschnitt findest du einige Änderungen, mit denen du in der täglichen Arbeit mit Java 22 eher nicht in Berührung kommen wirst. Es ist dennoch gut, über diese Änderungen Bescheid zu wissen.
Region Pinning for G1 – JEP 423
Bei der Arbeit mit JNI (was langfristig durch die in Java 22 finalisierte Foreign Function & Memory API abgelöst werden soll), verwendet man ggf. Methoden, die Zeiger auf die Speicheradresse eines Java-Objekts zurückgeben und dann wieder freigeben. Ein Beispiel sind die Funktionen GetStringCritical und ReleaseStringCritical.
Solange solch ein Zeiger noch nicht durch die entsprechende Release-Methode wieder freigegeben wurde, darf der Garbage Collector das Objekt nicht im Speicher verschieben, da dies den Zeiger ungültig machen würde.
Wenn ein Garbage Collector sogenanntes „Pinning” unterstützt, kann die JVM ihn anweisen, solch ein Objekt anzupinnen, was bedeutet, dass der Garbage Collector es nicht verschieben darf.
Wenn der Garbage Collector dieses Pinning jedoch nicht unterstützt, bleibt der JVM nichts anderes übrig, als die Garbage Collection komplett zu pausieren, sobald irgendeine Get*Critical-Methode aufgerufen wurde und sie erst dann wieder zu aktivieren, wenn alle entsprechenden Release*Critical-Methoden aufgerufen wurden. Je nach Verhalten einer Anwendung kann das drastische Konsequenzen auf den Speicherverbrauch und die Performance haben.
Der G1-Garbage-Collector hat bisher Pinning nicht unterstützt. Mit JDK Enhancement Proposal 423 wird er um die Pinning-Funktionalität erweitert, d. h. ab Java 22 braucht der Garbage Collector nicht mehr pausiert zu werden.
Support für Unicode 15.1
In Java 22 wird die Unicode-Unterstützung auf Unicode-Version 15.1 angehoben, die den Umfang des Zeichensatzes um 627 hauptsächlich chinesische Symbole auf insgesamt 149.813 Zeichen erhöht.
Relevant ist das für Klassen wie String und Character, die mit den neuen Zeichen umgehen können müssen. Ein Beispiel dafür findest du im Artikel zu Java 11.
(Für diese Änderung gibt es keinen JEP, sie ist im Bug-Tracker unter JDK-8296246 registriert.)
Make LockingMode a product flag
Die in Java 21 eingeführte VM-Option -XX:LockingMode
wird von einer experimentellen zu einer produktiven Option befördert, d. h. sie muss nicht mehr mit -XX:+UnlockExperimentalVMOptions
kombiniert werden.
(Für diese Änderung gibt es keinen JEP, sie ist im Bug-Tracker unter JDK-8315061 registriert.)
Vollständige Liste aller Änderungen in Java 22
In diesem Artikel hast du alle Java Enhancement Proposals kennengelernt, die in Java 22 umgesetzt wurden, sowie eine Auswahl weiterer Änderungen aus dem Bug-Tracker. Alle weiteren Änderungen findest du in den offiziellen Java 22 Release Notes.
Fazit
Die Fanfaren des Java-21-Launches sind noch zu hören, da wartet bereits Java 22 mit beeindruckenden Features auf:
Unbenannte Variablen und Patterns und Statements before super (letzteres noch in der Preview-Phase) werden Java-Code in Zukunft noch ausdrucksstärker machen.
Mit Stream Gatherers können wir endlich beliebige intermediäre Stream-Operationen schreiben, so wie wir mit Kollektoren auch schon immer terminale Operationen schreiben konnten.
Mit Launch Multi-File Source-Code Programs können wir Programme, die aus nur einer Datei bestehen, endlich sauber erweitern, ohne auf den Komfort des Startens ohne explizites Kompilieren verzichten zu müssen.
Die Foreign Function & Memory API ist endlich finalisiert und für den produktiven Einsatz bereit.
Structured Concurrency, Scoped Values, String Templates und Unnamed Classes and Instance Main Methods gehen in eine zweite Preview-Runde.
Diverse sonstige Änderungen runden wie immer das Release ab. Das aktuelle Java-22-Release kannst du hier herunterladen.
Auf welches Java-22-Feature freust du dich am meisten? Welches Feature vermisst du? Lasse es mich gerne über die Kommentarfunktion wissen!
Du möchtest über alle neue Java-Features auf dem Laufenden sein? Dann klicke hier, um dich für den kostenlosen HappyCoders-Newsletter anzumelden.