Sunteți pe pagina 1din 629

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.
4. 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.

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 stream-
ul 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 Descriere Marime


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

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 Descriere


+ op1 + op2 Adauga op1 and op2; de asemenea concateneaza stringuri
- op1 - op2 scade op2 din op1
* op1 * op2 inmulteste op1 cu op2
/ op1 / op2 divide op1 la op2
% op1 % 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 Descriere


++ op++ incrementeaza op cu 1; evaluarea lui op inainte de incrementare
++ ++op incrementeaza op cu 1; evaluarea lui op dupa incrementare
-- op-- decrementeaza op cu 1; evaluarea lui op inainte de decrementare
-- --op decrementeaza op cu 1; evaluarea lui op dupa decrementare
Operatori relationali

Acestia compara doua valori si determina relatia dintre ele.

Tabelul 5. Operatori relationali

Operator Folosire Descriere


> op1 > op2 Returneaza true daca op1 este mai mare decat op2
>= op1 >= op2 Returneaza true daca op1 este mai mare sau egal cu op2
< op1 < op2 Returneaza true daca op1 este mai mic decat op2
<= op1 <= op2 Returneaza true daca op1 este mai mic sau egal cu op2
== op1 == op2 Returneaza true daca op1 este egal cu op2
!= op1 != op2 Returneaza true daca op1 este diferit de op2

Operatori conditionali

Acestia returneaza o valoare true sau false evaluand operanzii aferenti.

Tabelul 6. Operatori conditionali

Operator Folosire Descriere


&& op1 && op2 Returneaza true daca op1 and op2 sunt true; evalueaza
conditional op2
|| op1 || op2 Returneaza true daca op1 sau op2 este true; evalueaza
conditional op2
! !op Returneaza true daca op este false
& op1 & op2 Returneaza true daca op1 si op2 sunt boolean si amandoi sunt
true; evalueaza intotdeauna op1 si op2

Daca ambii operanzi sunt numere, efectueaza SI pe biti


| op1 | op2 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


^ op1 ^ op2 Returneaza true daca op1 si op2 sunt diferiti, adica un XOR
pe biti
Operatori de siftare
Tabelul 6. Operatori de siftare

Operator Folosire Descriere


<< op1 << Siftarea bitilor lui op1 la stanga cu o lungime data de op2;
op2 umple cu zero bitii din partea dreapta
>> op1 >> Siftarea bitilor lui op1 la dreapta cu o lungime data de op2;
op2 umple cu bitul cel mai semnificativ (de semn) bitii din partea
stanga
>>> op1 >>> Siftarea bitilor lui op1 la dreapta cu o lungime data de op2;
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 Echivalent


Scurtaturi pentru operatorii += op1 += op2 op1 = op1 + op2
aritmetici -= op1 -= op2 op1 = op1 - op2
*= op1 *= op2 op1 = op1 * op2
/= op1 /= op2 op1 = op1 / op2
%= op1 %= op2 op1 = op1 % op2
Scurtaturi pentru operatorii pe &= op1 &= op2 op1 = op1 & op2
biti |= op1 |= op2 op1 = op1 | op2
^= op1 ^= op2 op1 = op1 ^ op2
Scurtaturi pentru siftare <<= op1 <<= op2 op1 = op1 << op2
>>= op1 >>= op2 op1 = op1 >> op2
>>>= op1 >>>= op2 op1 = op1 >>> op2
Alti operatori
Vom aminti aici si o lista cu operatorii care nu au fost inclusi in celelalte liste

Tabelul 8. Alti operatori

Operator Descriere
?: Scurtatura la o anumita instructiune if-else
[] Folosit la lucrul cu siruri
. Pentru accesarea membrilor, a claselor.
(params ) Lista de parametrii (unei metode, sau instructiuni)
(tip ) Operator cast: schimba tipul de data al unei variabile
New Creeaza un nou obiect sau sir.
Instanceof 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 //ambigua
(x+y)/100 // 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 [] . (params ) expr ++ expr --


mai mare
Operatori unari ++expr --expr +expr -expr ~ !
Operatori creeare sau new (type )expr
cast
Operatori de multiplicare * / %
Operatori aditivi + -
Operatori de siftare > >>>
Operatori relationali < > <= >= instanceof
Operatori de egalitate == !=
SI pe biti &
XOR pe biti ^
SAU pe biti |
SI logic &&
SAU logic ||
Scurtatura if-else ?:
Precedenta cea asignare = += -= *= /= %= &= ^= |= <<= >>= >>>=
mai mica
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 Cuvinte cheie


Bucla while, do-while, for
De decizie if-else, switch-case
Tratarea erorilor try-catch-finally, throw
Ramificare break, continue, label :, return

While

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

1
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:

3
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();
}
}
}

4
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.

7
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);

8
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;

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


minivan.viteza);
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[])
{

Book book0 = new Book("Cel mai iubit dintre pamanteni","Marin


Preda");
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 Contine un numar consistent de clase cu scop general

java.util Contine clasele care au diverse functionalitati utile

java.io Contine clasele care se ocupa cu operatiunile input/output

java.net Contine clasele care se ocupa de lucru in retea

java.applet Contine clasele care ajuta la lucru cu applet-uri

java.awt 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

1
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)

2
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;

4
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);

5
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 = 0 short = 0 double = 0.0 float = 0.0


int = 1 short = 0 double = 0.0 float = 0.0
int = 0 short = 0 double = 1.1 float = 0.0
int = 2 short = 0 double = 0.0 float = 3.0
int = 2 short = 0 double = 4.4 float = 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 = 0 short = 0 double = 0.0 float = 0.0
int = 0 short = 1 double = 0.0 float = 0.0
int = 0 short = 0 double = 1.1 float = 0.0
int = 2 short = 0 double = 0.0 float = 3.0
int = 2 short = 0 double = 4.4 float = 0.0

6
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.

7
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

8
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.

9
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

1
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)
{

2
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);
}
}

3
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

4
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++)

5
{
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

6
{
void setColDreaptaSus(double x, double y);
double getDreaptaSusX();
double getDreaptaSusY();
}
public interface Transformabil extends Scalabil, Translatabil,
Rotatabil {}
public interface Forma extends Positionabil, Transformabil {}

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 class Forma
{
public String categorie;
public abstract double Suprafata();
public abstract double Perimetrul(); //in loc de corp avem ";"
public void ShowTipForma()
{
System.out.println("Forma este de tip: " + categorie);
}
}
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()
{

7
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()
{

8
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

9
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
}
}
}

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
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];

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
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)
{
// 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] + " / " + deimp[i] +
" este " + numar[i]/deimp[i]);
}

catch (ArrayIndexOutOfBoundsException iex)


{
System.out.println("Am depasit limita la pasul
"+i+".");
}
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 Descriere
ArithmeticException Eroare de tip aritmetic, de exemplu impartire la zero.
ArrayIndexOutOfBoundsException Indexul din sir este in afara limitelor.
ArrayStoreException Se incearca atribuirea unui element din sir cu o valoare
incorecta.
ClassCastException Cast incorect.
IllegalArgumentException Parametrul transmis metodei este gresit.
IllegalMonitorStateException Operatie invalida, de exemplu, asteptarea unui fir de executie
neblocat.
IllegalStateException Aplicatia nu ruleaza in mediu corect, compatibil.
IllegalThreadStateException Operatia aplicata nu este in concordanta cu starea firului de
executie
IndexOutOfBoundsException Indexul este in afara limitelor
NegativeArraySizeException Sir creat cu limite negative.
NullPointerException Folosirea obiectului neinstantiat (egal cu null).
NumberFormatException Conversia dintr-un string intr-un numar a esuat.
SecurityException Posibila tentativa asupra securitatii.
StringIndexOutOfBounds Indexul unui string este in afara limitelor
CloneNotSupportedException Incercare de a folosi un obiect ce nu implementeaza Cloneable
IllegalAccessException Acces la clasa nepermis.
InstantiationException S-a incercat instantierea unei interfete sau clase abstracte
InterruptedException Un fir de executie a fost intrerupt
NoSuchFieldException S-a incercat accesarea unui membru care nu exista.
NoSuchMethodException 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 Implementeaza OutputStream
InputStream Clasa abstracata ce descrie input stream
ObjectInputStream Input stream pentru obiecte
ObjectOutputStream Output stream pentru obiecte
OutputStream Clasa abstracata ce descrie output stream
PipedInputStream pipe pentru intrari
PipedOutputStream pipe pentru iesiri
PrintStream Output stream ce poate apela print( ) si println( )
PushbackInputStream Input stream care permite ca byte-tii sa fie returnati dintr-un stream
SequenceInputStream 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 Descriere
int available( ) Returneza numbarul de bytes disponibili de la intrare.
void close( ) 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.
int read(char buffer[ ]) Acelasi ca in cazul byte:de data aceasta se citesc caractere

In cazul clasei Writer exista cateva metode noi:

Writer append(char ch) scrie la sfarsitul stream-ului caracterul ch.


Writer append(CharSequence chars) 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.
Writer append(CharSequence chars,
int begin, int end) scrie secventa chars la sfarsitul stream-ului delimitata
de begin si 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

2
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.

3
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.

4
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;

5
}
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;

7
//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( 0 ): 0.0 43.0 0.0 89.0 0.0
Element( 1 ): 2.3 5.0 6.7 11.0
Element( 2 ): 1.0 4.2 7.0 4.1 10.0 9.0
Element( 3 ): 0.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"};

8
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 Descriere
capacityIncrement Marimea pentru incrementarea capacitatii unui vector
elementCount Numarul de elemente dintr-un vector.
elementData sirul de date intern al vectorului.
modCount 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 Descriere
add() Adauga un element intr-un vector.
addAll() Adauga o colectie de elemente intr-un vector.
addElement() asemenea metodei add()
capacity() Returneaza capacitatea adica marimea sirului intern.
clear() sterge toate elementele unui vector.
clone() Creeaza o clona a vectorului.
contains() Verifica daca un vector contine un element.
containsAll() Verifica daca un vector contine o colectie.
copyInto() Copiaza elementele unui vector intr-un array.
elementAt() Returneaza elementul de la pozitia specificata.
elements() Returneaza un obiect al vectorului care permite vizitarea tuturor cheilor
vectorului.
ensureCapacity() Verifica si se asigura ca marimea buffer-ului intern sa fie de o anumita marime.
equals() verifica egalitatea cu un obiect.
firstElement() returneaza primul element.
get() returneaza un element de la o anumita pozitie.
indexOf() cauta prima aparitie a unui element in sir.
insertElementAt() Insereaza un element in sir.
isEmpty() verifica daca un vector este gol.
iterator() returneaza un iterator, adica un obiect ce permite vizitarea elementelor din sir
lastElement() Returneaza ultimul element din sir
lastIndexOf() Cauta pozitia ultimului element din sir care este egal cu obiectul specificat
listIterator() Returneaza un obiect care permite ca toate elementele sa fie vizitate secvential.
remove() Sterge un anumit element din vector.
removeAll() Sterge toate elementele specificate in colectia data ca parametru.
removeAllElements() Sterge toate elementele din sir si seteaza marimea acestui cu zero.
set() Schimba un element de la o anumita pozitie.
setElementAt() Acelasi lucru ca si set.
setSize() modifica marimea buffer-ului intern
size() returneaza numarul de elemente din sir
subList() returneaza o sectiune din sir.
toArray() returneaza elementele vectorului ca array.
trimToSize() „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 înlăntuite ................................................................................................ 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

2
Liste inlantuite

Lista simplu înlănţuita este cea mai simpla structura de data înlănţuita. O astfel de lista este o
secvenţa de obiecte alocate dinamic, fiecare obiect având referinţa către succesorul sau in lista. Exista o
multitudine de moduri de a implementa aceasta structura de date. In figura 6.1 avem câteva
reprezentări mai cunoscute a listelor simplu înlănţuie.

Figura 6.1 Tipuri de liste simplu inlantuite

Cea mai simpla lista dublu înlănţuia este in figura 6.1 1) in care fiecare element din lista indica
spre succesorul lui, iar ultimul element către null. Variabila cap are rolul de a păstra primul element din
lista. Acest tip de lista este ineficienta atunci când se dorește adăugarea elementelor la ambele capete
ale listei. Pentru a adăuga un element la capătul listei, este necesar sa localizam ultimul element.
Aceasta presupune parcurgerea întregii liste pentru o simpla operaţie de adăugare.
In figura 61. 2) avem reprezentarea unei liste având cap si coada, tocmai pentru a eficientiza
adăugarea elementelor in lista. Singura problema ar fi de spaţiu pentru variabila coada.
Lista simplu înlănţuită din figura 6.1 3) prezintă doua ajustări. Exista aici un element nou,
santinela care nu este folosit pentru a conţine date, dar este întotdeauna prezent. Avantajul principal in
folosirea santinelei este ca simplifica anumite operaţii. 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 către null; vorbim astfel despre o lista circulara. Avantajul este ca, inserarea la
capul listei, inserarea la coada listei sunt operaţii identice.
Se poate realiza o lista simplu înlănţuită care nu are nevoie de o santinela. Figura 6.1 4) prezintă
o variaţie 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 când nu conţin nici un element.

Figura 6.2 Listele goale

Reprezentarea listelor

Cum este reprezentat un element dintr-o lista simplu înlănţuita?


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 conţine un membru next tot de același tip ca si clasa. Aceasta
pentru ca, elementul din lista va indica spre următorul element din lista care este tot de același tip de
data. Pe lângă acest membru, clasa mai conţine un element de tip int data, care retine informaţii despre
nodul curent din lista. Bineînţeles ca pe lângă acest membru, se pot declara o serie de alţi membrii care
pot retine diverse informaţie, in funcţie de necesitaţi.

4
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ănţuite. Se declara prima
data un obiect de tip LinkedList cu un constructor fără parametrii. Apoi se instanţiază membrul next. In
acest moment se numește ca am alipit primului element din lista un nou element si anume: list.next.
Acest nou element este instanţiat la rândul 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 adăugarea 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

5
instrucţiunea iterator = iterator.next. Aceasta se poate face intr-un while, pana când
următorul element devine null, adică am ajuns la capătul 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 înlănţuită in care fiecare element tine o referinţă către
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 fără parametru, înseamnă ca instanţiem tocmai primul element din lista, de
aceea avem instrucţiunea head = this; in acest constructor.
class LinkedListDemo
{
public static void main(String args[])
{
LinkedList list = new LinkedList();
list.head = list;
list.data =23;

6
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 legătura 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ţă către capul listei.
Sunt multe erori care pot apare, încă de la începutul definiţiei unei liste, astfel ca e bine sa
încercam sa prevedem situaţiile nefericite ce pot sa apară. De exemplu daca se încearcă accesarea unui
element null, ca de exemplu primul element al listei, sa aruncam o excepţie. Pentru aceasta membrii
listei trebuie sa fie private sau protected (daca lista va fi moștenita), iar excepţiile vor apare in get-eri si
set-eri.
Iată o parte a clasei LinkedList cu modificările de mai sus:
class LinkedList
{
int data;
protected LinkedList next;
protected LinkedList head;
public LinkedList GetNextElement()
{

7
//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)
throw new NullPointerException("Elementul urmator este
null!");
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 următoare nu sunt implementate aceste „prevederi” legate de excepţii, însă un
bun exerciţiu este sa modificaţi exemplele adăugând si tratările posibilelor situaţii ce pot sa apară.
Excepţiile care pot sa apară nu sunt tratate, tocmai pentru a simplifica exemplele.

Operatiuni cu liste simplu înlăntuite

Pentru a înţelege mai bine toate operaţiunile 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;
}

8
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(" ");

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

Pe lângă cei doi membrii si anume next si data, se pot observa o serie de funcţii pentru a înlesni
adăugarea, căutarea, ștergerea elementelor dintr-o lista. Clasa Nod reprezintă un element din lista, însă
prin intermediul acelui nod din lista putem de exemplu adăuga element imediat dupa capul listei:
public void AddNodeAfter(int element)
{
next = new Nod(element,next);
}
Figura de mai jos explica ce se întâmpla când adăugam un nou element il lista:

Figura 6.3 Adăugarea unui element in lista

Se poate remarca faptul ca noul element este introdus intre cap si vechea santinela, urmând 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, sărim 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 lângă metodele de adăugare, ștergere exista si metode de parcurgere in lista ,


ListToString, de returnarea a unui element de la o anumita poziţie PositionInList, de căutare
a unui element după valoare in lista: SearchInList. Toate aceste metode presupun existenta unui
obiect numit iterator care primește de obicei primul element din lista Nod iterator = start;.
Totuși lăsam posibilitatea de a specifica orice element din lista pentru ca afișarea, sau parcurgerea sa
aibă loc de oriunde din cadrul listei, si nu neapărat 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, instrucţiune pe care o găsim in bucla

iterator = iterator.next;

O serie de alte funcţii sau de adăugări sau de ștergeri pot fi implementate, in aceasta clasa
pentru ca operaţiile sa fie cat mai ușor realizate. Iată un exemplu de inserare după un element din lista.
public void InsertAfterAnElement(int dataVal, Nod start, int newVal)
{
Nod tmp = new Nod(newVal,null);
Nod prev = start;
Nod iterator = start;
do
{
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, reţinem următorul acelui element
in prev si la sfârșit tmp va indica spre prev iar tmp devine succesorul elementului de dinaintea lui
prev. Pentru o mai buna înţelegere a situaţiei avem imaginea de mai jos figura 6.5.

Figura 6.5 Inserarea dupa un element

Mai jos avem funcţia 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 inserării, se parcurge lista de la primul element pana la cel căutat, si se sare peste
succesorul acestuia, marcând astfel ștergerea din lista: iterator.next = prev.next;
Pentru o mai buna înţelegere a acestei operaţii 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 ușor lista si in celalalt sens (si nu doar de la predecesor la
succesor), avem lista dublu înlănţuită.
Aceasta păstrează conceptele listei simplu înlănţuite, cu specificarea ca fiecare element al listei
mai conţine o referinţă către predecesorul, sau anteriorul sau, așa cum apare in figura 6.7

Figura 6.7 Reprezentarea unei liste dublu înlănţuite

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 păstrează aceleași metode, concepte ca in cazul listei simplu înlănţuite, dar
ţinând cont si de a doua legătura. Astfel in cazul adăugării, ștergerii, parcurgerii trebuie sa ţinem cont de
legătura prev.

FrameWork-ul Java pentru Colectii

Framework, in general înseamnă o serie de clase, librarii care joaca rol de „schelet” intr-o aplicaţie,
permiţând extinderea şi dezvoltarea ei pe baza acestor elemente. In Java , Framework-ul este
asemănător cu Standard Template Library (STL) din C++. Exista aproximativ douazecisicinci de clase,
interfeţe, care constituie acest nucleu.

14
Bazele Framework-ului

Acest Framework de colecţii costa din trei părţi: interfeţe, implementări si algoritmi. Implementările
sunt acele clase concrete pe care Framework-ul le are, iar algoritmii sunt acţiuni, metode predefinite
care pot exista in clase.

Interfetele

Exista mai multe interfeţe 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 interfeţele prezentate mai sus:

Clase concrete
Interfeţe HashTable Array mutabil Arbore Lista înlănţuita Altele
balansat
Set HashSet TreeSet
SortedSet TreeSet
List ArrayList LinkedList Vector, Stack
Map HashMap TreeMap Hashtable,
Properties
SortedMap TreeMap

Toate clasele implementează ori interfaţa Collection, ori interfaţa Map. Pentru a înţelege acest tabel el
va trebui citit de la stânga la dreapta si anume: pe al doilea rând: care sunt clasele ce implementează
interfaţa Set? Pe fiecare coloana vom găsi acea clasa ce implementează interfaţa, daca este cazul.
Vom analiza pe rând, toate aceste clase si interfeţe. Daca dorim sa creăm clase ce implementează aceste
interfeţe este bine sa ţinem cont de câteva aspecte:
- Toate implementările sunt desincronizate. Se poate adăuga acces sincron, dar nu este
necesar.
- Toate clasele oferă iteratori fail-fast. Daca o colecţie este modificata in timp ce iteram prin
elementele acesteia, va fi aruncata o excepţie de tip ConcurrentModificationException.
- Toate implementările lucrează cu elemente null.
- Clasele se bazează pe un concept de metode opţionale in interfete. Daca o clasa nu suporta
o anumita operaţiune, va arunca o excepţie numita UnsupportedOperation Exception.

Algoritmii Framework-ului

Exista o serie de algoritmi predefinit care se găsesc in Collections si in Arrays, ce ajuta in lucrul cu
aceste colecţii si pe care ii vom analiza in curând.

Interfata Collection

Aceasta interfaţa consta din o serie de metode necesare in lucru cu o colecţie si anume:

16
Metoda Descrierea
add() Adaugă un element intr-o colecţie
addAll() Adaugă o colecţie in alta colecţie
clear() Șterge toate elementele dintr-o colecţie
contains() Verifica daca un element se afla intr-o colecţie
containsAll() Verifica daca o colecţie se afla intr-o alta colecţie
equals() Verifica egalitatea a doua colecţii
hashCode() Returnează un id specific unei colecţii
isEmpty() Verifica daca o colecţie este goala
iterator() Returnează un obiect dintr-o colecţie ce permite vizitarea elementelor acesteia
remove() Șterge un element dintr-o colecţie
removeAll() Șterge elementele unei colecţii din colecţia curenta
retainAll() Șterge elementele dintr-o colecţie care nu sunt in alta colecţie
size() Returnează numărul de elemente dintr-o colecţie
toArray() Returnează elementele dintr-o colecţie ca sir.

Adaugarea elmentelor

Putem adăuga doar un element folosind metoda add. In mod normal, daca nu se arunca nici o excepţie,
acest element va fi in colecţie după return-ul din funcţie. In acest caz valoare returnata este true.

public boolean add(Object element)

Totuși daca ceva neprevăzut se întâmplă si elementul nu poate fi adăugat in colecţie se va


returna valoarea false.
Putem de asemenea adăuga o colecţie prin metoda addAll.

public boolean addAll(Collection c)

Fiecare element din colecţia c va fi adăugat in colecţia ce apelează metoda. Daca in urma
apelului, colecţia 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 excepţia de tip
UnsuporedOperationException.

Stergerea elementelor

Putem şterge toate elementele dintr-o colecţie folosind metoda clear:

public void clear()

In cazul in care colecţia este read-only vom primi o excepţie UnsuporedOperationException.


Se pot șterge anumite elemente dintr-o colecţie:

public boolean remove(Object element)

17
Pentru a determina daca un element este in colecţie sau nu, trebuie sa ne bazam pe metoda
equals. Atunci cand se determina care este elementul din colecţie ce va fi șters, metoda equals va fi
invocata. Daca ștergerea nu este permisa, vom primi excepţia de mai sus.
Se pot șterge si elementele unei colecţii folosind funcţia:

public boolean removeAll(Collection c)

Metoda șterge toate instanţele din colecţia sursa, care se afla si in colecţia c. Daca avem
elementele:
[1,5,8,1,2,4,2,5,7,6]
si colecţia c este:
[1,5]
colecţia rămasă în urma ștergerii este:
[8,2,4,2,7,6]

Retinerea colectiei

Aceasta operaţiune este inversa lui removeAll si anume presupune ca doar elementele din
colecţia c sunt păstrate in colecţia originală:

public boolean retainAll(Collection c)

Practic metoda funcţionează ca o intersecţie de mulţimi. Daca aplicam aceasta operaţie pentru
colecţia:
[1,5,8,1,2,4,2,5,7,6]
si c este:
[1,5,23,29]
colecţia ce rezulta este:
[1,5]

Alte Operatiuni

Returnarea elementelor

Exista o singura metoda pentru a returna elemente din colecţie. Prin folosirea unui iterator, si
anume prin apelul metodei iterator() putem vizita toate elementele unei colecţii:

public Iterator iterator()

Vom reveni mai târziu asupra acestui subiect.

18
Cautarea elementelor

Înainte de a căuta un element este indicat sa verificam existenta lui in colecţie:

public boolean contains (Object element)

Daca obiectul element este găsit, atunci funcţia va returna true, altfel va returna false. Ca si in
cazul remove(), se folosește metoda equals pentru comparare.
Se poate de asemenea verifica daca o colecţie conţine alta colecţie prin funcţia:

public boolean containsAll(Collection c)

Aceasta metoda va căuta subsetul c in colecţia curenta. Daca doar un element din c nu este găsit
in colecţia curenta se returnează false.
Funcţia pentru aflarea mărimii unei colecţii este:

public int size()

Aceasta funcţie returnează numărul de elemente din colecţie.


Pentru a verifica daca o colecţie este goala sau nu avem funcţia:

public boolean isEmpty()

Metoda returnează true daca nu avem nici un element in colecţie.

Clonarea colectiilor

Interfaţa Collection nu implementează nici Cloneable si nici Serializable. Pentru a copia o


colecţie, este indicat sa o transmitem la instanţierea obiectului ca parametru al constructorului.
Un alt mod de a copia o colecţie, 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 colecţie. Deoarece metoda
returnează un Object[], de fiecare data când avem nevoie sa luam un element din sir va trebui sa il
transformam către tipul de baza folosind operatorul de cast.
A doua versiune a metodei este folositoare când vrem sa determinam mărimea șirului de
returnat pe baza șirului pasat ca parametru. Daca mărimea colecţiei este mai mica decât a.length atunci
elementele sunt puse in sir si returnate. Daca șirul e prea mic, se creează un nou sir cu mărimea egala cu
cea a colecţiei, si acel sir este returnat. Daca șirul dat ca parametru este prea mare atunci metoda
înlocuiește cu null, elementele de după ultimul element copiat: a[collection.size()]=null.

19
Iată un mod de folosire a acestei funcţii:

// 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 funcţia equals:

public boolean equals(Object o)

Aceasta metoda se poate suprascrie si vom vedea in curând si cum.

Interfata Iterator

Iteratorul este acel obiect ce permite parcurgerea colecţiilor. Pentru aceasta el trebuie sa
implementeze interfaţa Iterator.
Interfaţa Iterator conţine trei metode:
hasNext() – ce verifica daca mai sunt elemente de iterat
next() – returnează următorul element din lista
remove() – șterge un element din iterator

Cum se folosește un iterator?

Asemănător unui Enumeration, se va parcurge intr-o buclă ca mai jos:

Collection c = ...
Iterator i = c.iterator();
while (i.hasNext())
{
process(i.next());
}

Se verifica daca mai avem element de iterat prin hasNext() si apoi se preia acest element cu
next(), pentru a fi utilizat mai departe.
Metoda remove() este o noutate si nu are echivalent in Enumeration. Atunci când este apelata,
va șterge din colecţia sursa, in cazul in care aceasta operaţie este suportata. Atenţie, daca se iterează in
colecţie cu metoda next() vom primi o excepţie de tipul ConcurrentModificationException.

20
Iterator pentru filtrare

Pe lângă posibilitatea de a parcurge elementele din colecţie, putem aplica un predicat, si anume
interfaţa având o metoda ce filtrează elementele din colecţie.

interface Predicate
{
boolean predicate(Object element);
}

Atunci când apelam metoda next() a iteratorului, va fi returnat acel următor element din
colecţie care îndeplinește condiţia 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 interfaţa 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ă interfaţa 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 condiţii de
polimorfism, vom arunca o eroare in acea funcţie. Pentru a exemplifica cele spuse avem metoda
remove(). In metoda hasNext() se preia următorul element din lista prin intermediul variabilei iter,

22
care este un iterator Java, si se aplica funcţia predicate cu parametru obiectul obţinut prin iter. Daca
funcţia returnează true, nu vom trece mai departe, rămânem pe elementul găsit si se returnează false in
cazul in care mai avem elemente sau true daca am ajuns la finalul colecţiei. Daca însa predicate
returnează false, mergem mai departe in colecţie, pana când funcţia va returna true, sau am ajuns la
finalul colecţiei.
In continuare avem un mic exemplu de folosire a acestor clase, si cu precădere 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 colecţie, si anume list:

List list = Arrays.asList(str);

Apoi se construiește un iterator personalizat pe baza iteratorului lui list:

Iterator i = new IteratorWithPredicate(i1, pred);

Folosind acest iterator se parcurge lista returnându-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 proprietăţii de fail-fast a iteratorilor.


Daca o colecţie 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 adăugăm un element in colecţia list in momentul parcurgerii
acesteia: list.add("Element");, si atunci va apare excepţia:
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 colecţiei sunt:
add()
addAll()
clear()
remove()
removeAll()
retainAll()

Exceptii pentru operatii neimplementate

Excepţia UnsupportedOperationException este aruncata atunci când se apelează o metoda a


unei colecţii, însa aceasta metoda nu este implementata corespunzător. Cu alte cuvinte acea colecţie nu
are capabilitatea de a efectua operaţiunea ceruta. Pentru a exemplifica, sa luam metoda Arrays.asList(),
ce returnează o colecţie de lungime fixa. Nu putem adăuga un nou element in acest tip de colecţie:

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 interfaţa: ArrayList si LinkedList. Interfata List oferă posibilitatea de a lucra ordonat, deci
permite păstrarea secvenţiala a elementelor dintr-o colecţie.

24
Metodele List

Pe lângă metodele implementate pentru ca List este o Collection mai avem:

Metoda Descriere
indexOf() Cauta un element in lista
lastIndexOf() Cauta un element in lista începând de la sfârșitul acesteia
listIterator() returnează iteratorul personalizat al listei
set() modifica un element specificat din lista
get() returnează un element din lista
remove() șterge un anumit element din lista
subList() returnează o parte din lista

Fiecare din clasele concrete vor implementa aceste metode. Pe lângă, vor avea si metode
specifice in funcţie de clasa.

ArrayList

Aceasta clasa este echivalentul clasei Vector, dar sub forma unei colecţii. Un ArrayList este o
colecţie de elemente indexate intr-o anumită ordine, dar nu neapărat sortate.
Aceasta indexare permite accesul rapid la date, dar o insertie si stergere mai lenta.
Iată câteva dintre funcţiile oferite in plus de ArrayList:

Metoda Descriere
ArrayList() Constructorul pentru o lista goala
ensureCapacity() Creează un buffer intern cu o capacitate dubla fata de cea anterioara
removeRange() Șterge un anumit interval de elemente din lista
trimToSize() limitează capacitatea buffer-ului intern la mărimea specificata

Crearea unui ArrayList

Se pot folosi doi constructori pentru acest lucru si anume:

public ArrayList()
public ArrayList (int initialCapacity)

Primul este pentru a instantia un obiect cu o lista goala. Al doilea constructor instanţiază o
colecţie 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 utilizând funcţiile de add:

public boolean add(Object element)


public boolean add(int index, Object element)

Prima funcţie adăuga un obiect in lista la sfârșitul listei. A doua funcţie, ce supraîncărca
funcţia add, permite adăugarea la indexul specificat si elementele de după, sunt împinse mai la
dreapta cu o unitate. Indexarea pornește de la zero.

Pentru a exemplifica adăugarea, avem porţiunea 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 colecţie, putem adăuga prin metodele de addAll, colecţii:

public boolean addAll(Collection c)


public boolean addAll(int index, Collection c)

fiecare element din colecţia, 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 căruia apelam aceste metode se modifica, atunci metoda
va returna true. Daca adăugarea nu este suportata, va apare o excepţie 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 poziţie, daca poziţia este valida. Prima
metoda șterge un element din lista comparând elementele listei cu parametrul. Pentru a verifica
egalitatea se folosește metoda equals(). In acest caz se șterge primul element din lista egal cu
parametrul.
Se poate șterge si o colecţie prin metoda

public boolean removeAll(Collection c)

Aceasta metoda va șterge toate instanţele obiectelor din lista găsita in colecţia 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

Funcţia retainAll este prezentata si mai sus, când am vorbit despre colecţii:

public boolean retainAll(Collection c)

Prin aceasta funcţie se reţin in colecţie doar acele elemente comune listei si colecţiei c.

Stergerea unui interval

Funcţia 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 căutarea elementelor
de la o anumita poziţie. 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 căuta un element si returna indexul pe care acesta se găsește:

public int indexOf(Object element)


public int lastIndexOf(Object element)

Prima metoda iterează începând cu primul element si in momentul in care a găsit elementul egal
cu parametrul returnează poziţia acestuia in lista. Compararea se face pe baza metodei equals.
A doua metoda are același comportament ca si prima, cu menţiunea ca, căutarea începe cu
ultimul element.
De asemenea se poate folosi si funcţia containsAll in condiţiile mai sus menţionate.

Modificarea unui element

Aceasta operaţiune 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.

Mărimea 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 conţine lista.
Pentru a micșora numărul de elemente din lista avem funcţia 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 lângă metoda equals, atunci când se compara doua elemente, se compara si id-urile lor adică
ceea ce returnează funcţia hashCode(). Am precizat in cursul anterior ca aceasta funcţie returnează un
int ce reprezintă codul unic al obiectului, si aceasta funcţie respecta mai multe conditii.
Mai jos avem declaraţia acesteia

public int hascode()

Pentru a înţelege 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 funcţia hashcode() va trebui sa returneze un număr 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 funcţia 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 situaţie, si
anume prin înmulţire 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 forţa 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ănţuite descrise la începutul acestui curs si anume lista
dublu înlănţuita. Vom discuta in continuare doar de metodele specifice LinkedList, celelalte metode fiind
implementate din interfaţa Collection.

Crearea unei LinkedList

Constructorii pentru crearea unei liste simplu înlănţuite sunt:

public LinkedList()
public LinkedList(Collection col)

Adaugarea in LinkedList

Pentru a adăuga un element intr-o lista exista:

public boolean add(Object element)


public boolean add(int index, Object element)

31
Într-adevăr, se pot adăuga elemente la un anumit index, pentru ca si intr-o lista inlantuita
elementele sunt ordonate. De asemenea o lista simplu înlănţuita poate funcţiona ca o stiva sau ca o
coada, ca atare implementează metodele:

public boolean addFirst(Object element)


public boolean addLast(Object element)

Cele doua metode adăugare permit plasarea elementului, fie la sfârșitul listei fie la capul ei.
Pentru a obţine 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 operaţiunea de ștergere nu este suportata, va apare excepţia


UnsupportedOperationException.

Iteratorul LinkedList

Iteratorul se numește ListIterator si extinde Iterator. Deoarece listele dublu înlănţuite conţin
elemente cu referinţa către next si prev, iteratorul va permite deplasarea in ambele direcţii.

Iata mai jos metodele implementate de acest iterator:

Metoda Descriere
hasNext verifica pe direcţia înainte, daca mai sunt elemente
hasPrevious verifica pe direcţia înapoi, daca mai sunt elemente
next returnează următorul elemente
nextIndex returnează indexul din colecţie al următorului element
previousIndex returnează indexul din colecţie al elementului anterior
remove șterge un element din iterator
set modifica elementul curent

32
Pentru a înţelege mai bine lucrul cu aceste liste avem următorul 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 următorul:

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

Interfaţa Set reprezintă un grup de elemente fără duplicate. Nu exista o condiţie anume ce
impune acest lucru: adică sa nu fie duplicate elementele unui Set, ci implementările din clasele Set, sunt
cele care impun aceasta condiţie.
Interfaţa Set deriva din Collections, deci va implementa aceleași metode ca si aceasta interfaţa,
cu specificarea ca elementele trebuie sa fie unice. De asemenea un element, aflat deja in mulţime, nu
poate fi modificat. Aceasta interfaţa 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 colecţii in cursul următor. In continuare, vom studia
operaţiunile 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 asemănători celor studiaţi. In cazul celui de-al treilea constructor, se
poate specifica un factor de mărire a capacitaţii, in cazul in care dorim acest lucru. De exemplu, daca nu
dorim sa mărim cu 100% colecţia, atunci când mărim capacitatea, putem specifica 75%, 50% etc.
Iată mai jos un exemplu de instanţiere a unui HashSet:

String elements[] = {"Englez", "German", "Roman","Italian"};


Set set = new HashSet(Arrays.asList(elements));

Adaugarea elementelor

Se pot adăuga elemente unul cate unul prin metoda:

public boolean add(Object element)

Metoda are ca efect adăugarea elementului, daca acesta nu este in Set, in caz contrar nu se
adăuga elementul si metoda returnează false. Comparaţia intre doua elemente se realizează cu ajutorul
metodei equals. Se poate adăuga si o colecţie de elemente cu:

public boolean addAll(Collection c)

34
Același regim se va aplica si aici, adică doar elemente unice din colecţie, care nu sunt in mulţime,
vor fi adăugate.

Stergerea elementelor

Pentru a șterge un anumit element din mulţime exista metoda:


public boolean remove(Object element)

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

public boolean removeAll(Collection c)

Efectul acestei funcţii este de a elimina din mulţime doar o singura data, elementele găsite in
colecţia c. de exemplu daca avem mulţimea:

{Englez", "German", "Roman","Italian"}

si ștergem din Set colecţia:

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

Va rămâne in mulţimea originala:

{"Roman","Italian"}

Pentru a retine anumite elemente exista funcţia

public boolean retainAll(Collection c)

ce are efectul invers funcţiei removeAll.

Celelalte operaţii din mulţime sunt perfect asemănătoare cu cele din ArrayList, cu menţiunea ca
se va respecta condiţia 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 funcţionare a verificării daca un obiect este in mulţime sau nu, va trebui sa suprascriem
corect ambele metode.
Folosind exact clasa Point, din exemplul de mai sus, iată cum se pot folosi mulţimile:

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)); // true
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);
}
}
}

Observam ca , se pot introduce in mulţime, ca si in ArrayList obiecte de diverse tipuri. Uneori se


dorește 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 același. 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 când încercăm sa adăugăm un element de tip String, altul
decât cel admis in listă. Listele generice admit doar referinţe, si nu admit tipuri primitive.

37
Curs7
TreeSet ................................................................................................................................................ 3
Crearea unui TreeSet ....................................................................................................................... 4
Adăugarea elementelor ............................................................................................................... 5
Comparatorul .............................................................................................................................. 5
Returnarea elementelor............................................................................................................... 6
Folosirea submulţimilor ............................................................................................................... 7
Sortarea colecţiilor .............................................................................................................................. 8
Interfaţa Comparable....................................................................................................................... 8
Comparator ................................................................................................................................... 11
Dictionary, HashTable şi Properties.................................................................................................... 13
Clasa Dictionary ............................................................................................................................. 13
Clasa HashTable ............................................................................................................................. 14
Creearea HashTable ................................................................................................................... 16
Adăugarea perechilor de chei-valoare ........................................................................................ 16
Ştergerea unei perechi .................................................................................................................. 16
Mărimea unui HashTable ........................................................................................................... 17
Operaţii cu HashTable .................................................................................................................... 17
Returnarea obiectelor din HashTable ......................................................................................... 17
Căutarea elementelor ................................................................................................................ 18
Verificarea egalităţii între HashTable.......................................................................................... 19
Clasa Properties ............................................................................................................................. 20
Setarea şi preluarea elmentelor ................................................................................................. 21
Încărcarea şi salvarea datelor ..................................................................................................... 21
Map................................................................................................................................................... 22
Interfaţa Map.Entry ....................................................................................................................... 23
Clasa HashMap .............................................................................................................................. 23
Creearea unui HashMap............................................................................................................. 24
Adăugarea în HashMap .............................................................................................................. 24
Ştergerea unui element ............................................................................................................. 24
Operaţii cu HashMap ................................................................................................................. 25
Clasa WeakHashMap ..................................................................................................................... 25
Clasa TreeMap ............................................................................................................................... 27
Crearea unui TreeMap ............................................................................................................... 27
Operaţiuni cu Map ..................................................................................................................... 28

1
Clasa Collections ................................................................................................................................ 30
Sortarea......................................................................................................................................... 30
Căutarea ........................................................................................................................................ 31

2
TreeSet

Implementările interfeţei Set, sunt HashSet şi TreeSet. Mai jos este o ierarhie completă a
claselor abstracte şi interfeţelor.

Figura 7.1 Ierarhia claselor Set

Clasa TreeSet funcţionează ca şi un HashSet cu menţiunea că păstrează elementele într-o ordine.


Aceste elemente sunt ordonate într-un arbore balansat şi anume un arbore roşu-negru. Păstrând
elementele într-un arbore roşu-negru, costul unei căutări devine logaritmic si ordinul de complexitate
este O(log n) .
Un arbore roşu-negru respectă următoarele reguli:
1. Fiecare nod este roşu sau negru
2. Rădăcina este întotdeauna un nod negru
3. Dacă nodul este roşu, copii lui trebuie sa fie de culoare neagră
4. Fiecare cale de la rădăcină la frunze trebuie sa conţină acelaşi număr de noduri
negre.

Mai jos avem o schiţă a unui arbore de acest tip:

3
Figura 7.2 Arbore roşu-negru

Iată metodele implementate de această clasă:

Nume metoda Descriere


TreeSet() Constructorul unui TreeSet.
add() Adaugă un element în mulţime
addAll() Adaugă o colecţie de elemente în mulţime
clear() Şterge elementele din mulţime
clone() Creează o clonă cu elementele din mulţime
comparator() Returnează un obiect de tip Comparator
contains () Verifică existenţa unui obiect în mulţime
first() Returnează primul element din mulţime
headSet() Returnează un subset de elemente de la începutul mulţimii
isEmpty() Verifică dacă mulţimea este goală.
iterator() Returnează un obiect din set ce permite vizitarea mulţimii
last() Returnează ultimul element din şir
remove() Şterge un element din mulţime
size() Returnează numărul de elemente din subset
subSet() Returnează o submulţime din mulţimea iniţială
tailSet() Returnează o submulţime de elemente de la sfârşitul mulţimii iniţiale

Crearea unui TreeSet

Clasa oferă patru constructori. Primii doi creează TreeSet-uri goale:

public TreeSet()
public TreeSet(Comparator comp)

4
Pentru a menţine o anumită ordine în această structură, elementele adăugate î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 colecţie transmisă ca parametru, este de tip SortedSet, atunci clasa TreeSet va efectua
unele optimizări, la adăugarea elementelor.

Adăugarea elementelor

Pentru a adăuga un singur element se poate apela metoda add:

Metoda add este aceeaşi ca şi pentru HashSet. Diferenţa majoră este că, elementele ce sunt
adăugate trebuie să implementeze interfaţa Comparable sau constructorul TreeSet trebuie să aibă un
parametru de tip Comparator. Dacă nici una din aceste condiţii nu este adevărată, atunci va fi aruncată
excepţia ClassCastException. Fiecare element din această colecţie 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);

5
//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 colecţiei se găsesc pe baza comparării elementelor şi anume


primul element este cel mai mic iar ultimul este cel mai mare.
Dacă nu există nici un element în colecţie, va fi aruncată excepţia NoSuchException.
Un mod de a parcurge elementele unei colecţii 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());
}
}
}

6
Acest exemplu are ca rezultat:
Andrei
Ion
Mircea
Radu
Vasile

După cum se poate vedea, folosirea iteratorului este aceeaşi cu utilizările prezentate în cazul
celorlalte colecţii.

Folosirea submulţimilor

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 colecţia originală. Altfel spus, dacă ștergem un obiect din submulţime, el va dispare şi din arborele
original. Dacă adăugam un element subarborelui, el va fi adăugat în arborele original. Mai jos avem un
exemplu de utilizare a acestei funcţii:

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

7
Dacă dorim ca toElement să fie în subarbore, va trebui, fie să transmitem ca parametru,
următorul element din colecţie, fie să folosim un truc, şi anume să adăugăm ceva la sfârşit:
SortedSet headSet = set.headSet(toElement+"\0");

Dacă nu vrem ca primul element să fie în subarbore, va trebui să apelăm la un truc asemănător:

SortedSet tailSet = set.tailSet(fromElement+"\0");

Sortarea colecţiilor

În acest capitol vom vedea ce mecanism implementează colecţiile, pentru a sorta elementele,
într-un mod cât mai eficient. Există în principiu două modalităţi de a sorta colecţiile: implementând
interfaţa Comparable sau printr-un Comparator personalizat.

Interfaţa Comparable

Clasele definite de Java au deja implementate interfaţa Comparable. Interfaţa Comparable are o
metodă şi anume compareTo(), ce defineşte modul în care se compară şi implicit, se sortează obiectele.
Mai jos avem o serie de clase ce implementează natural aceasta interfaţă:

Nume clasă Aranjare


BigDecimal Numeric (cu semn)
BigInteger Numeric (cu semn)
Byte Numeric (cu semn)
Character Numeric (fără semn)
CollationKey Alfabetic, pe setări locale
Date Cronologic
Double Numeric (cu semn)
File Alfabetic legat de cale
Float Numeric (cu semn)
Integer Numeric (cu semn)
Long Numeric (cu semn)
ObjectStreamField Alfabetic de tip String
Short Numeric (cu semn)
String Alfabetic

Există câteva condiţii pe care trebuie să le respectăm atunci când scriem o metodă compareTo:

public int compareTo(Object Obj)

8
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
excepţia ClassCastException atunci când implementăm interfaţa Comparable.
2. Valoarea de returnat exprimă poziţia relativă într-o ordonare naturală. Metoda compareTo
poate returna trei valori. Va returna un număr negativ, dacă obiectul curent vine înaintea
obiectului cu care se face comparaţia. Va returna un număr negativ, dacă obiectul curent
ajunge în colecţie după elementul cu care se face comparaţia. 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, aşa cum am văzut şi în
cadrul colecţiilor. Această condiţie 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 implementăm metoda, şi ea va fi automat apelată la operaţiile aplicate pe colecţii.

Pentru a demonstra funcţionarea 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

9
//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 întâi după
departament şi apoi după nume. Pentru aceasta se va calcula un număr deptComp pe baza comparării
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 comparării acestora. Altfel returnează rezultatul comparării obiectelor nume de tip
String. Se observă că logica poate fi extinsă în cazul în care avem mai multe proprietăţi de comparat şi
se poate stabili o prioritate în această operaţie.
Pentru a observa cum sunt folosite aceste mecanisme de comparaţie 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 colecţii 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 interfaţa Comparable. De asemenea pot fi comparate, doar obiecte de
acelaşi tip, ceea ce pentru majoritatea cazurilor este suficient.

Comparator

Atunci când nu dorim să impunem ordonarea naturală a claselor, sau clasele nu implementează
interfaţa Comparable, avem la dispoziţie metoda compare din interfaţa 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 următorul scenariu: un manager nou,
inspectează compania descrisă anterior, şi doreşte să afle mai întâi numele angajatului şi apoi
departamentul. Cum această clasă poate implementa doar o singură interfaţă Comparable cu o singură
metodă compareTo va trebui să implementăm 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
comparaţie. Logica este inversă faţă de exemplul anterior, şi anume se verifică mai întâi numele
angajatului şi apoi se verifică numele departamentului din care acesta face parte. Utilizarea acestui
Comparator este în funcţia 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 colecţii ce conţin, nu doar un element, ci pe fiecare poziţie sunt câte
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 aplicaţii acesta fiind o necesitate.
Mai jos avem diagrama ce exprimă ierarhia acestor clase:

Figura 7.3 Ierarhia claselor Dictionary, HashTable, Properties

Un obiect Dictionary funcţionează ca o carte de telefoane. Caut un anumit număr după numele
persoanei. Numele este cheia din dicţionar şi telefonul este valoarea. Presupunem în cadrul acestei
comparaţii, 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 alcătuit din perechi:
<cheie> - valoare

13
Mai jos sunt descrise metodele din această clasă:

Numele metodei Descriere


elements() Returnează un obiect din dicţionar ce permite vizitarea tuturor cheilor
get() 1.0 Returnează o valoare din dicţionar
isEmpty() Verifică dacă un dicţionar este gol
keys() Returnează colecţia de chei din dicţionar
put() Introduce un element format din cheie şi valoare
remove() Şterge un element din dicţionar
size() Returnează numărul de element din dicţionar

Deoarece Dictionary este o clasă abstractă, ea nu va fi folosită direct, ci vor exista implementări ale
acesteia, ca de exemplu HashTable.

Clasa HashTable

Un HashTable este un Dictionary ce se bazează pe un algoritm de hashing ce converteşte cheile


in coduri hash pentru a căuta mai rapid în colecţie. O funcţie hash este o funcţie matematică ce
converteşte o valoare numerică în date de mărime relativ mică, de obicei un întreg, ce poate folosi ca
index într-un şir. Valorile pe care funcţia hash le returnează se numesc coduri hash (a se vedea funcţia
hashCode()). Mai jos avem funcţiile pe care un HashTable le conţine:

Nume metodă Descriere


Hashtable() Constructorul colecţiei
clear() Şterge elementele din colecţie
clone() Creează un HashTable cu aceleaşi elemente
contains() Verifică existenţa unui obiect în colecţie
containsKey() Verifică existenţa unei chei în HashTable
containsValue() Verifică existenţa unei valori în HashTable
elements() Returnează un element ce permite vizitarea colecţiei
entrySet() Returnează un set de perechi cheie – valoare
equals() Verifică egalitatea între două obiecte
get() Returnează valoarea de la o anumită cheie.
hashCode() Calculează codul hash al unei colecţii
isEmpty() Verifică dacă colecţia este goală sau nu.
keys() Returnează o colecţie de chei din HashTable.
keySet() Returnează o colecţie de chei din HashTable.
put() Pune o pereche cheie-valoare în HashTable
putAll() Pune o colecţie de cheie-valoare în HashTable
rehash() Măreşte capacitatea colecţiei
remove() Şterge un element din colecţie
size() Returnează numărul de elemente din colecţie
toString() Returnează întregul HashTable ca un String
values() Returnează o colecţie de valori din HashTable

14
Avantajul principal al acestei colecţii este că inserarea într-o astfel de structură este făcută în
timp O(1) . Nu contează cât de mare este structura, va lua cam acelaşi timp, pentru inserarea unui
element. Cum se poate acest lucru?
Atunci când 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 acelaşi iar pentru două elemente diferite codul este diferit.
Dacă hashcode-ul este acelaşi pentru elemente diferite, discutăm de coliziune. Dacă sunt multe
coliziuni, timpul de inserţie şi căutare, creşte şi se pierde astfel avantajul principal. Dacă există elemente
multiple cu aceeaşi cheie, va trebui traversată o listă înlănţuită 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 funcţia 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 funcţii se calculează suma codurilor ASCII ale fiecărui caracter din nume. La rularea
acestui program avem următoarele coduri:
101
101
114
114

Acesta este motivul apariţiei 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 capacităţi iniţiale. Rata de mărire a capacităţii
implicite, care poate fi specificată în cel de-al doilea constructor este de dublul capacităţii actuale +1. Se
poate modifica prin ajustarea acelui procent.
Al patrulea constructor iniţializează HashTable prin copierea unei colecţii în tabelul actual.

public Hashtable(Map t)

Adăugarea 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 colecţie, 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 colecţie, obiectul returnat este null.
Afişarea unui HashTable se face natural pentru ca implementează metoda toString().

Ştergerea unei perechi

Se face utilizând funcţia remove():

public Object remove(Object key)

16
Dacă cheia key este prezentă în colecţie, atunci va fi eliminata perechea cheie-valoare
specificată prin key şi valoarea este returnată. O altă metodă de a şterge elementele dintr-o colecţie
de acest tip, este clear():

public void clear()

Această metodă va șterge toate perechile din HashTable.

Mărimea unui HashTable

Putem controla mărimea unui HashTable doar după crearea acestuia. Mai jos sunt câteva funcţii
pentru controlul mărimii unui HashTable:

public int size()


public boolean isEmpty()

Aceste funcţii returnează mărimea colecţiei şi verifică dacă HashTable este gol sau nu.
Există o funcţie de redimensionare a colecţiei, dar este protected:

protected void rehash()

Aceasta permite crearea unui nou şir intern mai mare, inserând toate valorile din şirul deja
existent.

Operaţii cu HashTable

Returnarea obiectelor din HashTable

Există câteva moduri de a returna date din HashTable. Cel mai simplu este apelând get:

public Object get(Object key)

dacă este găsită cheia dată ca parametru, atunci funcţia va returna valoarea corespunzătoare
cheii. Dacă nu este găsită, atunci funcţia va returna null.
Dacă vrem să aflăm cheile din colecţie putem folosi:

public Enumeration keys()


public Set keySet()

diferenţa constă în modul în care sunt returnate cheile. Prima metodă returnează ca un
Enumeration. A doua metodă, returnează colecţia sub forma unui Set. Pe lângă chei, se pot returna şi
valorile din HashTable:

public Enumeration elements()


public Collection values()

17
Metoda elements() returnează o mulţime de valori sub forma unei Enumeration. Metoda
values() returnează aceleaşi date, dar sub forma unei Collection.
Cea mai complexă metodă de returnare a elementelor dintr-o colecţie HashTable este entrySet()

public Set entrySet()

Aceasta returnează o colecţie de tip Set ce conţine 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 colecţii:

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 colecţie:

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());
}

Căutarea elementelor

Clasa Hashtable conţine trei metode, care ne permit căutarea unei chei sau a unei valori în
colecţie. Cea mai simplă rămâne metoda get() discutată mai sus.

public boolean containsKey(Object key)

Această funcţie verifică existenţa unei chei în colecţie. Alte două metode vor verifica existenţa
unei valori în colecţie:

public boolean contains(Object value)


public boolean containsValue(Object value)

Ambele realizează acelaşi lucru, însă pentru că HashTable implementează şi interfaţa Map, avem
această duplicitate. Este de recomandat să folosim aceste două funcţii cât mai puţin posibil, deoarece se
parcurge întreaga colecţie, algoritmul având cel un ordin O(n).

18
Verificarea egalităţii între HashTable

Aceasta se poate realiza folosind funcţia equals():

public boolean equals(Object o)

Egalitatea este definită de interfaţa 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 când au aceleaşi perechi cheie-valoare. Ordinea nu contează.
HashTable este imutabil. Dacă un HashTable este iniţializat cu un set de elemente, atunci pentru
redimensionare va trebui să iniţializăm un nou obiect. Pentru a obţine un Map, ce nu este imutabil avem
următoarea funcţie 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 număra cuvintele dintr-un fișier.

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ă cuvântul citit din fişier, iar valoarea reprezintă numărul de apariţii al acelui
cuvânt.
Metoda processLine() împarte linia citită în cuvinte separate prin spaţiu, şi apelează metoda
addWord. Aici se verifică dacă mai avem înregistrat cuvântul în colecţie, dacă nu adăugam perechea
(cuvânt nou, 1) semn că apare o dată. Altfel, dacă a mai fost înregistrat, incrementăm valoarea
corespunzătoare cheii, cu unu.
Pentru un fișier 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ă atât cheile cât şi valorile
sunt String. Atunci când lucrăm şi cu HashTable şi cu Properties, va trebui să ţinem cont de ierarhie: un
Properties poate fi un HashTable, însă un HashTable nu poate funcţiona ca un Properties.

20
Mai jos avem metodele specifice acestei clase:

Nume Descriere
Properties constructorul clasei
getProperty() returnează o valoare pentru o cheie din listă
list() listează proprietăţile li valorile aferente
load () încarcă proprietăţile dintr-un stream
propertyNames() returnează o colecţie de chei din listă
setProperty() setează o pereche cheie-valoare în listă
store() salvează lista de proprietăţi î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 lângă aceste metode, există o funcţie ce
returnează o listă cu cheile din Properties:

public Enumeration propertyNames()

Încărcarea şi salvarea datelor

Aceste două operaţii 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 colecţii:
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 fişier este:

#Aici vin comentarii


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

Metoda load() va încerca să găsească în fişierul sursă, o asemenea structură, de aceea este bine
să respectăm formatul, deşi metoda va putea să încarce diferite formate, însă nu de fiecare dată cu
rezultatul aşteptat.

Map

Interfaţa Map aparţine 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.
Deşi face parte din FrameWork, interfaţa Map nu extinde Collection, ci este rădăcina unei noi
ierarhii. Sunt patru clase ce implementează această interfaţă: HashMap, WeakHashMap, TreeMap şi
HashTable. Toate elementele din aceste colecţii sunt de tip Map.Entry. Figura de mai jos reprezintă
ierarhia acestor clase şi interfeţe.

Figura 7.5 Ierarhia claselor Map

22
Interfaţa Map.Entry

Elementele unei Map sunt de tip Map.Entry, o interfaţă conţinută în Map. Fiecare pereche de
cheie-valoare este o instanţă a acestei interfaţă. Nu vom crea propriu-zis instanţe, dar clasele concrete
vor returna obiecte ce deja au implementat această interfaţă.
Mai jos avem funcţiile acestei interfeţe:

Metoda Descriere
equals() verifică egalitatea cu un alt obiect
getKey() returnează cheia din Map.Entry
getValue() returnează valoarea din Map.Entry
hashCode() calculează codul hash pentru obiectul actual
setValue() schimbă valoarea din Map.Entry

Iată cum se pot apela aceste metode pentru a afişa entităţile dintr-o colecţie 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());
}

Acelaşi 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 interfeţei Map şi anume o colecţie de perechi-
valori în care elementele nu sunt ordonate. Mai jos sunt metodele implementate:

Nume metodă Descriere


containsKey() Verifică existenţa unei chei in hash map
containsValue() Verifică existenţa unei valori in hash map
entrySet() Returnează o colecţie de perechi sub forma unui map

23
get() Returnează valoarea pentru o anumită cheie
isEmpty() Verifică dacă o colecţie este goală sau nu
keySet() Returnează toate cheile din hash map
put() Plasează o pereche în hash map
putAll() Introduce in hash map o colecţie de perechi cheie-valoare
remove() Scoate din colecţie o pereche
size() Returnează numărul de elemente dintr-un hash map
values() Returnează o colecţie de valori din hash map

Creearea unui HashMap

Există patru constructori pentru crearea unui HashMap:

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

În timp ce primii trei vor iniţializa o colecţie goală, al patrulea va crea hash map-ul pe baza
elementelor din parametru.

Adăugarea în HashMap

Pentru a adăuga în hash map avem două metode:

public Object put(Object key, Object value)


public void putAll(Map map)

Spre deosebire de HashTable, atât cheia cât şi valoarea unui element nou introdus pot fi null.
Pentru a copia o colecţie de perechi de la un Map la altul se foloseşte a doua metodă. Afişarea
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 colecţie atunci valoarea null, va fi returnată. Atenţie, şi null poate fi valoare
legitimă în colecţie. De aceea este de dorit să evităm plasarea null ca valoare în colecţie.
Altă metodă de a şterge toate elementele din colecţie este:
public void clear()

24
Operaţii cu HashMap

Se pot returna obiectele din colecţie, iar pentru aceasta avem o serie de metode:

public Object get(Object key)

Dacă cheia nu este găsită în colecţie, se returnează null. Altfel se va returna valoarea de la cheia
specificată ca parametru. Aceeaşi remarcă ar fi de făcut, că elementele într-un HashMap pot fi null. O
alta metodă este :

public Set keySet()

şi returnează colecţia de chei sub forma unei mulţimi. Pentru a prelua aceste chei ca o colecţie 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 colecţie de obiecte de tip Map.Entry.

Căutarea elementelor se face prin două metode:

public boolean containsKey(Object key)


public boolean containsValue(Object value)

Prima metodă este asemănătoare metodei get(), dar în loc de a returna o valoare a obiectului,
vom avea o valoare booleană, true dacă cheia se află în colecţie sau false în caz contrar.
A doua metodă, verifică existenţa unei valori specifice în HashMap. Valorile sunt comparate, şi
această comparare este făcută în timp liniar, de aceea este mai bine să folosim prima metodă.

Clasa WeakHashMap

Această clasă funcţionează 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 înţelege această frază trebuie să înţelegem ce este o referinţă slabă. În general, obiectele care nu
conţin date direct, ci conţin referinţe la un alt obiect sunt denumite referinţe. Aceste clase se găsesc În
pachetul java.lang.ref, de exemplu public abstract class Reference<T>.
Există patru tipuri de referinţă:
1. Referinţe puternice, nu au o clasă specială
2. Referinţe soft sunt ca un cache. Când memoria este puţină GC va elibera arbitrar aceste
referinţe.

25
3. Referinţe slabe: acestea sunt mai slabe decât cele soft. Dacă singura referinţă a unui obiect
sunt doar de tip slab, GC va putea şterge obiectul oricând.
4. Referinţe 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 referinţe 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 înţeles acum pentru că nu am prezentat încă firele de execuţie (Thread).
Totuşi concluzia este că după apelarea Garbage Collector-ului, se pierd legăturile slabe, deci se pierd
elementele din map. Acesta este rezultatul rulării programului:

{Cheie=Valoare}
Main waiting
Thread waiting
{}

Clasa TreeMap

Ultima implementare a interfeţei Map este un TreeMap. Un TreeMap este un Map care menţine cheile
într-o ordine prin intermediul unui arbore balansat, de tip roşu-negru. Pe lângă metodele expuse de
Map şi discutate mai sus avem următoarele:

Metoda Descriere
firstKey() Returnează prima cheie din Map
headMap() Returnează sub map-ul de la începutul map-ului original
lastKey() Returnează ultima cheie din colecţie
subMap() Returnează un subarbore oarecare din colecţie
tailMap() Returnează subarborele de la sfârşitul celui original

Acestea provin tocmai din faptul că TreeMap implementează interfaţa SortedMap, lucru ce impune
existenţa acestor metode.

Crearea unui TreeMap

Există patru constructori pentru un TreeMap. Primul este fără argument creează un Map gol, al doilea
este un constructor de copiere:

public TreeMap()
public TreeMap(Map map)

Pe lângă aceştia, 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
Operaţiuni cu Map

Pentru a vizualiza elementele unui arbore avem la dispoziţie 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 până la acea cheie, iar pentru tailMap avem parametrul fromKey ce indică subarborele de la
fromKey până la sfârşit. Pentru a include şi elementele de la capăt avem următorul 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 operaţiuni:

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",
"Remus");
System.out.println (group);

System.out.println (students.remove ("Andrei"));


System.out.println (students.remove ("Boggdan"));
System.out.println (students.containsKey ("Bogdan"));
System.out.println (students.containsKey ("Andrei"));
System.out.println (students.containsValue (7.8));

System.out.println (students);
}
}

În cazul acestui exemplu, instrucţiunea :


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ă considerăm care este
rădăcina 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 colecţii. Clasa Arrays este clasa corespondentă, ce lucrează cu şiruri.
Toţi membrii acestei clase sunt descrişi mai jos.

Metoda Descrierea
EMPTY_LIST Reprezintă o listă goală imutabilă
EMPTY_MAP Reprezintă o colecţie Map goală imutabilă
EMPTY_SET Reprezintă o mulţime goală imutabilă
binary_search() caută un element în listă folosind tehnica de căutare binară
copy() copiază elementele dintre două liste
enumeration() converteşte o listă la un Enumeration
fill() umple lista cu un element anume
max() caută maximul dintr-o colecţie
min() caută minimul dintr-o colecţie
nCopies() creează o listă imutabilă cu multiple copii ale unui element
reverse() inversează elementele într-o listă
reverseOrder() returnează un comparator ce inversează ordinea elementelor
shuffle() reordonează elementele aleatoriu
singleton() returnează un set imutabil de un element
singletonList() returnează o listă imutabilă de un element
singletonMap() returnează un Map imutabil de un element
sort() reordonează elementele în listă

Pe lângă acestea, mai sunt o serie de metode ce creează colecţii thread-safe, însă nu vom
discuta despre aceasta acum. În continuare vom prezenta câteva 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 obţinută 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());

Căutarea

Căutarea 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)

Căutarea binară constă în împărţirea unui sir sortat în două şi compararea elementului de căutat
cu mijlocul șirului. Dacă nu găsim elementul, căutăm mai departe. Cum? Dacă elementul din mijloc este
mai mic decât numărul de căutat, vom căuta în dreapta mijlocului, dacă nu vom căuta în stânga. Adică,
vom căuta fie in subşirul delimitat de 0 şi mijloc adică stânga, fie în subşirul delimitat de mijloc si
sir.lentgh adică dreapta. Aplicăm recursiv aceasta metodă până când elementul căutat este egal cu
mijlocul sau nu mai avem unde căuta pentru ca subşirul a devenit gol. Iată mai jos un exemplu pentru
această căutare:

În şirul 1 3 4 6 8 9 11 14 15 17 19 20 23 25 vom căuta elementul 4. Șirul are 14 elemente vom


căuta mijlocul si anume 13/2 = 7. Elementul de pe poziţia 7 este14, comparăm cu 4 este mai mare deci
vom căuta în stânga: 1 3 4 6 8 9 11 14. Aplicăm acelaşi algoritm pe un subşir de 8 elemente şi mijlocul va
fi la 7/2 = 3. Comparăm elementul de pe poziţia 3 si anume 6 cu 4, este mai mare. Aplicam aceeaşi
strategie pe subşirul 1 3 4 6. Avem 4 elemente / 2 = 2 şi găsim mijlocul egal cu elementul de căutat. Se
încheie algoritmul.
În funcţiile de mai sus key este elementul de căutat iar list este șirul nostru.
Mai jos avem şi un exemplu de căutare 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 interfaţa Runnable....................................................................................................... 4
Crearea unui fir de execuţie ................................................................................................................. 4
Interfaţa Runnable ........................................................................................................................... 4
Îmbunătăţiri aduse exemplului ........................................................................................................ 7
Moştenirea unui Thread .................................................................................................................. 9
Crearea mai multor fire de execuţie................................................................................................... 11
Când se încheie un fir de execuţie? ................................................................................................ 14
Priorităţi în fire de execuţie ............................................................................................................... 16
Sincronizarea ..................................................................................................................................... 18
Folosirea metodelor sincronizate ................................................................................................... 19
Blocul synchronized ....................................................................................................................... 21
Comunicarea între fire de execuţie .................................................................................................... 22
Suspendarea, reluarea şi oprirea firelor de execuţie .......................................................................... 26
Grupuri de fire de execuţie ................................................................................................................ 29
Alte metode................................................................................................................................... 30
Bazele multithreading

Există doua tipuri distincte de multitasking: unul bazat pe procese şi al doilea bazat pe fire de
execuţie sau thread-uri. Este important să facem distincţia dintre cele două. Un proces este, în esenţă,
un program ce se află în execuţie. De aceea multitasking bazat pe procese înseamnă faptul că două sau
mai multe programe rulează în acelaşi timp. De exemplu, compilatorul Java va permite crearea unor noi
programe, în timp ce folosiţi un editor de text, sau navigaţi 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
aplicaţia de mail Outlook, sau un client de mail va permite si trimiterea/primirea de mail-uri in timp ce
compuneţi un nou mail, sau verificaţi lista de priorităţi dintr-o zi, sau căutaţi un anumit mail, etc.
Avantajul principal al multithreading este că permite scrierea unor programe foarte eficiente,
deoarece se elimină spaţiile inutile temporale prezente în multe programe, atunci când acesta este idle.
Majoritatea dispozitivelor I/O sunt mai lente decât CPU. De aceea programul va petrece majoritatea
execuţiei aşteptând informaţia de la aceste dispozitive. În acest timp, în cazul folosirii multithreading, se
pot executa instrucţiuni 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 execuţie este o parte dintr-un proces executat secvenţial, o serie de instrucţiuni ce vor
fi executate secvenţial. Folosirea a două sau mai multe thread-uri duce la execuţia în paralel a acestor
serii de instrucţiuni în cadrul unui proces. Mai jos avem reprezentat modul de funcţionare al unui proces
pe un singur thread.

Figura 1. Un proces cu un singur fir de execuţie

Adevărata utilitate a unui thread intervine în cazul în care mai multe fire de execuţie coexistă în
acelaşi proces.

Figura 2. Un proces cu două fire de execuţie


Un fir de execuţie se poate afla în mai multe stări. Poate fi running, adică în execuţie. Poate fi
ready to run, în clipa în care dispecerul îi va asigura timpul în CPU. Un thread aflat în execuţie poate fi
suspendat, ceea ce înseamnă că este oprit pentru o perioadă. Poate fi reluat, mai târziu, adică va intra în
starea de resumed. Un thread poate fi blocat în momentul când aşteaptă o resursă. Un thread poate fi
terminat atunci când execuţia se încheie şi nu va mai fi reluat.
Figura de mai jos reprezintă stările unui fir de execuţie.

Figura 3. Stările de execuţie ale unui thread

Pe lângă aceste caracteristici, mai este nevoie de o anumită funcţionalitate, numită sincronizare,
ce permite execuţia 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 cât mai convenient lucrul cu thread-uri.
Clasa Thread şi interfaţa Runnable

În Java, sistemul multithreading este construit pe clasa Thread şi interfaţa Runnable. Pentru a
crea un nou fir de execuţie, va trebui să extindem clasa Thread sau să implementăm interfaţa Runnable.
Clasa Thread defineşte câteva metode care ajută la tratarea situaţiilor ce intervin în lucrul cu
thread-uri. Iată cele mai folosite:

Medoda Descriere

final String getName( ) Se obţine numele thread-ului


final int getPriority( ) Se obţine prioritatea thread-ului
final boolean isAlive( ) Determină faptul că un thread mai este în execuţie sau nu.
final void join( ) Aşteaptă terminarea unui thread pentru a continua.
void run( ) Entry point pentru un thread.
static void sleep(long milliseconds) Suspendă un thread un număr de milisecunde
void start( ) Începe execuţia unui fir prin apel run()

Toate procesele au cel puţin un fir de execuţie ce se numeşte firul principal sau main thread.
Acesta se va executa înainte celorlalte eventual fire de execuţie ce vor fi create ulterior.

Crearea unui fir de execuţie

Un fir de execuţie se poate crea prin instanţierea unui obiect de tip Thread. Clasa Thread
încapsulează un obiect ce implementează interfaţa Runnable. Există două moduri de a crea un fir:
1. Implementarea interfeţei Runnable
2. Extinderea (moştenirea) clasei Thread

Interfaţa Runnable

Această interfaţă defineşte doar o metodă numită run() declarată astfel:

public void run()

În cadrul acestei metode, veţi defini codul ce constituie noul fir de execuţie. Metoda run() poate
apela alte metode, folosi alte clase, şi declara variabile ca orice metodă. Diferenţa majoră este că run()
reprezintă entry point pentru un nou thread din cadrul procesului. Acest thread îşi încheie execuţia
atunci când se iese din funcţia run (cu return).
Modul de lucru este următorul:
1. Se creează o clasă ce implementează interfaţa Runnable.
2. Se instanţiază 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 până când nu apelăm 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 interfeţei 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 întâi se construieşte obiectul
// ce va conţine thread-ul
MyThread mt = new MyThread("Child #1");
// se construieşte 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 execuţie 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 . Execuţia unui thread începe cu start. Pe firul principal de execuţie,
instrucţiunile vor fi rulate ca şi când start() ar fi o metodă obişnuită. Firul principal de execuţie va rula o
buclă while în care aşteaptă ca mt.count să ajungă la 10. Acest lucru se va întâmpla, datorită faptului
că în thread-ul secundar count creşte.
Iată rezultatul rulării 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 următoarea:
Figura 4. Diagrama execuţiei unui fir

Îmbunătăţiri aduse exemplului

Programul anterior este valid, însă poate fi optimizat:


1. Putem avea un thread care să înceapă execuţia la instanţierea obiectului. În cazul MyThread
aceasta se face instanţiind 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
modificăm numele la instanţierea noului Thread. Pentru aceasta avem următorul
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 îmbunătăţirile specificate:
// crearea unui fir de execute
//prin implementarea interfeţei Runnable
class MyThread implements Runnable
{
int count;
//firul de execute cu care vom lucra
Thread thread;
MyThread(String name)
{
count = 0;
//instanţiem un nor fir
thread = new Thread(this, name);
//firul va porni la apelul constructorului
//adică la instanţierea 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 instanţierea 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.");
}
}

Modificările majore sunt: mutarea obiectului de tip Thread în cadrul clasei MyThread şi pornirea
acestui fir din constructorul clasei MyThread.

Moştenirea unui Thread

Implementarea interfeţei Runnable este un mod de a crea o clasă ce poate instanţia fire. A doua
ar fi extinderea unei clase numită Thread. Atunci când 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ă execuţia.
Pornind de la exemplul anterior, putem transforma acesta aplicând moştenirea lui Thread:
1. Se schimba declaraţia lui MyThread ca să extindă Thread:

class MyThread extends Thread


{
….

2. Se şterge această declaraţie, 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 execuţie.


MyThread(String name)
{
super(name); // apelez constructor cu nume
count = 0;
start(); // încep execuţia
}

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

Thread(String name);

4. Se schimbă run() astfel încât acum apelează getName().


După aceste modificări programul arată astfel:

class MyThread extends Thread


{
int count;
//firul de execuţie 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 modificări faţă de programul precedent.


Crearea mai multor fire de execuţie

Exemplele anterioare conţineau doar un singur fir de execuţie. Totuşi, programul poate crea
oricâte astfel de fire. Următorul 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 execuţia
}
//entry point in firul de execuţie
public void run()
{
System.out.println(thread.getName() + " starting.");
try
{
// se măreşte 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 execuţie
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
//aşteaptă 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 execuţie vor rula în paralel cu firul principal, acesta aşteptând ca cele trei sa îşi încheie
execuţia. O diagramă a acestei funcţionalităţi este în figura de mai jos:

Figura 5. Diagrama execuţiei pe mai multe fire


Rezultatul rulării 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 execuţie ( unul prin
extinderea Thread şi altul prin implementarea Runnable ) şi care este mai performant?
Răspunsul este că o clasă Thread defineşte 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 condiţia atunci când
clasa implementează interfaţa Runnable. Aşa că dacă nu se doreşte suprascrierea altor metode din
Thread, atunci este mai bine să folosim a doua metodă, pentru simplitate.
Când se încheie un fir de execuţie?

Este util să ştim când un fir de execuţie 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 soluţie slabă din punct de vedere tehnic. Clasa Thread oferă două moduri
de a determina faptul că un fir de execuţie s-a încheiat. Primul este prin funcţia isAllive():

final boolean isAllive()

Această metodă returnează true, dacă firul de execuţie, pentru care a fost apelată, încă rulează,
şi false altfel. Pentru a verifica funcţionalitatea isAlive(), vom modifica versiunea clasei
ThreeChildThreads.

class ThreeChildThreads {
public static void main(String args[])
{
System.out.println("Main thread starting.");
//cream trei fire de execuţie
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
//aşteaptă 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ă funcţie, oferind robusteţe aplicaţiei.
Cel de-al doilea mod de a aştepta ca un fir să îşi încheie execuţia este join():

final void join() throws IntrerruptedException


Această metodă va impune aşteptarea terminarea firului pentru care a fost apelată. Numele
provine de la un concept de crearea a unui thread, lansarea a acestuia şi aşteptarea terminării lui pentru
ca cel care a creat firul să se “unească” cu acesta. Există forme adiţionale ale acestei funcţii, prin care se
permite specificarea unui număr de milisecunde, sau nanosecunde, timp de aşteptare ca acel fir să se
încheie. Mai jos este exemplul pentru această funcţie:

class ThreeChildThreads
{
public static void main(String args[])
{
System.out.println("Main thread starting.");
//cream trei fire de execuţie
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
//aşteaptă 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.");
}
}

Aşteptarea ca un fir de execuţie să se încheie are loc prin apelurile de tipul:


mt1.thread.join();
Priorităţi în fire de execuţie

Fiecare fir de execuţie are asociat o anumită prioritate. Aceasta este determinată de cât timp în
CPU este alocat acelui thread. În general priorităţile joase denotă puţin timp, iar cele mari denotă mai
mult timp în CPU. Evident, cât de mult timp va avea acces la CPU, un fir de execuţie, influenţează direct,
fluxul logic al programului.
Este important să înţelegem factorii care influenţează accesul la CPU. De exemplu, dacă un fir cu
prioritate mare aşteaptă o resursă auxiliară, atunci el va fi blocat, şi alt fir cu o prioritate mai mică va
avea acces. Totuşi odată ce thread-ul cu prioritatea mai mare are resursa, va opri execuţia firului cu
prioritate mai mică pentru aşi relua propria execuţie. Alt factor care poate afecta dispecerul de fire de
execuţie, este modul în care sistemul de operare implementează multitasking. De aceea, doar prin
asignarea de priorităţi mai mici, mai mari, nu va influenţa neapărat rapiditatea cu care un fir rulează.
Prioritatea înseamnă un acces probabil mai mic sau mai mare la CPU.
Atunci când un fir de execuţie porneşte, prioritatea sa este egală cu cea a thread-ului părinte.
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 obţine prioritatea actuală a unui fior
de execuţie apelând metoda getPriority():

final int getPriority()

Exemplul următor demonstrează folosirea a două fire de prioritate diferită. Metoda run()
conţine o bulcă while care contorizează iteraţii. Această bulcă se opreşte când numărul de iteraţii
depăşeşte 1000000 sau variabila stop a fost setată pe true. Iniţial 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
execuţie. Dacă este diferită, se realizează modificarea acestei variabile. Aceasta permite vizualizarea
accesării 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 porneşte thread-ul
Priority(String name)
{
thread = new Thread(this, name);
count = 0;
currentName = name;
}
// incepe execuţia 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 primeşte o prioritate mai mare decât 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();
//aşteptarea terminării 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 rulării 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 maşina pe care va rula.

Sincronizarea

Atunci când folosim mai multe fire de execuţie, este necesar uneori, să coordonăm 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 fişier, efectuată din două fire de execuţie trebuie
controlată. Aceasta se realizează astfel: un fir este pus în stare de aşteptare până când firul care are
acces la resursă termină acţiunea, urmând ca firul suspendat să îşi reia execuţia.
Sincronizarea în Java este realizată prin intermediul conceptului de monitor, ce controlează
accesul la un obiect. Monitorul funcţionează implementând conceptul de blocare. Când un obiect este
blocat de un fir de execuţie, nici un alt fir de execuţie nu are acces la acel obiect. Când firul de execuţie
eliberează acel lock, obiectul devine disponibil pentru celelalte fire de execuţie.
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 cuvântul cheie synchronized şi alte
câteva 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 cuvântul cheie synchronized. Atunci când acea
metodă este apelată , firul de execuţie în care s-a făcut apelul intră în obiectul monitor, care blochează
obiectul. În timpul blocării, nici un alt thread nu poate intra în metodă, sau accesa orice metodă
sincronizată. Atunci când firul de execuţie revine din metodă, se realizează deblocarea.
Următorul program demonstrează folosirea acestei metode de sincronizare:

//clasa ce calculează o sumă de elemente


//si conţine 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 execuţia 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);
}
}
Acest exemplu este compus din trei clase. Prima şi anume SumArray conţine 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 execuţie propriu zis. Firul de
execuţie 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 execuţie 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
două fire de execuţie ce au ca parametru acelaşi şir. Cu alte cuvinte, aceeaşi metodă
sa.sumArray(a); va fi apelată în ambele fire, creând 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 #1 is 1
Child #2 starting.
Running total for Child #1 is 3
Running total for Child #1 is 6
Running total for Child #1 is 10
Running total for Child #1 is 15
Running total for Child #2 is 1
Sum for Child #1 is 15
Child #1 terminating.
Running total for Child #2 is 3
Running total for Child #2 is 6
Running total for Child #2 is 10
Running total for Child #2 is 15
Sum for Child #2 is 15
Child #2 terminating.
Aceasta înseamnă că execuţia a avut loc conform aşteptărilor şi sumele calculate sunt cele
prevăzute. Dar dacă eliminăm cuvântul cheie synchronized din cadrul metodei, iată ce obţinem:

Child #2 starting.
Running total for Child #2 is 1
Child #1 starting.
Running total for Child #1 is 1
Running total for Child #2 is 3
Running total for Child #1 is 5
Running total for Child #2 is 8
Running total for Child #1 is 11
Running total for Child #2 is 15
Running total for Child #1 is 19
Running total for Child #1 is 24
Running total for Child #2 is 29
Sum for Child #1 is 29
Child #1 terminating.
Sum for Child #2 is 29
Child #2 terminating.
Evident, rezultatele pot varia în funcţie de maşină, sistem de operare, etc.

Blocul synchronized

Deşi crearea metodelor sincronizate, în cadrul claselor, este un mod eficient şi uşor de
sincronizare, nu funcţionează pentru orice caz. De exemplu, să presupunem că dorim accesul sincronizat
la o metodă care nu este declarată ca atare. Acest lucru se poate întâmpla î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 dispoziţie blocuri sincronizate:

synchronized(object)
{
//instrucţiunile ce trebuie sincronizate
}

Aici object este referinţa către 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 execuţie
apelant. De exemplu putem reedita programul mai sus considerând că metoda a fost declarată fără
cuvânt 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 execuţie

Să considerăm următoarea situaţie. 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
aşteptare 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ă soluţie ar fi ca T
să elibereze temporar controlul obiectului, permiţând altui fir să ruleze. Când R devine disponibilă, T
poate fi notificat şi îşi poate relua execuţia. Acest tip de abordare, presupune existenţa unei comunicări
între fire diferite. În Java aceasta se face prin metodele wait(), notify() şi notifyAll().
Aceste metode aparţin tuturor obiectelor, deoarece sunt implementate în clasa Object. Aceste
metode pot fi apelate dintr-o metodă sincronizată. Atunci când un thread este blocat temporar,
apelează wait(). Aceasta va face ca firul să intre în sleep, şi monitorul pentru acel obiect să fie eliberat,
permiţând altui fir să folosească obiectul. Mai târziu, firul ce era oprit, este trezit, atunci când un alt fir
care va intra în acelaşi 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ă aşteptarea până când apare o notificare sau până
când o perioada de timp, dată de parametrii, trece. Mai jos avem formele funcţiilor 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ţ celălalt thread
return;
}
System.out.print("Tic ");
notify(); //il las pe tac sa ruleze
try
{
wait(); // aştept 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ă celălalt fir
}
System.out.println("Tac");
notify(); // il las pe tic sa ruleze
try
{
wait(); // aştept ca tic sa ruleze
}
catch(InterruptedException exc)
{
System.out.println("Thread interrupted.");
}
}
}
class MyThread implements Runnable
{
Thread thread;
TicTac ttOb;
// un nou fir de execuţie
MyThread(String name, TicTac tt)
{
thread = new Thread(this, name);
ttOb = tt;
thread.start(); // pornesc firul
}
// încep execuţia 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, conţine
două metode sincronizate:
synchronized void tic(boolean running)
synchronized void tac(boolean running)
Acestea, au următoarea logică:

if(!running)
{ //opresc metoda
notify(); //anunţ celălalt thread
return;
}
System.out.print("Tic ");
notify(); //il las pe tac sa ruleze
wait(); // aştept pe tac

Dacă parametrul transmis nu este încă false (mai am drept să rulez metoda) atunci afișez Tic şi
dau dreptul lui Tac, urmând să aştept până când thread-ul ce rulează Tac, să mă înştiinţeze că a rulat în
monitor. Dacă parametrul running este false, atunci anunţ celălalt fir de execuţie şi părăsesc metoda.
Acelaşi lucru are loc şi pentru metoda tac, evident referitor la thread-ul Tic.
Clasa MyThread, ce implementează interfaţa Runnable, reprezintă cele două fire de execuţie
suport pentru metodele tic() şi tac(). În firul de execuţie 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(). Acelaşi lucru este valabil viceversa pentru Tac.
În clasa ThreadsDemo se creează cele două fire de execuţie, se lansează şi se aşteaptă
terminarea lor. Iată ce se întâmplă la rularea programului:

Tic Tac
Tic Tac
Tic Tac
Tic Tac
Tic Tac
Dacă modificăm însă, metodele tac() şi tic() eliminând metodele de comunicare şi anume
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
Suspendarea, reluarea şi oprirea firelor de execuţie

Uneori este util să suspendăm execuţia unui thread. De exemplu, un fir de execuţie poate fi
folosit pentru afişarea orei. Dacă utilizatorul nu doreşte 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( )

Totuşi folosirea acestora este îngrădită, de unele probleme cauzate de suspend() în trecut. De
exemplu, dacă un thread obţine un lock pe o zonă critică, şi el este suspendat, acele lock-uri sunt eterne.
În acest timp, alte fire de execuţie aşteaptă deblocarea lor. Metoda resume() este de asemenea
învechită. Nu cauzează probleme, dar nu poate fi folosită fără metoda suspend(). De asemenea metoda
stop() a fost considerată învechită.
Următorul exemplu reprezintă un mod în care se pot folosi aceste funcţii:

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 execuţie
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 execuţie
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, defineşte două variabile booleane, suspended şi stopped. Metoda run()
conţine blocul sincronizat ce verifică variabila suspended. Dacă este true atunci metoda wait() este
apelată şi suspendă execuţia firului. Metoda care stabileşte valoarea variabilei la true, este
mysuspend(). Pentru a relua firul, avem myresume(), care setează variabila suspended pe true şi
apelează notify(), pentru a relua execuţia firului. Ultima metodă este mystop() şi conţine paşii care duc la
oprirea firului de execuţie. În clasa SuspendDemo, metodele descrise sunt apelate în alternanţă pentru
a opri/relua execuţia firului de câteva 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 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
Suspending thread.
Resuming thread.
81 82 83 84 85 86 87 88 89 90
91 92 93 94 95 96 97 98 99 100
101 102 103 104 105 106 107 108 109 110
111 112 113 114 115 116 117 118 119 120
Stopping thread.
My Thread exiting.
Main thread exiting.

Grupuri de fire de execuţie

Fiecare fir de execuţie 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ă instrucţiune.
Dacă vom crea un fir fără a specifica grupul din care face parte, automat va fi pus în acelaşi grup
cu cel al firului părinte. Acest grup se mai numeşte şi current thread group. Atunci când o aplicaţie este
pornită, Java creează un TreadGroup numit main. Pentru a crea un grup separat de acesta, explicit, avem
la îndemână următoarele construcţii:

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 execuţie avem metoda getThreadGroup():

theGroup = myThread.getThreadGroup();

Iată un mod de folosire a acestei informaţii:

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ă funcţie preia numărul de fire din grup si le afişează.

Alte metode

Clasa ThreadGroup conţine câteva atribute şi metode dintre care:

· getMaxPriority şi setMaxPriority pentru manipularea priorităţii


· getDaemon şi setDaemon pentru controlul acestori tipuri de fire
· getName pentru preluarea numelui
· getParent şi parentOf pentru manipularea numelui firului părinte

Iată, spre exemplu, cum se poate stabili prioritatea maximă, atât pe fir cât ş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ă conţină un applet ....................................................................................................... 5
Iniţializarea şi terminarea unui applet .............................................................................................. 6
Redesenarea.................................................................................................................................... 7
Cum se executa un applet ................................................................................................................ 9
Folosirea status-ului din fereastră .................................................................................................. 10
Transmiterea parametrilor către un applet .................................................................................... 11
Clasa Applet................................................................................................................................... 13
Operaţiuni grafice într-un applet ....................................................................................................... 14
Când să desenăm? ......................................................................................................................... 15
Utilizarea fonturilor ....................................................................................................................... 15
Tratarea evenimentelor ..................................................................................................................... 18
Modelul delegării ........................................................................................................................... 18
Evenimente ................................................................................................................................... 19
Sursele evenimentelor ................................................................................................................... 19
Ascultători de evenimente ............................................................................................................. 19
Clase de evenimente...................................................................................................................... 20
Folosirea modelului de delegare .................................................................................................... 20
Tratarea evenimentelor de mouse ................................................................................................. 21

1
Introducere in applet-uri

Applet-ul este poate cea mai importanta dintre aplicaţiile Java, si acest subiect nu poate fi
cuprins intr-un capitol. Aşa că vom prezenta doar o privire de ansamblu asupra programării 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
primeşte o intrare de la orice context exterior. Atât conceptul de applet cât ş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 căror 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ă declaraţii de import. Prima importă clasele din pachetul AWT
adică Abstract Window Toolkit. Applet-urile interacţionează cu utilizatorul folosind aceste clase AWT, iar
nu prim consolă. AWT conţine o serie de clase ce permit lucrul cu ferestre şi o desenarea unei interfeţe
grafice.
Următoarea incluziune import se referă la pachetul applet. Acest pachet conţine clasa Applet,
iar orice applet creat trebuie să moştenească această clasă.
Clasa ce moşteneşte clasa Applet, este OneApplet. Această clasă ar trebui să fie declarată public,
deoarece va fi accesată din afara ei.
Clasa conţine o metodă paint(). Această metodă este definită de clasa Component din AWT, ce
este o clasă părinte pentru Applet şi trebuie să fie suprascrisă de OneApplet. Metoda paint() este apelată
ori de câte ori va avea loc reafişarea. Această reafişare poate surveni din mai multe motive. De exemplu,
fereastra în care applet-ul rulează, este suprascrisa de alta fereastră, sau este mişcată. 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ă afişează un String la coordonatele X,Y. Forma ei generală este:

void drawString(String message, int x, int y)

Mesajul message va fi afişat pe fereastră la coordonatele x,y. Colţul stânga sus are
coordonatele 0,0. De asemenea, de reţinut că, applet-ul nu conţine o metodă main().

2
Spre deosebire de majoritatea programelor, execuţia unui applet nu începe cu main. Pentru a
putea porni un applet, acesta trebuie inclus într-un fișier HTML, pentru a rula în browser, sau se poate
folosi un program auxiliar appletviewer.
Pentru a include un applet într-un browser, trebuie să modificăm un fișier HTML, adăugând un
tag APPLET, astfel:

<html>
<applet code="OneApplet" width=200 height=60>
</applet>
</html>

Numele OneApplet vine de la fişierul .class, ce a fost obţinut prin comanda:

javac OneApplet.java

Pe lângă acest nume, mai există width=200 height=60 ce înseamnă dimensiunea suprafeţei
de afişare alocată acestui applet. Pentru a executa programul OneApplet, se poate apela următoarea
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

3
Astfel, în urma compilării şi rulării,

Figura 1. Paşii pentru rularea unui applet

obţinem următoarea fereastra în care rulează applet-ul:

Figura 2. Interfaţa grafica a unui applet

După cum reiese şi din figura de mai jos, este un fel de Panel, el moştenind această clasă din
pachetul java.awt. ca orice Panel, un Applet, poate conţine 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 lângă clasele AWT, există o librărie despre care vom vorbi în următorul capitol pe larg,
denumită Swing. Aceasta este o alternativă la componentele AWT. De exemplu, pe lângă 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ă câteva concepte, ce nu
sunt prezente în programele ce rulează în consolă.
În primul rând, applet-urile sunt bazate pe evenimente şi seamănă cu un set de funcţii de
întrerupere. Un applet va aştepta până când un eveniment apare. Sistemul va notifica acest applet,
apelând o metodă de tratare a evenimentului apărut. Odată ce aceasta are loc, applet-ul va efectua
instrucţiunile din metodă, şi va preda înapoi controlul sistemului. În situaţiile în care un applet trebuie să
efectueze mai multe sarcini, până să returneze controlul sistemului, pentru a evita „îngheţarea”
ferestrei, putem lucra, sau efectua acele sarcini pe un alt fir de execuţie.
În al doilea rând, utilizatorul este acela care interacţionează cu applet-ul. În programe ce rulează
în consolă, programul aşteaptă intrări, şi îl notifică pe utilizator. Aici, utilizatorul va interacţiona cu
applet-ul, după bunul lui plac. Aceste interacţiuni sunt transmise applet-ului sub forma unor
evenimente. De exemplu, când utilizatorul apasă un buton al mouse-ului, se generează un eveniment de
mouse. Dacă utilizatorul apasă o tastă când focus-ul este pe fereastra applet-ului, se va genera un
eveniment de tastatură. Atunci când utilizatorul interacţionează cu un buton, sau un check-box, sau
orice alt control, se va genera un eveniment corespunzător.

Ce trebuie să conţină un applet

Deşi OneApplet, prezentat mai sus, este un applet real, el nu este complet, pentru că nu conţine
elementele necesare majorităţii applet-urilor. Metodele întâlnite î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 moştenită 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.

5
public void init()
{
// iniţializări
}
public void start()
{
// sart sau reiau execuţia
}
// apelată la oprirea appletului.
public void stop()
{
// suspendă execuţia
}
//apelată la terminarea execuţiei
//este ultima metodă apelată
public void destroy()
{
//activităţi de oprire
}
// la redesenarea ferestrei
public void paint(Graphics g)
{
//reafişarea conţinutului ferestrei
}
}

Aceste metode au in dreptul lor un comentariu cu referire la scopul lor ( ce ar trebui sa facă
atunci când sunt apelate ) şi in ce context sunt apelate.

Iniţializarea şi terminarea unui applet

Atunci când este lansat un applet, următoarele acţiuni au loc:


1. Apel init()
2. Apel start()
3. Apel paint()
Când un applet îşi încheie execuţia, următoarele acţiuni au loc:
1. Apel stop()
2. Apel destroy()

Prima metodă ce se apelează este init() şi în interiorul acesteia se iniţializează variabilele


clasei şi alte activităţi de iniţializare. 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 rulării unui applet. Metoda paint() este folosită la redesenarea ferestrei în care rulează
applet-ul si a elementelor din fereastră. Metoda stop() permite suspendarea instrucţiunilor din
applet, inclusiv a thread-urilor copii.
Metoda destroy() este apelată la încheierea totală a execuţiilor din applet.

6
Redesenarea

Ca regulă generală, un applet va scrie în fereastra doar când metoda paint() este apelată de
sistem. Problema care se pune este următoarea: cum poate un applet să cauzeze aceste update-uri? De
exemplu, dacă un applet afişează un banner în mişcare, 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 informaţii, sau actualizarea lor, este repaint().
Metoda repaint() este definită în clasa Component din AWT. Aceasta face ca sistemul să execute
un apel al funcţiei update(), metodă ce va apela paint(). Dacă o parte a applet-ului trebuie să afişeze un
String, poate stoca acest mesaj într-o variabilă şi apela repaint(). Metoda paint() va prelua acea variabilă
şi o va afişa 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 colţul stânga 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ă când applet-ul a cerut ca o
porţiune din fereastră să fie redesenată. Există metode de a suprascrie această funcţie, astfel încât
funcţionalitatea să fie mult mai mult decât 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 afişarea unui banner.";
Thread t;
boolean stopFlag;
// Iniţializarea threadului cu null
public void init()
{
t = null;
}
// start al appletului
//folosit pentru a porni threadul t
public void start()
{
t = new Thread(this);

7
stopFlag = false;
t.start();
}
//când threadul rulează
public void run()
{
char ch;
//Se afişează 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;
}
// afişarea 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 afişat, este cel din
variabila String msg. Firul care se ocupă cu aceste modificări este dat de variabila t.
Variabila booleană stopFlag, este folosită pentru a opri applet-ul. În interiorul init() variabila este
iniţializată cu null, urmând ca la pornirea applet-ului şi anume la apelul funcţiei 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 poziţie. Aceste acţiuni au loc într-o buclă infinită, ceea ce
ar trebui sa „îngheţe” fereastra. Cum firul care se ocupa cu apelul metodei paint() este cel principal,
funcţionarea nu va avea acest impediment. Când se încheie execuţia applet-ului, respectiv se apelează
metoda stop(), acelaşi lucru se întâmplă ş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
execuţia 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 aceeaşi instanţă de JRE.
Plugin-ul de Java, va porni o instanţă de JRE în următoarele cazuri:
1. Când un applet cere să fie executat în versiunea corespunzătoare deJRE
2. Când un applet specifică proprii parametrii pentru JRE, de exemplu, mărimea heap-ului. Un
nou applet foloseşte JRE existent, dacă specificaţiile sunt o submulţime a specificaţiilor JRE
existent.
Un applet va rula într-o instanţă de JRE existentă, dacă toate condiiţiile sunt îndeplinite:
1. Versiunea de JRE cerută de applet se potriveşte cu cea existentă.
2. Parametrii de start ai JRE, satisfac cerinţele applet-ului.

Următoarea diagrama prezintă modul în care applet-urile sunt executate în JRE:

Figura 4. Execuţia unui applet în JRE

9
Applet-urile pot invoca funcţii prezente în pagina web. Funcţiile JavaScript pot de asemenea,
invoca metode ale unui applet, încorporat în aceeaşi 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 execuţie, mai ales când mai multe applet-uri rulează simultan,
apelurile între cele două limbaje trebuie să fie scurte, şi să nu conţină, pe cât posibil bucle.

Folosirea status-ului din fereastră

Pe lângă afişarea informaţiilor într-o fereastra, un applet poate afişa 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 afişat.


Fereastra de status, se poate folosi la informarea utilizatorului, despre acţiunile ce au loc în
applet, de a sugera opţiuni, de a raporta erori, etc. Poate, fi folosita, de asemenea, la debug, pentru a
afişa informaţii despre variabile, etc.
Mai jos este o simpla metoda ce afișează î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
{
//afişarea unui mesaj in status
public void paint(Graphics g)
{
g.drawString("Acesta este un mesaj afişat in fereastra.", 10,
20);
showStatus("Acesta este un mesaj afişat în status bar");
}
}

Iată rezultatul rulării acestui program:

10
Figura 5. Mesaj in status bar

Transmiterea parametrilor către un applet

Pentru a transmite parametrii unui applet, se foloseşte atributul PARAM al tag-ului APPLET.
Acest atribut va specifica numele parametrului şi valoarea sa. Pentru a afla valoarea unui parametru, se
foloseşte getParameter() definită în clasa Applet. Apelul acestei metode este:

String getParameter(String paramName)

Aici, paramName este numele parametrului. Funcţia va returna valoarea parametrului specificat,
sub forma unui obiect String. Pentru valori booleane, va trebui să convertim reprezentările String într-un
format intern. Dacă un parametru nu este găsit, 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 declaraţi parametrii ce vor fi transmiși 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 preluaţi 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 moştenesc clasa Applet. La rândul ei, clasa Applet moștenește următoarele
clase definite în AWT: Component, Container şi Panel. În acest fel, un applet poate avea acces la
funcţionalitatea completă oferită de AWT.
Pe lângă aceste metode, Applet mai conţine câteva funcţii ce permit controlul total al applet-ului
funcţii ce sunt descrise mai jos:

Metoda Descriere
void destroy() Apelată de browser înainte de terminarea applet-ului.
Applet-ul va suprascrie această metodă pentru a curăţa 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 găsit
la locaţia specificată prin url.
AudioClip getAudioClip(URL url, Returnează un obiect de tip AudioClip ce încapsulează clipul audio găsit
String clipName) la locaţia specificată prin url şi având 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 găsită, la o
locaţie specificată prin url.
Image getImage(URL url, Returnează un obiect imagine ce încapsulează imaginea găsită, la o
String imageName locaţie specificată prin url şi având ca nume imageName.
Locale getLocale() Returnează un obiect Locale ce este folosit în clase şi metode ce
folosesc setările specifice unei regiuni.
String getParameter(String paramName)
Această metodă returnează un parametru asociat cu paramName sau
null dacă nu este găsit acel parametru.
String[ ] [ ] getParameterInfo( ) Returnează o tabelă String ce descrie parametrii recunoscuţi de către
applet. Fiecare element din tabelă trebuie să fie format din trei string-
uri ce conţin numele parametrului, descrierea tipului de dată, şi
explicaţii cu privire la scopul lui.
void init() această metodă este apelată la începutul execuţiei 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 ) găsit la url-ul specificat. Această metodă este similară cu getAudioClip,
însă este statică.

13
void play(URL url) Dacă un audio clip este găsit la locaţia specificată de url, atunci clipul
este rulat.
void play(URL, String clipName) Dacă un audio clip este găsit la locaţia 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. Conţine două câmpuri:
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 locţiitor de cod, pentru
applet. Această metodă este folosită de sistemul run-time şi nu este
apelată de applet-ul în sine. Un locţiitor sau stub este o bucată de cod,
ce asigură o legătură dintre applet şi browser.
void showStatus(String str) Afişează str în fereastra status a browser-ului sau a ferestrei de afişare a
applet-ului.
void start() apelat de browser atunci când un applet trebuie să fi pornit. Este apelat
automat după metoda init().
void stop() apelat de browser atunci când un applet trebuie să îşi suspende
activitatea. Odată oprit, un applet poate fi repornit prin start().

Operaţiuni grafice într-un applet

Clasa Graphics are o mulţime de funcţii 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ă desenăm, în paint() sau altundeva?

Când să desenăm?

Există mai multe opţiuni, pe care le putem alege când efectuăm desenarea unui obiect. Cea mai
corectă metodă este însă de a desena în metoda paint(), deoarece nu se poate desena într-o fereastră
până când aceasta nu este creată şi plasată pe ecran. În plus, se poate ca metodele să dureze mult, ceea
ce blochează afişarea unor obiecte pe fereastra. Este recomandat ca în celelalte metode să se modifice
parametrii obiectelor ce vor fi afişate, ş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 serveşte
la setarea şi lucrului concret cu anumite fonturi, redimensionarea lor, etc. A doua serveşte la preluarea
măsurilor 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 funcţionalităţi 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 poziţia 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;
//acelaşi lucru si pentru înălţime
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 adăuga, 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;
/*Lăţimea si înălţimea */
protected int width, height;
/*Numele fontului*/
protected String fontName;
/*variabila ce modifică fontul */
protected Font theFont;
/*mărimea fontului */
protected int fontSize = 18;

16
/*deplasamentul umbrei*/
protected int theOffset = 3;
/*daca am primit toţi parametrii */
protected boolean inittedOK = false;
/*pentru a verifica dacă iniţializarea 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 aplicaţia ajutătoare appletviewer:

DropShadow dp = new DropShadow();


//pe care îl pornim
dp.start();

Pe lângă aceste funcţionalităţi, există foarte multe alte componente de GUI pe care le vom
aborda într-un capitol următor. Î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, oricărui applet. Cele mai multe evenimente la care applet-ul va răspunde sunt generate de
utilizator. Aceste evenimente sunt transmise applet-ului în diverse moduri. Există mai multe tipuri de
evenimente. Cele mai des întâlnite sunt cele generate de mouse, tastatură şi diverse controale cum ar fi,
un buton. Aceste clase care se ocupă de evenimente sunt conţinute în pachetul java.awt.event.
Înainte de începerea discuţiei, trebuie spus că modul în care evenimentele sunt tratate de către
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 delegării

Acest mod de abordare nou, se bazează pe un model de a delega evenimentele. Acesta


defineşte mecanismele standard de a genera şi procesa evenimente. Conceptul este următorul: o sursă
generează un eveniment şi trimite o notificare unuia sau mai multor ascultători. În această schemă,
ascultătorii aşteaptă pur şi simplu până când primesc un eveniment. Odată primit, ascultătorul
procesează evenimentul şi apoi returnează controlul. Avantajul este că separă conceptele de interfaţa cu
utilizatorul. O interfaţă cu utilizatorul poate delega procesarea unui eveniment unei alte părţi de cod.
Pentru a putea primi o notificare, ascultătorii trebuie să se aboneze la un eveniment.

18
Evenimente

În modelul delegării, un eveniment este un obiect ce descrie o schimbare de context. Poate fi


generat în urma interacţiunii unei persoane cu elementele grafice din interfaţă ( apăsarea 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 ascultătorii pentru a aceştia să primească notificări 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 referinţa la un ascultător. De exemplu metoda
care înregistrează un ascultător la un eveniment de tastatură este addKeyListenter(). Metoda care
înregistrează mişcarea unui mouse este addMouseMotionListener(). Atunci când un eveniment are loc,
toţi ascultătorii care s-au abonat la acel eveniment sunt notificaţi, primind o copie a obiectului
eveniment.
O sursă va trebui să ofere şi o metodă ce permite anularea înregistrării 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 deînregistrarea. De exemplu, dacă se
doreşte ştergerea unui ascultător 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 adăugarea şi ştergerea ascultătorilor
la tastatură şi mouse.

Ascultători de evenimente

Un ascultător, este un obiect ce este notificat atunci când un eveniment are loc. Acest ascultător
trebuie să îndeplinească două condiţii:
1. Să fie înregistrat cu una sau mai multe surse pentru a primi notificări despre anumite
evenimente.
2. Să implementeze metode ce să proceseze aceste notificări.
Metodele care primesc şi procesează notificări sunt definite într-o mulţime de interfeţe din
pachetul java.awt.event. de exemplu interfaţa MouseMotionListener defineşte metode pentru
a primi notificări atunci când 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 vârful


ierarhiei se află clasa EventObject, care se găseşte î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 părinte pentru toate evenimentele din AWT.
Pachetul java.awt.event defineşte câteva tipuri de evenimente ce sunt generate de diverse
elemente de interfaţă. În tabelul de mai jos sunt enumerate aceste evenimente.

Clasa de eveniment Descriere


ActionEvent Generat când un buton este apăsat, se efectuează dublu click pe o listă,
sau se selectează un meniu.
AdjustmentEvent Generat când un scroll bar este manipulat
Component Event Generat când o componentă este ascunsă, mişcată sau redimensionată.
ContainerEvent Generat când o componentă este adăugată sau ştearsă dintr-un obiect
Container.
FocusEvent Generat când o componentă primeşte sau pierde focusul tastaturii
InputEvent O clasa abstractă părinte tuturor componentelor de evenimente de
intrare
ItemEvent Generat când se efectuează click pe un checkbox sau o listă, sau este
Realizată o selecţie de opţiune într-un meniu.
KeyEvent Generat când intrarea este recepţionată de la tastatură
MouseEvent Generat când un mouse este trasat, sau mutat apăsat sau eliberat
TextEvent Generat când valoarea unui câmp text se modifică.
WindowEvent Generat când o fereastră este activată, închisă, deactivată, etc.

Pentru toate sursele mai sus menţionate există p serie de ascultători, şi anume interfeţe ce
folosesc la captarea notificărilor evenimentelor produse.
Acestea sunt : ActionListener, AdjustmentListener, …, WindowListener.

Folosirea modelului de delegare

Pentru a programa acest model trebuie să respectăm următorii paşi:


1. Se implementează o interfaţă corespunzătoare în ascultător aşa încât va recepţiona
evenimentele corespunzătoare.
2. Implementarea codului astfel încât să înregistreze şi de-înregistreze (după caz) un ascultător, 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 recepţiona notificări de la mai multe
evenimente. Pentru aceasta trebuie să implementeze toate interfeţele cerute.
În continuare vom analiza tratarea evenimentelor mouse-ului.

20
Tratarea evenimentelor de mouse

Pentru a trata evenimentele de mouse, trebuie să implementăm interfeţele MouseListener şi


MouseMotionListener. Interfaţa MouseListener defineşte cinci metode. Dacă un buton al mouse-
ului este apăsat, atunci este invocată metoda mouseClicked(). Atunci când mouse-ul se află în cadrul
unei componente, cum ar fi o fereastră, este apelată metoda mouseEntered(), iar când părăseşte
componenta, se apelează metoda mouseExited(). De asemenea, metodele mousePressed() şi
mouseReleased() sunt apelate când un buton al mouse-ului este apăsat respectiv eliberat.
Iată forma metodelor:

void mouseClicked(MouseEvent me)


void mouseEntered(MouseEvent me)
void mouseExited(MouseEvent me)
void mousePressed(MouseEvent me)
void mouseReleased(MouseEvent me)

Interfaţa MouseMotionListener() defineşte două metode. Prima, mouseDragged() este apelată


de mai multe ori când mouse-ul este trasat. Metoda mouseMoved() este invocată ori de cîte ori
mişcăm 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 afişa coordonatele actuale ale mouse-ului în cadrul ferestrei. De fiecare dată când un buton este
apăsat, este afişat cuvântul „Down”, iar când este eliberat, se afişează cuvântul „Up”. Dacă un buton
este apăsat se va afişa în colţul stânga sus mesajul „Mouse clicked”.
Atunci când mouse-ul se află deasupra ferestrei, mesajul de notificare apare în colţul stânga sus.
Atunci când se trasează mouse-ul, se afişează un mesaj „*” în coordonatele actuale ale mouse-ului.
În toate aceste modificări, coordonatele mouse-ului vor fi salvate în mouseX şi mouseY. Aceste
Variabile vor fi utilizate în metoda paint() pentru a afişa 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 iniţializare
//ne abonam pentru a fi notificaţi
//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 ieşire muse
public void mouseExited(MouseEvent me)
{
mouseX = 0;
mouseY = 10;
msg = "Mouse exited.";
repaint();
}
// Tratare eveniment de apăsare 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 mişcare mouse
public void mouseMoved(MouseEvent me)
{
//afişarea pe status
showStatus("Moving mouse at " + me.getX() + ", " +
me.getY());
}
// se afişează mesajul la coordonatele salvate
public void paint(Graphics g)
{
g.drawString(msg, mouseX, mouseY);

}
}
In figura de mai jos se poate vedea rezultatul acţiunilor, notificate către 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 ascultător 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 asemănător celui anterior.
Diferenţa este că, abonarea se va face la evenimentele de tastatură, şi evident nu vom avea
coordonate, de această dată. Totuşi, 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 afişează mesajul conform tastei apăsate


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

1
Introducere, concepte

Librăria AWT (Abstract Window Toolkit) oferă o interfaţă grafică pentru programele Java.
Această librărie oferă unelte folosite pentru comunicarea program – utilizator.
Problema acestei librării 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 mulţi ani, programatorii au trecut prin mari bătăi de cap în
încercările de a porta un program scris pentru sisteme Unix, Linux pe sistem Windows sau Macintosh. În
1995, Sun anunţa o posibilă soluţie: Java. Pentru sisteme de operare bazate pe ferestre, portabilitatea
era o problemă. Când transferăm un program scris pentru Windows pe sisteme Macintosh, codul care
tratează lucrul cu interfaţa grafică trebuie rescris complet. În Java însă, acest neajuns cauzat de
portabilitate, este rezolvat parţial prin AWT. Prin această librărie, programele se pot rula în aceeași
manieră în orice sistem de operare, ele vor arata la fel. De exemplu dacă aplicaţia folosește liste de tip
Combobox, acestea vor arăta ca o listă de tip Windows, dacă aplicaţia rulează sub Windows, sau ca o
lista de Unix dacă aplicaţia rulează sub Unix.
În construcţia acestei librarii există câteva 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

Interfeţele cu utilizatorul sunt construite în jurul ideii de componente: dispozitive care


implementează o parte din interfeţe. Începând cu anii ’80 aceste dispozitive au fost extinse, cele mai
cunoscute fiind: butoane, meniuri, ferestre, checkbox-uri, bare de scroll, etc. AWT conţine un set de
astfel de componente, şi în plus, o maşinărie de creat componente personalizate. În continuare vom
prezenta componentele principale ale AWT fără a intra în detalii.

Text static

Clasa Label oferă un mod de a afişa 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 instanţe de Label

2
Componente pentru introducere date

Java oferă câteva moduri de a permite utilizatorului de a introduce date într-o aplicaţie.
Utilizatorul poate tipări informaţia, sau o poate selecta dintr-o listă de opţiuni 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 validări a textului introdus de
utilizator. În figura de mai jos sunt câteva exemple pentru aceste componente:

Figura 10.2 Elementele de text

Clasele CheckBox şi CheckboxGroup

Componentele menţionate oferă mecanisme pentru a permite utilizatorului să selecteze dintr-o


listă de opţiuni, una singură sau mai multe. Primul mecanism se numeşte Checkbox, şi permite bifarea
unei opţiuni. În partea stângă a ferestrei se află un astfel de element numit Dialog. Un clic pe această
căsuţa marchează opţiunea ca fiind true si se bifează. Un clic ulterior, deselectează opţiunea marcând-o
ca false.
Clasa CheckboxGroup nu este o componentă, ci oferă un mod de a grupa căsuţele de bifare într-
o mulţime în care opţiunile se exclud mutual. Denumirea lor este de butoane radio.

3
Figura 10.3 Căsuţe de opţiuni

Clasa de listare de opţiuni

Problema cu clasele Checkbox şi CheckboxGroup este că atunci când listele sunt prea mari, si
opţiunile 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

Totuşi dacă dorim sa afişăm integral elementele unei liste, şi să oferim posibilitatea de a selecta
multiple elemente, avem la dispoziţie clasa List. În figura de mai jos avem o astfel de componentă.

4
Figura 10.5 Clasa List

Meniuri

Interfeţele moderne folosesc aceste elemente, însă în Java în această librărie, meniurile pot
apare doar într-un Frame. O clasă Menu, este complexă şi comportă multe părţi: bare de meniu,
elemente de meniu, etc.

Clasa PopupMenu

Meniurile Pop-up sunt folosite în funcţie de context, mai ales când 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

5
Declanşatoare de evenimente

Java oferă două componente a căror scop este de a declanşa acţiuni pe ecran: Button şi
Scrollbar. Ele permit utilizatorului să specifice momentul realizării unei acţiuni.

Clasa Scrollbar

Într-un program gen Word sau într-un browser Web, atunci când o imagine sau text este prea
mare pentru a fi afişată în pagină, acest element permite deplasarea utilizatorului în diverse părţi ale
ecranului. Atunci când se efectuează click pe Scrollbar efectul este exact deplasare într-o anumită
direcţie pentru a viziona conţinutul paginii. În figura 10.7 este reprezentat acest element.

Figura 10.7 Scrollbar vertical şi orizontal

Unele componente ca TextArea sau List conţin deja aceste Scrollbar-uri pentru a diminua efortul
programatorului. Pentru alte elemente ca Panel sau Frame va trebui implementat de la zero
funcţionalitatea pentru ScrollBar-uri.

Clasa Buton

Butonul este cel mai cunoscut element pentru declanşarea 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 super-
componentelor ce conţin alte componente predefinite.

6
În continuare vom descrie în detaliu elementele amintite mai sus pentru a oferi o perspectivă
asupra librăriei AWT.

Evenimente

Am descris în cursul anterior câteva 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 când se recepţionează un eveniment iniţiat 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ă găsirea 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, acţiunea de a efectua click pe frame-ul
de mai jos, mai exact pe butonul Blood, va declanșa metoda deliverEvent() din cadrul acestei clase.
Procesul va continua până când ţinta găsită este butonul Blood, buton ce nu mai conţine alte
componente, așa că se va executa evenimentul aferent lui.

Figura 10.8 identificarea ţintei

Mai jos este prezentată ierarhia de apeluri a metodei deliverEvent, până când evenimentul este livrat
ţintei.

7
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ă moștenită de la obiectul
Component moștenit, și nu se întâmplă nimic. În continuare vom descrie ierarhia claselor ce se ocupă de
evenimente.

Clasa Event

Atunci când apare sau este declanșat un eveniment, este de responsabilitatea programatorului
să capteze acel eveniment. Se poate decide netratarea lui, așa că evenimentul va trece ca și când nu ar fi
avut loc. Atunci când se dorește transmiterea unui eveniment mai departe, sau tratarea lui, trebuie să
înţelegem 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 legătura cronologic și conceptual între
aceste două moduri de a soluţiona problema legată de evenimente.

Variabilele

Clasa Event conţine multe variabile ce oferă informaţie despre evenimentul ce are loc. Vom discuta
despre câteva.

public int clickCount

Acest membru al clasei Event, permite verificarea evenimentului de double-click. Câmpul este relevant
doar pentru evenimente de tip MOUSE_DOWN.

8
public Event evt

Acest câmp este folosit pentru a transmite evenimente, după cum am văzut 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 câmp conţine identificatorul evenimentului. Evenimentele generate de sistem au


constantele predefinite, și câteva 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

Câmpul target conţine referinţa către un obiect care produce acest eveniment. De exemplu,
dacă utilizatorul selectează un buton, butonul este ţinta evenimentului. Dacă utilizatorul mișcă mouse-ul
pe un obiect Frame, atunci Frame este ţinta. Target va indica locul unde a avut loc evenimentul, și nu
neapărat componenta care tratează evenimentul.

public long when

Această variabilă conţine 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 conţine nenumărate constante, unele pentru a desemna ce eveniment a avut loc,
altele pentru a ajuta la determinarea tastei apăsate.
Constantele ce se referă la taste se împart în două, după tipul de eveniment ce este generat:
KEY_ACTION - și se numesc taste pentru acţiune
KEY_PRESS – se desemnează alfanumericele.
În tabelul de mai jos sunt prezentate câteva constante și tipul de eveniment care folosește aceste
constante:
Constanta – Tip eveniment Constanta – Tip eveniment
HOME KEY_ACTION F9 KEY_ACTION
END KEY_ACTION F10 KEY_ACTION
PGUP KEY_ACTION F11 KEY_ACTION
PGDN KEY_ACTION F12 KEY_ACTION
UP KEY_ACTION PRINT_SCREEN KEY_ACTION
DOWN KEY_ACTION SCROLL_LOCK KEY_ACTION
LEFT KEY_ACTION CAPS_LOCK KEY_ACTION
RIGHT KEY_ACTION NUM_LOCK KEY_ACTION
F1 KEY_ACTION PAUSE KEY_ACTION
F2 KEY_ACTION INSERT KEY_ACTION
F3 KEY_ACTION ENTER (\n) KEY_PRESS
F4 KEY_ACTION BACK_SPACE (\b) KEY_PRESS
F5 KEY_ACTION TAB (\t) KEY_PRESS
F6 KEY_ACTION ESCAPE KEY_PRESS
F7 KEY_ACTION DELETE KEY_PRESS

Există și modificatori pentru tastele Shift, Alt, Control. Atunci când utilizatorul apasă o tastă sau
generează alt tip de eveniment, se poate verifica dacă a fost apăsată simultan și una din tastele mai sus
menţionate. Pentru aceasta avem la dispoziţie modificatorii:

public static final int ALT_MASK


public static final int CTRL_MASK
public static final int SHIFT_MASK

Atunci când raportează un eveniment, sistemul setează câmpul modifiers ce poate fi verificat ulterior.

Evenimente de fereastră

Aceste evenimente au loc pentru componentele ce aparţin unui Window. Câteva din aceste
evenimente sunt valabile doar pentru anumite platforme OS.

public static final int WINDOW_DESTROY

Acest eveniment este produs când sistemul spune unei ferestre să se autodistrugă. Aceasta
intervine de obicei când utilizatorul selectează Close sau Quit. Implicit, instanţele Frame nu tratează
acest eveniment.

10
public static final int WINDOW_EXPOSE

Acest eveniment este transmis atunci când o parte a ferestrei devine vizibilă. Pentru a afla ce
parte a ferestrei a fost descoperită, se poate folosi getClipRect(), metodă ce aparţine instanţa clasei
Graphics.

Evenimente de scroll

Aceste evenimente sunt declanșate de acţiunea 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 când utilizatorul apasă
săgeata de sus a unui scrollbar vertical sau pe săgeata din stânga a unui scrollbar orizontal.

public static final int SCROLL_LINE_DOWN

Această constantă este setată unui eveniment care are loc atunci când utilizatorul apasă săgeata
de jos a unui scrollbar vertical sau pe săgeata din dreapta a unui scrollbar orizontal.

public static final int SCROLL_PAGE_UP

Această constantă este setată unui eveniment care are loc atunci când utilizatorul apasă lângă
săgeata de sus a unui scrollbar vertical sau lângă săgeata din stânga a unui scrollbar orizontal, cauzând
deplasarea unei pagini întregi în acea direcţie.

public static final int SCROLL_PAGE_DOWN

Această constantă este setată unui eveniment care are loc atunci când utilizatorul apasă lângă
săgeata de jos a unui scrollbar vertical sau lângă săgeata din dreapta a unui scrollbar orizontal, cauzând
deplasarea unei pagini întregi în acea direcţie.

Evenimente de focus

public static final int GOT_FOCUS

Acest eveniment apare când 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 când o anumită componentă este selectată. Metoda
FocusListener.focusLost() poate fi folosită pentru tratarea acestui eveniment.

Metodele clasei Event

De obicei evenimentele vor fi declanșate de contextul exterior (apăsarea unui buton, a unei
taste, etc). totuși dacă ne creăm propriile componente, sau dorim să comunicăm între thread-uri, atunci
va trebui să ne creăm 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 iniţializează toate câmpurile obiectului cu
parametrii specificaţi. Ceilalţi doi constructori vor omite câteva iniţializări 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 măștilor modificatorilor.


public boolean shiftDown ()

Metoda shiftDown() returnează true dacă tasta Shift a fost apăsată sau false în caz contrar.
Metode asemănătoare există și pentru celelalte taste și anume Ctrl sau Alt.

Modelul nou de evenimente

Deși pare mai complex (modelul este format din mai multe părţi) este mai simplu și mai eficient.
Acest model solicită obiectelor să se aboneze pentru a primi evenimente. Acest model se numește
“delegare” și implementează modelul Observer-Observable descris în cursul anterior. Reluând, 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 ascultători. Fiecare
tip de eveniment corespunde unei interfeţe de ascultare ce specifică metodele care sunt apelate atunci
când un eveniment are loc.
Ierarhia de evenimente și clasele cu rol de ascultători pentru acestea, este reprezentată în figura
de mai jos:

12
Figura 10.10 Modelul AWTEvent

13
Unele interfeţe de ascultători sunt construite astfel încât să trateze mai multe evenimente. De
exemplu, interfaţa MouseInterface declară cinci metode pentru a trata diverse evenimente de mouse.
Aceasta înseamnă că un obiect interesat de evenimente de mouse trebuie să implementeze interfaţa
MouseListener și va trebui să implementeze toate cele cinci metode.
Ce se întâmplă dacă dorim să tratăm doar un eveniment de mouse? Suntem obligaţi să scriem
cod de patru ori in plus? Din fericire pachetul java.awt.event include clase adapter, ce sunt scurtături ce
permit scrierea facilă a acestor handler-e de evenimente. Clasa adapter oferă implementări nule a
tuturor metodelor interfeţei. De exemplu MouseAdapter oferă implementări implicite alea metodelor
mouseEntered(), mouseExisted(), etc. Putem astfel să ne concentrăm pe metoda care ne interesează la
un moment dat și anume mouseClicked().

Clasa AWTEvent

Variabile

protected int id

Câmpul 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 corelaţie cu metoda Component.eventEnabled().

14
Constructori

Clasa AWTEvent este abstractă, constructorii nu pot fi folosiţi imediat. Ei sunt apelaţi la instanţierea 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 folosește ca identificator al
tipului de eveniment.

Metode

protected void consume()

Metoda consume()este apelată pentru a menţiona faptul că un eveniment a fost tratat. Un


eveniment care a fost marcat astfel, este livrat mai departe celorlalţi ascultători daca este cazul. Doar
evenimentele de tastatură și mouse pot fi marcate ca și consumate. Un scenariu de folosire a acestei
facilităţi este atunci când dorim să refuzăm introducerea anumitor caractere, practic acele caractere nu
sunt afișate.

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 operaţii dintr-un Container
FocusEvent Conţine evenimentele ce sunt generate la primirea/pierderea focusului
WindowEvent Se ocupă de evenimentele specifice unei ferestre
PaintEvent Încapsulează evenimentele specifice acţiunii de desenare. Aici este un caz
special deoarece nu există clasa PaintListener. Se vor folosi totuşi 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 acţiune asupra unei componente.
AdjustmentEvent Este altă clasă din ierarhia claselor de evenimente. Încapsulează evenimente
care reprezintă mişcările unui scrollbar.

15
ItemEvent Este clasa ce permite tratarea evenimentelor ce au loc atunci când utilizatorul
Selectează un checkbox sau un buton radio etc.
TextEvent Încapsulează evenimentele ce au loc atunci când conţinutul unui
TextComponent s-a schimbat.

Pentru toate aceste elemente descrise există interfeţe de ascultători şi clase adaptori.
De exemplu pentru clasa ActionEvent există interfaţa ActionListener ce conţine metode pentru tratarea
evenimentelor de acest tip.
Pentru clasa ComponentEvent există interfaţa ComponentListener ce conţine patru metode
pentru mutarea şi redimensionarea componentei. Clasa ComponentAdapter este adaptorul
corespunzător, folosit mai ales când se doreşte ascultarea unui eveniment specific.

Clasa AWTEventMulticaster

Aceasta este folosită de AWT pentru a trata cozile de ascultători pentru diverse evenimente, şi
pentru a trimite evenimente tuturor ascultătorilor interesaţi (multicasting). Iată cum se foloseşte
această clasă:

public static ActionListener add(ActionListener first, ActionListener


second)
Metoda de mai sus are doi parametrii de tip ActionListener şi returnează tot un
ActionListener. Acest obiect returnat este un multicaster ce conţine doi ascultători. De asemenea
se poate construi un adevărat lanţ de ascultători astfel:

actionListenerChain=AWTEventMulticaster.add(actionListenerChain,
newActionListener);

Pentru a transmite un eveniment lanţului de ascultători astfel format avem metoda


actionPerformed():

actionListenerChain.actionPerformed(new ActionEvent(...));

Evident există metode pentru toate tipurile de evenimente, şi acestea sunt:

public void actionPerformed(ActionEvent e)


public void adjustmentValueChanged(AdjustmentEvent e)
public void componentAdded(ContainerEvent e)
public void componentHidden(ComponentEvent e)
public void componentMoved(ComponentEvent e)
public void componentRemoved(ContainerEvent e)
public void componentResized(ComponentEvent e)
public void componentShown(ComponentEvent e)
public void focusGained(FocusEvent e)
public void focusLost(FocusEvent e)
public void itemStateChanged(ItemEvent e)
public void keyPressed(KeyEvent e)

16
public void keyReleased(KeyEvent e)
public void keyTyped(KeyEvent e)
public void mouseClicked(MouseEvent e)
public void mouseDragged(MouseEvent e)
public void mouseEntered(MouseEvent e)
public void mouseExited(MouseEvent e)
public void mouseMoved(MouseEvent e)
public void mousePressed(MouseEvent e)
public void mouseReleased(MouseEvent e)
public void textValueChanged(TextEvent e)
public void windowActivated(WindowEvent e)
public void windowClosed(WindowEvent e)
public void windowClosing(WindowEvent e)
public void windowDeactivated(WindowEvent e)
public void windowDeiconified(WindowEvent e)
public void windowIconified(WindowEvent e)
public void 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
apăsarea butonului de mouse. Din moment ce nu avem ascultători pentru mouse, trebuie să apelăm
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 aplicaţie 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, câmpurile de text și
container-ele.

Constante

Acestea sunt folosite pentru a specifica cum ar trebui aliniată componenta curentă:

public static final float BOTTOM_ALIGNMENT


public static final float CENTER_ALIGNMENT
public static final float LEFT_ALIGNMENT
public static final float RIGHT_ALIGNMENT
public static final float TOP_ALIGNMENT

Metode

protected Component()

Constructorul este declarat protected deoarece clasa Component este abstractă.


Constructorul permite crearea unei componente fără 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 rezoluţia, mărimea ecranului, sau fonturi disponibile).

public Color getForeground ()

Metoda returnează culoarea de prim plan. Dacă nu există o culoare setată, se va lua culoarea
părintelui componentei actuale. Dacă nici părintele 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ă același 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 excepţia InternalError la apelul acestei metode.

public Locale getLocale ()


public void setLocale (Locale l)

Aceste metode modifică sau ajută la preluarea setării Locale a componentei curente. Localizarea
este o parte dintr-un subiect al internaţionalizării programelor Java și nu îl vom aborda acum.

public Cursor getCursor ()


public synchronized void setCursor (Cursor c)

Aceste două metode preiau/modifică informaţiile legate de forma și dimensiunea mouse-ului.

public Point getLocation ()


public Point location ()

Aceste metode returnează poziţia curentă a Component-ei, poziţie relativă la părintele acestei
structuri. Punctul returnat prin Point este colţul stânga 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 poziţie (x,y). Coordonatele se referă la


colţul stânga sus al dreptunghiului ce delimitează componenta și sunt relative la părintele acesteia.

public Dimension getSize ()


public Dimension size ()
public void setSize (int width, int height)
public void resize (int width, int height)

Primele două metode ajută la preluarea dimensiunii componentei, în timp ce următoarele 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 stânga în
timp ce o valoare apropiată de 1 indică o deplasare către 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 acţionează ca și
când 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ă instanţa componentei. Dacă nu, returnează null.

public void paint (Graphics g)

Metoda permite afișarea 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 funcţii de redesenare, care în principiu realizează același lucru: redesenarea
componentei cât mai repede cu putinţă.

public void print (Graphics g)

Apelul implicit al acestei metode este către paint. Dar se poate imprima conţinutul unei
componente, dacă parametrul Graphics implementează PrintGraphics.

21
Metode de vizualizare

Pachetul AWT conţine de asemenea clasa abstractă Image, care se ocupă cu tratarea imaginilor,
provenite din diverse surse, cele mai întâlnite fiind fișierele de imagini.
Un mic exemplu pentru folosirea acestei clase, pentru încărcarea unei imagini și afișarea 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 câtorva funcţii 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 interfaţa java.awt.image.ImageObserver implementată de clasa


Component. Permite actualizarea asincronă a interfeţei pentru a primi notificări despre imaginea dată
de parametrul image. Această metodă este necesară pentru că încărcarea imaginii se face pe un thread
separat. Parametrii x,y specifică poziţia imaginii încărcate, iar width și height specifică dimensiunea
imaginii. Parametrul infoflags este o mască pe biţi ce permite setarea diferitelor opţiuni de update.

public Image createImage (int width, int height)

22
Metoda creează o imagine goală de mărime 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 forţează încărcarea unei imagini, asincron, în alt thread. Obiectul observer este
componenta care va fi afișată de imaginea image la încărcarea imaginii. Dacă image a fost deja
încărcată atunci funcţia returnează true. în caz contrar se returnează false. Din moment de image este
încărcată asincron, prepareImage returnează imediat ce este apelată.

public int checkImage (Image image, ImageObserver observer)

Metoda returnează statusul construcţiei unei reprezentări a imaginii, status raportat de


observer. Dacă imaginea nu s-a încărcat, 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 când este apelată componenta
este invalidată. Metoda este apelată de sistem la crearea componentei, sau atunci când componenta
este adăugată la un Container și acesta era deja afișat.

public synchronized void removeNotify ()

Metoda distruge conţinutul componentei și o va șterge de pe ecran. Informaţia despre statusul


componentei este reţinut într-un subtip specific.

public void setVisible(boolean condition)


public void show (boolean condition)

Cele două metode, din care ultima este invechită, afișează pe ecran componenta (în cazul
metodei setVisible doar dacă variabila booleană este setată pe true). Părintele Container, trece în
starea invalid, deoarece cel puţin un copil a modificat aspectul.

public void hide ()

Metoda ascunde componenta și are același 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/interacţiona cu aceste componente sau nu.

23
public void requestFocus ()

Această metodă permite iniţializarea unei cereri ca și componenta să recepţioneze focus.

public boolean isFocusTraversable()

Această metodă verifică dacă acea componentă este capabilă de a recepţiona focus sau nu.

public void nextFocus ()

Metoda permite transferarea focus-ului către următoarea 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 lângă acestea există o serie de funcţii 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 funcţii pentru înregistrarea sau
deînregistrarea ascultătorilor:

public void addComponentListener(ComponentListener listener)


public void removeComponentListener(ComponentListener listener)
public void addFocusListener(FocusListener listener)
public void removeFocusListener(FocusListener listener)
public void addKeyListener(KeyListener listener)
public void removeKeyListener(KeyListener listener)
public void addMouseListener(MouseListener listener)
public void removeMouseListener(MouseListener listener)
public void addMouseMotionListener(MouseMotionListener listener)
public void removeMouseMotionListener(MouseMotionListener listener)

Pentru a putea trata evenimentele (asta dacă nu dorim neapărat înregistrarea la un anumit
eveniment) trebuie să permitem captarea acestora. Aceasta se realizează prin funcţia:

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 recepţionării unor evenimente pentru componenta curentă.

protected void processEvent(AWTEvent e)

Metoda primește toate evenimentele AWTEvent ce au ca ţintă componenta curentă. Aceste


evenimente vor fi transmise celorlalte metode specifice, pentru o procesare ulterioară. Atunci când se
suprascrie metoda processEvent, este posibilă procesarea evenimentelor fără a înregistra ascultători.

protected void processComponentEvent(ComponentEvent e)

Această metodă primește un eveniment de tipul ComponentEvent ce are drept ţintă


componenta actuală. Dacă sunt abonaţi ascultători, aceștia sunt notificaţi. Suprascrierea metodei
processComponentEvent este echivalentă cu redimensionarea componentei.

protected void processFocusEvent(FocusEvent e)

Metoda este apelată atunci când componenta primește focus. Suprascrierea acestei metode
este echivalentă cu suprascrierea metodelor gotFocus() sau lostFocus().

protected void processKeyEvent(KeyEvent e)

Metoda primește 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 ascultătorii înregistraţi.
Suprascrierea metodei este similară metodelor keyDown() sau keyUp() din modelul vechi.

protected void processMouseEvent(MouseEvent e)

Această metodă este similară cu cea anterioară cu specificaţia că se ocupă cu evenimente de


mouse.

Etichete

O etichetă, este reprezentată de clasa Label, ce derivă din Component și afișează o linie de text. Este
folosită mai ales la stabilirea unor titluri sau a atașa 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)

Aceștia sunt constructorii acestei clase, prin care se permite instanţierea unei etichete, și
specificarea textului afișat 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 excepţie de tip IllegalArgumentException.
Clasa Label poate reacţiona la evenimentele pe care le recepţionează, deși nu este scopul ei.

Butoane

Clasa Button derivă de asemenea din Component și se folosește pentru declanșarea unor acţiuni.

Metode

public Button ()
public Button (String label)

Aceștia sunt constructorii, ultimul permiţând crearea unui buton a cărui 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 celălalt este folosit
de programator și se numește comanda butonului. Aceasta a fost introdusă pentru internaţionalizarea
programelor. De exemplu daca eticheta butonului conţine Yes însă programul rulează pe sistem francez
sau german, eticheta va afișa Oui sau Ja. Oricare ar fi sistemul pe care rulează comanda butonului poate
rămâne Yes.

public synchronized void addNotify ()

Metoda permite adăugarea butonului ca și componentă, permiţând-ui acestuia să își schimbe


aspectul păstrând funcţionalitatea.

Evenimentele unui buton sunt cele de mouse, de focus de tastatură practic cele prezentate mai
sus în clasa Component.
Pentru a înţelege mai bine evenimentele și diferenţa între numele comenzii și numele afișat
avem următorul 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");
}
}
}

Deși etichetele ultimelor butoane sunt altfel decât Four sau Three mesajul va fi afișat pentru că
se compară numele acţiunii butonului.

Clasa Canvas

Aceasta este o clasă pe servește ca suport pentru noi componente ce pot fi create pe lângă cele
deja oferite de librăria 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 aplicaţiilor 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 moștenește Canvas este folosită în cadrul unui
Applet, mai exact adăugată î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 final static int DEFAULT_CURSOR


public final static int CROSSHAIR_CURSOR
public final static int TEXT_CURSOR
public final static int WAIT_CURSOR
public final static int HAND_CURSOR
public final static int MOVE_CURSOR
public final static int N_RESIZE_CURSOR
public final static int S_RESIZE_CURSOR
public final static int E_RESIZE_CURSOR
public final static int W_RESIZE_CURSOR
public final static int NE_RESIZE_CURSOR
public final static int NW_RESIZE_CURSOR
public final static int SE_RESIZE_CURSOR
public final static int 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ă excepţia IllegalArgumentException. Metoda verifică dacă obiectul de tip Cursor există deja
și dacă da returnează referinţa 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 mișcările mouse-ului. Pentru
introducerea de caractere există două clase TextField și TextArea. Acestea fac parte din clasa părinte
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 funcţii:

public int getSelectionStart ()

Metoda returnează poziţia iniţială a textului selectat. Poziţia poate fi considerată numărul de
caractere ce precedă primul caracter selectat. Dacă nu este selectat text, se returnează poziţia
cursorului. Valoarea de start de la începutul textului este 0.

public int getSelectionEnd ()

30
Această metodă returnează poziţia cursorului ce indică sfârșitul selecţiei curente. Dacă nu este
selectat nimic atunci se va returna poziţia 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ă selecţia 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" : "Read-
only"));
return true;
}
}
return false;
}
}

31
Pe lângă evenimentele cunoscute se poate trata modificarea textului componentei folosind următoarea
funcţie:

public synchronized void addTextListener(TextListener listener)

Această metodă permite înregistrarea obiectului listener pentru a primi notificări atunci când
are loc un eveniment de tip TextEvent. Metoda listener.textValueChanged() este apelată atunci când
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 următor, urmând ca acum să studiem cum se pot grupa aceste componente.

Clasa Container

Clasa Container este o subclasă a Component, ce conţine alte componente, inclusiv alte
recipiente. Container permite crearea de grupuri de obiecte ce apar pe ecran. în continuare vor fi atinse
câteva 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 târziu când vom vorbi despre
Layout.

Container

Container este o clasă abstractă ce oferă suport pentru alte obiecte de tip Component. Această
clasă conţine 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 instanţiat un obiect de acest tip, de aceea ea
trebuie derivată şi apoi utilizată.

Constructori

Constructorul clasei ce moşteneşte Container permite instanţierea unui obiect şi asocierea unui
Layout şi anume folosind un manager pentru aspect. Următorul 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 conţinute de Container:

public int getComponentCount()


public int countComponents()

Metoda getComponentCount() returnează numărul 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 dispoziţie metodele:

33
public Component getComponent (int position)
public Component[] getComponents()

Prima metodă returnează o componentă cu un anumit index. Dacă poziţia este incorectă va fi aruncată
următoarea excepţie: ArrayIndexOutOfBoundsException.
A doua metodă returnează un şir ce conţine toate componentele din Container-ul curent. Orice
modificare a oricărui element din acest şir va apare imediat pe ecran.

Adăugarea elementelor

public Component add (Component component, int position)

Metoda add() adaugă o componentă în Container, la o anumită poziţie. Dacă position este -1
inserarea va avea loc la sfârşit. Dacă position este incorect, metoda va arunca excepţia
IllegalArgumentException. Dacă se încearcă adăugarea Container-ului curent la sine însuşi metoda va
arunca aceeaşi excepţie. Dacă totul merge perfect, componenta este adăugată în recipient, şi metoda
returnează obiectul Component adăugat.

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 situaţiile în care avem componente Layout ce solicită
informaţii în plus pentru a dispune componentele pe ecran. Informaţia adiţională 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
suprafaţa unui BorderLayout, etc.

public Component add (String name, Component component)

Această metodă permite folosirea metodei anterioare, în care String-ul folosit defineşte o
anumită constrângere. Apelarea aceste metode va genera un ContainerEvent cu id-ul
COMPONENT_ADDED. Acestea sunt câteva din metodele de adăugare, însă mai sunt şi altele care pot fi
folosite.

Ştergerea elementelor

public void remove (int index)

Metoda remove şterge componenta de la poziţia 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 când este apelată, va genera un
ContainerEvent cu id-ul COMPONENT_REMOVED.

Alte medode

public boolean isAncestorOf(Component component)

Metoda verifică faptul că component este părinte 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 poziţionarea 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 poziţia componentelor din Container poate fi controlată de programator.

public Dimension getPreferredSize ()


public Dimension preferredSize ()

Aceste returnează un obiect de tip Dimension ce conţine mărimea preferată a componentelor


din Container. Container-ul poate determina mărimea preferată apelând metoda preferredLayoutSize()
care va returna spaţiul necesar managerului pentru a aranja componentele. A doua metodă este vechea
funcţie 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


spaţiu necesar (lăţime ş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 apelând 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 poziţionată mai la stânga, iar cele apropiate
de 1 indică faptul că, componenta trebuie poziţionată 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 suprafeţei.

public void doLayout ()


public void layout ()

Aceste două metode indică managerului de Layout să afişeze Container-ul.

public void validate ()

Metoda setează starea de validitate a container-ului pe true şi validează recursiv componentele


sale. Dacă acesta mai conţine un Container, atunci şi acela va fi validat. Unele componente nu sunt
iniţializate până nu sunt validate. De exemplu, nu se pot interoga dimensiunile de afişare ale unui
Button, până când acesta nu este validat.

public void invalidate ()

Metoda invalidează Container-ul actual şi componentele din cadrul său.

Evenimente

public void deliverEvent (Event e)

Această metodă este apelată atunci când eveniment de tip Event are loc. Metoda încearcă să
localizeze o componentă din Container pentru a primi evenimentul aferent ei. Dacă este găsită, 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 fiecărei componente din Container
pentru a vedea dacă, coordonatele x şi y sunt în interiorul componentei. Dacă da, acea componentă este
returnată. Dacă nu se găseşte 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ă locaţia se specifică printr-un obiect de
tip Point.

Ascultători

public synchronized void addContainerListener(ContainerListener listener)

Metoda înregistrează un obiect listener interesat de a primi notificări atunci când un obiect
de tip ContainerEvent trece prin coada de evenimente la care Container-ul curent este abonat.
Atunci când aceste evenimente au loc, metodele listener.componentAdded() sau
listener.componentRemoved() sunt apelate. Evident că şi alţi ascultători pot fi de asemenea înregistraţi.
În codul ce urmează a fi prezentat, este exemplificată folosirea unui ContainerListener pentru a
înregistra ascultători pentru toate butoanele adăugate unui applet. Ceea ce face ca acest cod să
funcţioneze, este apelul funcţiei enableEvents() care are ca efect livrarea evenimentelor în absenţa
ascultătorilor.

/*
<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 întâmplă nimic.

protected void processEvent(AWTEvent e)

Metoda processEvent()primeşte toate evenimentele AWTEvent ce au Container-ul curent


drept ţintă. Metoda le va transmite tuturor ascultătorilor abonaţi 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 ascultător pentru procesare ulterioară. Suprascrierea metodei permite preprocesarea
evenimentelor înainte ca acestea să fie retransmise.

Panel

Această clasă oferă un Container generic pentru afișarea unei suprafeţe. 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 iniţial și anume layout.
Evenimentele acestei clase sunt în concordanţă cu cele amintite la clasa Container, din moment
ce Panel moștenește un Container.

Mai jos avem un exemplu simplu pentru a înţelege această clasă.

import java.awt.*;
import java.awt.event.*;

public class PanelTest extends Frame {

38
private Button copyButton;
private Button cutButton;
private Button pasteButton;
private 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, așa că diferenţierea vizuală s-


a făcut prin culoarea de fundal.

39
Window

Un Window este o suprafaţă de afișare de top în afara unui browser sau a unui applet. Frame
este o subclasă a Window ce conţine limite, bara de titlu, etc. în mod normal Window se folosește
pentru a permite crearea meniurilor pop-up sau a altor componente ce necesită acest spaţiu. Clasa are o
serie de metode ce influenţează aparenţa ferestrei reprezentate.

public Window (Frame parent)

Este constructorul ce poate specifica părintele ferestrei, adică în cadrul cărei ferestre va activa
această fereastră. Atunci când părintele este minimizat, același lucru se întâmplă cu copilul.

public void show ()

Metoda show afișează fereastra. Atunci când 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, ascunzând-o și apoi eliberând 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ă informaţii despre
platforma pe ca rulează programul. Aceasta va permite redimensionarea ferestrei sau alegerea de
imagini pentru aplicatie, etc.

Metodele pentru captarea sau deînregistrarea 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 menţionate avem următorul 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
Interfața LayoutManager ............................................................................................................... 16
Interfața 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

1
Componente

Clasa Frame

Frame-ul este un fel de Window care permite adăugarea 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 conţine 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 când acesta se află
deasupra frame-ului. Mai jos este o listă cu aceste constante:

public final static int DEFAULT_CURSOR


public final static int CROSSHAIR_CURSOR
public final static int TEXT_CURSOR
public final static int WAIT_CURSOR
public final static int SW_RESIZE_CURSOR
public final static int SE_RESIZE_CURSOR
public final static int NW_RESIZE_CURSOR
public final static int NE_RESIZE_CURSOR
public final static int N_RESIZE_CURSOR
public final static int S_RESIZE_CURSOR
public final static int W_RESIZE_CURSOR
public final static int E_RESIZE_CURSOR
public final static int HAND_CURSOR
public final static int 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. Iniţial, aceasta


este null, dar poate fi setată ulterior. Pe unele platforme, acest concept nu este implementat.
A doua metodă permite modificarea acestei iconiţe.

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 aplicaţie să aibă mai multe bare de meniu, aceasta depinzând
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
obișnuiește 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ă excepţia 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ă apelăm mai întâi super.addNotify().

Evenimentele Frame-ului

Evenimentele care pot fi generate de un Frame, sunt cele generate de Component, deoarece
Frame moștenește această clasă. Pe lângă acestea, Frame poate genera evenimente de tip Window.
Acestea sunt: WINDOW_DESTROY, WINDOW_EXPOSE, WINDOW_ICONIFY, WINDOW_DEICONIFY, și
WINDOW_MOVED.
Un eveniment des întâlnit, WINDOW_DESTROY, este generat atunci când utilizatorul încearcă să
închidă Frame-ul, selectând Quit, sau Exit din meniu. Implicit acest eveniment nu face nimic. Trebuie să
oferiţi 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 ascultători
abonaţi. 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)
{
//Notificăm si pe altii că se inchide
if (windowListener != null)
windowListener.windowClosing(e);
System.exit(0);
} else
{
super.processEvent(e);
}
}

În cazul în care uităm să apelăm metoda enableEvents,se poate ca metoda


processWindowEvent să nu fie apelată niciodată, astfel că închiderea ferestrei nu va avea loc.

4
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 încărca imagini într-o aplicaţie. Butoanele apar atunci când utilizatorul apasă butonul
dreapta al mouse-ului, asemănător 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;
}

5
}
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ă, încărcăm o imagine în cadrul Frame-
ului, 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 câte
ori ceva se întâmplă Frame-ului, același eveniment va avea loc și pe Dialog. De exemplu, dacă părintele
Frame are iconiţă, Dialog-ul dispare până când iconiţa este ștearsă din Frame.
Dialog-ul poate fi modal sau amodal. Un Dialog amodal este acela în care utilizatorul poate
interacţiona cu Frame-ul și cu Dialogul în același timp. Un Dialog modal este acela care blochează orice
interacţiune cu alte componente ale aplicaţiei, inclusiv Frame-ul asociat cu acesta.
În figura de mai jos este exemplificat un astfel de dialog.

6
Figura 11.1 Exemplu de Dialog

Constructor și metode

public Dialog (Frame parent)


public Dialog (Frame parent, boolean modal)
public Dialog (Frame parent, String title)
public Dialog (Frame parent, String title, boolean modal)

Primul constructor creează o instanţă de Dialog, fără titlu cu părintele dat de parent. Acest
dialog nu este modal și este redimensionabil.
Al doilea constructor specifică părintele prin parametrul parent. Se poate specifica și faptul că
este sau nu modal. Iniţial, dialogul este redimensionabil.
Al treilea constructor permite specificarea titlului dialogului. Dialogul nu este modal.
Al patrulea constructor este cel mai complex și permite specificarea atât a titlului cât ș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 când 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 aceleași ca și cele ale unui Frame.

7
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) &&

8
(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 ();
}
}

Explicaţiile despre modul cum funcţionează acest program sunt date ca și comentariu. După cum
se poate observa, tratarea evenimentelor și unele modele conceptuale sunt conform modelului Java 1.0,
așa că acest exemplu este mai mult informativ, nu trebuie utilizat la implementare.

9
Mai jos este un exemplu, de asemenea cu explicaţii, î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 fișierelor sau salvarea acestora.
FileDialog este întotdeauna un Dialog modal, cu alte cuvinte aplicaţia apelantă va fi blocată până la
selectarea unui fișier sau până la închiderea dialogului de fișier.

11
Metodele clasei FileDialog

Un FileDialog are două moduri: unul pentru încărcarea fișierelor și unul pentru salvarea
acestora. Următoarele variabile descrise oferă posibilitatea specificării acestui mod de lucru. Diferenţa
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 fișiere ce are specificat Frame-ul părinte ca
parametru. Titlul este iniţial gol.
Al doilea constructor permite specificarea și a titlului iniţial.
Al treilea constructor permite specificarea, pe lângă parametrii descriși, ș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
căutarea fișierului. Metoda setDirectory trebuie apelată înainte de afișarea 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 fișierului selectat implicit la
afișarea dialogului.

public FilenameFilter getFilenameFilter ()


public void setFilenameFilter (FilenameFilter filter)

Metoda getFilenameFilter permite preluarea filtrului de nume al fișierului. Clasa


FilenameFilter face parte din pachetul java.io și permite limitarea căutării unui fișier după diverse
filtre. De exemplu, dacă filtrăm 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
aplicaţiilor ce lucrează cu fișiere.

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;
}
}

Aplicaţia permite introducerea într-o fereastră ca cea de mai jos a unui text, care poate fi
“încărcat” dintr-un fișier sau poate salva textul editat într-un fișier. Explicaţiile despre cum funcţionează
acest program se găsesc sub forma unor comentarii inserate în dreptul instrucţiunilor.

Layout

Fiecare Container are un manager de layout ( adică al aspectului componentelor ). Acesta este
responsabil pentru poziţionarea componentelor în cadrul recipientului, indiferent de platformă sau
mărimea ecranului. Practic aceste clase elimină necesitatea de a calcula poziţia în care plasăm o
componentă. Chiar și pentru o componentă simplă cum ar fi un buton, calcularea poziţiei și a
dimensiunilor relative la fereastra curentă, se poate întinde pe zeci poate sute de linii de cod. În
continuare vom descrie câteva managere de aspect care ușurează poziţionarea acestor componente pe
ecran.

Interfața LayoutManager

Această interfaţă definește responsabilităţile unei clase ce va afișa componente din cadrul unui
Container. Sarcina acestei interfeţe este să determine poziţia și mărimea fiecărei componente din cadrul
Container-ului. Nu se apela direct metoda unui LayoutManager, deoarece apelurile acestor clase sunt
ascunse de programatori. Metodele acestei interfeţe se pot suprascrie atunci când implementăm un nou
manager de aspect, diferit de cele oferite de Java.

Metodele interfeței LayoutManager

public abstract void addLayoutComponent (String name, Component component)

16
Această metodă este apelată când programul atribuie un nume componentei atunci când o
adaugă Layout-ului (de exemplu când 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ă mărimea preferată a componentelor din cadrul Container-ului. Metoda va


returna un obiect Dimension ce conţine lăţimea și lungimea necesară afișării componentelor.

public abstract Dimension minimumLayoutSize (Container parent)

Metoda minimumLayoutSize este apelată pentru a determina mărimea minimă a


componentelor din cadrul Container.

public abstract void layoutContainer (Container parent)

Această metodă ajută la poziţionarea tuturor componentelor ale parent .

Interfața LayoutManager2

Odată cu introducerea Java 1.1, au apărut modificări interfeţei mai sus menţionată.
Noua interfaţă rezolvă o problemă care apare atunci când lucrăm cu GridBagLayout. Deși
metoda addLayoutComponent(String, Component), funcţionează bine pentru BorderLayout și
CardLayout, nu poate fi folosită pentru GridBagLayout. Poziţia unei componente într-un GridBagLayout
este controlată de un număr de constrângeri, care sunt încapsulate în obiectul de tip
GridBagConstraints. Pentru a asocia aceste constrângeri, trebuie apelată metoda setConstraints().
Interfaţa LayoutManager2 definește o versiune a metodei addLayoutComponent() ce poate fi
folosită de toţi managerii de aspect, indiferent de constrângerile particulare. Metoda are un parametru
de tip Object care poate reprezenta orice tip de informaţie, cum ar fi tipul de constrângeri de mai sus.

Metodele interfeței LayoutManager2

public abstract void addLayoutComponent(Component comp, Object constraints)

Metoda este apelată când programul asignează constraints componentei comp atunci când
o adaugă layout-ului. Este treaba layout-ului să decidă ce va face cu constraints.

public abstract Dimension maximumLayoutSize(Container target)

Această metodă returnează mărimea maximă a target în cadrul acestui manager de layout.

17
Anterior, doar mărimea minimă sau cea preferată se putea obţine.

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 poziţionare mai aproape de stânga, dacă vorbim
de prima metodă, în timp ce valorile mai aproape de 1 semnifică o poziţionare mai la dreapta. Dacă ne
referim la a doua metodă, o valoare aproape de 0 înseamnă o poziţionare mai sus, în timp ce o valoare
mai aproape de jos.

public abstract void invalidateLayout(Container target)

Metoda spune managerului de aspect că orice informaţie 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 rânduri, de la stânga la dreapta. Dacă nu se mai găsesc componente în rând, ca
începe un nou rând. Atunci când Container-ul este redimensionat, componentele sunt repoziţionate pe
același 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 setări 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) stânga B) centrat C) dreapta)

Al treilea constructor, permite setarea spaţiului 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 menţionate.

public int getHgap ()


public void setHgap (int hgap)
public int getVgap ()
public void setVgap (int hgap)

Aceste metode permit preluarea respectiv setarea spaţiului vertical și orizontal dintre
componente. După apelul metodei setVgap sau setHgap trebuie apelată metoda de validate() a
Container-ului. Începând cu versiunea 1.2, Java admite alte două constante pentru orientare și anume
TRAILING și LEADING.

19
În exemplul de mai jos se poate înţelege cum se efectuează afișarea componentelor pe rânduri.
Pentru o mai bună înţelegere 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 ușor de a poziţiona
componentele conform unor limite ale ferestrei, de unde vine și numele.
Următorul apel: setLayout(newBorderLayout() modifică managerul de aspect astfel încât layout-
ul să fie unul de tip BorderLayout.

Constantele BorderLayout

public static final String CENTER


public static final String EAST
public static final String NORTH
public static final String SOUTH
public static final String WEST

Aceste constante vor indica poziţia 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ă spaţiul adiacent dintre
componente.
Al doilea constructor permite crearea unui BorderLayout cu delimitatori a căror dimensiune este
specificată de parametrii lui. Se poate ca acești parametrii să fie negativi, în cazul acesta componentele
suprapunându-se unele peste altele.

public int getHgap ()


public void setHgap (int hgap)
public int getVgap ()
public 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 aceleași ca și în cazul FlowLayout, diferă doar
comportamentul aspectului și anume faptul a acest Layout poziţionează 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 combinaţie, în funcţie 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ă interfaţa 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ă acţiunea de scroll.

Metodele Choice

public Choice ()

Există un singur constructor pentru această clasă. Componenta, la creare, este iniţial goală. La
adăugarea 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 numărul de elemente selectabile atunci va fi aruncată excepţia
ArrayIndexOutOfBoundsException.

public synchronized void add (String item)


public synchronized void addItem (String item)

Aceste metode permit adăugarea unui nou element în lista Choice-ului. Dacă parametrul este un
null, atunci va fi aruncată excepţia NullPointerException.

public synchronized void insert (String item, int index)

Metoda permite adăugarea unui nou element la poziţia indicată de index. Dacă index este
ilegal, atunci va fi aruncată excepţia 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ă poziţie, atâta timp cât 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ă funcţia va returna null.

public Object[] getSelectedObjects ()

Metoda permite aflarea elementului selectat sub forma unui șir de Object. Metoda este impusă
de interfaţa ItemSelectable, și este folositoare cu adevărat în cazul clasei List.

public int getSelectedIndex ()

Metoda returnează poziţia din listă a elementului selectat. Primul element se află pe poziţia 0.

public synchronized void select (int position)

Această metodă forţează ca item-ul de pe poziţia position să fie cel selectat.

public void select (String string)

Același efect este obţinut doar că elementul este specificat prin numele său.

Evenimentele Choice

Evenimentul cel mai des întâlnit 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 când 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 apăsată.

26
public void addItemListener(ItemListener listener)
public void removeItemListener(ItemListener listener)

Poate cele mai importante metode, permit inregistrarea/deînregistrarea unui obiect de tip
Choice la/de la ascultarea evenimentelor de modificare a acestui obiect.

protected void processEvent(AWTEvent e)

Metoda este apelată atunci când 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 adăugarea unor elemente în lista Choice, precum și
tratarea evenimentelor de selecţie 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 afișa 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 numărului 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ă numărul 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 poziţia index.

public String[] getItems ()

Metoda returnează un șir ce conţine toate elementele din listă.

28
Nu contează dacă elementul este sau nu selectat.

public synchronized void add (String item)


public synchronized void addItem (String item)
public synchronized void add (String item, int index)
public synchronized void addItem (String item, int index)

Primele două metode permit adăugarea unui item la sfârșitul listei. Ultimele două elemente
permit adăugarea unui element pe o poziţie specificată de index. Dacă indexul este mai mic ca zero sau
mai mare ca numărul de elemente de listă, elementul este adăugat la sfârșit.

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 său, iar celelalte două permit
ștergerea unui element după poziţia 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ă indecșii elementelor selectate.

public synchronized String getSelectedItem ()


public synchronized String[] getSelectedItems ()

Aceste metode sunt corespondentele metodelor de mai sus, returnând numele elementelor
selectate.

public synchronized Object[] getSelectedObjects ()

Metoda returnează elementele selectate sub forma unui șir de obiecte, conform interfeţei
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 selecţie unică, la apelul metodei select(), elementul anterior selectat
este automat deselectat. Dacă lista este de tipul multiselecţie, atunci metoda select() nu are acest
efect. Metoda deselect() deselectează elementul de la poziţia 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 multiselecţie sau nu.

public void setMultipleMode (boolean value)


public void setMultipleSelections (boolean value)

Metodele permit schimbarea stării curente a listei: dacă valoarea parametrului este true atunci
lista va trece în mod multiselecţie, dacă valoarea parametrului este false, atunci lista va fi cu selecţie
unică.

public int getRows ()

Metoda returnează numărul de rânduri care a fost transmis la instanţierea listei. Nu returnează
numărul de rânduri vizibile.

public Dimension getPreferredSize (int rows)


public Dimension preferredSize (int rows)

Metodele returnează dimensiunea preferată, pentru a ști înălţimea rândurilor.

public Dimension getPreferredSize ()


public Dimension preferredSize ()

Metodele returnează dimensiunea preferată a listei. Pentru a calcula această dimensiune, se vor
folosi numărul de rânduri specificate la instanţierea 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ă totuși
o altă metodă numită action() care înregistrează evenimente apărute pe o listă:

public boolean action (Event e, Object o)

30
Metoda este apelată atunci când utilizatorul efectuează un dublu-click pe un item din listă, iar o
reprezintă acel obiect. Dacă lista este de tip multiselecţie, prinderea acestui eveniment poate duce la
confuzii din cauză că nu este clar dacă utilizatorul a vrut să selecteze elementul sau selecţia se referă la
toate elementele alese. Următorul exemplu prezintă un mod de a rezolva această situaţie, 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 ascultători, 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 acţiune.


În general modelul este cel întâlnit la componente, deci regulile se aplică asemenea.
Pentru a exemplifica utilizarea acestor evenimente, precum și folosirea unei liste cu selecţie
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 menţionat că un Frame poate avea un meniu. Pentru a asigura un meniu aplicaţiei,
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 afișa un meniu, acesta va fi pus într-un MenuBar, care la rândul lui va fi adăugat unui
Frame. Un meniu poate conţine un MenuItem, dar poate conţine și alte meniuri care formează
submeniurile sale. Componentele MenuItem sunt materialele construite ca și panel-ele care formează
cortinele. Cortina este Menu. Meniurile sunt atașate 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ă părintele obiectelor legate de meniuri. Nu se va crea
niciodată o instanţă a acestei clase, ci se poate eventual moșteni. Utilitatea acesteia, este aceea de a
conţine 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
când se creează o componentă de meniu, fontul iniţial 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 instanţiere. 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 vârful ierarhiei ce îl conţine pe MenuComponent.
Dacă suprascriem această metodă suntem obligaţi să transmitem mai departe celorlalte componente,
prin apelul return super.postEvent (e);.

protected void processEvent(AWTEvent e)

Această metodă primește 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 moștenesc.

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 scurtătură de taste pentru un MenuItem. Atunci când au loc
aceste evenimente, un eveniment de acţiune este generat astfel încât el declanșează acţiunea 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 combinaţie de taste
gen Shift sau nu.

public int getKey ()

Metode returnează codul cheii care a declanșat MenuShortcut actual.

public boolean usesShiftModifier()

Această metodă specifică faptul că obiectul MenuShortcut impune ca tasta Shift să fie apăsată
sau nu.

MenuItem

În principiu acesta este elementul ce se găsește î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 scurtătură de taste.
Al doilea constructor creează un meniu ce permite specificarea unei etichete, în timp ce al
treilea mai adaugă și o scurtătură 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 scurtăturile de taste.

public void deleteMenuShortcut ()

Metoda permite ștergerea unor scurtături 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 conţin elementele de meniu descrise mai sus.

Metodele

public Menu ()
public Menu (String label)
public Menu (String label, boolean tearOff)

Primul constructor permite instanţierea unui meniu care nu are etichetă și care nu poate fi oprit.
Al doilea constructor permite instanţierea 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 numărului de elemente dintr-un meniu.

public MenuItem getItem (int index)

Metoda returnează elementul de la poziţia dată de index. Dacă index este invalid, atunci
metoda aruncă excepţia ArrayIndexOutOfBoundsException.

public synchronized MenuItem add (MenuItem item)

Această metodă permite adăugarea unui obiect item în meniu. Eticheta asociată cu item este
afișată î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 adăugarea acestuia la meniu.

public synchronized void insert(MenuItem item, int index)


public synchronized void insert(String label, int index)

Metodele permit adăugarea unui item la indexul specificat. Dacă indexul nu este valid atunci va
fi aruncată excepţia IllegalArgumentException.

public void addSeparator ()


public void insertSeparator(int index)

38
Meniul nu va genera un eveniment atunci când este selectat, evenimentul fiind generat de
selectarea unui MenuItem.

MenuBar

Aceasta este componenta ce va fi adăugată Frame-ului.

Metodele

public MenuBar()

Constructorul permite crearea unui ManuBar gol. Pentru a adăuga meniuri acestuia se va folosi
metoda add().

public int getMenuCount ()


public int countMenus ()

Metodele returnează numărul de meniuri aflate în cadrul barei de meniuri.

public Menu getMenu (int index)

Metoda returnează meniul de la poziţia index. Dacă index nu este valid, metoda aruncă
excepţia ArrayIndexOutOfBoundsException.

public synchronized Menu add (Menu m)

Metoda permite adăugarea unui meniu m pe bara de meniuri. Eticheta care a fost folosită pentru
crearea lui m va fi afișată pe bara de meniuri. Dacă m se găsește 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 poziţia 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 scurtătura de taste shortcut. Dacă acest item de
meniu nu există, se returnează null.

public synchronized Enumeration shortcuts()

39
Pentru a afla scurtăturile 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 stânga
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
Curs de Java si structuri de date
Cuprins:

1. Introducere in Java
2. Clase si pachete
3. Polimorfism
4. Colectii
5. Exceptii si IO
6. Interfete ,clase abstracte
7. Aplicarea cunostintelor in rezolvarea unor proiecte
8. Multithreading
9. AWT / Swing
Ce este Java?

JRE – Platforma Limbajul de


Java programare Java

JVM – Masina
virtuala Java
Java ruleaza pe orice masina ce suporta JVM
Ruleaza pe orice sistem

Sigur

Orientat pe lucru in retea

Flexibil, dinamic

Performant
Ce poate oferi Java?
Un prim exemplu
/*
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!");
}
}

Pasii de baza:
•Scrierea programului.
•Compilarea acestuia.
•Rularea programului.
Erori de sintaxa
Example.java: 7 ’;’ expected
Public static void main(String[] args)
Example.java:10: class, interface or enum
expected
}
^
2 errors

Erori de runtime
System.out.println(args[1]);

Exception in thread „main”


java.lang.ArrayIndexOutOfBoundException : 1
at Example.main<Example.java:8>
Bazele limbajului
Variabila

Tip de data Nume variabila

public class Variabile{


public static void main(String args[]){
// integer
byte largestByte;
largestByte = Byte.MAX_VALUE;
int largestInteger = Integer.MAX_VALUE;
// real
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 double value este " +
largestDouble);
}
}
Tipurile primitive

Cuvant cheie Descriere Marime


Integer byte Intreg de lungime byte 8-bit cu semn

short Intreg short 16-bit cu semn


int Intreg 32-bit cu semn
long Intreg short 64-bit cu semn
Real float Real, virgula mobila cu o 32-bit format IEEE
singura precizie 754

double Real, virgula mobila cu o 64-bit format IEEE


dubla precizie 754

Alte tipuri char Un singur caracter Unicode 16-bit caracater


Unicode
boolean Valoarea boolean-a (true 8-bit/1-bit (8
sau false) bits de spatiu, 1
bit de data)
Tipuri referinta

Conditii de numire a variabilelor:

1. Trebuie sa inceapa cu o litera si sa fie alcauit din carctere Unicode


2. Nu trebuie sa fie un cuvant cheie.
3. Trebuie sa fie unic in domeniul de vizibilitate.
Domenii de vizibilitate a variabilelor
Operatori

Operatori aritmetici

Operator Folosire Descriere


+ op1 + op2 Adauga op1 and op2; de asemenea concateneaza stringuri
- op1 - op2 scade op2 din op1
* op1 * op2 inmulteste op1 cu op2
/ op1 / op2 divide op1 la op2
% op1 % op2 Calculeaza restul impartirii lui op1 la op2

Operatori aritmetici unari


Operator Folosire Descriere
+ +op Transforma op in int daca este byte, short sau char

- -op Aplica negatia aritmetica asupra lui op


Operatori de incrementare/decrementare

Operator Folosire Descriere


++ op++ incrementeaza op cu 1; evaluarea lui op inainte
de incrementare

++ ++op incrementeaza op cu 1; evaluarea lui op dupa


incrementare

-- op-- decrementeaza op cu 1; evaluarea lui op inainte


de decrementare

-- --op decrementeaza op cu 1; evaluarea lui op dupa


decrementare
Operatori conditionali

Operator Folosire Descriere

&& op1 && op2 Returneaza true daca op1 and op2 sunt true; evalueaza
conditional op2
|| op1 || op2 Returneaza true daca op1 sau op2 este true; evalueaza
conditional op2
! !op Returneaza true daca op este false
& op1 & op2 Returneaza true daca op1 si op2 sunt boolean si amandoi
sunt true; evalueaza intotdeauna op1 si op2

Daca ambii operanzi sunt numere, efectueaza SI pe biti

| op1 | op2 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

^ op1 ^ op2 Returneaza true daca op1 si op2 sunt diferiti, adica un
XOR pe biti
Operatori de siftare
Operator Folosire Descriere
<< op1 << op2 Siftarea bitilor lui op1 la stanga cu o lungime data de
op2; umple cu zero bitii din partea dreapta

>> op1 >> op2 Siftarea bitilor lui op1 la dreapta cu o lungime data de
op2; umple cu bitul cel mai semnificativ (de semn) bitii
din partea stanga
>>> op1 >>> op2 Siftarea bitilor lui op1 la dreapta cu o lungime data de
op2; umple cu zero bitii din partea stanga

Operatori de prescurtare
Operator Folosire Echivalent
Scurtaturi pentru operatorii += op1 += op2 op1 = op1 + op2
aritmetici -= op1 -= op2 op1 = op1 - op2
*= op1 *= op2 op1 = op1 * op2
/= op1 /= op2 op1 = op1 / op2
%= op1 %= op2 op1 = op1 % op2
Scurtaturi pentru operatorii pe biti &= op1 &= op2 op1 = op1 & op2
|= op1 |= op2 op1 = op1 | op2
^= op1 ^= op2 op1 = op1 ^ op2
Scurtaturi pentru siftare <<= op1 <<= op2 op1 = op1 << op2
>>= op1 >>= op2 op1 = op1 >> op2
>>>= op1 >>>= op2 op1 = op1 >>> op2
Operator Descriere
?: Scurtatura la o anumita instructiune if-else

[] Folosit la lucrul cu siruri


. Pentru accesarea membrilor, a claselor.

(params ) Lista de parametrii (unei metode, sau instructiuni)

(tip ) Operator cast: schimba tipul de data al unei variabile

New Creeaza un nou obiect sau sir.


Instanceof Determina daca primul operand este instanta celui de-al doilea
operand
Precedenta cea mai Operatori postfixati [] . (params ) expr ++ expr
mare --
Operatori unari ++expr --expr +expr -expr ~
!
Operatori creeare sau cast new (type )expr

Operatori de multiplicare * / %

Operatori aditivi + -
Operatori de siftare > >>>
Operatori relationali < > <= >= instanceof
Operatori de egalitate == !=

SI pe biti &
XOR pe biti ^
SAU pe biti |
SI logic &&
SAU logic ||
Scurtatura if-else ?:
Precedenta cea mai asignare = += -= *= /= %= &= ^= |=
mica <<= >>= >>>=
Tipul de instructiune Cuvinte cheie
Bucla while, do-while, for
De decizie if-else, switch-case
Tratarea erorilor try-catch-finally, throw
Ramificare break, continue, label :, return

while

while (expresie) {
... lista de instructiuni
}
... instructiuni dupa blocul while

Exemplu
int i=0;
while (i<10)
{
System.out.println(i++);
}
System.out.println("Dupa while");
for

for (initializari; conditie de terminare; incrementare)


{
...lista de instructiuni
}

for (int i=0,j=1; (i<10 || j<16);i++, j+=2)


{
System.out.println(i+" " + j);
}
System.out.println("Dupa for");
Instructiuni conditionale

if (response == OK)
{ // code to perform OK action }
else
{ // code to perform Cancel action}

switch ( n )
{
case 1: System.out.println( "one" ); break;
case 2: System.out.println( "two" ); break;
default: System.out.println( "something else" );
} // end switch (n)
Capitolul 2

Alte instructiuni
Introducere in OOP
Pachete
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.");
}
}

char ch;
for( ; ; ) {
ch = (char) System.in.read(); // citesc un caracter
if(ch == 'q') break;
}
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);
}
}
}
Obiecte

Bicicleta
class numeclasa
{
//declar variabilelor clasei, ce vor fi disponibile in fiecare
instanta
type var1;
type var2;
// ...
type varN;
// declare metodele
type metoda1(parameteii)
{
//corpul metodei 1
}
type metoda2(parameterii)
{
//corpul metodei 2
}
// ...
type metodaN(parameterii)
{
//corpul metodei N
}
}
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);
}
} main()
AutomobilDemo
Automobil minivan = new Automobil();
Automobil masinasport = new Automobil();
minivan.model = "Logan MCV";
minivan.viteza = 180;
masinasport.model = "BMW";
masinasport.viteza = 320;
System.out.println(minivan.model +" are viteza maxima " +
minivan.viteza);
System.out.println(masinasport.model +" are viteza maxima
" + masinasport.viteza);

masina sport
minivan

Automobil
Automobil
Automobil masina1 = new Automobil();
Automobil masina2 = masina1;
Automobil masina3 = new Automobil;
masina2 = masina3;
masina2

masina1 Automobil

masina3
Automobil

Automobil
Functii
tip_de_returnat nume(lista de parametrii)
{
//corpul metodei
}

int myFunction(double param1, Integer param2, Automobil


minivan)
{
//... instructiuni din corpul metodei
return param2;
}
Functii in clase

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);

}
}
Rectangle
width = 10 width=12

GerPerimeter
height = 7
R1 height = 20
width

height

R2
GetArea
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);
}
}
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));
}
}
static - variabile static - functii

Rectangle

GerPerimeter
width

DemoClass
height

static GetArea

main

GetPerimeter

R1
class Numbers
{
int GetSumofTwoInt(int a, int b)
{
return a+b;
}
int GetSumofThreeInt(int a, int b, int c)
{
return a+b+c;
}
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));
}
}
Constructori

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
....
//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;
}

Automobil minivan = new Automobil("Logan MCV");


minivan.viteza = 180;

Automobil masinasport = new Automobil("BMW",320);


Garbage Collector

GC

R1
protected void finalize()
{
//codul la distrugerea obiectului
}
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;
}
//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);
}
}
this
class Power
{ Power(double base, int exp)
double b;
int e;
double val;
Power(double base, int exp)
{ get_power
this.b = base;
this.e = exp; Power
this.val = 1;
if(exp==0) return;
for( ; exp>0; exp--)
this.val = this.val * base; n
} e
double get_power() w
{
return this.val;
}
} this
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());
}
}
Pachete

Package Project1; pack1

package pack1.pack2.pack3;

pack2

pack3

import pachet.MyClass;

import pachet.*;

A
B

my package
package BookPack;
class Book
{
private String title;
private String author;

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[])
{

Book book0 = new Book("Cel mai iubit dintre pamanteni","Marin Preda");


Book book1 = new Book("Batranul si marea","Ernest Hemingway");
Book book2 = new Book("Dune","Frank Hebert");
book0.show();
book1.show();
book2.show();
}
}

javac BookDemo.java

java BookPack.BookDemo
Pachete in Java

java.lang Contine un numar consistent de clase cu scop general


java.util Contine clasele care au diverse functionalitati utile
java.io Contine clasele care se ocupa cu operatiunile input/output
java.net Contine clasele care se ocupa de lucru in retea
java.applet Contine clasele care ajuta la lucru cu applet-uri
java.awt Contine clasele pentru lucru cu Abstract Window Toolkit
Capitolul 3
Polimorfism

•Supraincarcarea metodelor
•Recursivitate
•Parametrii variabili
•Bazele mostenirii
•Specificatori de acces
•Constructorii in mostenire
•super
•Suprascrierea metodelor
•Object
Supraincarcarea metodelor

variabile

Supraincarcare
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);

}
}

int double
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);
int i = obj.functieSupraincarcata(7);
System.out.println(i);

}
}

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);
class Numbers
{ Numbers(float val)
int m_int; {
short m_short; m_float = val;
double m_double; m_int = 0;
float m_float; m_double =0;
Numbers() m_short = 0;
{ }
m_int = 0; Numbers(int ival, double dval)
m_double =0; {
m_short = 0; m_int = ival;
m_float =0; m_double = dval;
} m_short = 0;
Numbers(double val) m_float =0;
{ }
m_double = val; Numbers(int ival, float fval)
m_int = 0; {
m_short = 0; m_float = fval;
m_float =0; m_int = ival;
} m_double =0;
Numbers(short val) m_short = 0;
{ }
m_short = val; }
m_int = 0;
m_double =0;
m_float =0;
}

Numbers(int ival, double dval)

variabile

Numbers
public class OverloadingDemo
{ class Numbers
public static void main(String[] args) {
{ int m_int;
Numbers number1 = new Numbers(); short m_short;
Numbers number2 = new Numbers(1); double m_double;
Numbers number3 = new Numbers(1.1); float m_float;
Numbers number4 = new Numbers(2,3); Numbers()
Numbers number5 = new Numbers(2,4.4); {
System.out.println(number1); m_int = 0;
m_double =0;
System.out.println(number2); m_short = 0;
m_float =0;
System.out.println(number3); }
Numbers(double val)
System.out.println(number4); {
m_double = val;
System.out.println(number5); m_int = 0;
m_short = 0;
m_float =0;
} }
Numbers(float val) Numbers(short val)
} { {
m_float = val; m_short = val;
m_int = 0; m_int = 0;
m_double =0; m_double =0;
m_short = 0; m_float =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;
}
}
int = 0 short = 0 double = 0.0 float = 0.0
int = 0 short = 0 double = 0.0 float = 1.0
int = 0 short = 0 double = 1.1 float = 0.0
int = 2 short = 0 double = 0.0 float = 3.0
int = 2 short = 0 double = 4.4 float = 0.0

Numbers number3 = new Numbers(1.1f);

Numbers number3 = new Numbers(1.1d);

Numbers number3 = new Numbers((short)1);

int = 0 short = 0 double = 0.0 float = 0.0


int = 0 short = 1 double = 0.0 float = 0.0
int = 0 short = 0 double = 1.1 float = 0.0
int = 2 short = 0 double = 0.0 float = 3.0
int = 2 short = 0 double = 4.4 float = 0.0
Recursivitate int factR(int n)
{
int result;
if(n==1) return 1;
result = factR(n-1) * n;
return result;
}

factR(3)

factR(2) n este 1 atunci


returnez -> 1
result = 1*2
result = 2*3
returnez 2
returnez 6

Clasa Factorial
Parametrii variabili

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();
}

corect: TestParam(1,2,3);
incorect: TestParam(1,2.5,3);

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);
}
}
Bazele mostenirii

class Forma2D
{
double inaltime;
double latime;
void AfisezDimensiunile()
{
System.out.println("inaltimea este " +
inaltime + " latimea este " + latime);
}
}
class Triunghi extends Forma2D
{
String tip_triunghi;
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 ();
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";
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());
}
}
class Forma2D
{
double inaltime;
double latime;
void AfisezDimensiunile()
{
System.out.println("inaltimea este " +
inaltime + " latimea este " + latime);
}
}
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 ();
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();
...
Specificatori de acces
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 AnyClass
class SubClass extends BaseClass {
{ float f;
double d; //Atentie in cadrul acestei clase
void PrintVariables() //nu am acces direct asupra membrilor
{ //celorlalte clase pentru ca nu exista
//membrul propriu //relatie de mostenire, ci prin instantiere
System.out.println(d); //vom accesa membrii claselor
//membrii lui BaseClass void PrintVariablesOfBaseClass(BaseClass obj)
System.out.println(i); {
System.out.println(k); 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;
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);
}
}
Metode Get Set

class ExampleGetSet
{
private String mystr;
private double a;

//initializam pe constructor cu string vid


public ExampleGetSet()
{
mystr = "";
}
//functie de get cu acces public
public String GetString()
class DemoExampleGetSet
{
{
return mystr;
} public static void main(String args[])
//functie de set cu acces public {
public void SetString(String str) ExampleGetSet obj = new ExampleGetSet();
{ obj.SetString("Orice fel de sir");
if (str !=null) obj.SetVal(3.4);
mystr = str; System.out.println(obj.GetVal());
else System.out.println(obj.GetString());
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 Triunghi extends Forma2D
{ Rolul constructorilor in mostenire
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");
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());
}
}
super
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;
}
void AfisezTip()
{
System.out.println("Triunghiul este " + tip_triunghi);

}
}
Suprascrierea metodelor

show() show()

int i,j int k

A B

class A
{ class B extends A
int i, j; {
A(int a, int b) int k;
{ B(int a, int b, int c)
i = a; {
j = b; super(a, b);
} k = c;
//afisez i si j }
void show() //afisez doar k
{ void show()
System.out.println("i si j: " + i + " {
" + j); System.out.println("k: " + k);
} }
} }
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

}
}
Object
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.
class AnyClass
{
int i;
toString()
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);
}
}
class Circle
{ equals()
private final int x, y, r;
// Constructorul de baza hashCode()
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)
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));
}
}
Curs 4

Clase abstracte
Interfete
Exceptii
I/O
Regulile clasei 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.
public abstract class Forma
{
public String categorie;
public abstract double Suprafata();
public abstract double Perimetrul(); //in loc de corp avem ";"
}

class Dreptunghi extends Forma


{
protected double latime, inaltime;
public Dreptunghi(double latime,
double inaltime)
class Cerc extends Forma {
{ categorie = "Dreptunghi";
public static final double PI = this.latime = latime;
3.1415926535897; this.inaltime = inaltime;
protected double raza; }
public Cerc(double r) public double getLatime()
{ {
this.raza = r; return latime;
categorie = "Cerc"; }
} public double getInaltime()
public double getRaza() {
{ return inaltime;
return raza; }
} //metodele abstracte care "prind
//metodele abstracte care "prind forma": forma":
public double Suprafata() public double Suprafata()
{ {
return PI*raza*raza; return latime*inaltime;
} }
public double Perimetrul() public double Perimetrul()
{ {
return 2*PI*raza; 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);
}
}
Forma.java:86: cannot find symbol
symbol : variable PI
location: class Forma
System.out.println("PI este: " + sir_forme[i].PI);
Interfete

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;
}

public interface Centrat


{
void setCentru(double x, double y);
double getCentruX();
double getCentruY();
}

public interface Positionabil extends Centrat


{
void setColDreaptaSus(double x, double y);
double getDreaptaSusX();
double getDreaptaSusY();
}
public interface Transformabil extends Scalabil, Translatabil,
Rotatabil {}
public interface Forma extends Positionabil, Transformabil {}
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;
}
}

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);
public interface Serii
{
int getNext(); //numarul urmator din serie
void reset(); // resetez
void setStart(int x); // cu ce incep
}

class DinDoiinDoi implements Serii class DinTreiinTrei implements Serii


{ {
int start; int start;
int val; int val;
DinDoiinDoi() DinTreiinTrei()
{ {
start = 0; start = 0;
val = 0; val = 0;
} }
public int getNext() public int getNext()
{ {
val += 2; val += 3;
return val; return val;
} }
public void reset() public void reset()
{ {
start = 0; start = 0;
val = 0; val = 0;
} }
public void setStart(int x) public void setStart(int x)
{ {
start = x; start = x;
val = x; val = x;
} }
} }
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
}
}
}
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];
for(int i=MIN; i < 11; i++)
{
if(i >= MAX) System.out.println(ERRORMSG);
else
{
nums[i] = i;
System.out.print(nums[i] + " ");
}
}
}
}

var.java:12: cannot assign a value to final variable MAX


MAX=20;
^
1 error
Exceptii

try
{
// blocul in care eroarea poate apare
}
catch (ExcepType1 exOb)
{
// handler pentru ExcepType1
}
catch (ExcepType2 exOb)
{
// handler pentru ExcepType2
}
finally
{ …
}
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.");
}
}
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;
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)
{
// prindem si tratam exceptia
System.out.println("Depasirea limitei sirului!");
}
System.out.println("Dupa blocul try catch.");
}
}
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+".");
}
}
}
}

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
Am depasit limita la pasul 6.
Am depasit limita la pasul 7.
try
{
...
}
catch (RuntimeException rex)
{
...
}
catch(Exception ex)
{
...
}
catch(Throwable tex)
{
...
}
AclNotFoundException, AWTException, BackingStoreException, BadLocationException, CertificateException,
ClassNotFoundException, CloneNotSupportedException, DataFormatException, DestroyFailedException, ExpandVetoException,
FontFormatException, GeneralSecurityException, GSSException, IllegalAccessException, InstantiationException,
InterruptedException, InvalidMidiDataException, InvalidPreferencesFormatException, IOException, LastOwnerException,
LineUnavailableException, MidiUnavailableException, MimeTypeParseException, NamingException,
NoninvertibleTransformException, NoSuchFieldException, NoSuchMethodException, NotBoundException, NotOwnerException,
ParseException, ParserConfigurationException, PrinterException, PrintException, PrivilegedActionException, PropertyVetoException,
RefreshFailedException, RemarshalException, RuntimeException, SAXException, ServerNotActiveException, SQLException,
TooManyListenersException, TransformerException, UnsupportedAudioFileException, UnsupportedCallbackException

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 (ArrayIndexOutOfBoundsException iex)


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

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();
}
}

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

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


{
...
}
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.";
}
}
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);
}
}
}
}
Stream-uri I/O
Byte Stream

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
FilterOutputStream Implementeaza OutputStream
InputStream Clasa abstracata ce descrie input stream
ObjectInputStream Input stream pentru obiecte
ObjectOutputStream Output stream pentru obiecte
OutputStream Clasa abstracata ce descrie output stream
PipedInputStream pipe pentru intrari
PipedOutputStream pipe pentru iesiri
PrintStream Output stream ce poate apela print( ) si println( )
PushbackInputStream Input stream care permite ca byte-tii sa fie returnati dintr-un stream
SequenceInputStream Input stream ce este o combinatie de mai multe stream-uri de intrari ce vor fi citite secvential unul dupa celalalt
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();
}
}
}
}
InputStream

Metoda Descriere
int available( ) Returneza numbarul de bytes disponibili de la intrare.
void close( ) 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.

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
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)
{
inputStream.close();
}
if (outputStream != null)
{
outputStream.close();
}
}
}
}
Reader

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.
int read(char buffer[ ]) Acelasi ca in cazul byte:de data aceasta se citesc caractere

Writer

Writer append(char ch) scrie la sfarsitul stream-ului caracterul ch.


Writer append(CharSequence chars) 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.
Writer append(CharSequence chars,
int begin, int end) scrie secventa chars la sfarsitul stream-ului delimitata
de begin si end.

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();
}
}
import java.io.*;
class PrintStreamDemo
{

public static void main (String[] argv)


{

try
{

FileOutputStream ostream = new FileOutputStream (”file1”);


PrintStream printStream = new PrintStream(ostream);
printStream.println("HELLO …");
printStream.close();
ostream.close();
}
catch (Exception e)
{
System.out.println("Exception occurred: " + e);
}
}

}
Curs 5

• Array
• Vector
• Stack
• Enumeration
• IO alte operatii
Array

int[] un_sir_de_date;

int[] un_sir_de_date = new int[10];

int[] creeazaSir(int marime)


{
return new int[marime];
}

NegativeArraySizeException!
int[] sir = new sir[3];
sir[0] = 23; int[] sir = {23,12,45,78};
sir[1] = 12; for (int i = 0; i < sir.length; i++) {
sir[2] = 45; System.out.print(sir[i] + " ");
sir[3] = 78; }
System.out.println();

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


class Country
{
String name;
long population;
public class ArrayDemo {
}
public static void main(String[] args)
class Invention
{
{
// declar un sir de int
String name; Object[] sir = new Object[4];
String description; sir[0] = new Country();
} ((Country)sir[0]).name = "Italia";
class Painting ((Country)sir[0]).population = 600000000000l;
{ sir[1] = new Painting();
String name; ((Painting)sir[1]).name = "Mona Lisa";
String technique; ((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

int matrix[][];

int matrix[][] = new int[3][2];

matrix[1][2] = 34;

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();
}
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();
}

Element( 0 ): 0.0 43.0 0.0 89.0 0.0


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

sir[1] = new float[]{2.3f,5,6.7f,11};

String[] names = {"Ion","Vasile","Andrei","Radu"};

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();
}
}

functie (new String[][]{{"Ion","Vasile","Adrian"},{"Andrei","Radu"}});


Copierea sirurilor

arraycopy (Object sourceArray, int sourceOffset,


Object destinationArray, int destinationOffset, int numberOfElementsToCopy)

int[] sir_sursa = {23,12,46,8};


int[] sir_destinatie = new int[6];
System.arraycopy(sir_sursa,0, sir_destinatie, 1,sir_sursa.length);

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 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));
}
Egalitatea a doua siruri

int array1[] = {1, 2, 3, 4, 5};


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

System.out.println(java.util.Arrays.equals(array1,array3));

int array2[] = {1, 2, 3, 4, 6};

int array2[] = {1, 2, 3};

Sirurile sunt imutabile


Vector

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

public Vector(Collection c) Vector v = new Vector(Arrays.asList(array));

import java.util.Vector;
public class InsertVector
{
public static void main (String args[])
{
Vector v = new Vector();
for (int i=0, n=args.length; i<n; i++)
{
v.add(args[i]);
}
}
}

arg0 arg1 arg2 arg3 arg4


public void add(int index, Object element)
public void insertElementAt(Object element, int index)

primul al doilea al treilea al patrulea

vector.add(2,”nou”);

primul al doilea nou al treilea al patrulea

public boolean addAll(Collection c)


public boolean addAll(int index, Collection c)

IllegalArgumentException.
Vectori de 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));
}
}
}
Afisarea vectorilor

System.out.println(v);

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Stergerea vectorilor

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


public void removeAllElements() public void removeElementAt(int index)

primul al doilea de sters al treilea al patrulea

vector.remove(2);

primul al doilea al treilea al patrulea


public void ensureCapacity(int minCapacity)
public int capacity()

primul Al doilea primul Al doilea

Al treilea

get

Vector v = new Vector();


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

Enumeration

Vector v = . . .
Enumeration e = v.elements();
while (e.hasMoreElements())
{
process(e.nextElement());
}
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
//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)+" ");

}
}
Cautare in vector

public int indexOf(Object element)


public int indexOf(Object element, int index)

public boolean contains(Object elment)

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]);
}
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")); Contine UnNume?: false
System.out.println("Unde e DAvid?: " + Contains Mircea?: true
v.indexOf("DAvid")); Unde e Radu?: 2
System.out.println("Unde e Radu de la sfarsit?: " +
Unde e DAvid?: -1
v.lastIndexOf("Radu"));
} Unde e Radu de la sfarsit?: 5
}
Copierea vectorilor
Vector v1 = . . .;
Vector v2 = (Vector)v1.clone();

public Object[] toArray()


public Object[] toArray(Object[] a)
public void copyInto(Object[] anArray)

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

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

Vector v = . . .;
String array[] = new String[v.size()];
v.copyInto(array);
Stiva
Operatii cu stiva
public Object pop()

public Object push(Object element)

public boolean empty()

public Object peek()

Stack s = new Stack();


public int search(Object element)
s.push("Primul element");
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();
Next: Al treilea element count--;
Al patrulea element }
// scot din stiva pe el si il afisez
[Primul element, Al doilea element, System.out.println(s.pop());
Al treilea element, Al cincilea element, //este stiva goala?
Al saselea element] System.out.println(s.empty());
System.out.println(s);
Al doilea element
false
[Primul element]
Enumerator

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

for (Enumeration enum = . . .; enum.hasMoreElements(); )


{
Object o = enum.nextElement();
processObject(o);
}
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()
{
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());
}
}
}
IO Scanare si formatare

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();
}
}
}

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();
}
}
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 + ".");
}
}

The square root of 2 is 1.4142135623730951.


The square root of 5 is 2.23606797749979.

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);
}
}

The square root of 2 is 1.414214.


public class Format
{
public static void main(String[] args)
{
System.out.format("%f, %1$+020.10f %n", Math.PI);
}
}

3.141593, +00000003.1415926536

Serializarea obiectelor

FileOutputStream out = new FileOutputStream("theTime");


ObjectOutputStream s = new ObjectOutputStream(out);
s.writeObject("Today");
s.writeObject(new Date());
0001101010
s.flush();

FileInputStream in = new FileInputStream("theTime");


ObjectInputStream s = new ObjectInputStream(in);
String today = (String)s.readObject();
Date date = (Date)s.readObject();
class MyClassToSerialize implements Serializable
{
String s;
int i;
double d;
public MyClassToSerialize(String s, int i, double d)
{
this.s = s;
this.i = i;
this.d = d;
}
public String toString()
{
return "s=" + s + "; i=" + i + "; d=" + d;
}
}
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);
}
}
}
StringBuffer

x = "a" + 4 + "c"

x = new StringBuffer().append("a").append(4).append("c") .toString()

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);

StringBuffer sb = new StringBuffer("Hello World!");


sb.delete(0,6);
sb.delete(5,6);
System.out.println(sb);

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);
Cursul 6

Liste inlantuite
Collection
List
Set
Liste simplu inlantuite
Reprezentarea listelor
class LinkedList
{
int data;
LinkedList next;
public LinkedList()
{
next = null;
data =0;
}
}

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;
23 10 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);
}
}
class LinkedList
{
int data;
LinkedList next;
LinkedList head;
public LinkedList()
{
head = this;
next = null;
data =0;
}
public LinkedList(LinkedList head)
{ class LinkedListDemo
this.head = head; {
next = null; public static void main(String args[])
data =0; {
} 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;
23 10 49 }
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);
}
}
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)
throw new NullPointerException("Elementul urmator este null!");
return next.data;
}

LinkedList list = new LinkedList();


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

public void RemoveNodeAfter()


{
next = next.next;
}
public void InsertAfterAnElement(int dataVal, Nod start, int newVal)
{
Nod tmp = new Nod(newVal,null);
Nod prev = start;
Nod iterator = start;
do
{
if (iterator.data == dataVal)
{
prev = iterator.next;
break;
}
iterator=iterator.next;
}
while(iterator!=null);
tmp.next = prev;
iterator.next = tmp;
}
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;
}
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));
System.out.println(Nod.SearchInList(head, 40));
head.RemoveNodeAfter();
System.out.println(head.ListToString());
}

3 20 12 15
3 36 20 12 15
3 36 12 15
3 60 40 36 12 15
36
40
3 40 36 12 15
Liste dublu inlantuite

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;
}

}

null null
Frameworkul de colectii Java

Clase concrete
Interfeţe HashTable Array mutabil Arbore Lista înlănţuita Altele
balansat
Set HashSet TreeSet
SortedSet TreeSet
List ArrayList LinkedList Vector, Stack
Map HashMap TreeMap Hashtable,
Properties
SortedMap TreeMap
Collection

Metoda Descrierea
add() Adaugă un element intr-o colecţie
addAll() Adaugă o colecţie in alta colecţie
clear() Șterge toate elementele dintr-o colecţie
contains() Verifica daca un element se afla intr-o colecţie
containsAll() Verifica daca o colecţie se afla intr-o alta colecţie
equals() Verifica egalitatea a doua colecţii
hashCode() Returnează un id specific unei colecţii
isEmpty() Verifica daca o colecţie este goala
iterator() Returnează un obiect dintr-o colecţie ce permite vizitarea elementelor acesteia
remove() Șterge un element dintr-o colecţie
removeAll() Șterge elementele unei colecţii din colecţia curenta
retainAll() Șterge elementele dintr-o colecţie care nu sunt in alta colecţie
size() Returnează numărul de elemente dintr-o colecţie
toArray() Returnează elementele dintr-o colecţie ca sir.
public boolean contains (Object element)
public boolean add(Object element)

public boolean addAll(Collection c)


public boolean containsAll(Collection c)

public void clear()


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

public boolean isEmpty()


public boolean removeAll(Collection c)

[1,5,8,1,2,4,2,5,7,6]

c = [1,5]

[8,2,4,2,7,6]
Copierea colectiilor

public boolean retainAll(Collection c)


// creem un sir cu elemente din colectie:
Collection c = ...;
String array[] = new String[0];
[1,5,8,1,2,4,2,5,7,6]
array = (String[])c.toArray(array);
//Marimea sirului o vom stabili noi
c = [1,5,23,29]
Collection c = ...;
String array[] = new String[c.size()];
[1,5]
array = (String[])c.toArray(array);

public Iterator iterator()


Iterator
Collection c = ...
hasNext() – verifica daca mai sunt elemente de iterat Iterator i = c.iterator();
next() – returnează următorul element din lista while (i.hasNext())
{
remove() – șterge un element din iterator process(i.next());
}

Iterator ce permite filtarea


interface IPredicate
{
public boolean predicate(Object o);
}
class Predicate implements IPredicate
{
public boolean predicate(Object o)
{
return o.toString().startsWith("A");
}
}
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
//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; //si metoda next ale interfetei Iterator
boolean hasNext; public Object next()
while (hasNext = iter.hasNext()) {
{ if (!doneNext)
next = iter.next(); {
if (pred.predicate(next)) boolean has = hasNext();
{ if (!has)
break; {
} throw new NoSuchElementException();
} }
return hasNext; }
} doneNext = false;
return next;
}
}
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());
}
}
} Exceptii in colectii

import java.util.*;
public class CollectionException
{
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");
}
}
}

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)
List list = Arrays.asList(args);
list.add("Add"); // Arunca UnsupportedOperationException

Liste: List

Metoda Descriere
indexOf() Cauta un element in lista
lastIndexOf() Cauta un element in lista începând de la sfârșitul acesteia
listIterator() returnează iteratorul personalizat al listei
set() modifica un element specificat din lista
get() returnează un element din lista
remove() șterge un anumit element din lista
subList() returnează o parte din lista

ArrayList

Metoda Descriere
ArrayList() Constructorul pentru o lista goala
ensureCapacity() Creează un buffer intern cu o capacitate dubla fata de cea anterioara
removeRange() Șterge un anumit interval de elemente din lista
trimToSize() limitează capacitatea buffer-ului intern la mărimea specificata
public ArrayList()
public ArrayList (int initialCapacity)

String elements[] = {"Shopping List","Wine List","Food List"};


List list = new ArrayList(Arrays.asList(elements));

public boolean add(Object element)


public boolean add(int index, Object element

List list = new ArrayList();


list.add("3");
Un nou
list.add("abs");
list.add("58"); 3 abs 58 3 element abs 58
// Adaug in interiorul listei
list.add(1, "un nou element");

public boolean addAll(Collection c)


public boolean addAll(int index, Collection c)

public Object get(int index)

public void clear()

public boolean remove(Object element)


public Object remove(int index)
public boolean removeAll(Collection c)

{”element”,”index”,”element”,”sir”,”clasa”}

C ={”lista”,”multime”,”element”}

{”index”,”sir”,”clasa”}

public boolean retainAll(Collection c)

protected void removeRange(int fromIndex, int toIndex)

public class ArrayLists


public Iterator iterator()
{
public ListIterator listIterator()
public static void main(String args[])
public ListIterator listIterator(int index)
{
ArrayList list = new ArrayList();
list.add("prim");
List list = Arrays.asList(new String[]
list.add("2");
{"323", "re", "12","eo"});
list.add("al treilea");
Iterator iter = list.iterator();
list.add(1,"intre 1 si 2");
while (iter.hasNext())
if (list.contains("al treilea"))
{
list.remove("al treilea");
System.out.println(iter.next());
System.out.println(list);
}
System.out.println(list.indexOf("2"));
}
public int indexOf(Object element) }
public int lastIndexOf(Object element)

public Object set(int index, Object element)


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 static void main(String args[])


{
ArrayList list = new ArrayList();
list.add(new Point(1,2));
false
list.add(new Point(3,4));
true
list.add(new Point(2,1));
true
list.add(new String("un punct"));
[x = 1 y = 2 id = 106
Point pt = new Point(-1,-1);
, x = 3 y = 4 id = 186
System.out.println(list.contains(pt));
, x = 2 y = 1 id = 100
System.out.println(list.contains("un punct"));
, un punct]
System.out.println(list.contains(new Point(3,4)));
System.out.println(list);
}
LinkedList
public LinkedList()
public LinkedList(Collection col)

public boolean add(Object element)


public boolean add(int index, Object element)

public boolean addFirst(Object element)


public boolean addLast(Object element)

public Object getFirst()


public Object getLast()

public Object removeFirst()


public Object removeLast()

Iteratorul LinkedList

Metoda Descriere
hasNext verifica pe direcţia înainte, daca mai sunt elemente
hasPrevious verifica pe direcţia înapoi, daca mai sunt elemente
next returnează următorul elemente
nextIndex returnează indexul din colecţie al următorului element
previousIndex returnează indexul din colecţie al elementului anterior
remove șterge un element din iterator
set modifica elementul curent
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);
}
}

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]
Set
public HashSet()
public HashSet(int initialCapacity)
public HashSet(int initialCapacity, int loadFactor)

String elements[] = {"Englez", "German", "Roman","Italian"};


Set set = new HashSet(Arrays.asList(elements));

public boolean removeAll(Collection c)

{Englez", "German", "Roman","Italian"}

c ={Englez", "German", "Englez","Englez"}

{"Roman","Italian"}

public boolean retainAll(Collection c)

public boolean equals(Object o)


public int hashCode()
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)); // true
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);
}
}
}
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);
Curs 7

•TreeSet
•Sortarea Colecţiilor
•Dictionary, HashTable şi Properties
•Map
•Clasa Collections
TreeSet
Reguli:
•Fiecare nod este roşu sau negru
•Rădăcina este întotdeauna un nod negru
•Dacă nodul este roşu, copii lui trebuie sa fie de culoare neagră
•Fiecare cale de la rădăcină la frunze trebuie sa conţină acelaşi număr de noduri negre.
Nume metoda Descriere
TreeSet() Constructorul unui TreeSet.
add() Adaugă un element în mulţime
addAll() Adaugă o colecţie de elemente în mulţime
clear() Şterge elementele din mulţime
clone() Creează o clonă cu elementele din mulţime
comparator() Returnează un obiect de tip Comparator
contains () Verifică existenţa unui obiect în mulţime
first() Returnează primul element din mulţime
headSet() Returnează un subset de elemente de la începutul mulţimii
isEmpty() Verifică dacă mulţimea este goală.
iterator() Returnează un obiect din set ce permite vizitarea mulţimii
last() Returnează ultimul element din şir
remove() Şterge un element din mulţime
size() Returnează numărul de elemente din subset
subSet() Returnează o submulţime din mulţimea iniţială
tailSet() Returnează o submulţime de elemente de la sfârşitul mulţimii iniţiale

public TreeSet()
public TreeSet(Comparator comp)

public TreeSet(Collection col)


public TreeSet(SortedSet set)
Comparator

public Comparator comparator()

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());
}
}

[Vasile, Radu, Mircea, Ion, Andrei]


java.util.Collections$ReverseComparator@3e25a5
public Object first()
public Object last()
NoSuchException

public Iterator iterator()

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());
}
}
}
Andrei
Ion
Mircea
Radu
Vasile
Subarbori

public SortedSet headSet(Object toElement)


public SortedSet tailSet(Object fromElement)
public SortedSet subSet(Object fromElement, Object toElement)

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);
}

[Ion, Mircea, Radu]


[Andrei, Ion, Mihai, Radu, Vasile]

fromElement <= set view < toElement

SortedSet headSet = set.headSet(toElement+"\0");


Sortarea colecţiilor

Interfaţa Comparable
public int compareTo(Object Obj)

•Elementele trebuie să fie comparabile mutual.


•Valoarea de returnat exprimă poziţia relativă într-o ordonare naturală.
•Ordonarea naturală se va face pe baza metodei equals().
• Niciodată nu se apelează metoda direct.

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();
}
}
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"),
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);
} [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]
Interfaţa Comparator
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);
} 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)
{
//asa ca va trebui sa apelam cast
Angajat a = (Angajat)o;
System.out.println(a);
}
}
Dictionary, HashTable şi Properties

30 11 45 19 30 11 45 19

0 1 2 3 4we abc o40 rt2

Dictionary
elements() Returnează un obiect din dicţionar ce permite vizitarea tuturor cheilor
get() 1.0 Returnează o valoare din dicţionar
isEmpty() Verifică dacă un dicţionar este gol
keys() Returnează colecţia de chei din dicţionar
put() Introduce un element format din cheie şi valoare
remove() Şterge un element din dicţionar
size() Returnează numărul de element din dicţionar
Nume metodă Descriere
Hashtable() Constructorul colecţiei
clear() Şterge elementele din colecţie
clone() Creează un HashTable cu aceleaşi elemente
contains() Verifică existenţa unui obiect în colecţie
containsKey() Verifică existenţa unei chei în HashTable
containsValue() Verifică existenţa unei valori în HashTable
elements() Returnează un element ce permite vizitarea colecţiei
entrySet() Returnează un set de perechi cheie – valoare
equals() Verifică egalitatea între două obiecte
get() Returnează valoarea de la o anumită cheie.
hashCode() Calculează codul hash al unei colecţii
isEmpty() Verifică dacă colecţia este goală sau nu.
keys() Returnează o colecţie de chei din HashTable.
keySet() Returnează o colecţie de chei din HashTable.
put() Pune o pereche cheie-valoare în HashTable
putAll() Pune o colecţie de cheie-valoare în HashTable
rehash() Măreşte capacitatea colecţiei
remove() Şterge un element din colecţie
size() Returnează numărul de elemente din colecţie
toString() Returnează întregul HashTable ca un String
values() Returnează o colecţie de valori din HashTable
Coliziuni

101
101
114
114

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"));
}
}
Metodele clasei HashTable
public Hashtable()
public Hashtable(int initialCapacity)
public Hashtable(int initialCapacity, float loadFactor)

public Hashtable(Map t)

public Object put(Object key, Object value)

public Object remove(Object key)

public void clear()


Enumeration enum = hash.keys();
public int size() while (enum.hasMoreElements())
public boolean isEmpty() {
String key = (String)enum.nextElement();
System.out.println(key + " : " + hash.get(key));
public Object get(Object key)
}

public Enumeration keys()


public Set keySet()

Set set = hash.entrySet();


public Enumeration elements() Iterator it = set.iterator();
public Collection values() while (it.hasNext())
{
public Set entrySet() Map.Entry entry = (Map.Entry)it.next();
System.out.println(entry.getKey() + " : “
public boolean containsKey(Object key) + entry.getValue());
}
public boolean contains(Object value)
public boolean containsValue(Object value)
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); eee fff eeds
String line; fff aaa eeds aaa
while ((line = br.readLine()) != null)
{
processLine(line, map); fff : 2
} eeds : 2
Enumeration en = map.keys(); eee : 1
while (en.hasMoreElements()) aaa : 2
{
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()) static void addWord(Map map, String word)
{ {
//map este cheia Object obj = map.get(word);
//iar st.nextToken() este valoare if (obj == null)
addWord(map, st.nextToken()); {
} map.put(word, ONE);
} }
else
{
int i = ((Integer)obj).intValue()
+ 1;
map.put(word, new Integer(i));
}
}
}
Properties
public Object setProperty(String key, String value)
public String getProperty(String key)
public String getProperty(String key, String defaultValue)

public Enumeration propertyNames()

void load(InputStream inStream) throws IOException


void store(OutputStream out, String header) throws IOException

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");
prop.setProperty("cheia2", "valoarea2");
prop.setProperty("cheia3", "valoarea3");
prop.setProperty("cheia1", "valoarea");
prop.save(fo, "Aici vin comentarii");
}
}

#Aici vin comentarii


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

Interfaţa Map.Entry

Metoda Descriere
equals() verifică egalitatea cu un alt obiect
getKey() returnează cheia din Map.Entry
getValue() returnează valoarea din Map.Entry
hashCode() calculează codul hash pentru obiectul actual
setValue() schimbă valoarea din Map.Entry
HashMap

Nume metodă Descriere


containsKey() Verifică existenţa unei chei in hash map
containsValue() Verifică existenţa unei valori in hash map
entrySet() Returnează o colecţie de perechi sub forma unui map
get() Returnează valoarea pentru o anumită cheie
isEmpty() Verifică dacă o colecţie este goală sau nu
keySet() Returnează toate cheile din hash map
put() Plasează o pereche în hash map
putAll() Introduce in hash map o colecţie de perechi cheie-valoare
remove() Scoate din colecţie o pereche
size() Returnează numărul de elemente dintr-un hash map
values() Returnează o colecţie de valori din hash map

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

public Object put(Object key, Object value)


public void putAll(Map map)

public Object remove(Object key) public Set entrySet()

public boolean containsKey(Object key)


public Object get(Object key)
public boolean containsValue(Object value)
public Set keySet()

public Collection values()


WeakHashMap

Referinţe
•Referinţe puternice, nu au o clasă specială
•Referinţe soft sunt ca un cache. Când memoria este puţină GC va elibera arbitrar aceste referinţe.
•Referinţe slabe: acestea sunt mai slabe decât cele soft. Dacă singura referinţă a unui obiect sunt doar de tip slab, GC va putea şterge obiectul oricând.
•Referinţe fantomă. Acestea permite o notificare înainte ca GC să apeleze finalizarea pe obiectul în cauză.
import java.util.*;
public class Weak
{ Thread t = new Thread(runner);
private static Map map; System.out.println(map);
public static void main (String args[]) //incep procedura run
{ t.start();
map = new WeakHashMap(); //firul principal sta si asteapta ca
map.put(new String("Cheie"), "Valoare"); System.out.println("Main waiting");
//o metoda ce se executa pe un alt fir try
//decat cel curent {
Runnable runner = new Runnable() //cel nou sa isi incheie executia
{ t.join();
public void run() //dupa ce thread-ul t nu mai este
{ System.out.println(map);
while (map.containsKey("Cheie")) }
{ catch (InterruptedException ignored)
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();
}
}
};
TreeMap

firstKey() Returnează prima cheie din Map


headMap() Returnează sub map-ul de la începutul map-ului original
lastKey() Returnează ultima cheie din colecţie
subMap() Returnează un subarbore oarecare din colecţie
tailMap() Returnează subarborele de la sfârşitul celui original

public TreeMap()
public TreeMap(Map map)

public TreeMap(Comparator comp)


public TreeMap(SortedMap map)

SortedSet headMap(Object toKey)


SortedSet tailMap(Object fromKey)
SortedSet subMap(Object fromKey, Object toKey) fromKey <= map keys < toKey

Map headMap = map.headMap(toKey+"\0");

Object firstKey()
Object lastKey()

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

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

Map<String, Double> group =


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

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


{Catalin=8.55}
4.67
null
true
false
true
{Sebastian=6.0, Remus=8.96, Catalin=8.55, Bogdan=7.8}
Clasa Collections
Metoda Descrierea
EMPTY_LIST Reprezintă o listă goală imutabilă
EMPTY_MAP Reprezintă o colecţie Map goală imutabilă
EMPTY_SET Reprezintă o mulţime goală imutabilă
binary_search() caută un element în listă folosind tehnica de căutare binară
copy() copiază elementele dintre două liste
enumeration() converteşte o listă la un Enumeration
fill() umple lista cu un element anume
max() caută maximul dintr-o colecţie
min() caută minimul dintr-o colecţie
nCopies() creează o listă imutabilă cu multiple copii ale unui element
reverse() inversează elementele într-o listă
reverseOrder() returnează un comparator ce inversează ordinea elementelor
shuffle() reordonează elementele aleatoriu
singleton() returnează un set imutabil de un element
singletonList() returnează o listă imutabilă de un element
singletonMap() returnează un Map imutabil de un element
sort() reordonează elementele în listă
public static void sort(List list)
public static void sort(List list, Comparator comp)

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();
}
}
Căutarea binară

1 3 4 6 8 9 11 14 15 17 19 20 23 25

1 3 4 6 8 9 11 14

1346

public static int binarySearch(List list, Object key)


public static int binarySearch(List list, Object key, Comparator comp)
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);
}
}

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]
Curs 8

•Bazele multithreading
•Clasa Thread si Interfata Runnable
•Mai multe fire de executie
•Sincronizarea
•Comunicarea intre fire de executie
•Suspendarea/reluarea firelor de executie
•Grupuri de fire de executie
Conceptul de thread
Starile unui thread
Clasa Thread

Medoda Descriere
final String getName( ) Se obţine numele thread-ului
final int getPriority( ) Se obţine prioritatea thread-ului
final boolean isAlive( ) Determină faptul că un thread mai este în execuţie sau nu.
final void join( ) Aşteaptă terminarea unui thread pentru a continua.
void run( ) Entry point pentru un thread.
static void sleep(long milliseconds) Suspendă un thread un număr de milisecunde
void start( ) Începe execuţia unui fir prin apel run()

Interfata Runnable

public void run()


// crearea unui fir de executie
//prin implementarea interfetei 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 intai se construieste obiectul
// ce va contine thread-ul Main thread starting.
MyThread mt = new MyThread("Child #1"); .Child #1 starting.
// se construieste un fir pe baza obiectului ....In Child #1, count is 0
Thread newThrd = new Thread(mt); .....In Child #1, count is 1
// incepe noul fir de executie .....In Child #1, count is 2
newThrd.start(); .....In Child #1, count is 3
do .....In Child #1, count is 4
{ .....In Child #1, count is 5
System.out.print("."); .....In Child #1, count is 6
try .....In Child #1, count is 7
{ .....In Child #1, count is 8
Thread.sleep(100); .....In Child #1, count is 9
} Child #1 terminating.
catch(InterruptedException exc) Main thread ending.
{
System.out.println("Main thread interrupted.");
}
} while (mt.count != 10);
System.out.println("Main thread ending.");
}
}
Diagrama exemplului anterior
class MyThread implements Runnable
{
int count;
//firul de executie cu care vom lucra
Thread thread;
MyThread(String name)
{
count = 0;
//instantiem un nor fir
thread = new Thread(this, name);
//firul va porni la apelul constructorului
//adica la instantierea 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 threadul nou va porni automat
//la instantierea 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.");
}
}
Mostenirea clasei Thread

class MyThread extends Thread