11 Gründe

Darum bleibt Java relevant

19.01.2024 von Peter Wayner
Java ist seiner ursprünglichen Vision längst entwachsen. Mit Blick auf die Zukunftsfähigkeit der Programmiersprache ist das eine gute Sache.
"Java - bist Du´s?"
Foto: Stefan Csontos | shutterstock.com

Am 23. Mai 1995 erblickte Java offiziell das Licht der Welt - und entwickelte sich in der Folge zu einer essenziellen Grundlage für moderne Software, die nahezu überall zum Einsatz kommt. Trotz ihres Alters kann man jedoch nicht davon sprechen, dass Java zum alten Eisen gehört. Das liegt auch daran, dass Sun Microsystems und (ab 2010) Oracle bemerkenswerte Arbeit geleistet haben, um die Programmiersprache kontinuierlich mit neuen Funktionen auszustatten - ohne dabei die Kernfunktionen zu stark zu verändern.

Das hat allerdings auch dazu geführt, dass sich Java seit seinen Anfangszeiten dramatisch verändert hat - im Wesentlichen zum Besseren. Im Folgenden haben wir elf Gründe zusammengetragen, die dafür sprechen, dass Java auch in Zukunft relevant bleibt.

1. Virtual Threads

Die ursprüngliche Java-Version ermöglichte Entwicklern, ihre eigenen Thread-Objekte zu erstellen und zu steuern, wie der Code in Multithread- und Multicore-Umgebungen ausgeführt wird. Dabei stellten die Developer schnell fest, dass die Thread-Objekte ziemlich groß waren und es viel Zeit kostete, sie zu erstellen und wieder aufzulösen. Einen permanenten Thread-Pool zum Programmstart einzurichten, wurde so zu einem gängigen Workaround.

Mit Java 19 und der Einführung von Virtual Threads war all das passé: Seitdem übernimmt die Java Virtual Machine (JVM) das Gros der Arbeit, wenn es darum geht, die Systemressourcen in Java-Programmen zu verteilen. Dazu geben die Programmierer einfach an, wenn Parallelität verfügbar ist - die JVM führt den Code zur Laufzeit aus, wo immer sie kann. Insbesondere für moderne IT-Architekturen wie Microservices sind virtuelle Threads ein Segen, weil sie einfacher zu entwickeln und zu supporten sind.

2. Structured Concurrency

Doch damit nicht genug: Java verfügt inzwischen auch über ein abstraktes Modell für Parallelität, das Programmierern und JVM das Leben leichter macht, wenn es darum geht, Workloads zeitgleich zu verarbeiten. Das neue Structured-Concurrency-Modell ermöglicht Devs, einen Java Workload in verschiedene Tasks zu unterteilen, die wiederum in sogenannten Scopes gruppiert werden. Die Scopes werden in Fibers zusammengefasst, die im selben Thread zusammenarbeiten.

Das Ziel dabei: Java-Entwicklern eine Blaupause an die Hand zu geben, um parallele Programme zu erstellen. Structured Concurrency erleichtert zudem der JVM, Möglichkeiten für eine gleichzeitige Execution zu identifizieren und diese den Prozessorkernen zuzuordnen.

3. Immutable Data

In der Anfangszeit von Java waren Strings in Stein gemeißelt: Sobald sie erstellt waren, konnten sie nicht mehr verändert werden. Eine Funktion wie toLowerCase aufzurufen, erzeugte einen neuen String. Dieser Prozess erleichterte es der JVM, die Synchronisation über Threads hinweg sicherzustellen und abzusichern.

Heute können Java-Entwickler die gleichen Regeln der Unveränderbarkeit für ihre eigenen Objekte festlegen - mit "Records". Der Code listet die Namen und Typen der Felder auf und die JVM erledigt den Rest. Gängige Methoden wie equals, hashCode und toString werden dabei automatisch erstellt. Unterdessen sorgt die JVM für die Unveränderlichkeit der Records. Das vereinfacht viele Programmdetails und beschleunigt den laufenden Code.

4. Garbage Collection

Speicherzuweisung und -rückgewinnung an die JVM zu delegieren, kommt den meisten Entwicklern gelegen. Dabei wirkte sich der ursprüngliche Garbage Collector (GC) von Java jedoch in manchen Fällen spürbar auf die Performance respektive User Experience aus. Inzwischen stehen Java-Programmierer vier verschiedene Garbage Collectors zur Auswahl, die unterschiedliche Algorithmen nutzen und auf spezifische Applikationen ausgelegt sind:

Das macht es für Entwickler überflüssig, auf andere Lösungen zurückzugreifen - etwa das eigene Memory Management zu simulieren, indem Objekte wiederverwendet werden. Jede der vier GC-Optionen bietet zudem weitere Möglichkeiten zum Feintuning und für Experimente.

5. Pattern Matching mit Switch

Die Java-Verantwortlichen haben die Sprache allerdings auch auf unterster syntaktischer Ebene optimiert, um Entwickler zu einer saubereren, ausdrucksstarken Logik zu führen. Dabei unterstützt das Keyword switch (mit dem Stacks von if-then-else-Konditionalen kreiert werden können) jetzt auch Pattern Matching. Das hat zur Folge, dass die Logik, um verschiedene Cases zu spezifizieren, nicht auf grundlegende Expressions wie equals beschränkt ist.

Java-Code, der mit diesen Mustern geschrieben wurde, ist besonders prägnant und zudem in der Lage, nicht nur nach dem Value innerhalb der Daten, sondern auch nach Objekttyp zu differenzieren. Dazu können alle Referenztypen und der Null Pointer verwendet werden. Selbstverständlich wird die traditionelle Logik mit Fall-Through-Semantik weiterhin unterstützt - alter Code läuft also auch weiterhin reibungslos.

6. Vereinheitlichte Syntax

Obwohl Java im Kern eine tiefe Verwandtschaft mit Lisp aufweist, unterschied sich die Programmierarbeit anfangs nicht wesentlich vom Vorgehen bei C oder C++. Geschweifte Klammern und Semikolons wirkten nahezu identisch, Loops wurden klassisch dreiteilig strukturiert.

Inzwischen sind allerdings Ergänzungen neuerer Skriptsprachen wie Das hat allerdings auch dazu geführt, dass sich Java seit seinen Anfangszeiten dramatisch verändert hat - im Wesentlichen zum Besseren. Im Folgenden haben wir elf Gründe zusammengetragen, die dafür sprechen, dass Java auch in Zukunft relevant bleibt. und Python eingeflossen: For-Loops etwa müssen nicht mehr bis ins kleinste Detail beschrieben werden, weil der Compiler sie intuitiv erkennt, sobald sie eine Liste oder ein Array durchlaufen. Anonyme Funktionen und Lambda-Expressions sind weitere Wege für Developer, Tastenanschläge einzusparen.

7. Sealed Classes

Die JVM war von Anfang an darauf ausgelegt, viele gängige Sicherheitslücken zu schließen, die Entwickler (versehentlich) in ihren Programmen hinterlassen könnten. Diese Fähigkeiten wurden inzwischen um zahlreiche weitere Features ergänzt - zum Beispiel Sealed Classes in Java 17. Damit lässt sich genau definieren, welche Klassen erweitert werden dürfen und somit verhindern, dass grundlegende Funktionen von Dritten überschrieben werden können.

Sealed Classes bieten gegenüber herkömmlichen Klassen zudem Geschwindigkeitsvorteile und ermöglichen eine aggressivere Optimierung und Inlining.

8. Foreign Functions und Memory

Die Java Virtual Machine war ursprünglich als "Walled Garden" oder "Typesafe Sandbox" konzipiert: Sie "bewacht" den Code und verhindert viele Angriffe, die möglich werden, wenn dieser nativ ausgeführt wird. Das ursprüngliche Java Native Interface (JNI) war für viele Entwickler, die auf Bibliotheken und Stacks zugreifen mussten, die in anderen Sprachen geschrieben waren, eine Art Hintertür.

Inzwischen gibt es die Foreign Function & Memory API (derzeit in der dritten Preview). Diese Schnittstelle gestaltet es erheblich einfacher und sicherer, externe Verbindungen herzustellen. Dazu kommen verbesserte Schutzmechanismen wie Type Checking, um potenzielle Overflow-Angriffe zu verhindern. Diese API kann dafür sorgen, dass Java künftig auch vermehrt für Low-Level- und Data-Processing-Aufgaben zum Einsatz kommt. Und sie bietet Programmierern einen sichereren Weg, aus der Sandbox auszubrechen.

9. Vector API

Die ursprüngliche Vector-Klasse war mehr eine Datenstruktur als ein mathematisches Tool - eine flexible und synchronisierte Lösung, um Objekte zu speichern, die sich nicht wesentlich von List unterschied. Ganz anders die neue Vector API: Sie ist ein Werkzeug für mathematische Datenverarbeitung, die im KI-Zeitalter immer häufiger relevant wird. Bei den einzelnen Elementen kann es sich um primitive Typen handeln. Viele grundlegende mathematische Operationen wie Dot Products werden unterstützt.

Der Unterschied zwischen der Vectorwerden dabei automatisch erstellt. Unterdessen sorgt die JVM für die Unveränderlichkeit deradd-Methode: In der Original-Klasse wird einfach ein Objekt ans Ende der Datenstruktur angehängt, wie bei allen anderen Collections-Klassen auch. In der API wird sie verwendet, um die einzelnen Elemente mathematisch zu addieren. Die Vector API verspricht zudem, die enorme Rechenleistung einiger neuerer SIMD-Prozessoren zu erschließen und Java-Programmierer in die Lage zu versetzen, Code zu erstellen, der viele lange Vektoren durchlaufen kann.

10. Besseres Null Processing

ist die Default-Option, die einen optimierten Durchsatz mit kürzeren Pausen bietet. G1 baut auf Techniken auf, die aus früheren Iterationen der Java-Garbage-Collection entstanden sind.

Die Stream API kann beispielsweise lange Datenströme verarbeiten und bleibt nicht hängen, wenn gelegentlich ein Nullwert auftaucht. Devs die trotzdem prüfen wollen, nutzen den Null-Safe-Operator (?.), der das auf sehr prägnante Art und Weise erledigt.

11. Keine Kosten - zumindest für Devs

Java war - zumindest für Softwareentwickler - schon immer kostenlos. Sun Microsystems wusste die Devs zu locken und unternahem 1997 den mutigen Schritt, Teile der Sprache und die JVM als quelloffene Software anzubieten.

Seitdem Sun im Jahr 2010 von Oracle geschluckt wurde, gestaltet sich das Lizenzgebahren etwas undurchsichtiger. Viele Java-Versionen von Oracle sind kostenlos, einige erfordern aber Lizenzen mit bemerkenswerten Bedingungen. Scheinbar versucht das Unternehmen Programmierern weiterhin kostenlosen Zugang zu ermöglichen, während Unternehmen, die vom Einsatz von Java finanziell profitieren, zahlen sollen. In der Praxis bedeutet das, dass Oracle für die sogenannten " werden. Jede der vier GC-Optionen bietet zudem weitere Möglichkeiten zum Feintuning und für Experimente.5. Pattern Matching mit Switch

Dieser Beitrag basiert auf einem Artikel unserer US-Schwesterpublikation Infoworld.