P. 1
CursPoo

CursPoo

|Views: 31|Likes:
Publicado porAlex Teaca

More info:

Published by: Alex Teaca on Oct 15, 2011
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

10/15/2011

pdf

text

original

Sections

  • Diferente între limbajele Java si C
  • Structura programelor Java
  • Tipuri clasã si tipuri referintã
  • Spatii ale numelor în Java
  • Definirea si utilizarea de vectori în Java
  • Platforma Java
  • Clase si obiecte
  • Clasele creeazã noi tipuri de date
  • Clasele permit programarea genericã
  • Clasele creeazã un model pentru universul aplicatiei
  • Vectori intrinseci si obiecte vector
  • Clase fãrã obiecte. Metode statice
  • Clase instantiabile. Metode aplicabile obiectelor
  • Variabile referintã la un tip clasã
  • Argumente de functii de tip referintã
  • Clase cu obiecte nemodificabile
  • Metode statice si metode ale obiectelor
  • Utilizarea unor clase de intrare-iesire
  • Definirea unei clase în Java
  • Functii constructor
  • Cuvântul cheie "this"
  • Atribute ale membrilor claselor
  • Incapsularea datelor în clase
  • Structura unei clase Java
  • Metode care pot genera exceptii
  • Clase si obiecte Java în faza de executie
  • Clase derivate
  • Derivarea ca metodã de reutilizare
  • Derivare pentru creare de tipuri compatibile
  • Clasa Object ca bazã a ierarhiei de clase Java
  • Polimorfism si legare dinamicã
  • Structuri de date generice în POO
  • Clase abstracte în Java
  • Interfete Java
  • Diferente între interfete si clase abstracte
  • Compararea de obiecte în Java
  • Interfete pentru filtre
  • Functii “callback”
  • Clase abstracte si interfete pentru operatii de I/E
  • Infrastructura claselor colectie
  • Multimi de obiecte
  • Liste secventiale
  • Clase dictionar
  • Colectii ordonate
  • Clase iterator
  • Definirea de noi clase colectie
  • Colectii virtuale
  • Colectii arborescente
  • Clase generice în Java 5
  • Forme posibile pentru parametri tip
  • Exemple comparative
  • Colectii Google
  • Reutilizarea codului prin delegare
  • Reutilizarea codului prin derivare
  • Comparatie între compozitie si derivare
  • Combinarea compozitiei cu derivarea
  • Clase de intrare-iesire cu derivare si delegare
  • 10. Clase incluse
  • Clase incluse
  • Clase interioare
  • Simplificarea comunicãrii între clase
  • Clasele interioare cu date comune
  • Clase interioare anonime
  • Probleme asociate claselor incluse
  • Programarea unei interfete grafice (GUI)
  • Clase JFC pentru interfatã graficã
  • Solutii de programare a interfetelor grafice
  • Dispunerea componentelor într-un panou
  • Componente vizuale cu text
  • Panouri multiple
  • Apleti Java
  • Evenimente Swing
  • Tratarea evenimentelor Swing
  • Evenimente de mouse si de tastaturã
  • Evenimente asociate componentelor JFC
  • Evenimente produse de componente text
  • Mecanismul de generare a evenimentelor
  • Structura programelor dirijate de evenimente
  • Utilizarea de clase incluse
  • Clase generator si clase receptor de evenimente
  • Reducerea cuplajului dintre clase
  • Comunicarea prin evenimente si clase “model”
  • Arhitectura MVC
  • Utilizarea unui model de listã
  • Utilizarea unui model de tabel
  • Utilizarea unui model de arbore
  • Fisiere XML în aplicatii Java
  • Utilizarea unui parser SAX
  • Utilizarea unui parser DOM
  • Utilizarea unui parser StAX
  • Vizualizarea arborelui XML
  • O aplicatie cu fisiere XML
  • Analiza si proiectarea orientate pe obiecte
  • Proiectarea unei aplicatii Java de traversare directoare
  • Solutii posibile pentru clasa Finder
  • Extinderea clasei Finder
  • Alte exemple de utilizare a clasei Finder
  • 16. Scheme de proiectare
  • Scheme de proiectare
  • Fabrici abstracte
  • Clase cu un singur obiect
  • Fire de executie concurente
  • Fire de executie în Java
  • Fire de executie cu date comune
  • Sincronizarea firelor de executie concurente
  • Fire de executie în aplicatii Swing

Florian Moraru – Programare Orientata pe Obiecte CUPRINS 1. Java ca limbaj de programare cu obiecte Diferente între limbajele Java si C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4 Structura programelor Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Tipuri clasã si tipuri referintã . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Spatii ale numelor în Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Definirea si utilizarea de vectori în Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Exceptii program în Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Platforma Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2. Introducere în programarea orientatã pe obiecte Clase si obiecte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 Clasele sunt module de program reutilizabile . . . . . . . . . . . . . . . . . . . . . . . . . 19 Clasele creeazã noi tipuri de date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Clasele permit programarea genericã . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 Clasele creeazã un model pentru universul aplicatiei . . . . . . . . . . . . . . . . . . . 26 Vectori intrinseci si obiecte Vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 3. Utilizarea de clase si obiecte în Java Clase fãrã obiecte. Metode statice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 Clase instantiabile. Metode aplicabile obiectelor . . . . . . . . . . . . . . . . . . . . . . . 30 Variabile referintã la un tip clasã . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 Argumente de functii de tip referintã . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Clase cu obiecte nemodificabile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Metode statice si metode ale obiectelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Utilizarea unor clase de intrare-iesire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 4. Definirea de noi clase în Java Definirea unei clase în Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Functii constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Cuvântul cheie "this" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Atribute ale membrilor claselor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Incapsularea datelor în clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Structura unei clase Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 Metode care pot genera exceptii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 Clase si obiecte Java în faza de executie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 5. Derivare. Mostenire. Polimorfism Clase derivate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 Derivare ca metodã de reutilizare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 Derivare pentru creare de tipuri compatibile . . . . . . . . . . . . . . . . . . . . . . . . . . 58 Clasa Object ca bazã a ierarhiei de clase Java . . . . . . . . . . . . . . . . . . . . . . . . . 60 Polimorfism si legare dinamicã . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 Structuri de date generice în POO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 6. Clase abstracte si interfete Clase abstracte în Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 Interfete Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 Diferente între interfete si clase abstracte . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

1

Florian Moraru – Programare Orientata pe Obiecte Compararea de obiecte în Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 Interfete pentru filtrare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Functii “callback”. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Clase abstracte si interfete pentru operatii de I/E . . . . . . . . . . . . . . . . . . . . . . .76 7. Colectii de obiecte Object Infrastructura claselor colectie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 Multimi de obiecte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 Liste secventiale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 Clase dictionar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Colectii ordonate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 Clase iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 Definirea de noi clase colectie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 Colectii virtuale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 Colectii arborescente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 8. Colectii generice Clase generice în Java 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 Forme posibile pentru parametri tip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 Exemple comparative . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 Colectii Google . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 9. Reutilizarea codului în POO Reutilizarea codului prin delegare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 Reutilizarea codului prin derivare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 Comparatie între compozitie si derivare . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 Combinarea compozitiei cu derivarea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 Clase de intrare-iesire cu derivare si delegare . . . . . . . . . . . . . . . . . . . . . . . 112 10. Clase incluse Clase incluse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 Clase interioare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 Simplificarea comunicãrii între clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 Clase interioare cu date comune . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 Clase interioare anonime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 Probleme asociate claselor incluse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .123 11. Clase pentru o interfatã graficã Programarea unei interfete grafice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 Clase JFC pentru interfata graficã . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .126 Solutii de programare a interfetelor grafice . . . . . . . . . . . . . . . . . . . . . . . . . 127 Dispunerea componentelor într-un panou . . . . . . . . . . . . . . . . . . . . . . . . . . 129 Componente vizuale cu text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 Panouri multiple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 Apleti Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 12. Programare bazatã pe evenimente Evenimente Swing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .137 Tratarea evenimentelor Swing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .137 Evenimente de mouse si de tastaturã . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .138 Evenimente asociate componentelor JFC . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

2

Florian Moraru – Programare Orientata pe Obiecte Evenimente produse de componente cu text . . . . . . . . . . . . . . . . . . . . . . . . . 142 Mecanismul de generare a evenimentelor . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 Structura programelor dirijate de evenimente . . . . . . . . . . . . . . . . . . . . . . . . 145 Utilizarea de clase incluse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 Clase generator si receptor de evenimente . . . . . . . . . . . . . . . . . . . . . . . . . . 149 Reducerea cuplajului dintre clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 Comunicarea prin evenimente si clase “model” . . . . . . . . . . . . . . . . . . . . . . 152 13. Componente Swing cu model Arhitectura MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 Utilizarea unui model de listã . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 Utilizarea unui model de tabel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 Utilizarea unui model de arbore. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 14. Java si XML Fisiere XML în aplicatii Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 Utilizarea unui parser SAX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .168 Utilizarea unui parser DOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 Utilizarea unui parser StAX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 Vizualizarea arborelui XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176 O aplicatie cu fisiere XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 15. Proiectare orientatã pe obiecte Analiza si proiectarea orientate pe obiecte . . . . . . . . . . . . . . . . . . . . . . . . 183 Proiectarea unei aplicatii de traversare directoare . . . . . . . . . . . . . . . . . . . 184 Solutii posibile pentru clasa Finder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 Extinderea clasei Finder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 Alte exemple de utilizare a clasei Finder . . . . . . . . . . . . . . . . . . . . . . . . . . 191 16. Scheme de proiectare Scheme de proiectare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 Metode "fabricã" de obiecte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 Fabrici abstracte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 Clase cu rol de comandã . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 Clase cu un singur obiect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 Schema claselor observat-observator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 Clase model în schema MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 17. Programarea cu fire de executie Fire de executie concurente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 Fire de executie în Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210 Fire de executie cu date comune . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 Sincronizarea firelor de executie concurente . . . . . . . . . . . . . . . . . . . . . . . 215 Probleme în programarea cu fire concurente . . . . . . . . . . . . . . . . . . . . . . . 218 Procese de tip producãtor-consumator . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 Fire de executie în aplicatii Swing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224

3

Florian Moraru – Programare Orientata pe Obiecte 1. Java ca limbaj de programare cu obiecte Diferente între limbajele Java si C Limbajul Java foloseste aceleasi instructiuni cu limbajul C, mai putin instructiunea goto. Tipurile de date primitive sunt aproape aceleasi, plus tipul boolean, care a schimbat si sintaxa instructiunilor care testeazã conditii. Diferentele importante apar la tipurile de date derivate (vectori, structuri, pointeri) si la structura programelor. In limbajul C existã un singur fel de comentarii, care încep prin perechea de caractere "/*" si se terminã prin perechea de caractere "*/". In C++ au apãrut, în plus, comentarii care încep prin perechea de caractere "//" si se terminã la sfârsitul liniei în care apare acel comentariu. Java preia aceste douã feluri de comentarii, la care se adaugã comentarii destinate generãrii automate a documentatiilor programelor (cu ajutorul programului “javadoc”); aceste comentarii încep printr-un grup de 3 caractere "/**" si se terminã la fel cu comentariile C, prin "*/" Exemplu:
/** Clasa Heap * @ Data : Apr. 2000 */

Tipurile de date primitive Java preia de la C si C++ aproape toate tipurile aritmetice (short, int, long, float, double) si tipul void, dar impune o aceeasi lungime si reprezentare a tipurilor numerice pentru toate implementãrile limbajului. Un întreg de tip int ocupã 32 de biti, un short ocupã 16 biti, iar un long ocupã 64 de biti. Un float ocupã 32 de biti iar un double ocupa 64 de biti. Tipul aritmetic byte ocupã 8 biti (valori între –128 si 127). Tipul char ocupã 16 biti pentru cã standardul de reprezentare a caracterelor este UTF-16 sau Unicode (în locul codului ASCII) si permite utilizarea oricãrui alfabet. Toate tipurile aritmetice din Java reprezintã numere cu semn si nu mai existã cuvântul unsigned pentru declararea de variabile aritmetice fãrã semn. Tipul boolean din Java ocupã un singur bit; constantele de tip boolean sunt true si false. Existenta acestui tip modificã sintaxa instructiunilor if, while, do si a expresiei conditionale, precum si rezultatul expresiilor de relatie (care este acum de tip boolean si nu de tip int). Asadar, instructiunile urmãtoare sunt gresite sintactic în Java, desi sunt corecte în C si C++.
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);

Variabilele declarate în functii nu primesc valori implicite iar compilatorul semnaleazã utilizarea de variabile neinitializate 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

Ierarhizarea tipurilor aritmetice, de la “inferior” la “superior” este: byte, short, int, long, float, double

4

sqrt(4). de promovare se face pentru rezultatul unei functii. // return –1. Cea mai importantã diferentã dintre Java. dacã tipul expresiei din instructiunea return diferã de tipul declarat al functiei. Exemplu: static float rest (int a) { return a. // "floor" are rezultat double } Compilatorul Java verificã dacã este specificat un rezultat la orice iesire posibilã dintr-o functie cu tip diferit de void (de exemplu. // conversie int->float } Conversia de la un tip numeric “superior” la un tip aritmetic “inferior” trebuie cerutã explicit prin folosirea operatorului “cast” de fortare a tipului si nu se face automat ca în C. pentru determinarea memoriei ocupate de un tip sau de o variabilã. // cu trunchiere int r = (int) Math. pentru cã nu este necesar acest operator (nici chiar pentru alocare dinamicã de memorie). este absenta tipurilor pointer din Java.5f). Exemplu: byte b=65. b =(byte)ch.length. // conversie necesara de la double la int // functie de rotunjire din clasa Math public static int round (float a) { return (int)floor(a + 0. si limbajele C. ch='\n'. char ch. // cu pierdere de precizie n=(int)f.sqrt(2). Exemple : f= (float)d. ch =(char)b. 2 O altã conversie automatã. // eroare de compilare ! } In Java nu existã operatorul sizeof din C. C++ pe de altã parte.i<a. instructiuni if fãrã else ).Florian Moraru – Programare Orientata pe Obiecte Tipul char nu este un tip aritmetic dar se pot face conversii prin operatorul (tip) între tipul char si orice tip aritmetic întreg. int b) { // pozitia lui b in vectorul a for (int i=0. Supradefinirea functiilor Supradefinirea sau supraîncãrcarea functiilor (“Function Overloading”) a fost introdusã în C++ pentru a permite definirea mai multor functii cu acelasi nume si cu acelasi tip dar cu argumente 5 . pe de o parte. Deci nu existã posibilitatea de a declara explicit variabile pointer si nici operatorii unari ‘&’ (pentru obtinerea adresei unei variabile) si ‘*’ (indirectare printr-un pointer). Exemplu: double r = Math. deoarece compilatorul face automat o atribuire a valorii argumentului efectiv la argumentul formal corespunzãtor.i++) if (a[i]==b) return i. // promovare de la int la double ptr. Exemplu: public static int indexOf (int a[]. Operatorul new din C++ pentru alocare dinamicã are în Java un rezultat o referintã si nu un pointer. // ch este 'A' // b este 10 Aceleasi reguli de conversie între tipuri numerice se aplicã si între argumentele efective si argumentele formale.

valueOf” este si ea supradefinitã pentru diferite argumente. // sirul analizat private int crt. nu existã variabile externe claselor. Locul declaratiei este important. In Java. care au mai multe definitii.indexOf (' '. Declaratii de variabile O declaratie de variabilã poate sã aparã fie într-o functie. iar o variabilã declaratã la nivel de clasã este utilizabilã de orice functie din clasã (si chiar de functii din alte clase). pentru fiecare tip de date primitiv si pentru tipurile clasã String si Object : public class PrintStream . o functie este deosebitã de alte functii din aceeasi clasã (de cãtre compilator) prin "semnãtura" sa (prin "amprenta" functiei). } // extrage urmatorul cuvant dintr-un sir public String nextToken () { int start. } // verifica daca mai sunt cuvinte public boolean hasMoreTokens () { return crt < str. } public void print (float f) { write (String.charAt(crt)==' ') ++crt.crt). } public void print (boolean b) { write (b ? “true” : “false”)..Florian Moraru – Programare Orientata pe Obiecte diferite într-o aceeasi clasã. dar acesta nu este un caz de supradefinire. care este formatã din numele. 6 .length(). class StrTok { // variabile ale clasei private String str. crt=0.start)..valueOf(f)). write (s).length(). // unde se termina acest cuvant if ( crt < 0) // ultimul cuvant nu trebuie urmat de spatiu crt= str. fiindcã ele se aflã în spatii de nume diferite. // pozitia curenta in sir // constructor public StrTok (String s) { str=s. ca si în C++.{ public void print (int i) { write (String. tipul si argumentele functiei. fie în afara functiilor. Un exemplu uzual de functii supradefinite este cel al functiilor de afisare la consolã în mod text “print” si “println”. } public void print (String s) { if (s== null) s= “null”. Pot exista functii cu acelasi nume (eventual si cu acelasi argumente si tip) în clase diferite.length() && str. o variabilã dintr-o functie este localã acelei functii. return str. // ignora spatii start= crt.valueOf(i)).substring (start. } // din pachetul java.io // scrie un întreg // scrie un numãr real // scrie un boolean // scrie un sir de caractere Functia “String. // unde incepe acest cuvant crt= str. // inceput de cuvant (variabila locala) while ( crt < str. dar într-o clasã.

In C++ si în Java o declaratie poate apare oriunde într-un bloc. care contine cel putin o metodã cu numele "main" de tip void si cu atributele static si public. Domeniul de valabilitate al unei variabile începe în momentul declarãrii si se terminã la sfârsitul blocului ce contine declaratia. functii care pot fi executate fãrã a crea obiecte de tipul clasei respective. k<n.out. deci o functie poate fi apelatã înainte de a fi definitã si nici nu este necesarã declararea functiilor utilizate (nu se folosesc prototipuri de functii). Exemplul urmãtor este un program minimal. #include.Florian Moraru – Programare Orientata pe Obiecte } } In C toate declaratiile dintr-un bloc trebuie sã preceadã prima instructiune executabilã din acel bloc. într-o clasã si trebuie sã aibã un argument vector de siruri. între alte instructiuni sau declaratii. ca metodã staticã.14159265358979323846. Orice functie apartine unei clase si nu se pot defini functii în afara claselor. In exemplele urmãtoare se vor folosi numai clase care reunesc câteva metode statice. Instructiunea for constituie un caz special: variabila contor se declarã de obicei în instructiunea for. executia unui program începe cu functia “main”. Exemplul urmãtor este un fisier sursã cu o singurã clasã. care afiseazã un text constant: public class Main { public static void main (String arg[) { System. Ca si în C. } } In Java nu conteazã ordinea în care sunt scrise functiile (metodele) unei clase. In Java nu existã operatorul sizeof . iar la alocarea de memorie (cu new) nu trebuie specificatã dimensiunea alocatã. Metoda "main" trebuie sã aibã ca unic argument un vector de obiecte String. ambele publice si statice: 7 . iar valabilitatea acestei declaratii este limitatã la instructiunile repetate prin instructiunea for . Exemplu: public static final double PI = 3. k++) if ( x[k]==y) break. Structura programelor Java O aplicatie Java contine cel putin o clasã. deci nu existã directivele preprocesor atât de mult folosite în C si în C++ : #define. Compilatorul Java nu are preprocesor. return k==n ? false : true. Exemplu: public static boolean este ( int x[ ]. // in clasa Math Alte diferente între Java si C In Java nu existã declaratia typedef deoarece definirea unei clase introduce automat un nume pentru un nou tip de date. pentru cã lungimea variabilelor este cunoscutã.println (" Main started "). care contine douã metode.length. doar cã “main” trebuie sã fie inclusã. int y) { int n=x. // eroare: k nedeclarat ! } In Java nu existã cuvântul cheie const iar constantele sunt declarate ca variabile cu atributele static si final. // lungime vector x for (int k=0. etc.

Faza de executie a unui program Java constã din încãrcarea si interpretarea tuturor claselor necesare executiei metodei “main” din clasa specificatã în comanda “java”.x=2.Florian Moraru – Programare Orientata pe Obiecte class Main { public static void main (String arg[ ]) { writeln ("Hello world !"). dar numai una din ele poate avea atributul public. // coordonate punct } // creare si utilizare obiect din clasa "Point" Point a = new Point().writeln ("Hello world !"). y.println (txt).println (txt). Exemplu: public class Point { // orice punct din plan public double x. } } // cu "main" incepe executia // afiseaza un text pe ecran O metodã ne-staticã trebuie apelatã pentru un anumit obiect. se compileazã si alte fisiere sursã cu clase folosite de fisierul transmis spre compilare. Compilatorul Java creeazã pentru fiecare clasã din fisierul sursã câte un fisier cu extensia “class” si cu numele clasei.y.y =-3. variabilã publicã din clasa System. iar definirea unei clase foloseste cuvântul cheie class. a. } } // cu "main" incepe executia // afiseaza un text pe ecran Numele unei metode statice trebuie precedat de numele clasei din care face parte (separate printr-un punct). generat automat a. Metoda "println" este apelatã pentru obiectul adresat de variabila "out". Dacã este necesar. Tipuri clasã si tipuri referintã O clasã este o structurã care poate contine atât date cât si functii ca membri ai structurii. Numele fisierului sursã (de tip “java”) trebuie sã coincidã cu numele clasei publice pe care o contine. O clasã publicã este accesibilã si unor clase din alte pachete de clase. Se pot defini clase ce contin numai date publice. Un fisier sursã Java poate contine mai multe clase. Exemplu: public class Main { public static void main (String arg[ ]) { Util. } public static void writeln (String txt) { System.. // constructor implicit. } } public class Util { public static void writeln (String txt) { System. // un punct in cadranul 4 In practicã se preferã ca variabilele clasei "Point" sã fie de tip private (inaccesibile unor metode din alte clase) si ca initializarea lor sã se facã în constructorul clasei: public class Point { private double x. In Java nu mai existã cuvântul cheie struct .out. dacã este apelatã dintr-o metodã a unei alte clase. // orice punct din plan // coordonate punct 8 .out. iar numele ei trebuie precedat de numele obiectului (si un punct). echivalente structurilor din limbajele C si C++.

O metodã staticã corespunde unei functii din limbajul C. iar println este numele unei metode din clasa PrintStream. utilizabile în alte clase.println ("Eroare !").abs(x). // valoarea absoluta a lui x // radical din x // conversie sir "str" la tipul "int" Definirea unei clase instantiabile T creeazã automat un nou tip de date T. care pot genera obiecte. Exemple: System. String tcuv[] ={"unu". Un obiect de tip T este o instantiere a clasei T si este referit printr-o variabilã de tip T.out. double y = Math.out.Clase neinstantiabile . Exemplu: int len = mesaj. Exemple: String msg = "Eroare". String msg. Clasa String contine mai multe metode (functii) publice. metoda length. vectori sau functii de tip String. Exemple: double xabs = Math.println ( “ Eroare “). // o variabila sir // un vector de siruri Un obiect Java corespunde unei variabile structurã din C. Un alt exemplu este o constructie mult folositã în Java pentru afisarea la consolã (în mod text) a unor siruri de caractere: System.sqrt(x). care contin doar metode statice (si eventual constante).parseInt (str). Acest exemplu aratã cã membrii unei clase se folosesc la fel cu membrii unei structuri. cu diferenta cã numele functiei trebuie precedat de numele clasei din care face parte.Florian Moraru – Programare Orientata pe Obiecte public Point (double xi. System. ce poate fi folosit în declararea de variabile. iar variabila de tip clasã trebuie initializatã cu rezultatul operatorului new. De exemplu. care contin date si metode (ne-statice). ale cãror adrese pot fi folosite în atribuiri sau initializãri la declarare.println (mesaj). double yi) { x=xi. int n = Integer. iar o variabilã de un tip clasã corespunde unei variabile pointer la o structurã din C. are ca rezultat (întreg) lungimea sirului continut în obiectul de tip String pentru care se apeleazã metoda. y=yi.Clase instantiabile. ."trei"}. Clasa Java cu numele String defineste un tip de date String. indiferent cã ei sunt variabile (câmpuri) sau functii (metode). // alocare memorie pentru sir Pentru constantele de tip String se creeazã obiecte automat. In Java toate obiectele sunt alocate dinamic. // String este o clasã predefinitã mesaj = new String (“ Eroare ! “) .out. Exemplu: String mesaj. 9 . out este numele unei variabile publice (din clasa System) de un tip clasã (PrintStream). In aceste exemple System este numele unei clase predefinite. } } // functie constructor Clasele (neabstracte) Java sunt de douã categorii: . fãrã argumente.length(). de cãtre compilator. msg = " Corect". folosind operatorul new."doi".

s2="util.size() ).". Exemple: String s1="java.out.Rand Operatorul de concatenare “+” este singurul operator “supradefinit” în Java si el poate fi utilizat între operanzi de tip String sau cu un operand de tip String si un alt operand de orice tip primitiv sau de un tip clasã (pentru care existã o functie de conversie la tipul String ). Exemplu: System. In cadrul unui spatiu nu pot exista douã sau mai multe nume identice (exceptie fac metodele supradefinite dintr-o aceeasi clasã). // a = variabila referinta la un obiect de tip Vector Atunci când se apeleazã o metodã pentru un obiect.out.println ( a.isDigit ( str.getName()). poate crea impresia cã variabilele de tip String contin chiar sirurile care se concateneazã si nu adresele lor. Indirectarea prin variabila referintã este realizatã automat de compilator. b=2 .charAt(0)) ) .Florian Moraru – Programare Orientata pe Obiecte Numele unei metode poate fi precedat de numele unei variabile clasã sau de numele unei clase. // daca primul caracter e o cifra System. // 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. .length().util. O variabilã referintã apare de obicei în stânga unei atribuiri cu operatorul new sau cu constanta null în partea dreaptã. Tipul referintã a fost introdus în C++ în principal pentru a declara parametri modificabili în functii. deoarece toate variabilele de un tip clasã sunt automat considerate ca variabile referintã. ca în C . dar întotdeauna caracterul separator este un punct. Fiecare clasã creeazã un spatiu de nume pentru variabilele si metodele clasei.out. . Exemple: Main. fãrã a folosi un operator special. metoda writeln 10 .println (obj + obj. O referintã la un tip clasã T este de fapt un pointer la tipul T dar care se foloseste ca si cum ar fi o variabilã de tipul T. // afisare dimensiune vector a Operatorul de concatenare '+'. s3="Rand". Exemplu: Vector a = new Vector( ). se foloseste numele variabilei referintã ca si cum acest nume ar reprezenta chiar obiectul respectiv si nu adresa sa. // clasa Main. System.out. int maxdigits= (Integer. cu simplificarea scrierii si utilizãrii acestor functii. Exemplu: int a=3. In Java nu trebuie folositã o sintaxã specialã pentru declararea de variabile sau de parametri referintã.println (s1+s2+s3). // scrie java. Este uzual în Java sã avem denumiri de variabile sau de metode care contin câteva puncte de separare a numelor folosite în precizarea contextului.". ca urmare numele metodelor sau datelor publice dintr-o clasã trebuie precedate de numele clasei. Nu se pot defini referinte la tipuri primitive.writeln ("abc"). Spatii ale numelor în Java Un spatiu de nume ("namespace") este un domeniu de valabilitate pentru un nume simbolic ales de programator. Pot exista nume identice în spatii diferite. O variabilã referintã Java nu este un obiect.MAX_VALUE+""). dar contine adresa unui obiect alocat dinamic. System. Exemple: if ( Character.println ( a + “+” + b + “=“ + (a+b)). atunci când se folosesc în alte clase.getClass(). folosit între obiecte de tip String.

} Pachetul cu numele "java. parte din JDK. System.Random. Exemplul urmãtor ilustreazã folosirea instructiunii "import": import java.lang : public static void main (String arg[]) { java. fac parte din pachetul java. . metoda sqrt // clasa System. variabila out Clasele înrudite ca rol sau care se apeleazã între ele sunt grupate în "pachete" de clase ("package").lang. dar care au la început aceeasi instructiune "package". încep prin java sau javax. iar clasa Random este definitã în pachetul "java. } // fisierul EOFException.nextFloat()).out // clasa Math. cu fisierele de tip “class” corespunzãtoare claselor din pachet. java.lang" ("language") este folosit de orice program Java si de aceea numele lui nu mai trebuie mentionat înaintea numelui unei clase din java. Instructiunea import permite simplificarea referirilor la clase din alte pachete si poate avea mai multe forme. Notatia "rand. fãrã a mai fi precedate de numele pachetului.java package java.util".i++) // scrie 10 numere aleatoare System.util. .util. Numele unui pachet poate avea mai multe componente. separate prin puncte. javax. Instructiunea package se foloseste pentru a specifica numele pachetului din care vor face parte clasele definite în fisierul respectiv.swing.io. } Variabila cu numele "rand" este de tipul Random.io.a. class R { public static void main (String arg[]) { 11 .Florian Moraru – Programare Orientata pe Obiecte Math.System s. De observat cã un fisier sursã nu creeazã un spatiu de nume.java package java. public class IOException extends Exception { .util.Random rand =new java.*.util. // sau import java.out.Random(). Un nume de pachet corespunde unui nume de director.util.Integer. atunci când este folosit într-un alt pachet.tree Un pachet este un spatiu al numelor pentru numele claselor din acel pachet. Exemplu: // fisierul IOException. for (int i=1.* .println ( rand. Exemple : java.nextFloat()" exprimã apelul metodei "nextFloat" din clasa "Random" pentru obiectul adresat de variabila "rand".regex . este posibil si chiar uzual ca în componenta unui pachet sã intre clase aflate în fisiere sursã diferite.Object.sqrt(x). . In general numele unei clase (publice) trebuie precedat de numele pachetului din care face parte.io . java.awt. ea trebuie sã fie prima instructiune din fisierul sursã Java. public class EOFException extends IOException { . situatia unor mici programe de test pentru depanarea unor clase. Cea mai folositã formã este: import pachet.i<=10. .lang. In lipsa unei instructiuni package se considerã cã este vorba de un pachet anonim implicit. Instructiunea anterioarã permite folosirea numelor tuturor claselor dintr-un pachet cu numele "pachet". Numele de pachete cu clase predefinite. Clasele String. Exemplu care ilustreazã utilizarea unei clase dintr-un alt pachet decât java.

// clasa java. // corect: int a[] = new int[100]. atunci când vectorul este initializat la declarare cu un sir de valori. int [ ][ ] b. In Java. Exemplu: int a[ ][ ] . byte[ ][ ] graf = new byte [n][n]. Exemple: float x[ ] = new float [10]. // la fel ca in C si C++ // specific Java Declararea matricelor (vectori de vectori) poate avea si ea douã forme. echivalente: tip nume [ ].List sau interfata java.util.*. // aloca memorie ptr 10 reali int n=10.println ( rand.2. deoarece alocarea de memorie nu se face niciodatã la compilare. Este posibilã si o alocare automatã. expresia determinã numãrul de componente (nu de octeti !) pe care le poate contine vectorul. . cu valori ce depind de tipul lor: zerouri pentru elemente numerice. Exemplu care aratã riscurile importului tuturor claselor dintr-un pachet: import java.3.out.awt. C++ si Java. . Acest tip este diferit de tipul definit de clasa JDK Vector (vectori ce se pot extinde automat) si de aceea vom folosi si denumirea de vector intrinsec (limbajului) pentru vectori ca cei din C.Florian Moraru – Programare Orientata pe Obiecte Random rand =new Random(). tip [ ] nume. null pentru variabile referintã de orice tip.i<=10. // o matrice de întregi // altã matrice de întregi In Java nu este permisã specificarea unor dimensiuni la declararea unor vectori sau matrice.util. In lipsa unei initializãri explicite. } } Uneori se preferã importul de clase individuale.*. O variabilã vector este automat în Java o variabilã referintã iar memoria trebuie alocatã dinamic pentru orice vector.5. . import java. declararea unei variabile (sau unui parametru formal) de un tip vector se poate face în douã moduri.List ? } } Definirea si utilizarea de vectori în Java Cuvântul “vector” este folosit aici ca echivalent pentru “array” din limba englezã si se referã la un tip de date implicit limbajelor C.7}.nextFloat()). 12 . Exemplu: short prime[ ] = {1. atât pentru documentare cât si pentru evitarea ambiguitãtilor create de clase cu acelasi nume din pachete diferite.i++) // scrie 10 numere aleatoare System.awt. Exemplu de eroare: int a[100]. Alocarea de memorie pentru un vector se face folosind operatorul new urmat de un nume de tip si de o expresie (cu rezultat întreg) între paranteze drepte. for (int i=1. componentele unui vector sunt initializate automat. class test { public static void main (String av[ ]) { List list.

Florian Moraru – Programare Orientata pe Obiecte Un vector intrinsec cu componente de un anumit tip este considerat ca un obiect de un tip clasã. Exemplu: int [ ] a= new int [10].i < a. deci în exemplul anterior se produce exceptia de depãsire a limitelor la valoarea i=10 .i++) if ( max. Functia urmãtoare determinã sirul cel mai lung dintr-un vector de siruri: static String maxLen ( String v[ ]) { String max =v[0].length() ) // compara lungimile a doua siruri max=v[i]. [F pentru float[] s.d.int b[ ] ) { for (int i=0. se verificã automat. } De retinut cã length este dimensiunea alocatã si nu dimensiunea efectivã a unui vector. // retine in max sirul cel mai lung return max. Functia care determinã sirul de lungime maximã dintr-un vector de siruri se poate scrie astfel: static String maxLen ( String v[ ]) { String max =0. for (int i=0. Variabila predefinitã cu numele length poate fi folositã ( ca membru al claselor vector ) pentru a obtine dimensiunea alocatã pentru un vector (“capacitatea vectorului”). // exceptie la a[10]=10 Numerotarea componentelor unui vector este de la zero la (length-1). [Z pentru boolean[].i<=10. // retine adresa sirului cel mai lung return max. Numele acestor clase este format din caracterul ‘[‘ urmat de o literã ce depinde de tipul componentelor vectorului: [I pentru int[].length() < v[i]. // initializare maxim partial for (String s: v) // repeta pentru s cu valori in vectorul v if ( max.m. [B pentru byte[]. atribuind succesiv lui “ob” valorile din vectorul “a” (valori ce pot fi de un tip primitiv sau de un tip clasã). 13 . Variabila length nu trebuie confundatã cu metoda "length()" din clasa String.length. In Java. } Din versiunea 5 se poate itera printr-un vector la fel ca si printr-uo colectie de obiecte folosind un ciclu de forma: for ( tip ob: a) { …} // “tip” este tipul elementelor vectorului a cu semnificatia: “repetã ceva pentru fiecare valoare a variabilei “ob” din vectorul “a”. [C pentru char[]. tip recunoscut de compilator dar care nu este definit explicit în nici un pachet de clase. iar numãrul de elemente din vector se transmite ca argument la functii. i++) // a. Exemplu: // functie de copiere a unui vector public static void copyVec ( int a [ ] .a.length =dimensiune vector a b[i] = a[i]. atunci când diferã de capacitatea sa.i++) a[i]=i. for (int i=1. la executie.length() ) // compara lungimile a doua siruri max=s. iesirea din limite produce o exceptie si terminarea programului. } O matrice este privitã si în Java ca un vector de vectori. iar variabila "length" se poate folosi pentru fiecare linie din matrice. încadrarea indicilor între limitele declarate.i<v. pentru a determina numãrul de coloane.length.length() < s.

i<a.equals (x. Nu este necesarã transmiterea dimensiunilor matricei la o functie dacã matricea este ocupatã complet (la capacitatea ei).toString(t)). } } // functie de afisare matrice // utilizare functie O functie poate avea un rezultat de un tip vector (sau matrice).out.print (a[i][j]+ " ").4.i++) { for (int j=0. for (int i=1.out.3}.sort (x)."5".a.binarySearch (t.util” reuneste functii pentru operatii uzuale cu vectori având elemente de orice tip (primitiv sau clasã): afisare.i++) if ( n %i == 0) d[k++]=i.6 }."2".6} }.8. "3")."1" }. Exemplu: import java. nu existã probleme la transmiterea unei matrice ca argument la o functie. // scrie "true" } } Din versiunea 5 s-au adãugat clasei Arrays metode statice pentru transformarea în sir de caractere a continutului unui vector intrinsec unidimensional sau multidimensional cu elemente de orice tip 14 . {4. transmiterea parametrilor se face prin valoare. System. j<a[i]. ca si în C. int d[ ]) { int k=0.length. // ordonare vector t System.Florian Moraru – Programare Orientata pe Obiecte Deoarece orice matrice este alocatã dinamic. // cautare in vector ordonat float x[ ] = { 3. 2. // afisare vector int k = Arrays.clone(). adicã se copiazã valorile parametrilor efectivi în parametrii formali corespunzãtori. cãutare s. printVec(x). printmat (mat). // aloca memorie ptr vector divizori int nd = divizori (m.println ( Arrays. // completare vector divizori Clasa Arrays din pachetul “java. Exemplu: class Matrice { public static void printmat (int a[ ][ ]) { for (int i=0. // y este o copie a vectorului x System. } } public static void main (String [ ] arg) { int mat[ ][ ]= { {1. div). // un vector de numere reale Arrays. dar poate modifica componentele unui vector primit ca argument. // pune divizorul i în vectorul d return k.5. înainte de executia functiei.2. In Java.length."3".println ().1. j++) System. Exemplu: // creare vector cu divizorii unui întreg static int divizori (int n. 7.out.i<n. // un vector de siruri Arrays.println ( Arrays. sortare. // numar de divizori } // utilizare functie int div[ ]= new int [m].y)).Arrays. 5. class ExArrays { // utilizare functii din clasa Arrays public static void main ( String args[] ) { String t[ ] ={ "4".out. Deci o functie Java nu poate transmite rezultate prin argumente de un tip primitiv."6".sort (t).util. // ordonare vector x float y[ ] = (float[ ]) x.

println ( Arrays. Exemplu: public static void main (String arg[ ]) throws Exception { int ch= System. erori la operatii de citire-scriere. exceptie generatã în functia "parseInt". Exemplu de folosire: double [ ] a= {1. fie "aruncate" mai departe. pentru cã permite semnalarea la executie a unor erori uzuale. 2. utilizarea unei variabile referintã ce contine null pentru referire la date sau la metode publice. .Florian Moraru – Programare Orientata pe Obiecte primitiv sau de tip Object. Ambele exceptii mentionate erau exceptii "Runtime".out. dar care pot fi interceptate si tratate de cãtre programator. 0.0.3.println ( Integer.in. Exceptiile generate de operatiile de intrare-iesire (inclusiv la consolã) trebuie fie aruncate.deepToString(b)). pentru cã altfel compilatorul marcheazã cu eroare functia în care poate apare o astfel de eroare ("Checked Exceptions": exceptii a cãror tratare este verificatã de compilator). 1. 1. s. Compilatorul stie care metode pot genera exceptii si cere ca functiile care apeleazã aceste metode sã arunce mai departe sau sã trateze exceptiile posibile.0.out.println ((char) ch). 2. ce pot apãrea în anumite puncte din program si care afecteazã continuarea programului. conversie prin "cast" între tipuri incompatibile. Aceste exceptii corespund unor erori grave de programare. } } // afiseaza primul argument O comandã pentru executia programului de forma "java Exc" produce exceptia ArrayIndexOutOfBoundException. fie tratate pentru cã suntem obligati de cãtre compilatorul Java. împãrtire prin zero. prin abatere de la cursul normal. dar nu produc neapãrat terminarea programului.Aceste exceptii corespund unor situatii speciale care apar la utilizarea unui program (fisiere negasite. 2.). Exceptiile pot fi privite ca evenimente previzibile. System. // citeste un caracter de la tastatura System.Exceptii care nu necesitã interventia programatorului (numite "Runtime Exceptions"). care nu permit continuarea executiei si care apar frecvent în programe. {2.2" (argumentul arg[0] nu este un sir corect pentru un numãr întreg) produce o exceptie NumberFormatException.1.out. cum ar fi: erori de indexare a elementelor unui vector (indice în afara limitelor). date incorecte).toString(a)).Exceptii care trebuie fie tratate.2} }. {1. Dacã nu sunt tratate.2. evitând efectele imprevizibile ale acestor erori (în C. vectorul "arg" are lungime zero si deci nu existã arg[0] (indice zero). Double b[ ][ ]= { {0. Exceptii program în Java O exceptie program este o situatie anormalã apãrutã în executie si care poate avea cauze hardware sau software.read ( ). aceste exceptii produc afisarea unui mesaj referitor la tipul exceptiei si terminarea fortatã a programului. 4.println(Arrays.1. 0. de ex.0.parseInt(arg[0])).1. Exceptiile Java sunt de douã categorii: .2}. deoarece nu s-au transmis argumente prin linia de comandã. dacã este folosit gresit: class Exc { public static void main (String arg[ ]) { System. Urmãtorul program poate produce (cel putin) douã exceptii.2}. prin mesaje clare asupra cauzei si locului unde s-a produs eroarea. // afisare caracter citit } 15 . Existenta exceptiilor este un mare avantaj al limbajului Java.out. O linie de comandã de forma "java Exc 1. System.1. 3.a.4}.

out. In aceste cazuri vom face o tratare minimalã a exceptiilor (afisarea cauzei cu metoda “printStackTrace” prezentã în orice clasã exceptie). fie sã trateze exceptia printr-un bloc trycatch care sã includã apelul metodei. Exemplu: public static void main (String arg[]) { Object ref=null. cu acelasi efect la executie ca al exceptiilor “Runtime”: int ch.read()) >=0) // citeste un octet de la consola System. // afisare in ecou ca un caracter } catch(IOException e) {e.parseFloat (s). try {int h = ref.} Variabilele folosite în blocul try-catch vor fi declarate în afara acestui bloc. care împiedicã aparitia unui mesaj de avertizare la producerea exceptiei.Florian Moraru – Programare Orientata pe Obiecte Absenta clauzei throws din functia "main" este semnalatã ca eroare de compilator. pentru a obliga programatorul sã ia în considerare exceptia ce poate apare la functia de citire "read". pentru a fi accesibile si în afara blocului. datoritã citirii caracterului EOF (sfârsit de fisier) sau unei erori la citire. Aruncarea exceptiei nu este posibilã în definirea unor functii din interfete sau mostenite de la alte clase (pentru cã ar modifica antetul functiei). } catch(NumberFormatException ex) {return false. } catch (NumberFormatException ex) { return false.hashCode(). Existã si situatii când exceptia trebuie tratatã si altfel decât prin afisarea unui mesaj. // daca s-a produs exceptie } return true.} return true. exemplul urmãtor aratã cum putem verifica dacã un sir de caractere reprezintã un numãr corect (în orice format permis pentru numere neîntregi).printStackTrace().parseDouble(s). fãrã a examina fiecare caracter în parte: public static boolean isNumber (String s){ try { Double. O metodã care apeleazã o metodã ce aruncã exceptii verificate trebuie fie sã semnaleze mai departe posibilitatea aparitiei acestei exceptii (prin clauza throws).println ((char)ch). } 16 .in. Exemplu de verificare a unui sir dacã reprezintã un numãr corect sau nu. prin tratarea exceptiei: public static boolean isNumber (String s) { try { Float. } catch (NullPointerException e) { } } // exceptie daca ref==null // interzice afisare mesaj (nerecomandat!) Anumite functii sunt apelate de mai multe alte functii si deci aruncarea exceptiei obligã si functiile care le apeleazã sã arunce exceptie. Tratarea exceptiilor necesitã folosirea unui bloc try-catch pentru delimitarea sectiunii de cod pentru care exceptiile posibile sunt redirectate cãtre secvente scrise de utilizator pentru tratarea exceptiilor produse. try { while ( (ch = System. // daca nu s-a produs exceptie } Este preferabilã aruncarea unei exceptii fatã de tratarea prin ignorarea exceptiei.

. Producerea unor exceptii poate fi prevenitã prin verificãri efectuate de programator. sau a utilizãrii unor variabile referintã ce pot fi nule). de ex. // deschide fisier pentru citire } catch (IOException e) { // exceptie de fisier negasit System.println ("Fisier negasit")."r").exit(-1). } catch (EOFException e) { } catch (IOException e) { System. In exemplul urmãtor se foloseste un singur bloc try pentru diferite exceptii posibile: try { f= new RandomAccessFile(arg[0].out.out. iar un bloc try poate contine una sau mai multe instructiuni.Florian Moraru – Programare Orientata pe Obiecte O functie poate contine unul sau mai multe blocuri try . } catch (FileNotFoundException e) { System.) combinatã cu tratarea altor exceptii (de exemplu exceptia de sfârsit de fisier).println("Eroare la citire fisier")."r")."r").out. Un bloc try se terminã cu una sau mai multe clauze catch pentru diferite tipuri de exceptii care pot apare în bloc. } Varianta urmãtoare foloseste douã blocuri try pentru a trata separat exceptiile: try { f= new RandomAccessFile(arg[0]. ca în exemplele urmãtoare: void fun (Object obj) { if (obj==null) { System. 17 . // afiseaza primul argument } Uneori este preferabilã verificarea prin program (ca în cazul unor conversii de tip nepermise). } .println ("Null Reference").out. // citeste si scrie un numar } catch (IOException e) { e.println ( arg[0]).println ( f. System. în care pot apare exceptii.out. while ( true) System.exit(-1). dar separã fiecare tip de exceptie: try { f= new RandomAccessFile("numer". } try { while ( true) System.out. } public static void main (String arg[ ]) { if (arg.out.readInt()). dar alteori este preferabilã tratarea exceptiei (ca în cazul detectãrii existentei unui fisier înainte de a fi deschis.println ( f.length == 0 ) { System.println ( f.readInt()).println ("No Argument").out. } System.printStackTrace(). } Este posibilã si aruncarea unor exceptii putin probabile (exceptia de citire..println("Eroare la citire fisier"). // citeste si scrie un numar } catch (IOException e) { } // exceptie de sfarsit de fisier la citire Varianta urmãtoare foloseste un singur bloc try. System.readInt()). // deschide fisier pentru citire while ( true) System.out.

Semnalarea erorilor prin exceptii si informatiile furnizate despre exceptii se datoreazã tot executiei programelor Java sub controlul masinii virtuale. Groovy. Codul intermediar generat de compilatoarele Java (numit “bytecode”) este interpretat sau executat într-o masinã virtualã Java (JVM). alãturi de independenta fatã de procesorul pe care se executã. se pot adãuga metadate (“annotations”=adnotãri). numele superclasei.) sunt interpretate. C#. numite si metadate: tipul clasei (sau interfetei). apeluri de metode determinate la executie si alte operatii imposibile pentru un limbaj compilat. ceea ce a dat nastere expresiei de cod controlat (“managed code”). In plus.a. explicã de ce limbajele orientate pe obiecte mai noi (Java.a. Codul intermediar Java este “asistat” (supravegheat) la executie de cãtre masina virtualã. formele de constructori pentru obiectele clasei s. 18 . Aceste facilitãti. Masina virtualã împreunã cu bibliotecile standard de clase Java formeazã “mediul de executie Java” (JRE=Java Runtime Environment). Ruby s. utilizatorii au posibilitatea de a modifica anumite aspecte ale comportãrii masinii virtuale Java. utilizabile de cãtre diverse programe înainte de executie sau în cursul executiei. numele variabilelor din clasã. Acest mod de lucru face posibilã utilizarea de clase cu nume cunoscut doar la executie sau înlocuirea unor clase. aceeasi masinã virtualã Java si aceleasi biblioteci de clase (extinse si cu alte clase). Impreunã cu codul clasei (metodele clasei în format compilat) se mai încarcã si informatii despre clasã. Interpretorul încarcã automat sau la cerere clasele necesare pentru executia functiei “main”(din fisiere de tip “class” sau de tip “jar” locale sau aduse prin retea de la alte calculatoare). Scala) care au în comun acelasi cod intermediar. Aceste metadate permit obtinerea de informatii despre clase la executie. numele si tipul metodelor clasei.Florian Moraru – Programare Orientata pe Obiecte Platforma Java 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. fãrã interventie în codul sursã (cu conditia ca aceste clase sã respecte anumite interfete). JRuby. Mai nou. instantierea de clase cunoscute numai la executie.

fclose(in).read()) != -1) // cere obiectului “in” operatia “read” out. Introducere în programarea orientatã pe obiecte Clase si obiecte Programarea cu clase si obiecte reprezintã un alt mod de abordare a programãrii decât programarea proceduralã (în limbaje ca C sau Pascal). iar prin metoda “write” se cere obiectului “out” sã scrie în fisier caracterul primit ca argument. } Actiunile necesare copierii se realizeazã prin functiile fopen. In programarea orientatã pe obiecte programatorul creeazã obiectele necesare aplicatiei si apeleazã metode ale acestor obiecte pentru actiuni ce folosesc datele continute în obiectele aplicatiei.close(). while ( (ch= in. Un program orientat pe obiecte este o colectie de obiecte care interactioneazã prin apeluri de functii specifice fiecãrui tip de obiect. Pentru obiectele “in” si “out” se pot apela si alte metode. Un program procedural (scris în C de exemplu) este o colectie de functii. folosind operatorul new se si deschid cele douã fisiere (actiunea functiei “fopen” din C). char * dst) { char ch. out."r"). prin metoda “read” se cere obiectului “in” sã citeascã si sã furnizeze un caracter. Obiectele “in” si “out” contin informatii despre fisierele “src” si “dst” necesare prelucrãrii lor."w"). fputc. FILE * in =fopen(src. Exemplul urmãtor este o functie Java de copiere octeti dintr-un fisier sursa “src” intr-un fisier destinatie “dst”: public static void filecopy (String src. Un obiect corespunde unei variabile structurã din C.close().Florian Moraru – Programare Orientata pe Obiecte 2. Exemplul urmãtor este o functie C de copiere a unui fisier. // cere obiectului “out” operatia “write” in. // scrie un caracter fclose(out). sub formã de proceduri (functii). din clasele respective. In C++ o variabilã de un tip clasã este un nume pentru un obiect. In programarea proceduralã sarcina programatorului este de a specifica actiuni de prelucrare. cam aceleasi care se memoreazã într-o structurã de tip FILE în limbajul C. // deschide fisier sursa FILE * out =fopen(dst. iar datele prelucrate se transmit între functii prin argumente (sau prin variabile externe). fclose care primesc ca date variabilele “in”. // cere obiectelor operatia “close” } In acest exemplu se folosesc douã obiecte: un obiect de tip FileReader (prin variabila “in”) si un obiect de tip FileWriter (prin variabila “out”).write(ch). octet cu octet: // copiere fisier in C void filecopy ( char * src. cu avantaje în dezvoltarea programelor mari. // un obiect FileWriter out = new FileWriter (dst). fgetc. Definitia unei clase poate fi privitã ca o extindere a definirii unui tip structurã din C. La crearea obiectelor. mai corectã este exprimarea ‘se apeleazã 19 . Aceste functii se numesc si metode ale clasei. care contine si functii pentru operatii cu datelor continute în clasã. “out” si “ch”. // deschide fisier destinatie while ( (ch=fgetc (in)) != -1) // citeste un caracter fputc (ch. o metodã poate fi aplicatã numai obiectelor din clasa care contine si metoda. In Java si în C++ functiile (metodele) sunt subordonate claselor. String dst) throws IOException { FileReader in = new FileReader (src). dar în Java o variabilã de un tip clasã contine un pointer cãtre un obiect (corespunde unei variabile referintã din C++). De aceea. out). // alt obiect int ch.

// sursa datelor private FileWriter out. apelatã implicit de operatorul new. realizate prin metodele clasei din care fac parte. } // o metoda de copiere public void copy () throws IOException { int c.write(c). iar un obiect este un caz concret (o realizare a conceptului sau o instantiere a clasei).Florian Moraru – Programare Orientata pe Obiecte metoda “read” pentru obiectul adresat prin variabila “in”’. trebuie spus cã utilizarea unei functii statice de copiere nu este în spiritul POO. definitã anterior. clasa StringTokenizer folositã la extragerea cuvintelor dintr-un sir s. obiecte diferite contin date diferite. ce trebuia sã fie foarte simplu. clasa Enumerator folositã la enumerarea elementelor unei colectii. // cere obiectului “fc” operatia “copy” } } Clasa “FileCopier”. // creare obiect “fc” fc. Eliberarea memoriei se face automat si nu cade în sarcina programatorului Java. out. arg[1]). In Java existã obiecte comparator (de un subtip al tipului Comparator) folosite în compararea altor obiecte. Obiectele Java sunt create dinamic. ar trebui definitã o clasã copiator de fisiere. Relativ la exemplul Java. while ( (c= in. care are ca rezultat o referintã la obiectul alocat si initializat printr-o functie constructor.close().copy(). Mai aproape de stilul propriu POO. si nu ca un model de clasã realã Java. In general. folosind de obicei operatorul new. Un obiect de tip FileReader corespunde unui anumit fisier. Functiile statice Java corespund functiilor C si pot fi folosite fãrã ca sã existe obiecte (ele fac parte totusi din anumite clase). String dst) throws IOException { in = new FileReader (src). In exemplele anterioare si în cele ce vor urma se poate observa mutarea accentului de pe actiuni (functii) pe obiecte (date) în programarea orientatã pe obiecte. având si o metodã (nestaticã) de copiere.a.close(). Numele de clase sunt substantive. // destinatia datelor // constructor public FileCopier (String src.read()) != -1) out. Este posibil ca mai multe variabile de un tip clasã sã continã adresa aceluiasi obiect. Exemplu: public class FileCopier { // datele clasei private FileReader in. uneori derivate din verbul ce defineste principala actiune asociatã obiectelor respective. cu sau fãrã argumente. cu nume dat la construirea obiectului. 20 . trebuie privitã doar ca un prim exemplu. O clasã corespunde unei notiuni abstracte cum ar fi “orice fisier disc”. out = new FileWriter (dst). dar toate obiectele suportã aceleasi operatii. Clasele sunt module de program reutilizabile Definirea si utilizarea de module functionale permite stãpânirea complexitãtii programelor mari si reutilizarea de module prin crearea de biblioteci. } } // clasa pentru verificarea clasei FileCopier class UseFileCopier { public static void main (String arg[]) throws IOException { FileCopier fc = new FileCopier (arg[0]. in.

In limbajul C se pot defini functiile pentru operatii cu stiva astfel ca utilizarea lor sã nu depindã de implementarea stivei. // initializare stiva int emptySt (Stiva * s). în sensul cã modificarea anumitor module nu va afecta restul programului. ca posibilitãti oferite). Pentru concretizare vom prezenta solutiile C si Java pentru câteva probleme.O clasã poate încapsula algoritmi de complexitate ridicatã. Aceastã decuplare sau separare între module se poate realiza prin mai multe metode. scoate datele din vârful stivei si test de stivã goalã. care sã fie folositã împreunã cu programul de utilizare a stivei. s. x++) // genereaza date ptr continut stiva push (&s. Un exemplu de utilizare a unei stive: #include "stiva. pop (&s) ) . prin reutilizarea acestor clase (care trebuie cunoscute. Exemple sunt algoritmi pentru lucrul cu expresii regulate. // scoate din stiva si afiseaza } Modificarea tipului de stivã necesitã un alt fisier “stiva. pentru arhivare-dezarhivare. în spatele cãrora pot sta clase cu implementãri diferite dar cu acelasi mod de utilizare. initSt (&s).a. fie printr-o listã înlãntuitã. // initializare stiva for (x=1. pentru operatii cu anumite structuri de date (arbori binari cu auto-echilibrare). Utilizarea de clase ca module componente ale programelor are o serie de avanaje fatã de utilizarea de functii independente: . unele interne clasei.Florian Moraru – Programare Orientata pe Obiecte In limbajul C un modul de program este o functie. indiferent de implementare: pune date pe stivã. efectul unei metode este fie de a face accesibile date din clasã. // scoate din stiva un element de tip T Definitia tipului “Stiva” si definitiile functiilor depind de implementare.x). fie de a modifica variabile din clasã pe baza argumentelor primite.Se poate realiza un cuplaj mai slab între module. x<10. .h” si o altã bibliotecã de functii (push. prin folosirea unui pointer la o structurã: void initSt ( Stiva * sp).Sunt posibile verificãri de utilizare corectã a unor metode sau asupra succesiunii de apelare a unor functii. astfel de algoritmi fie nu sunt disponibili în C. pop). realizati prin colaborarea mai multor functii. dar operatiile cu stiva sunt aceleasi. // test stiva goala int push (Stiva * sp.h" #define T int void main () { int x. O stivã poate fi realizatã fie printr-un vector. Variabilele unei clase sunt implicit accesibile metodelor clasei si nu mai trebuie transmise explicit. . T x). // pune x pe stiva while ( ! emptySt (&s) ) // cat timp stiva contine ceva printf("%d \n". . dar interne clasei). fie sunt disponibili prin biblioteci de functii destul de greu de utilizat.Metodele unei clase necesitã mai putine argumente. Stiva s . printre care folosirea de interfete Java.Solutii mai simple pentru functii al cãror efect depinde de stare (de context). Existenta unui numãr mare de clase predefinite în Java reduce mult timpul de dezvoltare a unor noi aplicatii. iar aceste argumente nu sunt modificate în functie. prin argumente (ca niste variabile externe metodelor. . cum ar fi de apeluri anterioare ale aceleeasi functii sau ale altor functii pregãtitoare. dar în Java si C++ un modul este o clasã. care reuneste în general mai multe functii în jurul unor date. Primul exemplu se referã la utilizarea structurii de tip stivã (“stack”) în aplicatii. // pune in stiva un element de tip T T pop (Stiva * sp ). de exemplu nu se poate folosi o metodã de scriere pentru un fisier deschis numai pentru citire sau dintr-o clasã ce permite numai citirea de date. In Java acelasi program de exersare a operatiilor cu stiva aratã astfel: 21 .

while (cuv !=NULL) { puts(cuv).println (token). Un al doilea exemplu este cel al extragerii de cuvinte succesive dintr-un sir de caractere ce poate contine mai multe cuvinte.empty()) // cat timp stiva nu e goala System. // d=0 cand nu mi exista un numar corect In Java existã clasa de bibliotecã StringTokenizer. Exemplu: cuv=strtok(str.nextToken(). dar pretul plãtit este modificarea sirului analizat si imposibilitatea de a analiza în paralel mai multe siruri (pentru cã adresa curentã în sirul analizat este o variabilã staticã internã a functiei).out. cuv=strtok(0. astfel cã se pot analiza în paralel mai multe siruri. // creare obiect stiva for (int x=1. primul apel diferã de urmãtoarele apeluri ale functiei.push ( new Integer(x)). In plus. Problema este aceea cã dupã fiecare cuvânt extras se modificã adresa curentã în sirul analizat. deci starea sau contextul în care se executã functia ce dã urmãtorul cuvânt. O situatie asemãnãtoare apare la utilizarea unor functii care compun o interfatã graficã si care trebuie folosite 22 . // “sir” = sir analizat while (st..sep).Florian Moraru – Programare Orientata pe Obiecte public static void main (String arg[ ]) { Stack s = new Stack().\n\t"). // afiseaza obiectul scos din stiva } Modificarea implementãrii stivei. de exemplu pentru a putea scrie într-un fisier. separate prin anumite caractere date. folositã dupã cum urmeazã: String sep = new String (" .hasMoreTokens()) { // daca mai sunt cuvinte in sirul analizat String token = st. In limbajul C se pot întâlni mai multe solutii ale acestei probleme în diferite functii de bibliotecã: Functia “strtok” se foloseste relativ simplu. In plus.println ( s. Metodele “nextToken” si “hasMoreTokens” folosesc în comun o variabilã a clasei care contine pozitia curentã în sirul analizat (initializatã cu adresa sirului. // extrage urmatorul cuvint din linie System. sep).delim). } while (d != 0). Exemplu de utilizare: char * p = str. // afisare cuvint } La crearea unui obiect StringTokenizer se specificã sirul analizat. la construirea obiectului).pop()). prin definirea unei alte clase “Stack” nu necesitã modificãri în functia anterioarã. } // primul cuvânt din “str”. pentru fiecare folosind un alt obiect. // cauta de la adresa p si pune tot in p adresa urmatoare printf ("%lf\n". // s. ci doar punerea noii clase în cãile de cãutare ale compilatorului si interpretorului Java. In prelucrarea fisierelor apar situatii când executia cu succes a unei functii depinde de folosirea anterioarã a altor functii (cu anumite argumente). modificarea tipului datelor puse în stivã necesitã modificãri mai mici în Java decât în C. // lista separatori de cuvinte StringTokenizer st = new StringTokenizer (sir. // str = sir analizat do { d= strtod (p. x<10. sep= sir de separatori // daca s-a gasit un cuvant // afisare cuvant // urmatorul cuvant din “str” Functia “strtod” extrage urmãtorul numãr dintr-un sir si furnizeazã adresa imediat urmãtoare numãrului extras.&p). double d.push(x) in Java 5 while ( ! s.. d). x++) // pune 10 numere in stiva s. acesta trebuie anterior deschis pentru creare sau pentru adãugare (extindere fisier existent).out.

este refolositã si într-o clasã stivã vector. parte imaginarã) pentru un numãr complex: typedef struct { float re. String. fie delegate spre executie metodelor unei alte clase. Astfel de conditionãri reciproce nu se pot verifica automat în C. fiind vorba de functii independente. O componentã JavaBeans poate fi (re)utilizatã fãrã a scrie cod. care va specifica si operatiile permise cu date de acest tip. Multe din clasele de bibliotecã Java pot fi privite ca având drept scop extinderea limbajului cu noi tipuri de date. De exemplu. } Complex . un obiect de tip String contine ca date un vector de caractere si lungimea sa si suportã un numãr mare de metode ce corespund unor operatii uzuale cu siruri (realizate prin functii de bibliotecã în limbajul C). O clasã creeazã un spatiu de nume pentru metodele clasei: pot exista metode cu acelasi nume (si aceleasi argumente) în clase diferite. Character. In Java. Functionalitatea unei clase poate fi reutilizatã în alte clase fie prin derivare. fie prin agregare (compunere). O interfatã contine una sau mai multe operatii (metode abstracte) cu rol bine definit. Pentru fiecare tip de obiecte (comparabile) va exista o altã definitie a functiei “compare”. prin asamblarea de componente prefabricate (în special pentru interfata graficã. utile în mai multe aplicatii. Exemple de metode des folosite din clasa String: 23 . Pachetul de clase (“package”) este o altã unitate Java care creeazã un spatiu de nume pentru clasele continute în el.a. Float. Cel mai simplu exemplu din Java este interfata Comparator. O notiune proprie programãrii cu obiecte este notiunea de componentã software. Short. precum si modul de construire a obiectelor de tip “Complex” (de initializare a pãrtii reale si pãrtii imaginare). dar interfetele cu mai multe metode creeazã posibilitãti inexistente în C. cu o singurã metodã “compare”. fie prin folosirea unei variabile Vector în clasa stivã. Pentru fiecare tip primitiv de date existã un tip clasã asociat: Integer. indiferent de structura colectiei: adãugare obiect la colectie s. introducerea unui tip “Complex” se va face prin definirea unei clase. vom defini o structurã cu douã variabile (parte realã. In POO adaptarea unei clase la cerinte specifice unor aplicatii nu se face prin interventie în codul clasei ci prin derivare sau prin delegare. Ideea este de a obtine rapid un prototip al aplicatiei fãrã a scrie cod sau cu un minim de programare. BigDecimal. prin generarea automatã a operatiilor de instantiere. In acest fel. Exemple: BigInteger. tehnici specifice POO. necesarã uneori dupã adãugarea la vector. O componentã poate contine una sau mai multe clase si poate fi reutilizatã si adaptatã fãrã interventie în codul sursã al componentei (care nici nu este disponibil).Florian Moraru – Programare Orientata pe Obiecte într-o anumitã ordine. toate cu acelasi rol dar cu mod de lucru diferit. float im. prin definirea de tipuri structurã. Clasele creeazã noi tipuri de date In limbajul C. O interfatã cu o singurã metodã corespunde unui pointer la o functie din limbajul C. Long. Double. se pot defini noi tipuri de date ca grupuri de variabile de alte tipuri predefinite. dar nu numai). dar a cãror implementare nu poate fi precizatã. O componentã este de obicei o clasã care respectã anumite conditii. operatiile necesare într-o clasã sunt fie mostenite de la o altã clasã. Interfata Collection defineste câteva operatii ce trebuie sã existe în orice colectie de obiecte. Date etc. De exemplu. Boolean. extinderea automatã a unui vector. Cel mai folosit tip de date definit printr-o clasã este tipul String. în urma unor comenzi date de utilizator unui mediu vizual de dezvoltare a aplicatiilor. In Java operatiile sunt metode dintr-o aceeasi clasã si se poate verifica printro variabilã a clasei succesiunea corectã de folosire a metodelor. fie prin definirea clasei stivã ca o clasã derivatã din vector. pentru compararea a douã obiecte (dupã modelul comparatiei de siruri din C). O interfatã poate fi implementatã de mai multe clase. de modificare a proprietãtilor si de conectare cu alte clase (prin apeluri de metode sau prin evenimente).

// comparatie de siruri cu acelasi rezultat ca “strcmp” (C) char charAt (int i).j<n. trebuie sã redefineascã anumite metode mostenite: “toString”. Exemplu de functie staticã care foloseste metodele “indexOf”.int j) { return a[i][j]. In Java orice clasã este automat derivatã dintr-o clasã genericã Object si.indexOf(s1). // p = pozitia primei aparitiii a lui s1 in s while ( p >=0 ) { // indexOf are rezultat –1 daca s nu contine pe s1 s = s. p+s2. // comparatie la egalitate de siruri int compareTo (String s). “length” si “substring” din clasa String // inlocuire repetata in sirul s a subsirului s1 prin sirul s2 public static String replaceAll (String s. // lungimea acestui sir (pentru care se apeleazã metoda) boolean equals (String s).indexOf(s1.j++) s=s + ( a[i][j]==true ? "1 ": "0 ") .a.Florian Moraru – Programare Orientata pe Obiecte int length (). // extrage subsirul dintre pozitiile ‘i’ si ‘j’ De observat cã operatiile de comparatie (si alte metode) au un singur argument desi comparã douã obiecte sir: obiectul pentru care se executã metoda si obiectul primit ca argument. // are ca rezultat sirul modificat prin inlocuire } Utilizatorii îsi pot defini propriile clase. “equals” s. de functii. Exemplu de clasã minimalã pentru o matrice de biti: // matrice cu elemente de tip boolean public class BoolMatrix { private boolean a[ ][ ]. // concatenare subsiruri p=s.length()). // indicele (pozitia) caracterului ‘c’ in acest sir int indexOf (String s).n=n. pentru tipuri de date necesare aplicatiilor. String s2) { int p = s.i++) { for (int j=0. // extrage din acest sir subsirul care incepe in pozitia ‘i’ String substring(int i. // nr de linii si coloane // constructor de obiecte public BoolMatrix (int n) { this. } // sir cu elementele din matrice public String toString () { String s="". de exemplu.p)+ s2 + s. boolean b) { a[i][j]=b. // indicele (pozitia) sirului ‘s’ in acest sir (prima aparitie) String substring(int i).int j. de argumente de functii. // urmatoarea aparitie a lui s1 in s } return s.length()).substring(p+s1. // caracterul din pozitia ‘i’ a acestui sir int indexOf (char c). } // citire valoare element public boolean getElement (int i. Vom citi un apel de metodã de forma a. ca urmare. Noile tipuri de date se pot folosi în declaratii de variabile.substring(0. // o matrice patratica private int n. s=s+"\n". putem defini o clasã “BoolMatrix” pentru o matrice cu elemente de tip boolean. for (int i=0.i<n. // aloca memorie ptr matrice } // modifica valoare element public void setElement (int i. } 24 . int j). a= new boolean[n][n].compareTo(b) astfel: comparã acest obiect (‘a’) cu obiectul ‘b’. String s1.

i++) mat.toString()). Se mai spune cã datele sunt ascunse sau sunt încapsulate în fiecare obiect. indiferent de tipul datelor care vor fi memorate în vector (în listã). Pentru a permite colectii 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. In Java nu se pot supradefini operatori. Tot programarea genericã ne permite sã folosim o singurã functie (metodã) pentru a parcurge elementele oricãrei colectii (indiferent de structura ei fizicã ) sau pentru a ordona orice listã abstractã (o colectie care suportã acces direct prin indice la orice element din colectie).true). orice modificare a vectorului de caractere dintr-un obiect StringBuffer (StringBuilder) este însotitã de modificarea lungimii sirului (în metodele care pot modifica lungimea sirului). iar colectiile cu tipuri de date ca parametri au apãrut din versiunea 5 si sunt numite generice (“generics”). } // exemplu de utilizare public static void main (String arg[]) { BoolMatrix mat = new BoolMatrix(4). clasa D fiind derivatã din clasa B.prin colectii ce contin un supertip al tuturor tipurilor clasã (tipul Object în Java). De exemplu. System. Clasele permit programarea genericã Programarea genericã ne permite sã avem o singurã clasã pentru un vector (sau pentru o listã). Variabilele dintr-o clasã sunt declarate de obicei cu atributul private. asa cum tipul int poate fi considerat ca un subtip al tipului long.i<4. cu argumente de un tip general si utilizabile cu o multitudine de tipuri de argumente (asa cum functia “sqrt” se poate apela cu argument de orice tip numeric din C).out. 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.i.println (mat). dar lungimea nu poate fi modificatã direct de cãtre functii din alte clase (si nici continutul vectorului de caractere).prin clase ce au ca parametri tipurile de date folosite (numite “templates” în C++).println ( mat. Deoarece datele dintr-un obiect (variabile private) nu sunt direct accesibile din afara clasei si pot fi modificate numai prin intermediul metodelor clasei. adãugarea la vector a unui pointer oarecare nu necesitã o conversie. tot asa un argument formal de un tip clasã B poate fi înlocuit cu un argument efectiv de un tip clasã D. Tipul unei clase derivate este subtip al tipului clasei din care derivã.setElement (i. deci operatiile cu noile tipuri de date se pot exprima numai prin metode asociate obiectelor (metode nestatice). pentru cã acestor variabile li se pot atribui variabile de orice alt tip clasã (care contin adresele unor obiecte). dar la extragerea din vector trebuie trecut de la tipul void* la tipul de pointer folosit la adãugare (prin operatorul de conversie de tip “cast”). . La fel cum un argument formal de tip double poate fi înlocuit cu un argument efectiv de tip int. ceea ce le face inaccesibile pentru metode din alte clase. Colectiile cu elemente de tip Object au existat de la început în Java.Florian Moraru – Programare Orientata pe Obiecte return s. In felul acesta se pot scrie functii generice. O colectie de variabile de tip Object poate contine referinte la obiecte de orice tip. Tipurile de date definite prin clase pot forma ierarhii de tipuri compatibile (care se pot înlocui prin atribuire sau la transmitere de argumente). Metodele clasei sunt de obicei publice pentru a putea fi apelate din alte clase. Genericitatea colectiilor de obiecte poate fi realizatã în limbajele cu clase prin douã metode: . care este rãdãcina ierarhiei de clase Java. utilizarea tipurilor de date definite prin clase este mai sigurã decât a celor definite prin structuri. for (int i=0. } } // sau System. iar tipul float ca un subtip al tipului double. 25 .out.

Float. substantivele din 26 . k<11. dar conversia în jos (de la Object la Integer. O colectie Java genericã specificã tipul obiectelor continute încã de la declarare (care poate fi orice subtip de Object sau de alt tip).k<v. for (int k=0.3… 10 v.k<v.k++) { Integer obj = (Integer) v. pentru rezultatul metodei “get”) trebuie cerutã în mod explicit prin operatorul de conversie ( ca si în C). // creare obiect vector de Integer for (int k=1. // adauga numarul k la vector ca obiect de tip Integer int sum=0. Character) este trecerea automatã de la tipul primitiv la tipul clasã corespunzãtor (“autoboxing”) si trecerea inversã (“unboxing”). O altã simplificare privind utilizarea colectiilor generice de tipuri ce corespund tipurilor primitive (Byte. // conversie automata de la “Integer” la “int” Clasele creeazã un model pentru universul aplicatiei Un program destinat unei aplicatii trebuie sã transforme notiunile si actiunile specifice aplicatiei în constructii specifice limbajului de programare folosit (functii. Evolutia limbajelor de programare poate fi privitã si ca un progres al abstractizãrii.add ( new Integer(k)). for (int k=0.). // conversie automata de la “int” la “Integer” int sum=0.3… 10 v.intValue().2. în sensul îndepãrtãrii progresive de masina fizicã prin introducerea de notiuni tot mai abstracte. Intr-un program Java nu existã altceva decât obiecte si clase.add ( new Integer(k)). k<11. Analiza orientatã pe obiecte poate începe cu o descriere în limbaj natural a ceea ce trebuie sã facã programul.3… 10 v. k++) // genereaza numerele 1.intValue(). k++) // genereaza numerele 1. Identificarea obiectelor si actiunilor specifice unei aplicatii se face în faza de analizã orientatã obiect a problemei de rezolvat si implicã o abordare diferitã de cea anterioarã.k++) sum += v.k++) sum += v. Double. Boolean.2. variabile. argumente de functii. // aduna numarul de tip “int” din obiectul “obj” } Conversia în sus de la subtipul Integer la supertipul Object se face automat (argumentul metodei “add” este de tip Object).k<v.Florian Moraru – Programare Orientata pe Obiecte Exemplu de calcul a sumei valorilor unor obiecte numerice dintr-un vector: Vector v = new Vector(). Acelasi exemplu dinainte cu colectii generice: Vector <Integer> v = new Vector<Integer>(). // adauga numarul k la vector ca obiect de tip Integer int sum=0.get(k). k<11.add ( k). k++) // genereaza numerele 1. Integer. // creare obiect vector de Integer for (int k=1.size(). iar la extragere nu mai trebuie fãcutã nici o conversie de tip.size(). etc. Short. Exemplu reluat: Vector <Integer> v = new Vector<Integer>(). // extrage din vector si conversie din Object în Integer sum += obj. Fiecare limbaj de programare oferã programatorilor o masinã virtualã (sau abstractã ) diferitã de masina concretã pe care se vor executa programele lor. // creare obiect vector (extensibil) for (int k=1. for (int k=0. Programarea orientatã pe obiecte permite definirea de clase si obiecte ce corespund direct obiectelor din universul aplicatiei si modelarea relatiilor statice si dinamice dintre aceste obiecte.size().get(k). Un program Java poate fi privit ca o descriere a unor obiecte si a interactiunilor dintre aceste obiecte.2.get(k).

Vectori intrinseci si obiecte vector In Java coexistã vectorii mosteniti din limbajul C (ca tip de date predefinit si recunoscut de compilator) cu obiecte din clasele de bibliotecã Vector sau ArrayList. Intr-o aplicatie bancarã vor exista clase si obiecte de genul “Account” (cont bancar) si “Customer” (client al bãncii).6 se poate folosi una din metodele statice “copyOf” din clasa Arrays pentru realocare vectori intrinseci. pentru retragerea de bani din cont si pentru vizualizarea sumei de bani din cont. public Array (Object a[]){ this. iar verbele din text corespund unor metode.iterator(). Fiecare solutie de grupare a unor date are avantaje si dezavantaje si de aceea unele metode din clase de bibliotecã au ca rezultat sau primesc vectori intrinseci iar altele obiecte Vector. obiectele vizuale afisate pe ecran si care pot fi selectate sau actionate de operator (ferestre.Metodele “list” si “listFiles” din clasa File produc un vector cu toate fisierele dintr-un director dat: File[] listFiles(). } public void add (Object ob) { a=Arrays. pentru jocuri. Se recomandã atunci când vectorul nu îsi modificã dimensiunea initialã sau dimensiunea se modificã mai rar. Din versiunea 1. accesul la elementele vectorului este mai rapid si nu necesitã conversie prin “cast”.a. precum si metode pentru depunerea de bani în cont. a[a. Un vector intrinsec ocupã mai putinã memorie. butoane.length-1]=ob. ca în cazul obiectelor Vector.copyOf(a.a=a. O astfel de abordare este potrivitã pentru aplicatii grafice. cu colectii de obiecte. casete cu text.length+1). Variabila publicã length are ca valoare dimensiunea alocatã pentru un vector intrinsec (numitã si “capacitate”). . Redimensionarea unui vector intrinsec se face printr-o nouã alocare de memorie si nu prin modificarea variabilei length (care are atributul final). Exemplu: class Array { private Object a[]. pentru aplicatii bancare s. s.a. } } Obiectele din clasa Vector asigurã extinderea automatã a unui vector (realocarea dinamicã pentru mãrirea capacitãtii) la operatii de adãugare si au o multime de metode pentru operatii uzuale cu 27 . Aplicatiile mai pot contine obiecte ajutãtoare (“helper”) sau obiecte din clase predefinite pentru operatii cu anumite tipuri de date.) corespund direct unor obiecte din programele Java.asList (Object[]) transformã un vector intrinsec într-o colectie (de tip List). Obiectele de tipul “Account” sau “Customer” se numesc si obiecte din domeniul aplicatiei (“domain objects”). meniuri. metoda staticã Arrays. Multe aplicatii folosesc o interfatã graficã cu utilizatorii (operatorii) aplicatiei. cu conexiuni între calculatoare s. Un obiect de tip “Account” va contine suma din cont (si alte date asupra operatiilor cu acel cont). Un obiect “Customer” va contine date de identificare ale clientului si metode pentru obtinerea sau modificarea acestor date. Orice clasã colectie (deci si clasa Vector) contine o metodã “toArray” care produce un vector intrinsec cu obiectele din colectie.Florian Moraru – Programare Orientata pe Obiecte acest text corespund în general unor obiecte (clase). Intr-un astfel de vector se pot memora si date de un tip primitiv.a.asList(a). Exemple: . cu baze de date.Metoda “split” din clasa String produce un vector cu toate cuvintele dintr-un sir dat: String[] split (String regexp).a. } public Iterator iterator() { return Arrays.

// adauga obj la sfarsitul acestui vector void addElement (Object obj).hasMoreTokens()) a[i++]=tok.toString()). Exemplu de functie similarã functiei “split”. // prima pozitie de dupa i unde se afla obiectul obj int lastIndexOf (Object obj).out.toString (w)). eliminarea de elemente. dar fãrã expresii regulate. // inlocuieste elementull din pozitia i cu obj Object[] toArray(). // produce un sir cu elementele din vector. // adauga obj la sfarsitul acestui vector void insertElementAt (Object obj. int i). care are aceeasi functionalitate cu clasa Vector. int i).out. // dimensiunea acestui vector (nr de elemente) boolean contains (Object obj). // produce un vector intrinsec cu aceleasi elemente String toString(). return a.a. while ( tok.println (w. // ultima pozitie din acest vector unde se afla obj Object elementAt (int i). // prima pozitie unde se afla obiectul obj sau –1 int indexOf (Object obj. plus variabile pentru capacitatea alocatã si pentru dimensiunea efectivã a vectorului.println(w). Metoda “println” cu argument Object foloseste automat metoda “toString” pentru obiectul respectiv: System. int i=0.out. } } Afisarea continutului unui obiect Vector (ca si afisarea continutului oricãrei colectii) este simplificatã de existenta metodei “toString”. cãutarea unui obiect dat. System. 28 . // daca acest vector contine obiectul obj int indexOf (Object obj). // obiectul din pozitia i din acest vector boolean add (Object obj). // obiectul din pozitia i din acest vector Object get(int i). // introduce obj in pozitia i din vector boolean remove (Object obj). folosind ceea ce se numeste un vector generic. String[] a = new String [tok.println (Arrays. ca si pentru orice altã clasã colectie.Florian Moraru – Programare Orientata pe Obiecte vectori: obtinerea numãrului de elemente. // elimina elementul cu valoarea obj din vector Object remove (int i).nextToken(). pentru extragere de cuvinte dintr-un sir (cuvintele sunt separate între ele printr-un numãr oarecare de spatii albe): class Unu { // cu rezultat vector intrinsec public static String[] split(String s) { StringTokenizer tok = new StringTokenizer(s). Clasa Vector contine un vector intrinsec de elemente de tip Object. // elimina elementul din pozitia i din acest vector void setElementAt (Object obj.countTokens()]. } // utilizare functie public static void main (String arg[]) { String s = “ unu doi trei patru cinci “. int i). Se recomandã pentru vectori de obiecte cu continut variabil (în decursul executiei programului). Incepând din versiunea 5 se poate preciza tipul elementelor din vector. ca siruri Existenta unor metode diferite cu acelasi efect se explicã prin faptul cã din versiunea 2 clasa Vector a fost modernizatã pentru compatibilizare cu interfata List si a fost adãugatã clasa ArrayList. Exemple de metode utilizate frecvent: int size( ). s. // la fel cu: System. String w[] = split(s). pentru clasa Vector.

// afisare cu format Scanner sc=Scanner. Câteva clase Java nu sunt altceva decât grupãri de functii statice relativ independente. Metodele statice sunt în general si publice.K. Metode statice. pentru a putea fi apelate din orice altã clasã. Exemplu: class A { static String msg = "O.Metode statice.out. Exemple: System. Din versiunea 1. // metoda ptr citirea sirului urmator int value=sc.intValue (). int x = getint (a). total).println (msg).next(). Metodele Java sunt de douã categorii: .nextInt().create(System. // apel metodã nestatica } // exemplu de utilizare pentru conversie de la String la int Integer a = new Integer (str). O metodã staticã se poate referi numai la variabile statice din clase (variabile definite în afara functiilor si cu atributul static). // metoda ptr citirea urmatorului numar intreg Pentru functiile matematice nu existã altã posibilitate de definire decât ca metode statice. Utilizarea de clase si obiecte Clase fãrã obiecte. // sau x = new Integer(str).Florian Moraru – Programare Orientata pe Obiecte 3. Exemple: // o metoda statica pentru conversie numar din Integer in int public static int getInt (Integer a) { return a. // functie statica din clasa Integer double r = Math. deoarece ele primesc un parametru de un tip primitiv (double). } } In Java se citesc din fisiere sau se preiau din linia de comandã siruri de caractere.out.parseInt (args[0]). name. In felul acesta putem avea functii cu acelasi nume în clase diferite si se reduce posibilitatea unui conflict de nume. // functie statica din clasa Math System. dar poate fi folositã numai precedatã de numele clasei. utilizabile independent de obiecte (“Class Methods”) O metodã staticã Java corespunde unei functii din limbajul C.printf ("%s %5d\n". Exemplu de utilizare a unor metode statice: // afisare radical dintr-un numar primit in linia de comanda class Sqrt { public static void main (String args[ ]) { int x = Integer.Metode aplicabile obiectelor (“Object Methods”) .println (r). deci nu pot genera obiecte.out.in).sqrt (x).5 au fost introduse în Java metode similare functiilor "scanf" si "printf" din C. } } 29 . iar analiza lor si conversia în format intern pentru numere se face explicit de cãtre programator. // "static" este necesar aici public static void main (String arg[ ]) { System.intValue(). Aceste clase nu sunt instantiabile. Metodele statice au un parametru în plus fatã de metodele nestatice cu acelasi efect.". // citire cu format String param= sc. Pentru metodele cu un operand de un tip clasã avem de ales între o functie ne-staticã si o functie staticã.

println (sp+d. de obicei cu operatorul new. int y = zero.Florian Moraru – Programare Orientata pe Obiecte Metoda “main” este staticã si de aceea variabilele referite si metodele apelate direct trebuie sã fie statice (adicã sã nu fie conditionate de existenta unor obiecte). In practicã functia “main” creeazã unul sau câteva obiecte si apeleazã una sau câteva metode pentru obiectele create (de obicei “main” contine putine instructiuni). care coboarã recursiv în fiecare subdirector întâlnit (metoda “listFiles” din clasa File nu furnizeazã decât fisierele si subdirectoarele de pe primul nivel): static void printFiles (File d. // conversie din int in String static boolean Character. sp+" "). Toate obiectele sunt create dinamic.out.parseInt (String s). Integer zero = new Integer(0). desi nu este imposibil sã avem si metode nestatice recursive. Metode aplicabile obiectelor Specific programãrii orientate pe obiecte este utilizarea de clase care contin si date si care pot genera obiecte. // daca nu e subdirector.println(st.isDigit (char ch). Intr-un program Java se creeazã obiecte si se apeleazã metode ale acestor obiecte. In Java metodele statice sunt putine. // un sir cu mai multe cuvinte StrTok st = new StrTok(str). int x = Integer. String sp) { // d= director. // daca ch este cifra static boolean Character. Exemple: String s=”-123”. Multe functii recursive se scriu în Java ca metode statice. // conversie din String in Integer static int Integer. Un obiect este un caz particular concret al unei clase. sp=spatii ptr indentare System. Un obiect String poate fi creat pe baza unui vector intrinsec de caractere sau de 30 .i++) // pentru fiecare fisier din d printFiles (files[i]. se iese File [] files=d. Una din cele mai folosite clase în Java este clasa String. // creare obiect folosit la impartirea in cuvinte while ( st. // conversie din String in int static String String.out.valueOf (String s). iar variabile statice sunt în general constantele simbolice. deci o “instantiere” a unei clase.valueOf (int i). O astfel de clasã poate fi privitã ca un sablon pentru crearea de obiecte care au în comun aceleasi operatii (metode) dar contin date diferite. // afiseaza urmatorul cuvant } Metoda “main” de mai sus poate fi inclusã în clasa verificatã StrTok sau poate fi în altã clasã.nextToken ()).listFiles(). ca în exemplele urmãtoare: static Integer Integer. Metode statice pot fi întâlnite si în clase cu majoritatea metodelor nestatice.hasMoreTokens()) // cat timp mai sunt cuvinte System.i<files.getName()). // vector de fisiere din directorul d for (int i=0. Pentru exemplificare vom defini o functie care sã afiseze fisierele dintr-un director dat si din subdirectoarele sale.length.isDirectory()) return.parseInt(s). // daca ch este litera O metodã staticã poate fi precedatã de numele clasei sau de numele unei variabile de tipul clasei din care face parte (caz în care se apeleazã la fel ca o metodã nestaticã).parseInt(s). // afiseaza nume director primit if ( ! d. // apel recursiv. Exemplu de verificare a clasei “StrTok” definite anterior: public static void main (String[] arg) { String str = " unu doi trei ". care poate genera obiecte ce contin fiecare un sir de caractere.isLetter (char ch). cu alta indentare } Clase instantiabile.

31 .append (“x=”). for (int i=0. pentru comparatie de siruri si pentru anumite operatii de modificare a unui sir. Ordinea de evaluare pentru operatorul punct este de la stânga la dreapta (ca în C). i<n. // creare sir cu un nume de fisier // pozitia primului punct din nume // creare sir cu extensia numelui In exemplul de mai sus.i++) count[i] = zero. sb.indexOf(‘. Pentru utilizarea mai eficientã a memoriei si pentru reducerea timpului de executie se recomandã sã nu se creeze obiecte inutile. In Java obiectele sunt create dinamic. Este posibilã si apelarea directã a colectorului de resturi de memorie (“garbage collector”). memoria ocupatã de un obiect este eliberatã atunci când obiectul respectiv devine inaccesibil si inutilizabil.append(y). aplicatã aceluiasi obiect are ca rezultat un alt obiect. . dar momentul exact al recuperãrii memoriei nu poate fi precizat si depinde de modul de gestiune a memoriei dinamice. memorat la adresa din variabila "fext". // un obiect de tip Integer // un vector cu elem.out. // citeste linie."r"). Exemple: String line = f. String line=null. Exemple: String fname = new String (“test.java “).append(x). Pentru o scriere mai compactã se practicã uneori înlãntuirea de metode (“method chaining”).substring (p+1). In Java gestiunea memoriei dinamice este automatã iar programatorul nu trebuie sã aibã grija eliberãrii memoriei alocate pentru obiecte. // nu: line = new String().toLowerCase(). elimina spatii si trece in litere mari if ( fname. int p = fname. de aceea este posibil sã înlãntuim mai multe apeluri ale metodei “append” astfel: StringBuilder sb = new StringBuilder(). Exemplu: RandomAccessFile f = new RandomAccessFile ("date. Metoda “append” din clasele StringBuffer si StringBuilder au ca rezultat un obiect de acelasi tip cu obiectul pentru care se aplicã metoda (este sirul rezultat prin adãugarea altor caractere sirului initial). metoda "indexOf" se aplicã obiectului cu adresa în variabila "fname" si are ca rezultat un întreg.append (“ y=”). De exemplu. într-o aceeasi expresie. de tip Integer // nu: count[i]= new Integer(0).substring(fname.. line = f. adicã aplicarea unei metode asupra rezultatului unei alte metode.trim().readLine().println (sb).toUpperCase(). folosind direct operatorul new (sau apelând o metodã “fabricã” de obiecte).Florian Moraru – Programare Orientata pe Obiecte octeti sau pe baza altui obiect String sau StringBuffer( StringBuilder)... la executie. Integer count[ ]= new Integer [n]. String fext = fname. Adresa unui obiect se memoreazã într-o variabilã referintã de tipul clasei cãreia apartine obiectul. pentru extragere de subsiruri.txt". Teoretic. System. iar metoda "substring". Variabilele de un tip clasã reprezintã singura posibilitate de acces la obiectele Java si ele corespund variabilelor referintã din C++. o variabilã de un tip clasã care va primi ulterior rezultatul unei functii nu va fi initializatã la declarare cu altceva decât cu constanta null.indexOf (‘.’).’)+1).readLine().. // citeste o linie din fisierul f O altã situatie este cea în care un vector de obiecte trebuie initializat cu un acelasi obiect (de fapt cu o referintã la un obiect unic): Integer zero = new Integer (0).equals(“java”) ) . dar nu se practicã decât foarte rar. atunci când existã alte posibilitãti. Clasa String contine metode pentru cãutarea într-un sir.

public static void main (String arg[]) { System. float med) { // constructor al clasei Elev nume = unnume. Float medie. Exemplu: class Elev { String nume. } . // metode ale clasei Elev } Variabile referintã la un tip clasã Variabilele Java pot fi: variabile de un tip primitiv (char. // utilizare variabilã neinitializatã Variabilele declarate în functii nu sunt initializate de compilator.out. int. System. // tab[i]=null in mod implicit // NullPointerException 32 . medie = new Float (med). int n=tab[0].out. dar variabilele declarate în afara functiilor sunt initializate automat cu zero sau cu null. de obicei este suficientã retinerea adresei obiectului primit ca parametru de constructor si nu trebuie creat un nou obiect.println (v. Secventa urmãtoare va provoca o eroare la compilare : String nume. Exemplu: String tab[ ] = new String[10]. . // nu: nume = new String(unnume). Variabila care primeste rezultatul operatorului new nu contine chiar obiectul ci este o referintã la obiectul creat. Nu se pot defini referinte la tipuri primitive sau parametri referintã de un tip primitiv.println (nume). float. } } // NullPointerException Pentru elementele unui vector intrinsec compilatorul nu poate stabili dacã au fost sau nu initializate si se produce exceptie la executie. fãrã a se folosit un operator . Exemplu de eroare care nu este semnalatã la compilare si produce o exceptie la executie: class Eroare { static Vector v. O referintã la un tip T este de fapt un pointer la tipul T care se foloseste ca si cum ar fi o variabilã de tipul T. care sunt variabile referintã.length().Florian Moraru – Programare Orientata pe Obiecte O a treia situatie frecventã este la construirea unui obiect pe baza altor obiecte.size()). Referinta Obiect Simpla declarare a unei variabile de un tip clasã nu antreneazã automat crearea unui obiect. Indirectarea prin variabila referintã este realizatã automat de compilator. boolean etc) sau variabile de un tip clasã (sau de un tip vector). // datele clasei public Elev ( String unnume. Compilatorul Java verificã si anuntã utilizarea unei variabile care nu a fost initializatã. .

Clasele JDK care contin variabile de un tip referintã au metoda “clone” redefinitã pentru a face o copiere “profundã” a datelor din obiectul clonat în obiectul clonã. c) Folosind metoda generalã “clone” (mostenitã de la clasa Object).append (”doi”). Copierea este superficialã în sensul cã se copiazã variabile referintã (pointeri) si nu datele la care se referã acele variabile. // “linie” de tip String Metoda “equals” existã în orice clasã Java iar metoda “compareTo” (cu rezultat “int”) existã numai în clasele cu obiecte “comparabile”. Efectul atribuirii între douã variabile referintã este copierea unei adrese si nu copierea unui obiect. Operatorul de atribuire se poate folosi numai între variabile referintã de acelasi tip sau pentru atribuirea constantei null la orice variabilã referintã. int len = s.equals (“. s1=new StringBuffer (“unu”). b) Prin construirea unui nou obiect care preia datele din vechiul obiect (daca existã metode de extragere a datelor dintr-un obiect). Exemplu: String s=null. String s2 = new String (s1).out. // referinte la doi vectori // creare b si copiere date din a în b In metoda “clone” se alocã memorie pentru un nou obiect si se copiazã datele din obiectul vechi în noul obiect ( rezultat al metodei “clone”). Exemplu: Integer m1 = new Integer(1). // adresa sirului “unu” s2. b.”) break. Exemplu: Vector a. Atribuirea între variabile referintã duce la multiplicarea referintelor cãtre un acelasi obiect. Float. System. Integer. Integer m2 = new Integer ( m1. Date etc. b= (Vector) a.readLine(). // citeste o linie din fisierul f // incorect.. if (linie == “. .println (s1).clone().”)) break. // scrie: unudoi s1 unud s2 Copierea datelor dintr-un obiect într-un alt obiect de acelasi tip se poate face: a) Prin construirea unui nou obiect pe baza unui obiect existent (dacã existã constructor): String s1 = “unu”.. s2=s1. se compara adrese ! Comparatia la egalitate între obiecte Java se face prin metoda "equals" (de tip boolean).intValue()). // exceptie ! 33 .Exemplu : if (linie.Florian Moraru – Programare Orientata pe Obiecte Utilizarea operatorului de comparatie la egalitate "==" între douã variabile referintã are ca efect compararea adreselor continute în cele douã variabile si nu compararea datelor adresate de aceste variabile.length(). Un rezultat nedorit este aparitia unor obiecte cu date comune. Incercarea a utiliza o variabilã referintã cu valoarea null pentru apelarea unei metode cauzeazã exceptia NullPointerException. dar numai pentru obiecte din clase care implementeazã interfata Clonable. cum sunt clasele String. Exemplu de eroare: String linie=f. Exemplu: StringBuffer s2.

iar adresa sa este memoratã în variabila localã “t” (care continea initial adresa sirului dat). a[1]="doi". Argumentele de un tip primitiv nu pot fi modificate de o metodã Java. dar acest lucru nici nu este necesar. Exemplu de functie fãrã efect în afara ei: // metoda statica pentru trecere sir in litere mari . O functie nu poate trasmite în afarã adresa unui obiect creat în functie printr-un parametru referintã. Arrays. Un obiect creat într-o functie trebuie transmis ca rezultat al functiei. // se creeaza un nou obiect.toUpperCase(). iar efectul metodei este modificarea variabilelor clasei si nu modificarea argumentelor.toUpperCase(). cum ar fi transmiterea unui sir la construirea unui obiect de tip StrTok (sau StringTokenizer). iar prelucrarea unui vector completat partial poate produce exceptia de utilizare a unui pointer (referintã) cu valoarea null. care sã permitã modificarea oricãrui vector. b = (Object[]) a. Argumente de functii de tip referintã In Java. adicã se copiazã valoarea parametrului efectiv în parametrul formal corespunzãtor. Constructorul StrTok retine o referintã cãtre sirul de analizat si nu face o copie a acestui sir.sort(a). Exemplu: Object [ ] b = new Object[10]. // 10 valori null a[0]="unu".gresit !!! static void toUpper (String t) { t = t. transmiterea unui parametru efectiv la apelarea unei functii se face prin valoare. Argumentele unei metode sunt de obicei date initiale. } 34 . înainte de executia instructiunilor din functie. retinând în vectorul clonã numai elementele nenule. Un exemplu este un vector de vectori. pentru cã nu modificã sirul primit. Prin copierea adresei unui obiect în parametrul formal corespunzãtor apar referinte multiple la un acelasi obiect (multiplicarea referintelor). Intr-un vector sau într-o altã colectie se memoreazã referinte cãtre obiecte si nu cãtre clone ale obiectelor.Florian Moraru – Programare Orientata pe Obiecte Elementele unui vector de obiecte sunt initializate automat cu null la alocarea de memorie. ca si a obiectelor memorate în acesti vectori. a[2]="trei". Situatia este similarã unui vector de pointeri din limbajul C (cãtre siruri sau cãtre diferite structuri alocate dinamic): nu se cloneazã structurile sau sirurile.sort (b). deci o metodã nu poate transmite mai multe rezultate de un tip primitiv. Exemplu: Object a[] = new Object [10] . // exceptie aruncata de functia “sort” Metoda “clone” de copiere a unui vector intrinsec nu copiazã si valorile null. ca si în C. pentru a permite modificarea obiectelor din colectie. O situatie în care pare necesarã clonarea este la transmiterea de obiecte pentru construirea altor obiecte. Exemplu: static String toUpper (String s) { return s. cu alta adresa } Aici se creeazã un obiect String prin metoda “toUpperCase”. iar adresele lor sunt memorate în vector).clone(). // nu produce exceptie Clonarea de obiecte este o operatie folositã destul de rar în practica programãrii. In cazul parametrilor de un tip clasã (parametri referintã) se copiazã adresa obiectului din functia apelantã în parametrul formal si nu se copiazã efectiv obiectul. Arrays.

s2). minus (aux.xor(c). // c= a*b dar s-a modificat si a ! d. Exemplu de utilizare gresitã a metodei “replaceAll” din clasa String pentru înlocuirea unui subsir s1 cu un alt subsir s2 în sirul s: s. d=new BitSet(). de aceea utilizarea corectã este urmãtoarea: s = s. Avantajele si riscurile obiectelor modificabile trasmise ca argumente pot fi ilustrate prin clasa BitSet. // atunci se face a[i]=0 } Dacã vrem sã pãstrãm multimea “a” atunci vom transmite o copie a ei BitSet aux= (BitSet) a.and(b).i++) if (b.a.clear(i). d. Functia modificã în mod nedorit multimile primite ca argumente din cauza multiplicãrii referintelor. Diferenta a douã multimi A-B poate fi realizatã prin functia urmãtoare: public static void minus (BitSet a. // c . // c. // d=b-a return d.s2).get(i)) // daca b[i] este 1 a. nu contin metode pentru modificarea datelor din aceste clase.and(b). d si a se refera la acelasi obiect c. In acest fel se protejeazã obiectul transmis ca argument fatã de modificarea sa nedoritã de cãtre functia care îl foloseste.b). deci nu se poate extinde cu metode de modificare a vectorului de caractere continut în fiecare obiect.i<b. Obiectele din clase fãrã metode de modificare a datelor (clase “read-only”) se numesc obiecte nemodificabile (“immutable objects”).or(a). public static BitSet minus (BitSet a. deci o functie care primeste o referintã la un astfel de obiect nu poate modifica acel obiect. d=a. BitSet b) { for (int i=0. Clasa String este o clasã read-only si finalã. c.or(a). // c=a*b d.clone().replaceAll (s1.Florian Moraru – Programare Orientata pe Obiecte O functie poate modifica un obiect a cãrui adresã o primeste ca argument numai dacã în clasa respectivã existã metode pentru modificarea obiectelor. Integer. // copiaza pe a in aux // aux = aux-b In exemplul urmãtor diferenta A-B se obtine pe baza unor operatii existente: A-B = A / (A*B) unde A/B este diferenta simetricã a multimilor A si B. // d este multimea vidã ! } Urmeazã o variantã corectã pentru calculul diferentei a douã multimi de tip BitSet: public static BitSet minus (BitSet a. BitSet b) { BitSet c=a. BitSet b) { BitSet c=new BitSet(). Float s. Metodele “replaceAll” si “replace” au ca rezultat un nou sir obtinut dupã substituire si nu modificã obiectul pentru care se apeleazã.xor(c).d multimi initial vide c. pentru multimi de întregi realizate ca vectori de biti.replaceAll (s1. // d= d / c dar s-a modificat si a ! return d. } Clase cu obiecte nemodificabile Clasele String. 35 .size().

setLength.i<n-1.length().str.sqrt(2). Dacã trebuie sã facem multe concatenãri de siruri este preferabil ca timp sã se foloseascã direct metoda “append” din clasa StringBuilder (StringBuffer).out.toUpperCase()).Exemplu: System. existã în clasa Arrays metoda staticã “toString” cu argument vector. Exemplu: public static String arrayToString ( int a[ ]) { // creare sir cu continutul unui vector StringBuilder aux = new StringBuilder (”[”). insert. se apeleazã metoda “append” si apoi creeazã un obiect String din obiectul StringBuffer rezultat din concatenare: String a=“unu”. Un obiect de tipul StringBuffer transmis unei functii ca argument poate fi modificat de cãtre functie. StringBuffer am= new StringBuffer (a). mai nou. a= new String (am). am. } De observat cã. } Clasa StringBuilder este mai performantã decât StringBuffer pentru programele fãrã fire de executie multiple.replace (0. delete. b=“doi”. Exemplu: float x = (float) Math. // sau str = String. Variantã pentru functia “toUpper”: static void toUpper (StringBuffer s) { String str= new String (s).str.i++) aux.append (a[n-1] +”]”) ) . int n =a. Concatenarea de siruri este o operatie frecventã în Java.println ( “x= “ + x).Florian Moraru – Programare Orientata pe Obiecte Clasele StringBuffer si StringBuilder (din 1. Metoda “println” folositã pentru afisarea pe ecran poate avea un singur argument de tip String. Aceastã observatie poate fi folositã si pentru conversia unui numãr în sir de caractere. Eliminarea unui subsir dintr-un sir se poate face folosind metoda “delete” din clasa StringBuffer sau cu metode ale clasei String. setCharAt.append (a[i] + ”. dacã unul din operanzi este de tip String. String str = ““+x. // x de orice tip Intr-o expresie cu operatorul binar ‘+’. atunci compilatorul Java face automat conversia celuilalt operand la tipul String (pentru orice tip primitiv si pentru orice tip clasã care redefineste metoda “toString”).length() ) return s. O instructiune de forma a=a+b. s. Exemple de metode care modificã sirul continut întrun obiect de tip StringBuffer: append. Exemplu: static String delete1 (String s. // s nemodificat ! 36 . return new String (aux. ca alternativã a utilizãrii metodei “valueOf” din clasa String. int to ) { // sterge din s intre pozitiile “from” si “to” if ( from > to || from < 0 || to > s. Pentru a scrie mai multe siruri acestea se concateneazã într-un singur sir cu operatorul ‘+’.valueOf(x).5.”). int from. dar existã numai din versiunea 1.length. for (int i=0.5) sunt variante ale clasei String care contin în plus si metode pentru modificarea obiectului (sirului). cu “a” si “b” de tip String este tratatã de compilator astfel: se transformã obiectele a si b în obiecte de tip StringBufer.append(bm).

Florian Moraru – Programare Orientata pe Obiecte
return s.substring(0,2) + s.substring(5); } // variantã cu StringBuffer static String delete2 (String s, int from, int to ) { StringBuffer sb = new StringBuffer(s); sb.delete (from,to); // exceptie daca argumente incorecte ! return sb.toString(); }

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 ce contin date si produce un sir 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 sir String prin metoda “toString”, apelatã explicit sau implicit, dar nu si 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”

Metode statice si metode ale obiectelor 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, mostenire, polimorfism, programare cu interfete, s.a. Avantajele metodelor obiect sunt mai evidente în cazul unor familii de clase deschise pentru extindere si atunci când se urmãreste reutilizarea metodelor unor clase în alte clase. Pentru a arãta diferentele sintactice dintre definirea si utilizarea celor douã tipuri de metode vom folosi exemplul unei clase cu rolul clasei StringTokenizer, dar fãrã posibilitatea de a specifica lista de caractere separator între atomi; atomii sunt separati numai prin spatii albe.
class StrTok { // clasa cu metode statice public static boolean hasMoreTokens (String str, int pos) { // daca mai sunt atomi in sirul analizat return pos < str.length(); } public static int nextToken (String str, int pos, String tok[]) { // urmatorul atom lexical din str while (pos < str.length() && Character.isSpace(str.charAt(pos))) ++pos; int p= str.indexOf (' ',pos); if ( p < 0) p= str.length(); tok[0]= str.substring (pos,p); return p; // pozitia dupa acest atom } } class UseStrTok { // utilizare clasa StrTok public static void main (String[] arg) { String str = " sigma = alfa + epsilon "; String tok[] = new String[1]; int k=0; while ( StrTok.hasMoreTokens(str,k)) { k= StrTok.nextToken ( str, k, tok); System.out.println(tok[0]); } } }

37

Florian Moraru – Programare Orientata pe Obiecte Am folosit un vector cu un singur element ca artificiu pentru a permite functiei “nextToken” sã transmitã unul din rezultate (urmãtorul atom extras) printr-un argument, lucru imposibil printr-un argument de tip String. Functia are douã rezultate: atomul extras si pozitia din sir de dupã acest atom. Urmeazã varianta cu metode ale obiectelor:
class StrTok { // variabile ale clasei private String str; // sirul analizat private int crt; // pozitia curenta in sir // constructor public StrTok (String s) { str=s; crt=0; } // daca mai sunt atomi in sirul analizat public boolean hasMoreTokens () { return crt < str.length(); } // extrage urmatorul atom lexical dintr-un sir public String nextToken () { while ( crt < str.length() && str.charAt(crt)==' ') ++crt; // ignora spatii int start= crt; // inceput de atom crt= str.indexOf (' ',start); if ( crt < 0) crt= str.length(); return str.substring (start,crt); } } // utilizare class UseStrTok { public static void main (String[] arg) { String str = " sigma = alfa + epsilon "; String token; StrTok st = new StrTok(str); // creare obiect “tokenizer” while ( st.hasMoreTokens()) { // apel de metoda a obiectului “st” token= st.nextToken (); System.out.println(token); } } }

Utilizarea unor clase de intrare-iesire Existã un numãr relativ mare de clase Java pentru operatii de intrare-iesire cu fisiere dar aici vom da câteva “retete” simple pentru realizarea de aplicatii Java de tip “Desktop” (de tip consolã) cu clase din pachetul java.io. Afisarea pe ecran si scrierea în fisiere text a oricãrui tip de date se face simplu folosind metodele “print” si “println” din clasele PrintStream sau PrintWriter. Aceste metode pot primi un singur argument de orice tip primitiv sau de tipul generic Object si realizeazã automat conversia numerelor din format intern în format extern (sir de caractere). Toate clasele de I/E pentru operatii cu fisiere disc au constructori cu argument String si respectiv File pentru a primi numele fisierului disc cu care lucreazã. Toate clasele au metode de citire a unui caracter (sau octet) si de scriere a unui caracter sau octet. Constructorii si metodele acestor clase pot genera exceptii IOException, care trebuie aruncate sau tratate (prin try… catch).

38

Florian Moraru – Programare Orientata pe Obiecte Pentru a scrie într-un fisier disc se creeazã mai întâi un obiect FileInputStream sau FileWriter si apoi se creeazã obiectul PrintStream sau PrintWriter. Exemple:
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();}

Pentru scrierea de siruri în fisiere text se poate utiliza direct metoda “write (String)” din clasa FileWriter, dar pentru a scrie linii de text trebuie adãugat explicit caracterul ‘\n’. Exemplu:
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();}

Nu existã o clasã de citire date corespondentã cu PrintStream sau PrintWriter, care sã facã conversie automatã de numere la citire (din sir de caractere zecimale în format intern binar), dar din versiunea 1.5 s-a introdus clasa Scanner în pachetul java.util pentru astfel de operatii. Citirea de siruri de la tastaturã sau dintr-un fisier text se poate face în douã moduri: - Folosind metoda “read” din clasa FileReader pentru citire într-un vector de octeti urmatã de conversie la tipul String; - Folosind metoda “readLine” din clasa BufferedReader sau BufferedInputStream pentru citire din fisiere text formate din linii (linii terminate prin caracterul ‘\n’). Exemple de citire integralã în memorie a unui fisier text:
try { File f = new File (arg[0]); // creare obiect File int flen = (int) f.length(); // lungime fisier (nr de caractere) char txt[]= new char[flen]; // aloca memorie ptr continut fisier FileReader fr = new FileReader (f); // creare obiect FileReader fr.read(txt,0,flen); // citeste flen caractere in txt System.out.println( new String(txt,0,len)); // creare obiect String din vector de caractere } catch (IOException e) { e.printStackTrace();}

Semnificatia si ordinea argumentelor metodei “read” sunt aceleasi ca într-un constructor al clasei String, astfel cã se poate trece simplu de la un vector de caractere de lungime cunoscutã la un String. Exemplu de citire linii de text:
String line; // aici se citeste o linie de text try { BufferedReader br = new BufferedReader (new FileReader(arg[0])); while ( (line = br.readLine()) != null) // readLine are rezultat null la sfarsit de fisier System.out.println (line); } catch (Exception e) { e.printStackTrace();}

Variabila publicã System.out este de tip PrintStream dar variabila System.in este de tip InputStream, pentru care se poate folosi metoda “read” de citire a unui octet sau a unui vector de octeti de lungime specificatã. Pentru a citi linii de text de la consolã vom folosi o clasã care contine o metodã “readLine”: BufferedReader sau DataInputStream.

39

Florian Moraru – Programare Orientata pe Obiecte
String line; try{ BufferedReader stdin = new BufferedReader (new InputStreamReader (System.in)); while ( (line=stdin.readLine()) != null) System.out.println (line); } catch( IOException e) { e.printStackTrace();}

Pentru citirea de numere de la consolã sau din fisiere text trebuie folositã o metodã de conversie cu argument String, cum sunt Integer.parseInt, Float.parseFloat, Double.parseDouble. Clasa RandomAccessFile are o serie de avantaje fatã de alte clase de I/E: - permite atât operatii de scriere cât si operatii de citire; - permite crearea si citirea de fisiere binare, cu numere în format intern (readInt, writeInt, etc.); - permite citirea de linii de text (readLine); - permite accesul direct la date din fisier pe baza adresei de octet în fisier (seek), aflarea adresei curente în fisier (getFilePointer) si aflarea lungimii unui fisier (length). - permite citirea unui întreg fisier în memorie (readFully). Exemplu de citire si scriere linii de text folosind obiecte de tip RandomAccessFile:
public static void main (String[] arg) throws IOException { String linie ; RandomAccessFile f1=new RandomAccessFile (arg[0],"r"); RandomAccessFile f2=new RandomAccessFile (arg[1],"rw"); while ( (linie=f1.readLine ()) != null) f2.writeBytes(linie+”\n”); f1.close(); f2.close(); }

// nu se poate doar “w” !

In general, metodele de citire din clasele Java raporteazã prin rezultatul lor când se ajunge la sfârsitul fisierului si nu existã metode care sã testeze sfârsitul de fisier. Aceastã regulã nu se aplicã si metodelor care citesc numere din fisiere binare deoarece rezultatul (valoarea cititã) poate fi orice configuratie binarã; solutia este sã se compare pozitia curentã în fisier cu lungimea fisierului:
while (f1.getFilePointer() < f1.length() ) { int x = f1.readInt(); // citeste un intreg din fisier in x … // prelucrare x }

Deoarece clasa RandomAccessFile este definitã în afara familiilor de clase de I/E, acest tip nu apare ca posibil argument în constructorii altor clase care folosesc fisiere disc: clase pentru compresie si arhivare de fisiere, clase parser de fisiere text sau de fisiere XML, s.a. Acesti constructori au fie un argument File, fie un argument clasã abstractã Reader (InputStream) sau Writer (OutputStream). Clasa Scanner are metode ca nextInt, nextFloat, nextDouble, s.a care se folosesc oarecum similar functiei “scanf” din limbajul C, deci programatorul trebuie sã stie ce tip de date urmeazã sã citeascã:
Scanner sc = new Scanner (new FileReader (arg[0])); int x, sum=0; while ( sc.hasNext( )) { x=sc.nextInt(); // citire de numere intregi sum += x; }

Existã si un corespondent al functiei “printf” sub forma metodei “format” din clasele String si java.util.Formatter, dar ca si în cazul functiei “printf” existã multe detalii legate de precizarea formatului dorit.

40

In Java toate metodele trebuie definite (si nu doar declarate) în cadrul clasei. Exemplu de variabilã localã unei metode: public Complex conj ( ) { Complex c = new Complex().im. Definirea de noi clase Definirea unei clase în Java Definirea unei clase se face prin definirea variabilelor si functiilor clasei. Ele sunt necesare mai multor metode si sunt de obicei putine. } // c este o variabilã localã 41 . iar argumentele sunt de obicei date initiale necesare pentru operatiile cu variabilele clasei. Clasele de uz general se declarã public pentru a fi accesibile din orice alt pachet. return c. c. Variabilele de interes numai pentru anumite metode vor fi definite ca variabile locale în functii si nu ca variabile ale clasei.re. cum ar fi obiecte de tip nod de arbore sau obiecte ce corespund unor elemente din fisiere XML.im. Este posibil ca o metodã sã apeleze o altã metodã definitã ulterior. } } // parte realã si parte imaginarã Se observã cã metodele unei clase au putine argumente. Atunci când se creeazã multe obiecte dintr-o clasã.Florian Moraru – Programare Orientata pe Obiecte 4. im = im . iar aceste argumente nu se modificã.cpx. ceea ce este tipic pentru multe clase cu date: metodele clasei au ca efect modificarea datelor clasei. // metode ale clasei // adunare de numere complexe public void add ( Complex cpx) { re += cpx. Variabilele clasei sunt accesibile tuturor functiilor clasei pentru citire sau modificare si sunt initializate de obicei în constructorul clasei.im= -im. im += cpx.im. în aceeasi clasã. Nu se vor defini ca variabile ale clasei acele variabile care sunt folosite numai în una sau douã functii ale clasei.re= re. Ordinea în care sunt definiti membrii unei clase (date si functii) este indiferentã. dar în cazul unor clase cu un singur obiect (cum sunt clasele derivate din JFrame pentru fereastra aplicatiei) nu conteazã numãrul variabilelor clasei. Majoritatea claselor instantiabile grupeazã mai multe functii în jurul unor date comune. Acesta este si un avantaj al programãrii cu clase fatã de programarea proceduralã: functii cu argumente putine si care nu sunt modificate în functie. Ca exemplu vom schita o definitie posibilã pentru o clasã ale cãrei obiecte sunt numere complexe (nu existã o astfel de clasã predefinitã în bibliotecile JDK): public class Complex { // datele clasei private double re.re. } // scadere de numere complexe public void sub ( Complex cpx) { re = re . este important ca dimensiunea lor sã fie mentinutã la minimum. Toate variabilele numerice ale unei clase sunt initializate implicit cu zerouri si toate variabile referintã sunt initializate cu null. Dimensiunea unui obiect este determinatã mai ales de numãrul si tipul variabilelor clasei si nu de numãrul metodelor clasei. c.cpx. Datele clasei se declarã în afara metodelor clasei si vor fi prezente în fiecare obiect al clasei.

Florian Moraru – Programare Orientata pe Obiecte Pentru utilizarea comodã a obiectelor clasei "Complex" mai sunt necesare functii pentru initializarea datelor din aceste obiecte (numite "constructori") si pentru afisarea datelor continute în aceste obiecte. . pentru afisare public String toString ( ) { return ( "(" + re+ ". double y) { re=x. se poate afisa continutul unei colectii de obiecte "Complex".-3) . Aceste valori sunt preluate de fiecare obiect creat.re && im==cobj.out. Constructorii nu sunt considerati metode. Pentru a permite configurarea obiectelor create se practicã initializarea variabilelor clasei cu valori diferite pentru fiecare obiect în parte folosind unul sau mai multi constructori cu parametri. im.1). deoarece nu pot fi apelati explicit (prin nume). dacã nu se fac alte initializãri prin constructori. 42 . Functia urmãtoare (metodã a clasei "Complex" ) trebuie inclusã în definitia clasei: // conversie în sir.out. System. Un constructor este o functie fãrã tip si care are obligatoriu numele clasei din care face parte. pentru cã metoda "toString" a colectiei apeleazã metoda "toString" a obiectelor din colectie.im.println”) sau introdus într-un alt flux de date sau folosit de alte metode. Complex c2= new Complex (0." + im+ ")"). } . // metode ale clasei } Exemple de creare a unor obiecte de tipul “Complex” : Complex c1 = new Complex (2.im= parte imaginarã public Complex ( double x. In Java clasele cu date nu contin metode de afisare pe ecran a datelor din obiectele clasei. Redefinirea metodei "equals" în clasa "Complex" permite cãutarea unui obiect dat într-o colectie si alte operatii care necesitã comparatia la egalitate. Variabilele oricãrei clase sunt initializate automat la crearea de obiecte (cu valori zero pentru variabile numerice si null pentru variabile de orice tip clasã). // re=parte realã . // scrie (0.0) In plus. return re==cobj. dar contin o metodã cu numele “toString” care produce un sir de caractere ce reprezintã datele clasei si care poate fi scris pe ecran (cu metoda “System. } Functii constructor Orice clasã instantiabilã are cel putin un constructor public. Exemplu: public boolean equals (Object obj) { Complex cobj = (Complex) obj. Exemplu: public class Complex { private double re. im=y.println (c). . } Existenta metodei "toString" ne permite sã afisãm direct continutul unui obiect din clasa "Complex" astfel: Complex c = new Complex(). definit implicit sau explicit si apelat de operatorul new la crearea de noi obiecte.

Florian Moraru – Programare Orientata pe Obiecte Dacã nu se defineste nici un constructor atunci compilatorul genereazã automat un constructor implicit. dar au acelasi nume (un caz de supradefinire a unor functii).key=key. // nr de elemente in vector // constructori public IntVector( int n) { // n= capacitate initiala vector value= new int[n]. In cazul când variabilele clasei sunt vectori intrinseci sau clase colectie în constructor se alocã memorie pentru vectori sau colectii.add(t..re.value=value.value. // aloca memorie ptr vector count= 0. im=c.im.re. care diferã prin argumente. // adresa vector private int count. iar alteori nu existã posibilitatea modificãrii acestor obiecte (din clase “read-only”). this. Acest fel de initializare se practicã pentru variabile membru de tip clasã si pentru clase cu un singur obiect.key= new String(key). Exemplu: public class IntVector { // un vector de numere private float value[]. // initial nici un element } ."Center"). } In cazul când argumentele unui constructor sunt referinte la alte obiecte (si nu valori primitive) se retin de obicei aceste referinte în obiectul creat si nu se creeazã alte obiecte. // nu: this. De exemplu. Este uzual sã existe mai multi constructori într-o clasã. fãrã argumente si fãrã efect asupra variabilelor clasei (dar care apeleazã constructorul superclasei din care este derivatã clasa respectivã). public Pair (String key. } … // metode ale clasei } Uneori metodele clasei chiar trebuie sã modifice obiectele ale cãror adrese le primeste.. // initializare in afara constructorului // constructor clasa // adauga camp text la fereastra 43 . } Este posibilã si supradefinirea unor metode ale claselor. String value) { this. putem defini douã metode de adunare la un complex. Exemplu: public Complex ( Complex c) { re=c. cu efect pentru toate obiectele clasei. Exemplu: class Pair { private String key. cu acelasi nume dar cu argumente diferite: // adunare cu un alt complex public void add ( Complex c) { re += c. public TextFrame () { getContentPane(). im += c. // metode ale clasei } Variabilele unei clase pot fi initializate la declararea lor.im. Exemplu de initializare a unei variabile la declarare: class TextFrame extends JFrame { JTextField t = new JTextField (10). } // adunare cu un numar real public void add ( double x) { re = re + x.

In Java nu sunt permise argumente formale cu valori implicite. dar existã situatii când folosirea lui this este necesarã..Florian Moraru – Programare Orientata pe Obiecte } . Exemple: // constructor cu un parametru din clasa Complex public Complex (double re) { this (re. this. Crearea obiectului câmp text JTextField nu se face printr-o instructiune ci printr-o declaratie cu initializare. dar trebuie sã fie inclusã într-o functie. Un prim caz de folosire curentã a variabilei this este la definirea unui constructor cu argumente formale având aceleasi nume cu variabile ale clasei. formale de var.. // ptr. Exemplu: public Complex ( float re. metoda primeste ca argument implicit o referintã la obiectul respectiv. // extrage subsir din pozitia k din sirul a Acest argument implicit poate fi folosit in interiorul unei metode nestatice prin intermediul variabilei predefinite this.im=im.re=re. float im) { this. // lungimea sirului a // extrage subsir din pozitia k din sirul a corespund instructiunilor urmãtoare din limbajul C: int n = length(a).count. Instructiunea din constructor se va executa o singurã datã.substring (k). Definitia metodei "length" ar putea fi rescrisã folosind variabila this astfel: public int length (this) { return this. // lungimea sirului a String b = substring (a. ca si o metodã. într-un bloc static de initializare sau într-o metodã staticã private. } // alte metode Intr-o aplicatie se va crea un singur obiect de tip "TextFrame".length(). a distinge arg. Un constructor nu poate fi apelat explicit. Initializãri ale variabilelor clasei se mai pot face într-un bloc cuprins între acolade (inclus de compilator în orice constructor). clasei } 44 . dar un constructor cu mai putine argumente poate apela un constructor cu mai multe argumente. } // apel constructor cu un argument Cuvântul cheie "this" Metodele nestatice dintr-o clasã actioneazã asupra unor obiecte din acea clasã. dintre care unele au valori implicite. Altfel spus. instructiunile urmãtoare : int n = a. // "count" este o variabilã a clasei String } In mod normal nu trebuie sã folosim variabila this pentru referire la datele sau la metodele unui obiect. Atunci când se apeleazã o metodã pentru un anumit obiect. String b = a. iar atunci când este necesar acest apel (din aceeasi clasã) se foloseste cuvântul cheie this ca nume de functie.0). } // apel constructor cu douã argumente // constructor fara parametri din clasa Complex public Complex () { this (0). Cuvântul cheie this este o variabilã referintã care desemneazã în Java adresa obiectului curent ("acest obiect").k). iar obiectul de la adresa "t" va avea întotdeauna mãrimea 10.

abstract (numai pentru metode). Atribute ale membrilor claselor Membrii unei clase.sort(this). // un vector de caractere private int count. } Uneori un obiect îsi trimite adresa sa metodei apelate (metodã staticã sau dintr-un alt obiect). count--. count-index-1). Un alt caz de folosire a variabilei this este în instructiunea return.Florian Moraru – Programare Orientata pe Obiecte Astfel de situatii pot fi evitate prin alegerea unor nume de argumente diferite de numele variabilelor clasei. value. Cu exceptia tipului. dar clasele JDK folosesc aceleasi nume (si variabila this ) pentru cã argumentele unui constructor contin valori initiale pentru variabilele clasei.arraycopy (value.. O variabilã staticã a clasei declaratã final este de fapt o constantã. au în Java mai multe atribute: tipul variabilei sau metodei ( tip primitiv sau tip clasã ). public static final Class TYPE = Class. final. folositã atunci când nu se declarã explicit o altã valoare.. Variabilele locale pot avea doar atributul final. Cuvintele cheie folosite pentru modificarea atributelor implicite se numesc "modificatori". // ordonare obiect ptr care s-a apelat metoda “add” } } Variabila this nu poate fi folositã în metode statice. // constructori si metode clasa Byte 45 . De exemplu orice membru este nestatic si nefinal atunci când nu se folosesc modificatorii static sau final.. variabile si metode. .getPrimitiveClass(“byte”). alte metode ale clasei StringBuffer } Un caz particular este metoda "toString" din clasa "String": public String toString () { return this. // caractere efectiv folosite din vector // metode public StringBuffer deleteCharAt (int index) { // sterge caracterul din poz index System.. un atribut de accesibilitate (public. private) plus atributele static. protected. index+1. Exemple de constante din clasa Byte: public class Byte { public static final byte MIN_VALUE = -128. // rezultatul este obiectul modificat de metodã } // . Exemple: // un vector isi transmite adresa sa metodei statice de sortare class SortedVec extends Vector { public void addElement (Object obj) { // adaugare element si ordonare super. fiecare din aceste atribute are o valoare implicitã. pentru metodele care modificã datele unui obiect si au ca rezultat obiectul modificat. Exemplu: public final class StringBuffer { private char value[ ]. return this.addElement (obj). index. nemodificabilã prin operatii ulterioare primei initializãri. public static final byte MAX_VALUE = 127. // apel metoda din clasa Vector Collections.

int). dar pot exista si câteva metode statice în fiecare clasã cu date. Exemplu: public String toString() { // metoda din clasa "Integer" return String. Exemplu din clasa String: public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.. pentru cã folosirea unei metode nesatice este conditionatã de existenta unui obiect. // static int parseInt(String.Florian Moraru – Programare Orientata pe Obiecte } Se vede din exemplul anterior cã o variabilã staticã finalã poate fi initializatã si cu rezultatul unei functii. Exemplu de metodã din clasa Integer. iar functia de citire a unei linii (într-un obiect String) are rezultat null la sfârsit de fisier. pentru cã initializarea se face la încãrcarea clasei. De exemplu. return. pentru conversia unui sir de caractere într-un întreg : public static int parseInt (String s) throws NumberFormatException { if (s == null) throw new NumberFormatException("null"). care specificã ce fel de exceptii se pot produce în metoda respectivã si care permite compilatorului sã verifice dacã exceptiile produse sunt sau nu tratate acolo unde se apeleazã metoda.toString(). O metodã staticã poate apela un constructor. Exemplul urmãtor aratã cum se poate evita aparitia unei exceptii de iesire din limitele unui vector intrinsec. dar metoda staticã se poate folosi si în absenta unor obiecte.println (“usage: java copy input output”). // metoda statica din clasa String } O metodã staticã (inclusiv functia main) nu poate apela o metodã nestaticã. . anumite functii de citire a unui octet dintr-un fisier au rezultat negativ la sfârsit de fisier.length < 2) { System. Uneori este posibilã anticiparea si prevenirea unor exceptii.out. // prelucrare argumente primite } 46 . Exemplu: public static Integer valueOf (String s) throws NumberFormatException { return new Integer(parseInt(s)). } Parte din definitia unei metode este si clauza throws. Metodele unei clase instantiabile sunt de obicei nestatice. din cauza utilizãrii gresite a unui program Java: // program cu doua argumente în linia de comanda public static void main ( String arg[ ]) { if ( arg. } . . } Anumite situatii speciale apãrute în executia unei metode sunt uneori raportate prin rezultatul functiei (boolean) si nu prin exceptii. . } // sir echivalent unui obiect O metodã staticã poate fi apelatã de o metodã nestaticã..valueOf(value).

y0=y. cu mentinerea interfetei sale. care recunoaste tipul urmãtorului simbol extras dintr-un fisier (flux de date): numãr. Object d. Atributul de accesibilitate se referã la drepturile unei clase asupra membrilor unei alte clase. Incapsularea datelor în clase De obicei datele unei clase nu sunt direct accesibile utilizatorilor clasei. dar valoarea numãrului sau sirului cuvânt se aflã în variabile publice ale clasei. Definitia lor depinde de particularitatile sistemului de calcul pe care se implementeazã metodele. indiferent de atributul de accesibilitate al acestor variabile (private. metodele publice (tip. height. protected.. De exemplu. int di. Toate obiectele unei clase au aceleasi drepturi. Datele unei clase au în general atributul private sau protected. declaratã dar nedefinitã în clasa unde apare pentru prima datã. Metode declarate dar nedefinite în Java sunt si metodele "native" (cu atributul native). argumente) si variabilele public accesibile. Exemplu: 47 . O clasã expune utilizatorilor sãi o interfatã publicã. Tipul urmãtorului simbol este rezultatul unei metode. O metodã abstractã este o metodã cu atributul abstract. Documentatia unei clase prezintã numai interfata sa publicã. int y0.io”. care constã din totalitatea metodelor publice (accesibile) ale clasei si care aratã ce se poate face cu obiecte ale clasei respective. mai rare.Florian Moraru – Programare Orientata pe Obiecte O metodã declaratã final nu mai poate fi redefinitã într-o subclasã si ar putea fi implementatã mai eficient de cãtre compilator. indiferent de ordinea definirii lor si de atributul de accesibilitate. cuvânt sau caracter special. int w. height=h. y. si este legat de ceea ce se numeste "încapsulare" sau "ascunderea datelor". // coordonate: colt. metodele si constructorii unei clase se declarã public.. folosite relativ frecvent în alte clase si pentru care accesul prin intermediul metodelor ar fi incomod si ineficient. Atributul public se foloseste pentru functiile si/sau variabilele clasei ce urmeazã a fi folosite din alte clase. în care nu se respectã principiul încapsulãrii iar variabilele unei clase sunt public accesibile. In general. width=w. width. Exista si cazuri. lãtime. iar pe de altã parte este mai sigur ca aceste date sã fie modificate numai de metodele clasei si nu de cãtre orice utilizator. Exemplu de metodã nativã: public static native void arraycopy (Object s. publice. deci nu sunt accesibile functiilor din alte clase. Pe de o parte utilizatorii nu sunt interesati de aceste date (de exemplu. fãrã ca programele ce folosesc stiva sã necesite vreo modificare. cel care foloseste o stivã nu are nevoie sã “vadã” vectorul stivã si alte detalii). int h) { x0=x. Variabilele public accesibile se mai numesc si proprietãti ale obiectelor. Este posibilã modificarea implementãrii unei clase. Metodele unei clase se pot apela unele pe altele. Metodele unei clase pot folosi variabilele clasei. O clasã care contine cel putin o metodã abstractã este o clasã abstractã si nu poate fi instantiatã. înãltime public Rectangle (int x0. adicã constructorii publici. public). care se implementeazã într-un alt limbaj decât Java (de obicei limbajul C). Exemplu din pachetul “java. deci nu se pot crea obiecte pentru aceastã clasã (dar se pot defini variabile de un tip clasã abstractã). ale clasei respective. putem folosi o stivã realizatã ca listã înlãntuitã în locul unei stive vector. stabilite la definitia clasei. int si. iar accesul la aceste date este permis numai prin intermediul metodelor. Un obiect StreamTokenizer este un mic analizor lexical. nume. Este vorba de clase cu mai multe variabile. int length).awt”: public class Rectangle { // dreptunghi de incadrare figura geometrica public int x. } . // alte metode } Un alt exemplu este clasa StreamTokenizer din pachetul “java.

TT_WORD si TT_NUMBER sunt constante (variabile publice statice si finale) din clasa StreamTokenizer.out. // legatura la nodul urmator public Node (int v) { val=v. O solutie alternativã ar fi fost obtinerea valorii cu o metodã publicã având un rezultat de tipul generic Object.0) în acelasi pachet si în acelasi fisier sursã.out. De exemplu. // afisare numar else System. Variabilele cu atributul protected dintr-o clasã sunt accesibile pentru toate clasele derivate direct din A (indiferent de pachetul unde sunt definite) si pentru clasele din acelasi pachet cu ea. dar utilizarea acestei valori ar fi necesitat o conversie la tipul indicat de metoda “nextToken”. // inaintare spre sfarsit de lista 48 . public class MyList { // lista inlantuita simpla (cu santinela) protected Node prim. // un fisier cu numele in arg[0] StreamTokenizer st = new StreamTokenizer(is). // adresa primului element public MyList ( ) { prim = new Node(0).nval).println (type+" "+ st.2 clasa enumerator este inclusã în clasa vector). . Clasa “MyList” contine o variabilã de tip “Node” (adresa primului element din listã).println (type+" "+ st. care poate fi “private” sau “protected”.nextToken()) != st.} // constructor } Metodele clasei listã trebuie sã aibã acces direct la variabilele clasei “Node”. ceea ce se poate realiza în douã moduri: .Clasele “Node” si “MyList” se aflã în acelasi pachet.println((char)type). // date din nod Node leg.TT_NUMBER) // daca e un numar System. // afisare cuvant if (type == st. In continuare vom schita definitia unei clase pentru o listã înlãntuitã de numere întregi. // creare nod nou cu valoarea v Node p=prim. In ambele cazuri variabilele clasei “Node” pot sã nu aibã nici un atribut explicit de accesibilitate. // un obiect analizor lexical int type. o clasã care poate sã nu aibã nici o metodã si eventual nici constructor explicit: class Node { // nod de lista inlantuita int val. iar variabile cu atributul protected din clasa Vector sunt folosite direct în clasa VectorEnumerator (în Java 1. Mai întâi trebuie definitã o clasã pentru un nod de listã.TT_EOF) { // repeta pana la sfarsit de fis if (type == st. Dacã nu se specificã explicit modul de acces la un membru al unei clase A atunci se considerã implicit cã el este accesibil pentru toate clasele definite în acelasi pachet cu A. // creare nod sentinela (cu orice date) } // adaugare la sfirsit de lista public void add (int v) { // adaugare intreg v la lista Node nou= new Node(v). iar TT_EOF.Clasa “Node” este interioarã clasei “MyList”.Florian Moraru – Programare Orientata pe Obiecte public static void main (String arg[]) throws Exception { FileReader is = new FileReader(arg[0]). leg=null.out. dacã anticipãm eventuale subclase derivate.TT_WORD) // daca e un cuvant System.sval). // tip atom while ( (type=st. numite uneori clase “prietene”. // afisare caractere speciale } } Variabilele “sval” (de tip String) si “nval” (de tip double) sunt variabile publice. clasele VectorEnumerator si Vector sunt definite (în Java 1.

Conform unor cerinte mai noi ale standardului Java Beans. continut lista public String toString () { String aux="".leg.Florian Moraru – Programare Orientata pe Obiecte while (p. int x) { if (crt==null) // daca sfarsit de lista return false. // x gasit return find (crt.leg=nou. pentru operatii generale. . “toString”. } } // rezultatul functiei toString // se pleaca de la primul nod din lista // cat timp mai exista un element urmator // intregul p.leg != null) p=p.. metodele de acces la variabile private au un nume care începe cu "get" si continuã cu numele variabilei. Majoritatea claselor instantiabile au unul sau mai multi constructori publici folositi pentru initializarea obiectelor si apelati de operatorul new. Node p=prim. p=p.. Metodele unei clase pot fi clasificate în câteva grupe: . while ( p != null) { aux=aux + p. definite în afara metodelor clasei (de obicei înaintea functiilor). // apel functie recursiva } // fct recursiva de cautare (din clasa MyList) private boolean find (Node crt. Structura unei clase Java O clasã care poate genera obiecte contine în mod normal si date.val se converteste automat in “String” // avans la nodul urmator // sirul cu toate valorile din lista // nou urmeaza ultimului element Pentru metoda “contains” vom defini o functie auxiliarã recursivã. care se modificã de la un apel la altul (adresa primului nod din sublista în care se cautã): // cautare in lista (metoda a clasei MyList) public boolean contains (int x) { return find (prim. .Metode care permit citirea datelor clasei ( metode accesor). } return aux.x). // parte reala si imaginara // metode de citire a datelor public float getReal ( ) { return re. Exemplu: public class Complex { private double re.val) return true. x). aplicabile oricãrui obiect Java ( “equals”.Metode pentru operatii specifice clasei respective. “hashCode”). // atunci x negasit if ( x==crt. cu un argument suplimentar. p.leg.val + " ". iar metodele de modificare a variabilelor au un nume care începe cu "set". deci variabile ale clasei.Metode care permit modificarea datelor clasei ( si care lipsesc din anumite clase ). } // extrage parte reale 49 . // continua cautarea in sublista } Functia “find” putea fi si staticã ( private static boolean find .leg.Metode mostenite de la clasa Object si redefinite.). . im.leg. } // sir ptr.

Florian Moraru – Programare Orientata pe Obiecte
public float getImag ( ) { return im; } // extrage parte imaginara // metode de modificare a datelor public void setReal (float x) { re =x; } // modifica parte reala public void setImag (float y) { im=y; } // modifica parte imaginara // complex conjugat public Complex conj () { return new Complex(re,-im); } // alte operatii cu obiecte de tip Complex ... // metode din “Object” redefinite public boolean equals (Object obj) {. . . } public String toString () { . . . } }

Componentele JavaBeans sunt clase care se pot instantia de cãtre un operator prin intermediul unui mediu vizual de dezvoltare (tipic sunt componente de interfatã graficã: butoane, cãsute cu text, s.a.), dupã care se pot modifica proprietãtile lor (tot interactiv, în faza de proiectare a interfetei grafice). De aceea clasele JavaBeans au si un constructor fãrã argumente si, obligatoriu, metode pentru citirea si modificarea proprietãtilor (care sunt variabile private ale clasei). Clasele cu date care se instantiazã exclusiv prin program (prin operatorul new) vor avea constructori cu argumente, pentru cã este mai comod sã stabilim proprietãtile la construirea obiectului. Clasa String contine un vector de caractere si dimensiunea sa, mai multi constructori si diverse functii care realizeazã operatii uzuale asupra acestui vector: cãutarea unui caracter dat în vector, extragerea unui subsir din întregul sir, compararea cu un alt sir etc. Exemplu:
public class String { // variabilele clasei private char value[ ]; // un vector de caractere private int count; // numar de caractere folosite // un constructor public String( char [ ] str) { count= str.length; value= new char[count]; System.arraycopy (str,0,value,0,count); } // metodele clasei public int length () { // lungime sir return count; } public String substring (int start, int end) { // extrage subsir dintre start si end int n=end-start+1; // nr de caractere intre start si end char b[ ] = new char[n]; System.arraycopy (value,start,b,0,n); return new String (b); } public String substring (int pos) { // extrage subsir din pozitia pos return substring (pos, length()-1); // return this.substring (pos,length()-1); } // ... alte metode }

A se observa existenta unor metode cu acelasi nume dar cu argumente diferite si modul în care o metodã (nestaticã) apeleazã o altã metodã nestaticã din clasã: nu se mai specificã obiectul pentru care se apeleazã metoda “substring” cu doi parametri, deoarece este acelasi cu obiectul pentru care s-a apelat metoda “substring” cu un singur parametru (obiectul this = chiar acest obiect).

50

Florian Moraru – Programare Orientata pe Obiecte O clasã mai poate contine constante si chiar alte clase incluse. In limbajul C o declaratie de structurã este o definitie de tip care nu alocã memorie si de aceea nu este posibilã initializarea membrilor structurii. In Java o definitie de clasã creeazã anumite structuri de date si alocã memorie, ceea ce justificã afirmatia cã o clasã este un fel de sablon pentru crearea de obiecte de tipul respectiv. Definitia unei clase Java nu trebuie terminatã cu ‘;’ ca în C++. Metode care pot genera exceptii In antetul unor metode trebuie sã aparã clauza throws, dacã în aceste metode pot apare exceptii, a cãror tratare este verificatã de compilator. Intr-o metodã poate apare o exceptie fie datoritã unei instructiuni throw, fie pentru cã se apeleazã o metodã în care pot apare exceptii. La executia unei metode sau unui constructor este posibil sã aparã o situatie de exceptie, iar metoda trebuie sã anunte mai departe aceastã exceptie. Semnalarea unei exceptii program înseamnã în Java crearea unui obiect de un anumit tip clasã (derivat din clasa Exception) si transmiterea lui în afara functiei, cãtre sistemul care asistã executia ("Runtime system"). Exemplu de metodã care verificã dacã parametrul primit este eronat, caz în care produce o exceptie:
public char charAt (int index) { // caracterul din pozitia ‘index’ a unui sir if ((index < 0) || (index >= count)) // daca indice negativ sau mai mare ca lungimea sirului throw new StringIndexOutOfBoundsException(index); // arunca exceptie predefinita return value[index]; // daca indice corect, returneaza caracterul respectiv }

Instructiunea throw se foloseste pentru generarea unei exceptii într-o metodã si specificã un obiect “exceptie”(de obicei un obiect creat ad-hoc chiar în instructiune). Alegerea acestui cuvânt ("to throw"= a arunca) se explicã prin aceea cã la detectarea unei exceptii nu se apeleazã direct o anumitã functie (selectatã prin nume); functia care va trata exceptia (numitã "exception handler") este selectatã dupã tipul obiectului exceptie "aruncat" unui grup de functii. Metoda "charAt" aruncã o exceptie care nu trebuie declaratã. O metodã în care se poate produce o exceptie si care aruncã mai departe exceptia trebuie sã continã în definitia ei clauza throws. Cuvântul cheie throws apare în antetul unei metode, dupã lista de argumente si este urmat de numele unui tip exceptie (nume de clasã) sau de numele mai multor tipuri exceptie. In Java se considerã cã erorile semnalate de o metodã fac parte din antetul metodei pentru a permite compilatorului sã verifice dacã exceptia produsã este ulterior tratatã si sã oblige programatorii la tratarea anumitor exceptii. Exemplu de exceptie generatã de un constructor:
public static FileReader fopen ( String filename) throws IOException { return new FileReader (filename); // exceptie de fisier negãsit }

Orice functie (metodã) care apeleazã metoda “fopen” de mai sus trebuie fie sã arunce mai departe exceptia (prin clauza throws), fie sã o trateze (prin try-catch):
public static void fileCopy ( String src, String dst) throws IOException { FileReader fr = fopen (src); // aici poate apare exceptie de I/E …

Orice altã functie care apeleazã pe “fileCopy” trebuie sã trateze sau sã arunce mai departe exceptia; decizia se va lua în aplicatia care foloseste functia (dacã se afiseazã doar un mesaj sau se încearcã o repetare a operatiei de introducere nume fisier). O tratare minimalã afiseazã cauza exceptiei si locul unde s-a produs:
public static FileReader fopen ( String filename) {

51

Florian Moraru – Programare Orientata pe Obiecte
try { return new FileReader (filename); } catch (IOException ex) { ex.printStackTrace(); return null; } } // daca fisier negãsit

Pentru erori uzuale sunt predefinite o serie de tipuri exceptie, dar utilizatorii pot sã-si defineascã propriile tipuri exceptie, sub forma unor noi clase (derivate fie din clasa Exception fie din clasa RuntimeException). Pentru erorile care nu permit continuarea programului vom prefera exceptiile derivate din clasa RuntimeException sau din clasa Error, care nu obligã programatorul la o anumitã actiune ("prindere" sau aruncare mai departe). Existã chiar pãrerea cã toate exceptiile ar trebui sã fie de acest fel, pentru simplificarea codului (prin eliminarea clauzei throws din antetul metodelor) si pentru a evita tratarea lor superficialã (prin interzicerea oricãrui mesaj la aparitia exceptiei). Clase si obiecte Java în faza de executie Pentru fiecare clasã încãrcatã în masina virtualã Java este creat automat câte un obiect de tip Class, cu informatii despre clasa asociatã (metadate). Obiecte Class sunt create automat si pentru interfete, clase abstracte si vectori intrinseci Java. Prin “reflectie” (“reflection”) se întelege obtinerea de informatii despre o clasã sau despre un obiect în faza de executie (este “reflectatã” starea masinii virtuale). In plus, se pot crea si modifica dinamic obiecte în faza de executie. Reflectia este asiguratã în principal de clasa numitã Class, dar si de alte clase din pachetul “java.lang.reflect”: Constructor, Method, Field, s.a. O variabilã de tip Class contine o referintã la un obiect descriptor de clasã; ea poate fi initializatã în mai multe feluri: - Folosind cuvântul cheie class (literalul class) ca si cum ar fi un membru public si 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 sir 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 mostenitã de la clasa Object, deci existã în orice clasã):
Float f = new Float (3.14); Class cF = f.getClass();

Clasa Class contine metode care au ca rezultat numele clasei, tipul clasei (clasã sau interfatã sau vector), tipul superclasei, clasa externã, interfete implementate, tipul obiectelor declarate în clasã, numele câmpurilor (variabilelor clasei), numele metodelor clasei, formele functiilor constructor s.a. Metoda “getName” are ca rezultat un sir ce reprezintã numele clasei al cãrui tip este continut întrun obiect Class. Exemplul urmãtor aratã cum se poate afisa tipul real al unui obiect primit ca argument de tipul generic Object:
void printClassName (Object obj) { System.out.println ( obj.getClass().getName()); }

Crearea de obiecte de un tip aflat în cursul executiei dar necunoscut la compilare (cu conditia ca tipul respectiv sã fi fost definit printr-o clasã, iar fisierul “class” sã fie accesibil la executie) se poate face simplu dacã în clasã existã numai un constructor fãrã argumente . Exemplu:

52

Florian Moraru – Programare Orientata pe Obiecte
public static Object getInstance (String clsname) throws Exception { Class cl = Class.forName(clsname); // clsname = nume clasa (complet) return cl.newInstance(); }

Deoarece putem obtine toti constructorii unei clase, cunoscând tipul argumentelor, se pot “fabrica” obiecte si apelând constructori cu argumente. Exemplu:
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);

Prin reflectie un asamblor de componente dintr-un mediu vizual poate sã determine proprietãtile si metodele proprii unor obiecte, sã modifice proprietãtile acestor obiecte si sã genereze apeluri de metode între obiecte. In rezumat, reflectia permite operatii cu clase si cu obiecte necunoscute la scrierea programului, dar care pot fi determinate dinamic, în cursul executiei. De observat cã tipul unui obiect nu este neapãrat acelasi cu tipul variabilei prin care se fac referiri la obiect; tipul variabilei poate fi o interfatã, o clasã abstractã sau neabstractã din care este derivat tipul obiectului, deci tipul variabilei poate fi mai general decât tipul obiectului. De exemplu, rezultatul unei metode de extragere dintr-un vector (elementAt) este de tip Object, dar tipul real al obiectului poate fi orice tip clasã. Pentru apelarea metodelor specifice clasei respective se face o conversie prin operatorul “cast” la tipul cunoscut. Exemplu:
Vector v = new Vector(); v.add (new Integer(1)); // adauga la v un obiect de tip Integer ... int x = (Integer)v.elementAt(0).intValue(); // x= 1

Utilizarea metodei “intValue” fãrã conversie prealabilã este semnalatã ca eroare la compilare, deoarece metoda nu existã în clasa Object, iar compilatorul verificã dacã metodele (nestatice) apelate existã în clasa de tipul cãreia este variabila. 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 stie care este tipul exact al obiectului. Exemplu:
String s = v.elementAt(i).toString(); if ( v.elementAt(i).equals(obj)) ... // se apeleazã Integer.toString() // se apeleazã Integer.equals()

De aceea determinarea tipului unui obiect la executie, folosind obiectul Class asociat, este rareori necesarã în programele obisnuite.

53

numãrul si tipul argumentelor . poate fi folosit numai într-un constructor si trebuie sã fie prima instructiune (executabilã) din constructorul subclasei. ca nume de functie. iar o variabilã dintr-o subclasã poate "ascunde" o variabilã cu acelasi nume din superclasã. La definirea unei clase derivate se foloseste cuvântul cheie extends urmat de numele clasei de bazã.i).binarySearch(this.obj). deci numele si tipul metodei. Apelarea metodei originale din superclasã nu este în general necesarã pentru un obiect de tipul subclasei. Exemplu: public class IOException extends Exception { public IOException() { super(). } // metoda redefinita: cautare binara in vector ordonat public int indexOf (Object obj) { // cauta in vector un obiect return Collections. } } In Java se spune cã o subclasã extinde functionalitatea superclasei.Florian Moraru – Programare Orientata pe Obiecte 5. O metodã redefinitã într-o subclasã "ascunde" metoda corespunzãtoare din superclasã. mostenire. de la care “mosteneste” toate variabilele si metodele publice si “protected”. In general o subclasã este o specializare. Tipul D este un subtip al tipului A. dar nu si modul de utilizare al metodei. dar se poate face folosind cuvântul cheie "super" în locul numelui superclasei. subclasele nu fac altceva decât sã redefineascã una sau câteva metode ale superclasei din care sunt derivate. în sensul cã ea poate contine metode si date suplimentare.addElement (obj). De multe ori. // apel metoda “addElement” din clasa Vector 54 . Exemplu: // vector ordonat cu adaugare la sfarsit si reordonare public class SortedVector extends Vector { public void addElement (Object obj) { // adaugare element si ordonare super. } } Principala modalitate de specializare a unei clase este redefinirea unor metode din superclasã. Derivare. static. Cuvântul super. Majoritatea claselor Java care contin date redefinesc metodele “equals” si “toString” (mostenite de la clasa Object). if (i < 0) i=-i-1. private. Redefinirea unei metode trebuie sã pãstreze "amprenta" functiei. o particularizare a superclasei si nu extinde domeniul de utilizare al superclasei. Exemplu: public class SortedVector extends Vector { // vector ordonat // metoda redefinita ptr adaugare la vector ordonat public void addElement (Object obj) { // adaugare element si ordonare int i=indexOf (obj). pentru a permite compararea de obiecte si conversia datelor din clasã într-un sir de caractere. } public IOException(String s) { super(s). polimorfism Clase derivate Derivarea înseamnã definirea unei clase D ca o subclasã a unei clase A. Prin redefinire ("override") se modificã operatiile dintr-o metodã. insertElementAt(obj. Subclasa D poate redefini metode mostenite de la clasa pãrinte (superclasa) A si poate adãuga noi metode si variabile clasei A. Nu se poate extinde o clasã finalã (cu atributul final) si nu pot fi redefinite metodele din superclasã care au unul din modificatorii final.

i++) { 55 . int pos) { // ptr a nu modifica ordinea throw new UnsupportedOperationException().tip.sort(this). fie indirect. } } Exemplul urmãtor aratã cum putem scrie o metodã recursivã într-o clasã derivatã: class FileExt extends File { public FileExt (String name) { super (name).. // apel metoda din superclasa Collections.util.out.5 adnotarea @Override ca prefix al metodei: import java.size().i<v.length. if ( ! isDirectory()) return.} public SortVec ( Vector v) { super().println (sp + getName())..* . cu argument Object. } Lipsa adnotãrii @Override în exemplul anterior nu permite detectarea erorii cã metoda “addElement” cu argument String nu redefineste metoda “addElement” din superclasã. // vector ordonat cu adaugare la sfirsit si reordonare class SortVec extends Vector { public SortVec () { super(). } // afisare recursiva fisiere din subdirectoare // varianta 1: cu metoda listFiles public void printFiles (String sp) { System.i<files. dacã variabilele din superclasã sunt de tip public sau protected. public boolean empty() { // test de stiva goala return size()==0. Exemplu: public class Stack extends Vector { . prin metode publice ale superclasei.argumente) cu metoda din superclasã atunci nu are loc o redefinire si doar se adaugã altã metodã la cele mostenite. } // metoda mostenita automat dar interzisa in subclasa public void insertElementAt (Object obj.i++) addElement(v. Pentru a permite compilatorului sã verifice cã are loc o redefinire de metodã mostenitã s-a introdus din 1. for(int i=0. } } Dacã metoda din subclasã nu are exact acelasi prototip (nume.Florian Moraru – Programare Orientata pe Obiecte Collections. O subclasã se poate referi la variabilele superclasei fie direct prin numele lor.addElement (obj). // sau return elementCount==0.elementAt(i)). // apelat implicit ! for (int i=0. } @Override public void addElement ( String obj) { // adaugare element si ordonare super.sort(this). File [] files= listFiles().

O clasa derivatã "mosteneste" de la superclasa sa datele si metodele nestatice cu atributele public si protected . Este deci necesar ca un constructor din subclasã sã apeleze un constructor din superclasã. O subclasã nu mosteneste constructorii superclasei. toString s. refoloseste extinderea automatã a vectorului.println ( a... definitã anterior. O solutie de preluare a unor functii de la o clasã la alta este derivarea. metodele si datele suplimentare (dacã existã) si trebuie redefinite metodele care se modificã în subclasã. System. Nu se genereazã automat decât constructori fãrã argumente. Exemplu: public class SortedVector extends Vector { 56 . prin apelul metodei cu acelasi nume din superclasã. In Java atributele membrilor mosteniti nu pot fi modificate în clasa derivatã: o variabilã public din superclasã nu poate fi fãcutã private în subclasã. In schimb. Clasa “SortedVector”.i<files. Initializarea variabilelor mostenite este realizatã de constructorul superclasei. prin numele sãu. f. diferite în clasele înrudite. for(int i=0.i++) { FileExt f = new FileExt (this+"/"+ files[i]). " înainte de prima instructiune din constructorul subclasei. Exemplu: SortedVector a = new SortedVector().out. dacã nu existã un apel explicit si dacã nu se apeleazã un alt constructor al subclasei prin "this(. } } } Derivarea ca metodã de reutilizare Derivarea este motivatã uneori de necesitatea unor clase care folosesc în comun anumite functii dar au si operatii specifice. desi valorile folosite la initializare sunt primite ca argumente de constructorul subclasei.)".printFiles (sp+" "). f. indexOf. Apelul unui constructor din superclasã dintr-o subclasã se face folosind cuvântul cheie super ca nume de functie si nu este permisã apelarea directã. deci fiecare clasã are propriile sale functii constructor (definite explicit de programator sau generate de compilator). if ( ! isDirectory()) return.length.toString()).a. Compilatorul Java genereazã automat o instructiune "super().println (sp + getName()).getAbsolutePath()). a acestui constructor. mosteneste metodele clasei Vector: elementAt. String [] files= list().out. Metodele si variabilele mostenite ca atare de la superclasã nu mai trebuie declarate în subclasã si pot fi folosite fãrã prefixul super.printFiles (sp+" "). } } // varianta 2: cu metoda list public void printFiles (String sp) { // afisare recursiva cu indentare System. // afisare continut vector Chiar si metoda “addElement” redefinitã în clasa derivatã.Florian Moraru – Programare Orientata pe Obiecte FileExt f = new FileExt (files[i]. trebuie definite functiile constructor. // nu exista constructor cu argument ! In Java constructorul implicit (fãrã argumente) generat pentru o subclasã apeleazã automat constructorul fãrã argumente al superclasei. Din acest motiv nu putem crea un obiect vector ordonat prin instructiunea urmãtoare: SortedVector a = new SortedVector(n).

Exemplu: public class SortedVector extends Vector { private Comparator comp.printStackTrace().. care nu se mai genereazã automat dacã se defineste (cel putin) un constructor cu argumente. // text va contine tot fisierul try { BufferedReader f= new BufferedReader( new FileReader(fname)). // super() poate lipsi. // text += line+”\n”.line.Florian Moraru – Programare Orientata pe Obiecte public SortedVector () { // vector cu dimensiune implicita super(). Comparator c) { super(max).readLine()) !=null) text=text+line+"\n". // apel metoda “addElement” din clasa Vector Collections. Dacã subclasa are variabile în plus fatã de superclasã. pentru cã nu existã încã obiectul pentru care se apeleazã metoda): // extragere cuvinte separate prin spatii dintr-un fisier text class FileTokenizer extends StringTokenizer { public FileTokenizer ( String fname) { super(readFile(fname)).out.hasMoreTokens()) System.comp).} return text. // max folosit la alocarea de memorie in superclasa } .addElement (obj). while ( (line=f. // initializari in superclasa } public SortedVector (int max) { // vector cu dimensiune specificata la instantiere super(max). In exemplul anterior este necesarã definirea constructorului fãrã argumente. // ordonare dupa comparatorul primit } } Apelul constructorului superclasei (“super”) trebuie sã fie prima instructiune din constructorul subclasei. // argument de tip String } // citire continut fisier text intr-un sir ( functie statica !) static String readFile( String fname) { String text ="". } // verificare metode clasa FileTokenizer public static void main (String arg[]) { FileTokenizer t = new FileTokenizer (arg[0]). atunci ele vor fi initializate în constructorul subclasei.print(" | "+ t. atunci ele vor fi grupate într-o metodã staticã (metode nestatice nu pot fi apelate înaintea unui constructor. Dacã trebuie efectuate anterior anumite operatii. while ( t. // variabila a subclasei // constructori public SortedVector ( int max. } public SortedVector ( Comparator c) { super(). } } 57 . comp=c. e generat automat } public void addElement (Object obj) { // adaugare element si ordonare super. } catch (IOException ex) { ex. comp=c.nextToken()).sort(this..

// primul nod cu date prim. public class MyQueue extends MyList { // Lista coada private Node ultim.val. Cele douã clase folosesc în comun variabila “prim” (adresa de început a listei) si metoda “toString”. // initializare variabila “prim” ultim=prim. if (empty()) // daca coada este goala ultim=prim. } // test daca coada goala public boolean empty() { return prim.leg. care poate fi folosit în declararea unor variabile. argumente sau functii de tipul respectiv. si cu o metodã de extragere (si stergere) obiect de la începutul listei.leg=nou. MyQueue q= new MyQueue().val=v. a = q. Derivare pentru creare de tipuri compatibile Definirea unei noi clase este echivalentã cu definirea unui nou tip de date. Metoda “add” este redefinitã din motive de performantã. // downcasting 58 . dar are acelasi efect: adãugarea unui nou element la sfârsitul listei.leg==null. return p. Avantajele reutilizãrii prin extinderea unei clase sunt cu atât mai importante cu cât numãrul metodelor mostenite si folosite ca atare este mai mare. // variabila prezenta numai in subclasa public MyQueue () { super().leg=p. } } Din clasa “MyList” se poate deriva si o clasã stivã realizatã ca listã înlãntuitã. // adresa ultimului element din lista } // adaugare la sfirsit coada (mai rapid ca in MyList) public void add (Object v) { Node nou= new Node(). } // eliminare obiect de la inceputul cozii public Object del () { if ( empty()) return null. Nod p=prim. Exemple de conversii între tipuri compatibile: MyList a . î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. Tipurile superclasã si subclasã sunt compatibile. ultim. Ideea este cã atât coada cât si stiva sunt cazuri particulare de liste. nou. cu redefinirea metodei de adãugare “add”. iar trecerea de la o subclasã D la o superclasã A se poate face prin conversie explicitã (“cast”). Prin derivare se creeazã subtipuri de date compatibile cu tipul din care provin.leg.Florian Moraru – Programare Orientata pe Obiecte In exemplul urmãtor clasa “MyQueue” (pentru liste de tip coadã) este derivatã din clasa “MyList” pentru liste simplu înlãntuite. ultim=nou. pentru adãugare la început de listã. Conversia de tip între clase incompatibile produce exceptia ClassCastException. // upcasting q = (MyQueue) a .

unor argumente de functii sau componente ale unor colectii. float. In felul acesta se reduce numãrul de constructori care ar fi fost necesar pentru fiecare clasã instantiabilã derivatã din clasa abstractã InputStream. public Scanner (Readable source).).) pentru toate tipurile de numere (considerate ca subtipuri ale tipului double). care sunt toate compatibile ca tip cu clasa de la baza ierarhiei. Posibilitatea de creare a unor tipuri compatibile si de utilizare a unor functii polimorfice constituie unul din motivele importante pentru care se foloseste derivarea. Ierarhii de tipuri (primitive) existã si în limbajul C. Un exemplu este familia claselor exceptie. Conversia explicitã de la tipul de bazã la un subtip este necesarã pentru a putea folosi metodele noi ale subclasei pentru obiectele subclasei.a.a.Florian Moraru – Programare Orientata pe Obiecte Tipuri compatibile sunt tipurile claselor aflate într-o relatie de descendentã (directã sau indirectã) pentru cã aceste clase au în comun metodele clasei de bazã. MyList downcasting MyQueue upcasting De remarcat cã douã subtipuri ale unui tip comun A nu sunt compatibile ( de ex.). exp. siruri sau vectori de octeti s. s. Trecerea între cele douã tipuri nu se poate face prin operatorul de conversie. Aplicarea repetatã a derivãrii produce ierarhii de tipuri si familii de clase înrudite. de obicei un subtip ( IOException. desi sunt derivate din tipul Number). Tipul superclasei este mai general decât tipurile subclaselor si de aceea se recomandã ca acest tip sã fie folosit la declararea unor variabile.util). tipurile Integer si Float nu sunt compatibile. unele derivate din altele. De exemplu clasa Scanner (java. si mai specializate.a. Number Float Integer Procesul de extindere sau de specializare a unei clase poate continua pe mai multe niveluri. NullPointerException s. Tipurile InputStream si Readable sunt tipuri foarte generale. deci motivul extinderii clasei nu este reutilizarea unor metode ci relatia specialã care apare între tipurile subclasei si superclasei. In general se definesc familii de clase. Putem constata în practica programãrii cu obiecte cã unele superclase transmit subclaselor lor putinã functionalitate (putine metode mostenite întocmai). care include ca niste cazuri particulare fisiere text. Instructiunea throw trebuie sã continã un obiect de un tip compatibil cu tipul Exception. In felul acesta programele au un caracter mai general si sunt mai usor de modificat. dar se poate face prin construirea altui obiect. în loc sã avem câte o functie pentru fiecare tip numeric (int. In acest scop existã urmãtorii constructori cu argumente de tip interfatã: public Scanner (InputStream source). permitând sã existe câte o singurã functie matematicã (sqrt. log s. derivate direct din clasa Exception sau din subclasa RuntimeException. deci fiecare subclasã poate deveni superclasã pentru alte clase. O subclasã a clasei Exception nu adaugã metode noi si contine doar constructori: public class RuntimeException extends Exception { 59 . de exemplu. poate prelua textul de scanat si dintr-un flux de intrare-iesire.a. care recunoaste si converteste automat numere din format extern în format intern.

lang. fãrã sã ne intereseze ce implementare de dictionar s-a folosit în functie."a"}. realizat printr-un tabel de dispersie. care contine metode utilizabile pentru orice clasã dictionar.out. // pune in dictionar cheia si valoarea asociata } return dic. // valoarea asociata cheii key in dictionar if ( nr != null) // daca exista cheia in dictionar cnt = nr. // cuvintele sunt chei in dictionar Integer nr = (Integer) dic. i< v. Dictionary d= wordfreq(lista). // tipul Hashtable se poate inlocui cu alt tip int cnt. new Integer(cnt)). Alegerea unei alte implementãri se face prin modificarea primei linii din functie. care nu transmite metode subclaselor sale. Clasa de bibliotecã Hashtable extinde clasa Dictionary.Object) este superclasa tuturor claselor JDK si a claselor definite de utilizatori. remove. put."b". Putem sã ne definim si alte clase care sã extindã clasa Dictionary. Deoarece clasa Object nu contine 60 .Florian Moraru – Programare Orientata pe Obiecte public RuntimeException() { super(). // atunci se mareste contorul de aparitii else // daca cheia nu exista in dictionar cnt =1. Clasa Object ca bazã a ierarhiei de clase Java In Java.1 este clasa abstractã Dictionary. System. b=2. dar creeazã clase compatibile cu clasa abstractã. keys. Exemplu de folosire a functiei: public static void main (String arg[]) { String lista[ ] = {"a". De asemenea. // contor de aparitii for (int i=0. care este tipul din care derivã toate celelalte tipuri clasã si care este folosit pentru argumentele multor functii (din clase diferite) si pentru toate clasele colectie predefinite (Vector.a. de aceea în alte ierarhii de clase (din alte limbaje) rãdãcina ierarhiei de clase este o clasã abstractã. Clasa Object transmite foarte putine operatii utile subclaselor sale. // atunci este prima ei aparitie dic."c"."b". clasa Object (java. i++) { Object key = v[i]."a". In Java (si în alte limbaje) aceastã tehnicã a condus la crearea unui tip generic Object. } } // s= un mesaj suplimentar Uneori superclasa este o clasã abstractã (neinstantiabilã). Orice clasã Java care nu are clauza extends în definitie este implicit o subclasã derivatã direct din clasa Object. // scrie {c=1. a=3} } Utilizarea unui tip mai general pentru crearea de functii si colectii cu caracter general este o tehnicã specificã programãrii cu obiecte.intValue()+1. s.length. } public RuntimeException(String s) { super(s). } Rezultatul functiei “wordfreq” poate fi prelucrat cu metodele generale ale clasei Dictionary. Exemplu de creare a unui dictionar cu numãrul de aparitii al fiecãrui cuvânt dintr-un vector: public static Dictionary wordfreq ( String v[]) { Dictionary dic = new Hashtable().println (d). Un exemplu din Java 1. clasele abstracte si interfetele Java sunt subtipuri implicite ale tipului Object. indiferent de implementarea dictionarului: get.get(key). fiind un caz particular de dictionar. cum ar fi un dictionar vector. HashTable). Atunci când scriem un program sau o functie vom folosi variabile sau argumente de tipul Dictionary si nu de tipul Hashtable sau de alt subtip.put (key.

return false. dar în practicã se redefinesc câteva metode. deci nu se pot schimba tipul functiei sau tipul argumentelor. La apelarea metodei "Complex. un utilizator al clasei String ( sau al altor clase cu date) se asteaptã ca metoda "equals" sã aibã rezultat true dacã douã obiecte diferite (ca adresã) contin aceleasi date. } atunci ea ar fi fost consideratã ca o nouã metodã. 61 . O variabilã sau un argument de tip Object (o referintã la tipul Object) poate fi înlocuitã cu o variabilã de orice alt tip clasã. if ( obj != null && obj instanceof Complex) if (this. deoarece un argument formal de tip superclasã poate fi înlocuit (fãrã conversie explicitã de tip) printrun argument efectiv de un tip subclasã. Metoda “toString” din clasa Object transformã în sir adresa obiectului si deci trebuie sã fie redefinitã în fiecare clasã cu date.. Exemplu din clasa “Complex”: public boolean equals (Object obj) { Complex cpx =(Complex) obj.equals" cu argument de tip String se foloseste în Java operatorul instanceof. metoda "equals" este rescrisã în clasele unde se poate defini o relatie de egalitate între obiecte.equals" cu argument de tip "Complex". sau a metodei "Complex.equals" poate fi folosit un argument efectiv de tipul "Complex". } Pe de altã parte. Redefinirea unei metode într-o subclasã trebuie sã pãstreze aceeasi semnãturã cu metoda din superclasã. Metoda “toString” permite obtinerea unui sir echivalent cu orice obiect. care are ca operanzi o variabilã de un tip clasã si tipul unei (sub)clase si un rezultat boolean. Dacã am fi definit metoda urmãtoare: public boolean equals (Complex cpx) { . In acest caz am fi avut o supradefinire ("overloading") si nu o redefinire de functii. deoarece orice tip clasã este în Java derivat din si deci compatibil cu tipul Object (la fel cum în limbajul C o variabilã sau un argument de tip void* poate fi înlocuitã cu o variabilã de orice tip pointer). argumentul metodei "Complex. toate cu argument de tip Object si care pot fi apelate cu argument de orice tip clasã. nu se impune cu necesitate redefinirea vreunei metode.im == cpx.im) return true. String s = d.Florian Moraru – Programare Orientata pe Obiecte nici o metodã abstractã.equals" este de tip Object si nu este de tip "Complex". pentru a produce un sir cu continutul obiectului. deci trecerea de la orice tip la tipul String (dar nu prin operatorul de conversie ): Date d = new Date(). Din acest motiv.toString(). // dar nu si : String s = (String) d. cum ar fi clasele flux de intrare-iesire.re && this. diferitã de metoda "equals" mostenitã de la clasa Object.re == cpx. } Pentru a evita erori de programare de tipul folosirii metodei "String. Metoda "equals" nu trebuie redefinitã în clasele fãrã date sau în clasele cu date pentru care nu are sens comparatia la egalitate. Metoda "equals" din clasa Object considerã cã douã variabile de tip Object sau de orice tip derivat din Object sunt egale dacã si numai dacã se referã la un acelasi obiect: public boolean equals (Object obj) { return (this == obj). cum ar pãrea mai natural. Existã deci mai multe metode "equals" în clase diferite.. De aceea.

Object f = new Float (3. adicã un apel de metodã este tradus într-o instructiune de salt care contine adresa functiei apelate... Metodele “equals”.14).toString(). al cãror efect depinde de tipul obiectului pentru care sunt apelate (altul decât tipul variabilei referintã).add().Florian Moraru – Programare Orientata pe Obiecte Polimorfism si legare dinamicã O functie polimorficã este o functie care are acelasi prototip.toString() String fs = f.. Exemple: Object d = new Date(). // apel Float. String ds = d.. q. la executie. q. Asocierea unui apel de metodã cu functia ce trebuie apelatã se numeste "legare" ("Binding"). Clasa MyQueue va avea o altã adresã pentru functia "add" decât clasa MyList.toString().toString(). toString a prim aprim apri MyList equals toString toString add toString add totoStrin totoStri . q prim aprim ultim MyQueu uee equals aprim apri toStrin add add del totoStri del .equals() s. Object equals toString equals toString toString .toString() Apelul functiilor polimorfice este mai putin eficient ca apelul functiilor cu o singura formã. Figura urmãtoare aratã cum se rezolvã. // apel Date. Fiecare clasã are asociat un tabel de pointeri la metodele (virtuale) ale clasei. Variabile Obiecte Clase 62 . Pentru metodele finale si statice legarea se face la compilare. dar implementãri (definitii) diferite în clase diferite dintr-o ierarhie de clase... apeluri de forma a.add(). “toString” sunt exemple tipice de functii polimorfice.a. metodã numitã polimorficã. In Java majoritatea metodelor sunt polimorfice. Legarea se poate face la compilare (“legare timpurie”) sau la executie ("legare târzie" sau "legare dinamicã"). q. Metodele polimorfice Java corespund functiilor virtuale din C++. Legarea dinamicã are loc în Java pentru orice metodã care nu are atributul final sau static.

equals (array[i])) // “equals” este polimorfica return i. Apelul “q.equals” merge în sus pe ierahia de clase si ajunge la definitia din clasa Object. deoarece nici o subclasã a clasei Object nu redefineste metoda “equals”. // negasit } Pentru functia anterioarã este esential cã metoda “equals” este polimorficã si cã se va selecta metoda de comparare din clasa cãreia apartine obiectul referit de variabila “obj” (aceeasi cu clasa elementelor din vectorul “array”).toString” conduce la metoda “toString” din superclasã (“MyList”) deoarece este o metodã mostenitã si care nu a fost redefinitã.add” pleacã de la obiectul referit de variabila "q" la tabela metodelor clasei "MyQueue" si de acolo ajunge la corpul metodei "add" din clasa "MyQueue". O colectie genericã poate fi realizatã ca o colectie de obiecte de tip Object iar o functie genericã este o functie cu argumente formale de tip Object si/sau de un tip colectie genericã. Structuri de date generice în POO O colectie genericã este o colectie de date (sau de referinte la date) de orice tip.i<array. } O metodã staticã poate apela o metodã polimorficã. // indice unde a fost gasit return -1. etc. Object obj) { for (int i=0. O metodã staticã. care este membrã a unei familii de clase.length. Numai metodele obiectelor pot fi polimorfice. este o metodã care efectueazã aceleasi operatii în toate subclasele clasei A. nu poate fi selectatã astfel. q = new MyQueue(). pentru a ordona un vector cu orice fel de obiecte. Compilatorul traduce un apel de metodã polimorficã printr-o instructiune de salt la o adresã calculatã în functie de continutul variabilei referintã si de definitia clasei. lista înlãntuitã.Florian Moraru – Programare Orientata pe Obiecte Fiecare obiect contine. chiar dacã este redefinitã în subclase. de exemplu “Arrays. De exemplu. Pentru a memora 63 . în functie de tipul variabilei pentru care se apeleazã metoda. a = new MyList(). Exemplu: public final boolean contains (Object elem) { return indexOf (elem) >=0 . pe lângã datele nestatice din clasã. Solutia functiilor polimorfice este specificã programãrii orientate pe obiecte si se bazeazã pe existenta unei ierarhii de clase. iar un algoritm generic este un algoritm aplicabil unei colectii generice.i++) if ( obj. Alegerea automatã a variantei potrivite a unei metode polimorfice se poate face succesiv.sort” apeleazã functia polimorficã “compareTo” (sau “compare”). q. în sensul cã selectarea metodei apelate se face în functie de tipul obiectului pentru care se apeleazã metoda. în functie de tipul acestor obiecte (String. Integer. Fie urmãtoarele variabile: MyList a. datoritã modului de apelare. pe mai multe niveluri. un pointer la tabela de metode virtuale (polimorfice). apelul metodei “toString” pentru un obiect colectie alege functia ce corespunde tipului de colectie (vector. Date. Legãtura dinamicã se face de la tipul variabilei la adresa metodei apelate.). ea nu mai poate fi redefinitã. la rândul ei metoda “toString” dintr-o clasã vector apeleazã metoda “toString” a obiectelor din colectie. dar cu efecte diferite în clase diferite. O metodã finalã (atributul final) dintr-o clasã A. al cãrei efect depinde de tipul obiectelor comparate. etc). Apelul “q. Apelul “q. care contin toate metode (nestatice) cu aceeasi semnaturã. Exemplu de functie pentru cãutare secventialã într-un vector de obiecte: public static int indexOf (Object[] array.

toString().append ( a[i]. } Ceea ce numim o colectie de obiecte este de fapt o colectie de pointeri (referinte) la obiecte alocate dinamic.asList”) si invers (metoda “toArray” din orice clasã colectie): String a[]={“1”.valueOf(k)). afisare în vector. } public static String vectorToString ( Vector v) { // obiect vector StringBuffer sb=new StringBuffer(). ”4”}. for (int i=0.size(). ”3”. double.add (String. int n) { // vector intrinsec StringBuffer sb=new StringBuffer(). Double.i<v.toString(). De exemplu. Exemplu de functie care creazã un sir cu valorile dintr-un vector.toString()+"\n"). In plus. ”2”.2 sunt mai multe clase si sunt mai bine sistematizate. Byte.add ( k+””). Object[] b = v.k<11. return sb. 64 . Char. Vectorul intrinsec de obiecte generice se foloseste atunci când continutul sãu nu se modificã sau se modificã foarte rar. Existã posibilitatea de a trece de la un vector intrinsec la o clasã vector (metoda “Arrays. insertie într-o pozitie datã s.) se pot folosi clasele “anvelopã” ale tipurilor primitive: Integer.Boolean.a. return sb. poate fi o colectie).toArray(). dar timpul de acces la elemente este important. boolean s. Short.k++) v. ele vor fi preluate de metoda “asList” în obiectul vector si pot produce exceptii la operatii asupra lor (si la ordonare). for (int k=1. cu prelungirea actiunii unor functii polimorfice la subcolectii.asList(a)). Hashtable si Stack dar din Java 1. dar aceastã realitate este ascunsã de sintaxa Java.Florian Moraru – Programare Orientata pe Obiecte într-o colectie de obiecte si date de un tip primitiv (int. Un alt avantaj al utilizãrii de clase colectie fatã de vectori intrinseci este acela cã se pot creea colectii de colectii.i++) sb. la rândul lui.elementAt(i). Cea mai simplã colectie genericã este un vector cu componente de tip Object: Object [ ] a = new Object[10].i<10. Vector v = new Vector (Arrays. metoda “toString” din orice colectie apeleazã repetat metoda “toString” pentru fiecare obiect din colectie (care.i++) sb. Primele colectii generice din Java au fost clasele Vector.a. Exemplu de adãugare a unor siruri (referinte la siruri) la o colectie de tip Vector: Vector v = new Vector (10). ca în cazul unui vector intrinsec (variabila “length” nu spune câte elemente sunt în vector ci doar capacitatea vectorului). // aloca memorie pentru vector // sau v. numãrul de elemente din vector se obtine prin metoda “size” si nu trebuie transmis separat ca argument functiilor.toString()+”\n”).append ( v. // sau StringBuilder în Java 5 for (int i=0. pe linii separate: public static String arrayToString ( Object a[]. Dacã existã valori null în vectorul intrinsec. Clasa Vector oferã extinderea automatã a vectorului si metode pentru diferite operatii uzuale: cãutare în vector. // sau StringBuilder în Java 5 for (int i=0.i<n.i++) a[i]= new Integer(i). Float.

out. Operatorul de conversie (“cast”) are prioritate mai micã decât operatorul punct si de aceea trebuie folosite paranteze pentru elementul cu tip schimbat. având drept elemente obiecte de tip Vector (clasa Vector este si ea subclasã a clasei Object). public class HSet extends Vector { // tabel de dispersie ca vector de vectori private int n.i<args. // afisare vector } In Java. în vector principal (nr pozitiv) return ((Vector)elementAt(k)). // poate lipsi.i++) addElement (new Vector()). pentru a putea aplica metode specifice tipului respectiv. // dimensiune implicita vector principal } // apartenenta obiect la multime public boolean contains (Object el) { int k= (el. fãrã a mai utiliza variabile intermediare. for (int i=0.size(). // poz. // aloca mem.println(v). i). // sir din pozitia i v.elementAt(i)).i++) { String str = (String)v.contains } // adaugare obiect la multime (daca nu exista deja) public boolean add (Object el) { // redefineste metoda “add” din Vector 65 .i<v. Exemplu: for (int i=0.Florian Moraru – Programare Orientata pe Obiecte La extragerea unui element dintr-o colectie genericã trebuie efectuatã explicit conversia la tipul obiectelor introduse în colectie.i++) v. Fiecare vector (sau listã) contine un numãr de “sinonime”. // trece sir in litere mici } System.toLowerCase(). // adauga sir la vector // modificare vector for (int i=0. dar clasa Vector asigurã extinderea automatã a unui vector atunci când este necesar.toLowerCase(). Expresiile cu metode înlãntuite aplicate unor elemente din colectii Java necesitã astfel de paranteze. // apel Vector. înainte de a folosi acest element pentru apelul unei metode. Metoda de dispersie are ca rezultat restul împãrtirii codului de dispersie (produs de metoda “hashCode”) prin dimensiunea vectorului principal. ca si în C. ptr un vector de sinonime } public HSet () { // alt onstructor this(11). este super.hashCode() & 0x7FFFFFFF) % n.i++) v. dar notatiile se simplificã în Java 5 dacã se folosesc clase generice (cu tipuri declarate). adicã elemente cu valori diferite pentru care metoda de dispersie a produs un acelasi indice în vectorul principal.i).setElementAt( str. for (int i=0.add (args[i]). deci ca un obiect de tip Vector.n=n.length.size(). Exemplu de creare a unui vector de siruri. Numãrul de sinonime din fiecare vector nu poate fi cunoscut si nici estimat. Sã considerãm o multime de obiecte realizatã ca tabel de dispersie (“hashtable”). modificarea sirurilor si afisarea vectorului : public static void main (String args[]) { Vector v = new Vector() . O solutie mai bunã este cea din clasa JDK Hashtable.setElementAt ( ( (String)v. // alocare memorie ptr vector principal this. sunt posibile expresii ce contin apeluri succesive de metode.contains(el).elementAt(i). Tabelul de dispersie va fi realizat ca un vector de vectori.i<v.i<n.size() ! public HSet (int n) { // un constructor super(n). în care se foloseste un vector de liste înlãntuite.

toString() .Florian Moraru – Programare Orientata pe Obiecte int k = (el. } Metodele polimorfice “toString”. Pentru a evita astfel de erori la executie s-au introdus din Java 1. for (int k=0.hashCode() & 0x7FFFFFFF) % n. 66 . care parcurge secvential toate elementele vectorului. sin. Subliniem cã variabilele unei colectii au tipul Object. // adauga la vector din pozitia k return true.toString()+"\n". Object obj = f. if (v !=null) s=s + v. Incercarea de conversie între tipuri incompatibile poate fi semnalatã la compilare sau la executie. // obiectul din vector poate avea orice tip Float f = (Float) obj.k++) // ptr fiecare din cei n vectori m=m+((Vector)elementAt(k)). printr-o exceptie. } // numar de obiecte din colectie public int size() { // redefineste metoda “size” din Vector int m=0. în vector principal (nr pozitiv) Vector sin = (Vector)elementAt(k). } // conversie continut colectie în sir public String toString () { // redefineste metoda din Vector String s="".k<n. String.elementAt(i). String s = (String) obj. De remarcat cã astfel de conversii nu modificã tipul obiectelor ci doar tipul variabilelor prin care ne referim la obiecte (tipul unui obiect nu poate fi modificat în cursul executiei). for (int k=0.5).). // sau: Float f = (Float) v.elementAt(i). // eroare la compilare // exceptie la executie In al doilea caz tipul String este subtip al tipului Object si deci compilatorul Java permite conversia (nu stie tipul real al obiectului referit de variabila “obj”). // poz. // aduna dimensiune vector return m.k<n. dar o parte din ele pot avea valoarea null si genereazã exceptii la folosirea lor în expresii de forma v. } return s. Redefinirea metodei “toString” a fost necesarã deoarece metoda “toString” mostenitã de la clasa Vector (care o mosteneste de la o altã clasã) foloseste un iterator.contains (el)) return false.add (el).k++) { Vector v=(Vector)elementAt(k).5 colectii generice care permit specificarea tipului obiectelor din colectie la definirea colectiei. chiar dacã ele se referã la variabile de un alt tip (Integer. Aceastã situatie este destul de frecventã în Java si face necesarã utilizarea operatorului de conversie (tip) ori de câte ori o variabilã de un tip mai general (tip clasã sau interfatã) se referã la un obiect de un subtip al tipului variabilei.size(). “contains” si “size” din clasa “HSet” apeleazã metodele cu acelasi nume din clasa Vector. astfel nu se declarã doar un obiect Vector ci se declarã un vector de obiecte String sau Float sau de un alt tip: Vector<String> a = new Vector<String>. etc. // vector de sinonime if ( sin. copilatorul Java nu “vede” decât tipul declarat (Object) si nu permite utilizarea de metode ale clasei din care fac parte obiectele reunite în colectie. String s = (String) f. Exemplu: Object obj = v. Exemple: Float f = new Float(2.

Interfete si clase abstracte Clase abstracte în Java Generalizarea si abstractizarea în programarea cu obiecte sunt realizate si prin clase abstracte. O clasã abstractã nu este instantiabilã. Short. ci de a transmite anumite metode comune pentru toate subclasele derivate din clasa abstractã (metode implementate sau neimplementate). csize=0. // verifica daca colectia este goala … // alte metode } public abstract class AbstractCollection implements Collection { public abstract int size(). O clasã abstractã constituie o bazã de plecare pentru definirea altor clase. // dimensiune colectie boolean isEmpty(). Ea urmeazã a fi definitã într-o subclasã a clasei (interfetei) care o contine. Exemplu: public class MyCollection extends AbstractCollection { private Object [ ] array. tipul Number este o generalizare a tipurilor clasã numerice (Double. cu constructor private. de exemplu. Derivarea dintr-o clasã abstractã se face la fel ca derivarea dintr-o clasã neabstractã folosind cuvântul extends. // nr de obiecte in colectie } public int size() { return csize. } … // alte metode } 67 . O metodã abstractã este declaratã cu atributul abstract si nu are implementare. nu pot fi instantiate (nu pot genera obiecte): .} // metoda implementata … } Scopul unei clase abstracte nu este acela de a genera obiecte utilizabile.Clase ne-abstracte. Integer. care contine atât metode abstracte cât si metode definite. O clasã care contine cel putin o metodã abstractã trebuie declaratã ca abstractã. clasele abstracte pot contine variabile si metode neabstracte. fie o interfatã. // o colectie bazata pe un vector private int csize. Byte). Metodele abstracte pot apare numai în interfete si în clasele declarate abstracte. Exemplu: public interface Collection { // declara metode prezente in orice colectie int size(). dar poate specifica ce operatii (metode) ar fi necesare pentru toate subclasele sale. care nici nu au nevoie de un constructor Spre deosebire de interfete. dar ar putea contine metode statice sau nestatice utilizabile în subclase. // metoda abstracta public boolean isEmpty ( return size()==0. O clasã abstractã este de multe ori o implementare partialã a unei interfete.Clase abstracte. . Uneori superclasa este atât de generalã încât nu poate preciza nici variabile si nici implementãri de metode. ca urmare. // nr de elemente in colectie public MyCollection (int maxsize) { array = new Object[maxsize]. dar nu este obligatoriu ca o clasã abstractã sã continã si metode abstracte. Float. In astfel de cazuri superclasa Java este fie o clasã abstractã. O superclasã defineste un tip mai general decât tipurile definite prin subclasele sale. care împiedicã instantierea.Florian Moraru – Programare Orientata pe Obiecte 6. In Java avem douã posibilitãti de a defini clase care contin doar metode statice si.

pentru cã nu se stie care din ele sunt efectiv necesare în subclase. // folosire sau afisare caracter ch } } Interfete Java O interfatã Java ar putea fi consideratã ca o clasã abstractã care nu contine decât metode abstracte si. // caracter generat de tasta apasata . prin definirea metodelor declarate în interfetele respective. constante simbolice. Dintre clasele care implementeazã aceastã interfatã. în vederea prelucrãrii succesive a unui grup de obiecte. interfata mai veche Enumeration contine douã metode comune oricãrei clase cu rol de enumerare a elementelor unei colectii : public interface Enumeration { boolean hasMoreElements(). } // daca mai sunt elemente in colectie // elementul curent din colectie Enumerarea unor obiecte poate fi privitã ca o alternativã la crearea unui vector cu obiectele respective. respectând declaratiiile acestora ( ca tip si ca argumente)... Totusi. unele redefinite în subclase. care implementeazã o interfatã prin metode cu definitie nulã. public boolean hasMoreElements() { return hasMoreTokens(). Exemplu de clasã adaptor utilã în definirea de clase ascultãtor la evenimente generate de tastaturã: 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 } O subclasã (care reactioneazã la evenimente generate de taste) poate redefini numai una din aceste metode. existã deosebiri importante între interfete si clase abstracte. Object nextElement().Florian Moraru – Programare Orientata pe Obiecte O clasã abstractã poate face o implementare partialã. dar metodele lor nu sunt abstracte.. De exemplu. sunt clasele enumerator pe vector si enumerator pe un tabel de dispersie Hashtable. In bibliotecile de clase Java existã câteva clase adaptor. deci poate contine si metode neabstracte si/sau variabile în afara metodelor abstracte.. Ele sunt clase abstracte pentru a nu fi instantiate direct. Metodele declarate într-o interfatã sunt implicit publice si abstracte. fãrã sã se preocupe de celalte metode nefolosite de subclasã: class KListener extends KeyAdapter { public void keyPressed(KeyEvent e) { char ch = e.getKeyChar(). } public Object nextElement() { return nextToken(). eventual. care face o enumerare a cuvintelor dintr-un text: public class StringTokenizer implements Enumeration { . Ulterior s-au adãugat metode din aceastã interfatã si clasei StringTokenizer. } } 68 . O clasã (abstractã sau instantiabilã) poate implementa una sau mai multe interfete. Pentru a obtine clase instantiabile dintr-o clasã abstractã trebuie implementate toate metodele abstracte mostenite.

out.nextElement(). private int crt = 0. se pot defini variabile.append(".println (count (v. Enumeration e = elements().nextElement(). } buf. } } In exemplul anterior. return buf.append("[").”1”)). while (enum. argumente formale si functii de un tip interfatã sau clasã abstractã.elements().”1”. // variabila de tip interfata ! buf. Astfel de variabile vor fi înlocuite (prin atribuire sau prin argumente efective) cu variabile de un tip clasã care implementeazã interfata respectivã. // utilizarea variabilei interfata buf.”1”.append("\b]"). } // utilizare public static void main(String arg[]) { String a[]={“2”. for (int i = 0 . } // definirea clasei iterator pe vector class VectorEnumerator implements Enumeration { private Vector v. dar se pot declara variabile.").”1”. i <= size() .equals(obj) ) m++.”2”.toString(). Object obj) { int m=0.toString public final String toString() { StringBuffer buf = new StringBuffer(). Vector v = new Vector (Arrays. deci cu referinte la obiecte care implementeazã interfata sau care extind clasa abstractã.Florian Moraru – Programare Orientata pe Obiecte Utilizarea unei interfete comune mai multor clase permite unificarea metodelor si modului de utilizare a unor clase cu acelasi rol. metoda “elements” din clasa Vector are ca rezultat un obiect “enumerator pe vector”. buf.”4”.asList(a)).append(s). return m. dar si scrierea unor metode general aplicabile oricãrei clase care respectã interfata comunã. i++) { String s = e. Exemplu de posibilã implementare a metodei “elements” din clasa “Vector” : public Enumeration elements() { return new VectorEnumerator (this).hasMoreElements() ) if (enum. functii sau argumente de functii de un tip interfatã. System. Exemplu: // variantã pentru metoda Vector. de tipul Enumeration. } Nu pot fi create obiecte de un tip interfatã sau clasã abstractã. Exemplu de metodã care calculeazã de câte ori apare un obiect într-o colectie de obiecte care are un enumerator: public static int count (Enumeration enum.”3”.”1”} . Prin definirea unei interfete sau clase abstracte se creeazã un tip de date comun mai multor clase deoarece o variabilã (sau un argument) de un tip interfatã poate fi înlocuitã fãrã conversie explicitã cu o variabilã de orice subtip.toString(). // indice element curent din enumerare public VectorEnumerator (Vector v) { 69 . Ca si în cazul claselor abstracte.

Serializable (obiectele lor pot fi salvate sau serializate în fisiere disc sau pe alt mediu extern). mai multe clase predefinite SDK.size()) return v. O interfatã este consideratã ca un contract pe care toate clasele care implementeazã acea interfatã se obligã sã îl respecte. Clonable.} public class Date implements Serializable. 70 . definitã astfel: public interface Clonable { } Clasa Object contine metoda "clone". implementeazã simultan interfetele Comparable (obiectele lor pot fi comparate si la mai mic – mai mare).elementAt (crt++). cu date.a. s. Un exemplu sunt clasele cu obiecte comparabile. Ca exemplu. } } O interfatã poate extinde o altã interfatã (extends) cu noi metode abstracte. implicit extinsã) si sã implementeze una sau mai multe interfete. putând contine si alte metode. cum este cazul interfetei Comparator. interfata TreeNode reuneste metode de acces la un nod de arbore. deci o clasã îsi asumã obligatia de a defini toate metodele din interfetele mentionate în antetul ei. Clonable (obiectele lor pot fi copiate).. Comparable { . Un exemplu tipic este interfata Clonable.} Este posibil ca în momentul definirii unei interfete sã nu existe nici o singurã clasã compatibilã cu interfata. folositã numai de clasele care declarã cã implementeazã interfata Clonable.. o clasã poate mosteni date si/sau metode de la o singurã clasã. dar poate mosteni mai multe tipuri (poate respecta simultan mai multe interfete). } public boolean hasMoreElements() { return crt < v. iar interfata MutableTreeNode o extinde pe prima cu metode de modificare nod de arbore (dupã crearea unui arbore poate fi interzisã sau nu modificarea sa): public interface MutableTreeNode extends TreeNode { void insert(MutableTreeNode child. Integer. O interfatã fãrã nici o metodã poate fi folositã pentru a permite verificarea utilizãrii unor metode numai în anumite clase. în faza de executie. care toate implementeazã interfata Comparable: String.. O clasã poate simultan sã extindã o clasã (alta decât clasa Object.size(). Clasele care implementeazã o aceeasi interfatã pot fi foarte diverse si nu formeazã o ierarhie (o familie). BigDecimal.. Date. } Diferente între interfete si clase abstracte O clasã poate implementa (implements) o interfatã sau mai multe si poate extinde (extends) o singurã clasã (abstractã sau nu). // elimina fiu cu indice dat al acestui nod . Serializable { . // adauga un fiu acestui nod void remove(int index).v=v. int index).Florian Moraru – Programare Orientata pe Obiecte this. Exemple: public class String implements Comparable. dar este aplicabilã numai pentru o parte din clase.. Altfel spus. De exemplu.. } public Object nextElement() { if (crt < v. Metoda neabstractã "clone" este mostenitã automat de toate clasele Java.

} public interface ItemListener extends EventListener { public void itemStateChanged(ItemEvent e). In felul acesta se obtine o familie de clase deschisã pentru extinderi ulterioare cu clase compatibile. deoarece depind de tipul evenimentului. In Java. Interfetele si clasele abstracte nu trebuie opuse. specifice anumitor ascultãtori (pentru anumite evenimente): public interface ActionListener extends EventListener { public void actionPerformed(ActionEvent e). dar metodele de tratare a evenimentului nu pot fi precizate nici ca prototip. dar nu ar putea extinde si clasa abstractã Dictionary . Un exemplu simplu este cel al metodelor considerate esentiale pentru orice clasã dictionar: pune pereche cheie-valoare in dictionar. Practic. iar clasa abstractã este o implementare partialã a interfetei. O interfatã care stabileste un tip comun poate fi atât de generalã încât sã nu continã nici o metodã. determinare maxim sau minim s.Florian Moraru – Programare Orientata pe Obiecte Pentru a semnala utilizarea gresitã a metodei "clone" pentru obiecte ne-clonabile. } Interfetele sunt preferabile în general claselor abstracte.a. In plus. care nu trebuie definite însã de la zero. se produce o exceptie de tip CloneNotSupportedException atunci când ea este apelatã pentru obiecte din clase care nu aderã la interfata Clonable. dar poate fi o functie polimorficã. pentru a permite anumite verificãri. care stabileste tipul “ascultãtor la evenimente”. Un exemplu este interfata Set (sau Collection) si clasa AbstractSet (AbstractCollection) care permit definirea rapidã de noi clase pentru multimi (altele decât HashSet si TreeSet). compatibile cu interfata dar care beneficiazã de metode mostenite de la clasa abstractã. O utilizare asemãnãtoare o are interfata Serializable. Incepând cu Java 2 aceleasi metode (plus încã câteva) au fost grupate în interfata Map.util").2 într-o clasã abstractã (Dictionary) care continea numai metode abstracte. cu acelasi nume. Aceste metode au fost reunite înainte de Java 1. pentru a facilita definirea de noi clase prin extinderea clasei abstracte. Un exemplu este interfata EventListener (pachetul "java. pentru a distinge clasele ale cãror obiecte sunt serializabile (care contin metode de salvare si de restaurare în / din fisiere) de clasele ale cãror obiecte nu pot fi serializate ( fãrã obiecte "persistente"). Interfata este extinsã de alte interfete.) este necesarã compararea de obiecte (de acelasi tip). Interfete ca Serializable si Clonable se numesc interfete de "marcare" a unui grup de clase ("tagging interfaces"). tip si argumente dar cu definitii diferite. pentru cã oferã mai multã libertate subclaselor. toate clasele cu date sunt serializabile. iar uneori sunt folosite împreunã într-un “framework” cum este cel al claselor colectie sau cel al claselor Swing (JFC): interfata defineste metodele ce ar trebui oferite de mai multe clase. Compararea de obiecte în Java In unele operatii cu vectori de obiecte (sortare. Avantajul solutiei interfatã în raport cu o clasã abstractã este evident atunci când definim un dictionar realizat ca un vector de perechi cheie-valoare: o clasã derivatã din clasa Vector ar putea mosteni metode utile de la superclasã. atunci ar fi fost necesarã o interfatã de "marcare" pentru a distinge clasele cu obiecte comparabile de clasele cu obiecte necomparabile. functia “compareTo” este destinatã comparatiei dupa criteriul cel mai “natural” (cel mai frecvent iar uneori si unicul criteriu). Functia de comparare depinde de tipul obiectelor comparate. Pot fi comparate cu metoda “compareTo” numai clasele care implementeazã interfata Comparable si deci care definesc metoda abstractã “compareTo”: 71 . Clasa Object nu contine o metodã de comparare pentru stabilirea unei relatii de precedentã între obiecte. dar dacã s-ar fi decis ca metoda "compareTo" sã facã parte din clasa Object. obtine valoarea asociatã unei chei date s. douã obiecte pot fi comparate dupã mai multe criterii (criteriile sunt variabile sau combinatii de variabile din aceste obiecte).a.

Date. Exemple de clase Java cu obiecte comparabile : Integer. String. } public int compareTo (Object obj) { Arc a = (Arc) obj. . Putem considera cã interfata Comparable adaugã metoda “compareTo” clasei Object si astfel creeazã o subclasã de obiecte comparabile. Exemplu: // determinare maxim dintr-un vector de obiecte oarecare public static Object max (Object [ ] a) { Comparable maxim = (Comparable) a[0].Florian Moraru – Programare Orientata pe Obiecte public interface Comparable { int compareTo (Object obj).sort” cu un argument foloseste implicit metoda “compareTo”. } // rezultat <0 sau = 0 sau >0 Clasele care declarã implementarea acestei interfete trebuie sã continã o definitie pentru metoda "compareTo". Exemplu: public class Byte extends Number implements Comparable { private byte value.). cu argument de tip Object. iar metoda “compareTo” este folositã la cãutarea într-o colectie ordonatã (“Collections. BigDecimal s.int y) { v=x. De exemplu.i++) if ( maxim.compareTo(a[i]) < 0) // daca maxim < a[i] maxim=(Comparable)a[i]. s. // arce egale dacã au aceleasi extremitati } public String toString () {return v+"-"+w. // arce egale dacã au costuri egale ! } } Metoda “equals” este folositã la cãutarea într-o colectie neordonatã (“contains”. // cost arc public Arc (int x. ceea ce poate conduce la rezultate diferite pentru cele douã metode de cãutare într-o listã de obiecte “Arc”. w=y.v && w==a.cost.((Byte) obj). “add” pentru multimi.a. } Functia “Arrays. // constructori. return (v==a.binarySearch”). } } Metoda ”compareTo” se poate aplica numai unor obiecte de tip Comparable si de aceea se face o conversie de la tipul general Object la subtipul Comparable.value.length. .w. . metode specifice public int compareTo( Object obj) { // compara doua obiecte Byte return this. Este posibil ca egalitatea de obiecte definitã de metoda “compareTo” sã fie diferitã de egalitatea definitã de metoda “equals”.value .w).i<a.a. Float. // noduri capete arc float cost. clasa “Arc” produce obiecte de tip “arc de graf cu costuri”: class Arc implements Comparable { int v. return maxim. 72 . } public boolean equals (Object obj) { Arc a= (Arc)obj. return cost – a. for (int i=0.

length.Florian Moraru – Programare Orientata pe Obiecte Pentru comparare de obiecte dupã alte criterii decât cel “natural” s-a introdus o altã functie cu numele “compare”. Object t2) { return ((String)t1). pentru cã este improbabil ca o clasã comparator sã mai mosteneascã si de la o altã clasã. } } . } // implicit abstracta. Functia “Arrays.sort” are si o formã cu douã argumente."1". De exemplu. Comparator c) { Object maxim = a[0].k++) if ( c. // linia o1. Object t2). // linia o2. vom defini urmãtoarea clasã comparator: class LengthComparator implements Comparator { public int compare (Object t1. // afisare matrice a } 73 . for (int i=0. // ordonare dupa lungime String [ ] a = {”patru”. Arrays. } // constructor cu un argument public int compare(Object o1. new LengthComparator()). . ."5"."4"} }. k<a. ne-statica ! Interfata Comparator putea fi si o clasã abstractã cu o singurã metodã.compareTo(c2).sort(a. for (int k=1.compare (maxim. pentru ordonarea unui vector de siruri dupã lungimea sirurilor. // ordonare dupa coloana i printMat (a). coloana c return c1. Functia care determinã maximul dintr-un vector de obiecte poate fi scrisã si astfel: public static Object max (Object a[ ]. a[k]) < 0) // c este un obiect comparator maxim = a[k].sort( a.i++) { // pentru fiecare coloana i Arrays. // numar coloana ce determina ordinea public CompCol (int col) { c=col. al doilea argument este de tipul Comparator si precizeazã functia de comparare (alta decât “compareTo”).”unu”}. {"1".new CompCol(i)). parte din interfata Comparator: public interface Comparator { int compare (Object t1."6"}. {"7". coloana c Comparable c2 =(Comparable) ((Object[ ])o2)[c].”trei”.i<3. Pentru ordonarea unei matrice de obiecte dupã valorile dintr-o coloanã datã putem defini o clasã comparator cu un constructor care primeste numãrul coloanei: class CompCol implements Comparator { int c."3". } Functia “max” cu douã argumente este mai generalã si poate fi folositã pentru a ordona un acelasi vector dupã diferite criterii (dupã diverse proprietãti).length() . // compara valorile din coloana c (liniile o1 si o2) } } Exemplu de utilizare a acestui comparator în functia de sortare : Object a[ ][ ] = { {"3".length()."2"}. Object o2) { Comparable c1 =(Comparable) ((Object[ ])o1)[c]. return maxim.((String)t2).

list().io. Exemplu de listare selectivã cu mascã a directorului curent: // clasa pentru obiecte filtru class FileTypeFilter implements FileFilter { String ext.0 boolean accept (File path. class Dir { public static void main (String arg[]) throws IOException { String dir =".length.ext = ext.println ("Directory of "+ d.getAbsolutePath()). // java.println (files[i]). } } Metoda “listFiles” produce un vector de obiecte File ce corespund fisierelor din directorul pentru care se apeleazã metoda.". // path=director(cale) } public interface FileFilter { // prezenta din jdk 1. // vector cu fisierele din directorul curent System. care sã spunã dacã obiectul primit ca argument este acceptat sau respins de filtru.io.io” poate genera obiecte ce corespund unor fisiere sau directoare. Cel mai folosit constructor din clasa File primeste un argument de tip String ce reprezintã numele unui fisier de date sau unui fisier director. dar pot fi folosite si în alte situatii.metoda “listFiles” cu argument de tip FileFilter sau FilenameFilter. } public boolean accept (File f) { 74 .metoda “list” cu argument de tip FilenameFilter .File(dir). String filename).Florian Moraru – Programare Orientata pe Obiecte Interfete pentru filtre Un filtru este un obiect care permite selectarea (sau respingerea) anumitor obiecte dintr-o colectie sau dintr-un vector. Ea este destinatã operatiilor de listare sau prelucrare a fisierelor dintr-un director si nu contine functii de citire-scriere din/în fisiere. Cea mai folositã metodã a clasei File este metoda “list” care produce un vector de obiecte String.out. pentru extragere selectivã de fisiere din director: . // extensie nume fisier public FileTypeFilter (String ext) { this.File d = new java.2 boolean accept (File path).io.i++) System. In bibliotecile Java filtrele se folosesc în clasa File pentru listare selectivã de fisiere dintr-un director. String [ ] files = d.out. // path=nume complet fisier (cu cale) } Metoda “accept” va fi apelatã de “list” sau “listFiles” pentru fiecare fisier din director si spune dacã acel fisier este sau nu este acceptat în vectorul creat de functie. Interfetele FileFilter si FilenameFilter contin o singurã metodã. i< files. for (int i=0.*. Clasa File din pachetul “java. // "numele" directorului curent File d =new File(dir). Un filtru poate contine o singurã metodã cu rezultat boolean. Metodele “list” si “listFiles” au si o variantã cu un argument de un tip interfatã ce specificã filtrul aplicat fisierelor. cu numele fisierelor din directorul specificat la construirea obiectului File. Exemplu de folosire a unui obiect File pentru afisarea continutului directorului curent: import java. numitã “accept”: public interface FilenameFilter { // prezenta din jdk 1. Utilizatorul are sarcina de a defini o clasã care implementeazã una din aceste interfete si de a transmite o referintã la un obiect al acestei clase fie metodei “list” fie metodei “listFiles”.

iar “sort” foloseste adresa continutã în obiectul comparator pentru a se referi înapoi la functia "compare". for (int i=0.equals(fname.i++) System. Situatia mai poate fi schematizatã si astfel: o functie A ("main". Collections. Comparator c) { Object maxim = enum. Exemplu: // determinare maxim dintr-o enumerare de obiecte necunoscute public static Object max (Enumeration enum.listFiles(new FileTypeFilter("java")). i< files. } 75 . deoarece este apelatã de metode existente (Collections. Adresa functiei X este primitã de B de la functia A. care va fi apelatã înapoi de B. De fiecare datã când A apeleazã pe B îi transmite si adresa functiei X. tip si numãr de argumente). La scrierea functiei de sortare (Collections.) apeleazã o functie B ("sort". iar B apeleazã o functie X ("compare") dintr-un grup de functii posibile. Putem deci sã scriem o functie care apeleazã functii încã nedefinite.sort. } } Acelasi filtru (dupã extensie fisier) în varianta cu interfata FilenameFilter: class DirFilter implements FilenameFilter { String ftype. In limbajul C se transmite un pointer explicit la o functie.indexOf(ftype) != -1.hasMoreElements()) { Object next = enum. public DirFilter (String ft) { ftype=ft.println (files[i]). ca metodã abstractã inclusã într-o interfatã.} public boolean accept (File dir. next) < 0) // apel functie de comparare maxim=next.nextElement(). if ( c.Florian Moraru – Programare Orientata pe Obiecte String fname = f. // primul element din enumerare while (enum. dar care respectã toate un prototip (tip rezultat. return ext. de ex.sort) nu se cunostea exact definitia functiei de comparare (pentru cã pot fi folosite diverse functii de comparare).getName(). Practic. s.indexOf('. dar s-a putut preciza prototipul functiei de comparare.a) dar trebuie furnizatã de aplicatia care foloseste una din aceste metode.'). String name) { return name. de ex. adresa obiectului comparator este transmisã “sort”. } } // Listare fisiere de un anumit tip class Dir { public static void main (String arg[ ]) throws IOException { File d =new File(“. // din directorul curent File [ ] files = d.).nextElement().max. if (pp==0) return false. } } // name= nume fisier // daca numele contine tipul ca subsir Functii “callback” Functia "compare" poartã numele de functie "callback".”).compare (maxim. int pp = fname.substring(pp+1)) .length.out. se transmite un pointer printr-un obiect ce contine o singurã functie. Metoda “sort” primeste adresa functiei “compare” (printr-un obiect dintr-o clasã care implementeazã interfata Comparator) ceea ce-i permite sã se refere "înapoi" la functia de comparare.

transmise tuturor subclaselor.. dar functia de comparare face parte din aplicatie. pentru un flux vector de octeti metoda "read" preia urmãtorul octet din vector. } Tehnica “callback” este folositã la scrierea unor clase de bibliotecã.length). Clasele Reader si Writer sunt mai noi si sunt preferabile claselor mai vechi InputStream si OutputStream pentru cã se lucreazã cu caractere în loc de octeti (byte). } public int available( ) throws IOException { return 0. vãzutã ca sir de octeti. care apeleazã metodele de citire/scriere octet. ByteInputStream. // alte metode } // citeste un octet (-1 la sfarsit de fisier) // citeste un bloc de octeti // nr de octeti rãmasi de citit Clasele concrete de tip flux de citire sunt toate derivate din clasa InputStream. public int read ( byte b[ ]) throws IOException { return read (b. Ca suport concret al unui flux de date Java se considerã urmãtoarele variante: fisier disc..Florian Moraru – Programare Orientata pe Obiecte return maxim.b.0. precum si alte metode care nu sunt definite dar nici nu sunt abstracte. In acest caz parserul SAX stie sã recunoascã atomi XML dar nu stie ce sã facã cu acesti atomi. apelatã de metodele “list” si “listFiles” din clasa de bibliotecã File. care recunoaste marcaje dintr-un fisier XML si apeleazã metode ce vor fi scrise de utilizatori pentru interpretarea acestor marcaje (dar care nu pot fi definite la scrierea analizorului lexical). ceea ce se reflectã si în numele lor: FileInputStream. Functiile “callback” sunt definite de cei care dezvoltã aplicatii dar sunt apelate de cãtre functii din clase predefinite (de bibliotecã). Clasele abstracte InputStream si OutputStream contin si metode neabstracte pentru citire si scriere blocuri de octeti. cu suport neprecizat. Un alt exemplu este un analizor lexical SAX. Ideea generalã este cã o parte din algoritmul implementat de clasa de bibliotecã depinde de specificul aplicatiei care foloseste acest algoritm. etc. dar definitã ca parte din aplicatie. Un algoritm de sortare foloseste o functie de comparare. care contin metodele abstracte “read” si “write”. în schimb aplicatia stie cum sã prelucreze atomii XML dar este scutitã de analiza documentelor XML (realizatã de parser). Metoda "read()" este redefinitã în fiecare din aceste clase si definitia ei depinde de tipul fluxului: pentru un flux fisier "read" este o metodã "nativã" (care nu este scrisã în Java si depinde de sistemul de operare gazdã). Operatiile elementare de citire si de scriere octet sunt aplicabile oricãrui tip de flux si de aceea au fost definite douã clase abstracte: o sursã de date InputStream (Reader) si o destinatie de date OutputStream (Writer). Functia “accept” din interfetele filtru este tot o functie “callback”. pentru cã ea comparã date din aplicatie. Clase abstracte si interfete pentru operatii de I/E Un flux de date Java ("input/output stream") este orice sursã de date (la intrare) sau orice destinatie (la iesire). vector de octeti (în memorie). Pentru fiecare tip de flux existã implementãri diferite ale metodelor abstracte “read” si “write” pentru citire/scriere de octeti. ale cãror functii apeleazã functii din programele de aplicatii. canal de comunicare între fire de executie (în memorie). Analizorul SAX impune aplicatiei sã foloseascã functii care respectã interfetele definite de analizor si apeleazã aceste functii “callback”. sir de caractere (în memorie). Exemplu: public abstract class InputStream { public abstract int read ( ) throws IOException. } . Clasa Reader diferã de InputStream prin utilizarea tipului char în loc de byte: public abstract class Reader { 76 . scrise ulterior de utilizatorii pachetului.

length). // alte metode } Clasele DataInputStream si DataOutputStream implementeazã interfetele DataInput si respectiv DataOutput. return (byte)(ch).. dar numãrul lor este mentinut la minim. . . public native void close () throws IOException. Aceste clase simplificã crearea si exploatarea de fisiere text si de fisiere binare.. cbuf.Florian Moraru – Programare Orientata pe Obiecte protected Reader() { . public native int read () throws IOException.. . ).read(). float readFloat () throws IOException. String readLine () throws IOException. else return cb[0]. In acest tip de fisiere (binarer) numerele se reprezintã în formatul lor intern (binar virgulã fixã sau virgulã mobilã) si nu se face nici o conversie la citire sau la scriere din/în fisier.a. int len) throws IOException. 0. long readLong () throws IOException. // alte metode } Interfata DataInput reuneste metode pentru citire de date de tipuri primitive: public interface DataInput { char readChar () throws IOException. int off. DataInput { private native void open (String name. public native long length () throws IOException. 0. boolean write) throws IOException. } abstract public int read(char cbuf[ ]. Exemplu de variantã modificatã a clasei DataInputStream : public class DataInputStream extends InputStream implements DataInput { InputStream in. if (ch < 0) throw new EOFException().. public native void write (int b) throws IOException. // “in” poate fi orice fel de flux (ca suport fizic) public DataInputStream (InputStream in) { this.. } Clasa RandomAccessFile implementeazã ambele interfete si deci asigurã un fisier în care se poate scrie sau din care se poate citi orice tip primitiv. . abstract public void close() throws IOException. } // ptr a interzice instantierea clasei public int read() throws IOException { // citire caracter urmator (-1 la sfarsit de fisier) char cb[ ] = new char[1]. deci o parte din metodele claselor de I/E sunt metode "native". ce contin date de un tip primitiv ( numere de diferite tipuri s.. } public int read(char cbuf[ ]) throws IOException { // citire bloc de octeti return read(cbuf. Operatiile cu fisiere depind de sistemul de operare gazdã. 1) == -1) return -1. if (read(cb.in=in. . Un fragment din clasa RandomAccessFile aratã astfel: public class RandomAccessFile implements DataOutput. int readInt () throws IOException. } // constructor public final byte readByte() throws IOException { int ch = in. } public final short readShort() throws IOException { 77 .

care suportã metoda “readLine”. care precizeazã suportul fizic al datelor. } // citeste un numar intreg public float readFloat ( ) { ...in” este de un subtip (anonim) al tipului InputStream si corespunde tastaturii ca flux de date.read().out.readLine()) != null) { // dacã nu s-a introdus ^Z (EOF) System. putem crea un obiect de tip DataInputStream .. return (short)((ch1 << 8) + (ch2 << 0)). } } 78 . System.out. } // citeste o linie public int readInt ( ) { . // citeste doi octeti if ((ch1 | ch2) < 0) throw new EOFException(). // asamblare octeti pe 16 biti } public String readLine ( ) { .in)).. } Varianta pentru citire de linii de la tastaturã : public static void main (String arg[ ]) throws IOException { BufferedReader stdin = new BufferedReader(new InputStreamReader (System. } // citeste un numar real . Pentru a citi linii de la consolã (terminate cu Enter).. int ch1 = in. int ch2 = in.in). String line= f. while ( (line=stdin.read(). Pentru obiectul adresat de variabila “System.println (“ S-a citit: "+ line). Exemplu: // citire linie de la tastatura public static void main (String arg[]) throws Exception { DataInputStream f = new DataInputStream(System. Clasa DataInputStream nu are constructor fãrã argumente si nici variabile care sã specifice suportul datelor.Florian Moraru – Programare Orientata pe Obiecte InputStream in = this. Variabila publicã “System.in.in” se poate folosi metoda “read” pentru citirea unui octet (caracter) dar nu se poate folosi o metodã de citire a unei linii..println (line). .readLine(). String line. // alte metode } Obiecte de tip DataInputStream nu pot fi create decât pe baza altor obiecte. .

este un tip de date abstract care reuneste un grup de obiecte de tip Object. Colectiile se folosesc pentru memorarea si regãsirea unor date sau pentru transmiterea unui grup de date de la o metodã la alta. în sensul Java. în care fiecare element are un succesor si un predecesor si este localizabil prin pozitia sa în listã (un indice întreg). // copiere colectie intr-un vector } Din interfata Collection sunt derivate direct douã interfete pentru tipurile abstracte: . dictionare). // produce un iterator pentru colectie boolean containsAll(Collection c). care respectã aceste interfete impuse si sunt compatibile cu cele existente (pot fi înlocuite unele prin altele). // adauga un element la colectie boolean remove(Object element). colectiei c boolean retainAll(Collection c).dictionar) sunt prevãzute câte douã clase concrete care implementeazã interfetele respective. Pentru fiecare din cele 3 structuri de date abstracte (listã. Colectii de obiecte Object Infrastructura claselor colectie O colectie este un obiect ce contine un numãr oarecare. realizate fie cu elemente de tip Object. fie cu clase sablon (cu tipuri parametrizate) . toate conforme cu anumite interfete ce reprezintã tipuri abstracte de date (liste. . // sterge continut colectie Object[ ] toArray(). // dimensiune colectie (nr de elemente) boolean isEmpty(). Nu toate aceste operatii trebuie implementate obligatoriu de clasele care implementeazã interfata Collection. Familia claselor colectie din Java este compusã din douã ierarhii de clase : .Florian Moraru – Programare Orientata pe Obiecte 7. Interfata Collection contine metode aplicabile pentru orice colectie de obiecte. Colectiile Java sunt structuri de date generice. multimi. Un utilizator îsi poate defini propriile clase colectie. // elimina un element din colectie Iterator iterator(). listã dublu înlãntuitã. // adauga elem. O colectie. care depind de structura fizicã a colectiei). din colectia c boolean addAll(Collection c). // retine in colectie numai elem.Ierarhia care are la bazã interfata Map. // daca colectia contine elem. fiecare pereche trebuie sã aibã o cheie unicã. . 79 . Structurile de date concrete folosite pentru implementarea tipurilor de date abstracte sunt : vector extensibil.List pentru secvente de elemente. // verifica daca colectie vida boolean contains(Object element). multime. variabil de obiecte. Un dictionar (o asociere ) este o colectie de perechi cheivaloare (ambele de tip Object). Infrastructura colectiilor (Collection Framework) oferã clase direct utilizabile si suport pentru definirea de noi clase (sub formã de clase abstracte). din c void clear(). // daca colectia contine un obiect dat boolean add(Object element). //elimina din colectie elem. tabel de dispersie si arbore binar. deci nu pot fi douã perechi identice. Urmeazã descrierea completã a interfetei Collection : public interface Collection { // operatii generale ptr orice colectie int size().Ierarhia care are la bazã interfata Collection. Clasele abstracte existente fac uz de iteratori si aratã cã aproape toate metodele unei clase colectie pot fi scrise numai folosind obiecte iterator (cu exceptia metodelor de adãugare obiect la colectie si de calcul numãr de elemente din colectie. o clasã care nu defineste o metodã optionalã poate semnala o exceptie la încercarea de apelare a unei metode neimplementate.Set pentru multimi de elemente distincte. numite si elemente ale colectiei. din c la colectie boolean removeAll(Collection c).

“binarySearch”.println (list. Clasele multime predefinite si care implementeazã interfata Set sunt: HashSet : pentru o multime neordonatã realizatã ca tabel de dispersie TreeSet : pentru o multime ordonatã. care produce un sir cu toate elementele colectiei. dar implementãrile acestei interfete asigurã unicitatea elementelor unei multimi. // obiect minim din colectia col public static Object min (Collection col . De asemenea sunt prevãzute metode de trecere de la vectori intrinseci de obiecte (Object [] ) la colectii de obiecte si invers: functia “Arrays.w) 80 .Florian Moraru – Programare Orientata pe Obiecte Toate clasele colectie instantiabile redefinesc metoda “toString”. // ordonare lista lst public static void sort (List lst. “reverse”. Metoda de adãugare a unui obiect la o multime "add" verificã dacã nu existã deja un element identic. Afisarea continutului unei colectii se poate face printr-o singurã instructiune. de tip Object[].a.out. List list = Arrays. cu un argument vector intrinsec de obiecte si rezultat de tip List si functia “toArray” din clasa AbstractCollection. arc = new HashSet(). Tabelul de dispersie asigurã cel mai bun timp de cãutare. Clasa Collections contine metode statice pentru mai multi algoritmi "generici" aplicabili oricãrei colectii ("min".”trei”}.asList(sv). Exemple în forma mai veche dar mai simplã: public static Object min (Collection col ).toString()). Comparator cmp).int w) { // daca exista arcul (v.toArray().n =n. } public void addArc (int v. Toate colectiile au iteratori dar nu si clasele dictionar (se poate însã itera pe multimea cheilor sau pe colectia de valori).println (list).w)). Ei foloseau initial tipul generic Object si metodele polimorfice equals. iar arborele echilibrat permite pãstrarea unei relatii de ordine între elemente si un timp de cãutare bun.add (new Arc (v. // System.int w) { // adauga arcul (v. Exemplul urmãtor foloseste o multime de tipul HashSet pentru un graf reprezentat prin multimea arcelor (muchiilor) : class Graph { // graf reprezentat prin multimea arcelor private int n.. separate prin virgule si încadrate de paranteze drepte. String aux[] = (String[ ]) list. Exemplu: String sv[] = {“unu”. public static void sort (List lst). } public boolean isArc (int v. compareTo s. // lista de arce public Graph ( int n) { this. nici LinkedList ! System. De aceea obiectele introduse în multime trebuie sã aibã metoda “equals” redefinitã. "max") sau numai listelor (“sort”. pentru a nu se introduce duplicate în multime.”doi”. Multimi de obiecte O multime este o colectie de elemente distincte pentru care operatia de cãutare a unui obiect în multime este frecventã si trebuie sã aibã un timp cât mai scurt. // numar de noduri in graf private Set arc. dar acum folosesc tipuri parametrizate (din versiunea 5). Comparator cmp). // nu este nici ArrayList.asList”. Interfata Set contine exact aceleasi metode ca si interfata Collection. // aux identic cu sv O a treia ierarhie are la bazã interfata Iterator. “shuffle”). pentru ca obiecte diferite sã nu aparã ca fiind egale la compararea cu “equals”. realizatã ca arbore binar echilibrat automat (“Red-Black Tree”).out.w) la graf arc. pentru metodele specifice oricãrui iterator asociat unei colectii sau unui dictionar.

addAll (s2) s1. se vor folosi pe cât posibil variabile de tip Collection sau Set si nu variabile de tip HashSet sau TreeSet (numai la construirea obiectului colectie se specificã implementarea sa). // un cuvant din fisier while ( sc. extinde interfata Set cu câteva metode aplicabile numai pentru colectii ordonate: Object first(). // subSet (first(). Interfata SortedSet.println ("unice: " + unice). // toate cuvintele distincte din text Set dupl =new TreeSet (). // primul si ultimul element din multime SortedSet subSet(Object from. } } In general se recomandã programarea la nivel de interfatã si nu la nivel de clasã concretã.to) SortedSet tailSet (Object from) // subSet (from. // este o aparitie multipla } System.w)). aux.add (word).Florian Moraru – Programare Orientata pe Obiecte return arc.toString().retainAll (s2) s1. dar clasa LinkedHashSet contine o metodã “toString” care produce un sir cu elementele multimii în ordinea adãugãrii lor la multime. Asadar. } public String toString () { return arc. Set aux = new HashSet(s1). pentru a permite afisarea cuvintelor în ordine lexicograficã.add (word) ) // daca a mai fost in “toate” dupl. // afiseaza cuvinte cu o singura aparitie } Nici una din multimile HashSet sau TreeSet nu pãstreazã ordinea de adãugare a elementelor la multime. S-a folosit o multime ordonatã. // elimina cuvinte multiple System.println ("multiple: "+ dupl). sdif. unice.removeAll (dupl). // reuniune s1+s2 // intersectie s1*s2 in aux // reuniune minus intersectie Exemplul urmãtor aratã cum putem folosi douã multimi pentru afisarea cuvintelor care apar de mai multe ori si celor care apar o singurã datã într-un text. // scoate urmatorul cuvant if ( ! toate.implementatã de clasa TreeSet.removeAll(aux).out.containsAll (s2) s1. String word="". // cuvintele cu aparitii multiple Scanner sc = new Scanner (new File(arg[0])). Operatiile cu douã colectii sunt utile mai ales pentru operatii cu multimi: s1. Object last(). simdif. public static void main (String arg[ ]) throws IOException { Set toate = new HashSet (). // submultimea definita prin 2 valori SortedSet headSet(Object to). // afisare cuvinte repetate Set unice = new TreeSet (toate).next(). last()) 81 .contains (new Arc(v.addAll(s2).hasNext() ) { // repeta cat mai sunt cuvinte in fisier word= sc.removeAll (s2) // true dacã s1 contine pe s1 (includere de multimi) // reuniunea multimilor s1 si s2 (s1=s1+s2) // intersectia multimilor s1 si s2 (s1=s1*s2) // diferenta de multimi (s1= s1-s2) Diferenta simetricã a douã multimi se poate obtine prin secventa urmãtoare: Set sdif = new HashSet(s1). Object to).out.retainAll (s2). dar are aceleasi performante la cãutare ca HashSet.

Metode pentru crearea de iteratori specifici listelor: listIterator (). la elementele listei. lastIndexOf (object) .setElementAt (aux .2) Object get(int) Object set (i.get(j)). // negasit } 82 .Metodã de extragere sublistã dintr-o listã: List subList(int from.set (i.Metode pentru determinarea pozitiei unde se aflã un element dat. int to). de aceea metodele “sort” si “binarySearch” din clasa Collections au argument de tip List si nu Collection. deci cãutarea primei si ultimei aparitii a unui obiect într-o listã: indexOf (object). else return mid.Florian Moraru – Programare Orientata pe Obiecte Liste secventiale Interfata List contine câteva metode suplimentare fatã de interfata Collection : .1) Object elementAt (int) Object setElementAt (Objext. Noile metode au nume mai scurte si o altã ordine a argumentelor în metodele "add" si "set": Forma veche (1. rezultatul este pozitia unde ar trebui inserat obiectul cãutat astfel ca lista sã rãmânã ordonatã ( decalatã cu 1 si cu minus). int i. Metoda “set” se poate folosi numai pentru modificarea unor elemente existente în listã. public static int binSearch (List list. Object key) { int low = 0. int) Forma nouã (1. high = list. // a.Metode pentru acces pozitional. j) } De observat cã metodele "set" si "remove" au ca rezultat vechiul obiect din listã. int j) { // din clasa Collections Object aux = a. // a.setElementAt (a.set (j. add (index. preferatã când sunt multe inserãri sau stergeri de elemente în listã. set (index.a. i) a. Algoritmii de ordonare si de cãutare binarã sunt exemple de algoritmi care necesitã accesul direct. // gasit } return -(low + 1).aux). else if (cmp > 0) high = mid . if (cmp < 0) low = mid + 1.object). clasei Vector i-au fost adãugate noi metode pentru a o face compatibila cu interfata List. nu si pentru adãugare de noi elemente.1. // a. // acces prin indice int cmp = ((Comparable)midVal). remove (index) .elementAt(i) a. listIterator (index) . Urmeazã o variantã a metodei “binarySearch”.compareTo(key). care a fost modificat sau eliminat. while (low <= high) { int mid =(low + high)/2. In plus. pe baza unui indice întreg care reprezintã pozitia : get (index). object). Object) void add (i. în caz de obiect negãsit.Object) Exemplu de functie care schimbã între ele valorile a douã elemente i si j: static void swap (List a. Object midVal = list.get(mid). prin indici. preferabilã pentru acces aleator frecvent la elementele listei. LinkedList listã dublu înlãntuitã.elementAt(j) .size()-1. Existã douã implementãri pentru interfata List: ArrayList listã vector. int) void insertElementAt (Object.get(i).

Din versiunea 5 au mai fost adãugate interfetele Queue si Deque cu câteva metode noi. getFirst. // adauga arc la coada pq … // afisare coada in ordinea prioritatilor (costului arcelor) while ( ! pq. Interfata Deque extinde interfata Queue cu metode de acces si la primul si la ultimul element din listã: addFirst.4) Definitia simplificatã a interfetei Map este urmãtoarea: 83 . rapid la vectori dar mai putin eficient în cazul listelor înlãntuite. elementul din fatã (obtinut prin una din metodele “peek”. LinkedList.w. dar o cãutare secventialã în liste secventiale.cost)). “poll” sau “remove”) este cel cu prioritatea minimã. addFirst(Object). în plus fatã de clasa abstractã AbstractList. care este extinsã de clasa LinkedList dar nu si de clasa ArrayList. Cele mai multe clase de tip coadã (ca BlockingQueue si SynchronousQueue) au fost introduse. 1. Coada cu prioritãti este implementatã ca un vector “heap” extensibil (fãrã limite de capacitate).binarySearch.key). offerLast. în ipoteza cã metoda “compareTo” din clasa “Arc” comparã costuri: PriorityQueue<Arc> pq = new PriorityQueue<Arc>().isEmpty()) System. Clasa LinkedList contine.out.Florian Moraru – Programare Orientata pe Obiecte Exemplu de utilizare a functiei de cãutare într-o secventã care adaugã un obiect la o listã ordonatã. Pentru aplicatiile cu structuri de date sunt interesante clasele ArrayDeque si PriorityQueue. LinkedHashMap tabel de dispersie cu mentinere ordine de introducere (din vers. printre care AbstractQueue. în care cheile sunt unice . addLast. Existã trei implementãri pentru interfata Map: HashMap dictionar realizat ca tabel de dispersie. TreeMap dictionar realizat ca arbore echilibrat. interfete implementate de mai multe clase. în pachetul java. getLast(). 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. removeFirst.concurrent pentru programare cu fire de executie concurente.binarySearch (alist. “poll” si “remove” cu eliminare din coadã). De aceea s-a definit o clasã abstractã AbstractSequentialList. cu cel mai bun timp de cãutare. astfel ca lista sã rãmânã ordonatã: int pos = Collections. utile pentru cazuri particulare de liste (stive. cu parametru de tipul general List. In exemplul urmãtor se adaugã si se scoate dintr-o coadã de arce ordonatã dupã costuri. … pq. alãturi de alte clase. peek ( offerFirst.): getFirst(). cozi etc. cu metode de acces la primul element din colectie (din coadã) cu si fãrã exceptie în caz cã nu existã (“element” si “peek” fãrã eliminare din coadã. poll. removeFirst().util. // se adauga key inainte de primul element mai mare ca el Accesul pozitional este un acces direct.add (-pos-1. removeLast().remove()). addLast(Object). getLast si variantele care nu produc exceptii: offer. PriorityQueue.…). Metoda staticã Collections. care garanteazã ordinea de enumerare. if (pos < 0) // daca key nu exista in lista alist. recunoaste tipul de listã si face o cãutare binarã în vectori. mai important. key). Indiferent de ordinea de adãugare la coadã.println( pq. urmãtoarele metode. // scoate si sterge din coada Clase dictionar Interfata Map contine metode specifice operatiilor cu un dictionar de perechi cheie valoare. ArrayDeque Interfata Queue extinde interfata Collection cu o metodã de adãugare “offer” care nu produce exceptie dacã nu mai este loc în colectie si.add (new Arc(v. removeLast. clasa LinkedList implementeazã acum si interfata Deque si de aceea nu existã o clasã LinkedDeque.

“getValue” si “setValue”.Florian Moraru – Programare Orientata pe Obiecte public interface Map { Object put(Object key. StringTokenizer st = new StringTokenizer (new String (text)). // verifica daca exista o cheie data boolean containsValue(Object value).ONE). // o constanta public static void main (String arg[]) { Map dic = new TreeMap (). String word. // multimea perechilor cheie-valoare } Metoda “get” are ca rezultat valoarea asociatã unei chei date sau null dacã cheia datã nu se aflã în dictionar. // multimea cheilor Collection values(). In exemplul urmãtor se afiseazã numãrul de aparitii al fiecãrui cuvânt distinct dintr-un text. // verifica daca exista o valoare data int size().intValue()+1)). // eliminã pereche cu cheie datã boolean containsKey(Object key). Metoda “put” adaugã sau modificã o pereche cheie-valoare si are ca rezultat valoarea asociatã anterior cheii date (perechea exista deja în dictionar) sau null dacã cheia perechii introduse este nouã. // afisare dictionar } } Cheile dintr-un dictionar pot fi extrase într-o multime cu metoda “keySet”.println (dic). Entry este o interfatã inclusã în interfata Map si care are trei metode: “getKey”. // prima aparitie else dic.Metoda “entrySet” produce o multime echivalentã de perechi "cheie-valoare". new Integer (nrap. String text =“ trei unu doi trei doi trei “. De observat cã metodele “entrySet”.out. class FrecApar { // frecventa de aparitie a cuvintelor intr-un text private static final Integer ONE= new Integer(1). iar valorile din dictionar pot fi extrase într-o colectie (o listã) cu metoda “values”.get(word).“keySet” si “values” (definite în AbstractMap) creeazã doar imagini noi asupra unui dictionar si nu alte colectii de obiecte. // dimensiune dictionar (nr de perechi) boolean isEmpty(). // alta aparitie } System. folosind un dictionar-arbore pentru afisarea cuvintelor în ordine. Object value). Testul de apartenentã a unei chei date la un dictionar se poate face fie direct prin metoda “containsKey”. void clear(). // extrage valoare asociatã unei chei date Object remove(Object key). fie indirect prin verificarea rezultatului operatiei “get”. Clasele care genereazã obiecte memorate într-un obiect HashMap sau HashSet trebuie sã redefineascã metodele "equals" si "hashCode".hasMoreTokens()) { word = st. // colectia valorilor din dictionar Set entrySet(). // pune o pereche cheie-valoare Object get(Object key). // elimina toate perechile din dictionar Set keySet().v)” în cazul cã existã o pereche cu cheia “k” în dictionar este acela de înlocuire a valorii asociate cheii “k” prin noua valoare “v” (valoarea înlocuitã este transmisã ca rezultat al metodei “put”).put (word. astfel încât sã se poatã face cãutarea la egalitate dupã codul de dispersie. orice modificare în dictionar se va 84 . Efectul metodei “put (k.put (word. unde clasa pereche are tipul Entry. Integer nrap = (Integer) dic. if (nrap == null) dic.nextToken(). while ( st.

// ordonare cu comparator primit din afara return modif.sort” deoarece aceasta apeleazã metoda “set” si apare o recursivitate indirectã infinitã de forma set -> sort -> set -> sort -> set ->… 85 .asList (tab).unu] } Putem sã ne definim vectori sau liste ordonate automat. cmp=comp. // adresa obiectului comparator public SortedArray () { super()."trei".sort (this). în timp ce la HashMap ordinea este aparent întâmplãtoare (ea depinde de capacitatea tabelei de dispersie. iar tabelul de dispersie asigurã un timp bun de regãsire dupã cheie. // retine adresa obiectului comparator } public boolean add (Object obj) { // adaugare la vector ordonat boolean modif= super. Diferenta dintre clasele HashMap si LinkedHashMap apare numai în sirul produs de metoda “toString” a clasei: la LinkedHashMap ordinea perechilor în acest sir este aceeasi cu ordinea de introducere a lor în dictionar. de exemplu).cmp). Acest lucru nu este posibil prin folosirea metodei “Collections. // daca s-a modificat colectia dupa adaugare if ( ! modif) return modif."doi".”cinci”}. Listele sunt implicit neordonate (se adaugã numai la sfârsit de listã) si pot fi ordonate numai la cerere. de functia “hashCode” si de ordinea de introducere a cheilor). // [cinci. fãrã ca sã apelãm din nou metodele respective.out. Collections. sau alte alte structuri compatibile cu interfata List si care asigurã ordinea (un arbore binar. // ordonare dupa criteriul natural else Collections. System.} public SortedArray (Comparator comp) { super(). prin apelul unei metode statice (Collections.sort). Metodele clasei imagine sunt definite apoi pe baza iteratorului.patru.add(obj).doi. ca si dictionarele. } } Problema clasei anterioare este cã ar trebui redefinitã si metoda “set” care modificã elemente din listã.trei.println (lista).Florian Moraru – Programare Orientata pe Obiecte reflecta automat în aceste “imagini”. Pentru pãstrarea ordinii de adãugare se foloseste o listã înlãntuitã. Multimile. Exemplu de ordonare a unei liste: public static void main (String arg[ ]) { String tab[ ] = {"unu".”patru”. Exemplu de încercare de a defini o clasã pentru o multime ordonatã implementatã printr-un vector (dupã modelul clasei TreeSet): public class SortedArray extends ArrayList implements List { Comparator cmp=null. care dã acces la datele din dictionar. Colectii ordonate Problema ordonãrii este rezolvatã diferit pentru liste fatã de multimi si dictionare.sort (this. // colectia nemodificata ramane ordonata if ( cmp==null) Collections. pentru a mentine lista ordonatã.sort (lista). O imagine este creatã printr-o clasã care defineste un iterator pe datele clasei dictionar si nu are propriile sale date. au o variantã de implementare (printr-un arbore binar ordonat) care asigurã mentinerea lor în ordine dupã orice adãugare sau eliminare de obiecte. List lista = Arrays.

care implementeazã (optional) interfata SortedSet. min. // e1. Adãugarea sau modificarea valorilor dintr-un arbore se face cu mentinerea ordinii si nu necesitã reordonarea multimii sau dictionarului. Rezultatul metodei "compare" este acelasi cu al metodei "compareTo". Se pot defini si alte tipuri de colectii sau dictionare ordonate.getValue()).a... // comparator pentru ordine inversa lui cmp Clase iterator Una din operatiile frecvente asupra colectiilor de date este enumerarea tuturor elementelor colectiei (sau a unei subcolectii) în vederea aplicãrii unei prelucrãri fiecãrui element obtinut prin enumerare. Clasele JDK cu date (String.e2=prechi cheie-val return ((Integer)e2.entrySet(). Pentru ordonarea dupã un alt criteriu decât cel natural si pentru ordonarea dupã mai mlte criterii se va folosi o clasã compatibilã cu interfata Comparator. Pentru a utiliza o functie “compare” trebuie definitã o clasã care contine numai metoda "compare". Collections. deci un numãr negativ dacã ob1<ob2. // sau se adauga cu metoda addAll Iteratorul unei colectii ordonate parcurge elementele în ordinea dictatã de obiectul comparator folosit mentinerea colectiei în ordine.sort (entries.Entry)o2. O problemã comunã colectiilor ordonate este criteriul dupã care se face ordonarea.Florian Moraru – Programare Orientata pe Obiecte O multime ordonatã este de tipul TreeSet iar un dictionar ordonat este de tipul TreeMap. min. definite de utilizatori. este ordinea numericã fãrã semn pentru caractere si este ordinea lexicograficã pentru obiecte de tip String. în ordinea inversã a numãrului de aparitii: Set entset = dic. Exemplu de ordonare a unei liste de nume (distincte) prin crearea si afisarea unei multimi ordonate: SortedSet lst = new TreeSet (lista). } } Din versiunea 5 s-a introdus în clasa Collections si un comparator pentru ordinea inversa: Comparator reverseOrder(Comparator cmp). Date s. // clasa comparator obiecte Integer in ordine inversa celei naturale class Comp implements Comparator { public int compare (Object o1. . max) ce necesitã compararea de obiecte. Anumite metode statice (sort. realizate ca douã interfete diferite si care au aplicabilitate distinctã.getValue()). Un argument de tip Comparator apare în constructorii unor clase si în câteva metode din clasa Collections (sort. ArrayList entries = new ArrayList(entset).Entry)o1.Entry e1= (Map. Exemplu de ordonare a dictionarului de cuvinte-frecventã creat anterior. parte a interfetei Comparable. Pentru alte clase. Realizarea concretã a enumerãrii depinde de tipul colectiei si foloseste un cursor care înainteazã de la un element la altul. într-o anumitã ordine (pentru colectii neliniare). zero dacã ob1==ob2 si un numãr pozitiv dacã ob1>ob2.compareTo ((Integer) e1. trebuie implementatã interfata Comparable prin definirea metodei "compareTo". dacã obiectele clasei pot fi comparate (si sortate). Integer. Obiectele introduse într-o colectie TreeSet sau TreeMap trebuie sã apartinã unei clase care implementeazã interfata Comparable si contine o definitie pentru metoda “compareTo”. new Comp()). Object o2) { Map.) implementeazã interfata Comparable si deci contin o metoda "compareTo" pentru o ordonare "naturalã" ( exceptie face clasa Boolean. care depinde de tipul obiectelor comparate. deci functia de comparatie. Sunt prevãzute douã solutii pentru aceastã problemã. max s. e2= (Map. ale cãrei obiecte nu sunt comparabile). respectiv interfata SortedMap. Cursorul este un indice întreg în 86 .) si unele metode din clase pentru multimi ordonate apeleazã în mod implicit metoda "compareTo".a. Ordinea naturalã este ordinea valorilor algebrice (cu semn) pentru toate clasele numerice. iar un obiect al acestei clase se transmite ca argument functiei.

it. Generalizarea modului de enumerare a elementelor unei colectii pentru orice fel de colectie a condus la aparitia claselor cu rol de “iterator” fatã de o altã clasã colectie.add( "x"). Exemplu: public static void sort (List list) { Object a[] = list. for (int j=0. i. un vector) pot exista mai multi iteratori. // transforma lista in vector intrinsec Arrays.iterator().next(). 87 . Iterator it = a. j++) { // modificare elemente din lista i. Un obiect iterator este singura posibilitate de enumerare a elementelor unei multimi si o alternativã pentru adresarea prin indici a elementelor unei liste. } Pentru fiecare clasã concretã de tip colectie existã o clasã iterator. } // daca exista un element urmator in colectie // extrage element curent si avans la urmãtorul // elimina element curent din colectie (optional) De observat cã modificarea continutului unei colectii se poate face fie prin metode ale clasei colectie. dar nu ar trebui folosite simultan ambele moduri de modificare.set(a[j]). Pentru colectii mai complexe. // ordonare vector intrinsec (mai eficienta) ListIterator i = list. j<a. pentru cã fiecare obiect iterator are o variabilã cursor proprie. programatorul este obligat sã creeze întâi obiectul colectie si numai dupã aceea obiectul iterator. fie prin metoda “remove” a clasei iterator. iar obiectele iterator se obtin prin apelarea unei metode a clasei colectie (metoda “iterator”). Object next(). Interfata Iterator contine metode generale pentru orice iterator: public interface Iterator { boolean hasNext(). In exemplul urmãtor apare o exceptie de tip ConcurrentModificationException: ArrayList a = new ArrayList(). Clasele iterator nu sunt direct instantiabile (nu au constructor public). care progreseazã în mod diferit în cadrul colectiei.Pozitionarea pe primul element din colectie . enumerarea poate folosi indici (pentru vectori) si pointeri (pentru noduri legate prin pointeri).Detectarea sfârsitului colectiei (test de terminare a enumerãrii).length.next(). dar multimea perechilor si multimea cheilor din dictionar au iteratori. } } Pentru enumerarea unei colectii se va folosi o variabilã de tip Iterator si nu metoda iterator().Obtinerea elementului curent din colectie . Mai multi algoritmi generici realizati ca metode statice (în clasa Collections) sau ca metode ne-statice din clasele abstracte folosesc un obiect iterator pentru parcurgerea colectiei.hasNext()) { a.Florian Moraru – Programare Orientata pe Obiecte cazul unui vector sau un pointer (o referintã) pentru o listã înlãntuitã sau pentru un arbore binar. Un dictionar nu poate avea un iterator. while (it. Toate clasele iterator rebuie sã includã urmãtoarele operatii: .sort(a).listIterator(). deoarece fiecare apel al metodei reinitializeazã iteratorul prin pozitionare pe primul element.toArray(). In felul acesta. Orice clasã colectie Java 2 poate avea o clasã iterator asociatã.Pozitionarea pe urmãtorul element din colectie . cum ar fi vectori de liste. void remove(). Pentru un acelasi obiect colectie (de ex.

O clasã dictionar (Map) nu are un iterator asociat. while (e. nextIndex().getValue()).iterator(). precum si douã colectii performante pentru elemente de un tip enumerat: EnumSet si EnumMap 88 .println ( e. add(Object o). previousIndex().5 s-a introdus tipul de date definit prin enumerare (si cuvântul cheie enum).next())) modified = true.println (i. set (Object o). } public boolean addAll (Collection c) { boolean modified = false. next().listIterator (a. } public Object[] toArray() { Object[] result = new Object[size()].size()). definitã în interiorul interfetei Map: for (Iterator it= map.equals(e.iterator().hasNext()) { if(add(e.next(). for (ListIterator i= a. i. previous(). dar este posibilã extragerea unei multimi de chei sau unei multimi de perechi cheie-valoare dintr-un dictionar.out. Secventa urmãtoare face o enumerare a perechilor cheie-valoare dintr-un dictionar “map”.next())) return true. for (int i=0.Entry e = (Map. iar aceste multimi pot fi enumerate. return false.hasNext(). hasPrevious().Florian Moraru – Programare Orientata pe Obiecte Fragmentul urmãtor din clasa AbstractCollection aratã cum se pot implementa metodele unei clase colectie folosind un iterator pentru clasa respectivã: public abstract class AbstractCollection implements Collection { public boolean contains(Object o) { // fara cazul o==null Iterator e = iterator(). Exemplu de parcurgere a unei liste de la coadã la capãt: List a = new LinkedList(). } Definirea de noi clase colectie Din versiunea 1.out. } return modified. i++) result[i] = e.next(). } } Interfata ListIterator contine metode pentru traversarea unei liste în ambele sensuri si pentru modificarea elementelor enumerate : hasNext().entrySet().hasNext(). Metoda “listIterator()” pozitioneazã cursorul pe începutul listei. Iterator e = iterator(). e. ) System. System.previous()). Iterator e = c. return result. while (e. iar metoda “listIterator(index)” pozitioneazã initial cursorul pe indicele specificat.) { Map.hasPrevious().getKey()+”:”+e. it. folosind interfata publicã Entry.Entry) it. remove().hasNext()) if (o.

clear.Sunt necesare alte tipuri abstracte de date neimplementate în SDK: graf.add (new LinkedList()). remove. // adauga w la vecinii lui v } public boolean isArc (int v. clasa DefaultTreeModel pentru un (model de) arbore si interfete pentru alte eventuale implementãri. ceea ce simplificã definirea si utilizarea colectiilor de colectii.get(v). Clasele AbstractSet si AbstractList extind clasa AbstractCollection. Desi ele au fost create special pentru clasa JTree care asigurã vizualizarea acestor arbori. s. containsAll.int w) { // adauga arcul (v. . Object value) { 89 . elementele unei colectii pot fi de orice tip derivat din Object.swing. Definirea unor noi clase colectie poate fi necesarã din mai multe motive: . a = new ArrayList (n). toArray. pot fi folosite si separat de clasa JTree.a. desi unele din ele sunt redefinite din motive de performantã. // multime-vector de perechi cheie-valoare public ArrayMap (int n) { entries = new ArraySet(n). addAll. deci pot fi la rândul lor colectii. // daca contine nodul w } public String toString () { // un sir cu toate listele de adiac.a. Astfel se mostenesc toate functiile mentionate. contains. cu facilitãti de expandare/contractie noduri. Aceste noi clase colectie ar trebui sã se conformeze cadrului creat de interfetele Collection.contains (new Integer(w)). toString.int w) { // verifica daca arc de la v la w List vlist = (List)a. // adauga o lista vida de vecini } public void addArc (int v. Biblioteca de clase Java mai contine si clase pentru crearea si prelucrarea de arbori multicãi. Se foloseste un vector de liste înlãntuite: public class Graph { // graf reprezentat prin liste de adiacente private int n.n =n. Exemplu de clasã pentru obiecte dictionar realizate ca vectori de perechi: public class ArrayMap extends AbstractMap { // dictionar vector de perechi private ArraySet entries . // aloca memorie pentru vector for (int v=0. în pachetul “javax. Exemplul urmãtor este o clasã pentru grafuri memorate prin liste de adiacente. Map). removeAll. retainAll.. } } Clasa abstractã AbstractCollection. Set. folosind iteratori : isEmpty.w) la graf List vlist = (List)a. return a. Iterator s.tree”: clasa instantiabilã DefaultMutableTreeNode pentru un nod de arbore. // lista vecinilor lui v return vlist. iar clasele instantiabile pentru multimi si liste extind aceste clase abstracte.v++) a. de exemplu o multime de obiecte realizatã ca vector sau ca listã simplu înlãntuitã. contine implementãri pentru o serie de operatii care pot fi realizate indiferent de tipul colectiei. Map. Prin extinderea claselor abstracte se pot defini noi clase colectie cu un efort de programare minim si cu pãstrarea compatibilitãtii cu interfetele comune (List. List. } public Object put ( Object key. // lista de vecini ai nodului v vlist. si pot sã refoloseascã metode definite în clasele abstracte si în clasele instantiabile deja existente.add(new Integer(w)). In Java.Cerinte de performantã (de memorie ocupatã sau timp de acces) impun alte implementãri pentru tipurile abstracte de date existente. // vector sau lista de noduri public Graph ( int n) { this.toString(). colectie de multimi disjuncte.Florian Moraru – Programare Orientata pe Obiecte (implementate prin vectori de biti). Set. compatibilã cu interfata Collection.a.v<n. arbore binar s.get(v). selectie de noduri. // numar de noduri in graf private List a.

Entry { private Object key.} public Object next() { Object e= (Entry)entries. } public Iterator iterator () { return new ASIterator(). } } // multime-vector de obiecte class ArraySet extends AbstractSet { public int size() { return entries. new MEntry(key. // valoare asociata anterior cheii entries. val=v.remove(i-1).size(). } } public Set entrySet ( ) { return entries. ASIterator () { i=0.} } 90 .getValue(). Object v) { key=k.equals(key).value)).val.value)).value)). } public String toString( ) { return entries.Entry” si redefineste metoda “equals” (eventual si metoda “toString”): class Entry implements Map. // modifica perechea return v.set (i.add (new MEntry(key.indexOf( new MEntry(key. } public String toString() { return key+":"+val. } else { // daca cheia key exista deja in dictionar Object v = ((MEntry) entries. } } // multimea cheilor // mostenita din AbstractMap dar redefinita Clasa “Entry” implementeazã interfata “Map. return v. } public boolean hasNext() { return i < entries. return e. if (i<0) { // daca cheia key nu exista anterior in dictionar entries.} public boolean equals (Object obj) { return ((Entry)obj).} } // clasa iterator (inclusa in clasa ArrayMap) class ASIterator implements Iterator { int i.get(i). } public Object getKey() { return key.toString().get(i)). i++.} public Object setValue (Object v) { val=v. public Entry (Object k. } public void remove () { entries.getKey(). // se adauga o noua pereche return null. } public Object getValue() { return val.Florian Moraru – Programare Orientata pe Obiecte int i = entries.size().

tabel care însã nu este creat efectiv pe disc ci este doar o imagine diferitã a unor date existente prezentatã utilizatorilor bazei de date pentru afisare sau pentru cãutare. pentru a facilita întelegerea ideii de colectie virtualã.Florian Moraru – Programare Orientata pe Obiecte Colectii virtuale Vom folosi denumirea de colectie virtualã pentru ceea ce în literatura de limbã englezã se numeste “view”. se vor defini noi clase pentru multimea cheilor si colectia valorilor. Implementarea acestei idei în clasa AbstractMap foloseste clase incluse anonime. // adresa obiectului creat la primul apel keySet public Set keySet() { if (keySet == null) // daca este primul apel keySet = new KSet(entrySet()). dar aici vom folosi clase exterioare. care trebuie sã furnizeze o multime a cheilor si respectiv o colectie a valorilor din dictionar. iar la un apel ulterior se verificã doar valoarea acestei variabile (initial valoarea null): private transient Set keySet = null. Pe lângã consumul inutil de memorie ar exista si problema mentinerii în concordantã a dictionarului cu cele douã colectii dupã orice modificare efectuatã în dictionar (sau a refacerii colectiilor la fiecare nou apel). // atunci se creeazã obiectul unic de tip KSet return keySet. adicã o altã imagine (perspectivã) asupra unei colectii de date din memoria RAM sau de pe un suport extern. Un alt exemplu îl oferã metodele “keySet” si “values” din clasele dictionar. Clasa nouã extinde o clasã abstractã (AbstractList) si defineste metodele abstracte (get. o variabilã a clasei AbstractMap retine o referintã la obiectul creat numai la primul apel. dar fãrã a copia datele într-un alt tip de colectie. iar clasa iterator se poate defini prin parcurgerea multimii de perechi cheie-valoare (“entrySet”) si extragerea câmpului dorit din fiecare pereche (fie cheia. Asadar. } public Iterator iterator () { // iterator pe multimea cheilor return new KIter(eset). fie valoarea).size(). O altã observatie utilã pentru întelegerea codului este aceea cã metodele “keySet” si “values” au rol de metode fabricã de obiecte (“Factory Methods”) si nu vor produce câte un nou obiect la fiecare apel. } 91 . In Java problema se pune de a privi un vector intrinsec sau o colectie ca fiind de un alt tip. Exemplul cel mai simplu este metoda staticã Arrays. pentru cã metoda “next” a iteratorului clasei va furniza urmãtoarea cheie sau valoare din dictionarul existent (într-o ordine care depinde de tipul clasei dictionar). Solutia acestei probleme foloseste ideea urmãtoare: o nouã colectie (multime) se poate defini prin extinderea unei clase abstracte cu definirea unei clase iterator. // referinta la multimea de perechi cheie-valoare public KSet (Set entrySet) { eset=entrySet. dictionar definit complet prin multimea perechilor cheie-valoare. Prima utilizare a acestui termen a apãrut în domeniul bazelor de date unde existã posibilitatea prezentãrii datelor din mai multe tabele fizice sub forma unui nou tabel ( cu o parte din coloanele tabelelor fizice). size) pe baza datelor existente în vectorul initial. multime generatã de metoda “entrySet” (specificã fiecãrui tip de dictionar): class KSet extends AbstractSet { Set eset. } Clasa KSet implementeazã o multime virtualã a cheilor dintr-un dictionar.asList() care ne permite sã vedem un vector intrinsec ca o colectie nemodificabilã de un tip (anonim) compatibil cu interfata List. } public int size() { return eset. dar aceste clase nu vor contine date. dar fãrã sã creeze noi colectii (fãrã sã copieze referintele din dictionar în aceste noi colectii).

Vom folosi în continuare un nume mai scurt pentru clasa DefaultMutableTreeNode astfel: class TNode extends DefaultMutableTreeNode { public TNode() { super(). folositã pentru pãstrarea datelor care vor fi afisate într-o componentã graficã JTree. } } // elimina o cheie atunci cand se elimina perechea care o contine In mod similar se procedeazã la metoda “values” pentru a crea o colectie virtualã a valorilor din dictionar. prin iterare pe aceeasi multime de perechi “entrySet”. O idee ar fi înlocuirea vectorului extensibil de succesori cu o listã înlãntuitã de succesori. Un arbore este complet definit prin nodul rãdãcinã. Se pot defini si alte variante dar care ar trebui sã implementeze interfata MutableTreeNode pentru a fi folosite de un obiect JTree. } public Object next() { return ((Map. public KIter (Set eset) { i=eset.hasNext().swing. Colectii arborescente Bibliotecile de clase Java contin o singurã clasã care poate fi utilizatã ca arbore general. // scoate cheia dintr-o pereche Map.} public TNode (Object info) { super(info). TNode getParent(). // adauga acestui nod fiul “child” // numarul fiilor acestui nod // parintele acestui nod 92 .contains(k). Dintre metodele publice mai des folosite mostenite de clasa TNode mentionãm: void add (TNode child).Entry) i. în principal). deci un obiect arbore este de fapt un obiect nod de arbore. multimea cheilor nu este modificatã direct ci numai indirect.next()). } } Metoda de adãugare la multime (“add”) nu poate fi definitã pentru multimea KSet dar nici nu este necesar (pentru cã nu este metodã abstractã în AbstractSet).iterator(). multicãi.} } Clasa nod de arbore foloseste o implementare în care fiecare nod contine un vector extensibil de referinte la nodurile fii (lista fiilor este un obiect de tip Vector). // iterator pe multimea de perechi cheie-valoare } public boolean hasNext() { return i. o referintã la nodul pãrinte si un obiect cu date (de tipul generic Object).remove(). sub numele de DefaultMutableTreeNode în pachetul javax. prin metode ale clasei dictionar (“put”.getKey(). Clasa iterator parcurge multimea de perechi si extrage cheia din fiecare pereche: class KIter implements Iterator { private Iterator i. int getChildCount().Florian Moraru – Programare Orientata pe Obiecte public boolean contains(Object k) { return eset.Entry } public void remove() { i.tree. Este o clasã cu multe metode si posibilitãti de utilizare si în aplicatii fãrã interfatã graficã.

parcurs in latime Exemple de utilizare a unor obiecte de tip DefaultMutableTreeNode: // clasa ptr un nod de arbore ( cu un nume mai scurt) class TNode extends DefaultMutableTreeNode { public TNode() { super().get(parent). parent = tok. String child. r.equals(obj)) return n.TNode>(). // r este nodul radacina DataInputStream cin= new DataInputStream(System.TNode> h = new Hashtable<String.preorderEnumeration(). Object obj) { Enumeration e = root. } return null.getUserObject().hasMoreElements()){ TNode n = (TNode) e. in adancime Enumeration breadthFirstEnumeration(). // noduri din subarbore.readLine()) != null) { StringTokenizer tok = new StringTokenizer(line).add(c=new TNode(child)). // pune adresa nod fiu in dictionar } } catch (IOException ex) { ex. // fiii directi ai acestui nod Object getUserObject(). } Crearea unui arbore depinde de modul cum sunt furnizate datele ce vor fi introduse în arbore.in).c). line.printStackTrace().} } // cauta in arborele cu radacina root nodul ce contine un obiect dat obj public static TNode find (TNode root.} return r.put(child.nextToken(). c.put(parent. // p = adresa nod parinte in arbore if (p==null) { // daca nu exista nod parinte r=p=new TNode(parent). fie folosim un dictionar în care cheile sunt valori din noduri si au asociate adresele nodurilor în arbore (dacã valorile din noduri sunt distincte).} public TNode (Object info) { super(info). if (n. Dacã aceste date vin sub forma unor perechi parinte-fiu atunci fie utilizãm metoda “find” de mai înainte pentru cãutarea adresei nodului pãrinte. try { while ( (line=cin.nextToken(). Exemplu: private TNode buildTree() { TNode p. // datele din acest nod void remove (TNode child). // valoare nod fiu p = h.nextElement(). // creare si adaugare nod fiu h. while ( e. } Crearea unui arbore cu numele fisierelor dintr-un director dat si din subdirectoarele sale este un caz particular important de creare a unui arbore general: // subclasa ptr arbori multicai de fisiere class FTree extends DefaultMutableTreeNode { 93 . // atunci creeaza nod parinte h. // elimina fiul “child” din lista fiilor acestui nod Enumeration preorderEnumeration().p). // si pune adresa lui in dictionar } p. // valoare nod parinte child =tok. Hashtable<String.Florian Moraru – Programare Orientata pe Obiecte Enumeration children(). // noduri din subarborele acestui nod. parent.

dir). // adauga fiecare fiu la radacina } } } Exemplu de afisare în mod text. "").println (ind+ r. // cu cat creste indentarea la un nou nivel System.list(). // adauga continut subdirector f } } } Se poate folosi si un constructor recursiv de nod.setParent(r). String ind) { // ind = indentare la fiecare apel final String inc =" ". // nume fisiere din directorul d for(int i=0. f). // afisare nod radacina Enumeration e = r. } static void printTree (FTree r.listFiles().nextElement()).i<files.i++) { FileNode s=new FileNode(files[i]). // fiii nodului r ind=ind+inc.Florian Moraru – Programare Orientata pe Obiecte public FTree() { super().ind).isDirectory( ) ) return.toString()). // creare nod ptr nume fisier r. s.setParent(this). // noduri fii ai radacinii add(s). } 94 . care construieste întreg arborele: class FileNode extends DefaultMutableTreeNode { public FileNode (File d) { super (d).} public FTree (File dir) { // arbore cu fisierele din directorul “dir” this (dir.length. kid.i++){ // ptr fiecare fisier File f = new File(d+"\\"+files[i]).out. File [] files=d. // creste indentarea la un nou nivel while ( e. folosind metode ale clasei DefaultMutableTreeNode. // creare obiect File FTree kid = new FTree (f.length. // adaugare recursiva noduri la acest arbore } // creare arbore cu fisierele dintr-un director d si din subdirectoarele sale void dirtree ( FTree r. // radacina arbore. // leaga nod la arbore dirtree (kid. // fisiere din directorul d for (int i=0.hasMoreElements()) // afisare recursiva fii printTree ((FTree) (e.isDirectory() ) return. cu indentare diferitã la fiecare nivel din arbore: public void print () { // metoda a clasei FTree printTree ( (FTree) getRoot(). // iesire daca d nu e fisier director String [ ] files=d.i<files. // creare nod radacina if ( ! d.add ( kid).getName()). cu nume fisier director dirtree (this. File d) { if ( ! d.getName()).} public FTree (String info) { super(info).children().

out. while ((line=in. ArrayList<String> b = ArrayList<String> (100).List<Integer>> cref = new TreeMap<String. ArrayList<LinkedList<Integer>> graf. ca si la apelul de functii. iar la extragerea din colectie nu mai este necesarã o conversie de la tipul generic la tipul specific aplicatiei.add (new Integer (nl)).add( new Integer(i) ). word. Exemplu: 95 .Florian Moraru – Programare Orientata pe Obiecte 8. String line. StringTokenizer st = new StringTokenizer (line). BufferedReader in = new BufferedReader (new FileReader (arg[0])). Colectii generice Clase generice în Java 5 Clasele colectie cu tipuri parametrizate au fost introduse în versiunea 1. dar în Java existã o singurã clasã genericã (sursã si compilatã) în care se înlocuiesc parametri formali cu parametrii efectivi.List<Integer>>( ).i++) { Integer val= list.size().println (cref). // fara conversie de tip ! sum += val.put (word. while ( st. // nu se poate adauga alt tip decat Integer int sum=0.i<10.5. } Trecerea de la un tip primitiv la tipul clasã corespunzãtor si invers se face automat în versiunea 1.i++) list. int nl=0. lst=new LinkedList<Integer>()). procese numite “autoboxing” si “unboxing”.hasMoreTokens() ) { word=st.get (word).readLine()) != null) { ++nl. lst. } Exemplu de utilizare dictionar cu valori multiple pentru problema listei de referinte încrucisate .în ce linii dintr-un fisier text apare fiecare cuvânt distinct : public static void main (String[ ] arg) throws IOException { Map<String. List<Integer> lst = cref. ca solutii alternative pentru colectiile de obiecte Object. i< list. // initializata ulterior Avantajele sunt acelea cã se poate verifica la compilare dacã se introduc în colectie numai obiecte de tipul declarat pentru elementele colectiei. if (lst==null) cref. Exemplu cu un vector de obiecte Integer: ArrayList<Integer> list = new ArrayList<Integer>(). } } System.nextToken(). Exemple de declarare a unor vectori cu elemente de diferite tipuri : ArrayList<Integer> a = new ArrayList<Integer>(). for (int i = 0.intValue(). ArrayList<TNode> c = ArrayList<TNode> (n). Aceste clase au fost numite generice (“generics”) si nu clase “sablon” (“templates”) pentru cã desi se definesc si se folosesc aproape la fel cu clasele sablon din C+ + ele diferã prin implementare: în C++ din clasa sablon se genereazã diverse clase prin substitutia parametrilor tip din clasa sablon.5. for (int i=1.get(i).

Pentru exemplificare redãm un fragment din clasa ArrayList. } . for (String s : list) sb += s..5 toate interfetele si clasele colectie au fost redefinite pentru a permite specificarea tipului elementelor colectiei. Exemplu: // transforma un vector intr-o colectie (gresit) static void arrayToCol (Object[] a.add(x+""). public E get(int index) { RangeCheck(index).5 se poate utiliza o formã mai simplã pentru enumerarea elementelor unei colectii care implementeazã interfata Iterable.i++) list.hasNext().intValue(). unde “E” este tipul neprecizat al elementelor colectiei : public class ArrayList<E> extends AbstractList<E> implements List<E>.io.add(o).iterator(). java. return true. // eroare la compilare } // transforma un vector intr-o colectie (corect) static <T> void arrayToCol (T[] a. // trecere automata de la Integer la int Tot din versiunea 1. Un alt exemplu. Cloneable. RandomAccess. // correct 96 .) sum += i.x++) list. Exemplu: for (Integer it : list) sum += it.. return elementData[index]. // suma valorilor din vector for (Iterator i = list.add(o). // trecere automata de la int la Integer int sum=0.i<10. } Nu numai clasele pot avea parametri tip dar si metodele (numite metode generice). Collection<T> c) { for (T o : a) c. i.next(). // concatenare de siruri extrase din vector In versiunea 1. cu o listã de siruri : LinkedList<String> list = new LinkedList<String>(). for (int i=1.x<9. } public boolean add(E o) { ensureCapacity(size + 1).Florian Moraru – Programare Orientata pe Obiecte ArrayList<Integer> list = new ArrayList<Integer>(). // Increments modCount!! elementData[size++] = o. // it este iterator pe colectia list de obiecte Integer // sau sum += (int) it. String sb="".add(10*i). for (int x=1. Collection<?> c) { for (Object o : a) c. private int size.Serializable { private transient E[] elementData. sau sum += it. } public boolean contains(Object elem) { return indexOf(elem) >= 0.

addToMap (m. List<Object> lo = ls.out. System.V>) obj. } Functia urmãtoare nu produce avertisment la compilare: public boolean equals (Object obj) { MEntry e = (MEntry) obj. Object k.V> extends Object implements Map.. Exemplu: class OldClass { // metoda statica fara generice public static void addToMap (Map m.V> { .V> e = (MEntry<K.println(m). } Compilatorul Java poate verifica numai utilizarea corectã de tipuri statice. pe baza numelor de tip. arrayToCol (sa..Integer> m = new HashMap<String.v). Object v) { m. Forme posibile pentru parametri tip O colectie Collection<Object> nu este un supertip al unei colectii Collection<T> desi Object este un supertip al lui T.Integer> ().1). public boolean equals (Object obj) { MEntry<K. String s = ls.add(new Object()).put(k.Entry<K. Exemplu: List<String> ls = new ArrayList<String>(). Collection<String> cs = new LinkedList<String>(). // eroare la compilare ! Exemplul urmãtor de utilizare aratã de ce nu este corectã atribuirea anterioarã: lo. // adauga la lista lo // incercare de atribuire a unui Object la un String Exemplul urmãtor aratã de ce se foloseste caracterul ? (wildcard) ca parametru de tip: 97 . OldClass. Exemplu: class MEntry<K. } } class Generic2 { public static void main (String arg[]){ Map<String.get(0). } } Avertismente la compilare apar si la redefinirea unor metode din clasa Object în alte clase.Florian Moraru – Programare Orientata pe Obiecte } // utilizare (fara argumente efective de tip la apel) String[] sa = new String[100]."unu". cs). // T presupus a fi String La utilizarea unui cod anterior versiunii Java 5 (cod fãrã generice) de cãtre programe Java cu generice pot apãrea mesaje la compilare inevitabile (care pot fi suprimate numai prin adnotarea @SuppressWarning ).

return s. Functia "printC" poate fi folositã numai cu colectii de Object si nu cu colectii de un subtip al lui Object.add(new Object()). iar caracterul '?' are sensul de "tip necunoscut". Declaratiile urmãtoare nu sunt echivalente: Collection<Object> Collection<?> In linia 2 din exemplul urmãtor apare eroare la compilare: Collection<?> c = new ArrayList<String>(). Fie functia urmãtoare pentru calculul sumei numerelor dintr-o colectie: static double sum (Collection<Number> c){ double s=0. } // utilizare incorecta HashSet<String> m = new HashSet<String> (). for (Number x: c) s+= x. Tipurile necunoscute pot fi limitate inferior sau superior (Bounded Wildcard) folosind sintaxa <? extends T> pentru tipuri limitate inferior si <? super T> pentru tipuri limitate superior.add(2L). System.doubleValue(). a. } In concluzie Collection<?> este supertipul oricãrei colectii de obiecte..out.. c.add(3L).doubleValue(). } // utilizare incorecta ArrayList<Long> a = new ArrayList<Long> (). printCol (m).println( (long)sum(a)). a. } 98 .out. Urmeazã varianta corectã de definire a functiei de afisare a unei colectii generice: // functie generica de afisare a oricarei colectii static void printCol (Collection<?> c) { for (Object e : c) System. return s.Florian Moraru – Programare Orientata pe Obiecte // functie naiva de afisare a unei colectii generice static void printCol (Collection<Object> c) { for (Object e : c) System. Dacã argumentul functiei "sum" ar fi ‘?' atunci nu am putea folosi metoda "doubleValue" decât dupã conversie de la Object la Number: static double sum (Collection<?> c){ double s=0.println(e). // eroare ! Compilatorul nu stie daca "Object" este un subtip al tipului necunoscut '?'. for (Object x: c) s+= ((Number)x).println(e).out.

println(sum(a)). .} deci functia "sum" poate opera cu o colectie de obiecte de orice subtip al lui Number (Byte... .} Definitia este prea restrictivã deoarece permite numai compararea de obiecte de tip T între ele... } TreeSet<String> a = new TreeSet<String> ( new MyComp<String>()). Exemplu de situatie care produce eroare la compilare desi nu ar trebui: class A implements Comparable<Object> {... Integer. public TreeSet (Comparator<E>) { . Exemplul urmãtor aratã necesitatea unor tipuri necunoscute limitate inferior (? super T). } deoarece compilatorul nu poate verifica dacã tipul necunoscut '?' este un supertip al tipului Long. public TreeSet (Comparator<? super E>) { .} Collection<A> a = .} } // utilizare class MyComp<String> implements Comparator<String> { . O colectie ordonatã poate primi un comparator folosit la compararea elementelor din colectie: class TreeSet<E> ..out.add (new Long(x)). Long. De observat cã urmãtoarea functie se compileazã cu erori: static void addLong (Collection<? extends Number> c.. Tipurile limitate superior se folosesc în general pentru argumente de functii. System. long x) { c.. { . . { .Florian Moraru – Programare Orientata pe Obiecte Desi nu apar mesaje la compilare pot apare exceptii la executie.. Double)..max" : public static <T extends Comparable<T>> T max(Collection<T> coll) {. 99 . Tipurile limitate superior se folosesc în general pentru rezultatele functiilor. ca în exemplul urmãtor: ArrayList<Date> d = new ArrayList<Date> ()... Se spune ca Number este limita superioara a tipului necunoscut '?'. } TreeSet<String> a = new TreeSet<String> ( new MyComp<Object>())..} class MyComp<? super E> implements Comparator<? super E> { . Necesitatea unor limite multiple (“Multiple bounds”) este ilustratã de urmãtoarea posibilã definire a functiei "Collections..max(a)... Short. A am = Collections.. .. Float. Pentru a permite utilizarea unui comparator mai general se va scrie: class TreeSet<E> .. // exceptie la conversia din Date in Number Solutia sigurã pentru definirea functiei "sum" este: static double sum (Collection<? extends Number> c) { ... .

for (int i=0. Exemplu de clasã pentru un graf reprezentat printr-un vector de liste (de noduri vecine): class Graph { private int n.add(new Integer(w)). vlist.i<nl. Definitia urmãtoare rezolvã aceastã compatibilitate : public static <T extends Object & Comparable<? super T>> T max(Collection<T> coll) Exemple comparative Discutia anterioarã aratã cã este mai simplu sã folosim clase si metode generice existente decât sã definim clase generice noi aplicabile în diverse situatii si care sã nu producã erori la compilare. for (int v=1. // vector de liste de intregi public Graph ( int nn) { n =nn+1.v++) a. // constructori public MyTable(int nl.i++) data.get(v)). int nc) { data= new Vector<Vector<Object>>(nl). // numar de noduri in graf private ArrayList<LinkedList<Integer>> a. de variabile Object.int w) { LinkedList<Integer> vlist = a. } } Exemplul urmãtor este o clasã pentru un tabel (o matrice) de obiecte memorat ca un vector de vectori: class MyTable extends AbstractCollection { private Vector<Vector<Object>> data. } public MyTable (Object a[][]){ 100 . a = new ArrayList<LinkedList<Integer>> (n).get(v).int w) { LinkedList<Integer> vlist = a.Florian Moraru – Programare Orientata pe Obiecte Eroarea provine din aceea cã tipul A nu implementeazã tipul <Comparable><A> si ar trebui ca tipul T sã fie comparabil cu orice supertip al lui T : public static <T extends Comparable<? super T>> T max(Collection<T> coll) Aceastã definire nu este compatibilã cu definitia anterioarã versiunii Java 5: public static Object max (Collection coll). } public void addArc (int v.add (new Vector<Object>(nc)).get(v). } public int arcs (int v) { // nr de vecini (de arce) ptr nodul v return a.add (new LinkedList<Integer>()).contains (new Integer(w)). Exemplele care urmeazã ilustreazã beneficiile claselor generice comparativ cu colectiile mai vechi. return vlist.size(). } public boolean isArc (int v.v<=n.

} public Iterator<T> iterator(){ return new TIterator<T> (this). ++i.} public Iterator iterator(){ return new TIterator ().j++) col.set (j.size()) lin. else lin. public boolean hasNext () { return i<size().size(). int j) { return data.add (j. int j) { Vector<Object> lin = data. } public void setValueAt (T v.add(a[i][j]).v).length.i++){ Vector<Object> col = new Vector<Object> (a[0].length).v).size().get(0).add(col). int j) { return data.get(i).get(j) } public void setValueAt (Object v.get(j). } class TIterator implements Iterator { int i=-1. } public Object next() { int c= getColumns().get(i). for (int i=0.size() } public Object getValueAt(int i. } } // metode public int size() { return data.Florian Moraru – Programare Orientata pe Obiecte data = new Vector<Vector<Object>>(a. // ((Vector)data.int i. data.set (j.get(i). for (int j=0.get(i).length). } } 101 . i%c). } } Fragmente dintr-o altã variantã care nu produce avertismente “unchecked or unsafe operation”: class MyTable<T> extends AbstractCollection<T> { private Vector<Vector<T>> data.get(0)).size()) lin. // ((Vector)data. } public void remove(){} } public int getColumns() { return data. int i. int j) { Vector<T> lin = data.j<a[i].i<a.get(i)).v).length.add (j. else lin. if (j<lin.get(j). // metode public T getValueAt(int i. if (j<lin.v). return getValueAt (i/c.

“MultiMap” pentru dictionare cu chei multiple. // numar de aparitii al unui element dat boolean add (E element. adicã are toate metodele definite în functie de multimea de perechi furnizatã de metoda abstractã “entrySet”: public abstract class AbstractMultiset<E> extends AbstractCollection<E> implements Multiset<E> { public abstract Set<Entry<E>> entrySet(). // daca doua obiecte MultiSet sunt egale int hashCode(). dar permite realizarea eficientã a unor operatii cum sunt obtinerea numãrului de elemente cu o valoare datã.c. // adauga de n ori un element int remove( Object element. Set<E> elementSet().4]. “BiMap” pentru dictionare bidirectionale. String toString(). MyTable<T> t.size(). int n). care nu permite accesul direct. public T next() { int c= t.Florian Moraru – Programare Orientata pe Obiecte class TIterator<T> implements Iterator<T> { int i=-1.1]. [c. în diverse variante de implementare. prin indice (ca la liste). Astfel multimea de elemente {d.b. // multime virtuala de perechi element-contor de aparitii boolean equals(Object object). daca count(elem)>n int removeAllOccurrences(Object element).i%c). [a.d.c.d.getColumns().b. O multime cu valori multiple este o colectie în care elementele nu sunt neapãrat distincte. return t. // multime virtuala a elementelor distincte (view) Set<Entry<E>> entrySet(). String toString(). public TIterator (MyTable<T> t) { this.d} se va memora sub forma : { [d. // elimina n aparitii ale unui element. } public void remove(){} } } Colectii Google In 2007 firma Google a publicat un pachet de clase colectie care vin sã completeze clasele Java de bibliotecã.3] } Clasa “AbstractMultiSet” este definitã dupã exemplul clasei AbstractMap. // valoare element int getCount(). Dintre ele mentionãm clase “MultiSet” pentru multimi cu valori multiple.t=t. ++i. Interfata “MultiSet” este: public interface Multiset<E> extends Collection<E> { int count (Object element). // numar de aparitii boolean equals(Object o). } } Toate implementãrile interfetei “MultiSet” se bazeazã pe un dictionar în care cheia este elementul din multime iar valoarea asociatã cheii este numãrul de repetãri ale elementului respectiv. // metode din Collection redefinite … interface Entry<E> { // o pereche element si contor de aparitii E getElement(). int n). [b.c.a. // multime de perechi “Entry” @Override public int size() { // numar de elemente din multime long sum = 0L.2].getValueAt(i/c. } public boolean hasNext () { return i< t. eliminarea tuturor elementelor cu aceeasi valoare si compararea la egalitate a douã colectii cu elemente multiple. int hashCode(). for (Entry<E> entry : entrySet()) { 102 . dar toate cu generice.

"no calls to next() since the last call to remove()").getKey().get(getElement()). } } return count. // pentru eficienta // constructor protected AbstractMapBasedMultiset(Map<E. // dictionarul folosit de acest multiset private long size. return new AbstractMultisetEntry<E>() { public E getElement() { return mapEntry.iterator(). } return (int) Math. } public Multiset. return new Iterator<Multiset.size(). } // entrySet creeazã obiect numai la primul apel private transient volatile EntrySet entrySet.Entry<E>>() { Map.Entry<E>> entrySet() { if (entrySet == null) { entrySet = new EntrySet(). Integer. AtomicInteger> backingMap) { this.MAX_VALUE).entrySet(). Integer> mapEntry = backingEntries.get(). 103 .Entry<E.Entry<E>> { @Override public Iterator<Multiset.getValue(). public boolean hasNext() { return backingEntries. } public void remove() { checkState(toRemove != null.Entry<E> next() { final Map.getCount().size = super.Entry<E.get().backingMap = backingMap.next(). toRemove = mapEntry. } // alte metode … } Clasa AbstractMapBasedMultiMap defineste metoda abstractã “entrySet” în functie de un dictionar primit de constructorul clasei si care poate avea orice implementare: abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E> implements Serializable { private final Map<E. } return entrySet. this. @Override public Set<Multiset.Florian Moraru – Programare Orientata pe Obiecte sum += entry. Integer>> backingEntries = backingMap.hasNext().Entry<E>> iterator() { final Iterator<Map. } }. } public int getCount() { int count = mapEntry. Atomic> toRemove.min(sum. AtomicInteger> backingMap. if (count == 0) { Integer frequency = backingMap. } // clasa inclusa private class EntrySet extends AbstractSet<Multiset. if (frequency != null) { count = frequency.Entry<E.

Integer>) backingMap()). } // metode redefinite pentru multimi ordonate @Override public SortedSet<E> elementSet() { return (SortedSet<E>) super. } @Override protected Set<E> createElementSet() { return new SortedMapBasedElementSet((SortedMap<E. } backingMap.getValue(). toRemove = null.elementSet(). Integer>()). } public TreeMultiset(Comparator<? super E> comparator) { super(new TreeMap<E.Florian Moraru – Programare Orientata pe Obiecte size -= toRemove. TreeMultiSet apeleazã la implementãri existente pentru dictionarul folosit: public final class TreeMultiset<E> extends AbstractMapBasedMultiset<E> { // constructori public TreeMultiset() { super(new TreeMap<E. Integer>(comparator)).clear(). } // clase incluse si alte metode … } 104 .values()) { frequency. } @Override public int size() { return backingMap. } }. } } // alte metode mostenite si redefinite ptr eficienta } Clasele HashMultiSet.getAndSet(0). } @Override public void clear() { for (AtomicInteger frequency : backingMap.remove().size(). size = 0L. backingEntries.set(0).

delegate = checkNotNull(delegate). } public String toString () { return stack. pentru reutilizarea functionalitãtii clasei B în noua clasã A.removeFirst().toString(). care vor fi definite în subclasele acestei clase abstracte. Varianta definirii clasei "LinkedStack" ca o 105 . } public Object push (Object obj) { stack.isEmpty(). Reutilizarea codului în POO Reutilizarea codului prin delegare Unul din avantajele programãrii orientate pe obiecte este posibilitatea de reutilizare simplã si sigurã a unor clase existente în definirea altor clase. o tehnicã specificã POO este adãugarea de noi metode unor clase existente.toString(). } @Override public String toString() { return delegate(). In exemplul urmãtor se defineste o clasã pentru stive prin delegarea operatiilor cu stiva cãtre metodele listei înlãntuite de obiecte LinkedList: public class LinkedStack { private LinkedList stack. Clasa urmãtoare din colectiile Google ilustreazã esenta delegãrii pentru “decorarea” unei clase: public abstract class ForwardingObject implements Serializable { private final Object delegate. protected ForwardingObject(Object delegate) { this.Florian Moraru – Programare Orientata pe Obiecte 9. fãrã a modifica clasele initiale.addFirst (obj). } protected Object delegate() { return delegate. return obj. public LinkedStack () { stack= new LinkedList(). prin delegarea cãtre metodele clasei B a metodelor clasei A. } public Object pop () { return stack. Altfel spus. Metodele de reutilizare a codului sunt delegarea si derivarea. de la imaginea unor decoratiuni adãugate unui obiect existent pentru a-i spori utilitatea sau estetica. } } De remarcat cã nu sunt delegate si operatiile “equals” sau “hashCode”. } } // stiva lista (obiect continut) // constructor // metoda clasei LinkedStack // foloseste metoda clasei LinkedList // metode cu acelasi nume si tip De observat cã tipurile “LinkedStack” si LinkedList nu sunt compatibile si nu se pot face atribuiri între ele. iar schema de proiectare ce realizeazã extinderea functionalitãtii unei clase se numeste “decorator”. simultan cu reutilizarea unor metode mostenite. } public boolean isEmpty () { return stack. In cazul delegãrii o clasã A contine o variabilã de un tip clasã B. desi continutul claselor este atât de asemãnãtor.

get(key). public class MultiMap { HashMap m = new HashMap().).obj). // apel HashMap. modificabilã la executie. public void put (Object key.”removeFirst” ale clasei LinkedList) la un alt set de metode publice (“push”. StackList st2 = new StackList (new LinkedList()). } public Object push (Object obj) { stack. Delegarea de metode între obiecte poate fi o relatie dinamicã. return obj. Vom relua exemplul cu stiva adãugând un grad de generalitate prin posibilitatea utilizatorului de asi alege tipul de listã folosit pentru stivã (vector sau lista înlãntuitã): public class StackList { private List stack. public StackList (List list) { stack=list. Iterator ik=m.get } public String toString () { String str="". Clasa preia o mare parte din functionalitatea clasei HashMap.. // vector // lista inlantuita In exemplul urmãtor se defineste o clasa dictionar cu valori multiple. // apel HashMap. } . prin delegarea unor operatii cãtre metode din clasa HashMap.add (0. spre deosebire de relatia staticã de derivare. de exemplu) printr-o referintã la un obiect de un alt tip.put (key. // lista de valori a unei chei str = str+ key + " : " + lst. mai ales cã douã dintre metode pot fi mostenite ca atare ("isEmpty" si "toString"). care poate fi înlocuitã la executie (la construirea unui obiect. Clasa compusã poate contine o variabilã de un tip interfatã sau clasã abstractã. în care fiecare cheie are asociatã o lista de valori. “pop” etc.put } public List get (Object key) { return (List) m. List val) { m.hasNext() ) { List lst = get(ik. // apel HashMap. compatibil cu tipul variabilei din clasã.Florian Moraru – Programare Orientata pe Obiecte subclasã a clasei LinkedList este preferabilã aici.toString() + "\n".. } return str. } public Set keySet () { return m.keySet while (ik. Clasa “LinkedStack” este numitã si clasã “adaptor” pentru cã face trecerea de la un set de metode publice (“addFirst”. } } 106 .iterator().next()).keySet(). val). stabilitã la scrierea programului si care nu mai poate fi modificatã.keySet(). // alte metode } // adresa vector sau lista inlantuita // constructor // retine adresa obiect stiva La construirea unui obiect “StackList” trebuie precizat tipul de listã folosit (vector sau altceva): StackList st1 = new StackList (new ArrayList()).

// adauga prima valoare la lista return result. Solutia este de a redefini aceste metode în subclasã. De asemenea. // lista anterioara de valori } } 107 . // se introduce o pereche cheie-lista lst. Object val) { List lst = (List) get(key). cãutare în stivã s. } } De observat cã subclasa “LinkedStack” mosteneste metodele "toString". return obj. fie direct. In Java nu se face aceastã diferentã doarece obiectul compus contine referinte la obiectele componente. Reutilizarea codului prin derivare Prin derivare se face o adaptare sau o specializare a unei clase mai generale la anumite cerinte particulare fãrã a opera modificãri în clasa initialã. Extinderea unei clase permite reutilizarea unor metode din superclasã. cu efectul de aruncare a unor exceptii. O problemã în acest caz ar putea fi posibilitatea utilizatorilor de a folosi pentru obiecte "LinkedStack" metode mostenite de la superclasã. Operatiile cu o stivã se numesc traditional “push” si “pop”. vom defini douã metode noi: public class LinkedStack extends LinkedList { public Object push (Object obj) { addFirst (obj). dar clasa LinkedList foloseste alte nume pentru operatiile respective. pe baza observatiei cã lista de valori asociatã unei chei (de tip List) este tot un obiect (compatibil cu tipul Object) si deci se poate pãstra interfata publicã a clasei HashMap: public class MultiMap extends HashMap { public Object put (Object key. fie dupã "ajustãri" si "adaptãri" cerute de rolul subclasei.add (val).Florian Moraru – Programare Orientata pe Obiecte Delegarea se mai numeste “agregare” sau “compozitie”: clasa agregat (compusã) contine unul sau mai multe obiecte cãtre care deleagã anumite operatii. lst=new ArrayList()). Vom relua exemplul clasei pentru stive realizate ca liste înlãntuite. Exemplu: public Object remove (int index) { throw new UnsupportedOperationException(). } public Object pop () { return removeFirst().a.put (key. In UML se face distinctie între termenii “compozitie” si “agregare”: în compozitie distrugerea unui obiect compus are ca urmare distrugerea obiectele continute (care nu mai pot exista independent de obiectul care le contine). De aceea. dar interzise pentru stive: citire si modificare orice element din stivã. care pot exista si dupã disparitia obiectului compus (desi s-ar putea pierde referintele la ele). // rezultatul va fi lista anterioara if (lst==null) // daca cheia key nu exista in dictionar super. Superclasa transmite subclasei o mare parte din functiile sale. nefiind necesarã rescrierea sau apelarea metodelor mostenite. // extrage lista de valori asociata cheii key List result=lst. "isEmpty" si altele din clasa LinkedList. existã un constructor implicit care apeleazã constructorul superclasei si care initializeazã lista stivã. } Iatã si o altã solutie de definire clasei "MultiMap" (dictionar cu valori multiple).

fie prin delegare (prin apelarea metodelor obiectului continut). In plus. Un exemplu este cel al claselor flux de I/E care deleagã operatiile elementare de citire/scriere de caractere individuale cãtre obiecte ce depind de suportul fizic al fluxului de date (fisier disc. de exemplu) de la care mosteneste metode absolut necesare functionãrii sale ca aplet. Aceste exemple (si altele) pot fi interpretate si astfel: o parte din functionalitatea unei aplicatii este realizatã prin metode ale unor clase predefinite (de bibliotecã). dar interfetele celor douã clase sunt diferite. this. o zonã de memorie. // adresa obiect cu metoda de calcul suma control public CheckedOutputStream(OutputStream out. AbstractMap) o mare parte din metode. . derivarea creeazã tipuri compatibile: putem sã înlocuim o variabilã sau un argument de tipul A printr-o variabilã (sau argument) de tipul B . fie prin mostenire.a.O nouã clasã colectie poate mosteni de la AbstractCollection (AbstractSet.O clasã ce defineste un fir de executie Java nu se poate defini decât prin extinderea clasei de bibliotecã Thread sau prin implementarea interfetei Runnable. care va primi la instantiere adresa obiectului ce contine metoda de calcul a sumei de control. etc. O situatie care apare la utilizarea unor API standard.a. este aceea cã nu se cunosc decât nume de interfete si nu se cunosc nume de clase care implementeazã aceste interfete (pot fi mai multe astfel de implementãri si chiar unele care urmeazã sã aparã în viitor).). cksum. . In plus. pentru cã sunt apelate din alte clase predefinite (metoda “run” dintr-o subclasã a clasei Thread. .write(b).Florian Moraru – Programare Orientata pe Obiecte Comparatie între compozitie si derivare Delegarea (un B contine un A) se recomandã atunci când vrem ã folosim (sã reutilizãm) functionalitatea unei clase A în cadrul unei clase B.cksum = cksum. AbstractList.O clasã pentru o interfatã graficã Swing extinde de obicei clasa JFrame de la care mosteneste metode de adaugare a unor componente Swing. cum ar fi DOM (XML). // metoda din interfata Checksum } 108 . Delegarea se foloseste la definirea unor clase adaptor de la o interfatã la o altã interfatã dar si atunci când tipul obiectului delegat se poate modifica la executie (adresa obiectului este primitã de constructorul fiecãrui obiect). care contin o variabilã interfatã (Checksum). clase de intrare-iesire s. Checksum cksum) { super(out). s. In felul acesta clasele definite de programatori pot mosteni un numãr mare de metode (si date) cu un efort minim de programare. definit prin interfata Node) nu putem folosi derivarea si rãmâne doar delegarea. Exemple: . Derivarea (un B este un fel de A) se recomandã atunci când vrem sã reutilizãm o mare parte din (sau toatã) interfata clasei A si pentru clasa B.update(b). iar o altã parte prin metode ale unor clase definite de programatori si care este specificã fiecãrei aplicatii (sau pãrti de aplicatie). Atât derivarea cât si compozitia permit reutilizarea metodelor unei clase.). Un alt exemplu este cel al claselor flux de date cu sumã de control. Legãtura dintre un obiect flux si obiectul de calcul a sumei este stabilitã la executie si asigurã o flexibilitate sporitã. unele din metodele definite de programatori trebuie sã respecte anumite interfete impuse. } public void write(int b) throws IOException { out. metoda “paint” dintr-o subclasã a clasei JApplet. Dacã vrem sã redefinim o metodã dintr-o astfel de clasã anonimã (cum ar fi metoda “toString” asociatã unui nod de arbore DOM. de stabilire asezare (“Layout”) si de vizualizare.O clasã aplet (sau servlet) este definitã de programator prin extinderea unei clase de bibliotecã (JApplet. la care adaugã câteva metode specifice noii clase. Derivarea din clase instantiabile sau abstracte este solutia folositã în cazul unor infrastructuri de clase (“Frameworks”) pentru crearea unor ierarhii de tipuri compatibile: clasele colectii de Object. Exemplu: public class CheckedOutputStream extends FilterOutputStream { private Checksum cksum.

} Colectiile Google folosesc mai mult delegarea ca metodã de reutilizare. metodele din M fie apeleazã metode din clasa B. Clasa A este de multe ori o clasã abstractã. iar clasa B este instantiabilã sau abstractã. Exemplu de mostenire functii de la 3 clase: class A { public void f1 () { System.println ("B. fie sunt mostenite de la clasa A.f1().f2(). Un exemplu este cel al claselor pentru vectori si respectiv pentru stive. // initializarea var.println ("C. desi un obiect stivã nu foloseste metodele clasei Vector ( cu exceptia metodei "toString"). De cele mai multe ori metoda de reutilizare a unor clase se impune de la sine. } } class M extends C { private A a = new A ().f3().} // delegare obiect a pentru operatia f1 public void f2 () { b. Checker).out.f2().f1(). while (in. // o clasa care implementeaza interfata Checksum CheckedOutputStream out. } } class B { public void f2 () { System. } } class C { public void f3 () { System. O clasã M poate prelua metode de la douã clase A si B astfel: clasa M extinde pe A si contine o variabilã de tip B..available() > 0) { int c = in. m.. out = new CheckedOutputStream (new FileOutputStream("date"). 109 . out. care extinde clasa AbstractSet si contine o variabilã de tip ArrayList : public class ArraySet extends AbstractSet { private ArrayList set.println ("A. nici nu se recomandã accesul direct la orice element dintr-o stivã (prin metodele clasei Vector). m. } } Un exemplu real de mostenire multiplã poate fi o clasã pentru o multime realizatã ca vector.out.out. } // Exemplu de utilizare CRC32 Checker = new CRC32(). Mai mult. m.f1"). public void f1 () { a. dar uneori este necesar ca o clasã sã preia functii de la douã sau mai multe clase diferite.write(c). In Java o subclasã nu poate avea decât o singurã superclasã. comparativ cu clasele colectie din Java care se bazeazã mai mult pe derivare.} // delegare obiect b pentru operatia f2 } class X { public static void main (String arg[]) { M m = new M().f2"). a si b se poate face intr-un constructor private B b = new B ().f3"). dar uneori alegerea între compozitie si derivare nu este evidentã si chiar a condus la solutii diferite în biblioteci de clase diferite. Ce este o stivã ? Un caz particular de vector sau un obiect diferit care contine un vector ? In Java clasa Stack este derivatã din clasa Vector.Florian Moraru – Programare Orientata pe Obiecte .read(). Implementarea mai multor interfete de cãtre o clasã nu este o solutie pentru mostenirea multiplã de functii.

Uneori variabila din subclasã este chiar de tipul superclasei. iar delegarea permite mai multã flexibilitate la executie. care sã poatã folosi fie un vector. 110 . Clasa anvelopã care urmeazã este compatibilã cu tipul List si. în acelasi timp. clasa DefaultListModel preia metode de la superclasa AbstractListModel si deleagã unei variabile interne de tip Vector operatii cu un vector de obiecte. deoarece "decoreazã" cu noi functii o clasã existentã. dupã cum doreste programatorul.add(obj). Combinarea delegãrii cu derivarea îmbinã avantajele ambelor metode de reutilizare: mostenirea interfetei si posibilitatea modificãrii obiectului delegat. contains.. // metode ale clasei B } Clasele ce contin un obiect de acelasi tip sau de un tip compatibil cu al superclasei sale se mai numesc si clase “anvelopã” (“wrapper”).a=a. } public Iterator iterator() { return set. Aceeasi solutie de mostenire multiplã este folositã în câteva clase JFC (Swing) de tip “model”. containsAll. retainAll s.obj). unde A este o clasã instantiabilã: class B extends A { private A a . } } Pentru multimi de tipul "ArraySet" se pot folosi toate metodele mostenite de la clasa AbstractSet: toString. ca un ambalaj pentru acel obiect. } public boolean add (Object obj) { if (! set. tip clasã abstractã sau interfatã. } . removeAll. Combinarea compozitiei cu derivarea Derivarea pãstreazã interfata clasei de bazã. Totusi nu se justificã o constructie de forma urmãtoare. foloseste metode definite în clasa AbstractList : class StackList extends AbstractList { private AbstractList stack. fie o listã înlãntuitã.a. // retine adresa obiect stiva } public Object push (Object obj) { stack.contains(obj) ) return set. deoarece adaugã functii obiectului continut.size(). putem defini o clasã stivã mai generalã.Florian Moraru – Programare Orientata pe Obiecte public ArraySet() { set = new ArrayList().add (0. // adresa obiect stiva public StackList (List list) { // constructor stack=(AbstractList)list. // delegare pentru creare obiect iterator } public int size() { return set. addAll. // delegare pentru operatia de adaugare return false. de exemplu.. Un model de listã este un vector cu posibilitãti de generare evenimente (de apelare receptori) la modificãri operate în vector. public B (A a) { this. O clasa anvelopã este numitã si clasã “decorator”. De exemplu.iterator().

. } } Exemple de combinare a derivãrii si compozitiei se gãsesc în clasa Collections. 111 . în clasa derivatã. // alte metode } In realitate.} } .} public boolean contains(Object o) {return c. Serializable { Collection c. dar compatibile cu acestea ca tip. . } public int size() { return stack.} .. } Incercarea de adãugare a unui nou obiect la o colectie nemodificabilã produce o exceptie la executie. "remove" s. Serializable { Collection c. Exemplu: class UnmodifiableCollection implements Collection.isEmpty().size(). // colectia de baza public int size() { synchronized(this) {return c. Primul grup de clase este cel al colectiilor nemodificabile. Exemplu: class SynchronizedCollection implements Collection.contains(o)..a. Exceptia poate fi tratatã fãrã a împiedica executia programului.toString(). pentru definirea de clase colectie cu functionalitate putin modificatã fatã de colectiile uzuale. } .} public boolean isEmpty() {return c. dacã nu se defineau metodele "add". clasele prezentate sunt clase incluse statice si sunt instantiate în metode statice din clasa Collections: static class UnmodifiableList extends UnmodifiableCollection implements List { private List list.size().. } public Object pop () { Object obj= get(0). } public boolean remove (Object o) { throw new UnsupportedOperationException(). stack. // metode care nu modifica continutul colectiei public int size() {return c..} public String toString() {return c. return obj.Florian Moraru – Programare Orientata pe Obiecte return obj. Al doilea grup de clase sunt clasele pentru colectii sincronizate.size(). // metode care ar putea modifica continutul colectiei public boolean add (Object o){ throw new UnsupportedOperationException().remove(obj).add(o).} } public boolean add(Object o) { synchronized(this) {return c. atunci apelarea metodei "add" era semnalatã ca eroare la compilare. care diferã de colectiile generale prin interzicerea operatiilor ce pot modifica continutul colectiei.

Dacã s-ar fi utilizat derivarea pentru obtinerea claselor direct utilizabile atunci ar fi trebuit generate. . . citire-scriere de numere în format intern. O clasã anvelopã de I/E contine o variabilã 112 . Clasele de tip filtru sunt clase intermediare. cu citire-scriere de caractere. } . FilterOutputStream si FilterReader. FilterWriter. . "else". Toate clasele flux de intrare sunt subtipuri ale tipului InputStream (Reader) si toate clasele flux de iesire sunt subtipuri ale tipului OutputStream (Writer). FileReader.a. Dupã facilitãtile oferite avem de ales între: . this. PipedWriter.Citire-scriere la nivel de linie si pentru numere de diferite tipuri (fãrã conversie): DataInputStream. // alte metode } public static List unmodifiableList (List list) { return new UnmodifiableList(list).synchronizedSet (new HashSet()). din care sunt derivate clase care adaugã operatii specifice (de “prelucrare”): citire de linii de text de lungime variabilã.Citire-scriere la nivel de octet sau bloc de octeti (metode “read”.list = list.Buffer de siruri (în memorie): StringBufferInputStream. Principalele clase filtru de intrare-iesire sunt: FilterInputStream. BufferedReader. . FileOutputStream.Fisiere disc : FileInputStream.Canal pentru comunicarea sincronizatã între fire de executie : PipedInputStream. Set s = Collections. CharArrayWriter. StringBufferOutputStream. mai putine.unmodifiableList (Arrays. ByteArrayOutputStream CharArrayReader. “write”). Clasele Reader.Vector de octeti sau de caractere : ByteArrayInputStream. prin derivare. Combinarea celor 8 clase sursã/destinatie cu optiunile de prelucrare asociate transferului de date se face prin intermediul claselor anvelopã . deci 32 de clase (practic. Solutia claselor anvelopã permite sã se adauge unor clase de bazã diverse functii în diferite combinatii. Pentru a folosi mai multe optiuni cu acelasi flux ar fi trebuit mai multe niveluri de derivare si deci ar fi rezultat un numãr si mai mare de clase. Dupã suportul fizic al fluxului de date se poate alege între: . List list = Collections. . "for"}. DataOutputStream. Numãrul de clase instantiabile de I/E este relativ mare deoarece sunt posibile diverse combinatii între suportul fizic al fluxului de date si facilitãtile oferite de fluxul respectiv. scriere numere cu conversie de format s. .Citire cu punere înapoi în flux a ultimului octet citit (PushBackInputStream).Florian Moraru – Programare Orientata pe Obiecte UnmodifiableList(List list) { super(list). } Exemple de utilizare a claselor colectie "speciale" : String kw[] ={"if". "do". FileWriter s.a. PipedOutputStream. Clase de intrare-iesire cu derivare si delegare Combinarea derivãrii cu delegarea a fost folositã la proiectarea claselor din pachetul “java. BufferedWriter. Existã douã familii de clase paralele : familia claselor “flux” (“Stream”) cu citire-scriere de octeti si familia claselor “Reader-Writer”.io”. .Citire însotitã de numerotare automatã a liniilor citite (LineNumberInputStream).. . combinatii ale celor 8 clase cu cele 4 optiuni. Writer si celelalte sunt clase abstracte.asList(kw)). PipedReader. .Citire-scriere pe octeti dar cu zonã buffer: BufferedInputStream. care sunt numite si clase “filtru” de intrare-iesire. deoarece unele optiuni nu au sens pentru orice flux)."while". BufferedOutputStream.

// adresa obiectului "flux" } // citirea unui octet public int read () throws IOException { return in. care va fi înlocuitã cu o variabilã de un tip flux concret (FileOutputStream. // alte metode } Metoda "read" este o metodã polimorficã. La crearea unui obiect de tipul DataInputStream constructorul primeste un argument de tipul InputStream (sau un tip derivat direct din InputStream sau din FilterInputStream) prin care se specificã suportul fizic al datelor si modul concret de citire din flux.readLine()) != null) System.. "readLine". Nu se pot crea obiecte de tipul FilterInputStream deoarece constructorul clasei este de tip protected. sau “deleagã” operatia de citire cãtre clasa folositã la construirea obiectului filtru.println (lnis. etc. Cea mai folositã este clasa DataInputStream care adaugã metodelor de citire de octeti mostenite si metode de citire a tuturor tipurilor primitive de date: "readInt". String linie.Florian Moraru – Programare Orientata pe Obiecte de tipul abstract OutputStream sau InputStream. Clasele filtru de I/E fac parte din categoria clasele “decorator”. LineNumberInputStream lnis= new LineNumberInputStream (bis).). Pentru citire linii dintr-un fisier disc metoda “readLine” deleaga citirea de octeti cãtre metoda “read” din FileInputStream: FileInputStream fis= new FileInputStream (“t.getLineNumber()+" "+linie).out. "readBoolean". readFloat". BufferedInputStream. } De obicei nu se mai folosesc variabile intermediare la construirea unui obiect flux. // citirea depinde de tipul fluxului } . Decorarea unei clase flux de I/E se poate face repetat.read ().. LineNumberInputStream sunt derivate din clasa FilterInputStream si contin metode de prelucrare a datelor citite. Clasele DataInputStream. DataInputStream dis = new DataInputStream (fis). PushbackInputStream. iar selectarea metodei necesare se face în functie de tipul concret al variabilei "in" (transmis ca argument constructorului). protected FilterInputStream (InputStream in) { // constructor (in clasa abstracta) this. cu buffer.in=in. while ( (linie=dis. la construirea unui obiect de un tip flux direct utilizabil. astfel pentru a citi linii dintr-un fisier folosind o zonã tampon si cu numerotare de linii vom folosi urmãtoarea secventã de instructiuni: public static void main (String arg[]) throws IOException { FileInputStream fis= new FileInputStream (arg[0]). . BufferedInputStream bis = new BufferedInputStream (fis).. dar se pot crea obiecte din subclase ale clasei FilterInputStream. Clasa FilterInputStream este derivatã din InputStream si contine o variabilã de tip InputStream: public class FilterInputStream extends InputStream { protected InputStream in. dintr-un fisier disc: 113 .. DataInputStream dis = new DataInputStream (lnis). Exemplu de citire linii .txt”). care aplicã diverse “decoratiuni” (functionalitãti) unor clase existente. Un decorator nu adaugã functii (metode) noi clasei decorate dar modificã actiunea unor metode existente prin delegare cãtre metode ce provin din obiectul transmis ca argument la construirea unui obiect din clasa decorator (si care înlocuieste o variabilã de tip interfatã din clasa decorator). Metoda “read” din FilterInputStream preia functionalitatea metodei “read” din clasa delegat.

in = in. Un obiect InputStreamReader poate fi folosit la fel ca un obiect Reader pentru citirea de caractere. ByteToCharConverter btc) { super(in).readLine()) != null) System. } public int read(char cbuf[ ]. Si familia claselor Reader-Writer foloseste clase decorator: public abstract class FilterReader extends Reader { protected Reader in.read(). } public int read() throws IOException { return in. this.out. } public InputStreamReader(InputStream in) { this(in. Codul urmãtor ilustreazã esenta clasei adaptor: public class InputStreamReader extends Reader { private ByteToCharConverter btc.btc = btc. int len) throws IOException { return in. int off. } Ordinea în care sunt create obiectele de tip InputStream este importantã : ultimul obiect trebuie sã fie de tipul DataInputStream.in” este de un subtip al tipului InputStream si corespunde tastaturii. Clasele InputStreamReader si OutputStreamWriter sunt clase adaptor între clasele “Stream” si clasele “Reader-Writer”. o parte din operatiile impuse de superclasã sunt realizate prin apelarea operatiilor pentru variabila flux (prin “delegare”). iar constanta “System. } public void close() throws IOException { in. Clasa BufferedReader adaugã clasei Reader o metodã “readLine” pentru a permite citirea de linii din fisiere text. } public int read() throws IOException { int byte = in. // citire octet char ch = btc.in = in.read(). octetului citit } } 114 .convert(byte). Clasele adaptor extind o clasã Reader (Writer) si contin o variabilã de tip InputStream (OutputStream). Constanta “System. metode cu numele “print” sau “println”. } } Clasele PrintStream si PrintWriter adaugã claselor filtru metode pentru scriere cu format (cu conversie) într-un flux de iesire. this. while ( (linie=dis. private InputStream in.Florian Moraru – Programare Orientata pe Obiecte public static void main (String arg[ ]) throws IOException { DataInputStream dis = new DataInputStream ( new BufferedInputStream (new FileInputStream (arg[0]))).read(cbuf.out” este de tipul PrintStream si corespunde ecranului. this. ByteToCharConverter.close().println ( linie). len).getDefault()). protected FilterReader(Reader in) { super(in). dar în interior se citesc octeti si se convertesc octeti la caractere. // caracter coresp. Este deci un alt caz de combinare între extindere si agregare. // conversie din byte in char return ch. pentru a putea folosi metode ca "readLine" si altele. private InputStreamReader(InputStream in. String linie. off.

. ambele de tip Object: public interface Map { Object get (Object key).} public boolean equals (Object obj) { return ((Entry)obj). ca membri ai clasei.. In cazul unei singure clase incluse. return v. // alti membri ai clasei Outer } Clasele incluse cu nume primesc de la compilator un nume compus din numele clasei exterioare.Entry { private Object key. Object v) { key=k. Clasa inclusã poate fi si o clasã staticã. } } // alti membri ai clasei ArrayMap private ArraySet entries . Clasele care nu sunt incluse în alte clase se numesc clase de nivel superior (“top-level classes”). Clase incluse Clase incluse O clasã Java poate contine. Un exemplu de interes pentru definirea de noi clase dictionar este interfata Entry. } public String toString() { return key+"="+val.} public Object getKey() { return key. val=v. caracterul ‘$’ si numele clasei interioare (Outer$Inner). Object value). Clasa inclusã “Entry”implementeazã interfata inclusã Map. // date si/sau metode ale clasei Inner } . public Entry (Object k.. alte clase numite clase incluse (“nested classes”). sau o clasã abstractã sau o interfatã..} public Object setValue (Object v) { val=v. structura va arãta astfel: public class Outer { ..Entry: public class ArrayMap extends AbstractMap { // clasa interioara static class Entry implements Map.equals(key). // alte metode abstracte din interfata Map public interface Entry { // acces la cheia si valoarea dintr-o pereche Object getKey(). . // multime de perechi cheie-valoare public ArrayMap (int n) { // constructor 115 .val...Florian Moraru – Programare Orientata pe Obiecte 10. cu metode asociate unei perechi cheie-valoare. // date si/sau metode ale clasei Outer public class Inner { . inclusã în interfata Map. // valoarea Object setValue(Object value).. // cheia Object getValue(). } public Object getValue() { return val. Object put (Object key.getKey(). // daca doua perechi sunt egale } } In exemplul urmãtor clasa inclusã “Entry” este folositã numai în definitia clasei “ArrayMap”. // modifica valoarea boolean equals(Object o).

nu poate fi instantiatã direct ci numai prin intermediul colectiei (nu poate exista obiect iterator fãrã o colectie).Florian Moraru – Programare Orientata pe Obiecte entries = new ArraySet(n). Object o2) { Comparable c1 = (Comparable)o1. Cuvântul “nested” se referã la relatia sintacticã dintre clase: clasa inclusã este definitã în cadrul definitiei clasei exterioare. In acest fel clasa iterator are acces la variabile private ale colectiei. Cloneable { protected int count. // alte metode si clase incluse din clasa Collections } Clase interioare O clasã inclusã nestaticã este numitã si clasã interioarã (“inner class”). while (i. fiind ascunsã altor clase.value)).hasNext()) { Entry e = (Entry) i.compareTo(o2). } } Tipul Entry este folosit în mai multe metode ale clasei AbstractMap. } public Object put ( Object key. cuvântul “inner” se referã la relatia dintre obiectele claselor incluse: un obiect al clasei exterioare contine în el un obiect al clasei incluse. Object value) { entries. Exemplu: public Object get(Object key) { Iterator i = entrySet(). private static class ReverseComparator implements Comparator. este folositã de metoda staticã “reverseOrder” prin intermediul unei variabilei statice: public class Collections { public static Comparator reverseOrder() { // din clasa Collections return REVERSE_ORDER.add (new Entry(key. return key..getValue().next(). Un exemplu este o clasã iterator inclusã în clasa colectie pe care actioneazã. } private static final Comparator REVERSE_ORDER = new ReverseComparator().equals(e.getKey())) return e. // nr de elemente in vector protected Object elementData[]. return -c1.iterator(). // vector de obiecte 116 . } Clasa interioarã staticã ReverseComparator din clasa Collections. clasã interioarã: public class Vector extends AbstractList implements List. if (key. Exemplu de iterator pe vector.Serializable { public int compare(Object o1. } public Set entrySet () { return entries.. } return null. pentru cã fiecare obiect din clasa exterioarã va contine un obiect din clasa interioarã. } } .

. } } Un exemplu din Java Tutorial aratã cã uneori iteratorul trebuie sã aibã acces la datele colectiei (private). else return null. // indice element curent din enumerare public boolean hasMoreElements() { return count < elementCount. return c1. } public Object nextElement() { if (count < v. ea este utilã numai clasei în care este definitã (clasa exterioarã "SortedArray"): public class SortedArray extends ArrayList { // clasa interioara private class DefaultComp implements Comparator { public int compare (Object e1. ca referintã la un obiect comparator dintr-o clasã interioarã. public VectorEnum (Vector v) { this. Clasa "DefaultComp" nu mai trebuie definitã de utilizatori si transmisã din afarã.compareTo(c2). care face o enumerare a elementelor stivei de la vârf spre baza stivei. } public Object nextElement() { if (count < elementCount) return elementData[count++]. Object e2) { Comparable c1=(Comparable)e1.v=v.Florian Moraru – Programare Orientata pe Obiecte // metoda care produce obiect iterator public Enumeration elements() { return new VectorEnumeration() } // definirea clasei iterator pe vector (inclusa) class VectorEnumeration implements Enumeration { int count = 0. } } // . dacã primeste o referintã la clasa colectie (ca parametru în constructor) si foloseste metode publice ale colectiei: class VectorEnum implements Enumeration { private int count = 0. In exemplul urmãtor o clasã pentru un vector ordonat ("SortedArray") contine o variabilã comparator ("cmp"). care poate fi initializatã de un constructor al clasei. 117 . Dacã se foloseste constructorul fãrã argumente. } public boolean hasMoreElements() { return count < v.size()) return v. fãrã a scoate obiecte din stivã (fãrã a folosi o altã stivã). alte metode din clasa Vector } Clasa iterator se poate defini si ca o clasã top-level. nefiind suficiente metodele publice de acces la acestea. Este cazul unui iterator pe o stivã vector. atunci variabila comparator primeste o valoare implicitã. . // indice element curent din enumerare private Vector v.size(). Comparable c2=(Comparable)e2.elementAt(count++).

Pentru a permite clasei exterioare accesul la membri private ai clasei interioare.obj.println ("A.getValue()). cmp=comp.f3").Entry e2= (Map. if (k < 0) k= -k-1. Aceste mosteniri se pot combina în obiecte ale clasei exterioare.Pentru ca o clasã sã poatã mosteni functii de la câteva clase (mostenire multiplã). . } } class M extends C { // M preia metode de la clasele A.out. Exemplu de clasã pentru un obiect comparator inclusã în functia de ordomare: // ordonarea unui dictionar în ordinea valorilor din dictionar (nu a cheilor) static void sortByValue (Map m) { // clasa inclusa class VComp implements Comparator { // compara doua perechi cheie-val public int compare (Object o1. // o pereche cheie-valoare Map. } O altã formã de clasã interioarã este o clasã definitã într-o metodã a clasei externe.out.} public SortedArray (Comparator comp) { super(). // multime de perechi cheie-valoare ArrayList entries = new ArrayList(eset).out. // alta pereche cheie-valoare return ((Integer) e1. .entrySet(). .Entry)o2. return true. O clasã interioarã poate mosteni de la o implementare (nu de la o interfatã) în mod independent de mostenirea clasei exterioare si de mostenirile altor clase incluse.binarySearch (this.Pentru clase de interes local : clasa interioarã este necesarã numai clasei exterioare. } public boolean add (Object obj) { int k=indexOf(obj).cmp).Entry)o1. } public int indexOf (Object obj) { return Collections. // comparator implicit public SortedArray () { super().println ("C.out. new VComp()).Pentru a permite claselor interioare accesul la variabile ale clasei exterioare si deci o comunicare mai simplã între clasele incluse (prin variabile externe lor). B si C 118 . } } Set eset = m.Entry e1= (Map.println ("B.compareTo ((Integer) e2. // afisare perechi ordonate dupa valori } Motivele definirii de clase interioare pot fi diverse: .add(k. pentru care se pot folosi metode mostenite de toate clasele incluse.sort (entries. obj).getValue()).f1").Florian Moraru – Programare Orientata pe Obiecte } } // alti membri ai clasei SortedArray Comparator cmp=new DefaultComp(). Object o2) { Map. Exemplu: class A { void f1 () { System.Pentru reducerea numãrului de clase de nivel superior si deci a conflictelor între nume de clase (ca urmare a unor instructiuni “import” pentru pachete în care se aflã clase cu nume identice). // ordonare vector System.f2"). } } class C { void f3 () { System. // vector de perechi cheie-valoare Collections. } } class B { void f2 () { System.println (entries). . super.

// var.f1(). // nr de noduri in lista // functii membre ale clasei SimpleList public SimpleList () { head= new Node(null). // var. m.i<k. din clasa inclusa p.val.next. m.f2(). m. din clasa inclusa for (int i=0.next != null) // var.f2(). // var. // inceput lista private int n. } public Object get (int k) { if ( k > n) return null. // var. private Node next. // santinela n=0.f1(). public class SimpleList extends AbstractList { // clasa pentru liste simplu inlantuite // clasa inclusa in clasa SimpleList class Node { private Object val. // var.next=nou. } // clasa inclusa derivata din A // clasa inclusa derivata din B // metoda a clasei M // metoda a clasei M } class X { public static void main (String arg[]) { M m = new M().next. din clasa inclusa n=n+1.f3(). } } 119 .next. return true. } } // variabile ale clasei SimpleList private Node head. Simplificarea comunicãrii între clase In exemplul urmãtor metodele clasei exterioare “SimpleList” se pot referi direct la variabile din clasa inclusã “Node” (si nu prin intermediul unor metode publice). // var. din clasa inclusa while (p. public Node (Object ob) { val=ob. din clasa inclusa } public boolean add (Object el) { // adauga la sfarsit daca nu exista deja Node nou = new Node(el). din clasa inclusa return p.i++) p=p. B si C la care se pot adãuga si alte functii suplimentare. } void f2 () { new AltB(). } public int size() { return n. Node p=head. Node p =head.Florian Moraru – Programare Orientata pe Obiecte class AltA extends A { } class AltB extends B { } void f1 () { new AltA(). din clasa inclusa p=p. next=null. } } Pentru clasa M se pot apela functii mostenite de la clasele A.

din clasa Node return obj. } . // cursor= adresa nod curent SListIterator () { // constructor pos=head. O astfel de situatie apare în cazul a douã clase cu rol de producãtor si respectiv de consumator care-si transmit date printr-o coadã de obiecte.next. alãturi de obiectul coadã. public Producer (Queue q) { this. variabilele fiind componente ale instantei curente. // var..Transmiterea unei referinte la acest obiect la construirea obiectelor producãtor si consumator .add(x). Avem cel putin douã solutii pentru a permite accesul la obiectul comun coadã: . Urmeazã mai întâi solutia cu clase de acelasi nivel: class Producer { // proces producator Queue q. accesul direct la aceste variabile este simplificat dacã se include clasa iterator în clasa colectie. public Consumer (Queue q) { this.. . } public void put(Object x) { // pune un obiect in coada q. Includerea a douã clase A si B într-o aceeasi clasã C permite simplificarea transmiterii de date între clasele A si B prin variabile ale clasei C.q=q. // var. } public Object next() { Object obj =pos.Florian Moraru – Programare Orientata pe Obiecte O clasã iterator poate lucra cu variabile ale clasei colectie pe care face enumerarea. metode } Clasele interioare cu date comune O clasã inclusã într-o altã clasã este un membru al acelei clase si are acces la ceilalti membri ai clasei (variabile si metode). } 120 . pentru acoperirea diferentei dintre ritmul de producere si ritmul de consumare a mesajelor. // alti membri ai clasei exterioare: clase. variabile. } public void remove () { throw new UnsupportedOperationException(). // inceput lista // clasa iterator inclusa class SListIterator implements Iterator { private Node pos. Instantierea clasei interioare se face prin metoda “iterator” din clasa exterioarã: public class SimpleList extends AbstractList { private Node head.Includerea claselor producãtor si consumator într-o clasã gazdã.next. din clasa Node pos=pos.val. Obiectul coadã este folosit atât de obiectul producãtor cât si de obiectul consumator.q=q. // var “head” din clasa SimpleList } public boolean hasNext () { return pos != null. } } // metoda a clasei SimpleList public Iterator iterator () { return new SListIterator(). } } class Consumer { // proces consumator Queue q.

dar se pot defini ad-hoc clase incluse anonime.isEmpty()) System. // obiectul coada static Producer p . // proces consumator static byte k=1. // activare consumator } } // daca coada goala Pentru comparatie urmeazã solutia cu clase interioare. case 1: c. switch (r) { case 0: p. Clasele incluse au fost declarate statice pentru a putea fi instantiate din metoda staticã “main”.ql. // proces producator static Consumer c .run(). } } static class Consumer { // clasa inclusa public void run() { if ( ! q.put(new Integer(k)). break. folosind cuvântul class.println(c.isEmpty()) return null. k++.Florian Moraru – Programare Orientata pe Obiecte public Object get() { if ( q. } } // simulare procese producator-consumator class Simulare { public static void main(String[] args) throws Exception { int qm=0. return q.println("Consumator " + " scoate " + q. r. break. // simulare procese producator-consumator public static void main(String[] args) { q= new Queue(). // obiectul p se refera la ob. k++.random() * 2)). Integer x. while ( k <=20) switch ((int)(Math. Producer p=new Producer(q).del().run().out. Queue q= new Queue(). break. } } static Queue q . q while ( k < 21) { r= ((int)(Math.random()*2)) { case 0: p. p=new Producer().out. break.get()). q Consumer c=new Consumer(q). k=0.del()).add(new Byte(k)). } } } Clase interioare anonime In exemplele anterioare clasele incluse au fost definite explicit. deoarece nu se folosesc cuvintele extends sau implements). // activare producator case 1: System. pe baza unei alte clase sau a unei interfete (prin extindere sau prin implementare implicitã. c=new Consumer(). // obiectul c se refera la ob. 121 . class Simulare { static class Producer { // clasa inclusa public void run() { q.

clasa inclusã implementeazã o interfatã sau extinde o altã clasã si contine numai câteva metode scurte. In plus. // aici se termina definitia clasei si instructiunea Alt exemplu de clasã comparator definitã ca o clasã interioarã anonimã: class SortedArray extends ArrayList { private Comparator cmp=new Comparator () { // comparator implicit public int compare (Object e1. public SortedArray () { super().. return c1... O situatie tipicã pentru folosirea unei clase anonime definitã simultan cu crearea unui obiect de tipul respectiv este cea în care transmitem unei functii un obiect de un subtip al interfetei Comparator (adresa unei functii de comparare).obj.. public boolean hasNext () { return pos != null. new Comparator( ) { // ordonare descrescatoare public int compare (Object t1. // rezultat invers metodei compareTo } }). } }. pos=pos. Sintaxa definirii unei clase anonime este urmãtoarea: new Interf ( ) { .c1. unde "Interf" este un nume de interfatã (sau de clasã abstractã sau neabstractã) din care este derivatã (implicit) clasa inclusã anonimã. printr-un bloc care urmeazã operatorului new cu un nume de interfatã sau de clasã abstractã. public Iterator iterator () { return new Iterator() { // definirea clasei anonime iterator ca o clasa inclusa private Node pos=head. cmp=comp. Exemplu de sortare a unei liste de obiecte în ordine descrescãtoare Collections.Florian Moraru – Programare Orientata pe Obiecte Uneori numele unei clase incluse apare o singurã datã. // definitie clasa inclusa } . return . return obj.cmp). Object e2) { Comparable c1=(Comparable)e1. pentru a crea un singur obiect de acest tip. în care clasa iterator pentru liste este anonimã si este definitã în expresia care urmeazã lui new. } 122 .binarySearch (this. Pentru astfel de situatii se admite definirea ad-hoc de clase anonime.val. c2=(Comparable)t2. Comparable c2=(Comparable)e2.compareTo(c2). // alte metode } Iatã si o altã definitie a metodei “iterator” din clasa “SimpleList”.} public SortedArray (Comparator comp) { super().next. } . O astfel de clasã nu poate avea un constructor explicit si deci nu poate primi date la construirea unui obiect din clasa anonimã. Object t2) { // incepe definitia clasei anonime Comparable c1=(Comparable)t1. } public int indexOf (Object obj) { return Collections. } public Object next() { Object obj =pos.compareTo(c2).next.sort (list.

iterator(). Exemplu: public abstract class AbstractMap implements Map { public Collection values() { // o colectie a valorilor din dictionar if (values == null) { // ptr apeluri repetate ale metodei values values = new AbstractCollection() { // subclasa interioara anonima public Iterator iterator() { return new Iterator() { // alta clasa interioara anonima private Iterator i = entrySet().getValue(). inclusiv argumente formale ale functiei definite prin blocul respectiv. } public void remove() { i. // aici se termina instr. values= new … } return values.this.next()). Intre clasa interioarã si clasa exterioarã existã un "cuplaj" foarte strâns. dar pot exista dificultãti la întelegerea codului si erori de utilizare a acoladelor si parantezelor. // aici se termina instr.this. dar itereazã pe un vector intrinsec de obiecte.containsValue(v).Florian Moraru – Programare Orientata pe Obiecte public void remove () { } }.size().remove(). } } Probleme asociate claselor incluse O clasã inclusã într-un bloc poate folosi variabile locale din acel bloc. Orice variabilã sau parametru formal folosit într-o clasã inclusã trebuie declarat cu atributul final. } public Object next() { return ((Entry)i. In prima variantã a acestei functii se defineste o clasã cu nume interioarã unui bloc si care foloseste un argument al functiei care contine definitia clasei. deoarece aceastã variabilã este copiatã în fiecare instantã a clasei incluse si toate aceste copii trebuie sã aibã aceeasi valoare (nemodificatã în cursul executiei). public boolean hasNext() { return i. acest cuplaj poate fi un dezavantaj la restructurarea (refactorizarea) unei aplicatii. dar poate fi exploatat în definirea unor clase de bibliotecã (care nu se mai modificã). } }. Exemplul urmãtor este o functie similarã functiei “iterator” dintr-o clasã colectie. } }. return new Iterator … } public int size() { // din subclasa lui AbstractCollection return AbstractMap. // this = obiect din clasa anonima } public boolean contains(Object v) { return AbstractMap. // sfârsit instructiune return new Iterator ( ) } // sfarsit metoda iterator Prin definirea de clase anonime codul sursã devine mai compact iar definitia clasei apare chiar acolo unde este folositã.hasNext(). // iterator pentru vectori intrinseci 123 . public static Iterator arrayIterator (final Object a[] ) { // clasa interioara functiei class AI implements Iterator { int i=0.

iar lista de argumente trebuie sã fie vidã. O clasã anonimã nu poate simultan sã extindã o altã clasã si sã implementeze o interfatã. int i. Un nume de interfatã poate urma cuvântului cheie new numai la definirea unei clase anonime care implementeazã acea interfatã. sau sã implementeze mai multe interfete. } public void remove() { throw new UnsupportedOperationException().} }. } Clasa AI poate sã fie definitã ca o clasã inclusã anonimã deoarece acest nume nu este folosit în afara functiei “arrayIterator”. cu nume generate automat de compilator si transmise ca argumente constructorului clasei interioare.Florian Moraru – Programare Orientata pe Obiecte public boolean hasNext() { return i < a. fie sã implementeze o interfatã (ca în exemplul anterior) cu aceeasi sintaxã.length. i=0. public boolean hasNext() { return i < a. OuterClass$1 (Object val$a[]) { // constructor this. } public Object next() { return a[i++]. Variabilele locale din clasa exterioarã sau din blocul exterior sunt copiate de compilator în câmpuri private ale clasei interioare.val$a = val$a. } public void remove() { } // neimplementata } return new AI(). } public boolean hasNext () { return i < val$a. O clasã definitã într-un bloc nu este membrã a clasei ce contine acel bloc si nu este vizibilã din afara blocului.length && a[i] != null . Numele claselor incluse anonime sunt generate de compilator prin adãugarea la numele clasei exterioare a caracterului ‘$’si a unui numãr (a câta clasã inclusã este).} } 124 . Exemplu de clasã anonimã definitã într-un bloc: public static Iterator arrayIterator (final Object a[] ) { // iterator pentru vectori intrinseci return new Iterator () { // urmeaza definitia clasei anonime int i=0. } public Object next() { return val$a[i++]. } public Object next() { return a[i++].length && a[i] != null . // C este numele clasei ce contine metoda } class C$1 implements Iterator { private Object val$a[]. // aici se termina instr. Pentru functia anterioarã compilatorul Java genereazã un cod echivalent de forma urmãtoare: public static Iterator arrayIterator (final Object a[]) { return new C$1(a). } public void remove() { throw new UnsupportedOperationException(). return } O clasã anonimã nu poate avea un constructor explicit si poate fie sã extindã o altã clasã.

Acest cadru (“Framework”) mai este numit si infrastructurã sau colectie de clase de bazã (“Foundation Classes”). folosite ca atare si care nu pot contine alte componente (un buton este un exemplu de componentã atomicã). documente . Ca infrastructurã pentru aplicatiile Java cu interfatã graficã vom considera clasele JFC (Java Foundation Classes). care grupeazã mai multe componente atomice si/sau containere. componentele vizuale sunt de douã categorii: . Componentele container sunt si ele de douã feluri: . indiferent de sistemul de operare gazdã. pozitionarea întregului grup). numite si “Swing”.Componente “atomice”.Florian Moraru – Programare Orientata pe Obiecte 11. incluse în alte containere si care permit operatii cu un grup de componente vizuale (de exemplu.Elemente de dialog .Containere de nivel superior (“top-level”) pentru fereastra principalã a aplicatiei. fatã de alte limbaje. mai nou) variante (Windows.Componente pentru selectarea unei alternative (optiuni) .Componente cu text de diverse complexitãti: câmp text. zonã text.Meniuri si bare de instrumente In POO fiecare componentã a unei interfete grafice este un obiect dintr-o clasã predefinitã sau dintr-o subclasã a clasei de bibliotecã. programarea mai simplã si mai versatilã a interfetei grafice prin numãrul mare de clase si de facilitãti de care dispune. butoane radio . Componentele atomice pot fi grupate dupã rolul pe care îl au : . .Indicatoare de progres a unor activitãti de duratã . se transmit programului prin linia de comandã). Limbajul Java permite. MacOS sau Java). Majoritatea aplicatiilor actuale preiau datele de la operatorul uman în mod interactiv. printr-o interfatã graficã. Programele cu interfatã graficã sunt controlate prin evenimente produse fie de apãsarea unei taste fie de apãsarea unui buton de mouse.Panouri cu derulare verticalã sau orizontalã (pentru liste sau texte voluminoase) . folosind atât tastatura cât si mouse-ul pentru introducere sau selectare de date afisate pe ecran. în mod text. . iar modificarea unor caractere din text genereazã un alt tip de eveniment. Un eveniment des folosit este cel produs de pozitionarea cursorului pe suprafata unui “buton” desenat pe ecran si clic pe butonul din stânga de pe mouse. dar existã o fereastrã initiala cu care începe aplicatia. De exemplu.Componente “container”. O interfatã graficã simplã constã dintr-o singurã fereastrã ecran a aplicatiei pe care se plaseazã diverse componente vizuale interactive. Tipul evenimentelor este determinat de componenta vizualã implicatã dar si de operatia efectuatã. Linux cu X-Windows etc). Clasele JFC asigurã 125 . care reprezintã o evolutie fatã de vechile clase AWT (Abstract Window Toolkit). Clase pentru o interfatã graficã Programarea unei interfete grafice (GUI) Comunicarea dintre un program de aplicatie si operatorul (beneficiarul) aplicatiei poate folosi ecranul în modul text sau în modul grafic.Butoane de diverse tipuri: butoane simple. Interfata graficã cu utilizatorul (GUI = Graphical User Interface) este mai sigurã si mai "prietenoasã". pusã la dispozitie de sistemul gazdã (Microsoft Windows.Containere intermediare (panouri). aspectul (“Look and Feel”) componentelor vizuale poate fi ales dintre trei (patru. în sensul cã asigurã o bazã de clase esentiale si impune un anumit mod de proiectare a acestor aplicatii si de folosire a claselor existente. Colectia claselor GUI constituie un cadru pentru dezvoltarea de aplicatii cu interfatã graficã. In termenii specifici Java. într-un câmp cu text terminarea unei linii de text (cu “Enter”) genereazã un tip de eveniment. pe parcursul programului se deschid si alte ferestre. numite si “controale” pentru cã permit operatorului sã controleze evolutia programului prin introducerea unor date sau optiuni de lucru (care. Uneori. Multe din clasele JFC extind sau folosesc clase AWT. De exemplu.

show(). Exemplul urmãtor afiseazã o fereastrã cu titlu.pack(). JToolBar. JButton.Clase de tip “model”. atrãgãtoare si personalizate dupã cerintele aplicatiei si ale beneficiarilor. Primele douã sunt subclase (indirecte) ale clasei Window din AWT. class EmptyFrame { public static void main ( String args[]) { JFrame frm = new JFrame(“EmptyFrame”). Controalele JFC pot fi inscriptionate cu text si/sau cu imagini (încãrcate din fisiere GIF sau definite ca siruri de constante în program). fie pentru crearea de spatii controlabile între componente vecine. JScrollBar. navigatoare pe retea si alte utilitare folosite frecvent). JDialog si JApplet. In jurul componentelor pot fi desenate borduri . având aproape acelasi nume (cu prefixul 'J' la clasele JFC) si acelasi mod de utilizare: JComponent. Un program minimal cu clase JFC. In final. creazã si afiseazã componentele vizuale pe ecran. Fereastra principalã a aplicatiei este în general de tipul JFrame si contine o barã de titlu si trei “butoane” standard în coltul dreapta-sus al ferestrei pentru operatii de micsorare (minimizare).Clase JFC noi sau extinse: JSlider. JRadioButton. se stabilesc dimensiunile ferestrei principale (metoda “setSize” sau “pack”) si se comandã afisarea ferestrei principale (metoda “setVisible” sau “show”). JTable. Cea mai mare parte dintr-un astfel de program creazã în memorie structurile de date ce contin atributele componentelor vizuale si relatiile dintre ele: se creazã un obiect fereastrã (panou). JComboBox.Florian Moraru – Programare Orientata pe Obiecte elementele necesare proiectãrii de interfete grafice complexe.setVisible (true). JLabel. frm.setSize(500. dar fãrã alte componente vizuale : import javax.*. reducând substantial efortul de programare a unor astfel de aplicatii (inclusiv editoare de texte. dupã care poate fi mãritã. DefaultTreeModel.Clase JFC care au corespondent în clasele AWT. JInternalFrame. . // sau frm. JMenuBar. fãrã sã trateze evenimentele asociate acestor componente. concepute conform arhitecturii MVC (“Model-View-Controller”): DefaultButtonModel. care constituie fundalul pentru celelalte componente. JScrollPane. inclusiv clasa container intermediar JPanel.swing. In exemplul urmãtor se extrage "panoul" ferestrei cu "getContentPane" si se adaugã o componentã JLabel (o etichetã) la acest panou: 126 . AbstractTableModel etc. JMenu. JProgressBar. se creazã componente atomice si se adaugã la panou obiectele grafice create de programator (cu metoda “add”). JSplitPanel. Toate celelalte clase JFC sunt subclase directe sau indirecte ale clasei JComponent. JPanel. . mãrire (maximizare) si închidere fereastrã . fie pentru delimitarea lor. JFileChooser. JTextField. JTabbedPane. // sau frm. JTree. JList. JCheckBox. DefaultListSelectionModel. Clase JFC pentru interfatã graficã O altã clasificare posibilã a claselor Swing este urmãtoarea: .300) frm. JColorChooser etc. JPopUpMenu. JTextArea. } } Fereastra principalã se afiseazã initial într-o formã redusã la bara de titlu cu cele 3 butoane generale. O parte din clasele JFC sunt folosite ca atare (prin instantiere) iar altele asigurã doar o bazã pentru definirea de clase derivate. Clasele JFC container de nivel superior sunt numai trei: JFrame. Pentru afisarea continutului ferestrei chiar de la început se poate folosi metoda “pack” (din clasa JFrame) sau metoda “setSize” (din clasa Container). Adãugarea de componente vizuale la fereastra principalã JFrame se poate face în douã moduri.

culoare.) . // un obiect “panou” panel.add (new JLabel ("Eticheta")) . dar nu se terminã aplicatia dacã nu estre tratat evenimentul produs de acest clic. De aceea este necesarã tratarea acestui eveniment.) .add(label).Crearea componentelor atomice si stabilirea proprietãtilor acestora (dimensiuni. tip chenar etc. } } // fereastra cu o eticheta in ea // fereastra aplicatiei // creare eticheta // adauga eticheta la fereastrã // dimensiuni fereastra // afisare continut fereastrã Inainte de versiunea 6. de ex. extras din JFrame cu metoda “getContentPane”: frame. // afisare continut fereastrã } } Dupã apelul metodei “setVisible” sau “show” nu mai trebuie create si adãugate alte componente vizuale ferestrei JFrame. ci numai la un obiect Container. class LabelFrame { // fereastra cu o eticheta in ea public static void main (String args[]){ JFrame frame = new JFrame().200).show ().setDefaultCloseOperation (JFrame. si stabilirea proprietãtilor ferestrei (titlu.). frame. text afisat.a. // adaugã eticheta la panou frame.*. iar panoul este adãugat ulterior la fereastra JFrame: // adaugare la un panou introdus apoi in obiectul JFrame import javax. // fereastra aplicatiei JPanel panel = new JPanel(). pe care se plaseazã eticheta.swing.getContentPane(). Incepând cu versiunea 1. frame. culoare. dacã nu se preferã modul implicit de dispunere în fereastrã.add (panel). crearea si afisarea unei interfete grafice necesitã urmãtoarele operatii din partea programatorului aplicatiei: . nu era posibilã adãugarea de componente atomice (JLabel. frame. care sunt obiecte de tip JPanel sau de un subtip al acestei clase. 127 . dimensiuni etc. class LabelFrame { public static void main (String args[ ]){ JFrame frame = new JFrame(). de tip JFrame sau de un subtip al tipului JFrame. sau terminarea programului de cãtre operator. .Crearea unui obiect fereastrã principalã. prin Ctrl-C.Florian Moraru – Programare Orientata pe Obiecte import javax.*.setVisible(true).) direct la un obiect JFrame.Adãugarea containerelor intermediare la fereastra aplicatiei si stabilirea modului de asezare a acestora. Solutii de programare a interfetelor grafice In general. chiar dacã se modificã date prezentate în unele din aceste componente (cum ar fi JList. // sau: frame.setContentPane (panel).EXIT_ON_CLOSE).add(label). JTable s.3 mai existã o posibilitate de terminare a aplicatiei la închiderea ferestrei principale: frame. // adauga eticheta la fereastra principala In exemplul urmãtor se creeazã un panou JPanel.swing. Efectuarea unui clic pe butonul de închidere al ferestrei principale (X) are ca efect închiderea ferestrei.Gruparea componentelor atomice în containere intermediare.setSize(200. JLabel label = new JLabel ("Eticheta"). frame.

// constructor public GUI1 ( String title) { super(title). eventual.Afisarea ferestrei principale.Metoda staticã “main” trebuie redusã la crearea unui obiect si.Tratarea evenimentelor asociate componentelor si ferestrei principale. Prima variantã foloseste o subclasã a clasei JFrame: import javax. // txt1.swing.*. } // activare interfata grafica public static void main (String arg[]) { new GUI1("GUI solution 1").*. setDefaultCloseOperation (JFrame. prin metoda “setVisible” sau “show” a clasei JFrame.show(). . add (lbl1). la apelarea unei metode pentru acel obiect.addActionListener (new TxtListener()).awt. } // initializare componente private void init() { setLayout(new FlowLayout()).Florian Moraru – Programare Orientata pe Obiecte .*. private JTextField txt1 = new JTextField (16). // constructor public GUI2 ( String title) { frame = new JFrame(title). init(). add(txt1). . class GUI2 { private JFrame frame.show(). Exemplele anterioare nu reprezintã solutia recomandatã pentru programarea unei interfete grafice din urmãtoarele motive: .Variabilele referintã la obiecte JFC nu vor fi locale metodei “main” pentru cã ele sunt folosite si de alte metode. import java. inclusiv metode activate prin evenimente. init().swing. } } Varianta a doua foloseste “delegarea” sarcinilor legate de afisare cãtre un obiect JFrame. Obiectul apartine unei clase definite de programator si care foloseste clasa JFrame sau JPanel. import java. setSize (300. frame. class GUI1 extends JFrame { private JLabel lbl1 = new JLabel ("Directory"). continut de clasa GUI: import javax. } // initializare componente private void init() { 128 .awt. private JTextField txt1 = new JTextField (16). Vom prezenta în continuare trei variante uzuale de definire a clasei GUI în cazul simplu al unui câmp text însotit de o etichetã ce descrie continutul câmpului text.EXIT_ON_CLOSE). private JLabel lbl1 = new JLabel ("Directory").*.100). prin definirea de clase de tip “ascultãtor” la evenimentele generate de componentele vizuale.

frame. Metoda “init” de initializare a componentelor JFC poate lipsi dacã are numai câteva linii. } public static void main (String arg[]) { JFrame frame = new JFrame ("GUI solution 3"). // constructor public GUI3 () { init(). JTextField. frame. class GUI3 extends JPanel { private JLabel lbl1 = new JLabel ("Directory").setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE).setContentPane(new GUI3()). Variabilele de tipuri JFC ( JLabel.100). deoarece ele nu vor mai fi vizibile pe ecran. In schimb. Dupã executia metodei “setVisible” nu se vor mai crea alte obiecte JFC (de exemplu. // txt1. frame. ca rãspuns la evenimente generate de operatorul uman). s. deoarece va exista un singur obiect GUI.swing.setDefaultCloseOperation (JFrame.100). frame. dar este mult mai simplu sã apelãm la un obiect de control al asezãrii în panou (“Layout Manager”). frame. private JTextField txt1 = new JTextField (16). De observat cã pentru clasele GUI constructorul este cea mai importantã functie si uneori singura functie din clasã.add (lbl1).add (new GUI3()). frame. Dacã sunt putini ascultãtori. // frame. atunci clasa GUI poate implementa una sau câteva interfete de tip ascultator la evenimente si sã continã metodele de tratare a evenimentelor (dispare necesitatea unor clase ascultãtor separate). // txt1.setSize (300.EXIT_ON_CLOSE). frame. frame. } // activare interfata grafica public static void main (String arg[]) { new GUI2("GUI solution 2").*. add(txt1).setLayout(new FlowLayout()). metoda “setText” din clasele JTextField si JLabel poate modifica textul afisat în astfel de componente JFC. } } 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.a) pot fi initializate la declarare sau în constructorul clasei GUI.addActionListener (new TxtListener()).setSize(300.show(). de exemplu. } // initializare componente private void init() { add (lbl1).addActionListener (new TxtListener()).add(txt1). Dispunerea componentelor într-un panou Plasarea componentelor grafice pe un panou se poate face si prin pozitionare în coordonate absolute de cãtre programator. se practicã modificarea continutului afisat în componentele deja existente.Florian Moraru – Programare Orientata pe Obiecte frame. } } Varianta 3 defineste clasa GUI ca o subclasã a clasei JPanel: import javax. obiect selectat prin metoda “setLayout” si care stabileste automat dimensiunile 129 .

”Center”) cp. Exemplu: class FlowLayoutDemo { // Asezare butoane in fereastra public static void main (String args[ ]) { JFrame frame = new JFrame().In cazul unor componente de diferite dimensiuni BoxLayout sau GridBagLayout permite un control mai bun al plasãrii componentelor si al intervalelor dintre ele.add (new JButton (String. // cp.BorderLayout. // cp. // cp..setVisible(true). } } De observat cã pentru un panou JPanel asezarea implicitã este FlowLayout. Exemplu de plasare a trei butoane: class DefaultLayout { // exemplu de asezare butoane in fereastra public static void main (String args[ ]) { JFrame frame = new JFrame().add (..In cazul unei singure componente în panou care sã foloseascã la maximum suprafata acestuia se va alege GridBagLayout sau BorderLayout : pentru o zonã text sau o listã de selectie JList.i++) // 6 butoane cu cifre cp. Pentru panoul de “continut” al ferestrei JFrame este activ implicit “BorderLayout”.add (. Acest efect se poate obtine folosind un GridLayout cu douã coloane sau un BoxLayout cu asezare pe orizontalã. de exemplu.setSize(400. Container cp = frame.valueOf(i)) )... GridBagLayout. . la alegere) si CardLayout ( componente / panouri care ocupã alternativ acelasi spatiu pe ecran).add(new JButton(“ 3 ”).. } } De observat cã obiectul extras cu "getContentPane" are tipul Container. De exemplu..200). “West”) frame. de exemplu. Container cp = frame. . BorderLayout.. Asezarea componentelor într-un panou se poate modifica automat atunci când se modificã dimensiunile panoului. frame. atunci ele sunt suprapuse pe centrul ferestrei.BorderLayout. mod care foloseste un al doilea parametru în metoda “add”.. Alte modalitãti de dispunere a componentelor într-un panou sunt GridLayout (o matrice de componente egale ca dimensiune).add(new JButton(“ 2 ”).getContentPane().add (.”East”) cp. frame.setLayout(new FlowLayout()). BoxLayout ( asezare compactã pe verticalã sau pe orizontalã. dimensiunile sau numãrul componentelor (în modul FlowLayout). de exemplu.EAST)..i<7. O altã solutie este alegerea modului FlowLayout. cp. o etichetã (obiect JLabel) trebuie sã aparã întotdeauna la stânga unui câmp text sau deasupra unei zone text.CENTER). Existã diverse metode de a mentine pozitia relativã a douã sau mai multe componente vizuale.add(new JButton(“ 1 ”). Alegerea modului de dispunere depinde de specificul aplicatiei : . 130 . // sau new GridLayout() for (int i=1. iar dacã sunt mai multe componente.Florian Moraru – Programare Orientata pe Obiecte si pozitia fiecãrei componente într-un panou. indiferent de dimensiunile panoului unde sunt plasate.getContentPane().setVisible(true). care aseazã componentele una dupã alta de la stânga la dreapta si de sus în jos în functie de dimensiunile lor si ale ferestrei principale.In cazul a câteva componente ce trebuie sã aparã în mãrimea lor naturalã si cât mai compact se va folosi BoxLayout sau BorderLayout: pentru câteva butoane sau câmpuri text. cp. Dacã nu se specificã pozitia la adãugare. atunci componenta este centratã în fereastrã.In cazul mai multor componente de aceeasi mãrime se va folosi GridBagLayout: grupuri de butoane.WEST). .

// comandã afisarea } } Principala utilizare a unui câmp text este pentru introducerea de date de cãtre operatorul aplicatiei. // interzice editare camp text pane. // sau frame. public static void main (String args[]) { JPanel pane = new JPanel().add (new JLabel("Vârsta")). JPanel p =new JPanel(). // rubrica pentru nume p.setText ( dir.100).add (new JTextField (8)) . // rubrica pentru vârstã f. Un câmp text (JTextField) permite afisarea.add(pane) frame. static JTextField tf = new JTextField (20).setSize(300. // directorul curent pane. O zonã text (JTextArea) permite afisarea mai multor linii de text. introducerea si editarea unei linii de text în cadrul unei ferestre text.add(tf). frame. // o eticheta ptr rubrica varsta p.add (new JLabel("Nume")).setEditable(false). care pot fi introduse sau 131 . La construirea unui obiect JTextField se foloseste ca parametru un sir de caractere sau un întreg pentru dimensiunea ferestrei text.setVisible(true). // adaugã câmp text pe panou frame. Pentru a informa operatorul asupra semnificatiei unui câmp text se poate adãuga o etichetã fiecãrui câmp.100). // nume cu cale completa tf. // adauga panou la fereastra principala f.add (new JTextField (20)). // comanda afisarea pe ecran } } Realizarea unui formular cu mai multe rubrici este în general mai complicatã decât în exemplul anterior pentru cã necesitã controlul dispunerii câmpurilor text si etichetelor asociate.setContentPane(pane).setVisible(true). // o eticheta pentru rubrica nume p.add (new JLabel("Current Directory")). File dir =new File(". Exemplu de utilizare câmpuri text.setContentPane(p).Florian Moraru – Programare Orientata pe Obiecte Componente vizuale cu text Un text scurt (nu mai lung de o linie) poate fi afisat în mod grafic (într-o zonã de pe ecran) folosind diverse componente vizuale: o etichetã (obiect JLabel) contine un text constant (nemodificabil din exterior)."). f. iar un câmp text (obiect JTextField) contine un text si permite modificarea textului de cãtre operator sau prin program. Cele mai importante metode ale clasei JTextField sunt : setText(String txt) getText() setEditable(boolean b) // afisare text din program // citire text introdus de utilizator in câmp // permite sau interzice editarea de text In exemplul urmãtor se foloseste un câmp text (nemodificabil de la tastaturã) pentru afisarea numelui directorului curent: // afisare nume director curent class UseTextField { static JFrame frame = new JFrame(). folosind alte panouri si margini de separare între aceste panouri.setSize(300. // adaugã etichetã pe panou tf. p. cu etichete asociate. într-un formular de introducere date: class InputForm { public static void main (String arg[]) { JFrame f = new JFrame().getAbsolutePath() ).

cum ar fi un formular de introducere date sau o foaie de calcul. frm. Exemplu cu o zonã text pentru afisarea numelor fisierelor din directorul curent: public static void main (String av[ ]) { File dir = new File(". // directorul curent String files[ ] = dir. dar în JFC componenta JTextArea trebuie inclusã într-un panou cu derulare (JScrollPane) pentru a putea aduce în fereastra vizibilã elemente ce nu pot fi vãzute din cauza dimensiunii limitate a zonei text.show().5). // texte afisate in ComboBox JComboBox cbox = new JComboBox(s). Exemplu de utilizare zonã text : public static void main ( String args [ ]) { JFrame frm = new JFrame ().add (et. Un obiect JComboBox afiseazã pe ecran numai linia selectatã.add (ta). Tratarea unui eveniment se face într-o metodã cu nume si argumente impuse (functie de tipul evenimentului). // în panou cu derulare win."West"). frm. for (int i=1."Size"}. // o eticheta asociata Container cp = frm. frm."Center")."Date". Selectarea componentei focalizate se poate face fie prin program (metoda “requestFocus” sau “grabFocus”).append (" "+files[i]+"\n"). Dimensiunile zonei text se stabilesc la construirea unui obiectului JTextArea. // adauga eticheta si ComboBox frm.Florian Moraru – Programare Orientata pe Obiecte modificate de la tastaturã sau prin program (cu metoda “append”). cp. // comanda afisarea } Operatorul poate deplasa cursorul în cadrul zonei text si poate modifica textul.pack(). Exemplu: public static void main (String arg[ ]) { JFrame frm = new JFrame(). se spune cã tastatura este focalizatã pe acea componentã sau cã acea componentã detine controlul tastaturii.add (cbox. succesiv la JSpinner sau la cerere). pentru o altã selectie.getContentPane().list().append (100*i+"\n"). // comanda afisarea } Pentru ca programul sã poatã reactiona la selectarea sau modificarea unor linii trebuie adãugate programelor anterioare secvente de tratare a evenimentelor produse de aceste actiuni exterioare programului. // adauga nume fisiere la zona win. // fisiere din directorul curent JFrame win= new JFrame("Current Directory").length."). In cazul unui panou cu mai multe componente text.20).setVisible(true). dar prin mouse se poate cere afisarea tuturor liniilor continute.setVisible(true). Clasele JList. JComboBox si JSpinner permit selectarea unei linii (unei optiuni) din mai multe linii afisate (simultan la JList. // titlul ferestrei JTextArea ta = new JTextArea(20.getContentPane(). String s[ ]={"Name". cp. frm.add (new JScrollPane(ta)).pack(). // creare obiect ComboBox JLabel et = new JLabel ("Sort By: ")."Ext".i++) ta. JTextArea ta = new JTextArea(10. existã o singurã componentã care primeste date de la tastaturã. fie de cãtre operator prin clic pe mouse dupã pozitionare pe componentã sau prin tasta Tab (care mutã 132 .i<21.getContentPane().i<files. // o zona text for (int i=0. metodã inclusã într-o clasã care implementeazã o anumitã interfatã (functie de eveniment). win. } // max 10 linii cu max 5 caractere // adauga siruri de cifre // fiecare numar pe o linie separata In AWT o zonã text este automat plasatã într-o fereastrã cu derulare pe verticalã si pe orizontalã.i++) ta.pack().

} } // un panou cu doua butoane // alt panou cu butoane // 4 butoane // panou principal al aplicatiei // creare butoane // dispunere in panoul p1 // butoane din panoul p1 // dispunere in panoul p2 // butoane din panoul p2 // adauga panouri la panou princ Exemplul urmãtor afiseazã în douã panouri "legate" douã liste de fisiere diferite. fãrã suprapunere între ele. Pentru inserarea de spatii între panouri se pot crea margini (borduri) în jurul fiecãrui panou. 133 . p2).k++) b[k]= new JButton(“ “+ k +” “). public MFrame () { Container w = getContentPane(). dar el poate fi modificat astfel ca în panoul din dreapta sã se afiseze continutul fisierului director selectat din lista afisatã în panoul din stânga . w. f.add (b[3]).”South”).add (b[0]).setLayout (new FlowLayout()). Gruparea unor componente în (sub)panouri permite manipularea unui panou separat de celelalte si alegerea altui mod de dispunere în fiecare panou. Orice obiect Swing care poate primi intrãri de la tastaturã ( inclusiv butoane) poate detine controlul (“focus”). p1.show (). JScrollPane p2 = dirlist("c:\\").100). } // utilizare MFrame public static void main ( String args []) { JFrame f = new MFrame ().HORIZONTAL_SPLIT. for (int k=0. f.setLayout (new FlowLayout() ). . . p1. p2.Florian Moraru – Programare Orientata pe Obiecte focalizarea pe urmãtoarea componentã din fereastrã). Panourile pot fi independente unele de altele sau pot fi legate.k<4.setSize (100.Panouri multiple afisate succesiv in aceeasi zonã ecran : JTabbedPane. class SplitPane extends JFrame { public SplitPane() { JScrollPane p1 = dirlist(". In fiecare dintre panourile unei aplicatii putem avea un panou cu derulare JScrollPane.”North”).add (b[1]). Panouri multiple In cadrul unei ferestre principale avem urmãtoarele posibilitãti de lucru cu panouri: . JSplitPane sp = new JSplitPane(JSplitPane. getContentPane(). w. p2.add (b[2]). p1.Panouri multiple afisate simultan."). astfel ca selectarea unui element dintr-un panou sã aibã ca efect modificarea continutului celuilalt panou. . p1. JButton b[] = new JButton[4]. Exemplu cu douã panouri independente afisate simultan class MFrame extends JFrame { JPanel p1 = new JPanel().add (p2. p2.add (p1.add(sp).Panouri multiple afisate simultan si partial suprapus: JLayeredPane. Câstigarea sau pierderea controlului de cãtre o componentã produce evenimente specifice (“FocusEvent”) si apelarea de metode “focusGained” sau “focusLost”. JPanel p2 = new JPanel().Panouri divizate în douã (pe orizontalã sau pe verticalã): JSplitPanel. In cazul unui formular cu multe câmpuri text sau al unui tabel (JTable) pierderea focalizãrii poate însemna terminarea introducerii de date în acel câmp sau celulã si deci poate înlocui evenimentul produs de tasta Enter (terminare introducere text).

public void init () { add (et). dar care depinde de un alt program “gazdã” pentru crearea fereastrei principale (care nu trebuie creatã de programatorul apletului). Din punct de vedere sintactic un aplet este o clasã Java.setSize(400. în aceeasi zonã.list(). afisatã permanent pe ecran alãturi de “tab”-urile celorlalte panouri selectabile). public class LabelAplet extends Applet { Label et= new Label ("Eticheta". add(tabbedPane). } Apleti Java Cuvântul aplet (“applet”) desemneazã o micã aplicatie care foloseste ecranul în mod grafic. null.CENTER). tabbedPane. în functie de selectia operatorului (fiecare panou are un “tab” de prindere. } .applet. } } 134 . frame. adicã o micã portiune cu numele panoului. } } In exemplul urmãtor cele douã panouri sunt afisate alternativ. derivatã din clasa Applet sau din JApplet. Programarea unei interfete grafice într-un aplet este putin mai simplã decât într-o aplicatie deoarece apletul mosteneste de la clasa Panel (si de la clasele Container si Component) o serie de metode utile (inclusiv metoda “windowClosing”).Florian Moraru – Programare Orientata pe Obiecte } public static JScrollPane dirlist (String dirname) { String files[] = new File(dirname). t2="C:\\".200).addTab("Dir of "+t1. JScrollPane p2 = dirlist(t2). Fereastra de afisare a unui aplet nu poate fi manipulatã direct de operatorul uman ci numai indirect. JScrollPane p1 = dirlist(t1). null.awt.setVisible(true). Clasa JApplet este indirect derivatã din clasa Panel .*. Codul unui aplet (fisierul . tabbedPane.addTab("Dir of "+t2. Exemplu de aplet scris în varianta AWT: import java. class TabbedPane extends JFrame { public TabbedPane() { String t1=".. import java. Programul gazdã este fie un program navigator (“browser”).Label. } public static void main(String s[]) { JFrame frame = new SplitPane(). JTabbedPane tabbedPane = new JTabbedPane(). mãrire si micsorare. JList lst = new JList(files). prin fereastra programului browser. p1. setLayout(new GridLayout(1. destinat vizualizãrii rezultatului executiei unui aplet. return new JScrollPane(lst). "").. p2. fie programul “appletviewer”.". "").class) poate fi adus de cãtre browser si de la un alt calculator decât cel pe care se executã. frame. care asigurã oricãrui aplet o fereastrã cu butoane de închidere.*. 1)).

ceea ce conduce la instructiuni de forma urmãtoare comp.*. Un aplet care trateazã evenimente externe trebuie sã continã si metodele de tratare a evenimentelor. Clasa aplet mosteneste si redefineste de obicei metodele “init”. adicã la actionarea butonului din stânga de pe mouse dupã mutare mouse pe zona ecran ocupatã de buton. Dimensiunile ferestrei folosite de aplet se dau în fisierul de tip "html" si nu în codul Java. public class Aplet extends JApplet implements ActionListener { 135 . public class JAplet extends JApplet { JLabel et= new JLabel ("Eticheta".add (aplet). } } Clasa JApplet este derivatã din clasa Applet si permite în plus folosirea unui meniu într-un aplet si a componentelor vizuale noi din JFC (fãrã echivalent în AWT).Florian Moraru – Programare Orientata pe Obiecte Acelasi aplet în varianta JFC aratã astfel: import javax. "paint" si alte câteva metode. “start”. separate de clasa aplet. apelate de programul gazdã la producerea anumitor evenimente. Fisierul “class” generat de compilator pentru un aplet este specificat într-un fisier “html”. f. JAplet aplet = new JAplet(). aplet.addXXXListener(this). împreunã cu dimensiunile ferestrei folosite de aplet.JLabel. Este posibil ca anumite programe de navigare mai vechi (“Browser”) sã nu recunoascã clase JFC si din acest motiv s-a dat si varianta AWT pentru aplet.init().swing.setVisible (true). public void init () { getContentPane(). Obiectul ascultãtor la evenimente este chiar obiectul aplet. // comp este numele unei componente din aplet Exemplul urmãtor este un aplet care afiseazã un buton în centrul ferestrei puse la dispozitie de programul gazdã si emite un semnal sonor ("beep") la "apãsarea" pe buton.getContentPane(). Exemplu de fisier “html” necesar pentru executia apletului precedent: <applet code="JAplet. f.class" width="250" height="100"> </applet> In comanda “appletviewer” este specificat numele fisierului “html” si nu apare direct numele fisierului “class”. Din punct de vedere functional un aplet contine câteva functii. la care se adaugã un obiect aplet si se apeleazã metoda “init”: public static void main (String args[ ]) { JFrame f = new JFrame(). între marcajele <applet> si </applet>. Functia “init” este înlocuitã cu functia “main” la trecerea de la un aplet la o aplicatie. pentru cã nu se admit alte clase ascultãtor.CENTER).add (et). De remarcat cã o clasã care corespunde unui aplet trebuie sã aibã atributul public si nu contine o metodã “main”. } // se adauga la clasa JAplet O altã posibilitate este crearea unei clase separate în care se preia codul din aplet si se adaugã crearea si afisarea ferestrei principale a aplicatiei (de tip JFrame). O clasã aplet poate fi transformatã într-o aplicatie prin adãugarea unei functii “main” în care se construieste un obiect JFrame. care trebuie (re)definite de utilizator si sunt apelate de programul gazdã.

drawString ("text in aplet".beep(). getContentPane(). 30). getSize(). la încãrcarea codului apletului în memorie. 10.1. // afisare text } 136 .getDefaultToolkit().>. 0.. public void init() { button = new JButton("Click Me").Florian Moraru – Programare Orientata pe Obiecte JButton button. // semnal sonor } } Metoda "init" este apelatã o singurã datã. BorderLayout.. // margini fereastra g. // obiectul receptor este chiar apletul } public void actionPerformed(ActionEvent e) { // tratare eveniment buton Toolkit. iar metoda "start" este apelatã de fiecare datã când programul browser readuce pe ecran pagina html care contine si marcajul <applet . getSize(). Metoda "paint" are un parametru de tip Graphics. button. iar clasa Graphics contine metode pentru desenarea de figuri geometrice diverse si pentru afisarea de caractere cu diverse forme si mãrimi: public void paint (Graphics g) { g.drawRect (0.height .CENTER).width .addActionListener(this).1).add(button.

tastare “Enter”.). Functiile care preiau date nu sunt apelate direct si explicit de alte functii din program. deci trebuie sã continã anumite metode cu nume si semnãturã impuse de clasele JFC. De exemplu.a. Anumite interfete ascultãtor (“listener”) contin o singurã metodã.Obiecte generatoare de evenimente (sursa unui eveniment).a. La fiecare obiect generator de evenimente se pot “înregistra” (se pot înscrie) mai multe obiecte ascultãtor interesate de producerea evenimentelor generate. independent de evolutia programului si al cãrui moment de producere nu poate fi prevãzut la scrierea programului. etc. Tratarea evenimentelor Swing Programatorul unei aplicatii Java cu evenimente trebuie: . Intr-un program dirijat de evenimente existã douã tipuri principale de obiecte: . Un program controlat prin evenimente nu initiazã momentul introducerii datelor. Operatia de înregistrare se face prin apelarea unei metode de forma “addXListener” (din obiectul generator).Florian Moraru – Programare Orientata pe Obiecte 12.Sã înregistreze obiectele ascultãtor la obiectele generatoare de evenimente. un clic pe butonul de închidere a unei ferestre JFrame genereazã un alt eveniment decât un clic pe butonul de micsorare a ferestrei. 137 .). dar alte interfete contin mai multe metode ce corespund unor evenimente diferite asociate aceleeasi componente vizuale. Programare bazatã pe evenimente Evenimente Swing Programarea dirijatã de evenimente (“Event Driven Programming”) se referã la scrierea unor programe care reactioneazã la evenimente externe programului (cauzate de operatorul uman care foloseste programul). actionarea unui buton de “mouse”. unde ‘X’ este numele (tipul) evenimentului si care este totodatã si numele unei interfete. fie din taste (apãsare buton. interfata WindowListener contine sapte metode pentru tratarea diferitelor evenimente asociate unei ferestre (deschidere. Structura unui program dirijat prin evenimente diferã de structura unui program obisnuit prin existenta functiilor speciale de tratare a evenimentelor (“Event handlers”). Evenimentele JFC pot fi clasificate astfel: . Tipul obiectului eveniment este determinat de tipul componetei GUI care a generat evenimentul dar si de actiunea operatorului uman. In Java un eveniment este un obiect de un tip clasã derivat din clasa EventObject. micsorare). dar poate reactiona prompt la orice eveniment produs de o actiune a operatorului. câmp text. închidere. activare. s. care primeste ca argument un obiect “eveniment”. ci sunt apelate ca urmare a producerii unor evenimente.Evenimente asociate fiecãrei componente vizuale (buton.Obiecte receptoare de evenimente (obiecte ascultãtor). De exemplu. Evenimente tipice sunt: apãsarea unei taste. Aceste clase trebuie sã implementeze anumite interfete JFC. deplasare “mouse” s. dezactivare. generate fie prin “mouse”. Prin “eveniment” se întelege aici un eveniment asincron. Notiunea de eveniment a apãrut ca o abstractizare a unei întreruperi externe . care implementeazã interfete JFC.Evenimente asociate dispozitivelor de introducere ( mouse sau tastaturã). care nu sunt apelate direct din program.Sã defineascã clasele ascultãtor. . . Clasele receptor de evenimente sunt scrise de cãtre programatorul aplicatiei pentru cã metodele de tratare a evenimentelor observate sunt specifice fiecãrei aplicatii. prin definirea metodei sau metodelor interfetei pentru tratarea evenimentele observate. . Clasele generator sunt în general clase JFC sau AWT si ele creeazã obiecte “eveniment” ca efect al actiunii operatorului pe suprafata componentei respective. Declansarea unui eveniment are ca efect apelarea de cãtre obiectul generator a unei metode din obiectul ascultãtor.

*. import javax.event.awt.show ().Florian Moraru – Programare Orientata pe Obiecte Pentru a trata numai evenimentul “închidere fereastrã” este suficient sã scriem numai metoda “windowClosing”.show ().awt. iar programatorul trebuie sã redefineascã doar una sau câteva metode. iar clasa KeyAdapter contine definitii nule pentru cele trei metode. } Evenimente de mouse si de tastaturã Interfata KeyListener contine trei metode : “keyPressed”. f. majoritatea cu definitie nulã (fãrã efect).exit(0). } } class FrameExit { public static void main (String args[ ]) { JFrame f = new JFrame ().swing. care extinde clasa WindowAdapter si redefineste metoda “windowClosing”: class WindExit extends WindowAdapter { public void windowClosing( WindowEvent e) { System. import javax. Pentru a simplifica sarcina programatorului este prevãzutã o clasã “adaptor” WindowAdapter care contine definitii cu efect nul pentru toate metodele interfetei.*. static JTextArea ta. Pentru a compila exemplele care urmeazã se vor introduce la început urmãtoarele instructiuni: import java. Dacã s-ar defini o clasã care sã implementeze aceastã interfatã atunci ar trebui sã definim toate cele sapte metode ale interfetei.*. In exemplul urmãtor se defineste o clasã separatã. } } Putem folosi si o clasã inclusã anonimã pentru obiectul ascultãtor necesar: public static void main (String args[ ]) { JFrame f = new JFrame ().event. import java.append("\n key typed: " + e. f. class KeyHandler extends KeyAdapter { public void keyPressed(KeyEvent e) { ta.addWindowListener ( new WindExit()).swing. } }).exit(0). “keyTyped” si “keyReleased”.addWindowListener ( new WindowAdapter() { public void windowClosing( WindowEvent e) { System. Metoda “windowClosing” din aceastã clasã nu are nici un efect si trebuie redefinitã pentru terminarea aplicatiei la închiderea ferestrei principale. f. f.getKeyChar() ).*. In exemplul urmãtor este tratat numai evenimentul de tastã apãsatã prin afisarea caracterului corespunzãtor tastei: class KeyEvent1 { static JFrame f= new JFrame(). 138 . static JTextField tf.

class MouseClicks { static int clicks=0. class KeyHandler extends KeyAdapter { // tratare eveniment tastatura public void keyPressed(KeyEvent e) { ta. a cãrui apãsare are ca efect focalizarea tastaturii pe câmpul text. “mousePressed”. } Evenimentele de la tastaturã sunt asociate componentei selectate prin mouse. // modifica afisarea } 139 . dar ele pot fi extinse pentru a prezenta orice caracter introdus. final JLabel label= new JLabel("0")..requestFocus().”mouseDragged”).getContentPane(). // aici se afiseaza caracterele introduse Container cp = f.setText("").addKeyListener(kd. In exemplul urmãtor s-a adãugat un buton de stergere a continutului zonei text. } } // tratare eveniment buton class ActHandler implements ActionListener { public void actionPerformed(ActionEvent e) { ta. } Programele anterioare lucreazã corect numai pentru taste cu caractere afisabile. cp. Interfata MouseInputListener contine metode apelate la actiuni pe mouse (apãsarea sau eliberare buton: “mouseClicked”.new KeyHandler()).add(new JScrollPane (ta)).Florian Moraru – Programare Orientata pe Obiecte } } public static void main(String args[]) { KeyEvent1 kd = new KeyEvent1().show( ). static JButton button = new JButton("Clear").. Exemplul urmãtor aratã cum se poate reactiona la evenimentul de “clic” pe mouse prin numãrarea acestor evenimente si afisarea lor. static JTextField tf. ta = new JTextArea(20. neinteractiva label. f. // numar de apasari pe mouse public static void main (String args[]) { JFrame frame = new JFrame(). tf.setText("").addMouseListener (new MouseInputAdapter() { // receptor evenimente public void mouseClicked(MouseEvent e) { ++clicks. class KeyEvent2 { static JFrame f= new JFrame().add(tf).append("\n key typed: " + e.20). “mouseReleased”) si metode apelate la deplasare mouse (“mouseMoved”. MouseInputAdapter oferã o implementare nulã pentru toate cele 7 metode. Este posibilã si selectarea prin program a componentei pe care se focalizeazã tastatura folosind metoda care existã în orice componentã JFC numitã “requestFocus”. tf = new JTextField(20). // o comp. label. // refocalizare pe TextField } } . static JTextArea ta.toString()).getKeyChar() ). cp. cp.setText((new Integer(clicks)). // aici se introduc caractere tf.setLayout(new GridLayout()). tf.

pentru ca ea sã poatã primi intrãri de la tastaturã.getContentPane(). La apãsarea unui buton se creeazã un obiect de tip ActionEvent si se apeleazã metoda numitã “actionPerformed” (din interfata ActionListener) pentru obiectul sau obiectele înregistrare ca receptori la acel buton (prin apelul metodei “addActionListener”). cauzate de un clic pe imaginea componentei sau de apãsarea unei taste. In general nu se trateazã toate evenimentele ce pot fi generate de o componentã JFC. KeyEvent.getContentPane(). . ListSelectionEvent.a.ComponentEvent : produs de o modificare în dimensiunea. frame.MouseEvent : produs de apãsare sau deplasare mouse pe suprafata componentei.setVisible(true). si pentru fiecare tip de eveniment este prevãzutã o metodã de tratare a evenimentului. ListDataEvent.ActionEvent : produs de actiunea asupra unei componente prin clic pe mouse. .beep(). în mod uzual se foloseste numai ActionEvent si deci se apeleazã numai metoda “actionPerformed” din obiectele înregistrate ca receptori pentru butonul respectiv. MouseEvent. DocumentEvent etc. ComponentEvent . pot fi descrise astfel: . Programatorul trebuie sã defineascã clase ce implementeazã aceste interfete cu metode de tratare a evenimentelor.Florian Moraru – Programare Orientata pe Obiecte }). . // produce semnal sonor (beep) } } 140 . ChangeEvent (dacã s-a modificat ceva în starea butonului) s. frame. “remove*Listener”. Se poate spune cã se produc efectiv numai evenimentele pentru care s-au definit ascultãtori si deci metode de tratare a evenimentelor. .KeyEvent : produs de apãsarea unei taste si asociat componentei pe care este focalizatã tastatura.add(label). în sensul cã dupã ce se afiseazã forma si continutul componentei este posibil un clic cu mouse-ul pe suprafata componentei sau deplasarea unor elemente ale componentei. ceea ce genereazã evenimente. pozitia sau vizibilitatea componentei. Un buton este un obiect Swing de tip JButton care genereazã câteva tipuri de evenimente: ActionEvent (dacã s-a actionat asupra butonului). Desi un buton poate genera peste 5 tipuri de evenimente. Exemplu de tratare a evenimentului de clic pe un buton. FocusEvent.setLayout (new FlowLayout()). Evenimentele comune. Metodele de tratare sunt grupate în interfete. mostenite de la clasa Component. frame. } } Evenimente asociate componentelor JFC Orice componentã vizualã AWT sau JFC poate fi sursa unui eveniment sau chiar a mai multor tipuri de evenimente.Evenimente comune tuturor componentelor JFC: ActionEvent. prin emiterea unui semnal sonor la fiecare clic: // clasa ptr obiecte ascultator de evenimente class Listener implements ActionListener { public void actionPerformed(ActionEvent e) { Toolkit. MenuEvent. Astfel de clase “adaptor” au rolul de intermediar între componentã si aplicatie. Fiecare componentã genereazã anumite tipuri de evenimente. 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 suprafata sa). Evenimentele generate de componentele JFC pot fi clasificate în: . Majoritatea componentelor vizuale sunt interactive. De asemenea editarea textului dintrun câmp text sau dintr-o zonã text produce evenimente.getDefaultToolkit(). Numele evenimentelor apar în clasele pentru obiecte “eveniment” si în numele unor metode: “add*Listener”.FocusEvent: produs de “focalizarea” claviaturii pe o anumitã componentã JFC.Evenimente specifice fiecãrui tip de componentã: ChangeEvent.

cp. // acelasi ascultator la toate casutele } Container cp = getContentPane()."B". evenimentele generate de componente JFC contin în ele sursa evenimentului si alte informatii specifice fiecãrei componente.i++) cp.add(cb[i]).i++) if (source == cb[i]) { source. } } Pentru a obtine acelasi efect si prin apãsarea unei taste asociate butonului (de exemplu combinatia de taste Ctrl-C) este suficient sã adãugãm o instructiune: buton. for (int i=0. } // Ascultator la casute cu bifare. // definitia clasei receptor Diferenta dintre evenimentul generat de un buton si un eveniment generat de clic pe mouse este cã primul apare numai dacã dispozitivul mouse este pozitionat pe aria ocupatã de buton. In plus.setLayout(new GridLayout(0.i++) { cb[i] = new JCheckBox (aa[i]).beep().VK_C). în timp ce al doilea apare indiferent de pozitia mouse pe ecran. button. BorderLayout. Exemplul urmãtor foloseste componente de tip JCheckBox (cãsute cu bifare) care genereazã alt tip de evenimente (ItemEvent). // creare 3 casute cb[i]. ilustreazã un ascultãtor la mai multe surse de evenimente si utilizarea unei metode “removeXXXListener”: class CheckBox1 extends JFrame { JCheckBox cb[] = new JCheckBox[3]. String [ ] aa = {"A".getDefaultToolkit(). for (int i=0.add(buton. JTextField a = new JTextField(30). 1)). class CheckBoxListener implements ItemListener { public void itemStateChanged(ItemEvent e) { JCheckBox source = (JCheckBox)e.i<3. // ptr a evita repetarea selectiei 141 .addActionListener(new Listener())."C"}.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){ Toolkit.i<3. frame.i<3. pentru a crea un singur obiect.addItemListener (myListener). // litere afisate in casute public CheckBox1() { CheckBoxListener myListener = new CheckBoxListener(). De aceea se practicã frecvent definirea unei clase “ascultãtor” anonime acolo unde este necesarã: button.setVisible(true). cp.Florian Moraru – Programare Orientata pe Obiecte // creare buton si asociere cu ascultator class Beeper1 { public static void main ( String args[]) { JFrame frame = new JFrame().CENTER). } }). // inscriere receptor la buton frame.setMnemonic (KeyEvent. JButton buton = new JButton("Click Me"). // ascultator casute for (int i=0. Se observã cã am folosit clasa “Listener” o singurã datã.getContentPane().add(a).getSource().removeItemListener(this).

class TFListener implements ActionListener { // clasa interioara public void actionPerformed (ActionEvent ev) { String text = tf. } public static void main ( String av[]) { JFrame f = new MFrame ().getSelectedItem()).add (tf.i<str. return true. Pânã la apãsarea tastei “Enter” este posibilã si editarea (corectarea) textului introdus.beep(). Exemplu: // ascultator la ComboBox class CBListener implements ActionListener { JTextField tf. tf = new JTextField(10). String seltxt = (String) (cb. f. } } } // afisare nume casutã selectata Evenimente produse de componente text Pentru preluarea caracterelor introduse într-un câmp text trebuie tratat evenimentul ActionEvent. tf.getSource().length(). w. cu emiterea unui semnal sonor în caz de eroare : class MFrame extends JFrame { JTextField tf. // emite semnal sonor } } public MFrame () { Container w = getContentPane(). // text introdus de operator if ( ! isNumber(text)) // daca nu sunt doar cifre Toolkit.getDefaultToolkit(). f.getText()."Center"). public CBListener (JTextField tf) { this.pack(). // sursa eveniment // text selectat 142 . } // verifica daca un sir contine numai cifre static boolean isNumber (String str) { for (int i=0. Selectarea unui text dintr-o listã de optiuni JComboBox produce tot un eveniment de tip ActionEvent.Florian Moraru – Programare Orientata pe Obiecte a. } public void actionPerformed (ActionEvent ev) { JComboBox cb = (JComboBox) ev.getText()+aa[i]).setVisible(true). în locul tastei “Enter” sau ca o alternativã posibilã. generat de un obiect JTextField la apãsarea tastei "Enter".setText(a.i++) if ( ! Character. Exemplu de validare a continutului unui câmp text la terminarea introducerii.charAt(i)) ) return false.isDigit(str. } } Unele aplicatii preferã sã adauge un buton care sã declanseze actiunea de utilizare a textului introdus în câmpul text.tf=tf.addActionListener (new TFListener()).

Modelul de buton (DefaultButtonModel) este o sursã de evenimente si contine o listã de “ascultãtori” sub forma unui obiect de tipul EventListenerList. unde cuvântul “model” are sensul de model logic al datelor care vor fi prezentate vizual. public String getActionCommand(). } } class A { public static void main (String arg[]) { JFrame frm = new JFrame()."Doi". deoarece evenimentele fac parte din standardul pentru JavaBeans (componentele standard comunicã între ele si prin evenimente.setText (seltxt).add (cbox."Trei". 143 . void addActionListener(ActionListener l). Un model de buton trebuie sã respecte interfata ButtonModel. } } // afisare in campul text Mecanismul de generare a evenimentelor De cele mai multe ori. cp. frm. La o sursã de evenimente se pot conecta mai multi “ascultãtori” interesati de aceste evenimente. // primeste sursa evenimentului public Object getSource(). chiar dacã nu sunt componente “vizuale”). folosim surse de evenimente prefabricate (componente AWT sau JFC) dar uneori trebuie sã scriem clase generatoare de evenimente. // lista de optiuni JComboBox cbox = new JComboBox(s). // un sir echivalent obiectului } Mecanismul de producere a evenimentelor si de notificare a obiectelor “ascultãtor” poate fi urmãrit în cadrul claselor JFC de tip “model”. cbox. în Java.show().pack().util” astfel: public class EventObject extends Object implements Serializable { public EventObject (Object source). public void setActionCommand(String s). Un eveniment este încapsulat într-un obiect de un tip subclasã a clasei generale EventObject. redatã mai jos într-o formã mult simplificatã (prin eliminarea a cca 70 % dintre metode): public interface ButtonModel { boolean isPressed().Florian Moraru – Programare Orientata pe Obiecte tf. care este în esentã un vector de referinte la obiecte ce implementeazã interfata EventListener."Patru". public void setPressed(boolean b). // ascultator la ComboBox frm. // produce sursa evenimentului public String toString(). O clasã model contine date si poate genera evenimente la modificarea sau selectarea unor date continute."South").getContentPane(). Inregistrarea unui obiect ascultãtor la obiectul sursã de evenimente se face prin apelarea metodei “addActionListener”. folosind alte clase si interfete JDK. care era un vector de referinte la obiecte de tipul Object (in JDK 1. O sursã de evenimente trebuie sã continã o listã de receptori ai evenimentelor generate.addActionListener ( new CBListener (txt))."Center"). cp. definite în pachetul “java.add (txt.3 s-a introdus clasa EventListenerList pentru a manipula mai sigur aceastã listã). String s[]={"Unu". Acest lucru este necesar atunci când trebuie creatã o componentã standard Java (“bean”). dupã cum este posibil ca un acelasi obiect sã poatã “asculta” evenimente produse de mai multe surse. JTextField txt = new JTextField(10)."Cinci"}. // ptr afisare rezultat selectie Container cp = frm.

// declansare eveniment “buton actionat” if(!isPressed() && isArmed()) { fireActionPerformed( new ActionEvent (this . getActionCommand()) ). } fireStateChanged().class.getListenerList(). considerate semnificative pentru aceastã discutie: public class DefaultButtonModel implements ButtonModel. } Sirul numit “ActionCommand” reprezintã inscriptia afisatã pe buton si permite identificarea fiecãrui buton prin program (este diferit de numele variabilei JButton ). } // adaugare receptor la evenimente generate de buton public void addActionListener(ActionListener l) { listenerList. l).class) ((ActionListener)listeners[i+1]). i-=2) if (listeners[i]==ActionListener.length-2. } if (b) stateMask |= PRESSED.remove(ActionListener. // lista de ascultatori la evenimente generate de aceasta clasa protected EventListenerList listenerList = new EventListenerList(). } // notificare receptori despre producere eveniment protected void fireActionPerformed (ActionEvent e) { Object[] listeners = listenerList. protected transient ChangeEvent changeEvent = null.class. else stateMask &= ~PRESSED.actionPerformed(e).add(ActionListener. } } } } 144 .class) { if (changeEvent == null) changeEvent = new ChangeEvent(this). ((ChangeListener)listeners[i+1]).ACTION_PERFORMED.Florian Moraru – Programare Orientata pe Obiecte void removeActionListener(ActionListener l). i-=2) { if (listeners[i]==ChangeListener. l).length-2. Serializable { protected int stateMask = 0. Secventa urmãtoare contine o selectie de fragmente din clasa model de buton. public final static int PRESSED = 1 << 2. ActionEvent. for (int i = listeners.stateChanged(changeEvent).getListenerList(). i>=0. for (int i = listeners. i>=0. } // eliminare receptor la evenimente buton public void removeActionListener(ActionListener l) { listenerList. } // notificare receptori despre schimbarea stãrii protected void fireStateChanged() { Object[ ] listeners = listenerList. // actionare buton prin program public void setPressed(boolean b) { if((isPressed() == b) || !isEnabled()) { return. protected String actionCommand = null.

dar în listã se memoreazã si tipul (clasa) obiectului ascultãtor. // ascultator la buton "+" class B1L implements ActionListener { JTextField text.Florian Moraru – Programare Orientata pe Obiecte Apelarea metodei “setPressed” pentru un buton are acelasi efect cu actiunea operatorului de clic pe imaginea butonului. Producerea unui eveniment ActionEvent se traduce în apelarea metodei “actionPerformed” pentru toate obiectele de tip ActionListener înregistrate. clasa pentru liste de obiecte ascultãtor aratã astfel: public class EventListenerList { protected transient Object[ ] listenerList = null.arraycopy(listenerList.parseInt(text. dar câte un nou obiect ActionEvent pentru fiecare clic.length. Obiectele ascultãtor la butoanele ‘+’ si ‘-‘ trebuie sã actioneze asupra unui câmp text si deci trebuie sã primeascã o referintã la acest câmp text (în constructor). 0. tmp[i+1] = l. Intr-o formã mult simplificatã. adicã declanseazã un eveniment de tip “ActionEvent” si deci apeleazã metodele “actionPerformed” din toate obiectele ascultator înregistrate la butonul respectiv.valueOf(n+1)). In astfel de situatii avem de ales între mai multe posibilitãti de grupare a claselor din program. } } } O solutie mai simplã dar mai putin performantã pentru clasa EventListenerList poate folosi o colectie sincronizatã în locul unui vector intrinsec extins mereu. Structura programelor dirijate de evenimente Intr-un program care reactioneazã la evenimente externe trebuie definite clase ascultãtor pentru aceste evenimente. 0. Lista de ascultãtori la diferite evenimente este unicã pentru un obiect EventListenerList. // modifica continut camp text } 145 . împreunã cu adresa sa. // realocare pentru extindere vector System.setText(String. EventListener l) { if (listenerList == null) listenerList = new Object[ ] { t. Object[ ] tmp = new Object[i+2]. // valoarea din campul text text. fie direct. // adaugare la vectorul "tmp" listenerList = tmp. fie prin intermediul unei alte clase. acest exemplu aratã necesitatea interactiunii dintre componentele vizuale si obiectele “ascultãtor”. // copiere date in "tmp" tmp[i] = t.getText()). l }. primul buton (‘+’) are ca efect mãrirea cu 1 a numãrului afisat iar al doilea buton (‘-’) produce scãderea cu 1 a numãrului afisat. // referinta la campul text folosit public B1L (JTextField t) { text=t. In câmpul text se afiseazã un numãr întreg (initial zero). } public void actionPerformed (ActionEvent ev) { int n =Integer. // lista de obiecte ascultator // adauga un obiect ascultator la lista public synchronized void add (Class t. Existã un singur obiect eveniment ChangeEvent transmis ascultãtorilor la orice clic pe buton. deoarece sirul “actionCommand” poate fi modificat prin apelul metodei “setActionCommand”. else { int i = listenerList. Pentru a ilustra variantele posibile vom folosi un exemplu simplu cu douã butoane si un câmp text. Prima variantã foloseste numai clase de nivel superior (“top-level”): o clasã pentru fereastra principalã a aplicatiei si douã clase pentru tratarea evenimentelor de butoane. tmp. De multe ori clasele ascultãtor trebuie sã comunice între ele. fiecare cu avantaje si dezavantaje. i). Desi foarte simplu.

f. text. b1.pack(). O solutie uzualã este ca variabila externã sã fie transmisã la construirea unui obiect care foloseste aceastã variabilã (ca parametru pentru constructorul clasei). JTextField text. public MFrame() { Container c = getContentPane().setLayout (new FlowLayout()). Clasa Integer nu este nici ea de folos doarece nu contine metode pentru modificarea valorii întregi continute de un obiect Integer si este o clasã finalã. } // modificare valoare public String toString(){return String. Avem o situatie în care trei clase trebuie sã foloseascã în comun o variabilã si sã o modifice. } // constructor public int getValue () { return val. care nu poate fi extinsã cu noi metode. } // pentru verificare public static void main (String args[ ]) { JFrame f = new MFrame(). Dacã variabila este de un tip primitiv (int în acest caz). } // citire valoare public void setValue (int n) { val=n. iar metodele clasei nu pot modifica valoarea originalã. } } Numãrul afisat în câmpul text ar putea fi util si altor metode si deci ar putea fi memorat într-o variabilã. JTextField text = new JTextField (6). c. c. constructorul primeste o copie a valorii sale.setText(“0”).valueOf(val). JButton b2 = new JButton (" . b2. } public void actionPerformed (ActionEvent ev) { int n =Integer. c.valueOf(n-1)).addActionListener (new B2L(text) ).").parseInt(text. 146 .add(b1). f. desi este oricum memorat ca sir de caractere în câmpul text. public Int (int n) { val=n. Se poate defini o clasã “Int” ce contine o variabilã de tip int si o metodã pentru modificarea sa : class Int { int val.addActionListener (new B1L(text) ). } // pentru afisare } Clasele pentru obiecte ascultãtor la evenimente sunt aproape la fel: // receptor la butonul de incrementare class B1L implements ActionListener { Int n. // referinta la campul text folosit public B2L (JTextField t) { text=t.getText()).setVisible(true). // valoarea din campul text text.Florian Moraru – Programare Orientata pe Obiecte } // ascultator la buton "-" class B2L implements ActionListener { JTextField text. c.add(b2). // modifica continut camp text } } // Clasa aplicatiei: butoane cu efect asupra unui camp text class MFrame extends JFrame { JButton b1 = new JButton (" + ").setText(String.add (text).

text.add(b1). if (b. text. mai scurtã. text.addActionListener (this).").setText (n.Florian Moraru – Programare Orientata pe Obiecte public B1L (JTextField t. // afiseaza val initiala n } } O altã solutie. b1. c. ceea ce eliminã clasele separate cu rol de ascultãtor : class MFrame extends JFrame implements ActionListener { JButton b1 = new JButton (" + "). JTextField text = new JTextField (6). n.getActionCommand(). public BListener (JTextField t) { text=t. JButton b2 = new JButton (" . text.n=n. int n=0. b1.setText (" "+ n).setLayout (new FlowLayout()).setText(" "+ n).toString()). c.addActionListener (this). b2. public MFrame() { Container c = getContentPane(). public MFrame() { Container c = getContentPane(). JTextField text.add(b2).setText(n.addActionListener (new B1L(text.n) ). Int n= new Int(0). JTextField text = new JTextField (6).getValue(). 147 . porneste de la observatia cã metodele de tratare a evenimentelor diferã foarte putin între ele si cã putem defini o singurã clasã ascultãtor pentru ambele butoane: class BListener implements ActionListener { static int n=0.setValue(++x).n) ).addActionListener (new B2L(text. Int n) { text=t.add (text). else --n."). JButton b2 = new JButton (" . b2. } } O variantã cu numãr minim de clase este definirea clasei cu fereastra aplicatiei ca ascultãtor la evenimente. } } Clasa cu fereastra principalã a aplicatiei : // Doua butoane care comanda un camp text class MFrame extends JFrame { JButton b1 = new JButton (" + "). c. this. c.toString()). } public void actionPerformed (ActionEvent ev) { int x=n. } public void actionPerformed (ActionEvent ev) { String b = ev.indexOf('+')>=0) ++n.

} } Definirea de clase incluse anonime reduce si mai mult lungimea programelor. } public void actionPerformed (ActionEvent ev) { Object source =ev.addActionListener (new B1L()). int n= 0.Florian Moraru – Programare Orientata pe Obiecte c. text. text.setText(" "+n). } } class B2L implements ActionListener { // clasa inclusa in MFrame public void actionPerformed (ActionEvent ev) { text.setText(" "+ ++n). public MFrame() { Container c = getContentPane(). JTextField text = new JTextField (6).addActionListener (new B2L()).setText(" "+n).add(b2). c. c. } class B1L implements ActionListener { // clasa inclusa in MFrame public void actionPerformed (ActionEvent ev) { text. c. incluse în clasa cu fereastra aplicatiei: class MFrame extends JFrame { JButton b1 = new JButton (" + "). Exemplul anterior rescris cu clase incluse anonime: class MFrame extends JFrame { JButton b1 = new JButton (" + "). b1.addActionListener (new ActionListener(){ // definire clasa anonima public void actionPerformed (ActionEvent ev) { 148 .setLayout (new FlowLayout()).add(b1). b2 = new JButton (" .add (text). JTextField text = new JTextField (6). text. } Utilizarea de clase incluse Pentru reducerea numãrului de clase de nivel superior si pentru simplificarea comunicãrii între clasele ascultãtor la evenimente putem defini clasele receptor ca niste clase interioare cu nume. c. else if (source==b2) --n.setLayout (new FlowLayout()).add(b1).setText(" "+n). dar ele sunt mai greu de citit si de extins în cazul unor interactiuni mai complexe între componentele vizuale. O problemã poate fi si limitarea la constructori fãrã argumente pentru clasele anonime. public MFrame() { Container c = getContentPane(). if (source==b1) ++n. b2."). b1.getSource(). c. int n= 0.setText(" "+ --n)."). JButton b2 = new JButton (" . c.add (text).add(b2). c.

setLayout (new FlowLayout()). } // Doua butoane care comanda un camp text class MFrame extends JFrame { JButton b1. Container c = getContentPane(). dacã aceste variabile nu sunt definite ca variabile statice. } }). } } // clasa MFrame poate contine si metoda "main' Variabilele referintã la componente vizuale “b1”si “b2” sunt definite de obicei în afara functiilor (ca variabile ale clasei).getText()). functia “main” sau alte metode statice nu pot folosi direct variabilele referintã la butoane si la alte componente vizuale.add (text). JTextField text = new JTextField (6).setText(String. sa propus definirea de subclase specializate pentru fiecare componentã Swing folosite într-o aplicatie.b2. c. 149 .Florian Moraru – Programare Orientata pe Obiecte text. addActionListener(this). } } // Buton "-" class DecBut extends JButton implements ActionListener { . } public void actionPerformed (ActionEvent ev) { int x=Integer. Pe de altã parte. c.. public MFrame() { text. De aceea. Initializarea lor se poate face în afara functiilor (la încãrcarea clasei) deoarece se foloseste un singur obiect fereastrã.setText(" "+ --n). txt. dar care apare într-o altã clasã.setText(“0”). b2= new DecBut(text).setText(" "+ ++n). b2. b1= new IncBut(text).valueOf(++x)).addActionListener (new ActionListener(){ // alta clasa anonima public void actionPerformed (ActionEvent ev) { text. text. c. deoarece este probabil ca mai multe metode sã se refere la aceste variabile. Exemplu: // Buton "+" class IncBut extends JButton implements ActionListener { JTextField txt. Clase generator si clase receptor de evenimente Rolul unui buton sau unei alte componente se realizeazã prin metoda “actionPerformed” de tratare a evenimentelor generate de buton.setText(" "+n).parseInt(txt. O astfel de clasã contine si metoda “actionPerformed”. public IncBut (JTextField t) { super(" + "). c. c.add(b1).Obiectele acestor clase sunt în acelasi timp sursa evenimentelor dar si ascultãtorul care trateazã evenimentele generate. txt=t. } }). diferitã de clasa buton.add(b2)..setLayout (new FlowLayout()).

getSource(). JTextField text = new JTextField (6). Alegerea metodei “execute” de tratare efectivã a unui tip de eveniment se face prin polimorfism. } .add(b1). O variantã a acestei solutii este cea în care de defineste o singurã metodã “actionListener” ( mai exact.add (text). c. // functia “main” } Comunicarea între obiectele acestor clase specializate se realizeazã fie prin referinte transmise la construirea obiectului (sau ulterior.add (text). txt=t. } public void actionPerformed (ActionEvent ev) { Command cmd = (Command) ev.setText(String. c.execute(). public MFrame() { b1= new IncBut (text). c.. Desi 150 . printr-o metodã a clasei). Metoda dispecer de tratare evenimente poate fi inclusã în clasa derivatã din JFrame.setText("0"). txt. b1. Exemplu : // interfata comuna obiectelor vizuale care executã comenzi interface Command { public void execute(). } } // Un buton care comanda un camp text class MFrame extends JFrame implements ActionListener { JButton b1.Florian Moraru – Programare Orientata pe Obiecte c.setLayout (new FlowLayout()). care apeleazã o altã metodã din obiectele buton (sau alt fel de obiecte vizuale specializate). câte o singurã metodã pentru fiecare tip de eveniment). dar acestea nu reprezintã indicatori de calitate ai unui program cu obiecte si aratã o proiectare fãrã perspectiva extinderii aplicatiei sau reutilizãrii unor pãrti si în alte aplicatii. public IncBut (JTextField t) { super(" + "). } // Buton de incrementare class IncBut extends JButton implements Command { JTextField txt.getText()). } } Reducerea cuplajului dintre clase In examinarea unor variante pentru aplicatia anterioarã am urmãrit reducerea lungimii codului sursã si al numãrului de clase din aplicatie. c. cmd. fie prin includerea claselor respective într-o clasã anvelopã. Un dezavantaj comun solutiilor anterioare este cuplarea prea strânsã între obiectele “ascultãtor” la butoane si obiectul câmp text unde se afiseazã rezultatul modificãrii ca urmare a actionãrii unui buton: metoda “actionPerformed” apeleazã direct o metodã dintr-o altã clasã (“setText” din JTextField). txt. functie de sursa evenimentului..addActionListener(this).add(b2). c.parseInt(txt.add(b1). } public void execute () { int x=Integer. Container c = getContentPane().valueOf(++x)).

inc(). } } // Doua butoane care comanda un cimp text class MFrame extends JFrame { JButton b1."). } public void init() { txt.valueOf(x+1)). } } // Buton "-" class DecBut extends JButton implements ActionListener { Mediator med. care va transmite comenzile necesare modificãrii unor componente de afisare. Intr-o aplicatie cu multe componente vizuale care interactioneazã este mai dificil de urmãrit si de modificat comunicarea directã dintre obiecte.dec(). Fiecare dintre obiectele care comunicã prin mediator trebuie sã se înregistreze la mediator. Pentru reducerea lungimii codului sursã înregistrarea obiectului câmp text la mediator se face în constructorul clasei “MFrame”: // Buton "+" class IncBut extends JButton implements ActionListener { Mediator med.parseInt(txt.valueOf(x-1)). } public void actionPerformed (ActionEvent ev) { med. } } // clasa mediator class Mediator { private JTextField txt.getText()). In exemplul cu douã butoane si un câmp text.getText()).setText(String. } public void inc() { int x=Integer. Mediator m = new Mediator(). txt. addActionListener(this). txt. Fiecare din obiectele cu rol în aplicatie nu trebuie sã comunice decât cu obiectul mediator. public DecBut (Mediator m) { super(" . 151 . JTextField text = new JTextField (6).b2.Florian Moraru – Programare Orientata pe Obiecte programarea este mai simplã.parseInt(txt. } public void dec() { int x=Integer. obiectul mediator este informat de actionarea unuia sau altuia dintre butoane si comandã modificarea numãrului afisat în câmpul text. care va retine câte o referintã la fiecare obiect cu care comunicã. med=m.setText(String. totusi tratarea evenimentului de buton este specificã acestei aplicatii iar clasele ascultãtor nu pot fi reutilizate si în alte aplicatii. De aceea s-a propus o schemã de comunicare printr-o clasã intermediar (“mediator”). addActionListener(this). med=m. singura care cunoaste rolul jucat de celelalte clase. public IncBut (Mediator m) { super(" + "). } public void actionPerformed (ActionEvent ev) { med. public void registerTxt (JTextField t) { txt=t.setText("0").

se genereazã eveniment de tip ChangeEvent la modificarea valorii din obiectul model. Inregistrarea unui obiect ascultãtor la un obiect model se face la fel ca si la o componentã vizualã. Putem folosi o clasã model existentã sau sã ne definim alte clase model cu o comportare asemãnãtoare dar care diferã prin datele continute si prin tipul evenimentelor. Obiectul observator actioneazã asupra câmpului text. O variantã de realizare a aplicatiei oarecum asemãnãtoare foloseste schema de clase "observatobservator": butoanele nu actioneazã direct asupra câmpului text. Comunicarea prin evenimente si clase “model” O alternativã la obiectul mediator este un obiect “model”. c. m. c. dar diferã de o componentã Swing prin aceea cã evenimentele nu au o cauzã externã (o actiune a unui operator uman) ci sunt cauzate de mesaje primite de la alte obiecte din program.Florian Moraru – Programare Orientata pe Obiecte public MFrame() { b1= new IncBut (m).registerTxt (text).init(). JTextField txt. care diferã de un mediator prin aceea cã modelul genereazã evenimente în loc sã apeleze direct metode ale obiectelor comandate. Programul urmãtor foloseste clasa DefaultSingleSelectionModel.txt=txt. b2= new DecBut (m). 152 . m.add (text). Interactiunea dintre un obiect vizual si mediator poate avea loc în ambele sensuri. cu nume mai scurt: class SModel extends DefaultSingleSelectionModel { public void setElement (int x) { setSelectedIndex(x). } } // Buton "+" class IncBut extends JButton implements ActionListener { SModel mod. de exemplu un buton care comandã mediatorului o actiune dar care poate fi dezactivat de mediator.add(b1). iar mediatorul apeleazã metoda “setText” a obiectului JTextField folosit la afisare. în functie de obiectul observat (butonul) care l-a notificat de modificarea sa. this. public IncBut (SModel m. numãrul continut poate fi citit cu “getSelectedIndex” si modificat cu metoda “setSelectedIndex”. c. } } Comunicarea dintre obiecte se face aici prin apel direct de metode: obiectele buton apeleazã metodele “inc” si “dec” ale obiectului mediator. mod=m. JTextField txt) { super(" + ").setLayout (new FlowLayout()). Container c = getContentPane().add(b2). O clasã “model” seamãnã cu o componentã vizualã prin aceea cã poate genera evenimente. In exemplul nostru clasa model trebuie sã memoreze un singur numãr (un obiect) si putem alege între DefaultListModel (care contine un vector de obiecte Object) sau DefaultSingleSelectionModel (care contine un indice întreg). Pentru scurtarea codului am definit o subclasã a clasei model. c. } public int getElement () { return getSelectedIndex(). ci sunt obiecte observate.

TF text = new TF(6). mod. } } // Doua butoane care comanda un camp text class MFrame extends JFrame { JButton b1..add(b2). }.setElement(n+1). setText (String. } public void actionPerformed (ActionEvent ev) { int n = Integer.text).parseInt(txt. b2= new DecBut (m. c.trim()).addChangeListener (text). } public void stateChanged (ChangeEvent ev) { SModel m = (SModel) ev. m.Florian Moraru – Programare Orientata pe Obiecte addActionListener(this).getText(). c. // main } Pentru exemplul anterior putem sã ne definim o clasã model proprie. SModel m = new SModel().text).valueOf(x)). b1= new IncBut (m.setLayout (new FlowLayout()).add(b1). Container c = getContentPane().getElement(). care asigurã partea de generare a evenimentelor si de gestiune a listei de ascultãtori la evenimente. prin extinderea clasei AbstractListModel. c. c. 153 ..getSource(). public MFrame() { text. care sã continã o variabilã de tipul general Object si sã genereze evenimente de tip ActionEvent.setText("0"). } } // tratare evenimente generate de model class TF extends JTextField implements ChangeListener { public TF (int n) { super(n).b2. int x = m.add (text).

Componente Swing cu model Arhitectura MVC Un obiect vizual folositã într-o interfatã graficã îndeplineste mai multe functii: . Clasele 154 . eventual. In figura urmãtoare linia continuã reprezintã un apel de metodã iar linia întreruptã reprezintã un eveniment transmis între obiecte. Existã clase predefinite pentru obiecte model (DefaultComboBoxModel. iesiri (View). Clasele JFC folosesc o variantã a modelului MVC cu numai doi participanti: o clasã model si o clasã “delegat” care reuneste functiile de redare si de control pentru a usura sarcina proiectantului. un buton simplu este modelat printr-o variabilã booleanã cu douã stãri (apãsat sau ridicat). iar partea de comandã (“controller”) interpreteazã gesturile utilizatorului si actioneazã asupra modelului (defineste “comportarea” componentei).Florian Moraru – Programare Orientata pe Obiecte 13. Este posibil ca imaginea butonului sã fie modificatã direct de actiunea operatorului (de controler) si nu indirect prin intermediul modelului. deoarece comunicarea dintre controler si imagine poate fi destul de complexã. Imaginea (“view”) redã într-o formã vizualã modelul. Partea numitã “model” reprezintã datele si functionalitatea componentei. cu text sau cu imagine afisatã pe buton. cu sau fãrã contur.Permite modificarea datelor ca rãspuns la actiuni ale operatorului uman sau la cereri exprimate prin program si actualizeazã imaginea de pe ecran a acestor date.DefaultListModel. este comandat printr-un clic de mouse (si. Imaginea butonului reflectã de obicei starea sa. . iar partea de imagine poate interoga modelul. fie prin apeluri de metode. printr-o tastã) si are pe ecran imaginea unei ferestre dreptunghiulare (rotunde). în plus poate apela direct si metode ale obiectului de redare (pentru modificarea imaginii afisate). DefaultTreeModel) dupã cum se pot defini si alte clase model care sã respecte interfete impuse. date si prelucrãri (Model). Un model este o structurã de date care poate genera evenimente la modificarea datelor si contine metode pentru adãugarea si eliminarea de “ascultãtori” la evenimentele generate de model. Model View Controller De exemplu. Poate exista si o legãturã directã între partea de control si partea de redare. Obiectul model poate fi creat automat în constructor. Partea de comandã este notificatã prin evenimente de actiunile (“gesturile”) operatorului uman si modificã starea modelului prin apeluri de metode ale obiectului cu rol de “model”.Prezintã pe ecran date într-o imagine specificã (controlabilã prin program). prin crearea impresiei de buton în pozitie ridicatã sau coborâtã. Arhitectura MVC (Model-View-Controller) separã în cadrul unei componente vizuale cele trei functii esentiale ale unui program sau ale unui fragment de program: intrãri (Controller). sau poate fi creat de programator si transmis clasei care asigurã prezentarea datelor din model. pe baza unor colectii. JTable. Legãtura de la imagine la controler constã în aceea cã trebuie mai întâi pozitionat mouse-ul pe suprafata butonului si apoi apãsat butonul de la mouse.Preia actiunile transmise prin mouse sau prin tastaturã obiectului respectiv. Componentele JComboBox. prin evenimente. JMenuBar includ întotdeauna un obiect model care poate fi extras printr-o metodã “getModel” pentru a se opera asupra lui. prin apeluri de metode. JList. Modelul semnaleazã pãrtii de prezentare orice modificare în starea sa. deci defineste starea si logica componentei. în afara legãturilor dintre model si celelalte douã pãrti (imagine si comandã). Comunicarea dintre cele trei pãrti ale modelului MVC se face fie prin evenimente. . JTree.

Clasele model gata definite au un nume care începe cu “Default” (DefaultListModel. DefaultTableModel. ci se apeleazã metode care transmit la obiectul vizual un alt model (“setModel”). Ca tehnicã generalã. se afiseazã date din directoare sau din fisiere al cãror nume se introduce sau se modificã de cãtre operator dupã afisarea interfetei grafice (prin introducere în câmpuri text. ComboBoxModel. ele au de obicei înregistrate în lista de ascultãtori si obiecte ascultãtor la selectie. o matrice (model de tabel) sau un nod rãdãcinã (model de arbore). Datele prezentate într-un obiect JList. dar în listã se memoreazã atât adresa cât si tipul obiectului ascultãtor.JTable s. iar afisarea trebuie sã reflecte aceste modificãri.”trei”}. AbstractTableModel. fãrã argumente. obiect Vector sau obiect ListModel.a. de exemplu). de aceea existã metode “getModel” în toate aceste clase. pe baza cãrora se construiesc automat obiecte model. Obiectele vizuale cu model permit si selectarea unor elemente afisate (elemente din listã. dar aceste clase trebuie sã respecte anumite interfete (ListModel. DefaultTreeModel) si constructori ce primesc ca argument un vector (model de listã). Exemple: String v ={“unu”. TableModel. sub forma unei coloane. De exemplu. Utilizarea unui model de listã Componenta vizualã JList afiseazã pe ecran o listã de valori. dar are metode de adãugare si de modificare a obiectelor din vectorul folosit de obiectul model. indiferent dacã s-a transmis un model creat separat sau nu (model creat implicit). clasele model contin si metode de modificare a datelor (interfetele claselor model nu impun astfel de metode). JList list1 = new JList (v). Clasa model de listã predefinitã DefaultListModel are un singur constructor. // vector intrinsec Vector vec = new Vector ( Arrays.Florian Moraru – Programare Orientata pe Obiecte JList.”doi”. JList list2 = new JList(vec). deci modificarea datelor din model va modifica automat si afisarea pe ecran (se vor afisa noile date din obiectul model). Clasele cu model au atât constructor cu argument model. fie prin mouse. JTable. // obiect de tip Vector JList list3 = new JList (new DefaultListModel()). Existã mai multi constructori. sau se apeleazã metode de modificare a obiectului model. Lista de ascultãtori a unui obiect Swing este unicã. O altã posibilitate este sã se creeze un nou model (de exemplu dupã reordonarea unui vector sau unui tabel) si sã se retransmitã noul model la acelasi obiect vizual din interfata graficã. TreeModel). Un obiect JList cu continut variabil se poate folosi si fãrã obiect model creat explicit. o referintã la acest obiect este memoratã în obiectul JList de constructorul obiectului JList. fie prin tastele cu sãgeti. Datele afisate pot fi de orice tip clasã si sunt memorate într-un obiect colectie (Vector) sau model. si permite selectarea uneia dintre valorile afisate. cu generarea de evenimente de selectie. Utilizatorii au posibilitatea sã-si defineascã alte clase model. se pot modifica în cursul executiei programului. Exemplu de afisare într-un obiect JList a numelor fisierelor dintr-un director al cãrui nume se introduce (sau se modificã) într-un câmp text: 155 . noduri din arbore). nu se construiesc alte obiecte vizuale (JList.a. cu parametri de tipuri diferite: vector intrinsec. prin retransmiterea unui vector cu date la obiectul JList (metoda “setListData”). JTable. celule sau coloane din tabel. Modificãrile asupra obiectului model sunt redate automat pe ecran deoarece obiectul vizual este ascultãtor la evenimente generate de model.asList(v)). JTree s. In general. JTree) cu noile date dupã afisarea interfetei grafice si nici nu se apeleazã metode de reafisare (“repaint” sau altele). sunt ascultãtori la evenimente generate de clasele model respective. cât si constructori cu diverse structuri de date. Pentru facilitarea definirii de noi clase model (model de listã sau de tabel) existã clase abstracte care implementeazã partial interfetele mentionate: AbstractListModel.

list(). String files[] = d. // modelul modifica si obiectul JList ! } } Putem sã nu folosim obiect model separat atunci când lista este folositã doar pentru selectia unor elemente din listã iar continutul listei afisate nu se mai modificã.list(). Modificarea datelor din model (prin metode ca “addElement”. // pentru nume director public FileList() { Container cp = getContentPane(). Exemplu: class FileList extends JFrame implements ActionListener{ DefaultListModel model = new DefaultListModel().setListData(v). } public void actionPerformed (ActionEvent ev) { File d=new File(tf.i<files.asList(files)). } public void actionPerformed (ActionEvent ev) { File d=new File(tf. // vector cu nume de fisiere Vector v = new Vector (Arrays. String files[] = d.add (tf. dacã nu primeste unul ca argument. cp. “setElementAt”. show().clear().exists()) return. setSize(300. jlist. // creare ob.add (new JScrollPane(jlist)). model.addActionListener (this). Modelul creat implicit are numai metode de acces la date (“getElementAt”. cp. iar metoda “getModel” poate fi apelatã indiferent cum a fost creatã lista. deoarece obiectul JList este ascultãtor la evenimentele generate de model. cp. setSize(300. public FileList() { Container cp = getContentPane().600). // sterge continut anterior model for (int i=0.600).isDirectory ()) return."North"). care apeleazã metoda “setModel” din JList.addActionListener (this). File cu nume director if (! d. JTextField tf = new JTextField(12). // transmite vector la JList } } Modificarea datelor din vector (vector intrinsec sau obiect Vector) nu are efect asupra datelor afisate în obiectul JList decât dacã se apeleazã la fiecare modificare si metoda “setListData”.add (tf. cp.add (new JScrollPane(jlist)). show(). “removeElementAt”) se reflectã automat pe ecran. JList jlist = new JList(model). // pentru continut director JTextField tf = new JTextField(12). tf. 156 . if (! d."North").getText()).getText()).length.Florian Moraru – Programare Orientata pe Obiecte class FileList extends JFrame implements ActionListener{ JList jlist = new JList(). Trebuie observat cã orice constructor creeazã un obiect model simplu.i++) // adauga nume fisiere la model model.addElement(files[i]). tf. “getSize”) si nu permite modificarea datelor din acest model (date transmise printr-un vector).

"doi". // creare cimp text cp. class ListAdd extends JFrame implements ActionListener { private JList list. pentru verificarea selectiei corecte. // constructor public ListAdd() { listModel = new DefaultListModel(). private JTextField toAdd = new JTextField(10). // creare obiect lista list.add(new JScrollPane(list).Florian Moraru – Programare Orientata pe Obiecte De obicei lista este afisatã într-un panou cu derulare (JScrollPanel) pentru a permite aducerea pe ecran a elementelor unei liste oricât de mari (care nu este limitatã la numãrul de linii din panoul de afisare). // adauga buton cp. Putem obtine fie obiectul selectat (metoda “getSelectedValue”). tratat de metoda “valueChanged” (din interfata ListSelectionListener). cp. cp. Ca aspect. private DefaultListModel listModel. Exemplul urmãtor aratã cum se poate programa o listã de selectie. // campul text sub lista pack(). // lista de selectie public ListSel(Object a[]) { list = new JList(a).add(addBut).add(new JScrollPane(list)). setVisible(true). // date afisate in lista ListSel ls = new ListSel(a).addActionListener(this). addBut.”Center”).getSelectedValue(). class ListSel extends JFrame implements ListSelectionListener { JTextField field. Container cp = getContentPane().addActionListener(this).setText(sel). // afisare lista si cimp text } public void valueChanged(ListSelectionEvent e) { // lansata la selectie din lista String sel = (String) list. // valoare element selctat field. // acest obiect este si ascultator la ev. } } Programul anterior nu permite modificarea continutului listei de selectie deoarece interfata ListModel nu prevede metode de modificare a modelului. o listã JList seamãnã cu o zonã text JTextArea. // ascultator la camp text Container cp = getContentPane(). // afisare element selectat } public static void main(String s[ ]) { String[ ] a ={"unu". cu un buton pentru adãugare (“Add”) si un câmp text unde se introduce elementul ce va fi adãugat la listã."trei". // adauga camp text cp. Exemplul urmãtor aratã cum se pot adãuga elemente la sfârsitul unei liste de selectie. // adauga panou cu derulare pentru lista } 157 . // ascultator la buton toAdd. dar permite selectarea unuia sau mai multor elemente din listã.setLayout( new FlowLayout()). fie pozitia în vector (sau în model) a elementului selectat (metoda “getSelectedIndex”). // model de date pentru lista list = new JList(listModel).add(toAdd). elementul selectat este afisat într-un câmp text.”South”). afisare element selectat JList list. private JButton addBut=new JButton("Add"). field =new JTextField(10). Un obiect JList genereazã evenimente cauzate de selectarea unui element din listã si evenimente cauzate de modificarea continutului listei.addListSelectionListener(this). Selectarea unui element dintr-o listã produce un eveniment ListSelectionEvent.add(field."patru"}. cp. // ptr.

i<a. return. în cazul listei. Lantul de apeluri care produce activarea unei metode scrise de programator ca urmare a unei modificãri a colectiei de date din model este.addListSelectionListener(this). // pozitia elementului sters // constructor public LFrame (Object a[]) { for (int i=0. c. model.equals("")) { // verifica ce este in campul text Toolkit.addElement(a[i]). “intervalRemoved” si “contentsChanged”. c. 158 .add(new JScrollPane(list)). urmãtorul: DefaultListModel. Modificarea vectorului din modelul de listã genereazã evenimente pentru obiecte înregistrate ca ascultãtori la listã: metoda “addElement” din clasa DefaultListModel apeleazã metoda “fireIntervalAdded”.add(result.”North”). // model de lista JTextField result = new JTextField(4).addElement(toAdd.fireIntervalAdded → → (ListDataEvent) → ListDataListener. // daca nimic in campul text } listModel.setText (""). deci sã defineascã metodele “intervalAdded”.addElement → AbstractModel. int index. cu o metodã “valueChanged” .beep(). ListSelectionListener { DefaultListModel model = new DefaultListModel().intervalAdded Un observator la modificãri ale listei trebuie sã implementeazã interfata ListDataListener. care contine informatii despre sursa.addListDataListener( new LDL()).Florian Moraru – Programare Orientata pe Obiecte // receptor la buton si la campul text public void actionPerformed(ActionEvent e) { if (toAdd.length. but. metoda “removeElement” apeleazã metoda “fireIntervalRemoved” si “setElementAt” apeleazã metoda “fireContentsChanged”. c. Container c = getContentPane(). list. // rezultat modificare JButton but = new JButton("Delete"). list = new JList(model). toate cu argument ListDataEvent.”South”).getText().add(but.getText()). // adauga element la model lista toAdd.getDefaultToolkit(). Putem defini o clasã adaptor cu implementãri nule pentru toate cele trei metode : class ListDataAdapter implements ListDataListener { public void intervalRemoved (ListDataEvent ev) { } public void intervalAdded (ListDataEvent ev) { } public void contentsChanged (ListDataEvent ev) { } } Exemplul urmãtor aratã cum putem reactiona la stergerea unui element din listã prin afisarea dimensiunii listei dupã stergere: class LFrame extends JFrame implements ActionListener. // comanda operatia de stergere JList list.addActionListener (this). un obiect al acestei clase se va înregistra ca ascultãtor la listã. tipul evenimentului si pozitia din vector unde s-a produs modificarea.i++) model. // sterge camp text } } } Exemplul anterior poate fi extins cu un buton de stergere element selectat din listã si o clasã care implementeazã interfata ListSelectionListener.

removeElementAt(index).setText(" "+ model.int row. } // ascultator la butonul de stergere public void actionPerformed (ActionEvent ev) { model. modelul de date creat implicit este public accesibil si permite modificarea datelor din model prin metoda “setValueAt”. creat automat (implicit) sau primit de un constructor. “Englezã”.”Germanã”}. când vrem ca datele din celule sã fie prezentate prin imagini sau altfel decât prin metoda “toString” (folositã implicit la afisarea datelor). Construirea unui obiect JTable se poate face folosind un vector de titluri si o matrice de celule (de obiecte Object)..titles).”Two”. // cu vectori intrinseci // cu obiecte Vector // cu obiect model Clasa DefaultTableModel are mai multi constructori cu argumente (inclusiv cu matrice de celule si vector de titluri) si are metode pentru retransmiterea datelor la model (setDataVector) si pentru modificarea directã a valorii din oricare celulã din tabel: setValueAt(Object value. Secventa urmãtoare ilustreazã folosirea unor constructori: // tabelul primeste direct datele String [ ] titles = { “Românã”.setText(model. // nume de coloane Object[ ][ ] cells = { {“Unu”. } } Utilizarea unui model de tabel Componenta vizualã JTable afiseazã pe ecran un tabel de celule structurat în linii si coloane.”One”. setVisible(true). Fiecare coloanã poate avea un titlu. care apeleazã metodele cu acelasi nume ale obiectului model continut de obiectul tabel.titles). } // ascultator la stergere din lista class LDL extends ListDataAdapter { // clasa inclusa in LFrame public void intervalRemoved (ListDataEvent ev) { result. JTable table2 = new JTabel (model).int col).”Eins”}. Vector numecol). modificarea aspectului tabelului si obtinerea de informatii despre celulele selectate prin “mouse”. sau folosind un obiect model TableModel : JTable (Object[][] date.200). 159 . // tabelul primeste un model al datelor DefaultTableModel model = new DefaultTableModel (cells. }.”Zwei”}. Un obiect JTable include un obiect model.getSelectedIndex(). setSize(300. } // ascultator la selectie din lista public void valueChanged(ListSelectionEvent e) { index = list. {“Doi”.size()+" "). permitând selectarea si editarea de celule.Florian Moraru – Programare Orientata pe Obiecte result.. fie prin deplasarea (“tragerea”=“dragging”) marginilor coloanelor folosind un mouse.. Clasa JTable are si ea metodele “getValueAt” si “setValueAt”.size()). JTable (Vector date. JTable (TableModel model). Datele din celule pot fi de tipuri diferite. Object[] numecol). JTable table1 = new JTable (cells. Crearea unui model explicit este necesarã în cazuri mai speciale: când nu vrem ca toate celulele sã fie editabile. Dimensiunile coloanelor sunt implicit egale si calculate în functie de numãrul coloanelor. dar pot fi modificate fie prin program (metoda “setPreferredWidth”). dar numai tipuri clasã.

toLocaleString().length()).listFiles(). creat initial: class FileTable extends JFrame implements ActionListener { JTextField tf= new JTextField (14). setSize(400.i. add (tf.length. Object a[][] = new Object [fl.3)."North").setValueAt (fl[i].j<4.length][4]." Size ". // pentru nume director JTable jtable.1). // daca s-a introdus gresit TableModel model = jtable.Florian Moraru – Programare Orientata pe Obiecte In exemplele urmãtoare se afiseazã într-un obiect JTable atributele fisierelor dintr-un director al cãrui nume este introdus într-un câmp text (pe 4 coloane).i. // nume fisier model.titles).addActionListener(this).setValueAt (new Long(fl[i]. if ( ! f.lastModified()).getText()). // un obiect model public FileTable() { jtable = new JTable(model).isDirectory())."Center"). // titluri de coloane DefaultTableModel model= new DefaultTableModel() .i.setValueAt (new Boolean (fl[i].getModel().setValueAt ("". // daca s-a introdus gresit File [] fl =f." Dir "."North"). tf.addActionListener(this). } public void actionPerformed (ActionEvent ev) { File f = new File (tf. setSize(400.j). // titluri de coloane Object[][] cells= new Object[100][4] ." Date "}. // aloca memorie ptr matrice for (int i=0.i<fl. // daca e director model.setValueAt (new Date(fl[i].getName(). // atributele se obtin cu metode din File for (int i=0.isDirectory()) return. // creare tabel provizoriu (fara date) add (tf. tf.getRowCount(). } public void actionPerformed (ActionEvent ev) { File f = new File (tf.isDirectory()) return.length. // pune in model atribute fisiere din director File [] fl =f. // dimensiune fisier model.400).0).600).j++) model.i. // obtinere model creat de constructor // sterge ce era inainte in tabel for (int i=0.listFiles().2). // data } } } In varianta urmãtoare se creeazã un nou model de tabel pentru fiecare nou director si se transmite acest model la acelasi obiect JTable.i++) { // completare matrice cu atribute fisiere 160 . // obiect cu nume director if ( ! f. add(new JScrollPane(jtable)." Size ".i<model.i++) { // pentru fiecare linie i din modelul de tabel model. Prima solutie modificã datele din modelul creat implicit si extras cu “getModel”: class FileTable extends JFrame implements ActionListener { JTextField tf= new JTextField (14)." Dir ". // numarul de linii depinde de nr de fisiere ! public FileTable() { jtable = new JTable(cells." Date "}. add(new JScrollPane(jtable).i++) for (int j=0. // pentru continut director String [] titles= {"File Name".i. // pentru continut director String [] titles= {"File Name". // pentru nume director JTable jtable."Center").getText()).i<fl.

a[i][2]= new Long(fl[i].Evenimente produse de modificarea structurii tabelului In exemplul urmãtor se permite selectarea unei celule (prin “mouse” sau prin tasta Tab) si se afiseazã într-un câmp text coordonatele (linie-coloanã) celulei selectate. } public String getColumnName(int col) { return titles[col]. // titluri coloane Object [ ][ ] cells . // continut celule public MyTableModel ( Object c[][]. } // numar de linii public Object getValueAt (int row.int col) { return cells[row][col].prin metoda “getSelectedColumn” din clasa JTable. // crearea unui nou model jtable. } public int getColumnCount() { return titles. nu pe titlul coloanei). a[i][3]= new Date(fl[i].} } Pentru ambele variante crearea tabelului este identicã : JTable table = new JTable(new MyTableModel(cells. // nici o celula editabila (modificabila din exterior) } } // tabel nemodificabil prin extindere AbstractTableModel class MyTableModel extends AbstractTableModel { Object [ ] titles. 161 .cnames).getName(). } // numar de coloane public int getRowCount() { return cells. Exemplele urmãtoare aratã cum se pot defini alte clase model de tabel pentru un tabel cu celule nemodificabile de cãtre operator (dar modificabile prin program): // tabel nemodificabil prin extindere DefaultTableModel class MyTableModel extends DefaultTableModel { public MyTableModel (Object[ ][ ] cells. Un tabel poate genera mai multe tipuri de evenimente : . Pentru evenimente de selectie se foloseste modelul de selectie de la lista JList : fiecare linie din tabel este consideratã ca o listã.prin tratarea evenimentului de selectie coloanã ListSelectionEvent.lastModified()). Object[ ] cnames) { super (cells.toString(). } public boolean isCellEditable (int row.titles). JTable } } Obiectul model separat transmis la crearea unui obiect JTable poate fi: din clasa DefaultTableModel. Selectarea unei coloane din tabel se face prin clic pe mouse dupa pozitionarea pe coloana doritã (pe o celula cu date. titles=t.length()).length.Florian Moraru – Programare Orientata pe Obiecte a[i][0]= fl[i].titles)).Evenimente produse de modificarea datelor din tabel . dintr-o clasã nouã care sã implementeze interfata TableModel.int col) { return false.isDirectory()). dintr-o subclasã a clasei DefaultTableModel sau AbstractTableModel. iar pentru a considera o coloanã ca o listã trebuie folosit un model de coloanã ColumnModel. Numãrul coloanei selectate se poate afla în douã moduri: . } model = new DefaultTableModel(a.toLocaleString(). unei coloane sau unei celule. .setModel(model). .length. Object t[]) { cells=c. // transmis la ob. a[i][1]= new Boolean (fl[i].Evenimente produse la selectarea unei linii.

de a comprima un subarbore la rãdãcina sa si de a selecta orice nod din arbore prin “mouse”."+c). // model ptr selectie linii ListSelectionModel colSM .add(rc.getSource().addListSelectionListener(this).add(new JScrollPane(table)). rowSM. dar care implementeazã interfata TreeModel. de exemplu).Object b[]) { table= new JTable(a.). Un obiect JTree se poate construi în mai multe feluri: . colSM. JTable table . ca rãdãcinã a arborelui. Existã si un constructor JTree fãrã argumente. } } Utilizarea unui model de arbore Clasa JTree permite afisarea unor structuri arborescente (ierarhice). setSize(300. se poate face numai prin modificarea totalã (crearea altui obiect model) sau partialã a modelului folosit de obiectul JTree. } // actiune la selectie celula public void valueChanged(ListSelectionEvent e) { ListSelectionModel sm = (ListSelectionModel)e. cu posibilitatea de a extinde orice nod intern prin subarborele sãu. int i = sm. pentru a trata evenimente legate de selectia unor noduri este folosit un model TreeSelectionModel. cp.Pe baza unui model de arbore. Container cp= getContentPane().addListSelectionListener (this). dupã afisarea acestuia. O aplicatie poate trata evenimentele de selectare a unor noduri sau de modificare structurã arbore sau de extindere -contractie noduri corespunzãtor scopului aplicatiei.getSelectionModel(). Clasa model de arbore foloseste un obiect nod. // numar linie si coloana celula curenta ListSelectionModel rowSM . pentru un arbore de fisiere putem alege un director initial arbitrar (directorul curent.Florian Moraru – Programare Orientata pe Obiecte class TableSel extends JFrame implements ListSelectionListener { JTextField rc = new JTextField(5). fie obiect al unei clase definite de utilizator. colSM = table.getMinSelectionIndex(). Modelul de arbore poate fi creat explicit de utilizator înainte de a construi obiectul JTree sau este creat automat în interiorul clasei JTree pe baza unei alte colectii de date furnizate de utilizator (vector intrinsec. // i este numar de coloana rc. // i este numar de linie else c=i. cp. subtip al interfetei TreeModel.Pe baza unui vector de vectori (Object[] sau Vector). rowSM = table. show().b). Acest model genereazã numai evenimente de modificarea datelor.Pe baza unui obiect nod de arbore (rãdãcinã).setCellSelectionEnabled(true). // model ptr selectie coloane public TableSel(Object a[][]. . care afiseazã un arbore cu date constante fãrã legãturã cu aplicatia (colors."South").getSelectionModel(). etc. acesta poate fi un obiect din clasa DefaultMutableTreeNode sau obiecte din clase definite de utilizatori care implementeazã 162 . Modelul de arbore este fie un obiect din clasa DefaultTreeModel. obiect Vector. table. int r. subtip al interfetei TreeNode. din acest motiv este important sã stim cum se creeazã si cum se modificã modelul datelor dintr-un arbore. .300).setText(r+". Modificarea datelor dintr-un obiect JTree.c. food) dar de obicei putem evita aceastã situatie. sports. // indice selectat din lista if (sm==rowSM) r=i.getColumnModel().

class DirTree extends JFrame implements ActionListener { JTree tree. // nodul parinte al acestui nod boolean isLeaf().r). TNode r = new TNode(f. // eliminã acest nod void setParent(MutableTreeNode newParent). // enumerare noduri din subarbore public Enumeration postorderEnumeration() . tree= new JTree(new DefaultTreeModel(r)). // modifica datele din acest nod void removeFromParent(). plus o legãturã cãtre nodul pãrinte. prin metode ale clasei DefaultTreeModel. JTextField dir= new JTextField (14). Se poate modifica direct modelul. // elimina succesor dat ca nod void setUserObject(Object object). // daca acest nod este o frunza Enumeration children().isDirectory()) return.getText()). // enumerare noduri din subarbore public Enumeration breadthFirstEnumeration() . TNode r = new TNode(f. // enumerator pentru succesorii acestui nod Exemple de metode din interfata MutableTreeNode: void remove(int index). // enumerare noduri din subarbore Modificarea dinamicã a continutului unui obiect JTree se face fie prin crearea unui nou model de arbore si transmiterea lui la obiectul JTree deja afisat. fie prin crearea unui nou arbore (cu o altã rãdãcinã) si transmiterea adresei noii rãdãcini la modelul de arbore existent. // creare si transmitere model nou } // constructor public DirTree () { File f = new File(". Container cp = getContentPane(). // o noua radacina r dirtree(f. Exemple de metode din interfata TreeNode: int getChildCount(). // ascultator la campul text (modificare director) public void actionPerformed (ActionEvent ev) { File f=new File (dir. Exemple de metode în plus fatã de interfete: public Object getUserObject ()."). // modifica parintele acestui nod Clasa DefaultMutableTreeNode implementeazã interfata MutableTreeNode si foloseste câte un vector de succesori la fiecare nod (obiect de tip Vector). if (! f. // numar de succesori ai acestui nod TreeNode getChildAt(int childIndex). // succesor cu indice cunoscut int getIndex(TreeNode node). dar este mai simplu de lucrat cu metode ale clasei nod de arbore. 163 . Pentru a ilustra aceste posibilitãti de modificare dinamicã a unui obiect JTree vom relua problema afisãrii continutului oricãrui director (si a subdirectoarelor sale) într-un obiect JTree (numele directorului este preluat dintr-un câmp text). // indicele unui succesor dat TreeNode getParent().getAbsolutePath()). // obtine date din acest nod (orice obiect) public void add(MutableTreeNode child) . // nume director TNode r . // adaugare noduri la radacina tree.setModel(new DefaultTreeModel(r)).getAbsolutePath()).Florian Moraru – Programare Orientata pe Obiecte interfata TreeNode (noduri nemodificabile) sau interfata MutableTreeNode (cu operatii de modificare). dirtree( f. // adauga acestui nod un fiu public Enumeration preorderEnumeration() .r). // elimina succesor cu indice dat void remove(MutableTreeNode node).

toString()). TNode fiu=new TNode(file).add(fiu). cp.getText()).length.getUserObject(). dirtree(f.500). TNode r) { if ( ! d. // nume director DefaultTreeModel model = new DefaultTreeModel().getLastSelectedPathComponent(). Exemplu de afisare valoare nod selectat: public void valueChanged (TreeSelectionEvent ev) { try { // TNode node= (TNode) tree.Florian Moraru – Programare Orientata pe Obiecte cp. // daca nu e director (nod frunza) File [] files=d.add (dir. JTextField dir= new JTextField (14).printStackTrace(). if (! f.} } 164 .fiu).setText(node.getSelectionPath().isDirectory()) return. // adauga ca fiu la nodul r dirtree (file. La selectarea unui nod din arbore se genereazã evenimentul TreeSelectionEvent si se apeleazã metoda “valueChanged” din ascultãtorii de tip TreeSelectionListener care au fost înregistrati la obiectul JTree. dir. } Pentru ambele programe se afiseazã initial continutul directorului curent (atunci se creeazã modelul de arbore) deoarece constructorul fãrã argumente JTree afiseazã niste valori constante (care nu reprezintã nume de fisiere). // ascultator la campul text (modificare director) public void actionPerformed (ActionEvent ev) { File f=new File (dir. // afisare valoare nod selectat } catch(Exception ex) {ex."North"). TNode node =(TNode) tree.getLastPathComponent(). // continua pentru nodul “fiu” } } Urmeazã varianta cu modificarea rãdãcinii de arbore folosite de modelul existent: class DirTree extends JFrame { JTree tree. } .addActionListener( this). } // creare arbore de fisiere private void dirtree (File d.i++){ // pentru fiecare fisier din director File file=files[i]. model.isDirectory() ) return.i<files..getAbsolutePath()). TNode r = new TNode(f. // creare nod pentru acest fisier r. if (node==null) return.listFiles(). tf. // fisiere din directorul d for(int i=0.setRoot(r).r).. setSize(300.add (new JScrollPane(tree)).

Un element poate contine alte elemente. “org.xml” si “org. ca în HTML).a. 165 . în sensul cã multimea de marcaje folosite poate fi definitã în functie de tipul aplicatiei si nu este fixatã dinainte (ca în HTML). pe oricâte niveluri de includere. s. DOM. Un prim exemplu este un mic fragment dintr-un fisier XML listã de preturi: <priceList> <computer> <name> CDC </name> <price> 540 </price> </ computer > <computer> <name> SDS </name> <price> 495 </price> </ computer > </priceList> Un element este fragmentul cuprins între un marcaj de început si un marcaj de sfârsit: <price> 540 </price> Un element are un nume (acelasi cu marcajul de început) si poate avea mai multe atribute si un continut. un marcaj este un sir între paranteze unghiulare (‘<’ si ‘>’). Standardele formale sau impuse de practicã (SAX. transmiterea de mesaje între calculatoare (mesaje SOAP). proprietãtile unor entitãti (cum sunt numele si pretul unui produs) pot fi transmise fie ca atribute ale marcajului ce specificã entitatea. StAX s.) constituie si exemple de programare la nivel de interfatã.a. fisiere de configurare.w3c”. Incepând cu versiunea 4 clasele Java pentru aplicatii XML au devenit standard si s-au extins. Java si XML Fisiere XML în aplicatii Java Prelucrarea si generarea de documente XML în programe Java sunt operatii tot mai necesare datoritã diverselor utilizãri ale fisierelor XML: serializare continut obiecte si apeluri de proceduri la distantã. In afara marcajelor pereche pot exista si marcaje singulare de forma <tag/>. fisiere descriptor de componente Java (“deployment descriptor”). fie ca elemente incluse între marcajele ce definesc o entitate. Exemple: <computer type =”desktop”> . un marcaj singular (“empty element tag”) este o prescurtare pentru cazurile în care nu existã caractere între “start tag” si “end tag”.xml”. . în pachete ca “javax. Limbajul XML (“Extensible Markup Language”) este un limbaj cu marcaje (“tag” va fi tradus aici prin “marcaj”) extensibil. Un fisier XML este un fisier text care contine marcaje. . arãtând cum un API poate fi definit numai din interfete Java. fãrã nici o referire la o clasã concretã. Marcajele au rolul de a descrie datele încadrate de marcaje. deci semnificatia sau modul de interpretare a acestor date (si nu modul de prezentare. astfel <timestmp/> este o prescurtare pentru <timestmp> </timestmp> Marcajele de început sau singulare pot fi însotite de atribute. fisiere “build” folosite de programul “ant” pentru construire si instalare de aplicatii.Florian Moraru – Programare Orientata pe Obiecte 14. continutul poate include un text sau alte elemente sau elemente si text. Mediile integrate Java (IDE) includ editoare de XML si permit verificãri asupra sintaxei XML. </ computer > <computer name=”SDS” price=”495” /> Asa cum se vede din ultimul exemplu. fiecare atribut are un nume si o valoare.

asa cum un obiect este o instantiere a unei clase. atributele si tipurile de date descrise în schemã. Un document XML este o instantiere a unei scheme. Pentru a permite utilizarea de marcaje cu acelasi nume în scheme diferite s-a introdus si în XML conceptul “spatiu de nume”: un spatiu de nume contine marcaje distincte si este declarat la începutul unui fisier schemã. care poate permite si crearea de noi documente XML prin program. Clasele Java sunt generate pe baza unei scheme XML (de obicei un fisier DTD). Un parser XML este un analizor de documente XML. Exemplu de fisier DTD pentru lista de preturi: <!ELEMENT priceList (computer)+> <!ELEMENT computer (name. dar locul unde este definit ‘m’ nu este precizat. atunci numele de marcaje trebuie prefixate de un simbol asociat spatiului de nume. Se folosesc douã tipuri de fisiere schemã: fisiere DTD (Document Type Definition) si fisiere XSD (XML Schema Definition). Validarea mãreste timpul de analizã si este inutilã atunci când fisierele XML sunt generate de cãtre programe si nu sunt editate manual. verificã utilizarea corectã a marcajelor si (optional) face o validare fatã de fisierul DTD sau XSD specificat.</a>) Validarea XML necesitã specificarea unui fisier schemã (DTD sau XSD) la începutul documentului XML si înseamnã certificarea faptului cã au fost folosite numai marcajele din schemã. la fel cum incluse perechile de paranteze dintro expresie sau perechile de acolade din Java.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas. cu relatiile. iar trecerea între fisiere XML si clase Java (în ambele sensuri) este asiguratã de clasele din interfata API numitã JAXB (Java XML Binding).Florian Moraru – Programare Orientata pe Obiecte Multimea marcajelor folosite într-un document XML si relatiile dintre ele sunt definite într-un fisier “schemã” XML. primul are precizatã adresa Web (URI) unde este definit. Dacã într-un acelasi fisier XML se folosesc marcaje din mai multe spatii de nume. utilizat si la validarea fisierelor XML care folosesc aceste marcaje.. Fisierul XSD este un fisier XML care foloseste marcaje predefinite si oferã mai multe posibilitãti decât fisierul DTD în descrierea multimii de marcaje. Un fisier XML bine format (“well formed”) are toate perechile de marcaje incluse corect.org/soap/encoding/"> <SOAP-ENV:Body> <m:GetLastTradePrice xmlns:m="Some-URI"> <symbol>MOT</symbol> </m:GetLastTradePrice> </SOAP-ENV:Body> </SOAP-ENV:Envelope> In exemplul anterior se folosesc douã prefixe pentru spatii de nume: SOAP-ENV si ‘m’. Exemplu de mesaj (cerere la un serviciu Web) în protocolul SOAP : <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas. Se pot defini clase corespunzãtoare elementelor XML.xmlsoap. 166 . price) > <!ELEMENT name (#PCDATA) > <!ELEMENT price (#PCDATA) > PCDATA (“Parsed Character Data”) este un alt nume pentru siruri de caractere care trebuie luate în considerare de parser (spre deosebire de tipul CDATA). Un parser XML primeste ca intrare un fisier XML. Exemple de erori de utilizare a marcajelor semnalate de un parser: <a> … <b> … </a> … </b> <a> … <b> … </b> … </c> (corect este <a>…<b>…</b>.xmlsoap.

fie printr-un arbore în memorie. Toate spatiile albe consecutive dintre douã marcaje formeazã un singur atom XML. materializat într-un API Java. Un parser bazat pe modelul DOM are ca principal dezavantaj memoria necesarã pentru arborele corespunzãtor unui document XML mai mare.Parsere în flux continuu (“streaming parsers”). este XSLT (XML Stylesheet Language for Transformation).Parsere orientate document. fisierul listã de preturi este corect si sub forma urmãtoare: <!DOCTYPE priceList SYSTEM "priceList. s. Un parser XML respectã o interfatã cu aplicatiile (API). care creeazã în memorie o structurã de date ierarhicã (un arbore) cu structura si continutul fisierului XML pe care o transmite aplicatiei. Un alt standard.a.). de modificare în arbore si de creare a unui alt document XML pe baza arborelui. . adicã o serie de interfete Java care trebuie implementate de programul parser si sunt folosite de aplicatii pentru acces la datele furnizate de parser. De obicei spatiile albe sunt introduse (la editarea manualã) numai pentru aspect si nu sunt semnificative. dar modelul StAX permite aceste operatii. Aceste parsere sunt si ele de douã tipuri: cu apelare de metode “callback” din aplicatie de cãtre parser (“push parsers”) sau cu extragere de informatii despre fisierul XML prin apelarea de cãtre aplicatie a unor metode din parser (“pull parsers”). de acelasi tip cu alte siruri de caractere dintre marcaje succesive. dar actiunile declansate de aceste “evenimente” sunt specifice fiecãrei aplicatii si nu fac parte din programul parser. 167 . atribut. de sfârsit si goale). HTML). Avantajul unui parser care creeazã un obiect arbore este acela cã permite operatii de cãutare. sir dintre marcaje etc. comentarii XML. Un parser SAX sau StAX este mai rapid. iar metoda “transform” face transformãrile necesare de la sursã la rezultat. De aceea s-au propus si alte variante de modele arbore : JDOM si DOM4J (‘J’ de la Java). Problema principalã în proiectarea unui parser XML este aceea cã el trebuie sã informeze aplicatia despre marcajele. atributele si celelalte elemente de infoset întâlnite. sub forma unor interfete Java. instructiuni de prelucrare. Elementele unui infoset sunt atomi XML: marcaje (de început. de aceea ele sunt ignorate de aplicatii în majoritatea cazurilor. caractere între marcaje (date si spatii albe). Este util atunci când o aplicatie este interesatã numai de anumite sectiuni dintr-un document XML si nu de întreg continutul. fie ca o secventã de evenimente XML. dar nu permite operatii repetate de cãutare în document întrun mod eficient. care informeazã aplicatia imediat ce se întâlneste un element semnificativ. In principiu existã douã categorii mari de programe parser. De exemplu. dupã modul cum prezintã rezultatul analizei: .Florian Moraru – Programare Orientata pe Obiecte Datele dintr-un fisier XML si relatiile dintre ele constituie un “infoset”. Un obiect convertor (“transformer”) primeste la creare un obiect sursã si un obiect rezultat. care poate fi reprezentat fie printr-un fisier text (XML). Abrevierile SAX (“Simple API for XML”) si StAX (“Streaming API for XML”) se referã la standardizarea comunicãrii dintre aplicatii si parser (prin interfete în Java). Evenimentele SAX sunt produse de întâlnirea unui atom XML (marcaj. mai ales cã în acest arbore apar noduri si pentru spatiile albe folosite la indentare si la fiecare linie nouã. care permite specificarea unor transformãri asupra unui document XML în vederea conversiei sale într-un alt format (de ex. In cazul unui parser “pull” (de exemplu StAX) aplicatia detine controlul si cere programului parser informatii despre urmãtorul element de limbaj din documentul analizat. atribute folosite în marcaje. Un parser “push” (de exemplu SAX) detine controlul procesului de analizã XML si oferã aplicatiei informatii pe mãsurã ce parcurge documentul XML. Modelul SAX nu permite crearea sau modificarea de documente XML prin program. consumã mai putinã memorie si este mai usor de folosit (nu necesitã navigarea printr-un arbore). Standardul DOM (“Document Object Model”) defineste interfata API pentru accesul aplicatiilor la arborele creat de parser.DTD"> <priceList> <computer><name>CDC</name><price>540</price></computer> <computer><name>SDS</name><price>495</price> </ computer ></priceList> O altã criticã adusã modelului DOM este aceea cã interfata API nu este total în spiritul orientãrii pe obiecte si nu foloseste clasele colectie si iterator din Java.

Florian Moraru – Programare Orientata pe Obiecte Interfetele “Source” si “Result” sunt implementate de clase “DOMSource” si “DOMResult”, “SAXSource” si “SaxResult” sau “StreamSource”, “StreamResult” (fluxuri de intrare-iesire ca sursã si rezultat al transformãrii). In felul acesta se poate trece între diferite reprezentãri posibile ale documentelor XML, cu sau fãrã modificãri în continutul documentelor.

Utilizarea unui parser SAX
SAX (Simple API for XML) a fost primul API pentru XML si nu este un standard oficial, dar este utilizat si în prezent. De fapt, multe programe parser DOM au fost scrise pe baza unor parsere SAX . Un parser SAX citeste un fisier XML, detecteazã eventuale erori formale si aruncã exceptii, dar în principal apeleazã anumite metode cu nume si prototip ce depind de tipul elementului de infoset recunoscut în fisier. Metodele din aplicatie apelate de un parser SAX sunt grupate în câteva interfete (ContentHandler, DTDHandler, ErrorHandler), definite în pachetul org.xml.sax, dintre care numai ContentHandler este importantã atunci când nu se cere si validarea fatã de un DTD. Principalele metode “callback” din aceastã interfatã sunt:
// apelata la inceput de element (marcaj de inceput) public void startElement (String uri, String localName,String qName, Attributes atts) throws SAXException; // apelata la sfarsit de element (marcaj de terminare) public void endElement (String uri, String localName, String qName) throws SAXException; // apelata la detectarea unui sir de caractere public void characters (char ch[ ], int start, int length) throws SAXException;

Parametrul “uri” indicã locatia unde este definit spatiul de nume pentru acest tag, iar “qName” (“qualified name”) este numele complet, prefixat de identificatorul spatiului de nume; dacã nu este specificat un spatiu de nume (uri este un sir vid), atunci “qName” este numele de tag si nu “localName”(care este tot un sir vid). Interfata Attributes corespunde unei liste si are metode ca: getLength, getQName(int), getValue(int). Exemplu de extragere si afisare atribute:
public void startElement(String uri, String aName, String qName, Attributes atts) throws SAXException { for (int i = 0; i < atts.getLength(); i++) System.out.println (atts.getQName(i)+”=”+ atts.getValue(i)); ...

Utilizarea unui parser SAX implicã definirea unei clase care implementeazã cel putin interfata ContentHandler sau care extinde clasa DefaultHandler (clasã adaptor care implementeazã toate metodele interfetelor prin functii cu efect nul). Metodele activate la întâlnirea de marcaje primesc numele marcajului sub douã forme: numele local (în cadrul spatiului de nume implicit) si numele “qName”, calificat cu identificatorul spatiului de nume. Primul argument (uri) aratã unde este definit spatiul de nume al marcajului. In cazul unui singur spatiu de nume se foloseste numai argumentul “qName”, care contine numele marcajului . Obiectul parser SAX este creat prin apelul unei metode fabricã fie în constructorul clasei “handler”, fie în functia “main” sau într-o altã functie. Pentru cã existã mai multe programe parser SAX pentru Java si pentru a putea utiliza orice parser cu minim de modificãri, se practicã obtinerea unui obiect parser prin apelul unei metode fabricã si nu prin instantierea directã a unei singure clase.

168

Florian Moraru – Programare Orientata pe Obiecte Metoda “parse” a clasei “SAXParser” trebuie sã primeascã obiectul ce contine documentul XML (un flux de I/E) si obiectul “handler”, cu metode de tratare a evenimentelor. Exemplul urmãtor afiseazã “frumos” un document XML, cu indentare, deci un fel de “pretty printing” sau “beutifier” pentru fisiere XML:
import java.io.*; import org.xml.sax.*; import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.*; // Pretty printing cu parser SAX class PPSax extends DefaultHandler { String sp=""; // spatii ptr indentare la fiecare linie public static void main(String argv[])throws Exception { DefaultHandler handler = new PPSax(); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser saxParser = factory.newSAXParser(); saxParser.parse( new File(argv[0]), handler); } // metode din DefaultHandler redefinite (metode callback) // un marcaj de inceput element public void startElement(String namespaceURI,String sName,String qName, Attributes attrs) throws SAXException { StringBuffer sb = new StringBuffer(sp+ "<"+ qName+" "); if (attrs != null) { for (int i = 0; i < attrs.getLength(); i++) { String aName = attrs.getLocalName(i); // Attr name if ("".equals(aName)) aName = attrs.getQName(i); sb.append (" " + aName + "="+ "\"" + attrs.getValue(i)+"\" "); } } System.out.println (sb.append (">")); sp+=" "; // creste indentare dupa un marcaj de inceput } // un marcaj de sfarsit element public void endElement(String namespaceURI, String sName, String qName ) throws SAXException { sp=sp.substring(2); // scade indentarea System.out.println (sp+"</"+qName+">"); } // un sir de caractere delimitat de marcaje public void characters(char buf[], int offset, int len)throws SAXException { String s = new String(buf, offset, len); s=s.trim(); // elimina spatii albe if (s.length()==0) return; // daca au fost doar spatii albe System.out.println (sp+ s); } }

Utilizarea unui parser DOM O aplicatie DOM creeazã mai întâi un obiect parser (printr-o fabricã de obiecte) si apoi apeleazã metoda “parse” pentru acest obiect, cu specificarea unui fisier XML. Metoda “parse” are ca rezultat un obiect de tip Document, care este arborele creat pe baza documentului XML, conform modelului DOM.

169

Florian Moraru – Programare Orientata pe Obiecte Pentru fisierul XML anterior cu lista de preturi, dar scris tot pe o singurã linie si fãrã spatii albe, arborele DOM aratã astfel:
priceList computer name price computer name price

CDC

540

SDS

495

De fapt, orice arbore DOM mai are un nivel rãdãcinã (nefigurat aici) care corespunde întregului document XML (nod cu numele “#document” si valoarea null). Fiecare nod dintr-un arbore DOM are un nume, o valoare si un tip. Tipurile sunt numere întregi, dar existã si nume mnemonice pentru aceste tipuri. Exemple de constante din interfata Node:
public static final short ELEMENT_NODE public static final short ATTRIBUTE_NODE public static final short TEXT_NODE public static final short DOCUMENT_NODE = 1; = 2; = 3; = 9;

Nodurile cu text au toate acelasi nume (“#text”), iar valoarea este sirul de caractere ce reprezintã textul. In exemplul anterior cele 4 noduri terminale sunt noduri text (cu valorile “CDC”,”540”,”SDS”,”495”). Parserul DOM creeazã noduri text si pentru grupuri de spatii albe (blancuri sau terminator de linie înainte sau dupã un marcaj). Pentru fisierul XML cu 10 linii si indentare numãrul de noduri din arborele DOM este 20 : 1 pe nivelul 1, 5 pe nivelul 2, 10 pe nivelul 3 si 4 pe nivelul 4. Dintre acestea sunt 9 noduri text cu spatii albe (3 pe nivelul 2 si 6 pe nivelul 3). Nodurile pentru elemente au numele marcajului si valoarea null. In arborele de mai sus apar numele nodurilor de pe primele 3 niveluri (de tip 1) si valorile nodurilor de pe ultimul nivel (de tip 3). Modelul DOM defineste mai multe interfete Java, implementate de parser si folosite de cãtre aplicatii : - Interfata Node contine metode de acces la un nod de arbore DOM si la succesorii sãi, dar si metode pentru crearea si modificarea de noduri de arbore DOM. Exemple:
public String getNodeName(); // numele acestui nod public String getNodeValue() throws DOMException; // valoarea acestui nod public void setNodeValue(String nodeValue) throws DOMException; public short getNodeType(); // tipul acestui nod public NodeList getChildNodes(); // lista succesorilor acestui nod public Node getFirstChild(); // primul succesor al acestui nod public Node getNextSibling(); // urmatorul succesor al acestui nod public Node removeChild(Node oldChild) throws DOMException; public Node appendChild(Node newChild) throws DOMException; public NamedNodeMap getAttributes(); // atributele acestui nod

- Interfata NodeList contine metode pentru acces la lista de succesori ai unui nod:
public Node item(int index); public int getLength(); // nodul cu numarul “index” // lungime lista de noduri

- Interfetele Document, Element, Attr, CharacterData, Text, Comment, extind direct sau indirect interfata Node cu metode specifice acestor tipuri de noduri.

170

Florian Moraru – Programare Orientata pe Obiecte Interfata NamedNodeMap corespunde unei liste de atribute (obiecte de tip Attr) si contine metode ca getLength() si item(i). Exemplu de extragere atribute:
Element elm=(Element) node; // Node node; NamedNodeMap attrs = elm.getAttributes(); for (int i=0;i <attrs.getLength(); i++) Attr a = (Attr) attrs.item(i);

Interfata Attr contine metode de acces la numele si valoarea unui atribut, precum si la elementul cãruia apartine acel atribut:
public interface Attr extends Node { public String getName(); public String getValue(); public void setValue(String value) throws DOMException; public Element getOwnerElement(); }

Din interfata Element vom mentiona câteva metode:
public String getAttribute(String name); // obtinerea valorii unui atribut cu nume dat public void setAttribute(String name,String value) throws DOMException; public NodeList getElementsByTagName(String name);

Ultima metodã este utilã pentru extragerea selectivã de noduri dintr-un arbore DOM, dar existã în standardul DOM si alte mecanisme de filtrare a nodurilor din arbore. Clasele care implementeazã interfetele DOM fac parte din parserul DOM, iar numele si implementarea lor nu sunt cunoscute utilizatorilor; ele constituie un bun exemplu de separare între interfatã si implementare si de programare la nivel de interfatã. Clasa care implementeazã interfata Node contine si o metodã “toString”, care produce un sir de forma nume[valoare], cu numele si valoarea nodului, indiferent de tipul nodului si dacã existã sau nu atribute asociate unui element. Afisarea/prelucrarea unui arbore DOM se face de obicei printr-o functie recursivã care prelucreazã nodul curent (primit ca argument), atributele sale si apoi se apeleazã pe ea însãsi pentru fiecare succesor. Parcurgerea listei de succesori se poate face în douã moduri: - Folosind metodele “getFirstChild” si “getNextSibling” ; - Folosind metoda “getChildNodes” si metode ale interfetei NodeList. Exemplu de afisare nume si valoare noduri DOM folosind prima variantã:
import javax.xml.parsers.*; import org.w3c.dom.*; import java.io.File; // afisare nume si valori noduri din arbore DOM (cu metoda Node.toString ) class DomEcho { public static void printNode(Node node) { System.out.println (node.toString() ); //afisare nod curent (fara atribute) Node child = node.getFirstChild(); if (child !=null) { printNode (child); // afisare subarbore al primului fiu while ( (child=child.getNextSibling())!=null) printNode(child); // afisare subarbori pentru ceilalti fii } } // varianta de obtinere noduri fii

171

indent+” “).getNodeType(). k++) // enumerare fii printNode (kids.Florian Moraru – Programare Orientata pe Obiecte public static void printNode(Node node) { System. rezultatul unui parser DOM. Ca exemplu de creare a unui arbore DOM prin program (folosind metode ca “createElement”. Exemplul urmãtor afiseazã frumos. // sir cu nume si/sau valoare nod int type = node. // afisare nod curent (fara atribute) NodeList kids = node. k<kids.getNodeValue(). i<attrs.item(k)). String indent) { System.getDocumentElement()). arbore care poate fi creat prin program (nu pe baza unui fisier XML) si apoi folosit pentru crearea unui fisier XML. DocumentBuilder builder = factory. if (type==1) { // nod Element s+= node.println ( indent + node. Document doc = builder.getAttributes(). dar numai pentru noduri cu elemente si noduri text: Functia anterioarã poate fi modificatã pentru serializarea unui arbore DOM.getChildNodes(). “createAttribute”.getLength().out. } In general.item(i)+" ". createTextNode”).getLength(). // lista de fii if (kids != null) // daca exista fii ai nodului curent for (int k=0. // foloseste “toString” din clasa Attribute } else if (type==3) // daca nod text s = node. static String toString (Node node){ String s="".getLength(). cu indentare.trim().getChildNodes(). } } La functiile anterioare se poate adãuga un parametru suplimentar ce reprezintã nivelul sau indentarea necesarã la fiecare nivel: public static void printNode(Node node. // lista de atribute for (int i=0. // nume element (marcaj) NamedNodeMap attrs = node.toString()). operatiile efectuate la fiecare nod depind de tipul nodului iar functia recursivã va contine o selectie dupã tipul nodului (un bloc switch).item(k). // valoare nod cu eliminare de spatii albe return s. k<kids.getNodeName()+" ".out.toString” predefinitã poate fi înlocuitã cu o metodã staticã cu argument “Node” care sã tinã seama de tipul nodului si sã adauge si atributele la sirul ce reprezintã continutul unui nod DOM (pentru noduri text nu se afiseazã numele. cu indentare NodeList kids = node. k++) // enumerare fii printNode (kids. printNode(doc. i++) s+= attrs.toString()). // apel recursiv pentru fiecare fiu } Metoda “Node. // apel recursiv pentru fiecare fiu } // creare si afisare arbore DOM public static void main (String arg[]) throws Exception { DocumentBuilderFactory factory = DocumentBuilderFactory.println ( node. // afisare nod curent. iar pentru nodurile element nu se afiseazã valoarea).newDocumentBuilder().parse( new File(arg[0]) ). Pentru serializare trebuie adãugate marcaje de sfârsit si paranteze unghiulare la marcaje. // lista de fii if (kids != null) // daca exista fii ai nodului curent for (int k=0. urmeazã fragmente dintr-un program care creeazã un obiect DOM folosind un parser SAX: 172 .newInstance().

trim().parse( new File(xmlFile). crt = element. i++) { Attr a = dom. dom = builder. } public void endElement(String nsURI. Fragment din interfata pentru analiza unui document XML: public interface XMLStreamReader { public int next() throws XMLStreamException. iar o aplicatie trebuie sã aleagã între cele douã.newDocument().appendChild(element). String qName) throws SAXException { crt = (Node)crt.. this).length() > 0) crt. numite API cursor si API iterator..newDocumentBuilder(). saxParser.createTextNode(s.newInstance(). // rezultat=tip eveniment public boolean hasNext() throws XMLStreamException.getValue(i)). i < atts. } Metoda “next” are ca rezultat tipul atomului XML din dreptul cursorului si avansul cursorului pe urmãtorul atom. … 173 . . public String getLocalName(). Attributes atts) throws SAXException { Element element = dom.createElement(qName). element.createAttribute (atts.appendChild (dom. } // metode callback apelate de parserul SAX public void startElement(String uri. SAXParser saxParser = saxfact. Exemplu: XMLStreamReader xmlr. } crt. DocumentBuilder builder = domfact.setValue(atts. Acest tip (un întreg) poate fi comparat cu una din constantele simbolice definite în interfata XMLEvent sau se pot folosi metode din interfata XMLStreamReader pentru determinarea tipului de atom XML.getLength(). int start. start.newSAXParser().Florian Moraru – Programare Orientata pe Obiecte class SAXDOM extends DefaultHandler { private Node crt. public String getNamespaceURI(). length).trim())). String lName. public SAXDOM (String xmlFile) throws Exception { DocumentBuilderFactory domfact = DocumentBuilderFactory. String qName. a. for (int i = 0. // nod curent din arbore DOM private Document dom.getParentNode(). // radacina arbore SAXParserFactory saxfact = SAXParserFactory.setAttributeNode (a).newInstance(). Modelul cursor are douã interfete Java principale: XMLStreamReader si XMLStreamWriter. String lName. public String getText(). int length) throws SAXException { String s = new String(ch. crt=dom.getQName(i)). } public void characters(char[] ch. if (s. } Utilizarea unui parser StAX StAX reuneste douã interfete API.

Interfata XMLEvent contine constante si metode pentru analiza evenimentelor XML. } Modelul iterator lucreazã cu obiecte eveniment.isStartElement()|| xr.CHARACTERS == eventType) // sau ==xmlr.io. import javax.out.events.getLocalName().*.isWhiteSpace()) return (xr.next().createXMLStreamReader(new FileReader(args[0])). if (xr.xml. .*. if (s. // Afisare in ecou cu StAX cursor class PPCursor { public static void main(String[] args) throws Exception { XMLInputFactory xif = XMLInputFactory.xml..stream.newInstance(). obtinute prin metoda “nextEvent”.next().getText()). fãrã indentare.out. a unui fisier XML care contine numai elemente cu atribute si caractere: import java. si care contin toate informatiile despre urmãtorul element de limbaj XML.isCharacters() && !xr.print(xmlr.hasNext()){ int eventType = xmlr. return "". Interfetele pentru analiza si respectiv sinteza unui document XML din modelul iterator sunt XMLEventReader si XMLEventWriter: public interface XMLEventReader extends Iterator { public boolean hasNext(). import javax.*. … // cu determinare tip eveniment if (xmlr.print(xmlr. public void writeEndElement() throws XMLStreamException.out. } } public static String toString (XMLStreamReader xr){ if (xr.println (s)..Florian Moraru – Programare Orientata pe Obiecte while(xmlr. XMLStreamReader xr = xif. public void writeCharacters(String text) throws XMLStreamException.stream. Exemplu de afisare simplã.getText()).getEventType()) System. public XMLEvent nextEvent() throws XMLStreamException.length()>0) System.hasNext()){ int et = xr.hasText()) // daca este text in pozitia curenta System. // cu metode ale clasei parser if (xmlr. // tip eveniment String s = toString(xr). } } Pentru crearea de fisiere XML se foloseste interfata XMLStreamWriter: public interface XMLStreamWriter { public void writeStartElement(String localName) throws XMLStreamException. while(xr. 174 .getText()).isEndElement()) return xr.

toString()).nextEvent().*.nextEvent()..xml.O metodã staticã cu argument XMLEvent. public void add(XMLEvent e) throws XMLStreamException. // spatii ptr indentare static String toString (XMLEvent ev) { // sir asociat unui eveniment (inclusiv text cu spatii albe) switch (ev.println(txt).length()>0 && !e.isEndDocument()) System.* . public void close() throws XMLStreamException. XMLEventReader r = factory.START_ELEMENT: 175 .out.*. XMLEventReader r = factory.newInstance().. definitã de noi. while(r.. String txt= toString(e).createXMLEventReader( new FileReader(args[0])).isEndDocument()) System.hasNext()) { XMLEvent e = r. import javax. Exemplu pentru prima solutie: class PPEvent { // pretty printing cu parser StAX in modul iterator public static void main(String[] args) throws Exception { XMLInputFactory factory = XMLInputFactory. .getEventType()){ case XMLEvent.println(e.Florian Moraru – Programare Orientata pe Obiecte public Object next(). .hasNext()) { XMLEvent e = r. dar nu cunoastem numele clasei care implementeazã interfata XMLEvent. } Exemplu de afisare în ecou a continutului unui fisier XML folosind clase si metode din modelul StAX iterator. } } static String sp="".events. while(r.O altã clasã care contine o variabilã XMLEvent si o metodã toString (nestaticã). import javax. .out.stream.createXMLEventReader(new FileReader (args[0])). Avem câteva posibilitãti de a schimba sirul ce corespunde unui eveniment StAX: . Putem redefini metoda “toString” a obiectului eveniment XML pentru a adãuga indentarea.xml. class EventEcho { public static void main(String[] args) throws Exception { XMLInputFactory factory = XMLInputFactory.newInstance().trim(). } public interface XMLEventWriter { public void flush() throws XMLStreamException.stream.. if (txt. } } } Pentru afisare cu indentare trebuie determinat tipul evenimentului. if ( !e. public void add(Attribute attribute) \ throws XMLStreamException. inclusiv metoda “toString” a clasei care implementeazã interfata “XMLEvent”: import java.io.

getNodeValue(). if ( nt==Node.getChildNodes(). return sp+ev. Vizualizarea arborelui XML Afisarea într-un obiect JTree a rezultatului unui parser XML pune câteva probleme interesante de programare cu obiecte si admite mai multe solutii: . case XMLEvent.Clasa adaptor implementeazã interfata TreeNode si primeste un obiect org. for (int i=0. return s. return new NodeAdapter (node).dom.Florian Moraru – Programare Orientata pe Obiecte String s= sp+ ev.toString().getAttributes(). Avem douã variante: .w3c.CHARACTERS: default: return sp+ ev.toString(). Crearea de obiecte XMLEvent pentru fiecare atom XML (inclusiv spatii albe) este un dezavantaj al modelului iterator fatã de modelul cursor (ca memorie si ca timp). Pentru economie de spatiu vom folosi a doua variantã si vom redefini numai metodele folosite la vizualizarea arborelui (alte metode care nu sunt folosite la vizualizare sunt folosite în aplicatii care prelucreazã arborele afisat si trebuie de asemenea redefinite) : class NodeAdapter extends DefaultMutableTreeNode { Node domNode.getChildNodes(). } public int getChildCount() { return domNode. dar sub alte forme.TEXT_NODE) s=domNode. String s="". } } Variabila “sp” trebuie sã fie staticã pentru a-si pãstra valoarea între apeluri ale functiei “toString”.item(i).Folosirea unei clase adaptor de la un tip de nod la un alt tip de nod. deoarece metodele necesare pentru pentru operatii cu noduri sunt prezente în ambele tipuri de noduri.getLength(). if ( nt==Node. } public String toString() { // sir cu continut nod. i<attrs.END_ELEMENT: sp=sp. sp+=" ". NamedNodeMap attrs = domNode. public NodeAdapter(Node node) { domNode = node. . care depinde de tipul nodului int nt = domNode.getLength().getNodeName(). case XMLEvent.substring(2).toString().getNodeType().Clasa adaptor extinde clasa DefaultMutableTreeNode si primeste un obiect de tip Node.ELEMENT_NODE){ s=domNode.Crearea unui obiect JTree cu datele din arborele DOM . i++) { 176 .Node. Clasa adaptor primeste apeluri de metode din interfata TreeeNode si le transformã în apeluri de metode din interfata Node (DOM). } public TreeNode getChildAt(int i) { Node node = domNode.Stabilirea unei corespondente nod la nod între arborii DOM si JTree .

JFrame frame = new JFrame(argv[0]).parse( new File(argv[0]) ).child(i).Florian Moraru – Programare Orientata pe Obiecte Node attr = attrs. // sau cu model de arbore frame.480). public DomNode(Node node) { domNode = node. for (int i=0.setSize(500. } if (domNode. 177 .domNode) return i. i<count. frame.domNode == n.startsWith("#")) { s += nodeName.getNodeName(). JTree tree = new JTree(new NodeAdapter(doc)). } } Utilizarea clasei adaptor se va face astfel: public static void main(String argv[ ]) throws Exception { DocumentBuilderFactory factory = DocumentBuilderFactory.getNodeValue()+'\"'. } return s. In principiu se defineste o clasã care implementeazã metodele interfetei TreeModel prin apeluri de metode din interfata Node (DOM).getNodeName()+"="+'\"'+ attr. s += t. DocumentBuilder builder = factory. } } return s. model care sã foloseascã ca noduri (inclusiv nodul rãdãcinã) chiar noduri DOM (rãdãcina poate fi de orice subtip al clasei Object). if (child.add ( new JScrollPane(tree) ). } O altã solutie de afisare a unui arbore DOM într-un obiect JTree se bazeazã pe definirea unei clase model de arbore. i++) { DomNode n = this. String nodeName = domNode.newDocumentBuilder(). utile în clasa model de arbore: class DomNode { Node domNode.trim(). frame.setVisible(true). } return -1. if ( ! nodeName.newInstance().item(i). Putem sã definim o clasã pentru o variantã de nod DOM cu metoda “toString” redefinitã si cu câteva metode noi. dar din nou apare problema redefinirii metodei “toString” dintr-o clasã cu nume necunoscut (clasa pentru nod DOM) pentru a tine seama de tipul nodului (ca si în cazul clasei pentru XMLEvent).getNodeValue() != null) { // elimina spatii albe din noduri text String t = domNode. s+=attr.getContentPane().getNodeValue(). } public String toString() { String s= "". } // Metode noi pentru indici si numar fii public int index( DomNode child) { int count = childCount(). Document doc = builder.

int index) { DomNode node = (DomNode) parent. } public Object getChild(Object parent. } public void valueForPathChanged(TreePath path.addElement( listener ). public void addTreeModelListener(TreeModelListener listener) { listenerList.childCount() == 0.500). setSize(400. } } Clasa model de arbore cu noduri DOM aratã astfel: class DomTreeModel implements TreeModel { Node root.getLength(). 178 .getChildNodes().getChildNodes(). // radacina arbore DOM public DomTreeModel (Node root) { this. return node. } } Urmeazã clasa responsabilã cu afisarea arborelui: class domView extends JFrame { public domView(Node root) { // primeste radacina arbore DOM JTree tree = new JTree(new DomTreeModel(root)).Florian Moraru – Programare Orientata pe Obiecte } public DomNode child(int searchIndex) { Node node = domNode. return node.removeElement( listener ). } // Metode din interfata TreeModel public Object getRoot() { return new DomNode(root).index((DomNode) child). } public int childCount() { return domNode. add( new JScrollPane(tree)).childCount(). return node.child(index). } public void removeTreeModelListener(TreeModelListener listener) { listenerList. return node. return new DomNode(node). } public boolean isLeaf(Object aNode) { DomNode node = (DomNode) aNode.root=root. Object newValue) { // Nu se permit modificari in arborele afisat } private Vector listenerList = new Vector(). } public int getIndexOfChild(Object parent. Object child) { DomNode node = (DomNode) parent. } public int getChildCount(Object parent) { DomNode node = (DomNode) parent.item(searchIndex).

Florian Moraru – Programare Orientata pe Obiecte
setVisible(true); } public static void main(String argv[]) throws Exception { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse( new File(argv[0]) ); new domView(document); // radacina arbore DOM } }

Solutiile anterioare folosesc eficient memoria (nu dubleazã arborele DOM într-un arbore JTree), dar nu pot elimina nodurile text cu spatii albe, pentru cã se preiau toate nodurile DOM în arborele afisat cu JTree (nu se pot exclude noduri DOM decât cu anumite artificii). O solutie mai simplã construieste un arbore JTree având în noduri referinte la noduri din arborele DOM; se pot elimina noduri text care contin numai spatii albe, dar în memorie se vor afla doi arbori:
class domJTree1 { public static void main (String argv[]) throws Exception { JFrame frame = new JFrame(argv[0]); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse( new File(argv[0]) ); TNode root = new TNode (doc.getDocumentElement()); // creare nod radacina add (root,new TNode(doc)); // functie recursiva de creare arbore JTree t= new JTree (root); frame.add(new JScrollPane(t)); frame.setSize(400,400); frame.setVisible(true); } // adaugare nod fiu la nod parinte din arbore DOM in arbore JTree private static void add ( TNode child, TNode parent) { Node domnode = (Node) (child.getUserObject()); // nodul DOM din nodul JFC if (domnode.getNodeName().charAt(0)=='#' && // nu se creeaza noduri text “albe” ((String)domnode.getNodeValue()).trim().equals("")) return; parent.add(child); NodeList nodes = domnode.getChildNodes(); // fiii nodului curent if (nodes != null) for (int i=0; i<nodes.getLength(); i++) add ( new TNode (nodes.item(i)), child); // creare nod JFC din nod DOM } }

O aplicatie cu fisiere XML Programul Ant (Apache) realizeazã o secventã de actiuni descrisã într-un fisier XML, cu numele implicit “build.xml” sau cu un alt nume dat ca argument în comanda “ant” (unde mai pot apare si o serie de optiuni). O actiune (“target”) constã din mai multe operatii, are un nume si o actiune anterioarã, de care depinde. O operatie (“task”) constã de obicei în executarea unui program sau unei comenzi de sistem. Existã mai multe operatii cu nume predefinit dar si operatii generice, cum ar fi executia oricãrei comenzi de sistem (nume de target “exec”). Exemple de operatii predefinite : exec, mkdir, copy, delete, javac, java, etc. Urmeazã un exemplu simplu de fisier “build.xml” cu 3 actiuni: stergerea unor fisiere (“clean”), crearea unui nou director (“mkdir”), si copierea unui fisier în noul director (“copy”):

179

Florian Moraru – Programare Orientata pe Obiecte
<project name="MyProject" default="copy" > <target name="clean" description="delete files " > <echo message="del dist"/> <delete dir="dist" /> </target> <target name="mkdir" depends="clean"> <mkdir dir="dist"/> </target> <target name="copy" depends="mkdir" description="copy files " > <copy file="build.xml" tofile="dist/build.xml" /> </target> </project>

Un element “target” descrie o actiune, iar fiecare element continut de “target” reprezintã un task (o operatie). Ordinea de executie va fi: “clean”, “mkdir”, “copy” deoarece actiunea implicitã din acest proiect este “copy”, iar “copy” depinde de “mkdir”, care depinde de “clean”. De mentionat cã actiunile pot fi descrise în orice ordine, chiar dacã actiunea de care depinde actiunea curentã nu a fost deja descrisã. Exemplu de fisier “build.xml”:
<project name="MyProject" default="cinci" > <target name="trei" depends="doi" > <echo message= "3" /> </target> <target name="cinci" depends="patru" > <echo message= "5" /> </target> <target name="unu" > <echo message= "1" /> </target> <target name="doi" depends="unu" > <echo message= "2" /> </target> <target name="patru" depends="trei" > <echo message= "4" /> </target> </project>

Rezultatul executiei fisierului anterior de cãtre “ant” va fi:
Buildfile: build.xml unu: [echo] 1 doi: [echo] 2 trei: [echo] 3 patru: [echo] 4 cinci: [echo] 5

Vom schita un mic program “ant”, mult simplificat în raport cu programul real, dar care redã esenta acestuia. Simplificarea rezidã în numele de task-uri si de atribute ce pot apare în fisierul “build.xml”, dar se pãstreazã functionalitatea programului “ant”. Si în aceastã aplicatie ierarhia de elemente XML este importantã: astfel un task poate avea orice nume si este executat numai dacã apare în cadrul unui target. Anumite elemente task (“exec”, de exemplu) pot contine alte elemente (argumente pentru executia comenzii). Vom prefera de aceea un parser DOM care retine si relatile de subordonare dintre elemente, spre deosebire de un parser secvential (SAX). Programul are trei pãrti distincte, dar diferite ca dimensiune: crearea unei liste de actiuni (targets), în ordinea aparitiei lor în proiect, stabilirea ordinii de executie pentru aceste actiuni, executia fiecãrui task dintr-o actiune, functie de tipul operatiei. Toate elementele de tip target vor fi introduse într-o listã (un vector) pe mãsurã ce sunt întâlnite în fisierul “build.xml”. Pentru stabilirea ordinii de executie vom începe cu targetul implicit (atributul “default” din elementul “project”) si vom parcurge lantul de dependente, punând elementele într-o

180

Florian Moraru – Programare Orientata pe Obiecte stivã (lantul de executie este parcurs în ordine inversã folosing atributul “depends” al fiecãrui target). Executia actiunilor din proiect se va face în ordinea extragerii din stivã. Programul începe prin obtinerea obiectului parser si executia acestuia pentru crearea obiectului de tip Document (arborele DOM):
class AntDOM { public static void main(String argv[]) throws Exception { String xmlfile = "build.xml"; // nume implicit de fisier XML DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(new File(xmlfile)); // creare arbore DOM new DOMHandler().handleProject(doc); // utilizare arbore DOM } }

Metoda “handleProject” creeazã lista de actiuni si retine actiunea de start:
class DOMHandler { private String start=null; // start tag private List<Element> tlist = new ArrayList<Element>();

// target list

public void handleProject(Document doc) { Node pnode = doc.getDocumentElement(); // nodul <project> Element project = (Element) pnode; // ptr. acces la atribute start=project.getAttribute("default"); // prima actiune executata // prelucrare noduri "target" NodeList targets = pnode.getChildNodes(); // fiii nodului “project” for (int i=0; i< targets.getLength(); i++) { // ptr fiecare nod “target” Node knode=targets.item(i); if ( knode.getNodeType()==Node.ELEMENT_NODE) // ignora noduri text tlist.add((Element)knode); // adauga target la lista } execprj(); // executie actiuni din proiect }

Metoda “execprj” stabileste ordinea de executie pentru actiuni folosind o stivã si lanseazã secventa de operatii din fiecare actiune:
private void execprj () { // parcurgere lista in ordinea dependentelor Stack<Element> stack = new Stack<Element>(); String tn=start; // target name Element t, tt =null; // un target while ( ! tn.equals("")) { // repeta pana la un target fara “depends” // cauta in tlist un element cu numele tn for (Iterator it=tlist.iterator(); it.hasNext();) { t=(Element)it.next(); if ( t.getAttribute("name").equals(tn) ) tt=t; // tt este targetul cu numele tn } stack.push(tt); // pune target pe stiva tn= tt.getAttribute("depends"); // urmatorul target in lantul dependentelor } // executie targets in ordinea din stiva while ( ! stack.empty()) { t= stack.pop(); // un target

181

Florian Moraru – Programare Orientata pe Obiecte
System.out.println (t.getAttribute("name") + ":"); // scrie nume target NodeList tasks =t.getChildNodes(); // lista de taskuri for (int i=0; i<tasks.getLength();i++) { Node knode=tasks.item(i); if ( knode.getNodeType()==Node.ELEMENT_NODE) // elimina spatii albe exectask ( (Element) knode); // executie task } } }

Metoda “exectask” depinde de numele si de atributele elementului task si de aceea ne vom limita la câteva operatii simple:
private void exectask (Element task) { // executie task Runtime rt=Runtime.getRuntime(); // ptr lansare din Java de fisiere executabile String tname=task.getTagName(); // nume task System.out.print ("\t["+ tname +"] "); // scrie nume task if (tname.equals("echo")) // operatia “echo” System.out.print(task.getAttribute("message")); // are doar atributul “message” if (tname.equals("mkdir")) { // operatia “mkdir” String value=task.getAttribute("dir"); // are atributul “dir” cu nume director try { rt.exec ("cmd /c md "+ value);} // lansare comanda catch(Exception e) { e.printStackTrace();} } System.out.println(); // dupa orice task }

182

orice modificare în procedurile de lucru cu suportul extern sau în modelul XML ar necesita modificarea acestor clase care ar reuni mai multe responsabilitãti. astfel cã faza care precede scrierea de cod devine tot mai importantã. Conceptele devin interfete. Proiectare orientatã pe obiecte Analiza si proiectarea orientate pe obiecte Aplicatiile tind sã devinã tot mai complexe si mai distribuite. In plus. O clasã trebuie sã aibã o singurã responsabilitate si deci sã existe un singur motiv pentru modificarea ei. Un program si modulele sale componente trebuie proiectate de la început având în vedere posibilitatea de a fi extins si adaptat ulterior într-un mod cât mai simplu si mai sigur (proiectare în perspectiva schimbãrii = “design for change”). dar nu este scalabil si adaptabil la alte cerinte apãrute dupã realizarea prototipului aplicatiei. Un sistem software bine proiectat este usor de înteles. Analiza orientatã obiect modeleazã aplicatia printr-un sistem de obiecte care colaboreazã între ele. JDOM. Distribuirea de responsabilitãti între clase (interfete) se poate face în mai multe feluri (dupã diferite criterii). Conceptele stabile pot deveni servicii reutilizabile. obiecte si relatii dintre ele. Din acest motiv clasele Java nu contin metode pentru scrierea datelor clasei în fisiere sau pentru conversia datelor în XML. (AXI)OM. 183 . diagrame de obiecte. la solutii diferite pentru modelul obiect al unui infoset XML: DOM.a. Rezultatele proiectãrii se exprimã de obicei în diverse diagrame UML (diagrame statice de clase.Florian Moraru – Programare Orientata pe Obiecte 15. Clasele si obiectele necesare într-o aplicatie pot rezulta din: . . . Mai exact. s. cu relatii între aceste concepte (obiecte conceptuale). de exemplu. plus proiectarea la nivel abstract a structurii software. vizualizarea relatiilor dintre obiecte si clase. Programarea orientatã obiect este o paradigmã de descompunere a codului folosind clase si obiecte în loc de proceduri (functii). care iau decizii sau tin seama de contextul aplicatiei. chiar dacã anumite clase care implementeazã aceste interfete se vor modifica în timp. precum si a secventei de evenimente importante din sistem (UML= Unified Modelling Language). fiecare având responsabilitãti distincte. diagrame de evenimente. Un proiect ce contine numai clasele rezultate din analiza aplicatiei poate fi mai compact. DOM4J.Analiza aplicatiei concrete si modelarea obiectelor si actiunilor din aplicatie.Bibliotecile standard Java. In plus. In acest scop relatiile de dependentã dintre clase trebuie formulate explicit prin interfete bine definite si stabile. care nu se vor modifica odatã cu modificarea cerintelor. S-au propus si se folosesc în faza de proiectare diverse instrumente software care sã faciliteze identificarea si specificarea claselor (de exemplu. de instrumentele software folosite si de considerente de arhitecturã a sistemului.a. usor de mentinut si usor de refolosit (pãrti din el în alte aplicatii). CRC=Class. Proiectarea orientatã obiect completeazã modelul conceptual furnizat de analizã cu restrictii de implementare impuse de limbajul folosit .). ceea ce a condus. format din concepte specifice domeniului modelat. Rezultatul analizei este un model conceptual. s. trebuie avutã în vedere si reutilizarea unor clase din aplicatie în alte aplicatii înrudite. este vorba de concepte si de relatii stabile. iar conceptele instabile pot deveni clase algoritmice. In general se poate afirma cã mãrirea flexibilitãtii în extinderea si adaptarea unei aplicatii se poate obtine prin mãrirea numãrului de clase si obiecte din aplicatie.Responsability-Collaboration-Card).Folosirea unor solutii de proiectare deja folosite cu succes în alte aplicatii. Aceastã fazã cuprinde analiza cerintelor si a cazurilor de utilizare (“use cases”). este importantã o separare a pãrtilor susceptibile de schimbare de pãrtile care nu se mai modificã si evitarea cuplajelor "strânse" dintre clase. s.a. XOM. clase.

Un alt principiu este acela cã module de pe un nivel superior nu trebuie sã depindã de module de pe un nivel inferior. “find”. pentru diversi utilizatori ai clasei. functie numitã uneori “aplicator” si care primeste ca argument fisierul curent. dacã o clasã contine o referintã la o altã clasã. s. arhivarea fisierelor gãsite într-un singur fisier comprimat. cãutarea fisierelor care satisfac anumite conditii (exprimate eventual prin expresii regulate). Un cod sursã rezultat dintr-o bunã proiectare este simplu. care diferã însã prin operatiile aplicate asupra fisierelor gãsite. Altfel spus. imobilitatea (dificultatea de a extrage din program pãrti care sã poatã fi utilizate si în alte aplicatii).a.Codificarea trebuie precedatã de o etapã de analizã si de proiectare. opacitatea (intentiile nu sunt reflectate bine în cod). cu multe metode. indexarea grupului de fisiere în vederea unei cãutãri rapide (folosind un produs cum este biblioteca de clase Lucene. repetarea inutilã a unor secvente de cod si complexitatea inutilã (facilitãti nefolosite în prezent dar care vizeazã dezvoltãri ulterioare). astfel de situatii pot apare în cazul unor clase mari. necesare în aplicatii cum ar fi: afisarea selectivã a unora dintre aceste fisiere. nici o clasã nu trebuie sã depindã de o clasã concretã volatilã. “pkzip” s.) Toate aceste programe contin o functie recursivã de parcurgere a unui sistem ierarhic de fisiere. disponibilã pe site-ul Apache). usor de înteles.Cunoasterea solutiilor folosite în alte probleme. . ci ambele vor depinde de abstractii. dacã definim o clasã derivatã atunci vom deriva dintr-o clasã abstractã. Un alt principiu spune cã utilizatorul unui tip clasã trebuie sã poatã folosi o subclasã derivatã. Proiectarea unei aplicatii Java de traversare directoare Exemplul ales urmãreste sã ilustreze câteva probleme tipice care apar la scrierea unei aplicatii (relativ simple) cu obiecte: . . de îmbunãtãtire treptatã a solutiilor. “grep”. destinate diferitelor categorii de utilizatori ai clasei. Printre atributele unui cod sursã prost proiectat sunt: rigiditatea (încercarea de a modifica ceva antreneazã multe alte modificãri si în alte pãrti). Proiectarea este de obicei un proces iterativ. este posibilã de multe ori în abordarea unor probleme mai complexe. fragilitatea (modificarea unei pãrti de cod face sã nu mai lucreze corect alte pãrti de cod). optiunea “Search” din aplicatia “MyComputer” din Windows XP. atunci aceasta va fi o clasã abstractã sau o interfatã.Florian Moraru – Programare Orientata pe Obiecte Principiul “open-close” sustine cã o clasã trebuie sã fie deschisã pentru extindere dar închisã pentru modificãri. modificarea contextului în care se foloseste o clasã nu trebuie sã conducã si la modificarea clasei. pentru cã întotdeauna existã mai multe solutii diferite pentru o problemã. atunci se va arunca o exceptie de operatie nepermisã în subclasã. Au fost scrise si publicate o serie de programe care au în comun prelucrarea fisierelor dintr-un director si din subdirectoarele sale. Un programator cu experientã poate “simti” erori sau inabilitãti de proiectare (“design smells”) privind codul sursã. Beneficiarii unei clase nu trebuie sã depindã de metode pe care nu le folosesc si care s-ar putea modifica. Aceste programe sunt fie comenzi sistem sau utilitare independente (“dir” sau “ls”.). Parcurgerea este programatã explicit si pentru fiecare fisier este apelatã o functie diferitã de la aplicatie la aplicatie (functie “callback” încorporatã într-un obiect functional). compact. Solutia este fie împãrtirea în clase mai mici sau definirea de interfete pentru o parte din metodele clasei. deci a unor “scheme de proiectare” confirmate de practicã este utilã de multe ori.a. chiar dacã nu vedem decât rezultatul final al acestui proces. fie pãrti din aplicatii cu interfatã graficã (“WinZip”. Dacã o anumitã metodã din clasa de bazã nu are sens pentru o subclasã. pornind de la o variantã initialã simplã la care se adaugã treptat alte functii. De obicei sunt verificate dacã îndeplinesc conditiile si prelucrate numai fisierele normale. usor de testat si usor de întretinut. Mai precis. nu si fisierele subdirector (toate directoarele trec de filtre). fãrã sã stie nimic în plus despre acea subclasã si fãrã a folosi operatorul instanceof pentru a diferentia între diferite subtipuri.Dezvoltarea iterativã. 184 .

// cautare cu filtru Numele directorului initial putea fi transmis ca argument metodei “find” si nu constructorului clasei Finder. i++) { File file = files[i]. i < files. la care metoda “next” ar furniza urmãtorul fisier într-o parcurgere prefixatã a arborelui de fisiere. for (int i = 0. Programarea acestui iterator este mai dificilã decât scrierea functiei recursive care foloseste metoda “list”.length()). atunci nu se intrã în subdirectoare doarece toate fisierele director au lungimea zero.out. iterator folosit de metoda “list” din clasa Java File.length. } } } // exemple de utilizare new Finder("C:/Sun"). Este demn de remarcat cã la nivelul limbajului C (la nivelul sistemului de operare) se foloseste un astfel de iterator cu douã functii “findFirst” si “findNext”. Exemplu de clasã cu o functie de afisare a fisierele dintr-un director dat (si din subdirectoare) care satisfac o conditie implementatã ca un filtru: public class Finder { private FileFilter filter. // cautare fara filtru intr-un director new Finder(".null). public SizeFilter (long size) { this. Un astfel de iterator existã în clasa DefaultMutableTreeNode sub forma metodei “depthFirstEnumeration” dar pentru un arbore cu legãturi explicite între noduri. else files= dir. deci cade în sarcina aplicatiei. // director initial // constructori public Finder (String dirname. } public Finder (String d) { this(d. if ( filter == null) files= dir..listFiles(). this.length()>size.listFiles(filter).size=size.find().println(file + " "+ file. if (file.isDirectory() || f. new SizeFilter(1000)). } } 185 . Dacã se aplicã un filtru de forma “se acceptã numai fisierele cu dimensiune peste o valoare datã” asupra tuturor fisierelor.} // singura metoda publica a clasei public void find () { findrec(dir). Mai importantã însã este observatia cã evitarea aplicãrii filtrului asupra directoarelor se va face în clasa filtru si nu în metoda “find”.isDirectory()) findrec (file). else System.} public boolean accept (File f) { return f.find(). } // functie recursiva de parcurgere directoare private void findrec (File dir) { File[] files.filter = filter.". Exemplu de filtru: class SizeFilter implements FileFilter { // filtrare dupa lungime fisiere long size. // filtru aplicat fisierelor de metoda listFiles private File dir.Florian Moraru – Programare Orientata pe Obiecte In principiu ar fi posibilã si utilizarea unui iterator pe arborele de fisiere. FileFilter filter) { dir = new File (dirname).

println("intra in: " +file). Vom adãuga acum functiei recursive “findrec” evitarea filtrãrii directoarelor si semnalarea intrãrii si iesirii din subdirectoare. if ( filter. etc.a). în care obiectul observat este cel care viziteazã sistemul de fisiere (cu metoda recursivã). // iesire din director } } // prelucrare fisier gasit public void foundFile (File file) { System. dictionar completat fie pe baza argumentelor din linia de comandã. obtinerea unor optiuni de cãutare. Solutii posibile pentru clasa Finder Putem observa o asemãnare între fisierele gãsite de acest utilitar si elementele extrase dintr-un fisier XML de cãtre un parser XML (numãr posibil mare de elemente si structurã ierarhicã) si deci ne putem inspira din modul în care diverse programe parser XML transmit aplicatiilor informatiile 186 . s.out. de exemplu. private void findrec (File f) { File[] files. } public void endDir (File file) { System. indexare.out.accept(file)) // daca fisier acceptat de filtru foundFile(file).println(file).listFiles(). evenimentele care trebuie notificate observatorului sunt trei: intrarea într-un nou director (schimbarea directorului curent). startDir(f).out. Optiunile (conditiile) de cãutare se pot transmite sub forma unui dictionar cu nume de optiuni si valori ale acestora.Florian Moraru – Programare Orientata pe Obiecte Anumite aplicatii pot fi interesate de momentele în care se intrã (si se iese) dintr-un subdirector.isDirectory()) { // daca este un (sub)director files=f. // intrare intr-un director for (int i = 0.length. adicã la fiecare nou fisier gãsit. care pot fi folosite atât în modul text (linie de comandã) cât si cu interfatã graficã.). } Ultimele trei metode nu trebuie sã facã parte din clasa Finder. un program de afisare cu indentare va modifica indentarea la aceste momente. File file. } // operatii la schimbare director public void startDir (File file) { System. ele trebuie sã fie metode callback. Separarea algoritmului de traversare (parcurgere) de operatia aplicatã asupra fisierelor gãsite permite reutilizarea acestui algoritm într-o diversitate de aplicatii. Problema seamãnã cu o schemã “observat-observator”. In plus. iar obiectul observator este specific aplicatiei si este notificat la fiecare schimbare de stare în obiectul observat. fie pe baza datelor introduse de operator în componente vizuale (câmpuri text sau alte obiecte grafice).println("iese din: "+file). if (f. este izolatã partea din aplicatie care nu se mai modificã (procesul de cãutare în arborele de subdirectoare) de partea susceptibilã de modificare (modul de folosire a rezultatelor cãutãrii. i < files. } endDir(f). gãsirea unui nou fisier (normal) si iesirea din directorul curent. arhivare. De aceea. Pentru evitarea unor cãutãri de duratã se poate specifica o adâncime maximã de subdirectoare în care se cautã sau posibilitea de întrerupere fortatã a cãutãrii de cãtre utilizator. i++) { file = files[i]. parte din aplicatia care prelucreazã fisierele gãsite (prin afisare. // fisier gasit findrec (file).

. // si creste indentarea 187 . intrarea si iesirea dintr-un director. . // si apoi numele fisierului } protected void startDir(File dir) { // la intrarea intr-un director indent += step.getName()).int step) { // d este directorul initial super(d). this. dupã modelul SAX. de aceea semnaleazã aplicatiei evenimentele importante si transmite acesteia numele fisierelor sau subdirectoarelor gãsite. Asemãnarea constã în aceea cã functia de traversare a sistemului de fisiere (corespunde unei functii “parse”) stie sã gãseascã fisiere dar nu stie ce sã facã cu fisierele gãsite. } // metode callback redefinite protected void foundFile(File file) { for (int i=0. care include si metodele ce apeleazã metodele "callback".i<indent. iar un utilizator trebuie sã-si defineascã o clasã derivatã.step= step. // indentare curenta si pas indentare public FileLister (String d. inclusiv nume de directoare. în care sã implementeze si metodele "callback". In cazul nostru metoda “find" si cele câteva mici metode auxiliare pot fi incluse fãrã costuri într-o aplicatie ce implicã cãutarea de fisiere.Metodele "callback" fac parte din aceeasi clasã (abstractã) cu metoda "find" (ca metode abstracte sau cu efect nul). deoarece nu toate trebuie redefinite în clasa derivatã (aplicatia care foloseste metoda find este o clasã derivatã): abstract class Finder { … // la fel ca in clasa Finder anterioara // metode apelate de “find” protected void startDir(File dir) {} protected void foundFile(File file) {} protected void endDir(File dir) {} } Ca exemplu de utilizare vom realiza o afisare indentatã a numelor de fisiere.Crearea unui arbore cu fisierele gãsite sau unei liste de cãi la fisiere.out. cu metode având efect nul si nu cu metode abstracte.Apelarea de metode “callback” din aplicatie. // scrie “indent” spatii System. Vom prefera o clasã neinstantiabilã. sau generarea de evenimente specifice la gãsirea fiecãrui fisier.out.step=0. iar o referintã la aceastã clasã este transmisã metodei "find" (sau la construirea unui obiect ce contine metoda "find"). Si cu aceasta alegere avem (cel putin) douã variante: . Putem distinge douã categorii mari de solutii: . deci realizarea mai multor operatii diferite la producerea unui eveniment (cum ar fi arhivarea fisierelor gãsite si afisarea lor într-un obiect JTree). Generarea de evenimente si utilizarea de clase ascultãtor are ca avantaj posibilitatea existentei mai multor ascultãtori. dupã modelul evenimentelor generate de componentele grafice Swing (cu ascultãtori la evenimente). fãrã filtrare: class FileLister extends Finder { private int indent=0. Pentru a reduce dimensiunea programului nostru si a-l face mai usor de înteles vom folosi modelul SAX.i++) System. care trebuie implementatã de o clasã a aplicatiei. cu metode "callback" apelate de "find" si definite de aplicatie.Metodele "callback" grupate într-o interfatã. nu se foloseste pentru un parser XML (SAX) deoarece un program parser este compus din multe clase si metode si nu este eficient sã fie inclus în orice aplicatie care-l foloseste. dupã modelul DOM sau dupã modelul XPath. Solutia clasei abstracte.Florian Moraru – Programare Orientata pe Obiecte despre atomii XML gãsiti într-un fisier. Aceste solutii au avantajul cã evitã un consum mare de memorie în cazul unui numãr foarte mare de fisiere gãsite.print(" ").println(file.

} h. fie din câmpuri text sau alte componente vizuale de interfatã graficã.isDirectory()) { // daca este un (sub)director files=f. // pas de indentare de 3 spatii lister. In exemplu se creeazã dictionarul de optiuni în functia “main”. de acelasi tip. // scade indentarea } public static void main (String args[]) { FileLister lister = new FileLister(args[0]. void endDir (File dir) .a.startDir(f). 188 . } // cautare recursiva private void findrec (File f) { File[] files. pentru a fi folosit de cãtre metoda “listFiles”. dupã data ultimei modificãri a fisierului si dupã dimensiunea fisierului. s. void foundFile (File file) . // apel metoda din interfata findrec (file).foundFile(file). if (f. if ( filter == null || filter. findrec(dir).listFiles(). i++) { file = files[i]. // apel metoda din interfata for (int i = 0. } } In varianta cu interfatã. } Clasa Finder se modificã pentru a primi un obiect care implementeazã interfata FileHandler: private FileHandler h. fisiere care contin un anumit sir.endDir(f). dar într-o aplicatie realã optiunile sunt extrase fie din linia de comandã.3). fie al constructorului clasei.h=h. Read-Only. // apel metoda din interfata } } Extinderea clasei Finder Vom extinde acum clasa Finder cu anumite optiuni de cãutare transmise printr-un dictionar fie ca argument al metodei "find". // variabila a clasei Finder public void find (FileHandler h) { this.find(). Pentru ultimele douã criterii se dã o limitã. i < files.Florian Moraru – Programare Orientata pe Obiecte } protected void endDir(File dir) { // la iesirea dintr-un director indent -= step. h. în care se va defini numai metoda “foundFile”. In practicã se folosesc mai multe criterii: fisiere cu anumite atribute: Hidden. iar conditia de acceptare a unui fisier este ca data (lungimea) sã fie mai micã sau mai mare ca aceastã limitã. Ne vom rezuma la trei criterii de cãutare uzuale: dupã nume (cu sabloane “wildcards”). Ca exemplu simplu de utilizare vom da un program de afisare a cãilor la fisierele care satisfac simultan anumite conditii.accept(file)) h.length. Aceste optiuni vor fi transformate în filtre individuale de tipul FileFilter si apoi într-un singur filtru combinat. aceasta va contine numai trei metode callback: public interface FileHandler { void startDir (File dir) . File file.

// orice nume de director trece de filtru ! for (Iterator it = filters. this.next().*av?"). // nu se mai continua cu verificarea } } return true.put ("less".new Date (107. // afisare cale completa la fisier } public static void main (String args[]) { Map opts = new HashMap().Florian Moraru – Programare Orientata pe Obiecte class FileLister extends Finder { public FileLister (String d.iterator(). if (! filter. // director initial public Finder (String dirname.accept(file)) { // daca un filtru nu accepta fisierul return false.add (filter).7.find().put ("after".opts). // filtru complex de cautare private File dir. Map opt) { dir = new File (dirname).put ("name". // creare filtru combinat din optiuni } … // in rest la fel ca in clasa abstracta Finder data anterior Pentru satisfacerea simultanã a mai multor criterii vom defini o clasã auxiliarã pentru filtre combinate care foloseste o listã de filtre: public class ComboFilter implements FileFilter { private List<FileFilter> filters.opt). // daca file a trecut de toate filtrele } } Clasa IOFilter.size()>0) // daca exista optiuni filter = new IOFilter(opt). // obiect de listare fisiere cu optiuni lister.new Long (1200)). derivatã din ComboFilter. // nume si valoare optiune 3 FileLister lister = new FileLister(args[0]. } public void addFilter( FileFilter filter) { // adauga un nou filtru la lista filters. // nume si valoare optiune 2 opts.println(file). } } Rezultã cã a fost modificatã si clasa Finder astfel ca sã primeascã un argument de tip Map în locul unui argument de tipul FileFilter: abstract class Finder { private FileFilter filter.out. "*.11)). it. Map opt.isDirectory()) return true. opts. } protected void foundFile(File file) { System. int step) { // opt= dictionar de optiuni super(d. // nume si valoare optiune 1 opts.step= step. // lista de filtre public ComboFilter() { filters = new ArrayList<FileFilter>().) { FileFilter filter = (FileFilter) it. creeazã lista de filtre: 189 . if (opt !=null && opt. } public boolean accept( File file) { // verificare succesiva filtre din lista if (file.hasNext().

Entry)itr. return more ? !smaller : smaller.length()).next().limit = limit.Florian Moraru – Programare Orientata pe Obiecte class IOFilter extends ComboFilter { public IOFilter (Map options) { super(). if (opt. 190 . if (opt. true).after = after. } this.getValue()).Entry entry = (Map. private boolean after. if (filter != null) addFilter(filter). // o dimensiune data private boolean more.entrySet(). boolean more) { if (size < 0) { throw new IllegalArgumentException("The size is negative"). // verificare la mai mare sau la mai mic public SizeFilter(Long size) { this(size. else return null. entry. this. // false = before public DateFilter(Date limit) { this(limit.getKey().toString().equals("name") ) // fisiere cu nume dat (care se potrivesc) return new WildcardFilter((String)val). if (opt.false).equals("more")) // fisiere mai mari ca o valoare return new SizeFilter ((Long)val).equals("less")) // fisiere mai mici ca o valoare return new SizeFilter ((Long)val. FileFilter filter = selectFilter(entry. boolean after) { this.equals("before")) // fisiere anterioare unei date return new DateFilter ((Date)val. // adauga la lista de filtre } } } // selectarea unui filtru dupa numele optiunii private FileFilter selectFilter(final String opt.hasNext()) { // parcurge toata lista de optiuni Map. } // implicit la mai mare public SizeFilter(Long size.size = size. } public boolean accept(File file) { boolean smaller = new Long(file. Iterator itr = entries.more = more. // optiune necunoscuta } } Urmeazã clasele pentru filtre individuale: // filtru dupa lungime fisier public class SizeFilter implements FileFilter { private Long size.final Object val) { if ( opt. } // implicit “after” public DateFilter(Date limit. } } // filtru dupa data ultimei modificari a fisierului public class DateFilter implements FileFilter { private Date limit.false).equals("after")) // fisiere ulterioare unei date return new DateFilter ((Date)val). if (options != null) { Collection entries = options. if (opt. true).compareTo(size) < 0. while(itr. this.iterator().

String str) { while (str. if (pat.charAt(0) == '*') { do { pat=pat. } else if (pat.substring(1). fie în aplicatii cu interfatã text: 191 .after(limit) : filedate.length()==0) return true. str=str.substring(1))) return true. care va putea fi folosit fie în aplicatii cu interfatã graficã. } else if (pat.before(limit). Vom defini mai întâi o clasã derivatã din clasa abstractã Finder pentru crearea unui arbore de fisiere.lastModified()). // true=conteaza daca litere mari sau mici public WildcardFilter(String wildcard) { this(wildcard.substring(1).charAt(0) == '.length() > 0 ) { if (pat.charAt(0) == pat. return after ? filedate.') return false. true).length()>0 && pat. } public boolean accept(File file) { String name = file.substring(1).substring(1). while (str.toUpperCase ().substring(1). this.substring(1). } else { return false. name). str=str. private boolean caseSensi. } while (pat. } } // filtru dupa nume (potrivire cu un sablon ce poate contine ‘*’ si ‘?’) public class WildcardFilter implements FileFilter { private String wildcard. } return wildMatch(wildcard.charAt(0) == '?') { if (str.charAt(0)) { pat=pat.length()>0) if (wildMatch(pat. str=str.Florian Moraru – Programare Orientata pe Obiecte } public boolean accept(File file) { Date filedate = new Date(file.getName(). } Alte exemple de utilizare a clasei Finder Pentru a verifica cât de versatilã este clasa Finder vom încerca sã o folosim în douã aplicatii mai complexe: o aplicatie cu interfatã graficã pentru afisarea unui arbore de fisiere si un arhivator. if (str.charAt(0) == '*') pat=pat. if (! caseSensi) { name= name.toUpperCase(). pat=pat.} public WildcardFilter(String wildcard.wildcard = wildcard . boolean caseSensi) { this.length()==0. return false.length()>0 && pat. wildcard=wildcard.caseSensi = caseSensi . } } while (pat. } // daca sirul “str” se potriveste cu sablonul “pat” (pattern) private boolean wildMatch ( String pat.charAt(0) == '*'). return pat.length()==0) return false.

// pentru selectare nume fisiere cautate private JTextField size. // initializare obiecte vizuale setLayout(). // dictionar cu continut si adrese noduri public FileTree (String dir. Clasa pentru interfata graficã foloseste un obiect JTree pentru afisare. // initial afisare din directorul curent 192 . // comanda afisarea intr-o fereastra minimala } // initializare componente private void initComponents() { String mmopt[]={"at least". File d = new File(dir). In plus. mascã de selectie fisiere si dimensiune limitã fisiere. show(). // pentru introducere nume director private JTextField name. // adresa nod parinte par.get(parent). // ptr a evita apelul explicit al metodei find din afara clasei } public FileTree (String dir) { this(dir.getParentFile(). // utilizare limita: at least / at most private JButton start.").TNode>().kid). dimensiune limita a fisierelor cautate private JComboBox mm. options= new HashMap().TNode> nodes. // nodurile contin obiecte File root=new TNode (d).find(). // valoare nod parinte TNode par = nodes."at most"}. ar fi fost posibil sã existe mai multe noduri cu acelasi continut (fisiere cu acelasi nume în directoare diferite). // pune adresa nod in dict File parent = file.add(kid). // valoare si adresa nod radacina super. // ptr. // dictionar optiuni de cautare fisiere initComponents(). un obiect File contine calea completã la un fisier. // buton pornire cautare private JTree jtree. nodes.EXIT_ON_CLOSE). câmpuri text pentru preluare nume director.opts). } // constructor fara optiuni // redefinirea metodei apelate la fiecare fisier gasit protected void foundFile(File file) { TNode kid = new TNode(file). pack(). Map opts ) { super(dir. // creare nod ptr file nodes. // pentru afisare fisiere gasite private Map options. // afisate in JComboBox folder = new JTextField(10). // creare camp text director folder. } } Nodurile arborelui ar fi putut contine obiecte String cu numele fisierelor în loc de obiecte File. // dictionar cu nume si valori optiuni // initializari public FinderGUI() { super("File Finder").Florian Moraru – Programare Orientata pe Obiecte // clasa derivata pentru afisare cai la fisiere gasite (fara indentare) class FileTree extends Finder { private TNode root.put(file. private Map<File. // adauga nod fiu la nod parinte } public TNode getRoot () { return root. // radacina contine numele directorului nodes= new HashMap<File. obiect JComboBox pentru alegere mod de utilizare limitã (ca limitã inferioarã sau ca limitã superioarã) si un buton care comandã citirea datelor din câmpuri text si afisarea arborelui cu fisierele selectate conform optiunilor: class FinderGUI extends JFrame implements ActionListener { // obiecte vizuale din GUI private JTextField folder. care diferã chiar pentru fisiere cu acelasi nume din directoare diferite.root).put(d.null). dar atunci era mai complicat sã aflãm nodul pãrinte.setText(". // plasare componente vizuale pe ecran setDefaultCloseOperation (JFrame.

add (display.setLayout (new BorderLayout()).Florian Moraru – Programare Orientata pe Obiecte name = new JTextField(10). controls. display.setModel (new DefaultTreeModel(r)). FileTree ft = new FileTree (folder.2)). File folder = new File(dir). // modificare date afisate in JTree } public static void main (String args[]) { FinderGUI gui = new FinderGUI(). mainpanel.addActionListener(this). // creare arbore cu fisierele selectate TNode r= ft."Center"). controls. } // ascultator la eveniment produs de buton public void actionPerformed (ActionEvent ev) { options. // initial toate fisierele size= new JTextField(10).add( size).add(mm). // sterge continut anterior dictionar options.add (new JLabel("Folder")). // ascultator la actionare buton FileTree ft=new FileTree("."West"). controls.controls.setLayout (new BorderLayout()). // radacina arbore creat jtree.clear(). mainpanel.name.trim()).getSelectedIndex(). // creare obiect selectie start = new JButton("Start"). // initial limita inferioara zero mm=new JComboBox(mmopt).zip.*").setLayout( new GridLayout(0. Pentru ca arhiva sã aibã structura sistemului de fisiere arhivat (astfel ca la dezarhivare sã se refacã structura de directoare si fisiere) este suficient sã dãm metodei “putNextEntry” calea completã la fisier si nu doar numele fisierului arhivat. // nume optiune asupra dimensiunii de fisiere options.options).getRoot(). controls. // creare buton start. Exemplu de metodã staticã pentru arhivarea fisierelor dintr-un director care nu contine si subdirectoare: public static void zipFolder(String dir.controls.sz). Metoda “putNextEntry” scrie antetul care precede fiecare fisier în arhivã. JPanel display = new JPanel(). JPanel controls = new JPanel().getText().add (new JLabel("File Size")).add (new JLabel("File Name"))."). // creare camp text ptr limita dimensiune size. { 193 . controls. } // plasare obiecte vizuale pe ecran private void setLayout() { JPanel mainpanel = new JPanel(). // indice optiune selectata din ComboBox Long sz = new Long(size. String outFile) final int BUFFER=4096. } } Scrierea unui program de arhivare în format “zip” este facilitatã de existenta clasei de bibliotecã ZipOutputStream din pachetul java.add (controls.util. // controlalele pe o coloana setContentPane(mainpanel). display.add(start). // creare camp text masca nume fisiere name.put (how. // pune masca de selectie fisiere in dic.setText("*.put ("name".add( name). int ix=mm. controls.getRoot())).add (new JScrollPane(jtree)).setText("0"). // pune limita dimensiunii fisiere in dic. // limita dimensiuni fisiere String how = ix==0? "more":"less". // creare arbore afisat initial jtree= new JTree(new DefaultTreeModel (ft. iar metoda “write” scrie datele din fisierul arhivat în arhivã (un bloc de octeti).controls.add ( folder).getText().getText()). mainpanel.

close().write(data. count).isDirectory()) return. while((count = in. size.closeEntry(). 0. byte[] data = new byte[BUFFER].close().0. out. // write data header (name.close(). 194 . // write data header (name.putNextEntry(new ZipEntry(files[i]. BufferedInputStream in = null.write(data.closeEntry(). out.printStackTrace(). Metoda “foundFile” din clasa “Finder” este apelatã atât pentru fisiere normale cât si pentru subdirectoare.getAbsolutePath())).} catch(Exception e) {e. etc) int count. out. 0. //close each entry } in. // ptr reducerea timpului de citire din fisier private ZipOutputStream out. try { in = new BufferedInputStream(new FileInputStream (file). try { out = new ZipOutputStream ( new BufferedOutputStream( new FileOutputStream(outFile))).getName())). while((count = in.find().listFiles(). // obiect asociat cu fisierul arhiva public FinderZip(String dir. out. File files[] = folder.0. // close each entry in.printStackTrace().printStackTrace(). de aceea vom testa dacã este sau nu director pentru a evita exceptiile” class FinderZip extends Finder { final int BUFFER=4096.length. String outFile) { super (dir). i++) { in = new BufferedInputStream(new FileInputStream(files[i]). out.zip").5.read(data. if (file. } catch(Exception e) {e. byte[] data = new byte[BUFFER].putNextEntry(new ZipEntry(file.BUFFER)) != -1) out.BUFFER)) != -1) out.} super.read(data.close(). count). size.} } Dacã directorul “dir” contine si subdirectoare atunci apar exceptii la încercarea de citire date din aceste subdirectoare. "C:/test. } catch(Exception e) {e.4". i<files. BUFFER). etc) int count. for (int i=0.} } } Exemplu de utilizare: new FinderZip ("F:/groovy-1.} } protected void foundFile(File file) { BufferedInputStream in = null. } catch(Exception e) {e.printStackTrace(). BUFFER).Florian Moraru – Programare Orientata pe Obiecte try { ZipOutputStream out = new ZipOutputStream( new BufferedOutputStream( new FileOutputStream(outFile))). try {out.

Se mai foloseste schema pentru crearea de clase cu obiecte unice ("Singleton") în familia claselor colectie.Compozitia (agregarea) este preferabilã în raport cu derivarea. pentru cã permite separarea utilizãrii de implementare.Scheme de comunicare (de interactiune) între obiecte. clase observator. care furnizeazã aplicatiei datele si metodele necesare. Principalele recomandãri care rezultã din analiza schemelor de proiectare si aplicatiilor sunt: .Scheme de interactiune (Behavioral patterns). clase adaptor s. Clasele JDK (în special clase JFC) care urmeazã anumite scheme de proiectare au primit nume care sugereazã rolul clasei într-un grup de clase ce interactioneazã : clase iterator. O fabricã de obiecte (“Object Factory”) permite crearea de obiecte de tipuri diferite. care lasã mai multã libertate în detaliile de implementare a unor obiecte cu comportare predeterminatã. astfel existã clase iterator. Aceste obiective pot fi atinse în general prin clase (obiecte) “slab” cuplate.util”) si clasele observator si observat (“java. .util”) extinse în JFC la schema cu trei clase MVC ( Model-View-Controller). Trebuie remarcat cã si clasele predefinite JDK au evoluat de la o versiune la alta în sensul extinderii functionalitãtii si aplicãrii unor scheme de proiectare reutilizabile. adicã a unor grupuri de clase si obiecte care coopereazã pentru realizarea unor functii.Solutii optime pentru probleme comune de proiectare. O solutie mai flexibilã pentru crearea de obiecte este utilizarea unei fabrici de obiecte.Este recomandatã crearea de clase si obiecte suplimentare. . pentru decuplarea unor clase cu roluri diferite.swing”. clase “model” s. care nu se mai modificã. Clase si metode “fabricã” de obiecte In cursul executiei un program creeazã noi obiecte. Scheme de proiectare Scheme de proiectare Experienta acumulatã în realizarea unor aplicatii cu clase a condus la recunoasterea si inventarierea unor scheme (sabloane) de proiectare (“Design Patterns”).a. dar toate subtipuri ale unui tip comun (interfatã sau clasã abstractã). clase “fabricã” de obiecte.Florian Moraru – Programare Orientata pe Obiecte 16. pentru aceste obiecte. In cadrul acestor scheme existã clase care au un anumit rol în raport cu alte clase si care au primit un nume ce descrie acest rol. Argumentul principal în favoarea studierii si aplicãrii schemelor de clase este acela cã aplicarea acestor scheme conduce la programe mai usor de modificat. . Dincolo de detaliile de implementare se pot identifica clase cu aceleasi responsabilitãti în diferite aplicatii. Definitii posibile pentru schemele de proiectare folosite în aplicatii cu clase: . O clasificare uzualã a schemelor de proiectare distinge trei categorii: . prezentã în mai multe pachete. care grupeazã mai multe obiecte în structuri mai mari. care stiu cât mai putin unele despre altele. Schemele de interactiune prezente în JDK sunt clasele iterator (“java. . pentru crearea de diverse obiecte. .io” si clasele “adaptor” din “javax. Majoritatea obiectelor sunt create folosind operatorul new. cu rol de intermediari. Cea mai folositã schemã de creare obiecte în Java este metoda fabricã. . obiectelor sau componentelor. care definesc comunicarea între clase.Abstractii la un nivel superior claselor.Scheme structurale (Structural patterns).Scheme “creationale“ (Creational patterns) prin care se genereazã obiectele necesare.Proiectarea cu interfete si clase abstracte este preferatã fatã de proiectarea cu clase concrete. clase adaptor.a. Schemele structurale folosite in JDK sunt clasele “decorator” din pachetul “java. pe baza ipotezei cã existã o singurã clasã. O fabricã de obiecte se poate realiza în douã forme: 195 .

Utilizarea fabricilor de obiecte poate fi privitã ca un pas înainte pe calea separatiei interfatãimplementare.swing”) reuneste mai mai multe metode “fabricã” ce produc obiecte chenar de diferite forme pentru componentele vizuale Swing. } . Aceastã solutie se practicã atunci când obiectele fabricate nu diferã mult între ele. BevelBorder. . public static Border createEtchedBorder() { return sharedEtchedBorder.setBorder (new EtchedBorder ()). Metode si clase fabricã pot fi întâlnite în diverse pachete din JSE (Java Standard Edition ) si. b2= new JButton(“Etched2”).Ca metodã fabricã (de obicei metodã staticã) dintr-o clasã. fie prin fisiere de proprietãti (fisiere de configurare).Metoda creeazã un obiect numai la primul apel.setBorder (eb). Un exemplu este metoda “createEtchedBorder” din clasa BorderFactory care creeazã referinte la un obiect chenar unic. în sensul cã permite oricâte implementãri pentru o interfatã. care include toate subtipurile de obiecte fabricate de metodã (fabricã "polimorficã").Pentru a evita crearea de obiecte identice (economie de memorie si de timp). // chenar “gravat” // chenar construit cu metoda fabricã Border eb = BorderFactory. // chenar construit cu “new” b1. dupã care nu mai instantiazã clasa. produce referinte la obiecte deja existente.Florian Moraru – Programare Orientata pe Obiecte . ascunzând utilizatorilor efectul real al cererii pentru un nou obiect. Toate obiectele chenar apartin unor clase care respectã interfata comunã Border si care sunt fie clase predefinite. dar poate fi dedus din alte informatii furnizate de utilizator. în loc sã producã la fiecare apel un nou obiect. 196 . Pentru acest fel de metode fabricã existã câteva variante: . Exemple de clase chenar predefinite: EmptyBorder. O metodã fabricã poate fi o metodã staticã sau nestaticã. . b2. metoda fabricã va furniza la fiecare apel o referintã la un obiect unic si nu va instantia clasa. atunci când existã diferente mai mari între obiectele fabricate. la executie. desi se pot crea si obiecte chenar diferite cu new : JButton b1 = new JButton (“Etched1”).createEtchedBorder(). Metoda fabricã poate fi apelatã direct de programatori sau poate fi apelatã dintr-un constructor.Pentru cã tipul obiectelor ce trebuie create nu este cunoscut exact de programator.Metoda are ca rezultat o referintã la un obiect creat la încãrcarea clasei: public class BorderFactory { private BorderFactory () { } // neinstantiabilã static final sharedEtchedBorder = new EtchedBorder(). O metodã fabricã de un tip clasã instantiabilã tine evidenta obiectelor fabricate si. TitleBorder si CompundBorder (chenar compus). Rezultatul metodei este de un tip clasã instantiabilã. cu orice nume de clase si cu orice date initiale necesare fabricãrii obiectelor. Putem deosebi douã situatii în care este necesarã utilizarea unei metode “fabricã” în locul operatorului new: . care poate fi clasa abstractã ce defineste tipul comun al obiectelor fabricate. fie clase definite de utilizatori. Rezultatul metodei este de un tip interfatã sau clasã abstractã.. în JEE (Java Enterprise Edition). } // alte metode fabricã în clasã Clasa BorderFactory (pachetul “javax.Ca o clasã fabricã. mai ales. . Alegerea (sub)tipului de obiecte fabricate se poate face fie prin parametri transmisi metodei fabricã sau constructorului de obiecte “fabricã”. LineBorder..

//luna.text”: NumberFormat. fie sunt anticipate si alte implementãri ale unei clase în viitor. Tipul obiectelor fabricate este determinat de acesti parametri.a.getInstance().out. DateFormat df2 = DateFormat. Locale. Uneori se stie ce fel de obiecte sunt necesare.1 sau 2 parametri si care poate produce diferite obiecte de tip DateFormat. unul de tip TimeZone si altul de tip Local.out.Florian Moraru – Programare Orientata pe Obiecte Un exemplu de metodã fabricã controlabilã prin argumente este metoda “getInstance” din clasa Calendar.util”) contine câteva metode statice cu numele “getInstance” (cu si fãrã parametri). Metoda “getInstance” fãrã argumente produce implicit un obiect din cel mai folosit tip de calendar: public static Calendar getInstance() { return new GregorianCalendar(). Metoda “getInstance” poate avea unul sau doi parametri. System. Adaptarea se face printr-un parametru al metodei fabricã care precizeazã tara si este o constantã simbolicã din clasa Locale.format(date)). pentru tipul de calendar folosit în Europa si în America. Una din clasele instantiabile derivatã din Calendar este GregorianCalendar. s. zi cu luna in engleza System. } Obiectele de tip Calendar se pot utiliza direct sau transformate în obiecte Date: Calendar azi = Calendar.getDateInstance (). Portabilitatea doritã de Java cere ca operatiile sã fie programate uniform. sau de faptul cã existã mai multe implementãri.format(date)). indiferent de modul cum sunt implementate acele servicii. cu nume complet sau prescurtat pentru lunã etc. Alte metode fabricã care produc obiecte adaptate particularitatilor locale (de tarã) se aflã în clase din pachetul “java. pentru adaptarea orei curente la fusul orar (TimeZone) si a calendarului la pozitia geograficã (Local). care poate crea obiecte de diferite subtipuri ale tipului Calendar . Exemple: Date date = new Date().println ( df2. Afisarea unei date calendaristice se poate face în mai multe forme. // conversie din Calendar in Date System. dar ulterior s-a optat pentru o solutie mai generalã. pentru analizã de fisiere XML etc.println ( df1. Obiectele de tip datã calendaristicã au fost create initial în Java ca instante ale clasei Date. Clasa abstractã Calendar (din “java. DateFormat.).FRENCH). 197 . care fabricã obiecte de tip Calendar. dar care sã nu afecteze prea mult aplicatiile existente. dar nu se stie exact cum trebuie realizate aceste obiecte (cum trebuie implementatã clasa). pentru comunicarea prin mesaje (JMS. Astfel de interfete API existã pentru servicii prin natura lor diversificate ca mod de realizare: pentru acces la orice bazã de date relationalã (JDBC). an. Altfel spus. // zi. folosind o singurã interfatã API între client (cel care solicitã operatia) si furnizorul de servicii (“provider”). care sã tinã seama de diferitele tipuri de calendare folosite pe glob. Date now = azi.luna. unele necunoscute la scrierea metodei dar adãugate ulterior.an cu luna in franceza Fabrici abstracte Clasele de obiecte fabricã pot fi la rândul lor subtipuri ale unui tip comun numit fabricã abstractã (“AbstractFactory”).getTime(). fie nu existã o singurã clasã posibilã pentru aceste obiecte. se folosesc uneori fabrici de fabrici de obiecte.getDateInstance (2. SAAJ). fie existau anterior furnizori diferiti pentru aceste clase si se încearcã unificarea lor. Clasa DateFormat contine metoda “getDateInstance” care poate avea 0.out. DateFormat df1 = DateFormat.println (now). care depind de uzantele locale si de stilul dorit de utilizator (cu sau fãrã numele zilei din sãptãmânã.

"2".createConnection().forName (“sun. deci sã respecte un contract cu utilizatorii de conexiuni. Exemplu: 198 .jdbc. … In exemplul anterior obiectul fabricã “factory” este produs de o fabricã abstractã implicitã (numele este ascuns în metoda “newInstance”). Class cls) { Collection c=null. Pentru a întelege mai bine ce se întâmplã într-o fabricã (de fabrici) de obiecte am imaginat urmãtorul exemplu: o fabricã pentru crearea de obiecte colectie (de orice subtip al tipului Collection) pe baza unui vector implicit de obiecte. Obiectele conexiune sunt create de fabrici de conexiuni. try { c = (Collection) cls. dar nu existã în pachetul “java. care nu se poate face prin instantierea unei singure clase. obiectul fabricã de conexiuni nu este obtinut prin instantierea unei singure clase ci este fabricat într-un fel sau altul.length.getConnection (dbURL. care va putea fi folositã si pentru clase colectie încã nedefinite.Florian Moraru – Programare Orientata pe Obiecte Pentru acces la servicii oferite de un server se creeazã un obiect “conexiune”.passwd). Statement stm = con. Toate obiectele conexiune trebuie sã prevadã anumite metode. SOAPConnection con = factory. Utilizarea acestei fabrici este limitatã deoarece nu permite transmiterea de parametri la crearea obiectelor colectie. return c.newInstance().println(e).createStatement(). HashSet set = (HashSet) newCollection (a. care ascund particularitãti de implementare ale acestor obiecte. Obiectul conexiune este fabricat de metoda “getConnection” pe baza informatiilor extrase din clasa driver. } catch (Exception e) { System. de aceea.i<a.out. Exemplu: public static Collection newCollection (Object a[]. la fel este fabricat si obiectul “instructiune” de cãtre metoda “createStatement”. toate compatibile cu un tip comun (tipul interfatã “CollectionFactory”). dar care implementeazã interfata.sql” nici o clasã instantiabilã care sã implementeze aceste interfete. la crearea unei colectii ordonate este necesarã transmiterea unui obiect comparator (subtip al tipului Comparator). // clasa driver Connection con = DriverManager.newInstance(). fãrã a exclude posibilitatea altor fabrici de conexiuni SOAP. } // utilizare (in “main”) String a[ ] = { "1".JdbcOdbcDriver”).odbc."3"."2". de exemplu. cu nume si implementare cunoscute la scrierea aplicatiei. furnizatã de firma “Sun” pentru a permite exersarea de mesaje SOAP.user.} for (int i=0.add (a[i]). sub forma unei clase cu nume cunoscut (încãrcatã înainte de utilizarea clasei fabricã) ."1"}. Exemplu: Class. In exemplul anterior am folosit o metodã fabricã de obiecte colectie. // utilizare obiect conexiune In exemplul anterior Connection si Statement sunt interfete. livratã de obicei de cãtre furnizorul bazei de date.i++) c. iar operatiile ulterioare se exprimã prin apeluri de metode ale obiectului “conexiune”.class). numitã “DriverManager” foloseste o clasã “driver”. Fabricile de conexiuni pot fi uneori si ele foarte diferite si sã necesite parametri diferiti (ca tip si ca utilizare). Diversitatea apare în modul de creare a obiectului conexiune. de la diversi alti furnizori. Exemplu de utilizare a unei fabrici de fabrici de conexiuni pentru mesaje SOAP: SOAPConnectionFactory factory = SOAPConnectionFactory. O solutie ar putea fi definirea mai multor clase fabricã de colectii.HashSet. In JDBC clasa fabricã de conexiuni.

i<a. col =(Collection) constr.Florian Moraru – Programare Orientata pe Obiecte // interfata pentru fabrici de colectii interface CollectionFactory { public Collection newCollection (Object [] a).length.Comparator comp) { return new SortedCollectionFactory (clsName. Comparator comp) { try { Class cls = Class.} } public Collection newCollection (Object a[]) { for (int i=0.i++) col.getFactory ("java.getConstructor(clsargs). col =(Collection) cls.util.forName(clsName). } catch (Exception e) { System. } } // exemple de utilizare public static void main (String arg[]) throws Exception { String a[] = { "1".length.add (a[i]). } Instantierea directã a acestor clase fabricã ar obliga utilizatorul sã cunoascã numele lor si ar necesita modificãri în codul sursã pentru adãugarea unor noi clase fabricã.out. vom defini o metodã staticã (supradefinitã) cu rol de fabricã de obiecte fabricã: // metode fabrica de fabrici de colectii class FFactory { public static CollectionFactory getFactory (String clsName) { return new SimpleCollectionFactory (clsName).i<a. new MyComp()). Class[] clsargs = new Class[] {Comparator.forName(clsName).out. public SimpleCollectionFactory (String clsName) { try { Class cls = Class.newCollection (a).i++) col. CollectionFactory cf2 = FFactory.println(e). } // fabrica de colectii simple (neordonate) class SimpleCollectionFactory implements CollectionFactory { private Collection col. } } // fabrica de colectii ordonate class SortedCollectionFactory implements CollectionFactory { private Collection col.} } public Collection newCollection (Object a[]) { for (int i=0.TreeSet".add (a[i]).newInstance().println(e). return col.HashSet"). public SortedCollectionFactory (String clsName. 199 . De aceea. Constructor constr = cls. return col.getFactory ("java.class}. } public static CollectionFactory getFactory (String clsName."1"}. CollectionFactory cf1 = FFactory."2". HashSet set1 = (HashSet) cf1.comp) .util."3"."2". } catch (Exception e) { System.newInstance(args). Object[] args = new Object[] { comp }.

// sau un alt obiect ca argument Tratarea evenimentelor generate de diverse elemente de meniu (mai multe surse de evenimente.newCollection (a). In situatii reale pot fi diferente foarte mari între clasele fabricã pentru obiecte complexe. în aplicatiile reale) se poate face într-o singurã metodã: public void actionPerformed (ActionEvent e) { Object source = e. // sau un alt obiect ca argument exit.out. // optiune meniu vertical JMenuItem exit = new JMenuItem ("Exit"). Pentru a compara solutia schemei "Command" cu alte solutii de selectare a unei actiuni (comenzi). El constituie singura legãturã dintre partea de interfatã graficã a aplicatiei si partea de logicã specificã aplicatiei (tratarea evenimentelor).add (mFile). open. // actiune asociata optiunii "Open" 200 . // optiune pe bara orizontala mbar. System. // adauga optiuni la meniu vertical mFile. ceea ce permite modificarea lor separatã si chiar selectarea unei anumite actiuni în cursul executiei. System. Exemplu: public interface ActionListener { public void actionPerformed( ActionEvent ev). Se realizeazã astfel decuplarea celor douã pãrti. Ea foloseste o interfatã generalã. Un element de meniu este o optiune care declanseazã o actiune la selectarea ei. } In acest caz particular se putea folosi o singurã clasã fabricã de colectii cu mai multi constructori. // adauga bara meniu la JFrame JMenu mFile = new JMenu ("File"). Un meniu este format dintr-o barã meniu orizontalã (obiect JMenuBar).addSeparator( ). // creare bara meniu principal setJMenuBar (mbar). Clase cu rol de comandã (actiune) Schema numitã "Command" permite realizarea de actiuni (comenzi) multiple si realizeazã decuplarea alegerii operatiei executate de locul unde este emisã comanda.getSource(). care contine mai multe optiuni de meniu (obiecte JMenu) si fiecare optiune poate avea un submeniu vertical cu mai multe elemente de meniu (obiecte JMenuItem). Pentru programarea unui meniu cu o singurã optiune "File". de felul urmãtor: public interface Command { public void execute(). // optiune meniu vertical mFile. cu o singurã functie. deoarece sunt diferente mici între cele douã fabrici. // sursa evenimentului if ( source == open) fileOpen( ).println(set2). } // executã o actiune nedefinitã încã In Swing existã mai multe interfete "ascultãtor" corespunzãtoare interfetei “Command”.add (exit). // adauga optiunea mFile la bara meniu JMenuItem open = new JMenuItem ("Open"). care se extinde într-un meniu vertical cu douã elemente ("Open" si "Exit") sunt necesare instructiunile: JMenuBar mbar = new JMenuBar().addActionListener (this). sã examinãm pe scurt programarea unui meniu cu clase JFC.add (open).Florian Moraru – Programare Orientata pe Obiecte TreeSet set2 = (TreeSet) cf2.println(set1). mFile.addActionListener (this).out. prin producerea unui eveniment de tip ActionEvent. } Un obiect dintr-o clasã ascultãtor este un obiect "comandã" si realizeazã o actiune.

ActionListener cmd = new CmdListener(). Clase cu un singur obiect Uneori este nevoie de un singur obiect de un anumit tip. necesare pentru stabilirea si obtinerea proprietãtilor unui obiect actiune: text afisat în optiune meniu. prin utilizarea obiectelor "actiune". iar metoda "actionPerformed" corespunde metodei "execute". pentru acces la alte variabile din interfata graficã). 201 .O clasã staticã inclusã si o metodã staticã (în aceeasi clasã exterioarã) care instantiazã clasa (pentru a-i transmite un parametru). In loc sã se adauge celor douã bare obiecte diferite (dar care produc aceeasi actiune) se adaugã un singur obiect. Exemplu: class FileExitCmd extends JMenuItem implements Command { public FileExitCmd (String optname) { super(optname). Interfata Action corespunde interfetei "Command".addActionListener (cmd). Metoda "execute" este o metodã polimorficã. deci trebuie interzisã instantierea repetatã a clasei respective. } } .O clasã fãrã constructor public. câte un obiect pentru fiecare actiune selectatã. // actiune asociata optiunii } } In programarea anterioarã a meniului apar urmãtoarele modificãri: class CmdListener implements ActionListener { public void actionPerformed (ActionEvent e) { Commnad c = (Command) e.exit(0). numitã clasã "Singleton". // nume optiune (afisat in meniu) } public void execute () { System..exit(0).addActionListener (cmd). care implementeazã interfata Action. O barã de instrumente contine mai multe butoane cu imagini (obiecte JButton). cu o interfatã "Command". o scurtã descriere ("tooltip") a "instrumentului" afisat.Florian Moraru – Programare Orientata pe Obiecte else if (source == exit) System. iar selectarea unei implementãri sau alta se face în functie de tipul variabilei “c”.execute(). Existã câteva solutii: . c. cu o metodã staticã care produce o referintã la unicul obiect posibil ( o variabilã staticã si finalã contine aceastã referintã). } // actiune asociata optiunii "Exit" Aplicarea schemei "Command".. Schema "Command" mai este folositã în programarea meniurilor si barelor de instrumente ("toolbar"). conduce la definirea mai multor clase care implementeazã aceastã interfatã (clase de nivel superior sau clase incluse în clasa JFrame. Interfata Action extinde interfata ActionPerformed cu douã metode "setValue" si "getValue". deci de tipul obiectului care este sursa evenimentului. imagine afisatã pe buton din bara de instrumente. . care contine o metodã "actionPerformed". dar are acelasi rol cu o barã meniu: selectarea de actiuni de cãtre operator.getSource(). de tip Action. exit. open.

De exemplu.contains(o)) // "observers" este vector din clasa Observable observers. apeleazã o anumitã metodã a obiectelor observator înregistrate la el. compatibile cu interfata Observer. care ar trebui anuntate imediat de orice modificare în obiectul observat. SingletonSet(Object o) {element = o.addElement(o).util".. } private static class SingletonSet extends AbstractSet { private Object element. } 202 . SingletonMap). Programatorul de aplicatie va defini una sau mai multe clase cu rol de observator.i<n..Florian Moraru – Programare Orientata pe Obiecte Ambele solutii pot fi întâlnite în clasa Collections (neinstantiabilã) pentru a se crea obiecte din colectii "speciale" : colectii vide (EmptySet.i++) sets.add (new TreeSet (Collections. for (int i=0..} . la producerea unui eveniment. . EmptyList.. } . // public Iterator iterator() { . } } .. union } Schema claselor observat – observator In aceastã schemã existã un obiect care poate suferi diverse modificãri (subiectul observat) si unul sau mai multe obiecte observator. . pentru a realiza anumite actiuni. // metode find. EmptyMap) si colectii cu un singur obiect (SingletonSet. un buton JButton are rolul de obiect observat. Exemplu: public class Collections { public static Set singleton(Object o) { return new SingletonSet(o).} public int size() {return 1. Relatia dintre o clasã JFC generator de evenimente si o clasã ascultãtor (receptor) care reactioneazã la evenimente este similarã relatiei dintre o clasã observatã si o clasã observator. Motivul existentei acestei interfete este acela cã în clasa Observable (pentru obiecte observate) existã metode cu argument de tip Observer pentru mentinerea unui vector de obiecte observator. iar o clasã care implementeazã interfata ActionListener si contine metoda “actionPerformed” are rolul de observator. element)..singleton ( new Integer(i) ) )). // lista de multimi // constructor ( n= nr de elemente in colectie) public Sets (int n) { sets = new ArrayList (n). Schema observat-observator a generat clasa Observable si interfata Observer din pachetul "java. Obiectul observat contine o listã de referinte la obiecte observator si. Exemplu: public void addObserver(Observer o) { // adauga un nou observator la lista if (!observers.} public boolean contains(Object o) {return eq(o. SingletonList. } Exemplu de utilizare în problema claselor de echivalentã : public class Sets { // colectie de multimi disjuncte private List sets.

Exemplu de definire a unei subclase pentru obiecte observate: class MyObservable extends Observable { public void change() { // metoda adagata in subclasa setChanged(). for (int i = arrLocal. programatorul de aplicatie va defini o subclasã care preia toate metodele clasei Observable. arg). } // o este obiectul observat Clasa Observable nu este abstractã dar nici nu este direct utilizabilã. Metoda “notifyObservers” apeleazã metoda "update" pentru toti observatorii introdusi în vectorul "observers": public class Observable { private boolean changed = false. // daca s-a produs o scimbare private Vector obs. i--) ((Observer)obs. m=n/100.elementAt(I)).length-1. } . class ProgressBar implements Observer { public void update( Observable obs.update(this. } public void notifyObservers(Object arg) { // notificare observatori de schimbare if (!changed) return. // anunta observatorii ca s-a produs o schimbare } } Clasa observator poate arãta astfel. } } Exemplu de utilizare a obiectelor observat-observator definite anterior: public static void main(String[ ] av) { ProgressBar bar= new ProgressBar().out.print('#').addObserver(bar).. // din clasa Observable notifyObservers(). MyObservable model = new MyObservable().contains(o)) obs. dacã metoda “update” adaugã câte un caracter la o barã afisatã pe ecran (la fiecare apelare de cãtre un obiect MyObservable).. 203 . model. i>=0. int n=1000000000. apelatã de un obiect observat la o schimbare în starea sa care poate interesa obiectele observator înregistrate anterior: public interface Observer { void update(Observable o. observat changed = true.Florian Moraru – Programare Orientata pe Obiecte Interfata Observer contine o singurã metodã "update".addElement(o). Object arg). } protected void setChanged() { // comanda modificare stare ob. // lista de obiecte observator public void addObserver(Observer o) { // adauga un observator la lista if (!obs. Object x ) { System. dar adaugã o serie de metode specifice aplicatiei care apeleazã metodele superclasei “setChanged” si “notifyObservers”.

dar evenimentele nu sunt produse ca urmare directã a unor cauze externe programului (nu sunt cauzate direct de actiuni ale operatorului uman).a). Pentru a avea mai multã libertate în plasarea ferestrelor pe ecran vom crea douã panouri.Clase cu rol de redare vizualã a modelului. care genereazã evenimente pentru obiectele receptor înregistrate la model. fireChangeEvent actionPerformed.Clase cu rol de comandã a unor modificãri în model. prin usurinta de modificare separatã a fiecãrei pãrti (în raport cu un program monolit la care legãturile dintre pãrti nu sunt explicite). Obiectele folosite de aplicatie pot fi : JTextField pentru introducerea unui nume de fisier. au un rol similar cu interfata Observer. aplicatia verificã existenta acelui fisier în directorul curent si adaugã numele fisierului la o listã de fisiere afisatã într-o altã fereastrã. Un obiect “model” este un obiect observat (ascultat).change(). . care sunt douã “imagini” diferite asupra listei de fisiere selectate. putem modifica structurile de date folosite în memorarea foii de calcul (modelul) fãrã ca aceste modificãri sã afecteze partea de prezentare.a. Secventa urmãtoare realizeazã crearea obiectelor necesare si gruparea lor si este comunã pentru toate variantele discutate pentru aceastã aplicatie. Fereastra de introducere are rolul de “controler” deoarece comandã modificarea continutului celorlalte douã ferestre. Ele vor avea atasate si etichete JLabel care explicã semnificatia ferestrei. clase care implementeazã metodele de tratare a evenimentelor ("actionPerformed" s. addChangeListener fireActionEvent. aplicatii de tip “client-server” s. Separarea de responsabilitãti oferitã de modelul MVC este utilã pentru realizarea de aplicatii sau pãrti de aplicatii: componente GUI. stateChanged Clase “model” în schema MVC Schema MVC (Model-View-Controller) este o extindere a schemei "Observat-observator". corespund metodei "update". Arhitectura MVC foloseste clase având trei roluri principale: .Clase cu rol de “model”. } // modifica periodic stare obiect observat Interfetele ActionListener. Corespondenta dintre metodele celor douã grupe de clase este astfel: addObserver notifyObservers update addActionListener. adicã de obiect observator al modelului. o barã de instrumente “tool bar”). JTextField pentru afisare dimensiune totalã si JTextArea pentru lista de fisiere. ItemListener s. adicã de obiect observat care contine si date. de unde si numele de “Document-View-Controller”. Astfel. . Schema MVC a apãrut în legãturã cu programele de biroticã (pentru calcul tabelar si pentru editare de texte). Programatorul de aplicatie defineste clase ascultãtor compatibile cu interfetele "xxxListener".i++) if ( i%m==0) model. Pentru a ilustra folosirea schemei MVC în proiectarea unei aplicatii si afirmatia cã introducerea de obiecte (si clase) suplimentare face o aplicatie mai usor de modificat vom considera urmãtoarea problemã simplã: Operatorul introduce un nume de fisier. Ulterior vom adãuga o a treia fereastrã în care se afiseazã dimensiunea totalã a fisierelor selectate.a.a.Florian Moraru – Programare Orientata pe Obiecte for (int i=0. V si C) permite mai multã flexibilitate în adaptarea si extinderea programelor. Dacã nu existã fisier cu numele primit atunci se emite un semnal sonor si nu se modificã lista de fisiere (si nici dimensiunea totalã a fisierelor). iar metodele "actionPerformed" s. 204 . Separarea netã a celor trei componente (M.i<n. sau putem adãuga noi forme de prezentare a datelor continute în foaia de calcul sau noi forme de interactiune cu operatorul (de exemplu.

setLayout (new FlowLayout ()).getText().20). panel1.exists()) listView.setLayout(new FlowLayout()). // panou pentru controler si sizeView panel1.200). c. panel2. // imagine lista TextField sizeView = new TextField(15)..add(new JScrollPane(listView)). // ptr a verifica existenta fisierului if ( f. // adauga nume la lista afisata else Toolkit. Continutul clasei adaptor depinde si de tipul componentei de comandã (aici un câmp text) si de rolul componentei imagine în aplicatie. cu intervale de separare (“strouts”) si între ferestre si cu o altã grupare a componentelor atomice Swing în panouri. // panou pentru listView panel2. Pentru extinderea acestei aplicatii cu afisarea dimensiunii totale a fisierelor introduse pânã la un moment dat avem mai multe solutii: 205 .add(panel1). panel2.add (sizeView). } public static void main(String args[]) { MVC frame = new MVC().add(new JLabel("File")). // imagine dimensiune totala TextField controler = new TextField(10). // sirul introdus in “controler” File f = new File (newElem). c.setVisible(true). // Afisare imagini JPanel panel2 = new JPanel(). In secventa anterioarã s-a definit o clasã inclusã anonimã cu rol de “adaptor” între controler si imagine.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent ev) { String newElem = controler.beep(). panel1.getDefaultToolkit(). // Afisare controler JPanel panel1 = new JPanel().add(controler). frame.setText(""). In varianta aplicatiei cu numai douã ferestre (cu un singur obiect “imagine”) legarea obiectului “listView” la obiectul “controler” se face prin înregistrarea unui obiect ascultãtor la sursa de evenimente de tip ActionEvent si prin implementarea metodei “actionPerformed” cu actiunea declansatã în obiectul receptor: // Legare listView la controler controler. // Controler public MVC () { // constructor Container c = getContentPane().. // pentru defilare in zona text c.setSize(300. // daca nu exista fisier controler. // Legare imagini la controler . // sterge cimp de intrare } }).add( new JLabel("Size")). panel1.Florian Moraru – Programare Orientata pe Obiecte class MVC extends JFrame { TextArea listView = new TextArea(10.append ( " "+ newElem +"\n"). dar poate fi extins pentru “slefuirea” aspectului ferestrei principale cu chenare pe componente. frame. // afisare fereastra } } Programul este utilizabil.setLayout( new FlowLayout()). panel1.add(panel2).add( new JLabel("List")).

clasã care poate deveni foarte mare dacã numãrul 206 . } }). // sterge camp de intrare } }). De remarcat cã va trebui sã repetãm o serie de operatii din adaptorul deja existent.exists()) { listView.length(). actualizate într-o singurã functie apelatã la declansarea evenimentului de modificare a continutului câmpului text “controler”: // Legare controler la sizeView si listView controler. if ( f. // ptr a verifica existenta fisierului if ( f. File f = new File (newElem). // afisare dimensiune totala listView. // adauga nume la lista afisata } else Toolkit.getText().setText(" "+ sum). De fapt aplicatia functioneazã corect numai dacã este adãugat mai întâi adaptorul pentru câmpul text “sizeView” si apoi adaptorul pentru zona text “listView” (ordinea inversã modificã comportarea aplicatiei): // Legare controler la sizeView controler. // sirul introdus in “controler” File f = new File (newElem).setText("").Florian Moraru – Programare Orientata pe Obiecte a) Adãugarea unui nou receptor la “controler” printr-o altã clasã adaptor între controler si noul obiect “imagine”. modificarea sau extinderea ei necesitã modificãri într-o clasã existentã (clasa adaptor). cum ar fi verificarea existentei fisierului cu numele introdus în câmpul text.beep().setText(""). } else Toolkit.append ( " "+ newElem +"\n").getText(). // Legare controler la listView controler.addActionListener( new ActionListener() { int sum=0.getText().getDefaultToolkit(). controler.exists()) { sum+= f. // suma dimensiuni fisiere public void actionPerformed(ActionEvent ev) { String newElem = controler.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent ev) { String newElem = controler. b) Modificarea adaptorului astfel ca sã lege obiectul “controler” la ambele obiecte imagine.length(). // sterge cimp de intrare } }). sizeView. sizeView.setText(" "+ sum).append ( " "+ newElem +"\n"). controler.addActionListener( new ActionListener() { int sum=0. Desi este forma cea mai compactã pentru aplicatia propusã.getDefaultToolkit(). Mai neplãcut este faptul cã succesiunea în timp a executiei metodelor “actionPerformed” din cele douã obiecte adaptor (receptor) nu este previzibilã.exists()) { sum+= f.beep(). File f = new File (newElem). public void actionPerformed(ActionEvent ev) { String newElem = controler. if ( f.

In variantele prezentate aplicatia nu dispune de datele introduse într-un obiect accesibil celorlalte pãrti din aplicatie.lastElement(). “intervalRemoved” si “contentsChanged”. dintre care douã extind clasa “ListDataAdapter”: DefaultListModel model = new DefaultListModel(). Introducerea unui nou obiect cu rol de “model” care sã continã datele aplicatiei (lista de fisiere validate) permite separarea celor trei pãrti din aplicatie si modificarea lor separatã. dar va genera alte evenimente si va necesita alte metode de preluare a datelor).. pentru comprimare si adãugare la un fisier arhivã). dar fãrã nici un efect (“ListDataAdapter”). care contine un vector (si suportã toate metodele clasei Vector). eliminare element si modificare continut. Pentru problema datã se poate folosi modelul de listã DefaultListModel.addElement ( file ). // sterge cimp de intrare } } // clase adaptor intre model si imaginile sale class MV1Adapter extends ListDataAdapter { long sum=0. f= new File(file). String file. Acum sunt necesare trei clase adaptor care sã lege la model obiectul controler si cele douã obiecte imagine. pentru cã un câmp text sau o zonã text nu genereazã evenimente la modificarea lor prin program ci numai la interventia operatorului (evenimentele sunt externe si asincrone programului în executie).getDefaultToolkit(). c) Cuplarea obiectului “sizeView” la obiectul “listView” sau invers nu este posibilã.setText(" "+ sum). } } class MV2Adapter extends ListDataAdapter { public void intervalAdded (ListDataEvent ev) { // obiect “model” 207 . . definitã explicit. sum += f. // adaptor intre controler si model class CMAdapter implements ActionListener { public void actionPerformed(ActionEvent ev) { String file = controler. public void intervalAdded ( ListDataEvent ev) { file = (String) model. Un receptor al evenimentelor generate de un model ListModel trebuie sã implementeze interfata ListDataListener. Sau. putem defini o altã clasã care implementeazã interfata ListModel .exists()) model..Florian Moraru – Programare Orientata pe Obiecte obiectelor ce comunicã si/sau functiile acestora cresc. sumView.beep().setText(""). File f= new File(file). controler. Extragerea datelor direct dintr-o componentã vizualã Swing nu este totdeauna posibilã si oricum ar fi diferitã pentru fiecare tip de componentã. chiar dacã rãmâne inclusã în clasa MVC. if (f. Din acest motiv clasa adaptor este mai bine sã fie o clasã cu nume. Se poate defini o clasã adaptor cu toate metodele interfetei ListDataListener. care contine trei metode : “intervalAdded”. desi lista de fisiere ar putea fi necesarã si pentru alte operatii decât afisarea sa (de exemplu.getText(). else Toolkit. Dintre modificãrile posibile mentionãm : adãugarea unor noi obiecte imagine (de exemplu o fereastrã cu numãrul de fisiere selectate si o barã care sã arate proportia de fisiere selectate din fisierul curent ) si înlocuirea câmpului text din obiectul controler cu o listã de selectie (ceea ce este preferabil.length(). File f. dar în plus poate genera trei tipuri de evenimente: adãugare element.

// listView la model } O clasã "adaptor" are rolul de adaptare a unui grup de metode (o "interfatã") la un alt grup de metode si reprezintã o altã schemã de proiectare. unde partea de model contine clasele ce corespund tabelelor bazei de date (inclusiv logica de utilizare a acestor date). extinderea sau modificarea aplicatiei a devenit mai simplã si mai sigurã pentru cã se face prin prin adãugarea de noi obiecte sau prin înlocuirea unor obiecte. fãrã a modifica obiectele existente sau metodele acestor obiecte. // model la controler model. Schema MVC este mult folositã în realizarea aplicatiilor Web. } } . Aplicatia se poate îmbunãtãti prin modificarea modului de alegere a fisierelor de cãtre operator: în loc ca acesta sã introducã mai multe nume de fisiere (cu greselile inerente) este preferabil ca aplicatia sã afiseze continutul unui director specificat iar operatorul sã selecteze fisierele dorite.append(" "+ model. // sumView la model model.. 208 . iar partea de comandã (“controller”) corespunde preluãrii cererilor HTTP si transmiterii rãspunsurilor corespunzãtoare (care include de obicei date din model ). animatie si alte efecte vizuale). // Cuplare obiecte MVC prin obiecte adaptor controler. Desi textul sursã al aplicatiei a crescut substantial fatã de varianta monolit.addListDataListener( new MV2Adapter()).Florian Moraru – Programare Orientata pe Obiecte listView. partea de vizualizare corespunde interfetei cu utilizatorul (obiecte grafice afisate de aplicatie în browser.addListDataListener( new MV1Adapter()). validãri asupra datelor introduse de operator..addActionListener( new CMAdapter()).lastElement()+"\n").

P2) sunt concurente sau paralele atunci când executia lui P2 începe înainte de terminarea completã a lui P1. Un alt nivel de paralelism poate avea loc în cadrul fiecãrui program (aplicatie). sau blocat. astfel încât se creeazã aparenta cã un acelasi program (calculator) poate efectua în paralel mai multe operatii. cele douã procese pot evolua în paralel. pentru cã existã un alt proces mai important si gata de executie. Se spune cã douã procese (fire) P1 si P2 (lansate în ordinea P1. Un proces poate contine mai multe fire (subprocese). cum ar fi o decizie a planificatorului de procese. fisiere etc) si atunci este necesarã sincronizarea lor în încercarea de acces la o resursã comunã. Planificatorul de procese este parte din masina virtualã Java (JVM). Altfel spus. iar schimbarea stãrii sale este cauzatã fie de evenimente interne ( o actiune a procesului activ). chiar pe un singur procesor care executã alternativ secvente de intructiuni din P1 si din P2. fie de evenimente externe procesului.Suspendat. fiecare cu spatiul sãu de memorie (de nume). astfel ca în cazul când mai multe procese sunt gata de executie. Paralelismul proceselor nu implicã neapãrat mai multe procesoare care lucreazã simultan. . fiecare cu o conexiune separatã la server. Este posibil ca în douã procese (fire) paralele sã se execute aceeasi secventã de instructiuni sau secvente de instructiuni complet diferite. . Dupã crearea sa si înainte de terminare. Cedarea controlului procesorului de cãtre procesul activ (în executie) se poate face voluntar (la cererea sa.Florian Moraru – Programare Orientata pe Obiecte 17. Câteva exemple tipice de aplicatii cu paralelism între activitãtile din interiorul aplicatiei: . In Java paralelismul (aparent) are loc în cadrul unui singur program (sau aplet). care sã decidã ce proces primeste controlul procesorului. dar care pot comunica prin intermediul sistemului de operare. sau în asteptare. In anumite aplicatii este necesar ca mai multe activitãti sã progreseze în paralel. ci este o alternativã la executia strict secventialã a celor douã procese : se executã complet P1 si apoi se excutã complet P2 (fãrã întreruperi). când procesul asteaptã producerea unui eveniment. în cadrul unei aplicatii. se alege procesul cel mai important pentru activare. Activitãtile paralele pot evolua complet independent unele de altele (asincron) sau pot utiliza anumite resurse comune (zone de memorie. 209 .Gata de executie dar inactiv. când procesul este activ (detine controlul procesorului). prin apelul unei metode) sau ca urmare a lansãrii unei operatii de intrare-iesire.Algoritmi paraleli pentru îmbunãtãtirea performantelor unor operatii de cãutare sau de alt tip. iar numele folosit pentru o astfel de activitate este acela de “thread” (fir de executie) sau “proces de categorie usoarã” (“ligthweight process”).In executie. . . La nivelul sistemului de operare activitãtile paralele se numesc procese (în sisteme de tip Unix) sau “taskuri” (în sisteme Microsoft Windows ). Java este primul limbaj care a inclus facilitãti de programare cu subprocese concurente. Procesele paralele îsi disputã timpul procesorului si trebuie sã existe un planificator de procese (numit si dispecer). deoarece foloseste acelasi spatiu de memorie (si de nume) cu celelalte fire de executie din acelasi program. sub forma unor fire de executie (“threads”).Un joc cu imagini multiple animate simultan. Programarea cu fire de executie Fire de executie concurente Paralelismul unor activitãti se poate realiza la nivelul sistemului de operare pentru activitãti ce rezultã din executarea unor programe independente (nu neapãrat distincte). paralelism suprapus peste concurenta proceselor la nivelul sistemului de operare. la producerea anumitor evenimente cu efect asupra stãrii poceselor. un proces poate trece ciclic prin câteva stãri.Un program de tip “server” care trebuie sã rãspundã cererilor adresate de mai multi “clienti” (programe client). Principalele stãri în care se poate afla un proces sunt: . De obicei fiecare proces are o prioritate.

pentru lansarea sa în competitie cu celelalte fire. Exemplu: new T( ). fisiere de date. Firul pãrinte apeleazã de obicei o singurã metodã a firului “fiu” (metoda “start”). Metodele clasei Thread sunt de douã categorii: metode ale obiectelor si metode statice. Alte fire de executie pot fi create din firul “main” sau din alte fire create anterior. fir1.. fie pe baza unei clase care implementeazã interfata Runnable (interfatã cu o singurã metodã “run”). In Java existã întotdeauna un fir de executie implicit (cu numele “main”). Fire de executie în Java In Java un fir de executie este un obiect dintr-o subclasã a clasei de bibliotecã Thread.start().lang” si. Clasele pentru fire de executie se definesc fie ca subclase ale clasei Thread (cu metoda “run” redefinitã).).util. Firele din clase ce extind clasa Thread pot avea un constructor cu parametru de tip String. de aceea nici nu se mai creeazã uneori variabile de tipul subclasei.. Clasa Thread si interfata Runnable se aflã în pachetul “java. Incepând cu versiunea 5 au fost introduse noi clase si metode pentru executia si sincronizarea firelor concurente cu resurse comune în pachetul “java. In clasa Thread metoda “run” nu are nici un efect si de aceea trebuie definitã o subclasã. prezentã în orice fir de executie (mostenitã de la clasa Thread). prin care procesele se adreseazã planificatorului. atunci firul este activat imediat dupã apelul metodei “start”. fie în lista proceselor suspendate sau blocate. // obiect anonim de tipul T Apelul metodei “start” dintr-un fir nu are ca efect imediat apelul metodei “run” din acel fir. } } Crearea unui nou fir de executie înseamnã crearea unui nou obiect si se face într-un fir activ (care poate fi cel implicit. s. Dacã nu existã alte fire gata mai importante si dacã task-ul Java este activ. Interactiunile dintre procese (sau subprocese) paralele nu se fac prin apeluri directe între procese ci prin intermediul dispecerului. notify). Procesele paralele (concurente) îsi pot disputa anumite resurse comune (date din memorie.Florian Moraru – Programare Orientata pe Obiecte Un singur proces poate fi în executie. unele apelate de dispecer iar altele care apeleazã dispecerul. pentru functia “main”).a. Coordonarea si sincronizarea proceselor paralele necesitã existenta unor operatii specifice. fie metode din clasa Thread (sleep.start(). Dintre procesele gata planificatorul alege pe cel cu prioritate maximã si care este primul în coadã (dacã sunt mai multe procese gata cu aceeasi prioritate). Exemplu: Thread fir1 = new T(). Efectul metodei “run” (apelatã de dispecer) depinde de rolul pe care îl are firul de executie în cadrul unei aplicatii. Planificatorul activeazã un fir prin apelarea metodei “run”.) sau îsi pot transmite date între ele (procese numite “producãtor” si “consumator”). ci introducerea firului respectiv în coada proceselor gata de executie. s. yield. alãturi de cuvântul cheie “synchronized” au existat încã din prima versiune Java. Operatiile prin care un fir interactioneazã cu planificatorul sunt fie metode din clasa Object (wait. cu metoda “run” redefinitã: class T extends Thread { public void run() { . celelalte se aflã fie în lista proceselor “gata”. Clasa Thread contine câteva metode. prin care se poate da un nume firului creat. creat automat si în care se executã functia “main” cu rol de program principal. join.concurrent”. 210 .a. într-o pozitie care depinde de prioritatea firului.

nu poate arunca mai departe exceptia InterruptedException si de aceea exceptia trebuie “prinsã” si tratatã în metoda “run”.start(). dar si de alte evenimente din sistem (pentru un sistem de operare multi-tasking care alocã intervale de timp fiecãrui task.out. De obicei metoda “run” contine un ciclu în care se executã anumite operatii (afisare.start().Florian Moraru – Programare Orientata pe Obiecte Metode care pot fi aplicate oricãrui obiect fir de executie: start : Cere lansarea unui fir dintr-un alt fir (pãrinte).i++) { System. } // creare si lansare procese paralele public static void main (String [] arg) { T p1 = new T("Unu"). conditie care nu poate fi verificatã la compilare dar care produce la executie exceptia IllegalMonitorStateException. Este posibil ca mai multe fire concurente sã execute aceleasi operatii. p1. // apel constructor clasa Thread } public void run () { for (int i=1. cum este Microsoft Windows ). T p2 = new T("Doi"). interrupt: Cere întreruperea firului pentru care se apeleazã getName/setName : Obtine/modificã numele firului pentru care se apeleazã getPriority/setPriority : Obtine/modificã prioritatea firului pentru care se apeleazã join(): Asteaptã terminarea firului pentru care se apeleazã Metode statice care pot fi folosite doar de firul în executie (firul curent): Thread currentThread(): referintã cãtre firul curent. } // “doarme” un timp catch (InterruptedException e) { } } System. operatii cu fisiere etc. “sleep”.) si se apeleazã una din metodele prin care se cedeazã controlul celorlalte fire concurente (“yield”.i<10. 211 . // la terminarea procesului } private int time (int i) {return (int) (100*Math.out. în executie sleep(long millis): autosuspendare fir curent pentru un timp specificat în milisecunde boolean interrupted(): testeazã si anuleazã starea de întrerupere a firului curent Metodele urmãtoare sunt mostenite 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 detine o resursã comunã) notify.println (getName()+ " terminat"). când este posibil. } } Succesiunea de afisare depinde de secventa de numere aleatoare generate. Exemplu: class T extends Thread { // clasa pentru obiecte fire de executie public T (String nume) { super(nume). Metoda “run”. Metodele “wait” si “notify” pot fi folosite numai în secvente sincronizate.random()). notifyAll : Firul activ notificã (anuntã) firele în asteptare cã s-a produs evenimentul asteptat prin “wait”. // scrie numele procesului try { sleep (time(i)). “wait” etc. asa cum este definitã în interfata Runnable (pe care o implementeazã si clasa Thread). p2. isAlive : Verifica dacã firul pentru care se apeleazã s-a terminat definitiv sau nu. Metoda “run” din Thread se redefineste în fiecare fir definit de utilizatori si determinã efectul executãrii acestui fir.println (i + " " + getName()).). desenare.

Fiecare fir de executie primeste automat un nume. try { Thread. Exemplu: class Proces implements Runnable { // fire cu interfata Runnable public void run () { for (int i=1. Definirea unei subclase a clasei Thread nu mai este posibilã pentru procese paralele din cadrul unui aplet.} catch (Exception e) { } } } } Un fir este terminat definitiv fie prin terminarea instructiunilor din metoda “run”.sleep(100).in. deoarece un aplet este o subclasã a clasei Applet si în Java nu este permis ca o clasã sã extindã mai mult de o singurã clasã.read() > 0 ). se pot folosi metodele statice din clasa Thread : Thread. De aceea a fost creatã si o interfatã numitã Runnable.Florian Moraru – Programare Orientata pe Obiecte Exceptia InterruptedException apare atunci când se încearcã întreruperea unui fir aflat într-o stare de asteptare ca urmare a unui apel “wait”.currentThread si Thread. Fiecare fir are o prioritate asociatã (un întreg între 1 si 10). de exemplu).print(". fie datoritã unei exceptii propagate în afara metodei “run”. la initierea unei operatii de I/E.i++) { System.sleep ( milisec).currentThread() + ” “+ i).Dacã mai multe fire sunt gata de executie.out."). este ales firul cu prioritate maximã pentru a fi activat.isAlive()) { System. dar aceasã prioritate poate fi modificatã prin metoda “setPriority”.sleep(20). La creare un fir primeste prioritatea implicitã 5. terminarea operatiei respective este un eveniment tratat de planificator. } // asteapta tastarea unei clape catch (IOException e) {} } public static void main (String [] arg) { Fir t = new Fir ().println (Thread. // activare fir de citire de la tastatura while (t. fie într-o altã metodã (dintr-un alt fir. atunci când planificatorul preia controlul ( la apelul unei metode sau la terminarea unei operatii de I/E). care aratã importanta relativã a acelui fir fatã de alte fire. dacã nu primeste explicit un nume la construirea obiectului de un subtip al tipului Thread . t. care nu este subclasã a clasei Thread.i<8. Fiecare fir are asociatã o variabilã (boolean) cu starea sa de întrerupere. folosind un constructor al clasei Thread care admite un parametru de tip Runnable. In aceastã clasã. “sleep” sau “join”. care contine numai metoda “run”. în aceste cazuri nu se produce întreruperea solicitatã. fie în functia “main”. 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). // afisare punct la fiecare 100 ms try { Thread. “notify” si “notifyAll” nu puteau face parte din aceatã interfatã deoarece ele nu pot fi implementate de orice utilizator si de aceea fac parte din clasa Object. Exemplul urmãtor pune în evidentã aceastã situatie: // proces de citire de la tastatura class Fir extends Thread { public void run () { try { while ( System. ca metode native si finale. } catch (InterruptedException e) {} 212 . Cedarea controlului de cãtre un fir se face si implicit. De aceea trebuie creat câte un obiect Thread pe baza fiecãrui obiect Runnable. Lansarea în executie a unui fir se poate face numai prin apelul metodei “start” din clasa Thread .out. Metodele “wait”. Crearea unui fir de executie folosind interfata Runnable necesitã definirea unei clase care implementeazã aceastã interfatã si definirea metodei “run” din aceastã clasã. Metoda “main” este si ea parte dintr-un fir de executie creat automat la activarea masinii virtuale Java pentru interpretarea unui program.start().

unele vor fi mentionate aici. Firele paralele pot evolua un timp independent unele de altele. rezultatele unor programe pot fi diferite dacã sunt rulate în conditii diferite si nu s-a tinut seama la scrierea lor de toate eventualitãtile. atunci poate avea loc o actualizare eronatã a datelor si o informare gresitã a clientilor. Pentru un contor în memorie vom forta transferul controlului de la un fir la altul prin introducerea de apeluri la “sleep” sau “yield” între instructiunile care realizeazã actualizarea contorului. de ordinea lansãrii lor. Exemplul clasic este cel al unui sistem de rezervare a locurilor (trenuri. Actualizarea se realizeazã prin mai multe operatii (instructiuni masinã si/sau operatii de I/E) si aceastã secventã de operatii poate fi întreruptã (de planificator). Sistemul poate functiona corect cât timp cererile simultane nu se referã la un acelasi loc si cererile cãtre acelasi loc sunt secventiale în timp. cu douã metode publice: “get” care citeste valoarea curentã a variabilei contor (de tip private) si “incr” care mãreste cu 1 valoarea variabilei contor. sau situatii de actualizare eronatã a unor date comune sau situatii de transmitere incorectã a unor date între fire de executie. comportarea unui program corect trebuie sã fie reproductibilã. dar în anumite momente pot sã-si dispute resurse comune sau doresc sã-si transmitã date prin variabile comune. new Thread (new Proces ()). Contorul este un obiect dintr-o clasã “Contor” definitã de noi. avioane.start().start(). actualizarea bazelor de date din mai multe procese paralele poate conduce la astfel de erori si este prevãzutã gruparea operatiilor de actualizare a unui articol într-o “tranzactie”.) în care existã o bazã de date comune folositã si actualizatã de la mai multe terminale dispersate ca locatii si care actioneazã independenet unele de altele (nu poate fi controlatã ordinea în care apar solicitãrile de rezervare si continutul acestora). In ambele situatii trebuie reglementat accesul la resursele comune. Sincronizarea firelor concurente cu resurse comune trebuie sã asigure rezultate corecte indiferent de numãrul firelor. } } Fire de executie cu date comune Programarea cu fire concurente are o serie de particularitãti fatã de programarea secventialã. de timpul necesar fiecãrui fir pentru operatiile cu resursa comunã sau de alti parametri care nu au legãturã cu algoritmul aplicatiei cu fire multiple. iar un alt proces reactivat poate dori sã execute aceeasi secventã de incrementare a aceluiasi contor (cazul a douã oficii din localitãti diferite de la care se cere simultan rezervarea unui loc la un zbor sau la un tren). class Contor { private int m. hoteluri. indiferent de conditiile în care se executã programul respectiv.Florian Moraru – Programare Orientata pe Obiecte } } // creare si lansare procese paralele public static void main (String [] arg) { new Thread ( new Proces ()). Altfel spus. deoarece rezultatele sale pot sã depindã si de alt factori care nu au legãturã cu algoritmii folositi dar tin de interactiunile voluntare sau involuntare dintre aceste activitãti. Pentru a ilustra efectele nesincronizãrii a douã fire cu o resursã comunã vom considera exemplul a douã fire care actualizeazã aceleasi date (incrementeazã un acelasi contor). In general. Dacã secventa de operatii pentru rezervarea unui loc poate fi întreruptã de cãtre o altã cerere cãtre acelasi loc. } public void incr () { // pentru obiecte folosite de fire // incrementare m in trei pasi 213 . etc. Verificarea corectitudinii unui program cu activitãti paralele este mai complicatã decât în cazul unui program secvential. iar altele rãmân a fi descoperite în lucrãrile destinate acestei problematici. public Contor () { m=0. Altfel spus. deoarece în caz contrar pot apare situatii de blocare definitivã a unor procese. care nu poate fi întreruptã si care poate fi “anulatã” în caz de eroare la una din aceste operatii.

while ( f1.start().i<5.random()*20)). f2.isAlive() && f2.sleep(100). System.start().sleep((int) (Math.i++) 214 . } catch(InterruptedException e) {} c.file=file.incr().random()*20)).} catch (Exception e) {} System. // pasul 2 Thread.out. este întrerupt de firul f2 care citeste si el în variabila sa proprie “aux” aceeasi valoare “m”.i<250. // pasul 1 Thread. m=aux. fiecare proces incrementeazã apoi si scrie înapoi în “m” aceeasi valoare a variabilei sale “aux”. // pasul 3 } catch (InterruptedException e) {} } public int get () { return m. // contor folosit de cele doua fire Fir f1 = new Fir ( c ). ca urmare a initierii unor operatii de citire/scriere pe disc si nu prin cedarea voluntarã a controlului: class Fir extends Thread { String file. try { aux=m. dar valoarea finalã a contorului nu este 10. // valoare finala contor } } // citire valoare contor Fiecare din cele douã fire face câte 5 incrementãri ale contorului comun. ci este o valoare mai micã ( 5 pentru programul anterior).i++){ try { sleep(10). public Fir ( Contor c) { this.println (getName()+" "+c. Explicatia este simplã: firul f1 citeste în “aux” o valoare “m” a contorului.println (c.get()). } public void run () { for (int i=0. Un exemplu asemãnãtor foloseste un contor memorat într-un fisier disc si incrementat din mai multe fire concurente. Rezultatul final depinde în general de timpii de asteptare ai fiecãrui fir (între operatii). Fir f2 = new Fir ( c ).out.get()). } public void run () { for (int i=0. public Fir ( String file) { this.c=c.Florian Moraru – Programare Orientata pe Obiecte int aux.isAlive() ) // asteapta terminare f1 si f2 try {Thread. f1.sleep((int) (Math. Din aceastã cauzã valoarea contorului creste cu 1 si nu cu 2 cum ar fi trebuit dacã cele douã fire erau sincronizate fatã de resursa comunã. în acest caz pierderea controlului de cãtre fiecare fir concurent se face automat. } } // pentru obiecte fir de executie class Fir extends Thread { Contor c. } } } class ExFir { public static void main (String args[]) { Contor c= new Contor(). aux=aux+1. de ordinea lansãrii si de alte procese din sistem.

} } } In functia “main” au fost lansate 4 fire ca cel prezentat si s-a afisat valoarea din fisier dupã terminarea lor.”mutex”.random()*20)). astfel ca sã o putem aplica si la actualizarea unor date dintr-un fisier din mai multe fire concurente: 215 . rezultatul a fost mereu altul dar oricum mai mic decât 1000 (4*250).”monitor”). iar la iesirea din blocul sincronizat zãvorul este deschis pentru a permite executarea secventei sincronizate din alte fire.seek(0). la intrarea într-un bloc sincronizat zãvorul se închide si nu permite accesul din alte fire la obiectul protejat.close(). fãrã ca un alt proces sã poatã executa si el aceleasi operatii pentru un acelasi obiect. m=aux.sleep((int) (Math. // pasul 2 Thread. Putem generaliza solutia de mai sus prin definirea unui obiect sincronizat. de numãrul firelor concurente si de alti parametrii care nu ar trebui sã influenteze rezultatele programului. Sincronizarea firelor de executie concurente Prima solutie Java pentru sincronizarea firelor de executie 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 instrcutiuni. 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. In general. // repozitionare pentru scriere contor modificat f.sleep((int) (Math. // citire contor f. // pasul 1 Thread.readInt()."rw"). nu se permite altui fir sã mai execute o metodã sincronizatã pentru acelasi obiect. acest cuvânt se adaugã metodei “incr” (eventual si metodei “get”): public synchronized void incr () { int aux. Acest mod de comportare este tipic pentru programe cu fire de executie concurente nesincronizate: rezultate diferite în functie de numãrul de operatii. Altfel spus. In Java o sectiune criticã este fie o metodã. Este important ca instructiunile dintr-o sectiune criticã sã se execute neîntrerupt pentru un anumit proces si pentru un anumit obiect. } catch (IOException ex){ ex.printStackTrace(). fie un bloc de instructiuni precedate de cuvântul cheie synchronized. aux=aux+1. // pasul 3 } catch (InterruptedException e) {} } Dupã ce firul f1 a intrat în executia metodei sincronizate “incr” pentru un obiect “c”.Florian Moraru – Programare Orientata pe Obiecte try { RandomAccessFile f = new RandomAccessFile (file. trebuie serializat accesul la resursa comunã pentru procese (fire) concurente sau trebuie asiguratã excluderea mutualã a firelor în raport cu resursa comunã.random()*20)). // scrie contor modificat f. Sincronizarea se face la nivel de obiect si nu la nivel de functie. iar firul f2 care încearcã acest lucru este pus în asteptare pânã la terminarea metodei sincronizate.writeInt (n+1). Sectiunile de cod din fiecare proces care acceseazã resursa comunã se numesc sectiuni critice sau regiuni critice. try { aux=m. int n = f. In exemplul anterior cu incrementarea unui contor din memorie. Fiecare obiect are asociat un “zãvor” (“lock”.

// obiect cu metode sincronizate public Fir ( SyncObject obj) { this. } catch (IOException ex) { ex. este preferabil sã se sincronizeze numai operatiile de actualizare a unui aceluiasi articol din fire concurente. Deoarece sincronizarea se face la nivel de obiecte. Dupã apelarea unei metode “sincronizate” pentru un obiect acest obiect este “blocat” cu un “zãvor” sau “lacãt” (“lock”) si nu mai poate fi apelatã o altã metodã sincronizatã pentru acelasi obiect (dintrun alt proces).} } } In exemplul anterior este interzis accesul simultan din mai multe fire la orice operatie si la întregul fisier.printStackTrace().writeInt (n+1). 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 executie paralele.writeInt(0). f. // nume fisier public SyncObject(String file){ this."rw"). dar nu poate fi reapelatã pentru acelasi obiect dintr-un alt fir concurent.close(). nu este posibil ca douã fire sã adauge în paralel elemente la un acelasi vector (“addElement”). Toate metodele de acces la un vector (din clasa Vector) sunt sincronizate pentru a fi sigure la apelare din fire concurente.update(). ceea ce nu este bine ca timp pentru fisiere mari accesate din multe fire paralele.seek(0). f.i<250. int n = f. try { RandomAccessFile f = new RandomAccessFile (file. // metoda sincronizata a obiectului } } } Pentru fisierul cu un singur articol obiectul sincronizat va fi: class SyncObject { String file. O metodã sincronizatã poate fi întreruptã. f. f. } public void run () { for (int i=0.printStackTrace()."rw"). f. Deblocarea obiectului are loc la terminarea executiei metodei sincronizate.close(). De asemenea.file=file. fiind permisã citirea unui articol din diferite fire si actualizarea paralelã a unor articole diferite din acelasi fisier de cãtre fire diferite. datele comune proceselor trebuie încapsulate în obiecte 216 .readInt(). nu este permisã apelarea din fire diferite a unor metode sincronizate diferite pentru acelasi obiect.obj=obj.} } synchronized void update() { // metoda sincronizata try { RandomAccessFile f = new RandomAccessFile (file.i++){ obj. } catch(IOException ex){ ex. Multe din metodele claselor JDK sunt sincronizate (dar nu toate). De exemplu.Florian Moraru – Programare Orientata pe Obiecte class Fir extends Thread { SyncObject obj.

iar firul activ poate pierde controlul între apeluri de metode sincronizate: class Fir extends Thread { Vector c. totusi o secventã de apeluri de metode nu este automat sincronizatã.Florian Moraru – Programare Orientata pe Obiecte utilizate în comun.setElementAt(s. prin adãugarea unui caracter la fiecare sir din vector."doi". // continut final vector } } O clasã este sigurã într-un context cu fire concurente (“thread-safe”) dacã rezultatele metodelor sale sunt aceleasi indiferent din câte fire paralele sunt apelate aceste metode pentru aceleasi obiecte.i). // modifica sir din pozitia i sleep(10).println (c).sleep(100). colectie. Desi fiecare metodã din clasa Vector este sincronizatã.elementAt(i)). Douã fire pot executa în paralel secvente din douã metode sincronizate. Prin monitoare se asigurã accesul strict secvential al firelor concurente la operatii critice asociate unui obiect.asList(s)). De asemenea. Un monitor este un zãvor initial deschis si care se închide dupã ce un fir a apelat o metodã synchronized pentru obiectul respectiv. while ( f1.c=c.out. un fir poate executa o metodã sincronizatã si un alt fir o metodã nesincronizatã pentru un acelasi obiect.} catch (Exception e) {} System. Sincronizarea unei metode se poate exprima în douã moduri: synchronized void f() { … } sau: void f() { synchronized (this) { … } } Exemplul urmãtor aratã când poate fi necesarã sincronizarea unor secvente de instructiuni care opereazã asupra unui obiect.size().i<c. Douã procese modificã un acelasi vector. public void run () { for (int i=0. O altã denumire folositã este cea de “monitor” : fiecare obiect poate avea un monitor asociat (creat efectiv numai atunci când este necesar). f1.i++){ synchronized (c) { try { // pentru exceptii generate de “sleep” String s = (String) (c. Vector c= new Vector(Arrays. Fir f1 = new Fir ( c ). La crearea unui proces care foloseste o astfel de resursã comunã (variabilã.start(). } } class ModifyVector { public static void main (String args[]) { String s[]={"unu". 217 ."trei"}. Fir f2 = new Fir ( c ).isAlive() ) try {Thread. firul respectiv devine proprietarul monitorului pânã la terminarea metodei. din fire concurente.isAlive() && f2. fisier) se transmite adresa obiectului ce constituie resursa comunã. // firul activ pierde controlul s= i+" "+ s. // scoate sir din vector sleep(10). f2. // pune in pozitia i sirul modificat } catch(InterruptedException e) {} } } } public Fir ( Vector c) {this.start(). dar pentru obiecte diferite. // firul activ pierde controlul c. Un monitor este asociat unui obiect (sau unei clase) si nu unei metode.

join(). // cedare procesor catre t1 si t2 System. Metodele sincronizate au performante mai slabe decât metodele nesincronizate si de aceea nu toate clasele JDK sunt “thread-safe”. Exemplu: List safelist = Collections.i++) { System. mentionate în toate lucrãrile introductive.start(). Fir t2=new Fir(). System. Hashtable) au avut metode sincronizate. Pentru familiarizare cu problematica programãrii cu fire concurente în Java. t1.println (i + " " + getName()).start(). Probleme specifice programãrii cu fire concurente In afara erorilor de actualizare pot apare chiar blocãri definitive ale unor procese care se asteaptã reciproc pentru cã fiecare detine o resursã de care are nevoie celãlalt proces.println ("Pornire fire"). // asteapta semnal de la celalalt fir 218 . aceastã calitate a unei clase se obtine prin adãugarea atributului synchronized metodelor care modificã date din clasã.println ("Terminare fire").isAlive() && t2.isAlive()) Thread. dintre care mentionãm : . Metoda “start” pune un fir în coada firelor gata de executie (poate chiar în executie) si revine imediat în firul care a executat metoda “start”.Folosind metoda “isAlive” .out.out. dupã terminarea lor definitivã. fie nu transmite semnal de eliberare a resursei (prin “notify” sau “notifyAll”).i<10.out. t2.yield(). t2.out. Exemplu: // main lanseaza doua fire de tip “Fir” class Fir extends Thread { public synchronized void run () { for (int i=1. Acest tip de blocare (numit “starvation”) poate fi evitat prin programarea corectã a proceselor cu resurse comune. t2. iar procesul care detine resursa fie nu poate fi activat.println ("Pornire fire"). System. Sunt prevãzute însã metode de obtinere a unor clase colectie echivalente sincronizate. try { wait().join().start().out. vom începe cu câteva situatii mai usor de înteles si de tratat. t1. dar clasele colectie din Java 2 nu sunt implicit “thread-safe” din motive de performantã. Primele clase colectie (Vector. } // asteptare terminare cu metoda “join” public static void main (String [] arg) throws Exception { Fir t1=new Fir(). Uneori este nevoie ca firul “main” care a lansat alte fire sã afiseze rezultatele ce reprezintã efectul lor. System. t1.println ("Terminare fire").start(). while (t1.Folosind metoda “join” Exemplele urmãtoare ilustreazã cele douã posibilitãti: // asteptare terminare cu metoda “isAlive” public static void main (String [] arg) throws Exception { Fir t1=new Fir().Procesul asteaptã eliberarea unei resurse de cãtre un alt proces (prin “wait”). } Un proces poate fi blocat definitiv în câteva situatii.Florian Moraru – Programare Orientata pe Obiecte Practic. Fir t2=new Fir(). Avem cel putin douã variante ca firul “main” sã astepte terminarea celorlalte fire active: .synchronizedList (new ArrayList()).

In general folosim o variabilã comunã. 219 . // solutia 1: cu metoda “interrupt” // tratare evenimente de la butoane public void actionPerformed (ActionEvent ev) { Object source = ev.out.out.println(o1 + o2). în caz contrar butonul de stop nu poate fi actionat si deci operatorul nu poate transmite semnalul de oprire. de aceea trebuie luate mãsuri pentru evitarea blocãrii mutuale.println("STOP"). de obicei firul “pãrinte” terminã firul “fiu” pe care el l-a creat si lansat. // anunta celalalt fir ca poate continua } System. Blocarea reciprocã poate apare în cazul proceselor cu resurse comune si nu are solutie dupã ce s-a produs. } } . Acest tip de blocare reciprocã (numit “deadlock”) poate fi evitat prin impunerea unei discipline de acces la resursele comune (o a numitã ordine de ocupare si eliberare a resurselor comune). De observat cã trebuie sã existe cel putin douã fire de executie. ilustrate în douã exemple cu douã butoane Swing: un buton care porneste o activitate de lungã duratã (afisarea în mod text sau în mod grafic pentru a evidentia cum avanseazã firul) si un buton de oprire a activitãtii de afisare. Thread t1 = (new Thread() { public void run() { while (true) { synchronized (o1) { // pune zavor pe o1 yield(). } } } } }). iar firul pãrinte modificã starea variabilei comune când doreste terminarea firului fiu. // cedeaza controlul synchronized (o2) { // pune zavor pe o2 System. if (source==stop){ // buton de stop System.Florian Moraru – Programare Orientata pe Obiecte } catch (InterruptedException e) { } notify(). O altã situatie este aceea în care un fir trebuie sã termine executia unui alt fir. String o2 = "Step ".out. iar P2 asteaptã eliberarea unei alte resurse detinute de P1 (asteaptã semnal de la P1).out. } } } } }).getSource(). Avem cel putin douã solutii. Thread t2 = (new Thread() { public void run() { while (true) { synchronized (o2) { // pune zavor pe o2 yield().println (getName()+ " terminat"). Exemplu: String o1 = "Lock ". firul fiu testeazã periodic starea variabilei. la care au acces ambele fire. // cedeaza controlul synchronized (o1) { // pune zavor pe o1 System.println(o2 + o1).Un proces P1 asteaptã eliberarea unei resurse (asteaptã un semnal) de la un alt proces P2.

start().out.Secventa de activare a mai multor fire nu este dictatã strict de prioritatea lor. } System.").exit(0). mark=true. pentru cã poate conduce la blocare.println("STOP"). iar în cazul firelor cu aceeasi prioritate nu se poate anticipa ordinea de lansare. yield().getSource(). } // camp text cu nume fisiere if (source==start){ // buton de start Thread t=new Thread() { public void run(){ while (!mark){ System. dar operatiile de intrareiesire nu trebuie sã aparã în metode sincrinizate. } }. if (source==stop){ // buton de stop System. nu se poate preciza care dintre ele este scos din asteptare de executia metodei “notify”. Fire de tip producãtor-consumator Putem deosebi douã situatii de utilizare a unei resurse comune de cãtre douã fire concurente: 220 . metoda “notifyAll” scoate toate firele în asteptare din starea “wait” si le face gata de executie.Nici metoda “yield” nu trebuie folositã în metode sincronizate.print(". dacã este nevoie ca alte fire sã termine fortat aceste operatii de I/E. . t. t.out. ."). } // camp text cu nume fisiere if (source==start){ // buton de start t=new Thread() { // creare fir separat ptr activitatea de durata public void run(){ while (!mark){ // continua cat timp mark este “false” System.Metodele “wait” si “notify” pot fi folosite numai în blocuri sincronizate . t.Dacã sunt mai multe fire care asteaptã producerea unui eveniment cu metoda “wait”.out.exit(0). // poate lipsi daca terminarea are loc la inchiderea ferestrei } }.interrupt().Florian Moraru – Programare Orientata pe Obiecte mark=true.start().print(". . } } // mark este initial “false” // cere intreruperea firului t Vom mentiona si alte aspecte legate de efectul metodelor Java folosite în programele cu fire si care pot condice la situatii greu de explicat: . // afiseaza puncte } System. } } // solutia2 : firul de durata cedeaza periodic controlul firului in care se // trateaza evenimentele de la componentele Swing public void actionPerformed (ActionEvent ev) { Object source = ev.

put(i).random() * 100)). i < 10. i++) { comun. try {sleep((int)(Math. Variabila “full” este necesarã pentru a arãta când poate fi modificat (full=false) si când poate fi folosit (full=true) continutul variabilei comune “contents”.out. care blocheazã si deblocheazã accesul altor procese la variabila comunã pe durata acestor operatii.out.Ordinea în care firele acceseazã resursa este importantã.Ordinea accesului la resursã nu este importantã. } public void run() { for (int i = 0.println("Consumator scoate " + x). } catch (InterruptedException e) { } contents = value. Sectiunile critice sunt metodele “get” si “put” din clasa “Buffer”. i < 10. // continut variabila comuna private boolean full = false. } // scrie o valoare in variabila comuna public synchronized void put(int value) { while ( full) try { wait(). } } } // clasa pentru obiecte cu date comune class Buffer { private int contents. } catch (InterruptedException e) {} } } } class Consumer extends Thread { // proces consumator private Buffer comun. In exemplul urmãtor producãtorul “produce” numere întregi consecutive (la intervale de timp generate aleator) si le depune într-o variabilã (dintr-un obiect). i++) { int x= comun. pentru cã ele îsi transmit date prin intermediul resursei (fire de tip producãtor-consumator sau emitãtor-receptor). iar consumatorul preia aceste numere din variabila comunã si le afiseazã pentru a ne permite sã verificãm dacã toate numerele transmise au ajuns fãrã pierderi sau repetãri si în ordinea corectã la consumator. notifyAll(). return contents.Florian Moraru – Programare Orientata pe Obiecte . . public Consumer (Buffer c) { comun = c.println("Producator " + " pune " + i). // indicator de stare buffer (plin/gol) // citeste continut variabila comuna public synchronized int get() { while ( ! full) try { wait().get(). public Producer (Buffer c) { comun = c. Situatia transmiterii de date între douã procese paralele este schematizatã de obicei sub forma unui cuplu de procese numite “producãtor” si “consumator” (de mesaje). notifyAll(). // folosire (consumare) date System. 221 . } public void run() { for (int i = 0. // producere de date System. dar în timp ce un fir foloseste resursa nu rebuie permis celuilalt fir accesul la resursã. full = true. } catch (InterruptedException e) { } full = false. class Producer extends Thread { // proces producator private Buffer comun.

t2.random())).} catch(IOException e){} // pune caracter in flux try { Thread.io” permit comunicarea simplã de date între procese de tip producãtor si consumator printr-un “flux” de date. Clasele PipedWriter (PipedOutputStream) si PipedReader (PipedInputStream) din pachetul “java.print(c +" "). // conectare citire cu scriere // sau rd= new PipedReader() si rd.Florian Moraru – Programare Orientata pe Obiecte } } Metodele “wait”.} catch (Exception e) { } } } } // Consumator class Consumer implements Runnable { private PipedReader buffer.start(). pentru a permite altor fire sã modifice starea obiectului. în caz contrar apare o exceptie cauzatã de efectul acestor metode: “wait” elibereazã monitorul asociat obiectului respectiv. “notify” si “notifyAll” pot fi apelate numai în metode sincronizate. // Producator class Producer implements Runnable { private PipedWriter buffer. sau se foloseste metoda “connect”. public Producer(PipedWriter b) { buffer = b.write(c).random())). Cuplarea canalului de scriere (PipedWriter) cu canalul de citire (PipedReader) se poate face în douã feluri: la construirea obiectului PipedReader se transmite ca parametru o variabilã de tip PipedWriter. // canal de scriere PipedReader rd = new PipedReader(wr). c++) { try {buffer.out. Operatiile de acces la date sunt sincronizate iar gestiunea zonei comune de date (o coadã) nu cade în sarcina programatorului.start(). } } Incepând cu Java 5 s-au introdus câteva interfete si clase pentru cozi sincronizate. Exemplu de utilizare: 222 . } public void run() { for (char c='0'.} catch (Exception e) { } try { while ( (c= (char)buffer. try { Thread. public Consumer(PipedReader b) { buffer = b. Thread t2 = new Thread ( new Consumer (rd)). c<='z'. } public void run() { char c.read()) > 0) // citire caracter din flux System.sleep((int)(100*Math. care permit comunicarea mai simplã între procese de tip producãtor-consumator: interfata BlockingQueue si clasele ArrayBlockingQueue si LinkedBlockingQueue. dar ideea este cã operatiile de introducere în buffer si de extragere din buffer trebuie “sincronizate” pentru executarea lor strict secventialã în procese diferite. t1.sleep((int)(100*Math. // si afisare pe ecran } catch (IOException e) {} } } // Program de test class Pipes { public static void main ( String args[])throws IOException { PipedWriter wr = new PipedWriter(). De obicei comunicarea între procese (fire) se face printr-o coadã (un “buffer”).connect(wr) Thread t1 = new Thread ( new Producer (wr)).

class Producer implements Runnable { private final BlockingQueue queue. modificând timpii folositi în metoda “sleep” 223 .isAlive() && c. } public void run() { try { while (true) { consume(x=(Integer)queue.out.*.sleep(100). try { Thread. } catch (InterruptedException ex) { } } Object produce() { try { Thread. Thread. x=0. Thread c = new Thread (new Consumer(q)).out. } catch (InterruptedException e) { } System.util. public Consumer(BlockingQueue q) { queue = q. public Producer(BlockingQueue q) { queue = q.yield(). private int x. p. } } class Consumer implements Runnable { private final BlockingQueue queue.println(x+ " "+ Thread. Thread.currentThread().println( Thread.yield().getName()).isAlive()){ System. x++.sleep(10). c.start().sleep(150). while ( p.take()).out.concurrent.currentThread(). } public void run() { try { for (int i=0. return x.i++) queue.println(q). } catch (InterruptedException e) { } } } class Setup { public static void main(String a[]) { BlockingQueue q = new ArrayBlockingQueue(100).put(produce()). } } catch (InterruptedException ex) { } } void consume(Object x) { System. // afisare continut coada try { Thread. private int x. Thread p = new Thread(new Producer(q)).i<9.currentThread(). } catch (InterruptedException e) { } } } } Cu programele anterioare se poate experimenta.start().getName()).currentThread().Florian Moraru – Programare Orientata pe Obiecte import java.

stop. // sir continut de fisierele cautate private JButton stop. Exemplul urmãtor foloseste un singur fir de executie (în care se trateazã evenimentele Swing).invokeLater (new Runnable() { public void run() { new MyFrame (). add (text."South"). // pentru oprire cautare private JTextArea area.Metodele claselor Swing nu sunt sincronizate (nu sunt asigurate la apeluri din fire de executie concurente). stop = new JButton("Stop")."North"). prin intermediul metodei “invokeLater”: public static void main (String args[]) { SwingUtilities. care poate fi observatã în toate exemplele Swing din Java Tutorial: obiectul GUI (dintr-o clasã derivatã din JFrame) nu este creat direct în “main” ci într-un alt fir de executie creat de “main”.Interfata graficã ar putea “îngheta” (nu mai rãspunde la gesturile operatorului) dacã tratarea unui eveniment este o actiune de duratã si se petrece tot în firul de executie cu tratarea evenimentelor (cu metode de tip “actionPerformed”). add(stop. operatie care poate dura foarte mult pentru cã poate implica si citirea continutului unui numãr mare de fisiere în care se cautã un sir dat. "Center"). add(new JScrollPane(area). text = new JTextField(10). Firul în care se trateazã evenimentul de la butonul de oprire cere terminarea firului creat si lansat de butonul “Start”.addActionListener(this).addActionListener(this). Astfel de activitãti trebuie delegate altor fire de executie. Pentru a ilustra problema “înghetãrii” interfetei grafice vom considera cazul operatiei “Search” din Windows Explorer: dupã alegerea unor parametri de cãutare se lanseazã operatia de cãutare (printr-un buton “Search”). 224 . } Efectul metodei “invokeLater” este crearea unui obiect Thread si plasarea lui într-o coadã astfel ca firele pentru mai multe interfete grafice sã se execute succesiv si într-un fir separat de firul “main”. pack().EXIT_ON_CLOSE).50). Operatorul are posibilitatea sã termine fortat procesul de cãutare prin actionarea unui buton “Stop”. // pentru afisare nume fisiere gasite private boolean end=false. text. show(). } }). Fire de executie în aplicatii Swing Aplicatiile Swing ridicã douã probleme din punct de vedere al firelor de executie: . iar operatia de cãutare nu poate fi opritã doarece butonul de Stop nu poate fi actionat: class Search extends JFrame implements ActionListener { // obiecte vizuale din GUI private JTextField text. adicã ele trebuie tratate numai în ordinea în care s-au produs aceste evenimente. . Pentru prima problemã existã o solutie simplã. // variabila ptr comunicare intre activitati // initializari public Search () { area = new JTextArea (30. Pentru ca butonul “Stop” sã poatã fi actionat si sã producã terminarea cãutãrii este necesar ca actiunea de cãutare sã se producã într-un alt fir de executie decât firul evenimentelor Swing (“EventThread”). iar tratarea evenimentelor Swing trebuie sã se facã într-un acelasi fir de executie.Florian Moraru – Programare Orientata pe Obiecte din procesele producãtor si consumator si observând continutul cozii de mesaje. setDefaultCloseOperation (JFrame.

txt.v). // fisier gasit else area. eventual si numele unui director unde se cautã). if (source==stop) // buton de stop end=true. } }). 225 .listFiles(). i < files. area.getText(). // afisare simpla continut vector } } // functie de cautare void findAll (File dir. // cautare recursiva in subdirectoare else if ( file.add(file. Object source = ev. File[] files=dir. i++) { File file = files[i].toString()). } } Solutia problemei este crearea si lansarea unui alt fir ca urmare a evenimentului de câmp text (când se aflã numele sirului cãutat.append(".getSource().invokeLater(new Runnable() { public void run() { new Search().Florian Moraru – Programare Orientata pe Obiecte } // ascultatori la evenimente public void actionPerformed (ActionEvent ev) { Vector v= new Vector(). if (file. \n"). for (int i = 0.isDirectory()) findAll (file. // camp text cu nume fisiere if (source==text) { // nume fisiere cautate findAll (new File("C:/").contains(txt)) v.getName()+"\n").String txt. // fisier care nu satisface conditia } } public static void main (String args[]) { SwingUtilities.v).text.getName().Vector v) { if (end) return.append(v.length.

You're Reading a Free Preview

Descarga
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->