Zum Inhalt springen

Java 13 Features (mit Beispielen)

Tasse Cappuccino mit Herz im Milchschaum auf einem Holztisch – Java 13 Features

Java 13 wurde am 17.09.2019 veröffentlicht.

Die Änderungen in Java 13 sind ü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 Entwicklungsarbeit eher selten in Berührung kommen.

Experimentelle und Preview-Features

Auf die experimentellen und Preview-Features gehe ich an dieser Stelle nicht in der vollen Tiefe ein. Eine ausführliche Beschreibung findest du in denjenigen Teilen der Serie, in denen diese Features Produktionsreife erlangt haben.

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, an den JEP angelehnten Beispiel:

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

Auf Grundlage von Feedback aus der Entwickler-Community wird mit dem JDK Enhancement Proposal 354 in Switch Expressions 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;
};

yield ist ein sogenannter Restricted Identifier: Variablen dürfen weiterhin so heißen, eine Methode namens yield musst du aber qualifiziert aufrufen – z. B. Thread.yield() oder this.yield(…). Java 13 warnt bei unqualifizierten Aufrufen, ab Java 14 sind sie ein Kompilierfehler. Eine Klasse darf ab Java 14 nicht mehr yield heißen.

Switch Expressions haben im nächsten Release, Java 14, Produktionsstatus erreicht. Alle Details über sie findest du im Hauptartikel über Switch Expressions.

Text Blocks (Preview)

Um einen mehrzeiligen String zu definieren, mussten wir vor Java 13 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";

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""";

In Java 13 und 14 musst du Text Blocks als Preview-Feature aktivieren – entweder in deiner IDE (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.

Zusammen mit Text Blocks kamen drei neue Methoden in die Klasse String – in Java 13 ebenfalls als Preview-APIs:

  • stripIndent() entfernt die gemeinsame Einrückung aller Zeilen – mit demselben Algorithmus, den der Compiler auf Text Blocks anwendet.
  • translateEscapes() wertet Escape-Sequenzen wie \n und \t in einem String aus.
  • formatted(Object... args) entspricht String.format(this, args) und macht das Einsetzen von Werten in einen Text Block lesbarer.

Nach einem zweiten Preview in Java 14 haben Text Blocks in Java 15 Produktionsreife erlangt – und mit ihnen die drei String-Methoden. Eine Vorstellung in allen Details findest du im Hauptartikel über Text Blocks.

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 (seit Java 16 liegt das Ziel bei unter einer Millisekunde).

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. Standardmäßig ist dieser Wert auf 300 Sekunden eingestellt.

Das Feature ist standardmäßig aktiviert und kann mit -XX:-ZUncommit deaktiviert werden. Da ZGC den Heap nie unter die Mindestgröße -Xms verkleinert, ist es faktisch auch dann deaktiviert, wenn du -Xms und -Xmx auf denselben Wert setzt.

ZGC hat in Java 15 Produktionsstatus erreicht; im entsprechenden Artikel stelle ich den Garbage Collector genauer vor.

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.

Vor Java 13 war es 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

Mit dem JDK Enhancement Proposal 350 wird dieser Prozess vereinfacht. Das Shared Archive kann ab Java 13 über den Parameter -XX:ArchiveClassesAtExit im Anschluss an die Ausführung der Anwendung generiert werden:

java -XX:ArchiveClassesAtExit=helloworld.jsa \
    -cp target/helloworld.jar eu.happycoders.appcds.Main

Die Option -XX:+UseAppCDS aus dem Java-10-Beispiel fehlt hier nicht ohne Grund: Die JVM kennt sie seit Java 12 nicht mehr, denn AppCDS ist seit Java 11 standardmäßig aktiv. Auch ein -Xshare:on ist unnötig, da -Xshare:auto der Default ist.

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

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.

Seit Java 19 erledigt -XX:+AutoCreateSharedArchive das Erzeugen und Verwenden des Archivs in einem einzigen Aufruf, und seit Java 24 baut der AOT-Cache auf CDS auf und speichert die Klassen zusätzlich in geladener und gelinkter Form.

Soft Max Heap Size

Mit dem neuen Kommandozeilen-Parameter -XX:SoftMaxHeapSize kannst du eine „weiche“ Obergrenze für die Heap-Größe festlegen. Der Garbage Collector versucht dann, den Heap unter dieser Grenze zu halten, und überschreitet sie nur, 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öhtem Platzbedarf aber vorübergehend über die weiche Obergrenze hinaus wachsen.

Das Flag ist als „manageable“ deklariert, du kannst es also zur Laufzeit ändern – z. B. mit jcmd <pid> VM.set_flag SoftMaxHeapSize <bytes> oder über die HotSpot-MXBean. So lässt sich die Obergrenze anheben oder absenken, ohne die Anwendung neu zu starten.

In Java 13 unterstützt nur der (noch experimentelle) ZGC dieses Flag. Seit Java 16 kennt es auch Shenandoah; G1 bis heute (Java 27) nicht.

Für diese Änderung gibt es keinen JEP; sie wird im Bug-Tracker unter JDK-8222145 (das Flag) und JDK-8222182 (die Unterstützung in ZGC) aufgeführt.

Erweiterungen der JDK-Klassenbibliothek

Die Klasse ByteBuffer hat mehrere Methoden bekommen, mit denen Lese-/Schreiboperationen an vorgegebenen Buffer-Positionen möglich sind, anstatt – wie bisher üblich – an der durch den ByteBuffer selbst verwalteten Position. Dazu kommen neue Varianten von FileSystems.newFileSystem() und Factory-Methoden für XML-Parser mit Namespace-Unterstützung.

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 die Position des Puffers; der Bereich muss allerdings innerhalb des Limits liegen, sonst wirft sie eine IndexOutOfBoundsException.

Für diese Änderung gibt es keinen JEP; sie wird im Bug-Tracker unter JDK-5071718 aufgeführt.

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

Analog gibt es jeweils zwei neue get()- und put()-Methoden, mit denen die Daten nicht – wie bisher – an der aktuellen Position des Puffers gelesen/geschrieben werden, sondern an der explizit angegebenen 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.

Für diese Änderung gibt es keinen JEP; sie wird im Bug-Tracker unter JDK-5029431 aufgeführt.

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 anbieterspezifische Konfiguration des Dateisystems mitzugeben: 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.

Eine Falle bei der Migration: Rufst du die alte Zwei-Parameter-Variante mit null als Class Loader auf – FileSystems.newFileSystem(path, null) –, ist der Aufruf ab Java 13 mehrdeutig und kompiliert nicht mehr. Ein Cast schafft Abhilfe: FileSystems.newFileSystem(path, (ClassLoader) null).

Für diese Änderung gibt es keinen JEP; sie wird im Bug-Tracker unter JDK-8218875 aufgeführt.

XML-Parser mit Namespace-Unterstützung

Die Factories DocumentBuilderFactory und SAXParserFactory haben je drei neue Methoden bekommen, die Parser mit aktivierter Namespace-Unterstützung erzeugen: newDefaultNSInstance(), newNSInstance() und newNSInstance(String factoryClassName, ClassLoader classLoader). Vorher musstest du dafür nach dem Erzeugen der Factory setNamespaceAware(true) aufrufen:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newDefaultInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();

Ab Java 13 reicht eine Zeile:

DocumentBuilder db =
    DocumentBuilderFactory.newDefaultNSInstance().newDocumentBuilder();

Für diese Änderung gibt es keinen JEP; sie wird im Bug-Tracker unter JDK-8219692 aufgeführt.

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

Dieser Abschnitt listet Änderungen auf, mit denen eher wenige Java-Entwickler:innen 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 zugrunde liegende Code (eine Mischung aus Java- und C-Code) ist schwer wart- und erweiterbar, besonders im Hinblick auf Project Loom, in dem Virtual Threads (leichtgewichtige, durch die JVM verwaltete Threads) entwickelt wurden – final seit Java 21.

Mit dem JDK Enhancement Proposal 353 wird die alte Implementierung durch eine modernere, besser wart- und erweiterbare Implementierung ersetzt, die sich insbesondere auch ohne weitere Refactorings an Project Loom anpassen lässt.

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 diese Änderung gibt es keinen JEP; sie wird im Bug-Tracker unter JDK-8221431 aufgeführt.

Kleinere Änderungen ohne JEP

  • Die Klasse JapaneseEra kennt die am 1. Mai 2019 begonnene Ära „Reiwa“ – als Konstante JapaneseEra.REIWA oder über JapaneseEra.of(3) (JDK-8205432).
  • Die maximale Heap-Größe von ZGC wurde von 4 TB auf 16 TB erhöht (JDK-8221786).

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 ü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, es gibt einige neue Varianten der FileSystems.newFileSystem()-Methode, und XML-Factories erzeugen auf Wunsch direkt Parser mit Namespace-Unterstützung.

Die aus Java 1.0 stammende Socket API wurde komplett neu geschrieben, um fit für die in Project Loom entwickelten leichtgewichtigen Threads zu sein – die Virtual Threads, die seit Java 21 fester Bestandteil der Java-Plattform sind.

Hat dieser Artikel deine Fragen beantwortet? Dann freue ich mich über eine Bewertung auf meinem ProvenExpert-Profil – sie hilft anderen Entwickler:innen, diese Inhalte zu finden.

👉 Bewertung abgeben

Java entwickelt sich schneller, als man denkt – alle sechs Monate erscheint ein neues Release. Damit du kein Feature verpasst, klicke hier und melde dich für den HappyCoders-Newsletter an.

👉 Newsletter-Anmeldung

Dieses Thema im eigenen Code?

Du hast den Artikel gelesen – im Training arbeitet ihr damit. 2 Tage lang gehen wir die Themen an euren eigenen Projekten durch, statt an konstruierten Beispielen.

Praxisnah, verständlich und direkt auf euren Projektalltag übertragbar. Statt Theorie vermittle ich Prinzipien, die euch helfen, Code langfristig besser, wartbarer und performanter zu schreiben.

Java 17 SchulungAlle Trainings ansehen

Werde ein:e bessere:r Java-Entwickler:in

Mit meinem kostenlosen Newsletter bleibst du vorn. Modernes Java: neue Versionen & Features, Performance und JVM-Insights – 1x im Monat.

Suche