MarktundTechnik Home-Page

Previous Page TOC Index Next Page See Page


2. Tag:

Objektorientierte Programmierung und Java

von Laura Lemay

Objektorientierte Programmierung (OOP) ist einer der Begriffe, der in den letzten Jahren in der Programmierwelt große Verwirrung stiftet. Man könnte Jahre damit verbringen, alles über die Methoden der objektorientierten Programmierung und ihre Möglichkeiten, die «altmodische» Programmierung zu vereinfachen, zu lernen. Im Prinzip handelt es sich um das Organisieren von Programmen auf eine Art, die Dinge wie in der wirklichen Welt zusammenstellt.

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.

In Objekten denken: eine Analogie

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.

Objekte und Klassen

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.

Eine Klasse ist eine Sammlung aller Eigenschaften und Methoden der Objekte dieser Klasse und legt fest, wie diese zu erzeugen sind.

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).

Eine Instanz einer Klasse ist ein anderes Wort für ein tatsächliches Objekt. Während Klassen eine abstrakte Darstellung eines Objekts sind, ist eine Instanz dessen konkrete Darstellung.

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

Falls Sie aus der C-Welt kommen, können Sie sich eine Klasse als einen neuen, mittels struct und typedef zusammengesetzten Datentyp vorstellen. Klassen bieten aber mehr als nur eine Sammlung von Daten, wie sie im weiteren Verlauf dieser Lektion noch feststellen werden.

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.

Eine Klassenbibliothek ist eine Sammlung von Klassen.

Eigenschaften und Attribute

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

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.

Instanzvariablen definieren die Attribute eines Objekts. Die Klasse definiert die Art des Attributs, und jede Instanz speichert einen eigenen Wert für das jeweilige Attribut.

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.

Eigenschaften

Die Eigenschaften einer Klasse bestimmen, was die Instanzen der Klasse tun, wenn sich ihr interner Zustand ändert oder wenn die Instanz von einer anderen Klasse oder einem Objekt aufgefordert wird, etwas zu tun. Die Eigenschaften bestimmen, was Objekte alles machen können und was man mit Objekten machen kann. Um die theoretische Klasse Motorcycle wieder aufzugreifen, gibt es einige Eigenschaften, die diese Klasse haben könnte:

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 sind Funktionen, die innerhalb von Klassen definiert werden und auf Klasseninstanzen angewandt werden.

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.

Erstellen einer Klasse

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.

Ich gehe bei diesem Beispiel nicht in's Detail über die Syntax. Machen Sie sich keine Sorgen, falls Sie nicht alles verstehen. Später in dieser Woche erhalten Sie mehr Klarheit. Wichtig ist bei diesem Beispiel nur, daß Sie die grundlegenden Teile dieser Klassendefinition verstehen.

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.

In Java ist ein boolescher Ausdruck ein echter Datentyp, der den Wert true (wahr) oder false (falsch) haben kann. Im Gegensatz zu C sind boolesche Ausdrücke hier keine Zahlen. Sie erfahren morgen mehr darüber.

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.");

      }

   }

}



Die Einrückungen sind nicht Teil des Programms und haben für den Java-Compiler keine Bedeutung. Sie sichern lediglich eine bessere Übersichtlichkeit. In diesem Buch werden Instanzvariablen und Methoden unter der Klassendefinition eingerückt. In den Java-Klassenbibliotheken wird diese Einrückungsart ebenfalls angewandt. Sie können aber eine andere Einrückungsart anwenden.

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



Ab diesem Punkt gehe ich davon aus, daß Sie wissen, wie man Java-Programme kompiliert und ausführt, so daß hierzu keine Anleitungen mehr folgen.

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.

Listing 2.1: main()-Methode für Motorcycle.java


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:   }



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:


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.



Der Inhalt der main()-Methode sieht für Sie ganz neu aus, deshalb gehen wir ihn Zeile für Zeile durch, damit Sie verstehen, was passiert (Einzelheiten hierüber morgen und übermorgen).

Vererbung, Schnittstellen und Pakete

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.

Vererbung

Eines der wichtigsten Konzepte in der objektorientierten Programmierung ist die Vererbung. Sie hat keinen direkten Einfluß darauf, wie Java-Klassen entworfen und geschrieben werden. Vererbung ist ein starker Mechanismus, der bewirkt, daß Sie, um eine Klasse zu schreiben, nur bestimmen müssen, wie sich die Klasse von einer anderen Klasse unterscheidet. Gleichzeitig haben Sie dynamischen Zugang zu den Informationen dieser anderen Klasse.

Vererbung ist ein Mechanismus in der objektorientierten Programmierung, der bewirkt, daß alle Klassen in einer strikten Hierarchie angeordnet sind und etwas von ihren übergeordneten Klassen erben (siehe Abb. 2.2).

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.

Subclassing bedeutet das Erstellen einer neuen Klasse - einer sogenannten Subklasse, die von einer anderen Klasse in der Klassenhierarchie erbt. Anhand dieser Methode müssen nur die Unterschiede zwischen der neuen Klasse und ihrer Superklasse definiert werden. Alles übrige erhält die Klasse durch Vererbung.

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.

Erstellen einer Klassenhierarchie

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 Vererbung funktioniert

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

Overriding ist ein Vorgang, bei dem eine Methode in einer Subklasse erstellt wird, die die gleiche Unterschrift (Name, Zahl und Typ der Argumente) hat wie eine Methode in einer Superklasse. Durch diese neue Methode wird die Methode der Superklasse überschrieben (siehe Abb. 2.6).

Einzel- und Mehrfachvererbung

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

Schnittstellen und Pakete

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.

Eine Schnittstelle ist in der Java-Sprache eine Sammlung von Methodennamen ohne Definitionen, die andeuten, daß eine Klasse außer dem von der Superklasse geerbten Verhalten zusätzliche Eigenschaften hat.

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.

In Java ist ein Paket die Weise, wie zusammenhängende Klassen und Schnittstellen gruppiert werden. Pakete ermöglichen, daß modulare Klassengruppen nur verfügbar sind, wenn sie gebraucht werden, so daß potentielle Konflikte zwischen Klassennamen in unterschiedlichen Klassengruppen vermieden werden.

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.

Erstellen einer Subklasse

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:


public class HelloAgainApplet extends java.applet.Applet   {

}



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:


Font f = new Font("TimesRoman", Font.BOLD,36);



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():


public void paint(Graphics g)   {

   g.setFont(f);

   g.setColor(Color.red);

   g.drawString("Hello again!", 5, 25);

}



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ß.

Die paint()-Methode hat nur ein Argument: eine Instanz der Graphics-Klasse. Die Graphics-Klasse bietet ein plattformunabhängiges Verhalten für Fonts, Farben und einfache Zeichnungen. Sie lernen in Woche 2 mehr über die Graphics-Klasse, wenn Sie anspruchsvollere Applets erstellen.

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:


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);

   }

}



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:


HelloAgainApplet.java:7: Class Graphics not found in type declaration.



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:


import java.awt.Graphics;

import java.awt.Font;

import java.awt.Color;



Sie können auch ein ganzes Paket von (public) Klassen importieren, indem Sie anstelle eines spezifischen Klassennamens ein Sternchen (*) tippen. Um beispielsweise alle Klassen des awt-Pakets zu importieren, können Sie folgendes eingeben:


   import java.awt.*;



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:


<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>



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

Zusammenfassung

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:


Fragen und Antworten

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.


Copyright ©1996 Markt&Technik
Buch- und Software- Verlag GmbH
Alle Rechte vorbehalten. All rights reserved.

Schreiben Sie uns!

Previous Page TOC Index Next Page See Page