Was ist neu in Java 17?

Am 14. September 2021 wurde Java 17 veröffentlicht, die neueste Long-Term-Support (LTS)-Version der Java-Sprach- und Laufzeitplattform. Wir wollen uns genauer ansehen, welche Neuerungen Java 17 mit sich bringt und ob sich ein Upgrade für Sie lohnt.

Viele Applikationen setzen noch auf ältere Java-Versionen, darunter die vorherigen LTS-Versionen Java 11 und Java 8.

Warum sollten Unternehmen auf die aktuellste Java-Version umsteigen? Ein Upgrade auf Java 17 ist zwar mit einem gewissen Aufwand verbunden, doch dieser lohnt sich, um das volle Potenzial der neuen Merkmale und Funktionen innerhalb der JVM auszuschöpfen.

Viele Unternehmen nutzen Docker und Docker-Images, um den Umstieg auf Java 17 so einfach und zeitsparend wie möglich zu gestalten. Entwickler können ihre CI/CD-Pipelines (Continuous Integration/Deployment) definieren und alles in Docker-Images ausführen. Dies hat keine Auswirkungen auf Teams, die noch ältere Java-Versionen verwenden, da sie ihre alten Docker-Images weiterhin nutzen können.

JAVA 17-Funktionen

macOS- und AArch64-Unterstützung

Eine der wichtigsten Ergänzungen dieser JVM-Version ist die verbesserte Unterstützung für macOS auf der AArch64-Architektur (JEP 391). Dies bedeutet, dass die neueste Prozessorserie (M1) von Apple, die seit dem letzten Jahr in den Computern des Unternehmens verbaut wird, nun offiziell unterstützt wird.

Für Nutzer dieser Plattformen mag dies nicht unbedingt eine bahnbrechende Neuerung sein, da einige Anbieter bereits JDK-Versionen auf den Markt gebracht hatten, die diese Architektur unterstützten, teilweise sogar mit Rückwärtskompatibilität zu Java 8. Die offizielle Unterstützung ist jedoch unerlässlich, um die zukünftige Wartung und den Support der Plattform zu gewährleisten. Im Vergleich dazu wurde die Unterstützung für Linux/AArch64 mit Java 9 und für Windows/AArch64 mit Java 16 eingeführt.

Versiegelte Klassen

Mit Java 17 wurden Sealed Classes als neues Feature offiziell eingeführt, nachdem es zuvor als Preview-Funktion zur Verfügung stand. Diese „versiegelten Klassen“ erlauben es Entwicklern, die zulässigen Subtypen eines Typs festzulegen. Dadurch können sie verhindern, dass dieser auf unerwünschte Weise erweitert oder implementiert wird.

Versiegelte Klassen ermöglichen es dem Compiler, bereits zur Kompilierzeit Fehler zu melden, wenn ein nicht versiegelter Typ in einen unzulässigen Subtyp konvertiert wird. Java 17 bringt auch eine neue Rendering-Pipeline für AWT/Swing-Apps, die unter macOS laufen und die Apple Metal API anstelle von OpenGL verwenden. Zudem bietet Java 17 eine verbesserte API und erweiterte Funktionen zur Generierung von Zufallszahlen.

Änderungen, Löschungen und Einschränkungen in Java 17

Java 17 bringt neben neuen Funktionen auch einige Änderungen, Löschungen und Einschränkungen mit sich.

Kapselung von JDK Internals

Eine der wesentlichen Änderungen ist der Abschluss der Kapselung von JDK Internals. Dieser Prozess wurde mit Java 9 eingeleitet. Damals wurden zur Laufzeit Warnungen ausgegeben, wenn versucht wurde, mithilfe von Reflektion oder ähnlichem die üblichen Einschränkungen bei der Verwendung interner APIs zu umgehen. Zusätzlich wurden Befehlszeilenargumente eingeführt, um dieses Verhalten zu regulieren.

Ab Java 9 wurden verschiedene APIs entwickelt, um eine einheitliche Methode zur Ausführung der am häufigsten verwendeten Aufgaben zu schaffen. Diese APIs sollten von Nutzern intern verwendet werden. Mit Java 16 wurde die Standardeinstellung geändert, sodass anstatt einer Warnung eine Ausnahme ausgelöst wird, wenn der Zugriff deaktiviert ist. Das Verhalten kann jedoch über Befehlszeilenargumente angepasst werden.

In Java 17 entfällt nun die Möglichkeit, diese Einschränkung über Befehlszeilenargumente zu deaktivieren. Das bedeutet, dass jeglicher nicht autorisierte Zugriff auf diese internen APIs nun gesperrt ist.

Strikte Fließkomma-Semantik

Eine weitere „Entfernung“ ist die Wiedereinführung der immer strengen Fließkomma-Semantik. Mit Java 1.2 wurden Änderungen an der Standardeinstellung für die Fließkomma-Semantik in Java eingeführt. Diese erlaubten es der JVM, eine minimale Einbuße an Genauigkeit in Fließkomma-Berechnungen in Kauf zu nehmen, um die Leistung zu verbessern. Für Klassen und Methoden, bei denen eine strikte Semantik erforderlich war, wurde das Schlüsselwort `strictfp` eingeführt. Seitdem wurden verschiedene Befehlssatztypen in CPUs integriert, die eine strikte Fließkomma-Semantik ohne zusätzliche Kosten ermöglichen. Die Notwendigkeit, eine Standard- oder strikte Semantik zu implementieren, ist somit entfallen.

Java 17 entfernt die bisherige Standardsemantik. Alle Fließkommaoperationen werden nun strikt ausgeführt. Das Schlüsselwort `strictfp` existiert zwar noch, hat aber keine Auswirkungen mehr und führt zur Kompilierzeit zu einer Warnung.

Ahead-of-Time (AOT)-Kompilierung

Mit Java 9 wurde die Ahead-of-Time (AOT)-Kompilierung als experimentelles Feature eingeführt. Diese nutzt den Graal-Compiler, wobei der JIT-Code in Java geschrieben wird. Java 10 ermöglichte die Nutzung des Graal-Compilers als JIT-Compiler in OpenJDK durch die Einbindung der JVMCI-Schnittstelle. Seit seiner Veröffentlichung hat sich der Graal-Compiler enorm weiterentwickelt und seine JVM unter dem Namen GraalVM veröffentlicht.

RMI-Aktivierung

Die RMI-Aktivierung wurde mit JEP407 entfernt, nachdem sie bereits aus Java 8 entfernt, für veraltet erklärt und als Voraussetzung für die Entfernung in Java 15 markiert wurde. Die RMI-Aktivierung ermöglichte es, verteilte Objekte bei Bedarf mit RMI zu aktivieren. Sie wurde jedoch nur wenig genutzt und es gibt inzwischen bessere Alternativen. Der Rest der RMI bleibt von der Entfernung des Aktivierungsteils unberührt.

Applet-API-Entfernung

Die Applet-API wurde mit JEP398 endgültig entfernt. Sie war bereits in Java 9 als entfernt markiert worden. Die Applet-API ermöglichte es, Java AWT/Swing-Steuerelemente in eine Webseite innerhalb eines Browsers einzubinden. Da moderne Browser dies jedoch nicht mehr unterstützen, waren Applets in den letzten zehn Jahren praktisch unzugänglich.

Sicherheitsmanager

Die bedeutendste Neuerung ist die Abschaffung des Sicherheitsmanagers (JEP411). Der Sicherheitsmanager ist seit Java 1.0 im Einsatz. Er wurde entwickelt, um einzuschränken, was Java lokal auf dem Computer tun kann, z. B. den Zugriff auf Netzwerke, Dateien und andere Ressourcen. Zudem sollte er nicht vertrauenswürdigen Code in einer Sandbox ausführen, indem Reflektion und bestimmte APIs blockiert werden.

Das Ende des Sicherheitsmanagers begann in Java 12. Es wurde ein Befehlszeilenargument eingeführt, um die Verwendung des Sicherheitsmanagers zur Laufzeit zu blockieren. Die Änderung in Java 17 bedeutet, dass die JVM eine Laufzeitwarnung ausgibt, wenn versucht wird, einen Sicherheitsmanager festzulegen, entweder über die Befehlszeile oder dynamisch zur Laufzeit.

Inkubator- und Vorschaufunktionen

Viele haben sich gefragt, ob Java 17 Inkubator- und Vorschaufunktionen enthalten würde, da es sich um eine langfristig unterstützte Version handelt. Java 17 bietet tatsächlich zwei Inkubatormodule und eine Vorschaufunktion!

Vektor-API

Die Vektor-API (JEP414) befindet sich derzeit in der zweiten Phase des Inkubators. Diese API ermöglicht es Entwicklern, Vektorberechnungen zu definieren, die der JIT-Compiler dann in die entsprechenden Vektoranweisungen umwandelt, die von der jeweiligen CPU-Architektur unterstützt werden (z. B. durch Nutzung der SSE- oder AVX-Befehlssätze).

Bisher mussten Entwickler entweder skalare Funktionen verwenden oder native Bibliotheken entwickeln, die spezifisch für die jeweilige Plattform waren. Die Implementierung der Vektor-API in Java bietet zudem einen nahtlosen Fallback-Mechanismus, der in früheren Versionen komplizierter war.

Die Standardisierung der Vektor-API ermöglicht es auch Klassen innerhalb des JDK, diese zu verwenden. Die `mismatch()`-Methoden der Java Arrays könnten beispielsweise so geändert werden, dass sie stattdessen in Java ausgeführt werden, wodurch die Notwendigkeit entfällt, mehrere plattformspezifische Implementierungen innerhalb der JVM zu verwalten und zu entwickeln.

Fremdfunktions- und Speicher-API

Eine weitere Inkubatorfunktion ist die Foreign Function & Memory API (JEP412). Diese Funktion ist eine Weiterentwicklung und Zusammenführung zweier anderer Inkubatormodule aus Java 16, nämlich der Foreign Linker API (JEP389) und der Foreign-Memory-API (JEP 393). Beide ermöglichen den Zugriff auf nativen Speicher und Code durch statisch typisierte Programmierung in Java.

Musterabgleich für Switch

Die letzte Sprachvorschau in Java 17 ist der Pattern-Matching-Mechanismus für Switch (JEP406). Diese Sprachfunktion erweitert die Switch-Ausdrücke und -Anweisungen um die Möglichkeit, nach Typ zu unterscheiden, ähnlich wie bei der bereits in Java 16 standardisierten Musterabgleich-Syntax (JEP394).

In der Vergangenheit musste man, um verschiedene Aktionen basierend auf der dynamischen Natur eines Objekts auszuführen, eine if-else-Kette mit Instanzprüfungen erstellen, wie zum Beispiel:

String type(Object o) {
  if (o instanceof List) {
    return "A List of things.";
  }
  else if (o instanceof Map) {
    return "A Map! It has keys and values.";
  }
  else if (o instanceof String) {
    return "This is a string.";
  }
  else {
    return "This is something else.";
  }
}
  

Durch die Kombination des Switch-Ausdrucks mit der neuen Pattern-Matching-Funktion für Switches lässt sich der Prozess auf etwas Ähnliches reduzieren wie:

String type(Object o) {
  return switch (o) {
    case List l -> "A List of things.";
    case Map m -> "A Map! It has keys and values.";
    case String s -> "This is a string.";
    default -> "This is something else.";
  };
}
  

Wie Sie sehen können, wird im Überprüfungsprozess eine Variable deklariert. Wie bei anderen Variablen im Pattern-Matching zeigt der Instanzabgleich an, dass dieses Objekt typgeprüft und umgewandelt wurde und von der Variablen in ihrem aktuellen Bereich verwendet werden kann.

Diese Vorschaufunktion ist ein weiterer Schritt in Richtung umfassendes Pattern-Matching. Der nächste Schritt wird die Integration der Möglichkeit zur Dekonstruktion von Arrays und Datensätzen sein.

Sollten Sie auf Java 17 upgraden?

Ja, ein regelmäßiges Update auf die neueste Version ist empfehlenswert, jedoch nicht unbedingt sofort am ersten Tag. Es kann einige Zeit dauern, bis die von Ihnen verwendeten Software und Bibliotheken aktualisiert wurden, um die Kompatibilität mit Java 17 zu gewährleisten.

Wenn Sie an eine ältere LTS-Version von Java wie Java 8 oder Java 11 gebunden sind, gibt es zahlreiche Neuerungen sowohl in der Sprache selbst als auch innerhalb der JVM, die ein Upgrade auf Java 17 rechtfertigen. Da es sich um eine langfristig unterstützte Version handelt, ist es sehr wahrscheinlich, dass Ihre Produktionsumgebung früher oder später ebenfalls auf Java 17 umgestellt wird.

Wenn Sie ein völlig neues Projekt starten oder Ihr aktuelles Projekt auf Java 17 vorbereiten, ist es wahrscheinlich die beste Wahl, den Umstieg auf Java 17 so bald wie möglich anzugehen, da dies die Kosten für die Migration senkt. Dies ermöglicht es den Entwicklern, die an dem Projekt arbeiten, alle aktuellen Funktionen zu nutzen.

Sie können von den vielen Verbesserungen profitieren, die in den letzten Jahren vorgenommen wurden, wie beispielsweise die verbesserte Unterstützung für Container, die unter Java ausgeführt werden, sowie neue Garbage-Collector-Implementierungen mit geringer Latenz.