bedingte Anweisungen (Mehrfachverzweigungen)

<-- zurck zur Startseite | <- eine Seite vor | eine Seite weiter ->

  1. der else-Block
  2. der else-if-Block
  3. verschachtelte bedingte Anweisungen
  4. der ?-Operator
  5. der switch-Block
  6. Üung
  7. Lösung

1. der else-Block

Das if allein wird uns aber auf Dauer nicht reichen. Wenn der Ausdruck, der der if-Block übergeben wird, "false" ist, soll die bedingte Anweisung nicht komplett übersprungen werden, sondern ein alternativer Block ausgeführt werden.
Diesen kann man mit einer else-Block anschließen ("if" alleine gibt es, "else" alleine logischerweise nicht):

if (ausdruck) {
   // verschiedene Anweisungen
} else {
   // verschiedene Anweisungen
}

if und else brauchen dabei nicht unbedingt mit den jeweils geschweiften Klammern in einer Zeile stehen, das ist nur etwas, das ich mir so angewöhnt habe und man auch sonst oft liest. Folgendes geht aber auch (diese Schreibweise sieht man häufiger in C/C++):

if (ausdruck) 
{
   // Anweisungen

}
else 
{
   // Anweisungen
}

Wenn nur jeweils eine Code-Zeile im Block steht, z.B. weil man nur eine einzige Anweisung braucht, kann man sich sogar die Klammern sparen:

if (ausdruck)
    System.out.println("ausdruck ergibt wahr");
else
   System.out.println("ausdruck ergibt falsch");

Einrückungen wären hier aber angebracht. Alternativ kann man in diesem Fall sogar Anweisung und "if" bzw. "else" in eine Zeile schreiben:

if (ausdruck) System.out.println("ausdruck ergibt wahr");
else System.out.println("ausdruck ergibt falsch");

Dies sieht man doch recht häufig (Programmierer sind maximal tippfaul), deswegen der Hinweis darauf. Im Zweifel aber lieber mit Klammern schreiben!

nach oben

2. der else-if-Block

Die Möglichkeiten der bedingten Anweisungen gehen aber noch weiter: man kann auch mehrere if-Blöcke verketten, die mit else if angeschlossen werden. Syntax:

if (ausdruck1) {
   // verschiedene Anweisungen
} else if (ausdruck2) {
   // verschiedene Anweisungen
} else {
   // verschiedene Anweisungen
}

Mann kann beliebig viele "else if"-Blöcke anhängen.
Ist der Ausdruck1 "true", dann werden die Anweisungen hinter "if" ausgeführt. Ist er "false", wird zum ersten "else if" weiter gegangen und hier der Ausdruck geprüft. Ergibt er "true", dann wird der nachfolgende Block ausgeführt. Ergibt er "false", dann wird zum nächsten "else if"-Block gesprungen usw. Ist schließlich keiner der Ausdrücke richtig, dann wird der "else"-Block ausgeführt, sofern vorhanden.

nach oben

3. verschachtelte bedingte Anweisungen

Selbstverständlich lassen sich mehrere bedingte Anweisungen ineinander schachteln:

int alter = 23;
boolean arbeitet = true;
if (alter < 65) {
   if (!arbeitet) 
        System.out.println("wohl Arbeitsloser oder gar Student?");
} else {
   System.out.println("schon in Rente?");
}

Wenn man mehrere bedingte Anweisungen ineinander schachtelt, muss man aufpassen, dass man sich mit den geschweiften Klammern nicht verheddert und sie nicht richtig schließt. Wenn zusätzlich die Klammern weggelassen werden können, dann ist die Gefahr der falschen Zuordnung sehr groß:

int alter = 23;
boolean arbeitet = true;
if (alter < 65)
   if (!arbeitet) 
        System.out.println("wohl Arbeitsloser oder gar Student?");
else 
   System.out.println("schon in Rente?");

Hier würde der Compiler das "else" wieder Erwarten zur inneren if-Block zurechnen, und der Block nicht ausgeführt werden, wenn alter >= 65, sondern wenn arbeitet den Wert "false" besitzt. Dieses Problem existiert auch in C/C++ und wird dangling else genannt. Somit erscheint das Setzen von Klammern bei geschachtelten, bedingten Anweisungen auch hier sinnvoll, um diese Mehrdeutigkeit zu vermeiden!

nach oben

4. der ?-Operator

Eine Sonderform ist die bedingte Auswertung über den ?-Operator. Er wird vornehmlich dazu verwendet, Variablen in Abhängigkeit von einem booleschen Ausdruck verschiedene Werte zuzuweisen. Syntax:

variable = (ausdruck) ? (wert_für_true) : (wert_für_false);

Zuerst wird der Ausdruck ausgewertet. Ergibt er "true", so wird der Variable der erste Wert (hinter dem "?", aber vor dem ":") zugewiesen. Ergibt der Ausdruck "false", so erhält die Variable den zweiten Wert (hinter ":"). Ein Beispiel, um es zu verdeutlichen:

int zahl, x=3;
if (x < 5)
   zahl = 0;
else 
   zahl = 1;

Wenn x kleiner als 5 ist, dann soll der Variable "zahl" der Wert 0 zugewiesen werden, andernfalls 1.
Der extrem schreibfaule Programmierer wird es wohl kürzer halten:

int x=3;
int zahl = (x < 5) ? 0 : 1;

nach oben

5. der switch-Block

Eine besondere Form der Mehrfachverzweigung ist der switch-Block. Mit ihm kann man oftmals eine Reihe von "if" und "else if"-Blöcke ersetzen, welche leicht unübersichtlich werden.
Die Syntax des switch-Blocks lautet:

switch (ausdruck) {
   case Konstante:
      // eine oder mehrere Anweisungen
      break;
   case Konstante:
      // eine oder mehrere Anweisungen
      break;
   ...
   default:
      // eine oder mehrere Anweisungen
}

Die Besonderheit ist hier, dass der Ausdruck (in runden Klammern hinter switch) kein boolescher ist, sondern vom Typ "char", "byte", "short" oder "int" sein muss. Oft wird eine entsprechende Variable übergeben. Der Ausdruck wird ausgewertet und mit den Konstanten verglichen, die wir hinter case angegeben haben (konstante Werte, die bereits beim Kompilieren bekannt sind, also keine Variablen!). Stimmt das Ergebnis des Ausdrucks mit der angegebenen Konstante hinter "case" überein, dann werden die zugehörigen Anweisungen ausgeführt. Danach muss jeweils mit dem Schlüsselwort break der switch-Block verlassen werden. Das Programm setzt nach der schließenden geschweiften Klammer fort. Es können beliebig viele "case"-Blöcke angeschlossen werden! Stimmt keine der angegebenen Konstanten hinter "case" mit dem Ausdruck überein, so wird der Block hinter default ausgeführt. Ein "break" ist hier nicht mehr notwendig, da wir ohnehin am Ende des switch-Blocks angekommen sind. Der default-Block ist optional.
Achtung: Im Gegensatz zu Pascal wird der switch-Block (wie in C++) nicht automatisch verlassen, deshalb ist das Schlüsselwort "break" in der switch-Anweisung nötig! Andernfalls würde das Programm im switch-Block fortgesetzt und auch z.B. der default-Block ausgeführt. Hierin liegt auch der Unterschied zu if-else-Blöcken.

int klasse = 12;
if (klasse >= 1 && klasse <= 4) {
    System.out.println("Grundschule");
} else if (klasse >= 5 && klasse <= 7) {
    System.out.println("Unterstufe");
} else if (klasse >= 8 && klasse <= 10) {
    System.out.println("Mittelstufe");
} else if (klasse >= 11 && klasse <= 13) {
    System.out.println("Oberstufe");
} else {
    System.out.println("geht gar nicht zur Schule");
}

Nun, von Übersichtlichkeit kann hier eigentlich keine Rede mehr sein! Je nach Klasse gehört man entweder zur Grundschule (1-4), Unterstufe (5-7), Mittelstufe (8-10) oder Oberstufe (11-13). Dies kann man leicht in einen switch-Block ummünzen:

int klasse = 12;
switch (klasse) {
    case 1: 
    case 2:
    case 3:
    case 4:
        System.out.println("Grundschule");
        break;
    case 5: case 6: case 7:
        System.out.println("Unterstufe");
        break;
    case 8: case 9: case 10:
        System.out.println("Mittelstufe");
        break;
    case 11: case 12: case 13:
        System.out.println("Oberstufe");
}

Dem switch-Block wird als Parameter eine Integer-Variable namens "klasse" übergeben. Die "case"-Anweisung, bei dem die Konstante mit dem Wert der Variable "klasse" übereinstimmt, wird dann ausgeführt. Am Ende einer jeden "case"-Anweisung muss das Schlüsselwort break stehen: damit wird der switch-Block verlassen und hinter die schließende Klammer gesprungen.
An diesem Beispiel sieht man auch schön, dass man mehrere "case"-Audrücke sowohl nebeneinander als auch jeweils in eine neue Zeile schreiben kann.

Damit ist das Beispiel aber noch nicht komplett: uns fehlt noch die Alternative, die dem "else" entspricht, nämlich dass keine der Alternativen zutrifft. Wie oben erwähnt gibt es dazu innerhalb des switch-Blocks den "default"-Block. Unser komplettes Beispiel lautet nun:

int klasse = 12;
switch (klasse) {
    case 1: case 2: case 3: case 4:
        System.out.println("Grundschule");
        break;
    case 5: case 6: case 7:
        System.out.println("Unterstufe");
        break;
    case 8: case 9: case 10:
        System.out.println("Mittelstufe");
        break;
    case 11: case 12: case 13:
        System.out.println("Oberstufe");
        break;
    default:
        System.out.println("geht gar nicht zur Schule");
}

nach oben

6. Üung

  1. Schreibe eine Methode "tageImMonat", die als Parameter den Monat erhält (als Ganzzahl) und die Anzahl der Tage dieses Monats zurückliefert. Ist hier ein switch-Block sinnvoll?
  2. Schreibe eine Methode "istSchaltjahr", die zu einer Jahreszahl prüft, ob es sich dabei um ein Schaltjahr handelt. Dabei gelten nach dem gregorianischen Kalender folgende Regeln für ein Schaltjahr:
    - ist das Jahr durch 4 teilbar, dann ist es ein Schaltjahr
    - wenn das Jahr durch 100 teilbar ist, dann ist es aber kein Schaltjahr, es sei denn, man kann es auch durch 400 teilen.
    Das bedeutet z.B., dass 1900 kein Schaltjahr ist, 2000 aber schon!
  3. Erweitere die Methode "tageImMonat" dahingehend, dass sie für den Februar abhängig vom Jahr entweder 28 oder 29 Tage zurückgibt. Rufe dazu die Methode "istSchaltjahr" innerhalb von "tageImMonat" auf.

Hilfestellung: die beiden Methode könnten so definiert sein:
- public static int tageImMonat(int monat, int jahr)
- public static boolean istSchaltjahr(int jahr)

nach oben

7. Lösung

public class PruefeDatum {

  public static boolean istSchaltjahr(int jahr) {
    if (jahr % 400 == 0) return true;
    if (jahr % 100 == 0) return false;
    if (jahr % 4 == 0) return true;
    return false;
  }
  
  public static int tageImMonat(int monat, int jahr) {
    int tage;
    switch(monat) {
      case 4: case 6: case 9: case 11:
        tage = 30;
	break;
      case 2:
        if (istSchaltjahr(jahr))
	    tage = 29;
        else
	    tage = 28;
        break;
      default:
        tage = 31;
    }
    return tage;
  }

  public static void main(String[] args) {
    int jahr = 1996, monat = 2;     // Werte zum Testen
    int tage = tageImMonat(monat, jahr);
    System.out.println("Der " + monat + ". Monat im Jahre " + jahr + " hat " + tage + " Tage!");
  }

}

Die Methode "istSchaltjahr" zeigt, dass auch mehrere return-Anweisungen in einer Methode vorkommen können. Die Methode wird dabei bei der ersten return-Anweisung verlassen. Ist das Jahr ohne Rest durch 400 teilbar, dann ist es ein Schaltjahr und es wird "true" zurückgegeben. Ist das Jahr durch 100 ohne Rest teilbar, so ist es kein Schaltjahr (wenn das Jahr durch 400 teilbar gewesen wäre, dann wäre die Methode schon vorher verlassen worden). Trifft auch dies nicht zu und ist das Jahr durch 4 teilbar, handelt es sich wieder um ein Schaltjahr. Wird in keinen der if-Blöcke gesprungen, so wird "false" zurückgegeben. Wichtig: die Funktion muss unbedingt mit "return" verlassen werden. Ein oft gemachter Fehler ist hier, die letzte return-Anweisung einfach wegzulassen. Was aber, wenn dann mal keine der Bedingungen zutrifft?
Die Methode "tageImMonat" sollte leichter zu verstehen sein. Hierfür eignet sich eine switch-Anweisung optimal! Im Falle der Monate April (4), Juni (6), September (9) und November (10) bekommen wir 30 Tage, beim Februar muss hingegen unterschieden werden, ob es sich um ein Schaltjahr handelt oder nicht. Entsprechend wird entweder 28 oder 29 zurückgegeben. In den übrigen Fällen (die in der Mehrzahl sind, weswegen man sich Schreibarbeit spart, wenn man diese vom default-Block behandeln lässt) hat der Monat 31 Tage.

nach oben