INFORMATICĂI IE. TEHNICI AVANSATE DE PROGRAMARE [603564]

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 1 –

IE.
TEHNICI AVANSATE DE
PROGRAM ARE

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 2 – CUPRINS
Cuvânt înainte ……………………………………………………………………………………………….. …………………… ….. 6

Capitolul I E.01. Noțiuni de bază ale program ării orientate obiect ………………………… …………… 7

IE.01.1. Paradigme de programare …………………………………….. ………………………………………. 7
IE.01.2. Programare orientat ă pe obiecte ………………. ………………………………………………… … 8
IE.01.3. Avantajele p rogram ării cu obiecte ………………………………………………………………… 11
IE.01.4. Clasele ca module de program reutilizabile …………… ………………………………………. 13
IE.01.5. Încapsulare ………………………… …………………………………………………………………….. 14
IE.01.6. Clasele permit programarea generic ă …………………… ……………………………………….. 15
IE.01.7. Clasele creeaz ă un model pentru universul aplica ției ………………………………………. 16
IE.01.8.Tehnici de programare specifice POO …………… ………………………………….. ……….. 17
IE.01.9. Analiza și proiectarea orientate pe obiecte …………… …………………………………….. 19

Capitolul IE. 02. Clase și obiecte în Java ……………………………………………………………………………. ……… 21

IE.02.1. Sintaxa limbajului Java …………………… …………………………………………… ……………. 21
IE.02.2. Definirea de clase în Java …………………………………………………………….. ……………… 30
IE.02.3. Obiecte Java ……………………………………………………………………………………………….. 33
IE.02.4. Șiruri de caractere în Java …………………………………………………………………………….. 34
IE.02.5. Opera ții de citire -scriere în Java ……………………………………………………………………. 36

Capit olul IE. 03. Derivare, mo ștenire, polimorfism în Java ………………………………………… …..39

IE.03.1. Derivarea (extinderea) claselor ………….. …………………………………………… …………… 39
IE.03.2. Clasa Object ca baz ă a ierarhiei de clase Ja va …………………………………… ………….. 40
IE.03.3. Suprascriere de metode în subclase …….. …………………………………………… ……………. 41
IE.03.4. Derivarea ca metod ă de reutilizare ……….. …………. ……………………………… ……………. 42
IE.03.5. Derivare pentru crearea unor ierarhii de tipuri ………………………………….. …………….. 44
IE.03.6. Polimorfism ……………… ……………. ……………………………….. ……….. …………………. 46
IE.03.7. Delegarea ca alternativ ă la derivare pentru reutilizare ……………………………… ………. 48
IE.03.8. Moștenire multipl ă prin derivare și delegare ……………………………………. …………….. 50

Capitolul IE. 04. Clase abstracte și interfe țe …………………………………………………………… … 54

IE.04.1. Clase abstracte în Java ……… ………….. …………………………………………… …………… 54
IE.04.2. Interfe țe în Java …………. ………………………. ……………………………………….. …………… 56
IE.04.3. Compara ție între interfe țe și clase abstracte ……………………………………….. …………… 57
IE.04.4. Interfe țe Java pentru compa rarea de obiecte ……………………………………… ……………. 59
IE.04.5. Interfe țe Java pentru enumerare ………….. …………………………………………. ……………. 59
IE.04.6. Interfe țe Java pentru filtrare ………………… ………………………………….. ……………. 61

Capitolul IE.05. Tehnici de programare orientat ă obiect în Java …………………………………… .… 66

IE.05.1. Excep ții în Java ………………………………………………………………………………………….. 66
IE.05.2. Reflec ție în Java ……………………………………………………………… ………… ……………….. 69
IE.05.3. Clase incluse ………………………………………………………………………. 70
IE.05.4. Fire de execu ție ca obiecte ……………………………………………………….. ………. …………. 74

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 3 – Capitolul IE.06. Colec ții de obiecte în Java ………………………………………………………….. 82

IE.06.1. Grupul claselor colec ție ……………………………………………………………… ……………. 82
IE.06.2. Mulțimi de obiecte ……………………………………………………………………………………. 84
IE.06.3. Liste ( secven țe) …………… …………………………………………………………………………. 85
IE.06.4. Dicționare (asocieri) …………………………………………………………………………………. 87
IE.06.5. Colec ții ordonate ……………………………………………………………………………………… 88
IE.06.6. Iteratori ………………………………………… ………………………………………………………… 90
IE.06.7. Generice …………………………………………………………………………………………….. …… 92

Capitolul IE.07. Diagrame de clase în UML .. ……………………………………………………….. 96

IE.07.1. Grupul claselor colec ție ……………………………………………………………………………. 96
IE.07.2. Standardul UML ……………………………………………………………………………………… 96
IE.07.3. Relația de generalizare în UML………………………………………………………………….. 98
IE.07.4. Relații de asociere în UML……………………………… ………………………………………… 98
IE.07.5. Relația de includere în UML ……………………………………………………………………. 100
IE.07.6. Clase abs tracte și interfe țe în UML …………………………………………………………… 101
IE.07.7. Studiu de caz ……………………………………………………………………………………… …. 103

Capitolul IE.08. Șabloane de proiectare cu clase ………………………………………………….. 106

IE.08.1. Proiectarea orientat ă pe obiecte ………………………………………….. …………………. 106
IE.08.2. Șabloane de proiectare …………………………………………………………………………… 107
IE.08.3. Șablonul Singleton ……… …………………………………………………………………………. 107
IE.08.4. Șablonul Iterator ………….. ………………………………………………………………………… 109
IE.08.5. Șablonul Obser vator………… ……………………………………………………………………. 110
IE.08.6. Șablonul Adap tor……………………… …………… ……………………………………………… 111
IE.08.7. Șablonul Compozit ………………………………………………………………………………….. 112
IE.08.8. Șablonul Deco rator …………………………………………………………………………………. 113
IE.08.9. Șablonul Fabric ă de obiecte.. …………………………………………………….. …………… 114
IE.08.10. Șablonul Fabric ă abstract ă…….. …………………………………………………………… 116
IE.08.11. Șablonul Model -View -Controller.. ………… ……………………………………………….. 117
IE.08.12. Șablonul DI (Injectarea dependen țelor) ………………………………………………….. 119

Capitolul IE.09. Interfe țe grafice și programare orientat ă pe evenimente …………. …………….. 123

IE.09.1. Aplica ții cu interfa ță grafic ă ….. ……………………………………………………………….. 123
IE.09.2. Clase Java pentru o interfa ță grafic ă ………………………………………………………… 124
IE.09.3. Plasarea componentelor ……………………………………. ……………………………………. 125
IE.09.4. Structura programelor cu interfa ță grafic ă …………………………………………………. 127
IE.09.5. Programarea bazat ă pe evenimente …………………………………………………………… 129
IE.09.6. Evenimente Swing … ………………… …………………………………………………………… 130
IE.09.7. Structura programelor dirijate de evenimente …………………………………………….. 132
IE.09.8. Aple ți Java ……………… ……………………………………………………………………….. 135
IE.09.9. Clase Swing cu model ……… …………………………………………………………………..136

Capitolul IE.10. Mediul de programare NetBeans …………………………………………………. 140

IE.10.1. Dezvoltarea de aplica ții Java … ………………………………………………………………. 140
IE.10.2. Mediul integrat NetBeans ………………………………………………………………………. 141
IE.10.3. Editorul Java din NetBeans …………………………………………. …………………………. 145
IE.10.4. Refactorizare în NetBeans ………………………………………………………………………. 149
IE.10.5. Testarea programelor Jav a în NetBeans …………………………………………………….. 150
IE.10.6. Programare vizual ă în NetBeans …… …………………………………………………………. 153

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 4 – Capitolul IE.11. Platforma Java ………………………………………………………………….. 159

IE.11.1. Java ca platformă orientată obiect …………………………………………………………… 159
IE.11.2. Limb ajul Groovy …….. …………………………………………………………………………… 1 59
IE.11.3. Simplificarea codului surs ă în Groovy ………………………………………………………. 160
IE.11.4. Operatori și instruc țiuni Groovy ………………………………………………………………. 1 61
IE.11.5. Clase Groovy ………. ………… ……………….. ………………………………………………….. 1 62
IE.11.6. Diferen țe Groovy – Java ……………. …………………………………………………………… 1 63
IE.11.7. Metode noi în clase Java ………………………………………………………………………… 1 64
IE.11.8. Utilizarea de expresii regulate . ……………………………………………….. ………………. 1 65
IE.11.9. Functori Groovy ……………. ………………………………………………………………….. 1 66
IE.11.10. Colec ții Groovy ………….. …….. …………………………………………………………… 1 71
IE.11.11. Metaprogramare în Groovy ……. ……………………………………………………………. 17 5

Capitolul IE.12. Autoevaluare …………………………………………………………………….. 184

IE.12.1. Noțiuni de bază ale program ării orientate obiect ………………………………………. 1 84
IE.12.2. Clase și obiecte în Java …………………………………………………………………………… 1 85
IE.12.3. Derivare, mo ștenire, polimorfism în Java ………………………………………………… . 187
IE.12.4. Clase abstracte și interfe țe ……………………………………………………………………….. 1 88
IE.12.5. Tehnici de programare orientat ă obiect în Java …………………………………………… 1 89
IE.12.6. Colec ții de obiecte în Java …………. …………………………………… ……………………… 190
IE.12.7. Diagrame de clase în UML ………………………………………………. ……………………… 192
IE.12.8. Șabloane de proiectare cu clase ………………………………………… ……………………. .. 193
IE.12.9. Interfe țe grafice și programare orientat ă pe evenimente ……….. ……………………… 194
IE.12.10. Mediul de programare NetBeans ……………………………. ……… ……………………… 195
IE.12.11. Platforma Java ………………….. …………………………………… ………………………. 196

Bibliografie ………………………………….. …………………………………………………………………. ……………….. 197

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 5 –

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 6 –
Cuvânt înainte

Scrierea de programe pentru calculatoare poate fi abordat ă din puncte de vedere diferite, numite și
paradigme de programare. Principalele paradigme și limbajele de programare reprezentative pentru acestea
sunt: programarea procedural ă ( Pascal, C), programarea func țional ă (Lisp, Scheme), programarea logic ă
(Prol og), programare orientat ă pe obiecte (C++,C#, Java, Scala), programare declarativ ă (HTML,XML) și
programare specific ă unor domenii (SQL, Javascript, PHP). In raport cu programarea procedural ă,
programarea cu clase și obiecte este considerat ă ca programare ―avansat ă‖, care include tehnici de
programare procedural ă dar adaug ă conceptul de ―clas ă‖.
Pentru aplica ții mari sau cu interfa ță grafic ă s-a impus programarea orientat ă pe obiecte și limbajul Java,
care într -un studiu din aprilie 2013 era pe locul 2 între cele mai cerute limbaje de firme la angajare. Toate
sistemele de operare au un interpretor de Java i ar toate programele necesare dezvolt ării de aplica ții în Java
sunt gratuite. În plus exist ă câteva medii integrate de programare gratuite care facili tează mult dezvoltarea,
testarea și între ținerea aplica țiilor Java : Eclipse, NetBeans, IDEA. Limbajul Java și programarea cu obiecte
se studiaz ă la toate universit ătile și colegiile din lume, iar la Universitatea Politehnica din Bucuresti este
disciplin ă obligatorie la facult ățile de Automatic ă si Calculatoare și de Inginerie în Limbi Str ăine.
Acest suport de curs, care include și probleme propuse si/sau rezolvate , se bazeaz ă pe materia cursurilor
―Programare Orientat ă pe Obiecte‖ și ―Ingineria Pro gram ării‖, dar con ține cele mai importante subiecte
abordate și în cursuri cu acelasi profil din alte universit ăti din lume. De obicei prezentarea limbajului Java
este precedat ă de o discu ție despre specificul program ării cu obiecte și despre tehnicile de programare proprii
acestei abord ări: derivare, polimorfism, delegare, clase abstracte și interfe țe, interfe țe grafice și programare
cu evenimente, s.a. Scrierea unei aplica ții reale este precedat ă de o faz ă de analiz ă a cerintelor, de
identificare a no țiunilor importante din aplicatie; substantivele pot deveni clase (obiecte), iar verbele pot
deveni opera ții (metode ale obiect elor).
Proiectarea ca faz ă anterioar ă scrierii de cod înseamn ă găsirea celor mai bune solu ții de împ ărțire în clase
astfel înc ât modific ările ulterioare s ă fie u șurate și să nu produc ă efecte secundare nedorite. Aceste bune
practici sau solu ții optime de proiectare se mai numesc și ―șabloane de proiectare‖ sau modele de proiectare
(―design patterns‖) și trebuie cunoscute pentru a fi aplicate corect și eficient. O reprezentare grafic ă a
claselor și obiectelor existente, a rela țiilor statice și dinamice dintre ele este de obicei parte a procesului de
proiectare dar și a documenta ției care înso țeste codul surs ă; limbajul UML (Unifie d Modelling Language)
este standardizat și ofer ă aceast ă reprezentare grafic ă unitar ă.
In produc ția de software se foloseste un mediu integrat pentru dezvoltare (IDE=Integrated Development
Environment) ca parte a metodologiei și tehnicilor specifice in gineriei de software (―Software Engineering‖).
Am ales produsul NetBeans care are toate facilit ățile existente într -un IDE fiind în acela și timp intuitiv și
ușor de folosit; în plus permite și dezvoltarea de aplica ții în alte limbaje importante (C, C++, HT ML, XML,
PHP, Groovy).
Limbajul Java și bibliotecile de clase s -au dezvoltat continuu, iar î n ultima vreme Java s -a impus mai mult
ca platform ă ―poliglot ă‖ pentru dezvoltarea de programe, în sensul c ă tot mai multe limbaje conduc la codul
intermediar executat pe ma șina virtual ă Java (JVM) și folosesc bibliotecile de clase Java : Scala, Clojure,
Groovy, s.a. Am ales pentru prezentare aici limbajul Groovy, care este mai apropiat ca sintax ă de Java.

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 7 –
Capitolul IE.01. No țiuni de bază ale programării orien tate obiect
Cuvinte cheie
POO(Programare orientată pe obiect e),Programare procedurală,
Obiecte , Clase, Metode, Instan țiere, Mesaje, Metode statice
Incapsulare,Programare generic ă,Clase abstracte, Interfe țe

IE.01.1 Paradigm e de programare
Programarea calcu latoarelor este despre crearea unor programe pe baza cărora un calculator (sau mai multe)
să poată rezolva o problemă dintr -o clasă de probleme. Particularizarea unei probleme se face prin date de
intrare sau parametri. Un program descrie prelucrările (op erațiile) ce trebuie efectuate asupra datelor de
intrare pentru a ob ține anumite rezultate sau o anumită comportare sau descrie un algoritm de prelucrare a
datelor. Mai exact, programatorul comandă pa șii necesari numai în stilul de programare imperativ deo arece
în programarea declarativă se specifică doar ce rezultate (obiective) sunt urmărite dar nu și cum (prin ce
pași) vor fi ele ob ținute.
Au existat și există mai multe moduri de abordare a programării, numite și paradigme sau stiluri de
programare (―sti l‖ de programare se referă mai des la modul de redactare a textelor sursă: indentări, utilizare
de litere mari și mici, etc.). O clasificare posibilă distinge patru paradigme: programare imperativă
(procedurală), programare func țională, programare logică și programare orientată obiect (Object Oriented =
OO). Paradigmele de programare sunt aplicate prin limbaje de programare; există limbaje ―pure‖ care
reflectă o singură paradigmă și limbaje care permit două sau chiar trei paradigme. Mai nou se consideră că
programarea cu obiecte este o dezvoltare a programării procedurale și că principala distinc ție are loc între
programarea orientată pe obiecte (POO) și programarea func țională (PF).
Limbaje tipice pentru programarea procedurală sunt Pascal și C, limbaje ti pice pentru programarea
funcțională sunt Lisp și derivate din Lisp, un limbaj tipic pentru programare logică este Prolog, iar pentru
POO limbaje reprezentative sunt Java, Python, Ruby, C# ș.a. Limbaje care permit câteva paradigme de
programare sunt C++ (p rocedural și cu obiecte) și Scala (cu obiecte și func țional). Altfel spus, un limbaj
poate impune sau nu o anumită abordare; în C++ și chiar în Java (prin metode statice) se poate programa
procedural și/sau cu obiecte.
In plus, există o serie de limbaje de stinate programării concurente (paralele), subiect care are și el câteva
paradigme (fire de execu ție cu memorie comună, actori care comunică prin mesaje, ș.a.).
Toate limbajele de programare apărute după anii 1980 sunt limbaje orientate obiect: C++, Java, Python,
Ruby, JavaScript , iar limbaje mai vechi au fost extinse pentru POO: C (C++),Pascal, Basic, PHP, s.a.
Desi no țiunile de ―obiect‖ și de ―clasă‖ sunt strâns legate, nu toate limbajele orientate pe obiecte folosesc și
noțiunea de ―clasă‖; în astfel de limbaje un obiect ―prototip‖ poate fi modificat dinamic, pentru a ob ține
tipuri diferite de obiecte (din alte clase). Exemple sunt cele derivate din ECMAScript ( JavaScript, ș.a.).
La nivel de instruc țiuni ma șină (de limbaj de asamblare) programarea este i mperativă, astfel că în mod firesc
primele limbaje independente de ma șină (Fortran, Basic, Algol, s.a.) au fost și ele imperative dar s -au
îndepărtat de ma șină prin utilizarea de variabile și de instruc țiuni mai puternice.
Pe măsură ce aplica țiile (program ele) au devenit mai mari si mai complexe programarea imperativă a
progresat prin programarea structurată (utilizarea unor structuri de control ca decizia și ciclul, în locul

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 8 – instruc țiunilor de salt) și prin programarea modulară (definirea și utilizarea de funcții). Aceste câ știguri au
fost preluate și de programarea cu obiecte (POO).
Programele și limbajele sunt clasificate în limbaje statice si limbaje dinamice; un limbaj static nu permite
unui program să se modifice în cursul execu ției, spre deosebire de un limbaj dinamic. Desi primul limbaj
dinamic a fost Lisp, această caracteristică nu apartine numai paradigmei func ționale; tot mai multe limbaje
OO includ aspecte de metaprogramare (MOP= Meta -Object Protocol ) care permit astfel de modificări ale
claselor și obiectelor in cursul execu ției.
Un limbaj cu tipuri statice men ține tipurile declarate pentru variabile (parametri, func ții) la compilare și
permite efecturea unor verificări de utilizare corectă a lor încă de la compilare; un limbaj cu tipuri dinamice
nu declară tipul variabilelor, tip care rezultă la execu ție din tipul valorilor atribuite și care se poate modifica
ulterior în func ție de atribuirile efectuate. Tipurile dinamice sunt folosite mai ales în limbaje interpretate (de
scripting); ele simplifi că codul sursă dar opera țiile nu pot fi verificate (nu există compilare) și pot produce
erori sau rezultate nea șteptate la execu ție (prin conversii automate de tip).
Limbajele orientate obiect pot folosi tipuri statice (Java, Scala) sau tipuri dinamice (P ython, Ruby), pot fi
interpretate (JavaScript, Python) sau compilate (Java, Scala) sau admit ambele moduri de folosire (Groovy
poate fi folosit ca limbaj de scripting sau ca limbaj compilat).
Incepând cu Java, multe din limbajele cu orientare pe obiecte su nt par țial compilate și par țial interpretate;
compilatorul generează un cod intermediar care este apoi interpretat de o ma șină virtuală. Codul intermediar
și masina virtuală constituie o platformă pe care se pot folosi mai multe limbaje. In prezent se folosesc d ouă
platforme: platforma Java (bytecode și mașina virtuală Java JVM) și platforma .NET (cod intermediar
Microsoft MSIL și mașina virtuală CLR= Common Language Runtime ). Avantajul este acela că execu ția
codului intermediar are loc sub controlul ma șinii virt uale, care poate face o serie de verificări la execu ție și
poate genera mesaje (excep ții program), motiv pentru care se nume ște managed code (cod gestionat sau
asistat).
IE.01.2 Programare orientat ă pe obiecte
Programarea cu clase și obiecte reprezint ă un alt mod de abordare a program ării decât programarea
procedural ă, cu avantaje în dezvoltarea programelor mari. POO (Programare orientată pe obiecte,
Programare orientată obiect ) este o paradigmă de programare în care codul sursă este structurat pe clase și
obiecte și nu pe funcții ca în programarea procedurală ( în limbajul C, de exemplu).
Un program OO este o colec ție de obiecte care interac ționează și nu o succesiune de func ții care se apelează
unele pe altele. Ideea POO este că problemele reale con țin obi ecte care interac ționează iar programele care
rezolvă astfel de probleme vor con ține obiecte software care modelează obiectele reale.
Un obiect, în sensul POO, este un model pentru o entitate sau un concept și poate desemna aproape orice:
obiecte fizice, p ersoane, documente, servicii, agregate (colec ții), algoritmi, etc. Un obiect are o identitate,
atribute și responsabilită ți. Un obiect are un ciclu de via ță (lifetime ): creare, utilizare, distrugere.
Obiectele cu aceleași caracteristici formează clase de o biecte . De exemplu toate butoanele folosite în
interfe țe grafice formeazã o clasã ( JButton în Java), de și ele pot avea forme, dimensiuni, margini și inscrip ții
diferite. In general, obiecte diferite con țin date diferite, dar toate obiectele suportă acelea și opera ții, realizate
prin metodele clasei din care fac parte.
O clasă reprezintă o categorie, adică un grup de obiecte care au în comun aceleași proprietăti
(numite și atribute), aceeași comportare (operații , mod de utilizare) și aceleași relații cu o biecte

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 9 – din alte clase. O clasă este un șablon pentru toate obiectele care au aceeași comportare (aceleași opera ții).
Un obiect este un element particular al unei clase, numit și "instanță" a clasei. Crearea unui obiect
poartă și numele de instanțiere a clasei, acțiune prin care se stabilesc și o parte din atributele
obiectului creat , numite și variabile ale instantei ( instance variables ). Alte atribute pot fi stabilite
ulterior ca parametri ai unor metode apelate pentru acel obiect.
Metodele sunt acele ași pentru toate obiectele unei clase și reprezintă operațiile posibile cu aceste
obiecte. De exemplu, conturile din bancă ale clienților băncii constituie o clasă; contul unui client
este un obiect și are în general proprietăți diferite de alte obiecte din aceeași clasă: datele personale,
tipul de cont (debit / credit), soldul contului, istoricul operațiilor. Dar toate conturile bancare
suportă aceleași operații: crearea unui nou cont cu datele specifice clientului, depunerea unei sume
în cont, extragere a unei sume din cont, calcul dobânzi și comisioane, etc.
Programare cu obiecte înseamn ă definirea de clase plus crearea de obiecte plus apeluri de metode între
obiecte pentru realizarea obiectivelor aplicației. Un program procedural (scris în C de exem plu) este o
colec ție de func ții, iar datele prelucrate se transmit între func ții prin argumente (sau prin variabile externe).
In programarea procedural ă obiectivul este definirea de func ții pentru opera ții de prelucrare a datelor și
obținere a rezultatelo r, dar în programarea orientat ă obiect scopul este crearea de obiecte axate pe date, cu
opera ții ata șate acestor date. Accentul se mut ă
de pe ac țiuni (func ții) pe date (obiecte).

Acțiunile dintr -un program cu obiecte sunt realizate prin apeluri de m etode pentru anumite obiecte, apeluri
provenite din alte obiecte. Se mai spune că obiectele interac ționează prin transmiterea de mesaje. De
exemplu, atunci când se apelează metoda read() de citire a unui caracter dintr -un fișier (flux), se poate spune
că se transmite obiectului din clasa FileReader mesajul ―dă -mi următorul caracter din fi șierul asociat acestui
obiect‖ (numele fi șierului este transmis la crearea obiectului de tip FileReader ).

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 10 –
La nivel sintactic, obiect ele sunt asocieri de date și operatii aplicabile acestor date si pot fi privite ca o
generalizare a variabilelor structură din limbajele procedurale (de tip struct din limbajul C). A șa cum
defini ția unei structuri în C creează un nou tip de date, la fel defini ția unei clase va crea un n ou tip clasă.
In C o stivă vector se defineste printr -o structură care grupează un vector cu un indice către prima adresă
liberă (vârful stivei); în Java un obiect stivă mai con ține în plus și opera ții asociate unei stive: push() (pune
un obiect pe stivă) , pop() (scoate ultimul obiect pus în stivă), empty() (verifică dacă stiva este goală), init()
(inițializare stivă goală), ș.a.
Exemplul urm ător este o func ție C de copiere a unui fi șier, octet cu octet:

Acțiunile necesare copierii se realizeaz ă prin func țiile fopen(), fgetc(), fputc(), fclose() care primesc c a date
variabilele ―in‖, ―out‖ și ―ch‖. Exemplul urm ător este o func ție Java de copiere octe ți dintr -un fi șier surs ă
―src‖ într-un fișier destina ție ―dst‖:

In acest exemplu se foloses c două obiecte: un obiect de tip FileReader (prin variabila in) și un obiect de tip
FileWriter (prin variabila out); prin metoda read() se cere obiectului in să citească și să furnizeze un
caracter, iar prin metoda write() se cere obiectului out să scrie î n fisier caracterul primit ca argument. Pentru
obiectele in si out se pot apela și alte metode, din clasele respective. Obiectele in și out conțin informa ții
despre fisierele ―src‖ și ―dst‖ necesare prelucrării lor, cam acelea și care se memorează într -o structură de tip
FILE în limbajul C. La crearea obiectelor, folosind operatorul new, se deschid cele două fi șiere (ac țiunea
funcției fopen() din C).
O clasă corespunde unei no țiuni abstracte cum ar fi ―orice fi șier disc‖, iar un obiect este un caz concret (o
realizare a conceptului sau o instan țiere a clasei). Un obiect de tip FileReader corespunde unui anumit fi șier,
cu nume dat la construirea obiectului.
Relativ la exemplul Java, trebuie spus că utilizarea unei metode statice de copiere nu este în spiritul POO.
Metodele statice Java corespund func țiilor C și pot fi folosite fără ca să existe obiecte (ele fac parte totu și din // copiere fisier in C
void filecopy ( char * src, char * dst) {
char ch;
FILE * in =fopen(src,"r "); // deschide fisier sursa
FILE * out =fopen(dst,"w"); // deschide fisier destinatie
while ( (ch=fgetc (in)) != -1) // citeste un caracter
fputc (ch, out); // scrie un caracter
fclose(out); fclose(in);
}

public static void filecopy (String src, String dst) throws IOException {
FileReader in = new FileReader (src); // un obiect
FileWriter out = new FileWriter (dst); // alt obiect
int ch;
while ( (ch= in.read()) ! = -1) // cere obiectului “in” operatia “read”
out.write(ch); // cere obiectului “out” operatia “write”
in.close(); out.close(); // cere obiectelor operatia “close”
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 11 – anumite clase). Mai aproape de stilul propriu POO, ar trebui definită o clasă copiator de fisiere, având si o
metodă (nestatică) de cop iere copy() , aplicabilă obiectelor FileCopier . Exemplu:

In exemplele anterioare și în cele ce vor urma se poate observa mutarea accentului de pe ac țiuni (func ții) pe
obiecte (date) în programarea orientată pe obiecte. Numele de cl ase sunt substantive, uneori derivate din
verbul ce defineste principala ac țiune asociată obiectelor respective. In Java există obiecte comparator (de
tip Comparator ) folosite în compararea altor obiecte, clasa Enumerator folosită la enumerarea elementelo r
unei colec ții, clasa StringTokenizer folosită la extragerea cuvintelor dintr -un șir ș.a.
IE.01.3 Avantajele programării cu obiecte

– Programare modulară la nivel de clasă și nu la nivel de funcție simplifică p rogramarea aplicațiilor mari
– Existen ța unui num ăr mare de clase predefinite reduce mult timpul de dezvoltare a unor noi aplica ții
– Cuplajul între clase este mai slab și favorizează reutilizarea de clase ca module de program
– Funcțiile (metodele clasei) au mai puține argumente
– Se simplifică d efinirea unor noi tipuri de date
– Permite programarea generică a unor colecții cu date de orice tip
– Clasele pot încapsula algoritmi dificili de programat
– Programele modelează mai bine universul aplicației: obiectele din program corespund unor obiecte reale
– Programarea cu clase se face la un nivel de abstractizare mai ridicat.
Pentru concretizare vom prezenta solu țiile C si Java pentru câteva probleme. Primul exemplu se referă la
utilizarea structurii de tip stivă ( stack ) în aplica ții. O stivă poate fi realizată fie printr -un vector, fie printr -o
listă înlăn țuită, dar opera țiile cu stiva sunt acelea și, indiferent de implementare: pune date pe stivă, scoate public class Fil eCopier {
// datele clasei
private FileReader in; // sursa datelor
private FileWriter out; // destinatia datelor
// constructor
public FileCopier (String src, String dst) throws IOException {
in = new FileReader (src) ;
out = new FileWriter (dst);
}
// o metoda de copiere
public void copy () throws IOException {
int c;
while ( (c= in.read()) != -1)
out.write(c);
in.close(); out.close();
}
// verificarea clasei FileCopier
public static void main (String arg[]) throws IOException {
FileCopier fc = new FileCopier (arg*0+, arg*1+); // creare obiect “fc”
fc.copy(); // cere obiectului “fc” operatia “copy”
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 12 – datele din vârful stivei și test de stivă goală. In limbajul C se pot defini func țiile pentru op erații cu stiva astfel
ca utilizarea lor să nu depindă de implementare, prin folosirea unui pointer la o structură:
void initSt ( Stiva * sp); // initializare stiva
int emptySt (Stiva * s); // test stiva goala
int push (Stiva * sp, T x); // pune in stiva un element de tip T
T pop (Stiva * sp ); // scoate din stiva un element de tip T

Defini ția tipului ―Stiva‖ și defini țiile func țiilor depind de implementare. Exemplu de utilizare stivă în C:

Modificarea tipului de stivă (listă în loc de vector) sau modificarea tipului datelor memorate în stiv ă necesită
un alt fi șier ―stiva.h‖ și o altă bibliotecă de func ții push(), pop() ).
In POO se va crea un obiect de tip stiv ă și se vor apela pentru acest obiect metoda push() având ca argument
obiectul pus pe stiv ă și metoda pop() cu rezultat obiectul scos din stiv ă. Obiectele memorate în stiv ă pot avea
orice tip.
In Java exist ă o clasa predefinita Stack iar program ul de exersare a opera țiilor cu stiva arată astfel:

Modific area implementării stivei, prin definirea unei alte clase ―Stack‖ nu necesită modificări în func ția
anterioară, ci doar punerea noii clase în căile de căutare ale compilatorului și interpretorului Java. In plus, se
poate defini o clas ă abstract ă care s ă corespund ă tipului de date abstract ―Stiv ă‖ și care s ă precizeze
opera țiile cu stiva , f ără să presupun ă o anumit ă implementare (o structur ă de date concret ă).
Un al doilea exemplu este cel al extragerii de cuvinte succesive dintr -un șir de caractere ce poa te con ține
mai multe cuvinte, separate prin anumite caractere de limitator . Problema este aceea că după fiecare cuvânt
extras se modifică adresa curentă în șirul analizat, deci starea sau contextul în care se execută func ția care dă
următorul cuvânt.
In lim bajul C se pot întâlni mai multe solu ții ale acestei probleme în diferite func ții de bibliotecă. Una din ele
este func ția strtok() care modifica șirul analizat și foloseste o variabilă statică internă a func ției pentru public static void main (String arg[ ]) {
Stack s = new Stack(); // creare obiect stiva
for (int x=1; x<10; x++) // pu ne 10 numere in stiva
s.push ( new Integer(x)); // s.push(x) in Java 5
while ( ! s.empty()) // cat timp stiva nu e goala
System.out.println ( s.pop()); // afiseaza obiectul scos din stiva
}
#include "stiva.h"
void m ain () {
int x; Stiva s ;
initSt (&s); // initializare stiva
for (x=1; x<10; x++) // genereaza date ptr continut stiva
push (&s,x); // pune x pe stiva
while ( ! emptySt (&s) ) // cat timp stiva contine ceva
printf("%d \n", pop (&s) ) ; // scoate din stiva si afiseaza
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 13 – adresa curentă în șirul analizat. I n plus, primul apel diferă de următoarele apeluri ale func ției. Exemplu de
utilizare:

In Java există clasa de bibliotecă StringTokenizer , folosită după cum urmează:

La crearea unui obiect StringTokenizer se specifică șirul analizat, astfe l că se pot analiza în paralel mai
multe șiruri, pentru fiecare folosind un alt obiect. Metodele nextToken() și hasMoreTokens() folosesc în
comun o variabilă a clasei care con ține pozi ția curentă în șirul analizat (ini țializată cu adresa șirului, la
constru irea obiectului).
In prelucrarea fi șierelor apar situa ții când execu ția cu succes a unei func ții depinde de folosirea anterioară a
altor func ții (cu anumite argumente); de exemplu pentru a putea scrie într -un fișier, acesta trebuie mai întâi
deschis pentru creare sau pentru adăugare (extindere fi șier existent). O situa ție asemănătoare apare la
utilizarea unor func ții care compun o interfa ță grafică și care trebuie folosite într -o anumită ordine. Astfel de
condi ționări reciproce nu se pot verifica automat în C, fiind vorba de func ții independente. In Java opera țiile
sunt metode dintr -o aceea și clasă și se poate verifica printr -o variabilă a clasei succesiunea corectă de
folosire a metodelor.
IE.01.4 Clasele ca module de program reutilizabile

Definirea și utilizarea de module func ționale permite stăpânirea complexită ții programelor mari și
reutilizarea de module prin crearea de biblioteci.
În limbajul C un modul de program este o func ție, dar în POO un modul este o clasă, care reune ște în general
mai multe fu ncții în jurul unor date. Utilizarea de clase ca module componente ale programelor are o serie de
avanaje fată de utilizarea de func ții independente:
– Metodele unei clase necesită mai pu ține argumente, iar aceste argumente nu sunt modificate în func ție;
efectul unei metode este fie de a face accesibile date din clasă, fie de a modifica variabile din clasă pe baza
argumentelor primite. Variabilele unei clase sunt implicit accesibile metodelor clasei și nu mai trebuie
transmise explicit, prin argumente (ca variabile externe metodelor, dar interne clasei).
– Soluții mai simple pentru func ții al căror efect depinde de stare (de context), cum ar fi de apeluri anterioare
ale aceleeasi func ții sau ale altor func ții pregătitoare.
– Sunt posibile verificări de ut ilizare corectă a unor metode sau asupra succesiunii de apelare a unor func ții;
de exemplu nu se poate folosi o metodă de scriere pentru un fi șier deschis numai pentru citire sau dintr -o
clasă ce permite numai citirea de date. cuv=strtok(st r, sep); // primul cuvânt din “str”, sep= sir de separatori
while (cuv !=NULL) { // daca s -a gasit un cuvant
puts(cuv); // afisare cuvant
cuv=strtok(0,sep); // urmatorul cuvant din “str”
}
String sep = new String (" ,.; \n\t"); // lista separatori de cuvinte
StringTokenizer st = new StringTokenizer (sir,delim); // “sir” = sir analizat
while (st.hasMoreTokens()) { // daca mai sunt cuvinte in sirul analizat
String token = st.nextToken(); // extrage urmatorul cuvint din linie
System.out.println (token); // afisare cuvint
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 14 – – O clasă poate încapsula al goritmi de complexitate ridicată, realiza ți prin colaborarea mai multor func ții,
unele interne clasei; astfel de algoritmi fie nu sunt disponibili în C, fie sunt disponibili prin biblioteci de
funcții destul de greu de utilizat. Exemple sunt algoritmi pent ru lucrul cu expresii regulate, pentru arhivare –
dezarhivare, pentru opera ții cu anumite structuri de date (arbori binari cu auto -echilibrare), s.a.
– Se poate realiza un cuplaj mai slab între module, în sensul că modificarea anumitor module nu va afecta
restul programului. Această decuplare sau separare între module se poate realiza prin mai multe metode,
printre care folosirea de interfe țe Java, în spatele cărora pot sta clase cu implementări diferite dar cu ace lași
mod de utilizare.
Func ționalitatea unei clase poate fi reutilizat ă în alte clase fie prin derivare, fie prin deleg are (compunere). In
acest fel, opera țiile necesare într -o clas ă sunt fie mo ștenite de la o alt ă clasă, fie delegate spre execu ție
metodelor unei alte clase. De exemplu, extinderea a utomat ă a unui vector, necesar ă uneori dup ă adăugarea la
vector, este refolosit ă și într -o clas ă stivă vector, fie prin definirea clasei stiv ă ca o clas ă derivat ă din vector,
fie prin folosirea unei variabile Vector în clasa stiv ă.
In POO adaptarea unei c lase la cerin țe specifice unor aplica ții nu se face prin interven ție în codul clasei ci
prin derivare sau prin delegare, tehnici specifice POO.
O no țiune proprie program ării cu obiecte este no țiunea de component ă software. Ideea este de a ob ține rapid
un prototip al aplica ției fără a scrie cod sau cu un minim de programare, prin asamblarea de componente
prefabricate (în special pentru interfa ța grafic ă, dar nu numai). O component ă poate con ține una sau mai
multe clase și poate fi reutilizat ă și adaptat ă fără interven ție în codul surs ă al componentei (care nici nu este
disponibil).
O component ă JavaBeans poate fi utilizat ă fără a scrie cod, prin generarea automat ă a opera țiilor de
instan țiere, de modificare a propriet ăților si de conectare cu alte clase (pri n apeluri de metode sau prin
evenimente), în urma unor comenzi date de utilizator unui mediu vizual de dezvoltare a aplica țiilor. O
component ă este de obicei o clas ă care respect ă anumite condi ții.

IE.01. 5 Incapsulare
Datele conținute în obiecte nu sunt accesibile direct pentru metode din alte obiecte (sunt private), dar ele sunt
folosite de metodele apelate pentru obiectele respective. Datele sunt încapsulate și invizibile în afara clasei
iar utilizatorii clasei văd numai serviciile oferite de clasă pr in metodele ei.
Putem compara un obiect cu o cutie neagr ă care are câteva butoane și câteva afi șări (un televizor, de
exemplu); utilizatorul nu trebuie s ă știe ce este în interiorul acestei cutii negre ci doar ce comenzi se
transmit prin butoane și să interpreteze afi șările.
Interfața publică expusă de un obiect celorlalte obiecte este formată din constructori și metode publice,
utilizabile din alte clase. Constructorii sunt întotdeauna publici pentru a permite instanțierea clasei și crearea
de obiecte. Metodele publice sunt metode moștenite sau metode proprii.
Mai multe clase pot avea implementări diferite dar pot să prezinte o aceeași interfață publică; așa sunt clasele
pentru anumite colecții de date (liste, mulțimi, dicționare, etc.).
Variabilele dintr -o clas ă sunt declarate de obicei cu atributul private , ceea ce le face inaccesibile pentru
metode din alte clase. Se mai spune c ă datele sunt ascunse sau sunt încapsulate în fiecare obiect. Metodele
clasei sunt de obicei publice pentru a putea fi apelate din alte clase.

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 15 – Deoarece datele dintr -un obiect (variabile private) nu sunt direct accesibile din afara clasei și pot fi
modificate numai prin intermediul metodelor clasei, utilizarea tipurilor de date definite prin clase este mai
sigur ă decât a celor def inite prin structuri. De exemplu, orice modificare a vectorului de caractere dintr -un
obiect StringBuffer (StringBuilder) este înso țită de modificarea lungimii șirului (în metodele care pot
modifica lungimea șirului), dar lungimea nu poate fi modificat ă direct de c ătre func ții din alte clase ( și nici
conținutul vectorului de caractere).
In reprezentarea UML a unei clase atributul private este notat cu minus (‗ -‗), iar atributul public este notat cu
plus (‗+‘). Exemplu de clasă pentru o persoana, cu variabil ele ―name‘, ―address‖ și 5 metode publice:

IE.01. 6 Clasele permit programarea generică
Programarea generic ă ne permite s ă avem o singur ă clasă pentru un vector (sau pentru o list ă), indiferent de
tipul datelor care vor fi memorate în vector (în list ă). Tot programarea generic ă ne permite s ă folosim o
singur ă funcție (metod ă) pentru a parcurge elementele oric ărei colec ții (indiferent de structura ei fizic ă ) sau
pentru a ordona orice list ă abstract ă (o colec ție care suport ă acces direct prin indi ce la or ice element ).
Genericitatea colec țiilor de obiecte poate fi realizată în limbajele cu clase prin două metode:
– prin colec ții ce con țin un supertip al tuturor tipurilor clasă (tipul Object în Java);
– prin clase ce au ca parametri tipurile de date folos ite (numite templates în C++).
Colec țiile cu elemente de tip Object au existat de la început în Java, iar colec țiile cu tipuri de date ca
parametri au apărut din versiunea 5 si sunt numite generice ( generics ).
Pentru a permite colec ții cu date de orice tip limbajul Java consider ă că toate clasele predefinite sau care
urmeaz ă a fi definite de utilizatori sunt implicit derivate dintr -o clas ă Object , care este r ădăcina ierarhiei de
clase Java. Tipul unei clase derivate este subtip al tipului clasei din care de rivă, asa cum tipul int poate
fi considerat ca un subtip al tipului long, iar tipul float ca un subtip al tipului double . La fel cum un argument
formal de tip double poate fi înlocuit cu un argument efectiv de tip int, tot a șa un argument formal de un tip
clasă B poate fi înlocuit cu un argument efectiv de un tip clas ă D; clasa D fiind derivat ă din clasa B. In felul
acesta se pot scrie functii generice, cu argumente de un tip general și utilizabile cu o multitudine de tipuri de
argumente (asa cum functia sqrt() se poate apela cu argument de orice tip numeric din limbajul C).
O colec ție de variabile de tip Object poate con ține referinte la obiecte de orice tip, pentru c ă acestor variabile
li se pot atribui variabile de orice alt tip clas ă (care contin adrese le unor obiecte). Este la fel cum în limbajul
C un vector de pointeri de tip void* poate fi folosit pentru a memora adrese ale unor date (alocate dinamic)
de orice tip predefinit sau definit de utilizatori; ad ăugarea la vector a unui pointer oarecare nu necesit ă o
conversie, dar la extragerea din vector trebuie trecut de la tipul void* la tipul de pointer folosit la ad ăugare
(prin operatorul cast pentru conversie de tip).
Exemplu de calcul a sumei valorilor unor obiecte numerice dintr -un vector:

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 16 –

Conversia în sus de la subtipul Integer la supertipul Object se face automat (argumentul metodei add() este
de tip Object ), dar conversia în jos (de la Object la Integer , pentru rezultatul metodei get() ) trebuie cerut ă în
mod explicit prin operatorul de c onversie ( ca și în C).
O colec ție Java generic ă specific ă tipul obiectelor con ținute înc ă de la declarare (care poate fi orice subtip de
Object sau de alt tip), iar la extragere nu mai trebuie f ăcută nici o conversie de tip. Acela și exemplu dinainte
cu co lecții generice:

Programarea cu obiecte permite un nivel de abstractizare și de generalizare mai ridicat prin clase abstracte
și interfețe . Cel mai bun exemplu este cel oferit de tipurile de date colective (numite colecții sau structuri de
date): oric e colecție de obiecte trebuie să aibă operații pentru adăugarea și eliminarea de elemente, pentru a
obține dimensiunea colecției, pentru enumerarea elementelor din colecție ș.a.
Ca implementare, colecțiile pot fi realizate ca vectori ( Arrays ), ca liste înl ănțuite, ca arbori binari, ca tabele
hash sau altfel. O colecție de tip mulțime ( Set) este o colecție cu elemente distincte și cu timp de căutare
redus, dar o mulțime poate fi un arbore, un tabel hash, etc.
O interfață este o colecție de metode nedefinite (abstracte), iar o clasă abstractă are metode definite, metode
abstracte și (posibil) date. In Java există o interfață Collection , o subinterfață Set, o clasă abstractă
AbstractSet și clasele instanțiabile TreeSet și HashSet .
IE.01. 7 Clasele creează un m odel pentru universul aplica ției
Un program destinat unei aplica ții trebuie s ă transforme no țiunile și acțiunile specifice aplica ției în
construc ții specifice limbajului de programare folosit (func ții, variabile, argumente de func ții, etc.).
Evolu ția limba jelor de programare poate fi privit ă și ca un progres al abstractiz ării, în sensul îndep ărtării
progresive de ma șina fizic ă prin introducerea de no țiuni tot mai abstracte dar mai apropiate de aplica ție.
Programarea orientat ă pe obiecte permite definirea de clase și obiecte ce corespund direct obiectelor din
universul aplica ției și modelarea rela țiilor statice și dinamice dintre aceste obiecte. Identificarea obiectelor și
acțiunilor specifice unei aplica ții se face în faza de analiz ă orientat ă obiect a probl emei de rezolvat și implic ă
o abordare diferit ă de cea anterioar ă.
Intr-o aplica ție bancar ă vor exista clase și obiecte de genul ― Cont‖ si ―C lient‖ . Un obiect ―C lient‖ va con ține
date de identificare ale clientului și metode pentru ob ținerea sau modificare a acestor date. Un obiect de tip Vector v = new Vector(); // creare obiect vector (extensibil)
for (int k=1; k<11; k++) // genereaza numerele 1,2.3… 10
v.add ( new Integer(k)); // adauga numarul k la vector ca obiect de tip Integer
int sum=0;
for (int k=0;k<v.size();k++) {
Integer obj = (Integer) v.get(k); // extrage din vector si conversie din Object în Integer
sum += obj.intValue(); // aduna numarul de tip “int” din obiectul “obj”
}

Vector <Integer> v = new Vector<Integer>(); // creare obiect vector de Integer
for (int k=1; k<11; k++) // genereaza numerele 1,2.3… 10
v.add ( new Integer(k)); // adauga numarul k la vector ca obiect de tip Integer
int sum=0;
for (int k=0;k<v.size();k++)
sum += v.get(k).i ntValue();

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 17 – ―Cont‖ va con ține suma din cont (si alte date asupra opera țiilor cu acel cont), precum și metode pentru
depunerea de bani în cont, pentru retragerea de bani din cont și pentru vizualizarea sumei de bani din cont.
Obiectele de tipul ― Cont‖ sau ―C lient‖ se numesc și obiecte din domeniul aplica ției (domain objects ).
Aplica țiile mai pot con ține obiecte ajut ătoare ( helper ) sau obiecte din clase predefinite pentru opera ții cu
anumite tipuri de date, cu colec ții de obiecte, cu baze de date, cu conexiuni între calculatoare s.a.

IE.01. 8 Tehnici de programare specifice POO
Programarea cu obiecte a adus cu sine noi tehnici de programare, cum ar fi: derivarea (extinderea) claselor,
delegarea, supraîncărcarea functiilor, suprascrierea functiilor polimorfice dintr -o clasă într -o subclasă,
crearea unor ierarhii de tipuri, componente reutilizabile, sabloane de proiectare cu clase ( Design Patterns ),
standardul UML pentru reprezentarea grafică a claselor și rela țiilor dintre ele s.a.
Reutil izarea în programare este un vechi obiectiv și are două aspecte:
– Reutilizarea ca atare a unor module existente și adaptarea lor prin parametri modificabili
– Reutilizarea par țială a unor module existente în definirea altor module (func ții).
Reutilizarea unor module existente se realizează prin biblioteci de func ții și respectiv prin biblioteci de clase
în POO. Limbajul Java este renumit și prin numărul mare de clase predefinite care simplifică mult
dezvoltarea de noi aplica ții, în mod special a celor cu i nterfa ță grafică. Mai mult decât atât, se pot defini și
utiliza componente standard JavaBeans , care respectă anumite conven ții ce permit instan țierea lor fără
scrierea manuală de cod (prin interac țiune cu un mediu vizual de programare).
Reutilizarea par țială a unor functii în definirea altor func ții înseamnă, în programarea procedurală, preluarea
unor secven țe de cod și completarea lor cu alte opera ții noi. In POO reutilizarea par țială a unor clase nu se
face prin editarea codului sursă (opera ție supusă ero rilor) ci prin derivare sau prin delegare.
Un exemplu este definirea unei clase pentru mul țimi de obiecte realizate ca vectori sau ca liste înlăn țuite pe
baza unor clase existente (în bibliotecile de clase Java) pentru vectori și liste. Diferen ța dintre un vector și o
mulțime vector este interzicerea obiectelor cu valori identice în mul țimi, care se poate face în opera țiile de
adăugare a unor noi elemente la mul țimea vector. Alte opera ții cu multimi pot fi preluate ca atare de la
vectori , fără a mai fi def inite explicit în clasa multime: eliminare element, determinare dimensiune mul țime,
opera ții cu două mul țimi ș.a. Reutilizarea opera țiilor cu vectori într -o nouă clasă ―mul țime vector‖ se poate
face fie prin derivare, fie prin delegare.
Derivarea înseamnă definirea clasei ―mul țime vector‖ ca o subclasă a clasei ―vector‖ (ca un caz particular de
vector) cu redefinirea operatiilor care pot crea elemente cu valori egale. Rela ția dintre clase creată prin
derivare este o relatie de tip ―este un fel de ― ( is a ki nd of ), deci o mul țime vector este un fel de vector.
Metodele din calasa ―vector‖ care rămân la fel si în clasa ―mul țime vector‖ nu mai trebuie definite si nici
măcar declarate în noua clasă.

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 18 – Delegarea înseamnă că în clasa ―mul țime vector‖ există o variabi lă de tip ―vector‖ și că operatiile cu
mulțimea vector sunt ―delegate‖ prin această variabilă către opera țiile clasei ―vector‖ (prin apeluri de metode
din clasa ―vector‖).
Derivarea poate fi folosită și pentru crearea unor ierahii de tipuri: o clasă reprez intă un tip de date, o clasă
derivată reprezintă un subtip al tipului clasei din care a fost derivată. Cele două tipuri sunt compatibile la
atribuirea între variabile si la transmiterea de argumente, la fel cum în limbajul C orice tip aritmetic este
compat ibil cu alte tipuri aritmetice ( short, int, float, double , etc) sau cum sunt compatibile între ele tipul
void* cu orice alt tip pointer.
In programarea cu obiecte se folosesc multe ierarhii de tipuri și ele au diferite beneficii. De exemplu, în Java
toate clasele existente sau definite de utilizatori sunt subclase ale clasei Object si deci toate tipurile clasă
sunt compatibile cu tipul de bază Object (rădăcina ierarhiei de tipuri Java). O colec ție de variabile de tip
Object poate fi folosită drept colectie cu date de orice alt tip clasă, fiind deci o colec ție generică. O metodă
cu un argument de tip Object sau cu rezultat Object poate fi apelată cu argument de orice tip clasă si este o
funcție generică.
Specific POO este că ierarhiile de tipuri pot con ține tipuri abstracte, foarte generale în partea de sus a
ierarhiei. Familia claselor colec ție sau a claselor dic ționar începe cu o interfa ță care are sub ea clase
abstracte , care au ca subtipuri clase instan țiabile. O interfa ță are toate metodele abstracte și nu are date în
timp ce o clasă abstractă poate avea și date și metode definite (ne -abstracte). Interfe țele și clasele abstracte
Java sunt și ele subtipuri ale tipului Object . Interfa ța impune anumite metode pentru toate subtipurile sale,
clasa abstractă de finește o parte din metodele interfe ței (care nu depind de date și în func ție de alte metode),
iar clasa instan țiabilă define ște și metodele rămase abstracte ( și care depind de datele clasei).

O funcție polimorfică (virtuală) este o funcție cu acelasi nume, tip și argumente dar cu implementări
(definiții) diferite în clase diferite. Metoda polimorfică are același rol și primeste aceleași date dar definiția
diferă în funcție de specificul clasei; ea este virtuală pentru că definește un prototip de funcți e și nu o anume
funcție concretă. Exemplu: Metoda toString () din Java are rolul de a produce un șir (obiect String ) cu datele
dintr -un obiect, în vederea afișării sau scrierii acestora într -un fișier, sau pentru combinarea cu alte șiruri.
Deoarece clase di ferite con țin date diferite este normal ca și metoda toString() să fie diferită în clase diferite.
Metoda toString () este impusă de clasa Object tuturor celorlalte clase și este de obicei redefinită
(suprascrisă) în fiecare clasă; este posibil ca o aceeași definiție să fie folosită în comun de câteva clase (de
ex. toate clasele mulțime pot folosi toString () din clasa AbstractSet ). Selectarea uneia dintre metodele cu
același nume se face în funcție de tipul obiectului pentru care se apelează metoda, deci ace astă tehnică este
posibilă numai în programarea cu obiecte .
Suprascrierea func ților ( Ovverriding ) este necesară pentru a redefini o metodă mo ștenită într -o subclasă; în
felul acesta o metodă cu acela și nume, tip și argumente are efecte (defini ții) diferite în clasele din ierarhie și
este polimorfică. Pentru ca să fie posibilă selectarea unei defini ții dintre mai multe defini ții ale unei metode

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 19 – polimorfice în func ție de tipul obiectului pentru care se aplică s -a introdus și o sintaxă diferită de apelare a
metodelor, alta decât sintaxa de apelare a func țiilor C (sau a metodelor statice din Java). Numele metodei
este prefixat de numele variabilei referin ță care identifică obiectul pentru care se apelează metoda:
LinkedList list = new LinkedList(); String str = list .toString();
TreeSet s et = new TreeSet(); String str = s et.toString();

In exemplul anterior se va folosi me toda toString() din clasa LinkedList în primul apel și metoda toString()
din clasa TreeSet în al doilea apel, în func ție de tipul variabilelor ―list‖ si ―set‖.
Extinderea simultan ă a mai multor clase X,Y este necesar ă pentru a crea un nou tip compatibil simultan cu
tipurile X,Y. O clas ă C++ poate extinde simultan (prin derivare) mai multe clase, deci poate mo șteni
(metode s i date) din mai multe surse. Exist ă însă posibilitatea ca o aceea și variabil ă (dintr -o superclas ă A) să
fie mo ștenită de o clas ă D, pe c ăi diferite, de la subclase ale clasei A (fie B si C aceste subclase). Variabilele
din obiecte de tip B si C pot avea v alori diferite pentru variabila mo ștenită, dar obiectele clasei D ce valoare
moștenesc ?
O clas ă Java poate extinde o singura clas ă dar poate implementa simultan mai multe interfe țe, ceea ce
rezolv ă problemele create de o mo ștenire multipl ă: se preiau tipu ri dar nu și date de la interfe țe. In Java
interfe țele au o utilizare mult mai larg ă față de clasele abstracte (exist ă multe interfe țe predefinite în
bibliotecile de clase).
IE.01.09 Analiza și proiectarea orientate pe obiecte
Analiza și proiectarea orien tate pe obiecte reprezintă o parte a procesului de dezvoltare a programelor în
paradigma OO sau a ingineriei software orientate pe obiecte. Analiza se ocupă cu descompunerea aplica ției
în componente software distincte, abordabile separat, iar proiectarea s tabile ște modul cum sunt asamblate
componentele software în aplica ții func ționale și performante.
Analiza orientat ă pe obiecte identific ă în descrierea aplica ției acele substantive ce vor deveni obiecte în
program și acele verbe care vor deveni metode asociate obiectelor . Rezultatul analizei este un model
conceptual realizat pe baza specifica țiilor. Parte din această fază este analiza cazurilor de utilizare: cine și
cum foloseste aplica ția.
Modelul conceptual con ține no țiuni abstracte ce corespund particip anților la realizarea aplica ției și rela țiile
de colaborare dintre acestea. Sunt utile atât modele statice cât și modele dinamice, care arată succesiunea
evenimentelor și acțiunilor din sistemul proiectat.
In faza de proiectare se trece de la conceptele ab stracte din model la clase având în vedere diferite
constrângeri de performan ță și de bune practici în domeniu ( best practices ), adică de scheme de proiectare cu
clase verificate de practică.
In analiza și proiectarea orientate pe obiecte se pot folosi div erse instrumente auxiliare, cum ar fi diagrame
UML ( Unified Modelling Language ) și cartele CRC (Class Responsibility Collaboration card ). O cartelă
CRC precizează pentru fiecare clasă responsabilită țile și colaboratorii (alte clase). In faza de proiectare se
stabileste și care clase trebuie să aibă un caracter mai general, în vederea reutilizării lor în alte aplica ții,
precum și clasele de bibliotecă utilizabile.
Dezvoltarea unei aplica ții software este un proces iterativ, de rafinare succesivă, care începe cu un prototip
realizat cât mai repede pentru ca beneficiarii să -și poată preciza cerin țele, mai ales pentru aplica ții cu
interfa ță grafică. Din acest motiv există produse de tip framework sau medii vizuale pentru crearea rapidă de
prototipuri (RAD = Rapid Application Development ) pentru aplica ții Web si pentru aplica ții locale cu
interfa ță grafică.

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 20 – Ingineria software ( Software Engineering ) înseamn ă aplicarea sistematic ă și disciplinat ă a unor metode
(metodologii) pentru proiectarea, dezvoltarea (implement area), operarea si între ținerea unor produse
software de calitate. Metodologii propuse pentru dezvoltarea de aplica ții: Agile, Extreme, Lean, Joint,
Scrum, RAD. Aceste metodologii se referă nu numai la limbaje, la instrumente software folosite și la fazel e
procesului ci și la modul de colaborare cu beneficiarii și în cadrul echipei care dezvoltă o aplica ție.
Obiectele dintr -o aplica ție își au originea în:
– Obiecte din d omeniul aplica ției: client, cont, factur ă, tranzac ție,ș.a.
– Obiecte colec ție: vector i, liste, dic ționare, arbori etc.
– Obiecte care con țin algoritmi: opera ții pe șiruri, opera ții cu expresii regulate, opera ții de arhivare sau
dezarhivare, etc.
– Obiecte din interfa ța grafic ă a aplica ției: ferestre, butoane, c asete cu text, imagini, men iuri, etc
– Obiecte pentru opera ții de citire -scriere fi șiere, pentru opera ții în reteaua Internet, ―parsere‖ de fi șiere
XML , pentru fire de execu ție concurente, s.a.
– Obiecte auxiliare necesare în realizarea unor scheme de clase ( șabloane de proiectar e): obiecte iterator sau
enumerator al elementelor dintr -o colec ție, obiecte ascultător (observator), etc.

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 21 – Capitolul IE. 02. Clase și obiecte în Java
Cuvinte cheie
Sintaxa Java, Tipuri primitive , Supradefinire de funcții,
Tipuri clas ă,Variabile referin ță, Spații de nume, Pachete de clase,
Metode, Metode statice, Metode ale obiectelor,Vectori în Java

IE.02.1 Sintaxa limbajului Java
Limbajul Java folose ște acelea și instruc țiuni c u limbajul C, mai pu țin instruc țiunea goto. Tipurile de date
primitive sunt ace leași, plus tipul boolean , care a schimbat și sintaxa instruc țiunilor care verifică condi ții.
Diferen țele apar la tipurile de date derivate (vectori, structuri, pointeri) și la structura programelor.
In limbajul C exist ă un singur fel de comentarii, care î ncep prin perechea de caractere "/*" și se termin ă prin
perechea de caractere "*/". In C++ au ap ărut, în plus, comentarii care încep prin perechea de caractere "//"
și se termin ă la sfâr șitul liniei în care apare acel comentariu. Java preia aceste dou ă feluri de comentarii, la
care se adaug ă comentarii destinate gener ării automat e a documenta țiilor programelor ; aceste comentarii
încep prin șirul de 3 caractere "/**" și se termin ă la fel cu comentariile C, prin "*/" .
IE.02.1.1 Tipurile de date primiti ve
Java preia de la C și C++ aproape toate tipurile aritmetice ( short, int, long, float, double ) și tipul void, dar
impune o aceea și lungime și reprezentare a tipurilor numerice pentru toate implement ările limbajului. Un
întreg de tip int ocup ă 32 de bi ți, un short ocup ă 16 bi ți, iar un long ocup ă 64 de bi ți. Un float ocup ă 32 de
biți iar un double ocupa 64 de bi ți. Tipul aritmetic byte ocup ă 8 biți (valori între –128 si 127). Toate tipurile
aritmetice din Java reprezint ă numere cu semn și nu mai exist ă cuvântul unsigned pentru declararea de
variabile aritmetice f ără semn.
Tipul char ocup ă 16 bi ți pentru c ă standardul de reprezentare a caracterelor este UTF -16 sau Unicode (în
locul codului ASCII) și permite utilizarea oric ărui alfabet.
Tipul boolean din J ava ocup ă un singur bit; constantele de tip boolean sunt true și false . Existen ța acestui
tip modific ă sintaxa instructiunilor if, while, do și a expresiei condi ționale, precum și rezultatul expresiilor de
relație (care este acum de tip boolean și nu de t ip int). Asadar, instruc țiunile urm ătoare sunt gre șite sintactic
în Java, de și sunt corecte în C și C++.

Variabilele declarate în func ții nu primesc valori implicite iar compilatorul semnaleaz ă utilizarea de variabile
neini țializate explicit de p rogramator.
In Java, se fac automat la atribuire numai conversiile de ―promovare‖ de la un tip numeric inferior‖ la un tip
aritmetic ―superior‖, care nu implic ă o trunchiere. Exemple:
int n=3; float f; double d;
d=f=n; // corect f=3.0, d=3.0
n=f; // gresit sintactic
f=d; // gresit sintactic
while ( a%b) {a=b; b=a%b;} // corect este : while ( a%b !=0) {…};
return x ? 1:0 ; // corect este: return x !=0 ? 1:0 ; cu x de tip “int”
if ( ! n) { … } // corect este: if (n= =0) { … }
do { nf=nf *n –;} while (n) ; // corect este: do { nf=nf*n –;} while ( n>0);

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 22 – Ierarhizarea tipurilor aritmetice, de la ―inferior‖ la ―superior‖ este:
byte, short, in t, long, float, double
Tipul char nu este un tip aritmetic dar se pot face conversii prin operatorul cast, de forma (tip) între tipul
char și orice tip aritmetic întreg. Exemplu:
byte b=65; char ch; ch =(char)b; // ch este 'A'
ch='\n'; b =(byte)ch; // b este 10

Acelea și reguli de conversie între tipuri numerice se aplic ă și între ar gumentele efective și argumentele
formale, deoarece compilatorul face automat o atribuire a valorii argumentului efectiv la argumentul formal
corespunz ător. Exemplu : double r = Math.sqrt(2); // promovare de la int la double
Conversia de la un tip numeric ―superior‖ la un tip aritmetic ―inferior‖ trebuie cerut ă explicit prin folosirea
operatorului cast de for țare a tipului și nu se face automat ca în C. Exemple :

Compilatorul Java verific ă dacă este specificat un rezultat la orice ie șire posibil ă dintr -o func ție cu tip diferit
de void (de exemplu, instruc țiuni if fără else ). Exemplu:

Cea mai important ă diferen ță dintre Java, pe de o parte, și limbajele C, C++ pe de alt ă parte, este absen ța
tipurilor pointer din Java. Deci nu e xistă posibilitatea de a declara explicit variabile pointer și nici operatorii
unari ‗&‘ (pentru ob ținerea adresei unei variabile) și ‗*‘ (indirectare printr -un pointer). Operatorul new din
C++ pentru alocare dinamic ă are în Java un rezultat o referin ță și nu un pointer.
IE.02.1.2 Supradefinirea func țiilor
Supradefinirea sau supraînc ărcarea func țiilor ( Function Overloading ) a fost introdus ă în C++ pentru a
permite definirea mai multor func ții cu acela și nume și cu acelasi tip dar cu argumente diferite în tr-o aceea și
clasă. Pot exista func ții cu acela și nume (eventual și cu acelasi argumente și tip) în clase diferite, dar acesta
nu este un caz de supradefinire, fiindc ă ele se afl ă în spa ții de nume diferite.
In Java, ca și în C++, o func ție este deosebit ă de alte func ții din aceea și clas ă prin "semn ătura" sa (prin
"amprenta" func ției), care este format ă din numele, tipul și argumentele func ției. Un exemplu uzual de
funcții supradefinite este cel al func țiilor de afi șare la consol ă în mod text ―print‖ și ―pr intln‖, care au mai
multe defini ții, pentru fiecare tip de date primitiv și pentru tipurile clas ă String și Object :
f= (float)d; // cu pierdere de precizie
n=(int)f; // cu trunchiere
int r = (int) Math.sqrt(4); // conversie necesara de la double la int
// functie de rotunjire din clasa Math
public static int round (float a) {
return (int)floor(a + 0.5f); // "floor" are rezultat double
}
public static int indexOf (int a[], int b) { // pozitia lui b in vectorul a
for (int i=0;i<a.length;i++)
if (a[i]==b)
return i;
// return –1; // eroare de compilare !
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 23 –

IE.02.1.3 Declara ții de variabile
O declara ție de variabil ă poate s ă apară fie într -o func ție, fie în afara func țiilor, d ar într -o clas ă; nu exist ă
variabile externe claselor. Locul declara ției este important; o variabil ă dintr -o func ție este local ă acelei
funcții, iar o variabil ă declarat ă la nivel de clas ă este utilizabil ă de orice func ție din clas ă (și chiar de func ții
din alte clase ,dacă este publică ).
In C toate declara țiile dintr -un bloc trebuie s ă precead ă prima instruc țiune executabil ă din acel bloc. In C++
și în Java o declara ție poate apare oriunde într -un bloc, între alte instruc țiuni sau declara ții. Domeniul de
valabilitate al unei variabile începe în momentul declar ării și se termin ă la sfâr șitul blocului ce con ține
declara ția.
Instruc țiunea for constituie un caz special: variabila contor se declar ă de obicei în instruc țiunea for, iar
valabilitatea acestei declar ații este limitat ă la instruc țiunile repetate prin instruc țiunea for . Exemplu:

In Java nu exist ă cuvântul cheie const iar constantele sunt declarate ca variabile cu atributele static și final .
Exemplu:
public static final double PI = 3.141592653 58979323846; // in clasa Math
In Java nu exist ă declaratia typedef deoarece definirea unei clase introduce automat un nume pentru un nou
tip de date.
In Java nu exist ă operatorul sizeof , pentru c ă lungimea variabilelor este cunoscut ă, iar la alocar ea de
memorie (cu new) nu trebuie specificat ă dimensiunea alocat ă.

public class PrintStream … { // din pachetul java.io
public void print (int i) { // scrie un întreg
write (String.valueOf(i));
}
public void print (float f) , // scrie un număr real
write (String.valueOf (f));
}
public void print (boolean b) { // scrie un boolean
write (b ? “true” : “false”);
}
public void print (String s) { // scrie un sir de caractere
if (s== null) s= “n ull”;
write (s);
}
Type a quote from the document or the summary of an interesting point. You
can position the text box anywhere in the document. Use the Text Box Tools
tab to change the formatting of the pull quote text box.]
public static boolean este ( int x[ ], int y) { // daca y este in vectorul x
int n=x.length; // lungime vector x
for (int k=0; k<n; k++)
if ( x[k]==y)
brea k;
return k==n ? false : true; // eroare: k nedeclarat !
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 24 – IE.02.1.4 Structura programelor Java
O aplica ție Java con ține cel pu țin o clas ă, care con ține cel putin o metod ă cu numele main de tip void și cu
atributele static și public . Metoda main trebuie s ă aibă ca unic argument un vector de obiecte String . Ca și în
C, execu ția unui program începe cu func ția main , doar c ă main trebuie s ă fie inclus ă, ca metod ă static ă, într -o
clasă și trebuie s ă aibă un argument vector de șiruri. Exemplul urm ător este un program minimal, care
afișează un text constant:

In Java nu conteaz ă ordinea în care sunt scrise func țiile (metodele) unei clase, deci o func ție poate fi apelat ă
înainte de a fi definit ă și nici nu este necesar ă declararea func țiilor utili zate (nu se folosesc prototipuri de
funcții). Orice func ție apar ține unei clase și nu se pot defini func ții în afara claselor.
In exemplele urm ătoare se vor folosi numai clase care reunesc câteva metode statice, func ții care pot fi
executate f ără a crea ob iecte de tipul clasei respective.
Exemplu de fișier surs ă cu o singur ă clasă, care con ține dou ă metode, ambele publice și statice:

Numele unei metode statice trebuie precedat de numele clasei din care face parte (separate printr -un punct),
dacă este apelat ă dintr -o metod ă a unei alte clase. Exemplu:

O metod ă ne-static ă trebuie apelat ă pentru un anumit obiect, iar numele ei trebuie precedat de numele
obiectului ( și un punct). Metoda println() este apelat ă pentru obiectul adresat de variabi la out, variabil ă
public ă din clasa System . public class Main {
public static void main ( String arg[ ] ) {
System.out.println (" Main started ");
}
}

class Main {
public static void main (String arg[ ]) { // cu "main" incepe executia
writeln ("Hello world !");
}
public static void writeln (String txt) { // afiseaza un text pe ecran
System.out.println (txt);
}
}

public class Main {
public static void main (String arg[ ]) { // cu "main" incepe executia
Util.writeln ("Hello world !");
}
}
public class Util {
public static void writeln (String txt) { // afiseaza un text pe ecran
System.out.println (txt);
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 25 – Un fi șier surs ă Java poate con ține mai multe clase, dar numai una din ele poate avea atributul public . Numele
fișierului surs ă (de tip ―java‖) trebuie s ă coincid ă cu numele clasei publice pe care o con ține. O clas ă public ă
este accesibil ă și unor clase din alte pachete de clase.
Compilatorul Java creeaz ă pentru fiecare clas ă din fi șierul surs ă câte un fi șier cu extensia class și cu numele
clasei. Dac ă este necesar, se compileaz ă și alte fisiere surs ă cu clase folosi te de fi șierul transmis spre
compilare.
Faza de execu ție a unui program Java const ă din înc ărcarea și interpretarea tuturor claselor necesare
execu ției metodei main din clasa specificat ă în comanda java.
IE.02.1.5 Tipuri clas ă și variabile referin ță
O clas ă este o structur ă care poate con ține atât date cât și func ții ca membri ai structurii. In Java nu mai
exist ă cuvântul cheie struct , iar definirea unei clase folose ște cuvântul cheie class . Se pot defini clase numai
cu date publice, echivalent e structuril or din limbajele C și C++. Exemplu:

In practic ă se prefer ă ca variabilele clasei Point să fie de tip private (inaccesibile unor metode din alte clase)
și ca ini țializarea lor s ă se fac ă în constructorul clasei:

Clasele (neabstracte) Ja va sunt de dou ă categorii:
– Clase instan țiabile, care pot genera obiecte, care con țin date și metode (ne -statice).
– Clase neinstan țiabile, care con țin doar metode statice ( și eventual constante).
O metod ă static ă corespunde unei func ții din limbajul C, cu diferen ța că numele func ției trebuie precedat de
numele clasei din care face parte. Exemple:
double xabs = Math.abs(x); // valoarea absoluta a lui x
double y = Math.sqrt(x); // radical din x
int n = Integer.par seInt (str); // conversie sir "str" la tipul "int"

Definirea unei clase instan țiabile T creeaz ă automat un nou tip de date T. Un obiect de tip T este o instan țiere
a clasei T și este referit printr -o variabil ă de tip T. Clasa Java cu numele String define ște un tip de date
String , ce poate fi folosit în declararea de variabile, vectori sau funcii de tip String . Exemple: public class Point { // orice punct din plan
public double x, y; // coordonate punct
}
// creare si utilizare obiect din clasa " Point"
Point a = new Point(); // constructor implicit, generat automat
a.x=2.; a.y = -3; // un punct in cadranul 4

public class Point { // orice punct din plan
private double x,y; // coordonate punct
public Point (double xi, double yi) { x=xi; y=yi; } // functie constructor
}
Point a = new Point (2, -3);

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 26 – String msg = "Eroare"; // o variabila sir
String tcuv[] ={"unu","doi","trei"}; // un vector de siruri

Un obiect Java corespunde unei variabile structur ă din C, iar o variabil ă de un tip clas ă corespunde unei
variabile pointer la o structur ă din C. In Java toate obiectele sunt alocate dinamic, folosind operatorul new,
iar varia bila de tip clas ă trebuie ini țializat ă cu rezultatul operatorului new. Exemplu:
String mesaj; // String este o clasă predefinită
mesaj = new String (“ Eroare ! “) ; // alocare memorie pentru sir

Pentru const antele de tip String se creeaz ă obiecte automat, de c ătre compilator, ale c ăror adrese pot fi
folosite în atribuiri sau ini țializări la declarare. Exemple:
System.out.println ("Eroare !");
String msg; msg = " Corect";

Clasa String conține mai multe metode publice, utilizabile în alte clase. De exemplu, metoda length (), fără
argumente, are ca rezultat (întreg) lungimea șirului coninut în obiectul de tip String pentru care se apeleaz ă
metoda. Exemplu: int len = mesaj.length();
Acest e xemplu arat ă că membrii unei clase se folosesc la fel cu membrii unei structuri, indiferent c ă ei sunt
variabile (câmpuri) sau func ții (metode). Un alt exemplu este o construc ție mult folosit ă în Java pentru
afișarea la consol ă (în mod text) a unor șiruri de caractere:
System.out.println (mesaj);
System.out.println ( “ Eroare “);

In aceste exemple System este numele unei clase predefinite, out este numele unei variabile publice (din
clasa System ) de un tip clas ă (PrintStream ), iar println() este numele unei metode din clasa PrintStream .
Numele unei metode poate fi precedat de numele unei variabile clas ă sau de numele unei clase, dar
întotdeauna caracterul separator este un punct. Este uzual în Java s ă avem denumiri de variabile sau de
metode care con țin câteva puncte de separare a numelor folosite în precizarea contextului. Exemple:
if ( Character.isDigit ( str.charAt(0)) ) . . . // daca primul caracter e o cifra
System.out.println (obj + obj.getClass().getName());
int maxdigits= (Integer.MAX_VALUE+"").length();

O referin ță la un tip clas ă T este de fapt un pointer la tipul T dar care se folose ște ca și cum ar fi o variabil ă
de tipul T. Indirectarea prin variabila referin ță este realizat ă automat de compilator, f ără a folosi un op erator
special, ca în C .
Tipul referin ță a fost introdus în C++ în principal pentru a declara parametri modificabili în func ții, cu
simplificarea scrierii și utiliz ării acestor func ții. In Java nu trebuie folosit ă o sintax ă special ă pentru
declararea de variabile sau de parametri referin ță, deoarece toate variabilele de un tip clas ă sunt automat
considerate ca variabile referin ță. Nu se pot defini referin țe la tipuri primitive.
O variabil ă referin ță Java nu este un obiect, dar con ține adresa unui obiect alocat dinamic. O variabil ă
referint ă apare de obicei în stânga unei atribuiri cu operatorul new sau cu constanta null în partea dreapt ă.
Exemplu:
Vector a = new Vector( ); // a = variabila referinta la un obiect de tip Vector

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 27 – Atunci când se ap eleaz ă o metod ă pentru un obiect, se folose ște numele variabilei referin ță ca și cum acest
nume ar reprezenta chiar obiectul respectiv și nu adresa sa. Exemplu:
System.out.println ( a.size() ); // afisare dimensiune vector a

Operatorul d e concatenare '+', folosit între obiecte de tip String , poate crea impresia c ă variabilele de tip
String conțin chiar șirurile care se concateneaz ă și nu adresele lor. Exemple:
String s1="java.", s2="util.", s3="Rand";
System.out.pr intln (s1+s2+s3); // scrie java.util.Rand

Operatorul de concatenare ―+‖ este singurul operator ―supradefinit‖ în Java și el poate fi utilizat între
operanzi de tip String sau cu un operand de tip String și un alt operand de orice ti p primitiv sau de un tip
clasă (pentru care exist ă o func ție de conversie la tipul String ). Exemplu:
int a=3, b=2 ;
System.out.println ( a + “+” + b + “=“ + (a+b)); // scrie: 3 + 2 = 5

Efectul operatorului '+' depinde de tipul operan zilor: dac ă unul din operanzi este de tip String atunci este
interpretat ca operator de concatenare iar rezultatul este tot String .
IE.02.1.6 Spații de nume în Java
Un spa țiu de nume ( namespace ) este un domeniu de valabilitate pentru un nume simbolic ales de
programator. In cadrul unui spa țiu nu pot exista dou ă sau mai multe nume identice (excep ție fac metodele
supradefinite dintr -o aceea și clas ă). Pot exista nume identice în spa ții diferite.
Fiecare clas ă creeaz ă un spa țiu de nume pentru variabilele și metodele clasei; ca urmare numele metodelor
sau datelor publice dintr -o clas ă trebuie precedate de numele clasei, atunci când se folosesc în alte clase.
Exemple:
Main.writeln ("abc"); // clasa Main, metoda writeln
Math.sqrt(x); // clasa Math, metoda sqrt
System.out // clasa System, variabila out

Clasele înrudite ca rol sau care se apeleaz ă între ele sunt grupate în "pachete" de clase ( package ).
Instruc țiunea package se folose ște pentru a specifica num ele pachetului din care vor face parte clasele
definite în fisierul respectiv; ea trebuie s ă fie prima instruc țiune din fi șierul surs ă Java. In lipsa unei
instruc țiuni package se consider ă că este vorba de un pachet anonim implicit, situa ția unor mici prog rame de
test pentru depanarea unor clase. Un nume de pachet corespunde unui nume de director, cu fi șierele de tip
class corespunz ătoare claselor din pachet.
Numele unui pachet poate avea mai multe componente, separate prin puncte. Numele de pachete cu cla se
predefinite, parte din JDK, încep prin java sau javax . Exemple : java.io , java.util.regex , java.awt,
javax.swing.tree
Un pachet este un spa țiu pentru numele claselor din acel pachet. In general numele unei clase publice trebuie
precedat de numele p achetului din care face parte, atunci când este folosit în alt pachet. De observat c ă un
fișier surs ă nu creeaz ă un spa țiu de nume; este posibil și chiar uzual ca în componen ța unui pachet s ă intre
clase aflate în fi șiere surs ă diferite, dar care au la înc eput aceea și instruc țiune package .
Pachetul cu numele "java.lang" ( language ) este folosit de orice program Java și de aceea numele lui nu mai
trebuie men ționat înaintea numelui unei clase din java.lang . Clasele String,Integer,Object,System ș.a. fac
parte din pachetul java.lang. Exemplu de utilizare a unei clase dintr -un alt pachet decât java.lang :

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 28 –

Variabila cu numele "rand" este de tipul Random , iar clasa Random este definit ă în pachetul "java.util".
Nota ția rand.nextFloat() exprimă apelul metodei nextFloat() din clasa Random pentru obiectul adresat de
variabila rand .
Instruc țiunea import permite simplificarea referirilor la clase din alte pachete și poate avea mai multe forme.
Cea mai folosit ă form ă este: import pachet.* ;
Instruc țiunea anterioar ă permite folosirea numelor tuturor claselor dintr -un pachet cu numele "pachet", f ără a
mai fi precedate de numele pachetului. Exemplul urm ător ilustreaz ă folosirea instruc țiunii import :

Uneori se prefer ă importul de clase individuale, pent ru documentare și pentru evitarea ambiguit ăților create
de clase cu acela și nume din pachete diferite. Exemplu care arat ă riscurile importului tuturor claselor dintr –
un pachet:

IE.02.1.7 Definirea și utilizarea de vectori în Java
Cuvântul ―vector‖ este folosit aici ca echivalent pentru array din limba englez ă și se refer ă la un tip de date
implicit limbajelor C, C++ și Java. Acest tip este diferit de tipul definit de clasa Vector pentru vectori ce se
pot extinde automat) . In Java, declararea une i variabile (sau unui parametru formal) de un tip vector se
poate face în dou ă moduri, echivalente:
tip nume [ ]; // la fel ca in C si C++
tip [ ] nume; // specific Java

Declararea matricelor (vectori de vectori) poate avea și ea dou ă forme. Exemplu:
int a[ ][ ] ; // o matrice de întregi
int * +* + b; // altă matrice de întregi

In Java nu este permis ă specificarea unor dimensiuni la declararea unor vectori sau matrice, deoarece
alocarea de memorie nu se face niciodat ă la compilare. Exemplu de eroare: public static void main (String arg[]) {
java.util.Random rand =new java.util.Random();
for (int i=1;i<=10;i++) // scrie 10 numere aleatoare
System.out.println ( rand.nextFloat());
}
import java.util.*; // sau : import java.util.Random;
class R {
public stati c void main (String arg[]) {
Random rand =new Random();
for (int i=1;i<=10;i++) // scrie 10 numere aleatoare
System.out.println ( rand.nextFloat());
}
}
import java.util.*;
import java.awt.*;
. . .
public static void main (String av[ ]) {
List list; . . . // clasa java.awt.List sau interfa ța java.util.List ?
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 29 – int a[100]; // corect: int a[] = new int[100];

O variabil ă vector este automat în Java o variabil ă referin ță iar memoria trebuie alocat ă dinamic pentru orice
vector. Alocarea de memorie pentru un vector se face folosind operatorul new urmat de un nume de tip și
de o expresie (cu rezultat întreg) între paranteze drepte; expresia determin ă numărul de componente (nu de
octeți !) pe care le poate con ține vectorul. Exemple:
float x[ ] = new float [10]; // aloca memorie ptr 10 reali
int n=10;
byte[ ][ ] graf = new byte [n][n];

Este posibil ă și o alocare automat ă, atunci când vectorul este ini țializat la declarare cu un șir de valori.
Exemplu: short prime[ ] = {1,2,3,5,7};

In lipsa unei ini țializări explicite, componentele unui vector sunt ini țializate automat, cu valori ce depind de
tipul lor: zerouri pentru elemente numerice și null pentru variabile referin ță de orice tip.
Un vector intrinsec cu componente de un anumit tip este considerat ca un obiect de un tip clas ă, tip
recunoscut de compilator dar care nu este definit explicit în nici un pachet de clase. Numele acestor clase
este format din caracterul ‗[‗ urmat de o liter ă ce depinde de tip ul componentelor vectorului: [I pentru int[],
[B pentru byte[] , [Z pentru boolean[] , [C pentru char[] , [F pentru float[] s.a.m.d.
Variabila predefinit ă cu numele length poate fi folosit ă ( ca membru al claselor vector ) pentru a ob ține
dimensiunea alocat ă pentru un vector (―capacitatea vectorului‖). Exemplu:

De re ținut c ă length este dimensiunea alocat ă și nu dimensiunea efectiv ă a unui vector, iar num ărul de
elemente din vector se transmite ca argument la func ții, atunci când difer ă de capacitatea s a. Variabila length
nu trebuie confundat ă cu metoda length() din clasa String .
In Java, se verific ă automat, la execu ție, încadrarea indicilor între limitele declarate; ie șirea din limite
produce o excep ție și terminarea programului. Exemplu:

Numero tarea componentelor unui vector este de la zero la (length -1), deci în exemplul anterior se produce
excep ția de dep ășire a limitelor la valoarea i=10 .
O matrice este privit ă și în Java ca un ve ctor de vectori, iar variabila length se poate folosi pentru f iecare
linie din matrice, pentru a determina num ărul de coloane. Deoarece orice matrice este alocată dinamic, nu
există probleme la transmiterea unei matrice ca argument la o func ție. Nu este necesară transmiterea
dimensiunilor matricei la o func ție dacă m atricea este ocupată complet (la capacitatea ei).
O func ție poate avea un rezultat de un tip vector (sau matrice). // functie de copiere a unui vector
public static void copyVec ( int a [ ] ,int b[ ] ) {
for (int i=0;i < a.length; i++) // a.length =dimensiune a vector ului a
b[i] = a[i];
}
int [ ] a= new int [10];
for (int i=1;i<=10;i++) a[i]=i; // exceptie la a[10]=10

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 30 – In Java, ca și în C, transmiterea parametrilor se face prin valoare, adic ă se copiaz ă valorile parametrilor
efectivi în parametrii formali corespunz ători, înainte de execu ția func ției. Deci o func ție Java nu poate
transmite rezultate prin argumente de un tip primitiv, dar poate modifica componentele unui vector primit ca
argument. Exemplu:

Clasa Arrays din pachetul java.util reune ște funcții pentru opera ții uzuale cu vectori având elemente de orice
tip (primitiv sau clas ă): afi șare, sortare, căutare s.a.
IE.02.2 Definirea de clase în Java
O clasă Java corespunde unui tip structură din limbajul C, dar o clasă mai conține ca membri și fun cții
(metode) care realizează operații cu variabilele clasei. Clasele Java pot avea diferite niveluri de
accesibilitate fa ță de alte clase:
public (accesibil ă pentru orice alt ă clasă)
private (inaccesibil ă pentru orice alt ă clasă)
protected (accesibil ă pentru subclase)
package (implicit ) ( accesibil ă pentru clase din acela și pachet de clase)
Clasele de bibliotec ă Java sunt publice și fiecare este definit ă într-un fi șier separat de celelalte clase; numele
clasei este acela și cu numele fi șierului.
O clas ă (neabstract ă) poate con ține: numai metode statice , numai date , date și metode nestatice ( Object
methods ), date, metode statice și nestatice .
Notiunea de ―func ție‖ se folose ște în Java pentru ambele categorii de func ții dintr -o clasă Java:
– Metode statice s au nestatice (cu nume diferit de numele clasei)
– Constructori de obiecte (cu acelasi nume ca și clasa)
IE.02. 2.1 Metode statice
Metodele statice se folosesc rar: func ții cu date și rezultate de un tip primitiv , func ții recursive, ș.a.
Utilizarea excesiv ă de metode statice este specific ă program ării procedurale (practicat ă în limbajul C)
Metodele statice sunt precedate de cuv ântul static și au alt mod de utilizare dec ât metodele aplicabile
obiectelor. Utilizarea lor nu este condi ționat ă de existen ța unor obi ecte. Ele se mai numesc și metode ale
claselor ( Class Methods ) în contrast cu metodele nestatice (ale obiectelor). static int divizori (int n, int d[ ]) { // creare vector cu divizorii unui întreg
int k=0;
for (int i=1;i<n;i++)
if ( n %i == 0)
d[k++]=i; // pune divizorul i în vectorul d
return k; // numar de divi zori
}
// utilizare func ție
int div[ ]= new int [m]; // aloca memorie ptr vector divizori
int nd = divizori (m, div); // completare vector divizori

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 31 – Metodele statice corespund func țiilor din C; ele pot fi apelate f ără a crea obiecte dar folosind și numele clasei
înaintea numelui metodei. Metodele statice sunt de obicei și publice, pentru a fi apelate din afara clasei în
care sunt definite
Metoda main cu care începe execu ția unei aplica ții Java trebuie s ă fie o metod ă static ă pentru c ă la început
nu exist ă nici un obiect. Clasa de bibliotec ă Math grupeaz ă metode (func ții) matematice care au argumente
și rezultate de un tip primitiv (double), deci nu sunt asociate unor obiecte sau clase. Exemplu:

IE.02.2.1 Metode ale obiectelor
Specific program ării orientate obiect este definirea și utilizarea de clase cu date și metode nestatice, numite
Object Methods (metode aplicabile obiectelor) . Metodele obiectelor sunt in general publice pentru a putea fi
apelate din afara clasei. Metodele publice nestatice ale unei clase pot fi clasificate astfel :
– Metode de acces la datele încapsulate în clas ă (getter, setter )
– Metode mo ștenite de la clasa Object și redefinite (suprascrise)
– Metode specifice clasei respective (determinate de rolul ei in aplica ție)
O clasă Complex pentru numere complexe , va conține partea reală și partea imaginară a unui număr
complex (ca date private), metode publice de acces la aceste date, metode mostenite ( toString(), equals() )
dar și operații necesare lucrului cu numere complexe: adunare, scădere, ș.a.

Datele unui obi ect au de obicei atributul private ; ele nu sunt accesibile direct pentru metode din alte clase ci
numai prin intermediul metodelor clasei. De exemplu, variabilele re și im dintr -un obiect de tip Complex nu
pot fi citite sau modificate, dar metode ale clase i Complex operează cu aceste variabile. Dacă este nevoie de
citirea sau de modificarea directă a datelor dintr -un obiect atunci se vor defini metode getter și setter :
class Sqrt {
public static void main (String args[ ]) {
int x = Integer.parseInt (args[0]); // functie statica din clasa Integer
double r = Math.sqrt (x); // functie statica din clasa Math
System.out.println (r);
}
}
public class Complex {
// datele clas ei
private int re, im;
// constructor
public Complex (int re, int im) { this.re=re; this.im=im; }
// metode publice proprii clasei
public void add ( Complex cpx) { // adunarea a doua numere
re = re + cpx.re; im = im + cpx.im;
}
public void sub ( Complex cpx) { // scaderea a doua numere
re -= cpx.re; im -= cpx.im;
}
// metode mostenite si redefinite
public String toString () , return "(" + re + "," + im + ")“;-
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 32 – public void set Re (re,2); // re=2
public int get Re (re);

Modificarea datelor din obiecte este consistentă cu operațiile permise și se evită erori de programare cauzate
de modificări nedorite ale datelor. De exemplu, datele dintr -un vector folosit ca stivă nu se pot accesa sau
modifica aleator ci numai în cadrul impus de operațiile push și pop (la vârful stivei).
Toate clasele Java extind clasa Object și redefinesc câteva metode moștenite de la clasa Object : toString (),
equals (), ș.a.

O func ție nu poate tra nsmite în afar ă adresa unui obiect creat în func ție printr -un parametru referin ță.
Exemplu de func ție fără efect în afara ei:

Aici se creează un obiect String prin metoda toUpperCase() , iar adresa sa este memorată în variabila locală
―t‖ (care con ținea ini țial adresa șirului dat). Un obiect creat într -o func ție trebu ie transmis ca rezultat al
funcției. Exemplu:
static String toUpper (String s) { return s.toUpperCase(); }
O func ție poate modifica un obiect a c ărui adres ă o prime ște ca argument numai dac ă în clasa respectiv ă
exist ă metode pentru modificarea obiectel or.
Clasele String, Integer, Float ș.a. nu con țin metode pentru modificarea datelor din aceste clase, deci o
funcție care prime ște o referin ță la un astfel de obiect nu poate modifica acel obiect. In acest fel se protejeaz ă
obiectul transmis ca argument f ață de modificarea sa nedorit ă de către func ția care îl folose ște. Obiectele din
clase f ără metode de modificare a datelor ( read-only) se numesc obiecte nemodificabile ( immutable objects ).
Pentru o scriere mai compact ă se practic ă uneori înl ăntuirea de me tode ( method chaining ), adic ă aplicarea
unei metode asupra rezultatului unei alte metode, într -o aceeasi expresie. Exemple:
String line = f.readLine().trim().toUpperCase(); // citeste linie, elimina spatii si trece in litere mari
if ( fname.substring (fname.indexOf(‘.’)+1).toLowerCase().equals(“java”) ) …
public class Complex extends Object {
… // date si constructori
// redefinire metoda mostenita
public String toString () {
StringBuffer sb = new StringBuffer ();
sb.append(re);
if (im>0) sb.append('+');
else { sb.append ('-'); im = -im; }
return sb.append(im).toString();
}
… // alte metode
}

// metoda statica pentru trecere sir in litere mari – gresit !!!
static void toUpper (String t) {
t = t.toUpperCase(); // se cree aza un nou obiect, cu alta adresa
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 33 – Utilizarea abuzivă de metode statice este o prelungire a programării procedurale în limbajele orientate obiect.
Utilizarea de metode obiect (nestatice) permite utilizarea unor tehnici specifice pro gramării cu obiecte:
derivare, mo ștenire, polimorfism, programare cu interfete, s.a. Avantajele metodelor obiect sunt mai evidente
în cazul unor familii de clase deschise pentru extindere și atunci când se urmăre ște reutilizarea metodelor
unor clase în alt e clase.
IE.02.3 Obiecte Java
Un obiect Java corespunde unei variabile de un tip structură din C; obiecte diferite pot conține date diferite
dar operațiile sunt comune tuturor obiectelor.
Toate obiectele sunt create dinamic, folosind operatorul new, care apelează implicit constructorul clasei
pentru inițializarea variabilelor din obiectul creat.
Obiecte diferite din aceea și clas ă suport ă acelea și opera ții (comune obiectelor clasei) dar con țin de obicei
date diferite (nu neap ărat). Obiecte diferite au adre se diferite în memorie, adrese memorate în variabilele
referin ță. Variabilele de tip clas ă corespund variabilelor pointer din limbajele C . Ele se numesc variabile
referin ță deoarece permit referirea la obiecte. Exemplu de creare și utilizare de obiecte Java:

Variabilele de un tip clas ă contin referin țe (pointeri) la obiecte, în timp ce variabilele de un tip primitiv ( int,
double, char ) con țin valori . Inițializarea variabilelor referin ță se face de obicei cu rezultatul operatorului new
dar și ca rez ultat al unei atribuiri sau unei func ții. Este posibil ca dou ă sau c âteva variabile referin ță să se
refere la acela și obiect (prin atribuire între ele) .
Efectul atribuirii între variabile referin ță este copierea unei adrese și nu copierea unui obiect; dupl icarea unui
obiect se face prin clonare. Folosirea operatorului de compara ție == între variabile referin ță va compara
adrese și nu obiecte; compara ția la egalitate între obiecte se face cu metoda equals() care exist ă în orice clas ă
(moștenită de la clasa Object și redefinit ă). Exemplu:
String linie= file.readLine();
if (linie.equals (“.”)) break; // NU: if (linie==“.”) break;

Majoritatea claselor sunt clase instan țiabile (care pot genera obiecte prin instan țierea clas ei), deoarece
acțiunile dintr -un program Java rezult ă prin apeluri de metode între obiecte.
Instan țierea unei clase se face în Java folosind operatorul new, care aloc ă memorie pentru un nou obiect și
face ini țializările necesare . Inițializarea datelor (var iabilelor) dintr -un obiect este realizat ă prin apelarea unei
funcții numit ă ―constructor‖, care are numele clasei. Orice clas ă instan țiabilă trebuie s ă aibă (cel pu țin) un
constructor public (apelabil dintr -o alt ă clasă). Este uzual ca o clas ă să aibă câțiva constructori, care se
deosebesc prin argumente (parametri) și prin num ărul de variabile ini țializate .
public static void main (String[] a) {
Complex c1 = new Complex (2,3); // un obiect
Complex c2 = new Complex (1,4); // alt obiect
c1.add (c2); // c1=c1+c2
System.out.print ln (c1.toString( ) ); // afisare date obiect c1
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 34 – In lipsa unor constructori declara ți explicit de programator, o rice clas ă Java prime ște automat un constructor
public f ără argumente și care nu are ni ci un efect .
IE.02.4 Cuvântul cheie this
Cuvântul cheie this are dou ă utiliz ări în Java:
– ca variabil ă referin ță implicit ă care se refer ă la obiectul curent și este folosit în metode nestatice
– ca nume de constructor.
Cuvântul this este folosit ca varia bilă referin ță în diferite si țuatii:
– Pentru a deosebi variabilele clasei de argumente cu acela și nume:
public MyVector ( int nmax) , this.nmax=nmax; n=0; …-

– Pentru a explicita c ă este vorba despre metode sau variabile din aceea și clasă sau din acela și obiect:
public int size () { return this.n;} // din clasa MyVector

– Atunci c ând rezultatul metodei este chiar obiectul pentru care s -a apelat metoda ( modificat ) :

Un constructor se poate referi la alt constru ctor din aceea și clas ă numai folosind this și nu prin numele sau.
Exemplu:

IE.02.5 Șiruri de caractere în Java
Opera țiile cu șiruri sunt prezente în multe aplica ții și ele difer ă mult în Java de forma lor din limbajul C. In
Java un șir este u n obiect din c lasa String sau din clasele StringBuffer , StringBuilder , iar opera țiile cu
șiruri se realizeaz ă prin apeluri de metode din aceste clase. Constantele șir, între ghilimele, sunt obiecte
nemodificabile de tip String . // metoda din clasa StringBuffer
public StringBuffer deleteCharAt (int k) { // sterge caracterul din poz k
System.arraycopy (value, k+1, value, index, count -k-1);
count –; // numar de caractere in sir
return this; // rezultatul este obiectul modificat de metodă
}

public class MyVector {
private Object a[]; // adresa vector
private int n, nmax; // dimensiune si capacitate vector
public MyVector (int m) { // un constructor
nmax=m; n=0;
a= new Object[m]; // aloca memorie
}
// alt con structor
public MyVector () { this (10); } // capacitate implicita 10
….
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 35 – Clasa String este o clas ă read-only și final ă, deci nu se poate extinde cu metode de modificare a vectorului
de caractere con ținut în fiecare obiect. Exemplu de utilizare gre șită a metodei replaceAll() din clasa String
pentru înlocuirea unui sub șir s1 cu un alt sub șir s2 în șirul s: s.replaceAll (s1,s2);
Metodele replaceAll() si replace() au ca rezultat un nou șir ob ținut dup ă substituire și nu modific ă obiectul
pentru care se apeleaz ă; de aceea utilizarea corect ă este urm ătoarea: s = s.replaceAll (s1,s2);
Clasele StringBuffer si StringBuilder (din 1.5 ) sunt variante ale clasei String care con țin în plus și metode
pentru modificarea obiectului ( șirului). Exemple de metode care modific ă șirul con ținut într -un obiect de tip
StringBuffer : append, insert, delete, setCharA t, setLength. Un obiect de tipul StringBuffer transmis unei
funcții ca argument poate fi modificat de c ătre func ție. Variant ă pentru func ția toUpper() :

Clasa StringBuilder este mai performant ă decât StringBuffer pentru programele f ără fire de execu ție.
Concatenarea de șiruri este o opera ție frecvent ă în Java. Metoda println() folosit ă pentru afi șarea pe ecran
poate avea un singur argument de tip String . Pentru a scrie mai multe șiruri acestea se concateneaz ă într-un
singur șir cu operatorul ‗+‘.Exemplu :
System.out.println ( “x= “ + x); // x de orice tip

Intr-o expresie cu operatorul binar ‗+‘, dac ă unul din operanzi este de tip String , atunci compilatorul Java
face automat conversia celuilalt operand la tipul String (pentru orice tip pr imitiv și pentru orice tip clas ă care
redefineste metoda ―toString‖). Aceast ă observa ție poate fi folosit ă si pentru conversia unui num ăr în șir de
caractere, ca alternativ ă a utiliz ării metodei valueOf() din clasa String . Exemplu:
float x = (float) Ma th.sqrt(2);
String str = ““+x; // sau str = String.valueOf(x);

O instruc țiune de forma a=a+b; cu ―a‖ si ―b‖ de tip String este tratat ă de compilator astfel: se transform ă
obiectele a și b în obiecte de tip StringBuilder , se ape lează metoda append() și apoi creeaz ă un obiect String
din obiectul StringBuilder rezultat din concatenare:
String a=“unu”, b=“doi”;
StringBuffer am= new StringBuffer (a), am.append(bm);
a= new String (am);

Dacă trebuie s ă facem multe con caten ări de șiruri este preferabil ca timp s ă se foloseasc ă direct metoda
append() din clasa StringBuilder . Exemplu:

public static String a rrayToString ( int a[ ]) { // creare sir cu continutul unui vector
StringBuilder aux = new StringBuilder (”*”);
int n =a.length;
for (int i=0;i<n -1;i++)
aux.append (a*i+ + ”,”);
return new String (aux.append (a[n -1+ +”+”) ) ;
} static void toUpper (StringBuffer s) {
String str= new String (s);
s.replace (0,str.length(),str.toUpperCase());
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 36 – De observat c ă trecerea de la tipul String la tipul StringBuffer se poate face numai printr -un constructor, dar
trecerea invers ă se poate face prin metoda toString() , iar aceste transform ări pot fi necesare pentru c ă în clasa
StringBuffer nu se reg ăsesc toate metodele din clasa String .
Metoda toString() exist ă în toate clasele și produce un șir cu datele din obiectul pentru care s e apeleaz ă (face
conversia de la tipul datelor din obiect la tipul String ). Orice tip de obiect se poate transforma într -un șir
String prin metoda toString() , apelat ă explicit sau implicit, dar nu și prin cast. Exemplu:
Float x = new Float(3.14); String s = (String)x; // eroare la compilare
String s =x.toString(); String s = x+””; // apel implicit “toString”

Pentru extragerea de cuvinte ( tokens ) dintr -un text se folosesc fie clasele Scanner , StringTokenizer și
StreamTokenizer , fie clasele pentru lucrul cu expresii regulate: Pattern , Matcher ș.a.
Impăr țirea unui text în cuvinte se poate face folosind clasele Pattern , Matcher sau cu metoda split() din
clasa String , care creează un vector de cuvinte: String[] split (regex)
IE.02.6 Opera ții de citire -scriere în Java
Majoritatea aplica țiilor locale ( Desktop Application s) folosesc o interfa ță grafică pentru preluarea datelor de
intrare și pentru prezentarea rezultatelor. Programele folosite în linie de comandă și cele care folosesc fișiere
text trebuie să folosească clase de intrare -ieșire. Există un număr mare de clase de I/E: cele mai vechi în
pachetul java.io iar cele mai noi în pachetul java.nio . Aceste clase se folosesc direct (prin obiecte ale lor) sau
indirect, prin clase care adaugă și unele prelucrări cum sunt Scanner, Formatter , StreamTokenizer , s.a.
Afișarea pe ecran și scrierea în fi șiere text a oric ărui tip de date se face simplu folosind metodele print() și
println() din clasele PrintStream sau PrintWriter . Aceste metode pot primi un singur argument de orice tip
primitiv sau de tipul generic Object și realizeaz ă automat conversia numerelor din format intern în format
extern ( în șir de caractere). Variabila System.out , de tip PrintStream , desemnează ecranul și este folosită
pentru afi șarea în mod text (nu și într -o interfa ță grafică). Pentru citire de numere de la consolă, cu conversie
din format extern în format intern, se poate folosi clasa Scanner . Exemplu:

Numerele citite cu un obiect Scanner pot fi repartizate cât e unul sau mai multe pe o linie și terminate cu un
caracter nenumeric (care nu pot să apară într -un număr).
In general, un fi șier text este identificat sau prin numele său (de tip String ) sau prin calea completă la fi șier
(obiect de tip java.io.File ). Clasele de I/E pentru opera ții cu fi șiere disc au constructori cu argument String
si cu argument File pentru numele fi șierului disc cu care lucreaz ă. Constructorii și metodele acestor clase pot
genera excep ții de tip IOException , care tr ebuie aruncate sau tra tate.
Excep țiile sunt provocate în general de erori la execu ție, dar pot fi cauzate și de alte situa ții care nu sunt erori
și nu trebuie să producă terminarea automată a programului (de exemplu sfâr șit de fi șier la citire). Unele
excep ții nu necesită nici o acțiune din partea programatorului, iar efectul lor este oprirea programului cu un
mesaj privind cauza excep ției și linia sursă unde s -a produs. Excep țiile la opera ții de intrare -ieșire (și altele) public static void main (String arg[]){
float sum=0;
Scanner sc = new Scanner (System.in);
while ( sc.hasNextFloat())
sum += sc.nextFloat();
System.out.println(sum);
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 37 – trebuie fie aruncate (clauza throws cu numele excep ției în antetul func ției în care poate apare excep ția), fie
tratate printr -un bloc de instruc țiuni (construc ția try-catch ).
Clasele din pachetul java.io pot fi grupate astfel:
– Clasa RandomAccessFile , pentru opera ții de citire și/sau de scriere cu acces secven țial și direct
– Clasele din familiile InputStream si OutputStream , pentru opera ții de citire octe ți și respectiv de scriere
octeți (dar nu citire și scriere în acela și fișier)
– Clasele din familiile Reader si Writer , pentru opera ții de citire caractere și respectiv de scriere caractere
(dar nu citire și scriere în acela și fișier).
Pentru a scrie numere într-un fi șier text pe disc se creeaz ă mai întâi un obiect FileInputStream sau
FileWriter și apoi se creeaz ă obiectul PrintStream sau PrintWriter . Exempl u de scriere întregi :

Exist ă și un corespondent al func ției sprintf() din limbajul C sub forma metodei format() din clasele
String si java.util. Formatter , cu o utilizare asemănătoare func ției sprintf() .
Pentru scrierea de șiruri în fi șiere text se p oate utiliza direct metoda write () din clasa FileWriter , dar pentru
a scrie linii de text trebuie ad ăugat explicit caracterul ‗ \n‘. Exemplu:

Citirea de linii de la tastatur ă sau dintr -un fi șier text se poate face cu metoda readLine() din clasa
BufferedReader sau din DataInputStream , pentru citire din fisiere tex t formate din linii . Exemplu de citire
linii d intr-un fișier text pe disc :

Exemplu de citire linii de la tastatură (consolă):

try {
PrintStream ps = new PrintStream ( new F ileOutputStream ("numere.txt")) ;
for (int x=1; x<100;x++)
ps.print (x+" "); // cu spatii intre numere
ps.close();
} catch (IOException e) { e.printStackTrace();}
try {
FileWriter fw = new FileWriter("t.txt");
for (int i=1;i<21;i++)
fw.write (i+" "); // conversie din “int” in String si spatii intre numere
fw.close();
} catch (IOException e) { e.printStackTrace();}
String line, fname; // linie de text si nume fisier
try {
BufferedReader br = new BufferedReader (new FileReader(fname));
while ( (line = br.readLine()) != null) // rezultat null la sfârsit de fisier
System.out.println (line);
} catch (Exception e) { e.printStackTr ace();}
public static void main (String [] arg) throws IOException {
String line;
DataInputStream cin = new DataInputStream (System.in);
while ( (line = cin.readLine()) != null)
System.out.println (line);
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 38 – Pentru citire de numere de la consol ă trebuie extr ase din linia citit ă șirurile de cifre și făcută conversia din
format extern în format intern (binar) folosind metode statice ca Double.parseDouble(String s)
Integer.parseInt(String s), Float.parseFloat(String s) , ș.a. Exemplu:

Pentru prog ramul anterior am considerat c ă se introduce o liter ă sau alt caracter nenumeric pentru a termina
secven ța de numere introdus ă, iar aceast ă literă produce excep ția de format nenumeric. La citirea dintr -un
fișier text pe disc este mai simplu, deoarece sfâr șitul de fi șier este recunoscut dup ă lungimea fișierului și nu
trebuie folosite caractere speciale ca terminator de fi șier.
Clasa RandomAccessFile are o serie de avantaje fa ță de alte clase de I/E, dar acest tip nu apare ca posibil
argument în constructori i altor clase care folosesc fi șiere disc: clase pentru compresie și arhivare de fi șiere,
clase parser de fi șiere text sau de fi șiere XML, s.a. Ace ști constructori au fie un argument File, fie un
argument de tip Reader (InputStream ) sau Writer (OutputStream ). Clasa RandomAccessFile permite
atât opera ții de scriere cât și opera ții de citire, permite crearea și citirea de fi șiere binare, cu numere în
format intern ( readInt() , writeInt() , s.a.), permite citirea de linii de text ( readLine() ), permite accesul d irect
la date din fi șier pe baza adresei de octet în fi șier (seek() ), aflarea adresei curente în fi șier (getFilePointer() ),
aflarea lungimii unui fi șier (length() ) și permite citirea unui întreg fi șier în memorie ( readFully() ). public static void main (String arg[] )throws IOException{
float x,sum=0; String line, words[];
DataInputStream cin = new DataInputStream (System.in);
try {
while ( (line = cin.readLine()) != null){
words=line.split(" ");
for (String w: words )
if ( ! w.isEmpty()) {
x=Float.parseFloat( w);
sum += x;
}
}
} catch (NumberFormatException e){
System.out.println (sum);
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 39 – Capitolul IE. 03. Derivar e, moștenire, polimorfism în Java
Cuvinte cheie
Derivare, Extindere, Mo ștenire, Superclas ă, Subcla să,
Ierarhii de tipuri, Suprascriere metode, Cuvântul “super ”

IE.03.1 Derivarea (extinderea) claselor
Derivarea este o tehnic ă de programare specific ă program ării orientate obiect și este folosit ă pentru:
– Reutilizarea unor metode dintr -o clas ă existent ă în clasele derivate , fără a mai fi declarate sau definite.
– Crearea unor ierarhii de tipuri compatibile.
Derivarea înseamn ă definirea unei clase D ca o subclas ă a unei clase A, de la care ―mo ștenește‖ toate
variabilele și metodele dar nu și constructorii . Clasa A se mai nume ște și clas ă de baz ă sau clas ă părinte sau
superclasa lui D, iar D se nume ște subclasa lui A.
In Java se spune c ă o subclas ă extinde func ționalitatea superclasei, în sensul c ă ea poate con ține metode și
date suplimentare. In general o subclas ă este o specializare, o particularizare a superclasei și nu extinde
domeniul de utilizare al superclasei. De exemplu, o mul țime realizată ca vector este un caz particular de
vector în care elementele sunt diferite între ele, iar clasa mul țime poate fi derivată din clasa vector cu
redefinirea metodelor de adăugare la vector.
―Moștenire‖ ( Inheritance ) înseamnă că toate metodele publice și protect ed din clasa A pot fi folosite pentru
obiecte din clasa D, fără ca acestea să fie declarate sau definite în D. Variabilele clasei A se regăsesc și în
obiectele de tip D. Variabilele private din superclasa A se mo ștenesc dar nu sunt direct accesibile pentru a fi
folosite în noile metode definite în subclasa D.
Subclasa D poate redefini metode mo ștenite de la clasa p ărinte A și poate ad ăuga noi metode si variabile
clasei A. Tipul D este un subtip al tipului A. La definirea unei clase derivate se foloseste cu vântul cheie
extends urmat de n umele clasei de bază.
Exemplul următor arată cum se poate defini o clasă VSet pentru multimi din vectori prin extinderea clasei
java.util.Vector și redefinirea a două metode mo ștenite : addElement() si setElementAt (). Pentr u obiecte de
tip VSet se pot folosi toate metodele publice din clasa Vector (peste 50 ca număr): toString(), size() ,ș.a., a șa
cum se vede din exemplul următor:

Definirea clasei derivate, cu metodele mai vechi din clasa Vector , poate arăta astfel:

class VSet extends Vector {
public void addElement (Object obj ) { // adaugare la multimea vector
if ( ! contains(obj)) // daca nu exista deja obj
super.addElement(obj); // atunci se adauga la multimea vector
}
} public static void m ain (String [] args) {
VSetv =new VSet();
for (int i=1;i<21;i++)
v.addElement (new Integer(i%10));
System.out.println (v.toString()); System.out.println (v.size());
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 40 – Se observă cum metoda addElement() din subclasă apelează o metodă mo ștenită de la superclasa Vector și
anume contains (). Crearea de elemente cu aceea și valoare se poate face și cu metoda care modifică valoarea
unui element dintr -o pozi ție dată setElementA t ( Object obj, int i) , care ar trebui fie suprascrisă, fie interzisă
pentru mul țimi vector (în Java nu este permis accesul prin indici la mul țimi).
În acest exemplu metoda suprascrisă din subclasă apelează metoda cu acela și nume și argumente din
supercla să, iar pentru a deosebi cele două versiuni se foloseste cuvântul cheie super . Dacă nu s -ar folosi
super atunci func ția ar fi infinit recursivă.
O subclasă poate deveni superclasă pentru alte (sub)clase, iar derivarea (extinderea) poate continua pe oricâte
niveluri. In Java nu este permisă extinederea simultana a mai multor clase pentru mo ștenire multiplă de date
și opera ții de la câteva clase. Cuvântul extends poate fi urmat de un singur nume de clasă.
Nu se poate extinde o clas ă finală (cu atributul final ) și nu pot fi redefinite metodele din superclas ă care au
unul din modificatorii final , static, private .
IE.03.2 Clasa Object ca baz ă a ierarhiei de clase Java
In Java, clasa Object (java.lang.Object) este superclasa tuturor claselor JDK și a claselor de finite de
utilizatori. Orice clas ă Java care nu are clauza extends în defini ție este implicit o subclas ă derivat ă direct din
clasa Object . Clasele abstracte și interfe țele Java sunt și ele subtipuri implicite ale tipului Object .
Variabil e sau argument e de tip Object (referin țe la tipul Object ) pot fi înlocuit e cu variabil e de orice alt tip
clasă, deoarece orice tip clas ă este în Java derivat din și deci compatibil cu tipul Object .
Clasa Object transmite foarte pu ține opera ții utile subclaselor sale; de aceea în alte ierarhii de clase (din alte
limbaje) r ădăcina ierarhiei de clase este o clas ă abstract ă. Deoarece clasa Object nu con ține nici o metod ă
abstract ă, nu este obligatori e redefinirea metode lor mostenite , dar în practic ă se redefinesc câteva meto de:
toString(), equals(), hashCode().
Metoda toString() din clasa Object transform ă în șir adresa obiectului pentru care se apelează this (un șir de
8 caractere hexazecimale) și deci trebuie s ă fie redefinit ă în fiecare clas ă cu date, pentru a produce un șir cu
conținutul obiectului. Metoda toString() permite ob ținerea unui șir echivalent cu orice obiect, deci trecerea
de la orice tip la tipul String . Ea este apelată explicit sau implicit când este necesară conversia. Exemple:
Date d = new Date(); S tring s = d.toString(); // apel explicit
System.out.println (d); // apel implicit: println( d.toString())
String s = d+” \n”; // apel implicit: d.toString()+” \n”

Metoda equals() din clasa Object consider ă că două variabile de tip Object sau de orice tip derivat din
Object sunt egale dac ă și numai dac ă se refer ă la un acela și obiect:
public boolean equals (Object obj) { return (this == obj); }

Pe de alt ă parte, un utilizator al clasei String ( sau al altor clase cu date) se a șteapt ă ca metoda equals() să
aibă rezultat true dacă două obiecte diferite (ca adres ă) con țin acelea și date. De aceea, metoda equals() este
rescris ă în clasele unde se poate defini o rela ție de egalitate între obiecte. Exemplu de clasă pentru o pereche
cheie -valoar e, necesară în clasele dic ționar:

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 41 –

Metoda equals() moștenită are argument de tip Object , dar în cadrul clasei Entry ea va fi apelată pentru a
compara două obiecte de tip Entry ; de aceea prima opera ție din metoda redefinită este conversia la t ipul
Entry pentru a putea folosi membrii acestei clase (variabile și metode). De observat ca se compara doar
cheile în metoda equals(), nu si valorile.
O metodă equals() cu argument de tip Entry nu ar reprezenta rescrierea metodei mo ștenite ci o altă metod ă;
există metode în colectii și dictionare ( indexOf(), contains() , s.a.) care folosesc metoda equals() cu argument
Object și care nu ar lucra corect în cazul metodei equals() cu argument Entry .
Pentru dic ționare tabel de dispersie ( HashMap ) ar trebui redef inită și metoda hashCode() , dar poate fi
utilizată și varianta mostenită (care folose ște adresa obiectului drept identificator unic al obiectului).
IE.03.3 Suprascriere de metode î n subclase
De multe ori, subclasele nu fac altceva decât s ă redefineasc ă una sau câteva metode ale superclasei din care
sunt derivate. Acest tip de redefinire se nume ște suprascriere ( Overriding ) pentru a fi deosebită de
redefinirea unei metode cu argumente diferite în aceea și clasă, opera ție numită supraîncărcare ( Overloading ).
Supraîncărcarea este rezolvată de compilatorul Java, dar selectarea unei metode dintre mai multe metode
suprascrise și cu acela și nume se face la execu ție (pe baza unui tabel de metode generat de compilator pentru
fiecare clasă).
Redefinirea unei metode t rebuie s ă păstreze "amprenta" func ției, deci numele și tipul metodei , numărul și
tipul argumentelor . Dacă nu se respectă aceste reguli atunci compilatorul consideră că subclasa adaugă altă
metodă la cele mo ștenite și nu suprascrie o metodă mo ștenită. La în ceput compilatorul Java nu putea verifica
acest fel de eroare de programare și de aceea s -a adăugat limbajului o adnotare numită @Override care poate
fi folosită de programatori pentru a arăta că se inten ționează o suprascriere și care permite compilatorul ui să
verifice că metoda redefinită are acelasi tip și aceleasi argumente ca una mo ștenită.
Nu este neobi șnuit ca o metodă să fie și supraîncărcată (în aceeasi clasă) și suprascrisă (în alte clase). Un
exemplu este forma mai nouă a metodei de adăugare la u n vector de tip ArrayList , metodă numită add() .
In exemplul următor se define ște altă clasă pentru multimi vector, prin extinderea clasei java.util.ArrayList
si suprascrierea a două metode: adăugare la sfâr șit de vector și adăugare într -o pozi ție dată din vector
(inser ție în vector). public class Entry {
private Object key,value;
public Entry ( Object k, Object v ) {
key=k; value = v;
}
public String toString () {
return "("+key.toString()+"="+value.toString()+")";
}
public boolean equals (Object obj) {
Entry e = (Entry)obj;
return key.equals (e.key); // this.getKey().equals (e.getKey());
}
public Object getKey() { return key;}
public Object getValue() { return value;}
public void setKey( Object k) {key=k;}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 42 –

Pentru a fi compatibilă cu alte clase mul țime ar trebui ca și clasa ArraySet să implementeze interfa ța Set.
Operatiile clasei derivate Array Set pot fi și ele reutilizate prin extindere și definirea unei clase
SortedAr raySet pentru mul țimi ordonate realizate ca vectori.
Metodele equals() și toString() pot fi mo ștenite de subclasă sau redefinite dacă au efect diferit; în exemplul
anterior ele sunt mo ștenite fără modificări. Exemplul următor arată cum se poate defini o mu lțime ca tabel de
dispersie pe baza clasei Hashtable , punând valori identice cu cheile asociate.

Redefinirea metodei toString() s-a făcut pentru a nu se afi șa și pentru mul țimi perechi de valori
(identice) de forma k=v (k = cheie, v= valoare), a șa cum se face în clasa dic ționar Hashtable .
IE.03.4 Derivarea ca metod ă de reutilizare
Principala modalitate de specializare a unei clase este redefinirea unor metode din superclas ă. Prin redefinire
(override ) se modific ă opera țiile dintr -o metod ă, dar nu și modul de utilizare al metodei.
Clasa SortedArray definită anterior refolose ște func ționalitatea clasei ArrayList , adică toate opera țiile deja
definite acolo pentru vectori extensibili dinamic: adăugare, inser ție, înlocuire, eliminare de elemente,
adăugar ea unei colec ții la vector, căutarea unei valori date de la început sau de la sfâr șit ș.a. In mod
asemănător se pot defini prin derivare clase pentru mul țimi vector, pentru stive vector ș.a.
O clasă pentru mul țimi realizate ca listă înlăn țuită poate fi ob ținută la fel de simplu, prin extinderea clasei
java.util.LinkedList , fără să ne intereseze detaliile de lucru cu lista, care sunt încapsulate în clasa de
bibliotecă deja definită. public class ArraySet extends ArrayList {
@Override
public boolean add (Object obj) { // adaugare la multime
if ( contains(obj) return false; // daca exista deja, se iese
super.add(obj); // se adauga daca nu exista deja
return true;
}
@Override
public void add (int k, Object obj) {
throw new UnsupportedOperationException() ;
}
}
class HSet extends Hashtable {
public boolean add (Object obj) {
return (put (obj,obj)== null);
}
public String toString() {
String s="";
Enumeration e= keys();
while (e.hasMoreElements())
s= s+ e.nextEleme nt()+" ";
return s;
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 43 – Absen ța unor clase pentru mul țimi vector sau mul țimi liste sau dic ționare di n vectori din bibliotecile Java se
explică tocmai prin u șurința de a defini astfel de clase prin derivare de către programatori.
Avantajele reutilizării unor clase existente este și mai evident în cazul claselor Swing sau AWT pentru
obiecte de interfa ță grafică: pu țini programatori ar putea să scrie singuri clase ca JFrame, JPanel,
JTextField, JTable, JTree s.a. dar ob ținerea unor subclase ale acestora este destul de simplă.
Pentru a interzice utilizarea unor metode mo ștenite în clasa derivată se suprascriu acele metode cu o nouă
defini ție în care se aruncă excep ții de tip NotSupportedOperationException .
O clas ă derivat ă nu mo ștenește constructorii superclasei. Dacă nu exist ă nici un constructor definit explicit
într-o subclas ă atunci se genereaz ă automat un constructor care apeleaz ă un constructor f ără argumente din
superclas ă (dacă exist ă, altfel este eroare de compilare) . Regula anterioar ă nu se aplic ă dacă exist ă un
constructor cu argumente în subclas ă, caz în care apelul (super)constructorului f ără argum ente trebuie scris
explicit.
In clasa SortedArray nu apare explicit un constructor dar compilatorul Java generează automat un
constructor fără argumente care apelează constructorul fără argumente din superclasa ArrayList . Dacă vrem
să avem și alți constr uctori în clasa derivată atunci trebuie să definim explicit to ți ace ști constructori.
Exemplu:

Un constructor dintr -o subclasa D poate apela un constructor din superclasa A folosind cuvântul cheie super
și nu prin numele sau. Apelul super() trebuie s ă fie prima instruc țiune din constructor. Aceast ă situa ție
apare atunci c ând subclasa are variabile în plus fa ță de superclas ă; inițializarea variabilelor mo ștenite se face
prin constructorul superclasei iar ini țializarea variabilelor proprii subclasei se face în constructorul subclasei .
Exemplu:

public class SortedArray extends ArrayList {
// constructori
public SortedArray() { super();}
public SortedArray(int n) { super(n);}
// metode
. . .
public class SortedArray extends ArrayList {
private Comparato r cmp=null; // obiect comparator folosit la sortare
// constructori
public SortedArray() { super();}
public SortedArray (Comparator comp) {
super(); cmp=comp;
}
// metode
public int indexOf (Object obj) {
if (cmp==null)
return Collections.binarySearch (this,obj);
else
return Collections.binarySearch (this,obj,cmp);
}
. . .
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 44 – IE.03.5 Derivare pentru crearea unor ierarhii de tipuri
Definirea unei noi clase este echivalent ă cu definirea unui nou tip de date, care poate fi folosit în declararea
unor variabile, argumente sau fun cții de tipul respectiv. Prin derivare se creeaz ă subtipuri de date compatibile
cu tipul din care provin (compatibile la atribuire) . Totu și nu orice ierarhie de clase este folosită și ca ierarhie
de tipuri; de exemplu, o mul țime vector nu este folosită ca un subtip de vector, chiar dacă clasa pentru
mulțimi este derivată din clasa pentru vectori.
Tipurile superclas ă și subclas ă sunt compatibile, în sensul c ă o variabil ă (sau un parametru) de un tip A poate
fi înlocuit f ără conversie explicit ă cu o variabi lă (cu un parametru) de un subtip D, iar trecerea de la o
subclas ă D la o superclas ă A se poate face prin conversie explicit ă (cast). Conversia de tip între clase
incompatibile produce excep ția ClassCastException .

Conversia de la un subtip la supertip (upcast ) se face automat, f ără a folosi operatorul de conversie, deoarece
se pot folosi toate metodele din supertip și în subtip. Exemplu:
Vector v= new Vector;
Object obj = v;

Conversia de la un supertip la un subtip ( downcast ) se face folosind operatorul de for țare a tipului ( cast),
deoarece subtipul poate avea metode în plus fat ă de supertip. Exemplu:
Object obj;
Vector v = (Vector) obj;

Conversia de tip se aplic ă unor variabile referin ță (sau unor argumente de fu ncții) și nu unor obiecte. Tipul
variabilei referin ță este folosit de compilator pentru a verifica dac ă metodele folosite cu aceast ă variabil ă
exist ă pentru tipul respectiv sau nu. O variabil ă de tip Object nu poate fi urmat ă de o metod ă din clasa
Vector (de ex. size() ) chiar dac ă obiectul referit de variabil ă este de tip Vector .
Anumite conversii de tip nu pot fi verificate la compilare iar erorile de conversie apar la execu ție, ca excep ții
ClassCastException . Exemple:
Object x = new String() ;
Object y = new Vector() ;
Vector v = (Vector) x; // exceptie la executie : un String nu poate fi transformat în vector
int sz= y.size(); // eroare la compilare : clasa Object nu are metoda size()

Prin derivare succesiv ă se pot crea ierarhii de tipuri compatibile. Exemplu:
// vector ordonat
public class SVec extends Vec { …. }
// multime vector
public class VSet extends Vec { … }

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 45 – // multime ordonata
public class SVSet extends VSet {… }

De remarcat c ă două subtipuri ale unui tip comun A nu sunt compatibile ( de ex emplu tipurile SVec și VSet
nu sunt compatibile, de și ambele sunt derivate din tipul Vec). Trecerea între cele dou ă tipuri nu se poate face
prin operatorul de conversie, dar se poate face prin construirea altui obiect.
Uneori subclasele nu adaugă nimic superclasei dar motivul extinderii este crearea unei familii de tipuri
compatibile . Un exemplu este familia claselor excep ție, derivate direct din clasa Exception sau din subclasa
RuntimeException . Instruc țiunea throw trebuie s ă conțină un obiect de un tip compatibil cu tipul
Exception ; de obicei un subtip ( IOException , NullPointerException s.a.). O subclas ă a clasei Exception
nu adaug ă metode noi și con ține doar constructori:

Ierarhia claselor excep ție permite tratarea individual ă a unor excep ții de I/E (sfâr șit de fi șier, fi șier inexistent,
eroare de cirire/scriere) sau tratarea lor colectiv ă, folosind tipul mai general IOException :

Exemplu de tratare individual ă a diferitelor excep ții de I/E:

public class IOException extends Exception {
public IOException() { super(); }
public IOException(String s) { super(s); } // s= un mesaj suplimentar
}
public class EOFException extends IOException {
public EOFException() { super(); }
public EOFException(String s) { super(s); } // s= un mesaj suplimentar
}

public static void main (String[] arg) { // o sing ura actiune ptr toate exceptiile
RandomAccessFile f=null;
try {
f= new RandomAccessFile("date.bin ","r");
while ( true)
System.out.println ( f.readInt());
} catch (IOException e) { e.printStackTrace();} // ptr toa te exceptiile de I/E
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 46 –

IE.03.6 Polimorfism
O func ție polimorfic ă este o func ție care are acelasi prototip, dar implement ări (defini ții) diferite în clase
diferite dintr -o ierarhie de clase (obținute prin suprascrierea metodei) .
Metode le equals() și toString() sunt exemple tipice de func ții polimorfice, al c ăror efect depinde de tipul
obiectului pentru care sunt apelate (altul decât tipul variabilei referin ță). Exemple:
Object d = new Date(); Object f = new Float (3.14);
String ds = d.toString(); // apel Date.toString()
String fs = f.toString(); // apel Float.toString()

In definirea clasei Entry polimorfismul explic ă de ce în met oda equals() nu este un apel recursiv; ceea ce se
apelează este metoda equals() din clasa de care apar ține cheia (diferită de clasa Entry ):
public boolean equals (Object obj) { // equals din clasa Entry
Entry e = (Entry)obj;
return key.equals (e.key); // equals din clasa variabilei ke y
}

Asocierea unui apel de metod ă cu func ția ce trebui e apelată se numeste "legare" ( Binding ). Legarea se poate
face la compilare (―legare timpurie‖) sau la execu ție ("legare târzie" sau "legare dinamic ă"). Pentru
metodele finale și statice legarea se face la compilare, adic ă un apel de metod ă este tradus într -o instruc țiune
de salt care con ține adresa func ției apelate. Legarea dinamic ă are loc în Java pentru orice metod ă care nu
are atributul final sau static , metod ă numit ă polimorfic ă. In Java majo ritatea metodelor sunt polimorfice.
Metodele polimorfice Java corespund func țiilor virtuale din C++.
Figura următoare ilustrează mecanismul de realizare a polimorfismului pe traseul :
variabilă referin ță – obiect – tabel de metode al clasei – defini ție me todă polimorfică în clasa respectivă. public static void main (String[] arg) {
RandomAccessFile f=null;
try {
f= new RandomAccessFile("date.bin","r");
while ( true)
System.out.println ( f.readInt());
}
catch (FileNotFoundException e) { S ystem.out.println ("Fisier negasit"); }
catch (EOFException e) { }
catch (IOException e) { System.out.println("Eroare la citire fisier"); }
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 47 –

Fiecare obiect Java con ține, pe lâng ă datele nestatice din clas ă, un pointer la tabela de metode virtuale
(polimorfice). Compilatorul traduce un apel de metod ă polimorfic ă printr -o instruc țiune de salt la o adres ă
calculat ă în func ție de t ipul obiectului adresat de variabil a referin ță și de defini ția clasei.
Exemplu de secven țã pentru explicarea mecanismului:
Vec v = new Vec(); v.add(“1”);
VSet vs = new VSet(); vs.add (“1”); String s = vs.toString();

Apel ul v.add() pleac ă de la obiectul referit de variabila " v" la tabela metodelor clasei Vec și de acolo ajunge
la corpul metodei add() din clasa Vec. Apelul vs.toString() conduce la metoda toString() din clasa VSet și de
acolo la metoda toString() din super clasa Vec deoarece este o metod ă moștenită și care nu a fost redefinit ă.
Apelul vs.equals(v) merge în sus pe ierahia de clase și ajunge la defini ția din clasa Object , deoarece nici o
subclas ă a clasei Object nu redefine ște metoda equals() , în acest exempl u.
Alegerea automat ă a variantei potrivite a unei metode polimorfice se poate face succesiv, pe mai multe
niveluri. De exemplu, apelul metodei toString() pentru un obiect colec ție alege func ția ce corespunde tipului
de colec ție (vector, lista înl ăntuită, etc); la rândul ei metoda toString() dintr -o clas ă vector apeleaz ă metoda
toString() a obiectelor din colec ție, în func ție de tipul acestor obiecte ( String, Integer, etc.).
Soluția func țiilor polimorfice este specific ă program ării orientate pe obiecte și se bazeaz ă pe existen ța unei
ierarhii de clase, care con țin metode (nestatice) cu aceea și semnatur ă, dar cu efecte diferite în clase diferite.
Numai metodele obiectelor pot fi polimorfice, în sensul c ă selectarea metodei apelate se face în func ție de
tipul o biectului pentru care se apeleaz ă metoda. O metod ă static ă, chiar dac ă este redefinit ă în subclase, nu
poate fi selectat ă astfel, datorit ă modului de apelare.
Exemplul următor este o simplificare a polimorfismului din familiile de clase AWT sau Swing: toat e
componentele de interfa ță grafică AWT sunt derivate din clasa Component și au o metodă paint() pentru

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 48 – afișarea pe ecran a componentei; clasa Container este o componentă care poate con ține mai multe alte
componente. Simplificarea aici se referă la efectul metodei polimorfice paint() , care scrie un mesaj în loc să
deseneze componenta, dar efectul ei depinde de tipul obiectului pentru care se apelează.

IE.03.7 Delegarea ca alternativ ă la derivare pentru reutilizare
Reutilizarea func ționalit ătii unei clase A se poate face în două moduri:
– Prin derivare din clasa A, cu mo ștenirea datelor si metodelor publice;
– Prin delegare (compunere), cu apelarea metodelor clasei A.
Delegare sau compunere este atunci când o clasă D conține o variabil ă (delegat ) de un tip A iar o parte din
metodele clasei D apeleaz ă metode din clasa A prin intermediul variabilei delegat. Exemplu de clas ă pentru
mulțimi care deleag ă opera ții către clasa Vector :

abstract class Component { public abstract void paint (); }

class Container extends Component {
private Vector v;
public Container(){v= new Vector();}
public void add (Component d){v.add(d);}
public void paint (){
for (int i=0;i<v.size();i++)
((Component)v.get(i)).paint();
}
}
class SquareButton extends Compone nt {
private int x1,y1,x2,y2;
public SquareButton (int a1,int a2,int b1,int b2) { x1=a1;x2=a2;y1=b1;y2=b2; }
public void paint () { System.out.println ("Square ("+x1+","+x2+","+y1+","+y2+")"); }
}
class RoundButton extends Component {
private int xc,yc,rc;
public RoundButton (int x,int y,int r){ xc=x;yc=y;rc=r; }
public void paint () { System.out.println ("Round ("+xc+","+yc+","+rc+")"); }
}
class A {
public static void main (String [] a) {
Container c1 = new Container ();
c1.add (new SquareButton(1,1,5,5)); c1.add (new RoundButton(3,3,6));
Container c2 = new Container();
c2.add (new SquareButton(7,7,9,9)); c2.add (new SquareButton(6,6,8,8));
c2.add(c1); c2.paint ();
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 49 –

Sursa clasei anterioare poate fi mai mare dac ă vrem pen tru mul țimi și alte opera ții existente în clasa Vector .
Delegarea creeaz ă o rela ție de tipul ― D conține un A‖ ( D has an A ) și se recomand ă atunci c ând D folose ște
puține metode din A sau când se folosesc subtipuri ale tipului A.
Derivarea conduce la un c od mai compact pentru noua clas ă (nu se mai declar ă metodele mo ștenite) și
creeaz ă tipuri compatibile. Rela ția dintre clase derivate este o rela ție static ă stabilit ă la compilare.
Derivarea creeaz ă o rela ție de tipul ― D este un fel de A‖ (D is a kind of A) și se recomand ă atunci c ând noua
clasă D folose ște o mare parte a metodelor din A (și adaug ă sau modific ă numai c âteva metode) sau atunci
când tipul D trebuie s ă fie compatibil cu tipul A.
Alegerea între cele două tipuri de rela ții (is a sau has a ) poate fi discutabil ă și subiectiv ă: ce este o stiv ă?
Este un caz particular de vector (list ă) sau este un obiect care con ține un vector (o listă) ? Metodele clasei
stivă au nume consacrate și diferite de numele metodelor din clasele ArrayList (Vector ) sau LinkedList .
Clasele Java pentru stive sunt: java.util.Stack care este derivat ă din clasa Vector și clasa mai nou ă
java.util.ArrayDeque care con ține un vector, deci se folosesc ambele metode de reutilizare.
Pentru compara ție urmeaz ă trei variante de realiza re a unei stive ca list ă înlănțuită: prin derivare, prin
delegare și prin derivare plus delegare.
Exemplu de clasa stiv ă obținută prin derivare din clasa LinkedList :

class StackList extends LinkedList {
public void push (Object obj) {
addFirst (obj); // super.addFirst(obj);
}
public Object pop () {
return removeFirst(); / / super.removeFirst ();
}
public Object peek () {
return peekFirst(); // super.peekFirst();
}
public boolean empty() {
return size()==0; // super.size()
}
// toString() este mostenita si poate fi folosita ptr afisare stiva
}
class VSet {
private Vector v; // variabila delegat
public VSet () { v= new Vector(); }
public boolean add (Object obj) {
if ( v.contains(obj))
return false; // nu s -a modificat multimea
v.addElement (obj);
return true;
}
public String toStrin g () { return v.toString(); }
public int size() { return v.size();}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 50 – Exemplu de clas ă stivă obținută prin delegare din clasa LinkedList (pentru sti ve liste înl ănțuite):

Delegarea permite mai mult ă libertate în alegerea obiectului folosit ca delegat; acest obiect este primit prin
constructor și poate avea diferite tipuri compatibile cu variabila delegat. Rela ția dintre clasele D și A se
poate modifica dinamic, la execu ție. Exemplul urm ător foloseste derivare și delegare pentru o stiv ă
neprecizat ă ca implementare la definirea clasei; Clasa StackList extinde pe AbstractList și con ține o
variabil ă (delegat) de tip AbstractList . Stabilirea tipu lui de colec ție folosit ca stiv ă (ArrayList sau
LinkedList ) se face la construirea unui obiect:
StackList st = new StackList ( new ArrayList()); // sau (new LinkedList())

IE.03.8 Mo ștenire multipl ă prin derivare și delegare
Deriva rea și delegarea se pot combina pentru a realiza un fel de moștenire multipl ă: clasa D este derivat ă din
A și con ține o variabil ă delegat de tip B. Prin mo ștenire se preiau metode din A iar prin apelare se pot folosi
metode din B pentru obiecte de tip D. Exemplu: class StackList {
private LinkedList stack; // variabila delegat
public StackList (LinkedList list) { stack=list; }
// delegare de operatii catre LinkedList
public Object push (Object obj) {
stack.add (0,obj); return obj;
}
public Object po p () {
Object obj= stack.get(0); stack.remove(obj);
return obj;
}
public Object peek () { return stack.get(0); }
public boolean empty() { return stack.size()==0; }
public String toString() { return stack.toString(); }
}

class St ackList extends AbstractList {
private AbstractList stack; // delegat clasa abstracta
public StackList (AbstractList list) { stack=list; } // concretizare delegat
public Object push (Object obj) {
stack.add (0,obj);
return obj;
}
public Object pop () {
Object obj= get(0);
stack.remove(obj);
return obj;
}
public Object peek() {
return get(0);
}
public boolean empty() { return stack.isEmpty(); }
public int size() { return sta ck.size(); } // abstracta in AbstractList
public Object get (int i) { return stack.get(i); } // abstracta in AbstractList
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 51 –

Această soluție de mo ștenire multipl ă este folosit ă în câteva clase JFC (Swing) de tip ―model‖; de exemplu,
clasa DefaultListModel preia prin mo ștenire metode de la superclasa AbstractListModel și deleag ă unei
variabile interne de tip Vector opera ții cu un vector de obiecte. Un model de list ă este un vector cu
posibilit ăți de generare evenimente (de apelare ascultători la evenimente ) la modific ări operate în vector. In
general o clasă ―model‖ din JFC este o colec ție care poate avea asc ultători și poate apela metode din
obiectele ascultător (generează evenimente semnificative pentru ascultători).
Urmeaz ă un exemplu mai simplu : o mul țime realizat ă ca vector, care extinde clasa AbstractSet și con ține o
variabil ă de tip ArrayList . Clasa pre ia de la ArrayList metodele add(), iterator() și size() și de la
AbstractSet alte metode definite în functie de iterator() și size() : contains() ,toString(),remove() ș.a.

IE.03.9 Clase Java de I/E cu delegare și derivare
Combinarea deriv ării cu d elegarea a fost folosit ă la proiectarea claselor din pachetul java.io . Exist ă două
familii de clase paralele : familia claselor ―flux‖ ( Stream ) cu citire -scriere de octe ți și familia claselor
Reader -Writer , cu citire -scriere de caractere. Clasele Reade r, Writer și celelalte sunt abstracte. Toate class A {
public void f1 () { System.out.println ("A.f1"); }
}
class B {
public void f2 () { Syst em.out.println ("B.f2"); }
}
class D extends A {
private B b = new B ();
public void f2 () { b.f2();} // delegare obiect b pentru operatia f2
}
class X {
public static void main (String arg[]) {
D m = new D();
d.f1(); // metoda moste nita de la A
d.f2(); // metoda din clasa B
}
}

public class ArraySet extends AbstractSet {
private ArrayList set;
public ArraySet() {
set = new ArrayList();
}
public boolean add (Object obj) {
if (! contains(obj) )
return set.add(obj); // delegare pentru operatia de adaugare
return false;
}
public Iterator iterator() {
return set.iterator(); // delegare pentru creare obiect iterator
}
public int size() { return set.size(); } // delegare metoda size()
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 52 – clasele flux de intrare sunt subtipuri ale tipului InputStream (Reader ) și toate clasele flux de ie șire sunt
subtipuri ale tipului OutputStream (Writer ).
Numărul de clase instan țiabile de I/E este relativ mare deoarece sunt posibile diverse combina ții între
suportul fizic al fluxului de date și facilită țile oferite de fluxul respectiv. După suportul fizic al fluxului de
date se poate alege între: fisiere disc ( FileInputStream , FileOutputStream , FileReader, Fil eWriter s.a.),
vector de octe ți sau de caractere (ByteArrayInputStream, ByteArrayOutputStream , CharArrayReader,
CharArrayWriter ), buffer de șiruri în memorie (StringBufferInputStream, StringBufferOutputStream ),
canal pentru comunicarea sincronizat ă între f ire de execu ție (PipedReader, PipedWriter ,
PipedInputStream, PipedOutputStream ).
După facilită țile oferite avem de ales între: citire -scriere la nivel de octet sau bloc de octe ți (metode read(),
write() ), citire -scriere pe octe ți dar cu zonă buffer ( Buffe redInputStream, BufferedReader,
BufferedOutputStream, BufferedWriter), citire -scriere la nivel de linie și pentru numere de diferite tipuri,
fără conversie ( DataInputStream, DataOutputStream), c itire cu punere înapoi în flux a ultimului octet
citit ( PushB ackInputStream ), citire înso țită de numerotare automată a liniilor citite
(LineNumberInputStream ).
Combinarea celor 8 clase surs ă/destina ție cu op țiunile de prelucrare asociate transferului de date se face prin
intermediul claselor ―anvelop ă‖ , care sunt n umite și clase ―filtru‖ de intrare -ieșire. Dac ă s-ar fi utilizat
derivarea pentru ob ținerea claselor direct utilizabile atunci ar fi trebuit generate, prin derivare, combina ții ale
celor 8 clase cu cele 4 op țiuni, deci 32 de clase (practic, mai pu ține, deo arece unele op țiuni nu au sens pentru
orice flux). Pentru a folosi mai multe op țiuni cu acelasi flux ar fi trebuit mai multe niveluri de derivare și
deci ar fi rezultat un num ăr și mai mare de clase.
Clasele de tip filtru sunt clase intermediare, din care sunt derivate clase care adaug ă opera ții specifice (de
―prelucrare‖): citire de linii de text de lungime variabil ă, citire -scriere de numere în format intern, scriere
numere cu conversie de format ș.a. O clas ă anvelop ă de I/E con ține o variabil ă de tipul abstract
OutputStream sau InputStream , care va fi înlocuit ă cu o variabil ă de un tip flux concret
(FileOutputStream , …), la construirea unui obiect de un tip flux direct utilizabil.
Clasele filtru de I/E fac parte din categoria clasele ―decorator‖, care aplic ă diverse ―decora țiuni‖
(func ționalit ăți) unor clase existente. Un decorator nu adaug ă funcții noi clasei decorate dar modific ă
acțiunea unor metode existente prin delegare c ătre metode ce provin din obiectul transmis ca argument la
construirea unui obiect din clasa decorator ( și care înlocuie ște o variabil ă de tip interfa ță din clasa decorator).
Clasa FilterInputStream este derivat ă din InputStream și con ține o variabil ă de tip InputStream :

Metoda read() este o metod ă polimorfic ă, iar selec tarea metodei necesare se face în func ție de tipul concret
al variabilei "in" (transmis ca argument constructorului). Metoda read() din FilterInputStream preia public class FilterInputStream extends InputStream {
protected InputStream in;
protected FilterInputStream (InputStream in) { // constructor
this.in=in; // adresa obiectului "flux"
}
// citirea unui octet
public int read () throws IOException {
return in.read (); // citirea depinde de tipul fluxului
}
… // alte metode
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 53 – funcționalitatea metodei read() din clasa delegat, sau ―deleag ă‖ opera ția de citire c ătre clasa folosit ă la
construirea obiectului filtru.
Nu se pot crea obiecte de tipul FilterInputStream deoarece constructorul clasei este protected , dar se pot
crea obiecte din subclase ale clasei FilterInputStream : DataInputStream , BufferedInputStream ,
PushbackInpu tStream, LineNumberInputStream . Cea mai folosit ă este clasa DataInputStream care
adaug ă metodelor de citire de octe ți mo ștenite și metode de citire a tuturor tipurilor primitive de date:
readInt(), readBoolean(), readFloat(), readLine() , etc.
La crearea u nui obiect de tipul DataInputStream constructorul prime ște un argument de tipul InputStream
(sau un tip derivat direct din InputStream sau din FilterInputStream ) prin care se specific ă suportul fizic
al datelor și modul concret de citire din flux. Pentru c itire linii dintr -un fi șier disc metoda readLine() deleaga
citirea de octe ți către metoda read() din FileInputStream :
FileInputStream fis= new FileInputStream (“t.txt”);
DataInputStream dis = new DataInputStream (fis);

Decorarea unei clase flu x de I/E se poate face repetat; astfel pentru a citi linii dintr -un fi șier folosind o zon ă
tampon și numerotare de linii vom folosi urm ătoarea secven ță de instruc țiuni:

De obicei nu se mai folosesc variabile intermediare la construirea unui obiect flux. Exemplu de citire linii cu
buffer, dintr -un fișier disc:

Ordinea în care sunt create obiectele de tip InputStream este important ă : ultimul obiect trebuie s ă fie de
tipul DataInputStream , pentru a putea folosi metode ca readLine() și altele. Familia claselor Reader –
Writer folose ște de asemenea clase care combină derivarea cu delegarea.

public static void main (String arg[]) throws IOException {
FileInputStream fis= new FileInputSt ream (arg[0]);
BufferedInputStream bis = new BufferedInputStream (fis);
LineNumberInputStream lnis= new LineNumberInputStream (bis);
DataInputStream dis = new DataInputStream (lnis);
String linie;
while ( (linie=dis.readLine()) != null )
System.out.println (lnis.getLineNumber()+" "+linie);
}

public static void main (String arg[ ]) throws IOException {
DataInputStream dis = new DataInputStream (
new BufferedInputStream (new FileInputStream (arg [0])));
String linie;
while ( (linie=dis.readLine()) != null)
System.out.println ( linie);
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 54 – Capitolul IE.04. Clase abstracte și interfe țe
Cuvinte cheie
Clas ă abstract ă, Metod ă abstract ă, Interfa ță,Func ție callback
Interfe țele Comparable,Comparator, Filter
Interfe țele Enumerator, Iterator

IE.04.1 Clase abstracte în Java
Generalizarea și abstractizarea în programarea cu obiecte sunt realizate și prin clase abstracte. O superclas ă
define ște un tip mai general decât tipurile definite prin subclasele sal e. Uneori superclasa este atât de
general ă încât nu poate preciza nici variabile și nici implement ări de metode, dar poate specifica ce opera ții
(metode) ar fi necesare pentru toate subclasele sale. In astfel de cazuri superclasa Java este fie o clas ă
abstractă, fie o interfa ță. Exemplu din JDK: tipul (clasa) Number este o generalizare a tipurilor clas ă
numerice ( Double, Float, Integer, Short, Byte ).

O clas ă care con ține cel pu țin o metod ă abstract ă trebuie declarat ă ca abstract ă, dar nu este ob ligatoriu ca o
clasă abstract ă să conțină și metode abstracte. O clas ă abstract ă poate con ține date și metode neabstracte
utilizabile în subclase dar nu este instan țiabilă (nu se pot crea obiecte de acest tip clas ă). Constructorul clasei
abstracte este de obicei protected și nu public .
O metod ă abstract ă este doar declarat ă (ca nume, tip și argumente) dar nu este definit ă; ea este precedat ă de
cuvântul cheie abstract . Ea urmeaz ă a fi definit ă într-o subclas ă a clasei (interfe ței) care o con ține. Metodele
abstracte pot să apară numai în interfe țe și în clasele declarate abstracte.
Scopul unei clase abstracte nu este acela de a genera obiecte utilizabile, ci de a transmite anumite metode
comune pentru toate subclasele derivate din clasa abstract ă (metode impl ementate sau neimplementate).
Derivarea dintr -o clas ă abstract ă se face folosind cuvântul extends . O clas ă abstract ă poate face o
implementare par țială, deci poate con ține și metode neabstracte și/sau variabile în afara metodelor abstracte.
Pentru a obti ne clase instan țiabile dintr -o clas ă abstract ă trebuie implementate toate metodele abstract e
moștenite, respectând declara țiiile acestora ( ca tip și ca argumente).
O clas ă abstract ă constituie o baz ă de plecare pentru definirea altor clase. Exemplu:

public abstract class Number {
public abstract int intValue();
public abstract long long Value();
public abstract float floatValue();
publi c abstract double doubleValue();
public byte byteValue() { return (byte)intValue(); }
public short shortValue() { return (short)intValue(); }
}

public final class Integer extends Number {
private final int value;
public Integer(int value) { this.value = value; } // constructor
public Integer(String s) throws NumberFormatException { this.value = parseInt(s, 10); }
public static int parseInt(String s, int radix) throws NumberFormatException {. . . }
public static int parse Int(String s) throws NumberFormatException { return parseInt(s,10);
}
. . .
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 55 – O clas ă abstract ă este de multe ori o implementare par țială a unei interfe țe, pentru a simplifica definirea de
clase instan țiabile care să respecte interfa ța. In clasa abstractă mai multe metode sunt definite în func ție de
câteva metode abstracte (cum ar fi cea care produce un iterator). Clasele concrete care vor extinde clasa
abstracta vor avea de definit numai câteva metode Exemplu:

In bibliotecile de clase Java exist ă câteva clase adaptor, care implementeaz ă o interfa ță prin metode cu
definiție nul ă, unele redefinite în subclase. Ele sunt clase abstracte pentru a nu fi instan țiate direct, dar
metodele lor nu sunt abstracte, pentru c ă nu se știe care din ele sunt efectiv necesare în subclase. Exemplu
de clas ă adaptor util ă în definirea de clase ascult ător la evenimente generate de tastatur ă:

O subclas ă (care reac ționeaz ă la evenimente generate de taste) poate redefini numai una din aceste metode,
fără să se preocupe de celalte metode nefolosite de subclas ă:

Clasele Reader și Writer din pachetul java.io sunt clase generale de citire sau scriere din/în fluxuri
de caractere care au doar dou ă metode abstracte: read() sau write() și close() . Ele au un num ăr mare
de subclase care adaug ă și alte metode specifice lor: FileReader, Filt erReader, BufferedReader ,
StringReader, , etc. Fragment din clasa Reader :

public interface Collection { // declara metode prezente in orice colectie
int size(); // dimensiune colectie
boolean isEmpt y(); // verifica daca colectia este goala
… // alte metode
}
public abstract class AbstractCollection implements Collection {
public abstract int size(); // metoda abst racta
public boolean isEmpty ( return size()==0;} // metoda implementata

}

public abstract class KeyAdapter implements KeyListener {
public void keyTyped(KeyEvent e) { } // la apasare+ridicare tasta
public void k eyPressed(KeyEvent e) { } // numai la apasare tasta
public void keyReleased(KeyEvent e) { } // numai la ridicare tasta
}

class KListener extends KeyAdapter {
public void keyPressed(KeyEvent e) {
char ch = e.getKeyCha r(); // caracter generat de tasta apasata
. . . // folosire sau afisare caracter ch
}
}
public abstract class Reader {
protected Reader(); // constrc uctor
abstract void close(); // depinde de tipul fluxului
int read ( char[] buf); // apeleaza metoda abstracta
int read(); // citeste un caracter, apeleaza metoda abstracta
void reset (); // la fel ptr orice flux
…. // alte metode neabstracte
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 56 – IE.04.2 Interfe țe în Java
O interfa ță este o colec ție de metode abstracte și (eventual) defini ții de constante simbolice. Ea poate fi
considerată ca un caz particular de clasă abstract ă, ceea ce și este în C++ (unde nu exist ă noțiunea de
interfa ță ca în Java). Metodele declarate într-o interfa ță sunt implicit publice și abstracte . Din punct de
vedere s intactic, diferen țele dintre clase abstracte și interfe țe sunt:
– cuvinte cheie diferite la definire: abstract class, interface
– cuvinte diferite la definirea de subclase: extends, implements
Scopul interfe țelor este de a defini un tip compatibil simultan cu mai multe tipuri existente, dar f ără riscul de
moștenire multi plă. Se mai spune că o interfa ță stabile ște un ―contract‖ care trebuie respectat de clasele care
implementează interfa ța, în sensul că aceste clase se obligă să definească toate metodele din interfa ță (la care
se mai pot adăuga și alte metode publice).
Utilizarea unei interfe țe comune mai multor clase permite unificarea metodelor și modului de utilizare a unor
clase cu acela și rol, dar și scrierea unor metode general aplicabile oric ărei clase care respect ă interfa ța
comun ă. Exemplu de interfa ță din JDK:

Aceast ă interfa ță este respectat ă ( implementat ă ) de clase ca String, StringBuffer, StringBuilder,
Segment, CharBuffer. Exemplu:

Prin definirea unei interfe țe sau clase abstracte se creeaz ă un tip comun mai multor clase deoarece o variabi lă
(sau un argument) de un tip interfa ță poate fi înlocuit ă fără conversie explicit ă cu o variabil ă de orice subtip,
deci cu referin țe la obiecte care implementeaz ă interfa ța sau care extind clasa abstract ă.
Nu pot fi create obiecte de un tip interfa ță sau clas ă abstract ă, dar se pot declara variabile, argumente formale
și func ții de un tip interfa ță sau clas ă abstract ă. Astfel de variabile vor fi înlocuite (prin atribuire sau prin
argumente efective) cu variabile de un tip clas ă care implementeaz ă interfa ța respectiv ă. Exemplu care
folose ște interfa ța Collection pentrua compara la egalitate două colec ții de obiecte:

public interface CharSequence { // orice secventa de caractere
char charAt (int i); // caracterul din pozitia i a secventei
int length() ; // lungimea secventei
String toString(); // sir echivalent secventei
CharSequence subSequence (int i, int j); // extrage o subsecventa
}
public final class String implements CharSequence, Serializable {
// date
private final char v alue[];
private final int count;
// constructori . . .
// metode
public int length() { return count; }
public String toString() { return this; }
. . .
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 57 –

O interfa ță poate extinde o alt ă interfa ță (extends ) cu noi metode abstracte. Ca exemplu, interfa ța List
extinde interfa ța Collection cu metode de acces direct, prin indici, la elementele colec ției:

IE.04.3 Compara ție între interfe țe și clase abstracte
Diferen ța major ă este aceea c ă o interfa ță nu poate con ține date și că o clas ă poate implementa simultan mai
multe interfe țe dar nu poate extinde dec ât o singur ă clasă abstract ă (sau neabstract ă).
Clasele care implementeaz ă o aceea și interfa ță pot fi foarte diverse și nu formeaz ă o ierarhie de tipuri
compatibile . Un exemplu sunt clasele cu obiecte comparabile, care toate implement ează interfa ța
Comparable : String, Date, Integer, BigDecimal , ș.a.
O clas ă poate simultan s ă extind ă o clas ă (alta decât clasa Object , implicit extins ă) și să implementeze una
sau mai multe interfe țe. Altfel spus, o clas ă poate mo șteni date și/sau metode d e la o singur ă clasă, dar poate
moșteni mai multe tipuri (poate respecta simultan mai multe interfe țe). De exemplu, mai multe clase
predefinite SDK, cu date, implementeaz ă simultan interfe țele Comparable (obiectele lor pot fi comparate la
mai mic / mai mar e), Clonable (obiectele lor pot fi copiate), Serializable (obiectele lor pot fi salvate sau
serializate în fi șiere disc sau pe alt mediu extern). Exemple:
public class String implements Comparable, Serializable { …}
public class Date implements S erializable, Clonable, Comparable { …}

O interfa ță fără nici o metodă poate fi folosită pentru a permite verificarea utilizării unor metode numai în
anumite clase, în faza de execu ție. Un exemplu tipic este interfa ța Clonable , definită astfel:
public interface Clonable { }
Clasa Object conține metoda clone() , folosit ă numai de clasele care declar ă că implementeaz ă interfa ța
Clonable . Metoda neabstract ă clone() este mo ștenită automat de toate clasele Java, dar este aplicabil ă numai
pentru o parte din clase. Pentru a semnala utilizarea gre șită a metodei clone() pentru obiecte ne -clonabile, se
produce o excep ție de tip CloneNotSupportedException atunci când ea este apelat ă pentru obiecte din clase
care nu ader ă la interfa ța Clonable .
O utilizare ase mănătoare o are interfa ța Serializable , pentru a distinge clasele ale c ăror obiecte sunt
serializabile (care con țin metode de salvare și de restaurare în / din fi șiere) de clasele ale c ăror obiecte nu pot
fi serializate ( f ără obiecte "persistente"). Pract ic, toate clasele cu date sunt serializabile.
Interfe țe ca Serializable si Clonable se numesc interfe țe de "marcare" a unui grup de clase ( tagging
interfaces ), pentru a permite anumite verific ări. public static boolean equals (Collection a, Collection b) {
if (a.size() != b.size()) r eturn false; // size() este metoda din interfa ța Collection
returns a.containsAll(b); // containsAll() este metoda din interfa ța Collection
}
public interface List extends Collection {
void add ( int i, Object x); // introduce pe x in pozit ia i din colectie
Object get (int i); // valoare element din pozitia i
Object set (int i, Object x); // înlocuire element din pozitia i cu x
Object remove (int i); // elimina si returneaza elementul din pozitia i
. . .
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 58 – O interfa ță care stabileste un tip comun poate fi atât de g eneral ă încât s ă nu con țină nici o metod ă. Un
exemplu este interfa ța EventListener (pachetul "ja va.util"), care stabile ște tipul ―ascult ător la evenimente‖,
dar metodele de tratare a evenimentului nu pot fi precizate nici ca prototip, deoarece depind de ti pul
evenimentului. Interfa ța este extins ă de alte interfe țe, specifice anumitor ascult ători (pentru anumite
evenimente):

O clas ă cu toate metodele abstracte și fără date poate fi definit ă fie ca o clas ă abstract ă, fie ca o interfa ță. In
Java 1 clas a abstract ă java.util.Dictionary conținea metode obligatorii pentru orice obiect dic ționar, dar a
căror definire depinde de implementare. In Java 2 a fost introdus ă, ca alternativ ă preferabil ă, interfa ța Map
care con ține metodele din clasa Dictionary și alte metode abstracte.
Interfe țele sunt preferabile în general claselor abstracte, pentru c ă oferă mai mult ă libert ate subclaselor.
Avantajul solu ției interfa ță în raport cu o clas ă abstract ă este evident atunci când definim un dic ționar
realizat ca un vecto r de perechi cheie -valoare: o clas ă derivat ă din clasa ArrayMap ar putea mo șteni metode
utile de la superclas ă, dar nu ar putea extinde și clasa abstract ă Dictionary . Exemplu:
public class ArrayMap extends ArrayMap implements Map { … }

Interfe țele si clasele abstracte nu trebuie opuse, iar uneori sunt folosite împreun ă într-un framework cum este
cel al claselor colec ție sau cel al claselor Swing (JFC): interfa ța define ște metodele ce ar trebui să existe mai
multe clase, iar clasa abstrac tă este o implementare par țială a interfetei, pentru a facilita definirea de noi clase
prin extinderea clasei abstracte. In felul acesta se ob ține o familie de clase deschis ă pentru extinderi
ulterioare cu clase compatibile, care nu trebuie definite îns ă de la zero. Un exemplu este interfa ța Map și
clasa AbstractMap care permit definirea rapid ă de noi clase pentru dicționare (altele decât HashMap și
TreeMap ), compatibile cu interfa ța Map dar care beneficiaz ă de metode mo ștenite de la clasa abstract ă.
Exemplu de dic ționar realizat ca vector:

public interface ActionListener extends EventListener {
public void actionPerformed(ActionEvent e);
}
public interface ItemListener extends EventListener {
public void itemStateChanged(ItemEvent e);
}
class ArrayMap extends A bstractMap { // dictionar vector de perechi
private ArrayList entries ; // perechi cheie -valoare
public ArrayMap (int n) {entries = new ArrayList(n); }
public Object put ( Object key, Object value) {
for (SimpleEntry e: entries) // clasa Sim pleEntry inclusa în AbstractMap
if (e.getKey().equals(key)){ // daca exista cheia in dictionar
Object v = e.getValue(); // pentru rezultatul metodei
entries.remove(e); // inlocuire valoare prin eliminare
entries.ad d (new SimpleEntry(key,value)); // si adaugare
return v;
}
entries.add (new SimpleEntry(key,value)); // daca nu exista cheia in dictionar
return null;
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 59 –

In general se recomand ă programarea la nivel de interfa ță și nu la nivel de clas ă particular ă, pentru a avea
programe mai generale și mai u șor de modificat. Ideea este de folosi variabile de un tip c ât mai general, care
se pot referi la obiecte de orice subtip compatibil cu acesta. Mai mult, modificarea tipului obiectului referit se
poate face la execu ție. Un exemplu este o variabil ă mulțime sau dic ționar care va definit ă de tipul Set sau
Map, pentru ca ulterior (la atribuirea unei referin țe) să se poata preciza sau modifica tipul concret de
mulțime sau de dic ționar ( HashSet, TreeSet, HashMap, TreeMap , etc.) Exemplu:
Map d = new HashMap(); // se poate înlocui cu: d= new Ar rayMap();

IE.04.4 Interfe țe Java pentru compararea de obiecte
In unele opera ții cu vectori de obiecte (sortare, căutare binară, determinare maxim sau minim ș.a.) este
necesar ă compararea de obiecte (de acela și tip). Func ția de comparare depinde de tipul obiectelor comparate,
dar poate fi o func ție polimorfic ă, cu acela și nume, tip și argumente dar cu defini ții diferite. In plus, dou ă
obiecte pot fi comparate dup ă mai multe criterii (criteriile sunt variabile sau combina ții de variabile din
aceste obiecte) .
In Java, metoda compareTo() este destinat ă compara ției dupa criteriul cel mai ―natural‖ (cel mai frecvent iar
uneori și unicul criteriu). Pot fi comparate cu metoda compareTo() numai clasele care implementeaz ă
interfa ța Comparable și deci care definesc metoda abstract ă compareTo() :

Clasele care declar ă implementarea acestei interfe țe trebuie s ă conțină o defini ție pentru metoda
compareTo() , cu argument de tip Object . Exemple de clase Java cu obiecte comparabile : Integer, Float,
String, Date, BigDe cimal ș.a. Exemplu:

Metoda compareTo() se poate aplica numai unor obiecte de tip Comparable și de aceea se face o conversie
de la tipul general Object la subtipul Comparable . Exemplu: public interface Comparable {
int compareTo (Object obj); // rezultat <0 sau = 0 sau >0
}
public class Byte extends Number implements Comparable {
private byte value;
. . . // constructori, metode specifice
public int compareTo( Object obj) { // compara doua obiecte Byte
return this.val ue- ((Byte) obj).value ;
}
} public Set entrySet () { // abstracta in Ab stractMap
LinkedHashSet set = new LinkedHashSet();
for (int i=0;i<keys.size();i++)
set.add(new Simple Entry(keys.get(i), values.get(i)) );
return set;
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 60 –

Mai multe metode statice din clasele Arrays si Collect ions (sort(),max(),binarySearch() ), care au ca
argument doar vectorul sau colec ția, folosesc implicit metoda compareTo() .
Interfa ța Comparator a fost introdusă p entru comparare de obiecte dup ă alte criterii decât cel ―natural‖ ; ea
conține în principal met oda compare( ), cu dou ă argumente :

Functia Arrays.sort() are și o form ă cu dou ă argumente; al doilea argument este de tipul Comparator și
precizeaz ă funcția de comparare (alta decât compareTo() ). Func ția care determin ă maximul dintr -un vector
de obie cte poate fi scris ă și astfel:

Func ția max() cu dou ă argumente este mai general ă și poate fi folosit ă pentru a ordona un acela și vector
după diferite criterii (dup ă diverse propriet ăți). De exemplu, pentru ordonarea unui vector de șiruri dup ă
lungime a șirurilor, vom defini urm ătoarea clas ă comparator:

Func ția compare() din interfa ța Comparator poart ă numele de func ție callback , deoarece este apelat ă de
metode existente ( sort(), max() din clasele Arrays sau Collections ) dar trebuie furnizat ă de aplica ția care
folose ște una din aceste metode. La scrierea func ției de sortare nu se cuno ștea exact defini ția func ției de
comparare (pentru c ă pot fi folosite diverse func ții de comparare), dar s -a putut preciza prototipul func ției de
comparare, ca metod ă abstract ă dintr -o interfa ță. Putem deci s ă scriem o func ție care apeleaz ă funcții înc ă
nedefinite, dar care respect ă toate un prototip (tip rezultat, tip și num ăr de argumente). // determinare maxim dintr -un vector de obiecte oarecare
public static Object max (Object [ ] a) {
Comparable maxim = (Comparable) a[0];
for (int i=0;i<a.length;i++)
if ( maxim.compareTo(a[i] ) < 0) // daca maxim < a[i]
maxim=(Comparable)a[i];
return maxim;
}
public interface Comparator {
int compare (Object t1, Object t2); // implicit abstracta, ne -statica !
}
public static Object max (Object a[ ], C omparator c) {
Object maxim = a[0];
for (int k=1; k<a.length;k++)
if ( c.compare (maxim, a[k]) < 0) // c este un obiect comparator
maxim = a[k];
return maxim;
}
class LengthComparator implements Comparator {
public int compare (Object t1, Object t2) {
return ((String)t1).length() – ((String)t2).length();
}
}
. . . // ordonare dupa lungime
String * + a = ,”patru”,”trei”,”unu”-;
Arrays.sort( a, new LengthComparator());

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 61 – Tehnica callback este folosit ă la scrierea unor clase de bibliote că, ale c ăror func ții apeleaz ă funcții din
programele de aplica ții, scrise ulterior de utilizatorii pachetului. Ideea general ă este c ă o parte din algoritmul
implementat de clasa de bibliotec ă depinde de specificul aplica ției care foloseste acest algoritm. Un algoritm
de sortare foloseste o func ție de comparare, dar func ția de comparare face parte din aplica ție, pentru c ă ea
compar ă date din aplica ție. Situa ția poate fi schematizat ă astfel: o func ție A ( main() , de ex.) apeleaz ă o
funcție B ( sort() , de ex.) , iar B apeleaz ă o func ție X ( compare() ) dintr -un grup de func ții posibile. Adresa
funcției X este primit ă de B de la func ția A. De fiecare dat ă când A apeleaz ă pe B îi transmite și adresa
funcției X, care va fi apelat ă înapoi de B.
Aplicație Biblioteci JDK

IE.04.5 Interfe țe Java pentru enumerare
Interfa ța mai veche java.util. Enumeration conține dou ă metode comune oric ărei clase cu rol de enumerare
a elementelor unei colec ții de obiecte :

Enumera rea unor obiecte poate fi privit ă ca o alternativ ă la crearea unui vector cu obiectele respective, în
vederea prelucr ării succesive a unui grup de obiecte. Dintre clasele care implementeaz ă aceast ă interfa ță,
sunt clasele enumerator pe vector și enumerator pe un tabel de dispersie Hashtable . Ulterior s -au ad ăugat
metode din aceast ă interfa ță și clasei StringTokenizer , care face o enumerare a cuvintelor dintr -un text:

Exemplu de metod ă care calculeaz ă de câte ori apare un obiect într -o colec ție de ob iecte folosind numai
enumerator ul colec ției:

sort main
compare
public interface Enumeration {
boolean hasMoreElements(); // daca mai sunt elemente in colectie
Object nextElement(); // elementul urmator din colectie
}
public class StringTokenizer implements Enumeration {
. . .
public boolean hasMoreElements() { return hasMoreTokens(); }
public Object nextElement() { return nextToken(); }
}
public static int count (Enumeration enum, Object obj) {
int m=0;
while (enum.hasMoreElements() )
if (enum.next Element().equals(obj) )
m++;
return m;
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 62 – In exemplul anterior, metoda elements() din clasa Vector are ca rezultat un obiect ―enumerator pe vector‖,
de tipul Enumeration . Exemplu de posibil ă implementare a metodei elements() :

De obse rvat că interfa ța Enumeration nu are metode pentru modificarea obiectelor enumerate, care ar putea
veni în conflict cu metodele clasei colec ție care adaugă sau elimină elemente din colec ție.
Incepând din Java 2 s -a introdus interfa ța java.util.Iterator ca alternativă la interfa ța Enumeration :

Metoda iterator() din clase le colec ție are ca rezultat un obiect Iterator și înlocuie ște metoda elements() care
producea un obiect de tip Enumeration . Exemplu de clasa iterator pentru liste simplu înlăn țuite:

Exemplu de utilizare a unui obiect iterator:

public Enumeration elements() { return new VectorEnumerator (this);}
// definirea clasei iterator pe vector
class VectorEnumerator implements Enumeration {
private Vector v;
private int crt = 0; // indice element curent din enumerare
public VectorEnumerator (Vector v) { this.v=v; }
public boolean hasMoreElements() { return crt < v.size(); }
public Object nextElement() { return v.elementAt (crt++); }
}
public interface Iterator {
boolean hasNext(); // daca exista un element urmator in colectie
Object next(); // extrage element curent si avans la următorul
void remove(); // elimina e lement curent din colectie (optional)
}
public class SimpleList extends AbstractList {
private Node head; // inceput lista
private int n; // nr de noduri in lista
public SimpleList () { // constructor
head= new Node(null); // sa ntinela
}
public int size() { return n; }
public Iterator iterator () { return new SListIterator(); }
. . .
}
class SListIterator implements Iterator {
private Node pos=head.next;
public boolean hasNext () { return pos != null; }
public Object next() {
Object obj =pos.val; pos=pos.next;
return obj;
}
public void remove () { throw new UnsupportedOperationException(); }
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 63 –

Altă form ă de utilizare a unui iterator, într -o instruc țiune for:

Incepând din Java 5 este posibilă o utilizare implicită a unui obiect iterator, fără ca acesta să fie creat explicit.
Exemplu de concatenare șiruri dintr -o colec ție:

Instruc țiunea următoare ( for each )

for (Integer i: a) System.out.println(i); // repeta pentru fiecare valo are i di n a

este echivalent ă cu inst rucțiunea

for (Iterator i = a.iterator(); i.hasNext();)
System.out.print ln(i.next());

Forma nouă de iterator poate fi folosită pentru enumerarea obiectelor din orice clasă care implementează
interfa ța Iterable :
public interface Iterable {
Iterator iterator();
}

IE.04.6 Interfe țe Java pentru filtrare
Un filtru este un obiect care permite selectarea (sau respingerea) anumitor obiecte dintr -o colec ție sau dintr –
un vector. Un filtru poate con ține o singur ă metod ă cu rezultat boolean , care s ă spun ă dacă obiectul primit c a
argument este acceptat sau respins de filtru. In bibliotecile Java filtrele se folosesc în clasa File pentru listare
selectiv ă de fișiere dintr -un director, dar pot fi folosite și în alte situa ții. public static Object max (Collection c) {
Iterator it = c.iterator();
Comparable m=(Comparable) (it.next());
while (it.hasNext()) {
Comparable e=(Comparable) (it.next());
if (e.compareTo (m) > 0)
m=e;
}
return m;
}
for (Iterator it=c.iterator; it.hasNext(); ) {
Com parable e=(Comparable) (it.next());
. . .
}
public static String concat ( Collection a) {
String r="";
for (Object s: a ) // repeta pentru valori ale lui s în colectia a
r += s+" ";
return r;
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 64 – Clasa File din pachetul java.io poate genera obiecte ce corespund unor fi șiere sau directoare. Ea este
destinat ă opera țiilor de listare sau prelucrare a fi șierelor dintr -un director și nu con ține functii de citire –
scriere din/în fi șiere. Cel mai folosit constructor din clasa File prime ște un argument de tip String ce
reprezint ă numele unui fi șier de date sau unui fi șier director. Cea mai folosit ă metod ă a clasei File este
metoda list() care produce un vector de obiecte String , cu numele fi șierelor din directorul specificat la
construirea obiectului File. Exemplu de folosire a unui obiect File pentru afi șarea con ținutului directorului
curent:

Metoda listFiles() produce un vector de obiecte File ce corespund fi șierelor din directorul pentru care se
apeleaz ă metoda. Metodele list() și listFiles() au și o variant ă cu un argument de un tip interfa ță ce specific ă
filtrul aplicat fisierelor, pentru extragere selectiv ă de fișiere din director:
– metoda list() cu argument de tip FilenameFilter
– metoda listFiles() cu argument de tip FileFilter sau FilenameFil ter.
Interfe țele FileFilter și FilenameFilter conțin o singur ă metod ă accept() , care va fi apelat ă de list() sau
listFiles() pentru fiecare fi șier din director și care spune dac ă acel fi șier este sau nu acceptat în vectorul creat
de func ție.

Utilizatorul are sarcina de a defini o clas ă care implementeaz ă una din aceste interfe țe și de a transmite o
referin ță la un obiect al acestei clase fie metodei list() fie metodei listFiles() . Exemplu de filtru după tipul
(extensia numelui) fisierelor:

// utilizare
File d =new File("."); // din directorul curent
File [ ] files = d.listFiles(new FileTypeFilter("java"));
public static void mai n (String arg[]) throws IOException {
String dir ="."; // "numele" directorului curent
File d =new File(dir); // java.io.File d = new java.io.File(dir);
String [ ] files = d.list(); // vector cu fisierele din directorul curent
System.out.println ("Directory of "+ d.getAbsolutePath());
for (int i=0; i< files.length;i++)
System.out.println (files[i]);
}
public interface FilenameFilter { // prezenta din jdk 1.0
boolean accept (File path, String filename); // path=director(cale)
}
public interface FileFilter { // prezenta din jdk 1.2
boolean accept (File path); // path=nume complet fisier (cu cale)
}
class FileT ypeFilter implements FileFilter { // clasa pentru obiecte filtru
String ext; // extensie nume fisier
public FileTypeFilter (String ext) { this.ext = ext; }
public boolean accept (File f) {
String fname = f.getName(); // nume fisier
return fname . endsWith("."+ ext) ;
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 65 – Acela și filtru (dup ă extensie fi șier) în varianta cu interfa ța FilenameFilter :

Func ția accept() din interfe țele filtru este tot o func ție callback , apelat ă de metodele list() și listFiles() din
clasa de bibliotec ă File, dar definit ă ca parte din aplica ție pentru criteriul de filtrare dorit.
Exemplu de definire a unei interfe țe de t ip filtru pentru orice fel de obiecte, utilizat ă pentru filtrarea un ui
vector și creare a unui alt vector cu elementele selectate de filtru :

Func ția select() poate primi ca argument și un obiect enumerator dar nu poate crea o enumerare cu rezult atele
filtrării. class DirFilter implements FilenameFilter {
String ftype;
public DirFilter (String ft) { ftype=ft;}
public boolean accept (File dir, String name) { // nam e= nume fisier
return name. endsWith("."+ftype) ; // daca numele contine tipul ca subsir
}
}
public interface Filter {
boolean accept (Object s);
}
public static Vector select (Vector v, Filter f) {
Vector r = n ew Vector();
Enumeration e = v.elements();
while (e.hasMoreElements()) {
Object elem = e.nextElement();
if (f.accept(elem))
r.add( elem);
}
return r;
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 66 – Capitolul IE.05. Tehnici de programare orientat ă obiect în Java
Cuvinte cheie
Excep ții, Reflec ție,
Clase incluse, Clase interioare, Clase anonime
Fire de execu ție, Sincronizare, Sec țiuni critice

IE.05.1 Excep ții în Java
O excep ție progra m este o situa ție anormal ă apărută în execu ție și care poate avea cauze hardware sau
software. Excep țiile pot fi privite ca evenimente previzibile, ce pot ap ărea în anumite puncte din program și
care afecteaz ă continuarea programului, prin abatere de la cu rsul normal.
Existenta excep țiilor este un mare avantaj al limbajului Java, pentru c ă permite semnalarea la execu ție a unor
erori uzuale, p rin mesaje clare asupra cauzei și locului unde s -a produs eroarea, evitând efectele
imprevizibile ale acestor erori ( ca în cazul limbajului C, de ex emplu ).
Excep țiile Java sunt de dou ă categorii:
– Excep ții care nu necesit ă interven ția programatorului (numite Runtime Exceptions ), dar care pot fi
interceptate și tratate de c ătre programator. Dac ă nu sunt tratate, aceste excep ții produc afi șarea unui mesaj
referitor la tipul excep ției și terminarea for țată a programului. Aceste excep ții corespund unor erori grave de
programare, care nu permit continuarea executiei și care apar frecvent în programe, cum ar fi: erori de
indexare a elementelor unui vector (indice în afara limitelor), utilizarea unei variabile referin ță ce contine
null pentru referire la date sau la metode publice, împ ărțire prin zero, conversie prin operatorul cast între
tipuri incompatibile, ș.a.
– Excep ții care trebuie fie tratate, fie "aruncate" mai departe, pentru c ă altfel compilatorul marcheaz ă cu
eroare functia în care poate apare o astfel de eroare ( Checked Exceptions : excep ții a c ăror tratare este
verificat ă de compilator).Aceste excep ții corespund un or situa ții speciale care apar la uti lizarea unui program
(fișiere negasite, erori la opera ții de citire -scriere, date incorecte), dar nu produc neap ărat terminarea
programului. Urm ătorul program poate produce excep ții, dac ă este folosit gre șit:

O comand ă pentru execu ția programului anterior p roduce excep ția ArrayIndexOutOfBoundException ,
dacă nu s-au transmis argumente prin linia de comand ă: vectorul "arg" are lungime zero și deci nu exist ă
arg[0] ( nu există indice zero).
O linie de comand ă de form a "java Exc 1,2" (argumentul arg[0] nu este un șir corect pentru un num ăr întreg)
produce o excep ție NumberFormatException , excep ție generat ă în functia parseInt() . Ambele excep ții
mentionate sunt excep ții Runtime .
Excep țiile generate de opera țiile de int rare-ieșire (inclusiv la consol ă) trebuie fie aruncate, fie tratate pentru
că suntem obliga ți de c ătre compilatorul Java. Compilatorul știe care metode pot genera excep ții și cere ca
funcțiile care apeleaz ă aceste metode s ă arunce mai departe sau s ă tratez e excep țiile posibile. Exemplu: class Exc {
public static void main (String arg[ ]) {
System.out.pr intln ( Integer.parseInt(arg[0])); // afiseaza primul argument
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 67 –

Absen ța clauzei throws din functia main este semnalat ă ca eroare de compilator, pentru a obliga
programatorul s ă ia în considerare excep ția ce poate apare la func ția de citire read() , datorit ă citirii
caracterului EOF (sfârsit de fi șier) sau unei erori la citire.
O metod ă care apeleaz ă o funcție (metod ă sau constructor) ce arunc ă excep ții verificate trebuie fie s ă
semnaleze mai departe posibilitatea apari ției acestei excep ții (prin clauza throws ), fie s ă trateze excep ția
printr -un bloc try-catch care s ă includ ă apelul metodei. Exemplu cu aruncarea excep țiilor:

Tratarea excep țiilor necesit ă folosirea unui bloc try-catch pentru delimitarea sec țiunii de cod pentru care
excep țiile posibile sunt redirectate c ătre se cven țe scrise de utilizator pentru tratarea excep țiilor produse.
Sintaxa unui bloc try-catch este : try { bloc1 } catch ( Exception e) { bloc2 }
unde:
bloc1 este un bloc de instruc țiuni în care se pot produce excep ții la execu ție
bloc2 este un bloc de instruc țiuni care tratează excep țiile produse în ―bloc1‖
Exception este tipul excep ției tratate ( și tipul variabilei e)

Exemplu l anterior cu tratarea simplă a tuturor excep țiilor de I/E, indiferent cine le -a produs :

Deși blocul de instruc țiuni prin cuvântul catch poate lipsi, nu se recomandă această tratare prin ignorare a
excep țiilor care împiedic ă aparitia unui mesaj de ave rtizare la producerea excep ției; e ste preferabil ă
aruncarea unei exceptii cu throws pentru că apare mesaj de spre tipul de excep ție. Exemplu: public static void main (String arg[ ]) throws Exception {
int ch= System.in.read ( ); // citeste un caracter de la tastatura
System.out.println ((char ) ch); // afisare caracter citit
}
// deschidere fisier nou
public static RandomAccessFile rewrite ( String fname ) throws IOException {
RandomAccessFile f=new RandomAccessFile (fname,"rw"); // arunca exceptie
return f;
}
public static void main (String[] arg) throws IOException {
RandomAccessFile f= rewrite ("numere. bin "); // arunca exceptie
for (int i=0;i<20;i++)
f.writeInt (i); // arunca exceptie
f.close(); // arunca exceptie
}

public static void main (String[] arg) {
RandomAccessFile f=null;
try {
f= new RandomAccessFile("numere .bin","rw");
for (int i=0;i<20;i++)
f.writeInt (i);
f.close();
} catch (IOException e) { e.printStackTrace(); }
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 68 –

Tratarea excep țiilor se reduce de multe ori la scrierea unui mesaj pe ecran sau într -un fi șier, fie cu metoda
printStackTrace() din clasa Exception , fie cu o metodă de scriere dintr -o clasa de tip Writer sau Printer .
Prima solu ție are avantajul că prezintă toată secven ța de apeluri de func ții care a condus la producerea
excep ției și este folosiă în depanarea programelor.
Exist ă și situa ții când excep ția trebuie tratat ă și altfel decât prin afi șarea unui mesaj; exemplu l urm ător arat ă
cum putem verifica dac ă un șir de caractere reprezint ă un num ăr corect (în orice format permis pentru
numere neîntregi), f ără a examina fiecare caracter în parte:

Este posibilă și aruncarea unor excep ții puțin probabile (excep ția de citire, de ex.) combinată cu tratarea altor
excep ții (de exemplu excep ția de sfâr șit de fi șier).
Producerea unor excep ții poate fi prevenit ă prin verific ări efectuate de programator . Exemplu :

Uneori este preferabil ă verificarea prin program (ca î n cazul unor conversii de tip nepermise), dar alteori este
preferabil ă tratarea excep ției (ca în cazul detect ării existen ței unui fi șier înainte de a fi deschis, sau a utiliz ării
unor variabile referin ță ce pot fi nule).
In cazul programelor cu fi șiere pot să apară mai multe tipuri de excep ții (subtipuri de IOException ) și ne
interesează ce tip particular de excep ție s-a produs (deci cauza exactă a excep ției). Avem două solu ții
posibile de interceptare a acestor excep ții: cu mai multe blocuri try-catch pe fiecare instruc țiune sau cu un
singur bloc try care are mai multe clauze catch . Exemplu cu mai multe blocuri try:

public static void main (String a rg[]) {
Object ref=null;
try {int h = ref.hashCode(); } // exceptie daca ref==null
catch (NullPointerException e) { } // interzice afisare mesaj (nerecomandat!)
}
public static boolean isNumber (String s){
try { Double.parseDouble(s);
} catch(NumberFormatException ex) {return false;}
return true;
}
public static void main (String arg[ ]) {
if (arg.length == 0 ) { System.out.println ("No Argument"); System.exit( -1); }
System.out.println ( arg[0]); // afiseaza primul argument
}

public static void main (String[] arg) {
RandomAccessFile f=null;
try { f= new RandomAccessFile("numere .bin","r"); } // citire fisier
catch (IOException e ) { System.out.println ("Eroare la deschidere" + .getMessage()); }
try {
while (true) System.out.println ( f.readInt());
}
catch (IOException e) { e.printStackTrace() ; }
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 69 – In acest caz (la citirea din fi șiere binare de numere) excep ția apare fie la sfâr șit de fi șier (dar fără erori), fie în
cazul unei erori de citire din fi șier; de aceea este preferabil un bloc try cu două clauze catch :

IE.05.2 Reflec ție în Java
Pentru fiecare clas ă încărcată în ma șina virtual ă Java este creat automat câte un obiect de tip Class , cu
informa ții despre clasa asociat ă (metadate). Obiecte le Class sunt create automat și pentru interfe țe, clase
abstracte și vectori intrinseci Java.
Prin ―reflec ție‖ (reflection ) se în țelege ob ținerea de informa ții despre o clas ă sau despre un obiect în faza de
execu ție (este ―reflectat ă‖ starea ma șinii virtuale). In plus, se pot crea și modifica dinamic obiecte în faza de
execu ție. Reflec ția este asigurat ă în principal de clasa numit ă Class , dar și de alte clase din pachetul
java.lang.reflect : Constructor, Method, Field , s.a.
O variabil ă de tip Class conține o referin ță la un obiect descriptor de clas ă; ea poate fi ini țializat ă în mai
multe feluri:
– Folosind cuvântul cheie class ca și cum ar fi un membru public și static al clasei sau tipului primitiv :
Class cF = Float.class, cS = St iva.class, // clase predefinite sau proprii
cf = float.class, cv =void.class, // tipuri primitive
cN= Number.class, cI=Iterator.class ; // clase abstracte si interfete

– Folosind metoda static ă forName () cu argument nume de clas ă (ca șir de caractere):
Class cF = Class.forName(“java.util.Float”), cf = Class.forName (“float”);

– Folosind metoda getClass() pentru o variabil ă de orice tip clas ă (metoda getClass() este mo ștenită de la
clasa Object , deci exist ă în orice clas ă):
Float f = new Float (3.14); Class cF = f.getClass();

Clasa Class conține metode care au ca rezultat numele clasei, tipul clasei (clas ă sau interfa ță sau vector),
tipul superclasei, clasa extern ă, interfe țe implementat e, tipul obiectelor declarate în clas ă, numele câmpurilor
(variabilelor clasei), numele metodelor clasei, formele func țiilor constructor s.a.
Metoda getName() are ca rezultat un șir ce reprezint ă numele clasei al c ărui tip este con ținut într -un obiect
Class. Exemplul urm ător arat ă cum s e poate afi șa tipul real al unui obiect primit ca argument de tipul generic
Object :
public static void main (String[] arg) {
Rand omAccessFile f=null;
try { f= new RandomAccessFile("numere .bin","r"); }
catch (IOException e) { System.out.println ("Eroare la deschidere" + e.getMessage()); }
try {
while (true)
System.out.println ( f.readInt());
}
catch (EOFException e) { System.out.println ("Sf ârsit de fisier"); }
catch (IOException e) { System.out.println ("Eroare la citire din fisier"); }
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 70 – void printClassName (Object obj) {
System.out.println ( obj.getClass().getName());
}

Crearea de obiecte de un tip aflat în cur sul execu ției dar necunoscut la compilare (cu condi ția ca tipul
respectiv s ă fi fost definit printr -o clas ă, iar fi șierul de tip class să fie accesibil la execu ție) se poate face
simplu dac ă în clas ă exist ă numai un constructor f ără argumente . Exemplu:
public static Object getInstance (String clsname) throws Exception {
Class cl = Class.forName(clsname); // clsname = nume clasa (complet)
return cl.newInstance();
}

Deoarece putem ob ține toti constructorii unei clase, cunoscând tipul argumentelor , se pot ―fabrica‖ obiecte si
apelând constructori cu argumente. Exemplu:

Prin reflec ție un asamblor de componente JavaBeans dintr -un mediu vizual poate s ă determine propriet ățile
si metodele proprii unor obiecte, s ă modifice proprie tățile acestor obiecte și să genereze apeluri de metode
între obiecte. In rezumat, reflec ția permite opera ții cu clase și cu obiecte necunoscute la scrierea
programului, dar care pot fi deter minate dinamic, în cursul execu ției.
Dacă se apeleaz ă metode poli morfice pentru obiecte de tip necunoscut, atunci se va apela automat varianta
definit ă în clasa respectiv ă, chiar dac ă programatorul (si nici compilatorul) nu știe care este tipul exact al
obiectului. Exemplu:
String s = v.elementAt(i).toString(); // se apeleaz ă Integer.toString()
if ( v.elementAt(i).equals(obj)) … // se apeleaz ă Integer.equals()

De aceea determinarea tipului unui obiect la executie, folosind obiectul Class asociat, este rareori necesară în
programele obișnuite.

IE.05.3 Clase incluse
O clas ă Java poate con ține, ca membri ai clasei, alte clase numite clase incluse ( nested classes ). In cazul unei
singure clase incluse, structura va ar ăta astfel:
public static Object newObject (Constructor constructor, Object [ ] args) {
Objec t obj = null;
try {
obj = constructor.newInstance(args);
} catch (Exception e) { System.out.println(e); }
return obj;
}

// utilizare
Float x;
Class cls = Float.class;
Class[] argsCls = new Class[ ] {float.class}; // tip a rgumente constructor
Constructor constr = cls.getConstructor(argsCls); // obtinere constructor
Object[] args = new Object[ ] { new Float(3.5) }; // argument efectiv ptr instantiere
x =(Float) newObject (constr,args);

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 71 –

Clasa inclus ă poate fi o clas ă instan țiabilă, o clas ă static ă, o clas ă abstract ă sau o interfa ță.
Clasele incluse cu nume primesc de la compilator un nume compus din numele clasei exterioare, caracterul
‗$‘ și numele clasei interioare (Outer$Inner). Clasele care nu sunt incluse în alte clase se numesc clase de
nivel superior ( top-level classes ). Clasele incluse anonime, definite simultan cu instan țierea, primesc un
nume format din numele clasei exterioare, caracterul ‗$‘ și un num ăr.
Un exemplu din clasele JDK: Clasa interioar ă static ă ReverseComparator din clasa Collections , este
folosit ă de metoda static ă reverseOrder() prin intermediul unei variabilei statice:

Un exemplu de interes pentru definirea de noi clase dic ționar este interfa ța Entry , inclus ă în interfata Map ,
cu metode asociate un ei perechi cheie -valoare, ambele de tip Object :

Exemplu de clasă care implementează interfata Map.Entry:
public class Outer {
. . . // date si/sau metode ale clasei Outer
public class Inner {
. . . // date si/sau metode ale clasei Inner
}
. . . // alti membri ai clasei Out er
}

public class Collections {
public static Comparator reverseOrder() { // din clasa Collections
return REVERSE_ORDER;
}
private static final Comparator REVERSE_ORDER = new ReverseComparator();
private static class Re verseComparator implements Comparator,Serializable {
public int compare(Object o1, Object o2) {
Comparable c1 = (Comparable)o1;
return -c1.compareTo(o2);
}
}
. . . // alte metode si clase incluse din clasa Col lections
}
public interface Map {
Object get (Object key);
Object put (Object key, Object value);
. . . // alte metode abstracte din interfata Map

public interface Entry { // acces la cheia si valoarea dintr -o pereche
Object getKey(); // cheia
Object getValue(); // valoarea
Object setValue(Object value); // modifica valoarea
boolean equals(Object o); // daca doua perechi sunt egale
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 72 –

Un alt exemplu din JDK este o clas ă iterator inclus ă în clasa colec ție pe care ac ționeaz ă. In acest fel clasa
iterator are a cces la variabile private sau protected ale colec ției, nu poate fi instan țiată direct ci numai prin
intermediul colec ției (nu poate exista obiect iterator f ără o colec ție), fiind ascuns ă altor clase.

Uneori numele unei clase incluse a pare o singur ă dată, pentru a crea un singur obiect de acest tip. In plus,
clasa inclus ă implementeaz ă o interfa ță sau extinde o alt ă clasă și con ține numai câteva metode scurte. Pentru
astfel de situa ții se admite definirea ad -hoc de clase anonime, printr -un bloc care urmeaz ă operatorului new
cu un nume de interfa ță sau de clas ă abstract ă. Sintaxa definirii unei clase anonime este urm ătoarea:
new Interf ( ) { … // defini ție clasa inclusa } ;

unde "Interf" este un nume de interfa ță (sau de clas ă abstract ă sau neabstract ă) din care este derivat ă
(implicit) clasa inclus ă anonim ă. O astfel de clas ă nu poate avea un constructor explicit si deci nu poate
primi date la construirea unui obiect din clasa anonim ă. class E ntry implements Map.Entry {
private Object key,val;
public Entry (Object k, Object v) { key=k; val=v; }
public String toString() { return key+"="+val;}
public Object getKey() { return key; }
public Object getValue() { return val;}
publi c Object setValue (Object v) { val=v; return v;}
public boolean equals (Object obj) {
return ((Entry)obj).getKey().equals(key);
}
}
public class Vector {
protected int elementCount; // nr de elemente in vector
prot ected Object elementData[]; // vector de obiecte
public Vector (){ elementData= new Object[10]; }
// metoda care produce obiect iterator
public Enumeration elements() {
return new VectorEnumeration();
}
// definirea clas ei iterator pe vector (inclusa)
class VectorEnumeration implements Enumeration {
int count = 0; // indice element curent din enumerare
public boolean hasMoreElements() {
return count < elementCount;
}
public Object next Element() {
if (count < elementCount)
return elementData[count++];
else return null;
}
}
… // alte metode din clasa Vector
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 73 – O situa ție tipic ă pentru folosirea unei clase a nonime definit ă simultan cu crearea unui obiect de tipul
respectiv este cea în care transmitem unei func ții un obiect de un subtip al interfetei Comparator (adresa
unei func ții de comparare). Exemplu de sortare a unei liste de o biecte în ordine descrescăto are.

O clas ă inclus ă nestatic ă este numit ă și clas ă interioar ă (inner class ), pentru c ă fiecare obiect din clasa
exterioar ă va con ține un obiect din clasa interioar ă. Cuvântul nested se refer ă la rela ția sintactic ă dintre clase:
clasa inclus ă este def inită în cadrul defini ției clasei exterioare; cuvântul inner se refer ă la rela ția dintre
obiectele claselor incluse: un obiect al clasei exterioare con ține în el un obiect al clasei incluse.
Exemplu de clasa interioară anonimă pentru iterator pe vector:

O alt ă form ă de clas ă interioar ă este o clas ă definit ă într-o metod ă a clasei externe. Exemplu de clas ă pentru
un obiect comparator inclus ă în func ția de ordomare:

Collections.sort (list, new Comparator( ) { // ordonare descrescatoare
public i nt compare (Object t1, Object t2) { // incepe definitia clasei anonime
Comparable c1=(Comparable)t1, c2=(Comparable)t2;
return – c1.compareTo(c2); // rezultat invers metodei compareTo
}
}); // aici se termina definitia clasei si instructiunea

public class Vector {
protected int elementCount; // nr de elemente in vector
protected Object elementData[]; // vector de obiecte
public Vec (){ elementData= new Object[10]; }
// metoda care produce obiect iterator
public Enumeration elements() {
return new Enumeration(){
// definirea clasei iterator pe vector (inclusa)
int count = 0; // indice element cure nt din enumerare
public boolean hasMoreElements() {
return count < elementCount;
}
public Object nextElement() {
if (count < elementCount)
return elementData[count++];
else return null;
}
}; // aici se termina instructiunea return
} // aici se termina metoda elements
… // alte metode din clasa Vector
}

static void sortByValue (Map m) { // ordonarea unui dictionar în ordinea valorilor
// clasa inclusa
class VComp implements Comparator { // compara doua perechi cheie -val
public int compare (Object o1, Object o2) {
Map.Entry e1= (Map.Entry)o1; // o pereche cheie -valoare
Map.Entry e2= (Map.Entry)o2; // alta pereche cheie -valoare
return ((Integer) e1.getValue()).compareTo ((Integer) e2.getValue());
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 74 –

Prin definirea de clase anonime codul surs ă devine mai compact iar defini ția clasei apare chiar acolo unde
este folosit ă, dar pot exista dificult ăți la întelegerea codului și erori de utilizare a acoladelor și parantezelor.
Intre clasa interioar ă și clasa exterioar ă exist ă un "cuplaj" foarte strâns; acest cupl aj poate fi un dezavantaj la
restructurarea (refactorizarea) unei aplica ții, dar poate fi exploatat în definirea unor clase de bibliotec ă (care
nu se mai modific ă).
Motivele definirii de clase interioare pot fi diverse:
– Pentru clase de interes local : c lasa interioar ă este necesar ă numai clasei exterioare. Astfel se reduce
numărul de clase de nivel superior ( top-level ) și conflictele între nume de clase (ca urmare a unor instruc țiuni
import pentru pachete cu clase cu nume identice).
– Pentru a permite c lasei exterioare accesul la membri private ai clasei interioare.
– Pentru a permite claselor interioare accesul la variabile ale clasei exterioare și deci o comunicare mai
simpl ă între clasele incluse (prin variabile externe lor).
– Pentru crearea rapid a de obiecte functionale ( cu functii callback ), unice, necesare ca parametri efectivi
altor metode (prin definirea de subclase simultan cu instan țierea lor).
– Pentru ca o clas ă să poată moșteni func ții de la câteva clase (mo ștenire multipl ă).
IE.05.4 Fire de execu ție ca obiecte
IE.05.4.1 Fire de execu ție concurente
Paralelismul unor activit ăți se poate realiza la nivelul sistemului de operare pentru activit ăți ce rezult ă din
executarea unor programe independente (nu neap ărat distincte), fiecare cu spa țiul său de memorie , dar care
pot comunica prin intermediul sistemului de operare. La nivelul sistemului de operare activit ățile paralele
se numesc procese (în sisteme de tip Unix) sau taskuri (în sisteme Microsoft Windows ).
Un alt nivel de paralelism poa te avea loc în cadrul fiec ărui program (aplica ție), sub forma unor fire de
execu ție (threads ). Un proces poate con ține mai multe fire (subprocese). In anumite aplica ții este necesar
ca mai multe activit ăți să progreseze în paralel, astfel încât se creea ză aparen ța că un acela și program
(calculator) poate efectua în paralel mai multe opera ții. Câteva exemple tipice de aplica ții cu paralelism între
activit ățile din interiorul aplica ției:
– Un program de tip server care trebuie s ă răspund ă cererilor adresa te de mai mul ți ―clien ți‖ (programe
client), fiecare cu o conexiune separat ă la server;
– Un joc cu imagini multiple animate simultan;
– Algoritmi paraleli pentru îmbun ătățirea performan țelor unor opera ții de c ăutare sau de alt tip.
Se spune c ă două proces e (fire) P1 și P2 (lansate în ordinea P1,P2) sunt concurente sau paralele atunci când
execu ția lui P2 începe înainte de terminarea complet ă a lui P1. Altfel spus, cele dou ă procese pot evolua în
paralel, chiar pe un singur procesor care execut ă alternativ secven țe de in strucțiuni din P1 și din P2. Set eset = m.entrySet(); // multime de perechi cheie -valoare
ArrayList entries = new ArrayList(eset); // vector de perechi chei e-valoare
Collections.sort (entries, new VComp()); // ordonare vector
System.out.println (entries); // afisare perechi ordonate dupa valori
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 75 – Paralelismul proceselor nu implic ă neap ărat mai multe procesoare care lucreaz ă simultan, ci este o
alternativ ă la execu ția strict secven țială a celor dou ă procese : se execut ă complet P1 și apoi se ex ecută
comple t P2 (f ără întreruperi). Este posibil ca în dou ă procese (fire) paralele s ă se execute aceea și secven ță de
instruc țiuni sau secvente de instruc țiuni complet diferite.
Java este primul limbaj care a inclus facilit ăți de programare cu subprocese concurente, în cadrul unei
aplica ții, paralelism suprapus peste concuren ța proceselor la nivelul sistemului de operare. In Java
paralelismul (aparent) are loc în cadrul unui singur program (sau aplet), iar numele folosit pentru o astfel de
activitate este acela de thread (fir de execu ție) sau ― subproces‖ ( ligthweight process ), deoarece folose ște
acela și spatiu de memorie ( și de nume) cu celelalte fire de execu ție din acela și program. Planificatorul
(dispecerul) de procese este parte din ma șina virtual ă Java (JVM).
Cedarea controlului procesorului de c ătre procesul activ (în executie) c ătre dispecer se poate face voluntar (la
cererea sa, prin apelul unei metode) sau ca urmare a lans ării unei opera ții de intrare -ieșire.
Activit ățile paralele pot evolua complet independe nt unele de altele (asincron) sau pot utiliza anumite resurse
comune (zone de memorie, fi șiere etc) și atunci este necesar ă sincronizarea lor în încercarea de acces la o
resurs ă comun ă.
După crearea sa și înainte de terminare, un proces poate trece ciclic prin câteva st ări, iar schimbarea st ării
sale este cauzat ă fie de evenimente interne ( o ac țiune a procesului activ), fie de evenimente externe
procesului, cum ar fi o decizie a planificatorului de procese.
Principalele st ări în care se poate afla un proc es sunt:
– In execu ție (running ), când procesul este activ (de ține controlul procesorului).
– Suspendat, sau blocat, sau în a șteptare (waiting ), când procesul asteapt ă producerea unui eveniment.
– Gata de execu ție dar inactiv (ready ), pentru c ă exist ă un alt proces mai important și gata de execu ție.
Un singur proces poate fi în execu ție, celelalte se afl ă fie în lista proceselor ―gata‖, fie în lista proceselor
suspendate sau blocate. Dintre procesele gata planificatorul alege pe cel cu prioritate maxim ă și care este
primul în coad ă (dacă sunt mai multe procese gata cu aceea și prioritate).
Procesele paralele (concurente) î și pot disputa anumite resurse comune (date din memorie, fi șiere de date,
s.a.) sau î și pot transmite date între ele (procese numite ―pr oduc ător‖ și ―consumator‖).
Interac țiunile dintre procese (sau subprocese) paralele nu se fac prin apeluri directe între procese ci prin
intermediul dispecerului. Coordonarea și sincronizarea proceselor paralele necesit ă existen ța unor opera ții
specifice, prin care procesele se adreseaz ă planificatorului.
IE.05.4.2 Fire de execu ție în Java
In Java un fir de execu ție este un obiect dintr -o subclas ă a clasei de bibliotec ă Thread . Opera țiile prin care
un fir interac ționeaz ă cu planificatorul sunt fie metode d in clasa Object (wait, notify ), fie metode din clasa
Thread (sleep, yield, join , ș.a.). Planificatorul activeaz ă un fir prin apelarea metodei run() , prezent ă în orice
fir de execu ție (metodă moștenită de la clasa Thread ).
In Java exist ă întotdeauna un fir de execu ție implicit (cu numele main ), creat automat și în care se execut ă
funcția main cu rol de program principal. Alte fire de execu ție pot fi create din firul main sau din alte fire
create anterior. Clasele pentru fire de execu ție se definesc fie ca s ubclase ale clasei Thread (cu metoda run()
redefinit ă), fie pe baza unei clase care implementeaz ă interfa ța Runnable (interfa ță care con ține numai
metod a run() ).

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 76 – Clasa Thread și interfa ța Runnable se afl ă în pachetul java.lang și, alături de cuvântul chei e synchronized
au existat înc ă din prima versiune Java. Incepând cu versiunea 5 au fost introduse noi clase și metode pentru
execu ția și sincronizarea firelor concurente cu resurse comune în pachetul java.util.concurrent . Clasa
Thread contine câteva met ode, unele apelate de dispecer iar altele care apeleaz ă dispecerul. Efectul metodei
run (apelat ă de dispecer) depinde de rolul pe care îl are firul de execu ție în cadrul unei aplica ții. In clasa
Thread metoda run() nu are nici un efect și de aceea trebuie definit ă o subclas ă, cu metoda run() redefinit ă:
class T extends Thread {
public void run() {

}
}

Crearea unui nou fir de execu ție înseamn ă crearea unui nou obiect și se face într -un fir activ (care poate fi cel
implicit, pentru func ția main ). Exemplu:
Thread fir1 = new T(); fir1.start();

Firul p ărinte apeleaz ă de obicei o singur ă metod ă a firului ―fiu‖ (metoda start() ), pentru lansarea sa în
competi ție cu celelalte fire; de aceea nici nu se mai creeaz ă uneori variabile de tipul subclasei. Exemplu:
new T( ).start(); // obiect anonim de tipul T

Apelul metodei start() dintr -un fir nu are ca efect imediat apelul metodei run() din acel fir, ci introducerea
firului respectiv în coada pr oceselor gata de execu ție, într -o pozi ție care depinde de prioritatea firului. Dac ă
nu exist ă alte fire gata mai importante și dac ă task-ul Java este activ, atunci firul este activat imediat dup ă
apelul metodei start() .
Firele din clase ce extind clasa Thread pot avea un constr uctor cu parametru de tip String , prin care se poate
da un nume firului creat. Metodele clasei Thread sunt de dou ă categorii: metode ale obiectelor și metode
statice. Metode care pot fi aplicate oric ărui obiect fir de execu ție:
start() : Cere lansarea un ui fir dintr -un alt fir (p ărinte), când este posibil.
isAlive() : Verifica dac ă firul pentru care se apeleaz ă s-a terminat definitiv sau nu.

interrupt() : Cere întreruperea firului pentru care se apeleaz ă
getName() / setName() : Ob ține/modific ă numele fir ului pentru care se apeleaz ă
getPriority() / setPriority() : Ob ține/modific ă prioritatea firului pentru care se apeleaz ă
join() : Așteapt ă terminarea firului pentru care se apeleaz ă
Metode statice care pot fi folosite doar de firul în execu ție (firul cur ent):
Thread currentThread(): referin ță către firul curent, în execu ție
void sleep(long millis): autosuspendare fir curent pentru un timp specificat în milisecunde
boolean interrupted(): testeaz ă și anuleaz ă starea de întrerupere a firului curent
Metodele următoare sunt mo ștenite de la clasa Object dar se folosesc numai în obiecte de un tip derivat din
Thread sau Runnable :
wait() : Este apelat ă de firul activ pentru a se autosuspenda în asteaptarea unui semnal de la un alt fir paralel
(care de ține o resurs ă comun ă)

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 77 – notify(), notifyAll() : Firul activ notifică (anun ță) firele în a șteptare că s -a produs evenimentul a șteptat prin
wait() .
Metodele wait() si notify() pot fi folosite numai în secven țe sincronizate, condi ție care nu poate fi verificat ă
la compilar e dar care produce la execu ție excep ția IllegalMonitorStateException .
Metoda run() din Thread se redefine ște în fiecare fir definit de utilizatori și determin ă efectul execut ării
acestui fir. De obicei metoda run() conține un ciclu în care se execut ă anumi te opera ții (afi șare, desenare,
opera ții cu fi șiere etc.) și se apeleaz ă una din metodele prin care se cedeaz ă controlul celorlalte fire
concurente ( yield(), sleep(), wait() etc.). Este posibil ca mai multe fire concurente s ă execute acelea și
opera ții. Exe mplu:

Succesiunea de afi șare depinde de secven ța de numere aleatoare generate, dar și de alte evenimente din
sistem (pentru un sistem de operare multi -tasking care alocă intervale de timp fiecărui task, cum este
Microsoft Windows ).
Metoda run() , așa cum este definit ă în interfata Runnable (pe care o implementeaz ă și clasa Thread ), nu
poate arunca mai departe excep ția InterruptedException și de aceea excep ția trebuie ―prins ă‖ și tratat ă în
metoda run() . Excep ția InterruptedException apare atunci când se încearc ă întreruperea unui fir aflat într -o
stare de a steptare ca urmare a unui apel wait(), sleep(), join() ; în aceste cazuri nu se produce întreruperea
solicitat ă. Fiecare fir are asociat ă o variabil ă (boolean) cu starea sa de întrerup ere.
Cedarea controlului de c ătre un fir se face și implicit, la ini țierea unei operatii de I/E; terminarea opera ției
respective este un eveniment tratat de planificator. Exemplul urm ător pune în eviden ță aceast ă situatie:

class T extends Thread { // clasa pentru obiecte fire de executie
public T (String nume) { super(nume); } // apel constructor cla sa Thread
public void run () {
for (int i=1;i<10;i++) {
System.out.println (i + " " + getName()); // scrie numele procesului
try , sleep (time(i)); – // “doarme” un timp
catch (Inte rruptedException e) { }
}
System.out.println (getName()+ " terminat"); // la terminarea procesului
}
private int time (int i) {return (int) (100*Math.random()); }
// creare si lansare procese paralele
public static void main (S tring [] arg) {
T p1 = new T("Unu");
T p2 = new T("Doi");
p1.start(); p2.start();
}
}

// clasa pentru fire de executie reactivate la apasarrea unei taste
class Fir extends Thread {
public void run () {
try { while ( System.i n.read() > 0 ); } // asteapta tastarea unei clape
catch (IOException e) {}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 78 –

Un fir este termina t definitiv fie prin terminarea instruc țiunilor din metoda run() , fie datorit ă unei excep ții
propagate în afara metodei run() . Un program cu mai multe fire paralele se termin ă atunci când s -au terminat
toate firele (mai pot r ămâne fire de tip daemon după terminarea unui program).
Fiecare fir are o prioritate asociat ă (un întreg între 1 și 10), care arat ă importan ța relativ ă a acelui fir fa ță de
alte fire. La creare un fir primeste prioritatea implicit ă 5, dar aceas ă prioritate poate fi modificat ă prin meto da
setPriority() . Dacă mai multe fire sunt gata de execu ție, atunci când planificatorul preia controlul , este ales
firul cu prioritate maxim ă pentru a fi activat.
Definirea unei subclase a clasei Thread nu mai este posibil ă pentru procese paralele din ca drul unui aplet,
deoarece un aplet este o subclas ă a clasei JApplet și în Java nu este permis ca o clas ă să extind ă mai mult de
o singur ă clasă. De aceea a fost creat ă și o interfa ță numit ă Runnable , care con ține numai metoda run() .
Metodele wait(), notify () și notifyAll() nu puteau face parte din aceat ă interfa ță deoarece ele nu pot fi
implementate de orice utilizator și de aceea fac parte din clasa Object , ca metode native și finale.
Crearea unui fir de execu ție folosind interfa ța Runnable necesit ă defini rea unei clase care implementeaz ă
aceast ă interfa ță și definirea metodei run() din aceast ă clasă. In aceast ă clasă se pot folosi metodele statice
din clasa Thread : Thread.currentThread() si Thread.sleep ( milisec) .
Lansarea în executie a unui fir se p oate face numai prin apelul metodei ―start‖ din clasa Thread , fie în
funcția main , fie într -o alt ă metod ă (dintr -un alt fir, de exemplu). De aceea trebuie creat câte un obiect
Thread pe baza fiec ărui obiect Runnable , folosind un constructor al clasei Thread care admite un
parametru de tip Runnable . Exemplu:

class Proces implements Runnable { // fire cu interfata Runnable
public void run () {
for (int i=1;i<8;i++) {
System.out.println (Thread.cu rrentThread() + ” “+ i);
try { Thread.sleep(20); } catch (InterruptedException e) {}
}
}
// creare si lansare procese paralele
public static void main (String [] arg) {
new Thread ( new Proces ()).start();
new Thread (new Pr oces ()).start();
}
} public static void main (String [] ar g) {
Fir t = new Fir (); // creare fir de executie
t.start(); // activare fir de citire de la tastatura
while (t.isAlive()) { // repeta mereu
System.out.print("."); // afisare pu nct la fiecare 100 ms
try { Thread.sleep(100);} catch (Exception e) { }
}
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 79 – IE.05.4.3 Sincronizarea firelor de execu ție Java
Firele paralele pot evolua un timp independent unele de altele, dar în anumite momente pot s ă-și dispute
resurse comune sau doresc s ă-și transmit ă date prin variabile comune. In ambele situa ții trebuie reglementat
accesul la resursele comune, deoarece în caz contrar pot apare cazur i de blocare definitiv ă a unor procese,
sau de actualizare eronat ă a unor date comune sau de transmitere inco rectă a unor date între fire de execu ție.
Sincronizarea firelor concurente cu resurse comune trebuie s ă asigure rezultate corecte indiferent de num ărul
firelor, de ordinea lans ării lor, de timpul necesar fiec ărui fir pentru opera țiile cu resursa comun ă sau de al ți
parametri care nu au leg ătură cu algoritmul aplica ției cu fire multiple. Altfel spus, comportarea unui program
corect trebuie s ă fie reproductibil ă, indiferent de condi țiile în care se execut ă programul .
Pentru a ilustra efectele nesincroniz ării a dou ă fire cu o resurs ă comun ă vom considera exemplul a dou ă fire
care actualizeaz ă acelea și date (incrementeaz ă un acela și contor). Contorul este un obiect dintr -o clas ă
Contor definit ă de noi, cu dou ă metode publice: get() care cite ște valoarea curent ă a variabilei contor și
incr() care m ărește cu 1 valoarea variabilei contor. Actualizarea se realizeaz ă prin mai multe opera ții
(instruc țiuni ma șină și/sau opera ții de I/E) și aceast ă secvent ă de opera ții poate fi întrerupt ă (de planificator),
iar un alt proces reactivat poate dori s ă execute aceeasi secven ță de incrementare a aceluia și contor (cazul a
două oficii din localit ăți diferite de la care se cere simultan rezervarea unui loc la un zbor sau la un tren).
Pentru un contor în memorie vom for ța transferul controlului de la un fir la altul prin introducerea de apeluri
sleep() sau yield() între instruc țiunile care realizeaz ă actualizarea contorului.

class Contor { // pentru obiecte folosite de fire
private int m;
public Contor () { m=0; }
public void incr () { // incrementare m in trei pasi
int aux;
try {
aux=m; // pasul 1
Thread.sleep((int) (Math.random()*20));
aux=aux+1; // pasul 2
Thread.sleep((int) (Math.random()*20));
m=aux; // pasul 3
} catch (InterruptedException e) {}
}
public int get () { // citire valoare contor
return m;
}
}
// pentru obiecte fir de executie
class Fir extends Thread {
Contor c;
public Fir ( Contor c) { this.c=c; }
public void run () {
for (int i=0;i<5;i++){
try { sleep(10); } catch(InterruptedException e) {}
c.incr();
System.out.println (getName()+" "+c.get());
}
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 80 –

Fiecare din cele dou ă fire face câte 5 increment ări ale contor ului comun, dar valoarea final ă a contorului nu
este 10, ci este o valoare mai mic ă ( 5 pentru programul anterior). Rezultatul final depinde în general de
timpii de asteptare ai fiec ărui fir (între opera ții), de ordinea lans ării și de alte procese din sis tem.
In acest exemplu firul f1 citeste în ―aux‖ o valoare ―m‖ a contorului, este întrerupt de firul f2 care cite ște și el
în variabila sa proprie ―aux‖ aceeasi valoare ―m‖; fiecare proces incrementeaz ă și scrie înapoi în ―m‖ aceea și
valoare a variabilei sa le ―aux‖. Din aceast ă cauză valoarea contorului creste cu 1 și nu cu 2 cum ar fi trebuit
dacă cele dou ă fire erau sincronizate fa ță de resursa comun ă.
Un exemplu asem ănător foloseste un contor memorat într -un fi șier disc și incrementat din mai multe fire
concurente; în acest caz pierderea controlului de c ătre fiecare fir concurent se face automat, ca urmare a
initierii unor operatii de citire/scriere pe disc și nu prin cedarea voluntară a controlului.
In general, când un proces începe s ă modifice o resurs ă comun ă trebuie interzis altor procese s ă modifice
aceeasi resurs ă înainte ca procesul care a obtinut primul resursa s ă fi terminat operatiile de modificare a
resursei. Altfel spus, trebuie serializat accesul la resursa comun ă pentru procese (fire) concuren te sau trebuie
asigurat ă excluderea mutual ă a firelor în raport cu resursa comun ă.
Secțiunile de cod din fiecare proces care acceseaz ă resursa comun ă se numesc sectiuni critice sau regiuni
critice. Este important ca instruc țiunile dintr -o sec țiune critic ă să se execute neîntrerupt pentru un anumit
proces și pentru un anumit obiect, f ără ca un alt proces s ă poată executa și el aceleasi operatii pentru un
acelasi obiect. In Java o sec țiune critic ă este fie o metod ă, fie un bloc de instruc țiuni precedate de c uvântul
cheie synchronized .
Prima solu ție Java pentru sincro nizarea firelor de execu ție a fost utilizarea cuvântului cheie synchronized în
declararea unor metode de acces la resurse comune (înainte de tipul metodei) sau ca atribut al unui bloc de
instrucțiuni. Sincronizarea se face la nivel de obiect și nu la nivel de func ție.
In exemplul anterior cu incrementarea unui contor din memorie, acest cuvânt se adaug ă metodei incr() :

class ExFir {
public static void main (String args[]) {
Contor c= new Contor(); // contor folosit de cele doua fire
Fir f1 = new Fir ( c ); Fir f2 = new Fir ( c );
f1.start(); f2.start();
while ( f1.isAlive() && f2.isAliv e() ) // asteapta terminare f1 si f2
try {Thread.sleep(100);} catch (Exception e) {}
System.out.println (c.get()); // valoare finala contor
}
}

public synchronized void incr () {
int aux;
try {
aux=m ; // pasul 1
Thread.sleep((int) (Math.random()*20));
aux=aux+1; // pasul 2
Thread.sleep((int) (Math.random()*20));
m=aux; // pasul 3
} catch (InterruptedException e) {}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 81 – După ce firul f1 a intrat în execu ția metodei sincronizate ―incr‖ pentr u un obiect ―c‖, nu se permite altui fir s ă
mai execute o metod ă sincronizat ă pentru acelasi obiect, iar firul f2 care încearc ă acest lucru este pus în
așteptare pân ă la terminarea metodei sincronizate.
O metod ă sincronizat ă poate fi întrerupt ă, dar nu poa te fi reapelat ă pentru acela și obiect dintr -un alt fir
concurent. De asemenea, nu este permis ă apelarea din fire diferite a unor metode sincronizate diferite pentru
acela și obiect. De exemplu, nu este posibil ca dou ă fire s ă adauge în paralel elemente la u n acela și vector
(metoda addElement() ). Toate metodele de acces la un vector (din clasa Vector ) sunt sincronizate pentru a fi
sigure la apelare din fire concurente.
Cuvântul synchronized ar trebui ad ăugat oric ărei metode care modific ă un obiect ce poate f i folosit în
comun de mai multe fire de execu ție paralele. Multe din metodele claselor JDK sunt sincronizate.
După apelarea unei metode ―sincronizate‖ pentru un obiect acest obiect este ―blocat‖ cu un ―z ăvor‖ sau
―lacăt‖ (lock) și nu mai poate fi apelat ă o altă metod ă sincronizat ă pentru acelasi obiect (dintr -un alt proces).
Deblocarea obiectului are loc la terminarea execu ției metodei sincronizate. Deoarece sincronizarea se face la
nivel de obiecte, datele comune proceselor trebuie încapsulate în obiecte utilizate în comun. La crearea unui
proces care folose ște o astfel de resurs ă comun ă (variabil ă, colec ție, fi șier) se transmite adresa obiectului ce
constituie resursa comun ă. Prin monitoare se asigur ă accesul strict secven țial al firelor concurente la ope rații
critice asociate unui obiect. Un monitor este asociat unui obiect (sau unei clase) și nu unei metode.
Două fire pot executa în paralel secven țe din dou ă metode sincronizate, dar pentru obiecte diferite. De
asemenea, un fir poate executa o metod ă sincronizat ă și un alt fir o metod ă nesincronizat ă pentru un acela și
obiect. Sincronizarea unei metode se poate exprima în dou ă moduri:

synchronized void f() , … – sau void f() , synchronized (this) , … – –

O clasă este sigură î ntr-un context cu fire concurente ( thread -safe) dacă rezultatele metodelor sale sunt
acelea și indiferent din câte fire paralele sunt apelate aceste metode pentru acelea și obiecte. Practic, această
calitate a unei clase se ob ține prin adăugarea atributului synchronized metodelor care modifică date din
clasă. Metodele sincronizate au performan țe mai slabe decât metodele nesincronizate și de aceea nu toate
clasele JDK sunt thread -safe.
Primele clase colec ție (Vector, Hashtable ) au avut metode sincronizate, dar începând din Java 2 clasele
colec ție nu sunt implicit thread -safe din motive de performan ță. Se pot obține însă clase colec ție echivalente
sincronizate. Exemplu de vector cu acces sincronizat ob ținut din ArrayList :
List safelist = Collections.synchro nizedList (new ArrayList());

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 82 – Capitolul IE.06. Colec ții de obiecte în Java
Cuvinte cheie
Colec ții Java, Colec ții ordonate, Colec ții generice, Liste, Mul țimi,
Dicționare, Interfe țele Collection ,List, Set, Map ,Iteratori, Generice

IE.06.1 Grupul claselo r colec ție
O colec ție este un obiect ce con ține un num ăr oarecare, variabil de obiecte. Colec țiile se folosesc pentru
memorarea și regăsirea unor date sau pentru transmiterea unui grup de date de la o metod ă la alta. Colec țiile
Java sunt structuri de date generice, realizate fie cu elemente de tip Object , fie cu generice (cu tipuri
parametrizate) .
Grupul claselor colecție din Java cuprinde interfețe, clase abstracte și clase direct utilizabile și se poate
extinde cu alte clase care respectă aceleași inter fețe. Ele formează o infrastructură ( Collection Framework ),
adică o bază pe care se pot dezvolta alte clase colecție. Infrastructura colec țiilor ofer ă clase direct utilizabile
și suport pentru definirea de noi clase (sub form ă de clase abstracte), toate co nforme cu anumite interfe țe ce
reprezint ă tipur i abstracte de date (liste, mul țimi, dic ționare).
Un utilizator î și poate defini propriile clase colec ție, care respect ă aceste interfe țe impuse și sunt compatibile
cu cele existente (pot fi înlocuite unele p rin altele).
Familia claselor colec ție din Java este compus ă din dou ă ierarhii de clase :
– Ierarhia care are la baz ă interfa ța Collection ,
– Ierarhia care are la baz ă interfa ța Map .

O colec ție, în sensul Java, este un tip de date abstract care reune ște un grup de obiecte de tip Object , numite
și elemente ale colec ției. Un dic ționar (o asociere ) este o colec ție de perechi chei -valoare (ambele de tip
Object ); fiecare pereche trebuie s ă aibă o cheie unic ă, deci nu pot fi dou ă perechi identice.
Interfața Collection conține metode aplicabile pentru orice colec ție de obiecte. Nu toate aceste opera ții
trebuie implementate obligatoriu de clasele care implementeaz ă interfa ța Collection ; o clas ă care nu
define ște o metod ă opțional ă poate semnala o exc epție la încercarea de apelare a unei metode
neimplementate.
Urmeaz ă descrierea complet ă a interfe ței Collection :

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 83 –

Din interfa ța Collection sunt derivate direct dou ă interfe țe pentru tipurile abstracte:
– Set pentru mul țimi de elemente dist incte.
– List pentru secven țe de elemente, în care fiecare element are un succesor și un predecesor și este
localizabil prin pozitia sa în list ă (un indice întreg).
Clasele abstracte existente fac uz de iteratori și implementează aproape toate metodele u nei clase colec ție
folosind numai metoda iterator() (cu excep ția metodelor de ad ăugare obiect la colec ție și de calcul num ăr de
elemente din colec ție, care depind de structura fizic ă a colec ției).
Pentru fiecare din cele 3 structuri de date abstracte (li stă, mul țime,dic ționar) sunt prev ăzute câte dou ă clase
concrete care extind clase abstracte și implementeaz ă interfe țele respective. Structurile de date concrete
folosite pentru implementarea tipurilor de date abstracte sunt : vector extensibil, list ă dublu înlănțuită, tabel
de dispersie și arbore binar.
public interface Collection { // operatii generale ptr orice colectie
int size(); // dimensiune cole ctie (nr de elemente)
boolean isEmpty(); // verifica daca colectie vida
boolean contains(Object element); // daca colectia contine un obiect dat
boolean add(Object element); // adauga un element la co lectie
boolean remove(Object element); // elimina un element din colectie
Iterator iterator(); // produce un iterator pentru colectie
boolean containsAll(Collection c); // daca colectia contine elem. din colectia c
boolean addAll(Collection c); // adauga elem. din c la colectie
boolean removeAll(Collection c); //elimina din colectie elem. colectiei c
boolean retainAll(Collection c); // retine in colectie numai elem. din c
void clear(); // sterge continut colectie
Object[ ] toArray(); // copiere colectie intr -un vector
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 84 – Toate clasele colec ție instan țiabile redefinesc metoda toString() , care produce un șir cu toate elementele
colec ției, separate prin virgule și încadrate de paranteze drepte. Afi șarea continutului unei co lecții se poate
face printr -o singur ă instruc țiune.
De asemenea sunt prev ăzute metode de trecere de la vectori intrinseci de obiecte ( Object [] ) la colec ții de
obiecte și invers: func ția Arrays.asList() , cu un argument vector intrinsec de obiecte și rezul tat de tip List și
funcția toArray() din clasa AbstractCollection , de tip Object []. Exemplu:
String sv*+ = ,“unu”,”doi”,”trei”-;
List list = Arrays.asList(sv); // nu este nici ArrayList, nici LinkedList !
System.out.println (list); // System.out.println (list.toString());
String aux[] = (String[ ]) list.toArray(); // aux identic cu sv

O a treia ierarhie are la baz ă interfa ța Iterator , pentru metodele specifice oric ărui iterator asociat unei
colec ții sau unui dictionar. Toate colec țiile au iteratori dar nu și clasele dic ționar (se poate îns ă itera pe
mulțimea cheilor sau pe colec ția de valori).
Clasa Collections conține metode statice pentru mai multi algoritmi "generici" aplicabili oric ărei colec ții
(min(), max() ) sau numai listelor ( sort(), binarySearch(), reverse(), shuffle() ). Ei foloseau ini țial tipul
generic Object și metodele polimorfice equals(), compareTo() ș.a., dar acum folosesc tipuri parametrizate
(din versiunea 5). Exemple în forma mai vec he dar mai simpl ă:
public static Object min (Collection col ); // obiect minim din colectia col
public static Object min (Collection col , Comparator cmp);
public static void sort (List lst); // ordonare lista lst
publ ic static void sort (List lst, Comparator cmp);

IE.06.2 Mulțimi de obiecte
O mul țime este o colec ție de elemente distincte pentru care opera ția de c ăutare a unui obiect î n mul țime este
frecvent ă și trebuie s ă aibă un timp cât mai scurt.
Interfa ța Set conține exact acelea și metode ca și interfa ța Collection , dar implement ările acestei interfe țe
asigur ă unicitatea elementelor unei mul țimi. Metoda de ad ăugare a unui obiect la o mul țime add() verific ă
dacă nu exist ă deja un element identic, pentru a n u se int roduce duplicate în mul țime. De aceea obiectele
introduse în mul țime trebuie s ă aibă metoda equals() redefinit ă, pentru ca obiecte diferite s ă nu apar ă ca fiind
egale la compararea cu equals() .
Clasele mul țime predefinite și care implementeaz ă interfa ța Set sunt:
HashSet : pentru o mul țime neordonat ă realizat ă ca tabel de dispersie
TreeSet : pentru o mul țime ordonat ă, realizat ă ca arbore binar echilibrat automat ( Red-Black Tree ).
Tabelul de dispersie asigur ă cel mai bun timp de c ăutare, iar arborele echilib rat permite p ăstrarea unei rela ții
de ordine între elemente și un timp de c ăutare bun.
Opera țiile cu dou ă colec ții sunt utile mai ales pentru operatii cu mul țimi:
s1.containsAll (s2) // true dac ă s1 contine pe s1 (includere de multimi)
s1.addAll (s2) // reuniunea multimilor s1 si s2 (s1=s1+s2)
s1.retainAll (s2) // intersectia multimilor s1 si s2 (s1=s1*s2)
s1.removeAll (s2) // diferenta de multimi (s1= s1 -s2)

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 85 – Diferen ța simetric ă a dou ă mulțimi se poate ob ține prin secven ța urm ătoare:

Exemplul urm ător arat ă cum putem folosi dou ă mulțimi pentru afisarea cuvinte lor care apar de mai multe ori
și celor care apar o singur ă dată într-un text. S -a folosit o mul țime ordonat ă, pentru a permite afi șarea
cuvintelor în ordine lexicografic ă.

In general se recomand ă programarea la nivel de interfa ță și nu la nivel de clas ă concret ă. Asadar, se vor
folosi pe cât posibil variabile de tip Collection sau Set și nu variabile de tip HashSet sau TreeSet (numai la
construirea obiectului colec ție se specific ă implementarea sa).
Nici una din mul țimile HashSet sau TreeSet nu păstreaz ă ordinea de ad ăugare a elementelor la mul țime,
dar în clasa LinkedHashSet metoda toString() produce un șir cu elementele mul țimii în ordinea ad ăugării
lor la mul țime, dar are acelea și performan țe la c ăutare ca și clasa HashSet .
Interfa ța SortedSet , implementat ă de clasa TreeSet , extinde interfa ța Set cu câteva metode aplicabile numai
pentru colec ții ordonate:
Object first(), Object last() // primul si ultimul element din multime
SortedSet subSet(Object from, Object to) // submultimea definita prin 2 valori
SortedSet headSet(Object to) // subSet (first(),to)
SortedSet tailSet (Object fr om) // subSet (from, last())

IE.06.3 Liste (Secven țe)
Interfa ța List conține câteva metode suplimentare fat ă de interfa ța Collection :
– Metode pentru acces pozitional, pe baza unui indice întreg care reprezint ă poziția :
get (index), set (index,object), add (index, object), remove (index) public static void m ain (String arg[ ]) throws IOException {
Set toate = new HashSet (); // toate cuvintele distincte din text
Set dupl =new TreeSet (); // cuvintele cu aparitii multiple
Scanner sc = new Scanner (new File( arg[0]));
String word=""; // un cuvant din fisier
while ( sc.hasNext() ) { // repeta cat mai sunt cuvinte in fisier
word= sc.next(); // scoate urmatorul cuvant
if ( ! toate.add (word) ) // daca a mai fost in “toate”
dupl.add (word); // este o aparitie multipla
}
System.out.println ("multiple: "+ dupl); // afisare cuvinte repetate
Set unic e = new TreeSet (toate);
unice.removeAll (dupl); // elimina cuvinte multiple
System.out.println ("unice: " + unice); // afiseaza cuvinte cu o singura aparitie
} Set sdif = new HashSet(s1);
sdif.addAl l(s2); // reuniune s1+s2
Set aux = new HashSet(s1);
aux.retainAll (s2); // intersectie s1*s2 in aux
simdif.removeAll(aux); // reuniune minus intersectie

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 86 – – Metode pentru determinarea pozi ției unde se afl ă un element dat, deci c ăutarea primei și ultimei apari ții a
unui obiect într -o list ă:
indexOf (object), lastIndexOf (object)
– Metode pentru crearea de iteratori specifici listelor:
listIterator (), listIterator (index)
– Metod ă de extragere sublist ă dintr -o list ă:
List subList(int from, int to);
Exist ă două implement ări pentru interfa ța List:
ArrayList listă vector, preferabil ă pentru acces aleator frecvent la elemente le listei.
LinkedList listă dublu înl ănțuită, preferat ă când sunt multe inser ări sau ștergeri de elemente în list ă.
In plus, clasei Vector i-au fost ad ăugate noi metode pentru a o face compatibila cu interfa ța List. Noile
metode au nume mai scurte și o alt ă ordine a argumentelor în metodele add() și set():
Forma veche (1.1) Forma nou ă (1.2)

Object elementAt (int) Object get(int)
Object setElementAt (Objext, int) Object set (i, Object)
void insertElementAt (Object, int) void add (i,Object)

Exemplu de func ție care schimb ă între ele valorile a dou ă elemente i si j:

De observat c ă metodele set() si remove() au ca rezultat vec hiul obiect din list ă, care a fost modificat sau
eliminat. Metoda set() se poate folosi numai pentru modificarea unor elemente existente în list ă, nu și
pentru ad ăugare de noi elemente.
Algoritmii de ordonare și de c ăutare binar ă sunt exemple de algorit mi care necesit ă accesul direct, prin
indici, la elementele listei; de aceea metodele sort() si binarySearch() din clasa Collections au argument de
tip List și nu Collection .
Accesul pozi țional este un acces direct, rapid la vectori dar mai pu țin eficient în cazul listelor înl ănțuite. De
aceea s -a definit o clas ă abstract ă AbstractSequentialList , care este extins ă de clasa LinkedList dar nu și de
clasa ArrayList . Metoda static ă Collections.binarySearch , cu parametru de tipul general List, recunoaste
tipul d e list ă și face o c ăutare binar ă în vectori, dar o c ăutare secven țială în liste înlăn țuite.
Clasa LinkedList contine, în plus fa ță de clasa abstract ă AbstractList , urm ătoarele metode, utile pentru
cazuri particulare de liste (stive, cozi etc.): getFirst() , getLast(), removeFirst(), removeLast(),
addFirst(Object), addLast(Object).
Din versiunea 5 au mai fost ad ăugate interfe țele Queue si Deque cu câteva metode noi, interfe țe
implementate de mai multe clase, printre care AbstractQueue, PriorityQueue, Linked List, ArrayDeque .
Interfa ța Queue extinde interfa ța Collection cu o metod ă de ad ăugare offer() care nu produce excep ție dac ă static void swap (List a, int i, int j) { / / din clasa Collections
Object aux = a.get(i); // a.elementAt(i)
a.set (i,a.get(j)); // a.setElementAt (a.elementAt(j) , i)
a.set (j,aux); // a.setElement At (aux , j)
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 87 – nu mai este loc în colec ție și cu metode de acces la primul element din colec ție (din coad ă) cu și fără excep ție
în caz c ă nu exist ă (element() si peek() fără eliminare din coad ă, poll() și remove() cu eliminare din coad ă).
Interfa ța Deque extinde interfa ța Queue cu metode de acces și la primul si la ultimul element din list ă:
addFirst(), removeFirst(), getFirst(), addLast(), removeLas t(), getLast() și variantele care nu produc
excep ții: offer(), poll(), peek(), offerFirst(), offerLast() ,…
Cele mai multe clase de tip coad ă (ca BlockingQueue si SynchronousQueue ) au fost introduse, al ături de
alte clase, în pachetul java.util.concurrent pentru programare cu fire de execu ție concurente.
Pentru aplica țiile cu structuri de date sunt interesante clasele ArrayDeque si PriorityQueue ; clasa
LinkedList implementeaz ă acum și interfa ța Deque și de aceea nu exist ă o clas ă LinkedDeque .
Coada cu priori tăți este implementat ă ca un vector heap extensibil (f ără limite de capacitate). Indiferent de
ordinea de ad ăugare la coad ă, elementul din fa ță (obținut prin una din metodele peek(), poll() sau remove() )
este cel cu prioritatea minim ă. Prioritatea este det erminat ă de c ătre metoda compareTo() a obiectelor
introduse în coad ă (constructor f ără argumente) sau de c ătre metoda compare() a obiectului comparator
transmis ca argument la construirea unei cozi.
IE.06.4 Dicționare (Asocieri)
Interfa ța Map conține met ode specifice opera țiilor cu un dic ționar de perechi cheie valoare, în care cheile
sunt unice . Exist ă trei implement ări pentru interfa ța Map :
HashMap dicționar realizat ca tabel de dispersie, cu cel
mai bun timp de căutare.

TreeMap dicționar realizat ca arbore echilibrat, care
garantează ordinea de enumerare.

LinkedHashMap tabel de dispersie cu men ținere ordine
de introducere (din versiunea 4)

Definitia simplificat ă a interfe ței Map este urm ătoarea:

Metoda get() are ca rezultat valoarea asocia tă unei chei date sau null dacă cheia dat ă nu se afl ă în dic ționar.
Metoda put() adaug ă sau modific ă o pereche cheie -valoare și are ca rezultat valoarea asociat ă anterior cheii
date (perechea exista deja în dic ționar) sau null dacă cheia perechii introduse este nou ă. Efectul metodei put
(k,v) în cazul c ă exist ă o pereche cu cheia k în dic ționar este acela de înlocuire a valorii asociate cheii k prin public interface Map {
Object put(Object key, Object value); // pune o pereche cheie -valoare
Object get(Object key); // extrage valoare asociată unei chei date
Object remove(Object key); // elimină pereche cu cheie dată
boolean containsKey(Object key); // verifica daca exista o cheie data
boolean containsValue(Object value); // verifica daca exista o valoare data
int size(); // dimensiune dictionar (nr de perechi)
boolean isEmpty();
void clear(); // elimina toate perechile din dictionar
Set keySet(); // multimea cheilor
Collection val ues(); // colectia valorilor din dictionar
Set entrySet(); // multimea perechilor cheie -valoare
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 88 – noua valoare v (valoarea înlocuit ă este transmis ă ca rezultat al metodei ). Testul de apartenen ță a unei che i
date la un dic ționar se poate face fie direct prin metoda containsKey() , fie indirect prin verificarea
rezultatului opera ției get() .
Clasele care genereaz ă obiecte memorate într -un obiect HashMap sau HashSet trebuie s ă redefineasc ă
metodele equals() si hashCode() , astfel încât s ă se poat ă face c ăutarea la egalitate dup ă codul de dispersie.
In exemplul urm ător se afi șează numărul de apari ții al fiec ărui cuvânt distinct dintr -un text, folosind un
dicționar -arbore pentru scrie rea cuvintelor în ordine.

Cheile dintr -un dictionar pot fi extrase într -o mul țime cu metoda keySet() , iar valorile din dic ționar pot fi
extrase într -o colec ție (o list ă) cu metoda values() . Metoda entrySet() produce o mul țime echivalent ă de
perechi "cheie -valoare", unde clasa pereche are tipul Entry . Entry este o interfa ță inclus ă în interfa ța Map și
care are trei metode: getKey() , getValue() și setValue() .
Clasa AbstractMap define ște majoritatea metodelor din interfa ța Map în func ție de metoda abstractă
entrySet() , iar metoda put() nu este abstractă dar nici nu este definită (aruncă o excep ție).
De observat c ă metodele entrySet(), keySet() și values() (definite în AbstractMap ) creeaz ă doar imagini noi
(views ) asupra unui dic ționar și nu alte colec ții de obiecte; orice modificare în dic ționar se va reflecta
automat în aceste ―imagini‖, f ără ca să apelăm din nou metodele respective. O imagine (view)este creat ă
printr -o clas ă care define ște un iterator pe datele clasei dic ționar și nu are propriile sale date. Metodele clasei
imagine sunt definite apoi pe baza iteratorului, care d ă acces la datele din dic ționar.
Diferen ța dintre clasele HashMap si LinkedHashMap apare numai în șirul produs de metoda toString() a
clasei: la LinkedHashMap ordinea perechilor în acest șir este aceea și cu ordinea de introducere a lor în
dicționar, în timp ce la HashMap ordinea este aparent întâmpl ătoare (ea depinde de capacitatea tabelei de
dispersie, de func ția hashCode() și de ordinea de introducere a cheilor). Pentru p ăstrarea ordinii de ad ăugare
se folos ește o list ă înlănțuită, iar tabelul de dispersie asigur ă un timp bun de reg ăsire dup ă cheie.
IE.06.5 Colec ții ordonate
Problema ordon ării este rezolvat ă diferit în Java pentru liste fa ță de mul țimi și dic ționare. Listele sunt
implicit neordonate (se adaug ă numai la sfâr șit de list ă) și pot fi ordonate numai la cerere, prin apelul unei class FrecApar { // frecventa de aparitie a cuvintelor intr -un tex t
private static final Integer ONE= new Integer(1); // o constanta
public static void main (String arg[]) {
Map dic = new TreeMap (); // dictionar de cuvinte
String text =“ trei unu doi trei doi trei “; String word;
StringTokenize r st = new StringTokenizer (new String (text));
while ( st.hasMoreTokens()) {
word = st.nextToken(); // cuvantul urmator
Integer nrap = (Integer) dic.get(word); // numar de aparitii
if (nrap == null) // daca nu exista anterior
dic.put (word,ONE); // prima aparitie
else
dic.put (word, new Integer (nrap.intValue()+1)); // alta aparitie
}
System.out.println (dic); // af isare dictionar
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 89 – metode statice ( Collections.sort() ). Multimile, ca și dicționarele, au o variant ă de implementare (printr -un
arbore binar ordonat) care asigur ă menținerea lor în ordine dup ă orice ad ăugare sau eliminare de obiecte.
Exemplu de ordonare a unei liste:

Putem s ă ne definim vectori sau liste ordonate automat, sau alte alte structuri compatibile cu interfa ța List și
care asigur ă ordinea (un arbore binar, de exemplu).
O mul țime ordonat ă este de tipul TreeSet iar un dic ționar ordonat este de tipul TreeMap . Se pot defini și
alte tipuri de colec ții sau dic ționare ordonate, care implementeaz ă (optional) interfa ța SortedSet , respectiv
interfa ța SortedMap . Adăugarea sau modificarea v alorilor dintr -un arbore se face cu men ținerea ordinii și nu
necesit ă reordonarea mul țimii sau dic ționarului. Obiectele introduse într -o colec ție TreeSet sau TreeMap
trebuie s ă aparțină unei clase care implementeaz ă interfa ța Comparable și deci conține o d efiniție pentru
metoda compareTo() .
Exemplu de ordonare a unei liste de nume (distincte) prin crearea unei mul țimi ordonate:
SortedSet lst = new TreeSet (lista); // sau se adauga cu metoda addAll

Iteratorul unei colec ții ordonate parcurge elemen tele în ordinea dictat ă de obiectul comparator folosit
menținerea colec ției în ordine.
O problem ă comun ă colec țiilor ordonate este criteriul dup ă care se face ordonarea, deci func ția de
compara ție, care depinde de tipul obiectelor comparate. Sunt prev ăzute două soluții pentru aceast ă problem ă,
care folosesc două interfe țe diferite : Comparable și Comparator .
Anumite metode statice ( sort, min, max s.a.) și unele metode din clase pentru mul țimi ordonate apeleaz ă în
mod implicit metoda compareTo() , parte a inte rfeței Comparable . Clasele JDK cu date ( String, Integer,
Date s.a.) implementeaz ă interfa ța Comparable și deci contin o metoda compareTo() pentru o ordonare
"natural ă" ( excep ție face clasa Boolean , ale c ărei obiecte nu sunt comparabile).
Ordinea natural ă este ordinea valorilor algebrice (cu semn) pentru toate clasele numerice, este ordinea
numeric ă fără semn pentru caractere și este ordinea lexicografic ă pentru obiecte de tip String . Pentru alte
clase, definite de utilizatori, trebuie implementat ă interfa ța Comparable prin definirea metodei
compareTo() , dac ă obiectele clasei pot fi comparate ( și sortate).
Pentru ordonarea dup ă un alt criteriu decât cel natural și pentru ordonarea dup ă mai m ulte criterii se va folosi
metoda compare() dintr -o clasă compatibi lă cu interfa ța Comparator .
Rezultatul metodei compare( Object ob1, Object ob2 ) este acela și cu al metodei compareTo() , deci un num ăr
negativ dac ă ob1<ob2, zero dac ă ob1==ob2 și un num ăr pozitiv dac ă ob1>ob2.
Un argument de tip Comparator apare în construct orii unor clase și în câteva metode din clasa Collections
(sort, min, max ) ce necesit ă compararea de obiecte. public static void main (String arg[ ]) {
String tab* + = ,"unu","doi","trei",”patru”,”cinci”-;
List lista = Arrays.asList (tab);
Collections.sort (lista);
System.out.println (lista); // scrie: [cinci,doi,patru,trei,unu ]
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 90 – Pentru a utiliza o functie compare() trebuie definit ă o clasă care con ține numai metoda compare() , iar un
obiect al acestei clase se transmite ca argument func ției. Exemplu de ordonare a dic ționarului de cuvinte –
frecven ță creat anterior, în ordinea invers ă a num ărului de apari ții:

IE.06.6 Iteratori
Una din opera țiile frecvente asupra colec țiilor de date este enumerarea tuturor elementelor c olecției (sau a
unei subcolec ții) în vederea aplic ării unei prelucr ări fiec ărui element obtinut prin enumerare. Realizarea
concret ă a enumer ării depinde de tipul colec ției și foloseste un cursor care înainteaz ă de la un element la
altul, într -o anumit ă ordine (pentru colec ții neliniare). Cursorul este un indice întreg în cazul unui vector sau
un pointer (o referin ță) pentru o list ă înlănțuită sau pentru un arbore binar.
Generalizarea modului de enumerare a elementelor unei colec ții pentru orice fel de cole cție a condus la
apari ția claselor cu rol de ―iterator‖ fa ță de o altă clasă colec ție. Orice clasă colec ție Java 2 poate avea o clasă
iterator asociată. Pentru un acelasi obiect colec ție (de ex. un vector) pot exista mai mul ți iteratori, care
progresează î n mod diferit în cadrul colec ției, pentru că fiecare obiect iterator are o variabilă cursor proprie.
Toate clasele iterator trebuie să includă următoarele opera ții: pozi ționarea pe primul element din colec ție,
poziționarea pe următorul element din colec ție, obținerea elementului curent din colec ție, detectarea
sfârsitului colec ției (test de terminare a enumerării).
Interfa ța Iterator este varianta mai nou ă a interfetei Enumerator si conține următo arele metode :
public interface Iterator {
boolean hasN ext(); // daca exista un element urmator in colectie
Object next(); // extrage element curent si avans la urm ătorul
void remove(); // elimina element curent din colectie (optional)
}

Exemplu de utilizare a unui iterator:

Set entset = dic.entrySet();
ArrayList entries = new ArrayList(entset);
Collections.sort (entries, new Comp());
. . .
// clasa comparator obiecte Integer in ordine inversa celei naturale
class Comp implements Comparator {
public int com pare (Object o1, Object o2) {
Map.Entry e1= (Map.Entry)o1, e2= (Map.Entry)o2; // e1,e2=prechi cheie -valoare
return ((Integer)e2.getValue()).compareTo ((Integer) e1.getValue());
}
}

public static Object max ( Collection c ) { // determinare maxim din colectia c
Iterator it = c.iterator();
Comparable m=(Comparable) (it.next());
while (it.hasNext()) {
Comparable e=(Comparable) (it.next());
if (e.compareTo (m) > 0)
m=e;
}
return m;
}
[Type a quote from the document or the summary of an interesting point. You can
position the text box anywhere in the document. Use the Text Box Tools tab to
change the formatting of the pull quote text box.]

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 91 – De observat c ă modificarea con ținutului unei colec ții se poate face fie prin metode ale clasei colec ție, fie
prin metoda remove() a clasei iterator, dar nu ar trebui folosite simultan ambele moduri de modif icare. In
exemplul urm ător apare o excep ție la execu ție:

Pentru fiecare clas ă concret ă de tip colec ție exist ă o clas ă iterator. Un obiect iterator este singura posibilitate
de enumerare a elementelor unei mul țimi și o alternativ ă pentru adresarea prin indici a elementelor unei liste.
Clasele iterator nu sunt direct instan țiabile (nu au constructor public), iar obiectele iterator se ob țin prin
apelarea unei metode a clasei colec ție (metoda iterator() ). In felul acesta, programatorul este obligat s ă
creeze întâi obiectul colec ție și numai dup ă aceea obiectul iterator. Mai mul ți algoritmi generici realiza ți ca
metode statice (în clasa Collections ) sau ca metode ne -statice din clasele abstracte folosesc un obiect iterator
pentru parcurgerea colec ției. E xemplu:

Fragmentul urm ător din clasa AbstractCollection arată cum se pot implementa metodele unei clase colec ție
folosind un iterator pentru clasa respectiv ă:

ArrayList a = new ArrayList();
Iterator it = a.iterator();
for (int i=0;i<7;i++) {
a.add(i); it.remove();
}
public static void sort (List list) {
Object a[] = list.toArray(); // transforma lista in vector intrinsec
Arrays.sort(a); // ordonare vector intrinsec (mai eficienta)
ListIterator i = list.listIterator();
for (int j=0; j<a.length; j++) { // modificare elemente din lista
i.next(); i.set(a[j]);
}
}

public abstract class AbstractCollection implem ents Collection {
public boolean contains(Object o) { // fara cazul o==null
Iterator e = iterator();
while (e.hasNext())
if (o.equals(e.next()))
return true;
return false;
}
public Object[] toArray() {
Obje ct[] result = new Object[size()];
Iterator e = iterator();
for (int i=0; e.hasNext(); i++)
result[i] = e.next();
return result;
}
. . .
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 92 – Interfa ța ListIterator conține metode pentru traversare a unei liste în ambele sensuri și pentru modificarea
elementelor enumerate : hasNext(), next(), hasPrevious(), previous(), nextIndex(), previousIndex(),
remove(), set (Object o), add(Object o). Exemplu de parcurgere a unei liste de la coad ă la cap ăt:

O clas ă dicționar ( Map) nu are un iterator asociat, dar este posibil ă extragerea unei mul țimi de chei sau unei
mulțimi de perechi cheie -valoare dintr -un dic ționar, iar aceste mul țimi pot fi enumerate.
Secven ța urm ătoare face o enumerare a perechilor c heie-valoare dintr -un dic ționar, folosind interfa ța public ă
Entry , definit ă în interiorul interfe ței Map :

IE.06. 7 Generice
Clasele colec ție cu tipuri parametrizate au fost introduse în versiunea 5, ca solu ții alternative pentru colec țiile
de obiecte Object . Aceste clase au fost numite generice ( generics ) și nu clase ― șablon‖ ( templates ) pentru c ă
deși se definesc și se folosesc aproape la fel cu clasele șablon din C++ ele difer ă prin implementare: în C++
din clasa șablon se genereaz ă diverse clase prin substitu ția parametrilor t ip din clasa șablon, dar în Java
exist ă o singur ă clasă generic ă (surs ă și compilat ă) în care se înlocuiesc parametri i formali cu parametrii
efectivi, ca și la apelul de func ții. Intr-o colec ție generică t ipul obiectelor din colec ție apare ca parametru -tip
după numele clasei. Exemple:
ArrayList<Integer> a = new ArrayList<Integer>();
ArrayList<String> b = ArrayList<String> (100);
ArrayList<TNode> c = ArrayList<TNode> (n);
ArrayList<LinkedList<Integer>> graf; // initializata ulterior
TreeMap<String, Integer> d ; // cheie=String, val oare =Integer

Ultimul exemplu arată că o clasă poate avea mai mul ți parametri -tip.
Avantajele sunt acelea c ă se poate verifica la compilare dac ă se introduc în colec ție numai obiecte de ti pul
declarat pentru elementele colec ției, iar la extragerea din colec ție nu mai este necesar ă o conversie de la tipul
generic la tipul specific aplica ției. Exemplu cu un vector de obiecte Integer :

List a = new LinkedList();
for (ListIterator i= a.listIterator (a.size()); i.hasPrevious(); )
System.out.println (i.previous());

for (Iterator it= map.entrySet().iterator(); it.hasNext();) {
Map.Entry e = (Map.Entry) it.next();
System.out.println ( e.getKey()+”:”+e.getValue());
}

ArrayList<Integer> list = new ArrayList< Integer>();
for (int i=1;i<10;i++)
list.add( new Integer(i) ); // nu se poate adauga alt tip decat Integer
int sum=0;
for (int i = 0; i< list.size();i++) {
Integer val= list.get(i); // fara conversie de tip !
sum += val.intValue();
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 93 – Sursele unor clase colec ție s-au modificat substan țial prin trecerea la generice. Exemplu:

Exemplu de utilizare dic ționar cu valori multiple pentru problema listei de referin țe încruci șate – în ce linii
dintr -un fișier text apare fiecare cuvânt distinct :

Compilatorul Java emite averism ente la utilizarea formei mai vechi pentru colec ții (cu elemente de tip
Object ), iar documenta ția claselor a fost rescris ă pentru forma cu parametri -tip. Clasele mai vechi care
foloseau colec ții nu au mai fost modificate ; un exemplu este clasa DefaultMutab leTreeNode .
Pentru colec ții generice s -a introdus o form ă mai simpl ă de iterare . Exemplu :

public static void main (String[ ] arg) throws IOException {
Map<String,List<Integer>> cref = new TreeMap<String,List<Integer>>( );
BufferedReader in = new BufferedReader (new FileReader (arg[0]));
String line, word; int nl=0;
while ((line=in.re adLine()) != null) {
++nl;
StringTokenizer st = new StringTokenizer (line);
while ( st.hasMoreTokens() ) {
word=st.nextToken();
List<Integer> lst = cref.get (word);
if (lst==null)
cref.put (word, lst=new Li nkedList<Integer>());
lst.add (new Integer (nl));
}
}
System.out.println (cref);
}

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {
// …
public V get (Object key) , …-
public V put ( K key, V value) , … –
Set<K> keySet() , … –
Set < Map.Entry<K,V>> entrySet () , …-
Collection<V> values() , … –
V remove (Object key) , … –
int size() , …-
boolean containsKey (Object key) , …. –
boolean containsValue (Object value) , … –
void putAll( Map <? extends K, ? extends V> m) ,…-
void clear() , … –
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 94 –

Trecerea de la un tip primitiv la tipul clas ă corespunz ător și invers se face automat în versiunea 5, opera ții
numite autoboxing și unboxing . Exemplu:

La declararea unui parametru tip se poate folosi și caracterul wildcard ‗?‘ cu sensul de ―orice tip‖ :

Exemplu de func ție incorect ă și utilizare care produce erori la compilare:

O colec ție Collection<Object> nu este un supertip al unei colectii Collection<T> chiar dacă tipul Object
este supertip al oricărui tip clasă T din Java . In concluzie Collection<?> este supertipul oricãrei colec ții de
obiecte, iar caracterul '?' a re sensul de "tip necunoscut".
Declara ția Collection<Object> nu este echivalentă cu declara ția Collection<?> .
Tipurile necunoscute pot fi limitate inferior sau superior ( Bounded wildcard s) folosind sintaxa <?
extends T> pentru tipuri limitate superior și <? super T> pentru tipuri limitate inferior. Exemplu:
static double sum (Collection<? extends Number> c) { …}

deci func ția sum() poate opera cu o colec ție de obiecte de orice subtip al lui Number (Byte, Short, Integer,
Long, Float, Double ). Se spune c ă Number este limita superioar ă a tipului necunoscut '?'. public static void main (String arg[]){
String sa[]={"unu","doi","trei"};
TreeSet<String> a = new TreeSet<String> ();
for (int i=0;i<sa.leng th ;i++)
a.add(sa[i]);
String r="";
for (String s: a ) // iterator Java versiunea 5
r += s+” -";
System.out.println( r); // doi -trei-unu-
}

LinkedList<String> list = new LinkedList<String>();
for (int x=1;x< 9;x++)
list.add(x+"");
String sb="";
for (String s : list)
sb += s; // concatenare de siruri extrase din vector

// functie generic ă de afisare a oric ărei colectii
static void printCol (Collection<?> c) {
for (Ob ject e : c) System.out.println(e);
}

// functie naiv ă de afisare a unei colectii generice
static void printCol (Collection<Object> c) {
for (Object e : c) System.out.println(e);
}
// utilizare incorect ă
HashSet<String> m = new HashS et<String> (); printCol (m);

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 95 – Nu numai clasele pot avea parametri tip dar și metodele (numite metode generice). Exemplu:

// transforma un vector intr -o colectie (gresit)
static void arrayToCol (Object[] a, Collection<?> c) {
for (Object o : a)
c.add(o); // eroare la compilare
}
// transforma un vector intr -o co lectie (corect)
static <T> void arrayToCol (T[] a, Collection<T> c) {
for (T o : a)
c.add(o); // correct
}
// utilizare (fara argumente efective de tip la apel)
String[] sa = new String[100];
Collection<String> c s = new LinkedList<String>();
arrayToCol (sa, cs); // T presupus a fi String

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 96 – Capitolul IE.07. Diagrame de clase în UML
Cuvinte cheie
UML, Atribute, Opera ții, Stereotip, Asociere, Generalizare
Diagrame stati ce, Diagrame dinamice, Diagrame de stare,
Diagrame fizice, Cazuri de utilizare, Diagrama de clase,

IE.07.1 Standardul UML
UML ( Unified Modelling Language ) este un standard pentru reprezentarea grafic ă a claselor, obiectelor și a
relațiilor statice și dinamice dintre acestea. UML e ste independent de un anumit limbaj de programare și
permite vizualizarea relațiilor dintre mai multe clase (obiecte) dintr -o aplica ție sau a unei p ărți din aplica ție.
Pentru păstrarea independen ței față de orice limbaj stand ardul UML folose ște cuvinte diferite în general de
cuvintele folosite în limbajele existente la vremea elaborării sale: atribute pentru datele sau variabilele clasei,
opera ții pentru metodele clasei, stereotip pentru atributele unei clase sau unei rela ții, asociere pentru rela ția
numită ―delegare‖ sau ―compunere‖, generalizare pentru derivare etc.
Standardul UML pune la dispozi ție mai multe categorii de diagrame:
– Diagrame statice în care apar clase, obiecte, colec ții și rela țiile statice dintre ele : imp lementare, derivare,
compunere, includere, etc.
– Diagrame dinamice care arat ă cum interac ționeaz ă obiectele software în cursul execu ției aplica ției și
evenimentele semnificative din cursul execu ției.
– Diagrame de stare după modelul ma șinilor cu st ări finite (FSM)
– Diagrame fizice care reprezint ă entităti din aplica ție (fi șiere, biblioteci)
– Diagrame pentru cazuri de utilizare a aplica ției de c ătre diverse categorii de utilizatori ( Use Cases )
Diagramele UML pot fi la diferite niveluri de detaliu și sunt utile ca:
– Reprezentare conceptuală a func țonării aplica ției
– Specifica ții de proiectare a programelor (înainte de implementare)
– Documenta ție de implementare a aplica ției (după implementare)
De observat c ă exist ă diferite programe care pot genera diag rame de clase din cod surs ă (Java) și că unele
dintre acestea se mai abat pu țin de la standardul UML. Diagramele prezentate aici sunt create de o versiune
mai veche (6.7.1) a mediului integrat pentru dezvoltarea de programe NetBeans .
Este posibil ă și opera ția de generare de cod din diagrame de clase .
IE.07.2 Diagrame de clase în UML
Diagrama UML a unei clase este un dreptunghi cu trei secțiuni dispuse pe verticală : numele clasei , datele și
metodele clasei.

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 97 – Variabilele clasei, numite "atribute" în diagrama U ML, au de obicei modul de acces private sau protected
pentru a respecta principiul încapsul ării datelor în clase și sunt marcate cu minus (‗ -‘). Constructorii și
metodele publice ( numite ―opera ții‖ în UML) sunt marcate cu plus (‗+‘) .
Uneori se reprezint ă numai metodele cele mai folosite sau caracteristice, fără metode mo ștenite de la alte
clase (de la clasa Object , de ex.).
Exemplu de definire a unei clase:

Diagrama acestei clase în NetBeans arat ă astfel:

public class MyVector {
protected Object data[]; // date din vector
protected int count; // nr de elemente din vector
public MyVector( int n) {
data = new Object[n]; // count=0 implicit
}
public MyVector () { this(10); }
// metode publice
public int size ( ) { return count; }
public String toString ( ) // redefinita metoda din Object
String str="[";
for (int i=0;i<count;i++)
str = str+ data[i] +" ";
return str+"]";
}
public void add (Object obj) { // adaugare la vector
if ( count >= data.length )
resize( );
data[count++]= obj;
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 98 – IE.07.3 Rela ția de gener alizare în UML
In diagrama UML rela ția de generalizare ( derivare , extindere) este reprezentat ă printr -o săgeată vertical ă de
la subclas ă la superclas ă iar în subclas ă apar doar metodele noi și cele redefinite.
Exemplul urm ător este o clas ă Stiva , deriva tă din clasa MyVector , pornind de la observa ția că o stiv ă este
un caz particular de vector. Diagrama este generată de NetBeans din codul sursă următor:

IE.07.4 Rela ții de asociere în UML
Relația de asociere (compunere sau delegare) dintre două clase se reprezintă printr -o săgeată orizontală de la
clasa ob ținută prin delegare la clasa delegat, cu un romb în punctul de plecare. class Stiva extends MyVector {
public void push (Object item) {
add (item);
}
public Object pop() {
if (empty( )) return null;
Object obj = data[count -1];
removeAt(count);
return obj;
}
public boolean empty () {
return size( )==0;
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 99 – UML deosebe ște două tipuri de asocieri: compozi ție și agregare. Diferen ța este aceea că rombul este alb
(agregare) sau negru (compozitie). In Java cele două tipuri sunt identice dar în C++ pot fi diferente.
Asocierile UML pot avea o multiplicitate, dacă în clasa compusă există un vector sau altă colec ție de obiecte
din clasa con ținută. Multiplicitatea se poate e xprima în mai multe moduri:
 cifră = numărul exact de variabile în colec ție
 Domeniu (2..5) = orice număr între aceste limite
 Asterisc (*) = oricâte variabile (posibil zero)

Exemplu de asociere a unei clase cu ea însă și: un nod de arbore binar con ține do uă referin țe către nodurile fii
(stânga și dreapta):
public class BinaryTreeNode {
private BinaryTreeNode leftNode;
private BinaryTreeNode rightNode;
}

In cazul unui nod de arbore multic ăi cu num ăr nelimitat de fii în locul lui ‗2‘ va fi caracteru l asterisc ‗*‘.

Exemplu de asociere prin delegare între clasa Stiva și clasa MyVector :

Diagrama de clase UML pentru
codul sursă anterior:

publi c class Stiva {
private MyVector items; // variabila “delegat”
public Stiva( ) { items = new MyVector(); }
public void push (Object item) {
items.add(item);
}
public Object pop( ) {
int len = items.size( );
if (len == 0)
throw new EmptyStackException();
Object obj = items.get (len – 1);
items.removeAt (len – 1);
return obj;
}
public boolean empty( ) { return (items.size( ) == 0); }
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 100 – IE.07.5 Rela ția de includere în UML
In Java se poate defini o clas ă interioar ă ca membru al unei clase exterioare în scopul de a simplifica
comunicarea între clasa exterioar ă și clasa inclus ă. Relația de includere apare în reprezentarea UML printr -o
linie cu extremitate diferit ă în clasa exterioar ă (un '+' într-un cerculet).

Codul Java care a stat la baza acestei diagrame:

public class ObjList {
// clasa inclusa ptr nod de lista
class ListNod {
Object val;
ListNod leg;
public ListNod (Object v) {
val=v; leg=null;
}
}
// membrii ai clasei exterioare
protected ListNod cap; // inceput de lista
public ObjList() { // constructor
cap=new ListNod("");
}
public String toString ( ) {
String aux=""; ListNod p= cap.leg;
while ( p != null ) {
aux=aux + p.val.toString()+" ";
p=p.leg;
}
return aux;
}
public void add (Object v) {
ListNod nou= new ListNod(v);
ListNod p=cap;
while (p.leg != null )
p=p.leg;
p.leg=nou;
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 101 – IE.07.6 Clase abstracte și interfe țe în UML
Clasele abstracte și interfe țele sunt privite în UML ca orice altă clasă (instan țiabilă) și nu au o reprezentare
diferită de cea a claselor. Singura diferen ță este aceea că numele clasei este precedat de ―stereotipul‖
<<abstract>> sau <<interface>> și că numele metodelor abstracte sunt scrise înclinat (cu ―italice‖). Pentru o
clasă abstractă numele clasei poate fi scris și el cu italice, fără a mai fi precedat de <<abstract>>.
In exemplul al ăturat clasa StackList moșteneste mai multe metode definite în clasa AbstractList , mai
puțin metodele get() și size() care trebuie definite. Opera țiile cu stiva sunt delegate c ătre obiectul "stack―
al cărui tip este stabilit l a instan țierea clasei StackList .

Urmează un e xemplu cu o clasă abstractă și cu o interfa ță:

class StackList extends AbstractList {
private AbstractList stack;
public StackList (List list) {
stack=(AbstractList)list;
}
public Object push (Object obj) {
stack.add (0,obj);
return obj;
}
public Object pop () {
Object obj= get(0);
stack.remove(obj);
return obj;
}
public int size() {
return stack.size();}
public Object get (int i) {
return stack.get(i); }
}
class UseStack {
public static void main (String arg[]) {
StackList st1 = new StackList
(new ArrayList());
StackList st2 = new StackList
(new LinkedList());
String d[] = { "1","2","3","4","5"};
for (int i=0;i<d.length;i++) {
st1.push(d[i]); st2.push (d[i]);
}
System.out.println (st1+" \n"+st2);
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 102 –

Clasa iterator:

class ArraySet extends AbstractSet {
Object a[]; int n,nmax;
public ArraySet (in t max) {
a= new Object[max]; nmax=max; n=0;
}
public boolean add(Object obj) {
if ( contains(obj)) return false;
if ( n==nmax) throw new OutOfMemoryError();
a[n++]=obj;
return true;
}
public int size( ) {return n; }
publi c Iterator iterator() {
return new ArrayIter (this);
}
}
class ArrayIter implements Iterator {
private int k; private ArraySet as;
ArrayIter( ArraySet set) { k=0; as=set; }
public Object next() {
if (k < as.n) return as.a[k++];
else return null;
}
public boolean hasNext() { return k < as.n; }
public void remove () { throw new UnsupportedOperationException(); }
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 103 – IE.07.7 Studiu de caz
Aplica ția men ține o agenda personal ă cu numere de telefon și alte date despre o serie de persoane .
Desenul următor ilustrează diverse cazuri de utilizare a aplica ției în versiunea UML:

Diagrama static ă de clase:

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 104 –
Diagrama la nivel
conceptual:

Diagrama clasei Person :

Diagrama clasei AddressBook :

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 105 –

Diagram ele
opera țiilor
addPerson()
saveAddressBook()

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 106 – Capitolul IE.08. Șabloane de proiectare cu clase
Cuvinte cheie
Șablon de proiectare, Iterator, Observator, Adaptor,
Compozit, Decorator, Fabric ă de obiecte, Fabric ă abstract ă
MVC(Model -View -Controller), Injectarea d ependen țelor

IE.08.1 Proiectarea orientat ă pe obiecte
Proiectarea unei aplica ții cu obiecte înseamn ă selectarea unor clase existente și definirea unor noi clase care,
prin instan țiere vor produce obiectele care vor realiza func ționalitatea aplica ției. Sta bilirea claselor necesare
și a rela țiilor dintre ele este o problem ă cu mai multe solu ții posibile și care necesit ă cuno ștințe din domeniul
proiect ării cu obiecte. Principalele obiective urm ărite în proiectare sunt satisfacerea cerin țelor beneficiarilor
și ușurința între ținerii și modific ării ulterioare a codului surs ă; obiective legate între ele doarece și cerin țele
se modific ă în timp, pe m ăsura folosirii aplica ției.
Alt obiectiv al proiect ării este optimizarea performan țelor aplica ției, ca timp de execu ție și ca memorie
utilizat ă; dar acesta vine de multe ori în contradic ție cu obiectivul u șurinței de extindere și de adaptare a
aplica ției la noi cerin țe. Un program compact, cu pu ține obiecte puternic cuplate, este în general mai greu de
modificat decât u n program cu mai multe obiecte slab cuplate și cu mai multe linii surs ă.
Se consider ă de multe ori c ă scopul unei bune proiect ări este ob ținerea unei aplica ții care s ă evite
următoarele defecte (caracteristici ale unei proiect ări deficitare): rigiditate, f ragilitate și imobilitate. Un
sistem de clase este rigid dac ă este greu de modificat datorit ă dependen țelor dintre clase, mai ales a celor
propagate (transitive). Un sistem fragil este cel în care modific ările produc erori nea șteptate. Un sistem
imobil are p ărți dificil de reutilizat în alte aplica ții datorit ă dependen țelor de alte p ărți ale aplica ției.
Pentru ob ținerea acestor calit ăți se recomand ă câteva principii ale proiect ării orientate obiect:
– Principiul responsabilit ății unice (SRP= Single Respon sability Principle ): Fiecare func ție din sistemul
proiectat trebuie s ă corespund ă unei clase; modific ări într-o clas ă din sistem nu trebuie s ă conduc ă la
modific ări în celelalte clase.
– Principiul deschis -închis (OCP = Open -Close principle ): O clas ă trebu ie să fie deschis ă pentru extinderi dar
închis ă pentru modific ări ale clasei; ad ăugarea de func ții unei clase nu se va face prin modificarea clasei ci
prin reutilizarea ei în alte clase (prin derivare sau delegare ).
– Princip iul substitu ției de tipuri (LS P=Liskov Substitution Principle ): Inlocuirea unui subtip S cu tipul mai
general T nu trebuie s ă afecteze comportarea programului; nu orice ierarhie de clase corespunde unei ierarhii
de tipuri.
– Principiul separ ării interfe țelor (ISP= Interface Segregation Principle ): Dependen țele dintre dou ă clase
trebuie s ă se bazeze pe o interfa ță minim ă; un client nu trebuie s ă depind ă de o interfa ță pe care nu o
folose ște.
Principalele recomandări care rezultă din analiza so luțiilor de proiectare confirmate de practic ă sunt:
– Compozi ția (delegarea) este preferabi lă de multe ori extinderii (derivăr ii).
– Proiectarea cu interfe țe și clase abstracte este prefera bilă față de proiectarea cu clase concrete, pentru că
permite separarea utilizării de implementare.
– Este reco mandată crearea de clase și obiecte suplimentare, cu rol de intermediari, pentru decuplarea unor
clase cu roluri diferite

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 107 – IE.08.2 Șabloane de proiectare
Experien ța acumulată în realizarea unor aplica ții cu clase a condus la recunoa șterea și inventarierea unor
scheme de proiectare sau șabloane de proiectare ( Design Patterns ), adic ă a unor grupuri de clase și obiecte
care cooperează pentru realizarea unor func ții. In cadrul acestor scheme exisă clase care au un anumit rol în
raport cu alte clase și care au primit un nume ce descrie acest rol; astfel există clase iterator, clase observator,
clase fabrică de obiecte, clase adaptor s.a. Dincolo de detaliile de implementare se pot identifica clase cu
acelea și responsabilită ți în diferite aplica ții.
Câteva defin ții posibile pentru șabloanele de proiectare folosite în aplica ții cu clase:
– Soluții optime pentru probleme comune de proiectare ( Best practices ).
– Abstrac ții la un nivel superior claselor, obiectelor sau componentelor.
– Scheme de comunicare (de intera cțiune) între clase și obiecte.

Argumentul principal în favoarea studierii și aplicării schemelor de clase este acela că aplicarea acestor
scheme conduce la programe mai ușor de modificat. Aceste obiective pot fi atinse în general prin clase
(obiecte) ―sl ab‖ cuplate ( care știu cât mai pu țin unele despre altele) și prin introducerea de obiecte
suplimentare fa ță de cele rezultate din analiza aplica ției. Deși efortul de scriere ini țială a codului folosind
șabloanele de proiectare ar putea fi mai mare, efort ul de men ținere ulterioar ă a codului este mai mic.

O clasificare uzuală a schemelor de proiectare distinge trei categorii:
– Scheme de creare (Creational patterns ) prin care se generea ză obiectele necesare.
– Scheme structurale ( Structural patterns ), care grupea ză mai multe obiecte în structuri mai mari.
– Scheme de intera cțiune ( Behavioral patterns ), care definesc comunicarea între clase.

O parte dintre aceste șabloane de proiectare sunt utilizate și în clasele Java din bibliotecile SDK: iterator,
adaptor, observator, MVC, fabric ă de obiecte ș.a. Multe din șabloanele de proiectare sunt aplicate și pot fi
exemplificate prin aplica ții Java cu interfa ță grafică.

IE.08.3 Șablonul Singleton

Uneori este nevoie de un singur obiect de un anumit tip, deci tre buie interzis ă instan țierea repetat ă a clasei
respective, numit ă clasă Singleton . Exist ă câteva solu ții:
– O clas ă fără constructor public, cu o metod ă static ă care produce o referin ță la unicul obiect posibil.
– O clas ă static ă inclus ă și o metod ă statică (în aceea și clas ă exterioar ă) care instan țiază clasa (pentru a -i
transmite un parametru).
Un exemplu este metoda createEtchedBorder() din clasa BorderFactory care creeaz ă referin țe la un obiect
chenar unic :

Variante de implementare :
a) Metoda are ca rezultat o referin ță la un obiect creat la înc ărcarea clasei:
JButton b1 = new JButton (“Etched1”), b2= new JButton(“Etched2”);
Border eb = BorderFactory.cre ateEtchedBorder();
b2.setBorder (eb);

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 108 –

Clasa BorderFactory (pachetul javax.swing ) reune ște mai mai multe metode ce produc obiecte chenar de
diferite forme pentru componentele vizuale Swing. Toate obiectele chenar apar țin unor clase care respect ă
interfa ța comun ă Border și care sunt fie clase predefinite, fie clase definite de utilizatori. Exemple de clase
chenar predefinite: EmptyBorder, LineBorder, BevelBorder, TitleBorder și CompundBorder .
b)- Metoda creeaz ă un obiect nu mai la primul apel, dup ă care nu mai instan țiază clasa :

Metoda care creează obiectul unic este o metodă fabrică de obiecte, dar care fabrică un singur obiect.
Alte exemple pot fi întâlnite în clasa Collections (neinstan țiabilă) pentru a se crea obie cte din colec ții mai
speciale : colec ții cu un singur obiect ( SingletonSet, SingletonList, SingletonMap ) și colec ții vide
(EmptySet, EmptyList, EmptyMap ). Exemplu:

public class Collections {
public static Set singleton(Object o) {
return new SingletonSet(o);
}
private static class SingletonSet extends AbstractSet {
private Object element;
SingletonSet(Object o) {element = o;}
public int size() {return 1;}
public boolean contains(Object o) {return eq(o, element);}
… // public Iterator iterator() { … }
}
. . .
}
// utilizare: crearea unui vector de multimi cu câte un element initial
sets = new ArrayList (n);
for (int i=0;i<n;i++)
sets.add (new TreeSet (Collections.singleton ( new Integer(i) ) ));
public class BorderFactory {
private BorderFactory () , – // neinstantiabilă
static final EtchedBorder sharedEtchedBorder = new EtchedBorder();
public static Border createEtchedBorder() {
return sharedEtchedBorder;
}
… // alte metode
}

public class BorderFactory {
private EtchedBorder sharedBorder= null;
public Border createEtchedBorder() {
if (sharedBorder==null)
sharedBord er = new EtchedBorder()
return sharedBorder;
}

}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 109 –
IE.08.4 Șablonul Iterator
Un iterator este un mecanism de enumerare a componen telor unui obiect agregat, cum ar fi o colec ție de
obiecte sau un text format din cuvinte , într -un mod unitar și independent de particularit ățile colectiei .
Desenul următor pune în eviden ță principala metod ă a unui iterator next() , care furnizeaz ă următorul obiect
din grup, la care se adaug ă în Java și metoda hasNext() care spune dac ă mai sunt obiecte în grup sau nu. In
plus, se vede c ă exist ă o interfa ță iterator și, posibil, o interfaț ă pentru obiectele agregat.

Obiectul iterator poate fi creat direct prin instan țierea unei clase, cum este StringTokenizer , iar obiectul
agregat (de tip String ) este primit ca argument de constructorul obiectului iterator. Clasa StringTokenizer
implementeaz ă interfa ța Enumeration . Exemplu :

Pentru colec țiile din Ja va obiectul iterator este creat printr -o metod ă din clasa colec ție (metoda elements()
sau iterator() ) și nu prin instan țiere direct ă, deoarece un iterator este asociat unei colec ții și nu poate fi creat
decât dup ă crearea colec ției. Aceste metode ilustreaz ă șablonul ―metod ă fabric ă de obiecte‖:

In Java mecanismul iterator a evoluat în timp de la interfa ța Enumeration cu dou ă metode, la interfa ța
Iterator cu trei metode și la o form ă simplificat ă de iterare prin colec ții si vectori, ce corespunde unei
instruc țiuni "for each" din alte limbaje. Pentru liste Java exist ă un iterator bidirec țional cu metode de
înaintare ( next() ) și de revenire la elementul anterior ( previous() ). De observat c ă pentru mul țimi iteratorul
este singura posibilitate de acces la e lementele mul țimii deoarece nu este posibil accesul prin indici (nu
exist ă noțiunea de pozi ție a unui element în mul țime).
Implementarea unui iterator folose ște un cursor care depinde de tipul obiectului agregat: pentru extragere de
cuvinte ( tokens ) cursor ul este un indice în cadrul textului, pentru vectori cursorul este un indice întreg iar
pentru liste înlănțuite cursorul este un pointer (o variabil ă referin ță în Java). Exemplu:
String str = " 1, 2 . 3 ; 4. ";
StringToken izer st = new StringTokenizer(str," .,;");
while (st.hasMoreElements())
System.out.println (st.nextElement());

Vector v = new Vector();
for (int i=1; i<9;i++) v.add(i);
Enumeration e = v.elements();
while (e.hasMoreElements())
System.out.print (e.nextElement()+",");

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 110 –

IE.08.5 Șablonul Observ ator
In această schemă există un obiect observat care poate suferi diverse modificări și unul sau mai multe
obiecte observator, care trebuie anunțate (―notificate‖) imediat de orice modificare în obiectul observat,
pentru a realiza anumite acțiuni. Obiectul observat conține o listă de referințe la obiecte observator și, la
producerea unui eveniment, apelează o anumită metodă a obiectelor observator înregistrate la el.
Desenul urm ător ilustreaz ă componentele schemei Observator:

Schema observat -observator a generat clasa Observable și inte rfața Observer din pachetul java.util .
Programatorul de aplicație va defini una sau mai multe clase cu rol de observator, compatibile cu interfața
Observer . Subiectul supus observa ției con ține metode de adăugare sau de eliminare a unor observatori. In
desen aceste metode se numesc attach() și detach() , iar în Java se numesc addObserver() și deleteObserver() ;
metoda notify() din desen se numeste notifyObservers() in clasa Observable .
Interfa ța Observer conține o singur ă metod ă update() , apelat ă de un obiect ob servat la o schimbare în starea
sa și care poate interesa obiectele observator :
public interface Observer { void update(Observable o, Object arg); }

Clasa Observable nu este abstract ă dar nici nu este direct utilizabil ă; programatorul de aplica ție va de fini o
class StrTok {
String str; // sirul analizat
int crt; // pozitia curenta in sir
// constructor
public StrTok (String s) {
str=s; crt=0;
}
public boolean hasMoreTokens () {
return crt < str.length();
}
public String nextToken () {
while ( crt < str.length() && str.charAt(crt)==' ')
++crt; // ignora spatii
int start= crt; // inceput de cuvânt
crt= str.indexOf (' ',start); // sfârsit de cuvânt
if ( cr t < 0) crt= str.length(); // ultimul cuvânt din sir
return str.substring (start,crt);
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 111 – subclas ă care preia toate metodele clasei Observable și adaug ă o serie de metode specifice aplica ției, care
apeleaz ă metodele superclasei setChanged() și notifyObservers() . Metoda notifyObservers() apeleaz ă
metoda update() pentru to ți observatorii i ntrodu și în vectorul "observers". Metoda getChanged() poate fi
folosit ă pentru a vedea dac ă s-a modificat starea subiectului observat, iar metoda setChanged() modific ă o
variabil ă intern ă pentru a semnala modificarea st ării subiectului observat.
Urmează un exemplu simplu care folose ște clasele Java pentru șablonul Observator:

O alt ă utilizare a șablonului Observator este în aplica țiile Java cu interfa ță grafică: practic toate obiectele
grafice Swing sunt obiecte observabile și genereaz ă evenimente Swing ca urmare a gesturilor operatorului
(clic pe buton de mouse sau ap ăsare tast ă de consol ă); producerea unui eveniment este notificat ă unor obiecte
numite ―ascult ător‖ (cu rol de observator) înregistrate anterior la sursa de evenimente (sub iectul supus
observa ției).
Ordinea în care sunt notifica ți observatorii sau ascult ătorii nu este aceea și cu ordinea de ad ăugare a lor la
subiectul observat (în Java este chiar ordinea invers ă celei de la ad ăugare).

IE.08.6 Șablonul Adap tor
Schema "adapto r" permite adaptarea apelurilor de
metode dintr -o clas ă "Client" la metodele diferite
ca prototip dar echivalente ca efect ale unei clase
"Adaptee" . Clientul apelează metoda operation()
din adaptor, care va delega execu ția ei, prin
variabila adaptee, c ătre metoda
adaptedOperation() .
class Observat extends Observable {
private int x;
public void setValue (int x) {this.x=x;}
public int getValue() {return x;}
public void change() { setChanged(); notifyObservers(x); }
}

class Observator implements Observer {
private String nume;
public Observator(String nume){ this.nume=nume;}
public void update( Observable obs, Object x ) {
System.out.println (nume + " " +x);
}
}
class Main {
public static void main (String args[]) {
Observat subiect = new Observat();
Observator ob1 = new Observator("unu");
Observator ob2 = new Observator("doi");
subiect.addObserver(ob1); subiect.addObserver(ob2);
for ( int i=1;i<10;i++){
subiect.setValue(i); subiect.change();
}
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 112 – Schema "Adaptor" o întâlnim la clasele InputStreamReader si OutputStreamWriter care sunt clase
adaptor între clasele ―Stream‖ și clasele ―Reader -Writer‖. Clas a adaptor InputStreamReader extind e clasa
Reader (Writer ) și con ține o variabil ă de tip InputStream (OutputStream ); o parte din opera țiile impuse
de superclas ă sunt realizate prin apelarea opera țiilor pentru variabila flux (prin ―delegare‖). Este deci un alt
caz de combinare între extindere și agregare. Un obiect InputStre amReader poate fi folosit la fel ca un
obiect Reader pentru citirea de caractere, dar în interior se citesc octe ți și se convertesc octe ți la caractere.
Exemplul următor este o clasa adaptor între interfa ța Iterator și interfa ța mai veche Enumeration , care
permite utilizarea noilor metode pentru clase mai vechi cum este Hashtable .

In Java no țiunea de clas ă adaptor este folosit ă și pentru clase care implementeaz ă prin metode cu efect nul o
interfa ță cu mai multe metode. In acest ultim caz se sim plific ă definirea unor clase care implementeaz ă
numai o parte din metodele interfe ței. De exemplu, interfa ța MouseInputListener conține metode apelate la
acțiuni pe mouse (apăsare sau eliberare buton: mouseClicked(), mousePressed() , mouseReleased() ) și
metode apelate la deplasare mouse (mouseMoved(), mouseDragged() ). Clasa MouseInputAdapter oferă o
implementare nul ă pentru toate cele 7 metode.
IE.08.7 Șablonul Compozit
Schema "compozit" se aplic ă structurilor ierarhice, în care orice component ă este compu să din alte
componente. Un arbore general (multic ăi) este imaginea cea mai bun ă pentru o structur ă compozit ă:
elementul "Leaf" din figur ă este un nod frunz ă, iar elementul "Component" este un nod interior, care poate fi
privit ca r ădăcina unui subarbore. F iecare component ă (nod) poate fi tratat ca un obiect separat sau ca un
grup de obiecte, folosind aceea și interfa ță. Exemple concrete de structuri compozit sunt arbori DOM pentru
documente HTML/XML, arbori pentru expresii din limbajele de programare, liste de produse BOMP (Bill of
Material Processor) ș.a.

public class EnumAdapter implements Iterator {
Enumeration enum;
public EnumAdapter (Enumeration enum) { this.enum=enum; }
public Object next() {
return enum.nextElement();
}
public boolean hasNext() {
return enum.hasMoreElements ();
}
public void remove () { }
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 113 – Exemplul următor arat ă cum se poate face evaluarea unor expresii aritmetice care con țin doar numere întregi
ca operanzi și cațiva operatori binari, expresii reprezentate prin arbori. Interfa ța "Exp ression" din program
corespunde interfe ței "Component" din figur ă iar metoda eval() din program corespunde metodei
operation() din figur ă.

IE.08.8 Șablonul Decorator
Schema "decorator" permite ad ăugarea
dinamic ă (la execu ție) de noi responsabilit ăți
și moduri de comportare unor obiecte, atunci
când nu se poate folosi doar derivarea în acest
scop. Se spune c ă o clas ă prime ște noi
"decora țiuni" (facilit ăți) de la clasa decorator.
Un decorator nu adaug ă funcții noi clasei
decor ate dar modific ă acțiunea unor metode
existente prin delegare c ătre metode ce provin
din obiectul transmis ca argument la
construirea unui obiect decorator ( și care înlocuie ște o variabil ă de tip interfa ță din clasa decorator).

interface Expression { int eval(); }
public class Expr implements Expression {
protected TNode node;
publ ic Expr(TNode node) { this.node=node;}
public int eval () {
if (node==null) return 0;
if (node.isLeaf()) return new Leaf(node).eval();
else return new BinExpr(node).eval();
}
}
class Leaf extends Expr implements Expression {
public Leaf(TNode node){ super(node);}
public int eval() {
return (Integer)node.getUserObject();
}
}
class BinExpr extends Expr implements Expression {
public BinExpr(TNode node){ super(node);}
private Expr left,right;
public int eval(in t op) {
return evalBin( op,left.eval(), right.eval() );
}
int evalBin (int op, int lval, int rval){
switch (op){
case '+': return lval+rval;
case ' -': return lval -rval;
case '*': return lval*rval;
}
retur n 0;
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 114 – Clasele filtru de I/E din Java fac parte din categoria claselor decorator . Principalele clase filtru de I/E sunt:
FilterInputStream, FilterOutputStream și FilterReader, FilterWriter . Clasele de tip filtru sunt clase
intermediare, din care sunt derivate clase care adaug ă opera ții specifice (de ―prelucrare‖): citire de linii de
text de lungime variabil ă, citire -scriere de numere în format intern, scriere numere cu conversie de format
s.a. O clas ă anvelop ă de I/E con ține o variabil ă de tipul abstract OutputStream sau InputStream , care va fi
înlocuit ă cu o variabil ă de un tip flux concret ( FileOutputStream s.a.), la construirea unui obiect de un tip
flux direct utilizabil. Clasa FilterInputStream este derivat ă din InputStream și con ține o variabil ă de tip
InputStream . Metoda read() este o metod ă polimorfic ă, iar selectarea metodei necesare se face în functie de
tipul concret al variabilei "in" (transmis ca argument constructorului). Metoda read() din clasa
FilterInputStream preia functionalitatea metodei read() din clasa delegat, sau ―del eagă‖ operatia de citire
către clasa folosit ă la construirea obiectului filtru.

Clasele DataInputStream , BufferedInputStream , PushbackInputStream, LineNumberInputStream
sunt derivate din clasa FilterInputStream si contin metode de prelucrare a d atelor citite. Cea mai folosit ă
este clasa DataInputStream care adaug ă metodelor de citire de octeti mostenite si metode de citire a tuturor
tipurilor primitive de date: readLine(), readInt(), readFloat() etc.

IE.08.9 Șablonul Fabric ă de obiec te
Crearea de noi obiecte se face de obicei prin instan țierea unei clase cu nume cunoscut la scrierea
programului. O solu ție mai flexibil ă pentru crearea de obiecte este utilizarea unei fabrici de obiecte, care
lasă mai mult ă libertate în detaliile de imp lementare a unor obiecte cu comportare predeterminat ă. O fabric ă
de obiecte ( Object Factory ) permite crearea de obiecte de tipuri diferite, dar toate subtipuri ale unui tip
comun (interfa ță sau clas ă abstract ă). public class FilterInputStream extends InputStream {
protected InputStream in;
protected FilterInputStream (InputStream in) { // constructor
this.in=in; // stabilire tip obiect "flux de I/E "
}
// citirea unui octet
public int read () throws IOException {
return in.read (); // citirea depinde de tipul fluxului
}
… // alte metode
}

// "decorare" repetata, cu adaugare de facilitati la op. de I/E
public static void main (String arg[]) throws IOException {
FileInputStream fis= new FileInputStream (arg[0]); // flux cu suport
fisier
BufferedInputStream bis = new BufferedInputStream (fis); // plus buffer de
citire
DataInputStream dis = new DataInputStream ( bis); // plus operatii de citire de
linii si numere
String linie;
while ( (linie=dis.readLine()) != null)
System.out.println (lnis.getLineNumber()+" "+linie);
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 115 – Utilizarea fabricilor de obiecte poate fi p rivită ca un pas înainte pe calea separa ției interfa ță-implementare, în
sensul c ă permite oricâte implement ări pentru o interfa ță, cu orice nume de clase și cu orice date ini țiale
necesare fabric ării obiectelor.
O fabric ă de obiecte se poate realiza în dou ă forme:
– Ca metod ă fabric ă (de obicei metod ă static ă) dintr -o clas ă, care poate fi clasa abstract ă ce define ște tipul
comun al obiectelor fabricate. Aceast ă soluție se practic ă atunci când obiectele fabricate nu difer ă mult între
ele.
– Ca o clas ă fabric ă, atunci când exist ă diferen țe mari între obiectele fabricate. Alegerea (sub)tipului de
obiecte fabricate se poate face fie prin parametri transmisi metodei fabric ă sau constructorului de obiecte
―fabric ă‖, fie prin fi șiere de propriet ăți (fișiere de con figurare).
O metod ă fabric ă poate fi o metod ă static ă sau nestatic ă. Metoda fabric ă poate fi apelat ă direct de
programatori sau poate fi apelat ă dintr -un constructor, ascunzând utilizatorilor efectul real al cererii pentru
un nou obiect. Desenul urm ător c orespunde unei metode fabric ă din clasa Creator având ca rezultat un
obiect de tipul general Product .

Se folosesc metode fabric ă atunci când tipul obiectelor ce trebuie create nu este cunoscut exact de
programator, dar poate fi dedus din alte infor mații furnizate de utilizator, la execu ție. Rezultatul metodei este
de un tip interfa ță sau clas ă abstract ă, care include toate subtipurile de obiecte fabricate de metod ă.
Un exemplu de metod ă fabric ă controlabil ă prin argumente este metoda getInstance() din clasa Calendar ,
care poate crea obiecte de diferite subtipuri ale tipului Calendar , unele necunoscute la scrierea metodei dar
adăugate ulterior. Obiectele de tip dat ă calendaristic ă au fost create ini țial în Java ca instan țe ale clasei
Date , dar ult erior s -a optat pentru o solu ție mai general ă, care s ă țină seama de diferitele tipuri de calendare
folosite pe glob. Clasa abstract ă Calendar (din ―java.util‖) con ține câteva metode statice cu numele
getInstance() (cu și fără parametri), care fabric ă obiecte de tip Calendar . Una din clasele instan țiabile
derivat ă din Calendar este GregorianCalendar , pentru tipul de calendar folosit în Europa. Metoda
getInstance() fără argumente produce un obiect din cel mai folosit tip de calendar:
public static Calend ar getInstance() { return new GregorianCalendar(); }

Obiectele de tip Calendar se pot utiliza direct sau transformate în obiecte Date :
Calendar azi = Calendar.getInstance();
Date now = azi.getTime(); // conversie din Calendar in Date
System.out.println (now);

Metoda getInstance() poate avea unul sau doi parametri, unul de tip TimeZone și altul de tip Local , pentru
adaptarea orei curente la fusul orar (TimeZone) și a calendarului la pozi ția geografic ă (Local). Tipul
obiectelor fabricate este determinat de ace ști parametri.

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 116 – Alte metode fabric ă care produc obiecte adaptate particularit ăților locale (de țară) se afl ă în clase din
pachetul ―java.text‖: NumberFormat , DateFormat , s.a. Adaptarea se face printr -un parametru al metodei
fabric ă care precizeaz ă țara și este o constant ă simbolic ă din clasa Locale . Afișarea unei date calendaristice
se poate face în mai multe forme, care depind de uzan țele locale și de stilul dorit de utilizator (cu sau f ără
numele zilei din s ăptămână, cu nume complet sau prescurtat pentru lun ă etc.). Clasa DateFormat conține
metoda getDateInstance() care poate avea 0, 1 sau 2 parametri și care poate produce diferite obiecte de tip
DateFormat . Exemple:

IE.08.10 Șablonul Fabric ă abstractă
Obiectele de tip fabric ă pot fi la rândul lor ―fabricate‖ printr -o metodă, atunci când ele au subtipuri ale unui
tip comun . Fabrica de fabrici se numește fabric ă abstract ă (AbstractFactory ). Ideea este de a decupla
beneficiarul (clientul) unor servicii de particularită țile furnizorului de servicii, atunci când există mai multi
furnizori posibili sau se anticipează că pot apărea și alte implementări pentru fabrica de obiecte.
Desenul următor arată cum clientul folose ște un tip general (abstract) pentru fabrica de obiecte, obiectele
produse fiind și ele de un tip mai general (interfa ță sau clasă abstractă):

Exemple de servi cii: acces la orice baz ă de date rela țional ă (JDBC), comunicarea prin mesaje (JMS), analiz ă
de fișiere XML . Un parser XML de tip DOM este un program care analizează documente XML, semnalează
erori formale și creează un arbore echivalent documentului XML. E xistă mai multe programe parser de la
diferi ți furnizori și este posibil să apară si alte parsere DOM mai performante în viitor. Deci nu există o
singură clasă parser DOM dar există o interfa ță numită DocumentBuilder ce corespunde interfe ței
AbstractProduc t din desenul anterior. Toate clasele parser DOM trebuie să respecte această interfa ță, cu
metode de acces la arborele DOM. Numele și numărul acestor clase parser nu este cunoscut, deci nu se poate
obține un obiect parser prin instan țierea unei clase. Nume le clasei parser nu apare în aplica ție pentru a nu fi
necesară modificarea surselor aplica ției la schimbarea tipului de parser (decuplare utilizare de
implementare). Numele clasei parser este determinat la execu ția aplica ției de către o metodă fabrică
newI nstance() , care selectează acest nume după un algoritm: dintr -un fi șier de proprietă ți sau o clasă parser
implicită dacă nu apare alta în fi șierul de proprietă ți. Fabrica de clase parser este DocumentBuilderFactory
și corespunde clasei AbstractFactory din desen; metoda newInstance() corespunde unei metode
createProduct() din desen.
Date date = new Date();
DateFormat df1 = DateFormat.getDateInstance ();
DateFormat df2 = DateFormat.getDateInstance (2, Locale.FRENCH);
System.out.println ( df1.format(date)); //luna, an, zi cu luna in engleza
System.out.println ( df2.format(date)); // zi,luna,an cu luna in franceza

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 117 – Secven ța urm ătoare arată cum se ob ține și cum se folose ște un obiect parser DOM . Acest cod nu con ține
nici un nume de clas ă parser; obiectul parser este produs de metod a fabric ă newDocumentBuilder() aplicat ă
unui obiect produs de metoda fabric ă static ă newInstance() .

IE.08.11 Șablonul Model -View -Controller
Schema MVC (Model -View -Controller) extinde schema "Observat -observator". Un obiect ―model‖ este un
obiect observat ( a scultat), care generează evenimente pentru obiectele receptor înregistrate la model.
Arhitectura MVC foloseste clase având trei roluri principale:
– Clase ―controller‖, cu rol de comandă a unor modificări în model ca urmare a unor evenimente externe .
– Clase ―model‖, cu rol de obiect observat care con ține date și prelucr ări ale acestor date.
– Clase ―view‖, cu rol de redare vizuală a stării modelului, fiind obiect observator al modelului.

Schema MVC a apărut în legătur ă cu programele de birotică (pen tru calcul tabelar și pentru editare de texte),
de unde și numele alternativ de Document -View -Controller . Separarea netă a celor trei componente (M, V si
C) permite mai multă flexibilitate în adaptarea și în extinderea programelor, prin u șurința de modific are
separată a fiecărei p ărți (în raport cu un program monolit la care legăturile dintre păr ți nu sunt explicite).
Astfel, putem modifica structurile de date folosite în memorarea foii de calcul (modelul) fără ca aceste
modificări să afecteze partea de pre zentare, sau putem adăuga noi forme de prezentare a datelor con ținute în
foaia de calcul sau noi forme de interac țiune cu operatorul (de exemplu, o bară de instrumente toolbar ).
Separarea de responsabilită ți oferită de modelul MVC este utilă pentru realiza rea de aplica ții sau păr ți de
aplica ții: componente GUI, aplica ții Web de tip client -server s.a.
Practic toate aplica țiile Web sunt construite în prezent după schema MVC, care se reflectă și în structura de
directoare a aplica ției. In aplica țiile Web par tea de model con ține clasele ce corespund tabelelor bazei de
date (inclusiv logica de utilizare a acestor date), partea de vizualizare corespunde interfe ței cu utilizatorul
(obiecte grafice afi șate în browser, valid ări asupra datel or introduse de operator , anima ție și alte efecte
vizuale), iar partea de comand ă (controller ) corespunde prelu ării cererilor HTTP și transmiterii r ăspunsurilor
corespunz ătoare (care include de obicei date din model ).
Unele componente Swing sunt construite după schema MVC și au în componen ța lor un obiect model :
JButton, JList, JTable, JTree ș.a. Modelul este o structură de date care poate genera evenimente și este
specific fiecărei componente: DefaultButtonModel, DefaultListModel, DefaultTableModel , etc.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInsta nce(); // obiect fabrică
DocumentBuilder builder = factory.newDocumentBuilder(); // obiect parser
Document doc = builder.parse( new File(arg[0]) ); // creare arbore DOM dintr -un fisier

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 118 – Exemplul care urmeaz ă folose ște dou ă butoane și un câmp text și este structurat dup ă arhitectura MVC.
Componenta model (clasa Model ) con ține ca date un num ăr întreg și genereaz ă evenimente la modificarea
valorii acestui num ăr. Clasa Controller trateaz ă evenimentele de la butoan e, prin care operatorul aplica ției
poate modifica starea modelului (valoarea din model). Clasa View reune ște toate componentele de interfa ță
grafic ă (butoane și câmp text) și modific ă afisarea din câmpul text ca urmare a modific ării modelului
(reflect ă starea modelului).
Clasa controler reune ște ascult ătorii la butoanele care controleaz ă starea modelului:

Clasa model generează eveniment PropertyChangeEvent la modificarea valorii memorate în model:

Clasa cu imaginea aplica ției:

class Controller implements ActionListener {
private Model m;
public Controller (Model m) { this.m=m; }
public void actionPerformed (ActionEvent evt){
JButton b = (JButton) evt.getSource();
String txt = b.getActionCommand();
int x= m.getElement();
if (txt.equals("+")) m.setElement (x+1);
if (txt.equals(" -")) m.setElement (x -1);
}
}
class Model extends JComponent {
int value; // date din model (proprietate a componentei JFC)
public void setElement (int x) { // la modificare proprietate
firePropertyChange ("value",value,value=x);
}
public i nt getElement () {return value; } // valoare proprietate
}
class View extends JFrame implements PropertyChangeListener {
JButton b1,b2;
JTextField t = new JTextField (6);
Model m; Controller c;
public View(Model m, Controller c) {
this.m=m; this.c=c;
b1= new JButton("+"); b2= new JButton(" -");
m.addPropertyChangeListener ("value",this);
setLayout (new FlowLayout());
add (t); add(b1); add(b2);
b1.addActionListener (c); b2.addActionListener (c);
setSize(200,100); setVisible(true);
}
// reactioneaza la evenimente din model
public void propertyChange (PropertyChangeEvent ev) {
int x = m.getElement(); t.setText (""+x);
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 119 –
Dependentele dintre clase sunt transmise aici prin contructori, dar în general se folosesc metode care transmit
unui obiect adresa unui alt obiect (injectare dependente prin metode):

E.08.12 Șablonul Injec tarea Dependen țelor
Un principiu general de p roiectare a aplica țiilor complexe este acela c ă între modulele unei aplica ții cuplajul
trebuie s ă fie cât mai slab, astfel ca înlocuirea unui modul cu un altul s ă nu necesite (multe) modific ări în
celelalte module care depind de cel înlocuit. Avantajele ap licațiilor formate din obiecte slab cuplate sunt:
Înlocuirea unui obiect cu altul care respect ă aceea și interfa ță este mai simpl ă și mai rapid ă, reutilizarea unor
părți dintr -o aplica ție în alte aplica ții este facilitat ă, la realizarea aplica ției pot lucr a mai mul te persoane
(echipe) în paralel, î ntreținerea (modificarea) codului este simplificat ă, testarea unitar ă a codului este
simplificat ă.
Cuplajul slab folose ște interfețe sau clase abstracte: un obiect ―vede‖ obiectele cu care colaboreaz ă ca
interfețe deci ca abstrac ții ale obiectelor reale. Soluțiile de realizare a unui cuplaj slab între obiectele care
comunic ă fac parte din schemele de proiectare bazate pe bune practici ( Design Patterns ) și au evoluat de la
fabrici de obiecte la localizarea servicii lor (Service Locator ) și la injectarea dependentelor (Dependency
Injection ). Schemele Service Locator si Dependency Injection nu se exclud reciproc, iar containerele Apache
Avalon și Google Guice injecteaz ă în aplicații referințe la obiecte ServiceLocator .
La injectarea dependentelor se vorbe ște despre o inversare a controlului, în sensul c ă gestiunea leg ăturilor
dintre componente nu mai revine aplica ției ci unui ―asamblor‖ de componente, numit container sau
framework , care ―injecteaz ă‖ în aplica ție referi nțele la obiectele apelate prin nume simbolice.
Relația dintre dou ă obiecte care colaboreaz ă este privit ă uneori ca rela ție dintre un obiect care ofer ă anumite
servicii ( Service Object ) și un obiect care foloseste sau consum ă aceste servicii ( Client Objec t). In testarea
unitar ă relația dintre obiecte este privit ă ca o dependen ță: un obiect (client) depinde de serviciile oferite de
un alt obiect (furnizor de servicii).
Ca exemplu vom considera un obiect filtru care selecteaz ă dintr -o colecție de obiecte pe cele care satisfac o
anumit ă condiție. Colecția complet ă de obiecte este produs ă de un obiect de tip DAO ( Data Access Object )
care extrage aceste date dintr -o baz ă de date sau dintr -un fi șier. In practic ă DAO este o clas ă abstract ă sau o
interfață, care s uport ă diverse implement ări ce depind de modul de acces la datele externe: JDBC, Hibernate,
JPA, JDO, etc. In cazul nostru interfa ța Dao are o singur ă metod ă findAll() , dar de obicei are mai multe
metode. Interfețele și client ul pentru serviciile Dao pot a răta astfel:

public static void main (String args[]) {
Model m = new Model ();
Controller c = new Controller (m);
View v = new View(m,c);
v.go(); // pornire aplicatie
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 120 –

Prin definirea interfe ței Dao s-a realizat o decuplare par țială a obiectului DaoClient de implementarea
efectiv ă a metodei findAll() și de organizarea datelor externe, care se poate modifica oricând f ără să necesite
modific ări și în clasa client. Putem avea diverse clase care implementeaz ă interfața Dao și care obțin datele
din fisier e text sau XML, dintr -un tabel SQL sau prin intermediul unui serviciu Web.
Pentru a ob ține o aplica ție executabil ă trebuie s ă putem lega un obi ect client de un obiect al unei clase
DaoImpl (care implementeaz ă interfața Dao) și de un obiect filtru. Aceast ă ―legare‖ (binding ) sau asamblare
de obiecte se face dup ă compilare (dar înainte de execu ție) și poate fi realizat ă de:
– Obiectul client, care folose ște o fabric ă de obiecte compatibile cu interfa ța Dao: schema Object Factory
– Obiectul client, care caut ă (―localizeaz ă‖) obiectul DaoImpl folosind un serviciu de nume și directoare
(compatibil JNDI pentru aplica ții Java) : schema Service Locator .
– Un container (un framework) care ―injecteaz ă‖ în obiectul client o referin ță către obiectul care
implementeaz ă interfața Dao: schema Dependency Injection .
In primele dou ă cazuri obiectul client mai depinde și de un alt obiect: fabrica de obiecte sau serv iciul de
localizare (care este un dic ționar ce asociaz ă nume cu referin țe la obiecte).
Exemplu de utilizare a unei fabrici de obiecte (ca metod ă static ă):

public interface Dao { // orice obiect Dao va contine metoda findAll
public List findAll ();
}
public interface Filter { // or ice obiect Filter va contine metoda accept
public boolean accept (Object obj);
}
public class DaoClient { // foloseste obiecte de tip Dao
private Dao dao;
public List findBy (Filter filter){
List all = dao.findAll ();
for (Iterator it= all .iterator();it.hasNext();)
if (! filter.accept(it.next())) // daca nu e acceptat de filtru
it.remove(); // se elimina din lista
return all;
}
}

class DaoFactory {
private static Dao dao= new DaoImpl();
public static Dao getInst ance() { return dao; }

}
// utilizare
public class DaoClient {
public List findBy (Filter f) {
Dao dao= DaoFactory.getInstance();
List all = dao.findAll ();

}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 121 – Obiectul care injecteaz ă dependen țele trebuie s ă dispun ă de informa ții despre obiecte le pe care le injecteaz ă
(nume și referințe); aceste informa ții se transmit de c ătre persoana care dezvolt ă aplicația fie prin fi siere de
configurare XML, fie prin adnot ări Java introduse în codul surs ă. Injectarea într -un obiect a unei
referințe către un alt obiect se poate face în trei moduri:
– Prin constructorul clasei ( Constructor Injection )
– Printr -o met odă a clasei ( Setter Injection )
– Printr -o interfață de injectare ( Interface Injection )
Injectarea prin constructor necesit ă adăugarea unui constru ctor cu argument de tip Dao:

Injectarea printr -o metod ă necesit ă adăugarea unei metode de modificare a variabilei ―dao‖:

Injectarea prin interfa ță necesit ă definirea unei interfe țe, iar c lasa client va implementa aceast ă interfață:

Exemplu de transmitere c ătre container a informa țiilor necesare injec ției (Spring framework):

Exemplu de utilizare a metodei findBy() în Spring :
public class DaoClient {
private Dao dao;
public DaoCli ent (Dao dao) { this.dao=dao;}
public List findBy (Filter filter) , … –
}
public class DaoClient {
private Dao dao;
public setDao (Dao dao) { this.dao=dao;}
public List findBy (Filter filter) , … –
}

public interface InjectDao { void injectDao (Dao dao); }
public class DaoClient implements InjectDao {
private Dao dao;
public void injectDao(Dao dao){ this.dao=dao; }
public List findBy (Filter f) , …-
}

<beans>
<bean id="DaoClient" class="spring.DaoClient">
<prope rty name="dao">
<ref local="Dao"/>
</property>
</bean>
<bean id="Dao" class="spring.DaoImpl">
<property name="file">
<value>date.txt</value>
</property>
</bean>
</beans>

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 122 –

Produsele de tip container ( Framework ) cu injectare dependen țe pentru aplica ții Java pot fi clasifi cate în:
– Produse simple, pentru verificarea aplicabilit ății schemei DI pe aplica ții cu cerin țe reduse:
Pico Container & NanoContainer, Avalon (Apache), HiveMind (Apache), s.a.
– Produse complexe, folosite efectiv pentru aplica ții reale:
Spring Framework, Guice (Google), Weld/ Web Beans (JBoss)
Pe platforma .Net (C#) exist ă de asemenea mai multe containere DI:
StructureMap, ObjectBuilder, SpringFramework.Net, Unity , MEF (Microsoft Extensibility Framework).
Tendințele mai noi în realizarea unor containere cu injectarea de dependen țe sunt:
– Utilizarea de adnot ări Java în locul fi șierelor de configurare pentru specificarea obiectelor care vor fi
injectate de container și a punctelor de injectare (JEE6, Guice, Spring Framework);
– Asocierea unui context (unui ciclu de viat ă) obiectelor injectate și gestiunea st ării obiectelor de c ătre
container, de unde și denumirea de CDI ( Context and Dependency Injection ) din JEE6.
– Inserția de c ătre container a unor secven țe de cod în puncte specificate, deci programare or ientat ă pe
aspecte (AOP) sau ―interceptori‖ sau orthogonal concerns . AOP (Aspect Oriented Programming ) înseamn ă
pe scurt injectarea unor secven țe pentru apelarea unor servicii în diverse puncte specificate de programator,
dar care nu corespund în general c u fluxul logic. Un exemplu este inser ția de apeluri c ătre un serviciu de
logger pentru înscrierea de mesaje într -un fișier de tip ―log‖ (jurnal de evenimente).

public void testWithSpring() throws Exception {
ApplicationContext ctx = new FileSystemXmlApplicationContext("spring.xml");
DaoClient finder = (Dao) ctx.getBean("Dao");
List result = finder.findBy( new MyFilter());
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 123 – Capitolul IE.09. Interfe țe grafice și programare orientat ă pe evenimente
Cuvin te cheie
Interfa ță grafic ă, Componente vizuale, Fereastr ă,
Componente atomice, Container Swing, Clase model,
Evenimente Swing ,Ascult ători, Programare bazatã pe evenimente

IE.09.1 Aplica ții cu interfa ță grafic ă
Comunicarea dintre un program de aplica ție și utilizatorul aplica ției poate folosi ecranul în modul text sau în
modul grafic. Majoritatea aplica țiilor actuale preiau datele de la operatorul uman în mod interactiv, printr -o
interfa ță grafică, pusă la dispozi ție de sistemul de operare gazdă. Interfa ța grafică cu utilizatorul (GUI =
Graphical User Interface ) este mai sigură și mai "prietenoasă", folosind atât tastatura cât și mouse -ul pentru
introducere sau selectare de date afisate pe ecran.
O interfa ță grafică simplă constă dintr -o singură fereastră ecran a aplica ției pe care se plasează diverse
componente vizuale interactive, numite și ―controale ‖ (controls ) pentru că permit operatorului să
controleze evolu ția programului prin introducerea unor date sau op țiuni de lucru (care, în mod text, se
transmi t programului prin linia de comandă). Uneori, pe parcursul programului se deschid și alte ferestre, dar
există o fereastră ini țiala cu care începe aplica ția. O fereastrã (Window) este o zonã dreptunghiularã din
ecran, cu nargini orizontale si verticale.
Programele cu interfa ță grafică sunt controlate prin evenimente externe, produse fie de apăsarea unei taste
fie de apăsarea unui buton de mouse. Un eveniment des folosit este cel produs de pozi ționarea cursorului pe
suprafa ța unui ―buton‖ desenat pe ecran și apăsare (clic) pe butonul din stânga de pe mouse. Tipul
evenimentelor este determinat de componenta vizuală implicată dar și de opera ția efectuată. De exemplu,
într-un câmp cu text terminarea unei linii de text (cu tasta Enter ) generează un tip de evenim ent, iar
modificarea unor caractere din text generează un alt tip de eveniment.
Un mare avantaj al limbajului Java, fa ță de limbaje ca C, C++ este acela că programele Java cu interfa ță
grafică nu depind de sistemul de operare gazdă, clasele și metodele fo losite fiind acelea și. Limbajul Java
permite, fată de alte limbaje, programarea mai simplă și mai versatilă a interfe ței grafice prin numărul mare
de clase și de facilită ți de care dispune. De exemplu, aspectul ( Look and Feel ) componentelor vizuale poate
fi ales dintre patru variante, indiferent de sistemul de operare gazdă.
In termenii specifici Java, componentele vizuale sunt de două categorii:
– Componente atomice , folosite ca atare și care nu pot con ține alte componente (un buton este un exemplu
de componentă atomică);
– Componente container , care grupează mai multe componente atomice și/sau containere. Componentele
container sunt și ele de două feluri:
– Containere de nivel superior ( top-level ) pentru fereastra principală a aplica ției;
– Contai nere intermediare (panouri), incluse în alte containere și care permit opera ții cu un grup de
componente vizuale (de exemplu, pozi ționarea întregului grup).
Componentele atomice pot fi grupate după rolul pe care îl au :
– Butoane de diverse tipuri: bu toane simple, butoane radio
– Elemente de dialog
– Componente pentru selectarea unei alternative (op țiuni)

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 124 – – Indicatoare de progres a unor activită ți de durată
– Componente cu text de diverse complexită ți: câmp text, zonă text, documente
– Panouri cu derulare verticală sau orizontală (pentru liste sau texte voluminoase)
– Meniuri și bare de instrumente
In programarea cu obiecte fiecare componentă a unei interfe țe grafice este un obiect dintr -o clasă predefinită
sau dintr -o subclasă a clasei de bibliot ecă. Colec ția claselor GUI constituie un cadru pentru dezvoltarea de
aplica ții cu interfa ță grafică, în sensul că asigură o bază de clase esentiale și impune un anumit mod de
proiectare a acestor aplica ții și de folosire a claselor existente. Acest cadru ( Framework ) mai este numit și
infrastructură sau clase de bază ( Foundation Classes ).
Ca infrastructură pentru aplica țiile Java cu interfa ță grafică vom considera clasele JFC ( Java Foundation
Classes ), numite și Swing , care reprezintă o evolu ție față de vech ile clase AWT ( Abstract Window Toolkit ).
Multe din clasele JFC extind sau folosesc clase AWT. Clasele JFC asigură elementele necesare proiectării de
interfe țe grafice complexe, atrăgătoare și personalizate după cerin țele aplica ției și ale beneficiarilor,
reducând substan țial efortul de programare a unor astfel de aplica ții (inclusiv editoare de texte, navigatoare
Web și alte utilitare folosite frecvent).

IE.09.2 Clase Java pentru o interfa ță grafic ă
Pachetul javax.swing conține un număr mare de clase pentr u interfe țe grafice; o parte din ele sunt folosite ca
atare (prin instan țiere) iar altele asigură doar o bază pentru definirea de clase derivate.
Clasele JFC ar putea fi grupate astfel:
– Componente atomice: JButton, JLabel, JTextField, JList, JTable, JTre e, etc.
– Clase container: JFrame, JPanel, JDialog, JApplet , etc.
– Clase auxiliare: clase pentru dispunerea componentelor atomice într -un container ( Layout Manager ), clase
ascultător la evenimente, clase model, clase pentru borduri (margini), etc.
O altă clasificare a claselor Swing în raport cu clasele AWT este următoarea:
– Clase JFC care au corespondent în clasele AWT, având aproape acela și nume (cu prefixul 'J' la clasele JFC)
și acela și mod de utilizare: JComponent, JButton, JCheckBox, JRadioButton, JMenu, JComboBox,
JLabel, JList, JMenuBar, JPanel, JPopUpMenu, JScrollBar, JScrollPane, JTextField, JTextArea .
– Clase JFC noi sau extinse: JSlider, JSplitPanel, JTabbedPane, JTable, JToolBar, JTree, JProgressBar,
JInternalFrame, JFileChooser, JColorChoo ser etc.
– Clase de tip ― model ‖, concepute conform arhitecturii MVC (―Model -View -Controller‖):
DefaultButtonModel, DefaultListSelectionModel, DefaultTreeModel, AbstractTableModel etc.
Clasele JFC container de nivel superior sunt numai trei: JFrame, JDialo g si JApplet . Primele două sunt
subclase (indirecte) ale clasei Window din AWT. Toate celelalte clase JFC sunt subclase directe sau
indirecte ale clasei JComponent , inclusiv clasa container intermediar JPanel .
Controalele JFC pot fi inscrip ționate cu text și/sau cu imagini (încărcate din fi șiere GIF sau definite ca șiruri
de constante în program). In jurul componentelor pot fi desenate borduri , fie pentru delimitarea lor, fie
pentru crearea de spa ții controlabile între componente vecine.
Un program mini mal cu clase JFC, crează și afișează componentele vizuale pe ecran, fără să trateze
evenimentele asociate acestor componente. Cea mai mare parte dintr -un astfel de program crează în memorie
structurile de date ce con țin atributele componentelor vizuale și relațiile dintre ele: se crează un obiect
fereastră (panou), care constituie fundalul pentru celelalte componente; se crează componente atomice și se
adaugă la panou obiectele grafice create de programator (cu metoda add() ).

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 125 – In final, se stabilesc dimensiu nile ferestrei principale (metoda setSize() sau pack() ) si se comandă afi șarea
ferestrei principale (metoda setVisible() sau show() ). Fereastra principală a aplica ției este în general de tipul
JFrame și conține o bară de titlu și trei ―butoane‖ standard în col țul dreapta -sus al ferestrei pentru opera ții
de micsorare (minimizare), mărire (maximizare) și închidere fereastră(X) .
Exemplul următor afi șează o fereastră cu titlu, dar fără alte componente vizuale :

In lipsa unui apel al metodelor setSize() sau pack() , fereastra principal ă se afi șează inițial într -o form ă
redus ă la bara de titlu cu cele 3 butoane generale, dup ă care poate fi m ărită. Metoda setSize() permite
afișarea ferestrei JFrame de la început cu dimensiunile dorite.
Adăugarea de componente atomice la containerul JFrame se face cu metoda add() , aceea și metodă de
adăugare a unui nou element la un vector (obiect de tip Vector ), deoarece clasa JFrame folose ște un vector
ca o colec ție de obiecte grafice. Exemplu de interfa ță cu o etich etă JLabel :

După apelul metodei setVisible() sau show() nu mai trebuie create și adăugate alte componente vizuale
ferestrei JFrame , chiar dacă se modifică datele prezentate în unele din aceste componente (prin metode ale
claselor Swing respecti ve) .
Efectuarea unui clic pe butonul de închidere al ferestrei principale (X) are ca efect închiderea ferestrei, dar
nu se termină aplica ția dacă nu estre tratat evenimentul produs de acest clic. De aceea este necesară tratarea
acestui eveniment, sau ter minarea programului de către operator, prin Ctrl -C. O solu ție simplă de terminare a
aplica ției la închiderea ferestrei principale este apelul unei metode din JFrame :

frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);

Adăugarea mai multor obiecte grafice la JFrame ridică problema modului de dispunere ( Layout ) a acestor
componente unele fa ță de altele, în fereastră.

IE.09.3 Plasarea componentelor

Plasarea componentelor grafice pe un panou se poate face și prin pozi ționare în coord onate absolute de către
programator, dar este mult mai simplu să apelăm la un obiect de control al a șezării în panou ( Layout
import javax.sw ing.*;
class EmptyFrame {
public static void main ( String args[]) {
JFrame frm = new JFrame(“EmptyFrame”);
frm.setSize(500,300) // sau frm.pack();
frm.setVisible (true); // sau frm.show();
}
}
public static void main (String args[ ]){
JFrame frame = new JFrame(); // fereastra aplicatiei
JLabel label = new JLabel ("Folder"); // creare eticheta
frame.add(label); // adauga eticheta la fereastră
frame.setSize(200,200); // dimensiuni fereastra
frame.setVisible(true); // afisare continut fereastră
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 126 – Manager ), obiect selectat prin metoda setLayout() din clase container ( JFrame,JPanel , ș.a.) și care
stabile ște automat dimensiunile și pozi ția fiecărei componente într -un panou. Pentru fereastra JFrame este
implicit modul de asezare BorderLayout , mod care folose ște un al doilea parametru în metoda add() pentru
poziția componentei. Există 5 pozi ții posibile: central, sus (nord), jos (s ud), stânga (vest) și dreapta (est).
Poziția poate fi specificată fie printr -o constantă din clasa BorderLayout , fie printr -un sir de caractere:
―Center‖, ―North‖, ―South‖, East‖, ―West‖. Exemplu cu o etichetă și un câmp text:

Dacă nu se spec ifică pozi ția la adăugare, atunci componenta este centrată în fereastră, iar dacă sunt mai
multe componente, atunci ele sunt suprapuse pe centrul ferestrei și nu se vede decât ultima adăugată.
Pentru exemplul anterior este preferabil, ca aspect, să folosim modul de asezare FlowLayout :

Modul FlowLayout plasează componentele una după alta de la stânga la dreapta și de sus în jos în func ție de
dimensiunile lor și ale ferestrei principale, ceea ce este foarte comod pentru început. Dezavantajul ac estui
mod este acela că asezarea componentelor se modifică automat atunci când se modifică dimensiunile
panoului, dimensiunile sau numărul componentelor. Există diverse metode de a men ține pozi ția relativă a
două sau mai multe componente vizuale, indifere nt de dimensiunile ferestrei.
Alte modalită ți de dispunere a componentelor într -un panou sunt GridLayout (o matrice de componente
egale ca dimensiune), GridBagLayout , BoxLayout ( așezare compactă pe verticală sau pe orizontală, la
alegere) si CardLayout ( componente / panouri care ocupă alternativ acela și spațiu pe ecran).
Modurile de a șezare mentionate sunt moduri utilizabile în programarea manuală a interfe țelor grafice,
deoarece un mediu vizual ca NetBeans folose ște alte clase ( GroupLayout , SpringLayout ) pentru plasarea
automată a componentelor într -o fereastră, ca urmare a ac țiunilor de ―tragere‖ ( dragging ) a componentelor
de către operator pe suprafa ța vizuală. In cazul unor interfe țe grafice cu un număr mai mare de componente
este preferabilă utilizar ea unui mediu vizual pentru a șezarea componentelor, codul Java fiind generat
automat.
Utilizarea de containere intermediare JPanel este una din metodele pentru controlul plasării de obiecte
grafice. Un panou JPanel are modul implicit de plasare FlowLayout , dar care poate fi modificat. Exemplu
de afi șare a unui mic formular cu două rubrici, fiecare cu o eticheta în stânga casetei text unde se vor
class DefaultLayout {
public static void main (String args[ ]) {
JLabel lbl1 = new JLabel ("Directory");
JTextField txt1 = new JTextField (16);
JFrame frm = new JFrame("Simple GUI");
frm.add (lbl1,"West"); frm.add(txt1,"Center");
frm.setVisible(true);
}
}

public static void main (String arg[]) {
JLabel lbl1 = new JLabel ("Directory");
JTextField txt1 = new JTextField (16);
JFrame frm = new JFrame("Simple GUI");
frm.setLayout (new Flo wLayout());
frm.add (lbl1); frm.add(txt1);
frm.setSize (300,80);
frm.show();
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 127 – introduce datele specificate de etichetă:

IE.09.4 Structura programelor cu interfa ță grafic ă

Crearea și afisarea unei interfe țe grafice necesită următoarele opera ții din partea programatorului aplica ției:
– Crearea unui obiect fereastră principală, de tip JFrame sau de un subtip al tipului JFrame , și stabilirea
proprietă ților ferestrei (titlu, cul oare, dimensiuni etc.)
– Crearea componentelor atomice și stabilirea proprietă ților acestora (dimensiuni, text afisat, culoare, tip
chenar etc.)
– Gruparea componentelor atomice în containere intermediare, care sunt obiecte de tip JPanel sau de un
subtip al acestei clase.
– Adăugarea containerelor intermediare la fereastra aplica ției și stabilirea modului de a șezare a acestora,
dacă nu se preferă modul implicit de dispunere în fereastră.
– Tratarea evenimentelor asociate componentelor și ferestrei princ ipale, prin definirea de clase de tip
―ascultător ‖ la evenimentele generate de componentele vizuale.
– Afișarea ferestrei principale, prin metodele clasei JFrame setVisible() sau show() .
Exemplele anterioare nu reprezintă solu ția recomandată pentru progra marea unei interfe țe grafice din mai
multe motive:
– Partea de interfa ță trebuie separată de partea de logică a aplica ției, prin clase separate.
– Variabilele referin ță la obiecte Swing nu vor fi locale metodei main pentru că ele sunt folosite și de alte
metode, inclusiv metode activate prin evenimente.
– Metoda statică main trebuie sã fie cât mai scurtă, redusă la crearea unui obiect grafic și, eventual, la
apelarea unei metode pentru acel obiect (obiect dintr -o clasă definită de programator pe baza clasei JFrame ).
Vom prezenta în continuare trei variante uzuale de definire a păr ții de interfa ță grafică dintr -o aplica ție Java
în cazul simplu al unui câmp text înso țit de o etichetă ce descrie con ținutul câmpului text.
Prima variantă folose ște o subclasă a clasei JFrame :

public st atic void main (String arg[]) {
JLabel l1 = new JLabel ("N umele");
JLabel l2 = new JLabel ("Adres a ");
JTextField t1 = new JTextField (16);
JTextField t2 = new JTextField (16);
JPanel p1= new JPanel();
JPanel p2= new JPanel();
JFrame frm = new JFrame();
Frm. setLayout(new FlowLayout ( ));
p1.add (l1); p1.add (t1); add (p1);
p2.add (l2); p2.add (t2); add (p2);
frm. setSize (300,200); frm.
setVisible(true);
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 128 –

Varianta a doua folose ște ―delegarea‖ sarcinilor legate de afisare către un obiect JFrame :

Varianta 3 define ște clasa GUI ca o subclasă a clasei JPanel :

class GUI1 extends JFrame {
private JLabel lbl1 = new JLabel ("Directory");
private JTextField txt1 = new JTextField (16);
// constructor
public GUI1 ( String title) {
super(title); init();
}
// initializare componente
private void init() {
setLayout(new FlowLayout());
add (lbl1); add(txt1);
setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
setSize (300,100);
}
// activare interfata grafica
public static void main (Strin g arg[]) {
new GUI1("GUI solution 1").show();
}
}

class GUI2 {
private JFrame frame;
private JLabel lbl1 = new JLabel ("Directory");
private JTextField txt1 = new JTextField (16);
// constructor
public GUI2 ( String title) {
frame = new JFrame(title);
init(); frame.show();
}
// initializare componente
private void init() {
frame.setLayout(new FlowLayout());
frame.add (lbl1); frame.add(txt1);
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.se tSize (300,100);
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 129 –

Clasele ascultător la evenimente ( TxtListener si altele) sunt de obicei clase incluse în clasa GUI pentru a
avea acces la variabilele ce definesc obiecte JFC.
Variabilele de tipuri JFC ( JLabel, JTextField , s.a) pot fi ini țializate la declarare sau în constructorul clasei
GUI, deoarece va exista un singur obiect GUI. Metoda init() de ini țializare a componentelor JFC poate lipsi
dacă are numai câteva linii. De observat că pentru clasele GUI constructorul este cea mai importantă func ție
și uneori singura funcție din clasă.

IE.09.5 Programarea bazat ă pe evenimente

O interfață grafică servește nu numai pentru afișarea de componente grafice pe ecran dar și pentru
introducerea de date sau de comenzi de către operatorul aplicației. In acest fel se asigură c aracterul interactiv
al aplicațiilor cu interfață grafică. Operatorul uman poate folosi mouse -ul sau tastatura pentru a selecta
anumite componente grafice afișate pe ecran. Selectarea înseamnă poziționarea cu dispozitivul mouse (sau
cu touchpad ) pe o com ponentă grafică și click pe butonul din stânga. Selectarea unor componente grafice se
poate face și din tastele cu săgeți de pe tastatur ă. In cazul unui buton afișat (obiect JButton ) spunem că în
acest fel se ―apasă ‖ pe buton. Majoritatea componentelor Swing afișate pot genera evenimente la selectarea
și apăsarea lor. In cazul componentei JTextField se generează eveniment și la apăsarea tastei Enter pentru
terminare introducere text. Excepție face JLabel care nu poate genera evenimente. Aplica ția cu inter față
grafic ă trebuie s ă conțină cod care s ă reacționeze la evenimentele generate de interfa ța grafic ă.
Prelucr ările dintr -o aplica ție cu interfa ță grafic ă au loc ca r ăspuns la evenimente generate de operatorul uman
al aplica ției. Evenimentele generate de interfa ța grafic ă sunt cele care activeaz ă funcții (metode) ascult ător la
evenimente și nu apeluri efectuate din alte func ții. Acest stil de programare se nume ște Event Driven
Programming sau Event Based Programming , deci programare bazat ă pe evenimente.
Programarea dirijat ă de evenimente ( Event Driven Programming ) se refer ă la scrierea unor programe care
reacționeaz ă la evenimente externe programului (cauzate de operatorul uman care foloseste programul). Prin
―eveniment‖ se întelege aici un eveniment asin cron, independent de evolu ția programului și al c ărui moment
de producere nu poate fi prev ăzut la scrierea programului. Evenimente tipice sunt: ap ăsarea unei taste, class GUI3 extends JPanel {
private JLabel lbl1 = new JLabel ("Directory");
private JTextField txt1 = new JTextField (16);
// constructor
public GUI3 () {
init();
}
// initializare componente
private void init() {
add (lbl1);
add(txt1);
// txt1.addActionListener (new TxtListener());
}
public static void main (String arg[]) {
JFrame frame = new JFrame ("GUI solution 3");
frame.add (new GUI3()); // frame.setContentPane(new GUI3());
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.setSize(300,100); frame.show();
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 130 – acționarea unui buton de mouse , deplasare mouse s.a. No țiunea de eveniment a ap ărut ca o ab stractizare a
unei întreruperi externe .
Un program controlat prin evenimente nu ini țiază momentul introducerii datelor, dar poate reac ționa prompt
la orice eveniment produs de o ac țiune a operatorului. Func țiile care preiau date nu sunt apelate direct și
explicit de alte func ții din program, ci sunt apelate ca urmare a producerii unor evenimente.
Structura unui program dirijat prin evenimente difer ă de structura unui program obisnuit prin existen ța
funcțiilor speciale de tratare a evenimentelor ( Event ha ndlers ), care nu sunt apelate direct din program. Intr –
un program dirijat de evenimente exist ă două tipuri principale de obiecte:
– Obiecte generatoare de evenimente (sursa unui eveniment);
– Obiecte receptoare de evenimente (obiecte ascult ător).
Clasele generator sunt în general clase JFC sau AWT si ele creeaz ă obiecte ―eveniment‖ ca efect al ac țiunii
operatorului pe suprafa ța componentei respective. Clasele receptor de evenimente sunt scrise de c ătre
programatorul aplicatiei pentru c ă metodele de tr atare a evenimentelor observate sunt specifice fiec ărei
aplica ții. Aceste clase trebuie s ă implementeze anumite interfete JFC, deci trebuie s ă conțină anumite metode
cu nume și semn ătură impuse de clasele JFC.
In Java un eveniment este un obiect de un tip clasă derivat din clasa EventObject . Declan șarea unui
eveniment are ca efect apelarea de c ătre obiectul generator a unei metode din obiectul ascult ător, care
prime ște ca argument un obiect ―eveniment‖. Tipul obiectului eveniment este determinat de tipul co mponetei
GUI care a generat evenimentul dar și de actiunea operatorului uman. De exemplu, un clic pe butonul de
închidere a unei ferestre JFrame genereaz ă un alt eveniment decât un clic pe butonul de mic șorare a
ferestrei.
Evenimentele JFC pot fi clasifi cate astfel:
– Evenimente asociate fiec ărei componente vizuale (buton, câmp text, etc.), generate fie prin mouse , fie din
taste (ap ăsare buton, tastare Enter , ș.a.).
– Evenimente asociate dispozitivelor de introducere ( mouse sau tastatur ă).
La fiecare ob iect generator de evenimente se pot ―înregistra‖ (se pot înscrie) mai multe obiecte ascult ător
interesate de producerea evenimentelor generate. Opera ția de înregistrare se face prin apelarea unei metode
de forma ―addXListener‖ (din obiectul generator), und e ‗X‘ este numele (tipul) evenimentului si care este
totodat ă și numele unei interfe țe.
Un eveniment Swing poate avea mai mul ți ascultãtori și un ascultãtor poate ―asculta‖ la mai mul ți generatori
de evenimente. De exemplu, terminarea introducerii într -un câmp text se poate face fie prin tasta Enter
(eveniment ActionEvent ), fie prin taste cu sãge ți sau prin mutarea cursorului pe un al câmp (eveniment
FocusLost ); ambele evenimente pot avea un singur ascultãtor.
IE.09.6 Evenimente Swing
Evenimentele generate de componentele JFC pot fi clasificate în:
– Evenimente comune tuturor componentelor JFC: ActionEvent, FocusEvent, KeyEvent, MouseEvent,
ComponentEvent

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 131 – – Evenimente specifice fiec ărui tip de component ă: ChangeEvent, MenuEvent, ListDataEvent,
ListSelectio nEvent, DocumentEvent etc.
Evenimentele comune, mo ștenite de la clasa Component , pot fi descrise astfel:
– ActionEvent : produs de ac țiunea asupra unei componente prin clic pe mouse.
– ComponentEvent : produs de o modificare în dimensiunea, pozi ția sau v izibilitatea componentei.
– FocusEvent : produs de ―focalizarea‖ claviaturii pe o anumit ă component ă JFC, pentru ca ea s ă poată
primi intr ări de la tastatur ă.
– KeyEvent : produs de ap ăsarea unei taste și asociat componentei pe care este focalizat ă tastatura.
– MouseEvent : produs de ap ăsare sau deplasare mouse pe suprafa ța componentei.
Numele evenimentelor apar în clasele pentru obiecte ―eveniment‖ și în numele unor metode:
―add*Listener‖, ―remove*Listener‖.
Un buton este un obiect Swing de tip JButton care genereaz ă câteva tipuri de evenimente: ActionEvent
(dacă s-a acționat asupra butonului), ChangeEvent (dacă s-a modificat ceva în starea butonului) s.a. La
apăsarea unui buton se creeaz ă un obiect de tip ActionEvent și se apeleaz ă metoda numit ă
action Performed() (din interfata ActionListener ) pentru obiectul sau obiectele înregistrare ca receptori la
acel buton (prin apelul metodei addActionListener() ). Tratarea evenimentului înseamn ă scrierea unei metode
cu numele actionPerformed care s ă produc ă un anumit efect ca urmare a ―ap ăsării‖ butonului (prin clic pe
suprafa ța sa).
In general nu se trateaz ă toate evenimentele ce pot fi generate de o component ă JFC. De și un buton poate
genera peste 5 tipuri de evenimente, în mod uzual se foloseste numai ActionEv ent si deci se apeleaz ă numai
metoda actionPerformed() din obiectele înregistrate ca receptori pentru butonul respectiv. Se poate spune c ă
se produc efectiv numai evenimentele pentru care s -au definit ascult ători și deci metode de tratare a
evenimentelor. Exemplu de tratare a evenimentului de clic pe un buton, prin emiterea unui semnal sonor la
fiecare clic:

// clasa ptr obiecte ascultator de evenimente
class Listener implements ActionListener {
public void actionPerformed(ActionEvent e) {
Toolkit.getDefaultToolkit().beep();
}
}
// creare buton si asociere cu ascultator
class Beeper {
public static void main ( String args[]) {
JFrame frame = new JFrame();
JButton buton = new JButton("Click Me");
frame.getContentPane().add(buton, BorderLayout.CENTER);
button.addActionListener(new Listener()); // inscriere receptor la buton
frame.setVisible(true);
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 132 –

Pentru a ob ține acela și efect si prin ap ăsarea unei taste asociate butonului (de exemplu combina ția de taste
Ctrl-C) este suficient s ă adăugăm o instruc țiune: buton.setMnemonic (KeyEvent.VK_C);
Se observ ă că am folosit clasa ascultător o singur ă dată, pentru a crea un singur obiect. De aceea se practic ă
frecvent definirea unei clase ―ascult ător‖ anonime acolo unde este necesar ă:

Diferen ța dintre evenimentul generat de un buton și un eveniment generat de clic pe mouse este c ă primul
apare numai dac ă dispozitivul mouse este pozi ționat pe aria ocupat ă de buton, în timp ce al doilea apare
indiferent de pozi ția cursorul ui mouse pe ecran. In plus, evenimentele generate de componente JFC con țin în
ele sursa evenimentului și alte informa ții specifice fiec ărei componente.
IE.09.7 Structura programelor dirijate de evenimente

Intr-un program care reac ționeaz ă la evenimente ex terne trebuie definite clase ascult ător pentru aceste
evenimente. De multe ori clasele ascult ător trebuie s ă comunice între ele, fie direct, fie prin intermediul unei
alte clase. In astfel de situa ții avem de ales între mai multe posibilit ăți de grupare a claselor din program,
fiecare cu avantaje și dezavantaje.
Pentru a ilustra variantele posibile vom folosi un exemplu simplu cu dou ă butoane și un câmp text. In câmpul
text se afi șează un num ăr întreg (ini țial zero); primul buton (‗+‘) are ca efect m ărirea cu 1 a num ărului afi șat
iar al doilea buton (‗ -‘) produce sc ăderea cu 1 a num ărului afisat. De și foarte simplu, acest exemplu arat ă
necesitatea interac țiunii dintre componentele vizuale și obiectele ―ascult ător‖.
Prima variant ă foloseste numai clase de ni vel superior ( top-level ): o clas ă pentru fereastra principal ă și dou ă
clase pentru tratarea evenimentelor de butoane. Obiectele ascult ător la butoanele ‗+‘ și ‗-‗ trebuie s ă
acționeze asupra unui câmp text și deci trebuie s ă primeasc ă o referin ță la câmp ul text (în constructor).

button.ad dActionListener(new ActionListener(){ // definitia clasei receptor
public void actionPerformed(ActionEvent e){
Toolkit.getDefaultToolkit().beep();
}
});
class B1L implements ActionListener { // ascultator la buton "+"
JTextField text; // referinta la campul text folosit
public B1L (JTextField t) { text=t; }
public void actionPerformed (ActionEvent ev) {
int n =Integer.parseInt(text.getText()); // valoarea din campul text
text.setText(String.valueOf(n+1)); // modifica continut camp text
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 133 –

Pentru acest exemplu putem defini o singur ă clasă ascult ător la ambele butoane . Atunci când există un singur
ascultător la mai multe evenimente se pune problema recunoa șterii componentei care a generat f iecare din
evenimentele produse. Swing oferă două posibilită ți:
– Metoda getSource() , cu rezultat Object , prezentă în orice clasă eveniment (rezultatul este adresa obiectului
care a generat evenimentul).
– Metoda getActionCommand() , cu rezultat String , prezentă numai în anumite clase Swing (rezultatul este
șirul inscrip ționat pe buton sau pe altă componentă).
Exemplu de ascultător la butoanele inscrip ționate cu ―+‖ și cu ― -― (sau Incr și Decr ).

class B2L implements ActionListener { // ascultator la buton " -"
JTextField text; // referinta la campul text folosit
public B2L (JTextField t) { text=t; }
public void actionPerformed (ActionEvent ev) {
int n =Integer.parseInt(text.getText()); // valoarea din campul text
text.setText(String.valueOf(n -1)); // modifica continut camp text
}
}
// Clasa aplicatiei: butoane cu efect asupra unui camp text
class MFrame extend s JFrame {
JButton b1 = new JButton (" + ");
JButton b2 = new JButton (" – ");
JTextField text = new JTextField (6);
public MFrame() {
text.setText(“0”);
b1.addActionListener (new B1L(text) );
b2.addActionListener (new B2L(t ext) );
setLayout (new FlowLayout());
add(b1); c.add(b2);
add (text);
}
// pentru verificare
public static void main (String args[ ]) {
JFrame f = new MFrame();
f.pack(); f.setVisible(true);
}
}
class BListener imple ments ActionListener {
static int n=0; JTextField text;
public BListener (JTextField t) {
text=t; text.setText (" "+ n);
}
public void actionPerformed (ActionEvent ev) {
String b = ev.getActionCommand();
if (b.conta ins("+")>=0) ++n; // if (b.contains("Incr") n++;
else –n;
text.setText(" "+ n);
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 134 –
O variant ă cu num ăr minim de clase este definirea c lasei cu fereastra aplica ției ca ascult ător la evenimente,
ceea ce elimin ă clasele separate cu rol de ascult ător :

Pentru reducerea num ărului de clase de nivel superior și pentru simplificarea comunic ării între clasele
ascult ător la evenimente putem i nclude clasele re ceptor în clasa cu fereastra aplica ției:

class MFrame extends JFrame implements ActionListener {
JButton b1 = new JButton (" + ");
JButton b2 = new JButton (" – ");
JText Field text = new JTextField (6);
int n=0;
public MFrame() {
Container c = getContentPane();
b1.addActionListener (this);
b2.addActionListener (this);
c.setLayout (new FlowLayout());
c.add(b1); c.add(b2);
text.setText(" "+n); c.add (text);
}
public void actionPerformed (ActionEvent ev) {
Object source =ev.getSource();
if (source==b1) ++n;
else if (source==b2) –n;
text.setText(" "+n);
}
}

class MFrame extends JFrame {
JButton b1 = new JButton (" + "), b2 = new JButton (" – ");
JTextField text = new JTextField (6);
int n= 0;
public MFrame() {
Container c = getContentPane();
b1.addActionListener (new B1L()); b2.addActionListener (new B2L());
c.setLayout (new FlowLayout());
c.add(b1); c.add(b2);
text.setText(" "+n);
c.add (text);
}
class B1L implements ActionListener { // clasa inclusa in MFrame
public void actionPerformed (ActionEvent ev) {
text.setText(" "+ ++n);
}
}
class B2L implements ActionListener { // clasa inclusa in MFrame
public void actionPerformed (ActionEvent ev) {
text.setText(" "+ –n);
}
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 135 – Utilizarea de clase incluse anonime reduce și mai mult lungimea programelor, dar ele sunt mai greu de citit și
de extins în cazul unor interac țiuni mai comp lexe între componentele vizuale .
In examinarea unor variante pentru aplica ția anterioar ă am urm ărit reducerea lungimii codului surs ă și al
numărului de clase din aplica ție, dar acestea nu reprezint ă indicatori de calitate ai unui program cu obiecte și
arată o proiectare f ără perspectiva extinderii aplica ției sau reutiliz ării unor p ărți și în alte aplica ții. Un
dezavantaj comun solu țiilor anterioare este cuplarea prea strâns ă între obiectele ―ascult ător‖ la butoane și
obiectul câmp text unde se afi șează rezultatul modific ării ca urmare a ac ționării unui buton: metoda
actionPerformed() apeleaz ă direct o metod ă dintr -o altă clasă (setText() din JTextField ). De și programarea
este mai simpl ă, totu și tratarea evenimentului de buton este specific ă acestei aplicatii iar clasele ascult ător nu
pot fi reutilizate si în alte aplica ții.
In cazul programelor Java cu interfa ță grafică generată de un mediu vizual (solu ție recomandată) structura
este stabilită automat de IDE și nu este alegerea programatorului.
IE.09.8 Aple ți Java

Cuvântul aplet ( applet ) desemneaz ă o mic ă aplica ție care folose ște ecranul în mod grafic, dar care depinde
de un alt program ―gazd ă‖ pentru crearea fereastrei principale (care nu trebuie creat ă de programatorul
apletului). Programul gazd ă este fie un program navigator ( Web browser ), fie programul appletviewer ,
destinat vizualiz ării rezultatului execu ției unui aplet. Codul unui aplet (fi șierul .class) este de obicei adus de
către browser de la un alt calculator din retea decât cel pe care se execut ă.
Din punct de vedere sintactic un aplet este o clas ă Java, derivat ă din clasa Applet sau din JApplet .
Clasa JApplet este indirect derivat ă din clasa Panel , care asigur ă oricărui aplet o fereastr ă cu butoane de
închidere, m ărire și micsorare. Fereastra de afi șare a unui aplet nu poate fi manipulat ă direct de operatorul
uman ci numai indirect, prin fereastra programului browser.
Programarea unei interfe țe grafice într -un aplet este pu țin mai simpl ă decât într -o aplica ție deoarece apletul
moșteneste de la clasa Panel (și de la clasele Container si Component ) o serie de metode utile (inclusiv
metoda windowClosing() ). Exemplu de aplet scris în varianta Swing :

Fișierul ―class‖ generat de compilator pentru un aplet este specificat într -un fișier html, împreun ă cu
dimensiunile ferestrei folosite de a plet, între marcajele <applet> și </applet>. Exemplu de fisier html necesar
pentru execu ția apletului precedent:
<applet code="Aplet.class" width="250" height="100"> </applet>

In comanda ―appletviewer‖ este specificat numele fi șierului html și nu apare direct numele fisierului ―class‖.
Dimensiunile ferestrei folosite de aplet se dau în fisierul de tip html și nu în codul Java.
De remarcat c ă o clas ă care corespunde unui aplet trebuie s ă aibă atributul public și nu contine o metod ă
main . Clasa aplet mo ștenește și redefine ște de obicei metodele init(), start(), paint() și alte câteva metode,
apelate de programul gazd ă la producerea anumitor evenimente. public class Aplet extend s JApplet {
JLabel et= new JLabel ("Eticheta", JLabel.CENTER);
public void init () {
add (et);
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 136 – O clas ă aplet poate fi transformat ă într-o aplica ție prin scrierea unei func ții main () în care se construieste un
obiect JFrame , la care se adaug ă un obiect aplet si se apeleaz ă metoda init() :

Din punct de vedere func țional un aplet contine câteva func ții, care trebuie (re)definite de utilizator și sunt
apelate de programul gazd ă. Un aplet care trateaz ă evenimente externe trebuie s ă con țină și metodele de
tratare a evenimentelor, pentru c ă nu se admit alte clase ascult ător, separate de clasa aplet. Obiectul
ascult ător la evenimente este chiar obie ctul aplet, ceea ce conduce la instruc țiuni de forma urm ătoare
comp.addXXXListener(this); // comp este numele unei componente din aplet
Exemplul urm ător este un aplet care afi șează un buton în centrul ferestrei puse la dispozi ție de programu l
gazdă și emite un semnal sonor ( beep ) la "ap ăsarea" pe buton, adic ă la acționarea butonului din stânga de pe
mouse dup ă mutare mouse pe zona ecran ocupat ă de buton.

Metoda "init" este apelat ă o singur ă dată, la înc ărcarea codului apletului în memorie, iar metoda "start" este
apelat ă de fiecare dat ă când programul browser readuce pe ecran pagina html care contine și marcajul
<applet …>. Metoda "paint" are un parametru de tip Graphics , iar clasa Graphics contine metode pentru
desenarea de f iguri geometrice diverse și pentru afi șarea de caractere cu diverse forme și mărimi:

IE.09.9 Clase Swing cu model

Un obiect vizual folosit ă într-o interfa ță grafic ă îndepline ște mai multe func ții:
– Prezint ă pe ecran date într -o imagine specific ă (controlabil ă prin program). public static void main (String args[ ]) { // se adauga la clasa JAplet
JFrame f = new JFrame();
JAplet aplet = new JAplet();
f.getContentPane().add (aplet);
aplet.init();
f.setVisible (true);
}
public class Aplet extends JApplet implements ActionListener {
JButton button;
public void init() {
button = new JButton("Click Me");
getContentPane().add(button, BorderLayout.CENTER);
button.addActionListener(this); // obiectul receptor este chiar apletul
}
public void actionPerformed(ActionEvent e) { // tratare eveniment buton
Toolkit.getDefaultToolkit().beep(); / / semnal sonor
}
}
public void paint (Graphics g) {
g.drawRect (0, 0, getSize().width – 1, getSize().height – 1); // margini fereastra
g.drawString ("text in aplet", 10, 30); // afisare text
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 137 – – Preia ac țiunile transmise prin mouse sau prin tastatur ă obiectului respectiv.
– Permite modificarea datelor ca r ăspuns la ac țiuni ale operatorului uman sau la cereri exprimate prin
program si actualizeaz ă imaginea de pe ecran a acestor date.
Arhitectura MVC ( Model -View -Controller ) separ ă în cadrul unei componente vizuale cele trei func ții
esențiale ale unui program sau ale unui fragment de program: intr ări (Controller ), date și prelucr ări (Model ),
iesiri ( View ).
Partea numit ă ―model‖ reprezint ă datele și func ționalitatea componentei, deci define ște starea și logica
componentei. Imaginea ( view) red ă într-o form ă vizual ă modelul, iar partea de comand ă (controller )
interpreteaz ă gesturile utilizatorului și acționeaz ă asupra modelu lui (defineste ―comportarea‖ componentei).
Poate exista și o leg ătură direct ă între partea de control și partea de redare, în afara leg ăturilor dintre model și
celelalte dou ă părți (imagine și comand ă).
Comunicarea dintre cele trei p ărți ale modelului MVC se face fie prin evenimente, fie prin apeluri de
metode. Modelul semnaleaz ă părții de prezentare orice modificare în starea sa, prin evenimente, iar partea de
imagine poate interoga modelul, prin apeluri de metode. Partea de comand ă este notificat ă prin ev enimente
de ac țiunile (―gesturile‖) operatorului uman si modific ă starea modelului prin apeluri de metode ale
obiectului cu rol de ―model‖; în plus poate apela direct și metode ale obiectului de redare (pentru modificarea
imaginii afisate).
Clasele JFC fol osesc o variant ă a modelului MVC cu numai doi participan ți: o clas ă model și o clas ă
―delegat‖ care reune ște func țiile de redare și de control pentru a usura sarcina proiectantului, deoarece
comunicarea dintre controler și imagine poate fi destul de comple xă.
Componentele JComboBox , JList, JTable, JTree, JMenuBar includ întotdeauna un obiect model care
poate fi extras printr -o metod ă getModel() pentru a se opera asupra lui. Obiectul model poate fi creat automat
în constructor, pe baza unor colectii, sau p oate fi creat de programator și transmis clasei care asigur ă
prezentarea datelor din model. Exist ă clase predefinite pentru obiecte model
(DefaultComboBoxModel ,DefaultListModel, DefaultTreeModel ) dup ă cum se pot defini și alte clase
model care s ă respecte interfete impuse.
Un model este o structur ă de date care poate genera evenimente la modificarea datelor și con ține metode
pentru ad ăugarea și eliminarea de ―ascult ători‖ la evenimentele generate de model. Clasele JList,JTable ș.a.
sunt ascult ători la eveni mente generate de clasele model respective, deci modificarea datelor din model va
modifica automat și afisarea pe ecran (se vor afi șa noile date din obiectul model).
Datele prezentate într -un obiect JList, JTable, JTree s.a. se pot modifica în cursul exec uției programului,
iar afi șarea trebuie s ă reflecte aceste modific ări. De exemplu, se afi șează date din directoare sau din fi șiere al
căror nume se introduce sau se modific ă de către operator dup ă afișarea interfe ței grafice (prin introducere în
câmpuri te xt, de exemplu).
Ca tehnic ă general ă, nu se construiesc alte obiecte vizuale ( JList, JTable, JTree ) cu noile date dup ă afisarea
interfe ței grafice și nici nu se apeleaz ă metode de reafi șare (repaint () sau altele), ci se apeleaz ă metode care
transmit la ob iectul vizual un alt model ( setModel() ), sau se apeleaz ă metode de modificare a obiectului
model.
Modific ările asupra obiectului model sunt redate automat pe ecran deoarece obiectul vizual este ascult ător la
evenimente generate de model. In general, clasel e model contin și metode de modificare a datelor (interfe țele
claselor model nu impun astfel de metode).

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 138 – O alt ă posibilitate este s ă se creeze un nou model (de exemplu dup ă reordonarea unui vector sau unui tabel)
și să se retransmit ă noul model la acela și obiect vizual din interfa ța grafic ă.
Utilizatorii au posibilitatea s ă-și defineasc ă alte clase model, dar aceste clase trebuie s ă respecte anumite
interfe țe Java (ListModel, ComboBoxModel, TableModel, TreeModel ). Pentru facilitarea definirii de noi
clase m odel (model de list ă sau de tabel) exist ă clase abstracte care implementeaz ă parțial interfe țele
mentionate: AbstractListModel, AbstractTableModel . Clasele model gata definite au un nume care începe
cu Default (DefaultListModel, DefaultTableModel, DefaultT reeModel ) și constructori ce primesc ca
argument un vector (model de list ă), o matrice (model de tabel) sau un nod r ădăcină (model de arbore).
Clasele cu model au atât constructor cu argument model, cât și constructori cu diverse structuri de date, pe
baza cărora se construiesc automat obiecte model; de aceea exist ă metode getModel() în toate aceste clase,
indiferent dac ă s-a transmis un model creat separat sau nu (model creat implicit).
Componenta vizual ă JList afiseaz ă pe ecran o list ă de valori, sub for ma unei coloane, și permite selectarea
uneia dintre valorile afi șate, fie prin mouse, fie prin tastele cu s ăgeți. Datele afi șate pot fi de orice tip clas ă și
sunt memorate într -un obiect colec ție (Vector ) sau model; o referin ță la acest obiect este memorat ă în
obiectul JList de constructorul obiectului JList . Exist ă mai mul ți constructori, cu parametri de tipuri diferite:
vector intrinsec, Vector sau ListModel . Exemple:

Clasa predefinit ă DefaultListModel pentru model de list ă are un singur construc tor, f ără argumente, dar are
metode de ad ăugare si de modificare a obiectelor din vectorul folosit de obiectul model. Un obiect JList cu
conținut variabil se poate folosi și fără obiect model creat explicit, prin retransmiterea unui vector cu date la
obiectul JList (metoda setListData() ). Exemplu de afi șare într -un obiect JList a numelor fi șierelor dintr -un
director al c ărui nume se introduce (sau se modific ă) într -un câmp text:

String v =,“unu”,”doi”,”trei”-;
JList list1 = new JList (v); // vector intrinsec
Vector vec = new Vector ( Arrays.asList(v));
JList list2 = new JList(vec); // obiect de tip Vector
JList list3 = new JList (new DefaultListModel());

class FileList extends JFrame implements ActionListener{
JList jlist = new JList(); // pentru continut director
JTextField tf = new JTextField(12); // pentru nume director
public FileList() {
Container cp = getContentPane();
cp.add (tf,"North"); cp.add (new JScrollPane(jlist));
tf.addActionListener (this);
setSize(300,600); show();
}
public void actionPerformed (ActionEvent ev) {
File d=new File(tf.get Text()); // creare ob. File cu nume director
if (! d.isDirectory ()) return;
String files[] = d.list(); // vector cu nume de fisiere
Vector v = new Vector (Arrays.asList(files));
jlist.setListData(v); // transmite vector la JList
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 139 – Modificarea datelor din vector (vector intrinsec sau obiect Vector ) nu are efect asupra datelor afi șate în
obiectul JList decât dac ă se apeleaz ă la fiecare modificare și metoda setListData() , care apeleaz ă metoda
setModel() din JList . Modificarea datelor din model (prin metode ca addElement(), setElementAt(),
removeElementAt() ) se reflect ă automat pe ecran, deoarece obiectul JList este ascult ător la evenimentele
generate de model. Exemplu:

class FileList extends JFrame implements ActionListener{
DefaultListModel model = new DefaultListModel();
JList jlist = new JList(model);
JTextField tf = new JTextField(12);
publi c FileList() {
Container cp = getContentPane();
cp.add (tf,"North");
cp.add (new JScrollPane(jlist));
tf.addActionListener (this);
setSize(300,600); show();
}
public void actionPerformed (ActionEvent ev) {
File d=new File(tf. getText());
if (! d.exists()) return;
String files[] = d.list();
model.clear(); // sterge continut anterior model
for (int i=0;i<files.length;i++) // adauga nume fisiere la model
model.addElement(files[ i]); // modelul modifica si obiectul JList !
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 140 – Capitolul IE.10. Mediul de programare NetBeans
Cuvinte cheie
Dezvoltarea de aplicatii, Construirea de aplicatii ,Mediu integrat IDE,
Proiect, Depanare, Puncte de oprire, Refactorizare,Testare unitar ă
Programare vizual ă, Mediu vizual,

IE.10.1 Dezvoltarea de aplica ții Java
Dezvoltarea de aplica ții (Application Development ) este un termen care reune ște totalitatea opera țiilor
necesare pentru ob ținerea și men ținerea unui produs software comercial: scrierea ini țiala de module, legarea
modulelor într -un program unitar, testarea și depanarea produsului, actualizarea programului la modificarea
unor cerin țe sau extinderea programului cu noi func ții, arhivarea într -un singur fi șier, instalarea programului
pe calculatorul beneficiarului sau pe un server de aplica ții (Deployment ), etc.
Dezvoltarea aplica țiilor mici și medii se poate face în modul linie de comandă, cu comenzi introduse manual
pentru fiecare dintre aceste opera ții și cu utilizarea unor instrumente pentru construirea de aplica ții (Build
Tools ) și pentru men ținerea de versiuni succesive ( Version Control System ).
In dezvoltarea unei aplica ții se repetă anumite ope rații necesare pentru ob ținerea unui executabil după
modificarea surselor: compilare, ștergere și creare de fisiere și/sau foldere, copiere de fi șiere, ș.a. Aceste
opera ții (ac țiuni) sunt cuprinse într -un fi șier de construire aplica ție (Build File ) sau pro iect, care este de
obicei un fi șier XML și care este folosit de către un instrument ( Build Tool ). In plus, pentru aplica țiile Java
se folosesc de obicei mai multe biblioteci de clase, sub formă de arhive jar, care pot avea și ele diferite
versiuni. Pentru limbajul Java cele mai utilizate instrumente pentru construirea de aplica ții sunt: Ant (plus
Ivy), de la Apache și Maven. Maven permite reutilizarea bibliotecilor de clase aflate într-un ―depozit‖
central (repository ) și accesate automat prin Internet .
Exemplu de fi șier (proiect) pentru programul Ant:
<project name="StringUtilsBuild" default="package"
xmlns:ivy="antlib:org.apache.ivy.ant" xmlns="antlib:org.apache.tools.ant">
<target name="clean">
<delete dir="target"/>
<delete dir="lib"/>
</target>
<targ et name="compile" depends=" -init-ivy">
<mkdir dir="target/classes"/>
<javac srcdir="src/main"
destdir="target/classes"/>
</target>
<target name="compileTest" depends="compile">
<mkdir dir="target/test -classes"/>
<javac srcdir="src/test"
destdir="target /test -classes">
<classpath>
<pathelement location="target/classes"/>
<fileset dir="lib" includes="*.jar"/>
</classpath>
</javac>
</target>

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 141 – </project>

Gruparea mai multor opera ții în ac țiuni numite target , permite reutilizarea acestora în diferite proiec te.
Multe dintre aplica țiile Java sunt aplica ții Web, care necesită instalarea pe un server Web pentru testare.
Timpul necesar dezvoltării de aplica ții mai mari poate fi mult redus prin utilizarea unui mediu integrat de
dezvoltare ( IDE= Integrated Developm ent Environment ), care asigură o serie de facilită ți pentru editarea
programelor, pentru depanarea lor, pentru construirea de aplica ții și pentru testarea lor, toate cu o interfa ță
grafică prietenoasă și usor de utilizat. Un astfel de produs integrează mai multe programe diferite utilizate în
linie de comandă: editor de texte, compilator, depanator, suport pentru teste unitare (JUnit), constructor de
aplica ții, server Web, etc.
Pentru editare (scrierea și modificarea surselor) un IDE este de ajutor prin sem nalarea unor erori înainte de
compilare (se face o analiză sintactică pe măsură ce se introduc instruc țiuni), prin autocompletarea unor
nume de clase sau metode, prin ajutor ( Help ) cu documenta ția claselor, prin refactorizare s.a. Pentru
construirea de apl icații se creează automat fi șiere build Ant pentru fiecare proiect. Unitatea de lucru a unui
IDE este un ― proiect ‖ (project ), care grupează într -o structură de foldere fi șiere diverse: surse, fi șiere
rezultate din compilare, biblioteci, fi șiere de propriet ăți, fisiere build , s.a. In general un proiect corespunde
unei aplica ții dar este posibil ca o aplica ție să fie dezvoltată prin câteva proiecte, unele pentru biblioteci de
clase folosite în aplica ție și un proiect pentru pornirea aplica ției (cu metoda main ()).
Cele mai utilizate produse IDE pentru Java sunt Eclipse, NetBeans si Intellij IDEA care permit dezvoltarea
de aplica ții în mai multe limbaje: Java, C, C++, PHP, Groovy, ș.a. In plus, ele pot fi folosite și ca medii
vizuale, pentru crearea interfe țelor grafice ale aplica țiilor fără programare manuală: utilizatorul alege
componentele grafice, le configurează proprietă țile și le plasează pe o suprafa ță ce reprezintă fereastra
principală a aplica ției, iar IDE generează codul sursă pentru a realiza interfa ța grafică desenată de utilizator și
ajută la tratarea evenimentelor generate de componentele grafice.
IE.10.2 Mediul integrat NetBeans
Eclipse și NetBeans sunt cele mai folosite medii IDE datorită facilit ăților oferite și pentru faptul c ă sunt
complet grat uite. Alegerea între cele dou ă este relativ subiectiv ă și poate fi determinat ă de specificul
aplica țiilor dezvoltate, în sensul c ă unul sau altul este preferabil dintr -un anumit punct de vedere.
Mediul NetBeans poate fi desc ărcat în câteva variante: numai pentru aplica ții Java standard (SE = Standard
Edition), numai pentru aplica ții C,C++, numai pentru aplica ții PHP, pentru aplica ții Web în Java ( JEE =
Java Enterprise Edition) sau pentru toate variantele anterioare plus alte facilit ăți (Java ME, Groovy, J ava
Card). In varianta SE se aduce și SDK (compilator, biblioteci s.a.) dar nu și documenta ția Javadoc pentru
clasele Ja va; pentru instalarea documenta ției în NetBeans se alege din meniu:
Tools> Java Platform > Javadoc > Add ZIP/Folder > C:/jdk -7-doc/api
Orice aplica ție dezvoltat ă sub NetBeans necesit ă crearea unui proiect. Crearea unui nou proiect se face prin
selectarea op țiunii File din meniul principal și apoi a op țiunii New Project (din File) sau prin ―scurt ătura‖
Ctrl-Shift -N.

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 142 –

Urmează alegerea tipului de proiect; vom alege un proiect standard Java (Java Application):

Urmeaz ă stabilirea numelui și loca ției folderului care va con ține proiectul; se pot accepta propunerile IDE
sau se pot modifica numele și locul proiectului.

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 143 –

Dacă s-a ales generarea clasei Main, atunci NetBeans va crea un schelet care va fi completat de utilizator.
Exemplu:
/* To change this template, choose Tools | Templates and open the template in the editor. */
package helloworldapp;
/* @author <your name> */
public class HelloWorldApp {
/* @param args the command line arguments */
public static void main(String[] args) {
System.out.println("Hello World!");
}
}

Ecranul unui proiect activ (în lucru) con ține mai multe sec țiuni, a șa cum se vede din figura următoare

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 144 – Compilarea se face automat la salvarea textului introdus . Execuția se comandă fie din meniu
(Run>RunMainProject ), fie cu tasta F6 . Rezultatele apar în fereastra cu titlul ―Output‖, deschisă automat :

Aplicațiile reale conțin multe cla se și alte fișiere . Beneficiarul aplicației primește de obicei o arhiva de tip
―jar‖ în care se împachetează toate fișierele necesare folosirii aplicației . Crearea arhivei se face fie din
meniu: Run > Clean and Build Main Projec t sau cu ―scurtătura‖ Shift -F11. Arhiva este plasată în sub –
directorul dist (pentru distribu ție) din directorul proiect .
Pentru includerea în proiect a unor biblioteci de clase Java (altele decât cele standard SDK):
Extindere nod proiect > click –dreapta pe ―Libraries‖ > Add JAR/Fol der > selectare nume/cale fișier de tip
jar sau folder.
Depanarea programelor ( Debugging ) se face prin selectarea op țiunii Debug din meniul principal sau prin
Ctrl-F5. In acest mod se pot utiliza următoarele procedee: Stablilirea unor puncte de oprire tem porară
(Breakpoints ), Execuția pas -cu-pas ( Step-Over ) a instrucțiunilor sursă , Execuția până la poziția cursorului ,
Inspectarea conținutului unor variabile din program , Modificarea valorii unor variabile din panoul de
variabile .
Comenzi utilizabile în mo dul ―depanare‖:
Continue : se continuă execuția până la următorul punct de oprire sau până la terminarea programului
Step-into : se intră în execuția pas cu pas a instrucțiunilor unei funcții (metode) apelate; în mod normal
funcțiile apelate nu se execută pas cu pas.
Step-out : se iese din execuția pas -cu-pas a unei funcții și se trece la funcția care a facut apelul.
Run-to-cursor : alternativă la breakpoint pentru execuția unei secvențe de instrucțiuni (până la poziția
cursorului)
Finish : ieșire din modul debugging

Stabilirea unor puncte de oprire ( Breakpoints ) se poate face si prin clic pe bara din marginea din stânga a
ferestrei cu sursa programului:

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 145 –

Instruc țiunea din program care urmează a fi executată este marcată cu o linie verde:

Exemplu de tabel NetBeans cu valorile variabilelor din program:

IE.10.3 Editorul Java din NetBeans
La introducerea textului sursă mediul NetBeans oferă următoarele facilită ți:
Colorare sintactic ă: comentariile cu gri, cuvintele cheie cu albastru, variabile și câmpuri cu v erde, oarametri
cu oranj, clase sau metode învechite ( deprecated ) subliniate cu o linie ondulată, iar membrii nefolositi tăia ți
cu o linie. Exemplu:

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 146 –

Marcare erori (Hints for Syntax errors ) : instrucțiunile cu erori sunt marcate cu un punct roșu la st ânga;
dacă se apasă pe ―becul‖ afișat sau se folosesc tastele Alt -Enter atunci se afișează cauza erorii .Erorile de
compilare sunt semnalate chiar de la introducerea textului sursă în fereastra de edi tare, prin subliniere cu
roșu. Exemplu:

O eroare frecvent ă este absenta instructiunii import la introducerea unui nume de clasă; pentru corectare se
face clic dreapta pe numele clasei și se alege Fix Import .:
Completare automat ă (Auto -complete ) : după introducerea parțială a numelui unei clase sau metode se
apasă Ctrl -space pentru afișarea opțiunilor posibile de completare automată a întregului nume . Exemplu:

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 147 – Sugestiile editorului se aplică și pentru cuvinte cheie, variabile și parametrii de func ții. Exemple:

Editorul NetBeans semnalează clasele din pachetele neimpo rtate și adaugă la cerere instruc țiunile import
necesare (cu combina ția de taste Ctrl -Shift -I). Exemplu:

La selectarea unei clase pe ntru completare automat ă se adaug ă si instr ucțiunea import necesar ă.
Editorul NetBeans poate genera automat cod Java pentru constructori, metode, metode suprascrise, metode
delegate s.a. folosind tastele Alt -Insert . In exemplul urm ător se cere generarea u nui constructor

Afișare documenta ție Javadoc : Clic dreapta pe numele metodei > Show Javadoc sau Alt -F1. Altă variantă:
se pu ne cursorul pe numele unei metode sau clase și se apasă Ctrl -space pentru afișarea parțială a
documentației referitoare la metoda sau clasa respectivă . In continuare se poate tasta din nou Ctrl -space
pentru lista metodelor sau se folosesc butoanele afi șate în fereastră pentru a alege afi șarea documenta ției
pentru clasa respectivă într -un browser Web
sau afi șarea sursei clasei
(dacă este disponibilă mediului
NetBeans). Exemplu:

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 148 –

Șabloane de cod (Code templates) : se pot defini forme prescurtate pentru secvențe uzuale folosind tasta
TAB; de ex. ―sout‖ în loc de System.out.println sau ―fori‖ pentru un ciclu ―for‖. Exemplu:

Pentru ad ăugarea sau modificarea unui șablon de cod se procedeaz ă astfel:

– Tools > Options > Editor > Code templates
– Se alege limbajul (Java); se vor afisa șabloanele existente
– Butoanele New si Remove permit ad ăugarea sau eliminarea unui șablon de cod
– Pentru editare se alege un șablon existent si se modific ă în fereastra .
– Se alege tasta pentru expandarea șabloanelor (implicit este TAB)
Numerotare linii (Line Numbers ): click -dreapta pe marginea din stânga a unei instrucțiuni pentru afișare
numere linii sursă
Format : Formatare cod sursă prin Alt-Shift -F sau click -dreapta (Source din meniu) și Format pentru
indentare și formatare cod sursă
Comentare temporară bloc de cod sursă : selectare bloc și Source ->Toggle Comment din meniu

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 149 – Navigare prin codul surs ă: fie p rin activare Navigator ,
fie prin scurtături (combina ții de taste). Pentru activare
navigator selectăm din meniu Window > Navigating >
Navigator (sau Ctrl -7); se va afi șa o fereastă cu structura
fișierului sursă din care se poate selecta un element:

IE.10.4 Refactorizare în NetBeans
Refactorizarea codului surs ă se referă la modificarea disciplinată a codului fără a modifica efectul codului la
execu ție. Refactorizarea conduce la surse mai u șor de în țeles, de modificat (de între ținut) și permite găsirea
și eliminarea unor erori.
Cea mai folosită opera ție de refactorizare este schimbarea numelui unui pachet sau unei clase, unei metode
sau unei variabile. NetBeans modifică toate apari țiile acelui nume, inclusiv referiri la el. Pentru schimbarea
unui nume avem două posibilită ți: clic dreapta pe nume > Refactor > Rename sau din meniu selectăm
Refacor > Rename.

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 150 –

Exemple de alte opera ții de refactorizare :
– Inlocuirea unui bloc de cod cu o metod ă prin introducerea defini ției metodei și apelului metodei
(Introduce Method ).
– Generarea metodelor get()și set() pentru un câmp și înlocuirea referin țelor la câmp prin apeluri de
metode ( Encapsulate Fields )
– Mutarea unei clase într -un alt pachet ( Move Class )
– Ștergere sigur ă (Safely Delete), dac ă nu mai sunt referinte l a acel element.
– Modificarea parametrilor unei metode (Change Method Parameters)

IE.10.5 Testarea programelor Java în NetBeans
Testarea unitară este parte din procesul dezvoltării de programe, iar pentru aplica țiile Java se folose ște
preponderent bibliotec a de clase JUnit . In NetBeans se pot folosi atât versiunea 4 JUnit, cu adnotări, cât și
versiunea 3 fără adnotări.
Testarea unitar ă verific ă, pentru fiecare unitate de program (metod ă Java), faptul c ă, pentru anumite date
inițiale, rezultatele sunt cele a șteptate. In acest scop se scrie pentru fiecare metod ă testat ă o func ție de test în
care se folosesc aser țiuni. Se poate folosi instruc țiunea assert din Java sau aser țiuni JUnit. O aser țiune este o
afirma ție care poate fi adev ărată sau fals ă; dac ă este adev ărată programul continu ă, iar dac ă este fals ă atunci
se produce excep ția AssertionFailedError . Exemple de aser țiuni:
assertTrue (boolean condition) Trece dacă condi ție adevărată
assertEquals(Object expected,Object actual) Trece dacă obiectele sunt ega le după metoda equals()
assertEquals (int expected, int actual) Trece dacă cele două valori sunt egale (==). Valorile
pot fi de orice tip primitiv Java .
assertSame(Object expected, Object actual) Trece dacă cele două obiecte sunt identice
assertNull(O bject object) Trece dacă argumentul este null

Fiecare metod ă de tip ―assert‖ poate avea un parametru suplimentar de tip String care este un mesaj afi șat în
caz c ă aserțiunea (condi ția) este fals ă.
In func țiile de test se folosesc în general obiecte ale clasei testate; aceste obiecte pot fi create în interiorul
metodei sau în afara metodelor de test. In acest scop sunt prev ăzute în clasa de test metodele cu numele
setUp() si tearDown() (JUnit 3) sau metode cu orice nume dar adnotate cu @Before si @After . Metoda
setUp() creeaz ă obiectele necesare metodelor de test și initializeaz ă variabile ale clasei care vor fi folosite în
metodele de test, înainte de fiecare test. Metoda pereche tearDown() anuleaz ă operatiile din setUp() și reface
starea ini țială, dac ă e necesar.
Testele unitare pot fi clasificate în trei categorii:

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 151 – – teste pozitive : se verific ă rezultatul a șteptat al unei ac țiuni
– teste negative: se verific ă comportarea în cazul unor date de intrare (parametri) cu erori
– teste de producere excep ții: se verific ă dacă sunt tratate excep țiile posibile
Exemplul folosit aici este o clas ă cu dou ă metode: înmul țire și împ ărțire de întregi:
public class Calc {
public int mul (int a, int b) { return a*b; }
public int div (int a, int b) { return a/b; }
}

Exemplu de fi șier cu codul de testare a clasei ―Calc‖ scris manual folosind JUnit 3 :
import org.junit.*;
import static org.junit.Assert.*;
import junit.framework.*;
public class UnitTest1 extends TestCase {
Calc c;
public void setUp(){
c=new Calc();
}
public void testMul() {
assertEquals (c.mul(2,3),6) ; // assertTrue ( c.mul(2,3)==6);
}
public void testDiv() {
assertEquals (c.div(6,3),2) ; // assertTrue ( c.div(6,3)==2);
}
}

class Runner {
public static void main (String a[ ]){
org.junit.runner.JUnitCore.main("UnitTest1");
}

Pentru generarea de teste JUnit în NetBeans se face click dreapta pe numele clasei sau pe numele pachetului
de clase (dac ă sunt mai multe clase supuse testelor), se se lecteaz ă
Tools > Create Tests > Framework > JUnit
Putem apoi alege între versiunile 3 și 4 de JUnit. Efectul este acela de generare a unui nou pachet de clase în
directorul TestPackages cu clase de test pentru fiecare clas ă din aplica ție. In cazul cl asei Calc se genereaz ă
clasa de test urm ătoare dup ă selec ția variantei JUnit 3:
import junit.framework.TestCase;
public class CalcTest extends TestCase {
public CalcTest(String testName) {
super(testName);
}

@Override
protected void setUp() throws Exception {
super.setUp();
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 152 – @Override
protected void tearDown() throws Exception {
super.tearDown();
}
public void testDiv() {
System.out.println("div");
int a = 0;
int b = 0;
Calc instance = new Calc();
int expResult = 0;
int result = instance.div(a, b);
assertEquals(expResult, result);
// TODO review the generated test code and remove the default call to fail.
fail("The test cas e is a prototype.");
}
}
. . . // testMul
Așa cum indic ă și comentariile vom elimina liniile finale care apeleaz ă metoda fail() si vom cere execu ția
testelor în una din variantele:
– Din meniul principal: Run > Test Project
– Click dreapta pe numele proiectului > Test ( sau Alt-F6)

Deoarece se genereaz ă automat date de test (a,b) cu valoarea zero se va produce o excep ție neprev ăzută și
netratat ă în metoda div() , iar testul e șuează (Test failed ) din cauza acest ei erori.
Pentru testarea metodelor care pot genera excep ții se poate verifica în metoda de test dac ă excep ția a fost
tratat ă sau nu în metoda verificat ă. Dac ă adăugăm metodei ―div‖ tratarea exceptiei aritmetice atunci testul va
trece cu bine Exemplu:
public void testDiv() {
int a = 0; int b = 0;
int result=0;
Calculator instance = new Calculator();
int expResult = 0;
try {
result = instance.div(a, b);
} catch (ArithmeticException ex){
fail ("ArithmeticException");
}
assertEquals(expResult, result);
}

Teste generate în varianta JUnit 4:
public class CalcTest {
public CalcTest() { }
@BeforeClass
public static void setUpClass() { }
@AfterClass
public static void tearDownClass() { }
@Before
public void setUp() { }
@After

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 153 – public void tearDown() { }
@Test
public void testMul() {
System.out.println("mul");
int a = 0;
int b = 0;
Calc instance = new Calc();
int expResult = 0;
int result = instance.mul(a, b);
assertEquals(expResult, result);
// TODO review the generated test code and remove the default call to fail.
fail("The test case is a prototype.");
}
@Test
public void testDiv() {
. . .
}
}

Dacă metoda testat ă arunc ă o excep ție (tratat ă în alt ă metod ă din aplica ție) atunci putem specifica în metoda
de test c ă se asteapt ă producerea acelei excep ții:
@Test (expected=ArithmeticException.class)
public void testDiv() , …-

IE.10.6 Programare vizuală în NetBeans
Termenul de ―programare vizuală‖, ca alternativă la ―programarea manuală‖ se referă la un mod de lucru în
care codul sursă este generat de către un mediu vizual pe baza desenului interfe ței realizat pe ecran de către
utilizator (numit form în documenta ția NetBeans), desen format din formele componentelor predefinite
(butoane, câmpuri text, etichete, etc). Pe ecran se afi șează o ―paletă‖ de componente și o ―foaie‖ de
proprietă ți pentru fiecare componentă selectată. Utili zatorul aduce ( drag and drop ) fiecare componentă
necesară pe o suprafa ță de asamblare a componentelor și stabileste sau modifică proprietă țile lor (multe
proprietă ți au valori implicite). Mediul vizual generează cod sursă pe baza ansamblului de componente.
Legăturile (interac țiunile) dintre componente pot să apară grafic sau nu.
Una dintre aplica țiile importante ale programării vizuale a fost generarea de cod pentru interfe țele grafice din
aplica ții, folosind drept componente obiecte grafice (controale) ce compun aceste interfe țe. Ideea este că
dimensiunile obiectelor grafice, dispunerea lor relativă pe ecran și modificări ale acestor atribute se pot face
mult mai u șor prin vizualizarea lor și tragerea obiectelor sau a marginilor lor, decât prin programare m anuală
și execu ții repetate pentru a vedea efectul modificărilor efectuate în surse. Medii vizuale au existat și există
pentru diferite limbaje: Delphi pentru Pascal, Microsoft Visual Studio pentru C++ și Basic, NetBeans și
Eclipse pentru Java ș.a.
Inainte a versiunii 7 NetBeans se putea alege un tip de proiect Java Desktop Application pentru a intra în
editorul ―vizual‖, dar după această versiune se pot crea vizual interfe țe grafice pentru proiecte standard Java
Application , prin adăugarea la proiect a unui nou element de tip JFrame. Descrierea modului de lucru se face
pentru NetBeans 7.2.
Indiferent de versiunea utilizată, în modul de lucru vizual ecranul mai con ține pe lângă fereastra proiectului
si fereastra de rezultate ( Output ) o fereastră în care putem comuta între două ecrane diferite: ecranul numit

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 154 – Design (cu suprafata JFrame, paleta de componente Palette si foile de proprietăti) și ecranul Source cu codul
sursă generat de NetBeans pe baza desenului din ecranul Design .

Primul exemplu este un buto n care generează un semnal sonor la actionarea sa. Se creează un nou proiect, se
acceptă numele propuse pentru proiect și pentru clasa JFrame și se trage pe suprafata de proiectare o
componentă JButton. Se editează proprietatea ―text‖ (Edit Text) pentru mo dificarea inscrip ției de pe buton:
Clic dreapta pe buton > Edit Text > Click Me

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 155 –

In modul Design se pot muta (―trage‖) imaginile componentelor, se pot trage de marginile lor pentru
modificarea dimensiuilor și se pot ajusta spa țiile libere (intervalele) di ntre componente.
Imaginea din ecranul Design nu este identică cu interfa ța afisată la execu ția codului generat; pentru a vedea
cum va arăta la execu ție fie executăm aplica ția, fie afi șăm un Preview folosind butonul
.

Pentru asocierea unei ac țiuni la ac ționarea butonului trebuie tratat evenimentul generat de ―apăsarea‖ sa prin:
Clic dreapta pe buton > Events > Action > actionPerformed.

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 156 – IDE generează scheletul metodelor ascultător și asocierea lor cu componentele respective, iar utilizatorul
poate să comp leteze manual, în modul Source , instruc țiunile din metodele ascultător specifice aplica ției.

Vom introduce o singură instruc țiune, pentru a genera un semnal beep :

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 157 – Codul sursă generat are sec țiuni care nu pot fi modificate manual (marcate cu un fond c olorat) și sec țiuni
care pot fi modificate direct de utilizator. Sec țiunile nemodificabile sunt cele care con țin declara ții de
variabile și func ția initComponents() care ini țializează aceste variabile și care stabile ște modul de dispunere
(GroupLayout ). Numele variabilelor sunt generate de IDE pe baza numelor componentelor Swing (jButton1,
jButton2,…) și pot fi schimbate numai din modul Design (clic dreapta > Change Variable Name).
In al doilea exemplu se dezvoltă o aplica ție cu interfa ță grafică
pentru simularea unui calculator de buzunar cu patru opera ții
aritmetice. Interfa ța grafică con ține următoarele componente:
trei câmpuri text pentru introducerea celor două numere și
pentru rezultat, trei etichete pentru fiecare câmp text și patru
butoane pentr u comanda opera țiilor.
Se va crea un proiect nou de tip Java Application dar fără bifarea
casetei Create Main Class . Se va adăuga acestui proiect o
fereastra JFrame astfel:
Clic dreapta pe nume proiect > New > Other > JFrame Form

Se aduc apoi succesiv p e suprafa ța de lucru JFrame componentele din paleta de componente: clic stânga pe
numele și desenul componentei din fereastra Palette , deplasare în pozi ția dorită din fereastra centrală și clic
stânga pentru aducerea componentei selectate (în forma și cu n umele propuse de IDE). Pentru modificarea
unei proprietă ți se face clic dreapta pe componenta respectivă și se alege proprietatea; de exemplu pentru a
schimba numele etichetei (din jLabel1, jLabel2, etc) se alege Edit Text și se introduce alt nume pe desen ul
componentei.

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 158 – Pentru tratarea evenimentelor generate de o componentă se face clic dreapta pe componentă, se alege Events
și apoi tipul de eveniment și numele metodei ascultător. Pentru butonul de adunare se va alege Events >
Action > actionPerformed, du pă care IDE comută pe ecranul Source în pozi ția unde trebuie inserate
instruc țiuni în metoda actionPerformed() . Exemplu de instruc țiuni introduse:
float num1 = Float.parseFloat(jTextField 1.getText());
float num2 = Float.parseFloat(jTextField 2.getTe xt());
float result = num1+num2;
jTextField 3.setText(String.valueOf(result));

Pentru verificare a se execut ă aplica ția: Run > Run Main Project (F6)
Dacă se cere numele clasei cu metoda main() atunci se accept ă sau se introduce numele clasei cu interfa ța
grafic ă (de ex. NewJFrame ).
Pentru execu ția aplica ției fără NetBeans (în linie de comandă) trebuie creată o arhivă jar cu rol de fi șier
executabil (în subdirectorul dist al proiectului, pentru distribuirea aplica ției catre clien ți):
Run > Clean and Build Main Project (Shift -F11)

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 159 – Capitolul IE.11. Platforma Java
Cuvinte cheie
Platforma Java, JVM (Masina virtuala Java),
Groovy, Scala, Limbaje statice, Limbaje dinamice, Scripting,
Functori (Closure), Metaprogramare, Limba je specializate (DSL)

IE.11.1 Java ca platformă orientată obiect
Java a început ca un limbaj de programare dar în prezent a devenit o platformă de programare pe care se
pot utiliza mai multe limbaje (Java, Groovy , Scala , JRuby, Jython, Clojure, s.a.) car e au în comun acela și
cod intermediar, aceea și mașină virtual ă Java și acelea și biblioteci de clase (extinse de obicei ).
Codul intermediar generat de compilatoarele Java (numit ―bytecode‖) este int erpretat sau executat într -o
mașină virtual ă Java (JVM). Mașina virtuală Java împreun ă cu bibliotecile standard de clase Java formeaz ă
―mediul de execu ție Java‖ (JRE=Java Runtime Environment).
O parte dintre aceste noi limbaje au și câte un ―framework‖ pentru dezvoltarea rapid ă de aplica ții Web:
Grails pentru G roovy, Lift pentry Scala, Django pentru Python (Jython), Ruby on Rails pentru JRuby.
Interpretorul Java încarc ă automat sau la cerere clasele necesare (din fi șiere de tip ―class‖ sau de tip ―jar‖
locale sau aduse prin re țea de la alte calculatoare). Aces t mod de lucru face posibil ă utilizarea de clase cu
nume cunoscut doar la execu ție sau înlocuirea unor clase, f ãră interven ție în codul surs ă (cu condi ția ca
aceste clase s ă respecte anumite interfe țe). Impreun ă cu codul clasei (metodele clasei în format compilat) se
mai încarc ă și informa ții despre clas ă, numite și metadate: tipul clasei (sau interfe ței), numele superclasei,
numele variabilelor din clas ă, numele și tipul metodelor clasei, formele de constr uctori pentru obiectele
clasei ș.a. Aceste metada te permit ob ținerea de informa ții despre clase la execu ție, instan țierea de clase
cunoscute numai la execu ție, apeluri de metode determinate la execu ție și alte opera ții imposibile pentru un
limbaj compilat.
Semnalarea erorilor prin excep ții și inform ațiile furnizate despre excep ții se datoreaz ă tot execu ției
programelor Java sub controlul ma șinii virtuale. Codul intermediar Java este ―a sistat‖ (supravegheat) la
execu ție de c ătre ma șina virtual ă, ceea ce a dat na ștere expresiei de cod controlat (―mana ged code‖).
Aceste facilit ăți, alături de independen ța față de procesorul pe care se execut ă, explic ă de ce limbajele mai
noi orientate pe obiecte (Java, C#, Ruby s.a.) sunt (parțial) interpretate într-o ma șină virtual ă.
Limbajele moderne de programare pot fi împ ãrțite în dou ă categorii mari:
– Limbaje statice , cu tipuri de date declarate și verificate la compilare : Java, Scala ș.a.
– Limbaje dinamice , cu tipuri de date stabilite și modificate la execu ție: Javascript, PHP, ș.a.
Limbajele dinamice sunt de obicei interpretate, f ără o compilare anterioar ã, fiind numite si limbaje de
scripting (―script‖= program scurt direct interpretat pentru a produce rapid rezultate). Limbajul Groovy
poate fi folosit atât ca limbaj compilat cât si ca limbaj interpretat (d e scripting); el permite și tipurile de date
statice din Java dar și tipuri dinamice, deduse din modul lor de utilizare.
IE.11.2 Limbajul Groovy
Dintre limbajele utilizabile pe platforma Java cel mai apropiat ca sintax ă de Java și cel care o recunoa ștere
oficial ă ( JSR#241, JSR= Java Service Request) este limbajul Groovy.

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 160 – Groovy este un limbaj utilizabil pe ma șina virtual ă Java, cu o sintax ă asem ănătoare cu Java dar mai simpl ă,
care poate fi utilizat ca limbaj de scripting sau ca limbaj compilat. Codul su rsă Groovy este mult mai
compact decât codul Java echivalent dar și mai u șor de citit pentru c ă are constructii mai ―naturale‖ .
Groovy are facilit ăți inspirate din Ruby, Python, Smalltalk si ilustreaz ă multe concepte moderne în
programare: programare ori entat ă pe obiecte avansat ă (fără tipuri primitive, cu obiecte colec ție, ș.a.) lucrul
cu expresii regulate, facilit ăți de prelucrare si de creare documente XML, functori (closure ), obiecte colec ție
cu o sintax ă simpl ă (fără instan țiere de clase), tipuri def inite implicit prin valoarea atribuit ă (duck types ) si
dinamice (o variabil ă își poate modifica tipul), metaprogramare (Meta Object Protocol ) care permite
adăugarea de noi metode la execu ție fie unei clase fie unui obiect, definirea și utilizarea de limbaj e
specializate în Groovy ( DSL =Domain Specific Languages ), adăugarea simpl ă de teste unitare și de obiecte
surogat ( Mock Objects ), integrarea în limbaj a unor instrumente de gestiune a proiectelor software (Ant,
Maven), ș.a.
Groovy folose ște și extinde JDK: bibliotecile de clase Java, unele extinse cu noi metode, la care se adaug ă
clase specifice Groovy (GDK). Oricare alte biblioteci de clase Java pot fi folosite în Groovy.
Cele mai importante medii integrate pentru dezvoltare de programe Java (Eclipse, Ne tBeans, IDEA) au
suport și pentru limbajul Groovy (recunoa ștere sintax ă, documenta ție, integrare cu JUnit).
IE.11. 3 Simplificarea codului sursă în Groovy

Groovy aduce, fat ă de Java, unele simplific ări în sensul elimin ării unor elemente de cod neesen țiale astfel
încât programatorul s ă se poat ă concentra pe logica programului, eliberat fiind de unele constrângeri
sintactice, care l-ar putea distrage de la esen ța programului. Se spune c ă Groovy cere mai pu țin ―protocol‖
decât Java (mai pu ține declara ții, instruc țiuni, paranteze, virgule sau al ți separatori ). In plus, un script
Groovy poate s ă conțină direct instruc țiuni și variabile, f ără a fi nevoie s ă declar ăm o clas ă și o func ție
―main‖ pentru a compila și executa acele instruc țiuni. Exemplu de scri pt Groovy care ordonează o listă :

– Pachete de clase importate implicit în Groovy (f ără a mai folosi instruc țiuni import ):

java.lang.*, java.util.*, java.io.*, java.net.*, groovy.lang, groovy.util

– Metodele print() și println() au fost adă ugate clasei Object și sunt folosite în locul metodelor
System.out.print() și System.out.println() .
– Terminatorul de instruc țiune ';' poate lipsi dac ă acea instruc țiune este singur ă pe o linie. Ex emplu :

println ( "Today is" + new Date() )

– Parantezel e (cu sau f ără argumente) folosite la apelarea metodelor pot lipsi în general (dar exist ă și situa ții
când ele sunt necesare). Ex emplu :

println "Today is " + new Date()

Parantezele sunt totu și necesare pentru a deosebi un apel de func ție fără parametri de o variabil ă (în scripturi
și în functori).

– Se poate da factor comun tipul mai multor argumente succesive. Ex emplu :
list=[4,2,8,6]
list.sort { a,b -> b-a}
println list

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 161 – def rest(int a,b) { return a%b }

– Se poate omite tipul variabilelor sau argumentelor de func ții; acest tip este considera t provizoriu ca fiind
Object până la atribuirea unei valori care va determina tipul efectiv al variabilei. Exemplu:

def rest(a,b) { return a%b }

Tipul variabilelor și al func țiilor poate fi specificat explicit sau rezult ă implicit din valoarea atribui tă acelei
variabile (ca și în alte limbaje dinamice ). Cuvântul cheie def se foloseste pentru a declara variabile cu tip
neprecizat. Dac ă nu se declar ă un tip explicit trebuie folosit def. Exemple :

Date date = new Date()
def date = new Date( )
def now ( ) { return new Date( ) }
def nl ( ) { println }

– Instruc țiunea return poate lipsi din defini ția unor metode, iar rezultatul metodei se consideră a fi rezultatul
ultimei instruc țiuni executate:
def rest(a,b) { a%b }

Uneori instruc țiunea return este necesar ă în defini ția unei func ții sau a unui functor.

– In Groovy exist ă două clase pentru șiruri: String si GString . Constantele de tip String se scriu între
apostrofuri, iar constantele de tip GString între ghilim ele. Intr-un sir GString se pot folosi substitu ții de
variabile si expresii de forma $var sau ${expr} .
Concatenarea de șiruri constante cu șiruri variabile (valori ale unor variabile sau rezultat al unor func ții)
poate fi evitat ă prin includerea într -un șir cu ghilimele (duble) a unor expresii de forma $var sau ${exp} care
înlocuiesc numele variabilei sau expres ia cu valorile lor (ca șiruri). Ex emple :
println "Today is ${new Date()}"
def msg="Azi este "
def date= new Date(); println "$msg $date"

– In definirea de clase se poate omite cuvântul "public" atât pentru clase cât și pentru metode, deoarece este
implicit (clasele și metodele sunt implicit publice, dar pot fi declarate și private sau protected ). Ex:

String toString () { "($re,$im)" } // din clasa Complex

IE.11. 4 Operatori și instruc țiuni Groovy

– Operatorul Groovy '?.' se poate folosi în locul operatorului '.' pentru o dereferen țiere "sigur ă", care s ă evite
producerea excep ției NullPointerException . Exemplu:

book?.title // are rezultat null daca book este null, dar nu produce exceptie

– Constantele numerice sunt tratate în Groovy ca obiecte (din clasele Integer, Float, Double ) și nu mai
exist ă tipurile primitive din Java. Clasa Integer are si metode n oi, cum ar fi metoda times care repet ă o
secven ță de cod ( un functor, mai exact) de un num ăr de o ri egal cu valoarea obiectului î ntreg. Ex emplu :
3.times {println} // scrie 3 linii albe

– Instruc țiunea for poate avea si o form ă care foloseste cuvântu l cheie in și repet ă ciclul pentru valori ale
variabilei contor dintr -un subdomeniu ( range ) sau dintr -o colec ție. Exemple:

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 162 – for ( k in 0..3) println // scrie trei linii albe
String[ ] days= ['Luni','Marti','Joi','Vineri']
for ( day in days) prin tln day

Pentru compara ție urmeaz ă versiunea Java :

String[ ] days= {'Luni','Marti','Joi','Vineri'};
for ( String day: days) System.out.println (day);

De observat și sintaxa diferit ă de ini țializare a unui vector în Groovy fa ța de Java.

– Comp arația de obiecte la egalitate se face în Groovy cu operatorul ' ==' (tradus în Java prin metoda equals()
sau prin compareTo() ). Exemplu:

s1='Groovy'; s2= new String('Groovy')
println s1==s2 ? 'egale' : 'diferite'

Dacă se dore ște compararea la ide ntitate (egalitate de adrese ale obiectelor) atunci se folose ște metoda is().
Exemplu :
obj1.is(obj2) // true/false

– Nici o excep ție nu mai trebuie tratat ă obligatoriu în Groovy, dar este posibil ă tratarea lor explicit ă la fel ca
în Java (pri n try..catch ). Toate excep țiile sunt deci considerate ca fiind de tipul RunTime Exception .
Aceast ă facilitate, împreun ă cu noi metode în clasele de intrare/iesire simplific ă mult programarea opera țiilor
cu fluxuri de intrare -ieșire.
– Groovy extinde expres iile cu rezultat boolean dincolo de expresiile condi ționale astfel c ă și alte valori sau
obiecte pot fi testate ca ni ște condi ții (în instruc țiuni if, while, assert ș.a.):
variabile referin ță (false dacă null, true dacă !null )
colec ții și dicționare ( false dacă colec ție vida, true dacă are cel pu țin un element)
numere ( false dacă zero)
șiruri ( false dacă șir vid)
potrivirea unui șir cu o expresie regulat ă (false dacă nici o potrivire( match ))
iteratori ( false dacă nu mai sunt elemente de enumerat)
IE.11.5 Clase Groovy
– O clas ă se define ște ca și în Java, dar metodele get() și set() sunt generate automat și nu mai trebuie
definite. Exemplu:

class Book {
Integer id // sau int id ( generat automat ptr clase domeniu)
String title
int price // sau Integer price
}
// utilizare
def b = new Book()
b.setTitle('POO')
b.setPrice(10)

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 163 – Variabilele dintr -un ob iect, numite și "propriet ăți", pot fi accesate ca și în Java, prin metode get() și set()
(generate automat) sau mai simplu, ca membri ai unei structuri (ca și cum ar fi variabile publice). Selectarea
unei propriet ăți se poate face și cu operatorul [ ].

IE.11. 6 Diferen țe Groovy – Java
– Este posibil ă utilizarea direct ă de obiecte colec ție (liste, dic ționare) f ără instan țierea unor clase. Ex:

[3,7,2,8,4].sort() // [2,3,4,7,8]

– Argumente cuvinte cheie în func ții: La apelul constructorilor si metodelor se pot folosi parametri cu nume.
In cazul unui constructor num ele parametrului efectiv este acela și cu numele câmpului din clas ă care este
inițializat cu valoarea parametrului (este numele unei propriet ăți). Exemplu:
def b= new Book (price:10, title:'POO')

– Chiar dac ă se folosesc tipuri generice în colec ții, Groovy le trateaz ă ca pe colec ții de obiecte Object și pot
primi date de orice tip. Ex emplu :
TreeSet<String> a = new TreeSet<String>()
a.add(123)

– In Groovy este posibil ă supraînc ărcarea operatorilor, prin asocierea fiec ărui operator (c e poate fi redefinit)
cu un nume de metod ă. Exemple de operatori și metode echivalente:
a+b a.plus(b)
a%b a.mod(b)
a++,++a a.next()

Definirea acestor metode într -o clas ă permite apelarea lor ( și) prin operatori. Ex:

– Utilizarea de functori, adic ă scrierea opera țiilor care realizeaz ă o prelucrare acolo unde este nevoie de acea
prelucrare, f ără a defini și instan ția o clas ă cu o func ție care s ă conțină acel cod. Ex emplu :
println ( [3,7,2,8,4]. findAll { x -> x< 6} ) // [3 ,2,4] class Complex {
private double re,im
Complex (double re, double im) { this.re=re;this.im=im }
def plus (Complex x) { this.re+=x.re; this.im+=x.im; return this }
String toString() {" ($re,$im)" }
}
Complex a=new Complex(3,4), b= new Complex(7,6 )
println a+b // sau a.plus(b)
// varianta de utilizare
def b = new Book()
b.title='POO' // b.setTitle ('POO')
b.price=10
println b.title+' '+ b.price
// println b.getTitle()+ ' '+ b.getPrice()
b['title']='POO'
b['price']=10

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 164 – Când exist ă un singur argument într -un functor, atunci se poate folosi cuvântul it pentru acest argument, care
nu se mai declar ă explicit. Ex emplu :
println ( [3,7,2,8,4]. findAll { it < 6} )

– Existen ța unor metode suplimentare în clase, mu lte din ele având ca argument un functor. Ex:
println ([1,2,3,2,3,3].unique()) // [1,2,3]
new File(‘.’).eachFileRecurse ,println it-

– Cuvinte cheie noi în Groovy: def, as, in, it
it este folosit ca argument unic implicit în functori
def folose ște la declararea unor variabile/metode de un tip neprecizat
as folose ște la definirea de nume alternative ( alias ) pentru clase și metode (într -o instruc țiune import ) dar și
pentru atribuirea de tip unui bloc. Exemple:
import static Math.random as rand
import groovy.lang.ExpandoMetaClass as EMC
double value=rand()
def metaClass= new EMC(Integer)

IE.11. 7 Metode noi în clase preluate din Java

Metode echivalente unor operatori și care permit redefinirea acestor operatori :

Operator Nume Metoda Operanzi

a + b Plus a.plus(b) Number, string, collection
a – b Minus a.minus(b) Number, string, collection
a * b Star a.multiply(b) Number, string, collection
a / b Divide a.div(b) Numbe r
a % b Modulo a.mod(b) Integral number
a++,++a Increment a.next() Number, string, range
a–, –a Decrement a.previous() Number, string, range
a**b Power a.power(b) Number
a | b Or a.or(b) Integral number
a & b And a.and(b) Integral number
a ^ b Xor a.xor(b) Integral number
~a Complement a.negate() Integral number, string
a[b] Subscript a.getAt(b) Object, list, map, String, Array
a[b] = c Subscript a.putAt(b, c) Object, list, map , StringBuffer,
Array
a << b Left shift a.leftShift(b) Integral number, “append” to
StringBuffers, Files, Writers, Lists
a >> b Right shift a.rightShift(b) Integral number
a >>> b RSh unsign a.rightShiftUnsigned(b) Integral number
a == b Equals a.equals(b) Object
a != b Not equal ! a.equals(b) Object
a <=> b Spaceship a.compareTo(b) java.lang.Comparable
a > b Greater than a.compareTo(b) > 0
a >= b Greater or equal a.compareTo(b) >= 0
a < b Less than a.compareTo(b) < 0
a <= b Less or equal a.compareTo(b) <= 0
a as type Type coercion a.as Type(typeClass) Any type
switch(a){ Classificatio n b.isCase(a) Object, range, list, collection, pattern, closure;
case b:
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 165 – Metode noi în clasa String :

String capitalize() Prima litera din sir devine litera mare, daca nu era deja
String center (Number size) Centrare sir pe o lungime data, cu adaug are de spa ții
Object eachLine (Closure clos) Aplica un functor pe fiecare linie din sir
String getAt(int k) Extrage caracterul din pozitia k (alternativa la operator de indexare)
String padLeft (Number size) Adauga spa ții la stânga până la lungimea „si ze‟
String padRight (Number size) Adauga spa ții la dreapta până la lungimea „size‟
int size() Lungime sir (echivalent cu length())
String stripIndent() Eliminare spatii din fata fiecarei linii din sir
List tokenize() Creare lista de cuvinte separate prin spa ții în sir
List tokenize(Character c) Creare lista de cuvinte separate prin caractere c în sir
List tokenize(String delim) Creare lista de cuvinte separate prin sirul delim în sir

Metode noi în clase de I/E

Metode din clasa Reader :

String getTe xt() Citeste con ținut obiect Reader ca sir
List readLines() Citeste toate liniile intr -o lista de linii
Iterator iterator() Creare iterator pe liniile citite
Object eachLine (Closure cls) Pentru fiecare linie citită aplică un functor „cls‟

Metode din clasa InputStream :

Object eachLine (Closure fun) Aplica un functor fun fiecărei linii citite
Iterator iterator() Creare iterator pe octe ți cititi
String getText() Creare sir cu toti octe ții cititi
List readLines() Creare lista cu toate liniile citite

Există metode pentru citire linii în toate clasele Reader și InputStream deci și pentru System.in .

Metode din clasele PrintWriter, PrintStream :

void print (Object obj) Scrie valoarea obiectului „obj‟ formatată în stil Groovy
void println (Object obj) Scrie valoarea obiectului „obj‟ formatată în stil Groovy si trecere la linie nouă

Exemple de citire linii de la consol ă și creare multime de cuvinte distincte extrase din aceste linii:

String text= System.in.getText()
Set words = text.tokenize() as Set

IE.11. 8 Utilizarea de expresii regulate ( RegEx )

Expresiile regulate folosesc la c ăutarea într -un text a unor secven țe care se ―potrivesc‖ ( matches ) cu un
șablon dat. Un șablon ( pattern ) descrie un format comun mai multor siruri (care pot avea si lungimi diferite)
folosind anumite caractere speciale, cu o anumit ă semnifica ție. Exemple de caractere utilizate în șiruri
sablon:
. orice caracter
\d orice cifra zecimala
\D orice caracter diferit de cifre zecimale
x* oricâte caractere x consecutive (eve ntual zero)

Groovy foloseste clasele Java pentru expresii regulate, la care adaug ă câțiva operatori:

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 166 – ~String pentru siruri sablon (pattern), obiect de tip ―java.util.regex.Pattern‖
=~ pentru metoda ―find‖ (c ăutare cu sablon pentru o potrivire parti ală)
==~ pentru metoda ―match‖ (o potrivire complet ă cu sablon)

Pentru a evita util izarea de secvente ―escape‖ în șirul sablon, în Groovy o constant ă șir poate fi delimitat ă și
de caractere ―slash‖ (‗/‘), pe lâng ă delimitatorii ghilimele (―) folositi în Java. Exemplu:
/\d\w/ în loc de ― \\d\\w‖
Operatorii ‗=~‘ și ―==~‖ au ca rezultat un obiect de tip java.util.regex.Matcher , iar pentru aces t obiect,
considerat ca o colec ție de secvente potrivite cu șablonul, putem folosi metodele:
size() ca să aflăm numărul de potriviri.
each() ca să aflăm fiecare potrivire a șablonului în șirul unde se caut ă.
replaceAll() ca să înlocuim toate secven țele care s -au potrivit cu un alt șir
Exemple:

IE.11. 9 Functori ( Closures ) în Groovy

Noțiunea de closure (închidere) este preluat ă din limbajele func ționale, ca și cea de functor (obiect func ție);
limbajul C# folose ște cuvântul delegate pentru aceea și construc ție (o func ție tratat ă ca obiect).
In Groovy un functor este o secven ță de cod între acolade (cu parametri) care poate fi atribuit ă unei variabile
(referin ță), poate fi transmis ă ca argument unei func ții (sau unui alt functor) si care poate constitui rezultatul
unei func ții (sau unui functor). Un functor poate fi folosit la fel ca orice al t obiec t si are tipul implicit
Closure (tip care poate fi folosit și în declararea unor variabile sau argumente).
In Java o clas ă cu o singur ă funcție poate fi definit ă ca o clas ă top-level sau ca o clas ă inclus ă cu sau f ără
nume; clasa trebuie instan țiată pentr u a folosi un obiect ce contine o func ție. Forma Java cea mai apropiat ă
de un functor Groovy este o clas ă inclus ă anonim ă definit ă în momentul instan țierii ei (instan țiere care poate
avea loc chiar în instruc țiunea care foloseste obiectul).
Un functor Gro ovy nu necesit ă definirea unei clase care s ă respecte o anumit ă interfa ță (un functor nu este
legat de o anumit ă interfa ță). In Groovy exist ă mai multe func ții predefinite care au ca argument un functor;
acest functor se poate defini în prealabil ( și cap ătă un nume) sau chiar în momentul folosirii lui ( functor
anonim). Utilizarea de functori are ca efect un cod surs ă mai compact și mai usor de în țeles (permit o
exprimare mai direct ă și mai natural ă a unor ac țiuni).
pattern = ~"(G|g)roovy"
text = 'Groovy is groovy really groovy'
def m= (text =~ ~/Ggr/)
println m.size()? 'gasit' : 'negasit' // negasit
assert (text ==~ pattern) == false // nu este o potrivire completa text cu
pattern
// mai multe potriviri ale sablonului in text
m= (text =~ pattern)
m.each {println it} // afi șare potriviri (secvente, sablon)
for ( i in 0..m.size() -1) println m[i][0] // afi șare numai secvente din text
println ((text =~ /groovy/).replaceAll( 'nice')) // Groovy is nice really nice

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 167 – IE.11. 9.1 Sintaxa pentru definirea si utilizarea de functori

Instruc țiunile dintr -un functor sunt încadrate de acolade, dar compilatorul Groovy deosebe ște un bloc de cod
de un functor. Un bloc poate s ă apară în defini ția unei clase, a unei interfe țe sau a unei metode, în
instruc țiunile if, wh ile, for, do, switch, try sau în ini țializari (de vectori, de ex.). Orice alt ă utilizare a unui
bloc între acolade este considerat ă ca un functor. Primul exemplu este un functor f ără parametri care
afișează o linie alb ă:
def nl ={ println ''} // definire functor cu nume
3.times (nl) // utilizare functor nl
3.times nl // alta utilizare functor nl

De multe ori functorul este definit chiar în locul unde este folosit și nu are nume. Ex emplu :

3.times { pri ntln ()} // scrie 3 linii albe

De observat absen ța parantezelor pentru argumentul func ției times() ; forma anterioar ă este o forma abreviat ă
pentru:
3.times ( { println()} )

Func ția println() din functor trebuie s ă aibă fie paranteze, fie un a rgument deoarece, în lipsa acestora, este
considerat ă ca o variabil ă din scriptul (din spa țiul de nume) în care este definit functorul, dar nu exist ă o
variabil ă cu acest nume.
Un functor poate fi apelat ca o func ție folosind metoda call() (din clasa Closu re). Ex emplu :
def wline = { def ln=''; 50.times{ln+=' -'}; println (ln)}
wline.call()

Un functor poate avea un rezultat. In exemplul urm ător functorul are ca rezultat un sir:

def line = { def ln=''; 50.times{ln+=' -'};return ln}
print line.call()

Instrucțiunea return poate lipsi dac ă rezultatul fu nctorului este acela și cu rezultatul ultimei instruc țiuni:

def line = {def ln=''; 50.times {ln +=' -'}; ln+=' \n'}

Obiectele functor apar țin clasei groovy.lang.Closure , iar cuvântul Closure poate fi folosit î n declararea de
variabile (inclusiv proprietă ți ale unei clase), de argumente și de func ții, ca orice alt tip clasă. Exemplu:

După contextul în care este definit un functor putem avea urm ătoarele situa ții: // calcul timp de rulare 'action' de n ori
def calcTime(n,Closure action) {
start=System.currentTimeMillis()
n.times {action (222,5)}
stop=System.currentTimeMillis()
return stop -start
}
println calcTime (1000,rec) // repeta de 1000 de ori calculul cmmdc(222,5)

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 168 – – Functor definit într -un script;
– Functor definit într -un obiect (într -o clasă)
– Functor definit în alt functor

IE.11.9.2 Functori cu argumente

Un functor poate avea argumente; dac ă este un singur argument, acesta poate folosi numele implicit it și nu
trebuie declarat explicit (dar poate fi). Exemplele urm ătoare folosesc argumentul implicit it:

Parametrii efectivi pot fi transmi și prin metoda call() din clasa Closure sau direct dup ă numele functorului
(utilizat ca o func ție).
In cazul parcurgerii unui dic ționar cuvântul cheie it se refer ă la o pereche cheie -valoare. Exemplu de creare a
unui dic ționar invers (în care cheia din dictionarul initial devine valoare iar valoarea devine cheie în noul
dictionar):

Exemplu de functor cu dou ă argumente folosit drept comparator la so rtarea unei liste:

fruit = [ "apple", "Orange", "Avocado", "pear", "cherry" ]
fruit.sort { a,b -> a.size() – b.size() } // odonare dupa lungime siruri

Este posibil s ă definim și tipul argumentelor, pentru a face codul mai usor de în țeles. Exemplu:

def w ord = { String s, int k -> s.split(' ')[k] } // functor cu doua argumente

Exemplu de funct or care extrag e un cuvânt dintr -un șir:

Pentru a introduce o nou ă metod ă word() în clasa String trebuie s ă adăugăm metaclasei un functor, care va
acționa a supra obiectului pentru care se apeleaz ă metoda (―delegate‖):
String.metaClass.word={ delegate.split(' ')[it] }
// utilizare
for (k in 0..3) println str.word(k) // unu,doi,trei,patru

// functor anonim definit ad -hoc
1.upto(10) { println "Radical din $it = ${Math.sqrt(it)} " }
// functor cu nume
def sqrt= { println "Radical din $it = ${Math.sqrt(it)} " }
for ( k in 1..10) sqrt.call(k) // forma extinsa de folosire
for (k in 1..10) sqrt(k) // forma scurta
romeng =['unu':'one','doi':'two','trei':'three'] // roman -englez
romeng.each { println it.key+'='+it.value} // afi șare
engrom=[:] // englez -roman
romeng.each {engrom[it.value]=it.key }
println engrom // afi șare cu Map.toString
def word = { s, k -> s.split(' ')[k] } // functor cu doua argumente
// utilizare fu nctor “word”
str="unu doi trei patru "
for (k in 0..3) println word(str,k) // unu,doi,trei,patru

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 169 – IE.11.9.3 Aplica ții uzuale pentru functori

Functori cu rol de functii callback

Rolul unei func ții callback în programare poate fi ilustrat prin func ția de comparare de obiecte care trebuie
transmis ă unei func ții de ordonare (sau de c ăutare într -o colec ție ordonat ă): definitia func ției de comparare
depinde de tipul o biectelor comparate dar ea poate avea acela și prototip indiferent de tipurile comparate
(folosind tipul generic Object în Java sau void* în C). Pentru a avea o singur ă functie de sortare pentru orice
listă (cu date de orice tip) vom transmite acestei func ții ca argument func ția de comparare. Exemplu de
ordonare descresc ătoare a unui vector de obiecte în Java:

In Groovy metoda "sort" se poate aplica oric ărui obiect de tip Collection , are ca rezultat un obiect de tip List
și poate avea ca argument un obiect de tip Comparator sau un functor. Exemplu cu argument Comparator :
list=['unu','doi','trei']
println list.sort() // crescator
println list.sort (new Comparator() {int compare(a,b){ b.compareTo(a)} })

Varianta cu functor este mai scurt ă si este în spiritul limbajului Groovy:

println list.sort {a,b -> b.compareTo(a)}

In Groovy metodele care pot avea ca parametru ( și) un functor sunt fie metode noi, fie metode Java care au
fost supraîncărcate cu o noua formă.
Functori ca ac țiuni repeta te într -un ciclu sau într -o enumerare

Primul exemplu este solu ția alternativ ă Groovy pentru programarea unui ciclu prin introducerea în clasa
Number a unor metode noi cu argument functor:
times(Closure cl), upto (Number to, Closure cl), eac h (Closure cl)

Exemple:

Arrays.sort (a, new Comparator() { // definire clasa anonima
public int compare (Object o1, Object o2) { // cu o singura functie
Comparable c1=(Comparable)o1;
Comparable c2=(Comparable)o2;
return c2.compareTo(c1);
}
});
// calcul factorial 5!
nf=1
(1..5).each {nf *= it } // varianta 1
1.upto(5) {nf *=it} // varianta 2

boolean prim(int n) { // definire functie de test daca numar prim
este=true
3.upto(n -1){ if(n%it==0) este=false}
return este
}
5.upto(50), print prim(it)? it+' ':''- // utilizare functie “prim”

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 170 – Deseori apare situa ția în care o colec ție de obiecte este parcurs ă pentru a prelucra elementele colec ției sau
pentru a c ăuta un anumit obiect sau pentru a selecta obiect ele care satisfac anumite condi ții. Pentru fiecar e
element din colec ție se execut ă o actiune (calcule, compara ții). In Java se foloseste de obicei un iterator pe
colec ție sau un ciclu cu reg ăsire prin indici asocia ți elementelor colec ției (numai pentru liste):
for (Iterator it=list.iterator(); it.hasNe xt();) {
Integer x = (Integer) it.next();
System.out.println ( x%2==0 ? x+" par" : x+" impar");
}

Incepând cu Java 5 este simplificat ă iterarea pe o colec ție generic ă:

for (Integer x: list)
System.out.println ( x%2==0 ? x+" par" : x+" impar");

In practic ă exist ă anumite prelucr ări tipice pe colec ții : c ăutarea primului obiect sau tuturor obiecte lor care
satisfac anumite condi ții, aplicarea unor opera ții (transform ări) fiec ărui element din colec ție, pe loc sau cu
crearea unei n oi colec ții cu rezultatele opera țiilor. Pentru aceste prelucrari s -au definit în Groovy noi metode
aplicabile tuturor colec țiilor: find(), findAll(), each(), collect(), sum() ș.a. Aceste metode primesc ca
argument un functor ce defineste fie criteriul de c ăutare, fie opera ția aplicat ă elementelor colec ției.
Argumentul implicit din functor desemneaz ă elementul curent din colec ție. Exemple:
int[] a =[1,2,3,4,5,6,7,8,9]
a.each { println " $it ${it%2==0?'even':'odd'}" }
a.findAll { it % 2 } // scrie numerele impare (1 3 5 7 9)

Anumite metode din interfa ța Java Collection au fost supraînc ărcate cu o form ă care admite ca argument un
functor. Exemplu de eliminare a duplicatelor dintr -o list ă :
a =[1,2,3,1,1,4,4,2] ; a.removeAll { a.count (it)>1 }
println a.sort() // [ 1 2 3 4 ]

Metoda each() este aplicabil ă și caracterelor dintr -un șir. Exemplu:

'abcd'.each { print it+' ' } // scrie: a b c d

In clasa File s-au introdus metode de enumerare a fisierelor dintr -un director ( eachFile ) si de enu merare a
subdirectoarelor ( eachDir ) cu parametri functor. Exemplu de definire a unui functor recursiv:

Variant ă de func ție pentru listare recursiv ă fișiere dintr -un director:

def listFiles // declaratie necesara inaintea apelul ui recursiv
listFiles = {
println "Dir ${it}"
it.eachDir (listFiles) // parametru nume de functor
it.eachFile { if(! it.isDirectory()) println " File ${it}" }
}
// utilizare
listFiles ( new File ("d:/groovy"))

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 171 –

In Groovy aceast ă opera ție se poate face mai simplu cu o noua metod ă:

new File(".").eachFileRecurse {println it}

Metoda eachLine() din clasa File repet ă un functor pentru fiecare linie dintr -un fișier, incluzând deschiderea,
inchiderea fi șierului și tratarea eventualelor excep ții. Exemplu de afi șare cu numerotare linii:
int k=1
new File("a.txt").eachLine { println k++ +' '+it } // { println "$k $it";k++}

Deoarece permit o exprimare mai natural ă a unor opera ții (ac țiuni) functorii se folosesc în crearea de limbaje
specializate DSL.
IE.11.10 Colec ții Groovy
Tipurile colective în Groovy sunt:
– domenii de valori ( ranges )
– liste ( lists)
– asocieri sau dictionare ( maps )

Un domeniu este specificat folosind operatorul ―..‖ (sau ―..<‖) între dou ă valori ce reprezint ă limita inferioar ă
și limita supe rioar ă a domeniului de valori. Exemple:
1..10 // inclusiv limitele 1 si 10
1..<10 // exclusiv limita superioar ă 10
10..1 // domeniu inversat
def a=1..9 // obiect domeniu cu nume

Ca obiecte dintr -o clas ă implicit ă (Range ), domeniile s uport ă anumite metode: contains, size, each , s.a. Un
domeniu se poate folosi într -o instruc țiune for astfel:
def log = '' "
for (x in 1..9) log += x // for ( x in a)

O alt ă utilizare este într -o instructiune switch pentru împ ărtirea uno r valori în (sub)clase:
age = 36
switch(age) {
case 16..20 : insuranceRate = 0.05 ; break
case 21..50 : insuranceRate = 0.06 ; break
case 51..65 : insurance Rate = 0.07 ; break
default: throw new IllegalArgumentException()
}

Metoda each() aplic ă un functor fiec ărei valori din domeniu. Exemplu:
def cls= {x -> println " $x ${x%2==0?'impar':'par'}" } // definire functor cu nume
(1..9).each (cls) // utilizare functor ‘cls’ def files (File f) {
f.eachFile { if( it.isDirectory()) files(it)
else println it
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 172 –
Limitele unui domeniu pot fi obiecte de tip Date sau din orice clas ă care con ține metoda compareTo()
(implementeaz ă interfata Comparable ) sau care implementeaz ă metodele next() si previous() (echivalente
operatorilor ―++‖ si ― —―).
O list ă Groovy este o colec ție cu el emente accesibile prin indici, și care se extinde automat prin atribuire
către elemente cu indici mai mari ca dimensiunea listei. O list ă Groovy combin ă avantajele vector ilor cu
dimensiune fix ă (selec ția de elemente prin indici) cu avantajele obiectelelor din clasa ArrayList (extindere
automat ă, compactare dup ă eliminare de elemente, s.a.). Listele au implicit tipul ArrayList si se pot construi
prin includerea elementelor listei între paranteze drepte si separate prin virgule. O list ă vidă se noteaz ă ca [ ].
Exemple de definire si utilizare a unei liste de siruri:
def a = ['zero','unu','doi','trei'] // un obiect de tip List
println a[1] // scrie ‘unu’
printl n a.size() // scrie 4 (dimensiune lista)
a << 'patru' // adaugare la sfarsit de lista
println a // afisare continut lista
a[8]='opt' // extindere lista
println a.size() // scrie 9 (dimensiune lista)

Pentru afișarea elementelor unei liste, câte unul pe o linie, avem mai multe posibilit ăți:
a.each { println it }
println a.join(' \n') // adauga ‘ \n’ fiecarui element din lista

Se poate folosi selec ția prin indici și extinderea prin folosirea de indici mai mari ca cei existen ți și pentru
obiectele de tip LinkedList . Exemplu:
def a=['a','b','c'] ; def list= new LinkedList (a)
list[3]='d' ; println list

Operatorul de indexare [ ] este echivalent metodelor getAt() (în dreapta) și putAt() (în stânga). Folosind aces t
operator se pot insera sau elimina mai multe elemente dintr -o list ă. Exemple:
myList = ['a','b','c','d','e','f']
myList [0..2] = ['x','y','z'] // devine *‘x’,’y’,’z’,’d’,’e’,’f’+
myList[3..5]=[ ] // devine *‘x’,’y’,’z’+

Pentru ad ăugare de eleme nte la o list ă se pot folosi și operatorii ― +‖ sau ― <<‖, iar pentru eliminare de
elemente se poate folosi operatorul ‗ -‗. Exemple:
a=[]; a+=*’1’,’2’+ ; a<<’3’<<’4’
a -= *‘4’+ // sau a -=’2’

Ca și domeniile, listele se pot folosi în instruc țiuni for,swit ch și în expresii regulate. Exemplu:
def isPrime = {n ->
def primes=[2,3,5,7,11,13]
if (n in primes ) return 'prim'
for (m in primes) {if (n%m==0) return 'neprim'}
return 'prim'
}
(6..20).each {println it+": "+ isPrime(it)}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 173 – In Groovy exist ă o mul țime de metode noi aplicabile listelor: sort, reverse, intersect, disjoint, remove,
removeAll, findAll, unique, collect etc. Metoda collect() aplicat ă unei liste va crea o nou ă listă cu rezultatele
aplic ării unui functor asupra elementelor listei ini țiale. E xemplu:
println ((2..20).asList().collect (isPrime)) // nu pot lipsi parantezele !

Orice list ă se poate folosi ca stiv ă prin metodele pop() și push() (echivalent cu ―<<‖). Ex emplu :
def stiva=[]
(1..5).each { stiva.push(it)}
while (stiva.size()>0)
print stiva.pop()+" "

Un exemplu de utilizare a listelor este si sortarea ―quicksort‖ recursiv ă:

Dictionare (Asocieri)

IE.11.10.1 Dic ționare
O asociere (sau dic ționar ="Map") este o list ă de perechi cheie -valoare, la care cheia poate fi f olosit ă ca
indice. Se poate extinde ca și o list ă. Un obiect dic ționar vid are forma [:]
Sintaxa general ă a unui obiect dic ționar este: [ key1:value1, key2:value2,..]
Exemple:
def b=['unu':1,'doi':2,'trei':3] // un obiect asociere
println b['doi'] // scrie 2
println b.doi // scrie 2
b.patru=4 // extindere dictionar
b['sase']=6 // extindere dictionar
println b // ["unu":1, "doi":2, "trei":3, "patru":4, "sase":6]
println b. size() // scrie 5 (dimensiune dictionar)

Pentru chei de tip String se pot omite ghilimelele, dac ă nu sunt cuvinte cheie ale limbajului și nu con țin
caractere speciale. Exemplu:
def b=[unu:1,doi:2,trei:3]

Selectarea valorii asociate unui chei se poate face în mai multe feluri:
– prin indexare nume dic ționar cu valoarea cheii : println b['doi']
– prin notatia map.key : println b.’doi’
– prin metoda get() : println b.get(‘doi’)
def quickSort(list) {
if (list.size() < 2) return list
def pivot = list[list.size().intdiv(2)]
def left = list.findAll {item -> item < pivot }
def middle = list.findAll {item -> item == pivot }
def right = list.findAll {item -> item > pivot }
return (quickSort(left) + middle + quickSort(right))
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 174 – Metoda get() poate primi dou ă argumente; dac ă nu se g ăseste cheia (primul argument) în dic ționar atunci se
adaug ă la dic ționar o pereche cu argumentele metodei get() .
Pentru asocierile Groovy se pot folosi metodele din interfa ța Map din Java: entrySet, keySet, values ,
containsKey, containsValue , ș.a. plus metode noi ca any() și every() .
Pentru a itera pe un dictionar putem folosi un ciclu for ( x in map) sau metoda each() .
Alte metode noi pentru asocieri sunt:
– subMap() pentru extragerea perechilor care au anumite chei:
myMap = [a:1, b:2, c:3] ; def abMap = myMap.subMap(['a','b'])

– findAll() caută perechile care satisfac un functor: def found = myMap.find { entry -> entry.value < 2}
– find() caută o pereche care satisface un functor
– collect() adun ă într-o list ă perechile care satisfac un functor:
def doubled = myMap.collect { entry -> entry.value *= 2}
In cazul unei metode folosirea unui parametru cu nume este de fapt transmiterea unui dic ționar, în care cheile
sunt nume de argumente iar valorile asociate sunt valorile argumentelor. Pot lipsi parantezele drepte dar nu și
parantezele rotunde care încadreaz ă lista de argumente (care este un dic ționar). Exemplu:
def f (Map args) { ['a','b','c'].each {args.get(it,0) } return args.a – args.b + args.c }
println f ([b:4, a:2])
println f (a:3, b :1, c:2)
println f ( c:1)

Exemplul urm ător folose ște un di ctionar pentru afi șarea frecven ței cuvintelor distincte dintr -un text:

IE.11.10.2 Metode Groovy pentru liste și dicționare
Metode noi în interfa ța Collection : def text="unu doi trei trei doi trei"
def words = text.tokenize( )
def wordFrequency = [:]
words.each { word ->
wordFrequency[word] = wordFrequency.get(word,0) + 1
}
def wordList = wordFrequency.keySet().toList()
wordList.sort { wordFrequency[it] }
def statistic = " \n"
def n=wordList.size()
wordList[0..n -1].each { w ord ->
statistic += word.padLeft(12) + ': '
statistic += wordFrequency[word] + " \n"
}
println statistic

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 175 – List collect (Closure f) aplica functorul f fiecarui element din colec ție si creeaza o lista
Number count (Object value) numara de cate ori apare ―value‖ in colec ție
Object find (Closure f) cauta elementul din colec ție care satisface condi ția f
Collection findAll (Closure f) creare colec ție cu elementele care satisfac condi ția f
Collection plus (Collection c) reunirea acestei colec ții cu colec ția c
List sort () ordonare colec ție cu rezultat lista folosind comparatorul implicit
List sort (Closure f) ordonare folosind drep t criteriu un functor f
Collection split (Closure f) sparge colec ția în doua pe baza functorului f
Collection unique() elimina elementele duplicat folosind comparatorul implicit
Collection unique(Closure f) elimina elementele duplicat folosind functorul f

Exemple de utilizare:
def odd = [1,2,3,4,5].findAll { item ->item % 2 == 1 } // odd=[1,3,5]
assert [1,2,3,4,5] == [1,[2,3],[[4]],[],5].flatten()

Metode noi în interfa ța List:
Object getAt (int k) pentru redefinire operator de selec ție element din l ista: a[k]
List getAt (Range r) ptr redefinire operator de selec ție de forma a[1..2]
List minus (Object obj) ptr redefinire operator ‗ -‗ ptr eliminare din lista
Object pop() scoate si elimina ultimul element din lista
boolean push (Object obj) adaug a ‗obj‘ la aceasta lista
void putAt(int k, Object obj) ptr redefinire operator de indexare la atribuire: a[k]=obj
List reverse() inversare ordine elemente din lista
List reverseEach(Closure f) aplica functorul f cu iterare in ordine inversa

IE.11.11 Metaprogramare în Groovy

IE.11.11.1 Manipularea dinamică de clase si obiecte în Java

Groovy este un limbaj dinamic, adic ă folose ște tipuri dinamice (modificate la atribuire), permite modificarea
la execu ție a tipurilor stabilite la compilare, permite de finirea de noi tipuri în cursul execu ției, are un
mecanism mai flexibil de apelare/interce ptare a metodelor și permite evaluarea de cod la execu ție.

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 176 – In Java se poate explora în cursul executiei, prin reflec ție, structura unui program, astfel c ă putem afla
numele claselor folosite, ce metode și constructori au, ș.a. Aceast ă structur ă este stabilit ă la compi lare (la
scrierea programului) și poate fi folosit ă la execu ție (de ex. pentru a instan ția clase obtinu țe la execu ție), dar
nu mai poate fi modificat ă.
Fiecare clas ă Java are asociat un obiect de tip Class care con ține informa ții despre clasa respectiv ă și care
constituie suportul pentru utilizarea de clase cunoscute numai la execu ție și pentru alte opera ții de reflec ție
sau introspectie. Exemplu de instan țiere clas ă cu nume necunoscut la scrierea programului dar care respect ă
o interfa ță (numele poate fi citit la execu ție dintr -un fișier de propriet ăți):

In Java legarea ( Binding ) corpului metodei de un apel al metodei (nestatice) se face la execu ție si nu la
compilare, ceea ce permite polimorfismul. Se spune c ă pentru metodele obiectelor are loc o legare
―întârziat ă‖ (Late Binding ), spre deosebire de legarea ―timpurie‖ (la compil are) de metodele statice .
Facilit ățile de metaprogramare din Gro ovy pot fi privit e ca o extindere a reflec ției din Java prin ad ăugarea
unei metaclase fiec ărei clase sau obiect. Spre deosebire de obiectul Class din Java, metaclasa Groovy
permite ad ăugarea de noi propriet ăți si metode claselor Groovy (sau Java). In plus se pot intercepta apelurile
de metode existente sau inexistente, pentru ad ăugarea de noi opera ții sau pentru înlocuirea opera țiilor
existente. In timp ce obiectul Class descrie comportarea unei clase la compilare, metaclasa Groovy descrie
comportarea la ex ecuție a obiectelor unei clase.
Exemplul următor pune în evidentă diferenta Groovy -Java la implementarea polimorfismului. In Groovy se
va scrie ―string‖ iar în Java programul următor va scrie ―object‖:

class Foo { // Groovy
def print (Object o) , println “object" –
def print (String s) , println “string" –
}
Object obj = "string"
new Foo().print(obj)
class Foo { // Java
void print (Obj ect o) { System.out.println ("object"); }
void print (String s) { System.out.println ("string"); }
public static void main (String a[]){
Object obj = "string" ;
new Foo().print(obj);
}
} public static Comparator ObjectFactory (String className) {
Comparator obj=null;
try {
Class cls = Class.forName(className );
obj =(Comparator) cls.newInstance();
} catch (Exception e) { e.printStackTrace() ;}
return obj;
}
// utilizare
String className="MyComp"; // se putea citi dintr -un fisier
Comparator comp = Factory.ObjectFactory(className ); // obiect comparator
Collections.sort (list,comp);

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 177 –
IE.11.11.2 Facilităti de metapr ogramare în Groovy

Prin metaprogramare se întelege posibilitatea de modificare a programelor în cursul execu ției, inclusiv
posibilitatea de a evalua expresii simbolice. Mecanismul de implementare a modific ării unui program de
către acesta se numeste proto col meta -obiect (MOP= Meta -Object Protocol ).
Groovy MOP permite interceptarea apelurilor de metode c ătre o clas ă, adăugarea de noi metode claselor
existente și chiar sintetizarea de metode și clase în mod dinamic (la execu ție). Aparent, obiectele î și
modif ică comportarea (deci tipul și clasa) în cursul execu ției.
Groovy MOP se bazeaz ă atât pe utilizarea noilor metode prezente în clasele Groovy ( invokeMethod() ș.a.)
cât si pe utilizarea de metaclase, care sunt generate de compilator pentru clasele Groovy, da r pot fi ad ăugate
și claselor Java care implementeaz ă o anumit ă interfa ță.
In Groovy exist ă mai multe facilit ăți care pot fi asociate cu metaprogramarea:
Evaluarea de cod: evaluate(“def add = ,x, y -> x + y-”)

Interceptarea tuturor apelurilor de metode (invokeMethod() )
Interceptarea apelurilor de metode inexistente ( methodMissing() )
Interceptarea accesului la propriet ăți (getProperty(), setProperty() )
Adăugarea dinamic ă de metode, constructori si propriet ăți (ExpandoMetaClass )
Preluarea temporar ă de meto de de la alte clase ( Categories )
Adăugarea de metode de la alte tipuri ( Mixins )
Modalitatea de modificare a metodelor unei clase depinde de situa ția clasei respective:
– Dacă dispunem de sursa clasei Groovy și o putem modifica atunci se redefine ște metoda invokeMethod
și/sau metoda missingMethod , prezente în clasele Groovy si în clasele Java care implementeaz ă interfa ța
GroovyObject ;
– Dacă nu dispunem de sursa clasei atunci se folose ște o clas ă ExpandoMetaClass generat ă de compilator
pentru clasa care tr ebuie modificat ă si care con ține metodele invokeMethod și missingMethod . Metaclasa
unei clase se obtine printr -o proprietate a oric ărei clase. Exemplu:
Foo.metaClass

Dintre utiliz ările actuale ale metaprogram ării în Groovy men ționăm:
– Programar ea orientat ă pe aspecte (AOP), adic ă injectarea de cod înainte ( before ), dup ă (after ) sau în locul
(around ) codului unei metode (cod scris de programator).
– Crearea de teste unitare (clase si metode de test generate automat).
– Crearea de obiecte surogat (Mock objects ) pentru testare.
– Crearea de metode de interogare a bazelor de date adaptate claselor domeniu (dynamic finders ).
– Crearea de generatori ( builders ) pentru structuri ierarhice.
– Definirea de noi limbaje specializate (DSL= Domain Specific Lan guages )

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 178 –
IE.11.11.3 Crearea de noi clase în Groovy

Așa cum sugereaz ă și numele, clasa Expando se poate extinde dinamic; ea nu are ini țial metode sau
propriet ăți, dar acestea se adaug ă ulterior. Metodele se definesc prin functori.
Exemplu de creare obiect dintr -o clas ă generat ă la execu ție dintr -un Expando :

O nou ă clasă poate rezulta și din reunirea ( mixin ) metodelor altor clase sau interfe țe, ceea ce are efectul
unei mo șteniri multiple. Exemplu:

IE.11.11.4 Metaclase în Groovy

Obiec tele utilizate în Groovy sunt de trei feluri:
– POJO ( Plain Old Java Object ), care extind clasa Object din Java (instan țieri de clase Java).
– POGO ( Plain Old Groovy Object ), care extind clasa Object si implementeaza interfa ța GroovyObject .
– Interceptori, care sunt obiecte POGO cu posibilită ți de interceptare,preluate prin implementarea interfe ței
GroovyInterceptable .
Un obiect POGO are în plus fa ță de un obiect POJO urm ătoarele metode:

Clasele Java (prelucrate de compilatorul Java) pot implement a interfa ța GroovyObject pentru a se comporta
asem ănător (dar nu identic) cu clasele Groovy.
Clasele Groovy și Java, prelucrate de compilatorul Groovy, implementeaz ă automat interfa ța GroovyObject
(au o implementare implicit ă pentru metodele din interfa ță); fiecare clas ă va avea asociat ă o metaclas ă si se def counter= new Expando()
def count= 0
counter.incr= { count++; show() }
counter.decr= { count –; show() }
counter.show= { timesShown++; count }
counter.timesShown= 0
counter.incr(); counter.incr(); counter.decr(); counter.incr()
counter.show() // 2

class Superman { def fly() { pri ntln "fly" } }
class Ninja { def fight() { println "fight" } }
class Person { def sleep () { println "sleep" } }
Person.mixin Superman, Ninja
p = new Person()
p.sleep() ; p.fly() ; p.fight()

public interface GroovyObject{
Object invokeMethod(String na me,Object args);
Object getProperty(String property);
void setProperty(String property,Object newValue);
MetaClass getMetaClass();
void setMetaClass(MetaClass metaClass);
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 179 – poate folosi metode getMetaClass() sau proprietatea metaClass . Exemplu de acces la metaclasa unei clase
JDK:
def ByteMeta = Byte.metaClass // java.lang.Byte
println ByteMeta.methods.join (' \n') // meto de din clasa Byte si metode din GroovyObject

Exemplu de acces la metaclasa unei clase Groovy:

class Book {
String title ; Date year
}
println Book.metaClass.methods.join (' \n')

In exemplul anterior se afi șează și metodele generate automat pentru ac ces la propriet ățile clasei Book :
public String getTitle(), public void setTitle (String title), public .Object Book.getProperty(String), public
void Book.setProperty(String,Object) , ș.a.
Interfa ța GroovyInterceptable extinde interfa ța GroovyObject fără să adauge alte metode; pentru obiectele
care implementeaz ă aceast ă interfa ță toate apelurile de metode sunt interceptate de metoda invokeMethod() .
Metaclasa Groovy con ține metadate despre clasa asociat ă (câmpuri, metode, propriet ăți) și implementeaz ă
următoarele metode:
Object invokeMethod(Object obj, String methodName, Object args)
Object invokeMethod(Object obj, String methodName, Object[] args)
Object invokeStaticMethod(Object obj, String methodName, Object[] args)
Object invokeConstructor(Object[] ar gs)

Implementarea implicit ă a metodei invokeMethod() din GroovyObject apeleaz ă metoda cu acela și nume
din metaclas ă:

Toate metaclasele sunt reunite într -un registru de metaclase astfel încât o metaclas ă să poată fi reg ăsită după
numele clasei aso ciate. In cazul claselor Groovy este posibil accesul la metaclas ă și pornind de la un obiect al
clasei. Metaclasa unui obiect Groovy poate fi diferit ă (cu metode în plus) de metaclasa clasei de care apar ține
acel obiect (se pot injecta metode la nivel de o biect).
Pentru a obtine metaclasa asociat ă unei clase Groovy se foloseste metoda getMetaClass() sau proprietatea
metaClass . Metaclasa unei clase A are metametode ce corespund metodelor clasei A, cu acelasi nume si
argumente. Exemplu de obtinere a numelui unei metametode din metaclasa clasei String :
println String.getMetaClass().getMetaMethod ('trim')
println String.metaClass.getMetaMethod ('trim')

Rezultatul metodei getMetaClass() sau al propriet ății metaClass aplicate unui obiect este un obiect de tipu l
HandleMetaClass , un tip clas ă care implementeaz ă interfa ța MetaClass .
ExpandoMetaClass este o alt ă implementare a interfe ței MetaClass si este diferit ă de HandleMetaClass .
Un obiect ExpandoMetaClass asociat unei clase se poate obtine și prin instan țiere:
class Person ,…- public Object invokeMethod(String s, Object obj) {
return getMetaClas s().invokeMethod(this, s, obj);
}
public Object getProperty(String s) { return getMetaClass().getProperty(this, s); }

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 180 – def emc= new ExpandoMetaClass(Person)

Metaclasa are acelea și metode ca și clasa asociat ă (String în exemplul anterior), inclusiv metodele
invokeMethod() și missingMethod() .
Exemplu de apelare a unei metametode (într -un script Groovy):
mytrim= String.getMetaClass().getMetaMethod ('trim')
mystr= mytrim.invoke(' abc ')
println mystr.size() // scrie: 3

De observat c ă numele (meta)metodei poate fi ob ținut în cursul execu ției (într -o variabil ă de tip String ) și că
în locul numelui clas ei String putem folosi o variabil ă de tip String pentru obtinerea metaclasei.
Metoda respondsTo() din orice metaclas ă ne permite sa afl ăm dac ă o clas ă conține sau nu o metoda cu nume
cunoscut. Metoda respondsTo() are ca rezultat o list ă de metametode cu nu mele indicat din metaclas ă; lista
poate fi vid ă dacă nu exist ă nici o metod ă cu acel nume și cu acel tip ( și/sau num ăr) de argumente. Exemplu:
str='abc'
println str.metaClass.respondsTo(str,'trim')?'yes':'no' // yes

IE.11.11.5 Extinderea dinamică a clase lor Groovy
Adăugarea unei noi metode la o clas ă se face prin ad ăugarea unei metametode la metaclasa asociat ă folosind
sintaxa urm ătoare:
metaclasa.metoda = { bloc } // metoda definita printr -un functor
Exemplu:

Exemplu de ad ăugare l a clasa String a metodei "word(int)" pentru extragerea unui cuvânt cu indice dat
dintr -un șir (șir cu mai multe cu vinte separate prin câte un spa țiu):
String.metaClass.word={ delegate.split(" ")[it] }
str="unu doi trei patru "
for (k in 0..3) println str .word(k) // unu,doi,trei,patru

Pentru a folosi noua metod ă fără paranteze (facilitate util ă la definirea de limbaje specializate), se va defini
aceast ă metod ă ca o metod ă de acces la o proprietate (iar numele metodei devine nume de proprietate).
Exemplu :
String.metaClass.getTrimAll = { … } // aceeasi definitie ca mai sus
String.metaClass.getSize = { delegate.size() }
assert s1.trimAll.size == s2.trimAll.size

De asemenea se pot ad ăuga metode statice unor clase Groovy. Exemplu:

Integer.me taClass.static.isEven={val ->val%2==0} String.metaClass.trimAll = { // elimina toate blancurile dintr -un sir
result=new StringBuffer()
delegate.each { if (it !=' ')resu lt.append(it) }
result
}
println 'a b c d'.trimAll() // abcd

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 181 – println "Is2even?" + Integer.isEven(2)
Pentru ad ăugarea unui nou constructor unei clase sintaxa este pu țin diferit ă:

Integer.metaClass.constructor << { Date d -> d.getDate() }
println new Integer(new Date()) // nu marul zilei in luna curenta

Metodele injectate prin ExpandoMetaClass nu sunt apelabile din programe Java (compilate cu un
compilator Java).
Adăugarea de noi propriet ăți unei clase se poate face fie prin redefinirea metodelor getProperty() si
setProperty() , fie prin metaclas ă.
Proprietatea propertie s pentru o clas ă produce o list ă cu toate metodele si câmpurile clasei, iar pentru un
obiect al clasei produce un dic ționar cu numele și valorile câmpurilor, inclusiv câmpurile ad ăugate de
compilator class și metaClass . Exemplu de afisare a numelor de câmpuri dintr -un obiect, dup ă excluderea
celor ad ăugate de compilator:
println new Book().properties.keySet().findAll { !(it =~ /lass/)} // keySet=nume de campuri

Exemplu de ad ăugare a unei metode toString() unei clase, care creeaz ă un șir cu numele și valoarea fiec ărui
câmp dintr -un obiect:

IE.11.11.6 Interceptarea de metode în Groovy

In Groovy intercep ția și inser ția de cod se pot face fie direct asupra unui obiect, fie prin intermediul
metaclasei sale.
Dacă o clas ă Groovy implementeaz ă interfa ța GroovyInterceptable atunci toate apelurile c ătre obiecte din
aceasta clas ă apeleaz ă invokeMethod() , iar dac ă este o clas ă Groovy "normal ă" atunci se va apela
invokeMethod() numai în caz c ă se apeleaz ă metode inexistente în clasa de care apar țin obiectele (în realitate
logica de apelare a metodelor Groovy este mai complicat ă).
Metoda invokeMethod() este cea care redirecteaz ă un apel c ătre o alt ă metod ă (eventual sintetizat ă ad-hoc în
functie de propriet ățile clasei) și are ca argumente numele și parametrii metodei c ătre care se face
redirectarea . Metoda invokeMethod() permite programarea orientat ă pe aspecte prin invocarea unei metode
ce trebuie executat ă înainte (sau dup ă) mai multe metode din una sau din mai multe clase.
Exemplu de clasa Calc , cu dou ă metode ( mul,div ) pentru care se dore ște trasarea apelurilor de metode prin
afișare pe ecran a numelui fiec ărei metode apelate împreun ă cu valorile argumentelor acestora :

Book.metaClass."toString" = {
def s=""
def props= delegate.properties.findAll { !(it =~ /lass/)}
props.each { s+= it.key +':'+ it.value + ' '}
return s
}
// utilizare
def b= new Book(title:'Java',author:'Gosling',year:1995)
println b.toString() // println b afiseaza adresa obiectului !

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 182 –

Interceptar ea de metode prin intermediul metaclasei este aplicabil ă si claselor Java.

IE.11.11.7 Utilizări practice ale metaprogramării

Delegarea de operatii c ătre obiecte din alte clase (în vederea reutiliz ării lor) este destul de incomod ă în Java,
deoarece tre buie scrise apelurile de metode pentru obiectele delegat. Ex:

O utilizare posibil ă a metaprogram ării este delegarea execut ării unor metode c ătre metode din alte clase, f ără
a scrie aceste apeluri explicit în clasa care face delegarea. Versiun ea Groovy pentru aceast ă clasă (și pentru
altele cu mai multe apeluri la delegat):

class StaffMemberUsingDelegation {
private delegate = new Perso n()
def salary
def getName() { delegate.name }
def setName(name) { delegate.name = name }
def getAge() { delegate.age }
def setAge(age) { delegate.age = age }
def getNationality() { delegate.nationality }
def setNatio nality(nationality) { delegate.nationality = nationality }
} class Calc implements GroovyInterceptable {
def mul (int a,b) {return a*b}
def div (int a,b) {return a/b}
def logon ( ) { System.out.print "logon " }
def invokeMethod (String name,args){ // name= nume metoda apelata
if (name != 'logon')
Calc.metaClass.getMetaMethod('logon').invoke(this,null) // insertie apel “logon”
System.out.println name+' '+args
def method=Calc.metaClass.getMetaMethod(name,args) // nume metoda apelata
if (method!= null) // daca exista metoda 'name'
method.invoke(this,args) // atunci se apele aza
else return Calc.metaClass.invokeMethod(this,name,args)
}
}
calc=new Calc() // verificare
println calc.mul(2,3)
println calc.div(3,2)

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 183 –

Metaprogramarea este folosit ă în Grails pentru generarea unor metode de c ăutare ( dynamic finders ) cu nume
specific clasei domeniu (se caut ă într-un tab el al bazei de date cu acela și nume ca și clasa domeniu). Fie o
clasă ―Person‖ cu câmpurile ―firstName‖,‖lastName‖, ―age‖ (care corespund unor coloane din tabelul SQL);
fără să fi fost declarate anterior se pot apela metode cu nume ca findByFirstName(),
findByFirstNameAndAge() ș.a. Corpul metodei se genereaz ă în functie de numele metodei: find() implic ă o
căutare, update() va efectua o salvare în baza de date.
In aplica țiile Web dar și în alte aplica ții este nevoie ca s ă ata șăm mai multor func ții (metode) anumite
opera ții executate fie înainte, fie dup ă instruc țiunile din functia respectiv ă. Aceste opera ții se numesc și
method advice sau cross -cutting concern în AOP, pentru c ă ele se execut ă oarecum perpendicu lar pe fluxul
de apelare a func țiilor din progr am (pe logica programului).
Utiliz ările tipice sunt: jurnalizarea ( logging ) într -un fisier a apelurilor de metode, efectuarea de valid ări
asupra datelor primite de metode, autentificarea dreptului de acces la metode ( log-in) sau ap licarea altor
"filtre" l a execu ția unor metode. Aceast ă ―augmentare‖ a codului se poate face manual (cu riscul
introducerii unor erori) sau automat, prin interceptarea apelurilor si insertia de cod în punctele dorite. class StaffMemberUsingMOP {
private delegate = new Person()
private hasLocalProperty(name) {
metaClass.properties.collect{ it.name }.contains(name)
}
def salary
StaffMemberUsingMOP(Map map) {
map.each{ k, v -> setProperty(k, v) }
}
void setProperty(String name, value) {
if (hasLocalProperty(name)) this.@"$name" = value
else delegate.setProperty(name, value)
}
def getProperty(S tring name) {
if (hasLocalProperty(name)) return this.@"$name"
else return delegate.getProperty(name)
}
}

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

– 184 –
Bibliografie

[B01] Bloch J. Effective Java, Editura Addison -Wesley , 200 8
[C01] Cooper J: Design Patterns -Java Companion , Editura Addison -Wesley , 1998
[E01] Eckel B. Thinking in Java , Editura Pren tice Hall , 2003
[M01 ] Moraru F., Odubă șteanu C. Programare orientată pe obiecte, Editura Bren , Buc., 2005
[N01] NetBeans General Java Development Learning Trail https://netbeans.org/kb/trails/jav a-se.html
[O01 ] Oracle, The Java Tutorials , http:// docs.oracle.com/ javase/tutorial /
[R01] Reed P. Developing Applications with Java and UML , Editura Pearson Education , 2002
[T01] Tănasă S ., ș.a., Java de la zero la expert , Editura Polirom , 2005

Similar Posts