Sunteți pe pagina 1din 90

Universitatea TRANSILVANIA din Braov Facultatea de Matematic i Informatic Catedra de Informatic Aplicat

Ini iere n programarea orientat pe obiecte din perspectiv Java

Dorin Bocu

...O parte din eforturile tiin ei calculatoarelor sunt dedicate mbunt irii permanente a paradigmelor de modelare. Prin istoric i prin realizrile de ultim or, ingineria softului nu face dect s confirme aceast aser iune. Modelarea orientat pe obiecte este un exemplu remarcabil de instrument, gndit pentru a fi utilizat n realizarea de sisteme soft, competitive din punct de vedere al pre ului i al calit ii. Programarea orientat pe obiecte ngduie fanilor ei s verifice, n practic, for a unui stil de a modela, a crui capacitate de a mapa domeniul problemei peste domeniul solu iei este cu mult superioar altor paradigme.

Cuvnt nainte al autorului Au trecut ani buni de cnd lumea a nceput s ntrebuin eze, n vorbire i n alte contexte, sintagma programare obiect orientat sau, ceva mai aproape de spiritul limbii romne, programare orientat pe obiecte. Pregtit i anun at de numeroasele controverse pe marginea preamultelor slbiciuni ale vechilor paradigme de programare, orientarea pe obiecte s-a instalat confortabil de-a lungul ntregului proces de dezvoltare a unui sistem soft, devenind n zilele noastre religia care guverneaz att etapa de modelare a unui sistem soft ct i etapa de implementare. Au aprut limbaje, precum i medii de programare i dezvoltare a sistemelor soft, a cror arhitectur este esen ial tributar ideii de orientare pe obiecte (Java, C++, C#, Object Pascal cteva exemple mai cunoscute de limbaje, Delphi, C-Builder, Visual C++ - cteva exemple mai cunoscute de medii de programare, Rational Rose, ObjectiF dou dintre mediile de dezvoltare cu bun rspndire n lumea ingineriei softului). Este clar, orientarea pe obiecte nu este doar o mod n programare, ci o modalitate, fr rival, pentru moment, de a dezvolta sisteme soft. Pentru informaticienii a cror practic se reduce la apropierea ct mai rapid de tastatur pentru a rezolva o problem, orientarea pe obiecte este o ncercare care tulbur min ile i ntrzie rezolvarea problemei. Seriile de studen i care mi-au trecut prin mn mi-au ntrit convingerea c nsuirea spiritului orientrii pe obiecte este o problem destul de dificil, deoarece, aproape tot ceea ce este reclamat de obnuin ele omului n materie de nv are, este dificil de opera ionalizat cnd este vorba de nsuirea acestui spirit. Mult mai apsat dect n alte paradigme, n orientarea pe obiecte, specialistul trebuie s acorde aten ia cuvenit elaborrii solu iei nainte de a o implementa. Metodele cele mai rspndite de nv are a limbajelor de programare se bazeaz pe formula: Prin aplica ii, spre descoperirea subtilit ilor sintactice, semantice i pragmatice ale unui limbaj de programare. Cititorul atent a n eles faptul c nv area unui limbaj de programare este un exerci iu de voin care presupune parcurgerea a trei etaje: nv area stereotipurilor sintactice fundamentale ale limbajului (sintaxa limbajului) Descoperirea unui numr ct mai mare de semantici primitive, care pot fi nvelite cu sintaxa limbajului (semantica limbajului) Formarea unor deprinderi de utilizare eficient a limbajului, n func ie de natura i complexitatea problemei, pentru rezolvarea respectivei probleme (pragmatica limbajului).

Ideea cluzitoare a acestui support de curs este de a invita cititorul s nve e singur sintaxa orientrii pe obiecte, ncercnd s l ajute, ndeosebi n efortul de deconspirare a semanticii i pragmaticii orientrii pe obiecte. Trimiterile de natur sintactic vor fi raportate la limbajul Java. Trebuie s mrturisesc, totodat, faptul c, n viziunea acestei lucrri, fiecare cititor este o instan cognitiv activ, capabil de efort permanent de abstractizare, singura modalitate de a ajunge la o n elegere superioar a esen ei unei paradigme de modelare i, n particular, de programare. Atitudinea de spectator, cu instinct de conservare puternic, la derularea ideilor din aceast lucrare, este absolut contraproductiv pentru atingerea obiectivului fundamental: nv area ct mai multor elemente suport, esen iale pentru modelarea / programarea orientat pe obiecte a solu iei unei probleme. Ce se va ntmpla cu cei care nu se vor putea mpca cu ideea de a se dezmor i voluntar, iat o problem n legtur cu care prefer s spun doar att: nu peste mult timp vor descoperi c au mbtrnit nainte de vreme, fr a fi n eles mare lucru despre plcerea de a uita de scurgerea timpului, realiznd lucruri care s uimeasc, deopotriv, pe al ii i pe propriul lor creator. Referindu-m la studen ii pentru care, n principal, am scris aceast lucrare, trebuie s spun c, n viziunea mea, ideea de student se confund cu imaginea unui individ care are apeten pentru studiu. Dac, n facultate, se mai studiaz i discipline care, unora li se par, nefolositioare, datoria studentului este s fac efortul de a se identifica cu ideile fundamentale ale unui numr ct mai mare de discipline, socotite de el folositoare. Dac, n facultate, studen ii se mai ntlnesac i cu profesori ale cror cutri nu sunt nc suficient de bine sistematizate, acesta nu este un motiv de a renun a la dorin a de a cunoate.

De la natur, studentul poate fi asimilat cu un obiect care are toate capabilit ile necesare pentru a parcurge drumul dificil, dar pasionant, al cunoaterii elementelor fundamentale, n diferite ramuri ale matematicii i tiin ei calculatoarelor. A aminti, tuturor celor care nu au realizat nc, faptul c omul are nevoie de tiin pentru a n elege o parte din realitatea necunoscut (rezult c tiin a are func ie explicativ), pentru a modela comportamentul unor fenomene i procese din realitatea obiectiv, n vederea optimizrii dinamicii lor (rezult c tiin a are func ie modelatoare), pentru a mbog i realitatea obiectiv cu obiecte artificiale (rezult c tiin a are func ie demiurgic). Sper ca cititorul s n eleag bine rolul activ pe care trebuie s i-l asume n deconspirarea, prin studiu individual i exerci ii perseverente, a etajelor sintactice esen iale programrii n Java.

1 Cum se explic permanenta nevoie de paradigme noi n ingineria softului. Ce n elegem prin orientarea pe obiecte
1.1 Cum se explic permanenta nevoie de paradigme noi n ingineria softului?
Binefacere sau blestem, omenirea este, asemenea ntregului univers, ntr-o continu deplasare spre alte repere ontologice i gnoseologice. Avea dreptate Heraclit din Efes cnd, folosind cuvinte meteugit alese, constata c singura certitudine a universului pare a fi devenirea. Dac, acum peste 2500 de ani n urm, devenirea ocupa o pozi ie central n modul de gndire al lui Heraclit, n zilele noastre devenirea s-a transformat n izvor nesecat i cauz a tuturor transformrilor importante pe care le suport universul cunoscut omului. S-ar prea c nimic din ceea ce face omul nu dureaz. Acest fapt este, dup ascu imea sim urilor noastre, n opozi ie cu ceea ce Natura sau Marele Creator fac. Omul aspir la eternitate luptnd cu efemerul inerent al crea iilor lui. Marele Creator este eternitatea nsi. Las pe seama filozofilor continuarea efortului de preamrire sau punere la index a rolului pe care l joac devenirea n via a omului i, de ce nu, n univers. Caracterul obiectiv al devenirii poate explica, n genere i nevoia de paradigme1 noi n ingineria softului i, n particular n programare. * Permi ndu-mi o scurt digresiune, ingineria softului este o ramur a tiin ei calculatoarelor care se ocup, la urma urmei, de studierea unei probleme extrem de delicate: Dat o problem oarecare, ce trebuie fcut pentru a o rezolva cu ajutorul calculatorului? Confrunta i de-a lungul timpului, cu diferite tipuri de probleme, specialitii n ingineria softului au fcut o descoperire banal: Efortul depus pentru a rezolva o problem cu ajutorul calculatorului este direct propor ional cu complexitatea problemei. Odat fcut aceast descoperire iese la iveal alt ntrebare: Cum definim complexitatea unei probleme? Aten ie, cititorule, este vorba de complexitatea unei probleme nu de complexitatea solu iei algoritmice a unei probleme. Complexitatea unei probleme este o caracteristic intrinsec a enun ului asumat al acesteia. Complexitatea solu iei algoritmice a unei probleme este o caracteristic intrinsec a modului n care o anumit instan cognitiv (de pild un student din anul II) ob ine respectiva solui ie algoritmic. Este, sper, ct se poate de clar faptul c cele dou tipuri de complexitate cuantific caracteristicile structurale ale unor colec ii de obiecte, diferite din punct de vedere al sferei de cuprindere i al con inutului. Cei care au parcurs unul sau mai multe cursuri de ini iere n Algoritmic i Programare i amintesc, probabil, n elesul pe care l are complexitatea unui algoritm. Formalizarea matematic a complexit ii algoritmilor este un prilej minunat de a ilustra for a de analiz a matematicii de puterea continuului, aplicat universului algoritmilor, al cror comportament este eminamente discret. Este corect s eviden iem, n acest punct, utilitatea teoretic i practic (ndeosebi n cazul aplica iilor critice relativ la complexitatea algoritmilor folosi i) a eforturilor de clasificare a algoritmilor secven iali i paraleli, n func ie de complexitatea lor Acest gen de afirma ii sunt prezentate n toat cuprinderea i adncimea lor n cr i fundamentale pentru nv rea programrii calculatoarelor2. Ingineria softului (IS) ia act de aceast realitate i, n replic, i concentreaz aten ia asupra complexit ii problemelor. O ncercare de a eviden ia elementele cu ajutorul crora putem descrie complexitatea unei probleme n ingineria softului ne conduce la seria de observa ii de mai jos. Rezolvarea unei probleme cu ajutorul calculatorului nseamn, de cele mai multe ori, simularea / asistarea de ctre calculator a unor activit i, desfurate de ctre sisteme de alt natur. Aadar,

1 Este cazul s spunem c, prin paradigm se n elege, fr a exagera cu explica iile, un mod de abordare a problemelor dintr-un anumit domeniu, evident, cu scopul de a rezolva aceste probleme. Poate c limba romn nu avea neaprat nevoie de acest cuvnt, dar insisten a cu care este utilizat n alte pr i poate fi considerat o scuz pentru utilizarea lui n aceast carte. 2 Knuth, Cormen

aproape invariabil, ne aflm n fa a unor dificult i care rezult din caracterul instabil al rela iei dintre model i sistemul modelat. Neglijarea acestui aspect simplific efortul de realizare, n cele din urm, a unui sistem soft, dar pre ul pltit este exprimat, sintetic, astfel: uzur moral rapid i posibilit i de adaptare reduse. Se poate lupta cu mijloace ra ionale mpotriva caracterului instabil al rela iei dintre model i sistemul modelat? Rspunsul este: da, se poate lupta, dac specialistul n IS este dispus s anticipeze impactul posibilelor schimbri ale sistemului modelat asupra solu iei (modelului). Anticipnd schimbrile sistemului modelat, simplificm efortul de adaptare a solu iei la aceste schimbri. Modelul care st la baza solu iei unei probleme abstractizeaz viziunea specialistului sau grupului de specialiti n IS, asupra sistemului modelat. Semn al posibilit ilor noastre limitate de a cunoate, orice viziune nu poate dect s aproximeze comportamentul sistemului modelat. Chiar i n aceste condi ii, de la a aproxima defectuos pn la a aproxima inspirat este nu doar un drum lung ci i plin de diverse tipuri de ncercri. Exemple de astfel de ncercri: specificarea complet a cerin elor func ionale; acordarea aten iei cuvenite cerin elor non-func ionale; alegerea inspirat a modalit ilor de armonizare a cerin elor contradictorii; alegerea inspirat a paradigmei de modelare, etc. Trecerea, cu succes, peste dificult ile schi ate mai sus este realizat n condi ii ergonomice dac managementul acestor dificult i (plus nenumrate altele) este tratat cu maximum de seriozitate i competen . ncercnd s for m o concluzie, complexitatea unei probleme n IS este influen at de: 1. 2. 3. Stabilitatea rela iei model-sistem modelat; lipsa de stabilitate n aceast rela ie, asumat contient, induce creterea complexit ii problemei de rezolvat. Complexitatea structurii sistemului modelat (creterea acesteia induce, complexit ii problemei de rezolvat). de asemenea, creterea

Desfurarea n condi ii de eficien a procesului de rezolvare a unei probleme, adaug un plus de complexitate oricrei probleme. Neasumndu-se acest plus de complexitate, putem uor compromite procesul de realizare a unui sistem soft (=solu ia executabil pe un calculator real a unei probleme date). Un management bun are de rezolvat probleme de procurare i alocare optim a resurselor, comunicare ntre partenerii de proiect (=specialitii n IS, beneficiarii, utilizatorii), lansare cu succes pe pia (dac este cazul), etc. Nu a sftui pe nici un specialist n IS s considere normal o afirma ie de genul: Dac cele spuse la punctul 2 sunt tratate cu maximum de seriozitate i competen , 1 i 3 nu mai nseamn mare lucru. ndeosebi n cazul proiectelor mari, s-a dovedit, de nenumrate ori, neadevrul unei astfel de afirma ii.

n IS, complexitatea problemelor nu este un accident; mai mult, putem spune c este legitim s ne ateptm la elemente de complexitate chiar i acolo unde s-ar prea c acestea sunt lips. Dac mai amintim cititorului observa ia tenden ioas, potrivit creia complexitatea de calitate se ntemeiaz pe simplitate, acesta n elege mai bine insisten a cu care prezint implica iile complexit ii unei probleme asupra calit ii solu iei acestei probleme. Calitate? Complexitate3? Complexitate de calitate? Multe se mai pot spune. Specialistul n IS dorete s tie cteva lucruri simple: Care sunt primejdiile? Care sunt exigen ele? Cum se poate evita un eec?, etc.

Astfel de ntrebri, ne vom pune, ntr-o form sau alta i n aceast lucrare de ini iere n programarea orientat pe obiecte i le pute i gsi reluate n cr ile Ini iere n ingineria sistemelor soft i Ini iere n

3 Orice exerci iu de modelare urmrete, indiferent de acuitatea mijloacelor de investiga ie, un anumit mod de organizare a complexit ii sistemului modelat.

modelarea obiect orientat a sistemelor soft utiliznd UML, scrise de D. Bocu i aprute la Editura Albastr. Nevoia presant de a cuta, permanent, rspunsuri noi la astfel de ntrebri, justific avalana de nout i care caracterizeaz i lumea limbajelor de modelare, specifice ingineriei softului.

1.2 Ce se n elege prin orientarea pe obiecte?


S-au scris destule cr i pe tema orientrii spre obiecte. i mai multe sunt, probabil, articolele din revistele de specialitate. Unele, foarte convingtoare. Altele mai pu in. Nu cred c voi umple, brusc, golul care mai exist, nc, n limba romn, scriind aceast carte. Dar voi ncerca s fac n eles modul de gndire al unui specialist n IS, care vede realitatea informa ional n termeni specifici orientrii spre obiecte. Orice sistem informa ional4 este mai uor de n eles dac avem elemente suficiente referitoare la datele vehiculate n sistem, procedeele de prelucrare (vehiculare) a datelor din sistem, interfe ele sistemului cu mediul n care acesta opereaz. Fiecare dintre elementele specificate mai sus are reguli proprii de organizare i fiin are. n plus, mai exist i rela iile strnse dintre aceste elemente. De fapt, ntreaga istorie a IS se nvrte n jurul ecua iei prezentat n Figura 1.

<Solu ia unei probleme>

= <Organizarea datelor> + <Organizarea prelucrrilor> + <Optimizarea interfe elor>

Figura 1. Ecua ia general a solu iei unei probleme n IS.


Ecua ia de mai sus a primit de-a lungul timpului numeroase rezolvri. * nainte de a descrie tipurile fundamentale de rezolvri, voi efectua o scurt trecere n revist a vocabularului IS, esen ial n comunicarea dintre partenerii unui proiect de dezvoltare a unui sistem soft. Prin urmare, o firm productoare de soft produce i livreaz produse i servicii care se adreseaz nevoilor i cerin elor clien ilor. Cerin ele clien ilor constitue un exemplu de problem cu care trebuie s se confrunte echipa de dezvoltare. Produsele i serviciile care satisfac aceste cerin e pot fi considerate ca fiind solu ii. Pentru a livra solu ii valide (= de calitate, la pre de cost redus i n timp util) firmele trebuie s fac permanent achizi ie, comunicare (partajare) i utilizare de cunotin e specifice referitoare la domeniul problemei de rezolvat. Pentru a discrimina i comunica eficient informa iile necesare pentru rezolvarea unei probleme, firmele folosesc tehnologii adecvate. O tehnologie este, n general vorbind, un instrument pe care membrii echipei de dezvoltare trebuie s nve e s-o foloseasc la ntregul ei poten ial. Aadar, problema de rezolvat, la care se adaug necesitatea de a nv a modul de utilizare a tehnologiilor necesare n rezolvarea problemei, ne permit o imagine, nc optimist, asupra complexit ii efortului de realizare a unui sistem soft. Amplificarea acestei complexit i este motivul pentru care ntregul efort de realizare a unui sistem soft este structurat sub forma unui proiect. n cadrul unui proiect se desfoar, dup reguli precise, toate activit ile necesare pentru a asigura succesul efortului de realizare a unui sistem soft. Dou dintre dimensiunile esen iale pentru succesul unui proiect sunt limbajul de modelare folosit i tipul de proces utilizat pentru a materializa for a limbajului de modelare. Despre toate acestea, mai multe aspecte la cursul care i propune ini ierea studen ilor n ingineria sistemelor soft. * Revenind la ntrebarea de baz a acestui paragraf, s observm c, nc de la apari ia primelor calculatoare, a aprut o nou dilem n fa a omenirii: Cum putem nv a calculatoarele s ne rezolve n mod optim problemele?

4 Ca i realitatea informa ional, sistemul informa ional desemneaz un ansamblu de resurse care optimizeaz fluxurile informa ionale dintrun sistem gazd. Pentru o documentare mai atent invit cititorul s rsfoiasc atent o carte bun de ingineria softului.

La nceput, cnd calculatoarele erau folosite exclusiv pentru rezolvarea unor probleme cu carater tiin ific, solu ia era programarea n cod main. Dificult ile de baz n rezolvare acestor tipuri de probleme erau dou :elaborarea modelelor matematice i transpunerea lor n cod main. Mai ales programarea n cod main, era un gen de exerci iu la care nu se ngrmdeau dect indivizi care manifestau un interes ieit din comun pentru sistemele electronice de calcul din acea genera ie. Apari ia limbajelor de asamblare a generat o relaxare a cortinei de fier instalat de codul main ntre calculatoare i marea mas a curioilor. O relaxare asemntoare s-a produs i n ceea ce privete tipurile de probleme abordabile cu ajutorul calculatoarelor. Era momentul n care intrau pe scen aplica iile de gestiune, datorit apari iei memoriilor externe. Locul datelor simple ncepe s fie luat de date structurate i din ce n ce mai voluminoase. Presiunea exercitat de cererea crescnd de aplica ii de gestiune a impus trecerea la limbajele de nivel nalt i mediu. n paralel, cu deosebire n fa a proiectelor uriae (controlul traficului aerian, gestiunea tranzac iilor unor bnci, informatizarea tot mai multor activit i ale intreprinderilor industriale) apare nevoia disciplinrii procesului de derulare a acestor proiecte din faza de specificare a cerin elor, trecnd prin faza de analiz, continund cu proiectarea solu iei i terminnd (grosier vorbind) cu programarea. De unde aceast nevoie de disciplinare? Foarte simplu, complexitatea problemelor (parc am mai auzit undeva despre acest lucru) nu mai putea fi stpnit lucrnd fr metod. Astfel au aprut, de-a lungul timpului, tot felul de mode n ceea ce privete abstractizarea solu iei i nu numai. Managementul unui proiect trebuie s aib, permanent, n vizor, cel pu in, asigurarea limbajului de modelare adecvat i a modelului de dezvoltare optim5. De la moda programelor-mamut (singura posibil n vremea programrii n cod main) s-a ajuns treptat la nevoia modularizrii (= descompunerea problemei ini iale n subprobleme, eventual aplicarea repetat a acestui procedeu, ob inndu-se nite piese ale solu iei numite module, care, asamblate, compuneau, n sfrit solu ia). ntrebarea care a furnicat, ani la rnd, creierele teoreticienilor i practicienilor, deopotriv, era: Care sunt criteriile dup care se face modularizarea unei solu ii? Exist o serie de factori externi ai calit ii unui sistem soft care in sub presiune tendin a de a modulariza de amorul artei, precum: corectitudinea, robuste ea, reutilizabilitatea, portabilitatea, etc. Exist i o serie de factori interni ai calit ii unui sistem soft, care preseaz, n egal msur, precum: structurarea solu iei, claritatea algoritmilor, calitatea arhitecturii, etc. Men inerea n echilibru a acestor factori de presiune este o sarcin extrem de dificil. n cele din urm, cercettorii au ajuns la concluzia c, din punct de vedere al celui care lucreaz, materializarea acestor factori la parametri satisfctori sau marginalizarea lor, depind de decizia luat n ceea ce privete rela ia dintre date i prelucrri n procesul de modularizare a solu iei unei probleme. Istoria ne arat c au existat teoreticieni i practicieni ncredin ati c singura metod de modularizare valid este modularizarea dirijat de date (altfel spus, nainte de a te ocupa de organizarea prelucrrilor, rezolv, n mod optim, problema organizrii datelor; din schema de organizare a acestora se va deriva i schema de structurare a prelucrrilor). Tot istoria ne arat c au existat i teoreticieni i practicieni ncredin ati c singura metod de modularizare valid este modularizarea dirijat de prelucrri (altfel spus, ocup-te, mai nti, de organizarea prelucrrilor i, mai apoi, f rost de datele necesare i organizeaz-le conform cerin elor prelucrrilor). Curnd s-a observat c dihotomia date-prelucrri nu este benefic sub nici o form. A existat o solu ie de compromis (modularizarea dirijat de fluxurile de date), care s-a dovedit n timp nesatisfctoare n numeroase situa ii din realitate. Din punct de vedere al criticilor fundamentale care se pot formula la adresa acestor abordri esen a este urmtoarea: Dac solu ia unei probleme nseamn ansamblul date-prelucrri, fiecare component avnd o existen relativ autonom i reguli proprii de organizare, atunci avem situa ia din Figura 2. Esen ial n mesajul transmis de Figura 2 este faptul c datele sunt sficient de izolate de prelucrri pentru ca o modificare n structura unei componente s dea uor peste cap structura celeilalte componente. Conform paradigmelor caracterizate n Figura 2, era posibil o caracterizare de tipul celei prezentate pentru problema de mai jos. Problema 1: S se realizeze un sistem soft care simuleaz deplasarea unui om pe o suprafa plan.

Mai multe detalii n aceast privin n cartea Ini iere n ingineria sistemelor soft, D. Bocu, Editura Albastr, Cluj-Napoca, 2002

DATE Problem (activitate-sistem real al crui comportament trebuie modelat)

PRELUCRRI

Figura 2 Perspectiva clasic asupra rela iei dintre date i prelucrri n structura unei solu ii
Dac a fi un partizan al modularizrii dirijate de date, mai nti mi-a pune urmtoarele ntrebri: care sunt atributele informa ionale care caracterizeaz un om (stilizat convenabil, s spunem)?. Cum se caracterizeaz un plan? Cum se memoreaz intern i extern datele despre aceste obiecte? Dup ce am distilat satisfctor lumea datelor, ncep s m ocup i de lumea prelucrrilor necesare n jurul acestor date. Ob in, inclusiv n viziunea limbajelor de programare suport, dou lumi, care interfer, dar au reguli de organizare i reprezentare n memorie distincte. Mai trist, legtura dintre date i prelucrri este o problem care se gestioneaz prin programare, ca i cnd nu ar fi suficiente problemele celelalte. Ce ne facem, ns, dac apare necesitatea simulrii deplasrii unei vie uitoare n genere, pe o suprafa plan? Pentru fiecare vie uitoare n parte iau travaliul de la capt? Posibil, dar total contraindicat din multe puncte de vedere( pre de cost, extensibilitate, ncadrare n termenele de execu ie, etc.). Este limpede, reformulat ca mai sus, enun ul problemei ne pune n fa a sarcinii de a modela comportamentul unor obiecte, heterogene ca tip, dar ntre care exist afinit i, att de natur informa ional ct i comportamental. Astfel apare, ceea ce n modelarea obiect orientat se numete problema gestiunii similarit ilor unor colec ii de obiecte. Gestiunea corect a similarit ilor unei colec ii de obiecte, heterogene din punct de Solu ia orientat pe obiecte a problemei vedere al tipului definitor, se realizeaz desfurnd n paralel efort de clasificare i, acolo unde este cazul, ierarhizare cu ajutorul C1 Problem opera iilor de (activitate-sistem generalizare / specializare. real al crui Cum arat lumea privit din comportament aceast perspectiv? trebuie modelat) Simplificnd inten ionat, C11 C12 cam ca n Figura 3. Nu voi da, nc, nici o defini ie, dar voi face o serie de observa ii pe care le voi aprofunda n capitolele urmtoare. C111 C121 C122

Figura 3. Perspectiva orientat pe obiecte a solu iei unei probleme


1. Se insinueaz faptul c solu ia orientat pe obiecte a unei probleme se ob ine n urma unui demers de organizare a unor obiecte, care au att propriet i informa ionale ct i comportament (se manifest, astfel, ntr-un cadru absolut natural, un mai vechi principiu utilizat n modelare i anume principiul ncapsulrii datelor i prelucrrilor. ncapsulare facem i n Pascal, cnd scriem unit-uri care furnizeaz anumite servicii unor categorii bine definite de utilizatori. Aceste unit-uri au interfa i implementare.

Manifestarea principiului ncapsulrii, n acest cadru, nsemna ascunderea detaliilor de implementare fa de utilizatori, prin publicarea unei interfe e stabile. O interfa este stabil dac utilizatorul ei nu sesizeaz eventualele modificri aduse implementrii interfe ei. 6 2. n sfrit, aplicnd riguros principiul ncapsulrii, putem defini clase de obiecte care au o importan esen ial pentru maparea domeniului problemei peste domeniul solu iei. Exist, ns, numeroase alte ra iuni pentru care principiul ncapsulrii se aplic conjugat cu alt principiu important n modelarea orientat pe obiecte: principiul motenirii. Aplicarea acestui principiu ne ajut s ra ionalizm redundan ele care apar, n mod inerent, n procesul de elaborare a unei solu ii n genere. Mai mult, principiul motenirii pregtete terenul pentru rezolvarea unor probleme interesante care in de polimorfism i genericitate. Exemplul de referin n aceast privin este Java.

Fr a mai insista prea mult, s desprindem concluzia care se impune evident la acest nivel de prezentare: Modelnd orientat pe obiecte, asigurm maximum de coresponden posibil ntre obiectele care populeaz sistemul modelat i obiectele care dau, practic, via solu iei. Lucru absolut remarcabil, deoarece principiul ncapsulrii (temelie a modularizrii de calitate n orientarea pe obiecte) introduce elemente de stabilitate deosebit a solu iei chiar i n situa ia n care apara modificri n sfera domeniului problemei. Prpastia dintre date i prelucrri este nlocuit de reguli precise de asociere a datelor i prelucrrilor, pentru a descrie tipuri de obiecte ntlnite n domeniul problemei i care sunt importante pentru economia de resurse a solu iei. Din aceast perspectiv privind lucrurile, este evident faptul c modelarea orientat pe obiecte este altceva dect modelarea clasic (indiferent de nuan ). Desluirea acestui altceva, la nivel de sintax, semantic i pragmatic (prin raportare la un limbaj de programare) ne va preocupa n continuare. Cei care se grbesc s abordeze i specificul modelrii orientate pe obiecte, abstrac ie fcnd de limbajele de programare- suport pentru implementare, pot consulta lucrarea Ini iere n modelarea obiect orientat utiliznd UML7.

Manifestarea principiului ncapsulrii, n acest cadru, nsemna ascunderea detaliilor de implementare fa de utilizatori, prin publicarea unei interfe e stabile. O interfa este stabil dac utilizatorul ei nu sesizeaz eventualele modificri aduse implementrii interfe ei.
7

D. Bocu, Editura Albastr, Cluj-Napoca

2 Concepte i principii n programarea orientat pe obiecte


2.1 Concepte n programarea orientat pe obiecte
ncepnd cu acest capitol, orientarea pe obiecte va fi privit, nu de la nl imea preceptelor ingineriei softului, ci din perspectiva programrii Java. Subliniez, nc odat, marea provocare pentru un programator care ncearc for a unui limbaj n materie de obiect orientare nu este n sintax, semantica asociat diferitelor tipuri de enun uri sintactice sau stilul de codificare, ci nsuirea spiritului orientrii pe obiecte aa cum este el promovat de elementele suport ale limbajului. De aceea, reamintesc cititorului contient faptul c va trebui s se ntrebuin eze serios pentru a descifra, dac mai este cazul, oferta limbajului C++ n ceea ce privete: tipurile fundamentale de date (similarit i remarcabile cu Java, dar i deosebiri, datorate n principal faptului c n C++ pointerii se manifest cu foarte mult vigoare n cele mai neateptate contexte), reprezentarea structurilor de prelucrare (din nou, similarit i remarcabile ntre C++ i Java), opera iile I/O relative la consola sistem, din perspectiv C, precum i suportul C pentru lucrul cu fluxuri, dac se dorete acest lucru. Nu voi spune dect urmtoarele: C++ este un superset al limbajului C; compilatoarele de C++ sunt realizate astfel nct toate enun urile din C sunt acceptate, dar ele recunosc i o varietate mare de enun uri specifice modului de lucru n orientarea pe obiecte. Dup cum rezult din titlul acestui paragraf, n aten ia mea se vor afla enun urile tipice programrii orientate pe obiecte n Java. nainte de a ajunge la aceste enun uri, trebuie s facem primii pai n nv area spiritului orientrii pe obiecte. Voi prezenta, n continuare conceptele fundamentale de care ne lovim frecvent cnd programm orientat pe obiecte. Spuneam n Capitolul 1 c, din perspectiv orientat pe obiecte, sistemul pe care l modelm va fi ntotdeauna abstractizat de o colec ie de tipuri de obiecte, ntre care exist anumite rela ii. S ne imaginm, de exemplu, c vrem s modelm lumea poligoanelor astfel nct s putem oferi suport pentru nv area asistat de calculator a propriet ilor poligoanelor. Exist o mare diversitate de poligoane. Chiar i cele care sunt de maxim interes din punct de vedere al predrii/nv rii n coal, sunt suficient de multe pentru a pune probleme de abordare a prezentrii propriet ilor lor. i n acest caz, ca n oricare altul, la nceput avem n fa a ochilor realitatea de modelat, care poate fi sau nu structurat dup legile ei naturale. Pentru un individ cu pregtire matematic adecvat este evident c obiectele din Figura 4 sunt clasificate aprioric. Eventual, putem spune c lipsesc unele tipuri de poligoane, pentru c inventarul fcut de noi n Figura 4 este incomplet.

Figura 4. Diferite tipuri de poligoane, aa cum se pot ntlni, n realitatea modelat, prin reprezentan i
Probleme noastr nu este de a stabili nite frontiere nuntrul crora s avem obiecte de acelai tip, ci de a spune care sunt obiectele care nu ne intereseaz. Nu se ntmpl, ntotdeauna, aa. Exist probleme n care efectiv trebuie s depunem eforturi pentru a clasifica obiectele. Opera ia de clasificare presupune identificarea unor categorii de obiecte, apelnd, simultan la omiterea unor detalii, socotite nesemnificative, pentru a ob ine efectul de similaritate n procesul de caracterizare a obiectelor.

Dac n aten ia noastr se afl problema clasificrii poligoanelor, atunci, dac n caracterizarea unui poligon re inem atribute precum: lista coordonatelor vrfurilor, defini ia(), aria(), perimetrul(), atunci rezultatul clasificrii este o clas de obiecte pe care o putem numi clasa Poligon. Astfel c putem da defini ia de mai jos. Defini ia 1 Se numete clas o colec ie de obiecte care partajeaz aceeai list de atribute informa ionale i comportamentale. Prin urmare, primul concept important cu care ne ntlnim n programarea orientat pe obiecte este conceptul de clas. Rezolvarea orientat pe obiecte a unei probleme se bazeaz esen ial pe abilitatea specialistului (n cazul nostru programatorul) de a descoperi care sunt clasele pe baza crora se poate construi solu ia. Presupunnd c avem i noi aceast abilitate i, n acord cu criteriile proprii de clasificare (reflectate i n inventarul din Figura 4), ob inem urmtoarea colec ie de clase candidate la ob inerea solu iei problemei noastre.

Clasa patrulaterelor

Clasa triunghiurilor

Clasa hexagoanelor

Figura 5. Clasele candidate la ob inerea solu iei pentru problema modelrii poligoanelor
Departe de mine ideea c am dat o solu ie definitiv problemei clasificrii poligoanelor. Am prezentat, ns, o solu ie tributar unei anumite viziuni. n conformitate cu aceast viziune, singurele poligoane care prezint interes pentru noi sunt triunghiurile, patrulaterele i romburile. Figura 5 ne atrage aten ia, explicit, asupra diversit ii tipologice a patrulaterelor, fapt care eviden iaz necesitatea recurgerii i la alt operator dect clasificarea pentru a gestiona aceast diversitate. Situa ia este, oarecum asemntoare i n cazul triunghiurilor, dar nu am subliniat explicit acest lucru. Fcnd abstrac ie de aceste elemente, deocamdat, s revenim asupra problemei care ne intereseaz cel mai mult n acest moment: cum stabilim propriet ile unei clase? Regulile de baz n stabilirea propriet ilor unei clase sunt urmtoarele: Lista atributelor informa ionale ale unei clase este, ntotdeauna, rezultatul unui compromis ntre necesitatea unui maximum de informa ii despre obiectele clasei respective (informa ii, care, n fond, caracterizeaz starea obiectelor din clasa respectiv) i necesitatea unui minimum de redundan e acceptate. Obiceiul unor programatori de a supradimensiona lista atributelor unei clase pe motiv c mai bine s fie dect s le ducem lipsa, nu este un model de urmat, nici atunci cnd exist memorie cu carul. Odat specificate, atributele trebuie declarate ca fiind resurse private ale clasei, folosind sintaxa specific limbajului pentru ascunderea unei resurse. Dogma orientrii pe obiecte n legtur cu lista atributelor este c acestea sunt accesibile, diferitelor categorii de clien i, n setare ca i n consultare, prin intermediul unor metode speciale de setare(numite i

modificatori) sau consultare(numite i selectori), crora li se mai adaug metode speciale implicate n crearea obiectelor unei clase (constructorii), respectiv, eliminarea acestor obiecte (destructorii). Evident, mai exist i alte tipuri uzuale de metode, precum iteratorii sau indexatorii, crora li se acord o aten ie special n C#. Odat specificat lista atributelor informa ionale se poate trece la specificarea listei opera iilor clasei, list care abstractizeaz comportamentul clasei. n procesul de specificare a comportamentului unei clase trebuie avute permanent n vedere cele dou dimensiuni ale comportamentului unei clase: comportamentul reclamat de gestiunea strii obiectelor (crearea lor, setarea valorilor atributelor, modificarea valorilor atributelor, consultarea valorilor atributelor, distrugerea obiectelor) precum i comportamentul reclamat de rela ia clasei n cauz cu alte clase. Lista opera iilor unei clase, la nevoie, poate fi organizat, din punct de vedere al modului de acces la aceste opera ii. Un singur lucru este general valabil n aceast privin : faptul c orice clas trebuie s afieze o list cu opera iile publice, care asigur accesul clien ilor la serviciile oferite de clas. Lista acestor opera ii se numete, n mod normal, interfa a clasei. Aten ie, cititorule! Cnd specifici resursele unei clase, eti preocupat s spui ce fac obiectele clasei respective, omi nd inten ionat cum face clasa ceea ce trebuie s fac. Aadar, nu stric s facem o distinc ie necesar ntre definirea unei clase (= specificarea atributelor i a opera iilor) i implementarea clasei (= scrierea codului asociat opera iilor clasei). Definirea rspunde unor comandamente externe (ce in de modul de utilizare a obiectelor clasei); implementarea rspunde unor comandamente care in de intimitatea comportamentului obiectelor (mod de reprezentare n memorie a obiectelor, mod de implementare a opera iilor n acest context). S mai adugm c o opera ie implementat se mai numete i metod.

Folosind nota ia UML pentru reprezentarea vizual a propriet ilor unei clase, avem situa ia din Figura 6. Un concept, inevitabil n programarea orientat pe obiecte este i conceptul de obiect. L-am folosit, deja, la modul intuitiv, ca fiind o parte a unei realit i avnd o anumit valoare de ntrebuin are n contextul n care apare. Acum este momentul s dm urmtoarea defini ie. Defini ia 2. Se numete obiect o instan a unei clase. De la teoria general a tipurilor de date, se tie c instan a unui tip de dat este o variabil avnd o anumit reprezentare n memorie, deci o identitate, i o anumit stare din punct de vedere al con inutului memoriei asociate. n toate limbajele de programare, care ofer suport orientrii pe obiecte, clasa este asimilat unui tip de dat (este drept un tip special), care devine folositor n momentul n care se manifest prin intermediul instan elor. Instan ele unei clase se vor numi, aadar, obiecte sau, uneori, variabile obiect. Dogmatic vorbind, dac solu ia unei probleme este modelat ca o singur clas, atunci ne ateptm ca dinamica aplica iei corespunztoare s fie opera comportamentului unei instan e a clasei. Ce se ntmpl, ns, dac solu ia unei probleme este abstractizat de o ierarhie sau de o re ea de clase? n acest caz, dinamica aplica iei este opera colaborrii ntre insta ele unora dintre clasele ierarhiei sau re elei n cauz. Semantic vorbind, modul n care colaboreaz mai multe obiecte pentru a rezolva o anumit problem, este greu de fixat ntr-o formul definitiv. Din punct de vedere tehnic, ns, rezolvarea este relativ simpl, dup cum se poate deduce i din Figura 7. <Nume clas> Lista atributelor informa ionale, specificate prin nume, tip i eventual valoare implicit Lista opera iilor, specificate prin signatur

Figura 6 Nota ia UML pentru o clas

Obiect

(Produs) CodProd:112233

ListareFurn(CodProd)

(Furnizor) AdresaListaFurn: XXXXXXXX

Clasa definitoare a obiectului Produs char codprod[11]; : void afisare(); : Furnizor

ListaFurn *AdresaListaFurn; : ListareFurn(char codp[11]) :

Opera ia afisare() apeleaz opera ia ListareFurn()

Figura 7.Exemplu de comunicare(colaborare) ntre dou obiecte


Nota ia UML8 pentru o clas i n elesul complet al no iunii de signatur9 pot fi urmrite, lund-o naintea timpului, consultnd lucrarea [Ini iere n modelarea obiect orientat utiliznd UML, Dorin Bocu, Editura Albastr, Cluj-Napoca, 2002] n Figura 7 se prezint schema fundamental pentru colaborarea dintre obiecte. Unul dintre obiecte (obiectul de tip Produs, n cazul nostru) ini iaz comunicarea cu cellalt obiect (de tip Furnizor, n cazul nostru). Se mai obinuiete s se spun c obiectul de tip Produs i-a trimis un mesaj obiectului de tip Furnizor. Este de ateptat ca, ntr-o form sau alta, mesajul s fie urmat de un rspuns. Aadar, dac OFurnizor este o variabil de tip Furnizor i dac aceast variabil este accesibil unui obiect de tip Produs, atunci comunicarea este posibil, cu condi ia ca obiectil de tip Produs s cunoasc interfa a clasei definitoare a obiectului OFurnizor i s aib acces la aceast interfa . n varianta cea mai simpl, un mesaj are structura: <Obiect>.<Nume_metod>([<Lista_de parametri_actuali>]); ceea ce ne ndrept ete s dm defini ia de mai jos. Defini ia 3. Se numete mesaj apelul unei metode a unui obiect, apel efectuat de ctre poten ial al obiectului n cauz. un client

Cunoaterea acestui fapt este deosebit de important n situa ia n care vrem s explicm semantica polimorfismului n programarea orientat pe obiect, ceea ce vom face n partea urmtoare a acestui capitol. n sfrit, s mai men ionez faptul c rspunsul pe care l d un obiect cnd primete un mesaj de la alt obiect depinde de starea n care se afl obiectul care primete mesajul. Este un aspect asupra cruia programatorul trebuie s fie atent, deoarece o stare improprie a obiectului destinatar, poate provoca euarea ini iativei obiectului expeditor de a comunica. Multe excep ii apar, n faza de testare a programelor, tocmai pentru c nu s-a manifestat suficient preocupare pentru evitarea utilizrii obiectelor atunci cnd acestea sunt ntr-o stare critic (referin e nerezolvate, resurse necesare insuficiente, etc.). Fie, n continuare, defini ia conceptului de stare. Defini ia 4. Se numete stare a unui obiect o abstractizare a valorilor atributelor acelui obiect precum i a rela iilor pe care obiectul le are cu alte obiecte.

8 UML-prescurtare de la Unified Modeling Language-Limbaj de modelare unificat, specificat de Rational Software Corporation i omologat ca standard de facto de ctre grupul OMG 9 Signatura cuprinde, n genere: numele oprera iei, lista de parametri i, op ional, tipul returnat

Aadar, recapitulnd, conceptele esen iale cu care operm n lumea orientrii pe obiecte sunt: clas, obiect, stare obiect, mesaj.

2.2 Principii n programarea orientat pe obiecte


Am vzut n paragraful 2.1 care sunt conceptele cele mai importante cu care ne ntlnim n programarea orientat pe obiecte. Se tie de la alte discipline exacte c, fr principii de utilizare a lor, conceptele sunt bune doar de pus n raft, ca nite bibelouri cu care putem aminti posterit ii de o lume disprut. Conceptele prind via , cu adevrat, doar n momentul n care sunt acompaniate de un set de principii care fixeaz regulile esen iale de utilizare a conceptelor. Evident, vom vedea care sunt aceste principii. Ce ne mai ateapt dincolo de ele? Ne ateapt invita ia de a proba singuri valabilitatea acestor principii, la nceput, improviznd cu inerent stngcie, mai apoi descoperind adevrate abloane de rezolvare a unor probleme tip. Ne st la dispozi ie, n cantit i industriale, n internet, experien a tuturor celor care i-au fcut o religie din orientarea pe obiecte i au realizat aplica ii de referin n acest spirit. Voi prezenta, n continuare, principiile pe care le consider absolut necesare pentru a programa n spiritul orientrii pe obiecte. Abstractizarea Obinuiesc s insist pe importan a acestui principiu deoarece mnuirea lui fr inspira ia necesar (iar inspira ia, ntr-un domeniu, are cunoaterea acelui domeniu ca naintemergtor) poate pune lesne sub semnul ntrebrii calitatea unei solu ii. Abstractizarea este procesul de ignorare inten ionat a detaliilor nesemnificative i de re inere a propriet ilor definitorii ale unei entit i. Prin urmare, din perspectiv procesual, abstractizarea este o modalitate de a reflecta asupra propriet ilor unei entit i, cu scopul de a ob ine reprezentri care descriu comportamentul entit ii, reprezentri care pot ndeplini simultan func ii explicative, func ii modelatoare i, de ce nu, fun ii demiurgice, la diferite paliere de profunzime. Utilitatea unei abstrac ii se manifest atunci cnd apar beneficiari n sfera ei de manifestare. Ca un exemplu, referindu-ne la limbajele de programare, putem observa c, toate limbajele ofer suport specific pentru abstractizare. Cu ct suportul pentru abstractizare este mai consistent, cu att putem spune c avem de-a face cu un limbaj de programare de nivel mai nalt. In cazul limbajelor de programare, abstractizarea nseamn un anumit gen de apropiere de limbajul uman i, prin aceasta, de gndirea uman. De asemenea, la nivelul limbajelor de programare vorbim despre trei tipuri fundamentale de abstrac ii, ca rezultate ale procesului de abstractizare: abstrac iile procedurale, abstrac iile la nivelul datelor i clasele. Abstrac iile procedurale sunt cel mai mult folosite n programare. Utilizarea lor metodic permite ignorarea detaliilor legate de desfurarea proceselor. Toate func iile puse la dispozi ia programatorilor n C prin intermediului sistemului de fiiere antet sunt exemple de abstrac ii procedurale, a cror utilizare este uor de nv at dac le cunoatem: numele, eventual lista de parametri i/sau tipul returnat, plus semantica opera iilor realizate de respectivele abstrac ii. Nu trebuie s avem, neaprat, informa ii despre implementarea acestor func ii. Care este ctigul? Se creaz, la un anumit nivel de abstractizare a unui program, posibilitatea ca acesta s fie exprimat ca o succesiune de opera ii logice i nu n termeni de instruc iuni primare ale limbajului. Pentru lizibilitatea programului i, n consecin , pentru depanare, aa ceva este de maxim interes. Abstrac iile la nivelul datelor permit, de asemenea, ignorarea detaliilor legate de reprezentarea unui tip de date, n beneficiul utilizatorilor tipului de date. Un exemplu remarcabil de astfel de abstrac ie la nivelul datelor este tipul variant, specificat de cei de la firma Borland n cadrul limbajului Object Pascal. Cei care au realizat aplica ii Delphi i au dat cu nasul peste tipul variant au probabil amintiri plcute despre versatilitatea i uurin a n utilizare a acestui tip de dat. Clasele ca abstrac ii combin ntr-o nou abstrac ie, extrem de puternic i polivalent semantic, cele dou abstrac ii mai sus pomenite, care in de acea epoc din istoria programrii n care deviza era: Algoritmi+structuri de date=programe. Arsenalul pus la dispozi ia programatorului de clase, n calitate de instrumente de abstractizare va fi n aten ia acestui curs, n continuare. ncapsularea Principiul ncapsulrii insist pe separarea informa iilor de manipulare a unei entit i de aspectele implementa ionale. Se deduce, cu uurin , faptul c ncapsularea este o varietate de abstractizare. Practicat metodic i cu suport sintactic adecvat, n programarea orientat pe obiecte, ncapsularea este sus inut i la alte

nivele, de ctre limbaje de programare diferite. De exemplu, conceptul de unit din Object Pascal, permite ncapsularea resurselor unei aplica ii Delphi, ceea ce, n fond, nseamn modularizare, la un nivel de abstractizare mai nalt dect cel specific ncapsulrii la nivel de clase. Cuvintele cheie pe care se sprijin opera ionalizarea principiului ncapsulrii sunt interfa a i implementarea. Motivul pentru care se insist att pe acest principiu este simplu: separnd interfa a de implementare i admi nd c interfa a este foarte bine structurat (rezult c este stabil n timp i acceptat de utilizatori din punct de vedere al utilit ii i comodit ii n utilizare) nseamn c eventuale modificri ale implementrii (absolut fireti n condi iile mbunt irii permanente a mediilor de execu ie i programare) nu vor putea afecta utilizatorii. Este bine ca cititorul s fac distinc ie ntre ncapsulare i localizarea datelor i a func iilor, n cadrul aceleeai entit i. ncapsularea are nevoie de localizare pentru a sublinia caracterul de black-box al entit ilor, dar ea nseamn mult mai mult dect att. De asemenea, nu trebuie s fetiizm ncapsularea, ateptnd de la ea s garanteze siguran a n procesul de manipulare a obiectelor. Ct de sigur n utilizare este un sistem soft, hotrte programatorul, care combin eficient for a principiului ncapsulrii cu procedeele tehnice de asigurare a protec iei fa de ingerin ele cu efecte negative. Aadar, din perspectiva ncapsulrii, o clas, indiferent de limbajul n care va fi implementat, trebuie s aib, obligatoriu, dou compartimente, ca n Figura 8.

Student

Date i opera ii private

(Implementarea)

Opera ii publice

(Interfa a)

Figura 8Componentele obligatorii ale unei clase, din perspectiva ncapsulrii


Motenirea Mult vreme, motenirea a fost un vis pentru a crui ndeplinire, ntr-o form destul de primitiv, programatorii trebuiau s depun eforturi intense. Apari ia limbajelor care ofer suport sintactic pentru opera ionalizarea principiilor orientrii pe obiecte (OO), a confirmat, printre altele i marea importan a principiului motenirii pentru specificul unei solu ii OO. Din perspectiv mecanic privind lucrurile, acest principiu afirm posibilitatea ca o clas B s moteneasc o parte dintre propriet ile unei clase A. n acest fel avem la dispozi ie un mecanism practic pentru gestiunea similarit ilor, naturale, de altfel ntr-o societate de obiecte foarte diversificat. n paragraful 2.1 (Figura 4) am prezentat, deja, un exemplu de societate posibil de obiecte, n care diversitatea punea probleme de clasificare, lsnd deschis problema gestiunii similarit ilor, n cazul mul imii patrulaterelor. Mergnd pe linia utilizrii nota iei UML pentru reprezentarea unei solu ii OO, atunci trebuie s spunem c dac avem o clas B care motenete o parte dintre propriet ile clasei A, acest lucru va fi redat grafic n felul n care este artat n Figura 9.

A
SPECIALIZARE

Clasa printe, superclasa, clasa de baz Simbolul care indic rela ia de generalizare dintre clasele A i B

GENERALIZARE

Clasa copil, subclasa, clasa derivat

Figura 9. O parte din semantica rela iei de motenire care opereaz ntre clase
Dup cum se vede, rela ia de motenire nu este doar o rela ie al crei n eles se reduce la posibilitatea ca B s moteneasc o parte din propriet ile lui A; semantica rela iei de motenire este mult mai subtil. Mai nti, este vorba despre necesitatea de a reflecta cu ndemnarea necesar la cele dou posibilit i de a identifica o astfel de rela ie: procednd top-down sau bottom-up, deci specializnd, dup ce am identificat o clas rdcin, sau generaliznd, dup ce am terminat opera ia de clasificare a claselor i am nceput s organizm clasele n familii, dup similarit ile care le leag. Indiferent de abordare, rezultatul trebuie s fie acelai: o ierarhie de clase, n care similarit ile sunt distribuite pe nivele de abstractizare, reducnd astfel la minimum redundan ele i pregtind terenul pentru o reutilizare elegant a codului i pentru o serie de alte avantaje care nso esc un lan de derivare. Exemplificm cu ierarhia din Figura 10, a crei semantic ne este deja cunoscut. n Figura 10 regsim aproape toate elementele specifice unei solu ii obiect orientate, care folosete judicios principiul motenirii. Astfel, aproape toate solu iile orientate pe obiect au o clas rdcin (care, n anumite situa ii poate fi o clas abstract, adic o clas care nu poate avea instan e directe, dar referin e avnd tipul ei pot fi asociate cu instan e ale descenden ilor), dac solu ia se reduce la o singir ierarhie de clase. n ierarhie putem ntlni i clase intermediare, precum i clase terminale sau frunz.
Poligon Clas rdcin, poate fi i abstract

Triunghi

Patrulater

Clas intermediar

Paralelogram

Trapez

Patrulater inscriptibil

Clase frunz Romb Dreptunghi

Figura 10.Principiul motenirii n ac iune


Din punctul de vedere al programatorului, pe lng utilitatea motenirii n procesul de gestiune a similarit ilor, mai exist un avantaj care poate fi exploatat n faza de implementare a solu iei, avantaj derivat din principiul motenirii sub forma unui alt principiu care afirm c: Orice printe poate fi substituit de oricare dintre descenden i Acest principiu este de mare utilitate pentru programatori, el fiind opera ionalizat prin intermediul opera ie de casting, aplicat obiectelor ale cror clase definitoare sunt n rela ie de motenire. Evident, este vorba de un casting implicit n lumea obiectelor (descendentul motenind tot ceea ce se poate de la strmoi, va

putea opera n locul strmoului). Castingul de acest tip se numete up-casting. Se practic, explicit i downcastingul, dar programatorul trebuie s fie pregtit s rspund de consecin e. ntr-un down-casting, este posibil ca obiectul de tip strmo, convertit la un obiect de tip descendent, s nu asigure coeren a informa ional de care are nevoie descendentul, deci pot apare, principial, excep ii. n Java se ntlnesc amndou tipurile de casting. n sfrit, n Figura 10 avem un exemplu de utilizare a motenirii simple, potrivit creia un descendent poate avea un singur strmo. Unele limbaje de programare(C++, de exemplu) accept i motenirea multipl, ceea ce nseamn c o clas poate avea doi sau mai mul i strmoi. O astfel de solu ie pentru principiul motenirii este plin de riscuri, cu toate avantajele pe care le presupune. Motenirea multipl poate genera ambiguit i care pot pune la ncercare rbdarea programatorului.

D Figura 11. Exemplu de motenire multipl


n exemplul din Figura 11, clasa D are ca strmoi clasele B i C. Obiectele clasei D vor avea motenite resursele lui A, att pe traseul lui B ct i pe traseul lui C. Ambiguitatea referirii la o resurs a lui A n cadrul unei instan e a lui D este evident. Cnd este considerat strict necesar, motenirea multipl se utilizeaz cu discernmnt. Java i C# nu mai promoveaz motenirea multipl, propunnd alte solu ii la aceast problem. Motenirea nu este doar o problem de sintax, ci este esen a nsi a programrii orientate pe obiecte. Pe temeiul motenirii are rost s vorbim n continuare de principiul polimorfismului, aplicat la lumea obiectelor. Polimorfismul Dup cum se vede i n exemplul prezentat n Figura 10, ierarhia de clase care modeleaz, la urma urmei, comportamentul unei aplica ii poate avea mai multe clase frunz, deci clase care pote genera instan e. Crearea unei instan e este, indicutabil o problem n care programatorul trebuie s se implice, el trebuind s stabileasc, n func ie de dinamica aplica iei, ce constructor va coopera la crearea unei instan e. n ideea c avem o aplica ie care se ocup de simularea nv rii poligoanelor, vi se pare interesant s declarm cte o variabil pentru fiecare tip de poligon din ierarhia prezentat n Figura 10 (mai pu in clasa rdcin)? Nu este interesant din dou motive: 1. 2. Explozia de variabile obiect nu este un indiciu de performan n programare. Odat create obiectele, programatorul trebuie s tie n orice moment ce fel de obiect lucreaz la un moment dat. Chiar c a ceva poate deveni suprtor ntr-o aplica ie de mare complexitate.

Nu ar fi mai civilizat s declarm o variabil de tip Poligon n care s pstrm instan e create cu ajutorul constructorilor oricror descenden i care pot avea urmai direc i? Admi nd c, n clasa Poligon, am declarat o opera ie calculArie() care este suprascris n fiecare descendent, atunci situa ia creat este urmtoarea: obiectul creat cu ajutorul constructorului unui descendent al clasei Poligon i pstrat ntr-o variabil de tip Poligon (principiul substitu iei ngduie acest lucru), s-i spunem p, va putea s apeleze diferite implementri ale opera iei calculArie(), n func ie de contextul n care se creaz p. Aadar, un mesaj de tipul p.calculArie() ce rspuns va genera? Rspunsul va fi dependent de context, controlul contextului (adic alegerea metodei specifice clasei definitoare a obiectului pstrat n p) realizndu-se de ctre sistem prin mecanisme specifice, n timpul execu iei programului. Acest mod de legare a unui nume de opera ie de codul metodei specifice tipului definitor al variabile obiect gazd se numete late binding. Este un mod de legare mai pu in performant dect legarea

static (la compilare), dar avantajele scuz dezavantajele insignifiante, n condi iile n care viteza procesoarelor i disponibilul de RAM sunt n expansiune permanent. S-a n eles, probabil, faptul c polimorfismul este de neconceput fr motenire n care specializarea claselor s se bazeze i pe suprascrierea unor metode n clase aflate n rela ie de motenire. Tot ceea ce trebuie s tie programatorul de aici ncolo este avertismentul c: Motenirea i polimorfismul sunt ideale ca uz i contraindicate ca abuz. Terminnd excursia n lumea conceptelor i a principiilor programrii orientate pe obiecte, nu ne rmne dect s anun m c n capitolele urmtoare vom ncerca s vedem aceste abstrac ii la lucru n context Java.

3 Specificarea i implementarea unei clase din perspectiv Java


3.1 n loc de introducere
Fr s am preten ia c am epuizat semantica modelrii orientate pe obiect, n capitolele precedente, n acest capitol voi ncepe incursiunea n lumea elementelor suport oferite de Java pentru implementarea orientat pe obiect a modelelor obiect orientate. Laboratoarele de cercetare i lumea practicienilor vd, nc, n acest limbaj un moment de referin n zbuciumata evolu ie a limbajelor de programare. Apari ia limbajului C# la orizont, prin bunvoin a celor de la Microsoft, se anun un concurent redutabil, care, i propune s ctige, deopotriv, aten ia fanilor Java i C++. Pn ce apele se vor limpezi, m voi ocupa de ceea ce, tocmai, am anun at n lista subiectelor care fac obiectul acestui capitol.

3.2 Aten ie la importan a efortului de abstractizare!


Voi ncerca s art cum se specific i implementeaz clasele n Java. n tot acest timp, ncerca i, mpreun cu mine, s nu minimaliza i nici o clip importan a hotrtoare a abstractizrii n elaborarea unor solu ii stabile i flexibile10. n acest scop voi considera un exemplu pretext de problem prin intermediul creia voi ncerca s pun n valoare puterea de reprezentare a limbajului Java. S se scrie codul Java care pune la dispozi ia unor utilizatori poten iali capabilit i de lucru cu liste simplu nln uite de numere ntregi, pentru care resursele necesare reprezentrii sunt alocate dinamic. n continuare m voi referi la aceast problem cu acronimul LSI. De ce numai acest tip de list? De ce doar numere ntregi? Pentru c aa cere utilizatorul n acest moment. Nici mai mult, nici mai pu in. Aa se ntmpl i n via a de toate zilele. Specialitii n IS trebuie s livreze beneficiarilor ceea ce acetia se ateapt s primeasc. Amorul artei sau dispre ul fa de beneficiar, sunt taxate necru tor de ctre ansamblul regulilor jocului din industria de soft. Nu voi exagera cu descrierea travaliului conceptual n urma cruia am ajuns la nite concluzii n legtur cu rezolvarea problemei LSI. Dar, cteva elemente de descriere a atmosferei trebuie, totui precizate. Se tie c structurile dinamice de date sunt preferate structurilor statice de date, atunci cnd utilizarea chibzuit a memoriei i flexibilitatea rela iilor dintre obiectele care populeaz structura sunt critice pentru calitatea unui sistem soft. Se mai tie, totodat, c o structur dinamic de date este complet specificat dac am clarificat: Tipul datelor care populeaz structura. Mecanismul de nln uire a datelor din structur. Punctele de intrare n structur. Opera iile cu ajutorul crora ntre inem i consultm structura.

Fiecare din cerintele de mai sus, poate face obiectul unor considera ii cu implica ii interesante asupra solu iei. M limitez doar la a observa faptul c o structur de date dinamic este un exemplu reprezentativ de tip de dat care poate fi modelat orientat pe obiecte, n a fel nct serviciile oferite s fie complete i uor de apelat. Intrnd pu in n domeniul problemei, dac ne gndim la o list simplu nln uit, semantica ei poate fi vizualizat, par ial, ca n Figura 12.

10 Nu este o contradic ie ntre termeni. Cele mai bune modele, ntr-o lume care nu st pe loc, sunt modelele care pot fi socotite, n acelai timp, nchise i deschise, deci stabile i flexibile.

Adresa de start a primului nod Data_ 1 Data_2 Data_n

Informa e nod Legtura spre urmtorul nod

Ultimul nod nu are un succesor

Figura 12. Incercare de vizualizare a unei liste simplu nln uite


Prin urmare, obiectele care fac parte din inventarul problemei sunt: Nod (avnd ca propriet i informa ionale Data i Adresa de legtur cu urmtorul element iar ca propriet i comportamentale, cel pu in opera ii legate de crearea unui nod i consultarea propriet ilor lui informa ionale) i Lista (un obiect care, n principiu este o agregare11 de obiecte de tip Nod). Un obiect care poart marca tipului Lista va avea, aadar, propriet i informa ionale i comportament specific. Abstractiznd cu nverunare, lista propriet ilor informa ionale ale unui obiect de tip Lista ar putea s con in doar adresa primului nod din list. ns, dac trecem n revist exigen ele clien ilor fa de un obiect de tip Lista, vom descoperi c este indicat s avem un atribut pentru a indica dac lista con ine sau nu elemente (un exemplu de redundan n procesul de utilizare a memoriei interne care asigur o vitez sporit n procesul de utilizare a unei liste). Dac acest atribut este un ntreg care indic chiar numrul elementelor din list, cu att mai bine. Aadar, n nota ie UML, n acest moment am putea avea situa ia din Figura 13.
Lista Nod -Int Data; -Nod Urmatorul; +Nod(int Numar); +void setareData(int Numar); +int consultareData(); +void setareUrmator(Nod AUrm) +Nod consultUrmator() -Nod Astart; -Nod AUltim; -Int NrElem; +void setareAStart(Nod Element); +Nod consultaAStart(); +void setareNrElem(int ne); +int consultaNrElem(); +setareAUltim(Nod Element) +Nod consultaAUltim(); +void insereazaDNod(Nod Element); +void insereazaINod(Nod Element); +void stergeNodAdr(Nod Element); +void stergeNodNum(int Nr); +salveazaInFisier(char numef[]); +incarcaDinFisier(char numef[]);)

Figura 13. Clasele candidate la rezolvarea problemei LSDI


Ce observa ii putem face? Clasa Nod este o clas concret, adic, avnd constructor, poate avea instan e. S mai observm faptul c atributele clasei Nod sunt prefixate cu cte un semn -, ceea ce, n UML, nseamn c sunt private. Conform dogmei OO, absolut normal. De asemenea, s mai observm c opera iile clasei Nod sunt prefixate cu semnul + i sunt scrise cu font drept, ceea ce, n UML, nseamn c sunt publice i au deja implementare valabil. Semantica acestei clase i a contextului n care opereaz s-ar prea c nu ne pretinde specificarea unor opera ii private sau protejate. Oare? Este adevrat c n Java, de exemplu, crearea unui obiect de tip Nod se va face rezonabil, chiar i dac nu am prevedea un constructor, dat fiind faptul c obiectele se creaz i n acest caz, atributele fiind ini ializate cu valori predefinite (0 pentru numere, null pentru obiecte, false pentru valori booleene, \u0000 pentru caractere). Dac un astfel de comportament implicit nu este de acceptat, atunci este loc pentru a specifica opera ii care fac validarea strii obiectelor. Aceste opera ii ar putea fi folosite, ca uz intern, de orice alt opera ie care este sensibil la starea obiectului care o folosete la un moment dat. Pe de alt parte, observm faptul c unele dintre opera iile clasei Lista sunt scrise cu caractere italice. n UML aceasta nseamn c aceste opera ii sunt abstracte. Prin urmare, clasa Lista este o clas abstract, deci nu are constructor i tipul introdus de aceast clas nu va putea avea instan e directe. Pentru a fi util, clasa Lista
11 In modelarea obiect orientat se folosete rela ia de agregare pentru a indica o asociere ntre obiecte care, din punct de vedere semantic, sunt ntr-o rela ie parte-ntreg. Reprezentarea unei rela ii de agregare este la latitudinea programatorului.

trebuie s aib descenden i. Care vor fi aceti descenden i? Vom considera c aceti descenden i sunt doi: o clas care modeleaz o list simplu nln uit de ntregi, n care cuvntul de ordine la creare este ordinea fizic de introducere i o clas care modeleaz o list simplu nln uit de ntregi, n care elementele listei sunt introduse astfel nct, dup fiecare introducere acestea s fie n ordine cresctoare. Pe scurt: liste indiferente la ordinea numerelor ntregi i liste sensibile la ordinea numerelor ntregi. Vom ob ine ierarhia din Figura 14.
Lista

ListaOarecare

ListaSortata

Figura 14. Ierarhia claselor care modeleaz dou variet i de list simplu nln uit.
Se poate observa c cele dou variet i posed opera iile necesare pentru a simula, n caz de nevoie i comportamentul unei stive (AStart este Top-ul iar inserareINod() i stergeNodAdr(AStart) sunt opera iile specifice unei stive, adic Push() i Pop()). Cititorul bnuiete c inserareINod() este abreviere de la inserare nainte de nod iar inserareDNod() este abtreviere de la inserare dup nod. Considera ii de acest gen i chiar mai profunde, trebuie s prilejuiasc orice ncercare de rezolvare orientat pe obiect a unei probleme. 3.3 Specificarea i implementarea unei clase n Java Este cunoscut faptul c, n Java, orice aplica ie este puternic obiect orientat, cel pu in datorit cadrului sintactic obligatoriu pentru realizarea unui applet sau a unei aplica ii Java obinuite. Dac spiritul orientrii pe obiecte este bine n eles, atunci Java este o solu ie interesant pentru multe probleme a cror rezolvare presupune realizarea unor aplica ii pentru care lumea Internet-ului este puternic deschis. Indiferent de tipul aplica iei, piesele de baz n realizarea acesteia sunt clasele. Vom avea, n cazul unei aplica ii Java obinuite, o singur clas public, care con ine func ia main() i una sau mai multe clase care modeleaz, la diferite nivele de rafinare, comportamentul aplica iei. De asemenea, n cazul unui applet Java, vom avea o singur clas public care extinde clasa Applet i una sau mai multe clase care modeleaz, la diferite nivele de rafinare, comportamentul applet-ului. Privit de la cel mai nalt nivel de abstractizare, defini ia unei clase n Java este: [<List modificatori>] class <Nume clas> [extends <Clasa de baz>] [implements <Lista interfe e>] { //List de resurse sau corp clas } Resursele unei clase sunt de dou tipuri: atribute i/sau opera ii. Problema noastr, dup cum am vzut, este de a organiza aceste resurse, astfel nct s putem beneficia de o serie de avantaje din punct de vedere al efortului de dezvoltare ct i din punct de vedere al calit ii softului12. n paragraful 2.2 am vzut c o ncapsulare corect (n acord i cu dogma OO) nseamn s declarm ca private atributele i s definim o interfa corespunztoare clasei. Vom vedea, mai jos, ce nseamn acest lucru n cazul problemei noastre. Atributele unei clase se specific sintactic astfel: [<List modificatori atribut>] Tip <Lista identificatori variabile>; Opera iile unei clase se specific prin signatur i implementare ca mai jos: [<List_modificatori_opera ie>]Tip_returnat Identificator_metod>
12

Despre calitatea softului cititorul poate gsi elementele esen iale n D. Bocu, Ini iere n ingineria sistemelor soft, Editura Albastr, 2002

([<List parametri>) [throws <Lista excep ii>] { <Corp opera ie> } Formalizmul folosit pentru prezentarea sintaxei de definire a unei clase se bazeaz, dup cum se poate deduce, pe urmtoarele conven ii: Orice construc ie a utilizatorului este prezentat ntre simbolurile < ...>. Orice construc ie op ional este ncadrat de simbolurile [...]. Cuvintele rezervate sunt eviden iate prin ngroare. ntrebarea fireasc care se pune este urmtoarea: la ce ne folosesc aceste elemente de varia ie n procesul de definire a unei clase. Voi ncerca s rspund pe rnd la toate ramurile acestei intrebri. Modificatorii aplicabili claselor Dup cum se poate observa, cuvntul cheie class poate fi prefixat, op ional, de un modificator. Lista complet a acestor modificatori este: abstract, final, public. Modificatorul de clas abstract Java permite extinderea unei clase existente cu o subclas. Cu timpul, este posibil s v constitui i propriile biblioteci de clase care considera i c vor fi extinse de al i programatori. Pentru unele clase, poate s fie inutil s implementa i o opera ie ct timp nu se cunoate cum va fi extins clasa. n astfel de cazuri, pute i utiliza cuvntul cheie abstract pentru a indica faptul c n descenden i toate opera iile clase trebuie s fie supradefinite obligatoriu. Clasa Lista din Figura 14 ar putea fi definit, prin urmare astfel, n Java: abstract class Lista { private Nod AStart; private Nod AUltim; private int NrElem; public void setareAStart(Nod Element); public Nod consultaAStart(); public void setaretNrElem(); public int consultaNrElem(); public Nod consultaAUltim(); public abstract void insereazaDNod(Nod Element); public abstract void insereazaINod(Nod Element); public abstract void stergeNodAdr(Nod Element); public abstract void stergeNodNum(int Nr); public final void salveazaInFisier(char numef[]); public final Nod incarcaDinFisier(char numef[]);) } Este evident faptul c descenden ii ListaOarecare i ListaSortata sunt obliga i s implementeze toate opera iile clasei Lista, care sunt abstracte. De asemenea, s observm c implementarea definitiv a opera iilor de salvare/restaurare a listei este realizat n clasa Lista i va fi folosit fr modificri n descenden i. Modificatorul de clas final (tocmai l-am utilizat mai sus) n general vorbind, Java permite unei clase s extind o alt clas. Atunci cnd definim o clas, n contextul n care anticipm utilizarea ei, s-ar putea s nu dorim extinderea ei de ctre alte clase. n acest caz, prin includerea cuvntului cheie final n cadrul defini iei clasei vom mpiedica crearea de subclase ale clasei n cauz. Aadar, clasa: public final class NumeClasa {...} nu va putea s aib urmai.

Modificatorul de clas public Atunci cnd utilizm cuvntul cheie public n cadrul declara iei clasei, ne asigurm c acea clas este vizibil / accesibil de oriunde. Dac dorim s controlm accesul la o clas, cuvntul cheie public nu are ce cuta n declara ia clasei. S reamintim, totodat, faptul c Java permite o singur clas public ntr-un fiier cu cod surs. Evident, caracterul public al unei clase poate avea alte conota ii n contextul organizrii codului surs al unei aplica ii cu ajutorul pachetelor. Mai precis spus, o clas public poate fi utilizat din exteriorul pachetului n care a fost declarat, pentru a crea instan e sau pentru a o extinde. n schimb, o clas care nu a fost declarat public este considerat o clasa friend , putnd fi accesat doar din interiorul pachetului n care este rezident. Modificatorii aplicabili atributelor Domeniul de valabilitate al unui atribut definete loca iile din program n care atributul este cunoscut. n procesul de definire a unei clase putem controla domeniul unui atribut al clasei precednd declara ia lui cu unul din cuvintele cheie: public, private, protected, static, final, tranzient, volatile. S men ionm faptul c un atribut care nu este nso it de nici un modificator este vizibil friendly, adic doar din interiorul clasei i din clasele din acelai pachet. Modificatorul de atribut public Un atribut public este vizibil / accesibil oriunde este vizibil / accesibil clasa care l con ine. Aadar, pentru a declara ca public un atribut vom proceda ca mai jos: : public int vizibilOriundeEsteVizibilaClasa; : Dogma spune c o clas care ajunge la client trebuie s-i ascund atributele fa de acesta, accesul la ele fiind mijlocit de interfa , dac este cazul. Nu este, ns, exclus ca o serie de clase neterminale (care nu sunt, deci clase frunz) s declare ca publice o parte a atributelor, protec ia lor fiind controlat, la nivelul descenden ilor prin intermediul interfe elor sau al organizrii n pachete. Modificatorul de atribut private Un atribut privat este vizibil numai n interiorul clasei sale. Subclasele i clien ii externi nu pot accesa aceste atribute. Modificatorul de atribut protected Un atribut al clasei, declarat ca protejat, este accesibil n descenden ii clasei sau n cadrul pachetului din care face parte clasa de intoare. Aten ie, un atribut declarat ca protected ntr-o clas va putea fi accesat n scriere i citire n to i descenden ii clasei n cauz, reziden i chiar i n afara pachetului gazd al clase care de ine atributul protejat. Nu va fi permis accesul direct la atributul protejat pentru clase care nu sunt descende i ai clasei care declar atributul protejat. Modificatorul de atribut static Orice atribut care nu este declarat ca static este numit atribut de instan , ceea ce nseamn c fiecare instan are propria copie a atributului. Atunci cnd este n interesul comportamentului clasei ca un atribut s fie partajat de toate obiectele clasei n cauz, acel atribut va fi declarat ca static. Modificatorul de atribut final Atunci cnd n defini ia unei clase men ionm un atribut final, indicm compilatorului faptul c acel atribut are valoare constant, care nu poate fi modificat de program. Ini ializarea atributului cu o valoare se poate face la crearea obiectlui gazd, prin contribu ia constructorului sau n cadrul unei declara ii de tipul: : protected static final int nr=10; : Aten ie! Cele dou metode de ini ializare sunt mutual exclusive.

Modificatorul de atribut transient

Atunci cnd declarm un atribut ca fiind de tip transient, indicm compilatorului Java faptul c atributul nu este o parte permanent a obiectului, deci de uz intern i n cazul serializrii, de exemplu, nu va fi salvat pe memoria extern. Un atribut tranzient se declar astfel: : private transient String password; : Modificatorul de atribut volatile Atunci cnd se compileaz programul, compilatorul analizeaz codul i, adeseori va efectua anumite manevre cu scopul de a optimiza performan ele codului. Atunci cnd dorim s scoatem un atribut de sub inciden a unei astfel de eventualit i, o declarm ca volatil. Practic, aceasta nseamn c exist situa ii particulare n care comunica ia cu alte programe sau rutine necesit neinterven ia compilatorului asupra unui atribut, esen ial pentru buna desfurare a comunica iei n cauz. Modificatorii aplicabili opera iilor n aceast sec iune vom prezenat o serie de modificatori care, de cele mai multe ori, sunt aplicabili metodelor care implementeaz opera iile claselor. Aceti modificatori sunt: public, private, protected, static, final, abstract, native, synchronized. Modificatorul de metod public Semnifica ia acestui modificator, n cazul n care se aplic unei metode, este asemntoare cu cea pe care o are cnd se aplic unui atribut. Modificatorul de metod private Semnifica ia acestui modificator, n cazul n care se aplic unei metode, este asemntoare cu cea pe care o are cnd se aplic unui atribut. Modificatorul de metod protected Semnifica ia acestui modificator, n cazul n care se aplic unei metode, este asemntoare cu cea pe care o are cnd se aplic unui atribut. Modificatorul de metod static Semnifica ia acestui modificator, n cazul n care se aplic unei metode, este, ntr-o oarecare msur, asemntoare cu cea pe care o are cnd se aplic unui atribut. Mai precis, trebuie s spunem c o metod static, pentru a fi utilizat nu reclam neaprat o instan , putnd fi utilizat i printr-un apel de tipul: NumeleClasei. NumeleMetodeiStatice(parametri); O metod static poate fi utilizat pentru a accesa al i membri statici ai clasei, dar, n nici un caz, pentru a accesa variabile nestatice. Modificatorul de metod final Am vzut, deja, n cursul 2, c anumite metode ale claselor pot fi supradefinite n clasele descendente. Dac, din variate motive, dorim s blocm posibilitatea supradefinirii unei metode, atunci vom informa compilatorul de aceast inten ie declarnd metoda ca final astfel: public final void MetodaNuPoateFiSupradefinita(); Modificatorul de metod abstract Dac o metod a unei clase este precedat de cuvntul cheie abstract, atunci compilatorul nu va autoriza crearea de instan e ale clasei n cauz. Totodat, o clas care extinde clasa n cauz va trebui s implementeze metoda abstract obligatoriu. Declararea se face astfe: public abstract void implementareUlterioar(); Aten ie! O metod abstract nu poate fi privat sau final.

Semantica cuvntului cheie abstract (= metoda va fi implementat n descenden i) vine n contradic ie cu semantica cuvintelor cheie private i final care spun, n moduri diferite, c metoda nu poate fi modificat. Modificatorul de metod native Acest modificator se utilizeaz pentru a spune compilatorului c o anumit metod utilizeaz cod scris ntrun alt limbaj de programare, cum ar fi C/C++, de exemplu. Aceast posibilitate trebuie folosit cu discernmnt deoarece lovete puternic n portabilitatea aplica iei, aa cum este ea n eleas n Java. Modificatorul de metod synchronized Se tie c Java ofer suport pentru multitasking sub forma un program mai multe fire de execu ie. n func ie de activitatea programului respectiv, uneori este necesar s garantm faptul c, dou sau mai multe fire nu pot accesa simultan aceeai metod. Prin urmare, pentru a controla numrul de fire de execu ie care pot accesa o metod la un moment dat, utilizm cuvntul cheie synchronized. Atunci cnd compilatorul Java ntlnete o metod prefixat de cuvntul cheie synchronized, introduce un cod special care blocheaz metoda cnd un fir ncepe execu ia instruc iunilor metodei i o deblocheaz cnd firul i ncheie execu ia. n mod uzual, sincronizarea metodelor este reclamat de necesitatea partajrii datelor. Am prezentat, mai sus, semantica cuvintelor cheie ascunse sub sintagma modificatori, aplica i atributelor sau opera iilor. Sintaxa care st la baza definirii unei clase mai con ine, op ional i cuvntul cheie extends pentru a indica o clas de baz clasei n curs de definire. De asemenea, n sintaxa prezentat se mai evoc i eventualitatea apari iei cuvntului cheie implements urmat de numele interfe elor pe care le implementeaz clasa, atunci cnd este cazul. Evident, cuvntul cheie extends este legat de problematica motenirii n programarea orientat pe obiecte n Java iar cuvntul cheie implements este legat de problematica utilizrii interfe elor, pentru a introduce numeroase elemente de flexibilitate n programarea Java, inclusiv rezolvarea motenirii multiple, neadmis direct n Java. Despre aceste dou probleme vom discuta pe ndelete in Capitolul 5. nainte de a trece la prezentarea cadrului C++ pentru definirea unei clase putem urmri, mai jos, codul Java asociat definirii claselor Nod i Lista, aa cum apar ele n lumina observa iilor fcute pn acum. //--------------------------------------------//Cod Java care demareaza procesul de rezolvare //a problemei LSI //--------------------------------------------//Specificare minimala a clasei Nod //Implementare clasa Nod class Nod { private int Data; private Nod Urmatorul; //constructor public Nod(int Numar) { Data=Numar; }; //modificator atribut Data public void setareData(int nr) { Data=Numar; }; //selector atribut Data public int consultareData() { return Data; };

//Modificator atribut Urmatorul public void setUrmator(Nod Element) { Urmatorul=Element; }; //Selector atribut Urmatorul public Nod consultaUrmator() { return Urmatorul; }; }; //Specificare aproape completa a clasei abstracte Lista abstract class Lista { private Nod AStart; private Nod AUltim; private int NrElem; //Modificator atribut AStart public void setareAStart(Nod Element) { AStart=Element; }; //Selector atribut AStart public Nod consultaAStart() { return AStart; }; //Modificator atribut NrElem public void setareNrElem(int ne) { NrElem=ne; }; //Selector atribut NrElem public int consultaNrElem() { return NrElem; }; //Modificator atribut AUltim public void setareAUltim(Nod Element) { AUltim=Element; }; //Selector atribut AUltim public Nod consultaAUltim() { return AUltim; }; //Metoda abstracta //Va fi implementata in descendenti

//Insereaza un element dupa ultimul nod introdus public abstract void insereazaDNod(Nod Element); //Metoda abstracta //Va fi implementata in descendenti //Insereaza un element inaintea ultimului nod introdus public abstract void insereazaINod(Nod Element); //Metoda abstracta //Va fi implementata in descendenti //Sterge un element de adresa specificata public abstract void stergeNodAdr(Nod Element); //Metoda abstracta //Va fi implementata in descendenti //Insereaza un element de pozitie specificata public abstract void stergeNodNum(int Nr); //Metoda publica finala //Deocamdata neimplementata //Salveaza lista reperata de AStart //intr-un fisier de nume specificat public final void salveazaInFisier(char numef[]) { System.out.println("Neimplementata..."); }; //Metoda publica finala //Deocamdata neimplementata //Restaureaza lista folosind informatiile din fisierul //de nume specificat public final Nod incarcaDinFisier(char numef[]) { System.out.println("Neimplementata..."); return null; }; } Codul prezentat mai sus trebuie s fie extins i rafinat pentru a acoperi toate cerin ele ini iale ale problemei i pentru a face fa exigen elor de calitate fireti (modularizare corect, fiabilitate, extensibilitate, etc.).

4 Motenirea n programarea orientat pe obiecte din perspectiv Java


4.1 Scurt introducere
Am vzut, deja, faptul c unul dintre principiile importante n programarea orientat pe obiecte este principiul motenirii. Aa cum se ntmpl, n general, cu principiile, nici principiul motenirii nu trebuie fetiizat sau bagatelizat. Bagatelizarea lui nseamn, ceva de genul: Hai s facem motenire ca s ne aflm n treab sau ca s vedem dac func ioneaz. Fetiizarea, din contr, ar nsemna Sfinte Sisoe, nimic mai frumos i mai eficient dect aplicarea principiului motenirii la tot pasul.Ambele variante sunt false. Cum am mai spus i alt dat, principiul motenirii este disponibil pentru uz, nu pentru abuz. Ceva mai concret spus: Motenirea este o modalitate performant de reutilizare a codului, dar nu este ntotdeauna cel mai bun instrument pentru ndeplinirea acestui obiectiv. Dac este folosit necorespunztor, programele ob inute vor fi destul de fragile. Motenirea poate fi utilizat cu succes n cadrul unui pachet, unde implementrile subclaselor i superclaselor sunt controlate de aceeai programatori. De asemenea, poate fi folosit la extinderea claselor care au fost concepute i documentate exact n acest scop. ns, motenirea unor clase concrete obinuite, n afara grani elor unui pachet, poate fi periculoas. Trebuie s subliniez faptul c toate considera iile pe care le fac n acest paragraf se refer la motenirea n eleas ca motenire a implementrii (o clas extinde o alt clas, exprimndu-ne n spirit Java). Se tie, desigur c rela ia de motenire opereaz i n rela ia dintre interfe e, chestiune care nu cade sub inciden a observa iilor critice, de mai sus, la adresa motenirii. Defec iunea esen ial care poate apare cnd apelm la motenire se refer la faptul c, prin motenire, ncapsularea are de suferit n mod natural. i aceasta deoarece func ionarea corect a unei subclase depinde de detaliile de implementare ale superclasei. Implementarea superclasei se poate modifica de la o versiune a programului la alta, situa ie n care , subclasa este expus deteriorrii chiar i n cazul n care codul ei a rmas neatins. Prin urmare, subclasa este obligat s evolueze odat cu superclasa, exceptnd, binen eles, situa ia n care autorii superclasei au conceputo i documentat-o special pentru a fi extins. O regul de bun sim n utilizarea principiului motenirii ne spune c este bine s folosim motenirea numai dac subclasa este, ntr-adevr, un subtip al superclasei. Altfel spus, o clas B trebuie s extind o clas A numai dac ntre cele dou clase exist o rela ie de tipul B este un A. Cu referire la mul imea poligoanelor, prerea unor aa zii specialiti, conform creia putem deriva clasa dreptunghi din clasa triunghi este complet n afara logicii rela iei de motenire. Nici n visul cel mai frumos un dreptungi nu este un triunghi. n schimb, putem spune c triunghiul este un descendent al poligonului, pe motiv c orice triunghi este un poligon, etc. Concluzionnd, motenirea este un aspect important n programare, dar problematic, pentru c poate fi aplicat denaturndu-i esen a i pentru c ncalc, n mod natural, regulile ncapsulrii. Motenirea se poate folosi eficient numai cnd ntre superclas i subclas exist o rela ie real de genul tip-subtip. Chiar i n acest caz, codul ob inut poate fi fragil, avnd probleme cnd apar modificri i fiind predispus la anumite bree n securitatea resurselor unei clase. Evitarea unor astfel de probleme este posibil, n anumite situa ii, prin utilizarea compunerii n locul motenirii. Rela ia de compunere presupune ca n defini ia unei clase A s apar i instan e ale altor clase, ale cror servicii urmeaz s fie utilizate de ctre clasa A. Pe de alt parte, este tot att de adevrat faptul c multe

probleme rezolvate n C++ cu ajutorul pointerilor, nu ar avea rezolvare echivalent semantic n Java, fr suportul motenirii.

4.2 Motenirea n Java


Nu este cazul s revin asupra sintaxei opera iei de motenire n Java. Java a optat pentru un cuvnt cheie pentru a informa compilatorul de inten ia unei clase de a moteni propriet ile altei clase. Acest cuvnt cheie este extends. Ceea ce trebuie s semnalm ca important, pentru spiritul motenirii n limbajul Java, este faptul c acesta nu ofer suport pentru motenirea multipl, n sensul n care este aceasta n eleas n alte limbaje. n Java, o clas poate avea o singur superclas. De aici rezult posibilitatea teoretic de a construi doar ierarhii de clase, cu suportul oferit de motenirea cu extends. Dat fiind faptul c n practic exist i nenumrate situa ii n care semantica motenirii multiple se impune ca alternativa cea mai valabil, Java ofer o porti pentru a simula motenirea multipl cu ajutorul interfe elor. Jongleriile care se pot face cu ajutorul interfe elor au depit de mult inten iile ini iale ale specificatorilor limbajului Java. M voi ocupa de acest subiect n paragraful 5.3. Exemplul 4.1 ilustreaz motenirea, avnd ca idee cluzitoare specializarea clasei de baz prin adugare de membri noi. De asemenea, Exemplul 5.1 ilustreaz i cele dou tipuri de casting posibile n programarea orientat pe obiecte din Java. n esen , este vorba despre faptul c, avnd contextul din Figura 15, sunt posibile dou tipuri de casting, principial deosebite. A

Figura 15. Derivare pretext pentru dou tipuri de casting


Primul tip de casting este implicit. Este vorba despre conversia unei variabile obiect de tip B la o variabil de tip A. Motivul pentru care acest tip de conversie este implicit este simplu: resursele lui A se regsesc printre resursele lui B. Astfel c, atunci cnd B este coerent din punct de vedere al strii, nu exist nici un motiv ca A s fie altfel, n urma conversiei. Al doilea tip de casting necesit acordul explicit al programatorului pentru a fi efectuat i ntr-un anumit sens, asumarea rspunderii pentru aceast conversie. Este vorba de conversia unei variabile obiect de tip A la o variabil de tip B. De ce este necesar acordul? Foarte simplu: resursele lui A sunt incluse printre resursele lui B. Conversia de mai sus este posibil s aduc variabila de tip B ntr-o stare improprie pentru utilizarea unor metode, deoarece unele date membre pot rmne neini ializate adecvat. Cu toate acestea, down casting-ul este esen ial n programarea generic, asupra creia vom reveni ntr-un curs special. Exemplul 4.1 //Clasa de baz //Modeleaz informa ional obiectul Fruct //Potrivit abordrii din acest cod //metoda consultaTipFruct() nu poate fi //redefinit n descenden i class Fruct { private int tipfruct; Fruct(int t) { tipfruct=t; System.out.println("Constructor Fruct..."); }

final int consultaTipFruct() { return tipfruct; } } //Subclas a clasei Fruct //Para este un fruct->este respectat spiritul natural //al motenirii //Se adaug noi atribute informa ionale //Se adaug metode specifice //Este un exemplu clasic de specializare prin adaugare class Para extends Fruct { private double greutate; private int forma; Para(int t,double g,int f) { super(t); System.out.println("Constructor Para..."); greutate=g; forma=f; } double consultaGreutate() { return greutate; } int consultaForma() { return forma; } } //Subclas a clasei Fruct //Portocala este un fruct->este respectat spiritul natural //al motenirii //Se adaug noi atribute informa ionale //Se adaug metode specifice //Este un exemplu clasic de specializare prin adugare class Portocala extends Fruct { private int tipcoaja; Portocala(int tf,int tc) { super(tf); System.out.println("Constructor Para..."); tipcoaja=tc; } int consultaTipCoaja() { return tipcoaja; } } //Clasa care utilizeaz ierarhia de clase de mai sus //Tipurile Para i Portocala au acelai supertip //dar sunt incompatibile la atribuire

//deoarece sunt pr i ale unor lan uri de derivare //diferite public class Mosten1 { public static void main(String[] s) { //Declarare variabil referin //avnd tipul clasei rdcin Fruct of; //Declarare i alocare variabil referin //de tip Para Para obpara=new Para(1,1.5,2); //Declarare i alocare variabil referin //de tip Portocala Portocala obport=new Portocala(10,1); //Utilizare normal a variabilei de tip Para System.out.println("Para ..creata ca referinta Para"); System.out.println("Tip fruct:"+ obpara.consultaTipFruct()); System.out.println("Greutate fruct:"+obpara.consultaGreutate()); System.out.println("Forma fruct:"+obpara.consultaForma()); //Utilizare normala a variabilei de tip Portocala System.out.println("Portocala creata ca referinta Portocala"); System.out.println("Tip fruct:"+obport.consultaTipFruct()); System.out.println("Tip coaja:"+obport.consultaTipCoaja()); //Exemplu de Up casting (implicit) of=new Para(1,2.5,3); //Exemplu de Down casting (explicit); //Foarte util in programarea generica obpara=(Para)of; //Utilizare variabile referin //setate prin casting explicit System.out.println("Para ...creata ca referinta Fruct"); System.out.println("Tip fruct:"+obpara.consultaTipFruct()); System.out.println("Greutate fruct:" +obpara.consultaGreutate()); System.out.println("Forma fruct:"+obpara.consultaForma()); } }; Exemplul 4.2 ilustreaz specializarea prin redefinirea metodelor, ca baz pentru comportamentul polimorfic al obiectelor n Java. Mecanismul polimorfismului l voi pune n discu ie n Capitolul 5. Exemplul45.2 //Clasa de baza //Modeleaz inform ional obiectul Fruct //Metoda consultaTipFruct() se va redefini //in descendentul Para class Fruct { private int tipfruct; Fruct(int t) {

tipfruct=t; System.out.println("Constructor Fruct..."); } int consultaTipFruct() { return tipfruct; } } //Subclas a clasei Fruct //Para este un fruct->este respectat spiritul natural //al motenirii //Se adaug noi atribute informa ionale //Se redefinete metoda consultaTipFruct() //Este un exemplu clasic de specializare prin redefinire class Para extends Fruct { private double greutate; private int forma; Para(int t,double g,int f) { super(t); System.out.println("Constructor Para..."); greutate=g; forma=f; } int consultaTipFruct() { System.out.println("Consultare tip fruct..redefinire Para"); return super.consultaTipFruct(); } double consultaGreutate() { return greutate; } int consultaForma() { return forma; } } //Subclas a clasei Fruct //Portocala este un fruct->este respectat spiritul natural //al motenirii //Se adaug noi atribute informa ionale //Se redefinete metoda consultaTipFruct() //Este un exemplu clasic de specializare prin redefinire class Portocala extends Fruct { private double greutate; private int forma; Portocala(int t,double g,int f) { super(t); System.out.println("Constructor Portocala..."); greutate=g;

forma=f; } int consultaTipFruct() { System.out.println("Consultare tip fruct..redefinire Porto"); return super.consultaTipFruct(); } double consultaGreutate() { return greutate; } int consultaForma() { return forma; } } public class Mosten2 { public static void main(String[] s) { //Declarare variabil referin de tipul clasei rdcin Fruct of; //Alocare referin utiliznd constructorul unei subclase //a clasei rdcin (Para) of=new Para(1,2.75,3); System.out.println("Para ...creata ca referinta Fruct"); //Utilizare, de fapt, n spirit polimorfic //a variabilei definite i alocate mai sus System.out.println("Tip fruct:"+of.consultaTipFruct()); //Alocare referin utiliznd constructorul unei subclase //a clasei rdcin (Portocala) of=new Portocala(2,0.75,4); System.out.println("Portocala ...creata ca referinta Fruct"); //Utilizare, de fapt, n spirit polimorfic //a variabilei definite si alocate mai sus System.out.println("Tip fruct:"+of.consultaTipFruct()); } };

4.3 Motenirea multipl n Java


Dei o modalitate comod de a iei din impas n anumite situa ii, motenirea multipl este criticat pentru confuziile pe care le poate genera, dac este utilizat fr un efort de inventariere, adecvat focalizat asupra propriet ilor claselor candidate la calitatea de superclase pentru o clas derivat din ele. Duplicarea cmpurilor i a metodelor, precum i violarea cras a ncapsulrii sunt principalele motive de ngrijorare cnd este vorba de utilizarea motenirii multiple. Aadar, ne aflm n situa ia din Figura 16. Dac semantica din domeniul problemei impune o asemenea abordare, din punct de vedere conceptual, atunci n Java solu ia pentru posibilitatea de a spune despre C c este A sau B sau A i B o reprezint utilizarea interfe elor. Interfa a este o varietate de clas, caracterizat prin faptul c poate declara metode abstracte i publice i, dac este necesar, variabile care sunt considerate, implicit, de tip public, static i final, deci constante. n foarte mare msur, comportamentul unei interfe e este asemntor comportamentului unei clase. Att de profund este asemnarea nct, n anumite situa ii interfe ele i clasele se pot substitui reciproc.

Figura 16.Motenirea multipl


Pentru a n elege modul de lucru cu interfe ele consider esen iale urmtoarele precizri: 1. Mai nti trebuie s nv m cum se declar o interfa . Aceasta este o problem de sintax, mult mai simpl dect problema conceptual, care trebuie clarificat nainte de a ajunge la sintax. Din punct de vedere conceptual, tipul de ra ionament pe care l facem seamn cu cel pe care l facem cnd specificm o clas abstract care are toate metodele virtuale pure, n C++ sau abstracte n Java. Din punct de vedere sintactic, avem cadrul: interface <Nume_interfa > [extends <List _de_interfe e>] { <Signaturi de metode> } Se observ, deja, amnuntul care deosebete mecanica utilizrii claselor de mecanica utilizrii interfe elor, anume, suportul pentru motenire multipl n cazul interfe elor. Pe acest amnunt se bazeaz alternativa Java la motenirea multipl relativ la clase. 2. n al doilea rnd, utilizarea unei interfe e este posibil, n mai multe moduri, dup ce am specificat i implementat o clas care o utilizeaz. Ne amintim de sintaxa: class <Nume_clas> [extends Nume_superclas] implements <List de interfe e> { <Date membre> <Func ii membre> }; Dup cum se poate observa, o clas poate implementa mai multe interfe e, depindu-se, astfel, restric ia Java n legtur cu motenirea multipl n rela ia dintre clase. S mai subliniez i faptul c o clas care implementeaz una sau mai multe interfe e poate avea cel mult un strmo, eventual nici unul. Disciplina astfel introdus este, sper, clar: metodele unei clase (care are, eventual un strmo) pot fi acoperitoare ca specificare i implementare pentru listele de metode ale unor de interfe e. n acest mod, avem la dispozi ie un mecanism de a vedea, din unghiuri diferite, ansamblul resurselor unei clase. Este clar c solu ia Java determin programatorii s mediteze mai atent nainte de a face jonc iunea cu mai multe interfe e. 3. Utilizarea efectiv a interfe elor este divers: ca tipuri definitoare pentru referin e la obiecte, ca tipuri implicate n casting, ca tipuri utile n programarea generic, etc. Cteva modalit i de utilizare a interfe elor se pot vedea n Exemplul 4.3 i n Exemplul 4.4. Exemplul4.3 //Interfata I1 //Expune functia f1() interface I1 { public void f1(); }

//Interfata I2 //Expune functia f2() interface I2 { public void f2(); }; //Interfata I12 //Extinde interfetele I1 si I2 //Exemplu de mostenire multipla //a interfetelor interface I12 extends I1,I2 { public void f3(); } //Clasa A implementeaza interfata I1 class A implements I1 { public A() { System.out.println("AAAA..."); }; public void f1() { System.out.println("f1...."); }; }; //Clasa B implementeaza interfata I2 class B implements I2 { public B() { System.out.println("BBBB..."); }; public void f2() { System.out.println("f2...."); }; }; //Clasa C implementeaza interftata I12 //Evident,functiile expuse de interfetele //I1 si I2 sunt implementate si de catre //clasa C //Deoarece I12 este derivata din I1 si I2 //ni se ingaduie sa privim instantele de tip //I12 ca fiind de tip I1 sau I2, dupa cum //este in interesul nostru class C implements I12 { public C() { System.out.println("CCCC..."); }; public void f3() {

System.out.println("f3..."); }; public void f1() { System.out.println("f1...."); }; public void f2() { System.out.println("f2...."); }; };

public class TestInterf { public static void main(String args[]) { //Crearea unui obiect de tip I1 //utilizand clasa A I1 ob1=new A(); System.out.println("Ura..."); //Utilizarea obiectului de tip I1 ob1.f1(); //Crearea unui obiect de tip I12 //utilizand clasa C I12 ob12=new C(); //Utilizarea obiectului de tip I12 ob12.f1(); ob12.f2(); ob12.f3(); //Crearea unui obiect de tip I1 //utilizand clasa C I1 ob2=new C(); //Utilizare obiect de tip I1 //creat cu suport C ob2.f1(); //Down casting dirijat de //interfete ob2=(I1)ob12; ob2.f1(); } }

Exemplul 4.4 //Interfata IConst care declara //doua constante si o metoda interface IConst { int Numar=100; String s="Bine ati venit!"; void ftest();

} //A foloseste interfata IConst //Este obligata sa implementeze ftest() class A implements IConst { A() { System.out.println("Constructor.."); }; public void ftest() { System.out.println("ftest() la lucru..."); }; } public class TInterf1 { public static void main(String s[]) { //Creare variabila obiect //de tip A //se mostenesc constantele //interfetei IConst A va=new A(); System.out.println(va.Numar); System.out.println(va.s); va.ftest(); //Creare variabila obiect //de tip IConst IConst iva=new A(); System.out.println(iva.Numar); System.out.println(iva.s); iva.ftest(); }; } nainte de a pune punct subiectului s mai precizez urmtoarele: Un program nu poate crea instan e dintr-o interfat. Toate metodele unei interfe e sunt implicit publice i abstracte. Nici un alt tip nu este permis. Toate metodele trebuie s fie implementate de clasa care utilizeaz interfa a.

5 Polimorfismul n programarea orientat pe obiecte din perspectiv Java


5.1 S reamintim, pe scurt, ce este polimorfismul.
Dup cum stau lucrurile n limbajele de programare orientate pe obiecte, polimorfismul este singurul principiu a crui for se manifest n timpul execu iei programelor. Valoarea principiului motenirii este esen ial concentrat n posibilitatea de a reutiliza efortul de dezvoltare a unui sistem soft. ncapsularea este, de asemenea, un principiu a crui manifestare nu este evident dect de pe pozi ia de programator, n esen . Ar fi, ns, nedrept s nu subliniem c att ncapsularea ct i motenirea trebuie s fie mnuite cu mult abilitate pentru a ob ine efecte polimorfice de mare subtilitate i utilitate. ncercnd o defini ie a polimorfismului, independent de limbajul de programare i din punctul de vedere al programatorului care beneficiaz de el, numim polimorfism posibilitatea ca un apel de func ie (metod , opera ie) s genereze rspunsuri diferite n func ie de contextul n care a fost formulat.

5.2 Tipuri de polimorfism la nivelul limbajelor de programare. Exemplificare recapitulativ n C++.


Nevoia de polimorfism, resim it acut mai ales n programare, este n mare msur sinonim cu nevoia de confort. n stadiul n care se afl, actualmente, realizrile specialitilor n materie de polimorfism la nivelul limbajelor de programare, putem semnala urmtoarele tipuri importante de polimorfism: Polimorfismul orientat pe suprascrierea func iilor n programarea clasic. Polimorfismul orientat pe suprascrierea func iilor n cadrul defini iei unei clase. Polimorfsimul orientat pe suprancrcarea operatorilor n programarea orientat pe obiecte. Polimorfismul orientat pe redefinirea func iilor n programarea orientat pe obiecte, ntr-un lan de derivare.

Indiferent de tipul lui, polimorfismul de calitate cere investi ie de timp i creativitate, pe moment, n beneficiul unor viitoare reutilizri, cu minimum de efort din partea clien ilor. Polimorfismul orientat pe suprascrierea func iilor n programarea clasic Aceast form de polimorfism este, practic, cea mai veche. Ea presupune posibilitatea de a scrie func ii care au acelai nume, retuneaz acelai tip de dat, dar se pot deosebi prin tipul i numrul parametrilor. Aceast posibilitate este ilustrat n Exemplul 5.1, perfect legal n programarea n limbajele C/C++. Exemplul 5.1 //Suprascrierea func iilor n programarea clasic n C //Sunt specificate i implementate dou versiuni, //diferite prin lista de parametri ale func iei suma() #include <iostream.h> #include <conio.h> //Prima versiune a func iei suma() //Parametrul s este transmis prin referin void suma(float &s,int o1,int o2) { s=o1+o2; };

//A doua versiune a func iei suma() //Parametrul s este transmis prin referin void suma(float &s,int o1, int o2, int o3) {

s=o1+o2+o3; }; void main() { float st; clrscr(); //Utilizarea versiunii 2 suma(st,12,13,14); cout<<st<<endl; //Utilizarea versiunii 1 suma(st,12,13); cout<<st; getch(); }; Care sunt observa iile care se impun? Mai nti, este de remarcat faptul c trebuie s existe un programator care este suficient de informat cu privire la varia iile de comportament ale unei func ii avnd acelai nume i care returneaz acelai tip de dat. Dei nu excludem posibilitatea de a ntlni o astfel de situa ie i n alte contexte, programatorii versa i tiu foarte bine ct de mult valoreaz versionarea unei func ii n programarea generic, atunci cnd solu ia template-urilor prezint unele inconveniente. n al doilea rnd, dac versionarea este realizat cu sim de rspundere, utilizarea diferitelor versiuni n diferite situa ii este extrem de comod i benefic, gsind o solu ie de partajare a codului versiunilor ntre mai multe programe. n al treilea rnd, nu putem trece cu vederea faptul c la compilare este realizat legarea unui apel de versiune de codul aferent (acest gen de legare se numete early binding). Polimorfismul orientat pe suprascrierea func iilor n cadrul defini iei unei clase Aceast form de polimorfism satisface unele cerin e de versionare a comportamentului opera iilor unei clase, n spiritul celor spuse relativ la suprascrierea n stil clasic a func iilor. Dup cum se anticipeaz n Exemplul 5.2 (de cod C++), acest tip de polimorfism poate fi combinat cu polimorfismul orientat pe supradefinirea metodelor ntr-un lan de derivare. Exemplul 5.2 #include <iostream.h> #include <conio.h> //Clasa de baza class Super { int numar; public: Super(int n) { numar=n; }; //Versiunea 1 a functiei f1() void f1() { cout<<"Super::Functie de test"<<endl; getch(); }; //Versiunea 2 a functiei f1() // Suprascrie prima versiune a lui f1() inauntrul //clasei Super

//In raport cu clasa Baza f1()este virtuala //Deci urmeaza sa fie supradefinita virtual void f1(int n) { cout<<"Super::Numar: "<<n<<endl; getch(); }; }; //Clasa derivata class Baza:public Super { public: Baza(int n):Super(n) { }; void f1(int n) { cout<<"Baza::Numar: "<<n<<endl; getch(); }; }; void main() { //Pointer la Super Super *PSuper; //Alocare dinamic a memoriei pentru pointer-ul Psuper //n context Super PSuper=new Super(10); clrscr(); //Utilizare Psuper; apelare succesiva a doua versiuni ale //functiei f1() PSuper->f1(); PSuper->f1(10); delete PSuper; //Alocare dinamic a memoriei pentru pointer-ul PSuper //n context Baza PSuper=new Baza(12); PSuper->f1(12); delete Psuper; }; Polimorfsimul orientat pe suprancrcarea operatorilor n programarea orientat pe obiecte Subiect ocolit de specificatorii limbajului Java, ns generator de satisfac ii deosebite pentru programatorii n C++. Ideea de baz const n faptul c este la latitudinea celor care programeaz orientat pe obiecte n C++ s redefineasc comportamentul unui foarte mare numr de operatori (+, -, *, >>, <<, new, delete, etc.). Aten ie! Nu poate fi schimbat nici aritatea nici prioritatea operatorilor predefini i, prin suprancrcare. Protocolul de suprancrcare a unui operator, astfel nct acesta s opereze asupra obiectelor unei clase este urmtorul: 1. Defini ia clasei trebuie s con in o func ie operator membru sau o func ie operator prieten, avnd sintaxa special:

Varianta func ie membr <Tip returnat> operator # (<Lista de argumente>); sau Varianta func ie friend friend <Tip returnat> operator # (<Lista de argumente>); n aceast sintax, atrag aten ia cuvntul cheie operator (care informeaz compilatorul c func ia suprancarc un operator) i caracterul # care semnific un substitut pentru operatorul pe care dori i s-l suprancrca i, altul dect: . , * , :: , ? . De remarcat faptul c, alegnd varianta func ie membr, un operator binar va fi specificat ca o func ie cu un parametru, care va indica operandul din stnga, operandul din dreapta fiind vizibil prin intermediul pointerului this. De asemenea, dac alegem varianta func ie membr, un operator unar va fi implementat ca o func ie fr parametri, pointerul this permi nd referirea operandului. Defec iunea n cazul utilizrii unei func ii membru pentru suprancrcarea unui operator este clar: parametrul din stnga trebuie s fie un obiect, nu poate fi o constant. Este evident c, n aceste condi ii func iile prietene sunt de preferat. 2. Func iile operator se vor implementa folosind una din sintaxele:

<Tip returnat> <Nume clas>::operator # (<Lista de argumente>) { // Corp func ie operator specificat ca membr }; sau <Tip returnat> operator # (<Lista de argumente>) { // Corp func ie operator specificat ca prieten }; Lucrurile pot fi n elese i mai bine, urmrind Exemplul 5.3 (cod C++). Exemplul 5.3 #include<conio.h> #include<iostream.h> //Clasa complex contine functia operator + ca membru //operatorul + este extins la multimea numerelor complexe //cu ajutorul unei metode membru a clasei complex //Clasa complex contine functia operator - ca functie friend //operatorul - este extins la multime numerelor complexe //cu ajutorul unei metode friend class complex { float x,y; public: complex(){}; complex(float a,float b) { static int i; i++;

clrscr(); cout<<"Lucreaza constructorul...Obiectul->:"<<i; getch(); x=a; y=b; }; void disp_nc(); //prototipul operatorului + complex operator+(complex &op2); //prototipul operatorului friend complex operator-(complex &op1,complex &op2); }; void complex::disp_nc() { cout<<x<<"+i*"<<y; }; //Implementare operator + //Aceasta sintaxa transforma apelul <ob1+ob2> //in +(ob2), ob1 fiind accesibil prin pointerul //special <this> complex complex::operator+(complex &op2) { complex temp; temp.x=op2.x+x; temp.y=op2.y+y; return temp; }; complex operator -(complex &op1,complex &op2) { complex temp; temp.x=op1.x-op2.x; temp.y=op1.y-op2.y; return temp; }; void main() { complex tamp1,tamp2; complex *pod1,*pod2; complex ob1(10,10),ob2(11,11); clrscr(); gotoxy(20,10);cout<<"Primul numar complex ->:"; ob1.disp_nc(); getch(); gotoxy(20,11);cout<<"Al doilea numar complex->:"; ob2.disp_nc(); getch(); ob1=ob1+ob2; gotoxy(20,13);cout<<"Suma numerelor complexe->:"; ob1.disp_nc(); getch(); pod1=new complex(200,200);

pod2=new complex(300,300); tamp1=*pod1; clrscr(); gotoxy(20,10);cout<<"Al treilea numar complex tamp1.disp_nc();

->:";

tamp2=*pod2; gotoxy(20,11);cout<<"Al patrulea numar complex ->:"; tamp2.disp_nc(); gotoxy(20,14);cout<<"Suma numerelor complexe->:"; tamp1=tamp1+tamp2; tamp1.disp_nc(); tamp1=*pod1; tamp2=*pod2; tamp1=tamp1-tamp2; gotoxy(20,15);cout<<"Diferenta numerelor complexe->:"; tamp1.disp_nc(); getch(); } Polimorfismul orientat pe redefinirea func iilor n programarea orientat pe obiecte, ntr-un lan de derivare Este element suport esen ial pentru specializarea claselor ntr-un lan de derivare, specializare care se realizeaz prin redefinirea comportamentului unor metode ale strmoilor. Pentru a se mbina extinderea comportamentului cu reutilizarea codului, este de dorit ca redefinirea comportamentului s planifice utilizarea comportamentului versiunii din strmo. Exemplul 5.4 ne arat cum se pune problema redefinirii n C++. Exemplul 5.4 #include <iostream.h> #include <conio.h> //Structura suport pentru pastrarea //coordonatelor varfurilor poligoanelor struct Varf { int x,y; Varf *Legs; }; //Clasa Poligon //Clas abstracta->nu are constructor i destructor //Furnizeaz prototipurile metodelor definitie() i arie() //ca metode virtuale pure. //Furnizeaz implementarea pentru metodele: // perimetru() // ElibMem() // setare_pvarfuri() // consultare_pvarfuri() // setare_nrvarfuri() // consulatre_nrvarfuri() class Poligon { Varf *pvarfuri; int nrvarfuri;

public: //Metode virtuale pure //Vor fi redefinite n descenden i virtual void definitie()=0; virtual float arie()=0; float perimetru(); void ElibMem(); void setare_pvarfuri(Varf *p); Varf * consultare_pvarfuri(); void setare_nrvarfuri(int nv) { nrvarfuri=nv; }; int consultare_nrvarfuri() { return nrvarfuri; }; }; float Poligon::perimetru() { cout<<"perimetru(): "; cout<<"Calculul perim. este neimplem... Poligon" <<endl; getch(); return 0; }; void Poligon::ElibMem() { Varf*pwork; while (pvarfuri!=NULL) { pwork=pvarfuri->Legs; delete pvarfuri; pvarfuri=pwork; }; }; void Poligon::setare_pvarfuri(Varf *p) { pvarfuri=p; }; Varf * Poligon::consultare_pvarfuri() { return consultare_pvarfuri(); }; //Clasa Triunghi //Clas concret avnd ca superclas clasa Poligon //Redefinete comportamentul metodelor: // definitie(); arie() //Furnizeaz constructor i destructor class Triunghi:public Poligon {

public: Triunghi(Varf *pt,int tnrv) { setare_pvarfuri(pt); setare_nrvarfuri(tnrv); cout<<"Constructor Tringhi..."<<endl; }; virtual ~Triunghi() { cout<<"Destructor Triunghi..."<<endl;; ElibMem(); }; //Redefinire metode void definitie(); float arie(); }; void Triunghi::definitie() { cout<<"definitie(): "; cout<<"Triunghiul este poligonul cu trei laturi"<<endl; getch(); }; float Triunghi::arie() { cout<<"arie(): "; cout<<"Neimplementata deocamdata...Triunghi"<<endl; getch(); return 0; }; class Patrulater:public Poligon { public: Patrulater(Varf *pt,int tnrv) { setare_pvarfuri(pt); setare_nrvarfuri(tnrv); cout<<"Constructor Patrulater..."<<endl; }; //Destructor virtual virtual ~Patrulater(); void definitie(); float arie(); }; Patrulater::~Patrulater() { ElibMem(); cout<<"Destructor Patrulater..."<<endl; }; void Patrulater::definitie() { cout<<"definitie(): ";

cout<<"Patrulaterul este poligonul cu patru laturi"<<endl; getch(); }; float Patrulater::arie() { cout<<"arie(): "; cout<<"Neimplementata deocamdata...Patrulater"<<endl; getch(); return 0; }; class Paralelogram:public Patrulater { public: Paralelogram(Varf *pt,int tnrv):Patrulater(pt,tnrv) { cout<<"Constructor Paralelogram..."<<endl; }; //Destructor virtual virtual ~Paralelogram() { ElibMem(); cout<<"Destructor Paralelogram..."<<endl; }; //Redefinire metode void definitie(); float arie(); }; void Paralelogram::definitie() { cout<<"definitie(): "; cout<<"Paralelogramul este patrulat. cu laturile paral. doua cate doua"<<endl; getch(); }; float Paralelogram::arie() { cout<<"arie(): "; cout<<"Neimplementata deocamdata...Paralelogram"<<endl; getch(); return 0; }; class Dreptunghi:public Paralelogram { public: Dreptunghi(Varf *pt,int tnrv):Paralelogram(pt,tnrv) { cout<<"Constructor dreptunghi..."<<endl; }; virtual ~Dreptunghi() { ElibMem(); cout<<"Destructor Dreptunghi..."<<endl;

}; void definitie(); float arie(); }; void Dreptunghi::definitie() { cout<<"definitie(): "; cout<<"Dreptunghiul este paralelogramul cu un unghi drept"; cout<<endl; getch(); }; float Dreptunghi::arie() { cout<<"arie(): "; cout<<"Neimplementata deocamdata...Dreptunghi"<<endl; return 0; }; void main() { Poligon *RefPol; Patrulater *RefPatr; clrscr(); RefPol=new Triunghi(NULL,3); RefPol->arie(); RefPol->definitie(); RefPol->perimetru(); cout<<endl; delete RefPol; RefPatr=new Patrulater(NULL,4); RefPatr->arie(); RefPatr->definitie(); RefPatr->perimetru(); cout<<endl; delete RefPatr; RefPatr=new Paralelogram (NULL,4); RefPatr->arie(); RefPatr->definitie(); RefPatr->perimetru(); delete RefPatr; }; Pentru o mai bun n elegere a Exemplului 5.4, sunt necesare o serie de precizri n ceea ce privete genul de polimorfism ilustrat. Mai nti, din punct de vedere sintactic, trebuie s observm faptul c informm compilatorul de inten ia de redefinire a unei metode n aval (ntr-un lan de derivare) prin specificarea acesteia n clasa gazd ca metod virtual sau ca metod virtual pur. Prototipul unei metode virtuale are sintaxa: virtual <Tip returnat> <Nume metoda>([<Lista de parametri>]); Prototipul unei metode virtuale pure are sintaxa: virtual <Tip returnat> <Nume metoda>([<Lista de parametri>])=0;

Clasele care con in cel pu in o metod virtual pur sunt clase abstracte, deci nu pot avea instan e directe, neavnd nici constructori. n schimb, clasele abstracte pot fi folosite pentru a declara referin e ctre descenden i, ceea ce exte extrem de folositor dac dorim polimorfism. De remarcat c redefinirea se bazeaz pe o restric ie important: n procesul de redefinire se conserv signatura (numrul de parametri, tipul lor i tipul returnat). Odat ce o metod a fost declarat virtual sau virtual pur, compilatorul tie c aceast metod este posibil s fie redefinit n descenden i i, de asemenea, compilatorul tie c pentru clasa care con ine metode virtuale i pentru toate clasele descendente ei, la crearea primului obiect, constructorul va crea i tabela VMT (Virtual Methode Table), o structur partajat de toate obiectele unei clase, folosit de sistem pentru a realiza genul de legare a unui apel de codul contextual, numit late binding. Prin urmare, atunci cnd se creaz un obiect, al crui tip definitor este undeva ntr-un lan de derivare, dac n amonte a existat inten ie de redefinire a unor metode, sistemul va crea, numai n cazul primului obiect de tipul respectiv, o tabel care con ine adresele metodelor virtuale ale clasei. Aceste adrese vor fi utilizate n procesul de late binding. S mai observm faptul c, fr a fi prefixa i de cuvntul cheie virtual, destructorii sunt apela i pe principiul ntotdeauna lucreaz constructorul tipului definitor al unei variabile obiect sau al unui pointer la un obiect, ceea ce nseamn un gen de legare static a destructorului. Dac dorim legare dinamic, atunci destructorul este declarat ca virtual. Efectul poate fi urmrit n Exemplul 5.4.

5.3 Polimorfismul n context Java


Java implementeaz principiul polimorfismului la scara posibilit ilor proprii. n Java nu avem dect programare orientat pe obiecte, oricare ar fi calitatea acesteia. Astfel c se ofer suport pentru polimorfism orientat pe suprascrierea func iilor i polimorfism orientat de supradefinire. Java nu ofer sintax pentru suprancrcarea operatorilor, deci nu este posibil polimorfismul aferent. Merit s remarcm faptul c supradefinirea n Java este mai simpl dect n C++, din punct de vedere sintactic vorbind. Pur i sumplu, dac compilatorul sesizez c n amontele unui lan de derivare exist o metod care este supradefinit n aval, atunci compilatorul genereaz informa ii necesare pentru realizarea legrii la execu ie. Cerin a conservrii signaturii n procesul de supradefinire este prezent i n Java. Un model de utilizare a polimorfismului se poate observa n Exemplul 5.5. Exemplul 5.5 //Clasa radacina class Poligon { private String definitie; public Poligon(String d) { definitie=new String(d); }; public String citesteDefinitie() { return definitie; }; //Metoda va fi supradefinita in descendenti public void arie() { System.out.println("Poligon...neimplementata!"); }; }; class Triunghi extends Poligon { public Triunghi(String d)

{ super(d); }; //Supradefinire public void arie() { System.out.println("Triunghi...neimplementata!"); }; }; class Patrulater extends Poligon { public Patrulater(String d) { super(d); }; //Supradefinire public void arie() { System.out.println("Patrulater...neimplementata!"); }; }; public class Polimorf { public static void main(String[] s) { //Referinta la radacina Poligon PRef; //Alocare in context Poligon PRef=new Poligon("Linie franta inchisa"); System.out.println(PRef.citesteDefinitie()); //Sintaxe la utilizare este aceeasi in cele trei contexte PRef.arie(); System.out.println(""); //Alocare in context Triunghi PRef=new Triunghi("Poligonul cu trei laturi"); System.out.println(PRef.citesteDefinitie()); PRef.arie(); System.out.println(""); //Alocare in context Patrulater PRef=new Patrulater("Poligonul cu patru laturi"); System.out.println(PRef.citesteDefinitie()); PRef.arie(); System.out.println(""); }; }; Nu am motive s reiau discu ia pe marginea mecanismului de legare dinamic a metodelor supradefinite n Java. Chiar dac compilatorul folosete alt gen de informa ii, la intrare, rezultatul final, pentru programator este acelai. Nu consider o problem deosebit comentarea i exemplificarea suprascrierii n clasele Java.

6 Tratarea structurat a excep iilor n programarea orientat pe obiecte


6.1 O problem, n plus, n programare: tratarea excep iilor
Programatorii adevra i trebuie s ia, obligatoriu, n calcul i posibilitatea de a crea programe robuste, care fac fa att cerin elor specificate dar nerafinate suficient, ct i cerin elor nespecificate dar formulate de utilizator, din diverse motive. Programele care au aceste calit i se numesc robuste. n programarea clasic, solu ia acestei probleme se putea numi, destul de exact spus, programare defensiv. Seamn pu in cu conducerea preventiv din oferie dac ne gndim c programnd defensiv, n fond punem rul nainte, deci nu ne bazm pe cumsecdenia i buna pregtire a utilizatorului. ncercarea de a trata situa iile de excep ie care pot apare la execu ia unui program, folosind metode clasice (programarea defensiv) duce la creterea semnificativ a complexit ii codului ceea ce afecteaz, n mod direct, lizibilitatea i, n mod indirect, corectitudinea codului Pentru a face fa cerin elor legate de problema tratrii excep iilor (aa se numesc n jargon profesional erorile care apar n timpul execu iei programelor) anumite limbaje de programare ofer suport adecvat. Includem aici limbaje precum: Object Pascal, C++, Java, Visual C++. Nu toate compilatoarele de C++ ofer suport, dar standardul ANSI C++ cere acest lucru n mod explicit. Compilatoarele din familia Borland, ncepnd cu versiunea 4.0 ofer acest suport. Esen ialul din punctul de vedere al programatorului C++ este ca el s-i formeze abilitatea de a scrie, n jurul aplica iilor, cod C++ care ndeplinete func ia de handler de excep ii.

6.2 Maniera Java de tratare a excep iilor


Aa cum am men ionat deja, ncercarea de a trata situa iile de excep ie care pot apare la execu ia unui program, folosind metode clasice (programarea defensiv) duce la creterea semnificativ a complexit ii codului, ceea ce afecteaz, n mod direct, lizibilitatea i, n mod indirect, corectitudinea codului. Din aceast cauz cei care au creat Java au gndit un sistem de tratare a excep iilor (n continu evolu ie, de la o versiune la alta a limbajului Java) care s permit programatorului: Tratarea situa iilor de excep ie, pe ct posibil, independent de fluxurile de control normale; Tratarea excep iilor, la un nivel superior celui n care apar; Propagarea excep iilor la nivelele superioare n mod ierarhic; Tratarea unitar a excep iilor de acelai tip.

n mare parte, asemntor sistemului C++ de tratare a excep iilor, sistemul Java are, totui, o ofert mai bine pus la punct din acest punct de vedere. Java se bazeaz pe un numr restrns de cuvinte cheie (try, catch, throw, finally, throws) i pe o ierarhie de clase, specializate n tratrarea unor clase de erori. Pentru a n elege mai bine mecanismul tratrii excep iilor n Java, consider c este util o scurt descriere a modului n care apar i sunt procesate excep iile n Java. Astfel, cnd apare o excep ie n interiorul unei metode a unei clase Java, se creeaz un obiect excep ie (obiect ce caracterizeaz excep ia i starea programului n momentul cnd excep ia apare). Odat creat acest obiect, el este aruncat utiliznd cuvntul cheie throw. Sarcina crerii i aruncrii obiectului excep ie apar ine programatorului. Din momentul aruncrii unui obiect excep ie, folosind cuvntul cheie throw, maina virtual Java (JVM), prin componenta RuntimeSystem, preia obiectul i l transmite secven ei de cod responsabil de tratarea excep iei respective. n acest scop, RuntimeSystem va cuta un handler al excep iei (=o secven de cod responsabil de tratarea excep iei), ncepnd de la nivelul (nivelul este o metod) n care a aprut excep ia i continund la nivelele superioare. Cutarea se face n urm (backward), utiliznd stiva de apel (call stack). Primul handler (un bloc try-catch), corespunztor obiectului excep ie, se va ocupa de solu ionarea excep iei. Dac RuntimeSystem a epuizat stiva de apel, fr a gsi o metod care s ofere un handler al obiectului excep ie aruncat, RuntimeSystem va fi responsabil de tratarea excep iei respective (va afia un mesaj de eroare i va opri firul de execu ie). Mecanismul descris mai sus poate fi vizualizat ca n Figura 19. Problema care se afl n fa a programatorului este, evident, urmtoarea: cum poate folosi ra ional mecanismul respectiv? Spun aceasta deoarece, ca orice facilitate a limbajului i suportul oferit pentru tratarea

excep iilor poate fi utilizat n mod abuziv. A abuza de tratarea excep iilor nseamn a vedea excep ii i unde nu este cazul, fapt care provoac complexificarea artificial a codului i, foarte important, diminueaz performan ele programului, deoarece, a cum rezult i din Figura 17, mecanismul tratrii excep iilor consum resurse pentru a se desfura corespunztor. De aceea, este necesar o disciplinare a gndirii programatorului, n ceea ce privete decizia de a considera excep ie sau nu un anumit context de prelucrare, n interiorul unei metode i apoi, decizia de a aborda, ntr-un anumit mod, problema tratrii excep iei respective. n esen , programatorul trebuie s acumuleze suficien experien nct s deosebeasc o excep ie nerecuperabil de o excep ie din care se poate reveni.
RuntimeSuystem - JVM -preia obiectul excep ie -caut, ncepnd cu nivelul j, n sus, primul handler corespunztor (=primul handler care rezolv o excep ie de tipul celei aruncate) -transfer obiectul excep ie handler-ului Nivel_1

Nivel_i Nivel Tratare Excep ie Con ine un handler (un bloc try-catch) -preia obiect excep ie -trateaz excep ie

Nivel_j Nivel apari ie excep ie -creaz obiectul excep ie Exception exc=new Exception(); -arunc excep ia throw(exc);

Figura 17. Mecanismul Java de tratare a excep iilor


Pentru a crea un obiect excep ie, Java pune la dispozi ia utilizatorului o ierahie de clase, aflat n pachetul java.lang. Pe lng clasele de tip excep ie aflate n java.lang, fiecare pachet Java introduce propriile tipuri de excep ii. Utilizatorul nsui poate defini clase de tip excep ie, care ns pentru a avea instan e compatibile cu sistemul Java, trebuie s fie descenden i ai clasei Throwable, clas care ocup o pozi ie important n ierarhia simplificat a claselor de tip excep ie, prezentat n Figura 18.

Throwable

Exception

Error

RuntimeException

Figura 18. Ierarhia simplificat a claselor de tip excep ie din pachetul java.lang
Dup cum se poate observa n Figura 18, clasa Throwable are doi descenden i: clasa Error i clasa Exception. Nici una din cele dou clase nu adaug metode suplimentare, dar au fost introduse pentru a delimita

dou tipuri fundamentale de excep ii care pot apare ntr-o aplica ie Java (de fapt, acest mod de gndire este aplicabil n orice limbaj de programare care ofer suport pentru tratarea sistematic a excep iilor). Clasa Error corespunde excep iilor care nu mai pot fi recuperate de ctre programator. Apari ia unei excep ii de tip Error impune terminarea programului. Aruncarea unei excep ii de tip Error nseamn c a aprut o eroare deosebit de grav n execu ia programului sau n maina virtual Java. n marea majoritate a cazurilor, aceste excep ii nu trebuie folosite de ctre programator, nu trebuie prinse prin catch, i nici aruncate prin throw de ctre programator. Aceste tipuri de erori sunt utilizate de JVM, n vederea afirii mesajelor de eroare. Clasa Exception este, de fapt, clasa utilizat efectiv de ctre programatori n procesul de tratare a excep iilor. Aceast clas i descenden ii ei modeleaz excep ii care pot fi rezolvate de ctre program, fr a determina oprirea programului. Prin urmare, regula este simpl: dac Java nu con ine o clas derivat din Exception care poate fi utilizat ntr-un anumit context, atunci programatorul va trebui s o implementeze, el nsui, ca o clas derivat din Exception. Exist o mare varietate de clase derivate din Exception care pot fi utilizate. Mai mult, fiecare pachet Java adaug noi tipuri de clase derivate din Exception, clase legate de func ionalitatea pachetului respectiv. Dac astfel lucreaz cei de la SUN, de ce n-ar lucra la fel i un programator oarecare? Din categoria claselor derivate din Exception, se remarc clasa RuntimeException13 i clasele derivate din ea. Din aceast categorie fac parte excep ii care pot apare n execu ia unui program, n urma unor opera ii nepermise de genul: opera ii aritmetice interzise (mpr ire la zero), acces nepermis la un obiect (referin null), depirea index-ului unui tablou sau ir, etc. Nu ne rmne dect s prezentm protocolul de lucru cu excep ii n Java. Aruncarea excep iilor Aruncarea unei excep ii se face cu ajutorul cuvntului cheie throw, conform sintaxei: throw <obiectExceptie>; unde <obiectExceptie> este o instan a a clasei Throwable sau a unei clase, derivat din aceasta. Evident, n locul variabilei <obiectExceptie> poate fi o expresie care returneaz un obiect de tip convenabil. De fapt, n practic, modul de aruncare a unei excep ii urmeaz schema: throw new <clasaExceptie>(Mesaj); Evident, putem avea i cazuri n care o func ie poate arunca n mod indirect o excep ie, ceea ce nseamn c func ia nu va con ine o expresie throw, ci va apela o func ie care poate arunca o excep ie. O metod poate arunca mai multe excep ii. Important este s n elegem c prin aruncarea unei excep ii se iese din metod fr a mai executa secventele de cod care urmau. n cazul n care o func ie arunc o excep ie, fie prin throw , fie prin apelul unei func ii, fr a avea o secven try-catch de prindere atunci aceast func ie trebuie s specifice clar aceast inten ie n defini ia func iei. Pentru acest caz, sintaxa de definire a func ie este: public void <numeMetoda> throws <clasExcept1>,<clasExcept2>, { throw <obiectExcep1>; throw <obiectExcept2>; };

13

Detalii cu privire la descenden ii clasei RuntimeException se pot gsi n Clin Marin Vduva, Programarea n Java, Editura Albastr, 2001.

Prinderea excep iilor Pentru a beneficia de avantajele mecanismului de tratare a excep iilor, odat ce am aruncat o excep ie este nevoie s o prindem. Prinderea unei excep ii se face prin intermediul unui bloc try-catch, a crui sintax generic este prezentat mai jos. try { //Cod ce poate arunca o excep ie } catch(<clasExcept1> <idExcept1>) { //handler exceptie de tip <clasExcept1> } catch(<clasExcept2> <idExcept2>) { //handler exceptie de tip <clasExcept2> } [finally { //secven a de cod executat oricum }] Dup cum se poate observa, structura de prindere a excep iilor poate fi delimitat n trei blocuri. Blocul try, numit i bloc de gard atrage aten ia c secven a de cod inclus n el poate arunca, n anumite condi ii, excep ii. n cazul n care acest lucru nu se ntmpl, secven a din interiorul blocului de gard se execut n ntregime, controlul fiind predat primei instruc iuni de dup construc ia try-catch. n cazul n care se arunc o excep ie, execu ia secven ei din blocul de gard se ntrerupe i se declaneaz procedura de tratare a excep iei. Tratarea excep iei se poate face prin intermeiul blocurilor catch, numite i handlere de excep ii. n momentul n care apare o excep ie n regiunea de gard, se parcurge lista blocurilor catch n ordinea n care apar n programul surs. n cazul n care excep ia aruncat corespunde unui bloc catch, se execut codul eferent blocului i se termin cutarea n list, considerndu-se c excep ia a fost rezolvat. Sintaxa ne arat c pot exista mai multe blocuri catch, ceea ce nseamn c n blocul de gard pot fi aruncate excep ii de mai multe tipuri. Situa iile deosebite care pot apare n utilizarea blocurilor catch sunt urmtoarele: am putea dori s tratm excep ii de tip EC1 i EC2, unde EC2 este o clas derivat din EC1. Datorit faptului c blocurile catch sunt parcurse secven ial este necesar s avem handler-ul clasei EC2 naintea handler-ului clasei EC1, altfel, nu se va ajunge niciodat la secven a catch de tratare a excep iilor de tipul EC2. De asemenea, putem avea situa ii n care s dorim tratarea unei excep ii pe mai multe nivele. n acest caz, se poate lua n considerare faptul c, odat prins o excep ie ntr-un bloc catch, o putem re-arunca cu un apel simplu de tip throw. n sfrit, blocul finally, dac este folosit, cuprinde secven a de cod care se va executa, indiferent dac apare sau nu o excep ie, situa ie reclamat de nenumrate contexte n care apari ia unei excep ii, ca i lipsa acesteia, presupun rezolvarea unor probleme care pot scuti sistemul de introducerea unor elemente perturbatoare prin nerezolvarea lor. n Exemplul 6.1 i n Exemplul 6.2 se pot vedea elementele de baz ale tratrii excep iilor ntr-o aplica ie Java. Utilizarea cuvntului cheie finally nu mi se pare o problem deosebit. Exemplul 6.1 //Metoda arunca o exceptie la nivelul superior //Clasa care modeleaza exceptiile clasei NumarReal class ENumarReal extends Exception { public ENumarReal(String s) { super(s);

}; } //Clasa NumarReal //o tentativa de modelare a lucrului cu numere reale class NumarReal { private double numar; public NumarReal(double nr) { numar=nr; }; public double getNumar() { return numar; }; //Metoda div() imparte doua numere reale //Suspecta de a arunca o exceptie la impartirea la zero //Declara acest lucru cu ajutorul cuvantului cheie throws public NumarReal div(NumarReal n) throws ENumarReal { if (n.getNumar()==0) throw new ENumarReal("Exceptie...Impartire la zero..."); else return new NumarReal(this.getNumar()/n.getNumar()); }; } public class Except1 { public static void main(String [] s) { //Blocul de garda care capteaza exceptia aruncata //de metoda div() try { NumarReal onr1=new NumarReal(12); NumarReal onr2=new NumarReal(6); System.out.println(onr1.div(onr2).getNumar()); onr1=new NumarReal(11); onr2=new NumarReal(0); onr1.div(onr2); } catch(ENumarReal e) { System.out.println(e.getMessage()); }; }; }; Exemplul 6.2 //Metoda arunca o exceptie dar o si capteaza //Clasa care modeleaza exceptiile clasei NumarReal class ENumarReal extends Exception

{ public ENumarReal(String s) { super(s); }; } //Clasa NumarReal //o tentativa de modelare a lucrului cu numere reale class NumarReal { private double numar; public NumarReal(double nr) { numar=nr; }; public double getNumar() { return numar; }; //Metoda div() imparte doua numere reale //suspecta de a genera o exceptie la impartirea la zero //Are bloc try-catch pentru captarea si tratarea //exceptiei public NumarReal div(NumarReal n) { try { if (n.getNumar()==0) throw new ENumarReal("Exceptie...Impartire la zero..."); else return new NumarReal(this.getNumar()/n.getNumar()); } catch(ENumarReal e) { System.out.println(e.getMessage()); return new NumarReal(0); }; }; } public class Except { public static void main(String [] s) { NumarReal onr1=new NumarReal(12); NumarReal onr2=new NumarReal(6); System.out.println(onr1.div(onr2).getNumar()); onr1=new NumarReal(11); onr2=new NumarReal(0); onr1.div(onr2); }; };

7 Programare generic n C++ i Java


7.1 Ce este programarea generic
Adeseori, programatorul se afl n situa ia de a efectua acelai tip de prelucrare asupra unor tipuri de date diferite. Solu ia nceptorului este scrierea de cod complet pentru fiecare tip de dat. Vrem s sortm un fiier dup o cheie ntreag? Scriem o func ie care realizeaz acest lucru, folosind, de exemplu, metoda bulelor. Vrem s sortm un fiier dup o cheie alfanumeric? Scriem o func ie care tie s sorteze fiierul dup o astfel de cheie, folosind tot metoda bulelor. Nu ne va fi greu s observm c, n cele dou rezolvri date de noi exist un element de invarian : codul ablon care efectueaz sortarea. Deosebirile se refer la tipurile de date implicate n procesul de sortare (fiierele pot avea nregistrri de lungime diferit i, evident, cu structur diferit iar cheile de sortare pot fi diferite ca tip). Problema n fa a creia ne aflm nu este o problem de algoritmic ci una de tehnic de programare. Programarea care are n vedere specificarea unor structuri de prelucrare capabile s opereze asupra unor tipuri variate de date se numete programare generic. Evident, exist limbaje de programare n specificarea crora au fost prevzute i elemente suport pentru rezolvarea acestui tip de problem. De exemplu, n Object Pascal se poate face programare generic apelnd la tipuri procedurale i la referin ele de tip pointer. n C++ se pot utiliza, n scopuri generice, suprascrierea func iilor, conceptul de pointer, func iile ablon sau clasele ablon i pointerii la func ii. n sfrit, n Java, utiliznd cu abilitate motenirea i interfe ele putem simula genericitatea de o manier destul de acceptabil. A eviden ia, dintre toate tipurile de elemente suport prezentate mai sus, clasele ablon din C++, socotite abstrac ii foarte puternice, care permit simularea a ceea ce, n ingineria softului, numim metaclase.

7.2 Genericitatea n Java


Java nu dispune de pointeri i de template-uri n adevratul sens al cuvntului. S-ar putea crede c genericitatea este dificil sau aproape imposibil n Java. Adevrul este c lucrurile nu stau chiar aa. n programarea orientat pe obiecte Java, putem combina for a referin elor la clase cu puterea oferit de motenire i interfe e pentru a ob ine un suport interesant pentru programarea generic. Motenirea ajut la crearea cadrului organizat n care putem specifica mai multe tipuri de obiecte asupra crora efectum aceleai prelucrri. Conversiile down, permise ntre rubedeniile unei ierarhii de clase sunt esen iale pentru a implementa genericitatea. Interfe ele ajut la specificarea cadrului natural de introducere, n Java, a referintelor la metodele membre ale unor clase. Exemplul 7.1 ilustreaz rolul motenirii n scrierea de cod Java pentru crearea i vizualizarea unei liste simplu nln uite generice. Exemplul 7.2 ilustreaz simularea pointerului la o metod generic, n Java, cu ajutorul unei instan e a unei clase singleton. Exemplul 7.3 ilustreaz simularea pointerului la o metod generic, n Java, cu ajutorul interfe elor. Exemplul7.1 //Clasa care modeleaza nodul listei //capabil sa pastreze orice tip de data class Nod { private Object inf; Nod legs; public Object read() { return inf; }; public void write(Object x) { inf=x; }; } //Clasa care modeleaza comportamentul

//unei liste simplu inlantuite class Lista { Nod start; Nod prec; public Lista() { start=null; }; public void adaugdupa(Object on) { if(start==null) { Nod tamp=new Nod(); start=tamp; start.legs=null; start.write(on); prec=start; } else { Nod tamp=new Nod(); tamp.write(on); prec.legs=tamp; tamp.legs=null; prec=tamp; } } //Metoda nu respecta cerintele //care i-ar da dreptul sa figureze //in API-ul clasei. //Am specificat-o din motive didactice. //Se poate observa un prilej potrivit pentru utilizarea //enuntului instance of //In intentie, aceasta metoda este un iterator public void PentruToate() { Nod w=start; int tip=0; Integer i; String s; do { if(w.read() instanceof Integer) tip=1; if(w.read() instanceof String) tip=2; switch(tip) { case 1: { i=(Integer)(w.read()); System.out.println(i);break; } case 2: { s=(String)(w.read()); System.out.println(s);break; }

}; w=w.legs; } while(w!=null); } } public class CreLisGen { public static void main(String[] arg) { Nod obiect; Lista lis=new Lista(); for(int i=0;i<8;i++) { obiect=new Nod(); obiect.write(new Integer(i).toString()); lis.adaugdupa(obiect.read()); obiect=new Nod(); obiect.write(new Integer(i)); lis.adaugdupa(obiect.read()); } lis.PentruToate(); } } Exemplul 7.2 //Clasa pretextInt introduce o strategie concreta de comparare //relativ la numere intregi //Exemplu de clasa SINGLETON class pretextInt { //Constructor privat //pentru a asigura caracterul de singleton private pretextInt() { }; public static final pretextInt INSTANCE=new pretextInt(); public int comp(Object a,Object b) { Integer ia,ib; ia=(Integer)a; ib=(Integer)b; if(ia.intValue()<ib.intValue())return -1; if(ia.intValue()==ib.intValue())return 0; else return 1; }; }; //Clasa pretextInt introduce o strategie concreta de comparare //relativ la numere reale in virgula mobila //Exemplu de clasa SINGLETON class pretextFlo

{ //Constructor privat //pentru a asigura caracterul de singleton private pretextFlo() { }; public static final pretextFlo INSTANCE=new pretextFlo(); public int comp(Object a,Object b) { Float fa,fb; fa=(Float)a; fb=(Float)b; if(fa.floatValue()<fb.floatValue())return -1; if(fa.floatValue()==fb.floatValue())return 0; else return 1; }; }; public class Simpfunc { public static void main(String[] s) { Integer nri1=new Integer(100); Integer nri2=new Integer(200); Float nrf1=new Float(1.75); Float nrf2=new Float(1.0); System.out.println(pretextInt.INSTANCE.comp(nri1,nri2)); System.out.println(pretextFlo.INSTANCE.comp(nrf1,nrf2)); }; }; Exemplul 7.3 //Interfata prin intermediul careia se va simula //ideea de pointer la metoda comp interface SimPointMet { int comp(Object a,Object b); }; //Clasa gazda a primei versiuni a metodei comp //Va utiliza interfata SimPointMet class pretextInt implements SimPointMet { public pretextInt() { }; public int comp(Object a,Object b) { Integer ia,ib; ia=(Integer)a; ib=(Integer)b; if(ia.intValue()<ib.intValue())return -1; if(ia.intValue()==ib.intValue())return 0; else return 1;

}; }; //Clasa gazda a celei de-a doua versiuni a metodei comp //Va utiliza interfata SimPointMet class pretextFlo implements SimPointMet { public pretextFlo() { }; public int comp(Object a,Object b) { Float fa,fb; fa=(Float)a; fb=(Float)b; if(fa.floatValue()<fb.floatValue())return -1; if(fa.floatValue()==fb.floatValue())return 0; else return 1; }; }; public class PointMet { public static void main(String[] s) { //Interfata SimPointMet lucreaza in context pretextInt Integer ni1,ni2; Float nf1,nf2; SimPointMet INSTANCE1=new pretextInt(); //Interfata SimPointMet lucreaza in context pretextFlo SimPointMet INSTANCE2=new pretextFlo(); ni1=new Integer(100); ni2=new Integer(200); nf1=new Float(200); nf2=new Float(100); System.out.println(INSTANCE1.comp(ni1,ni2)); System.out.println(INSTANCE2.comp(nf1,nf2)); }; };

8 Fluxuri obiect orientate i serializare n Java


8.1 Scurt introducere
Dei mai tnr dect C++, Java a acumulat deja o experien apreciabil n ceea ce privete rezolvarea problemei persisten ei datelor. El propune mai multe ierarhii de clase, care pun n valoare conceptul, deja clasic, de flux i propune i elemente suport pentru serializarea colec iilor de obiecte. La fel ca n C++, stream-urile Java ofer posibiliatea tratrii unitare a interfe elor de comunicare ntre entit ile unui sistem informatic, fie ele entit i soft sau hard. Un stream este un canal de comunica ie generalizat, definit n mod unic prin capetele sale: sursa i destina ia. De cele mai multe ori, unul din capete este chiar programul n care se declar stream-ul. i n Java, exist dou tipuri fundamentale de stream-uri: input stream-urile, utilizate pentru citirea datelor din diferite surse i output stream-urile, utilizate pentru scrierea datelor n diferite destina ii. Mai putem observa i alte asemnri ntre perspectiva Java i perspectiva C++, n ceea ce privete persisten a: exist fluxuri standard i alte fluxuri dect cele standard (relativ la fiiere, relativ la iruri de caractere, relativ la buffe-re de octe i), exist filtre de diferite tipuri. Programatorul care vrea s nve e s lucreze eficient cu fluxurile n Java, se izbete de o situa ie oarecum asemntoare celei din C++, dac nu cumva mai rea: Instrumentele puse la dispozi ie de Sun sunt extrem de diversificate i se promoveaz chiar filozofii diferite de lucru cu fluxurile, datorit faptului c prima ierarhie de clase care fundamenta lucrul cu fluxuri era orientat pe 8 bi i ( dou ierarhii avnd drept clase rdcin clasele InputStream i OutputStream ) iar din ra iuni de implementare a conceptului de Internationalization s-a dezvoltat o solu ie alternativ care este orientat pe 16 bi i (dou ierarhii avnd drept clase rdcin clasele Reader i Writer)14. Astfel c, programatorul se confrunt cu dou ierarhii de clase, ntre care exist destule asemnri pentru a nu dispera cu totul dar i destule deosebiri pentru a nu putea renun a la nici una dintre ele deocamdat. Cert este c solu ia Java pentru lucrul cu fluxuri este puternic orientat pe obiecte, ca solu ie tehnic. n sfrit, s mai precizm faptul c oferta C++ pentru salvarea-restaurarea obiectelor i gsete n Java un rspuns mai ndrzne , sub forma serializrii. Despre toate acestea n cele ce urmeaz.

8.2 Stream-uri standard n Java


Java pune la dispozi ia utilizatorului, n ideea comunicrii cu consola, trei stream-uri standard: Standard Input Standard Output StandardError.

Stream-ul Standard Input este utilizat pentru preluarea datelor, n timp ce celelalte dou sunt utilizate pentru afiarea datelor i a mesajelor de eroare. Implicit, Standard Input preia datele de la tastatur iar celelalte dou afieaz datele la monitor. Unul dintre avantajele utilizrii stream-urilor, n comunicarea cu utilizatorul, l reprezint i posibilitatea de a redirecta stream-urile standard spre alte periferice. n Java, toate stream-urile standard sunt accesate prin clasa System: pentru Standard Input avem System.in, pentru Standard Output avem System.out, pentru Standard Error avem System.err. System.in este un membru static al clasei System i este de tipul InputStream, o clas abstract din pachetul java.io. O parte dintre func iile clasei InputStream i aspecte relativ la redirectare n cele ce urmeaz.

14 Pentru mai multe detalii relativ la structura acestor ierarhii se poate consulta Clin Marin Vduva, Programarea n Java, Editura Albastr, Cluj-Napoca, 2001

Func ii de citire i de control al pozi iei la citire: public abstract int read() throws IOException public int read(byte b[]) throws IOException public int read(byte b[], int off, int len) throws IOException public long skip(long n) throws IOException Func ii de repetare citire, func ii de gestiune buffer: public synchronized void mark (int readlimit) public synchronized void reset() throws IOException public boolean markSuported() Func ii de informare: public int available() throws IOException Func ia de nchidere stream: public void close() throws IOException. De fapt, aceste metode ale clasei InputStream prefigureaz elementele fundamentale ale strategiei Java de lucru cu fluxurile. Func ia read(), fr nici un parametru, citete octetul curent din stream i l returneaz sub forma unui ntreg ntre 0 i 255. Dac s-a ajuns la captul stream-ului, se returneaz valoarea -1. Func iile read, avnd ca parametru un tablou de octe i, citesc de la pozi ia curent din stream un numr de octe i egal cu len sau cu lungimea tabloului b i l ncarc n tabloul b, la pozi ia off dac aceasta este specificat. Ele returneaz numrul de octe i citi i n buffer-ul b sau -1 dac s-a ajuns la captul stream-ului. Func ia skip este utilizat pentru a muta pozi ia citirii peste un anumit numr de octe i. Toate aceste metode blocheaz firul de execu ie n care ne aflm, pn cnd toate datele care se cer sunt disponibile, s-a ajuns la sfritul stream-ului sau s-a aruncat o excep ie. Redirectarea stream-urilor standard se poate realiza cu ajutorul urmtoarelor trei func ii, disponibile n clasa System: public static void setIn(InputStream in) public static void setOut(PrintStream out) public static void setErr(PrintStream err) n Exemplul 8.1 sunt artate elementele de protocol fundamentale pentru lucrul cu stream-uri n Java, cu referire la stream-urile standard. Este vorba despre urmtoarele elemente invariabile: Asocierea fluxului cu un fiier, echipament standard sau alt structur de date. Efectuarea de opera ii de tipul citire sau scriere de date. Pozi ionarea n flux, cnd acest lucru este posibil nchiderea fluxului

Aa cum se va vedea i n exemplele care vor urma i cum, de altfel, era previzibil din signatura metodelor pe care le-am anun at ca fcnd parte din structura clasei InputStream, tratarea excep iilor n cazul opera iilor I/O este imperativ. n Exemplul 8.2 se arat cadrul Java pentru redirectarea stream-urilor standard. Exemplul 8.1 //Utilizare stream-uri standard //Acestea sunt asociate implicit cu echpamentele periferice

//Tastatura Sistem.in //Ecranul monitorului Sistem.out / Sistem.err import java.io.*; import java.util.*; public class IO1 { public static void main(String[] s) { boolean exit=false; System.out.println("Incerc IO\n "+ " Informatii despre while(!exit) { System.out.println("Optiuni...."); System.out.println("\t (D) Data"); System.out.println("\t (P) Proprieteti sistem"); System.out.println("\t (T) Terminare"); try { char readChar=(char)System.in.read(); int avlb=System.in.available(); System.in.skip(avlb); switch(readChar) { case 'D': case 'd': System.out.println("Data:"+ new Date().toString()); break; case 'P': case 'p': Properties prop=System.getProperties(); prop.list(System.out); break; case 'T': case 't': System.out.println("La revedere..."); exit=true; break; } } catch(IOException e) { System.err.println(e.getMessage()); } } } } Exemplul 8.2 // Exemplific redirectarea stream-urilor standard import java.io.*; public class Redirect { // Arunca exceptii IOException la consola public static void main(String[] args) throws IOException { //Flux de intrare cu buffer asociat cu fisierul //text care contine programul BufferedInputStream in = new BufferedInputStream( new FileInputStream("Redirect.java"));

sistem");

//Filtru asociat cu fluxul definit mai sus PrintStream out =new PrintStream( new BufferedOutputStream( new FileOutputStream("test.out"))); //Redirectare fluxuri standard System.setIn(in); System.setOut(out); System.setErr(out); //Filtrarea stream-ului standard cu ajutorul clasei //BufferedReadre pentru a permite utilizarea metodei readLine() //versiune ne-deprecated. //Deschidere flux BufferedReader br = new BufferedReader(new InputStreamReader String s; //Citire flux pana la terminare while((s = br.readLine()) != null) System.out.println(s); //Inchidere flux out.close(); } }

(System.in));

8.3 Clasa File n lucrul cu stream-uri


Clasa File, din biblioteca I/O Java, furnizeaz o abstractizare independent de platform pentru ob inerea informa iilor despre fiiere, ca de exemplu: numele de cale, dimensiunea fiierului, data modificrii, etc. Pentru a ob ine astfel de informa ii despre fiier trebuie ca, mai nti, s crea i un obiect File utiliznd unul din constructorii de mai jos: File (String cale); File (String cale, String nume); File (File dir, String nume); Parametrul cale din prima versiune de constructor con ine calea ctre fiier, n timp ce acelai parametru, din cea de-a doua versiune, con ine calea directorului. Paramerul nume specific numele fiierului. Parametrul dir, din ce-a de-a treia versiune permite utilizarea unui alt obiect File, ca director. Utilitatea clasei File poate fi desprins, ca un nceput, i din Exemplul 8.3 i Exemplul 8.4. Exemplul8.3 //Listarea tuturor fiierelor din directorul curent import java.io.*; public class TestFile { public static void main(String[] sir) { File dc=new File("."); String listaf[]=dc.list(); for(int i=0;i<listaf.length;i++) { if(i % 23==0) { try { System.in.read();

System.in.read(); } catch(IOException e) {} }; System.out.println(listaf[i]); } } } Exemplul 8.4 //Listarea tuturor fisierelor din directorul curent //avand o extensie data import java.io.*; class JavaFileFilter implements FilenameFilter { public boolean accept(File dir, String nume) { return nume.endsWith(".java"); } } public class FiltruF { public static void main(String[] sir) { File dc=new File("."); String listaf[]=dc.list(new JavaFileFilter()); for(int i=0;i<listaf.length;i++) { if(i % 23==0) { try { System.in.read(); System.in.read(); } catch(IOException e) {} }; System.out.println(listaf[i]); } } }

8.4 Citirea datelor dintr-un stream


Aa cum, probabil c s-a n eles, exist dou grupuri mari de stream-uri, n func ie de obiectivul lor: scrierea sau citirea datelor. Pentru citirea datelor dintr-un flux avem clasele derivate din clasele abstracte InputStream sau Reader. Amndou aceste clase sunt clase abstracte care furnizeaz metode care permit opera ii asemntoare celor pe care le-am prezentat deja n discu ia referitoare la stream-urile standard. Referindu-ne la InputStream, fiind o clas abstract nu poate fi utilizat n instan ierea unui obiect stream. Pentru crearea obiectelor de tip stream, pornind de la clasa InputStream, s-au derivat mai multe clase. Aceste clase le-am putea mpr i, la rndul lor, n dou grupuri importante: clase stream conectate la diferite tipuri de surse;

clase stream care se conecteaz la cele de mai sus, adugnd noi opera ii i func ionnd ca filtre aplicate opera iilor de citire.

Clasele din prima categorie sunt derivate direct din clasa InputStream. Pentru a putea utiliza efectiv interfa a anun at de clasa InputStream a fost nevoie de construirea unor clase derivate din aceasta, clase care s poat fi conectate la diferite tipuri de surse reale. Dintre aceste clase remarcm ca fiind cel mai mult folosite: ByteArrayInputStream Este o clas care permite conectarea unui stream la un tablou de octe i. Opera iile de citire din stream vor permite citirea datelor din tabloul de octe i, gestiunea opera iilor fiind asumat de ctre instan a stream. StringBufferInputStream Permite conectarea unui stream la un ir de caractere. Aceast clas este considerat deprecated, recomandndu-se utilizarea clasei StringReader. FileInputStream Este una dintre cele mai utilizate clase de tip stream i ne ofer posibilitatea conectrii cu un fiier pentru a citi datele nregistrate n acesta. Dup cum se poate vedea, la analiza atent a defini iei clasei FileInputStream, aceasta con ine mai multe versiuni de constructori, care permit asocierea stream-ului cu un fiier n diferite moduri: numele specificat ca o variabil sau constant String, numele specificat ca o variabil File, numele specificat ca o variabil FileDescriptor. O categorie important de clase derivate din InputStream o formeaz clasele de tip filtru, derivate din clasa FilterInputStream, la rndul ei, derivat din clasa InputStream. Dintre clasele din aceast categorie se cuvine s remarcm cteva utilizate intens: DataInputStream Este una dintre cele mai utilizate clase dintre cele de tip filtru. Aceast clas con ine mai multe func ii, care permit citirea unor tipuri fundamentale de date (int, float, double, char, etc) ntr-un mod independent de main. De regul, aceast clas este utilizat mpreun cu clasa DataOutputStream, clas care are opera ii de scriere n stream, orientate pe tipurile fundamentale. mpreun, aceste dou clase, ofer o solu ie elegant la problema gestiunii fiierelor a cror nregistrare are structura definit de utilizator. Este momentul s remarcm c, n principiu, n Java, la fel ca n C++, putem avea fluxuri de octe i, fluxuri de caractere i fluxuri de date cu structur cunoscut. Revenind la clasa DataInputStream, prezentm, n continuare, cteva dintre metodele mai mult folosite. Metoda boolean readBoolean() byte readByte Int readUnsignedByte() short readShort() char readChar() int readInt() long readLong() float readFloat() double readDouble() String readLine() String readUTF() Rolul Citete o dat boolean Citete un octet Citete un octet unsigned Citete un short (16 bi i) Citete un caracter Unicode Citete un ntreg pe 32 bi i Citete un long pe 64 bi i Citete un numr real n virgul mobil simpl precizie Citete un numr real n virgul mobil dubl precizie Citete o linie Citete un ir de caractere n format UTF (Unicode Text Format)

Tabel 1. Metode ale clasei DataInputStream

Dintre clasele de tip filtru merit s mai remarcm i clase precum: BufferedInputStream, LineNumberInputStream, ZipInputStream, etc.

8.5 Scrierea datelor ntr-un stream


Pentru scrierea datelor ntr-un stream avem clasele derivate din clasele abstracte OutputStream sau Writer. Amndou aceste clase sunt clase abstracte, care furnizeaz metode care permit opera ii de scriere a datelor n stream-uri, complementare celor de citire, ca func ionalitate. Referindu-ne la clasa OutputStream, fiind o clas abstract nu poate fi utilizat n instan ierea unui obiect stream. Totui, ea este o ocazie de a specifica o interfa general valabil n opera iile de scriere n fluxuri, avnd urmtoarea defini ie: public abstract class OutputStream { public abstract void write(int b) throws IOException public void write(byte b[] ) throws IOException public void write(byre[], int off, int len) throws IOException public void flush()throws IOException public void close()throws IOException } Pentru crearea obiectelor de tip stream, pornind de la clasa OutputStream, s-au derivat mai multe clase. Aceste clase le-am putea mpr i, la rndul lor, n dou grupuri importante: clase stream conectate la o destina ie; clase stream care se conecteaz la cele de mai sus, adugnd noi opera ii i func ionnd ca filtre aplicate opera iilor de scriere.

Clasele din prima categorie sunt derivate direct din clasa OutputStream. Pentru a putea utiliza efectiv interfa a anun at de clasa OutputStream a fost nevoie de construirea unor clase derivate din aceasta, clase care s poat fi conectate la diferite tipuri de destina ii reale. Dintre aceste clase remarcm ca fiind cel mai mult folosite: ByteArrayOutputStream Este o clas care permite conectarea unui stream la un tablou de octe i. Opera iile de scriere n stream vor permite adugare de date n tabloul de octe i, gestiunea opera iilor fiind asumat de ctre instan a stream. FileOutputStream Este clasa pereche a clasei FileInputStream, dintre cele mai utilizate clase de tip stream i ne ofer posibilitatea conectrii cu un fiier pentru a scrie date n acesta. Dup cum se poate vedea, la analiza atent a defini iei clasei FileOutputStream, aceasta con ine mai multe versiuni de constructori, care permit asocierea stream-ului cu un fiier n diferite moduri: numele specificat ca o variabil sau constant String, numele specificat ca o variabil File, numele specificat ca o variabil FileDescriptor. O categorie important de clase derivate din OutputStream o formeaz clasele de tip filtru, derivate din clasa FilterOutputStream, la rndul ei, derivat din clasa OutputStream. Dintre clasele din aceast categorie se cuvine s remarcm cteva utilizate intens: DataOutputStream Este una dintre cele mai utilizate clase dintre cele de tip filtru. Aceast clas con ine mai multe func ii care permit citirea unor tipuri fundamentale de date (int, float, double, char, etc) ntr-un mod independent de main. De regul, aceast clas este utilizat mpreun cu clasa DataInputStream, clas care are opera ii de citire n stream, orientate pe tipurile fundamentale. mpreun, aceste dou clase ofer o solu ie elegant la problema gestiunii fiierelor, a cror nregistrare are structura definit de utilizator.

Clasa DataOutputStream, are o serie de metode folosite, dup caz, la realizarea opera iilor de scriere n stream-uri. Metoda void writeBoolean(boolean v) void writeByte(int v) void writeBytes(String s) void writeShort(int v) void writeChar(int v) void writeInt(int v) void writeLong(long v) void writeFloat(float v) void writeDouble(double v) void writeChars(String s) void writeUTF(String S) Rolul Scrie o dat boolean Scrie un octet Scrie un ir de caractere ca o secven de octe i Scrie un short (16 bi i) Scrie un caracter Unicode Scrie un ntreg pe 32 bi i Scrie un long pe 64 bi i Scrie un numr real n virgul mobil simpl precizie Scrie un numr real n virgul mobil dubl precizie Scrie un ir de caractere ca o secven de 16 bi i Scrie un ir de caractere n format UTF (Unicode Text Format)

Tabel 2. Metode ale clasei DataOutputStream


Dintre clasele de tip filtru merit s mai remarcm i clase precum: BufferedOutputStream, PrintStream, ZipOutputStream, etc. Relativ la lucrul cu fiiere, un rol important l joac clasa RandomAccessFile, care nu este subclas nici a clasei InputStream, nici a clasei OutputStream. ns, cu ajutorul instan elor ei, pute i efectua n acelai timp att opera ii de scriere ct i de citire. n plus, dup cum arat i numele, un obiect RandomAccessFile furnizeaz acces aleator la datele dintr-un fiier, ceea ce instan ele descenden ilor claselor InputStream sau OutputStream nu pot. Pentru compatibilitate, la utilizare, cu clasele DataInputStream i DataOutputStream, clasa RandomAccessFile implementeaz interfe ele DataOutput i DataInput, interfe e pe care le implementeaz i clasele DataInputStream i DataOutputStream. O discu ie asemntoare se poate purta relativ la ierarhiile de clase ale cror rdcini sunt clasele Reader i Writer, iearhii care implementeaz alternativa I/O Java pe 16 bi i. Func ionalitatea lor, ns, nu elimin cu totul utilitatea ierarhiilor pe care le-am prezentat mai sus, pe scurt. n elegerea exact a modului de lucru cu oricare dintre ierarhiile men ionate mai sus poate fi realizat consultnd documenta ia aferent kit-urilor jdk1.o sau jdk1.1. Jungla protocoalelor de lucru cu stream-uri n Java este, dup cum se vede, mult mai diversificat dect oferta C++. Programatorul din lumea real trebuie s se acomodeze cu elementele fundamentale relativ la stream-urile Java, rmnnd ca n situa ii excep ionale s nve e utilizarea unor procedee excep ionale de manevrare a stream-urilor. Exemplele care urmeaz ncearc s eviden ieze elemente de protocol socotite uzuale n lucrul cu stream-uri n Java. Exemplul 8.5 //Situatii tipice de utilizarea fluxurilor in Java import java.io.*; public class IOStreamDemo { // Metoda ridica exceptii la consola public static void main(String[] args) throws IOException { //1a. Citirea orientata pe linii intr-un fisier text BufferedReader in = new BufferedReader(

new FileReader("IOStreamDemo.java")); String scit; String sImRAM = new String(); //s2 pastreaza continutul fisierului IOStreamDemo.java //ca imagine RAM while((scit = in.readLine())!= null) sImRAM += scit + "\n"; in.close(); // 1b. Citire de la tastatura: BufferedReader stdin =new BufferedReader( new InputStreamReader(System.in)); System.out.print("Enter a line:"); System.out.println(stdin.readLine()); System.in.read(); // 2. Citire din memorie //Se va folosi sImRAM, creat la 1a StringReader in2 = new StringReader(sImRAM); int c; //Afisare imagine memorie a continutului //fisierului IOStreamDemo.java while((c = in2.read()) != -1) System.out.print((char)c); System.in.read(); System.in.read(); // 3. Preluare date formatate in memorie //Din nou se apeleaza la imaginea memorie a //fisierului IOStreamDemo.java try { DataInputStream in3 =new DataInputStream( ByteArrayInputStream(sImRAM.getBytes())); while(true) System.out.print((char)in3.readByte()); } catch(EOFException e) { System.err.println("End of stream"); } System.in.read(); System.in.read(); // 4. Creare fisier format output try { BufferedReader in4 =new BufferedReader( new StringReader(sImRAM)); PrintWriter out1 =new PrintWriter( new BufferedWriter( new FileWriter("IODemo.out"))); int lineCount = 1; while((scit = in4.readLine()) != null ) out1.println(lineCount++ + ":" + scit); out1.close(); new

} catch(EOFException e) { System.err.println("End of stream"); } String sir; BufferedReader inper = new BufferedReader( new FileReader("IODemo.out")); System.out.println("################################"); while((sir = inper.readLine())!= null) System.out.println(sir); inper.close(); System.in.read(); System.in.read(); // 5. Salvare si consultare date cu tip try { DataOutputStream out2 =new DataOutputStream( new BufferedOutputStream( new FileOutputStream("Data.txt"))); out2.writeDouble(3.14159); out2.writeBytes("Acesta este numarul PI\n"); out2.writeDouble(1.41413); out2.writeUTF("Radacina patrata a lui 2"); out2.close(); DataInputStream in5 =new DataInputStream( new BufferedInputStream( new FileInputStream("Data.txt"))); BufferedReader in5br =new BufferedReader( new InputStreamReader(in5)); // Trebuie sa folositi DataInputStream pentru date: System.out.println(in5.readDouble()); // Numai metoda readUTF() va recupera // sirul Java-UTF corect: // Cu readLine() se citesc corect date // scrise cu writeBytes. System.out.println(in5br.readLine()); System.out.println(in5.readDouble()); System.out.println(in5.readUTF()); } catch(EOFException e) { System.err.println("End of stream"); } // 6.Citire/scriere fisier in acces aleator RandomAccessFile rf =new RandomAccessFile("rtest.dat", "rw"); for(int i = 0; i < 10; i++) rf.writeDouble(i*1.414); rf.close(); rf =new RandomAccessFile("rtest.dat", "rw"); rf.seek(5*8); rf.writeDouble(47.0001); rf.close();

rf =new RandomAccessFile("rtest.dat", "r"); for(int i = 0; i < 10; i++) System.out.println("Value " +i+":"+rf.readDouble()); rf.close(); } } Ca observa ii finale la cele discutate pn acum relativ la problema persisten ei datelor n Java, a men iona: Puternica orientare pe obiecte a solu iilor Java la problema persisten ei. Flexibilitatea cu care putem utiliza diferitele variet i de fluxuri (filtrare, redirectare) Obligativitatea tratrii excep iilor I/O n codul Java, ceea ce sporete coeficientul de robuste e al codului Java afectat opera iilor I/O.

8.6 Serializarea obiectelor


Java 1.1 introduce un nou concept, numit serializarea obiectelor. Utiliznd serializarea, un obiect poate fi transformat ntr-o secven de octe i ( care poate fi transmis n re ea sau care poate fi stocat ntr-o specie de memorie), secven care poate fi folosit pentru refacerea complet a obiectului ini ial. Avantajul serializrii este evident, prin faptul c simplific procedura de transmitere a unui obiect ntre dou entit i ale unui sistem informa ional automatizat. Prin utilizarea serializrii, programatorul a scpat de grija transformrii obiectului ntr-o succesiune de octe i, de ordonarea lor, de problema diferen ei de reprezentare pe diferite platforme, toate acestea fcndu-se automat. n rezumat, putem spune c serializarea introduce un alt nivel de transmitere a datelor, la care unitatea fundamental de transfer este obiectul. n practic, serializarea obiectelor este impus de situa ii precum: RMI (Remote Method Invocation) Comunicare de obiecte ntre aplica ii aflate pe calculatoare diferite, ntr-o re ea. Lightweight persistence Posibilitatea stocrii unui obiect, ca ansamblu unitar, n vederea utilizrii lui n cadrul ulterioare. Tehnologia Java Beans Tehnologie Java de lucru cu componente. condi ii. Procedeul Java de serializare/deserializare Serializarea unui obiect este o operea ie relativ simpl, care implic lucrul cu clasa ObjectOutputStream, care nfoar un stream de tipul OutputStream, conectat la o destina ie. Conexiunea cu un stream OutputStream se face prin intermediul constructorului: public ObjectOutputStream(OutputStream out) throws IOException; Clasa ObjectOutputStream este derivat din clasa OutputStream i implementeaz interfa a ObjectOutput (derivat din DataOutput). Interfa a ObjectOutput declar metodele specifice serializrii unui obiect. Dintre aceste metode, cea mai important este metoda writeObject avnd signatura: public final void writeObject(Object obj) throws IOException Prin intermediul clasei ObjectOutputStream, se pot scrie att date primitive (cu ajutorul metodelor declarate de interfa a DataOutput) ct i obiecte, folosind metoda writeObject.

unei

execu ii

Procedeul Java de serializare/deserializare a unui obiect este simplu, dac acesta ndeplinete anumite

Deserializarea (adic reconstituirea unui obiect dintr-un stream) este, de asemenea, simpl i implic utilizarea clasei ObjectInputStream. Aceast clas nfoar un stream de tipul InputStream, stream transmis ca i parametru n constructorul clasei: public ObjectInputStream(InputStream in) throws IOException, StreamCorruptedException Clasa ObjectInputStream este derivat din clasa InputStream i implementeaz interfa a ObjectInput (derivat din interfa a DataInput). Interfa a ObjectInput declar metodele specifice deserializrii unui obiect. Dintre acestea, cea mai important este metoda readObject, definit astfel: public final Object readObject() throws OptionalDataException, ClassNotFoundException, IOException Excep ia OptionalDataException apare n cazul n care, n locul unui obiect n stream se afl un tip de dat primitiv. Excep ia ClassNotFoundException apare atunci cnd clasa obiectului din stream nu poate fi gsit, n contextul actual de execu ie. O n elegere mai bun a serializrii / deserializrii poate fi ob inut urmrind exemplele de mai jos. Exemplul 8.6 // Exemplificarea serializrii obiectelor import java.io.*; class Data implements Serializable { private int i; Data(int x) { i = x; } public String toString() { return Integer.toString(i); } } public class Serializ implements Serializable { //Generare numar aleator intreg private static int r() { return (int)(Math.random() * 10); } private Data[] d = {new Data(r()), new Data(r()), new Data(r())}; private Serializ next; private char c; // Valoarea lui i indic numarul de elemente din lista Serializ(int i, char x) { System.out.println(" Serializ constructor: " + i); c = x; if(--i > 0) next = new Serializ(i, (char)(x + 1)); } Serializ()

{ System.out.println("Constructor implicit"); } public String toString() { String s = ":" + c + "("; for(int i = 0; i < d.length; i++) s += d[i].toString(); s += ")"; if(next != null) s += next.toString(); return s; } // Ridica exceptii la consola public static void main(String[] args) throws ClassNotFoundException, IOException { Serializ w = new Serializ(6, 'a'); System.out.println("w = " + w); ObjectOutputStream out =new ObjectOutputStream( new FileOutputStream("serializ.out")); out.writeObject("Serialize storage"); out.writeObject(w); out.close(); // Also flushes output ObjectInputStream in =new ObjectInputStream( new FileInputStream("serializ.out")); String s = (String)in.readObject(); Serializ w2 = (Serializ)in.readObject(); System.out.println(s + ", w2 = " + w2); ByteArrayOutputStream bout =new ByteArrayOutputStream(); ObjectOutputStream out2 =new ObjectOutputStream(bout); out2.writeObject("Serializ storage"); out2.writeObject(w); out2.flush(); ObjectInputStream in2 =new ObjectInputStream( new ByteArrayInputStream(bout.toByteArray())); s = (String)in2.readObject(); Serializ w3 = (Serializ)in2.readObject(); System.out.println(s + ", w3 = " + w3); } } Exemplul 8.7 import java.io.*; import java.util.*; class House implements Serializable {} class Animal implements Serializable { String name; House preferredHouse; Animal(String nm, House h) { name = nm; preferredHouse = h;

} public String toString() { return name + "[" + super.toString() + "], " + preferredHouse + "\n"; } } public class Serializ1 { public static void main(String[] args) throws IOException, ClassNotFoundException { House house = new House(); ArrayList animale = new ArrayList(); animale.add(new Animal("Grivei -cainele", house)); animale.add(new Animal("Coco papagalul", house)); animale.add(new Animal("Vasile -motanul", house)); System.out.println("animale: " + animale); ByteArrayOutputStream buf1 =new ByteArrayOutputStream(); ObjectOutputStream o1 =new ObjectOutputStream(buf1); o1.writeObject(animale); //Inca odata o1.writeObject(animale); // Scriem si intr-un stream diferit ByteArrayOutputStream buf2 =new ByteArrayOutputStream(); ObjectOutputStream o2 =new ObjectOutputStream(buf2); o2.writeObject(animale); // Acum le citim ObjectInputStream in1 =new ObjectInputStream( new ByteArrayInputStream(buf1.toByteArray())); ObjectInputStream in2 =new ObjectInputStream( new ByteArrayInputStream(buf2.toByteArray())); ArrayList animale1 =(ArrayList)in1.readObject(); ArrayList animale2 =(ArrayList)in1.readObject(); ArrayList animale3 =(ArrayList)in2.readObject(); System.out.println("animale1: " + animale1); System.out.println("animale2: " + animale2); System.out.println("animale3: " + animale3); } } Exemplele 8.6 i 8.7 arat, printre altele, i condi iile pe care trebuie s le ndeplineasc un obiect pe care vrem s l serializm. n spe , este vorba de faptul c pentru ca obiectul s poat fi serializat, clasa lui definitoare trebuie s respecte una din urmtoarele condi ii: S implementeze interfa a Serializable n aceast situa ie transformrile obiect-stream i stream-obiect se pot face automat. S implementeze interfa a Externalizable i s suprascrie metodele acesteia writeExternal i readExternal. n acest caz, programatorul este responsabil (prin specificarea metodelor anterior amintite) de transformarea obiectului n secven e de octe i i invers.

Evident, spa iul de care dispunem nu ne permite s tratm exhaustiv problemele pe care le pune asigurarea persisten ei datelor n aplica iile Java. Scopul acestei cr i a fost de a realiza o deschidere asupra universului problematic dezvoltat n Java n jurul ideii de persisten . St n puterea fiecrui cititor n parte s se aplece cu temeinicie asupra aspectelor de detaliu sau , de ce nu, filosofice, neelucidate nc.

9 Programare concurent cu suport obiect orientat. Perspectiva Java


9.1 No iuni de programare concurent
Programarea concurent presupune existen a mai multor sarcini care trebuie s fie executate n paralel, ceea ce implic, ntr-o form sau alta, partajarea resurselor comune ale sistemului pe care se deruleaz execu ia sarcinilor n cauz. Fcnd abstrac ie de arhitectura hard15 care o sus ine, programarea concurent poate fi materializat prin suport pentru programarea multitasking, suport pentru programarea multifir i suport pentru programarea distribuit. Atunci cnd o aplica ie este implementat pe un sistem multiprocesor, se spune c avem o aplica ie multiprocesat. n situa ia n care aplica ia este implementat pe o re ea de calculatoare, spunem c aplica ia este distribuit. n acest curs nu ne vom interesa de programarea distribuit i nici de programarea multitasking. Pentru a fi posibil programarea multitasking, mai nti sistemul de operare trebuie s fie capabil de execu ia simultan a mai multor programe. n cazul n care aa ceva este posibil, la nivelul limbajelor de programare ne putem pune probleme de sincronizare a accesului la resursele comune. Din acest punct de vedere, rela ia dintre Java i Windows, ca sistem de operare, nu este extrem de cordial. n schimb Object Pascal, limbajul de programare n mediul de programare vizual Delphi, gndit pentru a realiza aplica ii avnd ca int platforma Windows, posed nveli sintactic specific pentru rezolvarea problemelor de partajare a resurselor critice, n spiritul WIN32API16. Pe de alt parte, programarea distribuit este posibil, n Java, apelnd la tehnologii care sus in corespunztor acest stil de programare(RMI, CORBA, etc.). n aceast carte ne vom interesa de posibilit ile pe care le ofer Java pentru a face programare multifir. Majoritatea compilatoarelor de C++, legate de maina MSDOS, nu ofer suport nativ sau nveli sintactic corespunztor pentru programarea multifir, ci doar rudimente sintactice pentru simularea greoaie a multitaskingului. n sfrit, s mai observm c programarea multifir la care ne referim va fi asociat cu posibilit ile unei maini monoprocesor, ceea ce nseamn, iari, c sistemul de operare este arbitrul care stabilete regulile de baz care trebuie urmate pentru ca un fir de execu ie s poat accesa reursele partajabile ale mainii (ndeosebi timpul UC). Arbitrajul exercitat de sistemul de operare (n rela ia cu timpul UC) se reduce, practic, la acordarea unor cuante de timp UC tuturor firelor de execu ie active la un moment dat, eventual, n func ie de priorit ile asociate acestora la creare. Toate celelalte probleme care decurg din execu ia simultan a mai multor fire de execu ie sunt de competen a programatorilor. Aceste probleme se regsesc, generic, n sintagma comunicare i sincronizare, pentru care limbajele ofer mijloace a cror ntrebuin are este la latitudinea programatorilor. S mai observm c no iunea de fir de execu ie se refer la o unitate de prelucrare, asociat cu no iunea de proces, n sensul c fiecare fir de execu ie este gzduit n spa iul de adrese al unui proces. n fine, trebuie spus c, referindu-ne la Java, func ia main() a unui program Java este, ea nsi, un fir de execu ie, care se numete firul principal de execu ie. O situa ie asemntoare apare i n Object Pascal unde programul principal, aflat n fiierul cu extensia .dpr este asimilat cu no iune de fir de execu ie principal. Lucrul cu fire de execu ie este o necesitate, n foarte multe situa ii de programare. Dac, de exemplu, ne gndim la o aplica ie care simuleaz calculul tabelar, nu este greu de priceput necesitatea mai multor fire de execu ie: unul care se ocup de interactivitatea cu utilizatorul, unul care gestioneaz implica iile modificrii con inutului celulei curente asupra con inutului altor celule, etc. Vom ncerca, n continuare, s fixm, ct mai clar posibil, bazele utilizrii firelor de execu ie n Java.

9.2 Fire de execu ie (thread-uri) n Java


Pentru ca programatorul Java s poat realiza aplica ii multifir, Java ofer n pachetul java.lang, deci chiar n java core, dou clase i o interfa : clasa Thread clasa ThreadGroup interfa a Runnable
15

16

Sistem monoprocesor, sistem multiprocesor, sistem vectorial, sistem distribuit, etc. Interfa a de Programare a Aplica iilor sub sistemul de operare Windows.

Clasa Thread i interfa a Runnable ofer suport pentru lucrul cu fire de execu ie, ca entit i separate ale aplica iei iar clasa ThreadGroup permite crearea unor grupuri de fire de execu ie, n vederea tratrii acestora ntr-un mod unitar.
Nota ie UML pentru

interfa
Runnable Rela ie de realizare

Thread ThreadGroup

Figura 19. Resurse Java predefinite pentru programarea multifir. Rela iile dintre ele.
Dup cum se poate observa, n Figura 23, clasa Thread implementeaz interfa a Runnable iar clasa ThreadGroup se compune din mai multe obiecte Thread. Simbolurile folosite pentru a indica rela ia de compunere dintre ThreadGroup i Thread, precum i rela ia de realizare dintre Thread i Runnable sunt de provenien UML. Deoarece discu ia referitoare la grupuri de fire se bazeaz pe n elegerea lucrului cu fire independente, n continuare ne vom ocupa de problema utilizrii firelor de execu ie independente. Pentru a crea un fir de execu ie n Java avem dou posibilit i: Definirea unei clase derivate din clasa Thread Definirea unei clase care implementeaz interfa a Runnable

Crearea unui fir de execu ie derivnd clasa Thread


Alegnd aceast variant, avem de efectuat un numr redus de opera ii: Definirea unei clase derivate din clasa Thread. Derivarea se face, dup cum se tie, cu o sintax de tipul:

class FirulMeu extends Thread { //Date membre //Func ii membre } Suprascrierea func iei public void run(), motenit de la clasa Thread, n clasa derivat. Aceast metod trebuie s implementeze comportamentul firului de execu ie. Aa cum metoda main() este metoda apelat de Java Runtime System n momentul n care se execut o aplica ie Java, metoda run() este metoda apelat cnd se execut un fir. De fapt, trebuie s subliniem c atunci cnd se pornete maina virtual Java (JVM), odat cu ea se pornete un fir de execu ie care apeleaz metoda main(). JVM i va nceta execu ia n momentul in care nu mai exist fire n execu ie sau a fost apelat metoda exit() a clasei System. Instan ierea unui obiect fir, folosind operatorul new:

FirulMeu firulmeu=new FirulMeu(); Pornirea firului instan iat, prin apelul metodei start(), motenit de la clasa Thread.

firulmeu.start();

Acestea sunt opera iile strict necesare pentru a ncepe lucrul cu fire de execu ie n Java, utiliznd clasa Thread. Exemplul 9.1 //Clasa care modeleaza firul class TFirPers extends Thread { static int id=0; int[] vect=new int[10]; //Constructorul clasei public TFirPers() { }; //Metoda run(), care modeleaza comportamentul firului public void run() { id++; System.out.println("Lucreaza TFirPers.... "+id); for(int j=0;j<10;j++) vect[j]=j; }; }; //Clasa care modeleaza aplicatia public class Fire { public static void main(String sir[]) { int[] vecmain=new int[20]; //Declarare referinte TFirPers fir1,fir2; //Alocare referinte-fir de executie fir1=new TFirPers(); fir2=new TFirPers(); //Lansarea in executie a firelor fir1.start(); fir2.start(); try { //Intarzierea firului principal pentru //a lasa ragaz firelor derivate din Thread //sa lucreze Thread.currentThread().sleep(2000); } catch(InterruptedException e) {} //Valorificarea rezultatelor furnizate de //cele doua fire de executie for(int k=0;k<10;k++)

vecmain[k]=fir1.vect[k]; for(int l=0;l<10;l++) vecmain[l+10]=fir2.vect[l]+10; for(int i=0;i<20;i++) System.out.println(vecmain[i]); }; }; Codul Java, prezentat n Exemplul 9.1, face apel la un mic subterfugiu (adormirea firului principal de executie timp de 2 secunde pentru a lsa timp firelor de executie, paralele firului principal, s-i ndeplineasc atribu iile. Dac nu se acord acest rgaz, se va observa c firul principal va accesa datele corespunztoare firelor secundare nainte ca acestea s fie conforme ateptrilor noastre. Altfel spus, paralelismul specific lucrului cu mai multe fire de execu ie este efectiv i, prin Exemplul 9.1, se atrage deja aten ia asupra modificrii atitudinii programatorului fa de problema organizrii structurilor de prelucrare.

Crearea unui fir de execu ie utiliznd interfa a Runnable


O alt modalitate de a crea fire de excu ie este utilizarea interfe ei Runnable. Aceast modalitate devine interesant n momentul n care se dorete ca o clas de tip Thread, pe care o implementm, s moteneasc capabilit i disponibile n alte clase. Opera iile specifice crerii unui fir de execu ie utiliznd interfa a Runnable sunt urmtoarele: Definirea unei clase care implementeaz interfa a Runnable. Aceasta se face utiliznd sintaxa adecvat i implementnd cel pu in metodele interfe ei Runnable (de fapt, doar metoda public void run()). class FirRunnable extends Exemplu implementes Runnable { //Definitie } Clasa care implementeaz interfa a Runnable trebuie s suprascrie func ia public void run().

public void run() { //Cod aferent } Se instan iaz un obiect al clasei de mai sus, cu o sintax de tipul:

FirRunnable obiectRunnable=new FirRunnable(); Se creaz un obiect de tip Thread, utiliznd un constructor care are ca i parametru un obiect de tip Runnable. n acest mod se asociaz un fir cu o metod run().

Thread firulMeu=new Thread(obiectRunnable); n sfrit, se pornete firul, la fel ca n metoda derivrii din Thread a firului.

Paii preciza i mai sus se pot vedea i n Exemplul 9.2 Exemplul 9.2 //Clasa care modeleaza aplicatia public class FirRunn { public static void main(String s[]) { System.out.println("Creare obiect Runnable...");

classRunnable obiectRunn=new classRunnable(); System.out.println("Creare fir..."); Thread fir=new Thread(obiectRunn); System.out.println("Start fir..."); fir.start(); System.out.println("Din nou in main()..."); } } //Clasa auxiliara class Display { public void display(String mesaj) { System.out.println(mesaj); } } //Clasa care implementeaza interfata Runnable si mosteneste //clasa Display class classRunnable extends Display implements Runnable { public void run() { int nrpasi=3; display("Run are "+nrpasi+" pasi de facut..."); for(int i=0;i<3;i++) display("Pasul: "+i); display("Run si-a terminat munca..."); } }

Controlul unui fir de execu ie


Problema controlului unui fir de execu ie este legat de cunoaterea strilor posibile ale firelor de execu ie. n spe , pe timpul execu iei unui program Java multifir, o instan Thread poate s se afle n una din urmtoarele patru stri: new, runnable, blocked i dead. Atunci cnd crem un fir de execu ie, acesta intr n starea new. n aceast stare firul de execu ie ateapt apelarea metodei start() a firului. Nici un cod nu ruleaz nc. n starea runnable un fir execut codul prezent n metoda sa run(). Pentru ca firul s treac din starea new n starea runnable trebuie executat metoda start(). Nu se recomand apelarea direct a metodei run() deoarece face acest lucru, n locul dumneavostr, metoda start(). Cnd un fir de execu ie este inactiv despre el se spune c este n starea blocked sau not runnable. Un fir poate deveni inactiv dac apelm metode precum sleep(), suspend() sau wait(), ori dac trebuie s atepte dup anumite metode I/O pn la finalizarea execu iei acestora. Dup cum se va vedea, fiecare dintre aceste metode are un mecanism propriu de refacere a strii runnable pentru fir. n Exemplul 11.1 am folosit deja metoda sleep() pentru a rezolva o problem banal de sincronizare ntre firul principal de execu ie i firele secundare.

Suspendarea i reluarea execu iei unui fir


Am vzut deja cum putem suspenda execu ia unui fir pentru o perioad de timp prin utilizarea metodei sleep() a clasei Thread. Putem suspenda execu ia unui fir i pn la apari ia unor condi ii obiective de reluare a execu iei. n acest sens putem utiliza metoda suspend() a clasei Thread, care pune un fir n starea not runnable, pn la apelarea metodei resume(). Ca un exemplu, utilizarea metodei suspend() permite oprirea unei secven e de anima ie la apsarea butonului mouse-ului i reluarea anima iei la ridicarea degetului de pe butonul mouseului. De asemenea, un gen special de suspendare/reluare a execu iei unui fir se realizeaz i cu ajutorul perechii de metode wait()/notify() asupra creia vom reveni mai jos.

9.3 Sincronizarea firelor


Dac sunt executate asincron, mai multe fire care partajeaz anumite date s-ar putea s fie obligate s-i sincronizeze activit ile pentru a ob ine rezultate corecte. Pe lng posibilit ile pe care le ofer metode precum sleep() sau suspend()/resume(), n Java a fost introdus modificatorul synchronized, tocmai pentru a introduce un cadru adecvat atomizrii activit ilor, n condi ii de concuren la resurse. Ideea de baz a modificatorului synchronized este ct se poate de simpl: primul fir care intr n posesia unui obiect marcat de modificatorul synchronized rmne proprietar al obiectului pn cnd i termin execu ia. n acest mod se creaz un cadru simplu pentru evitarea coliziunilor n timpul accesului concurent la resurse. Ceea ce este simplu nu este ntotdeauna i eficient. Uneori, pre ul sincronizrii s-ar putea s fie mai mare dect poate suporta clientul aplica iei (timpii de execu ie pot fi diminua i drastic).

Sincronizare bazat pe modificatorul synchronized


Modifcatorul synchronized poate fi utilizat pentru a realiza sincronizarea firelor. Orice fir are propia sa memorie de lucru, unde i ine copii proprii ale variabilelor pe care le utilizeaz. Cnd este executat un fir, acesta opereaz numai asupra acestor copii. Memoria principal (main memory), asociat firului principal, con ine copia master a fiecrei variabile. Exist reguli care condi ioneaz modul n care se poate efectua schimb de con inut ntre cele dou tipuri de copii ale variabilelor. Important pentru sincronizare este, ns, faptul c memoria main con ine i zvoare, care pot fi asociate obiectelor sau metodelor declarate synchronized. Firele pot intra n competi ie pentru achizi ionarea zvoarelor. Ac iunile de zvorre i dezvorre (dac un astfel de cuvnt exist!) sunt atomice, asemenea ac iunilor de citire sau scriere. Aceste zvoare pot fi utilizate pentru sinconizarea activit ilor unui program multifir. Declararea ca synchronized a unui obiect sau a unei metode determin asocierea acestora cu un zvor. Important este c un singur fir, la un moment dat, poate s nchid zvorul, altfel spus, un singur fir poate de ine obiectul asociat cu zvorul. Dac un fir vrea s acceseze un obiect sau o metod sincronizat, dar gsete zvorul nchis, el trebuie s atepte ntr-o coad, pn cnd zvorul va fi deschis de ctre proprietarul lui circumstan ial. Astfel c, n aplica iile Java multifir, putem ntlni: sincronizare cu metode, sinconizare pe blocuri, sincronizare cu obiecte, pentru a introduce la anumite nivele, disciplina de utilizare mutual exclusiv a acestor trei categorii de concepte. Elemente de sintax i aspecte referitoare la modul de utilizare a acestor tehnici se pot urmri n Exemplul 9.3, Exemplul 9.4 i Exemplul 9.5. Modelele teoretice utilizate n limbajele care ofer suport pentru programarea multifir ofer i alte solu ii la problema sincronizrii. Java ofer suport pentr majoritatea acestor modele, remarcndu-se, fa de alte limbaje, prin aducerea problemei concuren ei n interiorul limbajului, spre deosebire de alte solu ii, care se bazeaz pe enun uri sistem pentru implementarea prelucrrilor multifir. Pentru informarea cititorului, dou mari direc ii de rezolvare a problemelor de sincronizare sunt: monitoarele (introduse de C.A.R. Hoare) i semafoarele (introduse de Dijkstra). Fiecare dintre aceste solu ii pune n discu ie concepte precum sec iunea critic, prin care se n elege o por iune de cod la care accesul concurent trebuie monitorizat, pentru a evita disfunc iile n utilizarea anumitor resurse. Atrag aten ia cititorului i asupra ofertei limbajului Java n ceea ce privete posibilitatea de a defini grupuri de fire, a cror manevrare unitar poate constitui un avantaj, n anumite situa ii. De asemenea, n Java exist i posibilitatea de a defini nite fire speciale, numite daemon-i, fire a cror destina ie este asigurarea de servicii pentru celelalte fire de execu ie. Exemplul clasic de daemon, n Java este firul care asigur func ia de garbage collector. Exemplul 9.3 //Ilustreaza sincronizarea bazata pe obiecte //Obiectul monitor este balanta. class unFir extends Thread { //Obiectul monitor static Integer balanta = new Integer(1000); static int cheltuieli=0; public void run()

{ int vol; for(int i=0;i<10;i++) { try { sleep(100); } catch(InterruptedException e){} int bon=((int)(Math.random()*500)); //Accesul la blocul de cod de mai jos este //monitorizat cu ajutorul obiectului balanta synchronized(balanta) { if(bon<=balanta.intValue()) { System.out.println("Verif:"+bon); balanta=new Integer(balanta.intValue()-bon); cheltuieli+=bon; System.out.print("Balanta: "+balanta.intValue()); System.out.println("Cheltuieli: "+cheltuieli); } else { System.out.println("Respins: "+bon); } } } } } public class Lacat { public static void main(String s[]) { new unFir().start(); new unFir().start(); } } Exemplul 9.4 //Ilustreaza sincronizarea cu obiecte sinchronized //apeland la wait() si notify() class Fir1 extends Thread { Object ob; Fir1(Object obi) { ob=obi; } public void run() { while(true) { System.out.println("Firul "+getName()); try

{ synchronized(ob) { ob.wait(); } } catch(InterruptedException e) {} } } } class Fir2 extends Thread { Object ob; Object obman=new Object(); Fir2(Object obi) { ob=obi; } public void run() { while(true) { System.out.println("Firul "+getName()); try { synchronized(ob) { ob.notify(); } } catch(Exception e) { System.out.println("Exceptie: "+e); } try { synchronized(obman) { obman.wait(2000); } } catch(InterruptedException e) {} } } } public class WaitNoti { public static void main(String[]s) { //Obiect pretext pentru sincronizare Object obiect=new Object(); //Obiect pe care se face asteptarea

Object obman=new Object(); Fir1 fir1=new Fir1(obiect); Fir2 fir2=new Fir2(obiect); fir1.start(); fir2.start(); try { synchronized(obman) { obman.wait(35000); } } catch(InterruptedException e) {} } } Exemplul 9.5 //Ilustreaza sincronizarea cu metode synchronized class Distribuitor { int marfa=0; //Metoda atomizata cu ajutorul modificatorului //synchronized public synchronized int consuma() { int temp; while(marfa==0) { try { wait(); } catch(InterruptedException e) {} } temp=marfa; marfa=0; System.out.println("Consumat :"+temp); notify(); return temp; } //Metoda atomizata cu ajutorul modificatorului //synchronized public synchronized void produce(int vol) { while(marfa!=0) { try { wait(); } catch(InterruptedException e) {} } marfa=vol;

notify(); System.out.println("Produs } }

:"+marfa);

class unFir extends Thread { boolean producator=false; Distribuitor distr; public unFir(Distribuitor d,String t) { distr=d; if(t.equals("Producator")) producator=true; } public void run() { for(int i=0;i<20;i++) { try { sleep((int)(Math.random()*1000)); } catch(InterruptedException e) {} if(producator) distr.produce((int)(Math.random()*6)+1); else distr.consuma(); } } } public class ProdCons { public static void main(String s[]) { Distribuitor dis=new Distribuitor(); new unFir(dis,"Consumator").start(); new unFir(dis,"Producator").start(); } } Exemplul 9.5 ne arat cum putem combina sincronizarea bazat pe metode synchronized cu posibilit ile oferite de sincronizarea bazat pe ateptare. Esen iale, n sincronizarea bazat pe ateptare, sunt metodele wait() i notify().

Bibliografie esen ial


[1] [2] [3] [4] [5] [6] Eckel, B., Thinking in Java, 2nd edition, Revision 12, format electronic. Jamsa & Klander, C i C++ (Manualul fundamental de programare n C i C++), Editura Teora. Joshua Bloch, Java. Ghid practic pentru programatori avansa i, Editura Teora, 2002. Lemay, L., Cadenhead, R., Java 2 fr profesor n 21 de zile, Editura Teora, 2000. Mark C. Chan, .a., Java. 1001 secrete pentru programatori, Editura Teora Negrescu,L., Limbajele C i C++ pentru nceptori, Limbajul C++ (volumul II), Editura Albastr, Cluj-Napoca

CUPRINS

CUVNT NAINTE AL AUTORULUI.............................................................................................. 3

1 CUM SE EXPLIC PERMANENTA NEVOIE DE PARADIGME NOI N INGINERIA SOFTULUI. CE N ELEGEM PRIN ORIENTAREA PE OBIECTE ........................................... 6 1.1 1.2 2 2.1 2.2 3 3.1 3.2 CUM SE EXPLIC PERMANENTA NEVOIE DE PARADIGME NOI N INGINERIA SOFTULUI?........ 6 CE SE N ELEGE PRIN ORIENTAREA PE OBIECTE? ..................................................................... 8 CONCEPTE I PRINCIPII N PROGRAMAREA ORIENTAT PE OBIECTE................ 12 CONCEPTE N PROGRAMAREA ORIENTAT PE OBIECTE .......................................................... 12 PRINCIPII N PROGRAMAREA ORIENTAT PE OBIECTE ............................................................ 16 SPECIFICAREA I IMPLEMENTAREA UNEI CLASE DIN PERSPECTIV JAVA ...... 21 N LOC DE INTRODUCERE ........................................................................................................... 21 ATEN IE LA IMPORTAN A EFORTULUI DE ABSTRACTIZARE!.................................................. 21

4 MOTENIREA N PROGRAMAREA ORIENTAT PE OBIECTE DIN PERSPECTIV JAVA .................................................................................................................................................... 30 4.1 4.2 4.3 SCURT INTRODUCERE .............................................................................................................. 30 MOTENIREA N JAVA ............................................................................................................... 31 MOTENIREA MULTIPL N JAVA .............................................................................................. 35

5 POLIMORFISMUL N PROGRAMAREA ORIENTAT PE OBIECTE DIN PERSPECTIV JAVA ....................................................................................................................... 40 5.1 S REAMINTIM, PE SCURT, CE ESTE POLIMORFISMUL. ............................................................ 40 5.2 TIPURI DE POLIMORFISM LA NIVELUL LIMBAJELOR DE PROGRAMARE. EXEMPLIFICARE N C++. 40 5.3 POLIMORFISMUL N CONTEXT JAVA ......................................................................................... 50 6 TRATAREA STRUCTURAT A EXCEP IILOR N PROGRAMAREA ORIENTAT PE OBIECTE............................................................................................................................................. 52 6.1 6.2 7 7.1 O PROBLEM, N PLUS, N PROGRAMARE: TRATAREA EXCEP IILOR...................................... 52 MANIERA JAVA DE TRATARE A EXCEP IILOR .......................................................................... 52 PROGRAMARE GENERIC N C++ I JAVA ...................................................................... 58 CE ESTE PROGRAMAREA GENERIC.......................................................................................... 58

7.2 8 8.1 8.2 8.3 8.4 8.5 8.6

GENERICITATEA N JAVA ........................................................................................................... 58 FLUXURI OBIECT ORIENTATE I SERIALIZARE N JAVA........................................... 63 SCURT INTRODUCERE .............................................................................................................. 63 STREAM-URI STANDARD N JAVA............................................................................................... 63 CLASA FILE N LUCRUL CU STREAM-URI .................................................................................. 66 CITIREA DATELOR DINTR-UN STREAM ...................................................................................... 67 SCRIEREA DATELOR NTR-UN STREAM...................................................................................... 69 SERIALIZAREA OBIECTELOR ..................................................................................................... 73

9 PROGRAMARE CONCURENT CU SUPORT OBIECT ORIENTAT. PERSPECTIVA JAVA .................................................................................................................................................... 78 9.1 9.2 9.3 NO IUNI DE PROGRAMARE CONCURENT ................................................................................ 78 FIRE DE EXECU IE (THREAD-URI) N JAVA ............................................................................... 78 SINCRONIZAREA FIRELOR .......................................................................................................... 83

BIBLIOGRAFIE ESEN IAL ......................................................................................................... 88