Der 16. September markiert einen wichtigen Meilenstein im Java-Ökosystem: Die Veröffentlichung der neuesten LTS-Version – Java 25. Es sind fast genau zwei Jahre seit dem letzten LTS-Release Java 21 (19. September 2023) vergangen, und diese Version mit ihren 18 JEPs enthält spannende Neuerungen sowohl für Einsteiger als auch für erfahrene Entwickler.
API-Änderungen
JEP 506: Scoped Values
Scoped Values ermöglichen es einer Methode, unveränderliche Daten sowohl mit ihren aufgerufenen Methoden innerhalb eines Threads als auch mit untergeordneten Threads zu teilen. Sie sind vergleichbar mit ThreadLocal, das in Java 1.2 eingeführt wurde, wurden jedoch im Hinblick auf Benutzerfreundlichkeit, Verständlichkeit, Robustheit und Performance entwickelt.
Einfach ausgedrückt erlaubt es Scoped Values, dass verschiedene Teile eines Java-Programms einen Zustand gemeinsam nutzen können, ohne dass dieser explizit als Eingabeparameter jeder Methode übergeben werden muss. Das kann besonders nützlich in Webanwendungen sein, bei denen ein Kontext zwar von einem Request-Handler aus einer Anfrage abgeleitet wird, aber von jeder Methode verwendet werden kann, die diesen Kontext benötigt – auch wenn sie selbst keinen Zugriff auf das ursprüngliche Request hat.
Dies lässt sich im folgenden Beispiel demonstrieren:
class Framework {
private static final ScopedValue<FrameworkContext> CONTEXT
= ScopedValue.newInstance();
void serve(Request request, Response response) {
var context = createContext(request);
where(CONTEXT, context)
.run(() -> Application.handle(request, response));
}
public PersistedObject readKey(String key) {
var context = CONTEXT.get();
var db = getDBConnection(context);
db.readKey(key);
}
}
Hier richtet ScopedValue#where
die Umgebung ein, und #run
führt Application#handle
mit diesem Kontext aus. Der Kontext wird unmittelbar nach dem Verlassen der Methode wieder gelöscht.
JEP 510: Key Derivation Function API
A KDF (Key Derivation Function) is often used to create cryptographic data from which multiple keys can be obtained. …Deriving keys is similar to hashing passwords.
Diese neue API, die sich in der Vorschau in JEP 478 befand und in dieser Version finalisiert wird, bringt eine einheitliche Möglichkeit für Java, KDF-Algorithmen zu handhaben – zum Beispiel durch die Erzeugung kryptografisch sicherer Schlüssel, wie im folgenden Beispiel:
// Ein KDF-Objekt für den Algorithmus HKDF-SHA256 erzeugen
KDF hkdf = KDF.getInstance("HKDF-SHA256");
// Eine Parameter-Spezifikation von Typ ExtractExpand erzeugen
AlgorithmParameterSpec params =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial)
.addSalt(salt).thenExpand(info, 32);
// Den 32-byte AES Schlüssel ermitteln
SecretKey key = hkdf.deriveKey("AES", params);
// Weitere Schlüsselermittlungen können mit demselben KDF-Objekt durchgeführt werden.
Wenn Sie es gewohnt sind, die alten APIs wie KeyGenerator
und SecretKeyFactory
zu verwenden, werden Sie sicher zustimmen, dass die neue Vorgehensweise deutlich eleganter und prägnanter ist.
JEP 511: Module Import Declarations
Dieses JEP zielt darauf ab, die Gesamtanzahl der Import-Anweisungen am Anfang jeder Java-Datei zu reduzieren, indem es ermöglicht wird, ganze Module statt einzelner Klassen zu importieren – ähnlich wie es bereits mit Klassen und Interfaces im Paket java.lang
gehandhabt wird. Anstatt also etwas wie Folgendes schreiben zu müssen:
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
kann dasselbe jetzt mit einer einzigen Zeile erreicht werden:
import module java.base;
Falls es zu Mehrdeutigkeiten bei der importierten Klasse oder dem Interface kommt (etwa weil verschiedene Pakete Klassen oder Interfaces mit demselben Namen enthalten), kann dies durch den expliziten Import des vollständig qualifizierten Namens aufgelöst werden, z. B.:
import module java.base; // exportiert java.util, das eine public Date-Klasse enthält
import module java.sql; // exportiert java.sql, as eine public Date-Klasse enthält
import java.sql.Date; // behebt die Mehrdeutigkeit des einfachen Namens "Date"!
...
Date d = ... // Alles gut! Date wird zu java.sql.Date aufgelöst.
JEP 512: Compact Source Files and Instance Main Methods
Diese Änderung richtet sich in erster Linie an Anfänger, indem sie Boilerplate-Code reduziert – besonders an Stellen, an denen Neueinsteiger ihren ersten Code schreiben, ohne bereits alle Bestandteile der Java-Sprache zu verstehen. Das wird im folgenden Beispiel deutlich. Hier ist das klassische “Hallo Welt”-Beispiel, dem jeder Anfänger begegnet:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hallo, Welt!");
}
}
Und so lässt sich dieselbe Funktionalität nun auf neue Weise erreichen:
void main() {
IO.println("Hallo, Welt!");
}
Es ist definitiv viel einfacher und unkomplizierter für eine Lehrkraft, einem Anfänger zu erklären, wie es funktioniert. Natürlich kann die neue IO-Klasse nicht nur zum Ausgeben auf der Konsole verwendet werden, sondern auch zum Einlesen! Zum Beispiel:
void main() {
String name = IO.readln("Bitte geben Sie Ihren Namen ein: ");
IO.print("Schön Sie kennenzulernen, ");
IO.println(name);
}
JEP 513: Flexible Constructor Bodies
Flexible Constructor Bodies vereinfachen die Initialisierungslogik von Objekten in Java, indem sie es erlauben, Anweisungen vor einem expliziten Konstruktoraufruf, also super()
oder this()
, zu platzieren. Dadurch wird eine einfache Möglichkeit geschaffen, Argumente zu validieren, bevor die Konstruktoren der Superklasse aufgerufen werden, und insgesamt zusätzliche Sicherheiten geboten, dass der Zustand eines neuen Objekts vollständig initialisiert ist, bevor es bei anderen Stellen des Programms verwenden kann. Hier ist ein einfaches Beispiel, das zeigt, was nun möglich ist:
class Employee extends Person {
String officeID;
Employee(..., int age, String officeID) {
if (age < 18 || age > 67)
// Now fails fast!
throw new IllegalArgumentException(...);
super(..., age);
this.officeID = officeID;
}
}
Änderungen der Java-Platform
JEP 503: Remove the 32-bit x86 Port
Mit der Deprecation des 32-Bit-x86-Ports in Java 24 durch JEP 501 wird nun auch der Quellcode entfernt. Dies betrifft den Linux 32-Bit-x86-Port, der als letzter im JDK verbleibt. Ziel ist es, die Build- und Test-Infrastruktur des JDK zu vereinfachen und neue Features zu ermöglichen, die plattformspezifische Unterstützung benötigen, ohne auf 32-Bit-x86-Fallbacks Rücksicht nehmen zu müssen.
JEP 519: Compact Object Headers
Compact Object Headers sind keine experimentelle Funktion mehr. Zusammen mit JEP 450 ist das Ziel, die Größe der Objekt-Header in der HotSpot JVM auf 64-Bit-Architekturen von bisher 96 bis 128 Bit auf 64 Bit zu reduzieren. Dies verringert die Heap-Größe und erhöht die Datenlokalität.
Compact headers bieten eindeutige Leistungsverbesserungen, wie in den folgenden Experimenten gezeigt wird:
- In einem Szenario verwendet der SPECjbb2015-Benchmark 22% weniger Heap-Speicher und 8% weniger CPU-Zeit.
- In einem anderen Szenario wird die Anzahl der Garbage Collections im SPECjbb2015-Benchmark um 15% reduziert, sowohl bei den G1- als auch den Parallel-Collector.
- Ein hochparalleler JSON-Parser-Benchmark läuft 10% schneller.
JEP 521: Generational Shenandoah
Der Shenandoah-Garbage-Collector wurde in JEP 404 (Java 24) mit generationalen Sammelfähigkeiten erweitert, um den nachhaltigen Durchsatz, die Belastungsspitzen-Resilienz und die Speichernutzung zu verbessern. Mit Java 25 ist dies keine experimentelle Funktion mehr und kann über die folgenden Kommandozeilenoptionen aktiviert werden:
$ java -XX:+UseShenandoahGC \
-XX:ShenandoahGCMode=generational ...
AOT-Optimierungen
JEP 514: Ahead-of-Time Command-Line Ergonomics
Um das Erstellen von ahead-of-time caches, die den Start von Java-Anwendungen beschleunigen, zu erleichtern, wurden die für gängige Anwendungsfälle erforderlichen Kommandozeilenargumente vereinfacht. Was zuvor ein zweistufiger Workflow war:
$ java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconf \
-cp app.jar com.example.App ...
$ java -XX:AOTMode=create -XX:AOTConfiguration=app.aotconf \
-XX:AOTCache=app.aot
kann jetzt in nur einem, kürzeren Befehl erledigt werden:
$ java -XX:AOTCache=app.aot -cp app.jar com.example.App ...
JEP 515: Ahead-of-Time Method Profiling
Weitere Optimierungen wurden für die AOT-Kompilierung der HotSpot JVM implementiert. Mit dieser Änderung ist der JIT-Compiler in der Lage, nativen Code sofort beim Anwendungsstart zu generieren, anstatt darauf warten zu müssen, dass Profile gesammelt werden. Diese Optimierungen betreffen den in JEP 483 eingeführten AOT-Cache, und gemäß der Spezifikation können die Startzeiten bei kleineren Anwendungen um 19 % verbessert werden, während komplexe und länger laufende Programme ebenfalls schneller aufgewärmt werden können.
JFR-Verbesserungen
JEP 518: JFR Cooperative Sampling
Das Parsen von Thread-Stacks im JDK Flight Recorder kann jetzt noch genauere Ergebnisse liefern. Bisher verwendete JFR Thread-Unterbrechungen zur Datensammlung, was bei hoher Last die Ergebnisse verfälschen konnte. Nun pausieren Threads an safepoints, um Statistiken zu melden. Die Daten bleiben konsistent, und die Auswirkung auf die Performance ist minimal.
JEP 520: JFR Method Timing & Tracing
Durch die Verwendung von bytecode instrumentation ist der JFR nun in der Lage, Methodentimings und Tracing genauer zu erfassen, ohne sich auf stichprobenbasierte Statistiken zu verlassen.
MethodTiming
zeichnet die Ausführungszeiten von Methoden mit Nanosekunden-Präzision aufMethodTrace
erstellt Aufrufketten für kritische Abschnitte.
Vorschau
Die folgenden Features sind noch nicht finalisiert und können nur mit speziellen Flags in der Kommandozeile aktiviert werden. Da es üblicherweise von JEP zu JEP zu Breaking Changes kommt, werden wir sie nur als Referenzen aufführen, bis die Features stabil sind und in einer zukünftigen Java-Version enthalten sind:
- JEP 470: PEM Encodings of Cryptographic Objects (Preview)
- JEP 502: Stable Values (Preview)
- JEP 505: Structured Concurrency (Fifth Preview)
- JEP 507: Primitive Types in Patterns, instanceof, and switch (Third Preview)
- JEP 508: Vector API (Tenth Incubator)
- JEP 509: JFR CPU-Time Profiling (Experimental)
Fazit
Dieser wichtige Meilenstein – die Veröffentlichung der neuesten LTS-Version von Java (Java 25) – bringt spannende Features und Optimierungen in die Programmiersprache und rückt uns gleichzeitig einen Schritt näher an spannende Veränderungen wie die Structured Concurrency, insbesondere nach der Einführung der Virtual Threads in Java 21. In jedem Fall zeigt diese neue Version, dass sich Java kontinuierlich weiterentwickelt und dabei jedes Mal die Werkzeuge, die wir schon lieben, noch besser macht.