Výjimky v Javě
Z MiS
				
				
				(Rozdíly mezi verzemi)
				
																
				
				
								
				|  (Vytvořen obsah stránky.) |  (Přidán příklad.) | ||
| Řádka 29: | Řádka 29: | ||
| #* Pokud chyba nastane, zbytek bloku kódu se přeskočí a ihned se provede reakce na chybu (viz další krok). | #* 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):<br /> <code>catch (''TypVýjimky'' ''názevVýjimky'') { </code> ... jak zareagovat ... <code> } </code> | # Poté popíšeme, jak zareagovat na jednotlivé typy chyb (jak zareagovat na výjimky):<br /> <code>catch (''TypVýjimky'' ''názevVýjimky'') { </code> ... jak zareagovat ... <code> } </code> | ||
| − | # A nakonec můžeme uvést kód, který se má provést  | + | #* 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:<br /> <code> finally { </code> ... Co se má stát vždy na konci bloku ... <code> } </code> | ||
| − | == Šablona kódu == | + | == Šablona kódu ošetření výjimek == | 
|   '''try''' { ''// Začni provádět kód, ve kterém může nastat chybový stav...'' |   '''try''' { ''// Začni provádět kód, ve kterém může nastat chybový stav...'' | ||
| Řádka 45: | Řádka 46: | ||
|       ''// Co se má stát, pokud nastane chyba TypVýjimky2...'' |       ''// Co se má stát, pokud nastane chyba TypVýjimky2...'' | ||
| + | |||
| + |      ''// Takto můžeme popsat reakci na různé typy výjimek. | ||
|   } '''finally''' { |   } '''finally''' { | ||
| Řádka 50: | Řádka 53: | ||
|       ''// Co se má stát každopádně po skončení bloku...'' |       ''// Co se má stát každopádně po skončení bloku...'' | ||
|       ''// Provede se i tehdy, kdy nastane neočekávaná chyba. |       ''// Provede se i tehdy, kdy nastane neočekávaná chyba. | ||
| + | |||
| + |      ''// Část finally není povinná, může se vynechat... | ||
|   } |   } | ||
| Řádka 56: | Řádka 61: | ||
| == Příklad — práce se souborem == | == 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   <code>throws</code> == | == Delegování chyb   <code>throws</code> == | ||
| + | * 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 <code>throws</code>, kterým zařídíme, že v případě chyby metoda skončí a výjimka se takzvaně „deleguje“ nadřazenému kódu. | ||
| + | |||
| + | <div class="Priklad"> | ||
| + | ; 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í <code>System.err.println()</code>,  | ||
| + | ** 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í. | ||
| + | * Chybu tedy nechceme ošetřovat hned, protože ještě nevíme jak to nejlépe provést. | ||
| + | </div> | ||
| + | |||
| + |  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   <code>throw</code> == | ||
| + | * Můžeme také chtít sami za nějakých okolností vytvořit výjimku. | ||
| + | * To provedeme pomocí klíčového slova <code>'''throw'''</code>, 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č"); | ||
| + |  } | ||
Verze z 14. 4. 2020, 13:25
| 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
