9 Anweisungen und Kontrollstrukturen Hier beziehen wir uns auf Kapitel 6 von Go To Java 2. Die Steuerung des Programmablaufs geschieht durch sogenannte Anweisungen. Jede Anweisung bewirkt irgendwelche Aktionen des Prozessors. Einige Anweisungen haben Sie bereits kennengelernt, so ist z.B. die Verwendung der Ausgaberoutine eine Anweisung. Auch die Definition von Variablen oder die Verwendung von Ausdrücken sind Anweisungen. Jede Anweisung in einem Java–Programm muß mit einem Semikolon enden. Dies ist nötig, da Zeilenumbrüche für den Java–Compiler keine Bedeutung haben und er daher nicht anhand der Zeilenstruktur erkennen kann, wo eine Anweisung zuende ist. 9.1 Leere Anweisung Siehe auch Kapitel 6.1 in Go To Java. Die einfachste Anweisung in einem Java–Programm ist die leere Anweisung. Sie hat keine Wirkung. Daß die leere Anweisung erlaubt ist, bedeutet im wesentlichen, daß Sie beliebig viele Semikoli setzen dürfen — auch dort, wo nicht unbedingt eines stehen muß. Beispiel: public class DemoEmpty { public static void main( String[] args ) { ; // Leere Anweisung ;; // Zwei leere Anweisungen } } 9.2 Blockanweisung Der Block gruppiert eine Gruppe von Anweisungen zu einer Einheit. Sie hat die Form { Anweisung1; Anweisung2; ... } Sie sollten sich angewöhnen, den Inhalt eines Blockes durch Einrückung der im Block enthaltenen Zeilen optisch kenntlich zu machen — in allen Beispielprogrammen machen wir Ihnen das auch so vor. Beachten Sie, daß die Beschreibung der Blockanweisung rekursiv ist — der Block ist eine Anweisung und in einem Block dürfen wieder Anweisungen stehen. Daher darf in einem Block auch ein weiterer Block enthalten sein. 65 Beispiel: Dies ist ein gültiges Java–Programm. Im Klassenkörper ist die Methode main enthalten. In deren Methodenkörper sind zwei ineinander verschachtelte Blöcke enthalten. Der Klassenkörper wird auch durch geschweifte Klammern abgegrenzt, sieht also genauso aus wie ein Blöcke aus. Er ist aber dennoch kein Anweisungsblock, da er keine Anweisungen sondern nur Definitionen enthalten darf. public class DemoBlock { // hier startet der Klassenkörper public static void main( String[] args ) { // hier startet der Methodenkörper ; // Leere Anweisung { // hier startet Block 1 { // und hier startet Block 2 System.out.println("Hallo, Welt !"); } // hier endet Block 2 } // hier endet Block 1 } // hier endet der Methodenkörper } // hier endet der Klassenkörper 9.3 Variablendefinitionen Variablendefinitionen haben wir schon oft benutzt. Sie wissen bereits, daß man Variablen nur verwenden kann, nachdem man sie definiert hat. Zu beachten ist, daß eine Variablen Variablendefinition immer nur für den Block gilt, in welchem sie definiert wurde. Sobald die Programmausführung einen Block verläßt, haben die in diesem Block definierten Variablen keine Bedeutung mehr. Man sagt auch: Variablendefinitionen gelten nur lokal im enthaltenden Block. Wenn in einem Block ein weiterer Block enthalten ist, erbt dieser alle Variablendefinitionen des äußeren Blockes: Beispiel: Im folgenden Beispiel wird im Block 2 die Variable i1 definiert und initialisiert. Da Block 3 und Block 4 in Block 2 enthalten sind, kann i1 auch aus Block 3 und 4 verwendet werden. Auch in Block A wird die Variable i1 definiert. Diese Variable i1 hat gar nichts mit der Variable i1 in Block 2 zu tun, da sie außerhalb Block 2 definiert wurde. Die Variablendefinitionen von Block 2 und Block A beeinträchtigen sich überhaupt nicht gegenseitig. In Block 1 ist die Variable i1 nicht bekannt, da sie weder in Block 1 noch in einem Block 1 umschließenden Block definiert wurde. Die Verwendung der Variable i1 in Block 1 führt daher zu einer Fehlermeldung des Compilers. public class DemoBlock1 { public static void main( String[] args ) { // Block 1 { // Block 2 int i1 = 5; { // Block 3 { // Block 4 System.out.println(i1); 66 } } } { // Block A String i1 = "Hallo, Welt"; } System.out.println(i1); // Fehler ! } } Sie dürfen in einem Block keine Variable zweimal definieren. Beispiel: Das folgende Programm ist fehlerhaft, da die Variable i1 im gleichen Block zweimal definiert wird: public class DemoBlock2 { public static void main( String[] args ) { int i = 1; double i = 3.5; //Fehler } } Leider dürfen sich in Java Variablen nicht gegenseitig verdecken — sie dürfen Variablen auch dann nicht in einem Block definieren, wenn sie zuvor nur in einem diesen Block umschließenden Block definiert wurden. Beispiel: Im folgenden Beispiel wird in Block 1 eine int–Variable definiert. Block 2 erbt diese Definition, da er in Block 1 enthalten ist. Daher darf in Block 2 i nicht nochmal definiert werden. public class DemoBlock3 { public static void main( String[] args ) { // Block 1 int i = 1; { // Block 2 int i = -5; // Fehler } } } 9.4 Ausdrucksanweisungen Zu den Ausführungen über Ausdrucksanweisunge in Kapitel 6.1 von Go To Java 2 braucht nichts hinzuzugefügt zu werden außer einem Beispiel: Beispiel: Hier ein Beispiel mit verschiedenen Ausdrucksanweisungen. 01 public class DemoVar1 { 02 public static void main( String[] args ) 03 { 04 System.out.println("Hallo, Welt !"); 67 05 06 07 08 09 } 10 } int i = 5; i = 6; i++; 5 + 2; // Variablendefinition und Initialisierung // Ausdrucksanweisung: Zuweisung // Ausdrucksanweisung: Inkrement // In der Ausdrucksanweisung in Zeile 08 wird eine Addition durchgeführt. Die Ausdrucksanweisung hat aber keine weitere Wirkung, da der Wert des Ausdrucks weder ausgegeben noch sonstwie verwendet wird. Die Ausdrücke in Zeile 06 und 07 haben dagegen eine Wirkung (nämlich jeweils eine Veränderung der Variable i), da in ihnen Operatoren mit Nebeneffekten verwendet werden. 9.5 If–Anweisung Kapitel 6.2 von Go To Java 2 sollte so verständlich sein. Nur fehlen mal wieder Beispiele. Sie sehen ja in Go To Java 2, daß eine if –Anweisung in zwei Versionen auftreten kann. Einmal mit und einmal ohne else–Zweig. Beachten Sie bei der Besprechung aller weiteren Anweisungen, daß eine Anweisung auch ein Block sein kann. Beispiel: Hier ein Beispiel zu if public class DemoIf1 { public static void main( String[] args ) { int temperatur = 30; if ( temperatur > 25 ) System.out.println("Boah, ganz schön warm"); } } Beispiel: Hier ein Beispiel, bei welchem die Anweisung ein Block ist: public class DemoIf2 { public static void main( String[] args ) { int temperatur = 30; if ( temperatur > 25 ) { System.out.println("Boah, ganz schön warm"); System.out.println("Stell doch mal einer die Klimaanlage an"); } } } Beispiel: Und hier noch ein Beispiel, in dem es auch einen else–Zweig gibt: public class DemoIfElse { public static void main( String[] args ) { 68 int temperatur = 30; if ( temperatur > 22 ) { System.out.println("Boah, ganz schön warm"); System.out.println("Stell doch mal einer die Klimaanlage an"); } else { System.out.println("Schön --- zu heiß ist es nicht."); } } } Das in Go To Java erwähnte Dangling else (“hängendes Else”) bedarf noch einer weiteren Diskussion: Sie sollten Dangling else—Strukturen immer vermeiden. Verwenden Sie in Fällen, wo ein Dangling else auftritt, immer Blöcke ! Beispiel: Hier tritt ein Dangling else auf — aufgrund der suggestiven Einrückung ist nicht auf den ersten Blick klar, daß die else–Anweisung nicht zur ersten sondern zur zweiten if –Anweisung gehört. Das Programm arbeitet daher nicht so, wie gewünscht. public class DemoDangling1 { public static void main( String[] args ) { int temperatur = 30; boolean istKlimaanlageAn = true; if ( temperatur < 15 ) // if-Anweisung 1 if ( istKlimaanlageAn ) // if-Anweisung 2 System.out.println("Klimaanlage abstellen"); else System.out.println("Schön --- zu kalt ist es nicht."); } } Das Problem kann durch Verwendung von Blöcken behoben werden. Sie sollten sich angewöhnen, in solchen leicht mißverständlichen Situationen immer Blöcke zu verwenden ! public class DemoDangling2 { public static void main( String[] args ) { int temperatur = 30; boolean istKlimaanlageAn = true; if ( temperatur < 15 ) { if ( istKlimaanlageAn ) System.out.println("Klimaanlage abstellen"); } else System.out.println("Schön --- zu kalt ist es nicht."); } } 9.6 Switch–Anweisung Die Switch–Anweisung ist in Go To Java in Kapitel 6.2.2 beschrieben. 69 Zur switch–Anweisung ist hinzuzufügen, daß hinter jedem case eine Reihe von Anweisungen steht. Wird ein case angesprungen, so werden auch die darauf folgenden case–Blöcke abgearbeitet, wenn kein break in den Anweisungen steht. Beispiel: Das folgende Programm gibt die textliche Darstellung einer Zahl aus. Dastut es einmal mit einer Folge von if –Anweisungen und einmal mit einer gleichwertigen switch–Anweisung. Tippen Sie das Programm ab und testen Sie, was passiert, wenn Sie einige break–Anweisungen entfernen ! public class DemoSwitch { public static void main( String[] args ) { int zahl = 1; if ( zahl == 1) { System.out.println("Eins"); } else if (zahl == 2) { System.out.println("Zwei"); } else if ( zahl == 3) { System.out.println("Drei"); } else if (zahl == 4) { System.out.println("Vier"); } else { System.out.println("Diese Zahl kenne ich nicht"); } // Äquivalente Lösung per switch: switch(zahl) { case 1: System.out.println("Eins"); break; case 2: System.out.println("Zwei"); break; case 3: System.out.println("Drei"); break; case 4: System.out.println("Vier"); break; default: System.out.println("Diese Zahl kenne ich nicht-"); break; } } } 9.7 Schleifen Zu Kapitel 6.3 ist nicht viel hinzuzufügen außer einigen Beispielen. 9.7.1 Die While–Schleife Die While–Schleife können Sie immer dann einsetzen, wenn Sie eine oder mehrere Anweisungen immer wieder ausführen wollen, solange eine gewisse Bedingung erfüllt ist. Sie hat die Form while( Schleifenbedingung ) anweisung; 70 Der Anweisungsblock einer while–Anweisung wird immer wieder abgearbeitet, während die Schleifenbedingung erfüllt ist. Im Detail wird eine while–Anweisung wie folgt abgearbeitet: Zunächst wird die Schleifenbedingung ausgewertet, welche ein boolscher Ausdruck sein muß. Falls sich hier der Wert false ergibt, so wird mit der nächsten Anweisung hinter der while–Schleife weitergemacht. Falls sich hingegen für die Schleifenbedingung der Wert true ergibt, so wird die Schleifenanweisung durchgeführt. Beachten Sie, daß diese Anweisung auch ein Block sein darf. Nachdem die Schleifenanweisung abgearbeitet ist, wird die Schleifenbedingung erneut geprüft, wenn sich hier wieder der Wert true ergibt, wird die Schleifenanweisung erneut ausgeführt. Dies geschieht wieder und wieder, bis die Schleifenbedingung den Wert false ergibt. Beispiel: Die Anweisung in Zeile 05 wird niemals ausgeführt, da die Schleifenbedingung in Zeile 04 immer false ist. 01 public class DemoWhile1 { 02 public static void main( String[] args ) 03 { 04 while( false ) 05 System.out.println("Hallo, Welt"); 06 } 07 } Beispiel: Eine Schleife, die immer wieder ausgeführt wird und niemals wieder verlassen wird, nennt man Endlosschleife. Das folgende Programm ist eine solche, da die Bedingung in Zeile 04 immer true ist. Dieses Programm beendet sich nie, nachdem Sie es gestartet haben. Es gibt wieder und wieder “Hallo, Welt” aus. Sie können es auf die harte Tour unterbrechen, indem Sie die Tastenkombination Ctrl+c bzw. Strg+c verwenden. 01 public class DemoWhile2 { 02 public static void main( String[] args ) 03 { 04 while( true ) 05 System.out.println("Hallo, Welt"); 06 } 07 } Beispiel: Das folgende Programm gibt die Zahlen von 1 bis 10 auf den Bildschirm aus. Es verwendet dazu eine Zählervariable. Es demonstriert außerdem, daß die Schleifenanweisung auch ein Block sein kann. 01 public class DemoWhile3 { 02 public static void main( String[] args ) 03 { 04 int i = 1; 04 while( i <= 10 ) { 05 System.out.println(i); 06 i++; 07 } 07 } 08 } 71 Man könnte das obige Programm auch noch kompakter schreiben, indem man den Inkrement–Operator in den Ausgabe–Befehl schreibt: 01 public class DemoWhile4 { 02 public static void main( String[] args ) 03 { 04 int i = 1; 04 while( i <= 10 ) 05 System.out.println(i++); 06 } 07 } Eine weitere Variante des vorigen Programmes ist lehrreich. Wir können die Variable i auch innerhalb der Schleifenbedingung inkrementieren. Dabei müssen wir aber darauf achten, daß die Schleifenbedingung schon vorm ersten Durchlauf der Schleife einmal ausgeführt wird. Auch das folgende Programm gibt die Zahlen 1 bis 10 aus. Machen Sie sich das klar ! Achten Sie vor allem darauf, daß nun in der Schleifenbedingung ein echtes kleiner und nicht ein kleiner–gleich–Zeichen steht. 01 public class DemoWhile4 { 02 public static void main( String[] args ) 03 { 04 int i = 0; 04 while( i++ < 10 ) 05 System.out.println(i); 06 } 07 } 9.7.2 Die Do–Schleife Die do–Schleife arbeitet so ähnlich wie die while–Schleife. Sie hat die Form do anweisung; while ( Schleifenbedingung ); Sie unterscheidet sich von der while–Schleife dadurch, daß der Schleifenkörper in jedem Fall mindestens einmal durchlaufen wird. Genau wie bei der while–Schleife wird nach jedem Durchlauf des Schleifenkörpers die Schleifenbedingung geprüft. Immer wenn die Bedingung true ergibt, wird die Schleife erneut durchlaufen. Beispiel: Im folgenden Programm wird der Schleifenkörper genau einmal durchlaufen und dabei “Hallo, Welt” ausgegeben. Da die Schleifenbedingung immer false ist, wird die Schleife danach nicht weiter ausgeführt. 01 public class DemoDo1 { 02 public static void main( String[] args ) 03 { 04 do 72 05 06 07 } 08 } System.out.println("Hallo, Welt"); while( false ); Beispiel: Im folgenden Programm wird der Schleifenkörper immer wieder einmal durchlaufen und dabei “Hallo, Welt” ausgegeben, da Schleifenbedingung immer true ist. Das Programm unterscheidet sich im Resultat nicht von der vorhin besprochenen while–Endlosschleife. 01 public class DemoDo2 { 02 public static void main( String[] args ) 03 { 04 do 05 System.out.println("Hallo, Welt"); 06 while( true ); 07 } 08 } Beispiel: Das folgende Programm gibt die Zahlen von 1 bis 10 aus. Beachten Sie den feinen Unterschied zum äquivalenten Programm mit while–Schleife (class DemoWhile4) aus dem vorigen Abschnitt. Machen Sie sich klar, warum hier nicht mit i=0 sondern mit i=1 begonnen wird. 01 public class DemoWhile4 { 02 public static void main( String[] args ) 03 { 04 int i = 1; 04 do { 05 System.out.println(i); 06 } while ( i++ < 10 ); 07 } 07 } Man kann while– in do–Schleife ineinander umschreiben. Eine do–Schleife der Form do anweisung; while( Schleifenbedingung ); kann als while–Schleife äquivalent so geschrieben werden: anweisung; while ( Schleifenbedingung ) anweisung; Eine while–Schleife der Form while ( Schleifenbedingung ) anweisung; kann als äquivalente do–Schleife so geschrieben werden: 73 if ( Schleifenbedingung ) { do anweisung; while( Schleifenbedingung); } 9.7.3 Die For–Schleife Die For–Schleife ist die wohl am häufigsten verwendete Schleife. Während die while– und do–Schleife meist dann eingesetzt werden, wenn man beim Beginn der Schleifenabarbeitung noch nicht genau sagen kann, wie oft die Schleife durchlaufen werden soll, setzt man die for–Schleife meist dann ein, wenn man schon vor Beginn der Schleife weiß, wie oft die Schleife durchlaufen werden soll. Meist wird die for–Schleife dazu eingesetzt, eine Variable einen gewissen Wertebereich durchlaufen zu lassen. Sie hat die Form for (init; test; update) anweisung; In den runden Klammern hinter dem for–Befehl werden drei durch Semikoli getrennte Ausdrücke erwartet. Wie eine for–Schleife arbeitet, wird am besten klar, indem wir sie in eine äquivalente while–Schleife umschreiben. Die for–Schleife kann man als Abkürzung für die folgende Schleife interpretieren: init; while( test) { anweisung; update; } Der erste Teil in den runden Klammern (init) wird vor dem Start der Schleife ausgewertet. Er dient meist dazu, in der Schleife benötigte Variablen zu definieren oder zu initialisieren. Folgende Anweisungen und Ausdrücke sind hier erlaubt: • “init” darf ein Ausdruck sein, wie in folgendem Beispiel: for( i=0; i < 10; i++) System.out.println(i); • “init” darf eine Liste von Ausdrücken sein, die dann durch Kommata getrennt sein müssen, wie in diesem Beispiel: for(i=0, j=0 ; i < 10; i++, j+=i ) { System.out.println(i); System.out.println(j); } • “init” darf eine Variablendefinition sein: 74 for(int i=0 ; i < 10; i++) System.out.println(i); Eine so definierte Variable ist nur innerhalb der for–Anweisung gültig. Daher ist der folgende Programmtext gültiger Java–Code: for(int i=0 ; i < 10; i++) System.out.println(i); for(double i=0.5 ; i < 10.5; i++) System.out.println(i); • “init” darf leer sein: for( ; false; ) ; Der zweite Ausdruck (test–Ausdruck) muß boolschen Datentyp haben. Er wird ausgewertet, um zu prüfen, ob die Schleife durchlaufen werden soll. Während der test–Ausdruck true ergibt, wird die Schleifenanweisung immer wieder ausgeführt. Nach jedem Ausführen der Schleifenanweisung wird der update–Teil in den runden Klammern ausgewertet. Dabei darf “update” ein Ausdruck oder eine durch Kommata getrennte Liste von Ausdrücken sein (siehe Beispiele oben). Im “update”–Teil werden meist mit Hilfe von Inkrement–Operatoren Variablen verändert. Der update–Ausdruck darf leer sein. Beispiel: Das folgende Programm gibt die Zahlen von 1 bis 10 aus. 01 public class DemoFor1 { 02 public static void main( String[] args ) 03 { 04 int i; 05 for(i=1 ; i <= 10 ; i++ ) 06 System.out.println(i); 07 } 08 } Beispiel: Auch das folgende Programm gibt die Zahlen von 1 bis 10 aus. Es empfiehlt sich, die Variablendefinition in die Initialisierungsanweisung zu schreiben. 01 public class DemoFor2 { 02 public static void main( String[] args ) 03 { 04 for(int i=10 ; i <= 100 ; i+=10 ) 05 System.out.println(i / 10 ); 06 } 07 } Beispiel: Das folgende Programm gibt die Zahlen von 1 bis 10 rückwärts aus: 75 01 public class DemoFor3 { 02 public static void main( String[] args ) 03 { 04 for(int i=10; i > 0 ; i-- ) 05 System.out.println(i); 06 } 07 } Beispiel: In diesem Programm wird die Anweisung in Zeile 05 niemals ausgeführt, da die Schleifenbedingung in Zeile 04 immer false ist. 01 public class DemoFor4 { 02 public static void main( String[] args ) 03 { 04 for( ; false ; ) 05 System.out.println("Hallo, Welt"); 06 } 07 } Beispiel: Dies ist eine Endlosschleife, da die Schleifenbedingung immer true ist — es wird immer wieder der Text “Hallo, Welt” ausgegeben. 01 public class DemoFor5 { 02 public static void main( String[] args ) 03 { 04 for( ; true ; ) 05 System.out.println("Hallo, Welt"); 06 } 07 } Beispiel: Oft verschachtelt man mehrere Schleifen ineinander. Das folgende Programm gibt eine dreieckige Figur aus. Dazu verwenden wir die Methode “System.out.print”. Diese arbeitet genauso wie die “println”– Methode, beginnt aber nicht nach jeder Ausgabe mit einer neuen Zeile sondern gibt aufeinanderfolgende Ausgaben hintereinander in der gleichen Zeile aus. 01 public class DemoFor6 { 02 public static void main( String[] args ) 03 { 04 for(int i=1 ; i <= 10 ; i++ ) { 05 for(int j = 1; j <= i ; j++ ) { 06 System.out.print("*"); 07 } 08 System.out.println(); // neue Zeile beginnen 09 } 10 } 11 } Beispiel: Sie können in einer for–Schleife beliebige Variablen als Kontrollvariablen verwenden, nicht nur int–Variablen. Wir demonstrieren hier außerdem die Verwendung mehrerer Update–Ausdrücke. Das Programm gibt 6–Mal die Zahl 5.0 aus. 76 public class DemoFor7 { public static void main( String[] args ) { int i = 5; for(double d=0 ; d <= 5 ; d++, i-- ) { System.out.println(d + i); } } } 9.7.4 break und continue Bitte lesen Sie die Ausführungen zu break und Continue in Go To Java, Kapitel 6.3 nach. 77 9.8 Zusammenfassung Sie müssten nun die folgenden Fragen beantworten können: ⊲ Was ist ein Block ? ⊲ Wieso darf man in Java auch mehr als ein Semikolon zum Abschluß eines Befehls verwenden ? ⊲ Was für einfache Anweisungen kennen Sie ? ⊲ Erklären Sie, wo eine einmal definierte Variable überall verwendet werden darf — was ist ihr Gültigkeitsbereich ? ⊲ Können sich Variablendefinitionen in Java gegenseitig verdecken ? ⊲ Nennen Sie Beispiele für Ausdrucksanweisungen ! ⊲ Welche Beziehung gibt es zwischen Variablendefinitionen und Blöcken ? ⊲ Was ist ein Dangling Else ? ⊲ Wie kann man ein Dangling Else vermeiden ? ⊲ Welche Schleifen kennen Sie ? ⊲ Wie werden do–Schleifen aufgebaut ? ⊲ Wodurch unterscheiden sich do und while–Schleife ? ⊲ Was ist eine Endlosschleife ? ⊲ Wie kann man ein Programm unterbrechen, das in einer Endlosschleife hängt ? ⊲ Geben Sie ein Beispiel für eine Endlosschleife ! ⊲ Wie kann man eine Do in eine While–Schleife umschreiben ? ⊲ Wie kann man eine While– in eine Do–Schleife umschreiben ? ⊲ Wie kann man eine for–Schleife in eine while–Schleife umschreiben ? ⊲ Wie muß eine For–Schleife aufgebaut sein ? ⊲ Wie kann der init–Teil einer For–Schleife aussehen ? ⊲ Wenn eine for–Schleife verwendet wird, um eine Variable einen gewissen Zahlenbereich durchlaufen zu lassen — wo sollte diese Variable definiert werden ? ⊲ Schreiben Sie ein Programm, das rückwärts von 10 bis 1 zählt – einmal mit einer do–Schleife, einmal mit einer for– und einmal mit einer while– Schleife. 78