Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

 << zurück
Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 5
Java ist auch eine Insel

Java ist auch eine Insel
5., akt. und erw. Auflage
1454 S., mit CD, 49,90 Euro
Galileo Computing
ISBN 3-89842-747-1
gp Kapitel 7 Exceptions
  gp 7.1 Problembereiche einzäunen
    gp 7.1.1 Exceptions in Java mit try und catch
    gp 7.1.2 Eine Datei auslesen mit RandomAccessFile
    gp 7.1.3 Ablauf einer Ausnahmesituation
    gp 7.1.4 Wiederholung kritischer Bereiche
    gp 7.1.5 throws im Methodenkopf angeben
    gp 7.1.6 Abschließende Arbeiten mit finally
    gp 7.1.7 Nicht erreichbare catch-Klauseln
  gp 7.2 Die Klassenhierarchie der Fehler
    gp 7.2.1 Die Exception-Hierarchie
    gp 7.2.2 Oberausnahmen fangen
    gp 7.2.3 Alles geht als Exception durch
    gp 7.2.4 RuntimeException muss nicht aufgefangen werden
    gp 7.2.5 Harte Fehler: Error
  gp 7.3 Werfen eigener Exceptions
    gp 7.3.1 Mit throw Ausnahmen auslösen
    gp 7.3.2 Typecast auf ein null-Objekt für eine NullPointerException
    gp 7.3.3 Neue Exception-Klassen definieren
    gp 7.3.4 Geschachtelte Ausnahmen
  gp 7.4 Rückgabewerte bei ausgelösten Ausnahmen
  gp 7.5 Der Stack Trace
    gp 7.5.1 Stack Trace aus Throwable
    gp 7.5.2 Stack Trace aus Thread
  gp 7.6 Assertions
    gp 7.6.1 Assertions in Java
    gp 7.6.2 Assertions in eigenen Programmen nutzen
    gp 7.6.3 Assertions aktivieren


Galileo Computing

7.3 Werfen eigener Exceptiondowntop

Bisher wurden Exceptions lediglich aufgefangen, aber noch nicht selbst erzeugt. Routinen, die durch Exceptions das Misslingen einer Operation anzeigen, finden sich im Laufzeitsystem oder in der Standardbibliothek zu Genüge.


Galileo Computing

7.3.1 Mit throw Ausnahmen auslösen  downtop

Soll eine Funktion selbst eine Exception auslösen, muss sie ein Exception-Objekt erzeugen und die Ausnahmebehandlung anstoßen. Im Sprachschatz dient das Schlüsselwort throw dazu, eine Ausnahme auszulösen.

public void ichKann( String s )
{
  if ( ! kannIchWasMitStringMachen( s ) )
      throw new SecurityException   ( "Ätsch, das kannst du mit " + s +
                                  " nicht machen!" );
}

Kann mit der übergebenen Zeichenkette s eine bestimmte Operation nicht ausgeführt werden, so wird mit new ein SecurityException-Objekt erzeugt und diesem eine Zeichenkette als Fehlermeldung mit auf den Weg gegeben. Für nicht passende Werte sieht die Standardbibliothek etwa die Fehlerklasse IllegalArgumentException vor.

Gerne werden Exceptions in den default-Zweig einer switch-Anweisung mit hineingenommen. Im folgenden Beispiel wird versucht, ein Exemplar der Klasse Schokolade mit einer Farbe zu erzeugen. Sollte das übergebene Argument falsch sein, so wird eine IllegalArgumentException ausgelöst.

Listing 7.9   Schokolade.java

class Schokolade
{
  public final static int WEISS = 0BRAUN = 1;
  private int farbe;
  Schokolade( int f )
  {
    switch( f )
    {
      case WEISS:
      case BRAUN: farbe = f;
                  break;
      default   : throw new IllegalArgumentException(
                               "Falsche Schoko-Farbe: " + f );
     }
  }
  public void test()
  {
    System.out.println( "Aha, du magst also " +
                        ( ( farbe == WEISS) ? "weisse " : "braune " ) +
                        "Schokolade gerne!" );
  }
  public static void main( String[] args )
  {
//    Schokolade ws = new Schokolade( Schokolade.BRAUN );
    Schokolade ws = new Schokolade( 4 );
    ws.test();
  }
}

Nach dem Aufruf bekommen wir folgende Meldung:

java.lang.IllegalArgumetException: Falsche Schoko-Farbe: 4
        at Schokolade.<init>(Schokolade.java:13)
        at Schokolade.main(Schokolade.java:28)

Galileo Computing

7.3.2 Typecast auf ein null-Objekt für eine NullPointerException  downtop

Mit einem anderen Trick lässt sich die manuelle Objekterzeugung beim Auslösen einer Exception umgehen. Werfen wir einen Blick auf folgendes Codebeispiel:

Listing 7.10   ThrowNull.java

class ThrowNull
{
  @SuppressWarnings( "all" )
  static void bar()
  {
    throw   (RuntimeException) null;
    }
  public static void main( String[] args )
  {
    bar();
  }
}

Das Programm kompiliert und löst eine NullPointerException aus.

java.lang.NullPointerException
   at ThrowNull.bar(ThrowNull.java:5)
   at ThrowNull.main(ThrowNull.java:10)

Da wir null auf RuntimeException casten, müssen wir auch kein throws in der Methodendeklaration angeben und den Fehler auch nicht bei bar() behandeln. Das ist aber ein recht fragwürdiger Stil, da hier das hierarchische Typsystem für Fehlerarten unterlaufen wird und alle Fehler zur NullPointerException werden.


Galileo Computing

7.3.3 Neue Exception-Klassen definieren  downtop

Eigene Exceptions sind direkte oder indirekte Unterklassen von Exception. Sie implementieren oft zwei Konstruktoren: einen Standard-Konstruktor und einen mit einem String parametrisierten Konstruktor. Um für die Klasse Schokolade im letzten Beispiel einen neuen Fehlertyp zu definieren, erweitern wir IllegalArgumentException zur illegalen Schoko-Farbe.

Listing 7.11   IllegalSchokoColorException.java

public class IllegalSchokoColorException   extends IllegalArgumentException
  {
  public IllegalSchokoColorException()
  {
    super();
  }
  public IllegalSchokoColorException( String s )
  {
    super( s );
  }
}

Nehmen wir uns die Abfrage noch einmal vor, dann wird anstelle der vorherigen IllegalArgumentException eine IllegalSchokoColorException ausgelöst:

throw new IllegalSchokoColorException(
  "Diese Schokoladen-Farbe gibt es nicht: " + f );

Im Hauptprogramm können wir auf diese Ausnahme reagieren: Entweder wir fangen in einem Aufruf IllegalSchokoColorException ab, oder wir lassen die spezielle IllegalArgumentException, die ja eine RuntimeExeption ist und daher nicht abgefangen zu werden braucht, die JVM beenden. Um die alte Schokoladen-Klasse mit der IllegalArgumentexception von der neuen mit der eigenen Fehlerklasse IllegalSchokoColorException zu trennen, nennen wir die neue Klasse Schokolade2.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Listing 7.12   Schokolade2.java, main()

public static void main( String[] args )
{
  try
  {
    Schokolade2 ws = new Schokolade2( 4 );
    ws.test();
  }
  catch( IllegalSchokoColorException isce )
  {
    System.err.println( "Falsche Schokoladen-Farbe abgefangen" );
    isce.printStackTrace();
  }
  System.err.println();
  Schokolade2 ws1 = new Schokolade2(3);
  ws1.test();          // Abbruch duch IllegalSchokoColorException
}

Die erwartete Ausgabe ist:

Falsche Schokoladen-Farbe abgefangen
IllegalSchokoColorException: Diese Schokoladen-Farbe gibt es nicht: 4
    at Schokolade2.<init>(Schokolade2.java:26)
    at Schokolade2.main(Schokolade2.java:40)
IllegalSchokoColorException: Diese Schokoladen-Farbe gibt es nicht: 3
    at Schokolade2.<init>(Schokolade2.java:26)
    at Schokolade2.main(Schokolade2.java:50)
Exception in thread "main"

Tipp   Es ist immer eine gute Idee, Unterklassen von Exception zu bauen. Würden wir keine Unterklassen anlegen, sondern direkt mit throw new Exception() einen Fehler anzeigen, so könnten wir unseren Fehler später nicht mehr von anderen Fehlern unterscheiden. Denn mit der Hierarchiebildung wird die Spezialisierung bei mehreren catch-Anweisungen unterstützt sowie eine Unterscheidung mit instanceof. Wir müssen immer unseren Fehler mit catch(Exception e) auffangen und bekommen so alle anderen Fehler mit aufgefangen, die dann nicht mehr unterschieden werden können.


Galileo Computing

7.3.4 Geschachtelte Ausnahmen  toptop

Besonders bei eigenen Ausnahmen mag ein Grund für die Auslösung sein, dass ein eingebetteter Teil versagt. Das ist vergleichbar mit einer Transaktion: Ist ein Teil der Kette fehlerhaft, so ist der ganze Teil nicht ausführbar. Bei unseren Ausnahmen ist das nicht anders. Nehmen wir an, wir haben eine Methode, die, wenn sie fehlschlägt, eine Ausnahme auslöst. Ruft unsere Methode nun eine Unterfunktion auf, die zum Beispiel eine Ein-/Ausgabeoperation tätigt, doch das geht schief, wird der Anlass für unsere Exception die IO-Exception sein. Es ist also nahe liegend, bei der Nennung des Grundes für das eigene Versagen die Unteraufgabe zu nennen. (Wieder ein Beweis, wie »menschlich« Programmieren sein kann.)

Eine geschachtelte Ausnahme (engl. nested exception) speichert einen Verweis auf eine weitere Ausnahme. Wenn ein Exception-Objekt aufgebaut wird, lässt sich der Grund (engl. cause) als Argument im Konstruktor der Throwable-Klasse übergeben. Die Ausnahme-Basisklasse bietet dafür zwei Konstruktoren:

gp  Throwable( Throwable cause )
gp  Throwable( String message, Throwable cause )

Der Grund kann über die Methode Throwable getCause() erfragt werden.

Da Konstruktoren in Java nicht vererbt werden, bieten die Unterklassen oft Konstruktoren an, um den Grund anzunehmen. Zumindest Exception macht das und kommt somit auf vier Erzeuger:

gp  Exception()
gp  Exception( String message )
gp  Exception( String message, Throwable cause )
gp  Exception( Throwable cause )

Einige der tiefer liegenden Unterklassen haben dann auch diese Konstruktor-Typen mit Throwable-Parameter, andere, wie IOException, wiederum nicht. Für eigene Unterklassen etwa von IOException ist das nicht so schlimm, denn Throwable bietet mit initCause() die Möglichkeit, genau einmal eine geschachtelte Ausnahme anzugeben.

In modernen Frameworks ist die Nutzung von Ausnahmen, die nicht geprüft werden müssten, also Exemplare von RuntimeException sind, häufiger geworden. Bekannte zu prüfende Ausnahmen werden in RuntimeException-Objekte verpackt (eine Art Exception-Wrapper), die den Verweis auf die auslösende nicht- RuntimeException speichern.

 << zurück




Copyright © Galileo Press GmbH 2005
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de