Sunteți pe pagina 1din 346

Capitolul 1

Introducere in Java .......................................................................................................................................................... 2 Ce reprezinta Java? ...................................................................................................................................................... 2 Care sunt beneficiile Java? ........................................................................................................................................... 3 Ce poate oferi Java? ..................................................................................................................................................... 4 Un prim exemplu ......................................................................................................................................................... 5 Un prim exemplu de program Java ........................................................................................................................... 5 Scrierea programului ................................................................................................................................................ 5 Compilarea programului........................................................................................................................................... 5 Detalierea primului program .................................................................................................................................... 6 Erori de sintaxa, erori de runtime ............................................................................................................................. 7 Bazele limbajului .......................................................................................................................................................... 8 Variabile................................................................................................................................................................... 8 Conditii de numire a variabilelor..........................................................................................................................10 Domeniul de vizibilitate .......................................................................................................................................10 Initializarea variabilelor .......................................................................................................................................11 Operatori ................................................................................................................................................................11 Operatori aritmetici .............................................................................................................................................12 Operatori relationali ............................................................................................................................................13 Operatori conditionali .........................................................................................................................................13 Operatori de siftare .............................................................................................................................................14 Operatori de asignare ..........................................................................................................................................14 Alti operatori .......................................................................................................................................................15 Precedenta operatorilor ......................................................................................................................................15 Instructiuni de control .............................................................................................................................................16 Bloc de instructiuni ..............................................................................................................................................16 While ..................................................................................................................................................................17 For ......................................................................................................................................................................18 If else................................................................................................................................................................18 Switch .................................................................................................................................................................19

Introducere in Java
Ce reprezinta Java?
Java este unul dintre cele mai raspandite limbaje de nivel inalt insa acesta nu este principalul merit al sau. Acest limbaj a revolutionat programarea, in multe sensuri pe care le vom detalia in acest curs. Scopul acestei lucrari este de a prezenta la nivel mediu acest limbaj si de a-l utiliza pentru intelegerea conceptelor de structuri de date. Limbajul de programare Java este acel limbaj in care se pot scrie aplicatii, applet-uri, servlet-uri. Atunci cand un program Java este compilat, codul sursa va fi convertit in cod te tip byte code si anume un limbaj masina ce este portabil pe orice arhitectura CPU. Acest lucru este posibil datorita existentei unei masini virtuale JVM care faciliteaza interpretarea byte code in limbaj masina specific acelei masini pe care va fi programul va fi rulat. Platforma Java, limbajul Java si masina virtuala Java sunt trei lucruri distincte pe care le vom detalia in cele ce urmeaza. Platforma Java este multimea de clase Java care exista in orice kit de instalare Java. Aceste clase vor putea fi folosite de orice aplicatie Java care ruleaza pe calculatorul unde acestea au fost instalate. Platforma Java se mai numeste mediul Java (Java enviroment) sau kernelul Java API (Application Programing Interface). O alta denumire a aceste multimi de clase este si cea de framework. Clasele Java sunt grupate in colectii de clase numite pachete. Utilitatea acestora o vom detalia mai tarziu in acest curs. Pachetele sunt de asemenea organizate dupa rolul/functia lor ca de exemplu: pachete de retele, grafica, manipularea interfetelor cu utilizatorul, securitate, etc. Limbajul de programare Java este limbajul OO (orientat pe obiecte) asemanator cu C++, foarte puternic si usor de folosit si mai ales de invatat de catre programatori. Este rezultatul multor ani de lucru si inglobeaza un design elegant si functionalitati de ultima ora ceea ce il face destul de popular printre programatori. Versatilitatea, flexibilitatea, eficienta si portabilitatea sunt alte aspecte care propulseaza Java inaintea altora. Pe langa acestea faptul ca programatorii pot creea programe care pot rula in cadrul unor browsere sau web service-uri, sau ca pot creea aplicatii care sa ruleze pe diferite platforme, sau faptul ca pot creea programe ce sa ruleze pe aproape orice dispozitiv electronic mai nou (mobile, aparate medicale, industriale, la distanta etc), fac din acest limbaj unul foarte puternic. Masina virtuala Java constituie elementul fundamental Java. Programele Java sunt portabile pe orice sistem de operare, arhitectura hardware care suporta un interpretator Java. Sun, firma care realizeaza diverse kituri VM (Virtual Machine), suporta interpretatoare pentru platforme Solaris, Microsoft si Linux. De asemenea au fost creeate interpretatoare si pentru dispozitive ce au ca sisteme de operare Windows CE sau PalmOS.

Figura 1. Java poate rula pe orice sistem de operare/arhitectura hardware. Una din caracteristicile de baza a tehnologiei VM este compilarea just-in-time (JIT) unde byte code-ul Java este convertit la momentul executiei, in limbaj nativ. Astfel compilarea are loc doar odata, iar interpretarea ori de cate ori ruleaza programul. Pentru a vizualiza acest lucru in figura 2, avem cele doua evenimente schitate:

Figura 2. Compilarea si interpretarea unui program Java

Care sunt beneficiile Java?


In cadrul acestei sectiuni vom urmari cateva avantaje ale acestui care incearca sa raspunda la intrebarea fireasca: de ce sa utilizam Java cand avem alte limbaje OOP la dispozitie? 1. Scris odata va rula oriunde. Aceasta lozinca a firmei Sun este de fapt nucleul conceptual pe care s-a construit platforma Java. Altfel spus odata ce aplicatia a fost scrisa, ea va rula pe orice platforma ce suporta Java, fara a fi nevoie de modificari. Acesta este un avantaj asupra altor limbaje care trebuie rescrise (de cele mai multe ori total) pentru a rula pe alte sisteme de operare. 2. Securitate. Platforma permite utilizatorilor sa downloadeze cod prin retea intr-un mediu sigur: codul nesigur nu poate infecta sistemul gazda, nu poate scrie/citi fisiere pe hardisc etc. Aceasta capacitate facea ca Java sa fie unica pana la aparitia altor platforme concurente (.NET). 3. Programare orientata catre retele. Alt principu Sun spune ca Reteaua este computerul. Cei care au conceput Java credeau in importanta comunicarii prin retea si au avut in vedere acest fapt: Java faciliteaza folosirea resurselor prin retea si de a creea arhitecturi pe mai multe niveluri.

Programe dinamice. Programele scrise in Java sunt usor de extins deoarece organizarea este modulara si anume pe clase. Clasele sunt stocate in fisiere separate si incarcate de interpretator ori de cate ori si doar atunci cand este nevoie. Astfel o aplicatie Java apare ca o interactiune intre diverse componente software independente. Aceasta caracteristica este net superioara aplicatiilor ce constau dintr-un cod organizat ca un bloc monolitic. 5. Performanta. Masina virtuala Java ruleaza un program interpretand instructiuni portabile byte-code. Aceasta arhitectura inseamna ca programele Java sunt mai lente decat cele C, C++ care sunt compilate folosind cod nativ. Totusi, pentru eficienta, anumite portiuni ale Java, cum ar fi manipularea string-urilor folosesc instructiuni cod nativ. De la versiune la versiune acest neajuns a fost imbunatatit.

4.

Ce poate oferi Java?


Unelte de dezvoltare: aceste unelte reprezinta cam tot de ce are nevoie un programator si anume unelte pentru compilare, rulare, monitorizare, debug, documentare. In principiu se vor folosi javac compilatorul, java programul ce ruleaza aplicatiile Java, si javadoc pentru documentare. Application Programming Interface (API): Aceasta reprezinta nucleul de functii Java. Ofera o serie de clase folositoare ce le veti folosi in aplicatii. Acest nucleu este foarte mare, iar pentru a avea o idee despre ce contine avem imaginea de mai jos. In figura 3 sunt prezentate doua concepte JRE (Java Runtime Enviroment) si JDK (Java Development Kit). JRE ofera librariile, masina virtuala Java si alte componente necesare rularii applicatiilor si a applet-urilor. Acest mediu poate fi redistribuit odata cu aplicatiile pentru a le da autonomie. JDK include JRE si unelte cum ar fi compilatoare sau debuger-e ce sunt utile dezvoltatorilor. Unelte pentru interfete grafice: Swing si Java 2D sunt folosite pentru crearea de GUI (Graphical User Interfaces) si ofera facilitati pentru un design (si nu numai) frumos al aplicatiilor. Librarii pentru integrare: De exemplu Java IDL(Interface Definition Language) API, JDBC(Java Database Connectivity), Java Naming and Directory InterfaceTM ("J.N.D.I.") API, Java Remote Method Invocation etc. Aceste librarii permit accesul la baze de date sau manipularea obiectelor la distanta.

Figura 3.

Java Standard Edition

Un prim exemplu
Obtinerea JDK (Java Development Kit) Inainte de a incepe compilarea sau rularea programelor java, trebuie sa avem acest mediu instalat pe calculatoare. Pentru aceasta trebuie sa instalam JDK de la Sun (exista si alte pachete oferite de alte firme, insa vom folosi pe aceasta pentru ca este gratuit si constituie autoritatea principala a limbajului Java). Acesta poate fi downloadat de pe http://java.sun.com/ Un prim exemplu de program Java
/* Acesta este primul program Ce va fi scris intr-un fisier Example.java

*/ class Example { // Orice program Java incepe cu main(). public static void main(String args[]) { System.out.println("Hello Java!"); } }

Intotdeauna pasii care vor fi urmati in crearea unui program Java vor fi : 1. Scrierea programului. 2. Compilarea acestuia. 3. Rularea programului. Evident lucrurile se vor complica si pasii se pot detalia foarte mult. In cele ce urmeaza vom descrie pe scurt ce se intampla si cateva indicatii pentru fiecare din cei trei pasi. Scrierea programului

Pentru editarea unui program Java se pot folosi orice editoare: notepad, wordpad daca lucrati sub Windows, sau joe, nano, vi daca lucrati sub Linux. De asemena se pot folosi IDE(Integrated Development Enviroment) in cazul in care aplicatiile devin complexe. Pentru majoritatea limbajelor, numele fisierelor ce contin codul sursa, poate fi oricare. Pentru Java numele fisierului ce contine codul sursa este important. De exemplu in acest caz ar trebui sa fie Example.java. In Java un fisier Java se numeste o unitate de compilare. Este un fisier text care contine una sau mai multe definitii de clase. Compilatorul cere ca acest fisier sa aiba extensia java. Numele fisierului este Example iar acesta nu este o coincidenta cu numele clasei. In Java totul trebuie sa fie intr-o clasa. Numele clasei publice trebuie sa fie acelasi cu numele fisierului in care acea clasa se afla. Atentie, Java, ca si C, este case sensitive, adica Example este diferit de example sau de eXample. Compilarea programului

Pentru a compila un program se va executa compilatorul, javac, specificand numele fisierului sursa: C:\>javac Example.java

Compilatorul javac creeaza o clasa Example.class care contine byte code-ul corespunzator codului sursa din Example.java. Atentie, acest cod nu este executabil direct. El va fi executat de JVM. Pentru a rula programul, va trebui sa utilizam interpretatorul Java, si anume java: C:\>java Example Atunci cand codul Java este compilat, fiecare clasa individuala este plasata in fisierul numit ca si clasa, folosind extensia class. De aceea, este o idee buna sa dam fisierelor sursa acelasi nume ca si clasa pe care o contin. Atunci cand rulam interpretatorul, acesta va cauta fisierul Example care are extensia . class. Il va gasi si va executa codul continut in acea clasa. Detalierea primului program
/* */ Acesta este primul program Ce va fi scris intr-un fisier Example.java

Acesta este un comentariu pe mai multe linii: acesta incepe cu /* si se va termina cu */. Orice caracter dintre aceste doua simboluri va fi ignorat la compilare. Alt mod de a comenta cod ar fi pe o singura line ca mai jos:
int i =0;//Ceea ce urmeaza este un comentariu doar pe aceasta linie

Apoi urmeaza :
class Example {

Aceasta linie foloseste cuvantul class marcand inceputul definitie undei noi clase. Clasa este in Java, unitatea de baza pentru incapsulare. Clasa va fi definita in blocul delimitat de { si se va termina cu }. Momentan nu vom intra in detalii legate de continutul unei clase, acesta fiind aspectul urmatorului capitol din curs. Urmatorul rand din program este un comentariu pe o singura linie (cu referire la functia ce va urma). Urmatorul rand este metoda main sau entry point:
public static void main(String args[]) {

Se numeste astfel pentru ca reprezinta functia care este prima executata atunci cand interpretatorul java ruleaza programul. Toate aplicatiile Java incep executia cu apelul functiei main(). Cuvantul cheie public este un specificator de acces. Cuvintele cheie ale unui limbaj reprezinta acele cuvinte care alcatuiesc limbajul si definesc divesele instructiuni sau operatori sau tipuri de data, etc. Un specificator de acces determina modul in care alte clase din program pot accesa membrii unei clase. Membrul unei clase poate fi considerat o variabila sau o functie de exemplu. Cuvantul cheie static permite ca apelul functiei main() sa fie facut fara a fi nevoie de instantierea clasei din care aceasta functie face parte. Cuvantul cheie void specifica tipul de date returnat de functia main(), in cazul acesta nimic, adica spune compilatorului ca functia nu va returna nici o valoare. Intre parantezele functiei main() se afla lista de parametrii, in cazul de fata String args[]. Aceasta declaratie String args[] inseamna colectia de obiecte de tip String(sir de caractere). Ultimul caracter al acestei linii este { si anume faptul ca incepe corpul functiei main. Corpul functiei se termina cu } asemanator ca si corpul clasei. Urmatoarea parte din cod este:
System.out.println("Hello Java!");

Aceasta are ca efect la rulare afisarea mesajului Hello Java! Afisarea are loc datorita functiei gata definite, sau predefinite, println(). Linia totusi incepe cu System.out... In momentul acesta vom spune ca System este o clasa predefinita care ofera acces la sistem, si out este streamul de iesire conectat la consola. Practic System.out este un obiect care incapsuleaza iesirea la consola. Consola nu este des folosita in programele reale Java, sau in applet-uri, dar este utila la procesul de invatare Java.

Erori de sintaxa, erori de runtime Atunci cand scrieti un cod sursa, mai ales daca sunteti la inceput este posibil sa omiteti din neatentie sau nestiinta anumite portiuni din cod. Din fericire, daca ceva nu este corect, compilatorul va raporta aceste probleme ca erori de sintaxa.Compilatorul va incerca sa dea un sens codului sursa, indiferent in ce consta acesta. De aceea, eroarea raportata nu reflecta de cele mai multe ori cauza problemei! De exemplu daca omitem caracterul { din
public static void main(String args[])

La compilare va apare urmatorul mesaj: Example.java: 7 ; expected Public static void main(String[] args) Example.java:10: class, interface or enum expected } ^ 2 errors Evident ceva nu este in regula cu mesajul pentru ca nu lipseste ; ci {. Ideea este ca atunci cand programul contine o eroare de sintaxa, mesajul nu trebuie interpretat cuvant cu cuvant. Trebuie sa ne uitam la contextul in care apare eroarea, la liniile din jurul locului unde este indicata eroarea si sa deducem care este cauza reala a acesteia. Refacand codul si greseala de dinainte, sa inlocuim afisarea mesajului cu acest cod: System.out.println(args[1]); Ce realizeaza acest cod? Afisarea celui de al doilea argument transmis programului: argumentele sunt un sir care va fi scris la rularea programului. Compilam si rulam: java Example Urmatoarea eroare va apare: Exception in thread main java.lang.ArrayIndexOutOfBoundException : 1 at Example.main<Example.java:8> Acest tip de exceptie apare in momentul rularii programului (de aceea se numeste runtime error) si se datoreaza unor situatii neprevazute de programator. In cazul de fata se presupune ca avem argumente la rulare, cand realitatea este ca sirul de argumente este gol. Accesarea argumentului cu numarul 1 este astfel o greseala! Vom detalia tot intelesul acestei erori la momentul cuvenit.

Bazele limbajului
Inainte de a incepe sa exploram sintaxa Java, vom studia mai intai in mare alcatuirea unui program Java. Java consta din una sau mai multe unitati de compilare, adica fisiere ce contin clase. Fiecare unitate poate incepe cu un cuvant cheie optional package, urmata eventual de declaratii import. Acestea, asemanator directivei de include din C permit folosirea claselor din alte pachete. Vom discuta mai tarziu despre aceste aspecte. Apoi urmeaza definirea uneia sau mai multor clase sau interfete, dar mai nou si a structurilor enum. In cadrul claselor putem defini campuri(variabile), metode sau constructori. Toate acestea se numesc membrii clasei. Majoritatea metodelor vor contine instructiuni ce includ expresii, operatori, tipuri de data etc. Abordarea in continuare va fi exact de la unitatile de baza catre pachete in final. Pachetele contin clase, clasele contin metode. Aceasta relatie de incluziunie este marcata prin punct. Diferenta este ca in cazul pacheteleor . Semnifica o relatie de incluziune a directoarelor/folderelor iar in cazul claselor/metodelor incluziunea are lor in acelasi fisier. Vom insista asupra acestui aspect in capitolul 2.

Figura 4. Organizarea programelor Java

Variabile

Variabila este un element de un anumit tip de data identificat printr-un nume, utilizat la stocarea datelor. Numele variabilei denumit si identificator este compus din o serie de caractere ce incepe cu o litera. Tipul de data al variabilei determina ce valori poate lua acea variabila. Declaratia va fi de forma : tip nume

Pe langa nume si tip, o variabila mai are si un domeniu de vizibilitate. Sectiunea de cod unde acea variabila poate fi folosita se numeste domeniu de vizibilitate al variabilei. Sa analizam codul de mai jos pentru o intelegere mai buna acelor descrise mai sus: public class Variabile{ public static void main(String args[]) // integer byte largestByte; largestByte = Byte.MAX_VALUE; int largestInteger = Integer.MAX_VALUE; // real float largestFloat = Float.MAX_VALUE; double largestDouble = Double.MAX_VALUE; // afisarea System.out.println("Valoarea maxima byte este " + largestByte); System.out.println("Valoarea maxima integer value este " + largestInteger); System.out.println("Valoarea maxima float value este " + largestFloat); System.out.println("Valoarea maxima double value este " + largestDouble); } } Clasa Variable contine o metoda main() si nici o variabila. Da, este corect, nici o variabila, deoarece domeniul in care cele patru variabile sunt vizibile, este cel al functiei main(). Sa luam declaratia variabilei byte largestByte; Prin aceasta instructiune se declara o variabila numita largestByte care are tipul de data byte. Acest tip de data este o valoare intreaga cu semn cuprinsa intre -128 si 127. Vom detalia in tabelul 1 tipurile primitive de date. Java contine doua categorii de tipuri de date: referinta si primitive. O variabila de tip primitiv poate contine doar o singura valoare conform cu acel tip si avand formatul acelui tip. Evident tipurile referinta sunt mai complexe si putem spune ca inglobeaza si tipuri primitive.

Tabelul 1. Tipurile primitive Cuvant cheie byte short int long float double Alte tipuri char boolean Descriere Intreg de lungime byte Intreg short Intreg Intreg short Real, virgula mobila cu o singura precizie Real, virgula mobila cu o dubla precizie Un singur caracter Unicode Valoarea boolean-a (true sau false) Marime 8-bit cu semn 16-bit cu semn 32-bit cu semn 64-bit cu semn 32-bit format IEEE 754 64-bit format IEEE 754 16-bit caracater Unicode 8-bit/1-bit (8 bits de spatiu, 1 bit de data)

Integer

Real

Clasele, sirurile de data si interfetele sunt tipuri de data referinta. Valoarea unei variabile de tip referinta este adresa unui obiect. O referinta este denumita si obiect, sau adresa unei memorii in alte limbaje, insa in Java nu avem posibilitatea de a accesa ca in C zona de memorie direct.

Figura 5. Variabila de tip referinta contine adresa obiectului

Conditii de numire a variabilelor Pentru ca numele unei variabile sa fie valid trebuie sa indeplineasca urmatoarele conditii: 1. Trebuie sa inceapa cu o litera si sa fie alcatuit din caractere Unicode 2. Nu trebuie sa fie un cuvant cheie. 3. Trebuie sa fie unic in domeniul de vizibilitate. Domeniul de vizibilitate

Domeniul de vizibilitate al unei variabile este regiunea din program in care se poate face referire la acea variabila. Alt scop al domeniului este de a determina cand sistemul creeaza sau distruge memoria alocata pentru o variabila.

Figura 6. Diverse categorii de domenii de vizibilitate ale variabilelor Initializarea variabilelor

Variabilele locale si membre trebuie initializate inainte de a fi folosite. Initializarea inseamna atribuirea de valori acestora: int largestInteger = Integer.MAX_VALUE; sau int largestInteger = 23; Cuvantul cheie final in cazul variabilelor semnifica faptul ca o variabila nu mai poate fi modificata odata ce a fost initializata, fiind practic o constanta. final int largestInteger = 0; Operatori

Un operator indeplineste o functie, pentru unul doi sau trei operanzi. Un operator care necesita doar un operand este denumit unar. De exemplu ++ este operator unar care incrementeaza valoarea operandului cu 1. Un operator care necesita doi operanzi se numeste binar. De exemplu = este un operator ce asigneaza o valoare operandului din stanga, valoarea fiind a operandului din dreapta. Un operator cu trei operanzi ar fi ?: si vom vorbi imediat despre el.

Operatorii unari pot avea notatie prefixata ,infixata sau postfixanta: operator op - notatie prefixata op operator notatie postfixata op1 operator op2 notatie infixata In cele ce urmeaza vom vorbi de diversele categorii de operatori exemplificand utilizarea lor. Operatori aritmetici

Acestia realizeaza operatiunile matematice de baza dupa tabelul de mai jos Tabelul 2. Operatori aritmetici Operator + * / % Folosire op1 + op2 op1 - op2 op1 * op2 op1 / op2 op1 % op2 Descriere Adauga op1 and op2; de asemenea concateneaza stringuri scade op2 din op1 inmulteste op1 cu op2 divide op1 la op2 Calculeaza restul impartirii lui op1 la op2

Pe langa acesti operatori mai exista si operatori unari aritmetici: Tabelul 3. Operatori aritmetici unari Operator Folosire Descriere + +op Transforma op in int daca este byte, short sau char -op Aplica negatia aritmetica asupra lui op

Alti operatori aritmetici sunt ++ care incrementeaza cu 1 operandul si -- care decrementeaza cu 1 operandul: Tabelul 4. Operatori de incrementare/decrementare Operator Folosire ++ op++ incrementeaza op cu ++ ++op incrementeaza op -op-decrementeaza op cu ---op decrementeaza op Descriere evaluarea lui op 1; evaluarea lui evaluarea lui op 1; evaluarea lui

1; cu 1; cu

inainte op dupa inainte op dupa

de incrementare incrementare de decrementare decrementare

Operatori relationali

Acestia compara doua valori si determina relatia dintre ele. Tabelul 5. Operatori relationali Operator > >= < <= == != Folosire op1 > op2 op1 >= op2 op1 < op2 op1 <= op2 op1 == op2 op1 != op2 Descriere este mai mare decat op2 este mai mare sau egal cu op2 este mai mic decat op2 este mai mic sau egal cu op2 este egal cu op2 este diferit de op2

Returneaza Returneaza Returneaza Returneaza Returneaza Returneaza

true true true true true true

daca daca daca daca daca daca

op1 op1 op1 op1 op1 op1

Operatori conditionali

Acestia returneaza o valoare true sau false evaluand operanzii aferenti. Tabelul 6. Operatori conditionali Operator Folosire && op1 && op2 Returneaza true conditional op2 || op1 || op2 Returneaza true conditional op2 ! !op Returneaza true & op1 & op2 Returneaza true true; evalueaza Descriere daca op1 and op2 sunt true; evalueaza daca op1 sau op2 este true; evalueaza daca op este false daca op1 si op2 sunt boolean si amandoi sunt intotdeauna op1 si op2

op1 | op2

Daca ambii operanzi sunt numere, efectueaza SI pe biti Returneaza true daca ambii op1 si op2 sunt de tip boolean, si atat op1 cat si op2 sunt true; evalueaza intotdeauna op1 si op2 Daca ambii operanzi sunt numere, efectueaza SAU pe biti Returneaza true daca op1 si op2 sunt diferiti, adica un XOR pe biti

op1 ^ op2

Operatori de siftare Tabelul 6. Operatori de siftare Operator Folosire << op1 << op2 >> op1 >> op2 >>> op1 >>> op2 Descriere bitilor lui op1 la stanga cu o lungime data de op2; zero bitii din partea dreapta bitilor lui op1 la dreapta cu o lungime data de op2; bitul cel mai semnificativ (de semn) bitii din partea

Siftarea umple cu Siftarea umple cu stanga Siftarea bitilor lui op1 la dreapta cu o lungime data de op2; umple cu zero bitii din partea stanga

Operatori de asignare Operatorul = este cel implicit de asignare. De exemplu daca dorim sa adaugam o valoare la o variabila putem scrie i = i+2; De asemenea putem prescurta aceasta operatie astfel: i += 2; Acest lucru este valabil pentru diverse operatii: Tabelul 7. Operatori de asignare Operator += -= *= /= %= &= |= ^= <<= >>= >>>= Folosire op1 += op2 op1 -= op2 op1 *= op2 op1 /= op2 op1 %= op2 op1 &= op2 op1 |= op2 op1 ^= op2 op1 <<= op2 op1 >>= op2 op1 >>>= op2 Echivalent = op1 + op2 = op1 - op2 = op1 * op2 = op1 / op2 = op1 % op2 = op1 & op2 = op1 | op2 = op1 ^ op2 = op1 << op2 = op1 >> op2 = op1 >>> op2

Scurtaturi pentru operatorii aritmetici

Scurtaturi pentru operatorii pe biti Scurtaturi pentru siftare

op1 op1 op1 op1 op1 op1 op1 op1 op1 op1 op1

Alti operatori Vom aminti aici si o lista cu operatorii care nu au fost inclusi in celelalte liste Tabelul 8. Alti operatori Operator ?: [] . (params ) (tip ) New Instanceof Descriere Scurtatura la o anumita instructiune if-else Folosit la lucrul cu siruri Pentru accesarea membrilor, a claselor. Lista de parametrii (unei metode, sau instructiuni) Operator cast: schimba tipul de data al unei variabile Creeaza un nou obiect sau sir. Determina daca primul operand este instanta celui de-al doilea operand

Precedenta operatorilor Cand folosim operatorii in combinatie cu operanzi obtinem expresii. Expresiile ajuta la calcularea si asignarea de valori variabilelor si ajuta la contorul fluxului logic al unui program. Expresia este o serie de variabile, operatori, apeluri care rezulta in final intr-o singura valoare. Exemple de expresii: x * y * z x+y/10 (x+y)/100 //ambigua // clara, asa este recomandat

Atunci cand se va face evaluarea expresiilor se va tine cont ce operator va fi prima data evaluat: Tabelul 9. Precedenta operatorilor Precedenta cea Operatori postfixati mai mare Operatori unari Operatori creeare sau cast Operatori de multiplicare Operatori aditivi Operatori de siftare Operatori relationali Operatori de egalitate SI pe biti XOR pe biti SAU pe biti SI logic SAU logic Scurtatura if-else Precedenta cea asignare mai mica [] . (params ) expr ++ expr -++expr --expr +expr -expr ~ ! new (type )expr * / % + > >>> < > <= >= instanceof == != & ^ | && || ?: = += -= *= /= %= &= ^= |= <<= >>= >>>=

Instructiuni de control Bloc de instructiuni

Un bloc de instructiuni este cuprins intre { si } si poate fi privit ca o singura instructiune. De exemplu: if (i>0) { System.out.println(i este pozitiv); } else { System.out.println(i este negativ); } Acest cod poate fi scris si asa: if (i>0) System.out.println(i este pozitiv); else System.out.println(i este negativ); Atunci la ce sunt folositoare acoladele? Daca de exemplu dorim ca atunci cand i este mai mare ca zero, nu numai sa afisam un mesaj despre asta dar sa decrementam aceasta valoare (motivatii pot fi multe ) atunci trebuie sa facem intr-o instructiune compusa: if (i>0) { System.out.println(i este pozitiv); i--; } else { System.out.println(i este deja negativ); }

In cele ce urmeaza vom descrie instructiunile de baza pentru controlul logicii unui program: Tabelul 10. Instructiuni de control Tipul de instructiune Bucla De decizie Tratarea erorilor Ramificare While Cuvinte cheie while, do-while, for if-else, switch-case try-catch-finally, throw break, continue, label :, return

Forma generala a instructiunii while este: While (expresie) { ... lista de instructiuni } ... instructiuni dupa blocul while Prima data se evalueaza expresia dintre paranteze care trebuie sa returneze o valoare booleana. Daca ea returneaza true atunci se executa lista de instructiuni din corpul while. Se revine la evaluarea expresiei, daca ea returneaza true se executa a doua oara lista de instructiuni pana cand expresia va returna false. In acel moment se sare peste corpul de instructiuni din while la prima instructiune ce urmeaza dupa blocul while. Exemplu: int i=0; while (i<10) { System.out.println(i++); } System.out.println("Dupa while"); Se vor executa afisarile 0 pana la 9 si la sfarsit mesajul Dupa while. Instructiunea do while are aproape acelasi efect singura diferenta este ca evaluarea expresiei se va face la sfarsitul blocului de instructiuni, astfel ca acele instructiuni se vor efectua cel putin odata: do { lista de instructiuni } while (expresie);

For Ca si while permite executarea instructiunilor in bucla. In plus permite iterarea unor variabile intr-un mod compact: for (initializari; conditie de terminare; incrementare) { ...lista de instructiuni } Initializari reprezinta secventa de instructiuni care se executa la intrarea in bucla; se executa doar odata la inceput. Conditie de terminare reprezinta conditia care determina iesirea din bucla. Incrementare este o instructiune sau un set de instructiuni care este efectuat la fiecare iteratie prin bucla. Exemplu: for (int i=0,j=1; (i<10 || j<16);i++, j+=2) { System.out.println(i+" " + j); } System.out.println("Dupa for"); Se vor afisa in paralel valorile lui i pana la 9 si lui j pana la 19. If else

Aceasta instructiune permite executarea unor instructiuni doar in anumite cazuri: in acest fel se poate separa logica programului. In cea mai simpla forma instructiunea coditie poate fi scrisa asa: if (expresie) { ... lista instructiuni } Daca expresia va fi evaluata cu valoarea true, atunci lista de instructiuni este executata, altfel se sare peste. Ce se intampla daca insa conditia este false si doar in acel caz vrem sa executam alte instructiuni? In acest caz avem instructiunea compusa if else de exemplu: if (response == OK) { // code to perform OK action } else { // code to perform Cancel action } Mai departe, sa cosideram ca avem urmatorul scenariu: avem o variabila care poate lua valori de la 1 la 12 si in functie de valoarea ei de la un moment dat vrem sa afisam luna calendaristica, corespunzatoare ei.

int month = 8; if (month == 1) System.out.println("Ianuarie"); else if (month == 2) System.out.println("Februarie"); else if (month == 3) System.out.println("Martie"); else if (month == 4) System.out.println("Aprilie"); else if (month == 5) System.out.println("Mai"); else if (month == 6) System.out.println("Iunie"); else if (month == 7) System.out.println("Iulie"); else if (month == 8) System.out.println("August"); else if (month == 9) System.out.println("Septembrie"); else if (month == 10) System.out.println("Octombrie"); else if (month == 11) System.out.println("Noiembrie"); else if (month == 12) System.out.println("Decembrie"); Problema este ca pentru a evalua care din instructiuni trebuie executate se fac multe verificari, degeaba.Alta instructiune care realizeaza acelasi lucru mai eficient este switch Switch int month = 8; switch (month) { case 1: System.out.println("Ianuarie "); break; case 2: System.out.println("Februarie "); break; case 3: System.out.println("Martie "); break; case 4: System.out.println("Aprilie"); break; case 5: System.out.println("Mai "); break; case 6: System.out.println("Iunie "); break; case 7: System.out.println("Iulie "); break; case 8: System.out.println("August"); break; case 9: System.out.println("Septembrie "); break; case 10: System.out.println("Octombrie "); break; case 11: System.out.println("Noiembrie "); break; case 12: System.out.println("Decembrie "); break; }

In cazul instructiunii switch se va extrage valoarea variabilei sau expresiei dintre paranteze, in cazul de fata month si in functie de valoarea acelei expresii se va executa una din ramificatiile case. Dupa ce se executa instructiunea din dreptul case, se va executa si instructiunea break, altfel se vor efectua si celelate instructiuni din switch care urmeaza. Spre exemplu: int month = 8; switch (month) { case 1: System.out.println("Ianuarie "); break; case 2: System.out.println("Februarie "); break; case 3: System.out.println("Martie "); break; case 4: System.out.println("Aprilie"); break; case 5: System.out.println("Mai "); break; case 6: System.out.println("Iunie "); break; case 7: System.out.println("Iulie "); break; case 8: System.out.println("August"); case 9: System.out.println("Septembrie "); case 10: System.out.println("Octombrie "); case 11: System.out.println("Noiembrie ");break; case 12: System.out.println("Decembrie "); } Va afisa August dar si lunile ce urmeaza dupa, pana la intalnirea unui break si anume Noiembrie(inclusiv).

Capitolul2
Capitolul 2: clase, obiecte, metode .......................................................................................................... 2 Alte instructiuni de control .................................................................................................................. 2 Break ............................................................................................................................................... 2 Continue .......................................................................................................................................... 3 Bucle repetitive imbricate ................................................................................................................ 4 Operatorul conditional..................................................................................................................... 5 Programare orientata pe obiecte ......................................................................................................... 5 Obiecte ............................................................................................................................................ 5 Clasa ................................................................................................................................................ 6 Forma generala a unei clase............................................................................................................. 7 Functii ........................................................................................................................................... 11 Constructori ................................................................................................................................... 16 Constructori cu unul sau mai multi parametrii ............................................................................... 17 Garbage Collection si finalizatori .................................................................................................... 19 Metoda finalize.............................................................................................................................. 20 Cuvantul cheie this ........................................................................................................................ 21 Pachete ............................................................................................................................................. 22 Definirea pachetelor ...................................................................................................................... 23 Pachete si CLASSPATH ................................................................................................................... 23 Importarea pachetelor ................................................................................................................... 25

Capitolul 2: clase, obiecte, metode


Alte instructiuni de control
Break Se poate uneori forta parasirea unei bucle repetitive(for, while), utilizand instructiunea break. In cele ce urmeaza, conform exemplelor anterioare am realizat o bucla for, dar de data aceasta nu mai exista conditie de continuare, deci oprirea nu poate avea loc decat din cadrul acestui for:
class BreakSample { public static void main(String args[]) { int num; num = 10; // continua la "nesfarsit", conditia de continuare nu este for(int i=0; ; i++) { if(i >= num) break; // i a ajuns la 10, se iese din bucla System.out.print(i + " "); } System.out.println("S-a terminat bucla."); } }

Acest program va genera urmatoarea secventa:


0 1 2 3 4 5 6 7 8 9 S-a terminat bucla.

Dupa cum se poate vedea acest tip de instructiune are loc in conjunctura cu instructiuni conditionale: altfel s-ar executa doar odata bucla, eliminand caracterul de repetitivitate al acesteia. Alte scopuri ale utilizarii acestei instructiuni pot fi, spre exemplu, citirea repetitiva unor caractere: in momentul in care ceea ce am citit este egal ca valoare cu caracterul ce marcheaza oprirea citirii (de exemplu q), parasim imediat bucla repetitva in care are loc citirea.
char ch; for( ; ; ) { ch = (char) System.in.read(); // citesc un caracter if(ch == 'q') break; }

Alt mod de a folosi break, pe langa cele mentionate, este ca si instructiune de salt la o anumita eticheta. Java nu are o instructiune clasica goto deoarece aceasta permite un flux logic nestructurat. 2

Programele care folosesc tipul acesta de instructiuni sunt greu de urmarit si de mentinut, de aceea expunerea este pur informativa, si nu recomand folosirea break in acest scop. Forma generala a unui break cu sens de goto este:
break eticheta;

In acest caz, eticheta este numele etichetei care identifica un bloc de instructiuni. Atunci cand se ajunge la instructiunea break eticheta; se transfera controlul acelui bloc indicat de numele eticheta. Iata un exemplu pentru a ilustra pe scurt functionarea acestei instructiuni:
class BreakLabel { public static void main(String args[]) { int i; int count=0; et1: for(i=++count; ; i++) { System.out.println("\n i este " + i); if(i>2) break et1; } System.out.println("Dupa blocul for."); } }

Se poate observa ca dupa trei rulari a instructiunii for, se va executa instructiunea break cu directionare catre eticheta et1. Se va afisa mesajele i este 1, i este 2, i este 3 si se executa acel break cu salt la et1. Cand are loc acest fapt, controlul se va preda blocului imediat urmator si anume afisarea mesajului Dupa blocul for. Continue

Aceasta instructiune este folosita intr-o bucla repetitiva permitand ca, in cazul in care nu vrem sa continuam cu instructiunile din acea bucla repetitiva, instructiuni care urmeaza dupa continue, sa trecem la pasul urmator. Practic se sare la urmatorul pas din instructiunea repetitva ignorand ce se intampla dupa continue. De exemplu daca vrem sa afisam numerele pare dintre 0 si 50:

class ContinueSample { public static void main(String args[]) { int i; // afisez numere pare intre 0 si 50 for(i = 0; i<=50; i++) { if((i%2) != 0) continue; // daca nu e par trec la //urmatorul pas din for System.out.println(i); } } }

In acest exemplu for fi afisate numerele pare deoarece: pe instructiunea conditionala se intra doar daca restul impartirii numarului curent la 2 este zero. Atunci cand se intra (deci cand numarul este impar) se efectueaza continue, adica salt la urmatorul i din for si nu se mai executa afisarea i-ului curent. Bucle repetitive imbricate

Inainte de a incheia acest subcapitol si anume al instructiunilor de baza vom exemplifica folosirea buclelor repetitve una in interiorul celeilalte. Astfel in cele ce urmeaza putem calcula factorii fiecarui numar de la 2 la 50 in felul urmator: se parcurge fiecare numar de la 2 la 50; in clipa in care am facut un pas suntem in pozitia in care putem calcula factorii numarului pe care ne aflam sa zicem X. In acest moment urmeaza o alta bucla pentru a parcurge numerele pana la X (poate fi evident optimizat). In ultima bucla se verifica restul impartirii lui X la numerele din intervalul [2..X), daca este zero inseamna ca am gasit un factor al lui X:
class Factors { public static void main(String args[]) for(int i=2; i <= 50; i++) { System.out.print("Factorii lui " + i + ": "); for(int j = 2; j < i; j++) if((i%j) == 0) System.out.print(j + " "); System.out.println(); } } } {

Operatorul conditional Acest operator ?: este un operator ternar mostenit din C. Permite inglobarea unei conditii intr-o expresie. Primul operand este separat de al doilea operand prin ? in timp ce al doilea operand este separat de al treilea prin :. Primul operand trebuie sa fie de tip boolean. Al doilea si al treilea operand vor fi de orice tip de data insa de acelasi tip de data, sau convertibil catre acelasi tip de data. Cum se evalueaza acest operator? Se evalueaza primul operand, daca e true operatorul evalueaza al doilea operand si ii va folosi valoarea. Daca este false operatorul evalueaza al treilea operand si ii returneaza valoarea. De exemplu:
int max = (x>y) ? x : y; String nume = (nume!=null) ? nume : necunoscut;

max va lua valoarea lui x daca x este mai mare ca y, altfel va lua valoarea lui y.

Programare orientata pe obiecte


Obiecte

Obiectele sunt principalul concept din cadrul acestei tehnologii. Obiectele reale ce ne inconjoara, ca masa, televizorul, unelte, animale etc, au doua caracteristici in comun: stare si comportare. De exemplu animalele au stari (nume, colorit, specie) si comportare (mod deplasare, mod de a respira, de a se hrani). Bicicletele au stari (viteza curenta, greutate, cadenta pedalarii) si comportare (accelerare, franare, schimbarea treptelor de viteza). Obiectele sunt modelate in programare prin una sau mai multe variabile, iar comportarea obiectelor este modelata prin metode.

Figura 2.1. Obiectul modelat software 5

Tot ce se stie despre un obiect reprezinta starea lui exprimata prin variabile si ce poate obiectul face, reprezinta comportamentul sau reprezentat prin metode. De exemplu un obiect software care modeleaza o bicicleta va avea variabile ce indica starea curenta a bicicletei: viteza de 10 km/h, cadenta pedalelor de 90 rpm, si treapta de viteza curenta (a treia). Pe langa variabile, o bicicleta reprezentata software poate avea metode pentru a frana, schimba treapta de viteza. Totusi nu poate avea metoda de schimbare a vitezei pentru ca viteza este consecinta cadentei de pedalare, a franarii/sau nu, a pantei. Eventual poate avea metoda de redare a vitezei calculata pe baza acestor factori. Aceste metoda se numesc metode de instanta pentru ca evalueaza sau modifica o stare a unei anume biciclete si nu tuturor bicicletelor.

Figura 2.2 Un obiect bicicleta modelat ca obiect software Diagramele de mai sus semnaleaza faptul ca variabilele formeaza centrul, nucleul obiectelor, iar metodele inconjoara acest nucleu. Impachetarea variabilelor obiectului pentru a proteja informatia continuta de ele, se numeste incapsulare. Clasa In lumea reala, multe obiecte pot fi categorizate ca fiind de acelasi tip. De exemplu automobilul detinut de cititor este la aproximativ la fel cu orice automobil din aceasta lume. Au roti pe care se deplaseaza, motorul cauzeaza deplasarea, si este nevoie de combustibil (sub o forma sau alta) pentru a alimenta motorul. Asemenea si o bicicleta, va avea roti, un sistem de pedalare, una sau mai multe viteze etc. Putem spune ca o anume bicicleta de exemplu Atomik Mountain Bike este o instanta a clasei bicicleta. Se cheama ca am construit un obiect cu anume caracteristici (culoare, rezistenta) dar care arata ca o bicicleta si se comporta asemenea. Alta alegorie pentru a intelege mai bine diferenta intre obiect si clasa este o cel compus dintr-o matrita de imprimat bancnote si bancnota. Hartia, bancnota propriu zisa este obiectul ca instanta a clasei matrita care va imprima sute de hartii toate cu aceleasi caracteristici dar comportamente diferite. Intr-o clasa, atat variabilele cat si metodele se vor numi membrii acelei clase. 6

Forma generala a unei clase

Clasa este creata folosind cuvantul cheie class ca mai jos:


class numeclasa { //declar variabilelor clasei, ce vor fi disponibile in fiecare instanta type var1; type var2; // ... type varN; // declare metodele type metoda1(parametri) { //corpul metodei 1 } type metoda2(parametri) { //corpul metodei 2 } // ... type metodaN(parametri) { //corpul metodei N } }

Acesta este sintaxa generala de definire a unei clase, se poate ca o clasa sa nu contina decat o variabila, sau o metoda, sau niciuna. Pentru a ilustra conceptul de clasa, vom scrie o clasa ce cuprinde informatiile despre automobile. Aceasta clasa se numeste Automobil, si va contine trei tipuri de informatii ca: model, consum, viteza maxima. Mai jos avem definitia clasei cu cele trei variabile:
class Automobil { String model; //combi, SUV, MCV, caminon etc

int consum; / 5 .. 15 int viteza; //viteza maxima ce poate fi atinsa: 180 .. 340 }

Noul tip de data se numeste Automobil, si orice obiect de acest tip va contine cele trei variabile membru. Atentie, codul de mai sus este doar o descriere a tipului si nu prezinta crearea unui obiect.

Pentru a instantia un nou obiect de tipul Automobil se va folosi operatorul new:


Automobil minivan = new Automobil(); //Creez un nou obiect

In urma acestei instructiuni, minivan devine instanta clasei Automobil. De fiecare data cand cream o nou instanta a unei clase, se aloca acelui obiect nou creat un spatiu de memorie necesar membrilor clasei din care obiectul face parte. Fiecare obiect de tip Automobil va contine propriile copii ale instantelor variabilelor model, consum, viteza. Pentru a accesa aceste variabile se va folosi operatorul .. Acesta expune membrii (atat variabile cat si metode) unui obiect:
object.member

Spre exemplu putem seta minivan-ului un anume consum:


minivan.consum = 10;

In sectiunea de cod ce urmeaza vom vedea cum in alta clasa, utilizam obiecte de tip Automobil:
class Automobil { String model; //combi, SUV, MCV, caminon etc

int consum; // 5 .. 15 int viteza; //viteza maxima ce poate fi atinsa: 180 .. 340 } public class AutomobilDemo { public static void main(String[] args) { Automobil minivan = new Automobil(); minivan.model = "Logan MCV"; minivan.consum =10; minivan.viteza = 180; System.out.println(minivan.model + " are un consum de " + minivan.consum + " si viteza maxima " + minivan.viteza); } }

Inainte de a trece mai departe vom prezenta un alt exemplu in care lucram cu doua obiecte, instante ale aceleasi clase. Scopul este pentru a clarifica faptul ca variabilele continute de un obiect pot diferi ca si valoare de variabilele continute de alt obiect, chiar daca cele doua obiecte au aceeasi clasa, deci sunt de acelasi tip:
class Automobil { String model; //combi, SUV, MCV, caminon etc

int consum; // 5 .. 15 int viteza; //viteza maxima ce poate fi atinsa: 180 .. 340 } public class AutomobilDemo { public static void main(String[] args) { Automobil minivan = new Automobil(); Automobil masinasport = new Automobil(); minivan.model = "Logan MCV"; minivan.viteza = 180;

masinasport.model = "BMW"; masinasport.viteza = 320;

minivan.viteza);

System.out.println(minivan.model +" are viteza maxima " +

System.out.println(masinasport.model +" are viteza maxima " + masinasport.viteza); } }

In urma rularii acestui cod se va obtine:


Logan MCV are viteza maxima 180 BMW are viteza maxima 320

Ce se intampla la crearea obiectelor? Atunci cand apelam operatorul new ca in instructiunea:


Automobil minivan = new Automobil();

prima data are loc declararea variabilei minivan: Automobil minivan 9

iar apoi instantierea unui nou obiect new Automobil() pentru ca mai apoi copia noului obiect sa fie atribuita variabilei minivan. Operatorul new aloca dinamic memorie la momentul rularii programului, si returneaza referinta obiectului pentru care a alocat memorie. Aceasta referinta este adresa din memorie a noului obiect alocat. Astfel in Java, toate obiectele trebuie alocate dinamic. Pentru a disocia cele doua notiuni de declarare si instantiere separam instructiunea de mai sus:
Automobil minivan; //declaram unu obiect de tip referinta Automobil

Minivan = new Automobil(); //alocam memorie pentru un obiect de tip //Automobil

Asignarea obiectelor

In cazul obiectelor, operatorul de asignare si anume = actioneaza altfel decat in vazul variabilelor primitive de tip int. Atunci cand asignam o variabila de tip primitiv cu valoarea altei variabile de tip primitiv, variabila din stanga operatorului primeste o copie a valorii variabilei din dreapta. Atunci cand asignam un obiect catre alt obiect, lucrurile se schimba, deoarece obiectele inglobeaza mai multe variabile. De exemplu:
Automobil masina1 = new Automobil(); Automobil masina2 = masina1;

La prima vedere, este usor de spus ca masina1 si masina2 se refera la obiecte diferite, insa nu este deloc asa: atat masina1 cat si masina2 se vor referi la acelasi obiect. De aceea atunci cand vom afisa cosumul ambelor vom avea surpriza sa constatam ca este acelasi.
System.out.println(masina1.consum); System.out.println(masina2.consum);

Alt exemplu pentru a intelege si mai bine asignarea:


Automobil masina1 = new Automobil(); Automobil masina2 = masina1; Automobil masina3 = new Automobil; masina2 = masina3; //acum atat obiectul masina2 si masina3 se //refera la acelasi obiect, iar masina1 este //neschimbat si independent de celelalte doua

10

Functii

In exemplele de mai sus, clasa Automobil contine date sub forma unor variabile, insa nici o metoda. Desi clasele ce contin doar variabile sunt perfect valabile, majoritatea vor contine metode, pentru asigurarea unui flux logic. Metodele sunt functii, subrutine care trateaza, manipuleaza date definite in clase, pentru a asigura logica dorita. O metoda consta din semnatura acesteaia si corpul ei. Semnatura metodei se refera la tipul de data returnat, nume si lista parametrilor iar corpul inseamna blocul de instructiuni executat ori de cate ori functia este apelata. O metoda contine una sau mai multe instructiuni, gandite ca impreuna sa efectueze o anumita sarcina. Metodele au nume, prin care sunt identificate in cadrul clasei, iar dupa nume intotdeauna urmeaza paranteze. Intre paranteze sunt declarati parametrii metodei (variabile de diverse tipuri). Aceste variabile vor lua valori in momentul apelului metodei. O metoda generala are forma:;
tip_de_returnat nume(lista de parametrii) { //corpul metodei }

Tipul_de_returnat reprezinta orice tip de data valid, inclusiv clase pe care le creati; ca exemple de tip de data avem int, Integer, String, double, Automobil. In cazul in care metoda nu returneaza nici un tip de data acesta va fi void. Numele metodei poate fi orice identificator valabil, si respecta ca si formare, denumirea variabilelor (a se revedea conditiile de numire a variabilelor). Lista de parametrii este o secventa de declaratii de variabile/parametrii ce vor fi vizibili in cadrul metodei, separati prin virgula:
int myFunction(double param1, Integer param2, Automobil minivan) { //... instructiuni din corpul metodei return param2; }

Adaugarea unei metode intr-o clasa

Sa luam spre exemplu clasa Automobil de mai sus, si clasa AutomobilDemo, la care mai adaugam o metoda pe langa metoda deja existenta si anume PrintProperties. Aceasta este declarata cu static, deoarece va fi apelata dintr-o metoda statica si anume main. Alt mod de a rezolva aceasta problema era sa cream un nou obiect de tip AutomobilDemo in main si sa apelam metoda prin acel obiect dupa cum vom vedea imediat dupa exemplul de mai jos:

11

class Automobil { String model; //combi, SUV, MCV, caminon etc

int consum; // 5 .. 15 int viteza; //viteza maxima ce poate fi atinsa: 180 .. 340 } public class MethodDemo { public static void main(String[] args) { Automobil minivan = new Automobil(); Automobil masinasport = new Automobil(); minivan.model = "Logan MCV"; minivan.viteza = 180; masinasport.model = "BMW"; masinasport.viteza = 320; PrintProperties(minivan); PrintProperties(masinasport); } static void PrintProperties(Automobil vehicul) { //verificam sa nu avem obiecte null //altfel avem eroare la accesarea membrilor lui

if (vehicul == null) { System.out.println("Introduceti un obiect instantiat!"); return; } System.out.println("Masina "+ vehicul.model + " are viteza maxima " + vehicul.viteza);

} }

12

In acest exemplu se poate observa declararea metodei PrintProperties ce are ca parametru un obiect de tip Automobil:
static void PrintProperties(Automobil vehicul)

Aceasta inseamna ca atunci cand vom apela aceasta metoda, va trebui sa specificam intre paranteze doar obiecte de tip Automobil, sau care deriva din clasa Automobil (vom detalia in capitolul urmator). Mai jos este apelul functiei cu un parametru de tipul Automobil.
PrintProperties(minivan);

In exemplul de mai sus avem o functie care nu returneaza nici o valoare. Sa vedem si un exemplu de functie ce returneaza o valoare care va fi folosita ulterior:
class Rectangle { public int width; //latimea dretunghiului public int height; //inaltimea dreptunghiului

static int GetArea(Rectangle aRectangle) { if (aRectangle == null) { System.out.println("Introduceti un obiect instantiat!"); return -1; } return aRectangle.width*aRectangle.height;

} static int GetPerimeter(Rectangle aRectangle) { if (aRectangle == null) { System.out.println("Introduceti un obiect instantiat!"); return -1; } return 2*(aRectangle.width+aRectangle.height); } }

13

public class MethodDemo { public static void main(String[] args) { //exemplu pentru doua dreptunghiuri diferite Rectangle myRectangle = new Rectangle(); Rectangle myOtherRectangle = new Rectangle(); myRectangle.width = 10; myRectangle.height = 7; myOtherRectangle.width = 12; myOtherRectangle.height = 20;

System.out.println("Aria primului dreptunghi " + Rectangle.GetArea(myRectangle)); System.out.println("Aria celuilalt dreptunghi " + Rectangle.GetArea(myOtherRectangle));

System.out.println("Perimetrul primului dreptunghi " + Rectangle.GetPerimeter(myRectangle)); System.out.println("Perimetrul celuilalt dreptunghi " + Rectangle.GetPerimeter(myOtherRectangle)); } }

In acest exemplu, metodele se vor declara in clasa Rectangle si vor fi apelate din interiorul metodei main din clasa MethodDemo. Se observa faptul ca apelul functiilor comporta in fata numelui lor si numele clasei: Rectangle. Aceasta pentru ca metodele sunt statice, iar in acest caz o metoda statica poate fi apelata fara sa fie nevoie de instantierea unui obiect de acel tip. Metodele statice si variabilele statice sunt aceleasi per clasa, altfel spus variabilele membre clasei Rectangle vor avea aceleasi valori pentru orice obiect de aceasta clasa. Daca un obiect de tip Rectangle A, modifica valoarea lui width la 5, si width ar fi declarat static in Rectangle, atunci si un alt obiect B va avea ca width tot valoarea 5.

14

Figura 2.3 sensul cuvantului cheie static Alt fapt de remarcat este ca valoarea returnata de functie poate fi folosita imediat in expresii cum ar fi alcatuirea unui String sau in asignari, urmand ca variabila care ia valoarea returnata de functie sa fie utilizata mai departe:
System.out.println("Perimetrul primului dreptunghi " + Rectangle.GetPerimeter(myRectangle));

Aceasta instructiune poate fi scrisa si asa:


int perimeter = Rectangle.GetPerimeter(myRectangle); System.out.println("Perimetrul primului dreptunghi " + perimeter);

Utilizarea mai multor parametrii In exemplele de mai sus am folosit doar un singur parametru (de tip rectangle). In cele ce urmeaza vom avea doi si chiar trei parametrii de tipuri diferite pentru a evidentia utilizarea parametrilor.
class Numbers { int GetSumofTwoInt(int a, int b) { return a+b; } int GetSumofThreeInt(int a, int b, int c) { return a+b+c; }

15

double GetSumofTwoDouble(double a, double b) { return a+b; } } public class MoreParameters { public static void main(String[] args) { Numbers number = new Numbers(); System.out.println("Suma 6 + 7 = " + number.GetSumofTwoInt(6,7)); System.out.println("Suma 6 + 7 +8 = " + number.GetSumofThreeInt(6,7,8)); System.out.println("Suma 6.6 + 7.2 = " + number.GetSumofTwoDouble(6.6,7.2)); } }

Dupa cum se poate observa in acest exemplu toate cele trei metode sunt apelate dupa instantierea obiectului number. Aceasta deoarece ele nu sunt statice, astfel ca acum nu mai putem utiliza numele clasei pentru a apela functia. Ca exercitiu incercati sa refaceti metodele astfel ca ele sa fie statice, eliminand cu totul utilizarea obiectului number. Constructori

In exemplele anterioare, in cele in care am folosit clasa Automobil, am initializat membrii clasei ca model, viteza in functia in care am si creat noul obiect minivan:
minivan.model = "Logan MCV"; minivan.viteza = 180;

Modul acesta de lucru nu este unul profesional, deoarece necesita o atentie in plus din partea programatorului, acesta putand omite initializarea unor membrii (ce s-ar intampla daca avem zece membrii in aceeasi clasa?). Un constructor este o metoda care initializeaza un obiect atunci cand acesta este creat (cu operatorul new). Are acelasi nume ca si clasa si nu returneaza nimic. In mod normal constructorii sunt folositi pentru a crea proceduri de initializare pentru a da o forma noului obiect. Mai jos avem un exemplu de un constructor pentru clasa Automobil:

16

class Automobil { String model; //combi, SUV, MCV, caminon etc

int consum; // 5 .. 15 int viteza; //viteza maxima ce poate fi atinsa: 180 .. 340 //Acesta este constructorul clasei Automobil apelat la instantierea //unui nou obiect de tip Automobil public Automobil() { model = "Necunoscut"; consum = 0; viteza = 0; } } ... public class AutomobilDemo { public static void main(String[] args) { Automobil minivan = new Automobil(); //Acesta este locul in care //apelam constructorul ....

Constructori cu unul sau mai multi parametrii

In exemplul anterior, a fost folosit un constructor fara parametrii. Desi este ok, in multe situatii, exista anumite momente cand trebuie sa avem constructori prin care sa manipulam valorile initiale ale unui obiect. Pentru aceasta, vom transmite aceste valori ca parametrii unui constructor al aceleasi clase, la fel cum se intampla la apelul unor functii cu diversi parametrii:

17

class Automobil { String model; //combi, SUV, MCV, caminon etc

int consum; // 5 .. 15 int viteza; //viteza maxima ce poate fi atinsa: 180 .. 340 //constructor fara parametrii public Automobil() { model = "Necunoscut"; consum = 0; viteza = 0; } //constructor cu un parametru public Automobil(String modelulinitial) { model = modelulinitial; consum = 0; viteza = 0; } //constructor cu doi parametrii public Automobil(String modelulinitial, int vitezainitala) { model = modelulinitial; consum = 0; viteza = vitezainitala; } } public class AutomobilDemo { public static void main(String[] args) { //Constructor cu un parametru //ulterior mai trebuie sa initializez separat viteza Automobil minivan = new Automobil("Logan MCV"); minivan.viteza = 180;

18

//Constructor cu doi parametrii, initializez tot //direct prin apelul acestui Automobil masinasport = new Automobil("BMW",320);

System.out.println(minivan.model +" are viteza maxima " + minivan.viteza); System.out.println(masinasport.model +" are viteza maxima " + masinasport.viteza); } }

In exemplul de mai sus avem un constructor fara parametrii (implicit sau default) si doi constructori cu unul respectiv doi parametrii. Evident tipul de data al parametrilor poate varia, exact ca in cazul metodelor. Am precizat ca un costructor seamana cu o functie. Atunci cand este apelat un costructor? Atunci cand folosim operatorul new pentru a crea un nou obiect, mai intai se apeleaza constructorul a caror parametrii sunt de tipul celor cu care s-a facut apelul, si apoi se creeaz referinta pentru acel obiect: Automobil minivan = new Automobil("Logan MCV"); Cum eliminam atunci referinta unui obiect atunci cand nu mai este nevoie de el? Garbage Collection si finalizatori

Atunci cand instantiem un obiect, se creeaz o zona de memorie care va fi alocata acelui obiect. Memoria insa, nu este infinita, si memoria libera trebuie bine manipulata. Se poate uneori ca new sa esueze pentru ca nu exista memorie suficienta pentru a crea obiectul. De aceea, o componenta cheie a limbajului este un mecanism de alocare dinamic, si de recuperarea a memoriei de la obiectele nefolosite, facand ca acea memorie sa fie disponibila altor noi obiecte. In C++, acest lucru este facut manual, de catre programator, cu anumite instructiuni gen delete, sau free. Java foloseste altceva si anume garbage collector (GC). GC este un mecanism ce recupereaza memoria de la obiectele ce nu mai sunt folosite (si aici sunt mai multe scenarii), si o face disponibila pentru alte noi obiecte. Pentru eficienta, GC va rula cand doua conditii sunt indeplinite: exista obiecte care trebuie reciclate si este nevoie ca ele sa fie reciclate. De aceea nu se poate preciza, sau controla cand GC va rula. In acest caz cum controlam ce se intampla la stergerea obiectului?

19

Metoda finalize

Se poate defini o metoda care va fi apelata automat inainte ca obiectul sa fie distrus de GC. Aceasta se numeste finalize(), si se utilizeaza cand dorim ca obiectul sa fie distrus intr-un mod ordonat. De exemplu se poate folosit metoda finalize() pentru a inchide un fisier eventual deschis de acel obiect. Forma generala a metodei este:
protected void finalize() { //codul la distrugerea obiectului }

Aici cuvantul cheie protected este un specificator de acces ce nu permite accesul la aceasta metoda din afara clasei (decat in anumite conditii asupra carora vom reveni). In cele ce urmeaza am prezentat un exemplu pentru a intelege mai bine aceasta metoda:
class Finalize { public static void main(String args[]) { int count; FinalizeDemo ob = new FinalizeDemo(0); /* Generam un numar mare de obiecte, care apoi vor fi distruse de GC la un moment dat*/ for(count=1; count < 100000; count++) ob.generator(count); } } class FinalizeDemo { int x; FinalizeDemo (int i) { x = i; }

20

//apelat de GC protected void finalize() { System.out.println("Finalizarea lui " + x); } // genereaza un obiect care este imediat distrus void generator(int i) { FinalizeDemo o = new FinalizeDemo(i); } }

In acest exemplu se creeaz 10000 de obiecte de tip FinalizeDemo, prin intermediul metodei generator(). Atunci cand se paraseste domeniul de definitie al acestei metode (in cadrul for-ului din metoda main()) practic obiectul o nu mai este folosit. Acest lucru va fi remarcat de GC atunci cand acesta va face colectarea obiectelor care nu mai sunt folosite. Cand o este distrus se va apela automat metoda finalize(). Aceasta metoda, pentru fiecare obiect de tip FinalizeDemo afiseaza mesajul Finalizarea lui si numarul care a fost transmis (in cadrul for-ului) ca parametru constructorului, si anume valoarea lui x. Acest x, poate fi vazut ca un identificator de obiect in cadrul celor 10000 care vor fi create si la un moment dat distruse. Cuvantul cheie this

Atunci cand apelam o metoda ce apartine unui obiect din interiorul obiectului, implicit se considera ca referinta cu ajutorul caruia aceasta metoda s-a apelat este obiectul ce o invoca. Aceasta referinta este this. this reprezinta instanta obiectului curent. Pentru a intelege mai bine this avem exemplul de mai jos, in care calculam puterea unui numar:
class Power { double b; int e; double val; Power(double base, int exp) { this.b = base; this.e = exp; this.val = 1; if(exp==0) return;

21

for( ; exp>0; exp--) this.val = this.val * base; } double get_power() { return this.val; } } class DemoPower { public static void main(String args[]) { Power x = new Power(3, 2); System.out.println(x.b + " la " + x.e + " este " + x.get_power()); } }

In cazul acesta, in constructor instructiunea this.b = base; se refera valoarea b declarata ca membru al clasei Power. Atunci cand cream un nou obiect se va apela constructorul Power, iar in cadrul constructorului initializam membrii noului obiect adica this.

Pachete
In programare este folositor sa grupam organizat, modulele scrise. In Java, acest lucru este posibil datorita existentei pachetelor. Pachetul ofera un mecanism de organizare a bucatilor dintr-un program ca un tot unitar si totodata ofera un mod de grupa colectiile de clase. Mai mult clasele definite intr-un pachet pot fi ascunse in acel pachet si astfel nu vor fi accesibile unui alt pachet, sau in afara pachetului in care sunt definite. In general cand denumim o clasa, ii alocam un spatiu de nume: namespace. Acesta este ca o regiune in cadrul careia doua clase nu pot avea acelasi nume. De ce este necesar acest namespace? In cadrul programelor simple, ca cele prezentate mai sus numele claselor difera. In programe mari, insa putem avea acelasi nume de clasa pentru doua scopuri diferite: spre exemplu un program care ar efectua diverse calcule pentru un motor ar putea avea doua clase Power , una pentru calculul puterii unui motor, iar alta pentru calculul matematic de ridicare la putere. In acest caz separam codul in doua module/namespace-uri unul denumit eventual engine.characteristics iar altul denumit math. In cele ce urmeaza vom analiza cum se definesc aceste nume. 22

Definirea pachetelor

Toate clasele apartin, in Java, unui pachet. Atunci cand nu apare instructiunea package pentru a specifica in mod clar apartenenta, se foloseste de fapt pachetul default sau global. Pentru a crea un nou pachet, comanda package va apare la inceputul fisierului sursa:
Package pachet;

Aici pachet este numele unui pachet. De exemplu, pentru crearea unui pachet Project1:
Package Project1;

Sistemul de tratare a pachetelor in Java este in felul urmator: fiecare pachet este stocat in folderul cu numele sau. De exemplu fisierele .class declarate ca fiind din proiectul Projectul1 trebuie salvate in folderul Project1. Se poate crea o ierarhie de pachete. Pentru a face acest lucru fiecare pachet poate fi inclus ca un arbore:
package pack1.pack2.pack3;

Desigur folderul pack3 va sta in folderul pack2 iar pack2 va fi subfolder al lui pack1.

Pachete si CLASSPATH

Problema care intervine este, daca vom avea o ierarhie de clase si multe foldere, de unde stie Java sa ia aceste clase? Se poate specifica prin intermediul CLASSPATH, care este o variabila de mediu, o serie de cai, unde o cale va fi de forma (in Windows) C:\Pachete\Project1. Fiind o variabila de mediu, aceste cai vor fi separate prin ;. Mai jos avem un exemplu care sa lamureasca folosirea pachetelor:
package BookPack; class Book { private String title; private String author;

23

Book(String t, String a) { title = t; author = a; } void show() { System.out.println(title); System.out.println(author); } } class BookDemo { public static void main(String args[]) {

Preda");

Book book0 = new Book("Cel mai iubit dintre pamanteni","Marin Book book1 = new Book("Batranul si marea","Ernest Hemingway"); Book book2 = new Book("Dune","Frank Hebert");

book0.show(); book1.show(); book2.show(); } }

In acest exemplu, ambele clase se afla in pachetul BookPack. In urma compilarii folosind comanda
javac BookDemo.java

vom obtine doua fisiere: Book.class, BookDemo.class . Acestea trebuie sa fie in folderul BookPack neaparat. Din folderul parinte lui BookPack vom lansa interpretatorul:
java BookPack.BookDemo

Cum folosim alte pachete?

24

Importarea pachetelor Folosind cuvantul cheie import putem aduce in codul curent declaratia tuturor claselor din pachetele pe care le importam. Intr-un fel acest import functioneaza ca si directiva include din C++. Acesta este declaratia generala:
import pkg.classname;

unde, pkg este numele pachetului importat, iar classname este evident numele clasei ce va fi importata. Daca vrem sa importam toate clasele, sau tot continutul unui pachet se va folosi * .
import pachet.MyClass; import pachet.*;

Java contine o serie de pachete ce servesc la definirea claselor pe care le vom studia in continuare. Iata cateva: java.lang java.util java.io java.net java.applet java.awt Contine un numar consistent de clase cu scop general Contine clasele care au diverse functionalitati utile Contine clasele care se ocupa cu operatiunile input/output Contine clasele care se ocupa de lucru in retea Contine clasele care ajuta la lucru cu applet-uri Contine clasele pentru lucru cu Abstract Window Toolkit

25

Capitolul 3
Supraincarcarea metodelor ................................................................................................................. 2 Supraincarcarea constructorilor ........................................................................................................... 4 Recursivitate........................................................................................................................................ 7 Parametrii variabili .............................................................................................................................. 8 Mostenire ............................................................................................................................................... 9 Bazele mostenirii ................................................................................................................................. 9 Specificatori de acces ........................................................................................................................ 13 Metode de get, set ............................................................................................................................ 15 Rolul constructorilor in mostenire ..................................................................................................... 17 super ................................................................................................................................................. 18 Suprascrierea metodelor ................................................................................................................... 21 final ................................................................................................................................................... 23 Clasa Object .......................................................................................................................................... 24 clone() ........................................................................................................................................... 24 toString() ....................................................................................................................................... 25 equals() ......................................................................................................................................... 26 hashcode() ..................................................................................................................................... 27

Supraincarcarea metodelor
In Java, se pot gasi doua sau mai multe metode in cadrul aceleiasi clase, care sa aiba acelasi nume, atata timp cat parametrii lor sunt diferiti. In acest caz se spune ca metoda este supraincarcata, iar procedeul se numeste de supraincarcarea metodelor. Este unul dintre modurile prin care Java suporta polimorfismul. Ce inseamna parametrii diferiti? In acest procedeu exista o restrictie: parametrii trebuie sa fie diferiti: atat tipul de data al parametrilor cat si numarul lor poate sa difere. Daca tipul de data returnat de functie si numai acesta este cel care difera, compilatorul nu poate alege care metoda trebuie apelata. Tipurile de data returnate nu ofera informatie suficienta pentru a face diferenta. In cele ce urmeaza vom exemplifica supraincarcarea metodelor.
class Supraincarcare { void functieSupraincarcata() { System.out.println("Fara parametrii"); } void functieSupraincarcata(int unparam) { System.out.println("Un parametru: " + unparam); } int functieSupraincarcata(int a, double b) { System.out.println("Doi parametrii: " + a + " " + b); return a + (int)b; } } public class DemoSupraincarcare { public static void main(String args[]) { Supraincarcare obj = new Supraincarcare(); //Apelul functiei fara nici un parametru obj.functieSupraincarcata(); //Apelul functiei cu un parametru de tip int obj.functieSupraincarcata(5); //Apelul functiei cu doi parametrii int i = obj.functieSupraincarcata(5,5.7); System.out.println(i); } }

In exemplul de mai sus in clasa Supraincarcare avem functia functieSupraincarcata redefinita de doua ori dupa prima definitie fara nici un parametru:
int functieSupraincarcata(int a, double b) void functieSupraincarcata(int unparam)

Dupa cum se vede una din functii nu intoarce nimic (void) pe cand cealalta returneaza o valoare de tip int care va fi influentata de parametrii transmisi la apelul functiei:
i = obj.functieSupraincarcata(5,5.7);

Atunci cand se efectueaza acest apel, Java va chema acea functie care are ca prim parametru de tip int, iar al doilea de tip double sau transformabil catre double. Astfel si un apel de tipul acesta este valabil:
i = obj.functieSupraincarcata(5,6);

Dupa cum am precizat, supraincarcarea nu consta doar in schimbarea tipului de data returnat de functie. Mai jos este un exemplu in care am definit doua functii cu acelasi parametru, dar tip de data returnat diferit:
class Supraincarcare { void functieSupraincarcata(int b) { System.out.println("Un parametru: " + b); } int functieSupraincarcata(int a) { System.out.println("parametrul int: " + a); return a*a; } } public class DemoNoSupraincarcare { public static void main(String args[]) { Supraincarcare obj = new Supraincarcare(); //Apelul functiei cu un parametru de tip int obj.functieSupraincarcata(5); //Apelul functiei tot cu un parametru int i = obj.functieSupraincarcata(7); System.out.println(i); } }

In exemplul acesta vom avea urmatoarele erori de compilare: DemoNoSupraincarcare.java:8: functieSupraincarcata(int) is already defined in Supraincarcare int functieSupraincarcata(int a) ^ DemoNoSupraincarcare.java:22: incompatible types found : void required: int int i = obj.functieSupraincarcata(7); 3

Functia functieSupraincarcata cu parametru de tip int a mai fost definita, desi cu un tip de data returnat altul decat int, si anume void dupa cum ne dam seama din eroarea a doua. De ce avem nevoie de supraincarcarea unor functii? Acest mecanism este denumit si paradigma o interfata, metode multiple, iar in limbajele care nu suporta acest conceput, fiecare metoda trebuie sa aiba nume diferit. Sa consideram functia abs() care returneaza valoarea absoluta a unui numar. Numerele pot fi reprezentate in multe feluri, astfel ca in C spre exemplu, aceasta functie ar purta denumirea de labs() in cazul in care returneaza un long, sau fabs() in cazul in care returneaza un float. Problema este ca pentru un om, devine destul de complicat sa retina cateva nume diferite pentru aceeasi functie, daca vorbim deja de cateva sute de functii pe care le foloseste in mod uzual. Evident functia abs() este un singur exemplu pentru a raspunde la intrebare.

Supraincarcarea constructorilor
Constructorul este o metoda, astfel ca suporta acelasi mecanism de supraincarcare descris mai sus. In cele ce urmeaza am ales un exemplu clasic de supraincarcare a constructorilor:
class Numbers { int m_int; short m_short; double m_double; float m_float;

Numbers() { m_int = 0; m_double =0; m_short = 0; m_float =0; } Numbers(double val) { m_double = val; m_int = 0; m_short = 0; m_float =0; } Numbers(short val) { m_short = val; m_int = 0; m_double =0; m_float =0; }

Numbers(float val) { m_float = val; m_int = 0; m_double =0; m_short = 0; } Numbers(int ival, double dval) { m_int = ival; m_double = dval; m_short = 0; m_float =0; } Numbers(int ival, float fval) { m_float = fval; m_int = ival; m_double =0; m_short = 0; } public String toString() { return "int = " + m_int + " short = " + m_short + " double = " + m_double + " float = " +m_float; } } public class OverloadingDemo { public static void main(String[] args) { Numbers number1 = new Numbers(); Numbers number2 = new Numbers(1); Numbers number3 = new Numbers(1.1); Numbers number4 = new Numbers(2,3); Numbers number5 = new Numbers(2,4.4); System.out.println(number1); System.out.println(number2); System.out.println(number3); System.out.println(number4); System.out.println(number5); } }

Deocamdata sa ignoram functia toString() care ajuta la o afisare rapida a membrilor clasei Numbers. In exemplul de mai sus avem sase constructori, toti cu parametrii de tipuri diferite. Acestia in interiorul lor, initializeaza membrii, fie cu zero, fie cu valoarea corespunzatoare a parametrilor:
Numbers(int ival, float fval) { m_float = fval; m_int = ival; m_double =0; m_short = 0; }

De exemplu, daca am ales sa transmit un parametru int si unul float, m_int si m_float sunt initializati cu acele valori, iar restul cu zero. Evident logica poate fi schimbata si in cazul de fata toate variabilele intializate cu unul din parametrii constructorului. Ce este interesant, vom afla la apelul constructorilor in interiorul clasei OverloadingDemo. Constructorul implicit este cel fara parametrii si in cadrul lui toate variabilele se initializeaza cu zero. In cazul apelului Numbers number2 = new Numbers(1); ar trebui sa se apeleze constructorul cu parametrul de tip int, si asa se si intampla. Totusi in cazul celui de al treilea apel si anume: Numbers number3 = new Numbers(1.1); lucrurile devin neclare. Numarul transmis ca parametru poate fi interpretat de Java ca si double sau ca float. Aceeasi ambiguitate o avem si in apelurile ulterioare, pentru ca nu stim care dintre constructori vor fi apelati, deoarece parametrii pot fi interpretati fie ca double, fie ca float si asa mai departe. Aceasta este ceea ce interpretatorul java va deduce:
int int int int int = = = = = 0 1 0 2 2 short short short short short = = = = = 0 0 0 0 0 double double double double double = = = = = 0.0 0.0 1.1 0.0 4.4 float float float float float = = = = = 0.0 0.0 0.0 3.0 0.0

Pentru a controla bine aceste date avem doua cai: cast specificat de tipuri sau cast al valorilor. Sa analizam prima metoda pentru instructiunea Numbers number3 = new Numbers(1.1); in care lucram fie cu float fie cu double. Pentru a interpreta numarul 1.1 ca double vom pune caracterul d la sfarsitul numarului: Numbers number3 = new Numbers(1.1d);.Asemenea, daca vrem sa formatam numarul ca float vom pune caracterul f la sfarsit:
Numbers number3 = new Numbers(1.1f);

A doua metoda pentru a constrange tipul de data al parametrului transmis este de a efectua cast explicit catre un anume tip de data:
Numbers number3 = new Numbers((short)1);

Astfel ceea ce vom obtine se schimba, pentru ca apelam un alt constructor:


int int int int int = = = = = 0 0 0 2 2 short short short short short = = = = = 0 1 0 0 0 double double double double double = = = = = 0.0 0.0 1.1 0.0 4.4 float float float float float = = = = = 0.0 0.0 0.0 3.0 0.0

Recursivitate
O metoda se poate apela pe sine (din interiorul ei), iar acest concept se numeste recursivitate. In general recursivitatea este folosita atunci cand definim o expresie, sau termeni care sunt circulari prin definitie. Unul din exemplele clasice de recursivitate este factorialul, si anume produsul numerelor naturale din 1..n. Mai jos este codul care realizeaza calculul factorialului:
class Factorial { int factR(int n) { int result; if(n==1) return 1; result = factR(n-1) * n; return result; } } class FactorialDemo { public static void main(String args[]) { Factorial f = new Factorial(); System.out.println("Factorial de 5 este " + f.factR(5)); } }

In cadrul clasei Factorial este definita functia factR care returneaza o valoare de tip int. In corpul functiei are loc o conditie
if(n==1) return 1;

si anume conditia de terminare (a apelurilor recursive). Urmeaza dupa aceasta o conditie, o instructiune care duce la apelul functiei factR:
result = factR(n-1) * n;

Mai intai se va apela functia aceasta si iar se va executa conditia de mai sus, pana cand n devine 1. In acea clipa are loc primul return din functie si anume cu valoarea 1. Se revine in functia apelata penultima data inainte sa ajungem la n egal cu 1 si se evalueaza result ca fiind 1*2, si se returneaza valoarea 2. Mai departe se reevalueaza valoarea lui result ca fiind 2*3 si asa mai departe. Atunci cand o metoda se apeleaza pe sine, alti parametrii locali si variabile sunt alocate pe stiva, metoda se executa cu acesti noi parametrii. Atunci cand se revine din apel, variabilele vechi si parametrii initiali sunt refacuti, si instructiunea se reia de acolo de unde inital apelasem functia. Dupa cum intuiti, in cazul mai multori apeluri, stiva se va incarca cu metode aditionale, ceea ce va duce la o eroare cauzata de lipsa memoriei: stack overflow. De ce o abordare recursiva? Raspunsul este simplu, datorita faptului ca unii algoritmi (de exemplu QuickSort) pot fii implementati mai usor in mod recursiv decat iterativ. Vom reveni asupra acestui subiect, cu exemple diverse, in capitolele urmatoare.

Parametrii variabili
Un astfel de parametru este specificat prin trei puncte (...). De exemplu, iata o metoda ce preia un numar variabil de parametrii:
static void TestParam(int ... p) { System.out.println("Numarul argumentelor: " + p.length); for(int i=0; i < p.length; i++) System.out.println(" argumentul " + i + ": " + p[i]); System.out.println(); }

Aceasta declaratie semnifica faptul ca metoda TestParam poate fi apelata cu oricati parametrii atata timp cat acestia sunt de tip int: corect: TestParam(1,2,3); incorect: TestParam(1,2.5,3); In felul acesta variabila p va deveni un sir de date de tip int si va functiona ca atare. Parametrii variabili se pot utiliza impreuna cu parametrii obisnuiti iar ca exemplu avem clasa de mai jos:
class TestParam { static void TestParam(String amesage, double adouble,int ... p) { System.out.println("Numarul argumentelor: " + p.length); for(int i=0; i < p.length; i++) System.out.println(" argumentul " + i + ": " + p[i]); System.out.println(); } public static void main(String args[]) { TestParam("Mesaj1",2.3,3,4); } }

Acest exemplu este corect, deoarece parametrii variabili se declara la sfarsitul listei de parametri. Daca insa, inversam ordinea in care apar parametrii in lista ca mai jos:
static void TestParam(String amesage, int ... p, double adouble)

vom avea erori de compilare si anume:


Test.java:13: TestParam(int...) in TestParam cannot be applied to (int,double,in t) TestParam(1,2.5,3); ^ 1 error

I:\Java\Curs3\Cod_curs3>javac Test.java Test.java:3: ')' expected static void TestParam(String amesage, int ... p, double adouble) ^ Test.java:3: ';' expected static void TestParam(String amesage, int ... p, double adouble) ^ 2 errors

De asemenea nu este permis sa avem mai multi parametrii variabili in aceeasi lista.

Mostenire
Bazele mostenirii
Ca orice limbaj de programare orientat pe obiecte, Java implementeaza un mecanism de relationare al claselor si anume ca o clasa poate contine o alta clasa in declararea ei. Acesta se face prin cuvantul cheie extends. Aceasta inseamna ca o subclasa extinde functionalitatile unei superclase. Pentru a intelege mai bine, sa luam un exemplu clasic si anume al unei superclase Forma2D ce va fi extinsa de cel putin doua subclase Triunghi, Dreptunghi.

Figura 3.1 Extinderea clasei Forma2D In ce consta extinderea acestei clase? In primul rand presupunem ca Forma2D contine doi membrii de tip double si anume latime si inaltime pe care ii vom folosi la calculul ariei formei.

class Forma2D { double inaltime; double latime; void AfisezDimensiunile() { System.out.println("inaltimea este " + inaltime + " latimea este " + latime); } }

Deocamdata avem o clasa, insa nici o posibilitate de a calcula aria unei forme din moment ce nu stim despre ce fel de forma este vorba. Ca atare, clasa Forma2D nu va contine nici o functie de tip GetArea() sau CalcArea().

In continuare vom implementa o clasa Triunghi ce mosteneste clasa Forma2D


class Triunghi extends Forma2D { String tip_triunghi; double CalcArea() { return inaltime* latime/ 2; } void AfisezTip() { System.out.println("Triunghiul este " + tip_triunghi); } }

Aceasta clasa va mosteni de la clasa Forma2D atat cei doi membrii inaltime si latime cat si metoda AfisezDimensiunile(). Ca atare putem folosi membrii in interiorul unei metode cum ar fi CalcArea(). In continuare sa vedem cum pot fi folosite metodele cele mostenite si cele proprii:
class DemoMostenire{ public static void main(String args[]) { Triunghi t1 = new Triunghi (); Triunghi t2 = new Triunghi (); t1.latime = 8.0; t1.inaltime = 4.0; t1.tip_triunghi = "dreptunghic"; t2.latime = 4.0; t2.inaltime = 6.0; t2.tip_triunghi = "isoscel";

10

System.out.println("Informatiile despre t1: "); t1.AfisezTip(); t1.AfisezDimensiunile(); System.out.println("Aria " + t1.CalcArea()); System.out.println(); System.out.println("Informatiile despre t2: "); t2.AfisezTip(); t2.AfisezDimensiunile(); System.out.println("Aria " + t2.CalcArea()); } }

In functia main exista doua obiecte de tip Triunghi. Dupa cum se poate observa aceste obiecte contin, sau au acces la membrii inaltime si latime dar si la metoda AfisezDimensiunile(). Aceasta pentru ca clasa Triunghi mosteneste clasa Forma2D si implicit membrii ei. Pe langa metodele si variabilele mentionate t1 si t2 au acces la variabila tip_triunghi de tip String, dar si la AfisezTip()si CalcArea(). Inainte nu puteam implementa o metoda de calcul al suprafetei pentru ca nu cunosteam tipul de forma (triunghi, cerc etc). In clipa aceasta, putem spune ca clasa Forma2D prinde contur prin extinderea ei in clasa Triunghi. Vom vedea in cele ce urmeaza ca expresia prinde contur poate avea mai multe intelesuri cand vom intra in detaliile polimorfismului in capitolele urmatoare. Deocamdata spunem ca metodele AfisezTip si CalAreasi variabila tip_triunghi sunt membrii clasei Triunghi si numai ai clasei Triunghi, iar inaltime, latime si AfisezDimensiunile() sunt membrii clasei Forma2D si, datorita faptului ca Triunghi mosteneste Forma2D, sunt si membrii lui Triunghi. Pentru a duce lucrurile mai departe si a exemplifica pluralismul mecanismului de mostenire vom mosteni din aceeasi clasa Forma2D si o clasa Dreptunghi ce seamana cu Triunghi dar are alta implementare. class Dreptunghi extends Forma2D { double CalcArea() { return inaltime* latime; } void AfisezTipDreptunghi() { if (latime == inaltime) System.out.println("Dreptunghiul este patrat"); else System.out.println("Dreptunghiul este oarecare"); } } class DemoMostenire{ public static void main(String args[]) { Dreptunghi d1 = new Dreptunghi (); Dreptunghi d2 = new Dreptunghi (); 11

d1.latime = 4.0; d1.inaltime = 4.0; d2.latime = 4.0; d2.inaltime = 6.0; System.out.println("Informatiile despre d1: "); d1. AfisezTipDreptunghi (); d1.AfisezDimensiunile(); System.out.println("Aria " + d1.CalcArea()); System.out.println(); System.out.println("Informatiile despre d2: "); d2. AfisezTipDreptunghi (); d2.AfisezDimensiunile(); System.out.println("Aria " + d2.CalcArea()); } } Dupa cum se observa clasa Dreptunghi nu mai are nevoie de un membru de gen tip_triunghi pentru ca putem sa calculam foarte simplu cu ce fel de dreptunghi lucram si acest lucru il facem in functia AfisezTipDreptunghi() a carei implementare este diferita fata de cea in Triunghi a functiei AfisezTip(). Pentru a intelege mai bine mostenirea avem reprezentarile de mai jos:

Figura 3.2 Clasa Triunghi mosteneste clasa Forma2D

Figura 3.3 Clasa Dreptunghi mosteneste clasa Forma2D

12

In general mostenirea se va declara in urmatorul fel:


class nume_subclasa extends nume_superclasa { //corpul clasei }

Java nu suporta mostenirea din mai multe superclase, spre deosebire de C++. Se poate totusi crea o ierarhie de mosteniri in care o subclasa devine superclasa pentru alta clasa. Desigur, o clasa nu poate fi propria superclasa.

Specificatori de acces
In Java exista la nivel de membrii ai clasei, trei tipuri de specificatori de acces: public, private si protected. private inseamna ca acel membru nu poate fi accesat decat in interiorul clasei, de catre metodele din interiorul clasei. protected inseamna ca nu poate fi accesat decat fie in interiorul clasei, de catre metodele clasei, fie din interiorul claselor ce mostenesc clasa de baza (in care membrii au fost declarati). In cadrul claselor din acelasi pachet general membrii declarati astfel sunt vizibili in aceste clase. public inseamna ca membrii pot fi accesati si din afara clasei.

Figura 3.4 Specificatorii de acces 13

Mai jos avem implementarea claselor din aceasta diagrama


class BaseClass { public int i; private int j; protected int k; void PrintMyVariables() { //membrii proprii System.out.println(i); System.out.println(j); System.out.println(k); } } class SubClass extends BaseClass { double d; void PrintVariables() { //membrul propriu System.out.println(d); //membrii lui BaseClass System.out.println(i); System.out.println(k); } } class AnyClass { float f; //Atentie in cadrul acestei clase //nu am acces direct asupra membrilor //celorlalte clase pentru ca nu exista //relatie de mostenire, ci prin instantiere //vom accesa membrii claselor void PrintVariablesOfBaseClass(BaseClass obj) { System.out.println(obj.i); //Acest apel ar duce la eroare //System.out.println(obj.j); } } class DemoAcces { public static void main(String args[]) { BaseClass objB = new BaseClass(); objB.i = 20;

14

objB.PrintMyVariables(); SubClass objS = new SubClass(); objS.d = 3.0; objS.i= 2; objS.k = 6; objS.PrintVariables(); AnyClass objA = new AnyClass(); objA.PrintVariablesOfBaseClass(objB); } }

Membrii i si k ai clasei BaseClass vor fi vizibili atat in SubClass cat si in AnyClass dar din motive diferite. Clasa SubClass este derivata din BaseClass, automat mosteneste si membrii public si protected ai sai. De aceea putem face referire la membrii acestei clase fara a avea probleme. Problemele apar daca incercam sa facem referire la membrul j care este declarat privat in clasa de baza. Pe de alta parte in clasa AnyClass, ce nu mosteneste nici o clasa, va trebui sa folosim membrii clasei de baza (pentru exemplificare) prin intermediul unui obiect de tip BaseClass. Acest obiect poate fi instantiat chiar in cadrul unei metode din clasa AnyClass sau poate fi transmis ca parametru unei functii din aceasta clasa. Evident al doilea caz l-am implementat si anume functia:
void PrintVariablesOfBaseClass(BaseClass obj)

Un alt specificator de acces ar fi cel implicit, si anume membrii declarati fara specificatori de acces isi extind domeniul de folosire in cadrul pachetului, adica sunt vizibili in orice clasa din pachetul clasei de baza. Daca totusi vreau sa accesez un membru private?

Metode de get, set


In cazul in care se doreste expunerea in exterior a unei variabile declarata privata exista posibilitatea implementarii unor functii pentru aceasta. Acestea se numesc getter si setter si sunt folosite la returnarea valorii variabilei respectiv setarea valorii. Mai mult, in cadrul acestor functii se pot verifica anumite conditii pentru evitarea aparitiei unor erori de logica.
class ExampleGetSet { private String mystr; private double a; //initializam pe constructor cu string vid public ExampleGetSet() { mystr = ""; }

15

//functie de get cu acces public public String GetString() { return mystr; } //functie de set cu acces public public void SetString(String str) { if (str !=null) mystr = str; else mystr = ""; } //functie de get cu acces public public double GetVal() { return a; } //functie de set cu acces public public void SetVal(Double val) { if (val !=Double.NaN) a = val; else a = 0; } } class DemoExampleGetSet { public static void main(String args[]) { ExampleGetSet obj = new ExampleGetSet(); obj.SetString("Orice fel de sir"); obj.SetVal(3.4); System.out.println(obj.GetVal()); System.out.println(obj.GetString()); } }

16

In exemplul de mai sus metodele SetVal si SetString vor seta membrii privati ai clasei doar in cazul in care se trece de anumite conditii si anume ca String-ul transmis ca parametru sa fie diferit de null sau ca valoarea double transmisa ca parametru sa fie valida. Asemenea si in cazul GetString sau GetVal se pot face unele validari, insa in general cand returnam din interior spre exterior, valorile ar trebui sa fie valide. Nu exista regula de validare generala dar o functie Get ar trebui sa returneze un tip de data acelasi cu variabila pe care o expune iar Set are tipul void, iar ca parametru o variabila de tip de data acelasi cu membrul ce va fi modificat. Nu exista reguli pentru cand o variabila trebuie declarata privata, dar sunt doua principii. Daca o variabila este folosita doar de metodele din interiorul clasei atunci ea trebuie declarata private. Daca valorile unei variabile trebuie sa fie intr-un interval atunci ea trebuie facuta private, altfel riscam ca apelata in exteriorul clasei sa genereze erori de logica sau alte erori.

Rolul constructorilor in mostenire


Intr-o ierarhie rezultata in urma mostenirii, este posibil ca atat superclasele cat si subclasele sa aiba constructorii lor proprii. Intrebarea este: care constructor se ocupa de instantierea obiectului subclasei? Este cel din superclasa, din subclasa, ambele? Raspunsul este urmatorul: constructorul superclasei va ajuta la instantierea portiunii de superclasa a obiectului si constructorul subclasei va instantia portiunea de subclasa. Pentru a fi mai explicit reluam exemplul cu Forma2D si Triunghi.
class Triunghi extends Forma2D { String tip_triunghi; Triunghi(double a, double b, String tip) { inaltime =a;//initalizez portiunea legata de latime = b;//Forma2D adica superclasa tip_triunghi = tip; //instantiez portiunea de subclasa } double CalcArea() { return inaltime* latime/ 2; } void AfisezTip() { System.out.println("Triunghiul este " + tip_triunghi); } } class DemoMostenire { public static void main(String args[]) { Triunghi t1 = new Triunghi (4,8,"dreptunghic"); Triunghi t2 = new Triunghi (4,6,"isoscel");

17

System.out.println("Informatiile despre t1: "); t1.AfisezTip(); t1.AfisezDimensiunile(); System.out.println("Aria " + t1.CalcArea()); System.out.println(); System.out.println("Informatiile despre t2: "); t2.AfisezTip(); t2.AfisezDimensiunile(); System.out.println("Aria " + t2.CalcArea()); } }

Portiunea legata de superclasa este instatiata automat, apeland constructorul implicit al clasei Forma2D.

super
Pe langa cele prezentate mai sus, o subclasa poate apela constructorul superclasei prin utilizarea cuvantului cheie super. Utilizarea este:
super(lista de parametrii);

Pentru a vedea cum se utilizeaza acest apel vom modifica clasa de mai sus
class Forma2D { double inaltime; double latime; public Forma2D(double a, double b) { inaltime =a; latime = b; } void AfisezDimensiunile() { System.out.println("inaltimea este " + inaltime + " latimea este " + latime); } } class Triunghi extends Forma2D { String tip_triunghi; Triunghi(double a, double b, String tip) { super(a, b); tip_triunghi = tip; } double CalcArea() { return inaltime* latime/ 2; }

18

void AfisezTip() { System.out.println("Triunghiul este " + tip_triunghi); } }

Am definit un constructor in clasa Forma2D cu doi parametrii de tip double. In constructorul din Triunghi am inlocuit cele doua instructiuni ce initializau membrii din Forma2D cu apelul constructorului clasei parinte.
inaltime =a;//initalizez portiunea legata de latime = b;//Forma2D adica superclasa

Aceste doua instructiuni au fost inlocuite de super(a, b); Aici clasa Triunghi apeleaza constructorul clasei Forma2D cu doi parametrii de tip double, in felul acesta nu mai initializeaza subclasa membrii superclasei.
class Forma2D { double inaltime; double latime; public Forma2D() { inaltime =0; //in constructorul fara parametrii latime = 0;//initializez membrii cu zero } public Forma2D(double a, double b) { inaltime =a; latime = b; } void AfisezDimensiunile() { System.out.println("inaltimea este " + inaltime + " latimea este " + latime); } } class Triunghi extends Forma2D { String tip_triunghi; Triunghi(String tip) { super(); //apelez Forma2D() si membrii superclasei vor fi zero tip_triunghi = tip; } Triunghi(double a, double b, String tip) { super(a, b); //apelez Forma2D(a,b) tip_triunghi = tip;

19

} double CalcArea() { return inaltime* latime/ 2; } void AfisezTip() { System.out.println("Triunghiul este " + tip_triunghi); } } class DemoMostenire{ public static void main(String args[]) { Triunghi t1 = new Triunghi ("fara dimensiuni"); Triunghi t2 = new Triunghi (4,6,"isoscel"); System.out.println("Informatiile despre t1: "); t1.AfisezTip(); t1.AfisezDimensiunile(); System.out.println("Aria " + t1.CalcArea()); System.out.println(); System.out.println("Informatiile despre t2: "); t2.AfisezTip(); t2.AfisezDimensiunile(); System.out.println("Aria " + t2.CalcArea()); } }

In clasa Triunghi avem doi constructori dintre care unul doar cu un parametru de tip String. In acel constructor vom apela constructorul clasei Forma2D super();fara parametrii si anume a carui definitie este:
public Forma2D() { inaltime =0; latime = 0; }

Prin aceasta am exemplificat si supraincarcarea constructorului clasei parinte, si anume cu doi parametrii de tip double si fara parametrii, si mai mult apelarea diversilor constructori in functie de caz. Mai mult, din subclasa se pot apela membrii (variabilele) superclasei prin intermediul super, in acelasi mod in care am utilizat this. De fapt putem spune ca super este this-ul superclasei, si anume instanta obiectului curent al parintelui. Intrebarea vine in mod firesc: cine se executa primul, constructorul parintelui sau cel al copilului? Constructorii sunt apelati in ordinea derivarii, de la superclasa la subclasa.

20

Suprascrierea metodelor
Atunci cand o metoda dintr-o clasa copil are aceeasi semnatura (parametrii, nume si tip de data) ca si metoda din clasa parinte, atunci metoda din copil suprascrie metoda din clasa parinte. Atunci cand se apeleaza o metoda suprascrisa din subclasa, referirea se face doar la metoda din subclasa (ca si cum metoda din superclasa nu ar exista)
class A { int i, j; A(int a, int b) { i = a; j = b; } //afisez i si j void show() { System.out.println("i si j: " + i + " " + j); } } class B extends A { int k; B(int a, int b, int c) { super(a, b); k = c; } //afisez doar k void show() { System.out.println("k: " + k); } } class DemoSuprascriere { public static void main(String args[]) { B obj = new B(1, 2, 3); obj.show(); // se va apela show() din B } }

In exemplul de mai sus evident, clasa B este copilul lui A. Metoda show() este suprascrisa in B, adica mai este redefinita in B, desi ea exista si in parinte si anume A. In clasa DemoSuprascriere cand se lucreaza cu obiectul obj de tip B, si se apeleaza metoda obj.show(), este ca si cum clasa A nu ar defini 21

metoda show(). Pe de alta parte, daca comentam definitia metodei show() din clasa B, si efectuam apelul obj.show(), se vor afisa valorile lui i si j din superclasa A. In clipa aceasta poate aparea confuzia legata de conceptul de supraincarcare. Supraincarcarea presupune ca aceeasi functie sa aiba parametrii diferiti, ceea ce in cazul de mai sus nu se intampla. Totusi in cele ce urmeaza vom implementa o supraincarcare a metodei show()in B pentru a exemplifica diferenta.
class A { int i, j; A(int a, int b) { i = a; j = b; } //afisez i si j void show() { System.out.println("i si j: " + i + " " + j); } } class B extends A { int k; B(int a, int b, int c) { super(a, b); k = c; } void show(String str) //Metoda supraincarca show() din A { System.out.println(str); } //in clasa B nu mai am show() suprascrisa } class DemoSuprascriere { public static void main(String args[]) { B obj = new B(1, 2, 3); obj.show("Mesaj 1"); //apelez show() din B obj.show(); // se va apela show() din A } }

22

Motivele pentru care se mentine acest mecanism de suprascriere sunt multiple, si este unul din elementele ce contribuie la polimorfism la momentul rularii. Polimorfismul este fundamental in OOP deoarece permite unei clase generale sa specifice metode ce vor fi aceleasi pentru toate clasele derivate din ea, in timp ce unora din clasele copii le permite sa aiba propriile implementari pentru acele metode.

final
Atunci cand nu se doreste ca o metoda sa poata fi suprascrisa intr-una din clasele copil derivate din clasa parinte, se poate specifica final pentru acea metoda. Acest program
class A { int i, j; A(int a, int b) { i = a; j = b; } //afisez i si j final void show() { System.out.println("i si j: " + i + " " + j); } } class B extends A { int k; B(int a, int b, int c) { super(a, b); k = c; } void show() { System.out.println(k); } //in clasa B nu mai am show() suprascrisa } class DemoSuprascriere { public static void main(String args[]) { B obj = new B(1, 2, 3); obj.show(); // se va apela show() din B }

23

Va returna urmatoarea eroare:


A.java:23: show() in B cannot override show() in A; overridden method is final void show() ^ 1 error

In cazul variabilelor cuvantul cheie final este asemanator cuvantului const din C++ si marcheaza o variabila ca fiind constanta, iar aceasta nu mai poate fi intializata cu alta valoare.

Clasa Object
Toate clasele in Java, extind direct sau indirect, clasa Object aflata in pachetul java.lang. Aceasta clasa defineste cateva metode importante de care trebuie sa tinem seama, pentru ca fiecare clasa pe care o scriem poate suprascrie sau folosi aceste metode. Metoda Scop Object clone( ) Creeaza un obiect cu aceleasi proprietati ale obiectului clonat. boolean equals(Object object) Determina faptul ca un obiect este/ nu este egal cu altul. void finalize( ) Apelul inainte ca obiectul sa fie distrus. Class<? extends Object> getClass( ) Determina clasa din care obiectul face parte. int hashCode( ) Returneaza un id specific fiecarui obiect. void notify( ) Reia executia firului de executie aflat in asteptare. void notifyAll( ) Reia executia firelor de executie aflate in asteptare. String toString( ) Returneaza un sir de caractere ce descrie obiectul. void wait( ) Suspenda firul de executie apelant. Metodele getClass(), notify(), notifyAll() si wait sunt declarate final. Vom reveni asupra lor la momentul oportun. In cele ce urmeaza vom discuta despre celelalte metode. clone( ) Metoda returneaza un obiect ce are membrii identici cu cel al obiectului curent. Metoda functioneaza doar daca clasa implementeaza interfata Cloneable (vom aborda subiectul legat de interfete in capitolul urmator). Metoda este declarata protected adica doar subclasele lui Object pot suprascrie aceasta metoda. Vom reveni cu un exemplu mai elaborat cand discutam despre interfete.
class AnyClass implements Cloneable { int i; String s; public double d; public void InitVars() {

24

i=3; s="Mesaj"; d= 4.1; } protected AnyClass clone() { AnyClass newobj = new AnyClass(); newobj.i=i; newobj.s = s; newobj.d = d; return newobj; } } class DemoObject extends AnyClass { public static void main(String args[]) { AnyClass obj1 = new AnyClass(); obj1.InitVars(); AnyClass obj2 = obj1.clone(); obj2.i =1; System.out.println(obj1); System.out.println(obj2); System.out.println(obj1.i + obj1.s + obj1.d); System.out.println(obj2.i + obj2.s + obj2.d); } }

Pentru moment vom spune ca Cloneable este un fel de clasa parinte pentru AnyClass si metoda clone() este mostenita si suprascrisa in AnyClass. Tipul de data returnat de clone este tot un AnyClass si obiectul returnat este unul nou instantiat, cu valorile preluate din obiectul curent. Metoda clone() poate fi suprascrisa in orice fel, putem returna chiar si instanta obiectului curent, insa nu acesta este scopul. Atunci cand rulam programul vom observa doua referinte, si anume a doua obiecte diferite, chiar daca au initial aceleasi proprietati:
AnyClass@19821f AnyClass@addbf1 3Mesaj4.1 1Mesaj4.1

Atunci cand rulam


System.out.println(obj1);

Rezultatul este unul oarecum ciudat si anume AnyClass@19821f. In continuare vom lamuri aceasta ciudatenie toString( ) Atunci cand apelam metoda de afisare a oricarui obiect in Java, implicit se apeleaza o metoda toString() mostenita din cadrul clasei Object. Implicit aceasta metoda returneaza un format de genul 25

urmator: TipulClasei@hascode_al_clasei. Astfel se explica de ce cand afisam direct obiectul ca mai sus obtinem acel rezultat. Pentru a creea rezultate care sa insemne ceva ce poate fi interpretat de oameni, putem suprascrie metoda toString() pentru a afisa de exemplu membrii obiectului.
class AnyClass { int i; String s; public double d; public void InitVars() { i=3; s="Mesaj"; d= 4.1; } public String toString() { String str=""; str += "i este " + i + "\n"; str += "s este " + s + "\n"; str += "d este " + d + "\n"; return str; } } class DemoObject { public static void main(String args[]) { AnyClass obj1 = new AnyClass(); obj1.InitVars(); //afisam obj1 prin apelului lui toString System.out.println(obj1); //Alt mod de a afisa continutul lui obj1 System.out.println(obj1.i + obj1.s + obj1.d); } }

Dupa cum se poate observa apelul inlesneste afisarea obiectului obj1. Mai mult daca am avea membrii private metoda toString() expune valorile acestora. equals( ) Operatorul == testeaza daca doua obiecte pointeaza catre aceeasi referinta. Pentru a vedea daca doua obiecte contin valori diferite, trebuie sa folosim equals().

26

hashcode() Atunci cand suprascriem metoda equals, trebuie sa suprascriem si metoda hashcode(). Aceasta metoda returneaza un intreg care este folosit de java pentru a diferentia doua obiecte. Este important ca doua obiecte egale conform metodei equals() sa aiba aceleasi hashcode-uri. De asemenea doua obiecte diferite in acelasi sens trebuie sa aiba hashcode-uri diferite. In continuare avem un exemplu de suprascriere a celor doua metode mai sus mentionate:
class Circle { private final int x, y, r; // Constructorul de baza public Circle(int x, int y, int r) { this.x = x; this.y = y; this.r = r; } //Constructor de copiere - alternativa la clone() public Circle(Circle original) { x = original.x; y = original.y; r = original.r; } // functii Get public int getX() { return x; } public int getY() { return y; } public int getR() { return r; } // suprascrierea equals @Override public boolean equals(Object o) { if (o == this) return true; //referinte egale if (!(o instanceof Circle)) return false; //daca tipul de data nu este corect Circle that = (Circle) o; //se aplica un cast catre tipul corect if (this.x == that.x && this.y == that.y && this.r == that.r)

27

return true; else return false; }

@Override public int hashCode() { int result = 17; result = 37*result + x; result = 37*result + y; result = 37*result + r; return result; } } class CircleDemo { public static void main(String args[]) { Circle c1 = new Circle(1,1,4); System.out.println(c1.hashCode()); Circle c2 = new Circle(1,2,4); System.out.println(c2.hashCode()); System.out.println(c1.equals(c2)); } }

Specificatorul @Override intareste ideea ca metoda este suprascrisa. In metoda equals pasii sunt tot timpul aceeasi: 1. Se verifica daca referintele sunt egale 2. Se verifica faptul ca obiectul cu care se va face comparatia sa aiba acelasi tip de data cu obiectul curent. 3. Se compara proprietatile celor doua obiecte si se returneaza true sau false. HashCode este mai interesanta. In cadrul acestei metode este imperativa sa implementam un algoritm care sa produca un numar, care sa respecte conditiile mentionate la descrierea metodei hasCode. Acest algoritm, prin inmultirea cu un numar prim si insumarea membrilor obiectului asigura acest lucru. O suma a lui x, y si r nu ar fi suficienta deoarece trei numere pot da aceeasi suma cu alte trei numere diferite insumate. De aceea un algoritm ar fi sa insumam cele trei numere inmultite cu numere prime pentru a diminua aceasta posibilitate: de exemplu 23*x+31*y+17*r. Cu cat algoritmul matematic este mai complex cu atat regulile hashCode sunt respectate in mai multe cazuri.

28

Capitolul 4
Clase si metode abstracte .................................................................................................................... 2 Interfete .............................................................................................................................................. 6 Mostenirea unor interfete ............................................................................................................... 6 Implementarea unei interfete .......................................................................................................... 7 Interfete si clase abstracte ............................................................................................................. 10 Mai multe exemple cu interfete ..................................................................................................... 10 Variabile in interfete ...................................................................................................................... 12 Exceptii .............................................................................................................................................. 13 Cum functioneaza? ........................................................................................................................ 13 Folosirea handlerului de exceptii try catch ..................................................................................... 14 Prinderea exceptiilor conform ierahiei ........................................................................................... 18 Cum aruncam o exceptie?.............................................................................................................. 19 Clasa Throwable ............................................................................................................................ 20 finally............................................................................................................................................. 22 I/O in Java.......................................................................................................................................... 27 Stream in Java................................................................................................................................ 27 Byte Stream ................................................................................................................................... 28 Character Stream ........................................................................................................................... 30 Stream-uri salvate in buffer-e ........................................................................................................ 32

Clase si metode abstracte


Sa reluam exemplul din capitolul anterior in care derivam din clasa Forma2D alte doua clase: Triunghi si Dreptunghi. Sa presupunem ca vrem sa derivam un numar mai mare de clase: Dreptunghi, Triunghi, Elipsa, Trapez si asa mai departe. Putem observa ca toate aceste clase au doua elemente comune si anume Suprafata si Perimetrul sau Circumferinta. Pentru a putea lucra usor cu un sir de obiecte care pot fi sau Triunghi sau Elipsa, este util ca acestea sa faca parte din clase ce au o superclasa si anume Forma2D. In aceste conditii, ar trebui ca Forma2D sa implementeze functiile Suprafata si Perimetrul, insa intrebarea este cum daca forma nu este una anume, deci nu stim exact cum vor fi implementate aceste doua metode. In aceste situatii Java permite utilizarea metodelor abstracte. O metoda abstracta nu are un corp definit, ci doar o semnatura, iar declaratia functiei se incheie cu ;. Mai jos sunt o serie de reguli pentru a defini metode abstracte si clase abstracte: Orice clasa ce contine o metoda abstracta este in mod automat abstracta, asa ca trebuie declarata ca atare. O clasa abstracta nu poate fi instantiata. O subclasa a unei clase abstracte poate fi instantiata doar daca suprascrie metodele abstracte ale parintelui si ofera o implementare pentru toate aceste metode. Aceste clase copil se numesc si concrete pentru a sublinia ca nu sunt abstracte. Daca o clasa copil a unei superclase abstracte nu implementeaza toate metodele abstracte pe care le mosteneste, atunci si clasa copil este abstracta si va fi declarata ca atare. Metodele declarate static, private sau final nu pot fi abstracte deoarece niciuna din acestea nu poate fi suprascrisa de subclase. De asemenea o clasa declarata final nu poate contine metode abstracte. O clasa poate fi declarata abstract chiar daca nu contine metode abstracte. De obicei aceasta semnifica faptul ca o clasa este incompleta si este clasa parinte pentru una sau mai multe clase copil. Mai jos avem une exemplu de folosire a claselor abstracte
public abstract class Forma { public String categorie; public abstract double Suprafata(); public abstract double Perimetrul(); }

//in loc de corp avem ";"

class Cerc extends Forma { public static final double PI = 3.1415926535897; protected double raza; public Cerc(double r) {

this.raza = r; categorie = "Cerc"; } public double getRaza() { return raza; } //metodele abstracte care "prind forma": public double Suprafata() { return PI*raza*raza; } public double Perimetrul() { return 2*PI*raza; } } class Dreptunghi extends Forma { protected double latime, inaltime; public Dreptunghi(double latime, double inaltime) { categorie = "Dreptunghi"; this.latime = latime; this.inaltime = inaltime; } public double getLatime() { return latime; } public double getInaltime() { return inaltime; } //metodele abstracte care "prind forma": public double Suprafata() { return latime*inaltime; } public double Perimetrul() { return 2*(latime + inaltime); } }

class AbstractDemo { public static void main(String args[]) { //declaram un sir de clase abstracte Forma[] sir_forme = new Forma[4]; //instantiam diverse obiecte din sir sir_forme[0] = new Cerc(2.0); sir_forme[1] = new Dreptunghi(1.0, 3.0); sir_forme[2] = new Dreptunghi(4.0, 2.0); sir_forme[3] = new Cerc(5.0); double suprafata_totala = 0; for(int i = 0; i < sir_forme.length; i++) { suprafata_totala += sir_forme[i].Suprafata(); //putem afisa aceste date deoarece sunt din clasa //parinte System.out.println("Categoria: " + sir_forme[i].categorie); System.out.println("Perimetrul: " + sir_forme[i].Perimetrul()); System.out.println("Suprafata: " + sir_forme[i].Suprafata()); //nu putem accesa aceste date, deoarece sunt specifice //doar unei subclase si nu parintelui //System.out.println("PI este: " + sir_forme[i].PI); } System.out.println("Suprafata totala: " + suprafata_totala); } }

In urma rularii acestui program rezultatul este:


Categoria: Cerc Perimetrul: 12.5663706143588 Suprafata: 12.5663706143588 Categoria: Dreptunghi Perimetrul: 8.0 Suprafata: 3.0 Categoria: Dreptunghi Perimetrul: 12.0 Suprafata: 8.0 Categoria: Cerc Perimetrul: 31.415926535897

Suprafata: 78.5398163397425 Suprafata totala: 102.1061869541013

Clasa Forma este declarata abstract si ca atare nu va pute fi instantiata. Totusi obiectele, sau sirul de obiecte este de tip Forma, pentru ca instantierea se petrece ulterior cu o subclasa a parintelui si anume Cerc:
sir_forme[0] = new Cerc(2.0);

Clasele Cerc cat si Dreptunghi nu sunt abstracte deoarece ambele implementeaza toate metodele declarate abstracte ale clasei Forma. Cele doua metode Suprafata si Perimetrul sunt declarate cu un cuvant cheie abstract in clasa Forma si nu au corp, deci se vor termina cu ; ca orice declaratie obisnuita. Pe de alta parte cele doua clase copil Cerc si Dreptunghi, implementeaza ambele metode:
public double Suprafata() { return PI*raza*raza; } public double Perimetrul() { return 2*PI*raza; }

Pe langa aceste doua metode ambele clase pot contine propriile metode, separat una de cealalta. De exemplu Cerc mai implementeaza urmatoarea metoda:
public double getRaza() { return raza; }

Atunci cand folosim obiectele acestor clase trebuie sa avem grija, deoarece, desi toate obiectele din sirul sir_forme sunt de tipul Forma, primul si ultimul obiect sunt de tipul Cerc, iar al doilea si al treilea sunt de tipul Dreptunghi. Daca un obiect este de tip Forma, nu putem apela un membru (desi public) al clasei Cerc. O astfel de instructiune este gresita
System.out.println("PI este: " + sir_forme[i].PI);

si produce urmatoarea eroare de compilare:


Forma.java:86: cannot find symbol symbol : variable PI location: class Forma System.out.println("PI este: " + sir_forme[i].PI);

Mai mult, in clasa parinte Forma, pot exista pe langa metode abstracte si alte metode concrete ca de exemplu aceasta:
public void ShowTipForma() { System.out.println("Forma este de tip: " + categorie); }

Exemplu de folosire a acestei functii:


for(int i = 0; i < sir_forme.length; i++)

{ sir_forme[i].ShowTipForma();

Interfete
In OOP este indicat sa definim ce trebuie sa faca o functie fara a da toate detaliile despre cum trebuie sa faca acele sarcini. O metoda abstracta este raspunsul la aceasta problema. In unele cazuri in care clasa nu are nevoie de metode concrete, se pot crea interfete, ce separa total implementarea unei clase de definitia metodelor acesteia. Interfetele sunt asemanatoare cu clasele abstracte, dar intr-o interfata, nici o metoda nu are voie sa aiba corp, adica implementare. Mai mult, o clasa poate implementa una sau mai multe interfete. Pentru ca implementarea unei interfete sa fie corecta, o clasa trebuie sa ofere implementari acelor metode declarate in interfata. Evident, fiecare clasa poate avea propria implementare pentru metodele interfetei. Mai jos este modul general de a declara o interfata:
acces interface nume_interfata { tip_data nume_metoda1(param-list); tip_data nume_metoda2(param-list); tip_data var1 = valoare; tip-_data var2 = valoare; // ... tip_data nume_metodaN(param-list); tip_data varN = valoare; }

unde acces este public sau nu este folosit deloc. Metodele sunt declarate folosind doar semnatura lor exact ca niste metode abstracte. Implicit metodele sunt public. Variabilele declarate intr-o interfata nu sunt variabile de instanta, ci trebuie declarate public, final si static si trebuie initializate. In exemplul de mai sus, clasa Forma poate implementa o interfata care poate specifica centrul unei forme geometrice:
public interface Centrat { void setCentru(double x, double y); double getCentruX(); double getCentruY(); }

De asemenea o interfata nu poate fi instantiata si nu poate defini un constructor. Mostenirea unor interfete Interfetele pot mosteni alte interfete, asemanator claselor, folosind cuvantul cheie extends. Cand o interfata mosteneste o alta interfata, va mosteni toate metodele abstracte ale interfetei parinte, putand defini si metode abstracte noi si alte constante. Spre deosebire de clase, o interfata poate mosteni mai multe interfete:
public interface Positionabil extends Centrat

} public interface Transformabil extends Scalabil, Translatabil, Rotatabil {} public interface Forma extends Positionabil, Transformabil {}

void setColDreaptaSus(double x, double y); double getDreaptaSusX(); double getDreaptaSusY();

Implementarea unei interfete

Asa cum o clasa foloseste extends, pentru a mosteni o clasa parinte, se poate folosi implements pentru a mosteni interfete. Atunci cand o clasa mosteneste o interfata, ofera o implementare tuturor metodelor interfetei. Daca o clasa implementeaza o interfata fara a oferi o implementare pentru fiecare metoda, va mosteni acele metode abstracte, si devine la randul sau abstracta. Forma generala a unei implementari este:
acces class nume_clasa extends superclasa implements interfata { // corpul clasei }

Acces este fie public fie nu este declarat. Clauza extends este optionala, in cazul in care clasa originala extinde alte clase obisnuite. Ce intereseaza in declaratia de mai sus este clauza implements ce permite implementarea unei interfete. Mai jos se gaseste un exemplu de utilizare al interfetelor:
abstract { public public public public { } } class Dreptunghi extends Forma { protected double latime, inaltime; public Dreptunghi(double latime, double inaltime) { categorie = "Dreptunghi"; this.latime = latime; this.inaltime = inaltime; } public double getLatime() { class Forma String categorie; abstract double Suprafata(); abstract double Perimetrul(); void ShowTipForma()

//in loc de corp avem ";"

System.out.println("Forma este de tip: " + categorie);

return latime; } public double getInaltime() { return inaltime; } //metodele abstracte care "prind forma": public double Suprafata() { return latime*inaltime; } public double Perimetrul() { return 2*(latime + inaltime); } } public interface Centrat { void setCentru(double x, double y); double getCentruX(); double getCentruY(); } class Dreptunghi_Centrat extends Dreptunghi implements Centrat { //campuri ale clasei Dreptunghi_Centrat private double cx, cy; //constructorul public Dreptunghi_Centrat (double cx, double cy, double latime, double inaltime) { super(latime, inaltime); this.cx = cx; this.cy = cy; } //Daca implementam interfata Centrat //trebuie sa ii implementam metodele public void setCentru(double x, double y) { cx = x; cy = y; } public double getCentruX() {

return cx; } public double getCentruY() { return cy; } } class DemoInterfete { public static void main(String args[]) { Dreptunghi_Centrat drept_centrat = new Dreptunghi_Centrat(2.4,3.5,6,9); Centrat c = (Centrat) drept_centrat; double cx = c.getCentruX(); //coordonatele centrului double cy = c.getCentruY(); //calculam distanta de la origine double dist = Math.sqrt(cx*cx + cy*cy); System.out.println("Suprafata " + drept_centrat.Suprafata()); System.out.println("Distanta de calibrat : " + dist); } }

Dupa cum se observa, interfata declarata este Centrat, iar clasa care o implementeaza este Dreptunghi_Centrat. Deoarece vorbim despre o figura geometrica de tip dreptunghi aceasta clasa, Dreptunghi_Centrat va mosteni de asemenea si clasa Dreptunghi.

Figura 4.1 Diagrama claselor din exemplul pentru interferte

Pentru a intelege mai bine toata ierarhia de clase care sunt folosite, avem diagrama din figura 4.1. In aceasta figura se poate vedea cum interfata este implementata in Dreptunghi_centrat, iar clasa abstracta Forma este mostenita de Dreptunghi. Implicit, atunci si Dreptunghi_centrat va mosteni proprietatile clasei Forma. In continuare vom prezenta o dezbatere pe seama diferentelor si asemanarilor dintre interfete si clasele abstracte. Interfete si clase abstracte Atunci cand definim tipuri de data abstracte (de exemplu Forma) care pot avea subclase (de exemplu Cerc, Dreptunghi etc) vom avea de ales daca acel tip de data este o clasa abstracta sau o interfata. O interfata este recomandata pentru ca orice clasa poate sa o implementeze, chiar si atunci cand acea interfata extinde o alta superclasa care nu are nici o legatura cu interfata. Totusi, cand o interfata contine multe metode poate deveni greoi implementarea tuturor metodelor, in continuu pentru fiecare clasa ce o implementeaza. Din acest punct de vedere putem spune ca o clasa abstracta nu trebuie sa fie in intregime abstracta, ea poate contine o implementare cel putin partiala, iar subclasele o pot folosi pe aceasta. Pe de alta parte, o clasa care extinde o clasa abstracta nu mai poate extinde (mosteni) o alta clasa, ceea ce duce la unele dificulati. O alta diferenta dintre clase abstracte si interfete este legata de compatibilitate. Daca definim interfata si o adaugam intr-o librarie, iar mai apoi adaugam o metoda acelei interfete, atunci clasele care implementau versiunea anterioara de interfata vor fi stricate (nu implementeaza corect interfata noua). Se spune ca am stricat compatibilitatea. Daca folosim clase abstracte pe de alta parte, putem adauga metode nonabstracte fara ca si clasele ce o mostentesc sa fie alterate de aceasta. Mai multe exemple cu interfete Folosirea referintelor la o interfata Desi nu putem instantia o interfata, se poate lucra cu un obiect care face referinta la o interfata. Atunci cand apelam o metoda a unui obiect prin referinta la interfata, este de fapt vorba de metoda implementata de obiect. Pentru a intelege mai bine conceptul avem interfata de mai jos:
public interface Serii { int getNext(); //numarul urmator din serie void reset(); // resetez void setStart(int x); // cu ce incep } class DinDoiinDoi implements Serii {

10

int start; int val; DinDoiinDoi() { start = 0; val = 0; } public int getNext() { val += 2; return val; } public void reset() { start = 0; val = 0; } public void setStart(int x) { start = x; val = x; } } class DinTreiinTrei implements Serii { int start; int val; DinTreiinTrei() { start = 0; val = 0; } public int getNext() { val += 3; return val; } public void reset() { start = 0; val = 0; } public void setStart(int x) { start = x; val = x; }

11

} class DemoSerii { public static void main(String args[]) { DinDoiinDoi doiobj = new DinDoiinDoi (); DinTreiinTrei treiobj = new DinTreiinTrei (); Serii ob; for(int i=0; i < 10; i++) { ob = doiobj; System.out.println("Urmatorul numar par " + ob.getNext()); //apelez metoda definita de interfata //implementata in clasa DinDoiinDoi ob = treiobj; System.out.println("Urmatorul numar ternar " + ob.getNext()); //apelez metoda definita de interfata //implementata in clasa DinTreiinTrei } } }

putem spune ca sunt de acelasi tip.La fel putem avea instantierea lui ob cu un obiect de tipul DinTreiinTrei pe acelasi principiu: ob = treiobj;. Observam ca interfata nu contine deocamdata variabile, insa ambele clase folosesc aceleasi tipuri de variabile si anume int start; si int val; pentru a contoriza urmatorul numar si pozitia de inceput. Oare aceste variabile pot fi declarate in interfata? Variabile in interfete Variabilele in interfete trebuie sa fie static, public si final. Practic este vorba de constante.
interface IConst { int MIN = 0; int MAX; String ERRORMSG = "Eroare de limita"; } class DemoIConst implements IConst { public static void main(String args[]) { int nums[] = new int[MAX];

Cele doua clase DinDoiinDoi si DinTreiinTrei implementeaza aceeasi interfata si anume Serii. Aceasta va fi referita prin obiectul ob, insa acest obiect nu poate fi instantiat cu o interfata. Totusi, el va prinde forma atunci cand este referentiat prin doiobj, fapt perfect legal pentru ca

12

for(int i=MIN; i < 11; i++) { if(i >= MAX) System.out.println(ERRORMSG); else { nums[i] = i; System.out.print(nums[i] + " "); } } } }

Orice incercare de modificare a valorii unei constante va esua la compilare


var.java:12: cannot assign a value to final variable MAX MAX=20; ^ 1 error

De asemena, orice nerespectare a conditiilor de mai sus va aduce cu sine aparitia diverselor erori de compilare corespunzatoare.

Exceptii
O exceptie este o eroare care apare la un moment dat in timpul rularii programului. Folosind mecanismul de tratare al erorilor oferit de Java, putem sa controlam logic ce se intampla atunci cand apare o eroare. Un avantaj pe care acest mecanism il ofera este ca automatizeaza codul pentru tratarea erorilor, care initial trebuia introdus manual de programator, adica prevazand fiecare situatie in parte. De exemplu, atunci cand o metoda esueaza, este returnat un cod de eroare, iar corespunzator acelui cod, se verifica ce anume nu a mers sau a esuat. Acest mod de rezolvare este greoi si necesita atentie din partea programatorului. Mecanismul oferit de Java este ca o plasa de protectie, care nu permite ca programatorul sa greseasca, deoarece cand apare o eroare, automat o functie handler de exceptii este apelata, fara sa avem grija codului returnat de functii. Un alt motiv pentru care tratarea exceptiilor este importanta, este ca Java defineste un standard pentru diversele tipuri de exceptii de exemplu fisierul ce nu exista sau diviziune cu zero. Cum functioneaza? Atunci cand o eroare apare intr-o metoda, metoda creaza un obiect si il preda sistemului runtime Java. Obiectul poarta denumirea de obiectul exceptiei sau mai simplu, exceptie. El contine informatie despre eroare, inclusiv tipul erorii si starea programului atunci cand a aparut eroarea. Creearea unui obiect exceptie si predarea acestuia catre sistem se numeste aruncarea unei exceptii. Dupa ce o metoda arunca o exceptie, sistemul runtime incearca sa gaseasca prima functie sau bloc care trateaza acesta eroare. Multimea de aceste posibile functii sau blocuri care sa trateze eroarea ordonata intr-o lista a evenimentelor se numeste call stack sau stiva de apel.

13

Sistemul cauta in stiva de apel o metoda care contine un bloc ce trateaza exceptia. Acest bloc se numeste handler de exceptie. Cautarea incepe cu metoda in care apare eroarea si continua in ordine inversa a apelurilor cu toata stiva de apel. Se spune ca handler-ul de exceptie prinde exceptia. Daca sistemul runtime a epuizat stiva de apel fara a gasi un handler de exceptie potrivit, executia se incheie.

Figura 4.2 Stiva de apel si incercarea de tratare a erorii Sa presupunem ca apare eroarea in cadrul functiei m1. Metoda va fi aruncata in functia care a apelat metoda m1 si anume m2. Problema este ca functia m2 nu contine nici un bloc de prindere a erorii astfel eroarea este aruncata mai departe in functia care a apelat m2 si anume m3. Acum exista doua posibilitati, m3 sa prinda eroarea si atat sau sa o arunce mai departe. Vom detalia in continuare. Se recomanda ca o metoda sa prinda sau sa specifice faptul ca poate genera o anumita exceptie, daca este cazul. Anume, prinderea erorii se specifica prin cuvantul cheie catch, iar faptul ca arunca o exceptie se specifica prin cuvantul cheie throws. Folosirea handlerului de exceptii try catch Forma generala a acestui mecanism este:
try { // blocul in care eroarea poate apare } catch (ExcepType1 exOb) { // handler pentru ExcepType1 } catch (ExcepType2 exOb) { // handler pentru ExcepType2 }

14

In aceasta declaratie, in blocul specificat de try va sta codul obisnuit si anume instructiunile pe care dorim sa le implementam, iar in catch care si are forma unei functii cu un parametru va sta codul de tratare al exceptiilor. Se arata in aceasta declaratie ca pot fi mai multe handler-e asociate unui try. Aceasta inseamna ca mai multe tipuri de erori pot sa apara intr-un bloc try iar ele sunt tratate separat. Ne putem gandi ca in functie de tipul erorii care apare, aceste handler-e sunt suprascrise, iar apelarea lor se face in functie de tipul exceptiei. Sa analizam un exemplu simplu de tratare a exceptiilor
class ExceptionDemo1 { public static void main(String args[]) { int nums[] = new int[4]; try { System.out.println("Inainte de generarea erorii."); // Generam o exceptie prin depasirea limitei sirului nums[5] = 7; System.out.println("Eroarea a aparut deja. Instructiunea aceasta nu va mai fi executata."); } catch (ArrayIndexOutOfBoundsException exc) { // prindem si tratam exceptia System.out.println("Depasirea limitei sirului!"); } System.out.println("Dupa blocul try catch."); } }

Codul care monitorizeaza erorile va sta in blocul try. Atunci cand apare eroarea, adica atunci cand se incearca executia nums[5] = 7; exceptia este este aruncata din blocul try si se iese din acest bloc. De aceea, instructiunea ce ar fi urmat nu se mai executa, ci se va executa blocul catch aferent. Mai precis, se executa instructiunea System.out.println("Depasirea limitei sirului!");. Dupa ce se termina de executat blocul catch se trece la urmatoarea instructiune si anume
System.out.println("Dupa blocul try catch.");.

Mai departe putem exemplifica pentru modelul din figura 4.2 cum exceptiile pot fi prinse si eventual, tratate in blocul catch scris in alta functie diferita de cea care genereaza eroarea:
class ExceptionDemo1 { public static void genException() { int nums[] = new int[4]; System.out.println("Inainte de generarea erorii."); // Generam o exceptie prin depasirea limitei sirului nums[7] = 10;

15

Instructiunea } }

System.out.println("Eroarea a aparut deja. aceasta nu va mai fi executata?");

class ExceptionDemo2 { public static void main(String args[]) { try { ExceptionDemo1.genException(); } catch (ArrayIndexOutOfBoundsException exc) { // prindem si tratam exceptia System.out.println("Depasirea limitei sirului!"); } System.out.println("Dupa blocul try catch."); } }

Acest program va produce acelasi afisaj ca cel de sus, insa, din cauze diferite. De data aceasta, exceptia este generata in clasa ExceptionDemo1m mai exact, in functia genException. Exceptia nu mai este tratata in cadrul functiei ci este aruncata mai departe in functia care efectueaza apelul, si anume, main din clasa ExceptionDemo2. In cazul in care o exceptie nu este prinsa de nici una din functiile din call stack, atunci exceptia este prinsa de JVM si executia programului se incheie brusc. Acest lucru nu este prea placut pentru utilizatorii programului scris fara un mecanism de tratare a exceptiilor si ii va deceptiona. Mai sus am mentionat ca exceptiile pot avea mai multe tipuri. Daca blocul de catch este scris pentru o alta exceptie decat cea care va apare probabil in blocul try, atunci tot scopul mecanismului de tratare a exceptiilor dispare.
class ExceptionTypeMismatch { public static void main(String args[]) { int nums[] = new int[4]; try { System.out.println("Inainte ca exceptia sa apara."); // generam o exceptie nums[6] = 4; System.out.println("nu va ajunge aici."); } //nu aceasta este tipul de eroare care va apare catch (ArithmeticException exc)

16

{ //prind exceptia System.out.println("In afara limitelor!"); } System.out.println("Dupa try catch."); } }

In exemplul de mai sus se incearca tratarea exceptiei de tipul ArithmeticException si in blocul try apare alt tip de exceptie. Ceea ce se intampla la rulare, este usor de prevazut, si anume, faptul ca exceptia ArrayIndexOutOfBoundException ce apare va duce la oprirea programului. Este ca si cand eroarea nu este tratata. In cazul acesta rezolvarea rapida ar fi sa tratam ambele tipuri de exceptii. Alta rezolvare implica modul in care sunt implementate exceptiile in Java, pe care il vom analiza mai tarziu.
class ExcMultipleDemo { public static void main(String args[]) { int numar[] = { 5, 3, 12, 1, 20, 27, 10, 56 }; int deimp[] = { 1, 0, 3, 4, 0, 5 }; for(int i=0; i<numar.length; i++) { try { System.out.println(numar[i] + " / " + deimp[i] + " este " + numar[i]/deimp[i]); } catch (ArithmeticException aex) { System.out.println("impart la zero la pasul "+ i+"!"); } catch (ArrayIndexOutOfBoundsException iex) { System.out.println("Am depasit limita la pasul "+i+"."); } } } }

Acesta este rezultatul:


5 / 1 este 5 impart la zero la pasul 1! 12 / 3 este 4 1 / 4 este 0 impart la zero la pasul 4! 27 / 5 este 5

17

Am depasit limita la pasul 6. Am depasit limita la pasul 7.

In cazul de mai sus, am scris handler-ele astfel ca ele sa trateze si erorile de tipul ArithmeticException si cele de tipul ArrayIndexOutOfBoundsException. Astfel enumerand blocurile catch unul sub celalalat putem trata mai multe tipuri de exceptii ce pot apare pe parcurs. Alta remarca ar fi ca in cadrul blocurilor catch regulile domeniilor de vizibilitate al variabilelor si functiilor se mentin. Dupa cum se poate observa folosim i din cadrul for-ului pentru ca blocul try catch este in interiorul acestui for si i este vizibil tot in interiorul for-ului. Prinderea exceptiilor conform ierahiei Sa analizam in continuare cealalta metoda de a rezolva cazul de mai sus. Atunci cand prindem exceptii ca cele de mai sus, este indicat, sa plasam catch-urile unul sub altul, insa nu putem scrie zeci de catch-uri pentru a trata toate erorile posibile. In Java exista o ierarhie de clase ce se ocupa de exceptii si una care se ocupa de erori. Toate au ca parinte clasa throwable:

Figura 4.3 Ierarhia claselor de exceptii Dupa cum se vede in figura clasa Throwable este parinte pentru orice clasa de tip Exception si Error. Asadar, cand scriem codul catch este important sa plasam codul catch in ordinea mostenirilor astfel ca urmatoarea ordine este recomandata:
try { ... } catch (RuntimeException rex) { ... } catch(Exception ex) { ...

18

} catch(Throwable tex) { ... }

Sa vedem si un alt exemplu referitor la prinderea exceptiilor matematice:


class ExcMultipleDemo { public static void main(String args[]) { int numar[] = { 5, 3, 12, 1, 20, 27, 10, 56 }; int deimp[] = { 1, 0, 3, 4, 0, 5 }; for(int i=0; i<numar.length; i++) { try { System.out.println(numar[i] + " / " + " este " + numar[i]/deimp[i]); }

deimp[i] +

"+i+".");

catch (ArrayIndexOutOfBoundsException iex) { System.out.println("Am depasit limita la pasul } catch (Throwable tex) { System.out.println("o alta eroare a aparut."); } }

} }

Avantajul acestui cod este ca indiferent de tipul de eroare care apare in blocul try, aceasta va fi tratata. Problema este ca tratarea este una cat mai general aproximata. Eventual putem folosi informatii din parametrul din catch, dupa cum vom vedea in continuare. Cum aruncam o exceptie? In exemplele prezentate pana acum, exceptiile au fost generate automat de JVM Este posibil sa provocam - aruncam o exceptie utilizand throw. Forma generala este:
throw exceptioObject;

19

In acest caz exceptioObject este un obiect de clasa exceptie derivat din Throwable. Mai jos avem un exemplu pentru throw:
class ThrowDemo { public static void main(String args[]) { try { System.out.println("inainte ca exceptia sa apara."); throw new ArithmeticException(); } catch (ArithmeticException exc) { System.out.println("Exceptie prinsa."); } System.out.println("Dupa blocul try catch."); } }

Obiectul de tip ArithmeticException a fost creat folosind operatorul new. Apoi a fost aruncat folosind operatorul throw. Mai apoi a fost prins, adica transmis ca parametru in blocul catch. Clasa Throwable Pana in acest moment singurul lucru pe care l-am facut atunci cand am prins erori a fost sa afisam un mesaj corespunzator erorii prinse. Daca profitam de faptul ca toate exceptiile deriva din Throwable, atunci putem folosi functiile acestei clase si anume: Metoda Descriere Throwable fillInStackTrace( ) Returneaza un obiect de tip Throwable care contine stiva completa cu apelurile efectuate. String getLocalizedMessage( ) Returneaza o descriere specifica subclasei a erorii. String getMessage( ) Returneaza descrierea generala a erorii. void printStackTrace( ) Afiseaza stiva. void printStackTrace(PrintStream stream) Trimite continutul stivei unui stream. String toString( ) Returneaza un sir ce contine descrierea erorii.

Aceste metode se pot apela in blocul catch dupa cum urmeaza:


class ExceptionDemo1 { public static void genException() { int nums[] = new int[4]; System.out.println("Inainte de generarea erorii.");

20

// Generam o exceptie prin depasirea limitei sirului nums[7] = 10; System.out.println("Eroarea a aparut deja. Instructiunea aceasta nu va mai fi executata?");

} } class ExceptionDemo2 { public static void main(String args[]) { try { ExceptionDemo1.genException(); } catch (ArrayIndexOutOfBoundsException exc) { System.out.println("Mesajul standard al exceptiei: "); System.out.println(exc); System.out.println("Mesajul exceptiei: "); System.out.println(exc.getMessage()); System.out.println("Mesajul local: "); System.out.println(exc.getLocalizedMessage()); System.out.println("\nStack trace: "); exc.printStackTrace(); } System.out.println("Dupa blocul try catch."); } }

Rezultatul rularii acesui program este unul previzibil:


Inainte de generarea erorii. Mesajul standard al exceptiei: java.lang.ArrayIndexOutOfBoundsException: 7 Mesajul exceptiei: 7 Mesajul local: 7 Stack trace: java.lang.ArrayIndexOutOfBoundsException: 7 at ExceptionDemo1.genException(ex.java:8) at ExceptionDemo2.main(ex.java:21) Dupa blocul try catch.

21

finally Acest bloc din cadrul declaratiei try catch permite executia unor instructiuni inainte de iesirea din blocul try catch. De exemplu atunci cand apare o exceptie, se poate efectua un return din functie inainte ca instructiunile de inchidere de fisier sau de conexiuni sa aiba loc. Este recomandat ca aceste tipuri de instructiuni sa le scriem in finally:
try { // blocul in care eroarea poate apare } catch (ExcepType1 exOb) { // handler pentru ExcepType1 } finally { //cod care se executa tot timpul }

Mai jos avem o exemplificare a folosirii acestei clauze:


class ExFinally { public static void genException(int p) { int t; int nums[] = new int[2]; System.out.println("Am primit " + p); try { switch(p) { case 0: t = 6 / p; break; case 1: nums[10] = 9; break; case 2: // return din blocul try return; } } catch (ArithmeticException exc) { System.out.println("Impartire la zero!"); return; // return din catch

22

} catch (ArrayIndexOutOfBoundsException exc) { System.out.println("Limite depasite."); } finally { System.out.println("Parasesc try."); } } } class FinallyDemo { public static void main(String args[]) { for(int i=0; i < 3; i++) { ExFinally.genException(i); System.out.println(); } } }

In clasa FinallyDemo se apeleaza de trei ori metoda statica genException() din clasa ExFinally. In functie de parametru se genereaza doua exceptii si in al treilea caz se paraseste blocul try. Se observa ca in toate cazurile, indiferent ce catch este executat, se intra pe blocul finally inainte de a reveni din apelul functiei genException().

Am primit 0 Impartire la zero! Parasesc try. Am primit 1 Limite depasite. Parasesc try. Am primit 2 Parasesc try.

In unele cazuri, daca o metoda poate genera o exceptie pe care nu o trateaza, trebuie semnalat acest lucru. Pentru aceasta se foloseste throws:
tip_de_data nume_metoda(lista_parametrii) throws lista_exceptii { //corpul metodei }

23

lista_exceptii reprezinta una sau mai multe exceptii care sunt separate prin virgula. Aceste exceptii pot fi generate in interiorul metodei. Exceptiile care sunt subclase ale clasei Error sau al clasei RuntimeException nu trebuie neaparat declarate in acea lista. Regula este impusa tuturor celorlalte tipuri de Exceptii. Iata cateva clase de exceptii, subclase ale lui Exception ce trebuie declarate cu throws in metoda apelanta: CloneNotSupportedException, IOException, NamingException, NotBoundException. Clasa de mai jos este gresit declarata:
import java.io.*; class ThrowsDemo { public static void writeList(int[] vector) { PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt")); for (int i = 0; i < 10; i++) { out.println("valoare de la : " + i + " = " + vector[i]); } out.close(); } }

Iar la compilare vom primi urmatoarea eroare:


throws.java:6: unreported exception java.io.IOException; must be caught or decla red to be thrown PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt")) ; ^ 1 error

Corect este sa declaram ca metoda poate arunca o exceptie de tip IO:


public static void writeList(int[] vector) throws IOException { ...

Iata cateva exceptii, majoritatea derivate din RuntimeException care nu trebuie incluse in lista throws:

24

Exceptia ArithmeticException ArrayIndexOutOfBoundsException ArrayStoreException ClassCastException IllegalArgumentException IllegalMonitorStateException IllegalStateException IllegalThreadStateException IndexOutOfBoundsException NegativeArraySizeException NullPointerException NumberFormatException SecurityException StringIndexOutOfBounds CloneNotSupportedException IllegalAccessException InstantiationException InterruptedException NoSuchFieldException NoSuchMethodException

Descriere Eroare de tip aritmetic, de exemplu impartire la zero. Indexul din sir este in afara limitelor. Se incearca atribuirea unui element din sir cu o valoare incorecta. Cast incorect. Parametrul transmis metodei este gresit. Operatie invalida, de exemplu, asteptarea unui fir de executie neblocat. Aplicatia nu ruleaza in mediu corect, compatibil. Operatia aplicata nu este in concordanta cu starea firului de executie Indexul este in afara limitelor Sir creat cu limite negative. Folosirea obiectului neinstantiat (egal cu null). Conversia dintr-un string intr-un numar a esuat. Posibila tentativa asupra securitatii. Indexul unui string este in afara limitelor Incercare de a folosi un obiect ce nu implementeaza Cloneable Acces la clasa nepermis. S-a incercat instantierea unei interfete sau clase abstracte Un fir de executie a fost intrerupt S-a incercat accesarea unui membru care nu exista. S-a incercat accesarea unei metode care nu exista.

Desi Java implementeaza multe clase care se ocupa cu tratarea erorilor, mecanismul nu este limitat la genul de liste de mai sus. Putem defini propriile noastre clase ca subclase ale parintelui Exception. Iata un exemplu de creare a unei exceptii customizate:
class MyException extends Exception { int n; int d; MyException(int i, int j) { n = i; d = j; } public String toString() { return "Rezultatul " + n + " / " + d + " nu este intreg."; } }

25

class CustomExceptionDemo { public static void main(String args[]) { int numar[] = { 3, 6, 30, 44, 21, 50, 16 }; int deimp[] = { 3, 0, 12 , 2, 5 }; for(int i=0; i<numar.length; i++) { try { if((numar[i]%deimp[i]) != 0) throw new MyException(numar[i], deimp[i]); System.out.println(numar[i] + " / " + deimp[i] + " este " +numar[i]/deimp[i]); } catch (ArithmeticException exc) { System.out.println("Impart la zero!"); } catch (ArrayIndexOutOfBoundsException ex) { System.out.println("Index in afara limitelor."); } catch (MyException ex) { System.out.println(ex); } } } }

In primul rand clasa trebuie sa mosteneasca Exception. In al doilea rand aruncarea exceptiei se face prin apelul throws cu un obiect de clasa customizata. Pentru crearea unui obiect de acest tip de exceptie s-a folosit un constructor cu doi parametrii de tip int. Dupa ce exceptia este aruncata se poate folosi obiectul ex care este de tip MyException, ca orice obiect din Java.

26

I/O in Java
Sistemul de tratare al intrarilor si iesirilor si al lucrului cu siruri de data in Java este extrem de variat. In cele ce urmeaza vom incerca sa surprindem conceptele, practic sa vedem varful iceberg-ului, pentru ca mai tarziu sa intram in detalii anumite aspecte legate de IO. Stream in Java Operatiuniile de scriere/citire in Java se realizeaza cu ajutorul stream-urilor. Un stream este o abstractie a datelor concrete conectat la un sistem de I/O (fisier, memorie, imprimanta etc). In general va exista o sursa de la care vom citi si atunci vorbim de input stream si o sursa catre care scriem si atunci vorbim de output stream:

Fig 4.4 Stream-uri I/O Byte Stream si Character Stream In Java exista doua versiuni de stream: byte si caracter. Byte stream-ul este un wrapper peste un sir de byte. Acesta se foloseste la scrierea si citirea datelor binare si la transmiterea, citirea de fisiere. Stream-urile de caractere sunt destinate lucrului cu texte si folosesc Unicode pentru a putea fi internationalizate. Clasele Byte Stream sunt definite prin doua ierarhii de clase: InputStream si OutputStream din care deriva o serie de clase pentru lucrul cu diverse dispozitive.

27

Clasele destinate lucrului cu text, si anume Character Stream, sunt Reader si Writer. Analog din acestea deriva o serie de subclase avand particularitatile lor. Exista stream-uri predefinite in Java pentru lucru cu intrarea standard : System.in, iesirea standard System.out si eroarea standard System.err. Byte Stream

Ierarhia tipurilor de data byte stream este prezentata mai jos:

Figura 4.5 Ierarhia claselor byte stream In tabelul de mai jos avem explicatiile pentru aceste clase: Clasa Scop BufferedInputStream input stream ca buffer BufferedOutputStream output stream ca buffer ByteArrayInputStream Input stream care citeste din sir de byte ByteArrayOutputStream Output stream ce scrie in sir de byte DataInputStream Un input stream ce contine metode pentru citirea tipurilor standard de data din Java DataOutputStream Un output stream ce contine metode pentru scrierea tipurilor standard de data din Java FileInputStream Input stream care citeste dintr-un fisier FileOutputStream Output stream care scrie intr-un fisier FilterInputStream Implementeaza InputStream 28

FilterOutputStream InputStream ObjectInputStream ObjectOutputStream OutputStream PipedInputStream PipedOutputStream PrintStream PushbackInputStream SequenceInputStream
.

Implementeaza OutputStream Clasa abstracata ce descrie input stream Input stream pentru obiecte Output stream pentru obiecte Clasa abstracata ce descrie output stream pipe pentru intrari pipe pentru iesiri Output stream ce poate apela print( ) si println( ) Input stream care permite ca byte-tii sa fie returnati dintr-un stream Input stream ce este o combinatie de mai multe stream-uri de intrari ce vor fi citite secvential unul dupa celalalt

Iata si une exemplu de folosire al unora dintre clasele mai sus mentionate:
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; class CopyBytes { public static void main(String[] args) throws IOException { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("filei.txt"); out = new FileOutputStream("fileo.txt"); int c; while ((c = in.read()) != -1) { out.write(c); } } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } } }

In exemplul de mai sus este folosita functia read() care are ca scop citirea byte cu byte a intrarii. De altfel, clasa InputStream defineste o serie de metode ce sunt mostenite si de copii sai:

29

Metoda int available( ) void close( )

Descriere Returneza numbarul de bytes disponibili de la intrare. Inchide sursa. Alte incercari de a citi dupa, vor genera IOException. void mark(int numBytes) Pune un semn la byte-ul curent care va ramane valid pana ce vom fi citit numBytes. boolean markSupported( ) Returneaza true daca stream-ul suporta sistemul de marcare descris. int read( ) Returneaza un integer ce reprezinta byte-ul urmator din stream sau 1 daca s-a intalnit sfarsit de fisier. int read(byte buffer[ ]) Se incearca citirea unui buffer de lungimea celui specificat in paranteze si returneaza numarul de bytes citit sau -1 daca s-a intalnit sfarsit de fisier. int read(byte buffer[ ], int offset,int numBytes) Se incearca citirea unui buffer de lungime numBytes, in buffer incepand cu offset, si returneaza numarul de bytes cititi sau -1 daca am ajuns la sfarsitul fisierului. void reset( ) Reseteaza acel semn stabilit de mark. long skip(long numBytes) Ignora numBytes bytes din intrare si returneaza numarul de bytes ignorat. Mai jos avem descrise metodele specifice unui OutputStream: Metoda Descriere void close( ) Inchide stream-ul de scriere catre iesire, o alta scriere generand IOException. void flush( ) Forteaza scrierea din buffer la iesire, curatind bufferul. void write(int b) Scrie un byte la iesire. Parametru este un int. void write(byte buffer[ ]) Scrie un sir de bytes la iesire. void write(byte buffer[ ], int offset, int numBytes) Scrie o portiune dintr-un buffer, delimitat de offset si numBytes. Character Stream Platforma Java permite utilizarea carcterelor folosind conventii Unicode. Stream-urile carcater sunt automat transpuse intr-un format intern, care poate fi ulterior convertit la diverse formate (depinde de locul unde va fi instalata aplicatia). Pentru majoritatea aplicatiilor, stream-urile de carctere I/O, sunt la fel de simple ca cele byte si regulile sunt aceleasi. Situatia se poate complica daca programul va fi unul international. Toate clasele de acest gen deriva din Reader sau Writer. Iata mai jos ierarhia acestora:

30

Mai jos este un program pentru exemplificarea utilizarii acestor clase:


import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; class CopyCharacters { public static void main(String[] args) throws IOException { FileReader inputStream = null; FileWriter outputStream = null; try { inputStream = new FileReader("filei.txt"); outputStream = new FileWriter("fileo.txt"); int c; while ((c = inputStream.read()) != -1) { outputStream.write(c); } } finally { if (inputStream != null)

31

{ inputStream.close(); } if (outputStream != null) { outputStream.close(); } } } }

Metodele suportate de aceste clase sunt aceleasi ca si la byte streams cu deosebirea ca aceste metode lucreaza cu char si nu byte. De exemplu in clasa Reader, iata cateva metode ce difera sau sunt noi: int read(CharBuffer buffer) Se incearca citirea unui buffer de lungimea celui specificat in paranteze si returneaza numarul de bytes citit sau -1 daca s-a intalnit sfarsit de fisier. CharBuffer este o clasa ce incapsuleaza o secventa de caractere asa cum face String. Acelasi ca in cazul byte:de data aceasta se citesc caractere

int read(char buffer[ ])

In cazul clasei Writer exista cateva metode noi: Writer append(char ch) Writer append(CharSequence chars) scrie la sfarsitul stream-ului caracterul ch. scrie secventa chars la sfarsitul stream-ului. Returneaza o referinta a stream-ului ce invoca metoda. CharSequence este o interfata ce defineste operatii read-only pe o secventa de caractere. scrie secventa chars la sfarsitul stream-ului delimitata de begin si end.

Writer append(CharSequence chars, int begin, int end)

Stream-uri salvate in buffer-e Majoritatea exemplelor nu controlau buffer-ul in care se salveaza datele inainte de a fi afisate sau citite. Pentru a optimiza accesul la disc, retea sau oricare sursa implicata in schimbul de date se poate folosi un mecanism Java si anume Buffered Streams. Acestea sunt siruri de date din memorie in care se plaseaza date controlat. Clasele pentru stream-uri byte sunt BufferedInputStream si BufferedOutputStream in timp ce pentru stream-urile caracter exista BufferedReader si BufferedWriter. Iata si un exemplu de folosire al acestor clase:

32

import java.io.*; class BufferDemo { public static void main(String args[]) throws Exception { FileReader fr = new FileReader("filei.txt"); BufferedReader br = new BufferedReader(fr); String s; while((s = br.readLine()) != null) { System.out.println(s); } fr.close(); } }

Evident scopurile pot fi multiple, si in general se va folosi pentru citirea controlata in buffer-e de o anumita dimensiune.

33

Cursul 5:

Structuri de date I

Cursul 5 ................................................................................................................................................... 1 Array ................................................................................................................................................... 3 Siruri de tipuri de date primitive ...................................................................................................... 4 Siruri de date de tip referinta ........................................................................................................... 4 Siruri multidimensionale .................................................................................................................. 6 Initializarea sirurilor ......................................................................................................................... 8 Copierea, clonarea sirurilor.............................................................................................................. 9 Sirurile sunt imutabile.................................................................................................................... 11 Verificarea egalitatii a doua siruri................................................................................................... 11 Clasa Vector ...................................................................................................................................... 12 Metodele de baza .......................................................................................................................... 12 Crearea Vectorilor ......................................................................................................................... 14 Manipularea elementelor din vector.............................................................................................. 14 Adaugarea la sfarsit ................................................................................................................... 14 Adaugarea undeva in interior..................................................................................................... 15 Adaugarea unei colectii .............................................................................................................. 15 Vectori de tipuri primitive .......................................................................................................... 16 Afisarea vectorilor...................................................................................................................... 16 Stergerea elementelor ............................................................................................................... 16 Marimea unui vector ................................................................................................................. 17 Capacitatea vectorului ............................................................................................................... 18 Cautarea in vector ......................................................................................................................... 18 Preluarea dupa index ................................................................................................................. 18 Preluare dupa pozitie in sir......................................................................................................... 19 Enumerarea elementelor ........................................................................................................... 19 Gasirea elementelor intr-un sir .................................................................................................. 20 Copierea unui vectorilor ................................................................................................................ 21 Stiva .................................................................................................................................................. 22 Clasa Stack ..................................................................................................................................... 24 Adaugarea elementelor ............................................................................................................. 24 Stergerea unui element ............................................................................................................. 24 1

Enumerator ....................................................................................................................................... 25 IO: scanare si formatare..................................................................................................................... 28 Scanner ......................................................................................................................................... 28 Formatarea .................................................................................................................................... 29 Serializarea obiectelor ................................................................................................................... 31 Clasa StringBuffer .......................................................................................................................... 33

Array
Un sir de date sau un array este o structura ce contine valori multiple de acelasi tip de data. Lungimea unui sir de date este stabilita la crearea array-ului si va fi fixa pe intreaga existenta a acestuia. In figura de mai jos si anume figura 5.1 am reprezentat un array cu zece elemente si indexul incepand cu pozitia zero. Evident ultimul element poate fi accesat la pozitia data de lungimea sirului minus 1.

Figura 5.1 un sir cu 10 elemente. Cum se declara un array? Sa consideram faptul ca un sir este un obiect ce contine un set de elemente. Aceste elemente pot fi, fie de tip primitiv (int, float, char etc ), fie de tip referinta (deriva din Object). Pentru a declara un sir de un date anumit tip se specifica acel tip de data urmat de paranteze:
int[] un_sir_de_date;

Odata ce un sir este declarat, el poate fi instantiat. Este necesar, ca inainte de folosirea sirului, acesta sa fie instantiat. Ca orice referinta, se va folosi operatorul new pentru instantiere, urmat de tipul de data si numarul de elemente al sirului:
int[] un_sir_de_date = new int[10];

Iata o functie de exemplu ce foloseste la instantierea unui sir de o anumita marime transmisa ca parametru:
int[] creeazaSir(int marime) { return new int[marime];

} Daca incercam sa cream un sir a carui lungime este negativa atunci va fi aruncata eroarea NegativeArraySizeException. Sirurile de lungime zero sunt insa corecte din punct de vedere sintactic.

Siruri de tipuri de date primitive Atunci cand se creeaza un sir de tipuri de data primitv, sirul ca contine valorile acelor elemente. De exemplu, pentru un sir cu patru intregi prezentat in figura de mai jos avem si durata obiectului in memorie: in acest caz pe heap se aloca un numar fix si anume patru ori marimea tipului int.

Figura 5.2 Stiva si heap in cazul unui sir de primitive In figura 5.2 am reprezentat cum arata un sir in heap (memoria dinamica a programului). Acestui sir ii pot fi atribuite aceste valori in doua moduri:
int[] sir = new sir[3]; sir[0] = 23; sir[1] = 12; sir[2] = 45; sir[3] = 78;

Sau
int[] sir = {23,12,45,78};

Siruri de date de tip referinta Spre deosebire de tipurile de data primitive, atunci cand cream un sir de obiecte, acestea nu sunt stocate intr-un masiv. Array-ul va stoca doar referintele catre obiectele propriu-zise, si initial fiecare referinta este null. Pentru a reprezenta instantierea unui sir de obiecte avem figura 5.3. De aceasta data fiecare obiect se afla in alta zona de memorie (in heap), alta decat ce in care este declarat sirul. Elementele din care este compus sirul sunt referintele acestor obiecte, care sunt sau pot fi, de tipuri diferite.

Figura 5.3 Reprezentare Stivei si heap-ului in cazul unui sir de obiecte Inainte de a trece mai departe sa vedem un exemplu din fiecare sir de date mentionat mai sus. Pentru inceput vom alege sirul de primitive de tip int, si anume o simpla parcurgere a unui sir cu patru elemente si afisarea acestora:
public class ArrayDemo { public static void main(String[] args) { // declar un sir de int int[] sir = {23,12,45,78}; // assign a value to each array element and print for (int i = 0; i < sir.length; i++) { System.out.print(sir[i] + " "); } System.out.println(); } }

Dupa cum vedem in acest exemplu toate elementele din sir sunt de tipul int. Pe de alta parte, daca utilizam un sir de obiecte, putem intalni elemente de clase diferite. Totusi, in general un sir de referinte va contine elemente de aceeasi clasa specifica.
class Country { String name; long population; } class Invention { String name; String description;

} class Painting { String name; String technique; } public class ArrayDemo { public static void main(String[] args) { // declar un sir de int Object[] sir = new Object[4]; sir[0] = new Country(); ((Country)sir[0]).name = "Italia"; ((Country)sir[0]).population = 600000000000l; sir[1] = new Painting(); ((Painting)sir[1]).name = "Mona Lisa"; ((Painting)sir[1]).technique ="Sfumatto"; sir[2] = new Invention(); ((Invention)sir[2]).name = "Elicopter"; ((Invention)sir[2]).description = "masina pentru zburat propulsata" + " de un rotor orizontal"; sir[3] = new Country(); ((Country)sir[0]).name = "Franta"; ((Country)sir[0]).population = 650000000000l; } }

Siruri multidimensionale Din moment ce un sir de date poate contine referinte catre alte obiecte, de ce nu ar contine referinte catre alte siruri? Atunci cand un sir se refera, sau contine alt sir avem de a face cu siruri multidimensionale. Iata un exemplu clasic de sir multidimensional:
int matrix[][];

Pentru a instantia un astfel de sir se vor specifica, de obicei, ambele dimensiuni si anume:
int matrix[][] = new int[3][2];

In acest caz avem un sir bidimensional, astfel: 3 siruri care la randul lor contin siruri de cate doua elemente de tip int. 6

Accesarea acestor elemente se poate face prin specificarea ambilor indici:


matrix[1][2] = 34;

Un exemplu simplu pentru a intelege mai bine lucrul cu matrici este urmatorul:
public class MatrixDemo { public static void main(String[] args) { int[][] matrix = new int[3][3]; matrix[0][0]=1; matrix[1][1]=1; matrix[2][2]=1; for(int i=0;i<3;i++) { for(int j=0;j<3;j++) System.out.print(matrix[i][j]); System.out.println(); } } }

In acest exemplu se instantiaza o matrice de 3 x 3 elemente. Apoi se asigneaza valoarea 1 elementelor pe diagonala si mai departe se afiseaza valorile din matrice, parcurgand mai intai pe linii si apoi pe coloane. Acest mod de folosirea a sirurilor multidimensionale nu este unic. Putem initializa subsiruri de diverse dimensiuni ca in exemplul de mai jos:
public class SirCrescator { public static void main(String args[]) { float sir[][] = new float[4][]; //orice subsir poate avea numar diferit de elmente //urmeaza instantierea fiecarui subsir sir[0] = new float[5]; sir[1] = new float[]{2.3f,5,6.7f,11}; sir[2] = new float[]{1f,4.2f,7f,4.1f,10f,9f}; sir[3] = new float[20]; //urmeaza partea de accesare a datelor //vom modifica doar primul subsir sir[0][1] = 1; sir[0][1] = 43; sir[0][3] = 89; //si ultimul subsir //deoarece celelalte contin deja valori sir[3][5] = 14; sir[3][10] = 5; sir[3][17] = 19;

//mai intai parcurgem sirul "mare" pana //la lungimea acestuia (pe linii) for (int i = 0; i < sir.length; i++) { System.out.print("Element( "+ i +" ): "); //aici parcurgem subsirurile for (int j = 0; j < sir[i].length; j++) { System.out.print(" "+ sir[i][j]); } System.out.println(); } } }

Afisare:
Element( Element( Element( Element( 0 1 2 3 ): ): ): ): 0.0 2.3 1.0 0.0 43.0 0.0 89.0 0.0 5.0 6.7 11.0 4.2 7.0 4.1 10.0 9.0 0.0 0.0 0.0 0.0 14.0 0

In acest exemplu avem un sir de 4 subsiruri de tip float. Acestea pot avea dimensiuni diferite. Primul subsir initializat si anume sir[0] va fi initializat ca un sir de float de 5 elemente. Vom avea acces l a elementele acestuia de la sir[0][0]pana la sir[0][4]. Mai departe urmatorul subsir este sir[1], care este automat instantiat si intializat cu valori astfel:
sir[1] = new float[]{2.3f,5,6.7f,11};

In acest caz subsirul de pe pozitia 1 are 4 elemente cu valorile dintre acolade. Accesarea unui anumit element din acest sir bidimensional se face prin ambii indecsi si anume
sir[3][5] = 14;

adica elementul din subsirul 3 cu indexul 5 in acel subsir. Apoi se poate parcurge ca in cazul matricii, insa, de data aceasta se tine cont de lungimea fiecarui subsir in parte:
for (int j = 0; j < sir[i].length; j++)

pentru ca de aceasta data fiecare subsir are o lungime variabila. Aplicatiile acestor siruri multidimensionale provin din diversele necesitati matematice, cum ar fi de exemplu calculul triunghiului lui Pascal, sau reprezentarea valorilor din figuri geometrice, piramide, trapez, triunghi etc. Initializarea sirurilor Atunci cand se creeaza un array, sistemul Java va initializa fiecare element al sirului cu o valoare, zero daca este vorba de siruri numerice, false daca vorbim de siruri cu elemente de tip boolean, sau zero pentru cele care contin referinte. Totusi, se pot specifica valorile direct la instantierea obiectelor:
String[] names = {"Ion","Vasile","Andrei","Radu"};

Acesta este un sir de String-uri care contine 4 elemente. Din acest moment el nu mai poate fi modificat, adica nu se pot sterge sau adauga elemente in sir. El va contine pe toata durata sa fix 4 elemente. Valorile din aceste elemente se pot, totusi, modifica. Pentru siruri multidimensionale aceasta initializare poate avea loc astfel:
String[][] sir_nume = {{"Ion","Vasile","Adrian"},{"Andrei","Radu"}}; System.out.println(sir_nume[0][2]);

In acest caz avem, doua subsiruri de String-uri, primul cu trei elemente si al doilea cu doua. Se va afisa al treilea elment din primul subsir. Mai mult se poate ca un sir sa fie trimis ca parametru si creeat la apelul metodei astfel:
public static void functie(String[][] sir) { for(int i=0;i<sir.length;i++) { for(int j=0;j<sir[i].length;j++) System.out.print(" " +sir[i][j]); System.out.println(); } }

Aceasta este functia ce primeste un sir de siruri de String-uri si ce afiseaza fiecare element din acele subsiruri. Mai jos avem si apelul acestei functii
functie (new String[][]{{"Ion","Vasile","Adrian"},{"Andrei","Radu"}});

Inainte ca functia sa fie apelata, se creeaza un obiect de tip String[][] cu valorile dintre acolade, si acel obiect este transmis functiei ca parametru. Copierea, clonarea sirurilor Un sir poate fi copiat in alt sir, sau poate fi clonat in alt sir. Aceasta copiere se poate face in mai multe feluri. Daca trebuie sa crestem numarul de elemente ale unui sir, trebuie sa cream un sir nou cu numarul dorit de elemente si sa copiem element cu element vechiul sir in noul sir. Daca dorim totusi, sa lasam marimea intacta, si sa modificam valorile din sir, trebuie sa clonam acel sir. Metoda arraycopy() a clasei System permite copierea elementelor dintr-un sir in altul. Sirul destinatie trebuie sa fie mai mare sau egal ca numar de elemente, cu sirul sursa. In caz contrar, se arunca o exceptie de tip ArrayIndexOutOfBoundsException la rulare. Metoda arraycopy() are cinci parametrii si anume:
arraycopy (Object sourceArray, int sourceOffset, Object destinationArray, int destinationOffset, int numberOfElementsToCopy).

Mai jos sunt explicatiile pentru fiecare dintre parametri: sourceArray sirul sursa din care se copiaza elementele sourceOffset pozitia din sir de la care consideram elementele sursa 9

destinationArray sirul destinatie, in care se copiaza elementele destinationOffset pozitia din destinatie de incepe copierea elementelor numberOfElementsToCopy cate elemente vor fi copiate.

Mai jos este un exemplu de folosire a functiei arraycopy:


int[] sir_sursa = {23,12,46,8}; int[] sir_destinatie = new int[6]; System.arraycopy(sir_sursa,0, sir_destinatie, 0,sir_sursa.length);

Atentie, atunci cand copiati elemente dintr-un sir in altul, ele trebuie sa fie de acelasi tip de data, sau compatibile, altfel va fi aruncata o exceptie de tip ArrayStoreException. Un exemplu de siruri incompatibile ar fi doua siruri unul de obiecte si unul de primitive. Pentru a intelege mai bine cum se foloseste arraycopy avem clasa de mai jos:
public class copiere { public static void main(String args[]) { int array1[] = {1, 2, 3, 4, 5}; int array2[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; System.out.println("Original size: " + array1.length); printArray(array1); System.out.println("New size: " + doubleArray(array1).length); printArray(doubleArray(array1)); System.out.println("Original size: " + array2.length); printArray(array2); System.out.println("New size: " + doubleArray(array2).length); printArray(doubleArray(array2)); } static int[] doubleArray(int original[]) { int length = original.length; int newArray[] = new int[length*2]; System.arraycopy(original, 0, newArray, 0, length); return newArray; } public static void printArray(int[] sir) { for(int i=0;i<sir.length;i++) { System.out.print(" " +sir[i]); } System.out.println(); } }

In acest exemplu, functia doubleArray apeleaza in interiorul ei, functia System.arraycopy(original, 0, newArray, 0, length); cu destinatia newArray un sir de doua ori mai mare decat originalul. Functia returneaza acest sir cu lungime dubla. 10

Functia printArray afiseaza elementele sirului transmis ca parametru. In main se folosesc cele doua functii pentru a dubla numarul de elemente ale sirurilor originale si anume array1 si array2. Sirurile sunt imutabile Daca declaram un sir fina, si static:
final static int array[] = {1, 2, 3, 4, 5};

nu suntem constransi sa nu putem modifica elementele lui. Ce nu putem insa modifica este insasi structura sirului, si anume numarul de elemente. Adica nu putem avea in continuare o astfel de atribuire:
array = new int[] {6, 7, 8, 9};

Insa putem modifica orice element din sir: array[2]=8; In general obiectele imutabile sunt cele a caror stare nu poate fi modificata. De exemplu pentru modificarea marimii unui sir, suntem fortati sa cream un alt obiect, adica sir, si sa copiem elementele din vechiul sir. Aceasta proprietate, imutabilitate are multe implicatii si avantaje despre care vom vorbi mai tarziu. Cand efectuam o atribuire a unui sir catre alt sir, adica int[] b = a; cele doua siruri vor pointa catre acelasi obiect, aceeasi referinta. Verificarea egalitatii a doua siruri Sirurile sunt obiecte, deci respecta regulile privind egalitatea. Atunci cand se foloseste operatorul == pentru aceasta verificare se vor compara referintele.
int array1[] = {1, 2, 3, 4, 5}; int array2[] = array1; System.out.println(array1==array2);

In acest caz rezultatul va fi true dar daca am fi avut:


int array1[] = {1, 2, 3, 4, 5}; int array2[] = {1, 2, 3, 4, 5}; System.out.println(array1==array2);

Verificarea corecta a valorilor dintr-un sir se face folosind equals. Aceasta metoda nu este cea suprascrisa din Object ci este din clasa java.util.Array. Astfel comparatia de mai sus devine:
int array1[] = {1, 2, 3, 4, 5}; int array3[] = {1, 2, 3, 4, 5}; System.out.println(java.util.Arrays.equals(array1,array3));

Aceasta clasa mai contine metode de sortare a elementelor unui sir, de umplere a sirurilor si de cautare binara. 11

Sirurile sunt foarte potrivite cand stim apriori cate elemente ne trebuie pentru a rezolva o anumita problema. Dar daca nu stim? Putem crea siruri de lungime zero si sa folosim arraycopy pentru a redimensiona. Totusi aceasta implica crearea altor siruri, ocuparea de memorie in plus. Pentru aceasta Java ofera o alta clasa si anume Vector.

Clasa Vector
Clasa Vector face parte din pachetul java.util si are urmatoarea declaratie:
public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable

Astfel putem concluziona sirul de mosteniri, si legatura dintre Vector si Stack astfel:

Pe langa aceste clase pe care le mosteneste, Vector mai implementeaza si interfetele Cloneable si Serialize. Interfata Serialize permite ca orice obiect care o implementeaza sa poata fi serializat pentru a fi transmis catre diverse destinatii, sau deserializat, pentru a putea fi corect preluat. Metodele de baza

Mai jos avem variabilele clasei Vector si descrierea pentru fiecare dintre ele:

12

Variabila capacityIncrement elementCount elementData modCount

Descriere Marimea pentru incrementarea capacitatii unui vector Numarul de elemente dintr-un vector. sirul de date intern al vectorului. mostenita din AbstractList: folosit de Iterator pentru a verifica modificari concurente.

Capacitatea unui vector reprezinta numarul maxim de elemente admis. Urmeaza o lista cu metodele din clasa Vector: Metoda add() addAll() addElement() capacity() clear() clone() contains() containsAll() copyInto() elementAt() elements() ensureCapacity() equals() firstElement() get() indexOf() insertElementAt() isEmpty() iterator() lastElement() lastIndexOf() listIterator() remove() removeAll() removeAllElements() set() setElementAt() setSize() size() subList() toArray() trimToSize() Descriere Adauga un element intr-un vector. Adauga o colectie de elemente intr-un vector. asemenea metodei add() Returneaza capacitatea adica marimea sirului intern. sterge toate elementele unui vector. Creeaza o clona a vectorului. Verifica daca un vector contine un element. Verifica daca un vector contine o colectie. Copiaza elementele unui vector intr-un array. Returneaza elementul de la pozitia specificata. Returneaza un obiect al vectorului care permite vizitarea tuturor cheilor vectorului. Verifica si se asigura ca marimea buffer-ului intern sa fie de o anumita marime. verifica egalitatea cu un obiect. returneaza primul element. returneaza un element de la o anumita pozitie. cauta prima aparitie a unui element in sir. Insereaza un element in sir. verifica daca un vector este gol. returneaza un iterator, adica un obiect ce permite vizitarea elementelor din sir Returneaza ultimul element din sir Cauta pozitia ultimului element din sir care este egal cu obiectul specificat Returneaza un obiect care permite ca toate elementele sa fie vizitate secvential. Sterge un anumit element din vector. Sterge toate elementele specificate in colectia data ca parametru. Sterge toate elementele din sir si seteaza marimea acestui cu zero. Schimba un element de la o anumita pozitie. Acelasi lucru ca si set. modifica marimea buffer-ului intern returneaza numarul de elemente din sir returneaza o sectiune din sir. returneaza elementele vectorului ca array. taie o portiune din sir, astfel ca el sa ramana de marimea specificata.

13

Crearea Vectorilor Se pot folosi patru constructori pentru a creea un Vector. Folosind unul din primii trei constructori, se poate creea un vector gol, cu o capacitate specificata. Cand spatiul devine insuficient, vectorul isi va dubla marimea.
public Vector() public Vector(int initialCapacity) public Vector(int initialCapacity, int capacityIncrement)

Motivul pentru care exista cei doi constructori cu parametru, este de a permite initializarea cu un numar de elemente a sirului, in cazul in care stim apriori de cate elemente este nevoie. Crearea unui nou sir si copierea elementelor ocupa timp, de aceea este mai eficient sa redimensionam un array. Daca marimea vectorului specificata prin initialCapacity este negativa se arunca o exceptie de tip IllegalArgumentException. Al patrulea constructor se refera la utilizarea unei colectii pentru initalizarea vectorului. Practic, se iau elementele din colectie si se construieste un array cu acestea. Vom aborda subiectul colectii in cele ce urmeaza.
public Vector(Collection c)

Vectorul nou creeat are cu 10% mai mult decat numarul de elemente din colectia c. Cum copiem atunci rapid un sir clasic intr-un Vector? Pentru aceasta, se poate transforma sirul intr-o colectie cu functia Arrays.asList() si apoi folosi constructorul de mai sus:
Vector v = new Vector(Arrays.asList(array));

Manipularea elementelor din vector Pentru a adauga elemente avem mai multe oferte din partea Java, si a clasei Vector. Adaugarea la sfarsit Se apeleaza metoda add sau addElement dupa cum sunt declarate:
public boolean add(Object element) public void addElement(Object element)

Pentru a demonstra adaugarea la sfarsit vom copia dintr-un sir de String-uri intr-un vector, unul cate unul adaugand la sfarsit:
import java.util.Vector; public class InsertVector { public static void main (String args[])

14

Vector v = new Vector(); for (int i=0, n=args.length; i<n; i++) { v.add(args[i]); }

Adaugarea undeva in interior Exista momente, cand dorim sa inseram elementele la anumite pozitii in sir, mutand celelalte elemente mai la dreapta. Exista doua functii care fac acest lucru:
public void add(int index, Object element) public void insertElementAt(Object element, int index)

Motivul pentru care exista doua doua metode care fac acelasi lucru, este ca, clasa Vector implementeaza interfata List, deci este parte din Collection. Aceeasi eroare va fi aruncata daca indexul este negativ, si anume IllegalArgumentException. Trebuie facuta diferenta intre add si set: ultima metoda doar modifica elementul de la pozitia specificata. Pentru a intelege mai bine adaugarea am luat un vector cu patru elemente. Ce se intampla de exemplu cand inseram un element nou in sirul de mai jos: primul al doilea al treilea al patrulea

In urma inserarii unui element prin comanda:


vector.add(2,nou);

se obtine: primul al doilea nou al treilea al patrulea

Adaugarea unei colectii Ultima metoda de a adauga elemente in vector este addAll:
public boolean addAll(Collection c) public boolean addAll(int index, Collection c)

Practic se copiaza elementele dintr-un obiect de tip Collection in vector. Aceste elemente se pot adauga fie la sfarsitul vectorului, folosind prima metoda, fie se pot insera la incepand cu un anumit 15

index, folosind cea de a doua metoda. Este mai eficient sa adaugam toate elementele dintr-un bloc de elemente decat sa incercam sa le adaugam unul cate unul. Daca indexul este invalid atunci se va furniza o exceptie de tip IllegalArgumentException. Deoarece vectorii au fost ganditi sa lucreze cu obiecte, pentru a lucra cu primitive trebuie sa mai depunem un efort. Vectori de tipuri primitive Pentru a lucra cu valori de tip primitv trebuie sa convertim acestea in clase wrapper. Spre exemplu int are clasa wrapper Integer, sau float are clasa wrapper Float. Aceste specificatii sunt in cursul numarul 1. Exemplul de mai jos prezinta modul de lucru cu tipuri primitive:
import java.util.Vector; public class PrimVector { public static void main (String args[]) { Vector v = new Vector(); for (int i=1; i<=10; i++) { v.add(new Integer(i)); } } }

La preluarea valorilor din vector va trebui de asemenea sa transformam din Integer in int, deci din tip referinta in primitiv. Afisarea vectorilor Ca orice Object, si clasa vector implementeaza metoda toString(), ceea ce inseamna ca putem folosi direct obiectul de tip vector pentru afisare:
System.out.println(v);

unde v este vectorul din exemplul de mai sus. Aceasta comanda va afisa urmatorul sir:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Stergerea elementelor Ca si in cazul adaugarii, exista multe feluri in care se pot sterge elemente din vectori, si vom discuta despre acestea in cele ce urmeaza. Cel mai simplu mod de a sterge, este de a elimina toate elementele folosind:

16

public void clear() public void removeAllElements()

Pentru a sterge un element de la o anumita pozitie se vor folosi metodele:


public Object remove(int index) public void removeElementAt(int index)

Atata timp, cat indexul este mai mare ca zero si nu depaseste lungimea sirului, nu vom primi o exceptie de tip ArrayIndexOutOfBoundsException. Atunci cand stergem un element, ramane un loc vid, si intregul subsir ce urmeaza elementului sters va fi deplasat la stanga: primul al doilea de sters al treilea al patrulea

De exemplu daca rulam comanda


vector.remove(2);

rezultatul este urmatorul primul al doilea al treilea al patrulea

Daca nu se cunoaste pozitia, indexul elementul in sir, dar se cunoaste valoarea acestuia, atunci se va folosi una din metodele:
public boolean remove(Object element) public boolean removeElement(Object element)

Aceste metode cauta in sir, primul obiect care este egal cu obiectul dat ca parametru. Pentru comparatia obiectelor (cele din sir si parametru) se foloseste metoda equals. Pe langa aceste metode de stergere, mai exista una care permite eliminarea unei colectii de elemente:
public boolean removeAll(Collection c)

Metoda va prelua ca parametru colectia c si va sterge fiecare element din vector care se gaseste in colectia c. Alta metoda de a sterge, mai precisa, este removeRange si permite stergerea elementelor din subvectorul delimitat de doua margini: fromIndex si toIndex:
protected void removeRange(int fromIndex, int toIndex)

Marimea unui vector Pentru a dimensiona sau a afla marimea unui sir exista metodele: 17

public int size() public void setSize(int newSize)

Prima metoda returneaza marimea sirului, adica numarul de elemente, iar a doua permite setarea marimii vectorului la un numar de elemente specificat ca parametru. Vom vedea ca marimea unui sir poate fi diferita de capacitatea acestuia. Capacitatea vectorului Capacitatea reprezinta numarul de elemente pe care un vector le poate contine. Aceasta capacitate se poate modifica prin ajustarea marimii vectorului, insa este de preferat sa folosim aceasta metoda de cat mai putine ori, pe cat este posibil. Metoda care returneaza capacitatea este:
public int capacity()

Daca marimea vectorului trebuie sa fie mai mare decat capacitatea lui, vectorul va creste dinamic. Atunci cand trebuie sa adaugam elemente in vector, este bine sa verificam daca avem capacitatea necesara. Pentru aceasta exista metoda ensureCapacity care tocmai asta face:
public void ensureCapacity(int minCapacity)

Daca deja capacitatea este suficient de mare, nu se intampla mai nimic. Daca in schimb, capacitatea este mai mica decat numarul de elemente ce se va obtine in urma adaugarii, vectorul isi va dubla marimea. Se poate insa stabili si numarul de elemente care va fi atribuit marimii vectorului in cazul apelarii metodei ensureCapacity. Cautarea in vector Aceasta cautare si redare a unui element se poate face in mai multe feluri. Preluarea dupa index Pentru a prelua pe baza indexului un element dintr-un vector exista doua metode:
public Object get(int index) public Object elementAt(int index)

De exemplu , folosind functia get putem prelua un element de pe orice pozitie:


Vector v = new Vector(); v.add("Hello"); v.add("World"); String s = (String)v.get(1);

18

Preluare dupa pozitie in sir Pentru aceasta exista doua functii pentru returnarea primului si ultimului element din sir:
public Object firstElement() public Object lastElement()

Enumerarea elementelor Pentru ca si cautarea sa aiba loc cat mai eficient, si anume sa parcurgem tot sirul pe indecsi pentru a gasi un element, exista un mod mai flexibil, si anume folosirea unui obiect de tip Enumeration. Obiectele de tip Enumeration au doua functii nextElement() si hasMoreElement() si sunt ideale pentru iterari prin colectii. Pentru a transforma un vector intr-un obiect de tip Enumeration exista metoda:
public Enumeration elements()

Mai jos avem un exemplu de folosire a acestui tip de obiect si anume Enumeration:
Vector v = . . . Enumeration e = v.elements(); while (e.hasMoreElements()) { process(e.nextElement()); }

In acest while se va parcurge atata timp cat functia e.hasMoreElements() va returna true. process este o functie care va lucra cu elementul din sir, si anume fiecare element pana la epuizarea marimii sirului. Pentru a intelege si mai bine lucrul cu Enumeration avem exemplul de mai jos:
import java.util.Enumeration; import java.util.Vector; class enumerare { public static void main(String args[]) { Vector v=new Vector(10,10); for(int i=0;i<20;i++) v.addElement(new Integer(i)); System.out.println("Vector in ordinea originala"); //se initializeaza obiectul e cu v.elements //astfel ca putem parcurge enumeratorul e for(Enumeration e=v.elements();e.hasMoreElements();) { //nu exista conditie de incrementare in for //dar e.nextElmement() se deplaseaza

19

//la urmatorul element System.out.print(e.nextElement()+" "); } System.out.println(); //afisarea vectorului original in ordine inversa System.out.println("\nVector in ordinea inversa"); for(int i=v.size()-1;i>=0;i--) System.out.print(v.elementAt(i)+" "); } }

Gasirea elementelor intr-un sir Mai intai exista functia contains care verifica daca obiectul dat ca parametru exista in sir:
public boolean contains(Object elment)

Exista doua functii care se ocupa cu gasirea unui obiect in sir si anume:
public int indexOf(Object element) public int indexOf(Object element, int index)

Incepand cu primul element din sir (in cazul primei functii), sau cu elementul de la pozitia index (in cazul celei de-a doua), se cauta obiectul element in sir si se returneaza indexul unde a fost gasit prima data. Aproape acelasi lucru este realizat de cele doua functii de mai jos, cu precizarea ca, cautarea incepe de la sfarsit catre primul element din sir.
public int lastIndexOf(Object element) public int lastIndexOf(Object element, int index)

Evident pentru a compara doua elemente si anume parametrul si obiectele din sir se foloseste metoda equals(). Mai jos avem un exemplu pentru cautarea in vectori:
public class finding { static String members[] = {"Andrei", "Ion", "Radu", "Mircea", "David","Radu", "Gheorghe"}; public static void main (String args[]) { Vector v = new Vector(); for (int i=0, n=members.length; i<n; i++) { v.add(members[i]); }

20

System.out.println(v); System.out.println("Contine UnNume?: " + v.contains("UnNume")); System.out.println("Contains Mircea?: " + v.contains("Mircea")); System.out.println("Unde e Radu?: " + v.indexOf("Radu")); System.out.println("Unde e DAvid?: " + v.indexOf("DAvid")); System.out.println("Unde e Radu de la sfarsit?: " + v.lastIndexOf("Radu")); } }

Rezultatul rularii acestui program este:


Contine UnNume?: false Contains Mircea?: true Unde e Radu?: 2 Unde e DAvid?: -1 Unde e Radu de la sfarsit?: 5

Copierea unui vectorilor Exista mai multe feluri de copiere a unui vector. Se poate clona ca orice obiect, sau se poate copia. Mai intai sa vedem cum se cloneaza:
Vector v1 = . . .; Vector v2 = (Vector)v1.clone();

In acest moment avem doi vectori diferiti insa cu aceleasi elmente. Alt mod de a copia este cel folosind metoda toArray si apoi copyInto:
public Object[] toArray() public Object[] toArray(Object[] a) public void copyInto(Object[] anArray)

Adica, vom converti un vector in array si apoi copiere in alt vector prin copyInto. Mai jos avem transformarea unui vector in sir:
Vector v = . . .; String array[] = new String[0]; array = (String[])v.toArray(array);

dupa ce apeleaza metoda toArray(), sirul este redimensionat cu numarul de elemente din v. Apoi se poate transforma din sir in vector astfel:

21

Vector v = . . .; String array[] = new String[v.size()]; array = (String[])v.toArray(array);

Cel mai indicat mod de a copia este folosind metoda copyInto:


Vector v = . . .; String array[] = new String[v.size()]; v.copyInto(array);

Pe langa vectori putem folosi structuri dedicate de date cum ar fi stiva sau coada, pe care le vom analiza in cele ce urmeaza.

Stiva
O stiva este o structura de tip LIFO, ultimul intrat primul iesit. Mai intai sa vedem cum functioneaza o stiva. Exista doua functii de introducere in stiva push si pop de eliminare a unui element din stiva. Introducerea unui element se va face peste ultimul element din stiva iar eliminarea va fi a ultimului element din stiva:

Figura 5.4 Stiva este goala, se introduce un elment folosind functia push.

22

Figura 5.5 Stiva contine un element, se introduce al doilea element folosind functia push. Apoi se introduce al treilea element.

Figura 5.6 Se scoate elementul din varf cu functia pop, stiva va contine doua elemente. Din cele trei figuri se poate intelege functionarea unei stive. Evident se pot scoate si celelalte elemente, adica sa revenim la starea initiala si anume stiva goala. Daca se incearca introducerea unui element atunci cand deja stiva este plina, va apare o eroare care sa semnaleze acest lucru. Evident, incercarea de a scoate dintr-o stiva goala duce la o eroare.

23

Clasa Stack Clasa Stack in Java, este clasa copil a clasei Vector. Totusi exista cateva operatiuni specifice clasei Stack si anume: Adaugarea elementelor Aceasta operatiune se face prin metoda push():
public Object push(Object element)

Aceasta va adauga un element la sfarsitul sirului din Vector. Stergerea unui element Aceasta operatiune se va face prin apelul functiei pop():
public Object pop()

Aceasta va prelua primul element din stiva, il va sterge din stiva, si va returna valoarea lui. Daca stiva este goala vom primi o eroare de tipul EmptyStackException. Pentru a verifica daca o stiva este goala sau nu avem functia:
public boolean empty()

pentru a prelua elementul din varful stivei exista functia peek():


public Object peek()

To search an element in a stack we have the following function:


public int search(Object element)

Spre deosebire de lucrul cu vectori sau siruri, acest mecanism al stivei functioneaza astfel: Elementul din varf este pe pozitia 1, pe pozitia 2 fiind elementul de dedesubt si asa mai departe. Daca nu este gasit obiectul cautat atunci o eroare corespunzatoare este returnata. Un exemplu pentru a ilustra mai bine folosirea stivelor este dat mai jos:
import java.util.Stack; public class Stiva { public static void main (String args[]) { Stack s = new Stack(); s.push("Primul element");

24

s.push("Al doilea element"); s.push("Al treilea element"); System.out.println("Next: " + s.peek()); s.push("Al patrulea element"); System.out.println(s.pop()); s.push("Al cincilea element"); s.push("Al saselea element"); //afisarea stivei System.out.println(s); // Gasim al doilea element int count = s.search("Al doilea element"); // scot din stiva tot pana la el while (count != -1 && count > 1) { s.pop(); count--; } // scot din stiva pe el si il afisez System.out.println(s.pop()); //este stiva goala? System.out.println(s.empty()); System.out.println(s); } }

In acest exemplu se vor introduce sase elemente cu scoaterea celui de al patrulea:


s.push("Al patrulea element"); System.out.println(s.pop());

practic imediat dupa introducere. In continuare se cauta pozitia celui de-al doilea element in stiva, se scoate pe rand de la varf, toate elementele pana la el si apoi se scoate si el:
Next: Al treilea element Al patrulea element [Primul element, Al doilea element, Al treilea element, Al cincilea element, Al saselea element] Al doilea element false [Primul element]

Enumerator
Acest mecanism este unul eficient, si comporta doar doua metode: hasMoreElements() ce verifica daca mai exista elemente in enumeratie si returneaza un boolean. Cealalta metoda nextElement(), returneaza urmatorul element din colectie ca si Object. Daca nu mai sunt elemente si totusi este apelata metoda nextElement(), atunci este aruncata exceptia NoSuchElementException. Pentru a parcurge orice structura de acest tip, pasii sunt: 25

Enumeration enum = . . .; while (enum.hasMoreElements()) { Object o = enum.nextElement(); processObject(o); }

Daca folosim for in loc de while:


for (Enumeration enum = . . .; enum.hasMoreElements(); ) { Object o = enum.nextElement(); processObject(o); }

Intrebarea este, de unde gasim aceste tipuri de enumerare? Un raspuns ar fi din colectii sau din vectori dupa cum am vazut mai sus. Alt raspuns este ca putem creea propriile noastre enumerari. Pentru aceasta clasa care reprezinta o enumerare va trebui sa implementeze o interfata si anume Enumeration. In exemplul de mai jos clasa care realizeaza acest lucru este ArrayEnumeration:
import java.util.*; class ArrayEnumeration implements Enumeration { private final int size; private int cursor; private final Object array; public ArrayEnumeration(Object obj) { Class type = obj.getClass(); if (!type.isArray()) { throw new IllegalArgumentException("Invalid type: " + type); } //o metoda care preia lungimea unui sir //nu intram in detalii deocamdata size = java.lang.reflect.Array.getLength(obj); array = obj; } public boolean hasMoreElements() { return (cursor < size); } //preluam elementul de pe pozitia //data de cursor si marim cursorul public Object nextElement()

26

{ return java.lang.reflect.Array.get(array, cursor++); } } class Enumerare { public static void main(String args[]) { Enumeration enumerare = new ArrayEnumeration(args); //parcurg sirul de argumente while (enumerare.hasMoreElements()) { System.out.println(enumerare.nextElement()); } Object obj = new int[] {2, 3, 5, 8, 13, 21}; enumerare = new ArrayEnumeration(obj); //parcurg un sir de obiecte while (enumerare.hasMoreElements()) { System.out.println(enumerare.nextElement()); } try { //incerc sa instantiez o enumerare //ce are la baza un obiect ce nu e de tip Array enumerare = new ArrayEnumeration(ArrayEnumeration.class); } catch (IllegalArgumentException e) { System.out.println("Oops: " + e.getMessage()); } } }

O clasa utila care implementeaza aceasta interfata si anume Enumeration este StringTokenizer. Scopul aceste clase este de a imparti un String in diferite elemente, iar delimitarea se face pe baza unui caracter anume (de obicei spatiu sau virgula). Astfel poate fi parcurs acest sir de siruri obtinut:
StringTokenizer tokenizer = new StringTokenizer("This is a test"); while (tokenizer.hasMoreElements()) { Object o = tokenizer.nextElement(); System.out.println(o); }

27

Pe langa metodele hasMoreElements si nextElement pe care le mosteneste din Enumeration, clasa StringTokenizer mai are si metodele hasMoreTokens si nextToken care fac cam acelasi lucru, in plus expun in loc de Object, direct String-uri.
StringTokenizer tokenizer = new StringTokenizer("This is a test"); while (tokenizer.hasMoreTokens()) { String s = tokenizer.nextToken(); System.out.println(s); }

IO: scanare si formatare


Scanner Scanarea, sau preluarea de informatii dintr-o sursa poate fi facuta mai elegant folosind clasa Scanner. Un obiect Scanner fragmenteaza intrarea in subelemente numite token, pe baza unor delimitator. Un exemplu de a citi numere din System.in:
Scanner sc = new Scanner(System.in); int i = sc.nextInt();

Acest obiect poate folosi si alti delimitatori diferiti de spatiu. Acest exemplu citeste elementele dintr-un sir separate prin linia:
import java.io.*; import java.util.Scanner; public class ScanTest { public static void main(String[] args) throws IOException { Scanner s = null; try { InputStream input = new FileInputStream("test.txt"); s = new Scanner(input); s.useDelimiter("linia"); while (s.hasNext()) { System.out.println(s.next()); } } finally { if (s != null) { s.close(); } } } }

28

Mai mult se pot folosi expresii regulate pentru a marca delimitatorii:


public class ScanTest { public static void main(String[] args) throws IOException { String input = "1 3,4 2,4"; Scanner s = new Scanner(input).useDelimiter("[,\\s]"); System.out.println(s.nextInt()); System.out.println(s.nextInt()); System.out.println(s.next()); System.out.println(s.next()); s.close(); } }

Formatarea Obiectele stream care implementeaza formatarea sunt fie PrintWriter fie PrintStream. Pentru afisarea datelor exista doua niveluri de formatare: print si println modul standard format formateaza orice numar pe baza unui string Prima metoda este cunoscuta iar valorile afisate nu sunt formatate:
public class FormatSample { public static void main(String[] args) { int i = 2; double r = Math.sqrt(i); System.out.print("The square root of "); System.out.print(i); System.out.print(" is "); System.out.print(r); System.out.println("."); i = 5; r = Math.sqrt(i); System.out.println("The square root of " + i + " is " + r + "."); } }

Aceasta produce o afisare bruta a datelor:


The square root of 2 is 1.4142135623730951. The square root of 5 is 2.23606797749979.

Metoda format permite formatarea argumentelor pe baza unui pattern. Acest pattern este un string de formatare alcatuit din specificatori de formatare. Mai jos avem un prim exemplu de astfel de formatare: 29

public class Root { public static void main(String[] args) { int i = 2; double r = Math.sqrt(i); System.out.format("The square root of %d is %f.%n", i, r); } }

Rezultatul este unul mult mai simplu si la fel de corect:


The square root of 2 is 1.414214.

Toti specificatorii de format incep cu % si se termina cu unul sau doua caractere de conversie ce specifica tipul de formatare. Iata cateva conversii sunt: o d formateaza o valoare intreaga ca decimal o f formateaza o valoare floating point ca decimal o n afiseaza terminare linie o x formateaza intreg ca hexazecimal o s formateaza orice valoare ca string o tB formateaza un intreg ca nume de luna Iata un exemplu mai complex despre formatarea unui numar real ca float:
public class Format { public static void main(String[] args) { System.out.format("%f, %1$+020.10f %n", Math.PI); } }

Mai jos este rezultatul:


3.141593, +00000003.1415926536

In imaginea de mai jos sunt explicate aceste caractere de conversie

Figura 5.7 Specificatori de format 30

Serializarea obiectelor Cheia pentru a scrie intr-un obiect este de a reprezenta starea lui intr-o forma serializabila, cu informatii suficiente pentru a reconstrui acel obiect la citire. De aceea citirea obiectelor si scrierea lor se numeste serializare. Serializarea se poate folosi astfel: 1. Remote method invocation (RMI) - comunicatii intre obiecte via soketi. 2. Persitenta usoara arhivarea unui obiect pentru invocarea ulterioare in program. Cum scriem obiecte? De exemplu, se preia timpul in milisecunde, se construieste un obiect de tip Date si se serializeaza obiectul:
FileOutputStream out = new FileOutputStream("theTime"); ObjectOutputStream s = new ObjectOutputStream(out); s.writeObject("Today"); s.writeObject(new Date()); s.flush(); ObjectOutputStream trebuie sa fie construit pe baza altui stream. In acest caz se construieste pe baza unui FileOutputStream care directioneaza catre theTime

Metoda writeObject serializeaza obiectul, si scrie acest stream obtinut catre destinatie, mentinand relatiile dintre obiecte. Cum citim obiecte ? Odata ce obiectele au fost scrise intr-un stream, acestea se pot citi pentru a reconstrui obiectele. Mai jos sunt citite obiectele care au fost scrise prin codul de mai sus:
FileInputStream in = new FileInputStream("theTime"); ObjectInputStream s = new ObjectInputStream(in); String today = (String)s.readObject(); Date date = (Date)s.readObject();

Metoda readObject deserializeaza urmatorul obiect din stream si recursiv toate referintele lui pentru ca starea obiectului sa fie aceeasi ca la momentul serializarii. Un obiect este serializabil doare daca clasa din care face parte, implementeaza interfata Serializable. Serializarea se poate personaliza, prin implementarea a doua metode din interfata Serializable: readObject si writeObject. Pentru un control cat mai bun al relatiilor dintre clasa si superclasa, trebuie implementata si interfata Externalizable. Mai jos avem un exemplu pentru a demonstra serializarea obiectelor

31

import java.io.*; public class Serializare { public static void main(String args[]) { // Object serialization try { MyClassToSerialize objectOut = new MyClassToSerialize("Hello", 48, 34.658); System.out.println("object out: " + objectOut); FileOutputStream fos = new FileOutputStream("serial"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(objectOut); oos.flush(); oos.close(); } catch(Exception e) { System.out.println("Exception during serialization: " + e); System.exit(0); } // Object deserialization try { MyClassToSerialize objectIn; FileInputStream fis = new FileInputStream("serial"); ObjectInputStream ois = new ObjectInputStream(fis); objectIn = (MyClassToSerialize)ois.readObject(); ois.close(); System.out.println("object2: " + objectIn); } catch(Exception e) { System.out.println("Exception during deserialization: " + e); System.exit(0); } } } class MyClassToSerialize implements Serializable { String s; int i; double d; public MyClassToSerialize(String s, int i, double d) { this.s = s; this.i = i;

32

this.d = d; } public String toString() { return "s=" + s + "; i=" + i + "; d=" + d; } }

Clasa care implementeaza interfata Serializable este MyClassToSerialize. Obiectele de acest tip de data pot fi serializate. In programul de mai sus serializarea se realizeaza atunci cand apelam metoda oos.writeObject(objectOut); , moment in care se salveaza in fisierul serial starea obiectului objectOut. Daca deschidem acest fisier, cu un text editor vom vedea ca nu poate fi inteles, ceea ce inseamna un plus in securitate. Seria de bytes poate fi folosita pentru deserializarea obiectului folosind metoda
objectIn = (MyClassToSerialize)ois.readObject();

Aici se citeste din fisier stream-ul si se construieste un obiect care este transformat in MyClassToSerialize, pentru a fi folosit ulterior. Clasa StringBuffer Am vazut mai sus ca sirurile in general sunt imutabile. Ceea ce inseamna ca odata declarate cu un numar de caractere, ele raman fix cu acel numar de caractere. Clasa String este implementata tot ca un sir de caractere, deci este imutabila. Un StringBuffer este asemenea unui String, dar poate fi modificat, si anume lungimea acestui string poate fi modificata prin cateva functii pe care le vom studia. StringBuffer-ele sunt sigure in multithreading, adica pot fi folosite de mai multe fire de executie, fara a genera un comportament aleator. String-urile pot fi concatenate si aceasta operatie reda la final un singur string format din string-urile alipite:
x = "a" + 4 + "c"

Problema aici este ca pentru a realiza aceasta operatie este nevoie de patru obiecte si anume sirurile a, 4,c si x. Aceasta creare de obiecte, a caror referinta se pierde, duce la aparitia unui overhead (instructiuni, operatiuni auxiliare pentru indeplinirea unei operatiuni principale), ceea ce inseamna lipsa de eficienta ca timp si ca memorie. Modul in care se lucreaza elegant cu String-uri este urmatorul:
x = new StringBuffer().append("a").append(4).append("c") .toString()

Prin aceasta operatiune se creeaza doar un obiect si anume un StringBuffer caruia i se alipeste la sfarsit, pe rand, string-ul a apoi 4 apoi c. Pe urma acest StringBuffer este convertit la tipul String. 33

Iata un alt exemplu pentru functia append care suporta diverse supraincarcari pentru mai multe tipuri de data:
char[] c1 = new char[] {'S','i','r','d','e','c','h','a','r'}; StringBuffer sbc = new StringBuffer("Sirul nou format : "); sbc.append(c1); System.out.println(sbc);

Alta functie este cea de stergere si anume delete:


StringBuffer sb = new StringBuffer("Hello World!"); sb.delete(0,6); sb.delete(5,6); System.out.println(sb);

Functia pentru inlocuire de string-uri este replace:


StringBuffer sb = new StringBuffer("Hello World!"); System.out.println("Original Text : " + sb); sb.replace(6,11,"Lume"); sb.replace(0,5,"Buna "); System.out.println("Replaced Text : " + sb);

Pe langa aceste functii, mai sunt o serie de metode pentru inserare, de cautare, redimensionare etc.

34

Curs 6
Liste inlantuite ..................................................................................................................................... 3 Reprezentarea listelor...................................................................................................................... 4 Operatiuni cu liste simplu nlntuite ................................................................................................ 8 Liste dublu inlantuite ..................................................................................................................... 14 FrameWork-ul Java pentru Colectii .................................................................................................... 14 Bazele Framework-ului .................................................................................................................. 15 Interfetele.................................................................................................................................. 15 Clasele Framework-ului.............................................................................................................. 16 Algoritmii Framework-ului ......................................................................................................... 16 Interfata Collection ........................................................................................................................ 16 Adaugarea elmentelor ............................................................................................................... 17 Stergerea elementelor ............................................................................................................... 17 Retinerea colectiei ..................................................................................................................... 18 Alte Operatiuni .............................................................................................................................. 18 Returnarea elementelor............................................................................................................. 18 Cautarea elementelor ................................................................................................................ 19 Clonarea colectiilor ................................................................................................................... 19 Interfata Iterator............................................................................................................................ 20 Iterator pentru filtrare ................................................................................................................... 21 Exceptiile ce pot apare in colectii ................................................................................................... 23 Exceptii de modificare concurentiala.......................................................................................... 23 Exceptii pentru operatii neimplementate ................................................................................... 24 Liste: List ........................................................................................................................................... 24 Metodele List................................................................................................................................. 25 ArrayList ........................................................................................................................................ 25 Crearea unui ArrayList................................................................................................................ 25 Adaugarea unor elemente ......................................................................................................... 26 Returnarea unui element ........................................................................................................... 26 Stergerea unui element ............................................................................................................. 26 Retinerea unei colectii ............................................................................................................... 27 Stergerea unui interval............................................................................................................... 27 Operatii cu Liste ......................................................................................................................... 28 LinkedList ...................................................................................................................................... 31 Crearea unei LinkedList .............................................................................................................. 31 1

Adaugarea in LinkedList ............................................................................................................. 31 Stergerea elementelor ............................................................................................................... 32 Iteratorul LinkedList ................................................................................................................... 32 Multimi: Set....................................................................................................................................... 34 HashSet ......................................................................................................................................... 34 Crearea unui HashSet................................................................................................................. 34 Adaugarea elementelor ............................................................................................................. 34 Stergerea elementelor ............................................................................................................... 35 Liste Generice ................................................................................................................................ 37

Liste inlantuite

Lista simplu nlnuita este cea mai simpla structura de data nl nuita. O astfel de lista este o secvena de obiecte alocate dinamic, fiecare obiect avnd referina ctre succesorul sau in lista. Exista o multitudine de moduri de a implementa aceasta structura de date. In figura 6.1 avem cteva reprezentri mai cunoscute a listelor simplu nlnuie.

Figura 6.1 Tipuri de liste simplu inlantuite

Cea mai simpla lista dublu nlnuia este in figura 6.1 1) in care fiecare element din lista indica spre succesorul lui, iar ultimul element ctre null. Variabila cap are rolul de a pstra primul element din lista. Acest tip de lista este ineficienta atunci cnd se dore te adugarea elementelor la ambele capete ale listei. Pentru a aduga un element la captul listei, este necesar sa localizam ultimul element. Aceasta presupune parcurgerea ntregii liste pentru o simpla operaie de adugare. In figura 61. 2) avem reprezentarea unei liste avnd cap si coada, tocmai pentru a eficientiza adugarea elementelor in lista. Singura problema ar fi de spaiu pentru variabila coada. Lista simplu nlnuit din figura 6.1 3) prezint doua ajustri. Exista aici un element nou, santinela care nu este folosit pentru a conine date, dar este ntotdeauna prezent. Avantajul principal in folosirea santinelei este ca simplifica anumite operaii. De exemplu, din cauza acestui element, nu va trebui niciodat sa modificam variabila cap. De asemenea ultimul element va indica spre aceasta santinela, in loc sa indice ctre null; vorbim astfel despre o lista circulara. Avantajul este ca, inserarea la capul listei, inserarea la coada listei sunt operaii identice. Se poate realiza o lista simplu nlnuit care nu are nevoie de o santinela. Figura 6.1 4) prezint o variaie in care, doar o variabila este folosita pentru a tine lista, de aceasta data coada, si anume ultimul element din lista. 3

Mai jos avem o reprezentarea acestor liste atunci cnd nu con in nici un element.

Figura 6.2 Listele goale Reprezentarea listelor Cum este reprezentat un element dintr-o lista simplu nlnuita? In cel mai simplu caz el consta intr-o clasa cu doi membrii: data si urm torul element din lista. Mai jos avem exemplu de o astfel de lista:
class LinkedList { int data; LinkedList next; public LinkedList() { next = null; data =0; } }

Se poate observa ca aceasta clasa conine un membru next tot de acelai tip ca si clasa. Aceasta pentru ca, elementul din lista va indica spre urmtorul element din lista care este tot de acelai tip de data. Pe lng acest membru, clasa mai conine un element de tip int data, care retine informaii despre nodul curent din lista. Bineneles ca pe lng acest membru, se pot declara o serie de ali membrii care pot retine diverse informaie, in funcie de necesitai.

class LinkedListDemo { public static void main(String args[]) { LinkedList list = new LinkedList(); list.data =23; list.next = new LinkedList(); list.next.data = 10; list.next.next = new LinkedList(); list.next.next.data = 49; //acum parcurgem lista de la capat //si anume din list LinkedList iterator = list; do { System.out.println(iterator.data); } while((iterator = iterator.next)!=null); iterator = list; do { System.out.println(iterator.data); } while((iterator = iterator.next)!=null); } }

In clasa LinkedDemo avem un exemplu de folosire a listelor simplu nl nuite. Se declara prima data un obiect de tip LinkedList cu un constructor fr parametrii. Apoi se instaniaz membrul next. In acest moment se numete ca am alipit primului element din lista un nou element si anume: list.next. Acest nou element este instaniat la rndul lui tot cu un obiect de tip LinkedList: list.next = new LinkedList();. Din acest moment avem posibilitatea de a modifica datele acestui nou obiect cat si de a alipi un nou element acestuia, s.a.m.d. Vom vedea in continuare si cum se face adugarea intr-un mod elegant. Parcurgerea este un proces interesant. Desigur ca putem accesa elementele urm toare prin sintaxe de genul list.next.next.data. nsa practica, nu ne permite acest mod de acces: daca avem o lista cu sute sau mii de elemente? De aceea o parcurgere presupune existenta unui iterator, sau element care sa se transforme in succesorul sau. Practic se trece de la element la element prin

instruciunea iterator = iterator.next. Aceasta se poate face intr-un while, pana cnd urmtorul element devine null, adic am ajuns la captul listei. In exemplul de mai sus, pentru a parcurge lista de doua ori, a trebuit sa m folosesc de un element auxiliar. Daca as fi parcurs lista direct, folosind obiectul list, a doua oara nu as mai putea reveni de la nceputul listei. De aceea este indicat sa inem un element pe post de capul listei. Mai jos avem un exemplu de lista simplu nlnuit in care fiecare element tine o referin ctre capul listei. Acest fapt este avantajos mai ale in cazul in care tergem un element, sau accesam un element in interiorul listei.
class LinkedList { int data; LinkedList next; LinkedList head; public LinkedList() { head = this; next = null; data =0; } public LinkedList(LinkedList head) { this.head = head; next = null; data =0; } }

De aceasta data avem doi constructori dintre care unul ce are ca parametru primul element din lista.Mai jos se poate vedea cum are loc aceasta instan iere : list.next.next = new LinkedList(list);. Primul element din lista, adic head, este dat ca parametru al constructorului si anume list. In cazul in care folosim constructorul fr parametru, nseamn ca instaniem tocmai primul element din lista, de aceea avem instruciunea head = this; in acest constructor.
class LinkedListDemo { public static void main(String args[]) { LinkedList list = new LinkedList(); list.head = list; list.data =23;

list.next = new LinkedList(list); list.next.data = 10; list.next.next = new LinkedList(list); list.next.next.data = 49; //acum parcurgem lista de la capat //si anume din list while (list.next !=null) { System.out.println(list.data); list = list.next; } System.out.println(list.data); list = list.head; while (list.next !=null) { System.out.println(list.data); list = list.next; } System.out.println(list.data); } }

In legtura cu utilizarea listelor de acest fel, putem remarca faptul ca lista se poate folosi si dup o parcurgere list = list.head;, nsa ce e mai interesant este ca se poate relua aceasta parcurgere din orice element al listei, din moment ce fiecare tine o referin ctre capul listei. Sunt multe erori care pot apare, nc de la nceputul definiiei unei liste, astfel ca e bine sa ncercam sa prevedem situaiile nefericite ce pot sa apar. De exemplu daca se ncearc accesarea unui element null, ca de exemplu primul element al listei, sa aruncam o excepie. Pentru aceasta membrii listei trebuie sa fie private sau protected (daca lista va fi mo tenita), iar excepiile vor apare in get-eri si set-eri. Iat o parte a clasei LinkedList cu modificrile de mai sus:
class LinkedList { int data; protected LinkedList next; protected LinkedList head; public LinkedList GetNextElement() {

//Aici nu se impune sa aruncam o exceptie //metoda apelanta va decide in functie de //faptul ca next este null sau nu return next; } public int GetNextElementData() { if (next==null) null!"); } throw new NullPointerException("Elementul urmator este return next.data;

Iar utilizarea acestor metode:


LinkedList list = new LinkedList(); int i = list.GetNextElementData();

Evident si apelul metodelor trebuie sa se afle intr-un bloc de try catch pentru a prinde excep iile aruncate din aceste metode. In exemplele urmtoare nu sunt implementate aceste prevederi legate de excepii, ns un bun exerciiu este sa modificai exemplele adugnd si tratrile posibilelor situaii ce pot sa apar. Excepiile care pot sa apar nu sunt tratate, tocmai pentru a simplifica exemplele. Operatiuni cu liste simplu nlntuite Pentru a nelege mai bine toate operaiunile descrise in continuare vom reface structura elementelor de compun o lista si anume cele de clasa Nod:
public class Nod { private Nod next; private int data; public Nod(int data, Nod next) { this.data = data; this.next = next; } public int getData () { return data; } public Nod getNext () { return next; }

public void setData (int newData) { data = newData; } public void setLink (Nod newNext) { next = newNext; } public void AddNodeAfter(int element) { next = new Nod(element,next); } public void RemoveNodeAfter() { next = next.next; } //iterare prin lista de la prima pozitie pana la //pozitia idicata public static Nod PositionInList (Nod start, int index) throws Exception { int i; Nod iterator; if (!(index>0)) throw new Exception("Index incorect!"); iterator = start; for(i=1;(i<index && iterator!=null);i++) { iterator = iterator.next; } return iterator; } public static Nod SearchInList(Nod start, int target) { Nod iterator = start; for(;iterator!=null;iterator=iterator.next) { if (iterator.data == target) return iterator; } return null; } public String ListToString() { Nod iterator = this; StringBuffer str=new StringBuffer(); for(;iterator!=null;iterator=iterator.next) { str.append(iterator.data); str.append(" ");

} return str.toString(); } public String toString() { return data +""; } }

Pe lng cei doi membrii si anume next si data, se pot observa o serie de funcii pentru a nlesni adugarea, cutarea, tergerea elementelor dintr-o lista. Clasa Nod reprezint un element din lista, ns prin intermediul acelui nod din lista putem de exemplu aduga element imediat dupa capul listei:
public void AddNodeAfter(int element) { next = new Nod(element,next); }

Figura de mai jos explica ce se ntmpla cnd adugam un nou element il lista:

Figura 6.3 Adugarea unui element in lista Se poate remarca faptul ca noul element este introdus intre cap si vechea santinela, urmnd ca noul element sa fie succesorul capului, ceea ce l face pe acesta noua santinela. tergerea va fi tot a acestei santinele, care nseamn primul element dupa capul listei. Cand tergem santinela, de fapt, srim peste aceasta in felul urm tor: next = next.next; ceea ce nseamn ca de acum capul listei va indica spre elementul ce urma santinelei. Acest lucru este reprezentat in figura 6.4.

10

Figura 6.4 tergerea unui element din lista Pe lng metodele de adugare, tergere exista si metode de parcurgere in lista , ListToString, de returnarea a unui element de la o anumita pozi ie PositionInList, de cutare a unui element dup valoare in lista: SearchInList. Toate aceste metode presupun existenta unui obiect numit iterator care primete de obicei primul element din lista Nod iterator = start;. Totui lsam posibilitatea de a specifica orice element din lista pentru ca afiarea, sau parcurgerea sa aib loc de oriunde din cadrul listei, si nu neaprat de la primul element. Parcurgerea presupune iterarea prin elementele listei adic trecerea de la un element la succesorul sau, iar aceasta se realizeaz cu iterator=iterator.next, instruciune pe care o gsim in bucla
iterator = iterator.next;

O serie de alte funcii sau de adugri sau de tergeri pot fi implementate, in aceasta clasa pentru ca operaiile sa fie cat mai uor realizate. Iat un exemplu de inserare dup un element din lista.
public void { Nod Nod Nod do { InsertAfterAnElement(int dataVal, Nod start, int newVal) tmp = new Nod(newVal,null); prev = start; iterator = start;

if (iterator.data == dataVal) { prev = iterator.next; break; } iterator=iterator.next; } while(iterator!=null); tmp.next = prev; iterator.next = tmp; }

11

Aceasta metoda realizeaz inserarea dup un element specificat ca prim parametru. Valoarea noului parametru este al treilea parametru. tmp reprezint elementul nou, de aceea la creare nu specificam next-ul lui. Apoi parcurgem lista pana la elementul dup care inseram, reinem urmtorul acelui element in prev si la sfrit tmp va indica spre prev iar tmp devine succesorul elementului de dinaintea lui prev. Pentru o mai buna nelegere a situaiei avem imaginea de mai jos figura 6.5.

Figura 6.5 Inserarea dupa un element Mai jos avem funcia de tergere dupa un element:
public void DeleteAfterAnElement(int dataVal, Nod start) { Nod prev = start; Nod iterator = start; do { if (iterator.data == dataVal) { prev = iterator.next; break; } iterator=iterator.next; } while(iterator!=null); iterator.next = prev.next; }

12

Ca si in cazul inserrii, se parcurge lista de la primul element pana la cel cutat, si se sare peste succesorul acestuia, marcnd astfel tergerea din lista: iterator.next = prev.next; Pentru o mai buna nelegere a acestei operaii avem figura 6.6 mai jos:

Figura 6.6 tergerea dup un element din lista In continuare vom vedea modul in care aceste structuri de date sunt implementate in Java, si mai ales Framework-ul pe care acestea sunt construite. Mai jos avem un exemplu de folosire a clasei de mai sus, cu toate metodele discutate:
public static void main(String[] args) throws Exception { Nod head = new Nod(3,null); head.AddNodeAfter(15); head.AddNodeAfter(12); head.AddNodeAfter(20); System.out.println(head.ListToString()); head.InsertAfterAnElement(3,head,36); System.out.println(head.ListToString()); head.DeleteAfterAnElement(36,head); System.out.println(head.ListToString()); head.AddNodeAfter(40); head.AddNodeAfter(60); System.out.println(head.ListToString()); System.out.println(Nod.PositionInList(head, 4));

13

System.out.println(Nod.SearchInList(head, 40)); head.RemoveNodeAfter(); System.out.println(head.ListToString()); }

Liste dublu inlantuite In cazul in care dorim sa parcurgem uor lista si in celalalt sens (si nu doar de la predecesor la succesor), avem lista dublu nlnuit. Aceasta pstreaz conceptele listei simplu nlnuite, cu specificarea ca fiecare element al listei mai conine o referin ctre predecesorul, sau anteriorul sau, aa cum apare in figura 6.7

Figura 6.7 Reprezentarea unei liste dublu nlnuite Mai jos este o clasa ce reprezint un element al acestei liste:
public class Nod { private Nod next; private Nod prev; private int data; public Nod(int data, Nod next,Nod prev) { this.data = data; this.next = next; this.prev = prev; } }

In continuare se pstreaz aceleai metode, concepte ca in cazul listei simplu nlnuite, dar innd cont si de a doua legtura. Astfel in cazul adugrii, tergerii, parcurgerii trebuie sa inem cont de legtura prev.

FrameWork-ul Java pentru Colectii


Framework, in general nseamn o serie de clase, librarii care joaca rol de schelet intr-o aplicaie, permind extinderea i dezvoltarea ei pe baza acestor elemente. In Java , Framework-ul este asemntor cu Standard Template Library (STL) din C++. Exista aproximativ douazecisicinci de clase, interfee, care constituie acest nucleu.

14

Bazele Framework-ului Acest Framework de colecii costa din trei pri: interfee, implementri si algoritmi. Implementrile sunt acele clase concrete pe care Framework-ul le are, iar algoritmii sunt aciuni, metode predefinite care pot exista in clase. Interfetele Exista mai multe interfee in acest Framework si anume: Collection, List, Set, Map, SortedSet, si SortedMap. Ierarhia acestora este prezentata in figura de mai jos:

Figure 6.8 Interfetele din Collection

Se poate observa care este ierarhia de mosteniri in aceasta figur. Collection este un grup de date generic, in care fiecare membru este un element. Un tip Collection poate contine duplicate si poate sa nu aiba elementele sortate. Interfata List este o colectie specializata, in sensul ca, defineste o anumita ordinde pentru elementele sale. Pot exista duplicate, dar important este ca exista o ordine. Set-ul este acea colectie ce simuleaza o multime matematica. Daca avem nevoie de o multime sortata, avem la dispozitie SortedSet. Cealalta ierarhie de clase este Maps si anume multimi de perechi cheie-valoare. Deoarece map-urile au un element compus din doua parti, au o alta implementare decat Collection. In caz ca lucram cu o colectie de chei sortate putem folosi SortedMap.

15

Clasele Framework-ului Iata cum se poate folosi un obiect din acest framework:
List list = new ();

Mai jos este un tabel cu aceste clase concrete care mo tenesc interfeele prezentate mai sus:

Interfee Set SortedSet List Map SortedMap

HashTable HashSet

Array mutabil

Clase concrete Arbore balansat TreeSet TreeSet TreeMap TreeMap

Lista nlnuita

Altele

ArrayList HashMap

LinkedList

Vector, Stack Hashtable, Properties

Toate clasele implementeaz ori interfaa Collection, ori interfaa Map. Pentru a nelege acest tabel el va trebui citit de la stnga la dreapta si anume: pe al doilea rnd: care sunt clasele ce implementeaz interfaa Set? Pe fiecare coloana vom gsi acea clasa ce implementeaz interfaa, daca este cazul. Vom analiza pe rnd, toate aceste clase si interfee. Daca dorim sa crem clase ce implementeaz aceste interfee este bine sa inem cont de cteva aspecte: - Toate implementrile sunt desincronizate. Se poate aduga acces sincron, dar nu este necesar. - Toate clasele ofer iteratori fail-fast. Daca o colecie este modificata in timp ce iteram prin elementele acesteia, va fi aruncata o excep ie de tip ConcurrentModificationException. - Toate implementrile lucreaz cu elemente null. - Clasele se bazeaz pe un concept de metode opionale in interfete. Daca o clasa nu suporta o anumita operaiune, va arunca o excepie numita UnsupportedOperation Exception. Algoritmii Framework-ului Exista o serie de algoritmi predefinit care se gsesc in Collections si in Arrays, ce ajuta in lucrul cu aceste colecii si pe care ii vom analiza in curnd.

Interfata Collection Aceasta interfaa consta din o serie de metode necesare in lucru cu o colecie si anume:

16

Metoda add() addAll() clear() contains() containsAll() equals() hashCode() isEmpty() iterator() remove() removeAll() retainAll() size() toArray()

Descrierea Adaug un element intr-o colecie Adaug o colecie in alta colecie terge toate elementele dintr-o colecie Verifica daca un element se afla intr-o colecie Verifica daca o colecie se afla intr-o alta colecie Verifica egalitatea a doua colecii Returneaz un id specific unei colecii Verifica daca o colecie este goala Returneaz un obiect dintr-o colecie ce permite vizitarea elementelor acesteia terge un element dintr-o colecie terge elementele unei colecii din colecia curenta terge elementele dintr-o colecie care nu sunt in alta colecie Returneaz numrul de elemente dintr-o colecie Returneaz elementele dintr-o colecie ca sir.

Adaugarea elmentelor Putem aduga doar un element folosind metoda add. In mod normal, daca nu se arunca nici o excepie, acest element va fi in colecie dup return-ul din funcie. In acest caz valoare returnata este true.
public boolean add(Object element)

Totui daca ceva neprevzut se ntmpl si elementul nu poate fi adugat in colecie se va returna valoarea false. Putem de asemenea aduga o colecie prin metoda addAll.
public boolean addAll(Collection c)

Fiecare element din colecia c va fi adugat in colecia ce apeleaz metoda. Daca in urma apelului, colecia se modifica, metoda returneaz true, altfel este returnat false. Daca apar duplicate, iar acest lucru nu este suportat de acel tip de colec ie, va fi aruncata excepia de tip UnsuporedOperationException. Stergerea elementelor Putem terge toate elementele dintr-o colecie folosind metoda clear:
public void clear()

In cazul in care colecia este read-only vom primi o excepie UnsuporedOperationException. Se pot terge anumite elemente dintr-o colecie:
public boolean remove(Object element)

17

Pentru a determina daca un element este in colecie sau nu, trebuie sa ne bazam pe metoda equals. Atunci cand se determina care este elementul din colecie ce va fi ters, metoda equals va fi invocata. Daca tergerea nu este permisa, vom primi excepia de mai sus. Se pot terge si elementele unei colecii folosind funcia:
public boolean removeAll(Collection c)

Metoda terge toate instanele din colecia sursa, care se afla si in colecia c. Daca avem elementele: [1,5,8,1,2,4,2,5,7,6] si colecia c este: [1,5] colecia rmas n urma tergerii este: [8,2,4,2,7,6] Retinerea colectiei Aceasta operaiune este inversa lui removeAll si anume presupune ca doar elementele din colecia c sunt pstrate in colecia original:
public boolean retainAll(Collection c)

Practic metoda funcioneaz ca o intersecie de mulimi. Daca aplicam aceasta operaie pentru colecia: [1,5,8,1,2,4,2,5,7,6] si c este: [1,5,23,29] colecia ce rezulta este: [1,5] Alte Operatiuni Returnarea elementelor Exista o singura metoda pentru a returna elemente din colecie. Prin folosirea unui iterator, si anume prin apelul metodei iterator() putem vizita toate elementele unei colecii:
public Iterator iterator()

Vom reveni mai trziu asupra acestui subiect.

18

Cautarea elementelor nainte de a cuta un element este indicat sa verificam existenta lui in colecie:
public boolean contains (Object element)

Daca obiectul element este gsit, atunci funcia va returna true, altfel va returna false. Ca si in cazul remove(), se folosete metoda equals pentru comparare. Se poate de asemenea verifica daca o colecie conine alta colecie prin funcia:
public boolean containsAll(Collection c)

Aceasta metoda va cuta subsetul c in colecia curenta. Daca doar un element din c nu este gsit in colecia curenta se returneaz false. Funcia pentru aflarea mrimii unei colecii este:
public int size()

Aceasta funcie returneaz numrul de elemente din colecie. Pentru a verifica daca o colecie este goala sau nu avem funcia:
public boolean isEmpty()

Metoda returneaz true daca nu avem nici un element in colecie. Clonarea colectiilor Interfaa Collection nu implementeaz nici Cloneable si nici Serializable. Pentru a copia o colecie, este indicat sa o transmitem la instanierea obiectului ca parametru al constructorului. Un alt mod de a copia o colecie, se realizeaz prin intermediul metodei toArray()
public Object[] toArray() public Object[] toArray(Object[] a)

Prima metoda va returna un sir de obiecte si anume elementele din colecie. Deoarece metoda returneaz un Object[], de fiecare data cnd avem nevoie sa luam un element din sir va trebui sa il transformam ctre tipul de baza folosind operatorul de cast. A doua versiune a metodei este folositoare cnd vrem sa determinam mrimea irului de returnat pe baza irului pasat ca parametru. Daca mrimea coleciei este mai mica dect a.length atunci elementele sunt puse in sir si returnate. Daca irul e prea mic, se creeaz un nou sir cu mrimea egala cu cea a coleciei, si acel sir este returnat. Daca irul dat ca parametru este prea mare atunci metoda nlocuiete cu null, elementele de dup ultimul element copiat: a[collection.size()]=null. 19

Iat un mod de folosire a acestei funcii:


// creem un sir cu elemente din colectie: Collection c = ...; String array[] = new String[0]; array = (String[])c.toArray(array); //Marimea sirului o vom stabili noi Collection c = ...; String array[] = new String[c.size()]; array = (String[])c.toArray(array);

Egalitatea este verificata prin funcia equals:


public boolean equals(Object o)

Aceasta metoda se poate suprascrie si vom vedea in curnd si cum. Interfata Iterator Iteratorul este acel obiect ce permite parcurgerea coleciilor. Pentru aceasta el trebuie sa implementeze interfaa Iterator. Interfaa Iterator conine trei metode: hasNext() ce verifica daca mai sunt elemente de iterat next() returneaz urmtorul element din lista remove() terge un element din iterator Cum se folosete un iterator? Asemntor unui Enumeration, se va parcurge intr-o bucl ca mai jos:
Collection c = ... Iterator i = c.iterator(); while (i.hasNext()) { process(i.next()); }

next(), pentru a fi utilizat mai departe.

Se verifica daca mai avem element de iterat prin hasNext() si apoi se preia acest element cu

Metoda remove() este o noutate si nu are echivalent in Enumeration. Atunci cnd este apelata, va terge din colecia sursa, in cazul in care aceasta operaie este suportata. Atenie, daca se itereaz in colecie cu metoda next() vom primi o excepie de tipul ConcurrentModificationException.

20

Iterator pentru filtrare

Pe lng posibilitatea de a parcurge elementele din colecie, putem aplica un predicat, si anume interfaa avnd o metoda ce filtreaz elementele din colecie.
interface Predicate { boolean predicate(Object element); }

Atunci cnd apelam metoda next() a iteratorului, va fi returnat acel urm tor element din colecie care ndeplinete condiia data de metoda predicate(). Mai jos avem un exemplu de folosire al acestui mecanism:
interface IPredicate { public boolean predicate(Object o); } class Predicate implements IPredicate { public boolean predicate(Object o) { return o.toString().startsWith("A"); } }

Avem aici interfaa Ipredicate si clasa ce o implementeaz si anume Predicate. In implementarea din clasa Predicate, metoda predicate va returna true, doar daca String-ul, si anume elementul curent, ncepe cu litera A. Pentru a vedea implementarea clasei particularizate Iterator si anume IteratorWithPredicate, avem exemplul de mai jos:
import java.util.*; public class IteratorWithPredicate implements Iterator { //elementul cu ajutorul caruia parcurgem //colectia care va folosi IteratorWithPredicate Iterator iter; //elementul care va filtra Predicate pred; //urmatorul obiect din colectie Object next; //variabila care ne avertizeaza ca am //parcurs intreaga colectie

21

//adica daca mai am element de returnat boolean doneNext = false; public IteratorWithPredicate(Iterator iter, Predicate pred) { this.iter = iter; this.pred = pred; } public void remove() { //inca nu oferim cod valid pentru aceasta metoda throw new UnsupportedOperationException(); } //implementam metoda hasNext public boolean hasNext() { doneNext = true; boolean hasNext; while (hasNext = iter.hasNext()) { next = iter.next(); if (pred.predicate(next)) { break; } } return hasNext; } //si metoda next ale interfetei Iterator public Object next() { if (!doneNext) { boolean has = hasNext(); if (!has) { throw new NoSuchElementException(); } } doneNext = false; return next; } }

Clasa IteratorWithPredicate implementeaz interfaa definit in Java Iterator, deci va trebui sa suprascrie metodele remove(), next() si hasNext(). In cazul in care nu dorim sa implementam o metoda, cu un corp particularizat, nsa suntem nevoi i sa o facem din condiii de polimorfism, vom arunca o eroare in acea funcie. Pentru a exemplifica cele spuse avem metoda remove(). In metoda hasNext() se preia urmtorul element din lista prin intermediul variabilei iter, 22

care este un iterator Java, si se aplica funcia predicate cu parametru obiectul obinut prin iter. Daca funcia returneaz true, nu vom trece mai departe, rmnem pe elementul gsit si se returneaz false in cazul in care mai avem elemente sau true daca am ajuns la finalul coleciei. Daca nsa predicate returneaz false, mergem mai departe in colecie, pana cnd funcia va returna true, sau am ajuns la finalul coleciei. In continuare avem un mic exemplu de folosire a acestor clase, si cu precdere a iteratorului personalizat.
class PredTest { static Predicate pred = new Predicate(); public static void main (String args[]) { String[] str ={"Anca","Razvan","Maria","Daniela","Paul", "Adrian","Dorin"}; List list = Arrays.asList(str); Iterator i1 = list.iterator(); Iterator i = new IteratorWithPredicate(i1, pred); while (i.hasNext()) { System.out.println(i.next()); } } }

In acest mic exemplu, se transforma irul str, in colecie, si anume list:


List list = Arrays.asList(str);

Apoi se construiete un iterator personalizat pe baza iteratorului lui list:


Iterator i = new IteratorWithPredicate(i1, pred);

Folosind acest iterator se parcurge lista returnndu-se elementele pe baza filtrului din Predicate, si anume acele elemente ce ncep cu A. Exceptiile ce pot apare in colectii Exceptii de modificare concurentiala Exceptia ConcurrentModificationException apare datorita proprietii de fail-fast a iteratorilor. Daca o colecie este modificata in timp ce iteram prin elementele acesteia, apare acest conflict. Pentru a exemplifica avem clasa de mai jos:
import java.util.*; public class CollectionException

23

{ public static void main (String args[]) { String[] str = {"1","a","et"}; List list = new ArrayList(Arrays.asList(str)); Iterator i = list.iterator(); while (i.hasNext()) { System.out.println(i.next()); list.add("Element"); } } }

Problema este ca dorim sa adugm un element in colecia list in momentul parcurgerii acesteia: list.add("Element");, si atunci va apare excepia:
1 Exception in thread "main" java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) at java.util.AbstractList$Itr.next(AbstractList.java:343) at curs6.CollectionException.main(CollectionException.java:23)

Metodele de modificare a coleciei sunt:


add() addAll() clear() remove() removeAll() retainAll()

Exceptii pentru operatii neimplementate Excepia UnsupportedOperationException este aruncata atunci cnd se apeleaz o metoda a unei colecii, nsa aceasta metoda nu este implementata corespunztor. Cu alte cuvinte acea colecie nu are capabilitatea de a efectua operaiunea ceruta. Pentru a exemplifica, sa luam metoda Arrays.asList(), ce returneaz o colecie de lungime fixa. Nu putem aduga un nou element in acest tip de colecie:
List list = Arrays.asList(args); list.add("Add"); // Arunca UnsupportedOperationException

Liste: List
List este in Java o interfa ce implementeaz Collection. Exista doua clase ce implementeaz aceasta interfaa: ArrayList si LinkedList. Interfata List ofer posibilitatea de a lucra ordonat, deci permite pstrarea secveniala a elementelor dintr-o colecie.

24

Metodele List Pe lng metodele implementate pentru ca List este o Collection mai avem: Metoda indexOf() lastIndexOf() listIterator() set() get() remove() subList() Descriere Cauta un element in lista Cauta un element in lista ncepnd de la sfritul acesteia returneaz iteratorul personalizat al listei modifica un element specificat din lista returneaz un element din lista terge un anumit element din lista returneaz o parte din lista

Fiecare din clasele concrete vor implementa aceste metode. Pe lng , vor avea si metode specifice in funcie de clasa. ArrayList Aceasta clasa este echivalentul clasei Vector, dar sub forma unei colecii. Un ArrayList este o colecie de elemente indexate intr-o anumit ordine, dar nu neaprat sortate. Aceasta indexare permite accesul rapid la date, dar o insertie si stergere mai lenta. Iat cteva dintre funciile oferite in plus de ArrayList: Metoda ArrayList() ensureCapacity() removeRange() trimToSize() Crearea unui ArrayList Se pot folosi doi constructori pentru acest lucru si anume:
public ArrayList() public ArrayList (int initialCapacity)

Descriere Constructorul pentru o lista goala Creeaz un buffer intern cu o capacitate dubla fata de cea anterioara terge un anumit interval de elemente din lista limiteaz capacitatea buffer-ului intern la mrimea specificata

Primul este pentru a instantia un obiect cu o lista goala. Al doilea constructor instaniaz o colecie de elemente null, cu dimensiunea specificata in parametru. De exemplu:
String elements[] = {"Shopping List","Wine List","Food List"}; List list = new ArrayList(Arrays.asList(elements));

25

Adaugarea unor elemente Se va face utiliznd funciile de add:


public boolean add(Object element) public boolean add(int index, Object element)

Prima funcie aduga un obiect in lista la sfritul listei. A doua funcie, ce suprancrca funcia add, permite adugarea la indexul specificat si elementele de dup, sunt mpinse mai la dreapta cu o unitate. Indexarea pornete de la zero. Pentru a exemplifica adugarea, avem poriunea de mai jos:
List list = new ArrayList(); list.add("3"); list.add("abs"); list.add("58"); // Adaug in interiorul listei list.add(1, "un nou element");

De asemenea, ArrayList fiind o colecie, putem aduga prin metodele de addAll, colecii:
public boolean addAll(Collection c) public boolean addAll(int index, Collection c)

fiecare element din colecia, data ca argument, va fi pusa in lista, prin apelul metodei add(), in cazul primei metode addAll(). In cazul in care folosim si un parametru index, inserarea in lista va avea loc de la indexul specificat. Daca lista, cu ajutorul cruia apelam aceste metode se modifica, atunci metoda va returna true. Daca adugarea nu este suportata, va apare o excepie de tipul UnsupportedOperationException. Returnarea unui element Se realizeaz prin metoda get:
public Object get(int index)

i are ca efect returnarea elementului de la pozi ia specificata. Stergerea unui element Se pot sterge toate elementele dintr-o lista:
public void clear()

26

De asemenea se pot terge elemente specifice din lista:


public boolean remove(Object element) public Object remove(int index)

A doua metoda terge un element de la o anumita poziie, daca poziia este valida. Prima metoda terge un element din lista comparnd elementele listei cu parametrul. Pentru a verifica egalitatea se folosete metoda equals(). In acest caz se terge primul element din lista egal cu parametrul. Se poate terge si o colecie prin metoda
public boolean removeAll(Collection c)

Aceasta metoda va terge toate instanele obiectelor din lista gsita in colecia c. De exemplu, daca lista originala era:
{element,index,element,sir,clasa}

Si colectia data este:


{lista,multime,element}

atunci lista rezultanta este:


{index,sir,clasa}

Retinerea unei colectii Funcia retainAll este prezentata si mai sus, cnd am vorbit despre colecii:
public boolean retainAll(Collection c)

Prin aceasta funcie se rein in colecie doar acele elemente comune listei si coleciei c. Stergerea unui interval Funcia removeRange() este o metoda implementata doar de ArrayList:
protected void removeRange(int fromIndex, int toIndex)

Evident, nu poate fi folosita decat in clase ce extind ArrayList si are ca efect tergerea elementelor cuprinse intre fromIndex si toIndex.

27

Operatii cu Liste Returnarea elementelor din lista Aceasta se va realiza prin intermediul iteratorului sau a metodei listIterator(), specifica doar listelor:
public Iterator iterator() public ListIterator listIterator() public ListIterator listIterator(int index)

Al treilea obiect, are ca parametru un index, pentru ca sa putem ncepe cutarea elementelor de la o anumita poziie. De exemplu:
List list = Arrays.asList(new String[] {"323", "re", "12","eo"}); Iterator iter = list.iterator(); while (iter.hasNext()) { System.out.println(iter.next()); }

Gasirea elementelor nainte de a returna un element in lista putem apela metoda contains, pentru a verifica daca un element se alfa in lista sau nu:
public boolean contains(Object element)

Putem apoi cuta un element si returna indexul pe care acesta se gsete:


public int indexOf(Object element) public int lastIndexOf(Object element)

Prima metoda itereaz ncepnd cu primul element si in momentul in care a gsit elementul egal cu parametrul returneaz poziia acestuia in lista. Compararea se face pe baza metodei equals. A doua metoda are acelai comportament ca si prima, cu meniunea ca, cutarea ncepe cu ultimul element. De asemenea se poate folosi si funcia containsAll in condiiile mai sus menionate. Modificarea unui element Aceasta operaiune se realizeaz cu metoda set(): 28

public Object set(int index, Object element)

Se poate astfel modifica valoarea unui element de la o pozi ie specificata prin index. Mrimea unei liste Folosind metoda size() se poate afla cate elemente sunt intr-o lista. Pe deasupra, prin metoda ensureCapacity(), se poate redimensiona buffer-ul intern care conine lista. Pentru a micora numrul de elemente din lista avem funcia trimToSize. Pentru a concluziona cele prezentate mai sus avem un mic exemplu de folosirea acestor tipuri de liste:
public class ArrayLists { public static void main(String args[]) { ArrayList list = new ArrayList(); list.add("prim"); list.add("2"); list.add("al treilea"); list.add(1,"intre 1 si 2"); if (list.contains("al treilea")) list.remove("al treilea"); System.out.println(list); System.out.println(list.indexOf("2")); } }

Egalitate in liste De mai multe ori am afirmat ca egalitatea se verifica prin metoda equals. Aceasta poate fi suprascrisa, in cazul in care lucram cu o lista particularizata dup cum vom vedea in continuare. Pe lng metoda equals, atunci cnd se compara doua elemente, se compara si id-urile lor adic ceea ce returneaz funcia hashCode(). Am precizat in cursul anterior ca aceasta funcie returneaz un int ce reprezint codul unic al obiectului, si aceasta funcie respecta mai multe conditii. Mai jos avem declaraia acesteia
public int hascode()

Pentru a nelege cum se folosesc aceste func ii avem exemplul de mai jos:

29

import java.util.ArrayList; class Point { public int x; public int y; public Point (int x, int y) { this.x = x; this.y = y; } public boolean equals(Object o) { if (!(o instanceof Point)) return false; Point pt = (Point)o; return ((pt.x == this.x) && (pt.y==this.y)); } public int hashCode() { return 17*this.x +23*this.y+43; } public String toString() { return "x = " +x+ " y = " +y + " id = "+ hashCode()+ "\n"; } } public class Mylist { public static void main(String args[]) { ArrayList list = new ArrayList(); list.add(new Point(1,2)); list.add(new Point(3,4)); list.add(new Point(2,1)); list.add(new String("un punct")); Point pt = new Point(-1,-1); System.out.println(list.contains(pt)); System.out.println(list.contains("un punct")); System.out.println(list.contains(new Point(3,4))); System.out.println(list); } }

30

ntotdeauna funcia hashcode() va trebui sa returneze un numr diferit daca obiectele sunt diferite. Spre exemplu :
list.add(new Point(1,2)); list.add(new Point(2,1));

Avem doua obiecte diferite. Daca in funcia hashCode() s-ar fi returnat simplu suma celor doua coordonate, obiectele ar fi fost egale. De aceea se ncearc o formula care sa evite aceasta situaie, si anume prin nmulire cu numere prime si apoi nsumare. In urma rul rii acestui program, acesta este rezultatul:
false true true [x = 1 y = 2 id = 106 , x = 3 y = 4 id = 186 , x = 2 y = 1 id = 100 , un punct]

Evident obiectul de tip String un punct, nu face parte din clasa Point, deci nu va respecta acele metode equals si hashCode(). Pentru a fora ca intr-o lista sa fie acceptate doar elemente de un anumit tip de data, se folosesc liste generice despre care vom vorbi imediat. In continuare vom studia cealalt clasa ce implementeaz ArrayList si anume LinkedList LinkedList Aceasta este implementarea listei nl nuite descrise la nceputul acestui curs si anume lista dublu nlnuita. Vom discuta in continuare doar de metodele specifice LinkedList, celelalte metode fiind implementate din interfaa Collection. Crearea unei LinkedList Constructorii pentru crearea unei liste simplu nlnuite sunt:
public LinkedList() public LinkedList(Collection col)

Adaugarea in LinkedList Pentru a aduga un element intr-o lista exista:


public boolean add(Object element) public boolean add(int index, Object element)

31

ntr-adevr, se pot aduga elemente la un anumit index, pentru ca si intr-o lista inlantuita elementele sunt ordonate. De asemenea o lista simplu nl nuita poate funciona ca o stiva sau ca o coada, ca atare implementeaz metodele:
public boolean addFirst(Object element) public boolean addLast(Object element)

Cele doua metode adugare permit plasarea elementului, fie la sfritul listei fie la capul ei. Pentru a obine elementele de la capete avem:
public Object getFirst() public Object getLast()

Stergerea elementelor Se pot sterge elementele de la capete folosind metodele:


public Object removeFirst() public Object removeLast()

In cazul in care operaiunea de tergere nu este suportata, va apare excepia UnsupportedOperationException. Iteratorul LinkedList Iteratorul se numete ListIterator si extinde Iterator. Deoarece listele dublu nl nuite conin elemente cu referina ctre next si prev, iteratorul va permite deplasarea in ambele direcii. Iata mai jos metodele implementate de acest iterator: Metoda hasNext hasPrevious next nextIndex previousIndex remove set Descriere verifica pe direcia nainte, daca mai sunt elemente verifica pe direcia napoi, daca mai sunt elemente returneaz urmtorul elemente returneaz indexul din colecie al urmtorului element returneaz indexul din colecie al elementului anterior terge un element din iterator modifica elementul curent

32

Pentru a nelege mai bine lucrul cu aceste liste avem urmtorul exemplu:
public class LinkedLists { public static void main(String args[]) { LinkedList list = new LinkedList(); list.add(new Integer(2)); list.addFirst(new Double(5.6)); list.addFirst(new Double(5)); list.addFirst(new Float(3.4)); list.addLast(new Short((short)10)); ListIterator it = list.listIterator(); //parcurgere de la cap la coada while (it.hasNext()) System.out.print(" " + it.next()); //sterg din iterator ultimul element //care a fost returnat System.out.println(); it.remove(); //parcurgere de la coada la cap while (it.hasPrevious()) { System.out.print(" "+ it.previous()); System.out.print(" " +it.previousIndex()); } System.out.println(); System.out.println(list); } }

Rezultatul este urmtorul:


3.4 5.0 5.6 2 10 2 2 5.6 1 5.0 0 3.4 -1 [3.4, 5.0, 5.6, 2]

33

Multimi: Set
Interfaa Set reprezint un grup de elemente fr duplicate. Nu exista o condiie anume ce impune acest lucru: adic sa nu fie duplicate elementele unui Set, ci implementrile din clasele Set, sunt cele care impun aceasta condi ie. Interfaa Set deriva din Collections, deci va implementa aceleai metode ca si aceasta interfaa, cu specificarea ca elementele trebuie sa fie unice. De asemenea un element, aflat deja in mul ime, nu poate fi modificat. Aceasta interfaa va fi implementata de doua clase: TreeSet si HashSet. HashSet nainte de a prezenta aceasta clasa, vom spune ca HashSet este implementat ca un HashMap, sau un hashtable. Vom insista asupra acestor colecii in cursul urmtor. In continuare, vom studia operaiunile principale cu acest tip de data. Crearea unui HashSet
public HashSet() public HashSet(int initialCapacity) public HashSet(int initialCapacity, int loadFactor)

Primii doi constructori sunt asemntori celor studiai. In cazul celui de-al treilea constructor, se poate specifica un factor de mrire a capacitaii, in cazul in care dorim acest lucru. De exemplu, daca nu dorim sa mrim cu 100% colecia, atunci cnd mrim capacitatea, putem specifica 75%, 50% etc. Iat mai jos un exemplu de instaniere a unui HashSet:
String elements[] = {"Englez", "German", "Roman","Italian"}; Set set = new HashSet(Arrays.asList(elements));

Adaugarea elementelor Se pot aduga elemente unul cate unul prin metoda:
public boolean add(Object element)

Metoda are ca efect adugarea elementului, daca acesta nu este in Set, in caz contrar nu se aduga elementul si metoda returneaz false. Comparaia intre doua elemente se realizeaz cu ajutorul metodei equals. Se poate aduga si o colecie de elemente cu:
public boolean addAll(Collection c)

34

Acelai regim se va aplica si aici, adic doar elemente unice din colecie, care nu sunt in mulime, vor fi adugate. Stergerea elementelor

Pentru a terge un anumit element din mulime exista metoda:


public boolean remove(Object element)

La fel, se pot terge mai multe elemente, prin:


public boolean removeAll(Collection c)

Efectul acestei funcii este de a elimina din mulime doar o singura data, elementele g site in colecia c. de exemplu daca avem mulimea:
{Englez", "German", "Roman","Italian"}

si tergem din Set colecia:


{Englez", "German", "Englez","Englez"}

Va rmne in mulimea originala:


{"Roman","Italian"}

Pentru a retine anumite elemente exista funcia


public boolean retainAll(Collection c)

ce are efectul invers funciei removeAll. Celelalte operaii din mulime sunt perfect asemntoare cu cele din ArrayList, cu meniunea ca se va respecta condiia ca elementele sa fie unice. Insistam asupra metodei hashCode si equals:
public boolean equals(Object o) public int hashCode()

Aceste doua metode sunt cele prin care se verifica daca un element este deja in mul ime sau nu. Pentru o buna funcionare a verificrii daca un obiect este in mulime sau nu, va trebui sa suprascriem corect ambele metode. Folosind exact clasa Point, din exemplul de mai sus, iat cum se pot folosi mulimile:

35

public class Multimi { public static void main(String args[]) { // Create the set Set set = new HashSet(); // Adaug in multime set.add(new Point(1,2)); set.add(new Point(2,1)); set.add("c"); // Sterg un element din multime set.remove("c"); //Marimea unei multimi int size = set.size(); System.out.println(size); // Adaug un element ce exista deja set.add(new Point(1,2)); //fara a avea insa efect size = set.size(); System.out.println(size); //Verificam daca un element este deja //in multime boolean b = set.contains(new Point(2,1)); System.out.println(b); b = set.contains("c"); // false System.out.println(b); // Parcurgem multimea Iterator it = set.iterator(); while (it.hasNext()) { //si afisam elementele Object element = it.next(); System.out.println(element); } } }

// true

Observam ca , se pot introduce in mulime, ca si in ArrayList obiecte de diverse tipuri. Uneori se dorete lucrul cu anumit tip de obiecte. In acel caz vom folosi liste generice.

36

Liste Generice Se declara folosind < si > pentru a specifica tipul de data ce va fi admis in aceste colec ii. In rest comportamentul este exact acelai. Reluam un exemplu de la ArrayList folosind liste generice.
List<Point> list = new ArrayList(); list.add(new Point(1,2)); list.add(new Point(3,4)); list.add(new Point(2,1)); list.add(new String("un punct")); Point pt = new Point(-1,-1);

Eroarea de sintaxa, va apare atunci cnd ncercm sa adugm un element de tip String, altul dect cel admis in list. Listele generice admit doar referine, si nu admit tipuri primitive.

37

Curs7
TreeSet ................................................................................................................................................ 3 Crearea unui TreeSet ....................................................................................................................... 4 Adugarea elementelor ............................................................................................................... 5 Comparatorul .............................................................................................................................. 5 Returnarea elementelor............................................................................................................... 6 Folosirea submulimilor ............................................................................................................... 7 Sortarea coleciilor .............................................................................................................................. 8 Interfaa Comparable....................................................................................................................... 8 Comparator ................................................................................................................................... 11 Dictionary, HashTable i Properties.................................................................................................... 13 Clasa Dictionary ............................................................................................................................. 13 Clasa HashTable ............................................................................................................................. 14 Creearea HashTable ................................................................................................................... 16 Adugarea perechilor de chei-valoare ........................................................................................ 16 tergerea unei perechi .................................................................................................................. 16 Mrimea unui HashTable ........................................................................................................... 17 Operaii cu HashTable .................................................................................................................... 17 Returnarea obiectelor din HashTable ......................................................................................... 17 Cutarea elementelor ................................................................................................................ 18 Verificarea egalitii ntre HashTable.......................................................................................... 19 Clasa Properties ............................................................................................................................. 20 Setarea i preluarea elmentelor ................................................................................................. 21 ncrcarea i salvarea datelor ..................................................................................................... 21 Map................................................................................................................................................... 22 Interfaa Map.Entry ....................................................................................................................... 23 Clasa HashMap .............................................................................................................................. 23 Creearea unui HashMap............................................................................................................. 24 Adugarea n HashMap .............................................................................................................. 24 tergerea unui element ............................................................................................................. 24 Operaii cu HashMap ................................................................................................................. 25 Clasa WeakHashMap ..................................................................................................................... 25 Clasa TreeMap ............................................................................................................................... 27 Crearea unui TreeMap ............................................................................................................... 27 Operaiuni cu Map ..................................................................................................................... 28 1

Clasa Collections ................................................................................................................................ 30 Sortarea......................................................................................................................................... 30 Cutarea ........................................................................................................................................ 31

TreeSet
Implementrile interfeei Set, sunt HashSet i TreeSet. Mai jos este o ierarhie complet a claselor abstracte i interfeelor.

Figura 7.1 Ierarhia claselor Set

Clasa TreeSet funcioneaz ca i un HashSet cu meniunea c pstreaz elementele ntr-o ordine. Aceste elemente sunt ordonate ntr-un arbore balansat i anume un arbore rou-negru. Pstrnd elementele ntr-un arbore rou-negru, costul unei cutri devine logaritmic si ordinul de complexitate este O(log n) . Un arbore rou-negru respect urmtoarele reguli: 1. Fiecare nod este rou sau negru 2. Rdcina este ntotdeauna un nod negru 3. Dac nodul este rou, copii lui trebuie sa fie de culoare neagr 4. Fiecare cale de la rdcin la frunze trebuie sa conin acelai numr de noduri negre. Mai jos avem o schi a unui arbore de acest tip:

Figura 7.2 Arbore rou-negru

Iat metodele implementate de aceast clas: Nume metoda TreeSet() add() addAll() clear() clone() comparator() contains () first() headSet() isEmpty() iterator() last() remove() size() subSet() tailSet() Crearea unui TreeSet Clasa ofer patru constructori. Primii doi creeaz TreeSet-uri goale:
public TreeSet() public TreeSet(Comparator comp)

Descriere Constructorul unui TreeSet. Adaug un element n mulime Adaug o colecie de elemente n mulime terge elementele din mulime Creeaz o clon cu elementele din mulime Returneaz un obiect de tip Comparator Verific existena unui obiect n mulime Returneaz primul element din mulime Returneaz un subset de elemente de la nceputul mulimii Verific dac mulimea este goal. Returneaz un obiect din set ce permite vizitarea mulimii Returneaz ultimul element din ir terge un element din mulime Returneaz numrul de elemente din subset Returneaz o submulime din mulimea iniial Returneaz o submulime de elemente de la sfritul mulimii iniiale

Pentru a menine o anumit ordine n aceast structur, elementele adugate n arbore trebuie s fie sortate ntr-un anume fel. Al doilea constructor permite specificarea obiectului de tip Comparator, ce va ajuta la sortarea acestei structuri. Al doilea set de constructori, constituie constructori de copiere:
public TreeSet(Collection col) public TreeSet(SortedSet set)

Dac o colecie transmis ca parametru, este de tip SortedSet, atunci clasa TreeSet va efectua unele optimizri, la adugarea elementelor. Adugarea elementelor Pentru a aduga un singur element se poate apela metoda add: Metoda add este aceeai ca i pentru HashSet. Diferena major este c, elementele ce sunt adugate trebuie s implementeze interfaa Comparable sau constructorul TreeSet trebuie s aib un parametru de tip Comparator. Dac nici una din aceste condi ii nu este adevrat, atunci va fi aruncat excepia ClassCastException. Fiecare element din aceast colecie trebuie s fie comparabile. Comparatorul Metoda comparator() returneaz obiectul Comparator al acestui arbore:
public Comparator comparator()

Aceast metod nu este apelat frecvent, dar dac ne intereseaz ce tip de comparator este folosit, poate fi util. Metoda va returna null, n cazul n care se alege ordinea natural de sortare a elementelor. Mai jos este un exemplu de sortare:
import java.util.*; public class Comparare { public static void main (String args[]) throws Exception { String elements[] = {"Radu", "Andrei", "Ion","Vasile", "Mircea"}; //creearea unui set cu un comparator Set set = new TreeSet(Collections.reverseOrder()); for (int i=0, n=elements.length; i<n; i++) { set.add(elements[i]); } //afisez elementele setului System.out.println(set);

//afisez comparatorul actualului set System.out.println(((TreeSet)set).comparator()); } }

Acest program va avea ca rezultat, n urma rul rii:


[Vasile, Radu, Mircea, Ion, Andrei] java.util.Collections$ReverseComparator@3e25a5

Vom reveni ulterior asupra metodelor de comparare. Returnarea elementelor Se pot folosi metodele first() i last() pentru a returna primul sau ultimul element din set:
public Object first() public Object last()

Aceste elemente de la capetele coleciei se gsesc pe baza comparrii elementelor i anume primul element este cel mai mic iar ultimul este cel mai mare. Dac nu exist nici un element n colecie, va fi aruncat excepia NoSuchException. Un mod de a parcurge elementele unei colecii TreeSet,este metoda iterator():
public Iterator iterator()

Din moment ce elemente sunt sortate, metoda va returna elementele n ordinea din arbore. Mai jos este un exemplu pentru a parcurge elementele unui set:
import java.util.*; public class Iterare { public static void main(String args[]) { String elements[] = {"Radu", "Andrei", "Ion","Vasile", "Mircea"}; Set set = new TreeSet(Arrays.asList(elements)); Iterator iter = set.iterator(); while (iter.hasNext()) { System.out.println(iter.next()); } } }

Acest exemplu are ca rezultat:


Andrei Ion Mircea Radu Vasile

Dup cum se poate vedea, folosirea iteratorului este aceeai cu utilizrile prezentate n cazul celorlalte colecii. Folosirea submulimilor Din moment ce un TreeSet, este ordonat, un subarbore va fi tot ordonat. Iat dou metode ce returneaz un subarbore format din elementele celui original:
public SortedSet headSet(Object toElement) public SortedSet tailSet(Object fromElement) public SortedSet subSet(Object fromElement, Object toElement)

Toate metodele vor returna un obiect, o interfa a arborelui original care, reflect elementele din colecia original. Altfel spus, dac tergem un obiect din submulime, el va dispare i din arborele original. Dac adugam un element subarborelui, el va fi adugat n arborele original. Mai jos avem un exemplu de utilizare a acestei funcii:
public static void main(String args[]) { String elements[] = {"Radu", "Andrei", "Ion","Vasile", "Mircea"}; TreeSet set = new TreeSet(Arrays.asList(elements)); SortedSet subset = set.subSet("Ion", "Vasile"); System.out.println(subset); subset.remove("Mircea"); subset.remove("Mihai"); subset.add("Mihai"); System.out.println(set); }

Rezultatul va fi:
[Ion, Mircea, Radu] [Andrei, Ion, Mihai, Radu, Vasile]

Intervalul din care sunt preluate elementele este:


fromElement <= set view < toElement

Dac dorim ca toElement s fie n subarbore, va trebui, fie s transmitem ca parametru, urmtorul element din colecie, fie s folosim un truc, i anume s adugm ceva la sfrit:
SortedSet headSet = set.headSet(toElement+"\0");

Dac nu vrem ca primul element s fie n subarbore, va trebui s apelm la un truc asemntor:
SortedSet tailSet = set.tailSet(fromElement+"\0");

Sortarea coleciilor
n acest capitol vom vedea ce mecanism implementeaz coleciile, pentru a sorta elementele, ntr-un mod ct mai eficient. Exist n principiu dou modaliti de a sorta coleciile: implementnd interfaa Comparable sau printr-un Comparator personalizat. Interfaa Comparable Clasele definite de Java au deja implementate interfaa Comparable. Interfaa Comparable are o metod i anume compareTo(), ce definete modul n care se compar i implicit, se sorteaz obiectele. Mai jos avem o serie de clase ce implementeaz natural aceasta interfa: Nume clas BigDecimal BigInteger Byte Character CollationKey Date Double File Float Integer Long ObjectStreamField Short String Aranjare Numeric (cu semn) Numeric (cu semn) Numeric (cu semn) Numeric (fr semn) Alfabetic, pe setri locale Cronologic Numeric (cu semn) Alfabetic legat de cale Numeric (cu semn) Numeric (cu semn) Numeric (cu semn) Alfabetic de tip String Numeric (cu semn) Alfabetic

Exist cteva condiii pe care trebuie s le respectm atunci cnd scriem o metod compareTo:
public int compareTo(Object Obj)

1. Elementele trebuie s fie comparabile mutual. Dac dou elemente sunt comparabile mutual, nseamn c au variabile ce pot fi distinctive. Pe de alt parte exist obiecte ce nu pot fi comparabile mutual ca de exemplu un File, un TreeSet etc. n cazul acesta vom primi excepia ClassCastException atunci cnd implementm interfaa Comparable. 2. Valoarea de returnat exprim poziia relativ ntr-o ordonare natural. Metoda compareTo poate returna trei valori. Va returna un numr negativ, dac obiectul curent vine naintea obiectului cu care se face comparaia. Va returna un numr negativ, dac obiectul curent ajunge n colecie dup elementul cu care se face comparaia. Va returna zero dac obiectele sunt egale. 3. Ordonarea natural se va face pe baza metodei equals(). Faptul c dou elemente sunt egale sau nu, ar trebui definit prin suprascrierea metodei equals, aa cum am vzut i n cadrul coleciilor. Aceast condiie este mai mult o recomandare. 4. Niciodat nu se apeleaz metoda direct. Acest mecanism de sortare, este implementat n cadrul FrameWork-ului i nu avem acces direct. Singurul lucru pe care trebuie s l facem este sa implementm metoda, i ea va fi automat apelat la operaiile aplicate pe colecii. Pentru a demonstra funcionarea acestui mecanism avem exemplul de mai jos:
import java.util.*; public class Angajat implements Comparable { String departament; String nume; public Angajat(String departament, String nume) { this.departament = departament; this.nume = nume; } public String getdepartament() { return departament; } public String getnume() { return nume; } public String toString() { return "[dep=" + departament + ",nume=" + nume + "]"; } public int compareTo(Object obj) { Angajat decomparat = (Angajat)obj; //daca sunt din departamente diferite //vom compara departamentele int deptComp = departament.compareTo(decomparat.getdepartament()); //daca sunt din acelasi departament

//vom compara numele angajatilor return ((deptComp == 0) ? nume.compareTo(decomparat.getnume()) : deptComp); } public boolean equals(Object obj) { if (!(obj instanceof Angajat)) { return false; } Angajat a = (Angajat)obj; return departament.equals(a.getdepartament()) && nume.equals(a.getnume()); } public int hashCode() { return 43*departament.hashCode() + nume.hashCode(); } }

n acest exemplu avem o clas cu dou variabile membru i anume departament si nume. Aceste dou elemente sunt comparabile, deci le vom putea utiliza n sortare. Metoda compareTo() va returna implementa logica dorit i anume de a sorta mai nti dup departament i apoi dup nume. Pentru aceasta se va calcula un numr deptComp pe baza comparrii departamentului obiectului transmis ca parametru i al obiectului actual. Vom folosi aceasta valoare deptComp n rezultatul final astfel:
return ((deptComp == 0) ? nume.compareTo(decomparat.getnume()):deptComp);

adic, n cazul in care deptComp este diferit de zero, deci avem dou departamente diferite, returneaz rezultatul comparrii acestora. Altfel returneaz rezultatul comparrii obiectelor nume de tip String. Se observ c logica poate fi extins n cazul n care avem mai multe proprieti de comparat i se poate stabili o prioritate n aceast operaie. Pentru a observa cum sunt folosite aceste mecanisme de comparaie avem exemplul de mai jos:
class Companie { public static void main (String args[]) { Angajat angajati[] = { new Angajat("HR", "Irina"), new Angajat("HR", "Cristina"), new Angajat("Ingineri", "Daniel"), new Angajat("Ingineri", "Vlad"), new Angajat("Ingineri", "Octavian"), new Angajat("Vanzari", "Emil"),

10

new Angajat("Vanzari", "Eugen"), new Angajat("RC", "Bogdan "), new Angajat("RC", "Avram"), }; Set set = new TreeSet(Arrays.asList(angajati)); //parcurgem multimea sortata deja //insa ea contine obiecte for(Object o : set) { //asa ca va trebui sa apelam cast Angajat a = (Angajat)o; System.out.println(a); } } }

Automat, la constituirea noii colecii format din obiecte de tip Angajat, se apeleaz metoda de sortare i anume compareTo. Dup cum se observ aceast metod nu este apelat explicit. Rezultatul este:
[dep=HR,nume=Cristina] [dep=HR,nume=Irina] [dep=Ingineri,nume=Daniel] [dep=Ingineri,nume=Octavian] [dep=Ingineri,nume=Vlad] [dep=RC,nume=Avram] [dep=RC,nume=Bogdan ] [dep=Vanzari,nume=Emil] [dep=Vanzari,nume=Eugen]

Aceast metod de sortare este una complex i acoper multe cazuri, ns oblig ca obiectele ce vor fi sortate s implementeze interfaa Comparable. De asemenea pot fi comparate, doar obiecte de acelai tip, ceea ce pentru majoritatea cazurilor este suficient. Comparator Atunci cnd nu dorim s impunem ordonarea natural a claselor, sau clasele nu implementeaz interfaa Comparable, avem la dispoziie metoda compare din interfaa Comparator:
public int compare(Object obj1, Object obj2)

De asemenea trebuie suprascris metoda equals(), n cazul n care vorbim despre obiecte complexe (cu mai multe variabile membru). Anterior am folosit deja un Comparator pentru a demonstra lucrul cu TreeSet:
Set set = new TreeSet(Collections.reverseOrder());

11

Pentru a exemplifica lucrul cu aceast interfa avem urmtorul scenariu: un manager nou, inspecteaz compania descris anterior, i dorete s afle mai nti numele angajatului i apoi departamentul. Cum aceast clas poate implementa doar o singur interfa Comparable cu o singur metod compareTo va trebui s implementm o clas separat de clasa de baz i anume un Comparator. Mai jos avem un exemplu pentru o astfel de clas:
class AngComparator implements Comparator { public int compare(Object obj1, Object obj2) { Angajat a1 = (Angajat)obj1; Angajat a2 = (Angajat)obj2; int nameComp = a1.getnume().compareTo(a2.getnume()); return ((nameComp == 0) ? a1.getdepartament().compareTo(a2.getdepartament()) : nameComp); } }

Aceasta este clasa care poate constitui un comparator pentru o nou list dup cum vom vedea mai jos. De aceast dat, metoda compare are dou obiecte obj1 i obj2 pe care le va utiliza pentru comparaie. Logica este invers fa de exemplul anterior, i anume se verific mai nti numele angajatului i apoi se verific numele departamentului din care acesta face parte. Utilizarea acestui Comparator este n funcia main rescris:
public static void main (String args[]) { Angajat angajati[] = { new Angajat("HR", "Irina"), new Angajat("HR", "Cristina"), new Angajat("Ingineri", "Daniel"), new Angajat("Ingineri", "Vlad"), new Angajat("Ingineri", "Octavian"), new Angajat("Vanzari", "Emil"), new Angajat("Vanzari", "Eugen"), new Angajat("RC", "Bogdan "), new Angajat("RC", "Avram"), }; //noua coletie are un comparator de tip //AngComparator Set set = new TreeSet(new AngComparator()); set.addAll(Arrays.asList(angajati)); //parcurgem multimea sortata deja //insa ea contine obiecte for(Object o : set) {

12

//asa ca va trebui sa apelam cast Angajat a = (Angajat)o; System.out.println(a); } }

In continuare vom discuta despre colecii ce conin, nu doar un element, ci pe fiecare pozi ie sunt cte dou elemente, adic o pereche format din cheie i valoare.

Dictionary, HashTable i Properties


Aceste clase ajut n lucrul cu perechi de chei i valori, n foarte multe aplicaii acesta fiind o necesitate. Mai jos avem diagrama ce exprim ierarhia acestor clase:

Figura 7.3 Ierarhia claselor Dictionary, HashTable, Properties Un obiect Dictionary funcioneaz ca o carte de telefoane. Caut un anumit numr dup numele persoanei. Numele este cheia din dicionar i telefonul este valoarea. Presupunem n cadrul acestei comparaii, c numele persoanei este unic, deoarece, dup cum vom vedea cheia trebuie s fie unic. Clasa Dictionary Dictionary este o clas abstract ce con ine doar metode abstracte. Este alctuit din perechi: <cheie> valoare

13

Mai jos sunt descrise metodele din aceast clas: Numele metodei elements() get() 1.0 isEmpty() keys() put() remove() size() Descriere Returneaz un obiect din dicionar ce permite vizitarea tuturor cheilor Returneaz o valoare din dicionar Verific dac un dicionar este gol Returneaz colecia de chei din dicionar Introduce un element format din cheie i valoare terge un element din dicionar Returneaz numrul de element din dicionar

Deoarece Dictionary este o clas abstract, ea nu va fi folosit direct, ci vor exista implementri ale acesteia, ca de exemplu HashTable. Clasa HashTable Un HashTable este un Dictionary ce se bazeaz pe un algoritm de hashing ce convertete cheile in coduri hash pentru a cuta mai rapid n colecie. O funcie hash este o funcie matematic ce convertete o valoare numeric n date de mrime relativ mic, de obicei un ntreg, ce poate folosi ca index ntr-un ir. Valorile pe care funcia hash le returneaz se numesc coduri hash (a se vedea funcia hashCode()). Mai jos avem funciile pe care un HashTable le conine: Nume metod Hashtable() clear() clone() contains() containsKey() containsValue() elements() entrySet() equals() get() hashCode() isEmpty() keys() keySet() put() putAll() rehash() remove() size() toString() values() Descriere Constructorul coleciei terge elementele din colecie Creeaz un HashTable cu aceleai elemente Verific existena unui obiect n colecie Verific existena unei chei n HashTable Verific existena unei valori n HashTable Returneaz un element ce permite vizitarea coleciei Returneaz un set de perechi cheie valoare Verific egalitatea ntre dou obiecte Returneaz valoarea de la o anumit cheie. Calculeaz codul hash al unei colecii Verific dac colecia este goal sau nu. Returneaz o colecie de chei din HashTable. Returneaz o colecie de chei din HashTable. Pune o pereche cheie-valoare n HashTable Pune o colecie de cheie-valoare n HashTable Mrete capacitatea coleciei terge un element din colecie Returneaz numrul de elemente din colecie Returneaz ntregul HashTable ca un String Returneaz o colecie de valori din HashTable

14

Avantajul principal al acestei colecii este c inserarea ntr-o astfel de structur este fcut n timp O(1) . Nu conteaz ct de mare este structura, va lua cam acelai timp, pentru inserarea unui element. Cum se poate acest lucru? Atunci cnd folosim perechi chei-valoare, cheile sunt convertite n cod hash, folosind un algoritm de hash. Apoi acest cod este redus la o structur intern, pentru a fi folosit ca un index. Pentru dou elemente egale, codul va fi acelai iar pentru dou elemente diferite codul este diferit. Dac hashcode-ul este acelai pentru elemente diferite, discutm de coliziune. Dac sunt multe coliziuni, timpul de inserie i cutare, crete i se pierde astfel avantajul principal. Dac exist elemente multiple cu aceeai cheie, va trebui traversat o list nlnuit pentru a ajunge la valori. Mai jos avem un HashTable cu mai multe coliziuni:

Figura 7.4 un HashTable cu mai multe coliziuni

Mai jos avem i funcia care permite crearea acestor coliziuni


public class HashFunction { public static int hashFunction(String str) { int sum=0; for(int i=0;i<str.length();i++) sum=sum+Character.getNumericValue(str.charAt(i)); return sum; } public static void main (String args[]) { System.out.println(hashFunction("Marcu")); System.out.println(hashFunction("Adrian")); System.out.println(hashFunction("Ancuta")); System.out.println(hashFunction("Manole")); } }

15

n cadrul acestei funcii se calculeaz suma codurilor ASCII ale fiecrui caracter din nume. La rularea acestui program avem urmtoarele coduri:
101 101 114 114

Acesta este motivul apariiei coliziunilor n HashTable. Creearea HashTable Exist patru constructori pentru un HashTable, dintre care primii trei sunt:
public Hashtable() public Hashtable(int initialCapacity) public Hashtable(int initialCapacity, float loadFactor)

Ultimii doi constructori permit specificarea unei capaciti iniiale. Rata de mrire a capacitii implicite, care poate fi specificat n cel de-al doilea constructor este de dublul capacitii actuale +1. Se poate modifica prin ajustarea acelui procent. Al patrulea constructor iniializeaz HashTable prin copierea unei colecii n tabelul actual.
public Hashtable(Map t)

Adugarea perechilor de chei-valoare Spre deosebire de clasele studiate anterior, trebuie s oferim o cheie dar i o valoare:
public Object put(Object key, Object value)

ntr-un HashTable, cheile sau valorile nu pot fi null. ncercarea de introducere o cheie null n HashTable va produce eroarea NullPointerException. Introducerea unei perechi n care cheia se afla deja n colecie, va avea ca efect nlocuirea valorii de la acea cheie cu valoarea nou . Obiectul returnat va fi vechea valoare, sau dac obiectul nu era n colecie, obiectul returnat este null. Afiarea unui HashTable se face natural pentru ca implementeaz metoda toString(). tergerea unei perechi Se face utiliznd funcia remove():
public Object remove(Object key)

16

Dac cheia key este prezent n colecie, atunci va fi eliminata perechea cheie-valoare specificat prin key i valoarea este returnat. O alt metod de a terge elementele dintr-o colecie de acest tip, este clear():
public void clear()

Aceast metod va terge toate perechile din HashTable. Mrimea unui HashTable Putem controla mrimea unui HashTable doar dup crearea acestuia. Mai jos sunt cteva funcii pentru controlul mrimii unui HashTable:
public int size() public boolean isEmpty()

Aceste funcii returneaz mrimea coleciei i verific dac HashTable este gol sau nu. Exist o funcie de redimensionare a coleciei, dar este protected:
protected void rehash()

Aceasta permite crearea unui nou ir intern mai mare, insernd toate valorile din irul deja existent. Operaii cu HashTable Returnarea obiectelor din HashTable Exist cteva moduri de a returna date din HashTable. Cel mai simplu este apelnd get:
public Object get(Object key)

dac este gsit cheia dat ca parametru, atunci funcia va returna valoarea corespunztoare cheii. Dac nu este gsit, atunci funcia va returna null. Dac vrem s aflm cheile din colecie putem folosi:
public Enumeration keys() public Set keySet()

diferena const n modul n care sunt returnate cheile. Prima metod returneaz ca un valorile din HashTable:
Enumeration. A doua metod, returneaz colecia sub forma unui Set. Pe lng chei, se pot returna i

public Enumeration elements() public Collection values()

17

Metoda elements() returneaz o mulime de valori sub forma unei Enumeration. Metoda values() returneaz aceleai date, dar sub forma unei Collection. Cea mai complex metod de returnare a elementelor dintr-o colecie HashTable este entrySet()
public Set entrySet()

Aceasta returneaz o colecie de tip Set ce conine perechile de chei-valori. Acest tip de data returnat este Map, i vom discuta despre el n cele ce urmeaz. Iat dou moduri de a accesa cheile unei astfel de colecii:
Enumeration enum = hash.keys(); while (enum.hasMoreElements()) { String key = (String)enum.nextElement(); System.out.println(key + " : " + hash.get(key)); }

Un alt mod, implic folosirea ntregului obiect din colecie:

Set set = hash.entrySet(); Iterator it = set.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); System.out.println(entry.getKey() + " : " + entry.getValue()); }

Cutarea elementelor Clasa Hashtable conine trei metode, care ne permit cutarea unei chei sau a unei valori n colecie. Cea mai simpl rmne metoda get() discutat mai sus.
public boolean containsKey(Object key)

Aceast funcie verific existena unei chei n colecie. Alte dou metode vor verifica existena unei valori n colecie:
public boolean contains(Object value) public boolean containsValue(Object value)

Ambele realizeaz acelai lucru, ns pentru c HashTable implementeaz i interfaa Map, avem aceast duplicitate. Este de recomandat s folosim aceste dou funcii ct mai puin posibil, deoarece se parcurge ntreaga colecie, algoritmul avnd cel un ordin O(n).

18

Verificarea egalitii ntre HashTable Aceasta se poate realiza folosind funcia equals():
public boolean equals(Object o)

Egalitatea este definit de interfaa Map, iar nu la nivelul HashTable. n consecin, HashTable poate fi egal cu orice alt Map i nu doar un alt HashTable. Regula de baz este c, dou HashMap-uri sunt egale atunci cnd au aceleai perechi cheie-valoare. Ordinea nu conteaz. HashTable este imutabil. Dac un HashTable este iniializat cu un set de elemente, atunci pentru redimensionare va trebui s iniializm un nou obiect. Pentru a obine un Map, ce nu este imutabil avem urmtoarea funcie unmodifiableMap:
Hashtable h = new Hashtable(); // se va umple hashtable Map m = Collections.unmodifiableMap(h);

Mai jos avem un exemplu de folosire a HashTable pentru a numra cuvintele dintr-un fiier.
import java.io.*; import java.util.*; public class Cuvinte { static final Integer ONE = new Integer(1); public static void main (String args[]) throws IOException { Hashtable map = new Hashtable(); FileReader fr = new FileReader(args[0]); BufferedReader br = new BufferedReader(fr); String line; while ((line = br.readLine()) != null) { processLine(line, map); } Enumeration en = map.keys(); while (en.hasMoreElements()) { String key = (String)en.nextElement(); System.out.println(key + " : " + map.get(key)); } } static void processLine(String line, Map map) { StringTokenizer st = new StringTokenizer(line); while (st.hasMoreTokens()) { //map este cheia

19

//iar st.nextToken() este valoare addWord(map, st.nextToken()); } } static void addWord(Map map, String word) { Object obj = map.get(word); if (obj == null) { map.put(word, ONE); } else { int i = ((Integer)obj).intValue() + 1; map.put(word, new Integer(i)); } } }

Ideea acestui program este ca in map, variabil de tip HashTable s inem o pereche de chei valori, unde cheia reprezint cuvntul citit din fiier, iar valoarea reprezint numrul de apariii al acelui cuvnt. Metoda processLine() mparte linia citit n cuvinte separate prin spaiu, i apeleaz metoda addWord. Aici se verific dac mai avem nregistrat cuvntul n colecie, dac nu adugam perechea (cuvnt nou, 1) semn c apare o dat. Altfel, dac a mai fost nregistrat, incrementm valoarea corespunztoare cheii, cu unu. Pentru un fiier simplu ca mai jos:
eee fff eeds fff aaa eeds

aaa

rezultatul este:
fff : 2 eeds : 2 eee : 1 aaa : 2

Clasa Properties Clasa Properties reprezint un HashTable specializat. n aceast clas att cheile ct i valorile sunt String. Atunci cnd lucrm i cu HashTable i cu Properties, va trebui s inem cont de ierarhie: un Properties poate fi un HashTable, ns un HashTable nu poate funciona ca un Properties.

20

Mai jos avem metodele specifice acestei clase: Nume Properties getProperty() list() load () propertyNames() setProperty() store() Descriere constructorul clasei returneaz o valoare pentru o cheie din list listeaz proprietile li valorile aferente ncarc proprietile dintr-un stream returneaz o colecie de chei din list seteaz o pereche cheie-valoare n list salveaz lista de proprieti ntr-un stream

Setarea i preluarea elmentelor Pentru aceasta exist trei metode i anume:


public Object setProperty(String key, String value) public String getProperty(String key) public String getProperty(String key, String defaultValue)

prima metod va modifica cheia key cu valoarea value. Metoda getProperty returneaz valoarea de la cheia key. A doua metod de getProperty va returna valoarea de la cheia key, iar dac aceasta este null, va returna valoarea defaultValue. Pe lng aceste metode, exist o funcie ce returneaz o list cu cheile din Properties:
public Enumeration propertyNames()

ncrcarea i salvarea datelor Aceste dou operaii sunt foarte importante deoarece simplific exportarea i importarea structurilor de date de acest tip:
void load(InputStream inStream) throws IOException void store(OutputStream out, String header) throws IOException

Iat de exemplu, cum se poate salva n fi ier toate datele unei astfel de colecii:
import java.io.*; import java.util.*; public class Proprietati { public static void main(String args[]) throws IOException { Properties prop = new Properties(); FileOutputStream fo = new FileOutputStream(args[0]); prop.setProperty("cheia1", "valoarea1");

21

prop.setProperty("cheia2", "valoarea2"); prop.setProperty("cheia3", "valoarea3"); prop.setProperty("cheia1", "valoarea"); prop.save(fo, "Aici vin comentarii"); } }

Ceea ce se salveaz n fiier este:


#Aici vin comentarii #Fri Nov 20 19:53:44 EET 2009 cheia3=valoarea3 cheia2=valoarea2 cheia1=valoarea

Metoda load() va ncerca s gseasc n fiierul surs, o asemenea structur, de aceea este bine s respectm formatul, dei metoda va putea s ncarce diferite formate, ns nu de fiecare dat cu rezultatul ateptat.

Map
Interfaa Map aparine framework-ului Collections i vine s nlocuiasc clasa Dictionary. n timp ce Dictionary este o clas abstract, era imperativ ca metodele ei s se afle ntr-o interfa. Aceasta este Map, i suport lucrul cu perechi cheie-valoare. Dei face parte din FrameWork, interfaa Map nu extinde Collection, ci este rdcina unei noi ierarhii. Sunt patru clase ce implementeaz aceast interfa: HashMap, WeakHashMap, TreeMap i HashTable. Toate elementele din aceste colecii sunt de tip Map.Entry. Figura de mai jos reprezint ierarhia acestor clase i interfee.

Figura 7.5 Ierarhia claselor Map

22

Interfaa Map.Entry Elementele unei Map sunt de tip Map.Entry, o interfa coninut n Map. Fiecare pereche de cheie-valoare este o instan a acestei interfa. Nu vom crea propriu-zis instane, dar clasele concrete vor returna obiecte ce deja au implementat aceast interfa. Mai jos avem funciile acestei interfee: Metoda equals() getKey() getValue() hashCode() setValue() Descriere verific egalitatea cu un alt obiect returneaz cheia din Map.Entry returneaz valoarea din Map.Entry calculeaz codul hash pentru obiectul actual schimb valoarea din Map.Entry

Iat cum se pot apela aceste metode pentru a afi a entitile dintr-o colecie de tip Properties:
Properties props = System.getProperties(); Iterator iter = props.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry)iter.next(); System.out.println(entry.getKey() + " -- " + entry.getValue()); }

Acelai cod poate fi scris cu vechile metode din Properties i Enumeration astfel:
Properties props = System.getProperties(); Enumeration en = props.propertyNames(); while (en.hasMoreElements()) { String key = (String)en.nextElement(); System.out.println(key + " -- " + props.getProperty(key)); }

Clasa HashMap

Clasa HashMap este folosit la implementarea interfeei Map i anume o colecie de perechivalori n care elementele nu sunt ordonate. Mai jos sunt metodele implementate: Nume metod containsKey() containsValue() entrySet() Descriere Verific existena unei chei in hash map Verific existena unei valori in hash map Returneaz o colecie de perechi sub forma unui map 23

get() isEmpty() keySet() put() putAll() remove() size() values() Creearea unui HashMap

Returneaz valoarea pentru o anumit cheie Verific dac o colecie este goal sau nu Returneaz toate cheile din hash map Plaseaz o pereche n hash map Introduce in hash map o colecie de perechi cheie-valoare Scoate din colecie o pereche Returneaz numrul de elemente dintr-un hash map Returneaz o colecie de valori din hash map

Exist patru constructori pentru crearea unui HashMap:


public public public public HashMap() HashMap(int initialCapacity) HashMap(int initialCapacity, float loadFactor) HashMap(Map map)

n timp ce primii trei vor iniializa o colecie goal, al patrulea va crea hash map-ul pe baza elementelor din parametru. Adugarea n HashMap Pentru a aduga n hash map avem dou metode:
public Object put(Object key, Object value) public void putAll(Map map)

Spre deosebire de HashTable, att cheia ct i valoarea unui element nou introdus pot fi null. Pentru a copia o colecie de perechi de la un Map la altul se folosete a doua metod. Afiarea elementelor unui HashMap se face natural, deoarece suprascrie metoda toString(). tergerea unui element Pentru a terge dintr-un HashMap, avem metoda:
public Object remove(Object key)

Dac cheia se afl n HashMap, perechea va fi tears, i valoarea obiectului va fi returnat. Dac obiectul nu se afl n colecie atunci valoarea null, va fi returnat. Atenie, i null poate fi valoare legitim n colecie. De aceea este de dorit s evitm plasarea null ca valoare n colecie. Alt metod de a terge toate elementele din colecie este:
public void clear()

24

Operaii cu HashMap Se pot returna obiectele din colecie, iar pentru aceasta avem o serie de metode:
public Object get(Object key)

Dac cheia nu este gsit n colecie, se returneaz null. Altfel se va returna valoarea de la cheia specificat ca parametru. Aceeai remarc ar fi de fcut, c elementele ntr-un HashMap pot fi null. O alta metod este :
public Set keySet()

i returneaz colecia de chei sub forma unei mul imi. Pentru a prelua aceste chei ca o colecie general avem metoda:
public Collection values()

pentru a returna elementele complete, adic perechea de cheie-valoare, avem la dispozi ie metoda:
public Set entrySet()

Aceasta returneaz o colecie de obiecte de tip Map.Entry. Cutarea elementelor se face prin dou metode:
public boolean containsKey(Object key) public boolean containsValue(Object value)

Prima metod este asemntoare metodei get(), dar n loc de a returna o valoare a obiectului, vom avea o valoare boolean, true dac cheia se afl n colecie sau false n caz contrar. A doua metod, verific existena unei valori specifice n HashMap. Valorile sunt comparate, i aceast comparare este fcut n timp liniar, de aceea este mai bine s folosim prima metod.

Clasa WeakHashMap Aceast clas funcioneaz identic ca i un HashMap, cu o diferen important: dac managerul de memorie Java nu mai are o referin puternic la un obiect, atunci acea pereche va fi tears. Pentru a nelege aceast fraz trebuie s nelegem ce este o referin slab. n general, obiectele care nu conin date direct, ci con in referine la un alt obiect sunt denumite referine. Aceste clase se gsesc n pachetul java.lang.ref, de exemplu public abstract class Reference<T>. Exist patru tipuri de referin: 1. Referine puternice, nu au o clas special 2. Referine soft sunt ca un cache. Cnd memoria este puin GC va elibera arbitrar aceste referine. 25

3. Referine slabe: acestea sunt mai slabe dect cele soft. Dac singura referin a unui obiect sunt doar de tip slab, GC va putea terge obiectul oricnd. 4. Referine fantom. Acestea permite o notificare nainte ca GC s apeleze finalizarea pe obiectul n cauz. Mai jos avem o exemplificare a acestui mecanism i cum se lucreaz cu referine slabe:
import java.util.*; public class Weak { private static Map map; public static void main (String args[]) { map = new WeakHashMap(); map.put(new String("Cheie"), "Valoare"); //o metoda ce se executa pe un alt fir //decat cel curent Runnable runner = new Runnable() { public void run() { while (map.containsKey("Cheie")) { try { //intra in standby 0.5 secunde Thread.sleep(500); } catch (InterruptedException ignored) { } System.out.println("Thread waiting"); //sistemul GC va rula fortat System.gc(); } } }; Thread t = new Thread(runner); System.out.println(map); //incep procedura run t.start(); //firul principal sta si asteapta ca System.out.println("Main waiting"); try { //cel nou sa isi incheie executia t.join(); //dupa ce thread-ul t nu mai este System.out.println(map);

26

} catch (InterruptedException ignored) { } } }

Exemplul poate fi greu de neles acum pentru c nu am prezentat nc firele de execuie (Thread). Totui concluzia este c dup apelarea Garbage Collector-ului, se pierd legturile slabe, deci se pierd elementele din map. Acesta este rezultatul rul rii programului:
{Cheie=Valoare} Main waiting Thread waiting {}

Clasa TreeMap Ultima implementare a interfeei Map este un TreeMap. Un TreeMap este un Map care menine cheile ntr-o ordine prin intermediul unui arbore balansat, de tip rou-negru. Pe lng metodele expuse de Map i discutate mai sus avem urmtoarele: Metoda firstKey() headMap() lastKey() subMap() tailMap() Descriere Returneaz prima cheie din Map Returneaz sub map-ul de la nceputul map-ului original Returneaz ultima cheie din colecie Returneaz un subarbore oarecare din colecie Returneaz subarborele de la sfritul celui original

Acestea provin tocmai din faptul c TreeMap implementeaz interfaa SortedMap, lucru ce impune existena acestor metode. Crearea unui TreeMap Exist patru constructori pentru un TreeMap. Primul este fr argument creeaz un Map gol, al doilea este un constructor de copiere:
public TreeMap() public TreeMap(Map map)

Pe lng acetia, mai exist doi constructori: unul accept un Comparator pentru a defini un mod de ordonare personalizat i al doilea accept un SortedMap, fiind un constructor de copiere optimizat.
public TreeMap(Comparator comp) public TreeMap(SortedMap map)

27

Operaiuni cu Map Pentru a vizualiza elementele unui arbore avem la dispoziie metodele de mai jos:
SortedSet headMap(Object toKey) SortedSet tailMap(Object fromKey) SortedSet subMap(Object fromKey, Object toKey)

Pentru a specifica subarborele, pentru headMap avem parametrul toKey ce indic subarborele de la nceput pn la acea cheie, iar pentru tailMap avem parametrul fromKey ce indic subarborele de la fromKey pn la sfrit. Pentru a include i elementele de la capt avem urmtorul truc:
Map headMap = map.headMap(toKey+"\0");

n cazul celei de-a treia metod i anume subMap, se va returna un arbore cuprins ntre:
fromKey <= map keys < toKey

Metodele firstKey() i lastKey() permit accesarea rapid a primului, respectiv ultimului element din map:
Object firstKey() Object lastKey()

Mai jos avem un exemplu pentru ilustrarea acestor operaiuni:


import java.util.*; class MyComparator implements Comparator<String> { public int compare(String o1, String o2) { //sa comparam perechile return o2.compareTo(o1); } } public class Studenti { public static void main (String[ ] args) { Map<String, Double> students = new TreeMap<String, Double>(new MyComparator()); students.put ("Sebastian",6.0); students.put ("Bogdan", 7.8); students.put ("Andrei", 4.67);

28

students.put ("Remus", 8.96); students.put ("Catalin", 8.55); System.out.println (students); Map<String, Double> group = ((TreeMap<String, Double>)students).subMap("Bogdan", System.out.println (group); System.out.println System.out.println System.out.println System.out.println System.out.println (students.remove ("Andrei")); (students.remove ("Boggdan")); (students.containsKey ("Bogdan")); (students.containsKey ("Andrei")); (students.containsValue (7.8));

"Remus");

System.out.println (students); } }

n cazul acestui exemplu, instruciunea :


Map<String, Double> group = ((TreeMap<String, Double>)students).subMap("Bogdan", "Remus");

Ar produce o eroare de tipul:


Exception in thread "main" java.lang.IllegalArgumentException: fromKey > toKey

Aceasta are loc tocmai din modul n care este construit acest arbore, si anume pe baza unui comparator, MyComparator, care va inversa ordinea fireasc a cheilor. De aceea, va trebui s considerm care este rdcina arborelui i s alegem subarborele corect:
Map<String, Double> group = ((TreeMap<String, Double>)students).subMap("Remus", "Bogdan");

n cazul acesta va fi format dintr-un singur element, cel marcat cu font italic: {Sebastian=6.0, Remus=8.96, Catalin=8.55, Bogdan=7.8, Andrei=4.67}
{Catalin=8.55} 4.67 null true false true

29

{Sebastian=6.0, Remus=8.96, Catalin=8.55, Bogdan=7.8}

Pentru a include i capetele intervalului va trebui s folosim acel truc descris mai sus.

Clasa Collections
Clasa Collections este o clasa a FrameWork-ului ce const din metode statice i obiecte pentru a nlesni lucrul cu colecii. Clasa Arrays este clasa corespondent, ce lucreaz cu iruri. Toi membrii acestei clase sunt descri i mai jos. Metoda EMPTY_LIST EMPTY_MAP EMPTY_SET binary_search() copy() enumeration() fill() max() min() nCopies() reverse() reverseOrder() shuffle() singleton() singletonList() singletonMap() sort() Descrierea Reprezint o list goal imutabil Reprezint o colecie Map goal imutabil Reprezint o mulime goal imutabil caut un element n list folosind tehnica de cutare binar copiaz elementele dintre dou liste convertete o list la un Enumeration umple lista cu un element anume caut maximul dintr-o colecie caut minimul dintr-o colecie creeaz o list imutabil cu multiple copii ale unui element inverseaz elementele ntr-o list returneaz un comparator ce inverseaz ordinea elementelor reordoneaz elementele aleatoriu returneaz un set imutabil de un element returneaz o list imutabil de un element returneaz un Map imutabil de un element reordoneaz elementele n list

Pe lng acestea, mai sunt o serie de metode ce creeaz colecii thread-safe, ns nu vom discuta despre aceasta acum. n continuare vom prezenta cteva din metodele de mai sus, pe cele mai importante. Sortarea Metoda sort() permite sortarea elementelor unei liste:
public static void sort(List list) public static void sort(List list, Comparator comp)

30

Iat un exemplu pentru folosirea acestei metode:


import java.util.*; public class Colectii { public static void main(String args[]) throws Exception { List list = Arrays.asList(args); Collections.sort(list); for (int i=0, n=list.size(); i<n; i++) { if (i != 0) System.out.print(", "); System.out.print(list.get(i)); } System.out.println(); } }

Lista obinut din irul argumentelor este cel mai probabil nesortat. Prin apelarea acestei metode vom ordona elementele listei. O alt metod ar fi de a folosi ordinea invers i anume:
Collections.sort(list, Collections.reverseOrder());

Cutarea Cutarea unui element este cea binar, i este implementat prin cele dou metode:
public static int binarySearch(List list, Object key) public static int binarySearch(List list, Object key, Comparator comp)

Cutarea binar const n mprirea unui sir sortat n dou i compararea elementului de cutat cu mijlocul irului. Dac nu gsim elementul, cutm mai departe. Cum? Dac elementul din mijloc este mai mic dect numrul de cutat, vom cuta n dreapta mijlocului, dac nu vom cuta n stnga. Adic, vom cuta fie in subirul delimitat de 0 i mijloc adic stnga, fie n subirul delimitat de mijloc si sir.lentgh adic dreapta. Aplicm recursiv aceasta metod pn cnd elementul cutat este egal cu mijlocul sau nu mai avem unde cuta pentru ca subirul a devenit gol. Iat mai jos un exemplu pentru aceast cutare: n irul 1 3 4 6 8 9 11 14 15 17 19 20 23 25 vom cuta elementul 4. irul are 14 elemente vom cuta mijlocul si anume 13/2 = 7. Elementul de pe pozi ia 7 este14, comparm cu 4 este mai mare deci vom cuta n stnga: 1 3 4 6 8 9 11 14. Aplicm acelai algoritm pe un subir de 8 elemente i mijlocul va fi la 7/2 = 3. Comparm elementul de pe poziia 3 si anume 6 cu 4, este mai mare. Aplicam aceea i strategie pe subirul 1 3 4 6. Avem 4 elemente / 2 = 2 i gsim mijlocul egal cu elementul de cutat. Se ncheie algoritmul. n funciile de mai sus key este elementul de cutat iar list este irul nostru. Mai jos avem i un exemplu de cutare si sortare folosind Collections. 31

"]");

import java.util.*; public class Colectii { public static void main(String args[]) throws Exception { String nume[] = {"George", "Victor", "Laura", "Raluca", "Costel", "Maria", "Dorel"}; // Convertim la colectii List list = new ArrayList(Arrays.asList(nume)); // sortam lista Collections.sort(list); System.out.println("Sorted list: [length: " + list.size() + System.out.println(list); // caut un element int index = Collections.binarySearch(list, "Victor"); System.out.println("Found Victor @ " + index); // Search for element not in list index = Collections.binarySearch(list, "fals"); System.out.println("Didn't find fals @ " + index); // Insert int newIndex = -index -1; list.add(newIndex, "false"); System.out.println(list); } }

Iat rezultatul: Sorted list: [length: 7] [Costel, Dorel, George, Laura, Maria, Raluca, Victor] Found Victor @ 6 Didn't find fals @ -8 [Costel, Dorel, George, Laura, Maria, Raluca, Victor, false]

32

Curs 8
Bazele multithreading.......................................................................................................................... 2 Clasa Thread i interfaa Runnable....................................................................................................... 4 Crearea unui fir de execuie ................................................................................................................. 4 Interfaa Runnable ........................................................................................................................... 4 mbuntiri aduse exemplului ........................................................................................................ 7 Motenirea unui Thread .................................................................................................................. 9 Crearea mai multor fire de execuie................................................................................................... 11 Cnd se ncheie un fir de execuie? ................................................................................................ 14 Prioriti n fire de execuie ............................................................................................................... 16 Sincronizarea ..................................................................................................................................... 18 Folosirea metodelor sincronizate ................................................................................................... 19 Blocul synchronized ....................................................................................................................... 21 Comunicarea ntre fire de execuie .................................................................................................... 22 Suspendarea, reluarea i oprirea firelor de execuie .......................................................................... 26 Grupuri de fire de execuie ................................................................................................................ 29 Alte metode................................................................................................................................... 30

Bazele multithreading
Exist doua tipuri distincte de multitasking: unul bazat pe procese i al doilea bazat pe fire de execuie sau thread-uri. Este important s facem distincia dintre cele dou. Un proces este, n esen, un program ce se afl n execuie. De aceea multitasking bazat pe procese nseamn faptul c dou sau mai multe programe ruleaz n acelai timp. De exemplu, compilatorul Java va permite crearea unor noi programe, n timp ce folosii un editor de text, sau navigai pe Internet. n cazul multitasking bazat pe thread-uri, acesta este cea mai mic unitate ce va fi programat de dispecer. Aceasta nseamn c un program poate realiza mai multe lucruri simultan. De exemplu aplicaia de mail Outlook, sau un client de mail va permite si trimiterea/primirea de mail-uri in timp ce compunei un nou mail, sau verificai lista de prioriti dintr-o zi, sau cutai un anumit mail, etc. Avantajul principal al multithreading este c permite scrierea unor programe foarte eficiente, deoarece se elimin spaiile inutile temporale prezente n multe programe, atunci cnd acesta este idle. Majoritatea dispozitivelor I/O sunt mai lente dect CPU. De aceea programul va petrece majoritatea execuiei ateptnd informaia de la aceste dispozitive. n acest timp, n cazul folosirii multithreading, se pot executa instruciuni care s realizeze alte sarcini. De exemplu, n timp ce un program trimite un fi ier pe Internet, alt parte a programului poate citi de la tastatura, urm toarea bucat ce va fi trimis. Un fir de execuie este o parte dintr-un proces executat secvenial, o serie de instruciuni ce vor fi executate secvenial. Folosirea a dou sau mai multe thread-uri duce la execuia n paralel a acestor serii de instruciuni n cadrul unui proces. Mai jos avem reprezentat modul de funcionare al unui proces pe un singur thread.

Figura 1. Un proces cu un singur fir de execuie Adevrata utilitate a unui thread intervine n cazul n care mai multe fire de execuie coexist n acelai proces.

Figura 2. Un proces cu dou fire de execuie

Un fir de execuie se poate afla n mai multe stri. Poate fi running, adic n execuie. Poate fi ready to run, n clipa n care dispecerul i va asigura timpul n CPU. Un thread aflat n execuie poate fi suspendat, ceea ce nseamn c este oprit pentru o perioad. Poate fi reluat, mai trziu, adic va intra n starea de resumed. Un thread poate fi blocat n momentul cnd ateapt o resurs. Un thread poate fi terminat atunci cnd execuia se ncheie i nu va mai fi reluat. Figura de mai jos reprezint strile unui fir de execuie.

Figura 3. Strile de execuie ale unui thread Pe lng aceste caracteristici, mai este nevoie de o anumit funcionalitate, numit sincronizare, ce permite execuia firelor ntr-o manier controlat. Fa de modul n care limbajul C trateaz aceast problem a multithreading-ului , Java ascunde unele detalii , tocmai pentru a nlesni i a face ct mai convenient lucrul cu thread-uri.

Clasa Thread i interfaa Runnable


n Java, sistemul multithreading este construit pe clasa Thread i interfaa Runnable. Pentru a crea un nou fir de execuie, va trebui s extindem clasa Thread sau s implementm interfaa Runnable. Clasa Thread definete cteva metode care ajut la tratarea situaiilor ce intervin n lucrul cu thread-uri. Iat cele mai folosite: Medoda final String getName( ) final int getPriority( ) final boolean isAlive( ) final void join( ) void run( ) static void sleep(long milliseconds) void start( ) Descriere Se obine numele thread-ului Se obine prioritatea thread-ului Determin faptul c un thread mai este n execuie sau nu. Ateapt terminarea unui thread pentru a continua. Entry point pentru un thread. Suspend un thread un numr de milisecunde ncepe execuia unui fir prin apel run()

Toate procesele au cel puin un fir de execuie ce se numete firul principal sau main thread. Acesta se va executa nainte celorlalte eventual fire de execuie ce vor fi create ulterior.

Crearea unui fir de execuie


Un fir de execuie se poate crea prin instanierea unui obiect de tip Thread. Clasa Thread ncapsuleaz un obiect ce implementeaz interfaa Runnable. Exist dou moduri de a crea un fir: 1. Implementarea interfeei Runnable 2. Extinderea (motenirea) clasei Thread Interfaa Runnable Aceast interfa definete doar o metod numit run() declarat astfel:
public void run()

n cadrul acestei metode, vei defini codul ce constituie noul fir de execuie. Metoda run() poate apela alte metode, folosi alte clase, i declara variabile ca orice metod. Diferena major este c run() reprezint entry point pentru un nou thread din cadrul procesului. Acest thread i ncheie execuia atunci cnd se iese din funcia run (cu return). Modul de lucru este urmtorul: 1. Se creeaz o clas ce implementeaz interfaa Runnable. 2. Se instaniaz un obiect de tip Thread, ntr-una din metodele din acea clas ( chiar si in constructorul clasei ). Constructorul unui Thread va fi:

Thread(Runnable threadOb)

3. Odat creat, noul obiect de tip Thread, nu va porni pn cnd nu apelm metoda start(). n principiu apelul metodei start() nseamn apelul metodei run(). Apelul metodei start este:
void start()

Mai jos avem un exemplu de folosirea a unui nou fir de execute:


// crearea unui fir de execute //prin implementarea interfeei Runnable class MyThread implements Runnable { int count; String threadName; MyThread(String name) { count = 0; threadName = name; } // Entry point al firului public void run() { System.out.println(threadName + " starting."); try { do { //suspend thread-ul curent Thread.sleep(500); System.out.println("In " + threadName + ", count is " + count); count++; } while(count < 10); } catch(InterruptedException exc) { System.out.println(threadName + " interrupted."); } System.out.println(threadName + " terminating."); } } class DemoThread { public static void main(String args[]) { System.out.println("Main thread starting."); // mai nti se construiete obiectul // ce va conine thread-ul MyThread mt = new MyThread("Child #1"); // se construiete un fir pe baza obiectului Thread newThrd = new Thread(mt); // incepe noul fir de execute newThrd.start(); do

} }

System.out.print("."); try { Thread.sleep(100); } catch(InterruptedException exc) { System.out.println("Main thread interrupted."); } } while (mt.count != 10); System.out.println("Main thread ending.");

Prima dat, clasa MyThread implementeaz Runnable. Aceasta nseamn c un obiect de tipul MyThread va fi folosit pentru a crea un thread i deci, va fi parametrul unui constructor Thread. n interiorul metodei run(), exist o bucl while, ce contorizeaz de la 0 la 9. Metoda Thread.sleep(500); are ca scop, suspendarea firului de execuie curent timp de 500 de milisecunde. n clasa DemoThread, n metoda main(), se creeaz un nou obiect, de tip MyThread care va fi apelat ulterior de newThrd:
MyThread mt = new MyThread("Child #1"); Thread newThrd = new Thread(mt); // incepe noul fir de execute newThrd.start();

Obiectul mt este folosit pentru a crea un Thread, iar acest lucru este posibil pentru c MyThread implementeaz Runnable . Execuia unui thread ncepe cu start. Pe firul principal de execuie, instruciunile vor fi rulate ca i cnd start() ar fi o metod obinuit. Firul principal de execuie va rula o bucl while n care ateapt ca mt.count s ajung la 10. Acest lucru se va ntmpla, datorit faptului c n thread-ul secundar count crete. Iat rezultatul rulrii acestui program:
Main thread starting. .Child #1 starting. ....In Child #1, count is 0 .....In Child #1, count is 1 .....In Child #1, count is 2 .....In Child #1, count is 3 .....In Child #1, count is 4 .....In Child #1, count is 5 .....In Child #1, count is 6 .....In Child #1, count is 7 .....In Child #1, count is 8 .....In Child #1, count is 9 Child #1 terminating. Main thread ending.

O diagrama a acestui program ar fi urmtoarea:

Figura 4. Diagrama execuiei unui fir

mbuntiri aduse exemplului Programul anterior este valid, ns poate fi optimizat: 1. Putem avea un thread care s nceap execuia la instanierea obiectului. n cazul MyThread aceasta se face instaniind un obiect de tip Thread n constructorul lui MyThread. 2. Nu mai este nevoie ca MyThread s aib un obiect de tip Thread cu un nume, deoarece modificm numele la instanierea noului Thread. Pentru aceasta avem urmtorul constructor:
Thread(Runnable threadOb, String name)

Se poate ob ine ulterior, numele Thread-ului prin apelul metodei getName:


final String getName( )

Setarea numelui unui thread, dup ce acesta a fost creat, se face cu setName():
final void setName(String threadName)

Iat mai jos o versiune a programului anterior, cu mbuntirile specificate:


// crearea unui fir de execute //prin implementarea interfeei Runnable class MyThread implements Runnable { int count; //firul de execute cu care vom lucra Thread thread; MyThread(String name) { count = 0; //instaniem un nor fir thread = new Thread(this, name); //firul va porni la apelul constructorului //adic la instanierea lui MyThread thread.start(); } // Entry point al firului public void run() { System.out.println(thread.getName() + " starting."); try { do { //suspend thread-ul curent Thread.sleep(500); System.out.println("In " + thread.getName() + ", count is " + count); count++; } while(count < 10); } catch(InterruptedException exc) { System.out.println(thread.getName() + " interrupted."); } System.out.println(thread.getName() + " terminating."); } } class DemoThread { public static void main(String args[]) { System.out.println("Main thread starting."); //acum thread-ul nou va porni automat //la instanierea lui mt MyThread mt = new MyThread("Child #1");

do { System.out.print("."); try { Thread.sleep(100); } catch(InterruptedException exc) { System.out.println("Main thread interrupted."); } } while (mt.count != 10); System.out.println("Main thread ending."); } }

Modificrile majore sunt: mutarea obiectului de tip Thread n cadrul clasei MyThread i pornirea acestui fir din constructorul clasei MyThread. Motenirea unui Thread

Implementarea interfeei Runnable este un mod de a crea o clas ce poate instania fire. A doua ar fi extinderea unei clase numit Thread. Atunci cnd o clas extinde Thread, va trebui s suprascrie metoda run(), care este un entry point pentru noul fir. De asemenea trebuie s apeleze start() pentru ca noul fir s i nceap execuia. Pornind de la exemplul anterior, putem transforma acesta aplicnd motenirea lui Thread: 1. Se schimba declaraia lui MyThread ca s extind Thread:
class MyThread extends Thread { .

2. Se terge aceast declaraie, nu mai avem nevoie de ea:


Thread thread;

Variabila thread nu mai are nici o utilitate, din moment ce MyThread este un Thread. 3. Se schimb constructorul lui MyThread ca s arate astfel:
// construirea unui fir de execuie. MyThread(String name) {

super(name); // apelez constructor cu nume count = 0; start(); // ncep execuia }

Apelul lui super(name); este n fapt, apelul unui Thread cu parametru un String:
Thread(String name);

4. Se schimb run() astfel nct acum apeleaz getName(). Dup aceste modificri programul arat astfel:
class MyThread extends Thread { int count; //firul de execuie cu care vom lucra Thread thread; MyThread(String name) { super(name); count = 0; this.start(); } // Entry point al firului public void run() { System.out.println(getName() + " starting."); try { do { //suspend thread-ul curent Thread.sleep(500); System.out.println("In " + getName() + ", count is " + count); count++; } while(count < 10); } catch(InterruptedException exc) { System.out.println(getName() + " interrupted."); } System.out.println(getName() + " terminating."); } }

Clasa DemoThread nu sufer nici un fel de modificri fa de programul precedent.

Crearea mai multor fire de execuie


Exemplele anterioare conineau doar un singur fir de execuie. Totui, programul poate crea oricte astfel de fire. Urmtorul program creeaz trei thread-uri:
class MyThread implements Runnable { int count; Thread thread; // Constructorul ce creeaz un nou thread MyThread(String name) { thread = new Thread(this, name); count = 0; thread.start(); // incepe execuia } //entry point in firul de execuie public void run() { System.out.println(thread.getName() + " starting."); try { // se mrete contorul do { Thread.sleep(500); System.out.println("In " + thread.getName() + ", count is " + count); count++; } while(count < 10); } catch(InterruptedException exc) { System.out.println(thread.getName() + " interrupted."); } System.out.println(thread.getName() + " terminating."); } } class ThreeChildThreads { public static void main(String args[]) { System.out.println("Main thread starting."); //cream trei fire de execuie MyThread mt1 = new MyThread("Child #1"); MyThread mt2 = new MyThread("Child #2"); MyThread mt3 = new MyThread("Child #3"); //care vor fi automat lansate //in bucla while, thread-ul principal

//ateapt ca toate firele sa se ncheie do { System.out.print("."); try { Thread.sleep(100); } catch(InterruptedException exc) { System.out.println("Main thread interrupted."); } } while (mt1.count < 10 || mt2.count < 10 || mt3.count < 10); System.out.println("Main thread ending."); } }

Cele trei fire de execuie vor rula n paralel cu firul principal, acesta ateptnd ca cele trei sa i ncheie execuia. O diagram a acestei funcionaliti este n figura de mai jos:

Figura 5. Diagrama execuiei pe mai multe fire

Rezultatul rulrii acestui program este:


Main thread starting. Child #1 starting. .Child #2 starting. Child #3 starting. ....In Child #1, count is 0 In Child #2, count is 0 In Child #3, count is 0 .....In Child #1, count is 1 In Child #3, count is 1 In Child #2, count is 1 .....In Child #1, count is 2 In Child #2, count is 2 In Child #3, count is 2 .....In Child #1, count is 3 In Child #2, count is 3 In Child #3, count is 3 .....In Child #1, count is 4 In Child #2, count is 4 In Child #3, count is 4 .....In Child #1, count is 5 In Child #2, count is 5 In Child #3, count is 5 .....In Child #1, count is 6 In Child #2, count is 6 In Child #3, count is 6 .....In Child #1, count is 7 In Child #3, count is 7 In Child #2, count is 7 .....In Child #1, count is 8 In Child #3, count is 8 In Child #2, count is 8 .....In Child #1, count is 9 Child #1 terminating. In Child #3, count is 9 Child #3 terminating. In Child #2, count is 9 Child #2 terminating. Main thread ending.

Acest rezultat depinde de sistemul pe care este rulat i poate diferi. ntrebarea fireasc este de ce avem nevoie de dou moduri de a crea fire de execuie ( unul prin extinderea Thread i altul prin implementarea Runnable ) i care este mai performant? Rspunsul este c o clas Thread definete o serie de metode ce pot fi suprascrise de clasa ce deriv din Thread. Singura care se impune a fi suprascris este run(). Aceasta este i condiia atunci cnd clasa implementeaz interfaa Runnable. Aa c dac nu se dorete suprascrierea altor metode din Thread, atunci este mai bine s folosim a doua metod, pentru simplitate.

Cnd se ncheie un fir de execuie? Este util s tim cnd un fir de execuie s-a ncheiat, pentru a controla logica i fluxul programului. n exemplele anterioare, acest lucru a fost posibil datorit unei variabile de control i anume count. Aceasta este o soluie slab din punct de vedere tehnic. Clasa Thread ofer dou moduri de a determina faptul c un fir de execuie s-a ncheiat. Primul este prin funcia isAllive():
final boolean isAllive()

Aceast metod returneaz true, dac firul de execuie, pentru care a fost apelat, nc ruleaz, i false altfel. Pentru a verifica funcionalitatea isAlive(), vom modifica versiunea clasei ThreeChildThreads.
class ThreeChildThreads { public static void main(String args[]) { System.out.println("Main thread starting."); //cream trei fire de execuie MyThread mt1 = new MyThread("Child #1"); MyThread mt2 = new MyThread("Child #2"); MyThread mt3 = new MyThread("Child #3"); //care vor fi automat lansate //in bucla while, thread-ul principal //ateapt ca toate firele sa se ncheie do { System.out.print("."); try { Thread.sleep(100); } catch(InterruptedException exc) { System.out.println("Main thread interrupted."); } } while (mt1.thread.isAlive() || mt2.thread.isAlive() || mt3.thread.isAlive() ); System.out.println("Main thread ending."); } }

Dup cum se poate observa, acest program este la fel ca i cel anterior, cu o modificare: condi ia din while se bazeaz pe aceast funcie, oferind robustee aplicaiei. Cel de-al doilea mod de a atepta ca un fir s i ncheie execuia este join():
final void join() throws IntrerruptedException

Aceast metod va impune ateptarea terminarea firului pentru care a fost apelat. Numele provine de la un concept de crearea a unui thread, lansarea a acestuia i ateptarea terminrii lui pentru ca cel care a creat firul s se uneasc cu acesta. Exist forme adiionale ale acestei funcii, prin care se permite specificarea unui numr de milisecunde, sau nanosecunde, timp de ateptare ca acel fir s se ncheie. Mai jos este exemplul pentru aceast funcie:
class ThreeChildThreads { public static void main(String args[]) { System.out.println("Main thread starting."); //cream trei fire de execuie MyThread mt1 = new MyThread("Child #1"); MyThread mt2 = new MyThread("Child #2"); MyThread mt3 = new MyThread("Child #3"); //care vor fi automat lansate //in bucla while, thread-ul principal //ateapt ca toate firele sa se ncheie try { mt1.thread.join(); System.out.println("Child 1 joined."); mt2.thread.join(); System.out.println("Child 2 joined."); mt3.thread.join(); System.out.println("Child 3 joined."); } catch(InterruptedException exc) { System.out.println("Main thread interrupted."); } System.out.println("Main thread ending."); } }

Ateptarea ca un fir de execuie s se ncheie are loc prin apelurile de tipul:


mt1.thread.join();

Prioriti n fire de execuie


Fiecare fir de execuie are asociat o anumit prioritate. Aceasta este determinat de ct timp n CPU este alocat acelui thread. n general prioritile joase denot puin timp, iar cele mari denot mai mult timp n CPU. Evident, ct de mult timp va avea acces la CPU, un fir de execuie, influeneaz direct, fluxul logic al programului. Este important s nelegem factorii care influeneaz accesul la CPU. De exemplu, dac un fir cu prioritate mare ateapt o resurs auxiliar, atunci el va fi blocat, i alt fir cu o prioritate mai mic va avea acces. Totui odat ce thread-ul cu prioritatea mai mare are resursa, va opri execu ia firului cu prioritate mai mic pentru ai relua propria execuie. Alt factor care poate afecta dispecerul de fire de execuie, este modul n care sistemul de operare implementeaz multitasking. De aceea, doar prin asignarea de prioriti mai mici, mai mari, nu va influena neaprat rapiditatea cu care un fir ruleaz. Prioritatea nseamn un acces probabil mai mic sau mai mare la CPU. Atunci cnd un fir de execuie pornete, prioritatea sa este egal cu cea a thread-ului printe. Valoarea nivelului de prioritate trebuie s fie ntre MIN_PRIORITY i MAX_PRIORITY. n mod normal, aceste valori sunt 1 i 10. Pentru a returna un fir cu prioritate implicit, avem NORM_PRIORITY ce nseamn 5. Aceste valori sunt constante n clasa Thread. Se poate obine prioritatea actual a unui fior de execuie apelnd metoda getPriority():
final int getPriority()

Exemplul urmtor demonstreaz folosirea a dou fire de prioritate diferit. Metoda run() conine o bulc while care contorizeaz iteraii. Aceast bulc se oprete cnd numrul de iteraii depete 1000000 sau variabila stop a fost setat pe true. Iniial stop este setat pe false, dar primul thread care termin de iterat, o va seta pe true. Evident aceasta va face ca i al doilea thread s ncheie bucla. La fiecare parcurgere a buclei, variabila string, currentName, este comparat cu firul aflat n execuie. Dac este diferit, se realizeaz modificarea acestei variabile. Aceasta permite vizualizarea accesrii CPU a unui anumit thread.
class Priority implements Runnable { int count; Thread thread; static boolean stop = false; static String currentName; //Constructorul unui nou fir //acesta nu pornete thread-ul Priority(String name) { thread = new Thread(this, name); count = 0; currentName = name; } // incepe execuia unui nou fir public void run() { System.out.println(thread.getName() + " starting.");

do { count++; if(currentName.compareTo(thread.getName()) != 0) { currentName = thread.getName(); System.out.println("In " + currentName); } } while(stop == false && count < 10000000); stop = true; System.out.println("\n" + thread.getName() + " terminating."); } } class PriorityDemo { public static void main(String args[]) { Priority mt1 = new Priority("High Priority"); Priority mt2 = new Priority("Low Priority"); // mt1 primete o prioritate mai mare dect mt2 mt1.thread.setPriority(Thread.NORM_PRIORITY+2); mt2.thread.setPriority(Thread.NORM_PRIORITY-2); //pornesc ambele tread-uri mt1.thread.start(); mt2.thread.start(); //ateptarea terminrii ambelor thread-uri try { mt1.thread.join(); mt2.thread.join(); } catch(InterruptedException exc) { System.out.println("Main thread interrupted."); } System.out.println("\nHigh priority thread counted to " + mt1.count); System.out.println("Low priority thread counted to " + mt2.count); } }

Efectul rulrii acestui program este:


High Priority starting. In High Priority Low Priority starting. In Low Priority In High Priority In Low Priority In Low Priority In High Priority In High Priority In High Priority In Low Priority In High Priority In High Priority In Low Priority In High Priority High Priority terminating. Low Priority terminating. High priority thread counted to 10000000 Low priority thread counted to 1194749

n acest mod putem observa c pentru acest exemplu, thread-ul cu prioritate mai mare a avut majoritatea timpului CPU. Acest rezultat va depinde de sistemul de operare i maina pe care va rula.

Sincronizarea
Atunci cnd folosim mai multe fire de execuie, este necesar uneori, s coordonm fluxul logic, acest proces numindu-se sincronizare. Cel mai simplu motiv pentru aceast sincronizare, este ca dou sau mai multe fire s aib acces la o resurs comun, dar doar un singur fir s acceseze la un moment dat acea resurs. De exemplu scrierea ntr-un fiier, efectuat din dou fire de execuie trebuie controlat. Aceasta se realizeaz astfel: un fir este pus n stare de ateptare pn cnd firul care are acces la resurs termin aciunea, urmnd ca firul suspendat s i reia execuia. Sincronizarea n Java este realizat prin intermediul conceptului de monitor, ce controleaz accesul la un obiect. Monitorul funcioneaz implementnd conceptul de blocare. Cnd un obiect este blocat de un fir de execuie, nici un alt fir de execuie nu are acces la acel obiect. Cnd firul de execuie elibereaz acel lock, obiectul devine disponibil pentru celelalte fire de execuie. Toate obiectele n Java au un monitor. Aceast caracteristic este implementat n limbaj. De aceea toate obiectele pot fi sincronizate. Acest lucru se va realiza prin cuvntul cheie synchronized i alte cteva metode pe care toate obiectele le au. Exist dou metode prin care se poate sincroniza fluxul logic al unui program.

Folosirea metodelor sincronizate Se poate sincroniza accesul la o metod folosind cuvntul cheie synchronized. Atunci cnd acea metod este apelat , firul de execuie n care s-a fcut apelul intr n obiectul monitor, care blocheaz obiectul. n timpul blocrii, nici un alt thread nu poate intra n metod, sau accesa orice metod sincronizat. Atunci cnd firul de execuie revine din metod, se realizeaz deblocarea. Urmtorul program demonstreaz folosirea acestei metode de sincronizare:
//clasa ce calculeaz o sum de elemente //si conine metoda sincronizata class SumArray { private int sum; //metoda are specificatorul de sincronizare synchronized int sumArray(int nums[]) { sum = 0; // resetarea sumei for(int i=0; i<nums.length; i++) { sum += nums[i]; System.out.println("Running total for " + Thread.currentThread().getName() + " is " + sum); try { Thread.sleep(15); //timp in care //se poate schimba de la un fir la altul } catch(InterruptedException exc) { System.out.println("Main thread interrupted."); } } return sum; } } //clasa care se ocupa de thread class MyThread implements Runnable { Thread thread; static SumArray sa = new SumArray(); int a[]; int answer; // Constructorul unui nou fir MyThread(String name, int nums[]) { thread = new Thread(this, name); a = nums;

thread.start(); //pornesc thread-ul } public void run()//incepe execuia noului fir { int sum; System.out.println(thread.getName() + " starting."); answer = sa.sumArray(a); System.out.println("Sum for " + thread.getName() + " is " + answer); System.out.println(thread.getName() + " terminating."); } } class SyncDemo { public static void main(String args[]) { int a[] = {1, 2, 3, 4, 5}; MyThread mt1 = new MyThread("Child #1", a); MyThread mt2 = new MyThread("Child #2", a); } }

dou fire de execuie ce au ca parametru acelai ir. Cu alte cuvinte, aceeai metod sa.sumArray(a); va fi apelat n ambele fire, crend o concuren de acces la suma rezultat. Suma nu va fi ns afectat, deoarece firele vor executa succesiv metoda sumArray . Rezultatul este:
Child #1 starting. Running total for Child Child #2 starting. Running total for Child Running total for Child Running total for Child Running total for Child Running total for Child Sum for Child #1 is 15 Child #1 terminating. Running total for Child Running total for Child Running total for Child Running total for Child Sum for Child #2 is 15 Child #2 terminating. #1 is 1 #1 #1 #1 #1 #2 is is is is is 3 6 10 15 1

Acest exemplu este compus din trei clase. Prima i anume SumArray conine metoda sumArray ce realizeaz nsumarea elementelor dintr-un ir. Acest ir este pasat ca parametru al metodei. A doua clas este, MyThread cea care se ocup de firul de execuie propriu zis. Firul de execuie va apela metoda sumArray prin intermediul obiectului sa de tip SumArray. Acesta este declarat static n clasa MyThread. Pe metoda de run() a firului de execuie se va rula aceast metoda de nsumare a elementelor unui ir. n clasa SyncDemo, n metoda main() este creat un ir i anume a, i

#2 #2 #2 #2

is is is is

3 6 10 15

Aceasta nseamn c execuia a avut loc conform ateptrilor i sumele calculate sunt cele prevzute. Dar dac eliminm cuvntul cheie synchronized din cadrul metodei, iat ce obinem:
Child #2 starting. Running total for Child Child #1 starting. Running total for Child Running total for Child Running total for Child Running total for Child Running total for Child Running total for Child Running total for Child Running total for Child Running total for Child Sum for Child #1 is 29 Child #1 terminating. Sum for Child #2 is 29 Child #2 terminating.

#2 is 1 #1 #2 #1 #2 #1 #2 #1 #1 #2 is is is is is is is is is 1 3 5 8 11 15 19 24 29

Evident, rezultatele pot varia n funcie de main, sistem de operare, etc. Blocul synchronized Dei crearea metodelor sincronizate, n cadrul claselor, este un mod eficient i uor de sincronizare, nu funcioneaz pentru orice caz. De exemplu, s presupunem c dorim accesul sincronizat la o metod care nu este declarat ca atare. Acest lucru se poate ntmpla n cazul n care clasa a fost creat de altcineva i nu avem acces la codul surs. Pentru a accesa obiecte care nu au fost anterior sincronizate, avem la dispoziie blocuri sincronizate:
synchronized(object) { //instruciunile ce trebuie sincronizate }

Aici object este referina ctre obiectul de sincronizat. Un bloc sincronizat asigur faptul c apelul unei metode membr a obiectului object, va avea loc ntr-un monitor al firului de execuie apelant. De exemplu putem reedita programul mai sus considernd c metoda a fost declarat fr cuvnt cheie synchronized:
int sumArray(int nums[])

Ceea ce se modific, este n cadrul metodei run() a clasei Thread:


public void run() { synchronized(sa) { int sum; System.out.println(thread.getName() + " starting."); answer = sa.sumArray(a); System.out.println("Sum for " + thread.getName() + " is " + answer); System.out.println(thread.getName() + " terminating."); } }

n acest fel se asigur un calcul predictiv al sumei format din elementele lui sa.

Comunicarea ntre fire de execuie


S considerm urmtoarea situaie. Un fir numit T execut o metod sincronizat i are nevoie de o resurs R, care este temporar indisponibil. Ce ar trebui s fac T? Dac T intr ntr-o bucl de ateptare a lui R, blocheaz obiectul prevenind alte fire s o acceseze. Aceast abordare este consumatoare de timp i poate duce la deadlock-uri adic blocare permanent. O alt soluie ar fi ca T s elibereze temporar controlul obiectului, permind altui fir s ruleze. Cnd R devine disponibil, T poate fi notificat i i poate relua execuia. Acest tip de abordare, presupune existena unei comunicri ntre fire diferite. n Java aceasta se face prin metodele wait(), notify() i notifyAll(). Aceste metode aparin tuturor obiectelor, deoarece sunt implementate n clasa Object. Aceste metode pot fi apelate dintr-o metod sincronizat. Atunci cnd un thread este blocat temporar, apeleaz wait(). Aceasta va face ca firul s intre n sleep, i monitorul pentru acel obiect s fie eliberat, permind altui fir s foloseasc obiectul. Mai trziu, firul ce era oprit, este trezit, atunci cnd un alt fir care va intra n acelai monitor, apeleaz metoda notify(), sau notifyAll(). Un apel la metoda notify() va reporni firul oprit. Iat diversele forme ale metodei wait():
final void wait( ) throws InterruptedException final void wait(long millis) throws InterruptedException final void wait(long millis, int nanos) throws InterruptedException

Ultimele dou forme ale metodei semnific ateptarea pn cnd apare o notificare sau pn cnd o perioada de timp, dat de parametrii, trece. Mai jos avem formele funciilor de notificare:
final void notify( ) final void notifyAll( )

Iat un exemplu de folosire a wait() i notify():


class TicTac { synchronized void tic(boolean running) { if(!running) { //opresc metoda notify(); //anun cellalt thread return; } System.out.print("Tic "); notify(); //il las pe tac sa ruleze try { wait(); // atept pe tac } catch(InterruptedException exc) { System.out.println("Thread interrupted."); } } synchronized void tac(boolean running) { if(!running) { // opresc pe tac notify(); // anun pe tic return; // adic cellalt fir } System.out.println("Tac"); notify(); // il las pe tic sa ruleze try { wait(); // atept ca tic sa ruleze } catch(InterruptedException exc) { System.out.println("Thread interrupted."); } } } class MyThread implements Runnable { Thread thread; TicTac ttOb; // un nou fir de execuie MyThread(String name, TicTac tt) {

thread = new Thread(this, name); ttOb = tt; thread.start(); // pornesc firul } // ncep execuia firului public void run() { if(thread.getName().compareTo("Tic") == 0) { // sunt in thread-ul tic for(int i=0; i<5; i++) ttOb.tic(true); //la sfarsit opresc tic ttOb.tic(false); } else { // sunt in thread-ul tac for(int i=0; i<5; i++) ttOb.tac(true); //la sfarsit opresc tac ttOb.tac(false); } } } class ThreadsDemo { public static void main(String args[]) { TicTac tt = new TicTac(); MyThread mt1 = new MyThread("Tic", tt); MyThread mt2 = new MyThread("Tac", tt); try { mt1.thread.join(); mt2.thread.join(); } catch(InterruptedException exc) { System.out.println("Main thread interrupted."); } } }

n acest exemplu exist trei clase, TicTac, MyThread, ThreadsDemo. Prima clasa, conine dou metode sincronizate:
synchronized void tic(boolean running) synchronized void tac(boolean running)

Acestea, au urmtoarea logic:


if(!running) { //opresc metoda notify(); //anun cellalt thread return; } System.out.print("Tic "); notify(); //il las pe tac sa ruleze wait(); // atept pe tac

Dac parametrul transmis nu este nc false (mai am drept s rulez metoda) atunci afiez Tic i dau dreptul lui Tac, urmnd s atept pn cnd thread-ul ce ruleaz Tac, s m ntiineze c a rulat n monitor. Dac parametrul running este false, atunci anun cellalt fir de execuie i prsesc metoda. Acelai lucru are loc i pentru metoda tac, evident referitor la thread-ul Tic. Clasa MyThread, ce implementeaz interfaa Runnable, reprezint cele dou fire de execuie suport pentru metodele tic() i tac(). n firul de execuie pentru Tic se apeleaz de cinci ori metoda tic() cu parametru true, i ultima dat cu parametru false, pentru a permite deblocarea firului care executa tac(). Acelai lucru este valabil viceversa pentru Tac. n clasa ThreadsDemo se creeaz cele dou fire de execuie, se lanseaz i se ateapt terminarea lor. Iat ce se ntmpl la rularea programului:
Tic Tic Tic Tic Tic Tac Tac Tac Tac Tac

wait() i notify()rezultatul poate fi: Tic Tic Tic Tic Tic Tac Tac Tac Tac Tac sau Tac Tac Tac Tac Tac Tic Tic Tic Tic Tic

Dac modificm ns, metodele tac() i tic() eliminnd metodele de comunicare i anume

Suspendarea, reluarea i oprirea firelor de execuie


Uneori este util s suspendm execuia unui thread. De exemplu, un fir de execuie poate fi folosit pentru afiarea orei. Dac utilizatorul nu dorete vizualizarea orei, acest fir va fi suspendat. El va putea fi repornit (resume) ulterior. Metodele ce realizeaz acest lucru sunt:
final void resume( ) final void suspend( ) final void stop( )

Totui folosirea acestora este ngrdit, de unele probleme cauzate de suspend() n trecut. De exemplu, dac un thread obine un lock pe o zon critic, i el este suspendat, acele lock-uri sunt eterne. n acest timp, alte fire de execuie ateapt deblocarea lor. Metoda resume() este de asemenea nvechit. Nu cauzeaz probleme, dar nu poate fi folosit fr metoda suspend(). De asemenea metoda stop() a fost considerat nvechit. Urmtorul exemplu reprezint un mod n care se pot folosi aceste funcii:
class MyThread implements Runnable { Thread thread; //volatile adic variabila poate fi //modificata de alte parti ale programului //cum ar fi un thread volatile boolean suspended; volatile boolean stopped; MyThread(String name) { thread = new Thread(this, name); suspended = false; stopped = false; thread.start(); } // entry point pentru un fir de execuie public void run() { System.out.println(thread.getName() + " starting."); try { for(int i = 1; i < 1000; i++) { System.out.print(i + " "); //din 10 in 10 are loc o pauza if((i%10)==0) { System.out.println(); Thread.sleep(250); }

//bloc sincronizat pentru verificarea //stopped sau suspended synchronized(this) { while(suspended) { wait(); } //in cadrul opririi se iese din fir if(stopped) break; } } } catch (InterruptedException exc) { System.out.println(thread.getName() + " interrupted."); } System.out.println(thread.getName() + " exiting."); } // stop firul de execuie synchronized void mystop() { stopped = true; // firul suspendat este oprit suspended = false; //anun firul care este suspendat notify(); } //suspendarea firului synchronized void mysuspend() { suspended = true; } //Reluarea firului synchronized void myresume() { suspended = false; //anun firul care este suspendat notify(); } } class SuspendDemo { public static void main(String args[]) { MyThread ob1 = new MyThread("My Thread"); try {

Thread.sleep(1000); //pauz pe firul principal ob1.mysuspend(); //suspend firul ob1 System.out.println("Suspending thread."); Thread.sleep(1000); ob1.myresume(); //reiau firul ob1 System.out.println("Resuming thread."); Thread.sleep(1000); ob1.mysuspend(); System.out.println("Suspending thread."); Thread.sleep(1000); ob1.myresume(); System.out.println("Resuming thread."); Thread.sleep(1000); ob1.mysuspend(); System.out.println("Stopping thread."); ob1.mystop(); } catch (InterruptedException e) { System.out.println("Main thread Interrupted"); } try { ob1.thread.join(); } catch (InterruptedException e) { System.out.println("Main thread Interrupted"); } System.out.println("Main thread exiting."); } }

Clasa MyThread, definete dou variabile booleane, suspended i stopped. Metoda run() conine blocul sincronizat ce verific variabila suspended. Dac este true atunci metoda wait() este apelat i suspend execuia firului. Metoda care stabilete valoarea variabilei la true, este mysuspend(). Pentru a relua firul, avem myresume(), care seteaz variabila suspended pe true i apeleaz notify(), pentru a relua execuia firului. Ultima metod este mystop() i conine paii care duc la oprirea firului de execuie. n clasa SuspendDemo, metodele descrise sunt apelate n alternan pentru a opri/relua execuia firului de cteva ori. Rezultatul rul rii acestui program este:
My Thread starting. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 Suspending thread.

Resuming thread. 41 42 43 44 45 46 47 48 51 52 53 54 55 56 57 58 61 62 63 64 65 66 67 68 71 72 73 74 75 76 77 78 Suspending thread. Resuming thread. 81 82 83 84 85 86 87 88 91 92 93 94 95 96 97 98 101 102 103 104 105 106 111 112 113 114 115 116 Stopping thread. My Thread exiting. Main thread exiting.

49 59 69 79

50 60 70 80

89 90 99 100 107 108 109 110 117 118 119 120

Grupuri de fire de execuie


Fiecare fir de execuie este membru al unui grup de fire. Un grup de thread-uri ofer un mecanism pentru colectarea multiplelor fire ntr-un singur obiect pentru a manipula aceste fire. De exemplu, se pot suspenda toate firele printr-o singur instruciune. Dac vom crea un fir fr a specifica grupul din care face parte, automat va fi pus n acelai grup cu cel al firului printe. Acest grup se mai numete i current thread group. Atunci cnd o aplicaie este pornit, Java creeaz un TreadGroup numit main. Pentru a crea un grup separat de acesta, explicit, avem la ndemn urmtoarele construcii:
public Thread(ThreadGroup group, Runnable target) public Thread(ThreadGroup group, String name) public Thread(ThreadGroup group, Runnable target, String name)

Mai jos exist un mod de folosire a acestor constructori:


ThreadGroup myThreadGroup = new ThreadGroup("My Group of Threads"); Thread myThread = new Thread(myThreadGroup, "a thread for my group");

Pentru a prelua grupul unui fir de execuie avem metoda getThreadGroup():


theGroup = myThread.getThreadGroup();

Iat un mod de folosire a acestei informaii:


public static void listCurrentThreads() { ThreadGroup currentGroup = Thread.currentThread().getThreadGroup(); int numThreads = currentGroup.activeCount(); Thread[] listOfThreads = new Thread[numThreads];

currentGroup.enumerate(listOfThreads); for (int i = 0; i < numThreads; i++) { System.out.println("Thread #" + i + " = " + listOfThreads[i].getName()); } }

Aceast funcie preia numrul de fire din grup si le afieaz. Alte metode Clasa ThreadGroup conine cteva atribute i metode dintre care:

getMaxPriority i setMaxPriority pentru manipularea prioritii getDaemon i setDaemon pentru controlul acestori tipuri de fire getName pentru preluarea numelui getParent i parentOf pentru manipularea numelui firului printe Iat, spre exemplu, cum se poate stabili prioritatea maxim, att pe fir ct i pe grup:
class MaxPriorityDemo { public static void main(String[] args) { ThreadGroup groupNORM = new ThreadGroup( "A group with normal priority"); Thread priorityMAX = new Thread(groupNORM, "A thread with maximum priority"); // set Thread's priority to max (10) priorityMAX.setPriority(Thread.MAX_PRIORITY); // set ThreadGroup's max priority to normal (5) groupNORM.setMaxPriority(Thread.NORM_PRIORITY); System.out.println("Group's maximum priority = " + groupNORM.getMaxPriority()); System.out.println("Thread's priority = " + priorityMAX.getPriority()); } }

Curs 9
Introducere in applet-uri ..................................................................................................................... 2 Organizarea applet-urilor..................................................................................................................... 5 Arhitectura unui applet .................................................................................................................... 5 Ce trebuie s conin un applet ....................................................................................................... 5 Iniializarea i terminarea unui applet .............................................................................................. 6 Redesenarea.................................................................................................................................... 7 Cum se executa un applet ................................................................................................................ 9 Folosirea status-ului din fereastr .................................................................................................. 10 Transmiterea parametrilor ctre un applet .................................................................................... 11 Clasa Applet................................................................................................................................... 13 Operaiuni grafice ntr-un applet ....................................................................................................... 14 Cnd s desenm? ......................................................................................................................... 15 Utilizarea fonturilor ....................................................................................................................... 15 Tratarea evenimentelor ..................................................................................................................... 18 Modelul delegrii ........................................................................................................................... 18 Evenimente ................................................................................................................................... 19 Sursele evenimentelor ................................................................................................................... 19 Asculttori de evenimente ............................................................................................................. 19 Clase de evenimente...................................................................................................................... 20 Folosirea modelului de delegare .................................................................................................... 20 Tratarea evenimentelor de mouse ................................................................................................. 21

Introducere in applet-uri
Applet-ul este poate cea mai importanta dintre aplicaiile Java, si acest subiect nu poate fi cuprins intr-un capitol. Aa c vom prezenta doar o privire de ansamblu asupra programrii applet-uri. Applet-urile folosesc o arhitectur unic, ce necesit folosirea unor tehnici de programare speciale. Una din aceste tehnici este tratarea evenimentelor. Evenimentele sunt modul in care un applet primete o intrare de la orice context exterior. Att conceptul de applet ct i cel de eveniment este foarte mare. n cele ce urmeaz vor fi tratate doar fundamentele acestor dou concepte. Applet-urile difer de programele precedente deoarece ele sunt mici programe a cror scop este s ruleze pe Web. Iat un mic exemplu de acest tip de program:
import java.awt.*; import java.applet.*; public class OneApplet extends Applet { public void init() {} public void paint(Graphics g) { g.drawString("First Applet.", 20, 20); } }

Acest program ncepe cu dou declaraii de import. Prima import clasele din pachetul AWT adic Abstract Window Toolkit. Applet-urile interacioneaz cu utilizatorul folosind aceste clase AWT, iar nu prim consol. AWT conine o serie de clase ce permit lucrul cu ferestre i o desenarea unei interfee grafice. Urmtoarea incluziune import se refer la pachetul applet. Acest pachet conine clasa Applet, iar orice applet creat trebuie s moteneasc aceast clas. Clasa ce motenete clasa Applet, este OneApplet. Aceast clas ar trebui s fie declarat public, deoarece va fi accesat din afara ei. Clasa conine o metod paint(). Aceast metod este definit de clasa Component din AWT, ce este o clas printe pentru Applet i trebuie s fie suprascris de OneApplet. Metoda paint() este apelat ori de cte ori va avea loc reafiarea. Aceast reafiare poate surveni din mai multe motive. De exemplu, fereastra n care applet-ul ruleaz, este suprascrisa de alta fereastr, sau este micat. Sau poate fereastra este minimizat, i apoi restaurat. De asemenea paint() este apelat la pornirea applet-ului. Metoda paint() are un parametru de tip Graphics. Acest parametru con ine contextul grafic n care ruleaz applet-ul. n interiorul paint(), se afl o metod drawString() , care este un membru al clasei Graphics. Aceast metod afieaz un String la coordonatele X,Y. Forma ei general este:
void drawString(String message, int x, int y)

Mesajul message va fi afiat pe fereastr la coordonatele x,y. Colul stnga sus are coordonatele 0,0. De asemenea, de reinut c, applet-ul nu conine o metod main(). 2

Spre deosebire de majoritatea programelor, execuia unui applet nu ncepe cu main. Pentru a putea porni un applet, acesta trebuie inclus ntr-un fiier HTML, pentru a rula n browser, sau se poate folosi un program auxiliar appletviewer. Pentru a include un applet ntr-un browser, trebuie s modificm un fiier HTML, adugnd un tag APPLET, astfel:
<html> <applet code="OneApplet" width=200 height=60> </applet> </html>

Numele OneApplet vine de la fiierul .class, ce a fost obinut prin comanda:


javac OneApplet.java

Pe lng acest nume, mai exist width=200 height=60 ce nseamn dimensiunea suprafeei de afiare alocat acestui applet. Pentru a executa programul OneApplet, se poate apela urmtoarea comand:
C:\>appletviewer OneApplet.html

Pentru a eficientiza rularea acestui applet, exist o cale i mai simpl: se include codul HTML n codul surs, astfel:
import java.awt.*; import java.applet.*; /* <applet code="OneApplet" width=200 height=60> </applet> */ public class OneApplet extends Applet { public void init() {} public void paint(Graphics g) { g.drawString("First Applet.", 20, 20); } }

In felul acesta, codul HTML ce apare n comentariu la nceputul programului, este folosit de appletviewer pentru a rula direct programul astfel:
C:\>appletviewer OneApplet.java

Astfel, n urma compilrii i rulrii,

Figura 1. Paii pentru rularea unui applet obinem urmtoarea fereastra n care ruleaz applet-ul:

Figura 2. Interfaa grafica a unui applet Dup cum reiese i din figura de mai jos, este un fel de Panel, el motenind aceast clas din pachetul java.awt. ca orice Panel, un Applet, poate conine componente de UI - user interface i poate folosi toate modelele de desenare i tratare a evenimentelor, specifice unei clase Component.

Figura 3. Pachetul java.applet 4

Pe lng clasele AWT, exist o librrie despre care vom vorbi n urmtorul capitol pe larg, denumit Swing. Aceasta este o alternativ la componentele AWT. De exemplu, pe lng butoane, etichete, etc, Swing ofer paneluri pe care se poate efectua scroll, arbori, i tabele. Practic este o extensie a acestor componente AWT.

Organizarea applet-urilor
Arhitectura unui applet Un applet este un proces bazat pe ferestre. n astfel de arhitecturi, exist cteva concepte, ce nu sunt prezente n programele ce ruleaz n consol. n primul rnd, applet-urile sunt bazate pe evenimente i seamn cu un set de funcii de ntrerupere. Un applet va atepta pn cnd un eveniment apare. Sistemul va notifica acest applet, apelnd o metod de tratare a evenimentului aprut. Odat ce aceasta are loc, applet-ul va efectua instruciunile din metod, i va preda napoi controlul sistemului. n situaiile n care un applet trebuie s efectueze mai multe sarcini, pn s returneze controlul sistemului, pentru a evita nghearea ferestrei, putem lucra, sau efectua acele sarcini pe un alt fir de execu ie. n al doilea rnd, utilizatorul este acela care interacioneaz cu applet-ul. n programe ce ruleaz n consol, programul ateapt intrri, i l notific pe utilizator. Aici, utilizatorul va interaciona cu applet-ul, dup bunul lui plac. Aceste interaciuni sunt transmise applet-ului sub forma unor evenimente. De exemplu, cnd utilizatorul apas un buton al mouse-ului, se genereaz un eveniment de mouse. Dac utilizatorul apas o tast cnd focus-ul este pe fereastra applet-ului, se va genera un eveniment de tastatur. Atunci cnd utilizatorul interacioneaz cu un buton, sau un check-box, sau orice alt control, se va genera un eveniment corespunz tor. Ce trebuie s conin un applet

Dei OneApplet, prezentat mai sus, este un applet real, el nu este complet, pentru c nu conine elementele necesare majoritii applet-urilor. Metodele ntlnite n majoritatea acestor applet-uri sunt: init(), start(), stop() i distroy(). Acestea sunt metodele definite de clasa Applet. A cincea metod este paint(), i este motenit din clasa Component. Mai jos avem un exemplu de implementare a unui Applet:
//Structura de baza a unui applet import java.awt.*; import java.applet.*; /* <applet code="AppletStructure" width=300 height=100> </applet> */ public class AppletStructure extends Applet { //prima metoda apelata.

public void init() { // iniializri } public void start() { // sart sau reiau execuia } // apelat la oprirea appletului. public void stop() { // suspend execuia } //apelat la terminarea execuiei //este ultima metod apelat public void destroy() { //activiti de oprire } // la redesenarea ferestrei public void paint(Graphics g) { //reafiarea coninutului ferestrei } }

Aceste metode au in dreptul lor un comentariu cu referire la scopul lor ( ce ar trebui sa fac atunci cnd sunt apelate ) i in ce context sunt apelate. Iniializarea i terminarea unui applet Atunci cnd este lansat un applet, urmtoarele aciuni au loc: 1. Apel init() 2. Apel start() 3. Apel paint() Cnd un applet i ncheie execuia, urmtoarele aciuni au loc: 1. Apel stop() 2. Apel destroy() Prima metod ce se apeleaz este init() i n interiorul acesteia se iniializeaz variabilele clasei i alte activiti de iniializare. Metoda start() este apelat imediat dup init() sau pentru a reporni un applet care a fost oprit. De aceea start() poate fi apelat de mai multe ori pe parcursul rulrii unui applet. Metoda paint() este folosit la redesenarea ferestrei n care ruleaz applet-ul si a elementelor din fereastr. Metoda stop() permite suspendarea instruciunilor din applet, inclusiv a thread-urilor copii. Metoda destroy() este apelat la ncheierea total a execuiilor din applet. 6

Redesenarea Ca regul general, un applet va scrie n fereastra doar cnd metoda paint() este apelat de sistem. Problema care se pune este urmtoarea: cum poate un applet s cauzeze aceste update-uri? De exemplu, dac un applet afieaz un banner n micare, ce mecanism permite acelui applet s updateze fereastra de fiecare dat? Nu se poate crea o bucl infinit n metoda paint(), pentru c ar bloca astfel sistemul. Metoda care permite redesenarea unor informaii, sau actualizarea lor, este repaint(). Metoda repaint() este definit n clasa Component din AWT. Aceasta face ca sistemul s execute un apel al funciei update(), metod ce va apela paint(). Dac o parte a applet-ului trebuie s afieze un String, poate stoca acest mesaj ntr-o variabil i apela repaint(). Metoda paint() va prelua acea variabil i o va afia folosind drawString(). Exist dou metode de redesenare:
void repaint() void repaint(int left, int top, int width, int height)

n a doua metod, coordonatele din colul stnga sus al regiunii ce va fi redesenat, sunt date de primii doi parametrii. Dimensiunea regiunii este dat de ultimii doi parametrii. Aceste dimensiuni sunt specificare n pixeli. Metoda update() este definit n Clasa Component, i este apelat cnd applet-ul a cerut ca o poriune din fereastr s fie redesenat. Exist metode de a suprascrie aceast funcie, astfel nct funcionalitatea s fie mult mai mult dect un simplu apel ulterior al metodei paint().

import java.awt.*; import java.applet.*; /* <applet code="Banner" width=300 height=50> </applet> */ //clasa implementeaza Runnable //pentru ca vom lucra cu thread-uri public class Banner extends Applet implements Runnable { String msg = " Java permite afiarea unui banner."; Thread t; boolean stopFlag; // Iniializarea threadului cu null public void init() { t = null; } // start al appletului //folosit pentru a porni threadul t public void start() { t = new Thread(this);

stopFlag = false; t.start(); } //cnd threadul ruleaz public void run() { char ch; //Se afieaz bannerul for( ; ; ) { try { repaint(); Thread.sleep(90); ch = msg.charAt(0); msg = msg.substring(1, msg.length()); msg += ch; if(stopFlag) break; } catch(InterruptedException exc) {} } } // pauza pe banner public void stop() { stopFlag = true; t = null; } // afiarea banner-ului public void paint(Graphics g) { g.drawString(msg, 50, 30); } }

Clasa Banner extinde clasa Applet, dar implementeaz i Runnable. Aceasta pentru c, applet-ul va crea un al doilea thread ce va fi folosit la actualizarea mesajului. Mesajul care va fi afiat, este cel din variabila String msg. Firul care se ocup cu aceste modificri este dat de variabila t. Variabila boolean stopFlag, este folosit pentru a opri applet-ul. n interiorul init() variabila este iniializat cu null, urmnd ca la pornirea applet-ului i anume la apelul funciei start(), aceast variabil sa fie false iar thread-ul s porneasc. Metoda care este cea mai interesant este run(). Aici se va modifica variabila msg, astfel ca primul caracter din String s fie pus pe ultima poziie. Aceste aciuni au loc ntr-o bucl infinit, ceea ce ar trebui sa nghee fereastra. Cum firul care se ocupa cu apelul metodei paint() este cel principal, funcionarea nu va avea acest impediment. Cnd se ncheie execuia applet-ului, respectiv se apeleaz metoda stop(), acelai lucru se ntmpl i cu thread-ul t, datorit variabilei stopFlag. 8

Cum se executa un applet Un applet ruleaz n general ntr-un browser. Plugin-ul Java din browser controleaz lansarea i execuia applet-ului. Browser-ul are de obicei i un interpretator JavaScript, care poate rula cod JavaScript n pagin. Plugin-ul Java Acest software, creeaz un thread pentru fiecare applet. Va lansa un applet ntr-o instan de Java Runtime Enviroment (JRE). n mod normal, toate applet-urile ruleaz n aceeai instan de JRE. Plugin-ul de Java, va porni o instan de JRE n urmtoarele cazuri: 1. Cnd un applet cere s fie executat n versiunea corespunztoare deJRE 2. Cnd un applet specific proprii parametrii pentru JRE, de exemplu, mrimea heap-ului. Un nou applet folosete JRE existent, dac specificaiile sunt o submulime a specificaiilor JRE existent. Un applet va rula ntr-o instan de JRE existent, dac toate condiiiile sunt ndeplinite: 1. Versiunea de JRE cerut de applet se potrivete cu cea existent. 2. Parametrii de start ai JRE, satisfac cerinele applet-ului. Urmtoarea diagrama prezint modul n care applet-urile sunt executate n JRE:

Figura 4. Execuia unui applet n JRE

Applet-urile pot invoca funcii prezente n pagina web. Funciile JavaScript pot de asemenea, invoca metode ale unui applet, ncorporat n aceeai pagin web. Plugin-ul Java i interpretatorul JavaScript vor orchestra aceste apeluri din cod Java n cod JavaScript i viceversa. Plugin-ul Java este multi-threaded, n timp ce interpretatorul JavaScript ruleaz pe un singur fir. Pentru a evita probleme legate de fire de execuie, mai ales cnd mai multe applet-uri ruleaz simultan, apelurile ntre cele dou limbaje trebuie s fie scurte, i s nu conin, pe ct posibil bucle. Folosirea status-ului din fereastr

Pe lng afiarea informaiilor ntr-o fereastra, un applet poate afia mesaje i n bara de status a ferestrei n care applet-ul ruleaz. Pentru a realiza acest lucru, se apeleaz metoda showStatus(), care este definit de clasa Applet. Apelul aceste metode este:
void showStatus(String msg)

unde variabila msg reprezint mesajul de afiat. Fereastra de status, se poate folosi la informarea utilizatorului, despre aciunile ce au loc n applet, de a sugera opiuni, de a raporta erori, etc. Poate, fi folosita, de asemenea, la debug, pentru a afia informaii despre variabile, etc. Mai jos este o simpla metoda ce afieaz n bara de status un mesaj:
import java.awt.*; import java.applet.*; /* <applet code="StatusWindow" width=300 height=50> </applet> */ public class StatusWindow extends Applet { //afiarea unui mesaj in status public void paint(Graphics g) { g.drawString("Acesta este un mesaj afiat in fereastra.", 10, 20); showStatus("Acesta este un mesaj afiat n status bar"); } }

Iat rezultatul rulrii acestui program:

10

Figura 5. Mesaj in status bar

Transmiterea parametrilor ctre un applet Pentru a transmite parametrii unui applet, se folosete atributul PARAM al tag-ului APPLET. Acest atribut va specifica numele parametrului i valoarea sa. Pentru a afla valoarea unui parametru, se folosete getParameter() definit n clasa Applet. Apelul acestei metode este:
String getParameter(String paramName)

Aici, paramName este numele parametrului. Funcia va returna valoarea parametrului specificat, sub forma unui obiect String. Pentru valori booleane, va trebui s convertim reprezentrile String ntr-un format intern. Dac un parametru nu este gsit, valoarea null este returnat. Iat un exemplu de folosire a parametrilor:
// Transmiterea parametrilor unui applet. import java.awt.*; import java.applet.*; /* <applet code="Param" width=300 height=80> <param name=author value="John Smith"/> <param name=purpose value="Demonstrate Parameters"/> <param name=version value=43 /> </applet> */ public class Param extends Applet { String author; String purpose; int ver; public void start() { String temp; author = getParameter("author");

11

if(author == null) author = "not found"; purpose = getParameter("purpose"); if(purpose == null) purpose = "not found"; temp = getParameter("version"); try { if(temp != null) ver = Integer.parseInt(temp); else ver = 0; } catch(NumberFormatException exc) { ver = -1; // error code } } public void paint(Graphics g) { g.drawString("Purpose: " + purpose, 10, 20); g.drawString("By: " + author, 10, 40); g.drawString("Version: " + ver, 10, 60); } }

Locul n care sunt declarai parametrii ce vor fi transmii applet-ului, la pornirea acestuia este n cadrul tag-ului <applet>:
<param name=author value="John Smith"/> <param name=purpose value="Demonstrate Parameters"/> <param name=version value=43 />

Odat ce parametrii au fost preluai la startul applet-ului, valorile lor vor fi folosite la afi area pe ecran, in metoda paint(). Iat rezultatul:

Figura 6. Parametrii unui applet

12

Clasa Applet Toate applet-urile motenesc clasa Applet. La rndul ei, clasa Applet motenete urmtoarele clase definite n AWT: Component, Container i Panel. n acest fel, un applet poate avea acces la funcionalitatea complet oferit de AWT. Pe lng aceste metode, Applet mai conine cteva funcii ce permit controlul total al applet-ului funcii ce sunt descrise mai jos: Descriere Apelat de browser nainte de terminarea applet-ului. Applet-ul va suprascrie aceast metod pentru a cura alte obiecte. AccessibleContext getAccessibleContext( ) Metod ce returneaz contextul de accesibilitate al obiectului ce invoc aceast metod. AppletContext getAppletContext( ) Metod ce returneaz contextul asociat unui applet. String getAppletInfo() Returneaz un string ce descrie applet-ul AudioClip getAudioClip(URL url ) Returneaz un obiect de tip AudioClip ce ncapsuleaz clipul audio gsit la locaia specificat prin url. AudioClip getAudioClip(URL url, Returneaz un obiect de tip AudioClip ce ncapsuleaz clipul audio gsit String clipName) la locaia specificat prin url i avnd numele clipName. URL getCodeBase( ) Returneaz URL asociat cu applet-ul. URL getDocumentBase( ) Returneaz URL al documentului HTML ce invoc applet-ul. Image getImage(URL url ) Returneaz un obiect imagine ce ncapsuleaz imaginea gsit, la o locaie specificat prin url. Image getImage(URL url, Returneaz un obiect imagine ce ncapsuleaz imaginea gsit, la o String imageName locaie specificat prin url i avnd ca nume imageName. Locale getLocale() Returneaz un obiect Locale ce este folosit n clase i metode ce folosesc setrile specifice unei regiuni. String getParameter(String paramName) Aceast metod returneaz un parametru asociat cu paramName sau null dac nu este gsit acel parametru. String[ ] [ ] getParameterInfo( ) Returneaz o tabel String ce descrie parametrii recunoscui de ctre applet. Fiecare element din tabel trebuie s fie format din trei stringuri ce conin numele parametrului, descrierea tipului de dat, i explicaii cu privire la scopul lui. void init() aceast metod este apelat la nceputul execuiei unui applet i este prima metod apelat. boolean isActive() Aceast metod returneaz true dac applet-ul a pornit, sau false dac a fost oprit. static final AudioClip Aceast metod returneaz un AudioClip ce ncapsuleaz clipul audio newAudioClip(URL url ) gsit la url-ul specificat. Aceast metod este similar cu getAudioClip, ns este static. 13 Metoda void destroy()

Dac un audio clip este gsit la locaia specificat de url, atunci clipul este rulat. void play(URL, String clipName) Dac un audio clip este gsit la locaia specificat de url, cu numele specificat de clipName, atunci clipul este rulat. void resize(Dimension dim) Redimensioneaz applet-ul conform dimensiunilor specificate de dim. Dimension este o clas ce provine din java.awt. Conine dou cmpuri: width i height. void resize(int width, int height) Redimensioneaz applet-ul conform dimensiunilor width i height. final void setStub(AppletStub stubObj) Creeaz obiectul stubObj, ce reprezint un lociitor de cod, pentru applet. Aceast metod este folosit de sistemul run-time i nu este apelat de applet-ul n sine. Un lociitor sau stub este o bucat de cod, ce asigur o legtur dintre applet i browser. void showStatus(String str) Afieaz str n fereastra status a browser-ului sau a ferestrei de afiare a applet-ului. void start() apelat de browser atunci cnd un applet trebuie s fi pornit. Este apelat automat dup metoda init(). void stop() apelat de browser atunci cnd un applet trebuie s i suspende activitatea. Odat oprit, un applet poate fi repornit prin start().

void play(URL url)

Operaiuni grafice ntr-un applet


Clasa Graphics are o mulime de funcii pentru desenare. Pentru fiecare figur geometric, dreptunghi, arc, elipsa, poligon, exist o metod specific ce deseneaz fie conturul, fie interiorul acelei figuri. Iat mai jos un exemplu de desenare a unui dreptunghi umplut cu o culoare:
//colorarea unui dreptunghi import java.awt.*; import java.applet.*; public class PaintDemo extends Applet { int rectX = 20, rectY = 30; int rectWidth = 50, rectHeight = 50; public void init() { resize(getPreferredSize()); } public void paint(Graphics g) {

14

g.setColor(Color.red); g.fillRect(rectX, rectY, rectWidth, rectHeight); } public Dimension getPreferredSize( ) { return new Dimension(100, 200); } }

Dup cum se vede desenarea are loc n metoda paint, dar redimensionarea n init. Dac am plasa redimensionarea n paint() ar fi practic imposibil redimensionarea ferestrei. Se pune problema, unde trebuie s desenm, n paint() sau altundeva? Cnd s desenm? Exist mai multe opiuni, pe care le putem alege cnd efectum desenarea unui obiect. Cea mai corect metod este ns de a desena n metoda paint(), deoarece nu se poate desena ntr-o fereastr pn cnd aceasta nu este creat i plasat pe ecran. n plus, se poate ca metodele s dureze mult, ceea ce blocheaz afiarea unor obiecte pe fereastra. Este recomandat ca n celelalte metode s se modifice parametrii obiectelor ce vor fi afiate, i s se recheme paint() prin intermediul metodei de actualizare. Utilizarea fonturilor

Pentru lucrul cu fonturi, n Java exist dou clase principale, Font i FontMetrics. Prima servete la setarea i lucrului concret cu anumite fonturi, redimensionarea lor, etc. A doua servete la preluarea msurilor unui font i a altor dimensiuni ale fontului. Mai jos se afl un exemplu pentru utilizarea acestor clase, ns este doar nceputul, sau o prim privire aruncat asupra acestor aspecte, pentru c exist foarte multe alte funcionaliti care nu sunt abordate aici.
// demonstrarea modului corect de desenare import java.awt.*; import java.applet.*; public class DrawStringDemo extends Applet { String message = "Hello Java"; //aici vom seta fontul folosit in applet public void init() { Font f = new Font("Calibri" , Font.BOLD|Font.ITALIC, 24); setFont(f); } //aici va avea loc desenarea public void paint(Graphics g) { //preluarea fontului curent si a dimensiunilor FontMetrics fm = getFontMetrics(getFont( ));

15

//folosirea dimensiunilor fontului, si a mesajului //pentru a afla poziia la care ncepem sa desenam int textX = (getSize( ).width - fm.stringWidth(message))/2; if (textX<0) // If dac string-ul este prea mare textX = 0; //acelai lucru si pentru nlime int textY = (getSize().height - fm.getLeading( ))/2; if (textY<0) textY = 0; //aici se deseneaz textul pe fereastra g.drawString(message, textX, text); } }

In cele ce urmeaz este exemplificat modul in care putem aduga, efecte, cum ar fi umbra unui text. De asemenea, scopul este de a exemplifica modul de lucru cu parametrii, deoarece datele legate de font sunt preluate din parametrii transmi i applet-ului:
import java.applet.*; import java.awt.*; /** <applet code="DropShadow" width=300 height=80> <param name=label value="Eticheta"/> <param name=fontname value="Arial"/> <param name=fontsize value=43 /> </applet> */ class Main extends Frame { public static void main(String args[]) { //aici cream un nou applet DropShadow dp = new DropShadow(); //pe care l pornim dp.start(); } } public class DropShadow extends Applet { /*eticheta ce va apare in fereastra */ protected String theLabel = null; /*Limea si nlimea */ protected int width, height; /*Numele fontului*/ protected String fontName; /*variabila ce modific fontul */ protected Font theFont; /*mrimea fontului */ protected int fontSize = 18;

16

/*deplasamentul umbrei*/ protected int theOffset = 3; /*daca am primit toi parametrii */ protected boolean inittedOK = false; /*pentru a verifica dac iniializarea e ok*/ public void init( ) { theLabel = getParameter("label"); if (theLabel == null) throw new IllegalArgumentException("LABEL is REQUIRED"); //ne ocupam de font fontName = getParameter("fontname"); if (fontName == null) throw new IllegalArgumentException("FONTNAME is REQUIRED"); String s; if ((s = getParameter("fontsize")) != null) fontSize = Integer.parseInt(s); if (fontName != null || fontSize != 0) { theFont = new Font(fontName, Font.BOLD + Font.ITALIC, fontSize); System.out.println("Name " + fontName + ", font " + theFont); } if ((s = getParameter("offset")) != null) theOffset = Integer.parseInt(s); setBackground(Color.green); inittedOK = true; this.resize(getPreferredSize()); } public Dimension getPreferredSize( ) { return new Dimension(200, 100); } /** Paint method showing drop shadow effect */ public void paint(Graphics g) { if (!inittedOK) return; g.setFont(theFont); g.setColor(Color.black); g.drawString(theLabel, theOffset+30, theOffset+50); g.setColor(Color.white); g.drawString(theLabel, 30, 50); } //ce fel de parametri sunt necesari public String[][] getParameterInfo( ) {

17

String info[][] = { { "label", "string", "Text to display" }, { "fontname", "name", "Font to display it in" }, { "fontsize", "10-30?", "Size to display it at" }, }; return info; } }

Totodat, n exemplul de mai sus, se vedea un alt mod de a porni un applet, din cadrul unei metode main(). Se renun astfel la browser, sau aplicaia ajuttoare appletviewer:
DropShadow dp = new DropShadow(); //pe care l pornim dp.start();

Pe lng aceste funcionaliti, exist foarte multe alte componente de GUI pe care le vom aborda ntr-un capitol urmtor. n cele ce urmeaz vom aborda modul n care applet-urile trateaz evenimentele ce pot apare.

Tratarea evenimentelor
Applet-urile sunt programe controlate de evenimente. De aceea, tratarea evenimentelor este inima, oricrui applet. Cele mai multe evenimente la care applet-ul va rspunde sunt generate de utilizator. Aceste evenimente sunt transmise applet-ului n diverse moduri. Exist mai multe tipuri de evenimente. Cele mai des ntlnite sunt cele generate de mouse, tastatur i diverse controale cum ar fi, un buton. Aceste clase care se ocup de evenimente sunt coninute n pachetul java.awt.event. nainte de nceperea discuiei, trebuie spus c modul n care evenimentele sunt tratate de ctre un applet, s modificat de la versiunea 1.0 la versiunea 1.1. Primele metode de tratare a evenimentelor, sunt nc implementate, dar au devenit nvechite. Ceea ce vom descrie n continuare, sunt metodele noi de tratare a evenimentelor. nc odat, trebuie spus c, ceea ce va fi prezentat este o mic parte din ntregul subiect, ns ofer o idee despre modul n care se lucreaz cu aceste concepte. Modelul delegrii Acest mod de abordare nou, se bazeaz pe un model de a delega evenimentele. Acesta definete mecanismele standard de a genera i procesa evenimente. Conceptul este urmtorul: o surs genereaz un eveniment i trimite o notificare unuia sau mai multor asculttori. n aceast schem, asculttorii ateapt pur i simplu pn cnd primesc un eveniment. Odat primit, asculttorul proceseaz evenimentul i apoi returneaz controlul. Avantajul este c separ conceptele de interfaa cu utilizatorul. O interfa cu utilizatorul poate delega procesarea unui eveniment unei alte pri de cod. Pentru a putea primi o notificare, asculttorii trebuie s se aboneze la un eveniment. 18

Evenimente n modelul delegrii, un eveniment este un obiect ce descrie o schimbare de context. Poate fi generat n urma interaciunii unei persoane cu elementele grafice din interfa ( apsarea unui buton, a unei taste, selectarea unui obiect dintr-o lista, etc). De asemenea poate fi provocat, artificial, de c tre proces. Sursele evenimentelor O surs a unui eveniment este un obiect ce genereaz acel eveniment. O surs trebuie s nregistreze asculttorii pentru a acetia s primeasc notificri legate de un eveniment. Aceasta este metoda care va realiza nregistrarea, sau abonarea:
public void addTypeListener(TypeListener el)

Aici Type este numele evenimentului, i el este referina la un asculttor. De exemplu metoda care nregistreaz un asculttor la un eveniment de tastatur este addKeyListenter(). Metoda care nregistreaz micarea unui mouse este addMouseMotionListener(). Atunci cnd un eveniment are loc, toi asculttorii care s-au abonat la acel eveniment sunt notificai, primind o copie a obiectului eveniment. O surs va trebui s ofere i o metod ce permite anularea nregistrrii de la un anumit eveniment. Forma general a metodei care realizeaz acest lucru este:
public void removeTypeListener(TypeListener el)

Aici Type este numele evenimentului de la care se face denregistrarea. De exemplu, dac se dorete tergerea unui asculttor de la tastatur, se va apela metoda removeKeyListener(). Metodele ce adaug sau terge un eveniment, sunt oferite de sursele ce genereaz aceste evenimente. De exemplu, clasa Component, ofer metode pentru adugarea i tergerea asculttorilor la tastatur i mouse. Asculttori de evenimente Un asculttor, este un obiect ce este notificat atunci cnd un eveniment are loc. Acest asculttor trebuie s ndeplineasc dou condiii: 1. S fie nregistrat cu una sau mai multe surse pentru a primi notificri despre anumite evenimente. 2. S implementeze metode ce s proceseze aceste notificri. Metodele care primesc i proceseaz notificri sunt definite ntr-o mulime de interfee din pachetul java.awt.event. de exemplu interfaa MouseMotionListener definete metode pentru a primi notificri atunci cnd mouse-ul este mutat sau tastat. Orice obiect poate primi i procesa aceste evenimente.

19

Clase de evenimente

Aceste clase reprezint mecanismul de baz de tratare a evenimentelor n Java. La vrful ierarhiei se afl clasa EventObject, care se gsete n pachetul java.util. Aceasta este superclasa pentru toate evenimentele. Clasa AWTEvent, definit n cadrul pachetului java.awt este o subclas a lui EventObject. Este clasa printe pentru toate evenimentele din AWT. Pachetul java.awt.event definete cteva tipuri de evenimente ce sunt generate de diverse elemente de interfa. n tabelul de mai jos sunt enumerate aceste evenimente. Clasa de eveniment ActionEvent AdjustmentEvent Component Event ContainerEvent FocusEvent InputEvent ItemEvent KeyEvent MouseEvent TextEvent WindowEvent Descriere Generat cnd un buton este apsat, se efectueaz dublu click pe o list, sau se selecteaz un meniu. Generat cnd un scroll bar este manipulat Generat cnd o component este ascuns, micat sau redimensionat. Generat cnd o component este adugat sau tears dintr-un obiect Container. Generat cnd o component primete sau pierde focusul tastaturii O clasa abstract printe tuturor componentelor de evenimente de intrare Generat cnd se efectueaz click pe un checkbox sau o list, sau este Realizat o selecie de opiune ntr-un meniu. Generat cnd intrarea este recep ionat de la tastatur Generat cnd un mouse este trasat, sau mutat apsat sau eliberat Generat cnd valoarea unui cmp text se modific. Generat cnd o fereastr este activat, nchis, deactivat, etc.

Pentru toate sursele mai sus menionate exist p serie de asculttori, i anume interfee ce folosesc la captarea notificrilor evenimentelor produse. Acestea sunt : ActionListener, AdjustmentListener, , WindowListener. Folosirea modelului de delegare Pentru a programa acest model trebuie s respectm urmtorii pai: 1. Se implementeaz o interfa corespunztoare n asculttor aa nct va recepiona evenimentele corespunztoare. 2. Implementarea codului astfel nct s nregistreze i de-nregistreze (dup caz) un asculttor, la i de la anumite evenimente. Orice surs poate genera mai multe tipuri de evenimente. Fiecare eveniment trebuie nregistrat separat. De asemenea, un obiect poate fi nregistrat pentru a recepiona notificri de la mai multe evenimente. Pentru aceasta trebuie s implementeze toate interfeele cerute. n continuare vom analiza tratarea evenimentelor mouse-ului.

20

Tratarea evenimentelor de mouse Pentru a trata evenimentele de mouse, trebuie s implementm interfeele MouseListener i MouseMotionListener. Interfaa MouseListener definete cinci metode. Dac un buton al mouseului este apsat, atunci este invocat metoda mouseClicked(). Atunci cnd mouse-ul se afl n cadrul unei componente, cum ar fi o fereastr, este apelat metoda mouseEntered(), iar cnd prsete componenta, se apeleaz metoda mouseExited(). De asemenea, metodele mousePressed() i mouseReleased() sunt apelate cnd un buton al mouse-ului este apsat respectiv eliberat. Iat forma metodelor:
void void void void void mouseClicked(MouseEvent me) mouseEntered(MouseEvent me) mouseExited(MouseEvent me) mousePressed(MouseEvent me) mouseReleased(MouseEvent me)

Interfaa MouseMotionListener() definete dou metode. Prima, mouseDragged() este apelat de mai multe ori cnd mouse-ul este trasat. Metoda mouseMoved() este invocat ori de cte ori micm mouse-ul. Formele acestor metode sunt:
void mouseDragged(MouseEvent me) void mouseMoved(MouseEvent me)

Evenimentul MouseMoved transmite un parametru me ce descrie evenimentul. Cele mai folositoare metode din cadrul acestei clase sunt getX() i getY() ce preiau coordonatele mouse-ului:
int getX() intgetY()

Mai jos se afl un eveniment, ce demonstreaz folosirea evenimentelor de mouse. Acest applet va afia coordonatele actuale ale mouse-ului n cadrul ferestrei. De fiecare dat cnd un buton este apsat, este afiat cuvntul Down, iar cnd este eliberat, se afi eaz cuvntul Up. Dac un buton este apsat se va afia n colul stnga sus mesajul Mouse clicked. Atunci cnd mouse-ul se afl deasupra ferestrei, mesajul de notificare apare n colul stnga sus. Atunci cnd se traseaz mouse-ul, se afieaz un mesaj * n coordonatele actuale ale mouse-ului. n toate aceste modificri, coordonatele mouse-ului vor fi salvate n mouseX i mouseY. Aceste Variabile vor fi utilizate n metoda paint() pentru a afia diversele mesaje.
import java.awt.event.*; import java.applet.*; import java.awt.*; /* <applet code="MouseWatcher" width=300 height=100> </applet> */ public class MouseWatcher extends Applet

21

implements MouseListener, MouseMotionListener { String msg = ""; int mouseX = 0, mouseY = 0; //coordonatele Image image; public void init() { //la iniializare //ne abonam pentru a fi notificai //la evenimentele mouse-ului addMouseListener(this); addMouseMotionListener(this); } // Tratare eveniment click de mouse public void mouseClicked(MouseEvent me) { mouseX = 0; mouseY = 10; msg = "Mouse clicked."; repaint(); } // Tratare eveniment de intrare mouse public void mouseEntered(MouseEvent me) { mouseX = 0; mouseY = 10; msg = "Mouse entered."; repaint(); } // Tratare eveniment de ieire muse public void mouseExited(MouseEvent me) { mouseX = 0; mouseY = 10; msg = "Mouse exited."; repaint(); } // Tratare eveniment de apsare mouse public void mousePressed(MouseEvent me) { // salvare coordonate mouseX = me.getX(); mouseY = me.getY(); msg = "Down"; repaint(); } // Tratare eveniment de eliberare mouse

22

public void mouseReleased(MouseEvent me) { // salvare coordonate mouseX = me.getX(); mouseY = me.getY(); msg = "Up"; repaint(); } // Tratare eveniment de trasare mouse public void mouseDragged(MouseEvent me) { //salvare coordonate mouseX = me.getX(); mouseY = me.getY(); msg = "*"; showStatus("Dragging mouse at " + mouseX + ", " + mouseY); repaint(); } // Tratare eveniment de micare mouse public void mouseMoved(MouseEvent me) { //afiarea pe status showStatus("Moving mouse at " + me.getX() + ", " + me.getY()); } // se afieaz mesajul la coordonatele salvate public void paint(Graphics g) { g.drawString(msg, mouseX, mouseY); } }

In figura de mai jos se poate vedea rezultatul aciunilor, notificate ctre handlerele de mouse, ce modific variabila de mesaj i coordonatele.

Figura 7. Tratarea evenimentelor unui mouse n cadrul procedurii de init(), applet-ul se nregistreaz ca asculttor la evenimentele mouse-ului. Aceasta se face prin cele dou apeluri de addMouseListener i addMouseMotionListener. 23

Ultimul exemplu se refer la tratarea evenimentelor tastaturii, i este asemntor celui anterior. Diferena este c, abonarea se va face la evenimentele de tastatur, i evident nu vom avea coordonate, de aceast dat. Totui, cele dou programe pot fi combinate astfel ca ambele tipuri de evenimente s fie tratate.
import java.awt.event.*; import java.applet.*; import java.awt.*; /* <applet code="KeyWatcher" width=300 height=100> </applet> */ public class KeyWatcher extends Applet implements KeyListener { String msg = ""; Image image; public void init() { //la initializare //ne abonam pentru a fi notificati //la evenimentele tastaturii addKeyListener(this); resize(800,100); } /* tratare evetiment tastare*/ public void keyTyped(KeyEvent e) { msg = "KEY TYPED: " + e.paramString(); repaint(); } /* tratare eveniment apasare tasta */ public void keyPressed(KeyEvent e) { msg = "KEY PRESSED: " + e.paramString(); repaint(); } /*tratare eveniment eliberare tasta*/ public void keyReleased(KeyEvent e) { msg = "KEY RELEASED: " + e.paramString(); repaint(); } // se afieaz mesajul conform tastei apsate public void paint(Graphics g) { g.drawString(msg,20,20); } }

24

AWT Introducere, concepte ......................................................................................................................... 2 Componente.................................................................................................................................... 2 Evenimente ......................................................................................................................................... 7 Modelul vechi de evenimente Java .................................................................................................. 7 Identificarea intei ........................................................................................................................... 7 Tratarea evenimentelor ................................................................................................................... 8 Clasa Event .......................................................................................................................................... 8 Variabilele ....................................................................................................................................... 8 Constante ........................................................................................................................................ 9 Evenimente de fereastr................................................................................................................ 10 Evenimente de scroll...................................................................................................................... 11 Evenimente de focus ..................................................................................................................... 11 Metodele clasei Event.................................................................................................................... 12 Modelul nou de evenimente.............................................................................................................. 12 Clasa AWTEvent................................................................................................................................. 14 Variabile ........................................................................................................................................ 14 Constante ...................................................................................................................................... 14 Constructori ................................................................................................................................... 15 Metode.......................................................................................................................................... 15 Clasa AWTEventMulticaster ........................................................................................................... 16 Componente ..................................................................................................................................... 19 Clasa Component........................................................................................................................... 19 Evenimentele unei Component...................................................................................................... 24 Etichete ............................................................................................................................................. 25 Constante ...................................................................................................................................... 25 Metode.......................................................................................................................................... 26 Butoane............................................................................................................................................. 26 Metode.......................................................................................................................................... 26 Clasa Canvas ...................................................................................................................................... 28 Clasa Cursor....................................................................................................................................... 29 TextComponent................................................................................................................................. 30 Clasa Container.................................................................................................................................. 33 Container....................................................................................................................................... 33 Panel ................................................................................................................................................. 38 Window............................................................................................................................................. 40

Introducere, concepte
Librria AWT (Abstract Window Toolkit) ofer o interfa grafic pentru programele Java. Aceast librrie ofer unelte folosite pentru comunicarea program utilizator. Problema acestei librrii este c Java 1.0.2 a fost nlocuit de versiuni ca 1.1 i 1.2, versiuni ce au introdus alte caracteristici. De foarte muli ani, programatorii au trecut prin mari bti de cap n ncercrile de a porta un program scris pentru sisteme Unix, Linux pe sistem Windows sau Macintosh. n 1995, Sun anuna o posibil soluie: Java. Pentru sisteme de operare bazate pe ferestre, portabilitatea era o problem. Cnd transferm un program scris pentru Windows pe sisteme Macintosh, codul care trateaz lucrul cu interfaa grafic trebuie rescris complet. n Java ns, acest neajuns cauzat de portabilitate, este rezolvat parial prin AWT. Prin aceast librrie, programele se pot rula n aceeai manier n orice sistem de operare, ele vor arata la fel. De exemplu dac aplicaia folosete liste de tip Combobox, acestea vor arta ca o list de tip Windows, dac aplicaia ruleaz sub Windows, sau ca o lista de Unix dac aplicaia ruleaz sub Unix. n construcia acestei librarii exist cteva concepte de baz pe care le vom detalia n acest prim capitol i anume: componente, membrii, aspect, recipient. n continuare vom vorbi mai pe larg despre fiecare. Componente Interfeele cu utilizatorul sunt construite n jurul ideii de componente: dispozitive care implementeaz o parte din interfee. ncepnd cu anii 80 aceste dispozitive au fost extinse, cele mai cunoscute fiind: butoane, meniuri, ferestre, checkbox-uri, bare de scroll, etc. AWT conine un set de astfel de componente, i n plus, o mainrie de creat componente personalizate. n continuare vom prezenta componentele principale ale AWT fr a intra n detalii. Text static Clasa Label ofer un mod de a afia o linie de text pe ecran. Se poate controla fontul folosit pentru text i culoarea. Mai jos este un exemplu de etichete desenate pe ecran:

Figura 10.1 instane de Label

Componente pentru introducere date Java ofer cteva moduri de a permite utilizatorului de a introduce date ntr-o aplicaie. Utilizatorul poate tipri informaia, sau o poate selecta dintr-o list de opiuni prestabilit. Clasele TextField i TextArea Exist dou componente pentru a putea introduce date de la tastatur: TextField pentru o singur linie i TextArea pentru linii multiple. Aceast ofer i mijloace pentru diferite validri a textului introdus de utilizator. n figura de mai jos sunt cteva exemple pentru aceste componente:

Figura 10.2 Elementele de text

Clasele CheckBox i CheckboxGroup Componentele menionate ofer mecanisme pentru a permite utilizatorului s selecteze dintr-o list de opiuni, una singur sau mai multe. Primul mecanism se numete Checkbox, i permite bifarea unei opiuni. n partea stng a ferestrei se afl un astfel de element numit Dialog. Un clic pe aceast csua marcheaz opiunea ca fiind true si se bifeaz. Un clic ulterior, deselecteaz opiunea marcnd-o ca false. Clasa CheckboxGroup nu este o component, ci ofer un mod de a grupa csuele de bifare ntro mulime n care opiunile se exclud mutual. Denumirea lor este de butoane radio.

Figura 10.3 Csue de opiuni

Clasa de listare de opiuni Problema cu clasele Checkbox i CheckboxGroup este c atunci cnd listele sunt prea mari, si opiunile sunt multe, nu exist loc pentru a plasa aceste butoane radio. n acest caz se folosesc componentele Choice, care reprezint o list de mai multe elemente, din care se poate selecta doar unul singur. n figura de mai jos este reprezentat un astfel de element.

Figura 10.4 Clasa Choice Clasa List Totui dac dorim sa afim integral elementele unei liste, i s oferim posibilitatea de a selecta multiple elemente, avem la dispoziie clasa List. n figura de mai jos avem o astfel de component.

Figura 10.5 Clasa List Meniuri Interfeele moderne folosesc aceste elemente, ns n Java n aceast librrie, meniurile pot apare doar ntr-un Frame. O clas Menu, este complex i comport multe pri: bare de meniu, elemente de meniu, etc. Clasa PopupMenu Meniurile Pop-up sunt folosite n func ie de context, mai ales cnd mouse-ul se afl ntr-o regiune a ferestrei. Mai jos sunt prezentate ambele tipuri de meniuri:

a) b) Figura 10.6 a) Meniuri b)Meniuri Pop-up

Declanatoare de evenimente Java ofer dou componente a cror scop este de a declana aciuni pe ecran: Button i Scrollbar. Ele permit utilizatorului s specifice momentul realizrii unei aciuni. Clasa Scrollbar ntr-un program gen Word sau ntr-un browser Web, atunci cnd o imagine sau text este prea mare pentru a fi afiat n pagin, acest element permite deplasarea utilizatorului n diverse p ri ale ecranului. Atunci cnd se efectueaz click pe Scrollbar efectul este exact deplasare ntr-o anumit direcie pentru a viziona coninutul paginii. n figura 10.7 este reprezentat acest element.

Figura 10.7 Scrollbar vertical i orizontal Unele componente ca TextArea sau List conin deja aceste Scrollbar-uri pentru a diminua efortul programatorului. Pentru alte elemente ca Panel sau Frame va trebui implementat de la zero funcionalitatea pentru ScrollBar-uri. Clasa Buton Butonul este cel mai cunoscut element pentru declanarea unor evenimente i cel mai intuitiv. Java permite si butoane cu imagini: ImageButton. Clasa Canvas Aceast clas reprezint o suprafa goal: nu are nici o vizualizare predefinit. Se pot folosi Canvas pentru desenarea imaginilor, construirea componentelor personalizate, sau a supercomponentelor ce conin alte componente predefinite.

n continuare vom descrie n detaliu elementele amintite mai sus pentru a oferi o perspectiv asupra librriei AWT.

Evenimente
Am descris n cursul anterior cteva evenimente, precum i conceptul din spatele acestui mecanism. n cele ce urmeaz vom aprofunda aceste concepte. Modelul vechi de evenimente Java Modelul este destul de simplu: atunci cnd se recep ioneaz un eveniment iniiat de utilizator, sistemul genereaz o instan de tip Event i o transmite programului. Programul identific inta (de exemplu componenta n care a avut loc evenimentul) i permite acelei componente s trateze evenimentul. Dac inta nu poate trata evenimentul, se ncearc gsirea unei componente care va putea. Identificarea intei Evenimentele au loc ntr-o clas Component. Programul decide care component va primi evenimentul, pornind de la nivelul cel mai nalt. n exemplul ales, aciunea de a efectua click pe frame-ul de mai jos, mai exact pe butonul Blood, va declana metoda deliverEvent() din cadrul acestei clase. Procesul va continua pn cnd inta gsit este butonul Blood, buton ce nu mai conine alte componente, aa c se va executa evenimentul aferent lui.

Figura 10.8 identificarea intei

Mai jos este prezentat ierarhia de apeluri a metodei deliverEvent, pn cnd evenimentul este livrat intei.

Figura 10.9 Livrarea evenimentului

Tratarea evenimentelor Odat ce metoda deliverEvent() identific inta, se va apela metoda handleEvent() a intei. Dac inta nu are suprascris aceast metod, se apeleaz implementarea implicit motenit de la obiectul Component motenit, i nu se ntmpl nimic. n continuare vom descrie ierarhia claselor ce se ocup de evenimente.

Clasa Event
Atunci cnd apare sau este declanat un eveniment, este de responsabilitatea programatorului s capteze acel eveniment. Se poate decide netratarea lui, aa c evenimentul va trece ca i cnd nu ar fi avut loc. Atunci cnd se dorete transmiterea unui eveniment mai departe, sau tratarea lui, trebuie s nelegem cum se comport un obiect de acest tip. nainte de a ncepe prezentarea acestei clase, trebuie spus c ea a fost nlocuit de AWTEvent pe care o vom descrie imediat dup. Motivul pentru care o descriem aici este mai mult informativ i pentru a putea face legtura cronologic i conceptual ntre aceste dou moduri de a soluiona problema legat de evenimente. Variabilele Clasa Event conine multe variabile ce ofer informaie despre evenimentul ce are loc. Vom discuta despre cteva.
public int clickCount

Acest membru al clasei Event, permite verificarea evenimentului de double-click. Cmpul este relevant doar pentru evenimente de tip MOUSE_DOWN. 8

public Event evt

Acest cmp este folosit pentru a transmite evenimente, dup cum am vzut mai sus. Programul poate trata evenimentul transmis, sau evenimentul poate fi trimis unui handler personalizat:
public boolean mouseDown (Event e, int x, int y) { System.out.println ("Coordinates: " + x + "-" + y); if (e.evt != null) postEvent (e.evt); return true; } public int id

Acest cmp conine identificatorul evenimentului. Evenimentele generate de sistem au constantele predefinite, i cteva dintre ele sunt:
WINDOW_DESTROY MOUSE_ENTER WINDOW_EXPOSE MOUSE_EXIT WINDOW_ICONIFY MOUSE_DRAG WINDOW_DEICONIFY SCROLL_LINE_UP KEY_PRESS SCROLL_LINE_DOWN KEY_RELEASE SCROLL_PAGE_UP KEY_ACTION SCROLL_PAGE_DOWN KEY_ACTION_RELEASE SCROLL_ABSOLUTE MOUSE_DOWN LIST_SELECT MOUSE_UP LIST_DESELECT MOUSE_MOVE ACTION_EVENT

public Object target

Cmpul target conine referina ctre un obiect care produce acest eveniment. De exemplu, dac utilizatorul selecteaz un buton, butonul este inta evenimentului. Dac utilizatorul mic mouse-ul pe un obiect Frame, atunci Frame este inta. Target va indica locul unde a avut loc evenimentul, i nu neaprat componenta care trateaz evenimentul.
public long when

Aceast variabil conine durata unui eveniment exprimat n milisecunde. Codul transform o valoare long a acestei variabile n tipul Date pentru a examina ulterior durata:
Date d = new Date (e.when);

Constante 9

Clasa Event conine nenumrate constante, unele pentru a desemna ce eveniment a avut loc, altele pentru a ajuta la determinarea tastei apsate. Constantele ce se refer la taste se mpart n dou, dup tipul de eveniment ce este generat: KEY_ACTION - i se numesc taste pentru aciune KEY_PRESS se desemneaz alfanumericele. n tabelul de mai jos sunt prezentate cteva constante i tipul de eveniment care folosete aceste constante: Constanta Tip eveniment Constanta Tip eveniment
HOME KEY_ACTION END KEY_ACTION PGUP KEY_ACTION PGDN KEY_ACTION UP KEY_ACTION DOWN KEY_ACTION LEFT KEY_ACTION RIGHT KEY_ACTION F1 KEY_ACTION F2 KEY_ACTION F3 KEY_ACTION F4 KEY_ACTION F5 KEY_ACTION F6 KEY_ACTION F7 KEY_ACTION F9 KEY_ACTION F10 KEY_ACTION F11 KEY_ACTION F12 KEY_ACTION PRINT_SCREEN KEY_ACTION SCROLL_LOCK KEY_ACTION CAPS_LOCK KEY_ACTION NUM_LOCK KEY_ACTION PAUSE KEY_ACTION INSERT KEY_ACTION ENTER (\n) KEY_PRESS BACK_SPACE (\b) KEY_PRESS TAB (\t) KEY_PRESS ESCAPE KEY_PRESS DELETE KEY_PRESS

Exist i modificatori pentru tastele Shift, Alt, Control. Atunci cnd utilizatorul apas o tast sau genereaz alt tip de eveniment, se poate verifica dac a fost apsat simultan i una din tastele mai sus menionate. Pentru aceasta avem la dispoziie modificatorii:
public static final int ALT_MASK public static final int CTRL_MASK public static final int SHIFT_MASK

Atunci cnd raporteaz un eveniment, sistemul seteaz cmpul modifiers ce poate fi verificat ulterior. Evenimente de fereastr Aceste evenimente au loc pentru componentele ce aparin unui Window. Cteva din aceste evenimente sunt valabile doar pentru anumite platforme OS.
public static final int WINDOW_DESTROY

Acest eveniment este produs cnd sistemul spune unei ferestre s se autodistrug. Aceasta intervine de obicei cnd utilizatorul selecteaz Close sau Quit. Implicit, instanele Frame nu trateaz acest eveniment.

10

public static final int WINDOW_EXPOSE

Acest eveniment este transmis atunci cnd o parte a ferestrei devine vizibil . Pentru a afla ce parte a ferestrei a fost descoperit, se poate folosi getClipRect(), metod ce apar ine instana clasei Graphics. Evenimente de scroll Aceste evenimente sunt declanate de aciunea utilizatorului pe o component de tip Scrollbar. Obiectele ce au un Scrollbar construit implicit (List, TextArea) nu genereaz aceste evenimente. Evenimentele pot fi tratate n metoda handleEvent() din cadrul Container-ului sau a unei subclase Scrollbar.
public static final int SCROLL_LINE_UP

Aceast constant este transmis unui eveniment care are loc atunci cnd utilizatorul apas sgeata de sus a unui scrollbar vertical sau pe sgeata din stnga a unui scrollbar orizontal.
public static final int SCROLL_LINE_DOWN

Aceast constant este setat unui eveniment care are loc atunci cnd utilizatorul apas sgeata de jos a unui scrollbar vertical sau pe sgeata din dreapta a unui scrollbar orizontal.
public static final int SCROLL_PAGE_UP

Aceast constant este setat unui eveniment care are loc atunci cnd utilizatorul apas lng sgeata de sus a unui scrollbar vertical sau lng sgeata din stnga a unui scrollbar orizontal, cauznd deplasarea unei pagini ntregi n acea direcie.
public static final int SCROLL_PAGE_DOWN

Aceast constant este setat unui eveniment care are loc atunci cnd utilizatorul apas lng sgeata de jos a unui scrollbar vertical sau lng sgeata din dreapta a unui scrollbar orizontal, cauznd deplasarea unei pagini ntregi n acea direcie. Evenimente de focus
public static final int GOT_FOCUS

Acest eveniment apare cnd o anumit component este selectat. Metoda FocusListener.focusGained() poate fi folosit pentru tratarea acestui eveniment.
public static final int LOST_FOCUS

11

Acest eveniment apare cnd o anumit component este selectat. Metoda FocusListener.focusLost() poate fi folosit pentru tratarea acestui eveniment. Metodele clasei Event De obicei evenimentele vor fi declanate de contextul exterior (apsarea unui buton, a unei taste, etc). totui dac ne crem propriile componente, sau dorim s comunicm ntre thread-uri, atunci va trebui s ne crem propriile noastre evenimente. Clasa Event are o serie de constructori i anume:
public Event (Object target, long when, int id, int x, int y, int key, int modifiers, Object arg)

Primul constructor este i cel mai complet, i iniializeaz toate cmpurile obiectului cu parametrii specificai. Ceilali doi constructori vor omite cteva iniializri dupa cum urmeaz:
public Event (Object target, long when, int id, int x, int y, int key, int modifiers) public Event (Object target, int id, Object arg)

Metode specifice modificatorilor Aceste metode servesc la verificarea valorilor mtilor modificatorilor.
public boolean shiftDown ()

Metoda shiftDown() returneaz true dac tasta Shift a fost apsat sau false n caz contrar. Metode asemntoare exist i pentru celelalte taste i anume Ctrl sau Alt.

Modelul nou de evenimente


Dei pare mai complex (modelul este format din mai multe pri) este mai simplu i mai eficient. Acest model solicit obiectelor s se aboneze pentru a primi evenimente. Acest model se numete delegare i implementeaz modelul Observer-Observable descris n cursul anterior. Relund, fiecare component este o surs de evenimente ce poate genera diverse tipuri de evenimente, care sunt de fapt, subclase a clasei AWTEvent. Obiectele interesate de un eveniment se numesc asculttori. Fiecare tip de eveniment corespunde unei interfee de ascultare ce specific metodele care sunt apelate atunci cnd un eveniment are loc. Ierarhia de evenimente i clasele cu rol de asculttori pentru acestea, este reprezentat n figura de mai jos:

12

Figura 10.10 Modelul AWTEvent

13

Unele interfee de asculttori sunt construite astfel nct s trateze mai multe evenimente. De exemplu, interfaa MouseInterface declar cinci metode pentru a trata diverse evenimente de mouse. Aceasta nseamn c un obiect interesat de evenimente de mouse trebuie s implementeze interfaa MouseListener i va trebui s implementeze toate cele cinci metode. Ce se ntmpl dac dorim s tratm doar un eveniment de mouse? Suntem obligai s scriem cod de patru ori in plus? Din fericire pachetul java.awt.event include clase adapter, ce sunt scurtturi ce permit scrierea facil a acestor handler-e de evenimente. Clasa adapter ofer implementri nule a tuturor metodelor interfeei. De exemplu MouseAdapter ofer implementri implicite alea metodelor mouseEntered(), mouseExisted(), etc. Putem astfel s ne concentrm pe metoda care ne intereseaz la un moment dat i anume mouseClicked().

Clasa AWTEvent
Variabile
protected int id

Cmpul id al clasei AWTEvent este accesibil prin intermediul getID(). Este identificatorul tipului de eveniment, ca de exemplu ACTION_PERFORMED al evenimentului ActionEvent sau MOUSE_MOVE. Constante Constantele clasei AWTEvent sunt folosite pentru a determina ce tip de tratare de eveniment va fi folosit i ce eveniment este procesat. Mai jos este o list cu acestea: public final static long ACTION_EVENT_MASK public final static long ADJUSTMENT_EVENT_MASK public final static long COMPONENT_EVENT_MASK public final static long CONTAINER_EVENT_MASK public final static long FOCUS_EVENT_MASK public final static long ITEM_EVENT_MASK public final static long KEY_EVENT_MASK public final static long MOUSE_EVENT_MASK public final static long MOUSE_MOTION_EVENT_MASK public final static long TEXT_EVENT_MASK public final static long WINDOW_EVENT_MASK Aceste constante sunt folosite n corelaie cu metoda Component.eventEnabled().

14

Constructori Clasa AWTEvent este abstract, constructorii nu pot fi folosi i imediat. Ei sunt apelai la instanierea unei clase copil.
public AWTEvent(Event event) public AWTEvent(Object source, int id)

Primul constructor creeaz un eveniment de tip AWTEvent folosindu-se de unul din versiunea veche Java. Al doilea creeaz un AWTEvent folosind o surs dat. Parametrul id folosete ca identificator al tipului de eveniment. Metode
protected void consume()

Metoda consume()este apelat pentru a meniona faptul c un eveniment a fost tratat. Un eveniment care a fost marcat astfel, este livrat mai departe celorlal i asculttori daca este cazul. Doar evenimentele de tastatur i mouse pot fi marcate ca i consumate. Un scenariu de folosire a acestei faciliti este atunci cnd dorim s refuzm introducerea anumitor caractere, practic acele caractere nu sunt afiate.
protected boolean isConsumed()

Metoda returneaz faptul c un eveniment a fost sau nu consumat. Exist o serie de alte clase de tratare a evenimentelor, iar acestea sunt specifice tipului de control GUI la care se refer evenimentul. Iat o list a acestor clase: Clasa Descriere ComponentEvent Reprezint evenimentul ce are loc n cadrul unui Component ContainerEvent Include evenimentele ce rezult din operaii dintr-un Container FocusEvent Conine evenimentele ce sunt generate la primirea/pierderea focusului WindowEvent Se ocup de evenimentele specifice unei ferestre PaintEvent ncapsuleaz evenimentele specifice aciunii de desenare. Aici este un caz special deoarece nu exist clasa PaintListener. Se vor folosi totui metodele paint() i update() pentru a procesa evenimentele. InputEvent Este clasa abstract de baz, pentru tratarea evenimentelor de tastatur i mouse KeyEvent Se ocup de evenimentele de tastatur MouseEvent Se ocup de evenimentele de mouse ActionEvent Este prima clas din ierarhia acestor clase. ncapsuleaz evenimentele care semnaleaz faptul c utilizatorul realizeaz o aciune asupra unei componente. AdjustmentEvent Este alt clas din ierarhia claselor de evenimente. ncapsuleaz evenimente care reprezint micrile unui scrollbar. 15

ItemEvent TextEvent

Este clasa ce permite tratarea evenimentelor ce au loc atunci cnd utilizatorul Selecteaz un checkbox sau un buton radio etc. ncapsuleaz evenimentele ce au loc atunci cnd coninutul unui TextComponent s-a schimbat.

Pentru toate aceste elemente descrise exist interfee de asculttori i clase adaptori. De exemplu pentru clasa ActionEvent exist interfaa ActionListener ce conine metode pentru tratarea evenimentelor de acest tip. Pentru clasa ComponentEvent exist interfaa ComponentListener ce conine patru metode pentru mutarea i redimensionarea componentei. Clasa ComponentAdapter este adaptorul corespunztor, folosit mai ales cnd se dorete ascultarea unui eveniment specific. Clasa AWTEventMulticaster Aceasta este folosit de AWT pentru a trata cozile de asculttori pentru diverse evenimente, i pentru a trimite evenimente tuturor asculttorilor interesai (multicasting). Iat cum se folosete aceast clas:
public static ActionListener add(ActionListener first, ActionListener second)

se poate construi un adevrat lan de asculttori astfel:

Metoda de mai sus are doi parametrii de tip ActionListener i returneaz tot un ActionListener. Acest obiect returnat este un multicaster ce conine doi asculttori. De asemenea

actionListenerChain=AWTEventMulticaster.add(actionListenerChain, newActionListener);

Pentru a transmite un eveniment lanului de asculttori astfel format avem metoda actionPerformed():
actionListenerChain.actionPerformed(new ActionEvent(...));

Evident exist metode pentru toate tipurile de evenimente, i acestea sunt:


public public public public public public public public public public public public void void void void void void void void void void void void actionPerformed(ActionEvent e) adjustmentValueChanged(AdjustmentEvent e) componentAdded(ContainerEvent e) componentHidden(ComponentEvent e) componentMoved(ComponentEvent e) componentRemoved(ContainerEvent e) componentResized(ComponentEvent e) componentShown(ComponentEvent e) focusGained(FocusEvent e) focusLost(FocusEvent e) itemStateChanged(ItemEvent e) keyPressed(KeyEvent e)

16

public public public public public public public public public public public public public public public public public

void void void void void void void void void void void void void void void void void

keyReleased(KeyEvent e) keyTyped(KeyEvent e) mouseClicked(MouseEvent e) mouseDragged(MouseEvent e) mouseEntered(MouseEvent e) mouseExited(MouseEvent e) mouseMoved(MouseEvent e) mousePressed(MouseEvent e) mouseReleased(MouseEvent e) textValueChanged(TextEvent e) windowActivated(WindowEvent e) windowClosed(WindowEvent e) windowClosing(WindowEvent e) windowDeactivated(WindowEvent e) windowDeiconified(WindowEvent e) windowIconified(WindowEvent e) windowOpened(WindowEvent e)

Mai jos avem un exemplu de folosire a unui obiect de multicasting.


import java.awt.*; import java.awt.event.*; class ItemEventComponent extends Component implements ItemSelectable { boolean selected; int i = 0; ItemListener itemListener = null; ItemEventComponent () { enableEvents (AWTEvent.MOUSE_EVENT_MASK); } public Object[] getSelectedObjects() { Object o[] = new Object[1]; o[0] = new Integer (i); return o; } public void addItemListener (ItemListener l) { itemListener = AWTEventMulticaster.add (itemListener, l); } public void removeItemListener (ItemListener l) { itemListener = AWTEventMulticaster.remove (itemListener, l); } public void processEvent (AWTEvent e) {

17

if (e.getID() == MouseEvent.MOUSE_PRESSED) { if (itemListener != null) { selected = !selected; i++; itemListener.itemStateChanged ( new ItemEvent (this, ItemEvent.ITEM_STATE_CHANGED, getSelectedObjects(), (selected?ItemEvent.SELECTED:ItemEvent.DESELECTED))); } } } } public class Multicasting extends Frame implements ItemListener { Multicasting () { super ("Listening In"); ItemEventComponent c = new ItemEventComponent (); add (c, "Center"); c.addItemListener (this); c.setBackground (SystemColor.control); setSize (200, 200); } public void itemStateChanged (ItemEvent e) { Object[] o = e.getItemSelectable().getSelectedObjects(); Integer i = (Integer)o[0]; System.out.println (i); } public static void main (String args[]) { Multicasting f = new Multicasting(); f.show(); } }

n acest exemplu se prezint modul de a folosi AWTEventMulticaster pentru a crea componente ce genereaz evenimente de tip ItemEvent. Clasa AWTEventMulticaster este folosit pentru a crea un obiect multicast prin metoda AWTEventMulticaster.add(). Metoda itemStateChanged() notific pe oricine este interesat. Evenimentul de tip item este generat la apsarea butonului de mouse. Din moment ce nu avem asculttori pentru mouse, trebuie s apelm metoda enableEvents pentru mouse.

18

Componente
n continuare vom prezenta clasa Component, mpreun cu unele componente specifice cum ar fi Label, Button i Canvas. De asemenea vom arunca o privire i asupra clasei Cursor. Clasa Component Fiecare aplicaie cu interfa grafic const dintr-un set de obiecte. Aceste obiecte sunt de tip Component, iar cele mai frecvent derivate din aceast clas, sunt butoanele, cmpurile de text i container-ele. Constante Acestea sunt folosite pentru a specifica cum ar trebui aliniat componenta curent:
public public public public public static static static static static final final final final final float float float float float BOTTOM_ALIGNMENT CENTER_ALIGNMENT LEFT_ALIGNMENT RIGHT_ALIGNMENT TOP_ALIGNMENT

Metode
protected Component()

Constructorul este declarat protected deoarece clasa Component este abstract. Constructorul permite crearea unei componente fr un tip anume (buton, text etc).
public Toolkit getToolkit ()

Metoda returneaz Toolkit-ul actual al componentei. Prin acest Toolkit avem acces la detaliile platformei curente (ca rezoluia, mrimea ecranului, sau fonturi disponibile).
public Color getForeground ()

Metoda returneaz culoarea de prim plan. Dac nu exist o culoare setat, se va lua culoarea printelui componentei actuale. Dac nici printele nu are culoare de prim plan se returneaz null.
public void setForeground (Color c)

Metoda modific culoarea de prim plan prin parametrul de tip Color c.


public Color getBackground ()

19

public void setBackground (Color c)

Aceste dou metode realizeaz acelai lucru ca cele de mai sus, dar se refer la culoarea fundalului.
public Font getFont () public synchronized void setFont (Font f)

Sunt metodele pentru preluarea/setarea fontului care va fi valabil pentru componenta curent.
public Graphics getGraphics ()

Metoda getGraphics preia contextul grafic al componentei. Cele mai multe componente nu specific, corect acest context i vor arunca excepia InternalError la apelul acestei metode.
public Locale getLocale () public void setLocale (Locale l)

Aceste metode modific sau ajut la preluarea setrii Locale a componentei curente. Localizarea este o parte dintr-un subiect al internaionalizrii programelor Java i nu l vom aborda acum.
public Cursor getCursor () public synchronized void setCursor (Cursor c)

Aceste dou metode preiau/modific informaiile legate de forma i dimensiunea mouse-ului.


public Point getLocation () public Point location ()

Aceste metode returneaz poziia curent a Component-ei, poziie relativ la printele acestei structuri. Punctul returnat prin Point este colul stnga sus ce delimiteaz dreptunghiul componentei.
public void setLocation (int x, int y) public void move (int x, int y)

Metodele au ca efect mutarea Component-ei n noua poziie (x,y). Coordonatele se refer la colul stnga sus al dreptunghiului ce delimiteaz componenta i sunt relative la printele acesteia.
public public public public Dimension getSize () Dimension size () void setSize (int width, int height) void resize (int width, int height)

Primele dou metode ajut la preluarea dimensiunii componentei, n timp ce urmtoarele dou, seteaz noua dimensiune a componentei. 20

public float getAlignmentX () public float getAlignmentY ()

Aceste dou metode returneaz aliniamentul componentei referitor la axa X respectiv Y. acest aliniament poate fi folosit ulterior de managerul de aspect pentru a pozi iona componente relativ la altele. Valoarea returnat este ntre 0 i 1, unde o valoare apropiat de 0 indic o apropiere de stnga n timp ce o valoare apropiat de 1 indic o deplasare ctre dreapta.
public void doLayout () public void layout ()

Metodele cauzeaz validarea componentei curente.


public boolean contains (int x, int y) public boolean inside (int x, int y)

Aceste dou metode verific dac, punctul de coordonate x i y se afl n cadrul dreptunghiului ce delimiteaz componenta. Dac aceast component nu este rectangular, metoda acioneaz ca i cnd ar fi delimitat de un dreptunghi.
public Component getComponentAt (int x, int y) public Component locate (int x, int y)

Metodele folosesc contains pentru a verific dac x i y se afl n cadrul componentei. Dac dac, metoda returneaz instana componentei. Dac nu, returneaz null.
public void paint (Graphics g)

Metoda permite afiarea diverselor lucruri n cadrul componentei. Se poate suprascrie, asemenea cum am prezentat n cursul anterior pentru applet-uri.
public void repaint ()

Exist o serie de funcii de redesenare, care n principiu realizeaz acelai lucru: redesenarea componentei ct mai repede cu putin.
public void print (Graphics g)

Apelul implicit al acestei metode este ctre paint. Dar se poate imprima coninutul unei componente, dac parametrul Graphics implementeaz PrintGraphics.

21

Metode de vizualizare Pachetul AWT conine de asemenea clasa abstract Image, care se ocup cu tratarea imaginilor, provenite din diverse surse, cele mai ntlnite fiind fiierele de imagini. Un mic exemplu pentru folosirea acestei clase, pentru ncrcarea unei imagini i afiarea ei se afl mai jos:
import java.awt.*; public class Imaging extends Frame { Image im; Imaging () { im = Toolkit.getDefaultToolkit().getImage ("c:\\1.jpg"); } public void paint (Graphics g) { g.drawImage (im, 0, 0, 175, 225, this); } public boolean mouseDown (Event e, int x, int y) { im.flush(); repaint(); return true; } public static void main (String [] args) { Frame f = new Imaging(); f.setVisible(true); } }

Revenind la clasa Component, aceasta suport lucrul cu imagini prin intermediul clasei Image i a ctorva funcii ce vor fi descrise n cele ce urmeaz.
public boolean imageUpdate (Image image, int infoflags, int x, int y, int width, int height)

Aceast metod provine din interfaa java.awt.image.ImageObserver implementat de clasa Component. Permite actualizarea asincron a interfeei pentru a primi notificri despre imaginea dat de parametrul image. Aceast metod este necesar pentru c ncrcarea imaginii se face pe un thread separat. Parametrii x,y specific poziia imaginii ncrcate, iar width i height specific dimensiunea imaginii. Parametrul infoflags este o masc pe bii ce permite setarea diferitelor opiuni de update.
public Image createImage (int width, int height)

22

Metoda creeaz o imagine goal de mrime width i height. Imaginea returnat este o imagine stocat n memorie. Dac nu poate fi creat, atunci apelul metodei returneaz null.
public boolean prepareImage (Image image, ImageObserver observer)

Metoda foreaz ncrcarea unei imagini, asincron, n alt thread. Obiectul observer este componenta care va fi afiat de imaginea image la ncrcarea imaginii. Dac image a fost deja ncrcat atunci funcia returneaz true. n caz contrar se returneaz false. Din moment de image este ncrcat asincron, prepareImage returneaz imediat ce este apelat.
public int checkImage (Image image, ImageObserver observer)

Metoda returneaz statusul construciei unei reprezentri a imaginii, status raportat de observer. Dac imaginea nu s-a ncrcat, atunci apelul metodei nu va produce acest lucru. Valoarea returnat este dat de flag-urile obiectului observer n SAU logic valabile pentru datele disponibile. Acestea sunt: WIDTH, HEIGHT, PROPERTIES, SOMEBITS, FRAMEBITS, ALLBITS, ERROR, i ABORT.
public void addNotify ()

Metoda este suprascris de fiecare component n parte. Atunci cnd este apelat componenta este invalidat. Metoda este apelat de sistem la crearea componentei, sau atunci cnd componenta este adugat la un Container i acesta era deja afiat.
public synchronized void removeNotify ()

Metoda distruge coninutul componentei i o va terge de pe ecran. Informaia despre statusul componentei este reinut ntr-un subtip specific.
public void setVisible(boolean condition) public void show (boolean condition)

Cele dou metode, din care ultima este invechit, afieaz pe ecran componenta (n cazul metodei setVisible doar dac variabila boolean este setat pe true). Printele Container, trece n starea invalid, deoarece cel puin un copil a modificat aspectul.
public void hide ()

Metoda ascunde componenta i are acelai efect ca i metoda setVisible apelat cu parametru false.
public synchronized void enable () public synchronized void disable () public void setEnabled (boolean condition)

Metodele stabilesc dac utilizatorul poate accesa/interaciona cu aceste componente sau nu. 23

public void requestFocus ()

Aceast metod permite iniializarea unei cereri ca i componenta s recepioneze focus.


public boolean isFocusTraversable()

Aceast metod verific dac acea component este capabil de a recepiona focus sau nu.
public void nextFocus ()

Metoda permite transferarea focus-ului ctre urmtoarea component. Evenimentele unei Component O clas Component suport metodele nvechite de control al evenimentelor, discutate mai sus: deliverEvent, postEvent i handleEvent, dar i metode noi de a posta noi evenimente ca:
public final void dispatchEvent(AWTEvent e)

Pe lng acestea exist o serie de funcii ce sunt nvechite cum ar fi keyUp, sau mouseDown ce pot fi folosite pentru captarea evenimentelor de buton sau taste. Pentru tratarea evenimentelor ap rute n cadrul unui Component exist modelul delegat i o serie de funcii pentru nregistrarea sau denregistrarea asculttorilor:
public public public public public public public public public public void void void void void void void void void void addComponentListener(ComponentListener listener) removeComponentListener(ComponentListener listener) addFocusListener(FocusListener listener) removeFocusListener(FocusListener listener) addKeyListener(KeyListener listener) removeKeyListener(KeyListener listener) addMouseListener(MouseListener listener) removeMouseListener(MouseListener listener) addMouseMotionListener(MouseMotionListener listener) removeMouseMotionListener(MouseMotionListener listener)

Pentru a putea trata evenimentele (asta dac nu dorim neaprat nregistrarea la un anumit eveniment) trebuie s permitem captarea acestora. Aceasta se realizeaz prin funcia:
protected final void enableEvents(long eventsToEnable)

Clasa AWTEvent permite definirea unor constant ce permit verificarea tipului de eveniment ce a avut loc. Tipul evenimentului este dat de variabila eventsToEnable.
protected final void disableEvents(long eventsToDisable)

24

Aceast metod permite oprirea recepionrii unor evenimente pentru componenta curent.
protected void processEvent(AWTEvent e)

Metoda primete toate evenimentele AWTEvent ce au ca int componenta curent. Aceste evenimente vor fi transmise celorlalte metode specifice, pentru o procesare ulterioar. Atunci cnd se suprascrie metoda processEvent, este posibil procesarea evenimentelor fr a nregistra asculttori.
protected void processComponentEvent(ComponentEvent e)

Aceast metod primete un eveniment de tipul ComponentEvent ce are drept int componenta actual. Dac sunt abonai asculttori, acetia sunt notificai. Suprascrierea metodei processComponentEvent este echivalent cu redimensionarea componentei.
protected void processFocusEvent(FocusEvent e)

Metoda este apelat atunci cnd componenta primete focus. Suprascrierea acestei metode este echivalent cu suprascrierea metodelor gotFocus() sau lostFocus().
protected void processKeyEvent(KeyEvent e)

Metoda primete un eveniment de tastatur, ce are ca int componenta curent. n caz de suprascriere, dac dorim ca procesarea s decurg normal trebuie apelat i metoda super.processKeyEvent(e),altfel evenimentele nu vor ajunge la asculttorii nregistrai. Suprascrierea metodei este similar metodelor keyDown() sau keyUp() din modelul vechi.

protected void processMouseEvent(MouseEvent e)

Aceast metod este similar cu cea anterioar cu specificaia c se ocup cu evenimente de mouse.

Etichete
O etichet, este reprezentat de clasa Label, ce deriv din Component i afieaz o linie de text. Este folosit mai ales la stabilirea unor titluri sau a ataa text altei componente. Constante
public final static int LEFT public final static int CENTER public final static int RIGHT

25

Acestea ofer aliniamentul textului din etichet. Metode


public Label () public Label (String label) public Label (String label, int alignment)

Acetia sunt constructorii acestei clase, prin care se permite instanierea unei etichete, i specificarea textului afiat label sau a aliniamentului.
public String getText () public void setText (String label)

Aceste metode permit preluarea textului etichetei respectiv setarea acestuia.


public int getAlignment () public void setAlignment (int alignment)

Aceste dou metode permit preluarea/specificarea aliniamentului unei etichete. Dac aliniamentul nu este valid, metoda de setare va arunca o excepie de tip IllegalArgumentException. Clasa Label poate reaciona la evenimentele pe care le recepioneaz, dei nu este scopul ei.

Butoane
Clasa Button deriv de asemenea din Component i se folosete pentru declanarea unor aciuni. Metode
public Button () public Button (String label)

Acetia sunt constructorii, ultimul permi nd crearea unui buton a crui text este label.
public String getLabel () public synchronized void setLabel (String label)

Aceste dou metode permit preluarea/specificarea textului afi at pe un buton.


public String getActionCommand () public void setActionCommand (String command)

26

Fiecare buton poate avea dou nume. Unul este ceea ce vede utilizatorul i cellalt este folosit de programator i se numete comanda butonului. Aceasta a fost introdus pentru internaionalizarea programelor. De exemplu daca eticheta butonului conine Yes ns programul ruleaz pe sistem francez sau german, eticheta va afia Oui sau Ja. Oricare ar fi sistemul pe care ruleaz comanda butonului poate rmne Yes.
public synchronized void addNotify ()

Metoda permite adugarea butonului ca i component, permind-ui acestuia s i schimbe aspectul pstrnd funcionalitatea. Evenimentele unui buton sunt cele de mouse, de focus de tastatur practic cele prezentate mai sus n clasa Component. Pentru a nelege mai bine evenimentele i diferena ntre numele comenzii i numele afiat avem urmtorul cod:
import java.applet.*; import java.awt.event.*; import java.awt.*; public class Buton extends Applet implements ActionListener { Button b; public void init () { add (b = new Button ("One")); b.addActionListener (this); add (b = new Button ("Two")); b.addActionListener (this); add (b = new Button ()); b.setLabel("Drei"); b.setActionCommand("Three"); b.addActionListener (this); add (b = new Button ()); b.setLabel("Quatre"); b.setActionCommand("Four"); b.addActionListener (this); } public void actionPerformed (ActionEvent e) { String s = e.getActionCommand(); if ("One".equals(s)) { System.out.println ("Pressed One"); }else if ("Two".equals(s)) { System.out.println ("Pressed Two"); } else if ("Three".equals(s)) {

27

System.out.println ("Pressed Three"); } else if ("Four".equals(s)) { System.out.println ("Pressed Four"); } } }

Dei etichetele ultimelor butoane sunt altfel dect Four sau Three mesajul va fi afiat pentru c se compar numele aciunii butonului.

Clasa Canvas
Aceasta este o clas pe servete ca suport pentru noi componente ce pot fi create pe lng cele deja oferite de librria AWT. Canvas poate fi folosit i ca loc de a desena componente adi ionale pe ecran, sau folosind Graphics se pot desena pe un Canvas diverse figuri. Cel mai bine este ca un obiect Canvas s stea n interiorul unui Container n cazul aplicaiilor mai complexe. Mai jos este un exemplu de folosirea a aceste clase.
import java.awt.Canvas; import java.awt.Graphics; import java.applet.Applet; public class CanvasTest extends Applet { public void init() { DrawingRegion region = new DrawingRegion(); add(region); } } class DrawingRegion extends Canvas { public DrawingRegion() { setSize(100, 50); } public void paint(Graphics g) { g.drawRect(0, 0, 99, 49); // draw border g.drawString("A Canvas", 20,20); } }

n cadrul acestui exemplu clasa DrawingRegion ce motenete Canvas este folosit n cadrul unui Applet, mai exact adugat n cadrul ferestrei principale. 28

Clasa Cursor
Aceasta ofer diferite forme i dimensiuni pentru a reprezenta pointer-ul mouse-ului. Iat mai jos constantele ce reprezint acele forme
public public public public public public public public public public public public public public final final final final final final final final final final final final final final static static static static static static static static static static static static static static int int int int int int int int int int int int int int DEFAULT_CURSOR CROSSHAIR_CURSOR TEXT_CURSOR WAIT_CURSOR HAND_CURSOR MOVE_CURSOR N_RESIZE_CURSOR S_RESIZE_CURSOR E_RESIZE_CURSOR W_RESIZE_CURSOR NE_RESIZE_CURSOR NW_RESIZE_CURSOR SE_RESIZE_CURSOR SW_RESIZE_CURSOR

Metode

public int getType()

Metoda returneaz tipul cursorului i este egal ca valoare cu una din constantele clasei.
static public Cursor getPredefinedCursor(int type)

Metoda returneaz un cursor predefinit. Dac tipul nu este egal cu una din constante, metoda arunc excepia IllegalArgumentException. Metoda verific dac obiectul de tip Cursor exist deja i dac da returneaz referina la obiectul existent. Iat un exemplu pentru utilizarea acestei componente:
import java.awt.*; import java.awt.event.*; public class CursorTest { public static void main(String[] args) { Frame f = new Frame("Change cursor"); Panel panel = new Panel();

29

Button comp1 = new Button("Ok"); Button comp2 = new Button("Cancel"); panel.add(comp1); panel.add(comp2); f.add(panel,BorderLayout.CENTER); f.setSize(200,200); f.setVisible(true); Cursor cur = comp1.getCursor(); Cursor cur1 = comp2.getCursor(); comp1.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); comp2.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent we){ System.exit(0); } }); } }

TextComponent
Exist dou moduri de a introduce date: de la tastatur sau prin micrile mouse-ului. Pentru introducerea de caractere exist dou clase TextField i TextArea. Acestea fac parte din clasa printe TextComponent.
public String getText () public void setText (String text)

Metodele ajut la preluarea/modificarea textului din cadrul acestei componente. Dac este vorba de o component de tip TextArea, atunci sunt permise i caracterele \n astfel c textul va apare pe mai multe linii. Utilizatorii pot selecta textul din cadrul acestei componente folosind mouse-ul sau tastatura. Pentru a lucra cu textul selectat avem urm toarele funcii:
public int getSelectionStart ()

Metoda returneaz poziia iniial a textului selectat. Poziia poate fi considerat numrul de caractere ce preced primul caracter selectat. Dac nu este selectat text, se returneaz poziia cursorului. Valoarea de start de la nceputul textului este 0.
public int getSelectionEnd ()

30

Aceast metod returneaz poziia cursorului ce indic sfritul seleciei curente. Dac nu este selectat nimic atunci se va returna poziia curent a cursorului.
public String getSelectedText ()

Aceast metod returneaz textul selectat sub forma unui String, sau null dac nu este nimic selectat.
public void setSelectionStart (int position) public void setSelectionEnd (int position)

Aceste dou metode modific selecia actuala a textului dup parametrul position.
public void setEditable (boolean state) public boolean isEditable ()

Aceste metode sunt pentru a activa sau dezactiva un TextComponent. Mai jos este un exemplu pentru a demonstra folosirea acestor clase:
import java.awt.*; import java.applet.*; public class TestText extends Applet { TextArea area; Label label; public void init () { setLayout (new BorderLayout (10, 10)); add ("South", new Button ("toggleState")); add ("Center", area = new TextArea ("Area to write", 5, 10)); add ("North", label = new Label ("Editable", Label.CENTER)); } public boolean action (Event e, Object o) { if (e.target instanceof Button) { if ("toggleState".equals(o)) { area.setEditable (!area.isEditable ()); label.setText ((area.isEditable () ? "Editable" : "Readonly")); return true; } } return false; } }

31

Pe lng evenimentele cunoscute se poate trata modificarea textului componentei folosind urm toarea funcie:
public synchronized void addTextListener(TextListener listener)

Aceast metod permite nregistrarea obiectului listener pentru a primi notificri atunci cnd are loc un eveniment de tip TextEvent. Metoda listener.textValueChanged() este apelat atunci cnd aceste evenimente au loc. Mai jos este un exemplu pentru folosirea acestora:
import java.applet.*; import java.awt.*; import java.awt.event.*; class TextFieldSetter implements ActionListener { TextField tf; TextFieldSetter (TextField tf) { this.tf = tf; } public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals ("Set")) { tf.setText ("You Just Say Hello"); } } } public class TextEvent1 extends Applet implements TextListener { TextField tf; int i=0; public void init () { Button b; tf = new TextField ("Hello", 20); add (tf); tf.addTextListener (this); add (b = new Button ("Set")); b.addActionListener (new TextFieldSetter (tf)); } public void textValueChanged(TextEvent e) { System.out.println (++i + ": " + e); } }

32

Celelalte tipuri de componente, precum listele, checkbox-urile sau combobox-urile vor fi abordate n cursul urmtor, urmnd ca acum s studiem cum se pot grupa aceste componente.

Clasa Container
Clasa Container este o subclas a Component, ce conine alte componente, inclusiv alte recipiente. Container permite crearea de grupuri de obiecte ce apar pe ecran. n continuare vor fi atinse cteva din aceste clase. Fiecare obiect Container are un o schi, o reprezentare ce permite organizarea componentelor din acel recipient. Toate aceste aspecte vor fi detaliate mai trziu cnd vom vorbi despre Layout. Container Container este o clas abstract ce ofer suport pentru alte obiecte de tip Component. Aceast clas conine metode pentru a grupa elementele de tip Component i a trata evenimentele care apar n cadrul ei. Deoarece este o clas abstract, nu poate fi instaniat un obiect de acest tip, de aceea ea trebuie derivat i apoi utilizat. Constructori Constructorul clasei ce motenete Container permite instanierea unui obiect i asocierea unui Layout i anume folosind un manager pentru aspect. Urmtorul cod exemplific acest lucru:
import java.awt.*; public class LightweightPanel extends Container { LightweightPanel () {} LightweightPanel (LayoutManager lm) { setLayout(lm); } }

Gruparea elementelor Aceste metode descriu modul de lucru cu obiectele coninute de Container:
public int getComponentCount() public int countComponents()

Metoda getComponentCount() returneaz numrul de componente din cadrul unui Container iar countComponents() este o veche metod dintr-o versiune Java 1.0. Pentru a prelua elementele dintr-un Container avem la dispoziie metodele: 33

public Component getComponent (int position) public Component[] getComponents()

Prima metod returneaz o component cu un anumit index. Dac poziia este incorect va fi aruncat urmtoarea excepie: ArrayIndexOutOfBoundsException. A doua metod returneaz un ir ce conine toate componentele din Container-ul curent. Orice modificare a oricrui element din acest ir va apare imediat pe ecran. Adugarea elementelor
public Component add (Component component, int position)

Metoda add() adaug o component n Container, la o anumit poziie. Dac position este -1 inserarea va avea loc la sfrit. Dac position este incorect, metoda va arunca excepia IllegalArgumentException. Dac se ncearc adugarea Container-ului curent la sine nsui metoda va arunca aceeai excepie. Dac totul merge perfect, componenta este adugat n recipient, i metoda returneaz obiectul Component adugat.
public Component add (Component component)

Aceast metod adaug un component ca ultim obiect al Container-ului . Aceasta se face prin apelul metodei anterioare cu position egal cu -1.
public void add (Component component, Object constraints)

Aceast metod este necesar pentru situaiile n care avem componente Layout ce solicit informaii n plus pentru a dispune componentele pe ecran. Informaia adiional este specificat prin parametrul constraints. Parametrul constraints depinde de managerul de interfa: LayoutManager. Poate fi folosit pentru a denumi Container-e din cadrul unui CardLayout, sau specifica suprafaa unui BorderLayout, etc.
public Component add (String name, Component component)

Aceast metod permite folosirea metodei anterioare, n care String-ul folosit definete o anumit constrngere. Apelarea aceste metode va genera un ContainerEvent cu id-ul COMPONENT_ADDED. Acestea sunt cteva din metodele de adugare, ns mai sunt i altele care pot fi folosite. tergerea elementelor
public void remove (int index)

Metoda remove terge componenta de la poziia indicat de index. Metoda va apela removeLayoutComponent() pentru a terge componenta din LayoutManager. 34

public void removeAll ()

Metoda va terge toate componentele din container. Atunci cnd este apelat, va genera un ContainerEvent cu id-ul COMPONENT_REMOVED. Alte medode
public boolean isAncestorOf(Component component)

Metoda verific faptul c component este printe a acestui Container. Va returna true n caz afirmativ, altfel va returna false. Metode de Layout Fiecare container are un LayoutManager. Acesta este responsabil cu poziionarea componentelor n cadrul container-ului. Metodele listate sunt folosite pentru a dimensiona obiectele din Container.
public LayoutManager getLayout ()

Aceast metod va returna obiectul LayoutManager asociat Container-ului curent.


public setLayout (LayoutManager lm)

Metoda schimb obiectul LayoutManager al Container-ului curent i-l invalideaz. Componentele din cadrul Container-ului vor fi re-dispuse dup regulile definite in obiectul lm. Dac obiectul lm este null poziia componentelor din Container poate fi controlat de programator.
public Dimension getPreferredSize () public Dimension preferredSize ()

Aceste returneaz un obiect de tip Dimension ce conine mrimea preferat a componentelor din Container. Container-ul poate determina mrimea preferat apelnd metoda preferredLayoutSize() care va returna spaiul necesar managerului pentru a aranja componentele. A doua metod este vechea funcie din Java 1.0 pentru preferredLayoutSize().
public Dimension getMinimumSize () public Dimension minimumSize () public Dimension getMaximumSize ()

Aceste metode returneaz obiectul de tip Dimension care va calcula minimul/maximul de spaiu necesar (lime i lungime) pentru ca managerul de Layout s aranjeze componentele.

35

public float getAlignmentX () public float getAlignmentY ()

Aceste metode returneaz aliniamentul componentelor din cadrul Container-ului pe componenta x respectiv y. Container-ul determin aliniamentul apelnd metoda getLayoutAlignmentX(), respectiv getLayoutAlignmentY() din managerul de Layout actual. Valoarea returnat va fi ntre 0 i 1. Valorile apropiate de 0 indic faptul c, componenta trebuie poziionat mai la stnga, iar cele apropiate de 1 indic faptul c, componenta trebuie poziionat mai la dreapta n cazul metodei getLayoutAlignmentX(). n cazul metodei getLayoutAlignmentY() 0 nseamn mai aproape de partea de sus iar 1 mai aproape de partea de jos a suprafeei.
public void doLayout () public void layout ()

Aceste dou metode indic managerului de Layout s afieze Container-ul.


public void validate ()

Metoda seteaz starea de validitate a container-ului pe true i valideaz recursiv componentele sale. Dac acesta mai conine un Container, atunci i acela va fi validat. Unele componente nu sunt iniializate pn nu sunt validate. De exemplu, nu se pot interoga dimensiunile de afi are ale unui Button, pn cnd acesta nu este validat.
public void invalidate ()

Metoda invalideaz Container-ul actual i componentele din cadrul su. Evenimente


public void deliverEvent (Event e)

Aceast metod este apelat atunci cnd eveniment de tip Event are loc. Metoda ncearc s localizeze o component din Container pentru a primi evenimentul aferent ei. Dac este gsit, atunci coordonatele x i y sunt transmise noii inte, evenimentul e de tip Event este astfel livrat.
public Component getComponentAt (int x, int y)

Aceast metod apeleaz metoda contains() din cadrul fiecrei componente din Container pentru a vedea dac, coordonatele x i y sunt n interiorul componentei. Dac da, acea component este returnat. Dac nu se gsete nici o component care s respecte aceast cerin atunci obiectul Container este returnat. Dac aceste coordonate sunt n afara Container-ului atunci se returneaz null.
public Component getComponentAt (Point p)

36

Aceast metod este identic cu cea anterioar, doar c locaia se specific printr-un obiect de tip Point. Asculttori
public synchronized void addContainerListener(ContainerListener listener)

Metoda nregistreaz un obiect listener interesat de a primi notificri atunci cnd un obiect de tip ContainerEvent trece prin coada de evenimente la care Container-ul curent este abonat. Atunci cnd aceste evenimente au loc, metodele listener.componentAdded() sau listener.componentRemoved() sunt apelate. Evident c i ali asculttori pot fi de asemenea nregistrai. n codul ce urmeaz a fi prezentat, este exemplificat folosirea unui ContainerListener pentru a nregistra asculttori pentru toate butoanele adugate unui applet. Ceea ce face ca acest cod s funcioneze, este apelul func iei enableEvents() care are ca efect livrarea evenimentelor n absena asculttorilor.
/* <applet code="UsingContainer" width=300 height=50> </applet> */ import java.awt.*; import java.applet.*; import java.awt.event.*; public class UsingContainer extends Applet implements ActionListener { Button b; public void init() { enableEvents (AWTEvent.CONTAINER_EVENT_MASK); add (b = new Button ("One")); add (b = new Button ("Two")); add (b = new Button ("Three")); add (b = new Button ("Four")); } protected void processContainerEvent (ContainerEvent e) { if (e.getID() == ContainerEvent.COMPONENT_ADDED) { if (e.getChild() instanceof Button) { Button b = (Button)e.getChild(); b.addActionListener (this); } } }

37

public void actionPerformed (ActionEvent e) { System.out.println ("Selected: " + e.getActionCommand()); } }

public void removeContainerListener(ContainerListener listener)

Aceast metod terge un listener, adic va face ca acel obiect s nu mai fie abonat. Dac listener nu este nregistrat, nu se ntmpl nimic.
protected void processEvent(AWTEvent e)

Metoda processEvent()primete toate evenimentele AWTEvent ce au Container-ul curent drept int. Metoda le va transmite tuturor asculttorilor abonai pentru procesarea ulterioar. Se poate suprascrie aceast metod pentru a preprocesa evenimentele nainte de a le transmite mai departe.
protected void processContainerEvent(ContainerEvent e)

Metoda processContainerEvent,folosit n programul de mai sus, va primi toate evenimentele de tip ContainerEvent ce au ca int Container-ul curent. Apoi, aceste evenimente se vor transmite la orice asculttor pentru procesare ulterioar. Suprascrierea metodei permite preprocesarea evenimentelor nainte ca acestea s fie retransmise.

Panel
Aceast clas ofer un Container generic pentru afiarea unei suprafee. Este cel mai simplu dintre toate Container-ele, fiind doar o suprafa rectangular. Constructorii acestei clase sunt:
public Panel () public Panel (LayoutManager layout)

Ultimul constructor permite setarea unui manager de aspect iniial i anume layout. Evenimentele acestei clase sunt n concordan cu cele amintite la clasa Container, din moment ce Panel motenete un Container. Mai jos avem un exemplu simplu pentru a nelege aceast clas.
import java.awt.*; import java.awt.event.*; public class PanelTest extends Frame {

38

private private private private

Button copyButton; Button cutButton; Button pasteButton; Button exitButton;

public PanelTest() { super("Test Panel"); setSize(450, 250); addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } } ); Panel toolbarPanel = new Panel(); toolbarPanel.setBackground(new Color(20, 20, 10)); toolbarPanel.setLayout(new FlowLayout(FlowLayout.CENTER)); copyButton = new Button("Copy"); toolbarPanel.add(copyButton); cutButton = new Button("Cut"); toolbarPanel.add(cutButton); pasteButton = new Button("Paste"); toolbarPanel.add(pasteButton); add(toolbarPanel, BorderLayout.EAST); // Bottom Panel Panel bottomPanel = new Panel(); bottomPanel.setBackground(new Color(100, 120, 10)); exitButton = new Button("Exit"); bottomPanel.add(exitButton); add(bottomPanel, BorderLayout.SOUTH); } public static void main(String[] args) { PanelTest mainFrame = new PanelTest(); mainFrame.setVisible(true); } }

De remarcat faptul c Panel-ul nu comport limitatori de margine, aa c diferenierea vizual sa fcut prin culoarea de fundal.

39

Window
Un Window este o suprafa de afiare de top n afara unui browser sau a unui applet. Frame este o subclas a Window ce conine limite, bara de titlu, etc. n mod normal Window se folosete pentru a permite crearea meniurilor pop-up sau a altor componente ce necesit acest spaiu. Clasa are o serie de metode ce influeneaz aparena ferestrei reprezentate.
public Window (Frame parent)

Este constructorul ce poate specifica printele ferestrei, adic n cadrul crei ferestre va activa aceast fereastr. Atunci cnd printele este minimizat, acelai lucru se ntmpl cu copilul.
public void show ()

Metoda show afieaz fereastra. Atunci cnd fereastra este creat, ea este implicit ascuns. Pentru a aduce n prim plan fereastra, se poate apela metoda toFront().
public void dispose ()

Aceast metod dealoc resursele ferestrei, ascunznd-o i apoi elibernd memoria. Se va genera un eveniment WindowEvent cu id-ul WINDOW_CLOSED.
public void toFront () public void toBack ()

Cele dou metode aduc fereastra n prim plan respectiv n fundal.


public Toolkit getToolkit ()

Metoda returneaz Toolkit-ul curent al ferestrei, adic obiectul ce ofer informaii despre platforma pe ca ruleaz programul. Aceasta va permite redimensionarea ferestrei sau alegerea de imagini pentru aplicatie, etc. Metodele pentru captarea sau denregistrarea de la evenimentele unei ferestre sunt:
public void addWindowListener(WindowListener listener) public void removeWindowListener(WindowListener listener)

De asemenea pentru a procesa evenimentele legate de o fereastr avem:


protected void processEvent(AWTEvent e) protected void processWindowEvent(WindowEvent e)

Pentru a exemplifica conceptele mai sus menionate avem urmtorul program:

40

import java.awt.*; public class WindowTest extends Frame { Window w = new PopupWindow (this); WindowTest () { super ("Window Example"); resize (250, 100); show(); } public static void main (String args[]) { Frame f = new WindowTest (); } public boolean mouseDown (Event e, int x, int y) { if (e.modifiers == Event.META_MASK) { w.move (location().x+x, location().y+y); w.show(); return true; } return false; } } class PopupWindow extends Window { PopupWindow (Frame f) { super (f); Panel p = new Panel (); p.add (new Button ("About")); p.add (new Button ("Save")); p.add (new Button ("Quit")); add ("North", p); setBackground (Color.gray); pack(); } public boolean action (Event e, Object o) { if ("About".equals (o)) System.out.println ("About"); else if ("Save".equals (o)) System.out.println ("Save Me"); else if ("Quit".equals (o)) System.exit (0); //fereastra este acunsa la //apasarea oricarui buton hide(); return true; } }

41

AWT II
Componente ....................................................................................................................................... 2 Clasa Frame ..................................................................................................................................... 2 Cum construim din Window o noua component ............................................................................ 5 Dialoguri .......................................................................................................................................... 6 Clasa FileDialog .............................................................................................................................. 11 Layout ............................................................................................................................................... 16 Interfaa LayoutManager ............................................................................................................... 16 Interfaa LayoutManager2 ............................................................................................................. 17 FlowLayout .................................................................................................................................... 18 BorderLayout ................................................................................................................................. 22 Componente tip lista ......................................................................................................................... 25 Choice ........................................................................................................................................... 25 Liste ............................................................................................................................................... 28 Meniuri ............................................................................................................................................. 33 MenuComponent .......................................................................................................................... 34 MenuContainer ............................................................................................................................. 35 MenuShortcut ............................................................................................................................... 36 MenuItem...................................................................................................................................... 36 Menu ............................................................................................................................................. 38 MenuBar ....................................................................................................................................... 39

Componente

Clasa Frame Frame-ul este un fel de Window care permite adugarea unui MenuBar, a titlului ferestrei, i altor caracteristici ale unei ferestre (cum ar fi redimensionarea, maximizarea, minimizarea, meniu de fereastr etc). Constantele unui Frame Clasa conine o serie de constante pentru a specifica tipul cursorului. n acest scop se poate folosi metoda Component.setCursor() pentru a schimba cursorul mouse-ului atunci cnd acesta se afl deasupra frame-ului. Mai jos este o list cu aceste constante:
public public public public public public public public public public public public public public final final final final final final final final final final final final final final static static static static static static static static static static static static static static int int int int int int int int int int int int int int DEFAULT_CURSOR CROSSHAIR_CURSOR TEXT_CURSOR WAIT_CURSOR SW_RESIZE_CURSOR SE_RESIZE_CURSOR NW_RESIZE_CURSOR NE_RESIZE_CURSOR N_RESIZE_CURSOR S_RESIZE_CURSOR W_RESIZE_CURSOR E_RESIZE_CURSOR HAND_CURSOR MOVE_CURSOR

Constructorii clasei Frame


public Frame ()

Constructorul implicit creeaz o fereastr ascuns cu numele Untitled sau un String gol. Ca i n cazul clasei Window, managerul de aspect implicit, al Frame-ului comport cursorul BorderLayout. DEFAULT_CURSOR. Deoarece Frame este ascuns la creare, trebuie apelat metoda show() sau setVisible(true) pentru a permite vizualizarea acesteia.
public Frame (String title)

Aceast versiune de constructor este identic primei, dar permite setarea titlului ferestrei prin specificarea parametrului de tip String. 2

Metodele clasei Frame


public String getTitle () public void setTitle (String title)

Metoda getTitle()returneaz titlul Frame-ului curent, iar setTitle() modific titlul Frame-ului curent.
public Image getIconImage () public void setIconImage (Image image)

Metoda getIconImage() returneaz imaginea folosit ca iconi a ferestrei. Iniial, aceasta este null, dar poate fi setat ulterior. Pe unele platforme, acest concept nu este implementat. A doua metod permite modificarea acestei iconie.
public MenuBar getMenuBar () public synchronized void setMenuBar (MenuBar bar)

Prima metod returneaz bara de meniu a Frame-ului curent. A doua metod modific bara de meniu cu parametrul bar. Se poate ca o aplicaie s aib mai multe bare de meniu, aceasta depinznd de context.
public synchronized void remove (MenuComponent component)

Aceast metod terge componenta specificat ca parametru, din cadrul Frame-ului, n cazul n care component este bara de meniuri a Frame-ului. Aceasta este echivalent cu apelul metodei setMenuBar cu parametru null.
public synchronized void dispose ()

Metoda dispose() elibereaz resursele folosite de Frame. Dac vre-una din clasele Dialog sau Window sunt asociate acestui Frame, atunci i resursele acestora sunt de asemenea eliberate. Se obinuiete ascunderea ferestrei nainte de eliberarea resurselor acesteia, pentru ca utilizatorii sa nu realizeze acest proces.
public boolean isResizable () public void setResizable (boolean resizable)

Prima metod returneaz faptul c Frame-ul curent este sau nu redimensionabil. A doua metod modific starea Frame-ului. O valoare true a parametrului nseamn c starea ferestrei este redimensionabil n timp ce false semnific faptul c utilizatorul nu poate redimensiona fereastra.
public void setCursor (int cursorType) public int getCursorType ()

Metoda setCursor permite specificarea cursorul Frame-ului curent. 3

Variabila cursorType trebuie s fie una din constantele de mai sus. Dac se specific alt valoare n locul acestor constante, va fi aruncat excepia IllegalArgumentException. A doua metod permite preluarea cursorului actual.
public synchronized void addNotify ()

Aceast metod creeaz un peer pentru Frame-ul curent. Fiecare component de interfa AWT are propriul ei peer. Un peer este implementarea acelei componente n mediul nativ. De exemplu, componenta Choice corespunde unor obiecte ce permit utilizatorului s selecteze un obiect dintr-o list. Metoda addNotify creeaz peer pentru Frame automat, la apelul metodei show(). Se poate suprascrie aceast metod ns trebuie s apelm mai nti super.addNotify(). Evenimentele Frame-ului Evenimentele care pot fi generate de un Frame, sunt cele generate de Component, deoarece Frame motenete aceast clas. Pe lng acestea, Frame poate genera evenimente de tip Window. Acestea sunt: WINDOW_DESTROY, WINDOW_EXPOSE, WINDOW_ICONIFY, WINDOW_DEICONIFY, i WINDOW_MOVED. Un eveniment des ntlnit, WINDOW_DESTROY, este generat atunci cnd utilizatorul ncearc s nchid Frame-ul, selectnd Quit, sau Exit din meniu. Implicit acest eveniment nu face nimic. Trebuie s oferii un handler, adic o metod care s trateze acest eveniment.
enableEvents (AWTEvent.WINDOW_EVENT_MASK);

Metoda garanteaz primirea tuturor evenimentelor de fereastr, chiar dac nu sunt asculttori abonai. Pentru a nchide o fereastr se poate folosi metoda processWindowEvent i n cazul Frame-ului:
protected void processWindowEvent(WindowEvent e) { if (e.getID() == WindowEvent.WINDOW_CLOSING) { //Notificm si pe altii c se inchide if (windowListener != null) windowListener.windowClosing(e); System.exit(0); } else { super.processEvent(e); } }

n cazul n care uitm s apelm metoda enableEvents,se poate ca metoda processWindowEvent s nu fie apelat niciodat, astfel c nchiderea ferestrei nu va avea loc.

Cum construim din Window o noua component Am discutat despre clasa Window i clasa Frame, iar acum putem s vedem cum sunt folosite n exemplul de mai jos. Construim o serie de butoane pop-up. De asemenea este folosit Toolkit-ul unui Frame pentru a ncrca imagini ntr-o aplicaie. Butoanele apar atunci cnd utilizatorul apas butonul dreapta al mouse-ului, asemntor exemplului din cursul precedent.
import java.awt.*; import java.awt.image.ImageObserver; public class PopupButtonFrame extends Frame implements ImageObserver { Image im; Window w = new PopupWindow (this); PopupButtonFrame () { super ("PopupButton Example"); im = getToolkit().getImage ("1.jpg"); MediaTracker mt = new MediaTracker (this); mt.addImage (im, 0); Dimension d = new Dimension(400,400); this.resize(d); this.show(); try { mt.waitForAll(); } catch (Exception e) {e.printStackTrace(); } } public static void main (String args[]) { Frame f = new PopupButtonFrame (); } public void paint (Graphics g) { if (im != null) g.drawImage (im, 20, 20, this); } public boolean mouseDown (Event e, int x, int y) { if (e.modifiers == Event.META_MASK) { w.setLocation(getLocation().x+x, getLocation().y+y); w.setVisible(true); return true; } return false; }

} class PopupWindow extends Window { PopupWindow (Frame f) { super (f); Panel p = new Panel (); p.add (new Button ("About")); p.add (new Button ("Save")); p.add (new Button ("Quit")); add ("North", p); setBackground (Color.gray); pack(); } public boolean action (Event e, Object o) { if ("About".equals (o)) System.out.println ("About"); else if ("Save".equals (o)) System.out.println ("Save Me"); else if ("Quit".equals (o)) System.exit (0); hide(); return true; } }

Noutatea fa de programul din cursul anterior, este c, ncrcm o imagine n cadrul Frameului, folosind un obiect de tip Image ce este desenat pe metoda de paint(). Dialoguri Clasa Dialog ofer implementarea unei ferestre speciale, care este n mod normal, folosit la mesaje pop-up pentru ca utilizatorul s poat introduce date. Ea trebuie asociat cu Frame, i ori de cte ori ceva se ntmpl Frame-ului, acelai eveniment va avea loc i pe Dialog. De exemplu, dac printele Frame are iconi, Dialog-ul dispare pn cnd iconia este tears din Frame. Dialog-ul poate fi modal sau amodal. Un Dialog amodal este acela n care utilizatorul poate interaciona cu Frame-ul i cu Dialogul n acelai timp. Un Dialog modal este acela care blocheaz orice interaciune cu alte componente ale aplicaiei, inclusiv Frame-ul asociat cu acesta. n figura de mai jos este exemplificat un astfel de dialog.

Figura 11.1 Exemplu de Dialog Constructor i metode


public public public public Dialog Dialog Dialog Dialog (Frame (Frame (Frame (Frame parent) parent, boolean modal) parent, String title) parent, String title, boolean modal)

Primul constructor creeaz o instan de Dialog, fr titlu cu printele dat de parent. Acest dialog nu este modal i este redimensionabil. Al doilea constructor specific printele prin parametrul parent. Se poate specifica i faptul c este sau nu modal. Iniial, dialogul este redimensionabil. Al treilea constructor permite specificarea titlului dialogului. Dialogul nu este modal. Al patrulea constructor este cel mai complex i permite specificarea att a titlului ct i a faptului c este sau nu modal.
public String getTitle () public void setTitle (String title)

Cele dou metode permit aflarea titlului dialogului curent, respectiv modificarea titlului.
public boolean isResizable () public void setResizable (boolean resizable)

Aceste dou metode permit aflarea faptului c dialogul curent este redimensionabil sau nu, respectiv setarea acestui fapt.
public boolean isModal () public void setModal (boolean mode)

Aceste dou metode permit interogarea/setarea faptului c Dialog-ul curent este modal sau nu. Data viitoare cnd dialogul este afi at folosind metoda show(), dac este setat modal, doar el va fi activ, n caz contrar i alte componente sunt accesibile. Evenimentele unui Dialog sunt aceleai ca i cele ale unui Frame.

import java.awt.*; interface DialogHandler { void dialogCreator (Object o); } class modeTest extends Dialog { TextField user; TextField pass; modeTest (DialogHandler parent) { //crearea unui dialog cu parinte, parent //nume Mode Test, modal super ((Frame)parent, "Mode Test", true); //adaugarea unei etichete add("North", new Label ("Please enter username/password")); //adaugarea unui panel cu doua etichete Panel left = new Panel (); left.setLayout (new BorderLayout ()); left.add ("North", new Label ("Username")); left.add ("South", new Label ("Password")); //panelul este adugat la stanga add ("West", left); //alt panel cu doua campuri pentru //introducerea unui text Panel right = new Panel (); right.setLayout (new BorderLayout ()); user = new TextField (15); pass = new TextField (15); //parola este ascunsa utilizatorului pass.setEchoCharacter ('*'); right.add ("North", user); right.add ("South", pass); //panelul este adaugat la stanga add ("East", right); add ("South", new Button ("Okay")); resize (250, 125); } public boolean handleEvent (Event e) { if (e.id == Event.WINDOW_DESTROY) { dispose(); return true; } else if ((e.target instanceof Button) &&

(e.id == Event.ACTION_EVENT)) { //cand are loc un eveniment este transmis //parintelui pentru afisarea datelor citite ((DialogHandler)getParent ()).dialogCreator(e.arg); } return super.handleEvent (e); } } public class modeFrame extends Frame implements DialogHandler { modeTest d; modeFrame (String s) { super (s); resize (100, 100); d = new modeTest (this); d.show (); } public static void main (String []args) { Frame f = new modeFrame ("Frame"); } public boolean handleEvent (Event e) { if (e.id == Event.WINDOW_DESTROY) { hide(); dispose(); System.exit (0); } return super.handleEvent (e); } public void dialogCreator(Object o) { // crearea unui dialog // in care sunt adaugate ca etichete // valorile text ale membrilor lui modeTest d.dispose(); add ("North", new Label (d.user.getText())); add ("South", new Label (d.pass.getText())); show (); } }

Explicaiile despre modul cum funcioneaz acest program sunt date ca i comentariu. Dup cum se poate observa, tratarea evenimentelor i unele modele conceptuale sunt conform modelului Java 1.0, aa c acest exemplu este mai mult informativ, nu trebuie utilizat la implementare. 9

Mai jos este un exemplu, de asemenea cu explicaii, n care tratarea evenimentelor i modul de lucru este mai aproape de cel versiunilor actuale de JDK.
import java.awt.*; //se renunta la interfata DialogHandler class modeTest11 extends Dialog { TextField user; TextField pass; //Crearea GUI al aplicatiei modeTest11 (Frame parent) { super (parent, "Mode Test", true); add ("North", new Label ("Please enter username/password")); //adaugarea panel-elor in mod asemanator Panel left = new Panel (); left.setLayout (new BorderLayout ()); left.add ("North", new Label ("Username")); left.add ("South", new Label ("Password")); add ("West", left); Panel right = new Panel (); right.setLayout (new BorderLayout ()); user = new TextField (15); pass = new TextField (15); pass.setEchoCharacter ('*'); right.add ("North", user); right.add ("South", pass); add ("East", right); add ("South", new Button ("Okay")); resize (250, 125); } //suprascrierea functiei de tratare a //evenimentelor de fereastra public boolean handleEvent (Event e) { if (e.id == Event.WINDOW_DESTROY) { dispose(); return true; } else if ((e.target instanceof Button) && (e.id == Event.ACTION_EVENT)) { hide(); } return super.handleEvent (e); } } //fereastra ce afiseaza datele public class modeFrame11 extends Frame

10

{ modeFrame11 (String s) { super (s); resize (100, 100); } public static void main (String []args) { Frame f = new modeFrame11 ("Frame"); modeTest11 d; //dialogul este afisat primul //el este "pornit" de frame d = new modeTest11 (f); //f.show (); //ce se intampla daca as decomenta instructiunea //de mai sus. De ce nu am acces la frame? //Pentru ca dialogul este modal.Daca ar fi amodal? d.show (); d.dispose(); //dupa ce dialogul este inlaturat //este afisat frame-ul f.add ("North", new Label (d.user.getText())); f.add ("South", new Label (d.pass.getText())); f.show (); } //aici sunt tratate evenimentele frame-ului public boolean handleEvent (Event e) { if (e.id == Event.WINDOW_DESTROY) { hide(); dispose(); System.exit (0); } return super.handleEvent (e); } }

Clasa FileDialog Aceasta este o subclas a Dialog-ului ce permite selectarea fiierelor sau salvarea acestora. FileDialog este ntotdeauna un Dialog modal, cu alte cuvinte aplicaia apelant va fi blocat pn la selectarea unui fiier sau pn la nchiderea dialogului de fiier.

11

Metodele clasei FileDialog Un FileDialog are dou moduri: unul pentru ncrcarea fiierelor i unul pentru salvarea acestora. Urmtoarele variabile descrise ofer posibilitatea specificrii acestui mod de lucru. Diferena vizibil este c pe ecran va apare Load sau Save.
public final static int LOAD public final static int SAVE

n continuare vom descrie constructorii acestei clase


public FileDialog (Frame parent) public FileDialog (Frame parent, String title) public FileDialog (Frame parent, String title, int mode)

Primul constructor creeaz un dialog pentru fiiere ce are specificat Frame-ul printe ca parametru. Titlul este iniial gol. Al doilea constructor permite specificarea i a titlului iniial. Al treilea constructor permite specificarea, pe lng parametrii descrii, i a modului dialogului: Save sau Load.
public String getDirectory () public void setDirectory (String directory)

Aceste dou metode permit preluarea sau setarea a directorului curent n care se va face cutarea fiierului. Metoda setDirectory trebuie apelat nainte de afiarea dialogului.
public String getFile () public void setFile (String file)

Unele dintre cele mai importante metode, acestea permit preluarea numelui fi ierului selectat (n cazul metodei getFile). Metoda setFile permite setarea numelui fiierului selectat implicit la afiarea dialogului.
public FilenameFilter getFilenameFilter () public void setFilenameFilter (FilenameFilter filter)

Metoda getFilenameFilter permite preluarea filtrului de nume al fiierului. Clasa FilenameFilter face parte din pachetul java.io i permite limitarea cutrii unui fiier dup diverse filtre. De exemplu, dac filtrm dup extensie, putem avea un filtru compus din .jpg, .gif, sau .mpeg. A doua metod permite setarea acestui filtru pentru dialogul ce va fi afi at.
public int getMode () public void setMode (int mode)

Aceste dou metode permit preluarea / setarea modului de lucru: Save sau Load. 12

Mai jos avem un exemplu pentru folosirea acestor tipuri de dialog, foarte folositoare n cazul aplicaiilor ce lucreaz cu fiiere.
import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.*; public class FileDialogTest extends Frame implements ActionListener { //zona de editarea textului TextArea myTextArea; Label myLabel; //butonul pentru deshiderea dialocului Load Button loadButton; //butonul pentru deshiderea dialocului Save Button saveButton; //se construieste fereastra de editare text FileDialogTest () { super ("File Dialog Tester"); Panel p = new Panel (); p.add (loadButton = new Button ("Load")); p.add (saveButton = new Button ("Save")); //butoanele sunt abonate la evenimentul //de apasare loadButton.addActionListener(this); saveButton.addActionListener(this); add ("North", myLabel = new Label ()); add ("South", p); add ("Center", myTextArea = new TextArea (10, 40)); pack(); } public static void main (String args[]) { //instantierea ferestrei principale FileDialogTest f = new FileDialogTest(); //si afisarea acesteia f.show(); } public boolean handleEvent (Event e) { if (e.id == Event.WINDOW_DESTROY) { hide(); dispose (); System.exit(0); return true; // never gets here }

13

return super.handleEvent (e); } //cand apare un eveniment public void actionPerformed(ActionEvent e) { //daca sursa evenimentului este de un buton if (e.getSource() instanceof Button) { int state; String msg; //daca este cel de Load if (e.getSource() == loadButton) { state = FileDialog.LOAD; msg = "Load File"; } else //sau este cel de Save {// if (e.target == saveButton) state = FileDialog.SAVE; msg = "Save File"; } //Se deschide un dialog de fisier //cu parintele this FileDialog file = new FileDialog (this, msg, state); file.setFile ("*.java"); // filtrul este extensia java file.show(); // afisarea dialogului si asteptare String curentFile; byte[] data = null; //daca s-a selectat un fisier se va prelua numele lui if ((curentFile = file.getFile()) != null) { //se compune calea completa a fisierului String filename = file.getDirectory() + curentFile; setCursor (Frame.WAIT_CURSOR); //daca dialogul a fost de Load if (state == FileDialog.LOAD) { try { //se incearca deschiderea fisierului //si citirea datelor din el data =ReadFile(filename); } catch (FileNotFoundException exc) { System.out.println("File Not Found: " + filename); } catch (IOException exc)

14

{ System.out.println("IOException: " + filename); } myLabel.setText ("Load: " + filename); } else//trebuie sa salvam... { //Stergem".*.*" daca apare acest text //inseamna ca fisierul nu exista if (filename.indexOf (".*.*") != -1) { filename = filename.substring (0, filename.length()-4); } try { //se incearca scrierea in fisier data = WriteInFile(filename); } catch (IOException exc) { System.out.println("IOException: " + filename); } myLabel.setText ("Save: " + filename); //numele fisierului este afisat la sfarsit } //daca s-a scris sau s-a citit corect if (data!=null) //putem plasa acea informatie in casuta de text myTextArea.setText (new String (data, 0)); //revenim la cursorul initial setCursor (Frame.DEFAULT_CURSOR); } } } //metoda citeste din fisierul dat ca parametru //si returneaza ceea ce a reusit sa citeasca public byte[] ReadFile(String filename) throws IOException { File f = new File (filename); byte[] data; FileInputStream fin = new FileInputStream (f); int filesize = (int)f.length(); data = new byte[filesize]; fin.read (data, 0, filesize); return data; } //metoda scrie din fisierul dat ca parametru //si returneaza ceea ce a reusit sa scrie

15

public byte[] WriteInFile(String filename) throws IOException { byte[] data; File f = new File (filename); FileOutputStream fon = new FileOutputStream (f); String text = myTextArea.getText(); int textsize = text.length(); data = new byte[textsize]; //se scrie in data ce se afla in myTextArea text.getBytes (0, textsize, data, 0); fon.write (data); fon.close (); return data; } }

Aplicaia permite introducerea ntr-o fereastr ca cea de mai jos a unui text, care poate fi ncrcat dintr-un fiier sau poate salva textul editat ntr-un fiier. Explicaiile despre cum funcioneaz acest program se gsesc sub forma unor comentarii inserate n dreptul instruciunilor.

Layout
Fiecare Container are un manager de layout ( adic al aspectului componentelor ). Acesta este responsabil pentru poziionarea componentelor n cadrul recipientului, indiferent de platform sau mrimea ecranului. Practic aceste clase elimin necesitatea de a calcula poziia n care plasm o component. Chiar i pentru o component simpl cum ar fi un buton, calcularea poziiei i a dimensiunilor relative la fereastra curent, se poate ntinde pe zeci poate sute de linii de cod. n continuare vom descrie cteva managere de aspect care uureaz poziionarea acestor componente pe ecran. Interfaa LayoutManager Aceast interfa definete responsabilitile unei clase ce va afia componente din cadrul unui Container. Sarcina acestei interfee este s determine poziia i mrimea fiecrei componente din cadrul Container-ului. Nu se apela direct metoda unui LayoutManager, deoarece apelurile acestor clase sunt ascunse de programatori. Metodele acestei interfee se pot suprascrie atunci cnd implementm un nou manager de aspect, diferit de cele oferite de Java. Metodele interfeei LayoutManager
public abstract void addLayoutComponent (String name, Component component)

16

Aceast metod este apelat cnd programul atribuie un nume componentei atunci cnd o adaug Layout-ului (de exemplu cnd apeleaz add(String,Component) sau add(Component,Object)).
public abstract void removeLayoutComponent (Component component)

Metoda va terge componenta component din memorie. Aceasta este apelat de obicei la eliberarea Container-ului n care se afl componenta.
public abstract Dimension preferredLayoutSize (Container parent)

Metoda determin mrimea preferat a componentelor din cadrul Container-ului. Metoda va returna un obiect Dimension ce conine limea i lungimea necesar afirii componentelor.
public abstract Dimension minimumLayoutSize (Container parent)

Metoda minimumLayoutSize este apelat pentru a determina mrimea minim a componentelor din cadrul Container.
public abstract void layoutContainer (Container parent)

Aceast metod ajut la poziionarea tuturor componentelor ale parent . Interfaa LayoutManager2 Odat cu introducerea Java 1.1, au aprut modificri interfeei mai sus menionat. Noua interfa rezolv o problem care apare atunci cnd lucrm cu GridBagLayout. Dei metoda addLayoutComponent(String, Component), funcioneaz bine pentru BorderLayout i CardLayout, nu poate fi folosit pentru GridBagLayout. Poziia unei componente ntr-un GridBagLayout este controlat de un numr de constrngeri, care sunt ncapsulate n obiectul de tip GridBagConstraints. Pentru a asocia aceste constrngeri, trebuie apelat metoda setConstraints(). Interfaa LayoutManager2 definete o versiune a metodei addLayoutComponent() ce poate fi folosit de toi managerii de aspect, indiferent de constrngerile particulare. Metoda are un parametru de tip Object care poate reprezenta orice tip de informaie, cum ar fi tipul de constrngeri de mai sus. Metodele interfeei LayoutManager2
public abstract void addLayoutComponent(Component comp, Object constraints)

Metoda este apelat cnd programul asigneaz constraints componentei comp atunci cnd o adaug layout-ului. Este treaba layout-ului s decid ce va face cu constraints.
public abstract Dimension maximumLayoutSize(Container target)

Aceast metod returneaz mrimea maxim a target n cadrul acestui manager de layout. 17

Anterior, doar mrimea minim sau cea preferat se putea obine.


public abstract float getLayoutAlignmentX(Container target) public abstract float getLayoutAlignmentY(Container target)

Metodele returneaz aliniamentul target de-a lungul axei X respectiv Y. Valoarea returnat este ntre 0.0 i 1. Valorile apropiate de 0 nseamn o poziionare mai aproape de stnga, dac vorbim de prima metod, n timp ce valorile mai aproape de 1 semnific o poziionare mai la dreapta. Dac ne referim la a doua metod, o valoare aproape de 0 nseamn o poziionare mai sus, n timp ce o valoare mai aproape de jos.
public abstract void invalidateLayout(Container target)

Metoda spune managerului de aspect c orice informaie de layout, care are ca int target este invalidat. FlowLayout Acesta este managerul de Layout implicit pentru un Panel. Un FlowLayout adaug componente container-ului curent, pe rnduri, de la stnga la dreapta. Dac nu se mai gsesc componente n rnd, ca ncepe un nou rnd. Atunci cnd Container-ul este redimensionat, componentele sunt repozi ionate pe acelai principiu.

public final static int LEFT public final static int CENTER public final static int RIGHT

Acestea sunt constantele acestei clase, constante ce returneaz aliniamentul. Metodele FlowLayout

public FlowLayout () public FlowLayout (int alignment) public FlowLayout (int alignment, int hgap, int vgap)

Primul constructor creeaz un layout de acest tip, folosind setri implicite, aliniament centrat cu o pauz orizontal i vertical ntre componente de cinci pixeli. De obicei acest constructor este apelat n cadrul metodei setLayout():setLayout (new FlowLayout()). Al doilea constructor permite setarea aliniamentului pentru componentele ad ugate. Astfel n figura de mai jos avem exemple pentru cele trei tipuri de aliniamente.

18

Figura 11.2 FlowLayout ( A) stnga B) centrat C) dreapta) Al treilea constructor, permite setarea spaiului dintre componente, pe orizontal i pe vertical. n figura de mai jos avem o reprezentarea a acestui tip de aliniament.

Figura 11.3 Aliniament cu hgap de 0 si vgap de 20

public int getAlignment () public void setAlignment (int alignment)

Cele dou metode permit returnarea, respectiv setarea aliniamentului layout-ului curent. Aliniamentul poate fi una din cele trei constante mai sus menionate.
public public public public int getHgap () void setHgap (int hgap) int getVgap () void setVgap (int hgap)

Aceste metode permit preluarea respectiv setarea spaiului vertical i orizontal dintre componente. Dup apelul metodei setVgap sau setHgap trebuie apelat metoda de validate() a Container-ului. ncepnd cu versiunea 1.2, Java admite alte dou constante pentru orientare i anume TRAILING i LEADING. 19

n exemplul de mai jos se poate nelege cum se efectueaz afiarea componentelor pe rnduri. Pentru o mai bun nelegere a acestor aliniamente recomand redimensionarea ferestrei.
import java.awt.*; import java.awt.event.*; public class FlowLayoutDemo extends Frame implements ActionListener { //panel-ul unde vor fi plasate butoanele final Panel compsToExperiment = new Panel(); //cele doua butoane de jos //care vor decide aliniamentul celor de sus Button RtoLbutton; Button LtoRbutton; //manager-ul de layout principal FlowLayout experimentLayout = new FlowLayout(); final String RtoL = "Right to left"; final String LtoR = "Left to right"; public boolean handleEvent(Event e) { //daca evenimentul este cel de inchidere a ferestrei if (e.id == Event.WINDOW_DESTROY) { dispose(); System.exit(1); } return super.handleEvent (e); } public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); //Check the selection if (command.equals("Left to right")) { //setarea orientarii stanga -> dreapta compsToExperiment.setComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT); } else { //setarea orientarii stanga <- dreapta compsToExperiment.setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT); } //update al layout-ului panelului compsToExperiment.validate(); compsToExperiment.repaint(); }

20

//constructorul Frame-ului public FlowLayoutDemo(String name) { super(name); addComponentsToFrame(); } public void addComponentsToFrame() { //panelul primeste ca layout experimentLayout compsToExperiment.setLayout(experimentLayout); experimentLayout.setAlignment(FlowLayout.TRAILING); //in acest panel vor sta butoanele din //partea de jos a ferestrei Panel controls = new Panel(); controls.setLayout(new FlowLayout()); LtoRbutton = new Button(LtoR); LtoRbutton.setActionCommand(LtoR); RtoLbutton = new Button(RtoL); RtoLbutton.setActionCommand(RtoL); //se aboneaza la ascultarea actiunilor LtoRbutton.addActionListener(this); RtoLbutton.addActionListener(this); //adaug butoane panelului compsToExperiment compsToExperiment.add(new Button("Button 1")); compsToExperiment.add(new Button("Button 2")); compsToExperiment.add(new Button("Button 3")); compsToExperiment.add(new Button("Long-Named Button 4")); compsToExperiment.add(new Button("5")); //initial se seteaza orientarea stanga -> dreapta compsToExperiment.setComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT); controls.add(LtoRbutton); controls.add(RtoLbutton); //se adauga panelul compsToExperiment in centrul frame-ului this.add(compsToExperiment, BorderLayout.CENTER); //se adauga panelul controls in partea de jos a frame-ului this.add(controls, BorderLayout.SOUTH); } private static void createAndShowGUI() { //se instantiaza si ruleaza fereastra FlowLayoutDemo frame = new FlowLayoutDemo("FlowLayoutDemo"); frame.pack(); frame.setVisible(true); }

21

public static void main(String[] args) { createAndShowGUI(); } }

BorderLayout Acesta este managerul implicit pentru un Window. Ofer un mod uor de a poziiona componentele conform unor limite ale ferestrei, de unde vine i numele. Urmtorul apel: setLayout(newBorderLayout() modific managerul de aspect astfel nct layoutul s fie unul de tip BorderLayout. Constantele BorderLayout
public public public public public static static static static static final final final final final String String String String String CENTER EAST NORTH SOUTH WEST

Aceste constante vor indica poziia cardinal a locului unde va fi plasat pe Container, noua component. Metodele BorderLayout
public BorderLayout () public BorderLayout (int hgap, int vgap)

Primul constructor creeaz un BorderLayout folosind o setare de delimitatori de zero pixeli pe vertical i zero pixeli pe orizontal. Dimensiunea delimitatorului nseamn spaiul adiacent dintre componente. Al doilea constructor permite crearea unui BorderLayout cu delimitatori a cror dimensiune este specificat de parametrii lui. Se poate ca aceti parametrii s fie negativi, n cazul acesta componentele suprapunndu-se unele peste altele.
public public public public int getHgap () void setHgap (int hgap) int getVgap () void setVgap (int hgap)

Aceste metode permit preluarea/schimbarea dimensiunii delimitatorului pe orizontal respectiv pe vertical.

22

n general metodele acestui tip de Layout sunt aceleai ca i n cazul FlowLayout, difer doar comportamentul aspectului i anume faptul a acest Layout poziioneaz pe regiuni cardinale, componentele pe Container-ul pe care l trateaz.
import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; public class BorderLayoutDemo extends Frame { public static boolean RIGHT_TO_LEFT = false; public BorderLayoutDemo() { super("BorderLayout Demo"); //container-ul unde vom adauga toate //butoanele Panel allcomp = new Panel(); //neaparat sa aiba un layout de acest tip allcomp.setLayout(new BorderLayout()); //adaugam butoanele panelului addComponentsTo(allcomp); //panelul este adaugat frame-ului curent add(allcomp,BorderLayout.CENTER); //pentru evenimentul de inchidere al frame-ului addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } } ); } public static void addComponentsTo(Container pane) { if (!(pane.getLayout() instanceof BorderLayout)) { pane.add(new Label("Container doesn't use BorderLayout!")); return; } //orientarea este stanga -> dreapta if (RIGHT_TO_LEFT) { pane.setComponentOrientation( java.awt.ComponentOrientation.RIGHT_TO_LEFT); } //se adauga cele 5 butoane Button button = new Button("Button 1 (PAGE_START)"); pane.add(button, BorderLayout.PAGE_START); //componenta din centru este de obicei cea mai mare

23

button = new Button("Button 2 (CENTER)"); button.setPreferredSize(new Dimension(200, 100)); pane.add(button, BorderLayout.CENTER); button = new Button("Button 3 (LINE_START)"); pane.add(button, BorderLayout.LINE_START); button = new Button("Long-Named Button 4 (PAGE_END)"); pane.add(button, BorderLayout.PAGE_END); button = new Button("5 (LINE_END)"); pane.add(button, BorderLayout.LINE_END); } private static void createAndShowGUI() { //instantiez si afisez fereastra BorderLayoutDemo frame = new BorderLayoutDemo(); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { createAndShowGUI(); } }

Mai exist o serie de alte clase Layout: GridLayout ,CardLayout ce pot fi studiate mai departe. Aceste Layout-uri pot fi utilizate n combinaie, n funcie de context. Iat un mic exemplu de cum putem combina mai multe aspecte:
import java.awt.*; public class ManyLayouts extends java.applet.Applet { public void init() { Panel s = new Panel(); Panel e = new Panel(); setLayout (new BorderLayout ()); add ("North", new Label ("Enter text", Label.CENTER)); add ("Center", new TextArea ()); e.setLayout (new GridLayout (0,1)); e.add (new Button ("Reformat")); e.add (new Button ("Spell Check")); e.add (new Button ("Options")); add ("East", e);

24

s.setLayout (new FlowLayout ()); s.add (new Button ("Save")); s.add (new Button ("Cancel")); s.add (new Button ("Help")); add ("South", s); } }

Componente tip lista


Choice Vom discuta n continuare despre trei componente AWT: Choice, List i CheckBox. Toate cele trei clase implementeaz interfaa ItemSelectable. Choice i List sunt similare, ambele ofer posibilitatea alegerii unui obiect dintr-o list. Tipul de list este ns diferit, Choice fiind o list de tip pull-down iar List fiind una pe care poate fi efectuat aciunea de scroll. Metodele Choice
public Choice ()

Exist un singur constructor pentru aceast clas. Componenta, la creare, este iniial goal. La adugarea unui item, folosind addItem() sau add(), atunci acesta poate fi vizualizat i selectat.
public int getItemCount () public int countItems ()

Aceste metode returneaz obiectele selectabile din componenta Choice.


public String getItem (int index)

Aceast metod returneaz textul unui item de la pozi ia dat de index. Dac index este mai mic ca zero sau mai mare ca numrul de elemente selectabile atunci va fi aruncat excepia
ArrayIndexOutOfBoundsException. public synchronized void add (String item) public synchronized void addItem (String item)

Aceste metode permit adugarea unui nou element n lista Choice-ului. Dac parametrul este un null, atunci va fi aruncat excepia NullPointerException.
public synchronized void insert (String item, int index)

Metoda permite adugarea unui nou element la poziia indicat de index. Dac index este ilegal, atunci va fi aruncat excepia IllegalArgumentException.. 25

public synchronized void remove (String item) public synchronized void remove (int position) public synchronized void removeAll ()

Metodele permit tergerea unui element sau a tuturora din lista de elemente disponibile. Prima metod permite selectarea elementului de ters, dup numele dat ca parametru. A doua metod permite tergerea de la o anumit poziie, atta timp ct este una valid. Ultima metod va terge toate elementele din lista Choice.
public String getSelectedItem ()

Metoda getSelectedItem() returneaz elementul selectat, sub forma unui String. Dac Choice este goal funcia va returna null.
public Object[] getSelectedObjects ()

Metoda permite aflarea elementului selectat sub forma unui ir de Object. Metoda este impus de interfaa ItemSelectable, i este folositoare cu adevrat n cazul clasei List.
public int getSelectedIndex ()

Metoda returneaz poziia din list a elementului selectat. Primul element se afl pe poziia 0.
public synchronized void select (int position)

Aceast metod foreaz ca item-ul de pe poziia position s fie cel selectat.


public void select (String string)

Acelai efect este obinut doar c elementul este specificat prin numele su. Evenimentele Choice Evenimentul cel mai des ntlnit este cel de selectarea a unui element. Aceasta poate fi captat printr-un ItemListener. De asemenea se pot folosi evenimente de mouse sau tastatur.
public boolean keyDown (Event e, int key) public boolean keyUp (Event e, int key)

Metodele sunt apelate atunci cnd utilizatorul apas o tast i focusul este pe Choice. Prin obiectul e de tip Event se poate interoga date specifice despre eveniment, iar variabila key poate furniza date despre tasta apsat.

26

public void addItemListener(ItemListener listener) public void removeItemListener(ItemListener listener)

Poate cele mai importante metode, permit inregistrarea/denregistrarea unui obiect de tip Choice la/de la ascultarea evenimentelor de modificare a acestui obiect.
protected void processEvent(AWTEvent e)

Metoda este apelat atunci cnd un eveniment de tip AWTEvent are loc. Suprascrierea acestei este echivalent cu suprascrierea handleEvent() din vechea versiune Java. n exemplul de mai jos se poate observa adugarea unor elemente n lista Choice, precum i tratarea evenimentelor de selecie ce apar.
import java.awt.*; import java.applet.*; import java.awt.event.*; class MyChoice extends Choice { MyChoice () { super (); //trebuie sa //activam ascultarea evenimentelor enableEvents (AWTEvent.ITEM_EVENT_MASK); } //suprascriem processItemEvent protected void processItemEvent(ItemEvent e) { //atunci cand are loc un eveniment ItemSelectable ie = e.getItemSelectable(); System.out.println ("Item Selected: " + ie.getSelectedObjects()[0]); super.processItemEvent (e); } } public class ChoiceDemo extends Applet implements ItemListener { Choice c; public void init () { String []fonts; //preluam fonturile existente pe sistem //pentru a fi incarcare in Choice fonts = Toolkit.getDefaultToolkit().getFontList(); c = new MyChoice(); for (int i = 0; i < fonts.length; i++) { //metoda de adaugare de elemente //in ComboList

27

c.add (fonts[i]); } //componenta Choice este adaugata Applet-ului add (c); //ascultam un eveniment de selectie c.addItemListener (this); } //ce se intampla cand selectam un element public void itemStateChanged(ItemEvent e) { ItemSelectable ie = e.getItemSelectable(); System.out.println ("State Change: " + ie.getSelectedObjects()[0]); } }

Liste Clasa List ofer un mod de a afia o secven de elemente, din care utilizatorul poate selecta unul sau mai multe. n mod normal, un scrollbar este asociat acestei componente. Metode
public List () public List (int rows) public List (int rows, boolean multipleSelections)

Primul constructor creeaz o list goal cu patru linii vizibile. Utilizatorul poate selecta n acest caz, un singur element. Al doilea constructor permite specificarea numrului de linii vizibile, prin parametrul rows. Al treilea constructor permite de asemenea specificarea num rului de linii, i totodat, specificarea faptului c utilizatorul poate selecta mai multe elemente sau nu.
public int getItemCount () public int countItems ()

Cele dou metode returneaz numrul de elemente din list. A doua metod este numele metodei din versiunea veche.
public String getItem (int index)

Metoda returneaz reprezentarea String a item-ului de la poziia index.


public String[] getItems ()

Metoda returneaz un ir ce conine toate elementele din list. 28

Nu conteaz dac elementul este sau nu selectat.


public public public public synchronized synchronized synchronized synchronized void void void void add (String item) addItem (String item) add (String item, int index) addItem (String item, int index)

Primele dou metode permit adugarea unui item la sfritul listei. Ultimele dou elemente permit adugarea unui element pe o poziie specificat de index. Dac indexul este mai mic ca zero sau mai mare ca numrul de elemente de list, elementul este adugat la sfrit.
public synchronized void removeAll () public synchronized void clear ()

Metodele permit tergerea tuturor elementelor dintr-o list.


public synchronized void remove (String item) public synchronized void remove (int position) public synchronized void delItem (int position)

Prima metod permite tergerea unui element dup numele su, iar celelalte dou permit tergerea unui element dup poziia lui n list.
public synchronized int getSelectedIndex ()

O alt metod des folosit, getSelectedIndex() permite aflarea pozi iei n list a elementului selectat. Dac mai multe elemente sunt selectate, metoda returneaz valoarea -1.
public synchronized int[] getSelectedIndexes ()

Metoda returneaz un sir de ntregi ce reprezint indecii elementelor selectate.


public synchronized String getSelectedItem () public synchronized String[] getSelectedItems ()

Aceste metode sunt corespondentele metodelor de mai sus, returnnd numele elementelor selectate.
public synchronized Object[] getSelectedObjects ()

Metoda returneaz elementele selectate sub forma unui ir de obiecte, conform interfeei ItemSelectable. Dac nici un item nu este selectat, irul este gol.
public synchronized void select (int index) public synchronized void deselect (int index)

Metodele permit selectarea/deselectarea unui element de la pozi ia index. 29

Dac lista este setat pe selecie unic, la apelul metodei select(), elementul anterior selectat este automat deselectat. Dac lista este de tipul multiselecie, atunci metoda select() nu are acest efect. Metoda deselect() deselecteaz elementul de la poziia index.
public boolean isIndexSelected (int index) public boolean isSelected (int index)

Metodele permit interogarea unui element cu privire la faptul c este sau nu selectat.
public boolean isMultipleMode () public boolean allowsMultipleSelections ()

Metodele returneaz starea curent a listei, daca este de tipul multiselecie sau nu.
public void setMultipleMode (boolean value) public void setMultipleSelections (boolean value)

Metodele permit schimbarea strii curente a listei: dac valoarea parametrului este true atunci lista va trece n mod multiselecie, dac valoarea parametrului este false, atunci lista va fi cu selecie unic.
public int getRows ()

Metoda returneaz numrul de rnduri care a fost transmis la instanierea listei. Nu returneaz numrul de rnduri vizibile.
public Dimension getPreferredSize (int rows) public Dimension preferredSize (int rows)

Metodele returneaz dimensiunea preferat, pentru a ti nlimea rndurilor.


public Dimension getPreferredSize () public Dimension preferredSize ()

Metodele returneaz dimensiunea preferat a listei. Pentru a calcula aceast dimensiune, se vor folosi numrul de rnduri specificate la instanierea listei. Evenimentele listei Se poate nregistra un ItemListener cu metoda addItemListener() sau un ActionListener cu metoda addActionListener() pentru a asculta evenimentele ce apar pe aceast component. Exist totui o alt metod numit action() care nregistreaz evenimente aprute pe o list:
public boolean action (Event e, Object o)

30

Metoda este apelat atunci cnd utilizatorul efectueaz un dublu-click pe un item din list, iar o reprezint acel obiect. Dac lista este de tip multiselecie, prinderea acestui eveniment poate duce la confuzii din cauz c nu este clar dac utilizatorul a vrut s selecteze elementul sau selecia se refer la toate elementele alese. Urmtorul exemplu prezint un mod de a rezolva aceast situaie, prin implicarea unui alt element i anume un buton.
import java.awt.*; import java.applet.*; public class ListDemo extends Applet { List l; public void init () { String fonts[]; //se preiau fonturile din sistem fonts = Toolkit.getDefaultToolkit().getFontList(); //se creaza o lista cu multiselectie l = new List(4, true); for (int i = 0; i < fonts.length; i++) { //se adauga elementele in lista l.add(fonts[i]); } setLayout (new BorderLayout (10, 10)); //se adauga componentele pe fereastra add ("North", new Label ("Pick Font Set")); add ("Center", l); add ("South", new Button ("Submit")); resize (preferredSize()); validate(); } public boolean action (Event e, Object o) { // la apasarea butonlui if (e.target instanceof Button) { //se preiau elementele selectate String chosen[] = l.getSelectedItems(); for (int i=0;i<chosen.length;i++) //pentru a fi afisate System.out.println (chosen[i]); } return false; } } public void addItemListener(ItemListener listener) public void removeItemListener(ItemListener listener)

31

Modelul nou de tratare a evenimentelor implic folosirea unor asculttori, iar pentru aceasta avem metodele de abonare/dezabonare la evenimentele de tip item.
public void addActionListener(ActionListener listener) public void removeActionListener(ActionListener listener)

Aceste metode se ocup cu nregistrarea/dezabonarea unei liste la evenimente de aciune. n general modelul este cel ntlnit la componente, deci regulile se aplic asemenea. Pentru a exemplifica utilizarea acestor evenimente, precum i folosirea unei liste cu selecie unic, avem exemplul de mai jos:
public class ListDemo1 extends Applet implements ItemListener { /*Membrii clasei*/ private LayoutManager Layout; private List Selector; private Font SansSerif; //constructorul initializeaza forma public ListDemo1 () { /*intr-un sir tinem culorile*/ String [] ColorList; int i; /* instantierea membrilor */ ColorList = new String [9]; SansSerif = new Font ("SansSerif", Font.BOLD, 14); Layout = new FlowLayout (); Selector = new List (6); //sunt initializate valorile din sir ColorList [0] = "Red"; ColorList [1] = "Magenta"; ColorList [2] = "Blue"; ColorList [3] = "Cyan"; ColorList [4] = "Green"; ColorList [5] = "Yellow"; ColorList [6] = "White"; ColorList [7] = "Gray"; ColorList [8] = "Black"; for (i = 0; i < ColorList.length; ++i) { Selector.add (ColorList [i]); } Selector.setBackground (Color.LIGHT_GRAY); Selector.setForeground (Color.red); Selector.setFont (SansSerif); // se adauga pe forma, lista setLayout (Layout); add (Selector); //ascultam la evenimentele de pe lista Selector.addItemListener (this); //setari initiale Selector.select (2); setBackground (Color.blue); } public void itemStateChanged(ItemEvent e) { //daca s-a selectat un element int Selection;

32

//se preia indexul acestuia Selection = Selector.getSelectedIndex(); //si in functie de elementul selectat //se modifica fundalul formei if (Selection == 0) { setBackground (Color.red); } else if (Selection == 1) { setBackground (Color.magenta); } else if (Selection == 2) { setBackground (Color.blue); } else if (Selection == 3) { setBackground (Color.cyan); } else if (Selection == 4) { setBackground (Color.green); } else if (Selection == 5) { setBackground (Color.yellow); } else if (Selection == 6) { setBackground (Color.white); } else if (Selection == 7) { setBackground (Color.gray); } else if (Selection == 8) { setBackground (Color.black); } } }

Meniuri
Mai sus, am menionat c un Frame poate avea un meniu. Pentru a asigura un meniu aplicaiei, acesta va sta ntr-un Frame. Implementarea unui meniu n cadrul unui Frame, implic o mbinare a mai multor elemente i anume: MenuBar, Menu, MenuItem. Pentru a afia un meniu, acesta va fi pus ntr-un MenuBar, care la rndul lui va fi adugat unui Frame. Un meniu poate conine un MenuItem, dar poate conine i alte meniuri care formeaz submeniurile sale. Componentele MenuItem sunt materialele construite ca i panel-ele care formeaz cortinele. Cortina este Menu. Meniurile sunt ataate de un MenuBar. Aceast bar de meniuri va sta de obicei n partea de sus a Frame-ului ca n figura de mai jos:

Figura 11.4 Componentele unui meniu n figura de mai jos avem toate aceste componente plasate in ierarhia AWT. 33

Figura 11.5 Ierarhia componentelor de meniu

MenuComponent Aceasta este o clas abstract ce reprezint printele obiectelor legate de meniuri. Nu se va crea niciodat o instan a acestei clase, ci se poate eventual moteni. Utilitatea acesteia, este aceea de a conine o serie de metode valabile pentru celelalte componente de care vom vorbi.

34

public Font getFont () public void setFont (Font f)

Cele dou metode permit preluarea/setarea fontului asociat unei componente de meniu. Atunci cnd se creeaz o component de meniu, fontul iniial ala acesteia este null.
public String getName () public void setName (String name)

Metodele permit preluarea/setarea numelui componentei de meniu. Fiecare obiect de subclas a MenuComponent va avea un nume la instaniere. A doua metod permite schimbarea acestui nume.
public MenuComponentPeer getPeer ()

Aceast metode permite preluarea peer-ului asociat componentei de meniu.


public boolean postEvent (Event e)

Metoda este folosit pentru a transmite un eveniment e, componentei MenuComponent. Evenimentul este transmis Frame-ului, la obiectul din vrful ierarhiei ce l conine pe MenuComponent. Dac suprascriem aceast metod suntem obligai s transmitem mai departe celorlalte componente, prin apelul return super.postEvent (e);.
protected void processEvent(AWTEvent e)

Aceast metod primete toate evenimentele AWTEvents ce au ca int o subclas a MenuComponent. Mai apoi, acestea pot fi transmise folosind metoda processEvent(). MenuContainer Aceasta este o interfa implementat de patru tipuri de Container-e i anume Frame, Meniu i MenuBar i Container. Metodele
public abstract Font getFont ()

Metoda returneaz fontul aferent componentei de meniu. Clasa MenuItem implementeaz aceast metod, astfel c toate subclasele o motenesc.
public abstract boolean postEvent (Event e)

Metoda va transmite evenimentul obiectului care implementeaz aceast metod.


public abstract void remove (MenuComponent component)

35

Metoda permite tergerea componentei specificat parametru din obiectul clasei ce implementeaz aceast metod. MenuShortcut Aceasta este o clas ce reprezint o scurttur de taste pentru un MenuItem. Atunci cnd au loc aceste evenimente, un eveniment de aciune este generat astfel nct el declaneaz aciunea unei componente de meniu. Metode
public MenuShortcut (int key)

Constructorul creeaz un obiect MenuShortcut cu cheia key. Aceast cheie reprezint valoarea ntreag returnat de evenimentul KEY_PRESS.
public MenuShortcut(int key, boolean useShiftModifier)

Acest constructor permite, n plus, specificarea faptului c este folosit o combinaie de taste gen Shift sau nu.
public int getKey ()

Metode returneaz codul cheii care a declanat MenuShortcut actual.


public boolean usesShiftModifier()

sau nu.

Aceast metod specific faptul c obiectul MenuShortcut impune ca tasta Shift s fie apsat

MenuItem n principiu acesta este elementul ce se gsete ntr-un meniu. Metodele


public MenuItem () public MenuItem (String label) public MenuItem (String label, MenuShortcut shortcut)

Prima metod creeaz un item de meniu cu o etichet goal i nici o scurttur de taste. Al doilea constructor creeaz un meniu ce permite specificarea unei etichete, n timp ce al treilea mai adaug i o scurttur de taste.

36

public String getLabel () public void setLabel (String label)

Aceste dou metode permit preluarea/setarea etichetei asociate cu item-ul curent.


public MenuShortcut getMenuShortcut () public void setShortcut (MenuShortcut shortcut)

Aceste metode permit preluarea/setarea scurtturile de taste.


public void deleteMenuShortcut ()

Metoda permite tergerea unor scurtturi de taste asociate cu item-ul curent.


public boolean isEnabled () public synchronized void setEnabled(boolean b)

Metoda isEnabled verific dac item-ul de meniu este activat. Un item activat poate fi selectat de utilizator, iar unul dezactivat apare scris cu gri. Metoda setEnabled activeaz/dezactiveaz item-ul de meniu, conform parametrului.
public synchronized void enable () public synchronized void disable ()

Metoda permite activarea/dezactivarea unui item de meniu.


public String getActionCommand() public void setActionCommand(String command)

Metoda getActionCommand()permite preluarea comenzii asociate cu item-ul de meniu actual. Comanda, n mod automat, este eticheta item-ului. Aceasta se poate modifica folosind metoda setActionCommand ce poate specifica alt String ce va trece drept noua comand.
public void addActionListener(ItemListener listener) public void removeActionListener(ItemListener listener)

Aceste metode permit abonarea/dezabonarea de la evenimente de tip action. n general sunt valabile evenimentele asociate componentelor AWT.

37

Menu Acestea sunt obiectele care conin elementele de meniu descrise mai sus.

Metodele
public Menu () public Menu (String label) public Menu (String label, boolean tearOff)

Primul constructor permite instanierea unui meniu care nu are etichet i care nu poate fi oprit. Al doilea constructor permite instanierea unui meniu cu etichet, care nu poate fi oprit. Al treilea constructor creeaz un meniu cu eticheta label dar ce poate fi oprit sau nu. Aceasta se va seta folosind al treilea parametru.
public int getItemCount() public int countItems ()

Aceste metode permit aflarea numrului de elemente dintr-un meniu.


public MenuItem getItem (int index)

Metoda returneaz elementul de la poziia dat de index. Dac index este invalid, atunci metoda arunc excepia ArrayIndexOutOfBoundsException.
public synchronized MenuItem add (MenuItem item)

Aceast metod permite adugarea unui obiect item n meniu. Eticheta asociat cu item este afiat n meniu. Dac item se afl deja ntr-un alt meniu atunci este ters de acolo.
public void add (String label)

Metoda permite crearea unui item de meniu cu eticheta label i adugarea acestuia la meniu.
public synchronized void insert(MenuItem item, int index) public synchronized void insert(String label, int index)

Metodele permit adugarea unui item la indexul specificat. Dac indexul nu este valid atunci va fi aruncat excepia IllegalArgumentException.
public void addSeparator () public void insertSeparator(int index)

38

Meniul nu va genera un eveniment atunci cnd este selectat, evenimentul fiind generat de selectarea unui MenuItem. MenuBar Aceasta este componenta ce va fi adugat Frame-ului. Metodele
public MenuBar()

Constructorul permite crearea unui ManuBar gol. Pentru a aduga meniuri acestuia se va folosi metoda add().
public int getMenuCount () public int countMenus ()

Metodele returneaz numrul de meniuri aflate n cadrul barei de meniuri.


public Menu getMenu (int index)

Metoda returneaz meniul de la poziia index. Dac index nu este valid, metoda arunc excepia ArrayIndexOutOfBoundsException.
public synchronized Menu add (Menu m)

Metoda permite adugarea unui meniu m pe bara de meniuri. Eticheta care a fost folosit pentru crearea lui m va fi afiat pe bara de meniuri. Dac m se gsete pe o bar de meniuri deja, el va fi ters.
public synchronized void remove (int index) public synchronized void remove (MenuComponent component)

Metoda remove terge componenta de la poziia index, dac este vorba de prima metod. n cazul celei de-a doua metod se terge meniul component din bara de meniuri.
public MenuItem getShortcutMenuItem (MenuShortcut shortcut)

Metoda returneaz MenuItem asociat cu scurttura de taste shortcut. Dac acest item de meniu nu exist, se returneaz null.
public synchronized Enumeration shortcuts()

39

Pentru a afla scurtturile de taste din obiectele asociate barei de meniuri actuale, avem metoda shortcuts().
public Menu getHelpMenu () public synchronized void setHelpMenu (Menu m)

Metoda returneaz meniul ce a fost desemnat ca meniu de ajutor prin intermediul celei de-a doua metode. Metoda setHelpMenu seteaz meniul de ajutor, i anume l va deplasa maxim la stnga pe m. n exemplul de mai jos sunt cuprinse toate elementele prezentate mai sus.
import java.awt.*; import java.awt.event.*; //clasa proprie de item de meniu class MyMenuItem extends MenuItem { //in care adaugam un ascultator //obiectului al //care va fi unul de tip MenuDemo public MyMenuItem (String s, ActionListener al) { super (s); addActionListener (al); } } public class MenuDemo extends Frame implements ActionListener, ItemListener { public MenuDemo () { super ("MenuTest"); MenuItem mi; //se creeaza meniu file Menu file = new Menu ("File", true); //caruia is se adauga un meniu Open file.add (new MyMenuItem ("Open", this)); //si un meniu Close (care este dezactivat) mi = file.add (new MyMenuItem ("Close", this)); mi.setEnabled (false); //cream un submeniu Extras cu trei meniuri Menu extras = new Menu ("Extras", false); //unul de tip checkbox CheckboxMenuItem ck = new CheckboxMenuItem ("What"); ck.addItemListener(this); mi = extras.add (ck); //iar celelalte normale ( A1 si A2) mi = extras.add (new MyMenuItem ("Second submenu (A1)", this));

40

mi.setActionCommand ("A1"); mi = extras.add (new MyMenuItem ("Third submenu (A2)", this)); mi.setActionCommand ("A2"); file.add (extras); //se adauga o linie de separatie file.addSeparator(); //si ultimul meniu Quit file.add (new MyMenuItem ("Quit", this)); //dupa meniul file, barei de meniuri //i se adauga meniul help Menu help = new Menu("Help"); //cu o optiune, About help.add (new MyMenuItem ("About", this)); MenuBar mb = new MenuBar(); //adugam barei de meniuri cele doua meniuri mb.add (file); mb.add (help); mb.setHelpMenu (help); //si bara o atasam frame-ului setMenuBar (mb); setSize (200, 200); enableEvents (AWTEvent.WINDOW_EVENT_MASK); } //atunci cand apasam unul din meniuri public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("Quit")) { System.exit(0); } //afisam selectia efectuata System.out.println ("User selected " + e.getActionCommand()); } public void itemStateChanged(ItemEvent e) { //daca meniul este de tip selectabil CheckBoxmenu if (e.getSource() instanceof ItemSelectable) { ItemSelectable is = (ItemSelectable)e.getSource(); System.out.println ("The value is: " + (is.getSelectedObjects()!=null)); } } protected void processWindowEvent(WindowEvent e) { if (e.getID() == WindowEvent.WINDOW_CLOSING) {

41

//notificam ascultatorii de eveniment super.processWindowEvent(e); //si se inchide procesul System.exit(0); } else { super.processWindowEvent(e); } } public static void main (String []args) { MenuDemo f = new MenuDemo(); f.setVisible(true); } }

42