Tento článek si klade za cíl seznámit čtenáře s principem fungování mechanismu výjimek v prostředí platformy Java. Ukážeme si používání konstrukcí try, catch a finally, základní rozdělení výjimek a jejich objektovou hierarchii.

Výjimku chápeme v kontextu Javy jako důsledek výjimečné, neočekávané či chybové události v rámci vykonávání programu. Je trochu zavádějící uvažovat o výjimkách pouze v intencích chyb. Mechanismus výjimek pokrývá daleko širší prostor. V případě vzniku výjimky bychom měli chování kódu podřídit třem základním faktorům:

  • úspěšně zpracovat vlastní výjimku
  • umožnit zpětné dohledání a analýzu výjimky
  • podat srozumitelnou informaci o výjimce navenek systému

Zpracování vlastní výjimky je proces, kdy se v kódu rozhodujeme, jak s výjimkou naložit. Aniž bychom nyní zabíhali do rozdělení výjimek, ke kterému se dostaneme v další části článku, prozradíme si, že výjimku můžeme ošetřit a nebo ji postoupit výše. Pokud máme v kontextu zachycení výjimky dost informací na její zpracování, měli bychom tak učinit.

Zpětné dohledání výjimky nastává v případech, kdy analyzujeme vznik výjimky, například při selhání systému nebo jeho neočekávaném chování. Činnost zpětného dohledání výjimky klade důraz na zaznamenání vlastní výjimky a poskytnutí maximálního počtu indicií, ze kterých je možné odvodit vznik výjimky. Zaznamenání výjimky spadá na úroveň aplikačního logu.

Podáním srozumitelné informace se rozumí takové chování, kdy klient systému porozumí důvodu vzniku výjimky. Pokud předaná informace vyvolaná vznikem výjimky inklinuje k variaci na téma zprávy Chyba:DataFormatException, dosáhnete spíše uživatelova zmatení než srozumitelného vysvětlení, že bylo zadáno datum v nesprávném formátu. Podání srozumitelné informace má více aspektů, například lokalizace chybové zprávy, vlastní forma zprávy (klient nemusí být uživatel, ale jiný systém) a podobně.

Try, catch, finally konstrukce a příkazy throw a throws

Před rozdělením výjimek si zopakujeme základní příkazy a konstrukce pro práci s výjimkami.

Chráněný blok, ošetření výjimky a finally

Chráněný blok je uvozen příkazem try a uzavřen mezi složené závorky. Jakoukoli výjimku vzniklou v chráněném bloku je možné ošetřit. K ošetření se používá příkaz catch, za nímž následuje konkrétní datový typ výjimky, kterou bude tento blok zpracovávat. Blok ošetření je opět omezen složenými závorkami.

try{
    //chráněný blok
}catch(Datovy typ výjimky){
    //ošetření výjimky
}

K chráněnému bloku patří i blok finally. Pokud vznikne v chráněném bloku výjimka, běh programu pokračuje nejdříve přes nejbližší blok catch, který ošetřuje výjimku daného datového typu, a poté přes blok finally (pokud je deklarován). Pokud výjimka nevznikne, běh programu pokračuje po posledním příkazu chráněného bloku opět přes blok finally. Blok finally se proto využívá pro operace, které musí být provedeny bez ohledu na fakt, jestli k výjimce dojde či nikoli – velmi často jde o uvolnění zdrojů (databázové připojení, soubor) alokovaných v rámci chráněného bloku.

try{
    //chráněný blok
}catch(Datovy typ výjimky){
    //ošetření výjimky
}finally{
    //kód, provedený bez ohledu na vznik výjimky
}

Je potřeba si uvědomit, že blok finally lze využít i bez bloku catch.

try{
    //chráněný blok
}finally{
    //kód, provedený bez ohledu na vznik výjimky
}

K výjimkám nerozlučně patří i příkazy throw a throws. Příkaz throw se používá k vyhození výjimky. V podstatě existují dva způsoby – buď konstruktorem vytvoříme novou výjimku a tu pomocí throw vyhodíme (throw new MyException();), nebo instanci výjimky již máme (například zachycením v bloku catch) a příkazem throw ji znovu vyhodíme.

Příkaz throws se zapisuje v signatuře metody a tvoří součást API public String doSomething() throws MyException(). Za throws následuje výčet datových typů výjimek, které může metoda vyhazovat. Jakoukoli výjimku deklarovanou v signatuře není programátor nucen uvnitř metody ošetřovat. Naopak throws lze z hlediska klienta API považovat za zřeknutí se výjimky.

Základní rozdělení výjimek

Už víme, co je to výjimka a jak bychom na ni měli nahlížet. Nyní nastal čas seznámení s rozdělením výjimek. Java podporuje dva základní typy výjimek z hlediska jejich správy:

  • výjimky kontrolované, takzvané checked exception
  • výjimky nekontrolované, takzvané běhové či runtime exception

Kontrolované výjimky

Kontrolované výjimky jsou charakteristické tím, že klientský kód je nucen tyto výjimky ošetřit (catch blok) anebo propagovat (throws). Kontrola, jestli se tak děje, je provedena staticky, tedy v době kompilace kódu. Standardní API Javy deklaruje několik desítek kontrolovaných výjimek, které mohou nastat při práci s tou či onou částí API. Podívejme se například na ošetření a propagace výjimky vzniklé při práci se souborem.

Ošetření kontrolované výjimky

public void configure(final String confFileName){
    Properties props;
    try{
        File confFile = new File(confFileName);
        FileInputStream fis = new FileInputStream(confFile);
        props = new Properties();
        props.load(fis);
    }catch(FileNotFoundException fnf){
        //použijeme defaultní konfiguraci
        props = defaultProps;
    }
    …
    …
    …
}

Propagování kontrolované výjimky

public void configure(final String confFileName) throws FileNotFoundException{
    File confFile = new File(confFileName)
    FileInputStream fis = new FileInputStream(confFile);
    Properties props = new Properties();
    …
    …
    …
}

Nekontrolované výjimky

Nekontrolované či běhové výjimky se od kontrolovaných odlišují v tom, že programátor není explicitně nucen k jejich ošetření či propagaci. Nekontrolované výjimky vznikají za běhu systému, a proto nemá cenu jejich výskyt predikovat a snažit se je ošetřit nebo je propagovat. Mezi tyto výjimky patří například práce mimo rozsah pole ArrayIndexOutOfBoundsException, práce na null referenci NullPointerException a podobně.

Objektová hierarchie výjimek

Kořenovou třídou pro všechny výjimky a chyby představuje Throwable. Standardní API Javy definuje dva potomky, Error a Exception. Třída Error a její potomci představují rodinu závažných chyb, jedná se o chyby nekontrolované (běhové) a jejich chytání nebo převod na kontrolované nemá smysl.

Do kategorie závažných chyb spadají například chyby vzniklé při běhu JVM (Java Virtual Machine), jako je nedostatek paměti, přetečení zásobníku nebo vnitřní chyba JVM. Třída Exception představuje základní třídu (předka) pro všechny výjimky. Od této třídy jsou přímo odvozeny všechny kontrolované výjimky. Nekontrolované výjimky jsou odvozeny od RuntimeException. Základní hierarchii znázorňuje následující model tříd:

Model výjimek v Javě

Takto nastavená hierarchie se využívá při tvorbě odvozených výjimek, které z těchto základních vycházejí. Výjimky mají ještě jednu základní vlastnost, umožňují řetězení, takzvaný exception chaining. Příklad odvozené kontrolované i nekontrolované výjimky najdete v přiložených zdrojových kódech.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Žádný příspěvek v diskuzi

Odpovědět