K čemu jsou výjimky (1)
-
Výjimky jsou mechanizmem umožňujícím reagovat na nestandardní (tj. chybové) běhové chování programu, které může mít různé příčiny:
- chyba okolí
-
uživatele, systému
- vnitřní chyba programu
-
tedy programátora.
K čemu jsou výjimky (2)
-
Mechanizmus, jak psát robustní, spolehlivé programy odolné proti chybám "okolí" i chybám v samotném programu.
-
Výjimky v Javě fungují podobně jako v C++, C#, Ruby, Pythonu a dalších zejména objektových jazycích.
-
v Javě jsou ještě lépe implementovány než v C++ (navíc klauzule
finally
) -
Podobně jako jinde, ani v Javě není dobré výjimkami potlačovat očekávané chyby programu samotného — to by bylo hrubé zneužití.
Co technicky jsou výjimky
- Výjimka (Exception)
-
objekt třídy
java.lang.Exception
.-
Příbuzným typem jsou rovněž vážné běhové chyby — ty jsou objekty třídy
java.lang.Error
. -
Každopádně v obou případech rozšiřuje třídu
java.lang.Throwable
. -
Ta je také často používaná jako deklarativní typ pro výjimky v širším slova smyslu.
-
Kdy výjimka vznikne
Objekt výjimky je vyhozen (throw
) buďto:
-
automaticky běhovým systémem Javy, nastane-li nějaká běhová chyba — např. dělení nulou, nebo
-
jsou vytvořeny a vyhozeny samotným programem, zdetekuje-li nějaký chybový stav, na nějž je třeba reagovat — např. do metody je předán špatný argument.
Kdy výjimka vznikne — příklad
try { // First, the exception can be raised in JVM when // dereferencing using a bad index in an array: String name = args[i]; // ArrayIndexOutOfBoundsException // or the exception(s) can be raised by programmer // in the constructor of Person // NullPointerException or IllegalArgumentException p = new Person(name); } catch(ArrayIndexOutOfBoundsException e) { System.err.println("No valid person name specified"); System.exit(1); } catch(NullPointerException e) { // reaction upon NullPointerException occurence } catch(IllegalArgumentException e) { // reaction upon IllegalArgumentException occurence }
Programové bloky pro práci s výjimkami
-
Bloky programu pro práci s výjimkami:
-
try
-
blok vymezující místo, kde může výjimka vzniknout
-
catch
-
blok, který se vykoná, nastane-li výjimka odpovídající typu v
catch
-
finally
-
blok, který se vykoná vždy, viz dále.
-
Co se s vyhozenou výjimkou stane
Vyhozený objekt výjimky je buďto:
-
Zachycen v rámci metody, kde výjimka vznikla → a to v bloku
catch
.-
Takto to fungovalo ve výše uvedeném příkladu — všechny výjimky byly zachyceny a řešeny svými bloky
catch
.
-
-
Nebo výjimka propadne do nadřazené (volající) metody, kde je buďto v bloku
catch
zachycena nebo opět propadne atd.-
Výjimka tedy "putuje programem" tak dlouho, než je zachycena.
-
Pokud není nikde zachycena, běh JVM skončí s hlášením o výjimce.
-
Syntaxe kódu s ošetřením výjimek
try { //zde může vzniknout výjimka } catch (ExceptionType exceptionVariable) { // zde je výjimka ošetřena // je možné zde přistupovat k "exceptionVariable" }
-
Bloku
try
se říká hlídaný blok, -
protože výjimky příslušného typu uvedeného v hlavičce/-ách bloků
catch
zde vzniklé jsou zachyceny.
Reakce na výjimku — možnosti
-
Jak můžeme na vyhozenou výjimku reagovat?
- Napravit příčiny vzniku chybového stavu
-
-
např. znovu nechat načíst vstup nebo
-
poskytnout za chybný vstup náhradu — např. implicitní hodnotu;
-
- Operaci neprovést
-
a sdělit chybu výše tím, že
-
výjimku propustíme z metody (propadne z ní).
-
Možnost úniku výjimky (propuštění) z metody se deklaruje pomocí
throws
v hlavičce metody.
-
Reakce na výjimku — pravidla
-
Vždy nějak reagujme, tzn. výjimku neignorujme, nepotlačujme.
-
Tudíž blok
catch
nenechávejme prázdný, -
přinejmenším vypišme
e.printStackTrace()
. -
Nelze-li rozumně reagovat na místě, propusťme výjimku výše a popišme to v dokumentaci, viz následující příklad.
Příklad komplexní reakce na výjimku
int i = 0; String param; int cislo = 0; boolean ok = false; do { try { // zde může vzniknout výjimka ArrayIndexOutOfBoundsException param = args[i]; // zde může vzniknout výjimka NumberFormatException cislo = Integer.parseInt(param); // sem se dostane, jen když nevznikla žádná výjimka ok = true; } catch (NumberFormatException nfe) { // sem se dostane, byl-li vstup ve špatném formátu (ne číslo) System.err.println("Parametr "+i+" neni platne cele cislo"); i++; // zkusíme další parametr } catch (ArrayIndexOutOfBoundsException iob) { // sem se dostane, byl-li překročena velikost pole -> chybový výstup err System.err.println("Nepredan zadny ciselny parametr"); // sami vyhodíme výjimku throw new IllegalArgumentException( "Nezadan zadny celociselny parametr."); } } while (!ok); System.out.println("Zadano cislo="+cislo);
Kaskády bloků catch
-
V některých blocích
try
mohou vzniknout výjimky více typů → -
pak můžeme uvádět více
catch
po sobě, viz přechozí příklad. -
Pokud
catch
takto řetězíme, musíme respektovat, že výjimka bude zachycena nejbližším příhodnýmcatch
a -
překladač si ohlídá, že kód neobsahuje nedosažitelné catch-bloky.
-
Pozor tedy na řetězení
catch
s výjimkami typů z jedné hierarchie tříd. -
Vždy musí být výjimka z podtřídy (tj. speciálnější) uvedena a tedy zachycována dříve než výjimka obecnější.
Příklad kaskády catch
špatně (1)
-
Definice vlastních výjimek — jedna podtřídou druhé
class MyException1 extends Exception {} class MyException2 extends MyException1 {}
Příklad kaskády catch
špatně (2)
-
Jejich chybné použití v kaskádě
try { if (args.length == 0 || args[0].equals("1")) throw new MyException1(); else if (args[0].equals("2")) throw new MyException2(); // CHYBA: v kaskádě je nejprve zachycována obecnější výjimka MyException1 } catch (MyException1 mv) { System.out.println("Zachycena vyjimka typu MyException1: " + mv); // speciálnější výjimka MyException2 by nikdy nebyla zachycena, // proto překladač takovou konstrukce ani nepřeloží. } catch (MyException2 mv) { System.out.println("Zachycena vyjimka typu MyException2: "+mv); }
Výjimky a jejich hlídání překladačem
-
Java je staticky (překladově) typovaný jazyk a jako takový zná místa potenciálního vyhození výjimky.
-
V dosud uváděných příkladech se neprojevovalo, že by nás nějak hlídal.
-
Bylo to proto, že jsme dosud používali tzv. běhové (nehlídané) výjimky (runtime-/unchecked exceptions), jejichž místa vzniku překladač nesleduje a nehlídá, jak na ně reagujeme.
-
Nyní nastíníme úplnou kategorizaci výjimek vč. hlídaných.
Kategorizace výjimek a dalších chybových objektů
- hlídané výjimky
-
checked exceptions
-
potomky/instancemi třídy
java.lang.Exception
-
překladač sleduje místa jejich vzniku a povinnou reakci na ně
-
nebo jejich možné propuštění z metody ven v případě deklarace
throws
-
- běhové výjimky
-
také nehlídané výjimky, unchecked exceptions
-
jsou typu nebo potomky
java.lang.RuntimeException
-
a nemusejí být zachytávány.
-
- vážné chyby JVM
-
signalizují těžce napravitelné chyby v JVM potomky
-
instance
java.lang.Error
-
např. Out Of Memory, Stack Overflow …,
-
ale též např. chybu programátora:
AssertionError
.
-
Kdy použít hlídanou a nehlídanou výjimku
- Unchecked exceptions
-
represent defects in the program (bugs) — often invalid arguments passed to a non-private method.
Unchecked runtime exceptions represent conditions that, generally speaking, reflect errors in your program’s logic and cannot be reasonably recovered from at run time.
- Checked exceptions
-
represent invalid conditions in areas outside the immediate control of the program (invalid user input, database problems, network outages, absent files).
Metody propouštějící výjimku
-
Ne všechny hlídané výjimky se musejí v metodě vzniku zachytit pomocí
catch
. -
Některé mohou být z metody propuštěny (mohou "propadnout výše").
-
Indikováno v hlavičce takové metody pomocí
throws
:
modifikátory návratovýTyp názevMetody(argumenty) throws TypPropouštěnéVýjimky { ... tělo metody, kde může výjimka vzniknout ... }
-
Pokud daná hlídaná výjimka nikde v těle nemůže vzniknout, překladač to zdetekuje a vypíše:
Exception XXX is never thrown in YYY
Příklad s propouštěnou výjimkou
private static void openFile(String filename) throws IOException { System.err.println("Zkouším otevřít soubor "+filename); FileReader r = new FileReader(filename); // povedlo se! // děláme s ním něco dále } public static void main(String[] args) { try { openFile(args[0]); System.err.println("Soubor otevřen"); } catch (IOException ioe) { System.err.println("Nelze otevřít soubor"); } }
Vlastní typy výjimek
-
Typy (=třídy) výjimek si můžeme definovat sami.
-
Bývá zvykem končit názvy tříd výjimek na
Exception
(např.OverloadedException
).
Klauzule finally
-
Klauzule (blok)
finally
může následovat ihned po blokutry
nebo až po blocíchcatch
. -
Slouží k "úklidu v každém případě", tj.
-
když je výjimka zachycena blokem
catch
, -
i když je výjimka propuštěna do volající metody.
-
-
Používá se typicky pro uvolnění (systémových) zdrojů — uzavření souborů, soketů.
Příklad finally
(1)
public class NotEnoughParametersException extends Exception { private int countParam; public NotEnoughParametersException(int countParam) { this.countParam = countParam; } // ... }
Note
|
Všimněte si, že u výjimek stejně jako u jiných tříd můžeme mít atributy, konstruktory, atd. |
Příklad finally
(2)
try { if (countParam < 2) throw new NotEnoughParametersException(countParam); // sem se půjde, nenastane-li výjimka System.out.println("Spravný počet parametrů: "+countParam); } catch (NotEnoughParametersException mp) { // sem se půjde, nastane-li výše výjimka System.out.println("Malo parametru: " +mp.getCountParam()); } finally { // sem se půjde VŽDY System.out.println("Konec"); }
Odkazy
- Oracle Java Tutorials