In diesem Kapitel werden die Grundlagen der Objektorientierten Programmierung beschrieben und ein Einstieg in Java geliefert. Fortgeschrittene LeserInnen, die schon Erfahrung in der Java-Programmierung haben und durch die Lektüre dieses Buches lediglich den Umgang mit leJOS und dem EV3 erlernen wollen, können wie folgt vorgehen:
Sowohl der EV3 als auch der verwendete Computer sind an dieser Stelle vollständig eingerichtet und es können die EV3-Beispielprogramme aus Kapitel 7, Kapitel 8, Kapitel 10 sowie aus Kapitel 11 übertragen und ausgeführt werden. Das Nachvollziehen dieser Beispielprogramme und die Beschreibung der verfügbaren leJOS-Klassen in Kapitel 10 liefern eine gute Grundlage um mit der Programmierung eigener Roboter zu beginnen.
Mit dem Begriff »Objektorientierung« wird eine Art der Modellbildung in der Softwareentwicklung beschrieben. Unter dieser ist, sehr vereinfacht ausgedrückt, die Abstraktion der realen Welt und der Transfer dieses abstrahierten Modells auf eine Maschine zu verstehen. Es ist dennoch weiterhin möglich, prozedural (Aufteilung von Aufgaben und Teilproblemen in Prozeduren) zu programmieren, wie es zum Beispiel in C gemacht wird. Das Konzept der Objektorientierung (kurz: OO) bietet viele Vorteile, die die Entwicklung von Software von Grund auf verändert und erleichtert. Dabei wird versucht auf menschliche Strukturierungs- und Klassifizierungsmethoden zurückzugreifen.
Der Objektorientierung liegt eine Aufteilung der zu beschreibenden Welt in Objekte mit ihren Eigenschaften und Operationen zugrunde. Realisiert wird diese Aufteilung durch das Konzept der Klasse, bei dem Objekte aufgrund ähnlicher Eigenschaften zusammengefasst und nach außen gekapselt werden. Die Struktur eines Objekts wird durch die Attribute (auch Eigenschaften) seiner Klassendefinition festgelegt. Das Verhalten des Objekts wird von den Methoden der Klasse bestimmt.
Ein Vorteil des auf der Objektorientierung beruhenden Programmierparadigmas, der Objektorientierten Programmierung (kurz: OOP), stellt die Möglichkeit dar, Programmteile wiederzuverwenden. Konsistent entwickelte Programmteile können in verschiedenen Konfigurationen und verschiedenen logischen Zusammenhängen genutzt werden. Ein weiterer Vorteil ist, dass das zu erzeugende Programm in übersichtliche Programmstücke geteilt werden kann. Deren Erstellung und Wartung lassen sich dadurch besser und transparenter strukturieren.
Die unterschiedlichen Programmteile sind somit unabhängig voneinander und können einzeln und von verschiedenen Personen entwickelt werden. Um von den beschriebenen Vorteilen zu profitieren, werden die vier folgenden Konzepte genutzt:
Diese vier Konzepte werden in Kapitel 7 beschrieben.
Bevor auf diese Konzepte eingegangen werden kann, werden die grundsätzlichen Strukturen der objektorientierten Programmierung vorgestellt.
Wie eingangs erwähnt, lehnt sich das Konzept der OOP stark an Strukturierungs- und Klassifizierungsmethoden aus der alltäglichen (menschlichen) Betrachtungsweise unserer realen Welt an. Das folgenden Beispiel soll dies verdeutlichen:
Der Begriff PKW stellt eine Klassifizierung von motorisierten Vehikeln dar, die (meist vierrädrig) zur Personenbeförderung von A nach B dienen. Der Begriff legt dabei aber weder die Anzahl der Türen, noch Farbe, Form, Hubraum etc. des PKWs fest.
Ein ähnliches, auf die Thematik dieses Buches bezogenes Beispiel bietet der Begriff Roboter:
Als Roboter wird im herkömmlichen Sinn eine technische Apparatur bezeichnet, die dazu konzipiert wurde, Aufgaben des Menschen zu übernehmen, um diesen zu entlasten. Dabei können viele verschiedene Arten beispielsweise im Aussehen unterschieden werden: humanoider Roboter, Industrieroboter, mobiler Roboter etc. Auch weitere Eigenschaften wie z.B. Einsatzgebiet, Kosten, Aufgabe oder die Achsenanzahl sind nicht definiert.
PKW
und Roboter
angelegt, um
festzulegen welche Attribute ein später erzeugtes Objekt der Klasse
besitzen soll. Das Objekt stellt dann mit konkreten Attributwerten
etwas tatsächlich Existierendes dar, z.B. den gelben VW Käfer
von Herrn Müller oder den Industrieroboter der Automobilfirma
Ford.
Folgendes Beispiel zeigt, wie in Java die Klasse ExampleRobot
definiert
wird.
Durch das Schlüsselwort class
wird die Klassendefinition begonnen.
In den geschweiften Klammern können beliebig viele Variablen
und Methodendefinitionen erfolgen. Die Variablen (auch Attribute
einer Klasse genannt) speichern die Eigenschaften der Klasse – hier
Bezeichnung (name
) , Kosten (price
), Kaufjahr (yearOfPurchase
) und
Aussehen (appearance
). Die Methoden definieren die Fähigkeiten bzw.
das Verhalten von Objekten. Im Beispiel kann über die Methode
age(currentYear)
das Alter des entsprechenden Roboters zurückgegeben
werden. Methoden werden innerhalb von Klassen angelegt und
können auf die Klassen-Attribute zugreifen. Der sogenannte Modifier
public
und weitere werden unter dem Punkt Kapselung in Kapitel 7
erklärt.
Ein Objekt (oder auch die Instanz1) ist eine konkrete Ausprägung einer Klasse. Wird ein Objekt von einer Klasse erzeugt, so erhält es alle Attribute und Methoden, die in dieser Klasse enthalten sind. Des Weiteren haben alle Objekte drei wichtige Eigenschaften:
Die aktuellen Werte der Variablen eines Objekts bilden seinen Zustand. Dieser kann sich im Verlauf eines Programms durchaus verändern. Das Verhalten eines Objekts, wird durch seine Methoden gebildet. Sie definieren unter anderem, wie das Objekt seinen oder den Zustand anderer Objekte verändern kann. Darüber hinaus besitzt jedes Objekt intern eine eigene, unveränderliche Identität. Auch wenn zwei Objekte einer Klasse den gleichen Zustand haben und gleiches Verhalten zeigen, sind es dennoch unterschiedliche Objekte mit unterschiedlichen Identitäten.
Für das Erzeugen eines Objekts muss eine Variable vom Typ der Klasse
(hier ExampleRobot
) deklariert werden. Mit dem =
-Operator wird
dieser Variable dann ein neu erzeugtes Objekt zugewiesen. Die
Erzeugung des Objekts erfolgt mit Hilfe des new
-Operators, gefolgt vom
Klassennamen mit zwei runden Klammern (also ExampleRobot()
). Dieser
Ausdruck wird Default-Konstruktor genannt und in Abschnitt 6.3
beschrieben.
Die Erzeugung des Objekts erfolgt hier in der sogenannten main
-Methode.
Diese stellt das Hauptprogramm in einer Klassendatei dar und wird
ebenfalls in Abschnitt 6.3 beschrieben.
ExampleRobot myEV3 = new ExampleRobot();
ExampleRobot
.
Dieser konkrete Roboter (myEV3
) hat die allgemeinen Attribute der
Klasse: Bezeichnung (name
), Kosten (price
), Kaufjahr (yearOfPurchase
)
und Aussehen (appearance
). Aber er besitzt noch keine Ausprägungen,
also einen undefinierten Zustand. Im Beispielprogramm 6.3 wird nun
folgender Zustand hergestellt:
Dies geschieht durch Verwendung des .
-Operators, mit dessen Hilfe
auf Variablen und Methoden eines Objekts zugegriffen werden
kann.
Jedes Programm benötigt eine grundlegende Struktur. Genau wie in anderen Programmiersprachen ist diese Struktur fest vorgegeben und muss unbedingt beachtet werden. Dieses Kapitel soll einen kurzen Überblick über die verschiedenen Elemente geben, die in einer Klasse vorkommen müssen.
Die Programmstruktur eines leJOS-Programms gestaltet sich recht
einfach. Es sollte darauf geachtet werden, einige Vorgaben und
Konventionen einzuhalten. Es ist üblich in jeder Datei eine öffentliche
Klasse (die mit Schlüsselwort public
deklariert wird) zu erstellen. In
der Datei können sich dann beliebig viele weitere Helferklassen
befinden, die aber jeweils nicht öffentlich sind. Der Dateiname
entspricht dem Klassennamen der öffentlichen Klasse. Die Klasse
ExampleRobot
wird also auch in der Datei ExampleRobot.java
abgespeichert.
Jedes Java-Programm nutzt bestimmte Bibliotheken. Darin sind die Befehle beschrieben, die bei der Programmierung eingesetzt werden sollen. Bei der Programmierung mit leJOS müssen die Klassen für Sensoren und Motoren etc. aus den jeweiligen Packages eingebunden werden, damit der Programmcode von Java verstanden und richtig umgesetzt werden kann. Möchte man die Klasse KLASSE aus dem Package PACKAGE einbinden, so funktioniert dies nach folgendem Grundprinzip:
Soll beispielsweise die Klasse »EV3ColorSensor« aus dem Package »lejos.hardware.sensor« zur Ansteuerung des EV3-Farbsensors verwendet werden, so lautet der Befehl dafür:
Möchte man direkt mehrere Klassen aus einem Package einbinden, zum
Beispiel die leJOS-Klassen zur Ansteuerung aller Sensoren, wird der
*
- Operator verwendet:
Eclipse unterstützt die EntwicklerInnen beim Einbinden der Klassen
durch einen Hinweis, falls Klassen verwendet werden sollen, die noch
nicht eingebunden sind. Durch einen einfachen Linksklick auf den
entsprechenden Hinweis fügt Eclipse die benötige import
-Zeile
automatisch zum Programmcode hinzu.
Nach den Paketimporten wird die eigentliche Klasse definiert. Mit den
Schlüsselwörtern public class <Klassenname>
wird eine neue
öffentliche Klasse angelegt. In unserem Beispiel ist dies:
Danach beginnt der Klassenrumpf, der von geschweiften Klammern umschlossen wird (siehe zum Beispiel Programm 6.3).
Ist das Grundgerüst geschaffen, bedarf es nun noch der Hauptfunktion die
den Namen main
trägt. Auch diese Methode ist vom Typ public
und da
sie eine Klassenmethode ist, bekommt sie noch das Schlüsselwort static
.
In unserem Beispiel ist dies:
Der main
-Funktion muss immer ein Stringarray (also eine Feldvariable
vom Typ String) übergeben werden. Übergeben wird das Feld args[]
, in
dem auf dem ersten Platz args[0]
der Klassenname selber steht.
Auf den folgenden Plätzen finden sich die Startparameter. In der
Hauptfunktion main
kann nun das gesamte Programm untergebracht,
andere Objekte erstellt oder Threads (siehe Abschnitt 8.3) gestartet
werden. Ist die main
- Funktion abgearbeitet, endet automatisch auch die
Programmausführung auf dem EV3. Ist eine Klasse jedoch nur
dazu da, Objekte von ihrem Typ zu instanziieren, verfügt sie nicht
über eine main
-Methode. Stattdessen existiert dort ein sogenannter
Konstruktor.
Der Konstruktor wird bei jeder Instanziierung eines Objekts aufgerufen.
Er kann als eine Art spezielle Methode ohne Rückgabewert bezeichnet
werden. Diese Methode ist fest mit der Klasse verbunden und trägt ihren
Namen. Mit der Ausführung des Konstruktors können beispielsweise
Initialwerte gesetzt oder Zählvariablen erhöht werden. Aufgerufen wird
der Konstruktor, wenn ein Objekt mit dem Schlüsselwort new
erzeugt
wird (siehe Programm 6.4). Damit wird der Konstruktor für jedes
Objekt genau einmal aufgerufen. Ein nachträglicher oder erneuter
Aufruf ist nicht möglich. Neben Konstruktoren gibt es in Java
auch Destruktoren, die vor dem Zerstören eines Objekts aufgerufen
werden. Da Java über eine automatische Speicherverwaltung verfügt,
wird den Destruktoren in diesem Buch keine weitere Bedeutung
beigemessen.
Im nächsten Beispielprogramm werden die Variablen des Roboters myEV3
nicht in der main
-Methode, sondern in einem eigens dafür erstellten
Konstruktor initialisiert.
Beim Erstellen des Objekts mit dem new
-Operator werden dem
Konstruktor die Variablenwerte als Parameter, getrennt durch Kommata,
übergeben. Bei der Konstruktordefinition kann dann angegeben werden,
was mit den so übergebenen Werten geschehen soll. In unserem
Fall werden Sie den Variablen des Objekts zugewiesen. Damit es
dabei nicht zu Verwechslungen zwischen Parameternamen und
Variablennamen kommt, werden letztere durch den sogenannten this
-
Pointer gekennzeichnet. Dieser verweist immer auf das Objekt, zu dem die
Methode (oder der Konstruktor) gehört.
Wird beim Anlegen einer Klasse kein expliziter Konstruktor angegeben, erzeugt der Compiler automatisch einen Default-Konstruktor, der ohne die Angabe von Parametern aufgerufen wird.
Die Java Codekonventionen stellen Regeln in Bezug auf die Form von Java Quelltext dar. Es ist ratsam sich an diese Regeln zu halten, da man sich so als Entwickler schneller in fremden Code einfinden kann und die Lesbarkeit erhöht wird. Die von Java herausgegebene Dokumentation zu den Codekonventionen kann unter http://www.oracle.com/technetwork/java/codeconvtoc-136057.html angesehen werden. Im folgenden werden die wichtigsten Konventionen vorgestellt:
In Java gibt es, wie in den meisten Programmiersprachen, mehrere
Möglichkeiten seinen Quellcode mit Kommentaren zu versehen. Die
Einfachste ist der sogenannte Zeilenkommentar. Dabei wird an beliebiger
Stelle im Quelltext durch Einfügen von //
ein Kommentar eingeleitet.
Alle Zeichen die danach in dieser Zeile stehen werden nicht vom Compiler
übersetzt. Der Kommentar endet mit der Zeile.
Die zweite Möglichkeit ist die Verwendung von sogenannten
Blockkommentaren. Diese beginnen mit /*
an beliebiger Stelle im
Quelltext und enden mit */
. Alle Zeichen dazwischen werden vom
Compiler ignoriert. Blockkommentare können sich auch über mehrere
Zeilen erstrecken.
Heutzutage werden solche externen Dokumentationen automatisch,
meistens im HTML-Format, erstellt. Bei Java übernimmt diese
Aufgabe das Programm »Javadoc«. Dazu werden vor jeder Klasse,
jedem Interface, jedem Konstruktor, jeder Methode und jedem
Attribut spezielle Dokumentationskommentare verwendet. Diese
beschreiben das entsprechende Element und werden von Javadoc in die
Dokumentation umgewandelt. Dokumentationskommentare sind
eine Sonderform der Blockkommentare. Sie beginnen mit /**
und
enden mit */
. Zeilen dazwischen beginnen meist mit einem *
, was
den Kommentar optisch aufwertet aber keine weitere Funktion
erfüllt.
Der erste Satz jedes Dokumentationskommentars wird in der Zusammenfassung der Methoden und Attribute angezeigt. Dieser erste Satz sollte nach Möglichkeit die Funktionalität schon komplett beschreiben. Der gesamte Dokumentationskommentar wird dann in der Detailansicht aufgeführt. Es sollte bei der Beschreibung der Methoden eher angegeben werden, was die Methode macht, als wie sie es macht. Ebenso sollten Parameter und Rückgabewerte angegeben werden. Dazu werden sogenannte »Tags« benutzt, mit denen eine Vielzahl von Informationen innerhalb eines Dokumentationskommentars an »Javadoc« übergeben werden kann. Eine Übersicht der wichtigsten Tags liefert Tabelle 6.1.
Tag | Beschreibung | Verwendung |
| Beschreibung eines Parameters |
|
| Beschreibung des Rückgabewerts |
|
| Verweis auf ein anderes Packet/Methode/Attribut | |
| Angabe der Exception, die die Methode werfen kann |
|
| Angabe des Autors |
|
| Gibt an, dass die Methode veraltet ist und nicht mehr verwendet werden sollte |
|
|
In Eclipse kann eine Javadoc-Dokumentation über File → Export und Auswahl von »Javadoc« in der Kategorie »Java« angelegt werden. Um die Verwendung einer solchen Dokumentation zu verdeutlichen, wurde Beispielprogramm 6.4 vollständig mit Javadoc-Kommentaren versehen:
Die grundsätzliche Funktionsweise von Variablen unterscheidet sich nicht von der anderer Programmiersprachen. Eine Übersicht über die Datentypen, mit denen Variablen belegt werden können und die leJOS bereitstellt, gibt Tabelle 6.2. Im Folgenden soll der Unterschied zwischen
deutlich werden.
Abhängig von der Art, wie eine Variable deklariert wird, ist sie entweder
eine Instanz- (instance-), eine Klassen- (class-) oder eine lokale
Variable (local variable). Eine Instanzvariable ist, wie der Name
andeutet, eine Variable, die nur dem gemeinsam mit ihr erzeugten
Objekt zur Verfügung steht. Eine Klassenvariable hingegen steht
allen Objekten einer Klasse zur Verfügung. Werden also, wie in
dem Beispiel beschrieben, einige Objekte von einer Klasse erzeugt,
so erhalten sie die Eigenschaften aus dieser Klasse. Obwohl es in
der Klasse nur ein Attribut appearence
(Aussehen) gibt, kann
jedes Objekt (also jeder konkrete Roboter) diesen Wert individuell
beschreiben und verändern, ohne damit die anderen Objekte zu
beeinflussen.
Soll aber zum Beispiel erfasst werden, wie viele Objekte der Klasse
ExampleRobot
erzeugt wurden, so benötigt die Klasse einen Zähler, auf
den alle Objekte zugreifen können. Dieser Zähler ist dann eine
Klassenvariable: Der Inhalt der Variable ist für alle Instanzen gleich. Eine
solche Variable wird bei Deklaration durch das Schlüsselwort static
gekennzeichnet.
Lokale Variablen werden innerhalb einer Methode definiert und existieren auch nur dort. Das Beispielprogramm 6.6 zeigt die verschiedenen Variablendeklarationen und die Verwendung einer Klassenvariablen.
Methoden geben einer Klasse ihre Fähigkeiten und ihr Verhalten. Konkrete Aufgaben wie Berechnungen können übersichtlich in einzelne Methoden gefasst werden. Eine Methode hat immer einen Rückgabewert und optional auch Eingabeparameter, mit denen einer Methode Werte übergeben werden können.
Methoden sind entweder objekt- oder klassenbezogen. Klassenmethoden
werden durch das Schlüsselwort static
deklariert. Diese Klassenmethoden
können innerhalb oder außerhalb der Klasse aufgerufen werden. Durch
den sogenannten Modifier werden die Zugriffsregeln auf die Methode
bestimmt.
Der Modifier private
erlaubt nur den Zugriff von der eigenen Klasse.
Durch den Modifier public
können beliebige andere Klassen auf die
Methode zugreifen. Eine vertiefende Beschreibung der Modifier und ihrer
Verwendung befindet sich unter dem Punkt Kapselung in Kapitel
Kapitel 7.
Methoden können mit Übergabeparametern angelegt werden. Diese
Parameter werden dann beim Aufruf direkt mit angegeben und stehen in
Klammern nach dem Methodennamen. Der Rückgabewert der Methode
wird mit return
an den Aufruf zurückgegeben. Ein Beispiel für einen
solchen Übergabeparameter haben wir bereits in der Klasse ExampleRobot
gesehen. Der Methode age
wird das aktuelle Jahr (int currentYear
) als
Integer übergeben und so mit Hilfe des Kaufjahres (yearOfPurchase
) das
Alter des Roboters berechnet. Dieser Wert wird dann mittels return
zurückgegeben.
Das nachfolgende Beispielprogramm zeigt einige Methoden mit
verschiedenen Modifiern, Übergabeparametern und Rückgabewerten. Zur
anschaulichen Darstellung werden die Rückgabewerte auf dem Display des
EV3 ausgegeben. Dabei werden vorab Methoden der Klassen LCD
und
Button
verwendet, die eigentlich erst in Kapitel 10 ab Seite 233
beschrieben werden.
LCD.drawString(...)
Button.ENTER.waitForPress()
Das Programm 6.7 »MethodTester.java« führt zu folgender Ausgabe auf dem EV3-Display:
Ausdrücke liefern bei ihrer Auswertung ein Ergebnis zurück. Sie bestehen aus Variablen, Operatoren und Methodenaufrufen. Einfache Beispiele geben die arithmetischen Operatoren (»+«, »−«, »∗«, »∕«), die zusammen mit zwei Operanden einen Ausdruck bilden. Der Datentyp des Ergebnisses hängt vom Typ der verwendeten Operanden ab.
Der »+«-Operator hat in Java neben der arithmetischen Verknüpfung
zweier Zahlen noch eine weitere sehr praktische Funktion: Das
Zusammenfügen von zwei Strings. Der Ausdruck “Ich bin ein “ +
“String“
liefert damit den String “Ich bin ein String“
zurück. Dabei
konvertiert der »+«- Operator auch Zahlenwerte aus Variablen zu
Strings. Der Ausdruck “Der Wert der Variable intValue lautet: “
+ intValue
liefert einen String zurück, der den aktuellen Wert der
Variable intValue angibt.
Auch die logischen Operatoren »==« (gleich), »!=« (ungleich), »>«
(größer), »<« (kleiner), »>=« (größer-gleich) und »<=« (kleiner-gleich)
bilden mit zwei Operanden einen Ausdruck. Bei Auswertung liefert
dieser einen booleschen Wert (true
oder false
) zurück, der den
Wahrheitsgehalt des Ausdrucks angibt.
Anweisungen bestehen aus Ausdrücken und bilden die kleinste ausführbare Einheit eines Computerprogramms. Es gibt drei verschiedene Arten von Anweisungen.
Ausdrucksanweisungen sind Ausdrücke die lediglich durch Abschluss mit
einem Semikolon zu einer Anweisung werden. Dazu gehören beispielsweise
Funktionsaufrufe oder Wertzuweisungen. Bei einer Wertzuweisung mit
dem »=«-Operator wird der Ausdruck intValue = 3
durch Abschluss
mit einem Semikolon zu einer Anweisung. Es handelt sich dabei ohne
Semikolon um einen Ausdruck, da der Wert auch zurückgeliefert
wird.
Weiter gibt es Kontrollflussanweisungen, die in Abschnitt 6.6 beschrieben
werden und Deklarationsanweisungen. Bei diesen handelt es sich
um jede Art von Variablendeklarationen, also zum Beispiel: int
intValue;
. Dabei wird dem Programm die Existenz einer Variable
bekannt gemacht, bevor mit ihr gearbeitet wird. Deklaration und
Zuweisung können auch in einer Anweisung kombiniert werden:
int intValue = 0;
.
Ein Block ist eine Gruppierung von mehreren Anweisung mit Hilfe geschweifter Klammern. Der Beginn eines Blocks wird durch »{«, das Ende eines Blocks durch »}« gekennzeichnet. Ein Block kann überall da verwendet werden, wo auch einzelne Anweisungen erlaubt sind. Ein Beispiel findet sich in der Syntax der if- else-Anweisung auf Seite 132.
Kontrollstrukturen sind Anweisungen, die den Programmfluss steuern, also die Reihenfolge beeinflussen, in der Anweisungen ausgeführt werden. Eine Kontrollstruktur ist entweder eine Verzweigung (if-else und switch) oder eine Schleife (while, do-while und for).
Die if
-Struktur wird verwendet, um Programmverzweigung aufgrund
einer Bedingung zu realisieren. Nur wenn die Bedingung zutrifft,
werden die Anweisungen nach dem Schlüsselwort if
abgearbeitet,
ansonsten werden die Anweisungen nach else
abgearbeitet. Das
folgende Flussdiagramm in Abbildung 6.2 erläutert die allgemeine
Programmverzweigung der if-Struktur.
Die Java-Syntax zur Implementierung einer if
-Abfrage lautet:
Mehrere if
bzw. if-else
-Anweisungen können beliebig verschachtelt
werden. Hierzu wird eine zusätzliche if
-Anweisung in den Anweisungsblock
von else
geschrieben. Es ist natürlich auch möglich, nur die if
-Anweisung
(ohne den else
-Teil) zu nutzen.
Im Gegensatz zur Zweifachverzweigung der einfachen if-else
-Struktur,
können mit dem switch
-statement Mehrfachverzweigungen im
Programmfluss erstellt werden.
Dazu wird zunächst der Wert einer Variablen oder eines Ausdrucks
evaluiert und je nach Ergebnis unterschiedliche Anweisungen ausgeführt.
Die Syntax des switch
-statements lautet wie folgt:
Der zu evaluierende Ausdruck (meist eine Variable) wird in Klammern
hinter dem Schlüsselwort switch
angegeben. Im darauffolgenden
Switch-Block können mittels case
für beliebig viele Ergebnisse dieses
Ausdrucks unterschiedliche Anweisungen angegeben werden. Ist ein
Ergebnis im Switch-Block nicht zu finden, werden die Anweisungen hinter
default
ausgeführt.
Wie in Diagramm 6.3 zu erkennen ist, werden – falls nicht anders
behandelt – für ein bestimmtes Ergebnis nicht nur die dafür
angegebenen Anweisungen ausgeführt, sondern auch alle darauf
folgenden Anweisungen anderer Ergebnisse. Das liegt daran, dass mit
case
und default
lediglich Sprungziele im Switch-Block für den
Programmfluss definiert werden. Nach einem Sprung zur dem Ergebnis
entsprechenden Stelle wird der Switch-Block dann regulär Zeile für Zeile
abgearbeitet.
In der Regel sollen die Anweisungen anderer Ergebnisse nicht mit
ausgeführt werden. Daher muss der Switch-Block nach jeder Sequenz
von Anweisungen für ein Ergebnis explizit mittels break
verlassen
werden.
Dieses Verhalten des switch
-Statements ist durchaus gewünscht und kein
Fehler. So ist es nämlich möglich, durch Weglassen des break
, für
mehrere Ergebnisse die gleichen Anweisungen auszuführen. Man spricht
dann auch vom sogenannten »fall through«.
Die while
-Schleife wird verwendet, um Wiederholungen von bestimmten
Anweisungen auszuführen, solange (engl. while) eine Bedingung
erfüllt ist (siehe Abbildung 6.4). Die while-
Schleife wird auch
kopfgesteuerte Schleife genannt. Es wird zunächst die Bedingung im
Schleifenkopf geprüft, ist diese erfüllt (true
), werden die Anweisungen im
Schleifenrumpf ausgeführt. Dies wiederholt sich, bis die Bedingung nach
einer Ausführung des Schleifenrumpfs nicht mehr erfüllt ist.
Die Java-Syntax zur Implementierung einer solchen while
-Schleife lautet
wie folgt:
Soll der Anweisungsblock mindestens einmal ausgeführt werden,
empfiehlt sich die Verwendung der do-while
-Schleife. Bei ihr wird im
Programmverlauf zuerst die Anweisung ausgeführt und danach geprüft,
ob die Bedingung noch gültig ist. Daher wird die do-while-
Schleife als
fußgesteuerte Schleife bezeichnet.
Die Java-Syntax zur Implementierung einer do-while
-Schleife
lautet::
Braucht man in einer wiederholenden Struktur einen Schleifenzähler,
empfiehlt es sich, statt der while
-Schleife eine for
-Schleife zu
verwenden.
Die Initialisierung (engl. initialization) wird ausgeführt bevor die
for
-Schleife das erste Mal durchlaufen wird. An dieser Stelle wird die
Zählvariable erstellt. Danach folgt die Bedingung (engl. condition), die
vor jedem Durchlauf der Schleife geprüft wird. Liefert sie true
wird der
nächste Durchlauf gestartet. Nach jedem Durchlauf wird der sogenannte
Fortschaltausdruck (for statement) ausgeführt. Dieser verändert die
Zählvariable auf gewünschte Art und Weise, kann aber theoretisch jede
beliebige Anweisung enthalten.
Die for
-Struktur sieht wie folgt aus:
Eine mögliche for
-Schleife, im Laufe derer die Zählvariable von 0 bis 8
erhöht wird, sieht dann beispielsweise so aus:
Die Integer-Variable i wird zu Beginn mit dem Wert 0
initialisiert.
Die Bedingung i<=8
bedeutet, dass die Anweisungen solange
wiederholt werden, wie i
kleiner oder gleich 8
ist. Der Ausdruck i++
(gleichbedeutend mit i=i+1
) erhöht den Wert der Zählvariable
nach jeder Ausführung des for
-Blocks um 1
. Alternativ können so
auch Schleifen konstruiert werden, deren Zählvariablen kleiner
werden.
Manchmal ist es nötig eine Schleife vorzeitig zu beenden oder eine Schleifeniteration an einer bestimmten Stelle abzubrechen und mit der nächsten Iteration zu beginnen. Dazu stehen dem Programmierer drei Befehle zur Verfügung:
break
beendet die Schleife komplett. Die Programmausführung
wird nach dem Schleifenrumpf, also ausserhalb der Schleife,
fortgesetzt. Meist wird dieser Befehl innerhalb einer Schleife in
Verbindung mit if
-Abfragen eingesetzt, um eine oder mehrere
Abbruchbedingungen für die Schleife zu erstellen.
continue
beendet die aktuelle Schleifeniteration und setzt die
Programmausführung im Schleifenkopf (bzw. Schleifenfuß bei
do-while
-Schleifen) fort. Bei while
- und do-while
-Schleifen
wird mit dem Testen der Schleifenbedingung fortgefahren. Bei
for
-Schleifen wird zuerst der Fortschaltausdruck ausgewertet
und dann die Schleifenbedingung getestet. Das
Programm springt daraufhin bei allen drei Schleifen (falls die
Schleifenbedingungen wahr sind) wieder in den Schleifenrumpf.
return
haben wir bereits kennengelernt. Schleifen können damit
verlassen werden, wenn sie sich innerhalb von Methoden
befinden, die nicht void
sind, also einen Rückgabewert liefern
müssen. Es wird der hinter return
angegebene Wert an
den Methodenaufruf zurückgeliefert, die Methode dadurch
beendet und damit auch die Schleife innerhalb der Methode
abgebrochen.break
und continue
nur auf den Block beziehen in dem sie aufgerufen werden. Bei verschachtelten Schleifenstrukturen laufen die äußeren Schleifen unverändert weiter, wenn in der Innersten ein break
oder continue
aufgerufen wird.
leJOS unterstützt alle Datentypen, die auch von Java unterstützt
werden. In Tabelle 6.2 sind die wichtigsten von Java unterstützten
Datentypen aufgeführt. Aus Performancegründen sind die primitiven
Datentypen (char, byte, short, int, long) nicht in echten Klassen definiert,
sondern in sogenannten Wrapper-Klassen. Wrapper-Klassen sind
Klassen, die den entsprechenden Datentyp in ein Objekt »einpacken«.
Hinzu kommt, dass es nicht, wie in anderen Programmiersprachen,
unsigned
-Typen gibt. Positive und negative Wertebereiche werden
implizit deklariert.
Art | Name/ Klasse | Datentyp | Größe | Wertebereich |
Binär | Boolean | boolean | 1 Bit | true oder false |
Ganzzahl | Byte | byte | 8 Bit | −27 bis −27 − 1 |
Ganzzahl | Short | short | 16 Bit | −215 bis 215 − 1 |
Ganzzahl | Integer | int | 32 Bit | −231 bis 231 − 1 |
Ganzzahl | Long | long | 64 Bit | −263 bis 263 − 1 |
Fließkommazahl | Float | float | 32 Bit | ±1,4E-45…±3,4E+38 |
Fließkommazahl | Double | double | 64 Bit | ±4,9E-324…±1,7E+308 |
Zeichen | Char | char | 16 Bit |
|
Zeichen | String | String | max. 231 − 1 Zeichen
| |
|
1 Instanz und Objekt werden in der Literatur oftmals synonym verwendet, wir schließen uns diesem Gebrauch innerhalb dieses Bandes ebenfalls an.