Java ist eine objektorientierte Programmiersprache und eine eingetragene Marke des Unternehmens Sun Microsystems, welches 2010 von Oracle übernommen wurde. Die Programmiersprache ist ein Bestandteil der Java-Technologie – diese besteht grundsätzlich aus dem Java-Entwicklungswerkzeug (JDK) zum Erstellen von Java-Programmen und der Java-Laufzeitumgebung (JRE) zu deren Ausführung. Die Laufzeitumgebung selbst umfasst die virtuelle Maschine (JVM) und die mitgelieferten Bibliotheken. Java als Programmiersprache sollte nicht mit der Java-Technologie gleichgesetzt werden; Java-Laufzeitumgebungen führen Bytecode aus, der sowohl aus der Programmiersprache Java als auch aus anderen Programmiersprachen wie Groovy, Kotlin und Scala kompiliert werden kann. Im Prinzip könnte jede Programmiersprache als Grundlage für Java-Bytecode genutzt werden, für die meisten existieren aber keine passenden Bytecode-Compiler.
Die Programmiersprache Java dient innerhalb der Java-Technologie vor allem zum Formulieren von Programmen. Diese liegen zunächst als reiner, menschenverständlicher Text vor, dem sogenannten Quellcode. Dieser Quellcode ist nicht direkt ausführbar; erst der Java-Compiler, der Teil des Entwicklungswerkzeugs ist, übersetzt ihn in den maschinenverständlichen Java-Bytecode. Die Maschine, die diesen Bytecode ausführt, ist jedoch typischerweise virtuell – das heißt, der Code wird meist nicht direkt durch Hardware, wie z. B. einen Mikroprozessor, ausgeführt, sondern durch entsprechende Software auf der Zielplattform.
Zweck dieser Virtualisierung ist Plattformunabhängigkeit: Das Programm soll ohne weitere Änderung auf jeder Rechnerarchitektur laufen können, sofern dort eine passende Laufzeitumgebung installiert ist. Oracle selbst bietet Laufzeitumgebungen für die Betriebssysteme Linux, macOS und Windows an. Andere Hersteller lassen eigene Java-Laufzeitumgebungen für ihre Plattform zertifizieren. Auch in Autos, HiFi-Anlagen und anderen elektronischen Geräten wird Java verwendet.
Um die Ausführungsgeschwindigkeit zu erhöhen, werden Konzepte wie die Just-in-time-Kompilierung und die Hotspot-Optimierung verwendet. In Bezug auf den eigentlichen Ausführungsvorgang kann die JVM den Bytecode also interpretieren, ihn bei Bedarf jedoch auch kompilieren und optimieren.
Java ist eine der populärsten Programmiersprachen. In dem seit 2001 veröffentlichten TIOBE-Index lag Java bis 2020, konkurrierend mit C, stets auf den ersten beiden Plätzen. Seit 2021 belegt jedoch Python den ersten Platz dieses Rankings – gefolgt von C, C++ und Java. Der Anteil von Java sank von ca. 25 % im Jahr 2001 auf etwa 10 % (2021–2025). Nach dem RedMonk-Programmiersprachenindex liegt Java seit 2020 bzw. 2021 hinter JavaScript und Python.
Grundkonzepte Der Entwurf der Programmiersprache Java strebte hauptsächlich fünf Ziele an:The Java Language: An Overview. 1995 Sun Whitepaper
Sie soll eine einfache, objektorientierte, verteilte und vertraute Programmiersprache sein. Sie soll robust und sicher sein. Sie soll architekturneutral und portabel sein. Sie soll sehr leistungsfähig sein. Sie soll interpretierbar, parallelisierbar und dynamisch sein.
Einfachheit Java ist im Vergleich zu anderen objektorientierten Programmiersprachen wie C++ oder C# einfach, da es einen reduzierten Sprachumfang besitzt und beispielsweise Operatorüberladung und Mehrfachvererbung nicht unterstützt. Objektorientierung Java gehört zu den objektorientierten Programmiersprachen. Verteilt Eine Reihe einfacher Möglichkeiten für Netzwerkkommunikation, von TCP/IP-Protokollen über Remote Method Invocation bis zu Webservices werden vor allem über Javas Klassenbibliothek angeboten; die Sprache Java selbst beinhaltet keine direkte Unterstützung für verteilte Ausführung. Vertrautheit Wegen der syntaktischen Nähe zu C++, der ursprünglichen Ähnlichkeit der Klassenbibliothek zu Smalltalk-Klassenbibliotheken und der Verwendung von Entwurfsmustern in der Klassenbibliothek ist der Umstieg auf Java für erfahrene Programmierer vergleichsweise einfach. Robustheit Viele der Designentscheidungen bei der Definition von Java reduzieren die Wahrscheinlichkeit ungewollter Systemfehler; Beispiele dafür sind die starke Typisierung, Garbage Collection, Ausnahmebehandlung sowie Verzicht auf Zeigerarithmetik. Sicherheit Zur Verbesserung der Sicherheit stehen Konzepte wie der Class-Loader, der die sichere Zuführung von Klasseninformationen zur Java Virtual Machine steuert, und Security-Manager, die sicherstellen, dass nur Zugriff auf Programmobjekte erlaubt wird, für die entsprechende Rechte vorhanden sind. Architekturneutralität Java wurde so entwickelt, dass dieselbe Version eines Programms prinzipiell auf einer beliebigen Computerhardware läuft, unabhängig von ihrem Prozessor oder anderen Hardwarebestandteilen. Portabilität Zusätzlich zur Architekturneutralität ist Java portabel. Das heißt, dass elementare Datentypen sowohl in ihrer Größe und internen Darstellung als auch in ihrem arithmetischen Verhalten standardisiert sind. Beispielsweise ist ein float immer ein IEEE 754 Float von 32 Bit Länge. Dasselbe gilt beispielsweise auch für die Klassenbibliothek, mit deren Hilfe man eine vom Betriebssystem unabhängige GUI erzeugen kann. Leistungsfähigkeit Java hat aufgrund der Optimierungsmöglichkeit zur Laufzeit das Potenzial, eine bessere Performance als auf Compilezeit-Optimierungen begrenzte Sprachen (C++ etc.) zu erreichen. Dem entgegen steht der Overhead durch die Java-Laufzeitumgebung, sodass die Leistungsfähigkeit von beispielsweise C++-Programmen in einigen Kontexten übertroffen, in anderen aber nicht erreicht wird. Um Leistungsfähigkeit zu gewährleisten, kann in der Java Virtual Machine (JVM) die Performance gemessen werden. Interpretierbarkeit Java wird in maschinenunabhängigen Bytecode kompiliert, dieser wiederum kann auf der Zielplattform interpretiert werden. Die Java Virtual Machine interpretiert Java-Bytecode, bevor sie ihn aus Performancegründen kompiliert und optimiert. Parallelisierbarkeit Java unterstützt Multithreading, also den parallelen Ablauf von eigenständigen Programmabschnitten. Dazu bietet die Sprache selbst die Schlüsselwörter synchronized und volatile – Konstrukte, die das „Monitor & Condition Variable Paradigma“ von C. A. R. HoareC. A. R. Hoare: Monitors: an operating system structuring concept. (PDF) In: Communications of the ACM, 17, Nr. 10, 1974, S. 549–557 doi:10.1145/355620.361161 unterstützen. Die Klassenbibliothek enthält weitere Unterstützungen für parallele Programmierung mit Threads. Moderne JVMs bilden einen Java-Thread auf Betriebssystem-Threads ab und profitieren somit von Prozessoren mit mehreren Rechenkernen. Dynamisch Java ist so aufgebaut, dass es sich an dynamisch ändernde Rahmenbedingungen anpassen lässt. Da die Module erst zur Laufzeit gelinkt werden, können beispielsweise Teile der Software (etwa Bibliotheken) neu ausgeliefert werden, ohne die restlichen Programmteile anpassen zu müssen. Interfaces können als Basis für die Kommunikation zwischen zwei Modulen eingesetzt werden; die eigentliche Implementierung kann aber dynamisch und beispielsweise auch während der Laufzeit geändert werden.
Objektorientierung mini|Abhängigkeitsgraph der Java-Core-Klassen (erstellt mit jdeps und Gephi). In der Mitte des Diagramms sind die am häufigsten verwendeten Klassen Object und String zu sehen.
Die Grundidee der objektorientierten Programmierung ist, Daten und zugehörige Funktionen möglichst eng in einem sogenannten Objekt zusammenzufassen und nach außen hin zu kapseln (Abstraktion). Die Absicht dahinter ist, große Softwareprojekte einfacher zu verwalten und die Qualität der Software zu erhöhen. Ein weiteres Ziel der Objektorientierung ist ein hoher Grad der Wiederverwendbarkeit von Softwaremodulen.
Ein neuer Aspekt von Java gegenüber den objektorientierten Programmiersprachen C++ und Smalltalk ist die explizite Unterscheidung zwischen Schnittstellen und Klassen, die durch entsprechende Schlüsselwörter interface und class ausgedrückt wird. Java unterstützt kein Erben von mehreren unabhängigen Basisklassen (sogenannte „Mehrfachvererbung“ wie in C++ oder Eiffel), wohl aber das Implementieren einer beliebigen Zahl von Schnittstellen, womit sich viele der entsprechenden Probleme ebenfalls lösen lassen. Dabei werden Methodensignaturen und Standardimplementierungen von Methoden an die abgeleiteten Klassen weitergegeben, jedoch keine Attribute.
Java ist nicht vollständig objektorientiert, da die Grunddatentypen (int, boolean usw.) keine Objekte sind (siehe auch unter Java-Syntax). Sie werden allerdings ab Java 5 bei Bedarf automatisch und für den Programmierer transparent mittels Autoboxing in die entsprechenden Objekttypen und umgekehrt umgewandelt.Autoboxing in Java (englisch)
Beispiel Source-Code /** * Diese Klasse ist eine allgemeine Klasse für jedes beliebige Tier und bietet * Methoden an, die alle Tiere gemeinsam haben. */ public class Tier { /** * Diese Methode lässt das Tier kommunizieren. Die Unterklassen dieser * Klasse können diese Methode überschreiben und eine passende * Implementierung für das jeweilige Tier anbieten. */ public void kommuniziere() { // Wird von allen Unterklassen verwendet, die diese Methode nicht überschreiben. IO.println("Tier sagt nichts.");
/** * Deklariert die Klasse "Hund" als Unterklasse der Klasse "Tier". * Die Klasse "Hund" erbt damit die Felder und Methoden der Klasse "Tier". */ class Hund extends Tier { /** * Diese Methode ist in der Oberklasse "Tier" implementiert. Sie wird * in dieser Klasse überschrieben und für die Tierart "Hund" angepasst. */ @Override public void kommuniziere() { // Ruft die Implementierung dieser Methode in der Oberklasse "Tier" auf. super.kommuniziere(); // Gibt einen Text in der Konsole aus. IO.println("Hund sagt: 'Wuff Wuff'");
/** * Deklariert die Klasse "Katze" als Unterklasse der Klasse "Tier". * Die Klasse "Katze" erbt damit die Felder und Methoden der Klasse "Tier". */ class Katze extends Tier { /** * Diese Methode ist in der Oberklasse "Tier" implementiert. Sie wird * in dieser Klasse überschrieben und für die Tierart "Katze" angepasst. */ @Override public void kommuniziere() { // Ruft die Implementierung dieser Methode in der Oberklasse "Tier" auf. super.kommuniziere(); // Gibt einen Text auf der Konsole aus. IO.println("Katze sagt: 'Miau'");
class Main { /** * Methode, die beim Programmstart aufgerufen wird. */ public static void main(String[] args) { // Deklariert eine Variable für Instanzen der Klassen "Hund" und "Katze" Tier tier;
// Erstellt eine Instanz der Klasse "Hund" und speichert die Instanz in // der Variable "tier" tier = new Hund(); // Ruft die Methode Hund.kommuniziere() auf tier.kommuniziere();
// Erstellt eine Instanz der Klasse "Katze" und speichert die Instanz in // der Variable "tier" tier = new Katze(); // Ruft die Methode Katze.kommuniziere() auf tier.kommuniziere();
Konsolenausgabe Tier sagt nichts. Hund sagt: 'Wuff Wuff' Tier sagt nichts. Katze sagt: 'Miau'
Reflexion Java bietet eine Reflexion-API als Bestandteil der Laufzeitumgebung. Damit ist es möglich, zur Laufzeit auf Klassen und Methoden zuzugreifen, deren Existenz oder genaue Ausprägung zur Zeit der Programmerstellung nicht bekannt war. Häufig wird diese Technik im Zusammenhang mit dem Entwurfsmuster Fabrikmethode (Factory Method) angewandt.
Annotationen