10. Ausnahmen (Exceptions)

10. Ausnahmen (Exceptions)

Notwendigkeit von Ausnahmebehandlung

Beim Ausführen von Programmen können Ereignisse auftreten die nicht zum normalen Ablauf gehören und trotzdem sinnvoll behandelt werden müssen. Nicht normaler Ablauf bedeutet hier, dass diese Ausnahmen nicht direkt von den Eingaben oder Anweisungen des Programmcodes hervorgerufen werden. Beispiele hierfür sind

  • Division einer Ganzzahl durch Null
  • Lesen aus einer Datei die nicht vorhanden ist
  • Fehlende Resourcen wie Hauptspeicher
  • Verlust einer Netzwerkverbindung
  • Feldüberlauf etc.

Es ist guter (und erforderlicher) Programmierstil mit solchen Ausnahmen umgehen zu können und sie angemessen behandeln.

Zur Erkennung und Behandlung solcher abnormaler Situationen gibt es eine Reihe von Möglichkeiten die von der gewählen Technologie abhängen können:

  • Präventives Kontrollieren von potentiellen Aushnahmen
    • Beispiel: Prüfung von Eingabewerten oder Zwischenwerten von Berechnungen auf potentielle Sonderdebingungen wie Nullwerte oder Feldgrenzenüberläufe
  • Fehlercoderückgaben bei jedem Operationsaufruf
    • Unixkommandos geben Beispielsweise immer einen Fehlerwert mit aus. Ist dieser ungleich Null liegt ein bestimmter Fehler (Ausnahme) vor
  • Unterbrechungsgesteuerte (Interrupthandling) Ausnahmen
    • Der reguläre Programmablauf wird verlassen und einer anderen Stelle fortgesetzt.

Bei der Behandlung von Ausnahmen steht der Entwickler in der Regel vor dem folgenden Zielkonflikten

Zielkonflikte
niedriger Implementierungsaufwand mit hohem Fehlerrisiko hoher Implementierungsaufwand mit geringem Fehlerrisiko
übersichlicher aber wenig robuster Code unübersichtlicher (und fehleranfälliger) Code mit vielen Präventivüberprüfungen

Moderne Programmiersprachen und -umgebungen bieten eine Infrastruktur um die diese Zielkonflikte zu mildern. Die Technologie einer Rückgabe von Fehlerzuständen bei jeder einzelnen Operation macht es seht unwahrscheinlich, dass ein Entwickler bei jeder einzelnen Operation diesen Wert auswertet und individuelle Maßnahmen ergreift.

Die Programmiersprache Java unterstützt den Entwickler mit einem mächtigen Konzept zur Ausnahmebehandlung (Exceptionhandling) mit dem man Unterstützung in den folgenden Bereichen bekommt:

  • Möglichkeit Entwickler zur Behandlung von Ausnahmen zu zwingen
  • Möglichkeit  potentielle Ausnahmen in größeren Codeblöcken an einer Stelle zu behandlen
  • Objektorientierte Konzepte um Ausnahmehierarchien zu modellieren und damit die Behandlung ganzer Klasse von Ausnahmen zu erlauben

Beispiel einer Ausnahme in Java

Die Zuweisung des Ausdrucks auf die Variable c löst eine Ausnahme aus, da hier durch Null dividiert wird:

package Kurs2.Exception;
public class Beispiel {
public static void main(String args[]) {
int a = 18;
int b = 6;
int c = (a+b)/(3*b-18); int d = 17; // wird nicht erreicht
}
}

Bei der Division durch Null erzeugt Java ein Ausnahmeobjekt in dem die Ausnahme beschrieben ist. Wird die Ausnahme nicht vom Entwickler behandelt nutzt Java diese Information. Gibt sie auf der Konsole aus und beendet das Programm.

Exception in thread "main" java.lang.ArithmeticException: / by zero
at Kurs2.Exception.Beispiel.main(Beispiel.java:6)

Syntax einer Java-Ausnahme auf der Konsole

Syntax einer Ausnahme auf der Konsole

Die Klassenhierarchie des Java-Laufzeitsystems

Java verwendet eine Reihe von Klassen zur Verwaltung von Ausnahmen. Bei Auftreten von Ausnahmen werden Instanzen dieser Klassen erzeugt. Mit Hilfe dieser Instanzen kann man Ausnahmen analysieren und behandeln. Anbei ein Auszug der Klassenhierarchie des Java-Laufzeitsystems:

Exceptionklassen des Java-laufzeitsystems

Exceptions (Ausnahmen) in UML

UML benutzt Unterbechungskanten um Ausnahmen in Aktivitätsdiagrammen zu beschreiben. Unterbrechungskanten werden als Pfeile in Form eines Blitzes gezeichnet. Die Ausnahmebedingung wird in rechteckigen Klammern an der Unterbrechungskante dokumentiert. Da folgende Beispiel zeigt das Sortieren von Personen welches abgebrochen wird wenn eine Person eine leere Zeichenkette für den Nachnamen besitzt.

UML Notation mit Pfeil in Blitzform

UML Notation mit Aktivitätspfeil
und separatem Blitzsymbol

Weiterführende Quellen

Stefan Schneider Sat, 10/23/2010 - 14:37

10.1 Java-Ausnahmen (Exceptions) behandeln

10.1 Java-Ausnahmen (Exceptions) behandeln

Java zwingt den Entwickler zur Behandlung von Ausnahmen wenn er Methoden aufruft die zu "Checked Exceptions" führen können.

Entwickler haben hier zwei Lösungsoptionen

  • die Möglichkeit die Behandlung einer Ausnahme an den Aufrufer der eigenen Methode zu delegieren
  • die Behandlung der Ausnahme im aktuellen Block selbst zu implementieren.

Der Javaübersetzer wird den quellcode nicht übersetzen falls der Entwickler keine Maßnahme ergriffen. Die zwangsweise Behandlung von "Checked Exceptions" ist Teil des Schnittstellenvertrags zur Benutzung einer Methode.

Behandeln einer Ausnahme mit einem try-catch Block

Java erlaubt die Ausnahmebehandlung mit Hilfe von try-catch-finally Programmblöcken die aus drei Teilen bestehen:

Block Anzahl Inhalt 
try einmal Block enthält den eigentlichen Anwendungscode. Er wird "versucht" (try) komplett auszuführen. In ihm können gewisse Ausnahmen auftreten. Er wird verlassen wenn wenn eine Ausnahme auftritt
catch einmal pro Exceptiontyp Block wird ausgeführt wenn eine bestimmte Ausnahme auftritt. Er "fängt" die Ausnahme und behandelt sie.
finally einmal, optional Block wird unbedingt ausgeführt. Er wird entweder nach dem regulären durchlaufen des try-Blocks oder nach dem Durchlaufen des catch-Blocks ausgeführt. Hier werden typischerweise Resourcen wie offene Dateien geschlossen und wieder freigegeben.

Der try Block wird beim Auftreten einer Ausnahme direkt verlassen und es wird in den catch-Block gesprungen.

Die Java hat die folgende Syntax:

try
{
  ... // in diesem Block wird der reguläre Code implementiert
      // Dieser Code kann an beliebigen Stellen Ausnahmen auslösen
}
catch (ExceptionTypeA myName1)
{
  ... // dieser Block wird aufgerufen wenn im try-Block eine Ausnahme
      // vom Typ ExceptionTypeA auslöst
}
catch (ExceptionTypeB myName2)
{
  ... // dieser Code wird aufgerufen wenn im try-Block eine Ausnahme
      // vom Typ ExceptionTypeB auslöst
}
finally
{
   ...// Dieser Block ist optional
       // er wird immer aufgerufen. Er hängt nicht vom Auslösen einer Ausnahme ab
}

Ablaufsteuerung

Wichtig: "Geworfene" Ausnahmen führen zum Beenden des Programmes falls sie nicht innerhalb eines try-Blocks mit der entsprechenden catch-Klausel und passendem Exception-typ gefangen und bearbeitet werden! Das Javaprogramm wird jede weitere Ausführung einstellen und die entsprechenden Ausführungsblöcke verlassen. Es wird auf der Kommandozeile das Auftreten der entsprechende Ausnahme dokumentieren.

Der try-Block kann jedoch in einem beliebigen äusseren Block implementiert sein. Das heißt, Methoden die einen solchen Codepfad direkt oder indirekt aufrufen, müssen in einem solchen try-catch-Block eingehüllt sein.

Ablauf mit finally Block Ablauf ohne finally Block
Aktivitätsdiagramm try-catch-finally Block Aktivitätsdiagramm try-catch Block;

 

Beispiel 1: NumberFormatException

Im folgenden Beispiel wird das erste Kommandozeilenargument in eine ganze Zahl umgewandelt. Hier kann eine NumberFormatException Ausnahme bei der Umwandlung der Eingabezeichenkette args[0] auftreten. Tritt diese Ausnahme auf, wird der Variablen firstArg eine Null zugewiesen.

package s1.block10;
public class Main1 {
    public static void main(String[] args) {
    int firstArg=0;
    int stellen=0;
    if (args.length > 0) {
        try {
            firstArg = Integer.parseInt(args[0]);
        } 
        catch (NumberFormatException e) {
            firstArg = 0;
        }
        finally {
           System.out.println("Die Zahl firstArg = " + firstArg + " wurde eingelesen");
        }
    }
}
}

In diesem Beispiel wurde die Größe des Eingabefeldes präventiv mit einer if Bedingung abgeprüft.

Beispiel 2: ArrayIndexOutOfBoundsException

Die präventive Prüfung der Feldgröße kann aber auch in einem zweiten catch Block zum gleichen try Block durchgeführt werden.

package s1.block10;
public class Main2 {

    public static void main(String[] args) {
        int firstArg = 0;
        int stellen = 0;
        try {
            firstArg = Integer.parseInt(args[0]);
        } catch (ArrayIndexOutOfBoundsException e) {
            firstArg = -1;
            System.out.println("Argument auf Position "
                    + e.getMessage()
                    + " nicht vorhanden");
        } catch (NumberFormatException e) {
            firstArg = 0;
        } finally {
            System.out.println("Die Zahl firstArg = " + firstArg + " wurde eingelesen");
        }
    }
}

Das Behandeln des Feldüberlaufs mit Hilfe eines catch-Blocks ist die flexiblere Lösung da sie jeglichen Feldüberlauf innerhalb des try-Blocks fangen kann.

Fehleranalyse im catch-Block

Dem catch-Block wird eine Instanz der bestimmten Klasse der Ausnahme mitgegeben. In Beispiel 2 wird die Ausnahmeinstanz e benutzt um mit Hilfe der Methode e.getMessage() die Postion im Feld auszulesen die die ArryIndexOutOfBoundsException Ausnahme auslöste.

Behandeln einer Ausnahme durch Delegation an die nächst äussere Methode

Die zweite Möglichkeit eines Entwicklers besteht darin, die Methode nicht direkt zu behandeln und das Behandeln der Ausnahme dem Aufrufer der akuellen Methode zu überlassen. Dies geschieht in dem man im Kopf der Methode dokumentiert welche Ausnahmen durch den Aufruf einer Methode ausgelöst werden können. Die Methode stringLesen2() kann zum Beispiel zum Auslösen einer IOException Ausnahme führen:

public static String StringLesen2() throws IOException{
...
}

Methoden können mehr als eine Ausnahme auslösen. Die Syntax des Methodenkopfs ist die folgende:

[Modifikatoren] Rückgabewert Name-der-Methode ([Parameter]) throws Name-Ausnahme [, Name-Ausnahme]

Der Aufrufer der Methode muss dann die Behandlung selbst durchführen.

Hinweis: Die Angabe der Ausnahmen die von einer Methode ausgelöst haben keine Bedeutung bei der Unterscheidung von überladenen Methoden.

Beispiel

Im folgenden Fall werden zwei Methoden zum Einlesen einer Konsoleneingabe gezeigt.

Die Methode stringLesen1() behandelt die IOException Ausnahme selbst. Der Aufrufer im main() Hauptprogramm muss keine Maßnahmen ergreifen.

Due Methode stringLesen2() behandelt die Ausnahme nicht und "exportiert" sie an den Aufrufer. Hier muss das Hauptprogramm main() die Ausnahme selbst.

package s1.block10;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main3 {

public static void main(String[] args) {
String eingabe = "";
eingabe = StringLesen1();
System.out.println(eingabe);
try {
eingabe = StringLesen2();
} catch (IOException e) {
eingabe = " KEINE EINGABE BEI ZWEITEM AUFRUF;";
}
System.out.println(eingabe);
}

public static String StringLesen1() {
String input = "Konsoleneingabe(1): ";
// liest einen vom Benutzer eingegebenen Text (String) ein
BufferedReader keyboard =
new BufferedReader(new InputStreamReader(System.in));
try {
input = input + keyboard.readLine() + ";";
} catch (IOException e) {
input = input + " KEINE EINGABE;";
}
return input;
}

public static String StringLesen2() throws IOException{
String input = "Konsoleneingabe(2): ";
// liest einen vom Benutzer eingegebenen Text (String) ein
BufferedReader keyboard =
new BufferedReader(new InputStreamReader(System.in));
input = input + keyboard.readLine() + ";";
return input;
}
}

 

Wichtig
Eine Methode kann nur "Checked Exceptions" auslösen die von ihr in der throws Klausel angegeben wurden.

Abarbeitung von mehreren catch-Klauseln

Java sucht bei mehreren catch-Klauseln (Handler) für eine Ausnahmebehandlung alle Klauseln von oben nach untern. Die erste Klausel mit einem passenden Typ der Ausnahme wird dann aufgerufen.

Aufgrund des Polymorphismus kann es mehrere Möglichkeiten geben. Klauseln die für eine Oberklasse einer Ausnahme geschrieben wurden werden benutzt werden wenn eine speziellere Ausnahme abgehandelt werden muss.

Beim Benutzen von mehreren catch Klauseln sollten daher die speziellen Ausnahmen vorne stehen und die allgemeinen Ausnahmen am Ende.

Finally Block (Vertiefung)

Der finally Block wird immer ausgeführt. Dies im Extremfall zu ungewöhnlichen Situationen führen. Analysieren Sie das folgende Programm:

package s1.block10;

class BeispielFinally {
public static int finallyTest (int a) {
int result=0;

try {
result = 17/a; // Hier kann eine ArithmeticException ausgelöst werden
}
catch (ArithmeticException e) {
return 0;
}
finally {
return 1;
}
//System.out.println("Methode beendet. Wir nie erreicht. Daher auskommentiert");
return result;
}
public static void main(String[] args) {
int x=0;
System.out.println(finallyTest(0));
}
}

Es erzeugt die folgende Konsolenausgabe:

1

 

Stefan Schneider Sat, 01/29/2011 - 12:05

10.2 Java-Ausnahmen deklarieren und werfen

10.2 Java-Ausnahmen deklarieren und werfen

Werfen von Ausnahmen

Java erlaubt nicht nur die vom Laufzeitsystem erzeugten Ausnahmen zu behandeln. Java erlaubt es auch selbst Ausnahmen zur Erzeugen. Dies geschieht durch das Erzeugen einer Instanz einer Klasse die aus der Klasse Throwable abgeleitet ist. Im folgenden Beispiel geschieht dies, in dem vor einer Division auf eine mögliche Division durch Null geprüft wird :

package s1.block10;
import java.lang.IllegalArgumentException;

public class Grundrechenarten1 {
   public static int division(int a, int b) {
      IllegalArgumentException ex;
      if (b==0) {
         ex = new IllegalArgumentException("Division durch 0!");
         throw ex;
      }
    return a/b;
   }
 
   public static void main(String[] args) {
      int c = division(3,0);
   }
}

Das Werfen einer eigenen Ausnahme erfolgt in zwei Schritten:

  • Dem Erzeugen einer Instanz der Ausnahmeklasse. Hier kann ein Text mit Informationen im Konstruktor mitgegeben werden.
  • Dem Werfen der Ausnahme mit dem Schlüsselwort throw und der Angabe eines Ausnahmeobjekts

Beim Ausführen des oben gezeigten Programms wird die folgende Meldung ausgegeben:

Exception in thread "main" java.lang.IllegalArgumentException: Division durch 0!
        at s1.block10.Grundrechenarten1.division(Grundrechenarten.java:9)
        at s1.block10.Grundrechenarten1.main(Grundrechenarten.java:16)

Da das Ausnahmeobjekt in der Regel nicht weiter oder wieder verwendet wird, kann man das Erzeugen des Objekts direkt mit dem Werfen der Ausnahme verbinden:

throw new IllegalArgumentException("Division durch 0!");

Selbstimplementierte Ausnahmeklassen

Java erlaubt auch das Implementieren eigener Ausnahmeklasse. Man kann zum Beispiel die Klasse IllegalArgumentException zu einer eigenen Klasse ableiten:

package s1.block10;

public class DivisionException extends IllegalArgumentException {
   int dividend;
   int divisor;

   public DivisionException(int a, int b) {
         super ("Versuch von Division " + a + "/" + b);
      dividend =a;
      divisor = b;
   }
}

Das Hauptprogramm muss entsprechend angepasst werden:

package s1.block10;
import java.lang.IllegalArgumentException;

public class Grundrechenarten2 {
   public static int division2(int a, int b) {
      DivisionException ex;
      if (b==0) {
         ex = new DivisionException(a,b);
         throw ex;
      }
      return a/b;
   }

   public static void main(String[] args) {
      int c = division2(3,0);
   }
}

Es wird dann eine angepasste Fehlermeldung geworfen:

Exception in thread "main" Kurs2.Exception.DivisionException: Versuch von Division 3/0
        at s1.block10.Grundrechenarten2.division2(Grundrechenarten.java:18)
        at s1.block10.Grundrechenarten2.main(Grundrechenarten.java:25)
Java Result: 1

 

Stefan Schneider Sat, 01/29/2011 - 12:11

10.3 "Checked" und "Unchecked Exceptions"

10.3 "Checked" und "Unchecked Exceptions"

Die Klasse Throwable

Die Klasse Throwable ist die Basisklasse der gesamten Ausnahmehierarchie. Instanzen von ihr sorgen dafür das Ausnahmen "geworfen" werden, die dann behandelt werden können.

Alle Klassen in der Throwable Hierarchie können den Grund für das Werfen einer Ausnahme im Konstruktor als Zeichenkette beschreiben.

Die Klasse Error

Die Klasse Error wird für Probleme der Java virtuellen Maschine selbst verwendet. Diese Fehler sollten nicht auftreten. Fehler die durch diese Klasse gemeldet werden sind in der Regel so schwerwiegend, dass keine weitere Fortführung des Programms sinnvoll ist.

Ein Beispiel hierfür ist das Fehlen von freiem Hauptspeicher. Er wird mit einem OutOfMemoryError Fehler gemeldet. Eine sinnvolle Behandlung dieses Fehlers ist nur schwer möglich. Selbst ein Berichten über die Details dieses Problem benötigt Speicherplatz zum Verwalten von Zeichenketten. Der Versuch einer Behandlung eines solchen Fehlers ist nur sinnvoll wenn man in der Lage ist, in nativen Bibliotheken die die Anwendung selbst verwaltet unmittelbar auf C/C++ Ebene Speicher freizugeben.

Da alle Fehler in dieser Klassenhierarchie (Errror) nicht direkt von der entsprechenden Sektion im Programm abhängen und sehr selten sind, zählen diese Fehler zu den "unchecked Exceptions". Ein Entwickler muss Codestrecken nicht mit einem try-catch Block versehen und eine Behandlung implementieren.

Wichtig

Alle abgeleiteten Klassen der Klasse Error sind "unchecked exceptions".

Implikation: Sie müssen nicht mit try-catch Blöcken abgesichert werden.

Hintergrund: Fehler in dieser Kategorie können in jeder Java-Codestrecke auftreten. Der Entwickler müsste seinen gesamten Code mit try-catch Blöcken versehen. Hinzukommt, dass eine Behandlung in der Regel nicht möglich ist. Try-catch Blöcke sind allso nicht sinnvoll für diese Kategerie von Ausnahmen.

Beispiele
Oberklasse Error Bedeutung
InstantiationError Versuch des Erzeugens einer Instanz einer abstrakten Klasse oder einer Schnittstelle
StackOverflowError Überlauf des Stapel (Stack). Typische Ursachen sind Programm mit extrem vielen und tiefen Methodenaufrufen oder ein nicht terminierendes rekurvsives Programm
OutOfMemoryError Der Javaprozess hat keinen freien Hauptspeicher mehr und kann keine weiteren Objekte allokieren

 

Die Klasse Exception

Aus der Klasse Exception werden alle Ausnahmen abgeleitet, die ein Entwickler zur Laufzeit behandeln kann. Alle Klassen in dieser Unterhierarchie sind "Checked Exceptions" mit Ausnahme der Klasse RuntimeException. Dies bedeutet Sie müssen mit try-catch Blöcken behandelt werden.

Die Klasse RuntimeException

In der Hierarchie der Klasse RuntimeException sind alle Ausnahmen zu finden die im "normalen" Betrieb des Laufzeitsystems auftreten können. Sie sind auch "unchecked Exceptions" die man nicht im Code behandeln muss, da sie jederzeit auftreten können. 

Wichtig

Alle abgeleiteten Klassen der Klasse RuntimeException sind "unchecked exceptions".

Implikation: Sie müssen nicht mit try-catch Blöcken abgesichert werden.

 

Einige Ausnahmeklassen aus dieser Hierarchie sind:

Beispiele
Oberklasse RuntimeException Bedeutung
ArithmeticException Division durch Null bei Ganzzahlwerten.
NegativeArraySizeException Versuch der Erzeugung eines Feldes mit einer negativen Größe.
NullPointerException Versuchter Zugriff auf eine Instanz deren Referenz mit null belegt ist.

 

Das Konzept von "Checked" und "Unchecked Exceptions"

Die Unterscheidung dieser beiden Arten von Ausnahmen hat für den Entwickler die Konsequenz, dass er bei Aufrufen von Methoden die zu "Checked Exceptions" führen, eine entsprechende Behandlung bei der Implementierung durchführen muss. Behandlung bedeutet. "Unchecked Exceptions" müssen nicht in der Implementierung berücksichtig und behandelt werden.

Checked Exception

Bei Aufrufen von Methoden die zu "Checked Exceptions" führen können hat der Entwickler zwei Möglichkeiten:

  • Aufruf der entsprechen Methode innerhalb eines try-catch Blocks und der entsprechenden Behandlung der Ausnahme 
  • Weiterreichen der Ausnahme indem die gesamte Methode das Schlüsselwort throws und die Ausnahme benutzt um die Behandlung der Ausnahme an den aufrufenden Block weiterreicht.

Wird eine "checked Exception nicht mit einem try-catch Block behandelt so besteht auch die Möglichkeit die Ausnahme nicht zu behandeln. In diesem Fall muss die Ausnahme aus der aktuellen Methode herausgereicht werden. Hierfür muss die Methode im Kopf der entsprechenden Methode mit Hilfe des Schlüsselwort throws nach Aussen weitergereicht werden. Die Syntax des Methodenkopfs ist:

qualifier [return-parameter] methoden-name (parameter-liste) {throws eine-ausnahme} {...}

Ein konkreter Methodenkopf ist z. Bsp.

public boolean istKleinerAls (Object s) throws NachnameExceptionRT {...} 

Beispiel: Die Klasse BufferReader.readLine() wird benötigt um Konsoleneingaben zu Lesen.

Unchecked Exception

"Unchecked Exception" sind sehr vielfältig und sie können in fast jeder Zeile Code auftreten.  Diese Ausnahmen müssen nicht zwingend mit try-catch Blöcken abgefangen werden.

Der Entwickler muss bei "Unchecked Exceptions" selbst die Entscheidung treffen welche Codestrecken das Potential haben eine Ausnahme auszulösen.

Das Entwurfsziel besteht darin sicher zu codieren und den Code noch lesbar zu gestalten.

Beispiel:

Eine potentielle Division durch Null kann an sehr vielen Stellen auftreten. Müsste man jede Division durch Null mit einem try-catch Block ummanteln wäre das sehr aufwendig und der Code wird sehr schlecht lesbar sein.

In der folgenden Tabelle können in den ersten beiden Fällen keine Divisionen durch Null auftreten. Im dritten Fall kann eine solche Division abhängig vom Eingabewert c auftreten. Hier obliegt es dem Entwickler das Risiko einer Division durch Null abzuschätzen und die Division eventuell mit einem try-catch Block zu schützen.

Fall 1: kein Risiko Fall 2: kein Risiko Fall 3: Risiko einer Ausnahme
public int test(int a) {
   int b = a / 2;
   return b;
}
public int test(int a) {
   int c = 2;
   int b = a / c;
   return b;
}
public int test(int a, int c) {
   int b = a / c;
   return b;
}

 

 

Stefan Schneider Fri, 04/01/2011 - 19:49

Anonymous (not verified)

Sat, 12/14/2019 - 18:11

Warum kann eine Methode erben? (Konzept der Checked Exceptions)

10.4 Weiterreichen von Ausnahmen

10.4 Weiterreichen von Ausnahmen

Java erlaubt es nicht nur in catch Blöcken Ausnahmen zu behandeln, Java erlaubt es in einem catch Block die Ausnahme zu bearbeiten und anschließend trotzdem unbehandelt weiter zureichen.

Hierdurch kann eine Aktion durchgeführt werden und die finale Lösung des Problems einem äusserem Block überlassen werden.

try {
   DemoException einProblem = new DemoException ("Anwendungsfehler 17");
   throw einProblem;
}
catch (DemoException e)
{
 //Analyse der Ausnahme
 if (e.getMessage().equals("Anwendungsfehler 17"))
    throw e;
}

Hinweis: Die Anwendung muss nicht notwendigerweise die gleiche Ausnahme weiterreichen.

Es ist auch möglich eine beliebige neue Ausnahme zu werfen. Dies kann nützlich sein, wenn man nur noch anwendungsspezifische Ausnahmen nach aussen reichen will, um andere Klassen von Ausnahmen zu verbergen.

Stefan Schneider Sun, 01/30/2011 - 17:04

10.5 Der try-with-resources Befehl

10.5 Der try-with-resources Befehl

Dies ist eine weiterführendes Thema...

Den try-with-resources Befehl kann man seit Java 7 benutzen. Er erlaubt das Schliesen von Ressourcen und ersetzt damit den finally Block.

Java erlaubt es mit externen Ressourcen wie Dateien oder Streams umzugehen indem man Sie öffnet. Diese Ressourcen müssen aber auch wieder geschlossen werden, da das Betriebsystem hierfür Ressourcen allokieren muss.

Vor Java 7 schloß man Ressourcen im folgenden Stil:

BufferedReader buffr = new BufferedReader(new FileReader(path));
try {
   return buffr.readLine();
   } finally {
     // Wird auf jeden Fall ausgeführt!
     if (buffr != null) {buffr.close()};
   }

Ab Java 7 kann man das eleganter beschreiben:

try (BufferedReader buffr =
   new BufferedReader(new FileReader(path))) {
      return buffr.readLine();
   }

Um diesen Stil zu verwenden, muss die Klasse die Schnittstelle java.lang.AutoCloseable b.z.w. java.io.Closeable  implementieren.

Dieser try-Block unterdrückt die geworfene Ausnahme!

Mehr Informationen gibt es im folgenden Oracle Tutorial.

Stefan Schneider Sat, 04/01/2017 - 12:09

10.6 Übungen (Ausnahmen)

10.6 Übungen (Ausnahmen)

Übung 1: Werfen einer Ausnahme

Verfeinern Sie die Implementierung der Klasse Person. Werfen Sie eine Ausnahme der Klasse UnsupportedOperationException  wenn beim Vergleich in der Methode compareTo() eine leere Zeichenkette ("") als Nachname implementiert ist.

Nutzen Sie die Klasse Person:

package s1.block10;
import java.text.Collator;
import java.util.Arrays;
import java.util.Locale;
public class Person implements Comparable {
private String nachname;
private String vorname;
private static int maxLaenge = 10; // Maximale Laenge von Zufallszeichenketten
private static Locale myLocale = Locale.GERMANY; // Zu verwendende Sortierordnung
/**
* Konstruktor der Klasse Person
* @param nn Nachname
* @param vn Vorname
*/
public Person(String nn, String vn) {
nachname = nn;
vorname = vn;
}
/**
*
* @param s Objekt mit dem verglichen wird
* @return -1 wenn der Eingabewert kleiner ist
* 0 wenn der Eingabewert gleich ist
* +1 wenn der Eingabewert größer ist
*/
@Override
public int compareTo(Object s) {
int result = -1;
Person p;
if (s.getClass() == this.getClass()) {
p = (Person) s;
// Konfiguriere die Sortierordnung
Collator myCollator = Collator.getInstance(myLocale);
myCollator.setStrength(Collator.TERTIARY);
result = myCollator.compare(nachname, p.nachname);
if (result ==0)
result = myCollator.compare(vorname, p.vorname);
}
return result;
}
/**
* Erlaubt den Vor- und Nachnamen als Text auszudrucken
* @return
*/
@Override
public String toString() {
return nachname + ", " + vorname;
} /** * * @param args wird nicht ausgewertet */

public static void main(String[] args) {
// Aufzählende Iniatialisierung eines Felds
Person[] psn = {
new Person("Schneider", "Hans"),
new Person("Schmidt", "Hans"),
new Person("Schneider", "Gretel"),
new Person("Schmitt", "Hans"),
new Person("Meier", "aschenputtel"),
new Person("", "Aschenputtel")
};
Arrays.sort(psn);
System.out.println("Sortiert:");
for ( Person p : psn) {
System.out.println(p);
}
}
}

Spoileralam für versierte Programmier; nicht weiterlesen!

Man kann einen leeren String mit dem folgenden Test erkennen:

if (p.vorname.equals(""))

Übung 2: Werfen einer selbstimplementierten Ausnahme

Implementieren Sie eine Klasse Kurs2.Exception.NachnameException mit den folgenden Eigenschaften:

  • Abgeleitet aus der Klasse RuntimeException oder CheckedException
    • Aus welcher der beiden Klassen können Sie Ihre Ausnahme nicht ableiten. Warum nicht? Wo gibt es einen Konflikt
       
  • Erfasst den Vornamen als Text der Ausnahme
  • Speichern des Vornamens für potentielle Wiederverwendung bei der Analyse der Ausnahme

Vorlage für Implementierung. Wählen Sie die passende der beiden Klassen aus der Sie die Ausnahme ableiten

package Kurs2.Exception;
public class NachnameException extends [RuntimeException|CheckedException] {
    ...
    public NachnameException(String vn) {...}
    }

Nutzen sie die Klasse Person der Übung 1. Passen Sie die Klasse Person so an, dass die neue Ausnahmenklasse aufgerufen wird.

Übung 3: Werfen einer selbstimplementierten Ausnahme und Behandeln

Behandeln Sie die Ausnahme in der main() Methode und geben Sie eine passende Konsolenausgabe.

Optionale Übung

Falls Ihre selbst geschriebene Ausnahme einen Zeiger auf das Objekt mit einem Null-String verwaltet, können Sie diesen Null-String mit etwas anderem ersetzen und den Sortiervorgang erneut aufrufen!

Stefan Schneider Sat, 04/02/2011 - 11:33

10.7 Lösungen (Ausnahmen)

10.7 Lösungen (Ausnahmen)

Übung 1: Werfen einer Ausnahme

Hinweis: Alle Klassen dieser Übung werden im Paket Kurs2.Exception neu implementiert. Sie haben daher andere import Kommandos als die Klassen im Paket Kurs2.Sort.

Klasse Person (nur die Methode mit den erforderlichen Änderungen)

...
/**
*
* @param o
* @return -1 wenn der Eingabewert kleiner ist
* 0 wenn der Eingabewert gleich ist
* +1 wenn der Eingabewert größer ist
*/
@Override
public int compareTo(Object s) {
int result = -1;
PersonUebung1 p;
if (s.getClass() == this.getClass()) {
p = (PersonUebung1) s;
if (p.vorname.equals("")) {
throw new UnsupportedOperationException("Vorname fehlt");
}
if (nachname.equals("")) {
throw new UnsupportedOperationException("Nachname fehlt");
}

// Konfiguriere die Sortierordnung
Collator myCollator = Collator.getInstance(myLocale);
myCollator.setStrength(Collator.TERTIARY);
result = myCollator.compare(nachname, p.nachname);
if (result ==0)
result = myCollator.compare(vorname, p.vorname);
}
return result;
}
...

 

Übung 2: Werfen einer selbstimplementierten Ausnahme

Klasse NachnameException:

package s1.block10;

public class NachnameException extends RuntimeException {
public String vorname;

public NachnameException(String vn) {
super(vn + " hat keinen Nachnamen");
vorname = vn;
}

}

In der Klasse Person der vorherigen Übung muss die Methode compareTo() auch angepasst werden:

...

/**
*
* @param o
* @return -1 wenn der Eingabewert kleiner ist
* 0 wenn der Eingabewert gleich ist
* +1 wenn der Eingabewert größer ist
*/
@Override
public int compareTo(Object s) {
int result = -1;
PersonUebung2 p;
if (s.getClass() == this.getClass()) {
p = (PersonUebung2) s;
if (p.vorname.equals("")) {
throw new UnsupportedOperationException("Vorname fehlt");
}
if (nachname.equals("")) {
throw new NachnameException(vorname);
}
// Konfiguriere die Sortierordnung
Collator myCollator = Collator.getInstance(myLocale);
myCollator.setStrength(Collator.TERTIARY);
result = myCollator.compare(nachname, p.nachname);
if (result ==0)
result = myCollator.compare(vorname, p.vorname);
}
return result;
}

...

Übung 3: Werfen einer selbstimplementierten Ausnahme und Behandeln

Klasse NachnameExceptionChecked

package Kurs2.Exception;

public class NachnameExceptionChecked extends Exception {
public String vorname;
public Boolean zweitesArgument;
/**
*
* @param vn Vorname des Objekts ohne Nachnamen
* @param zweitesArg ist wahr wenn das zweite Objekt beim Vergleich
* keinen Nachnamen hat. Ist falsch wenn das erste Objekt keinen
* Nachnamen hat
*/
public NachnameExceptionChecked(String vn, Boolean zweitesArg) {
super(vn + " hat keinen Nachnamen");
vorname = vn;
zweitesArgument = zweitesArg;
}

}

Klasse Person

Die Methode main():

public static void main(String[] args) {
Person[] psn = new Person[6];
psn[0] = new Person("Schneider", "Hans");
psn[1] = new Person("Schmidt", "Hans");
psn[2] = new Person("Schneider", "Gretel");
psn[3] = new Person("Schmitt", "Hans");
psn[4] = new Person("Meier", "aschenputtel");
psn[5] = new Person("", "Aschenputtel");
try {
Arrays.sort(psn);
}
catch (NachnameException e) {
System.out.println("Das Feld konnte nicht sortiert werden, da " +
e.vorname + " keinen Nachnamen besitzt");
}

System.out.println("Sortiert:");
for ( PersonUebung3 p : psn) {
System.out.println(p);
} }

 

Stefan Schneider Sun, 04/03/2011 - 13:35

10.8 Beispielprogramme aus der Vorlesung

10.8 Beispielprogramme aus der Vorlesung

In diesem Beispiel wird die Klasse Konto Schritt für Schritt weiterentwickelt um die den Einsatz von Assertions und Ausnahmen zu diskutieren.

Die durchnummierten Klassen repräsentieren verschiedene Enwticklungsstufen. Sie erlauben es wieder den Einstieg zu finden falls man beim Mittippen nicht mehr mitgekommen ist.

Klasse Konto

Beispiel eines Bankkontos. In der main() Methode werden einfache Überweisungsoperationen ausgeführt.

Welche inkorrekten Aufrufe können in dieser Klasse erfolgen?

package s1.block10;
public class Konto {
    int betrag;
    public Konto(int startBetrag) {
        betrag = startBetrag;
    }
    private void einzahlen(int b) {
        betrag += b;
    }
    private void auszahlen(int b) {
        betrag -= b;
    }
    public void ueberweisenAuf (Konto k, int wert) {
        auszahlen(wert);
        k.einzahlen(wert);
    }
    @Override
    public String toString() {return betrag + " Euro";}
    /**
    * Die main Methode sei eine Methode die von einem Team gepflegt wird
    * welches nichts von der internen Implementierung der Klasse weis.
    * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse
    * implementiert
    * @param args
    */
    public static void main(String[] args) {
        Konto a1 = new Konto(500);
        Konto a2 = new Konto(100);
        a2.ueberweisenAuf(a1, 50);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
        a2.ueberweisenAuf(a1, -500);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
    }

}

Klasse Konto1

package s1.block10;
public class Konto1 {
    int betrag;
    public Konto1(int startBetrag) {
        betrag = startBetrag;
    }
    private void einzahlen(int b) {
        betrag += b;
    }
    private void auszahlen(int b) {
        betrag -= b;
    }
    public void ueberweisenAuf (Konto1 k, int wert) {
        auszahlen(wert);
        k.einzahlen(wert);
    }
    @Override
    public String toString() {return betrag + " Euro";}
    /**
     * Die main Methode sei eine Methode die von einem Team gepflegt wird
     * welches nichts von der internen Implementierung der Klasse weis.
     * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse
     * implementiert
     * @param args
     */
    public static void main(String[] args) {
        Konto1 a1 = new Konto1(500);
        Konto1 a2 = new Konto1(100);
        a2.ueberweisenAuf(a1, 50);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
        a2.ueberweisenAuf(a1, -500);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
    }
}

Klasse Konto2

In diesem Beispiel werden die Methoden so modifiziert, dass sie einen Wahrheitswert zurückgeben der über die korrekte Ausführung informiert.

Welche Nachteile hat eine solche Implementierung?

package s1.block10;
public class Konto2 {
    int betrag;
    public Konto2(int startBetrag) {
        betrag = startBetrag;
    }
    private boolean einzahlen(int b) {
        if (b>=0) {
           betrag += b;
           return true;
        }
        else return false;
    }
    private boolean auszahlen(int b) {
        if (b>=0) {
           betrag -= b;
           return true;
        }
        else return false;
    }
    public boolean ueberweisenAuf (Konto2kb, int wert) {
        boolean korrekt;
        korrekt = auszahlen(wert);
        if (korrekt)
            korrekt= k.einzahlen(wert);
        return korrekt;
    }
    @Override
    public String toString() {return betrag + " Euro";}
    /**
     * Die main Methode sei eine Methode die von einem Team gepflegt wird
     * welches nicht die interne Implementierung der Klasse.
     * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse
     * implementiert
     * @param args
     */
    public static void main(String[] args) {
        Konto2 a1 = new Konto2(500);
        Konto2 a2 = new Konto2(100);
        boolean korrekt;
        korrekt = a2.ueberweisenAuf(a1, 50);
        if (korrekt)
            System.out.println("a1: "+ a1 + "; a2= " +a2);
        else
            System.out.println("Fehler; a1: "+ a1 + "; a2= " +a2);
        korrekt = a2.ueberweisenAuf(a1, -500);
        if (korrekt)
            System.out.println("a1: "+ a1 + "; a2= " +a2);
        else
           System.out.println("Fehler; a1: "+ a1 + "; a2= " +a2);
    }
}

Kasse Konto3

In dieser Implementierung werden "Assertions" verwendet.

Die Verwendung der Assertion wird mit Hilfe der Option -ea aktiviert.

Man startet das Programm mit dem Befehl

$ java -ea Kurs1.Exception.Demo.Konto3

Jetzt werden die Assertions ausgeführt und geprüft. Das Programm wird bei der ersten Verletzung einer Assertion beendet.

package s1.block10;
public class Konto3 {
    int betrag;
    public Konto3(int startBetrag) {
        betrag = startBetrag;
    }
    private void einzahlen(int b) {
        assert (b>=0): "Versuch " + b + " zu einzuzahlen";
        betrag += b;
    }
    private void auszahlen(int b) {
        assert (b>=0): "Versuch " + b + " zu auszuzahlen";
        betrag -= b;
    }
    public void ueberweisenAuf (Konto3 k, int wert) {
        auszahlen(wert);
        k.einzahlen(wert);
    }
    @Override
    public String toString() {return betrag + " Euro";}
    /**
     * Die main Methode sei eine Methode die von einem Team gepflegt wird
     * welches nichts von der internen Implementierung der Klasse weis.
     * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse
     * implementiert
     * @param args
     */
    public static void main(String[] args) {
        Konto3 a1 = new Konto3(500);
        Konto3 a2 = new Konto3(100);
        boolean korrekt;
        a2.ueberweisenAuf(a1, 50);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
        a2.ueberweisenAuf(a1, -500);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
    }
}

Tipp für Netbeansbenutzer

Auf Eclipse und IntelliJ ist sinngemäss das gleiche zu tun.

Einschalten des Assertionchecking für die Klasse Konto3:

  1. Rechtsklicken und halten  auf dem Projektsymbol (Kaffeetassensymbol) in dem sich die Klasse Konto3 befindet.
  2. Auswahl des letzten Menüpunkts "Properties".
  3. Auswahl der Kategory "Run"
  4. In "Configuration" den "New" Knopf drücken
  5. Im modalen Dialog einen Konfigurationsname wählen (z.Bsp. Konto3Assertion)
  6. Feld "Main Class" belegen in dem man den Knopf rechts davon "Browse" drückt und die gewünschte Klasse im Projekt wählt
  7. Es werden alle Klassen im Projekt angezeigt, die als Startprogramm dienen können. Wählen Sie die Klasse Konto3 aus.
  8. Fügen sie im Textfeld "VM Options" "-ea" um Assertions einzuschalten.
  9. Speichern Sie den Dialog duech Drücken der Taste "OK"

Starten des Programms

  1. Rechtsklicken und halten auf dem Projektsymbol (Kafeetassensymbol) in dem sich die Klasse Konto3 befindet.
  2. Auswahl des letzten Menüpunkts "Set Configuration".
  3. Wählen Sie die im Vorgängerschritt angelegte Konfiguration
  4. Starten Sie das Programm mit Rechtsklicken und halten auf dem Projektsymbol (Kafeetassensymbol) in dem sich die Klasse Konto3 befindet.
  5. Wählen Sie "Run" aus

Das Programm kann jetzt wahrscheinlich auch mit der Ikone mit dem großen grünen Dreieck in der Menüleiste gestartet werden.

Klasse Konto4

Das Verwenden von Assertions in den privaten Methoden einzahlen() und auszahlen() ist sinnvoll, da die beiden Methoden nur innerhalb der Klasse aufgerufen werden können. Der Entwickler der Klasse kann garantieren, dass sie immer mit korrekten Werten aufgerufen werden. Ein negativer Betrag als Parameter soll nicht vorkommen.

In der folgenden Klasse wir die Methode ueberweisenAuf() auch mit einer Assertion geschützt. Die Methode ist öffentlich und kann auch von externen Klassen aufgerufen werden.

Warum ist dies keine gute Implementierung?

package s1.block10;
public class Konto4 {
    int betrag;
    public Konto4(int startBetrag) {
        betrag = startBetrag;
    }
    private void einzahlen(int b) {
        assert (b>=0);
        betrag += b;
    }
    private void auszahlen(int b) {
        assert (b>=0);
        betrag -= b;
    }
    public void ueberweisenAuf (Konto4 k, int wert) {
        assert(wert>=0): "Versuch einer negativen Überweisung von " + wert ;
        auszahlen(wert);
        k.einzahlen(wert);
    }
    @Override
    public String toString() {return betrag + " Euro";}
    /**
     * Die main Methode sei eine Methode die von einem Team gepflegt wird
     * welches nichts von der internen Implementierung der Klasse weis.
     * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse
     * implementiert
     * @param args
     */
    public static void main(String[] args) {
        Konto4 a1 = new Konto4(500);
        Konto4 a2 = new Konto4(100);
        boolean korrekt;
        a2.ueberweisenAuf(a1, 50);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
        a2.ueberweisenAuf(a1, -500);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
    }
}

Klasse Konto5

Die Klasse Konto5 erweitert die Implementierung. Es wird ein Überweisungslimit eingeführt. Das Überweisungslimit wird im Konstruktor erfasst. Es sollen keine Überweisungen mit einem höheren Betrag als dem Überweisungslimit ausgeführt.

package s1.block10;
public class Konto5 {
    int betrag;
    private int ueberweisungsLimit;
    private static final int MINLIMIT =1;
    public Konto5(int startBetrag, int ll) {
        betrag = startBetrag;
        if (ll>0) ueberweisungsLimit=ll;
        else ueberweisungsLimit = MINLIMIT;
    }
    private void einzahlen(int b) {
        assert (b>=0);
        betrag += b;
    }
    private void auszahlen(int b) {
        assert (b>=0);
        betrag -= b;
    }
    public void ueberweisenAuf (Konto5 k, int wert) {
        assert(wert>=0);
        auszahlen(wert);
        k.einzahlen(wert);
    }
    @Override
    public String toString() {return betrag + " Euro";}
    /**
     * Die main Methode sei eine Methode die von einem Team gepflegt wird
     * welches nichts von der internen Implementierung der Klasse weis.
     * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse
     * implementiert
     * @param args
     */
    public static void main(String[] args) {
        Konto5 a1 = new Konto5(500, 50);
        Konto5 a2 = new Konto5(100, 80);
        boolean korrekt;
        a2.ueberweisenAuf(a1, 50);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
        a2.ueberweisenAuf(a1, -500);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
        a2.ueberweisenAuf(a1, 100);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
    }
}

Klasse Konto6

In dieser Implementierung werden inkorrekte Überweisungen mit negativen Beträgen oder Beträgen jenseits des Überweisungslimit mit Hilfe von unterschiedlichen Rückgabewerten und Konstanten gemeldet.

Die gewählte Implementierung vermeidet Fehler und meldet diese ausführlich.

Die Behandlung der Fehlerwerte ist jedoch aufwändig.

package s1.block10;
public class Konto6 {
    int betrag;
    private int ueberweisungsLimit;
    private static final int MINLIMIT =1;
    public static final int OK = 0;
    public static final int NEGATIVERWERT = 1;
    public static final int LIMITUEBERSCHRITTEN = 2;
    public Konto6(int startBetrag, int ll) {
        betrag = startBetrag;
        if (ll>0) ueberweisungsLimit=ll;
        else ueberweisungsLimit = MINLIMIT;
    }
    private void einzahlen(int b) {
        assert (b>=0);
        betrag += b;
    }
    private void auszahlen(int b) {
        assert (b>=0);
        betrag -= b;
    }
    public int ueberweisenAuf (Konto6 k, int wert) {
        if (wert < 0) return NEGATIVERWERT;
        else
            if ((wert > ueberweisungsLimit )|| (wert > k.ueberweisungsLimit ))
                return LIMITUEBERSCHRITTEN;
            else {
                auszahlen(wert);
                k.einzahlen(wert);
                return OK;
            }
    }
    @Override
    public String toString() {return betrag + " Euro";}
    /**
     * Die main Methode sei eine Methode die von einem Team gepflegt wird
     * welches nichts von der internen Implementierung der Klasse weis.
     * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse
     * implementiert
     * @param args
     */
    public static void main(String[] args) {
        Konto6 a1 = new Konto6(500, 50);
        Konto6 a2 = new Konto6(100, 80);
        int status;
        status = a2.ueberweisenAuf(a1, 50);
        if (status != OK)
            System.out.println("Fehler: " + status);
        else
            System.out.println("a1: "+ a1 + "; a2= " +a2);
        status = a2.ueberweisenAuf(a1, -500);
        if (status != OK)
            System.out.println("Fehler: " + status);
        else
            System.out.println("a1: "+ a1 + "; a2= " +a2);
        status = a2.ueberweisenAuf(a1, 100);
        if (status != OK)
            System.out.println("Fehler: " + status);
        else
            System.out.println("a1: "+ a1 + "; a2= " +a2);
    }
}

Klasse Konto7

In dieser Implementierung wird die Ausnahme IllegalArgumentException aus den Javastandardklassen verwendet.

package s1.block10;
public class Konto7 {
    int betrag;
    private int ueberweisungsLimit;
    private static final int MINLIMIT =1;
    public static final int OK = 0;
    public static final int NEGATIVERWERT = 1;
    public static final int LIMITUEBERSCHRITTEN = 2;
    public Konto7(int startBetrag, int ll) {
        betrag = startBetrag;
        if (ll>0) ueberweisungsLimit=ll;
        else ueberweisungsLimit = MINLIMIT;
    }
    private void einzahlen(int b) {
        assert (b>=0);
        betrag += b;
    }
    private void auszahlen(int b) {
        assert (b>=0);
        betrag -= b;
    }
    public void ueberweisenAuf (Konto7 k, int wert) {
        if (wert < 0) throw new IllegalArgumentException("Negativer Wert " + wert);
        else
            if (wert > ueberweisungsLimit )
                    throw new IllegalArgumentException("Limit ueberschritten " + wert
                            + "; Limit: " + ueberweisungsLimit);
            else
                if (wert > k.ueberweisungsLimit )
                    throw new IllegalArgumentException("Limit ueberschritten " + wert
                            + "; Limit: " + k.ueberweisungsLimit);
                else {
                    auszahlen(wert);
                    k.einzahlen(wert);
                }
    }
    @Override
    public String toString() {return betrag + " Euro";}
    /**
     * Die main Methode sei eine Methode die von einem Team gepflegt wird
     * welches nichts von der internen Implementierung der Klasse weis.
     * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse
     * implementiert
     * @param args
     */
    public static void main(String[] args) {
        Konto7 a1 = new Konto7(500, 50);
        Konto7 a2 = new Konto7(100, 80);
        int status;
        a2.ueberweisenAuf(a1, 50);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
        a2.ueberweisenAuf(a1, -500);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
        a2.ueberweisenAuf(a1, 100);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
    }
}

Klasse Konto8 und Ueberweisungsausnahme

 In dieser Implementierung werden eigens implementierte Ausnahmen (Exceptions) verwendet um eine Ausnahmebehandlung durchzuführen.

Klassenhierarchie der neuen Ausnahme

package s1.block10;
public class Konto8 {
    int betrag;
    private int ueberweisungsLimit;
    private static final int MINLIMIT =1;
    public static final int OK = 0;
    public static final int NEGATIVERWERT = 1;
    public static final int LIMITUEBERSCHRITTEN = 2;
    public Konto8(int startBetrag, int ll) {
        betrag = startBetrag;
        if (ll>0) ueberweisungsLimit=ll;
        else ueberweisungsLimit = MINLIMIT;
    }
    private void einzahlen(int b) {
        assert (b>=0);
        betrag += b;
    }
    private void auszahlen(int b) {
        assert (b>=0);
        betrag -= b;
    }
    public void ueberweisenAuf (Konto8 k, int wert) {
        if (wert < 0) throw new UeberweisungsAusnahme("Negativer Wert" + wert);
        else
            if (wert > ueberweisungsLimit )
                    throw new UeberweisungsAusnahme("Limit ueberschritten " + wert
                            + "; Limit: " + ueberweisungsLimit);
            else
                if (wert > k.ueberweisungsLimit )
                    throw new UeberweisungsAusnahme("Limit ueberschritten " + wert
                            + "; Limit: " + k.ueberweisungsLimit);
                else {
                    auszahlen(wert);
                    k.einzahlen(wert);
                }
    }
    @Override
    public String toString() {return betrag + " Euro";}
    /**
     * Die main Methode sei eine Methode die von einem Team gepflegt wird
     * welches nichts von der internen Implementierung der Klasse weis.
     * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse
     * implementiert
     * @param args
     */
    public static void main(String[] args) {
        Konto8 a1 = new Konto8(500, 50);
        Konto8 a2 = new Konto8(100, 80);
        int status;
        a2.ueberweisenAuf(a1, 50);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
        a2.ueberweisenAuf(a1, -500);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
        a2.ueberweisenAuf(a1, 100);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
    }
}

Die Klasse Ueberweisungsausnahme

Die Klasse wird aus der Klasse IllegalArgumentException abgeleitet. Sie muss nicht behandelt werden da diese Ausnahme eine Spezialisierung der RuntimeException ist. Das Auftreten der Ausnahme im Hauptprogramm kann aber behandelt werden.

package s1.block10;
public class UeberweisungsAusnahme extends IllegalArgumentException {
    public UeberweisungsAusnahme(String text) {
        super(text);
    }
}

Klasse Konto9

In der Klasse Konto9 wird in der main() Methode eine geworfene Ausnahme behandelt.

package s1.block10;
public class Konto9 {
    int betrag;
    private int ueberweisungsLimit;
    private static final int MINLIMIT =1;
    public static final int OK = 0;
    public static final int NEGATIVERWERT = 1;
    public static final int LIMITUEBERSCHRITTEN = 2;
    public Konto9(int startBetrag, int ll) {
        betrag = startBetrag;
        if (ll>0) ueberweisungsLimit=ll;
        else ueberweisungsLimit = MINLIMIT;
    }
    public void einzahlen(int b) {
        assert (b>=0);
        betrag += b;
    }
    public void auszahlen(int b) {
        assert (b>=0);
        betrag -= b;
    }
    public void ueberweisenAuf (Konto9 k, int wert) {
        if (wert < 0) throw new UeberweisungsAusnahme("Negativer Wert" + wert);
        else
            if (wert > ueberweisungsLimit )
                    throw new UeberweisungsAusnahme("Limit ueberschritten " + wert
                            + "; Limit: " + ueberweisungsLimit);
            else
                if (wert > k.ueberweisungsLimit )
                    throw new UeberweisungsAusnahme("Limit ueberschritten " + wert
                            + "; Limit: " + k.ueberweisungsLimit);
                else {
                    auszahlen(wert);
                    k.einzahlen(wert);
                }
    }
    @Override
    public String toString() {return betrag + " Euro";}
    /**
     * Die main Methode sei eine Methode die von einem Team gepflegt wird
     * welches nichts von der internen Implementierung der Klasse weis.
     * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse
     * implementiert
     * @param args
     */
    public static void main(String[] args) {
        Konto9 a1 = new Konto9(500, 50);
        Konto9 a2 = new Konto9(100, 80);
        int status;
        a2.ueberweisenAuf(a1, 50);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
        try {             a2.ueberweisenAuf(a1, -500);
        }
        catch (UeberweisungsAusnahme ue) {
            System.out.println("Fehler aufgetreten" + ue.getMessage());
            System.out.println ("Operation nicht ausgeführt");
        }
        System.out.println("a1: "+ a1 + "; a2= " +a2);
        a2.ueberweisenAuf(a1, 100);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
    }
}

Klasse Konto10

In der Klasse Konto10 wird zwischen einer allgemeinen ÜberweisungsAusnahme und einer LimitAusnahme unterschieden.

Klassenhierarchie mit Ausnahmen

package s1.block10;
public class Konto10 {
    int betrag;
    private int ueberweisungsLimit;
    private static final int MINLIMIT =1;
    public static final int OK = 0;
    public static final int NEGATIVERWERT = 1;
    public static final int LIMITUEBERSCHRITTEN = 2;
    public Konto10(int startBetrag, int ll) {
        betrag = startBetrag;
        if (ll>0) ueberweisungsLimit=ll;
        else ueberweisungsLimit = MINLIMIT;
    }
    public void einzahlen(int b) {
        assert (b>=0);
        betrag += b;
    }
    public void auszahlen(int b) {
        assert (b>=0);
        betrag -= b;
    }
    public void ueberweisenAuf (Konto10 k, int wert) {
        if (wert < 0) throw new UeberweisungsAusnahme("Negativer Wert" + wert);
        else {
            int maxlimit = (ueberweisungsLimit<k.ueberweisungsLimit) ?
            ueberweisungsLimit: k.ueberweisungsLimit;
            if (wert > maxlimit )
                    throw new LimitAusnahme("Limit ueberschritten " + wert,
                            maxlimit);
            else {
                auszahlen(wert);
                k.einzahlen(wert);
            }
        }
    }
    @Override
    public String toString() {return betrag + " Euro";}
    /**
     * Die main Methode sei eine Methode die von einem Team gepflegt wird
     * welches nichts von der internen Implementierung der Klasse weis.
     * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse
     * implementiert
     * @param args
     */
    public static void main(String[] args) {
        Konto10 a1 = new Konto10(500, 50);
        Konto10 a2 = new Konto10(50, 80);
        int status;
        a2.ueberweisenAuf(a1, 50);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
        try {
            a2.ueberweisenAuf(a1, -500);
        }
        catch (UeberweisungsAusnahme ue) {
            System.out.println("Fehler aufgetreten" + ue.getMessage());
            System.out.println ("Operation nicht ausgeführt");
        }
        System.out.println("a1: "+ a1 + "; a2= " +a2);
        a2.ueberweisenAuf(a1, 100);
        System.out.println("a1: "+ a1 + "; a2= " +a2);
    }
}

Klasse LimitAusnahme

Diese Klasse kann das Limit welches verletzt wurde verwalten. Hierdurch können Nutzer mehr Informationen erhalten und intelligenter reagieren.

package s1.block10;
public class LimitAusnahme extends UeberweisungsAusnahme {
    public int limit;
    public LimitAusnahme(String text, int ll) {
        super(text);
        limit = ll;
    }
}

Klasse Konto11

Die Klasse Konto11 reagiert auf eine Limitverletzung durch mehrere Überweisungen die unter dem Limit bleiben.

package s1.block10;
public class Konto11 {
    int betrag;
    private int ueberweisungsLimit;
    private static final int MINLIMIT =1;
    public static final int OK = 0;
    public static final int NEGATIVERWERT = 1;
    public static final int LIMITUEBERSCHRITTEN = 2;
    public Konto11(int startBetrag, int ll) {
        betrag = startBetrag;
        if (ll>0) ueberweisungsLimit=ll;
        else ueberweisungsLimit = MINLIMIT;
    }
    private void einzahlen(int b) {
        assert (b>=0);
        betrag += b;
    }
    private void auszahlen(int b) {
        assert (b>=0);
        betrag -= b;
    }
    public void ueberweisenAuf (Konto11 k, int wert) {
        if (wert < 0) throw new UeberweisungsAusnahme("Negativer Wert" + wert);
        else {
            int maxlimit = (ueberweisungsLimit<k.ueberweisungsLimit) ?
            ueberweisungsLimit: k.ueberweisungsLimit;
            if (wert > maxlimit )
                    throw new LimitAusnahme("Limit ueberschritten " + wert,
                            maxlimit);
            else {
                auszahlen(wert);
                k.einzahlen(wert);
            }
        }
    }
    @Override
    public String toString() {return betrag + " Euro";}
     /**
     * Die main Methode sei eine Methode die von einem Team gepflegt wird
     * welches nichts von der internen Implementierung der Klasse weis.
     * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse
     * implementiert
     * @param args
     */
   public static void main(String[] args) {
      Konto11 a1 = new Konto11(500, 50);
      Konto11 a2 = new Konto11(100, 80);
      int status;
      a2.ueberweisenAuf(a1, 50);
      System.out.println("a1: "+ a1 + "; a2= " +a2);
      try {
         a2.ueberweisenAuf(a1, -500);
      }
      catch (UeberweisungsAusnahme ue) {
          System.out.println("Fehler aufgetreten " + ue.getMessage());
          System.out.println ("Operation nicht ausgeführt");
      }
      System.out.println("a1: "+ a1 + "; a2= " +a2);
      int betrag = 100;
      try {
          a2.ueberweisenAuf(a1, betrag);
      }
      catch (LimitAusnahme ue) {
         // Splitte Überweisung in mehrere Ueberweisungen unterhalb des Limits
         int dasLimit = ue.limit;
         while (betrag > dasLimit) {
         betrag = betrag - dasLimit;
         a2.ueberweisenAuf(a1, dasLimit);
         System.out.println("a1: "+ a1 + "; a2= " +a2);
     }
     a2.ueberweisenAuf(a1, betrag);
     }
     System.out.println("a1: "+ a1 + "; a2= " +a2);
     }
}

Klasse Konto12

In der Klasse Konto12 wird eine andere Ausnahme bei einem Überweisungsfehler geworfen. Die Ausnahmeklasse UeberweisungsAusnahme12 ist aus der Klasse Exception abgeleitet.

Klassenhierarchie von Ausnahmen

Da die Ausnahme in der Methode ueberweisenAuf() geworfen wird muss

  • ... dies im Kopf der Methode dokumentiert werden
  • ... bei der Verwendung der Methode ueberweisenAuf() immer ein tra catch Block implementiert werden.
package s1.block10;
public class Konto12 {
    int betrag;
    private int ueberweisungsLimit;
    private static final int MINLIMIT =1;
    public static final int OK = 0;
    public static final int NEGATIVERWERT = 1;
    public static final int LIMITUEBERSCHRITTEN = 2;
    public Konto12(int startBetrag, int ll) {
        betrag = startBetrag;
        if (ll>0) ueberweisungsLimit=ll;
        else ueberweisungsLimit = MINLIMIT;
    }
    private void einzahlen(int b) {
        assert (b>=0);
        betrag += b;
    }
    private void auszahlen(int b) {
        assert (b>=0);
        betrag -= b;
    }
    public void ueberweisenAuf(Konto12 k, int wert) throws UeberweisungsAusnahme12  {
        if (wert < 0) throw new UeberweisungsAusnahme12("Negativer Wert" + wert);
        else {
            int maxlimit = (ueberweisungsLimit<k.ueberweisungsLimit) ?
            ueberweisungsLimit: k.ueberweisungsLimit;
            if (wert > maxlimit )
                    throw new LimitAusnahme("Limit ueberschritten " + wert,
                            maxlimit);
            else {
                auszahlen(wert);
                k.einzahlen(wert);
            }
        }
    }
    @Override
    public String toString() {return betrag + " Euro";}
     /**
     * Die main Methode sei eine Methode die von einem Team gepflegt wird
     * welches nichts von der internen Implementierung der Klasse kennt.
     * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse
     * implementiert
     * @param args
     */
    public static void main(String[] args) {
        Konto12 a1 = new Konto12(500, 50);
        Konto12 a2 = new Konto12(100, 80);
        int status;
        try {
            a2.ueberweisenAuf(a1, 50);
            System.out.println("a1: "+ a1 + "; a2= " +a2);
            a2.ueberweisenAuf(a1, -500);
            System.out.println("a1: "+ a1 + "; a2= " +a2);
            int betrag = 100;
            try {
                a2.ueberweisenAuf(a1, betrag);
                }
            catch (LimitAusnahme ue) {
                // Splitte Überweisung in mehrere unter des Limits
                int dasLimit = ue.limit;
                while (betrag > dasLimit) {
                    betrag = betrag - dasLimit;
                    a2.ueberweisenAuf(a1, dasLimit);
                    System.out.println("a1: "+ a1 + "; a2= " +a2);
                }
                a2.ueberweisenAuf(a1, betrag);
            }
            System.out.println("a1: "+ a1 + "; a2= " +a2);
        }
        catch (UeberweisungsAusnahme12 ue) {
            System.out.println("Fehler aufgetreten " + ue.getMessage());
            System.out.println ("Eine Überweisung wurde nicht ausgeführt");
        }
    }
}

Klasse Ueberweisungsausnahme12

Die Klasse wird aus der Klasse Exception abgeleitet. Sie ist eine "checked Exception" und muss behamdelt oder deklariert werden

package s1.block10;
public class UeberweisungsAusnahme12 extends Exception {
    public UeberweisungsAusnahme12(String text) {
        super(text);
    }
}

 

Stefan Schneider Sun, 03/11/2012 - 17:31

10.9 Lernziele (Ausnahmen)

10.9 Lernziele (Ausnahmen)

Am Ende dieses Blocks können Sie:

  • ...  abschätzen wann Sie im Programmablauf Ausnahmen einsetzen um den Kontrollfluß übersichtlich zu gestalten
  • ...  wissen Sie wie Sie auf Ausnahmen reagieren können ohne das die Anwendung beendet wird
  • ...  die Syntax  von try-catch Blöcken anwenden um Ausnahmen zu behandeln
  • ...  mit Hilfe der Exception-Klasshierarchie entscheiden, welcher try-catch Block beim Auftreten einer Ausnahme ausgeführt wird
  • ...  zwischen checked- und unchecked Exceptions unterscheiden und die entsprechenden Implementierungsmaßnahmen treffen.
  • ...  selbst Ausnahmen werfen
  • ...  selbst Ausnahmeklassen implementieren 
  • ... die folgenden Klassen in ihre Vererbungshierarchie einordnen: Throwable, Error, Exception, RuntimeException
  • ... try-catch Blöcke mit mehren catch Blöcken interpretieren

Lernzielkontrolle

Sie sind in der Lage die folgenden Fragen zu beantworten: Fragen zu Ausnahmen (Exception)

Feedback

Zur Umfrage

QR Code für Umfrage

Stefan Schneider Fri, 01/11/2013 - 16:55