Přihoditelné nehody Spouštění programů je riskantní počínání a tudíž je radno se pojistit. Java má však neobyčejně seriózní pojišťovnu, jež umožňuje téměř kdekoli v programu uzavírat specifická úrazová pojištění pro zmírnění následků případných softwarových nehod. K tomu slouží: Klasifikace typů úrazů košatým stromem třídy Throwable s větvemi: - Error - chyby - tj. fatální maléry ( nekontrolované kompilátorem ) - Exception - výjimky - tj. úrazy povětšinou léčitelné: - checked - tj. kontrolované kompilátorem - unchecked - tj. nekontrolované kompilátorem Objekty typu Throwable popisují nehodu - lze je vyhodit pomocí throw. Klauzule TCF ( try–catch–finally ) je pojistná smlouva pro odchyt a zpracování hozených objektů za účelem minimalizace škod. Stigma ( co znamení hanby ) tvoří throws a seznam kontrolovaných výjimek v hlavičkách metod a konstruktorů. PJV03
Strom třídy Throwable java.lang.Object java.lang.Throwable // co je černě, to je kontrolované java.lang.Error // unchecked subtree java.lang.VirtualMachineError java.lang.StackOverflowError … java.lang.Exception{ … } java.lang.RuntimeException // unchecked subtree java.lang.ArithmeticException java.lang.NullPointerException java.lang.IndexOutOfBoundsException my.Bomb java.util.NoSuchElementException java.io.IOException java.net.SocketException java.net.ConnectException PJV03
Třída java.lang.Throwable obsahuje čtyři konstruktory s kombinací dvou parametrů: String message – pro doprovodnou vysvětlivku Throwable cause – pro připojení příčiny a nestatické metody: String toString( ) - výpis v textovém tvaru, String getMessage( ) - získání doprovodné zprávy, Throwable getCause ( ) - získání předchozí příčiny. StackTraceElement[ ] getStackTrace ( ) - získání zásobníku jako pole, void printStackTrace( … ) - výpis zásobníku se sledem volaných metod, Potomci této třídy, tj. typicky Exception, další metody téměř nepřidávají – definují jen své konstruktory. PJV03
Kontrolované výjimky Kompilátor napomáhá prevenci nehod tím, že mentoruje programátora, aby uzavřel pojistku před voláním stigmatizovaných metod či konstruktorů. To lze učinit pomocí TCF try–catch–finally : - buď rovnou kolem místa volání - anebo v některé předchozí volající metodě či konstruktoru, pak je ovšem nutno stigmatizovat mezilehlé metody či konstruktory. Za vznik kontrolovaných výjimek programátor zpravidla nemůže, neboť ty jsou způsobeny vnějšími vlivy, leč on musí zajistit jejich zvládnutí. Nekontrolované Throwable, tj. RuntimeException a Error se považují za programátorovy omyly nebo nedůslednosti, které by měl odstranit či přiměřeně pojednat. PJV03
Syntax klauzule TCF ( Java 7 ) TCF může mít až 7 tvarů ( + značí i více ) kde: autocl. param pojistná zóna odchyt a léčba úklid try [ (AC … ) ] { … } catch (Th … ) { … }+ [ finally { … } ] try [ (AC … ) ] { … } finally { … } try (AC … ) { … } podrobněji: ( AC p = … [ ; AC r = … ] … [;] ) p, r - reference typu AutoCloseable - odkazují na objekty pro automatické uzavření ihned po dokončení bloku try anebo dojde-li v něm k vyhození objektu typu Throwable, pak se vyjímky vzniklé při uzavírání potlačí. ( Th [ | Th ] … ex ) Th - navzájem exkluzivní subtypy Throwable. Má-li TCF blok finally, pak ten se vždy nakonec provede. TCF lze vnořit do kterýchkoli bloků, tedy i do bloků jiné TCF. PJV03
try–catch–finally try ( AutoCloseable p = new AC( ) ; Closeable r = x ; … ) { // pojistná zóna - začátek } // pojistná zóna - konec // léčebny pro úrazy: catch ( XxxException e ) { … } // typu Xxx catch ( YyyException e ) { … } // typu Yyy catch ( ZzzError e ) { … } // typu Zzz finally { … } // povinná úklidová brigáda // uzavření AutoCloseable Typy musejí být řazeny od specifického k obecnějšímu. k nadřízené TCF pohoda nehoda nehoda Yyy Au PJV03
Osud pojištěnce Ten, kdo vejde do pojistné zóny se stane pojištěncem lokálně pojištěným v této pojistné zóně, tj. bude lokálně léčen na jediný úraz z uvedených v seznamu catch. ( Může však být pojištěn i z předchozích působišť. ) Pojištěnec opustí pojistnou zónu jako: Zdravý – pokud pracoval bez nehody. Zraněný – rozdělanou práci nedokončí, opustí místo nehody jsa vybaven průkazem o utrpěném úrazu při nehodě - což je objekt typu Throwable. Nešťastník se pak plazí shora dolů v seznamu catch a hledá první léčebnu typově kompetentní k jeho zranění. - Najde-li: vykoná tam požadované a odchází jako Zdravý. - Nenajde-li, zůstává Zraněný plazí se dál a hledá pomoc v léčebnách definovaných předchozími dosud platnými pojistkami atd. Blokem finally, musí nakonec Zdravý i Zraněný projít. Je to taková povinná úklidová brigáda. Žádný se však nemůže vrátit přímo k nedodělané práci. Leč i v bloku catch či finally lze utrpět úraz - to pak řeší nadřízená TCF . PJV03
Osud nepojištěnce Kdo moc nevěří pojišťovnám může uzavřít jen minimální pojistku ve formě try–finally, čili bez nároku na léčbu a uzdravení, avšak s povinnou úklidovou brigádou pro Zdravého i Zraněného. I to může být příhodné: uklidit po sobě, napsat závěť apod. A někdo se nepojistí vůbec, a je-li pak Zraněný, přesto hledá léčebnou péči ve vyšších a vyšších blocích a nakonec ( marně i v metodě main ) se vrátí do metody run( ) v nejvyšší autoritě, tj. do JVM. Tam však krutá JVM nikoho neléčí, nýbrž každého tj. i Zdravého nelítostně zavraždí. Zraněnému však ve svém finally vystaví parte s výpisem zásobníku o tom jak a kudy se nebožák na místo nehody dostal a jaký úraz tam utrpěl. K vraždě Zdravého JVM však jen lakonicky podotkne, že je to v pořádku. PJV03
Co bylo hozeno, musí být chyceno Nehody mohou vzniknout jen v čase běhu programu - buď v hardware ( např.: dělením nulou ) anebo záměrně programaticky v JVM či v aplikačním programu. V každém případě musí vzniknout popis nehody, tj. objekt vhodné třídy, tedy některé ze stromu Throwable. Konstrukci provede vlákno po nehodě s případným přispěním JVM. Operátorem throw se spustí hledání v zásobníku. Vlákno hledá až nalezne první blok catch kompatibilního typu a mezitím provádí případné mezilehlé bloky finally. Nalezený blok catch provede a i případně návazný finally blok. Pak se vrátí do volatele ( caller ) a pokračuje již normálně. Ovšem pokud v předchozím postupu nastane nová nehoda, přejde se na řešení té nové. Starou lze k ní připojit jako příčinu – tak může vzniknout řetěz popisů příčin a následků. Vše bude nakonec chyceno, neboť za metodou main skrytě číhá JVM s catch ( Throwable th ). Tam nic už nenapraví – jen vypíše pěkné parte. Tím se také JVM brání proti nehodám vzniklých v aplikacích či apletech. PJV03
Pozn.: při opouštění synchronizovaných metod či bloků vrací zámky. Příklad main m1 m2 m3 m4 try { try { try { try { try { m1(); m2(); m3(); m4(); B JVM } } } } } } catch A catch A finally catch C catch D catch B catch B catch E finally finally finally Pozn.: při opouštění synchronizovaných metod či bloků vrací zámky. PJV03
Ukončení s finally Blok finally představuje netriviální intervenci do toku řízení. Příklad: try { int k = 1 / n; return 0; } catch ( Exception ex ) { … return 1; } finally { … if ( b ) return 2; } Obdobné platí i pro break, continue, throw a assert . n b vrací !=0 false 0 !=0 true 2 ==0 false 1 ==0 true 2 PJV03
Stigmatizace Metody a konstruktory vyznačují ve svých hlavičkách typy kontrolovaných výjimek, které případně mohou vyhodit. Upřímě tím přiznávají stigmata své nekompetence, kterýmiž ohrožují své volatele, neboť neřeší v nich vzniklé nehody a přenechávají je jiným. Syntakticky konkrétní, abstraktní metody a konstruktor takto: ... ... met1( ... ) [ throws AaaException, BbbException, ... ] { … } ... ... met2( ... ) [ throws AaaException, BbbException, ... ] ; … Any ( ... ) [ throws AaaException, BbbException, ... ] { … } To se objeví také ve standardní dokumentaci. Mechanismus výjimek je časově náročný – při vstupu/výstupu do/z TCP, při vytvoření a vyhození objektu typu Throwable i při hledání místa odchytu. Doporučuje se využívat ho jen pro nehody, nikoli pro testy podmínek. V prostých pokusech lze vynechat nepřehledné try klauzule stigmatem throws Exception. PJV03
Vlastní Throwable Lze se vytvořit jako potomek Throwable - typicky Exception. Většinou se nepřidávají žádné vlastní atributy ani metody – jen se definují konstruktory, vše ostatní zdědí. Dobrým indikátorem nehody bývá dlouhé jméno, čímž vzniká integrovaný "chybník" pro run-time. Příklad výjimky pro případ konfliktu v tabulce. public class TableException extends Exception { int row, col; public TableException( int row, int col ) { // konstruktor vytvoří super( "Wrong item at: " +row+ " " +col ); // zprávu this.row = row; this.col = col; // i explicitní indikaci } ta se vytvoří a vyhodí např. takto: throw new TableException( 333, 7 ); PJV03
Příkaz assert ( od v. 1.4 ) Kontroluje platnost podmínek pro dodatečné ladění jako pro budoucnost nastražená past, která je zevně selektivně ovladatelná a v běžném provozu je neaktivní. Proto se mají užívat jen v private metodách či částech, které by se neměly vlastně provádět. Syntax: assert podmínka [ : výraz vracející hodnotu ] ; při podmínce false se vyhodí chyba. Nepovolit vedlejší efekty výrazu. java.lang.AssertionError: [ hodnota výrazu jako řetěz ] Tuto chybu nejspíš nemá smysl odchytávat. Nemá-li se chápat assert jako jméno ale jako příkaz. nutno kompilovat: javac –source { 1.4 | 1.5 | 1.6 | 1.7 | 1.8 } Příkazy assert se ovládají spuštěním: java -ea ... / java -da … ClassLoader umožní en / dis -abled dle stromu balíčků či tříd. java -ea -da:com.pack1 či java -ea -da:com.pack1.MyProgram PJV03