ie. tehnici avansate de programare - deliu.ros).pdf · programare vizuală în netbeans ..... 153 ....

184
INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 1 - IE. TEHNICI AVANSATE DE PROGRAMARE

Upload: lyque

Post on 01-Feb-2018

289 views

Category:

Documents


12 download

TRANSCRIPT

Page 1: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 1 -

IE.

TEHNICI AVANSATE DE

PROGRAMARE

Page 2: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 2 -

CUPRINS

Cuvânt înainte ........................................................................................................................................... 6

Capitolul IE.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 programă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

Capitolul 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 Java ........................................................ 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 compararea 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

Page 3: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 abstracte ș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 Observator............ ............................................................................... 110

IE.08.6. Şablonul Adaptor........................... ..................................................................... 111

IE.08.7. Şablonul Compozit............................................................................................... 112

IE.08.8. Şablonul Decorator.............................................................................................. 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 Java în NetBeans .............................................................. 150

IE.10.6. Programare vizuală în NetBeans ......................................................................... 153

Page 4: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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. Limbajul Groovy ........ ....................................................................................... 159

IE.11.3. Simplificarea codului sursă în Groovy................................................................ 160

IE.11.4. Operatori şi instrucţiuni Groovy ......................................................................... 161

IE.11.5. Clase Groovy ...................... ............................................................................... 162

IE.11.6. Diferenţe Groovy- Java ................ ..................................................................... 163

IE.11.7. Metode noi în clase Java .................................................................................... 164

IE.11.8. Utilizarea de expresii regulate ............................................................................ 165

IE.11.9. Functori Groovy …………….............................................................................. 166

IE.11.10. Colecţii Groovy …………..……....................................................................... 171

IE.11.11. Metaprogramare în Groovy ……....................................................................... 175

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

IE.12.1. Noţiuni de bază ale programării orientate obiect .............................................. 184

IE.12.2. Clase și obiecte în Java ....................................................................................... 185

IE.12.3. Derivare, moștenire, polimorfism în Java .......................................................... 187

IE.12.4. Clase abstracte și interfeţe ................................................................................... 188

IE.12.5. Tehnici de programare orientată obiect în Java ................................................... 189

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

Page 5: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 5 -

Page 6: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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ă

(Prolog), 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 iar toate programele necesare dezvoltării de aplicaţii în Java

sunt gratuite. În plus există câteva medii integrate de programare gratuite care facilitează 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 Programă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 obiectelor).

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 (Unified 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 ingineriei 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++, HTML, 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.

Page 7: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 7 -

Capitolul IE.01. Noţiuni de bază ale programării orientate obiect Cuvinte cheie

POO(Programare orientată pe obiecte),Programare procedurală,

Obiecte, Clase, Metode, Instanţiere, Mesaje, Metode statice

Incapsulare,Programare generică,Clase abstracte, Interfeţe

IE.01.1 Paradigme de programare

Programarea calculatoarelor 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 (operaţ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 deoarece

î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 (―stil‖ 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 tipice 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++ (procedural ş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 destinate 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 imperativă, 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 (programele) 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

Page 8: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 simplifică 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 (Python, 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 sunt 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 două

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 virtuale, 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 obiecte 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, persoane, 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 obiecte. 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 obiecte

Page 9: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 aceleaş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, extragerea 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 exemplu) 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 rezultatelor, 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 metode 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).

Page 10: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 10 -

La nivel sintactic, obiectele 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 nou 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 ca 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 folosesc 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” }

Page 11: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 copiere 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 clase 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 elementelor

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ă programarea 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ă definirea 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 FileCopier { // 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” } }

Page 12: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 operaţ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 programul de exersare a operaţiilor cu stiva arată astfel:

Modificarea 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 poate conţine

mai multe cuvinte, separate prin anumite caractere delimitator. 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 limbajul 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++) // pune 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 main () { 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 }

Page 13: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 13 -

adresa curentă în şirul analizat. In 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, astfel 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

construirea 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 funcţ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 utilizare 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(str, 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 }

Page 14: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 14 -

- O clasă poate încapsula algoritmi 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 pentru 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 acelaşi

mod de utilizare.

Funcţionalitatea unei clase poate fi reutilizată în alte clase fie prin derivare, fie prin delegare (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 automată 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 clase 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 (prin 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ă prin 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.

Page 15: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 definite 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 variabilele ―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 indice la orice 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 folosite (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 derivă, 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 adresele 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:

Page 16: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 conversie ( 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 colecţ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): orice 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 model 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 limbajelor 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 problemei de rezolvat şi implică

o abordare diferită de cea anterioară.

Intr-o aplicaţie bancară vor exista clase şi obiecte de genul ―Cont‖ si ―Client‖. Un obiect ―Client‖ va conţine

date de identificare ale clientului şi metode pentru obţinerea sau modificarea 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).intValue();

Page 17: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 ―Client‖ 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.

Reutilizarea î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 interfaţă 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ă erorilor) 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 definite 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 kind 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ă.

Page 18: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 18 -

Delegarea înseamnă că în clasa ―mulţime vector‖ există o variabilă 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ă reprezintă 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

compatibil 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ă defineş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ţie ş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 diferite 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 această 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

Page 19: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 set = new TreeSet(); String str = set.toString(); In exemplul anterior se va folosi metoda 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 si 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 valori 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 tipuri 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 orientate 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 stabileş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 participanţ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 abstracte 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 diverse 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ă.

Page 20: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 20 -

Ingineria software (Software Engineering) înseamnă aplicarea sistematică şi disciplinată a unor metode

(metodologii) pentru proiectarea, dezvoltarea (implementarea), 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 fazele

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 domeniul aplicaţiei: client, cont, factură, tranzacţie,ş.a.

- Obiecte colecţie: vectori, 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, casete cu text, imagini, meniuri, 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 proiectare): obiecte iterator sau

enumerator al elementelor dintr-o colecţie, obiecte ascultător (observator), etc.

Page 21: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 cu limbajul C, mai puţin instrucţiunea goto. Tipurile de date

primitive sunt aceleaş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 automate 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 primitive

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 Java 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 tip 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 programator.

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

Page 22: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 22 -

Ierarhizarea tipurilor aritmetice, de la ―inferior‖ la ―superior‖ este:

byte, short, int, 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 argumentele 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 există 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 într-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 ―println‖, 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 ! }

Page 23: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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, dar î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 declaraţ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.14159265358979323846; // 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 alocarea 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= “null”; 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) break; return k==n ? false : true; // eroare: k nedeclarat ! }

Page 24: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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

Page 25: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 folosite 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, echivalente structurilor 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) Java 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.parseInt (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);

Page 26: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 variabila 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 constantele 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 exemplu 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 operator

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

Page 27: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 27 -

Atunci când se apelează 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 de 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.println (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 tip 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 operanzilor: 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 numele 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 programe 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 clase 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 pachetului 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 început 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 :

Page 28: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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, pentru 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 unei 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 static 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 ? }

Page 29: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 tipul 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 sa. 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:

Numerotarea 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 vector de vectori, iar variabila length se poate folosi pentru fiecare

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ă matricea 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 =dimensiunea vectorului 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

Page 30: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 funcţ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 sau 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 obiecte. 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 divizori } // utilizare funcţie int div[ ]= new int [m]; // aloca memorie ptr vector divizori int nd = divizori (m, div); // completare vector divizori

Page 31: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 obiect 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 clasei 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 clasei 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 + ")“;- }

Page 32: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 32 -

public void setRe (re,2); // re=2 public int getRe (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 transmite î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 trebuie 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 obiectelor.

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 faţă 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 metode (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 creeaza un nou obiect, cu alta adresa }

Page 33: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 programă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 alte 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 adrese 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 rezultat 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; duplicarea 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 clasei), 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 (variabilelor) 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.println (c1.toString( ) ); // afisare date obiect c1 }

Page 34: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 34 -

In lipsa unor constructori declaraţi explicit de programator, orice clasă Java primeşte automat un constructor

public fără argumente şi care nu are nici 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 variabilă 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 constructor 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 un obiect din clasa 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 constructor public MyVector () { this (10); } // capacitate implicita 10 …. }

Page 35: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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, setCharAt, 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 primitiv ş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) Math.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 apelează 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 concatenări de şiruri este preferabil ca timp să se folosească direct metoda

append() din clasa StringBuilder. Exemplu:

public static String arrayToString ( 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()); }

Page 36: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 se 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 Applications) 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âte 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 trebuie aruncate sau tratate.

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

Page 37: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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. Exemplu 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 poate 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 text formate din linii. Exemplu de citire

linii dintr-un fişier text pe disc:

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

try { PrintStream ps = new PrintStream ( new FileOutputStream ("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.printStackTrace();}

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

Page 38: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 38 -

Pentru citire de numere de la consolă trebuie extrase 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 programul 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 constructorii 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 direct

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

Page 39: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 39 -

Capitolul IE.03. Derivare, moştenire, polimorfism în Java

Cuvinte cheie

Derivare, Extindere, Moştenire, Superclasă, Subclasă,

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 protected 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 cuvântul cheie

extends urmat de numele 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(). Pentru 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 main (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()); }

Page 40: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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ă setElementAt ( 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

superclasă, 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 definite 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.

Variabile sau argumente de tip Object (referinţe la tipul Object) pot fi înlocuite cu variabile 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 obligatorie redefinirea metodelor mostenite, dar în practică se redefinesc câteva metode:

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(); String 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-valoare, necesară în clasele dicţionar:

Page 41: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 tipul

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 redefinită ş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 trebuie 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 început 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 compilatorului 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 un 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;} }

Page 42: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 ArraySet pot fi şi ele reutilizate prin extindere şi definirea unei clase

SortedArraySet 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 mulţ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ăugarea 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.nextElement()+" "; return s; }

Page 43: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 43 -

Absenţa unor clase pentru mulţimi vector sau mulţimi liste sau dicţionare din 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ă argumente 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 constructori î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 Comparator 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); } . . . }

Page 44: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 funcţ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 variabilă (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 funcţ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 { ... }

Page 45: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 exemplu 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 singura 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 toate exceptiile de I/E }

Page 46: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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

Metodele 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 metoda 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 key } Asocierea unui apel de metodă cu funcţia ce trebuie 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 majoritatea 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 metodă 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) { System.out.println ("Fisier negasit"); } catch (EOFException e) { } catch (IOException e) { System.out.println("Eroare la citire fisier"); } }

Page 47: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 tipul obiectului adresat de variabila 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(); Apelul 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 superclasa 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 exemplu.

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 obiectului 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: toate

componentele de interfaţă grafică AWT sunt derivate din clasa Component şi au o metodă paint() pentru

Page 48: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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

Page 49: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 49 -

Sursa clasei anterioare poate fi mai mare dacă vrem pentru 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 cod 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 realizare 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 toString () { return v.toString(); } public int size() { return v.size();} }

Page 50: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 50 -

Exemplu de clasă stivă obţinută prin delegare din clasa LinkedList (pentru stive 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 tipului 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

Derivarea ş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 pop () { 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 StackList 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 stack.size(); } // abstracta in AbstractList public Object get (int i) { return stack.get(i); } // abstracta in AbstractList }

Page 51: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 ascultă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 preia 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 delegarea 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 Reader, Writer şi celelalte sunt abstracte. Toate

class A { public void f1 () { System.out.println ("A.f1"); } } class B { public void f2 () { System.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 mostenita 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() }

Page 52: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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, FileWriter 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 fire 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 (BufferedInputStream, BufferedReader,

BufferedOutputStream, BufferedWriter), citire-scriere la nivel de linie şi pentru numere de diferite tipuri,

fără conversie (DataInputStream, DataOutputStream), citire cu punere înapoi în flux a ultimului octet

citit (PushBackInputStream), 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 numite ş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, deoarece 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 selectarea 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 }

Page 53: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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,

PushbackInputStream, 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 unui 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 citire 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 flux 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 FileInputStream (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); }

Page 54: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 sale. 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 obligatoriu 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 implementate 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 obtine clase instanţiabile dintr-o clasă abstractă trebuie implementate toate metodele abstracte

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 longValue(); public abstract float floatValue(); public 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 parseInt(String s) throws NumberFormatException { return parseInt(s,10); } . . . }

Page 55: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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, FilterReader, BufferedReader,

StringReader, , etc. Fragment din clasa Reader:

public interface Collection { // declara metode prezente in orice colectie int size(); // dimensiune colectie boolean isEmpty(); // verifica daca colectia este goala … // alte metode } public abstract class AbstractCollection implements Collection { public abstract int size(); // metoda abstracta 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 keyPressed(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.getKeyChar(); // caracter generat de tasta apasata . . . // folosire sau afisare caracter ch } }

public abstract class Reader { protected Reader(); // constrcuctor 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 }

Page 56: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 sintactic, 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 multiplă. 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 variabilă

(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 value[]; private final int count; // constructori . . . // metode public int length() { return count; } public String toString() { return this; } . . . }

Page 57: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 implementează 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 de 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 mare), 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 Serializable, 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 asemă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"). Practic, 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()) return 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 pozitia 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 . . . }

Page 58: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 58 -

O interfaţă care stabileste un tip comun poate fi atât de generală încât să nu conţină nici o metodă. Un

exemplu este interfaţa EventListener (pachetul "java.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 tipul

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 clasa 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ă libertate subclaselor.

Avantajul soluţiei interfaţă în raport cu o clasă abstractă este evident atunci când definim un dicţionar

realizat ca un vector 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 abstractă 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 AbstractMap { // 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 SimpleEntry 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.add (new SimpleEntry(key,value)); // si adaugare return v; } entries.add (new SimpleEntry(key,value)); // daca nu exista cheia in dictionar return null; }

Page 59: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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

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, BigDecimal ş.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.value- ((Byte) obj).value ; } }

public Set entrySet () { // abstracta in AbstractMap LinkedHashSet set = new LinkedHashSet(); for (int i=0;i<keys.size();i++) set.add(new SimpleEntry(keys.get(i), values.get(i)) ); return set; } }

Page 60: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 60 -

Mai multe metode statice din clasele Arrays si Collections (sort(),max(),binarySearch()), care au ca

argument doar vectorul sau colecţia, folosesc implicit metoda compareTo().

Interfaţa Comparator a fost introdusă pentru comparare de obiecte după alte criterii decât cel ―natural‖; ea

conţine în principal metoda 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 obiecte 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ă

lungimea ş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[ ], Comparator 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());

Page 61: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 61 -

Tehnica callback este folosită la scrierea unor clase de bibliotecă, 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:

Enumerarea 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 obiecte folosind numai

enumeratorul 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.nextElement().equals(obj) ) m++; return m; }

Page 62: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 observat 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 clasele 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 element 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); // santinela } 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(); } }

Page 63: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 valoare i din a este echivalentă cu instrucţ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 ca

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

Page 64: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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

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

Page 65: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 tip filtru pentru orice fel de obiecte, utilizată pentru filtrarea unui

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 rezultatele

filtrării.

class DirFilter implements FilenameFilter { String ftype; public DirFilter (String ft) { ftype=ft;} public boolean accept (File dir, String name) { // name= 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 = new Vector(); Enumeration e = v.elements(); while (e.hasMoreElements()) { Object elem = e.nextElement(); if (f.accept(elem)) r.add( elem); } return r; }

Page 66: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 program 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 cursul normal.

Existenta excepţiilor este un mare avantaj al limbajului Java, pentru că permite semnalarea la execuţie a unor

erori uzuale, prin mesaje clare asupra cauzei şi locului unde s-a produs eroarea, evitând efectele

imprevizibile ale acestor erori (ca în cazul limbajului C, de exemplu).

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 unor situaţii speciale care apar la utilizarea 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 produce 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 forma "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 intrare-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ă trateze excepţiile posibile. Exemplu:

class Exc { public static void main (String arg[ ]) { System.out.println ( Integer.parseInt(arg[0])); // afiseaza primul argument } }

Page 67: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 secvenţ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)

Exemplul 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 avertizare la producerea excepţiei; este preferabilă

aruncarea unei exceptii cu throws pentru că apare mesaj despre 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();} }

Page 68: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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; exemplul 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 arg[]) { 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(); } }

Page 69: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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). Obiectele 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 = Stiva.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 implementate, 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 se poate afişa tipul real al unui obiect primit ca argument de tipul generic

Object:

public static void main (String[] arg) { RandomAccessFile 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"); } }

Page 70: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 cursul 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 proprietăţ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 determinate dinamic, în cursul execuţiei.

Dacă se apelează metode polimorfice 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) { Object 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 argumente 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);

Page 71: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 unei 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 Outer }

public class Collections { public static Comparator reverseOrder() { // din clasa Collections return REVERSE_ORDER; } private static final Comparator REVERSE_ORDER = new ReverseComparator(); private static class ReverseComparator 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 Collections }

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

Page 72: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 acces 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 apare 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 Entry 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;} public 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 protected Object elementData[]; // vector de obiecte public Vector (){ elementData= new Object[10]; } // metoda care produce obiect iterator public Enumeration elements() { return new VectorEnumeration(); } // definirea clasei iterator pe vector (inclusa) class VectorEnumeration implements Enumeration { int count = 0; // indice element curent din enumerare public boolean hasMoreElements() { return count < elementCount; } public Object nextElement() { if (count < elementCount) return elementData[count++]; else return null; } } ... // alte metode din clasa Vector }

Page 73: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 73 -

O situaţie tipică pentru folosirea unei clase anonime 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 obiecte în ordine descrescătoare.

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 definită î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 int 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 curent 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()); } }

Page 74: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 cuplaj 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 : clasa 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 clasei 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 rapida 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 poate 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 creează 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 adresate 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ă procese (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 instrucţiuni din P1 şi din P2.

Set eset = m.entrySet(); // multime de perechi cheie-valoare ArrayList entries = new ArrayList(eset); // vector de perechi cheie-valoare Collections.sort (entries, new VComp()); // ordonare vector System.out.println (entries); // afisare perechi ordonate dupa valori }

Page 75: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 execută

complet 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 independent 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 proces 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 ―producă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 din 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 subclase ale clasei Thread (cu metoda run()

redefinită), fie pe baza unei clase care implementează interfaţa Runnable (interfaţă care conţine numai

metoda run()).

Page 76: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 cheie 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 metode, 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 proceselor 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 constructor 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 unui 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 firului 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 curent):

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

Page 77: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 compilare 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ă anumite 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. Exemplu:

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 asteptare 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 întrerupere.

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 clasa 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 (InterruptedException 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 (String [] 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.in.read() > 0 ); } // asteapta tastarea unei clape catch (IOException e) {} }

Page 78: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 78 -

Un fir este terminat 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 metoda

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 cadrul 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ă definirea 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 poate 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.currentThread() + ” “+ 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 Proces ()).start(); } }

public static void main (String [] arg) { 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 punct la fiecare 100 ms try { Thread.sleep(100);} catch (Exception e) { } } } }

Page 79: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 cazuri de blocare definitivă a unor procese,

sau de actualizare eronată a unor date comune sau de transmitere incorectă 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()); } } }

Page 80: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 80 -

Fiecare din cele două fire face câte 5 incrementări ale contorului 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 sistem.

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 sale ―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) concurente 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 cuvântul

cheie synchronized.

Prima soluţie Java pentru sincronizarea 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.isAlive() ) // 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) {} }

Page 81: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 81 -

După ce firul f1 a intrat în execuţia metodei sincronizate ―incr‖ pentru 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 poate 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 un 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 fi 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 operaţ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.synchronizedList (new ArrayList());

Page 82: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 claselor 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 interfeţ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 conforme cu anumite interfeţe ce

reprezintă tipuri 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 prin 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 excepţie la încercarea de apelare a unei metode

neimplementate.

Urmează descrierea completă a interfeţei Collection :

Page 83: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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

- 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 unei 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 (listă, 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 colectie (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 colectie 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 }

Page 84: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 colecţ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 rezultat 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 veche 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 public 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 nu se introduce 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 echilibrat 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)

Page 85: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 cuvintelor 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 from) // 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 main (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 unice = 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.addAll(s2); // reuniune s1+s2 Set aux = new HashSet(s1); aux.retainAll (s2); // intersectie s1*s2 in aux simdif.removeAll(aux); // reuniune minus intersectie

Page 86: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 elementele 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 vechiul 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 algoritmi 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 de 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, LinkedList, 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.setElementAt (aux , j) }

Page 87: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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(), removeLast(), 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 priorităţ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 determinată 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 metode 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 asociată 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 values(); // colectia valorilor din dictionar Set entrySet(); // multimea perechilor cheie-valoare }

Page 88: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 88 -

noua valoare v (valoarea înlocuită este transmisă ca rezultat al metodei). Testul de apartenenţă a unei chei

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 scrierea 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 foloseş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 text 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; StringTokenizer 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); // afisare dictionar } }

Page 89: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 valorilor 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 definiţ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 elementele î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 interfeţ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 multe criterii se va folosi

metoda compare() dintr-o clasă compatibilă 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 constructorii 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] }

Page 90: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 colecţ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 colecţ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ătoarele metode :

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 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 compare (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.]

Page 91: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 modificare. 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. Exemplu:

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 implements 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() { Object[] result = new Object[size()]; Iterator e = iterator(); for (int i=0; e.hasNext(); i++) result[i] = e.next(); return result; } . . . }

Page 92: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 92 -

Interfaţa ListIterator conţine metode pentru traversarea 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 cheie-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 tip din clasa şablon, dar în Java

există o singură clasă generică (sursă şi compilată) în care se înlocuiesc parametrii formali cu parametrii

efectivi, ca şi la apelul de funcţii. Intr-o colecţie generică tipul 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, valoare=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 tipul

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

Page 93: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 averismente 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 DefaultMutableTreeNode.

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.readLine()) != 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 LinkedList<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() , … - }

Page 94: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 '?' are sensul de "tip necunoscut".

Declaraţia Collection<Object> nu este echivalentă cu declaraţia Collection<?>.

Tipurile necunoscute pot fi limitate inferior sau superior (Bounded wildcards) 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.length ;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 (Object 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 HashSet<String> (); printCol (m);

Page 95: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 colectie (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> cs = new LinkedList<String>(); arrayToCol (sa, cs); // T presupus a fi String

Page 96: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 statice, 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 este 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 standardul 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 : implementare, 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 diagrame 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.

Page 97: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 97 -

Variabilele clasei, numite "atribute" în diagrama UML, 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; } }

Page 98: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 98 -

IE.07.3 Relaţia de generalizare î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, derivată 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; } }

Page 99: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 exprima î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 două 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 caracterul asterisc ‗*‘.

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

Diagrama de clase UML pentru

codul sursă anterior:

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

Page 100: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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

Page 101: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 la instanţierea clasei StackList.

Urmează un exemplu 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); }

}

Page 102: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 102 -

Clasa iterator:

class ArraySet extends AbstractSet { Object a[]; int n,nmax; public ArraySet (int 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; } public 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(); }

}

Page 103: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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:

Page 104: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 104 -

Diagrama la nivel

conceptual:

Diagrama clasei Person:

Diagrama clasei AddressBook:

Page 105: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 105 -

Diagramele

operaţiilor

addPerson()

saveAddressBook()

Page 106: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 dependenţ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. Stabilirea 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 un 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, fragilitate ş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 Responsability 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ă trebuie 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 ).

- Principiul substituţiei de tipuri (LSP=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 soluţiilor de proiectare confirmate de practică sunt:

- Compoziţia (delegarea) este preferabilă de multe ori extinderii (derivării).

- Proiectarea cu interfeţe şi clase abstracte este preferabilă faţă de proiectarea cu clase concrete, pentru că

permite separarea utilizării de implementare.

- Este recomandată crearea de clase şi obiecte suplimentare, cu rol de intermediari, pentru decuplarea unor

clase cu roluri diferite

Page 107: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 interacţ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) ―slab‖ 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, efortul 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 generează obiectele necesare.

- Scheme structurale (Structural patterns), care grupează mai multe obiecte în structuri mai mari.

- Scheme de interacţ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 trebuie 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.createEtchedBorder(); b2.setBorder (eb);

Page 108: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 numai 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 obiecte 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) sharedBorder = new EtchedBorder() return sharedBorder; } ... }

Page 109: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 109 -

IE.08.4 Şablonul Iterator

Un iterator este un mecanism de enumerare a componentelor 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 Java 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 elementele 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) cursorul 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. "; StringTokenizer 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()+",");

Page 110: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 110 -

IE.08.5 Şablonul Observator

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 interfaţ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 observat 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 defini 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 ( crt < 0) crt= str.length(); // ultimul cuvânt din sir return str.substring (start,crt); } }

Page 111: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 introduş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 (subiectul 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 Adaptor

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

Page 112: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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‖. Clasa adaptor InputStreamReader extinde 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 InputStreamReader 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 simplifică 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 compusă 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. Fiecare 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 () { } }

Page 113: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 "Expression" 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

decorate 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; public 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(int 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; } return 0; } }

Page 114: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 ―deleagă‖ 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 datelor 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 obiecte

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

Page 115: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 115 -

Utilizarea fabricilor de obiecte poate fi privită 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 configurare).

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 corespunde 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 informaţ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 ulterior 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 Calendar 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.

Page 116: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 servicii: 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. Există 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

AbstractProduct 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. Numele 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ă

newInstance(), 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

Page 117: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 metoda 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 ( ascultat), 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ă (pentru 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 modificare

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 prezentare, 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 realizarea 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 partea 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 datelor 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.newInstance(); // obiect fabrică DocumentBuilder builder = factory.newDocumentBuilder(); // obiect parser Document doc = builder.parse( new File(arg[0]) ); // creare arbore DOM dintr-un fisier

Page 118: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 butoane, 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 int 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); } }

Page 119: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 Injectarea Dependenţelor

Un principiu general de proiectare 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 aplicaţ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 lucra mai multe 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 serviciilor (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 referinț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 Object). 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 suportă 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 clientul pentru serviciile Dao pot ară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 }

Page 120: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 fisiere 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 obiect 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 serviciul 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 { // orice 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 getInstance() { return dao; } … } // utilizare public class DaoClient { public List findBy (Filter f) { Dao dao= DaoFactory.getInstance(); List all = dao.findAll (); … } }

Page 121: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 121 -

Obiectul care injectează dependențele trebuie să dispună de informații despre obiectele pe care le injectează

(nume şi referințe); aceste informații se transmit de către persoana care dezvoltă aplicația fie prin fisiere 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 metodă a clasei (Setter Injection)

- Printr-o interfață de injectare (Interface Injection)

Injectarea prin constructor necesită adăugarea unui constructor 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 clasa 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 DaoClient (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"> <property name="dao"> <ref local="Dao"/> </property> </bean> <bean id="Dao" class="spring.DaoImpl"> <property name="file"> <value>date.txt</value> </property> </bean> </beans>

Page 122: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 122 -

Produsele de tip container (Framework) cu injectare dependențe pentru aplicații Java pot fi clasificate î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 orientată 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 cu 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()); }

Page 123: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 123 -

Capitolul IE.09. Interfeţe grafice şi programare orientată pe evenimente

Cuvinte 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

transmit 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 eveniment, 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 folosite 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;

- Containere 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: butoane simple, butoane radio

- Elemente de dialog

- Componente pentru selectarea unei alternative (opţiuni)

Page 124: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 bibliotecă. 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 vechile 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 pentru 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, JTree, 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, JColorChooser 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, JDialog 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 minimal 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()).

Page 125: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 125 -

In final, se stabilesc dimensiunile 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 etichetă 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 respective) .

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 terminarea 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 coordonate 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.swing.*; 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ă }

Page 126: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 (sud), 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 specifică 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 acestui

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, indiferent 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ă utilizarea 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 FlowLayout()); frm.add (lbl1); frm.add(txt1); frm.setSize (300,80); frm.show(); }

Page 127: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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, culoare, 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 principale, 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 programarea 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 static void main (String arg[]) { JLabel l1 = new JLabel ("Numele"); JLabel l2 = new JLabel ("Adresa "); 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); }

Page 128: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 (String 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.setSize (300,100); } }

Page 129: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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ă caracterul 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 componentă 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 interfaţă

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

Page 130: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 abstractizare 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 handlers), 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 tratare 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 componetei

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 clasificate 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 obiect 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), unde ‗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

Page 131: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 131 -

- Evenimente specifice fiecărui tip de componentă: ChangeEvent, MenuEvent, ListDataEvent,

ListSelectionEvent, 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 vizibilitatea 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ă

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

Page 132: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 cursorului 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 externe 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 nivel 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âmpul text (în constructor).

button.addActionListener(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 } }

Page 133: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 fiecare 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 extends 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(text) ); 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 implements 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.contains("+")>=0) ++n; // if (b.contains("Incr") n++; else --n; text.setText(" "+ n); } }

Page 134: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 134 -

O variantă cu număr minim de clase este definirea clasei 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 include clasele receptor în clasa cu fereastra aplicaţiei:

class MFrame extends JFrame implements ActionListener { JButton b1 = new JButton (" + "); JButton b2 = new JButton (" - "); JTextField 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); } } }

Page 135: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 complexe î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 aplet, î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 extends JApplet { JLabel et= new JLabel ("Eticheta", JLabel.CENTER); public void init () { add (et); } }

Page 136: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 obiectul 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 programul

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

Page 137: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 modelului (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 evenimente

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 folosesc 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 complexă.

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 poate 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 evenimente 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 execuţ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 text, 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 obiectul 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, clasele model contin şi metode de modificare a datelor (interfeţele

claselor model nu impun astfel de metode).

Page 138: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 model (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, DefaultTreeModel) ş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 forma 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 constructor, 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.getText()); // 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 } }

Page 139: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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

Page 140: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 operaţ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 proiect, 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> <target 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>

Page 141: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 141 -

</project> Gruparea mai multor operaţii în acţiuni numite target, permite reutilizarea acestora în diferite proiecte.

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 Development 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 semnalarea 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 aplicaţ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 gratuite. 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, Java

Card). In varianta SE se aduce şi SDK (compilator, biblioteci s.a.) dar nu şi documentaţia Javadoc pentru

clasele Java; 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.

Page 142: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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.

Page 143: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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

Page 144: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 clase ş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 Project 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/Folder > 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 temporară

(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 modul ―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:

Page 145: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 verde, oarametri

cu oranj, clase sau metode învechite (deprecated) subliniate cu o linie ondulată, iar membrii nefolositi tăiaţi

cu o linie. Exemplu:

Page 146: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 editare, 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:

Page 147: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 neimportate şi adaugă la cerere instrucţiunile import

necesare (cu combinaţia de taste Ctrl-Shift-I). Exemplu:

La selectarea unei clase pentru completare automată se adaugă si instrucţ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 unui constructor

Afişare documentaţie Javadoc: Clic dreapta pe numele metodei > Show Javadoc sau Alt-F1. Altă variantă:

se pune 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:

Page 148: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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

Page 149: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 149 -

Navigare prin codul sursă: fie prin 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.

Page 150: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 la 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 biblioteca 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 egale 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(Object 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:

Page 151: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 selectează

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

Page 152: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 case 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 acestei 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

Page 153: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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ă. Utilizatorul 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 manuală

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

Inaintea 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

Page 154: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 buton 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 modificarea inscripţiei de pe buton:

Clic dreapta pe buton > Edit Text > Click Me

Page 155: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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) dintre 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.

Page 156: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 156 -

IDE generează scheletul metodelor ascultător şi asocierea lor cu componentele respective, iar utilizatorul

poate să completeze 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:

Page 157: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 157 -

Codul sursă generat are secţiuni care nu pot fi modificate manual (marcate cu un fond colorat) ş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 pentru 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 pe 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 numele 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 desenul

componentei.

Page 158: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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, după 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(jTextField1.getText()); float num2 = Float.parseFloat(jTextField2.getText()); float result = num1+num2; jTextField3.setText(String.valueOf(result)); Pentru verificarea 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)

Page 159: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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, Limbaje 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.) care 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 interpretat 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 Groovy, 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). Acest 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 constructori pentru obiectele

clasei ş.a. Aceste metadate 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 informaţiile furnizate despre excepţii se datorează tot execuţiei

programelor Java sub controlul maşinii virtuale. Codul intermediar Java este ―asistat‖ (supravegheat) la

execuţie de către maşina virtuală, ceea ce a dat naştere expresiei de cod controlat (―managed 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 (de 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.

Page 160: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 sursă 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 orientată 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 definite 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 limbaje

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, NetBeans, 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 script 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. Exemplu:

println ( "Today is" + new Date() ) - Parantezele (cu sau fără argumente) folosite la apelarea metodelor pot lipsi în general (dar există şi situaţii

când ele sunt necesare). Exemplu:

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. Exemplu:

list=[4,2,8,6] list.sort { a,b -> b-a} println list

Page 161: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 considerat 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 atribuită 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 ghilimele. 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 expresia cu valorile lor (ca şiruri). Exemple:

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 noi, cum ar fi metoda times care repetă o

secvenţă de cod ( un functor, mai exact) de un număr de ori egal cu valoarea obiectului întreg. Exemplu:

3.times {println} // scrie 3 linii albe - Instrucţiunea for poate avea si o formă care foloseste cuvântul cheie in şi repetă ciclul pentru valori ale

variabilei contor dintr-un subdomeniu (range) sau dintr-o colecţie. Exemple:

Page 162: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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) println 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.

- Comparaţ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 identitate (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 (prin 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 expresiile 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)

Page 163: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 163 -

Variabilele dintr-un obiect, 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 numele 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. Exemplu:

TreeSet<String> a = new TreeSet<String>() a.add(123) - In Groovy este posibilă supraîncărcarea operatorilor, prin asocierea fiecărui operator (ce 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. Exemplu:

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

Page 164: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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. Exemplu:

println ( [3,7,2,8,4]. findAll { it < 6} )

- Existenţa unor metode suplimentare în clase, multe 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) Number 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){ Classification b.isCase(a) Object, range, list, collection, pattern, closure; case b: }

Page 165: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 adaugare 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 „size‟

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 getText() 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 (eventual zero)

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

Page 166: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 partială)

==~ pentru metoda ―match‖ (o potrivire completă cu sablon)

Pentru a evita utilizarea 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 acest 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 alt obiect 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ă pentru 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 Groovy 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

Page 167: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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, while, 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. Exemplu:

3.times { println ()} // 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 argument 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 Closure). Exemplu:

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

Page 168: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 sortarea 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 word = { String s, int k -> s.split(' ')[k] } // functor cu doua argumente Exemplu de functor care extrage 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 asupra 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 functor “word” str="unu doi trei patru " for (k in 0..3) println word(str,k) // unu,doi,trei,patru

Page 169: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 obiectelor 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 repetate î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), each (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”

Page 170: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 obiectele care satisfac anumite condiţii. Pentru fiecare

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.hasNext();) { 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 obiectelor care

satisfac anumite condiţii, aplicarea unor operaţii (transformări) fiecărui element din colecţie, pe loc sau cu

crearea unei noi 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 enumerare 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 apelului 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"))

Page 171: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 superioară 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 suportă 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 unor 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 : insuranceRate = 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 } }

Page 172: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 elemente 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 vectorilor 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’ println 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 acest

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 elemente 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,switch ş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)}

Page 173: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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. Exemplu:

println ((2..20).asList().collect (isPrime)) // nu pot lipsi parantezele ! Orice listă se poate folosi ca stivă prin metodele pop() şi push() (echivalent cu ―<<‖). Exemplu:

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 folosită 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)) }

Page 174: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 dictionar 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 { word -> statistic += word.padLeft(12) + ': ' statistic += wordFrequency[word] + "\n" } println statistic

Page 175: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 drept 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 lista: 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) adauga ‗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 definirea de noi tipuri în cursul execuţiei, are un

mecanism mai flexibil de apelare/interceptare a metodelor şi permite evaluarea de cod la execuţie.

Page 176: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 compilare (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 compilare) de metodele statice.

Facilităţile de metaprogramare din Groovy pot fi privite 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 execuţ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 (Object 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);

Page 177: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 177 -

IE.11.11.2 Facilităti de metaprogramare î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 protocol 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

modifică 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, dar 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 metode 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 trebuie 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:

- Programarea 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 Languages)

Page 178: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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

Obiectele 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 implementa 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() { println "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 name,Object args); Object getProperty(String property); void setProperty(String property,Object newValue); MetaClass getMetaClass(); void setMetaClass(MetaClass metaClass); }

Page 179: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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') // metode 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 acces 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[] args) 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 asociate. 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 obiect).

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 tipul

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 getMetaClass().invokeMethod(this, s, obj); } public Object getProperty(String s) { return getMetaClass().getProperty(this, s); }

Page 180: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 clasei 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 numele 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 claselor 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 la clasa String a metodei "word(int)" pentru extragerea unui cuvânt cu indice dat

dintr-un şir (şir cu mai multe cuvinte 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.metaClass.static.isEven={val->val%2==0}

String.metaClass.trimAll = { // elimina toate blancurile dintr-un sir result=new StringBuffer() delegate.each { if (it !=' ')result.append(it) } result } println 'a b c d'.trimAll() // abcd

Page 181: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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()) // numarul 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 properties 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 !

Page 182: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 182 -

Interceptarea 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 trebuie 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. Versiunea Groovy pentru această clasă (şi pentru

altele cu mai multe apeluri la delegat):

class StaffMemberUsingDelegation { private delegate = new Person() 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 setNationality(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 apeleaza else return Calc.metaClass.invokeMethod(this,name,args) } } calc=new Calc() // verificare println calc.mul(2,3) println calc.div(3,2)

Page 183: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

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 tabel 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 perpendicular pe fluxul

de apelare a funcţiilor din program (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 aplicarea altor

"filtre" la 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(String name) { if (hasLocalProperty(name)) return this.@"$name" else return delegate.getProperty(name) } }

Page 184: IE. TEHNICI AVANSATE DE PROGRAMARE - deliu.ros).pdf · Programare vizuală în NetBeans ..... 153 . INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE - 4 - Capitolul IE.11. Platforma

INFORMATICĂ*I* IE. TEHNICI AVANSATE DE PROGRAMARE

- 184 -

Bibliografie [B01] Bloch J. Effective Java, Editura Addison-Wesley, 2008

[C01] Cooper J: Design Patterns-Java Companion, Editura Addison-Wesley, 1998

[E01] Eckel B. Thinking in Java, Editura Prentice 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/java-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