Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 3. Auflage
 <<    <     >    >>   API  Kapitel 1 - Was ist Java?

1.2 Eigenschaften von Java



1.2.1 Sprachmerkmale

Java wurde vollständig neu entworfen. Die Designer versuchten, die Syntax der Sprachen C und C++ soweit wie möglich nachzuahmen, verzichteten aber auf einen Großteil der komplexen und fehlerträchtigen Merkmale beider Sprachen. Das Ergebnis ihrer Bemühungen haben sie wie folgt zusammengefaßt:

»Java soll eine einfache, objektorientierte, verteilte, interpretierte, robuste, sichere, architekturneutrale, portable, performante, nebenläufige, dynamische Programmiersprache sein.«

Der Erfolg von Java hängt eng damit zusammen, daß ein wesentlicher Teil dieser Forderungen tatsächlich in einer für viele Programmierer akzeptablen Weise erfüllt wurde - obgleich wir am Ende dieses Kapitels auch einige kritische Anmerkungen dazu geben werden.

Java ist sowohl eine objektorientierte Programmiersprache in der Tradition von Smalltalk als auch eine klassische imperative Programmiersprache nach dem Vorbild von C. Im Detail unterscheidet sich Java aber recht deutlich von C++, das denselben Anspruch erhebt. Durch die Integration einer großen Anzahl anspruchsvoller Features wie Multithreading, strukturiertem Exceptionhandling oder eingebauten grafischen Fähigkeiten implementiert Java eine Reihe interessanter Neuerungen auf dem Gebiet der Programmiersprachen.

Zudem profitiert Java davon, daß viele der Features von C++ nicht realisiert wurden und die Sprache dadurch schlank und übersichtlich wurde. So gibt es beispielsweise keine expliziten Pointer, keine separaten Header-Dateien, keine Mehrfachvererbung und keine Templates in Java. Wer allerdings glaubt, Java sei eine Programmiersprache, die nur das Allernötigste bietet, irrt. Tatsächlich ist Java eine elegante Sprache, die auch für größere Projekte und anspruchsvolle Aufgaben alle erforderlichen Reserven besitzt.

In Java gibt es die meisten elementaren Datentypen, die auch C besitzt. Arrays und Strings sind als Objekte implementiert und sowohl im Compiler als auch im Laufzeitsystem verankert. Methodenlose Strukturtypen wie struct oder union gibt es in Java nicht. Alle primitiven Datentypen sind vorzeichenbehaftet und in ihrer Größe exakt spezifiziert. Java besitzt einen eingebauten logischen Datentyp boolean.

Java bietet semidynamische Arrays, deren initiale Größe zur Laufzeit festgelegt werden kann. Arrays werden als Objekte angesehen, die einige wohldefinierte Eigenschaften haben. Mehrdimensionale Arrays werden wie in C dadurch realisiert, daß einfache Arrays ineinandergeschachtelt werden. Dabei können auch nicht-rechteckige Arrays erzeugt werden. Alle Array-Zugriffe werden zur Laufzeit auf Einhaltung der Bereichsgrenzen geprüft.

Die Ausdrücke in Java entsprechen weitgehend denen von C und C++. Java besitzt eine if-Anweisung, eine while-, do- und for-Schleife und ein switch-Statement. Es gibt die von C bekannten break- und continue-Anweisungen in normaler und gelabelter Form. Letztere ermöglicht es, mehr als eine Schleifengrenze zu überspringen. Java besitzt kein goto-Statement (obgleich es sich um ein reserviertes Wort handelt). Variablendeklarationen werden wie in C++ als Anweisungen angesehen und können an beliebiger Stelle innerhalb des Codes auftauchen. Seit der Version 1.4 besitzt Java eine assert-Anweisung, die zur Laufzeit an- und abgeschaltet werden kann.

Als OOP-Sprache besitzt Java alle Eigenschaften moderner objektorientierter Sprachen. Wie C++ erlaubt Java die Definition von Klassen, aus denen Objekte erzeugt werden können. Objekte werden dabei stets als Referenzdatentypen behandelt, die wie Variablen angelegt und verwendet werden können. Zur Initialisierung gibt es Konstruktoren, und es kann eine optionale Finalizer-Methode definiert werden, die bei der Zerstörung des Objekt aufgerufen wird. Seit der Version 1.1 gibt es lokale Klassen, die innerhalb einer anderen Klasse definiert werden.

Alle Methodenaufrufe in Java sind dynamisch. Methoden können überladen werden, Operatoren allerdings nicht. Anders als in C++ ist das Late-Binding standardmäßig aktiviert, kann aber per Methode deaktiviert werden. Java erlaubt Einfach-, aber keine Mehrfachvererbung von Implementierungen. Mit Hilfe von Interfaces (das sind abstrakte Klassendefinitionen, die nur aus Methoden bestehen) ist eine restriktive Form der Mehrfachvererbung möglich, die einen Kompromiß zwischen beiden Alternativen darstellt. Java erlaubt die Definition abstrakter Basisklassen, die neben konkreten auch abstrakte Methoden enthalten.

Neben Instanzvariablen und -methoden können auch Klassenvariablen und -methoden definiert werden. Alle Elemente einer Klassendefinition können mit Hilfe der aus C++ bekannten Schlüsselwörter public, private und protected in ihrer Sichtbarkeit eingeschränkt werden. Es gibt zwar keine friends, aber die Sichtbarkeit von Methoden oder Klassen kann auf das eigene Paket beschränkt werden. Objektvariablen werden als Referenzen implementiert. Mit ihrer Hilfe ist eine gegenüber C/C++ eingeschränkte Zeigerverarbeitung möglich, die das Erstellen dynamischer Datenstrukturen ermöglicht.

Das Speichermanagement in Java erfolgt automatisch. Während das Erzeugen von Objekten (von wenigen Ausnahmen abgesehen) immer einen expliziten Aufruf des new-Operators erfordert, erfolgt die Rückgabe von nicht mehr benötigtem Speicher automatisch. Ein Garbage-Collector, der als niedrigpriorisierter Hintergrundprozeß läuft, sucht in regelmäßigen Abständen nach nicht mehr referenzierten Objekten und gibt den durch sie belegten Speicher an das Laufzeitsystem zurück. Viele der Fehler, die bei der Programmierung in C oder C++ dadurch entstehen, daß der Entwickler selbst für das Speichermanagement verantwortlich ist, können in Java nicht mehr auftreten.

In Java gibt es ein strukturiertes Exceptionhandling. Damit ist es möglich, Laufzeitfehler zu erkennen und in strukturierter Weise zu behandeln. Eine Methode muß jeden Laufzeitfehler, der während ihrer Abarbeitung auftreten kann, entweder abfangen oder durch eine geeignete Deklaration an den Aufrufer weitergeben. Dieser hat dann seinerseits die Pflicht, sich um den Fehler zu kümmern. Exceptions sind normale Objekte, und die zugehörigen Klassen können erweitert und als Grundlage für anwendungsspezifische Fehler-Objekte verwendet werden.

1.2.2 Applets: Eine neue Klasse von Programmen

Eine der am meisten gebrauchten Erklärungen für den überraschenden Erfolg von Java ist die enge Verbindung der Sprache zum Internet und zum World Wide Web. Mit Hilfe von Java ist es möglich, Programme zu entwickeln, die über das Web verbreitet und innerhalb eines Browsers wie Netscape Navigator, SUN HotJava oder Microsoft Internet Explorer ausgeführt werden können. Dazu wurde die Sprache HTML um das APPLET-Tag erweitert. Sie bietet so die Möglichkeit, kompilierten Java-Code in normale Web-Seiten einzubinden.

Ein Java-fähiger Browser enthält einen Java-Interpreter (die virtuelle Java-Maschine, auch kurz VM genannt) und die Laufzeitbibliothek, die benötigt wird, um die Ausführung des Programms zu unterstützen. Die genaue Beschreibung der virtuellen Maschine ist Bestandteil der Java-Spezifikation, und Java-VMs sind auf praktisch alle bedeutenden Betriebssystem-Plattformen portiert worden. Ein Applet kann damit als eine neue Art von Binärprogramm angesehen werden, das über verschiedene Hardware- und Betriebssystemplattformen hinweg portabel ist und auf einfache Weise im Internet verteilt werden kann.

Im Gegensatz zu den eingeschränkten Möglichkeiten, die Script-Sprachen wie JavaScript bieten, sind Applets vollständige Java-Programme, die alle Merkmale der Sprache nutzen können. Insbesondere besitzt ein Applet alle Eigenschaften eines grafischen Ausgabefensters und kann zur Anzeige von Text, Grafik und Dialogelementen verwendet werden. Einer der großen Vorteile von Applets gegenüber herkömmlichen Programmen ist ihre einfache Verteilbarkeit. Anstelle explizit auszuführender Installationsroutinen lädt der Classloader des Browsers die Bestandteile eines Applets einfach aus dem Netz und führt sie direkt aus. Das ist vor allem bei kleineren und mittelgroßen Anwendungen in einer lokalen Netzwerkumgebung sehr hilfreich, insbesondere wenn diese sich häufig ändern.

Sicherheit war eines der wichtigsten Designziele bei der Entwicklung von Java, und es gibt eine ganze Reihe von Sicherheitsmechanismen, die verhindern sollen, daß Java-Applets während ihrer Ausführung Schaden anrichten. So ist es einem Applet, das in einem Web-Browser läuft, beispielsweise nicht erlaubt, Dateioperationen auf dem lokalen Rechner durchzuführen oder externe Programme zu starten.

Es soll allerdings nicht verschwiegen werden, daß die Applet-Euphorie im Laufe der Zeit deutlich abgeklungen ist. Dies lag einerseits an den Java-Implementierungen der Browser, die mit den JDKs nicht Schritt halten konnten. Zudem legten die Sicherheitsfeatures den Entwicklern oft so große Beschränkungen auf, daß sie den echten Nutzen der Applets einschränkten. Der große Erfolg von Java in der Post-1.1-Ära ist eher in der Entwicklung von Applikationen als von Applets zu sehen. Mit der Veröffentlichung der WebStart-Technologie während des JDK 1.3 wurden dann die Vorteile beider Technologien wieder miteinander verbunden.

 Hinweis 

1.2.3 Grafikprogrammierung

Die Java-Laufzeitbibliothek bietet umfassende grafische Fähigkeiten. Diese sind im wesentlichen plattformunabhängig und können dazu verwendet werden, portable Programme mit GUI-Fähigkeiten auszustatten. Seit der Version 1.2 des JDK werden diese Fähigkeiten unter dem Begriff Java Foundation Classes (kurz JFC) zusammengefaßt, deren drei wichtigste Komponenten die folgenden sind:

Es ist eine bemerkenswerte Innovation, daß Elemente für die GUI-Programmierung in einer Programmiersprache portabel zur Verfügung gestellt werden. Zwar gab es im Prinzip auch früher schon Programmiersprachen, die grafische Fähigkeiten hatten, aber wer einmal die Aufgabe hatte, eine grafische Benutzeroberfläche unter Windows, OS/2, UNIX und auf dem MAC zur Verfügung zu stellen, hatte meistens dennoch erheblichen Portierungsaufwand. Mit Standardmitteln der Sprachen C oder C++ und ihren Laufzeitbibliotheken war dies jedenfalls nicht möglich. Mit Java und ihren Klassenbibliotheken steht nun erstmals eine einfach zu verwendende Sprache zur Verfügung, die das Erstellen von GUI-Programmen bereits als Kernfunktionalität bietet.

 Hinweis 

Das AWT stellt eine Reihe von elementaren Operationen zur Verfügung, um grafische Ausgabeelemente, wie Linien, Polygone, Kreise, Ellipsen, Kreisabschnitte oder Rechtecke, zu erzeugen. Diese Methoden können auch in einem Füllmodus verwendet werden, der dafür sorgt, daß die gezeichneten Flächen mit Farbe ausgefüllt werden. Wie in den meisten Grafik-Libraries realisiert auch Java die Bildschirmausgabe mit Hilfe des Konzepts eines Grafikkontexts, der eine Abstraktion des tatsächlichen Ausgabegerätes bildet.

Neben grafischen Elementen kann natürlich auch Text ausgegeben und an beliebiger Stelle innerhalb der Fenster plaziert werden. Text kann skaliert werden, und es ist möglich, mit unterschiedlichen Fonts zu arbeiten. Das AWT bemüht sich, einen portablen Weg zur Font-Auswahl anzubieten, indem eine Reihe von elementaren Schriftarten auch über Plattformgrenzen hinweg angeboten werden. Mit Hilfe von Font-Metriken können numerische Eigenschaften der verwendeten Schriftarten bestimmt und bei der Ausgabe berücksichtigt werden.

Das Farbmodell von Java basiert auf dem RGB-Modell, das seine Farben additiv auf der Basis der enthaltenen Rot-, Grün- und Blauanteile bestimmt. Daneben wird auch das HSB-Modell unterstützt (hue, saturation, brightness), und es gibt Methoden zur Konvertierung zwischen beiden. Das Farbsystem unterstützt eine Reihe von vordefinierten Farben, die plattformübergreifend zur Verfügung stehen.

Neben Grafik kann auch Sound ausgegeben werden. Java unterstützt die Wiedergabe von au-Dateien (ein von SUN eingeführtes Format zur Speicherung von digitalen Sound-Samples) und seit der Version 1.2 auch wav- und aiff-Dateien, die entweder über das Internet oder aus einer lokalen Datei geladen werden können. Die Samples können einmalig abgespielt oder in einer Schleife wiederholt werden. Daneben ist es möglich, zwei oder mehr Sound-Dateien gleichzeitig abzuspielen. Seit dem JDK 1.2 gibt es ein eigenes Sound-API, das neben Wave-Dateien auch Midi-Dateien wiedergeben und bearbeiten kann.

Das AWT erlaubt die Anzeige und Manipulation von Bilddaten. Mit Hilfe von Standardmethoden können Grafiken in elementaren Formaten wie GIF oder JPEG geladen, skaliert und auf dem Bildschirm angezeigt werden. Zusätzlich gibt es das Paket java.awt.image, das für die Manipulation von Bilddaten entworfen wurde und ausgefeilte Funktionen zur Bild- und Farbmanipulation zur Verfügung stellt.

Wie in den meisten grafischen Entwicklungsumgebungen wird auch beim AWT der Programmfluß durch Nachrichten gesteuert. Sie werden beim Auftreten bestimmter Ereignisse an das Programm gesendet und von diesem in geeigneter Weise behandelt. Java stellt Nachrichten zur Bearbeitung von Maus-, Tastatur-, Fenster-, Dialog- und vielen anderen Ereignissen zur Verfügung. Das Event-Handling seit dem JDK 1.1 erlaubt es, Nachrichten an jedes beliebige Objekt zu senden, das die Schnittstelle eines Nachrichtenempfängers implementiert.

 Hinweis 

Die zweite graphische Oberfläche des JDK, das Swing-Toolkit, bietet noch weitreichendere Fähigkeiten als das AWT. Dazu wurde eine vollkommen neue Architektur entworfen, und es stehen viele zusätzliche Dialogelemente, wie Tabellen, Trees oder Karteikarten, zur Verfügung. Anstatt wie im AWT auf die Eigenschaften vorgefertigter GUI-Elemente zu vertrauen, verwendet Swing lediglich einen sehr eingeschränkten Satz an plattformspezifischen Grafikoperationen. Alle Dialogelemente werden unter Verwendung einfacher und portabler Grafikprimitive selbst dargestellt. Die Anzahl der Unterschiede zwischen den verschiedenen Plattformen wird auf diese Weise drastisch reduziert, und die Swing-Dialogelemente lassen sich wesentlich einfacher und konsistenter auf unterschiedliche Grafiksysteme portieren.

1.2.4 Umfangreiche Klassenbibliothek

Die Java-Klassenbibliothek bietet mit einer ganzen Reihe nützlicher Klassen und Interfaces die Möglichkeit, sehr problemnah zu programmieren. Einige dieser Features sind von Anfang an nützlich, andere erschließen sich erst nach einer gewissen Einarbeitung.

Neben grafischen Ausgabemöglichkeiten stellt Java auch einfache Textausgaben zur Verfügung, ähnlich den entsprechenden Funktionen in C oder C++. Damit ist es möglich, Programme mit einfachen, zeilenorientierten Ein-/Ausgabemöglichkeiten auszustatten, wenn keine aufwendige Benutzerschnittstelle benötigt wird. Einfache Textausgaben werden mit den Methoden der Klasse PrintStream erzeugt. Diese erlauben es, alle gängigen Datentypen in ein Terminalfenster auszugeben. Die Klassenvariable System.out bietet einen vordefinierten PrintStream, der vom Laufzeitsystem initialisiert wird. In ähnlicher Weise steht mit System.in die Möglichkeit zur Verfügung, einfache Texteingaben von der Tastatur einzulesen.

Eines der wichtigsten Elemente der Klassenbibliothek ist die Klasse String, die Java-Implementierung von Zeichenketten. String bietet eine Vielzahl wichtiger Methoden zur Manipulation und zum Zugriff auf Zeichenketten, wie beispielsweise Operationen für numerische Konvertierungen, Zeichen- und Teilstringextraktion sowie für Textsuche und Stringvergleich.

Interessanterweise kann sich ein String-Objekt nach seiner Initialisierung nicht mehr verändern, sondern behält stets seinen ursprünglichen Wert. Was zunächst wie eine schwerwiegende Restriktion aussieht, ist in der Praxis meist bedeutungslos. Denn in Zusammenarbeit mit der Klasse StringBuffer (die variabel lange Strings repräsentiert) und der Fähigkeit des Compilers, selbige automatisch bei der String-Initialisierung, -Zuweisung und -Verkettung zu verwenden, bleibt diese Tatsache für den Programmierer normalerweise verborgen. Dank des automatischen Speichermanagements und der effizienten Konvertierung von StringBuffer nach String ähnelt der Umgang mit Strings aus der Sicht des Programmierers dem mit variabel langen Zeichenketten in anderen Programmiersprachen. Wegen des automatischen Speichermanagements sind Java-Strings sehr viel sicherer als nullterminierte Strings in C oder C++.

Ein Vector in Java ist eine lineare Liste, die jede Art von Objekt aufnehmen kann und auf deren Elemente sowohl sequentiell als auch wahlfrei zugegriffen werden kann. Die Länge eines Vektors ist veränderlich, und Elemente können am Ende oder an einer beliebigen anderen Stelle eingefügt werden. Aufgrund dieser Flexibilität kann ein Vector oft da verwendet werden, wo ansonsten eine lineare Liste durch Verkettung von Objektreferenzen manuell erstellt werden müßte. Wie gewöhnlich erfolgt auch das Speichermanagement eines Vektors vollkommen automatisch. Neben Vector gibt es weitere Container-Klassen. So bietet beispielsweise Hashtable die Möglichkeit, Schlüssel-Wert-Paare zusammenhängend zu speichern und bei gegebenem Schlüssel den zugehörigen Wert effizient wieder aufzufinden.

Ein nützlicher Mechanismus zum Durchlaufen von Container-Klassen ist das Enumeration-Interface, das die Methoden hasMoreElements und nextElement zur Verfügung stellt. Diese können verwendet werden, um in einer Schleife alle Elemente des Containers sukkzessive zu durchlaufen. Alle vordefinierten Container-Klassen stellen Methoden zur Verfügung, die Enumeration-Objekte zum Durchlaufen der eigenen Elemente zurückgeben.

Seit dem JDK 1.2 gibt es in Java eine eigene Bibliothek für Container-Klassen, das Collection-API. Sie stellt eine umfassende Sammlung an Interfaces für Container-Klassen zur Verfügung und bietet unterschiedliche Implementierungen für verschiedene Anwendungsfälle. Die zuvor erwähnte Klasse Enumeration wird hier durch das Interface Iterator ersetzt, das einfacher zu bedienen ist. Das Collection-API stellt daneben einige Algorithmen zur Verarbeitung von Containern zur Verfügung (z.B. Sortieren), die es in den älteren Container-Klassen nicht gab.

Java stellt auch Zufallszahlen zur Verfügung. Das Paket java.util bietet eine Klasse Random, die das Initialisieren von Zufallszahlengeneratoren und den Zugriff auf ganzzahlige oder Fließkomma-Zufallszahlen ermöglicht. Neben gleichverteilten stellt die Klasse Random auch normalverteilte Zufallszahlen zur Verfügung.

Seit dem JDK 1.1 werden darüber hinaus mit jedem Release weitere hochspezialisierte (und teilweise sehr aufwendige) Bibliotheken zur Verfügung gestellt. So bietet beispielsweise JDBC (Java Database Connectivity) den Zugriff auf relationale Datenbanken, JavaBeans stellt eine portable Komponentenarchitektur zur Verfügung, und mit dem Networking-API, RMI (Remote Method Invocation) und der Java-eigenen CORBA-Implementierung javaidl kann unternehmensweit auf Netzwerkressourcen und verteilte Objekte zugegriffen werden. Per Serialisierung können Objekte persistent gemacht werden, und mit dem Reflection-API kann der Aufbau von Objekten und Klassen zur Laufzeit untersucht und dynamisch darauf zugegriffen werden. Wir werden in diesem Buch die wichtigsten dieser Bibliotheken ausführlich erläutern.


 Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0.1
 <<    <     >    >>   API  © 1998-2003 Guido Krüger, http://www.javabuch.de