Sunteți pe pagina 1din 58

INTEGRAREA TUTUROR ELEMENTELOR : FIRE DE EXECUTIE , EXCEPTII SI ALTELE

In acest capitol sunt examimate alte elemente ale limbajului Java :fire de executie ,exceptii si fluxuri. Java foloseste aceste structuri pentru crearea si controlul unor activitati simultane pentru tratarea erorilor precum si pentru realizarea comunicarii intre diferitele procese pe care le controleaza. Cu ajutorul firelor de executie ,o singura aplicatie Java poate executa cu usurinta mai multe secvente de cod concurente, ceea ce ii permite sa realizeze in acelasi timp operatii importante, cum ar fi preluarea din retea a unui fisier, actualizarea ecranului si raspunsul la informatiile introduse de utilizatori, fara o suprasarcina deosebita, precum cea introdusa de bifurcarile clasice din limbaje precum C/C++. Exceptiile sunt metode flexibile si puternice folosite de Java pentru tratarea erorilor, inlocuind codurile de eroare returnate in alte limbaje. In Java, exceptiile sunt obiecte si pot fi extinse la fel ca orice alt obiect. Ele contribuie la atingerea scopului urmarit de creatorii limbajului Java: acela de a obtine cel mai sigur limbaj de programare. Fluxurile reprezinta modalitatea folosita de Java pentru comunicarea cu lumea exterioara. Ele permit unei aplicatii Java sa comunice cu fisiere, retele, dispozitive si cu alte aplicatii. De asemenea, fuxurile fac posibila comunicarea intre firele de executie din aceeasi aplicatie. Ele se bazeaza pe scrierea sau citirea unei secvente de octeti in sau dintr-o sursa externa. Deoarece datele sunt privite ca un flux de octeti, nu sunt necesare informatii specifice privind sursa sau destinatia datelor. Acest lucru duce la forta si portabilitatea limbajului Java. Capitolul cuprinde cateva exemple care ilustreaza aceste concepte de programare. Incercati sa le folositi; dupa ce veti vedea rezultatele afisate de aceste programe, le veti intelege si aprecia la justa valoare. Trebuie sa tinem seama de faptul ca Java nu este un limbaj procedural, iar pentru majoritatea oamenilor, conceptualizarea non-secventiala nu este dificila. Dumneavoastra insa, ca viitor programator Java, veti avea mai multe satisfactii folosind aceste concepte avansate.

FIRE

DE

EXECUTIE

Firele de executie (threads) permit executarea simultana a mai multor parti dintr-un program. De exempu, o parte a programului poate afisa o animatie pe ecran, in timp ce alta construieste urmatoarea animatie ce va fi afisata. Pentru programatorii experimentati, firele de executie constituie o versiune redusa a unui proces. Cele doua instrumente se aseamana, in sensul ca pot fi executate independent si simultan dar se deosebesc prin faptul ca firele de executie nu implica suprasarcina impusa de procese. In C si C++ , pentru crearea unor noi procese este folosita comande fork . Un proces bifurcant (forked proces) este copia exacta a procesului original, continand aceleasi variabile, acelasi cod si asa mai departe. Crearea unei copii complete a procesului paronte este foarte costisitoare din punctul de vedere al resurselor. In timpul executiei, procesul descendent este complet independent de procesul parinte; el poate chiar modifica datele, fara ca acesta sa aibe vreun efect asupra procesului parinte. Firele de executie nu copiaza in intregime procesul parinte, ci doar ruleaza in paralel codul necesar. In felul acesta, ele pot fi lansate mai repede, deoarece nu mai apare suprasarcina introdusa de un proces complet. Cu toate acestea, firele de executie au acces la toate detele procesului parinte.

Firele de executie pot citi si/sau scrie date accesibile tuturor celorlalte fire de executie, ceea ce usureaza comunicarea intre procese, dar poate conduce la situatii in care mai multe fire de executie modifica datele intr-un mod imprevizibil. De aceea, este necesara o atntie sporita la programare.

UTILITATEA FIRELOR DE EXECUTIE


Firele de executie sunt instrumente de programare utile. In primul rand, ele permit programelor sa faca mai multe lucruri in acelasi timp. Aceasta posibilitate este utila in situatiile in care utilizatorul trebuie sa execute o anumita operatie, in timp ce in fundal se intampla altceva. In al doilea rand, firele de executie permit programatorului sa se concentreze asupra functionalitatii programului, fara a-si face griji in privinta implementarii schemelor de multitasking. El poate lansa pur si simplu un nou fir de executie pentru o operatie de fundal fara sa isi puna problema comunicarii intre procese.

DECLARAREA FIRELOR DE EXECUTIE


Pentru crearea clselor care folosesc fire de executie, trebuie sa extindeti clasa Thread sau sa implementati interfata Runnable. Rezultatele sunt in ambele cazuri aceleasi. Prin implementarea interfetei Runnable, clasele existente sunt convertite la fire de executie, fara modificarea claselor de baza.

CREAREA FIRELOR DE EXECUTIE PRIN EXTINDEREA CLASEI THREAD


Iata un exemplu de creare a unui fir de executie prin extinderea clasei Thread : public class MyMain { public static void main (String args[]) { CntThread cntThread; //declare thread cntThread = new CntThread(); //create thread cntThread.start(); //start thread running try {System.in.read();} //wait for keyboard input catch(java.io.IOException e){} cntThread.stop(); //stop thread } } class CntThread extends Thread { public void run() { int ix = 0; while (true) { System.out.println("running, ix = " + ix++); //write count to screen try {Thread.sleep(1000);} //sleep 1 second catch(InterruptedException e){} }

} } In acest exemplu, a fost creat un fir de executie care afiseaza pe ecran un contor de incrementare. Numaratoarea continua pana cand metoda main primeste un caracter de la tastatura, dupa care firul de executie al contorului se termina. Prin urmare, puteti apasa o tasta oarecare si apoi Enter ( sau numai tasta Enter ) pentru a opri numaratoarea. Acesta este un exemplu foarte bun de fir de executie, deoarece intoduce cuvintele cheie try si catch ( despre care vom discuta in sectiunea Exceptii din acest capitol ) si ilustreaza modul si ilusteraza modul de creare si de terminare a unui fir de executie.

CREAREA FIRELOR DE EXECUTIE PRIN IMPLEMENTAREA INTERFETEI RUNNABLE

Cea de-a doua modalitate de creare a unui fir de executie consta in implementarea interfetei Runnable. Ca si exemplul anterior, programul de mai jos creeaza un fir de executie care incrementeaza un contor pana cand se introduce un caracter de la tastatura : import java.applet.*; import java.awt.*; public class MyApplet extends Applet implements Runnable { int ix = 0; Thread mainThread; CntThread cntThread; public void start() { if (mainThread == null) { mainThread = new Thread(this); mainThread.start(); //start main thread } } public void run() { cntThread = new CntThread(this); //create CntThread instance cntThread.start(); //start cntThread instance } public boolean keyDown(Event evt, int key) { //process key press cntThread.stop(); //stop cntThread instance return(true); } public void paint(Graphics g) {

g.drawString("running, ix = " + ix, 10,20); //write count to screen } } class CntThread implements Runnable { MyApplet parent; boolean loop; Thread cntThread; public CntThread(MyApplet p) { //constructor for CntThread parent = p; //save parent instance } public void start() { if (cntThread == null) { cntThread = new Thread(this); //create counting thread cntThread.start(); //start counting thread } } public void stop() { loop = false; } //set value to exit main while loop

public void run() { loop = true; while (loop == true) { parent.ix++; //increment counter parent.repaint(); //repaint screen try {Thread.sleep(1000);} //sleep 1 second catch(InterruptedException e) {} } } } Inainte de a putea sa vizualizati miniaplicatia, cu ajutorul programului appletviewer sau al unui browser, trebuie sa creati un document HTML, ca in exemplul urmator : <HTML> <HEAD> <TITLE>Folosirea interfetei Runnable </TITLE> </HEAD> <BODY> <APPLET CODE=MyApplet.class WIDTH=400 HEIGHT=400> <EM><B>Aveti nevoie de un browser care recunoaste limbajul Java pentru a vedea aceasta miniaplicatie </B><EM> </APPLET>

</BODY> </HTML> Si acest exemplu foloseste instructiunile try si catch pentru tratarea exceptiilor. Examinati metodele start() si stop() din program. Lansarea si oprirea firelor de executie sunt rezentate in detaliu in sectiunea Metode pentru firele de executie din acest capitol.

CUVANTUL CHEIE NEW SI INSTANTIEREA FIRELOR DE EXECUTIE


Instantele firelor de executie sunt create cu ajutorul cuvantului cheie new. Argumentele acestuia pot folosi fie direct clasa Thread, ca in exemplul urmator : MainThread = new Thread (this); Fie o subclasa a clasei Thread, cum ar fi : MainThread = new MyThreadClass (); In primul exemplu, instanta curenta a unui obiect , this , este initializata ca fir de executie. Orice obiect poate fi transmis ca parametru clasei Thread . Clasa Thread are putini constructori. Daca acestia nu sunt suficienti, se poate folosi o subclasa a clasei Thread, in care sa fie implementate noile tipuri de constructori. Cel de-al doiea exemplu de creare a unui fir de executie permite acest lucru.

DISTRUGEREA UNUI FIR DE EXECUTIE


Puteti controla rularea unui fir de excutie in mai multe moduri, folosind metodele stop(), start() si destroy() . Cat timp este desemnat undeva in program , obiectul continua sa existe, chiar daca este apelata metoda stop(). Nu este necesara distrugerea explicita a firelor de executie. Sistemul Java de colectare a gunoiului se ocupa de acest amanunt. Daca vreti sa contribuiti la acest proces, asigurati-va ca au fost sterse toate referintele la obiectul fir de excutie. Cea mai simpla cale de a face acest lucru este sa atribuiti valoarea null tuturor variabelor care contin referinte la firul de executie, in exemplul urmator : Thread myThread = new Thread (); MyThread .start (); MyThread .stop (); MyThread = null; In acest exemplu, firul de executie este instantiat cu new, este lansat si apoi oprit. In continuare, variabilei care contine instanta firului de executie ii este atribuita valoarea null. Ultima operatie forteaza sistemul de colectare a gunoiului sa programeze dezalocarea resursei pentru obiectul respectiv.

METODE PENTRU FIRELE DE EXECUTIE

Toate firele de executie Java implementeaza patru metode : init(), run(), start(), stop(). Acestea sunt metodele prestabilite ale clasei Thread; daca o clasa implementeaza interfata Runnable, metodele trebuie declarate explicit. Init ( ) Metoda init ( ) este apelata pentru prima data la lansarea unui fir de executie. In general, este folosita pentru initializarea obiectelor, dar poate contine orice cod pe care programatorul il considera necesar. Iata un exemplu de metoda init ( ) : Public void init ( ) { Index = 0; } Aceasta matoda atribuie valoarea zero variabilei Index. In aplicatiile mai complexe, ea poate include codul necesar pentru deschiderea unor baze de date, crearea unor obiecte de afisare sau pentru orice actiune care trebuie executata la lansarea pentru prima data a unui fir de executie. Start ( ) Metoda start ( ) este apelata pentru lansarea unui fir de executie. Ea contine, de obicei, codul necesar pentru crearea si lansarea unui fir de executie. Iata un exemplu de metoda Start ( ) : Public void start ( ) { If (mainThread == null) { mainThread = new Thread (this); mainThread.start ( ); } } Aceasta metoda verifica daca firul de executie nu a fost deja creat , testand daca valoarea obiectului mainThread este null. In caz contrar, cu ajutorul cuvantului cheie new, este creat un nou fir de executie, care va fi lansat prin apelarea metodei start ( ). Stop ( ) Metoda stap ( ) contine codul necesar pentru oprrea unui fir de executie. De obicei, ea genereaza un semnal care este interpretat de toate firele de executie si determena oprirea acestora. De asemenea, metoda stop ( ) poate modifica un obiect care sa provoace iesirea din ciclul in care se face apelul metodei run ( ). Iata un exemplu de metoda stop ( ): Public void stop ( ) { Loop = false; } Aceasta metoda nu face decat sa atribuie unei variabile valoarea false. Variabila loop este folosita in ciclul principal al metodei run ( ) pentru controlul fluxului . Run ( ) Metoda run ( ) se aseamana, ca functionalitate, cu functia main ( ) din C sau C++. Ea contine corpul principal al codului care va fi rulat de firul de executie. Iata un exemplu de metoda run ( ): Public void run ( ) {

Loop = true; While (loop == true ) { System.out.println(index = + index++); Try {thread.sleep (1000);} Catch(InterruptedException e ) {} } } Corpul acestei metode contine initializarea variabilei loop si o bucla while, care este executata atat timp cat variabila loop are valoarea true. Corpul buclei afiseaza pe ecran valoarea variabilei index si apoi asteapta o secunda. Metoda poate fi combinata cu exemplul pentru stop ( ) in vederea controlarii procesului.

DENUMIREA FIRELOR DE EXECUTIE


Java va pune la dispozitie o modalitate prin care puteti atribui nume firelor de executie. Numele poate consta in orice sir de caractere acceptat de Java. Atribuirea numelor reprezinta o modalitate simpla de diferentiere a firelor de executie si permite unui proces parinte sa interogheze procesul descendent dupa nume. Un fir de executie poate primi un nume la crearea sa sau ulterior. Numele poate fi schimbat in orice moment . Pentru a denumi un fir de executie, folositi un constroctor al clasei Thread care accepta un sir de caractere ca argument suplimentar, astfel: Thread myThread = new Thread ( this . Primul fir de executie nominalizat); In acest exemplu, firul de executie este instantiat cu operatorul new si primeste Primul fir de executie nominalizat.

Metoda getName ( )
Puteti afla numele unui fir de executie cu ajutorul metodei getName ( ). Aceasta returneaza numele asociat unui obiect de tip fir de executie, ca in exemplul urmator : System.out.println( Numele acestui fir de executie este : + myThread. GetName ( ) ); In acest exemplu, numele asociat obiectului de tip fir de executie myThread este afisat pe ecran. Folosind si exeplul anteror, instructiunea de mai sus va afisa urmatorul text: Numele acestui fir de executie este : Primul fir de executie nominalizat In mod similar, un fir de executie isi poate afla propriul numele : System.out.println ( Numele meu este :+this.getName ( ) );

Metoda setName ( )
Dupa crearea unui fir de executie, ii puteti stabili sau schimba numele cu ajutorul metodei setName ( ). Aceasta poate fi folosita de procesul parinte, de firul de executie propriu-zis si de orice alta metoda care are acces la obiectul de tip fir de executie. Iata un exemplu de folosire a metodei setName ( ) pentru schimbarea numelui unui fir de executie: myThread.setName ( Firul de executie redenumit );

In exemplul de mai sus, numele firului de executie se modifica din Primul fir de executie nominalizat in Firul de executie redenumit.

SINCRONIZAREA FIRELOR DE EXECUTIE


Un obiect sau o metoda pot fi accesate de mai multe fire de executie, deoarece acestea nu sunt procese independente continand copii complete ale datelor. Totusi, nu exista nici o garantie privind firul de executie care va avea acces la un obiect la un moment dat, ceea ce poate conduce la rezultate imprevizibile. Daca acest lucru este inacceptabil, folositi logica de sincronizare. Cuvantul cheie este synchronized.

Cuvantul cheie synchronized


Cuvantul cheie synchronized este folosit pentru blocarea unui obiect pe perioada executarii unui bloc de cod. Nici un alt fir de executie nu poate modifica obiectul specificat, cat timp blocul de cod respectiv este in executie. Iata un exemplu de sincronizare : Public void printIndex ( ) { Synchronized ( index ) { Index++; System.out.println (index = + index ); } } In exemplul de mai sus, metoda printIndex ( ) contine un bloc de cod sincronizat. In cadrul lui, obiectul index este blocat pana la terminarea executiei blocului de cod care formeaza corpul instructiunii synchronized. Incrementarea variabilei index si afisarea ei se fac in interiorul acestui bloc. Astfel, valoarea variabilei index este afisata pe ecran inainte ca alt fir de executie sa o incrementeze. Cuvantul cheie synchronized poate fi folosit si ca modificator al unei metode, asigurandu-se astfel utilizarea ei de catre un singur fir de executie la un moment dat. Exemplul de mai sus poate fi rescris astfel: Public synchronized void printIndex ( ) { Index ++ System.out.println (index = + index ); }

Pe scurt, despre firele de executie


Firele de executie reprezinta modalitatea folosita de Java pentru executarea simultana a unor segmente de cod diferite. Ele sunt o varianta simplificata a proceselor, dar nu implica aparitia suprasarcinii caracteristice acestora. Firele de executie nu copiaza toate variabilele, asa cum face comanda fork din C/C++, ceea ce permite lansarea lor mai rapida in executie. In plus, firele de executie isi pot folosi datele reciproc, ceea ce usureaza comunicarea intre ele, dar poate conduce la aparitia unor probleme de sincronizare a accesului la date .

Firele de executie au ca baza clasa Thread si pot fi implementate prin extinderea acesteia sau prin folosirea interfetei Runnable. In cea de-a doua varianta , puteti adauga fire de executie unor clase extinse. Toate firele de executie implementeaza metodele init ( ) , start ( ) , stop ( ) si run ( ). Puteti sa le definiti explicit sau sa folositi metodele prestabilite ale clasei Thread. Metoda init ( ) este apelata la prima lansare a unui fir de executie si este locul in care puteti sa scrieti codul de initializare. Metoda start ( ) este apelata la fiecare lansare a unui fir de executie, de obicei dupa operatiile de initializare. Metoda stop ( ) este folosita la oprirea unui fir de executie si contine codul de terminare a corpului principal al acestuia. In sfarsit, metoda run ( ) este apelata de metoda start ( ) si contine, de obicei, corpul firului de executie. Datorita structurii limbajului Java, care permite accesul mai multor fire de executie la aceleasi variabile, pot aparea situatii in care fire de executie diferite modifica aceeasi variabila. De asemenea, in sarcina sistemului intra si planificarea firelor de executie, care poate fi diferita de cea anticipata de progaramator. Puteti controla aceasta situatie cu ajutorul modificatorului synchronized. Acesta poate fi folosit pentru o metoda sau pentru un obiect si determina executarea unui bloc de cod de catre un singur fir de executie la un moment dat .

EXCEPTII
Pana acum, cartea a prezentat modaliattile prin care un programator poate genera corect codul Java. Scrierea unui cod corect este, in definitiv, scopul programarii. Cu toate acestea, numai programele foarte simple sunt scutite de erori; de exemplu, daca nu alocati suficient spatiu pentru toate elementele dintr-o matrice, va fi generata o eroare. Exista probleme care nu se afla sub controlul programului si, prin urmare, nici al programatorului, de exemplu: epuizarea spatiului de memorie disponibil, caderea retelei sau defectarea componentelor hardware. In Java apar doua tipuri principale de probleme: erorile si exceptiile. Erorile sunt cauzate de probleme interne ale limbajului Java si tin de elemente de detaliu care nu pot fi rezolvate de program. De obicei, la aparitia unei erori, Java genereaza un mesaj de eroare si iese din program.

LOGICA EXCEPTIILOR
Pentru tratarea erorilor remediabile, Java foloseste exceptiile. Acestea reprezinta o clasa de obiecte care pot trata, teoretic, orice problema. Java se bazeaza pe ideea crearii unui cod pe cat posibil reutilizabil si lipsit de erori, tratand erorile ca obiecte. Codul de tratare a erorilor se afla in pachetul java.lang si este inclus in mod automat in toate programele compilate. Motivul pentru care Java trateaza exceptiile ca obiecte este posibilitatea folosirii unei interfete standard extensibile pentru diferite tipuri de erori. Aceasta abordare beneficiaza de toate avantajele proiectarii orientate pe obiecte, cum ar fi refolosirea si personalizarea. In Java, exceptiile pot fi tratate in mai multe moduri:uneori, ele pot fi ignorate pur si simplu; alteori, ele pot fi tratate direct de codul in care apar; sau pot fi transmise codului ce a apelat metoda care a generat exceptia, in ideea ca vor fi tratate de acestea. Daca o exceptie nu este tratata explicit de codul unui program, ea este transmisa interpretorului Java. Acesta poate sa o trateze in mod oarecare sau sa opreasca executia. In unei miniaplicatii executate de un browser, rezultatul ar putea fi blocarea browserului. O astfel de situatie nu este de dorit, asa incat se recomanda tratarea exceptiilor in cadrul programelor.

Asa cum am mentionat mai sus, unele exceptii pot fi complet ignorate. Compilatorul Java solicita tratarea altor exceptii, intr-un fel sau altul. Acesta este motivul pentru care ati vazut deja cateva exemple de tratare a execptiilor in capitolul 6, Elemente de baza ale limbajului Java , si in capitolul 7 Construirea obiectelor , cum ar fi exemplul metodei sleep ( ).Acesta poate genera o exceptie care trebuie tratata in mod explicit. Puteti modifica exceptiile existente sau crea altele noi. Retineti, toate exceptiile sunt incluse in clasa java.lang.Exception. Pentru a modifica o exceptie existenta, programul pur si simplu ii supradefineste metodele sau ii rescrie variabilele. Pentru a crea o noua exceptie, trebuie sa construiti o clasa care sa extinda java.lang.Exception.

Metode pentru exceptii


Tratarea exceptiilor se realizeaza, de obicei, prin prin folosirea metodelor try ( ), catch ( ), throw ( ) si finaly ( ). Desigur, aceste metode pot fi extinse in anumite circumstante. Java foloseste cuvintele chieie try , catch si throw pentru tratarea efectiva a exceptiilor. Aceasta se aseamana, din punct de vedere conceptual, cu o instructiune switch;considerati instructiunea try ca pe o instructiune switch in care este identificata exact conditia care trebuie tratata. Instructiunea catch specifica operatiile care trebuie executate pentru un anume tip de exeptie. Este similara cu sectiune case dintr-o instructiune switch. Pot exista mai multe instructiuni catch la rand care sa trateze fiecare tip de exceptie generat de un bloc specificat in instructiunea try. Throw Pentru intelegerea modului de tratare a exceptiilor in Java, este necesara asimilarea unei noi terminologii. Primul concept pe care trebuie sa-l retineti este acela de lansare sau aruncare ( throwing ) a unei exceptii, termen care desemneaza in Java generarea unei exceptii. De exmplu, sa presupunem ca ati scris o metoda pentru citirea dintr-un fisier. Daca metoda nu poate citi fisierul deoarece acesta nu exista, va fi generata exceptia IOException. In terminologia Java, se spune ca metoda a lansat exceptia IOException. Ganditi-va la un cal care si-a aruncat o potcoava: este bina sa opriti caruta inainte de a se intampla ceva mai rau. Catch Un alt termen folosit la tratarea exceptiilor este catch. Instructiunea catch (interceptare ) cuprinde codul care detecteaza aparitia unei exceptii si o trateaza in mod corespunzator. In terminologia Java se spune ca o exceptie lansata a fost interceptata. In cazul exceptiei IOException, lansata in exemplul precedent datorita inexistentei fisierului din care ar trebui sa se faca citirea, instructiunea catch va afisa pe ecran un mesaj de eroare, prin care avertizeaza utilizatorul ca fisierul specificat nu exista. Acesta este invitat sa introduca un nou nume in fisier, daca primul era incorect sau sa iasa din program. In terminologia Java se spune ca exceptia IOException a fost interceptata. Try

Try (incercare) este termenul folosit in Java pentru a specifica faptul ca programul va incerca sa execute un bloc de cod care poate genera (lansa) o exceptie. Instructiunea try este o modalitate de a-i spune compilatorului ca se incearca tratarea macar a unora dintre exceptiile care pot fi generate de blocul de cod. Finally Instructiunea finally este folosita pentru a preciza actiunile care urmeaza a fi executate in cazul in care nici una dintre instructiunile catch anterioare nu a putut rezolva situatia. Finally se aseamana cu sectiunea default dintr-o instructiune switch. Ea functioneaza ca o plasa in care se prinde tot ceea ce a trecut de instructiunile de tratare a exceptiilor. Un exemplu de folosire a instructiunilor try, catch si finally Exemplul urmator ilustreaza structura Java standard pentru tratarea exceptiilor, incluzand instructiunile try, catch si finally: try { instructiuni care pot genera o exceptie } catch (ExceptionType1 e ) { prelucrarea exceptiei de tipul 1 } catch (ExceptionType2 e ) { prelucrarea exceptiei de tipul 2 } finally { prelucrarea tuturor celorlalte tipuri de exceptii

Folosirea instructiunii try pentru tratarea exceptiilor


Instructiunea try este folosita pentru a informa interpretorul Java ca un bloc de cod poate genera o exceptie si ca tratarea acestei exceptii se va face imediat dupa instructiunea try. Sintaxa instructiunii este urmatoarea: Try instructiune; Sau Try { Instructiuni } Cuvantul cheie try marcheaza inceputul constructiei try si este urmat de o instructiune sau de un bloc de instructiuni alcatuind codul care poate genera o exceptie. Daca una dintre instructiuni genereaza o exceptie, restul instructiunilor din blocul try sunt neglijate si executia continua cu prima instructiune de dupa blocul try, care poate fi o instructiune catch sau finally. Acesta este un lucru demn de retinut. Se poate determina cu usurinta ce parte a codului a fost sarita in cazul aparitiei unei erori. Iata un exemplu:

public class MyMain { public static void main (String args[]) { int[] myArray = new int[10]; try { System.out.println("Before valid array assignment"); myArray[0] = 1; System.out.println("Before invalid array assignment"); myArray[100] = 1; System.out.println("After array exception"); } } } In acest exemplu, este creata matricea myArray avand 10 elemente. Urmeaza un bloc try care contine mai multe instructiuni. Prima, a treia si-a cincea instructiune nu fac decat sa afiseza mesaje de urmarire pe ecran. Cea de-a doua instructiune reprezinta o instructiune standard de atribuire, care atribuie valoarea 1 primului element al matricei (elementul 0). A patra instructiune este tot de atribuire; ea incearca sa atribuie valoarea 1 elementului 100 al matricei. Deoarece matricea are doar 10 elemente, aceasta instructiune genereaza o exceptie ArrayIndexOutOfBounds. Daca urmarim executia blocului de cod care urmeaza dupa instrictiunea try, vom vedea ca primele trei instructiuni sunt executate normal. A patra instructiune, cea care incearca sa faca o atribuire invalida, va incepe executia, dupa care va genera o exceptie, determinand saltul peste cea de-a cincea instructiune si reluarea executiei dupa blocul try. Daca incercati sa compilati codul de mai sus, asa cum se prezinta el in acest moment, veti obtine o eroare de compilare, deoarece blocul try trebuie sa fie urmat de una sau mai multe instructiuni catch sau finally. Intre sfarsitul blocului try si prima instructiune catch sau finally nu este permis un alt tip de instructiune. (Despre instructiunea catch vom mai discuta in sectiunea urmatoare)

Folosirea instructiunii catch pentru tratarea exceptiilor


Instructiunea catch este folosita pentru tratarea unei anumite exceptii generate intr-un bloc try. Sintaxa instructiunii este urmatoarea: catch (ExceptionType ExceptionObj) instructiune; sau catch (ExceptionType ExceptionObj) { instructiuni } Constructia incepe cu cuvantul cheie catch, urmat de o lista de parametri care contin tipul exceptiei si obiectul exceptie. In continuare, apare o instructiune sau un bloc de instructiuni continand codul pentru tratarea exceptiei. Ganditi-va la constructia catch ca la o metoda care va fi apelata de interpretorul Java in cazul in care este generata exceptia de tipul specificat in lista de parametri. Obiectul specificat in aceasta lista, ExceptionObj, este o variabila care contine obiectul exceptie generat si este local blocului catch. Obiectul poate fi manipulat in cadrul acestui bloc in functie de necesitati. Pentru a putea fi folosit in afara blocului catch, obiectul trebuie sa fie atribuit unei variabile al carei domeniu se intinde in afara blocului, ca in exemplul urmator:

public class MyMain { public static void main (String args[]) { int[] myArray = new int[10]; try { System.out.println("Before valid array assignment"); myArray[0] = 1; System.out.println("Before invalid array assignment"); myArray[100] = 1; System.out.println("After array exception"); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("An array index error has occured"); } } } Acest exemplu il continua pe cel din sectiunea anterioara, Folosirea instructiunii try pentru tratarea exceptiilor. Dupa blocul try a fost adaugata instructiunea catch. Aceasta intercepteaza exceptia ArrayIndexOutOfBoundsException. Daca apare o alta exceptie, aceasta instructiune catch este ignorata. In exemplul de mai sus, este generata o exceptie ArrayIndexOutOfBoundsException, asa incat va fi executat corpul instructiunii catch. Aceasta afiseaza un mesaj de eroare pe ecran. Executia continua normal cu prima instructiune de dupa blocul catch diferita de un bloc catch sau finally. Prima instuctiune poate fi urmata de alte instuctiuni catch. Acestea trebuie sa apara imediat dupa blocul try/catch;altfel va fi semnalata o eroare de compilare. In momentul in care este generata o esceptie in blocul try, interpretorul Java va trata instructiunile catch care urmeaza precum clauzele case dintr-o instuctiune switch. Prima instuctiune catch care corespunde exceptiei generate este executata, iar celelalte sunt ignorate. ( Un astfel de exemplu este prezentat in sectiunea Exceptii din acest capitol.) Remarcati faptul ca prelucrarea exceptiilor nu este absolut necesara in Java. Simpla existenta a unei instuctiuni catch urmata de un bloc vid pentru o exceptie este suficienta pentru a evita iesirea din program. Acest lucru a fost folosit in mai multe exemple din capitolele 6 si 7, in cazul metodei sleep ( ). Metoda sleep ( ) este folosita pentru intreruperea unui proces pe o perioada determinata de timp, in care poate fi generata o exceptie IntrruptedException. Aceasta ar putea aparea, de exemplu, in cazul cand un alt proces semnaleaza procesului curent sa reia sau sa isi incheie executia. In exemplele din carte, interceptarea acestor exceptii nu este considerata importanta si, ca urmare, nu se face prelucrarea lor. Totusi, deoarece compilatorul genereaza o eroare daca exceptia indusa de instuctiunea sleep nu este interceptata, s-a folosit o instuctiune catch care nu face nici o prelucrare, ca in exemplul urmator: try {Thread.sleep(timePeriod);} catch (IntrruptedException); Dupa cum vedeti, instuctiunea catch se termina cu caracterul punt si virgula, ceea ce inseamna ca nu urmeaza nici o prelucrare. Rezultatul este ca exceptia IntrruptedException este interceptata, si apoi complet ignorata.

Folosirea instructiunii finally pentru tratarea exceptiilor


Instructiunea finally este folosita pentru tratarea oricarei exceptii generate de un bloc try. Daca instructiunea catch discutata in sectiunea precedenta poate trata un singur tip de exceptii, instructiunea finally poate trata orice exceptie. Iata care este sintaxa instructiunii finally: finally instructiune ; sau finally { instructiunea } Constuctia finally incepe cu cuvantul cheie finally, urmat de o instructiune sau de un bloc care contine codul pentru tratarea exceptiei. Ca si instructiunea catch, instructiunea finally trebuie sa urmeze imediat dupa un bloc try sau dupa o alta instructiune catch. In caz contrar, este semnalata o eroare de compilare. O instructiune finally care urmeaza dupa mai multe instructiuni catch functioneaza precum clauza default dintr-o instructiune switch. Toate exceptiile care nu au fost rezolvate de o instructiune catch anterioara sunt interceptate de instructiunea finally. Ea nu poate fi urmata de alte instructiuni catch sau finally. Oricum, din moment ce finally intercepteaza toate exceptiile, celelalte instructiuni nu ar mai fi executate. Iata un exemplu de folosire a instructiunii finally: char readFile ( ) { char ch = \0; try { ch = myFile.read ( ); } catch ( EOFException e ) { System.out.println ( Am ajuns la sfarsitul fisierului ) ; Break ; } catch ( FileNotFoundException e ) { promptUserForFileName ( fileName ) ; myFile = openFile (fileName ); } finally ( ) { System.out.println ( Eroare neasteptata in timpul citirii fisierului ) ; System.exit ( 1 ); } } return (ch); } In acest exemplu, este folosita o bucla while pentru citirea unui caracter dintr-un fisier. Prima instuctiune din bucla este un bloc try in care se incearca citirea unui caracter dintr-un fisier. Acest bloc este urmat de doua instructiuni catch, care cauta doua tipuri de exceptii. Urmeaza o instructiune finally, pentru interceptarea oricarui tip de exceptii. Dupa incheierea ciclului, apare instructiunea de returnare a caracterului citit.

In timpul citirii unui fisier, pot aparea mai multe tipuri de erori, printre care End of File (sfarsit de fisier), File not found (fisierul nu a fost gasit ), Access permission errors ( erori legate de drepul de acces) si altele. Prima instructiune catch trateaza eroarea File not found, solicitand utilizatorolui sa introduca numele corect al fisierului, deschizand fisierul si permitand reluarea buclei while. In sfarsit, instructiunea finally itercepteaza toate celelalte erori. Ea afiseaza pe ecran un mesaj de eroaresi determina iesirea din aplicatie.

Folosirea instructiunii throw pentru tratarea exceptiilor


Instuctiunea throw determina generarea unei exceptii de catre codul propriu-zis, ramanad in sarcina procedurii apelante sa trateze exceptia. Aceasta este modalitatea specifica limbajului Java de transmitere a informatiilor despre erori catre procedurile apelatoare. Iata sintaxa acestei instuctiuni : import java.net.*; public class MyMain1 { public static void main (String args[] ) { MalformedURLException e; e = new MalformedURLException("Sunteti incepator? Aflati adresa URL corecta!"); throw(e); } } In acest exemplu, este declarat un obiect exceptie (e). Acesta este apoi creat cu o instructiune new standard, in care constructorul permite inlocuirea textului standard de eroare pentru exceptia respectiva. In final, obiectul exceptie este lansat(generat). Atunci cand este lansata o exceptie cu instructiunea throw, exceptia codului curent se opreste. Exceptia este transmisa proceduriiapelante si nici o alta instructiune nu este executata pana la interceptarea exceptiei. In acel moment, executia se reia din locul in care a fost interceptata exceptia. Retineti acest lucru atunci cand folositi instructiunea throw: este recomandabil sa faceti cat mai multe operatii de curatenie inainte de lansarea exceptiei si sa nu le lasati in seama procedurii apelante. In unele cazuri, este preferabil sa interceptati o exceptie Java standard si apoi sa generati o exceptie personalizata, prin includerea instructiunii throw in cadrul blocului catch, ca in exemplul urmator: public class MyMain2 { public static void main (String args[] ) { int[] myArray = new int[10]; try { myArray[100] = 1; } catch(ArrayIndexOutOfBoundsException e) { e = new ArrayIndexOutOfBoundsException("Asigurati-va ca indicele aprtine domeniului."); throw(e); } } }

Aceasta este o versiune a exemplului folosit anterior pentru instructiunile try si catch. Ea genereaza un mesaj de eroare propriu in legatura cu problema de indice a matricei, prin definirea unui nou obiect exceptie (e), care include mesajul personalizat, si folosirea unei instructiuni throw pentru lansarea unei alte exceptii, care va fi interceptata la un nivel superior. Orice instructiune catch sau finally care urmeaza dupa aceasta instructiune catch va fi ignorata. Insa daca instructiunea try ar fi inclusa intr-o alta instructiune try, exceptia ar fi interceptata. Cu alte cuvinte, este posibila interceptarea unei exceptii auto-generate, prin includerea unei instructiuni throw intr-o instructiune try. (E ca si cum ati incerca sa va prindeti singur, nu?)

Clasa java.lang.Exception
In sectiunea Exceptii, s-au facut referiri atat la personalizarea exceptiilor existente, cat si la crearea unor noi exceptii. Acestea pot fi folosite pentru furnizarea unor coduri de eroare personalizate pentru o anumita aplicatie. Java nu returneaza la fel mai multe coduri numerice de eroare ca alte limbaje, asa incat crearea unor noi tipuri de exceptii este binevenita. java.lang.Exception este superclasa tuturor exceptiilor Java. Crearea unor exceptii propri s face pur si simplu prin extinderea acestei clase. De exemplu, pentru crearea unei exceptii propri cu numele MyOutOfRangeException, ar putea fi folosita urmatoarea implementare: public class MyOutOfRangeException extends Exception { public MyOutOfRangeException ( ) { super ( ); } public MyOutOfRangeException (String s) { super ( ); } } In acest exemplu, a fost creata o noua clasa, numita MyOutOfRangeException, prin extinderea clasei Exception. Ea contine doi consructori. Primul apeleaza constructorul clasei parinte.Acesta creeaza o noua exceptie, dar nu pune la dispozitia rutinei apelante o modalitate de personalizare a mesajului. Al doilea constructor este asemanator, dar permite rutinei apelante sa specifice un mesaj de eroare asociat exceptiei. Pentru folosirea acestei exceptii, declarati variabila de tipul MyOutOfRangeException, o initializati cu unul dintre cei doi constructori si lansati exceptia, ca in exemplul urmator: public class VerifyRange { MyOutOfRangeException e; public void verifyIntRange (int value, int low, int high) throws MyOutOfRangeException { if ((value < low) || (value > high)) { e = new MyOutOfRangeException("numar " + value + " este in afara domeniului"); throw(e); } } }

In acest exemplu, a fost creata o clasa numita VerifyRange, care verifica domeniul obiectelor. Singura metoda a acestei clase, verifyIntRange ( ), verifica daca o valoare transmisa ca argument se incadreaza in domeniul specificat de metoda. Daca argumentul este in afara domeniului, se creeaza o exceptie MyOutOfRangeException cu ajutorul instructiunii new si apoi exceptia creata este lansata cu instructiunea throw. Remarcati faptul ca metoda declara explicit faptul ca lanseaza exceptia MyOutOfRangeException. Ca urmare, orice rutina care apeleza aceasta metoda trebuie sa contina o procedura de tratare a exceptiei sau sa declare in mod explicit ca metoda poate ignora exceptia respectiva. Acest lucru nu ar fi fost necesar daca nu ar fi existat sectiunea throw din declaratie. Ramane la latitudinea programatorului sa decida daca o exceptie este destul de importanta pentru a fi tratata de rutina apelanta.

Pe scurt, despre exceptii


Exceptiile reprezinta modalitatea folosita de Java pentru tratarea erorilor. Ele pot fi utilizate in diferite feluri, compensand astfel lipsa mare de coduri de eroare standard returnate in limbaje precum C si C++. Limbajul Java foloseste intensiv exceptiile pentru conditiile de eroare; prin urmare, orice program ar trebui sa le poata trata. Folosirea exceptiilor face programele mai robuste si mai performante. Exceptiile sunt tratate cu ajutorul a trei tipuri de instructiuni: try, catch si finally. Instructiunea try este folosita pentru a semnala compilatorului ca tratarea erorilor va fi executata in blocul de cod asociat. Instructiunea catch este folosita pentru tratarea unei anumite erori generate in blocul de cod try. Mai multe instructiuni catch pot fi introduse secvential, pentru tratarea intr-o anumita ordine a anumitor exceptii. Instructiunea finally poate fi folosita pentru interceptarea toturor exceptiilor care nu au fost interceptate explicit de una dintre instructiunile catch. Un program poate genera exceptii propri, cu ajutorul instructiunii throw. Acestea sunt fie predefinite, fie nou create de program. Chiar si exceptiile predefinite pot fi personalizate, prin modificarea mesajului de eroare la crearea obiectului exceptie. Puteti crea o noua exceptie prin extinderea unei clase de exceptii existente, cum ar fi java.lang.Exception. Exceptiile sunt de obicei destul de simple si pot fi create cu ajutorul a doi constructori. Resul cade in sarcina superclasei extinse de noua exceptie.

Fluxuri
Ati observat, probabil, faptul ca programarea in Java a fost prezentata gradual, in functie de complexitate. Urmatorul subiect constituie cel mai complex concept Java pe care trebuie sa-l stapaniti pentru a va completa cunostintele: fluxurile (streams). Fluxurile Java va pun la dispozitie o modalitate prin care doua sau mai multe procese pot comunica fara a avea informatii unul despre celalalt. Prin urmare, cu ajutorul fluxurilor, puteti crea o aplicatie sau o miniaplicatie care sa transmita date catre orice alta aplicatie sau miniaplicatie bazata pe fluxuri. Mai mult, este posibila comunicarea prin fluxuri intre mai multe fire de executie care formeaza aceeasi aplicatie.

Transmiterea datelor prin fluxurile Java


La cel mai inalt nivel, fluxurile functioneaza prin transmiterea unor informatii de la un proces producator catre un proces consumator. Procesul producator genereaza datele care vor fi folosite de un alt proces. Procesul consumator foloseste datele produse de un alt proces. Informatiile transmise de procesul producator sunt cunoscute sub numele de flux de iesire. Informatiile primite de un proces consumator poarta numele de flux de intrare. Informatiile care formeaza un flux reprezinta o secventa de blocuri de date transmise pe rand de la procesul producator catre procesul consumator. In Java, sunt acceptate direct numai datele primitive, dar orice orice obiect poate fi transmis ca bloc de date, prin crearea unor noi clase de fluxuri. Flluxurile sunt unidirectionale, ceea ce inseamna ca datele circula numai de la procesul producator catre procesul consumator. Procesul consumator nu poate trimite date catre procesul producator prin acelasi flux. Totusi, nimic nu impiedica procesul consumator sa deschida un nou flux catre procesul producator. De fapt, acelasi proces poate avea mai multe fluxuri, fiind in acelasi timp producator si consumator. In plus, aceste fluxuri pot fi deschise cu mai multe procese diferite. Acesta este unul dintre atibutele lor cele mai puternice. Fiecare flux are un singur procesul producator si un singur procesul consumator. Un flux de producator nu poate alimenta mai multe fluxuri de consumator, iar un flux de consumator nu poate fi alimentat de mai multe fluxuri de producator. Aceasta nu inseamna ca un procesul producator nu poate alimenta mai multe procese consumatoare, ci doar ca fiecare proces consumator este alimentat de un alt flux. Acest lucru se aplica si in cazul unui proces consumator alimentat de mai multe procese producatoare. Procesul consumator nu este nevoit sa accepte orbeste datele transmise de procesul producator. El poate porni si opri fluxul de informatii, opate citi doar o parte din el si, in unele cazuri, poate determina cantitatea de date ramase in flux. De asemenea, procesul consumator poate reinitializa fluxul de la un punct anterior. Acest control creeaza uneori impresia ca procesul consumator transmite date inapoi procesului producator, dar de fapt, el comunica cu elementele interne ale limbajului Java care manevreaza fluxurile. Chiar daca un proces consumator intrerupe operatia de receptionare a datelor dintr-un flux, nu este obligatoriu ca si procesul producator sa intrerupa transmisia lor. El poate continua sa produca date, pe care codul Java de manevrare a fluxurilor sa le pastreze in zone temporare de memorie (buffere), pana cand procesul consumator reia receptionarea lor. Iata un lucru important de retinut: consumatorul si producatorul nu comunica direct printr-o interfata de flux. Procesul producator genereaza un flux de date, care este transmis codului Java de tratare a fluxurilor. De aici, datele sunt trimise procesului consumator. Produvatorul si consumatorul nu stiu nimic unul despre celalalt.

Importanta fluxurilor
Fluxurile au multe utilizari in Java si, de fapt, sunt folosite intensiv chiar de limbajul Java propriu-zis. Ele ajuta la citirea si scrierea informatiilor in dispozitive de intrare/iesire, fisiere, baze de

date, lacasuri de retea, alte procese. Toate interfetele pentru fluxuri implementeaza un set de metode de baza, care pot fi folosite de programatori. Acestea sunt comune toturor categoriilor de fluxuri, ceea ce face ca folosirea unui nou tip de flux sa fie destul de simpla. Ezista cateva metode pentru fluxuri in pachetele de extensii ale limbajului Java, dar cele standard se gasesc in pachetul java.io. Acesta trebuie importat in toate aplicatiile si miniaplicatiile care folosesc fluxuri. Unele metode extinse pentru fluxuri nu sunt intotdeauna implementate, cum ar fi posibilitatea de reinitializare a unui flux la un punct anterior. Pentru aceste situatii, sectiunile urmatoare prezinta metodele pe care le puteti folosi pentru a verifica daca sunt acceptate metodele extinse. In acest fel, programele potbeneficia de avantajele metodelor extinse, daca acestea sunt disponibile. Asadar, cum se implementeaza un flux? Veti afla imediat!

Fluxuri de intrare
Fluxurile de intrare ( input streams ) sunt fluxurile de date acceptate si prelucrate de un proces consumator. Asa cum am vazut mai devreme, clasele pentru fluxuri si metodele asociate acestora se gasesc in pachetul java.io. Toate aplicatiile si miniaplicatiile care folosesc fluxuri trebuie sa importe explicit acest pachet la inceputul programului. In acest scop, se foloseste o instructiune import standard: import java.io.*; Exista mai multe clase pentru fluxurile de intrare. Fiecare dintre ele are un obiectiv specific si, ca orice alta clasa, poate fi extinsa si personalizata de programator in functie de necesitati. In tabelul 8.1 este prezentata o lista completa a claselor Java pentru fluxuri de intrare. Tabelul 8.1.Clase pentru fluxuri de intrare BufferedInputStream Citeste datele dintr-o zona tampon (buffer), pentru a micsora numarul de citiri efective din fluxul de intrare ByteArrayInputStream Citeste octetii de date intr-o matrice de octeti DataInputStream FileInputStream FilterInputStream InputStream LineNumberInputStream PipedInputStream PushBackInputStream SequenceInputStream StringBufferInputStream Citeste dintr-un flux de intare obiecte de date, nu siruri de octeti, precum celelalte clase Citeste date dintr-un fisier Permite unui flux sa se conecteze la subclase proprii care pot fi folosite pentru filtarea datelor Superclasa abstracta pentru toate clasele fluxului de intrare Citeste un flux de intrare care foloseste linii numerotate Citeste datele printr-un canal de redirectare catre un alt proces Permite returnarea unui singur caracter in fluxul de intrare Trateaza mai multe fluxuri de intrare secventiale ca pe unul singur Citeste date de tip caracter intr-o matrice de caractere (sir de caractere)

Metodele asociate cu fluxurile de intrare sunt de doua tipuri: unele functioneaza in mod garantat cu toate fluxurile de intrare ( a se vedea tabelul 8.2 ), altele nu ( a se vedea tabelul 8.3 ). Tabelul 8.2. Metode care functioneaza in mod garantat cu toate fluxurile de intrare read ( ) Citeste date dintr-un flux de intrare skip ( ) Ignora unele date din fluxul de intrare MarkAvailable ( ) Testeaza daca metoda mark( ) este disponibila pentru fluxul de intrare respectiv Close ( ) Inchide un flux de intrare Tabelul 8.2. Metode a caror functionare nu este garantata pentru toate fluxurile de intrare Available ( ) Determina cantitatea de date disponibile intr-un flux de Intrare mark ( ) Marcheaza in fluxul de intrare un punct la care se poate Reveni ulterior reset ( ) Revine la un punct specificat din fluxul de intrare Metodele pentru fluxuri de intrare a caror functionare nu este garantata ar putea sa nu fie disponibile ca metode valide sau sa returneze rezultate incorecte sau inconsecvente datorita unui proces producator incert. Unele procese producatoare trimit un flux de date consecvent; altele nu incep transmiterea datelor pana cand codul de tratare a fluxurilor nu semnaleaza ca le poate receptiona datele. Din aceste motive, rezultatele pot fi imprevizibile.

Read ( )
Metoda read ( ) este metoda de baza pentru fluxurile de intrare. Ea este folosita de o aplicatie pentru citirea datelor dintr-un flux. Metoda are mai multe variante, diferentele fiind legate, in principal, de cantitatea de date citite, de pozitia datelor in fluxul de intrare si de locul in care vor fi scrise. Alegerea variantei depinde de cerintele aplicatiei. Toate metodeleread ( ) folosesc blocarea la citire (blocking read), ceea ce inseamna ca atunci cand este apelata, o metoda read ( ) nu returneaza controlul pana la primirea tuturor datelor cerute sau pana la aparitia unei exceptii. Amintiti-va ca in cateva dintre exemplele anterioare, a fost citit un caracter de la tastatura. Acesta este un exemplu de blocare la citire. In cazul in care este necesara o functie similara, dar care sa nu faca blocarea la citire, aveti la dispozitie metode care verifica daca datele sunt disponibile inaintea operatiei de citire. Toate metodele read ( ) lanseaza exceptia IOException. Aceasta trebuie sa fie tratata de codul aplicatiei, prin folosirea unei structuri try/catch/finally sau prin adaugarea unei instructiuni throw in declaratia clasei. Iata care este sintaxa metodei read ( ): int read ( ); int read (byte[] buffer); int read (byte[] buffer, int offset, int length); Prima forma a metodei read ( ) incearca citirea unui singur octet dintr-un flux de intrare. Octetul citit este returnat chiar de functie, fiind convertit intr-un numar intreg (int). A doua forma a metodei read ( ) citeste datele intr-o zona tampon (buffer), care reprezinta o matrice de octeti. Metoda incearca sa citeasca un numar de octeti suficient pentru completarea matricei. Ultima versiune a metodei citeste

datele tot intr-o zona tampon, dar incepe citirea de la adresa indicata de parametrul offset si citeste un numar de octeti specificat de length. Toate versiunile metodei read ( ) returneaza un numar intreg, care indica numarul de octeti cititi. Daca nu exista date de citit, este returnata valoarea 1, ca in exemplul urmator: import java.io.*; public class MyMain { public static void main (String args[]) { FileInputStream s; //declara s ca flux de intrare pentru fisiere int rcode; //declara o variabila pentru codul returnat la citire try { //foloseste try pentru interceptarea exceptiilor la deschiderea fisierului s = new FileInputStream(args[0]); //deschide fluxul de intrare rcode = 0; while (rcode != -1) { //cicleaza citirea unui fisier pana la terminarea fisierului try { //foloseste try pentru interceptarea exceptiilor de In/Out rcode = s.read(); //citeste un caracter din fisier System.out.println("ch = " + (char)rcode);//afiseaza caracterul pe ecran } catch (IOException e) { //trateaza exceptiile de In/Out System.out.println("Eroare necunoscuta de In/Out " + args[0]); System.exit(2); } } } catch (FileNotFoundException e) { // trateaza exceptiile atunci cand nu este gasit fisierul System.out.println("File " + args[0] + " not found"); System.exit(1); } } } In acest exemplu, metoda read ( ) incearca sa citeasca octeti dintr-un flux de intrare de tip fisier. Numele fisierului este transmis programului ca parametru, la lansarea in executie din linia de comanda. Prin urmare, daca vreti ca programul sa citeasca dintr-un fisier numit sample.txt, trebuie sa apelati aplicatia MyMain astfel: java MyMain sample.txt In program, este declarata o variabila s de tip FileInputStream. Ea este apoi initializata prin deschiderea fisierului ca flux de intrare cu ajutorul operatorului new. In urma acestei operatii, fisierul este efectiv deschis. In cazul in care fisierul nu exista, in acest punct trebuie interceptata exceptia File not found. In continuare, este folosita o bucla while pentru citirea din fisier a unui singur octet pe rand, pana cand metoda read ( ) returneaza valoarea 1. Corpul buclei while incepe prin apelarea metodei read ( ) pentru citirea unui octet. Aceasta poate returna o exceptie IOException, asa incat este inclusa intr-un bloc try. Daca executia metodei read ( ) reuseste, octetul citit este afisat pe ecran.in caz contrar, o instructiune catch afiseaza un mesaj de eroare si aplicatia se incheie.

Acest exemplu ilustreaza manevrarea unui flux de intrare simplu. Ati vazut cum se deschide un flux de intrare, cum se citesc datele pe care le contine, cum se detecteaza sfarsitul fluxului si cum se trateaza exceptiile care pot aparea la deschiderea fluxului (open) sau la citirea datelor (read).

Skip ( )
Metoda skip ( ) este folosita pentru ignorarea unui anumit numar de octeti dintr-un flux de intrare, permitand programului sa il parcurga mai repede. Ea poate determina ocolirea datelor de care nu este nevoie si saltul intr-un anumit punct al fluxului. In cazul unui flux de intrare de la o baza de date, metoda skip ( ) determina saltul peste un numar stabilit de inregistrari astfel incat sa se ajunga la o anumita inregistrare. Daca este folosita cu un flux de intrare de tip fisier, functioneaza asemanator functiei lseek din C/C++. Metoda skip ( ) functioneaza numai in directia inainte. Nu poate fi folosita pentru a sari inapoi, la un punct din fluxul de intrare prin care programul a trecut deja. Unele fluxuri de intrare permit si aceasta operatie, dar folosesc metodele mark ( ) si reset ( ) (despre care vom discuta in sectiunile dedicate acestora). Ca si metoda read ( ), metoda skip ( ) lanseaza exceptia IOException. Aceasta trebuie sa fie tratata de codul aplicatiei, prin folosirea unei structuri try/catch/finally sau prin adaugarea unei instructiuni throw in declaratia clasei. Iata care este sintaxa metodei skip ( ): long skip (long num); Metoda skip ( ) accepta un singur argument, de tip long: numarul de octeti peste care se sare in fluxul de intrare. Deoarece fluxurile de date nu au o lungime fixa, in cazul in care este necesara ignorarea unui numar de octeti mai mare decat cel care poate fi reprezentat cu un argument de tip long, trebuie sa folositi mai multe instructiuni skip ( ). In momentul scrierii acestei carti (deci in versiunea Java 1.0), transmiterea catre metoda skip ( ) a unor argumente cu valori mari reprezinta o problema. Argumentul de tip long transmis metodei este convertit intern la un numar de tip int (intreg), deoarece metoda skip ( ) este implementata prin folosirea metodei read (byte[ ] ). Problema este ca matricele nu pot fi mai mari decat un numar de tip int. Ca urmare, daca sunt necesare salturi mai mari decat numarul care poate fi reprezentat cu un argument de tip int, trebuie sa folositi mai multe instructiuni skip ( ). Metoda skip ( ) returneaza un intreg de tip long, care indica numarul de octeti peste care s-a sarit. Daca la apelarea metodei skip ( ) s-a ajuns deja la sfirsitul fluxului de intrare, este returnata valoarea 1. Iata un exemplu de folosire a metodei skip ( ): public long skipRecords (int num) { int recordSize=512; //dimensiunea inregistrarii in octeti long rcode=0; try { rcode = s.skip(num*recordSize); //sare peste num inregistrari de 512 octeti if (rcode > 0) { rcode /= recordSize; //calculeaza inregistrarile peste care s-a sarit } } catch (IOException e) { // trateaza exceptiile de In/Out System.out.println( Eroare necunoscuta de In/Out in timpul saltului peste + num + inregistrari); }

return (rcode/recordSize); In acest exemplu, metoda skip ( ) este folosita pentru indexarea unui flux de intrare cu un numar de inregistrari de dimensiune fixa (512 octeti). Instructiunea try este folosita pentru interceptarea unor eventuale exceptii lansate in timpul executarii operatiei skip. Instructiunea catch afiseaza un mesaj de eroare in cazul generari unei exceptii IOException. Metoda returneaza numarul de inregistrari peste care s-a sarit, valoarea 1 daca metoda skip ( ) a fost apelata la sfirsitul fluxului de intrare sau valoarea 0 daca a aparut o exceptie. (Acest exemplu presupune ca obiectul flux s a fost deja deschis ca variabila globala inainte de lansarea metodei.)

Close ( )
Metoda close ( ) este folosita pentru inchiderea unui flux de intrare de care aplicatia sau multiaplicatia nu mai au nevoie. Java inchide automat fluxurile de intrare la iesirea din aplicatie sau multiaplicatie; este motivul pentru care nu am folosit pana acum metoda close ( ). Totusi, pentru formarea unor deprinderi corecte de programare, se recomanda inchiderea oricarei resurse de care aplicatia nu mai are nevoie. Ca si metodele read ( ) si skip ( ), metoda close ( ) lanseaza exceptia IOException. Aceasta trebuie sa fie tratata de codul aplicatiei, prin folosirea unei structuri try/catch/finally sau prin adaugarea unei instructiuni throw in declaratia clasei. Iata un exemplu de folosire a metodei close ( ): public long closeStream ( ) { try s.close ( ) ; //inchide fluxul de intrare catch (IOException e) { // trateaza exceptiile de In/Out System.out.println( Eroare necunoscuta de In/Out in timpul inchiderii fluxului de intrare); } } In acest exemplu, close ( ) este folosita intr-o metoda care incapsuleaza functionalitatile instructiunilor try si catch. Instructiunea try este folosita pentru interceptarea unor eventuale exceptii lansate in timpul executarii operatiei close ( ). Instructiunea catch afiseaza un mesaj de eroare in cazul generarii unei exceptii IOException.(Ca si in exemplul folosit pentru metoda skip ( ), se presupune ca s este un flux de intrare deja deschis ca variabila globala inainte de lansarea metodei.)

Available ( )
Metoda available ( ) este folosita pentru a determina daca o anumita cantitate de date poate fi citita fara blocarea fluxului de intrare si pentru a testa daca sunt disponibile destule date pentru prelucrare, inainte de apelarea unor alte metode, precum read ( ). Dar, metoda available ( ) nu returneaza informatii valide pentru toate fluxurile de intrare. Pentru unele returneaza intotdeauna valoarea 0, iar in cazul altora, valorile sunt inconsecvente. Aceasta se datoreaza necunoscutelor asociate procesului producator si implementarii interne a codului Java care manevreaza conexiunea dintre procesul producator si procesul consumator. In alte limbaje de programare, functionalitatea echivalenta metodei available ( ) este folosita pentru a avea siguranta ca datele care trebuie citite sunt disponibile, inaintea lansarii unei comenzi read ( ). Aceasta deoarece, de multe ori, instructiunea read ( ) blocheaza executia pana la terminarea operatiei de citire. Pentru aplicatiile care trebuie sa execute alte operatii de prelucrare pana cand toate

datele sunt disponibile, este singura modalitate de a va asigura ca aplicatia nu este blocata in asteptarea datelor. In Java, aceasta problema poate fi rezolvata prin folosirea firelor de executie. Atribuind un fir de executie pentru citirea datelor dintr-un flux de intrare, el poate fi blocat in asteptarea datelor, in timp ce alte fire de executie face ca metoda available ( ) sa fie, in multe cazuri, inutila. Acesta este si motivul pentru care rezultatele incorecte sau inconsecvente returnate de meroda available ( ) nu sunt considerate o mare problema. Iata care este sintaxa metodei available ( ): int available ( ) Metoda available ( ) returneaza un numar de tip int(intreg), care reprezinta numarul de octeti din fluxul de intrare ce pot fi cititi fara blocare. Ea nu accepta nici un parametru. Remarcati faptul ca valoarea returnata este de tip int. Fluxurile pot avea orice marime, dar metoda available ( ) returneaza o valoare de tip int, chiar daca numarul octetilor este mai mare. Iata un exemplu de folosire a metodei available ( ) : public boolean isRecordReady ( ) { int recordSize = 512; //dimensiunea inregistrarii in octeti boolean rcode = false; try { // testeaza daca sunt disponibili cel putin 512 octeti if ( s.available >= recordSize) { rcode = true; } } catch (IOException e) { //trateaza exceptiile de In/Out System.out.println ( Eroare necunoscuta de In/Out in timpul verificarii disponibilitatii unei Inregistrari ); } return(rcode); } In acest exemplu, metoda available ( ) este folosita pentru a determina daca in fluxul de intrare este disponibila o inregistrare imtrega cu dimensiunea de 512 octeti. Instructiunea try intercepteaza eventualele exceptii lansate in timpul executarii testului available. Instructiunea catch afiseaza un mesaj de eroare in cazul generarii unei exceptii IOException. Metoda returneaza valoarea de tip boolean true daca sunt disponibili cel putin 512 octeti si valoarea false in caz contrar. (Ca si in cazul exemplelor folosite in cazul metodelor skip ( ) si close ( ), se presupune ca s este un flux de intrare deja deschis ca variabila globala inainte de lansarea metodei.)

Metodele mark ( ) , markSupported ( ) si reset ( )


Metoda mark ( ) este folosita pentru a marca un anumit punct dintr-un flux de intrare, in vederea revenirii ulterioare. Ea nu este disponbila pentru toate tipurile de fluxuri de intrare. O alta metoda, numita markSupported ( ) determina daca un anumit flux de intrare o accepta. In cazul in care metoda mark ( ) este acceptata, puteti folosi metoda reset ( ) pentru a reveni la punctul marcat in fluxul de intrare. Metodele mark ( ) si reset ( ) au unele limitari care fac ca ele sa fie utile numai pentru anumite aplicatii. La folosirea metodei mark ( ), programul trebuie sa specifice cantitatea maxima de date care

poate fi citita inainte de apelarea metodei reset ( ). Daca se depaseste aceasta cantitate, metoda reset lanseaza o exceptie. Nu exista posibilitatea marcarii mai multor puncte intr-un flux de intrare. Daca metoda mark ( ) este apelata din nou inaintea metodei reset ( ), marcajul este mutat in noua pozitie, cu o noua limita maxima de date care pot fi citite inainte de folosirea metodei reset ( ). Iata sintaxa metodelor mark ( ), markSupported ( ) si reset ( ) : void mark (int readLimit); boolean markSupported ( ) ; void reset ( ); Metoda mark ( ) accepta un singur parametru, de tip int, care indica numarul de octeti peste care se poate trece pana la apelarea metodei reset ( ). Metoda markSupported ( ) nu are argumente; ea returneaza o valoare de tip boolean, indicand daca functia mark ( ) este disponibila pentru un flux de intrare dat. Metoda reset ( ) nu are argumente si nu returneaza nici o valoare. Ea nu face decat sa reinitializeze fluxul de intrare in punctul in care a fost apelata metoda mark ( ). Iata un exemplu de folosire a metodelor mark ( ), markSupported ( ) si reset ( ) : public static boolean checkForPostScriptHeader (int size) { byte buffer []; string txt; buffer = new byte[size]; if (s.markSupported ( ) == false) { System.out.println ( Fluxul de intrare nu permite folosirea marcajelor ); System.exit (1); } try s.read (buffer); catch (IOException e) { System.out.println ( Eroare necunoscuta de In/Out in timpul citirii fluxului de date ); System.exit (2); } try s.reset ( ); catch (IOException e) { System.out.println ( Eroare necunoscuta de In/Out in timpul reinitializarii fluxului de date ); System.exit (3); } txt = new String (buffer, size); if ((txt.indexOf (\n%!) != -1 || (txt.indexOf (%!) == 0)) return (true); else return (false); } Acest exemplu contine o metoda care verifica daca intr-un flux de intrare exista un antet postscript. In cazul ca exista, metoda returneaza valoarea true; daca nu exista, metoda returneaza valoarea false. Metodele mark ( ) si reset ( ) sunt folosit pentru reinitializarea fluxului de intrare in punctul original, astfel incat rutinele apelante sa il poata prelucra corespunzator. La inceput, se verifica daca metoda mark ( ) este disponibila pentru fluxul de intrare dat, folosind metoda markSupported ( ). Daca nu este disponibila, se forteaza iesirea din aplicatie. Daca metoda mark ( ) este disponibila, fluxul de intrare este marcat cu dimensiunea transmisa ca parametru. Aceasta permite aplicatiei apelante sa specifice pe ce lungime va fi verificat fluxul de intrare in cautarea

antetului postscript. Dupa marcare, fluxul de intrare este citit intr-un buffer. In cazul in care apare o exceptie de tipu IOException, metoda afiseaza pe ecran un mesaj de eroare si determina iesirea din aplicatie. Daca fluxul de intrare este citit fara probleme, se incearca reinitializarea acestuia in punctul in care se afla la apelarea metodei. Si de aceasta data se testeaza aparitia unei exceptii IOException, caz in care este afisat un alt mesaj de eroare si se iese din aplicatie. Dupa reinitializarea fluxului de intrare, bufferul este convertit intr-o variabila de tip sir de caractere, astfel incat sa se poata folosi metoda indexOf ( ) pentru localizarea antetului postscript. In sfarsit, o instuctiune if apeleaza metoda indexOf ( ) pentru localizarea antetului. Metoda este apelata de doua ori: prima data pentru a verifica daca exista o linie care incepe cu caracterele specifice antetului postscript, iar a doua oara pentru situatia particulara in care antetul se afla la inceputul fisierului si deci nu exista nici un caracter linie noua (\n) inaintea lui. Daca antetul este gasit, metoda returneaza valoarea true si exceptia se termina; in caz contrar, metoda returneaza valoarea false.

Fluxuri de iesire
Fluxurile de iesire (output stream) sunt fluxuri de date generate de un proces producator. Asa cum am aratat mai devreme, clasele pentru fluxuri si metodele asociate acestora se gasesc in pachetul java.io. toate aplicatiile si miniaplicatiile care folosesc fluxuri trebuie sa importe in mod explicit acest pachet la inceputul programului. In acest scop, se foloseste o instructiune import standard : import java.io.* ; Ca si in cazul fluxurilor de intrare, exista mai multe clase pentru fluxurile de iesire. Fiecare dintre ele are un obiectiv specific si, ca orice alta clasa, poate fi extinsa si personalizata de programator in functie de necesitati. In tabelul 8.4 este prezentata o lista completa a claselor Java pentru fluxuri de iesire. Tabelul 8.4.Clase pentru fluxuri de iesire BufferedOutputStream Scrie datele intr-o zona tampon (buffer), pentru a micsora numarul operatiilor de scriere efectiva din fluxul de iesire ByteArrayOutputStream Scrie datele intr-o matrice de octeti DataOutputStream FileOutputStream FilterOutputStream OutputStream PipedOutputStream PrintStream Scrie intr-un flux de iesire obiecte de date, nu siruri de octeti, precum celelalte clase Scrie date intr-un fisier Permite unui flux sa se conecteze la subclase proprii, care pot fi folosite pentru filtarea datelor Superclasa abstracta pentru toate clasele fluxului de iesire Scrie datele intr-un canal de redirectare catre un alt proces Scrie datele formatate pe ecranul utilizatorului

Tabelul 8.5 prezinta metodele asociate fluxurilor de iesire. Ca orice alte metode din Java, ele pot fi extinse si personalizate de programator. Tabelul 8.5. Metode pentru fluxurile de iesire write ( ) Scrie date intr-un flux de iesire flush ( ) Forteaza scrierea datelor intr-un canal de redirectare close ( ) Inchide un fluxul de iesire

Write ( )
Metoda write ( ) este metoda de baza pentru fluxuri de iesire. Ea este folosita de o aplicatie pentru scrierea datelor intr-un flux. Ca si metoda read ( ), metoda write ( ) are mai multe variante. Toate metodele write ( ) folosesc blocarea la scriere (blocking write), ceea ce inseamna ca, atunci cand este apelata, o metoda write ( ) nu returneaza controlul pana la acceptarea tuturor datelor sau pana la aparitia unei exceptii. Acest lucru poate cauza probleme in cazul in care programatorul doreste ca executia aplicatiei sa continue, iar scrierea datelor are o durata apeciabila. Solutia, ca si in cazul metodei read ( ), consta in folosirea firelor de executie. Daca metoda write ( ) apartine unui fir de executie separat, executia aplicatiei poate sa continue, indiferent de durata operatiei de scriere. Toate metodele write ( ) lanseaza exceptia IOException. Aceasta trebuie sa fie tratata de codul aplicatiei, prin folosirea unei structuri try/catch/finally sau prin adaugarea unei instructiuni throw in declaratia clasei. Iata care este sintaxa metodei write ( ): void write (int b); void write (byte[] buffer); void write (byte[] buffer, int offset, int length); Formele metodei write ( ) sunt similare celor ale metodei read ( ): prima forma incearca scrierea unui singur octet intr-un flux de iesire; cea de-a doua scrie datele intr-o zona tampon (buffer), care reprezinta o matrice de octeti. Ultima versiune a metodei scrie datele tot intr-o zona tampon, dar incepe scrierea de la adresa indicata de parametrul offset si scrie un numar de octeti specificat de length. Nici una dintre versiunile metodei write ( ) nu returneaza vreo valoare (sunt de tip void). Singura cale de a afla daca a aparut o eroare este interceptarea oricarei exceptii lansate. Iata un exemplu: import java.io.*; public class MyMain { public static void main (String args[]) { FileOutputStream s; //declare s to a file output stream int n,ix; //declare for loop variables try { //use try to catch file open exceptions s = new FileOutputStream(args[0]); //open the output stream for (n=0; n < 20; n++) { //outer for loop for (ix=0x30; ix < 0x7b; ix++) { //inner for loop try { //use try to catch IO exceptions s.write(ix); //write character to output stream } catch (IOException e) { //handle IO exceptions System.out.println("Unknown IO error writing file " + args[0]); System.exit(2); } } //bottom of inner loop try { s.write('\n'); } //write newline char to output stream catch (IOException e) { //handle IO exceptions System.out.println("Unknown IO error writing file " + args[0]);

System.exit(3); } } //bottom of outer loop } catch (IOException e) { //handle file open exceptions System.out.println("Unknown IO error opening file " + args[0]); System.exit(1); } } } In acest exemplu, metoda write ( ) este folosita pentru scrierea unor octeti intr-un flux de iesire de tip fisier. Numele fisierului este transmis programului la lansarea in executie, din linia de comanda. In program, este declarata o variabila s de tip FileOutputStream. Ea este apoi initializata prin deschiderea fluxului de iesire cu ajutorul operatorului new. In urma acestei operatii, este creat un nou fisier cu numele specificat in linia de comanda sau este suprascris un fisier existent cu acelasi nume. In ambele situatii, fisierul este deschis. In cazul in care fisierul nu este accesibil, in acest punct trebuie interceptata exceptia IOException. In continuare, sunt folosite doua bucle for imbricate, pentru scrierea unei serii de octeti in fluxul de iesire. Ciclul interior scrie in fisier toate caracterele ASCII, de la a la z. Corpul ciclului incepe prin apelarea metodei write ( ), pentru scrierea unui caracter in fluxul de iesire. Aceasta poate returna o exceptie IOException, asa incat este inclusa intr-un bloc try. Daca executia metodei write ( ) esueaza, o instructiune catch afiseaza un mesaj de eroare si aplicatia se termina. Ciclul extern determina executarea repetata a ciclului intern de 20 de ori. Dupa fiecare incheiere a ciclului intern, ciclul extern scrie un caracter newline in fluxul de iesire. In felul acesta, fisierul rezultat devine mai lizibil. Acest exemplu ilustreaza manevrarea unui flux de iesire simplu. Ati vazut cum se deschide un flux de iesire, cum se scriu datele in cadrul lui si cum se trateaza exceptiile care pot aparea la deschiderea fluxului (open) sau la scrierea datelor(write).

Flush ( )
Metoda flush ( ) este folosita pentru a forta scrierea catre dispozitivul de iesire a datelor stocate in zona tampon pentru un flux de iesire. Unele fluxuri pot fi conectate la procese consumatoare lente sau chiar blocate, caz in care metoda write ( ) poate bloca executia pentru o perioada lunga de timp, daca nu chiar la infinit. In aceste situatii, metoda flush ( ) poate fi folosita pentru a forta mediul Java sa accepte datele. Java poate stoca aceste date intr-o zona tampon, asteptand ca procesul consumator sa le accepte sau sa dispara, inchizand astfel canalul de redirectare. Retineti ca daca o operatie write este blocata, singura modalitate de a forta scrierea datelor este apelarea metodei flush ( ) dintr-un alt fir de executie. Acesta este un motiv in plus pentru a plasa apelurile fluxurilor de intrare/iesire in fire de executie proprii. Iata care este sintaxa metodei flush ( ): void flush ( ); Ca si metoda write ( ), metoda flush ( ) lanseaza exceptia IOException. Aceasta trebuie sa fie tratata de codul aplicatiei, prin folosirea unei structuri try/catch/finally sau prin adaugarea unei instructiuni throw in declaratia clasei, ca in exemplul urmator:

public void flushStream ( ) { try { s.flush();} //inchide fluxul de iesire catch (IOException e) { //trateaza exceptiile de intrare/iesire System.out.println( Eroare necunoscuta de intrare/iesire in timpul golirii fluxului de iesire ); } } In acest exemplu, metoda flush ( ) este folosita intr-o metoda care incapsuleaza functionalitatile instructiunilor try si catch. Instructiunea try intercepteaza eventualele exceptii lansate in timpul executarii operatiei flush ( ). Instructiunea catch afiseaza un mesaj de eroare in cazul generarii unei exceptii IOException. (Acest exemplu presupune ca s este un flux de intrare deja deschis ca variabila globala inainte de lansarea exceptiei.)

Close ( )
Este similara cu metoda close ( ) folosita pentru fluxurile de intrare. Ea este folosita pentru inchiderea unui flux de iesire de care aplicatia sau miniaplicatia nu mai are nevoie. La iesirea din aplicatie sau miniaplicatie, Java inchide automat fluxul de iesire; este motivul pentru care, in exemplele precedente, metoda close ( ) nu a fost folosita. Totusi, pentru formarea unor deprinderi corecte de programare, se recomanda inchiderea oricarei resurse de care aplicatia nu mai are nevoie. Iata care este sintaxa metodei close ( ): void close ( ) ; Ca si metodele write ( ) si flush ( ), metoda close ( ) poate lansa exceptia IOException. Aceasta trebuie sa fie tratata de codul aplicatiei, prin folosirea unei structuri try/catch/finally sau prin adaugarea unei instructiuni throw in declaratia clasei. Iata un exemplu de folosire a metodei close ( ): public void closeStream ( ) { try { s.close( );} //inchide fluxul de iesire catch (IOException e) { //trateaza exceptiile de intrare/iesire System.out.println( Eroare necunoscuta de intrare/iesire in timpul inchiderii fluxului de iesire); } } In acest exemplu, metoda flush ( ) este folosita intr-o metoda care incapsuleaza functionalitatile instructiunilor try si catch. Instructiunea try intercepteaza eventualele exceptii lansate in timpul executarii operatiei close . Instructiunea catch afiseaza un mesaj de eroare in cazul generarii unei exceptii IOException. (Acest exemplu presupune ca s este un flux de intrare deja deschis ca variabila globala inainte de lansarea exceptiei.)

Pe scurt, despre fluxuri


Fluxurile reprezinta modalitatea folosita in limbajul Java pentru citirea si scrierea datelor in entitati din afara aplicatiei, cum ar fi fisiere, retele, dispozitive sau procese. Toate fluxurile Java reprezinta secvente de octeti (8 biti).Unele clase pentru fluxuri permit si scrierea altor tipuri de obiecte

dar acestea nu fac decat sa converteasca intern obiectele respective in secvente de octeti. Fluxurile pot fi clasificate in doua mari categori:fluxuri de intrare si fluxuri de iesire. Fluxurile de intrare accepta date de la o sursa externa. Procesele care primesc date de la fluxuri de intrare se numesc consumatori. Metodele disponibile sunt: read ( ), skip ( ), markSupported ( ) si close ( ), care functioneaza numai cu anumite tipuri de fluxuri de intrare. Metoda read ( ) este folosita pentru citirea datelor dintr-un flux de intrare. Metoda skip ( ) determina ignorarea unor date din fluxul de intrare pentru a ajunge la date aflate intr-o alta secventa a fluxului. close ( ) inchide fluxul de intrare. Metoda available ( ) poate fi folosita pentru a determina daca in flux sunt disponibile un numar de date, dar rezultatele returnate de aceasta metoda nu sunt intotdeauna corecte. Metoda markSupported ( ) determina daca un anumit flux de intrare permite folosirea metodelor mark ( ) si reset ( ). Acestea sunt folosite oentru marcarea unui punct in fluxul de intrare si reinitializarea fluxului in punctul respectiv. Fluxurile de iesire produc date pentru o destinatie externa. Procesele care creeaza fluxuri de iesire sunt cunoscute sub numele de producatori. Metodele disponibile sunt: write ( ), flush ( ) si close ( ). Metoda write ( ) determina scrierea datelor intr-un flux de iesire. Metoda flush ( ) este folosita pentru a forta scrierea in fluxul de iesire a datelor pastrate temporar in zona de memorie tampon(buffer). Metoda close ( ) inchide un flux de iesire. Prin folosirea fluxurilor de intrare si de iesire, apliactiile si miniapliactiile Java stabilesc punti de legatura cu lumea exterioara.

Rezumat
In acest capitol, au fost prezentate piesele finale ale limbajului de programare Java: firele de executie, exceptiile si fluxurile. Sectiunea despre fire de executie trateaza problemele executiei simultane a mai multor secvente de cod. Sunt discutate metodele folosite pentru crearea, initializarea,lansarea,oprirea si distrugerea unui fir de executie. Firele de executie sunt elemente puternice ale limbajului de programare Java, iar programatorii care stiu sa le foloseasca pot scrie programe mai performante, mai robuste si mai prietenoase cu utilizatorul. Sectiunea despre exceptii discuta folosirea exceptiilor in Java, care inlocuiesc codurile de eroare returnate in alte limbaje de programare. Exceptiile va pun la dispozitie o modalitate mai robusta si mai flexibila pentru tratarea erorilor. Sunt discutate metodele de interceptare si tratare a exceptiilor, precum si cele de generare a unor exceptii noi pesonalizate. Fiind obiective Java, exceptiile beneficiaza de toate avantajele refolosirii si personalizarii codului. In sfarsit, sectiunea despre fluxuri prezinta modalitatile de folosire a acestor constructii de comunicare cu lumea exterioara. Fluxurile sunt secvente de octeti. Prin folosirea unui format de date atat de simplu, aplicatiile Java nu trebuie sa aiba prea multe informatii despre sursa datelor citite sau despre destinatia datelor scrise. Aceasta conduce la crearea unui cod mai simplu si usor de refolosit. In aceasta sectiune, sunt discutate metodele pentru deschiderea, citirea, scrierea si inchiderea unui flux de date, precum si unele concepte mai complexe, cum ar fi recitirea unei parti dintr-un flux de date. Fluxurile Java reprezinta o metoda flexibila si, cel mai important, standardizata de comunicare cu mediul extern. Aici am incheiat discutarea elementelor de baza ale limbajului de programare Java. Cu setul de instrumente prezentate pana in acest moment, puteti crea aplicatii Java utile. In partea a patra, Interferenta Java de programare a aplicatiilor, vom discuta despre bibliotecile de clase din Java.