Testen wichtiger Methoden von Throwable.

In diesem Beispiel können Sie die Wirkung einiger wichtiger Methoden der allgemeinen Oberklasse Throwable sehen. Wenn eine Exception geworfen wird, können Sie dieser eine Fehlermeldung im Konstruktor mitgeben. Diese Meldung kann über die Methode getMessage() abgerufen werden. Die toString-Methode bereitet das Exception-Objekt wie bekannt als String auf und die Methode printStackTrace() gibt den Stacktrace der Methode zurück.

public class ExceptionTest1 { public static void main(String [] args) { try { throw new Exception("Ich bin eine Ausnahme."); } catch (Exception e) { System.out.println("Ausnahme wurde abgefangen!"); System.out.println("e.getMessage(): " + e.getMessage()); System.out.println("e.toString(): " + e.toString()); System.out.println("e.getStackTrace()" + e.getStackTrace()); } } } /* Ausgabe: Ausnahme wurde abgefangen! e.getMessage(): Ich bin eine Ausnahme. e.toString(): java.lang.Exception: Ich bin eine Ausnahme. e.getStackTrace()[Ljava.lang.StackTraceElement;@15e8f2a0 */

Eine Ausnahme erneut werfen

Manchmal kann es sinnvoll sein, eine Ausnahme zu behandeln und im catch-Block noch einmal zu werfen, damit sie von einem übergeordeten catch-Block auch noch behandelt werden kann.Das ist interessant, wenn verschiedene Ausnahmen teilweise gleich behandelt werden müssen. Dazu rufen Sie im catch einfach noch einmal throw auf.

Sie sehen im Quellcode, dass die Methode, welche die Exception auslöst, diese nicht behandelt. Dies geschieht erst in der übergeordneten Methode und in der main-Methode. Aus diesem Grund gibt die Methode, die die Ausnahme nicht behandelt, mit der Anweisung throws an, dass sie eine Ausnahme werfen könnte.

An der Ausgabe des StackTrace können Sie verfolgen, wo eine Ausnahme ausgelöst wurde.

public class ExceptionTest2 { public void fehlerhafteMethode() throws Exception { System.out.println("Die fehlerhafte Methode startet."); throw new Exception("Ausnahme!"); } public void ersteAusnahmeBehandlung() throws Exception { try { fehlerhafteMethode(); } catch (Exception e) { System.out.println("ersteAusnahmebehandlung e.printStackTrace(): "); e.printStackTrace(); throw e; } } public static void main(String [] args) { ExceptionTest2 exceptionTest2 = new ExceptionTest2(); try { exceptionTest1.ersteAusnahmeBehandlung(); } catch (Exception e) { System.out.println("main-Methode e.printStackTrace(): "); e.printStackTrace(); } } } /* Die fehlerhafte Methode startet. ersteAusnahmebehandlung e.printStackTrace(): main-Methode e.printStackTrace(): java.lang.Exception: Ausnahme! at ExceptionTest2.fehlerhafteMethode(ExceptionTest2.java:6) at ExceptionTest2.ersteAusnahmeBehandlung(ExceptionTest2.java:11) at ExceptionTest2.main(ExceptionTest2.java:22) java.lang.Exception: Ausnahme! at ExceptionTest2.fehlerhafteMethode(ExceptionTest2.java:6) at ExceptionTest2.ersteAusnahmeBehandlung(ExceptionTest2.java:11) at ExceptionTest2.main(ExceptionTest1.java:22) */

NumberFormatException

Im folgenden Programm sehen Sie, wie die parse-Methode der Hüllenklassen zu den elementaren Datentypen funktionieren. Die Methoden wandeln einen String in einen entsprechenden Zahlendatentyp um. Integer.parseInt("123") würde also als Ergebnis den Integer 123 erzeugen. Der folgende Aufruf führt zu einem Fehler Integer.parseInt("123ABC"), und zwar zu einer NumberFormatException, da es sich um keine gültige Zahl handelt.

Sie sehen im Beispiel, dass erst die NumberFormatExceptions abgefangen werden, und dann in einem weiteren catch-Block die übrigen Exceptions. Ändern Sie ruhig mal die Werte und lösen Sie in anderen parse-Methoden die Exception aus.

public class NumberFormatExceptionTest { public static void main(String [] args) { try { double d = Double.parseDouble("2.23"); System.out.println(d); float f = Float.parseFloat("2.33"); System.out.println(f); // Hier wird die Exception ausgelöst! int i = Integer.parseInt("123ABC"); System.out.println(i); short s = Short.parseShort("3"); System.out.println(s); } catch (NumberFormatException e) { System.out.println("Falsches Zahlenformat: " + e); } catch (Exception e) { System.out.println("Sonstige Ausnahme: " + e); } } } /*Ausgabe: 2.23 2.33 Falsches Zahlenformat: java.lang.NumberFormatException: For input string: "123ABC"*/ 

IOException auffangen

Fehler der Klasse IOException treten im Zusammenhang mit Dateien und Streams auf, z.B. wenn eine zu lesende Datei nicht gefunden werden kann. Im konkreten Fehlerfall wird eine Unterklasse von IOException geworfen wie z.B. FileNotFoundException.

Im folgenden Codeausschnitt sehen Sie ein Programm, das versucht eine nicht vorhandene Datei zum Lesen zu öffnen. Da die Datei nicht gefunden werden kann, wird die beschriebene FileNotFoundException geworfen. Diese Exception muss behandelt werden, sonst beschwert sich der Compiler. Natürlich kann die Exception auch anders als im Beispiel an eine andere Methode weitergereicht werden.

Sie sehen, dass im Programm als erstes die FileNotFoundException aufgefangen wird. Der nächste catch-Block fängt alle anderen Unterklassen von IOException. Im dritten catch werden sämtliche anderen Ausnahmen behandelt. Sie müssen nicht immer eine solche Struktur aus mehreren catch auffangen, sondern nur, wenn Sie befürchten, dass entsprechende Fehler auftreten könnten.

import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class FileNotFoundExceptionTest { public static void main(String [] args) { String filename = "binNichtDa.txt"; File file = new File(filename); try { // Hier wird die Ausnahme ausgelöst. FileInputStream fileInputStream = new FileInputStream(file); } catch (FileNotFoundException e) { System.out.println("Die folgende Datei wurde nicht gefunden: " + filename); } catch (IOException e) { System.out.println("Bei der Verarbeitung folgender Datei ist ein Fehler aufgetreten: " + filename); } catch (Exception e) { System.out.println("Sonstiger Fehler."); } } } 

Ausnahme weiterreichen

Exceptions, die in einer Methode ausgelöst werden, müssen nicht nicht in dieser Methode durch eine try-catch-Anweisung aufgefangen werden. Sie können an die aufrufende Methode weitergegeben werden. Dies ermöglicht den Aufbau mehrerer Aufrufebenen in Ihrem Programm. So können Sie viele Ausnahmen an einer zentralen Stelle fangen. Das macht Ihren Code wesentlich besser lesbar und wiederverwertbar, was wichtige Prinzipien der objektorientierten Programmierung sind.

Wenn Sie eine Methode weiterreichen, muss dies in der Deklaration der Methode mit throws vereinbart werden. Hier können auch mehrere Exceptionklassen durch Komma voneinander getrennt angegeben werden.

Einfaches Beispiel: Ihr Programm besteht aus folgenden Klassen:

  • Eine Dialogklasse, die sich um die Ausgabe im Terminal kümmert
  • Eine Klasse1, die eine Datei lesen möchte.
  • Eine Klasse2, die ebenfalls eine Datei lesen möchte.

In beiden Klassen kann die oben beschriebene FileNotFoundException ausgelöst werden. Sie können sie also zwei Mal behandeln und dem Benutzer eine Fehlermeldung mittels System.out.println() ausgeben. Dadurch werden Ihre Klassen jedoch schwerer wiederverwendbar, denn in einer grafischen Oberfläche können Sie nicht mehr ohne Weiteres eingesetzt werden. Dort gibt es kein System.out.println(). Sie müssten den Code dieser beiden Klassen ändern.

Klüger ist es, wenn Sie die Methoden, welche die Dateien lesen, die Exceptions an die Dialogklasse weiterreichen lassen. Diese entscheidet dann, was genau geschehen soll. Sie sehen also, dass Sie die Fehler an eine höhere Verarbeitungsschicht weitergeben und dass sie dort zentral verarbeitet werden.

Schauen Sie sich die Klassen im folgenden Codeausschnitt genau an. Sie sehen, dass die Methoden in den beiden Klassen, die die Datei lesen, keine Fehlerbehandlung durchführen, sondern den Fehler mittels throws weiterreichen. Behandelt wird der Fehler erst in der main-Methode, also in der Benutzeroberfläche.Somit sind die beiden Klassen DateiLesenKlasse1 und DateiLesenKlasse2 in einer anderen Benutzeroberfläche wie z.B. Swing oder JSF wiederverwertbar. Das ist ein ganz wichtiges Konzept: Behandeln Sie die Fehler dort, wo es notwendig ist. Und das ist nicht unbedingt auf unterster Ebene.

import java.io.FileNotFoundException; import java.io.IOException; public class FileNotFoundExceptionTest2 { public static void main(String [] args) { String filename = "binNichtDa.txt"; DateiLesenKlasse1 dateiLesenKlasse1 = new DateiLesenKlasse1(); DateiLesenKlasse2 dateiLesenKlasse2 = new DateiLesenKlasse2(); try { dateiLesenKlasse1.liesDatei(filename); dateiLesenKlasse2.liesDatei(filename); } catch (FileNotFoundException e) { System.out.println("Die folgende Datei wurde nicht gefunden: " + filename); } catch (IOException e) { System.out.println("Bei der Verarbeitung folgender Datei ist ein Fehler aufgetreten: " + filename); } catch (Exception e) { System.out.println("Sonstiger Fehler."); } } } import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class DateiLesenKlasse1 { public void liesDatei(String filename) throws IOException { File file = new File(filename); // Hier wird die Ausnahme ausgelöst. FileInputStream fileInputStream = new FileInputStream(file); // Weitere Verarbeitungen wie Lesen aus der Datei. } } import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class DateiLesenKlasse2 { public void liesDatei(String filename) throws IOException { File file = new File(filename); // Hier wird die Ausnahme ausgelöst. FileInputStream fileInputStream = new FileInputStream(file); // Weitere Verarbeitungen wie Lesen aus der Datei. } }

Hinweise zu Exceptions

Verwenden Sie nicht zu viele try-catch-Blöcke, da Ihr Code dadurch unübersichtlich wird. Fangen Sie Fehler ab, wenn der Compiler sich beschwert oder wenn Sie ganz sicher davon ausgehen, dass eine Exception auftreten könnte. Auch falls Sie beim Testen auf eine nicht behandelte Exception stoßen, müssen Sie diese auffangen.

Laden ...
Fehler!