Výjimky v Javě
Z MiS
Obsah |
Co jsou výjimky?
- V každém programu mohou za určitých okolností nastávat chyby, které programátor může předvídat, ale kterým nelze obecně zabránit.
- Typicky takové chyby nastávají:
- při práci se soubory (chybějící soubor, poškozené médium, odpojené médium, uživatel špatně zadal název souboru,...)
- při práci se sítí (nelze se připojit, připojení bylo přerušeno v průběhu komunikace,...)
- při zpracovávání vstupu od uživatele (například očekáváme celé číslo, ale uživatel zadá do vstupního pole desetinné číslo)
- Pokud bychom museli psát podmínky a testovat chyby všude, kde může potenciálně nastat problém, kód aplikace by se stal brzy nepřehledným.
- Většina moderních programovacích jazyků proto zavádí koncept tzv. výjimek.
- Zjednodušeně to funguje tak, že označíme blok, kde může nastat chyba, a ošetření chyby (správná reakce na chybu) uvedeme až na konec tohoto bloku.
Ještě jednou pro ujasnění:
- Neřešíme teď situaci, kdy je aplikace špatně naprogramovaná!
- Aplikace je správně napsaná, ale problém nastane v něčem, co programátor nemůže dopředu ovlivnit.
- Například výpadek sítě při komunikaci přes síť. Takové chyby lze předvídat a můžeme do aplikace zabudovat vhodnou reakci na takovou chybu. Ale nelze zajistit, aby taková chyba nenastala.
Jak výjimky fungují?
- Pokud víme, že v nějaké části kódu aplikace může nastat chyba, kterou lze předvídat, ale nelze ji vyloučit, potom:
- Označíme blok kódu, kde chyba může nastat:
try {
...}
- Následně zapíšeme kód bez toho, abychom museli neustále testovat chyby
- Kód tedy může tedy zůstat přehledný a stručný, bez spousty podmínek.
- Pokud chyba nastane, zbytek bloku kódu se přeskočí a ihned se provede reakce na chybu (viz další krok).
- Poté popíšeme, jak zareagovat na jednotlivé typy chyb (jak zareagovat na výjimky):
catch (TypVýjimky názevVýjimky) {
... jak zareagovat ...}
- Počet typů chyb, na které reagujeme, není omezený.
- A nakonec můžeme (ale nemusíme) uvést kód, který se má provést bez ohledu na to, jestli nastala chyba:
finally {
... Co se má stát vždy na konci bloku ...}
Šablona kódu ošetření výjimek
try { // Začni provádět kód, ve kterém může nastat chybový stav... ... zde následuje kód, pracující se souborem... } catch (TypVýjimky1 ex) { // Co se má stát, pokud nastane chyba TypVýjimky1... } catch (TypVýjimky2 ex) { // Co se má stát, pokud nastane chyba TypVýjimky2... // Takto můžeme popsat reakci na různé typy výjimek. } finally { // Co se má stát každopádně po skončení bloku... // Provede se i tehdy, kdy nastane neočekávaná chyba. // Část finally není povinná, může se vynechat... }
Příklad — práce se souborem
- Předpokládejme, že máme soubor, kde jsou na každém řádku údaje o jednom pozemku.
- Na každém řádku vždy uchováváme:
- šířku pozemku (celé číslo)
- délku pozemku (celé číslo)
- popis typu pozemku (text)
- Položky jsou na řádku odděleny středníkem.
- Budeme postupně načítat jednotlivé řádky souboru a vypisovat je na obrazovku.
- Pokud nastane chyba, načítání se přeruší a aplikace vypíše vhodné chybové hlášení.
- Nakonec se vždy pokusíme soubor uzavřít. I v průběhu uzavírání souboru ale může dojít k chybě.
import java.util.Scanner; import java.io.File;
...
try { String nazev = "vstup.txt"; Scanner sc = new Scanner(new File(nazev), "windows-1250"); while (sc.hasNextLine()) { int sirka = sc.nextInt(); int delka = sc.nextInt(); String nazev = sc.next(); sc.nextLine(); System.out.println("Pozemek: "+nazev+" má šířku: "+sirka+" m a délku: "+delka+"\n"); System.out.println("Plocha pozemku je: "+(sirka*delka)+" m^2"); System.out.println("---------------"); } } catch (FileNotFoundException ex) { System.err.println("Nenalezen soubor: "+nazev+"!"; } } catch (IOException ex) { System.err.println("Chyba čtení ze souboru "+nazev+": "+ex.getLocalizedMessage()); } } catch (InputMismatchException ex) { System.err.println("Nesprávný formát dat v souboru "+nazev+": "+ex.getLocalizedMessage()); } } finally { try { sc.close(); } catch (IOException ex) { System.err.println("Došlo k chybě při zavírání souboru "+nazev+": "+ex.getLocalizedMessage()); } }
...
Delegování chyb throws
- Může se také stát, že máme metodu, ve které nastává chyba, ale my nevíme, jak ji ošetřit (jak na ni správně zareagovat).
- V takovém případě můžeme použít klíčové slovo
throws
, kterým zařídíme, že v případě chyby metoda skončí a výjimka se takzvaně „deleguje“ nadřazenému kódu.
- Například
- Píšeme knihovní funkci, která bude sloužit pro načítání XML souborů v různých aplikacích.
- Přitom ale nevíme, jaká aplikace to bude:
- některé aplikace mohou být textové,
- jiné mohou mít grafické uživatelské rozhraní,
- a některé aplikace mohou být třeba napsány v Greenfootu.
- Každá aplikace tedy bude chtít zareagovat na chybu jinak:
- v textovém režimu vypsáním hlášení pomocí
System.err.println()
, - v grafickém režimu pomocí vyskakovacího okna
- a v Greenfootu třeba tak, že nějakému aktérovi nastavíme text s popisem hlášení.
- v textovém režimu vypsáním hlášení pomocí
- Chybu tedy nechceme ošetřovat hned, protože ještě nevíme jak to nejlépe provést.
private void nactiSoubor() throws IOException { ... kód, kde může nastat chyba IOException, na kterou ale v tomto místě neumíme správně zareagovat... ... další kód, který se v případě chyby přeskočí... }
public void zpracuj() { try {
... nějaký kód...
nactiSoubor();
... nějaký kód - pokud v metodě nactiSoubor() nastane chyba, tento kód se přeskočí...
} catch (IOException ex) { ... jak reagovat na chybu... } }
Vyhození vlastní výjimky throw
- Můžeme také chtít sami za nějakých okolností vytvořit výjimku.
- To provedeme pomocí klíčového slova
throw
, za kterým následuje kód pro vytvoření instance některé výjimky.
if (castka > limit) { int rozdil = castka - limit; throw new MyException("Překročen limit o: "+rozdil+" Kč"); }
Související stránky