Excepţii Şi Aserţiuni-Limbajul Java
-
Upload
gabriel-minca -
Category
Documents
-
view
233 -
download
0
description
Transcript of Excepţii Şi Aserţiuni-Limbajul Java
1
6. Excepţii şi aserţiuni
2
6. Excepţii şi aserţiuni
• Tipuri excepţii• Clauza throws• Generarea excepţiilor• Clauzele try, catch şi finally• Recomandări pentru utilizarea excepţiilor• Aserţiuni
3
Introducere• Motive pentru eşuarea programelor la execuţie:
– Testare insuficientă înainte de livrare– Nu s-au anticipat la proiectare toate cazurile speciale– Fenomene din afara controlului programului: date greşite, erori hardware
• Testarea situaţiilor speciale se poate face cu construcţii condiţionale:if (! (ref.metoda()) == null){ // secv.normala}else{ // secv. de eroare}• Prea multe astfel de construcţii fac programul greu de înţeles• Se acceptă frecvent un compromis între corectitudinea (robusteţea) programului şi
claritate• Deja la C++ a apărut un mecanism care face posibilă verificarea erorilor fără a afecta
lizibilitatea: excepţiile, care, în plus, semnalează dirct erorile, fără valori speciale(fanioane)
• Java: excepţiile sunt obiecte care fac parte din contractul metodelor• Un obiect excepţie este generat în situaţia de eroare şi poate fi interceptat (în metoda
unde a fost generat sau mai sus în stiva de apeluri)• Pentru excepţii neinterceptate rezultatul este, de regulă, terminarea firului de execuţie
curent• Aserţiuni: condiţii care trebuie să fie satisfăcute într-un punct din program; dacă nu, se
semnalează eroare (prin generarea unei excepţii); depind numai de logica internă a programului
4
6.1. Tipuri excepţii (1)• Excepţiile fiind obiecte, Java are o
clasă de bază din care pot fi derivate excepţii corespunzătoare oricărei situaţii deosebite
• Clasa de bază este Throwable, derivată din Object
• Are 3 constructori:public Throwable (String message)public Throwable (String message,
Throwable cause)public Throwable(Throwable cause)• Una dintre metode:public void printStackTrace()• Două categorii de excepţii:
– Verificabile- se poate verifica la compilare că o metodă generează numai excepţii declarate în contract
– Neverificabile – erori ce nu pot fi interceptate
• Excepţiile def. de programatori într-o aplicaţie sunt obligatoriu verificabile, derivate din Exception, subclasă a lui Throwable
• Pachetele platformei Java definesc multe excepţii verificabile, ex. IOException, ClassNotFoundException, NoSuchMethodException
• Excepţiile neverificabile:– Erori– Excepţii în suportul de execuţie
• Erorile sunt derivate din clasa Error şi reprezintă condiţii anormale serioase: AssertionError, VirtualMachineError
• Excepţiile în suportul de execuţie sunt derivate din RuntimeException şi reprezintă condiţii speciale la funcţionarea normală a maşinii virtuale: NullPointerException, ClassCastException
5
6.1. Tipuri excepţii (2)
6
6.2. Clauza throws• Trebuie să conţină excepţiile verificabile
care pot fi generate într-o metodă şi nu sunt tratate în acea metodă
public void oMetoda()throws Exceptia1, Exceptia2 {
// corpul metodei}• Utilizatorii metodei sunt informaţi despre
excepţiile asupra cărora trebuie să acţioneze
• Compilatorul Java poate verifica dacă metoda generează (şi nu tratează) doar excepţiile declarate
• Este permis ca o metodă să genereze excepţii de tipuri extinse din cele declarate în clauza throws
• Nu se recomandă declararea unor tipuri generale de excepţii: se pierde informaţie importantă pentru utilizator
• Constructorii, ca şi metodele, pot genera excepţii verificabile din clauza lor throws; la fel, blocurile de iniţializare nestatice, dacă excepţiile sunt declarate în fiecare constructor
• Iniţializatorii şi blocurile de iniţializare statice nu pot genera excepţii, nici direct, nici prin apelarea unor metode
• Un utilizator al unei metode cu clauză throws poate:
– Să intercepteze excepţia şi să o trateze– Să intercepteze excepţia şi să o pună în
corespondenţă cu una din excepţiile propriei clauze throws
– Să declare excepţia în propria clauză throws
• Clauza throws a unei metode înlocuitoare a uneia moştenite sau care implementează o metodă abstractă trebuie să fie compatibilă cu clauza throws a metodei moştenite
• Explicaţie: regiunile de cod scrise pentru a utiliza metode din superclasă trebuie să rămână valide şi când la execuţie se folosesc metode înlocuitoare
• Dacă o metodă este moştenită multiplu (din superclasă şi o interfaţă), clauza metodei înlocuitoare trebuie să fie compatibilă cu toate clauzele throws moştenite
7
6.3. Generarea excepţiilor• Se foloseşte instrucţiunea throw: throw expression;• expression trebuie să conducă la o
valoare care poate fi atribuite unei referinţe Throwable; de regulă, constă în generarea unui obiect cu new
• Exemplu de utilizare:public void debitCont(double
suma)throws SoldInsuficient{ if (sold < suma)
throw new SoldInsuficient();
else sold -= suma;
}
• Aplicaţia unde apare metoda trebuie să conţină şi definiţia clasei SoldInsuficient, cel puţin sub forma:
public class SoldInsuficient{}
• Fără clauza throws, codul metodei debitCont nu ar fi acceptat de compilatorul Java
• Când se generează o excepţie, expresia sau instrucţiunea unde se produce este terminată abrupt şi controlul trece imediat la blocul sau apelul de metodă imediat anterior , până se ajunge la interceptarea excepţiei sau la terminarea firului de execuţie
• Excepţiile generate prin throw sunt excepţii sincrone, fiind produse ca rezultat al unei acţiuni directe în program; la fel sunt excepţiile neverificabile produse în suportul de execuţie (ex. la o împărţire cu zero)
• Excepţiile asincrone se produc ca rezultat al unor evenimente din afara programului, ex. erori interne în maşina virtuală Java; nu pot fi tratate în program
8
6.4. Clauzele try, catch şi finally (1)
• Cod pentru interceptarea şi tratarea unor excepţii:
try{ // instructiuni
}catch (tipexceptie1 ident1){ // instructiuni
}catch (tipexceptie2 ident2){ // instructiuni ...
}finally { // instructiuni
}
• Corpul clauzei try este executat până la terminarea cu succes sau până la generarea unei excepţii
• Dacă s-a generat excepţie, se examinează, pe rând, clauzele catch pentru a vedea dacă tipul obiectului excepţie generat poate fi atribuit tipului din clauza catch
• Dacă se poate face atribuirea, se execută blocul ataşat clauzei catch respective, restul clauzelor catch fiind omise
• O clauză try poate avea asociate oricâte clauze catch (inclusiv zero), toate cu tipuri excepţie diferite
• Dacă excepţia generată nu corespunde nici unei clauze catch, ea se va propaga spre clauze try exterioare
• Dacă este prezentă finally, obligatoriu ultima, codul ei se execută după ce celelalte prelucrări legate de try au fost terminate, indiferent dacă s-a generat sau nu excepţie
• În particular, codul lui finally se execută şi dacă înainte s-a executat o instrucţiune return sau break
• Generarea excepţiei poate avea loc direct în corpul lui try sau într-o metodă apelată din corpul lui try
9
6.4. Clauzele try, catch şi finally (2)
• O clauză catch este asemănătoare unei metode şi este prevăzută cu un parametru, excepţia interceptată
• În corpul clauzei catch apar instrucţiunile de tratare a excepţiei, dar este posibil şi să fie generată din nou excepţia, care se propagă astfel spre cod situat mai sus în stiva de apeluri
• Dacă excepţiile interceptate formează o ierarhie, cele mai specifice trebuie să apară primele (altfel nu vor mai fi examinateÎ), compilatorul Java sesizând eroare în caz contrar
• O construcţie try-catch-finally tratează o singură excepţie:dacă într-o clauză catch sau în finally se generează excepţie, va fi propagată
• Exemplu de utilizare: rescrierea metodei debitCont cu tratarea excepţiei SoldInsuficient:
public void debitCont (double suma) {
try{ if (sold < suma) throw new
SoldInsuficient(); else sold -= suma;
}catch (SoldInsuficient e){ System.out.println("Soldul "+sold+"este insuficient.");System.exit();
}}
10
6.4.1. Detalii despre finally (1)• Clauza finally se ataşează la try
dacă logica aplicaţiei cere o secvenţă de acţiuni indiferent cum se termină try: refacerea stării unui obiect, eliberarea unor resurse non-obiect (ex. fişiere)
• Există două idiomuri de programare pentru utilizarea corectă a clauzei finally, fiecare implicând două acţiuni: pre şi post
• Primul idiom: dacă apare pre, obligatoriu trebuie să apară şi post:
pre(); try { //instructiuni } finally { post(); }
• Dacă în pre se generează excepţie, nu se execută nici post
• Al doilea idiom: pre returnează o valoare, iar post trebuie să aibă loc numai dacă acea valoare arată că pre s-a terminat cu succes:
Object val = null; try { val = pre(); // alte actiuni } finally { if (val != null) post(); }
• Şi aici s-ar putea folosi secvenţa de la primul idiom, dar, prin plasarea lui pre() în blocul try se pot intercepta şi excepţiile generate de pre()
• Cu pre() în afara blocului try, ar fi fost necesar un try extern celui curent, deci s-ar lucra cu blocuri try încuibate, ceea ce ar complica structura programului
11
6.4.1. Detalii despre finally (2)• O clauză finally poate fi folosită şi
pentru acţiuni de curăţire după return, break, continue, dacă acestea se includ într-un try; astfel apar blocuri try fără catch
• Motivele intrării într-o clauză finally:– Terminarea normală a corpului lui
try– Execuţia unei instrucţiuni de
control a fluxului (return, break etc)
– Generarea unei excepţii în corpul lui try
• Motivul intrării rămâne valid dacă terminarea clauzei finally se face normal; altfel, motivul iniţial este înlocuit cu cel din finally
• Exemplu:class FinallyTest{ public static int
tryReturn(){ try{ return 1; } finally { return 2; }
} public static void
main(String[] args){ System.out.println(
"Valoarea returnata:“ +tryReturn()); }}• Programul va tipări valoarea 2, deşi în
blocul try s-a executat return 1;
12
6.5. Recomandări pentru utilizarea excepţiilor
• Excepţiile trebuie să indice condiţii neaşteptate în execuţia unui program, nu condiţii speciale, dar previzibile
• Exemplu: la citirea unui fişier este previzibil să se ajungă la sfârşitul fişierului, dar e neaşteptat să se citească dincolo de sfârşit
• Alt exemplu: parcurgerea unui tablou se face cu:for (int i = 0; i <tab.length; i++){
//operatii cu elementele tabloului}nu cu: try {
int i = 0;while (true){ //operatii cu elementele tabloului
} catch (IndexOutOfBoundsException e) {}
• Se preferă excepţii verificabile pentru situaţiile în care se poate interveni prin program pentru corecţii şi cele neverificabile pentru erori unde nu e posibilă (sau necesară) intervenţia
• Pentru excepţiile verificabile se recomandă metode în clasa excepţiei care să furnizeze informaţii utile pentru tratare
• Se recomandă utilizarea excepţiilor neverificabile predefinite în pachetele Java (se reduce numărul de clase din aplicaţii)
13
6.6.Aserţiuni6.6.1. Sintaxa şi semantica aserţiunilor (1)
• Aserţiuni: expresii ce reprezintă condiţii considerate ca trebuind să fie adevărate în anumite puncte de program
• Dacă o astfel de condiţie nu e satisfăcută, se semnalează eroare la execuţie
• Două forme sintactice pentru instrucţiunea assert:
assert expression1; assert expression1:
expression2;expression1: expresie cu valoare
booleanăexpression2: orice expresie care are
valoare (nu poate fi apel de metodă care returnează void)
• Dacă la execuţia lui assert valoarea expression1 este true, execuţia continuă normal, altfel se generează excepţia neverificabilă AssertionError
• Excepţia AssertionError poate fi interceptată în program; dacă nu, se va afişa un mesaj de forma:
Exception in thread “main” java.lang.AssertionError at ProgramName.main(ProgramName.java:23)
• Dacă este prezentă expression2, reprezentarea ei ca String se adaugă după AssertionError în mesajul de eroare (explică mai complet natura erorii)
• Exemplu:public class Assert{ public static void main(String[]
args){double number = Double.parseDouble(args[0]);assert number >= 0: "numar < 0";System.out.print("Radical din: " + number + " este: ");System.out.println(Math.sqrt(number))
}}
14
6.6.Aserţiuni6.6.1. Sintaxa şi semantica aserţiunilor (2)
• Rularea programului în forma: java Assert -7conduce la rezultatul: Radical din: -7 este: NaN• Dacă programul este rulat cu:java –ea Assert -7rezultatul va fi:Exception in thread “main”
java.lang.AssertionError: numar < 0 at Assert.main(Assert.java:4)
• Obs. Aserţiunile sunt implicit dezautorizate în maşina virtuală Java
• Chiar dacă AssertionError este interceptată într-o metodă, ca mai jos, este necesară autorizarea explicită a aserţiunilor
public class AssertTC{ public static void main(String[]
args){ double number = Double.parseDouble(args[0]); try{ assert number >= 0: "numar < 0"; System.out.print("Radical din: " +
number + " este: "); System.out.println(Math.sqrt(nu
mber)); }catch(AssertionError e){ System.out.println("Eroare:
numar negativ"); e.printStackTrace(); }}
}
15
6.6. Aserţiuni6.6.2. Utilizarea aserţiunilor
• Aserţiunile permit programare defensivă: reducerea efectului erorilor chiar când nu e cunoscută localizarea acestora
• Se folosesc în mod normal numai pentru faza de testare a unei aplicaţii nu trebuie să aibă efecte secundare
• Trei forme principale de utilizare:– La începutul unei metode, pentru a verifica dacă e apelată conform contractului (precondiţie)– La sfârşitul unei metode, pentru a verifica dacă rezultatul e plauzibil (postcondiţie)– În corpul unei metode, pentru a verifica dacă anumite proprietăţi sunt afectate (invarianţi)
• Variantă de program cu postcondiţie:public class AssertPrePost{
public static void main(String[] args){ double number = Double.parseDouble(args[0]); assert number >= 0: "numar negativ"; double rez = Math.sqrt(number); assert Math.abs(rez*rez - number) <= 1e-6 : "imprecis"; System.out.print("Radical din: " + number + " este: "); System.out.println(rez);}
}