Výjimky v C# a Javě
Výjimky v C# Výjimky poskytují v C# způsob, jak reagovat na nečekané události. Uživatel např. zadá místo čísla text nebo soubor, který chceme otevřít, je využíván jiným procesem. To vše a mnohem víc zachycují výjimky. Výjimky můžeme zachytávat blokem try-catch nebo try-catch-finally. Do bloku try vložíme „podezřelý“ kód – kód, ve kterém by mohlo dojít k nečekané události. Ihned za blokem try následuje blok catch (může jich být více), ve kterém napíšeme kód reagující na výjimečný stav vyvolaný v bloku try. Za blokem try může následovat blok finally, který se vykoná v každém případě.
Výjimky tvoří objekty třídy Exception nebo jeho potomci. V bloku catch dostaneme instanci třídy Exception nebo jeho potomka jako parametr. Třída obsahuje podrobné informace o výjimce.
static void Main(string[] args) { try { //Tento blok je pod kontrolou int cislo1 = Int32.Parse(Console.ReadLine()); int cislo2 = Int32.Parse(Console.ReadLine()); int vysledek = cislo1 / cislo2; Console.WriteLine(vysledek.ToString()); } catch (FormatException e1) { //Tento blok se vykoná, pokud uživatel nezadá číslo Console.WriteLine("Nezadal jsi číslo."); } catch (DivideByZeroException e2) { //Tento blok se vykoná, když dojde k dělení nulou Console.WriteLine("Pokus o dělení nulou."); } catch (Exception e3) { //Tento blok se vykoná při jakékoliv jiné chybě Console.WriteLine("Jiná chyba: " + e3.Message); } finally { //Text se vypíše vždy Console.WriteLine("Tento text se vypíše vždy."); } Console.Read(); }
Tento kód přijme od uživatele dvě čísla, vydělí je a vypíše výsledek. Celý hlavní kód je v bloku try. Za ním následuje první blok catch s parametrem typu FormatException. První blok catch bude vykonán pouze tehdy, pokud jedno ze zadaných čísel nebude číslo. Druhý blok catch DivideByZeroException bude vykonán, jen když dojde k dělení nulou. Třetí blok catch má parametr typu Exception, což je předek všech dalších výjimek (včetně FormatException a DivideByZeroException). Ten se tedy vykoná při každém vyvolání výjimky, kromě chybného převodu a dělení nulou, protože ty již zachytily první dva bloky. Můžeme nadefinovat libovolné množství bloků catch. Ale pozor jako první nadefinujeme „specializované“ bloky, protože nikdy se nevolá více než jeden blok catch.
Všechny výjimky jsou odvozeny od třídy Exception. Můžeme si tedy nadefinovat vlastní výjimku: class MyException : Exception { private DateTime time; public MyException(string message) : base(message) { time = DateTime.Now; //aktuální datum a čas } public DateTime Time { get { return time; } } } Třída naší výjimky si navíc během volání konstruktoru uloží aktuální čas. Výjimky můžeme vyvolávat sami. Vyvoláme tedy naši výjimku: throw new MyException("Zpráva o chybě");
Nejdříve uvedeme klíčové slovo throw a pak založíme instanci příslušné výjimky.
public class Kruznice { private int r; public int R { get{return r;} set { if (value >0) {r =value;} else { throw new MyException ("Poloměr musí být kladné číslo"); } } } }
static void Main(string[] args) { try { Kruznice o = new Kruznice(); o.R = Int32.Parse(Console.ReadLine()); } catch (FormatException e1) { Console.WriteLine("Nezadal jsi číslo."); } catch (MyException e2) { Console.WriteLine(e2.Message); } catch (Exception e3) { Console.WriteLine("Jiná chyba: " + e3.Message); }
}
Výjimky v Javě Mechanismus výjimek (exception) je jedním ze silných bezpečnostních prvků Javy. Rozlišují se tři základní druhy výjimek – Error, RuntimeException a Exception. Všechny výjimky jsou potomky jedné třídy Throwable. Třída Error představuje závažné chyby, které se mohou vyskytnout v JVM (interpret Javy). Na tyto chyby v programu obvykle nereagujeme, protože je v programu nedokážeme opravit. Po jejich výskytu program končí. Výjimky třídy RuntimeException (a její potomci) zahrnují chyby, na které dokážeme úspěšně reagovat. Jsou to chyby, které se mohou vyskytnou v programu kdekoliv a testuje je samotný JVM. Jedná se např. o AritmeticException (sem patří dělení nulou) nebo NumberFormatException (chybný převod na číslo). Protože se mohou vyskytnout kdekoli, nenutí nás překladač na ně reagovat. Ovšem reagovat na ně můžeme, pokud to považujeme za užitečné. Třída Exception představuje tzv. kontrolované výjimky. O ty se v programu postarat musíme, jedním z dále uvedených způsobů. Výjimku neumíme nebo nechceme ošetřit, proto se informace o jejím výskytu předá do nadřazené úrovně Výjimka se zachytí a kompletně ošetří v metodě, kde se vyskytla Výjimka se částečně či kompletně ošetří a navíc se informace o jejím výskytu pošle do nadřazené úrovně.
Předání výjimky výše Jedná se o využití tzv. šíření výjimky. Je to případ, kdy se metoda, ve které se výjimka vyskytla, „zříká odpovědnosti“ za její zpracování. Toto předání odpovědnosti se deklaruje v hlavičce metody pomocí slova throws. Řešení problému se pouze odsouvá, ale bude muset být provedeno. public static void Kopie(String vstup, String vystup) throws IOException { FileInputStream in = new FileInputStream(vstup); FileOutputStream out = new FileOutputStream(vystup); int b = in.read(); while ( b!= -1 ) { out.write(b); b = in.read(); } out.close(); in.close(); } public static void main(String[] args) throws IOException { Kopie("vstup.txt", "vystup.txt"); }
Může se zdát, že toto řešení je nejjednodušší. public static void Kopie(String vstup,String vystup) throws IOException
Tato řádka říká, že si je vědoma toho, že se v ní může vyskytnout výjimka, ale stav nijak neřeší. Problém se pouze přesouvá, takže pokud i nadřazená úroveň (v tomto případě main) použije stejnou strategii, musí to i ona uvést ve své hlavičce. Tím se problém řešení výjimky přesune do další nadřazené úrovně, kterou je v tomto případě JVM a ta tuto výjimku ošetří tím, že ukončí program a vypíše chybové hlášení.
Kompletní ošetření výjimky Pokud nechceme předávat výjimku do nadřazené úrovně, je možné ji v metodě kompletně ošetřit. K tomu používáme jazykovou konstrukci try-catch. Do bloku try uzavřeme kód, ve kterém se může výjimka vyskytnout. Blok catch, který následuje bezprostředně za blokem try, říká na jakou výjimku bude reagovat a jak. public static void Kopie(String vstup, String vystup) { try{ FileInputStream in = new FileInputStream(vstup); FileOutputStream out = new FileOutputStream(vystup); int b = in.read(); while ( b!= -1 ) { out.write(b); b = in.read(); } out.close(); in.close(); }catch (IOException e){ System.out.println("Problém při práci se souborem."); } }
Ošetření výjimky a její následné předání výš public static void Kopie(String vstup, String vystup) throws IOException { try{ FileInputStream in = new FileInputStream(vstup); FileOutputStream out = new FileOutputStream(vystup); int b = in.read(); while ( b!= -1 ) { out.write(b); b = in.read(); } out.close(); in.close(); }catch (IOException e){ System.out.println("Problém při práci se souborem."); throw e; } } public static void main(String[] args) { try{ Kopie("vstup.txt", "vystup.txt"); }catch (IOException e){ System.out.println("Program neproběhl správně."); } }
Jedná se o spojení předchozích dvou metod. Novinkou je pouze příkaz throw e, kterým Kopie(…) znovu vyvolává výjimku, která se již vyskytla. Metoda main by na ni mohla reagovat tím, že ji předá výš, ale rozumnější řešení je, že ji odchytí a kompletně ošetří.
Seskupování výjimek V předchozím případě jsme řešili výjimku IOException, zkusíme k ní ještě přidat výjimku FileNotFoundException, která je potomkem třídy IOException. Máme tu tedy dvě výjimky a rádi bychom reagovali na obě. To je naštěstí bez problémů možné, protože v javě není počet bloků catch, které následují za blokem try, nijak omezen. public static void Kopie(String vstup, String vystup) { try{ FileInputStream in = new FileInputStream(vstup); FileOutputStream out = new FileOutputStream(vystup); int b = in.read(); while ( b!= -1 ) { out.write(b); b = in.read(); } out.close(); in.close(); }catch (FileNotFoundException e){ System.out.println("Soubor nebyl nalezen."); } }catch (IOException e){ System.out.println("Problém při práci se souborem."); } }
Konstrukce try-catch-finally Dvojici bloků try-catch je možno rozšířit o nepovinný blok finally. Konstrukce vypadá takto: try {
//hlidany blok
} catch(typ vyjimky) { //osetreni vyjimky } finally { //tento kod se provede vzdy }
Koncovým blokem finally pokračuje program, ať výjimka nastala či nikoliv. Koncový blok se vykoná i v případě, že je v try nebo catch uveden return nebo se vyvolá nezachycená výjimka.
Vytvoření vlastní výjimky public class MoneyExc extends Exception { public MoneyExc(String msg) { super(msg); } }
Naši třídu MoneyExc oddědíme od třídy Exception. Dále pak vytvoříme třídu Bankomat, ve které naši výjimku využijeme.
public class Bankomat { private int kredit; public Bankomat() { this.kredit=5000; } public void vyber (int kolik) throws MoneyExc { if (kolik>kredit) { throw new MoneyExc("Nemas penize"); } else { kredit-=kolik; System.out.println ("Uspesne vydano "+kolik); } }
public int zustatek () { return kredit; } public void vloz (int vklad) throws MoneyExc { if (vklad<=0) { throw new MoneyExc("Nemuzes tolik vlozit"); } else { kredit+=vklad; System.out.println ("Uspesne vlozeno "+vklad); } } }
public static void main(String[] args) { try{ Bankomat b = new Bankomant(); int vybirana_castka = 500; b.vyber(vybirana_castka); }catch (MoneyExc e){ System.out.println(e.getMessage()); } }