Wie du eigene Java Exceptions entwickelst.

Keylearnings:

  • Was sind checked und unchecked Exceptions
  • Wie du eine eigene checked Exception entwickelst.
  • Wie du eine eigene unchecked Exception entwickelst.
  • VIDEOERKLÄRUNG Checked und Unchecked Exceptions

Wir alle haben Ecken und Kanten!

Jeder Jeck ist anders! (Kölner Sprichwort!)

Das gilt auch für unserer Programme!

Und noch mehr für unserer Anwender!

Jeder Anwender ist nämlich ein Unikat!

Damit müssen wir lernen umzugehen.

Wie das geht möchte ich dir in diesem Artikel zeigen.

Wir haben bereits hier gelernt wie man auf Ausnahmesituationen, den sogenannten Exceptions, reagiert.

Auch wenn JAVA bereits eine große Anzahl von fertigen Standard-Exceptions enthält, gibt es Situation, in denen wir auf Fehler individuell reagieren wollen.

Hierfür müssen wir eigene Exceptions entwickeln. Und wie das geht, schauen wir uns jetzt an.

Aber müssen wir eigentlich jede mögliche Ausnahme abfangen?

Checked und unchecked Exceptions

Nein! Denn JAVA unterscheidet zwischen zwei Arten von Exceptions.

  1. Der unchecked Exception.
  2. Der checked Exception.

Grob gesagt betreffen die unchecked Exceptions den Programmierer und die checked Exceptions den Anwender.

Bei unchecked Exceptions handelt es sich um Bugs im Programm, die der Programmierer im Quellcode abfangen muss und deshalb nicht zwingend während der Laufzeit des Programms über eine try-catch Kontrollstruktur behandeln muss.

Ein Klassiker dieser Art ist die ArrayIndexOutOfBoundsException, die beispielsweise auftritt, wenn man mit negativem Index auf ein Array-Element zugreift.

Bei checked Exceptions handelt es sich hingegen um Ausnahmen, die der Anwender herbeiführt und zwingend mit einer try-catch Kontrollstruktur abgefangen werden müssen.

Ein häufig anzutreffendes Beispiel für eine checked Exception ist die Auswahl einer Datei, auf die der Anwender keine Zugriffsrechte hat.

Wo liegt der programmiertechnische Unterschied?

Wie wir bereits hier gesehen haben, ist eine Exception die Instanz einer speziellen Exception Klasse (z.b. der Klasse ArithmeticException), die wir mit Hilfe es Schlüsselwortes new erzeugen.

Hierbei ist jede Exception-Klasse von einer Oberklasse abgeleitet.

Und genau hierin unterscheidet sich eine checked von einer unchecked Exception.

Eine checked Exception erbt von der Oberklasse Exception wohingegen eine unchecked Exception von der Oberklasse RuntimeException erbt.

checked und unchecked Java Exception

Diese Hierarchie müssen wir beim entwickeln unserer eigenen Ausnahmen im Hinterkopf behalten.

Wie du eine checked Exception erstellst

Beginnen wir mit einer eigenen checked Exception.

Unsere Methode berechneDivision aus dem letzten Artikel hat noch einen Bug!

Der Quotient zweier Ganzzahlen ist nicht zwingend wieder eine Ganzzahl. So ist z.B. 3:2=1.5.

Diesen Fall wollen wir mit einer eigens entwickelten Exception abfangen.

Da es sich hierbei um eine checked Exception handeln soll, benötigen wir eine von Exception abgeleitete Klasse.

Beginnen wir also mit einer Klasse namens EigeneCheckedException, die über das Schlüsselwort extends von der Oberklasse Exception erbt.

1: public class EigeneCheckedException extends Exception{

2:  public EigeneCheckedException(){
3:   super("Ich bin deine Exception");
4:  }

5:  public EigeneCheckedException(String fehlermeldung){
6:   super(fehlermeldung);
7:  }
8: }

Die Klasse hat zwei Konstruktor-Funktionen, die beide jeweils mit Hilfe des Schlüsselworts super den Konstruktor der Oberklasse aufrufen, dem wir als Parameter die Zeichenkette übergeben, die beim auslösen der Exception ausgegeben werden soll.

In den Zeilen zwei bis vier ist der Standard-Konstruktor definiert und in Zeile fünf bis sieben ein zweiter Konstruktor, mit dem sich eine individuelle Fehlermeldung festlegen lässt.

Wie du deine Exception auslöst.

Sobald der in der Methode berechneDivision berechnete Quotient keine Ganzzahl ist, soll die Exception geworfen werden.

Passen wir unsere Methode entsprechend an.

1: public static int berechneDivision(double a, double b) throws EigeneCheckedException{
2:   int c = (int) (a/b);
3:   double d = a/b;
4:   if (c!=d){
5:     throw new EigeneCheckedException("Keine Ganzzahl");
6:   }
7:   return c;
8:}

Als Übergabeparameter übergeben wir Zähler und Nenner des zu berechnenden Quotienten.

Das Ergebnis der Division speichern wir einmal als Integerwert und anschließend in einer Variable vom Typ double.

Unterscheiden sich diese Ergebnisse voneinander, wissen wir, dass es sich bei dem Quotienten um keine Ganzzahl handelt und schmeißen in Zeile fünf unsere Ausnahme.

Das Schlüsselwort throws

Abfangen wollen wir unsere Exception beim Aufruf der Methode berechneDivision.

Hierzu ist es bei einer checked Exception zwingend notwendig die geworfene Ausnahme über das Schlüsselwort throws aus der Methode an den Aufruf zu übergeben.

Vergessen wir die throws Anweisung im Funktionskopf, lässt sich unser Programm im Falle einer checked Exception nicht übersetzen.

Um einen Probelauf starten zu können, müssen wir den Methodenaufruf in eine try catch Kontrollstruktur packen.

1: try{
2:    System.out.println(berechneDivision(10,3));
3: }catch(EigeneCheckedException e){
4:    System.out.println(e.toString());
5:}
6:

Im Fall, dass der Quotient, aus den an die Methode berechneDivision übergebenen Zahlen, keine Ganzzahl ist, bewirkt der Aufruf der Methode eine EigeneCheckedException Exception, die in Zeile drei im catch Zweig abgefangen wird.

Um eine vernünftig aussehende Bildschirmausgabe zu erzeugen, konvertieren wir in Zeile vier die Exception in eine Zeichenkette.

Jetzt aber ans Eingemachte! Zwei Testfälle!

Testfall 1) a = 10 und b = 2

In diesem Fall gilt 10:2 = 5. Der Quotient ist also eine Ganzzahl, weshalb keine Exception geworfen wird.

Die Bildschirmausgabe ist in diesem Fall:

5

Testfall 2) a = 10 und b = 3

Jetzt haben wir die Situation, dass der Quotient 10:3 keine ganze Zahl ist und die Methode berechneDivision eine EigeneCheckedException Exception wirft.

Daher ist die Bildschirmausgabe diesmal:

EigeneCheckedException: Keine Ganzzahl

Die Zeichenkette Keine Ganzzahl, ist die Zeichenkette die wir beim Aufruf des Konstruktors der Oberklasse Exception als Parameter übergeben haben.

Entwicklung einer unchecked Exception

Zu guter letzt wollen wir uns noch ansehen, wie man eine unchecked Exception implementiert.

Wir hatten oben bereits besprochen, dass eine unchecked Exception von der Oberklasse RuntimeException abgeleitet werden muss, was wir wie gewohnt mittels des Schlüsselworts extends machen.

1: public class EigeneUncheckedException extends RuntimeException{
	
2:  public EigeneUncheckedException(){
3:	super("Deine EigeneUncheckedException");
4:  }
	
5:  public EigeneUncheckedException(String fehlermeldung){
6:	super(msg);
7:     }

8:}

Ansonsten ist die Klasse analog zu EigeneCheckedException von oben implementiert.

Wieder haben wir zwei Konstruktor-Funktionen, welche den Konstruktor der Oberklasse mit einem String-Argument aufrufen, welches die Bildschirmausgabe beim Auftreten der Exception festlegt.

Der Unterschied macht sich erst im Gebrauch bemerkbar!

Um das testen zu können, müssen wir in der Methode berechneDivision() von oben das Wörtchen EigeneCheckedException durch EigeneUncheckedException ersetzen.

1: public static int berechneDivision(double a, double b) throws EigeneUncheckedException{
2:    int c = (int) (a/b);
3:    double d = a/b;
4:    if (c!=d){
5:        throw new EigeneUncheckedException("Keine Ganzzahl");
6:    }
7:	return c;
8:}

Auf geht’s! Machen wir die gleichen Probeläufe wie bei unserer checked Exception.

Testfall 1) a = 10 und b = 2

Nach wie vor ist 10:2 = 5 eine ganze Zahl, weshalb keine Exception geworfen wird.

Und in der Tat ist die Bildschirmausgabe des Programms keine Überraschung!

5

Testfall 2) a = 10 und b = 3

Der Quotient aus 10:3 ist keine ganze Zahl, weshalb eine Exception geworfen wird.

Als Bildschirmausgabe erhalten wir:

EigeneUncheckedException: Keine Ganzzahl

Moment! Das ist genau das gleiche Ergebnis wie bei der checked Exception.

Du bist enttäuscht. Nicht wahr? Du fragst dich wo zum Geier ist der Unterschied?

Der Unterschied ist die Ebene, auf der die Exception geprüft wird.

Bei einer checked Exception wird beim Kompilieren geprüft, ob die Ausnahme im Programmcode abgefangen wird.

Eine nicht abgefangene checked Exception führt dazu, dass wir unser Programm nicht kompilieren können.

Im Unterschied hierzu, wird eine unchecked Exception erst während der Laufzeit des Programms überprüft.

Die Konsequenz hieraus ist, dass wir unseren Methoden-Aufruf bei einer unchecked Exception nicht in eine try catch Kontrollstruktur einbetten müssen.

Schauen wir uns auch das in der Praxis an.

berechneDivision(10,3)

Wir rufen die Methode erneut mit den Parametern a=10 und b=3 auf. Diesmal verzichten wir allerdings darauf die Exception abzufangen.

Starten wir das Porgramm und sehen uns das Ergebnis an. Da 10:3 keine Ganzzahl ist, wird eine Exception ausgelöst.

Die Bildschirmausgabe ist:

unchecked Exception

Ojee, das Programm ist abgestürzt! Eine checked Exception hätte uns gezwungen den Fehler abzufangen.

Im folgenden Video zeige ich dir wie du die einzelnen Schritte in Eclipse umsetzt.

Unterschiede im Überblick!

Zum Abschluss möchte ich, der Übersichtlickeit wegen, die wesentlichen Eigenschaften der beiden Exception Arten gegenüberstellen.

Checked Exeption

  • Ist abgeleitet von der Oberklasse Exception.
  • Wird beim kompilieren des Programms geprüft.
  • Muss mit einem try catch abgefangen werden.

Unchecked Exception

  • Ist abgeleitet von der Oberklasse RuntimeException.
  • Wird während der Laufzeit geprüft.
  • Führt ohne ein try catch zu einem Programmabsturz.

Fazit: In diesem Artikel hast du die beiden Exception-Arten, die es in Java gibt kennengelernt. Da wir durch Exceptions in der Regel keine Programm-Bugs abfangen, sondern auf Ausnahmen reagieren wollen, die der Anwender durch sein verhalten erzeugt, ist die Entwicklung von checked Exceptions in der Praxis häufiger anzutreffen.

Wie immer freue ich mich über deine Fragen im Kommentarbereich!

Hat dir der Artikel gefallen? Dann folge uns doch am besten gleich auf Facebook!

Hallo ich bin Kim und ich möchte ein großer Programmierer werden. Machst du mit?

Kommentare (2)

  • Antworte

    Die Vererbungspfeile in der Grafik oben sind falsch herum! Die gehen immer von der erbenden zur vererbenden Klasse.
    Sonst aber wieder ein sehr gelungener Beitrag

    • Ja, du hast recht. Vererbung findet immer von oben nach unten statt.

Hinterlasse ein Kommentar