Objektorientierte Programmierung und Java
von Laura Lemay
Heute erhalten Sie einen Einblick in die Konzepte der objektorientierten Programmierung von Java und die Art, wie sie sich auf die Struktur Ihrer Programme auswirken:
Wenn Sie mit objektorientierter Programmierung vertraut sind, ist der Großteil der heutigen Lektion für Sie ein alter Hut. Vielleicht gehen Sie statt dessen lieber in's Kino. Morgen gibt es wieder etwas Interessantes für Sie.
Betrachten wir einmal als Vergleich Legos. Für diejenigen, die nicht viel mit Kindern zu tun haben, sei erwähnt, daß Legos kleine Bausteine aus Kunststoff in verschiedenen Formen und Farben sind. Jedes Bausteinchen hat auf einer Seite kleine Noppen, die in runde Löcher anderer Bausteinchen passen, so daß aus mehreren Legos größere Formen zusammengesteckt werden können. Aus verschiedenen Lego-Teilen (Lego-Rädern, Lego-Motoren, Lego-Anhängerkupplungen) können wiederum größere Teile, z. B. Schlösser, Autos oder Riesenroboter, zusammengesetzt werden. Die Kombinationsmöglichkeiten sind schier endlos. Lego-Steinchen sind kleine Objekte, die auf vorgegebene Weise zusammengesetzt werden können, um größere Objekte zu bilden.
Hier ein anderes Beispiel. Sie gehen in einen Computerladen und stellen sich aus verschiedenen Teilen ohne viel Ahnung mit ein bißchen Hilfe des Verkäufers einen kompletten PC zusammen: eine Mutterplatine, einen CPU-Chip, eine Videokarte, eine Festplatte, eine Tastatur usw. Im Idealfall erhalten Sie ein System, dessen Einheiten zusammen ein größeres System bilden, mit dem Sie die Probleme lösen, für die Sie den Rechner eigentlich gekauft haben.
Intern können diese Teile komplex sein und von verschiedenen Herstellern stammen, die sie nach verschiedenen Konstruktionsmethoden gebaut haben. Sie braucht das aber nicht zu interessieren, was die einzelnen Chips auf der Karte alles machen oder wie ein «A» am Bildschirm erscheint, wenn Sie die A-Taste auf der Tastatur drücken. Alle Komponenten sind unabhängige Einheiten, die Sie nur insoweit interessieren, als sie (hoffentlich) nahtlos als Gesamtsystem zusammenarbeiten. Paßt diese Videokarte in den Steckplatz der Mutterplatine und funktioniert sie mit diesem Monitor? Sprechen die einzelnen Komponenten die richtige Sprache, um einander verstehen zu können? Wenn Sie wissen, welche Interaktionen zwischen den Komponenten ablaufen, können Sie mühelos als Laie ein Komplettsystem zusammenstellen.
Was hat das mit Programmierung zu tun? Alles. Objektorientierte Programmierung funktioniert genau auf diese Weise. Durch objektorientierte Programmierung besteht ein Gesamtprogramm aus vielen unabhängigen Komponenten (Objekten), die je eine bestimmte Rolle im Programm spielen und die miteinander auf vorgegebene Weise sprechen.
Objektorientierte Programmierung ist das Modellieren von Objekten, die wie im echten Leben aus vielen kleineren Objekten bestehen. Diese Möglichkeit des Kombinierens von Objekten ist aber nur ein sehr kleiner Aspekt der objektorientierten Programmierung. Objektorientierte Programmierung bietet viele andere Konzepte und Merkmale, um Objekte einfacher und flexibler zu gestalten und zu benutzen. Das wichtigste Merkmal ist die Klasse.
Wenn Sie ein Programm in einer objektorientierten Sprache schreiben, definieren Sie keine Objekte an sich. Sie definieren vielmehr Objektklassen.
Nehmen wir beispielsweise an, Sie haben eine Klasse namens Baum, die die Merkmale aller Bäume (haben Blätter und Wurzeln, wachsen, produzieren Chlorophyll) beschreibt. Die Baum-Klasse dient als abstraktes Modell für das Konzept eines Baums - die Hand ausstrecken und ihn anfassen, also mit ihm interagieren, oder ihn fällen - bedarf einer konkreten Instanz eines Baumes. Selbstverständlich können Sie viele Instanzen eines Baumes schaffen und jede Bauminstanz kann andere Merkmale haben (klein, groß, buschig, winterhart oder abfallend), ein Baum bleibt aber immer ein Baum (siehe Abb. 2.1).
Was genau ist also der Unterschied zwischen einer Instanz und einem Objekt? Eigentlich nichts. Objekt ist ein allgemeinerer Begriff, doch sind sowohl Instanzen als auch Objekte die konkrete Darstellung einer Klasse. In der objektorientierten Programmierung werden die Begriffe Instanz und Objekt meist gleichbedeutend verwendet. Eine Instanz eines Baumes und ein Baumobjekt sind das gleiche.
Als Beispiel, das dem Thema Java-Programmierung näher
kommt, könnten Sie eine Klasse für ein Element der Benutzeroberfläche,
etwa eine Schaltfläche (Button) erstellen. Die Button-Klasse
definiert die Merkmale eines Buttons (Beschriftung, Größe,
Aussehen) und wie er sich verhält (muß er einfach oder
doppelt angeklickt werden, ändert er beim Anklicken seine
Farbe, was wird durch ihn bewirkt?). Haben Sie die Button-Klasse
definiert, können Sie mühelos Instanzen dieses Buttons,
d. h. Button-Objekte, erstellen, die alle die Grundzüge des
ursprünglichen Buttons aufweisen, aber anders aussehen und
sich anders verhalten, je nach dem, wofür sie dienen. Dank
der Button-Klasse brauchen Sie nicht für jeden einzelnen
Button, den Sie in Ihrem Programm benötigen, einen eigenen
Code zu schreiben. Außerdem können Sie die Button-Klasse
wiederverwenden, um andere Button-Arten für das gleiche oder
andere Programme zu erstellen.
Abbildung 2.1: Baumklasse mit Bauminstanzen
Beim Schreiben eines Java-Programms werden Klassen entworfen und zusammengestellt. Läuft das Programm, werden Instanzen dieser Klassen nach Bedarf erstellt oder verworfen. Ihre Aufgabe als Java-Programmierer ist es, die richtigen Klassen anzulegen, um das zu erreichen, was Ihr Programm bewältigen soll.
Zum Glück müssen Sie nicht von Null beginnen. Die Java-Umgebung beinhaltet eine Bibliothek mit Klassen, die viele Grundzüge implementieren, nicht nur für grundlegende Programmieraufgaben (Klassen mit mathematischen Grundfunktionen, Arrays, Zeichenketten usw.), sondern auch für Grafiken und Vernetzung. In vielen Fällen reicht die Java-Klassenbibliothek aus, so daß Sie für ein Java-Programm nur eine Klasse erstellen müssen, die auf der Standard-Klassenbibliothek basiert, und die gewünschten Änderungen einbringen. Für komplizierte Java-Programme können Sie mehrere Klassen mit definierten Interaktionen erstellen.
Jede Klasse, die Sie in Java schreiben, besteht im allgemeinen aus zwei Komponenten: Attribute und Eigenschaften. In diesem Abschnitt lernen Sie, wie diese Komponenten auf eine theoretische Klasse namens Motorcycle angewandt werden. Am Schluß dieser Lektion erstellen Sie den Java-Code zur Implementierung einer Präsentation eines Motorrades.
Attribute sind die einzelnen Dinge, durch die sich ein Objekt von anderen unterscheidet. Sie bestimmen Aussehen, Zustand und andere Qualitäten eines Objekts. Wir erstellen jetzt eine theoretische Klasse namens Motorcycle. Ein Motorrad kann folgende Attribute haben:
Die Attribute eines Objekts können auch Informationen über seinen Zustand enthalten, z. B. Merkmale des Motorzustands (aus oder ein) oder den eingelegten Gang.
Attribute werden durch Variablen definiert. Sie können Sie sich als globale Variablen für das gesamte Objekt vorstellen. Da jede Instanz einer Klasse verschiedene Werte für ihre Variablen haben kann, nennt man jede Variable eine Instanzvariable.
Jedes Attribut hat eine einzelne entsprechende Instanzvariable. Durch Änderung des Wertes einer Variablen ändert sich das Attribut des Objekts. Instanzvariablen können gesetzt werden, wenn ein Objekt erstellt wird. Sie bleiben durch die gesamte Lebensdauer des Objekts konstant oder können sich nach Belieben bei der Ausführung des Programms ändern.
Zusätzlich zu Instanzvariablen gibt es Klassenvariablen, die die Klasse selbst und alle ihre Instanzen betreffen. Im Gegensatz zu Instanzvariablen, deren Werte in der Instanz gespeichert werden, werden die Werte von Klassenvariablen direkt in der Klasse gespeichert. Sie lernen in dieser Woche mehr über Klassenvariablen. Instanzvariablen werden in der morgigen Lektion behandelt.
Um das Verhalten eines Objekts zu definieren, erstellen Sie Methoden, die mit Funktionen in anderen Sprachen vergleichbar sind, jedoch innerhalb einer Klasse definiert werden. In Java werden keine Funktionen außerhalb von Klassen (wie etwa bei C++) definiert.
Methoden wirken sich aber nicht immer nur auf ein Objekt aus. Objekte kommunizieren auch miteinander durch Methoden. Eine Klasse oder ein Objekt kann Methoden einer anderen Klasse oder eines anderen Objekts aufrufen, um Änderungen in der Umgebung mitzuteilen oder das Objekt aufzufordern, seinen Zustand zu ändern.
Ebenso wie es Instanz- und Klassenvariablen gibt, gibt es auch Instanz- und Klassenmethoden. Instanzmethoden (die so üblich sind, daß Sie normalerweise nur «Methoden» genannt werden) werden auf eine Instanz angewandt. Klassenmethoden werden auf eine Klasse (oder auf andere Objekte) angewandt. Sie lernen noch in dieser Woche mehr über Klassenmethoden.
Diese Lektion war bisher eher theoretisch. In diesem Abschnitt erstellen Sie ein funktionierendes Beispiel der Motorcycle-Klasse, so daß Sie sehen können, wie Instanzvariablen und Methoden in einer Klasse definiert werden. Sie erstellen außerdem eine Java-Anwendung, die eine neue Instanz der Klasse Motorcycle erzeugt und deren Instanzvariablen anzeigt.
Sind Sie bereit? Dann fangen wir mit der grundlegenden Klassendefinition an. Öffnen Sie den Editor und geben Sie folgendes ein:
class Motorcycle { }
Herzlichen Glückwunsch! Sie haben soeben eine Klasse angelegt. Selbstverständlich passiert da nicht viel, aber Sie haben eine Java-Klasse in ihrer einfachsten Form.
Jetzt erstellen wir einige Instanzvariablen für diese Klasse - drei, um genau zu sein. Geben Sie nach der ersten Zeile folgende drei Zeilen ein:
String make; String color; boolean engineState;
Damit haben Sie drei Instanzvariablen erstellt: make und color können String-Objekte (String bedeutet «Zeichenkette» und ist Teil der vorher erwähnten Standard-Klassenbibliothek) enthalten. engineState ist ein boolescher Ausdruck, der bestimmt, ob der Motor läuft oder ausgeschaltet ist.
Jetzt werden bestimmte Eigenschaften (Methoden) in die Klasse eingefügt. Ein Motorrad kann sich auf unterschiedliche Weise verhalten. Wir halten uns in diesem Beispiel kurz und fügen nur eine Methode ein, die den Motor anläßt. Geben Sie unter den Instanzvariablen in Ihrer Klassendefinition folgende Zeilen ein:
void startEngine() { if (engineState == true) System.out.println("The engine is already on."); else { engineState = true; System.out.println("The engine is now on."); } }
Die startEngine-Methode prüft, ob der Motor bereits läuft (in der Zeile engineState == true). Trifft das zu, gibt sie eine diesbezügliche Meldung aus. Falls der Motor noch nicht läuft, ändert sich der Zustand des Motors auf true, dann erfolgt eine Meldung.
Speichern Sie das Programm als Datei namens Motorcycle.java ab (denken Sie daran, daß Sie Ihre Java-Dateien immer wie die Klasse, die sie definieren, benennen sollten). Ihr bisheriges Programm sollte so aussehen:
class Motorcycle { String make; String color; boolean engineState; void startEngine() { if (engineState == true) System.out.println("The engine is already on."); else { engineState = true; System.out.println("The engine is now on."); } } }
Bevor Sie diese Klasse kompilieren, fügen wir noch eine Methode ein. Die showAtts-Methode gibt die aktuellen Werte der Instanzvariablen in einer Instanz der Motorcycle-Klasse aus. Das sieht so aus:
void showAtts() { System.out.println("This motorcycle is a " + color + " " + make); if (engineState == true) System.out.println("The engine is on."); else System.out.println("the engine is off."); }
Die showAtts-Methode gibt zwei Zeilen am Bildschirm aus: Das Modell (make) und die Farbe (color) des Objekts Motorrad, und ob der Motor ein oder aus ist.
Speichern Sie die Datei und kompilieren Sie sie mit javac:
javac Motorcycle.java
Was passiert nun, wenn Sie den Java-Interpreter benutzen, um diese kompilierte Klasse auszuführen? Versuchen Sie's. Java nimmt an, daß diese Klasse eine Anwendung ist und sucht nach einer main-Methode. Da es sich aber nur um eine Klasse handelt, ist keine main-Methode vorhanden. Der Java-Interpreter (java) gibt eine Fehlermeldung aus, die in etwa so aussieht:
In class Motorcycle: void main(String argv[]) is not defined
Um etwas mit der Motorcycle-Klasse anfangen zu können, z. B. Instanzen dieser Klasse erstellen und damit spielen, müssen Sie eine Java-Anwendung schreiben, die diese Klasse benutzt, oder in diese Klasse eine main-Methode einfügen. Der Einfachheit halber entscheiden wir uns für letzteres. In Listing 2.1 sehen Sie die main()-Methode, die Sie in die Motorcycle-Klasse einfügen. Was dadurch bewirkt wird, folgt in Kürze.
Mit der main()-Methode ist die Motorcycle-Klasse jetzt eine Anwendung.
Wenn Sie sie erneut kompilieren, läuft sie diesmal. Die Ausgabe
sollte so aussehen:
Sie haben einen Einblick in das Grundprinzip von Klassen, Objekten,
Methoden und Variablen bekommen. Sie haben auch gesehen, wie alles
in einem Java-Programm zusammengesetzt wird. Nun ist es an der
Zeit, wieder für etwas Verwirrung zu sorgen. Vererbung,
Schnittstellen und Pakete sind Mechanismen zum Organisieren
von Klassen und ihren Eigenschaften. Die gesamte Java-Klassenbibliothek
nutzt diese Konzepte. Auch die besten Klassenbibliotheken, die
Sie für Ihre eigenen Programme schreiben, nutzen diese Konzepte.
Jede Klasse ist einer Superklasse untergeordnet (die Klasse über
der Hierarchie). Jede Klasse kann eine oder mehr Subklassen haben
(Klassen unter einer anderen Klasse in der Hierarchie). Klassen
weiter unten in der Hierarchie erben von den übergeordneten
Klassen.
Abbildung 2.2: Klassenhierarchie
Subklassen erben alle Methoden und Variablen von ihren Superklassen.
Das bedeutet, daß Sie eine Klasse nicht neu definieren oder
den Code von einer anderen Klasse kopieren müssen, wenn die
Superklasse das Verhalten aufweist, das Sie für die neue
Klasse brauchen. Ihre Klasse erbt automatisch das Verhalten von
ihrer Superklasse. Diese Superklasse erbt ihr Verhalten wiederum
von ihrer Superklasse usw. in der Hierarchie nach oben.
Ganz oben in der Java-Klassenhierarchie befindet
sich die Klasse Object. Alle Klassen erben von dieser Superklasse.
Object ist eine allgemeine Klasse. Sie definiert Verhaltensmuster,
die alle Objekte in der Java-Klassenhierarchie erhalten.
Weiter unten in der Hierarchie haben die Klassen zusätzliche
Informationen und sind für einen spezifischeren Zweck ausgelegt.
Auf diese Weise kann man sich eine Klassenhierarchie als Definition
sehr abstrakter Konzepte vorstellen, die von oben nach unten stufenweise
konkreter werden.
In der Regel muß man eine Klasse erstellen, die alle Informationen
einer anderen Klasse sowie zusätzliche Informationen hat.
Eventuell benötigen Sie eine Version einer Schaltfläche
mit einer anderen Beschriftung. Sie können alle Button-Informationen
in einem Schritt zusammentragen, indem Sie lediglich festlegen,
daß Ihre Klasse von Button erben soll. Ihre Klasse hat dann
automatisch alle in Button definierten Eigenschaften (Button ist
damit die Superklasse der neuen Klasse), so daß Sie sich
nur noch um die Unterschiede zwischen der Superklasse und der
neuen Klasse zu kümmern brauchen. Dieser Mechanismus zur
Definition neuer Klassen, bei dem nur Unterschiede festgelegt
werden, heißt Subclassing.
Was nun, wenn Sie eine Klasse definieren müssen, die ein
völlig anderes Verhalten aufweisen muß, das in keiner
anderen Klasse vorkommt? In diesem Fall kann Ihre Klasse trotzdem
alles von Object erben und sauber in die Java-Klassenhierarchie
eingepaßt werden. Wenn Sie eine Klasse definieren, die in
der ersten Zeile auf keine Superklasse verweist, geht Java automatisch
davon aus, daß die Vererbung von Object stammt. Die Motorcycle-Klasse,
die Sie im vorherigen Abschnitt geschrieben haben, hat von Object
geerbt.
Müssen Sie eine größere Anzahl von Klassen erstellen,
ist es sinnvoll, daß die Klassen nicht nur von der vorhandenen
Klassenhierarchie erben, sondern daß die Hierarchie
erweitert wird. Das muß meist im voraus geplant werden,
um den Java-Code richtig organisieren zu können. Dieser Aufwand
bietet aber entscheidende Vorteile:
Betrachten wir als Beispiel die Motorcycle-Klasse und nehmen wir
an, Sie haben ein Java-Programm geschrieben, um alle Merkmale
eines Motorrads zu implementieren. Sie haben die Arbeit beendet,
das Programm funktioniert und alle sind zufrieden. Die nächste
Aufgabe ist nun, eine Java-Klasse namens Car (Auto) zu schreiben.
Autos und Motorräder haben viele gemeinsame Merkmale. Beide
sind Kraftfahrzeuge, die von Motoren angetrieben werden. Beide
haben Scheinwerfer und Tachometer. Im ersten Anlauf sind Sie vielleicht
geneigt, die Datei mit der Motorcycle-Klasse zu öffnen und
die brauchbaren Informationen in die neue Klasse Car zu kopieren.
Sie tun das aber nicht, weil es eine bessere Vorgehensweise gibt:
Sie fassen die gemeinsamen Informationen für Car und Motorcycle
zu einer allgemeineren Klassenhierarchie zusammen. Das hört
sich zunächst wegen nur zwei Klassen nach relativ viel Aufwand
an. Bedenken Sie aber, daß Sie dadurch irgendwann Klassen
für Fahrräder, Lastwagen usw. definieren können,
die alle gemeinsame Eigenschaften haben. Diese Eigenschaften können
Sie aus einer wiederverwendbaren Superklasse entnehmen, so daß
sich insgesamt langfristig der Arbeitsaufwand erheblich reduziert.
Wir entwerfen nun eine Klassenhierarchie, die diesem Zweck dient.
Ganz oben befindet sich die Klasse Object, die Wurzel aller Java-Klassen.
Die gröbste Klasse, zu der Motorräder und Autos
gehören können, wäre z. B. Fahrzeug. Ein
Fahrzeug wird im allgemeinen als Gegenstand definiert, der jemanden
mit oder ohne körperliche Einwirkung von einer Stelle zu
einer anderen befördert. Sie definieren also die Klasse Vehicle
(Fahrzeug) lediglich mit den Eigenschaften, die ermöglichen,
daß der Gegenstand jemanden von A nach B befördert,
sonst nichts.
Unterhalb von Vehicle? Wie wär's mit diesen Klassen: PersonPoweredVehicle
und EnginePoweredVehicle? Damit definieren Sie zwei der Klasse
Vehicle untergeordnete Klassen, wobei eine Fahrzeuge betrifft,
die durch die körperliche Kraft des Menschen und eine, die
durch die Kraft von Motoren betrieben werden. EnginePoweredVehicle
unterscheidet sich von PersonPoweredVehicle dadurch, daß
es einen Motor, Eigenschaften wie Anlassen und Ausschalten gibt,
Benzin verbraucht wird und verschiedene Geschwindigkeiten möglich
sind. Von Menschen angetriebene Fahrzeuge haben andere Mechanismen,
um jemanden von A nach B zu befördern, z. B. Pedale. Diese
Hierarchie ist in Abb. 2.3 dargestellt.
Abbildung 2.3: Die Basishierarchie Vehicle
Um spezifischer zu werden, können Sie für EnginePoweredVehicle
mehrere Klassen für Motorräder, Autos, Lastwagen usw.
anlegen. Sie können aber weitere Verhaltenmuster zu einer
anderen Superklasse zusammenfassen, so daß Zwischenklassen
für zwei- und vierrädrige Fahrzeuge entstehen (siehe
Abb. 2.4).
Mit einer Subklasse für die zweirädrigen Kraftfahrzeuge
können Sie schließlich eine Klasse für Motorräder
definieren. Alternativ können Sie zusätzlich Kleinkrafträder
und Mopeds definieren. Beide sind zweirädrige Kraftfahrzeuge,
unterscheiden sich aber von Motorrädern.
Wo stehen nun Qualitäten wie Modell oder Farbe? Wo Sie wollen;
normalerweise an der Stelle, an der sie innerhalb der Klassenhierarchie
am besten passen und am sinnvollsten angeordnet sind. Sie können
Modell und Farbe in Vehicle definieren, dann erben alle Subklassen
von Vehicle auch diese Variablen. Wichtig ist dabei, sich vor
Augen zu halten, daß ein Merkmal oder Verhalten nur einmal
in der Hierarchie definiert werden muß und automatisch in
jeder Subklasse wiederverwendet wird.
Abbildung 2.4: Klassen für zwei- und vierrädrige Kraftfahrzeuge
Wie kann es sein, daß Instanzen einer Klasse automatisch
Variablen und Methoden von übergeordneten Klassen innerhalb
der Hierarchie erben?
Erstellen Sie eine neue Instanz einer Klasse, erhalten Sie einen
«Ausschnitt» jeder Variablen, die in der aktuellen Klasse
definiert ist und für jede Variable, die in allen ihren Superklassen
definiert ist. Auf diese Weise bilden alle Klassen zusammen eine
Maske für das aktuelle Objekt. Jedes Objekt übernimmt
dann die jeweils brauchbaren Informationen.
Methoden funktionieren auf ähnliche Weise: Neue Objekt haben
Zugang zu allen Methodennamen ihrer Klasse und deren Superklassen.
Jedoch werden Methodendefinitionen dynamisch beim Aufrufen einer
Methode gewählt. Das bedeutet, daß Java beim Aufrufen
einer Methode für ein bestimmtes Objekt zuerst die Klasse
des Objekts auf Definition der betreffenden Methode prüft.
Ist sie nicht in der Klasse des Objekts definiert, sucht Java
in der Superklasse dieser Klasse usw. aufwärts in der Hierarchie,
bis die Definition der Methode gefunden wird (siehe Abb. 2.5).
Die Dinge sind etwas komplizierter, wenn eine Subklasse eine Methode
definiert, die die gleiche Unterschrift (Name, Anzahl und Typ
der Argumente) wie eine in einer Superklasse definierte Methode
hat. In diesem Fall wird die Methodendefinition, die zuerst (von
unten nach oben in der Hierarchie) gefunden wird, ausgeführt.
Aus diesem Grund kann es sinnvoll sein, eine Methode in einer
Subklasse zu definieren, die die gleiche Unterschrift hat wie
eine Methode in der Superklasse, so daß die Methode der
Superklasse «verborgen» wird. Diesen Vorgang nennt man
Overriding bzw. Überschreiben einer Methode.
Sie lernen am 7. Tag alles über Methoden.
Abbildung 2.5: Vorgang des Auffindens von Methoden
Javas Vererbungssystem basiert auf der Einzelvererbung.
Das bedeutet, daß jede Java-Klasse nur eine Superklasse
haben kann (während jede Superklasse mehrere Subklassen haben
kann).
In anderen objektorientierten Programmiersprachen, z. B. C++ und
Smalltalk, können Klassen mehr als eine Superklasse haben
und verschiedene Variablen und Methoden aus mehreren Klassen erben.
Das ist die Mehrfachvererbung. Dieses System ist
dahingehend leistungsstark, daß man Klassen erstellen kann,
die alle nur erdenklichen Verhaltensmuster auf sich vereinen.
Andererseits wird die Klassendefinition dadurch wesentlich komplizierter
und der daraus entstehende Code ist verhältnismäßig
komplex.
Abbildung 2.6: Klassen für zwei- und vierrädrige Kraftfahrzeuge
Java hat zwei weitere Konzepte, die hier diskutiert werden: Pakete
und Schnittstellen. Beides sind wichtige Themen zur Implementierung
und zum Design von Klassengruppen und Klassenverhalten. Sie lernen
über Schnittstellen und Pakete am 16. Tag. Hier folgt eine
kurze Einführung.
Sie erinnern sich, daß Java-Klassen nur eine Superklasse
haben und Variablen und Methoden von dieser Superklasse und allen
Superklassen der Superklasse erben. Die Einfachvererbung vereinfacht
zwar die Beziehung zwischen Klassen und die Funktionalität
der Klassen, kann aber einschränkende Wirkungen haben, insbesondere,
wenn ein bestimmtes Verhalten auf mehrere «Zweige» der
Klassenhierarchie dupliziert werden muß. Java löst
dieses Problem durch Anwendung des Konzeptes von Schnittstellen.
Obwohl eine einzelne Java-Klasse (aufgrund der Einfachvererbung)
nur eine Superklasse haben kann, können in einer Klasse mehrere
Schnittstellen implementiert werden. Durch Implementieren einer
Schnittstelle bietet eine Klasse Methodenimplementierungen (Definitionen)
für die durch die Schnittstelle definierten Methodennamen.
Implementieren zwei verschiedene Klassen die gleiche Schnittstelle,
können beide auf die gleichen Methodenaufrufe (die durch
diese Schnittstelle definiert werden) reagieren, obwohl die Reaktion
der jeweiligen Klasse unterschiedlich sein kann.
Sie müssen an diesem Punkt keine Einzelheiten über Schnittstellen
lernen. Dieser Lehrstoff folgt später; er würde hier
nur unnötig verwirren.
Das letzte heute diskutierte Java-Konzept sind Pakete.
Sie lernen in Woche 3 alles über Pakete, unter anderem auch,
wie sie erstellt und verwendet werden. Vorläufig genügt
es, folgende Punkte zu verstehen:
Die Klassenbibliothek des Java Developer's Kits befindet sich
in einem Paket namens java. Die im java-Paket enthaltenen Klassen
sind garantiert in jeder Java-Implementierung verfügbar.
Das sind die einzigen Klassen, deren Verfügbarkeit in allen
Implementierungen garantiert ist. Das java-Paket selbst enthält
andere Pakete für Klassen, die die Sprache an sich sowie
die Ein- und Ausgabeklassen, einige Vernetzungsfunktionen und
die Fensterfunktionen beinhalten. Klassen in anderen Paketen (z.
B. Klassen in den Sun- oder Netscape-Paketen) sind eventuell nur
in bestimmten Implementierungen verfügbar.
Standardmäßig haben Java-Klassen Zugang zu den in java.lang
befindlichen Klassen (dem im java-Paket enthaltenen Sprachpaket).
Um Klassen eines anderen Pakets benutzen zu können, müssen
Sie sie entweder explizit nach Paketnamen einbeziehen oder in
Ihre Quelldatei importieren.
Um auf eine Klasse eines Pakets zu verweisen, listen Sie alle
Pakete, in der sich die Klasse befindet, und den Klassennamen
durch Punkte (.) getrennt auf. Um beispielsweise die Color-Klasse
zu nehmen, die sich im awt-Paket («awt»
ist die Abkürzung von «Abstract Windowing Toolkit»),
das sich im java-Paket befindet, setzen Sie in Ihr Programm eine
Referenz auf die Color-Klasse im Format java.awt.Color.
Zum Schluß der heutigen Lektion erstellen wir eine Klasse,
die eine Subklasse einer anderen Klasse ist, und wenden einige
Overriding-Methoden an. Außerdem erhalten
Sie in diesem Beispiel einen ersten Eindruck darüber, wie
Pakete funktionieren.
Der häufigste Grund, eine Subklasse zu erstellen, ist das
Schreiben eines Applets, zumindest beim Beginn der Java-Programmierung.
Alle Applets sind Subklassen der Klasse Applet (die Teil des java.applet-Pakets
ist). Durch Erstellen einer Subklasse von Applet erhalten Sie
automatisch alle Eigenschaften aus dem Window-Toolkit und den
Layoutklassen. Dadurch kann Ihr Applet an der richtigen Stelle
auf der Seite ausgegeben werden und mit Systemoperationen, z.
B. Tastenanschlägen und Mausklicks, interagieren.
In diesem Beispiel erstellen Sie ein Applet, das dem HelloWorld-Applet
von gestern ähnlich ist, jedoch die Hello-Zeichenkette in
einer größeren Schrift und in einer anderen Farbe ausgibt.
Zu Beginn dieses Beispiels erstellen wir zunächst die Klassendefinition.
Erinnern Sie sich an die HTML- und Klassenverzeichnisse von gestern?
Diese verwenden Sie wieder. Öffnen Sie Ihren Texteditor und
geben Sie folgende Klassendefinition ein:
Hier erstellen Sie die Klasse HelloAgainApplet. Beachten Sie,
daß der Teil extends java.applet.Applet bestimmt, daß
Ihre Applet-Klasse eine Subklasse der Applet-Klasse ist.
Die Applet-Klasse ist automatisch im java.applet-Paket enthalten,
deshalb brauchen Sie nicht auf diese Klasse zugreifen. Statt dessen
beziehen Sie sich explizit auf das Paket und den Klassennamen.
Der andere Teil dieser Klassendefinition ist das Schlüsselwort
public. Das bedeutet, daß Ihre Klasse nach dem Laden für
das gesamte Java-System verfügbar ist. Normalerweise muß
eine Klasse nur public sein, wenn sie für alle anderen Klassen
Ihres Java-Programms sichtbar sein soll. Applets müssen public
deklariert werden. Sie lernen in Woche 3 mehr über public-Klassen.
Eine Klassendefinition ohne Inhalt ist nicht sinnvoll. Ohne Hinzufügen
oder Überschreiben von Variablen oder Methoden der Superklasse
ist das Erstellen einer Subklasse sinnlos. Deshalb fügen
wir in diese Klasse einige Informationen ein, um sie von ihrer
Superklasse zu unterscheiden.
Zuerst geben wir eine Instanzvariable für ein Font-Objekt
ein:
Die Instanzvariable f enthält jetzt eine neue Instanz der
Klasse Font, die Teil des java.awt-Pakets ist. Dieses Font-Objekt
ist die Schriftart Times Roman, Fett, 36 Punkt. Im vorherigen
HelloWorld-Applet wurde für den Text der Standardfont benutzt:
Times Roman 12 Punkt. Sie können die Schrift eines Font-Objekts
für Text in Ihrem Applet jederzeit ändern.
Durch Erstellen einer Instanzvariablen, die dieses Font-Objekt
enthält, stellen Sie es allen Methoden der Klasse zur Verfügung.
Nun erstellen wir eine Methode, die das Objekt nutzt.
Für Applets gibt es mehrere «Standardmethoden»,
die in Applet-Superklassen definiert sind, mit denen eine Applet-Klasse
überschrieben wird (Overriding). Dazu zählen Methoden
zum Initialisieren des Applets, zum Handhaben von Operationen,
wie Mausbewegungen oder Mausklicks, oder zum «Aufräumen»,
wenn das Applet beendet wird. Eine dieser Standardmethoden ist
paint(), durch die das Applet am Bildschirm angezeigt wird. Die
Standarddefinition von paint() macht überhaupt nichts - sie
ist eine leere Methode. Durch Überschreiben von paint() weisen
Sie das Applet an, was am Bildschirm auszugeben ist. Hier eine
Definition von paint():
Diese Methode muß wie das Applet selbst public deklariert
werden. Das Überschreiben der paint()-Methode ist ebenfalls
public. Versuchen Sie, eine Methode in Ihrer Klasse zu überschreiben,
die in einer Superklasse public ist, erhalten Sie einen Kompilierfehler.
Das heißt, daß diese Methode immer public sein muß.
Innerhalb der paint()-Methode haben Sie im obigen Beispiel drei
Dinge erreicht:
Sie haben das Grafikobjekt angewiesen, daß die Standardschrift
in der Instanzvariablen f enthalten ist.
Sie haben das Grafikobjekt angewiesen, daß die Standardfarbe
eine Instanz der Color-Farbe für die Farbe Rot ist.
Sie haben die Ausgabe Ihrer Zeichenkette "Hello again!"
am Bildschirm in x- und y-Position 5 und 25 angegeben. Die Zeichenkette
wird in der Standardschrift und -farbe angezeigt.
Für ein derart einfaches Applet genügt das. Ihr Applet
sieht bisher so aus:
Vielleicht haben Sie bemerkt, daß mit diesem Beispiel etwas
nicht stimmt. Wenn Sie nicht genau wissen, was, versuchen Sie,
diese Datei abzuspeichern (unter dem gleichen Namen wie die Klasse,
also HelloAgainApplet.java) und sie mit dem Java-Compiler zu kompilieren.
Sie erhalten mehrere Fehlermeldungen, z. B. diese:
Warum erscheinen diese Fehlermeldungen? Weil die Klassen, auf
die Sie verweisen, Teil eines Pakets sind. Wie Sie wissen, ist
java.lang das einzige Paket, auf das automatisch zugegriffen wird.
Sie haben in der ersten Zeile der Klassendefinition durch Angabe
des vollen Paketnamens (java.applet.Applet) auf die Applet-Klasse
Bezug genommen. Weiter unten im Programm haben Sie jedoch auf
alle Arten anderer Klassen so Bezug genommen, als wären sie
bereits verfügbar.
Dieses Problem kann auf zwei Arten gelöst werden: Beziehen
Sie sich auf alle externen Klassen mit dem vollen Paketnamen oder
importieren Sie die betreffende Klasse bzw. das Paket in Ihre
Klassendatei an den Dateianfang. Welche Lösung Sie wählen,
steht Ihnen frei. Falls Sie auf eine Klasse, die in einem anderen
Paket enthalten ist, häufig verweisen, ist das Importieren
weniger zeitaufwendig als die manuelle Eingabe.
In diesem Beispiel importieren wir die benötigten Klassen.
Insgesamt sind das drei: Graphics, Font und Color. Alle drei befinden
sich im Paket java.awt. Nachfolgend die Zeilen zum Importieren
dieser Klassen. Fügen Sie sie an den Anfang vor der eigentlichen
Klassendefinition ein:
Jetzt sollte sich HelloAgainApplet richtig in eine Klassendatei
kompilieren lassen. Um das zu prüfen, erstellen Sie eine
HTML-Datei mit dem Tag <APPLET> genau wie gestern. Hier
die zu verwendende HTML-Datei:
Für dieses HTML-Beispiel ist Ihre Java-Klassendatei im gleichen
Verzeichnis wie die HTML-Datei. Speichern Sie die Datei unter
HelloAgainApplet.html und starten Sie Ihren Java-Browser oder
den Java-Appletviewer. Abb. 2.7 zeigt das Ergebnis, das Sie nun
erhalten sollten (die Zeichenkette Hello Again ist Rot):
Abbildung 2.7: Das HelloAgainApplet
Wenn das Ihre erste Begegnung mit objektorientierter Programmierung
war, erscheint Ihnen eine Menge in diesem Kapitel theoretisch
und kompliziert. Keine Sorge - je mehr Java-Anwendungen Sie im
weiteren Verlauf dieses Buches schreiben, um so besser werden
Sie das alles verstehen.
Eine der größten Hürden der objektorientierten
Programmierung sind nicht unbedingt die Konzepte, sondern die
Namen. In der objektorientierten Programmierung werden viele Fachbegriffe
verwendet. Insgesamt haben Sie heute folgende Fachbegriffe gelernt:
F: Methoden sind effektiv Funktionen, die innerhalb von Klassen
definiert werden. Warum nennt man sie nicht Funktionen, obwohl
sie wie Funktionen aussehen und sich so verhalten?
A: In einigen objektorientierten Programmiersprachen werden
sie Funktionen (bzw. in C++ Member-Funktionen) genannt. Andere
objektorientierte Sprachen unterscheiden zwischen Funktionen innerhalb
und außerhalb einer Klasse oder eines Objekts. Getrennte
Begriffe sind wichtig, um die Funktionsweise zu verdeutlichen.
Da der Unterschied in anderen Sprachen relevant ist und der Begriff
«Methode» jetzt in der objektorientierten Technologie
üblich ist, wird er auch in Java verwendet.
F: Ich verstehe Instanzvariablen und -methoden, aber nicht Klassenvariablen
und -methoden.
A: Fast alles, was man in einem Java-Programm hat, sind
Objekte. Einige Eigenschaften und Attribute sind jedoch sinnvoller,
wenn sie in der Klasse, nicht im Objekt gespeichert werden. Um
eine neue Instanz einer Klasse zu erstellen, brauchen Sie z. B.
eine Methode, die für die Klasse, nicht für das Objekt
definiert ist. (Andernfalls können Sie keine Instanz der
Klasse erstellen. Sie brauchen ein Objekt, um die neue Methode
aufzurufen, haben aber noch kein Objekt.) Demgegenüber werden
Klassenvariablen häufig benutzt, wenn ein Attribut bzw. dessen
Wert auch in der Instanz einer Klasse benutzt werden soll.
Vorwiegend werden Instanzvariablen und -methoden verwendet. Sie
lernen hierüber noch in dieser Woche mehr.
1. public static void main (String args[]) {
2: Motorcycle m = new Motorcycle();
3: m.make = "Yamaha RZ350";
4: m.color = "yellow";
5: System.out.println("Calling showAtts...");
6: m.showAtts();
7: System.out.println(".......");
8: System.out.println("Starting engine...");
9: m.startEngine();
10: System.out.println(".......");
11: System.out.println("Calling showAtts...");
12: m.showAtts();
13: System.out.println(".......");
14: System.out.println("Starting engine...");
15: m.startEngine();
16: }
Calling showAtts...
This motorcycle is a yellow Yamaha RZ350
The engine is off.
........
Starting engine...
The engine is now on.
........
Calling showAtts...
This motorcycle is a yellow Yamaha RZ350
The engine is on.
........
Starting engine...
The engine is already on.
Vererbung, Schnittstellen und Pakete
Vererbung
Erstellen einer Klassenhierarchie
Wie Vererbung funktioniert
Einzel- und Mehrfachvererbung
Schnittstellen und Pakete
Erstellen einer Subklasse
public class HelloAgainApplet extends java.applet.Applet {
}
Font f = new Font("TimesRoman", Font.BOLD,36);
public void paint(Graphics g) {
g.setFont(f);
g.setColor(Color.red);
g.drawString("Hello again!", 5, 25);
}
public class HelloAgainApplet extends java.applet.Applet {
Font f = new Font("TimesRoman",Font.BOLD,36);
public void paint(Graphics g) {
g.setFont(f);
g.setColor(Color.red);
g.drawString("Hello again!", 5, 25);
}
}
HelloAgainApplet.java:7: Class Graphics not found in type declaration.
import java.awt.Graphics;
import java.awt.Font;
import java.awt.Color;
import java.awt.*;
<HTML>
<HEAD>
<TITLE>Another Applet</TITLE>
</HEAD>
<BODY>
<P>My second Java Applet says:<BR>
<APPLET CODE="HelloAgainApplet.class" WIDTH=200 HEIGHT=75>
</APPLET>
</BODY>
</HTML>
Zusammenfassung
Fragen und Antworten
Copyright ©1996 Markt&Technik
Buch- und Software- Verlag GmbH
Alle Rechte vorbehalten. All rights reserved.