Excepţii Şi Aserţiuni-Limbajul Java

8
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

description

info

Transcript of Excepţii Şi Aserţiuni-Limbajul Java

Page 1: 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

Page 2: Excepţii Şi Aserţiuni-Limbajul Java

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

Page 3: Excepţii Şi Aserţiuni-Limbajul Java

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

Page 4: Excepţii Şi Aserţiuni-Limbajul Java

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

Page 5: Excepţii Şi Aserţiuni-Limbajul Java

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

Page 6: Excepţii Şi Aserţiuni-Limbajul Java

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)

Page 7: Excepţii Şi Aserţiuni-Limbajul Java

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(); }}

}

Page 8: Excepţii Şi Aserţiuni-Limbajul Java

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);}

}