<< Java Collections Framework | Obsah | Statické metódy a premenné, konštanty >>
Už vieme, že pri volaní nejakej metódy môže nastať za istých okolností nejaká výnimka. Keď ju neodchytíme, program nám skončí a vypíše sa stack trace.
Tiež vieme, že výnimky, ktorým vieme zabrániť overovaním vhodných podmienok pred spustením kritickej operácie neriešime odchytávaním, ale práve overovaním podmienok vopred.
Výnimky, ktorým nevieme predchádzať overovaním vstupných podmienok môžeme odchytávať v try-catch-finally blokoch.
Výnimky sa používajú pri výnimočných stavoch, s ktorými si daná metóda nevie poradiť. Ak si metóda vie poradiť sama, nemá zmysel výnimku vyhadzovať. Metóda vyhadzuje výnimku s tým, že je šanca, že sa s tým kus kódu, ktorý danú metódu volal, vie vysporiadať. Dôvodom na vyhodenie výnimky je väčšinou neschopnosť vykonať požadovanú akciu a vrátiť požadovanú hodnotu. Príčinou môže byť:
Doteraz, sme pracovali iba s výnimkami, ktoré hádzali cudzie metódy. Výnimky môžeme vyhadzovať aj zo svojich metód. Môžeme vyhadzovať už známe výnimky (java.lang.NumberFormatException, java.lang.IllegalArgumentException, ...) alebo si môžeme vyrobiť aj vlastné výnimky. Výnimky sú obyčajné triedy, ktoré dedia od triedy java.lang.Exception. Môžu teda mať vlastné inštančné premenné, metódy a konštruktory. Dokážeme tak odoslať veľmi komplexnú informáciu o vzniknutej sitácii, a nezriedka aj návrh na riešenie. Vyrobme si vlastnú výnimku ZápornýVstupException.
Vyrobme si príklad, ktorý počíta faktoriál čísla n. Ak príde n záporné, vyhodíme výnimku ZápornýVstupException.
Všimnite si, že výnimka je obyčajný objekt, vytvorený cez new. Pomocou kľúčového slova throw okamžite ukončíme beh metódy a vyhodíme výnimku. Výnimky, ktoré môžeme vyhadzovať, uvádzame v hlavičke za kľúčovým slovom throws. Ak môžeme vyhadzovať viac druhov výnimiek, oddeľujeme ich čiarkami.
Keď teraz budeme volať túto metódu, Java od nás bude požadovať ošetrenie výnimky. Zabalíme teda toto volanie do try-catch blokov.
Aby mohli bez ujmy na zdraví používať vaše programy aj iní ľudia (resp. po dlhšom čase aj vy sami), nikdy nevyhadzujte všeobecné výnimky, napr:
Ak nastane všeobecná výnimka, používateľ vášho kódu nemusí pochopiť, a obvykle ani nepochopí, čo sa stalo a čo bolo príčinou. Na druhej strane, keď dostane výnimku s rozumným menom, napr. ZápornýVstupException, už z jej názvu vie veľmi rýchlo pochopiť, že problém zrejme spočíva v záporných vstupoch.
Ak výnimku spôsobilo volanie inej metódy, máme dve možnosti:
V druhom prípade neošetrenú výnimku uvedieme v throws a necháme vyššie metódy nech to riešia. Skôr či neskôr sa však toto prehadzovanie zodpovednosti za ošetrenie vzniknutého stavu musí niekde zastaviť -- v opačnom prípade výnimka vybuble von z metódy main(), čo vyústi v násilné ukončenie programu. Napríklad, keby sme pri čítaní súboru neodchytili FileNotFoundException mohli by sme ju proste nechať vybublať vyššie:
Obveľa zaujímavejšou možnosťou je prebaliť výnimku do novej, popisnejšej. Vytvoríme si novú výnimku PocitadloException, ktorej cez konštruktor vložíme textový popis a aj pôvodnú výnimku FileNotFoundException, ktorú prebaľujeme. Ten, čo odchytí výnimku má tak lepšiu informáciu o tom, čo sa stalo, pretože má vlastne popisnú výnimku aj pôvodnú výnimku v jednom. Konštruktory triedy PocitadloException si vygenerujeme cez Source > Generate Constructors From Superclass.
Prebaľovanie výnimiek má zmysel aj preto, že používateľ cudzieho kódu môže mať s nízkoúrovňovými výnimkami, vybublanými z vnútra programu, problém. Má pochopiť, čo sa stalo, a hlavne čo bolo príčinou a ako to treba riešiť. Predstavte si, že vám cudzí modul vyhodí napr. výnimku NumberFormatException a vy ste pritom volali metódu na poslanie matice na server a nevidíte súvis.
Príklad správneho prebaľovania výnimiek bublacúcich z vnútra nejakého projektu:
VSúboreChýbaPremennáException: početBalíčkov
KonfiguráciaNemáPremennúException: početBalíčkov
ModulKonfiguráciaOutOfDateException
StarrýJarSúborException: konfiguracia.jar
Keby sa k vám dostala prvá výnimka, neviete čo s tým. Ale na základe poslednej už viete, že potrebujete zohnať novší modul konfigurácie.
Už vieme, že v Jave sú dva druhy výnimiek - kontrolované a nekontrolované. Okrem nich môžu metódy vyhadzovať aj chyby.
try-catch blokoch alebo nechať vybublať do volajúcej metódy. Ak sa kontrolované výnimky vyhadzujú, musia sa uviesť v hlavičke za throws.
RuntimeException. Nemusia odchytiť a nemusia sa uvádzať v throws.
Error. Podobne ako nekontrolované výnimky sa nemusia odchytávať a uvádzať v throws.
Chyby (Errors) predstavujú taký stav systému, z ktorého sa aplikácia nemá šancu zotaviť. Väčšinou ju vyhadzujú nízkoúrovňové metódy z vnútra Javy (bežný programátor ich nevyhadzuje), a predstavujú fatálne chyby, z ktorých je nemožné sa zotaviť. Príkladmi sú OutOfMemoryError -- indikácia vyčerpanej pamäte alebo VirtualMachineError, keď virtuálny stroj nevie púšťať Java programy.
Otázka, kedy použiť kontrolovanú a kedy nekontrolovanú výnimku, nie je jednoduchá. Sú vývojári, ktorí nepoužívajú kontrolované výnimky vôbec (iné OOP jazyky kontrolované výnimky nemajú). Nekontrolované výnimky majú tú nevýhodu, že zvádzajú k lenivosti. Dobrý programátor, keď hádže nekontrolované výnimky, uvádza ich, napriek tomu, že to nie je nutné, aj do dokumentácie aj do throws.
Výhodou nekontrolovaných výnimiek je, že ak sme si istí, že sme dodržali kontrakt a nenastanú výnimky identifikujúce nedodržanie kontraktu, nemusíme ich odchytávať v try-catch blokoch. Ak sa naviac vyhadzované nekontrolované výnimky uvedú do dokumentácie a do throws, nenastane situácia, že nejaká neočakávaná (nezdokumentovaná) výnimka vám vybuble z vnútra cudzieho kódu. Takéto situácie sú veľmi nepríjemné a spôsobujú často mnohé hodiny tápania a hľadania príčiny. Uvedenie v throws je to najmenej, čo môžeme urobiť, aj keby sme dokumentáciu ešte nemali dotiahnutú. Je to aspoň informácia o možnom probléme. Naviac vďaka throws vám Eclipse vie sám generovať try-catch bloky.
Pri kombinácii výnimiek a dedičnosti si treba dávať pozor. Platí totiž, že prekrývajúce metódy môžu vyhadzovať iba také výnimky ako ich náprotivky v rodičovských triedach. Inými slovami, ak chceme v oddedenej triede v nejakej metóde vyhadzovať viac výnimiek ako vyhadzuje metóda ktorú prekrývame, musíme rozšíriť zoznam výnimiek v throws pôvodnej rodičovskej metódy.
Napríklad ak metóda dajUmiestnenie() triedy Film vyhadzuje výnimku FilmException:
tak v metóde dajUmiestnenie() triedy FilmNaDvd, ktorá dedí od triedy Film môžeme vyhadzovať iba FilmException, jej potomkov alebo nič. Napríklad nasledujúci kód by nefungoval:
Možné opravy:
Táto oprava je však pomerne nešťastná. Všeobecný Film by totiž nemal mať do činenia s výnimkami, ktoré zväzujú triedu Filmu s jej potomkom DVD.
I v tomto prípade je oprava netradičná. Hádzanie všeobecnej výnimky Exception sme totiž vyššie v texte vyhlásili za nevhodné.
V tomto konkrétnom príklade je ideálne, keď DVDException oddedí od FilmException, a metóda dajUmiestnenie() bude môcť vyhodiť DVDException bez toho, aby narušila podmienku v rodičovi.
Už vieme že odchytávanie výnimiek sa deje v catch blokoch. Tiež vieme, že catch blokov môže byť viac. Fungovanie viacerych blokov je založené na tom, že výnimky odchytí prvý catch blok, ktorý vie prijať vyhodenú výnimku. V každom catch bloku sa uvádza ako parameter nejaká premenná, ktorej typ určuje, či sa výnimka odchytí alebo nie.
Na to, aby sme vedeli, ktoré výnimky budú odchytené ktorým catch blokom, potrebujeme poznať hierarchiu dedenia vyhadzovateľných (Throwable) objektov.

Už vieme, že z premennej nadradenej triedy vieme referencovať ľubovoľný objekt potomka tejto triedy. Tento vzťah platí aj vo svete výnimiek. To znamená, že ak v prvom catch bloku odchytávame výnimku do premennej typu Exception tak odchytíme ľubovoľnú výnimku. Jediné čo by sa neodchytilo by boli chyby. Z toho vyplýva, že keby sme v ďalších catch blokoch odchytávali napríklad výnimku FileNotFoundException, ktorá je potomkom triedy Exception tak, by sme ju týmto catch blokom nikdy neodchytili, pretože by bolá odchytená prvým catch blokom s typom Exception. catch bloky teda uvádzame vždy od najkonkrétnejších po najvšeobecnejšie.
Príklad správneho poradia odchytávania:
Exception.
try-catch bloky, throws je rýchlejšie napísané"