Java hat kürzlich („kürzlich“ im Kontext dieses Blogs zu verstehen, wo Ereignisse gerne mal ein paar Monate bis Jahre später kommentiert werden) sein 30-jähriges Jubiläum gefeiert. Am 23. Mai 1995 erblickte das erste Release das Licht der Welt. Ich weiß noch wie heute, als ich…nein, das ist gelogen. Ich weiß nicht mal mehr, wann ich mein erstes Java-Programm wirklich mit dem JDK compiliert habe, und was es gemacht hat (ich vermute, ein Fenster per AWT geöffnet) – aber es war das JDK Version 1.0.2. Müsste ich noch auf irgendeiner CD rumliegen haben.
Mein erster Eindruck von Java war sehr gemischt. Ich kannte – im Sinne von „seriös benutzt“, nicht nur „mal gesehen“ – damals verschiedene BASIC-Dialekte, Z80- und ARM-Assembler, Smalltalk, C, C++ (CFront-Variante, Stand 1994 oder so), Turbo Pascal (inklusive der OO-Features von TP 5.5), Modula-2, Ada 83 und Ada 9X, Scheme, Lisp und Prolog. An Java gefielen mir die Interfaces, die Idee mit JavaDoc, das AWT als Chance für „write-once-run-anywhere“ auch für Anwendungen mit grafischer Oberfläche, die umfangreiche (was eben damals als umfangreich durchging…) Systembibliothek, und die prinzipielle Idee der JVM als abstrahierende, portierbare Runtime. Und natürlich der Garbage Collector. Auch die Applets, um dem drögen statischen HTML-Content endlich etwas Interaktivität beizubringen erschien als coole Idee. Unicode-out-of-the-box war sicher auch eine bemerkenswerte Idee. Und die integrierten Mechanismen zur Serialisierung von Objekten klangen auch cool. Und die vier Levels der Sichtbarkeit fanden auch mein Gefallen. Reflection schien Potenzial zu haben, auch wenn ich damals noch nicht ahnte, was man damit alles veranstalten kann. Und die Java-Variante der Exceptions fand ich cooler als die Ada-Variante, weil Java den Programmierer zu „throws“-Klauseln zwang, während bei Ada im Prinzip alles RuntimeExceptions waren und separat, also nicht erzwungen im Code, dokumentiert werden mussten – und bekanntlich laufen Code und Doku gerne mal auseinander.
Aber da war auch die andere Seite. Das Multithreading war löblich, aber gegenüber dem Tasking aus Ada doch sehr rudimentär – auf der anderen Seite aber auch ein – wenn auch unnötig kleiner – Fortschritt gegenüber pthreads. Die Basisdatentypen waren zwar Gott sei Dank genau spezifiziert (was in C bezüglich der Portabilität immer zu Schwierigkeiten führte, wenn man nicht höllisch aufpasste), aber sehr leistungsarm gegenüber beispielsweise Ada. Kein Operator Overloading. Records mussten durch Klassen abgebildet werden und hatten keine Kontrolle über die Repräsentation im Speicher. Arrays begannen immer mit Index 0. Keine Syntax für Methode-in-Methode. Kein Datentyp für Aufzählungen. Die merkwürdige Mischung aus „alles ist ein Objekt, außer Basisdatentypen“. Keine Generics wie in Ada oder C++, so dass man wieder auf Runtime-Typechecking statt Compile-Time-Safety angewiesen war. Der Interpreter der JVM ging eher gemächlich zu Sache. Der GC ebenso. Threads waren keine nativen OS-Threads, sondern wurden von der JVM simuliert („green threads“). Startup von Anwendungen war sehr gemächlich. Und das AWT war wirklich „kleinster gemeinsamer Nenner“ und mit dem Java 1.0-Sprachumfang, der bekanntlich weder innere Klassen noch deren anonyme Ausprägung unterstützte, sehr „harzig“ zu benutzen, weil man kein Event-Listener-Konzept hatte mit Callbacks, sondern im Prinzip alles ableitete und die entscheidenden Methoden überschreiben musste.
„Gemischt“ war auch das Stichwort zur Einschätzung der Lage beim damals tobenden Streit „curly-braces vs. begin-end“. Ich fand begin-end nach Pascal- oder Modula-Art nicht so gut, nach Ada-Art aber super. Und das Fehlen von Mehrfachvererbung hat auch sein Für und Wider, die Erfindung der Interfaces schien aber den Mangel ausreichend abzumildern.
Nach diesem ersten durchwachsenen Eindruck legte ich Java erst mal wieder beiseite. Im Endstadium meines Informatik-Studiums hießen die Programmiersprachen der Wahl DEC Pascal (Ada-ähnlich) in der Studienarbeit, C++ (mit dem IBM XLC als Compiler auf AIX, der leider die neuen coolen C++-Features allesamt vermissen ließ) in der Diplomarbeit, und Ada 95 für mein privates Großprojekt CDBurn https://www.hubersn-software.com/products_legacy.html. Die Idee des „Java-NC“ bzw. des ThinClients generell setzte sich nicht durch, obwohl es mit Erscheinen von Java durchaus größere Aktivitäten von großen Softwarehäusern gab, relevante Softwarepakete nach Java zu überführen. Ich nenne mal Corel Office als prominentes Beispiel. Es war schon beeindruckend zu sehen, dass WordPerfect plötzlich auf dem RISC OS-Desktop lief, und das alles bloß aufgrund der Verfügbarkeit einer Java-Runtime. Aber genau wie der Java-Prozessor von Sun und CPU-Features wie Jazelle von ARM für die beschleunigte Ausführung von Bytecode setzte sich das nicht durch. Die Zeit war noch nicht reif, die CPUs noch nicht schnell genug, die JVM noch zu lahmelig. Zuviele Abstraktionsschichten sind des Performancempfindens‘ Tod. Software als Sanduhr-Anzeige-Programm. Die Älteren fühlten sich bezüglich der fehlenden Schwuppdizität an Emacs erinnert („Eight Megabytes And Constantly Swapping“).
Aber ich hatte weiter ein Auge auf Java und nahm erfreut zur Kenntnis, dass einige wichtige Sprachfeatures in Java 1.1 nachgerüstet wurden – Stichwort innere Klassen und anonyme innere Klassen, die für die neue AWT- und Swing-Event-Listener-Mechanik essentiell waren – und an der Browser-Front Java 1.1 quasi serienmäßig zur Ausführung von Applets eingebaut wurde – HotJava und Internet Explorer waren die Vorreiter, für alle anderen gab es das Java-PlugIn. Dass Netscape dann mit „JavaScript“ etwas Verwirrung stiftete und Microsoft die übliche Embrace-and-extend-Strategie versuchte, waren bemerkenswerte aber letztlich irrelevante Randnotizen der Geschichte. Ende 1997 war auch noch das Release von Java 1.0.2 für RISC OS bemerkenswert (offiziell von Sun lizenziert und von Acorn implementiert), und Chockcino als clean-room-Java-1.1-Implementierung wurde angekündigt – in der damaligen Welt, als viele Homebanking-Lösungen auf Java 1.1-Applets setzten, war es überaus enttäuschend, dass diese Ankündigung niemals wahr wurde. Ein weiterer Sargnagel für RISC OS.
Mit Java 1.1 begann Sun auch endlich, an der Ablösung bzw. Ergänzung des leistungsschwachen AWT zu arbeiten. „Swing“ war in aller Munde, und mit Swing 1.0.2 („com.sun.java.swing“) und erst recht mit Swing 1.1.1 („javax.swing“) konnte man endlich mit einem reichhaltigen, leistungsfähigen Werkzeugkasten plattformübergreifend UIs bauen. Swing wurde damals, wohl um den Zusammenhang zu MFC, den „Microsoft Foundation Classes“, dem damaligen Benchmark für UI-Klassenbibliotheken, „JFC“ genannt – die „Java Foundation Classes“. Genaugenommen umfasste das AWT, Swing und Java2D. Und wir lernten damals, dass „lightweight“ nicht unbedingt in allen Kontexten für „schnell und speicherplatzsparend“ steht.
Ende 1998 dann das Release von Java 1.2, ein echter Meilenstein, und deshalb zurecht „Java 2 Platform“ genannt. Das Collection-Framework war ein riesiger Fortschritt (vorher war entweder „Vector“ oder „Hashtable“ allüberall genutzt), und Swing war nun endlich integrierter Teil des JDK. Allerdings dauerte es ein wenig, bis alles wirklich stabil und gut war – Java 1.2.2 war schließlich die Version der Wahl ab Mitte 1999. RMI wurde nutzbar (wenn auch umständlich mit rmic und stubs und skeletons), JIT und GC wurden performanter, das Plugin etablierte sich als Ausführungsumgebung in allen gängigen Browsern.
Ich will nicht durch alle Java-Meilensteine durchgehen, das dauert Stunden und kann auch von ChatGPT erzeugt werden. Aus der Praxis kann ich noch berichten, dass Java bei meinem ersten Arbeitgeber Anfang 1999 als Mittel der Wahl für die Implementierung eines großen Softwaresystems auserkoren wurde. Eine Entscheidung mit erheblichem Risiko damals. Hauptgründe dafür: die starke Unterstützung von IBM und damit die versprochene Verfügbarkeit von Integrationscode für Großrechner-Technologien wie MQ, CICS und IMS. Und die Cross-Platform-UI-Geschichte, weil die ersten drei interessanten Kunden drei verschiedene Client-Technologien am Start hatten: Windows, OS/2 und Linux (sowohl x86 als auch PowerPC). Im Nachhinein, nachdem die Software nun etwa 25 Jahre in Produktion ist, kann man wohl sagen: es war die richtige Wahl. Die Software lief erfolgreich produktiv auf Servern aller Art, von Windows über Linux und Solaris bis zu AIX. Clientseitig auf Windows, OS/2 und Linux sowohl als FatClient als auch als WebStart-Anwendung und natürlich auch lange als Applet, in einem Fall voll integriert in eine Kunden-Fachanwendung mit speziell gedengeltem Swing-L&F, so dass der „Bruch“ im Browser zwischen den beiden Anwendungen praktisch unsichtbar wurde. Erwähnenswert zum Start der Entwicklung 1999: wichtige Maßgabe am Anfang war die Anforderung eines potenziellen Kunden, dass der Code (zumindest der Client-Code für die UI) auch unter Java 1.1 laufen muss. Ich vermute, dass man deshalb noch in einzelnen Teilen des Sourcecodes einen „Vector“ findet, der Objekte per „addElement“ bekommt.
Ebenfalls erwähnen will ich „IBM VisualAge for Java“ als meine erste IDE. Positiv zu nennen: es war der Grund für den Chef, für die Entwickler-Rechner die damals nahezu als „unendlich“ kategorisierte Menge von 256 MB RAM zu spendieren (AMD K6-II damals als CPU, Windows NT 4 als OS). Und das war auch dringend nötig. Die IDE war schneckenlahm, ein in Smalltalk geschriebenes Monster, und die Idee, den Code nicht im Dateisystem zu haben sondern in einem „Repository“, das im Prinzip eine Datenbank war, und dann nur per Import und Export wieder Dateien draus zu machen – gewöhnungsbedürftig. Genauso wie die Tatsache, dass die Suchfunktion nicht alle Fundstellen tatsächlich gefunden hat. Und der VCE, der „Visual Component Editor“, war mein erster Versuch mit einem GUI-Builder. Er war optimal geeignet, schon damals meine Überzeugung zu befüttern, dass GUI-Code, zumindest wenn das Layout dynamisch per LayoutManager gemacht werden soll, am besten von Hand geschrieben wird. Der eigentliche Grund, VisualAge zu verwenden – das IBM-Versprechen, in der „Enterprise-Edition“ diverse Großrechner-Integrationsbausteine einfacher verwenden zu können – war alsbald als vage Marketing-Idee entlarvt, und die Entwickler entschieden sich für Forte4Java (eine freie Version von NetBeans, damals von Sun entwickelt), JBuilder oder UltraEdit, bis sich dann alle wieder bei Eclipse 2.1 zusammenfinden konnten.
Es gab einige kritische Momente in der Java-Geschichte – die Anwandlungen von Microsoft, einen inkompatiblen Ableger zu schaffen. Die schleppende Weiterentwicklung, auch durch die finanziellen Probleme von Sun. Der beschwerliche Weg in die vollständige Open-Source-JDK-Welt von heute. Die Übernahme durch Oracle und so manche Detailverwirrung bei der Lizenzierung. Undurchschaubare Strategiewechsel bei Oracle. Aber letztlich waren das – allen Unkenrufen, auch von mir, zum Trotz – nur kurzfristige Irritationen.
Interessant in der langen Java-Geschichte finde ich den „ersten Wendepunkt“: von Konstruktion und Features her startete Java ja, obwohl in einer Server-lastigen Firma erfunden, mit starkem Client-Fokus. Fast, als ob die Erfinder dringend Microsoft ans Bein pinkeln wollten. Irgendwann, so ab Java 1.3, drehte sich das dann – auch dank Java EE und Application Servern und der Verfügbarkeit performanter JREs auf allen relevanten Plattformen – nahezu komplett in Richtung Server und Enterprise, während der Client mehr und mehr aufs Abstellgleis geschoben wurde. Und später ironischerweise durch Google mit Android und GWT wiederbelebt wurde. Man könnte sicher interessante historische Abhandlungen drüber schreiben.
Heute würde ich Java als „lebendiger denn je“ bezeichnen. Der Hotspot-JIT erzeugt sehr effizienten Code (wenn er lange genug läuft), die Garbage-Collectors sind effizient auch bei unglaublich großen Heaps und haben inzwischen unglaublich kurze STW-Zeiten, und das Tooling ist generell sehr ausgereift, dazu die Auswahl an zig stabilen und leistungsfähigen Frameworks. Die neue „release cadence“ sorgt für rasche – manchmal für mich fast zu rasche – Weiterentwicklung der Sprache, ohne die so identitätsstiftende Rückwärtskompatibilität aus den Augen zu verlieren. Es ist schon bemerkenswert, dass man heute ohne größere Handstände immer noch Java 8-Code schreiben kann, der auch problemlos noch unter Windows XP lauffähig ist. Sprachen, die Java beerben wollten wie Scala oder Kotlin sind letztlich nur Ergänzungen des JVM-Ökosystems geworden, dank der inzwischen raschen Weiterentwicklung von Java aber aus meiner Sicht maximal als Ideengeber relevant. Und dank neuerer Entwicklungen wie der direkten Ausführbarkeit von Java-Files ohne vorherigen Compile-Schritt und der diversen Vereinfachungen unter dem Schlagwort „simple onramp“ nebst coolem Tooling wie JBang ist Java inzwischen selbst für Aufgaben, die klassischerweise mit dedizierten Skriptsprachen erledigt wurden, eine interessante Alternative. Keine andere Sprache ist ähnlich universell einsetzbar. Manche Sprachen sind in speziellen Bereichen die bessere Wahl, aber keine andere Sprache ist in so vielen Bereichen „good enough“, vom einfachen Skript bis zur Enterprise-Anwendung, vom Microservice bis zur FatClient-Applikation.
Und seit ich regelmäßig Konferenzvorträge von Brian Goetz schaue, habe ich auch großes Vertrauen, dass die Sprache Java weiterhin mit Augenmaß, aber kontinuierlich weiterentwickelt wird.
Wer anderer Leute „trip down the memory lane“ lesen/schauen will: der offizielle Geburtstagslivestream, die JetBrains-Jubiläums-Seite, und interessante Erinnerungsstücke bis zurück in die Gründerzeit von BellSoft-Mitarbeitern.