Kontrollstrukturen

Kontrollstrukturen: if

int count = getCount();   // Methode im Programm

if (count < 0) {
    System.out.println("Fehler: Zahl ist negativ.");
} else if (count > getMaxCount()) {
    System.out.println("Fehler: Zahl ist zu groß.");
} else {
    System.out.println("Es kommen " + count + " Leute");
}

Generell ist die Syntax des if-Statements:

if (boolescher Ausdruck)
    Statement oder Block;
else if (boolescher Ausdruck)
    Statement oder Block;
else
    Statement oder Block;

Es kann beliebig viele else if-Zweige geben, aber nur einen else-Zweig.

Praxis-Tipp: if immer mit Block

  • Von der Syntax her greift die if-Kontrollstruktur immer für das nächste Statement oder den nächsten Block
  • Schreiben Sie immer einen Block, auch wenn nur ein Statement unter der if-Bedingung stehen soll
// Beim Einfügen eines Statements geht es schnell schief
if (lottogewinn) {
    freuen();
    jobKuendigen();
}

if (lottogewinn)
    freuen();
    jobKuendigen(); // Wird immer ausgeführt!!!

Die Verwendung von if ohne Block ist unter C-Programmierer:innen sehr verbreitet, ist aber dort ebenfalls als gefährlicher Stil anzusehen. Programme sollten immer möglichst defensiv geschrieben sein, um spätere Überraschungen und Probleme zu minimieren. Dazu gehört ein Coding-Stil, bei dem man sich nicht selbst ein Bein stellt.

Kontrollstrukturen: switch

switch (mitarbeiterArt) {

    case MANAGER:
        addEinzelnesBuero();
        addFirmenwagen();
        addSekretaerin();
        break;

    case SENIOR_DEVELOPER:
        addEinzelnesBuero();
        addFirmenwagen();
        break;

    default:
        addGrossraumbuero();
}

Die Variable, die im switch-Statement verwendet wird (hier mitarbeiterArt) muss entweder zuweisungskompatibel mit dem Typ int sein (byte, short, char, int) oder es muss sich um eine Referenz auf eine Enumeration handeln (Enumerationen werden später eingeführt). Es ist nicht möglich im switch-Statement Variablen von anderen Typen zu verwenden, d. h. double, float, boolean etc. sind nicht verwendbar. Der Grund hierfür liegt in der internen Implementierung von switch in der Java Virtual Machine. Seit Java 7 kann man String in einem switch-Statement verwenden (siehe unten).

In den case-Ästen brauchen Sie keinen expliziten Block zu schreiben, d. h. anders als beim if bezieht sich der case-Ast auf alle Statements bis zum nächsten case oder break.

Der default-Ast wird ausgeführt, wenn keine der case-Konstanten gepasst hat.

Bei Java fällt bei eine switch-Statement der Kontrollfluss durch alle case-Statements, die auf das case folgen, bei dem die Bedingung zutraf. Daher muss man immer explizit ein break; hinter die case-Statements schreiben, wenn man dieses Verhalten nicht möchte, was fast immer der Fall ist. Java hat dieses Verhalten von C geerbt, wo es ebenfalls als obskur gilt.

Die allgemeine Syntax des switch-Statements ist:

switch (expr1) {
    case constant2:
        statements;
        break;
    case constant3:
        statements;
        break;
    default:
        statements;
        break;
}

Kontrollstrukturen: switch in Java 7

switch (titel) {
    case "Manager":
        addEinzelnesBuero();
        addFirmenwagen();
        addSekretaerin();
        break;

    case "Senior":
        addEinzelnesBuero();
        addFirmenwagen();
        break;

    default:
        addGrossraumbuero();
        break;
}

Mit Java 7 sind zusätzlich Variablen vom Typ String in switch-Statements erlaubt. Die Beschränkung aller vorhergehenden Java-Versionen auf Variablen, die sich nach int auswerten lassen, ist damit gefallen. Weiterhin kann man aber kein long oder float oder double verwenden.

Kontrollstrukturen: switch ab Java 12

Man merkt switch seine Abstammung von der Programmiersprache C deutlich an. Insbesondere die Verwendung von break und das Fehlen von geschweiften Klammern für die Instruktionen, die ausgeführt werden, wirken nicht „natürlich“. Aus diesen Gründen hat man in Java 12 eine neue Form von switch eingeführt, die syntaktisch anders aussieht und mehr nach „Java schmeckt“.

switch (titel) {
    case "Manager" -> {
        addEinzelnesBuero();
        addFirmenwagen();
        addSekretaerin();
    }
    case "Senior" -> {
        addEinzelnesBuero();
        addFirmenwagen();
    }
    default -> addGrossraumbuero();
}

Wenn nur ein einzelnes Statement in dem Zweig vorkommt, kann man die Klammern auch weglassen, wie es hier im default-Zweig gezeigt ist.

Wenn man im switch nur einen Wert zuweisen möchte, kann man die Kontrollstruktur als Ausdruck verwenden.

// old-school switch (Java < 12)
int tage;
switch (monat) {
    case 2:  tage = 28; break;
    case 4:
    case 6:
    case 9:
    case 11: tage = 30; break;
    default: tage = 31;
}

// Neue Form (Java >= 12)
int days = switch (monat) {
    case 2 -> 28;
    case 4, 6, 9, 11 -> 30;
    default -> 31;
};

Mit Java 14 wurde dann auch noch die Syntax des alten Switch mit : erweitert, sodass man die Konstanten durch Komma (,) getrennt schreiben kann:

// new old-school switch (Java >= 14)
int tage;
switch (monat) {
    case 2:
        tage = 28;
        break;
    case 4, 6, 9, 11:
        tage = 30;
        break;
    default:
        tage = 31;
}

Problematisch wird die Zuweisung eines Wertes in der neuen Form, wenn man einen Block verwenden möchte, um noch etwas zu berechnen. Hierzu führt Java das neue Schlüsselwort yield ein, mit dem man in der neuen case-Variante etwas zurückgeben kann.

Neues Schlüsselwort yield für case

// case mit yield
int days = switch (monat) {
    case 2 -> { int d = 27; d++; yield d; }
    case 4, 6, 9, 11 -> 30;
    default -> 31;
};

Kontrollstrukturen: for-Schleife

// Kindersimulator
for (int i = 0; i < 100; i++) {
    System.out.println("Sind wir schon da?");
    System.out.println("Ist es noch weit?");
}

System.out.println("Jetzt sind wir angekommen!");

Allgemein hat die for-Schleife die Form:

for (Initialisierung; Boolescher Test; Veränderungs-Ausdruck)
    statement oder block;

Wie bei if kann man bei for den Block weglassen, wodurch sich die for-Schleife auf das nächste Statement bezieht. Hiervon ist aber, genauso wie bei if, dringend abzuraten, da es den Source-Code erheblich schlechter lesbar macht.

Man kann in der for-Schleife einzelne Teile weglassen. Daher sind die folgenden Schleifen korrekter Java-Code:

int i = 0;
for (; i < 10; i++) {
    System.out.println("Variable außerhalb deklariert");
}
int j = 0;
for (; j < 10;) {
    j++;
    System.out.println("Variable ausserhalb deklariert");
    System.out.println("Inkrement innerhalb");
}
for (;;) {
    System.out.println("Endlosschleife");
}

Da sie aber verwirrend sind, sollte man solche Formen normalerweise nicht verwenden.

Es ist zusätzlich möglich, in der for-Schleife mehrere Initialisierungen und Veränderungen parallel durchzuführen. Hierzu dient das Komma (,) als Trenner. Aber dieses Konstrukt ist ebenfalls mit Vorsicht zu genießen:

for (i = 0, j = 0; j < 10; i++, j++) { }

Kontrollstrukturen: for-each Schleife

int[] primzahlen = { 2, 3, 5, 7, 11, 13, 17, 19 };

int summe = 0;

for (int primzahl : primzahlen) {
    summe += primzahl;
}

Die hier dargestellte foreach Schleife ist semantisch vollständig identisch mit der klassischen Variante, die einen Index verwendet, um über das Array zu iterieren.

for (int i = 0; i < primzahlen.length; i++) {
    int primzahl = primzahlen[i];
    summe += primzahl;
}

Der Java-Compiler erzeugt sogar aus der foreach-Schleife genau die oben dargestellte klassische Variante.

Kontrollstrukturen: while-Schleife

int i = 0;

while (i < 10) {
    System.out.println("Sind wir schon da?");
    System.out.println("Ist es noch weit?");
    i++;
}

System.out.println("Jetzt sind wir angekommen!");

Die while-Schleife hat die allgemeine Form:

while (Boolescher Ausdruck)
    Statement oder Block;

Der Rumpf der while-Schleife wird nur betreten, wenn die Bedingung des booleschen Ausdrucks wahr ist. Wenn man eine Schleife wünscht, die mindestens einmal durchlaufen wird, muss man die do/while-Schleife wählen. Die Variable auf die getestet wird, sollte richtig initialisiert sein und man sollte nicht vergessen sie im Schleifenrumpf zu verändern, da man andernfalls in einer Endlosschleife landet.

Wenn die Anzahl der Schleifendurchläufe schon im Voraus feststeht, sollten Sie die for-Schleife verwenden. Das hier gewählte Beispiel ist also nicht optimal, da man es besser mit for implementiert hätte. while ist gut geeignet für Schleifen, bei denen währende des Durchlaufens erst festgestellt werden kann, wann der Abbruch erfolgen soll, z. B. bei linearen Suchen etc.

Kontrollstrukturen: do/while-Schleife

int i = 0;

do {
    System.out.println("Sind wir schon da?");
    System.out.println("Ist es noch weit?");
    i++;
} while (i < 10);


System.out.println("Jetzt sind wir angekommen!");

Die do/while-Schleife hat die Form:

do {
  Statement oder Block;
} while (Boolescher Ausdruck);

Der Rumpf der do/while-Schleife wird immer mindestens einmal betreten, da der Test erst am Ende erfolgt – es handelt sich also um eine fußgesteuerte Schleife. Wenn man eine Schleife wünscht, die die Bedingung vorher testet, muss man die while-Schleife verwenden. Die Variable auf die getestet wird sollte richtig initialisiert sein und man sollte nicht vergessen sie im Schleifenrumpf zu verändern, da man andernfalls in einer Endlosschleife landet.

Wenn die Anzahl der Schleifendurchläufe schon im Voraus feststeht, sollten Sie die for-Schleife verwenden.

break, continue und Labels

Zusätzlich Schlüsselworte und Konstrukte

  • break - Springt aus Schleifen und switch-Statements
  • continue - Springt an das Ende der Schleife und führt den Schleifen-Test erneut aus
  • Zusätzlich kann man noch Marken (labels) im Code anlegen. Gibt man die Marke bei break oder continue an, wird nicht die aktuelle Schleife verlassen bzw. erneut ausgeführt, sondern die mit dem Label markierte Schleife
  • Marken können einen beliebigen Namen, gefolgt von einem Doppelpunkt haben, z. B. aussen:

Deklaration einer Klasse

class Ding {

    // Attribut, Instanzvariable, Membervariable
    int x;

    // Methode
    int getX() {
        return x;
    }

    // Methode
    void setX(int newX) {
        x = newX;
    }
}

In Java werden Klassen deklariert, indem man das Schlüsselwort class, gefolgt vom Namen der Klasse schreibt. Im Rumpf der Klassendeklaration werden dann die Attribute und Methoden-Deklarationen geschrieben. Formal ist die Grammatik für die Deklaration einer Klasse:

<class_declaration> ::=
  <modifier> class <name> {
    <attribute_declaration>*
    <constructor_declaration>*
    <method_declaration>*
  }

Die Attribute, innerhalb einer Klasse werden definiert gemäß:

<attribute_declaration> ::=
  <modifier>  <type> <name> [=  <default_value>];

<type> ::= byte | short | int | long | char
           float | double | boolean |  <class>

Analog dazu werden Methoden geschrieben mit:

<method_declaration> ::=
  <modifier> <return_type> <name> (<parameter>*) {
    <statement>*
  }

<parameter> ::= <parameter_type> <parameter_name>,

Copyright © 2025 Thomas Smits