Ošetření chyb pomocí výjimek
Výjimka je událost, která nastane za běhu programu a která naruší normální tok instrukcí programu
1
Obsah 1. 2. 3. 4.
Úvod Přehled ošetření výjimek Ošetření dělení nulou Ošetření aritmetické výjimky a výjimky InputMismatchException 5. Kdy používat ošetření chyb pomocí výjimek 6. Hierarchie výjimek v Javě 7. Blok finally 8. Zásobník výjimky 9. Metody printStackTrace, getStackTrace a getMessage 10. Zřetězené výjimky 11. Preconditions a postconditions 12. Assertations 2
Úvod •
•
•
Výjimečná událost – výjimka je problém, který brání dalšímu vykonávání metody nebo pasáže, v níž se právě program nachází. V případě výjimečných událostí nemůže program pokračovat, protože v aktuálním kontextu nemá informace, které by mu umožňovaly problém vyřešit. V programu se tedy musíme přesunout o jednu úroveň výše a k tomu dojde automaticky po vyvolání výjimky. 3
Zásobník volání metod
Metoda, kde nastala chyba volání metody Metoda, bez ovladače ošetření výjimky volání metody Metoda, s ovladačem ošetření výjimky volání metody main
zásobník volání (The call stack) 4
Prohledávání zásobníku volání metod na ovladač výjimky
vyhození výjimky
odeslání výjimky
Metoda, kde vznikla chyba
hledání vhodného ovladače
Metoda bez ovladače ošetření výjimky hledání vhodného ovladače
odchycení výjimky
Metoda s ovladačem ošetření výjimky
main
5
Úvod • Dělení nulou (jmenovatel = 0) – jste-li na situaci připraveni, vyřešíte ji v aktuálním oboru. • Jedná-li se o neočekávanou hodnotu, program si s ní neporadí, a proto místo pokračování musí vyvolat výjimku. • Vyvolá-li se výjimka: – je vytvořen objekt výjimky (pomocí klíčového slova new) – běh programu se přeruší (v té části, kde nelze pokračovat bez vyřešení problému) – odkaz na objekt výjimky je předán do aktuálního bloku
6
Úvod – nyní přebírá řízení mechanismus ošetření výjimek a hledá v rámci aplikace vhodné místo, kde by mohl program pokračovat – tím místem je ovladač – handler, jehož úkolem je opravit systém po vzniklé chybě, aby vykonávání programu mohlo pokračovat dál – jednoduchý příklad if( t == null ) throw new NullPointerException(); – ověření platnosti odkazu – if; vytvoříte objekt znázorňující Vaší informaci NullPointerException a „vymrštíte“ jej z aktuálního bloku programu. – této operaci se říká – vyvolání výjimky 7
Úvod • Uvedený kód vyvolá výjimku a řešení pokračuje jinde • ošetření chyb pomocí výjimek umožňuje oddělit od sebe hlavní tok zpracování aplikace a ošetření neočekávaných stavů • objekty výjimek jsou stejné jako ostatní objekty jazyka vytvářeny na haldě pomocí klíčového slova new • Všechny standardní výjimky mají definovány dva konstruktory – implicitní (bez argumentů) a druhý s argumentem String – může sloužit pro popis výjimky 8
Úvod throw new NullPointerException(“t == null”);
• objekt je metodou throw „vrácen“ – jeho typ neodpovídá původnímu návratovému typu • mechanismus výjimek lze zjednodušeně považovat za jistý druh návratové hodnoty • Jako výjimku lze použít libovolný objekt třídy Throwable • Zpravidla se pro každý typ chyby vyvolává vlastní typ třída výjimek.
9
Úvod • Informace o výjimce je zastoupena jednak uvnitř objektu výjimky, jednak je implicitně dána typem vybraného objektu výjimky. • Tyto skutečnosti umožňují kódu v širším kontextu zjistit, co by se s touto situací dalo dělat. – obvykle je jedinou informací právě typ objektu výjimky – objekt výjimka sám většinou žádnou smysluplnou informaci neobsahuje
10
Zachycení výjimky • vyvolá-li metoda výjimku předpokládá se, že ji někdo „zachytí“ a vypořádá se s ní • hlídaná oblast – je úsek programového kódu, který může vyvolat výjimky a za nímž následuje speciální kód, jenž se s výjimkou vypořádá • blok try{ … } je blok, který umožní, aby byla výjimka zachycena a metoda tak nebyla ukončena • blok try zabezpečí, abychom se nemuseli při každém volání metody obklopovat testovacím kódem • kód programu je potom snazší (oddělení kódu) 11
Ovladače - handlery • Vyvolané výjimky jsou zpracovány ovladači. Ovladače můžeme použít pro všechny typy výjimek, které chcete ve svém programu zachytit. • Ovladače následují bezprostředně za klíčovým slovem try a jsou označeny klíčovým slovem catch
12
Osnova try { // kod, kod, ktery muze vyvolat vyjimky } catch(Typ1 id1) { // obslouzeni vyjimek typu Typ1 } catch(Typ2 id2) { // obslouzeni vyjimek typu Typ2 } catch(Typ3 id3) { // obslouzeni vyjimek typu Typ3 } // atd ...
13
Ovladače - handlery • každý ovladač je jako malá metoda, která přijímá pouze jeden jediný argument určitého typu • identifikátory id1, id2 ... lze použít uvnitř ovladače – pracuje se s nimi stejně jako s běžnými argumenty metod – mnohdy tyto argumenty nejsou uvnitř metody třeba, protože typ výjimky poskytuje dostatek informací • ovladače musí být umístěny bezprostředně za blokem try
14
Ovladače - handlery • při vzniku výjimky začne mechanismus obsluhy výjimek prohledávat ovladače a nalezne ovladač se stejným typem argumentu, jako vyvolaná výjimka • po nalezení příslušného ovladače přesune běh programu do tohoto ovladače a výjimka se považuje za obslouženou. • při prohledávání se vykoná pouze příslušný ovladač s klauzulí catch. Na rozdíl od příkazu switch se na konci nemusí přidávat break. • uvnitř bloku try může výjimku vyvolat celá řada příkazů, k obsloužení stačí jeden ovladač 15
// An application that attempts to divide by zero. zero. import java. java.util.Scanner; util.Scanner; public class DivideByZeroNoExceptionHandling { // demonstrates throwing an exception when a dividedivide-byby-zero occurs public static int quotient( quotient( int numerator, numerator, int denominator ) { return numerator / denominator; denominator; // possible division by zero } // end method quotient
Osnova Dělení nulou, bez ošetření výjimky
public static void main main( ( String args[] args[] ) { Scanner scanner = new Scanner( System.in System.in ); // scanner for input System. numerator: : " ); System.out. out.print( print( "Please "Please enter an integer numerator int numerator = scanner.nextInt scanner.nextInt(); nextInt(); System. System.out. out.print( print( "Please "Please enter an integer denominator: denominator: " ); int denominator = scanner.nextInt scanner.nextInt(); nextInt(); int result = quotient( quotient( numerator, numerator, denominator ); System. System.out. out.printf( printf( "\nResult: nResult: %d / %d = %d\ %d\n", numerator, numerator, denominator, denominator, result ); } // end main } // end class DivideByZeroNoExceptionHandling
16
Please enter an integer numerator: 100 Please enter an integer denominator: 7 Result: 100 / 7 = 14
Osnova Dělění nulou, ošetření bez výjimky
Please enter an integer numerator: 100 Please enter an integer denominator: 0 Exception in thread "main" java.lang.ArithmeticException: java.lang.ArithmeticException: / by zero at DivideByZeroNoExceptionHandling.quotient(DivideByZeroNoExceptionHandl Handl DivideByZeroNoExceptionHandling.quotient(DivideByZeroNoException ing.java:10) at DivideByZeroNoExceptionHandling.main(DivideByZeroNoExceptionHandling. ling. DivideByZeroNoExceptionHandling.main(DivideByZeroNoExceptionHand java:22) Please enter an integer numerator: 100 Please enter an integer denominator: ahoj Exception in thread "main" java.util.InputMismatchException at java.util.Scanner.throwFor(Unknown Source) at java.util.Scanner.next(Unknown Source) at java.util.Scanner.nextInt(Unknown Source) at java.util.Scanner.nextInt(Unknown Source) at DivideByZeroNoExceptionHandling.main(DivideByZeroNoExceptionHandling. ling. DivideByZeroNoExceptionHandling.main(DivideByZeroNoExceptionHand java:20)
17
// An exceptionexception-handling example that checks for dividedivide-byby-zero. zero. import java. java.util. util.InputMismatchException; InputMismatchException; import java. java.util.Scanner; util.Scanner; public class DivideByZeroWithExceptionHandling { // demonstrates throwing an exception when a dividedivide-byby-zero occurs public static int quotient( quotient( int numerator, numerator, int denominator ) throws ArithmeticException { return numerator / denominator; denominator; // possible division by zero } // end method quotient
Osnova Dělení nulou s ošetřením ArithmeticException a InputMismatchException
public static void main( main( String args[] args[] ) { Scanner scanner = new Scanner( System.in System.in ); // scanner for input boolean continueLoop = true; // determines if more input is needed do { try // read two numbers and calculate quotient { System. System.out. out.print( print( "Please "Please enter an integer numerator: numerator: " ); int numerator = scanner.nextInt scanner.nextInt(); nextInt(); System. System.out. out.print( print( "Please "Please enter an integer denominator: denominator: " ); int denominator = scanner.nextInt scanner.nextInt(); nextInt(); int result = quotient( quotient( numerator, numerator, denominator ); System. System.out. out.printf( printf( "\ "\nResult: nResult: %d / %d = %d\ %d\n", numerator, numerator, denominator, denominator, result ); continueLoop = false; false; // input successful; successful; end looping } // end try
18
catch ( InputMismatchException inputMismatchException ) { System. System.err. err.printf( printf( "\ "\nException: nException: %s\ %s\n", inputMismatchException ); scanner.nextLine scanner.nextLine(); nextLine(); // discard input so user can try again System. System.out. out.println( println( "You must enter integers. integers. Please try again. again.\n" ); } // end catch catch ( ArithmeticException arithmeticException ) { System. System.err. err.printf( printf( "\ "\nException: nException: %s\ %s\n", arithmeticException ); System. System.out. out.println( println( "Zero is an invalid denominator. denominator. Please try again. again.\n" ); } // end catch } while ( continueLoop ); // end do...while } // end main } // end class DivideByZeroWithExceptionHandling
Osnova Dělení nulou s ošetřením ArithmeticException a InputMismatchException
19
Please enter an integer numerator: 100 Please enter an integer denominator: 7
Osnova
Result: 100 / 7 = 14
Please enter an integer numerator: 100 Please enter an integer denominator: 0 Exception: java.lang.ArithmeticException java.lang.ArithmeticException: : / by zero Zero is an invalid denominator. Please try again. Please enter an integer numerator: 100 Please enter an integer denominator: 7 Result: 100 / 7 = 14
Please enter an integer numerator: numerator: 100 Please enter an integer denominator denominator: : ahoj Exception: java. java.util. util.InputMismatchException You must enter integers. integers. Please try again. again. Please enter an integer numerator numerator: : 100 Please enter an integer denominator: denominator: 7 Result: Result: 100 / 7 = 14
20
Ukončení versus obnovení • model ukončení – vzniklá chyba je tak závažná, že se nedá pokračovat ve vykonávání programu • model obnovení – úkolem ovladače je vykonat nějakou záchranou akci a vrátit řízení programu zpět do chybné metody. Předpoklad, že metoda bude při druhém průchodu úspěšná. • ošetření typu while a uvnitř test správnosti může vést k nekonečným cyklům
21
Jednoduchá výjimka • Jednoduchá výjimka – podtřída třídy Exception
22
package vyjimky; vyjimky; public class JednoduchaVyjimka extends Exception { }
Osnova Jednoduchá výjimka – podtřída třídy Exception
package vyjimky; vyjimky; public class Vyjimka { public void f() throws JednoduchaVyjimka { System. System.out. out.println( println( "Vyvolani vyjimky JednoduchaVyjimka z metody f()"); throw new JednoduchaVyjimka(); } // uvnitr metody f() vyvolaná vyvolaná výjimka public static void main( main(String args[]) args[]) { Vyjimka sed = new Vyjimka(); Vyjimka(); try { sed.f(); System. System.out. out.println( println(“Pokracujeme dale” dale”); } catch(JednoduchaVyjimka catch(JednoduchaVyjimka e) { System. System.err. err.println(" println("Vyjimka ("Vyjimka zachycena!"); } } }
23
D:\ D:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Priklady>java Priklady>java vyjimky.Vyjimka Vyvolani vyjimky JednoduchaVyjimka z metody f() Vyjimka zachycena! zachycena!
Osnova
24
// Vytvoreni vyjimky s konstruktorem package vyjimky; vyjimky; public class JednoduchaVyjimka1 extends Exception { public JednoduchaVyjimka1() { } public JednoduchaVyjimka1(String JednoduchaVyjimka1(String zprava){ super (zprava); } }
Osnova Výjimka s konstruktorem s řetězcem
25
package vyjimky; vyjimky;
Osnova
public class Vyjimka1 { public static void f() throws JednoduchaVyjimka1 { System. System.out. out.println( println( "Vyvolani vyjimky JednoduchaVyjimka1 z metody f()"); throw new JednoduchaVyjimka1(); } public static void g() throws JednoduchaVyjimka1 { System. System.out. out.println( println( "Vyvolani vyjimky JednoduchaVyjimka1 z metody g()"); throw new JednoduchaVyjimka1("Puvod JednoduchaVyjimka1("Puvod vyjimky v metode g()"); } public static void main( main(String args[]){ args[]){ try{ try{ f(); } catch(JednoduchaVyjimka1 catch(JednoduchaVyjimka1 v){ v.printStackTrace v.printStackTrace( printStackTrace(System. System.err); err); } try{ try{ g(); } catch(JednoduchaVyjimka1 v){ v.printStackTrace v.printStackTrace( printStackTrace(System. System.err); err); } } }
26
D:\ D:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Priklady>java Priklady>java vyjimky.Vyjimka1 Vyvolani vyjimky JednoduchaVyjimka1 z metody f() vyjimky.JednoduchaVyjimka1 at vyjimky.Vyjimka1.f(Vyjimka1.java:7) at vyjimky.Vyjimka1.main(Vyjimka1.java:17) Vyvolani vyjimky JednoduchaVyjimka1 z metody g() vyjimky.JednoduchaVyjimka1: Puvod vyjimky v metode g() at vyjimky.Vyjimka1.g(Vyjimka1.java:12) at vyjimky.Vyjimka1.main(Vyjimka1.java:23)
Osnova
D:\ D:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Priklady> Priklady>
27
package vyjimky; vyjimky;
Osnova
public class JednoduchaVyjimka2 extends Exception { private int i; public JednoduchaVyjimka2() {} public JednoduchaVyjimka2(String JednoduchaVyjimka2(String zprava){ super(zprava); } public JednoduchaVyjimka2(String JednoduchaVyjimka2(String zprava, int c){ super(zprava); i = c; } public int getVal(){ getVal(){ return i; } }
28
package vyjimky; vyjimky;
Osnova
public class Vyjimka2 { public static void f() throws JednoduchaVyjimka2 { System. System.out. out.println( println( "Vyvolani vyjimky JednoduchaVyjimka2 z metody f()"); throw new JednoduchaVyjimka2(); } public static void g() throws JednoduchaVyjimka2 { System. System.out. out.println( println( "Vyvolani vyjimky JednoduchaVyjimka2 z metody g()"); throw new JednoduchaVyjimka2("Puvod JednoduchaVyjimka2("Puvod vyjimky je v metode g()"); } public static void h() throws JednoduchaVyjimka2 { System. System.out. out.println( println( "Vyvolani vyjimky JednoduchaVyjimka2 z metody h()"); throw new JednoduchaVyjimka2( "Puvod vyjimky je v metode h()", 47); } public static void main( main(String args[]){ args[]){ try { f(); } catch(JednoduchaVyjimka2 catch(JednoduchaVyjimka2 k){ k.printStackTrace k.printStackTrace( printStackTrace(System. System.err); err); } try{ try{ g(); }
29
catch(JednoduchaVyjimka2 catch(JednoduchaVyjimka2 k){ k.printStackTrace k.printStackTrace( printStackTrace(System. System.err); err); } try{ try{ h(); } catch(JednoduchaVyjimka2 catch(JednoduchaVyjimka2 k){ k.printStackTrace k.printStackTrace( printStackTrace(System. System.err); err); System. System.err. err.println("k. println("k.getVal ("k.getVal() getVal() = "+k.getVal "+k.getVal()); getVal()); }
Osnova
} }
30
D:\ D:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Priklady>java Priklady>java vyjimky.Vyjimka2 Vyvolani vyjimky JednoduchaVyjimka2 z metody f() vyjimky.JednoduchaVyjimka2 at vyjimky.Vyjimka2.f(Vyjimka2.java:7) at vyjimky.Vyjimka2.main(Vyjimka2.java:22) Vyvolani vyjimky JednoduchaVyjimka2 z metody g() vyjimky.JednoduchaVyjimka2: Puvod vyjimky je v metode g() at vyjimky.Vyjimka2.g(Vyjimka2.java:12) at vyjimky.Vyjimka2.main(Vyjimka2.java:28) Vyvolani vyjimky JednoduchaVyjimka2 z metody h() vyjimky.JednoduchaVyjimka2: Puvod vyjimky je v metode h() at vyjimky.Vyjimka2.h(Vyjimka2.java:17) at vyjimky.Vyjimka2.main(Vyjimka2.java:34) k.getVal() k.getVal() = 47
Osnova
D:\ D:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Priklady> Priklady>
31
Javovská hierarchie výjimek Throwable
Exception
RuntimeException
IOException
Error
AWTError
ArrayIndexOutOfBoundException
ThreadDeath
OutOfMemoryError
InputMismatchException
ArithmeticException
ClassCastException NullPointerException
32
Standardní výjimky jazyka Java • Třída Throwable popisuje všechno, co může být jako výjimka vyvoláno • Existují dva typy objektů třídy Throwable: – Objekty třídy Error – systémové a překladové chyby (jejich zachycení nesledujeme) – Třída Exception – je základní (bázový) typ, který lze vyvolat v libovolné metodě třídy, umístěné ve standardní knihovně jazyka Java. Objekty této třídy lze vyvolat ve všech uživatelských metodách.
• Název výjimky popisuje vzniklý problém.
33
Speciální případy výskytu výjimky RuntimeException • if (t == null) throw new NulPointerException(); • tato podmínka je vyvolána zcela automaticky • celá kategorie metod, které jsou vyvolány zcela automaticky
34
Výstup chyb • System.err a System.out jsou standardní error stream a output stream, které jsou nasměrovány na konzolu
35
Vytváření vlastních výjimek • Vlastní výjimky mohou označit speciální typ chyb, k nimž může docházet při vykonávání kódu uživatelsky vytvořené knihovny. • Jejich vznik je nepředpokládaný v jazyce Java a proto nejsou zařazeny ani do hierarchie výjimek. • Při vlastním vytváření je třeba vycházet z existujícího typu výjimky – nejlépe toho, který je nově vzniklému typu výjimky nejbližší. • Nejjednodušší způsob vytvoření nového typu výjimky je nechat vytvoření implicitního konstruktoru na překladači. 36
// Jednoducha vyjimka // Odvozeni vlastnich typu vyjimek public class JednoduchaVyjimka extends Exception{ }
Osnova
public class SimpleExceptionDemo { public void f() throws JednoduchaVyjimka { System.out.println( System.out.println( “Vyvolani vyjimky JednoduchaVyjimka z metody f()” f()”); throw new JednoduchaVyjimka(); JednoduchaVyjimka(); } public static void main(String[] main(String[] args) args) { SimpleException sed = new SimpleExceptionDemo(); SimpleExceptionDemo(); try { sed.f(); sed.f(); } catch(JednoduchaVyjimka e) { System.err.println(“ System.err.println(“Vyjimka zachycena! zachycena!”); } } }
37
Specifikace výjimek • nutnost informovat programátory klientských aplikací o výjimkách knihovních metod • zdrojový kód se zpravidla nepublikuje, proto Java poskytuje (vynucuje si) speciální syntaxi jímž se uživatelé upozorňují na výjimky • specifikace výjimek používá klíčové slovo throws void f() throws PrilisVysoko, PrilisNizko, PrilisPozde { …}
38
Blok finally • Program, který získá jisté typy zdrojů je musí vrátit explicitně, aby se vyhnul unikání, nedostatku zdrojů (resource leaks). • Typy zdrojů: soubory, spojení s databází, síťová spojení • blok finally (i klíčové slovo) je volitelným blokem, je uveden až za posledním blokem catch a bude vždy prováděn, bez ohledu na přítomnost / nepřítomnost výjimky • jedině pokud aplikace skončí před blokem try např. příkazem System.exit pak se finally neprovede 39
Osnova try { prikazy prikazy pro obsazeni zdroju } catch (Druhvyjimky (Druhvyjimky vyjimka1){ prikazy vyjimky } . . catch(DalsiDruhVyjimky vyjimkaD) vyjimkaD) { // prikazy vyjimky } finally { prikazy uvolnujici zdroje }
Obecná struktura bloků try, catch a finally
40
// Demonstration of the try...catch...finally exception handling // mechanism. public class UsingExceptions { public static void main( String args[] args[] ) { try { throwException(); // call method throwException } // end try catch ( Exception exception ) // exception thrown by throwException { System.err.println( System.err.println( "Exception handled in main" ); } // end catch
Osnova UsingExceptions 1/3
doesNotThrowException(); } // end main
41
// demonstrate try...catch...finally public static void throwException() throws Exception { try // throw an exception and immediately catch it { System.out.println( System.out.println( "Method throwException" ); throw new Exception(); // generate exception } // end try catch ( Exception exception ) // catch exception thrown in try { System.err.println( System.err.println( "Exception handled in method throwException" ); throw exception; // rethrow for further processing
Osnova UsingException 2/3
// any code here would not be reached, exception rethrown in catch } // end catch finally // executes regardless of what occurs in try...catch { System.err.println( System.err.println( "Finally executed in throwException" ); } // end finally // any code here would not be reached } // end method throwException
42
// demonstrate finally when no exception occurs public static void doesNotThrowException() { try // try block does not throw an exception { System.out System.out. out.println( println( "Method "Method doesNotThrowException" ); } // end try catch ( Exception exception ) // does not execute { System.err System.err. err.println( println( exception ); } // end catch finally // executes regardless of what occurs in try...catch { System.err System.err. err.println( println( "Finally executed in doesNotThrowException" ); } // end finally
Osnova UsingExceptions 3/3
System.out System.out. out.println( println( "End "End of method doesNotThrowException" ); } // end method doesNotThrowException } // end class UsingExceptions
43
Osnova Method throwException Exception handled in method throwException Finally executed in throwException Exception handled in main Method doesNotThrowException Finally executed in doesNotThrowException End of method doesNotThrowException
44
Odvinující se zásobník Stack Unwinding • Když je vyhozena výjimka, ale není odchycena v dané oblasti (scope), je udělán další pokus chytit výjimku v následujícím vnějším bloku catch. • Tomuto procesu se říká stack unwinding (odvinující se zásobník) • Stack unwinding znamená, že metoda kde vznikla výjimka je ukončena, všechny lokální proměnné si zachovávají platnost v rozsahu vnějšího bloku a řízení se vrací na příkaz, který původně vyvolal metodu s výjimkou. • Celý proces se opakuje, dokud není výjimka odchycena viz následující příklad. 45
// Demonstration of stack unwinding.
Osnova
public class UsingExceptions { public static void main( String args[] args[] ) { try // call throwException to demonstrate stack unwinding { throwException(); } // end try catch ( Exception exception ) // exception thrown in throwException { System.err.println( System.err.println( "Exception handled in main" ); } // end catch } // end main // throwException throws exception that is not caught in this method method public static void throwException() throws Exception { try // throw an exception and catch it in main { System.out.println( System.out.println( "Method throwException" ); throw new Exception(); // generate exception } // end try catch ( RuntimeException runtimeException ) // catch incorrect type { System.err.println( System.err.println( "Exception handled in method throwException" ); } // end catch
46
finally // finally block always executes { System.err.println( System.err.println( "Finally is always executed" ); } // end finally } // end method throwException } // end class UsingExceptions
Osnova
Method throwException Finally is always executed Exception handled in main
47
Metody printStackTrace, getStackTrace getMessage • Třída Throwable poskytuje metody: • printStackTrace – která vydá standardní error stream stack trace – trasovací zásobník chybového streamu • getStackTrace – zpřístupní stack-trace informace, které mohou být vytištěny pomocí metody printStackTrace • getMessage – vrací popisný řetězec uložený ve výjimce
48
// Demonstrating getMessage and printStackTrace from class Exception.
Osnova
public class UsingExceptions { public static void main( main( String args[] args[] ) { try { method1(); // call method1 } // end try catch ( Exception exception ) // catch exception thrown in method1 { System.err System.err. err.printf( printf( "%s\ "%s\n\n", exception. exception.getMessage() getMessage() ); exception.printStackTrace exception.printStackTrace(); printStackTrace(); // print exception stack trace // obtain the stackstack-trace information StackTraceElement[] StackTraceElement[] traceElements = exception. exception.getStackTrace(); getStackTrace(); System.out System.out. out.println( println( "\ "\nStack trace from getStackTrace:" ); System.out System.out. out.println( println( "Class "Class\ Class\t\tFile\ tFile\t\t\tLine\ tLine\tMethod" tMethod" ); // loop through traceElements to get exception description for ( StackTraceElement element : traceElements ) { System.out System.out. out.printf( printf( "%s\ "%s\t", element.getClassName element.getClassName() getClassName() ); System.out System.out. out.printf( printf( "%s\ "%s\t", element.getFileName element.getFileName() getFileName() ); System.out System.out. . printf( printf ( "%s\ "%s \ t", element.getLineNumber element. getLineNumber() out getLineNumber() ); System.out System.out. printf( "%s\ "%s\n", element.getMethodName element.getMethodName() out.printf( getMethodName() ); } // end for } // end catch } // end main
49
// call method2; throw exceptions back to main public static void method1() throws Exception { method2(); } // end method method1
Osnova
// call method3; throw exceptions back to method1 public static void method2() throws Exception { method3(); } // end method method2 // throw Exception back to method2 public static void method3() throws Exception { throw new Exception( "Exception thrown in method3" ); } // end method method3 } // end class UsingExceptions
50
Exception thrown in method3
Osnova
java.lang.Exception: java.lang.Exception: Exception thrown in method3 at UsingExceptions.method3(UsingExceptions.java:49) at UsingExceptions.method2(UsingExceptions.java:43) at UsingExceptions.method1(UsingExceptions.java:37) at UsingExceptions.main(UsingExceptions.java:10) Stack trace from getStackTrace: getStackTrace: Class File UsingExceptions UsingExceptions.java UsingExceptions UsingExceptions.java UsingExceptions UsingExceptions.java UsingExceptions UsingExceptions.java
Line 49 43 37 10
Method method3 method2 method1 main
51
Zřetězené výjimky Chained Exceptions • Existujicí výjimka se zabalí (obalí) do nové výjimky – umožňuje výjimce, aby udržovala kompletní trasovací zásobník (stack-trace)
52
// Demonstrating chained exceptions.
Osnova
public class UsingChainedExceptions { public static void main( String args[] args[] ) { try { method1(); // call method1 } // end try catch ( Exception exception ) // exceptions thrown from method1 { exception.printStackTrace(); exception.printStackTrace(); } // end catch } // end main // call method2; throw exceptions back to main public static void method1() throws Exception { try { method2(); // call method2 } // end try catch ( Exception exception ) // exception thrown from method2 { throw new Exception( "Exception thrown in method1", exception exception ); } // end try } // end method method1
53
// call method3; throw exceptions back to method1 public static void method2() throws Exception { try { method3(); // call method3 } // end try catch ( Exception exception ) // exception thrown from method3 { throw new Exception( "Exception thrown in method2", exception exception ); } // end catch } // end method method2
Osnova
// throw Exception back to method2 public static void method3() throws Exception { throw new Exception( "Exception thrown in method3" ); } // end method method3 } // end class UsingChainedExceptions
54
Osnova java.lang.Exception: java.lang.Exception: Exception thrown in method1 at UsingChainedExceptions.method1(UsingChainedExceptions.java:27) UsingChainedExceptions.method1(UsingChainedExceptions.java:27) at UsingChainedExceptions.main(UsingChainedExceptions.java:10) UsingChainedExceptions.main(UsingChainedExceptions.java:10) Caused by: java.lang.Exception: java.lang.Exception: Exception thrown in method2 at UsingChainedExceptions.method2(UsingChainedExceptions.java:40) UsingChainedExceptions.method2(UsingChainedExceptions.java:40) at UsingChainedExceptions.method1(UsingChainedExceptions.java:23) UsingChainedExceptions.method1(UsingChainedExceptions.java:23) ... 1 more Caused by: java.lang.Exception: java.lang.Exception: Exception thrown in method3 at UsingChainedExceptions.method3(UsingChainedExceptions.java:47) UsingChainedExceptions.method3(UsingChainedExceptions.java:47) at UsingChainedExceptions.method2(UsingChainedExceptions.java:36) UsingChainedExceptions.method2(UsingChainedExceptions.java:36) ... 2 more
55
Preconditions a Postconditions • Programátoři stráví velkou část času nad údržbou kódu a laděním. • K usnadnění těchto úloh a zlepšení celkového návrhu se často specifikují očekávané stavy před a po vykonání metody. Tyto stavy se nazývají preconditions resp. postconditions. • precondition metody – je podmínka, která musí být true, když je metoda volána. • preconditions popisují parametry metody a další očekávání, které má metoda 56
Preconditions a Postconditions • pokud uživatel nesplní preconditions, chování metody je pak nedefinované – může vyhodit výjimku atd. • postconditions metody je podmínka, která je true – když metoda skončila úspěšně • postconditions popisují návratové hodnoty a další side-effects, které může mít metoda • Např. metoda charAt třídy String má jeden parametr int – index řetězce. Podmínka precondition testuje zda je index v rozsahu daného řetězce. 57
Assertions - tvrzení • Často je výhodné mít testovací podmínku uvnitř metody. Tyto podmínky se nazývají assertions a pomáhají zlepšit testování programu • tvar použití: – assert vyraz; příkaz vyhodnotí výraz a vyhodí výjimku AssertationError je-li výraz false – assert vyraz1 : vyraz2 příkaz vyhodnotí vyraz1 a vyhodí AssertationError s výrazem2 jako chybovou zprávou, je-li vyraz1 false
58
package vyjimky; vyjimky; import java. java.util.Scanner; util.Scanner; public class AssertTest { public static void main( main(String args[]){ args[]){ Scanner input = new Scanner(System Scanner(System.in); System.in);
Osnova
System. System.out. out.println("Zadejte println("Zadejte cislo v rozsahu 0 az 10: "); int number = input. input.nextInt(); nextInt(); assert (number >= 0 && number <= 10) : "cislo "cislo mimo rozsah: " + number; number; System. System.out. out.printf("Zadal printf("Zadal jste %d\ %d\n", number); number); } } // spousteni programu s prepinacem // java –ea AssertTest
-ea
59
Osnova Zadejte cislo v rozsahu 0 az 10: 123 Zadal jste 123 D:\ D:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Priklady>java Priklady>java -ea vyjimky.AssertTest Zadejte cislo v rozsahu 0 az 10: 9 Zadal jste 9 D:\ D:\DATA\ DATA\Java\ Java\eclipseData\ eclipseData\Priklady>java Priklady>java -ea vyjimky.AssertTest Zadejte cislo v rozsahu 0 az 10: 66 Exception in thread "main" java.lang.AssertionError: java.lang.AssertionError: cislo mimo rozsah: rozsah: 66 at vyjimky.AssertTest.main(AssertTest.java:10)
60
Výhody použití zpracování výjimek 1. Oddělení kódu pro zpracování chyb od „normálního“ kódu aplikace 2. Šíření hlášení o chybách směrem k vrcholu „zásobníku“ volání metod 3. Seskupování a diferenciace typů chyb
61
readFile { open the file; determine its size; allocate that much memory; read the file into memory; close the file; }
Osnova Oddělení kódu pro zpracování chyb od „normálního“ kódu aplikace
Na první první pohled se uvedená uvedená funkce zdá zdá jednoduchá jednoduchá, ale ignoruje vš všechny následují sledující potenciá potenciální lní chyby:
* * * * *
Co Co Co Co Co
se se se se se
stane, stane, stane, stane, stane,
když když když když když když když když když když
soubor nemůž nemůže ůže být otevř otevřen? en? nemůž nemůže ůže být stanovena délka souboru? souboru? nemůž nemůže ůže být př přidě idělen dostatek operač operační pamě paměti? ti? čtení tení souboru skonč skončí s chybou? chybou? soubor nemůž nemůže ůže být uzavř uzavřen? en?
62
errorCodeType readFile { initialize errorCode = 0; open the file; file; if (theFileIsOpen (theFileIsOpen) theFileIsOpen) { determine the length of the file; file; if (gotTheFileLength (gotTheFileLength) gotTheFileLength) { allocate that much memory; memory; if (gotEnoughMemory (gotEnoughMemory) gotEnoughMemory) { read the file into memory; memory; if (readFailed (readFailed) readFailed) { errorCode = -1; } } else { errorCode = -2; } } else { errorCode = -3; } close the file; file; if (theFileDidntClose (theFileDidntClose && errorCode == 0) { errorCode = -4; } else { errorCode = errorCode and -4; } } else { errorCode = -5; } return errorCode; errorCode;
Osnova Klasické řešení bez použití výjimek
}
63
readFile { try { open the file; determine its size; allocate that much memory; read the file into memory; close the file; } catch (fileOpenFailed (fileOpenFailed) fileOpenFailed) { doSomething; doSomething; } catch (sizeDeterminationFailed (sizeDeterminationFailed) sizeDeterminationFailed) { doSomething; doSomething; } catch (memoryAllocationFailed (memoryAllocationFailed) memoryAllocationFailed) { doSomething; doSomething; } catch (readFailed (readFailed) readFailed) { doSomething; doSomething; } catch (fileCloseFailed (fileCloseFailed) fileCloseFailed) { doSomething; doSomething; } }
Osnova Řešení s využitím výjimek chybových stavů
64
Osnova Předpoklá edpokládejme, že metoda readFile je v poř pořadí adí čtvrtou metodou v řadě adě vnoř vnořených volá volání metod, které které vyvolal hlavní hlavní program: program: metoda metoda1 volá volá metodu metodu2, která která volá volá metodu metodu3, která která volá volá koneč konečnou metodu readFile. readFile. method1 { call method2; }
Šíření hlášení o chybách směrem k vrcholu „zásobníku“ volání metod
method2 { call method3; } method3 { call readFile; readFile; }
65
Tradič Tradiční techniky oznamová oznamování chyb nutí nutí metodu2 a metodu3 šíř šířit chybový kó kód vrá vrácený metodou readFile smě směrem k vrcholu zá zásobní sobníku volá volání dokud chybový kód koneč konečně nedosá nedosáhne metodu1— metodu1—jedinou metodu, která která se o to zají zajímá.
Osnova
method1 { errorCodeType error; error = call method2; if (error) doErrorProcessing; doErrorProcessing; else proceed; proceed; } errorCodeType method2 { errorCodeType error; error = call method3; if (error) return error; else proceed; proceed; } errorCodeType method3 { errorCodeType error; error = call readFile; readFile; if (error) return error; else proceed; proceed; }
66
Připomeň ipomeňme, že javovské javovské běhové hové prostř prostředí edí (Java runtime environment) environment) prohledá ává zpě prohled zpětně tně přes zá zásobní sobník volá volání, aby nalezlo jaké jakékoli metody, které které se zají zajímají mají o zpracová zpracování konkré konkrétní tní výjimky. výjimky. Pouze metody, které které se zabývají zabývají chybami se musí musí obá obávat př případné padné detekce chyb.
Osnova
method1 { try { call method2; } catch (exception e) { doErrorProcessing; doErrorProcessing; } } method2 throws exception { call method3; } method3 throws exception { call readFile; readFile; }
67
Metoda můž ůže etoda můž může ůže deklarovat specifický specifický ovladač ovladač, který m ůže zpracová zpracovávat velmi specifickou výjimku. výjimku. Třída FileNotFoundException nemá nemá podtř podtřídy, dy, takž takže následují výjimky. . sledující ovladač ovladač může ůže zpracová zpracovávat pouze jeden typ výjimky catch (FileNotFoundException (FileNotFoundException e) { ... }
Osnova Seskupování a diferenciace typů chyb
Method může patř ůže odchytit výjimku pat řící do skupiny vš všeobecných typů typů specifikací í jaké specifikac jakékoli výjimky nadtř nadtřídy v př příkazu catch. catch. Na př příklad, klad, k odchycení odchycení všech vstupně vstupně/výstupní /výstupních (I/O (I/O) I/O) výjimek, výjimek, bez ohledu na jejich specifický typ, typ, ovladač ovladač výjimky specifikuje argument IOException. IOException. catch (IOException (IOException e) { ... } Tento ovladač ovladač bude schopen odchytit vš všechny vstupně vstupně/výstupní /výstupní výjimky včetně etně FileNotFoundException, FileNotFoundException, EOFException, EOFException, a další dalších ších výjimek. výjimek. Detaily naleznete použ použití itím ná následují sledujícího „trasovací trasovacího“ ho“ tisku. tisku. catch (IOException (IOException e) { e.printStackTrace(); e.printStackTrace(); //Output goes to System.err. e.printStackTrace(System.out); e.printStackTrace(System.out); //Send trace to stdout. stdout. }
68
Osnova Můžete ůžete dokonce vytvoř vytvořit ovladač ovladač výjimky, který oš ošetř etří jakoukoli výjimku. výjimku. catch (Exception e) { ... }
//A (too) general exception handler
69