
Java ist eine plattformunabhängige, kompilierbare, statisch typisierte und objektorientierte Programmiersprache. Mit der Einführung von Annotationen in Java 5 unterstützt Java auch aspektorientierte Programmierung. Seit Java 8 unterstützt Java mit Lambda-Funktionen auch funktionale Programmierung.
Die Sprache Java wurde von Sun Microsystems entwickelt und 1995 erstmalig der Öffentlichkeit zugänglich gemacht. Als Urvater von Java gilt der Kanadier James Gosling. Mit der Übernahme von Sun Microsystems durch Oracle gehört Java seit 2010 zu selbigem. Die Programmiersprache Java ist abzugrenzen von der Technologie Java. Neben der Programmiersprache Java gibt es zahlreiche weitere Sprachen, die auf der Java Virtual Machine (JVM) laufen, wie etwa Scala und Kotlin. Auch für andere Sprachen, die ursprünglich nichts mit der Java-Technologie zu tun hatten, gibt es Bytecode-Compiler, die diese auf der JVM lauffähig machen. Dazu zählen etwa Python (JPython) und Ruby (JRuby).
Der Name Java geht auf das Café Java City und die dort angebotene und bei den Entwicklern der Sprache sehr beliebte Kaffeesorte Java zurück.
Alleinstellungsmerkmale
Java ist bekannt dafür, sehr verbos zu sein. Dies macht sich zum Beispiel dadurch bemerkbar, dass man für die meisten Felder einer Klasse Getter und Setter schreibt, die im Regelfall immer gleich aufgebaut sind, trotzdem aber jeweils mehrere Zeilen Programmcode bedeuten. Das Gleiche gilt für die Standardmethoden toString()
, equals()
und hashcode
() – erstere ermöglicht eine String-Repräsentation eines Objektes, die letzteren beiden erlauben den Vergleich eines Objektes mit anderen.
Darüber hinaus ist Java eine sehr konservative und auf Abwärtskompatibilität bedachte Sprache. Sehr alter Programmcode kann überlicherweise ohne oder mit nur sehr wenigen Anpassungen problemlos mit einer aktuellen Version der Sprache kompilieren. Das bedeutet allerdings auch, dass Java viele Altlasten mit sich rumschleppt. Darunter sind veraltete Methoden wie Thread.stop()
und Object.finalize()
, die seit vielen Java-Versionen als @Deprecated
annotiert sind und von deren Verwendung abgeraten wird, aber auch Klassen aus den Anfangszeiten von Java wie Hashtable
und Vector,
die aufgrund besserer Alternativen heutzutage faktisch nicht mehr verwendet werden und nur noch in den wenigsten Legacy-Anwendungen anzutreffen sind. Andere Klassen erfreuen sich weiterhin Beliebtheit, wie die Klasse Date
, in der bereits seit Java 1.1 ganze 22 Konstruktoren und Methoden als @Deprecated
markiert sind und für die es spätestens seit Einführung der Time-API mit Java 8 bessere Alternativen gibt.
Einsatzbereiche
Java ist eine vielseitig einsetzbare Sprache, die vor allem im Enterprise-Umfeld weit verbreitet ist. Java EE (Java Platform, Enterprise Edition), mittlerweile Eclipse Jakarta EE Platform und die Definition diverser Schnittstellen wie Java Message Service (JMS), Java Persistence API (JPA) und Java Transaction API (JTA) haben zu dieser Verbreitung beigetragen. Während Java EE vor einigen Jahren noch üblicherweise in einen (Web) Application Server wie Apache Tomcat, Glassfish oder WildFly (JBoss) deployed wurde, hat das Framework Spring Boot es zum Standard gemacht, dass der (Web) Application Server mit der Java-Anwendung zusammen in einer Datei ausgeliefert wird und die Anwendung damit als Standalone ausführbar ist. Im Enterprise-Umfeld hat sich das Prinzip des Inversion of Control (IoC) durchgesetzt, das eine Menge Boilerplate-Code für den Entwickler unsichtbar hinter Annotationen versteckt und damit z.B. die Realisierung eines HTTP-REST-Servers in sehr wenigen Zeilen Code ermöglicht.
Vor allem in der Vergangenheit war es auch vorteilhaft, dass Java mit Swing eine eigene graphische Benutzeroberfläche im Java Development Kit (JDK) mit ausliefert. Damit war die Programmierung von FatClient-Anwendungen möglich, ohne dass weitere Bibliotheken mit eingebunden werden mussten. JavaFx hat das Swing-Framework 2014 inoffiziell abgelöst, hat heute aber aufgrund des schwindenden Anteils von FatClients gegenüber WebClients immer weniger Relevanz. Im Bereich Webfrontends ist Java mit Template Engines wie Thymeleaf und Apache Freemarker vertreten, in der Vergangenheit waren auch Technologien wie Java Server Pages (JSP) und später Java Server Faces (JSF) weit verbreitet. Ein bekanntes Framework für Rich Internet Applications (RIA) mit Java ist Vaadin.
Für den Einsatz auf schwacher Hardware wurde vor vielen Jahren Java ME (Java Platform, Micro Edition) geschaffen, spielt aufgrund der wachsenden Performance mobiler Endgeräte heute aber keine Rolle mehr. Java ist auch die dominierende Sprache zur Entwicklung von Apps für das Betriebssystem Android.
Ein Auszug an Frameworks, die in Java geschrieben sind: ActiveMQ, Cassandra, Elasticsearch, Hadoop, Kafka, Solr, Tomcat, ZooKeeper
Pros
Java zeigt seine Stärken in der Entwicklung großer Anwendungen. Die zur Verfügung stehenden Sichtbarkeitsstufen (public, protected, default und private), die Möglichkeit Konstruktoren und Methoden zu überladen, Interfaces und abstrakte Klassen sowie Java Doc erlauben es, Klassen genau auf die eigenen Absichten abzustimmen. Entwickler haben damit in der Hand, wie ihre Klassen verwendet werden, ob eine Klasse als Singleton implementiert wird oder über eine Factory erzeugt wird, welche Werte die Felder einer Klasse einnehmen dürfen und über welche Mechanismen sie geändert werden können. Damit ist Java ideal zur Modellierung komplexer Geschäftsdomänen.
Durch seine weite Verbreitung gibt es eine Vielzahl an Standards und Schnittstellen. Mehr oder weniger jede Software, die Bindings für mehrere Programmiersprachen bereitstellt, hat auch eines für die Sprache Java parat. Die Bekanntheit der Sprache führt auch dazu, dass es zu diesen Bindings in der Regel viel Wissen online gibt, sei es in Blogbeiträgen, Tutorials, oder auf Seiten wie stackoverflow. Allein das Spring-Ökosystem ermöglicht die Anbindung diverser SQL- und noSQL-Datenbanken, Message Broker, Search Engines und Storage Frameworks. Build-Werkzeuge wie Maven und Gradle mit integriertem Paketmanager sorgen dafür, dass neue Bibliotheken mit minimalem Aufwand eingebunden und sofort verwendet werden können. Dank Javas Popularität kann man auch sicher sein, selbst in 10-20 Jahren als Java-Softwareentwickler noch gut aufgestellt zu sein.
Java ist eine Sprache, die sich leicht dekompilieren lässt und damit sehr transparent ist. Das bringt enorme Vorteile bei der Entwicklung mit sich, wenn man eine moderne Entwicklungsumgebung wie Eclipse oder IntelliJ IDEA verwendet. Es ist kein Problem, an einer beliebigen Stelle im Programm einen Breakpoint zu setzen und alle zu diesem Zeitpunkt erreichbaren Objekte bis ins kleinste Detail aufzuschlüsseln oder aus diesem Kontext heraus beliebige Ausdrücke auszuwerten. Das funktioniert auch in Bibliotheken Dritter wie z.B. Bibliotheken des Spring-Frameworks. Moderne IDEs erlauben es auch, bedingte, an Konditionen geknüpfte Breakpoints zu definieren, Werte in Objekten zur Laufzeit zu ändern oder das letzte Frame im Stack zu droppen (eine Methode „zurück“ zu springen).
Die Testunterstützung in Java ist ebenfalls hervorragend durch Frameworks wie JUnit zur Testausführung, Mockito zur Emulation von Objekten oder Methoden, die sich ansonsten ggf. nicht ohne Weiteres im Testkontext rekonstruieren lassen und AssertJ zum Schreiben gut verständlicher Assertions. Bibliotheken wie Testcontainers erlauben es sogar, im Test andere beliebige Software in einem Docker-Container hochzufahren und in den Test zu integrieren, etwa eine Redis-Instanz, um das Caching der Anwendung in einem Integrationstest zu testen.
Ein nicht zu unterschätzendes Feature von Java sind Annotationen. Sie erlauben es Frameworks wie Spring, ihre Magie zu entfalten. Annotationen sind letztlich Metadaten, die zur Kompilier- oder auch zur Laufzeit vorliegen und über einen Annotation Processor geparst werden können. Dies macht sich beispielsweise die Bibliothek Lombok zunutze, um zur Kompilierzeit zusätzlichen Code zu generieren. Annotationen sind zwar sehr mächtig, eigene Annotation Processors zu schreiben aber auch verhältnismäßig kompliziert.
Zuletzt finde ich es sehr erfreulich, dass Java mit jedem Release weitere sinnvolle Ergänzungen in die Sprache einbaut, auch wenn dies manchmal länger gedauert hat, als man es sich eigentlich wünscht. Positiv hervorzuheben sind Generics in Java 5, Try-with-Resources in Java 7, Lambda-Funktionen in Java 8, Typinferenz für lokale Variablen in Java 10, mehrzeilige Strings in Java 13 und Pattern Matching in Java 14.
Cons
Die größte Schwäche von Java ist meines Erachtens seine Verbosität. Der Einsatz von Lombok kann zwar den Boilerplate-Code erheblich reduzieren und seit Java 10 gibt es mit dem Schlüsselwort var
Typinferenz für lokale Variablen, die die Textflut in Methoden ein Stück weit reduzieren können, jedoch ergibt sich allein schon durch die Konventionen der Sprache eine gewisse Komplexität. In Java ist es üblich, Klassen, Methoden und Variablen sehr sprechend zu benennen, was schnell mal länger werden kann, gerade wenn es viele Implementierungen eines Interfaces gibt. Zusätzlich fördert der modulare Grundgedanke der Sprache komplexen Code für eigentlich einfache Dinge, was sich aber zum Glück mittlerweile weniger stark bemerkbar macht als früher. Das folgende Beispiel verdeutlicht dies anhand einer Methode, die einen String in eine Datei schreibt.
Umsetzung bis einschließlich Java 6:
void writeToFileJava6(String content, String filename) {
BufferedWriter bufferedWriter = null;
try {
bufferedWriter = new BufferedWriter(new FileWriter(new File(filename)));
bufferedWriter.write(content);
} catch (IOException e) {
e.printStackTrace(); // Fehlerbehandlung hier einbauen
} finally {
if (bufferedWriter != null) {
try {
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace(); // Fehlerbehandlung hier einbauen
}
}
}
}
Umsetzung ab Java 7:
void writeToFileJava7(String content, String filename) {
try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get(filename))){
bufferedWriter.write(content);
} catch (IOException e) {
e.printStackTrace(); // Fehlerbehandlung hier einbauen
}
}
Hohe Komplexität entsteht aber nicht nur durch Modularität, sondern auch durch die Eigenschaften der Sprache. Selbst ein thread-sicheres Singleton erfordert in Java besondere Vorkehrungen. Ein weiteres Beispiel für Komplexität in Java ist das Thema Autoboxing. In Java gibt es sowohl Objekte als auch primitive Typen. Für die primitiven Typen wie int
, float
, char
und boolean
wurden bereits mit Java 2 äquivalente Klassen eingeführt. Neben dem primitiven Typen int
gibt es die Klasse Integer
, neben dem primitiven Typen char
gibt es die Klasse Character
usw. Einem int
kann dabei ein Integer
zugewiesen werden und andersrum, diese automatische Umwandlung heißt Autoboxing. Einem int
kann aber kein Long
(Klasse zum primitiven Typen long
) zugewiesen werden. Für eine solche Zuweisung benötigt man einen Cast oder eine entsprechende Helferklasse. Das folgende Beispiel verdeutlicht dies:
Long giveMeAnInt() {
return 8L;
}
int a = (int)(long) giveMeAnInt();
In diesem Beispiel wird durch einen Doppel-Cast der Long
zunächst auf einen primitiven long
und dann nochmal auf einen primitiven int
umgewandelt, damit die Zuweisung möglich ist. Ärgerlich ist dies in der Regel vor allem dann, wenn man keinen Einfluss auf die Methodensignatur hat, weil sie etwa in Bibliotheken Dritter liegt.
Datenblatt
Name | Java |
Webseite | https://www.oracle.com/java/technologies/ |
Erscheinungsjahr | 1995 |
Aktuellste Version (Stand 27. September 2020) | Version 14.0.2 vom 14. Juli 2020 |
Typisierung | statisch |
Paradigmen | imperativ objektorientiert funktional aspektorientiert |
Pros | Cons |
---|---|
hoher Funktionsumfang, viele Spezifikationen und Schnittstellen | sehr verbos |
sehr weit verbreitet, riesige Community | lange Historie |
unzählige Bibliotheken | viele Altlasten |
geringe Einstiegshürde | teilweise unnötig kompliziert |
gute Abwärtskompatibilität | teilweise sehr modular |
viele Strukturierungs- und Abgrenzungsmöglichkeiten | |
ideal für große Systeme | |
Annotation Processor als sehr mächtiges Werkzeug | |
plattformunabhängig | |
interaktive Shell (REPL) |
virtuallet
virtuallet ist ein kleines Programm von mir, welches ich in diversen Programmiersprachen implementiert habe. Hier geht es direkt zum Java-Code von virtuallet auf GitLab. Hier gibt es weitere Infos zu virtuallet.