Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Java ist auch eine Insel von Christian Ullenboom
Buch: Java ist auch eine Insel (Galileo Computing)
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 Ausnahmen, die nicht aufgefangen werden müssen: RuntimeException
gp 7.2.5 Harte Fehler: Error
gp 7.3 Werfen eigener Exceptions
gp 7.3.1 Typecast auf ein null-Objekt für eine NullPointerException
gp 7.3.2 Neue Exception-Klassen definieren
gp 7.4 Rückgabewerte bei ausgelösten Ausnahmen
gp 7.5 Stack-Aufruf analysieren
gp 7.6 Assertions
gp 7.6.1 Assertions in eigenen Programmen nutzen
gp 7.6.2 Assertions aktivieren
gp 7.6.3 Assertion-Nutzung in den Sun-Quellen
gp 7.7 Sicherheitsfragen mit dem SecurityManager klären
gp 7.7.1 Programm beenden


Galileo Computing

7.3 Werfen eigener Exceptionsdowntop

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. 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 der Übergabeparameter falsch sein, so wird eine Illegal ArgumentException ausgelöst.

Listing 7.7 Schokolade.java

class Schokolade
{
  public final static int WEISS = 0, BRAUN = 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();
  }
}

Es ist bei diesem Programm deutlich, dass die Fehlerquelle dadurch verringert wird, dass Konstanten die Eigenschaften des Objekts beschreiben. 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.1 Typecast auf ein null-Objekt für eine NullPointerExceptiondowntop

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.8 ThrowNull.java

class ThrowNull
{
  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.2 Neue Exception-Klassen definierentoptop

Eigene Fehlerarten werden definiert, indem die Klasse Exception in einer Unterklasse erweitert wird. Dabei werden oft zwei Konstruktoren verwendet, ein Standard-Konstruktor und ein Zweiter mit einem String als Parameter. Um für die Klasse Schokolade im letzten Beispiel einen neuen Fehlertyp zu definieren, erweitern wir IllegalArgumentException zur illegalen Schoko-Farbe.

Listing 7.9 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.10 Schokolade2.java

  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.






Copyright (c) Galileo Press GmbH 2004
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, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de