Sunteți pe pagina 1din 129

CRENGUŢA BOGDAN LUCA DAN ŞERBĂNAŢI

PROGRAMAREA ORIENTATĂ
SPRE OBIECTE
(LIMBAJUL JAVA)

NOTE DE CURS

2006
CONŢINUT

1. INTRODUCERE IN PROGRAMAREA ORIENTATA SPRE OBIECTE...3


OBIECTE .....................................................................................................................................3
CLASE DE OBIECTE ......................................................................................................................6
2. RELATII DE ASOCIERE INTRE CLASE ................................................9
CARACTERISTICILE ABORDARII ORIENTATE PE OBIECTE ...............................................................14
3. RELAŢIA DE GENERALIZARE/SPECIALIZARE ÎNTRE CLASE .........16
MOŞTENIREA ÎN JAVA ................................................................................................................17
4. ARRAY-URI ..........................................................................................23
INIŢIALIZAREA UNUI ARRAY .........................................................................................................23
ARRAY-URI MULTIDIMENSIONALE ................................................................................................24
LUCRUL CU ARRAY-URI ..............................................................................................................25
ARRAY ÎN METODE .....................................................................................................................26
5. CLASE ABSTRACTE............................................................................28
6. INTERFETE ..........................................................................................32
7. CLASE INTERNE .................................................................................36
DEFINIREA CLASELOR INTERNE ..................................................................................................36
CLASE INTERNE STATICE ...........................................................................................................38
CLASE INTERNE ŞI MOŞTENIREA .................................................................................................38
UTILIZAREA CLASELOR INTERNE ÎN APLICAŢII OO .......................................................................39
8. INPUT/OUTPUT ÎN JAVA.....................................................................42
9. GESTIUNEA EXCEPŢIILOR ................................................................55
10. COLECTII .............................................................................................62
STRUCTURI DE DATE DINAMICE ..................................................................................................62
TIPOLOGII DE COLECŢII ..............................................................................................................62
COLECŢII ÎN JAVA ......................................................................................................................76
11. APPLET ................................................................................................91
12. INTERFETE GRAFICE .........................................................................99
13. GESTIUNEA EVENIMENTELOR .......................................................105
13.1 MODELUL GESTIUNII EVENIMENTELOR ÎN PROIECTAREA COMPONENTELOR GRAFICE .......105
13.2 EVENIMENTE SEMANTICE .............................................................................................107
13.3 EVENIMENTE DE NIVEL COBORÂT..................................................................................113
13.4 CLASE ADAPTER ALE INTERFEŢELOR DE ASCULTARE .....................................................118
14. PROGRAMAREA INTERFETELOR GRAFICE CU SWING ...............122
FERESTRE INTERNE.................................................................................................................124
FERESTRE INTERNE.................................................................................................................125
CLASA JVIEWPORT..................................................................................................................126
JSCROLLPANE ........................................................................................................................127
15. BIBLIOGRAFIE ...................................................................................129

2
1. INTRODUCERE IN PROGRAMAREA ORIENTATA SPRE OBIECTE

OBIECTE
D. Un obiect este un un mod simplificat de a identifica într-un program un lucru, o entitate din
lumea reală sau imaginată.
Din punctul de vedere al paradigmei pe obiecte, un obiecte este o combinatie de:
- informatii de structura, descrise de o multime de atribute ale obiectului, si
- functionalitate descrisa de o multime de operatii ce actioneaza asupra atributelor obiectului si
eventual, asupra altor obiecte.
D. Un atribut este o abstractizare a unei proprietati a unui obiect din lumea reala. Un atribut se
caracterizeaza prin: nume, tip, valoare si eventual constrangeri. De exp, magazinul Tomis, ca orice
magazin se caraterizeaza prin urmatoarele proprietati: denumire, numele proprietarului, ora de
deschidere si ora de inchidere. In plus, am putea avea si un numar de identificare care sa diferentieze
unic magazinul Tomis de orice alt magazin.
D. Valorile curente ale tuturor atributelor unui obiect formeaza starea obiectului. De exemplu, starea
magazinului Tomis ar putea fi urmatoarea:
numar de identificare= 10
denumire= Tomis
numele proprietarului= Ionescu Pop
ora de deschidere= 10
ora de inchidere= 18
Atributele unui obiect sunt descrise in momentul analizei si proiectarii programului OO folosind
limbajul UML si apoi sunt implementate cu ajutorul variabilelor intr-un limbaj de programare (in
particular, Java):
Nivelul specificarii (limbajul UML) Nivelul implementarii (limbajul Java)
- nrIdentificare: Integer = 10 {valoare unica private int nrIdentificare;
pentru fiecare obiect}
- denumire: String private String denumire;
- numeProprietar: String private String numeProprietar;
- oraDeschidere: Integer = 9 private int oraDeschidere= 9;
- oraInchidere: Integer = 18 private int oraInchidere= 18;
D. O operatie este un algoritm privat al obiectului, adica se executa in mediul său si care opereaza
asupra valorilor atributelor sale sau ale altor obiecte pentru a furniza un serviciu unui alt obiect
numit client. Operatiile se deduc din responsabilitatile obiectului pe care trebuie sa le indeplineasca.
De exemplu, obiectul magazinul 10 trebuie sa furnizeze acces la ora de inchidere a magazinului,
adica sa furnizeze informatii de stare a obiectului, deoarece proprietarul magazinului vrea sa stie la
ce ora se inchide magazinul.
In plus, proprietarul magazinului (ca orice client al obiectului) poate sa modifice ora de inchidere a
magazinului, modificand astfel starea obiectului Magazin. Sau proprietarul ar vrea sa stie care este
intregul orar al magazinului.
Nivelul specificarii (limbajul UML) Nivelul implementarii (limbajul Java)
+ obtineOraInchidere(): Integer public int getOraInchidere(){
return oraInchidere;
}
+ modificaOraInchidere(nouaOra: Integer) public void setOraInchidere(int oraNoua){
oraInchidere=oraNoua;
}

3
Nivelul specificarii (limbajul UML) Nivelul implementarii (limbajul Java)
+ obtineOrar(): String public String getOrar(){
return oraDeschidere+”:00-”
+oraInchidere+”:00”;}

D. Implementarea unei operatii in limbajul Java se numeste metoda. O metoda se caracterizeaza prin
urmatoarele elemente:
- prototipul metodei = contine elementele de declarare a metodei
- semnatura metodei = combinatie intre numele metodei si tipul parametrilor in ordinea declararii
lor
- definitia metodei = corpul metodei ce contine instructiuni ce implementeaza algoritmul operatiei.
Elementele de structura si functionalitate ale unui obiect pot fi proiectate pentru a fi accesibile din
afara obiectului. Pana acum, orice client (inclusiv proprietarul) are acces la operatiile obiectului 10
deoarece operatiile respective sunt publice.
D. Totalitatea elementelor ce pot fi accesate din exteriorul obiectului formeaza interfata acestuia.
Rezulta ca starea unui obiect poate fi modificata numai prin intermediul interfetei lui.
Observatie. Desi obiectele se diferentiaza prin stare, aceasta din urma nu asigura identitatea
obiectelelor. Adica, puteam avea doua obiecte cu aceeasi stare, dar care sa fie diferite. In cazul
obiectului 10, identitatea acestuia este data de valoarea atributului numar de identificare. In acest
caz, spunem ca atributul (proprietatea) numar de identificare furnizeaza un criteriu de identitate
pentru obiectele de acelasi tip.
Principiul OO care sta la baza definirii interfetei unui obiect este cel al incapsularii informatiilor
(“information hiding”). Conform acestui principiu, trebuie sa ascundem in interiorul obiectului
(folosind modificatorul de acces private) toate elementele (atribute sau operatii) care nu sunt utile
altor obiecte (numite obiecte client: se afla in exteriorul obiectului si comunica cu el prin mesaje) si
sa lasam numai acele informatii in care clientii sunt interesati.
De exemplu, interfata obiectului 10 este formata din metodele publice fara sa dam acces direct la
variabilele instanta. In acest caz ele sunt variable private.
Un alt exemplu il constituie cazul in care proprietarul, ca orice client al obiectului 10, vrea sa stie
daca magazinul cu numarul de identificare 10 este deschis sau nu. Solutia este ca obiectul 10 sa aiba
o metoda publica care sa-i spuna proprietarului ca este deschis sau nu in functie de momentul
apelarii metodei esteDeschis():
Nivelul specificarii Nivelul implementarii (limbajul Java)
(limbajul UML)
+ obtineRaspuns(): String public String getMesaj(){
if (esteDeschis()) return “Este deschis!”;
else return “Este inchis!”;
}
private boolean esteDeschis(){
Calendar c=Calendar.getInstance();
int oraCurenta=c.get(Calendar.HOUR_OF_DAY);
if (oraCurenta>=oraDeschidere &&
oraCurenta<=oraInchidere) return true;
return false;
}

CONCEPTE
Un obiect este o instanta (sau exemplificare) a unui concept.
D. Un concept este o idee sau o notiune care se aplica:
- unor entitati concrete: lucruri care ne inconjoara sau fapte care se petrec de-a lungul timpului; sau
4
- unor entitati abstracte cum ar fi constructiile noastre mentale.
Un concept se caracterizeaza prin trei elemente:
- nume: cuvantul pe care-l folosim cand ne referim la acel concept;
- o intentie (semantica): ideea sau notiunea la care se refera numele;
- o extensie data de multimea instantelor conceptului.
Conceptele pot fi clasificate in mai multe categorii:
Categorie Exemple de concepte Instante de concepte
tangibil persoana, masina, scaun colegul Ionescu, masina
mea
intangibil timp, firma 17:23, Torent Computers
fiinta din lumea reala persoana, pisica, caine colegul Ionescu, Azor
lucru din lumea reala artefact, masina, casa, copac
descriere a unui alt obiect marca de masina, specificatia unei Mercedes
aplicatii OO, documentatie de
proiect
organizatie firma, agentie de voiaj, banca, Fundatia Oamenilor de
fundatie afaceri
loc magazin, port, oras Magazinul Tomis, portul
Constanta
rol in scenarii reale sau pilot, administrator, profesor,
imaginare medic, proprietar, profesor, autor,
student, angajat
relational casatorie, asociatie familiala
tranzactie vanzare, rezervare, cumparare,
contractare, inchiriere
elementele ce compun o articol dintr-o vanzare de mai
tranzactie multe produse, rezervarea pentru o
persoana in rezervarea unei
excursii in grup
eveniment trimitere mesaj, revizie, vizita
medicala, inregistrare automobil
proces achizitie on-line a unor produse,
înscriere la universitate, admitere
agregat clasa de elevi, fereastra de
componente grafice, masina, masa,
roi de albine
entitate continuta intr-un elev intr-o clasa, buton intr-o
agregat fereastra, motor, picior de masa,
albina
dispozitiv vitezometru, modem, inregistrator
de casa
sistem extern sistemul informatic al serviciului
personal, sistemul de contabilitate,
sistemul de autorizare a utilizarii
unui card bancar
inregistrari de contracte sau factura, log-are, memorarea sosirii
evenimente unui mesaj in sistem, comanda de

5
Categorie Exemple de concepte Instante de concepte
achizitie
concepte abstracte calendar gregorian, integrala,
multime, ecuatie
Observatie. Categoriile de concepte nu sunt ortogonale (adica, independente intre ele), un concept
putand sa apartina mai multor categorii.
CLASE DE OBIECTE
D. O clasă este o descriere a unei mulţimi de obiecte care au aceleaşi atribute, aceleaşi operaţii,
aceleaşi relaţii cu alte clase şi aceeaşi semantică.
Unitatea de baza a limbajului Java (ca orice limbaj orientat spre obiecte) este clasa. Din acest
motiv, un program Java contine cel putin o clasa.

O clasă descrie instantele unui concept.

Nivelul Nivelul specificarii (limbajul UML) Nivelul implementarii (limbajul Java)


conceptual
Magazin public class Magazin{
Magazin private int nrIdentificare;
private String denumire,
nrIdentificare : Integer numeProprietar;
denumire : String private int oraDeschidere,
numeProprietar : String oraInchidere;
oraDeschidere : Integer public int getOraInchidere(){
oraInchidere : Integer return oraInchidere;
}
obtineOraInchidere() : Integer public int getOraDeschidere(){
obtineOraDeschidere() : Integer return oraDeschidere;
modificaOraDeschidere(nouaOra : Integer) }
public String getMesaj(){
modificaOraInchidere(nouaOra : Integer)
if (esteDeschis())
obtineOrar() : String return “Este deschis!”;
obtineRaspuns() : String else return “Este inchis!”;
}
private boolean esteDeschis(){
Calendar c=Calendar.getInstance();
int
oraCurenta=c.get(Calendar.HOUR_OF_DAY);
if (oraCurenta>=oraDeschidere &&
oraCurenta<=oraInchidere)
return true;
return false;
}
}
Observatie. Atributele si operatiile obiectelor sunt implementate in Java ca variabile instanta,
respectiv metode instanta.
O clasa poate defini variabile si metode de clasa. O variabila de clasa implementeaza un atribut al
clasei si nu al obiectelor sale. Asadar, valoarea unei variabile de clasa este partajata de toate
obiectele clasei respective. De exemplu, pentru a implementa in Java constrangerea ca valoarea
variabilei nrIdentificare este unica avem nevoie de o variabila statica care sa numere instantele
(obiectele) clasei Magazin: private static int idCurent=1. Valoarea reprezinta numarul de
identificare al noului obiect.
O metoda de clasa este o metoda care apartine clasei si din acest motiv este definita ca fiind statica
si la care au acces toate obiectele clasei.

6
Se pot proiecta si implementa clase care au numai metode statice. Se numesc clase utilitare si
furnizeaza servicii globale. De exemplu, clasa Arrays contine metode de sortare a elementelor unui
array de numere intregi, numere reale, caractere, inclusiv obiecte dupa diferite criterii.
O clasa este un model pentru obiectele sale. De exemplu, toate obiectele clasei Magazin, vor
avea aceeasi structura: atribute si operatii.

O clasa este o fabrica pentru obiectele sale, deoarece orice clasa are cel putin un constructor.

D. Un constructor este o metoda cu ajutorul careia sunt create obiecte din care clasa in care este
definit constructorul si initializeaza variabilele instanta ale noului obiect.
Alte caracteristici ale unui constructor:
- are acelasi nume ca numele clasei
- nu are tip al rezultatului
- este apelat prefixandu-l cu operatorul new. Folosirea acestui operator determina crearea unei zone
de memorie in Heap in care se aloca spatiu pentru fiecare variabila de instanta a obiectului nou
creat.
Asadar, clasa Magazin are un constructor. Se numeste constructor implicit deoarece este furnizat de
Java. Acesta nu are parametri si initializeaza cu valori implicite sau cu valorile definite in sectiunea
de declarare a variabilelor instanta.
O clasa poate defini explicit unul sau mai multi constructori care sa realizeze initializarea
variabilelor instanta si eventual modificarea variabilelor de clasa, daca este necesar.
In cazul in care o clasa are mai multi constructori, acestia vor fi spuraincarcati, adica au acelasi
nume si difera prin numarul si/sau tipul parametrilor. Sau altfel supus, semnatura lor difera numai
prin numarul si/sau tipul parametrilor. De exemplu, clasa Magazin ar putea avea doi constructori:
public Magazin(String numeM, String numeP, int oraD, int oraI) { //Constructor
nrIdentificare=idCurent++ ;
denumire=numeM;
numeProprietar=numeP;
oraDeschidere=oraD;
oraInchidere=oraI;
}
public Magazin(String numeM, String numeP) { //Constructor
nrIdentificare=idCurent++ ;
denumire=numeM;
numeProprietar=numeP;
oraDeschidere=10;
oraInchidere=18;
}
this = Cuvânt cheie care se utilizează în interiorul corpului unei metode instanţă şi care furnizează
referinţa la obiectul pentru care a fost apelată metoda.
Intr-un constructor se poate apela un alt constructor al aceleaşi clase. De exemplu, al doilea
constructor ar putea fi definit astfel:
public Magazin(String numeM, String numeP) {
this(numeM, numeP, 10, 18);
}
În UML reprezentăm relaţia de instanţiere dintre o clasă şi un obiect al său ca o dependenţă

7
clasă obiect

instanţă a
Magazin Tomis: Magazin

Diagrama 1. Relaţia de instanţiere

O clasa furnizeaza un tip de date cu care putem declara variabile referinta de acest tip.
De exemplu, vom scrie o alta clasa TestMagazin care, in metoda main(), sunt create două obiecte
din clasa Magazin şi se memoreaza referinţele la ele în variabile de acelaşi tip:
Magazin m1 = new Magazin(“Tomis Mall”, “Ionescu Pop”);
Magazin m2 = new Magazin(“Constanta”, “Popescu”,“Ion”);

Accesarea variabilelor
Variabilele m1 si m2 ar putea fi folosite pentru a accesa valorile variabilelor instanţă, daca ne dau
voie. In cazul in care o variabila instanta furnizeaza acces, atunci puteam folosi operatorul punct
pentru a accesa valorile acestora.
In cazul variabilelor de clasa, daca aceastea furnizeaza acces, ele sunt modificate sau folosite cu
NumeClasa.numeVariabilaClasa.
Apelul de metodă
D. Apel de metoda = mecanism oferit de un limbaj de programare prin care o entitate din program
(in cazul nostru un obiect) cere executia unei metode. Aceasta metoda poate sa apartina obiectului
apelant (apel intern) sau, in cazul general, poate sa apartina unui alt obiect. Pentru a putea apela o
metoda M a unui obiect B, un obiect A trebuie sa aiba acces la metoda M. Accesul la o metoda este
stabilit de regulile de vizibilitate din Java.
De obicei un apel de metoda are loc in momentul in care obiectul apelant executa o metoda a sa.
Apelul metodei intrerupe executia metodei curente si trece controlul metodei apelate. La sfarsitul
executiei metodei apelate, controlul revine la metoda intrerupta, exact in punctul de intrerupere.
Daca metoda apelata restituie (intoarce) o valoare metodei apelante, aceasta valoare poate participa
la calculul unei expresii devenind un operand in acea expresie. Este cazul in care apelul de metoda
apare in interiorul unei expresii sau in locul unei expresii.
Apelul unei metode poate fi vazut si ca un mesaj pe care obiectul apelant il trimite obiectului apelat.
In acest sens putem spune ca obiectele comunică între ele prin intermediul metodelor.
Apelarea unei metode se face cu ajutorul operatorului binar “.”. Primul operand al apelului trebuie
sa fie o variabila-reference sau o expresie a carui valoare sa fie de tip reference catre un obiect in
memorie. Al doilea operand este chiar numele metodei urmat de lista parametrilor efectivi. De
exemplu, m1.getMesaj().
In cazul apelurilor de metode ale clasei (este cazul metodelor declarate static), regula de construire a
apelului este aceeasi doar ca primul operand trebuie sa fie chiar numele clasei.
Nota. Ca si in cazul variabilelor-membru, apelul unei metode apartinand unui obiect din interiorul
unei metode che apartine aceluiasi obiect se face fara a utiliza operatorul “.” si fara a utiliza
referinta la obiectul destinatie. Acest obiect este subinteles a fi obiectul curent in care ne aflam cu
executia.
De exemplu, clasa Magazin are metoda getMesaj() care intoarce un sir care indica daca magazinul
este deschis (“Este deschis!”) sau inchis (“Este inchis!”) in momentul apelarii metodei
esteDeschis().

8
2. RELATII DE ASOCIERE INTRE CLASE
Pe parcursul acestui curs vom trata problema de a gestiune a facturilor dintr-un magazin a carei
descriere este prezentata in cele ce urmeaza.

Specificatia proiectului
Magazinul Tomis emite facturi pe baza produselor vandute clientilor. Pentru a gestiona automat
facturile emise de magazin, proprietarul magazinului a decis sa cumpere de la o firma de software o
aplicatie care se realizeze acest lucru.
Cand este executata, aplicatia afiseaza fereastra principala:

Pentru a vizualiza date despre magazin, clientul (sau oricine este interesat) actioneaza butonul “Date
despre magazin”, iar aplicatia va afisa urmatoarea interfata grafica:

Pentru a realiza o vanzare, clientul sau casiera trebuie sa actioneze butonul “Comandati produse” din
ferestra principală. In acest moment, aplicatia va afisa urmatoarea interfata grafica:

Datele clientilor sunt memorate intr-un fisier numit “clienti.txt”, a.i. la urmatoarele operatii, acestia
vor fi recunoscuti. Daca este vorba de un client “vechi”, dupa introducerea numelui si prenumelui
său, si actionarea tastei Enter, adresa acestuia este citita din fisier si afisata in campul de text:
Adresa. Daca este vorba de un client nou, aplicatia atentioneaza clientul ca este nou si trebuie sa
introduca adresa.

9
Clientul nu poate efectua o vanzare, daca nu a fost apasat butonul Submit. In acel moment, in cazul
unui client nou, datele acestuia (adica, nume, prenume si adresa) vor fi memorate in fisierul
“client.txt”. In plus, butonul Adauga este activat si clientul isi poate alege produse (pe rand, dintr-o
lista de produse) si trebuie sa specifice cantitatea. Daca uită sa adauge cantitatea, atunci programul
afiseaza un mesaj de eroare. Aceasta operatie poate fi efectuata si de catre casieră, la sugestiile
clientului.
Actionarea butonului Adauga determina afisarea informatiilor despre produsul comandat: nume,
cantitate si pretul unitar al acestuia, in tabelul aflat pe interfata sistemului. Aceste informatii (numele
si pretul unitar al produsului) sunt preluate dintr-un fisier numit “produse.txt”.
Dupa ce au fost alese toate produsele ce vor fi cumparate, se actioneaza butonul Executa care
determina crearea si vizualizarea facturii, al carei continut va fi afisat in fereastra Factura.
Dupa cum se observa, atributele facturii sunt numar factura (care este unic) si data la care s-a emis
factura. Pentru fiecare articol al facturii, se afiseaza numele, pretul unitar, cantitatea comandata si
pretul articolului. La sfarsit, se face totalul (=suma pretului articolelor) din care se scade 5% din
total daca cumpararea s-a facut intr-o zi de lucru (luni-vineri), la care se apoi adauga un tva de 20%
din suma obtinuta si se obtine totalul general.
Factura poate fi tiparita la imprimanta, daca se actioneaza butonul Print, poate fi memorata intr-un
fisier (butonul PrintToFile), respectiv poate fi arhivata (butonul Arhivare). In ultimul caz, factura
memorata in fisierul “arhiva.txt” ce contine toate facturile furnizate de magazin. Operatia de vanzare
este anulata daca se actioneaza butonul Cancel.

10
Paşii de rezolvare a unei probleme

Pas 1. Identificarea conceptelor


Magazin Vanzare
Produs Interfata grafica
Proprietar Fisier
Data Vanzatoare
Factura Lista de produse
Client Program
Fereastra Articol
Buton Imprimanta
Pasul 2. Identificarea claselor prin analizarea conceptelor obtinute la pasul anterior.
Magazin Vanzare
Produs Interfata grafica
Proprietar Lista de produse
Factura Client
Articol

Pasul 3. Identificarea atributelor obiectelor claselor


Magazin: denumire, oraDeschidere, oraInchidere
Factura: numar (unic), dataEmiterii
SpecificatieProdus: denumire, pretUnitar
Client: nume, prenume, adresa
Proprietar: nume, prenume
Articol (sau ElementVanzare): specificatieProdus, cantitate
Vanzare: tva, total

Pasul 4. Construirea diagramei de clase


Pentru a putea sa construim diagrama de clase, trebuie sa identificam relatiile dintre acestea.

Relatia de asociere intre clase


In general, o legătură reprezintă un mod de a conecta, asocia sau raporta între ele două sau mai
multe entitati. Legăturile ne oferă capacitatea de a reuni elementele în ansambluri, configuraţii sau
grupuri. Putem clasifica legăturile în mai multe categorii: fizice, conceptuale, semiotice, logice şi
semantice.
Legăturile fizice, concrete între entităţi sunt identificate în lumea reală. În unele cazuri, identificarea
se realizează prin experimente fizice în urma cărora se deduce faptul că mobilitatea entităţilor
respective este limitată, prin transmiterea de forţe sau mişcări de la o entitate la alta.
Legăturile conceptuale sau mentale sunt deduse din observarea lumii reale şi aplicarea unui proces
de raţionare asupra ei. In cazul nostru, procesul de rationare este realizat prin aplicarea principiilor
paradigmei OO.
Legăturile semiotice sunt deduse din descrierea în limbaj natural a elementelor din lumea reală. Ele
se împart în legături sintactice, semantice şi morfologice (gramaticale). Dintre aceste categorii, ne
interesează numai primele două. Legăturile semantice au loc între semne şi înţelesul lor pentru cei
care le utilizează.

11
Vanzare
tva : Integer Client
areLoc adresa : String
addElementVanzare(a : ElementVanzare)
getElementeVanzare() : Collection 1..* 1
getClient() : Client getAdresa()
calculeazaTotal() : Long

estePentru

Factura
nrFactura : Integer
data : Calendar

formatare(sir : String, latime : Integer) : String

Legăturile sintactice între semne sunt induse de relaţiile dintre înţelesul semnelor ce furnizează
semantica limbajului natural folosit. Astfel avem relaţii de contrarietate (bazate pe antonime), de
similaritate (bazate pe sinonime), de generalizare (este_un) şi mereologice (constructii sintactice:
este compus din, contine, este format din, face parte din). Dintre acestea ne intereseaza numai
ultimele doua tipuri de legaturi sintactice.
Legăturile logice sunt deduse din descrierea în limbaj natural a elementelor din lumea reală, asupra
căreia aplicăm un raţionament. In cazul analizei, este vorba de descrierea problemei si specificatia
proiectului.

D. O legatura (link) in abordarea orientata spre obiecte este o conectare fizica, conceptuala,
sintactica (semantica sau sintactica) sau logica intre doua obiecte. Daca un obiect care partine unei
clase trebuie sa trimita un mesaj unui obiect ce apartine unei altei clase, atunci intre cele doua
obiecte trebuie sa existe un link. Link-ul poate fi:
- unilateral (primul obiect il cunoaste pe al doilea, dar al doilea nu)
- bilateral (cele doua obiecte se cunoasc intre ele)
D. O asociere este o relatie intre doua clase ce descrie o multime de link-uri cu o structura si o
semantica comune intre obiectele claselor implicate.
O asociere intre doua clase poate indica:
- o cale de comunicare intre obiectele celor doua clase;
- o cunoastere unilaterala sau bilaterala,
- un raport de tip client-furnizor intre obiectele celor doua clase;
- obiectele unei clase partajeaza cu alte obiecte obiectele celei de-a doua clase.
Orice clasa are implicit o relatie de asociere cu ea insasi, de aceea exista intotdeauna posibilitatea ca
un obiect sa-si trimita sieşi un mesaj.
Relatiile de asociere (ca orice tip de relatie) se identifica din analiza verbelor din descrierea
problemei sau/si specificatia de proiect sau/si raspunzand la urmatoarele intrebari:
- Un obiect trimite mesaje unui alt obiect, cerandu-i acestuia din urma sa-si modifice starea?
- Un obiect foloseste un alt obiect, fiindca are nevoie de informatii de la acesta din urma?
- Un obiect anunta un alt obiect?

12
O asociere se caracterizeaza prin urmatoarele elemente: nume, directia de folosire, multiplicitate,
numarul de obiecte care participa in relatia de asociere si roluri.
ElementVanzare SpecificatieProdus
cantitate : Integer denumire : String
pretUnitar : Integer
foloseste produs
ElementVanzare(p : SpecificatieProdus, c : Integer)
getProdus() : SpecificatieProdus * 1 SpecificatieProdus()
getCantitate() : Integer getDenumire()
calculeazaCost() : Long getPret()

Persoana
Magazin
nume : String
nrIdentificare : Integer
prenume : String
denumire : String esteCondus +proprietar
oraDeschidere : Integer * create()
oraInchidere : Integer
getNume()
getPrenume()

Nivelul de implementare
Orice asociere va fi implementata cu ajutorul unui atribut de tip clasa B, in clasa A. Numele
atributului este dat de rolul asocierii, daca acesta a fost specificat.
public class SpecificatieProdus{
private String denumire;
private int pretUnitar;

public SpecificatieProdus(String denumire, int pret){


this.denumire=denumire;
this.pretUnitar=pret;
}
public String getDenumire(){
return denumire;
}
public int getPret(){
return pretUnitar;
}
}
public class ElementVanzare{
private SpecificatieProdus produs;
private int cantitate;

public ElementVanzare(SpecificatieProdus p, int c){


produs=p;
cantitate=c;
}
public SpecificatieProdus getProdus(){
return produs;
}
public int getCantitate(){
return cantitate;
}
public long calculeazaCost(){
return produs.getPret()*cantitate;
}
}

13
public class TestArticol{
public static void main(String[] args){
ElementVanzare a1=new ElementVanzare(new SpecificatieProdus("tastatura",
1000), 3);
System.out.println(a1.calculeazaCost());
}
}

Principiile paradigmei orientarii spre obiecte


Orice aplicaţie orientată pe obiecte trebuie să fie construită folosind patru mecanisme conceptuale:
1.Abstractizare. Proces care capteaza caracteristicile esentiale ale unui obiect, caracteristici ce
diferentiaza acest obiect de toate celelalte tipuri de obiecte.
2.Incapsulare. Proces mental executat pe o abstractizare în care elementele structurale ale
abstractizarii sunt izolate de cele ce constituie comportamentul sau. Serveste la separarea
interfetei vizibile a unei abstractizari de implementarea sa. Din faptul ca clasele si obiectele
derivate din clase incapsuleaza într-un singur “pachet” datele si operatiile, deriva avantaje
importante:
• Detaliile interne de implementare si ale procedurilor sunt invizibile in exterior (information
hiding). Aceasta reduce propagarea efectelor in cazul modificarilor.
• Structurile de date si operatiile sunt definite intr-o entitate cu nume: clasa. Aceasta
faciliteaza reutilizarea componentelor.
• Interfetele intre obiecte sunt simplificate. Un obiect care trimite un mesaj nu trebuie sa se
preocupe de structurile interne de date ale obiectului destinatar. Se reduce astfel incarcarea
sistemului.
3.Modularitate. Proprietatea unei aplicaţii care a fost descompusă într-un ansamblu de module
(pachete) cu o mare coeziune interna si cu legaturi slabe intre ele.
4.Ierarhie. Este o relaţie de ordine între abstractizari care sunt tipuri abstracte de date. Există
două tipuri de ierarhii: bazate pe relaţia de generalizare/specializare şi cele bazate pe relaţia de
agregare.
Alte două concepte de importanta mai mica caracterizeaza obiectele intr-o modelare pe obiecte:
1.Typing. Este o constrângere pentru clasele de obiecte care le obliga sa schimbe între ele obiecte
de tipuri diferite sau sa le schimbe dar într-un mod controlat (este cazul polimorfismului de
exemplu). În general, polimorfismul este un concept in teoria tipurilor in care un unic
identificator poate denumi obiecte ce apartin unor clase diferite si care sunt inrudite la o
superclasa.
1. Persistenta (persistence). Reprezinta proprietatea unui obiect de a continua sa existe dupa ce
creatorul său a incetat sa mai existe sau de a fi mutat din locul în care a fost creat în altul.
Caracteristicile abordarii orientate pe obiecte
Proprietatilor obiectelor si a mecanismelor de structurare a universului OO au drept consecinte urmatoarele
caracteristici ale abordarii OO:
1. Identitate. Proprietate a unui obiect care il diferentiaza de alte obiecte. Toate obiectele au o
identitate proprie: obiectele sunt distincte intre ele.
2. Clasificare. Metoda utilizata pentru ordonarea cunostintelor despre obiecte. Cunostintele sunt
descrise în termeni de clase, instante, atribute, operatii.
3. Polimorfism. Proprietatea unei operatii de a putea fi aplicata în moduri diferite mai multor clase.
Polimorfismul este două tipuri: static (prin supraincarcarea metodelor) şi dinamic (prin
redefinirea metodelor).
4. Generalizare/Specializare. Specializarea este o relatie între clase în care o clasa (superclasa) îsi
transmite structura si functionalitatea uneia sau mai multor clase (subclase). Generalizarea este o

14
relatie intre clase in care structura si comportamentul comun a doua sau mai multor clase este
incapsulat intr-o singura clasa. Aceasta din urma va fi supraclasa pentru primele clase.
Generalizarea/specializarea reprezinta un al doilea mecanism de clasificare: obiectele se clasifica
în clase, iar clasele se clasifica prin intermediul superclaselor.

15
3. RELAŢIA DE GENERALIZARE/SPECIALIZARE ÎNTRE CLASE
Am văzut că a program spre obiecte înseamnă a raţiona în termeni de concepte. De exemplu, in
aplicatia Magazin clientul este o persoana care are o adresa.
Mai general, vom spune că Y este un tip de X care …, unde X şi Y sunt concepte. Această relaţie
particulară dintre concepte se numeşte relaţia de generalizare/specializare şi indică faptul că toate
instanţele lui Y sunt şi instanţe ale lui X, dar au ceva în plus (sau ceva specific) faţă de X. În această
relaţie, X este conceptul de bază, iar Y este conceptul derivat. Se mai spune că X este generalizarea
lui Y, iar Y este specializarea lui X.
Specializarea tipului client determina aparitia a inca 3 concepte: Angajat, Somer, Sef.
Relaţia de generalizare/specializare se păstrează şi între clasele care reprezintă cele două concepte.
În UML, această relaţie între clase este arătată astfel:
X
X se mai numeşte superclasă (sau supraclasă), iar Y subclasă.

Într-o relaţie de generalizare/specializare un obiect al unei subclase este şi obiect al supraclasei


clasei repsective, având aceeaşi structură şi aceleaşi operaţii cu cele ale obiectelor din supraclasă,
dar au ceva diferit sau în plus.
Dar, ce poate fi diferit în subclasă faţă de supraclasă? Una sau mai multe proprietăţi (variabile)
specifice subclasei respective. De exp, in urmatoarea ierarhie de clase, clasa Angajat este o subclasă

Persoana
nume : String
prenume : String

Client
adresa : String

Angajat Somer
salariu : Double procentCalcul : Integer
costOra : Double
catVechime : Integer calculeazaAjutorSomaj()

calculeazaSalariu(nrOreLucrate : Integer)

Sef
sporDeConducere : Double

16
a clasei Client şi faţă de aceasta din urmă, are următoarele variabile: salariu, costOra si catVechime.
Apoi, subclasa poate adăuga unul sau mai multe comportamente specifice sau să le modifice pe cele
moştenite din supraclasă. In cazul clasei Angajat, aceasta contine metoda calculeazaSalariu().
MOŞTENIREA ÎN JAVA
Relaţia de generalizare/specializare dintre concepte sau clase induce ideea unei moşteniri pe care o
subclasă o primeşte de la supraclasa sa. Această moştenire are două aspecte: structura obiectelor şi
modul lor de comportament; şi aproape întotdeauna în aceste situaţii, subclasele pot adăuga ceva
specific.
Pentru a reprezenta relaţia de generalizare/specializare în limbajele de programare spre obiecte,
precum Java, se propune un mecanism numit moştenire. Acest mecanism permite unei noi clase să
beneficieze de structura şi comportamentul definite într-o clasă deja existentă prin declararea că
moştenim acea clasă.
In Java, mecanismul de moştenire se numeşte extensia clasei existente şi cere subclasei să declare ce
clasă extinde. Apoi, subclasa respectivă trebuie să definească propriile variabile şi metode.
Declararea că o clasă extinde o altă clasă este indicată în Java prin cuvântul cheie extends în
declaraţia clasei respective:
class Identificator extends NumeSuperClasa {
//definim variabile şi metode specifice subclasei
}
unde Identificator este numele subclasei, iar NumeSuperClasa este superclasa sa.
Regulă. În Java, o clasă poate extinde o singură clasă. Aşadar, moştenirea este simplă.
În aplicaţia noastră, clasa Manager este o subclasă a clasei Angajat. Iată structura celor două clase:
public class Persoana{
private String nume, prenume;
public String getNume(){return nume;}
public String getPrenume(){return prenume;}
}

public class Client extends Persoana{


private String adresa;
public void setAdresa(String n){adresa=n; }
public String getAdresa(){return adresa; }
}

public class Angajat extends Client{


private double costOra, salariu;
private int catVechime;

public void setCostOra(double c){ costOra=c;}

public void setCatVechime(int c){ catVechime=c;}

public void calculeazaSalariu(int nr){


salariu=nr*costOra;
if(catVechime<3) salariu+=5*salariu/100;
else if(3<=catVechime && catVechime<7) salariu+=7*salariu/100;
else if (7<=catVechime && catVechime<10) salariu+=10*salariu/100;
else salariu+=15*salariu/100;
}
public double getSalariu(){return salariu;}
}
public class Somer extends Client{

17
private int procentDeCalcul;

public void setProcent(int p){procentDeCalcul=p;}

public double calculeazaAjutor(double salariuMediu){


return salariuMediu*procentDeCalcul/100.0;
}
}
Vizibilitatea membrilor în subclase
Variabilele instanţă private ale unei supraclase nu sunt moştenite în subclasele ei.
De aici putem concluziona că:
O subclasă moşteneste toate variabilele şi metodele neprivate ale supraclasei pe
care o extinde.
Rămâne posibilitatea accesării variabile private prin intermediul metodelor set şi get.
Redefinirea membrilor în subclase
O subclasă care doreste sa se deosebeasca de superclasa sa (pentru ca altfel ce rost ar avea sa o
introducem?) are două posibilităţi:
- să adauge membri noi si/sau
- să redefinească membrii superclasei.
Dacă o metodă este redefinită în subclasă, trebuie să îndeplinească următoarele condiţii:
- metoda redefinită trebuie sa aibă exact aceeaşi semnătură;
- metoda redefinită trebuie să aibă acelaşi tip al rezultatului întors;
- dacă metoda redefinită îşi schimbă modificatorul de acces, acesta trebuie să dea cel puţin la fel
de mult acces ca metoda moştenită din supraclasă.
Exemplu.
super = Cuvânt cheie care se utilizează în interiorul corpului unei metode a unei instanţe a unei clase
pentru a indica obiectul superclasei ce intra in structura instantei curente.
Nota. Un obiect al unei subclase este alocat in memorie intr-o zona care include atat spatiul
alocat obiectului superclasei, cat si spatiul necesar variabilelor declarate in subclasa.
Variabila salariu este protected in Angajat.
public class Sef extends Angajat{
private double sporConducere;

public void setSporConducere(double s){ sporConducere=s;}

public void calculeazaSalariu(int nr){


salariu=super.calculeazaSalariu(nr);
salariu+=sporConducere;
}
}
În cazul variabilelor instanţă, redefinirea este folosită mai puţin şi este vorba de fapt de ascunderea
variabilelor din supraclase.
Ierarhia claselor Java
În Java, clasele formează o structură de arbore în care rădăcina este clasa Object, a cărei definiţie
apare în pachetul java.lang. Orice clasă extinde direct (implicit sau explicit) sau indirect clasa
Object. Aşadar, toate clasele definite de programator care nu extind o altă clasă sunt automat
subclase ale clasei Object.
Dacă doreşte, o clasă poate redefini una sau mai multe metode moştenite din clasa Object.
Structura clasei Object este următoarea:

18
public class Object {
public java.lang.Object();
public java.lang.String toString();
protected native java.lang.Object clone() throws CloneNotSupportedException;
public boolean equals(java.lang.Object);
public native int hashCode();
protected void finalize() throws Throwable;
public final native java.lang.Class getClass();
public final native void notify();
public final native void notifyAll();
public final void wait() throws InterruptedException;
public final native void wait(long) throws InterruptedException;
public final native void wait(long, int) throws InterruptedException;
}
Datorită ierarhiei claselor Java, metodele clasei Object sunt moştenite de toate clasele Java. În
consecinţă, oricare din aceste metode poate fi apelată asupra oricărui obiect.
Cunoaştem deja unele metode din clasa Object, cum ar fi de exemplu toString() sau equals().
Metoda toString() furnizează o “reprezentare” sub formă de şir a unui obiect. Dacă această metodă
nu este redefinită, atunci va fi afişată numele clasei din care face parte obiectul respectiv împreună
cu un şir ce reprezinta codul hash al obiectului în memoria heap. De aceea, oridecâte ori se doreşte
afişarea unor informaţii despre un obiect (cum ar fi starea obiectului) se va rescrie metoda
toString().
Metoda equals() permite compararea oricăror două obiecte. Rezultatul este true numai dacă cele
două obiecte sunt de fapt acelaşi obiect. Dar, se poate redefi această metodă dacă dorim ca metoda
să realizeze un alt tip de comparare. De exemplu, clasa String redefineşte această metodă pentru a
compara lexicografic şirurile conţinute în cele două obiecte care se “compară”.
Constructori şi moştenirea
După cum ştim, constructorii încapsulează algoritmi de iniţializare a obiectelor ce vor fi create.
Când este vorba de un obiect al unei subclase este important să ştim cum va fi iniţializată partea
obiectului ce aparţine structurii supraclasei, mai ales când variabilele instanţă ale supraclasei nu sunt
moştenite, deoarece sunt private.
Presupunem că clasa Persoana contine următorul constructor:
public Persoana(String n, String prenume){
nume=n;
this.prenume=prenume;
}
Datorită observaţiei anterioare, nu putem scrie un constructor în clasa Client care să iniţializeze
direct variabilele nume şi prenume.
O soluţie ar fi să apelăm metode setNume() şi setPrenume() care sunt moştenite de clasa Client şi
am avea următorul constructor:
public Client (String nume, String prenume, String adresa){
setNume(nume);
setPrenume(prenume);
this.adresa=adresa;
}
Codul este corect, dar la compilare vom avea eroare: nu cunoaşte constructorul fără parametri al
supraclasei Persoana. Chiar dacă schimbăm modificatorul de vizibilitate al variabilelor nume şi
prenume, vom avea aceeaşi eroare de compilare.
O soluţie bună ar fi să folosim cuvântul cheie super şi apelăm constructorul cu doi parametri din
clasa Persoana:
public Client (String nume, String prenume, String adresa){

19
super(nume, prenume);
this.adresa=adresa;
}
Regulă. Dacă intr-un constructor al unei subclase se apelează un constructor al
supraclasei sale, atunci acest apel (folosindu-se super) se face pe prima linie a
corpului constrcutorului respectiv.
Mecanismul de iniţializare a obiectelor
Apelul unui constructor nu este un apel al unei metode oarecare, deoarece înainte de a executa
algoritmul respectiv, mediul Java apelează automat constructorul fără parametri al supraclasei dacă
acesta poate fi apelat. Dacă supraclasa nu are un constructor fără parametri, atunci caută
constructorul fără parametri al supraclasei clasei respectiv. Şi tot aşa, până când clasa Object este
supraclasa clasei respective. Aceasta are un singur constructor, care nu are parametri. Cu această
clasă este creat obiectul şi apoi este iniţializat pe drumul invers al apelului cu executarea
Clasa
Constructor Object() Object

5: initializeaza obiect
4: Object()

…. Clasa A
Constructor 6: algoritmul lui A()
predefinit A() 2: A()

…. Clasa X
Constructor X(…)
1: X(…) 7: algoritmul lui X(…)

new X(…)

Crearea unui obiect în Java


algoritmilor din constructorii supraclaselor până la clasa respectivă (Figura 6-2).
Aşadar avem două soluţii:
- folosim super şi apelăm un anumit constructor din supraclasă
- în supraclasa Angajat introducem încă un constructor, fără parametri.
Utilizarea obiectelor ale subclaselor
Având clasa Sef definită ca subclasă a clasei Angajat, putem crea obiecte ale clasei Sef şi apela
metodele lor astfel:
Sef s1=new Sef("Ionescu", „Pop”, „Aleea rozelor”, 7);//constructorul
s1.setSporConducere(100);//metoda instanta definita in clasa Sef
s1.setCatVechime(25);//metoda mostenita din Angajat
s1.calculeazaSalariu(200);//se apeleaza metoda definita in clasa Sef
Apelurile de metode sunt în regulă pentru că un obiect al clasei Sef este automat un obiect al clasei
Angajat şi din această cauză pot fi apelate metodele moştenite din supraclasa Angajat.
Din acelaşi motiv, putem memora într-o variabilă de tip Angajat o referinţă la un obiect al clasei
Sef:

20
Angajat s2=new Sef("Marinescu", „Marin”, „Aleea stejarilor”, 10);
s2.setNume("Georgescu");//metoda mostenita din Persoana
s2.calculeazaSalariu(160);//se apeleaza metoda definita in clasa Sef
s2.setSporConducere(150);//eroare la compilare!
După cum se observă, dacă apelăm o metodă ce aparţine numai clasei Sef şi nu clasei Angajat,
compilatorul va genera o eroare pentru că variabila s2 a fost declarată de tip Angajat, adică de tip
supraclasă.
Regulă. Dacă într-o variabilă de tip supraclasa unei clase va fi memorată o
referinţă la un obiect al subclasei respective, atunci obiectul accesat prin
intermediul varibilei referinţă, pierde temporar specificul subclasei,
comportându-se ca un obiect al supraclasei.
Atunci, dacă avem o variabilă de tip supraclasă nu suntem siguri că obiectul referit de variabilă este
de tip supraclasă. În acest caz, putem folosi operatorul instanceof pentru a accesa variabilele şi
metodele obiectului referit de variabilă.
De exemplu, expresia s2 instanceof Sef are valoarea true deoarece referinţa memorată în
variabila s2 indică un obiect al clasei Sef. Altfel, valoarea expresiei ar fi fost false.
Aşadar, dacă vrem să folosim pe deplin obiectul respectiv (adică, conform clasei din care face parte)
folosim operatorul instanceof împreună cu operatorul cast la clasa originală:
if (s2 instanceof Sef){
Sef s3=(Sef) s2;

}
Mecanismul apelarii metodelor într-o ierarhie de clase
Am văzut că în cazul moştenirii, un obiect al unei subclase poate fi folosit pentru a apela metode
definite în clasa respectivă, dar şi metode definite în supraclasa sa directă sau în supraclasele sale
îndirecte şi moştenite de clasa sa. În plus, subclasele pot redefini aceeaşi metodă de mai multe ori, în
funcţie de propriile necesităţi. Atunci ar trebui să vedem cum va fi rezolvat un apel de metodă
aplicat unui obiect dintr-o subclasă. Astfel presupunem că un obiect a apelează o metodă a
obiectului b din Clasa5 din figura următoare. După cum se observă avem două cazuri:
- metoda respectivă este moştenită din clasa Clasa1 şi nu a fost redefinită în ierarhia sa directă:
Clasa5 şi Clasa3. Atunci metoda care va fi executată va fi metoda din Clasa1 (Figura 6-3 a).

- metoda este definită iniţial în clasa Clasa1 şi redefinită în Clasa3. Atunci metoda care va fi
executată, va fi metoda din Clasa3.

21
Definiţia metodei
Definiţia iniţială a metodei

Clasa1 Clasa1

Metoda
4 redefinită

Clasa2 Clasa3
Clasa2 Clasa3
3 3

Clasa4 Clasa5 Clasa5


Clasa4

2 2

Obiect a 1 Obiect b
Obiect a 1 Obiect
Obiect b

Observăm că, apelul unei metode a unui obiect aparţinând unei subclase, dacă metoda apelată nu
este definită in subclasă, este transmis în sus în ierarhia de clase până când este găsită o primă
definiţie a metodei apelate.

22
4. ARRAY-URI
Array = O colecţie liniară de elemente în care fiecare element este accesibil prin intermediul unui
indice.
Un array furnizează spaţiu pentru valorile de tip primitiv sau referinţă la obiectele de un acelaşi tip.
Aceste valori se numesc elementele array-ului. Elementele unui array sunt ordonate dupa unul sau
mai multe criterii numite dimensiuni. O dimensiune a unui array este indicata printr-o pereche de [].
Dupa numarul de dimensiuni, un array este uni- sau multidimensional.
Referinţa la un array va fi memorată într-o variabilă array. Sintaxa declararii unei variabile array
unidimensional este urmatoarea:
TipDeDate[] id; sau TipDeDate id[];
De exemplu, putem declara două variabile array numere şi personal astfel:
int[] numere;
Persoana personal[];
Nota. Dupa executarea acestor declaratii, array-urile propriu-zise nu exista in memorie, ci numai
variabilele array asociate.
Pentru a exista într-un program, un array trebuie să fie creat (alocat) cu new. Sintaxa alocarii
memoriei unui array este:
id = new tip[expresie]
unde tip este tipul de date al variabilei array cu identificatorul id, iar expresie este fie un
literal intreg, fie o expresie cu valoare dreapta un numar intreg. De exemplu,
numere = new int[5];
personal = new Persoana[1000];
In acest moment, toate elementele array-ului numere sunt initializate cu 0, iar cele ale array-ului
personal cu valoarea null.
După creare, elementele array-ului se pot umple cu valori, cu observatia ca indicele unui array
începe de la 0. Astfel un array cu 10 elemente va avea indicele de la 0 la 9. De exemplu, daca array-
ul numere contine primele 5 numere pare naturale, umplerea array-ul cu aceste valori se face astfel:
for (int i=0; i<5;i++) numere[i]=2*i;
In cazul array-ului de tip referinta personal, valorile lui sunt referinte catre obiecte de tip
Persoana, care trebuia sa fie create. Astfel,
for (int i=0; i<1000;i++) personal[i]=new Persoana();
Grafic, cele doua array-uri arata astfel:
array-ul personal
… obiecte
array-ul numere Persoana
0 2 4 6 8 0 1 2 … 999
0 array unidimensional
1 2 de 53întregi 4 array unidimensional de 1000 obiecte Persoana

Prin definitie, lungimea unui array este egala cu numărul de elemente ale array-ului. Pentru a
determina lungimea unui array se foloseste variabila length. De exemplu, valoarea expresiei
personal.length este 1000.
INIŢIALIZAREA UNUI ARRAY
Am vazut ca la momentul creării unui array, elementelor sale le sunt atribuite valoarea zero (tipuri
primitive numerice), \u0000 (tipul char), false (tipul boolean) sau null (tipuri referinţă).
Elementele unui array pot fi initializate in declararea variabilei array printr-o lista de initializatori
cuprinsa intre {} ce le contine si care este atribuita variabilei array asociata. Sintaxa este urmatoarea:
TipDeDate[] id={listaDeInitializatori}

23
Aceasta este de fapt o forma prescurtata pentru
TipDeDate[] id=new TipDedate[]{listaDeInitializatori};
unde listaInitializatori este o multime de elemente de acelasi tip cu tipul array-ului, ce sunt valorile
elementelor array-ului id.
Numarul de elemente din lista de initializatori funrizeaza valoarea variabilei length pe variabila
array asociata.
De exemplu, elementele array-ul indicat prin variabila numere pot fi initializate de la declararea
variabilei numere astfel:
int[] numere={0,2,4,6,8};
ARRAY-URI MULTIDIMENSIONALE
Array-urile multidimensionale sunt declarate prin adaugarea dimensiunilor. De exemplu, declararea
unei matrici m cu elemente de tip intreg se realizeaza astfel:
int[][] m;
Alocarea memoriei necesare memorarii matricii m se face astfel:
m=new[3][3];
In memorie, elementele lui m nu vor fi memorate ca intr-o matrice ci liniar adica, un array
multidimensional este memorat ca un array de array-uri (si la randul lor pot fi de array-uri), astfel ca
fiecare dimensiune este memorata ca un array diferit. De aceea, putem declara si construi array-uri
multidimensionale de dimensiuni diferite. De exemplu,
int[][] b=new int[2][]; //b are 2 linii
b[0]=new int[3];// prima linie are 3 coloane sau elemente
b[1]=new int[4];// a doua linie are 4 elemente
Nota: Daca numarul coloanelor poate sa difere, tipul elementelor trebuie sa fie acelasi.
In cazul array-urilor multidimensionale variabila length are valori diferite. De exemplu,
b.length furnizeaza numarul de linii ale lui b, adică 2, pe cand b[1].length=4.
Similar, un array multidimensional poate fi initializat si la declarare folosind o listă ce contine un
numar de liste de initializatori egal cu numarul de dimensiuni ale array-ului, liste separate prin
virgula. De exemplu, int[][] a={{1,0}, {0,1}} memoreaza matricea unitate bidimensionala.
Exemplu. Următorul exemplu arată cum pot fi create, iniţializate şi afişate diferite array-uri
multidimesionale:
import java.util.*;
public class ArrayMultiDimensionale{
static Random aleator= new Random();
static int pRand(int mod) {
return Math.abs(aleator.nextInt())%mod;
}
public static void main(String[] args){
//array 3-D cu dimensiuni fixe:
int[][][] a2 = new int[2][2][4];
for(int i = 0; i < a2.length; i++)
for(int j = 0; j< a2[i].length; j++)
for(int k=0; k<a2[i][j].length;k++)
afiseaza("a2[” + i+"]["+j+"]["+k+"] = " + a2[i][j][k]);
//array 3-D cu vectori de lungime diferită pentru cele trei dimensiuni
int[][][] a3 = new int[pRand(7)][][];
for(int i = 0; i<a3.length; i++){
a3[i] = new int[pRand(5)][];
for(int j=0; j<a3[i].length; j++)a3[i][j] = new int[pRand(5)];
}
for(int i=0; i<a3.length; i++)
for(int j=0; j<a3[i].length; j++)
for(int k=0; k<a3[i][j].length; k++) afiseaza("a3["+i+ "]["+j+"]["+k+"]= "

24
+a3[i][j][k]);
//array 3x2 de obiecte Integer
Integer[][] a4={{new Integer(1), new Integer(2)},
{new Integer(3), new Integer(4)},
{new Integer(5), new Integer(6)}};
for(int i = 0; i < a4.length; i++) for(int j = 0; j < a4[i].length; j++)
afiseaza("a4[" + i + "][" + j + "] = “ + a4[i][j]);
}
static void afiseaza(String s){
System.out.println(s);
}}
LUCRUL CU ARRAY-URI
Cand lucram cu array-uri putem folosi urmatoarea metodă statică a clasei System ce permite
copierea unui număr de elemente consecutive ale unui array într-un alt array:
static void arrraycopy(arraySursă, pozSursă, arrayDest, pozDest, nrElemente)
In mod obisnuit, array-urile sunt folosite in probleme de sortare si de cautare. De exemplu, sortarea
elementelor unui array folosind metoda QuickSort se face astfel in Java:
public class Ordonare{
static int[] a;
public static void initializeaza(){
a=new int[20];
int nr;
for(int i=0; i<a.length;i++){
nr=(int)(Math.random()*30);
a[i]=nr;
}
afiseaza();
}
public static void quickSort(int p, int u){
int i=p, j=u;
int temp=a[(p+u)/2];
do{
while(a[i]<temp)i++;
while(a[j]>temp)j--;
if (i<j){
int t=a[i];
a[i]=a[j];
a[j]=t;
}
if (i<=j) {i++;j--;}
}while (i<j);
if(p<j) quickSort(p,j);
if(i<u) quickSort(i,u);
}

public static void afiseaza(){


for(int i=0;i<a.length;i++) System.out.print(a[i]+" ");
System.out.println();
}
public static void main(String[] args){
initializeaza();
quickSort(0,19);
afiseaza();
}
}
Cautarea unei valori intr-un array folosind metoda Divide et Impera se realizeaza astfel:

25
public class Cautare{
static int[] a;
public static void initializeaza(){
a=new int[20];
int nr;
for(int i=0; i<a.length;i++){
nr=(int)(Math.random()*30);
a[i]=nr;
}
afiseaza();
}
public static boolean cauta(int val){
int p=0, u=a.length-1, m=(p+u)/2;
while(p<u && val!=a[m]){
if(val<a[m]) u=m-1;
else p=m+1;
m=(p+u)/2;
}
return a[m]==val;
}

public static void afiseaza(){


for(int i=0;i<a.length;i++) System.out.print(a[i]+" ");
System.out.println();
}

public static void main(String[] args){


initializeaza();
int nr=(int)(Math.random()*30);
if(cauta(nr)) System.out.println("A gasit="+nr);
else System.out.println("Nu a gasit="+nr);
}
}
ARRAY ÎN METODE
Daca parametrul unei metode este o variabila array, atunci transferul se face prin referinta, adica
orice modificare efectuata asupra elementelor array-ului respectiv se memoreaza. Elementele unui
array de tip primitiv se transmit prin valoare, exact ca niste variabile simple. De exemplu, daca in
programul Ordonare anterior am fi avut metoda
public static void schimba(int i, int j){
int t=i;
i=j;
j=t;
}
apelul acestei metode schimba(a[i], a[j]) nu ar fi realizat operatia respectiva. Pentru a
realiza într-adevăr schimbarea două valori transmise prin valoare, metoda schimba() trebuie să fie
scrisă astfel:
public static int[] schimba(int i, int j){
Integer n1=new Integer(i);
Integer n2=new Integer(j);
int[] n=new int[2];
Integer n3=n1;
n1=n2;
n2=n3;

26
n[0]=n1.intValue();
n[1]=n2.intValue();
return n;
}
In acest caz, metoda quickSort ar putea apela metoda schimba() astfel:
public static void quickSort(int p, int u){
int i=p, j=u;
int temp=a[(p+u)/2];
do{
while(a[i]<temp)i++;
while(a[j]>temp)j--;
if (i<j){
int[] n=schimba(a[i],a[j]);
a[i]=n[0];
a[j]=n[1];
}
if (i<=j) {i++;j--;}
}while (i<j);
if(p<j) quickSort(p,j);
if(i<u) quickSort(i,u);
}
Lucrul cu colectii de obiecte legate intre ele prin relatia de moştenire
Presupunem ca vrem sa completam programul Angajaţi cu o clasă de test a cărei metodă main
contine un array de doi angajaţi pentru a memora un angajat şi un manager. Apoi, afişăm date despre
cei doi angajaţi, după care le mărim salariul cu 2% şi le afişăm din nou datele pentru a verifica
creşterea salariului.
public class TestAngajati {
static Angajat[] personal;
public static void main(String[] args) {
....
// creaza si memoreaza 1 angajat si un manager ca personal al intreprinderii
// afiseaza date despre fiecare membru al personalului
// creste salariul cu 2%
// afiseaza date despre fiecare membru al personalului
}
}
Cum afişăm de două ori date despre angajaţi, ar trebui să scriem algoritmul de afişare într-o metoda
statică şi s-o apelăm din metoda main, când avem nevoie.
public static void afiseazaDate() {
System.out.println("\nLISTA PERSONAL");
for (int i = 0; i < personal.length; i++) System.out.println(personal[i]);
}
public static void main(String[] args) {
personal= new Angajat[2];
personal[0] = new Angajat(" ", 2500.00);
personal[1] = new Manager(" ", 3000.00, " ");
afiseazaDate();
for (int i = 0; i < personal.length; i++) personal[i].maresteSalariu(0.02);
afiseazaDate();
}
În metoda main, am utilizat o variabilă array personal pentru a crea un array de doi angajaţi, dintre
care unul este Manager. Putem să facem acest lucru, datorită relaţiei de generalizare/specializare
dintre Angajat şi Manager.

27
5. CLASE ABSTRACTE
O clasa abstractă este o clasă fără instanţe directe, dar are subclase care sunt clase concrete, adică
pot fi instanţiate direct. O clasă abstractă este declarată cu modificatorul abstract.
Sintaxa declarării unei clase abstracte:
ClasăAbstractă ::= [modificator de vizibilitate] abstract class Identificator{…}
unde Identificator este numele clasei abstracte.
Fiind fără instanţe directe, constructorul unei clase abstracte nu poate fi apelat. De aceea, chiar dacă
o clasă abstractă conţine unul sau mai mulţi constrcutori, dacă incercăm să-I apelăm vom avea
eroare de compilare.
În schimb, putem declara variabile de tip o supraclasă abstractă care să conţină referinţe la obiectele
subclaselor concrete ale clasei respective. Aşadar, rolul unei clase abstracte este de a facilita
elaborările polimorfice asupra obiectelor subclaselor sale. Atunci, metodele abstracte dintr-o clasa
abstractă oferă o interfaţă comună tuturor subclaselor clasei abstracte.
O clasă abstractă trebuie să conţină cel puţin o metodă abstractă adică metodă care nu defineşte
algoritmul de implementare.
Sintaxa declarării unei metode abstracte este următoarea:
MetodaAbstractă::=[Modificator de vizibilitate] abstract Tip numeMetoda([parametri formali]);
Exemple:
abstract void metoda();
public abstract int read() throws IOException;
Aşadar, o clasă abstractă poate avea metode concrete, adică declarate şi definite în clasa respectivă.
În plus, o clasă abstractă poate avea variabile ce pot fi moştenite de subclasele clasei respective.
Cum comportamentul obiectelor unei clase este dat de mulţimea tuturor metodelor clasei, atunci
obiectele unei clase abstracte au un comportament parţial definit, deoarece el diferă în funcţie de
fiecare subclasă a clasei respective.
O subclasă a unei clase abstracte are obligaţia de a defini (implementa) metodele abstracte ale
supraclasei. În caz contrar, subclasa respectivă trebuie să fie declarată abstractă şi este
responsabilitatea subclaselor ei să implementeze metodele care au rămas abstracte din supraclasă.
5.1.1 Dezvoltarea unei aplicatii pe obiecte
Aplicaţia Cont bancar
Se consideră definirea unei clase care descrie comportamentul unui cont bancar.
1. Definirea numelui clasei: ContBancar.
2. Determinarea proprietăţilor clasei, care caracterizează obiectele clasei. Se inserează variabilele:
sold de tip long şi proprietar de tip String.
3. Înţelegerea modului în care se comportă obiectele clasei. Care sunt operaţiile care se pot face
asupra unui cont bancar?
Responsabilitatea clasei ContBancar:
Depune o sumă de bani ⇒ depune(suma:long)
Scoate o sumă de bani ⇒ scoate(suma:long)
Cere soldul curent ⇒ getSold():long
Cere numele proprietarului ⇒ getProprietar():String
4. Definirea modurilor in care vor fi create obiecte ContBancar.
Un cont bancar poate fi creat vid, numai cu numele proprietarului ⇒ ContBancar(numeProp: String)

28
Un cont bancar poate fi creat iniţializând soldul şi numele proprietarului ⇒ ContBancar(sold: Long
sold, numeProp:String)
Un cont bancar este de două tipuri: cont de economii sau cont curent.
Un cont de economii este un cont bancar caracterizat de o dobândă ce poate fi vizualizată la cerere.
Dobânda este utilizată la anumite intervale de timp, când trebuie să fie calculată şi adăugată la soldul
curent. Această operaţie este realizată de metoda aplicăDobânda().
Un cont curent este un cont bancar caracterizat de un număr de operaţii sau tranzacţii efectuate
asupra contului într-o anumită perioadă de timp.Fiecare tranzacţie efectuată are un cost constant
exprimat în euro. Costul unei tranzacţii este constant, este iniţializat la crearea unui cont curent şi
memorat în constanta COST_TRANZACTIE. Un cont curent are un număr de tranzacţii gratuite.
Acest număr este memorat în constanta NR_TRANZACTII_GRATUITE, iniţializată la crearea
contului curent. După efectuarea acestui număr de tranzacţii, restul tranzacţiilor trebuie să fie plătit.
La anumite intervale de timp, costul tranzacţiilor va fi extras din cont şi numărul tranzacţiilor
porneşte de la zero.
Orice persoană care vrea să-şi deschidă un cont bancar trebuie să specifice tipul contului: de
economii sau curent. Tinând cont de această cerinţă, clasa ConBancar este o clasă abstractă cu
metodele abstracte depune şi scoate.
După ce am analizat problema, obţinem următoarea diagramă de clase în UML:

ContBancar
sold : Long
proprietar : String

ContBancar(numeProp : String)
ContBancar(sold : Long, numeProp : String)
depune(suma : Long)
scoate(suma : Long)
getSold() : Long
getProprietar() : String

ContEconomii ContCurent
rataDobanda : Double nrTranzactii : Integer
NR_TRANZACTII_GRATIS : Integer
ContEconomii(prop : String, rata : Double) COST_TRANZACTII : Integer
ContEconomii(prop : String, sold : Long, rata : Double)
aplicaDobanda() ContCurent(prop : String, suma : Long, ntg : Integer, ct : Integer)
ContCurent(prop : String, ntg : Integer, ct : Integer)
descarcaCheltuieli()

Implementarea în Java
public abstract class ContBancar{
protected long sold;
protected String proprietar;

public ContBancar(String numeProp){


proprietar=numeProp;
}
public ContBancar(long sold, String numeProp){
this.sold=sold;
proprietar=numeProp;
}
public abstract void depune(long suma);

29
public abstract void scoate(long suma);

public long getSold(){


return sold;
}
public String getProprietar(){
return proprietar;
}
public String toString(){
return "Cont cu proprietarul: "+proprietar+" si suma curenta: "+sold;

}
}
public class ContEconomii extends ContBancar{
private double rataDobanda;

public ContEconomii(String prop, double rata){


super(prop);
rataDobanda=rata;
}
public ContEconomii(String prop, long sold, double rata){
super(sold, prop);
rataDobanda=rata;
}
public void depune(long suma){
sold+=suma;
}
public void scoate(long suma){
if (suma<=sold) sold-=suma;
}
public void aplicaDobanda(){
sold+=sold*rataDobanda;
}
public String toString(){
return super.toString()+", dobanda: "+rataDobanda;
}
}
public class ContCurent extends ContBancar{
private int nrTranzactii;
private final int NR_TRANZACTII_GRATIS;
private final int COST_TRANZACTIE;

public ContCurent(String prop, long suma, int ntg, int ct){


super(suma, prop);
NR_TRANZACTII_GRATIS=ntg;
COST_TRANZACTIE=ct;
}
public ContCurent(String prop, int ntg, int ct){
this(prop, 0, ntg, ct);
}
public void depune(long s){
sold+=s;
nrTranzactii++;
}
public void scoate(long s){

30
if(s<=sold){
sold-=s;
nrTranzactii++;
}
}
private void descarcaCheltuieli(){
if (nrTranzactii>NR_TRANZACTII_GRATIS){
scoate((nrTranzactii-NR_TRANZACTII_GRATIS)*COST_TRANZACTIE);
nrTranzactii=0;
}
}

public String toString(){


return super.toString()+", numar de tranzactii gratis: "+
NR_TRANZACTII_GRATIS+", cost tranzactie: "+COST_TRANZACTIE;
}
}
Pentru a testa ierarhia de clase construită vom scrie clasa TestContBancar care crează un cont de
economii şi un cont curent, executăm 2-3 tranzacţii pentru fiecare cont şi vizualizăm situaţia, la
început şi după efectuarea tranzacţiilor.
public class TestContBancar{
public static void main(String[] args){
ContEconomii ce=new ContEconomii("Ionescu",10000, 0.15);
ContCurent cc=new ContCurent("Popescu", 20000, 5, 2000);
ce.depune(5000);
System.out.println(ce);
cc.depune(15000);
System.out.println(cc);
cc.scoate(2000);
System.out.println(cc);
ce.scoate(12000);
System.out.println(ce);
}
}

31
6. INTERFETE
Conceptul de interfaţă
Presupunem că avem următoarele două probleme:
1. “Să se ordoneze conturile de economii după dobânda pentru a decide care dintre ele este mai bun
pentru a depune o sumă de bani disponbila.”
2. “Să se ordoneze angajaţii unei întreprinderi după salariu pentru a putea face o statistică legată de
taxele de plătit.”
Pentru rezolvarea acestor probleme avem două soluţii:
1. ContEconomii sau Angajat au obiecte care nu sunt comparabile. Ordonarea se poate face în
mod personalizat, pentru fiecare clasă.
2. Cele două clase implementează interfaţa Comparable care are o metodă
compareTo(Object). Fiecare clasă trebuie să implementeze în mod personalizat metoda:
public class ContEconomii implements Comparable {
. . . .
public in compareTo(Object o) {
return (this.dobanda > o.dobanda)?1:(this.dobanda < o.dobanda)?-1:0;
}
}
Atunci, se poate scrie simplu, având două conturi cont122 şi cont304:
if(cont122.compareTo(cont304)>0) then . . .
Interfeţe în Java
Interfaţă = Listă de metode publice şi abstracte.
O interfaţă este o colecţie de comportamente abstracte care pot fi implementate de o clasă pentru
adăugarea unui comportament care nu este furnizat de superclasa clasei respective.
Interfeţele reprezintă, ca şi clasele, tipuri de date, numai că aceste tipuri, în loc să prezinte
date+servicii, prezintă numai servicii.
Interfaţa în Java este o entitate distinctă de clase şi array-uri.
O interfaţă poate conţine şi membri-date constante.
O interfaţă poate moşteni de la o altă interfaţă.
Interfeţele în Java trebuie să fie declarate.
Declaraţia unei interfeţe:
[Modificator] interface Identificator [extends NumeInterfata...] {
Tip Identificator= . . . // date-membru
[Modificator] SemnaturaMetoda; . . . // metode abstracte
}
Exemplu. public interface Comparable {public int compareTo (Comparable b);}
Pentru fiecare declaraţie de interfaţă compilatorul crează un fişier .class.
Implementarea unei interfeţe
O clasă care decide să implementeze o interfaţă trebuie să declare acest lucru în definiţie. Sintaxa:
class Identificator implements NumeInterfata,...{
CorpClasa
}
O clasă care doreşte să implementeze o interfaţă, trebuie să implementeze toate metodele interfeţei.
Exemplu. Dacă vrem sa ordonăm angajaţii unei întreprinderi după salariu pentru a putea face o
statistică legată de taxele de plătit, clasa Angajat va implementa interfaţa Comparable care are
o metodă compareTo(Object). Clasa trebuie să implementeze în mod personalizat metoda:
public class Angajat implements Comparable {
. . . .

32
public int compareTo(Object o) {
return (int)(getSalariu() – ((Angajat)o).getSalariu());
}
. . . .
}
O clasă poate implementa mai multe interfeţe. O interfaţă poate fi implementată de mai multe clase.
Variabile-interfaţă
Intr-un program se pot declara variabile de tip interfaţă, ca de exemplu: Comparable s;
O variabilă interfaţă poate memora referinţa la o instanţă a oricărei clase care implementează acea
interfaţă, la fel cum face şi o variabilă a unei clase.
s = new Angajat(. . . );
Angajat d = new Angajat(. . .);
O variabilă interfaţă poate participa în calcule la fel ca orice variabilă referinţă.
if (s.compareTo(d) < 0) . . .
Variabilele interfaţă sunt în special utilizate ca parametri în metode pentru a permite upcasting al
obiectelor ce provin din clase diferite între ele.
if (s.compareTo(d) < 0) . . //Angajat promovat ca tip Comparable
O variabilă interfaţă poate fi accesată numai cu metodele interfeţei.
String nume = s.getNume(); // Eroare!!
Cum utilizăm interfeţele
1. Utilizaţi o interfaţă pentru a separa un comportament care este implementat în orice clase-
furnizor ale claselor client care le utilizează.
2. Utilizaţi interfeţele ca parametri ai metodelor. În acest caz:
2.1. în interiorul unei astfel de metode putem invoca orice metodă a interfeţei;
2.2. la execuţie, se poate transmite ca argument orice obiect al unei clase care implementează
interfaţa
2.3. la invocarea unei metode pentru acel obiect determină executarea unei implementări
particulare a metodei ce aparţine clasei obiectului;
2.4. transmiţând unui alt apel un obiect al unei alte clase care implementează interfaţa se
detrmină o execuţie care poate fi complet diferită de prima.
Interfeţe Java standard
Interfaţa Cloneable trebuie să fie implementată de toate clasele care se clonează.
protected native Object clone() //clone restituie un Object generic
throws CloneNotSupportedException;
. . . . . .
Vector v = new Vector();
Vector v2; //Pentru a-l putea utiliza, rezultatul
v2 = (Vector) v.clone(); // va fi convertit la tipul dorit
Interfaţa Serializable nu are metode:
package java.io;
public interface Serializable {};
Exemplu. In cazul proiectului nostru, introducem interfaţa OperatiiObiecte ce va fi realizată de către
clasele OperatiiSpecificatiiProdus si OperatiiClient.
public interface OperatiiObiecte{
public void scrieObiect(Object o);
public Object citesteObiect(String cheie);
}

33
import java.io.*;
import java.util.*;
public class OperatiiSpecificatiiProdus implements OperatiiObiecte{
private PrintWriter pw;
public OperatiiSpecificatiiProdus(){
try{
pw=new PrintWriter(new FileWriter("produse.txt"), true);
}catch(IOException io){io.printStackTrace();}
}
public void scrieObiect(Object o){
if (!(o instanceof SpecificatieProdus)) {
System.out.println("instanta invalida");
return;
}else {
SpecificatieProdus p=(SpecificatieProdus)o;
pw.println(p.getDenumire()+"_"+p.getPret());
pw.flush();
}
}
public Object citesteObiect(String cheie){
SpecificatieProdus sp=null;
try{
BufferedReader br=new BufferedReader(new FileReader("produse.txt"));
StringTokenizer st;
String linie;
while((linie=br.readLine())!=null){
st=new StringTokenizer(linie, "_");
if(st.nextToken().equals(cheie)) {
sp=new SpecificatieProdus(cheie, Integer.parseInt(st.nextToken()));
break;
}
}
}catch(IOException io){io.printStackTrace();}
return sp;
}
}
import java.io.*;
import java.util.*;

public class OperatiiClient implements OperatiiObiecte{


private ObjectOutputStream oos;
private ObjectInputStream ois;

public OperatiiClient(){
try{
oos=new ObjectOutputStream(new FileOutputStream("clienti.txt"));
ois=new ObjectInputStream(new FileInputStream("clienti.txt"));
}catch(IOException io){}
}

public void scrieObiect(Object o){


if (!(o instanceof Client)) {System.out.println("instanta invalida");return;}
else {try{oos.writeObject(o);}catch(IOException io){io.printStackTrace();}}
}
public Object citesteObiect(String cheie){
Client c;
try{
c=(Client)ois.readObject();

34
while (c!=null){
if (cheie.equals(c.getNume()+" "+c.getPrenume()+" "+c.getAdresa()))return c;
c=(Client)ois.readObject();
}
}catch(IOException io){io.printStackTrace();}
catch(ClassNotFoundException io){io.printStackTrace();}
return null;
}
}
public class TestOperatii{

public static void main(String args[]){


OperatiiObiecte oo=new OperatiiSpecificatiiProdus();
oo.scrieObiect(new SpecificatieProdus("flori", 120000));
oo.scrieObiect(new SpecificatieProdus("miere", 150000));
SpecificatieProdus sp=(SpecificatieProdus)oo.citesteObiect("flori");
System.out.println("pret= "+sp.getPret());

oo=new OperatiiClient();
oo.scrieObiect(new Client("Ionescu", "Pop", "alea rozelor"));
oo.scrieObiect(new Client("Popescu", "Ion", "alea stejarilor"));
oo.scrieObiect(new Client("Marin", "Pop", "alea crizantemelor"));
Client c=(Client)oo.citesteObiect("Marin Pop alea crizantemelor");
System.out.println("date client= "+c.getNume()+" "+c.getPrenume());
}
}

35
7. CLASE INTERNE

DEFINIREA CLASELOR INTERNE

Într-un program Java este posibil să plasăm o definiţie a unei clase în spaţiul de nume al altei clase.
În acest caz, spunem că am definit clasa internă celei de-a doua clase, care este clasă externă pentru
aceasta.
Clasele interne ne permit să grupăm clasele care sunt conectate logic între ele şi să controlăm
vizibilitatea acestora.
Exemplu. Continuând aplicaţia GestiuneAngajaţi din ultimele două cursuri, presupunem că clasa
Manager are o clasă internă numită Secretara:
public class Manager extends Angajat{
private int vechime;
public Manager(String nume, long salariu, int v){
super(nume, salariu);
vechime=v;
}
public class Secretara{
private String nume;
public Secretara(String nume){
this.nume=nume;
}
String getNume(){
return nume;
}
}

Dintr-o clasă externă putem crea fără probleme obiecte ale oricărei clase internă a sa. Din afara
clasei externe, instanţierea clasei interner depinde de modificatorul de vizibilitate a acesteia din
urmă. Astfel, dacă clasa internă oferă acces, putem crea obiecte din această clasă, cu ajutorul unei
variabile instanţă de tip clasa externă şi tipul noii variabile instanţă este un tip compus:
NumeClasaExterna.NumeClasaInterna, ca în exemplul următor:
public static void main(String[] args){//in clasa Manager
Manager m=new Manager("Ionescu", 500, 7);
Manager.Secretara s=m.new Secretara("Maria");
System.out.println(s.getNume());
}
Dacă clasa internă este privată şi vrem să dăm acces clienţilor clasei externe la obiectele primei
clase, clasa externă trebuie să conţină o metodă ce returnează o referinţă la un nou obiect al clasei
interne:
public Secretara obtineObiectSecretara(String nume){//in clasa Manager
return new Secretara(nume);
}
Notă. O clasă internă privată furnizează o modalitate de a ascunde informaţii despre implementarea
realizată de clasa externă.
Orice obiect al unei clase interne este legat automat de un obiect din clasa externă din care face parte
şi din acest motiv, acesta poate accesa orice variabilă şi pot apela orice metodă (indiferent ce
modificator de vizibilitate au) ale obiectului înconjurător. De exemplu, în clasa Secretara putem
defini următoarea metodă:
36
public int getVechime(){return vechime;}
care poate fi apelată din metoda main:
System.out.println(“vechime manager= ”+ s.getVechime());

Într-o clasă internă putem scrie o metodă care să returneze o referinţă la obiectul înconjurător. Acest
obiect poate fi folosit pentru a acesa, respectiv apela, toate variabilele, respectiv metodele, neprivate
ale clasei externe:
public Manager getManager(){return Manager.this;}
Clasele interne pot fi definite într-o metodă sau chiar într-un bloc arbitrar clasei din care fac parte. O
astfel de decizie apare din două motive:
- clasa internă extinde o clasă abstractă sau implementează o interfaţă şi astfel putem crea şi
returna o referinţă la aceasta
- este o decizie de proiectare sau chiar de implementare, deoarece clasa externă rezolvă o
problemă mai grea şi avem nevoie de o clasă ajutătoare pe care să o folosim local.
De exemplu, putem defini clasa SecretaraE în corpul metodei obtineObiectSecretara(String):
public Secretara obtineObiectSecretara(String nume){
class SecretaraE extends Secretara{
private String nume;
private SecretaraE(String nume){
this.nume=nume;
}
public String getNume(){return nume;}
}//clasa interna
return new SecretaraE(nume);
}
Mai mult, o clasă internă definită în corpul unei metode poate fi facută clasă anonimă în
următoarele condiţii:
- clasa internă anonimă nu defineşte un constructor propriu
- putem declara variabile instanţă şi le putem iniţializa cu valorile transmise prin parametrii
metodei, numai că aceştia trebuie să fie declaraţi finali:
public Secretara obtineObiectSecretara(final String n){
return new Secretara(){
private String nume=n;
public String getNume(){return n;}
};
}
Iniţializarea variabilelor instanţă ale clasei interne poate fi facută într-un bloc de iniţializare:
public Secretara obtineObiectSecretara(final String n){
return new Secretara(){
private String nume;
{
nume=n;
}
public String getNume(){
return n;
}
};
}

37
CLASE INTERNE STATICE

O clasă internă statică se declară folosind modificatorul static:


private static class SecretaraE extends Secretara{//in clasa Manager
private String nume;
private SecretaraE(String nume){
this.nume=nume;
}
public String getNume(){
return nume;
}
}//clasa interna
Clasele interne statice îndeplinesc următoarele condiţii:
1. Pentru a crea un obiect al clasei interne statice nu avem nevoie de un obiect al clasei externe
2. Fiind statică, dintr-un obiect al unei astfel de clase nu putem accesa un obiect al clasei
înconjurătoare şi astfel nu putem accesa, respectiv apela nici o variabilă, respectiv metodă
instanţă a clasei înconjurătoare, numai variabile şi metode statice.
De exemplu, putem crea în metoda main un obiect al clasei SecretaraE astfel:
Secretara s=new SecretaraE("Maria");//in main
System.out.println(s.getNume());
Mai departe, clasele interne statice pot fi incorporate în interfeţe:
interface ISecretara{
public String getNume();
static class SecretaraEI implements ISecretara{
private String nume;
public SecretaraEI(String nume){
this.nume=nume;
}
public String getNume(){
return nume;
}
}//clasa interna
}//interfata
public static void main(String[] args){//din Manager
ISecretara s=new ISecretara.SecretaraEI("Maria");
System.out.println(s.getNume());
}
CLASE INTERNE ŞI MOŞTENIREA

Un motiv pentru care putem folosi clase interne este de a obţine în Java moştenirea multiplă: o clasă
internă extinde o clasă concretă sau abstractă sau implementează o interfaţă. De exp, putem declara
următoarea clasa abstractă:
abstract class Secretara{
public abstract String getNume();
}
În acest caz, putem declara clasa internă SecretaraE privată şi atunci este o clasă ascunsă oricărui
client al clasei externe, Manager. În schimb, se poate obţine o referinţă la clasa de bază Secretara
(sau la interfaţa implementată) care furnizează funcţionalitatea declarată.
private class SecretaraE extends Secretara{

38
...
private SecretaraE(String nume)
}
public Secretara obtineObiectSecretara(String nume){...}
In metoda main, de exemplu, putem obţine un obiect din clasa SecretaraE astfel:
Secretara s=m.obtineObiectSecretara("Maria");//in main
O clasă internă poate extinde orice clasă concretă sau abstractă. Mai mult, o clasă A internă clasei B
poate extinde o clasă internă a supraclasei lui B. De exemplu, clasa ManagerE extinde clasa
Manager:
public class ManagerE extends Manager{
public ManagerE(String nume, long salariu, int v){
super(nume, salariu, v);
}
public class SecretaraE extends Manager.Secretara{
public SecretaraE(String nume){
(ManagerE.this).super(nume);
}
}
public static void main(String[] args){
Manager m=new ManagerE("Ionescu", 500, 7);
Manager.Secretara s=m.obtineObiectSecretara("Maria");
System.out.println(s.getNume());
}
}
O altă soluţie ar fi ca constructorul clasei interne SecretaraE să conţină o variabilă referinţă la clasa
înconjurătoare a clasei externe extinsă. Cu acest constructor putem apela constructorul supraclasei
extinse:
public class ManagerE extends Manager{
public ManagerE(String nume, long salariu, int v){
super(nume, salariu, v);
}
public class SecretaraE extends Manager.Secretara{
public SecretaraE(Manager m, String nume){
m.super(nume);
}
}
În acest caz putem introduce o nouă metodă (overloaded cu cea moştenită) care să returneze un
obiect al subclasei interne SecretaraE:
public SecretaraE obtineObiectSecretara(ManagerE m, String nume){
return new SecretaraE(m,nume);
}
public static void main(String[] args){
ManagerE m=new ManagerE("Ionescu", 500, 7);
ManagerE.SecretaraE s=m.obtineObiectSecretara(m,"Maria");
System.out.println(s.getNume());
}
UTILIZAREA CLASELOR INTERNE ÎN APLICAŢII OO

Clasele interne au fost introduse pentru a trata evenimentele gestionate de aplicaţiile programate
orientat spre obiecte. Un eveniment este o entitate conceptuală care spune că ceva are loc în timp şi

39
spaţiu şi are consecinţe asupra unei aplicaţii. Cu alte cuvinte, în momentul în care apare un
eveniment, aplicaţia noastră trebuie să execute o acţiune. Acţiunea depinde de tipul evenimentului
apărut.
Evenimentele sunt în general de două tipuri:
- evenimente externe, cele generate de utilizator în interacţiunea sa cu aplicaţia noastră şi
- evenimente interne, cele generate în interiorul aplicaţiei.
Atunci putem defini o clasă abstractă Eveniment ca o clasă generică de evenimente:
public abstract class Eveniment{
private long moment;
public Eveniment(long mT){
moment=mT;
}
public boolean aAparut(){
return System.currentTimeMillis()>=moment;
}
public abstract void executaActiune();
public abstract String descriere();
}
şi un gestor generic de evenimente:
public class Controller{
private Eveniment e;
public void setEveniment(Eveniment e){
this.e=e;
}
public void gestioneaza(){
while(true)
if (e.aAparut()){
e.executaActiune();
System.out.println(e.descriere());
e=null;
break;
}
}
}
Exemplu. Să scriem de exemplu o aplicaţie care simulează funcţionarea unui semafor de circulaţie.
Vom avea clasa Semafor care conţine o variabilă instanţă privată culoare, ce reprezintă culoarea
curentă aratată de semafor:
public class Semafor{
private String culoare;
public void setCuloare(String c){culoare=c;}
public String getCuloare(){return culoare;}
}
Semaforul este controlat de clasa GestorSemafor care extinde clasa Controller şi tratează fiecare
eveniment produs de schimbarea culorii la semafor. Aşadar, toate evenimentele tratate de clasa
GestorSemafor sunt de acelaşi tip SchimbareCuloare:
public class GestorSemafor extends Controller{
private Semafor s;
public GestorSemafor(Semafor s){
this.s=s;
}

40
public class SchimbareCuloare extends Eveniment{
int culoare;
public SchimbareCuloare(long timp, int culoare){
super(timp);
this.culoare=culoare;
}
public void executaActiune(){
switch(culoare){
case 1: s.setCuloare("rosu");break;
case 2: s.setCuloare("galben");break;
case 3: s.setCuloare("verde");
}
}

public String descriere(){


return "Culoarea aratata de semafor este "+s.getCuloare();
}
}
}
Observăm că clasa SchimbareCuloare este o clasă internă şi este o subclasă a clasei Eveniment.
Clasa Test crează un obiect Semafor în metoda main şi produce la intervale scurte de timp
evenimente generate de schimbarea culorii la semafor:
public class Test{
public static void main(String[] args){
Semafor s=new Semafor();
GestorSemafor gs=new GestorSemafor(s);
for (int i=1; i<=10; i++){
long timp=System.currentTimeMillis();
gs.setEveniment(gs.new SchimbareCuloare(timp+1000*i, 1));
gs.gestioneaza();
gs.setEveniment(gs.new SchimbareCuloare(timp+2000*i+1000, 2));
gs.gestioneaza();
gs.setEveniment(gs.new SchimbareCuloare(timp+3000*i+2000, 3));
gs.gestioneaza();
}
}
}

41
8. INPUT/OUTPUT ÎN JAVA
Pachetul java.io
Furnizează suport pentru citirea şi scrierea datelor de la şi la dispozitive diferite.
Conţine 4 categorii de clase:
- clase de fluxuri de intrare şi clase de citire
- clase de fluxuri de ieşire şi clase de scriere
- clase de gestiune a fişierelor
- clasa StreamTokenizer
1. Clase de fluxuri de octeţi: BufferedInputStream, DataInputStream, FileInputStream şi
StringBufferInputStream. Derivate din clasa abstractă InputStream.
Clase de citire a caracterelor Unicode: Reader, BufferedReader, FileReader şi StringReader
sunt derivate din clasa abstractă Reader.
2. Clase de fluxuri de ieşire: OutputStream, FileOutputStream, BufferedOutputStream,
PrintStream şi StringBufferOutputStream. Sunt derivate din clasa OutputStream.
Clase de scriere a caracterelor în Unicode: Writer, BufferedWriter, FileWriter şi PrintWriter.
Sunt derivate de clasa abstractă Writer.
3. Claselor FileInputStream şi FileOutputStream se adaugă alte două clase destinate gestiunii
fişierelor: File şi RandomAccessFile.
4. Clasa StreamTokenizer permite convertirea unui flux de intrare a datelor într-un flux de token-i
şi conţine metode pentru definirea sintaxei token-ilor.

42
Ierarhia claselor I/O

DataInput File
FileDescriptor ByteArrayInputStream
Serializable FileInputStream BufferedInputStream
FilterInputStream DataInputStream
Externalizable InputStream ObjectInputStream LineNumberInputStream
PipedInputStream
ObjectInputValidation SequenceInputStream
ObjectInput StringBufferInputStream BufferedReader LineNumberReader
ObjectOutput CharArrayReader
Object InputStreamReader FileReader
Reader FilterReader PushbackReader
OutputStream ByteArrayOutputStream PipedReader BufferedOutputStream
DataOutput FileOuptputStream StringReader DataOutputStream
FilterOutputStream PrintStream
Throwable ObjectOutputStream BufferedWriter
RandomAccessFile PipedOutputStream CharArrayWriter
Writer FilterWriter FileWriter
FilenameFilter StreamTokenizer OutputStreamWriter
PipedWriter
PrintWriter EOFException
StringWriter FileNotFoundException
Exception IOException InterruptedIOException
ObjectInputValidation SyncFailedException
InvalidClassException UTFDataFormatException
InvalidObjectException ObjectStreamException CharConversionException
NotActiveException UnsupportedEncodingException
NotSerializableException WriteAbortedException
OptionalDataException
StreamCorruptedException

43
Clasa InputStream
InputStream = clasă abstractă din care derivează toate clasele fluxului de intrare.
Metodele clasei InputStream sunt prezentate în următorul tabel:
Semnătura metodei Semnificaţie –Valoarea returnată
abstract int read() throws IOEXception returnează un octet citit de pe fluxul de intrare.
Dacă se detectează sfârşitul fluxului atunci metoda
intoarce –1.
int read(byte[] b) throws IOEXception citeşte octeţi de pe fluxul de intrare şi îi memorează
în array-ul b. Dacă b este mai mic decât numărul de
octeţi citiţi, se lansează o exceptie. Metoda
returnează numărul de octeţi citiţi efectiv.
int read(byte[] b, int poz, int nr) citeşte maxim nr octeţi de pe fluxul de intrare şi îi
throws IOEXception memorează în array-ul b, de la poziţia poz. Dacă b
este mai mic decât numărul de octeţi citiţi, se
lansează o exceptie. Metoda returnează numărul de
octeţi citiţi efectiv.
long skip(long n) throws IOException ignoră maxim n octeţi din fluxul de intrare şi
întoarce numărul de octeţi cu care s-a avansat in
flux.
int available() throws IOException returnează numărul de octeţi disponibili pentru
citire fără blocare.
void mark(int poz) marchează poziţia poz în flux pentru ca ulterior să
se poată reveni la ea. Nu toate fluxurile de intrare
oferă această operaţie.
void reset() throws IOException repoziţionează fluxul la ultima poziţie marcată.
boolean markSupported() întoarce true dacă fluxul suportă operaţia de
marcare şi revenire la poziţia marcată.
void close() throws IOException închide fluxul de intrare şi eliberează resursa
alocată.

Clasa Reader
Aceeaşi structură are clasa abstractă Reader, specializată în citirea caracterelor. De aceea, sintaxa
metodelor read() a fost modificată astfel:
Semnătura metodei Semnificaţie –Valoarea returnată
int read() throws IOEXception citeşte un caracter de pe fluxul de intrare şi
returnează codul său Unicode. Dacă se detectează
sfârşitul fluxului atunci metoda intoarce –1.
int read(char[] b) throws IOEXception citeşte caractere de pe fluxul de intrare şi îi
memorează în array-ul b. Dacă b este mai mic decât
numărul de caractere citite, se lansează o exceptie.
Metoda returnează numărul de caractere citite
efectiv.
abstract int read(char[] b, int poz, citeşte maxim nr caractere de pe fluxul de intrare şi
int nr) throws IOEXception îi memorează în array-ul b, de la poziţia poz. Dacă
b este mai mic decât numărul de caractere citite, se
lansează o exceptie. Metoda returnează numărul de
caractere citite efectiv.

System.in = variabilă statică a clasei System care memorează o instanţă a clasei


InputStream. De obicei, această instanţă reprezintă fluxul standard de intrare (tastatura), dar
poate fi setată cu System.setIn(InputStream) astfel:
FileInputStream fi = new FileInputStream(“input.txt”);
System.setIn(fi);
Crearea unui flux de date de intrare

45
Crearea unui flux de date de intrare respectă modelul pipeline Unix: face parte dintr-o sursă de date
cu interfaţa InputStream şi se crează succesiv cu filtri ai aceleaşi interfeţe.
Caracteristicile citirii:
- este un flux continuu.
- când nu mai sunt date disponibile, instanţa clasei se blochează în aşteptarea ca datele să devină
disponibile.
Pentru a citi date în Java procedura este întotdeauna aceeaşi:
1. Se crează o instanţă a unei clase de flux de intrare.
2. Se indică locul din care vor fi citite datele.
FileInputStream BufferedInputStream DataInputStream
byte
int
File char
Bytes Buffer de bytes Date formatate float
File system long
(Sursă date)
Memoria

Exemplu. Metoda urmatoare citeşte un caracter de la tastatură şi-l returnează:


public char citeste(){
char c='0';
try{
c=(char)System.in.read();
System.in.skip(2);//nu se citeşte caracterul generat de apăsarea lui
Enter
}catch(IOException e){System.out.println(e.getMessage());}
return c;
}
Clase de flux de octeţi de intrare
1. DataInputStream (InputStream in):
- utilizată pentru citirea datelor primitive (int, char, long etc.);
- moşteneşte atât interfaţa DataInput cât şi clasa abstractă FilterInputStream;
- la metodele furnizate de InputStream se adaugă metodele interfeţei DataInput readxxx()
(xxx = Boolean, Byte, Short, UnsignedShort, Char, Int, Long, Float sau
Double) care citesc un număr de octeţi din fluxul de intrare necesar pentru a obţine o valoare
de tip primitiv specificat în numele metodei. De exemplu, metoda readInt() citeşte patru
octeţi din fluxul de intrare şi întoarce o valoare de tip int.
2. BufferedInputStream (InputStream in):
- furnizează un flux de intrare buffer-izat permiţând astfel o citire mult mai rapidă;
- include numai metodele furnizate de InputStream;
- are unele variabile membru: byte buf[], int count, int pos, int markpos şi
int marklimit, utile dacă se extinde clasa BufferedStream.
3. FileInputStream (String nume):
- execută operaţii de intrare simple asupra fişierelor (pentru operaţii mai avansate se utilizează
RandomAccessFile).
- construieşte fluxul şi cu un obiect File sau FileDescriptor.
- funcţionează ca un InputStream, dar operează asupra unui fişier.
4. PushBackInputStream (InputStream in)
- construieşte un flux cu un caracter lookahead.
- are o metodă unread(int ch).
- funcţionează ca InputStream.

46
Clasa abstractă Reader are subclase asemănătoare cu cele ale clasei abstracte InputStream:
BufferedReader, FileReader, StringReader, PushBackReader. BufferedReader are o metoda String
readLine(). In plus, există clasa InputStreamReader care face legătura dintre o clasa de tip
InputSream şi un Reader.
Următorul exemplu arată mai multe modalitaţi de a face o citire folosind clasele derivate din
InputStream şi Reader:
import java.io.*;
public class FluxDeIntrareIesire{
public static void main(String[] args) {
try {
//1. Citirea unei linii de la tastatura
BufferedReader in1 = new BufferedReader( new InputStreamReader(System.in));
String s=in1.readLine();
in1.close();
//2. Citirea dintr-un fisier
BufferedReader in2 = new BufferedReader( new FileReader("intrare.txt"));
String s1, s2 = new String();
while((s1 = in2.readLine())!= null) s2 += s1 + "\n";
in2.close();
//3. Citirea din memorie
StringReader in3 = new StringReader(s2);
int c;
while((c = in3.read() ) != -1) System.out.print((char)c);
in3.close();
//4. Citirea din memorie formatata
DataInputStream in4 = new DataInputStream(
new FileInputStream("intrare.txt"));
while(true) System.out.print((char)in4.readByte());
} catch(EOFException e){
System.out.println("Am ajuns la sfarsitul fisierului");
} catch (FileNotFoundException e){
System.out.println("Fisier inexistent");
} catch (IOException e){
System.out.println("Exceptie IO");
}
}
}
Clasa OutputStream
OutputStream = clasă abstractă din care derivează toate clasele fluxului de ieşire.
Metodele clasei OutputStream sunt prezentate în următorul tabel:
Semnătura metodei Semnificaţie –Valoarea returnată
abstract void write(int n) throws scrie valoarea lui n pe fluxul de ieşire.
IOEXception
void write(byte[] b) throws IOEXception scrie pe fluxul de ieşire octeţii din array-ul b
void write(byte[] b, int poz, int nr) scrie nr octeţi pe fluxul de ieşire începând de la
throws IOEXception poziţia poz a array-ului b.
void flush() throws IOException goleşte fluxul de ieşire
void close() throws IOException închide fluxul de intrare şi eliberează resursa
alocată.

Clasa Writer
Clasa Writer specializată în scrierea fluxurilor de caractere are o ierarhie similară cu ierarhia lui
OutputStream. Semnătura metodelor write() se modifică şi apar două forme noi ale acestei metode:

Semnătura metodei Semnificaţie –Valoarea returnată


void write(int n) throws scrie valoarea lui n pe fluxul de ieşire.
IOEXception
void write(char[] b) throws scrie pe fluxul de ieşire caracterele din array-ul b

47
Semnătura metodei Semnificaţie –Valoarea returnată
IOEXception
abstract void write(char[] b, int scrie nr caractere pe fluxul de ieşire începând de la
poz, int nr) throws IOEXception poziţia poz a array-ului b.
public void write(String str) scrie şirul str pe fluxul de ieşire.
throws IOException
public void write(String str, int scrie pe fluxul de ieşire subşirul de lungime nr pe
poz, int nr) throws IOException care-l extrage de la poziţia poz a şirului str.

System.out = variabilă statică a clasei System care memorează o instanţă a clasei PrintStream ce
reprezintă de obicei ecranul monitorului. Este setată cu System.setOut(OutputStream):
FileOutputStream fs = new FileOutputStream(“log.txt”);
System.setOut(new printStream(fs));
Caracteristicile scrierii:
- este un flux continuu.
- instanţa clasei de flux trimite date cât timp acestea sunt disponibile.
Procedura de scriere a datelor cu o clasă de fluxuri de ieşire este întotdeauna aceeaşi:
1. Se crează o instanţă a clasei fluxului de ieşire specificând fluxul final, la destinaţie. Instanţa se
poate crea plecând de la destinaţie şi compunând fluxul din obiecte-flux încuibate, deoarece
obiectul obţinut are caracteristicile dorite: buffer-izat, în stare să scrie date primitive etc.
2. Se indică locul unde vor fi scrise datele.

byte DataInputStream BufferedInputStream FileInputStream


int
File
char
float File system
Date formatate Buffer de bytes Bytes Flux de bytes (Sursă date)
long
Memoria

Clase de flux de caractere în ieşire


1. PrintWriter
- derivează din Writer
- afişează date-text (şiruri sau numere).
- metodele sale, altele decât cele furnizate de Write, include: print(tip) şi
println(tip) unde tip ia una din valorile: Object, String, boolean, char, char[], int, long,
float şi double.
PrintWriter out= PrintWriter(new FileWriter(“personale.txt”), true);
2. BufferedWriter
- derivează din Writer,
- furnizează un flux de ieşire buffer-izat,
- constructorul are ca argument un obiect Writer,
- nu conţine alte metode în afară de cele furnizate de Writer,
- are două variabile membru utile: byte buf[] şi int count.
3. FileWriter
- execută operaţii simple de ieşire de caractere pe fişiere (pentru operaţii mai avansate pe fişiere
se utilizează RandomAccessFile),
- construieşte un obiect numai cu un şir, un obiect File sau un FileDescriptor,
- funcţionează ca Write, dar operează pe fişiere.
Scrierea cu clasele Output Stream şi Writer
Primul exemplu din această subsecţiune arată cum se poate determina numărul de linii ale unui
fişier şi scrierea fiecărei linii citite într-un alt fişier, împreună cu numărul liniei curente.

48
Observaţie. Pentru a afla numărul liniei curente vom folosi un cititor buffer-izat ce păstrează
numărul liniei citite. Adică vom folosi o instanţă a clasei LineNumberReader, subclasă a lui
BufferedReader.
try{
LineNumberReader lnr = new LineNumberReader(new FileReader("intrare.txt"));
PrintWriter out=new PrintWriter(new BufferedWriter(
new FileWriter("iesire.txt")));
String s="";
while((s = lnr.readLine()) != null) out.println("Linia " +lnr.getLineNumber()+
" "+ s);
lnr.close();
out.close();
}catch(EOFException e){System.out.println("Sfarsitul fisierului");}
catch(IOException ioe){System.out.println("Eroare de intrare/iesire");}
Următorul exemplu arată cum scriem şi citim date formatate într-un/dintr-un fişier. Vom folosi
instanţe ale claselor DataOutputStream şi DataInputStream pentru a scrie numărul
3.14159 şi şirul “Valoarea lui PI”:
try{
DataOutputStream out=new DataOutputStream(new BufferedOutputStream(
new FileOutputStream("Data.txt")));
out.writeDouble(3.14159);
out.writeBytes("Valoarea lui PI");
out.close();
DataInputStream in = new DataInputStream(new BufferedInputStream(
new FileInputStream("Data.txt")));
BufferedReader inbr = new BufferedReader(new InputStreamReader(in));
System.out.println(in.readDouble());
System.out.println(inbr.readLine());
}catch(EOFException e) {System.out.println("Sfarsitul fluxului");}
catch(IOException ioe){System.out.println("Eroare de intrare/iesire");}
Clasa următoare furnizează două metode care citesc, respectiv scriu date formatate pe linii de
lungime fixă:
import java.io.*;
public class DataIO{
public static String citesteSirFix(int size, DataInput in)throws IOException{
StringBuffer buffer=new StringBuffer(size);
int i=0;
boolean more=true;
while(more&& i<size){
char c=in.readChar();
i++;
if(c==0) more=false;
else buffer.append(c);
}
in.skipBytes(2*(size-i));
return buffer.toString();
}
public static void scrieSirFix(String s, int size, DataOutput out) throws
IOException{
for(int i=0;i<size;i++){
char c=0;
if(i<s.length()) c=s.charAt(i);
out.writeChar(c);
}
}
}

49
Această clasă poate fi folosită de exemplu, pentru a scrie un scrie un program care salveaza intr-un
fisier datele (id, nume, salariu) a trei angajati ai unei companii şi apoi le afişeaza în ordinea inversă
scrierii lor:
import java.io.*;

class Angajat{
public static final int SIZE_NUME=40;
public static final int RECORD_SIZE=2*SIZE_NUME+4+8;

private String nume;


private int id;
private double salariu;

public Angajat(){}

public Angajat(int id, String nume, double salariu){


this.id=id;
this.nume=nume;
this.salariu=salariu;
}

public void writeData(DataOutput out) throws IOException{


out.writeInt(id);
DataIO.scrieSirFix(nume, SIZE_NUME,out);
out.writeDouble(salariu);
}

public void readData(DataInput in)throws IOException{


id=in.readInt();
nume=DataIO.citesteSirFix(SIZE_NUME,in);
salariu=in.readDouble();
}

public String toString(){


return id+" "+nume+" "+salariu;
}
}

public class TestAngajat{


public static void main(String[] arg){
Angajat[] a=new Angajat[3];
a[0]=new Angajat(1, "popescu ion", 45.899);
a[1]=new Angajat(2, "ionescu pop", 435.877);
a[2]=new Angajat(2, "marinescu marin", 235.877);

try{
DataOutputStream out=new DataOutputStream(new
FileOutputStream("angajat.dat"));
a[0].writeData(out);
a[1].writeData(out);
a[2].writeData(out);
out.close();
}catch(IOException ioe){}

try{
RandomAccessFile in=new RandomAccessFile("angajat.dat", "r");
int n=(int)(in.length()/Angajat.RECORD_SIZE);
System.out.println("n="+n);
Angajat[] an=new Angajat[n];

for(int i=n-1; i>=0;i--){


an[i]=new Angajat();
in.seek(i*Angajat.RECORD_SIZE);

50
an[i].readData(in);
System.out.println(an[i]);
}
} catch(IOException e){System.out.println(e.getMessage());}
}
}
Clasa File
Clasa File prezintă o vedere abstractă a numelor fişierelor şi a directoarelor, precum si locaţiilor
unde se găsesc acestea într-un sistem de fişiere. Locaţia unui fişier reprezintă calea în sistemul de
fişiere unde este memorat acesta. Calea poate fi absolută sau relativă la directorul în care se găsesc
class-urile programului. Separatorul unei căi depinde de sistemul de operare pe care rulează
programul si anume:
- “\\” pentru sistemele Windows
- “/” pentru sistemele Unix.
Acesta este memorat în constantele String separator şi char separatorChar, văzut ca
un String, respectiv ca un caracter.
Constructori:
File(String cale)//crează un obiect File ce contine o cale absolută sau relativă
şi care poate conţine numele fişierului
File(String cale, String nume)//crează un obiect File ce conţine separat numele
fişierului şi calea directorului în care se află fişierul
File(File dir, String nume)//calea directorului părinte este dat ca un obiect
File
Metodele mai importante ale clasei File sunt prezentate în următorul tabel:
Semnătura metodei Semnificaţie –Valoarea returnată
String getName() returnează numele fişierului sau directorului curent
String getPath() returnează calea unde se află fişierul sau directorul curent pe
disc
String getAbsolutePath() returnează calea absolută unde se află fişierul sau directorul
curent pe disc
String getParent() întoarce directorul părinte a directorului sau în care se găseşte
fişierul curent
boolean exists() returnează true dacă fişierul indicat in constructor există pe
disc.
boolean canWrite() returnează true dacă fişierul respectiv poate fi modificat
boolean canRead() returnează true dacă fişierul respectiv poate fi citit
boolean isFile() returnează true dacă şirul indicat ca fişier există pe disc şi este
un fişier
boolean isDirectory() returnează true dacă şirul indicat ca director există pe disc şi
este un director
long lastModified() returnează data şi ora ultimei modificări
long length() returnează mărimea fişierului, în biţi
boolean mkdir() returnează true dacă a fost creat directorul specificat in cale
boolean mkdirs() returnează true dacă au fost create
directoarele specificate in cale
boolean renameTo(File dest) redenumeşte fişierul cu curent cu numele dest şi returnează
true
boolean delete() şterge fişierul sau directorul curent
String[] list() întoarce într-un array de şiruri numele fişierelor şi
directoarelor din directorul curent.
String[] list(FilenameFilter filtru) întoarce într-un array de şiruri numele fişierelor şi
directoarelor din directorul curent care îndeplinesc criteriul
filtru

51
De exemplu, pentru a crea un filtru trebuie să scriem o clasă care implementează interfaţa
FilenameFilter. De exemplu, următoarea clasa filtreaza fişierele dintr-un director si le acceptă
numai pe cele care sunt programe Java:
import java.io.*;
public class Filtru implements FilenameFilter{
public boolean accept(File dir, String nume){
String sufix="";
int i=nume.lastIndexOf('.');
if(i>0 && i<nume.length()-1) sufix=nume.substring(i+1);
return sufix.equals("java");
}
public String getDescription(){
return "fisiere java(*.java)";
}
}
Următorul exemplu citeşte numele unui fişier/director de la tastatură şi-l scrie informaţii despre el,
cum ar fi: dacă este fişier sau director, locaţia sa pe disc, data ultimei modificări, etc, în fişierul
analiza.txt:
import java.io.*;
public class Fisier{
BufferedReader br;
PrintWriter pw;

public Fisier(){
try{
br=new BufferedReader(new InputStreamReader(System.in));
pw=new PrintWriter(new FileWriter("analiza.txt"), true);
scrie(analizeaza(citeste()));
}catch(IOException ioe){}
}

public String citeste()throws IOException{


System.out.println("Dati numele fisierului/directorului");
return br.readLine();
}

public String analizeaza(String sir){


File nume=new File(sir);
StringBuffer buffer=new StringBuffer();
if(nume.exists()){
buffer.append(nume.getName()+" exista\r\n");
if(nume.isFile()) buffer.append("este un fisier\r\n");
if(nume.isDirectory())buffer.append("este un director\r\n");
if(nume.isAbsolute())buffer.append("este o cale absoluta\r\n");
else buffer.append("nu este o cale absoluta\r\n");
buffer.append("data ultimei modificari
"+nume.lastModified()+"\r\n");
buffer.append("lungimea "+nume.length()+"\r\n");
buffer.append("calea "+nume.getPath()+"\r\n");
buffer.append("calea absoluta "+nume.getAbsolutePath()+"\r\n");
buffer.append("parinte "+nume.getParent()+"\r\n");

if(nume.isFile()){

52
buffer.append("Continutul fisierului\r\n");
try{
RandomAccessFile in=new RandomAccessFile(nume, "r");
String s="";
while((s=in.readLine())!=null) buffer.append(s+"\r\n");
in.close();
}catch(IOException ioe){}
}
else
if(nume.isDirectory()){
buffer.append("Continutul directorului\r\n");
String[] dir=nume.list();
for(int i=0;i<dir.length; i++) buffer.append(dir[i]+"\r\n");
}
}
else
buffer.append("acest nume nu exista");
return buffer.toString();
}
public void scrie(String sir) throws IOException {
pw.print(sir);
pw.close();
}
public static void main(String[] a){
new Fisier();
}
}
Clasa StreamTokenizer
Ca şi StringTokenizer, această clasă imparte un flux in token-i, dar spre deosebire de prima clasă,
diferenţiază între tipuri de token-i: şiruri, numere, sfârşitul unei linii, sfârşitul fişierului, etc.
Constructorul clasei:
public StreamTokenizer(Reader r)//creaza un tokenizer care imparte fluxul de intrare r într-un
şir de token-i.
Pentru a sti daca s-a ajuns la sfarsitul liniei curente se foloseste constanta:
public static finale int TT_EOL //token-ul citit conţine sfarşitul liniei curente (“\r” sau “\n”)
dacă acesta este tratat ca un token, adică a fost apelată metoda void
eolIsSignificant(boolean val) cu val=true.
Pentru a trata sfarsitul fluxului de intrare folosim constanta
public static finale int TT_EOF //token-ul citit conţine sfârşitul fluxului de intrare
Deosebirea faţă de clasa StringTokenizer este ca aceasta clasă diferenţiaza token-ii obtinuti din
fragmentare. Astfel, clasa ofera urmatoarele constante:
public static finale int TT_NUMBER //token-ul citit este un număr
public static finale int TT_WORD //token-ul citit este un cuvânt
Pentru a obtine numărul, respectiv cuvântul citit se folosesc următoarele variabile instanţă:
public double nval //conţine valoarea numărului întors ca token
public String sval //conţine caracterele cuvântului întors ca token
Pentru a obtine un token din fluxul analizat se apelează metoda instanţă:
public int nextToken()
care returnează tipul token-ului citit ca un numar intreg. Acesta este memorat dupa apelul metodei
si în variabila instanţă public int ttype.
Clasa oferă si alte metode utile în analizarea unui flux de intrare specializat:
public void parseNumbers()//sunt analizate numerele si caracterele ‘.’ si ‘–‘.

53
public int lineno()//întoarce numărul liniei curente.
public void ordinaryChar(int ch) //trateaza caracterul dat la apelul metodei ca unul oarecare,
fara sa mai verifice tipul sau si-l intoarce ca un token de tip TT_WORD.
public void ordinaryChars(int min, int max) //toate caracterele ce au codul Unicod in
intervalul [min, max] sunt tratate ca oarecare.
public void commentChar(int ch) //caracterul ch indica inceputul unui comentariu. In acest
caz, token-ii fluxului de intrare aflati pana la sfarsitul liniei vor fi ignoraţi.
public void quoteChar(int ch) //caracterele gasite intre două apariţii a lui ch sunt tratate ca un
literal String si intoarse ca un token de tip TT_WORD. Dacă a doua apariţie a caracterului ch nu este
găsită pana la sfarşitul liniei, se iau caracterele pana la acesta. Daca ch apare o singură dată in fluxul
de intrare, se iau caracterele de la ch pana la sfarsitul fisierului.
public void whitespaceChars(int min, int max) // toate caracterele ce au codul Unicod in
intervalul [min, max] sunt tratate ca spatii.
public void wordChars(int min, int max) // toate caracterele ce au codul Unicod in
intervalul [min, max] sunt tratate ca parte din cuvinte.
public void pushBack()//token-ul curent va fi obtinut si la urmatorul apel al metodei
nextToken()
Exemplu. Sa scriem un program care citeste continutul fisierului “intrare.txt” si afiseaza tipul
fiecarui token si valoarea sa, precum şi numarul de cuvinte, respectiv numere, din fisierul respectiv.
import java.io.*;
public class Tokenizer{
private StreamTokenizer st;
private int w, n;

public Tokenizer(){
String linie;
try{
st=new StreamTokenizer(new BufferedReader(new FileReader("intrare.txt")));
}catch(FileNotFoundException nf){System.out.println(nf.getMessage());}
catch(IOException io){System.out.println(io.getMessage());}
}

public void imparte(){


if(st==null) System.exit(0);
try{
while(st.nextToken()!=StreamTokenizer.TT_EOF){
switch(st.ttype){
case StreamTokenizer.TT_WORD: System.out.print("cuvant= "+st.sval+" ");
w++; break;
case StreamTokenizer.TT_NUMBER: System.out.print("numar= "+st.nval+" ");
n++; break;
}
System.out.println();
}
System.out.println("Total cuvinte= "+w);
System.out.println("Total numere= "+n);
}catch(IOException io){System.out.println(io.getMessage());}
}

public static void main(String[] args){


Tokenizer t=new Tokenizer();
t.imparte();
}
}

54
9. GESTIUNEA EXCEPŢIILOR
O excepţie este o condiţie exceptională care întrerupe execuţia unui program şi care poate fi:
- condiţie de eroare provocată de erori interne sau lipsa resurselor mediului de executie Java,
- situaţie particulară apărută în timpul execuţiei programului care poate fi de unul din
următoarele tipuri:
- excepţii necontrolate cauzate de erori de programare: cast greşit, cast care depăşeşte limita
unui array, referinţă la un obiect null,
- excepţii controlate: erori de intrare/ieşire precum continuarea operaţiei de citire după ce s-a
ajuns la sfârşitul fişierului, încercarea de a deschide un URL greşit.
Când apare o excepţie este important să captăm cauza ce a dus la apariţia ei, împreună cu alte
informaţii, cum ar fi valoarea variabilelor folosite, ce vor ajuta la tratarea exceptiei. De aceea, toate
excepţiile în Java sunt reprezentate de obiecte ale subclaselor clasei Throwable din pachetul
java.lang:
public class Throwable{
public Throwable();
public Throwable(String mesaj);
public String getMessage();
public String toString();
public void printStackTrace();
public void printStackTrace(java.io.PrintStream ps);
public void printStackTrace(java.io.PrintWriter pw);
public Throwable fillInStackTrace();
}
După cum observăm, clasa Throwable implementează mai multe metode printre care şi metoda
getMessage() care returnează un şir care descrie situaţia care a cauzat excepţia.
De exemplu, clasa RunTimeException conţine tipuri de excepţii lansate automat de JVM. Dacă
nu sunt tratate de programator, dacă apare o astfel de excepţie se va apela metoda
printStackTrace() a clasei Throwable. De exemplu, executarea următorului program
public class Exceptie{
static void metoda1(int[] a){
metoda2(a);
}
static void metoda2(int[] b){
System.out.println(b[0]);
}
public static void main(String[] args){
metoda1(null);
}
}
determină afişarea mesajului:
java.lang.NullPointerException
at Exceptie.metoda2(Exceptie.java:6)
at Exceptie.metoda1(Exceptie.java:3)
at Exceptie.main(Exceptie.java:9)
Mesajul afişat conţine următoarele informaţii:
- tipul de exceptie aruncat,
- stiva apelurilor metodelor, fiecare cu clasa şi unitatea de compilare de care aparţine, împreuna
cu numărul liniei pe care se găseşte declaraţia metodei. Metoda din vârful stivei este cea care a
aruncat excepţia respectivă.
Toate excepţiile ce pot fi generate sunt împărţite în două clase: Exception şi Error, ambele
derivate din clasa Throwable.
Subclasele lui Exception, cu excepţia lui RuntimeException şi a subclaselor sale, indică
situaţii particulare care nu sunt erori. Error, RuntimeException şi subclasele lor indică
condiţii care în general nu sunt corecte şi care de obicei generează terminarea unui program. Toate

55
pachetele incluse în JDK generează diferite tipuri de excepţii. O parte din acestea sunt prezentate în
figurile de mai jos şi unele din acestea sunt explicate în următorul tabel:

Exceptie Cauzată de
ArithmeticException Erori matematice cum ar fi imparţirea la zero.
ArrayIndexOutOfBoundsException Indicele unui array este în afara array-ului.
ArrayStoreException Se memorează într-un array o valoare de tip diferit de tipul de date al
array-ului.
ClassNotFoundException Se încearcă folosirea unei clase pe care compilatorul nu o cunoaşte.
Clasa respectivă trebuie să fie importată sau scrisă de către
programator.
FileNotFoundException Se accesează un fişier care nu există în locaţia specificată de
program.
IOException Erori generale de I/O, cum ar fi incapacitatea de citi dintr-un fişier.
NullPointerException Referirea unui obiect null.
NumberFormatException O conversie eşuată între şiruri şi numere.
OutOfMemoryException Memorie insuficientă creării unui obiect nou.
SecurityException Un applet înacearcă să realizeze o acţiune care nu este permisă de
browser.
StackOverflowException Depăşirea stivei de execuţie.
StringIndexOutOfBoundsException Programul incearcă să acceseze un caracter de pe o poziţie care nu
există în şir.

56
Excepţii şi erori

ClassNotFoundException
ArithmeticException
CloneNotSupportedException
ArrayStoreException
Object IllegalAccessException IllegalThreadStateException
Throwable Exception
ClassCastException
InstantiationException NumberFormatException
InterruptedException IllegalArgumentException

NoSuchMethodException IllegalStateException
NoSuchFieldException IllegalMonitorStateException
RunTimeException IndexOutOfBoundsException
IOException ArrayIndexOutOfBoundsException
NegativeArraySizeException
StringIndexOutOfBoundsException
NullPointerException
ClassCircularityError
SecurityException
ClassFormatError
ExeptionInInitializerError AbstractMethodError
IllegalAccessError
LinkageError IncompatibleClassChangeError
InstantiationError
NoClassDefFoundError
NoSuchFieldError
Object Throwable Error UnsatisfiedLinkError
NoSuchMethodError
ThreadDeathError VerifyError

InternalError
VirtualMachineError OutOfMemoryError
StackOverflowError
UnknownError

57
Cum utilizăm excepţiile? Există mai multe alternative pentru programator:
- corectarea problemei şi apelarea metodei care a cauzat excepţia;
- se încearcă repararea unei greşeli şi se continuă fără să se reapeleze metoda;
- elaborarea unui rezultat alternativ raportat la cel produs în mod normal de metodă;
- se face tot ce se poate în contextul curent şi se relansează aceeaşi excepţie în contextul superior;
- se face tot ce se poate în contextul curent şi se relansează o excepţie diferită în contextul
superior;
- se termină programul.
Tratarea excepţiilor prezintă următoarele avantaje:
- simplifică dacă schema de elaborare a excepţiilor aleasă complică lucrurile,
- face mai fiabile programul şi librăriile utilizate. Aceasta e desigur o investiţie bună pe termen
scurt (pentru debugging) şi pe termen lung (pentru robusteţea programului).
Mecanismul de gestiune a excepţiilor furnizează reguli care impun cine este capabil să genereze
excepţii (throws) şi când (throw), cine va încerca excepţiile (try..catch) şi ce trebuie să facă pentru a
împiedica apariţia unei excepţii.
Interceptarea şi tratarea excepţiilor
Interceptarea şi tratarea excepţiilor se realizează de către gestorii de excepţii (exception handler).
Când în interiorul unei metode va fi lansată o excepţie sau apelul unei metode lansează o excepţie,
în mod normal, controlul abandonează metoda. Dacă nu se doreşte ca metoda respectivă să fie
abandonată, instrucţiunile ce pot genera cel puţin o excepţie se includ într-un bloc try care
“încearcă” să le execute. Blocul crează un mediu (spaţiu de nume) în interiorul metodei. Dacă apare
o excepţie ca rezultat al executării uneia din instrucţiunile blocului, ea poate fi “prinsă” (catch) de
gestorul excepţiei respective.
Gestorii excepţiilor pentru un bloc try sunt introduşi cu cuvântul cheie catch şi urmează blocul try.
Fiecare gestor de excepţii este ca o metodă care primeşte un unic argument care are tipul unei
excepţii şi va fi executată în cazul în care apare o excepţie de tipil respectiv. Argumentul metodei
poate să nu fie utilizat în codul gestorului, iar alegerea gestorului este în întregime bazată pe tipul
excepţiei.
Pentru acelaşi bloc try putem avea mai mulţi gestori. In acest caz este importantă ordinea gestorilor,
deoarece în cazul apariţiei unei excepţii, se verifică tipul excepţiei cu fiecare gestor în ordinea în
care apar dupa blocul try. Se va executa primul gestor care verifică excepţia, iar restul se ignoră. In
exemplul următor, acest lucru înseamnă că al doilea gestor niciodată nu va trata nici o excepţie,
deoarece primul este mai general şi va trata şi excepţiile de intrare/ieşire (deoarece IOException
este o subclasă a lui Exception). Această situaţie va fi detectată de compilatorul Java şi va
reporta o eroare.
void metoda1(){
try{
//cod care poate produce o exceptie
}catch(IOException ioe){
//cod care gestioneaza exceptia ioe
}
catch(Exception e){
//cod care gestioneaza exceptia e
}
}
Un bloc try poate fi urmat eventual şi de un bloc declarat cu finally. Un bloc finally conţine
instrucţiuni ce vor fi executate întotdeauna, chiar dacă apare o excepţie şi furnizează un spaţiu util
de a scrie cod care să fie executat indiferent ce se întâmplă.
De exemplu, următoarea metodă gestionează interceptarea unei excepţii şi a unei erori şi indiferent
dacă acestea apar sau nu, se va executa codul din blocul finally:
void metoda2(){
try{
//cod care poate produce o exceptie
}catch(Exception e){
58
//cod care gestioneaza exceptia e
}
catch(Error er){
//cod care gestioneaza eroarea er
}
finally{
//cod ce va fi executat indiferent daca apar sau nu exceptia e sau eroarea er
}
}
Gestorul poate prinde o excepţie de tip:
- Exception pentru orice excepţie din bibliotecile Java;
- specializat pentru un pachet Java (de exemplu IOException);
- definit de programator.
Declaraţii throws

Dacă o metodă nu tratează toate excepţiile ce pot apare atunci când este executat corpul său, atunci
ea trebuie să declare în header-ul său toate tipurile de excepţii care pot să apară şi nu le tratează.
Pentru aceasta se inserează o declaraţie throws după parametrii metodei şi înainte de corpul său:
modificatori numeTip numeMetoda(listaParametri) throws listaNumeTip{
//lista de instructiuni
}
unde listaNumeTip::=numeTip [, numeTip]*
Excepţiile declarate cu throws se vor propaga metodei superioare în stivă, aceleia care a apelat-o.
In acest caz, metoda apelantă trebuie să se afle în una din următoarele situaţii:
- metoda este apelată dintr-un bloc try care are gestori capabili să prindă toate tipurile de
excepţii ce pot fi aruncate de metoda apelată.
- metoda este apelată dintr-un bloc try care are gestori capabili să prindă numai unele din
tipurile de excepţii ce pot fi aruncate de metoda apelată. Pentru celelalte tipuri de excepţii,
metoda apelantă trebuie să le declare cu throws şi ele vor fi propagate mai departe.
- metoda este apelată din afara unui bloc try şi atunci toate tipurile de excepţii ce pot fi aruncate
trebuie să apară în declaraţia throw a metodei apelante.
Notă: Excepţiile de tip Error, RuntimeException şi a subclaselor sale nu trebuie să fie
declarate cu throws.
Următoarea metodă returnează un caracter citit de la tastatură:
public char citesteCaracter() throws IOException{
return (char)System.in.read();
}
Lansarea excepţiilor
În timpul proiectării programului sau în timpul depanării, dacă se consideră necesar ca o metodă să
lanseze o excepţie, se execută următorii paşi:
- căutarea clasei de excepţii corespunzătoare;
- dacă metoda nu tratează excepţia apărută, se declară că apelul metodei va lansa o excepţie (cu
ajutorul lui throws şi clasa de exceptie găsită la pasul anterior);
- crearea unui obiect al acelei clase;
- lansarea obiectului creat imediat după punctul în care se verifică excepţia, folosind
instrucţiunea throw;
Exemplu. Metoda următoare citeşte caracter cu caracter o linie de la tastatură pe care o returnează
metodei apelante care trebuie să trateze excepţia de tip IOException sau s-o arunce mai departe
metodei apelante:

59
import java.io.*;
public class Linie{
StringBuffer sb=new StringBuffer();

void readLine() throws IOException{


char c;
while(true){
c=(char)System.in.read();
if (c==13) throw new IOException("Caracterul citit este CR");
sb.append(c);
}
}

public static void main(String[] args){


Linie f=new Linie();
try{
f.readLine();
}catch(IOException e){System.out.println(e.getMessage());}
System.out.println(f.sb.toString());
}
}

Procesul de reportare a unei excepţiei în sus continuă până când excepţia va fi interceptată de o
metodă activă care se angajează să o rezolve sau până când ajunge în vârful stivei şi aplicaţia se
termină.
Următorul exemplu arată cum se poate citi conţinutul unui fişier şi să detectăm sfârşitul fişierului
aruncându-se o excepţie de tip EOFException:
String readData(BufferedReader in){
StringBuffer sb=new StringBuffer();
String s;
try{
while(true){
s=in.readLine();
if (s==null){
in.close();
throw new EOFException("Am ajuns la sfarsitul fisierului");
}
sb.append(s);
}
}catch(IOException ioe){
System.out.println("Exceptie de citire: "+ioe.getMessage());
}
finally{
return sb.toString();
}
}

Observaţie: Cum EOFException este o subclasă a lui IOException, metoda readData()


tratează excepţia apărută, deci ea nu va fi aruncată metodei apelante.
Important: lansând o excepţie, metoda nu o returnează apelului.
Se pot crea clase de excepţii noi. Trebuie să avem o clasă care extinde clasa Exception sau un alt
tip apropiat ca semantică cu excepţia noastră, în care să redefinim constructorii. De exemplu, ne
definim o clasă excepţie numită FileFormatException:
public class FileFormatException extends IOException{
public FileFormatException(){}
public FileFormatException(String mesaj){
super(mesaj);
}
}
Atunci, metoda readData() de mai sus ar putea fi rescrisă astfel:

60
String readData(BufferedReader in){
StringBuffer sb=new StringBuffer();
String s;
try{
while(true){
s=in.readLine();
if (s==null){
in.close();
throw new FileFormatException("Am ajuns la sfarsitul fisierului");
}
sb.append(s);
}
}catch(IOException ioe){
System.out.println("Exceptie de citire: "+ioe.getMessage());
}
finally{
return sb.toString();
}
}
O excepţie poate fi rearuncată şi dintr-un bloc catch. Atunci putem avea mai multe metode din
lanţul de metode active care să trateze excepţia, poate fiecare din acestea indeplinindu-si partea sa
din restaurarea stării programului astfel încât acesta din urmă să-şi poată continua execuţia după ce
excepţia a fost tratată.

61
10. COLECTII
STRUCTURI DE DATE DINAMICE
Presupunem că avem nevoie să utilizăm grupuri de obiecte care au unele proprietăţi
comune, cum ar fi personalul unei întreprinderi, conturile bancare ale clienţilor unei bănci
sau comenzile făcute în ultima perioadă. Numărul componentelor din grup nu este
cunoscut de obicei cunoscut de la început şi poate varia după necesităţile aplicaţiei.
Utilizarea unui astfel de grup este necesară din următoarele motive:
- verificarea că un anumit obiect există în grup,
- adăugarea unui nou obiect la grup,
- eliminarea unui obiect existent din grup.
Deseori trebuie să executăm operaţii care privesc întregul grup, cum ar fi:
- conţinutul obiectelor conţinute,
- verificarea dacă grupul este vid sau conţine cel puţin un obiect,
- adăugarea la grup sau eliminarea din grup a unui grup de obiecte,
- eliminarea tuturor obiectelor din grup.
Astfel de grupuri de obiecte se numesc colecţii şi pentru a le putea elabora, trebuie să proiectăm
algoritmi şi structuri de date corespunzătoare.
TIPOLOGII DE COLECŢII
Pentru a găsi soluţii standard la problema colecţiilor este mai întâi necesar să identificăm
tipurile de colecţii după care modul în care se doreşte elaborarea colecţiei. Pentru fiecare
tip de colecţie, pot fi oferite servicii care satisfac exigenţele programatorilor.
Diferenţele dintre colecţii se datorează fie din exigenţele ce privesc navigarea într-o
colecţie, fie din natura sa: colecţia de obiecte sau relaţii de tipuri funcţionale între două
colecţii, care formează întodeauna o colecţie.
De exemplu, programatorul poate fi interesat de o colecţie în care fiecare componentă se
găseşte într-o poziţie bine stabilită în raport cu alte componente sau colecţia nu impune
nici o ordine poziţională, dar atunci când este navigată se pot întâlni duplicate care pot
crea confuzie. În primul caz avem tipul listă (List) de colecţie, în schimb, în al doilea caz,
avem tipul mulţime (Set).
În schimb, nu este unica posibilitate de a găsi un element fie prin navigarea
componentelor sau prin identificarea lui prin intermediul un număr de ordine. În anumite
aplicaţii poate fi util să găsim o componentă prin intermediul proprietăţilor ei, sintetizate de
o dată cheie. Este cazul relaţiilor care sunt colecţii de legături între o cheie şi componenta
asociată (tipul Map).
În alte situaţii, mult mai complexe, relaţiile dintre componentele unei colecţii nu mai sunt de
ordine totală, ca în cazul listelor, ci de oridne parţială. Este cazul arborilor şi a grafurilor
care necesită modalităţi sofisticate de abordare.
Error! Reference source not found. reaminteşte principalele structuri de date, cu
avantajele şi dezavantajele lor de utilizare.

Structura de date Avantaje Dezavantaje


Array Inserare rapidă, acces rapid dacă indexul este Căutare lentă, eliminare lentă, capacitate
cunoscut limitată
Array ordonat Căutare mai rapidă decât într-un array neordonat Căutare lentă, eliminare lentă, capacitate
limitată
Listă înlănţuită Inserare şi eliminare rapide Căutare lentă
Stivă Acces “last-in, first-out” Acces lent la componentele care nu sunt în
vârf
Coadă Acces “first-in, first-out” Acces lent la componentele care nu sunt în
vârf
Arbore binar Căutare, inserare şi eliminare rapidă (dacă Algoritm de eliminare complex pentru ca
arborele rămâne balansat) arborele să rămână balansat
Arbore binar de Căutare, inserare şi eliminare rapidă. Arborele Este complex.
62
Structura de date Avantaje Dezavantaje
căutare este întotdeauna balansat. Poate fi utilizat pentru
file systems
Arbore rosu-negru Căutare, inserare şi eliminare rapidă. Arborele Este complex.
este întotdeauna balansat.
Tabel hash Acces foarte rapid dacă se cunoaşte cheia. Eliminare lentă, acces lent dacă cheia nu
Inserare rapidă. este cunoscută, utilizează ineficient
memoria.
Heap Inserare şi eliminare rapide Acces rapid la componente mai mari. Acces
lent la alte componente.
Graf Modelează situaţii frecvente în lumea reală Orice algoritm este complex şi lent
10.1.1 Structura de date listă
O listă este o secvenţa finită de zero sau mai multe elemente. Dacă toate elementele sunt de acelaşi
tip T, spunem că lista este de tip T.
Notatie. De obicei, o lista este este scrisă specificandu-se elementele sale ai: (a1, a2, …, an)
Exemple:
Lista numerelor prime 10 numere prime naturale: (2,3,5, 7,11, 13, 17, 19, 23, 29)
O linie de text este o lista de caractere.
Un document este o lista de linii de text.
Lungimea unei liste este numărul de apariţii a elementelor listei. Daca numarul este zero, atunci
lista este vidă. Notatie () sau ε.
Daca o lista este nevidă, atunci primul element se numeste capul listei, iar restul listei formează
coada listei.
Daca L=(a1, a2, …, an) este o listă, atunci pentru orice i şi j a.i. 1≤i≤j≤n, (ai, ai+1, …, aj) este o
sublista a lui L. Exp.
Prefixul unei liste este orice sublista care incepe la inceputul unei liste. Sufixul unei liste este o
sublista care se termină la sfarşitul listei. Exp.
fiecare element din listă are asociată o poziţie. Dacă L=(a1, a2, …, an) şi n≥1 atunci a1 este primul
element, a2 al doilea, samd, an este ultimul element in lista. De asemenea, putem spune că aI apare la
poziţia i in lista. Asadar, numarul de poziţii ale unei liste formeaza lungimea listei.
Operaţii asupra listelor
1. Inserarea unui element x într-o listă L. In principiu x ar putea să apară oriunde in listă şi nu
contează dacă x apare de mai multe ori. Ca un caz special, putem insera pe x la inceputul
listei şi atunci x va fi capul noii liste, iar lista initiala va deveni coada, L=(x, a1, a2, …, an).
2. Stergerea unui element din listă. Se va sterge prima apariţie a elementului x în listă. Daca x
nu apare in lista, atunci operatia de stergere nu are nici un efect.
3. Stergerea tuturor elementelor din lista.
4. Putem cauta un element intr-o lista L. Aceasta operatie este logică, returnand true daca x se
gaseste in lista.
5. prim(L) intoarce primul element al listei şi ultim(L) intoarce ultimul element al listei .
Ambele genereaza o eroare daca lista este vida.
6. elementAt(i, L) returneaza elementul de pe pozitia i a listei L. Avem eroare daca L este de
lungime mai mica decat i.
7. lungime(L) returneaza lungimea listei L.
8. esteVida(L) returnează true daca L nu contine nici un element.
9. concateneaza(L1, L2) două liste L1 şi L2.
Implementarea listelor
Modul cel mai uşor de a implementa o lista este să folosim o lista de noduri legate între ele. Fiecare
nod contine doua campuri:
– primul contine un element al listei,
– al doilea contine o referinta la urmatorul nod al listei.
Implementarea in Java a unui nod al listei este:
public class Nod{

63
private Object element;
private Nod next;
public Nod(Object o){
element=o;
}
public Nod(Object o, Nod n){
element=o;
next=n;
}
}
Atunci, pentru fiecare element al listei avem cate un nod; elementul ai apare în campul element al
nodului i. Variabila next din nodul i contine o referintă la nodul (i+1), pt i=1, n-1. Variabila next al
ultimul nod este null, indicând sfarşitul listei.
De exemplu: lista L=(a1, a2, …, an) este reprezentata ca o listă linked-itata astfel:
a1 a2 … an

Implementarea în Java a operaţiilor asupra listelor linked-itate este realizată în următorul program:
public class Lista{
private Nod cap;
private Lista coada;
private int nr;
public boolean esteVida(){
return lungime()<=0;
}
public int lungime(){
return nr;
}
//adauga un element la sfarsitul listei
public void adaugaElement(Object o){
Nod n=new Nod(o);
if (esteVida()){cap=n;nr++;}
else insereazaElement(o, nr);
}
//inserarea unui element la o anumita pozitie
public void insereazaElement(Object o, int index){
if (esteVida() && index==0){adaugaElement(o); return;}
if (verificaPozitie(index)){
Nod n=new Nod(o);
if(index==0){
n.next=cap;
cap=n;
}
else {
Nod curent=nodLa(index-1);
n.next=curent.next;
curent.next=n;
}
nr++;
}
}
//verificarea pozitiei unui element
private boolean verificaPozitie(int index){
if (index>lungime() || index<0) return false;
return true;
64
}
//returneaza nodul de la o anumita pozitie
public Nod nodLa(int index){
if (verificaPozitie(index)){
if (index==0) return cap;
Nod n=cap;
for(int i=0; i<index;i++) n=n.next;
return n;
}
return null;
}
//returneaza elementul de la o anumita pozitie
public Object elementulDeLa(int index){
Nod n;
if ((n=nodLa(index))!=null) return n.element;
return null;
}
//cauta un element in lista
public int cauta(Object o){
Nod n=cap;
for (int i=0;i<nr;i++){
if (n.element.equals(o)) return i;
else n=n.next;
}
return -1;
}
//returneaza primul element din lista
public Object prim(){
return cap.element;
}
//returneaza ultimul element din lista
public Object ultim(){
return nodLa(nr-1).element;
}
//sterge un element din lista daca acesta exista
public boolean stergeElement(Object o){
int poz=cauta(o);
if(poz==-1) return -1;
Nod curent=nodLa(poz);
if(poz==0) cap=curent.next;
else{
Nod anterior=nodLa(poz-1);
anterior.next=curent.next;
}
curent=null;
nr--;
return true;
}
//sterge toate elementele listei
public void stergeToateElementele(){
if (!esteVida()){
cap=null;
coada=null;
nr=0;
}

65
}
//afiseaza lista sub forma (o1, o2, ..., on)
public String toString(){
StringBuffer sb=new StringBuffer();
sb.append("[");
IteratorLista it=new IteratorLista(cap);
while (it.hasNext()){
sb.append(it.next().toString());
if (it.hasNext()) sb.append(", ");
}
sb.append("]");
return sb.toString();
}
//converteste lista la un array
public Object[] getListaArray{
Object[] o=new Object[lungime()];
int poz=0;
IteratorLista it=new IteratorLista(cap);
while(it.hasNext()){
o[poz]=it.next();
poz++;
}
return o;
}
//concateneaza lista curenta cu o alta lista
public Lista concateneaza(Lista l){
Lista rez=this;
IteratorLista it=new IteratorLista(new Nod(l.prim()));
while (it.hasNext()) rez.adaugaElement(it.next());
return rez;
}
}//clasa Lista

public class IteratorLista{


private Nod curent;
public IteratorLista(Nod c){
curent=c;
}
public boolean hasNext(){
return curent!=null;
}
public Object next(){
Object ret=curent.element;
curent=curent.next;
return ret;
}
}
public class TestLista{
public static void main(String[] args){
Lista l=new Lista();
Lista p=new Lista();
Student[] s=new Student[3];
s[0]=new Student(“Ionescu”, “Ion”, “Informatica”);
s[1]=new Student(“Popescu”, “Mircea”, “Matematica-Informatica”);
s[2]=new Student(“Marinescu”, "Ciorica", “Informatoca”);

66
l.adaugaElement(s[0]);
l.adaugaElement(s[1]);
System.out.println(l);

l.stergeElement(s[0]);
System.out.println("sterge= "+l);

l.insereazaElement(s[2],0);
System.out.println(l);

System.out.println("primul element= "+l.prim());


System.out.println("ultimul element= "+l.ultim());
if(l.cauta(s[0])) System.out.println("elementul se gaseste in lista");
else System.out.println("elementul nu se gaseste in lista");

p.adaugaElement(s[1]);
Lista rez=l.concateneaza(p);
System.out.println(rez);

Object o=rez.elementulDeLa(1);
if (o!=null) {
((Student)o).setMedie(9.35);
System.out.println(o);
}
System.out.println(rez);

l.stergeToateElementele();
System.out.println(l);
}
}
10.1.2 Structura de date stivă
O stivă este un model de date bazat pe modelul listă în care toate operaţiile sunt realizate la un capăt
al listei, care se numeşte vârful stivei. Stiva este o structură de date LIFO adică “last-in first-out
(ultimul intrat, primul ieşit)”.
Modelul abstract al unei stivei este acelaşi cu cel al unei liste – adică o secvenţă de elemente a1,
a2,…, an de un anumit tip. Ceea ce deosebeşte o stivă de o listă este setul de operaţii permise aupra
unei stive.
Operaţii asupra unei stive
Inserarea unui element x într-o stivă S, push(x, S), determină punerea elementului x în vârful stivei
S. Dacă stiva are vârful capătul stâng al stivei, operaţia push(x) aplicată listei (a1, a2,…, an)
generează lista (x, a1, a2,…, an).
Stergerea elementului din vârful stivei este realizată de metoda pop(S). Aceasta va sterge elementul
din vârful stivei şi va fi returnat de către metodă. Dacă stiva este vidă, metoda va genera o eroare.
Stergerea tuturor elementelor din stivă va fi realizată de metoda stergeToateElementele(S).
Metoda lungime(S) va returna lungimea stivei S, iar metoda esteVida(S) returnează true dacă
S nu contine nici un element.
Implementarea stivelor
Pentru a implementa static o stivă vom folosi un array de 10 obiecte:
public class StivaArray{
private Object[] lista;
private Object varf;
private static int nr=10;
67
private int poz;
public StivaArray(){
lista=new Object[nr];
}
public void push(Object x){
if (esteVida()) lista[0]=x;
else {
System.arraycopy(lista, 0, lista, 1, poz);
lista[0]=x;
}
varf=lista[0];
poz++;
}

public Object pop() throws ListaException{


if (esteVida()) throw new ListaException("Stiva este vida");
Object o=varf;
System.arraycopy(lista, 1, lista, 0, poz-1);
poz--;
return o;
}
public void stergeToateElementele(){
for (int i=0; i<poz; i++) lista[i]=null;
varf=null;
poz=0;
}
//verifica daca stiva este vida
public boolean esteVida(){
return lungime()<=0;
}
//returneaza lungimea stivei
public int lungime(){
return poz;
}
public String toString(){
String s="(";
if(poz!=0){
for (int i=0; i<poz-1; i++) s+=lista[i]+", ";
s+=lista[poz-1]+")";
}
else s+=")";
return s;
}
}
In continuare, vom implementa dinamic o stivă cu ajutorul listelor simplu înlănţuite.
public class Stiva{
private Lista l;
private Object varf;
private int nr;
public Stiva(){
l=new Lista();
}
public void push(Object x){
if (esteVida()) l.adaugaElement(x);
else l.insereazaElement(x, 0);
68
try{
varf=l.prim();
}catch(ListaException e){}
nr++;
}
public Object pop() throws ListaException{
if (esteVida()) throw new ListaException("Stiva este vida");
Object o=varf;
try{
varf=l.elementulDeLa(1);
}catch(ListaException le){varf=null;}
l.stergeElement(l.elementulDeLa(0));
nr--;
return o;
}

public void stergeToateElementele(){


l.stergeToateElementele();
varf=null;
nr=0;
}
//verifica daca stiva este vida
public boolean esteVida(){
return lungime()<=0;
}
//returneaza lungimea stivei
public int lungime(){
return nr;
}
public String toString(){
return l.toString();
}
}

10.1.3 Structura de date coadă


O coadă este un model de date bazat pe modelul listă în care elementele sunt inserate pe la un capăt
al listei, numit sfârşit, şi şterse pe la celălalt capăt al listei numit început. Mai precis, o coadă este o
structură de elemente în care putem insera şi sterge elemente după politica FIFO: first-in, first-out.
Dacă coada începe din capătul stâng al listei, operaţia extrage() aplicată listei (a1, a2,…, an)
generează lista (a2,…, an), iar operaţia introduce(x) generează lista (a1, a2,…, an, x).
Operaţii asupra unei cozi
- introduce(C, x) adaugă elementul x la sfârşitul cozii.
- extrage(C) extrage elementul de la începutul cozii. Dacă coada este vidă, atunci operaţia va
genera o eroare.
- stergerea tuturor elementelor din coadă, stergeToateElementele(C).
- lungime(C) returneaza lungimea coadă C.
- esteVida(C) returnează true dacă C nu contine nici un element
Implementarea unei cozi
Vom implementa operaţiilor ce pot fi executate asupra unei cozi cu ajutorul unei liste simplu
înlănţuită.
public class Coada{
private Lista l;
private Object inceput, sfarsit;

69
private int nr;
public Coada(){
l=new Lista();
}
public void introduce(Object x){
l.adaugaElement(x);
nr++;
try{
if (nr==1) inceput=l.prim();
sfarsit=l.ultim();
}catch(ListaException e){}
}
public Object extrage() throws ListaException{
if (esteVida()) throw new ListaException("Stiva este vida");
Object o=inceput;
inceput=l.elementulDeLa(1);
l.stergeElement(l.elementulDeLa(0));
nr--;
return o;
}
public void stergeToateElementele(){
l.stergeToateElementele();
inceput=null;
sfarsit=null;
nr=0;
}
//verifica daca stiva este vida
public boolean esteVida(){
return lungime()<=0;
}
//returneaza lungimea stivei
public int lungime(){
return nr;
}
public String toString(){
return l.toString();
}
}

public class TestStivaSiCoada{


public static void main(String[] args) throws ListaException{
Student[] s=new Student[3];
s[0]=new Student(“Ionescu”, “Ion”, “Informatica”);
s[1]=new Student(“Popescu”, “Mircea”, “Matematica-Informatica”);
s[2]=new Student(“Marinescu”, "Viorica", “Informatica”);
Stiva st=new Stiva();
st.push(s[0]);
st.push(s[1]);
System.out.println(st);
try{
Student o=(Student)st.pop();
System.out.println("scoate= "+o);
st.push(s[2]);
System.out.println(st);
st.stergeToateElementele();
70
System.out.println(st);
}catch(ListaException e){System.out.println(e.getMessage());}
Coada c=new Coada();
c.introduce(s[0]);
c.introduce(s[1]);
System.out.println(c);
try{
Student o=(Student)c.extrage();
System.out.println("scoate= "+o);
c.introduce(s[2]);
System.out.println(c);
c.stergeToateElementele();
System.out.println(c);
}catch(ListaException e){System.out.println(e.getMessage());}
}
}
10.1.4 Structura de date arbore
Un arbore este format dintr-o mulţime de noduri legate prin muchii ce îndeplinesc următoarele
condiţii:
- conţine un nod special, numit nod rădăcină. De aceea, un arbore A se poate defini ca fiind
format dintr-un nod rădăcină de care este legată o mulţime finită de arbori. Aceştia sunt numiţi
subarborii lui A, datorită relaţiei de “subordonare” faţă de rădăcină.
- orice muchie leagă două noduri distincte. O muchie este indicată prin perechea de noduri pe
care le leagă.
- orice nod c diferit de rădăcină este legat de un alt nod p al arborelui printr-o muchie. In acest
caz, nodul c este părintele lui p, iar p este un fiu al lui c. Aşadar, orice nod, cu excepţia nodului
rădăcină, are un singur nod părinte şi poate avea mai mulţi fii. In cazul în care un nod nu are
noduri fii, se numeşte nod frunză (sau terminal).
- un arbore este conectat, în sensul că accesul de la rădăcina unui (sub)arbore nevid la oricare al
nod presupune parcurgerea unei căi formate din a muchii (a≥0) pe care se găsesc n noduri
(n=a+1). Valoarea n reprezintă nivelul pe care se găseşte nodul faţă de rădăcină, al cărei nivel
este, prin convenţie, 1.
Aşadar, o cale într-un arbore este o succesiune de muchii adiacente două cate două, în sensul că
orice două muchii succesive din cale au un nod comun. In plus, putem defini înălţimea unui arbore
ca fiind maximul dintre nivelurile nodurilor frunză.
De exemplu, putem caracteriza următorul arbore astfel:
n1

n2 n3 n4

n5 n6

n7

- rădăcina arborelui este n1,


- are trei subarbori, fiecare cu rădăcina n2, n3 şi n4,
- nodul n3 este părinte pentru nodurile n5 şi n6 şi fiu pentru rădăcină,
- nodurile n2, n5, n7 şi n4 sunt noduri frunză,
- (n1, n3),(n3, n6) este calea de la rădăcină la nodul n6,
- arborele este de nivel 4,

71
- adăncimea arborelui este egală cu 4.
In continuare, vom lucra numai cu arbori binari, adică cu arbori ai căror noduri au cel mult doi fii
(direcţi). In acest caz, cei doi potenţiali subarbori ai unui arbore nevid se numesc subarbore stâng
şi subarbore drept. Similar, cei doi potenţiali fii ai unui nod al unui arbore binar se numesc fiu
stâng, respectiv fiu drept.
Implementarea in Java a unui nod al unui arbore binar este dată în continuare:
public class Nod{
private int info;
private Nod stang, drept;
public Nod(int inf){
info=inf;
}
public Nod(int inf, Nod l, Nod r){
info=inf;
stang=l;
drept=r;
}
public void setInformatie(int info){
this.info=info;
}
public int getInformatie(){
return info;
}
public void setNodStang(Nod s){
stang=s;
}
public Nod getNodStang(){
return stang;
}
public void setNodDrept(Nod s){
drept=s;
}
public Nod getNodDrept(){
return drept;
}
}
Operaţii asupra aborilor binari
Asupra unui arbore binar putem executa următoarele operaţii:
1. modificarea informaţiei memorată în rădăcina arborelui;
2. inserarea unui element în arbore;
3. căutarea unui element în arbore. Operaţia va începe cu rădăcina arborelui şi va returna valoarea
false dacă elementul nu a fost găsit.
4. stergerea unui element din arbore cu returnarea unei valori logice care ia valoarea true dacă
elementul există în arbore.
5. ştergerea întregului arbore.
6. afişarea arborelui. După cum ştim, informaţia din nodurile unui arbore va fi afişată în funcţie de
ordinea în care arborele binar a fost parcurs, începând cu rădăcina arborelui:
- preordine, adică vizitarea nodului curent si apoi a subarborelui stâng, respectiv drept al
acestuia,
- inordine presupune parcurgerea subarborelui stâng al nodului curent, apoi a acestuia din urmă
şi pe urmă subarborele drept,
- postordine, adică nodul curent este vizitat după ce au fost parcurşi cei doi subarbori.

72
In continuare vom exemplifica implementarea operaţiilor de inserare, cautare şi ştergere a unui
element in/dintr-un arbore binar ce îndeplineşte următoarea proprietate: informaţiile nodurilor sunt
ordonate astfel încăt informaţia unui nod oarecare este “mai mare” decât informaţia oricărui nod al
subarborelui stâng al său şi este “mai mică” decât informaţia oricărui nod al subarborelui drept al
său.
Ordonarea este cea obişnuită dacă informaţiile sunt numerice sau este de tip lexicografică dacă
avem de-a face cu caractere sau şiruri de caractere.
Inserarea unui element într-un arbore binar de căutare
Următorul algoritm recursiv descrie paşii necesari pentru ca un element x să fie inserat în arbore A:
Baza. Dacă A este vid, atunci se crează un nod ce conţine elementul x şi acesta va fi rădăcina
arborelui. Altfel, dacă un nod conţine o informaţie egală cu x, atunci x este deja în arbore şi operaţia
se termină.
Pas recursiv. Dacă A nu este vid şi x nu se găseşte ca înformaţie a unui nod oarecare al arborelui,
atunci se inserează x în subarborele stâng al rădăcinii dacă x este mai mic decât informaţia rădăcinii
sau se inserează în subarborele drept al rădăcinii dacă x este mai mare decât informaţia conţinută în
rădăcină. Dacă unul din subarbori nu există şi ne aflăm pe un nivel nou al arborelui, atunci
înălţimea arborelui este incrementată cu o unitate. Pentru aceasta, avem nevoie să ştim nodul părinte
al nodului curent.
Metoda următoare implementează în Java acest algoritm:
private void insereaza(int element, Nod n, Nod p, boolean nou){
if (esteVid()){
rad=new Nod(element);
inaltime=1;
nou=false;
}
else {
if(n.getInformatie()==element) return;
else if(element<n.getInformatie()) {
Nod n1=n.getNodStang();
if (n1!=null) insereaza(element, n1, n, false);
else {
n.setNodStang(new Nod(element, null, null));
if (p==null || p.getNodDrept()==null) nou=true;
}
}
else{
Nod n1=n.getNodDrept();
if (n1!=null) insereaza(element, n1, n, false);
else {
n.setNodDrept(new Nod(element, null, null));
if (p==null || p.getNodStang()==null) nou=true;
}
}
if (nou) inaltime++;
}
}
Căutarea unui element într-un arbore binar de căutare
Următorul algoritm recursiv descrie paşii necesari căutării unui element x în arborele A, începând
bineînţeles cu rădăcina acestuia:
Baza. Dacă A este vid, adică nodul curent (care iniţial va fi rădăcina) analizat este null atunci x nu
se găseşte în arbore.
73
Pas recursiv. Dacă nu suntem în condiţiile bazei, fie y informaţia nodului curent. Dacă x<y, atunci
căutăm x numai în subarborele stâng al nodului curent. Altfel, îl căutăm în subarborele drept.
Metoda următoare implementează în Java acest algoritm:
private boolean cauta(int element, Nod n){
if (n==null) return false;
else if (n.getInformatie()==element) return true;
else if(element<n.getInformatie()) return cauta(element,
n.getNodStang());
else return cauta(element, n.getNodDrept());
}
Stergerea unui element într-un arbore binar de căutare
Următorul algoritm descrie paşii necesari ştergerii unui element x în arborele A:
1. Dacă A nu contine nici un nod ce conţine x, atunci algoritmul se termină cu valoarea false.
2. Altfel, ne aflăm în unul din următoarele cazuri:
- dacă nodul curent este nod frunză, atunci acesta se şterge şi referinţa nodului părinte la nodul
respectiv este setată la null. De aceea, trebuie să ştim ce tip de fiu era pentru nodul părinte:
stâng sau drept.
- dacă nodul curent are un singur nod fiu, atunci primul este înlocuit cu al doilea nod.
- dacă nodul curent este un nod interior, atunci se caută nodul y ce conţine cea mai mică
informaţie din subarborele drept al nodului curent, informaţia mai mare decât x. Apoi nodul y
se şterge din arbore, după ce informaţia conţinută de el este memorată în nodul curent.
Metoda ce determină nodul cu informaţia cea mai mică, dar mai mare decât x este:
int stergeMinim(Nod n, Nod p, String poz){
int min;
if(n.getNodStang()==null){
min=n.getInformatie();
n=n.getNodDrept();
if (poz=="s") p.setNodStang(n);
else p.setNodDrept(n);
return min;
}
else return stergeMinim(n.getNodStang(), n, "s");
}
iar metoda următoare implementează întregul algoritm descris mai sus:
private boolean sterge(int element, Nod n, Nod p, String poz){
if(n!=null){
if(element<n.getInformatie())
return sterge(element, n.getNodStang(), n, "s");
else
if(element>n.getInformatie())
return sterge(element,n.getNodDrept(),n,"d");
else //nodul este un nod frunza
if(n.getNodStang()==null && n.getNodDrept()==null) {
if (poz=="s") p.setNodStang(null);
else p.setNodDrept(null);
if (p.getNodStang()==null && p.getNodDrept()==null) inaltime--;
}
else if(n.getNodStang()==null) n=n.getNodDrept();
else if(n.getNodDrept()==null) n=n.getNodStang();
else //aici ambele noduri fii nu sunt null

74
n.setInformatie(stergeMinim(n.getNodDrept(),n, "d"));
return true;
}
return false;
}
Afişarea informaţiilor unui arbore binar de căutare

Următoarele metode recursive arată diferite modalităţi de a afişa informaţiile unui arbore binar de
căutare prin parcurgerea acestuia din urmă în preordine, inordine sau postordine.
public void afiseaza(String forma){
if (forma.equals("preordine")) preordine(rad);
else if (forma.equals("inordine")) inordine(rad);
else if (forma.equals("postordine")) postordine(rad);
else afiseaza(rad, 0);
}
private void preordine(Nod n){
if (n==null) return;
System.out.println(n.getInformatie());
preordine(n.getNodStang());
preordine(n.getNodDrept());
}
private void inordine(Nod n){
if (n==null) return;
inordine(n.getNodStang());
System.out.println(n.getInformatie());
inordine(n.getNodDrept());
}
private void postordine(Nod n){
if (n==null) return;
postordine(n.getNodStang());
postordine(n.getNodDrept());
System.out.println(n.getInformatie());
}
}
Celelalte operaţii nu necesită explicaţii, iar metodele asociate furnizează interfaţa clasei Arbore:
public class Arbore{
private Nod rad;
private int inaltime;
public Arbore(){}
public Arbore(int inf, Nod st, Nod dr){
rad=new Nod(inf, st, dr);
inaltime=1;
}
public void modificaInformatie(int info){
rad.setInformatie(info);
}
public boolean esteVid(){
return rad==null;
}
public void stergeArbore(){
rad=null;
}
public Nod getRadacina(){
return rad;
75
}
public int getInaltime(){
return inaltime;
}
public void insereaza(int element){
insereaza(element, rad, null, false);
}
public boolean cauta(int element){
return cauta(element, rad);
}
public boolean sterge(int element){
return sterge(element, rad, null, null);
}
public void afiseaza(String forma){
if (forma.equals("preordine")) preordine(rad);
else if (forma.equals("inordine")) inordine(rad);
else if (forma.equals("postordine")) postordine(rad);
else afiseaza(rad, 0);
}
}
COLECŢII ÎN JAVA
Java furnizează o structură de clase şi interfeţe numită Collections Framework specializată în
memorarea şi elaborarea colecţiilor de obiecte.
Collections Framework furnizează un API convenabil oricăror tipuri de date abstracte utilizate
frecvent în informatocă: relaţii, mulţimi, liste, arbori, array, etc. Datorită orientării spre obiecte,
clasele din Collections Framework încapsulează atât structura de date cât şi algoritmii asociaţi
acestor abstractizări.
10.1.5 Teoria de bază a colecţiilor
La baza teoriei colecţiilor stă conceptul matematic de mulţime, chiar dacă colecţia a fost definită
având o semnificaţie mai generală, de grup de elemente, fără nici o legătură între ele. În schimb, în
Collections Framework o mulţime este o colecţie de elemente unice, adică o colecţie fără elemente
duplicat. Exemple de mulţimi sunt numeroase. Iată unele:
- mulţimea literelor mari de la 'A' la 'Z'
- mulţimea numerelor nenegative: {0, 1, 2 ...}
- mulţimea cuvintelor cheie din Java: {'import', 'class', 'public', 'protected'...}
- mulţimea angajaţilor unei întreprinderi
- mulţimea obiectelor Component dintr-un Container
- mulţimea vidă {}
Un caz particular de mulţime este relaţia funcţională sau maparea (în engleză, mapping). Este o
mulţime de legături în care fiecare legătură reprezintă o conexiune de la un element la un altul.
Exemple de mapări sunt:
- maparea adreselor IP la nume dintr-un domeniu de nume DNS,
- dicţionar (maparea cuvintelor la înţelesurile lor),
- conversia unui sistem de numeraţie la un alt sistem de numeraţie (sistemul arabic la cel roman).

76
O mapare poate servi la navigarea de la un element al unei legături la un alt element. Pentru mapare,
biblioteca Java conţine clase ce furnizează rapid obiectele căutate indicând numai cheia asociată.

Cheie1 legatura1 Valoare1

Cheie2 legatura2 Valoare2

Cheie3 legatura3 Valoare3

Cheie4 legatura4 Valoare4

Cheie5 legatura5 Valoare5

10.1.6 Collections Framework


Collections Framework este furnizat în pachetul java.util şi conţine interfeţe care se găsesc în relaţii
de generalizare/specializare ca cea prezentată în figura următoare. Unele interfeţe sunt
implementate de clase Java ce aparţin aceluiaşi pachet java.util. Aceste clase reprezintă colecţii de
obiecte sau relaţii între obiecte şi sunt prezentate în tabelul următor.

Collection Map

Set List SortedMap

SortedSet

Interfaţa Implementare Precedenţi istorici


Set HashSet
SortedSet TreeSet
List ArrayList LinkedList Vector
Stack
Map HashMap Hashtable
Properties
SortedMap TreeMap

77
Schema completă a lui Collections Framework este prezentată în figura următoare. Schema prezintă
în diferite nuanţe de gri interfeţele, clasele abstracte şi clasele concrete din Collections Framework
şi relaţiile dintre ele. În plus, sunt marcate clasele care provin din versiuni mai vechi cum ar fi
Vector, Stack, Hashtable şi Properties. Collections Framework mai conţine şi alte interfeţe cum ar fi
Iterator, ListIterator, Comparator şi Comparable care permit navigarea colecţiilor şi compararea
elementelor dintr-o colecţie.
produce produce
<interface> <interface> <interface> <abstract>
Iterator Collection Map Dictionary

<interface> <interface> <interface> <abstract> <interface> Hashtable


ListIterator List Set AbstractMap SortedMap

<interface> <abstract> HashMap TreeMap Properties


Comparator AbstractCollection

<interface>
Comparable
<abstract> <abstract> <interface>
AbstractList AbstractSet SortedSet
Legenda

Hashtable Clase din versiunile 1.0, 1.1


HashSet TreeSet
<interface> Interfeţe fundamentale
Map

Vector ArrayList <abstract>


AbstractSequentialList Clase concrete utile
HashMap

Stack
LinkedList

Interfaţa Collection
java.util.Collection este o interfaţă standard ce reprezintă un grup de elemente, numite elementele
colecţiei. Biblioteca Java nu conţine o implementare directă a interfeţei Collection. Această interfaţă
este utilizată ca numitor comun al altor interfeţe, pentru a permite transferul colecţiilor de la un tip
de colecţie la un alt tip şi elaborarea lui cu generalitate maximă. Operaţiile interfeţei Collection
sunt prezentate în tabelul următor.
Declaraţie metodă Semnificaţie
Operaţii de bază
boolean add(Object o) Verifică dacă colecţia conţine elementul o. În caz pozitiv, nu mai adaugă
elementul o şi întoarce false. În caz negativ, adaugă elementul al colecţie şi
returnează true.
boolean remove(Object o) Dacă elementul o se găseşte in colecţie, este eliminat şi meoda returnează
true. Altfel, metoda returnează false.
boolean contains(Object o) Returnează true dacă colecţia conţine elementul o
Iterator iterator() Întoarce un obiect de tip Iterator care se poate utiliza pentru navigarea
colecţiei.
int size() Întoarce numărul de elemente ale colecţiei
boolean isEmpty() Returnează true dacă colecţia nu are elemente.
Operaţii pe grupuri de obiecte
boolean addAll(Collection c) Adaugă toate elementele colecţiei c. Dacă operaţia are loc cu succes, metoda
întoarce true.
boolean removeAll(Collection c) Elimină toate elementele colecţiei c. Dacă operaţia are loc cu succes, metoda
întoarce true.
78
Declaraţie metodă Semnificaţie
Boolean retainAll(Collection c) Elimină toate elementele care nu fac parte din colecţia transmisă ca
argument.
void clear() Goleşte colecţia
Operaţii cu array
Object[] toArray() Returnează un array ce conţine toate elementele colecţiei.
Object[] toArray( Object []) Returnează un array ce conţine toate elementele colecţiei. Array-ul returnat
este de acelşi tip cu array-ul transmis ca parametru.
Iterfaţa Iterator
Iterator este o interfaţă implementată de un obiect obţinut prin invocarea metodei iterator() a unui
obiect din clasa Collection. Interfaţa conţine trei metode:
boolean hasNext()//verifică dacă colecţia mai mai are elemente de parcurs
Object next() //returnează următorul element al colecţiei
void remove() //poate fi apelată numai o singură pentru fiecare obiect
//obţinut cu metoda next()
Iteratorul ne permite să navigăm într-o colecţie pănă la terminarea elementelor conţinute. În timpul
navigării putem, de exemplu, să filtrăm o colecţie, adică să eliminăm din colecţie acele elemente
care nu îndeplinesc o anumită condiţie. Presupunem că avem o metodă
verificaEliminare(Object o) care întoarce true dacă obiectul transmis ca parametru va fi
efectiv eliminat. Folosind interfaţa Iterator, filtrul va fi implementat astfel:
Collection colectie = ...;
Iterator iterator = colectie.iterator();
while (iterator.hasNext()) {
Object element = iterator.next();
if (verificaEliminare(element)) {
iterator.remove();
}
}
Interfaţa Set
Collections Framework include o interfaţă Set şi numeroase clase concrete care implementează Set.
Interfaţa Set extinde interfaţa Collection şi împiedică crearea de elemente duplicat în colecţie. Nu
introduce alte metode diferite de cele din Collection. Verificarea claselor de implementare se
bazează pe metoda equals() a obiectului care este adăugat la colecţie. Interfaţa Set este
implementată de două clase concrete Java: java.util.HashSet şi java.util.TreeSet.
Obiectele din clasa HashSet sunt utilizate pentru memorarea colecţiilor fără duplicate. Obiectele ce
vor fi adăugate la un HashSet trebuie să implementeze metoda hashCode() astfel încât obiectele vor
fi egal distribuite în mod uniform cu ajutorul codurilor hash. Majoritatea claselor standard
redefinesc implementarea predefinită de clasa Object.
Clasa TreeSet este utilizată când vrem să lucrăm cu elementele unei colecţii ordonate. Obiectele
care se adaugă la un TreeSet trebuie să fie ordonabile, adică să poată fi comparate între ele după o
relaţie de ordine totală.
Clasa HashSet
Clasa implementează interfaţa Set şi este construită intern de un “hash table”. Figura următoare
prezintă schematic ideea care stă la baza unei structuri de de date de tip hash table. În centrul
structurii se găseşte un array ale cărui elemente conţin indicatori la lista înlănţuită ale cărei noduri
indică elemente ale colecţiei. Indexul tabelului variază între 0 şi un k-1. Căutarea într-o astfel de
structură începe cu calculul indicelui hash al obiectului care este căutat. Acest cod este un număr
întreg calculat după un algoritm particular care implică toate câmpurile obiectului văzute ca nişte
combinaţii de biţi. Odată găsit codul, el va fi folosit ca indice al array-ului central. Elementul
identificat poate conţine null, şi atunci obiectul căutat nu se găseşte în colecţie şi, adcă este necesar,
se poate adăuga, sau este vorba de un indicator la o listă “bucket” în care obiectul va fi căutat
79
secvenţial. Dacă nu este găsit în listă, înseamnă că nu se găseşte în colecţie şi poate fi adăugat la
lista de bucket. Într-o tabelă hash nu se poate prevede care va fi ordinea de navigare între elemente,
dar această ordine rămâne întotdeauna aceeaşi.
Clasa permite utilizarea unui element null în colecţie.

f i

Constructorii clasei HashSet sunt:


HashSet() //creaza o colectie HashSet vida
HashSet (Collection c) //creaza o colectie de tip HashSet ce contine
//elementele lui c
HashSet(int capacitate)//creaza o colectie HashSet de capacitate initiala
//transmisa ca argument
HashSet(int capacitate, int incr)//creaza o colectie HashSet de
capacitate initiala şi factor de încărcare transmise ca argumente
Exemplu. Vom scrie un program care preia cuvinte din linia de comandă şi vizualizează cuvintele
duplicat, numărul de cuvinte distincte, având într-o listă cuvintele unice:
import java.util.*;
public class Duplicate {
public static void main(String args[]) {
Set s = new HashSet();
for (int i=0; i<args.length; i++)
if (!s.add(args[i]))
System.out.println("Duplicat: "+args[i]);
System.out.println(s.size()+" cuvinte distincte identificate: "+s);
}
}
Exemplu. Presupunem că vrem să ştim care din cuvintele din linia de comandă au apărut o singură
dată şi care au apărut de mai multe ori, dar le afişăm numai o singură dată. Putem utiliza două
obiecte din clasa Set. Într-un obiect memorăm cuvintele liniei de comandă şi în al doilea obiect
memorăm numai cuvintele duplicat. Cuvintele care apar numai o singură dată se deduc din diferenţa
celor două mulţimi.
import java.util.*;
public class Duplicate1 {
public static void main(String args[]) {
Set unice = new HashSet();
Set dupl = new HashSet();
for (int i=0; i<args.length; i++)
if (!unice.add(args[i])) dupl.add(args[i]);
unice.removeAll(dupl); // Diferenta dintre multimi
System.out.println("Cuvinte unice: " + unice);
System.out.println("Cuvinte duplicat: " + dupl);
}
}
80
Clasa TreeSet
Clasa implementează SortedSet care este o subinterfaţă a lui Set şi garantează că mulţimea este
ordonată în ordine crescătoare. Ordonarea este cea naturală între elemente (care trebuie să
implementeze Comparable) sau va fi specificată prin intermediul unui obiect comparator (face parte
dintr-o interfaţă Comparator) la crearea lui TreeSet.
Constructorii clasei TreeSet sunt:
TreeSet()
TreeSet (Collection c)
TreeSet(Comparator comp)
TreeSet(SortedSet ss)
Al doilea constructor va fi utilizat în următorul program în care transmit elementele unei colecţii
HashSet unui TreeSet:
import java.util.*;
public class ExempluSet {
public static void main(String args[]) {
Set set = new HashSet();
set.add("Andreea");
set.add("Maria");
set.add("George");
set.add("Maria");
set.add("Clara");
System.out.println(set);
Set sortedSet = new TreeSet(set);
System.out.println(sortedSet);
}
}
Un alt exemplu de utilizare a clasei TreeSet este prezentat în continuare şi ilustrează utilizarea
elementelor ce pot fi comparate (Comparable) şi pot fi păstrate ordonate în interiorul unui TreeSet.
Fiind şi un Set, un TreeSet poate fi configurat la creare pentru accesarea obiectelor care respectă o
anumită relaţie între ele:
import java.util.*;
class ComparatorArticole implements Comparator {
public int compare(Object a, Object b){
Articol articolA = (Articol)a;
Articol articolB = (Articol)b;
String descrA=articolA.getDescriere();
String descrB=articolB.getDescriere();
return descrA.compareTo(descrB);
}
}
class Articol implements Comparable{
private String descriere;
private int numarParte;
public Articol(String oDescriere,int unNumarParte){
descriere = oDescriere;
numarParte = unNumarParte;
}
public String getDescriere(){
return descriere;
}
public String toString(){
return "[descriere=" + descriere + ", numarParte=" + numarParte + "]";
}
public boolean equals(Object alt){
81
if (alt instanceof Articol){
Articol altArticol = (Articol)alt;
return descriere.equals(altArticol.descriere) &&
numarParte == altArticol.numarParte;
}
else return false;
}
public int hashCode() {
return 13 * descriere.hashCode() + 17 * numarParte;
}
public int compareTo(Object alt){
Articol altArticol = (Articol)alt;
return numarParte - altArticol.numarParte;
}
}
public class TestTreeSet {
public static void main(String[] args) {
SortedSet parti = new TreeSet();
parti.add(new Articol("Monitor", 1234));
parti.add(new Articol("Imprimanta", 4562));
parti.add(new Articol("Modem", 9912));
System.out.println(parti);
SortedSet ordoneazaDupaDescriere=new TreeSet(new ComparatorArticole());
ordoneazaDupaDescriere.addAll(parti);
System.out.println(ordoneazaDupaDescriere);
}
}
Interfaţa List
List este o interfaţă care reprezintă o colecţie ordonată (numită şi secvenţă). Prin intermediul acestei
interfeţe, utilizatorul are un control exact al locului în care este inserat fiecare element. Utilizatorul
poate accesa elementele prin intermediul iteratorilor şi a indicilor întregi care reprezintă poziţia în
listă. În acest mod căutăm mai uşor în lista.
Listele acceptă elemente duplicat (adică, elemente e1 şi e2 care verifică e1.equals(e2)) şi acceptă şi
elemente null.
În afara declaraţiilor de metode din interfaţa Collection, List are următoarele metode:
Declaraţie metodă Semnificaţie
Acces după poziţie
Object get(int indice) Întoarce obiectul de la indicele transmis ca parametru
boolean add(int indice, Object o) Inserează obiectul o la poziţia indice
Object remove(int indice) Elimină obiectul de la poziţia indice
void set(int indice, Object o) Înlocuieşte obiectul de la poziţia indice cu obiectul o
boolean addAll(int indice, Inserează începând cu poziţia indice elementele colectiei
Collection c) c
Căutarea unui obiect
int indexOf(Object o) Returnează poziţia obiectului o din listă. Dacă nu-l
găseşte, returnează -1.
int lastIndexOf(Object o) Returnează poziţia ultimei apariţii a obiectului o din
listă. Dacă nu-l găseşte, returnează -1.
Iterare
ListIterator listIterator() Returnează un obiect de tip ListIterator al lui List
ListIterator listIterator(int Returnează un obiect de tip ListIterator al lui List. Cei

82
Declaraţie metodă Semnificaţie
indice) doi iteratori sunt folosiţi de LinkedList.
Operaţie globală
List subList(int in, int sf) Crează o sublistă formată din elementele listei între cei
doi indici transmişi ca parametri.

Ca întotdeauna când este vorba de o interfaţă, putem implementa în mod polimorfic algoritmi valizi
pentru toate obiectele claselor care implementează interfaţa. De exemplu, pentru a schimba locul
între două elemente ale unei liste poate fi utilizată următoarea metodă:
private void schimba(List a, int i, int j) {
Object t = a.get(i);
a.set(i, a.get(j));
a.set(j, t);
}
Vom utiliza metoda schimba() pentru a permuta elementele unei liste. Algoritmul polimorfic utilizat
parcurge lista şi generează numere aleatoare pentru a identifica locuri noi din care putem lua
elementul cu care înlocuim elementul curent:
public void permuta(List list, Random rnd) {
for (int i=list.size(); i>1; i--)
schimba(list, i-1, rnd.nextInt(i));
}
O variabilă de tip List poate conţine referinţe la obiecte ArrayList sau LinkedList care sunt
implementări ale interfeţei. Aceşti algoritmi sunt independenţi de clasa utilizată pentru
implementarea listei.
Interfaţa ListIterator
Interfaţa List furnizează un iterator mai bogat: ListIterator, care ne permite să traversăm lista în
ambele direcţii, să o modificăm în timpul traversării şi să obţinem poziţia curentă a iteratorului.
Interfaţa ListIterator este prezentată în continuare:
interface ListIterator extends Iterator {
void add(Object elem); // adaugă un element în poziţia curentă
Object previous(); //poziţionează cursorul şi returnează elementul
//precendent
boolean hasPrevious(); // verifica dacă există un element precedent
void set(Object elem); // suprascrie elementul curent cu elementul
//transmis ca parametru
int nextIndex(); // returnează indicele elementului succesiv
int previousIndex(); // returnează indicele elementului succesiv
}
Spre deosebire de metoda Collection.add(), metoda ListIterator.add() nu returnează o valoarea de
tip boolean.
Metoda nextIndex() returnează întotdeauna elementul care va fi restituit de un apel succesiv al lui
next(), în schimb previousIndex() returnează poziţia elementului întors la un apel al lui previous().
În consecinţă, nextIndex() == previous()+1. Apoi, un alt fapt: la primul apel al lui previous() după o
secevnţă de apeluri next() întoarce acelaşi element al ultimului apel al lui next. În mod similar,
primul apel al lui next() după o serie de apeluri ale lui previous() returnează acelaşi element al
utlimului apel al lui previous() .
Exemplu următor este cel al unei metode statice care implementează un algoritm polimorfic care
înlocuieşte într-o listă oarecare toate apariţiile unui obiect val cu un obiect nou:
public static void replace(List l, Object val, Object nou) {
for (ListIterator i = l.listIterator(); i.hasNext(); )
if (val==null ? i.next()==null : val.equals(i.next())) i.set(nou);
}
83
In schimb, metoda următoare implementează un algoritm polimorfic care returnează toate apariţiile
valorii val specificată ca parametru, cu o secvenţă de valori noi conţinute într-o listă:
public static void replace(List l, Object val, List valoriNoi) {
for (ListIterator i = l.listIterator(); i.hasNext(); ) {
if (val==null ? i.next()==null : val.equals(i.next())) {
i.remove();
for (Iterator j = nuoviValori.iterator(); j.hasNext(); )
i.add(j.next());
}
}
}
Clasa ArrayList
ArrayList este o clasă concretă care implementează interfaţa List şi se sprijină pe un array fără
limite de creştere. Modelează o listă cu elemente enumerate, deoarece are un acces obişnuit, direct,
relativ rapid.
La metodele interfeţei List, clasa ArrayList adaugă următoarele metode:
ArrayList(Collection)//construieşte un ArrayList dintr-un Collection
Object clone() //clonează obiectul ArrayList
void ensureCapacity(int) //garantează un număr minim pentru listă
void trimToSize() //Elimină spaţiile libere din array
În continuare, prezentăm un program care extrage virtual dintr-un pachet de cărţi – reprezentat ca o
listă - n cărţi pentru un jucător şi le furnizează întotdeauna ca o listă:
public static List furnizeazăCarte(List pachet, int n) {
int dimPachet = pachet.size();
List listaNoua = pachet.subList(dimPachet-n, dimPachet);
List cartiJucator = new ArrayList(listaNoua);
listaNoua.clear();
return cartiJucator;
}
Clasa LinkedList
LinkedList este o clasă concretă care furnizează un acces secvenţial optim cu operaţii economice
de eliminare şi inserare în interiorul listei. Are un acces aleator relativ lent (dacă accesul aleator este
frecvent trebuie să fie utilizat ArrayList). Cu un obiect LinkedList putem implementa cod deoarece
garantează o politică FIFO în elaborarea listei.
LinkedList adaugă următoarele metode la cele declarate de interfaţa List :
void addFirst( Object o) //adaugă o la începutul listei
void addLast(Object o) //adaugă o la sfârşitul listei
Object getFirst( ) //furnizează primul element din listă
Object getLast( ) //furnizează ultimul element al listei
Object removeFirst( ) //elimină primul element al listei
Object removeLast( ) //elimină ultimul element al listei
Următorul program ilustrează ilustrează trasferul uşor de la o implementare a unei liste al o alta şi
modul diferit în care cele două tipologii de liste îşi gestionează elementele:
import java.util.*;
public class ExempluLista {
public static void main(String[] args) {
List lista = new ArrayList();
lista.add("Andreea");
lista.add("Eliza");
lista.add("George");
lista.add("Eliza");
lista.add("Clara");

84
System.out.println(lista);
System.out.println("2: " + lista.get(2));
System.out.println("0: " + lista.get(0));
LinkedList coada = new LinkedList();
coada.addFirst("Andreea");
coada.addFirst("Eliza");
coada.addFirst("George");
coada.addFirst("Eliza");
coada.addFirst("Clara");
System.out.println(coada);
coada.removeLast();
coada.removeLast();
System.out.println(coada);
}
}
Rezultat:
[Andreea, Eliza, George, Eliza, Clara]
2: George
0: Andreea
[Clara, Eliza, George, Eliza, Andreea]
[Clara, Eliza, George]
Cum utilizăm interfaţa Collection
1. Ştim că utilizarea interfeţelor este obligatorie atunci când obiectele client nu trebuie să ştie
provenienţa obiectului (clasa care l-a generat) şi în general orice detalii de implementare care su
fost utilizate. În acest mod, pentru un client, dacă două obiecte a şi b implementează aceiaşi
interfaţă, se poate utiliza obiectul b în locul obiectului a, fără să necesite nici o modificare din
parte clientului. Este de ajuns să utilizeze o variabilă interfaţă în care să memoreze referinţa la
unul din cele două obiecte (figura următoare).
În consecinţă, nu contează ce obiect Java este utilizat pentru a reprezenta o colecţie şi dacă în mod
succesiv, acest obiect este înlocuit cu un altul care implementează aceeaşi interfaţă.
2. Implementările colecţiilor (ArrayList, LinkedList, HashSet, TreeSet) au toate un constructor cu
un parametru de tip Collection. Acest parametru este un obiect dintr-o clasă care implementează
interfaţa Collection şi care va fi utilizat pentru a iniţializa noua colecţie. În acest mod, noua
colecţie va fi iniţializată cu elementele colecţiei utilizată ca parametru, dar va avea un
comportament diferit.
În continuare, va fi prezentat un scenariu posibil de utilizare a varaibilelor de tip Collection:
1. Declarăm o variabilă colectie de tip Collection (poate fi un List sau un Set):
Collection colectie;
2. colectie memorează o referinţă la un obiect care implementează o colecţie, precum un
LinkedList, ArrayList, HashSet sau TreeSet:
colectie = new HashSet();
3. Se adaugă obiecte la colectie cu metoda add():
colectie.add(o1); colectie.add(o2); . . .
4. Se crează o lista de tip List ce se bazează pe un array, construită pe baza lui colecţie:
List lista = new ArrayList(colectie);
3. lista conţine elementele din colecţie memorate în HashSet, schimbând numai punctul de
vedere. Într-un List, elementele unice ale lui HashSet pot fi căutate în ordine şi poate accepta
duplicate.

85
Variabilă de tip a – implementare a lui Collection
Collection b- o altă implementare a lui Collection
b
a
Client
Înlocuirea unei implementări
a lui Collection cu o alta

Interfaţa Map
Map este o interfaţă care păstrează o asociere chei-valoare şi care permite căutări bazate pe chei.

Declaraţie metodă Semnificaţie


Operaţii de bază
Object get(Object cheie) Întoarce obiectul cu cheia transmisă ca parametru
void put(Object cheie, Object o) Inserează o legătură cheie-valoare
Object remove(Object cheie) Elimină legătura indicată de cheie
Boolean containsKey(Object cheie) Verifică existenţa cheii cheie în Map
Boolean containsValue(Object o) Verifică existenţa valorii o în Map
Int size() Returnează numărul de legături
Boolean isEmpty() Verifică dacă mulţimea legăturilor este vidă sau nu
Operaţii globale
Boolean putAll(Map m) Adaugă legăturile găsite în m
Void clear() Goleşte un Map
Vederi ca colecţii
Set entrySet() Furnizează Set-ul tuturor legăturilor cheie-valoare
Set keySet() Furnizeaza un Set compus din chei
Collection values() Furnizează o colecţie a tuturor valorilor din Map

Vederile (views) ale unui Map ne permit să utilizăm interfetele Collection (pentru valori) sau Set
(pentru chei) pentru eleborarea întregului Map:
for (Iterator i=map.keySet().iterator(); i.hasNext(); ) {
Object cheie = i.next();
System.out.println(cheie + ": " + map.get(cheie));
}
Map.Entry este o interfaţă statică internă a lui Map care reprezintă toate legăturile cheie-valoare din
Map. Map.Entry are următoarele trei metode:
Object getKey()
Object getValue()
Object setValue(Object val)
Fiecare element din Set-ul de legături returnat de metoda entrySet()este văzut ca un element de tip
Map.Entry. De aceea, putem parcurge o astfel de mulţime şi să afişăm fie cheia, fie valoarea
fiecărei legături. Acest lucru este realizat de următoarea secvenţă de cod:
Map m=…//crearea unui obiect al unei clase ce implementează interfaţa Map
for (Iterator i=m.entrySet().iterator(); i.hasNext(); ) {
Map.Entry e = (Map.Entry) i.next();
86
System.out.println(e.getKey() + ": " + e.getValue());
}
Interfaţa Map are două implementări principale: HashMap şi TreeMap şi una moştenită din prima
versiune de Java: Hashtable.
Clasa HashMap
Următorul exemplu verifică numerele aleatoare furnizate de metoda Math.random().
import java.util.*;
class Counter { //Obiectele acestei clase vor fi folosite ca numărători
int i = 1;
public String toString() {
return Integer.toString(i);
}
}
public class FreqHashMap {
public static void main(String[] args) {
HashMap ht = new HashMap();
for(int i = 0; i < 10000; i++) {
Integer r = new Integer((int)(Math.random()*20)); //Produce un număr
între
//0 şi 19 inclusiv
if(ht.containsKey(r)) ((Counter)ht.get(r)).i++;
else ht.put(r, new Counter());
}
System.out.println(ht);
}
}
Clasa TreeMap
Clasa TreeMap furnizează o implementare a interfeţei Map în care colecţia de legături chei-valoare
este ordonată după chei, implementând şi interfaţa SortedMap. Implicit, ordonarea este crescătoare
(conform interfeţei Comparable), dar criteriul de comparare poate fi specificat prin constructor:
TreeMap (Comparator c)
Alţi constructori:
TreeMap ()
TreeMap (Map m)
TreeMap (SortedMap m)
Exemplu. Următorul program ilustrează diferenţele dintre HashMap şi TreeMap:
import java.util.*;
public class FreqTreeMap {
public static void main(String args[]) {
Map map=new HashMap();
Integer unu=new Integer(1);
for (int i=0, n=args.length; i<n; i++) {
String cheie=args[i];
Integer frecventa=(Integer)map.get(cheie);
if (frecventa==null) frecventa = unu;
else {
int valoare = frecventa.intValue();
frecventa = new Integer(valoare + 1);
}
map.put(cheie, frecventa);
}
System.out.println(map.size()+" cuvinte distincte gasite:");
System.out.println(map);
87
Map mapSortat = new TreeMap(map);
System.out.println(mapSortat);
}
}
Clasa Properties
Furnizează o colecţie persistentă de legături chei-valori in care cheile şi valorile sunt String-uri. Din
acest motiv, valorile se numesc proprietăţi. O legătură cheie-proprietate este adăugată la un
Properties cu metoda Object setProperty(String key, String value) şi se poate obţine
cu metoda overloaded getProperty():
String getProperty(String cheie)
String getProperty(String cheie, String proprietateImplicita)
Persistenţa colecţiei permite memorarea şi încărcarea pe şi de pe suport persistent (sistem de
fişiere). În acest sens, clasa Properties furnizează următoarele metode instanţă:
public void store(OutputStream out, String header)
public void list(PrintStream out)
public void list(PrintWriter out)
public void load(InputStream in)
De exemplu, memorarea într-un fişier a conţinutului unui obiect Properties se realizează prin
următoarea secvenţă de cod:
Properties p=new Properties();
p.setProperty("1", "Ioana");
p.setProperty("4", "Maria");
try{
p.store(new FileOutputStream("proprietati.txt"), " Colectie de
proprietati");
}catch(IOException e){System.out.println(e.getMessage());}
Ieşire:
# Colectie de proprietati
#Tue Jan 04 10:03:53 EET 2005
4=Maria
1=Ioana

Colecţia de legături şi proprietăţi memorată într-un obiect Properties poate fi afişată şi pe ecran prin
apelarea metodei list, astfel: p.list(System.out);, unde p este variabila referinţă din exemplul
anterior.
Exemplu.
import java.util.*;
public class Catalog extends TreeSet{
private static Catalog c;
private OperatiiSpecificatiiProdus osp;

private Catalog(){osp=new OperatiiSpecificatiiProdus(); }

public static Catalog getInstanta(){


if(c==null) c=new Catalog();
return c;
}

public void add(SpecificatieProdus sp){super.add(sp);}

public SpecificatieProdus getProdus(String nume){


Iterator it=iterator();
SpecificatieProdus sp=null;

while(it.hasNext()){
sp=(SpecificatieProdus)it.next();
88
if(sp.getDenumire().equals(nume)) return sp;
}
sp=(SpecificatieProdus)osp.citesteObiect(nume);
if (sp!=null) add(sp);
return sp;
}

public void descarca(){


Iterator it=iterator();
while(it.hasNext()) osp.scrieObiect(it.next());
}
}
public class TestCatalog{
public static void main(String[] args){
Catalog c=Catalog.getInstanta();
c.add(new SpecificatieProdus("Imprimanta", 250));
c.add(new SpecificatieProdus("Calculator", 1300));

SpecificatieProdus sp=c.getProdus("miere");
System.out.println(sp);
sp=c.getProdus("Imprimanta");
System.out.println(sp);
}
}
import java.util.*;
public class Vanzare extends ArrayList{
private Client c;
public static int tva=20;
private double total;

public Vanzare(Client c){this.c=c;}

public Client getClient(){return c;}

public void addProdus(ElementVanzare ev){super.add(ev);}

public double calculeazaTotal(){


ListIterator li=listIterator();
ElementVanzare ev;
while(li.hasNext()) {
ev=(ElementVanzare)li.next();
total+=ev.calculeazaCost();
}
Calendar cl=Calendar.getInstance();
if(cl.get(Calendar.DAY_OF_WEEK)>=2&&cl.get(Calendar.DAY_OF_WEEK)<=6)total-
=5*total/100.0;
return total+calculeazaTVA();
}

public ArrayList getProduse(){return this; }

public double calculeazaTVA(){return total*tva/100.0; }


}
import java.util.*;import java.text.*;
public class Factura{

private static int id=1;


private int nrFactura;
private Calendar data;
private Vanzare v;
private Client c;
private ArrayList articole;

public Factura(Vanzare v){


nrFactura=id++;
data=Calendar.getInstance();
89
this.v=v;
c=v.getClient();
articole=v.getProduse();
}
public String formatare(String sir, int latime) {
String temp=sir;
for (int i=latime-sir.length(); i>0; --i) temp += " ";
return temp;
}
public String toString(){
String sir="Factura: "+nrFactura+"\r\n";
sir+="Data emiterii: "+
DateFormat.getDateInstance(DateFormat.LONG).format(data.getTime())+"\r\n"
sir+="------------------------------------------------------------------------
\r\n\r\n";
sir+="Cumparator: "+c.getNume()+" "+c.getPrenume()+"\r\n";
sir+="Adresa: "+c.getAdresa()+"\r\n";
sir+="------------------------------------------------------------------------
\r\n\r\n";
sir+=formatare("Denumire", 15)+ formatare("Pret unitar",
15)+formatare("Cantitate", 20)+formatare("Pret/articol", 25)+"\r\n";
Iterator it=articole.iterator();
ElementVanzare el;
double t=v.calculeazaTotal();
while(it.hasNext()){
el=(ElementVanzare)it.next();
sir+=formatare(""+el.getProdus().getDenumire(),
15)+formatare(""+el.getProdus().getPret(), 15)+formatare(""+el.getCantitate(),
20)+formatare(""+el.calculeazaCost(), 25)+"\r\n";}
sir+="------------------------------------------------------------------------
\r\n\r\n";
sir+="TOTAL\t\t"+t+" lei \r\n";
sir+="Din care TVA 20%\t"+v.calculeazaTVA()+" lei \r\n";
sir+="\r\n\r\nTOTAL GENERAL\t"+t+" lei \r\n";
return sir;
}

public class TestVanzare{


public static void main(String[] args){
Vanzare v=new Vanzare(new Client("Ionescu", "Pop", "aleea stejarilor"));
v.addProdus(new ElementVanzare(new SpecificatieProdus("Calculator", 123), 6));
Factura f=new Factura(v);
System.out.println(f);
}}

90
11. APPLET
Browser – program cu urmatoarele functiuni:
- vizualizează resursele codificate în HTML.
- Suport pentru orice mecanisme de scripting.
- Context pentru executarea applet-urilor Java.
- Suportă plug-ins.
- Cere resurse de la server-ele din reţea.
- Utilizează protocolul HTTP.
- Identifică resursele utilizând adrese unice - URI (Uniform Resource Identifier) sau URL.
Pagini Web
Paginile Web sunt resurse Web care au o adresă URL (Uniform Resource Locator).
Pagină Web = Document de text cu extensia .html sau .htm al cărui conţinut va fi citit şi interpretat
de un browser. Documentul poate fi scris cu word processor oarecare, este de ajuns să fie salvat în
format text cu extensia .html sau .htm.
HTML = Limbaj de comenzi cu care se scriu paginile Web. Utilizează tag-uri (comenzi între < şi >)
care au un înţeles bine precizat pentru browser.
Un fişier HTML simplu
Un tag poate avea modificatori. Modificatorii sunt atribute, de obicei perechi nume=valoare:
<BODY BGCOLOR=“yellow”>
<HTML>
<HEAD>
<TITLE>Arhitectura Internet</TITLE>
</HEAD>
Structura unei pagini Web: Header + Corp Structura header-ului: Structura corpului:
<HTML> <BODY TEXT=” “ LINK=” “
<HEAD>
<HEAD> ALINK=” “ VLINK=” “
Header <TITLE>text BACKGROUND=” “>
</HEAD> </TITLE>
tag-uri +text
</HEAD>
</BODY>
<BODY>
Corp
</BODY>
</HTML>
<BODY BACKGROUND="" BGCOLOR="#ffffff" TEXT="#000000" LINK="#0000ff"
VLINK="#800080" ALINK="#ff0000">
<P>Bine ati venit în site-ul nostru!</P>
</BODY>
</HTML>
Tag-uri de formatare a textelor
Titluri de secţiune: <H1>text</H1>, <H2>text</H2>,. . ., <H6>text</H6>
Alinearea textului: <CENTER>text</CENTER> sau ca atribut:
<H1 ALIGN=CENTER>text</H1>
Trecerea pe linia următoare în paragraful curent: <BR>
Text afisat asa cum este scris în pagină: <pre>text</pre>
Separarea paragrafelor: <P>text</P> sau cu un atribut: <P ALIGN=LEFT>
Liste ale căror elementele sunt prefixate de un punct:
<UL> introduce o lista
<LI> text introduce un element în listă
<LI> text
</UL> termină lista
Liste ale căror elemente sunt numerotate:
<OL>
<LI> text introduce un element în listă
91
<LI> text
</OL> termină lista
Linie de separare: <HR>
Tag-ul APPLET

<APPLET
[ALT=TEXT]
CODE=“NUMEAPPLET.CLASS”
[CODEBASE=URL-UL FIŞIERULUI .CLASS]
HEIGHT=ÎNĂLŢIMEA ÎN PIXELI
[HSPACE=SPAŢIUL ORIZONTAL DINTRE APPLET ŞI TEXTUL CARE-L ÎNCONJOARĂ]
WIDTH=LĂRGIMEA ÎN PIXELI
[VSPACE=SPAŢIUL VERTICAL ÎNTRE APPLET ŞI TEXTUL CARE-L ÎNCONJOARĂ]
[NAME=NUMELE APPLET-ULUI]
>
[<PARAM NAME=PARAMETRU1 VALUE=VALOARE PARAMETRU1>]
[<PARAM NAME=PARAMETRU2 VALUE=VALOARE PARAMETRU2>]
</APPLET>
Clasa Applet
Applet-urile sunt programe Java care se pot descarca din internet; aceasta descarcare este realizata
de browser; apare intr-o fereastra a broser-ului ca un dreptunghi (panel-panou) in care avem o
prezentare grafica cu un fond (background), putem desena caractere de un anumit font si o anumita
culoare, precum si alte forme precum dreptunghiuri, linii poligonale, elipse, cercuri.
Asadar, un applet are urmatoarele proprietati:
- funcţioneaza ca parte integrantă a unui browser;
- prezinta utilizatorului o interfaţă grafică şi poate capta evenimente declanşate de utilizatori.
Orice applet este un obiect al unei clase definită de utilizator care extinde clasa Applet a
pachetului java.applet sau clasa JApplet a pachetului javax.swing.
Clasa Applet este un Container (componentă grafică Java utilizată pentru a include, gestiona
şi vizualiza alte componente grafice) care utilizează metoda paint pentru vizualizarea desenelor şi
componentelor grafice pe o suprafaţă dreptunghiulară.
Exemplu:
import java.awt.*;
import java.applet.Applet;

public class PrimulApplet extends Applet {


public void init(){setBackground(Color.magenta);}
public void paint(Graphics g){
g.drawString(“Buna ziua!”,20,30);
}
}
În pagina HTML:
<applet name=”primul applet" code=”PrimulApplet.class” codebase=”classes"
width=”100” height="60”>
</applet>

Când întâlneşte tag-ul APPLET, browser-ul execută următorii paşi:


1. Incarcă fişierul PrimulApplet.class din directorul classes al directorului curent.
2. Alocă aria în care se vizualizează applet-ul.
3. Instanţiază clasa PrimulApplet.
4. Cere executarea metodelor standard implementate în applet: init, paint etc.

Structura unui applet:

92
import java.applet.Applet;
public class NumeApplet extends Applet{
public void init(){…}
public void start(){…}
public void stop(){…}
public void destroy(){…}
public void paint(Graphics g){…}//mostenita din supraclasa Container
}

Browser Applet
1. Accesează pagina care conţine un applet

Cere încărcarea applet-ului


Applet-ul este încărcat în sistem
Pentru a-l iniţializa, browser-ul cheamă init()

2. Applet-ul a fost încărcat în sistem şi iniţializat


start()
Browser-ul cheamă
applet-ul rulează…

apoi cheamă paint()


3. ... vizualizează întreaga pagină

4. Browser-ul lansează pagina şi cheamă stop()


applet-ul se opreşte
din rulare

5. Browser-ul revine în pagina applet-ului sau


browser-ul eliberează definitiv pagina conţinută
6. ... va fi apelată metoda destroy()

Desenarea cu paint()
- paint(Graphics g), repaint() şi update(Graphics g) sunt utilizate pentru
desenarea pe un Container, în particular într-un applet vizualizat în pagina HTML. În aceste metode
trebuie să descriem cum trasăm în dreptunghiul applet-ului un text, o linie, un fond de o anumită
culoare sau o imagine.
- pentru a vizualiza orice lucru avem nevoie să redefinim metoda paint mostenita de clasa
Applet din clasa Container:
public void paint(Graphics g){
//cod pentru desenat
}
- parametrul g este iniţializat cu un obiect al clasei Graphics care este creat şi transmis lui
paint() de către browser. Acest obiectul reprezintă contextul grafic al applet-ului. Pentru a putea
utiliza metoda paint() trebuie importată clasa Graphics din pachetul java.awt.

93
Metoda paint() va fi apelată automat de către browser când se desenează în applet. Când acelaşi
applet trebuie să fie redesenat (de exemplu când am schimbat culoarea fondului cu
setBackground()), se utilizează repaint().
Un applet care desenează
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Color;
public class Dreptunghiuri extends Applet{
public void paint(Graphics g) {
g.drawRect (10, 15, 20, 30);
g.translate(20, 35);
g.setColor(new Color(0, 0, 255));
g.fillRect (10, 15, 40, 65);
}
}
Graphics
Graphics = Clasă abstractă, bază pentru toate contextele grafice care aparţin unei aplicaţii de
desenat. Informaţiile de stare necesare pentru desenarea în Java sunt:
- obiectul Component pe care se desenează (un applet, de exemplu);
- coordonatele originii translatării pentru desenat şi pentru operaţia de clipping;
- clip-ul curent;
- culoarea curentă;
- font-ul curent;
- funcţia logică curentă a operaţiei în pixeli (XOR sau Paint).
Cine furnizează obiecte Graphics?
Obiectele Graphics sunt întoarse de metodele getGraphics() existente în clasele Component,
Image şi PrintJob.
Cele mai importante metode ale clasei Graphics sunt prezentate în tabelul urm.

Metoda Semnificaţie
dispose() Elimină contextul grafic.
getColor() Întoarce culoarea curentă.
setColor(Color) Setează culoarea curentă ca fiind culoarea
specificată.
getFont() Întoarce font-ul curent.
setFont(Font) Setează font-ul contextului curent ca fiind font-ul
specificat.
getFontMetrics() Întoarce dimensiunile tipului curent de font.
getFontMetrics(Font) Întoarce dimensiunilr tipului de font specificat.
translate(int, int) Pune originea contextului grafic în punctul (x, y) al
sistemului curent de coordonate.
draw3DRect(int, int, int, int, Desenează un dreptunghi în 3-D.
boolean)
drawArc(int, int, int, int, int, Desenează un arc înscris în dreptunghiul specificat.
int)
drawImage(Image, int, int, Color, Desenează atâta cât din imagine specificată este
ImgObs) disponibilă.
drawLine(int, int, int, int) Desenează o linie între două puncte ale sistemului
de coordonate al contextului grafic.
drawOval(int, int, int, int) Trasează o elipsă în interiorul dreptunghiului
specificat.
drawPolygon(int[], int[], int) Trasează un poligon definit de doua array-uri de
puncte x şi y.
drawPolyline(int[], int[], int) Trasează o secvenţă de linii conexe specificate de
array-urile de coordonate x si y. Fiecare pereche
94
Metoda Semnificaţie
(x[i], y[i]) defineşte un punct.
drawRect(int, int, int, int) Trasează dreptunghiul specificat.
drawRoundRect(int, int, int, int, Trasează un dreptunghi cu colţurile rotunjite
int, int) specificate.
drawString(String, int, int) Desenează şirul specificat cu culoarea contextului
grafic curent.
fill3DRect(int, int, int, int, Colorează interiorul dreptunghiului 3D cu culoarea
boolean)
curentă.
fillArc(int, int, int, int, int, Umple un sector de cerc continut in dreptunghiul
int) specificat cu culoarea curenta.
fillOval(int, int, int, int) Umple cu culoarea curentă o elipsă continută în
dreptunghiul specificat.
fillPolygon(int[], int[], int) Umple cu culoarea curentă un poligon definit de
două array-uri de coordonate x şi y.
fillRect(int, int, int, int) Umple dreptunghiul specificat cu culoarea curentă.
fillRoundRect(int, int, int, int, Umple dreptunghiul cu colţuri rotunde specificat cu
int, int) culoarea curentă.
Shape getClip() Întoarce aria de lucru curentă ca un obiect de tip
interfata Shape
getClipBounds() Intoarce dreptunghiul circumscris ariei de lucru.
setClip(int, int, int, int) Setează clip-ul curent dreptunghiul specificat cu
coordonatele date ca parametri actuali ai metodei.
Color
Color este o clasă care reprezintă o culoare.
Culorile pot fi construite cu constructori ca:
Color (xxx r, xxx g, xxx b)
Color (xxx r, xxx g, xxx b, xxx alpha) xxx=int sau float
Color (int rgb)
Color (int rgb, boolean hasAlpha)
Clasa Color conţine câmpuri constante iniţializate cu culori fundamentale:
Color.red, Color.yellow, Color.blue, Color.cyan, Color.white, Color.green,
Color.magenta etc
Font
static Font getFont(String nm)//returneaza un obiect de tip Font cu numele
specificat
static Font getFont(String nm, Font f)
int getSize()
int getStyle()
boolean isBold()
boolean isItalic()
boolean isPlain()
static final int BOLD=1;
static final int ITALIC=2;
static final int PLAIN=0;
Exemple de font-uri din JDK 1.4.1: Dialog, SansSerif, Serif, Monospaced,
Helvetica, TimesRoman, DialogInput.
Dimensiunile sunt specificate în puncte tipografice (1/72”):
Font (String nume, int stil, int dim)
Font f=new Font(“SansSerif”,Font.BOLD,12);
Se poate impune unui context grafic un font nou astfel:
void Graphics.setFont(Font f)
Applet pentru ora exactă

95
import java.awt.*;
import java.util.*;
public class Ceas extends java.applet.Applet {
private Color beige=new Color(255, 204, 102);
private String oraPrec=“”;
public void init() {
setBackground(Color.black);
}
public void paint(Graphics g) {
Graphics2D g2D = (Graphics2D) g;
Font tip=new Font (“Monospaced”, Font.BOLD, 20);
g2D.setFont(tip);
GregorianCalendar azi=new GregorianCalendar();
g2D.setColor(Color.black);
g2D.drawString(oraPrec, 5, 25);
g2D.setColor(beige);
String ora=azi.getTime().toString();
g2D.drawString(ora, 5, 25);
try {Thread.sleep(1000);
}catch(InterruptedException e){
}
oraPrec = ora;
repaint();
} <html>
<applet code="CeasNou" width=350
} height=50>
Applet pentru ora exactă cu parametri <param name="fond" value="#996633">
import java.awt.*; </applet>
import java.util.*; </html>
public class CeasNou extends java.applet.Applet{
private Color beige = new Color(255, 204, 102);
private String oraPrec = “”;
Color culoareFond;
public void init() {
String in=getParameter(“fond”);
culoareFond=Color.black;
if (in != null) {
try{
culoareFond=Color.decode(in);
}catch(NumberFormatException e) {showStatus(“Parametru eronat “ + in);}
}
setBackground(Color.black); }
public void paint(Graphics g) {
Graphics2D g2D=(Graphics2D) g;
Font tip=new Font(“Monospaced”, Font.BOLD, 20);
g2D.setFont(tip);
GregorianCalendar azi=new GregorianCalendar();
g2D.setColor(culoareFond);
g2D.drawString(oraPrec, 5, 25);
g2D.setColor(beige);
String ora=azi.getTime().toString();
g2D.drawString(ora, 5, 25);
try {Thread.sleep(1000);
}catch(InterruptedException e) {}
oraPrec=ora;
repaint();
}
}

96
FontMetrics
FontMetrics Component.getFontMetrics(Font)
FontMetrics Graphics.getFontMetrics()
FontMetrics Graphics.getFontMetrics(Font)
FontMetrics Toolkit.getFontMetrics(Font)
int charsWidth(char data[],int off,int len)
int bytesWidth(byte data[],int off,int len)
int charWidth(char ch)
int charWidth(int ch)
int getAscent()
getDescent()
getLeading()
getHeight()
getMaxAscent()
getMaxDescent()
int stringWidth(String s)
Cum se determină lungimea unui şir pe ecran?
Font f=new Font(“SansSerif”, Font.BOLD+Font.ITALIC, 12);
FontMetrics fm=g.getFontMetrics(f);
cx=fm.stringWidth(“Un şir lung de 31 de caractere.”);
Programarea cu obiecte Font
Exercitiu. Cum să scriem textul:
Acesta este un program pentru testarea font-urilor
poziţionat în mijlocul unui frame utilizând două font-uri diferite?
Solutia o gasiti în Error! Reference source not found..
public class TestFont2 extends Frame{
private Font f;
private Font fi;
private FontMetrics fm;
private FontMetrics fim;
private boolean fontsSet = false;
public void setFonts(Graphics g){
if (fontsSet) return;
f=new Font("SansSerif",Font.BOLD, 14);
fi=new Font("SansSerif",Font.BOLD + Font.ITALIC,14);
fm=g.getFontMetrics(f);
fim=g.getFontMetrics(fi);
fontsSet = true;
}
public void paint(Graphics g){
setFonts(g);
String s1 = "Acesta este";
String s2 = " un program pentru";
String s3 = " testarea font-urilor.";
int w1 = fm.stringWidth(s1);
int w2 = fim.stringWidth(s2);
int w3 = fm.stringWidth(s3);
Dimension d = getSize();
Insets in = getInsets();
int clientWidth=d.width-in.right-in.left;
int clientHeight =d.height-in.bottom-in.top;
int cx=(clientWidth-w1-w2-w3)/2 + in.left;
int cy=clientHeight/2+in.top;
g.drawRect(in.left,in.top,clientWidth-1, clientHeight-1);
g.setFont(f);
g.drawString(s1, cx, cy);

97
cx += w1;
g.setFont(fi);
g.drawString(s2, cx, cy);
cx += w2;
g.setFont(f);
g.drawString(s3, cx, cy);
}

public static void main(String args[]){


Frame f = new TestFont2();
f.show();
}
}

98
12. INTERFETE GRAFICE
AWT-Abstract Window Toolkit
Serie completă de componente ale interfeţei grafice utilizator (GUI)
Suport pntru “container-e” de componente grafice.
Fiecare componentă are ciclul său de viaţă, independent de al altora.
Un mecanism de evenimente care gestionează evenimentele sistemului şi evenimentele
utilizatorului.
Mecanisme pentru aranjare a componentelor într-un mod care permite obţinerea unui GUI
independent de platformă.
Gestionează ferestrele, menu-urile şi ferestrele de dialog.
Grafica
- Desenează figuri în 2D
- Controlează culorile
- Controlează font-urile
Java 2D API
- Posibilitatea de grafică mai laborioasă
- Desenează figuri în 2D personalizate
- Umple figurile cu culori şi modele
Container-e
Ideea fundamentală: O fereastră Java este un container care conţine o colecţie de componente
încuibate.
Container-ele sunt gestori ai componentelor conţinute.
Ierarhia de container-e poate avea orice nivel de complexitate.
Frame = Fereastra de nivel cel mai înalt într-o aplicaţie.
Applet = Container de componente grafice care poate fi executat de un browser.
Ierarhia claselor AWT

Object

Component

Canvas Container List Button TextComponent CheckBox ScrollBar Label Choice

Panel Window TextField TextArea

Applet Frame Dialog

FileDialog

java.awt.Component
java.awt.Component = Clasă abstractă care generalizează toate componentele AWT,
exceptând menu-urile. Unei componente i se asociază următoarele elemente:
- un obiect Graphics
- localizare
- dimensiune
- un peer nativ
- un container părinte
- font şi dimensiunile font-ului (font metrics)

99
- culori pentru foreground şi background
- specific lingvistic
- dimensiuni maxime şi minime
Component⇒Container ⇒Window⇒Frame
java.awt.Component
boolean isVisible() <<Interface>> <<Interface>> <<Interface>
void setVisible(boolean b) ImageObserver MenuContainer Serializable
boolean isShowing()
boolean isEnabled()
void setEnabled(boolean b)
Font getFont()
void setFont(Font f)
Font Component
Point getLocation()
void setLocation(int x, int y) 1 vizible : boolean +peer
void setLocation(Point p) enabled : boolean ComponentPeer
Point getLocationOnScreen() +fg, bg valid : boolean
Color
Graphics getGraphics() 2 x : int
void setBackground(Color c) y : int
void setForeground(Color c) Graphics width : int
Dimension getSize() 1 height : int
void setSize(int w, int h) *
void setSize(Dimension d)
java.awt.Container
Component add(Component c)
+parinte
Component add(String name,
Component c) Container <<Interface>>
Component getComponentAt(int 1 LayoutManager
x, int y)
Component
getComponentAt(Point p) +panelLayout
Component[] getComponents()
Window Panel FlowLayout
LayoutManager getLayout()
void setLayout(LayoutManager
mgr)
Insets getInsets() Frame Applet
void paint(Graphics g)
void print(Graphics g)
java.awt.Window
void toFront()
void toBack()
Toolkit getToolkit()
void show()
java.awt.Frame
void setResizable(boolean b)
void setTitle(String s)
Adăugarea componentelor grafice
import java.applet.Applet;
import java.awt.*;// importa toate clasele AWT
public class Primul extends Applet {
TextField text;//declara campul de text ca
TextField
Button buton1, buton2;//declara doua Button
public void init(){
text = new TextField(20);//creaza un
TextField
add(text);//adauga TextField-ul la fereastra
buton1=new Button ("Apasa");//creaza un Button
add(buton1);//adauga Button-ul la fereastra
buton2=new Button("Cancel"); //creaza un Button
add(buton2); //adauga Button-ul la fereastra
}
}

100
TextArea, TextField şi Checkbox
import java.applet.Applet; import java.applet.Applet;
import java.awt.*; import java.awt.*;
public class ContainerX extends Applet{ public class Container2 extends Applet{
//declaratii/alocari de componente TextArea tArea;
O arie de text
public void init() {
//declaratii/alocari de componente public void init() {
//adaugarea componentelor in applet tArea=new TextArea(10,20);
} add(tArea);
} }
}
import java.applet.Applet; import java.applet.Applet;
import java.awt.*; import java.awt.*;
public class Container1 extends Applet{ public class Container3 extends Applet{
TextField text1; CheckboxGroup cBG=new CheckboxGroup();
TextField text2; Checkbox c1, c2: un grup de
public void init() { public void init() { butoane radio
text1=new TextField(20); c1=new Checkbox(“Primul”,cBG, true);
add(text1); add(c1);
text2=new TextField(“Buna”,10); c2=new Checkbox(“AlDoilea”);
add(text2); c2.setCheckboxGroup(cBG);
} c2.setState(false);
} add(c2);
}
}
List, Scrollbar, Canvas şi Label
import java.applet.Applet; import java.applet.Applet;
import java.awt.*; import java.awt.*;
public class Container4 extends Applet{ public class Container5 extends Applet {
List list1; Scrollbar hSB, vSB; Bare de defilare
Lista
public void init(){ public void init(){ verticală şi orizontală
list1=new List(2, true); hSB=new Scrollbar(Scrollbar.HORIZONTAL,0,1,
list1.addItem(“Rosu”); 1,100);
list1.addItem(“Verde”); add(hSB);
list1.addItem(“Albastru”); vSB=new Scrollbar(Scrollbar.VERTICAL,0,1,1,
add(list1); 100);
} add(vSB);
} }
}
import java.applet.Applet; import java.applet.Applet;
import java.awt.*; import java.awt.*;
public class Container7 extends Applet{ public class Container6 extends Applet{
Canvas c; Canvas Label etic=new Label(“Nume si Prenume”);
public void init() { text1=new TextField(“Ion Pop”,20);
c=new Canvas(); public void init(){ Etichetă
c.resize(40,40); add(etic);
c.setBackground(Color.black); add(text1);
add(c); }
} }
}
Menu
In diagrama urmatoare se prezinta conceptele principale pe care trebuie sa le cunoastem pentru a
programa menu-uri cu java.awt .
MenuComponent

MenuBar MenuItem

CheckboxMenuItem Menu PopupMenu

101
Din programul urmator se poate vedea care este în esenta modalitatea de programare a menu-urilor.
import java.awt.*;
public class MenuFrame extends Frame {
MenuItem fileNew=new MenuItem(“Nou”);
MenuItem fileOpen=new MenuItem(“Deschide”);
MenuItem fileSave=new MenuItem(“Salveaza”);
MenuItem fileExit=new MenuItem(“Iesire”);
MenuItem editUndo=new MenuItem(“Cancel”);
MenuItem editCut=new MenuItem(“Cut”);
MenuItem editCopy=new MenuItem(“Copy”);
MenuItem editPaste=new MenuItem(“Paste”);
MenuItem helpContents=new MenuItem(“Rezumat”);
MenuItem helpAbout=new MenuItem(“Info...”);
public MenuFrame() {
super(“Exemplu de menu”);
MenuBar menubar=new MenuBar();
Menu fileMenu=new Menu(“Fisier”);
Menu editMenu=new Menu(“Modifica”);
Menu helpMenu=new Menu(“?”);
fileMenu.add(fileNew);
fileMenu.add(fileOpen);
fileSave.setEnabled(false);
fileMenu.add(fileSave);
fileMenu.addSeparator();
fileMenu.add(fileExit);
editUndo.setEnabled(false);
editMenu.add(editUndo);
editMenu.addSeparator();
editCut.setEnabled(false);
editMenu.add(editCut);
editCopy.setEnabled(false);
editMenu.add(editCopy);
editPaste.setEnabled(false);
editMenu.add(editPaste);
helpMenu.add(helpContents);
helpMenu.addSeparator();
helpMenu.add(helpAbout);
menubar.add(fileMenu);
menubar.add(editMenu);
menubar.add(helpMenu);
menubar.setHelpMenu(helpMenu);
setMenuBar(menubar);
setSize(new Dimension(400,300));
show();
}
public static void main(String[] args){
MenuFrame meu=new MenuFrame();
}
}
Grafica cu LayoutManager
Layout Manager = Interfaţă utilizată pentru asezarea diferitelor componente într-un container după
un şablon de aranjare. LayoutManager2 extinde LayoutManager şi este folosit pentru asocierea de
constrângeri aşezării componentelor.

102
Cele mai folosite clase de gestori de layout care implementează interfaţa LayoutManager sunt:
FlowLayout, BorderLayout, GridLayout, GridBagLayout, CardLayout.
setLayout(new FlowLayout()); setLayout(new BorderLayout()); setLayout(new GridLayout(3,2));
AppletViewer: GestoreLayout AppletViewer: GestoreLayout AppletViewer: GestoreLayout
Applet Applet Applet
North
W E
e a
s Center s
t t
South

Clasa GridBagLayout este un gestor flexibil care CardLayout aşează componentele (de obicei
aliniază componentele pe verticală şi orizontală, paneluri) în straturi, precum cărţile într-un pachet
fără să fie nevoie ca componetele să aibă aceleaşi de cărti. În orice moment, este vizibilă numai o
dimensiuni. Fiecare obiect GridBagLayout componentă, dar componentele pot fi “răsfoite” şi
păstrează o grilă dreptunghiulară dinamică de să controlăm care din ele să devină vizibilă. Acest
celule în care fiecare componentă ocupă una sau layout este util căn o aceeaşi suprafaţă trebuie
mai multe celule, care formează aria de partajată de mai multe paneluri care trebuie să fie
vizualizare a componentei. vizualizate în momente diferite de timp.

Exemplu.
import java.awt.*;
public class FacturaFrame extends Frame
{Button print, cancel,printF,arhivare;
TextArea arie;
Panel panel;
private Factura factura;
private GridBagLayout gb;
private GridBagConstraints gbc;
public FacturaFrame(Factura f)
{super("Factura");
factura=f;
gb=new GridBagLayout();

gbc=new GridBagConstraints();
gbc.fill=GridBagConstraints.HORIZONTAL;
gbc.anchor=GridBagConstraints.NORTH;

panel=new Panel();
panel.setLayout(gb);

arie=new TextArea(factura.toString(),20, 50);


arie.setEditable(false);
addComponent(arie,0,0,1,5);

print=new Button("Print");
addComponent(print,1,1,1,1);

printF=new Button("PrintToFile");
addComponent(printF,2,1,1,1);

arhivare=new Button("Arhivare");
addComponent(arhivare,3,1,1,1);

cancel=new Button("Cancel");
addComponent(cancel,4,1,1,1);
103
add(panel);
}

void addComponent(Component c, int linie,int col, int lat, int inal)


{ gbc.gridx=col;
gbc.gridy=linie;
gbc.gridwidth=lat;
gbc.gridheight=inal;
gb.setConstraints(c,gbc);
panel.add(c);
}
public static void main(String[] args){
Vanzare v=new Vanzare(new Client("Ionescu", "Pop", "alea rozelor"));
v.addProdus(new ElementVanzare(new SpecificatieProdus("mousepad", 11), 2));
v.addProdus(new ElementVanzare(new SpecificatieProdus("mouse", 50), 3));
Frame f=new FacturaFrame(new Factura(v));
f.setSize(480, 400);
f.setVisible(true);}}

104
13. GESTIUNEA EVENIMENTELOR
Programarea pe evenimente este o paradigmă de programare diferită de programarea procedurală
sau de cea orientată spre obiecte. După această paradigmă, colaborarea între participanţii dintr-o
elaborare va fi realizată prin intermediul mesajelor transmise nu unui unic participant ca în celelalte
două cazuri, ci mai multor participanţi interesaţi în primirea informaţiilor conţinute de mesajele
respective. Orice aplicaţie poate fi mai bine proiectată dacă se utilizează paradigma orientată de
evenimente.
Java utilizează această paradigmă pentru a oferi o mai bună comunicare între obiecte autonome,
implicate ocazional într-o activitate de grup. Este cazul interfeţelor grafice în care evenimentele
verificate sunt generate de componente grafice (click al mouse-ului asupra unei componente,
selectarea unui element dintr-o listă sau apăsarea unei taste când o componentă este selectată) şi
trebuie să fie anunţate altor componente care trebuie să ia anumite decizii. În continuare vom
prezenta paradigma pe evenimente, aşa cum este utilizată în interfeţele grafice Java.
13.1 MODELUL GESTIUNII EVENIMENTELOR ÎN PROIECTAREA
COMPONENTELOR GRAFICE
Într-un scenariu de evenimente există trei protagonişti (Figura 13-1):
- sursă de evenimente: orice componentă grafică. O componentă grafică este specializată în
trimiterea anumitor tipuri de evenimente (Tabelul 13-1);
- eveniment: obiect lansat de o sursă de evenimente. Evenimentul este de un tip care
specializează clasa AWTEvent. Între aceste tipuri de evenimente există un tip ActionEvent care
reprezintă evenimente rezultate în urma efectuării unui clic de mouse într-o interfaţă grafică sau
un tip MouseEvent care prezintă acţiuni efectuate de mouse asupra unei componente oarecare.
Toate evenimentele conţin informaţii asupra sursei evenimentului şi un mesaj care ilustrează
evenimentul apărut. Sursa evenimentului se obţine cu metoda getSource() aplicată pe
eveniment. Metoda întoarce un Object. Mesajul se obţine apelând evenimentul cu toString();
- ascultător (listener): obiect care implementează un gestor de evenimente de un anumit tip
(event handler - EH). Gestorul respectiv va indica acţiunile ce vor fi executate când apare
evenimentul pentru care s-a înregistrat. Clasa trebuie să implementeze o interfaţă de tip
EventListener.
Există interfeţe pentru toate tipurile de evenimente. De exemplu, pentru ActionEvent şi
MouseEvent avem interfeţele ActionListener şi MouseListener. Metodele interfeţelor diferenţiază
diferite situaţii în care se pot verifica pentru un anumit tip de eveniment. De exemplu,
ActionListener are o unică declaraţie de metodă:
void actionPerformed(ActionEvent e)
iar interfaţa MouseListener are următoarele declaraţii de metode:
public void mouseClicked(MouseEvent e);
public void mouseEntered(MouseEvent e);
public void mouseExited(MouseEvent e);
public void mousePressed(MouseEvent e);
public void mouseReleased(MouseEvent e);

Sursă de evenimente
Ascultător

Eveniment

Figura 13-1. Protagonişti într-un scenariu de evenimente


Pentru a vedea cum colaborează cei trei protagonişti, vom considera cazul evenimentelor de tip
ActionEvent lansat de un buton. Un click de mouse asupra butonului transformă butonul într-o
sursă de evenimente care generează mai multe obiecte-eveniment între care unul este de tip
ActionEvent. Evenimentul va fi primit de toţi ascultătorii care s-au înregistrat la acest tip de
eveniment şi produs de acest buton.
105
În programele care utilizează evenimente, sunt obligatorii trei secţiuni de cod:
- clasa ascultătoare declară că implementează o interfaţă Listener pentru tipul de eveniment de care
este interesată. Aceasta este o clasă internă a applet-ului sau ferestrei ce conţine componenta grafică
ce lansează evenimente de un anumit tip:
class AscultatorEveniment implements ActionListener {
...
}
- ascultătorul unui anumit tip de evenimente se înregistrează la sursă. Codul se găseşte de obicei
într-o metodă a unei instanţe a container-ului ce conţine sursa (deseori în constructor) sau într-o
metodă a unui alt obiect. De exemplu, pentru oComponenta, sursă de evenimente de tip
ActionEvent, ascultătorul instantaAscultatorEveniment se înregistrează astfel:
oComponenta.addActionListener(instantaAscultatorEveniment);
- ascultătorul implementează metodele interfeţei de ascultare. Cu fiecare din aceste metode
ascultătorul va fi anunţat de apariţia unui eveniment:
public void actionPerformed(ActionEvent e) { . . . }
În metode trebuie să identificăm sursa eveneimentului pentru a şti de unde a apărut evenimentul:
public void actionPerformed(ActionEvent e) {
if (e.getSource() instanceof Button) {...}
. . .
}
Ascultătorul poate fi chiar applet-ul sau fereastra ce conţine sursa de evenimente şi atunci acest container
trebuie să implementeze interfaţa ascultător pentru tipul de eveniment de care este interesat şi să se
înregistreze la sursa de evenimente:
public class Fereastra extends Frame implements ActionListener {
...
public Fereastra(){
...
oComponenta.addActionListener(this);
...
}
}
Componentă AWT Tipuri de evenimente generate
action adjus- compo- container focus item key mouse text window
tment nent
Button X X X X X
Canvas X X X X
Ceckbox X X X X X
CeckboxMenuItem X
Choice X X X X X
Component X X X X
Container X X X X X
Dialog X X X X X X
Frame X X X X X X
Label X X X X
List X X X X X X
MenuItem X
Panel X X X X X
Scrollbar X X X X X
ScrollPane X X X X X
TextArea X X X X X
TextComponent X X X X X
TextField X X X X X X
Window X X X X X X

Tabelul 13-1. Tipuri de evenimente generate de componente grafice

106
13.2 EVENIMENTE SEMANTICE
Evenimentele se împart în două categorii: evenimente semantice şi cele de nivel jos. În această
secţiune ne vom ocupa numai de evenimentele din prima categorie.
Evenimentele semantice (Diagrama 13-1) apar datorită interacţiunii utilizatorului cu componenta
grafică, adică cu sursa de evenimente. De exemplu:
- un utilizator face un clic asupra unui buton: ActionEvent;
- un utilizator modifică valoarea unei bare de defilare: AdjustmentEvent;
- un utilizator selectează un element dintr-un grup de elemente (dintr-o listă): ItemEvent;
- un utilizator modifică textul dintr-un câmp de text sau dintr-o arie de text: TextEvent.
EventObject

getSource() : Object
toString() : String

AWTEvent

ActionEvent AdjustmentEvent ItemEvent


TextEvent
getActionCommand() : String getAdjustable() : Adjustable getItem() : Object
getModifiers() : int getAdjustmentType() : int getItemSelectable() : ItemSelectable
getWhen() : long getValue() : int getStateChanged() : int

Diagrama 13-1. Tipuri de evenimente semantice

13.2.1 Gestiunea evenimentelor de tip ActionEvent


Exemplul 1. Considerăm cazul unui applet care vizualizează un câmp de text şi unul din mesajele
“Bine ati venit!” sau “La revedere” în funcţie de butonul apăsat: (Figura 13-2).

Figura 13-2. Un applet care gestionează evenimente de tip ActionEvent


import java.applet.Applet;
import java.awt.*; // importă clase AWT la cerere
import java.awt.event.*;
public class Primul extends Applet {
private TextField text; //declară un câmp de text ca
TextField
private Button buton1, buton2; //declară două Button
private AscultatorEveniment ae; //declară un ascultător

public void init(){


text = new TextField(20); //crează un TextField
add(text); //adaugă la fereastră TextField-ul
buton1 = new Button (”Bine ati venit"); //crează un Button
add(buton1); //adaugă la fereastră Button-ul
ae=new AscultatorEveniment();
107
buton1.addActionListener(ae); // adăugarea ascultătorului la buton1 si
//obiectul a cărui referinţă este memorată în ae tratează
aparitia
//evenimentului ActionEvent generat de buton1
buton2 = new Button (”La revedere"); //crează un Button
add(buton2); //adaugă la fereastră Button-ul
buton2.addActionListener(ae); // adăugarea ascultătorului la buton2 si
//obiectul a cărui referinţă este memorată în ae rezolvă
//evenimentul ActionEvent cand va fi generat de buton2
}
class AscultatorEveniment implements ActionListener{
public void actionPerformed (ActionEvent evt) { //tratarea
evenimentului
if (evt.getSource()==buton1)
text.setText(“Bine ati venit!”);
else
text.setText(“La revedere!”);
}
}
}
Exemplul2. Considerăm o fereastră numită “Jurnal” care are o arie de text şi două butoane:
Salvează şi Anulează. Utilizatorul introduce un text în aria de text, după care apasă unul din cele
două butoane. Dacă apasă butonul Salvează, conţinutul ariei de text va fi salvat în fişierul
“jurnal.txt”. Dacă apasă pe butonul Anulează, fereastra se închide şi conţinutul ariei de text se
pierde (Figura 13-3).

Figura 13-3. Fereastra Jurnal


Programul care implementează fereastra Jurnal este următorul:
import java.awt.*;
import java.awt.event.*;
import java.io.*;
public class AlDoilea extends Frame{
private Button b1, b2;
private TextArea ta;
private Ascultator a;

public AlDoilea(){
super("Jurnal");
a=new Ascultator();
108
ta=new TextArea(50,75);
add(ta, BorderLayout.CENTER);

Panel p = new Panel();


b1 = new Button ("Salveaza");
p.add(b1);
b1.addActionListener(a);
b2 = new Button ("Anuleaza");
p.add(b2);
b2.addActionListener(a);
add(p, BorderLayout.SOUTH);
}
class Ascultator implements ActionListener{
public void actionPerformed (ActionEvent e){
if (e.getSource()==b1){
String text=ta.getText();
try{
PrintWriter pw=new PrintWriter(new FileWriter("jurnal.txt"));
pw.println(text);
pw.close();
}catch(IOException io){System.out.println(io.getMessage());}
System.exit(0);
}
else System.exit(0);
}
}
public static void main(String[] args) {
Frame f = new AlDoilea ();
f.setBounds(40, 60, 450, 350);
f.setVisible(true);
}
}
Programul 13-1. Programul Jurnal

13.2.2 Gestiunea evenimentelor de tip AdjustmentEvent


Exemplu. Fie un program care vizualizează pe ecran fereastra (Figura 13-4) ce conţine trei bare de
defilare, fiecare corespunzând unei culori din sistemul RGB, şi trei câmpuri de text ce afişează
valorile culorilor roşu, verde şi albastru din care este creată culoarea curentă. Cu această culoare,
programul desenează un disc pe un canvas aflat în partea dreaptă a ferestrei. Acesta va fi redesenat
de fiecare dată când utilizatorul deplasează butonul de defilare sau execută un click de mouse pe
unul din butoanele de incrementare sau decrementare a valorii oricărei bare de defilare. Codul
programului este prezentat în continuare.
import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;
public class ColorChooser extends Frame{
private Scrollbar r,g,b;
private TextField rt,gt,bt;
private Panel p;
private Canvas canvas;
private GridBagLayout gb;
private GridBagConstraints gbc;
private Ascultator a;
public ColorChooser(){

109
super("ColorChooser");
a=new Ascultator();
p=new Panel();
gb=new GridBagLayout();
gbc=new GridBagConstraints();
gbc.fill=GridBagConstraints.NONE;
gbc.anchor=GridBagConstraints.WEST;
gbc.insets=new Insets(10,10,0,0);
p.setLayout(gb);
r=new Scrollbar(Scrollbar.VERTICAL, 0, 1,
0, 255);
adaugaComp(r,0,0,3,1);
r.addAdjustmentListener(a);
rt=new TextField(5);
adaugaComp(rt,0,3,1,1);
g=new Scrollbar(Scrollbar.VERTICAL, 0, 1,
0, 255);
adaugaComp(g,4,0,3,1);
g.addAdjustmentListener(a); Figura 13-4.Fereastra ColorChooser
gt=new TextField(5);
adaugaComp(gt,4,3,1,1);
b=new Scrollbar(Scrollbar.VERTICAL, 0, 1, 0, 255);
adaugaComp(b,7,0,3,1);
b.addAdjustmentListener(a);
bt=new TextField(5);
adaugaComp(bt,7,3,1,1);
canvas=new Canvas();
add(p, BorderLayout.WEST);
add(canvas, BorderLayout.EAST);
}
public void adaugaComp(Component c, int l, int col, int lat, int inal){
gbc.gridx=col;
gbc.gridy=l;
gbc.gridwidth=lat;
gbc.gridheight=inal;
gb.setConstraints(c,gbc);
p.add(c);
}
class Ascultator implements AdjustmentListener{
public void adjustmentValueChanged(AdjustmentEvent e){
int rosu=r.getValue();
int verde=g.getValue();
int albastru=b.getValue();
rt.setText(String.valueOf(rosu));
gt.setText(String.valueOf(verde));
bt.setText(String.valueOf(albastru));
Color c=new Color(rosu,verde,albastru);
deseneaza(c);
}
public void deseneaza(Color c){
Graphics g=getGraphics();
g.setColor(c);
g.fillOval(135,85,75,75);
canvas.paint(g);
}

110
}
public static void main(String[] arg){
Frame f=new ColorChooser();
f.setBounds(350, 240, 250, 220);
f.show();
}
}
Programul 13-2. Programul ColorChooser

13.2.3 Gestiunea evenimentelor de tip ItemEvent


Un obiect de tip ItemEvent este creat de fiecare dată când un utilizator alege sau selectează un
element dintr-o listă, moment în care este lansat un eveniment de acelaşi tip.
Exemplu. Vom îmbunătăţi programul anterior (Programul 13-2) pentru a permite utilizatorului să
aleagă dintr-o listă, forma grafică: disc, oval, patrat sau dreptunghi plin care vrea să fie desenată.
Pentru aceasta, introducem o componentă grafică de tip Choice numită lista ce conţine numele
formelor grafice în ordinea menţionată anterior, la care asociem un ascultător de tip ItemListener.
Codul necesar este următorul:
al=new AscultatorLista();
lista=new Choice();
lista.add("Disc");
lista.add("Oval");
lista.add("Patrat");
lista.add("Dreptunghi");
lista.addItemListener(al);
add(lista, BorderLayout.NORTH);
Instrucţiunile din metoda adjustmentValueChanged() a interfeţei AdjustmentListener sunt
mutate într-o metodă getCuloare() a clasei Ascultator:
public Color getCuloare(){
int rosu=r.getValue();
int verde=g.getValue();
int albastru=b.getValue();
rt.setText(String.valueOf(rosu));
gt.setText(String.valueOf(verde));
bt.setText(String.valueOf(albastru));
return new Color(rosu,verde,albastru);
}
metodă ce va fi apelată de ambii ascultători: cel al barelor de defilare şi cel al listei când apelează
metoda desenează():
class AscultatorLista implements ItemListener{
public void itemStateChanged(ItemEvent e){
a.deseneaza(a.getCuloare(), lista.getSelectedItem());
}
}
Modificăm şi metoda desenează()introducând un parametru ce reprezintă numele formei
geometrice ce va fi desenată pe canvas cu o anumită culoare, şi anume cea aleasă de utilizator:
public void deseneaza(Color c, String forma){
Graphics g=getGraphics();
g.clearRect(135, 85, 100, 100);
g.setColor(c);
if(forma.equals("Disc")) g.fillOval(135,85,75,75);
if(forma.equals("Oval")) g.fillOval(135,85,85,75);
if(forma.equals("Patrat")) g.fillRect(135,85,85,85);
if(forma.equals("Dreptunghi")) g.fillRect(135,85,85,75);
canvas.paint(g);
}
}
13.2.4 Gestiunea evenimentelor de tip TextEvent
111
Un eveniment de tip TextEvent are loc când textul unei componente grafice a fost modificat. În acel
moment este apelată metoda public void textValueChanged(TextEvent e) a ascultătorului de
tip TextListener, dacă acesta s-a înregistrat la componenta respectivă.
Exemplu. Să scriem un program care furnizează funcţionalitatea unei case de bilete a unui
cinematograf. Programul vizualizează cumpărătorilor de bilete interfaţa grafică din Figura 13-5 şi
calculează, respectiv afişează suma pe care trebuie să o platească un client dacă cumpără un număr
de bilete. Se ştie că persoanele cu vârsta între 7 şi 14 plătesc jumătate de bilet (50000 lei), iar copiii
au intrare gratuită. Astfel, un client alege categoria de vârstă la care vrea să cumpere un număr de
bilete pe care-l introduce în câmpul de text asociat categoriei. În acest moment, programul
calculează şi afişează cât îl costă pe client biletele respective. Dacă alege să cumpere şi bilete din
cealalta categorie, programul îi va recalcula, respectiv reafişa, costul total al biletelor. Codul
programului este prezentat în continuare.
import java.awt.*;
import java.awt.event.*;

public class CasaDeBilete extends Frame {


private Checkbox c1,c2;
private TextField tf1, tf2;
private Panel p;
private long cost;
private AscultatorCamp ac;

public CasaDeBilete(){
super("Casa de bilete");
ac=new AscultatorCamp();

p=new Panel();
p.setLayout(new GridLayout(5,2, 10,10));
p.add(new Label("Categorie de varsta"));
p.add(new Label("Numar de bilete")); Figura 13-5. Fereastra Casa de bilete
c1=new Checkbox("7<=x<=14", false);
p.add(c1);
tf1=new TextField(10);
tf1.addTextListener(ac);
p.add(tf1);

c2=new Checkbox("x>14", false);


p.add(c2);
tf2=new TextField(10);
tf2.addTextListener(ac);
p.add(tf2);

p.add(new Label("Cost bilete"));


add(p);
}

class AscultatorCamp implements TextListener{


private Label costEt;
private int nr;

public void textValueChanged(TextEvent e){


nr=0;
if (costEt!=null) {p.remove(costEt);p.validate();}
if (e.getSource()==tf1){
nr=Integer.parseInt(tf1.getText());
cost+=nr*50000;
if (c2.getState()){
cost+=100000*Integer.parseInt(tf2.getText());
}
}
if (e.getSource()==tf2){
nr=Integer.parseInt(tf2.getText());
cost+=nr*100000;
112
if (c1.getState()){
cost+=50000*Integer.parseInt(tf1.getText());
}
}
costEt=new Label(String.valueOf(cost));
p.add(costEt);
p.validate();
cost=0;
}
}

public static void main(String[] arg){


Frame f=new CasaDeBilete();
f.setBounds(250, 250, 250,200);
f.show();
}
}
13.3 EVENIMENTE DE NIVEL COBORÂT
Mai există o categorie de evenimente, evenimentele de nivel coborât (Diagrama 13-2), ce apar
datorită modificării componentelor şi sunt de mai multe tipuri:
- Evenimente ale componentelor raportează modificarea poziţiilor, dimensiunilor şi a vizibilităţii
componentelor: ComponentEvent;
- Evenimente de intrare generate de folosirea tastaturii sau a mouse-ului: InputEvent;
- Evenimente ale focusului când o componentă primeşte sau pierde focusul de la tastatură:
FocusEvent;
- Evenimente produse de tastatură: KeyEvent;
- Evenimente ale ferestrelor prezintă starea fiecărei ferestre: WindowEvent;
- Evenimente ale container-elor informează de adăugarea sau eliminarea unei componente dintr-
un container: ContainerEvent.

AWTEvent

ComponentEvent

getComponent() : Component

FocusEvent InputEvent WindowEvent ContainerEvent

getOppositeComponent() : Component isShiftDown() : boolean getNewState() : int getChild() : Component


isTemporary() : boolean isAltDown() : boolean getOldState() : int getContainer() : Container
isAltGraphDown() : boolean getWindow() : Window
isControlDown() : boolean getOppositeWindow() : Window

MouseEvent KeyEvent

getClickCount() : int <<static>> getKeyText(car : char) : String


getX() : int setKeyCode(cod : int)
getY() : int getKeyCode() : int
getPoint() : Point setKeyChar(car : char)
getKeyChar() : char

Diagrama 13-2. Tipuri de evenimente de nivel coborât

13.1.1 Gestiunea evenimentelor de tip FocusEvent


Un eveniment de tip FocusEvent este generat atunci când o componentă este selectată sau
deselectată cu ajutorul tastei Tab.
Exemplu. Să scriem o clasă care afişează o fereastră DateAngajat cu interfaţa grafică din Figura.
Observăm că fereastra conţine un câmp de text în care utilizatorul trebuie să introducă data naşterii
113
în momentul în care câmpul respectiv este activat, adică are focus-ul. Când câmpul de text pierde
focus-ul, programul verifică dacă data de naştere introdusă respectă formatul “zz/ll/aa”. În caz
afirmativ, focusul trece pe următoarea componentă, adică butonul Inchide şi dacă este apăsat Enter,
fereastra Angajat este închisă. Dacă data de naştere nu este validă, programul afişează o fereastră de
dialog ce conţine un mesaj de atenţionare şi plasează cursul în campul de text corespunzător datei
de naştere. Programul este prezentat în continuare.
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.text.*;
import java.util.*;
public class FereastraAngajat extends Frame{
private TextField nume, prenume, dataNastere;
private Button b;
private AscultatorCamp ac;
private AscultatorButon ab;
public FereastraAngajat(){
super("Date angajat");
setLayout(new GridLayout(4,2, 10,10));
add(new Label("Nume")); Figura 13-6. Fereastra DateAngajat
nume=new TextField(10);
add(nume);
add(new Label("Prenume"));
prenume=new TextField(10);
add(prenume);
add(new Label("Data nastere"));
dataNastere=new TextField(10);
add(dataNastere);
ac=new AscultatorCamp();
dataNastere.addFocusListener(ac);
b=new Button("Inchide");
add(b);
ab=new AscultatorButon();
b.addKeyListener(ab);
}
class AscultatorCamp extends FocusAdapter{
Dialog d;
public void focusLost(FocusEvent e){
String data=dataNastere.getText();
DateFormat df=DateFormat.getDateInstance(DateFormat.SHORT);
try{
df.parse(data);
}catch(ParseException nf){
d=new Dialog(FereastraAngajat.this, "Eroare");
d.setLayout(new GridLayout(2,1));
d.add(new Label("Format data invalid!! (ll/zz/aa)"));
Button ok=new Button("OK");
ok.addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent e){
dataNastere.setText("");
d.dispose();
dataNastere.requestFocus();
}
});
d.add(ok);
114
d.setBounds(250,250, 100, 100);
d.setVisible(true);
}//catch
}//metoda focusLost
}
class AscultatorButon extends KeyAdapter{
public void keyPressed(KeyEvent e){
if(e.getKeyCode()==KeyEvent.VK_ENTER)System.exit(0);
}
}
public static void main(String[] args){
Frame f=new FereastraAngajat();
f.setBounds(200,200,200,175);
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
}
}
Programul 13-3. Programul DateAngajat

13.3.1 Gestiunea evenimentelor de tip MouseEvent


Un obiect MouseEvent poate executa 5 metode ale interfeţei MouseListener după cum urmează:
- dacă evenimentul este de tip MOUSE_PRESSED, va fi apelată metoda mousePressed()
- dacă evenimentul este de tip MOUSE_RELEASED, va fi apelată metoda mouseReleased()
- dacă evenimentul este de tip MOUSE_CLICKED, va fi apelată metoda mouseClicked()
- dacă evenimentul este de tip MOUSE_ENTERED, va fi apelată metoda mouseEntered()
- dacă evenimentul este de tip MOUSE_EXITED, va fi apelată metoda mouseExited().
De exemplu, când utilizatorul face un clic cu mouse-ul, Java apelează metoda mouseClicked() al
obiectului ascultător.
Utilizând metodele getX() şi getY() pe argumentul MouseEvent, se pot obţine coordonatele x
şi y ale cursor-ului mouse-ului.
Pentru a diferenţia între un singur clic şi dublu clic se utilizează getClickCount(). Această
metodă returnează valoarea 2 dacă au fost efectuate două click-uri în timpul evenimentului de tip
MOUSE_CLICKED.
Exemplu. Vom prezenta un program numit EvenimenteMouse care utilizează evenimente de tip
MouseEvent. Programul va prezenta într-o fereastră două componente grafice: un Canvas la stânga
şi un TextArea la dreapta ().Obiectul Canvas va fi utilizat ca sursă de evenimente generate de
mouse. Ascultătorul este însăşi fereastra. De fiecare dată când mouse-ul intră în spaţiul canvas-ului
sau iese din această arie sau când utilizatorul face un click sau un dublu click pe canvas, obiectul
Canvas lansează evenimente de tip MouseEvent care vor fi primite şi gestionate de fereastra
EvenimenteMouse. Gestiunea evenimentelor presupune scrierea în aria de text a unui text ce
conţine coordonatele punctului în care s-a efectuat o acţiune cu mouse-ul şi a fost lansat
evenimentul, precum şi tipul evenimentului.

115
Figura 13-7. Fereastra programului EvenimenteMouse
Programul EvenimenteMouse este prezentat în continuare.
import java.awt.*;
import java.awt.event.*;
public class EvenimenteMouse extends Frame implements MouseListener{
TextArea ta;
Canvas c;
public EvenimenteMouse(){
super("Evenimente generate de mouse");
c = new Canvas();
c.setSize(250, 200);
c.setBackground(Color.red);
c.addMouseListener(this);
add(c, BorderLayout.CENTER);
ta = new TextArea(20, 40);
add(ta, BorderLayout.EAST);
}
public void mouseEntered(MouseEvent e){
ta.append("Mouse intrat in canvas in punctul "+e.getX()+","+e.getY()+")
\n");
}
public void mousePressed(MouseEvent e){
ta.append("Buton mouse apasat in punctul "+e.getX()+","+e.getY()+")
\n");
}
public void mouseReleased(MouseEvent e){
ta.append("Buton mouse eliberat in punctul
("+e.getX()+","+e.getY()+")\n");
}
public void mouseClicked(MouseEvent e){
if(e.getClickCount()==2) ta.append("Dublu click in punctul
("+e.getX()+","+e.getY()+")\n");
else ta.append("Un click in punctul ("+e.getX()+","+e.getY()+")\n");
}
public void mouseExited(MouseEvent e){
ta.append("Mouse iesit din canvas in punctul ("+e.getX()+","+e.getY()+
")\n");
}
public static void main(String[] args){
116
Frame f=new EvenimenteMouse();
f.setBounds(320, 240, 500, 250);
f.show();
}
}
Programul 13-4. Programul EvenimenteMouse

13.3.2 Gestiunea evenimentelor de tip KeyEvent


Un eveniment de tip KeyEvent este lansat ori de câte ori o tastă este apăsată, eliberată sau este
apăsată o tastă caracter. Dacă suntem în ultimul caz, caracterul transmis poate fi obţinut prin
folosirea getKeyChar() din clasa KeyEvent. Pentru evenimente generate de apăsarea sau eliberarea
unei taste, putem folosi metoda getKeyCode() ce returnează codul Unicode al tastei implicate în
eveniment. Aceste cod este furnizat şi de o constantă a clasei KeyEvent. De exemplu, codul
Unicode al tastei direcţionale ← este memorat în constanta VK_LEFT şi poate fi folosită pentru a
şti dacă tasta ← a fost apăsată de către utilizator.
Exemplu. Programul următor vizualizează o fereastră vidă pe ecran şi permite utilizatorului să apese
tastele săgeţi pentru a muta fereastra pe ecranul monitorului. În plus, utilizatorul poate folosi tastele
C, S şi D pentru a plasa fereastra în centrul, stânga sau dreapta ecranului.
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class AscultatorFereastra implements KeyListener{


private Frame f;
private int x, y;

public AscultatorFereastra(Frame f){


this.f=f;
Point p=f.getLocation();
x=(int)p.getX();
y=(int)p.getY();
}
public void keyPressed(KeyEvent e){
switch(e.getKeyCode()){
case KeyEvent.VK_UP: y-=10;break;
case KeyEvent.VK_DOWN: y+=10;break;
case KeyEvent.VK_LEFT: x-=10;break;
case KeyEvent.VK_RIGHT: x+=10;
}
f.setLocation(x,y);
}
public void keyReleased(KeyEvent e){
Toolkit toolkit=Toolkit.getDefaultToolkit();
Dimension dimEcran=toolkit.getScreenSize();
switch(e.getKeyCode()){
case KeyEvent.VK_S: f.setLocation(0,y);break;
case KeyEvent.VK_C: x=(int)(dimEcran.getWidth()/2-f.getWidth()/2);
y=(int)(dimEcran.getHeight()/2-f.getHeight()/2);
f.setLocation(x,y);
break;
case KeyEvent.VK_D: f.setLocation((int)(dimEcran.getWidth()-
f.getWidth()),y);
}
}
117
public void keyTyped(KeyEvent e){}
public static void main(String[] args){
Frame f=new Frame("Fereastra");
f.addKeyListener(new AscultatorFereastra(f));
f.setSize(100, 200);
f.setVisible(true);
}
}
13.3.3 Gestiunea evenimentelor de tip WindowEvent
Exemplu. Vom scrie codul ce permite crearea unei ferestre care se închide de la butonul cu această
funcţionalitate din bara de titlu a ferestrei. Când utilizatorul execută un clic de mouse pe acest
buton, are loc un eveniment de tip WindowEvent, adică este creat un obiect din această clasă.
Având acest obiect, putem implementa interfaţa WindowListener, care conţine 7 declaraţii de
metode:
public void windowClosed(WindowEvent e) ;
public void windowIconified(WindowEvent e);
public void windowOpened(WindowEvent e);
public void windowClosing(WindowEvent e);
public void windowDeiconified(WindowEvent e);
public void windowActivated(WindowEvent e);
public void windowDeactivated(WindowEvent e);
şi fereastra se poate înregistra ca ascultător al evenimentelor apărute de tip WindowEvent:
void addWindowListener(WindowListener)
Dintre declaraţiile de metode ale lui WindowListener, ne interesează numai
windowClosing(WindowEvent e), care va apelată în cazul închiderii ferestrei. Bineînţeles că şi
restul metodelor vor implementate, dar corpul acestora va fi vid. Programul este prezentat în
continuare.
import java.awt.*;
import java.awt.event.*;
public class FereastraCareSeInchide extends Frame implements
WindowListener {
public FereastraCareSeInchide() {
setSize(300, 200);
addWindowListener(this);
}

public void windowClosed(WindowEvent e) {}


public void windowIconified(WindowEvent e){}
public void windowOpened(WindowEvent e) {} Frame WindowListener
public void windowClosing(WindowEvent e) {
System.exit(0);
} FereastraCareSeInchide
public void windowDeiconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}

public static void main(String[] args) {


Frame f = new FereastraCareSeInchide();
f.show();
}
}
13.4 CLASE ADAPTER ALE INTERFEŢELOR DE ASCULTARE
După cum am văzut până acum, interfeţele ascultător ce tratează evenimentele de nivel coborât
conţin mai multe metode ce trebuie să fie implementate de orice clasă care se înregistrează la o
118
sursă ce generează evenimente din această categorie. Biblioteca Java furnizează în pachetul
java.awt.event clase adapter, câte una pentru fiecare din aceste interfeţe ascultător ce conţine mai
multe metode. Un adapter este o clasă care doar implementează una din aceste interfeţe şi atât. Cu
alte cuvinte, metodele oricărui adapter sunt vide. De exemplu, clasa WindowAdapter , adapter al
interfeţei WindowListener, are urmatoarea structură:
public class WindowAdapter implements WindowListener{
public void windowClosed(WindowEvent e){}
public void windowIconified(WindowEvent e){}
public void windowOpened(WindowEvent e){}
public void windowClosing(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowActivated(WindowEvent e){}
public void windowDeactivated(WindowEvent e){}
}
Atunci, orice clasă ascultător ar putea extinde o clasă adapter şi să redefinească numai metoda sau
metodele pe care o interesează. De exemplu, putem modifica programul anterior folosind o clasă
internă sau nu, numită InchidereFereastra care extinde clasa adapter WindowAdapter şi redefineşte
numai metoda windowClosing(). Programul rezultat este prezentat în continuare.
import java.awt.*;
import java.awt.event.*;
public class FereastraCareSeInchide extends Frame{
public FereastraCareSeInchide() {
InchidereFereastra if = new InchidereFereastra();
addWindowListener(if);
setSize(300, 200);
setTitle(getClass().getName());
}
public static void main(String[] args){
Frame f = new FereastraCareSeInchide();
f.show();
}
class InchidereFereastra extends WindowAdapter(
void windowClosing(WindowEvent e){
System.exit(0);
}
}
}
În tabelul următor (Tabelul 13-2) prezentăm toate interfeţele ascultător din pachetul java.awt.event
împreună cu metodele lor şi clasa adapter asociată, precum şi tipurile de componente grafice la care
se pot înregistra obiecte ale claselor care implementează interfaţa ascultător respectivă sau extind
clasa adapter corespunzătoare interfeţei respective.

Interfaţă Listener Clasă adapter Metode Evenimente generate


de:
ActionListener nu există actionPerformed Button, List,
MenuItem,
TextField
AdjustmentListener nu există adjustmentValueChanged Scrollbar
ComponentListener ComponentAdapter componentHidden Component
componentMoved
componenResized
componentShown
ContainerListener ContainerAdapter componentAdded Container
componentRemoved
FocusListener FocusAdapter focusGained Component
focusLost
ItemListener nu există itemStateChanged Checkbox,
CheckboxMenuItem,

119
Interfaţă Listener Clasă adapter Metode Evenimente generate
de:
Choice, List
KeyListener KeyAdapter keyPressed Component
keyReleased
keyTyped
MouseListener MouseAdapter mouseClicked Component
mouseEntered
mouseExited
mousePressed
mouseReleased
MouseMotionListener MouseMotionAdapter mouseDragged Component
mouseMoved
TextListener nu există textValueChanged TextComponent
WindowListener WindowAdapter windowActivated Window
windowClosed
windowClosing
windowDeactivated
windowDeiconified
windowIconified
windowOpened

Tabelul 13-2. Clase adapter ale interfeţelor ascultător

13.1.2 Clase anonime şi adaptori


Am văzut că orice interfaţă xxxxListener cu mai multe metode are o clasă xxxxAdapter care
introduce o implementare vidă pentru metodele interfeţei. Obiectul ascultător poate fi al unei
subclase a adaptorului care redefineşte numai metodele necesare.
În practică, obiectul ascultător face parte dintr-o clasă internă anonimă definită ca subclasă a
ascultătorului. De exemplu, putem modifica programul anterior şi să facem clasa InchidereFereastra
clasă anonimă. Atunci obiectul referit de variabila if va fi creat local în metoda addWindowListener
unde vom defini şi structura clasei anonime:
import java.awt.*;
import java.awt.event.*;
public class FereastraCareSeInchide extends Frame{
public FereastraCareSeInchide(){
addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});//inchiderea metodei addWindowListener
setSize(300, 200);
setTitle(getClass().getName());
}
public static void main(String[] args){
Frame f = new FereastraCareSeInchide();
f.show();
}
}
Exemplu.
class Controller implements ActionListener{
private PrintWriter out;
public void actionPerformed(ActionEvent e){
if (e.getSource()==print){
PrinterJob imprimanta=PrinterJob.getPrinterJob();
Book bk=new Book();
bk.append(new ContinutPanel(), imprimanta.defaultPage());
imprimanta.setPageable(bk);
if(imprimanta.printDialog()){
120
try{imprimanta.print();}
catch (PrinterException pe){arie.append("Imprimanta nu exista");arie.repaint();}
catch(ArrayIndexOutOfBoundsException ae){System.out.println("Ce se printeaza???");}
}
}
else if (e.getSource()==printF){
try{
out=new PrintWriter(new FileOutputStream("factura.dat"));
out.write(factura.toString());
out.flush();
out.close();
}catch(FileNotFoundException fe){}
catch(IOException ioe){}
}
else if (e.getSource()==arhivare){ facturi.adaugaFactura(factura); }
else if (e.getSource()==cancel) setVisible(false);}
}

class ContinutPanel extends Panel implements Printable


{ private BufferedReader br;
private String sir="";
public int print(Graphics g, PageFormat pf,int pageIndex) throws PrinterException{
g.setColor(Color.black);
try{
StringReader continut=new StringReader(arie.getText());
br=new BufferedReader(continut);
int i=0;
while((sir=br.readLine())!=null) {
if (sir.length()==0) sir=" ";
g.drawString(sir,100,100+i);i+= 20;
}
}catch(IOException io){}
catch (IllegalArgumentException ie){}
return Printable.PAGE_EXISTS;
}
}

121
14. PROGRAMAREA INTERFETELOR GRAFICE CU SWING
Swing este un tehnologie API care extinde AWT-ul pentru construirea interfetelor grafice. Swing
este o multime de componente usoare aflate in pachetul javax.swing.*.
Spre deosebire de componentele grele nu sunt incadrate intr-o fereastra (window) proprie, opaca,
nativa (1), ci in fereastra container-ului lor greu (2). Din (1) rezulta ca, componentele usoare pot
avea background-uri transparente.
Aproape toate componentele Swing-ului sunt usoare; exceptiile sunt container-ele de nivel inalt:
- JFrame – implementeaza o fereastra principala,
- JApplet- implementeaza o arie dreptunghiulara in fereastra browser-ului
- JWindow – o fereastra externa,
- JDialog – implementeaza o fereastra secundara
Din (2) rezulta ca componentele usoare trebuie sa stea in ultima instanta intr-un container greu,
adica o subclasa a clasei java.awt.Container.

JFrame public class Fereastra extends JFrame{

public Fereastra(){
Container
container=getContentPane();
JPanel panel=new JPanel();
Content pane panel.add(new JButton(“Buton”));
panel.add(new JLabel(“Label”));
container.add(panel,
BorderLayout.NORTH);
JPanel }
}

JButton JLabel

Container-e usoare:
1. JPanel – succesorul lui Panel si Canvas. De aceea, are un rol dublu: este un simplu container si
un canvas pentru afisarea graficelor.
2. JRootPane – este continut in orice container greu.
- furnizeaza o ierarhizare a continutului:
Panel radacina
Componenta Clasa Descriere
stratPanel stratPanel JLayeredPane contine continutPanel
continutPanel si menuBar
continutPanel JPanel contine componentele
menuBar aplicatiei sau applet
menuBar JMenuBar sta deasupra lui
sticlaPanel continutPanel
sticlaPanel JPanel capteaza evenimentele
mouse-ului si poate fi
transparent

122
Exemplu. Sa scriem un program care contine un buton care determina afisarea/ascunderea unui
panel de sticla ce afiseaza textul “Buna ziua” pe toata suprafata sa.
import javax.swing.*; class PanelSticla extends JPanel{
import java.awt.event.*;
import java.awt.*; public PanelSticla(){
setOpaque(false);
addMouseListener(new MouseAdapter(){
public class Prob1 extends JFrame implements public void mousePressed(MouseEvent e){
ActionListener setVisible(false);
{ }});
private Component panelSticla; }
private JButton buton;
public void paintComponent(Graphics g){
public Prob1(){ String sir="Buna ziua";
Dimension d= getSize();
panelSticla=new PanelSticla(); FontMetrics f=g.getFontMetrics();
setGlassPane(panelSticla); int l=f.stringWidth(sir);
int i=f.getHeight();
buton=new JButton("", new
ImageIcon("swing.small.gif")); g.setColor(Color.blue);
buton.addActionListener(this);
Container content=getContentPane(); for (int li=i; li<d.height; li+=i)
for (int c=0; c<d.width; c+=l)
content.add(buton); g.drawString(sir, c, li);
} }
}
public void actionPerformed(ActionEvent e){
panelSticla.setVisible(true);
}

public static void main(String[] arg){


JFrame f=new Prob1();
f.setSize(200,200);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}}
3. JLayeredPane –nu are un layout manager implicit si permite plasarea componentelor pe diferite
straturi:
Strat Valoare Descriere
FRAME_CONTENT_LAYER -3000 Stratul de baza, unde bar menu-ul si content pane sunt
asezate
DEFAULT_LAYER 0 Implicit, pe acest strat sunt plasate componentele
PALETTE_LAYER 100 Folosit pentru palete si toolbar-uri
MODAL_LAYER 200 Folosit de catre ferestrele de dialog
POPUP_LAYER 300 Pentru menu-urile popup
DRAG_LAYER 400 Util in deplasarea componentelor sau a celor care
trebuie sa stea deasupra tuturor celorlalt
Obs. Straturile cu valori mai mari sunt plasate deasupra straturilor cu valori mai mici.
Plasarea componentelor poate fi controlata si in cadrul unui acelasi strat prin urmatoarele
proprietati:
Proprietate Descriere
Index Indexul intr-un vector de componente pastrat de panelul stratificat. Indicele are
-getIndexOf(comp) legatura cu ordinea componentelor: cele cu indice mai mic sunt plasate
deasupra celor cu indice mai mare.
Strat Stratul pe care se afla componenta respectiva. Componentele de pe un strat cu
-getLayer(comp) numar mai mic sunt afisate sub componentele ce se afla pe straturi cu numar
mai mare.
Pozitie Pozitia unei componente relativa la celelalte componente din acelasi strat.
-getPosition(comp) Componentele cu pozitii mai mici sunt afisate deasupra celor cu pozitii mai
mari.

4. JTabbedPane – panel ce contine pagini (tab-uri) de paneluri.

123
Exemplu. Fereastra cu 2 pagini de panel-uri ce contin cate un buton.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;

public class Prob3 extends JFrame{

private JTabbedPane tp;

public Prob3(){
tp=new JTabbedPane();

JPanel panel1=new JPanel();


panel1.add(new JButton("buton in panelul 1"));

JPanel panel2=new JPanel();


panel2.add(new JButton("buton in panelul 2"));

tp.add(panel1, "Primul panel");


tp.addTab("Al doilea panel", new ImageIcon("ok.gif"), panel2,
"Panelul 2");

Container contentPane=getContentPane();
contentPane.add(tp);
}

5. JSplitPane – afiseaza doua componente (initial, butoane) separate de un despartitor (divider).


Componentele pot fi orientate vertical sau orizontal (JSplitPane.HORIZONTAL_SPLIT sau
VERTICAL_SPLIT). Metoda: setOrientation(constanta).

Panelul foloseste doua proprietati booleene:


- layout continuu – controleaza daca componentele continute sunt incontinuu actualizate in timp
ce separatorul este deplasat. Metoda: setContinuousLayout(boolean)
- redimensionare la comanda (one-touch expandable) determina daca afiseaza un control in
separator. Controlul permite marirea sau micsorarea componentelor cand este click-at. Metoda:
setOneTouchExpandable(boolean)
Exemplu.
public class Prob4 extends JFrame{

private JSplitPane sp;

public Prob4(){
sp=new JSplitPane();
sp.setContinuousLayout(true);
//sp.setOneTouchExpandable(true);
sp.setDividerSize(15);
sp.setOrientation(JSplitPane.HORIZONTAL_SPLIT);

Container contentPane=getContentPane();
contentPane.add(sp);
}

124
FERESTRE INTERNE
Swing ofera functionalitate MDI (Multiple Documente Interface) prin ferestre interne- clasa
JInternalFrame, care stau pe desktop -clasa JDesktopPane.
Ferestrele interne sunt:
- ferestre deoarece furnizeaza multe din caracteristicile unei ferestre (inchidere, deschidere,
maximizare, minimizare, iconificare, redimensionare),
- interne deoarece stau, fiind componente usoare, intr-un alt container Swing, de obicei un
desktop pane.
Proprietati:
Nume Tip de data Acc Valoarea Descriere
Proprietate es implicita
closable boolean C3S false Indica daca fereastra interna poate
G 1
fi inchisa de catre utilizator
closed boolean SG false indica daca fereastra este inchisa la
momentul respectiv
contentPane Container SG instanta de JPanel container-ul ce contine
componentele din fereastra
defaultCloseOpe int SG WindowConstant indica operatia care are loc cand
ration s.HIDE_ON_CL fereastra este inchisa
OSE
desktopIcon JDesktopIcon SG L&F Icon-ul afisat pe desktop cand
fereastra este redusa la icon
desktopPane JDesktopPane G - O instanta a lui JDesktopPane ce
contine una sau mai multe ferestre
interne
frameIcon Icon SG L&F Icon-ul afisat in bara de titlu al
ferestrei
glassPane Component SG instanta JPanel Panelul de sticla asociat cu panelul
radacina al unei ferestre interne
icon boolean SG false Determina daca fereastra interna
este (poate fi) redusa la icon –
setIcon(boolean)
iconifiable boolean C5S false Determina daca fereastra interna
G poate fi redusa la un icon prin
apelul metodei setIcon(boolean)
layer Integer SG DEFAULT_LAY Stratul pe care sta fereastra interna.
ER Implicit este DEFAULT_LAYER
layeredPane JLayeredPane SG JLayeredPane Panelul stratificat asociat panelului
radacina al ferestrei interne
maximizable boolean C4S false Indica daca fereastra poate fi
G maximizata printr-un clic pe
butonul corespunzator
maximum boolean SG false Determina daca fereastra este
maximizata
jMenuBar JMenuBar SG null Bara de menu asociata panelului
radacina al ferestrei interne
resizable boolean C2S false Determina daca fereastra poate fi
G redimensionata. Daca este
maximizata nu poate fi
redimensionata
rootPane JRootPane SG JRootPane Panelul radacina asociat ferestrei

1
C-poate apare in constructor, S-setter, G-getter
125
selected boolean SG false Selecteaza sau deselecteaza
fereastra interna
title String C1S null Titlul afisat in bara de titlu al
G ferestrei
Exemplu. Sa cream o fereastra care la actiunea unui buton vor fi create ferestre interne (ce vor
contine o eticheta)

public class Prob1 extends JFrame implements ActionListener{


private JDesktopPane dp;
private JInternalFrame fi;
private JButton buton;

public Prob1(){
Container contentPane=getContentPane();

buton=new JButton("creaza Frame");


buton.addActionListener(this);
contentPane.add(buton, BorderLayout.NORTH);

dp=new JDesktopPane();
dp.setLayout(new FlowLayout());
contentPane.add(dp, BorderLayout.CENTER);
}
public void actionPerformed(ActionEvent e){
fi=new JInternalFrame("O noua fereastra interna", true,true, true, true);
fi.setPreferredSize(new Dimension(250,150));
//fi.setFrameIcon(new ImageIcon("ok.gif"));
/*Container content=fi.getContentPane();
JPanel p=new JPanel();
p.add(new JLabel(new ImageIcon("swing.small.gif")));
content.add(p);*/
dp.add(fi);
fi.setVisible(true);
dp.revalidate();}
Scroll-area componentelor

Este necesara in cazul in care o componenta (tabele, texte, trees, imagini, liste) este mai mare decat
spatiul de afisare. Pentru aceasta putem folosi urmatoarele clase:
- container-ele usoare JViewport si JScrollPane (inlocuieste si imbogateste componenta grea
AWT ScrollPane)
- interfata Scrollable
- JScrollBar folosit pentru implementarea manuala a barelor de scroll-are
CLASA JVIEWPORT
- instantele sale furnizeaza un obiectiv (porthole) prin care se afiseaza o anumita regiune a
vederii. Pozitia vederii poate fi manevrata pentru a afisa regiuni diferite ale vederii astfel:
- pentru a muta vederea in sus: crestem coordonata Y a vederii
- pentru a muta vederea in jos: descrestem coordonata Y a vederii
- pentru a muta vederea la stanga: crestem coordonata X a vederii
- pentru a muta vederea la dreapta: descrestem coordonata X a vederii
Proprietati
Nume Tip de data Acc Valoarea Descriere
Proprietate es implicita
scrollMode int SG SIMPLE moduri de a realiza scroll-area:
_SCROL BLIT_SCROLL_MODE,
L_MOD BACKINGSTORE_SCROLL_MODE,
E SIMPLE_SCROLL_MODE
extentSize Dimension SG - Este o instanta a clasei Dimension care
reprezinta partea vizibila a vederii
view Component SG - Componenta afisata in viewport
126
viewPosition Point SG - Este un punct ce reprezinta coordonatele
vederii din coltul stanga sus al obiectivului
viewRect Rectangle G - O instanta a lui Rectangle care reprezinta
dimensiunea si pozitia partii vizibile a
vederii. Latimea si inaltimea dreptunghiului
sunt egale cu extent
viewSize Dimension SG - O instanta a lui Dimension care reprezinta
dimensiunea vederii. Daca nu este setata
explicit, este egala cu dimensiunea preferred
a vederii
JSCROLLPANE
- este container uşor ce contine un viewport cu scrollbar-uri si header-e de coloane si linie
optionale.
Elementele unui JScrollPane:

Proprietati
Nume Proprietate Tip de data Acc Valoarea Descriere
es implicita
columnHeader JViewport SG null o instanta a lui JViewport pentru header-ul de
coloane
columnHeaderVie Component S null o instanta a lui Component folosita ca header
w de coloane pentru vederea viewport-ului
corner Component SG null o componenta care este afisata in unul din
cele patru colturi:
ScrollPaneConstants.UPPER_LEFT_CORNE
R
ScrollPaneConstants.LOWER_LEFT_CORN
ER
ScrollPaneConstants.UPPER_RIGHT_CORN
ER
ScrollPaneConstants.LOWER_RIGHT_COR
NER
horizontalScrollb JScrollBar SG - Scrollbar-ul orizontal folosit de panel
ar
horizontalScrollb int CSG JScrollPa Politica folosita pentru detrminaea
arPolicy ne.HORI circumstantelor in care scrollbar-ul orizontal
ZONTA este afisat. Constante:
L_SCRO ScrollPaneConstants.HORIZONTAL_SCRO
LLBAR_ LLBAR_AS_NEEDED
127
AS_NEE ScrollPaneConstants.HORIZONTAL_SCRO
DED LLBAR_NEVER
ScrollPaneConstants.HORIZONTAL_SCRO
LLBAR_ALWAYS
rowHeader JViewport SG null o instanta a lui JViewport pentru header-ul de
linii
rowHeaderView Component G null o instanta a lui Component folosita ca header
de linii pentru vederea viewport-ului
verticalScrollbar JScrollBar SG -
verticalScrollbarP int CSG JScrollPa ScrollPaneConstants.VERTICAL_SCROLLB
olicy ne.HORI AR_AS_NEEDED
ZONTA ScrollPaneConstants.VERTICAL_SCROLLB
L_SCRO AR_NEVER
LLBAR_ ScrollPaneConstants.VERTICAL_SCROLLB
AS_NEE AR_ALWAYS
DED
viewport JViewport SG JViewpo O instanta a lui JViewport folosita pentru
rt afisarea componentei scroll-ata de panel
viewportBorder Border SG null O bordura pentru viewport
viewportView Component CSG null componenta afisata in viewport-ul panelului

128
15. BIBLIOGRAFIE
I. Athanasiu si colectiv, Limbajul Java. O perspectivă pragmatică. Editura Teora, 1998
B. Eckel, Thinking in Java. Prentice Hall, 1998
C. S. Horstmann, Computing Concepts with Java 2 Essentials, Second Edition. John Wiley&Sons, 2000
S. Tănasă, C. Olaru, S. Andrei, Java de la 0 la expert. Editura Polirom, 2003

129

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