Drucken

Exceptions

Exceptions geben uns die Möglichkeit, um auf Fehler oder aussergewöhnliche Situationen zu reagieren. Es handelt sich um eine Mechanismus, um Fehler in Programmen abzufangen, ohne dass man im vorneherein genau weiss, wo und unter welchen Bedingungen sie auftreten.

Im folgenden Beispiel berechnet die Methode getRoots() die Quadratwurzeln der Quadratischen Gleichung
gemäss der Lösungsformel
 

Dabei werden die Koeffizienten a, b und c von der Console eingelesen.

Beispiel 1:
Gibt man die Zahlen 1, 2, 3 ein, so ist die Diskriminante negativ und das Ziehen der Quadratwurzel führt zu einem Fehler. Gibt man dagegen die Zahlen 1, 4 , 3 ein, so erhält man die korrekten Lösungen.

// ExceptEx1.java

import ch.aplu.util.*;

public class ExceptEx1 extends Console
{
  public ExceptEx1()
  {
    while (true)
    {
      println("Roots of the quadratic equation");
      print("a = ");
      double a = readDouble();
      print("b = ");
      double b = readDouble();
      print("c = ");
      double c = readDouble();
      double[] roots = getRoots(a, b, c);
      println("Roots: (" + roots[0] + ", " + roots[1] + ")");
    }
  }

  private double[] getRoots(double a, double b, double c)
  {
    double discriminant = b * b - 4 * a * c;
    double root1 = (-+ Math.sqrt(discriminant)) / (2 * a);
    double root2 = (-- Math.sqrt(discriminant)) / (2 * a);
    double[] result = {root1, root2};
    return result;
  }

  public static void main(String[] args)
  {
    new ExceptEx1();
  }
}
 

Erklärungen zum Programmcode:
extends Console
Dadurch können die Methoden von Console direkt aufgerufen werden
double[] getRoots Die beiden Lösungen werden mit einem Array zurückgegeben

Im Fehlerfall gibt Java den "Wert" NaN (Not a Number) zurück, was allerdings zu einem unvorhersehbaren Verhalten bei komplexeren Programmen führt. Es ist angebracht, die Methode getRoots() so zu schreiben, dass im Fehlerfall eine entsprechende Information an den Aufrufer zurück gegeben wird. Wir werfen dazu bei einer negativen Diskriminante eine Exception. Dadurch wird der weitere Programmablauf abgebrochen und getRoots() kehrt sofort zurück.

Beispiel 2:

// ExceptEx2.java

import ch.aplu.util.*;

public class ExceptEx2 extends Console
{
  public ExceptEx2()
  {
    while (true)
    {
      println("Roots of the quadratic equation");
      print("a = ");
      double a = readDouble();
      print("b = ");
      double b = readDouble();
      print("c = ");
      double c = readDouble();
      double[] roots = getRoots(a, b, c);
      println("Roots: (" + roots[0] + ", " + roots[1] + ")");
    }
  }

  private double[] getRoots(double a, double b, double c)
  {
    double discriminant = b * b - 4 * a * c;
    if (discriminant < 0)
      throw new IllegalArgumentException("negative discriminant");
    double root1 = (-+ Math.sqrt(discriminant)) / (2 * a);
    double root2 = (-- Math.sqrt(discriminant)) / (2 * a);
    double[] result = {root1, root2};
    return result;
  }

  public static void main(String[] args)
  {
    new ExceptEx2();
  }
}
 

Erklärungen zum Programmcode:

throw new IllegalArgumentException
Da es sich bei einer IllegalArgumentException um eine implizite Exception handelt, muss diese nicht unbedingt gefangen werden . Im Fehlerfall wird die Exception von getRoots() an den Konstruktor ExceptEx2() weiter gegeben, worauf dieser sie an main() weiter gibt, was zum Abbruch des Programms führt.

Im Online-Editor tritt ein Anwendungsfehler auf (Anwendung konnte nicht gestartet werden). Das Klicken auf den Button Details zeigt genau, wo der Fehler aufgetreten ist.

Beispiel 3:
Im nächsten Beispiel vermeiden wir den Programmabsturz, in dem wir die Exception fangen. Dazu setzten wir den Aufruf von getRoots() in ein try - catch - Konstrukt. Tritt der Fehler im try-Block auf, so wird dieser sofort verlassen und es wird der catch-Block ausgeführt.

// ExceptEx2.java

import ch.aplu.util.*;

public class ExceptEx3 extends Console
{
  public ExceptEx3()
  {
    while (true)
    {
      println("Roots of the quadratic equation");
      print("a = ");
      double a = readDouble();
      print("b = ");
      double b = readDouble();
      print("c = ");
      double c = readDouble();
      try
      {
        double[] roots = getRoots(a, b, c);
        println("Roots: (" + roots[0] + ", " + roots[1] + ")");
      }
      catch (IllegalArgumentException ex)
      {
        println("no solution");
      }
    }
  }

  private double[] getRoots(double a, double b, double c)
  {
    double discriminant = b * b - 4 * a * c;
    if (discriminant < 0)
      throw new IllegalArgumentException("negative discriminant");
    double root1 = (-+ Math.sqrt(discriminant)) / (2 * a);
    double root2 = (-- Math.sqrt(discriminant)) / (2 * a);
    double[] result = {root1, root2};
    return result;
  }

  public static void main(String[] args)
  {
    new ExceptEx3();
  }
}
 

Erklärungen zum Programmcode:

catch ( IllegalArgumentException  ex )
Wir vermeiden den Programmabsturz und informieren den Benutzer lediglich, dass die Gleichung keine Lösung besitzt.

Beispiel 4:
Das nächste Beispiel zeigt, dass man durch Verwendung von expliziten Exceptions erreichen kann, dass diese gefangen werden MUSS.

// ExceptEx4.java

import ch.aplu.util.*;

public class ExceptEx4 extends Console
{
  public ExceptEx4()
  {
    while (true)
    {
      println("Roots of the quadratic equation");
      print("a = ");
      double a = readDouble();
      print("b = ");
      double b = readDouble();
      print("c = ");
      double c = readDouble();
      try
      {
        double[] roots = getRoots(a, b, c);
        println("Roots: (" + roots[0] + ", " + roots[1] + ")");
      }
      catch (Exception ex)
      {
        println("no solution");
      }
    }
  }

  private double[] getRoots(double a, double b, double c)
    throws Exception
  {
    double discriminant = b * b - 4 * a * c;
    if (discriminant < 0)
      throw new Exception("negative discriminant");
    double root1 = (-+ Math.sqrt(discriminant)) / (2 * a);
    double root2 = (-- Math.sqrt(discriminant)) / (2 * a);
    double[] result = {root1, root2};
    return result;
  }

  public static void main(String[] args)
  {
    new ExceptEx4();
  }
}
 

Erklärungen zum Programmcode:

throw new Exception ()
Damit kein Syntaxfehler auftritt, muss getRoots() mit throws Exception die Exception an den Aufrufer weiter werfen. Dieser fängt sie in einem catch-Block ab

Beispiel 5:
Das nächste Beispiel zeigt eine interessante Anwendung, bei die Benutzereingabe über ein GUI auf Korrektheit überprüft wird. Dazu wird der mit einer JOptionPane eingelesene String überprüft, ob es sich um eine Integer-Zahl handelt. Dabei wird die Exception abgefangen, die bei der Umwandlung des Strings in einen Integer geworfen wird.

// ExceptEx5.java

import javax.swing.*;

public class ExceptEx5
{
  public ExceptEx5()
  {
    Integer n = getInt("Gib eine Ganzzahl ein""Eingabe");
    String msg;
    if (== null)
      msg = "Abgebrochen";
    else
      msg = "Erhalten: " + n;

    JOptionPane.showMessageDialog(
      null, msg, "Antwort"JOptionPane.INFORMATION_MESSAGE);
  }

  private Integer getInt(String prompt, String title)
  {
    Integer value = null;
    String valueStr;
    boolean ok = false;

    while (!ok)
    {
      valueStr = getString(prompt, title);
      if (valueStr == null)
        return null;
      try
      {
        value = new Integer(valueStr);
        ok = true;
      }
      catch (NumberFormatException e)
      {
      }
    }
    return value;
  }

  private String getString(String prompt, String title)
  {
    String valueStr;
    boolean ok = false;

    do
    {
      valueStr = JOptionPane.showInputDialog(
        null, prompt, title, JOptionPane.QUESTION_MESSAGE);
      if (valueStr == null)
        return null;
    }
    while (valueStr.trim().length() == 0);
    return valueStr.trim();
  }

  public static void main(String[] args)
  {
    new ExceptEx5();
  }
}
 

Erklärungen zum Programmcode:

valueStr  = JOptionPane . showInputDialog ( )
Es wird ein modaler Eingabedialog mit einem Textfeld und den Buttons Ok und Cancel gezeigt. Beim Drücken des Cancel-Buttons wird Null zurückgegeben und diese Information dazu gebraucht, das Programm zu beenden.
while ( ! ok )
Der Eingabe-Dialog wird so lange gezeigt, bis ein korrekte Eingabe erfolgt ist.