05.02 Fehlerbehandlung

Wie sich ein Fehler in Ihrem Programm auswirken kann, haben Sie bereits im letzten Kapitel gelernt. Dieses Kapitel befasst sich mit dem Abfangen, Werfen und Weiterleiten von Exceptions.

Exceptions abfangen

Erwarten Sie, dass eine bestimmte Programmstelle einen Fehler verursacht, können Sie diese Stelle in einen so genannten try-catch-Block packen. Dadurch können Sie – falls der Fehler auftritt – entsprechend darauf reagieren oder eine Fehlermeldung ausgeben. Zur Demonstration verwenden wir wieder unseren fehlerhaften Integer.parseInt Aufruf aus dem letzten Kapitel.

int value = 0;
try {
  value = Integer.parseInt("A");
}
catch (Exception e) {
  value = Integer.parseInt("2");
}
System.out.println(value);

Alles, was sich im try-Block befindet, wird „versucht“ ausgeführt zu werden. Wenn irgendetwas in diesem try-Block schief läuft, wird selbiger abgebrochen und stattdessen alles im catch-Block ausgeführt. Geht alles gut, wird der try-Block bis zum Ende ausgeführt und der catch-Block ignoriert. In unserem Beispiel wird also versucht die Zeichenkette „A“ in eine Zahl umzuwandeln. Dies gelingt nicht, weshalb stattdessen die Zeichenkette „2“ in eine Zahl umgewandelt wird.

Beachten Sie, dass beim ersten Fehler im try-Block der komplette Block abgebrochen wird. Wenn Sie mehrere Zeilen Code programmiert haben, die unabhängig voneinander einen Fehler verursachen können, verwenden Sie am Besten entweder verschachtelte try-catch-Blöcke oder mehrere hintereinander.

Natürlich ist es recht sinnfrei im catch-Block dem Integer einfach einen anderen, willkürlich gewählten Wert zuzuweisen. In diesem Fall wäre eine entsprechende Fehlermeldung sinnvoller. Dies kann z. B. in Form einer Textnachricht, durch Ausgabe des geworfenen Fehlers, oder mithilfe des Stacktraces geschehen.

int value = 0;
try {
  value = Integer.parseInt("A");
}
catch (Exception e) {
  // Ausgabe des Stacktraces
  e.printStackTrace();
  // Ausgabe einer Meldung
  System.out.println("Konnte Zeichenkette nicht umwandeln");
  // Ausgabe des Fehlers
  System.out.println(e.toString());
}

Beachten Sie, dass exception.printStackTrace(); in den Error-Stream (System.err.print anstelle von System.out.print) und nicht in den normalen Ausgabe-Stream schreibt. Mehr zu Streams erfahren Sie im Kapitel 09. Input / Output (IO).

Im letzten Kapitel haben Sie gelernt, dass es für unterschiedliche Ausnahmen auch unterschiedliche Exceptions gibt. Dies sollten Sie in Ihrem catch-Block auch beachten und nur spezielle Exceptions wie z. B. die NumberFormatException oder die NullPointerException abfangen – und nicht allgemein alle Exceptions.

Falls Sie allerdings wirklich alles abfangen wollen, was geworfen werden kann, dann fangen Sie keine Exception ab, sondern java.lang.Throwable. Alles was geworfen werden kann, muss von Throwable oder einer Kindklasse abgeleitet sein.

try {
  value = Integer.parseInt("A");
}
catch (NumberFormatException e) {
  e.printStackTrace();
}

Selbstverständlich können Sie auch mehrere Exceptions abfangen. Ergänzen wir unser Beispiel um eine ArithmeticException. Diese wird geworfen, wenn bspw. durch 0 geteilt wurde:

int value = 0;
try {
  value = Integer.parseInt("0");
  value = 25 / value;
}
catch (NumberFormatException e) {
  System.out.println("Keine Zahl");
}
catch (ArithmeticException e) {
  System.out.println("Geteilt durch 0");
}

Manchmal ist es sogar zwingend notwendig, dass ein try-catch-Block eingesetzt wird. Nämlich immer dann, wenn die aufzurufende Methode in ihrem Methodenkopf angibt, dass eine Exception geworfen werden könnte. Dabei handelt es sich um sogenannte checked exceptions. Welche Methoden welche Exceptions werfen können, entnehmen Sie der Dokumentation der Methode in der Java-API-Dokumentation.

finally

Über die finally-Klausel können Sie nach dem letzten catch-Block einen weiteren Block definieren, der auf jeden Fall ausgeführt wird. Auf jeden Fall bedeutet hier, dass alles im finally-Block ausgeführt wird, auch wenn im try-Block …

  • … eine Exception geworfen wird, die durch catch abgefangen wurde
  • … eine Exception geworfen wird, die nicht durch catch abgefangen wurde
  • … ein Wert über das return-Statement zurück gegeben wurde
  • … eine Exception nach oben durchgereicht wurde (hierzu später im Kapitel mehr)

Der finally-Block wird allerdings nicht ausgeführt, wenn das Programm über System.exit im try-Block beendet wurde.

try {
  Integer.parseInt("A");
  System.out.println("Kein Fehler");
}
catch (NumberFormatException e) {
  System.out.println("Fehler");
}
finally {
  System.out.println("Egal!");
}

Exceptions weiterleiten

Nicht immer ist es sinnvoll einen Fehler selbst abzufangen. Z. B. wenn Sie selbst eine Library schreiben. Dann sollte derjenige, der Ihre Library verwendet, auf die evtl. geworfenen Fehler selbst reagieren können. Für dieses Szenario gibt es die throws-Klausel. Setzen Sie diese Klausel zwischen die runde, geschlossene Klammer und der offenen, geschweiften Klammer in Ihrem Methodenkopf. Dort können Sie jetzt Exceptions definieren, die unter Umständen von dieser Methode geworfen werden.

Als Beispiel dient uns der Aufruf Class.forName(), welcher eine Klasse anhand ihres Namens lädt und eine ClassNotFoundException werfen kann.

public class RuntimeFehler {

  public static void main(String[] args) {
		
    try {
      System.out.println(loadClass("java.lang.String").getSimpleName());
    }
    catch (ClassNotFoundException cnfe) {
      cnfe.printStackTrace();
    }
  }
	
  public static Class loadClass(String className) throws ClassNotFoundException {
    return Class.forName(className);
  }
}

Dadurch sparen Sie sich den try-catch-Block und sorgen dafür, dass die aufrufende Klasse über einen ggf. auftretenden Fehler informiert wird. Wollen Sie mehrere Exceptions „nach oben durchreichen“ lassen, können Sie diese durch Kommata trennen.

Eine (eigene) Exception werfen

Natürlich können Sie auch selbst eine Exception werfen. Hierzu verwenden Sie das throw-Schlüsselwort gefolgt von einem Objekt der gewünschten Exception.

throw new NumberFormatException("Fehler");

Zum Abschluss dieses Kapitels werfen Sie eine eigene Exception. Hierzu programmieren Sie zuerst die Exception-Klasse – z. B. so:

package de.exception;
public class MyFirstException extends Exception {

  public MyFirstException() {
    super();
  }
	
  public MyFirstException(String message) {
    super(message);
  }
}

Anschließend benötigen Sie noch eine Methode, die diese Exception wirft, und eine weitere Methode, die diese Methode aufruft:

package de.test;
import de.exception.MyFirstException;

public class RuntimeFehler {

  public static void main(String[] args) {
		
    try {
      RuntimeFehler rf = new RuntimeFehler();
      rf.throwMyFirstException(true);
    } 
    catch (MyFirstException e) {
      e.printStackTrace();
    }
  }
	
  public void throwMyFirstException(boolean throwTheException) throws MyFirstException {
		
    if (throwTheException) {
      throw new MyFirstException("Das ist ein Fehler!");
    }
    System.out.println("Alles ohne Probleme gelaufen");
  }
}

Somit haben Sie Ihre erste, eigene Exception geschrieben und geworfen.

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.