java 10 featuresjava 10 features
HappyCoders Glasses

Java 10 Features
(mit Beispielen)

Sven Woltmann
Sven Woltmann
Aktualisiert: 11. Januar 2024

Mit Java 10 begann am 20. März 2018 der sechsmonatige Release-Zyklus des JDKs. Statt jahrelang auf ein großes Update warten zu müssen, werden wir nun halbjährlich mit neuen Features – und Previews neuer Features – verwöhnt.

Ich habe die Änderungen nach Relevanz für die tägliche Entwicklerarbeit sortiert. Änderungen an der Sprache selbst stehen ganz oben, gefolgt von Erweiterungen an der JDK-Klassenbibliothek.

Danach folgen Performance-Verbesserungen, Deprecations und Löschungen und zuletzt sonstige Änderungen, von denen wir Entwickler wenig mitbekommen (sofern wir nicht am JDK selbst mitarbeiten).

Als Kapitel-Überschriften verwende ich die englischen Bezeichnungen der JDK Enhancement Proposals (JEPs). Diese auf deutsch zu übersetzen würde eher verwirren.

Local-Variable Type Inference ("var")

Seit Java 10 können wir zur Deklaration lokaler Variablen (lokal heißt: innerhalb von Methoden) das Schlüsselwort var verwenden. Das ermöglicht z. B. folgende Definitionen:

var i = 10;
var hello = "Hello world!";
var list = List.of(1, 2, 3, 4, 5);
var httpClient = HttpClient.newBuilder().build();
var status = getStatus();Code-Sprache: GLSL (glsl)

Zum Vergleich – so sehen die Definitionen in klassischer Schreibweise aus:

int i = 10;
String hello = "Hello world!";
List<Integer> list = List.of(1, 2, 3, 4, 5);
HttpClient httpClient = HttpClient.newBuilder().build();
Status status = getStatus();Code-Sprache: Java (java)

Inwieweit man var verwendet, wird vermutlich in vielen Teams zu langen Diskussionen führen. Ich setze es dann ein, wenn es a) deutlich kürzer ist und ich b) den Datentyp klar im Code erkennen kann.

Im Beispiel oben wäre das in Zeile 3 und 4 der Fall (bei List und HttpClient). Die klassiche Schreibweise ist in beiden Fällen deutlich länger. Und die Zuweisungen auf der rechten Seite – also List.of() und HttpClient.newBuilder().build() – lassen mich den Datentyp klar erkennen.

In folgenden Fällen würde ich hingegen auf var verzichten:

  • In Zeile 1 spart man kein einziges Zeichen; hier würde ich bei int bleiben.
  • In Zeile 2 ist var nur minimal kürzer als String – von daher würde ich auch hier eher String verwenden. Ich verstehe aber auch, wenn Teams sich hier anders entscheiden.
  • In Zeile 5 würde ich ganz klar bei der alten Schreibweise bleiben. Ansonsten kann ich nicht auf Anhieb erkennen, was getStatus() zurückliefert. Ist es ein int? Ein String? Ein Enum? Ein komplexes Value Object? Oder gar eine JPA-Entity aus der Datenbank?

Eine ausführlichere Abhandlung darüber, wann man var einsetzen sollte und wann nicht, findest Du in den offiziellen Style Guidelines. Wichtig ist, dass ihr euch im Team auf eine konsistente Verwendung einigt.

(Local-Variable Type Inference ist definiert im JDK Enhancement Proposal 286.)

Immutable Collections

Das Java Collections Framework bietet mit den Methoden Collections.unmodifiableList(), unmodifiableSet(), unmodifiableMap(), unmodifiableCollection() – und vier weiteren Varianten für sortierte und navigierbare Sets und Maps – die Möglichkeit unveränderbare Wrapper für Collection-Klassen zu erzeugen.

Hier ein Beispiel:

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
List<Integer> unmodifiable = Collections.unmodifiableList(list);Code-Sprache: Java (java)

Versuchen wir nun über den Wrapper ein Element hinzuzufügen, erhalten wir eine UnsupportedOperationException:

unmodifiable.add(4);

⟶

Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.Collections$UnmodifiableCollection.add(...)
	at ...Code-Sprache: Java (java)

Der Wrapper hält uns allerdings nicht davon ab, die zugrunde liegende Liste zu modifizeren. Alle nachträglichen Änderungen an dieser sind auch im Wrapper sichtbar. Denn der Wrapper enthält keine Kopie der Liste, sondern eine View:

list.add(4);
System.out.println("unmodifiable = " + unmodifiable);

⟶

unmodifiable = [1, 2, 3, 4]Code-Sprache: Java (java)

List.copyOf(), Set.copyOf() und Map.copyOf()

Mit Java 10 haben wir nun auch die Möglichkeit, unveränderbare Kopien von Collections zu erzeugen. Dafür stehen uns die statischen Interface-Methoden List.copyOf(), Set.copyOf() und Map.copyOf() zur Verfügung.

Erstellen wir so eine Kopie und modifizieren dann die ursprüngliche Collection, haben die Änderungen keine Auswirkungen mehr auf die Kopie:

List<Integer> immutable = List.copyOf(list);
list.add(4);
System.out.println("immutable = " + immutable);

⟶

immutable = [1, 2, 3]Code-Sprache: Java (java)

Der Versuch die Kopie zu ändern, wird – genau wie beim Einsatz von unmodifiableList() – mit einer UnsupportedOperationException quittiert:

immutable.add(4);

⟶

Exception in thread "main" java.lang.UnsupportedOperationException
    at java.base/java.util.ImmutableCollections.uoe(...)
    at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(...)
    at ...Code-Sprache: Java (java)

Hinweis: Solltest du eine änderbare Kopie der Liste benötigen, kannst du das seit jeher mit dem Copy Constructor machen:

List<Integer> copy = new ArrayList<>(list);Code-Sprache: Java (java)

Collectors.toUnmodifiableList(), toUnmodifiableSet() und toUnmodifiableMap()

Die mittels Collectors.toList(), toSet() und toMap() erstellten Collectoren sammeln die Elemente eines Streams in veränderlichen Listen, Sets und Maps. Das folgende Beispiel zeigt den Einsatz dieser Collectoren und die nachträgliche Änderung der Ergebnisse:

List<Integer> list = IntStream.rangeClosed(1, 3).boxed().collect(Collectors.toList());
Set<Integer> set = IntStream.rangeClosed(1, 3).boxed().collect(Collectors.toSet());
Map<Integer, String> map = IntStream.rangeClosed(1, 3).boxed()
        .collect(Collectors.toMap(Function.identity(), String::valueOf));

list.add(4);
set.add(4);
map.put(4, "4");

System.out.println("list = " + list);
System.out.println("set  = " + set);
System.out.println("map  = " + map);Code-Sprache: Java (java)

Wie zu erwarten, liefert das Programm die folgende Ausgabe (wobei die Elemente des Sets und der Map auch in anderer Reihenfolge auftreten dürfen):

list = [1, 2, 3, 4]
set  = [1, 2, 3, 4]
map  = {1=1, 2=2, 3=3, 4=4}Code-Sprache: Klartext (plaintext)

In Java 10 sind die Methoden Collectors.toUnmodifiableList(), toUnmodifiableSet() und toUnmodifiableMap() hinzugekommen, mit denen wir Stream-Elemente nun auch in unveränderlichen Listen, Sets und Maps sammeln können:

List<Integer> list =
    IntStream.rangeClosed(1, 3).boxed().collect(Collectors.toUnmodifiableList());

Set<Integer> set =
    IntStream.rangeClosed(1, 3).boxed().collect(Collectors.toUnmodifiableSet());

Map<Integer, String> map = 
    IntStream.rangeClosed(1, 3)
        .boxed()
        .collect(Collectors.toUnmodifiableMap(Function.identity(), String::valueOf));Code-Sprache: Java (java)

Der Versuch solch eine Liste, Set oder Map zu verändern, wird mit einer UnsupportedOperationException quittiert.

(Für die Erweiterungen an List, Set, Map und Collectors gibt es kein JDK Enhancement Proposal.)

Optional.orElseThrow()

Das in Java 8 eingeführte Optional bietet mit der Methode get() die Möglichkeit, den durch das Optional gewrappten Wert auszulesen. Vor dem Aufruf von get() sollte immer mit isPresent() geprüft werden, ob ein Wert existiert:

Optional<String> result = getResult();
if (result.isPresent()) {
  System.out.println(result.get());
}Code-Sprache: Java (java)

Ist das Optional leer, würde get() sonst eine NoSuchElementException werfen.

Um das Risiko einer unbeabsichtigten Exception zu minimieren, geben IDEs und Statische Code-Analyse-Tools eine Warnung aus, wenn get() ohne isPresent() verwendet wird:

IntelliJ-Warnung bei Optional.get() ohne isPresent()
IntelliJ-Warnung bei Optional.get() ohne isPresent()

Es gibt allerdings auch Fälle, in denen eine solche Exception gewünscht ist. Bisher musste man den Code mit entsprechenden @SuppressWarnings-Annotationen versehen, um die Warnungen zu unterdrücken.

Java 10 bietet mit der Methode orElseThrow() eine schönere Lösung: Die Methode ist eine exakte Kopie der get()-Methode – nur der Name ist anders. Da aus dem Namen klar ersichtlich ist, dass diese Methode eine Exception werfen kann, sind Missverständnisse ausgeschlossen. Die statische Code-Analyse moniert die Verwendung nicht mehr als Code Smell.

Hier zum Vergleich der Quellcode beider Methoden:

public T get() {
  if (value == null) {
    throw new NoSuchElementException("No value present");
  }
  return value;
}

public T orElseThrow() {
  if (value == null) {
    throw new NoSuchElementException("No value present");
  }
  return value;
}Code-Sprache: Java (java)

(Für diese Erweiterung gibt es kein JDK Enhancement Proposal.)

Time-Based Release Versioning

Nachdem von Java 8 auf 9 das Versionsformat (endlich) von dem eher kryptischen 1.8.0_291 auf ein deutlich besser lesbares 9.0.4 umgestellt wurde, wurde in Java 10 mit JEP 322 noch das Release-Datum hinzugefügt – und für Java 11 bereits im Voraus ein "LTS" (Long-Term Support) festgelegt.

Das Kommando java -version liefert in Java 8 bis 11 die folgenden Antworten:

Java 8:

$ java -version
java version "1.8.0_291"Code-Sprache: Klartext (plaintext)

Java 9:

$ java -version
java version "9.0.4"Code-Sprache: Klartext (plaintext)

Java 10:

$ java -version
java version "10.0.2" 2018-07-17Code-Sprache: Klartext (plaintext)

Java 11:

$ java -version
java version "11.0.11" 2021-04-20 LTSCode-Sprache: Klartext (plaintext)

Bis heute gab es keine weitere Änderung am Versionsschema.

Parallel Full GC for G1

Mit dem JDK 9 hat der Garbage-First (G1) Garbage Collector den Parallel Collector als Standard-GC abgelöst.

Während der Parallel GC auch eine vollständige Gargage Collection ("Full GC", also das Aufräumen aller Regionen des Heaps) parallel zur laufenden Applikation durchführen konnte, war dies beim G1 bisher nicht möglich. Der G1 musste dazu vorrübergehend die Application anhalten ("Stop-the-World"), was zu spürbaren Latenzen führen konnte.

Da der G1 mit dem Ziel entwickelt wurde vollständige Collections so gut es geht zu vermeiden, stellte dies nur selten ein Problem dar.

In Java 10 wurde mit dem JDK Enhancement Proposal 307 nun auch die vollständige Gargage Collection des G1 Collectors parallelisiert. Die worst-case Latenzen (Pausezeiten) erreichen die des Parallel Collectors.

Application Class-Data Sharing

Da viele Java-Entwickler nicht damit vertraut sind, möchte ich kurz ausholen und Class-Data Sharing (also ohne den "Application"-Prefix) erklären.

Class-Data Sharing

Wenn eine JVM startet, lädt sie die JDK-Klassenbibliothek aus dem Dateisystem (bis JDK 8 aus der Datei jre/lib/rt.jar; seit JDK 9 aus den jmod-Dateien im jmods-Verzeichnis). Dabei werden die class-Dateien aus den Archiven extrahiert, in eine architekturspezifische binäre Form gebracht und im Arbeitsspeicher des JVM-Prozesses abgelegt:

Laden der JDK-Klassenbibliothek ohne Class-Data Sharing – eine JVM
Laden der JDK-Klassenbibliothek ohne Class-Data Sharing – eine JVM

Werden auf derselben Maschine mehrere JVMs gestartet, wiederholt sich dieser Prozess. Jede JVM hält ihre eigene Kopie der Klassenbibliothek im RAM:

Laden der JDK-Klassenbibliothek ohne Class-Data Sharing – mehrere JVMs
Laden der JDK-Klassenbibliothek ohne Class-Data Sharing – mehrere JVMs

Mit Class-Data Sharing ("CDS") werden zwei Ziele verfolgt:

  1. Die Start-Zeit der JVM soll verkürzt werden.
  2. Der Memory-Footprint (auf deutsch: der Speicherbedarf) der JVM soll reduziert werden.

Class-Data Sharing funktioniert wie folgt:

  1. Mit dem Kommando java -Xshare:dump wird einmalig eine Datei classes.jsa (JSA steht für Java Shared Archive) erzeugt. Diese enthält die komplette Klassenbibliothek im Binärformat der aktuellen Architektur.
  2. Beim Start der JVM wird diese Datei per Memory Mapped I/O durch das Betriebssystem in den Arbeitsspeicher der JVM "gemappt". Das ist erstens schneller als das Laden der jar- bzw. jmod-Dateien. Und zweitens lädt das Betriebssystem die Datei nur ein einziges Mal in den RAM und stellt jedem JVM-Prozess eine Read-Only-Sicht auf denselben Speicherbereich zur Verfügung.

Die folgende Grafik soll dies verdeutlichen:

 Laden der JDK-Klassenbibliothek mit Class-Data Sharing
Laden der JDK-Klassenbibliothek mit Class-Data Sharing

Application Class-Data Sharing – Schritt für Schritt

Application Class-Data Sharing (auch "Application CDS" oder "AppCDS") erweitert CDS um die Möglichkeit neben der JDK-Klassenbibliothek auch die Klassen deiner Applikation in einer JSA-Datei zu speichern und diese unter den JVM-Prozessen zu teilen.

Wie das funktioniert, zeige ich dir an einem einfachen Beispiel (du findest den Quellcode auch in diesem GitHub-Repository):

Im Verzeichnis src/eu/happycoders/appcds liegen folgende zwei Java-Dateien:

Main.java:

package eu.happycoders.appcds;

public class Main {
  public static void main(String[] args) {
    new Greeter().greet();
  }
}Code-Sprache: GLSL (glsl)

Greeter.java:

package eu.happycoders.appcds;

public class Greeter {
  public void greet() {
    System.out.println("Hello world!");
  }
}Code-Sprache: Java (java)

Wir compilieren und paketieren die Klassen wie folgt und starten dann die Main-Klasse:

javac -d target/classes src/eu/happycoders/appcds/*.java
jar cf target/helloworld.jar -C target/classes .

java -cp target/helloworld.jar eu.happycoders.appcds.MainCode-Sprache: Klartext (plaintext)

Wir sollten nun die "Hello World!"-Begrüßung auf der Kommandozeile sehen.

Um Application CDS einzusetzen, müssen wir als nächstes eine Liste der Klassen erstellen, die die Applikation verwendet. Dazu führen wir folgendes Kommando aus (unter Windows musst du die Backslashes weglassen und alles in eine Zeile schreiben):

java -Xshare:off -XX:+UseAppCDS \
    -XX:DumpLoadedClassList=helloworld.lst \
    -cp target/helloworld.jar eu.happycoders.appcds.MainCode-Sprache: Klartext (plaintext)

Achtung: Dieses Kommando funktioniert so nur im OpenJDK. Im Oracle JDK wirst du den Hinweis erhalten, dass Application CDS ein kommerzielles Feature ist, das du zunächst mit -XX:+UnlockCommercialFeatures freischalten musst. Also am besten OpenJDK verwenden!

Im Arbeitsverzeichnis sollte sich jetzt die Datei helloworld.lst mit in etwa folgendem Inhalt befinden:

java/lang/Object
java/lang/String
...
eu/happycoders/appcds/Main
eu/happycoders/appcds/Greeter
...
java/lang/Shutdown
java/lang/Shutdown$LockCode-Sprache: Klartext (plaintext)

Wie du siehst, werden nicht nur die Klassen der Applikation aufgelistet, sondern auch die der JDK-Klassenbibliothek.

Als nächstes erstellen wir aus der Klassenliste die JSA-Datei.

(Hinweis: Während man in den vorherigen Schritten als Classpath auch das Verzeichnis target/classes hätte angeben können, funktioniert der folgende Schritt nur mit der paketierten helloworld.jar-Datei.)

java -Xshare:dump -XX:+UseAppCDS \
    -XX:SharedClassListFile=helloworld.lst \
    -XX:SharedArchiveFile=helloworld.jsa \
    -cp target/helloworld.jarCode-Sprache: Klartext (plaintext)

Du bekommst während der Verarbeitung einige Statistiken angezeigt und findest anschließend die Datei helloworld.jsa im Arbeitsverzeichnis. Sie sollte etwa 9 MB groß sein.

Um die JSA-Datei zu verwenden, startest du die Anwendung nun wie folgt:

java -Xshare:on -XX:+UseAppCDS \
    -XX:SharedArchiveFile=helloworld.jsa \
    -cp target/helloworld.jar eu.happycoders.appcds.MainCode-Sprache: Klartext (plaintext)

Wenn alles funktioniert hat, solltest du erneut ein "Hello world!" sehen.

Folgende Grafik fasst die Funktionsweise von Application Class-Data Sharing zusammen:

Application Class Data Sharing ("AppCDS")
Application Class Data Sharing ("AppCDS")

(Application CDS ist im Java Enhancement Proposal 310 definiert.)

Experimental Java-Based JIT Compiler

Seit Java 9 wird der Graal Compiler (ein in Java geschriebener Java-Compiler) als experimenteller Ahead-of-Time (AOT)-Compiler mit ausgeliefert. Dieser ermöglicht es ein Java-Programm in eine nativ ausführbare Datei (also z. B. eine exe-Datei unter Windows) zu compilieren.

In Java 10 wurde mit dem JEP 317 die Möglichkeit geschaffen, Graal auch als Just-in-Time (JIT)-Compiler einzusetzen – zumindest auf der Linux/x64-Plattform. Dazu verwendet Graal das im JDK 9 eingeführte JVM Compiler Interface (JVMCI).

Aktiviert wird Graal über die folgende Option in der java-Befehlszeile:

-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler

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

In diesem Abschnitt liste ich diejenigen Java 10 Features auf, von denen ich denke, dass nicht jeder Java-Entwickler im Detail über sie Bescheid wissen muss.

Andererseits schadet es nicht, wenigstens einmal von ihnen gehört zu haben. :-)

Heap Allocation on Alternative Memory Devices

Mit Umsetzung des JEP 316 kann der Java-Heap – statt auf herkömmlichem RAM – auch auf einem alternativen Speichergerät, z. B. auf NV-DIMM (nicht-flüchtigem Speicher) angelegt werden.

Der alternative Speicher muss dazu vom Betriebssystem über einen Dateisystem-Pfad zur Verfügung gestellt werden (z. B. /dev/pmem0) und wird über folgende Option in der java-Befehlszeile eingebunden:

-XX:AllocateHeapAt=<path>

Additional Unicode Language-Tag Extensions

Mit JEP 314 werden sogenannte "Language-Tag Extensions" hinzugefügt. Diese ermöglichen es in einem Locale-Objekt folgende Zusatzinformationen zu hinterlegen:

SchlüsselBezeichnung englischBezeichnung deutschBeispiele
cuCurrencyWährungISO 4217 Währungscodes
fwFirst day of weekErster Tag der Wochesun (Sonntag), mon (Montag)
rgRegion overrideÜberschreibung der Regionuszzzz (US-Einheiten)
tzTimezoneZeitzonedeber (Berlin), uslax (Los Angeles)

Die folgenden zwei Extensions existieren bereits seit Java 7:

SchlüsselBezeichnung englischBezeichnung deutschBeispiele
caCalendarKalendargregorian, buddhist, chinese
nuNumbering systemZahlensystemarab, roman

Der folgende Beispiel-Quellcode zeigt die Erstellung einer deutschen Locale ("de-DE") mit US-Dollar als Währung ("cu-usd"), Mittwoch als erstem Tag der Woche ("fw-wed") und der Zeitzone von Los Angeles ("tz-uslax"):

Locale locale = Locale.forLanguageTag("de-DE-u-cu-usd-fw-wed-tz-uslax");

Currency currency = Currency.getInstance(locale);

Calendar calendar = Calendar.getInstance(locale);
DayOfWeek firstDayOfWeek = DayOfWeek.of((calendar.getFirstDayOfWeek() + 5) % 7 + 1);

DateFormat dateFormat = DateFormat.getTimeInstance(LONG, locale);
String time = dateFormat.format(new Date());

System.out.println("currency       = " + currency);
System.out.println("firstDayOfWeek = " + firstDayOfWeek);
System.out.println("time           = " + time);Code-Sprache: Java (java)

Zum Zeitpunkt des Schreibens dieses Artikels (20:45 Uhr in Berlin) gibt das Beispiel-Programm folgendes aus:

currency       = USD
firstDayOfWeek = WEDNESDAY
time           = 11:45:50 PDTCode-Sprache: Klartext (plaintext)

Unter Java 9 werden die zusätzlichen Tags ignoriert, und das Programm gibt (40 Sekunden später) folgendes aus:

currency       = EUR
firstDayOfWeek = MONDAY
time           = 20:46:30 MESZCode-Sprache: Klartext (plaintext)

Da sich vermutlich nur sehr wenige Java-Entwickler mit solchen Details beschäftigen müssen, habe ich diese Erweiterung unter "Sonstiges" einsortiert.

Garbage Collector Interface

Einige Teile der Quellcodes der Garbage Collectoren liegen bis Java 9 in langen if-else-Ketten tief in den Quellcodes des Java-Interpreters und des C1- und C2-Compilers verborgen. Um einen neuen Garbage Collector zu implementieren, mussten Entwickler all diese Stellen kennen und für ihre spezifischen Anforderungen erweitern.

Durch den JDK Enhancement Proposal 304 wird im JDK-Quellcode eine saubere Garbage-Collector-Schnittstelle eingeführt und damit die Algorithmen der Garbage Collectoren vom Interpreter und den Compilern isoliert.

So können Entwickler in Zukunft neue GCs hinzufügen, ohne die Codebasis des Interpreters und Compilers angepassen zu müssen.

Root Certificates

Bis zu Java 9 enthielt das OpenJDK in der Keystore-Datei cacerts keine Root-Zertifikate, so dass SSL/TLS-basierte Features nicht ohne weiteres lauffähig waren.

Mit dem JDK Enhancement Proposal 319 wurden die im Oracle-JDK enthaltenen Root-Zertifikate in das OpenJDK übernommen.

Thread-Local Handshakes

Thread-Local Handshakes sind eine Optimierung, um die VM-Leistung auf x64- und SPARC-basierten Architekturen zu verbessern. Die Optimierung ist per default aktiviert. Details findest du im JDK Enhancement Proposal 312.

Remove the Native-Header Generation Tool

Mit JEP 313 wurde das Tool javah entfernt, mit dem man native Header-Dateien für JNI erzeugen konnte. Die Funktionalität wurde in den Java-Compiler, javac, integriert.

Consolidate the JDK Forest into a Single Repository

Im JDK 9 lag der Quellcode in acht separaten Mercurial-Repositories, was bei der Entwicklung oft zu einem erheblichen Mehraufwand führte. Bei über tausend Änderungen war es erforderlich logisch zusammenhängende Commits über mehrere Repositories zu verteilen.

Mit dem JEP 296 wurde der gesamte JDK-Quellcode in ein Monorepo konsolidiert. Die Verwendung eines Monorepos erlaubt nun atomare Commits, Branches und Pull Requests, was die Arbeit am JDK deutlich erleichtert.

Vollständige Liste aller Änderungen in Java 10

Dieser Artikel hat alle Features von Java 10 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 10 Release Notes.

Fazit

Mit var, Immutable Collections und Optional.orElseThrow() hat Java 10 uns einige hilfreiche neue Werkzeuge bereitgestellt. Der G1 Garbage Collector arbeitet jetzt nahezu komplett parallel. Und mit Application Class-Data Sharing können wir den Start unserer Applikation weiter beschleunigen und den Memory Footprint reduzieren. Wer Lust am experimentieren hat, kann den in Java geschriebenen Graal-Compiler aktivieren.

Wenn dir der Artikel gefallen hat, hinterlasse mir gerne einen Kommentar oder teile den Artikel über einen der Share-Buttons am Ende. Möchtest du informiert werden, wenn der nächste Artikel auf HappyCoders veröffentlicht wird? Dann klicke hier, um dich für den HappyCoders-Newsletter anzumelden.