Das neue Java 21 enthält eine Reihe interessanter Features, darunter diverse Änderungen an der Sprache und den verfügbaren Implementierungen. Allerdings hat Oracle auch die Zahlung wichtiger Unterhaltsleistungen eingestellt.
Java 21 erschien Mitte September 2023 pünktlich im mittlerweile eingespielten Halbjahrestakt. Das Release [1] wurde seit Jahren als erste wesentliche Version nach Java 11 und 17 angekündigt. Außerdem sollte es dank Langzeit-Support (LTS) zur neuen Standardversion avancieren.
In Sachen Neuerungen hat Java 21 einiges zu bieten: Allein 15 JDK Enhancement Proposals (JEPs) stehen in den Release Notes [2]. Acht davon sind zur allgemeinen Verwendung gedacht, die anderen befinden sich noch im Preview- oder Incubator-Status. Dazu kommen noch gut 80 kleinere Änderungen und diverse Bugfixes. Daneben entfielen einige nicht mehr gewünschte Features aus der Sprache, der Laufzeitbibliothek und den mitgelieferten Tools. Beim Thema LTS sieht es dagegen weniger rosig aus – mehr dazu weiter unten.
Projekt Amber
Das Projekt Amber [3] arbeitet seit einigen Jahren an Funktionen, die Java produktiver machen sollen. Weniger Codevolumen und bessere Unterstützung durch den Compiler stehen dabei ganz oben auf der Wunschliste. Sowohl die Record Patterns (JEP 440) als auch das Pattern Matching for Switch (JEP 441) stammen aus diesem Projekt und sind nun allgemein verfügbar.
Wie Listing 1 demonstriert, dürfen Sie Records jetzt einfach mit einem »instanceof« identifizieren, wobei Sie das Casten und Zuweisen zu einer Variable dann gleich mit erledigen. Das funktioniert nicht nur für die einfachen Werte eines Records (Zeile 5), sondern auch für geschachtelte Records (Zeile 7).
Listing 1
Record Patterns, Pattern Matching, Unnamed Patterns
public static record Eintrag(int klasse, String schüler) {}
public static record Seite(Date tag, Eintrag e1, Eintrag e2) {};
Object o = [...]
if (o instanceof Eintrag(int k, String s)) {
System.out.printf("Eintrag für %s aus Klasse %d %n",s,k);
} else if (o instanceof Seite(Date d, Eintrag a, Eintrag b)) {
System.out.printf("Seite vom %tD: %s, %s %n",d, a.schüler, b.schüler);
} else if (o instanceof String s) {
System.out.printf("String %s %n", s);
}
switch(o) {
// Typ und Nebenbedingungen
case Seite(var d, var a, var b)
when 10 == a.klasse -> {
System.out.printf("Untersekunda %s\n",a.schüler, b.schüler);
}
// Typ
case Seite(var d, var a, var b) -> {
System.out.printf("Klasse %d %s\n",a.klasse, a.schüler);
}
// Preview: Unnamed Variable
case Eintrag(_, var schüler) -> {
System.out.printf("Schüler%s\n",schüler);
}
default: {
System.out.printf("Anderes %s\n",o);
}
}
try {
[...]
} catch (IllegalArgumentException _) {}
Darüber hinaus erweiterten die Entwickler das Switch-Statement. Ähnlich wie bei »instanceof« gibt es hier eine automatische Zuweisung zur Variablen (Zeile 19) sowie die Möglichkeit zusätzlicher Nebenbedingungen mit dem When-Statement (Zeile 14). Das hätte man auf den ersten Blick genauso mit If-Statements erledigen können, das neue Switch-Statement verspricht allerdings deutlich mehr Sicherheit. Dabei überprüft der Compiler, ob der Code alle vorkommenden Möglichkeiten mit einem »case« abdeckt. Falls nicht, meldet er Compiler einen Fehler, und Sie müssen den Code mit weiteren Case-Statements oder einem generischen »default« erweitern.
Wie erwähnt, haben weitere Änderungen aus dem Projekt Amber Einzug in Java 21 gehalten. Da die Features den Preview-Status bisher nicht verlassen haben, müssen Sie beim Kompilieren und zur Laufzeit die Option »–enable-preview« angeben. Bei den Unnamed Patterns and Variables (JEP 443) geht es um die Fälle, in denen Sie Variablen aus Gründen der Syntax aufnehmen müssen, ohne sie inhaltlich tatsächlich zu nutzen. Die statische Codeanalyse in der IDE oder Werkzeuge wie Sonar meckern dann völlig legitim, aber unnötigerweise über ungenutzte Variablen. Dank der Syntaxerweiterung können Sie solche Variablen in Zukunft mit einem Unterstrich (»_«) schreiben. Damit wissen Compiler und Codeanalyse, dass die Variable selbst nicht zum Einsatz kommt. Das funktioniert für Switches (Zeile 22) und Try-Catch-Statements (Zeile 31).
Previews
Neben den Neuerungen aus Projekt Amber befinden sich noch zwei weitere interessante Features im Preview-Status. Die Scoped Values (JEP 446), in Java 20 vorgestellt, sollen als bessere Alternative zu den Thread-Locale-Variablen dienen. Gegenüber der ursprünglichen Beschreibung [4] flossen keine wesentlichen Änderungen ein, wahrscheinlich werden die Scoped Values in Java 22 allgemein zur Verwendung bereitstehen.
Einfacher Java-Code lässt sich schon länger ohne Kompilieren direkt auf der Kommandozeile starten, erwartet aber ein gültiges Java-Main-Program mit einer Klasse und der statischen Main-Methode. Dank der Unnamed Classes and Instance Main Methods (JEP 445) können Sie darauf verzichten: Das Beispiel aus Listing 2 ohne Klasse lässt sich nun direkt starten. Wegen des Preview-Status müssen Sie das Feature extra freischalten (Abbildung 1).
Listing 2
Code ohne Klassen
void main() {
System.err.println("Klassenlos");
}

Abbildung 1: Ausführen des Java-Codes aus Listing 2 auf der Kommandozeile.
Interpoliert
Mit Java 21 haben die Entwickler unterschiedliche Modifikationen an den mitgelieferten Bibliotheken vorgenommen. Eine echte Überraschung sind die im Preview-Status befindlichen String Templates (JEP 430). Mit ihnen soll Java zu anderen Sprachen wie C#, Javascript oder Python aufschließen und einen einfachen und leserlichen Weg zur String-Interpolation bieten. Zum Erstellen von Strings auf Basis von Variablen gab es in Java bisher lediglich die String Concatenation, seit Java 1.5 das Format-Statement, und separate Bibliotheken wie Freemarker [5]. Ein Äquivalent zur oft mit »$”…”« geschriebenen String-Interpolation fehlte hingegen.
Die im aktuellen Release gewählte Lösung fällt verglichen mit anderen Sprachen etwas aufwendiger aus, aber auch deutlich mächtiger. Als Templates dienen Strings, die neben konstanten Text Variablennamen enthalten. Letztere ersetzt ein Processor beim Auswerten durch die Variablenwerte. Zur Kennzeichnung der Variablennamen dienen »\{«- und »}«-Paare (Listing 3, Zeile 8). Statt des fest in der Sprachsyntax verankerten Dollar-Zeichens zur Deklaration der String-Interpolation hängen Sie das Template einfach zur Auswertung an ein Processor-Objekt. Im einfachsten Fall nutzen Sie einen der mitgelieferten Processors wie »STR« oder »FMT«, die als statische Variable bereitstehen (Listing 3, Zeile 1).
Listing 3
String Templates
import static java.lang.StringTemplate.STR;
var name = "Erika Mustermann";
var tag = new java.util.Date();
System.out.println(STR."Hallo \{name}");
var fp = FormatProcessor.create(Locale.GERMAN);
System.out.println(fp."""
\{name}, am %tA\{tag} %te\{tag} %tB\{tag}
""");
StringTemplate.Processor<JSONObject, JSONException> JSON = template -> {
String quote = "\"";
List<Object> ersatz = new ArrayList<>();
for (Object value : template.values()) {
String roh = value != null ? value.toString() : "null";
String gekapselt= roh.replace("\"", "\\\"");
ersatz.add(gekapselt);
}
var result = StringTemplate.interpolate(template.fragments(), ersatz);
return new JSONObject(result);
};
System.out.println(JSON."""
{
"name": "\{name}",
}
""");
Der Processor »STR« ersetzt die Variablennamen durch den String-Wert der Variablen. Der etwas aufwendigere Processor »FMT« unterstützt darüber hinaus Formatierungshinweise wie die Anzahl von Dezimalstellen oder die Angabe, welche Teile eines Datumsobjekts genutzt werden sollen. Neben den eingebauten Processors lassen sich eigene erzeugen. Für die Formatierung mit deutschen Tages- oder Monatsnamen wurde in Zeile 6 ein FMT-Processor mit deutscher Locale erzeugt. In Zeile 8 schließt sich ein mehrzeiliges Template mit spezieller Datumsformatierung an. Die Ausgabe von Listing 3 sehen Sie in Abbildung 2.

Abbildung 2: Die Ausgabe der ausgefüllten Templates aus Listing 3.
Das Implementieren eigener Processors funktioniert anders als mit der Syntax »$”«. Ein Anwendungsfall dafür liegt im Kapseln von Sonderzeichen in Variablenwerten, die bei Sprachen wie JSON oder XML nicht erlaubt sind. Ein JSON-Processor kann so das doppelte Anführungszeichen (»”«) in einem Variablenwert durch das äquivalente »\”« ersetzen, ein Processor für SQL-Statements kann das Einfügen von aktivem Code unterbinden, und so weiter.
Schließlich erlaubt das Interface »StringTemplate.Processor« sogar die Definition anderer Rückgabewerte als Strings. So definiert Zeile 10ff von Listing 3 einen Processor, der erst einen JSON-formatierten String erzeugt und ihn daraufhin direkt als JSON-Objekt zurückgibt. Das Kapseln der Anführungszeichen und Parsen des Strings findet im Processor statt.
Die Sequenced Collections (JEP 431) erleichtern die Arbeit mit Kollektionen. Wie Sie in Listing 4 zu sehen, können Sie ohne weitere Klimmzüge ein Element vom Anfang und vom Ende her adressieren. Dazu haben die Entwickler das spezielle Interface »SequencedCollection« geschaffen, das Lists sowie Sets und Maps mit Sortierreihenfolge unterstützen.
Ohne JEP kamen kleinere Änderungen im String-Umfeld aus. Das neue »indexOf« in Zeile 7 sucht nur von Position 6 bis 19 nach dem String »von2« und gibt den Index der Fundstelle zurück; Zeile 8 erledigt dasselbe für einzelne Buchstaben. Bei »splitWithDelimeters« lässt sich die maximale Anzahl der Teile angeben: In Zeile 9 bekommen Sie die Teile »abc« und »;« sowie den ungeteilten Rest »cde;efg«.
Listing 4
Collections und Strings
var liste = new ArrayList<String>();
liste.add("B");
liste.addFirst("B");
liste.addLast("C");
liste.removeFirst();
liste.removeLast();
int idx = "von1,von2,von3,von2".indexOf("von2",6,19);
idx = "von,von,von ".indexOf((int)'v',4,8);
String[] teile = "abc;cde;efg".splitWithDelimiters(";",2);
Verwoben
Das Project Loom [6] kümmert sich seit Java 19 um die Programmierung nebenläufiger Prozesse. Mit Java 21 haben die Virtual Threads (JEP 444) offiziell Einzug gehalten. Für Programmierer verhalten sie sich nicht anders als herkömmliche Threads, werden jedoch nicht eins zu eins auf einen Thread im Betriebssystem abgebildet. Stattdessen verteilt die JVM die Last auf mehrere Threads im Betriebssystem, was weniger Ressourcen erfordert. Gerade Webserver mit vielen kurzlebigen Threads können auf diese Weise mehr Anfragen beantworten.
Die damit zusammenhängende Structured Concurrency (JEP 453) verharrt allerdings noch im Preview-Status. Dank ihr lassen sich mehrere parallel ablaufende Unterprozesse so bündeln, dass Sie die gesamte Gruppe bezüglich der Ergebnis- oder Fehlerbehandlung einfach zusammenfassen können.
Neben diesen allgemein anwendbaren Änderungen gibt es mit Java 21 zusätzliche Bibliothekserweiterungen, die nur wenige Programmierer direkt nutzen dürften. Dazu gehört unter anderem die für neuere Verschlüsselungsverfahren wie elliptische Kurven notwendige Key Encapsulation Mechanism API (JEP 452). Sie läuft in der Regel unter der Haube eines HTTPS-Aufrufs.
Die weiteren Änderungen befinden sich schon länger in der Mache und scheinen nie zu einem Ende zu kommen. Die Foreign Function & Memory API (JEP 442) soll erlauben, C-Bibliotheken aus Java heraus zu verwenden, befindet sich aber inzwischen bereits in der dritten Preview.
Noch weiter entfernt von einer tatsächlichen Implementierung ist die Vector API (JEP 448) zur Vektorberechnung direkt auf der CPU. Sie hat mit dem sechsten Incubator-Status nahezu transzendente Höhen erreicht. Allerdings scheitere ich nicht als einziger daran, damit eine bessere Performance als mit normalem Java-Code zu erreichen. Den kompletten Beispielcode für diese API und alle anderen finden Sie auf Github [7].
Unterhaltsleistungen
In allen Roadmaps war Java 21 bisher als LTS-Edition gekennzeichnet. Dementsprechend galt es für die meisten Anwender als die Java-Version, die sie nach 11 und 17 produktiv einsetzen wollten. Die kleine Fußnote LTS/non-LTS designation and dates are subject to change [8] ging dabei nahezu unter. Umso größer war die Überraschung, als Nicolai Parlog im Juli 2023 in seinem Blog den Artikel “Java 21 is no LTS Version” postete [9].
Darin weist der bei Oracle angestellte Java Developer Advocate noch einmal darauf hin, dass sich eine LTS-Version technisch nicht von einer anderen Java-Version wie 19 oder 20 unterscheidet. Laut Parlog kommt es schlicht darauf an, ob jemand die fragliche Version über mehrere Jahre pflegt und dabei Sicherheitslücken und Bugs beseitigt. Das eigentliche Java-Entwicklungsteam konzentriert sich ja nach der Freigabe eines Releases auf die Arbeit an den nächsten Versionen. Das Unterhalten der bestehenden Veröffentlichungen übernahm bisher Oracle und stellte in den letzten Jahren insgesamt 28 Bugfix-Releases für Java 11 und 17 bereit. Pünktlich zu Java 21 entschied sich das Unternehmen nun, diese erweiterte Unterhaltsleistung für den Sprössling nicht mehr zu finanzieren.
Sieht man sich in der Presse und im Internet um, stellt man fest, dass diese Nachricht noch nicht bei allen angekommen ist, denn Java 21 wird immer noch als LTS bezeichnet. Allerdings gibt es anders als für Java 11 und Java 17 keine Möglichkeit, eine kostenlose JVM mit dem LTS-Stempel herunterzuladen.
Je nach Anwendungsfall können Java-Nutzer derzeit aus drei Optionen für den Einsatz von Java 21 wählen. Wer keinen LTS-Bedarf hat, kann direkt auf Java 21 wechseln und sich der neuen Features erfreuen. Wer dagegen ausschließlich LTS-Version einsetzen will oder darf, muss (zumindest momentan) kostenpflichtigen LTS-Support für Java 21 bei Azul oder Oracle einkaufen. Idealerweise würde zum Beispiel Adoptium Java 21 pflegen und als LTS für die nächsten zwei bis drei Jahre kostenlos bereitstellen.
Fazit
Rein von der technischen Seite her betrachtet ist Java 21 sehr attraktiv: Viele Änderungen machen das Programmieren sicherer und schneller oder stellen komplett neue Möglichkeiten bereit. Allerdings erweist Oracles Politikänderung in Sachen Langzeit-Support dem Erfolg von Java einen echten Bärendienst. Solange es keine kostenlose Java-Version mit LTS-Stempel gibt, werden viele Anwender und Bibliotheken schlicht an Java 17 festhalten.
Anders sieht es zum Beispiel bei der von Gluon gepflegten Oberflächenbibliothek JavaFX aus. Das moderne UI-Toolkit kam zeitgleich mit Java 21 als JavaFX 21, Gluon bietet jedoch anders als Oracle kostenfreien LTS-Support an [10]. JavaFX 21 selbst bringt knapp 90 Verbesserungen und Bugfixes mit sich. Linux-spezifisch ist der finale Schritt auf GTK3 vollzogen. Die alte GTK2-Bibliothek wird nicht länger unterstützt – 25 Jahre nach Erscheinen von GTK3 sicherlich kein übereilter Schritt.
JavaFX selbst zeigt sich ähnlich mächtig wie die Alternative von Microsoft, lässt sich aber plattformunabhängig einsetzen. Eine besondere Demo dafür bietet JavaFX Central. Diese Webseite ist mehr als eine reine Sammlung von JavaFX-Beispielanwendungen und Bibliotheken: JavaFX steht alternativ als lokale Desktop-Applikation beziehungsweise als Webanwendung im Browser [12] bereit (Abbildung 3). (csi/tre)
Der Autor
Carsten Zerbst erstellt Software für Ingenieure, vom einfachen Konverter bis hin zur unternehmenskritischen Integrationslösung. Sein Team sucht für nächstes Jahr Verstärkung in Hamburg und bietet interessante Themen für Abschlussarbeiten an.
Infos
- OpenJDK 21: https://adoptium.net/
- Release Notes: https://jdk.java.net/21/release-notes
- Project Amber: https://openjdk.org/projects/amber
- Java 20: Carsten Zerbst, “Sprunghaft”, LM 06/2023, S. 72, https://www.lm-online.de/49013
- Freemarker: https://freemarker.apache.org
- Project Loom: https://wiki.openjdk.org/display/loom/Main
- Beispielquellen: https://github.com/skfcz/Java21
- Java SE Support Roadmap: https://www.oracle.com/java/technologies/java-se-support-roadmap.html
- “Java 21 is no LTS Version”: https://nipafx.dev/inside-java-newscast-52
- Gluon: https://gluonhq.com/products/javafx/
- JavaFX Central: https://www.jfx-central.com/
- JavaFX im Browser: https://www.jfx-ensemble.com/sample/jfoenix/Masonry







