Limbajul Java este un limbaj n ntregime orientat pe obiecte: n Java (spre deosebire de C++) nu exista variabile sau functii care sa nu fie membre ale unei clase. Conceptele de programare obiectuala sunt aceleasi ca si n C++: programele sunt organizate ca si colectii de obiecte cooperante, fiecare obiect fiind o instanta a unei clase. Fiecare clasa reprezinta abstractizarea unui tip de entitate din realitatea modelata, iar clasele sunt membre ale unei ierarhii de clase, corelate ntre ele prin relatii de mostenire. Orice obiect este ncapsulat, ceea ce nseamna ca reprezentarea lui (adica structura interna a acelui obiect) nu este vizibila utilizatorilor, care au acces doar la functiile (metodele) pe care acel obiect este capabil sa le execute. 3.1. CLASE SI OBIECTE Forma completa de definire a unei clase este urmatoarea: lista_modificatori class nume_clasa extends nume_clasa_de_baza implements lista_interfete { // corpul clasei; } Definitia unei clase trebuie sa contina n mod obligatoriu cuvntul cheie class, numele clasei si corpul clasei. Toti ceilalti termeni din definitie sunt optionali. nainte de cuvntul class poate exista o lista de modificatori (despartiti prin spatiu) care atribuie anumite proprietati clasei definite. Acesti modificatori pot fi din categoria cuvintelor cheie: public, abstract si final. Pentru a deriva o clasa dintr-o clasa de baza (superclasa) se foloseste cuvantul cheie extends urmat de numele clasei de baza. n Java, spre deosebire de C++, poate exista o singura clasa de baza (Java nu supporta mostenirea multipla) si n lipsa mostenirii multiple se folosesc interfete Java. O interfata Java (interface) este un tip de date abstract, un fel de clasa n care nici una din metode nu este definita n intregime (lipseste corpul metodei, doar s-a declarat metoda). O clasa implementeaza o interfata daca declara acest lucru n declaratia clasei prin cuvntul cheie implements urmat de numele interfetei si daca defineste toate metodele declarate n interfata. O clasa poate implementa mai multe interfete utilizand cuvntul cheie implements urmat de numele interfetelor separate de virgula. n Java nu poate exista o declaratie de clasa independenta de corpul clasei. Corpul unei clase contine o serie de declaratii de membrii ai clasei si constructori. Membrii unei clase pot fi att atribute ct si metode. Un membru declarat ntr-o clasa este disponibil prin numele sau oriunde n interiorul acelei clase. Atributele unei clase (variabile membru) pot fi de orice tip de data primitiv sau referinta (clasa sau tablou). Declaratia atributelor are urmatoarea forma generala: lista_modificatori_atribut tip_data lista_declaratori_variabile; unde lista_declaratori_variabile cuprinde o serie de declaratori de variabila separati prin virgula. Un declarator de variabila are una din urmatoarele forme generale: nume_variabila = expresie_initializare; nume_tablou[] = expresie_initializare; Expresiile de initializare pot lipsi dintr-un declarator de variabila. Lista modificatorilor poate contine o serie de cuvinte cheie delimitate prin spatiu. Acesti modificatori stabilesc proprietatile unui atribut si pot fi unul din urmatoarele cuvinte cheie: public, protected, private, final, static, transient si volatile. Primii trei sunt modificatori de acces si nu poate exista dect unul dintre ei ntr-o declaratie. O metoda are urmatoarea forma generala de definitie: lista_modificatori tip_returnat nume(lista_parametri) throws lista_exceptii { //corp_metoda }
16 Modificatori de metoda care definesc proprietatile metodei pot fi urmatoarele cuvinte cheie: public, protected, private, abstract, static, final, synchronized sau native. Se pastreaza principiul suprancarcarii (overloading) metodelor din C++. O functie poate arunca o serie de exceptii. Acest lucru trebuie specificat prin cuvntul cheie throws urmat de o lista a tipurilor de exceptii care pot fi aruncate. Corpul metodei defineste operatiile pe care le va executa metoda la apelul ei. Constructorul este (ca si n C++) metoda cu nume identic cu numele clasei si care nu returneaza nici un tip de date. Ca orice metoda, si constructorii contin o lista de parametrii formali. Exista astfel posibilitatea suprancarcarii constructorilor prin modificarea listei parametrilor formali. n functie de variabilele (tipurile lor) utilizate ca si parametri la instantierea unui obiect prin operatorul new, se apeleaza constructorul corespunzator. n cazul n care nu se declara nici un constructor (doar n acest caz) compilatorul creaza un constructor implicit, avnd numele clasei, fara nici un parametru formal si avnd corpul constructorului gol. Corpul constructorului poate contine ca prima linie (doar ca prima linie) una din urmatoarele instructiuni: this(lista_parametri) sau super(lista_parametri) Cuvntul cheie this reprezinta referinta la obiectul curent. Prin this(lista_parametri) se poate apela un alt constructor (al aceleiasi clase) cu o lista de parametri diferita de cea a constructorului curent. Cuvntul cheie super reprezinta referinta la clasa de baza a clasei actuale. Prin instructiunea super(lista_parametri) se apeleaza constructorul superclasei (clasa de baza directa). Cele doua instructiuni nu pot exista simultan. Un exemplu de suprancarcare a constructorilor si utilizare a cuvntului cheie this ntr-un constructor este urmatorul: public class Masina{ public Masina(int nr){. . .} // primul constructor public Masina(int nr, String marca){ // al doilea constructor this(nr); // apelul primului constructor . . . . . . } } Cuvntul cheie this poate fi utilizat nu numai la apelul constructorului, ci si n alte metode. O situatie mai des ntlnita este aceea de accesare a unui atribut al clasei din interiorul unei metode a clasei n care exista o variabila (de regula, un parametru formal) cu nume identic cu cel al atributului clasei. Ca urmare, n doemniul de vizibilitate al metodei, numele respectiv va referi parametrul formal, nu atributul clasei. Situatia acesta se rezolva n modul urmator: int width; // atribut al clasei int setWidth(int width) // parametru formal { this.width = width; // this.width refera atributul clasei // iar width parametrul formal al metodei } Obiecte. n crearea unui obiect dintr-o clasa sunt trei etape: declararea, instantierea si initializarea. Daca declararea unui obiect se poate face separat, instantierea si initializarea sunt operatii ce au loc simultan prin folosirea operatorului new. Operatorul new aloca memoria necesara stocarii obiectului respectiv (atributelor lui) si apeleaza un constructor, care initializeaza obiectul conform operatiilor specificate n corpul constructorului. La instantierea unei clase se aloca memorie fiecarui atribut din clasa respectiva, se initializeaza la valoarea implicita (0 pentru tipurile primitive numerice, false pentru tipul boolean, si null pentru tipurile referinta), se atribuie valoarea din expresia de initializare din declaratia atributului (daca aceasta exista) si dupa aceea se apeleaza constructorul. Variabilele sunt initializate n ordinea definirii lor si naintea apelului oricarei metode inclusiv a constructorului. Doar dupa terminarea executiei constructorului obiectul este disponibil. n schimb referinta this poate fi utilizata n interiorul lui imediat dupa alocarea memoriei necesare stocarii lui. De multe ori este necesara eliberarea unor resurse n momentul distrugerii unui obiect. n limbajul C++ aveam la dispozitie destructorul. n Java, masina JVM contine o componenta numita Garbage Colector care se ocupa de dezalocarea zonelor de memorie alocate prin new, n momentul n care acele zone nu mai sunt referite de nici o variabila. Exista cazuri nsa n care e nevoie de dezalocarea altor resurse atasate unui obiect dect cele de memorie, ca de exemplu conexiuni la fisiere sau conexiuni n retea. O solutie este
17 utilizarea metodei finalize mostenita din clasa Object. Metoda finalize este apelata automat de JVM dupa ce obiectul nu mai este referit, dar nainte de eliberarea zonei de memorie alocata acestuia. Membrii statici ai unei clase. Utilizarea modificatorului static la declararea unui atribut sau la declararea unei metode nseamna apartenenta membrului respectiv la clasa si nu la o instanta a ei (la un obiect). n cazul unui atribut static, se aloca memorie o singura data, la prima initializare a clasei. La urmatoarele instantieri ale clasei nu se mai aloca memorie pentru un atribut static, dar toate obiectele din acea clasa pot accesa aceeasi variabila statica, aflata n aceeasi zona de memorie. Un exemplu de utilizare a unei variabile statice este de a contoriza obiectele create din acea clasa: class Contor{ static int contor = 0; public Contor(){ contor++; System.out.println(Acesta este obiectul nr. + contor); } } ... Contor c1 = new Contor(); // Acesta este obiectul nr. 1 Contor c2 = new Contor(); // Acesta este obiectul nr. 2 ... Metodele statice ale unei clase sunt un fel de functii globale si se pot folosi fara sa fie necesara crearea unei instante a clasei (obiect). Un exemplu de metoda statica este metoda main() a clasei principale a aplicatiilor. Metodele statice nu pot utiliza atribute si metode care nu sunt statice. Specificatorii de acces. n cazul n care o clasa este declarata de tipul public, ea poate fi utilizata din exteriorul package-ului n care a fost definita. Specificatorul public permite declararea unui obiect de tipul clasei respective (referinta obiectului). Pentru a putea fi nsa instantiata, clasa trebuie sa aiba si un constructor declarat de tip public. n cazul n care cuvntul cheie public lipseste din definitia clasei, clasa devine clasa friend si poate fi accesata doar din interiorul package-ului n care a fost creata. n cazul metodelor, atributelor si constructorilor unei clase, se poate vorbi despre patru niveluri de acces, prezentate n tabelul urmator.
Acces Specificator n clasa n clase derviate n package n exteriorul package-ului public DA DA DA DA DA DA DA NU protected DA DA NU NU private DA NU NU NU 3.2. MOSTENIREA CLASELOR Prin mostenire se pot crea clase noi (numite clase derivate sau subclase) care extind clasa de baza (superclasa). Clasa derivata mosteneste de la clasa de baza toate metodele si toate atributele. Constructorii nu se mostenesc. Ca urmare se poate spune ca un obiect din clasa derivata este si un obiect de tipul clasei de baza. Pe lnga metodele si atributele pe care le mosteneste de la clasa de baza, clasa derivata poate sa adauge altele noi. Clasa derivata este o specizalizare a clasei de baza. Derivarea unei clase n Java se face utiliznd cuvntul cheie extends urmat de numele clasei de baza. Dupa cuvntul cheie extends nu poate exista dect o singura clasa deoarece limbajul Java nu admite mostenirea multipla. n cazul claselor derivate compilatorul adauga la inceputul constructorului un apel al constructorului fara argumente al clasei de baza daca si numai daca nu exista un apel explicit al unui constructor al clasei de baza introdus n programul sursa. n cazul n care apelul se face explicit, este obligatoriu ca acest apel sa se faca naintea oricaror alte instructiuni. Apelul explicit se face prin intermediul cuvantului cheie super si este util n cazul n care dorim apelul unui alt constructor al clasei de baza dect cel fara parametru. La instantierea unei clase derivate, ordinea initializarilor este urmatoarea: Initializarea atributelor statice ale clasei de baza Initializarea atributelor statice ale clasei derivate Initializarea atributelor nestatice ale clasei de baza Apelul constructorului clasei de baza
18 Initializarea atributelor nestatice ale clasei derivate Apelul constructorului clasei derivate O clasa derivata poate declara o metoda cu acelasi nume si semnatura cu a unei metode din clasa de baza si aceasta metoda din clasa derivata substituie metoda din clasa de baza. Tehnica se numeste redefinire sau dominare (redefinition, overriding). La apelul respectivei metode dintr-un obiect de tip clasa derivata se va executa metoda declarata n clasa derivata. Accesul la metoda clasei de baza din interiorul clasei derivate se poate face folosind cuvntul cheie super. Nici atributele statice, nici metodele statice nu pot fi redefinite. n clasa derivata poate exista o metoda statica sau un atribut cu acelasi nume cu al unei metode sau al unui atribut din clasa de baza, dar nu le substituie pe acestea din urma. Modificatorul final. O clasa declarata final nu poate fi derivata. O metoda declarata final nu poate fi redefinita. Unul din motivele specificarii final al unei metode este eficienta executiei. n momentul n care compilatorul ntlneste un apel de metoda declarata final, el poate sa substituie apelul cu secventa de instructiuni din corpul metodei final. Metodele final din Java sunt corespondentele metodelor inline din C++. Un atribut declarat final reprezinta o constanta de program. Odata initializat el nu mai poate fi modificat. De multe ori modificatorul static se utilizeaza mpreuna cu modificatorul final pentru a declara o constanta a clasei. Clase si metode abstracte. O clasa poate fi declarata de tip abstract, caz n care ea nu poate fi instantiata. O clasa abstracta poate fi utilizata doar ntr-o relatie de derivare. O clasa poate contine o serie de metode abstracte. O clasa care are o metoda abstracta trebuie sa fie declarata abstracta. Clasele care sunt derivate din clase abstracte trebuie sa implementeze metodele abstracte ale acestora din urma sau sa fie declarate la rndul lor abstracte. O clasa este declarata abstracta pentru a oferi suport de implementare a polimorfismului pentru o ierarhie de clase derivate din ea. Conversii ntre tipuri referinta. ntre tipuri de date referinta ntre care exista relatii de mostenire se pot face conversii de tipuri. Conversia unui tip derivat ntr-un tip de baza se numeste conversie de largire (widening sau upcasting) si se face implicit. Conversia unui tip de baza ntr-un tip derivat se numeste conversie de ngustare (narrowing, downcasting) si se face explicit folosind operatorul cast. La downcasting pot aparea erori n timpul executie n cazul n care obiectul care se converteste nu a fost instantiat ca si tip derivat. 3.3. IERARHII DE CLASE Folosind mostenirea claselor, ntr-un program se pot crea ierarhii de clase pe mai multe niveluri. n figura urmatoare este data diagrama UML a claselor unei aplicatii simple de muzica. n aceasta aplicatie se pot urmari mai multe din aspectele de definire a claselor prezentate n lucrare: definirea claselor, crearea (instantierea) obiectelor, extinderea claselor, clase abstracte si concrete, conversie ntre tipurile derivate si tipul de baza.
19 Clasa de baza a ierarhiei, clasa Instrument (care, la rndul ei este derivata implicit din clasa Object, dar acest lucru nu s-a mai reprezentat n diagrama) are toate metodele declarate abstract, deci si ea trebuie sa fie declarata abstract. Clasele derivate din aceasta (clasele Suflatori, Coarde, Percutie) sunt clase concrete, care definesc toate cele trei metode mostenite de la clasa de baza. Implementarea acestor metode este foarte simpla, doar scriu la consola numele unei operatii (canta(), ajusteaza()), sau returneaza numele clasei instrumentului (getNume()). La fel, clasele de pe nivelul al treilea al ierarhiei create (clasele Vioara si Violoncel, care extind clasa Coarde), redefinesc metodele mostenite canta() si getNume(). Codul programului (Muzica.java) este urmatorul:
// // Muzica.java // import java.util.*; abstract class Instrument { abstract public void canta(); abstract public String getNume(); abstract public void ajusteaza(); } class Suflatori extends Instrument { public void canta() { System.out.println("Suflatori.canta()"); } public String getNume() { return "Suflatori"; } public void ajusteaza() {} } class Percutie extends Instrument { public void canta() { System.out.println("Percutie.canta()"); } public String getNume() { return "Percutie"; } public void ajusteaza() {} } class Coarde extends Instrument { public void canta() { System.out.println("Coarde.canta()"); } public String getNume() { return "Coarde"; } public void ajusteaza() {} } class Vioara extends Coarde { public void canta() { System.out.println("Vioara.canta()"); } public String getNume() { return"Vioara";} } class Violoncel extends Coarde { public void canta() { System.out.println("Violoncel.canta()"); } public String getNume() { return "Violoncel"; } } public class Muzica { static void acordare(Instrument[] e) { for(int i = 0; i < e.length; i++) e[i].canta(); } public static void main(String[] args) { Instrument[] orchestra = new Instrument[5]; int i = 0; // Conversie de largire - upcast orchestra[i++] = new Suflatori(); orchestra[i++] = new Percutie();
20 orchestra[i++] = new Coarde(); orchestra[i++] = new Vioara(); orchestra[i++] = new Violoncel(); acordare(orchestra); } }
n diagrama claselor nu este trecuta si clasa Muzica, folosita ca si clasa princpala (care contine functia main()) si care este legata de toate celelalte clase printr-o legatura de dependenta de utilizare . Toate metodele claselor reprezentate mai sus sunt metode virtuale, redefinite n fiecare clasa, care substituie (nlocuiesc, domina) metodele din clasele de baza. Obiectele create ca instante a diferitelor clase (Suflatori, Coarde, Percutie, Vioara si Violoncel) sunt organizate ca un tablou de referinte la clasa de baza (Instrument[] orchestra = new Instrument[5];). La crearea acestor obiecte, referintele returnate de operatorul new sunt convertite implicit (upcasting) n referinte la clasa de baza (de exemplu, orchestra[i++] = new Suflatori(); ). La executia programului se obtine urmatorul rezultat:
3.4. INTERFETE JAVA Interfetele Java reprezinta colectii de metode (fara corp) si constante. O interfata este declarata prin cuvntul cheie interface. O interfata Java poate mosteni alte interfete Java utiliznd cuvntul cheie extends. Spre deosebire de clase, interfetele suporta mostenire multipla. La fel ca si o clasa abstracta, o interfata Java nu poate fi instantiata. O interfata java poate fi implementata insa de o clasa utilizand cuvntul cheie implements. O clasa poate implementa mai multe interfete. O clasa care implementeaza o interfata trebuie sa defineasca toate metodele acelei interfete. Ca exemplu, se poate relua programul precedent si se transforma clasa abstracta Instrument n interfata, pastrnd restul programului aproape nemodificat (Muzica1.java):
// // Muzica1.java // import java.util.*; interface Instrument { public void canta(); public String getNume(); public void ajusteaza(); } class Suflatori implements Instrument {. . .} class Percutie implements Instrument {. . .} class Coarde implements Instrument {. . .} class Vioara extends Coarde {. . .} class Violoncel extends Coarde {. . .} public class Muzica1 {. . . }
21 Metodele declarate n interfata sunt implicit abstracte si nu mai este necesar introducerea specificatorului abstract. La executia acestui program se obtin aceleasi rezultate ca si la programul precedent. 2.5 COLECTII DE OBIECTE Java dispune de mai multe metode de pastrare a obiectelor. O prima metoda o reprezinta tablourile, care sunt tipuri predefinite (built-in) ale limbajului, care au fost studiate n lucrarea precedenta. Pe lnga tablouri, exista librarii (pachete) care contin mai multe categorii de clase de colectii (clase container) care ofera o mare varietate de modele de date si structuri de reprezentare a acestora. ntr-o colectie, numarul de elemente poate varia prin introducerea unor elemente noi sau sau prin extragerea unor elemente. Modelele de date cele mai folosite (si implementate n arhitectura colectiilor Java Java Collections Framework) sunt: lista (list - o secventa de elemente, memorate ntr-o anumita ordine), multimea (set - o grupare de elemente n care nu exista doua sau mai multe elemente de acelasi fel) si vectorul asociativ (map o grupare de elemente, n care fiecare element contine doua parti asociate, cheia si valoarea). La rndul lui, fiecare model de date (forma colectiei) poate fi realizat prin diferite structuri de date. De exemplu, o lista poate fi realizata printr-un tablou sau printr-o lista nlantuita; o multime poate fi realizata printr-o lista nlantuita, printr-un arbore sau printr-o tabela de dispersie etc. Arhitectura colectiilor Java consta din mai multe interfete, clase abstracte si clase instantiabile n pachetul java.util, prin care se diferentiaza doua categorii de containere, n functie de numarul de valori pe care l contine fiecare element al containerului: Tipul Collection defineste cte o valoare n fiecare element (denumirea este putin confuza, dat fiind ca ntraga colectie de clase se numeste colectii). Aceast tip cuprinde mai multe subtipuri (prin interfete derivate), n functie de restrictiile impuse. Tipul List (List este o interfata care extinde interfata Collection) defineste modelul de date lista, adica un container care contine o secventa de elemente aflate ntr-o anumita ordine; tipul Set (Set este o interfata care extinde Collection) defineste modelul de date multime, adic[ un container n care nu exista elemente duplicat. Tipul Map defineste modelul de date vector asociativ, adica un container n care fiecare element contine doua parti asociate (cheie, valoare). Forma unui container (modelul de date) este impusa prin interfata (List, Set, Map) pe care o implementeaza clasa containerului, iar structura de date a containerului este definita n clasa, obtinndu-se astfel mai multe clase de colectii care difera att prin modul de organizare ct si prin structura de date folosita. n figura urmatoare este reprezentata diagrama partiala a claselor si interfetelor arhitecturii de colectii Java. Denumirile claselor de colectii sunt formate din doua parti, prima parte reprezinta structura de date folosita, iar cea de a doua parte reprezinta forma colectiei (interfata implementata). De exemplu, clasa ArrayList (care este o lista reprezentata printr-un tablou de elemente) si clasa LinkedList (care este o lista reprezentata printr-o lista nlantuita de elemente) implementeaza interfata List.
Asa cum se vede din figura, interfata Collection este extinsa de interfetele List si Set; clasele ArrayList si LinkedList implementeaza interfata List; clasele TreeSet si HashSet implementeaza interfata Set; clasele TreeMap si HashMap implementeaza interfata Map.
22 Interfata Collection contine mai multe declaratii de metode care sunt definite n toate clasele care implementeaza interfetele List si Set (derivate din interfata Collection). Dintre acestea, metodele add(Object o) si remove(Object o) permit adaugarea unui nou element, respectiv eliminarea unui element existent n colectie. Metoda int size() returneaza numarul de elemente al colectiei. O caracteristica importanta a claselor de colectii l reprezinta faptul acestea contin referinte la Object (orice obiect poate fi referit printr-o astfel de referinta). Acest lucru nseamna ca n aceeasi colectie se pot introduce obiecte de orice clasa (dat fiind ca toate clasele sunt derivate din clasa Object) si nu se pot introduce date de tipuri primitive (care nu sunt derivate din nici o clasa). Restrictia colectiilor de a nu admite date primitive se rezolva utiliznd clasele echivalente tipurilor primitive (Character, Integer etc). Acesta caracteristica a containerelor Java (de a stoca referinte de tip Object) are totusi un dezavantaj, deoarece, dupa introducerea unui obiect ntr-o colectie (memorarea referintei acestuia ntr-un element al colectiei) se pierde tipul exact al referintei (care se memoreaza ca referinta la Object). Deoarece informatia de tip a referintei se pierde, trebuie sa fie facuta o conversie cast atunci cnd se utilizeaza elementele din colectie si, daca conversia nu se face pentru tipul exact al obiectului, atunci poate sa apara exceptii n timpul executiei. 2.5.1 Clasa ArrayList Clasa ArrayList implementeaza interfata List si, indirect, interfata Collection, definind toate metodele prevazute n aceste interfete. Metodele de inserare permit adaugarea unui element la sfrsitul listei (add (Object o)) sau ntr-o pozitie dorita (add(int index, Object o)). Metodele de stergere permit eliminarea unui element de la o pozitie data (remove(int index)), sau eliminarea tuturor elementelor dintr-un interval dat de pozitii (remove(int fromIndex, int toIndex)). Elementele se pot citi (fara a fi eliminate din lista) cu metodele get() si get(int index). Exemplul urmator (Colectie.java) evidentiaza modul de utilizare a unei colectii de tipul ArrayList, precum si problemele care pot sa apara n cazul conversiilor incorecte.
// // Colectie.java - Exemplu de container ArrayList // import java.util.*; class Mar { private int nr; Mar(int i) { nr = i; } void print() { System.out.println("Mar #" + nr); } } class Para { private int nr; Para(int i) { nr = i; } void print() { System.out.println("Para #" + nr); } } public class Colectie { public static void main(String[] args) { ArrayList mere = new ArrayList(); for(int i = 0; i < 7; i++) mere.add(new Mar(i)); // Se adauga pere la mere mere.add(new Para(7)); for(int i = 0; i < mere.size(); i++) ((Mar)mere.get(i)).print(); // Eroarea este detectata in timpul executiei } }
Colectia este creata ca o instanta a clasei ArrayList. n aceasta colectie se intoduc mai multe referinte de tip Mar si mai multe referinte de tip Par folosind metoda add. Dupa citirea elementelor din colectie (cu functia get()), trebuie sa fie facuta conversia referintei din referinta la tipul Object (asa cum a fost memorata) la tipul obiectului. Daca aceasta conversie se face incorect (ca n exemplul de mai sus, cnd
23 referinta la un obiect de clasa Para este convertita n referinta de clasa Mar), apare o excepte in cursul executiei. Rezutatul executiei acestui program se poate vedea n imaginea de mai jos :
2.5.2 Iteratori Un iterator este un obiect dintr-o clasa care implementeaza interfata Iterator si care permite parcurgerea elementelor unei colectii. Interfata Iterator prevede trei metode care se pot folosi pentru parcurgerea colectiilor: hasNext(), next() si remove(). boolean hasNext() returneaza valoarea true daca mai exista elemente de parcurs. Object next() returneaza referinta la urmatorul element din colectie. void remove() sterge din colectie ultimul element returnat de iterator. Un iterator se creeaza cu ajutorul metodei Iterator iterator() apelata pentru un obiect container. Acesta metoda este declarata n interfata Collection si implementata n fiecare clasa de colectii. Parcurgerea unei colectii folosind un iterator n locul functiei get()de citire a elementelor este, n general mai usoara, deoarece nu mai este necesar sa se compare numarul de elemente extrase cu numarul de elemente disponibile n container (aceasta operatie o face metoda hasNext() a iteratorului). n plus, interfata Iterator este aceeasi indiferent de tipul containerului, si deci metodele folosite pentru parcurgerea elementelor colectiei ramn aceleasi chiar daca se schimba tipul containerului. n exemplul urmator (Iteratori.java) se foloseste un iterator pentru parcurgerea si afisarea elementelor unei colectii ArrayList.
// // Iteratori.java // import java.util.*; class Numar{ int n; public Numar(int k){ n = k; } public String toString(){ return "n = " + n; } } public class Iteratori { public static void main(String[] args) { ArrayList numere = new ArrayList(); for(int i = 0; i < 7; i++) numere.add(new Numar(i));
// Se listeaza elementele folosind un iterator Iterator it = numere.iterator(); while (it.hasNext()){ Numar nr = (Numar)it.next(); System.out.println(nr); } } }
La executia acestui program se obtin urmatoarele date:
24
2.5.3 Clasa LinkedList Clasa LinkedList modeleaza o lista (secventa de elemente memorate ntr-o anumita ordine) printr- o structura de date de tip lista nlantuita. Pe lnga metodele de inserare, stergere si citire definite de interfata List (descrise la clasa ArrayList), clasa LinkedList mai defineste cteva metode utile de acces la elementele colectiei: adaugarea unui element la nceputul listei (void addFirst(Object o) sau la sfrsitul listei (void addLast(Object o)), eliminarea primului element (Object removeFirst()) sau a ultimului element din lista (Object removeLast()), citirea primului element (Object getFirst()) si a ultimului element (Object getLast()). Clasa LinkedList poate fi folosita pentru crearea altor modele de date mai speciale, cum sunt stivele (stacks) si cozile (queues).. Implementarea unei stive folosind clasa LinkedList. O stiva (stack) este un container de tipul ultimul introdus, primul extras LIFO: Last In, First Out) n care ultimul element (introdus cu operatia push) este extras cu operatia pop. O stiva se poate realiza foarte simplu folosind clasa LinkedList, asa cum se poate vedea n exemplul de mai jos (Stiva.java).
// // Stiva.java // import java.util.*; public class Stiva { private LinkedList list = new LinkedList(); public void push(Object v) {list.addFirst(v); } public Object top() { return list.getFirst(); } public Object pop() { return list.removeFirst();} public static void main(String[] args) { Stiva st = new Stiva(); for(int i = 0; i < 5; i++){st.push(new Integer(i));} System.out.println(st.pop()); System.out.println(st.pop()); System.out.println(st.pop()); System.out.println(st.pop()); System.out.println(st.pop()); } }
La executia acestui program se obtine urmatorul rezultat:
25 Implementarea unei cozi folosind clasa LinkedList. O coada (queue) este un container de tipul primul introdus, primul extras FIFO: First In, First Out) n care primul element (introdus cu operatia put) este extras cu operatia get. La fel ca si o stiva, o coada se poate realiza foarte simplu folosind clasa LinkedList, asa cum se poate vedea n exemplul de mai jos (Coada.java).
// // Coada.java // import java.util.*; public class Coada { private LinkedList list = new LinkedList(); public void put(Object v) { list.addFirst(v);} public Object get() { return list.removeLast(); } public static void main(String[] args) { Coada cd = new Coada(); for(int i = 0; i < 5; i++){ cd.put(new Integer(i)); } System.out.println(cd.get()); System.out.println(cd.get()); System.out.println(cd.get()); System.out.println(cd.get()); System.out.println(cd.get()); } }
La executia acestui program se afiseaza numerele exact n ordinea n care au fost introduse (0, 1, 2, 3, 4).
EXERCITII
3.1 Sa se creeze o clasa care sa contina ca data membra privata un vector (tablou unidimensional) de numere ntregi de dimensiune variabila, care se stabileste pentru fiecare obiect la constructia acestuia. n aceasta clasa redefiniti functia toString(), care sa afiseze numerele continute. n functia main() a clasei principale construiti un obiect din clasa creata care sa contina numerele 2, 9, 4, 5, 7, 8 si afisati continutul acestuia folosind functia toString() a clasei respective.
3.2 Sa se creeze o clasa Complex, pentru definirea unui numar complex, cu partea reala si imaginara ca numere de tip double. Definiti constructorii, functia de afisare (toString()), functia de testare a egalitatii a doua obiecte (equals()) , o functie care realizeaza adunarea a doua numere complexe. n functia main() creati doua obiecte din clasa Complex, cu valorile 2, 4, respectiv 5, 6 a partilor reale si imaginare. Verificati daca cele doua obiecte sunt egale si afisati rezultatul la consola. Calculati si afisati suma celor doua numere complexe.
3.3 Creati si executati programele Muzica.java si Muzica1.java, asa cum sunt descrise n lucrare. Testati diferitele cerinte de spacificatii ale claselor si metodelor abstracte (daca este posibil ca o metoda abstracta sa fie definita, ce se ntampla daca o clasa derivata nu defineste o metoda abstracta mostenita etc.). Comparati clasa abstracta cu interfata. Ce diferenta exista ?
3.4 Sa se creeze un program care sa contina urmatoarele clase si interfete: Clasa Flori care are o variabila membra private int petale; si functiile necesare pentru citirea si scrierea acestei variabile, constructori, functii utilitare (de ex. toString(), etc). Interfata Sepale care declara functiile int getSepale() si void setSepale(int s); Clasa Lalea derivata din clasa Flori, care implementeaza interfata Sepale. Clasa Ghiocel derivata din clasa Flori, care implementeaza interfata Sepale. n functia main() a programului sa se creeze un vector de referinte n care sa se introduca un obiect Lalea cu 4 petale si 5 sepale si un obiect Ghiocel cu 6 petale si 7 sepale. Afisati la consola obiectele din vector.
26
3.5 Sa se creeze un program care sa contina urmatoarele clase si interfete: Clasa Persoana care contine variabila membra private String nume, functiile necesare pentru citirea si scrierea acestei variabile, constructori, etc. Interfata Adresa care declara functiile abstracte String getAdresa() si void setAdresa(String s); Clasa Student care extinde clasa Persoana si implementeaza interfata Adresa. Clasa Profesor care extinde clasa Persoana si implementeaza interfata Adresa. n functia main() a programului sa se creeze un obiect Student, cu numele Marinescu si adresa Bucuresti si un obiect Profesor cu numele Popescu si adresa Ploiesti. Afisati la consola datele celor doua obiecte (numele si adresa).
3.6 Introduceti si executati programele de colectii prezentate n lucrare (Colectie.java, Iteratori.java, Stiva.java, Coada.java). Modificati programul Colectie.java, astfel nct elementele din lista sa fie parcurse folosind un iterator.
3.7* Clasa Collections contine mai multe metode statice care opereaza asupra colectiilor (la fel cum clasa Arrays contine metode statice care opereaza asupra tablourilor). Folosind documentatia Sun (docs) scrieti un program n care creati o colectie de obiecte de tip String, apoi gasiti si afisati sirul cel mai mare, cel mai mic sau un sir dat. ncercati aceeasi operatie pentru o colectie de obiecte dintr-o clasa proprie (de exemplu clasa Mar, din programul Colectie.java). Identificati cauza exceptiei care se obtine n timpul executiei si completati clasa Mar astfel nct sa suporte operatiile de comparatie necesare si sa otineti rezultatul corect.