0% au considerat acest document util (0 voturi)
126 vizualizări51 pagini

Java

Documentul prezintă principalele concepte ale programării orientate pe obiecte în Java, inclusiv clase, obiecte, derivare, interfete, colecții și programare bazată pe evenimente.

Încărcat de

Andrei Neacsiu
Drepturi de autor
© © All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca DOCX, PDF, TXT sau citiți online pe Scribd
0% au considerat acest document util (0 voturi)
126 vizualizări51 pagini

Java

Documentul prezintă principalele concepte ale programării orientate pe obiecte în Java, inclusiv clase, obiecte, derivare, interfete, colecții și programare bazată pe evenimente.

Încărcat de

Andrei Neacsiu
Drepturi de autor
© © All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca DOCX, PDF, TXT sau citiți online pe Scribd
Sunteți pe pagina 1/ 51

CUPRINS 1.

Java ca limbaj de programare cu obiecte Diferente ntre limbajele Java si


C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tipuri clas si tipuri
referint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Structura programelor
Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Spatii ale numelor n
Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Definirea si utilizarea de
vectori n Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exceptii program n Java . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Biblioteci de clase
Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2. Introducere n
programarea orientat pe obiecte Clase si obiecte . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . Clasele sunt module de program
reutilizabile . . . . . . . . . . . . . . . . . . . . . . . . . Clasele creeaz noi tipuri de
date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clasele permit programarea
generic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clasele creeaz un model pentru
universul aplicatiei . . . . . . . . . . . . . . . . . . . 3. Utilizarea de clase si obiecte n Java
Clase fr obiecte. Metode statice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Clase instantiabile. Metode aplicabile obiectelor . . . . . . . . . . . . . . . . . . . . . . . .
Variabile referint la un tip clas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Argumente de functii de tip referint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Clase cu obiecte nemodificabile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Clonarea obiectelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Obiecte Java n faza de executie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.
Definirea de noi clase n Java Definirea unei clase n Java . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . Functii constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . Variabila "this" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . Atribute ale membrilor claselor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . Incapsularea datelor n clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Structura unei clase Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Metode care pot genera exceptii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.
Derivare. Mostenire. Polimorfism Clase derivate . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . Derivare pentru
mostenire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6. Clase abstracte si interfete
Interfete Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Interfete fr functii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Compararea de obiecte n Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Interfete pentru functii de filtrare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Clase abstracte n Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Clase abstracte si interfete pentru operatii de I/E . . . . . . . . . . . . . . . . . . . . . . . . . . 7.
Colectii de obiecte n Java Familia claselor colectie . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . Multimi de obiecte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . Liste

secventiale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clase
dictionar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Colectii
ordonate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clase
iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Definirea de
noi clase colectie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clase
sablon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.
Reutilizarea codului n POO Reutilizarea codului prin
compunere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reutilizarea codului prin
derivare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Comparatie ntre compozitie
si derivare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mostenire multipl prin
compozitie si derivare . . . . . . . . . . . . . . . . . . . . . . . . . . Combinarea compozitiei cu
derivarea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9. Clase incluse Clase
incluse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clase
interioare cu nume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Simplificarea comunicrii ntre clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Clase interioare cu date comune . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Clase interioare anonime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Probleme asociate claselor incluse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.
Clase pentru o interfat grafic Programarea unei interfete grafice . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . Clase JFC pentru interfata
grafic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dispunerea componentelor
ntr-un panou . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Componente vizuale cu
text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Panouri multiple . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Apleti
Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.
Programare bazat pe evenimente Evenimente Swing . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . Tratarea evenimentelopr Swing . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . Evenimente de mouse si de tastatur . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . Evenimente asociate componentelor
JFC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . Evenimente produse de componente cu
text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mecanismul de generare a
evenimentelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Structura programelor dirijate
de evenimente . . . . . . . . . . . . . . . . . . . . . . . . . . . . Utilizarea de clase
interioare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clase generator si
receptor de evenimente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reducerea
cuplajului dintre clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.
Componente Swing cu model Comunicarea prin evenimente si clase "model" . . . . . .
. . . . . . . . . . . . . . . . . . . . Arhitectura MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . Utilizarea unui model de
list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Familii deschise de clase n
JFC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Utilizarea unui model de tabel .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Utilizarea unui model de
arbore. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13. Java si XML Fisiere XML
n aplicatii Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XML si

orientarea pe obiecte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Utilizarea


unui parser SAX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Utilizarea
unui parser DOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.
Proiectare orientat pe obiecte Proiectarea orientat pe
obiecte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Scheme de proiectare . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Metode "fabric" de
obiecte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clase observatorobservat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clase model n
schema MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Refactorizare n
POO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anexa A. Exemplu
de Framework: JUnit Anexa B. Dezvoltarea de aplicatii Java Comenzi de compilare si
executie. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fisiere de comenzi Medii
integrate de dezvoltare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Medii
vizuale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anexa C.
Versiuni ale limbajului Java Principalele versiuni ale limbajului
Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Noutti n versiunea 1.2 . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Noutti n versiunea
1.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Noutti n versiunea
1.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Probleme
propuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 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 putin si sintaxa
unor instructiuni. 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 aprut, n plus, comentarii care ncep prin perechea de
caractere "//" si se termin la sfrsitul liniei n care apare acel comentariu. Java
preia aceste dou feluri de comentarii, la care se adaug comentarii destinate
generrii 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 implementrile 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 oricrui alfabet. Toate tipurile aritmetice din Java reprezint
numere cu semn si nu mai exist cuvntul unsigned pentru declararea de variabile
aritmetice fr semn. Tipul boolean din Java ocup un singur bit; constantele de tip
boolean sunt true si false. Existenta acestui tip modific si 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 urmtoare sunt
gresite sintactic n Java, desi sunt corecte n C si C++. while ( d++ = s++) ; //
corect este : while ( (d++=s++) !=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. 2 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 Tipul char nu este un tip aritmetic dar se pot face conversii prin
operatorul (tip) ntre tipul char si orice tip aritmetic ntreg. Exemplu: byte b=65;
char ch; ch =(char)b; // ch este 'A' ch='\n'; b =(byte)ch; // b este 10 Aceleasi reguli
de conversie ntre tipuri numerice se aplic si ntre argumentele efective si
argumentele formale, deoarece compilatorul face automat o atribuire a valorii
argumentului efectiv la argumentul formal corespunztor. Exemplu: double r =
Math.sqrt(2); // promovare de la int la double ptr. 2 O alt conversie automat, de
promovare se face pentru rezultatul unei functii, dac tipul expresiei din
instructiunea return difer de tipul declarat al functiei. Exemplu: static float rest
(float a, float b) { int r = (int)a % (int) b; return r; } 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. Exemple : f=
(float)d; // cu pierdere de precizie n=(int)f; // cu trunchiere int r = (int)
Math.sqrt(4); // conversie necesara de la double la int // functie de rotunjire din clasa
Math public static int round (float a) { return (int)floor(a + 0.5f); // "floor" are
rezultat double } Compilatorul Java verific dac este specificat un rezultat la orice
iesire posibil dintr-o functie si nu permite instructiuni if fr else n functii cu tip
diferit de void. 3 In Java nu exist operatorul sizeof din C, pentru determinarea
memoriei ocupate de un tip sau de o variabil, pentru c nu este necesar acest
operator. Cea mai important diferent dintre Java, pe de o parte, si limbajele C, C+
+ pe de alt parte, este absenta tipurilor pointer din Java. Deci nu exist
posibilitatea de a declara explicit variabile pointer si nici operatorii unari & (pentru
obtinerea adresei unei variabile) si * (indirectare printr-un pointer). Operatorul new
pentru alocare dinamic din C++ exist n Java, dar are ca rezultat o referint si nu
un pointer. Supradefinirea functiilor Supradefinirea sau suprancrcarea 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 diferite ntr-o
aceeasi clas. Pot exista functii cu acelasi nume (eventual si cu acelasi argumente
si tip) n clase diferite, dar acesta nu este un caz de supradefinire, fiindc ele se afl
n spatii de nume diferite. In Java, ca si n C++, o functie este deosebit de alte
functii din aceeasi clas (de ctre compilator) prin "semntura" sa (prin "amprenta"
functiei), care este format din numele, tipul si argumentele functiei. Un exemplu
uzual de functii supradefinite este cel al functiilor de afisare la consol n mod text

print si println, care au mai multe definitii, pentru fiecare tip de date primitiv si
pentru tipurile clas String si Object : // din pachetul java.io public class
PrintStream ...{ // este o clas derivat public void print (int i) { // scrie un ntreg
write (String.valueOf(i)); } public void print (float f) { // scrie un numr real write
(String.valueOf(f)); } public void print (boolean b) { // scrie un boolean write (b ?
true : false); } public void print (String s) { // scrie un sir de caractere if (s==
null) s= null; write (s); } Functia String.valueOf este si ea supradefinit pentru
diferite argumente. Declaratii de variabile O declaratie de variabil poate s apar
fie ntr-o functie, fie n afara functiilor, dar ntr-o clas; nu exist variabile externe
claselor. Locul declaratiei este important : o variabil dintr-o functie este local
acelei functii, iar o variabil declarat la nivel de clas este utilizabil de orice
functie din clas (si chiar de functii din alte clase). In C toate declaratiile dintr-un
bloc trebuie s precead prima instructiune executabil din acel bloc. In C++ si n
Java o declaratie poate apare oriunde ntr-un 4 bloc, ntre alte instructiuni sau
declaratii. Domeniul de valabilitate al unei variabile ncepe n momentul declarrii si
se termin la sfrsitul blocului ce contine declaratia. Instructiunea for constituie un
caz special: variabila contor se declar de obicei n instructiunea for, iar
valabilitatea acestei declaratii este limitat la instructiunile repetate prin
instructiunea for . Exemplu: public static boolean este ( int x[ ], int y) { int
n=x.length; // lungime vector x for (int k=0; k<=10;i++) // scrie 10 numere
aleatoare System.out.println ( rand.nextFloat()); } Variabila cu numele "rand" este
de tipul Random, iar clasa Random este definit n pachetul "java.util". Notatia
"rand.nextFloat()" exprim apelul metodei "nextFloat" din clasa "Random" pentru
obiectul adresat de variabila "rand". Instructiunea import permite simplificarea
referirilor la clase din alte pachete si poate avea mai multe forme. Cea mai folosit
form este: import pachet.* ; Instructiunea anterioar permite folosirea numelor
tuturor claselor dintr-un pachet cu numele "pachet", fr a mai fi precedate de
numele pachetului. Exemplul urmtor ilustreaz folosirea instructiunii "import":
import java.util.*; // sau import java.util.Random; class R { public static void main
(String arg[]) { Random rand =new Random(); for (int i=1;i<=10;i++) // scrie 10
numere aleatoare System.out.println ( rand.nextFloat()); } } Uneori se prefer
importul de clase individuale, att pentru documentare ct si pentru evitarea
ambiguittilor create de clase cu acelasi nume din pachete diferite. Exemplu care
arat riscurile importului tuturor claselor dintr-un pachet: import java.util.*; import
java.awt.*; class test { public static void main (String av[ ]) { List list; . . . // clasa
java.awt.List sau interfata java.util.List ? } } Definirea si utilizarea de vectori n Java
10 Cuvntul vector este folosit aici ca echivalent pentru array din limba englez
si se refer la un tip de date implicit limbajelor C, 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.
In Java, declararea unei variabile (sau unui parametru formal) de un tip vector se
poate face n dou moduri, echivalente: tip nume [ ]; // la fel ca in C si C++ tip [ ]
nume; // specific Java Declararea matricelor (vectori de vectori) poate avea si ea
dou forme. Exemplu: int a[ ][ ] ; // o matrice de ntregi int [ ][ ] b; // alt matrice de

ntregi In Java nu este permis specificarea unor dimensiuni la declararea unor


vectori sau matrice, deoarece alocarea de memorie nu se face niciodat la
compilare. Exemplu: int a[100]; // eroare sintactic ! O variabil vector este automat
n Java o variabil referint iar memoria trebuie alocat dinamic pentru orice vector.
Alocarea de memorie pentru un vector se face folosind operatorul new ,urmat de un
nume de tip si de o expresie (cu rezultat ntreg) ntre paranteze drepte; expresia
determin numrul de componente (nu de octeti !) pe care le poate contine
vectorul. Exemple: float x[ ] = new float [10]; // aloca memorie ptr 10 reali int n=10;
byte[ ][ ] graf = new byte [n][n]; Este posibil si o alocare automat, atunci cnd
vectorul este initializat la declarare cu un sir de valori. Exemplu: short prime[ ] =
{1,2,3,5,7}; In lipsa unei initializri explicite, componentele unui vector sunt
initializate automat, cu valori ce depind de tipul lor: zerouri pentru elemente
numerice, null pentru variabile referint de orice tip. Un vector intrinsec cu
componente de un anumit tip este considerat ca un obiect de un tip clas, tip
recunoscut de compilator dar care nu este definit explicit n nici un pachet de clase.
Numele acestor clase este format din caracterul [ urmat de o liter ce depinde de
tipul componentelor vectorului: [I pentru int[], [B pentru byte[], [Z pentru boolean[],
[C pentru char[], [F pentru float[] s.a.m.d. Variabila predefinit cu numele length
poate fi folosit ( ca membru al claselor vector ) pentru a obtine dimensiunea
alocat pentru un vector. Exemplu: // functie de copiere a unui vector public static
void copyVec ( int a [ ] ,int b[ ] ) { for (int i=0;i < a.length; i++) // a.length
=dimensiune vector a b[i] = a[i]; 11 } De retinut c length este dimensiunea
alocat si nu dimensiunea efectiv a unui vector, deci numrul de elemente din
vector trebuie transmis ca argument la functii. In Java, se verific automat, la
executie, ncadrarea indicilor ntre limitele declarate; iesirea din limite produce o
exceptie si terminarea programului. Exemplu: int [ ] a= new int [10]; for (int
i=1;i<=10;i++) a[i]=i; // exceptie la a[10]=10 Numerotarea componentelor unui
vector este de la zero la (length-1), deci n exemplul anterior se produce exceptia de
depsire a limitelor la valoarea i=10 . Variabila length nu trebuie confundat cu
metoda "length()" din clasa String. Functia urmtoare determin sirul cel mai lung
dintr-un vector de siruri: static String maxLen ( String v[ ]) { String max =v[0]; for
(int i=0;i< v[i].length() ) // compara lungimile a doua siruri max=v[i]; // retine
adresa sirului cel mai lung return max; } O matrice este privit si n Java ca un
vector de vectori, iar variabila "length" se poate folosi pentru fiecare linie din
matrice. Deoarece orice matrice este alocat dinamic, nu exist probleme la
transmiterea unei matrice ca argument la o functie. Nu este necesar transmiterea
dimensiunilor matricei ca argumente la functia apelat. Exemplu: class Matrice
{ public static void printmat (int a[ ][ ]) { // functie de afisare matrice for (int
i=0;i=0 ) { s = s.substring(0,p)+ s2 + s.substring(p+s1.length()); p=s.indexOf(s1,
p+s2.length()); } return s; } Utilizatorii si pot defini propriile clase, pentru tipuri de
date necesare aplicatiilor; de exemplu, putem defini o clas BoolMatrix pentru o
matrice cu elemente de tip boolean. In Java orice clas este automat derivat dintro clas generic Object si, ca urmare, trebuie s redefineasc anumite metode
mostenite: toString, equals s.a. Exemplu de clas minimal pentru o matrice de

biti: // matrice cu elemente de tip boolean public class BoolMatrix { private boolean
a[ ][ ]; // o matrice patratica private int n; // nr de linii si coloane // constructor de
obiecte public BoolMatrix (int n) { this.n=n; a= new boolean[n][n]; // aloca memorie
ptr matrice } // modifica valoare element public void setElement (int i,int j, boolean
b) { a[i][j]=b; } // citire valoare element public boolean getElement (int i,int j)
{ return a[i][j]; } // sir cu elementele din matrice public String toString () { String
s=""; 8 for (int i=0;i to || from < 0 || to > s.length() ) return s; // s nemodificat !
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
transformri pot fi necesare pentru c n clasa StringBuffer nu se regsesc toate
metodele din clasa String. De exemplu, metodele indexOf si lastIndexOf pentru
determinarea pozitiei unui caracter sau unui subsir ntr-un sir (supradefinite n clasa
String) nu exist n clasa StringBuffer. 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). Operatii cu siruri de caractere
Operatiile cu siruri sunt prezente n multe aplicatii Java si pot ilustra utilizarea de
metode ale obiectelor si de metode ale claselor (statice). O problem uzual n
programare este extragerea de cuvinte (tokens), ce pot fi separate ntre ele prin
unul sau mai multe caractere cu rol de separator, dintr-un text dat. Solutia uzual
creeaz un obiect analizor lexical (din clasa StringTokenizer) si apeleaz metode ale
acestui obiect ("nextToken" = urmtorul cuvnt): import java.util.* ; class Tokens
{ public static void main ( String[ ] args) { String text = new String ("unu doi, trei.
patru; cinci"); // sirul analizat String tokens[ ] = new String[100]; // vector de cuvinte
StringTokenizer st = new StringTokenizer (text, " ,;.\t\n"); // separatori int k=0; //
numara cuvinte while (st.hasMoreTokens()) { // daca mai sunt cuvinte in sirul
analizat String token = st.nextToken(); // extrage urmatorul cuvant din linie
tokens[k++]=token; // memoreaza cuvant } } } 10 10 Solutia Java 1.4 foloseste
expresii regulate (metoda "split") pentru analiza textului: class Tokens { public static
void main ( String[ ] args) { String text = new String ("unu doi, trei. patru; cinci"); //
sirul analizat String tokens[] = text.split ("[,;.\t\n]+"); // argument expresie
regulat } } O expresie regulat ("regular expression") este un sir de caractere cu
rol de sablon ("pattern") pentru o multime de siruri care se "potrivesc" cu acel
sablon. Expresiile regulate permit cutarea de siruri, nlocuirea de siruri si
extragerea de subsiruri dintrun text (textul este obiect de tip "String"). Majoritatea
operatiilor care folosesc expresii regulate se pot realiza n dou moduri: a) Folosind
metode noi din clasa "String". b) Folosind metode ale claselor "Pattern" si "Matcher"
din "java.util.regex". Principalele metode din clasa "String" pentru lucrul cu expresii
regulate sunt: public boolean matches(String regex): public String[] split(String
regex) public String replaceFirst(String regex, String replacement) public String
replaceAll(String regex, String replacement) Metoda "matches" are rezultat "true"
daca sirul pentru care se aplic metoda se potriveste cu sablonul "regex". Metoda

"split" creeaz un vector cu toate sirurile extrase din textul pentru care se aplic
metoda, siruri separate ntre ele prin siruri care se potrivesc cu sablonul "regex".
Metode mai importante din clasa Pattern: static Pattern Pattern.compile(String
regex); boolean matches(String regex, String text); String[] split(String text);
Metoda "compile" construieste un obiect de tip "Pattern" si retine sablonul primit
ntr-un format intern (compilat). Metodele "matches" si "split" din clasa "Pattern" au
acelasi efect ca si metodele cu acelasi nume din clasa "String". Exemplu: String text
= new String ("unu doi, trei. patru; cinci"); // sirul analizat Pattern p =
Pattern.compile ("[,;.\t \n]+"); String tokens[] = p.split (text); // argument expresie
regulat Clasa Matcher contine metode pentru cutare (find), potrivire (match),
nlocuire (replaceAll, replaceFirst) s.a. un obiect de tipul Matcher se obtine apelnd
metoda matcher pentru un obiect Pattern. Exemplu de eliminare a comentariilor
care ncep cu caracterele // dintr-un text: String regex="//.+\\n" // o expresie
regulat 11 11 String text="linia1 // unu \nlinia2 // doi "; Matcher m =
Pattern.compile(regex).matcher(text); String text2= m.replaceAll("\n"); // eliminare
comentarii In general, operatiile pe siruri se poate realiza mai compact cu expresii
regulate, dar trebuie stpnite regulile de formare ale acestor sabloane. Exemplu de
eliminare a marcajelor HTML sau XML (tags) dintr-un text: String text=" 111 222
333 " // sir cu marcaje // fara expresii regulate textb = new StringBuffer(text); while
((p1=text.indexOf('<',p2)) >=0 ) { // p1=pozitia car. 0) // p2 = pozitia car. >
textb.delete(p1,p2+1); } // cu expresii regulate String regex="<[^<>]*>"; // orice
sir incadrat de < si > String text2= text.replaceAll(regex,""); Clase si obiecte Java n
faza de executie Pentru fiecare clas ncrcat n masina virtual Java este creat
automat cte 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 cuvntul 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(); 12 12 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 cmpurilor (variabilelor clasei), numele metodelor clasei, formele
functiilor constructor s.a. Metoda getName are ca rezultat un sir ce reprezint

numele clasei al crui tip este continut ntr-un obiect Class. Exemplul urmtor 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 fr argumente . Exemplu: 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, cunoscnd tipul argumentelor, se pot fabrica
obiecte si apelnd 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 propriettile si metodele proprii unor obiecte, s modifice propriettile
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. 4. Definirea de noi clase Definirea
unei clase n Java Definirea unei clase se face prin definirea variabilelor si functiilor
clasei. In Java toate metodele trebuie definite (si nu doar declarate) n cadrul clasei.
Ordinea n care sunt definiti membrii unei clase (date si functii) este indiferent.
Este posibil ca o metod s apeleze o alt metod definit ulterior, n aceeasi clas.
Datele clasei se declar n afara metodelor clasei si vor fi prezente n fiecare obiect
al clasei. Ele sunt necesare mai multor metode si sunt de obicei putine. Majoritatea
claselor instantiabile grupeaz mai multe functii n jurul unor date comune. Ca
exemplu vom schita o definitie posibil pentru o clas ale crei obiecte sunt numere
complexe (nu exist o astfel de clas predefinit n bibliotecile JDK): public class
Complex { // datele clasei private double re,im; // parte real si parte imaginar //
metode ale clasei // adunare de numere complexe public void add ( Complex cpx)
{ re += cpx.re; im += cpx.im; } // scadere de numere complexe public void sub
( Complex cpx) { re = re - cpx.re; im = im - cpx.im; } } Se observ c metodele
unei clase au putine argumente, iar aceste argumente nu se modific, ceea ce est
tipic pentru multe clase cu date: metodele clasei au ca efect modificarea datelor
clasei, iar argumentele sunt de obicei date initiale necesare pentru operatiile cu
variabilele clasei. Acesta este si un avantaj al programrii cu clase fat de
programarea procedural: functii cu argumente putine si nemodificabile. Clasele de
uz general se declar public pentru a fi accesibile din orice alt pachet. Toate
variabilele numerice ale unei clase sunt initializate implicit cu zerouri si toate
variabile referint sunt initializate cu null. Variabilele de interes numai pentru
anumite metode vor fi definite ca variabile locale n functii si nu ca variabile ale

clasei. Exemplu de variabil local unei metode: public Complex conj ( ) { Complex
c = new Complex(); // c este o variabil local c.re= re; c.im= -im; return c; } 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. In Java clasele cu date nu contin metode de
afisare pe ecran a datelor din obiectele clasei, 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.out.println) sau introdus ntr-un alt flux de
date sau folosit de alte metode. Functia urmtoare (metod a clasei "Complex" )
trebuie inclus n definitia clasei: // conversie n sir, pentru afisare public String
toString ( ) { return ( "(" + re+ "," + im+ ")"); } Existenta metodei "toString" ne
permite s afism direct continutul unui obiect din clasa "Complex" astfel: Complex
c = new Complex(); System.out.println (c); // scrie (0,0) In plus, se poate afisa
simplu continutul unei colectii de obiecte "Complex", pentru c metoda "toString" a
colectiei apeleaz metoda "toString" a obiectelor din colectie. Redefinirea metodei
"equals" n clasa "Complex" permite cutarea unui obiect dat ntr-o colectie si alte
operatii care necesit comparatia la egalitate. Exemplu: public boolean equals
(Object obj) { Complex cobj = (Complex) obj; return re==cobj.re &&
im==cobj.im; } Functii constructor Orice clas instantiabil are cel putin un
constructor public, definit implicit sau explicit si apelat de operatorul new la crearea
de noi obiecte. Un constructor este o functie fr tip si care are obligatoriu numele
clasei din care face parte. Constructorii nu sunt considerati metode, deoarece nu
pot fi apelati explicit (prin nume). Variabilele oricrei clase sunt initializate automat
la ncrcarea clasei (cu valori zero pentru variabile numerice si null pentru variabile
de orice tip clas). Aceste valori sunt preluate de fiecare obiect creat, dac nu se fac
alte initializri prin constructori. Pentru a permite initializarea variabilelor clasei n
mod diferit pentru fiecare obiect creat exist n clasele ce contin date unul sau mai
multi constructori. Exemplu: public class Complex { private double re, im; //
re=parte real ,im= parte imaginar public Complex ( double x, double y) { re=x;
im=y; } . . . // metode ale clasei } Exemple de creare a unor obiecte de tipul
Complex : Complex c1 = new Complex (2,3) , c2= c1.conj(); // c2 =(2,-3) Dac nu
se defineste nici un constructor atunci compilatorul genereaz automat un
constructor implicit, fr argumente si fr efect asupra variabilelor clasei (dar care
apeleaz constructorul superclasei din care este derivat clasa respectiv). Este
uzual s existe mai multi constructori ntr-o clas, care difer prin argumente, dar
au acelasi nume (un caz de supradefinire a unor functii). Exemplu: public Complex
( Complex c) { re=c.re; im=c.im; } Este posibil si supradefinirea unor metode ale
claselor. De exemplu, putem defini dou metode de adunare la un complex, cu
acelasi nume dar cu argumente diferite: // adunare cu un alt complex public void
add ( Complex c) { re += c.re; im += c.im; } // adunare cu un numar real public
void add ( double x) { re = re + x; } Variabilele unei clase pot fi initializate la
declararea lor, ceea ce are ca efect initializarea la ncrcarea clasei, aceeasi pentru
toate obiectele clasei. Acest fel de initializare se practic pentru variabile membru
de tip clas si pentru clase cu un singur obiect. Exemplu de initializare a unei

variabile la declarare: class TextFrame extends JFrame { JTextField t = new JTextField


(10); // initializata la incarcare public TextFrame () { // constructor clasa
getContentPane().add(t,"Center"); // adauga camp text la fereastra } . . . // alte
metode } Intr-o aplicatie se va crea un singur obiect de tip "TextFrame", iar obiectul
de la adresa "t" va avea ntotdeauna mrimea 10. Instructiunea din constructor se
va executa o singur dat, dar trebuie s fie inclus ntr-o functie. Crearea
obiectului cmp text JTextField nu se face printr-o instructiune ci printr-o declaratie
cu initializare. In Java nu sunt permise argumente formale cu valori implicite si
functii cu numr variabil de argumente (la apelare), dar un constructor cu mai
putine argumente poate apela un constructor cu mai multe argumente, dintre care
unele au valori implicite. Un constructor nu poate fi apelat explicit, ca si o metod,
iar atunci cnd este necesar acest apel (din aceeasi clas) se foloseste variabila
this. Exemplu: // constructor cu un parametru din clasa Complex public Complex
(double re) { this (re,0); } // apel constructor cu dou argumente // constructor fara
parametri din clasa Complex public Complex () { this (0); } // apel constructor cu un
argument Variabila "this" Metodele nestatice dintr-o clas actioneaz asupra unor
obiecte din acea clas. Atunci cnd se apeleaz o metod pentru un anumit obiect,
metoda primeste ca argument implicit o referint la obiectul respectiv. Altfel spus,
instructiunile urmtoare : int n = a.length(); // lungimea sirului a String b =
a.substring (k); // extrage subsir din pozitia k din sirul a corespund instructiunilor
urmtoare din limbajul C: int n = length(a); // lungimea sirului a String b = substring
(a,k); // extrage subsir din pozitia k din sirul a Acest argument implicit poate fi folosit
in interiorul unei metode nestatice prin intermediul variabilei predefinite this.
Cuvntul cheie this este o variabil referint care desemneaz n Java adresa
obiectului curent ("acest obiect"). Definitia metodei "length" ar putea fi rescris
folosind variabila this astfel: public int length (this) { return this.count; // "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, dar exist situatii cnd
folosirea lui this este necesar. Un prim caz de folosire curent a variabilei this este
la definirea unui constructor cu argumente formale avnd aceleasi nume cu
variabile ale clasei. Exemplu: public Complex ( float re, float im) { this.re=re;
this.im=im; // ptr. a distinge arg. formale de var. clasei } Astfel de situatii pot fi
evitate prin alegerea unor nume de argumente diferite de numele variabilelor
clasei, dar clasele JDK folosesc aceleasi nume (si variabila this ) pentru c
argumentele unui constructor contin valori initiale pentru variabilele clasei. Un alt
caz de folosire a variabilei this este n instructiunea return, pentru metodele care
modific datele unui obiect si au ca rezultat obiectul modificat. Exemplu: public final
class StringBuffer { private char value[ ]; // un vector de caractere private int count;
// caractere efectiv folosite din vector // metode public StringBuffer deleteCharAt (int
index) { // sterge caracterul din poz index System.arraycopy (value, index+1, value,
index, count-index-1); count--; return this; // rezultatul este obiectul modificat de
metod } // ... alte metode ale clasei StringBuffer } Un caz particular este metoda
"toString" din clasa "String": public String toString () { return this; } Uneori un
obiect si trimite adresa sa metodei apelate (metod static sau dintr-un alt obiect).

Exemple: // un obiect observat isi transmite adresa sa unui observator prin metoda
update for (int i = obs.length-1; i>=0; i--) // din clasa "Observable" ((Observer)
obs[i]).update(this, arg); // apel metoda "Observer.update" // un vector isi transmite
adresa sa metodei statice de sortare class SortedVec extends Vector { public void
addElement (Object obj) { // adaugare element si ordonare super.addElement (obj);
Collections.sort(this); // ordonare obiect ptr care s-a apelat metoda add } }
Variabila this nu poate fi folosit n metode statice. Atribute ale membrilor claselor
Membrii unei clase, variabile si metode, au n Java mai multe atribute: tipul
variabilei sau metodei ( tip primitiv sau tip clas ), un atribut de accesibilitate
(public, protected, private) plus atributele static, final, abstract (numai pentru
metode). Cu exceptia tipului, fiecare din aceste atribute are o valoare implicit,
folosit atunci cnd nu se declar explicit o alt valoare. Cuvintele cheie folosite
pentru modificarea atributelor implicite se numesc "modificatori". De exemplu orice
membru este nestatic si nefinal atunci cnd nu se folosesc modificatorii static sau
final. O variabil static a clasei declarat final este de fapt o constant,
nemodificabil prin operatii ulterioare primei initializri. Exemple de constante
definite n clasa Byte: public class Byte { public static final byte MIN_VALUE = -128;
public static final byte MAX_VALUE = 127; public static final Class TYPE =
Class.getPrimitiveClass(byte); ... // constructori si metode clasa Byte } Se vede din
exemplul anterior c o variabil static final poate fi initializat si cu rezultatul unei
functii, pentru c initializarea se face la ncrcarea clasei. Metodele unei clase
instantiabile sunt de obicei nestatice, dar pot exista si cteva metode statice n
fiecare clas cu date. Exemplu din clasa String: public static String valueOf(Object
obj) { // sir echivalent unui obiect return (obj == null) ? "null" : obj.toString(); } O
metod static poate fi apelat de o metod nestatic. Exemplu: public String
toString() { // metoda din clasa "Integer" return String.valueOf(value); // metoda
statica din clasa String } O metod static (inclusiv functia main) nu poate apela o
metod nestatic, pentru c folosirea unei metode nesatice este conditionat de
existenta unui obiect, dar metoda static se poate folosi si n absenta unor obiecte.
O metod static poate apela un constructor. Exemplu: public static Integer valueOf
(String s) throws NumberFormatException { return new Integer(parseInt(s)); // static
int parseInt(String,int); } Parte din definitia unei metode este si clauza throws, 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. Exemplu de metod din clasa Integer, pentru conversia unui sir
de caractere ntr-un ntreg : public static int parseInt (String s) throws
NumberFormatException { if (s == null) throw new NumberFormatException("null");
. . . } Anumite situatii speciale aprute n executia unei metode sunt uneori
raportate prin rezultatul functiei (boolean) si nu prin exceptii. De exemplu, anumite
functii de citire a unui octet dintr-un fisier au rezultat negativ la sfrsit de fisier, iar
functia de citire a unei linii (ntr-un obiect String) are rezultat null la sfrsit de fisier.
Uneori este posibil anticiparea si prevenirea unor exceptii. Exemplul urmtor arat
cum se poate evita aparitia unei exceptii de iesire din limitele unui vector intrinsec,
din cauza utilizrii gresite a unui program Java: // program cu doua argumente n

linia de comanda public static void main ( String arg[ ]) { if ( arg.length < 2)
{ System.out.println (usage: java copy input output); return; } . . . // prelucrare
argumente primite } O metod declarat final nu mai poate fi redefinit ntr-o
subclas si ar putea fi implementat mai eficient de ctre compilator. O metod
abstract este o metod cu atributul abstract, declarat dar nedefinit n clasa unde
apare pentru prima dat. O clas care contine cel putin o metod abstract este o
clas abstract si nu poate fi instantiat, deci nu se pot crea obiecte pentru aceast
clas (dar se pot defini variabile de un tip clas abstract). Metode declarate dar
nedefinite n Java sunt si metodele "native" (cu atributul native), care se
implementeaz ntr-un alt limbaj dect Java (de obicei limbajul C). Definitia lor
depinde de particularitatile sistemului de calcul pe care se implementeaz
metodele. Exemplu: // metoda System.arraycopy ptr copiere partiala vectori public
static native void arraycopy (Object s, int si, Object d, int di, int length);
Incapsularea datelor n clase De obicei datele unei clase nu sunt direct accesibile
utilizatorilor clasei, deci nu sunt accesibile functiilor din alte clase. Pe de o parte
utilizatorii nu sunt interesati de aceste date (de exemplu, cel care foloseste o stiv
nu are nevoie s vad vectorul stiv si alte detalii), iar pe de alt parte este mai
sigur ca aceste date s fie modificate numai de metodele clasei si nu de orice
utilizator. O clas expune utilizatorilor si o interfat public, care const din
totalitatea metodelor publice (accesibile) ale clasei si care arat ce se poate face cu
obiecte ale clasei respective. Documentatia unei clase prezint numai interfata sa
public, adic constructorii publici, metodele publice (tip, nume, argumente) si
variabilele public accesibile. Este posibil modificarea implementrii unei clase, cu
mentinerea interfetei sale. De exemplu, putem folosi o stiv realizat ca list
nlntuit n locul unei stive vector, fr ca programele ce folosesc stiva s necesite
vreo modificare. Atributul de accesibilitate se refer la drepturile unei clase asupra
membrilor unei alte clase, si este legat de ceea ce se numeste "ncapsulare" sau
"ascunderea datelor". Toate obiectele unei clase au aceleasi drepturi, stabilite la
definitia clasei. Metodele unei clase pot folosi variabilele clasei, indiferent de
atributul de accesibilitate al acestor variabile (private, protected, public). Metodele
unei clase se pot apela unele pe altele, indiferent de ordinea definirii lor si de
atributul de accesibilitate. Atributul public se foloseste pentru functiile si/sau
variabilele clasei ce urmeaz a fi folosite din alte clase. In general, metodele si
constructorii unei clase se declar public. Variabilele public accesibile se mai
numesc si proprietti ale obiectelor. Datele unei clase au n general atributul private
sau protected, iar accesul la aceste date este permis numai prin intermediul
metodelor, publice, ale clasei respective. Exista si cazuri, mai rare, n care nu se
respect principiul ncapsulrii iar variabilele unei clase sunt public accesibile. Este
vorba de clase cu mai multe variabile, folosite relativ frecvent n alte clase si pentru
care accesul prin intermediul metodelor ar fi incomod si ineficient. Exemplu din
pachetul java.awt: public class Rectangle { // dreptunghi de incadrare figura
geometrica public int x, y, width, height; // coordonate: colt, ltime, nltime public
Rectangle (int x0, int y0, int w, int h) { x0=x; y0=y; width=w; height=h; } ... // alte
metode } 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. 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, numite uneori clase prietene. De exemplu,
clasele VectorEnumerator si Vector sunt definite (n Java 1.0) n acelasi pachet si n
acelasi fisier surs, iar variabile cu atributul protected din clasa Vector sunt folosite
direct n clasa VectorEnumerator (n Java 1.2 clasa enumerator este inclus n clasa
vector). In continuare vom schita definitia unei clase pentru o list nlntuit de
numere ntregi. Mai nti trebuie definit o clas pentru un nod de list, o clas care
poate s nu aib nici o metod si eventual nici constructor explicit: class Node { //
nod de lista inlantuita int val; // date din nod Node leg; // legatura la nodul urmator
public Node (int v) { val=v; leg=null;} // 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. - Clasa Node este
interioar clasei MyList. In ambele cazuri variabilele clasei Node pot s nu aib
nici un atribut explicit de accesibilitate Clasa MyList contine o variabil de tip
Node (adresa primului element din list), care poate fi private sau protected,
dac anticipm eventuale subclase derivate. public class MyList { // lista inlantuita
simpla (cu santinela) protected Node prim; // adresa primului element public MyList
( ) { prim = new Node(0); // 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); // creare nod nou cu valoarea v Node p=prim; // inaintare spre sfarsit de
lista while (p.leg != null) p=p.leg; p.leg=nou; // nou urmeaza ultimului element } //
sir ptr. continut lista public String toString () { String aux=""; // rezultatul functiei
toString Node p=prim.leg; // se pleaca de la primul nod din lista while ( p != null)
{ // cat timp mai exista un element urmator aux=aux + p.val + " "; // intregul p.val
se converteste automat in String p=p.leg; // avans la nodul urmator } return
aux; // sirul cu toate valorile din lista } } O metod a unei clase nu poate fi
recursiv, cu argument modificabil de tipul clasei; solutia problemei este o functie
static recursiv, apelat de metod. Exemplu de functii din clasa MyList: //
cautare in lista public boolean contains (int x) { return find (prim,x); // apel fct
recursiva } // fct recursiva de cautare private boolean find (Node crt, int x) { if
(crt==null) // daca sfarsit de lista return false; // atunci x negasit if ( x==crt.val)
return true; // x gasit return find (crt.leg,x); // continua cautarea in sublista } In
exemplul anterior recursivitatea nu este justificat, dar pentru alte clase (cu arbori,
de exemplu) forma recursiv este preferabil. Structura unei clase Java O clas care
poate genera obiecte contine n mod normal si date, deci variabile ale clasei,
definite n afara metodelor clasei (de obicei naintea functiilor). Majoritatea claselor
instantiabile au unul sau mai multi constructori publici folositi pentru initializarea
obiectelor si apelati de operatorul new. Metodele unei clase pot fi clasificate n
cteva grupe: - Metode care permit citirea datelor clasei ( metode accesor). Metode care permit modificarea datelor clasei ( si care lipsesc din anumite clase ). Metode pentru operatii specifice clasei respective. - Metode mostenite de la clasa
Object si redefinite, pentru operatii generale, aplicabile oricrui obiect Java

( equals, toString, hashCode). Conform unor cerinte mai noi ale standardului
Java Beans, metodele de acces la variabile private au un nume care ncepe cu "get"
si continu cu numele variabilei, iar metodele de modificare a variabilelor au un
nume care ncepe cu "set". Exemplu: public class Complex { private double re, im; //
parte reala si imaginara // metode de citire a datelor public float getReal ( ) { return
re; } // extrage parte reale 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 () { . . . } } Clasa
String contine un vector de caractere, dimensiunea sa si mai multe functii care
realizeaz diverse operatii asupra acestui vector: cutarea 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). 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 cror 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. In cursul executiei
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, ctre sistemul care asist
executia ("Runtime system"). Exemplu de metod care poate produce exceptii: //
metoda din clasa String public char charAt (int index) { if ((index < 0) || (index >=
count)) throw new StringIndexOutOfBoundsException(index); return value[index +

offset]; } Instructiunea throw se foloseste pentru semnalarea unei exceptii ntr-o


metod si specific un obiect exceptie(de obicei un obiect anonim, creat chiar n
instructiune). Alegerea acestui cuvnt ("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. In exemplul urmtor metoda
"parseByte" arunc o exceptie ce trebuie tratat (de exemplu, prin repetarea citirii
sirului primit), exceptie care trebuie anuntat mai departe, pentru a se verifica
tratarea ei. // metoda din clasa Byte public static byte parseByte (String s, int radix)
throws NumberFormatException { int i = Integer.parseInt(s, radix); if (i < MIN_VALUE
|| i > MAX_VALUE) throw new NumberFormatException(); // genereaz exceptie
return (byte)i; } O metod n care se poate produce o exceptie si care arunc mai
departe exceptia trebuie s contin n definitia ei clauza throws. Cuvntul 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 negsit } 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 fileCopy ( String src, String dst) throws IOException { FileReader fr = fopen
(src); // aici poate apare exceptie de I/E 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 prerea 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 oricrui mesaj la aparitia
exceptiei). Exceptiile aruncate de toate functiile (inclusiv de main) au ca efect
terminarea programului dup afisarea unui mesaj si a secventei de apeluri ce a dus
la exceptie. 1 1 5. Derivare, mostenire, polimorfism Clase derivate Derivarea
nseamn definirea unei clase D ca o subclas a unei clase A, de la care
mosteneste toate variabilele si metodele publice. Subclasa D poate redefini
metode mostenite de la clasa printe (superclasa) A si poate aduga noi metode (si
variabile) clasei A. Tipul D este un subtip al tipului A. La definirea unei clase derivate
se foloseste cuvntul cheie extends urmat de numele clasei de baz. Exemplu: //
vector ordonat cu inserare element nou public class SortedVector extends Vector
{ // metoda redefinita ptr adaugare la vector ordonat public void addElement
(Object obj) { // adaugare element si ordonare int i=indexOf (obj); if (i < 0) i=-i-1;

insertElementAt(obj,i); } // metoda redefinita: cautare binara in vector ordonat


public int indexOf (Object obj) { // cauta in vector un obiect return
Collections.binarySearch(this,obj); } } In Java se spune c o subclas extinde
functionalitatea superclasei, n sensul c ea poate contine metode si date
suplimentare. In general o subclas este o specializare, o particularizare a
superclasei si nu extinde domeniul de utilizare al superclasei. Cuvntul super, ca
nume de functie, poate fi folosit numai ntr-un constructor si trebuie s fie prima
instructiune (executabil) din constructorul subclasei. Exemplu: public class
IOException extends Exception { public IOException() { super(); } public
IOException(String s) { super(s); } } Principala modalitate de specializare a unei
clase este redefinirea unor metode din superclas. Prin redefinire ("override") se
modific operatiile dintr-o metod, dar nu si modul de utilizare al metodei. De multe
ori, subclasele nu fac altceva dect s redefineasc una sau cteva metode ale
superclasei din care sunt derivate. Redefinirea unei metode trebuie s pstreze
"amprenta" functiei, deci numele si tipul metodei, numrul si tipul argumentelor .
Majoritatea claselor Java care contin date redefinesc metoda toString (mostenit
de la clasa Object), ceea ce permite conversia datelor din clas ntr-un sir de
caractere 2 2 si deci afisarea lor. Nu se poate extinde o clas final (cu atributul
final) si nu pot fi redefinite metodele din superclas care au unul din modificatorii
final, static, private. O metod redefinit ntr-o subclas "ascunde" metoda
corespunztoare din superclas, iar o variabil dintr-o subclas poate "ascunde" o
variabil cu acelasi nume din superclas. Apelarea metodei originale din superclas
nu este n general necesar pentru un obiect de tipul subclasei, dar se poate face
folosind cuvntul cheie "super" n locul numelui superclasei. Exemplu: // vector
ordonat cu adaugare la sfarsit si reordonare public class SortedVector extends
Vector { public void addElement (Object obj) { // adaugare element si ordonare
super.addElement (obj); // apel metoda addElement din clasa Vector
Collections.sort(this); } // metoda mostenita automat dar interzisa in subclasa public
void insertElementAt (Object obj, int pos) { // ptr a nu modifica ordinea throw new
UnsupportedOperationException(); } } O subclas se poate referi la variabilele
superclasei fie direct prin numele lor, dac variabilele din superclas sunt de tip
public sau protected, fie indirect, prin metode publice ale superclasei. Exemplu:
public class Stack extends Vector { . . . public boolean empty() { // test de stiva
goala return size()==0; // sau return elementCount==0; } } Derivarea ca metod
de reutilizare Derivarea este motivat uneori de necesitatea unor clase care
folosesc n comun anumite functii dar au si operatii specifice, diferite n clasele
nrudite. O solutie de preluare a unor functii de la o clas la alta este derivarea. O
clasa derivat "mosteneste" de la superclasa sa datele si metodele nestatice cu
atributele public si protected . Clasa SortedVector, definit anterior, mosteneste
metodele clasei Vector: elementAt, indexOf, toString s.a. Exemplu: SortedVector a =
new SortedVector(); System.out.println ( a.toString()); // afisare continut vector
Chiar si metoda addElement redefinit n clasa derivat, refoloseste extinderea
automat a vectorului, 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 fcut private n subclas. 3 3 Metodele si


variabilele mostenite ca atare de la superclas nu mai trebuie declarate n subclas.
In schimb, trebuie definite functiile constructor, metodele si datele suplimentare
(dac exist) si trebuie redefinite metodele care se modific n subclas. O subclas
nu mosteneste constructorii superclasei, deci fiecare clas are propriile sale functii
constructor (definite explicit de programator sau generate de compilator). Nu se
genereaz automat dect constructori fr argumente. Din acest motiv nu putem
crea un obiect vector ordonat prin instructiunea urmtoare: SortedVector a = new
SortedVector(n); // nu exista constructor cu argument ! In Java constructorul implicit
(fr argumente) generat pentru o subclas apeleaz automat constructorul fr
argumente al superclasei. Com
atorul Java genereaz automat o instructiune "super(); " nainte de prima
instructiune din constructorul subclasei, dac nu exist un apel explicit si dac nu
se apeleaz un alt constructor al subclasei prin "this(...)". Initializarea variabilelor
mostenite este realizat de constructorul superclasei, desi valorile folosite la
initializare sunt primite ca argumente de constructorul subclasei. Este deci necesar
ca un constructor din subclas s apeleze un constructor din superclas. Apelul unui
constructor din superclas dintr-o subclas se face folosind cuvntul cheie super ca
nume de functie si nu este permis apelarea direct, prin numele su, a acestui
constructor. Exemplu: public class SortedVector extends Vector { public
SortedVector () { // vector cu dimensiune implicita super(); // initializari in
superclasa } public SortedVector (int max) { // vector cu dimensiune specificata la
instantiere super(max); // max folosit la alocarea de memorie in superclasa } . . .
Dac se defineste un constructor cu argumente atunci nu se mai genereaz
automat un constructor fr argumente de ctre compilator (n orice clas). In Java
apelul unei metode din constructorul superclasei se traduce prin apelarea metodei
din subclas si nu a metodei cu acelasi nume din superclas, ceea ce uneori este de
dorit dar alteori poate da nastere unor exceptii sau unor cicluri infinite. In exemplul
urmtor clasa SortVec mentine o versiune ordonat si o versiune cu ordinea
cronologic de adugare a elementelor. La executia unei instructiuni SortVec vs =
new SortVec(v); apare exceptia de utilizare a unui pointer nul n metoda
addElement deoarece constructorul clasei Vector apeleaz metoda addElement
din clasa SortVec iar variabila v nu este initializat. class SortVec extends Vector
{ private Vector v; // vector cu ordinea de adaugare public SortVec () { super(); v=
new Vector(); } 4 4 public SortVec ( Vector a) { super(a); // apel constructor
superclasa (produce exceptie !) } public void addElement (Object obj) { // adaugare
element si ordonare super.addElement (obj); // adauga la vector ordonat dupa valori
v.addElement(obj); // adauga la vector ordonat cronologic
Collections.sort(this); } . . . // alte metode (si pentru acces la variabila v) } O
variant corect a constructorului cu argument Vector arat astfel: public SortVec
( Vector a) { v= new Vector(a); for (int i=0;i< v.length; i++) { Object key = v[i]; //
cuvintele sunt chei in dictionar Integer nr = (Integer) dic.get(key); // valoarea
asociata cheii key in dictionar if ( nr != null) // daca exista cheia in dictionar cnt =

nr.intValue()+1; // atunci se mareste contorul de aparitii Float Integer 7 7 else //


daca cheia nu exista in dictionar cnt =1; // atunci este prima ei aparitie dic.put (key,
new Integer(cnt)); // pune in dictionar cheia si valoarea asociata } return dic; }
Rezultatul functiei wordfreq poate fi prelucrat cu metodele generale ale clasei
Dictionary, fr s ne intereseze ce implementare de dictionar s-a folosit n functie.
Alegerea unei alte implementri se face prin modificarea primei linii din functie.
Exemplu de folosire a functiei: public static void main (String arg[]) { String lista[ ]
= {"a","b","a","c","b","a"}; Dictionary d= wordfreq(lista); System.out.println (d); //
scrie {c=1, b=2, a=3} } Utilizarea unui tip mai general pentru crearea de functii si
colectii cu caracter general este o tehnic specific programrii cu obiecte. In Java
(si n alte limbaje) aceast tehnic a condus la crearea unui tip generic Object, 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, HashTable). Clasa Object ca baz a ierarhiei de clase Java In
Java, clasa Object (java.lang.Object) este superclasa tuturor claselor JDK si a
claselor definite de utilizatori. Orice clas Java care nu are clauza extends n
definitie este implicit o subclas derivat direct din clasa Object. De asemenea,
clasele abstracte si interfetele Java sunt subtipuri implicite ale tipului Object. Clasa
Object transmite foarte putine operatii utile subclaselor sale; de aceea n alte
ierarhii de clase (din alte limbaje) rdcina ierarhiei de clase este o clas abstract.
Deoarece clasa Object nu contine nici o metod abstract, nu se impune cu
necesitate redefinirea vreunei metode, dar n practic se redefinesc cteva metode.
Metoda toString din clasa Object transform n sir adresa obiectului si deci trebuie
s fie redefinit n fiecare clas cu date, pentru a produce un sir cu continutul
obiectului. Metoda toString permite obtinerea unui sir echivalent cu orice obiect,
deci trecerea de la orice tip la tipul String (dar nu prin operatorul de conversie ):
Date d = new Date(); String s = d.toString(); // dar nu si : String s = (String) d;
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) { 8 8 return (this == obj); } Pe de alt
parte, 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. De aceea, metoda "equals" este rescris n clasele unde se poate
defini o relatie de egalitate ntre obiecte. Exemplu din clasa Complex: public
boolean equals (Object obj) { Complex cpx =(Complex) obj; if ( obj != null && obj
instanceof Complex) if (this.re == cpx.re && this.im == cpx.im) return true; return
false; } Pentru a evita erori de programare de tipul folosirii metodei "String.equals"
cu argument de tip "Complex", sau a metodei "Complex.equals" cu argument de tip
"String" se foloseste n Java operatorul instanceof, care are ca operanzi o variabil
de un tip clas si tipul unei (sub)clase si un rezultat boolean. Redefinirea unei
metode ntr-o subclas trebuie s pstreze aceeasi semntur cu metoda din
superclas, deci nu se pot schimba tipul functiei sau tipul argumentelor. Din acest
motiv, argumentul metodei "Complex.equals" este de tip Object si nu este de tip
"Complex", cum ar prea mai natural. Dac am fi definit metoda urmtoare: public

boolean equals (Complex cpx) { ... } atunci ea ar fi fost considerat ca o nou


metod, diferit de metoda "equals" mostenit de la clasa Object. In acest caz am fi
avut o supradefinire ("overloading") si nu o redefinire de functii. La apelarea
metodei "Complex.equals" poate fi folosit un argument efectiv de tipul "Complex",
deoarece un argument formal de tip superclas poate fi nlocuit (fr conversie
explicit de tip) printr-un argument efectiv de un tip subclas. Exist deci mai multe
metode "equals" n clase diferite, toate cu argument de tip Object si care pot fi
apelate cu argument de orice tip clas. Metoda "equals" nu trebuie redefinit n
clasele fr date sau n clasele cu date pentru care nu are sens comparatia la
egalitate, cum ar fi clasele flux de intrare-iesire. O variabil sau un argument de tip
Object (o referint la tipul Object) poate fi nlocuit cu o variabil de orice alt tip
clas, deoarece orice tip clas este n Java derivat din si deci echivalent cu tipul
Object. Exemplul urmtor arat cum un vector de obiecte Object poate fi folosit
pentru a memora obiecte de tip String . // afisarea unui vector de obiecte oarecare
public static void printArray ( Object array [ ]) { for (int k=0; k=0 ; } O metod
static poate apela o metod polimorfic; de exemplu Arrays.sort apeleaz functia
polimorfic compareTo (sau compare), al crei efect depinde de tipul obiectelor
comparate, pentru a ordona un vector cu orice fel de obiecte. Exemplu de functie
pentru cutare secvential ntr-un vector de obiecte: public static int indexOf
(Object[] array, Object obj) { for (int i=0;i<= size() ; i++) { String s =
e.nextElement().toString(); // utilizarea variabilei interfata buf.append(s);
buf.append(","); } buf.append("\b]"); return buf.toString(); } } In exemplul anterior,
metoda elements din clasa Vector are ca rezultat un obiect enumerator pe
vector, de tipul Enumeration. Exemplu de posibil implementare a metodei
elements din clasa Vector : public Enumeration elements() { return new
VectorEnumeration() } // definirea clasei iterator pe vector class VectorEnumeration
implements Enumeration { int count = 0; // indice element curent din enumerare
public boolean hasMoreElements() { return count < elementCount; // elementCount
= nr de elemente } public Object nextElement() { if (count < elementCount) return
elementData[count++]; // elementData = vector de obiecte } } Clasa noastr
VectorEnumeration trebuie s aib acces la datele locale ale clasei Vector
(elementData si elementCount), deci fie este n acelasi pachet cu clasa Vector,
fie este o clas interioar clasei Vector. O interfat poate extinde o alt interfat
(extends) cu noi metode abstracte. Ca exemplu, interfata TreeNode reuneste
metode de acces la un nod de arbore, 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, int index); // adauga un fiu acestui
nod void remove(int index); // elimina fiu cu indice dat al acestui nod . . . } Diferente
ntre interfete si clase abstracte 5 O clas poate implementa (implements) o
interfat sau mai multe si poate extinde (extends) o singur clas (abstract sau
nu). Clasele care implementeaz o aceeasi interfat pot fi foarte diverse si nu
formeaz o ierarhie (o familie). Un exemplu sunt clasele cu obiecte comparabile,
care toate implementeaz interfata Comparable: String, Date, Integer, BigDecimal,

s.a. O interfat este considerat ca un contract pe care toate clasele care


implementeaz acea interfat se oblig s l respecte, deci o clas si asum
obligatia de a defini toate metodele din interfetele mentionate n antetul ei, putnd
contine si alte metode. O clas poate simultan s extind o clas (alta dect clasa
Object, implicit extins) si s implementeze una sau mai multe interfete. Altfel spus,
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). De exemplu, mai
multe clase predefinite SDK, cu date, implementeaz simultan interfetele
Comparable (obiectele lor pot fi comparate si la mai mic mai mare), Clonable
(obiectele lor pot fi copiate), Serializable (obiectele lor pot fi salvate sau serializate
n fisiere disc sau pe alt mediu extern). Exemple: public class String implements
Comparable, Serializable { ...} public class Date implements Serializable, Clonable,
Comparable { ...} Este posibil ca n momentul definirii unei interfete s nu existe
nici o singur clas compatibil cu interfata, cum este cazul interfetei Comparator.
O interfat fr nici o metod poate fi folosit pentru a permite verificarea utilizrii
unor metode numai n anumite clase, n faza de executie. Un exemplu tipic este
interfata Clonable, definit astfel: public interface Clonable { } Clasa Object contine
metoda "clone", folosit numai de clasele care declar c implementeaz interfata
Clonable. Metoda neabstract "clone" este mostenit automat de toate clasele Java,
dar este aplicabil numai pentru o parte din clase. Pentru a semnala utilizarea
gresit a metodei "clone" pentru obiecte ne-clonabile, se produce o exceptie de tip
CloneNotSupportedException atunci cnd ea este apelat pentru obiecte din clase
care nu ader la interfata Clonable. O utilizare asemntoare o are interfata
Serializable, pentru a distinge clasele ale cror obiecte sunt serializabile (care contin
metode de salvare si de restaurare n / din fisiere) de clasele ale cror obiecte nu
pot fi serializate ( fr obiecte "persistente"). Practic, toate clasele cu date sunt
serializabile. Interfete ca Serializable si Clonable se numesc interfete de "marcare" a
unui grup de clase ("tagging interfaces"), pentru a permite anumite verificri. 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, atunci ar fi fost necesar o interfat de "marcare" pentru a
distinge clasele cu obiecte comparabile de clasele cu obiecte necomparabile. O
interfat care stabileste un tip comun poate fi att de general nct s nu contin
nici o metod. Un exemplu este interfata EventListener (pachetul "java.util"), 6 care
stabileste tipul asculttor la evenimente, dar metodele de tratare a evenimentului
nu pot fi precizate nici ca prototip, deoarece depind de tipul evenimentului. Interfata
este extins de alte interfete, specifice anumitor asculttori (pentru anumite
evenimente): public interface ActionListener extends EventListener { public void
actionPerformed(ActionEvent e); } public interface ItemListener extends
EventListener { public void itemStateChanged(ItemEvent e); } Interfetele sunt
preferabile n general claselor abstracte, pentru c ofer mai mult libertate
subclaselor. Un exemplu simplu este cel al metodelor considerate esentiale pentru
orice clas dictionar: pune pereche cheie-valoare in dictionar, obtine valoarea
asociat unei chei date s.a. Aceste metode au fost reunite nainte de Java 1.2 ntr-o

clas abstract (Dictionary) care continea numai metode abstracte. Incepnd cu


Java 2 aceleasi metode (plus nc cteva) au fost grupate n interfata Map. Avantajul
solutiei interfat n raport cu o clas abstract este evident atunci cnd definim un
dictionar realizat ca un vector de perechi cheie-valoare: o clas derivat din Vector
ar putea mosteni o serie de metode utile de la superclas, dar nu poate extinde
simultan clasele Dictionary si Vector. Interfetele si clasele abstracte nu trebuie
opuse, 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, iar clasa abstract este o implementare partial a
interfetei, pentru a facilita definirea de noi clase prin extinderea clasei abstracte. In
felul acesta se obtine o familie de clase deschis pentru extinderi ulterioare cu clase
compatibile, care nu trebuie definite ns de la zero. Un exemplu este interfata Set
(sau Collection) si clasa AbstractSet (AbstractCollection) care permit definirea
rapid de noi clase pentru multimi (altele dect HashSet si TreeSet), compatibile cu
interfata dar care beneficiaz de metode mostenite de la clasa abstract.
Compararea de obiecte n Java In unele operatii cu vectori de obiecte (sortare,
determinare maxim sau minim s.a.) este necesar compararea de obiecte (de
acelasi tip). Functia de comparare depinde de tipul obiectelor comparate, dar poate
fi o functie polimorfic, cu acelasi nume, tip si argumente dar cu definitii diferite. In
plus, dou obiecte pot fi comparate dup mai multe criterii (criteriile sunt variabile
sau combinatii de variabile din aceste obiecte). In Java, functia compareTo este
destinat comparatiei dupa criteriul cel mai natural (cel mai frecvent iar uneori si
unicul criteriu). Pot fi comparate cu metoda compareTo numai clasele care
implementeaz interfata Comparable si deci care definesc metoda abstract
compareTo: public interface Comparable { 7 int compareTo (Object obj); // rezultat
0 } Clasele care declar implementarea acestei interfete trebuie s contin o
definitie pentru metoda "compareTo", cu argument de tip Object. Exemple de clase
Java cu obiecte comparabile : Integer, Float, String, Date, BigDecimal s.a. Exemplu:
public class Byte extends Number implements Comparable { private byte value; . . .
// constructori, metode specifice public int compareTo( Object obj) { // compara
doua obiecte Byte return this.value- ((Byte) obj).value ; } } 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. Exemplu: // determinare maxim
dintr-un vector de obiecte oarecare public static Object max (Object [ ] a)
{ Comparable maxim = (Comparable) a[0]; for (int i=0;i< 0) // daca maxim < a[i]
maxim=(Comparable)a[i]; return maxim; } Functia Arrays.sort cu un argument
foloseste implicit metoda compareTo. Putem considera c interfata Comparable
adaug metoda compareTo clasei Object si astfel creeaz o subclas de obiecte
comparabile. Este posibil ca egalitatea de obiecte definit de metoda compareTo
s fie diferit de egalitatea definit de metoda equals. De exemplu, clasa Arc
produce obiecte de tip arc de graf cu costuri: class Arc implements Comparable
{ int v,w; // noduri capete arc float cost; // cost arc public Arc (int x,int y) { v=x;
w=y; } public boolean equals (Object obj) { Arc a= (Arc)obj; return (v==a.v &&
w==a.w); // arce egale dac au aceleasi extremitati } public String toString ()

{return v+"-"+w; } public int compareTo (Object obj) { Arc a = (Arc) obj; return cost
a.cost; // arce egale dac au costuri egale ! } } 8 Metoda equals este folosit la
cutarea ntr-o colectie neordonat (contains, add pentru multimi, s.a.), iar
metoda compareTo este folosit la cutarea ntr-o colectie ordonat
(Collections.binarySearch), ceea ce poate conduce la rezultate diferite pentru cele
dou metode de cutare ntr-o list de obiecte Arc. Pentru comparare de obiecte
dup alte criterii dect cel natural s-a introdus o alt functie cu numele
compare, parte din interfata Comparator: public interface Comparator { int
compare (Object t1, Object t2); // implicit abstracta, ne-statica ! boolean equals
(Object); // comparare de obiecte comparator ! } Interfata Comparator ar fi putut fi
si o clas abstract cu o singur metod, pentru c este improbabil ca o clas
comparator s mai mosteneasc si de la o alt clas. Functia Arrays.sort are si o
form cu dou argumente; al doilea argument este de tipul Comparator si
precizeaz functia de comparare (alta dect compareTo). Functia care determin
maximul dintr-un vector de obiecte poate fi scris si astfel: public static Object max
(Object a[ ], Comparator c) { Object maxim = a[0]; for (int k=1; k< 0) // c este un
obiect comparator maxim = a[k]; return maxim; } Functia max cu dou
argumente este mai general si poate fi folosit pentru a ordona un acelasi vector
dup diferite criterii (dup diverse proprietti ale obiectelor). De exemplu, pentru
ordonarea unui vector de siruri dup lungimea sirurilor, vom defini urmtoarea clas
comparator: class LengthComparator implements Comparator { public int compare
(Object t1, Object t2) { return ((String)t1).length() - ((String)t2).length(); } } . . . //
ordonare dupa lungime String [ ] a = {patru,trei,unu}; Arrays.sort( a, new
LengthComparator()); Pentru ordonarea unei matrice de obiecte dup valorile dintro coloan dat putem defini o clas comparator cu un constructor care primeste
numrul coloanei: class CompCol implements Comparator { int c; // numar coloana
ce determina ordinea public CompCol (int col) { c=col; } // constructor cu un
argument 9 public int compare(Object o1, Object o2) { Comparable c1
=(Comparable) ((Object[ ])o1)[c]; // linia o1, coloana c Comparable c2
=(Comparable) ((Object[ ])o2)[c]; // linia o2, coloana c return c1.compareTo(c2); //
compara valorile din coloana c (liniile o1 si o2) } } Exemplu de utilizare a acestui
comparator n functia de sortare : Object a[ ][ ] = { {"3","1","6"}, {"1","5","2"},
{"7","3","4"} }; for (int i=0;i< files.length;i++) System.out.println (files[i]); } } In
clasa File exist si metode de extragere selectiv de fisiere dintr-un director: metoda list cu argument de tip FilenameFilter si rezultat String[] - metoda
listFiles cu argument de tip FileFilter sau FilenameFilter si rezultat File[] FileFilter si
FilenameFilter sunt interfete cu o singur metoda accept. 10 In SDK 2 (pachetul
java.io) sunt prevzute dou interfete pentru clase de filtrare a continutului unui
director : public interface FilenameFilter { // prezenta din jdk 1.0 boolean accept
(File path, String filename); } public interface FileFilter { // prezenta din jdk 1.2
boolean accept (File path); } 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. Exemplu de listare selectiv cu
masc a directorului curent: // clasa pentru obiecte filtru class FileTypeFilter

implements FileFilter { String ext; // extensie nume fisier public FileTypeFilter (String
ext) { this.ext = ext; } public boolean accept (File f) { String fname = f.getName();
int pp = fname.indexOf('.'); if (pp==0) return false; return
ext.equals(fname.substring(pp+1)) ; } } // 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.listFiles(new FileTypeFilter("java")); for (int i=0;
i< files.length;i++) System.out.println (files[i]); } } Metoda accept definit de
utilizator este apelat de metoda list (listFiles), care contine si ciclul n care se
verific fiecare fisier dac satisface sau nu conditia continut de functia accept.
Functia list primeste adresa functiei accept prin intermediul obiectului argument
de tip FileFilter. Pentru filtrare dup orice sablon (masc) putem proceda astfel: a)
Se defineste o clas pentru lucrul cu siruri sablon : class Pattern { protected String
mask; public Pattern ( String mask) { this.mask=mask; 11 } public boolean match
( String str) { // ... compara str cu mask } } b) Se defineste o clas fltru dup
orice masc, derivat din clasa Pattern si care implementeaz o interfat filtru din
JDK : class MaskFilter extends Pattern implements FileFilter { public MaskFilter
(String mask) { super(mask); } public boolean accept (File f) { super.mask=
super.mask.toUpperCase(); String fname = f.getName().toUpperCase(); return
match(fname); } } c) Se foloseste un obiect din noua clas ntr-o aplicatie: class Dir
{ public static void main (String argv[ ]) throws IOException { File d =new
File(argv[0]); // nume director in argv[0] File[ ] files = d.listFiles(new
MaskFilter(argv[1])); // masca in argv[1] for (int i=0; i< files.length;i++)
System.out.println (files[i]); } } Acest exemplu poate explica de ce FileFilter este o
interfat si nu o clas abstract: clasa filtru poate prelua prin mostenire metoda
match de la o alt clas, pentru c nu este obligat s extind clasa abstract
(posibil) FileFilter. Functii callback O functie pentru ordonare natural n ordine
descresctoare poate fi scris astfel: public static void sortd (List list)
{ Collections.sort (a,new DComp()); } static class DComp implements Comparator
{ // folosit intr-o metoda statica ! public int compare (Object a,Object b)
{ Comparable ca=(Comparable)a; return - ca.compareTo(b); } } 12 Functia
"compare" poart numele de functie "callback". Clasa care contine functia sortd si
clasa DComp transmite functiei sort adresa functiei compare (printrun obiect
DComp) pentru a permite functie sort s se refere "napoi" la functia de
comparare. Practic, se transmite un pointer printr-un obiect ce contine o singur
functie; adresa obiectului comparator este transmis de la sortd la sort, iar
sort foloseste adresa continut n obiectul comparator pentru a se referi napoi la
functia "compare" (n cadrul functiei sort se apeleaz functia compare). In
limbajul C se transmite efectiv un pointer la o functie (un pointer declarat explicit).
Situatia mai poate fi schematizat si astfel: o functie A ("main", de ex.) apeleaz o
functie B ("sort", de ex.), iar B apeleaz o functie X ("compare") dintr-un grup de
functii posibile. Adresa functiei X este primit de B de la functia A. De fiecare dat
cnd A apeleaz pe B i transmite si adresa functiei X, care va fi apelat napoi de B.
La scrierea functiei de sortare (Collections.sort) nu se cunostea exact definitia
functiei de comparare (pentru c pot fi folosite diverse functii de comparare), dar s-

a putut preciza prototipul functiei de comparare, ca metod abstract inclus ntr-o


interfat. Putem deci s scriem o functie care apeleaz functii nc nedefinite, dar
care respect toate un prototip (tip rezultat, tip si numr de argumente). Tehnica
callback este folosit la scrierea unor functii de bibliotec, dintr-un pachet de
clase, functii care trebuie s apeleze functii din programele de aplicatii, scrise
ulterior de utilizatorii pachetului. Un exemplu este un analizor lexical SAX, 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). Analizorul SAX impune aplicatiei s foloseasc functii
care respect interfetele definite de analizor si apeleaz aceste functii callback.
Deci functiile callback sunt definite de cei care dezvolt aplicatii dar sunt apelate
de ctre functii din clase predefinite (de bibliotec). 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), cu suport neprecizat, vzut ca
sir de octeti. Operatiile elementare de citire si de scriere octet sunt aplicabile
oricrui tip de flux si de aceea au fost definite dou clase abstracte: o surs de date
InputStream si o destinatie de date OutputStream, care contin metodele abstracte
read si write. Ca suport concret al unui flux de date Java se consider
urmtoarele variante: fisier disc, vector de octeti (n memorie), sir de caractere (n
memorie), canal de comunicare ntre fire de executie (n memorie). Pentru fiecare
tip de flux exist implementri diferite ale metodelor abstracte read si write
pentru citire/scriere de octeti. Clasele abstracte InputStream si OutputStream contin
si metode neabstracte pentru citire si scriere blocuri de octeti, care apeleaz
metodele de citire/scriere octet, precum si alte metode care nu sunt definite dar nici
nu sunt abstracte, transmise tuturor subclaselor. Exemplu: 13 public abstract class
InputStream { public abstract int read ( ) throws IOException; // citeste un octet
public int read ( byte b[ ]) throws IOException { return read (b,0,b.length); // citeste
un bloc de octeti } public int available( ) throws IOException { // nr de octeti rmasi
de citit return 0; } ... // alte metode } Clasele concrete de tip flux de citire sunt toate
derivate din clasa InputStream, ceea ce se reflect si n numele lor: FileInputStream,
ByteInputStream, etc. 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),
pentru un flux vector de octeti metoda "read" preia urmtorul octet din vector.
Incepnd cu versiunea 1.1 s-au introdus alte dou clase abstracte, numite Reader si
Writer, cu propriile lor subclase. public abstract class Reader { protected Reader()
{ ... } // ptr a interzice instantierea clasei public int read() throws IOException { //
citire octet urmator char cb[ ] = new char[1]; if (read(cb, 0, 1) == -1) return -1; else
return cb[0]; } public int read(char cbuf[ ]) throws IOException { // citire bloc de
octeti return read(cbuf, 0, cbuf.length); } abstract public int read(char cbuf[ ], int
off, int len) throws IOException; abstract public void close() throws
IOException; . . . // alte metode } Interfata DataInput reuneste metode pentru citire
de date de tipuri primitive: public interface DataInput { byte readByte () throws
IOException; char readChar () throws IOException; int readInt () throws IOException;

long readLong () throws IOException; float readFloat () throws IOException; String


readLine () throws IOException; ... } 14 De observat c declaratiile acestor metode
nu impun modul de reprezentare extern a numerelor ci doar o form (un prototip)
pentru metodele de citire. Clasa RandomAccessFile implementeaz ambele interfete
si deci asigur un fisier n care se poate scrie sau din care se poate citi orice tip
primitiv. In acest tip de fisiere 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. Operatiile cu fisiere depind de sistemul de operare gazd, deci o parte
din metodele claselor de I/E sunt metode "native", dar numrul lor este mentinut la
minim. Un fragment din clasa RandomAccessFile arat astfel: public class
RandomAccessFile implements DataOutput, DataInput { private native void open
(String name, boolean write) throws IOException; public native int read () throws
IOException; public native void write (int b) throws IOException; public native long
length () throws IOException; public native void close () throws IOException; ... //
alte metode } Clasele DataInputStream si DataOutputStream implementeaz
interfetele DataInput si respectiv DataOutput. Aceste clase simplific crearea si
exploatarea de fisiere text si de fisiere binare, ce contin date de un tip primitiv
( numere de diferite tipuri s.a. ). Exemplu de variant modificat a clasei
DataInputStream : public class DataInputStream extends InputStream implements
DataInput { InputStream in; // in poate fi orice fel de flux (ca suport fizic) public
DataInputStream (InputStream in) { this.in=in; } // constructor public final byte
readByte() throws IOException { int ch = in.read(); if (ch < 0) throw new
EOFException(); return (byte)(ch); } public final short readShort() throws
IOException { InputStream in = this.in; int ch1 = in.read(); int ch2 = in.read(); //
citeste doi octeti if ((ch1 | ch2) < 0) throw new EOFException(); return (short)((ch1
<< 8) + (ch2 << 0)); // asamblare octeti pe 16 biti } public String readLine ( ) { ... }
// citeste o linie public int readInt ( ) { ... } // citeste un numar intreg public float
readFloat ( ) { ... } // citeste un numar real . . . // alte metode } Obiecte de tip
DataInputStream nu pot fi create dect pe baza altor obiecte, care precizeaz
suportul fizic al datelor. Clasa DataInputStream nu are constructor fr argumente si
nici variabile care s specifice suportul datelor. 15 Variabila public in, definit n
clasa System, este de un subtip (anonim) al tipului InputStream si corespunde
tastaturii ca flux de date. Pentru obiectul adresat de variabila System.in se poate
folosi metoda read pentru citirea unui octet (caracter) dar nu se poate folosi o
metod de citire a unei linii (terminate cu Enter). Pentru a citi linii de la consol,
putem crea un obiect de tip DataInputStream , care suport metoda readLine.
Exemplu: // citire linie de la tastatura public static void main (String arg[]) throws
Exception { DataInputStream f = new DataInputStream(System.in); String line=
f.readLine(); System.out.println ( S-a citit: "+ line); } Utilizarea metodelor readInt,
readFloat s.a. nu are sens pentru tastatur, deoarece trebuie fcut conversia de
la sir de caractere la ntreg (format intern binar), operatie care nu se face n aceste
metode. Dac o linie contine un singur numr care nu este neaprat ntreg, atunci
putem scrie: DataInputStream f = new DataInputStream(System.in); String line=
f.readLine(); double d = Double.parseDouble(line); Variabila public out din clasa

System nu este de un subtip al tipului OutputStream, ci este de un subtip (anonim)


al tipului PrintStream, tip care contine mai multe metode print, care fac conversia
numerelor din format intern binar n sir de caractere, nainte de scriere. 1 7. Colectii
de obiecte n Java 2 Infrastructura claselor colectie O colectie este un obiect ce
contine un numr oarecare, variabil de obiecte. Colectiile se folosesc pentru
memorarea si regsirea unor date sau pentru transmiterea unui grup de date de la
o metod la alta. Colectiile Java sunt structuri de date generice, realizate fie cu
elemente de tip Object, fie cu clase sablon (cu tipuri parametrizate) . Infrastructura
colectiilor (Collection Framework) ofer clase direct utilizabile si suport pentru
definirea de noi clase (sub form de clase abstracte), toate conforme cu anumite
interfete ce reprezint tipuri abstracte de date (liste, multimi, dictionare). Un
utilizator si poate defini propriile clase colectie, care respect aceste interfete
impuse si sunt compatibile cu cele existente (pot fi nlocuite unele prin altele).
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 adugare obiect la colectie si de calcul numar elemente din colectie,
care depind de structura fizic a colectiei). Familia claselor colectie din Java este
compus din dou ierarhii de clase : - Ierarhia care are la baz interfata Collection, Ierarhia care are la baz interfata Map. O colectie, n sensul Java, este un tip de date
abstract care reuneste un grup de obiecte de tip Object, numite si elemente ale
colectiei. Un dictionar (o asociere ) este o colectie de perechi chei-valoare (ambele
de tip Object); fiecare pereche trebuie s aib o cheie unic, deci nu pot fi dou
perechi identice. Interfata Collection contine metode aplicabile pentru orice colectie
de obiecte. Nu toate aceste operatii trebuie implementate obligatoriu de clasele
care implementeaz interfata Collection; o clas care nu defineste o metod
optional poate semnala o exceptie la ncercarea de apelare a unei metode
neimplementate. Urmeaz descrierea complet a interfetei Collection : public
interface Collection { // operatii generale ptr orice colectie int size(); // dimensiune
colectie (nr de elemente) boolean isEmpty(); // verifica daca colectie vida boolean
contains(Object element); // daca colectia contine un obiect dat boolean add(Object
element); // adauga un element la colectie boolean remove(Object element); //
elimina un element din colectie Iterator iterator(); // produce un iterator pentru
colectie boolean containsAll(Collection c); // daca colectia contine elem. din colectia
c boolean addAll(Collection c); // adauga elem. din c la colectie boolean
removeAll(Collection c); //elimina din colectie elem. colectiei c boolean
retainAll(Collection c); // retine in colectie numai elem. din c void clear(); // sterge
continut colectie Object[ ] toArray(); // copiere colectie intr-un vector } 2 Din
interfata Collection sunt derivate direct dou interfete pentru tipurile abstracte: Set pentru multimi de elemente distincte. - List pentru secvente de elemente, n
care fiecare element are un succesor si un predecesor si este localizabil prin pozitia
sa n list (un indice ntreg). Pentru fiecare din cele 3 structuri de date abstracte
(list, multime,dictionar) sunt prevzute cte dou clase concrete care
implementeaz interfetele respective. Structurile de date concrete folosite pentru
implementarea tipurilor de date abstracte sunt : vector extensibil, list dublu

nlntuit, tabel de dispersie si arbore binar. Toate clasele colectie instantiabile


redefinesc metoda toString, care produce un sir cu toate elementele colectiei,
separate prin virgule si ncadrate de paranteze drepte. Afisarea continutului unei
colectii se poate face printr-o singur instructiune. De asemenea sunt prevzute
metode de trecere de la vectori intrinseci de obiecte (Object [] ) la colectii de
obiecte si invers: functia Arrays.asList, cu un argument vector intrinsec de obiecte
si rezultat de tip List si functia toArray din clasa AbstractCollection, de tip
Object[]. Exemplu: String sv[] = {unu,doi,trei}; List list = Arrays.asList(sv); //
nu este nici ArrayList, nici LinkedList ! System.out.println (list); // System.out.println
(list.toString()); String aux[] = (String[ ]) list.toArray(); // aux identic cu sv O a treia
ierarhie are la baz interfata Iterator, pentru metodele specifice oricrui iterator
asociat unei colectii sau unui dictionar. Toate colectiile au iteratori dar nu si clasele
dictionar (se poate ns itera pe multimea cheilor sau pe colectia de valori). Clasa
Collections contine metode statice pentru mai multi algoritmi "generici" aplicabili
oricrei colectii: "min", "max" ( valori extreme dintr-o colectie). Alte metode sunt
aplicabile numai listelor: sort, binarySearch, reverse, shuffle. Algoritmii
generici se bazeaz pe existenta tipului generic Object si a metodelor polimorfice
(equals, compareTo s.a.) Multimi de obiecte O multime este o colectie de elemente
distincte pentru care operatia de cutare a unui obiect n multime este frecvent si
trebuie s aib un timp ct mai scurt. Interfata Set contine exact aceleasi metode
ca si interfata Collection, dar implementrile acestei interfete asigur unicitatea
elementelor unei multimi. Metoda de adugare a unui obiect la o multime "add"
verific dac nu exist deja un element identic, pentru a nu se introduce duplicate
n multime. De aceea obiectele introduse n multime trebuie s aib metoda
equals redefinit, pentru ca obiecte diferite s nu apar ca fiind egale la
compararea cu equals. Clasele concrete API care implementeaz interfata Set
sunt: HashSet : pentru o multime neordonat realizat ca tabel de dispersie
TreeSet : pentru o multime ordonat realizat ca un arbore echilibrat Tabelul de
dispersie asigur cel mai bun timp de cutare, dar arborele echilibrat permite
pstrarea unei relatii de ordine ntre elemente. 3 Exemplul urmtor foloseste o
multime de tipul HashSet pentru un graf reprezentat prin multimea arcelor
(muchiilor) : // graf reprezentat prin multimea arcelor class Graph { private int n; //
numar de noduri in graf private Set arc; // lista de arce public Graph ( int n) { this.n
=n; arc = new HashSet(); } public void addArc (int v,int w) { // adauga arcul (v,w) la
graf arc.add (new Arc (v,w)); } public boolean isArc (int v,int w) { // daca exista arcul
(v,w) return arc.contains (new Arc(v,w)); } public String toString () { return
arc.toString(); } } In general se recomand programarea la nivel de interfat si nu la
nivel de clas concret. Asadar, se vor folosi pe ct posibil variabile de tip Collection
sau Set si nu variabile de tip HashSet sau TreeSet (numai la construirea obiectului
colectie se specific implementarea sa). Operatiile cu dou colectii sunt utile mai
ales pentru operatii cu multimi: s1.containsAll (s2) // true dac s1 contine pe s1
(includere de multimi) s1.addAll (s2) // reuniunea multimilor s1 si s2 (s1=s1+s2)
s1.retainAll (s2) // intersectia multimilor s1 si s2 (s1=s1*s2) s1.removeAll (s2) //
diferenta de multimi (s1= s1-s2) Diferenta simetric a dou multimi se poate obtine

prin secventa urmtoare: Set sdif = new HashSet(s1); sdif.addAll(s2); // reuniune


s1+s2 Set aux = new HashSet(s1); aux.retainAll (s2); // intersectie s1*s2 in aux
simdif.removeAll(aux); // reuniune minus intersectie Exemplul urmtor 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. S-au folosit multimi arbori ordonati,
pentru a permite afisarea cuvintelor n ordine lexicografic. class Sets { public static
void main (String arg[ ]) throws IOException { Set toate = new HashSet (); // toate
cuvintele distincte din text 4 Set dupl =new TreeSet (); // cuvintele cu aparitii
multiple RandomAccessFile f = new RandomAccessFile (arg[0],r); String sir="",
line; while ( (line =f.readLine ()) != null) // citeste tot fisierul in sir sir=sir+line+"\n";
StringTokenizer st = new StringTokenizer (sir); while ( st.hasMoreTokens() ) { String
word= st.nextToken(); if ( ! toate.add (word) ) // daca a mai fost in toate dupl.add
(word); // este o aparitie multipla } System.out.println ("multiple: "+ dupl); Set unice
= new TreeSet (toate); unice.removeAll (dupl); // elimina cuvinte multiple
System.out.println ("unice: " + unice); } } Interfata SortedSet extinde interfata Set
cu cteva metode aplicabile numai pentru colectii ordonate: Object first(), Object
last(), SortedSet subSet(Object from, Object to), SortedSet headSet(Object to),
SortedSet tailSet (Object from). Liste secventiale Interfata List contine cteva
metode suplimentare fat de interfata Collection : - Metode pentru acces pozitional,
pe baza unui indice ntreg care reprezint pozitia : get (index), set (index,object),
add (index, object), remove (index) - Metode pentru determinarea pozitiei unde se
afl un element dat, deci cutarea primei si ultimei aparitii a unui obiect ntr-o list:
indexOf (object), lastIndexOf (object) - Metode pentru crearea de iteratori specifici
listelor: listIterator (), listIterator (index) - Metod de extragere sublist dintr-o list:
List subList(int from, int to); Exist dou implementri pentru interfata List: ArrayList
list vector, preferabil pentru acces aleator frecvent la elementele listei. LinkedList
list dublu nlntuit, preferat cnd sunt multe inserri sau stergeri. In plus, clasei
Vector i-au fost adugate noi metode pentru a o face compatibila cu interfata List.
Noile metode au nume mai scurte si o alt ordine a argumentelor n metodele "add"
si "set": Forma veche (1.1) Forma nou (1.2) Object elementAt (int) Object get(int)
Object setElementAt (Objext, int) Object set (i, Object) void insertElementAt (Object,
int) void add (i,Object) Exemplu de functie care schimb ntre ele valorile a dou
elemente i si j: 5 static void swap (List a, int i, int j) { // din clasa Collections Object
aux = a.get(i); // a.elementAt(i) a.set (i,a.get(j)); // a.setElementAt (a.elementAt(j) ,
i) a.set (j,aux); // a.setElementAt (aux , j) } De observat c metodele "set" si
"remove" au ca rezultat vechiul obiect din list, care a fost modificat sau eliminat.
Metoda set se poate folosi numai pentru modificarea unor elemente existente n
list, nu si pentru adugare de noi elemente. Algoritmii de ordonare si de cutare
binar sunt exemple de algoritmi care necesit accesul direct, prin indici, la
elementele listei. Urmeaz o variant simplificat a metodei binarySearch; n caz
de obiect negsit, rezultatul este pozitia unde ar trebui inserat obiectul cutat astfel
ca lista s rmn ordonat ( decalat cu 1 si cu minus). public static int binSearch
(List list, Object key) { int low = 0, high = list.size()-1; while (low <= high) { int mid
=(low + high)/2; Object midVal = list.get(mid); // acces prin indice int cmp =

((Comparable)midVal).compareTo(key); if (cmp < 0) low = mid + 1; else if (cmp > 0)


high = mid - 1; else return mid; // gasit } return -(low + 1); // negasit } Exemplu de
utilizare a functiei de cutare ntr-o secvent care adaug un obiect la o list
ordonat, astfel ca lista s rmn ordonat: int pos = Collections.binarySearch
(alist,akey); if (pos < 0) alist.add (-pos-1, akey); Accesul pozitional este un acces
direct, rapid la vectori dar mai putin eficient n cazul listelor nlntuite. De aceea s-a
definit o clas abstract AbstractSequentialList, care este extins de clasa
LinkedList dar nu si de clasa ArrayList. Metoda static Collections.binarySearch, cu
parametru de tipul general List, recunoaste tipul de list si face o cutare binar n
vectori, dar o cutare secvential n liste secventiale. Clasa LinkedList contine, n
plus fat de clasa abstract AbstractList, urmtoarele metode, utile pentru cazuri
particulare de liste (stive, cozi etc.): getFirst(), getLast(), removeFirst(),
removeLast(), addFirst(Object), addLast(Object). Clase dictionar6 Interfata Map
contine metode specifice operatiilor cu un dictionar de perechi cheie valoare, n care
cheile sunt unice . Exist trei implementri pentru interfata Map: HashMap dictionar
realizat ca tabel de dispersie, cu cel mai bun timp de cutare. TreeMap dictionar
realizat ca arbore echilibrat, care garanteaz ordinea de enumerare.
LinkedHashMap tabel de dispersie cu mentinere ordine de introducere (din vers.
1.4) Definitia simplificat a interfetei Map este urmtoarea: public interface Map
{ Object put(Object key, Object value); // pune o pereche cheie-valoare Object
get(Object key); // extrage valoare asociat unei chei date Object remove(Object
key); // elimin pereche cu cheie dat boolean containsKey(Object key); // verifica
daca exista o cheie data boolean containsValue(Object value); // verifica daca exista
o valoare data int size(); // dimensiune dictionar (nr de perechi) boolean isEmpty();
void clear(); // elimina toate perechile din dictionar public Set keySet(); // extrage
multimea cheilor public Collection values(); // extrage valori din dictionar } Metoda
get are ca rezultat valoarea asociat unei chei date sau null dac cheia dat nu se
afl n dictionar. Metoda put adaug sau modific o pereche cheievaloare si are ca
rezultat valoarea asociat anterior cheii date (perechea exista deja n dictionar) sau
null dac cheia perechii introduse este nou. Efectul metodei put (k,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). Testul de apartenent a unei chei date la un dictionar se poate face
fie direct prin metoda containsKey, 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", astfel nct
s se poat face cutarea la egalitate dup codul de dispersie. In exemplul urmtor
se afiseaz numrul de aparitii al fiecrui cuvnt distinct dintr-un text, folosind un
dictionar-arbore pentru afisarea cuvintelor n ordine. class FrecApar { // frecventa de
aparitie a cuvintelor intr-un text private static final Integer ONE= new Integer(1); //
o constanta public static void main (String arg[]) { Map dic = new TreeMap (); String
text = trei unu doi trei doi trei ; StringTokenizer st = new StringTokenizer (new
String (text)); String word; while ( st.hasMoreTokens()) { word = st.nextToken();
Integer nrap = (Integer) dic.get(word); if (nrap == null) dic.put (word,ONE); // prima

aparitie 7 else dic.put (word, new Integer (nrap.intValue()+1)); // alta aparitie }


System.out.println (dic); // afisare dictionar } } Cheile dintr-un dictionar pot fi
extrase ntr-o multime cu metoda keySet, iar valorile din dictionar pot fi extrase
ntr-o colectie (o list) cu metoda values.Metoda entrySet produce o multime
echivalent de perechi "cheie-valoare", unde clasa pereche are tipul Entry. Entry
este o interfat inclus n interfata Map si care are trei metode: getKey,
getValue si setValue. De observat c metodele entrySet,keySet si values
(definite n AbstractMap) creeaz doar imagini noi asupra unui dictionar si nu alte
colectii de obiecte; orice modificare n dictionar se va reflecta automat n aceste
imagini, fr ca s apelm din nou metodele respective. O imagine este creat
printr-o clas care defineste un iterator pe datele clasei dictionar si nu are propriile
sale date. Metodele clasei imagine sunt definite apoi pe baza iteratorului, care d
acces la datele din dictionar. 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, n timp ce la HashMap ordinea este aparent ntmpltoare (ea depinde de
capacitatea tabelei de dispersie, de functia hashCode si de ordinea de introducere
a cheilor). Pentru pstrarea ordinii de adugare se foloseste o list nlntuit, iar
tabelul de dispersie asigur un timp bun de regsire dup cheie. Colectii ordonate
Problema ordonrii este rezolvat diferit pentru liste fat de multimi si dictionare.
Listele sunt implicit neordonate (se adaug numai la sfrsit de list) si pot fi
ordonate numai la cerere, prin apelul unei metode statice (Collections.sort).
Multimile, ca si dictionarele, au o variant de implementare (printr-un arbore binar
ordonat) care asigur mentinerea lor n ordine dup orice adugare sau eliminare de
obiecte. Exemplu de ordonare a unei liste: public static void main (String arg[ ])
{ String tab[ ] = {"unu","doi","trei",patru,cinci}; List lista = Arrays.asList (tab);
Collections.sort (lista); System.out.println (lista); // [cinci,doi,patru,trei,unu] } Putem
s ne definim vectori sau liste ordonate automat, sau alte alte structuri compatibile
cu interfata List si care asigur ordinea (un arbore binar, de exemplu). 8 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; // adresa obiectului comparator public
SortedArray () { super();} public SortedArray (Comparator comp) { super();
cmp=comp; // retine adresa obiectului comparator } public boolean add (Object obj)
{ // adaugare la vector ordonat boolean modif= super.add(obj); // daca s-a modificat
colectia dupa adaugare if ( ! modif) return modif; // colectia nemodificata ramane
ordonata if ( cmp==null) Collections.sort (this); // ordonare dupa criteriul natural
else Collections.sort (this,cmp); // ordonare cu comparator primit din afara return
modif; } } Problema clasei anterioare este c ar trebui redefinit si metoda set
care modific elemente din list, pentru a mentine lista ordonat. Acest lucru nu
este posibil prin folosirea metodei Collections.sort deoarece aceasta apeleaz
metoda set si apare o recursivitate indirect infinit de forma set -> sort -> set ->
sort -> set -> O multime ordonat este de tipul TreeSet iar un dictionar ordonat
este de tipul TreeMap. Se pot defini si alte tipuri de colectii sau dictionare ordonate,

care implementeaz (optional) interfata SortedSet, respectiv interfata SortedMap.


Adugarea sau modificarea valorilor dintr-un arbore se face cu mentinerea ordinii si
nu necesit reordonarea multimii sau dictionarului. Obiectele introduse ntr-o
colectie TreeSet sau TreeMap trebuie s apartin unei clase care implementeaz
interfata Comparable si contine o definitie pentru metoda compareTo. Exemplu de
ordonare a unei liste de nume (distincte) prin crearea si afisarea unei multimi
ordonate: SortedSet lst = new TreeSet (lista); // sau se adauga cu metoda addAll
Iteratorul unei colectii ordonate parcurge elementele n ordinea dictat de obiectul
comparator folosit mentinerea colectiei n ordine. O problem comun colectiilor
ordonate este criteriul dup care se face ordonarea, deci functia de comparatie,
care depinde de tipul obiectelor comparate. Sunt prevzute dou solutii pentru
aceast problem, realizate ca dou interfete diferite si care au aplicabilitate
distinct. Anumite metode statice (sort, min, max s.a.) si unele metode din clase
pentru multimi ordonate apeleaz n mod implicit metoda "compareTo", parte a
interfetei Comparable. Clasele JDK cu date (String, Integer, Date s.a.)
implementeaz interfata Comparable si deci contin o metoda "compareTo" pentru o
ordonare "natural" ( exceptie face clasa Boolean, ale crei obiecte nu sunt
comparabile). 9 Ordinea natural este ordinea valorilor algebrice (cu semn) pentru
toate clasele numerice, este ordinea numeric fr semn pentru caractere si este
ordinea lexicografic pentru obiecte de tip String. Pentru alte clase, definite de
utilizatori, trebuie implementat interfata Comparable prin definirea metodei
"compareTo", dac obiectele clasei pot fi comparate (si sortate). Pentru ordonarea
dup un alt criteriu dect cel natural si pentru ordonarea dup mai mlte criterii se
va folosi o clas compatibil cu interfata Comparator. Rezultatul metodei "compare"
este acelasi cu al metodei "compareTo", deci un numr negativ dac ob1ob2. Un
argument de tip Comparator apare n constructorii unor clase si n cteva metode
din clasa Collections (sort, min, max) ce necesit compararea de obiecte. Pentru a
utiliza o functie compare trebuie definit o clas care contine numai metoda
"compare", iar un obiect al acestei clase se transmite ca argument functiei.
Exemplu de ordonare a dictionarului de cuvinte-frecvent creat anterior, n ordinea
invers a numrului de aparitii: Set entset = dic.entrySet(); ArrayList entries = new
ArrayList(entset); Collections.sort (entries, new Comp()); . . . // clasa comparator
obiecte Integer in ordine inversa celei naturale class Comp implements Comparator
{ public int compare (Object o1, Object o2) { Map.Entry e1= (Map.Entry)o1, e2=
(Map.Entry)o2; // e1,e2=prechi cheie-val return ((Integer)e2.getValue()).compareTo
((Integer) e1.getValue()); } } Clase iterator Una din operatiile frecvente asupra
colectiilor de date este enumerarea tuturor elementelor colectiei (sau a unei
subcolectii) n vederea aplicrii unei prelucrri fiecrui element obtinut prin
enumerare. Realizarea concret a enumerrii depinde de tipul colectiei si foloseste
un cursor care nainteaz de la un element la altul, ntr-o anumit ordine (pentru
colectii neliniare). Cursorul este un indice ntreg n cazul unui vector sau un pointer
(o referint) pentru o list nlntuit sau pentru un arbore binar. Pentru colectii mai
complexe, cum ar fi vectori de liste, enumerarea poate folosi indici (pentru vectori)
si pointeri (pentru noduri legate prin pointeri). 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. Orice clas colectie Java 2 poate
avea o clas iterator asociat. Pentru un acelasi obiect colectie (de ex. un vector)
pot exista mai multi iteratori, care progreseaz n mod diferit n cadrul colectiei,
pentru c fiecare obiect iterator are o variabil cursor proprie. Toate clasele iterator
rebuie s includ urmtoarele operatii: - Pozitionarea pe primul element din colectie
10 - Pozitionarea pe urmtorul element din colectie - Obtinerea elementului curent
din colectie - Detectarea sfrsitului colectiei (test de terminare a enumerrii).
Interfata Iterator contine metode generale pentru orice iterator: public interface
Iterator { boolean hasNext(); // daca exista un element urmator in colectie Object
next(); // extrage element curent si avans la urmtorul void remove(); // elimina
element curent din colectie (optional) } De observat c modificarea continutului
unei colectii se poate face fie prin metode ale clasei colectie, fie prin metoda
remove a clasei iterator, dar nu ar trebui folosite simultan ambele moduri de
modificare. In exemplul urmtor apare o exceptie de tip
ConcurrentModificationException: ArrayList a = new ArrayList(); Iterator it =
a.iterator(); while (it.hasNext()) { it.next(); it.remove(); a.add( "x"); } Pentru fiecare
clas concret de tip colectie exist o clas iterator. Un obiect iterator este singura
posibilitate de enumerare a elementelor unei multimi si o alternativ pentru
adresarea prin indici a elementelor unei liste. Un dictionar nu poate avea un
iterator, dar multimea perechilor si multimea cheilor din dictionar au iteratori.
Clasele iterator nu sunt direct instantiabile (nu au constructor public), iar obiectele
iterator se obtin prin apelarea unei metode a clasei colectie (metoda iterator). In
felul acesta, programatorul este obligat s creeze nti obiectul colectie si numai
dup aceea obiectul iterator. 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. Exemplu: public static void sort (List
list) { Object a[] = list.toArray(); // transforma lista in vector intrinsec Arrays.sort(a);
// ordonare vector intrinsec (mai eficienta) ListIterator i = list.listIterator(); for (int
j=0; j< entries.size();} public Object next() { Object e= (MEntry)entries.get(i); i++;
return e; } public void remove () { entries.remove(i-1); } } Clasa MEntry
implementeaz interfata Map.Entry si redefineste metoda equals (eventual si
metoda toString): class MEntry implements Map.Entry { private Object key,val;
public MEntry (Object k, Object v) { key=k; val=v; } public Object getKey() { return
key; } public Object getValue() { return val;} public Object setValue (Object v)
{ val=v; return v;} public boolean equals (Object obj) { return
((MEntry)obj).getKey().equals(key); 15 } public String toString() { return
key+":"+val;} } Clase colectie sablon Clasele colectie cu tipuri parametrizate au
fost introduse n versiunea 1.5, ca solutii alternative pentru colectiile de obiecte
deja existente. Exemple de declarare a unor vectori cu elemente de diferite tipuri :
ArrayList a = new ArrayList(); ArrayList b = ArrayList (100); ArrayList c = ArrayList
(n); ArrayList<LinkedList> graf; // initializata ulterior Avantajele sunt acelea c se
poate verifica la compilare dac se introduc n colectie numai obiecte de tipul
declarat pentru elementele colectiei, iar la extragerea din colectie nu mai este

necesar o conversie de la tipul generic la tipul specific aplicatiei. Exemplu cu un


vector de obiecte Integer: ArrayList list = new ArrayList(); for (int i=1;i< list.size();i+
+) { Integer val= list.get(i); // fara conversie de tip ! sum += val.intValue(); }
Exemplu de utilizare dictionar cu valori multiple pentru problema listei de referinte
ncrucisate - n ce linii dintr-un fisier text apare fiecare cuvnt distinct : public static
void main (String[ ] arg) throws IOException { Map<String,List> cref = new
TreeMap<String,List>( ); BufferedReader in = new BufferedReader (new FileReader
(arg[0])); String line, word; int nl=0; while ((line=in.readLine()) != null) { ++nl;
StringTokenizer st = new StringTokenizer (line); while ( st.hasMoreTokens() )
{ word=st.nextToken(); List lst = cref.get (word); if (lst==null) cref.put (word,
lst=new LinkedList()); lst.add (new Integer (nl)); } 16 } System.out.println (cref); }
Trecerea de la un tip primitiv la tipul clas corespunztor si invers se face automat
n versiunea 1.5, procese numite autoboxing si unboxing. Exemplu: ArrayList list
= new ArrayList(); for (int i=1;i list = new LinkedList(); for (int x=1;x extends
AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable
{ private transient E[] elementData; private int size; public E get(int index)
{ RangeCheck(index); return elementData[index]; } public boolean add(E o)
{ ensureCapacity(size + 1); // Increments modCount!! elementData[size++] = o;
return true; } public boolean contains(Object elem) { return indexOf(elem) >= 0; } .
. . 17 } Este posibil n continuare utilizarea claselor colectie ca naintea versiunii
1.5, cu elemente de tip Object. A mai fost adugat interfata Queue, cu cteva
metode noi, interfat implementat de mai multe clase, printre care AbstractQueue,
PriorityQueue si LinkedList. public interface Queue extends Collection { boolean
offer(E o); // adauga element la coada (false daca nu mai este loc) E poll(); // scoate
primul element din coada (null daca nu exista) E remove(); // scoate primul element
din coada (exceptie daca nu exista) E peek(); // primul element din coada (null daca
coada goala) E element(); // primul element din coada (exceptie daca coada
goala) } Interfetele si clasele de tip coad (ca BlockingQueue si SynchronousQueue)
au fost introduse, alturi de alte clase, n pachetul java.util.concurrent pentru
programare cu fire de executie concurente (paralele). Tot ca o noutate a aprut si o
alt colectie ordonat coada cu prioritti. Indiferent de ordinea de adugare la
coad, elementul din fat (obtinut prin metoda peek) este cel cu prioritatea
minim. Prioritatea este determinat de ctre metoda compareTo a obiectelor
introduse n coad (constructor fr argumente) sau de ctre metoda compare a
obiectului comparator transmis ca argument la construirea unei cozi. In exemplul
urmtor se adaug si se scoate dintr-o coad de arce ordonat dup costuri, n
ipoteza c metoda compareTo din clasa Arc compar costuri: PriorityQueue pq =
new PriorityQueue(); pq.add (new Arc(v,w,cost)); // adauga arc la coada pq //
afisare coada in ordinea prioritatilor (costului arcelor) while ( ! pq.isEmpty())
System.out.println( pq.remove()); // scoate si sterge din coada In clasa Arrays s-au
adugat metode statice pentru transformarea n sir de caractere a continutului unui
vector intrinsec unidimensional sau multidimensional cu elemente de orice tip
primitiv sau de tip Object. Exemplu de folosire: double [ ] a= {1.1, 2.2, 3.3, 4.4};
System.out.println ( Arrays.toString(a)); Double b[ ][ ]= { {0.0, 0.1, 0.2}, {1.0, 1.1,

1.2}, {2.0, 2.1, 2.2} }; System.out.println(Arrays.deepToString(b)); De asemenea, sau mai adaugat ctiva algoritmi generici pentru colectii: int frequency(Collection c,
Object o); // numara aparitiile obiectului o in colectie boolean disjoint(Collection c1,
Collection c2); // daca colectiile sunt disjuncte 18 Comparator
reverseOrder(Comparator cmp); // comp. pentru ordine inversa Din versiunea 1.5 sa introdus tipul de date definit prin enumerare (si cuvntul cheie enum), precum si
dou colectii performante pentru elemente de un tip enumerat: EnumSet si
EnumMap (implementate prin vectori de biti). 1 8. Reutilizarea codului n POO
Reutilizarea codului prin compunere Unul din avantajele programrii orientate pe
obiecte este posibilitatea de reutilizare simpl si sigur a unor clase existente n
definirea altor clase, fr a modifica clasele initiale. Metodele de reutilizare a
codului sunt compozitia si derivarea. O clas compus contine ca membri referinte
la obiecte din alte clase. Agregarea unor obiecte de tipuri deja definite ntr-un nou
tip de obiect se impune de la sine acolo unde, n programarea clasic, se definesc
tipuri structur (nregistrare). In exemplul urmtor, clasa "Pers" corespunde unei
persoane, pentru care se memoreaz numele si data nasterii. Fiecare obiect de tip
"Pers" va contine (pointeri la) un obiect de tip String si un obiect de tip Date. public
class Pers { private String nume; // nume persoana private Date nascut; // data
nasterii Pers (String nume, int zi, int luna, int an) { this.nume= nume; this.nascut=
new Date(luna,zi,an); } public String toString () { return nume + +
nascut.toString();} public Date getBDate () { return nascut;} } Obiectele de tip
Pers pot fi memorate ntr-o colectie ca orice alte obiecte derivate din Object: class
VecPers { // creare si afisare vector de persoane public static void main (String[ ] a)
{ Vector lista = new Vector(); lista.addElement ( new Pers ("unu",24,11,89));
lista.addElement ( new Pers ("doi",1,11,99)) ; System.out.println (lista); } } Pentru
clase ale cror obiecte se memoreaz n colectii trebuie redefinite metodele
"equals", "hashCode" si, eventual, "compareTo" pentru colectii ordonate (sortabile).
Uneori clasa agregat A contine un singur obiect dintr-o alt clas B, iar motivul
acestei relatii este reutilizarea functionalittii clasei B n noua clas A, adic
folosirea unor metode ale clasei B si pentru obiecte ale clasei A. Interfetele publice
ale claselor A si B trebuie s fie destul de diferite, pentru a justifica compozitia n
locul derivrii. In exemplul urmtor se defineste o clas pentru stive realizate ca
liste nlntuite de obiecte, cu preluarea functiilor de la obiectul continut (de tip
LinkedList): 2 public class LinkedStack { private LinkedList stack; // stiva lista (obiect
continut) public LinkedStack () { // constructor stack= new LinkedList(); } public
Object push (Object obj) { // metoda clasei LinkedStack stack.addFirst (obj); return
obj; // foloseste metoda clasei LinkedList } public Object pop () { return
stack.removeFirst(); } public boolean isEmpty () { // metode cu acelasi nume si tip
return stack.isEmpty(); } public String toString () { return stack.toString(); } } De
observat c tipurile LinkedStack si LinkedList nu sunt compatibile si nu se pot face
atribuiri ntre ele, desi continutul claselor este att de asemntor. Varianta definirii
clasei "LinkedStack" ca o subclas a clasei LinkedList este preferabil aici, mai ales
c dou dintre metode pot fi mostenite ca atare ("isEmpty" si "toString"). In
exemplul urmtor se defineste o clasa dictionar cu valori multiple, n care fiecare

cheie are asociat o lista de valori. Clasa preia o mare parte din functionalitatea
clasei HashMap, prin delegarea unor operatii ctre metode din clasa HashMap.
public class MultiMap { HashMap m = new HashMap(); public void put (Object key,
List val) { m.put (key, val); // apel HashMap.put } public List get (Object key)
{ return (List) m.get(key); // apel HashMap.get } public String toString () { String
str=""; Iterator ik=m.keySet().iterator(); // apel HashMap.keySet while
(ik.hasNext() ) { List lst = get(ik.next()); // lista de valori a unei chei str = str+ key +
" : " + lst.toString() + "\n"; } return str; } public Set keySet () { return
m.keySet(); } } 3 Reutilizarea codului prin derivare Solutia specific POO de
adaptare a unei clase A la alte cerinte este definirea unei clase D, derivat din clasa
A, si care modific functionalitatea clasei A. Nu se accept modificarea codului
clasei A de ctre fiecare
ilizator care constat necesitatea unor modificri, chiar dac acest cod surs este
disponibil. Prin derivare se face o adaptare sau o specializare a unei clase mai
generale la anumite cerinte particulare fr a opera modificri n clasa initial.
Extinderea unei clase permite reutilizarea unor metode din superclas, fie direct, fie
dup "ajustri" si "adaptri" cerute de rolul subclasei. Superclasa transmite
subclasei o mare parte din functiile sale, nefiind necesar rescrierea sau apelarea
metodelor mostenite. Vom relua exemplul clasei pentru stive realizate ca liste
nlntuite. Operatiile cu o stiv se numesc traditional push si pop, dar clasa
LinkedList foloseste alte nume pentru operatiile respective. De aceea, vom defini
dou metode noi: public class LinkedStack extends LinkedList { public Object push
(Object obj) { addFirst (obj); return obj; } public Object pop () { return removeFirst();
} } De observat c subclasa LinkedStack mosteneste metodele "toString",
"isEmpty" si altele din clasa LinkedList. De asemenea, exist un constructor implicit
care apeleaz constructorul superclasei si care initializeaz lista stiv. O problem
n acest caz ar putea fi posibilitatea utilizatorilor de a folosi pentru obiecte
"LinkedStack" metode mostenite de la superclas, dar interzise pentru stive: citire si
modificare orice element din stiv, cutare n stiv s.a. Solutia este de a redefini
aceste metode n subclas, cu efectul de aruncare a unor exceptii. Exemplu: public
Object remove (int index) { throw new NoSuchMethodError(); } Iat si o alt solutie
de definire clasei "MultiMap" (dictionar cu valori multiple), 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 pstra interfata public a clasei HashMap: public class
MultiMap extends HashMap { public Object put (Object key, Object val) { List lst =
(List) get(key); // extrage lista de valori asociata cheii key List result=lst; // rezultatul
va fi lista anterioara if (lst==null) // daca cheia key nu exista in dictionar super.put
(key, lst=new ArrayList()); // se introduce o pereche cheie-lista 4 lst.add (val); //
adauga prima valoare la lista return result; // lista anterioara de valori } } Toate
celelalte metode sunt mostenite ca atare de la superclasa HashMap : get, iterator,
toString. Dac dorim s modificam modul de afisare a unui dictionar atunci putem
redefini metoda toString din clasa MultiMap, ca n exemplul anterior.
Comparatie ntre compozitie si derivare Compozitia (un B contine un A) se

recomand atunci cnd vrem s folosim (s reutilizm) functionalitatea unei clase A


n cadrul unei clase B, dar interfetele celor dou clase sunt diferite. Derivarea (un B
este un fel de A) se recomand atunci cnd vrem s reutilizm o mare parte din
(sau toat) interfata clasei A si pentru clasa B. Aceast cerint poate fi motivat
prin crearea de tipuri compatibile A si B: putem s nlocuim o variabil sau un
argument de tipul A printr-o variabil (sau argument) de tipul B ("upcasting"). Att
derivarea ct si compozitia permit reutilizarea metodelor unei clase, fie prin
mostenire, fie prin delegare (prin apelarea metodelor obiectului continut). De cele
mai multe ori metoda de reutilizare a unor clase se impune de la sine, dar uneori
alegerea ntre compozitie si derivare nu este evident si chiar a condus la solutii
diferite n biblioteci de clase diferite. Un exemplu este cel al claselor pentru vectori
si respectiv pentru stive. 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,
desi un obiect stiv nu foloseste metodele clasei Vector ( cu exceptia metodei
"toString"). Mai mult, nici nu se recomand accesul direct la orice element dintr-o
stiv (prin metodele clasei Vector). Exemplul urmtor defineste o clas "Stiva" care
contine un obiect de tip Vector: public class Stiva { private Vector items; // vector
folosit ca stiva public Stiva() { // un constructor items = new Vector(10); } public
Object push(Object item) { // pune un obiect pe stiva items.addElement(item);
return item; } public Object pop() { // scoate obiect din varful stivei int n =
items.size(); // n = nr de elemente in stiva if ( n ==0 ) return null; Object obj =
items.elementAt (n - 1); items.removeElementAt (n - 1); return obj; } 5 public
boolean isEmpty() { // daca stiva goala return (items.size() == 0) } public String
toString () { return items.toString(); } } Clasele Stiva si java.util.Stack sunt
numite si clase adaptor pentru c fac trecerea de la un set de metode publice
(cele ale clasei Vector) la un alt set de metode publice (push, pop etc.). Clasa
Stack este numit si dublu-adaptor (Two-way Adapter), pentru c permite fie
folosirea metodelor superclasei Vector, fie folosirea metodelor specifice clasei Stack
(push, pop s.a.). Relatia de compunere dintre obiecte poate fi o relatie
dinamic, modificabil la executie, spre deosebire de relatia static de derivare,
stabilit la scrierea programului si care nu mai poate fi modificat. O clas compus
poate contine o variabil de un tip interfat sau clas abstract, care poate fi
nlocuit la executie (la construirea unui obiect, de exemplu) printr-o referint la un
obiect de un alt tip, compatibil cu tipul variabilei din clas. Vom relua exemplul cu
stiva adugnd un grad de generalitate prin posibilitatea utilizatorului de a-si alege
tipul de list folosit pentru stiv (vector sau lista nlntuit): public class StackList
{ private List stack; // adresa vector sau lista inlantuita public StackList (List list) { //
constructor stack=list; // retine adresa obiect stiva } public Object push (Object obj)
{ stack.add (0,obj); return obj; } . . . // alte metode } La construirea unui obiect
StackList trebuie precizat tipul de list folosit (vector sau altceva): StackList st1 =
new StackList (new ArrayList()); // vector StackList st2 = new StackList (new
LinkedList()); // lista inlantuita Un alt exemplu este cel al claselor flux de date cu
sum de control, care contin o variabil interfat (Checksum), care va primi la
instantiere adresa obiectului ce contine metoda de calcul a sumei de control.

Legtura dintre un obiect flux si obiectul de calcul a sumei este stabilit la executie
si asigur o flexibilitate sporit. Exemplu: 6 public class CheckedOutputStream
extends FilterOutputStream { private Checksum cksum; // adresa obiect cu metoda
de calcul suma control public CheckedOutputStream(OutputStream out, Checksum
cksum) { super(out); this.cksum = cksum; } public void write(int b) throws
IOException { out.write(b); cksum.update(b); // metoda din interfata
Checksum } . . . } Exemplu de utilizare a clasei anterioare: CRC32 Checker = new
CRC32(); // clasa care implem. interfata Checksum CheckedOutputStream out; out =
new CheckedOutputStream (new FileOutputStream("date"), Checker); while
(in.available() > 0) { int c = in.read(); out.write(c); } Mostenire multipl prin
derivare si compozitie In Java o subclas nu poate avea dect o singur superclas,
dar uneori este necesar ca o clas s preia functii de la dou sau mai multe clase
diferite. Implementarea mai multor interfete de ctre o clas nu este o solutie
pentru mostenirea multipl de functii. 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; metodele din
M fie apeleaz metode din clasa B, fie sunt mostenite de la clasa A. 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 { void f1 () { System.out.println ("A.f1"); } }
class B { void f2 () { System.out.println ("B.f2"); } } class C { void f3 ()
{ System.out.println ("C.f3"); } } class M extends C { A a = new A (); // initializarea
var. a si b se poate face intr-un constructor B b = new B (); void f1 () { a.f1();} //
delegare obiect a pentru operatia f1 void f2 () { b.f2();} // delegare obiect b pentru
operatia f2 } class X { 7 public static void main (String arg[]) { M m = new M();
m.f1(); m.f2(); m.f3(); } } Un exemplu real de mostenire multipl poate fi o clas
pentru o multime realizat ca vector, care extinde clasa AbstractSet si contine o
variabil de tip ArrayList : public class ArraySet extends AbstractSet { private
ArrayList set; public ArraySet() { set = new ArrayList(); } public boolean add (Object
obj) { if (! set.contains(obj) ) return set.add(obj); // delegare pentru operatia de
adaugare return false; } public Iterator iterator() { return set.iterator(); // delegare
pentru creare obiect iterator } public int size() { return set.size(); } } Pentru multimi
de tipul "ArraySet" se pot folosi toate metodele mostenite de la clasa AbstractSet:
toString, contains, containsAll, addAll, removeAll, retainAll s.a. Aceeasi solutie de
mostenire multipl este folosit n cteva clase JFC (Swing) de tip model; de
exemplu, clasa DefaultListModel preia metode de la superclasa AbstractListModel si
deleag unei variabile interne de tip Vector operatii cu un vector de obiecte. Un
model de list este un vector cu posibilitti de generare evenimente (de apelare
receptori) la modificri operate n vector. Combinarea compozitiei cu derivarea
Derivarea pstreaz interfata clasei de baz, iar agregarea permite mai mult
flexibilitate la executie. Combinarea agregrii cu derivarea mbin avantajele
ambelor metode de reutilizare: mostenirea interfetei si posibilitatea modificrii
obiectului interior din obiectul unei clase agregat. Uneori variabila din subclas este
chiar de tipul superclasei. Clasele ce contin un obiect de acelasi tip sau de un tip
compatibil cu al superclasei sale se mai numesc si clase anvelop (wrapper),
deoarece adaug functii obiectului continut, ca un ambalaj pentru acel obiect. O

clasa anvelop este numit si clas decorator, deoarece "decoreaz" cu noi functii
o clas existent. 8 De exemplu, putem defini o clas stiv mai general, care s
poat folosi fie un vector, fie o list nlntuit, dup cum doreste programatorul.
Clasa anvelop care urmeaz este compatibil cu tipul List si, n acelasi timp,
foloseste metode definite n clasa AbstractList : class StackList extends AbstractList
{ private AbstractList stack; // adresa obiect stiva public StackList (List list) { //
constructor stack=(AbstractList)list; // retine adresa obiect stiva } public Object
push (Object obj) { stack.add (0,obj); return obj; } public Object pop () { Object
obj= get(0); stack.remove(obj); return obj; } public int size() { return
stack.size(); } } Exemple de combinare a derivrii si compozitiei se gsesc n clasa
Collections, pentru definirea de clase colectie cu functionalitate putin modificat
fat de colectiile uzuale, dar compatibile cu acestea ca tip. Primul grup de clase
este cel al colectiilor nemodificabile, care difer de colectiile generale prin
interzicerea operatiilor ce pot modifica continutul colectiei. Exemplu: class
UnmodifiableCollection implements Collection, Serializable { Collection c; // metode
care nu modifica continutul colectiei public int size() {return c.size();} public
boolean isEmpty() {return c.isEmpty();} public boolean contains(Object o) {return
c.contains(o);} public String toString() {return c.toString();} . . . // metode care ar
putea modifica continutul colectiei public boolean add (Object o){ throw new
UnsupportedOperationException(); } public boolean remove (Object o) { throw new
UnsupportedOperationException(); } . . . } Incercarea de adugare a unui nou obiect
la o colectie nemodificabil produce o exceptie la executie; dac nu se defineau
metodele "add", "remove" s.a. n clasa 9 derivat, atunci apelarea metodei "add"
era semnalat ca eroare la compilare. Exceptia poate fi tratat fr a mpiedica
executia programului. Al doilea grup de clase sunt clasele pentru colectii
sincronizate. Exemplu: class SynchronizedCollection implements Collection,
Serializable { Collection c; // colectia de baza public int size() { synchronized(this)
{return c.size();} } public boolean add(Object o) { synchronized(this) {return
c.add(o);} } . . . // alte metode } In realitate, 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;
UnmodifiableList(List list) { super(list); this.list = list; } . . . // alte metode } public
static List unmodifiableList (List list) { return new UnmodifiableList(list); } Exemple
de utilizare a claselor colectie "speciale" : String kw[] ={"if", "else", "do","while",
"for"}; List list = Collections.unmodifiableList (Arrays.asList(kw)); Set s =
Collections.synchronizedSet (new HashSet()); Clase decorator de intrare-iesire
Combinarea derivrii cu delegarea a fost folosit la proiectarea claselor din pachetul
java.io. Exist dou familii de clase paralele : familia claselor flux (Stream) cu
citire-scriere de octeti si familia claselor Reader-Writer, cu citirescriere de
caractere. Numrul de clase instantiabile de I/E este relativ mare deoarece sunt
posibile diverse combinatii ntre suportul fizic al fluxului de date si facilittile oferite
de fluxul respectiv. Toate clasele flux de intrare sunt subtipuri ale tipului
InputStream (Reader) si toate clasele flux de iesire sunt subtipuri ale tipului
OutputStream (Writer). Clasele Reader, Writer si celelalte sunt clase abstracte. 10

Dup suportul fizic al fluxului de date se poate alege ntre: - Fisiere disc :
FileInputStream, FileOutputStream, FileReader, FileWriter s.a. - Vector de octeti sau
de caractere : ByteArrayInputStream, ByteArrayOutputStream CharArrayReader,
CharArrayWriter. - Buffer de siruri (n memorie): StringBufferInputStream,
StringBufferOutputStream. - Canal pentru comunicarea sincronizat ntre fire de
executie : PipedInputStream, PipedOutputStream, PipedReader, PipedWriter. Dup
facilittile oferite avem de ales ntre: - Citire-scriere la nivel de octet sau bloc de
octeti (metode read, write). - Citire-scriere pe octeti dar cu zon buffer:
BufferedInputStream, BufferedReader, BufferedOutputStream, BufferedWriter. Citire-scriere la nivel de linie si pentru numere de diferite tipuri (fr conversie):
DataInputStream, DataOutputStream.. - Citire cu punere napoi n flux a ultimului
octet citit (PushBackInputStream). - Citire nsotit de numerotare automat a liniilor
citite (LineNumberInputStream). Combinarea celor 8 clase surs/destinatie cu
optiunile de prelucrare asociate transferului de date se face prin intermediul claselor
anvelop , care sunt numite si clase filtru de intrare-iesire. Dac s-ar fi utilizat
derivarea pentru obtinerea claselor direct utilizabile atunci ar fi trebuit generate,
prin derivare, combinatii ale celor 8 clase cu cele 4 optiuni, deci 32 de clase
(practic, mai putine, deoarece unele optiuni nu au sens pentru orice flux). Pentru a
folosi mai multe optiuni cu acelasi flux ar fi trebuit mai multe niveluri de derivare si
deci ar fi rezultat un numr si mai mare de clase. Solutia claselor anvelop permite
s se adauge unor clase de baz diverse functii n diferite combinatii. Principalele
clase filtru de intrare-iesire sunt: FilterInputStream, FilterOutputStream si
FilterReader, FilterWriter. Clasele de tip filtru sunt clase intermediare, din care sunt
derivate clase care adaug operatii specifice (de prelucrare): citire de linii de text
de lungime variabil, citire-scriere de numere n format intern, scriere numere cu
conversie de format s.a. O clas anvelop de I/E contine o variabil de tipul abstract
OutputStream sau InputStream, care va fi nlocuit cu o variabil de un tip flux
concret (FileOutputStream, ...), la construirea unui obiect de un tip flux direct
utilizabil. Clasa decorator FilterInputStream este derivat din InputStream si, n
acelasi timp, contine o variabil de tip InputStream: public class FilterInputStream
extends InputStream { protected InputStream in; protected FilterInputStream
(InputStream in) { // constructor (in clasa abstracta) this.in=in; // adresa obiectului
"flux" } // citirea unui octet public int read () throws IOException { return in.read
(); // citirea depinde de tipul fluxului } ... // alte metode } 11 Metoda "read" este o
metod polimorfic, iar selectarea metodei necesare se face n functie de tipul
concret al variabilei "in" (transmis ca argument constructorului). Nu se pot crea
obiecte de tipul FilterInputStream deoarece constructorul clasei este de tip
protected. In schimb, se pot crea obiecte din subclase ale clasei FilterInputStream.
Clasele DataInputStream, BufferedInputStream, LineNumberInputStream si
PushbackInputStream sunt derivate din clasa FilterInputStream si sunt clasele de
prelucrare a datelor citite. Cea mai folosit este clasa DataInputStream care adaug
metodelor de citire de octeti mostenite si metode de citire a tuturor tipurilor
primitive de date: "readInt", "readBoolean", readFloat", "readLine", etc. La crearea
unui obiect de tipul DataInputStream constructorul primeste un argument de tipul

InputStream, sau un tip derivat direct din InputStream, sau de un tip derivat din
FilterInputStream. Pentru a citi linii dintr-un fisier folosind o zon tampon, cu
numerotare de linii vom folosi urmtoarea secvent de instructiuni: public static
void main (String arg[]) throws IOException { FileInputStream fis= new
FileInputStream (arg[0]); BufferedInputStream bis = new BufferedInputStream (fis);
LineNumberInputStream lnis= new LineNumberInputStream (bis); DataInputStream
dis = new DataInputStream (lnis); String linie; while ( (linie=dis.readLine()) != null)
System.out.println (lnis.getLineNumber()+" "+linie); } De obicei nu se mai folosesc
variabile intermediare la construirea unui obiect flux. Exemplu de citire linii , cu
buffer, dintr-un fisier disc: public static void main (String arg[ ]) throws IOException
{ DataInputStream dis = new DataInputStream ( new BufferedInputStream (new
FileInputStream (arg[0]))); String linie; while ( (linie=dis.readLine()) != null)
System.out.println ( linie); } Ordinea n care sunt create obiectele de tip
InputStream este important : ultimul obiect trebuie s fie de tipul
DataInputStream, pentru a putea folosi metode ca "readLine" si altele. Si familia
claselor Reader-Writer foloseste clase decorator: public abstract class FilterReader
extends Reader { protected Reader in; protected FilterReader(Reader in) { super(in);
this.in = in; } public int read() throws IOException { return in.read(); } public int
read(char cbuf[ ], int off, int len) throws IOException { return in.read(cbuf, off,
len); } public void close() throws IOException { 12 in.close(); } } Clasele PrintStream
si PrintWriter adaug claselor filtru metode pentru scriere cu format (cu conversie)
ntr-un flux de iesire, metode cu numele print sau println. Constanta
System.out este de tipul PrintStream si corespunde ecranului, iar constanta
System.in este de un subtip al tipului InputStream si corespunde tastaturii. Clasa
BufferedReader adaug clasei Reader o metod readLine pentru a permite citirea
de linii din fisiere text. Clasele InputStreamReader si OutputStreamWriter sunt clase
adaptor ntre clasele Stream si clasele Reader-Writer. Clasele adaptor extind o
clas Reader (Writer) si contin o variabil de tip InputStream (OutputStream); o
parte din operatiile impuse de superclas sunt realizate prin apelarea operatiilor
pentru variabila flux (prin delegare). Este deci un alt caz de combinare ntre
extindere si agregare. Un obiect InputStreamReader poate fi folosit la fel ca un
obiect Reader pentru citirea de caractere, dar n interior se citesc octeti si se
convertesc octeti la caractere, folosind metode de conversie ale unei clase
convertor. Codul urmtor ilustreaz esenta clasei adaptor, dar sursa clasei prevede
posibilitatea ca un caracter s fie format din doi sau mai multi octeti (conversia se
face pe un bloc de octeti): public class InputStreamReader extends Reader { private
ByteToCharConverter btc; private InputStream in; private
InputStreamReader(InputStream in, ByteToCharConverter btc) { super(in); this.in =
in; this.btc = btc; } public InputStreamReader(InputStream in) { this(in,
ByteToCharConverter.getDefault()); } public int read() throws IOException { int byte
= in.read(); // citire octet char ch = btc.convert(byte); // conversie din byte in char
return ch; // caracter coresp. octetului citit } . . . // alte metode } Exemplu de
utilizare a unei clase adaptor pentru citire de linii de la tastatur : public static void
main (String arg[ ]) throws IOException { BufferedReader stdin = new

BufferedReader(new InputStreamReader (System.in)); String line; while


( (line=stdin.readLine()) != null) { // dac nu s-a introdus ^Z (EOF)
System.out.println (line); } 13 } 1 9. Clase incluse Clase incluse O clas Java poate
contine, ca membri ai clasei, alte clase numite clase incluse (nested classes). In
cazul unei singure clase incluse, structura va arta astfel: public class Outer { . . . //
date si/sau metode ale clasei Outer public class Inner { . . . // date si/sau metode ale
clasei Inner } . . . // alti membri ai clasei Outer } Un bun exemplu este o clas
iterator inclus n clasa colectie pe care actioneaz. In acest fel clasa iterator are
acces la variabile private ale colectiei, nu poate fi instantiat direct ci numai prin
intermediul clasei (nu poate exista obiect iterator fr o colectie), fiind ascuns altor
clase. Exemplu de iterator pe vector, clas interiar: public class Vector extends
AbstractList implements List, Cloneable { protected int count; // nr de elemente in
vector protected Object elementData[]; // vector de 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; // indice element curent din enumerare public boolean
hasMoreElements() { return count < elementCount; } public Object nextElement() {
if (count < elementCount) return elementData[count++]; } } // . . . alte metode din
clasa Vector } Ca orice alt membru, clasa inclus poate fi declarat public sau
private si poate fi static (cu existent independent de instantele clasei
exterioare). O clas inclus nestatic este numit si clas interioar (inner class),
pentru c fiecare obiect din clasa exterioar va include un obiect din clasa
interioar. O alt form de clas interioar este o clas definit ntr-o metod a
clasei externe. Exemplu de clas pentru un obiect comparator inclus n functia de
ordomare: 2 // ordonarea unui dictionar n ordinea valorilor asociate cheilor static
void sortByValue (Map m) { // clasa inclusa class Comp implements Comparator { //
compara doua perechi cheie-val public int compare (Object o1, Object o2)
{ Map.Entry e1= (Map.Entry)o1; // o pereche cheie-valoare Map.Entry e2=
(Map.Entry)o2; // alta pereche cheie-valoare return
((Integer)e2.getValue()).intValue() - // compara valori ((Integer)
e1.getValue()).intValue(); } } Set eset = m.entrySet(); // multime de perechi cheievaloare ArrayList entries = new ArrayList(eset); // vector de perechi cheie-valoare
Collections.sort (entries, new Comp()); // ordonare vector System.out.println
(entries); // afisare perechi ordonate dupa valori } Toate aceste clase sunt definite
explicit, folosind cuvntul class. In plus, se pot defini ad-hoc clase incluse anonime,
pe baza unei alte clase sau a unei interfete (prin extindere sau prin implementare
implicit, deoarece nu se folosesc cuvintele extends sau implements). Motivele
definirii de clase interioare pot fi diverse: - Pentru clase de interes local : clasa
interioar este necesar numai clasei exterioare. - Pentru reducerea numrului 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). - Pentru
a permite clasei exterioare accesul la membri private ai clasei interioare. - Pentru a
permite claselor interioare accesul la variabile ale clasei exterioare si deci o
comunicare mai simpl ntre clasele incluse (prin variabile externe lor). - Pentru ca o

clas s poat mosteni functii de la cteva clase (mostenire multipl). Clase incluse
cu nume Clasele incluse cu nume primesc de la compilator un nume compus din
numele clasei exterioare, caracterul $ si numele clasei interioare. Clasele care nu
sunt incluse n alte clase se numesc clase de nivel superior (top-level classes). In
exemplul urmtor o clas pentru un vector ordonat ("SortedArray") contine o
variabil comparator ("cmp"), care poate fi initializat de un constructor al clasei.
Dac se foloseste constructorul fr argumente, atunci variabila comparator
primeste o valoare implicit, ca referint la un obiect comparator dintr-o clas
interioar. Clasa "DefaultComp" nu mai trebuie definit de utilizatori si transmis
din afar, ea este util numai clasei n care este definit (clasa exterioar
"SortedArray"): public class SortedArray extends ArrayList { // clasa interioara
private class DefaultComp implements Comparator { 3 public int compare (Object
e1, Object e2) { Comparable c1=(Comparable)e1; Comparable c2=(Comparable)e2;
return c1.compareTo(c2); } } // alti membri ai clasei SortedArray Comparator
cmp=new DefaultComp(); // comparator implicit public SortedArray () { super();}
public SortedArray (Comparator comp) { super(); cmp=comp; } public boolean add
(Object obj) { int k=indexOf(obj); if (k < 0) k= -k-1; super.add(k, obj); return true; }
public int indexOf (Object obj) { return Collections.binarySearch (this,obj,cmp); } In
exemplul urmtor clasa inclus Entry este folosit numai n definitia clasei
ArrayMap. Clasa inclus Entryimplementeaz o interfat inclus (interfata Entry
inclus n interfata Map este public si deci utilizabil din orice alt clas): public
class ArrayMap extends AbstractMap { // clasa interioara static class Entry
implements Map.Entry { private Object key,val; public Entry (Object k, Object v)
{ key=k; val=v; } public String toString() { return key+"="+val;} public Object
getKey() { return key; } public Object getValue() { return val;} public Object
setValue (Object v) { val=v; return v;} } // alti membri ai clasei ArrayMap private
Vector entries ; // perechi cheie-valoare public ArrayMap (int n) { // constructor
entries = new Vector(n); } public Object put ( Object key, Object value)
{ entries.addElement (new Entry(key,value)); return key; } public Set entrySet ()
{ HashSet set = new HashSet(); 4 for (int i=0;i n) return null; Node p=head.next; //
var. din clasa inclusa for (int i=0;i< 21) { r= ((int)(Math.random() * 2)); 7 switch (r)
{ case 0: p.put(new Integer(k)); k++; break; // activare producator case 1:
System.out.println(c.get()); break; // activare consumator } } Pentru comparatie
urmeaz solutia cu clase interioare. Clasele incluse au fost declarate statice pentru
a putea fi instantiate din metoda static main. class Simulare { static class
Producer { // clasa inclusa public void run() { q.add(new Byte(k)); k++; } } static
class Consumer { // clasa inclusa public void run() { if ( ! q.isEmpty())
System.out.println("Consumator " + " scoate " + q.del()); } } static Queue q ; //
obiectul coada static Producer p ; // proces producator static Consumer c ; // proces
consumator static byte k=1; // simulare procese producator-consumator public
static void main(String[] args) { q= new Queue(); p=new Producer(); c=new
Consumer(); while ( k <=20) switch ((int)(Math.random()*2)) { case 0: p.run();
break; case 1: c.run(); break; } } } Clase interioare anonime Uneori numele unei
clase incluse apare o singur dat, pentru a crea un singur obiect de acest tip. In

plus, clasa inclus implementeaz o interfat sau extinde o alt clas si contine
numai cteva metode scurte. Pentru astfel de situatii se admite definirea ad-hoc de
clase anonime, printr-un bloc care urmeaz operatorului new cu un nume de
interfat sau de clas abstract. Sintaxa definirii unei clase anonime este
urmtoarea: new Interf ( ) { ... // definitie clasa inclusa } ; 8 unde "Interf" este un
nume de interfat (sau de clas abstract sau neabstract) din care este derivat
(implicit) clasa inclus anonim. O astfel de clas nu poate avea un constructor
explicit si deci nu poate primi date la construirea unui obiect din clasa anonim. 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). Exemplu de
sortare a unei liste de obiecte n ordine descresctoare : Collections.sort (list, new
Comparator( ) { // ordonare descrescatoare public int compare (Object t1, Object t2)
{ // incepe definitia clasei anonime Comparable c1=(Comparable)t1,
c2=(Comparable)t2; return - c1.compareTo(c2); // rezultat invers metodei
compareTo } }); // aici se termina definitia clasei si instructiunea 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, Object e2) { Comparable c1=(Comparable)e1;
Comparable c2=(Comparable)e2; return c1.compareTo(c2); } }; public SortedArray
() { super();} public SortedArray (Comparator comp) { super(); cmp=comp; } public
int indexOf (Object obj) { return Collections.binarySearch (this,obj,cmp); } ... // alte
metode } Iat si o alt definitie a metodei iterator din clasa SimpleList, n care
clasa iterator pentru liste este anonim si este definit n expresia care urmeaz lui
new. public Iterator iterator () { return new Iterator() { // definirea clasei anonime
iterator ca o clasa inclusa private Node pos=head.next; public boolean hasNext ()
{ return pos != null; } public Object next() { Object obj =pos.val; pos=pos.next;
return obj; } 9 public void remove () { } }; // sfrsit 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, dar pot apare
dificultti la ntelegerea codului si erori de utilizare a acoladelor si parantezelor.
Intre clasa interioar si clasa exterioar exist un "cuplaj" foarte strns; 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). 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().iterator(); public boolean hasNext() { return
i.hasNext(); } public Object next() { return ((Entry)i.next()).getValue(); } public void
remove() { i.remove(); } }; // aici se termina instr. return new Iterator } public int
size() { // din subclasa lui AbstractCollection return AbstractMap.this.size(); // this =
obiect din clasa anonima } public boolean contains(Object v) { return
AbstractMap.this.containsValue(v); } }; // aici se termina instr. values= new }

return values; } } Mostenire multipl prin clase incluse 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. Aceste mosteniri
se pot combina n obiecte ale clasei exterioare, pentru care se pot folosi metode
mostenite de toate clasele incluse. Exemplu: class A { void f1 ()
{ System.out.println ("A.f1"); } } 10 class B { void f2 () { System.out.println ("B.f2");
} } class C { void f3 () { System.out.println ("C.f3"); } } class M extends C { class
AltA extends A { } class AltB extends B { } void f1 () { new AltA().f1(); } void f2 ()
{ new AltB().f2(); } } class X { public static void main (String arg[]) { M m = new
M(); m.f1(); m.f2(); m.f3(); } } Pentru clasa M se pot apela functii mostenite de la
clasele A, B si C la care se pot aduga si alte functii suplimentare. Clasele "AltA" si
"AltB" nu sunt folosite dect n functiile "f1" si "f2", deci am putea folosi clase
incluse anonime astfel: class M extends C { A makeA() { return new A() {}; } void f1
() { makeA().f1(); } B makeB() { return new B() {}; } void f2 () { makeB().f2(); } }
Acelasi efect se poate obtine si prin combinarea derivrii cu compozitia: class M
extends C { A a = new A (); B b = new B (); void f1 () { a.f1();} void f2 () { b.f2();} }
Solutia claselor incluse ofer n plus posibilitatea folosirii unor obiecte de tip M la
apelarea unor functii cu argumente de tip A sau B (si C, superclasa lui M): class X
{ static void useA (A a) { a.f1(); } // functie cu argument de tip A static void useB (B
b) { b.f2(); } // functie cu argument d tip B static void useC (C c) { c.f3(); } // functie
cu argument d tip B public static void main (String arg[ ]) { M m = new M(); m.f1();
m.f2(); m.f3(); useA (m.makeA()); useB (m.makeB()); useC (m); 11 } } Probleme
asociate claselor incluse O clas inclus ntr-un bloc poate folosi variabile locale din
acel bloc, inclusiv argumente formale ale functiei definite prin blocul respectiv. Orice
variabil sau parametru formal folosit ntr-o clas inclus trebuie declarat cu
atributul final, deoarece aceast variabil este copiat n fiecare instant a clasei
incluse si toate aceste copii trebuie s aib aceeasi valoare (nemodificat n cursul
executiei). Exemplul urmtor este o functie similar functiei iterator dintr-o clas
colectie, dar itereaz pe un vector intrinsec de obiecte. 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. // functie care produce un iterator
pentru vectori intrinseci public static Iterator arrayIterator (final Object a[] ) { //
clasa interioara functiei class AI implements Iterator { int i=0; public boolean
hasNext() { return i < a.length; } public Object next() { return a[i++]; } public void
remove() { } // neimplementata } return new AI(); } Clasa AI poate s fie definit ca
o clas inclus anonim deoarece acest nume nu este folosit n afara functiei
arrayIterator. O clas definit ntr-un bloc nu este membr a clasei ce contine acel
bloc si nu este vizibil din afara blocului. Exemplu de clas anonim definit ntr-un
bloc: // iterator pentru vectori intrinseci public static Iterator arrayIterator (final
Object a[] ) { return new Iterator () { // urmeaza definitia clasei anonime int i=0;
public boolean hasNext() { return i < a.length; } public Object next() { return a[i+
+]; } public void remove() { throw new UnsupportedOperationException();} 12 }; //
aici se termina instr. return } O clas anonim nu poate avea un constructor explicit
si poate fie s extind o alt clas, fie s implementeze o interfat (ca n exemplul

anterior) cu aceeasi sintax. Un nume de interfat poate urma cuvntului cheie new
numai la definirea unei clase anonime care implementeaz acea interfat, iar lista
de argumente trebuie s fie vid. O clas anonim nu poate simultan s extind o
alt clas si s implementeze o interfat, sau s implementeze mai multe interfete.
Numele claselor incluse anonime sunt generate de compilator prin adugarea la
numele clasei exterioare a caracterului $si a unui numr (a cta clas inclus
este). Variabilele locale din clasa exterioar sau din blocul exterior sunt copiate de
compilator n cmpuri private ale clasei interioare, cu nume generate automat de
compilator si transmise ca argumente constructorului clasei interioare. Pentru
functia anterioar compilatorul Java genereaz un cod echivalent de forma
urmtoare: public static Iterator arrayIterator (final Object a[]) { return new
C$1(a); // C este numele clasei ce contine metoda } class C$1 implements Iterator {
private Object val$a[]; int i; OuterClass$1 (Object val$a[]) { // constructor this.val$a
= val$a; i=0; } public boolean hasNext () { return i < val$a.length; } public Object
next() { return val$a[i++]; } public void remove() { throw new
UnsupportedOperationException();} } 10. 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. Majoritatea aplicatiilor actuale preiau datele de la operatorul uman
n mod interactiv, printr-o interfat grafic, pus la dispozitie de sistemul gazd
(Microsoft Windows, Linux cu X-Windows etc). Interfata grafic cu utilizatorul (GUI =
Graphical User Interface) este mai sigur si mai "prietenoas", folosind att
tastatura ct si mouse-ul pentru introducere sau selectare de date afisate pe ecran.
O interfat grafic simpl const dintr-o singur fereastr ecran a aplicatiei pe care
se plaseaz diverse componente vizuale interactive, numite si controale pentru c
permit operatorului s controleze evolutia programului prin introducerea unor date
sau optiuni de lucru (care, n mod text, se transmit programului prin linia de
comand). Uneori, pe parcursul programului se deschid si alte ferestre, dar exist o
fereastr initiala cu care ncepe aplicatia. Programele cu interfat grafic sunt
controlate prin evenimente produse fie de apsarea unei taste fie de apsarea unui
buton de mouse. Un eveniment des folosit este cel produs de pozitionarea
cursorului pe suprafata unui buton desenat pe ecran si clic pe butonul din stnga
de pe mouse. Tipul evenimentelor este determinat de componenta vizual implicat
dar si de operatia efectuat. De exemplu, ntr-un cmp cu text terminarea unei linii
de text (cu Enter) genereaz un tip de eveniment, iar modificarea unor caractere
din text genereaz un alt tip de eveniment. Limbajul Java permite, fat de alte
limbaje, programarea mai simpl si mai versatil a interfetei grafice prin numrul
mare de clase si de facilitti de care dispune. De exemplu, aspectul (Look and
Feel) componentelor vizuale poate fi ales dintre trei (patru, mai nou) variante
(Windows, MacOS sau Java), indiferent de sistemul de operare gazd. In termenii
specifici Java, componentele vizuale sunt de dou categorii: - Componente
atomice, folosite ca atare si care nu pot contine alte componente (un buton este
un exemplu de component atomic); - Componente container, care grupeaz
mai multe componente atomice si/sau containere. Componentele container sunt si

ele de dou feluri: - Containere de nivel superior (top-level) pentru fereastra


principal a aplicatiei; - Containere intermediare (panouri), incluse n alte containere
si care permit operatii cu un grup de componente vizuale (de exemplu, pozitionarea
ntregului grup). Componentele atomice pot fi grupate dup rolul pe care l au : Butoane de diverse tipuri: butoane simple, butoane radio - Elemente de dialog Componente pentru selectarea unei alternative (optiuni) - Indicatoare de progres a
unor activitti de durat - Componente cu text de diverse complexitati: cmp text,
zon text, documente 2 - Panouri cu derulare vertical sau orizontal (pentru liste
sau texte voluminoase) - 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. Colectia claselor GUI constituie un cadru pentru dezvoltarea de
aplicatii cu interfat grafic, 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. Acest cadru (Framework) mai este numit si infrastructur sau colectie
de clase de baz (Foundation Classes). Ca infrastructur pentru aplicatiile Java cu
interfat grafic vom considera clasele JFC (Java Foundation Classes), numite si
Swing, care reprezint o evolutie fat de vechile clase AWT (Abstract Window
Toolkit). Multe din clasele JFC extind sau folosesc clase AWT. Clasele JFC asigur
elementele necesare proiectrii de interfete grafice complexe, atrgtoare si
personalizate dup cerintele aplicatiei si ale beneficiarilor, reducnd substantial
efortul de programare a unor astfel de aplicatii (inclusiv editoare de texte,
navigatoare pe retea si alte utilitare folosite frecvent). O parte din clasele JFC sunt
folosite ca atare (prin instantiere) iar altele asigur doar o baz pentru definirea de
clase derivate. Clase JFC pentru interfat grafic O alt clasificare posibil a claselor
Swing este urmtoarea: - Clase JFC care au corespondent n clasele AWT, avnd
aproape acelasi nume (cu prefixul 'J' la clasele JFC) si acelasi mod de utilizare:
JComponent, JButton, JCheckBox, JRadioButton, JMenu, JComboBox, JLabel, JList,
JMenuBar, JPanel, JPopUpMenu, JScrollBar, JScrollPane, JTextField, JTextArea. - Clase
JFC noi sau extinse: JSlider, JSplitPanel, JTabbedPane, JTable, JToolBar, JTree,
JProgressBar, JInternalFrame, JFileChooser, JColorChooser etc. - Clase de tip model,
concepute conform arhitecturii MVC (Model-ViewController): DefaultButtonModel,
DefaultListSelectionModel, DefaultTreeModel, AbstractTableModel etc. Clasele JFC
container de nivel superior sunt numai trei: JFrame, JDialog si JApplet. Primele dou
sunt subclase (indirecte) ale clasei Window din AWT. Toate celelalte clase JFC sunt
subclase directe sau indirecte ale clasei JComponent, inclusiv clasa container
intermediar JPanel. Controalele JFC pot fi inscriptionate cu text si/sau cu imagini
(ncrcate din fisiere GIF sau definite ca siruri de constante n program). In jurul
componentelor pot fi desenate borduri , fie pentru delimitarea lor, fie pentru crearea
de spatii controlabile ntre componente vecine. Un program minimal cu clase JFC,
creeaz si afiseaz componentele vizuale pe ecran, fr s trateze evenimentele
asociate acestor componente. Cea mai mare parte dintr-un astfel de program
creeaz n memorie structurile de date ce contin atributele componentelor vizuale si
relatiile dintre ele: se creeaz un obiect fereastr (panou), care constituie fundalul
pentru celelalte componente; se creeaz componente atomice si se adaug la

panou obiectele grafice create de programator (cu metoda add). In final, se


stabilesc dimensiunile ferestrei principale (metoda setSize sau pack) si se
comand afisarea ferestrei principale (metoda setVisible sau show). 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), mrire (maximizare) si nchidere fereastr . Exemplul urmtor afiseaz
o fereastr cu titlu, dar fr alte componente vizuale : import javax.swing.*; class
EmptyFrame { public static void main ( String args[]) { JFrame frm = new
JFrame(EmptyFrame); frm.pack(); // sau frm.setSize(500,300) frm.setVisible (true);
// sau frm.show(); } } Fereastra principal se afiseaz initial ntr-o form redus la
bara de titlu cu cele 3 butoane generale, dup care poate fi mrit. Pentru afisarea
continutului ferestrei chiar de la nceput se poate folosi metoda pack (din clasa
JFrame) sau metoda setSize (din clasa Container). Adugarea de componente
vizuale la fereastra principal JFrame se poate face n dou moduri. In exemplul
urmtor se extrage "panoul" ferestrei cu "getContentPane" si se adaug o
component JLabel (o etichet) la acest panou: // adaugare la panoul extras din
obiectul JFrame import javax.swing.*; class LabelFrame { // fereastra cu o eticheta in
ea public static void main (String args[ ]){ JFrame frame = new JFrame(); // fereastra
aplicatiei JLabel label = new JLabel ("Eticheta"); // creare eticheta
frame.getContentPane().add(label); // adauga eticheta la fereastr
frame.setSize(200,200); // dimensiuni fereastra frame.setVisible(true); // afisare
continut fereastr } } In exemplul urmtor se creeaz un panou JPanel, pe care se
plaseaz eticheta, iar panoul este adugat ulterior la fereastra JFrame: // adaugare
la un panou introdus apoi in obiectul JFrame import javax.swing.*; class LabelFrame
{ // fereastra cu o eticheta in ea public static void main (String args[]){ JFrame
frame = new JFrame(); // fereastra aplicatiei JPanel panel = new JPanel(); // un obiect
panou panel.add (new JLabel ("Eticheta")) ; // adaug eticheta la panou
frame.setContentPane (panel); // sau: frame.add (panel); frame.show (); // afisare
continut fereastr } 4 } Efectuarea unui clic pe butonul de nchidere al ferestrei
principale (X) are ca efect nchiderea ferestrei, dar nu se termin aplicatia dac nu
estre tratat evenimentul produs de acest clic. De aceea este necesar tratarea
acestui eveniment, sau terminarea programului de ctre operator, prin Ctrl-C.
Incepnd cu versiunea 1.3 mai exist o posibilitate de terminare a aplicatiei la
nchiderea ferestrei principale: frame.setDefaultCloseOperation
(JFrame.EXIT_ON_CLOSE); Solutii de programare a interfetelor grafice In general,
crearea si afisarea unei interfete grafice necesit urmtoarele operatii din partea
programatorului aplicatiei: - Crearea unui obiect fereastr principal, de tip JFrame
sau de un subtip al tipului JFrame, si stabilirea propriettilor ferestrei (titlu, culoare,
dimensiuni etc.) - Crearea componentelor atomice si stabilirea propriettilor
acestora (dimensiuni, text afisat, culoare, tip chenar etc.) - Gruparea componentelor
atomice n containere intermediare, care sunt obiecte de tip JPanel sau de un subtip
al acestei clase. - Adugarea containerelor intermediare la fereastra aplicatiei si
stabilirea modului de asezare a acestora, dac nu se prefer modul implicit de
dispunere n fereastr. - Tratarea evenimentelor asociate componentelor si ferestrei

principale, prin definirea de clase de tip asculttor la evenimentele generate de


componentele vizuale. - Afisarea ferestrei principale, prin metoda setVisible sau
show a clasei JFrame. Exemplele anterioare nu reprezint solutia recomandat
pentru programarea unei interfete grafice din urmtoarele motive: - Variabilele
referint la obiecte JFC nu vor fi locale metodei main pentru c ele sunt folosite si
de alte metode, inclusiv metode activate prin evenimente. - Metoda static main
trebuie redus la crearea unui obiect si, eventual, la apelarea unei metode pentru
acel obiect. Obiectul apartine unei clase definite de programator si care foloseste
clasa JFrame sau JPanel. Vom prezenta n continuare trei variante uzuale de definire
a clasei GUI n cazul simplu al unui cmp text nsotit de o etichet ce descrie
continutul cmpului text. Prima variant foloseste o subclas a clasei JFrame: import
javax.swing.*; import java.awt.*; class GUI1 extends JFrame { private JLabel lbl1 =
new JLabel ("Directory"); private JTextField txt1 = new JTextField (16); // constructor
public GUI1 ( String title) { super(title); init(); } // initializare componente private
void init() { Container cp = getContentPane(); cp.setLayout(new FlowLayout());
cp.add (lbl1); cp.add(txt1); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); //
txt1.addActionListener (new TxtListener()); setSize (300,100); } // activare interfata
grafica public static void main (String arg[]) { new GUI1("GUI solution
1").show(); } } Varianta a doua foloseste delegarea sarcinilor legate de afisare
ctre un obiect JFrame, continut de clasa GUI: import javax.swing.*; import
java.awt.*; class GUI2 { private JFrame frame; private JLabel lbl1 = new JLabel
("Directory"); private JTextField txt1 = new JTextField (16); // constructor public GUI2
( String title) { frame = new JFrame(title); init(); frame.show(); } // initializare
componente private void init() { Container cp = frame.getContentPane();
cp.setLayout(new FlowLayout()); cp.add (lbl1); cp.add(txt1);
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); // txt1.addActionListener
(new TxtListener()); frame.setSize (300,100); } // activare interfata grafica public
static void main (String arg[]) { new GUI2("GUI solution 2"); } } Varianta 3 defineste
clasa GUI ca o subclas a clasei JPanel: import javax.swing.*; class GUI3 extends
JPanel { 6 private JLabel lbl1 = new JLabel ("Directory"); private JTextField txt1 =
new JTextField (16); // constructor public GUI3 () { init(); } // initializare componente
private void init() { add (lbl1); add(txt1); // txt1.addActionListener (new
TxtListener()); } // activare interfata grafica public static void main (String arg[])
{ JFrame frame = new JFrame ("GUI solution 3"); // frame.setContentPane(new
GUI3()); frame.getContentPane().add (new GUI3()); frame.setDefaultCloseOperation
(JFrame.EXIT_ON_CLOSE); frame.setSize(300,100); frame.show(); } } Clasele
asculttor la evenimente ( TxtListener si altele) sunt de obicei clase incluse n
clasa GUI pentru a avea acces la variabilele ce definesc obiecte JFC. Dac sunt
putini asculttori, atunci clasa GUI poate implementa una sau cteva interfete de tip
ascultator la evenimente si s contin metodele de tratare a evenimentelor (dispare
necesitatea unor clase asculttor separate). Variabilele de tipuri JFC ( JLabel,
JTextField, s.a) pot fi initializate la declarare sau n constructorul clasei GUI,
deoarece va exista un singur obiect GUI. Metoda init de initializare a
componentelor JFC poate lipsi dac are numai cteva linii. De observat c pentru

clasele GUI constructorul este cea mai important functie si uneori singura functie
din clas. Dup executia metodei show (sau setVisible) nu se vor mai crea alte
obiecte JFC (de exemplu, ca rspuns la evenimente generate de operatorul uman),
deoarece ele nu vor mai fi vizibile pe ecran. In schimb, se practic modificarea
continutului afisat n componentele deja existente; de exemplu, metoda setText
din clasele JTextField si JLabel poate modifica textul afisat n astfel de componente
JFC. Dispunerea componentelor ntr-un panou Plasarea componentelor grafice pe un
panou se poate face si prin pozitionare n coordonate absolute de ctre
programator, dar este mult mai simplu s apelm la un obiect de control al asezrii
n panou (Layout Manager), obiect selectat prin metoda setLayout si care
stabileste automat dimensiunile si pozitia fiecrei componente ntr-un panou. Pentru
panoul de continut al ferestrei JFrame este activ implicit BorderLayout, mod care
foloseste un al doilea parametru n metoda add. Dac nu se specific pozitia la
adugare, atunci componenta este centrat n fereastr, iar dac sunt mai multe
componente, atunci ele sunt suprapuse pe centrul ferestrei. Exemplu de plasare a
trei butoane: class DefaultLayout { // exemplu de asezare butoane in fereastra
public static void main (String args[ ]) { JFrame frame = new JFrame(); Container cp
= frame.getContentPane(); cp.add(new JButton( 1 ),BorderLayout.EAST); // cp.add
(...,East) cp.add(new JButton( 2 ),BorderLayout.CENTER); // cp.add (...,Center)
cp.add(new JButton( 3 ), BorderLayout.WEST); // cp.add (..., West)
frame.setVisible(true); } } De observat c obiectul extras cu "getContentPane" are
tipul Container. O solutie mai simpl este alegerea modului de dispunere
FlowLayout, care aseaz componentele una dup alta de la stnga la dreapta si de
sus n jos n functie de dimensiunile componentelor si ale ferestrei principale.
Exemplu: class FlowLayoutDemo { // Asezare butoane in fereastra public static void
main (String args[ ]) { JFrame frame = new JFrame(); Container cp =
frame.getContentPane(); cp.setLayout(new FlowLayout()); // sau new GridLayout()
for (int i=1;i. Exemplu de fisier html necesar pentru executia apletului precedent:
In comanda appletviewer este specificat numele fisierului html si nu apare
direct numele fisierului class. Dimensiunile ferestrei folosite de aplet se dau n
fisierul de tip "html" si nu n codul Java. 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. De remarcat c o clas care corespunde unui aplet
trebuie s aib atributul public si nu contine o metod main. Clasa aplet
mosteneste si redefineste de obicei metodele init, start, "paint" si alte cteva
metode, apelate de programul gazd la producerea anumitor evenimente. O clas
aplet poate fi transformat ntr-o aplicatie prin adugarea unei functii main n care
se construieste un obiect JFrame, la care se adaug un obiect aplet si se apeleaz
metoda init: public static void main (String args[ ]) { // se adauga la clasa JAplet
JFrame f = new JFrame(); JAplet aplet = new JAplet(); 14 f.getContentPane().add
(aplet); aplet.init(); f.setVisible (true); } 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). Functia init este nlocuit cu functia main
la trecerea de la un aplet la o aplicatie. Din punct de vedere functional un aplet

contine cteva functii, care trebuie (re)definite de utilizator si sunt apelate de


programul gazd. Un aplet care trateaz evenimente externe trebuie s contin si
metodele de tratare a evenimentelor, pentru c nu se admit alte clase asculttor,
separate de clasa aplet. Obiectul asculttor la evenimente este chiar obiectul aplet,
ceea ce conduce la instructiuni de forma urmtoare comp.addXXXListener(this); //
comp este numele unei componente din aplet Exemplul urmtor este un aplet care
afiseaz un buton n centrul ferestrei puse la dispozitie de programul gazd si emite
un semnal sonor ("beep") la "apsarea" pe buton, adic la actionarea butonului din
stnga de pe mouse dup mutare mouse pe zona ecran ocupat de buton. public
class Aplet extends JApplet implements ActionListener { JButton button; public void
init() { button = new JButton("Click Me"); getContentPane().add(button,
BorderLayout.CENTER); button.addActionListener(this); // obiectul receptor este
chiar apletul } public void actionPerformed(ActionEvent e) { // tratare eveniment
buton Toolkit.getDefaultToolkit().beep(); // semnal sonor } } Metoda "init" este
apelat o singur dat, la ncrcarea codului apletului n memorie, iar metoda
"start" este apelat de fiecare dat cnd programul browser readuce pe ecran
pagina html care contine si marcajul

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