Sunteți pe pagina 1din 94

Lucrarea 1

Concepte de baz ale programrii orientate pe obiecte

Cuprins
Concepte fundamentale n programarea orientat pe obiecte ................................................................................. 1 Primul program Java............................................................................................................................................... 2 De la surs la execuie ............................................................................................................................................ 3 Tem ....................................................................................................................................................................... 5

Un program dezvoltat cu ajutorul tehnologiei obiectuale are drept unitate de construcie nu subprogramul, ci obiectul. Un obiect nglobeaz date i operaii i reprezint o abstraciune a unei entiti din lumea real. Obiectele componente interacioneaz, determinnd transformarea datelor de intrare n date de ieire, adic rezolvarea problemei. ntr-un program dezvoltat n manier obiectual NU mai exist date globale (sau, n orice caz, foarte puine), datele fiind repartizate i nglobate n obiecte. Metoda obiectual de dezvoltare respect principii ale ingineriei software precum: localizarea i modularizarea: codul surs corespunztor unui obiect poate fi scris i actualizat independent de alte obiecte; ascunderea informaiei: un obiect are o interfa public pe care celelalte obiecte o pot utiliza pentru comunicare. Pe lng interfaa public, un obiect poate include date si operaii private, ascunse fa de alte obiecte, i care pot fi modificate oricnd, fr a afecta restul obiectelor; reutilizarea codului: clasele de obiecte pot fi definite pe baza claselor existente, prelund automat (prin motenire) coninutul acestora din urm.

Concepte fundamentale n programarea orientat pe obiecte


Principalele concepte care stau la baza programrii orientate pe obiecte sunt: 1) Abstractizarea este una dintre cile fundamentale prin care oamenii ajung s neleag i s cuprind complexitatea. Ea scoate n eviden toate detaliile semnificative pentru perspectiva din care este analizat un obiect, suprimnd sau estompnd toate celelalte caracteristici ale obiectului. n contextul programrii orientate pe obiecte, toate acestea se transpun astfel: obiectele i nu algoritmii sunt blocurile logice fundamentale; fiecare obiect este o instan a unei clase. Clasa este o descriere a unei mulimi de obiecte caracterizate prin structur i comportament similare; clasele sunt legate ntre ele prin relaii de motenire. 2) ncapsularea este conceptul complementar abstractizrii. ncapsularea este procesul de compartimentare a elementelor care formeaz structura i comportamentul unei
1

abstraciuni; ncapsularea servete la separarea interfeei contractuale de implementarea acesteia. Din definiia de mai sus rezult c un obiect este format din dou pri distincte: interfaa (protocolul); implementarea interfeei. Abstractizarea definete interfaa obiectului, iar ncapsularea definete reprezentarea (structura) obiectului mpreun cu implementarea interfeei. Separarea interfeei unui obiect de reprezentarea lui permite modificarea reprezentrii fr a afecta n vreun fel pe nici unul dintre clieni, deoarece acetia depind de interfa i nu de reprezentarea obiectului-server. 3) Modularizarea const n divizarea programului ntr-un numr de subuniti care pot fi compilate separat, dar care sunt cuplate (conectate) ntre ele. Gradul de cuplaj trebuie s fie mic, pentru ca modificrile aduse unui modul s afecteze ct mai puine module. Pe de alt parte, clasele care compun un modul trebuie s aib legturi strnse ntre ele pentru a justifica gruparea (modul coeziv). Limbajele care suport conceptul de modul fac distincia ntre: interfaa modulului, format din elementele (tipuri, variabile, funcii etc.) vizibile n celelalte module; implementarea sa, adic elemente vizibile doar n interiorul modulului respectiv. 4) Ierarhizarea reprezint o ordonare a abstraciunilor. Cele mai importante ierarhii n paradigma obiectual sunt: ierarhia de clase (relaie de tip is a); ierarhia de obiecte (relaie de tip part of). Motenirea definete o relaie ntre clase n care o clas folosete structuri i comportamente definite n una sau mai multe clase (dup caz vorbim de motenire simpl sau multipl). Semantic, motenirea indic o relaie de tip is a (este un/o). Ea implic o ierarhie de tip generalizare/specializare, n care clasa derivat specializeaz structura i comportamentul mai general al clasei din care a fost derivat. Agregarea este relaia ntre dou obiecte n care unul dintre obiecte aparine celuilalt obiect. Semantic, agregarea indic o relaie de tip part of (parte din).

Primul program Java


class PrimulProgram{ public static void main(String[ ] arg) { System.out.print(Hello world!); } } Referindu-ne strict la structura acestui program, trebuie spus c programele Java sunt constituite ca seturi de clase (n cazul nostru avem o singur clas). Pentru descrierea unei clase se folosete o construcie sintactic de forma: class nume_clasa { //continut clasa }

Aproape orice program, indiferent c este scris ntr-un limbaj procedural sau obiectual, are o rdcin sau un punct de plecare. Astfel, n programele Pascal avem ceea ce se numete program principal, iar n C avem funcia main(). n programele Java vom avea o clas rdcin care se caracterizeaz prin faptul c include o funcie al crei prototip este: public static void main(String[ ] arg) Elementele desemnate prin cuvintele public i static vor fi lmurite mai trziu. Numele clasei rdcin este un identificator dat de programator. Parametrul funciei main() este de tip tablou de elemente String (iruri de caractere). Prin intermediul acestui parametru putem referi i utiliza n program eventualele argumente specificate la momentul lansri n execuie a programului. Aciunile executate de programul de mai sus presupun dou operaii de afiare: print() i println(). Prefixul System.out care nsoete numele celor dou operaii reprezint numele unui obiect: este vorba despre un obiect predefinit care se utilizeaz atunci cnd destinaia unei operaii de scriere este ecranul monitorului. Deoarece metodele sunt incluse n clase i, majoritatea, i n instanele claselor, apelul presupune precizarea numelui clasei (daca e vorba de metode statice) sau al obiectului posesor. Cu alte cuvinte, n Java, apelul unei operaii se realizeaz folosind una din notaiile: nume_obiect.nume_metoda(parametri_actuali) sau: nume_clasa.nume_metoda(parametri_actuali) ntr-un program Java vom lucra cu 2 tipuri de clase: clase definite de programator; clase predefinite, furnizate mpreun cu mediul de dezvoltare Java, care formeaz aa numita API (Application Programming Interface), adic interfaa pentru programarea aplicaiilor. Revenind la primul nostru program Java, vom spune c metodele print()/println() pot primi ca parametru un ir de caractere dat, fie sub form de constante, aa ca n exemplul prezentat, fie sub form de expresii al cror rezultat este de tip String. Metodele print()/println() mai pot primi ca parametru i valori ale tipurilor primitive sau referine de clase, dar acestea sunt convertite tot la tipul String nainte de afiare. Println() poate fi apelat i fr parametri, caz n care execut doar un salt la linie nou: System.out.println( );

De la surs la execuie
Programul se editeaz cu un editor de text ASCII simplu (Notepad, editorul programului NortonCommander sau chiar editoarele unor medii integrate de dezvoltare cum sunt cele din gama Borland). Sursa se salveaz ntr-un fiier care va avea OBLIGATORIU extensia .java. De exemplu: lucrarea1.java. Dup editare, programul se compileaz cu comanda: javac nume_fisier_sursa.java de exemplu:

javac lucrarea1.java n urma compilrii vor rezulta un numr de fiiere egal cu numrul claselor coninute de surs. Fiecare dintre fiierele rezultate are extensia .class i numele identic cu numele clasei creia i corespunde. n cazul nostru, va rezulta un fiier cu numele PrimulProgram.class. Un fiier .class conine cod main virtual (Java Byte Code). Acesta este codul main al unui calculator imaginar. Pentru a putea fi executat pe o main real, este necesar un interpretor (sau executiv) care s execute fiecare instruciune a codului virtual n termenii operaiilor main ai calculatorului real. Dup compilare, programul n cod virtual obinut poate fi transportat pe orice main pentru care exist executivul corespunztor, iar scrierea unui executiv este mult mai uoar dect a unui compilator ntreg. Lansarea in execuie a interpretorului se face cu comanda: java nume_clasa_radacina Argumentul dat executivului Java la apel este numele unui fiier .class, acela corespunztor clasei rdcin (cea care conine metoda main). Pentru exemplul nostru, comanda de execuie va fi: java PrimulProgram Am afirmat n paragraful anterior c parametrul metodei main din clasa rdcin servete la accesarea eventualelor argumente date n linia de comand la lansarea n execuie a programului. De exemplu, se consider un program care face prezentarea unui student, mpreun cu numele i prenumele studentului, date ca argumente: class Prezentare{ public static void main(String[ ] arg) { System.out.println(Sunt studentul +arg[0]+ +arg[1] + +din anul V); } } Dup compilare, programul poate fi lansat n execuie cu comanda: java Prezentare Dan Popescu Programul a primit dou argumente (Dan i Popescu). Acestea sunt interpretate ca primele dou elemente ale tabloului arg ce figureaz ca parametru formal al metodei main() La fel ca i n C, n Java elementele unui tablou se indexeaz ncepnd cu 0. Dac programului nu i se furnizeaz cele 2 argumente, rezultatul este oprirea execuiei cu un mesaj prin care se reclam depirea indicelui de tablou (IndexOutOfBoundsException). Este vorba de tabloul arg care are dimensiunea 0 n cazul lipsei argumentelor. De aceea, e bine s prevedem secvene de verificare n program, prin care s testm dac utilizatorul a furnizat numrul necesar de parametri: class Prezentare{ public static void main(String[ ] arg) { if (arg.length < 2) System.out.println(Numr impropriu de parametri!!); else System.out.println(Sunt studentul + arg[0] + +arg[1] + +din anul V); }
4

} Expresia arg.length utilizat n secvena de mai sus determin numrul de elemente ale tabloului arg. O alt observaie este legat de parametrul funciei println(). De data acesta am folosit o expresie construit prin aplicarea operatorului + asupra unor operanzi de tip String, al crei rezultat este un ir de caractere obinut prin concatenarea operanzilor. Spre deosebire de funciile printf() din C, care acceptau un numr variabil de parametri, funciile print()/ println() din Java accept UN SINGUR parametru. Ca urmare, atunci cnd dorim s scriem printr-o singur instruciune mai multe valori, trebuie s le concatenm, spre a forma un singur ir.

Tem
Se cere s se scrie un program Java care s calculeze i s afieze perimetru i aria unui dreptunghi. Valorile pentru lungime i lime se trimit ca parametri la apel. Indicaie: Nu se pot transmite la apel dect parametri de tipul String. Ca urmare, pentru a putea efectua calcule, valorile transmise trebuie convertite de la String la Integer sau Double. Conversia se face astfel: Type x = Type.valueOf(arg[i])

Lucrarea 2

Componena unei clase. Crearea i iniializarea obiectelor

Cuprins
Componena unei clase........................................................................................................................................... 1 Crearea obiectelor................................................................................................................................................... 2 Modificatori de acces.............................................................................................................................................. 2 Iniializarea cmpurilor unui obiect. Constructori .................................................................................................. 3 Membri statici ai claselor........................................................................................................................................ 5 Tem ....................................................................................................................................................................... 6

Componena unei clase


n limbajele de programare orientate pe obiecte, clasele pot fi considerate ca fiind mecanisme prin care programatorul i construiete propriile sale tipuri de date, pe care, apoi, le va folosi aproape la fel cum folosete tipurile predefinite. Fiind un tip de date, unul dintre rolurile fundamentale ale clasei este acela de a servi la declararea variabilelor. Valorile unui tip clas se numesc obiecte sau instane ale clasei respective. O clas reprezint descrierea unei mulimi de obiecte care au aceeai structur i acelai comportament. Prin urmare, o clas va trebui s conin definiiile datelor i ale operaiilor ce caracterizeaz obiectele acelei clase. Datele definite ntr-o clas se mai numesc date-membru, variabile-membru, atribute sau cmpuri, iar operaiile se mai numesc metode sau funcii membru. Pentru a arta felul n care se definesc membrii unei clase, va fi utilizat urmtorul exemplu: class Punct{ private int private int public void x = xx; } x; y; init(int xx, int yy) { y = yy;

public void move(int dx, int dy) { x += dx; y += dy; } public int getX( ) { return x; } } public int getY( ) { return y; }

Clasa modeleaz un punct i are ca date-membru dou variabile de tip ntreg: x i y, reprezentnd coordonatele punctului, iar ca metode: funcia init(), iniializeaz coordonatele unui punct

funcia move(), deplaseaz un punct pe o anumit distan funciile getX() i getY(), returneaz coordonatele curente ale unui punct.

Crearea obiectelor
n continuare se va considera un mic program Java pentru a vedea cum se creeaz obiectele clasei Punct descris anterior: class Punct{ private int x; private int y; public void init(int xx, int yy) { x = xx; y = yy; } public void move(int dx, int dy) { x += dx; y += dy; } public int getX( ) { return x; } public int getY( ) { return y; } } class ClientPunct { public static void main(String[ ] arg) { Punct p1 = new Punct( ); //se creeaz o instan a clasei Punct Punct p2 = new Punct( ); // i nc una p1.init (10,20); p2.init (30,40); //se apeleaz metodele init ale instanelor p1.move(5,5); p2.move(6,-2); //se apeleaz metodele move System.out.println((x1,y1) = (+p1.getX()+,+ p1.getY( )+)); //se afieaz coordonatele curente ale primului punct System.out.println((x2,y2) = (+p2.getX()+,+ p2.getY( )+)); //se afieaz coordonatele curente ale celui de-al 2-lea punct } } n Java toate obiectele se creeaz n mod dinamic. Pn nu se iniializeaz variabila respectiv cu adresa unei zone de memorie alocat pentru valori ale tipului indicat de pointer, practic nu se poate folosi variabila n nici un fel. n programul de mai sus se observ aplicarea operatorului de alocare dinamic new.

Modificatori de acces
Se poate observa c definiiile datelor i ale funciilor din clasa Punct sunt prefixate de anumite cuvinte cheie (public, private) care se numesc modificatori de acces. Acetia

stabilesc drepturile de acces ale clienilor la membrii unei clase. Cnd se discut despre drepturile de acces la membrii unei clase trebuie s se abordeze acest subiect din dou perspective: Interiorul clasei sau, mai concret, metodele clasei. n cadrul metodelor unei clase exist acces nerestrictiv la toi membrii, date sau funcii. De exemplu, n metodele clasei Punct se face referire la cmpurile x i y. n interiorul clasei nu se folosete notaia cu punct pentru a referi membrii, acetia fiind pur si simplu accesai prin numele lor. Cnd o metod face referire la ali membri ai clasei, de fapt sunt accesai membrii corespunztori ai obiectului receptor, indiferent care ar fi el. De exemplu, cnd se apeleaz metoda init() a obiectului referit de p1, are loc iniializarea membrilor x i y ai acelui obiect. n legtur cu accesul din interiorul unei clase, trebuie spus c absena restriciilor se aplic i dac este vorba despre membrii altui obiect din aceeai clas, dar diferit de cel receptor. De exemplu, dac n clasa Punct am avea cte o metod de calcul a distanei pe vertical/orizontal dintre 2 puncte, unul fiind obiectul receptor, iar cellalt un obiect dat ca parametru, atunci am putea scrie: class Punct{ //. . . public int return } public int return } //. . . }

distV(Punct p) { y - p.y; distH(Punct p) { x - p.x;

Se observ c din interiorul metodelor distV()/distH() putem accesa liber membrii privai ai obiectului p dat ca parametru. La fel ar sta lucrurile i dac p ar fi o variabil local a unei metode din clasa Punct. Exteriorul sau clienii clasei. Clienii unei clase pot accesa doar acei membri care au ca modificator de acces cuvntul public. Membrii declarai cu modificatorul private NU sunt vizibili n afar, sunt ascuni. Dac s-ar ncerca folosirea, n metoda main() din exemplul considerat, o referin de genul: p1.x compilatorul ar raporta o eroare. Structura unei clase, sau modul ei de reprezentare, care este dat de variabilele membru, de regul se ascunde fa de clieni. Dac este necesar ca acetia s poat consulta valorile datelor membru, se va opta pentru definirea unor metode de genul getValoare(), iar nu pentru declararea ca publice a datelor respective.

Iniializarea cmpurilor unui obiect. Constructori


n exemplul considerat anterior, dup crearea obiectelor p1 i p2, s-a apelat pentru ele metoda init() care avea ca scop iniializarea cmpurilor x i y pentru cele dou puncte. Acesta este un exemplu de iniializare a cmpurilor unui obiect, ns are unele dezavantaje printre care i acela c programatorul ar putea omite apelul metodei, caz n care cmpurile vor avea valori implicite. n acest sens, programatorul poate prevedea la definirea unei clase

una sau mai multe metode speciale, numite constructori, care servesc tocmai la iniializarea datelor unui obiect imediat ce el a fost creat. Exemplul urmtor, descris tot folosind clasa Punct, va reliefa acest lucru: class Punct{ //. . . public Punct(int xx, int yy) { x = xx; y = yy; } public Punct(Punct p) { x = p.x; y = p.y; } //. . . } Caracteristicile unui constructor sunt: este o metod care are acelai nume cu clasa n care este definit nu are tip returnat (nici mcar void) se apeleaz n mod automat la crearea unui obiect Astfel, n locul metodei init(), n funcia main() a clasei ClientPunct, se poate scrie: class ClientPunct{ public static void Punct p1 = new a Punct p2 = new //. . . } } main(String[ ] arg) { Punct(10,20 ); //se creeaz o instan clasei Punct Punct(30,40 ); // i nc una

O clas poate avea mai muli constructori, datorit facilitii de suprancrcare a funciilor existent n limbajul Java. Se ofer, astfel, posibilitatea de a defini, n acelai domeniu de vizibilitate, mai multe funcii care au acelai nume, dar parametrii diferii ca tip i numr. Al doilea constructor definit anterior poate fi apelat astfel: Punct p3 = new Punct(p1); Dac programatorul nu prevede nici un constructor, atunci compilatorul va prevedea clasa respectiv cu un constructor implicit de tip no-arg, al crui corp de instruciuni este vid. Aa au stat lucrurile n cazul primei definiii a clasei Punct cnd la crearea unui obiect a fost apelat constructorul implicit Punct(). De asemenea, pentru a putea crea obiecte ale claselor descrise, constructorii trebuie s aib modificatorul de acces public. Pe lng constructori, mai exist i alte metode de iniializare a cmpurilor unei clase: Iniializatori la declarare, care pot fi implicii sau explicii: class Punct{ private int x; //iniializator implicit private int y = 1; //iniializator explicit //se presupune c nu exist constructori }
4

//. . . Punct p1 = new Punct(); //n acest caz p1.x = 0 si p1.y=1 Iniializatorii implicii depind de tipul cmpului respectiv: sunt 0 pentru tipurile numerice, false pentru tipul boolean i null pentru referine la obiecte. Blocuri de iniializare: sunt secvene de cod cuprinse ntre acolade, plasate imediat dup declaraia cmpului pe care l iniializeaz, i care pot fi asimilate cu nite constructori no-arg. Ele se folosesc atunci cnd valoarea de iniializare a unui cmp nu este o constant simpl, ci trebuie obinut prin calcule. Ca exemplu, se va presupune c variabila x din clasa Punct trebuie iniializat cu valoarea sumei primelor 6 numere Fibonacci: class Punct{ private int x=2; //iniializator explicit { //bloc de iniializare int a=1,b=1; for(int i=3;i <= 6;i++) { b+=a; a=b-a; x+=b; } //. . . } Deosebirea esenial dintre constructori i celelalte tipuri de iniializri este aceea c, n cazul constructorilor, se putea iniializa fiecare instan cu valori diferite care se transmit ca parametri. Celelalte mecanisme de iniializare presupun ca toate instanele clasei respective s aib aceleai valori. Dac se dorete crearea i iniializarea unui cmp cu comportare echivalent unei constante din limbajul C++, atunci cmpul este marcat ca final, cuvnt cheie plasat ntre modificatorul de acces i tip.

Membri statici ai claselor


Pn acum, n clasa Punct s-au definit doar membri non-statici. Acetia se mai numesc i membri ai instanelor. Limbajul Java permite definirea unei categorii speciale de membri, numii statici sau membri de clas. Aceti membri vor exista n exemplare unice pentru fiecare clas, fiind accesai n comun de toate instanele clasei respective. Mai mult, membrii statici pot fi referii chiar i fr a instania clasa, ei nedepinznd de obiecte. Pentru a defini un membru static se utilizeaz cuvntul cheie static, plasat dup modificatorul de acces: modificator_acces static tip_membru nume_membru; Referirea unui membru static nu se va face prin intermediul numelui obiectului, ci prin intermediul numelui clasei: nume_clasa.nume_membru_static Pentru exemplificare, se va modifica clasa Punct astfel nct s se poat calcula numrul de apeluri ale metodei move() pentru toate obiectele create ntr-un program. Pentru aceasta, se va defini o variabil static contor i o metod static getContor(): class Punct{

private int x; private int y; private static int contor=0; //iniializator implicit public Punct(int xx,int yy ) { x=xx; y=yy; } public void move(int dx, int dy) { x+=xx; y+=yy; contor++;} public static int getContor() {return contor;} //. . . } //metoda main din clasa rdcin Punct p1 = new Punct(10,20); Punct p2 = new Punct(15,13); p1.move(8,-2); p2.move(6,7); //. . . System.out.println(S-au executat mutari.);

+Punct.getContor()+

n legtur cu membrii statici ai unei clase trebuie fcut urmtoarea observaie: ntr-o metod static nu este permis referirea simpl a unui membru non-static. Acest lucru se explic prin aceea c un membru non-static exist doar n interiorul unui obiect, pe cnd membrii statici exist independent de obiecte. Dac n exemplul dat s-ar fi declarat variabila contor ca non-static, atunci ea ar fi existat n attea exemplare, cte obiecte s-ar fi creat i ar fi contorizat pentru fiecare obiect n parte numrul de apeluri ale metodei move().

Tem
Funcia f ( x) = a x 2 + b x + c are ca grafic o parabol cu vrful de coordonate
b b 2 + 4ac . 2a ; 4a

Se cere s se defineasc o clas Parabola ai crei membri vor fi: 3 variabile de tip double care reprezint coeficienii a, b i c un constructor cu 3 parametrii de tip double un constructor cu un parametru de tip Parabola o metod de afiare a funciei sub forma: f(x) = a x^2 + b x + c o metod pentru calculul coordonatelor vrfului o metod static ce primete ca parametri dou parabole i calculeaz coordonatele mijlocului dreptei care unete vrfurile celor dou parabole astfel: x +x y + y2 x= 1 2, y= 1 , unde ( x1 , y1 ) sunt coordonatele vrfului primei 2 2 parabole, iar ( x2 , y2 ) descriu vrful celei de a doua parabole.

Pe lng clasa Parabola, se va mai defini o clas ClientParabola care va exemplifica utilizarea metodelor clasei Parabola.

Lucrarea 3

Gestionarea memoriei dinamice. Tablouri i iruri de caractere. Conversii de date. Operaii de intrare / ieire

Cuprins
Simbolul this.............................................................................................................................................1 Colectorul de reziduuri (Garbage Collector) ...........................................................................................4 Transmiterea parametrilor ........................................................................................................................4 Tablouri n Java ........................................................................................................................................4 Clasa String ..............................................................................................................................................5 Obiecte String i tablouri de caractere......................................................................................................6 Compararea obiectelor..............................................................................................................................6 Compararea stringurilor............................................................................................................................7 Conversii ntre tipul String i tipurile primitive........................................................................................8 Clase nfurtoare ...................................................................................................................................9 Operaii de intrare/ieire la nivel de linii de caractere ............................................................................11 Operaii de citire a liniilor de text.......................................................................................................11 Operaii de scriere a liniilor de text ....................................................................................................12 Tem .......................................................................................................................................................14

n Java, variabilele al cror tip este o clas sunt reprezentate ca referine (adrese) spre obiecte ale clasei. Pentru a putea iniializa o asemenea referin, trebuie generat un obiect nou sau folosit o referin la un obiect deja existent. Unei referine i poate fi atribuit valoarea special null, care se traduce prin faptul c referina respectiv nu indic nici un obiect. Valoarea null nu este atribuit automat tuturor variabilelor referin la declararea lor, ci conform urmtoarei reguli: dac referina este o datmembru a unei clase i ea nu este iniializat n nici un fel, la crearea unui obiect al clasei respective referina va primi implicit valoarea null. Dac referina este o variabil local a unei metode, iniializarea implicit nu mai funcioneaz. De aceea, se recomand ca programatorul s realizeze NTOTDEAUNA o iniializare explicit a variabilelor.

Simbolul this
Simbolul this este o referin care poate fi utilizat doar n cadrul funciilor membru non-statice ale unei clase. Din punctul de vedere al unei funcii membru, acesta este referina spre obiectul receptor, posesor al acelei funcii. Se poate spune c this reprezint contiina de sine a unui obiect, n sensul c obiectul i cunoate adresa la care este localizat n memorie. Practic, orice referire a unei date membru nonstatice v n interiorul unei metode, poate fi considerat ca echivalent cu this.v. De exemplu, metoda move() din clasa Punct, definit n lucrarea precedent, poate fi scris sub forma: class Punct {

//. . . public void move(int dx, int dy) { this.x += dx; this.y += dy; } } Referina this este folosit explicit n cazul n care ar putea exista conflicte de nume cu datele membru ale unui obiect. De exemplu: class Punct { //. . . public Punct(int x, int y){ // parametrii constructorului au nume // identice cu cele ale datelor membru this.x = x; this.y = y; } } De asemenea, referina this se folosete atunci cnd metoda trebuie s returneze o referin la obiectul ei receptor: class Rational { // clasa care modeleaz lucrul cu numere raionale private int numitor=1; private int numarator=0; //. . . public Rational adauga(Rational q) { numarator = numarator * q.numitor + numitor * q.numarator; numitor *= q.numitor; return this; } public Rational adauga(int n) { // se suprancarc funcia adauga numarator += n*numitor; return this; } } // exemplu de utilizare a clasei class ClientRational { public static void main(String[ ] arg){ Rational a = new Rational(); Rational b = new Rational(); a.adauga(5).adauga(6); // putem apela metoda adauga n cascad, b.adauga(3).adauga(a);// deoarece ea returneaz o referin la un obiect Rational } }

O alt situaie cnd se folosete explicit referina this este atunci cnd referina la obiectul receptor trebuie transmis ca parametru la apelul unei alte metode. class Context { private int x; private Algoritm a; // obiectul Algoritm va executa pentru un obiect Context public Context(Algoritm a, int x) { this.a = a; this.x = x; } public int Calcul() { x = a.Calcul(this); return x; } public int getX() { return x; } } class Algoritm { public int Calcul(Context c) { return c.getX()*c.getX(); } }

anumite

calcule

n fine, simbolul this reprezint mijlocul prin care poate fi apelat un constructor al unei clase din interiorul altui constructor al aceleiai clase. class OClasa { //. . . public OClasa(int x, int y) { this.x = x; this.y = y; } public OClasa(OClasa p) { this(p.x, p.y); // se apeleaz constructorul cu 2 parametri int } } Apelul unui constructor din interiorul altui constructor al clasei n cauz NU nseamn crearea unui nou obiect, ci, pentru obiectul receptor curent, se va executa codul constructorului apelat, la fel ca n cazul apelrii unei funcii-membru obinuite. De asemenea, un constructor nu poate fi apelat cu ajutorul simbolului this DECT din interiorul altui constructor i nu al altor metode. n felul acesta se asigur ndeplinirea condiiei ca pentru un obiect iniializarea datelor prin constructor s se execute O SINGUR dat. Referina this NU poate fi utilizat n interiorul unei funcii-membru statice. Referinta this nu poate fi modificat (nu poate s apar ca membru stng ntr-o atribuire) i nu poate s apar n afara corpului de instruciuni al metodelor non-statice ale unei clase sau n afara blocurilor de iniializare asociate cu variabilele membru ale unei clase.

Colectorul de reziduuri (Garbage Collector)


Spre deosebire de limbaje precum Pascal sau C, unde programatorul trebuie s elibereze memoria ocupat de obiecte dup ce acestea nu mai sunt folosite, n Java programatorul este scutit de aceast sarcin de care se ocup o component a mainii virtuale numit Garbage Collector. Principiul de lucru al acestuia este urmtorul: dac spre un anumit obiect nu mai exist nici o referin extern, n nici o funcie activ, acel obiect devine candidat la eliminarea din memorie. Pentru utilizator, aciunea Garbage Collector-ului este transparent. Exist posibilitatea de a surprinde momentul n care un anumit obiect este eliminat, deoarece Garbage Collector-ul nu acioneaz fr preaviz. Astfel, dac n clasa de care aparine obiectul este definit o metod numit finalize(), cu prototipul: protected void finalize() throws Throwable; aceast metod va fi executat chiar nainte ca obiectul s dispar. Dac metoda este definit astfel nct s tipreasc pe ecran un anumit mesaj, poate fi detectat momentul n care un obiect este eliminat din memorie.

Transmiterea parametrilor
n Java, transmiterea parametrilor la apelul metodelor se face NUMAI prin VALOARE. Modificrile aduse parametrilor n interiorul unei funcii nu se pstreaz la revenirea din funcia respectiv. Dac unul dintre parametrii unei funcii are drept tip o clas, atunci, la apel, funcia va primi referina unui obiect al clasei. n cazul acesta, ceea ce se transmite prin valoare este chiar referina, NU obiectul indicat de ea. Funcia NU VA PUTEA MODIFICA REFERINA respectiv, dar VA PUTEA MODIFICA OBIECTUL indicat de ea.

Tablouri n Java
Limbajul Java ofer tipul tablou (tip predefinit), cu ajutorul cruia se pot crea colecii de variabile de acelai tip. Componentele tabloului pot fi accesate cu ajutorul unui index. Acesta trebuie s aparin unui tip ntreg, iar numerotarea elementelor ntr-un tablou ncepe ntotdeauna de la 0, la fel ca i n C. Un tablou se declar astfel: tip_element[ ] nume_tablou; n Java, elementele unui tablou se aloc DINAMIC, specificndu-se dimensiunea tabloului, iar numele unui tablou este considerat ca o REFERIN DE OBIECT: tip_element[ ] nume_tablou = new tip_element [numar_elemente]; La crearea unui tablou se memoreaz dimensiunile sale, programatorul putnd n orice moment s le cunoasc apelnd la atributului length: static void oFunctie(int[ ] tabp) { //. . . for(int i=0; i < tabp.length; i++) { // tabp.length returneaz dimensiunea tabloului // prelucreaz tabp[ i ] }

} public static void main(String[ ] arg) { int [ ] tab = new int[10]; //. . . oFunctie(tab); //. . . } Dac elementele unui tablou sunt, la rndul lor, de tip clas, elementele respective vor fi referine la obiecte ale acelei clase. La crearea tabloului se rezerv spaiu DOAR pentru acele referine i ele sunt iniializate cu valoarea null. Java accept i declaraii de tablouri bidimensionale: tip_element[ ] [ ] nume_matrice;

Clasa String
Clasa String este una dintre clasele predefinite, fundamentale, care modeleaz lucrul cu iruri de caractere. Modul cel mai simplu de manipulare a obiectelor de tip String l constituie constantele String, care sunt iruri de caractere ncadrate de ghilimele. Orice referire a unei asemenea constante n cadrul unui program Java are ca efect crearea unui obiect String care conine secvena de caractere respectiv. Clasa String are mai muli constructori: un constructor care are ca parametru o referin la String. Noul obiect creat va avea acelai coninut ca i obiectul indicat de parametru: String s1 = "un sir"; String s2 = new String(s1); un constructor no-arg, care creaz un ir vid: String svid = new String( ); un constructor care are ca parametru un tablou cu elemente de tip char: char[ ] tc = {'a','b','c'}; String stc = new String(tc); Clasa String ofer un set de metode pentru prelucrarea unor iruri existente: String toUpperCase(); String toLowerCase(); String substring(int); String substring(int, int); Metodele clasei String includ i operaii care permit consultarea irurilor de caractere, respectiv, obinerea de informaii despre coninutul lor: int length(); char charAt(int); int indexOf(char);

int indexOf(String); int lastIndexOf(char); int lastIndexOf(String); O proprietate foarte important a metodelor clasei String este aceea c ele nu modific NICIODAT obiectul receptor, ci creeaz obiecte noi n care este inclus irul de caractere modificat. n Java, operaia de concatenare se realizeaz cu ajutorul operatorului + . Va fi creat unu obiect nou, al crui coninut va fi irul rezultat prin concatenare. String s1 = "codul "; String s2 = "penal"; String s3 = s1 + s2; Operaia de concatenare suport urmtoarele precizri: clasa String este singura asupra creia se poate aplica operatorul +. Tipurile primitive numerice i tipul char mai au definit acest operator (cu alt semnificaie); ntr-o expresie de concatenare pot s apar i valori ale tipurilor primitive, acestea fiind automat convertite la String; String s2 = "x = " +8.5; ntr-o expresie de concatenare pot s apar ca operanzi i obiecte ale altor clase dect String, cu condiia ca n clasele respective s fie definit metoda toString care s genereze obiecte String.

Obiecte String i tablouri de caractere


Dei, aparent, un ir de caractere i un tablou de caractere (tipul char[ ]) modeleaz acelai lucru (o secven de caractere), cele dou tipuri se comport diferit. Unui obiect String nu i se poate modifica coninutul, unui tablou de caractere i se poate modifica oricare element. Pentru a obine caracterul aflat la o anumit poziie pos ntr-un String, s, nu putem folosi notaia s[pos] ca pentru tablouri, ci trebuie utilizat metoda charAt(pos). Metoda toCharArray() creeaz un tablou ale crui elemente sunt caracterele irului de caractere surs. Operaia invers se realizeaz cu ajutorul unui constructor al clasei String.

Compararea obiectelor
Una dintre operaiile cele mai frecvente de comparare a obiectelor este testarea egalitii. n Java aceast operaie poate avea dou sensuri: testarea identitii: n acest caz ceea ce se compar sunt dou referine de obiect, iar testul presupune a verifica dac cele dou referine indic unul

i acelai obiect. Testarea identitii se realizeaz cu ajutorul operatorului ==; testarea echivalenei: n acest caz operanzii sunt dou obiecte propriu-zise i testul presupune a verifica dac cele dou obiecte au coninut asemntor. De regul, pentru testarea echivalentei se utilizeaz o metod denumit equals(), operanzii fiind un obiect receptor i unul dat ca parametru metodei. Aceast metod este definit n clasa Object i este motenit de toate clasele. Definiia metodei equals(), aa cum apare ea n clasa Object, pentru majoritatea claselor utilizatorului nu este satisfctoare. Ca urmare, programatorul va trebui s-i defineasc propriile variante de equals(), acolo unde este cazul. Pentru clasa Punct, o posibil implementare a metodei equals() ar fi: class Punct{ //. . . public boolean equals(Punct p) { return ((x==p.x) && (y==p.y)); } } class ClientPunct { public void oMetoda( ) { Punct p1 = new Punct(1,2); Punct p2 = new Punct(1,2); Punct p3 = p1; boolean t1 = (p1==p2); //test de identitate; rezultat: false boolean t2 = p1.equals(p2); //test de echivalen; rezultat: true boolean t3 = (p1==p3); //test de identitate; rezultat: true //. . .

} }

Compararea stringurilor
Regulile prezentate mai sus se aplic i asupra clasei String. Ca atare, se va utiliza operatorul == pentru a testa identitatea obiectelor String i metoda equals() pentru a testa egalitatea irurilor de caractere coninute de obiectele String. class oClasa { public void oMetoda( ) { String s1 = "ababu"; String s2 = "ababu"; Punct s3 = s1; boolean t1 = (s1==s2); //test de identitate; rezultat: false boolean t2 = s1.equals(s2); //test de echivalen; rezultat: true
7

boolean t3 = (s1==s3); rezultat: true //. . . } }

//test

de

identitate;

Se observ c referinele s1 i s2 indic obiecte distincte, dei ele au fost iniializate cu acelai ir de caractere. Acest lucru este o consecin a faptului c n Java o constant String este echivalent cu new String(constant), ceea ce determin de fiecare dat crearea unui nou obiect. Pe lng metoda equals(), clasa String mai conine i alte metode de comparare: metoda equalsIgnoreCase(), care este similar cu equals(), dar la comparare nu difereniaz literele mari de cele mici. metoda compareTo(), care primete ca parametru o referin String i returneaz ca rezultat un numr ntreg a crui valoare este: negativ, dac irul din obiectul receptor este mai mic dect irul din obiectul parametru; nul, dac cele dou iruri sunt egale; pozitiv dac irul din obiectul receptor este mai mare dect irul din obiectul parametru; Metoda compareTo() poate fi folosit pentru realizarea sortrii alfabetice a irurilor de caractere.

Conversii ntre tipul String i tipurile primitive


ntr-un program Java, conversiile ntre tipul String i tipurile primitive sunt foarte necesare, n primul rnd pentru c datele de intrare ale programului sunt receptate n majoritatea cazurilor sub form de iruri de caractere, deoarece n Java nu exist ceea ce n C se numea funcie de citire formatat (scanf). n ceea ce privete conversiile, n Java exist o regul care spune c ntotdeauna tipul spre care se face conversia trebuie s aib metoda necesar conversiei. n acest sens, clasa String este dotat cu metode numite valueOf() care asigur conversiile de la tipurile primitive spre String. class oClasa { public void oMetoda( ) { int a = 4; double b = 13.2; boolean c = true; String s1 = String.valueOf(a); String s2 = String.valueOf(b); String s3 = String.valueOf(c); //. . . } }

valueOf() este o metod static, suprancrcat astfel nct s admit ca parametri valori ale tuturor tipurilor primitive. Efectul metodei este crearea unui obiect String al crui coninut este obinut prin conversia la ir de caractere a valorii parametrului. Ca rezultat, metoda returneaz o referin la obiectul String creat. Pentru a realiza conversiile n sens invers, adic de la String spre tipurile primitive se utilizeaz metode ale unor clase speciale, numite clase nfurtoare (wrapper classes).

Clase nfurtoare
Tuturor tipurilor primitive din limbajul Java le corespunde cte o clas nfurtoare. Aceste clase au fost definite din dou motive: s nglobeze metode de conversie ntre valori ale tipurilor primitive i obiecte ale altor clase, precum i constante speciale legate de tipurile primare; s permit crearea de obiecte care s conin valori ale tipurilor primare, i care apoi s poat fi utilizate n cadrul unor structuri unde nu pot s apar dect obiecte, nu date simple. Clasele nfurtoare sunt definite n pachetul java.lang i ele sunt: Integer pentru int Short pentru short Byte pentru byte Long pentru long Float pentru float Double pentru double Boolean pentru boolean Character pentru char Void pentru void Exceptnd clasa Void, restul claselor nfurtoare conin urmtoarele metode: cte un constructor care accept ca parametru o valoare a tipului primitiv corespondent. Integer a = new Integer(5); Double b = new Double(12.13); cte un constructor care accept ca parametru o referin String (excepie face clasa Character care nu are acest constructor). Se presupune c irul respectiv conine o secven compatibil cu tipul primitiv corespondent. Integer a = new Integer("5"); Boolean b = new Boolean("true"); o metod static valueOf() care accept ca parametru un String i returneaz un obiect al clasei nfurtoare, convertind coninutul irului;
9

apelul acestei metode este echivalent cu a executa un new cu constructorul descris mai nainte. Integer a = Integer.valueOf("5"); Double b = Double.valueOf("-8.12"); o metod toString() care realizeaz practic conversia de la clasa nfurtoare la String De multe ori este necesar ca datele coninute de obiecte s fie convertite la iruri de caractere n vederea participrii obiectelor respective ca operanzi n expresii de manipulare a irurilor. De exemplu, funciile print()/println() necesit ca parametru o referin String. Convertind un obiect la String, am putea s afim acel obiect cu ajutorul funciilor print()/println(). Programatorul trebuie s-si defineasc n clasele sale cte o metod toString(), dac dorete s converteasc obiectele respective la iruri. class Punct { //. . . public String toString( ) { String s = "("+String.valueOf(x)+","+ String.valueOf(y)+")"; return s; } } o metod typeValue(), unde type este numele tipului primitiv corespondent; aceast metod returneaz coninutul obiectului ca valoare a tipului primitiv. Integer a = new Integer("5"); int x = a.intValue( ); Double b = new Double("3.14"); double y = b.doubleValue( ); o metod equals() care realizeaz testul echivalenei obiectelor. o metod compareTo() care accept ca parametru un obiect al aceleiai clase nfurtoare ca i obiectul receptor. Metoda returneaz o valoare de tip int pozitiv, nul sau negativ dup cum coninutul obiectului receptor este mai mare, egal sau mai mic dect cel al parametrului. Integer a = new Integer(7); Integer b = new Integer(10); int x = b.compareTo(a); //val. x va fi pozitiv Pentru conversiile de la String la tipurile primitive se utilizeaz metode statice ale claselor nfurtoare, si anume: n cazul claselor care reprezint tipuri numerice exist metodele parseType(), unde Type este numele tipului primitiv corespondent scris cu prima liter majuscul; metoda accept ca parametru irul care

10

trebuie convertit i returneaz valoarea corespunztoare, n reprezentarea tipului primitiv respectiv; n cazul tipului boolean exista metoda getBoolean() care funcioneaz similar metodei parseType(). n cazul tipurilor numerice, dac irul care se convertete nu conine o secven n concordan cu tipul primitiv spre care se face conversia, programul va fi ntrerupt cu un mesaj de eroare corespunztor (excepie NumberFormatException).

Operaii de intrare/ieire la nivel de linii de caractere


Pachetul java.io este un set de clase cu ajutorul crora se realizeaz operaiile de intrare/ieire ntr-un program Java. n Java, operaiile de intrare/ieire se bazeaz pe conceptul de flux de date (stream). Un flux de date este o secven de date care se deplaseaz dinspre o surs extern spre memorie (flux de intrare input stream) sau din memorie spre o destinaie extern (flux de ieire output stream). Pentru operaiile de intrare/ieire cea mai frecvent surs extern este tastatura, iar destinaia este ecranul monitorului. Acestea se mai numesc i suporturi standard de intrare, respectiv ieire. Corespunztor suporturilor standard, n Java exista dou obiecte predefinite: System.in pentru tastatur i System.out pentru monitor. O linie de caractere este o secven terminat cu un caracter special, numit terminator de linie, care, la tastatur, are drept corespondent tasta Enter. Pentru a realiza citiri la nivel de linie vor fi utilizate dou subclase ale clasei Reader (BufferReader i InputStreamReader). Pentru scrierea de linii, se va folosi clasa PrintStream.

Operaii de citire a liniilor de text Clasa care modeleaz citirea unei linii dintr-un flux de intrare este BufferedReader, prin operaia readLine(). Aceast operaie nu are parametri, iar execuia ei are ca efect citirea din fluxul de intrare a unei secvene de caractere pn la ntlnirea terminatorului de linie. Operaia returneaz o referin la un obiect String care conine caracterele citite, dar fr a include i terminatorul. Cu alte cuvinte, irul returnat conine doar caracterele utile (semnificative) ale liniei. Dac s-a ajuns la sfritul fluxului de intrare, operaia returneaz valoarea null. Dac citirea nu se poate desfura, operaia emite o excepie de tip IOException. De aceea, semntura unei funcii care apeleaz metoda readLine(), dar nu trateaz eventualele erori de citire, trebuie sa conin clauza throws IOException. Una dintre cele mai frecvente erori ntr-un program Java este omiterea clauzelor throws din antetul funciilor utilizatorului care apeleaz funcii predefinite dotate cu aceast clauz. Pentru a crea un obiect al clasei BufferedReader este necesar s i se furnizeze constructorului acesteia o referin la un obiect al clasei InputStreamReader. Constructorul acesteia din urm necesit: o referin la un obiect FileInputStream, dac citirea se face dintr-un fiier de pe disc

11

referin System.in, dac citirea se face de la tastatur. Deci, dac urmeaz o citire dintr-un fiier al crui nume este dat de o variabil String nume_fis, va trebui s se creeze un obiect BufferedReader ca n secvena de mai jos: BufferedReader flux_in = new BufferedReader(new InputStreamReader(new FileInputStream(nume_fis))); Dac citirea se va face de la tastatur, obiectul BufferedReader se creeaz astfel: BufferedReader flux_in = new BufferedReader(new InputStreamReader (System.in)); n continuare, citirea se va realiza cu apelul: linie = flux_in.readLine(); unde linie este o referin String. n urma apelului, ea va indica spre un obiect care conine caracterele citite. n funcie de natura datelor reprezentate de aceste caractere, uneori pot fi necesare conversii de la String la alte tipuri, n vederea utilizrii datelor respective n diverse calcule. Se va prezenta un program care citete numere ntregi dintr-un fiier text, calculeaz i afieaz pe ecran suma acestora. Se presupune c fiecare numr din fiier se afl pe cte o linie distinct, iar numele fiierului este dat ca argument la execuia programului.
public class Suma { public static void main (String[ ] args) throws IOException { BufferedReader flux_in = new BufferedReader (new InputStreamReader (new FileInputStream(args[0]))); int suma = 0; String linie; while ((linie = flux_in.readLine()) != null) suma+=Integer.parseInt(linie); System.out.println("Suma = "+suma); flux_in.close(); //se inchide fisierul (fluxul) } }

Operaii de scriere a liniilor de text Afiarea unei linii pe ecran se realizeaz apelnd metodele print()/println() definite n clasa PrintStream, pentru obiectul System.out. Pentru a scrie o linie ntr-un fiier de pe disc, se vor folosi aceleai metode, dar va trebui s se creeze un obiect separat al clasei PrintStream. Pentru aceasta, trebuie furnizat constructorului clasei PrintStream, ca parametru, o referin la un obiect FileOutputStream, ca n secvena de mai jos: PrintStream flux_out = new PrintStream (new FileOutputStream(nume_fis)); unde nume_fis este o referin la un obiect String ce conine numele fiierului.

12

Se va prezenta un program care citete dintr-un fiier text o secven de numere reale, dispuse cte unul pe linie, determin numrul lor, suma, media aritmetic, valoarea minim/maxim i tiprete aceste informaii ntr-un alt fiier. Numele ambelor fiiere implicate se dau ca argumente la execuia programului. import java.io.*; public class Statist { public static void main (String[ ] args) throws IOException { BufferedReader flux_in = new BufferedReader (new InputStreamReader (new FileInputStream(args[0]))); PrintStream flux_out = new PrintStream (new FileOutputStream(args[1])); double suma = 0.0, min, max, val; int contor=0; String linie; while ((linie = flux_in.readLine()) != null) { contor++; val = Double.parseDouble(linie); //conversia String -> double suma+=val; if(contor==1) { //s-a citit primul numar min = val; max = val; } else { if(val < min) min = val; if(val > max) max =val; } } flux_out.println("S-au citit "+contor+" valori"); flux_out.println("Suma lor este "+suma); flux_out.println("Media aritmetica este "+(contor>0 ? suma/contor : 0.0)); flux_out.println("Element minim: "+min); flux_out.println("Element maxim: "+max); flux_in.close( ); flux_out.close(); } } n continuare, se va prezenta un program n care va fi ilustrat conceptul de introducere interactiv de date, care presupune c utilizatorul trebuie s introduc anumite date de la tastatur, Pentru aceasta, este necesar ca naintea operaiilor de citire, n program s existe operaii de afiare a unor mesaje de genul: Introducei denumirea: sau Dai cantitatea:. Programul considerat realizeaz completarea unui tablou de numere ntregi, citind numerele de la tastatur, apoi afieaz pe ecran tabloul ordonat cresctor. import java.io.*; public class Ordonare{

13

public static void IOException{

main

(String

[]

args)

throws

BufferedReader flux_in=new BufferedReader (new InputStreamReader(System.in)); int[] tab = new int[10]; int i, aux; String linie; boolean sw = true; for(i=0;i<tab.length;i++){ System.out.println("Dati pozitia "+(i+1)); linie=flux_in.readLine(); tab[i]=Integer.parseInt(linie); } while(sw){ sw=false; for(i=o;i<tab.length-1;i++) if(tab[i]>tab[i+1]){ aux=tab[i]; tab[i]=tab[i+1]; tab[i+1]=aux; sw=true; } } System.out.println("Tabloul ordonat este:"); for(i=0;i<tab.length;i++) System.out.println(tab[i]+" "); } } elementul de pe

Tem
S se scrie un program care citete dintr-un fiier text informaii referitoare la produsele dintr-un magazin. Pe fiecare linie se afl: denumirea produsului, preul (numr real) i cantitatea (numr real). Cele trei elemente sunt separate prin caracterul ";" (zahr;1.5;50). Programul va afia, sub forma unui tabel, coninutul fiierului i va determina denumirea, preul i cantitatea produsului cu pre minim, respectiv maxim pe care le va scrie ntr-un fiier destinaie. Ambele fiiere vor fi primite ca parametri la apel.

14

Indicaie: Pentru o tratare mai elegant a subiectului se poate defini o clas Produs care s conin ca date membru cele trei elemente ce descriu un produs. Pentru a tipri informaiile cerute se va prevedea o metod toString() n clasa Produs.

15

Lucrarea 4

Motenire i polimorfism
Cuprins
Relaia de motenire n Java .....................................................................................................................1 Reguli de vizibilitate n contextul relaiei de motenire ...........................................................................2 Constructorii i motenirea.......................................................................................................................4 Operatorul instanceof ...............................................................................................................................4 Redefinirea metodelor ..............................................................................................................................5 Legarea dinamic i constructorii.............................................................................................................7 Tem .........................................................................................................................................................8

Tehnologia orientat pe obiecte permite extinderea comportamentului unei clase existente prin definirea unei clase noi, care motenete coninutul primei clase, adugnd la acesta elementele specifice ei. Clasa motenit se numete clas de baz sau superclas, iar clasa care realizeaz extinderea se numete subclas, clas derivat, sau clas descendent. Relaia de motenire este o relaie de forma este un fel de, adic subclasa este un fel de superclas. Practic, relaia de motenire ntre clase este o reflectare a ierarhizrii existente ntre entitile modelate de clasele respective. Relaia de motenire prezint dou aspecte eseniale: reutilizare de cod; polimorfism. Relaia de motenire n Java n Java, relaia de motenire dintre o superclas i o subclas se exprim astfel: class nume_subclasa extends nume_superclasa { //coninut specific subclasei } Se consider clasa Poligon i o clas Dreptunghi care motenete clasa Poligon: class Poligon { protected double[ ] laturi; public Poligon(int n) { laturi = new double[n] } public double perimetru( ) { double s=0; for(int i=0;i<laturi.length;i++) s+=laturi[i]; return s; } } class Dreptunghi extends Poligon { public Dreptunghi(double L, double h){ super(4);
1

} public double aria( ){ return laturi[0]*laturi[1]; }

laturi[0] = laturi[2] laturi[3] = h;

L;

laturi[1]

Clasa Dreptunghi reutilizeaz codul definit n clasa Poligon, la care adaug elemente proprii. Pe lng Dreptunghi, am putea defini i alte clase, cum ar fi Triunghi, Patrat, Pentagon etc. care s moteneasc Poligon. Superclasa este factorul comun al subclaselor sale sau, altfel spus, codul factorului comun este reutilizat n subclase. Despre relaia de motenire n Java se pot spune urmtoarele: o subclas poate extinde o singur superclas (motenire simpl); o superclas poate fi motenit de mai multe subclase distincte. O subclas poate fi superclas pentru alte clase. O clas de baz mpreun cu toate descendentele ei formeaz o ierarhie de clase.

Reguli de vizibilitate n contextul relaiei de motenire


Regulile de vizibilitate vor fi discutate din dou perspective: accesul funciilor unei subclase la membrii motenii de la superclasele ei; accesul clienilor unei subclase la membrii ei, motenii sau specifici. Accesul funciilor unei subclase la membrii motenii Pentru a ilustra drepturile de acces vom lua urmtorul exemplu: class SuperClasa { private int priv; protected int prot; public int pub; } class SubClasa extends SuperClasa { private int priv_local; protected int prot_local; public int pub_local; public void metoda(SubClasa p ) { priv = 1; //eroare prot = 2; //corect pub = 3; //corect priv_local = 4; //corect prot_local = 5; //corect pub_local = 6; //corect p.priv = 1; //eroare p.prot = 2; //corect p.pub = 3; //corect p.priv_local = 4; //corect
2

} }

p.prot_local = 5; //corect p.pub_local = 6; //corect

n metodele specifice ale unei subclase pot fi referii acei membri motenii, care n superclas au fost precedai de modificatorii protected sau public. Membrii motenii, care n superclas sunt precedai de modificatorul private, dei fizic fac parte din subclas, nu sunt accesibili n metodele acesteia. Referirea membrilor motenii se face direct, folosind numele lor, la fel ca referirea membrilor specifici. Acest lucru este foarte normal, deoarece i membrii motenii sunt coninui n subclas, numai c ei nu au fost declarai explicit. Includerea lor se face automat, ca efect al clauzei extends. Acest lucru nu este valabil i pentru constructori. Revenind la exemplul cu clasele Poligon Dreptunghi, trebuie spus c un obiect al clasei Dreptunghi conine doi constructori: unul motenit, responsabil cu iniializarea poriunii motenite, i unul local, responsabil cu iniializarea poriunii specifice subclasei. Referirea n subclas la constructorul motenit se face folosind cuvntul cheie super, aa cum se observ n constructorul clasei Dreptunghi. Cuvntul super este un omolog al lui this i reprezint referina la partea motenit a unui obiect. n afar de apelul constructorului motenit, cuvntul super se va utiliza pentru a distinge ntre un membru local al subclasei i un membru motenit, ambii cu acelai nume. Accesul clienilor unei subclase la membrii acesteia Exemplului din paragraful anterior i se adaug secvena urmtoare: class Client { public void oFunctie( ) { SuperClasa sp = new SuperClasa(); SubClasa sb = new SubClasa(); sp.priv = 1; //eroare sp.prot = 2; //corect, doar dac Client i SuperClasa s-ar afla in acelai pachet sp.pub = 3; //corect sb.priv = 1; //eroare sb.prot = 2; //corect, doar dac Client i SubClasa s-ar afla n acelai pachet sb.pub = 3; //corect sb.priv_local = 4; //eroare sb.prot_local = 5; //corect, doar dac Client i SubClasa s-ar afla n acelai pachet sb.pub_local = 6; //corect

} }

n clienii unei subclase pot fi referii acei membri, motenii sau locali, care sunt precedai de modificatorul public. Membrii declarai ca protected sunt accesibili numai n situaia n care clientul se afl n acelai pachet cu subclasa

respectiv. La referirea membrilor unei subclase de ctre client, nu se face deosebire ntre membrii motenii i cei locali.

Constructorii i motenirea
n exemplul anterior se observ c cele dou clase nu au constructori explicii, constructorul SubClasa() fiind de forma: public SubClasa() { super( ); } Constructorul subclasei apeleaz constructorul no-arg al superclasei. Dac pentru SuperClasa am fi definit un constructor no-arg explicit, atunci acesta ar fi fost cel apelat de constructorul SubClasa. Dac ar fi existat un constructor oarecare n SubClasa, iar acesta nu ar fi apelat explicit vreun constructor din SuperClasa, n mod automat se ncearc apelul constructorului no-arg super(), nainte de a se executa instruciunile din constructorul SubClasa: public SubClasa(. . .) { super(); //apel implicit //alte instruciuni prevzute de programator } Dac SuperClasa nu are constructor no-arg, apelul implicit de mai sus ar genera eroare. Prin urmare, dac programatorul definete constructori explicii ntr-o subclas, el trebuie s aib grij ca aceti constructori s apeleze explicit constructorii adecvai ai superclasei: super (parametri-actuali) Un asemenea apel trebuie s fie prima instruciune din constructorul subclasei. Constructorul unei subclase se definete astfel nct lista lui de parametri s se compun dintr-un set de parametri necesari iniializrii cmpurilor motenite (transmis constructorului super) i un alt set, necesar iniializrii cmpurilor locale. Execuia constructorului se desfoar n 3 etape: apelul constructorului superclasei; iniializarea cmpurilor cu valori date la declarare i execuia blocurilor de iniializare, unde este cazul; execuia corpului propriu-zis al constructorului.

Operatorul instanceof
Prin intermediul unei referine de superclas putem indica i utiliza i obiecte ale subclaselor ei. Operatorul instanceof este util atunci cnd trebuie s cunoatem clasa concret de care aparine un obiect referit prin intermediul unei referine a unui supertip. Aplicarea operatorului se face printr-o expresie de forma: referinta_obiect instanceof nume_clasa

Redefinirea metodelor
Relaia de motenire poate fi utilizat atunci cnd o anumit clas (subclas) extinde comportamentul altei clase (superclase), n sensul c superclasa nglobeaz aspectele comune ale unei ierarhii de abstraciuni, iar subclasele adaug la aceast parte comun funciuni specifice. De asemenea, o referin la superclas poate indica i manipula obiecte ale oricrei clase descendente din ea. Manipularea se refer la faptul c, prin intermediul acelei referine se pot apela metodele definite n superclas, indiferent dac obiectul concret aparine superclasei sau uneia dintre subclase. Aceast facilitate constituie aa-numitul polimorfism parial. Folosind o referin a superclasei nu se poate, ns, apela o metod local a subclasei, dect dac se realizeaz o conversie explicit (casting) a referinei, la tipul subclasei. n Java relaia de motenire poate fi aplicat i n cazul n care se dorete ca, pe lng extinderea comportamentului, s realizm i o adaptare (specializare) a lui, n sensul c unele metode care n superclas au o anumit implementare, n subclase s aib o alt implementare, adaptat la cerinele locale ale subclasei. class Angajat{ protected String nume; protected double sal_ora; public Angajat(String nume, double sal_ora) { this.nume = nume; this.sal_ora = sal_ora; } public double calcSalar(int nr_ore) { return (sal_ora * nr_ore); } public String toString() { return nume; } } class Sef extends Angajat { private double proc_cond; //procent de indemnizaie conducere public Sef(String nume, double sal_ora, double proc_cond) { super(nume, sal_ora); this.proc_cond = proc_cond; } public double calcSalar(int nr_ore) { return ((1 + proc_cond / 100) * sal_ora * nr_ore); } } class Client { public void oMetoda { Angajat a1 = new Angajat("Artaxerse Coviltir", 1000); Angajat a2 = new Sef("Al Bundy", 2000, 33.2); int ore_luna = 160; System.out.println("Salar " + a1 + "=" + a1.calcSalar(ore_luna));

} //. . .

System.out.println("Salar " a2.calcSalar(ore_luna));

a2

"="

Se observ c aceeai metod, calcSalar() apare att n clasa Angajat, ct i n clasa Sef, dar cu implementri diferite. Atunci cnd ntr-o subclas apare o metod avnd semntura identic cu o metoda din superclas, se spune c metoda din subclas o redefinete pe omonima ei din superclasa. n acest caz, un obiect al subclasei practic are dou exemplare ale metodei respective, unul motenit i unul propriu. Ce se ntmpl la nivelul clienilor ierarhiei? Se observ c n metoda oMetoda() din clasa Client se folosesc dou referine la Angajat: una indic un obiect al clasei Angajat, iar cealalt un obiect al clasei Sef. Prin intermediul celor dou referine se apeleaz metoda calcSalar(). Se pune problema care dintre cele dou forme ale metodei se apeleaz efectiv? n Java, regula care se aplic n asemenea cazuri este urmtoarea: Se va executa ntotdeauna acel exemplar de metod care este definit n clasa la care aparine obiectul concret indicat de referina utilizat. Aceast facilitate, adugat polimorfismului parial conduce la polimorfismul total. Cu alte cuvinte, clasa obiectului dicteaz i nu tipul referinei. Astfel, dei ambele referine a1 i a2 din exemplul considerat sunt de tip Angajat, ele indic, de fapt, obiecte ale unor clase diferite. Deci, apelul a1.calcSalar(...) va declana execuia metodei calcSalar() definit n clasa Angajat, iar apelul a2.calcSalar(...) va declana execuia metodei calcSalar() definit n clasa Sef. Polimorfismul total permite interschimbarea obiectelor indicate de o referin a superclasei, ntr-o manier transparent pentru clieni. n Java, clasa concret la care aparine un obiect indicat de o referin se cunoate, ns, abia la execuia programului. Deci, un apel de metod nu este rezolvat (adic pus n coresponden cu o implementare anume) la compilare, ci doar la execuie. Acest procedeu se numete legare dinamic sau amnat (late binding). S-a spus mai sus c un obiect al clasei Sef posed dou metode calcSalar(): cea motenit de la Angajat i cea local. Din cauz c n Java se aplic legarea dinamic, nseamn c, din perspectiva clienilor clasei Sef varianta local o eclipseaz pe cea motenit, neexistnd posibilitatea ca un client s foreze apelul metodei motenite (dect aplicnd o soluie ocolitoare, adic folosind o metod cu alt nume care s apeleze metoda calcSalar() motenit). n interiorul subclasei se poate realiza distincia ntre cele dou variante ale unei metode redefinite, i anume folosind simbolul super. Astfel, metoda calcSalar() din clasa Sef se poate folosi de omonima ei din clasa Angajat: class Sef extends Angajat { //. . . public double calcSalar(int nr_ore) { return ((1 + proc_cond / 100) * super.calcSalar(nr_ore)); } }

Practic, aceasta este singura situaie n care nu se aplic legarea dinamic, ci apelul de forma super.numeMetoda(...) este pus n coresponden exact cu implementarea din superclasa clasei curente.

Legarea dinamic i constructorii


Deoarece n Java la apelul metodelor non-statice se aplic legarea dinamic, pe de o parte, i innd cont de modul n care se apeleaz constructorii ntr-o ierarhie de clase, pe de alt parte, trebuie s avem grij cum proiectm constructorii dac acetia apeleaz la rndul lor metode ale claselor respective. class SuperClasa { protected int a; protected int rez; private int x; public SuperClasa( ) { a = 1; rez = 2; x = calcul( ); } public int calcul( ) { return (rez + a); } } class SubClasa extends SuperClasa { protected int b; private int y; public SubClasa( ) { b =3; y = calcul( ); } public int calcul( ) { return (rez * b); } } class Client { public void oMetoda { SubClasa ob = new SubClasa( ); //. . . } } S urmrim ce se ntmpl la crearea unui obiect al clasei Subclasa: innd cont de ordinea n care au loc iniializrile cmpurilor unui obiect, nseamn c se execut urmtorii pai: cmpurile a, rez, x, b i y se iniializeaz cu valorile implicite corespunztoare tipurilor lor, deci, n cazul nostru cu 0; se lanseaz constructorul SubClasa(); ca prim aciune a acestuia are loc apelul constructorului no-arg SuperClasa(); n constructorul SuperClasa() se execut iniializrile lui a i rez cu 1, respectiv 2, dup care se apeleaz metoda calcul(). n acest moment, apelul se leag de varianta metodei calcul() definit n clasa SubClasa, de care aparine obiectul n curs de iniializare. Efectul este

c x se va iniializ cu valoarea 2 * 0, adic cu 0, deoarece la momentul respectiv cmpul b nc nu a apucat s primeasc o alt valoare; se revine n constructorul SubClasa(), se iniializeaz b cu 3 i y cu 2*3 = 6, ct returneaz metoda calcul() din SubClasa. Dac n constructorul unei superclase se apeleaz o metod care este redefinit n subclas, la crearea unui obiect al subclasei exist riscul ca metoda respectiv s refere cmpuri neiniializate nc la momentul apelului.

Tem
Problema propus ncearc s dea o mn de ajutor n gestionarea produselor unei firme care comercializeaz echipamente electronice. Fiecare echipament este nregistrat cu un numr de inventar nr_inv, are un pre pret i este plasat ntr-o anumit zon din magazie zona_mag. Orice echipament poate fi ntr-una din situaiile: achiziionat (intrat n magazie); expus (expus n magazin); vndut (transportat i instalat la client). Firma comercializeaz mai multe tipuri de echipamente. Toate echipamentele care folosesc hrtia drept consumabil sunt caracterizate de numrul de pagini scrise pe minut ppm. Imprimantele sunt caracterizate de rezoluie (numr de puncte per inch dpi) i numr de pagini/cartu p_car. Unei imprimante i se poate seta modul de scriere: tiprireColor (selectere a modului color de tiprire); tiprireAlbNegru (selectere a modului alb-negru de tiprire). Copiatoarele sunt caracterizate de numrul de pagini/toner p_ton. Se poate seta formatul de copiere: setFormatA4 (setare a formatului A4); setFormatA3 (setare a formatului A3). Sistemele de calcul au un monitor de un anumit tip tip_mon, un procesor de o anumit vitez vit_proc, o capacitate a HDD c_hdd i li se poate instala: instalWin (instalarea unei variante de Windows); instalLinux (instalarea unei variante de Linux). Metodele specificate mai sus vor afia textul din parantez. De asemenea, fiecare metod va afia numrul de inventar al echipamentului. Se cere s se realizeze ierarhia de clase corespunztoare modelului prezentat, s se realizeze cteva echipamente i s se aplice operaiile posibile asupra lor. n momentul n care un client cumpr un echipament de la respectiva firm, trebuie verificat buna funcionare a echipamentului, prin apelul unei metode numit Functionare(TipEchipament e) unde e poate fi un echipament care folosete hrtia sau un sistem de calcul. Se cere ca firma s vnd diferite tipuri de echipamente, efectund pentru fiecare verificarea funcionrii astfel:
8

imprimant: tiprirea unei pagini color i a unei pagini alb-negru copiator: setare format A4 sistem de calcul: instalare Windows

Lucrarea 5

Interfee
Cuprins
Interfee Java .......................................................................................................................................................... 1 Declararea unei interfee n Java............................................................................................................................. 1 Utilizarea interfeelor Java...................................................................................................................................... 2 Exemplu de lucru cu interfee Java......................................................................................................................... 3 Asocierea operaiilor cu obiectele .......................................................................................................................... 5 Tem ....................................................................................................................................................................... 5

Un obiect include date i metode (operaii) care permit modificarea datelor. Obiectul execut o operaie atunci cnd primete o cerere (un mesaj) de la un client. Mesajele reprezint singura cale prin care un obiect este determinat s execute o operaie, iar operaiile sunt singurul mod de a modifica datele interne ale obiectului. Din cauza acestor restricii, starea intern a obiectului se spune c este ncapsulat: ea nu poate fi accesat direct, iar reprezentarea ei este invizibil dinspre exteriorul obiectului. Pentru fiecare operaie declarat ntr-un obiect se precizeaz numele, parametrii i valoarea returnat. Aceste elemente formeaz semntura operaiei. Mulimea tuturor semnturilor corespunztoare operaiilor dintr-un obiect, accesibile clienilor, reprezint interfaa obiectului. Aceasta descrie complet setul mesajelor care pot fi trimise spre obiectul respectiv. Un tip este un nume utilizat pentru a referi o anumit interfa. Un obiect este de tipul T dac el accept toate mesajele corespunztoare operaiilor definite n interfaa numit T. Interfeele sunt elemente fundamentale n sistemele OO. Obiectele sunt cunoscute doar prin intermediul interfeelor lor. O interfaa nu d nici un detaliu relativ la implementarea unui obiect, iar obiecte distincte pot implementa diferit o aceeai cerere.

Interfee Java
Interfeele Java reprezint o colecie de metode abstracte (doar semntura, nu i implementarea acestora) i de constante. Utilizarea unei interfee este justificat n cazurile n care se dorete ca o clas s poat fi convertit n mai multe tipuri de baz ntre care nu exist relaie de motenire. Deoarece Java nu suport motenirea multipl, interfeele reprezint o variant de rezolvare a acestei situaii. La fel ca i o clas, nainte de a putea utiliza o interfa ea trebuie declarat. Deoarece conine metode abstracte, interfaa nu poate fi instaniat. Cu toate acestea, o variabil poate fi declarat ca fiind de tipul interfeei.

Declararea unei interfee n Java


O interfa Java este declarat prin cuvntul cheie interface. Structura general a declaraiei unei interfee este:
1

[modificator] interface ta_de_interfete] { lista_de_metode; list_de_constante; }

id_interfata

[extends

lis-

Modificatorul unei interfee poate fi public sau abstract. Avnd n vedere faptul c o interfa este abstract prin definiie, utilizarea modificatorului abstract este redundant. Spre deosebire de o clas, o interfa poate moteni mai multe interfee. Motenirea se face prin clauza extends urmat de lista interfeelor motenite (lista_de _interfete) separate prin virgul. Prin derivare, o interfa motenete de la interfaa de baz att metodele, ct i constantele acesteia. public interface Id1 extends Id2, Id3, Id4 n corpul interfeei pot exista o list de declaraii de metode i o list de declaraii de constante. Metodele declarate ntr-o interfa sunt implicit abstracte. Constantele declarate n interfa primesc atributele static i final. Unui cmp final, dup ce a fost iniializat, nu i se mai poate modifica valoarea. public interface Alergator { int lg_pista=1000; int timp_max=3; public void alearga(); public void accelereaz(); } Metodele dintr-o interfa nu pot fi statice. Referirea constantelor se va face astfel: Alergator.lg_pista; n urma compilrii unui fiier surs Java care conine o interfa, va rezulta un fiier cu extensia .class ataat interfeei respective.

Utilizarea interfeelor Java


Interfeele Java pot fi utilizate n declaraia unei clase, oblignd clasa respectiv s implementeze metodele declarate n interfa. n Java, acest lucru e realizat prin folosirea cuvntului cheie implements: class Sportiv extends Om implements Alergator, Saritor { //implementarea metodelor din interfaa Alergator; //implementarea metodelor din interfaa Saritor; \ } Aa cum se poate observa, o clas poate implementa mai multe interfee. Dac o clas care implementeaz o interfa nu implementeaz toate metodele declarate n interfaa respectiv, va trebui s fie declarat abstract, altfel se va semnala eroare la compilare. Dac o clas A implementeaz o interfa X, un obiect al clasei A este de tip X, el putnd s fie atribuit unui
2

alt obiect de tip X. Numele unei interfee poate fi utilizat la fel ca i numele unui clase, mai puin la instanierea prin operatorul new.

Exemplu de lucru cu interfee Java


Pentru a mai bun nelegere a conceptelor prezentate n lucrare, vom recurge, n continuare, la un exemplu. interface Cititor { public void citeste(); } interface Scriitor { public void scrie(); } interface Profesor extends Cititor, Scriitor { int nota_max=10; int nota_medie=5; int nota_min=1; public void apreciere(); } interface Scufundator { int h=10; public void scufundare(); } class Inotator implements Scufundator { static final int l=50; public void inot() { System.out.println("Inoata " + l + "metri "); } public void scufundare() { System.out.println("Scufunda " + h + "metri "); } } class ProfesorInot extends Inotator implements Profesor { public void citeste() { System.out.println("Citeste PRO Sport "); } public void scrie() { System.out.println("Nu scrie nimic!"); }
3

public void apreciere() { System.out.println("Slab. nota_medie); }

Primeti

nota

"

} class ProfesorJava implements Profesor { public void citeste() { System.out.println("Citeste Thinking in Java "); } public void scrie() { System.out.println("Scrie programe Java"); } public void apreciere() { System.out.println("Excelent. Primeti nota " + nota_max); } } public class TestInterfata { public static void main(String [] args) { ProfesorInot pi = new ProfesorInot(); ProfesorJava pj = new ProfesorJava(); ceCiteste(pi); ceScrie(pi); ceCiteste(pj); ceScrie(pj); cumApreciaza(pi); cumApreciaza(pj); pi.inot(); pi.scufundare(); //corect pj.inot(); pj.scufundare(); //incorect } public static void ceCiteste( Cititor c) { c.citeste(); } public static void ceScrie( Scriitor s) { s.scrie(); } public static void cumApreciaza( Profesor p) { p.apreciere(); } }

Folosind referina de interfa se pot apela metodele enumerate n interfa, iar ceea ce se execut va fi codul corespunztor clasei "implementatoare", la care aparine obiectul concret indicat de referin

Asocierea operaiilor cu obiectele


Cnd o cerere este trimis unui obiect, operaia care se va executa depinde de: cerere; obiectul care recepioneaz cererea. Obiecte diferite, care pot recepiona cereri identice, pot avea implementri diferite ale operaiilor care vor satisface cererile respective. Practic, prin cerere se identific serviciul dorit, iar prin obiectul receptor se alege o anume implementare a acelui serviciu. Asocierea dintre o operaie solicitat i obiectul care va pune la dispoziie implementarea concret a operaiei se numete legare (binding). n funcie de momentul n care ea se realizeaz, legarea poate fi: static: asocierea se realizeaz la compilare; dinamic: asocierea se realizeaz la execuie. n programele Java, majoritatea apelurilor de metode presupun legare dinamic. Asocierea dinamic permite scrierea de programe n care: la emiterea unei cereri s nu ne preocupe ce obiect o va recepiona, stiindu-se c orice obiect a crui interfa include o semntur potrivit va fi bun; obiecte avnd interfee identice pot fi substituite unul altuia, la execuie; aceast substituire se mai numete polimorfism.

Tem
Se cere s se modeleze activitatea unui ghieu bancar. Sistemul este format din urmtoarele entiti, cu atributele corespunztoare: Banca clienti (tablou de elemente de tip Client) codBanca (sir de caractere) ContBancar numarCont (String) suma(float) Client nume (String) adresa (String) conturi (tablou de elemente de tip ContBancar; un client trebuie s aib cel puin un cont, dar nu mai mult de cinci conturi)
5

Conturile bancare pot fi n RON i n EUR. Pentru conturile in RON, dobnda se calculeaz astfel: 0,3 RON pe zi pentru sume sub 500 RON si 0,8 RON pe zi pentru sume mai mari. Conturile in EUR au o dobnd de 0,1 EUR pe zi. Operaiunile posibile asupra unui cont sunt depunere sau extragere a unei sume specificate. Transferuri se pot efectua doar intre conturile in RON. public void transfer(ContBancar contDest, float suma). Toate conturile implementeaz o interfaa Operatiuni care are metodele public float getSumaTotala() la suma existent n cont se adaug dobnda aferent public float getDobanda() public void depunere(ContBancar contDest, float suma) public void extragere (ContBancar contDest, float suma) Toate clasele vor avea constructori i metode de prelucrare i de afiare a datelor membru. De asemenea, se va crea o clas ClientBanca pentru instanierea de obiecte ale celor trei clase specificate.

Lucrarea 6

Tratarea excepiilor
Cuprins
Tratarea clasic a excepiilor ....................................................................................................................1 Mecanismul de emitere-captare a excepiilor ...........................................................................................3 Instruciunea throw ...............................................................................................................................5 Clauza throws .......................................................................................................................................5 Care tipuri de excepie vor aprea ntr-o clauz throws?..................................................................5 Blocurile try-catch ................................................................................................................................6 Secvena finally ..................................................................................................................................10 Excepii predefinite ale limbajului Java .................................................................................................11 Tem .......................................................................................................................................................12

Pe parcursul execuiei unui program pot aprea evenimente deosebite care i modific parcursul normal. Asemenea evenimente pot fi situaii de eroare (de exemplu ncercarea de a accesa un element de tablou aflat dincolo de limitele tabloului) sau pur i simplu situaii care necesit o alt abordare dect cea obinuit (de exemplu, la parcurgerea secvenial a unui fiier de intrare, la un moment dat se atinge sfritul fiierului; acest eveniment, fr a fi o eroare, va schimba cursul de pn atunci al programului). Evenimentele de genul celor amintite se mai numesc excepii. O excepie este un obiect ce aparine clasei predefinite Throwable sau unei clase descendente din Throwable. Clasa Throwable este definit n pachetul java.lang. Limbajul Java dispune, de fapt, de o ntreag ierarhie de clase predefinite pentru reprezentarea excepiilor. Programatorul poate utiliza clasele predefinite sau poate s-i defineasc propriile clase de excepii, cu condiia ca ele s descind direct sau indirect din Throwable. Prin convenie, clasele definite de utilizatori pentru reprezentarea excepiilor vor fi derivate din clasa Exception. Definirea unui tip de excepie de ctre utilizator se justific de obicei prin: necesitatea reinerii de date suplimentare i/sau mesaje mai detaliate despre excepie; necesitatea ca dintr-o clas mai larg de excepii s fie captate doar o anumit parte.

Tratarea clasic a excepiilor


Se va considera un exemplu n care se va face abstracie de faptul c limbajul Java dispune de un mecanism special de tratare a excepiilor: class Tabel{ public static boolean fan_err = false; public static int Increm(int[ ] tab,int indice,int val) {

if(indice>=0 && indice<tab.length){ tab[indice]+=val; fan_err = false; return tab[indice]; } else{ fan_err = true; return 0; } } public static int indexOf(int[ ] tab, int val) { for(int i = 0;i<tab.length;i++) if(tab[i]==val) return i; return -1; } } class ClientTabel{ public static void main(String[ ] arg) { int[ ] tab = new int[Integer.parseInt(arg[0])]; //se completeaza tabloul cu valori int suma=0,indice,val; boolean gata=false; do{ //se citesc de la tastatura un indice si o valoare suma+=Tabel.Increm(tab,indice,val); if(Tabel.fan_err) //a fost eroare... else //merg mai departe if(Tabel.indexOf(tab,val)<0) //valoarea val nu exista in tablou else //valoarea val exista //. . . }while(!gata); } } Metoda Tabel.Increm() se poate confrunta cu situaia n care unul dintre parametrii si (indice) primete o valoare necorespunztoare. n maniera clasic de tratare a erorilor se poate adopta una dintre urmtoarele rezolvri: 1) terminarea forat a programului - nu reprezint o tratare propriu-zis a erorii; de multe ori o eroare nu este chiar att de grav, iar programul poate i trebuie s se descurce cu ea ntr-un mod mai inteligent. 2) returnarea unei valori speciale pe post de eroare. 3) poziionarea unui indicator de eroare i returnarea unei valori valide, care nsa poate implica coruperea strii programului.

4) apelul unei funcii prevzute special pentru cazurile de eroare; aceast funcie, la rndul ei are de ales ntre cele 3 alternative de mai sus. n exemplul prezentat, metoda indexOf() aplic varianta 2), iar metoda Tabel.Increm() varianta 3). Cu privire la varianta 2) trebuie spus ca ea nu este ntotdeauna uor de aplicat. Dac pentru funcia indexOf(), de exemplu, valoarea -1 ca indicator de eec este satisfctoare (este clar c un indice de tablou nu poate fi negativ n mod normal), n schimb, pentru Increm() se pune ntrebarea ce valoare a tipului int ar putea fi considerat ca ieit din comun? Pentru asemenea funcii o soluie ar putea fi aceea c, n loc de int, tipul returnat s fie o structur compus din rezultatul propriu-zis i un fanion de eroare. Pe de alt parte, chiar i acolo unde este aplicabil, opiunea 2) implic necesitatea testrii de fiecare dat a rezultatului execuiei funciilor respective. Acest lucru poate duce uor la dublarea dimensiunii codului. Alternativa 3) prezint dezavantajul c apelantul funciei n care a aprut eroarea poate s nu observe c s-a ntmplat ceva. De exemplu, multe funcii din biblioteca standard C indic apariia unei erori prin setarea variabilei globale errno. Dar prea puine programe ale utilizatorilor testeaz valoarea lui errno suficient de sistematic nct s se obin o tratare consistent a erorilor. n concluzie, metodele clasice de tratare a excepiilor se caracterizeaz prin faptul c poriunile de cod normale se ntreptrund cu cele de tratare a excepiilor, iar claritatea programelor are de suferit din cauza ramificrilor generate de numeroasele teste ale indicatorilor de eroare.

Mecanismul de emitere-captare a excepiilor


Mecanismul de emitere-captare a excepiilor are dou avantaje eseniale: permite separarea explicit a secvenelor de cod care trateaz situaiile normale fa de cele care trateaz excepiile. rezolv dilema de la alternativa 2) de tratare clasic a erorilor, prin aceea c ofer suportul necesar pentru a fora o funcie s returneze, n situaii deosebite, valori ale altui tip dect cel specificat n antet ca tip returnat. Se va ilustra n secvena de mai jos varianta cu excepii a metodei Tabel.Increm() din programul prezentat n paragraful anterior: class IndiceEronat extends Exception{ //clasa pt. reprezentarea exceptiei private int lung; private int index_er; public IndiceEronat(int l,int i) {//constructor lung=l; index_er=i; } public String toString(){ return "Indicele "+index_er+" eronat pentru tabloul de lungime "+ lung; } } class Tabel{

public static int Increm(int[ ] tab,int indice,int val) throws IndiceEronat{ if(indice>=0 && indice<tab.length){ tab[indice]+=val; return tab[indice]; } else //emitere exceptie throw new IndiceEronat(tab.length, indice); } //. . . } class ClientTabel{ public static void main(String[ ] arg) { int[ ] tab = new int[Integer.parseInt(arg[0])]; //se completeaza tabloul cu valori int suma=0,indice,val; boolean gata=false; do{ //se citesc de la tastatura un indice si o valoare try { //secventa sensibila la aparitia unei exceptii suma+=Tabel.Increm(tab,indice,val); } catch(IndiceEronat e){ //tratare exceptie System.out.println("Exceptie IndiceEronat: "+e); } //. . . }while(!gata); } } Ideea fundamental care st la baza mecanismului de emitere-captare a excepiilor este aceea c dac o funcie oarecare F1 se confrunt cu o situaie pe care nu o poate rezolva, ea va anuna acest lucru apelantului ei, prin emiterea unei aa-numite excepii, spernd c apelantul va putea (direct sau indirect) s rezolve problema. O funcie F2 care dorete s rezolve o situaie de excepie trebuie s-si manifeste intenia de a capta excepia emis n situaia respectiv. n exemplul de mai sus funcia F1 este Tabel.Increm(), iar F2 este ClientTabel.main(). Limbajul Java pune la dispoziie trei construcii de baz pentru lucrul cu excepii: instruciunea throw pentru emiterea excepiilor; blocul catch pentru definirea secvenei de captare i tratare a unei excepii; o asemenea secven se mai numete handler de excepii; blocul try pentru delimitarea secvenelor de cod sensibile la apariia excepiilor.

Instruciunea throw Ca sintax, aceast instruciune este asemntoare cu return: throw expresie_exceptie; unde expresie_exceptie are ca rezultat o referin la un obiect al unei clase derivate (direct sau indirect) din Throwable. De obicei, aceast expresie const din aplicarea operatorului new pentru a se crea un obiect al clasei alese pentru reprezentarea excepiei. O instruciune throw poate s apar ntr-o funcie numai dac: ea se gsete n interiorul unui bloc try-catch care capteaz tipul de excepie generat de expresia din throw, sau definiia funciei este nsoit de o clauza throws n care apare tipul de excepie respectiv sau excepia generat aparine claselor RunTimeException sau Error, sau descendenilor acestora Dac instruciunea throw nu apare ntr-un bloc try, efectul ei este asemntor unui return: se creeaz o excepie care va fi pasat spre apelantul funciei respective i se produce revenirea din funcie; funcia ns NU va transmite NICI UN rezultat chiar dac tipul returnat care apare n semntura funciei nu este void. De exemplu, dac funcia Tabel.Increm() execut instruciunea throw, ea nu va returna nici un rezultat de tip int. Clauza throws Aceast clauz se ataeaz la antetul unei funcii i ea precizeaz tipurile de excepii care pot fi generate pe parcursul execuiei funciei respective: tip_returnat nume_functie(lista_param) throws tip_ex1, tip_ex2,...,tip_exn Dac n lista de tipuri din clauza throws exist cel puin dou tipuri, tip_exi i tip_exj, derivate dintr-o superclas comun tip_exsup, limbajul permite nlocuirea celor dou cu tip_exsup. Este adevrat c, pn la urm, toate tipurile din lista throws deriv direct sau indirect din Exception (se poate spune chiar din Throwable sau Object), deci, teoretic clauza poate conine doar aceast clas. n practic este, ns, nerecomandat acest lucru, deoarece, aa cum tip_returnat reprezint tipul rezultatului returnat de funcie la o execuie normal, tot aa, clauza throws ofer informaii referitoare la situaiile de excepie care pot aprea n funcia respectiv. Dac n list se trece doar Exception, informaia este foarte vag pentru cititorul programului. Care tipuri de excepie vor aprea ntr-o clauz throws? Dac utilizatorul i definete propriile tipuri de excepie, atunci este foarte probabil ca el s tie n ce situaii urmeaz s se genereze excepiile respective i, ca urmare, le va prevedea n clauze throws sau n secvene catch. n cazul excepiilor

predefinite ale limbajului Java, acestea trebuie s apar n clauze throws ale funciilor care apeleaz metode din biblioteca Java care, la rndul lor, genereaz excepiile respective. Consultnd documentaia claselor predefinite ale limbajului Java se poate constata c la fiecare metod este indicat, unde e cazul, clauza throws. Pentru excepiile din clasele RunTimeException i Error, precum i din descendenii acestora nu sunt necesare clauze throws (v. exceptiile neverificate). Dac utilizatorul nu prevede clauza throws, i nici secvene try-catch la o funcie de a sa care apeleaz funcii predefinite dotate cu clauze throws, compilatorul va sesiza acest lucru ca eroare. Aceasta este una dintre cile prin care utilizatorul poate afla ce ar fi trebuit s pun n clauza throws a funciei sale. O situaie frecvent, care constituie exemplu n acest sens este cea a utilizrii funciilor de intrare/ieire din pachetul java.io, fr a prevedea clauze throws IOException. Blocurile try-catch Din punct de vedere sintactic un bloc try-catch are forma de mai jos: try { //secventa obisnuita intrerupta //de aparitia unei exceptii } catch(tip_ex1 e){ //tratare exceptie de tipul tip_ex1 } catch(tip_ex2 e){ //tratare exceptie de tipul tip_ex2 } //. . . catch(tip_exn e){ //tratare exceptie de tipul tip_exn } finally { //secventa optionala (despre finally vom vorbi intrun //paragraf urmator) } //. . .(*) de operatii, care poate fi

Un bloc try este urmat de 0, 1 sau mai multe blocuri catch i, opional, de un bloc finally. Vom presupune deocamdat c nu avem bloc finally. Modul de funcionare al unei asemenea construcii este urmtorul: dac n decursul desfurrii secvenei de operaii din blocul try este emis o excepie, fie direct (adic prin execuia unui throw explicit), fie indirect (adic prin apelul unei funcii care emite excepia), secvena se ntrerupe i se baleiaz lista de blocuri catch asociate pentru a-l detecta pe cel corespunztor tipului de excepie emis; dac un astfel de bloc exist, se va lansa n execuie secvena de tratare respectiv. dac aceast secven nu trece printr-un return sau un throw, la terminarea ei execuia programului continu cu linia care urmeaz dup ultimul bloc catch sau, dup caz, finally ataate blocului try curent (cea notata cu (*) n secvena de mai sus). tot de la acea linie se reia execuia i dac secvena try a rulat fr s fi aprut vreo excepie. Cnd spunem bloc catch corespunztor unei excepii, aceasta nseamn c tipul specificat ca parametru dup cuvntul catch este fie identic cu tipul excepiei, fie este o superclas a tipului excepiei. Dac nu exist nici un bloc catch corespunztor excepiei emise, aceasta va fi propulsat spre apelanii funciei curente, cutndu-se un eventual bloc try nconjurtor. Practic, un bloc catch poate fi asimilat cu o funcie anonim, cu un singur parametru specificat imediat dup cuvntul catch. Apelul unei asemenea funcii nu se face explicit, ci automat, dac n secvena try asociat apare o excepie corespunztoare. Un bloc catch se poate termina normal (adic prin epuizarea instruciunilor care l compun) sau printr-un throw care s emit o nou excepie sau tot pe cea primit. n acest din urm caz are loc retransmisia excepiei. Referitor la drumul parcurs de o excepie din momentul emiterii sale i pn ajunge s fie tratat, lucrurile stau astfel: stiva de apeluri se baleiaz dinspre vrf spre baz n cutarea unui bloc try nconjurtor n raport cu instruciunea throw emitent. Un bloc try este nconjurtor fa de o instruciune throw dac aceasta se gsete chiar n interiorul acelui bloc try sau dac se afl ntr-o funcie apelat (direct sau indirect) din acel bloc try. dac se gsete un bloc try nconjurtor, se caut ntre blocurile catch ataate, unul ce corespunde tipului excepiei emise. Dac exist un astfel de bloc catch, acesta preia excepia. n caz contrar, excepia este propagat mai departe, repetndu-se procesul de la pasul anterior. Dac se ajunge cu cutarea pn n funcia main() i nici aici nu exist un handler adecvat, excepia este preluat de handler-ul de excepii al mainii virtuale care determin abandonul programului, cu afiarea unui mesaj corespunztor. dac excepia este preluat de un bloc catch i acesta nu o retransmite, excepia respectiv se consider a fi tratat, indiferent de modul n care

handler-ul opereaz efectiv (n particular un handler poate avea corpul de instruciuni vid). Procesul de baleiere a stivei de apeluri implic, practic, prsirea tuturor funciilor ntlnite n cale pn la gsirea blocului try nconjurtor. Cu ajutorul unui exemplu vom ilustra mecanismul de funcionare a blocurilor try-catch i a instruciunii throw: class oExceptie extends Exception{/*...*/} class altaExceptie extends Exception{/*...*/} class OClasa { public static void oFunctie( ){ //. . . if(eroare) throw new oExceptie( ); } public static void altaFunctie( ){ oFunctie( ); } public static void siIncaUna( ){ //. . . try{ // 1 altaFunctie( ); } catch(oExceptie e){ if(poti_sa_rezolvi) fa_o( ); else throw e; // retransmisia exceptiei e } catch(altaExceptie e){ if(alta_eroare) throw new altaExceptie(); else tratare_normala( ); } try{ // 2 //presupunem ca aici nu se apeleaza oFunctie( ) si nici altaFunctie( ) } catch(Exception e){ //capteaza orice exceptie derivata din Exception aparuta in try 2 } //instructiunea imediat urmatoare blocului trycatch 2 }//end siIncaUna() public static void main(String[ ] arg) { //. . . } //. . . }end class OClasa Presupunem c la un moment dat lanul de apeluri este:

main() -> siIncaUna() -> altaFunctie() -> oFunctie() i c oFunctie() execut instruciunea throw. Se creeaz o excepie de tip oExceptie i se iese din oFunctie(), revenindu-se n altaFunctie(). Aici se constat c nu exist bloc try i, ca urmare, se iese din altaFunctie() ca i cum aici ar exista un throw, i se revine n siIncaUna(). De data aceasta revenirea are loc n interiorul unui bloc try (cel notat cu //1). Se caut printre ramurile sale catch i se gsete cea cu parametru de tip oExceptie. Dac excepia se poate rezolva, dup execuia secvenei catch, programul continu cu blocul try urmtor (cel notat cu //2). Dac se ajunge la retransmisia excepiei, atunci se iese din siIncaUna() i se verific dac apelul la aceast funcie nu se afl n interiorul unui bloc try din apelant (funcia main(), adic). La execuia unui throw din interiorul unui bloc catch nu se rebaleiaz lista curent de handler-e i nici nu se trece la urmtorul bloc try-catch n aceeai funcie, chiar dac acesta din urm ar putea capta excepia emis (cum ar fi cazul blocului try //2 din exemplul de mai sus). Exist i posibilitatea ca un bloc try-catch s fie inclus n secvena try a unui alt bloc try-catch, ca n exemplul urmtor: class oExceptie extends Exception{/*...*/} class altaExceptie extends Exception{/*...*/} class OClasa { public static void oFunctie( ) throws oExceptie{ //. . . if(eroare) throw new oExceptie( ); } public static void altaFunctie( ) throws oExceptie, altaExceptie{ try { //1 try { //2 oFunctie( ); } catch (altaExceptie e){ System.out.println("S-a captat altaExceptie"); //aici tratarea presupune pur si simplu o afisare de mesaj } } catch(oExceptie e){ if(poti_sa_rezolvi) fa_o( ); else throw e; // retransmisia exceptiei e } catch(altaExceptie e){ if(alta_eroare) throw new altaExceptie(); else tratare_normala( ); } } public static void main(String[ ] arg) {

//. . . } //. . . }//end class OClasa n acest exemplu se observ c blocul try-catch notat cu //2 nu va capta excepii de tipul oExceptie. Blocul try-catch notat cu //1 este considerat ca nconjurtor pentru blocul //2 i, deci, excepia emis de oFunctie() va ajunge s fie tratat de handler-ul corespunztor ataat blocului //1. Astfel, un bloc try este nconjurtor pentru un throw dac secvena try conine n mod direct acel throw, sau conine un bloc try-catch n care se afl acel throw, sau conine rdcina apelului la funcia n care se afl acel throw. Secvena finally Dac este prezent, se execut ntotdeauna dup ce s-a terminat secvena try sau secvenele catch din blocul try-catch curent. Practic, nu exist posibilitatea de a determina desfurarea execuiei unui bloc try prevzut cu ramura finally n sensul untrii acestei secvene. Secvena finally reprezint un mecanism prin care se foreaz execuia unei poriuni de cod indiferent dac o excepie a aprut sau nu. De obicei, ramura finally are rolul de a face curat, n sensul de a elibera resurse de genul fiiere deschise accesate prin intermediul unor variabile locale. public boolean Cautare(String nume_fis,String cuvant ) throws IOException{ BufferedReader flux_in = null; try{ flux_in = new (new InputStreamReader(new FileInputStream(nume_fis))); String linie; while ((linie = flux_in.readLine()) != null) if(linie.equals(cuvant)) return true; return false; //nu s-a gasit cuvantul in fisier } finally { if(flux_in != null) flux_in.close(); } } Primul lucru pe care l constatm la programul prezentat este absena ramurii catch. Aceasta nseamn c programatorul nu are intenia s trateze excepiile de tip IOException n cadrul funciei Cautare(). Mai mult, acest program este un exemplu de utilizare a blocului try nu neaprat pentru a rezolva situaii de eroare, ci, datorit blocului finally, mai mult pentru a determina o anumit nlnuire a operaiilor. De altfel, trebuie s ne obinuim cu ideea c o excepie nu este ntotdeauna un indiciu de eroare, ci, uneori, este pur i simplu un semn c n desfurarea programului a intervenit ceva deosebit care trebuie tratat altfel.

10

n secvena de mai sus dac fiierul nu ar fi putut fi deschis (de exemplu nume_fis nu exist) atunci referina flux_in ar fi rmas pe null. De aceea s-a prevzut testul din blocul finally. Presupunnd c deschiderea fiierului ar fi mers cum trebuie, n continuare, indiferent de modul n care se termin blocul try (pe unul dintre cele dou return-uri sau prin apariia unei excepii generate de funcia readLine()) prezena ramurii finally asigur nchiderea fiierului nainte de prsirea efectiv a funciei Cautare(). Ramura finally se mai poate utiliza i pentru a fora execuia unei poriuni de cod cnd n try avem bucle ce conin combinaii de break, continue i return. Excepii predefinite ale limbajului Java Exist dou clase principale de excepii predefinite: Error i Exception. Excepiile reprezentate de clasele descendente din Error indic, n general, probleme foarte serioase care trebuie s duc la ntreruperea programului i nu se recomand captarea lor de ctre utilizator. Ele sunt generate automat de suportul de execuie al programelor Java, la ntlnirea situaiilor de eroare respective. Clasele Error i RunTimeException, mpreun cu descendenii lor, formeaz categoria excepiilor neverificate (unchecked), adic excepii care pot fi generate n programe, fr obligativitatea ca ele s apar n clauze throws. Restul excepiilor sunt verificate (checked), adic la compilare se verific dac exist clauze throws corespunztoare. Un exemplu n acest sens este clasa IOException (definit n pachetul java.io), care trebuie specificat fie n clauze throws, fie n ramuri catch, acolo unde se folosesc funciile de intrare/ieire din pachetul java.io. Programatorul poate s-i defineasc anumite clase de excepii ca descendente din RuntimeException, fcndu-le, astfel, neverificate. Acest lucru trebuie evitat, deoarece lipsa clauzei throws din declaraia unei funcii care totui genereaz excepii, este, de fapt, o lips de informaie pentru potenialii utilizatori ai funciei respective. n exemplul care urmeaz vom arata cum putem folosi mecanismul excepiilor pentru a rezolva problema depirii limitelor unui tablou. Practic, vom completa elementele unui tablou fr ca, la parcurgerea tabloului, s aplicm comparaia clasic a indicelui fa de lungimea tabloului (i < tab.length). Aici ne vom folosi de o clas de excepii predefinit, IndexOutOfBoundsException, din pachetul java.lang: import java.io.*; public class Tablou { public static void main (String[ ] args) { int [ ] tab = new int[10]; int i; try { for(i=0;;) tab[i++]=i; //parcurg tabloul fara grija } catch(IndexOutOfBoundsException e) { //ajung aici cand i >=tab.length System.out.println(e);
11

System.out.println("Gata tabloul!"); } } } Se observ c aici nu avem instruciune throw, adic nu avem o emitere explicit a excepiei. Aceasta, deoarece n anumite situaii, cum este i depirea limitelor tablourilor, excepiile sunt generate automat, de ctre suportul de execuie Java. Excepia IndexOfBoundException este derivat din RuntimeException, deci este neverificat. Aceasta nseamn c i dac n programul de mai sus nu am fi prevzut ramura catch pentru ea, nu am fi fost obligai s atam funciei main() o clauz throws IndexOutOfBoundsException.

Tem
S se scrie un program care citete de la tastatur perechi de numere n care primul numr trebuie s fie mai mic dect al doilea. Dac aceast condiie nu este ndeplinit, folosind mecanismul excepiilor, se va semnala eroare i se va trata aceast eroare prin cererea unei alte perechi de numere. Toate perechile de numere care ndeplinesc condiia vor fi scrise ntr-un fiier primit ca parametru la apel.

12

Lucrarea 7

Colecii de obiecte

Cuprins
Interfaa Collection ......................................................................................................................................... 1 Interfaa List ................................................................................................................................................... 3 Interfaa Set..................................................................................................................................................... 3 Iteratori ........................................................................................................................................................... 4 Comparatori.................................................................................................................................................... 5 Tem ............................................................................................................................................................... 6

De regul, un program Java va crea ntotdeauna obiecte noi, pe baza unor criterii care sunt cunoscute abia n momentul rulrii. Din acest motiv, este necesar posibilitatea de a crea oricte obiecte, oricnd i oriunde. Pentru a rezolva aceast problem, Java pune la dispoziia programatorilor cteva metode de a reine obiectele (sau, mai degrab, referinele la obiecte). Tipul predefinit n acest scop este tabloul, pe lng care se mai gsete un set complet de clase container care pun la dispoziia programatorului metode sofisticate de a reine i chiar de a manipula obiectele. Biblioteca de containere Java reine obiectele programatorului i le divide n dou concepte distincte: a) colecie (Collection) un grup de elemente individuale, asupra crora pot fi aplicate anumite reguli. O list (List) conine elemente ntr-o anumit secven, iar o mulime (Set) nu poate avea duplicate ale elementelor. b) hart (Map) un grup de perechi de obiecte cheie-valoare. La o prim privire, s-ar prea c o astfel de hart este o colecie de perechi, dar cnd se ncearc implementarea ntr-o asemenea manier designul devine dificil. Pe de alt parte, este mai convenabil s se poat lucra cu poriuni ale unei hri, prin crearea unei colecii care s lucreze cu acea poriune. Astfel, o hart poate returna o mulime a cheilor sale, o colecie a valorilor sale, sau o mulime a perechilor sale. n continuare, vor fi prezentate metodele componente ale interfeelor Collection, List, Set (care motenesc interfaa Collection). Toate acestea se afl n pachetul predefinit din Java numit Java.util.

Interfaa Collection
Prezint urmtoarele metode: add(element: Object): boolean; addAll(collection: Collection): boolean;

clear(): void; contains(element: Object): boolean; containsAll(collection: Collection): boolean; equals(object: Object): boolean; hashCode(): int; iterator(): Iterator; remove(object: Object): boolean; removeAll(collection: Collection): boolean; retainAll(collection: Collection): boolean; size(): int; toArray(): Object[]; toArray(array: Object[]): Object[]; Semantica metodelor este urmtoarea: metodele add() i addAll() adaug la colecie obiectul dat ca parametru (colecia dat ca parametru); rezultatul returnat este true dac adugarea a avut loc, colecia original modificndu-i coninutul n acest caz; metoda clear() golete colecia; metodele contains() i containsAll() verific dac n colecie se gsete obiectul dat ca parametru (colecia dat ca parametru); pentru a verifica existena unui element, la comparare se aplic automat metoda equals() specific fiecrui obiect al coleciei; metoda equals() verific echivalena a dou colecii, astfel: dou liste sunt echivalente dac au aceeai lungime i conin aceleai elemente, n aceeai ordine, iar dou mulimi sunt echivalente dac au aceleai elemente, indiferent de ordinea n care ele sunt plasate fizic n colecii; metodele remove() i removeAll() elimin din colecie obiectul dat ca parametru (colecia dat ca parametru), dac acestea exist n colecia pentru care se execut metodele; verificarea existenei unui element se face folosind metoda equals(); rezultatul returnat este true dac eliminarea a avut loc, colecia iniial modificndu-i coninutul; metoda retainAll() elimin din colecie toate elementele, exceptnd elementele care se regsesc i n colecia dat ca parametru; rezultatul returnat este true dac eliminarea a avut loc, colecia iniial modificndu-i coninutul; metoda toArray() creaz i returneaz un tablou care va conine elementele coleciei primit ca parametru; metoda toArray(Object[]) va plasa elementele coleciei n tabloul dat ca parametru dac tabloul are dimensiunea acoperitoare fa de mrimea coleciei i dac tipul concret al elementelor tabloului sunt supertip pentru toate elementele coleciei; dac a doua condiie nu este ndeplinit, va fi emis o excepie de tip ArrayStoreException, iar dac doar prima condiie nu este ndeplinit, metoda creeaz un nou tablou n care plaseaz
2

elementele coleciei; metoda va returna o referin la tabloul n care au fost depuse elementele coleciei; metoda iterator() creeaz i returneaz un obiect Iterator, ataat coleciei respective; metoda hashCode() returneaz codul hashing asociat coleciei curente; codurile hashing asociate obiectelor sunt utilizate atunci cnd obiectele respective constituie elemente ale unor tabele hashing. Nu exist clase concrete care s implementeze interfaa Collection, ci doar clase care implementeaz interfeele List i Set.

Interfaa List
Pe lng metodele motenite de la interfaa Collection, interfaa List prezint i urmtoarele metode proprii: add(index: int, element: Object): boolean; addAll(index: int, collection: Collection): boolean; get(index: int): Object; indexOf(element: Object): int; lastIndexOf(element: Object): int; listIterator(startIndex: int): ListIterator; listIterator(): ListIterator; remove(index: int): Object; set(index: int, element: Object): Object; subList(fromIndex: int, toIndex: int): List; Clasele predefinite care implementeaz interfaa List sunt ArrayList i LinkedList. Clasa ArrayList este indicat atunci cnd se dorete acces aleator pentru consultarea elementelor listei i nu este necesar ca adugrile/eliminrile s se fac la indice specificat. Clasa LinkedList este mai potrivit atunci cnd operaiile cele mai frecvente sunt adugri/eliminri n/din list. Consultrile presupun parcurgeri secveniale ale listei. Clasa LinkedList are cteva metode specifice (metode care permit prelucrarea elementelor aflate la cele capetele ale listei), care permit modelarea cu uurin a unor structuri de date precum stiva sau coada: addFirst(element: Object): void ; addLast(element: Object): void; getFirst(): Object; getLast(): Object ; removeFirst(): Object; removeLast(): Object;

Interfaa Set
Este implementat de clasa HashSet i este extins de interfaa SortedSet care, la rndul ei este implementat de clasa TreeSet. SortedSet este un caz particular de

mulime, asupra elementelor fiind definit o relaie de ordine total. Pe lng declaraiile din Set, SortedSet mai conine: comparator(): Comparator; first(): Object; headSet(toElement: Object): SortedSet; last(): Object; subSet(fromElement: Object, toElement: Object): SortedSet; tailSet(fromElement: Object): SortedSet; Metoda comparator() returneaz o referin la obiectul utilizat pe post de comparator n mulimea curent. Rolul comparatorului const n a indica relaia de ordine n care se afl dou elemente ale mulimii, n vederea plasrii lor n mulimea ordonat. Elementele unei colecii de tip SortedSet trebuie s implementeze interfaa Comparable sau trebuie s poat fi comparate ntre ele cu ajutorul unui obiect care implementeaz interfaa Comparator. Obiectele elemente ale unei structuri de tip Set trebuie s aib implementat metoda hashCode(). Pentru operaii obinuite cu mulimi se va folosi clasa HashSet, care este mai eficient. Clasa TreeSet se utilizeaz atunci cnd se dorete extragerea elementelor ntr-o anumit ordine. Pentru exemplificare, se prezint un program care construiete o mulime de iruri folosind clasa HashSet, apoi afieaz elementele mulimii n ordine alfabetic, folosind o structur TreeSet. class Multime{ public static void main(String []arg){ Set oMultime = new HashSet(); oMultime.add("Popescu"); oMultime.add("Ionescu"); oMultime.add("Georgescu"); oMultime.add("Ixulescu"); System.out.println(oMultime); Set multimeOrdonata = new TreeSet(oMultime); System.out.println(multimeOrdonata); } }

Iteratori
Un iterator este un obiect care poate realiza traversarea ntr-o anumit ordine a coleciei la care este ataat. n Java exist o interfa predefinit, Iterator, care conine urmtoarele declaraii: interface Iterator { boolean hasNext(); Object next(); remove();

} n cazul claselor concrete predefinite, iteratorul ataat poate parcurge colecia respectiv n ordinea n care elementele au fost adugate, respectiv n ordinea indus de relaia de ordine existent n cazul unei colecii de tip SortedSet. n cazul listelor, pe lng iteratorii obinuii exist i iteratori de tip ListIterator (un subtip al lui Iterator). Interfaa ListIterator aduce, n plus, urmtoarele metode: interface ListIterator extends Iterator { void add(Object element); boolean hasPrevious(); int nextIndex(); Object previous(); int previousIndex(); void set(Object element); } Iteratorii de tip ListIterator pot s parcurg o list n ambele sensuri.

Comparatori
n cazul coleciilor sortate (SortedSet sau SortedMap) , atunci cnd se adaug elemente, locul lor este stabilit cu ajutorul metodei compareTo() pe care trebuie s o posede obiectul de adugat. La adugare se testeaz dac obiectul este de tip Comparable, dup care se apeleaz metoda compareTo(). Dac obiectul nu este de tip Comparable, se va genera o excepie ClassCastException. Relaia de ordine stabilit cu ajutorul metodei compareTo() se numete ordonare natural a clasei care conine aceast metod, iar metodei i se spune metoda de comparare natural. Se spune c ordonarea natural a unei clase este consistent n raport cu equals() dac i numai dac expresia ob1.compareTo((Object)ob2)==0 are aceeai valoare boolean cu expresia ob1.equals((Object)ob2) oricare ar fi obiectele ob1 i ob2 ale clasei implicate. Pentru clase definite de programator este obligatorie definirea metodei compareTo() i equals(). Se recomand ca definiiile celor dou metode s se fac astfel nct s se asigure consistena ordonrii naturale fa de equals(). n continuare, este prezentat un exemplu de clas definit de programator, care implementeaz interfaa Comparable, respectnd recomandrile enunate mai sus: import java.util.*; class Coordonate implements Comparable { private double x; private double y; public Coordonate(double x, double y) { this.x=x; this.y=y; } public double distToO( ) {
5

//calculeaza distanta fata de origine return Math.sqrt(x*x+y*y); } public boolean equals (Coordonate c) { return (x==c.x && y==c.y); } public int compareTo(Object c) { if(!(c instanceof Coordonate)) //nu pot stabili o relatie de obiecte de clase diferite throw new ClassCastException( ); Coordonate rc=(Coordonate)c; if (equals(rc)) return 0; double d1=distToO(); double d2=rc.distToO() if(d1>d2) return 1; else if(d1<d2) return -1; // cazul d1 == d if(x>rc.x) return 1; else if(x<rc.x) return -1; // cazul d1 == d2 si x == rc. if(y>rc.y) return 1; return -1; } public String toString() { return "("+x+","+y+")"; } //alte metode } class Client { public static void main(String[] args) { Coordonate c1=new Coordonate(1,2); Coordonate c2=new Coordonate(3,2); Coordonate c3=new Coordonate(2,4); SortedSet s=new TreeSet(); s.add(c1); s.add(c2); s.add(c3); System.out.println(s); } }

ordine

intre

Tem
Se cere s se scrie un program care s in evidena studenilor dintr-un an de studiu. Fiecare student este nregistrat cu prenume, nume, medie i grupa din care face parte. Citirea studenilor se face de la tastatur sau dintr-un fiier i se vor crea obiecte care vor

fi incluse ntr-o colecie. Se cere s se listeze alfabetic studenii dintr-o anumit grup, precum i studenii cu media cea mai mic i cu media cea mai mare din grupa respectiv. De asemenea, se cere s se listeze studenii cu media mai mare dect o valoare citit de la tastatur. Afiarea se va face astfel: Prenume NUME Media Grupa Ion POPESCU 9.50 4

Lucrarea 8

Pachete

Cuprins
Organizarea programelor Java n pachete ..................................................................................................................... 1 Numele pachetelor i ale claselor dintr-un pachet ........................................................................................................ 1 Accesul la clasele unui pachet ...................................................................................................................................... 2 Accesul la membrii claselor din pachete diferite .......................................................................................................... 3 Gestionarea fiierelor surs ale programelor structurate n pachete.............................................................................. 4 Tem ............................................................................................................................................................................. 6

Organizarea programelor Java n pachete


Necesitatea structurrii n pachete apare n condiiile creterii nivelului de complexitate al aplicaiilor scrise n Java. Practic, pachetele reprezint materializarea la nivelul limbajului Java a unuia dintre conceptele fundamentale ale tehnologiei OO: modularizarea. n cele ce urmeaz se vor prezenta construciile de limbaj care permit modularizarea programelor Java. Un program Java este o colecie de clase i interfee grupate opional n pachete (packages). Clasele care formeaz un pachet, la rndul lor, pot fi repartizate ntr-unul sau mai multe fiiere surs. Faptul c o clas NumeClasa aparine unui pachet cu numele numePachet se specific printr-o clauz package, sub forma: package numePacket; plasat pe PRIMA linie a fiierului surs n care se afl clasa NumeClasa. Dac pachetul numePachet este format din mai multe clase repartizate n mai multe fiiere surs, toate acele fiiere trebuie s conin pe prima linie o clauz package ca cea de mai sus. Deoarece clauza package nu poate figura dect pe prima linie a unui fiier surs, este clar c n acelai fiier NU pot aprea clase care s aparin unor pachete diferite. n cazul unui program nestructurat pe pachete (fr clauze package), clasele componente sunt considerate ca aparinnd implicit unui singur pachet anonim (unnamed). Acesta este modul n care au fost scrise toate programele Java de pn acum. n general, vor fi grupate mai multe clase ntr-un pachet dac acele clase modeleaz pri ale aceluiai concept (de exemplu, pachetul java.io conine clasele cu ajutorul crora se modeleaz operaiile de intrare/ieire, iar pachetul java.awt conine clasele cu care se pot reprezenta componente ale unei interfee grafice).

Numele pachetelor i ale claselor dintr-un pachet


ntruct pn acum am scris doar programe "nempachetate", referirea numelui unei clase s-a fcut pur i simplu folosind identificatorul respectiv, ales de programator. De fapt, numele complet al unei clase n Java este format astfel: numePacket.numeClasa
1

n majoritatea situaiilor se poate evita folosirea numelui complet al unei clase. n ceea ce privete numele unui pachet, acesta poate fi un identificator obinuit sau o secven de mai muli identificatori, separai ntre ei prin caracterul punct: PachetulNostru pachetulNostru.pachetulMeu pachetulNostru.pachetulMeu.pachetelulMeu. Cnd numele unui pachet este de forma nume1.nume2 i exist un alt pachet numit nume1, spunem c nume1.nume2 este un subpachet al lui nume1. Spre deosebire de relaia clas subclas, relaia pachetsubpachet este doar la nivel de nume, ea NU implic nici un fel de drepturi speciale ale subpachetului asupra pachetului.

Accesul la clasele unui pachet


Pentru ca o clas OClasa dintr-un pachet unPachet s fie accesibil ntr-un alt pachet altPachet, este necesar ca definiia clasei OClasa s fie precedat de modificatorul de acces public. Pentru clasele pe care le-am scris pn acum nu ne-a interesat modul de acces deoarece ele oricum nu erau destinate a fi utilizate de alte pachete. Presupunnd c OClasa este cu acces public, atunci n cadrul pachetului altPachet ea va putea fi referit utiliznd numele ei complet, adic: unPachet.OClasa Putem s referim OClasa i pe numele ei, cu condiia ca n fiierul surs n care apare referina, s existe clauza import: import unPachet.OClasa; Clauza import se plaseaz naintea definiiilor de clase, dar dup o eventual clauz package: // un fiier surs package unPachet; public class OClasa { //. . . } ----------------------------------------// alt fiier surs package altPachet; import unPachet.OClasa; class AltaClasa{ //. . . public static void oMetoda() { OClasa r = new OClasa(); //. . . } } // Obs: se presupune c n pachetul altPachet nu exist o clasa cu numele OClasa; n caz contrar este necesara denumirea lunga unPachet.OClasa pentru a rezolva conflictul.
2

Clauza import poate avea i forma: import unPachet.*; n acest caz, n fiierul care conine clauza vor putea fi referite direct toate clasele publice ale pachetului unPachet. Clasele dintr-un pachet care nu sunt publice nu vor putea fi utilizate n afara pachetului; ele nu pot face obiectul clauzelor import, nefiind subnelese nici de varianta cu * a clauzei. Trebuie spus c TOATE clasele predefinite ale mediului Java sunt grupate n pachete. Pentru multe dintre aceste clase, ns, nu ne-am pus problema s aflm din ce pachet fac parte, ci le-am folosit pur i simplu. Majoritatea claselor predefinite folosite pn acum i care constituie clase fundamentale pentru un program Java fac parte dintr-un pachet numit java.lang. Acest pachet este SINGURUL pentru care nu este necesar clauza import, respectiv utilizarea denumirii lungi a claselor. Clasele String, Math, clasele nfurtoare sunt exemple de clase din pachetul java.lang. Putem considera c un pachet, la fel ca i o clas, are o interfa i o implementare. Interfaa este constituit din clasele publice, accesibile altor pachete (clienii), iar implementarea din restul claselor care nu sunt accesibile n afara pachetului.

Accesul la membrii claselor din pachete diferite


Acest aspect va fi ilustrat cu ajutorul unui exemplu (fcnd abstracie de repartizarea fizic a claselor pe fiiere surs): package pachet1; public class Clasa11 { private int Vpriv; int Vpack; // cnd modificatorul de acces lipsete, spunem c membrul respectiv este cu acces de tip package protected int Vprot; public int Vpub; // . . . } class Clasa12 { Clasa11 ob; public Clasa12(){ ob = new Clasa11(); ob.Vpriv = 1; // eroare ob.Vpack = 2; // acceptabil ob.Vprot = 3; // idem ob.Vpub = 4; // idem } // . . . } //-----alt fisier sursa --------package pachet2; import pachet1.*; class Clasa21{ Clasa11 ob; public Clasa21() { ob = new Clasa11(); ob.Vpriv = 1; // eroare
3

ob.Vpack = 2; // idem ob.Vprot = 3; // idem ob.Vpub = 4; // acceptabil } // . . . } class Clasa22 extends Clasa11 { public Clasa22() { Vpriv = 1; // eroare Vpack = 2; // idem Vprot = 3; // acceptabil Vpub = 4; // idem } //. . . } n concluzie, regulile care guverneaz drepturile de acces ale unei clase C2 la membrii altei clase C1 sunt: dac C1 i C2 fac parte din acelai pachet, atunci clasa C2 are acces la toi membrii lui C1, mai puin la cei privai. dac C1 i C2 se afl n pachete distincte, atunci clasa C2 poate accesa doar membrii publici ai lui C1. dac C1 i C2 se afl n pachete distincte, iar C2 motenete clasa C1, atunci C2 poate accesa membrii publici i protejai motenii de la C1. Aici se impune o observaie cu privire la subpachete (pachete care au ca nume o secven de identificatori separai prin punct), i anume: din punct de vedere al regulilor de vizibilitate un pachet i un subpachet al su se comport ca i cum ar fi dou pachete distincte absolut oarecare. De altfel, exceptnd numele, cele dou pachete pot s nu aib nimic n comun. Mai mult: un subpachet poate exista i de unul singur, n sensul c se poate denumi un pachet sub forma nume1.nume2 fr a fi necesar s existe un alt pachet nume1. Posibilitatea ca numele unui pachet s fie o concatenare de identificatori a decurs mai degrab din necesitatea evitrii unor conflicte de nume atunci cnd la elaborarea unor aplicaii particip mai muli proiectani, posibil situai departe unul de altul din punct de vedere geografic. Atunci cnd totui exist un pachet cu mai multe subpachete, se obinuiete ca subpachetele s conin clase care modeleaz cazuri particulare sau aspecte diferite ale aceluiai concept modelat de pachet n ansamblu. De exemplu, pachetul java.awt conine clase pentru modelarea componentelor grafice ale unei interfee utilizator, iar java.awt.event conine clasele necesare pentru partea de tratare a evenimentelor generate de componentele grafice.

Gestionarea fiierelor surs ale programelor structurate n pachete


Considerm urmtoarea configuraie de pachete: // un pachet package pachet1; public class Clasa11 { // . . .
4

} class Clasa12 { // . . . } class Clasa13 { // . . . } // alt pachet package pachet2; class Clasa21{ // . . . } public class Clasa22 { // . . . } Se pune problema cum repartizm aceste clase n fiiere surs. n primul rnd, va trebui s inem cont de o regul: clasele publice dintr-un pachet trebuie s constituie fiecare cte un fiier surs SEPARAT, numele fiecrui fiier fiind format din numele clasei urmat de extensia .java. Aceast regul nu se aplic n cazul pachetului anonim. Clasele nepublice ale unui pachet pot s se afle toate ntr-un singur fiier surs, numele acestuia nefiind impus. Astfel, pentru exemplul de mai sus ar fi necesare urmtoarele fiiere surs: Clasa11.java pentru clasa pachet1.Clasa11 un fiier cu un nume la alegere, pentru clasele pachet1.Clasa12 i pachet1.Clasa13 Clasa22.java pentru clasa pachet2.Clasa22 un fiier cu un nume la alegere pentru clasa pachet2.Clasa21 n ceea ce privete fiierele .class rezultate din compilare, regula este: aceste fiiere vor fi plasate n directoare ale cror nume coincid cu numele pachetelor din care fac parte clasele respective. Pentru exemplul de mai sus acest lucru ar nsemna c n directorul de lucru s se creeze 2 subdirectoare cu numele pachet1 i pachet2 n care s se depun fiierele .class dup cum urmeaz: Clasa11.class, Clasa12.class i Clasa13.class n directorul pachet1 Clasa21.class i Clasa22.class n directorul pachet2 n cazul n care numele unui pachet ar fi de forma pac1.pac2, atunci pentru fiierele .class corespunztoare trebuie creat lanul de subdirectoare pac1/pac2. La prima vedere am putea concluziona c lucrul cu pachetele ne-ar complica teribil viata: pe de o parte ar trebui s dam mai multe comenzi de compilare, deoarece avem mai multe fiiere surs, iar pe de alt parte ar trebui s mai i gestionm o mulime de directoare i subdirectoare. n realitate, comenzile de compilare i lansare n execuie dispun de parametri care ne uureaz foarte mult munca n contextul pachetelor. Astfel, admind c toate fiierele surs care compun o aplicaie se afl in acelai director i c acest director este chiar directorul curent de lucru, atunci comanda de compilare va fi:
5

javac -d nume_director *.java Parametrul -d nume_director ne spune c structura de subdirectoare n care vor fi depuse fiierele .class se va crea ncepnd din directorul nume_director. Dac dorim ca structura de subdirectoare s fie creat ncepnd cu directorul curent de lucru, atunci comanda va fi: javac -d . *.java Aceast structur va fi creat automat, folosindu-se numele pachetelor. La execuie, dac suntem pozitionai n directorul n care s-a creat arborele de directoare cu fiiere .class, vom da comanda: java nume_pachet.nume_clasa_radacina Cu alte cuvinte, la execuie se indic numele complet al clasei.

Tem
S se scrie un program care gestioneaz o bibliotec universitar i care are urmtorul meniu: adugarea, tergerea, cutarea unui client, afiarea tuturor clienilor adugarea, tergerea unei cri, listarea crilor unui anumit autor mprumutarea i restituirea unei cri, listarea crilor mprumutate de un anumit client n acest scop, se vor implementa trei subsisteme (fiecare n cte un pachet): un subsistem pentru evidena clienilor (care pot fi studeni, cadre didactice sau externi). Pentru fiecare client intereseaz prenume, nume, vrsta, adres, facultatea i anul de studiu (student), facultatea i gradul didactic (cadru didactic), locul de munc i funcia (extern); un subsistem pentru evidena crilor din bibliotec. Intereseaz titlul, autorul, anul apariiei, editura, numrul de inventar; un subsistem care s le integreze pe celelalte dou subsisteme i s in evidena mprumuturilor i a restituirilor.

Lucrarea 9

Interfee grafice

Cuprins
Crearea ferestrei unei aplicaii....................................................................................................................................2 Tratarea evenimentelor n Java...................................................................................................................................2 Crearea unor butoane ntr-o fereastr .........................................................................................................................4 Introducerea textului de la tastatur ...........................................................................................................................6 Tem ...........................................................................................................................................................................9

n cazul programelor pe care le-am fcut pn acum, toate mesajele i rspunsurile apreau ca linii de text succesive, care defilau pe ecran (ecranul era folosit n mod text). Un astfel de stil de comunicare nu este atractiv pentru utilizatori, motiv pentru care se prefer dialogul prin interfee grafice sau GUI (Graphical User Interface) (ecranul este folosit n mod grafic). Componentele unei interfee grafice se numesc elemente de control (widgets) i pot fi activate de ctre utilizator cu ajutorul mausului sau al tastaturii. Activarea unui element de control determin un anumit rspuns din partea programului, rspuns concretizat prin execuia unei anumite operaii. Cele mai rspndite interfee grafice sunt cele bazate pe ferestre, adic fiecrei aplicaii active la un moment dat i se aloc o zon dreptunghiular pe ecran, numit fereastr. n interiorul ferestrei respective, se vor gsi toate elementele de control necesare dialogului utilizator-aplicaie, precum i informaiile afiate de aplicaie. Pentru ca o aplicaie care folosete interfaa grafic s poat funciona, este necesar existena unei platforme sau server grafic care s determine intrarea monitorului n regim grafic i care s ofere funciile primitive necesare, ntre altele, pentru desenarea componentelor GUI i pentru receptarea semnalelor generate de maus i tastatur. n sprijinul programatorilor au fost create suporturi software care permit ca o aplicaie grafic s poat fi construit din `pieseA standardizate, gata create. De regul, un suport software pune la dispoziia programatorului urmtoarele elemente: C o bibliotec de elemente de control (bare de defilare, chenare, butoane etc.). Aceast bibliotec elibereaz programatorul de sarcina de a desena i colora de fiecare dat toate elementele GUI ale unei aplicaii; C un manager de ferestre: acesta este cel care impune cum `aratA elementele GUI, dicteaz modul n care este asamblat i echipat o fereastr, precum i modul n care va fi manipulat. Mediul Windows are ncorporat un astfel de manager de ferestre, care definete aspectul general al unei ferestre Windows. Mediul Linux este mai configurabil din acest punct de vedere, permind utilizatorului o gam mai variat de manageri de ferestre: GNOME, KDE, FVWM, etc.; C o bibliotec de funcii sau clase predefinite. Aceast bibliotec permite construirea interfeei grafice, pornind de la fereastra aplicaiei, conform standardului impus de managerul de ferestre instalat pe maina pe care ruleaz aplicaia. Limbajul Java
1

ofer pachetul predefinit java.awt (Abstract Window Toolkit), mpreun cu o serie de subpachete ale acestuia, care ncorporeaz clasele necesare pentru a construi aplicaii grafice.

Crearea ferestrei unei aplicaii


O aplicaie grafic are o fereastr principal (frame) i una sau mai multe ferestre subordonate celei principale. Clasa java.awt.Frame este cea care conine elementele de baz necesare construirii ferestrei principale. n secvena de mai jos se creeaz i se afieaz o fereastr principal amplasat n mijlocul ecranului i a crei suprafa este 1/4 din suprafaa ecranului.

import java.awt.*; class FereastraMea extends Frame { public FereastraMea(String titlu) { super(titlu); Toolkit t = Toolkit.getDefaultToolkit(); Dimension d = t.getScreenSize(); int h = d.height; int w = d.width; setSize(w/2, h/2); setLocation(w/4, h/4); } } class PrimaFereastra { public static void main(String[] args) { Frame f = new FereastraMea("Prima fereastra"); f.show(); } } Orice acionare de ctre utilizator a unui element GUI prin intermediul mauslui sau al tastaturii determin apariia unui eveniment, iar elementul GUI acionat este sursa evenimentului. Pentru c acionarea unui element GUI s se soldeze cu un anumit rspuns, programatorul trebuie s rezolve partea de tratare a evenimentului. Aceasta nseamn c cineva trebuie s recepioneze evenimentul i apoi s execute o anumit funcie.

Tratarea evenimentelor n Java


La acionarea unui element GUI se creeaz un obiect al unei clase predefinite din pachetul java.awt.Event. Dac exist un obiect receptor (listener) al evenimentului creat i acest obiect este cunoscut de ctre sursa evenimentului, atunci se ncearc apelarea unei metode a receptorului creia i se transmite ca parametru obiectul eveniment. Dac programatorul a definit acea metod, execuia ei reprezint tratarea evenimentului.

Obiectele eveniment n momentul acionrii, fiecare element GUI determin un eveniment specific. De exemplu, diversele aciuni asupra unei ferestre conduc la crearea unui obiect al clasei java.awt.event.WindowEvent. Apsarea unui buton duce la crearea unui obiect al clasei java.awt.event.ActionEvent. Receptorul evenimentului Receptorul de eveniment este un obiect care are metode cu nume prestabilite, ale cror definiii se gsesc n interfee cu nume prestabilite. Exist cte o interfa specific fiecrui tip de eveniment. De exemplu, pentru recepionarea evenimentelor din clasa WindowEvent, trebuie implementat interfaa WindowListener. Metodele din interfeele EvenimentListener au ca parametru o referin la evenimentul corespunztor. De exemplu, metodele din interfaa WindowListener au ca parametru o referin la WindowEvent. Metodele din clasa ActionListener au ca parametru o referin la ActionEvent. Numele metodelor reflect aciunea executat de utilizator asupra sursei evenimentului. Spre exemplu, interfaa WindowListener conine urmtoarele metode: public void windowActivated(WindowEvent e); // este apelat n momentul n care fereastra devine fereastra activ public void windowClosed(WindowEvent e); // este apelat dup ce fereastra a fost nchis public void windowClosing(WindowEvent e); // este apelat n momentul n care utilizatorul nchide fereastra public void windowDeactivated(WindowEvent e); // este apelat n momentul n care fereastra nu mai este activ public void windowDeiconified(WindowEvent e); // este apelat n momentul n care fereastra minimizat este restaurat public void windowIconified(WindowEvent e); // este apelat n momentul n care fereastra este minimizat public void windowOpened(WindowEvent e); // este apelat n momentul n care fereastra este deschis pentru prima oar Legarea unui receptor de eveniment de sursa evenimentului se face prin invocarea de ctre obiectul care reprezint sursa evenimentului a unei metode predefinite, care primete drept parametru referina spre obiectul receptor de eveniment. Numele metodei este format din prefixul add la care se adaug numele interfeei corespunztoare evenimentului: obiectSursaEveniment.addEvenimentListener(obiectReceptorEveniment) ; n exemplul prezentat, obiectul clasei FereastraMea este o surs de eveniment, iar receptorul ei este clasa ReceptorEvenimentFereastra. Programul anterior devine: import java.awt.*; import java.awt.event.*;
3

class ReceptorEvenimentFereastra implements WindowListener { public void windowClosing(WindowEvent e) { System.exit(0); } // restul metodelor din WindowListener se vor implementa cu corpul vid } class FereastraMea extends Frame { public FereastraMea(String titlu) { super(titlu); ReceptorEvenimentFereastra r = new ReceptorEvenimentFereastra(); this.addWindowListener(r); Toolkit t = Toolkit.getDefaultToolkit(); Dimension d = t.getScreenSize(); int h = d.height; int w = d.width; setSize(w/2, h/2); setLocation(w/4, h/4); } } class PrimaFereastra { public static void main(String[] args) { Frame f = new FereastraMea("Prima fereastra"); f.show(); } }

Crearea unor butoane ntr-o fereastr


Pentru a aduga butoane, casete de text, etc. ntr-o fereastr, trebuie create obiecte ale claselor care modeleaz acele elemente. Apoi, pentru fiecare element, trebuie apelat metoda add a containerului n care vor fi pstrate elementele. Referina la elementul grafic adugat interfeei grafice va fi parametrul metodei add. Amplasarea elementelor n spaiul containerului este fcut de obiecte speciale (plasatori), care aparin unor clase descendente din LayoutManager. Cele mai cunoscute asemenea clase sunt FlowLayout i BorderLayout. Pentru a folosi plasatorul, obiectul care reprezint containerul (n cazul nostru FereastraMea) va trebui ca nainte de a aduga elemente GUI, s apeleze metoda setLayout dndu-i parametru referina la un obiect de tip FlowLayout. n Java, butoanele sunt modelate de clasa Button. Unul dintre constructorii clasei Button accept drept parametru un String, reprezentnd eticheta nscris pe buton. Un buton poate fi sursa unui eveniment de tip ActionEvent, care apare atunci cnd utilizatorul a apsat acel
4

buton. Receptorul unui eveniment ActionEvent trebuie s implementeze interfaa ActionListener, care declar metoda actionPerformed. Pentru exemplificare, este prezentat programul anterior, actualizat astfel nct n fereastr s apar un buton cu eticheta `Apasa-maA, iar la acionarea lui s se afieze valoarea unui contor care numr de cte ori a fost apsat butonul. Pentru a stpni complexitatea relaiilor dintre elementele GUI ale unei interfee grafice, aplicaiile pot fi structurate conform unui ablon de proiectare numit Mediator. Pentru aplicaia exemplificat, aceasta presupune definirea urmtoarelor clase: C o clas care s reprezinte fereastra (FereastraMea); C o clas care s reprezinte receptorul de evenimente (ReceptorEvenimenteFereastra), cu precizarea c ea se va ocupa i de evenimentele generate de buton; C o clas cu rol de mediator, care creeaz elementele GUI (n cazul de fa un buton i o etichet) i le asambleaz n fereastr, pstrnd referinele la ele i, ntiinat de ctre receptorul de evenimente ori de cte ori intervine o modificare a vreunui element GUI, va actualiza starea tuturor elementelor implicate. import java.awt.*; import java.awt.event.*; class ReceptorEvenimentFereastra extends WindowAdapter implements ActionListener { private Mediator m; public ReceptorEvenimentFereastra(Mediator m) { this.m = m; } public void windowClosing(WindowEvent e) { System.exit(0); } public void actionPerformed(ActionEvent e) { m.buttonPressed(); } } class Mediator { public Mediator(Frame f) { this.f = f; } public void assembleFrame() { ReceptorEvenimentFereastra r = new ReceptorEvenimentFereastra(this); f.addWindowListener(r); b = new Button("Apasa-ma"); b.addActionListener(r);
5

etic = new Label("0"); f.setLayout(new FlowLayout()); f.add(b); f.add(etic); f.show(); } public void buttonPressed() { contor++; etic.setText(""+contor); } private int contor=0; private Frame f; private Button b; private Label etic; } class FereastraMea extends Frame { public FereastraMea(String titlu) { super(titlu); Toolkit t = Toolkit.getDefaultToolkit(); Dimension d = t.getScreenSize(); int h = d.height; int w = d.width; setSize(w/2, h/2); setLocation(w/4, h/4); } } class PrimaFereastra { public static void main(String[] args) { Frame f = new FereastraMea("Prima fereastra"); Mediator m = new Mediator(f); m.assembleFrame(); } }

Introducerea textului de la tastatur


Pentru a putea introduce text de la tastatur ntr-o aplicaie cu interfa grafic, se utilizeaz un element GUI numit cmp de editare, modelat de clasa TextField. Pentru exemplificare, se consider urmtorul program care citete elementele unui tablou. Se utilizeaz: C un cmp de editare unde se introduce cte un element al tabloului, C un buton prin care se comand introducerea efectiv n tablou a textului editat; C o etichet care indic indicele elementului editat. import java.awt.*;
6

import java.awt.event.*; class Tablou { private int[] tab; public Tablou(int n) { tab=new int[n]; } public void setElem(int i, int val) { tab[i]=val; } public int getDim() { return tab.length; } } class ReceptorEvenimentFereastra extendsWindowAdapter implements ActionListener { private Mediator m; public ReceptorEvenimentFereastra(Mediator m) { this.m=m; } public void windowClosing(WindowEvent e) { System.exit(0); } public void actionPerformed(ActionEvent e) { m.buttonPressed(); } } class Mediator { public Mediator(Frame f, Tablou t) { this.f=f; this.t=t; } public void assembleFrame() { ReceptorEvenimentFereastra r = new ReceptorEvenimentFereastra(this); f.addWindowListener(r); b = new Button("Adauga"); b.addActionListener(r); etic = new Label("Element 0"); tx = new TextField(15);
7

f.SetLayout(new FlowLayout()); f.add(b); f.add(tx); f.add(etic); f.show(); } public void buttonPressed() { if (contor < t.getDim()) { try { int val = Integer.parseInt(tx.getText()); t.setElem(contor,val); contor++; } catch (NumberFormatException e) { // daca nu s-a introdus o valoare numerica, nu se actualizeaza tabloul } if (contor >= t.getDim()) etic.setText("Tablou plin!"); else etic.setText("Element "+contor); tx.setText(""); } } private int contor=0; private Tablou t; private Frame f; private Button b; private Label etic; private TextField tx; } class FereastraMea extends Frame { public FereastraMea(String titlu) { super(titlu); Toolkit t = Toolkit.getDefaultToolkit(); Dimension d = t.getScreenSize(); int h = d.height; int w = d.width; setSize(w/2, h/2); setLocation(w/4, h/4); } } class PrimaFereastra { public static void main(String[] args)
8

{ Frame f = new FereastraMea("Prima fereastra"); Tablou t = new Tablou(10); Mediator m = new Mediator(f,t); m.assembleFrame(); } } n cazul n care exist mai multe butoane ntr-o fereastr, se folosesc urmtoarele soluii: C se construiesc dou clase distincte pentru receptorii de evenimente ai celor dou butoane, definind corespunztor metodele actionPerformed; C se utilizeaz informaiile furnizate de obiectul eveniment ActionEvent pasat ca parametru metodei actionPerformed. n acest caz, pentru obiectul eveniment, poate fi apelat metoda: public Object getSource(); care returneaz o referin la obiectul surs de eveniment.

Tem
Se cere s se realizeze un calculator, a crui interfa s fie cea din figura de mai jos. Acesta trebuie s permit introducerea a doi operanzi i efectuarea celor patru operaii de baz: adunare, scdere, nmulire i mprire. Fiecare operaie este rezultat al acionrii cu mausul a butonului corespunztor. Rezultatele vor fi puse ntr-o list, care va avea facilitate de golire (prin acionarea butonului Clear). Cele patru butoane aferente operaiilor vor fi deselectate n cazul n care cei doi operanzi nu au ambii valori valide. n cazul mpririi la zero se va afia un mesaj de eroare.

Lucrarea 10

Fire de execuie

Cuprins
Definiii......................................................................................................................................................................... 1 Construcii de limbaj pentru declararea firelor de execuie .......................................................................................... 2 Sincronizarea firelor de execuie................................................................................................................................... 4 Tem ............................................................................................................................................................................. 8

Definiii
Firele de execuie (threads) reprezint poriuni de cod ale aceluiai program care se pot executa n paralel una fa de alta. Programele de pn acum au fost de tip single-threaded, adic au fost compuse fiecare din cte un singur fir de execuie. Firele de execuie reprezint modalitatea prin care limbajul Java suport programarea concurent. Execuia paralel a firelor depinde practic de suportul de execuie al programelor, hardware i software. n general, dac se dispune de un sistem cu mai multe procesoare firele vor fi distribuite pe acestea. Dac avem un singur procesor execuia paralel se simuleaz folosind diferite tehnici. n principiu se cunosc dou asemenea tehnici care constituie, de fapt, politici de planificare a firelor. Primele trei versiuni ale interpretorului Java (pentru Solaris, Windows 95 i Windows NT) aplic urmtoarea tehnic: un fir de execuie este lsat s ruleze pn cnd se termin sau pn cnd ajunge n stare de ateptare, moment n care este preluat un fir cu prioritate mai mare pregtit pentru execuie. Versiunile de interpretor realizate pentru 32 de bii aplic tehnica numit time-slicing (divizarea timpului). Practic, fiecrui fir i se aloc cte un interval de timp n care poate ocupa procesorul. n felul acesta, fiecare fir ajunge, prin rotaie, s ocupe cte puin procesorul, iar utilizatorul are impresia c firele ruleaz n paralel. Divizarea n fire de execuie este avantajoas mai ales n cazul operaiilor care necesit timp mare de rulare sau care foreaz procesorul s atepte pentru a accesa discul sau reeaua. Constituirea unor asemenea operaii ca fire separate conduce la: creterea vitezei de execuie a programului n ansamblu, deoarece procesorul nu mai pierde vremea ateptnd dup resurse, ci ia la rnd alte fire pn cnd resursele devin disponibile; creterea vitezei de rspuns a programului, deoarece interfaa utilizator poate avea propriul ei fir de execuie care s preia intrrile utilizatorului chiar n timpul ct celelalte fire sunt "ocupate". Desigur c toate aceste avantaje au un pre: ntruct mai multe fire de execuie aparin aceluiai pogram, rezult c ele pot accesa n comun anumite structuri de date ale programului. Aici se pune problema sincronizarii accesului, astfel nct s se asigure consistena datelor, lucru care complic puin proiectarea programului.

Construcii de limbaj pentru declararea firelor de execuie


Pentru ca o secven de program s fie considerat fir de execuie distinct, sunt necesare urmtoarele: construirea unei clase care extinde clasa predefinita Thread; redefinirea n acea clas a metodei run() care are prototipul: public void run(); Codul acestei metode constituie un fir de execuie. Altfel spus, n metoda run() se plaseaz secvena care trebuie rulat n paralel cu alte fire. Se precizeaz c metoda run() definit n clasa Thread nu face nimic. crearea unuia sau a mai multor obiecte ale clasei definite mai nainte i apelarea pentru ele a metodei start() (motenit de la Thread); metoda start() creeaz un nou flux de control (pe baza datelor obiectului) i lanseaz n execuie firul apelnd metoda run() a acestuia. Exemplu considerat creeaz trei fire de execuie cu rol de numrtoare: import java.io.*; class Fir extends Thread { private String nume; public Fir(String nume) {this.nume = nume;} public void run() { // aici pun codul firului de executie for(long i=0; i <= 10000; i++) System.out.println("Firul "+nume+" iteratia "+i); } public static void main(String[] args) { new Fir("Dandanache A.").start(); // se lanseaza executie un fir new Fir("Catavencu N.").start(); // se lanseaza executie al 2-lea fir new Fir("Coana Joitica").start(); // s.a.m.d System.out.println("Aici se termina metoda main."); } }

in in

Cele trei fire execut, de fapt, acelai lucru, ele fiind create prin intermediul unor obiecte ale aceleiai clase, Fir. Programul prezentat are n realitate patru fire de execuie: metoda main() constituie firul principal care va continua s se execute n paralel cu cele trei fire pe care le lanseaz. Pe lng cele patru fire vizibile ale programului mai exist un fir care corespunde Garbage Collector-ului. Dac politica de planificare a firelor nu este cea de time-slicing, atunci este foarte probabil ca cele trei fire din exemplul de mai sus s nu se execute interclasat, ci unul dup altul. Acest lucru se datoreaz faptului c un fir este lsat s ruleze pn fie se termin, fie ajunge n stare de ateptare. Firele din exemplu, nu ajung n stare de ateptare deoarece nu ateapt dup nici o resurs. n acest caz, ca s putem vedea totui o execuie paralel va trebui s form noi
2

intrarea n ateptare. Cel mai simplu mod de a simula ateptarea este acela de a lsa firul s doarm o bucat de timp. Adormirea se realizeaz cu metoda sleep() a clasei Thread, care primete ca parametru numrul de milisecunde reprezentnd durata somnului. Cu acestea, programul de mai sus poate fi rescris astfel: import java.io.*; class Fir extends Thread { private String nume; public Fir(String nume) {this.nume = nume;} public void run() { // aici pun codul firului de executie try { for(long i=0; i <= 10000; i++) { System.out.println("Firul "+nume+" iteratia "+i); sleep(100); // dorm 100 milisec. } } catch (InterruptedException e) { return; } } public static void main(String[] args) { new Fir("Dandanache").start(); // se lanseaza in executie un fir new Fir("Catavencu").start(); // se lanseaza in executie al 2-lea fir new Fir("Coana Joitica").start(); // s.a.m.d System.out.println("Aici se termina metoda main."); } } Introducerea blocului try-catch n metoda run() a fost necesar deoarece, pe de o parte, metoda sleep() are o clauz throws InterruptedException, iar pe de alt parte metoda run() nu are clauza throws, deci suntem nevoii s captm eventuala excepie emis de metoda sleep(). S-a expus mai sus o variant prin care putem defini fire de execuie. Aceast variant ar putea fi dezavantajoas atunci cnd clasa care conine metoda run() ar trebui s extind i o alt clas dect Thread. Deoarece Java nu permite motenire multipl, n asemenea cazuri putem folosi i o alt variant, n care ne bazm pe interfaa predefinit Runnable (pe care clasa Thread o implementeaz de altfel) i pe faptul c unul dintre constructorii clasei Thread accept ca parametru o referin la Runnable. Varianta cu Runnable a programului anterior este urmtoarea: import java.io.*; class Fir implements Runnable { private String nume; public Fir(String nume) {this.nume = nume;}
3

public void run() { // aici pun codul firului de executie for(long i=; i <= 10000; i++) System.out.println("Firul "+nume+" iteratia "+i); } public static void main(String[ ] args) { Fir f1 = new Fir("Dandanache A."); // se creeaza un fir Fir f2 = new Fir("Catavencu N."); // se creaza al 2-lea fir Fir f3 = new Fir("Coana Joitica"); // s.a.m.d new Thread(f1).start(); // se lanseaza in executie un fir new Thread(f2).start(); // se lanseaza in executie al 2lea fir new Thread(f3).start(); // s.a.m.d System.out.println("Aici se termina metoda main()."); } }

Sincronizarea firelor de execuie


n exemplele din paragraful precedent firele de execuie erau secvene absolut independente una de cealalt, n sensul c nu accesau date comune. Un caz tipic n care dou fire de execuie folosesc n comun structuri de date i cnd este necesar sincronizarea lor, este exploatarea n regim productor-consumator a unei zone de date. Presupunem c avem un tablou de numere ntregi, de dimensiune limitat. Unul dintre fire, productorul depune valori n tablou, cte una o dat. Cellalt fir, consumatorul, citete cte o valoare din tablou. Ambele operaii realizeaz parcurgerea circular a tabloului (adic, dup ce s-a accesat ultimul element al tabloului, la urmtorul acces se va prelucra primul element al tabloului). Sincronizarea este necesar, n primul rnd, pentru asigurarea consistenei datelor: productorul s nu ncerce s introduc valori cnd tabloul este plin, iar consumatorul s nu ncerce s citeasc dintr-un tablou gol. De asemenea, foarte important este ca att operaia de introducere a unei valori ct i cea de citire a unei valori din tablou s se desfoare fr ntrerupere sau n regim de excludere mutual. Ce nseamn aceasta? S presupunem c cele dou operaii arat c n secvena de mai jos: // introducerea void Pune(int v) { while(nr_elem == tablou.length) { //daca tabloul este plin, astept } iP = (iP + 1) % tablou.length; // iP va indica urmatoarea pozitie libera tablou[ iP ] = v; nr_elem++; // incrementez numarul de elemente
4

} // extragerea int Scoate() { while (nr_elem == 0) { // daca tabloul este gol, astept } iS = (iS + 1) % tablou.length; // iS indica urmatorul element de citit nr_elem--; return tablou[ iS ]; } S presupunem acum c este n curs de execuie metoda de introducere. Ea apuc s modifice indicele iP i, nainte ca valoarea v s se depun n tablou, expir cuanta de timp a firului productor. n acest moment intr n aciune firul consumator care, n cel mai bun caz va citi o valoare pe care a mai citit-o o dat, dac nu cumva se blocheaz pe motiv c tabloul este gol. Probleme similare pot fi ntlnite i n cazul extragerii. Soluia unor astfel de probleme este aceea de a impune ca pe durata execuiei uneia dintre operaii nici un alt fir s nu poat accesa tabloul. Limbajul Java ofer n acest sens posibilitatea de a declara anumite metode ca fiind de tip synchronized. Fie urmtoarea secven: class Resursa { public synchronized void oMetoda ( ) { ... } // alte metode } Faptul c oMetoda() este declarat ca fiind sincronizat nseamn c pe durata n care un fir execut aceast metod, obiectul receptor implicat intr n starea blocat, astfel nct nici un alt fir nu mai poate executa vreo alt metod sincronizat pentru obiectul respectiv. Dac un fir apeleaz o metod sincronizat pentru un obiect aflat n starea blocat, firul respectiv intr ntr-o coad de ateptare ataat obiectului. Ieirea din blocaj a obiectului se realizeaz la terminarea execuiei metodei sincronizate. Sincronizarea metodelor reprezint mecanismul prin care se asigur accesul firelor la resurse comune n regim de excludere mutual. Dac analizm metodele Pune() i Scoate() de mai sus constatm ca operaiile propriu-zise de scriere/citire se pot efectua doar n anumite condiii. Spre exemplu, nu putem citi dintr-un tablou gol. Ca urmare, dac tabloul este gol, firul consumator trebuie s atepte ca un eventual productor s depun ceva n tablou. Aici intervine problema comunicrii ntre fire. Comunicarea presupune un mecanism prin care un fir care a inut ocupat o resurs s anune celelalte fire n momentul eliberrii resursei. Intrarea n ateptare i anunarea eliberrii unei resurse se realizeaz cu ajutorul metodelor wait(), respectiv notifyAll() pe care toate clasele le motenesc de la Object. Metoda wait() permite firului care execut la un moment dat o metod sincronizat asupra unui obiect s intre n stare de ateptare, obiectul ieind din starea de blocaj. Ca urmare, un alt fir poate aciona asupra obiectului respectiv. Ieirea din starea de ateptare se va face cnd unul dintre
5

firele active apeleaz metoda notifyAll(). Aceasta ntiineaz firele aflate n ateptare c obiectul dorit de ele iese din blocaj. Modul n care se folosesc metodele wait() i notifyAll() este ilustrat n secvena de mai jos, n care sunt rescrise metodele Pune() i Scoate(), mpreuna cu clasa din care ele fac parte: class Resursa { private int[] tablou; private int nr_elem=0; private int iP=0; private int iS=0; public Resursa (int n) { tablou=new int[n]; } // introducerea public synchronized void Pune(int v) { while(nr_elem == tablou.length) { // tablou plin try { wait(); // asteapta } catch (InterruptedException e) { System.out.println(e);}
}

iP = (iP + 1) % tablou.length; // iP va indica urmatoarea pozitie libera tablou[ iP ] = v; nr_elem++; // incrementez numarul de elemente notifyAll(); // anunt firele in asteptare ca am eliberat resursa } // extragerea public synchronized int Scoate() {
while (nr_elem == 0)

{ // tablou gol try { wait(); // asteapta } catch (InterruptedException e) {System.out.println(e);} } iS = (iS + 1) % tablou.length; // iS indica urmatorul element de citit nr_elem--; notifyAll(); // anunt firele in asteptare ca am eliberat resursa return tablou[ iS ]; }
6

} S vedem acum cum scriem firele productor i consumator propriu-zise: class Producator extends Thread { private Resursa r; private int nr_valori; public Producator (Resursa r, int n) { this.r = r; nr_valori = n; } public void run() { for(int i=0; i < nr_valori; i++) { r.Pune(i); System.out.println("Am pus valoarea "+i); } } } class Consumator extends Thread { private Resursa r; private int nr_valori; public Producator (Resursa r, int n) { this.r = r; nr_valori = n; } public void run() { for(int i=0; i < nr_valori; i++) { int j = r.Scoate(); System.out.println("Am scos valoarea "+j); } } } class Client { public static void main(String[] args) { Resursa r = new Resursa(10); Producator p = new Producator(r,40); Consumator c = new Consumator(r,40); p.start(); c.start(); } } n secvena de mai sus am folosit variabila membru nr_valori n ambele fire pentru a reprezenta numrul total de valori care vor fi manipulate n procesul de scriere/citire. Este obligatoriu ca la crearea unei perechi de obiecte producator-consumator care manipuleaz aceeai resurs r s se dea aceeai valoare parametrului n din constructor.
7

Dac la consumator nr_valori este mai mare dect la productor, vom ajunge n situaia n care firul consumator nu mai poate iei din starea de ateptare, dup ce a citit ultima valoare creat de productor. O alt variant ar fi utilizarea unui fanion pe care productorul s-l seteze cnd a terminat de creat valorile, iar consumatorul s-i termine execuia n funcie de acest fanion.

Tem
Se consider o parcare pentru maini, cu n intrri i o singur ieire. Activitile din parcare sunt dirijate de ctre un paznic. Acesta autorizeaz intrrile (respectiv ieirile) mainilor n (respectiv din) parcare i memoreaz numrul s de locuri libere. nainte de a ncerca s intre sau s ias din parcare, fiecare ofer i comunic paznicului decizia sa. Acesta, fiind un om simplu, nu poate discuta simultan cu mai multe persoane, deci nici un ofer nu se poate adresa paznicului n intervalul de timp n care acesta dialogheaz cu un alt ofer. Exist dou modaliti prin care paznicul discut cu oferii: un ofer l anun pe paznic c dorete s intre n parcare. Paznicul verific dac exist locuri libere i, n caz afirmativ, i permite oferului accesul. Maina ocup unul dintre locurile libere, valoarea lui s se micoreaz cu o unitate, iar dialogul se ncheie. Dac nu mai sunt locuri n parcare, paznicul i spune oferului c nu poate intra deoarece parcarea este plin, dar c a notat numrul mainii, invitndu-l s fie gata s intre n parcare atunci cnd se va elibera un loc. Dialogul se ncheie. un ofer dorete s ias din parcare i ncepe dialogul cu paznicul pentru a-l informa de aceast intenie. Dac exist oferi care ateapt s intre n parcare, atunci paznicul i permite oferului care a iniiat dialogul s plece i, concomitent (pentru operativitate) l cheam pe unul dintre oferii aflai pe lista de ateptare s ocupe locul eliberat. Dialogul se ncheie. n caz contrar, paznicul i permite oferului, care a iniiat dialogul, s plece, mrete cu o unitate valoarea lui s i ncheie dialogul. S se scrie un program pentru vizualizarea modului n care se realizeaz dialogul dintre paznic i oferi.

Lucrarea 11

Applet-uri Java

Limbajul Java a cunoscut o rspndire foarte mare datorit faptului c un program Java poate fi ncorporat ntr-o pagin web, putnd fi executat ori de cte ori pagina respectiv este vizitat cu ajutorul unui program de navigare. O pagin web este un fiier care conine dou tipuri de informaii: text obinuit, reprezentnd partea din fiier care este afiat de ctre programul de navigare; construcii speciale numite tag-uri, care servesc la: controlul modului de afiare a textului, privind aspecte de genul: formatul literelor, alinierea pe orizontal sau vertical, prezentarea datelor sub form de tabele etc. includerea de imagini in pagin realizarea de legturi spre alte pagini. Ansamblul de reguli care guverneaz construirea i utilizarea tag-urilor se numete HyperText Markup Language HTML. Pentru ca un fiier HTML s fie recunoscut de o aplicaie de navigare pe Internet, el trebuie s aib o anumit structur: <HTML> <HEAD> <TITLE> Titlul paginii </TITLE> </HEAD> <BODY> Coninulul propriu-zis al paginii </BODY> <HTML> Limbajul HTML este un instrument de organizare a informaiei astfel nct aceasta s poat fi accesat i prezentat ntr-un mod util i plcut pentru utilizatori. Una dintre limitrile HTML-ului const n caracterul su pasiv deoarece nu poate fi folosit pentru a crea pagini de Internet interactive. Aceast limitare este rezolvat cu ajutorul applet-urilor Java. Un applet este un program Java compilat (adic un fiier cu extensia .class) al crui nume este referit ntr-o pagin web. Cnd pagina respectiv este ncrcat ntr-un browser, programul va fi i el ncrcat i lansat n execuie. Un asemenea program va fi capabil, pe lng simpla afiare de informaii, s citeasc date i s le prelucreze. Este important de reinut faptul c un applet nu poate fi lansat n execuie de sine stttor, ci doar prin intermediul browser-elor compatibile Java.

Un applet utilizeaz date din pachetul java.applet, care realizeaz controlul execuiei, precum i al comunicrii dintre un applet i contextul su. Pe lng acestea, applet-ul trebuie s conin elemente de interfa grafic pentru dialogul cu utilizatorul (din pachetul java.awt.). Exemplul care urmeaz prezint structura celui mai simplu applet, care afieaz mesajul Hello World. import java.awt.*; import java.applet.*; public class PrimulApplet extends Applet{ public void paint (Graphics g){ g.drawString(Hello World, 20, 20); } } Pentru a lansa n execuie un applet se parcurg urmtorii pai: se compileaz applet-ul obinnd un fiier .class (n cazul prezentat, PrimulApplet.class); se insereaz o referin la acest fiier n pagina web n care se dorete ncorporat applet-ul folosind secvena: <APPLET CODE =nume_fisier.class HEIGHT=dim_h> </APPLET> WIDTH=dim_w

unde dim_w i dim_h sunt numere ntregi care reprezint lungimea, respectiv limea, exprimate n numr de pixeli, ale unui dreptunghi pe ecran, considerat fereastr pentru applet. n interiorul acestui dreptunghi vor fi ncadrate toate afirile applet-ului; se ncarc pagina web ntr-un browser compatibil Java. Pentru exemplul nostru poate fi folosit urmtorul program HTML: <HTML> <HEAD> <TITLE> Primul applet </TITLE> </HEAD> <BODY> <B> Primul applet Java </B><BR><BR> <APPLET CODE = PrimulApplet.class WIDTH=300 WEIGHT=60></APPLET> </BODY> <HTML> Fiierul .class trebuie s se afle n acelai director cu fiierul HTML, altfel nu este suficient s se scrie doar numele su la atributul CODE. Clasa principal a unui applet trebuie neaprat s extind clasa predefinit Applet. Metoda paint() primete ca parametru o referin a unui obiect de tip Graphics (clas predefinit n java.awt), creat automat n momentul lansrii n execuie a applet-ului. Un obiect Graphics poate

realiza operaii de desenare ntr-o poriune a ecranului. Dup ce se opereaz modificri ntr-un applet, el trebuie recompilat, rezultnd un fiier .class modificat. Pentru ca n pagina web, care include applet-ul respectiv, s fie vizibile modificrile, este necesar acionarea butonului Reload al browser-ului n timp ce se ine apsat tasta Shift. Folosind clasele Font i Color din pachetul java.awt. se poate face applet-ul mai atractiv. Cu ajutorul obiectelor Color se pot stabili culori pentru umplerea diverselor forme geometrice sau pentru text. O culoare este specificat cu ajutorul a trei numere ntregi din intervalul [0-255] care indic nivelul de rou, verde i albastru (RGB) prin combinaia crora va rezulta culoarea dorit. Spre exemplu, pentru a scrie textul cu violet, applet-ul nostru va deveni: import java.awt.*; import java.applet.*; public class PrimulApplet extends Applet{ public void paint (Graphics g){ Color c=new Color(180,10,120); g.setColor(c); g.drawString(Hello World, 20, 20); } } Exist un set de obiecte de tip Color definite ca membri statici ai clasei Color: Color.black (negru), Color.blue (albastru), Color.darkGray (gri nchis), Color.gray (gri), Color.green (verde), Color.red (rou), Color.white (alb), Color.yellow (galben) etc. Pentru specificarea unui anumit font pentru caracterele textului se utilizeaz clasa Font cu precizarea a trei parametri: tipul fontului (TimesRoman); stilul, sub forma unor constante predefinite: Font.PLAIN (caractere normale), Font.BOLD (caractere ngroate) sau Font.ITALIC (caractere nclinate). Pentru combinarea stilurilor se utilizeaz operatorul sau |; dimensiunea caracterelor, exprimat ca numr de puncte: 10, 12, 14, 18 etc. Pentru a folosi fonturi Times New Roman, bold i cu dimensiunea de 18 puncte, exemplul nostru se modific astfel: import java.awt.*; import java.applet.*; public class PrimulApplet extends Applet{ public void paint (Graphics g){ Color c=new Color(180,10,120); g.setColor(c); Font f=new Font(TimesRoman, Font.BOLD, 18); g.drawString(Hello World, 20, 20); }

} Dac ntr-un applet se execut operaiile setColor sau setFont, valorile stabilite de ctre acestea pentru culoare i font vor rmne active pn la sfritul programului sau pn la o nou modificare. Pentru a obine informaii despre culoarea/fontul curente se pot folosi urmtoarele metode: getColor returneaz un obiect de tip Color reprezentnd culoarea curent. Pentru a afla componentele RGB ale culorii se vor aplica metodele getRed, getGreen i getBlue. getFont care returneaz referina unui obiect Font, reprezentnd fontul curent. Pe lng afiarea de text, clasa Graphics include i metode de desenare a unor forme geometrice: drawRect(int x, int y, int width, int height); deseneaz conturul unui dreptunghi cu colul din stnga sus n punctul de coordonate (x,y), lungimea egal cu width i nlimea egal cu height. Culoarea utilizat pentru contur este culoarea curent; drawLine(int x1, int y1, int x2, int y2); deseneaz un segment de dreapt ale crui capete au coordonatele (x1, y1), respectiv, (x2, y2). Culoarea utilizat pentru linie este culoarea curent; drawOval(int x, int y, int width, int height); deseneaz conturul unei elipse avnd cele dou diametre principale paralele cu laturile applet-ului. (x, y) reprezint coordonatele colului din stnga sus ale dreptunghiului imaginar care circumscrie elipsa, iar width i hieght reprezint lrgimea, respectiv nlimea elipsei. Culoarea utilizat pentru contur este culoarea curent; fillRect(int x, int y, int width, int height); coloreaz interiorul unui dreptunghi cu culoarea curent. Semnificaia parametrilor este aceeai ca i la drawRect; fillOval(int x, int y, int width, int height); coloreaz interiorul unei elipse cu culoarea curent. Semnificaia parametrilor este aceeai ca i la drawOval. Tem Se cere s se realizeze un applet care traseaz graficul funciei cosinus pe intervalul [xmin, xmax]. Indiciu Principiul de lucru este urmtorul: se parcurge intervalul ales cu un pas stabilit i, pentru fiecare valoare x a intervalului, se determin coordonata y=cos(x). Graficul va rezulta prin unirea fiecrei perechi de puncte succesive astfel determinate cu o linie dreapt. Ca

urmare, cu ct pasul de parcurgerea a domeniului este mai mic, cu att graficul va avea o precizie mai bun. Pe de alt parte, innd cont de faptul c fereastra applet-ului va avea o anumit dimensiune i presupunnd c vom avea pentru colul din stnga sus coordonatele (x1,y1), iar pentru colul din dreapta jos coordonatele (x2,y2), trebuie translatate intervalele [xmin, xmax] i [ymin,ymax] n [x1,x2] i [y1,y2]. Astfel, se determin: coordonatele originii graficului: x0 = x1 [xmin * (x2 x1 + 1)/(xmax xmin)] y0 = y1 + [ymin * (y2 y1 + 1)/(ymax ymin)] coordonata xt din intervalul [x1,x2], corespunztoare lui x din intervalul [xmin, xmax]: a) x > 0 xt = x0 + [x * (x2 x0 )/xmax] b) x < 0 xt = x0 [x * (x0 x1 )/xmin] c) x = 0 xt = x0 coordonata yt din intervalul [y1,y2], corespunztoare lui y din intervalul [ymin, ymax]: a) y > 0 yt = y0 [y * (y0 y1)/ymax] b) y < 0 yt = y0 + [y * (y2 y0)/ymin] c) y = 0 yt = y0

Lucrarea 12

Elemente GUI n applet-uri

Biblioteca de clase Java dispune de o gam foarte larg de mijloace de realizare a comunicrii ntre un applet i utilizatorii si, materializate prin elemente GUI: meniuri, butoane, liste etc. Trebuie precizat c la ncrcarea unui applet ntr-un browser, prima metod care se execut este metoda init(), definit n clasa Applet. Aceast metod realizeaz toate operaiile de iniializare, printre care i dotarea cu elemente de control, dac este cazul. Abia dup finalizarea metodei init() se vor executa celelalte metode ale applet-ului. Metoda init() este descris ntr-o form general n clasa Applet, coninnd toate operaiile absolut necesare unui applet. Includerea de elemente GUI reprezint o operaie particular i ea trebuie specificat explicit de ctre programator prin suprascrierea metodei init(). Un obiect de tip Applet poate fi considerat drept un container (sau o colecie) care poate conine elemente simple de control sau chiar alte containere. Din acest motiv, includerea unui element de control ntr-un applet se traduce prin adugarea acestuia folosind metoda add(). Spre exemplu, adugarea unui buton ntr-un applet se face astfel: import java.awt.*; import java.applet.*; public class Butoane extends Applet{ public void init (){ Button exp = new Button ("Exemplu"); add(exp); } } Se poate observa c, de aceast dat, nu mai apare metoda paint(). Motivul este urmtorul: paint(), ca i init(), este definit n clasa Applet, iar comportarea ei implicit presupune desenarea n fereastra applet-ului a tuturor elementelor de control existente n container dup execuia metodei init(). n cazul n care containerul este gol, atunci este necesar suprascrierea corespunztoare a metodei paint(). Dac se dorete ca un applet s conin mai multe butoane, atunci trebuie rezolvat problema amplasrii acestora n cadrul ferestrei applet-ului. Soluia const n crearea unor mici containere de butoane care vor fi, apoi, plasate n containerul mare, care este applet-ul. Pentru a gestiona amplasarea componentelor sale, un container se folosete de obiecte ale unor clase speciale, definite n pachetul java.awt, care joac rolul de plasatori. Cele mai des utilizate astfel de clase sunt FlowLayout i BorderLayout, motenitoare ale clasei LayoutManager.

Pentru a specifica modul de plasare a butoanelor, trebuie apelat metoda setLayout() n metoda init() a clasei Applet, avnd drept parametru un obiect plasator. Dac parametrul este de tip FlowLayout, toate elementele de control adugate n applet vor fi dispuse pe orizontal, cte ncap pe lungimea ferestrei, distanate implicit cu 5 pixeli pe orizontal i pe vertical. Dac parametrul este de tip BorderLayout, pot fi plasate ntr-un container maximum cinci elemente, ca in Figura 11.1.

Figura 11.1 Plasarea celor cinci elemente n BorderLayout

Spre deosebire de modul FlowLayout, unde ordinea n care sunt aezate elementele depinde de ordinea n care sunt scrise comenzile add(), la BorderLayout programatorul trebuie s specifice poziia unui element: est, vest, nord, sud sau centru: import java.awt.*; import java.applet.*; public class Butoane extends Applet{ public void init(){ setLayout(new BorderLayout()); add("North", new Button("N")); add("South", new Button("S")); add("East", new Button("E")); add("West", new Button("W")); add("Center", new Button("C")); } } Dac se dorete ca ntre elementele plasate n mod BorderLayout s rmn spaii, aceste spaii vor trebui precizate la crearea obiectului de acest tip: // setLayout(new BorderLayout(hdist,vdist)); // Nu este obligatoriu ca ntr-un container gestionat n modul BorderLayout s se pun exact cinci elemente. Dac vor fi puse mai puine elemente, dimensiunile acestora vor fi ajustate astfel nct ele s ocupe tot spaiul containerului. Dac n metoda init() nu se precizeaz politica de plasare, n mod implicit se consider c aceasta este de tip FlowLayout. n acest caz, n mod implicit, elementele din container vor fi centrate n cadrul rndului pe care se afl. import java.awt.*; import java.applet.*; public class Butoane extends Applet{

public void init(){ setLayout(new FlowLayout()); add(new Button("butonul1")); add(new Button("butonul2")); add(new Button("butonul3")); add(new Button("butonul4")); add(new Button("butonul5")); } }

Figura 11.2 Centrarea elementelor cu FlowLayout

Dac se dorete o alt aliniere a butoanelor cu FlowLayout, va trebui specificat acest lucru la crearea obiectului de tip FlowLayout. // setLayout(new FlowLayout(mod_aliniere) // unde mod_aliniere poate fi una dintre valorile FlowLayout.LEFT, FlowLayout.RIGHT sau FlowLayout.CENTER. n plus, se poate modifica i distana implicit dintre butoane: // setLayout(new FlowLayout(mod_aliniere, hdist, vdist) // n general, pentru a aplicaie mai complex, nu poate fi folosit n exclusivitate una dintre cele dou metod, ci se aplic o combinaie a acestor politici grupnd butoanele n subcontainere. n acest sens se folosete clasa Panel, definit n pachetul java.awt. care modeleaz cel mai simplu tip de container. Un astfel de container poate conine ca elemente i alte Panel-uri. Astfel, prin ncuibarea Panel-urilor se poate rezolva orice combinaie spaial de elemente de control. Spre exemplu, pentru realizarea unei interfee ca n Figura 11.3 se folosesc Panel-uri imbricate, precum i ambele metode de plasare.

Figura 1.3 Exemplu de aranjare a butoanelor ntr-un applet

import java.awt.*; import java.applet.* ; public class Exemplu extends Applet{ private static Font ff = new Font ("Courier", Font.BOLD,16); private Button buton (String nume){ Button b = new Button (nume) ; b.setFont(ff); return b; } private void CreeazaButoane(){ a = buton("A"); b = buton("B"); c = buton("C"); d = buton("D"); e = buton("E"); f = buton("F"); g = buton("G"); } private Panel CreeazaPanel(LayoutManager tip_plasare){ Panel p = new Panel(); p.setLayout(tip_plasare); return p; } public void init(){ setLayout(new FlowLayout(FlowLayout.CENTER,4,1)); CreeazaButoane(); linie1 = CreeazaPanel(new FlowLayout (FlowLayout.LEFT, 4,2)); linie1.add(a); linie1.add(b); linie1.add(c); linie1.add(d); add(linie1); pef = CreeazaPanel(new BorderLayout(2,2)); pef.add("North", e); pef.add("South", f); pefg = CreeazaPanel(new BorderLayout(2,2)); pefg.add("West", pef); pefg.add("East", g); add(pefg); } public void paint(Graphics gg){

setSize(linie1.getSize().width, 4*a.getSize().height); validate(); } private Panel linie1, pef, pefg; private Button a, b, c, d, e, f, g; } Dup cum se poate observa, dimensiunea ferestrei applet-ului poate fi ajustat i din interiorul applet-ului. Aceast ajustare se face n metoda paint() deoarece abia dup execuia metodei init() se cunosc toate componentele applet-ului i se poate estima dimensiunea acestora. Metoda folosit este setSize(), ea primind ca parametri lungimea i nlimea dorite. Interacionarea cu un applet se face prin tratarea evenimentelor care pot s apar. Principiul a fost deja prezentat n Lucrarea 8, la interfee grafice n Java. Forma general a unui applet care trateaz evenimente este: import java.awt.*; import java.applet.*; import java.awt.event.*; public class nume_applet extends Applet ActionListener{ public void init(){ b = new Button("nume_buton"); add(b); b.addActionListener(this); //alte elemente de control } Public void actionPerformed(ActionEvent ev){ //actiuni de executat la apasarea nume_buton } Private Button b ; // } implements

butonului

Cnd un astfel de applet este ncrcat ntr-un browser, n primul pas se execut metoda init() care realizeaz popularea applet-ului cu elemente de control. Apoi, programul rmne n ateptare pn cnd are loc un eveniment, precum apsarea unui buton. n acest moment intr n aciune metoda actionPerformed(). Pentru exemplificare, se consider urmtorul applet care nmulete dou numere introduse de ctre un utilizator. import java.awt.*; import java.applet.*; import java.awt.event.*;

public

class Calcul extends Applet implements ActionListener{ public void init(){ calcButon = new Button ("Calcul"); add(calcButon); calcButon.addActionListener(this); add(new Label("Primul numar:")); valnr1 = new TextField(10); add(valnr1); add(new Label("Al doilea numar:")); valnr2 = new TextField(10); add(valnr2); result = new Label("Rezultat = 0000"); add(result); } public void actionPerformed(ActionEvent ev){ int nr1 = Integer.parseInt(valnr1.getText()); int nr2 = Integer.parseInt(valnr2.getText()); int res = nr1 * nr2; result.setText("Rezultat = " + res); validate(); } private Button calcButon; private TextField valnr1, valnr2; private Label result;

} Pentru cazurile n care se lucreaz cu surse multiple de evenimente, pentru a identifica la un moment dat sursa unui eveniment se folosete metoda getActionCommand() din clasa ActionEvent. Dac evenimentul a constat n apsarea unui buton, aceast metod returneaz un String coninnd textul scris pe butonul respectiv. Spre exemplu, dac applet-ul de mai sus ar avea i un buton pentru scderea celor dou numere, metoda actionPerformed() ar fi: // public void actionPerformed(ActionEvent ev){ String s = ev.getActionCommand(); if(s.equals("Calcul")) //tratarea inmultirii else //tratarea scaderii // }

Tem
S se realizeze sub form de applet calculatorul prezentat ca tem n Lucrarea 8.

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