Über unsMediaKontaktImpressum
Christian Ullenboom 22. Dezember 2020

Wandlung von Java

Wie sich Java in 25 Jahren weiterentwickelt hat und was das für Entwickler bedeutet

Sun Microsystems stellt Java 1996 vor. In den letzten 25 Jahren hat sich das ganze Ökosystem in vielerlei Hinsicht verändert. Dieser Beitrag dokumentiert die Änderungen von Java und welche Auswirkungen der Wandel auf die Softwareentwicklung hat. Dabei sollte uns immer bewusst sein, dass Java aus drei Säulen besteht: der Programmiersprache, der Standardbibliothek und der Laufzeitumgebung. In allen drei Bereichen gibt es immer wieder Änderungen, mal eher an den Bibliotheken, mal an der Laufzeitumgebung, dann wieder an der Sprache.

Die zentrale Eigenschaft von Java, die bis heute gilt, ist die Plattformunabhängigkeit. Der Java Compiler generiert aus Quellcode Bytecode, der dann von virtuellen Maschinen (JVM) ausgeführt wird. In den Anfangszeiten war die Laufzeitumgebung ein reiner Interpreter und so sind auch einige Design-Entscheidungen zu erklären. So gibt es in Java die Unterscheidung zwischen primitiven Datentypen und Referenztypen, was heute zu unschönen APIs führt (Predicate, IntPredicate, LongPredicate, …). Dieser Fehler ist kaum zu korrigieren. Dabei wäre es auch anders gegangen: Smalltalk z. B. ist eine reine objektorientierte Programmiersprache, in der auch numerische Werte Objekte sind. Eigentlich möchte man nie Kompromisse eingehen bei Problemen, die heute noch nicht lösbar sind, aber in der Zukunft. Daher ist diese Trennung sehr unglücklich, aber Entwickler müssen damit leben.

Java 1.0 hat eine überschaubare Bibliothek; vieles ist noch nicht implementiert. Erst in der Java-Version 1.1, Anfang 1997, kommen die JDBC API, RMI, Reflection und umfangreicher Unicode-Support dazu. Die JDBC-API begleitet Entwickler bis heute. Sie ist ein allgemein akzeptierter Standard für Datenbankzugriffe, doch zwei Probleme machen der JDBC-API zu schaffen: Zum einen die vielen optionalen Fähigkeiten, die sich über die Metadaten eines JDBC-Treibers erfragen lassen, zum anderen die blockierende Eigenschaft. In Arbeit ist R2DBC (Reactive Relational Database Connectivity), was insbesondere von Pivotal vorangetrieben wird, dem Unternehmen hinter dem Spring Framework. Ebenfalls neu im JDK 1.1 ist RMI – Remote Method Invocation. Aus heutiger Sicht spielt das keine besonders große Rolle mehr, doch schon früh gab es neben der Java SE von Sun die Bestrebungen, einen Aufsatz, die Java Enterprise Edition zu entwickeln. In dem Zusammenhang ist RMI wichtig, denn so können Clients entfernte Methodenaufrufe auf einem Server aufrufen. Eine weitere zentrale Neuerung ist Reflection. Java-Programme können dynamisch zur Laufzeit Methoden aufrufen, Objekte erzeugen, Attribute auslesen und schreiben. Insbesondere in Zusammenhang mit den ebenfalls neu eingeführten JavaBeans lassen sich Daten auf Objekte übertragen und umgekehrt. Für objektrelationales Mapping wird das noch einmal sehr wichtig. JavaBean klingt so groß, doch es ist einfach nur eine Konvention, dass Klassen einen parameterlosen Konstruktor sowie Setter und Getter besitzen. Andere Programmiersprachen, wie C#, haben Properties in der Sprache, in Java hat man eine Namenskonvention gewählt. Die Entscheidung auf Properties zu verzichten führt zu viel Boilerplate-Code, der den Code aufbläht. Eine ganze Reihe von Werkzeugen arbeitet um die Setter/Getter herum, so z. B. Lombok oder andere Code-Generatoren. Lombok ist eine Art Compiler-Hack, der über besondere Annotationen den Compiler veranlasst, Setter, Getter, Konstruktoren und weitere Konstrukte in den Bytecode zu schreiben.

In der kommenden Java-Version 16 sind zudem Records eingebunden, die eine neue Möglichkeit bieten, einfach Behälter aufzubauen. Da diese allerdings immutable sind, gilt hier nicht die Setter-Getter-Konvention, aber zumindest eine kompakte Schreibweise für Behälter. Ebenfalls legen JavaBeans den Grundstein für Veränderbarkeit: mutable state. In modernen Architekturen versucht man eher in immutablen Datenstrukturen zu denken, doch die JavaBeans sind klar veränderbare Komponenten.

Java 1.2 erscheint Ende 1998. In der Sprache ist ein neues Schlüsselwort dazu gekommen: strictfp. Java ist eine plattformunabhängige Programmiersprache und im besten Fall sind die Rechenergebnisse von Java-Programmen auf jeder Plattform identisch. Die Frage ist nur, zu welchem Preis. Mathematische Operationen sollen in Hardware realisiert werden und hier könnten sich die Berechnungen prinzipiell je nach CPU unterscheiden. Java hat sich für 32- und 64-Bit-lange Fließkommazahlen entschieden, etwas, was in Hardware ungewöhnlich ist, wo 80 Bit üblich sind. Der Java-Standard schreibt vor, dass Fließkommazahlen nach dem IEEE 754-Standard berechnet werden, doch wenn Prozessoren eben nicht in 64 Bit nach diesem Standard rechnen, gibt es ein Problem: Entweder nimmt man in Kauf, dass die mathematischen Operationen sehr performant von der Hardware ausgeführt werden, oder eben strikt nach den Regeln, dann aber deutlich langsamer, weil die mathematischen Operationen in Software nachgebildet werden müssen. Softwareentwickler können bestimmen was sie wollen – im Alltag kommt strictfp selten vor.

Eine weitere Neuerung in Java 1.2 ist das Swing Framework. Das AWT ist schon Teil von Java 1.0, die GUI-Komponenten hatten allerdings immer "Partner" im jeweiligen Fenstersystem. Swing zeichnet grafische Oberflächen selbst, und ist somit von den eigentlichen GUI-Komponenten unabhängig; ein Button ist ein Rechteck mit Schatten und einem Text in der Mitte. Java-Entwickler konnten anspruchsvolle grafische Oberflächen realisieren und diese plattformunabhängig umsetzen. Zwar ist der Ansatz heute nicht mehr zeitgemäß, grafische Oberflächen im Code zu beschreiben und keine externen Ressourcen-Dateien einzusetzen, etwa XML oder ein anderes Beschreibungsformat, doch hat Swing das deutlich modernere JavaFX überlebt, zumindest was den Verbleib in der Java SE ausmacht. Weiterhin ist auf der Bibliotheksseite die Collection-API eingezogen. Sie ist wichtig, weil damit erstmals Typen wie Listen, Mengen, Assoziativspeicher deklariert werden. Zudem konnte man ein Problem aus Java 1.0 korrigieren, das nun Schnittstellen verschiedener Datentypen beschreiben (List, Set, Map, ...), und dann Klassen verschiedener Implementierungsphilosophien realisieren kann (ArrayList/LinkedList, TreeSet/HashSet, ...). Die Datenstrukturen aus der Collection-API sind weiterhin nicht threadsicher, bieten also eine etwas bessere Performance. Dennoch fehlen dem Framework einige Datenstrukturen und langsam entstehen die ersten Utility-Bibliotheken, die diese Lücken füllen (Apache Commons Collections 1.0 datiert auf Mitte 2001). Einige Zeit stand die Weiterentwicklung der Collection-API still, da Schnittstellen schlecht erweitert werden können, denn alle implementierenden Klassen müssten dann hinzugekommene Methoden realisieren. Erst in Java 8 gibt es etwas Bewegung durch die Default-Implementierung in Schnittstellen.

Java-Programme bekommen einen Performance-Boost und lassen sich für ernsthafte Aufgaben einsetzen.

Java 1.3 erscheint im Jahr 2000. Auf der Sprachseite gibt es keine Neuerungen, auf der Seite der Laufzeitumgebung gibt es eine Änderung, dass die Java Virtual Machine HotSpot standardmäßig ausgeliefert wird. Java-Programme bekommen daher einen Performance-Boost, und Java lässt sich für ernsthafte Aufgaben einsetzen. Eigentlich ist es ein Wunder, dass Unternehmen Java überhaupt eingesetzt haben, denn die Performance ist anfangs miserabel. Die Interpretation von Bytecode ist langsam und die stumpfe Übersetzung von Bytecode in Sequenzen von Maschinencode zur Laufzeit nicht optimal. Die Idee von Hotspot ist einfach: heiße Stellen zu erkennen – Stellen eines Programmes, in denen es brennt, also Taktzyklen verbraten werden. Diese Stellen nimmt sich die Laufzeitumgebung vor und optimiert sie. Während ein klassischer Compiler den Programmcode vor der Ausführung in Maschinencode übersetzt, macht das HotSpot erst zur Laufzeit, kann auch mehrere Anläufe nehmen, und im Laufe der Zeit immer besseren Maschinencode erzeugen. Diese Eigenschaft ist bis heute herausragend und andere Programmiersprachen mit einer virtuellen Maschine schauen neidisch auf die Architekten von Sun Microsystems. Ebenfalls neu in Java 1.3 ist das Java Naming and Directory Interface, kurz JNDI. Hier zeigt sich ein interessanter Trend: Dinge, die im Java-Enterprise-Bereich wichtig werden, werden in die Java SE übernommen; J2EE 1.2 erscheint Ende 1999. Für Java SE-Anwendungen spielt das JNDI allerdings keine große Rolle.

Java 1.4 erscheint Anfang 2002. Eine ganze Reihe spannender Ergänzungen kommen in die Bibliothek: Reguläre Ausdrücke, non-blocking-IO, eine Logging-Bibliothek, XML-Fähigkeiten, Security-Erweiterungen, Java Web Start. Die Logging-Bibliothek ist eher ein Problem als eine Lösung. Während viele Technologien im Java-Umfeld fundamental sind und einen Baukasten für höherwertige Dienste bilden, so auch das NIO, löst die Logging-Bibliothek das Problem nur halbherzig. Die Logging-Bibliothek ist ein gutes Beispiel, was vielen Java-Entwicklern heute Kopfschmerzen bereitet. Sun – und Oracle heute – lösen ein Problem nicht umfänglich (Datum/Zeit, Dateisystemabstraktion, HTTP-Client, ...) und so wird das Problem durch eine Open-Source-Bibliothek angegangen. Verschiedene Entwickler haben verschiedene Ideen, also gibt es unterschiedliche Bibliotheken. Irgendwann erkennt dann der Urheber das Problem, bietet eine Lösung an, doch die bleibt hinter den Anforderungen und Wünschen zurück. Das vervielfacht nur das Problem, denn einige Entwickler möchten bei der offiziellen Lösung bleiben, weil sie vielleicht reicht, andere benötigen umfangreichere Lösungen, und müssen weiter zu Bibliotheken greifen. Das fragmentiert die Entwicklergemeinde und führt zu aufwändigerer Konfiguration. Neben dem Logging ist J2EE (Ende 1999) und das Spring Framework (ehemals Interface 21, Anfang 2002-2003) ein weiteres Beispiel. Spring formte die Java EE 5 (2006), doch viele sind bei Spring geblieben, weil ihnen die Java EE nicht ausreicht. Java-Entwickler lernen, dass sie sich außerhalb des Standards umschauen müssen, der Einsatz von quelloffener Software nimmt zu.

Das spannendste Update der Sprache

Im Jahr 2004 erscheint Java 5 und ist das bisher spannendste Update der Sprache. Das liegt an einer ganzen Reihe an Neuerungen in der Sprache und in den Bibliotheken. Es fängt mit Generics an. Generics erlauben eine Parametrisierung der Typen. Das ermöglicht eine ganz neue Form der Typpräzisierung. Wie lässt sich eine Liste von Tischen von einer Liste von Türen unterscheiden? Es gab nur Prüfungen zur Laufzeit, aber nicht zur Compilezeit, es sei denn, man baut viele Spezialklassen. Generics können nunmehr Prüfungen zur Compilezeit realisieren, was bei statisch typisierte Sprachen immer möglich sein sollte, viele Fehler zur Compilezeit zu erkennen. Die Umsetzung der Generic wird aber von vielen bemängelt, weil gewisse Typinformationen nicht zur Laufzeit vorliegen. Eine Designentscheidung, die wir am Anfang schon diskutiert haben, rächt sich nun: dass es einen Unterschied zwischen primitiven Typen und Referenztypen gibt. Java-Generics können nur mit Referenztypen parametrisiert werden und nicht mit primitiven Datentypen – es ist nicht möglich, eine Liste von primitiven Ganzzahlen oder ein Predicate <byte> aufzubauen. Vielleicht ändert sich dies einmal mit dem Projekt Valhalla. Java 5 führt daher eine weitere Neuerung ein, um dieses Problem einigermaßen abzuschwächen: das Autoboxing. In der Praxis funktioniert das verhältnismäßig gut, allerdings kann es unüberlegt zu Performanceproblemen kommen, wenn insbesondere in Schleifen Wrapper-Objekte wiederholt aufgebaut werden.

Als Java 5 veröffentlicht wurde, standen die Generics im Vordergrund, und ein Feature ging ein wenig unter: die Annotationen. Das Einsatzfeld der Annotationen darf nicht unterschätzt werden, weil das eine völlig andere Art der Programmierung ermöglicht. Während Generics eine zusätzliche Typinformation erlauben, läuten Annotationen das deklarative Programmieren ein. Anstelle von externen Konfigurationsdateien und einer imperativen Programmierung (wie man etwas macht), geht die Programmierung über in ein "was" und überlässt dem Framework das "wie".

Zum Glück bekommen Entwickler davon nichts mit...

Java 5 ist ein gutes Beispiel dafür, dass Sun Spracheigenschaften einführen, aber den Bytecode nur unwesentlich erweitern wollte. Spracheigenschaften, wie Autoboxing, enum, die erweiterte for-Schleife oder varargs, sind nur Tricks des Compilers, einfachen Bytecode zu generieren. Bei neuen Spracheigenschaften stellt sich grundsätzlich die Frage, ob man neue Bytecode-Befehle einführen sollte oder der Compiler Verrenkungen macht, die Spracheigenschaften mit dem vorhandenen Bytecode abzubilden. Beides hat Vor- und Nachteile: Liegt zu viel Logik im Compiler und ein komplexer Bytecode entsteht, muss die Laufzeitumgebung aus diesem Bytecode diese Sprachkonstruktion wiedererkennen, um diesen effizient in Maschinencode abbilden zu können. Zum Glück bekommen Entwickler von dieser Abbildung nichts mit, sondern können sich über die neuen Spracheigenschaften freuen. Iterable ist auch so eine Schnittstelle, die der Compiler kennt: steht rechts vom Doppelpunkt bei der erweiterten for-Schleife etwas vom Typ Iterable, erzeugt der Compiler Bytecode, der einen Iterator erfragt und mit einem Paar hasNext() und next() abläuft. Erstmalig "kennt" der Compiler Schnittstellen, die Entwickler implementieren können und ein Sprachkonstrukt führt zur Ausführung eigener Anweisungen. Es ist interessant zu sehen, wie verschiedene API-Designer diese Möglichkeit nutzen; optionale Werte in Java werden durch java.util.Optional ausgedrückt, die nicht Iterable sind, auch com.google.common.base.Optional ist nicht Iterable, io.vavr.contro.Option schon. Auch mit dem neu hinzugekommenen statischen Import lässt sich Quellcode einsparen, denn statische Methoden oder statische Konstanten können ohne Typname hingeschrieben werden, was den Code kompakter macht; so wirken Java-Programme wie BASIC-Programme, mit eingebauten (statischen) Methoden wie sin(...), random(...) oder Konstanten wie PI. Aufzählungen sind besonders interessant, weil sie – anders als in anderen Programmiersprachen – nicht nur Konstanten enthalten müssen, sondern ebenfalls Methoden enthalten können, was zu eleganten Lösungen führt. Ein gutes Beispiel wäre TimeUnit.SECONDS.sleep(...). Überhaupt ist der Einzug von java.util.concurrent ein Hinweis darauf, dass nebenläufige Programmierung immer wichtiger wird. Einige Designentscheidungen sind dann im Nachhinein nicht so optimal, etwa dass die Hälfte der Methoden der absoluten Oberklasse java.lang.Object mit Objektbenachrichtigung zu tun hat, was mit dem Einzug der Typen in java.util.concurrent nicht mehr gebraucht wird.

Java 6 erscheint Ende 2006. Viel Interessantes ist in der Sprache nicht dabei. Eine JavaScript-Engine (Mozilla Rhino) wird integriert, später ausgewechselt (Nashorn in Java 8), wieder entfernt (Java 15), dann soll sie doch wieder eingeführt werden. Zudem wird JAX-WS eingeführt, auch das wird später wieder entfernt, das Gleiche gilt für JAXB, was ebenfalls wieder verschwindet. Die Motivation für den Einzug dieser Bibliotheken ist klar: Sun sieht ein Aufkeimen von SOAP-basierten Web-Services und will das in die Standard-Bibliothek integrieren. Es zeigt sich allerdings erneut, dass solche High-Level-Bibliotheken eher schwierig sind und wohl besser durch externe Bibliotheken abgedeckt werden. Externe Bibliotheken, die gerne auch quelloffen sein können, haben den Vorteil, dass sie auf der einen Seite unabhängig vom Versionsrhythmus der Standard-Edition weiterentwickelt werden können, und weiterhin können Entwickler unterschiedliche Geschmacksrichtungen auswählen. 2010 übernimmt Oracle dann Sun Microsystems.

Java 7 führt Mitte 2011 eine Reihe kleiner Sprachänderungen ein, etwa dass in einer switch-Anweisung Strings verwendet werden können oder auch den Diamond-Operator, um Generics abzukürzen, sowie die Möglichkeit binäre Ganzzahlliterale zu schreiben oder auch mehrere Exceptions in einem multi-catch abzufangen. Viel interessanter ist das try-mit-Ressourcen. Eine neue Schnittstelle AutoCloseable wurde eingeführt, sodass Ressourcen, die im try-Block benutzt werden, automatisch am Ende des Blocks wieder geschlossen werden. AutoCloseable ist heute eine sehr wichtige Schnittstelle und eigene Ressourcen-Typen sollten diese Schnittstelle implementieren, damit der Einsatz in einem try-mit-Ressourcen möglich ist. Oracle hat es bis heute versäumt, einige zentrale Typen AutoCloseable zu machen, was immer wieder zu Frust führt – als Beispiele seien javax.persistence.EntityManager und javax.xml.stream.XMLStreamWriter aufgeführt. Ein try-mit-Ressourcen ist ein weiteres Beispiel für den Compiler, der komplexen Bytecode generiert, um diese Sprachkonstruktion umzusetzen. Auch beim switch mit String wird gebastelt; Bytecode kann bei switch nur Ganzzahlen verarbeiten. Um von einem String auf eine Ganzzahl zu kommen, berechnet der Compiler den Hashcode des Strings und nutzt diesen als Ganzzahl. Da es Kollisionen beim Hashcode geben kann, führt der Compiler zusätzlich einen equals(...)-Test ein. Auf der Bibliotheksseite ist eine neue Dateisystem-API zu nennen. java.io.File-Objekte beziehen sich immer auf das lokale Dateisystem und es ist schwierig, Dateioperationen auf virtuellen Dateisystemen zu realisieren. Diese Möglichkeit besteht seit Java 7, Dateisysteme für ZIP-Archive, für die Dropbox, für einen Amazon S3-Speicher, etc. anzubieten.

Der Code ändert sich

Auch mit Java 7 sieht der Code fast immer noch so aus wie unter Java 6 oder Java 5. Das ändert sich erst mit dem Eintreffen von Java 8 im März 2014. Mit den einziehenden Lambda-Ausdrücken und Methoden/Konstruktor-Referenzen sieht der Code an vielen Stellen plötzlich völlig anders aus. Konzeptuell bieten Lambda-Ausdrücke dabei gar nicht mal so viel Neues. Lediglich die kompakte Schreibweise macht sie attraktiv, sodass man an vielen Stellen auf innere anonyme Klassen verzichten kann und Lösungen einsetzt, die vorher aufgrund des Code-Volumens nicht praktikabel waren. So rückt das funktionale Programmieren immer mehr in den Vordergrund. Funktional zu programmieren ist natürlich von der Programmiersprache abhängig und Java ist keine funktionale Programmiersprache, doch funktionales Programmieren ist eine Modellierung, die auf Zustandsänderungen und Anweisungsfolgen verzichtet und verschachtelte Ausdrücke in den Mittelpunkt stellt. Und das lässt sich sehr wohl in Java ausdrücken. Und auch, wenn sich Methoden nicht als Argument an andere Methoden übergeben lassen, und Methoden keine Methoden zurückgeben können, lassen sich doch mit Lambda-Ausdrücken vergleichbare Lösungen realisieren. Der Trick ist, die Methode in ein kleines Objekt einzupacken, und stattdessen dieses Objekt zu transferieren. Durch den Lambda-Ausdruck wirkt es so, als ob direkt der Code der Methode weitergegeben oder zurückgegeben wird – die Objekte drumherum werden sozusagen ausgeblendet. In Java 8 zieht zudem die Streaming-API ein, die mit den Lambda-Ausdrücken sehr elegante Lösungen ermöglicht. Eine Art Vorgänger der Streaming-API lässt sich bei Guava ablesen, dem com.google.common.collect.FluentIterable. Diese Klasse existiert seit Guava 12, zwei Jahre vor Java 8. Zusätzliche Bibliotheken, z. B. die Date-Time-API, gehen bei der Fülle von Änderungen schon fast unter. Dabei zeigt das auch ein Problem auf, mit dem Java-Entwickler umgehen müssen: die große Anzahl an Alternativen und Altlasten. java.util.Date gibt es seit Java 1.0, java.util.Calendar kommt in Java 1.1 hinzu. Es gibt Fehler und die beliebte Datum-Zeit-API Joda-Time erscheint 2005 (also kurz nach Java 5) in der Version 1.0. Daraus entsteht java.time in Java 8. In älteren Projekten kann man auf alle vier Varianten stoßen. Zusammenfassend lässt sich sagen, dass Java 5 und Java 8 fundamentale Änderungen mitbringen, die die Art und Weise, wie Java programmiert wird, grundlegend verändern. Dabei gibt es eine interessante Gemeinsamkeit von Java 5 und Java 8: das deklarative Programmieren. Wir drücken aus, was wir wollen, aber nicht wie es gemacht werden soll. Die Implementierung ist ein Detail des Frameworks, das im Laufe der Weiterentwicklung immer besser werden kann. Bei der Stream-API ist das ganz gut abzulesen: Wie die einzelnen Elemente von einem Strom zum nächsten wandern – wie bei der parallelen Abarbeitung der Ströme genau die Aufteilung aussieht – lässt sich später noch optimieren, aber der Ausdruck, so wie er steht, enthält keinen imperativen Anteil einer Befehlsfolge, sondern konfiguriert lediglich die Abfolge. Das Framework führt diese Abfolge aus, genauso wie eine Datenbank ein SQL-Statement ausführt und im Laufe der Jahrzehnte immer besser geworden ist. Genauso muss man auch die Streaming-API sehen.

Java 9 erscheint im September 2017 stark verzögert aufgrund des problematischen Modulsystems. Schon viele Jahre vorher wurden verschiedene Varianten eines Modulsystems durchgespielt, bis eine Entscheidung gefällt wurde. Manche sehen im Modulsystem eine interne Lösung von Oracle, die große Java-Bibliothek in Module zu zerlegen, andere sehen im Modulsystem die Möglichkeit in eigenen Projekten Abhängigkeit zu reduzieren, klare Grenzen zu dokumentieren, und kleinere Deployment-Einheiten zu bilden. Nach 13 Jahren lässt sich allerdings nicht ablesen, dass das Modulsystem auf großes Interesse stößt. Die Mehrzahl der großen Open-Source-Bibliotheken ist weiterhin nicht modularisiert und auch aktuelle Laufzeitumgebungen nutzt man in der Regel nicht mit Bibliotheken im Modulpfad, sondern weiterhin mit Java-Archiven im Klassenpfad. In den ersten Jahren gilt Java 9 als wenig beliebtes Release, da es viele Kompatibilitätsprobleme mit sich bringt. Es dauert einige Zeit, bis Bibliotheksentwickler sich von internen Sun-Klassen verabschieden und andere Lösungen finden.

Java 10 erscheint im März 2018 und die einzige nennenswerte Neuerung ist die local variable type inference, mit der bei lokalen Variablen der präzise Typ in vielen Stellen fallen gelassen werden kann und stattdessen var eingesetzt werden kann.

Die Zeit der im Browser eingebetteten Programme ist vorbei

Java 11 erscheint im September 2018 und ist eine LTS-Version. LTS steht für Long Term Support und Oracle bietet beim Oracle JDK kommerziellen Support. Erstmalig werden einer Java-Umgebung Bibliotheken entzogen, unter anderem verschwindet JavaFX, Java EE-Teile, CORBA, Java-Webstart, Java-Applets. Vorher war die Abwärtskompatibilität heilig, das heißt ein Programm, das unter Java 1.0 entwickelt wurde, sollte auch problemlos auf der allerneuesten Laufzeitumgebung funktionieren. Mit dem Entfernen einiger Module gilt das nun nicht mehr. Oftmals springen Bibliotheken ein, im Fall von Applets gibt es allerdings keine Weiterentwicklung. Java ist mit Applets groß geworden und mit Java 11 endet eine Ära. Die Zeit der im Browser eingebetteten Programme ist damit vorbei: Microsoft hat Silverlight eingestellt, Adobe ist mit Flash am Ende und Oracle hat Java-Applets aufgegeben. Anwendungen im Browser werden heute über JavaScript realisiert, WebAssembly ist im Kommen. Obwohl Java 11 ein gutes Release ist und ein Long Term Support hat, setzen über 60 Prozent aller Unternehmen immer noch auf Java 8. Daran kann man ablesen, dass der Industrie die Fähigkeiten von Java 8 reichen und möglicherweise das Modulsystem als ein Risiko erscheint. Jakarta EE 9 und das Spring Framework funktionieren wunderbar mit Java 8.

Java 12 ist ein eher unbedeutendes Release, das im März 2019 erscheint. Orakel experimentiert erstmalig mit Previews, das heißt Spracherweiterungen, die mit einem extra Schalter aktiviert werden müssen. Entwickler können auf diese Weise mit neuen Sprachfeatures experimentieren. Für Produktivsysteme ist das uninteressant, denn bei fast allen vorgestellten Preview-Features gibt es Änderungen, das heißt Produktivsysteme mit diesen Preview-Eigenschaften zu bauen, ist hochgradig riskant und zieht eine ganze Kette von Änderungen nach sich. Preview-Features haben also für Entwickler eine eher untergeordnete Relevanz. Das Eclipse einen eigenen Compiler implementiert, werden sich die Compilerbauer freuen, wenn ein Feature wieder geändert wird ...

In Java 13, welches im September 2019 veröffentlicht wird, geht es mit Preview-Features weiter. Es gibt Textblöcke und die switch-Ausdrücke. In Java 14, aus dem März 2020, sind die Textblöcke weiterhin ein Preview-Feature. Switch-Ausdrücke ziehen fest in die Sprache ein. Als neues Preview-Feature tauchen Pattern-Matching und Records auf. In Java 15 (September 2020) werden Textblöcke final, Records wiederum bleiben ein Preview-Feature.

Java 16 wird für März 2021 erwartet, Records und Pattern-Matching für instanceof werden dann feste Bestandteile der Sprache sein. Mit Records wird es neben den Klassen, Schnittstellen, Annotationstypen und enums einen neuen Typ geben. Ein Trend lässt sich bei den Records bereits ablesen: es geht in Richtung Immutability und Value-Types.

Autor
Das könnte Sie auch interessieren

Neuen Kommentar schreiben

Kommentare (0)