CATEGORII DOCUMENTE |
Exceptii program in Java
O exceptie program este o situatie exceptionala aparuta la executia unui program si care poate avea cauze hardware (un defect disc, de ex.) sau software (impartire prin zero, de ex.).
Exceptiile pot fi privite si ca evenimente, previzibile, ce se pot produce in anumite puncte din program si care au ca efect intreruperea cursului normal al programului. Acestea sunt evenimente interne, sincrone cu executia programului, spre deosebire de evenimentele externe, produse de cauze externe programului (actionarea unei taste sau a unui buton de 'mouse').
Exceptiile program uzuale sunt:
- Exceptii aritmetice: impartire prin zero, depasire domeniu de valori pentru tipul numeric respectiv, etc.
- Exceptii de adresare: utilizarea unui pointer nul pentru obtinerea unor date sau pentru apelarea unor functii.
- Exceptii la utilizare de vectori: indici in afara limitelor (in afara memoriei alocate pentru vector).
- Exceptii la operatii de intrare-iesire: deschidere fisier inexistent, citire imposibila, pozitionare in afara fisierului, etc.
Programarea orientata pe obiecte a facut necesara, dar si posibila, o noua abordare a tratarii exceptiilor program.
Posibilitatea de a defini noi tipuri de date si de noi operatii asociate acestor tipuri a condus la marirea diversitatii tipurilor de exceptii care pot apare. De exemplu, incercarea de extragere dintr-o stiva goala poate produce o exceptie, care nu este neaparat o eroare fatala.
Pe de alta parte, posibilitatea de a avea mai multe functii cu acelasi nume, dar cu argumente diferite si de a selecta o functie dupa tipul argumentelor si nu dupa numele sau a creat si premizele acestui nou mod de tratare a exceptiilor program.
Tratarea exceptiilor in C++ si in Java este foarte asemanatoare, dar nu identica. Formal, se folosesc aceleasi cuvinte cheie in ambele limbaje: throw, throws, try si catch.
1 Tratarea exceptiilor in programarea procedurala
In limbajele procedurale (C, Pascal, Fortran) principalele modalitati de tratare a exceptiilor program sunt:
a) Nici o actiune explicita din partea programului, deoarece programatorul considera ca o astfel de eroare nu va apare in program, sau se bazeaza pe tratarea implicita a unor erori grave prin terminarea programului dupa afisarea unui mesaj de eroare. Exemplu:
a= new int[n]; // aici poate apare o exceptie de alocare
for (i=0;i<n;i++) a[i]=i;
b) Verificarea printr-o instructiune 'if' sau 'while' a conditiei de exceptie. Exemplu:
if ( (a=new int[n])== NULL )
printf ('Memorie insuficienta !');
else
c) Utilizarea macroinstructiunii 'assert' (in C si C++):
a= new int[n];
assert (a != NULL);
for (i=0;i<n;i++) a[i]=i;
d) Utilizarea unor optiuni de compilare, pentru a cere compilatorului sa insereze instructiuni suplimentare de verificare a unor conditii (in Turbo-Pascal, de exemplu).
S-a observat ca programatorii au tendinta de a neglija tratarea tuturor exceptiilor ce pot apare la executie si din cauza instructiunilor ce trebuie adaugate programului, care complica programele si le fac mai greu de inteles. Chiar si atunci cand anumite functii de biblioteca raporteaza prin rezultatul lor modul de terminare (normal/anormal), exista tendinta de a ignora rezultatul functiei si de apelare a lor ca pe functii de tip void (ceea ce este corect sintactic in C, C++, Java si in Borland-Pascal).
Una din problemele asociate exceptiilor program este aceea ca exceptia poate apare intr-un subprogram (scris de o persoana), iar tratarea ei se face in programul apelant (scris de o alta persoana). Altfel spus, o functie de biblioteca stie cand poate apare exceptia dar nu stie cum sa o trateze, iar programul client stie cum trebuie tratata exceptia.
Un exemplu tipic este exceptia care apare la incercarea de a deschide un fisier, atunci cand fisierul specificat nu exista (nu a fost gasit in directorul indicat explicit sau implicit). Un program poate decide in acest caz una din urmatoarele actiuni:
- Terminarea programului
- Repetarea citirii numelui fisierului si a operatiei de deschidere
- Crearea unui nou fisier cu numele respectiv (un alt fel de deschidere).
2 Exceptii program in programarea cu obiecte
Motivatia introducerii unui nou mecanism de tratare a exceptiilor program in POO poate fi rezumata astfel:
- Pentru separarea instructiunilor necesare tratarii exceptiilor de instructiunile care realizeaza algoritmul de prelucrare specific aplicatiei.
- Pentru tratarea uniforma a diferitelor exceptii, in toate programele scrise intr-un anumit limbaj.
- Pentru a permite tratarea unei exceptii nu numai in blocul care a aparut exceptia, dar si intr-un bloc ierarhic superior. Exceptiile se propaga 'in sus' pe un lant de functii care se apeleaza unele pe altele.
- Pentru a obliga programatorul sa trateze anumite exceptii si a nu-l lasa sa omita tratarea unor erori.
In POO semnalarea unei exceptii program inseamna crearea unui obiect de un anumit tip clasa (derivat din clasa Exception) si transmiterea lui catre sistemul care asista executia ('Runtime system'). Acest sistem este interpretorul (masina virtuala) Java.
Pentru erori uzuale (cum sunt cele asociate operatiilor de I/E) sunt predefinite o serie de tipuri exceptie, dar utilizatorii pot sa-si defineasca propriile tipuri exceptie, sub forma unor noi clase (derivate si ele din clasa Exception).
Inainte de a prezenta elementele de limbaj introduse pentru tratarea exceptiilor, trebuie spus ca uneori este posibila anticiparea si prevenirea unor exceptii. Exemplul urmator arata cum se poate evita aparitia unei exceptii de iesire din limitele unui vector intrinsec, din cauza utilizarii gresite a unui program Java:
// program cu doua argumente in linia de comanda
public static void main ( String arg[])
. . . // copiaza fisierul cu numele in arg[0] intr-un alt fisier cu numele arg[1]
}
Un al doilea exemplu in care se previne aparitia unei exceptii este cel al unei functii equals redefinita intr-o clasa oarecare si care primeste un argument de un alt tip decat tipul clasei care contine metoda. Compararea a doua obiecte de tipuri diferite nu va produce o exceptie ci doar un rezultat false pentru functia equals :
class Arc implements Comparable
}
Cuvantul cheie 'throws' ('to throw'= a arunca) este folosit pentru semnalarea unei exceptii de catre o metoda. Alegerea acestui cuvant se explica prin aceea ca la detectarea unei exceptii nu se apeleaza direct o anumita functie (selectata prin nume); functia care va trata exceptia (numita 'Exception Handler') este selectata dupa tipul obiectului exceptie 'aruncat' unui grup de functii (dupa tipul argumentului functiei). Aceste functii se pot afla pe diferite nivele ierahice in program.
Functiile de tip 'exception handlers' sunt introduse prin cuvantul cheie 'catch' ('to catch'= a prinde, a apuca), care este urmat (intre paranteze) de un argument de un tip exceptie. Un tip exceptie este fie un tip clasa predefinit, fie un tip clasa definit de programator (parte din familia claselor exceptie).
Asocierea dintre diferite obiecte exceptie aruncate si functiile catch se face dupa tipul argumentului catch. Este ca si cum toate functiile de tratare a exceptiilor ar avea numele catch, dar se deosebesc intre ele prin tipul argumentului.
Cuvantul cheie catch este parte dintr-un bloc introdus prin cuvantul cheie try, folosit in acest context cu sensul de 'verifica' instructiunile care urmeaza daca produc exceptii.
Forma blocului try-catch este urmatoarea:
try catch (exceptie1 e1) catch (exceptie2 e2) // secvente ptr alte tipuri de exceptii
O secventa de tratare a exceptiilor, introdusa prin catch, este activata nu numai de erorile produse direct in blocul try, dar si de erorile produse indirect, in alte metode apelate din cadrul blocului try.
Cuvantul cheie trows apare in antetul unei metode, dupa lista de argumente si este urmat de numele unui tip exceptie (nume de clasa) sau de numele mai multor tipuri exceptie. Se considera ca erorile semnalate de o metoda fac parte din interfata publica a metodei, alaturi de numele ei, de tipul ei si de lista argumentelor formale. Exemplu:
public static FileReader open ( String filename) throws IOException
Cuvantul cheie throw introduce o noua instructiune, care poate apare intr-o instructiune if sau intr-o alta instructiune care verifica o conditie de eroare. Exemplu:
public Object pop () throws EmptyStackException
Instructiunea throw are un efect asemanator cu instructiunea go to, deoarece are ca efect o iesire definitiva din functia care o contine, dar nu produce un salt la functia apelanta ci la o functie 'catch', care are un argument de tip 'EmptyStackException'. Daca nu exista nici o astfel de functie, atunci sistemul 'runtime' afiseaza un mesaj si termina programul.
De fapt, catch are statut de instructiune si nu de functie, deoarece dupa executarea instructiunilor introduse prin 'catch' (daca exista) se trece la instructiunea urmatoare blocului try si nu se revine in punctul din blocul try unde s-a semnalat exceptia.
Tipurile exceptie pot fi clasificate in:
- Tipuri exceptie predefinite ( de ex. IOException)
- Tipuri exceptie definite de programator.
In Java, toate tipurile exceptie trebuie sa fie subclase ale clasei Exception, subclasa a clasei Throwable.
3 Tipuri exceptie in Java
Exceptiile Java sunt de doua categorii:
- Exceptii care nu necesita interventia programatorului (numite 'Runtime Exceptions'), dar care pot fi interceptate si tratate de catre programator. Daca nu sunt tratate, aceste exceptii produc afisarea unui mesaj referitor la tipul exceptiei si terminarea fortata a programului.
- Exceptii care trebuie fie tratate, fie 'aruncate' mai departe, pentru ca altfel compilatorul marcheaza cu eroare functia in care poate apare o astfel de eroare ('Checked Exceptions'= exceptii a caror tratare este verificata de compilator).
De exemplu, functia 'open' de mai sus produce o astfel de eroare daca nu contine clauza 'throws', deoarece apeleaza constuctorul clasei FileReader, care 'arunca' o exceptie
de intrare-iesire, de tip FileNotFoundException (derivat din tipul mai general IOException).
Exceptiile care nu necesita interventia programatorului au fost alese dupa urmatoarele criterii:
- Ele pot apare in multe situatii de programare, iar tratarea lor ar complica codul programului (de exemplu, incercarea de folosire a unei variabile referinta neinitializate si care contine null);
- Ele nu pot fi recuperate si nu permit continuarea programului (sunt erori fatale).
Printre exceptiile Java care nu trebuie tratate de programator, dar se manifesta la executia programului, sunt: erori de indexare a elementelor unui vector (indice in afara limitelor), utilizarea unei variabile referinta ce contine null (pointer care nu contine o adresa valabila)
pentru referirea la date sau metode publice.
Exceptiile care necesita o actiune din partea programatorului sunt cele care apar mai rar si care se preteaza la o recuperare din eroare; un exemplu tipic il constitie erorile la operatii de intrare-iesire.
O metoda definita de programator si care apeleaza o metoda a unei clase din pachetul 'java.io' (sau orice alta metoda care arunca exceptii) trebuie fie sa semnaleze mai departe
posibilitatea aparitiei acestei exceptii (prin clauza throws), fie sa trateze exceptia printr-un bloc 'try-catch' care sa includa apelul metodei de biblioteca (deschidere fisier, citire din fisier, pozitionare in fisier).
Programul propus ca exemplu creeaza un fisier de numere (reprezentate in binar), pe care apoi il citeste si il afiseaza. Ca fisier se foloseste un obiect de tipul RandomAccessFile.
La citirea unui fisier binar de numere problema detectarii sfarsitului de fisier se pune altfel decat la citirea dintr-un fisier text. Metoda 'readLine()' are rezultat null la incercarea de citire dupa sfarsitul fisierului, dar metoda 'readInt()', ca si alte metode de citire a unor numere, poate avea ca rezultat orice numar binar pe 32 de biti, deci nu poate raporta prin rezultatul ei eroarea de citire dupa sfarsitul fisierului.
O solutie este sa determinam lungimea fisierului (in octeti), folosind metoda 'length()', si apoi sa calculam cate numere intregi (sau de alt tip) se afla in fisier.
Fiecare din metodele clasei RandomAccesFile folosite in acest program semnaleaza o exceptie de tip IOException.
Prima varianta a acestui program nu trateaza nici o exceptie si toate exceptiile sunt transmise (aruncate) mai departe sistemului 'runtime'.
Se va observa cum fiecare metoda in care poate apare o exceptie trebuie sa o semnaleze mai departe, deoarece in caz contrar apar erori la compilare.
// detectare sfirsit de fisier binar
import java.io.* ;
class Eof
// deschidere fisier existent
public static RandomAccessFile reset ( String fname )throws IOException
public static void main (String[] arg)throws IOException
}
A doua varianta a acestui program trateaza toate exceptiile de I/E, inclusiv exceptia care apare la incercarea de citire dupa sfarsitul fisierului (de tip EOFException, subclasa a lui IOException).
Toate exceptiile de tip IOException sunt oprite in metodele statice ale acestui program, deci nu este semnalata mai departe nici o exceptie.
// tratare exceptii de I/E la fisiere binare
import java.io.* ;
class ExcEof catch (IOException e)
return f;
}
// deschidere fisier existent
public static RandomAccessFile reset ( String fname ) catch (IOException e)
return f;
}
public static void main (String[] arg) catch (IOException e)
if ((f=reset ('numere.dat'))==null)
return;
// citire fisier
try
catch (IOException e)
}
}
4 Tehnici de programare a exceptiilor in Java
Faptul ca exista tipuri exceptie de I/E care sunt subtipuri ale tipului IOException creeaza posibilitatea unei separari a exceptiilor chiar prin tipul argumentului unei instructiuni catch.
Consecinta directa este aceea ca programatorul are de ales intre o secventa de tratare a exceptiilor mai generala, sau mai multe secvente de tratare a unor exceptii specifice.
Sa luam ca exemplu ultimul bloc try din functia 'main' a programului anterior. In cadrul acestui bloc se pot produce trei exceptii: eroare la citire din fisier, detectare sfarsit de fisier sau eroare la inchiderea fisierului. Indiferent de tipul exceptiei, tratarea este aceeasi si
anume terminarea programului fara nici un mesaj explicativ; de fapt, nu se executa nici o instructiune in blocul catch, dar se trece apoi mai departe la acolada de sfarsit a functiei 'main' (unde compilatorul adauga o instructiune return pentru iesire din 'main').
O solutie simpla pentru ca utilizatorul programului sa fie informat despre modul de terminare este afisarea stivei de apeluri prin care s-a ajuns la exceptia respectiva. Exemplu:
try
catch (IOException e)
Totusi, cel mai probabil este ca s-a produs exceptia de sfarsit normal de fisier (la citire), iar un mesaj ar putea induce in eroare pe cel care foloseste programul si care ar putea crede ca s-a produs o eroare.
O solutie preferabila este sa se scrie secvente catch separate pentru fiecare tip de exceptie. Exemplu:
try
catch ( EOFException e)
catch (IOException e)
In exemplul anterior este foarte importanta ordinea celor doua clauze catch, deoarece exceptia de tip EOFException este o subclasa a exceptiei mai generale IOException, iar inversarea ordinei va face imposibila separarea exceptiei 'sfarsit de fisier'. Clauzele catch sunt examinate in ordinea aparitiei lor la sfirsitul blocului try, iar dupa selectarea unui catch nu se mai continua examinarea clauzelor catch care urmeaza.
Ramane problema separarii erorii la citire de eroarea la inchidere, deoarece ambele sunt de acelasi tip IOException. Solutia este cate un bloc try pentru fiecare din cele doua instructiuni care pot produce acelasi tip de exceptie. Exemplu:
try
catch ( EOFException e)
catch (IOException e)
try
catch (IOException e)
Un bloc try cu mai multe instructiuni catch este necesar atunci cand o singura instructiune poate produce mai multe tipuri de exceptii sau cand vrem sa reducem numarul de blocuri try dintr-un program. Exemplu:
// comanda 'sort' din Unix
import java.io.*;
class FileSort
catch (ArrayIndexOutOfBoundsException e)
catch (IOException e)
// citire din fisier, sortare, afisare
}
}
Blocul try poate contine, dupa toate clauzele catch si o clauza finally, care reuneste instructiuni ce se vor executa dupa blocul try, indiferent daca au aparut sau nu exceptii in blocul 'try'. Exemplu:
// comanda 'sort' din Unix
import java.io.*;
class FileSort
catch (ArrayIndexOutOfBoundsException e)
catch (IOException e)
finally
// citire din fisier, sortare, afisare
}
La producerea unei exceptii intr-un bloc try se sare la secventa catch fara posibilitatea de revenire in blocul try, deci nu se incearca o recuperare din situatia de eroare. Daca dorim sa facem mai multe incercari ale unei operatii, pana cand nu mai apare exceptie la operatia respectiva, atunci putem include blocul try intr-un ciclu de repetare a operatiei. De obicei se prevede si un numar maxim de incercari (de repetari), pentru ca programul sa nu ramana 'agatat' in secventa de incercari repetate.
Exemplul urmator este o functie pentru deschiderea in citire a unui fisier existent, cu repetarea solicitarii numelui fisierului (de maxim 3 ori), daca fisierul cu numele specificat nu a fost gasit.
// deschidere fisier existent
public static RandomAccessFile reset ( ) catch (IOException e)
finally // try
return null; // daca s-a incercat de 3 ori fara succes
}
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 1340
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved