Dione
Z. KotalaP. Toman: Java
Predchozi (Obsah) Dalsi

13. Výjimky

Výjimka (exception) je definována jako událost, která nastane během provádění programu a která naruší normální běh instrukcí. Výjimka je vyvolána například při chybném otevření souboru, při překročení mezí pole, při aritmetické chybě apod. Mechanismus výjimek umožňuje tyto chybové stavy zachytit a ošetřit. K tomu slouží dvojice bloků try a catch, jejichž syntaxe je následující:

   try {
      // hlídaný blok
   } catch( třídaVýjimek1 jménoProměnné1 ) {
      // ošetření výjimky
   } catch( třídaVýjimek2 jménoProměnné2 ) {
      // ošetření výjimky
   }

  • Hlídaný blok try - uzavírá kritickou část programu, kde "může" být vyvolána výjimka.

  • Záchytné bloky catch - ošetřují výjimky, musí následovat bezprostředně za blokem try a může jich být libovolný počet.

    Vyvolaná výjimka je objekt a každý záchytný blok zachycuje jeden typ výjimek (třídaVýjimek1, třídaVýjimek2). Na vyvolanou výjimku v záchytném bloku odkazuje referenční proměnná (jménoProměnné1, jménoProměnné2).

    Pokud v hlídaném bloku výjimka:

    • není vyvolána, pokračuje pokračuje program za posledním záchytným blokem.

    • je vyvolána, pokračuje program prvním záchytným blokem, který ošetřuje odpovídající třídu výjimek (viz 13.1.). Po opuštění záchytného bloku pokračuje program za posledním záchytným blokem.

      Pozn.: Hlídané a záchytné bloky je možné do sebe vnořovat, tj. každý záchytný blok může opět obsahovat dvojici bloků try-catch.

Priklad 13.1.
Metoda nactiBajt otevře soubor zadaného jména (1) a načte z něj první byte, který vypíše na standardní výstup (1) (2).

void nactiBajt(String jSouboru) {
   try {
      FileInputStream soubor = new FileInputStream(jSouboru);   // (1)
      System.out.println(soubor.read());                        // (2)

   } catch(FileNotFoundException e) {                           // (3)
      System.out.println("Soubor " + jSouboru + "nenalezen.");  // (4)
   } catch(IOException e) {                                     // (5)
      System.out.println("Chyba při čtení souboru " + jSouboru);// (6)
   }
}
Při pokusu otevření souboru se může stát, že tento nebude nalezen (nastane výjimka FileNotFoundException), např. pokud soubor neexistuje nebo je chybně zadané jméno. Pokud tento výjimečný stav nastane, je zachycen blokem catch na řádce (3) a vypíše se chybová zpráva (4).

Také při čtení může nastat chyba (výjimka IOException), např. pokud soubor není určen pro čtení nebo nastane chyba zařízení. Tento stav zachycuje blok catch na řádce (5) a vypíše se chybová zpráva (6).

Pokud vše proběhne bez problémů, pokračuje program za záchytnými bloky (metoda skončí).

Pozn.: Všimněte si, že díky výjimkám je důsledně oddělen "užitečný kód" od kódu, který pouze ošetřuje chyby. Výjimka jedné třídy může být navíc vyvolána v hlídaném bloku na několika místech a k ošetření postačuje jeden blok catch. Ve srovnání s klasickým způsobem testování chyb (příkazem if) je zachycování chyb pomocí výjimek mnohem přehlednější.

13.1. Třídy výjimek

Rodičovskou třídou všech výjimek je Throwable (z balíku java.lang), která má standardně dva přímé potomky:

  • Error - reprezentuje fatální chyby, které by neměly být zachycovány, neboť se z nich nelze zotavit (chyba JVM apod.).

  • Exception - slouží jako rodičovská třída pro všechny ostatní výjimky.

Část stromu výjimek třídy Exception vypadá takto:

vyjimky.gif

Díky objektové hierarchii výjimek je možné zachytit více tříd výjimek v jednom catch bloku - jedna třída výjimek v sobě zahrnuje i všechny potomky této třídy.

Protože však u záchytných bloků záleží na pořadí, (2) je možné některé potomky zachytit zvlášť a ostatní nechat bloku zachycující výjimky rodičovské třídy:

Priklad 13.2.
try {
   // ...
} catch(ArithmeticException e) {
   // zachycení výjimek třídy ArithmeticException
} catch(Exception e) {
   // zachycení všech výjimek třídy Exception , tj.
   // InterruptedException , RuntimeException ,
   // IndexOutOfBoundsException atd. (kromě ArithmeticException )
}

Pozn.: Programátor si samozřejmě může vytvořit vlastní třídu výjimek - stačí vytvořit potomka některé z tříd výjimek z Java Core API (nedoporučuje se však jako rodičovskou třídu použít přímo Throwable).

13.2. Vyvolání výjimky

Výjimky je možné programově vyvolávat příkazem throw. Jeho syntaxe je:

   throw instanceVýjimky ;

Výjimka instanceVýjimky musí být instancí třídy Throwable nebo jejích potomků.

Priklad 13.3.
Vyvolání výjimky třídy ArithmeticException se provede pomocí:

throw new ArithmeticException();

13.3. Deklarace výjimek

Všechny výjimky, které mohou v dané metodě nastat, musí být zachyceny nebo deklarovány (viz 11.2.2.), jinak překladač ohlásí chybu. (3) Druhý případ znamená, že metoda přenechává ošetření deklarovaných výjimek volající metodě - dochází k tzv. propagaci výjimek (viz 13.4.). Volající metoda opět musí tyto (resp. všechny) výjimky zachytit nebo deklarovat.

Deklarace výjimek se používá zejména v případě, že na dané úrovni (např. přímo v knihovně) není vhodné provádět ošetření výjimky (není možné rozhodnout, jak by mělo vypadat ošetření):

Priklad 13.4.
Konstruktor FileInputStream(), který otevírá soubor zadaného jména (viz příklad 13.1.) neošetřuje případ, že soubor není nalezen (nastane výjimka FileNotFoundExeption) a přenechává tuto starost volající metodě. Ta může nechat uživatele zadat jméno souboru znovu, doplnit si jméno sama, skončit atd.

13.4. Propagace výjimek

Výjimka může být uvnitř metody vyvolána dvěma způsoby:

  • příkazem throw (viz 13.2.),

  • voláním metody, která výjimku deklaruje (viz 13.3.).

Pokud není vyvolaná výjimka zachycena blokem catch (viz str. 13) v metodě, kde byla vyvolána, je předána o úroveň výš (volající metodě). Takto se výjimka rekurzivně předává dokud nedojde k jejímu zachycení a v krajním případě je zachycena runtime systémem, který zachycuje všechny výjimky (standardně vypíše chybovou zprávu a ukončí program). Tento mechanismus se nazývá propagace výjimek.

Priklad 13.5.
public class Test {
   public static void main(String[] args) {
      try {
         a();                                        // (1)
      } catch(Exception e) {                         // (2)
         System.out.println("Výjimka zachycena.");
         e.printStackTrace();                        // (3)
      }
   }

   static void a() throws Exception {                // (*)
      b();                                           // (4)
   }

   static void b() throws Exception {                // (**)
      throw new Exception();                         // (5)
   }
}

Program začíná metodou main(), kde je volána metoda a() (1), která následně volá metodu b() (4), v níž je vyvolána výjimka Exception (5). Tato výjimka není metodou b() zachycena a je proto propagována do volající metody a(). Zde rovněž není zachycena a je dále propagována do metody main(), kde je konečně zachycena (2) a způsobí výpis (3):

Vyjimka zachycena.
java.lang.Exception
        at Test.b(A.java:16)
        at Test.a(A.java:12)
        at Test.main(A.java:4)
Obě metody, v nichž může být výjimka vyvolána, ale není zachycována, ji deklarují (*), (**).

13.5. Runtime výjimky

Zvláštní postavení mezi výjimkami mají tzv. runtime výjimky - instance třídy RuntimeException a jejích potomků. Tyto výjimky nemusí být v metodách zachycovány ani deklarovány.

Runtime výjimky totiž reprezentují chyby, které mohou nastat "kdekoliv" v programu. Jedná se například o ArrayIndexOutOfBoundsException (přetečení indexu pole) nebo ArithmeticException (aritmetická chyba - celočíselné dělení nulou) apod.

Ošetřování těchto výjimek by mnohdy znamenalo zbytečnou práci navíc, neboť například ve for cyklu lze snadno zajistit, aby index pole meze nepřekročil. Pokud by ArrayIndexOutOfBoundsException nebyla runtime výjimkou, bylo by ji nutné v každé metodě, která pole používá zachytit nebo deklarovat.

13.6. Koncový blok (finally)

Dvojici bloků try - catch (viz str. 13) je možné rozšířit o nepovinný koncový blok finally následovně:

   try {
      // hlídaný blok
   } catch( třídaVýjimek1 jménoProměnné1 ) {
      // ošetření výjimky
   } finally {
      // zde uzavřený kód se provede vždy
   }

Koncovým blokem program pokračuje po ukončení hlídaného i záchytného bloku (tj. ať už výjimka nastane nebo ne). Koncový blok se vykoná dokonce i v případě, že:

  • je v try bloku vyvolána výjimka, kterou žádný catch blok nezachycuje, např. runtime výjimka (viz 13.5.) - a to ještě před propagací této výjimky,

  • je v hlídaném bloku vyvolán příkaz return (viz 9.13.).


  • (1) Pro čtení ze souboru použita je třída FileInputStream (viz kap. 15.) z balíku (viz kap. 12.) java.io. Ve stejném balíku jsou definovány i třídy výjimek FileNotFoundException a IOException.
  • (2) Při vyvolání výjimky v hlídaném bloku se hledá první catch blok, který zachycuje odpovídající třídu výjimek.
  • (3) Výjimkou z toho pravidla jsou runtime výjimky (viz 13.5.) a výjimky třídy Error.

Predchozi
Converted by Selathco v0.9 on 25.09.1999 19:46
Dalsi