Java programozási nyelv 9. rész Kivételkezelés Nyugat-Magyarországi Egyetem Faipari Mérnöki Kar Informatikai Intézet Soós Sándor 2005. szeptember A Java programozási nyelv Soós Sándor 1/24
Tartalomjegyzék Mit nevezünk kivételkezelésnek? Kivétel keletkezése Kivétel kiváltásának típusai A megfelelő kivételkezelő keresése A kivétel elkapása (catching exception) A kivétel kiváltása Java-ban A kivétel elkapása Java-ban A finally blokk Kivételek specifikálása Klasszikus példa a Java nyelv specifikációjából A Java programozási nyelv Soós Sándor 2/24
Mit nevezünk kivételkezelésnek? Szűkebb értelemben: A programban fellépő futásidejű hibák kezelését. Tágabb értelemben: A program normál logikájához képest különleges, attól eltérő helyzetek kezelését. Ez azt is jelenti, hogy abszolút nem hibás esetek kezelését is megoldhatjuk a kivételkezelés mechanizmusával. A végső cél: Teljes egészében kezünkben tartsuk a program működését, minden helyzetben a szándékaink szerint reagáljon a program. Soha ne forduljon elő olyan eset, amikor a program úgy fejeződik be, hogy valamit "elvarratlanul" hagyott. A Java programozási nyelv Soós Sándor 3/24
Miért fontos ez? Milyen problémákat okozhat egy rendkívüli program leállás? adatvesztés: nyitva maradt fájlok még el nem mentett, berögzített adat. A felhasználó begépelt 5 oldalnyi szöveget, amikor a program "elszáll", vagy hibajelzéssel leáll. időveszteség: egy 5 órája futó kalkuláció a végeredmény elkészülte és a részeredmények elmentése nélkül hibajelzéssel leáll. erőforrások fogvatartása a program futása során lefoglal bizonyos erőforrásokat (fájlok, adatbázis rekordok), ezeket a futás végén szabadítaná fel. pl. kórházi rendszer, lefagy a terminál, zárolt marad a rekord A Java programozási nyelv Soós Sándor 4/24
Mi a megoldás? A programot fel kell készíteni minden eshetőségre. Például: Az előző program hibakezeléssel: Egy egyszerű program: program { fuggveny1(); fuggveny2(); fuggveny3(); } Miért nem jó ez? program { if (fuggveny1() = OK) { if (fuggveny2() = OK) { if (fuggveny3() = OK) { // minden rendben } else { // hibakezelés3 } } else { // hibakezelés2 } } else { // hibakezelés1 } } A Java programozási nyelv Soós Sándor 5/24
Miért nem jó ez a megoldás? A hibakezelés elrejti, széttördeli az eredeti programlogikát: nehezen olvasható kód nehezen javítható, módosítható a kód A hibakezelő programrésznek nincs információja a hiba jellegéről, okáról Nagyobb programok esetén ez a megoldás kivitelezhetetlen, sőt... hiba bekövetkezhet a hibakezelő programrészben is, ezért oda is kellene ellenőrzés,... A Java programozási nyelv Soós Sándor 6/24
A szép megoldás class Exception1 extends Exception {} class Exception2 extends Exception {} class Exception3 extends Exception {} Pogram() { try { fuggveny1(); fuggveny2(); fuggveny3(); } catch(exception1 hiba1) { /* hibakezelés1 */ } catch(exception2 hiba2) { /* hibakezelés2 */ } catch(exception3 hiba3) { /* hibakezelés3 */ } } A Java programozási nyelv Soós Sándor 7/24
A kivételkezelés ötlete Válasszuk szét az eredeti programlogikát és a kivételes helyzetek (hibaállapotok) kezelését A kivételkezelő kapjon információt a hiba típusáról, helyéről és egyéb körülményeiről A kivételkezelő legyen képes visszatérni az eredeti programlogikához, ha a hiba jellege ezt lehetővé teszi. Legyen lehetőség garantálni a program és minden részprogram korrekt befejeződését bármilyen körülmények fennállása esetén (fájlok bezárása, erőforrások felszabadítása, stb.) A Java programozási nyelv Soós Sándor 8/24
Kivételkezelés a Java-ban Ezeket az elveket különböző módokon valósítják meg a különböző programnyelvek (C++, Ada, Eiffel, Object Pascal Delphi) A Java ezt is teljes mértékben osztályokkal valósítja meg. A kivételeket is beépíti az osztályhierarchiába. A kivételek csak annyiban kivételesek, hogy ők "kiválthatóak", míg a többi osztály nem. A Java programozási nyelv Soós Sándor 9/24
Kivétel keletkezése Amikor valamilyen hiba lép fel egy metódus végrehajtása során (vagy a programozó külön útra kívánja terelni a végrehajtást), akkor létrejön egy kivétel objektum A "hiba" adatai és a program állapota rögzítésre kerül a kivétel objektumban Az objektum a Java Virtuális Gép felügyelete alá kerül. Ezt nevezzük a kivétel kiváltásának (throwing exception) Ekkor a blokk futása megszakad, oda már nem kerül vissza a vezérlés. A Java programozási nyelv Soós Sándor 10/24
Kivétel kiváltásának típusai Kivétel háromféleképpen keletkezhet: A program futása közben valamilyen rendellenes dolog történt: nullával osztás, erőforrások elfogyása, osztálybetöltési hiba, stb. A program végrehajtott egy throw utasítást. Ezt megteheti valamely Java könyvtári osztály, vagy maga a programozó Egy aszinkron kivétel lépett fel. Ez akkor következhet be, amikor a program több szálon fut és egy másik szál futása megszakad. Ezzel most nem foglalkozunk. A Java programozási nyelv Soós Sándor 11/24
A megfelelő kivételkezelő keresése A kivétel kiváltása után a JVM megkeresi a megfelelő kivételkezelő kódblokkot. Egy kivételkezelő kódblokk akkor megfelelő, ha a hatáskörében keletkezett a kivétel a típusa megfelelő a kiváltott kivételhez. A típus akkor megfelelő, ha vagy a kezelő által kezelt kivétel típusa azonos a kivételével vagy az őse annak A blokkok egymásba ágyazása miatt több alkalmas kezelő is lehet. Ezek közül a legbelsőt választja a JVM. Ezt nevezzük a kivétel elkapásának (catching exception) A Java programozási nyelv Soós Sándor 12/24
A kivétel elkapása (catching exception) A megfelelő kivételkezelő blokk megtalálásakor a JVM átadja a vezérlést a kiválasztott kezelőnek Lefut a blokk A kivételkezelő blokk lefutása után a kezelő blokk utáni utasításra kerül a vezérlés. A kivételkezelés közben is kiváltódhat kivétel, ekkor a blokk végrehajtása megszakad, és ugyanaz lezajlik, mint az előbb. Ha a JVM nem talál alkalmas kivételkezelő blokkot, akkor megszakad a program futása, (legalábbis az egyszálú programok esetében.) A Java programozási nyelv Soós Sándor 13/24
Konkrét példa Bevezetésképpen nézzünk meg egy konkrét példát! A Verem.java fájlban látjuk egy verem adattípus részletét A VeremTeszt.java fájlban pedig kipróbáljuk ezt a verem típust. A Java programozási nyelv Soós Sándor 14/24
A kivételkezelés megvalósítása Java-ban A Java nyelv a következő utasításokat nyújtja a kivételkezeléshez: throw: kivétel kiváltása try-catch-finally: kivételkezelő blokk meghatározása throws: kivételek specifikálása A Java programozási nyelv Soós Sándor 15/24
A kivétel kiváltása Java-ban Vagy a Virtuális Gép, vagy egy metódus válthatja ki a kivételt (throw utasítás). A throw paramétereként egy kivételosztályt kell megadni. Például: class UjException extends Exception { } throw new Exception( "Hiba történt! ); throw new UjException(); A throw paramétereként csak olyan objektumot lehet megadni, ami kiváltható, azaz az osztálya a java.lang.throwable osztály leszármazottja. A Java programozási nyelv Soós Sándor 16/24
A kivétel elkapása Java-ban A kivételek elkapásához a vizsgálni kívánt utasításokat egy try blokkba kell tenni: try { // Java utasítások } A try blokkban lévő utasításokat figyeli a JVM kivételek keletkezése szempontjából. Rajtunk múlik, hogy mekkora blokkokat alakítunk ki, tehetünk minden utasítást külön blokkba, vagy mindet egybe, és ezek között tetszőleges átmenet használható. A Java programozási nyelv Soós Sándor 17/24
A kivétel elkapása, folyt. A try blokkban kiváltott kivételeket az utána következő catch blokk, vagy blokkok kezelik le: try { // java utasítások } catch(típus1 változó1) { /* Java utasítások */ } catch(típus2 változó2) { /* Java utasítások */ } catch(típus3 változó3) { /* Java utasítások */ } Minden catch ágban a "paraméterként" megadott típus határozza meg, hogy mely kivételeket képes lekezelni. A Java programozási nyelv Soós Sándor 18/24
A catch ágak sorrendje A catch ágak sorrendje fontos, mert a megadott sorrendben keresi a JVM a megfelelő kezelőt. A kivételosztályok egymásra épülése miatt több blokk is alkalmas lehet. Ezek közül az elsőt választja a JVM. A Java fordítási hibát jelez, ha biztosan el nem érhető kódot talál. Ez a tény befolyásolja a catch ágak lehetséges sorrendjét. A Java programozási nyelv Soós Sándor 19/24
A finally blokk Az utolsó catch blokk után megadható egy finally blokk. Az ebbe írt utasítások mindenképpen lefutnak, akkor is, ha nem lépett fel kivétel a kivételt valamelyik catch blokk lekezelte a kivételt egyik catch ág sem tudta lekezelni nem megfelelő típus miatt. Ekkor a hívó metódus kivételkezelő blokkjához kerül a vezérlés, de előtte lefut a finally blokk Nézzük meg a FinallyTest.java példát! A Java programozási nyelv Soós Sándor 20/24
Egy teljes kivételfigyelő blokk try { // java utasítások } catch(típus1 változó1) { /* Java utasítások */ } catch(típus2 változó2) { /* Java utasítások */ } catch(típus3 változó3) { /* Java utasítások */ } //... finally { /* Java utasítások */ } A try blokk után kötelezően következnie kell legalább egy catch vagy finally blokknak. Ha mindkettő van, akkor catch finally sorrendben kell megadni őket. A Java programozási nyelv Soós Sándor 21/24
Kivételek specifikálása Ha a programozó úgy dönt, hogy egy kivételt nem akar lekezelni az adott metódusban, hanem egy közvetett, vagy közvetlen hívójára hagyja azt, akkor a metódus fejében specifikálnia kell azt a throws kulcsszóval. metódus() throws Exception1, Exception2,... { //... } Ha a metódus nem kezeli le a specifikált kivételt, akkor a JVM egy szinttel feljebb keres alkalmas kezelőt. A Java programozási nyelv Soós Sándor 22/24
Mikor érdemes specifikálni egy kivételt? Van olyan eset, amikor a kivételt nem a kiváltó metódus szintjén lehet lekezelni, hanem feljebb. Például ha tele van a verem, akkor azt nem a verem tudja kezelni, hanem a vermet használó osztály. Ezért a Betesz metódus ne kezelje le a kivételt, hanem specifikálja azt. A Java programozási nyelv Soós Sándor 23/24
Klasszikus példa a Java nyelv specifikációjából Nézzük meg a Teszt.java programot! Aki megértette a program működését, az valóban érti a Java kivételkezelő mechanizmusát. A Java programozási nyelv Soós Sándor 24/24