Sunteți pe pagina 1din 14

Exceptii

Aplicatiile intalnesc erori la executie. Aplicatiile robuste trebuie sa manipuleze erorile cat mai
elegant cu putinta. Cateva motive de aparitie a erorilor:
- Aplicatia nu are comportamentul asteptat
- rezultat al unui bug
- factori din spatele aplicatiei: conexiune la baza de date intrerupta, functionarea driver-
elor, etc
In Java, atunci cand folosim resurse externe, compilatorul cere sa manipulam exceptiile ce pot
aparea (spre ex: java.nio, java.sql). A manipula o exceptie inseamna sa adaugam in cod
un bloc de manipulare a erorii (try-catch). A declara o exceptie inseamna ca declaram ca o
metoda poate esua in executie sa (clauza throws).
O exceptie reprezinta, asadar, un eveniment ce intrerupe executia normala a unui program.
Cand se intalneste o exceptie la rulare, metoda in care a aparut exceptia creaza un obiect
exceptie. Metoda poate arunca exceptia inapoi catre metoda apelanta, care probabil o va trata.
Obiectul exceptie include informatii despre exceptie, cum ar fi metoda in care exceptia a
aparut si cauza exceptiei. Metoda apelanta poate trimite exceptia, mai departe, unei alte
metode, care la randul sau a apelat-o. Daca nicio metoda nu manipuleaza exceptia, programul
se termina cand obiectul exceptie atinge metoda main().
Cand apare o exceptie o putem preveni trimitand-o in stiva de apel sau tratand-o, astfel
aplicatia continundu-si executia pe o alta cale.
Blocul try cuprinde codul ce poate cauza o exceptie. Acest bloc este numit si cod protejat
(protected code). Un bloc try este urmat de zero sau mai multe blocuri catch ce specifica
tipurile de exceptie ce pot fi prinse.
catch ar trebui sa fie folosit pentru:
- a reincerca executia unei operatii
- incercarea de a executa o alta operatie
- o iesire eleganta sau apelul lui return
Scrierea unui bloc catch vid este o bad practice.
Blocurile catch se executa in ordinea in care au fost scrise, de sus in jos, pina la intilnirea
primei exceptii ce se potriveste ca tip cu cel al tipului aruncat. Cel mult un bloc catch se
executa. De aceea, vom plasa blocul catch, cel mai general, pe ultima pozitie in blocurile din
sirul blocurilor catch. Daca nu se arunca nicio exceptie atunci nici un bloc catch se executa.
Blocul finally este optional, daca try este urmat de macar un catch. Blocul finally se
executa indiferent daca avem sau nu aruncata exceptie, cu o singura exceptie, atunci cand
executam System.exit.
// sintaxa de baza a lui try
try {
  // cod ce poate arunca o exceptie
}
catch (TipExceptie1 Identificator1) {
  // cod de manipulare a exceptiei
}
catch (TipExceptie2 Identificator2) {
  // cod de manipulare a exceptiei
}

finally {
  // 0 sau 1 clauze finally
}
Cand deschidem resurse precum fisiere sau conexiuni la baza de date, trebuie intotdeauna sa
le inchidem. Incercarea de a le inchide in try poate fi problematica deoarece putem incheia
blocul sarind operatia. De aceea este recomandata inchiderea acestora in blocul finally.
Uneori operatia pe care o executam in blocul finally poate genera o exceptie. In acest caz va
trebui sa scrie un bloc try-catch in interiorul lui finally. Putem avea try-catch si in
interiorul lui try sau al lui catch.
public class FinallyExampleMain {
public static void main(String[] args) {
InputStream in = null;
try {
System.out.println("About to open a file");
in = new FileInputStream("missingfile.txt");
System.out.println("File open");
int data = in.read();
} catch (FileNotFoundException e) {
System.out.println(e.getMessage());
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
System.out.println("Failed to close file");
}
}
}
}
Toate exceptiile si erorile Java sunt subclase ale clasei Throwable din pachetul java.lang.
Putem loga exceptiile ce apar pe parcursul aplicatiei folosind Apache Log4j si framework-uri
predefinite din java.util.
In Java SE7 avem la dispozitie un try cu resurse, ce va inchide automat resursele. Astfel
eliminam folosirea lui finally. Orice clasa ce implementeaza java.lang.AutoCloseable
poate fi utilizata ca resursa. Pentru ca o resursa sa fie automat inchisa, referinta sa trebuie
declarata in interiorul parantezelor rotunde ale lui try.
public class FinallyExampleMain {
public static void main(String[] args) {
System.out.println("About to open a file");
try(InputStream in = new FileInputStream("missingfile.txt")) {
System.out.println("File open");
int data = in.read();
} catch (FileNotFoundException e) {
System.out.println(e.getMessage());
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
Putem deschide mai multe resurse daca ele sunt separate prin punct si virgula. Inchiderea lor
se va face in ordine inversa deschiderii.
Daca o exceptie apare in momentul crearii unei resurse AutoCloseable, controlul va fi
preluat imediat de blocul catch.
Daca o exceptie apare in blocul try, toate resursele vor fi inchise inainte de a rula catch.
Daca o exceptie va fi generata in timpul inchiderii resursei, ea va fi suprimata. Controlul va fi
preluat de un bloc catch. Exceptiile ar putea fi afisate astfel:
public class FinallyExampleMain {
public static void main(String[] args) {
System.out.println("About to open a file");
try (InputStream in = new FileInputStream("missingfile.txt")) {
System.out.println("File open");
int data = in.read();
} catch (Exception e) {
System.out.println(e.getMessage());
for (Throwable t : e.getSuppressed())
System.out.println(t.getMessage());
}
}
}
Resursele din try cu resurse trebuie sa implementeze unul dintre:
- java.lang.AutoCloseable, care poate arunca un Exception
- java.io.Closeable, care extinde AutoCloseable si care poate arunca o
IOException
Interfata AutoCloseable are o singura metoda void close() throws Exception;
Metoda close() din AutoCloseable, spre deosebire de cea din Closeable, nu este
idempotenta. Aceasta inseamna ca apelul sau mai mult decat o data poate avea efecte
secundare. Cei care implementeaza metoda din AutoCloseable vor trebui sa o faca
idempotenta
Java SE7 furnizeaza o clauza multi-catch. Avantajele deriva din faptul ca uneori dorim sa
facem anumite actiuni independent de exceptia ce este generata. Multi-catch reduce cantitatea
de cod scris, prin eliminarea necesitatii scrieri mai multor clauze catch cu acelasi
comportament. Un alt avantaj este acela ca face mai putin probabila incercarea de a prinde o
exceptie generica. Alternativele de tip, separate prin bara verticala, nu trebuie sa aiba o relatie
de mostenire.
public class FinallyExampleMain {
public static void main(String[] args) {
System.out.println("About to open a file");
try (InputStream in = new FileInputStream("missingfile.tx");
Scanner s = new Scanner(in);) {
System.out.println("File open");
int data = s.nextInt();
} catch (NoSuchElementException | IOException e) {
System.out.println(e.getClass().getName());
System.out.println("Quiting");
}
}
}
Numai un obiect Throwable poate fi aruncat, si acesta include exceptii sau erori de sistem.
Metodele din Throwable sunt:
 getMessage(), ce returneaza un string cuprinzand mesajul de eroare al obiectului
Throwable
 toString(), returneaza o descriere a obiectului exceptie incluzand si tipul exceptiei
 initCause(), seteaza cauza exceptiei care este intotdeauna un alt obiect Throwable,
ceea ce permite inlantuirea exceptiilor
 printStackTrace(), permite identificarea metodei ce a aruncat o anumita exceptie
Ierarhia Throwable este urmatoarea:
Clasa Throwable are doua subclase directe:
 Error, si clasele derivate din ea sunt folosite pentru erori de sistem sau erori la
compilare ce nu pot fi tratate de aplicatie, ca spre ex.
ExceptionInInitializerError, StackOverflowError si NoClassDefFoundError
 Exception, si clasele derivate din ea sunt utilizate pentru implementarea exceptiilor
pe care o aplicatie se asteapta sa le intilneasca. Fiecare subclasa a lui Exception
reprezinta un tip particular de exceptie
Clasa RuntimeException si subclasele sale descriu obiectele exceptie ce sunt aruncate
automat de JVM, la rulare. Exceptiile la rulare sunt generate, in general, de bug-uri in codul
sursa. Printre subclasele acesteia amintim:
 ArithmeticException, pentru exceptiile ce cuprind incalcarea regulilor matematice,
cum ar fi impartirea la zero
 IndexOutOfBoundsException, cand indexul unui string sau array nu este in domeniul
acceptat
 NegativeArraySizeException, cand incercam sa cream un sir cu numar negativ de
elemente
Pentru a prinde exceptiile la rulare putem folosi blocurile try-catch, dar acest lucru nu este
obligatoriu. Daca try-catch nu este prezent, toate exceptiile la rulare vor fi considerate
exceptii neverificate (unchecked exceptions). Aceasta inseamna ca compilatorul nu verifica
daca ele au fost declarate sau manipulate.
Toate exceptiile ce mostenesc Exception dar nu mostenesc RuntimeException sunt
cunoscute ca clase exceptie verificate. Acestea trebuie declarate intr-o metoda folosind clauza
throws sau tratate explicit in corpul metodei. Altfel se va genera eroare la compilare. Din
categoria acestor exceptii amintim:
 ClassNotFoundException, este aruncata atunci cand un program incearca sa incarce
o clasa, folosind numele ei, dar definirea clasei cu numele specificat nu este gasita.
Aceste exceptii apar atunci cand un program utilizeaza metoda forName() din Class
sau findSystemClass() sau loadClass() din ClassLoader
 InterruptedException, aruncata atunci cand un fir a fost inactiv pentru mult timp si
un alt fir ce foloseste metoda interrupt() din Thread incearca sa-l intrerupa
 IllegalAccessException, aruncata atunci cand executia metodei curente nu are
acces la definitia unui camp pe care doreste sa-l acceseze sau modifice sau la definitia
unei metode pe care doreste sa o invoce
 FileNotFoundException, apartine pachetului java.io si este aruncata atunci cand
aplicatia nu poate deschide un fisier specificat (fisier inaccesibil sau inexistent).
Exceptiile pot fi tratate in interiorul metodei sau in metoda sa apelanta. Cand o exceptie a fost
aruncata trebuie sa implementam interfata ExceptionListener ce receptioneaza doar
exceptii si apeleza metoda exceptionThrown().
Pentru a ne asigura ca o metoda poate arunca exceptii utilizam cuvantul rezervat throws cand
declaram metoda. In clauza throws specificam tipurile de exceptii ce pot fi aruncate, tipuri
separate prin virgula.
In metoda apelanta exceptia poate fi tratata sau trimisa metodei apelante a acesteia adaugand
clauzei throws exceptiile netratate.
Daca o metoda suprascrie o metoda din clasa de baza ea poate arunca numai aceleasi exceptii
ca si metoda din clasa de baza, o submultime a lor sau subclase ale exceptiilor. Metoda
suprascrisa nu poate arunca exceptii ce sunt superclase ale claselor exceptie ale metodei din
clasa de baza, sau exceptii ce apartin unei alte ierarhii. Aceste reguli se aplica numai
exceptiilor verificate.
Fie urmatorul exemplu:
public class ThrowsMain {
public static void main(String[] args) {
try {
int data = readByteFromFile();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}

public static int readByteFromFile() throws IOException {


try (InputStream in = new FileInputStream("a.txt")) {
System.out.println("File open");
return in.read();
}
}
}
Nu am utilizat catch in definitia metodei readByteFromFile(). Rolul lui try este doar acela
al destionarii resurselor. Tot in semnatura metodei am declarat aruncarea lui IOException,
fara a specifica explicit aruncarea lui FileNotFoundException. Aceasta datorita faptului ca
cele doua clase sunt ierarhice. Totusi, este o buna practica sa le aruncam pe ambele.
O aplicatie Java SE trebuie sa-si manipuleze exceptile inainte de a fi aruncate de main().
main() este posibil sa arunce exceptii, dar acest lucru este recomandat a fi evitat.
Putem rearunca o exceptie ce deja a fost prinsa.
public static int readByteFromFile() throws IOException {
try (InputStream in = new FileInputStream("a.txt")) {
System.out.println("File open");
return in.read();
}
catch(IOException e){
System.out.println(e.getMessage());
throw e;
}
}
Java SE7 suporta rearuncarea precisa a tipului exceptie. Urmatorul exemplu nu s-ar compila
in Java SE6 pentru ca catch primeste o Exception, dar arunca o IOException.
public static int readByteFromFile() throws IOException {
try (InputStream in = new FileInputStream("a.txt")) {
System.out.println("File open");
return in.read();
}
catch(Exception e){
System.out.println(e.getMessage());
throw e;
}
}
Putem crea clase exceptii custom prin extinderea clasei Exception sau a uneia dintre subclase
ei.
package exceptions;

public class DAOException extends Exception {


private static final long serialVersionUID = 1L;

public DAOException() {
super();
}

public DAOException(String message) {


super(message);
}
}
Exceptiile custom nu sunt aruncate niciodata de clasele standard Java. Sta in priceperea
dezvoltatorului aruncarea lor.
Clasa exceptie standard poate suprascrie metode sau adauga noi functionalitati. Deoarece
exceptiile captureaza informatii despre o problema aparuta va trebui sa adaugam campuri si
metode dependente de tipul de informatie pe care dorim sa-l capturam. Daca un string poate
captura toate informatiile necesare putem utiliza metoda getMessage() pe care toate clase
exceptie o mostenesc din Throwable. Orice constructor de exceptie ce primeste un string il va
stoca, astfel incat sa fie returnat cu getMessage().
Java permite ca exceptiile sa fie create si aruncate:
 implicit, exceptiile sunt create si aruncate automat de catre JVM, la rulare
 explicit, exceptiile sunt create explicit prin specificarea in cod ca un anumit tip de
obiect exceptie trebuie sa fie creat si aruncat atunci cand o anumita situatie este
intalnita. Acest tip de tratare este folositor daca dorim ca metoda sa trimita exceptia
metodei apelante, dar in anumite circumstante. Pentru a arunca o exceptie explicit se
utilizeaza cuvantul rezervat throw, pentru orice obiect ce este o instanta sau o subclasa
a lui Throwable. Noul obiect exceptie trebuie sa fie creat in aceeasi linie unde se afla
throw. Aceasta deoarece urma stivei de apel arata unde obiectul exceptie a fost creat si
nu unde a fost aruncat. Cand cream si aruncam o exceptie explicit putem adauga
propriul mesaj de eroare. Aceasta este posibil deoarece clasa Throwable are un
constructor cu argument string. Mesajul customizat va fi afisat cand aplicatia se
termina.
In versiunile de dinainte de 1.4 era dificil de comunicat cauzele unui esec peste doua sau mai
multe layere abstracte ale aplicatiei. O solutie era trimiterea exceptiei de la un layer la altul.
Aceasta crea o dependenta intre layere care trebuiau sa fie distincte. Aceasta solutie este
dezavantajoasa. Alta solutie ar fi extinderea clasei Exception pentru a asocia o cauza (cause)
Throwable unei exceptii particulare, dar si aceasta solutie este inacceptabila atunci cand avem
nevoie sa folosim altundeva API-ul.
Inlantuirea exceptiilor incepand cu versiunea 1.4, Java ne permite sa pastram urma exceptiei
originale ce a fost aruncata. Aceasta se face pe baza proprietatii cause si a inca doi
constructori ai clasei Throwable ce admit cause ca parametru. Setarea cauzei directe a
fiecarei exceptii aruncate determina posibilitatea urmaririi complete a istoriei exceptiei. O
singura exceptie contine lantul exceptiilor cu care se afla in legatura. Cauza unui obiect
Throwable este o referinta la un alt obiect Throwable care a cauzat aruncarea primului.
Proprietatea cause nu poate fi accesata direct, dar poate fi setata respectiv accesata prin:
 initCause() sau printr-un constructor al clasei Throwable
 getCause()
Exista patru constructori ai clasei Throwable care sunt implementati in clasele Exception,
RuntimeException si Error. Dintre acestia doi initializeaza cauza.
public Throwable();
public Throwable(String message);
public Throwable(Throwable cause);
public Throwable(String message, Throwable cause);
In exemplul urmator putem observa modul in care a fost creata o cauza si apoi cum ea a fost
urmarita:
public class Test{

static void metoda (int x, int y) throws Exception{


try {
for (int i = 1; i >= x ; i--) {
System.out.println (y /i) ;
}
}
catch (ArithmeticException e) {
Exception myExcept = new Exception("Eroare a unei operatii
aritmetice") ;
myExcept.initCause (e) ;
throw myExcept ;
}
}

public static void main (String args[]) {


try {
metoda(-5,6);
} catch (Exception e) {
System.out.println (e.getMessage()) ;
System.out.println (e.getCause().getMessage()) ;
}
}
}
Uneori este necesar sa suprascriem metoda getCause(), spre exemplu atunci cand tratam
exceptii in cod legacy, in versiuni ale Javei in care se folosea doar cod legacy si proprietatea
cause nu exista.
Utilizatorii pot sa-si creeze propriile exceptii cu ajutorul carora pot oferi mai multe informatii
despre cum exceptia a aparut sau sa manipuleze, dependent de aplicatie, exceptia. Pentru
aceasta vom crea o noua clasa ce extinde Exception sau o clasa ce a extins Exception. Clasa
exceptie utilizator nu poate extinde Throwable pentru ca aceasta este clasa de baza atat pentru
exceptii cat si pentru erori. De asemenea, nu poate extinde clasa Error, aceasta fiind folosita
pentru erori pe care aplicatia nu le poate recupera.
Nu este recomandat sa creem subclase utilizator ale clasei RuntimeException si aceasta
deoarece obiectele acestea ar trebui sa fie generate si aruncate de JVM. Utilizatorul nu trebuie
sa se ingrijeasca de aceste exceptii.
Pentru a ascunde tipul unei exceptii ce a fost generate utilizam o exceptie wrapper. Fie
urmatorul exemplu:
import entities.Entity;
import exceptions.DAOException;

public class EntityDAO {


private Entity[] entityArray;

public EntityDAO(int val) {


entityArray = new Entity[val];
}

public Entity findById(int id) throws DAOException {


try {
return entityArray[id];
} catch (ArrayIndexOutOfBoundsException e) {
throw new DAOException("out of bounds", e);
}
}

}
package exceptions;

public class DAOException extends Exception {


private static final long serialVersionUID = 1L;

public DAOException(Throwable cause) {


super(cause);
}

public DAOException(String message, Throwable cause) {


super(message, cause);
}

}
import daos.EntityDAO;
import exceptions.DAOException;

public class TestEntityDao {

public static void main(String[] args) {


EntityDAO ent = new EntityDAO(3);

try {
ent.findById(5);
} catch (DAOException e) {
Throwable t = e.getCause();
System.out.println(e.getMessage() + t.getMessage());
}

}
O implementare DAO poate utiliza o exceptie wrapper pentru a pastra abstractia si pentru a
evita netratarea exceptiilor. Iata explicatia: majoritatea metodelor din DAO trebuie sa trateze
exceptii (IOExceptions sau SQLExceptions). Daca am folosi semnaturi fara throws am
determina clientul sa trateze exceptiile, la implementare. Am pierde astfel abstractia. Prin
folosirea exceptiilor wrapper ii dam clientului posibilitatea de a stii care sunt problemele
aparute la rulare.
In versiunile anterioare ale Javei daca aparea o eroare intr-un fir atunci se arunca o eroare
exceptie si firul se termina, exceptia navigand spre radacina ThreadGroup. Numele firului,
numele exceptiei, mesajul exceptiei si stack trace-ul erau afisate. In 1.4 si versiunile
anterioare inseram cod propriu in ThreadGroup pentru a manipula aceste exceptii. In 1.6
manipularea exceptiilor neprinse este simplificata pentru ca putem defini manipulatorul
exceptiilor neprinse, la nivelul firului de executie.
Metoda setUncaughtExceptionHandler(), introdusa in 1.5, ne permite sa preluam controlul
deplin asupra raspunsului unui fir, in cazul exceptiilor neprinse, prin setarea unui manipulator
de exceptii neprinse. Pentru a seta manipulatorul in toate firele vom trimite ca parametru al
acestei metode o implementare a interfetei Thread.UncaughtExceptionHandler. Metoda
acestei interfete este uncaughtException(). Daca niciun manipulator nu este setat obiectul
ThreadGroup va functiona ca un manipulator de exceptie.
Metoda getUncaughtExceptionHandler() returneaza handler-ul invocat atunci cand un fir
se termina din cauza unei exceptii neprinse. Pentru a determina cum sa manipuleze exceptia
neprinsa, JVM mai intai cauta un manipulator de fire pe care sa-l invoce. Daca exista il
invoca. Apoi JVM trimite exceptia in ierarhia ThreadGroup pina ce atinge radacina acesteia,
ceea ce inseamna ca ThreadGroup nu a suprascris uncaughtException(). In cele din urma
JVM apeleza Thread.getDefaultExceptionHandler() pentru a invoca manipulatorul
predefinit.
In exemplul urmator vom crea un manipulator de exceptie, HandlerPropriu, pe care il vom
seta intr-un fir, ExceptieNeprinsa. Metoda run() a acestui fir nu prinde nicio exceptie, desi
face o impartire la zero. Exceptia este preluata automat de catre manipulatorul definit.
class HandlerPropriu implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
System.err.printf("%s: %s la linia %d in fisierul %s%n",
t.getName(), e.toString(), e.getStackTrace()[0].getLineNumber(),
e.getStackTrace()[0].getFileName());
}
}

public class ExceptieNeprinsa extends Thread{


public ExceptieNeprinsa() {
setName("Exceptia neprinsa explicit:") ;
setUncaughtExceptionHandler(new HandlerPropriu()) ;
}

public void run(){


System.out.println(3/0);
}

public static void main (String args[]) {


ExceptieNeprinsa x=new ExceptieNeprinsa();
x.start();
}
}

Asertiuni (assertions)
Asertiunea ne permite sa testam presupunerea despre cum ruleaza un program. Asertiunea
contine o expresie booleana, avand valoarea predefinita true. Asertiunea este diferita de toate
celelalte teste pentru ca verifica conditii ce nu pot fi violate. Daca expresia booleana are
valoarea false atunci sistemul arunca o AssertionError. Mecanismul de asertiuni este
valoros deoarece ofera debugging mult mai eficient, ajuta la intretinerea codului prin
documentarea lucrului intern al programului, incurajeaza forma de programare contractuala
(contract programming).
Programarea contractuala este o tehnica utilizata pentru a incuraja un inalt nivel al calitatii
softului, reutilizarii si increderii. Furnizorul si consumatorul unui apel de metoda sunt
impreuna de acord sa adere la un contract bine definit, ce este fortat de folosirea asertiunilor.
Asertiunile sunt in mod predefinit dezactivate. Putem sa le activam sau dezactivam folosind
comenzi la rulare. Asertiunile asigura ca o regula stabilita in cod nu poate fi niciodata
modificata. Putem folosi asertiunile ca pe o masura de siguranta, pentru a confirma
presupunerile despre comportamentul unui program.
Exista doua forme de asertiuni:
 simpla, ce poate fi exprimata ca: assert Expresie; unde Expresie este o expresie
logica. Cand asertiunile sunt activate, sistemul ruleaza asertiunile si evalueaza
expresia din ele. Daca aceasta are valoarea false atunci se va arunca o
AssertionError, fara niciun mesaj
 complexa, ce poate fi exprimata ca: assert Expresie1:Expresie2;. Aceasta se
foloseste pentru a furniza un mesaj complex in caz de eroare. Expresie1 este
booleana si va fi evaluata. In caz de eroare valoarea lui Expresie2 va fi trimisa
constructorului lui AssertionError si un mesaj de eroare detaliat va fi afisat.
AssertionError este o eroare ce opreste executia programului si reprezinta o
subclasa a java.lang.Error. Aceasta eroare nu poate fi prinsa.
Nu vom folosi asertiunile pentru:
 verificarea argumentelor metodelor: metodele publice au un contract clar definit cu
utilizatorii, care nu depinde de faptul ca asertiunile sunt activate. In locul asertiunile
pentru argumente ilegale se poate folosi IllegalArgumentException.
 efectuarea functiilor critice cerute de aplicatie. Aceasta problema apare atunci cand
asertiunile sunt dezactivate
 producerea efectelor secundare. Acesta problema apare atunci cand asertiunile sunt
dezactivate
Urmatorul exemplu ilustreaza de ce nu putem folosi asertiunile pentru executarea operatiilor
critice
assert carNames.remove(null);
In cazul in care asertiunile sunt dezactivate metoda remove() nu se va executa, ceea ce ar
produce rezultate inconsitente. Solutia de rezolvare consistenta a situatiei este urmatoarea:
boolean nullsRemoved = carNames.remove( null );
assert nullsRemoved;
Asertiunile pot fi folosite pentru testarea:
 invariantilor interni: un invariant intern este o conditie ce trebuie sa fie true tot
timpul. Asertiunile se folosesc ori de cate ori facem o presupunem asupra unui
invariant. O folosim in if-uri multiple, pe ultima ramura else sau in switch pe
ramura default. Spre exemplu:
if (j % 5 == 0) {
...
} else if (j % 5 == 1) {
...
} else {
assert j % 5 == 2 : "presupunerea este incorecta";
....
}
 invariantilor de control ai fluxului: pentru aceasta plasam asertiunea intr-o locatie de
cod in care presupunem ca nu putem ajunge. Spre exemplu:
void metoda() {
  for (...) {
    if (...)
      return;
  }
  assert false; // acest punct al executiei nu ar trebui atins
}
De obicei se foloseste assert false;
 Preconditiilor, postconditiilor si invariantilor clasei. O preconditie este o conditie pe
care o presupunem adevarata la inceputul metodei. Putem testa presupunerea intr-o
metoda nepublica, la inceputul acesteia
private void metoda(int param) {
  assert ((param >= 50) && (param <= 500)) : param;
    ...
}
Putem folosi asertiunile pentru a testa postconditiile atat in metode publice cat si
nepublice. Putem testa o post conditie chiar inainte de instructiunea return. Pentru
aceasta insa trebuie sa ne asiguram ca exista o singura iesire din metoda. Un invariant
de clasa este o conditie ce trebuie sa fie adevarata pentru fiecare instanta a clasei.
Presupunem ca avem o metoda booleana privata numita IsCorrect() care trebuie sa
returneze true inainte si dupa ce orice metoda s-a incheiat. In cod adaugam o
asertiune inainte de instructiunea return pentru fiecare metoda publica si constructor.
In acest moment testul devine un invariant.
Folosim comenzi in linia de comanda pentru a activa asertiunile. Putem activa asertiunile
pentru o aplicatie, un pachet sau o clasa. Sintaxa generala este:
java [-enableassertions | -ea] [:package name"..." | :class name]
Comanda urmatoare activeaza asertiunile aplicatiei Application:
java –ea Application
Pentru o clasa folosim comanda:
java –ea:com.vehicle.motorcar.YourClass
iar pentru un pachet:
java –ea:com.vehicle.motorcar…
cele trei puncte din finalul comenzii precizeaza ca asertiunile sunt active pentru pachetul
motorcar si pentru toate subpachetele sale.
Unele clase nu sunt incarcate de class loader ci de JVM. De aceea folosim in completarea
comenzii de activare a asertiunilor clauza -eas sau –enablesystemassertions. Spre
exemplu:
java –esa –ea:com.vehicle.motorcar.YourClass
Dezactivarea asertiunilor se face folosind clauza –da sau –disableassertions.
Cuvantul rezervat assertion nu poate fi folosit cu versiunile mai vechi ale Javei. Compilarea
cu versiunea 1.3 va genera o eroare sau un avertisment. In rest compilarea se face cu: javac
–source 1.x fisier, unde x este 4, 5 sau 6.
Vom exemplifica utilizarea asertiunilor folosind frameworkul JUnit 4 din Eclipse. Vom crea
mai intai o clasa a carei metoda dorim sa o testam.
public class Asertiuni {

private final String[] ZILELE_SAPTAMANII = {"Luni", "Marti",


"Miercuri", "Joi", "Vineri", "Sambata", "Duminica"} ;

public String numeleZilei (int zi) {


assert ((zi >= 1) & (zi <= 7)) : "In afara domeniului:" + zi ;
return ZILELE_SAPTAMANII[zi-1] ;
}
}
Metoda numeleZilei() contine o asertiune ce verifica domeniul indicelui. Asertiunea va
trimite si un mesaj in caz de eroare customizat. Vom crea o unitate de testare folosind JUnit
Test Case-ul Eclipse, numit Test1, in pachetul Testare, avand Class under test clasa
anterioara. Putem selecta, in asistent, metodele pe care dorim sa le supunem testarii.
import static org.junit.Assert.*;

import org.junit.Test;

public class Test1 {

@Test
public final void testMain() {
Asertiuni a=new Asertiuni();
a.numeleZilei(8);
}

}
Observatie: daca folosim asertiuni (ca in cazul exemplului nostru) din
Window>Preferences>Java>JUnit selectam checkbox-ul Add ‘-ea’….
Rulam apoi aplicatia ca pe un JunitTest. Un API complet gasim in org.junit.Assert.

JUnit (more) in Detail


Static imports with Eclipse

JUnit uses a lot of static methods and Eclipse cannot automatically import static imports. You
can make the JUnit test methods available via the content assists.

Open the Preferences via Window -> Preferences and select Java > Editor > Content Assist >
Favorites. Add then via "New Member" the methods you need. For example this makes the
assertTrue, assertFalse and assertEquals method available.

You can now use Content Assist (Ctrl+Space) to add the method and the import.

I suggest to add at least the following new members.

 org.junit.Assert.assertTrue
 org.junit.Assert.assertFalse

 org.junit.Assert.assertEquals
 org.junit.Assert.fail

 Annotations

The following give an overview of the available annotations in JUnit 4.x

Table 1. Annotations

Annotation Description
Annotation @Test identifies that this
@Test public void method()
method is a test method.
Will perform the method() before each test.
This method can prepare the test
@Before public void method()
environment, e.g. read input data, initialize
the class)
@After public void method() Test method must start with test
Will perform the method before the start of
all tests. This can be used to perform time
@BeforeClass public void method()
intensive activities for example be used to
connect to a database
Will perform the method after all tests
have finished. This can be used to perform
@AfterClass public void method()
clean-up activities for example be used to
disconnect to a database
Will ignore the test method, e.g. useful if
the underlying code has been changed and
@Ignore the test has not yet been adapted or if the
runtime of this test is just to long to be
included.
Tests if the method throws the named
@Test(expected=IllegalArgumentException.class)
exception
Fails if the method takes longer then 100
@Test(timeout=100)
milliseconds

Assert statements

The following gives an overview of the available test methods:

Table 2. Test methods

Statement Description
Let the method fail, might be usable to check that a
fail(String)
certain part of the code is not reached.
assertTrue(true); True
Statement Description
assertsEquals([String message], Test if the values are the same. Note: for arrays the
expected, actual) reference is checked not the content of the arrays
assertsEquals([String message], Usage for float and double; the tolerance are the
expected, actual, tolerance) number of decimals which must be the same
assertNull([message], object) Checks if the object is null
assertNotNull([message], object) Check if the object is not null
assertSame([String], expected, actual) Check if both variables refer to the same object
Check that both variables refer not to the same
assertNotSame([String], expected, actual)
object
assertTrue([message], boolean condition) Check if the boolean condition is true.

S-ar putea să vă placă și