Nachdem das vorherige Release eher klein ausgefallen war, wurde am 17. März 2020 Java 14 mit eindrucksvollen 16 umgesetzten JDK Enhancement Proposals (JEPs) veröffentlicht.
Mit Java 14 hält es auch wieder eine große Änderung Einzug in die Sprache: Switch Expressions.
Ein weiteres nützliches Feature, "Helpful NullPointerExceptions", wird uns in Zukunft viel Arbeit bei der Fehlersuche abnehmen.
Mit Records und "Pattern Matching for instanceof" sind außerdem zwei spannende Previews mit von der Partie.
Wie immer gibt es auch einige Performance-Verbesserungen; und eine ganze Menge Features wurde als "deprecated" markiert oder entfernt.
Switch Expressions (Standard)
Mit Switch Expressions erreicht die zweite Sprachverbesserung aus Project Amber den Produktionsstatus (die erste war "var" in Java 10). Switch Expressions ermöglichen mit der Pfeilnotation eine deutlich kompaktere Schreibweise als bisher:
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
case TUESDAY -> System.out.println(7);
case THURSDAY, SATURDAY -> System.out.println(8);
case WEDNESDAY -> System.out.println(9);
}
Code-Sprache: Java (java)
Als Ausdruck kann switch
darüberhinaus auch einen Wert zurückgeben:
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
};
Code-Sprache: Java (java)
Welche Möglichkeiten Switch Expressions noch bieten, wie man z. B. aus Code-Blöcken mit yield
einen Wert zurückgibt, wann default
-Fälle nötig sind und wann nicht, und wie der Compiler bei Enums eine Vollständigkeitsanalyse durchführen kann, erfährst du im Hauptartikel über Switch Expressions.
(Erstmals wurden Switch Expressions als Preview-Feature in Java 12 vorgestellt. Im zweiten Preview in Java 13 wurde das ursprünglich zur Rückgabe von Werten verwendete Keyword break
durch yield
ersetzt. Aufgrund des positiven Feedbacks wurden Switch Expressions in Java 14 mit dem JDK Enhancement Proposal 361 ohne weitere Änderungen als finales Feature veröffentlicht.)
Helpful NullPointerExceptions
Wir kennen alle das folgende Problem: Unser Code wirft eine NullPointerException
:
Exception in thread "main" java.lang.NullPointerException
at eu.happycoders.BusinessLogic.calculate(BusinessLogic.java:80)
Code-Sprache: Klartext (plaintext)
Und im Code finden wir dann so etwas:
long value = context.getService().getContainer().getMap().getValue();
Code-Sprache: Java (java)
Was ist jetzt null
?
context
?context.getService()
?Service.getContainer()
?Container.getMap()
?Map.getValue()
? (falls diese Methode einLong
-Objekt zurückgibt)
Um den Fehler zu beheben, haben wir folgende Möglichkeiten:
- Wir könnten den Quellcode analysieren.
- Wir könnten die Applikation debuggen (sehr aufwändig, wenn der Fehler schwer reproduzierbar ist oder nur in Produktion auftritt).
- Wir könnten den Code auf mehrere Zeilen aufteilen und erneut ausführen (wir müssten die Applikation neu deployen und abwarten, bis der Fehler erneut auftritt).
Nach einem Upgrade auf Java 14 wirst du dir diese Frage nicht mehr stellen müssen, denn dann sieht die Fehlermeldung z. B. so aus:
Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "Map.getValue()" because the return value of "Container.getMap()" is null
at eu.happycoders.BusinessLogic.calculate(BusinessLogic.java:80)
Code-Sprache: Klartext (plaintext)
Wir können nun ganz genau ablesen, an welcher Stelle die Exception aufgetreten ist: Container.getMap()
lieferte null
zurück, so dass Map.getValue()
nicht aufgerufen werden konnte.
Ähnliches kann beim Zugriff auf Arrays passieren, wie z. B. in folgender Code-Zeile:
this.world[x][y][z] = value;
Code-Sprache: Java (java)
Tritt in dieser Zeile eine NullPointerException
auf, war bisher nicht erkennbar, ob world
, world[x]
oder world[x][y]
null
war. Mit Java 14 geht das klar aus der Fehlermeldung hervor:
Exception in thread "main" java.lang.NullPointerException:
Cannot store to int array because "this.world[x][y]" is null
at eu.happycoders.BusinessLogic.calculate(BusinessLogic.java:107)
Code-Sprache: Klartext (plaintext)
In Java 14 sind Helpful NullPointerExceptions noch standardmäßig deaktiviert und müssen mit -XX:+ShowCodeDetailsInExceptionMessages
aktiviert werden. In Java 15 wird das Feature dann von Haus aus aktiviert sein.
(Helpful NullPointerExceptions sind in JDK Enhancement Proposal 358 spezifiziert.)
Experimentelle, Preview- und Incubator-Features
Die folgenden Features sind noch nicht produktionsreif. Sie werden in einem mehr oder weniger fortgeschrittenen Entwicklungsstadium mit ausgeliefert, um sie ggf. anhand von Feedback aus der Java-Community noch verbessern zu können.
Die ersten drei Features (Records, Pattern Matching for instanceof und Text Blocks) werden (wie Switch Expressions) in Project Amber entwickelt, dessen Ziel es ist, die Java-Syntax moderner und prägnanter zu machen.
Ich werde die Features nicht in allen Einzelheiten vorstellen, sondern jeweils nur kurz anreißen und auf die Java-Version verweisen, in denen die Features Produktionsreife erlangen. Im entsprechenden Artikel dieser Serie werden sie dann ausführlich behandelt.
Records (Preview)
Das erste neue Preview-Feature in Java 14 sind Records, definiert in JDK Enhancement Proposal 359.
Ein Record bietet eine kompakte Syntax für eine Klasse mit nur finalen Feldern. Diese werden im Konstruktor gesetzt und sind über Zugriffsmethoden auslesbar.
Hier ein einfaches Beispiel:
record Point(int x, int y) {}
Code-Sprache: Java (java)
Dieser Einzeiler erzeugt eine Klasse Point
mit
- finalen Instanzfeldern
x
undy
, - einem Konstruktor, der beide Felder setzt
- und Zugriffsmethoden
x()
undy()
zum Lesen der Felder.
Point
kann wie folgt eingesetzt werden:
Point p = new Point(3, 5);
int x = p.x();
int y = p.y();
Code-Sprache: Java (java)
Die Methoden equals()
, hashCode()
und toString()
werden für Records automatisch generiert.
Records können um statische Felder und Methoden erweitert werden, nicht aber um Instanzfelder. Sie können Interfaces implementieren, aber nicht von anderen Klassen erben. Sie sind implizit final, man kann also auch nicht von ihnen erben.
Records werden in Java 16 Produktionsreife erreichen. Eine detaillierte Vorstellung findest du im Hauptartikel über Java-Records.
Pattern Matching for instanceof (Preview)
Das zweite Preview in Java 14 ist "Pattern Matching for instanceof". Dieses Feature, definiert in JDK Enhancement Proposal 305, eliminiert die lästige Notwendigkeit nach einer instanceof
-Prüfung einen Cast auf den Zieltyp durchzuführen.
Am einfachsten lässt sich das an einem Beispiel zeigen.
Der folgende Code bekommt das Object obj
geliefert. Wenn es ein String
ist und länger als fünf Zeichen, soll es in Großbuchstaben umgewandelt und ausgegeben werden. Ist es hingegen ein Integer
, soll es quadriert und ausgegeben werden.
Dazu müssen wir in den Zeilen 4 und 9 jeweils einen Cast durchführen.
Object obj = getObject();
if (obj instanceof String) {
String s = (String) obj;
if (s.length() > 5) {
System.out.println(s.toUpperCase());
}
} else if (obj instanceof Integer) {
Integer i = (Integer) obj;
System.out.println(i * i);
}
Code-Sprache: Java (java)
Viele von uns haben sich so an diese Schreibweise gewöhnt, dass wir sie gar nicht mehr in Frage stellen. Doch es geht besser!
Ab Java 14 können wir die Casts weglassen und den Code stattdessen wie folgt schreiben:
if (obj instanceof String s) {
if (s.length() > 5) {
System.out.println(s.toUpperCase());
}
} else if (obj instanceof Integer i) {
System.out.println(i * i);
}
Code-Sprache: Java (java)
Hinter einem instanceof
-Statement können wir jetzt einen Variablennamen angeben. Wenn obj
vom angegebenen Typ ist, wird es an den neuen Variablennamen gebunden; diese neue Variable ist dann vom angegebenen Zieltyp und im "then-Block" sichtbar.
Wir können sogar noch einen Schritt weitergehen und die if
-Statements der Zeilen 1 und 2 kombinieren:
if (obj instanceof String s && s.length() > 5) {
System.out.println(s.toUpperCase());
} else if (obj instanceof Integer i) {
System.out.println(i * i);
}
Code-Sprache: Java (java)
Somit haben wir neun Zeilen Code auf fünf Zeilen reduziert und gleichzeitig die Lesbarkeit deutlich erhöht.
Genau wie Records wird auch "Pattern Matching for instanceof" in Java 16 production-ready sein.
Text Blocks (Second Preview)
Text Blocks wurden in Java 13 als Preview eingeführt. Sie ermöglichen die Notation mehrzeiliger Strings wie in folgendem Beispiel:
String sql = """
SELECT id, firstName, lastName FROM Employee
WHERE departmentId = "IT"
ORDER BY lastName, firstName""";
Code-Sprache: Java (java)
JDK Enhancement Proposal 368 führt in Java 14 zwei neue Escape-Sequencen ein:
- Backslash am Zeilenende, um einen Zeilenumbruch zu unterdrücken.
- \s für Leerzeichen, um zu verhindern, dass Leerzeichen vom Zeilenende entfernt werden.
Beispiele für diese Escape-Sequenzen findest du im Hauptartikel über Text Blocks.
Text Blocks werden in der nächsten Version, Java 15, den Produktionsstatus erreichen. Im oben verlinkten Artikel beschreibe ich sie in allen Einzelheiten.
ZGC on macOS + Windows (Experimental)
Der ZGC, ein von Oracle entwickelter Garbage Collector mit dem Ziel Pausezeiten von maximal 10 ms zu erreichen, wurde erstmals in Java 11 als experimentelles Feature für Linux vorgestellt.
Mit den JDK Enhancement Proposals JEP 364 und JEP 365 ist der Z Garbage Collector nun auch unter macOS und Windows verfügbar (weiterhin als experimentelles Feature).
Du kannst ZGC mit den JVM-Flags -XX:+UnlockExperimentalVMOptions -XX:+UseZGC
aktivieren.
ZGC wird in Java 15 produktionsreif sein. Im entsprechenden Artikel werde ich ihn ausführlich vorstellen.
Packaging Tool (Incubator)
Basierend auf dem JDK Enhancement Proposal 343 wird das Tool jpackage
entwickelt, mit dem ein plattformspezifischer Installer für eine Java-Anwendung erstellt werden kann, der wiederum die Anwendung und die dafür benötigte JRE installiert.
Plattformspezifisch bedeutet dabei, dass sich der Installer für Benutzer einer bestimmten Plattform vertraut anfühlt. Für Windows ist das z. B. eine msi- oder eine exe-Datei, die per Doppelklick gestartet wird. Für macOS eine pkg- oder dmg-Datei. Und für Linux eine deb- oder rpm-Datei.
Die Funktionalität soll sich an das Tool javapackager
anlehnen, das seit JDK 8 mit ausgeliefert wurde, allerdings in Java 11 mitsamt JavaFX wieder entfernt wurde.
jpackage
wird in Java 16 Produktsionsreife erlangen. Im entsprechenden Artikel dieser Serie werde ich zeigen, wie man das Tool einsetzt.
Foreign-Memory Access API (Incubator)
Mit dem JDK Enhancement Proposal 370 wird eine API eingeführt, die es Java-Programmen erlaubt, effizient und sicher auf Speicher außerhalb des Java-Heaps zuzugreifen.
Die Foreign-Memory Access API ist ein Teil von Projekt Panama, das einen schnelleren und einfacher zu verwendenden Ersatz für das Java Native Interface (JNI) schaffen soll.
Diese Schnittstelle wird bis Java 18 im Incubator-Status bleiben und in Java 19 als "Foreign Function & Memory API" zum ersten Mal als Preview-Version erscheinen.
Performance-Verbesserungen
In Java 14 gibt es einige Performance-Verbesserungen beim Zugriff auf Speicher und in den Garbage Collectoren.
Non-Volatile Mapped Byte Buffers
Mit einem MappedByteBuffer
kannst du eine Datei in eine Speicherregion "mappen", um sie dann über reguläre Speicherzugriffe lesen und beschreiben zu können. Mehr dazu findest du im Abschnitt "Memory-mapped Files" der Artikelserie "Dateien in Java".
Geänderte Daten müssen regelmäßig in das Speichermedium übertragen werden. Für NVMs gibt es hierfür effizientere Vorgehensweisen mit weniger Overhead als bei herkömmlichen Speichermedien.
Ab Java 14 kannst du diese effizienten Mechanismen einsetzen. Dazu musst du beim Erstellen eines MappedByteBuffer
angeben, dass die Datei auf einem NVM-Medium liegt. Dazu gibst du beim Aufruf von FileChannel.map()
einen der neuen Modi ExtendedMapMode.READ_ONLY_SYNC
bzw. ExtendedMapMode.READ_WRITE_SYNC
an, wie in folgendem Beispiel:
try (FileChannel channel =
FileChannel.open(
Path.of("test-file.bin"),
StandardOpenOption.CREATE,
StandardOpenOption.WRITE,
StandardOpenOption.READ)) {
MappedByteBuffer buffer = channel.map(ExtendedMapMode.READ_WRITE_SYNC, 0, 256);
// read from / write to the buffer
}
Code-Sprache: Java (java)
Non-Volatile Mapped Byte Buffers sind nur unter Linux verfügbar, da nur dort die erforderlichen speziellen Betriebssystem-Aufrufe existieren.
(Non-Volatile Byte Buffers sind in JDK Enhancement Proposal 352 spezifiziert.)
NUMA-Aware Memory Allocation for G1
Auf modernen Maschinen mit mehreren CPUs und mehreren Kernen pro CPU spielt die physische Distanz zwischen CPU-Kern und Speichermodul eine immer größere Rolle. Je weiter ein Speichermodul vom CPU-Kern entfernt ist, desto höher die Latenz beim Speicherzugriff.
Durch den JDK Enhancement Proposal 345 kann der G1 Garbage Collector ab Java 14 solche Architekturen vorteilhaft nutzen, um die Gesamtperformance zu erhöhen.
Threads werden naheliegenden NUMA-Knoten zugeordnet. Objekte, die von einem Thread erstellt werden, werden auf immer demselben NUMA-Knoten angelegt. Und solange sie sich in der Young Generation befinden, verbleiben sie auf diesem, indem sie nur in Survivor Regions auf demselben NUMA-Knoten evakuiert werden.
NUMA-Aware Memory Allocation ist nur für Linux verfügbar und muss über die VM-Option +XX:+UseNUMA
explizit aktiviert werden.
Parallel GC Verbesserungen
Im parallelen Garbage Collector wurde das Management für die parallele Abarbeitung von Tasks optimiert, was zu einer deutlichen Leistungsverbesserungen führen kann.
Deprecations und Löschungen
Die folgenden Funktionen wurden in Java 14 als "deprecated" oder "for removal" markiert oder endgültig aus dem JDK entfernt.
Thread Suspend/Resume Are Deprecated for Removal
Neben Thread.stop()
sind auch die folgenden Methoden bereits seit Java 1.2 als "deprecated" markiert:
Thread.suspend()
Thread.resume()
ThreadGroup.suspend()
ThreadGroup.resume()
ThreadGroup.allowThreadSuspension()
Der Grund dafür ist, dass Thread-Pausierung äußerst anfällig ist für Deadlocks:
Wenn Thread.suspend()
innerhalb eines synchronized
-Blocks aufgerufen wird, bleibt der entsprechende Monitor mindestens solange gesperrt, bis Thread.resume()
aufgerufen wird. Geschieht dies allerdings in einem anderen Thread, innerhalb eines synchronized
-Blocks auf demselben Monitor, blockiert dieser zweite Thread beim Versuch den synchronized
-Block zu betreten.
In Java 14 wurden die o. g. Methoden als "for removal" markiert.
Die ebenfalls als "deprecated" markierte – und in meinen Augen viel gefährlichere – Methode Thread.stop()
wurde bisher nicht als "for removal" markiert und wird uns wohl noch eine Weile begleiten.
(Für diese Änderung existiert kein JDK Enhancement Proposal.)
Deprecate the Solaris and SPARC Ports
Das Betriebssystem Solaris und die SPARC-Prozessorarchitektur sind nicht mehr zeitgemäß. Um Entwicklungsresourcen anderweitig einsetzen zu können, hat Oracle im JDK Enhancement Proposal 362 vorgeschlagen, die Portierungen Solaris/SPARC, Solaris/x64 und Linux/SPARC in Java 14 als "deprecated" zu markieren und in einem der nächsten Releases komplett zu entfernen.
Remove the Concurrent Mark Sweep (CMS) Garbage Collector
Der Concurrent Mark Sweep (CMS) Garbage Collector wurde in Java 9 mit JEP 291 als "deprecated" markiert. Die Entwicklungsressourcen sollten zugunsten modernerer Garbage Collectoren wie G1GC und ZGC umverteilt werden.
Als Ersatz für CMS wird der bereits seit Java 6 verfügbare und in Java 9 zum Standard-Garbage-Collector erhobene Allrounder G1 empfohlen.
Mit JEP 363 wird in Java 14 CMS endgültig aus dem JDK entfernt.
Deprecate the ParallelScavenge + SerialOld GC Combination
Es existiert eine ungewöhnliche und selten genutzte Kombination von GC-Algorithmen: die Paarung aus parallelem GC-Algorithmus für die junge Generation ("ParallelScavenge") und seriellem Algorithmus für die alten Generation ("SerialOld").
Diese Kombination kann man aktivieren, indem man den ParallelGC aktiviert und gleichzeitig den ParallelOldGC deaktiviert, was wiederum automatisch den SerialOldGC aktiviert:
-XX:+UseParallelGC -XX:-UseParallelOldGC
So kann man eine Reduzierung des Gesamtspeicherverbrauch von bis zu 3 % des Java-Heaps erzielen.
Dieser Vorteil wiegt jedoch den hohen Wartungsaufwand nicht auf. Daher wurde entschieden, die Entwicklungsresourcen anderweitig einzusetzen und diese GC-Kombination in Java 14 durch JEP 366 als "deprecated" zu markieren.
Als Ersatz wird die Verwendung von parallelem GC sowohl für die junge als auch die alte Generation empfohlen, die wie folgt aktiviert wird:
-XX:+UseParallelGC
Bereits in Java 15 wird die Kombination ParallelScavenge + SerialOld nicht mehr verwendbar sein.
Remove the Pack200 Tools and API
Das in Java 5 eingeführte Kompressionsverfahren für .class- und .jar-Dateien, Pack200 wurde in Java 11 als "deprecated" markiert.
Der Aufwand um solch spezielle Kompressionsverfahren zu maintainen – nur um gegenüber Standardverfahren ein paar zusätzliche Bytes herauszukitzeln – steht in Zeiten von 100-MBit-DSL-Leitungen und 18-TB-Festplatten in keinem Verhältnis mehr zum Nutzen.
Mit dem JDK Enhancement Proposal 367 wurden Pack200 und die zugehörigen Tools jetzt endgültig aus dem JDK entfernt.
Sonstige Änderungen in Java 14
Habe ich in dieser Kategorie bisher immer Änderungen aufgelistet, die man als Java-Entwicklerin und -Entwickler nicht unbedingt kennen musste, sind in Java 14 auch die "Sonstigen Änderungen" durchaus interessant.
JFR Event Streaming
Im Artikel über Java 11 habe ich Java Flight Recorder (JFR) und JDK Mission Control (JMC) vorgestellt. Flight Recorder sammelt während der Ausführung einer Anwendung wertvolle Daten über die JVM und speichert sie in einer Datei. Die gespeicherten Daten können dann mit Mission Control visualisiert werden:
JDK Enhancement Proposal 349 ermöglicht ab Java 14 auch eine kontinuierliche Überwachung einer Java-Anwendung, indem die von Flight Recorder gesammelten Daten aus der laufenden Anwendung heraus ausgelesen werden können (anstatt sie in einer Datei zu speichern und im Nachhinein zu analyiseren).
Wie das funktioniert, zeigt folgender Beispiel-Quellcode:
int[] array = createRandomArray(1_000_000_000);
try (var rs = new RecordingStream()) {
rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
rs.onEvent(
"jdk.CPULoad",
event -> {
float jvmUser = event.getFloat("jvmUser");
float jvmSystem = event.getFloat("jvmSystem");
float machineTotal = event.getFloat("machineTotal");
System.out.printf(
Locale.US,
"JVM User: %5.1f %%, JVM System: %5.1f %%, Machine Total: %5.1f %%%n",
jvmUser * 100,
jvmSystem * 100,
machineTotal * 100);
});
rs.startAsync();
Arrays.parallelSort(array);
}
Code-Sprache: Java (java)
Zunächst wird mit new RecordingStream()
ein Stream von JFR-Events erzeugt.
Mit RecordingStream.enable()
wird ein konkretes Event (im Beispiel "jdk.CPULoad" aktiviert).
Mit RecordingStream.onEvent()
definieren wir, wie auf das Event reagiert werden soll. Das Event selbst besteht aus mehreren Datenfelder, die wir mit getFloat()
oder – je nach Datentyp – anderen Gettern auslesen können.
Mit RecordingStream.startAsync()
starten wir die Aufzeichnung in einem separaten Thread. Im Hauptthread sortieren wir ein Array mit einer Milliarde Elemente, was auf meinem Laptop etwa 15 Sekunden dauert.
Währenddessen sieht man anhand der Flight-Recorder-Daten gut, dass Arrays.parallelSort()
die CPU nahezu vollständig auslastet:
JVM User: 45.1 %, JVM System: 15.0 %, Machine Total: 60.1 %
JVM User: 86.5 %, JVM System: 0.5 %, Machine Total: 95.2 %
JVM User: 91.8 %, JVM System: 0.3 %, Machine Total: 100.0 %
JVM User: 93.0 %, JVM System: 0.2 %, Machine Total: 96.9 %
...
Code-Sprache: Klartext (plaintext)
Accounting Currency Format Support
In manchen Ländern, z. B. in den USA, werden negative Zahlen in der Buchhaltung nicht mit einem Minuszeichen gekennzeichnet, sondern durch runde Klammern.
In Java 14 wird die sogenannte Language-Tag Extension "u-cf-account" hinzugefügt, die es ermöglicht, in einem Locale
-Objekt die Zusatzinformation anzugeben, ob dieses im Kontext der Buchhaltung verwendet wird.
Du kannst diese Extension wie in folgendem Beispiel einsetzen:
// Example *without* language tag extension
Locale locale = Locale.forLanguageTag("en-US");
NumberFormat cf = NumberFormat.getCurrencyInstance(locale);
System.out.println("Normal: " + cf.format(-14.95));
// Example *with* language tag extension
Locale localeAccounting = Locale.forLanguageTag("en-US-u-cf-account");
NumberFormat cfAccounting = NumberFormat.getCurrencyInstance(localeAccounting);
System.out.println("Accounting: " + cfAccounting.format(-14.95));
Code-Sprache: Java (java)
Ab Java 14 gibt das Programm folgendes aus:
Normal: -$14.95
Accounting: ($14.95)
Code-Sprache: Klartext (plaintext)
Wenn du das Programm unter Java 13 oder älter laufen lässt, wird die Tag Extension ignoriert, und das Programm formatiert beide Male gleich:
Normal: -$14.95
Accounting: -$14.95
Code-Sprache: Klartext (plaintext)
Weitere Unicode Language-Tag Extensions findest du im Artikel über Java 10.
Vollständige Liste aller Änderungen in Java 14
Dieser Artikel hat alle Features von Java 14 vorgestellt, die in JDK Enhancement Proposals definiert sind, sowie einige Performance-Verbesserungen und Löschungen, die keinem JEP zugeordnet sind.
Eine vollständige Liste aller Änderungen findest du in den offiziellen Java 14 Release Notes.
Fazit
Java 14 ist ein eindrucksvolles Release. Switch Expressions sind produktionsreif. Und dank "Helpful NullPointerExceptions" sparen wir uns in Zukunft eine Menge Debugging-Arbeit.
Mit Records und "Pattern Matching for instanceof" wurden zwei weitere Features aus Project Amber als Preview ins JDK übernommen. Text Blocks wurden um die Escape-Sequenzen "Backslash am Zeilenende" und "\s" erweitert.
Der (noch experimentelle) Low-Latency Garbage Collector ZGC ist nun auch unter Windows und macOS verfügbar. Und wer seit Java 11 den javapackager
vermisst, kann ab Java 14 mit dessen Nachfolger jpackage
experimentieren.
JFR Event Streaming und mehrere Performance-Verbesserungen runden das Release ab.
Wenn dir der Artikel gefallen hat, hinterlasse mir gerne einen Kommentar oder teile den Artikel über einen der Share-Buttons am Ende.
Und wenn du informiert werden möchtest, wenn der nächste Artikel online geht, dann klicke hier, um dich für den kostenlosen HappyCoders-Newsletter anzumelden.