Kapitel 10
Ausnahmen und Enums
Bisher brach ein Programm einfach ab, wenn etwas schiefging, etwa bei Integer.parseInt("abc"). Mit Ausnahmen (Exceptions) fängst du solche Fehler ab und reagierst darauf, oder du meldest selbst einen Fehler. Dazu kommen Enums: Typen mit einer festen Liste von Werten, wie die Phasen einer Ampel.
Ausnahmen abfangen
Code, der schiefgehen kann, steht in einem try-Block. Fliegt dort eine Ausnahme, springt Java sofort in den passenden catch-Block. Der Rest des try-Blocks wird übersprungen. Danach geht es normal weiter.
try {
int value = Integer.parseInt(text);
} catch (NumberFormatException e) {
// text was not a number
}
e.getMessage() liefert die Fehlermeldung. Ein finally-Block läuft immer, ob es geklappt hat oder nicht, sogar wenn im try ein return steht. Dort gehört hin, was auf jeden Fall passieren muss, zum Beispiel eine Verbindung schließen.
public class Main {
public static void main(String[] args) {
String[] inputs = {"42", "abc", "7"};
int sum = 0;
for (String input : inputs) {
try {
int value = Integer.parseInt(input);
sum += value;
System.out.println("Added " + value);
} catch (NumberFormatException e) {
System.out.println("Not a number: " + input);
} finally {
System.out.println("Checked " + input);
}
}
System.out.println("Sum: " + sum);
}
}Ausnahmen werfen und eigene Exceptions
Mit throw new IllegalArgumentException("No values"); meldest du selbst einen Fehler. Die Methode endet an dieser Stelle, der Aufrufer bekommt die Ausnahme.
Eine eigene Exception ist eine Klasse, die von Exception erbt. Die Meldung gibt ihr Konstruktor mit super(...) weiter. Es gibt zwei Sorten:
- Checked: alles, was von
Exceptionerbt. Der Compiler verlangt, dass du sie abfängst oder mitthrowsim Methodenkopf ankündigst:void buyApple() throws OutOfStockException. - Unchecked: alles, was von
RuntimeExceptionerbt, z. B.IllegalArgumentExceptionundNumberFormatException. Hier verlangt der Compiler nichts.
Ein throws reicht die Ausnahme an den Aufrufer weiter. Dann muss sich der darum kümmern.
class OutOfStockException extends Exception {
public OutOfStockException(String product) {
super(product + " is out of stock");
}
}
class Shop {
private int apples = 2;
public void buyApple() throws OutOfStockException {
if (apples == 0) {
throw new OutOfStockException("Apple");
}
apples--;
}
}
public class Main {
public static double divide(double a, double b) {
if (b == 0) {
throw new IllegalArgumentException("Cannot divide by 0");
}
return a / b;
}
public static void main(String[] args) {
try {
System.out.println(divide(10, 4));
System.out.println(divide(1, 0));
System.out.println("This line never runs");
} catch (IllegalArgumentException e) {
System.out.println("Error: " + e.getMessage());
}
Shop shop = new Shop();
for (int i = 1; i <= 3; i++) {
try {
shop.buyApple();
System.out.println("Bought apple " + i);
} catch (OutOfStockException e) {
System.out.println(e.getMessage());
}
}
}
}Enums
enum Season { SPRING, SUMMER, AUTUMN, WINTER } legt einen Typ mit genau vier Werten an. Eine Variable vom Typ Season kann nichts anderes enthalten, Tippfehler meldet der Compiler.
- Enum-Werte vergleichst du mit
==. values()liefert alle Werte als Array, in der Reihenfolge der Deklaration.ordinal()ist die Position, ab 0.- Als Text ergibt ein Wert seinen Namen, z. B.
SUMMER. - Im
switchschreibst du die Werte ohne Klassennamen:case SUMMER ->.
Ein Enum kann wie eine Klasse Attribute, einen Konstruktor und Methoden haben. Die Werte in Klammern hinter jeder Konstante gehen an den Konstruktor: EURO(100).
enum Season {
SPRING, SUMMER, AUTUMN, WINTER
}
enum Coin {
CENT(1), TEN_CENTS(10), EURO(100);
private final int cents;
Coin(int cents) {
this.cents = cents;
}
public int getCents() {
return cents;
}
}
public class Main {
public static void main(String[] args) {
Season now = Season.SUMMER;
System.out.println(now);
System.out.println("Position: " + now.ordinal());
System.out.println(now == Season.SUMMER);
for (Season s : Season.values()) {
System.out.print(s + " ");
}
System.out.println();
String advice = switch (now) {
case SUMMER -> "Sunscreen";
case WINTER -> "Gloves";
default -> "Umbrella";
};
System.out.println(advice);
int total = 0;
for (Coin c : Coin.values()) {
total += c.getCents();
}
System.out.println("All coins: " + total + " cents");
}
}Typische Fehler
Checked Exception nicht behandelt
Rufst du eine Methode mit
throws OutOfStockExceptionauf, ohnetry/catchund ohne eigenesthrows, meldet der Compilerunreported exception OutOfStockException; must be caught or declared to be thrown.Falsche Reihenfolge der catch-Blöcke
Java nimmt den ersten passenden
catch. Stehtcatch (Exception e)vorcatch (NumberFormatException e), kann der zweite nie greifen. Der Compiler meldetexception NumberFormatException has already been caught. Die speziellere Ausnahme gehört nach oben.Leerer catch-Block
catch (Exception e) { }verschluckt jeden Fehler, auch solche, mit denen du nicht gerechnet hast. Fang nur die Ausnahme ab, die du erwartest, und reagiere sichtbar darauf.Durch 0 teilen
Bei
intwirft10 / 0eineArithmeticException. Beidoublegibt es keine Ausnahme:10.0 / 0ergibtInfinity. Deshalb prüftdivideim Beispiel selbst und wirft eine Ausnahme.