Documente Academic
Documente Profesional
Documente Cultură
1. Tratarea excepţiilor.
Scopul lucrării
1.1. Excepţii
Pe parcursul execuţiei unui program pot să apară situaţii în care unele operaţii nu se pot
efectua (spre exemplu nu se poate deschide un fişier deoarece acesta nu există). Pentru determinarea
acestor situaţii în programarea clasică se efectuau o serie de teste: se testa valoarea întoarsă de o funcţie
despre care se ştie că poate să eşueze şi/sau se verifica valoarea unor indicatori de eroare globali. Acest
stil de programare, pe lângă faptul că este greu de urmărit, prezintă câteva neajunsuri majore: nu
tratează situaţiile neprevăzute şi necesită un efort de programare mare pentru tratarea unor cazuri rar
întâlnite. Secvenţa de pseudocod de mai jos ilustrează stilul de programare clasic:
Limbajele de programare mai noi introduc noţiunea de excepţie ca fiind o situaţie neprevăzută
(spre exemplu împărţirea la zero). Un programator în limbajul Java se poate întâlni cu trei categorii de
excepţii [5]:
excepţii normale;
excepţii de execuţie (run-time);
erori.
Excepţii normale sunt cauzate de o anumită secvenţă de cod. Se poate spune cu alte cuvinte că
locul de apariţie al excepţiei este previzibil, chiar dacă excepţia în sine nu e. Un exemplu în acest sens
ar fi o excepţie FileNotFoundException, care apare ca urmare a unei încercări de a deschide un fişier
inexistent. Un alt exemplu ar fi depăşirea indicilor unui tablou într-o expresie care utilizează tabloul.
Spre deosebire de prima categorie, cauza excepţiilor run-time este mai greu de stabilit. Să
considerăm exemplul unei excepţii NullPointerException, care poate să apară la oricare referire a unei
instanţe nule, sau ArithmeticException, care poate să apară în orice expresie matematică ce conţine o
împărţire la zero.
Erorile semnalează evenimente de genul depăşirii stivei sau a adresării în afara memoriei
fizice şi semnalează funcţionarea defectuoasă a maşinii virtuale sau probleme legate de sistemul de
operare (spre exemplu: OutOfMemoryError, StackOverflowError); tratarea acestora de către
programator nu prezintă interes practic.
Mecanismul de tratare a erorilor prezintă câteva avantaje faţă de metodele tradiţionale [5]:
permite separarea codului responsabil de funcţionalitatea programului de tratarea erorilor,
mărind astfel lizibilitatea;
oferă un mecanism ce permite propagarea erorilor în stiva de apeluri; astfel o metodă
apelantă poate să trateze o eroare ce survine într-una dintre metodele apelate;
permite diferenţierea şi tratarea mai multor tipuri de condiţii anormale.
1.1.2. Cuvintele cheie try, throw, catch
Programatorul poate delimita o secvenţă care ar putea genera o excepţie cu ajutorul cuvintelor
rezervate try şi catch. Să considerăm un prim exemplu:
int x, y;
…
try {
y = 1/x;
/** x ar putea fi nul */
} catch (Exception e) {
System.out.println(e);
y = 0;
}
System.out.println(“y = “ + y);
try se foloseşte pentru a delimita un bloc de instrucţiuni suspect de a genera o excepţie. După
acolada ce închide blocul se observă folosirea lui catch pentru “prinderea” eventualelor excepţii.
În cazul în care valoarea lui x este diferită de 0, împărţirea poate fi efectuată şi execuţia
programului continuă cu afişarea valorii lui y. Dacă x ia valoarea zero atunci împărţirea nu se poate
efectua şi se va genera o excepţie. În exemplul de mai sus s-a prins o excepţie generală de tip
Exception, care va fi afişată. Tratarea erorii constă în iniţializarea y cu valoarea zero.
Într-un bloc try pot să se genereze mai multe tipuri de excepţii, care necesită tratare
diferenţiată. Pentru aceasta se pot folosi mai multe blocuri catch, fiecare pentru un caz particular.
Secvenţa de mai jos ilustrează un astfel de exemplu:
try {
y = 1 / unInt.intValue();
/** x ar putea fi nul */
} catch (ArithmeticException a) {
/** Impărţire la 0 */
System.out.println(a);
y = 0;
} catch (NullPointerException n) {
/** folosirea unei referinţe nule */
System.out.println(n);
} catch (Exception e) {
/** Altceva... */
System.out.println(e);
}
System.out.println("y=" + y);
Se pot specifica mai multe tipuri de excepţii, dintre care se va alege primul care este
compatibil cu excepţia curentă. Datorită faptului că excepţiile sunt la rândul lor obiecte, derivate direct
sau indirect din java.lang.Error şi java.lang.Exception (derivate la rândul lor din java.lang.Throwable),
observaţiile referitoare la atribuirea extinsă la subclase (vezi Error: Reference source not found) sunt
valabile. Consecinţa este că se prind mai întâi excepţiile specializate şi mai apoi excepţiile generale.
Excepţiile se “aruncă” cu ajutorul lui throw. Exemplul de mai jos va arunca o excepţie dacă
valoarea lui i este negativă:
…
if (i < 0) {
throw new Exception( "Număr negativ" );
}
Limbajul Java a fost proiectat în vederea dezvoltării de programe cât mai robuste. De aceea se
cere ca orice metodă care ar putea arunca o excepţie normală fie să o trateze cu un bloc try…catch, fie
să o declare cu o clauză throws:
…{
try {
metodaExceptionala( ) ;
} catch (Exception e) {…}
}
O metodă declarată ca mai sus trebuie neapărat inclusă într-un bloc try…catch, altfel se va
genera o eroare de compilare. Prin această restricţie impusă de limbaj se forţează conştientizarea
excepţiilor care ar putea să apară, deci adoptarea unui stil de programare defensiv. Se recomandă ca
metodele “periculoase” să fie declarate cu clauza throws.
try {
/** Se aşteaptă 1000 milisecunde, timp în care
firul curent de execuţie poate fi întrerupt */
Thread.sleep(1000)
} catch (InterruptedException e) {…}
try{
…
try {
} catch (Exception e) {
/** tratez şi aici… */
throw e;
}
} catch (Exception e) {
/** … şi aici */
…
}
Excepţiile pot să apară în timpul unor operaţii care necesită alocare de resurse (ex: deschidere
de fişiere). Pentru a da posibilitatea terminării “curate”, cu dezalocarea tuturor resurselor, limbajul Java
prevede clauza finally.
Codul aferent unei clauze finally va fi executat întotdeauna, indiferent de modul de părăsire a
blocului try:
normal, prin execuţia fără “evenimente” şi atingerea sfârşitului blocului try;
datorită unui break, continue sau return;
datorită unei excepţii tratate prin catch;
datorită unei excepţii netratate prin catch.
try {
…
} catch (Exceptia1) {
…
} catch (Exceptia2) {
…
} finally {
System.out.println("De mine nu scapi!");
}