Java 13 Features mit Beispielen

Java 13 Features (mit Beispielen)

von Sven Woltmann – 8. November 2021

Artikelserie: Java-Versionen

Teil 1: Neue Features in Java 10

Teil 2: Neue Features in Java 11

Teil 3: Neue Features in Java 12

Teil 4: Neue Features in Java 13

Teil 5: Neue Features in Java 14

Teil 6: Neue Features in Java 15

(Melde dich für den HappyCoders-Newsletter an,
um sofort über neue Teile informiert zu werden.)

Bonus:

Java-Version unter Windows umschalten

Java 13 wurde am 17.09.2019 veröffentlicht.

Die Änderungen in Java 13 sind recht überschaubar. Insgesamt haben es nur fünf JDK Enhancement Proposals (JEPs) in das Release geschafft – und davon sind drei als experimentelle bzw. Preview-Features eingestuft.

Der Artikel beginnt mit den experimentellen und Preview-Features, da diese die spannendsten Änderungen in Java 13 darstellen.

Es folgen Performance-Verbesserungen, Erweiterungen der JDK-Klassenbibliothek und sonstige Änderungen, mit denen wir in unserer täglichen Entwicklerarbeit eher selten in Berührung kommen.

Experimentelle und Preview-Features

Auf die experimentellen und Preview-Features werde ich an dieser Stelle nicht in der vollen Tiefe eingehen. Eine ausführliche Beschreibung folgt in denjenigen Teilen der Serie, in denen diese Features Produktionsreife erlangen werden.

Switch Expressions (Second Preview)

Mit dem JEP 325 wurden in Java 12 Switch Expressions als Preview eingeführt. switch kann seither als Anweisung oder als Ausdruck eingesetzt werden. "Ausdruck" bedeutet, dass switch einen Wert zurückgibt, wie z. B. in folgendem Beispiel aus dem JEP:

int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY: break 6; case TUESDAY: break 7; case THURSDAY, SATURDAY: break 8; case WEDNESDAY: break 9; };
Code-Sprache: Java (java)

Auf Grundlage von Feedback aus der Entwickler-Community wird mit dem JDK Enhancement Proposal 354 in Switch-Ausdrücken das break-Keyword durch das neue Keyword yield ersetzt:

int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY: yield 6; case TUESDAY: yield 7; case THURSDAY, SATURDAY: yield 8; case WEDNESDAY: yield 9; };
Code-Sprache: Java (java)

Das neue Keyword ist nur im Scope eines Switch-Ausdrucks bekannt. Solltest du also in deinem Quellcode yield anderweitig verwenden, gibt es höchstwahrscheinlich keinen Anlass deinen Quellcode anzupassen.

Switch Expressions werden im nächsten Release, Java 14, Produktionsstatus erreichen.

Text Blocks (Preview)

Um einen mehrzeiligen String zu definieren, mussten wir bisher Escape-Sequenzen für Zeilenumbrüche und im String enthaltene Anführungszeichen einsetzen. Ein SQL-Statement sah beispielsweise so aus:

String sql = "SELECT id, firstName, lastName FROM Employee\n" + "WHERE departmentId = \"IT\"\n" + "ORDER BY lastName, firstName";
Code-Sprache: Java (java)

JDK Enhancement Proposal 355 ermöglicht es solch einen String deutlich lesbarer zu notieren:

String sql = """ SELECT id, firstName, lastName FROM Employee WHERE departmentId = "IT" ORDER BY lastName, firstName""";
Code-Sprache: Java (java)

Textblöcke werden in Java 15 Produktionsreife erlangen. Im entsprechenden Teil dieser Serie werde ich genauer auf sie eingehen.

Um Text Blocks in Java 13 einzusetzen, musst du sie entweder in deiner IDE aktivieren (in IntelliJ über File→Project Structure→Project Settings→Project→Project language level) oder mit dem Parameter --enable-preview beim Aufruf der javac- und java-Kommandos.

Text Blocks ersetzt den zurückgezogenen JEP 326, "Raw String Literals", der von der Community nicht akzeptiert wurde. Falls du Interesse an den Gründen hast, du findest sie in der jdk-dev-Mailingliste.

ZGC: Uncommit Unused Memory (Experimental)

ZGC ist ein in Java 11 experimentell eingeführter Garbage Collector, der extrem kurze Stop-The-World-Pausen von maximal 10 ms verspricht.

Mit JDK Enhancement Proposal 351 wurde ZGC dahingehend erweitert, dass er ungenutzten Heap-Speicher nach einer bestimmten Zeit an das Betriebssystem zurückgibt.

Mit -XX:ZUncommitDelay kannst du die Zeit in Sekunden angeben, nach der ungenutzter Speicher zurückgegeben wird. Per default ist dieser Wert auf 300 Sekunden eingestellt.

Das Feature ist standardmäßig aktiviert und kann mit -XX:-ZUncommit deaktiviert werden.

ZGC wird in Java 15 Produktionsstatus erreichen. Im entsprechenden Artikel werde ich den neuen Garbage Collector genauer vorstellen.

Performance-Verbesserungen

Dynamic CDS Archives

In Java 10 wurde Application Class Data Sharing eingeführt – ein Feature, das es ermöglicht, eine sogenannte Shared-Archive-Datei zu erstellen. Diese Datei enthält die Klassen der Anwendung in einer binären Form, wie sie die JVM der verwendeten Plattform benötigt. Die Datei wird per Memory-Mapped I/O in den Arbeitsspeicher der JVM gemappt.

Bisher war es relativ aufwändig diese Datei zu erstellen. So musste zunächst während eines Probelaufs der Anwendung eine Klassenliste erstellt werden. Erst in einem zweiten Schritt wurde aus dieser Liste dann das Shared Archive generiert.

Die folgenden Beispiel-Aufrufe sind dem oben verlinkten Artikel entnommen:

java -Xshare:off -XX:+UseAppCDS \ -XX:DumpLoadedClassList=helloworld.lst \ -cp target/helloworld.jar eu.happycoders.appcds.Main java -Xshare:dump -XX:+UseAppCDS \ -XX:SharedClassListFile=helloworld.lst \ -XX:SharedArchiveFile=helloworld.jsa \ -cp target/helloworld.jar
Code-Sprache: Klartext (plaintext)

Mit dem JDK Enhancement Proposal 350 wird dieser Prozess vereinfacht. Das Shared Archive kann ab Java 13 über den Paramter -XX:ArchiveClassesAtExit im Anschluss an die Ausführung der Anwendung generiert werden. Die zusätzlichen Parameter -Xshare:on und -XX:+UseAppCDS werden nicht mehr benötigt:

java -XX:ArchiveClassesAtExit=helloworld.jsa \ -cp target/helloworld.jar eu.happycoders.appcds.Main
Code-Sprache: Klartext (plaintext)

Das erstellte Shared Archive ist deutlich kleiner als zuvor (256 KB statt 9 MB). Es enthält nur noch die Klassen der Applikation. Die JDK-Klassen werden aus dem mit dem JDK ausgelieferten Basis-Archiv classes.jsa geladen.

Verwendet wird das Shared Archive ab Java 13 wie folgt:

java -XX:SharedArchiveFile=helloworld.jsa \ -cp target/helloworld.jar eu.happycoders.appcds.Main
Code-Sprache: Klartext (plaintext)

In dem zu Beginn des Abschnitts verlinkten Artikel findest du ein Beispiel für den Einsatz von AppCDS mit einer Schritt-für-Schritt-Anleitung. Versuch doch einmal das Beispiel nachzustellen und dabei statt der bisherigen zwei Schritte die neue Option -XX:ArchiveClassesAtExit einzusetzen.

Soft Max Heap Size

Mit dem neuen Kommandozeilen-Parameter -XX:SoftMaxHeapSize kann man eine "weiche" Obergrenze für die Heap-Größe festlegen. Der Garbage Collector wird dann versuchen den Heap unter dieser Grenze zu halten und sie nur dann zu überschreiten, wenn es erforderlich ist, um einen OutOfMemoryError zu vermeiden.

Einsatzszenario sind Umgebungen, in denen für die tatsächliche RAM-Nutzung bezahlt wird. So kann der Heap grundsätzlich klein gehalten werden, bei erhöhten Platzbedarf aber vorübergehend über die weiche Obergrenze hinaus wachsen.

Aktuell untersützt dieses Feature nur der (noch experimentelle) ZGC.

(Zu dieser Erweiterung gibt es kein JDK Enhancement Proposal.)

Erweiterungen der JDK-Klassenbibliothek

Es wurden mehrere Methoden zur Klasse ByteBuffer hinzugefügt, mit der Lese-/Schreiboperationen an vorgegebenen Buffer-Positionen möglich sind, anstatt – wie bisher üblich – an der durch den ByteBuffer selbst verwalteten Position.

Falls du eine Auffrischung zum Thema ByteBuffer benötigst, empfehle ich dir diesen ByteBuffer-Grundlagenartikel.

ByteBuffer.slice()

Mit ByteBuffer.slice() kannst du eine View auf einen Teil des Puffers erzeugen. Diese bereits vor Java 13 existierende Methode liefert eine View, die an der aktuellen Position des Puffers beginnt und deren Kapazität und Limit den verbleibenden Bytes des Puffers entsprechen.

Neu ist die Methode ByteBuffer.slice(int index, int length). Mit dieser kannst du eine View erstellen, die an Position index beginnt und length Bytes enthält. Die neue Methode ignoriert somit Position, Kapazität und Limit des zugrunde liegenden Puffers.

Neue ByteBuffer.get()- und put()-Methoden

Analog gibt es jeweils zwei neue get()- und put()-Methoden, mit der die Daten nicht – wie bisher – an der aktuellen Position des Puffers gelesen/geschrieben werden, sondern an der explizit angegeben Position:

  • get​(int index, byte[] dst, int offset, int length) – überträgt length Bytes ab der durch index angegebenen Position des Puffers in das Byte-Array dst ab Position offset.
  • get​(int index, byte[] dst) – überträgt Daten ab der durch index angegebenen Position des Puffers in das Byte-Array dst. Die Anzahl der übertragenen Bytes entspricht der Länge des Ziel-Arrays.
  • put​(int index, byte[] src, int offset, int length) – überträgt length Bytes aus dem Byte-Array src ab Position offset in den Puffer ab Position index.
  • put​(int index, byte[] src) – überträgt alle Bytes aus dem Byte-Array src in den Puffer ab Position index.

Die Position des Puffers bleibt bei allen vier Methoden unverändert.

FileSystems.newFileSystem()

Mit der Methode FileSystems.newFileSystem(Path path, ClassLoader loader) kannst du ein Pseudo-Dateisystem erstellen, dessen Inhalt auf eine Datei (wie z. B. eine ZIP- oder JAR-Datei) gemappt wird.

Die Methode wurde in Java 13 mit einer Variante überladen, die es ermöglicht eine anbieterspezifischen Konfiguration des Dateisystems mit zu übergeben: FileSystems.newFileSystem​(Path path, Map env, ClassLoader loader)

Weiterhin wurden zwei Varianten jeweils ohne den loader-Parameter hinzugefügt. Ein Class Loader wird nur dann benötigt, wenn der sogenannte FileSystemProvider für den zu mappenden Dateityp nicht im JDK registriert ist, sondern über den angegebenen Class Loader geladen werden soll. Für gängige Dateitypen wie ZIP oder JAR ist das nicht erforderlich.

Sonstige Änderungen in Java 13 (die man als Java-Entwickler nicht unbedingt kennen muss)

Dieser Abschnitt listet Änderungen auf, mit denen eher wenige Java-Entwicklerinnen und -Entwickler in Berührung kommen werden.

Reimplement the Legacy Socket API

Die APIs java.net.Socket und java.net.ServerSocket existieren seit Java 1.0. Der zugrundeliegende Code (eine Mischung aus Java- und C-Code) ist schwer wart- und erweiterbar, besonders im Hinblick auf Project Loom, mit dem leichtgewichtige Threads ("Fibers") eingeführt werden sollen.

Mit dem JDK Enhancement Proposal 353 wird die alte Implementierung durch eine modernere, besser wart- und erweiterbare Implementierung ersetzt, die sich inbesondere auch ohne weitere Refactorings an Projekt Loom anpassen lassen soll.

Unicode 12.1

Wie schon in den vorangegangenen zwei Java-Releases wurde auch in Java 13 der Unicode-Support erhöht – auf Version 12.1. Das bedeutet, dass Klassen wie z. B. String und Character mit den neuen Zeichen, Codeblöcken und Schriftsystemen umgehen können müssen.

Ein Beispiel dazu findest du im Artikel über Unicode 10 in Java 11.

(Für die Unterstützung von Unicode 12.1 existiert kein JDK Enhancement Proposal.)

Vollständige Liste aller Änderungen in Java 13

Dieser Artikel hat alle Features von Java 13 vorgestellt, die in JDK Enhancement Proposals definiert sind, sowie Erweiterungen an der JDK-Klassenbibliothek, die keinem JEP zugeordnet sind.

Eine vollständige Liste aller Änderungen findest du in den offiziellen Java 13 Release Notes.

Fazit

Java 13 ist ein sehr überschaubares Release.

Im zweiten Preview der "Switch Expressions" wurde break durch yield ersetzt. Mit dem Preview von "Text Blocks" halten endlich mehrzeilige Strings Einzug in die Sprache.

Der noch experimentelle ZGC kann ungenutzten Speicher an das Betriebssystem zurückgeben und mit einer "weichen" maximalen Heap-Größe konfiguriert werden.

Mit "Dynamic CDS Archives" ist es ab Java 13 ein Kinderspiel Application Class Data Sharing einzusetzen.

ByteBuffer wurde um Methoden erweitert, um lesend und schreibend auf absolute Positionen zuzugreifen, und es gibt einige neue Varianten der FileSystems.newFileSystem()-Methode.

Die aus Java 1.0 stammende Socket API wurde komplett neugeschrieben, um fit für die in Project Loom entwickelten leichtgewichtige Threads zu sein.

Wenn dir der Artikel gefallen hat, freue ich mich sehr über einen Kommentar, oder wenn du den Artikel über einen der Share-Buttons am Ende teilst.

Wenn du informiert werden möchtest, wenn der nächste Teil der Serie online geht, klicke hier, um dich für den HappyCoders-Newsletter anzumelden.

Sven Woltmann
Über den Autor
Ich bin freiberuflicher Softwareentwickler mit über 20 Jahren Erfahrung in skalierbaren Java-Enterprise-Anwendungen. Mein Schwerpunkt liegt auf der Optimierung komplexer Algorithmen und auf fortgeschrittenen Themen wie Concurrency, dem Java Memory Model und Garbage Collection. Hier auf HappyCoders.eu möchte ich dir helfen, ein besserer Java-Programmierer zu werden. Lies mehr über mich hier.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Die folgenden Artikel könnten dir auch gefallen