Documente Academic
Documente Profesional
Documente Cultură
IE.
TEHNICI AVANSATE DE
PROGRAMARE
-1-
INFORMATIC*I*
CUPRINS
Cuvnt nainte ........................................................................................................................................... 6
Capitolul IE.01. Noiuni de baz ale programrii orientate obiect 7
IE.01.1. Paradigme de programare .......................................................................................... 7
IE.01.2. Programare orientat pe obiecte ............................................................................ ... 8
IE.01.3. Avantajele programrii cu obiecte ........................................................................... 11
IE.01.4. Clasele ca module de program reutilizabile ............................................................. 13
IE.01.5. ncapsulare .............................................................................................................. 14
IE.01.6. Clasele permit programarea generic ....................................................................... 15
IE.01.7. Clasele creeaz un model pentru universul aplicaiei .............................................. 16
IE.01.8.Tehnici de programare specifice POO ....................................................... 17
IE.01.9. Analiza i proiectarea orientate pe obiecte ............................................... 19
Capitolul IE.02. Clase i obiecte n Java ................................................................................................. 21
IE.02.1. Sintaxa limbajului Java ........................................................................................... 21
IE.02.2. Definirea de clase n Java ......................................................................................... 30
IE.02.3. Obiecte Java .............................................................................................................. 33
IE.02.4. iruri de caractere n Java ......................................................................................... 34
IE.02.5. Operaii de citire-scriere n Java ............................................................................... 36
Capitolul IE.03. Derivare, motenire, polimorfism n Java ..39
IE.03.1. Derivarea (extinderea) claselor ................................................................................ 39
IE.03.2. Clasa Object ca baz a ierarhiei de clase Java ........................................................ 40
IE.03.3. Suprascriere de metode n subclase ........................................................................... 41
IE.03.4. Derivarea ca metod de reutilizare ............................................................................ 42
IE.03.5. Derivare pentru crearea unor ierarhii de tipuri .......................................................... 44
IE.03.6. Polimorfism ....................................................................................... 46
IE.03.7. Delegarea ca alternativ la derivare pentru reutilizare .............................................. 48
IE.03.8. Motenire multipl prin derivare i delegare ............................................................ 50
Capitolul IE.04. Clase abstracte i interfee ... 54
IE.04.1. Clase abstracte n Java ................................................................................ 54
IE.04.2. Interfee n Java ....................................................................................................... 56
IE.04.3. Comparaie ntre interfee i clase abstracte.............................................................. 57
IE.04.4. Interfee Java pentru compararea de obiecte ............................................................. 59
IE.04.5. Interfee Java pentru enumerare ............................................................................... 59
IE.04.6. Interfee Java pentru filtrare ......................................................... 61
Capitolul IE.05. Tehnici de programare orientat obiect n Java . 66
IE.05.1. Excepii n Java ........................................................................................................ 66
IE.05.2. Reflecie n Java ........................................................................................................ 69
IE.05.3. Clase incluse . 70
IE.05.4. Fire de execuie ca obiecte ........................................................................................ 74
-2-
INFORMATIC*I*
INFORMATIC*I*
-4-
INFORMATIC*I*
-5-
INFORMATIC*I*
Cuvnt nainte
Scrierea de programe pentru calculatoare poate fi abordat din puncte de vedere diferite, numite i
paradigme de programare. Principalele paradigme i limbajele de programare reprezentative pentru acestea
sunt: programarea procedural ( Pascal, C), programarea funcional (Lisp, Scheme), programarea logic
(Prolog), programare orientat pe obiecte (C++,C#, Java, Scala), programare declarativ (HTML,XML) i
programare specific unor domenii (SQL, Javascript, PHP). In raport cu programarea procedural,
programarea cu clase i obiecte este considerat ca programare avansat, care include tehnici de
programare procedural dar adaug conceptul de clas.
Pentru aplicaii mari sau cu interfa grafic s-a impus programarea orientat pe obiecte i limbajul Java,
care ntr-un studiu din aprilie 2013 era pe locul 2 ntre cele mai cerute limbaje de firme la angajare. Toate
sistemele de operare au un interpretor de Java iar toate programele necesare dezvoltrii de aplicaii n Java
sunt gratuite. n plus exist cteva medii integrate de programare gratuite care faciliteaz mult dezvoltarea,
testarea i ntreinerea aplicaiilor Java : Eclipse, NetBeans, IDEA. Limbajul Java i programarea cu obiecte
se studiaz la toate universittile i colegiile din lume, iar la Universitatea Politehnica din Bucuresti este
disciplin obligatorie la facultile de Automatic si Calculatoare i de Inginerie n Limbi Strine.
Acest suport de curs, care include i probleme propuse si/sau rezolvate, se bazeaz pe materia cursurilor
Programare Orientat pe Obiecte i Ingineria Programrii, dar conine cele mai importante subiecte
abordate i n cursuri cu acelasi profil din alte universitti din lume. De obicei prezentarea limbajului Java
este precedat de o discuie despre specificul programrii cu obiecte i despre tehnicile de programare proprii
acestei abordri: derivare, polimorfism, delegare, clase abstracte i interfee, interfee grafice i programare
cu evenimente, s.a. Scrierea unei aplicaii reale este precedat de o faz de analiz a cerintelor, de
identificare a noiunilor importante din aplicatie; substantivele pot deveni clase (obiecte), iar verbele pot
deveni operaii (metode ale obiectelor).
Proiectarea ca faz anterioar scrierii de cod nseamn gsirea celor mai bune soluii de mprire n clase
astfel nct modificrile ulterioare s fie uurate i s nu produc efecte secundare nedorite. Aceste bune
practici sau soluii optime de proiectare se mai numesc i abloane de proiectare sau modele de proiectare
(design patterns) i trebuie cunoscute pentru a fi aplicate corect i eficient. O reprezentare grafic a
claselor i obiectelor existente, a relaiilor statice i dinamice dintre ele este de obicei parte a procesului de
proiectare dar i a documentaiei care nsoeste codul surs; limbajul UML (Unified Modelling Language)
este standardizat i ofer aceast reprezentare grafic unitar.
In producia de software se foloseste un mediu integrat pentru dezvoltare (IDE=Integrated Development
Environment) ca parte a metodologiei i tehnicilor specifice ingineriei de software (Software Engineering).
Am ales produsul NetBeans care are toate facilitile existente ntr-un IDE fiind n acelai timp intuitiv i
uor de folosit; n plus permite i dezvoltarea de aplicaii n alte limbaje importante (C, C++, HTML, XML,
PHP, Groovy).
Limbajul Java i bibliotecile de clase s-au dezvoltat continuu, iar n ultima vreme Java s-a impus mai mult
ca platform poliglot pentru dezvoltarea de programe, n sensul c tot mai multe limbaje conduc la codul
intermediar executat pe maina virtual Java (JVM) i folosesc bibliotecile de clase Java: Scala, Clojure,
Groovy, s.a. Am ales pentru prezentare aici limbajul Groovy, care este mai apropiat ca sintax de Java.
-6-
INFORMATIC*I*
INFORMATIC*I*
instruciunilor de salt) i prin programarea modular (definirea i utilizarea de funcii). Aceste ctiguri au
fost preluate i de programarea cu obiecte (POO).
Programele i limbajele sunt clasificate n limbaje statice si limbaje dinamice; un limbaj static nu permite
unui program s se modifice n cursul execuiei, spre deosebire de un limbaj dinamic. Desi primul limbaj
dinamic a fost Lisp, aceast caracteristic nu apartine numai paradigmei funcionale; tot mai multe limbaje
OO includ aspecte de metaprogramare (MOP=Meta-Object Protocol) care permit astfel de modificri ale
claselor i obiectelor in cursul execuiei.
Un limbaj cu tipuri statice menine tipurile declarate pentru variabile (parametri, funcii) la compilare i
permite efecturea unor verificri de utilizare corect a lor nc de la compilare; un limbaj cu tipuri dinamice
nu declar tipul variabilelor, tip care rezult la execuie din tipul valorilor atribuite i care se poate modifica
ulterior n funcie de atribuirile efectuate. Tipurile dinamice sunt folosite mai ales n limbaje interpretate (de
scripting); ele simplific codul surs dar operaiile nu pot fi verificate (nu exist compilare) i pot produce
erori sau rezultate neateptate la execuie (prin conversii automate de tip).
Limbajele orientate obiect pot folosi tipuri statice (Java, Scala) sau tipuri dinamice (Python, Ruby), pot fi
interpretate (JavaScript, Python) sau compilate (Java, Scala) sau admit ambele moduri de folosire (Groovy
poate fi folosit ca limbaj de scripting sau ca limbaj compilat).
Incepnd cu Java, multe din limbajele cu orientare pe obiecte sunt parial compilate i parial interpretate;
compilatorul genereaz un cod intermediar care este apoi interpretat de o ma in virtual. Codul intermediar
i masina virtual constituie o platform pe care se pot folosi mai multe limbaje. In prezent se folosesc dou
platforme: platforma Java (bytecode i maina virtual Java JVM) i platforma .NET (cod intermediar
Microsoft MSIL i maina virtual CLR= Common Language Runtime). Avantajul este acela c execuia
codului intermediar are loc sub controlul mainii virtuale, care poate face o serie de verificri la execuie i
poate genera mesaje (excepii program), motiv pentru care se numete managed code (cod gestionat sau
asistat).
O clas reprezint o categorie, adic un grup de obiecte care au n comun aceleai proprietti
(numite i atribute), aceeai comportare (operaii , mod de utilizare) i aceleai relaii cu obiecte
-8-
INFORMATIC*I*
din alte clase. O clas este un ablon pentru toate obiectele care au aceeai comportare (aceleai operaii).
Un obiect este un element particular al unei clase, numit i "instan" a clasei. Crearea unui obiect
poart i numele de instaniere a clasei, aciune prin care se stabilesc i o parte din atributele
obiectului creat, numite i variabile ale instantei (instance variables). Alte atribute pot fi stabilite
ulterior ca parametri ai unor metode apelate pentru acel obiect.
Metodele sunt aceleai pentru toate obiectele unei clase i reprezint operaiile posibile cu aceste
obiecte. De exemplu, conturile din banc ale clienilor bncii constituie o clas; contul unui client
este un obiect i are n general proprieti diferite de alte obiecte din aceeai clas: datele personale,
tipul de cont (debit / credit), soldul contului, istoricul operaiilor. Dar toate conturile bancare
suport aceleai operaii: crearea unui nou cont cu datele specifice clientului, depunerea unei sume
n cont, extragerea unei sume din cont, calcul dobnzi i comisioane, etc.
Programare cu obiecte nseamn definirea de clase plus crearea de obiecte plus apeluri de metode ntre
obiecte pentru realizarea obiectivelor aplicaiei. Un program procedural (scris n C de exemplu) este o
colecie de funcii, iar datele prelucrate se transmit ntre funcii prin argumente (sau prin variabile externe).
In programarea procedural obiectivul este definirea de funcii pentru operaii de prelucrare a datelor i
obinere a rezultatelor, dar n programarea orientat obiect scopul este crearea de obiecte axate pe date, cu
operaii ataate acestor
date. Accentul se mut
de pe aciuni (funcii) pe
date (obiecte).
Aciunile dintr-un program cu obiecte sunt realizate prin apeluri de metode pentru anumite obiecte, apeluri
provenite din alte obiecte. Se mai spune c obiectele interacioneaz prin transmiterea de mesaje. De
exemplu, atunci cnd se apeleaz metoda read() de citire a unui caracter dintr-un fiier (flux), se poate spune
c se transmite obiectului din clasa FileReader mesajul d-mi urmtorul caracter din fiierul asociat acestui
obiect (numele fiierului este transmis la crearea obiectului de tip FileReader).
-9-
INFORMATIC*I*
La nivel sintactic, obiectele sunt asocieri de date i operatii aplicabile acestor date si pot fi privite ca o
generalizare a variabilelor structur din limbajele procedurale (de tip struct din limbajul C). Aa cum
definiia unei structuri n C creeaz un nou tip de date, la fel definiia unei clase va crea un nou tip clas.
In C o stiv vector se defineste printr-o structur care grupeaz un vector cu un indice ctre prima adres
liber (vrful stivei); n Java un obiect stiv mai conine n plus i operaii asociate unei stive: push() (pune
un obiect pe stiv), pop()(scoate ultimul obiect pus n stiv), empty() (verific dac stiva este goal), init()
(iniializare stiv goal), .a.
Exemplul urmtor este o funcie C de copiere a unui fiier, octet cu octet:
// copiere fisier in C
void filecopy ( char * src, char * dst) {
char ch;
FILE * in =fopen(src,"r");
// deschide fisier sursa
FILE * out =fopen(dst,"w"); // deschide fisier destinatie
while ( (ch=fgetc (in)) != -1) // citeste un caracter
fputc (ch, out);
// scrie un caracter
fclose(out); fclose(in);
}
Aciunile necesare copierii se realizeaz prin funciile fopen(), fgetc(), fputc(), fclose() care primesc ca date
variabilele in, out i ch. Exemplul urmtor este o funcie Java de copiere octei dintr-un fiier surs
src ntr-un fiier destinaie dst:
public static void filecopy (String src, String dst) throws IOException {
FileReader in = new FileReader (src);
// un obiect
FileWriter out = new FileWriter (dst);
// alt obiect
int ch;
while ( (ch= in.read()) != -1)
// cere obiectului in operatia read
out.write(ch);
// cere obiectului out operatia write
in.close(); out.close();
// cere obiectelor operatia close
}
In acest exemplu se folosesc dou obiecte: un obiect de tip FileReader (prin variabila in) i un obiect de tip
FileWriter (prin variabila out); prin metoda read() se cere obiectului in s citeasc i s furnizeze un
caracter, iar prin metoda write() se cere obiectului out s scrie n fisier caracterul primit ca argument. Pentru
obiectele in si out se pot apela i alte metode, din clasele respective. Obiectele in i out conin informaii
despre fisierele src i dst necesare prelucrrii lor, cam aceleai care se memoreaz ntr-o structur de tip
FILE n limbajul C. La crearea obiectelor, folosind operatorul new, se deschid cele dou fiiere (aciunea
funciei fopen() din C).
O clas corespunde unei noiuni abstracte cum ar fi orice fiier disc, iar un obiect este un caz concret (o
realizare a conceptului sau o instaniere a clasei). Un obiect de tip FileReader corespunde unui anumit fiier,
cu nume dat la construirea obiectului.
Relativ la exemplul Java, trebuie spus c utilizarea unei metode statice de copiere nu este n spiritul POO.
Metodele statice Java corespund funciilor C i pot fi folosite fr ca s existe obiecte (ele fac parte totui din
- 10 -
INFORMATIC*I*
anumite clase). Mai aproape de stilul propriu POO, ar trebui definit o clas copiator de fisiere, avnd si o
metod (nestatic) de copiere copy(), aplicabil obiectelor FileCopier. Exemplu:
public class FileCopier {
// datele clasei
private FileReader in;
// sursa datelor
private FileWriter out;
// destinatia datelor
// constructor
public FileCopier (String src, String dst) throws IOException {
in = new FileReader (src);
out = new FileWriter (dst);
}
// o metoda de copiere
public void copy () throws IOException {
int c;
while ( (c= in.read()) != -1)
out.write(c);
in.close(); out.close();
}
// verificarea clasei FileCopier
public static void main (String arg[]) throws IOException {
FileCopier fc = new FileCopier (arg*0+, arg*1+);
// creare obiect fc
fc.copy();
// cere obiectului fc operatia copy
}
}
In exemplele anterioare i n cele ce vor urma se poate observa mutarea accentului de pe aciuni (funcii) pe
obiecte (date) n programarea orientat pe obiecte. Numele de clase sunt substantive, uneori derivate din
verbul ce defineste principala aciune asociat obiectelor respective. In Java exist obiecte comparator (de
tip Comparator) folosite n compararea altor obiecte, clasa Enumerator folosit la enumerarea elementelor
unei colecii, clasa StringTokenizer folosit la extragerea cuvintelor dintr-un ir .a.
INFORMATIC*I*
datele din vrful stivei i test de stiv goal. In limbajul C se pot defini funciile pentru operaii cu stiva astfel
ca utilizarea lor s nu depind de implementare, prin folosirea unui pointer la o structur:
void initSt ( Stiva * sp); // initializare stiva
int emptySt (Stiva * s);
// test stiva goala
int push (Stiva * sp, T x); // pune in stiva un element de tip T
T pop (Stiva * sp );
// scoate din stiva un element de tip T
Definiia tipului Stiva i definiiile funciilor depind de implementare. Exemplu de utilizare stiv n C:
#include "stiva.h"
void main () {
int x; Stiva s ;
initSt (&s);
// initializare stiva
for (x=1; x<10; x++)
// genereaza date ptr continut stiva
push (&s,x);
// pune x pe stiva
while ( ! emptySt (&s) )
// cat timp stiva contine ceva
printf("%d \n", pop (&s) ) ; // scoate din stiva si afiseaza
}
Modificarea tipului de stiv (list n loc de vector) sau modificarea tipului datelor memorate n stiv necesit
un alt fiier stiva.h i o alt bibliotec de funcii push(), pop()).
In POO se va crea un obiect de tip stiv i se vor apela pentru acest obiect metoda push() avnd ca argument
obiectul pus pe stiv i metoda pop() cu rezultat obiectul scos din stiv. Obiectele memorate n stiv pot avea
orice tip.
In Java exist o clasa predefinita Stack iar programul de exersare a operaiilor cu stiva arat astfel:
public static void main (String arg[ ]) {
Stack s = new Stack();
// creare obiect stiva
for (int x=1; x<10; x++)
// pune 10 numere in stiva
s.push ( new Integer(x));
// s.push(x) in Java 5
while ( ! s.empty())
// cat timp stiva nu e goala
System.out.println ( s.pop()); // afiseaza obiectul scos din stiva
}
Modificarea implementrii stivei, prin definirea unei alte clase Stack nu necesit modificri n funcia
anterioar, ci doar punerea noii clase n cile de cutare ale compilatorului i interpretorului Java. In plus, se
poate defini o clas abstract care s corespund tipului de date abstract Stiv i care s precizeze
operaiile cu stiva , fr s presupun o anumit implementare (o structur de date concret).
Un al doilea exemplu este cel al extragerii de cuvinte succesive dintr-un ir de caractere ce poate conine
mai multe cuvinte, separate prin anumite caractere delimitator. Problema este aceea c dup fiecare cuvnt
extras se modific adresa curent n irul analizat, deci starea sau contextul n care se execut funcia care d
urmtorul cuvnt.
In limbajul C se pot ntlni mai multe soluii ale acestei probleme n diferite funcii de bibliotec. Una din ele
este funcia strtok() care modifica irul analizat i foloseste o variabil static intern a funciei pentru
- 12 -
INFORMATIC*I*
adresa curent n irul analizat. In plus, primul apel difer de urmtoarele apeluri ale funciei. Exemplu de
utilizare:
cuv=strtok(str, sep);
// primul cuvnt din str, sep= sir de separatori
while (cuv !=NULL) {
// daca s-a gasit un cuvant
puts(cuv);
// afisare cuvant
cuv=strtok(0,sep);
// urmatorul cuvant din str
}
In Java exist clasa de bibliotec StringTokenizer, folosit dup cum urmeaz:
String sep = new String (" ,.;\n\t");
// lista separatori de cuvinte
StringTokenizer st = new StringTokenizer (sir,delim); // sir = sir analizat
while (st.hasMoreTokens()) {
// daca mai sunt cuvinte in sirul analizat
String token = st.nextToken();
// extrage urmatorul cuvint din linie
System.out.println (token);
// afisare cuvint
}
La crearea unui obiect StringTokenizer se specific irul analizat, astfel c se pot analiza n paralel mai
multe iruri, pentru fiecare folosind un alt obiect. Metodele nextToken()i hasMoreTokens() folosesc n
comun o variabil a clasei care conine poziia curent n irul analizat (iniializat cu adresa irului, la
construirea obiectului).
In prelucrarea fiierelor apar situaii cnd execuia cu succes a unei funcii depinde de folosirea anterioar a
altor funcii (cu anumite argumente); de exemplu pentru a putea scrie ntr-un fiier, acesta trebuie mai nti
deschis pentru creare sau pentru adugare (extindere fiier existent). O situaie asemntoare apare la
utilizarea unor funcii care compun o interfa grafic i care trebuie folosite ntr-o anumit ordine. Astfel de
condiionri reciproce nu se pot verifica automat n C, fiind vorba de funcii independente. In Java operaiile
sunt metode dintr-o aceeai clas i se poate verifica printr-o variabil a clasei succesiunea corect de
folosire a metodelor.
INFORMATIC*I*
- O clas poate ncapsula algoritmi de complexitate ridicat, realizai prin colaborarea mai multor funcii,
unele interne clasei; astfel de algoritmi fie nu sunt disponibili n C, fie sunt disponibili prin biblioteci de
funcii destul de greu de utilizat. Exemple sunt algoritmi pentru lucrul cu expresii regulate, pentru arhivaredezarhivare, pentru operaii cu anumite structuri de date (arbori binari cu auto-echilibrare), s.a.
- Se poate realiza un cuplaj mai slab ntre module, n sensul c modificarea anumitor module nu va afecta
restul programului. Aceast decuplare sau separare ntre module se poate realiza prin mai multe metode,
printre care folosirea de interfee Java, n spatele crora pot sta clase cu implementri diferite dar cu acelai
mod de utilizare.
Funcionalitatea unei clase poate fi reutilizat n alte clase fie prin derivare, fie prin delegare (compunere). In
acest fel, operaiile necesare ntr-o clas sunt fie motenite de la o alt clas, fie delegate spre execuie
metodelor unei alte clase. De exemplu, extinderea automat a unui vector, necesar uneori dup adugarea la
vector, este refolosit i ntr-o clas stiv vector, fie prin definirea clasei stiv ca o clas derivat din vector,
fie prin folosirea unei variabile Vector n clasa stiv.
In POO adaptarea unei clase la cerine specifice unor aplicaii nu se face prin intervenie n codul clasei ci
prin derivare sau prin delegare, tehnici specifice POO.
O noiune proprie programrii cu obiecte este noiunea de component software. Ideea este de a obine rapid
un prototip al aplicaiei fr a scrie cod sau cu un minim de programare, prin asamblarea de componente
prefabricate (n special pentru interfaa grafic, dar nu numai). O component poate conine una sau mai
multe clase i poate fi reutilizat i adaptat fr intervenie n codul surs al componentei (care nici nu este
disponibil).
O component JavaBeans poate fi utilizat fr a scrie cod, prin generarea automat a operaiilor de
instaniere, de modificare a proprietilor si de conectare cu alte clase (prin apeluri de metode sau prin
evenimente), n urma unor comenzi date de utilizator unui mediu vizual de dezvoltare a aplicaiilor. O
component este de obicei o clas care respect anumite condiii.
IE.01.5 Incapsulare
Datele coninute n obiecte nu sunt accesibile direct pentru metode din alte obiecte (sunt private), dar ele sunt
folosite de metodele apelate pentru obiectele respective. Datele sunt ncapsulate i invizibile n afara clasei
iar utilizatorii clasei vd numai serviciile oferite de clas prin metodele ei.
Putem compara un obiect cu o cutie neagr care are cteva butoane i cteva afiri (un televizor, de
exemplu); utilizatorul nu trebuie s tie ce este n interiorul acestei cutii negre ci doar ce comenzi se
transmit prin butoane i s interpreteze afirile.
Interfaa public expus de un obiect celorlalte obiecte este format din constructori i metode publice,
utilizabile din alte clase. Constructorii sunt ntotdeauna publici pentru a permite instanierea clasei i crearea
de obiecte. Metodele publice sunt metode motenite sau metode proprii.
Mai multe clase pot avea implementri diferite dar pot s prezinte o aceeai interfa public; aa sunt clasele
pentru anumite colecii de date (liste, mulimi, dicionare, etc.).
Variabilele dintr-o clas sunt declarate de obicei cu atributul private, ceea ce le face inaccesibile pentru
metode din alte clase. Se mai spune c datele sunt ascunse sau sunt ncapsulate n fiecare obiect. Metodele
clasei sunt de obicei publice pentru a putea fi apelate din alte clase.
- 14 -
INFORMATIC*I*
Deoarece datele dintr-un obiect (variabile private) nu sunt direct accesibile din afara clasei i pot fi
modificate numai prin intermediul metodelor clasei, utilizarea tipurilor de date definite prin clase este mai
sigur dect a celor definite prin structuri. De exemplu, orice modificare a vectorului de caractere dintr-un
obiect StringBuffer (StringBuilder) este nsoit de modificarea lungimii irului (n metodele care pot
modifica lungimea irului), dar lungimea nu poate fi modificat direct de ctre funcii din alte clase (i nici
coninutul vectorului de caractere).
In reprezentarea UML a unei clase atributul private este notat cu minus (-), iar atributul public este notat cu
plus (+). Exemplu de clas pentru o persoana, cu variabilele name, address i 5 metode publice:
INFORMATIC*I*
INFORMATIC*I*
Cont va conine suma din cont (si alte date asupra operaiilor cu acel cont), precum i metode pentru
depunerea de bani n cont, pentru retragerea de bani din cont i pentru vizualizarea sumei de bani din cont.
Obiectele de tipul Cont sau Client se numesc i obiecte din domeniul aplicaiei (domain objects).
Aplicaiile mai pot conine obiecte ajuttoare (helper) sau obiecte din clase predefinite pentru operaii cu
anumite tipuri de date, cu colecii de obiecte, cu baze de date, cu conexiuni ntre calculatoare s.a.
INFORMATIC*I*
Delegarea nseamn c n clasa mulime vector exist o variabil de tip vector i c operatiile cu
mulimea vector sunt delegate prin aceast variabil ctre operaiile clasei vector (prin apeluri de metode
din clasa vector).
Derivarea poate fi folosit i pentru crearea unor ierahii de tipuri: o clas reprezint un tip de date, o clas
derivat reprezint un subtip al tipului clasei din care a fost derivat. Cele dou tipuri sunt compatibile la
atribuirea ntre variabile si la transmiterea de argumente, la fel cum n limbajul C orice tip aritmetic este
compatibil cu alte tipuri aritmetice (short, int, float, double, etc) sau cum sunt compatibile ntre ele tipul
void* cu orice alt tip pointer.
In programarea cu obiecte se folosesc multe ierarhii de tipuri i ele au diferite beneficii. De exemplu, n Java
toate clasele existente sau definite de utilizatori sunt subclase ale clasei Object si deci toate tipurile clas
sunt compatibile cu tipul de baz Object (rdcina ierarhiei de tipuri Java). O colecie de variabile de tip
Object poate fi folosit drept colectie cu date de orice alt tip clas, fiind deci o colecie generic. O metod
cu un argument de tip Object sau cu rezultat Object poate fi apelat cu argument de orice tip clas si este o
funcie generic.
Specific POO este c ierarhiile de tipuri pot conine tipuri abstracte, foarte generale n partea de sus a
ierarhiei. Familia claselor colecie sau a claselor dicionar ncepe cu o interfa care are sub ea clase
abstracte, care au ca subtipuri clase instaniabile. O interfa are toate metodele abstracte i nu are date n
timp ce o clas abstract poate avea i date i metode definite (ne-abstracte). Interfeele i clasele abstracte
Java sunt i ele subtipuri ale tipului Object. Interfaa impune anumite metode pentru toate subtipurile sale,
clasa abstract definete o parte din metodele interfeei (care nu depind de date i n funcie de alte metode),
iar clasa instaniabil definete i metodele rmase abstracte (i care depind de datele clasei).
O funcie polimorfic (virtual) este o funcie cu acelasi nume, tip i argumente dar cu implementri
(definiii) diferite n clase diferite. Metoda polimorfic are acelai rol i primeste aceleai date dar definiia
difer n funcie de specificul clasei; ea este virtual pentru c definete un prototip de funcie i nu o anume
funcie concret. Exemplu: Metoda toString() din Java are rolul de a produce un ir (obiect String) cu datele
dintr-un obiect, n vederea afirii sau scrierii acestora ntr-un fiier, sau pentru combinarea cu alte iruri.
Deoarece clase diferite conin date diferite este normal ca i metoda toString() s fie diferit n clase diferite.
Metoda toString() este impus de clasa Object tuturor celorlalte clase i este de obicei redefinit
(suprascris) n fiecare clas; este posibil ca o aceeai definiie s fie folosit n comun de cteva clase (de
ex. toate clasele mulime pot folosi toString() din clasa AbstractSet). Selectarea uneia dintre metodele cu
acelai nume se face n funcie de tipul obiectului pentru care se apeleaz metoda, deci aceast tehnic este
posibil numai n programarea cu obiecte.
Suprascrierea funcilor (Ovverriding) este necesar pentru a redefini o metod motenit ntr-o subclas; n
felul acesta o metod cu acelai nume, tip i argumente are efecte (definiii) diferite n clasele din ierarhie i
este polimorfic. Pentru ca s fie posibil selectarea unei definiii dintre mai multe definiii ale unei metode
- 18 -
INFORMATIC*I*
polimorfice n funcie de tipul obiectului pentru care se aplic s-a introdus i o sintax diferit de apelare a
metodelor, alta dect sintaxa de apelare a funciilor C (sau a metodelor statice din Java). Numele metodei
este prefixat de numele variabilei referin care identific obiectul pentru care se apeleaz metoda:
LinkedList list = new LinkedList(); String str = list.toString();
TreeSet set = new TreeSet(); String str = set.toString();
In exemplul anterior se va folosi metoda toString() din clasa LinkedList n primul apel i metoda toString()
din clasa TreeSet n al doilea apel, n funcie de tipul variabilelor list si set.
Extinderea simultan a mai multor clase X,Y este necesar pentru a crea un nou tip compatibil simultan cu
tipurile X,Y. O clas C++ poate extinde simultan (prin derivare) mai multe clase, deci poate moteni
(metode si date) din mai multe surse. Exist ns posibilitatea ca o aceeai variabil (dintr-o superclas A) s
fie motenit de o clas D, pe ci diferite, de la subclase ale clasei A (fie B si C aceste subclase). Variabilele
din obiecte de tip B si C pot avea valori diferite pentru variabila motenit, dar obiectele clasei D ce valoare
motenesc ?
O clas Java poate extinde o singura clas dar poate implementa simultan mai multe interfee, ceea ce
rezolv problemele create de o motenire multipl: se preiau tipuri dar nu i date de la interfee. In Java
interfeele au o utilizare mult mai larg fa de clasele abstracte (exist multe interfee predefinite n
bibliotecile de clase).
INFORMATIC*I*
Ingineria software (Software Engineering) nseamn aplicarea sistematic i disciplinat a unor metode
(metodologii) pentru proiectarea, dezvoltarea (implementarea), operarea si ntreinerea unor produse
software de calitate. Metodologii propuse pentru dezvoltarea de aplicaii: Agile, Extreme, Lean, Joint,
Scrum, RAD. Aceste metodologii se refer nu numai la limbaje, la instrumente software folosite i la fazele
procesului ci i la modul de colaborare cu beneficiarii i n cadrul echipei care dezvolt o aplicaie.
Obiectele dintr-o aplicaie i au originea n:
- Obiecte din domeniul aplicaiei: client, cont, factur, tranzacie,.a.
- Obiecte colecie: vectori, liste, dicionare, arbori etc.
- Obiecte care conin algoritmi: operaii pe iruri, operaii cu expresii regulate, operaii de arhivare sau
dezarhivare, etc.
- Obiecte din interfaa grafic a aplicaiei: ferestre, butoane, casete cu text, imagini, meniuri, etc
- Obiecte pentru operaii de citire-scriere fiiere, pentru operaii n reteaua Internet, parsere de fiiere
XML , pentru fire de execuie concurente, s.a.
- Obiecte auxiliare necesare n realizarea unor scheme de clase (abloane de proiectare): obiecte iterator sau
enumerator al elementelor dintr-o colecie, obiecte asculttor (observator), etc.
- 20 -
INFORMATIC*I*
INFORMATIC*I*
// ch este 'A'
// b este 10
Aceleai reguli de conversie ntre tipuri numerice se aplic i ntre argumentele efective i argumentele
formale, deoarece compilatorul face automat o atribuire a valorii argumentului efectiv la argumentul formal
corespunztor. Exemplu: double r = Math.sqrt(2);
// promovare de la int la double
Conversia de la un tip numeric superior la un tip aritmetic inferior trebuie cerut explicit prin folosirea
operatorului cast de forare a tipului i 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 ieire posibil dintr-o funcie cu tip diferit
de void (de exemplu, instruciuni if fr else ). Exemplu:
public static int indexOf (int a[], int b) { // pozitia lui b in vectorul a
for (int i=0;i<a.length;i++)
if (a[i]==b)
return i;
// return 1;
// eroare de compilare !
}
Cea mai important diferen dintre Java, pe de o parte, i limbajele C, C++ pe de alt parte, este absena
tipurilor pointer din Java. Deci nu exist posibilitatea de a declara explicit variabile pointer i nici operatorii
unari & (pentru obinerea adresei unei variabile) i * (indirectare printr-un pointer). Operatorul new din
C++ pentru alocare dinamic are n Java un rezultat o referin i nu un pointer.
- 22 -
INFORMATIC*I*
// in clasa Math
In Java nu exist declaratia typedef deoarece definirea unei clase introduce automat un nume pentru un nou
tip de date.
In Java nu exist operatorul sizeof , pentru c lungimea variabilelor este cunoscut, iar la alocarea de
memorie (cu new) nu trebuie specificat dimensiunea alocat.
- 23 -
INFORMATIC*I*
Numele unei metode statice trebuie precedat de numele clasei din care face parte (separate printr-un punct),
dac este apelat dintr-o metod a unei alte clase. Exemplu:
public class Main {
public static void main (String arg[ ]) {
Util.writeln ("Hello world !");
}
}
public class Util {
public static void writeln (String txt) {
System.out.println (txt);
}
}
O metod ne-static trebuie apelat pentru un anumit obiect, iar numele ei trebuie precedat de numele
obiectului (i un punct). Metoda println() este apelat pentru obiectul adresat de variabila out, variabil
public din clasa System.
- 24 -
INFORMATIC*I*
Un fiier surs Java poate conine mai multe clase, dar numai una din ele poate avea atributul public. Numele
fiierului surs (de tip java) trebuie s coincid cu numele clasei publice pe care o conine. O clas public
este accesibil i unor clase din alte pachete de clase.
Compilatorul Java creeaz pentru fiecare clas din fiierul surs cte un fiier cu extensia class i cu numele
clasei. Dac este necesar, se compileaz i alte fisiere surs cu clase folosite de fiierul transmis spre
compilare.
Faza de execuie a unui program Java const din ncrcarea i interpretarea tuturor claselor necesare
execuiei metodei main din clasa specificat n comanda java.
In practic se prefer ca variabilele clasei Point s fie de tip private (inaccesibile unor metode din alte clase)
i ca iniializarea lor s se fac n constructorul clasei:
public class Point {
// orice punct din plan
private double x,y;
// coordonate punct
public Point (double xi, double yi) { x=xi; y=yi; } // functie constructor
}
Point a = new Point (2,-3);
Clasele (neabstracte) Java sunt de dou categorii:
- Clase instaniabile, care pot genera obiecte, care conin date i metode (ne-statice).
- Clase neinstaniabile, care conin doar metode statice (i eventual constante).
O metod static corespunde unei funcii din limbajul C, cu diferena c numele funciei trebuie precedat de
numele clasei din care face parte. Exemple:
double xabs = Math.abs(x);
// valoarea absoluta a lui x
double y = Math.sqrt(x);
// radical din x
int n = Integer.parseInt (str);
// conversie sir "str" la tipul "int"
Definirea unei clase instaniabile T creeaz automat un nou tip de date T. Un obiect de tip T este o instaniere
a clasei T i este referit printr-o variabil de tip T. Clasa Java cu numele String definete un tip de date
String, ce poate fi folosit n declararea de variabile, vectori sau funcii de tip String. Exemple:
- 25 -
INFORMATIC*I*
// o variabila sir
// un vector de siruri
Un obiect Java corespunde unei variabile structur din C, iar o variabil de un tip clas corespunde unei
variabile pointer la o structur din C. In Java toate obiectele sunt alocate dinamic, folosind operatorul new,
iar variabila de tip clas trebuie iniializat cu rezultatul operatorului new. Exemplu:
String mesaj;
// String este o clas predefinit
mesaj = new String ( Eroare ! ) ; // alocare memorie pentru sir
Pentru constantele de tip String se creeaz obiecte automat, de ctre compilator, ale cror adrese pot fi
folosite n atribuiri sau iniializri la declarare. Exemple:
System.out.println ("Eroare !");
String msg; msg = " Corect";
Clasa String conine mai multe metode publice, utilizabile n alte clase. De exemplu, metoda length(), fr
argumente, are ca rezultat (ntreg) lungimea irului coninut n obiectul de tip String pentru care se apeleaz
metoda. Exemplu:
int len = mesaj.length();
Acest exemplu arat c membrii unei clase se folosesc la fel cu membrii unei structuri, indiferent c ei sunt
variabile (cmpuri) sau funcii (metode). Un alt exemplu este o construcie mult folosit n Java pentru
afiarea la consol (n mod text) a unor iruri de caractere:
System.out.println (mesaj);
System.out.println ( Eroare );
In aceste exemple System este numele unei clase predefinite, out este numele unei variabile publice (din
clasa System) de un tip clas (PrintStream), iar println() este numele unei metode din clasa PrintStream.
Numele unei metode poate fi precedat de numele unei variabile clas sau de numele unei clase, dar
ntotdeauna caracterul separator este un punct. Este uzual n Java s avem denumiri de variabile sau de
metode care conin cteva puncte de separare a numelor folosite n precizarea contextului. Exemple:
if ( Character.isDigit ( str.charAt(0)) ) . . . // daca primul caracter e o cifra
System.out.println (obj + obj.getClass().getName());
int maxdigits= (Integer.MAX_VALUE+"").length();
O referin la un tip clas T este de fapt un pointer la tipul T dar care se folosete ca i cum ar fi o variabil
de tipul T. Indirectarea prin variabila referin este realizat automat de compilator, fr a folosi un operator
special, ca n C .
Tipul referin a fost introdus n C++ n principal pentru a declara parametri modificabili n funcii, cu
simplificarea scrierii i utilizrii acestor funcii. In Java nu trebuie folosit o sintax special pentru
declararea de variabile sau de parametri referin, deoarece toate variabilele de un tip clas sunt automat
considerate ca variabile referin. Nu se pot defini referine la tipuri primitive.
O variabil referin Java nu este un obiect, dar conine adresa unui obiect alocat dinamic. O variabil
referint apare de obicei n stnga unei atribuiri cu operatorul new sau cu constanta null n partea dreapt.
Exemplu:
Vector a = new Vector( );
- 26 -
INFORMATIC*I*
Atunci cnd se apeleaz o metod pentru un obiect, se folosete numele variabilei referin ca i cum acest
nume ar reprezenta chiar obiectul respectiv i nu adresa sa. Exemplu:
System.out.println ( a.size() );
Operatorul de concatenare '+', folosit ntre obiecte de tip String, poate crea impresia c variabilele de tip
String conin chiar irurile care se concateneaz i nu adresele lor. Exemple:
String s1="java.", s2="util.", s3="Rand";
System.out.println (s1+s2+s3);
// scrie java.util.Rand
Operatorul de concatenare + este singurul operator supradefinit n Java i el poate fi utilizat ntre
operanzi de tip String sau cu un operand de tip String i un alt operand de orice tip primitiv sau de un tip
clas (pentru care exist o funcie de conversie la tipul String ). Exemplu:
int a=3, b=2 ;
System.out.println ( a + + + b + = + (a+b)); // scrie: 3 + 2 = 5
Efectul operatorului '+' depinde de tipul operanzilor: dac unul din operanzi este de tip String atunci este
interpretat ca operator de concatenare iar rezultatul este tot String.
INFORMATIC*I*
// la fel ca in C si C++
// specific Java
// o matrice de ntregi
// alt matrice de ntregi
In Java nu este permis specificarea unor dimensiuni la declararea unor vectori sau matrice, deoarece
alocarea de memorie nu se face niciodat la compilare. Exemplu de eroare:
- 28 -
INFORMATIC*I*
int a[100];
O variabil vector este automat n Java o variabil referin iar memoria trebuie alocat dinamic pentru orice
vector. Alocarea de memorie pentru un vector se face folosind operatorul new urmat de un nume de tip i
de o expresie (cu rezultat ntreg) ntre paranteze drepte; expresia determin numrul de componente (nu de
octei !) pe care le poate conine vectorul. Exemple:
float x[ ] = new float [10];
// aloca memorie ptr 10 reali
int n=10;
byte[ ][ ] graf = new byte [n][n];
Este posibil i o alocare automat, atunci cnd vectorul este iniializat la declarare cu un ir de valori.
Exemplu:
short prime[ ] = {1,2,3,5,7};
In lipsa unei iniializri explicite, componentele unui vector sunt iniializate automat, cu valori ce depind de
tipul lor: zerouri pentru elemente numerice i null pentru variabile referin de orice tip.
Un vector intrinsec cu componente de un anumit tip este considerat ca un obiect de un tip clas, tip
recunoscut de compilator dar care nu este definit explicit n nici un pachet de clase. Numele acestor clase
este format din caracterul [ urmat de o liter ce depinde de tipul componentelor vectorului: [I pentru int[],
[B pentru byte[], [Z pentru boolean[], [C pentru char[], [F pentru float[] s.a.m.d.
Variabila predefinit cu numele length poate fi folosit ( ca membru al claselor vector ) pentru a obine
dimensiunea alocat pentru un vector (capacitatea vectorului). 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 =dimensiunea vectorului a
b[i] = a[i];
}
De reinut c length este dimensiunea alocat i nu dimensiunea efectiv a unui vector, iar numrul de
elemente din vector se transmite ca argument la funcii, atunci cnd difer de capacitatea sa. Variabila length
nu trebuie confundat cu metoda length() din clasa String.
In Java, se verific automat, la execuie, ncadrarea indicilor ntre limitele declarate; ieirea din limite
produce o excepie i 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
excepia de depire a limitelor la valoarea i=10 .
O matrice este privit i n Java ca un vector de vectori, iar variabila length se poate folosi pentru fiecare
linie din matrice, pentru a determina numrul de coloane. Deoarece orice matrice este alocat dinamic, nu
exist probleme la transmiterea unei matrice ca argument la o funcie. Nu este necesar transmiterea
dimensiunilor matricei la o funcie dac matricea este ocupat complet (la capacitatea ei).
O funcie poate avea un rezultat de un tip vector (sau matrice).
- 29 -
INFORMATIC*I*
In Java, ca i n C, transmiterea parametrilor se face prin valoare, adic se copiaz valorile parametrilor
efectivi n parametrii formali corespunztori, nainte de execuia funciei. Deci o funcie Java nu poate
transmite rezultate prin argumente de un tip primitiv, dar poate modifica componentele unui vector primit ca
argument. Exemplu:
static int divizori (int n, int d[ ]) {
// creare vector cu divizorii unui ntreg
int k=0;
for (int i=1;i<n;i++)
if ( n %i == 0)
d[k++]=i;
// pune divizorul i n vectorul d
return k;
// numar de divizori
}
// utilizare funcie
int div[ ]= new int [m];
// aloca memorie ptr vector divizori
int nd = divizori (m, div);
// completare vector divizori
Clasa Arrays din pachetul java.util reunete funcii pentru operaii uzuale cu vectori avnd elemente de orice
tip (primitiv sau clas): afiare, sortare, cutare s.a.
- 30 -
INFORMATIC*I*
Metodele statice corespund funciilor din C; ele pot fi apelate fr a crea obiecte dar folosind i numele clasei
naintea numelui metodei. Metodele statice sunt de obicei i publice, pentru a fi apelate din afara clasei n
care sunt definite
Metoda main cu care ncepe execuia unei aplicaii Java trebuie s fie o metod static pentru c la nceput
nu exist nici un obiect. Clasa de bibliotec Math grupeaz metode (funcii) matematice care au argumente
i rezultate de un tip primitiv (double), deci nu sunt asociate unor obiecte sau clase. Exemplu:
class Sqrt {
public static void main (String args[ ]) {
int x = Integer.parseInt (args[0]);
double r = Math.sqrt (x);
System.out.println (r);
}
}
O clas Complex pentru numere complexe, va conine partea real i partea imaginar a unui numr
complex (ca date private), metode publice de acces la aceste date, metode mostenite (toString(), equals())
dar i operaii necesare lucrului cu numere complexe: adunare, scdere, .a.
public class Complex {
// datele clasei
private int re, im;
// constructor
public Complex (int re, int im) { this.re=re; this.im=im; }
// metode publice proprii clasei
public void add ( Complex cpx) { // adunarea a doua numere
re = re + cpx.re; im = im + cpx.im;
}
public void sub ( Complex cpx) { // scaderea a doua numere
re -= cpx.re; im -= cpx.im;
}
// metode mostenite si redefinite
public String toString () , return "(" + re + "," + im + ");}
Datele unui obiect au de obicei atributul private; ele nu sunt accesibile direct pentru metode din alte clase ci
numai prin intermediul metodelor clasei. De exemplu, variabilele re i im dintr-un obiect de tip Complex nu
pot fi citite sau modificate, dar metode ale clasei Complex opereaz cu aceste variabile. Dac este nevoie de
citirea sau de modificarea direct a datelor dintr-un obiect atunci se vor defini metode getter i setter :
- 31 -
INFORMATIC*I*
// re=2
Modificarea datelor din obiecte este consistent cu operaiile permise i se evit erori de programare cauzate
de modificri nedorite ale datelor. De exemplu, datele dintr-un vector folosit ca stiv nu se pot accesa sau
modifica aleator ci numai n cadrul impus de operaiile push i pop (la vrful stivei).
Toate clasele Java extind clasa Object i redefinesc cteva metode motenite de la clasa Object: toString(),
equals(), .a.
public class Complex extends Object {
... // date si constructori
// redefinire metoda mostenita
public String toString () {
StringBuffer sb = new StringBuffer ();
sb.append(re);
if (im>0) sb.append('+');
else { sb.append('-'); im =-im; }
return sb.append(im).toString();
}
... // alte metode
}
O funcie nu poate transmite n afar adresa unui obiect creat n funcie printr-un parametru referin.
Exemplu de funcie fr efect n afara ei:
// metoda statica pentru trecere sir in litere mari - gresit !!!
static void toUpper (String t) {
t = t.toUpperCase();
// se creeaza un nou obiect, cu alta adresa
}
Aici se creeaz un obiect String prin metoda toUpperCase(), iar adresa sa este memorat n variabila local
t (care coninea iniial adresa irului dat). Un obiect creat ntr-o funcie trebuie transmis ca rezultat al
funciei. Exemplu:
static String toUpper (String s) { return s.toUpperCase(); }
O funcie poate modifica un obiect a crui adres o primete ca argument numai dac n clasa respectiv
exist metode pentru modificarea obiectelor.
Clasele String, Integer, Float .a. nu conin metode pentru modificarea datelor din aceste clase, deci o
funcie care primete o referin la un astfel de obiect nu poate modifica acel obiect. In acest fel se protejeaz
obiectul transmis ca argument fa de modificarea sa nedorit de ctre funcia care l folosete. Obiectele din
clase fr metode de modificare a datelor (read-only) se numesc obiecte nemodificabile (immutable objects).
Pentru o scriere mai compact se practic uneori nlntuirea de metode (method chaining), adic aplicarea
unei metode asupra rezultatului unei alte metode, ntr-o aceeasi expresie. Exemple:
String line = f.readLine().trim().toUpperCase(); // citeste linie, elimina spatii si trece in litere mari
if ( fname.substring(fname.indexOf(.)+1).toLowerCase().equals(java) ) ...
- 32 -
INFORMATIC*I*
Utilizarea abuziv de metode statice este o prelungire a programrii procedurale n limbajele orientate obiect.
Utilizarea de metode obiect (nestatice) permite utilizarea unor tehnici specifice programrii cu obiecte:
derivare, motenire, polimorfism, programare cu interfete, s.a. Avantajele metodelor obiect sunt mai evidente
n cazul unor familii de clase deschise pentru extindere i atunci cnd se urmrete reutilizarea metodelor
unor clase n alte clase.
Majoritatea claselor sunt clase instaniabile (care pot genera obiecte prin instanierea clasei), deoarece
aciunile dintr-un program Java rezult prin apeluri de metode ntre obiecte.
Instanierea unei clase se face n Java folosind operatorul new, care aloc memorie pentru un nou obiect i
face iniializrile necesare. Iniializarea datelor (variabilelor) dintr-un obiect este realizat prin apelarea unei
funcii numit constructor, care are numele clasei. Orice clas instaniabil trebuie s aib (cel puin) un
constructor public (apelabil dintr-o alt clas). Este uzual ca o clas s aib civa constructori, care se
deosebesc prin argumente (parametri) i prin numrul de variabile iniializate.
- 33 -
INFORMATIC*I*
In lipsa unor constructori declarai explicit de programator, orice clas Java primete automat un constructor
public fr argumente i care nu are nici un efect.
- Atunci cnd rezultatul metodei este chiar obiectul pentru care s-a apelat metoda ( modificat) :
// metoda din clasa StringBuffer
public StringBuffer deleteCharAt (int k) {
// sterge caracterul din poz k
System.arraycopy (value, k+1, value, index, count-k-1);
count--;
// numar de caractere in sir
return this;
// rezultatul este obiectul modificat de metod
}
Un constructor se poate referi la alt constructor din aceeai clas numai folosind this i nu prin numele sau.
Exemplu:
public class MyVector {
private Object a[];
// adresa vector
private int n, nmax;
// dimensiune si capacitate vector
public MyVector (int m) {
// un constructor
nmax=m; n=0;
a= new Object[m];
// aloca memorie
}
// alt constructor
public MyVector () { this (10); } // capacitate implicita 10
.
}
- 34 -
INFORMATIC*I*
Clasa String este o clas read-only i final, deci nu se poate extinde cu metode de modificare a vectorului
de caractere coninut n fiecare obiect. Exemplu de utilizare greit a metodei replaceAll() din clasa String
pentru nlocuirea unui subir s1 cu un alt subir s2 n irul s:
s.replaceAll (s1,s2);
Metodele replaceAll() si replace() au ca rezultat un nou ir obinut dup substituire i nu modific obiectul
pentru care se apeleaz; de aceea utilizarea corect este urmtoarea:
s = s.replaceAll (s1,s2);
Clasele StringBuffer si StringBuilder (din 1.5) sunt variante ale clasei String care conin n plus i metode
pentru modificarea obiectului (irului). Exemple de metode care modific irul coninut ntr-un obiect de tip
StringBuffer: append, insert, delete, setCharAt, setLength. Un obiect de tipul StringBuffer transmis unei
funcii ca argument poate fi modificat de ctre funcie. Variant pentru funcia toUpper():
static void toUpper (StringBuffer s) {
String str= new String (s);
s.replace (0,str.length(),str.toUpperCase());
}
Clasa StringBuilder este mai performant dect StringBuffer pentru programele fr fire de execuie.
Concatenarea de iruri este o operaie frecvent n Java. Metoda println() folosit pentru afiarea pe ecran
poate avea un singur argument de tip String. Pentru a scrie mai multe iruri acestea se concateneaz ntr-un
singur ir cu operatorul +.Exemplu:
System.out.println ( x= + x);
// x de orice tip
Intr-o expresie cu operatorul binar +, dac unul din operanzi este de tip String, atunci compilatorul Java
face automat conversia celuilalt operand la tipul String (pentru orice tip primitiv i pentru orice tip clas care
redefineste metoda toString). Aceast observaie poate fi folosit si pentru conversia unui numr n ir de
caractere, ca alternativ a utilizrii metodei valueOf() din clasa String. Exemplu:
float x = (float) Math.sqrt(2);
String str = +x;
// sau str = String.valueOf(x);
O instruciune de forma a=a+b; cu a si b de tip String este tratat de compilator astfel: se transform
obiectele a i b n obiecte de tip StringBuilder, se apeleaz metoda append()i apoi creeaz un obiect String
din obiectul StringBuilder rezultat din concatenare:
String a=unu, b=doi;
StringBuffer am= new StringBuffer (a), am.append(bm);
a= new String (am);
Dac trebuie s facem multe concatenri de iruri este preferabil ca timp s se foloseasc direct metoda
append() din clasa StringBuilder. Exemplu:
public static String arrayToString ( int a[ ]) { // creare sir cu continutul unui vector
StringBuilder aux = new StringBuilder (*);
int n =a.length;
for (int i=0;i<n-1;i++)
aux.append (a*i+ + ,);
return new String (aux.append (a[n-1+ ++) ) ;
}
- 35 -
INFORMATIC*I*
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.
Metoda toString() exist n toate clasele i produce un ir cu datele din obiectul pentru care se apeleaz (face
conversia de la tipul datelor din obiect la tipul String). Orice tip de obiect se poate transforma ntr-un ir
String prin metoda toString(), apelat explicit sau implicit, dar nu i prin cast. Exemplu:
Float x = new Float(3.14); String s = (String)x; // eroare la compilare
String s =x.toString(); String s = x+; // apel implicit toString
Pentru extragerea de cuvinte (tokens) dintr-un text se folosesc fie clasele Scanner, StringTokenizer i
StreamTokenizer, fie clasele pentru lucrul cu expresii regulate: Pattern, Matcher .a.
Imprirea unui text n cuvinte se poate face folosind clasele Pattern, Matcher sau cu metoda split() din
clasa String, care creeaz un vector de cuvinte: String[] split (regex)
INFORMATIC*I*
trebuie fie aruncate (clauza throws cu numele excepiei n antetul funciei n care poate apare excepia), fie
tratate printr-un bloc de instruciuni (construcia try-catch).
Clasele din pachetul java.io pot fi grupate astfel:
- Clasa RandomAccessFile, pentru operaii de citire i/sau de scriere cu acces secvenial i direct
- Clasele din familiile InputStream si OutputStream, pentru operaii de citire octei i respectiv de scriere
octei (dar nu citire i scriere n acelai fiier)
- Clasele din familiile Reader si Writer, pentru operaii de citire caractere i respectiv de scriere caractere
(dar nu citire i scriere n acelai fiier).
Pentru a scrie numere ntr-un fiier text pe disc se creeaz mai nti un obiect FileInputStream sau
FileWriter i apoi se creeaz obiectul PrintStream sau PrintWriter. Exemplu de scriere ntregi:
try {
PrintStream ps = new PrintStream ( new FileOutputStream ("numere.txt")) ;
for (int x=1; x<100;x++)
ps.print (x+" ");
// cu spatii intre numere
ps.close();
} catch (IOException e) { e.printStackTrace();}
Exist i un corespondent al funciei sprintf() din limbajul C sub forma metodei format() din clasele
String si java.util.Formatter, cu o utilizare asemntoare funciei sprintf().
Pentru scrierea de iruri n fiiere text se poate utiliza direct metoda write () din clasa FileWriter, dar pentru
a scrie linii de text trebuie adugat explicit caracterul \n. Exemplu:
try {
FileWriter fw = new FileWriter("t.txt");
for (int i=1;i<21;i++)
fw.write (i+" ");
// conversie din int in String si spatii intre numere
fw.close();
} catch (IOException e) { e.printStackTrace();}
Citirea de linii de la tastatur sau dintr-un fiier text se poate face cu metoda readLine() din clasa
BufferedReader sau din DataInputStream, pentru citire din fisiere text formate din linii. Exemplu de citire
linii dintr-un fiier text pe disc:
String line, fname;
// linie de text si nume fisier
try {
BufferedReader br = new BufferedReader (new FileReader(fname));
while ( (line = br.readLine()) != null)
// rezultat null la sfrsit de fisier
System.out.println (line);
} catch (Exception e) { e.printStackTrace();}
Exemplu de citire linii de la tastatur (consol):
public static void main (String [] arg) throws IOException {
String line;
DataInputStream cin = new DataInputStream (System.in);
while ( (line = cin.readLine()) != null)
System.out.println (line);
}
- 37 -
INFORMATIC*I*
Pentru citire de numere de la consol trebuie extrase din linia citit irurile de cifre i fcut conversia din
format extern n format intern (binar) folosind metode statice ca Double.parseDouble(String s)
Integer.parseInt(String s), Float.parseFloat(String s), .a. Exemplu:
public static void main (String arg[])throws IOException{
float x,sum=0; String line, words[];
DataInputStream cin = new DataInputStream (System.in);
try {
while ( (line = cin.readLine()) != null){
words=line.split(" ");
for (String w: words)
if ( ! w.isEmpty()) {
x=Float.parseFloat(w);
sum += x;
}
}
} catch (NumberFormatException e){
System.out.println (sum);
}
}
Pentru programul anterior am considerat c se introduce o liter sau alt caracter nenumeric pentru a termina
secvena de numere introdus, iar aceast liter produce excepia de format nenumeric. La citirea dintr-un
fiier text pe disc este mai simplu, deoarece sfritul de fiier este recunoscut dup lungimea fiierului i nu
trebuie folosite caractere speciale ca terminator de fiier.
Clasa RandomAccessFile are o serie de avantaje fa de alte clase de I/E, dar acest tip nu apare ca posibil
argument n constructorii altor clase care folosesc fiiere disc: clase pentru compresie i arhivare de fiiere,
clase parser de fiiere text sau de fiiere XML, s.a. Aceti constructori au fie un argument File, fie un
argument de tip Reader (InputStream) sau Writer (OutputStream). Clasa RandomAccessFile permite
att operaii de scriere ct i operaii de citire, permite crearea i citirea de fiiere binare, cu numere n
format intern (readInt(), writeInt(), s.a.), permite citirea de linii de text (readLine()), permite accesul direct
la date din fiier pe baza adresei de octet n fiier (seek()), aflarea adresei curente n fiier (getFilePointer()),
aflarea lungimii unui fiier (length()) i permite citirea unui ntreg fiier n memorie (readFully()).
- 38 -
INFORMATIC*I*
INFORMATIC*I*
Se observ cum metoda addElement() din subclas apeleaz o metod motenit de la superclasa Vector i
anume contains(). Crearea de elemente cu aceeai valoare se poate face i cu metoda care modific valoarea
unui element dintr-o poziie dat setElementAt ( Object obj, int i), care ar trebui fie suprascris, fie interzis
pentru mulimi vector (n Java nu este permis accesul prin indici la mulimi).
n acest exemplu metoda suprascris din subclas apeleaz metoda cu acelai nume i argumente din
superclas, iar pentru a deosebi cele dou versiuni se foloseste cuvntul cheie super. Dac nu s-ar folosi
super atunci funcia ar fi infinit recursiv.
O subclas poate deveni superclas pentru alte (sub)clase, iar derivarea (extinderea) poate continua pe oricte
niveluri. In Java nu este permis extinederea simultana a mai multor clase pentru motenire multipl de date
i operaii de la cteva clase. Cuvntul extends poate fi urmat de un singur nume de clas.
Nu se poate extinde o clas final (cu atributul final) i nu pot fi redefinite metodele din superclas care au
unul din modificatorii final, static, private.
- 40 -
INFORMATIC*I*
INFORMATIC*I*
Redefinirea metodei toString() s-a fcut pentru a nu se afia i pentru mulimi perechi de valori
(identice) de forma k=v (k = cheie, v= valoare), aa cum se face n clasa dicionar Hashtable.
IE.03.4 Derivarea ca metod de reutilizare
Principala modalitate de specializare a unei clase este redefinirea unor metode din superclas. Prin redefinire
(override) se modific operaiile dintr-o metod, dar nu i modul de utilizare al metodei.
Clasa SortedArray definit anterior refolosete funcionalitatea clasei ArrayList, adic toate operaiile deja
definite acolo pentru vectori extensibili dinamic: adugare, inserie, nlocuire, eliminare de elemente,
adugarea unei colecii la vector, cutarea unei valori date de la nceput sau de la sfrit .a. In mod
asemntor se pot defini prin derivare clase pentru mulimi vector, pentru stive vector .a.
O clas pentru mulimi realizate ca list nlnuit poate fi obinut la fel de simplu, prin extinderea clasei
java.util.LinkedList, fr s ne intereseze detaliile de lucru cu lista, care sunt ncapsulate n clasa de
bibliotec deja definit.
- 42 -
INFORMATIC*I*
Absena unor clase pentru mulimi vector sau mulimi liste sau dicionare din vectori din bibliotecile Java se
explic tocmai prin uurina de a defini astfel de clase prin derivare de ctre programatori.
Avantajele reutilizrii unor clase existente este i mai evident n cazul claselor Swing sau AWT pentru
obiecte de interfa grafic: puini programatori ar putea s scrie singuri clase ca JFrame, JPanel,
JTextField, JTable, JTree s.a. dar obinerea unor subclase ale acestora este destul de simpl.
Pentru a interzice utilizarea unor metode motenite n clasa derivat se suprascriu acele metode cu o nou
definiie n care se arunc excepii de tip NotSupportedOperationException.
O clas derivat nu motenete constructorii superclasei. Dac nu exist nici un constructor definit explicit
ntr-o subclas atunci se genereaz automat un constructor care apeleaz un constructor fr argumente din
superclas (dac exist, altfel este eroare de compilare). Regula anterioar nu se aplic dac exist un
constructor cu argumente n subclas, caz n care apelul (super)constructorului fr argumente trebuie scris
explicit.
In clasa SortedArray nu apare explicit un constructor dar compilatorul Java genereaz automat un
constructor fr argumente care apeleaz constructorul fr argumente din superclasa ArrayList. Dac vrem
s avem i ali constructori n clasa derivat atunci trebuie s definim explicit toi aceti constructori.
Exemplu:
public class SortedArray extends ArrayList {
// constructori
public SortedArray() { super();}
public SortedArray(int n) { super(n);}
// metode
...
Un constructor dintr-o subclasa D poate apela un constructor din superclasa A folosind cuvntul cheie super
i nu prin numele sau. Apelul super() trebuie s fie prima instruciune din constructor. Aceast situaie
apare atunci cnd subclasa are variabile n plus fa de superclas; iniializarea variabilelor motenite se face
prin constructorul superclasei iar iniializarea variabilelor proprii subclasei se face n constructorul subclasei.
Exemplu:
public class SortedArray extends ArrayList {
private Comparator cmp=null; // obiect comparator folosit la sortare
// constructori
public SortedArray() { super();}
public SortedArray (Comparator comp) {
super(); cmp=comp;
}
// metode
public int indexOf (Object obj) {
if (cmp==null)
return Collections.binarySearch (this,obj);
else
return Collections.binarySearch (this,obj,cmp);
}
...
}
- 43 -
INFORMATIC*I*
Conversia de la un subtip la supertip (upcast) se face automat, fr a folosi operatorul de conversie, deoarece
se pot folosi toate metodele din supertip i n subtip. Exemplu:
Vector v= new Vector;
Object obj = v;
Conversia de la un supertip la un subtip (downcast) se face folosind operatorul de forare a tipului (cast),
deoarece subtipul poate avea metode n plus fat de supertip. Exemplu:
Object obj;
Vector v = (Vector) obj;
Conversia de tip se aplic unor variabile referin (sau unor argumente de funcii) i nu unor obiecte. Tipul
variabilei referin este folosit de compilator pentru a verifica dac metodele folosite cu aceast variabil
exist pentru tipul respectiv sau nu. O variabil de tip Object nu poate fi urmat de o metod din clasa
Vector (de ex. size()) chiar dac obiectul referit de variabil este de tip Vector.
Anumite conversii de tip nu pot fi verificate la compilare iar erorile de conversie apar la execuie, ca excepii
ClassCastException. Exemple:
Object x = new String() ;
Object y = new Vector() ;
Vector v = (Vector) x; // exceptie la executie: un String nu poate fi transformat n vector
int sz= y.size();
// eroare la compilare : clasa Object nu are metoda size()
Prin derivare succesiv se pot crea ierarhii de tipuri compatibile. Exemplu:
// vector ordonat
public class SVec extends Vec { .... }
// multime vector
public class VSet extends Vec { ... }
- 44 -
INFORMATIC*I*
// multime ordonata
public class SVSet extends VSet {... }
De remarcat c dou subtipuri ale unui tip comun A nu sunt compatibile ( de exemplu tipurile SVec i VSet
nu sunt compatibile, dei ambele sunt derivate din tipul Vec). Trecerea ntre cele dou tipuri nu se poate face
prin operatorul de conversie, dar se poate face prin construirea altui obiect.
Uneori subclasele nu adaug nimic superclasei dar motivul extinderii este crearea unei familii de tipuri
compatibile. Un exemplu este familia claselor excepie, derivate direct din clasa Exception sau din subclasa
RuntimeException. Instruciunea throw trebuie s conin un obiect de un tip compatibil cu tipul
Exception; de obicei un subtip ( IOException, NullPointerException s.a.). O subclas a clasei Exception
nu adaug metode noi i conine doar constructori:
public class IOException extends Exception {
public IOException() { super(); }
public IOException(String s) { super(s); } // s= un mesaj suplimentar
}
public class EOFException extends IOException {
public EOFException() { super(); }
public EOFException(String s) { super(s); } // s= un mesaj suplimentar
}
Ierarhia claselor excepie permite tratarea individual a unor excepii de I/E (sfrit de fiier, fiier inexistent,
eroare de cirire/scriere) sau tratarea lor colectiv, folosind tipul mai general IOException:
public static void main (String[] arg) { // o singura actiune ptr toate exceptiile
RandomAccessFile f=null;
try {
f= new RandomAccessFile("date.bin","r");
while ( true)
System.out.println ( f.readInt());
} catch (IOException e) { e.printStackTrace();} // ptr toate exceptiile de I/E
}
- 45 -
INFORMATIC*I*
IE.03.6 Polimorfism
O funcie polimorfic este o funcie care are acelasi prototip, dar implementri (definiii) diferite n clase
diferite dintr-o ierarhie de clase (obinute prin suprascrierea metodei).
Metodele equals() i toString() sunt exemple tipice de funcii polimorfice, al cror efect depinde de tipul
obiectului pentru care sunt apelate (altul dect tipul variabilei referin). Exemple:
Object d = new Date(); Object f = new Float (3.14);
String ds = d.toString();
// apel Date.toString()
String fs = f.toString();
// apel Float.toString()
In definirea clasei Entry polimorfismul explic de ce n metoda equals() nu este un apel recursiv; ceea ce se
apeleaz este metoda equals() din clasa de care aparine cheia (diferit de clasa Entry):
public boolean equals (Object obj) {
Entry e = (Entry)obj;
return key.equals (e.key);
}
Asocierea unui apel de metod cu funcia ce trebuie apelat se numeste "legare" (Binding). Legarea se poate
face la compilare (legare timpurie) sau la execuie ("legare trzie" sau "legare dinamic"). Pentru
metodele finale i statice legarea se face la compilare, adic un apel de metod este tradus ntr-o instruciune
de salt care conine adresa funciei apelate. Legarea dinamic are loc n Java pentru orice metod care nu
are atributul final sau static, metod numit polimorfic. In Java majoritatea metodelor sunt polimorfice.
Metodele polimorfice Java corespund funciilor virtuale din C++.
Figura urmtoare ilustreaz mecanismul de realizare a polimorfismului pe traseul :
variabil referin obiect tabel de metode al clasei definiie metod polimorfic n clasa respectiv.
- 46 -
INFORMATIC*I*
Fiecare obiect Java conine, pe lng datele nestatice din clas, un pointer la tabela de metode virtuale
(polimorfice). Compilatorul traduce un apel de metod polimorfic printr-o instruciune de salt la o adres
calculat n funcie de tipul obiectului adresat de variabila referin i de definiia clasei.
Exemplu de secven pentru explicarea mecanismului:
Vec v = new Vec(); v.add(1);
VSet vs = new VSet(); vs.add (1); String s = vs.toString();
Apelul v.add() pleac de la obiectul referit de variabila "v" la tabela metodelor clasei Vec i de acolo ajunge
la corpul metodei add() din clasa Vec. Apelul vs.toString() conduce la metoda toString() din clasa VSet i de
acolo la metoda toString() din superclasa Vec deoarece este o metod motenit i care nu a fost redefinit.
Apelul vs.equals(v) merge n sus pe ierahia de clase i ajunge la definiia din clasa Object, deoarece nici o
subclas a clasei Object nu redefinete metoda equals(), n acest exemplu.
Alegerea automat a variantei potrivite a unei metode polimorfice se poate face succesiv, pe mai multe
niveluri. De exemplu, apelul metodei toString() pentru un obiect colecie alege funcia ce corespunde tipului
de colecie (vector, lista nlntuit, etc); la rndul ei metoda toString() dintr-o clas vector apeleaz metoda
toString() a obiectelor din colecie, n funcie de tipul acestor obiecte (String, Integer, etc.).
Soluia funciilor polimorfice este specific programrii orientate pe obiecte i se bazeaz pe existena unei
ierarhii de clase, care conin metode (nestatice) cu aceeai semnatur, dar cu efecte diferite n clase diferite.
Numai metodele obiectelor pot fi polimorfice, n sensul c selectarea metodei apelate se face n funcie de
tipul obiectului pentru care se apeleaz metoda. O metod static, chiar dac este redefinit n subclase, nu
poate fi selectat astfel, datorit modului de apelare.
Exemplul urmtor este o simplificare a polimorfismului din familiile de clase AWT sau Swing: toate
componentele de interfa grafic AWT sunt derivate din clasa Component i au o metod paint() pentru
- 47 -
INFORMATIC*I*
afiarea pe ecran a componentei; clasa Container este o component care poate conine mai multe alte
componente. Simplificarea aici se refer la efectul metodei polimorfice paint(), care scrie un mesaj n loc s
deseneze componenta, dar efectul ei depinde de tipul obiectului pentru care se apeleaz.
abstract class Component { public abstract void paint (); }
class Container extends Component {
private Vector v;
public Container(){v= new Vector();}
public void add (Component d){v.add(d);}
public void paint (){
for (int i=0;i<v.size();i++)
((Component)v.get(i)).paint();
}
}
class SquareButton extends Component {
private int x1,y1,x2,y2;
public SquareButton (int a1,int a2,int b1,int b2) { x1=a1;x2=a2;y1=b1;y2=b2; }
public void paint () { System.out.println ("Square ("+x1+","+x2+","+y1+","+y2+")"); }
}
class RoundButton extends Component {
private int xc,yc,rc;
public RoundButton (int x,int y,int r){ xc=x;yc=y;rc=r; }
public void paint () { System.out.println ("Round ("+xc+","+yc+","+rc+")"); }
}
class A {
public static void main (String [] a) {
Container c1 = new Container ();
c1.add (new SquareButton(1,1,5,5)); c1.add (new RoundButton(3,3,6));
Container c2 = new Container();
c2.add (new SquareButton(7,7,9,9)); c2.add (new SquareButton(6,6,8,8));
c2.add(c1); c2.paint ();
}
}
Delegare sau compunere este atunci cnd o clas D conine o variabil (delegat) de un tip A iar o parte din
metodele clasei D apeleaz metode din clasa A prin intermediul variabilei delegat. Exemplu de clas pentru
mulimi care deleag operaii ctre clasa Vector:
- 48 -
INFORMATIC*I*
class VSet {
private Vector v;
// variabila delegat
public VSet () { v= new Vector(); }
public boolean add (Object obj) {
if ( v.contains(obj))
return false;
// nu s-a modificat multimea
v.addElement (obj);
return true;
}
public String toString () { return v.toString(); }
public int size() { return v.size();}
}
Sursa clasei anterioare poate fi mai mare dac vrem pentru mulimi i alte operaii existente n clasa Vector.
Delegarea creeaz o relaie de tipul D conine un A ( D has an A) i se recomand atunci cnd D folosete
puine metode din A sau cnd se folosesc subtipuri ale tipului A.
Derivarea conduce la un cod mai compact pentru noua clas (nu se mai declar metodele motenite) i
creeaz tipuri compatibile. Relaia dintre clase derivate este o relaie static stabilit la compilare.
Derivarea creeaz o relaie de tipul D este un fel de A (D is a kind of A) i se recomand atunci cnd noua
clas D folosete o mare parte a metodelor din A (i adaug sau modific numai cteva metode) sau atunci
cnd tipul D trebuie s fie compatibil cu tipul A.
Alegerea ntre cele dou tipuri de relaii (is a sau has a) poate fi discutabil i subiectiv: ce este o stiv?
Este un caz particular de vector (list) sau este un obiect care conine un vector (o list) ? Metodele clasei
stiv au nume consacrate i diferite de numele metodelor din clasele ArrayList (Vector) sau LinkedList.
Clasele Java pentru stive sunt: java.util.Stack care este derivat din clasa Vector i clasa mai nou
java.util.ArrayDeque care conine un vector, deci se folosesc ambele metode de reutilizare.
Pentru comparaie urmeaz trei variante de realizare a unei stive ca list nlnuit: prin derivare, prin
delegare i prin derivare plus delegare.
Exemplu de clasa stiv obinut prin derivare din clasa LinkedList :
class StackList extends LinkedList {
public void push (Object obj) {
addFirst (obj);
// super.addFirst(obj);
}
public Object pop () {
return removeFirst();
// super.removeFirst ();
}
public Object peek () {
return peekFirst(); // super.peekFirst();
}
public boolean empty() {
return size()==0; // super.size()
}
// toString() este mostenita si poate fi folosita ptr afisare stiva
}
- 49 -
INFORMATIC*I*
Exemplu de clas stiv obinut prin delegare din clasa LinkedList (pentru stive liste nlnuite):
class StackList {
private LinkedList stack;
// variabila delegat
public StackList (LinkedList list) { stack=list; }
// delegare de operatii catre LinkedList
public Object push (Object obj) {
stack.add (0,obj); return obj;
}
public Object pop () {
Object obj= stack.get(0); stack.remove(obj);
return obj;
}
public Object peek () { return stack.get(0); }
public boolean empty() { return stack.size()==0; }
public String toString() { return stack.toString(); }
}
Delegarea permite mai mult libertate n alegerea obiectului folosit ca delegat; acest obiect este primit prin
constructor i poate avea diferite tipuri compatibile cu variabila delegat. Relaia dintre clasele D i A se
poate modifica dinamic, la execuie. Exemplul urmtor foloseste derivare i delegare pentru o stiv
neprecizat ca implementare la definirea clasei; Clasa StackList extinde pe AbstractList i conine o
variabil (delegat) de tip AbstractList. Stabilirea tipului de colecie folosit ca stiv (ArrayList sau
LinkedList) se face la construirea unui obiect:
StackList st = new StackList ( new ArrayList()); // sau (new LinkedList())
class StackList extends AbstractList {
private AbstractList stack;
// delegat clasa abstracta
public StackList (AbstractList list) { stack=list; } // concretizare delegat
public Object push (Object obj) {
stack.add (0,obj);
return obj;
}
public Object pop () {
Object obj= get(0);
stack.remove(obj);
return obj;
}
public Object peek() {
return get(0);
}
public boolean empty() { return stack.isEmpty(); }
public int size() { return stack.size(); } // abstracta in AbstractList
public Object get (int i) { return stack.get(i); } // abstracta in AbstractList
}
INFORMATIC*I*
class A {
public void f1 () { System.out.println ("A.f1"); }
}
class B {
public void f2 () { System.out.println ("B.f2"); }
}
class D extends A {
private B b = new B ();
public void f2 () { b.f2();} // delegare obiect b pentru operatia f2
}
class X {
public static void main (String arg[]) {
D m = new D();
d.f1();
// metoda mostenita de la A
d.f2();
// metoda din clasa B
}
}
Aceast soluie de motenire multipl este folosit n cteva clase JFC (Swing) de tip model; de exemplu,
clasa DefaultListModel preia prin motenire metode de la superclasa AbstractListModel i deleag unei
variabile interne de tip Vector operaii cu un vector de obiecte. Un model de list este un vector cu
posibiliti de generare evenimente (de apelare asculttori la evenimente ) la modificri operate n vector. In
general o clas model din JFC este o colecie care poate avea asculttori i poate apela metode din
obiectele asculttor (genereaz evenimente semnificative pentru asculttori).
Urmeaz un exemplu mai simplu: o mulime realizat ca vector, care extinde clasa AbstractSet i conine o
variabil de tip ArrayList. Clasa preia de la ArrayList metodele add(), iterator() i size() i de la
AbstractSet alte metode definite n functie de iterator() i size() : contains(),toString(),remove() .a.
public class ArraySet extends AbstractSet {
private ArrayList set;
public ArraySet() {
set = new ArrayList();
}
public boolean add (Object obj) {
if (! contains(obj) )
return set.add(obj);
// delegare pentru operatia de adaugare
return false;
}
public Iterator iterator() {
return set.iterator();
// delegare pentru creare obiect iterator
}
public int size() { return set.size(); } // delegare metoda size()
}
INFORMATIC*I*
clasele flux de intrare sunt subtipuri ale tipului InputStream (Reader) i toate clasele flux de ieire sunt
subtipuri ale tipului OutputStream (Writer).
Numrul de clase instaniabile de I/E este relativ mare deoarece sunt posibile diverse combinaii ntre
suportul fizic al fluxului de date i facilitile oferite de fluxul respectiv. Dup suportul fizic al fluxului de
date se poate alege ntre: fisiere disc ( FileInputStream, FileOutputStream, FileReader, FileWriter s.a.),
vector de octei sau de caractere (ByteArrayInputStream, ByteArrayOutputStream, CharArrayReader,
CharArrayWriter), buffer de iruri n memorie (StringBufferInputStream, StringBufferOutputStream),
canal pentru comunicarea sincronizat ntre fire de execuie (PipedReader, PipedWriter,
PipedInputStream, PipedOutputStream).
Dup facilitile oferite avem de ales ntre: citire-scriere la nivel de octet sau bloc de octei (metode read(),
write()), citire-scriere pe octei dar cu zon buffer (BufferedInputStream, BufferedReader,
BufferedOutputStream, BufferedWriter), citire-scriere la nivel de linie i pentru numere de diferite tipuri,
fr conversie (DataInputStream, DataOutputStream), citire cu punere napoi n flux a ultimului octet
citit (PushBackInputStream), citire nsoit de numerotare automat a liniilor citite
(LineNumberInputStream).
Combinarea celor 8 clase surs/destinaie cu opiunile de prelucrare asociate transferului de date se face prin
intermediul claselor anvelop , care sunt numite i clase filtru de intrare-ieire. Dac s-ar fi utilizat
derivarea pentru obinerea claselor direct utilizabile atunci ar fi trebuit generate, prin derivare, combinaii ale
celor 8 clase cu cele 4 opiuni, deci 32 de clase (practic, mai puine, deoarece unele opiuni nu au sens pentru
orice flux). Pentru a folosi mai multe opiuni cu acelasi flux ar fi trebuit mai multe niveluri de derivare i
deci ar fi rezultat un numr i mai mare de clase.
Clasele de tip filtru sunt clase intermediare, din care sunt derivate clase care adaug operaii specifice (de
prelucrare): citire de linii de text de lungime variabil, citire-scriere de numere n format intern, scriere
numere cu conversie de format .a. O clas anvelop de I/E conine o variabil de tipul abstract
OutputStream sau InputStream, care va fi nlocuit cu o variabil de un tip flux concret
(FileOutputStream, ...), la construirea unui obiect de un tip flux direct utilizabil.
Clasele filtru de I/E fac parte din categoria clasele decorator, care aplic diverse decoraiuni
(funcionaliti) unor clase existente. Un decorator nu adaug funcii noi clasei decorate dar modific
aciunea unor metode existente prin delegare ctre metode ce provin din obiectul transmis ca argument la
construirea unui obiect din clasa decorator (i care nlocuiete o variabil de tip interfa din clasa decorator).
Clasa FilterInputStream este derivat din InputStream i conine o variabil de tip InputStream:
public class FilterInputStream extends InputStream {
protected InputStream in;
protected FilterInputStream (InputStream in) {
// constructor
this.in=in;
// adresa obiectului "flux"
}
// citirea unui octet
public int read () throws IOException {
return in.read (); // citirea depinde de tipul fluxului
}
... // alte metode
}
Metoda read() este o metod polimorfic, iar selectarea metodei necesare se face n funcie de tipul concret
al variabilei "in" (transmis ca argument constructorului). Metoda read() din FilterInputStream preia
- 52 -
INFORMATIC*I*
funcionalitatea metodei read() din clasa delegat, sau deleag operaia de citire ctre clasa folosit la
construirea obiectului filtru.
Nu se pot crea obiecte de tipul FilterInputStream deoarece constructorul clasei este protected, dar se pot
crea obiecte din subclase ale clasei FilterInputStream: DataInputStream, BufferedInputStream,
PushbackInputStream, LineNumberInputStream. Cea mai folosit este clasa DataInputStream care
adaug metodelor de citire de octei motenite i metode de citire a tuturor tipurilor primitive de date:
readInt(), readBoolean(), readFloat(), readLine(), etc.
La crearea unui obiect de tipul DataInputStream constructorul primete un argument de tipul InputStream
(sau un tip derivat direct din InputStream sau din FilterInputStream) prin care se specific suportul fizic
al datelor i modul concret de citire din flux. Pentru citire linii dintr-un fiier disc metoda readLine() deleaga
citirea de octei ctre metoda read() din FileInputStream:
FileInputStream fis= new FileInputStream (t.txt);
DataInputStream dis = new DataInputStream (fis);
Decorarea unei clase flux de I/E se poate face repetat; astfel pentru a citi linii dintr-un fiier folosind o zon
tampon i numerotare de linii vom folosi urmtoarea secven de instruciuni:
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 fiier 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() i altele. Familia claselor ReaderWriter folosete de asemenea clase care combin derivarea cu delegarea.
- 53 -
INFORMATIC*I*
INFORMATIC*I*
O clas abstract este de multe ori o implementare parial a unei interfee, pentru a simplifica definirea de
clase instaniabile care s respecte interfaa. In clasa abstract mai multe metode sunt definite n funcie de
cteva metode abstracte (cum ar fi cea care produce un iterator). Clasele concrete care vor extinde clasa
abstracta vor avea de definit numai cteva metode Exemplu:
public interface Collection { // declara metode prezente in orice colectie
int size();
// dimensiune colectie
boolean isEmpty();
// verifica daca colectia este goala
// alte metode
}
public abstract class AbstractCollection implements Collection {
public abstract int size();
// metoda abstracta
public boolean isEmpty ( return size()==0;} // metoda implementata
}
In bibliotecile de clase Java exist cteva clase adaptor, care implementeaz o interfa prin metode cu
definiie nul, unele redefinite n subclase. Ele sunt clase abstracte pentru a nu fi instaniate direct, dar
metodele lor nu sunt abstracte, pentru c nu se tie care din ele sunt efectiv necesare n subclase. Exemplu
de clas adaptor util n definirea de clase asculttor la evenimente generate de tastatur:
public abstract class KeyAdapter implements KeyListener {
public void keyTyped(KeyEvent e) { }
// la apasare+ridicare tasta
public void keyPressed(KeyEvent e) { }
// numai la apasare tasta
public void keyReleased(KeyEvent e) { } // numai la ridicare tasta
}
O subclas (care reacioneaz la evenimente generate de taste) poate redefini numai una din aceste metode,
fr s se preocupe de celalte metode nefolosite de subclas:
class KListener extends KeyAdapter {
public void keyPressed(KeyEvent e) {
char ch = e.getKeyChar();
// caracter generat de tasta apasata
...
// folosire sau afisare caracter ch
}
}
Clasele Reader i Writer din pachetul java.io sunt clase generale de citire sau scriere din/n fluxuri
de caractere care au doar dou metode abstracte: read() sau write() i close(). Ele au un numr mare
de subclase care adaug i alte metode specifice lor: FileReader, FilterReader, BufferedReader,
StringReader, , etc. Fragment din clasa Reader:
public abstract class Reader {
protected Reader();
abstract void close();
int read ( char[] buf);
int read();
void reset ();
.
}
// constrcuctor
// depinde de tipul fluxului
// apeleaza metoda abstracta
// citeste un caracter, apeleaza metoda abstracta
// la fel ptr orice flux
// alte metode neabstracte
- 55 -
INFORMATIC*I*
- 56 -
INFORMATIC*I*
Clasa Object conine metoda clone(), folosit numai de clasele care declar c implementeaz interfaa
Clonable. Metoda neabstract clone() este motenit automat de toate clasele Java, dar este aplicabil numai
pentru o parte din clase. Pentru a semnala utilizarea greit a metodei clone() pentru obiecte ne-clonabile, se
produce o excepie de tip CloneNotSupportedException atunci cnd ea este apelat pentru obiecte din clase
care nu ader la interfaa Clonable.
O utilizare asemntoare o are interfaa Serializable, pentru a distinge clasele ale cror obiecte sunt
serializabile (care conin metode de salvare i de restaurare n / din fiiere) de clasele ale cror obiecte nu pot
fi serializate ( fr obiecte "persistente"). Practic, toate clasele cu date sunt serializabile.
Interfee ca Serializable si Clonable se numesc interfee de "marcare" a unui grup de clase (tagging
interfaces), pentru a permite anumite verificri.
- 57 -
INFORMATIC*I*
O interfa care stabileste un tip comun poate fi att de general nct s nu conin nici o metod. Un
exemplu este interfaa EventListener (pachetul "java.util"), care stabilete tipul asculttor la evenimente,
dar metodele de tratare a evenimentului nu pot fi precizate nici ca prototip, deoarece depind de tipul
evenimentului. Interfaa este extins de alte interfee, 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);
}
O clas cu toate metodele abstracte i fr date poate fi definit fie ca o clas abstract, fie ca o interfa. In
Java 1 clasa abstract java.util.Dictionary coninea metode obligatorii pentru orice obiect dicionar, dar a
cror definire depinde de implementare. In Java 2 a fost introdus, ca alternativ preferabil, interfaa Map
care conine metodele din clasa Dictionary i alte metode abstracte.
Interfeele sunt preferabile n general claselor abstracte, pentru c ofer mai mult libertate subclaselor.
Avantajul soluiei interfa n raport cu o clas abstract este evident atunci cnd definim un dicionar
realizat ca un vector de perechi cheie-valoare: o clas derivat din clasa ArrayMap ar putea moteni metode
utile de la superclas, dar nu ar putea extinde i clasa abstract Dictionary. Exemplu:
public class ArrayMap extends ArrayMap implements Map { ... }
Interfeele si clasele abstracte nu trebuie opuse, iar uneori sunt folosite mpreun ntr-un framework cum este
cel al claselor colecie sau cel al claselor Swing (JFC): interfaa definete metodele ce ar trebui s existe mai
multe clase, iar clasa abstract este o implementare parial a interfetei, pentru a facilita definirea de noi clase
prin extinderea clasei abstracte. In felul acesta se obine o familie de clase deschis pentru extinderi
ulterioare cu clase compatibile, care nu trebuie definite ns de la zero. Un exemplu este interfaa Map i
clasa AbstractMap care permit definirea rapid de noi clase pentru dicionare (altele dect HashMap i
TreeMap), compatibile cu interfaa Map dar care beneficiaz de metode motenite de la clasa abstract.
Exemplu de dicionar realizat ca vector:
class ArrayMap extends AbstractMap {
// dictionar vector de perechi
private ArrayList entries ;
// perechi cheie-valoare
public ArrayMap (int n) {entries = new ArrayList(n); }
public Object put ( Object key, Object value) {
for (SimpleEntry e: entries)
// clasa SimpleEntry inclusa n AbstractMap
if (e.getKey().equals(key)){
// daca exista cheia in dictionar
Object v = e.getValue();
// pentru rezultatul metodei
entries.remove(e);
// inlocuire valoare prin eliminare
entries.add (new SimpleEntry(key,value)); // si adaugare
return v;
}
entries.add (new SimpleEntry(key,value)); // daca nu exista cheia in dictionar
return null;
}
- 58 -
INFORMATIC*I*
In general se recomand programarea la nivel de interfa i nu la nivel de clas particular, pentru a avea
programe mai generale i mai uor de modificat. Ideea este de folosi variabile de un tip ct mai general, care
se pot referi la obiecte de orice subtip compatibil cu acesta. Mai mult, modificarea tipului obiectului referit se
poate face la execuie. Un exemplu este o variabil mulime sau dicionar care va definit de tipul Set sau
Map, pentru ca ulterior (la atribuirea unei referine) s se poata preciza sau modifica tipul concret de
mulime sau de dicionar (HashSet, TreeSet, HashMap, TreeMap, etc.) Exemplu:
Map d = new HashMap(); // se poate nlocui cu:
d= new ArrayMap();
Clasele care declar implementarea acestei interfee trebuie s conin o definiie pentru metoda
compareTo(), cu argument de tip Object. Exemple de clase Java cu obiecte comparabile : Integer, Float,
String, Date, BigDecimal .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 i de aceea se face o conversie
de la tipul general Object la subtipul Comparable. Exemplu:
- 59 -
INFORMATIC*I*
Functia Arrays.sort() are i o form cu dou argumente; al doilea argument este de tipul Comparator i
precizeaz funcia de comparare (alta dect compareTo()). Funcia care determin maximul dintr-un vector
de obiecte poate fi scris i astfel:
public static Object max (Object a[ ], Comparator c) {
Object maxim = a[0];
for (int k=1; k<a.length;k++)
if ( c.compare (maxim, a[k]) < 0) // c este un obiect comparator
maxim = a[k];
return maxim;
}
Funcia max() cu dou argumente este mai general i poate fi folosit pentru a ordona un acelai vector
dup diferite criterii (dup diverse proprieti). De exemplu, pentru ordonarea unui vector de iruri dup
lungimea irurilor, 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());
Funcia compare() din interfaa Comparator poart numele de funcie callback, deoarece este apelat de
metode existente (sort(), max() din clasele Arrays sau Collections) dar trebuie furnizat de aplicaia care
folosete una din aceste metode. La scrierea funciei de sortare nu se cunotea exact definiia funciei de
comparare (pentru c pot fi folosite diverse funcii de comparare), dar s-a putut preciza prototipul funciei de
comparare, ca metod abstract dintr-o interfa. Putem deci s scriem o funcie care apeleaz funcii nc
nedefinite, dar care respect toate un prototip (tip rezultat, tip i numr de argumente).
- 60 -
INFORMATIC*I*
Tehnica callback este folosit la scrierea unor clase de bibliotec, ale cror funcii apeleaz funcii din
programele de aplicaii, scrise ulterior de utilizatorii pachetului. Ideea general este c o parte din algoritmul
implementat de clasa de bibliotec depinde de specificul aplicaiei care foloseste acest algoritm. Un algoritm
de sortare foloseste o funcie de comparare, dar funcia de comparare face parte din aplicaie, pentru c ea
compar date din aplicaie. Situaia poate fi schematizat astfel: o funcie A (main(), de ex.) apeleaz o
funcie B (sort(), de ex.), iar B apeleaz o funcie X (compare()) dintr-un grup de funcii posibile. Adresa
funciei X este primit de B de la funcia A. De fiecare dat cnd A apeleaz pe B i transmite i adresa
funciei X, care va fi apelat napoi de B.
Aplicaie
Biblioteci JDK
main
sort
compare
IE.04.5 Interfee Java pentru enumerare
Interfaa mai veche java.util.Enumeration conine dou metode comune oricrei clase cu rol de enumerare
a elementelor unei colecii de obiecte:
public interface Enumeration {
boolean hasMoreElements();
Object nextElement();
}
Enumerarea unor obiecte poate fi privit ca o alternativ la crearea unui vector cu obiectele respective, n
vederea prelucrrii succesive a unui grup de obiecte. Dintre clasele care implementeaz aceast interfa,
sunt clasele enumerator pe vector i enumerator pe un tabel de dispersie Hashtable. Ulterior s-au adugat
metode din aceast interfa i clasei StringTokenizer, care face o enumerare a cuvintelor dintr-un text:
public class StringTokenizer implements Enumeration {
...
public boolean hasMoreElements() { return hasMoreTokens(); }
public Object nextElement() { return nextToken(); }
}
Exemplu de metod care calculeaz de cte ori apare un obiect ntr-o colecie de obiecte folosind numai
enumeratorul coleciei:
public static int count (Enumeration enum, Object obj) {
int m=0;
while (enum.hasMoreElements() )
if (enum.nextElement().equals(obj) )
m++;
return m;
}
- 61 -
INFORMATIC*I*
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() :
public Enumeration elements() { return new VectorEnumerator (this);}
// definirea clasei iterator pe vector
class VectorEnumerator implements Enumeration {
private Vector v;
private int crt = 0;
// indice element curent din enumerare
public VectorEnumerator (Vector v) { this.v=v; }
public boolean hasMoreElements() { return crt < v.size(); }
public Object nextElement() { return v.elementAt (crt++); }
}
De observat c interfaa Enumeration nu are metode pentru modificarea obiectelor enumerate, care ar putea
veni n conflict cu metodele clasei colecie care adaug sau elimin elemente din colecie.
Incepnd din Java 2 s-a introdus interfaa java.util.Iterator ca alternativ la interfaa Enumeration:
public interface Iterator {
boolean hasNext();
Object next();
void remove();
}
Metoda iterator() din clasele colecie are ca rezultat un obiect Iterator i nlocuiete metoda elements() care
producea un obiect de tip Enumeration. Exemplu de clasa iterator pentru liste simplu nlnuite:
public class SimpleList extends AbstractList {
private Node head;
// inceput lista
private int n;
// nr de noduri in lista
public SimpleList () {
// constructor
head= new Node(null);
// santinela
}
public int size() { return n; }
public Iterator iterator () { return new SListIterator(); }
...
}
class SListIterator implements Iterator {
private Node pos=head.next;
public boolean hasNext () { return pos != null; }
public Object next() {
Object obj =pos.val; pos=pos.next;
return obj;
}
public void remove () { throw new UnsupportedOperationException(); }
}
- 62 -
INFORMATIC*I*
- 63 -
INFORMATIC*I*
Clasa File din pachetul java.io poate genera obiecte ce corespund unor fiiere sau directoare. Ea este
destinat operaiilor de listare sau prelucrare a fiierelor dintr-un director i nu conine functii de citirescriere din/n fiiere. Cel mai folosit constructor din clasa File primete un argument de tip String ce
reprezint numele unui fiier de date sau unui fiier director. Cea mai folosit metod a clasei File este
metoda list() care produce un vector de obiecte String, cu numele fiierelor din directorul specificat la
construirea obiectului File. Exemplu de folosire a unui obiect File pentru afiarea coninutului directorului
curent:
public static void main (String arg[]) throws IOException {
String dir =".";
// "numele" directorului curent
File d =new File(dir);
// java.io.File d = new java.io.File(dir);
String [ ] files = d.list();
// vector cu fisierele din directorul curent
System.out.println ("Directory of "+ d.getAbsolutePath());
for (int i=0; i< files.length;i++)
System.out.println (files[i]);
}
Metoda listFiles() produce un vector de obiecte File ce corespund fiierelor din directorul pentru care se
apeleaz metoda. Metodele list()i listFiles() au i o variant cu un argument de un tip interfa ce specific
filtrul aplicat fisierelor, pentru extragere selectiv de fiiere din director:
- metoda list() cu argument de tip FilenameFilter
- metoda listFiles() cu argument de tip FileFilter sau FilenameFilter.
Interfeele FileFilter i FilenameFilter conin o singur metod accept(), care va fi apelat de list() sau
listFiles() pentru fiecare fiier din director i care spune dac acel fiier este sau nu acceptat n vectorul creat
de funcie.
public interface FilenameFilter {
// prezenta din jdk 1.0
boolean accept (File path, String filename);
// path=director(cale)
}
public interface FileFilter {
// prezenta din jdk 1.2
boolean accept (File path);
// path=nume complet fisier (cu cale)
}
Utilizatorul are sarcina de a defini o clas care implementeaz una din aceste interfee i de a transmite o
referin la un obiect al acestei clase fie metodei list() fie metodei listFiles(). Exemplu de filtru dup tipul
(extensia numelui) fisierelor:
class FileTypeFilter implements FileFilter {
// clasa pentru obiecte filtru
String ext;
// extensie nume fisier
public FileTypeFilter (String ext) { this.ext = ext; }
public boolean accept (File f) {
String fname = f.getName();
// nume fisier
return fname. endsWith("."+ext) ;
}
}
// utilizare
File d =new File(".");
// din directorul curent
File [ ] files = d.listFiles(new FileTypeFilter("java"));
- 64 -
INFORMATIC*I*
Funcia accept() din interfeele filtru este tot o funcie callback, apelat de metodele list() i listFiles() din
clasa de bibliotec File, dar definit ca parte din aplicaie pentru criteriul de filtrare dorit.
Exemplu de definire a unei interfee de tip filtru pentru orice fel de obiecte, utilizat pentru filtrarea unui
vector i creare a unui alt vector cu elementele selectate de filtru:
public interface Filter {
boolean accept (Object s);
}
public static Vector select (Vector v, Filter f) {
Vector r = new Vector();
Enumeration e = v.elements();
while (e.hasMoreElements()) {
Object elem = e.nextElement();
if (f.accept(elem))
r.add( elem);
}
return r;
}
Funcia select() poate primi ca argument i un obiect enumerator dar nu poate crea o enumerare cu rezultatele
filtrrii.
- 65 -
INFORMATIC*I*
INFORMATIC*I*
INFORMATIC*I*
Tratarea excepiilor se reduce de multe ori la scrierea unui mesaj pe ecran sau ntr-un fiier, fie cu metoda
printStackTrace() din clasa Exception, fie cu o metod de scriere dintr-o clasa de tip Writer sau Printer.
Prima soluie are avantajul c prezint toat secvena de apeluri de funcii care a condus la producerea
excepiei i este folosi n depanarea programelor.
Exist i situaii cnd excepia trebuie tratat i altfel dect prin afiarea unui mesaj; exemplul urmtor arat
cum putem verifica dac un ir de caractere reprezint un numr corect (n orice format permis pentru
numere nentregi), fr a examina fiecare caracter n parte:
public static boolean isNumber (String s){
try { Double.parseDouble(s);
} catch(NumberFormatException ex) {return false;}
return true;
}
Este posibil i aruncarea unor excepii puin probabile (excepia de citire, de ex.) combinat cu tratarea altor
excepii (de exemplu excepia de sfrit de fiier).
Producerea unor excepii poate fi prevenit prin verificri efectuate de programator. Exemplu:
public static void main (String arg[ ]) {
if (arg.length == 0 ) { System.out.println ("No Argument"); System.exit(-1); }
System.out.println ( arg[0]);
// afiseaza primul argument
}
Uneori este preferabil verificarea prin program (ca n cazul unor conversii de tip nepermise), dar alteori este
preferabil tratarea excepiei (ca n cazul detectrii existenei unui fiier nainte de a fi deschis, sau a utilizrii
unor variabile referin ce pot fi nule).
In cazul programelor cu fiiere pot s apar mai multe tipuri de excepii (subtipuri de IOException) i ne
intereseaz ce tip particular de excepie s-a produs (deci cauza exact a excepiei). Avem dou soluii
posibile de interceptare a acestor excepii: cu mai multe blocuri try-catch pe fiecare instruciune sau cu un
singur bloc try care are mai multe clauze catch. Exemplu cu mai multe blocuri try:
public static void main (String[] arg) {
RandomAccessFile f=null;
try { f= new RandomAccessFile("numere.bin","r"); } // citire fisier
catch (IOException e) { System.out.println ("Eroare la deschidere" + .getMessage()); }
try {
while (true) System.out.println ( f.readInt());
}
catch (IOException e) { e.printStackTrace(); }
}
- 68 -
INFORMATIC*I*
In acest caz (la citirea din fiiere binare de numere) excepia apare fie la sfrit de fiier (dar fr erori), fie n
cazul unei erori de citire din fiier; de aceea este preferabil un bloc try cu dou clauze catch:
public static void main (String[] arg) {
RandomAccessFile f=null;
try { f= new RandomAccessFile("numere.bin","r"); }
catch (IOException e) { System.out.println ("Eroare la deschidere" + e.getMessage()); }
try {
while (true)
System.out.println ( f.readInt());
}
catch (EOFException e) { System.out.println ("Sfrsit de fisier"); }
catch (IOException e) { System.out.println ("Eroare la citire din fisier"); }
}
Class cF = f.getClass();
Clasa Class conine metode care au ca rezultat numele clasei, tipul clasei (clas sau interfa sau vector),
tipul superclasei, clasa extern, interfee implementate, tipul obiectelor declarate n clas, numele cmpurilor
(variabilelor clasei), numele metodelor clasei, formele funciilor constructor s.a.
Metoda getName() are ca rezultat un ir ce reprezint numele clasei al crui tip este coninut ntr-un obiect
Class. Exemplul urmtor arat cum se poate afia tipul real al unui obiect primit ca argument de tipul generic
Object:
- 69 -
INFORMATIC*I*
// se apeleaz Integer.toString()
// se apeleaz Integer.equals()
De aceea determinarea tipului unui obiect la executie, folosind obiectul Class asociat, este rareori necesar n
programele obinuite.
- 70 -
INFORMATIC*I*
Clasa inclus poate fi o clas instaniabil, o clas static, o clas abstract sau o interfa.
Clasele incluse cu nume primesc de la compilator un nume compus din numele clasei exterioare, caracterul
$ i numele clasei interioare (Outer$Inner). Clasele care nu sunt incluse n alte clase se numesc clase de
nivel superior (top-level classes). Clasele incluse anonime, definite simultan cu instanierea, primesc un
nume format din numele clasei exterioare, caracterul $ i un numr.
Un exemplu din clasele JDK: Clasa interioar static ReverseComparator din clasa Collections, este
folosit de metoda static reverseOrder() prin intermediul unei variabilei statice:
public class Collections {
public static Comparator reverseOrder() {
// din clasa Collections
return REVERSE_ORDER;
}
private static final Comparator REVERSE_ORDER = new ReverseComparator();
private static class ReverseComparator implements Comparator,Serializable {
public int compare(Object o1, Object o2) {
Comparable c1 = (Comparable)o1;
return -c1.compareTo(o2);
}
}
. . . // alte metode si clase incluse din clasa Collections
}
Un exemplu de interes pentru definirea de noi clase dicionar este interfaa Entry, inclus n interfata Map,
cu metode asociate unei perechi cheie-valoare, ambele de tip Object:
public interface Map {
Object get (Object key);
Object put (Object key, Object value);
...
// alte metode abstracte din interfata Map
public interface Entry {
// acces la cheia si valoarea dintr-o pereche
Object getKey();
// cheia
Object getValue();
// valoarea
Object setValue(Object value);
// modifica valoarea
boolean equals(Object o);
// daca doua perechi sunt egale
}
}
- 71 -
INFORMATIC*I*
- 72 -
INFORMATIC*I*
O situaie tipic pentru folosirea unei clase anonime definit simultan cu crearea unui obiect de tipul
respectiv este cea n care transmitem unei funcii un obiect de un subtip al interfetei Comparator (adresa
unei funcii 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
O clas inclus nestatic este numit i clas interioar (inner class), pentru c fiecare obiect din clasa
exterioar va conine un obiect din clasa interioar. Cuvntul nested se refer la relaia sintactic dintre clase:
clasa inclus este definit n cadrul definiiei clasei exterioare; cuvntul inner se refer la relaia dintre
obiectele claselor incluse: un obiect al clasei exterioare conine n el un obiect al clasei incluse.
Exemplu de clasa interioar anonim pentru iterator pe vector:
public class Vector {
protected int elementCount;
// nr de elemente in vector
protected Object elementData[];
// vector de obiecte
public Vec (){ elementData= new Object[10]; }
// metoda care produce obiect iterator
public Enumeration elements() {
return new Enumeration(){
// definirea clasei iterator pe vector (inclusa)
int count = 0;
// indice element curent din enumerare
public boolean hasMoreElements() {
return count < elementCount;
}
public Object nextElement() {
if (count < elementCount)
return elementData[count++];
else return null;
}
};
// aici se termina instructiunea return
}
// aici se termina metoda elements
... // alte metode din clasa Vector
}
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 funcia de ordomare:
static void sortByValue (Map m) { // ordonarea unui dictionar n ordinea valorilor
// clasa inclusa
class VComp implements Comparator {
// compara doua perechi cheie-val
public int compare (Object o1, Object o2) {
Map.Entry e1= (Map.Entry)o1;
// o pereche cheie-valoare
Map.Entry e2= (Map.Entry)o2;
// alta pereche cheie-valoare
return ((Integer) e1.getValue()).compareTo ((Integer) e2.getValue());
}
}
- 73 -
INFORMATIC*I*
Prin definirea de clase anonime codul surs devine mai compact iar definiia clasei apare chiar acolo unde
este folosit, dar pot exista dificulti la ntelegerea codului i erori de utilizare a acoladelor i parantezelor.
Intre clasa interioar i clasa exterioar exist un "cuplaj" foarte strns; acest cuplaj poate fi un dezavantaj la
restructurarea (refactorizarea) unei aplicaii, dar poate fi exploatat n definirea unor clase de bibliotec (care
nu se mai modific).
Motivele definirii de clase interioare pot fi diverse:
- Pentru clase de interes local : clasa interioar este necesar numai clasei exterioare. Astfel se reduce
numrul de clase de nivel superior (top-level) i conflictele ntre nume de clase (ca urmare a unor instruciuni
import pentru pachete cu clase cu nume identice).
- Pentru a permite clasei exterioare accesul la membri private ai clasei interioare.
- Pentru a permite claselor interioare accesul la variabile ale clasei exterioare i deci o comunicare mai
simpl ntre clasele incluse (prin variabile externe lor).
- Pentru crearea rapida de obiecte functionale ( cu functii callback), unice, necesare ca parametri efectivi
altor metode (prin definirea de subclase simultan cu instanierea lor).
- Pentru ca o clas s poat moteni funcii de la cteva clase (motenire multipl).
INFORMATIC*I*
Paralelismul proceselor nu implic neaprat mai multe procesoare care lucreaz simultan, ci este o
alternativ la execuia strict secvenial a celor dou procese : se execut complet P1 i apoi se execut
complet P2 (fr ntreruperi). Este posibil ca n dou procese (fire) paralele s se execute aceeai secven de
instruciuni sau secvente de instruciuni complet diferite.
Java este primul limbaj care a inclus faciliti de programare cu subprocese concurente, n cadrul unei
aplicaii, paralelism suprapus peste concurena proceselor la nivelul sistemului de operare. In Java
paralelismul (aparent) are loc n cadrul unui singur program (sau aplet), iar numele folosit pentru o astfel de
activitate este acela de thread (fir de execuie) sau subproces (ligthweight process), deoarece folosete
acelai spatiu de memorie (i de nume) cu celelalte fire de execuie din acelai program. Planificatorul
(dispecerul) de procese este parte din maina virtual Java (JVM).
Cedarea controlului procesorului de ctre procesul activ (n executie) ctre dispecer se poate face voluntar (la
cererea sa, prin apelul unei metode) sau ca urmare a lansrii unei operaii de intrare-ieire.
Activitile paralele pot evolua complet independent unele de altele (asincron) sau pot utiliza anumite resurse
comune (zone de memorie, fiiere etc) i atunci este necesar sincronizarea lor n ncercarea de acces la o
resurs comun.
Dup crearea sa i nainte de terminare, un proces poate trece ciclic prin cteva stri, iar schimbarea strii
sale este cauzat fie de evenimente interne ( o aciune a procesului activ), fie de evenimente externe
procesului, cum ar fi o decizie a planificatorului de procese.
Principalele stri n care se poate afla un proces sunt:
- In execuie (running), cnd procesul este activ (deine controlul procesorului).
- Suspendat, sau blocat, sau n ateptare (waiting), cnd procesul asteapt producerea unui eveniment.
- Gata de execuie dar inactiv (ready), pentru c exist un alt proces mai important i gata de execuie.
Un singur proces poate fi n execuie, celelalte se afl fie n lista proceselor gata, fie n lista proceselor
suspendate sau blocate. Dintre procesele gata planificatorul alege pe cel cu prioritate maxim i care este
primul n coad (dac sunt mai multe procese gata cu aceeai prioritate).
Procesele paralele (concurente) i pot disputa anumite resurse comune (date din memorie, fiiere de date,
s.a.) sau i pot transmite date ntre ele (procese numite productor i consumator).
Interaciunile dintre procese (sau subprocese) paralele nu se fac prin apeluri directe ntre procese ci prin
intermediul dispecerului. Coordonarea i sincronizarea proceselor paralele necesit existena unor operaii
specifice, prin care procesele se adreseaz planificatorului.
INFORMATIC*I*
Clasa Thread i interfaa Runnable se afl n pachetul java.lang i, alturi de cuvntul cheie synchronized
au existat nc din prima versiune Java. Incepnd cu versiunea 5 au fost introduse noi clase i metode pentru
execuia i sincronizarea firelor concurente cu resurse comune n pachetul java.util.concurrent.
Clasa
Thread contine cteva metode, unele apelate de dispecer iar altele care apeleaz dispecerul. Efectul metodei
run (apelat de dispecer) depinde de rolul pe care l are firul de execuie n cadrul unei aplicaii. In clasa
Thread metoda run() nu are nici un efect i de aceea trebuie definit o subclas, cu metoda run() redefinit:
class T extends Thread {
public void run() {
...
}
}
Crearea unui nou fir de execuie nseamn crearea unui nou obiect i se face ntr-un fir activ (care poate fi cel
implicit, pentru funcia main). Exemplu:
Thread fir1 = new T(); fir1.start();
Firul printe apeleaz de obicei o singur metod a firului fiu (metoda start()), pentru lansarea sa n
competiie cu celelalte fire; de aceea nici nu se mai creeaz uneori variabile de tipul subclasei. Exemplu:
new T( ).start();
Apelul metodei start() dintr-un fir nu are ca efect imediat apelul metodei run() din acel fir, ci introducerea
firului respectiv n coada proceselor gata de execuie, ntr-o poziie care depinde de prioritatea firului. Dac
nu exist alte fire gata mai importante i dac task-ul Java este activ, atunci firul este activat imediat dup
apelul metodei start().
Firele din clase ce extind clasa Thread pot avea un constructor cu parametru de tip String, prin care se poate
da un nume firului creat. Metodele clasei Thread sunt de dou categorii: metode ale obiectelor i metode
statice. Metode care pot fi aplicate oricrui obiect fir de execuie:
start() : Cere lansarea unui fir dintr-un alt fir (printe), cnd este posibil.
isAlive() : Verifica dac firul pentru care se apeleaz s-a terminat definitiv sau nu.
interrupt(): Cere ntreruperea firului pentru care se apeleaz
getName() / setName() : Obine/modific numele firului pentru care se apeleaz
getPriority() / setPriority() : Obine/modific prioritatea firului pentru care se apeleaz
join(): Ateapt terminarea firului pentru care se apeleaz
Metode statice care pot fi folosite doar de firul n execuie (firul curent):
Thread currentThread(): referin ctre firul curent, n execuie
void sleep(long millis): autosuspendare fir curent pentru un timp specificat n milisecunde
boolean interrupted(): testeaz i anuleaz starea de ntrerupere a firului curent
Metodele urmtoare sunt motenite de la clasa Object dar se folosesc numai n obiecte de un tip derivat din
Thread sau Runnable :
wait() : Este apelat de firul activ pentru a se autosuspenda n asteaptarea unui semnal de la un alt fir paralel
(care deine o resurs comun)
- 76 -
INFORMATIC*I*
notify(), notifyAll() : Firul activ notific (anun) firele n ateptare c s-a produs evenimentul ateptat prin
wait().
Metodele wait() si notify() pot fi folosite numai n secvene sincronizate, condiie care nu poate fi verificat
la compilare dar care produce la execuie excepia IllegalMonitorStateException.
Metoda run() din Thread se redefinete n fiecare fir definit de utilizatori i determin efectul executrii
acestui fir. De obicei metoda run() conine un ciclu n care se execut anumite operaii (afiare, desenare,
operaii cu fiiere etc.) i se apeleaz una din metodele prin care se cedeaz controlul celorlalte fire
concurente (yield(), sleep(), wait() etc.). Este posibil ca mai multe fire concurente s execute aceleai
operaii. Exemplu:
class T extends Thread {
// clasa pentru obiecte fire de executie
public T (String nume) { super(nume); }
// apel constructor clasa Thread
public void run () {
for (int i=1;i<10;i++) {
System.out.println (i + " " + getName());
// scrie numele procesului
try , sleep (time(i)); // doarme un timp
catch (InterruptedException e) { }
}
System.out.println (getName()+ " terminat");
// la terminarea procesului
}
private int time (int i) {return (int) (100*Math.random()); }
// creare si lansare procese paralele
public static void main (String [] arg) {
T p1 = new T("Unu");
T p2 = new T("Doi");
p1.start(); p2.start();
}
}
Succesiunea de afiare depinde de secvena de numere aleatoare generate, dar i de alte evenimente din
sistem (pentru un sistem de operare multi-tasking care aloc intervale de timp fiecrui task, cum este
Microsoft Windows ).
Metoda run(), aa cum este definit n interfata Runnable (pe care o implementeaz i clasa Thread), nu
poate arunca mai departe excepia InterruptedException i de aceea excepia trebuie prins i tratat n
metoda run(). Excepia InterruptedException apare atunci cnd se ncearc ntreruperea unui fir aflat ntr-o
stare de asteptare ca urmare a unui apel wait(), sleep(), join(); n aceste cazuri nu se produce ntreruperea
solicitat. Fiecare fir are asociat o variabil (boolean) cu starea sa de ntrerupere.
Cedarea controlului de ctre un fir se face i implicit, la iniierea unei operatii de I/E; terminarea operaiei
respective este un eveniment tratat de planificator. Exemplul urmtor pune n eviden aceast situatie:
// clasa pentru fire de executie reactivate la apasarrea unei taste
class Fir extends Thread {
public void run () {
try { while ( System.in.read() > 0 ); }
// asteapta tastarea unei clape
catch (IOException e) {}
}
- 77 -
INFORMATIC*I*
- 78 -
INFORMATIC*I*
- 79 -
INFORMATIC*I*
class ExFir {
public static void main (String args[]) {
Contor c= new Contor();
// contor folosit de cele doua fire
Fir f1 = new Fir ( c ); Fir f2 = new Fir ( c );
f1.start(); f2.start();
while ( f1.isAlive() && f2.isAlive() )
// asteapta terminare f1 si f2
try {Thread.sleep(100);} catch (Exception e) {}
System.out.println (c.get());
// valoare finala contor
}
}
Fiecare din cele dou fire face cte 5 incrementri ale contorului comun, dar valoarea final a contorului nu
este 10, ci este o valoare mai mic ( 5 pentru programul anterior). Rezultatul final depinde n general de
timpii de asteptare ai fiecrui fir (ntre operaii), de ordinea lansrii i de alte procese din sistem.
In acest exemplu firul f1 citeste n aux o valoare m a contorului, este ntrerupt de firul f2 care citete i el
n variabila sa proprie aux aceeasi valoare m; fiecare proces incrementeaz i scrie napoi n m aceeai
valoare a variabilei sale aux. Din aceast cauz valoarea contorului creste cu 1 i nu cu 2 cum ar fi trebuit
dac cele dou fire erau sincronizate fa de resursa comun.
Un exemplu asemntor foloseste un contor memorat ntr-un fiier disc i incrementat din mai multe fire
concurente; n acest caz pierderea controlului de ctre fiecare fir concurent se face automat, ca urmare a
initierii unor operatii de citire/scriere pe disc i nu prin cedarea voluntar a controlului.
In general, cnd un proces ncepe s modifice o resurs comun trebuie interzis altor procese s modifice
aceeasi resurs nainte ca procesul care a obtinut primul resursa s fi terminat operatiile de modificare a
resursei. Altfel spus, trebuie serializat accesul la resursa comun pentru procese (fire) concurente sau trebuie
asigurat excluderea mutual a firelor n raport cu resursa comun.
Seciunile de cod din fiecare proces care acceseaz resursa comun se numesc sectiuni critice sau regiuni
critice. Este important ca instruciunile dintr-o seciune critic s se execute nentrerupt pentru un anumit
proces i pentru un anumit obiect, fr ca un alt proces s poat executa i el aceleasi operatii pentru un
acelasi obiect. In Java o seciune critic este fie o metod, fie un bloc de instruciuni precedate de cuvntul
cheie synchronized.
Prima soluie Java pentru sincronizarea firelor de execuie a fost utilizarea cuvntului cheie synchronized n
declararea unor metode de acces la resurse comune (nainte de tipul metodei) sau ca atribut al unui bloc de
instruciuni. Sincronizarea se face la nivel de obiect i nu la nivel de funcie.
In exemplul anterior cu incrementarea unui contor din memorie, acest cuvnt se adaug metodei incr() :
public synchronized void incr () {
int aux;
try {
aux=m;
// pasul 1
Thread.sleep((int) (Math.random()*20));
aux=aux+1;
// pasul 2
Thread.sleep((int) (Math.random()*20));
m=aux;
// pasul 3
} catch (InterruptedException e) {}
}
- 80 -
INFORMATIC*I*
Dup ce firul f1 a intrat n execuia metodei sincronizate incr pentru un obiect c, nu se permite altui fir s
mai execute o metod sincronizat pentru acelasi obiect, iar firul f2 care ncearc acest lucru este pus n
ateptare pn la terminarea metodei sincronizate.
O metod sincronizat poate fi ntrerupt, dar nu poate fi reapelat pentru acelai obiect dintr-un alt fir
concurent. De asemenea, nu este permis apelarea din fire diferite a unor metode sincronizate diferite pentru
acelai obiect. De exemplu, nu este posibil ca dou fire s adauge n paralel elemente la un acelai vector
(metoda addElement()). Toate metodele de acces la un vector (din clasa Vector) sunt sincronizate pentru a fi
sigure la apelare din fire concurente.
Cuvntul synchronized ar trebui adugat oricrei metode care modific un obiect ce poate fi folosit n
comun de mai multe fire de execuie paralele. Multe din metodele claselor JDK sunt sincronizate.
Dup apelarea unei metode sincronizate pentru un obiect acest obiect este blocat cu un zvor sau
lact (lock) i nu mai poate fi apelat o alt metod sincronizat pentru acelasi obiect (dintr-un alt proces).
Deblocarea obiectului are loc la terminarea execuiei metodei sincronizate. Deoarece sincronizarea se face la
nivel de obiecte, datele comune proceselor trebuie ncapsulate n obiecte utilizate n comun. La crearea unui
proces care folosete o astfel de resurs comun (variabil, colecie, fiier) se transmite adresa obiectului ce
constituie resursa comun. Prin monitoare se asigur accesul strict secvenial al firelor concurente la operaii
critice asociate unui obiect. Un monitor este asociat unui obiect (sau unei clase) i nu unei metode.
Dou fire pot executa n paralel secvene din dou metode sincronizate, dar pentru obiecte diferite. De
asemenea, un fir poate executa o metod sincronizat i un alt fir o metod nesincronizat pentru un acelai
obiect. Sincronizarea unei metode se poate exprima n dou moduri:
synchronized void f() , -
sau
O clas este sigur ntr-un context cu fire concurente (thread-safe) dac rezultatele metodelor sale sunt
aceleai indiferent din cte fire paralele sunt apelate aceste metode pentru acelea i obiecte. Practic, aceast
calitate a unei clase se obine prin adugarea atributului synchronized metodelor care modific date din
clas. Metodele sincronizate au performane mai slabe dect metodele nesincronizate i de aceea nu toate
clasele JDK sunt thread-safe.
Primele clase colecie (Vector, Hashtable) au avut metode sincronizate, dar ncepnd din Java 2 clasele
colecie nu sunt implicit thread-safe din motive de performan. Se pot obine ns clase colecie echivalente
sincronizate. Exemplu de vector cu acces sincronizat obinut din ArrayList:
List safelist = Collections.synchronizedList (new ArrayList());
- 81 -
INFORMATIC*I*
O colecie, n sensul Java, este un tip de date abstract care reunete un grup de obiecte de tip Object, numite
i elemente ale coleciei. Un dicionar (o asociere ) este o colecie de perechi chei-valoare (ambele de tip
Object); fiecare pereche trebuie s aib o cheie unic, deci nu pot fi dou perechi identice.
Interfaa Collection conine metode aplicabile pentru orice colecie de obiecte. Nu toate aceste operaii
trebuie implementate obligatoriu de clasele care implementeaz interfaa Collection; o clas care nu
definete o metod opional poate semnala o excepie la ncercarea de apelare a unei metode
neimplementate.
Urmeaz descrierea complet a interfeei Collection :
- 82 -
INFORMATIC*I*
Din interfaa Collection sunt derivate direct dou interfee pentru tipurile abstracte:
- Set pentru mulimi de elemente distincte.
- List pentru secvene de elemente, n care fiecare element are un succesor i un predecesor i este
localizabil prin pozitia sa n list (un indice ntreg).
Clasele abstracte existente fac uz de iteratori i implementeaz aproape toate metodele unei clase colecie
folosind numai metoda iterator() (cu excepia metodelor de adugare obiect la colecie i de calcul numr de
elemente din colecie, care depind de structura fizic a coleciei).
Pentru fiecare din cele 3 structuri de date abstracte (list, mulime,dicionar) sunt prevzute cte dou clase
concrete care extind clase abstracte i implementeaz interfeele respective. Structurile de date concrete
folosite pentru implementarea tipurilor de date abstracte sunt : vector extensibil, list dublu nlnuit, tabel
de dispersie i arbore binar.
- 83 -
INFORMATIC*I*
Toate clasele colecie instaniabile redefinesc metoda toString(), care produce un ir cu toate elementele
coleciei, separate prin virgule i ncadrate de paranteze drepte. Afiarea continutului unei colecii se poate
face printr-o singur instruciune.
De asemenea sunt prevzute metode de trecere de la vectori intrinseci de obiecte (Object [] ) la colecii de
obiecte i invers: funcia Arrays.asList(), cu un argument vector intrinsec de obiecte i rezultat de tip List i
funcia toArray() din clasa AbstractCollection, de tip Object[]. Exemplu:
String sv*+ = ,unu,doi,trei-;
List list = Arrays.asList(sv);
System.out.println (list);
String aux[] = (String[ ]) list.toArray();
O a treia ierarhie are la baz interfaa Iterator, pentru metodele specifice oricrui iterator asociat unei
colecii sau unui dictionar. Toate coleciile au iteratori dar nu i clasele dicionar (se poate ns itera pe
mulimea cheilor sau pe colecia de valori).
Clasa Collections conine metode statice pentru mai multi algoritmi "generici" aplicabili oricrei colecii
(min(), max()) sau numai listelor (sort(), binarySearch(), reverse(), shuffle()). Ei foloseau iniial tipul
generic Object i metodele polimorfice equals(), compareTo() .a., dar acum folosesc tipuri parametrizate
(din versiunea 5). Exemple n forma mai veche dar mai simpl:
public static Object min (Collection col );
// obiect minim din colectia col
public static Object min (Collection col , Comparator cmp);
public static void sort (List lst);
// ordonare lista lst
public static void sort (List lst, Comparator cmp);
INFORMATIC*I*
// reuniune s1+s2
// intersectie s1*s2 in aux
// reuniune minus intersectie
Exemplul urmtor arat cum putem folosi dou mulimi pentru afisarea cuvintelor care apar de mai multe ori
i celor care apar o singur dat ntr-un text. S-a folosit o mulime ordonat, pentru a permite afiarea
cuvintelor n ordine lexicografic.
public static void main (String arg[ ]) throws IOException {
Set toate = new HashSet ();
// toate cuvintele distincte din text
Set dupl =new TreeSet ();
// cuvintele cu aparitii multiple
Scanner sc = new Scanner (new File(arg[0]));
String word="";
// un cuvant din fisier
while ( sc.hasNext() ) {
// repeta cat mai sunt cuvinte in fisier
word= sc.next();
// scoate urmatorul cuvant
if ( ! toate.add (word) )
// daca a mai fost in toate
dupl.add (word);
// este o aparitie multipla
}
System.out.println ("multiple: "+ dupl); // afisare cuvinte repetate
Set unice = new TreeSet (toate);
unice.removeAll (dupl);
// elimina cuvinte multiple
System.out.println ("unice: " + unice);
// afiseaza cuvinte cu o singura aparitie
}
In general se recomand programarea la nivel de interfa i nu la nivel de clas concret. Asadar, se vor
folosi pe ct posibil variabile de tip Collection sau Set i nu variabile de tip HashSet sau TreeSet (numai la
construirea obiectului colecie se specific implementarea sa).
Nici una din mulimile HashSet sau TreeSet nu pstreaz ordinea de adugare a elementelor la mulime,
dar n clasa LinkedHashSet metoda toString() produce un ir cu elementele mulimii n ordinea adugrii
lor la mulime, dar are aceleai performane la cutare ca i clasa HashSet.
Interfaa SortedSet, implementat de clasa TreeSet, extinde interfaa Set cu cteva metode aplicabile numai
pentru colecii ordonate:
Object first(), Object last()
// primul si ultimul element din multime
SortedSet subSet(Object from, Object to) // submultimea definita prin 2 valori
SortedSet headSet(Object to)
// subSet (first(),to)
SortedSet tailSet (Object from)
// subSet (from, last())
INFORMATIC*I*
- Metode pentru determinarea poziiei unde se afl un element dat, deci cutarea primei i ultimei apariii 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 interfaa List:
ArrayList list vector, preferabil pentru acces aleator frecvent la elementele listei.
LinkedList list dublu nlnuit, preferat cnd sunt multe inserri sau tergeri de elemente n list.
In plus, clasei Vector i-au fost adugate noi metode pentru a o face compatibila cu interfaa List. Noile
metode au nume mai scurte i o alt ordine a argumentelor n metodele add() i set():
Forma veche (1.1)
Object elementAt (int)
Object setElementAt (Objext, int)
void insertElementAt (Object, int)
De observat c metodele set() si remove() au ca rezultat vechiul obiect din list, care a fost modificat sau
eliminat. Metoda set() se poate folosi numai pentru modificarea unor elemente existente n list, nu i
pentru adugare de noi elemente.
Algoritmii de ordonare i de cutare binar sunt exemple de algoritmi care necesit accesul direct, prin
indici, la elementele listei; de aceea metodele sort() si binarySearch() din clasa Collections au argument de
tip List i nu Collection.
Accesul poziional este un acces direct, rapid la vectori dar mai puin eficient n cazul listelor nlnuite. De
aceea s-a definit o clas abstract AbstractSequentialList, care este extins de clasa LinkedList dar nu i de
clasa ArrayList. Metoda static Collections.binarySearch, cu parametru de tipul general List, recunoaste
tipul de list i face o cutare binar n vectori, dar o cutare secvenial n liste nlnuite.
Clasa LinkedList contine, n plus fa de clasa abstract AbstractList, urmtoarele metode, utile pentru
cazuri particulare de liste (stive, cozi etc.): getFirst(), getLast(), removeFirst(), removeLast(),
addFirst(Object), addLast(Object).
Din versiunea 5 au mai fost adugate interfeele Queue si Deque cu cteva metode noi, interfee
implementate de mai multe clase, printre care AbstractQueue, PriorityQueue, LinkedList, ArrayDeque.
Interfaa Queue extinde interfaa Collection cu o metod de adugare offer() care nu produce excepie dac
- 86 -
INFORMATIC*I*
nu mai este loc n colecie i cu metode de acces la primul element din colecie (din coad) cu i fr excepie
n caz c nu exist (element() si peek() fr eliminare din coad, poll()i remove() cu eliminare din coad).
Interfaa Deque extinde interfaa Queue cu metode de acces i la primul si la ultimul element din list:
addFirst(), removeFirst(), getFirst(), addLast(), removeLast(), getLast()i variantele care nu produc
excepii: offer(), poll(), peek(), offerFirst(), offerLast(),
Cele mai multe clase de tip coad (ca BlockingQueue si SynchronousQueue) au fost introduse, alturi de
alte clase, n pachetul java.util.concurrent pentru programare cu fire de execuie concurente.
Pentru aplicaiile cu structuri de date sunt interesante clasele ArrayDeque si PriorityQueue; clasa
LinkedList implementeaz acum i interfaa Deque i de aceea nu exist o clas LinkedDeque.
Coada cu prioriti este implementat ca un vector heap extensibil (fr limite de capacitate). Indiferent de
ordinea de adugare la coad, elementul din fa (obinut prin una din metodele peek(), poll() sau remove())
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.
INFORMATIC*I*
noua valoare v (valoarea nlocuit este transmis ca rezultat al metodei). Testul de apartenen a unei chei
date la un dicionar se poate face fie direct prin metoda containsKey(), fie indirect prin verificarea
rezultatului operaiei 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 afieaz numrul de apariii al fiecrui cuvnt distinct dintr-un text, folosind un
dicionar-arbore pentru scrierea 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 ();
// dictionar de cuvinte
String text = trei unu doi trei doi trei ; String word;
StringTokenizer st = new StringTokenizer (new String (text));
while ( st.hasMoreTokens()) {
word = st.nextToken();
// cuvantul urmator
Integer nrap = (Integer) dic.get(word);
// numar de aparitii
if (nrap == null)
// daca nu exista anterior
dic.put (word,ONE);
// prima aparitie
else
dic.put (word, new Integer (nrap.intValue()+1)); // alta aparitie
}
System.out.println (dic);
// afisare dictionar
}
}
Cheile dintr-un dictionar pot fi extrase ntr-o mulime cu metoda keySet(), iar valorile din dicionar pot fi
extrase ntr-o colecie (o list) cu metoda values(). Metoda entrySet() produce o mulime echivalent de
perechi "cheie-valoare", unde clasa pereche are tipul Entry. Entry este o interfa inclus n interfaa Map i
care are trei metode: getKey(), getValue()i setValue().
Clasa AbstractMap definete majoritatea metodelor din interfaa Map n funcie de metoda abstract
entrySet(), iar metoda put() nu este abstract dar nici nu este definit (arunc o excepie).
De observat c metodele entrySet(), keySet() i values() (definite n AbstractMap) creeaz doar imagini noi
(views) asupra unui dicionar i nu alte colecii de obiecte; orice modificare n dicionar se va reflecta
automat n aceste imagini, fr ca s apelm din nou metodele respective. O imagine (view)este creat
printr-o clas care definete un iterator pe datele clasei dicionar i nu are propriile sale date. Metodele clasei
imagine sunt definite apoi pe baza iteratorului, care d acces la datele din dicionar.
Diferena dintre clasele HashMap si LinkedHashMap apare numai n irul produs de metoda toString() a
clasei: la LinkedHashMap ordinea perechilor n acest ir este aceeai cu ordinea de introducere a lor n
dicionar, n timp ce la HashMap ordinea este aparent ntmpltoare (ea depinde de capacitatea tabelei de
dispersie, de funcia hashCode()i de ordinea de introducere a cheilor). Pentru pstrarea ordinii de adugare
se folosete o list nlnuit, iar tabelul de dispersie asigur un timp bun de regsire dup cheie.
INFORMATIC*I*
Iteratorul unei colecii ordonate parcurge elementele n ordinea dictat de obiectul comparator folosit
meninerea coleciei n ordine.
O problem comun coleciilor ordonate este criteriul dup care se face ordonarea, deci funcia de
comparaie, care depinde de tipul obiectelor comparate. Sunt prevzute dou soluii pentru aceast problem,
care folosesc dou interfee diferite: Comparable i Comparator.
Anumite metode statice (sort, min, max s.a.) i unele metode din clase pentru mulimi ordonate apeleaz n
mod implicit metoda compareTo(), parte a interfeei Comparable. Clasele JDK cu date (String, Integer,
Date s.a.) implementeaz interfaa Comparable i deci contin o metoda compareTo() pentru o ordonare
"natural" ( excepie face clasa Boolean, ale crei obiecte nu sunt comparabile).
Ordinea natural este ordinea valorilor algebrice (cu semn) pentru toate clasele numerice, este ordinea
numeric fr semn pentru caractere i este ordinea lexicografic pentru obiecte de tip String. Pentru alte
clase, definite de utilizatori, trebuie implementat interfaa Comparable prin definirea metodei
compareTo(), dac obiectele clasei pot fi comparate (i sortate).
Pentru ordonarea dup un alt criteriu dect cel natural i pentru ordonarea dup mai multe criterii se va folosi
metoda compare() dintr-o clas compatibil cu interfaa Comparator.
Rezultatul metodei compare(Object ob1, Object ob2) este acelai cu al metodei compareTo(), deci un numr
negativ dac ob1<ob2, zero dac ob1==ob2 i un numr pozitiv dac ob1>ob2.
Un argument de tip Comparator apare n constructorii unor clase i n cteva metode din clasa Collections
(sort, min, max) ce necesit compararea de obiecte.
- 89 -
INFORMATIC*I*
Pentru a utiliza o functie compare() trebuie definit o clas care conine numai metoda compare(), iar un
obiect al acestei clase se transmite ca argument funciei. Exemplu de ordonare a dicionarului de cuvintefrecven creat anterior, n ordinea invers a numrului de apariii:
Set entset = dic.entrySet();
ArrayList entries = new ArrayList(entset);
Collections.sort (entries, new Comp());
...
// clasa comparator obiecte Integer in ordine inversa celei naturale
class Comp implements Comparator {
public int compare (Object o1, Object o2) {
Map.Entry e1= (Map.Entry)o1, e2= (Map.Entry)o2; // e1,e2=prechi cheie-valoare
return ((Integer)e2.getValue()).compareTo ((Integer) e1.getValue());
}
}
IE.06.6 Iteratori
Una din operaiile frecvente asupra coleciilor de date este enumerarea tuturor elementelor coleciei (sau a
unei subcolecii) n vederea aplicrii unei prelucrri fiecrui element obtinut prin enumerare. Realizarea
concret a enumerrii depinde de tipul coleciei i foloseste un cursor care nainteaz de la un element la
altul, ntr-o anumit ordine (pentru colecii neliniare). Cursorul este un indice ntreg n cazul unui vector sau
un pointer (o referin) pentru o list nlnuit sau pentru un arbore binar.
Generalizarea modului de enumerare a elementelor unei colecii pentru orice fel de colecie a condus la
apariia claselor cu rol de iterator fa de o alt clas colecie. Orice clas colecie Java 2 poate avea o clas
iterator asociat. Pentru un acelasi obiect colecie (de ex. un vector) pot exista mai muli iteratori, care
progreseaz n mod diferit n cadrul coleciei, pentru c fiecare obiect iterator are o variabil cursor proprie.
Toate clasele iterator trebuie s includ urmtoarele operaii: poziionarea pe primul element din colecie,
poziionarea pe urmtorul element din colecie, obinerea elementului curent din colecie, detectarea
sfrsitului coleciei (test de terminare a enumerrii).
Interfaa Iterator este varianta mai nou a interfetei Enumerator si conine urmtoarele metode :
public interface Iterator {
boolean hasNext();
Object next();
void remove();
}
INFORMATIC*I*
De observat c modificarea coninutului unei colecii se poate face fie prin metode ale clasei colecie, fie
prin metoda remove() a clasei iterator, dar nu ar trebui folosite simultan ambele moduri de modificare. In
exemplul urmtor apare o excepie la execuie:
ArrayList a = new ArrayList();
Iterator it = a.iterator();
for (int i=0;i<7;i++) {
a.add(i); it.remove();
}
Pentru fiecare clas concret de tip colecie exist o clas iterator. Un obiect iterator este singura posibilitate
de enumerare a elementelor unei mulimi i o alternativ pentru adresarea prin indici a elementelor unei liste.
Clasele iterator nu sunt direct instaniabile (nu au constructor public), iar obiectele iterator se obin prin
apelarea unei metode a clasei colecie (metoda iterator()). In felul acesta, programatorul este obligat s
creeze nti obiectul colecie i numai dup aceea obiectul iterator. Mai muli algoritmi generici realizai ca
metode statice (n clasa Collections) sau ca metode ne-statice din clasele abstracte folosesc un obiect iterator
pentru parcurgerea coleciei. 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<a.length; j++) { // modificare elemente din lista
i.next(); i.set(a[j]);
}
}
Fragmentul urmtor din clasa AbstractCollection arat cum se pot implementa metodele unei clase colecie
folosind un iterator pentru clasa respectiv:
public abstract class AbstractCollection implements Collection {
public boolean contains(Object o) {
// fara cazul o==null
Iterator e = iterator();
while (e.hasNext())
if (o.equals(e.next()))
return true;
return false;
}
public Object[] toArray() {
Object[] result = new Object[size()];
Iterator e = iterator();
for (int i=0; e.hasNext(); i++)
result[i] = e.next();
return result;
}
...
}
- 91 -
INFORMATIC*I*
Interfaa ListIterator conine metode pentru traversarea unei liste n ambele sensuri i pentru modificarea
elementelor enumerate : hasNext(), next(), hasPrevious(), previous(), nextIndex(), previousIndex(),
remove(), set (Object o), add(Object o). Exemplu de parcurgere a unei liste de la coad la capt:
List a = new LinkedList();
for (ListIterator i= a.listIterator (a.size()); i.hasPrevious(); )
System.out.println (i.previous());
O clas dicionar (Map) nu are un iterator asociat, dar este posibil extragerea unei mulimi de chei sau unei
mulimi de perechi cheie-valoare dintr-un dicionar, iar aceste mulimi pot fi enumerate.
Secvena urmtoare face o enumerare a perechilor cheie-valoare dintr-un dicionar, folosind interfaa public
Entry, definit n interiorul interfeei Map:
for (Iterator it= map.entrySet().iterator(); it.hasNext();) {
Map.Entry e = (Map.Entry) it.next();
System.out.println ( e.getKey()+:+e.getValue());
}
IE.06. 7 Generice
Clasele colecie cu tipuri parametrizate au fost introduse n versiunea 5, ca soluii alternative pentru coleciile
de obiecte Object. Aceste clase au fost numite generice (generics) i nu clase ablon (templates) pentru c
dei se definesc i se folosesc aproape la fel cu clasele ablon din C++ ele difer prin implementare: n C++
din clasa ablon se genereaz diverse clase prin substituia parametrilor tip din clasa ablon, dar n Java
exist o singur clas generic (surs i compilat) n care se nlocuiesc parametrii formali cu parametrii
efectivi, ca i la apelul de funcii. Intr-o colecie generic tipul obiectelor din colecie apare ca parametru-tip
dup numele clasei. Exemple:
ArrayList<Integer> a = new ArrayList<Integer>();
ArrayList<String> b = ArrayList<String> (100);
ArrayList<TNode> c = ArrayList<TNode> (n);
ArrayList<LinkedList<Integer>> graf;
// initializata ulterior
TreeMap<String, Integer> d ;
// cheie=String, valoare=Integer
Ultimul exemplu arat c o clas poate avea mai muli parametri-tip.
Avantajele sunt acelea c se poate verifica la compilare dac se introduc n colecie numai obiecte de tipul
declarat pentru elementele coleciei, iar la extragerea din colecie nu mai este necesar o conversie de la tipul
generic la tipul specific aplicaiei. Exemplu cu un vector de obiecte Integer:
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i=1;i<10;i++)
list.add( new Integer(i) );
// nu se poate adauga alt tip decat Integer
int sum=0;
for (int i = 0; i< list.size();i++) {
Integer val= list.get(i);
// fara conversie de tip !
sum += val.intValue();
}
- 92 -
INFORMATIC*I*
Sursele unor clase colecie s-au modificat substanial prin trecerea la generice. Exemplu:
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {
//
public V get (Object key) , public V put ( K key, V value) , Set<K> keySet() , Set < Map.Entry<K,V>> entrySet() , Collection<V> values() , V remove (Object key) , int size() , boolean containsKey (Object key) , . boolean containsValue (Object value) , void putAll( Map <? extends K, ? extends V> m) ,void clear() , }
Exemplu de utilizare dicionar cu valori multiple pentru problema listei de referine ncruciate - n ce linii
dintr-un fiier text apare fiecare cuvnt distinct :
public static void main (String[ ] arg) throws IOException {
Map<String,List<Integer>> cref = new TreeMap<String,List<Integer>>( );
BufferedReader in = new BufferedReader (new FileReader (arg[0]));
String line, word; int nl=0;
while ((line=in.readLine()) != null) {
++nl;
StringTokenizer st = new StringTokenizer (line);
while ( st.hasMoreTokens() ) {
word=st.nextToken();
List<Integer> lst = cref.get (word);
if (lst==null)
cref.put (word, lst=new LinkedList<Integer>());
lst.add (new Integer (nl));
}
}
System.out.println (cref);
}
Compilatorul Java emite averismente la utilizarea formei mai vechi pentru colecii (cu elemente de tip
Object), iar documentaia claselor a fost rescris pentru forma cu parametri-tip. Clasele mai vechi care
foloseau colecii nu au mai fost modificate; un exemplu este clasa DefaultMutableTreeNode.
Pentru colecii generice s-a introdus o form mai simpl de iterare. Exemplu:
- 93 -
INFORMATIC*I*
La declararea unui parametru tip se poate folosi i caracterul wildcard ? cu sensul de orice tip :
// functie generic de afisare a oricrei colectii
static void printCol (Collection<?> c) {
for (Object e : c) System.out.println(e);
}
Exemplu de funcie incorect i utilizare care produce erori la compilare:
// functie naiv de afisare a unei colectii generice
static void printCol (Collection<Object> c) {
for (Object e : c) System.out.println(e);
}
// utilizare incorect
HashSet<String> m = new HashSet<String> (); printCol (m);
O colecie Collection<Object> nu este un supertip al unei colectii Collection<T> chiar dac tipul Object
este supertip al oricrui tip clas T din Java. In concluzie Collection<?> este supertipul oricrei colecii de
obiecte, iar caracterul '?' are sensul de "tip necunoscut".
Declaraia Collection<Object> nu este echivalent cu declaraia Collection<?>.
Tipurile necunoscute pot fi limitate inferior sau superior (Bounded wildcards) folosind sintaxa
extends T> pentru tipuri limitate superior i <? super T> pentru tipuri limitate inferior.Exemplu:
<?
INFORMATIC*I*
Nu numai clasele pot avea parametri tip dar i metodele (numite metode generice). Exemplu:
// transforma un vector intr-o colectie (gresit)
static void arrayToCol (Object[] a, Collection<?> c) {
for (Object o : a)
c.add(o);
// eroare la compilare
}
// transforma un vector intr-o colectie (corect)
static <T> void arrayToCol (T[] a, Collection<T> c) {
for (T o : a)
c.add(o);
// correct
}
// utilizare (fara argumente efective de tip la apel)
String[] sa = new String[100];
Collection<String> cs = new LinkedList<String>();
arrayToCol (sa, cs); // T presupus a fi String
- 95 -
INFORMATIC*I*
De observat c exist diferite programe care pot genera diagrame de clase din cod surs (Java) i c unele
dintre acestea se mai abat puin de la standardul UML. Diagramele prezentate aici sunt create de o versiune
mai veche (6.7.1) a mediului integrat pentru dezvoltarea de programe NetBeans.
Este posibil i operaia de generare de cod din diagrame de clase.
- 96 -
INFORMATIC*I*
Variabilele clasei, numite "atribute" n diagrama UML, au de obicei modul de acces private sau protected
pentru a respecta principiul ncapsulrii datelor n clase i sunt marcate cu minus (-). Constructorii i
metodele publice (numite operaii n UML) sunt marcate cu plus (+).
Uneori se reprezint numai metodele cele mai folosite sau caracteristice, fr metode motenite de la alte
clase (de la clasa Object, de ex.).
Exemplu de definire a unei clase:
public class MyVector {
protected Object data[];
protected int count;
public MyVector( int n) {
data = new Object[n];
}
public MyVector () { this(10); }
// metode publice
public int size ( ) { return count; }
public String toString ( )
String str="[";
for (int i=0;i<count;i++)
str = str+ data[i] +" ";
return str+"]";
}
public void add (Object obj) {
if ( count >= data.length )
resize();
data[count++]= obj;
}
}
// adaugare la vector
- 97 -
INFORMATIC*I*
- 98 -
INFORMATIC*I*
UML deosebete dou tipuri de asocieri: compoziie i agregare. Diferena este aceea c rombul este alb
(agregare) sau negru (compozitie). In Java cele dou tipuri sunt identice dar n C++ pot fi diferente.
Asocierile UML pot avea o multiplicitate, dac n clasa compus exist un vector sau alt colecie de obiecte
din clasa coninut. Multiplicitatea se poate exprima n mai multe moduri:
Exemplu de asociere a unei clase cu ea nsi: un nod de arbore binar conine dou referine ctre nodurile fii
(stnga i dreapta):
public class BinaryTreeNode {
private BinaryTreeNode leftNode;
private BinaryTreeNode rightNode;
}
In cazul unui nod de arbore multici cu numr nelimitat de fii n locul lui 2 va fi caracterul asterisc *.
Exemplu de asociere prin delegare ntre clasa Stiva i clasa MyVector:
- 99 -
INFORMATIC*I*
- 100 -
INFORMATIC*I*
- 101 -
INFORMATIC*I*
- 102 -
INFORMATIC*I*
- 103 -
INFORMATIC*I*
Diagrama la nivel
conceptual:
- 104 -
INFORMATIC*I*
Diagramele
operaiilor
addPerson()
saveAddressBook()
- 105 -
INFORMATIC*I*
- 106 -
INFORMATIC*I*
Variante de implementare:
a) Metoda are ca rezultat o referin la un obiect creat la ncrcarea clasei:
- 107 -
INFORMATIC*I*
Clasa BorderFactory (pachetul javax.swing) reunete mai mai multe metode ce produc obiecte chenar de
diferite forme pentru componentele vizuale Swing. Toate obiectele chenar aparin unor clase care respect
interfaa comun Border i care sunt fie clase predefinite, fie clase definite de utilizatori. Exemple de clase
chenar predefinite: EmptyBorder, LineBorder, BevelBorder, TitleBorder i CompundBorder.
b)- Metoda creeaz un obiect numai la primul apel, dup care nu mai instaniaz clasa:
public class BorderFactory {
private EtchedBorder sharedBorder= null;
public Border createEtchedBorder() {
if (sharedBorder==null)
sharedBorder = new EtchedBorder()
return sharedBorder;
}
...
}
Metoda care creeaz obiectul unic este o metod fabric de obiecte, dar care fabric un singur obiect.
Alte exemple pot fi ntlnite n clasa Collections (neinstaniabil) pentru a se crea obiecte din colecii mai
speciale : colecii cu un singur obiect (SingletonSet, SingletonList, SingletonMap) i colecii vide
(EmptySet, EmptyList, EmptyMap). Exemplu:
public class Collections {
public static Set singleton(Object o) {
return new SingletonSet(o);
}
private static class SingletonSet extends AbstractSet {
private Object element;
SingletonSet(Object o) {element = o;}
public int size() {return 1;}
public boolean contains(Object o) {return eq(o, element);}
... // public Iterator iterator() { ... }
}
...
}
// utilizare: crearea unui vector de multimi cu cte un element initial
sets = new ArrayList (n);
for (int i=0;i<n;i++)
sets.add (new TreeSet (Collections.singleton ( new Integer(i) ) ));
- 108 -
INFORMATIC*I*
Obiectul iterator poate fi creat direct prin instanierea unei clase, cum este StringTokenizer, iar obiectul
agregat (de tip String) este primit ca argument de constructorul obiectului iterator. Clasa StringTokenizer
implementeaz interfaa Enumeration. Exemplu :
String str = " 1, 2 . 3 ; 4. ";
StringTokenizer st = new StringTokenizer(str," .,;");
while (st.hasMoreElements())
System.out.println (st.nextElement());
Pentru coleciile din Java obiectul iterator este creat printr-o metod din clasa colecie (metoda elements()
sau iterator()) i nu prin instaniere direct, deoarece un iterator este asociat unei colecii i nu poate fi creat
dect dup crearea coleciei. Aceste metode ilustreaz ablonul metod fabric de obiecte:
Vector v = new Vector();
for (int i=1; i<9;i++) v.add(i);
Enumeration e = v.elements();
while (e.hasMoreElements())
System.out.print (e.nextElement()+",");
In Java mecanismul iterator a evoluat n timp de la interfaa Enumeration cu dou metode, la interfaa
Iterator cu trei metode i la o form simplificat de iterare prin colecii si vectori, ce corespunde unei
instruciuni "for each" din alte limbaje. Pentru liste Java exist un iterator bidirecional cu metode de
naintare (next()) i de revenire la elementul anterior (previous()). De observat c pentru mulimi iteratorul
este singura posibilitate de acces la elementele mulimii deoarece nu este posibil accesul prin indici (nu
exist noiunea de poziie a unui element n mulime).
Implementarea unui iterator folosete un cursor care depinde de tipul obiectului agregat: pentru extragere de
cuvinte (tokens) cursorul este un indice n cadrul textului, pentru vectori cursorul este un indice ntreg iar
pentru liste nlnuite cursorul este un pointer (o variabil referin n Java). Exemplu:
- 109 -
INFORMATIC*I*
class StrTok {
String str;
// sirul analizat
int crt;
// pozitia curenta in sir
// constructor
public StrTok (String s) {
str=s; crt=0;
}
public boolean hasMoreTokens () {
return crt < str.length();
}
public String nextToken () {
while ( crt < str.length() && str.charAt(crt)==' ')
++crt;
// ignora spatii
int start= crt;
// inceput de cuvnt
crt= str.indexOf (' ',start); // sfrsit de cuvnt
if ( crt < 0) crt= str.length(); // ultimul cuvnt din sir
return str.substring (start,crt);
}
}
Schema observat-observator a generat clasa Observable i interfaa Observer din pachetul java.util.
Programatorul de aplicaie va defini una sau mai multe clase cu rol de observator, compatibile cu interfaa
Observer. Subiectul supus observaiei conine metode de adugare sau de eliminare a unor observatori. In
desen aceste metode se numesc attach()i detach(), iar n Java se numesc addObserver()i deleteObserver();
metoda notify() din desen se numeste notifyObservers() in clasa Observable.
Interfaa Observer conine o singur metod update(), apelat de un obiect observat la o schimbare n starea
sa i care poate interesa obiectele observator :
public interface Observer { void update(Observable o, Object arg); }
Clasa Observable nu este abstract dar nici nu este direct utilizabil; programatorul de aplicaie va defini o
- 110 -
INFORMATIC*I*
subclas care preia toate metodele clasei Observable i adaug o serie de metode specifice aplicaiei, care
apeleaz metodele superclasei setChanged() i notifyObservers(). Metoda notifyObservers() apeleaz
metoda update() pentru toi observatorii introdui n vectorul "observers". Metoda getChanged() poate fi
folosit pentru a vedea dac s-a modificat starea subiectului observat, iar metoda setChanged() modific o
variabil intern pentru a semnala modificarea strii subiectului observat.
Urmeaz un exemplu simplu care folosete clasele Java pentru ablonul Observator:
class Observat extends Observable {
private int x;
public void setValue (int x) {this.x=x;}
public int getValue() {return x;}
public void change() { setChanged(); notifyObservers(x); }
}
class Observator implements Observer {
private String nume;
public Observator(String nume){ this.nume=nume;}
public void update( Observable obs, Object x ) {
System.out.println (nume + " " +x);
}
}
class Main {
public static void main (String args[]) {
Observat subiect = new Observat();
Observator ob1 = new Observator("unu");
Observator ob2 = new Observator("doi");
subiect.addObserver(ob1); subiect.addObserver(ob2);
for (int i=1;i<10;i++){
subiect.setValue(i); subiect.change();
}
}
}
O alt utilizare a ablonului Observator este n aplicaiile Java cu interfa grafic: practic toate obiectele
grafice Swing sunt obiecte observabile i genereaz evenimente Swing ca urmare a gesturilor operatorului
(clic pe buton de mouse sau apsare tast de consol); producerea unui eveniment este notificat unor obiecte
numite asculttor (cu rol de observator) nregistrate anterior la sursa de evenimente (subiectul supus
observaiei).
Ordinea n care sunt notificai observatorii sau asculttorii nu este aceeai cu ordinea de adugare a lor la
subiectul observat (n Java este chiar ordinea invers celei de la adugare).
INFORMATIC*I*
- 112 -
INFORMATIC*I*
Exemplul urmtor arat cum se poate face evaluarea unor expresii aritmetice care conin doar numere ntregi
ca operanzi i caiva operatori binari, expresii reprezentate prin arbori. Interfaa "Expression" din program
corespunde interfeei "Component" din figur iar metoda eval() din program corespunde metodei
operation() din figur.
interface Expression { int eval(); }
public class Expr implements Expression {
protected TNode node;
public Expr(TNode node) { this.node=node;}
public int eval () {
if (node==null) return 0;
if (node.isLeaf()) return new Leaf(node).eval();
else return new BinExpr(node).eval();
}
}
class Leaf extends Expr implements Expression {
public Leaf(TNode node){ super(node);}
public int eval() {
return (Integer)node.getUserObject();
}
}
class BinExpr extends Expr implements Expression {
public BinExpr(TNode node){ super(node);}
private Expr left,right;
public int eval(int op) {
return evalBin( op,left.eval(), right.eval() );
}
int evalBin (int op, int lval, int rval){
switch (op){
case '+': return lval+rval;
case '-': return lval-rval;
case '*': return lval*rval;
}
return 0;
}
}
- 113 -
INFORMATIC*I*
Clasele filtru de I/E din Java fac parte din categoria claselor decorator. Principalele clase filtru de I/E sunt:
FilterInputStream, FilterOutputStream i FilterReader, FilterWriter. Clasele de tip filtru sunt clase
intermediare, din care sunt derivate clase care adaug operaii 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 conine o variabil de tipul abstract OutputStream sau InputStream, care va fi
nlocuit cu o variabil de un tip flux concret (FileOutputStream s.a.), la construirea unui obiect de un tip
flux direct utilizabil. Clasa FilterInputStream este derivat din InputStream i conine o variabil de tip
InputStream. Metoda read() este o metod polimorfic, iar selectarea metodei necesare se face n functie de
tipul concret al variabilei "in" (transmis ca argument constructorului). Metoda read() din clasa
FilterInputStream preia functionalitatea metodei read() din clasa delegat, sau deleag operatia de citire
ctre clasa folosit la construirea obiectului filtru.
public class FilterInputStream extends InputStream {
protected InputStream in;
protected FilterInputStream (InputStream in) { // constructor
this.in=in;
// stabilire tip obiect "flux de I/E"
}
// citirea unui octet
public int read () throws IOException {
return in.read ();
// citirea depinde de tipul fluxului
}
... // alte metode
}
Clasele DataInputStream, BufferedInputStream, PushbackInputStream, LineNumberInputStream
sunt derivate din clasa FilterInputStream si contin metode de prelucrare a datelor citite. Cea mai folosit
este clasa DataInputStream care adaug metodelor de citire de octeti mostenite si metode de citire a tuturor
tipurilor primitive de date: readLine(), readInt(), readFloat() etc.
// "decorare" repetata, cu adaugare de facilitati la op. de I/E
public static void main (String arg[]) throws IOException {
FileInputStream fis= new FileInputStream (arg[0]);
// flux cu suport
fisier
BufferedInputStream bis = new BufferedInputStream (fis); // plus buffer de
citire
DataInputStream dis = new DataInputStream (bis); // plus operatii de citire de
linii si numere
String linie;
while ( (linie=dis.readLine()) != null)
System.out.println (lnis.getLineNumber()+" "+linie);
}
- 114 -
INFORMATIC*I*
Utilizarea fabricilor de obiecte poate fi privit ca un pas nainte pe calea separaiei interfa-implementare, n
sensul c permite oricte implementri pentru o interfa, cu orice nume de clase i cu orice date iniiale
necesare fabricrii obiectelor.
O fabric de obiecte se poate realiza n dou forme:
- Ca metod fabric (de obicei metod static) dintr-o clas, care poate fi clasa abstract ce definete tipul
comun al obiectelor fabricate. Aceast soluie se practic atunci cnd obiectele fabricate nu difer mult ntre
ele.
- Ca o clas fabric, atunci cnd exist diferene mari ntre obiectele fabricate. Alegerea (sub)tipului de
obiecte fabricate se poate face fie prin parametri transmisi metodei fabric sau constructorului de obiecte
fabric, fie prin fiiere de proprieti (fiiere de configurare).
O metod fabric poate fi o metod static sau nestatic. Metoda fabric poate fi apelat direct de
programatori sau poate fi apelat dintr-un constructor, ascunznd utilizatorilor efectul real al cererii pentru
un nou obiect. Desenul urmtor corespunde unei metode fabric din clasa Creator avnd ca rezultat un
obiect de tipul general Product.
Se folosesc metode fabric atunci cnd tipul obiectelor ce trebuie create nu este cunoscut exact de
programator, dar poate fi dedus din alte informaii furnizate de utilizator, la execuie. Rezultatul metodei este
de un tip interfa sau clas abstract, care include toate subtipurile de obiecte fabricate de metod.
Un exemplu de metod fabric controlabil prin argumente este metoda getInstance() din clasa Calendar,
care poate crea obiecte de diferite subtipuri ale tipului Calendar , unele necunoscute la scrierea metodei dar
adugate ulterior. Obiectele de tip dat calendaristic au fost create iniial n Java ca instane ale clasei
Date, dar ulterior s-a optat pentru o soluie mai general, care s in seama de diferitele tipuri de calendare
folosite pe glob. Clasa abstract Calendar (din java.util) conine cteva metode statice cu numele
getInstance() (cu i fr parametri), care fabric obiecte de tip Calendar. Una din clasele instaniabile
derivat din Calendar este GregorianCalendar, pentru tipul de calendar folosit n Europa. Metoda
getInstance() fr argumente produce un obiect din cel mai folosit tip de calendar:
public static Calendar getInstance() { return new GregorianCalendar(); }
Obiectele de tip Calendar se pot utiliza direct sau transformate n obiecte Date:
Calendar azi = Calendar.getInstance();
Date now = azi.getTime();
// conversie din Calendar in Date
System.out.println (now);
Metoda getInstance() poate avea unul sau doi parametri, unul de tip TimeZone i altul de tip Local, pentru
adaptarea orei curente la fusul orar (TimeZone) i a calendarului la poziia geografic (Local). Tipul
obiectelor fabricate este determinat de aceti parametri.
- 115 -
INFORMATIC*I*
Alte metode fabric care produc obiecte adaptate particularitilor locale (de ar) se afl n clase din
pachetul java.text: NumberFormat, DateFormat, s.a. Adaptarea se face printr-un parametru al metodei
fabric care precizeaz ara i este o constant simbolic din clasa Locale. Afiarea unei date calendaristice
se poate face n mai multe forme, care depind de uzanele locale i de stilul dorit de utilizator (cu sau fr
numele zilei din sptmn, cu nume complet sau prescurtat pentru lun etc.). Clasa DateFormat conine
metoda getDateInstance() care poate avea 0,1 sau 2 parametri i care poate produce diferite obiecte de tip
DateFormat. Exemple:
Date date = new Date();
DateFormat df1 = DateFormat.getDateInstance ();
DateFormat df2 = DateFormat.getDateInstance (2, Locale.FRENCH);
System.out.println ( df1.format(date));
//luna, an, zi cu luna in engleza
System.out.println ( df2.format(date));
// zi,luna,an cu luna in franceza
Exemple de servicii: acces la orice baz de date relaional (JDBC), comunicarea prin mesaje (JMS), analiz
de fiiere XML. Un parser XML de tip DOM este un program care analizeaz documente XML, semnaleaz
erori formale i creeaz un arbore echivalent documentului XML. Exist mai multe programe parser de la
diferii furnizori i este posibil s apar si alte parsere DOM mai performante n viitor. Deci nu exist o
singur clas parser DOM dar exist o interfa numit DocumentBuilder ce corespunde interfeei
AbstractProduct din desenul anterior. Toate clasele parser DOM trebuie s respecte aceast interfa, cu
metode de acces la arborele DOM. Numele i numrul acestor clase parser nu este cunoscut, deci nu se poate
obine un obiect parser prin instanierea unei clase. Numele clasei parser nu apare n aplicaie pentru a nu fi
necesar modificarea surselor aplicaiei la schimbarea tipului de parser (decuplare utilizare de
implementare). Numele clasei parser este determinat la execuia aplicaiei de ctre o metod fabric
newInstance(), care selecteaz acest nume dup un algoritm: dintr-un fiier de proprieti sau o clas parser
implicit dac nu apare alta n fiierul de proprieti. Fabrica de clase parser este DocumentBuilderFactory
i corespunde clasei AbstractFactory din desen; metoda newInstance() corespunde unei metode
createProduct() din desen.
- 116 -
INFORMATIC*I*
Secvena urmtoare arat cum se obine i cum se folosete un obiect parser DOM . Acest cod nu conine
nici un nume de clas parser; obiectul parser este produs de metoda fabric newDocumentBuilder() aplicat
unui obiect produs de metoda fabric static newInstance().
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // obiect fabric
DocumentBuilder builder = factory.newDocumentBuilder(); // obiect parser
Document doc = builder.parse( new File(arg[0]) );
// creare arbore DOM dintr-un fisier
Schema MVC a aprut n legtur cu programele de birotic (pentru calcul tabelar i pentru editare de texte),
de unde i numele alternativ de Document-View-Controller. Separarea net a celor trei componente (M, V si
C) permite mai mult flexibilitate n adaptarea i n extinderea programelor, prin uurina de modificare
separat a fiecrei pri (n raport cu un program monolit la care legturile dintre pri nu sunt explicite).
Astfel, putem modifica structurile de date folosite n memorarea foii de calcul (modelul) fr ca aceste
modificri s afecteze partea de prezentare, sau putem aduga noi forme de prezentare a datelor coninute n
foaia de calcul sau noi forme de interaciune cu operatorul (de exemplu, o bar de instrumente toolbar).
Separarea de responsabiliti oferit de modelul MVC este util pentru realizarea de aplicaii sau pri de
aplicaii: componente GUI, aplicaii Web de tip client-server s.a.
Practic toate aplicaiile Web sunt construite n prezent dup schema MVC, care se reflect i n structura de
directoare a aplicaiei. In aplicaiile Web partea de model conine clasele ce corespund tabelelor bazei de
date (inclusiv logica de utilizare a acestor date), partea de vizualizare corespunde interfeei cu utilizatorul
(obiecte grafice afiate n browser, validri asupra datelor introduse de operator, animaie i alte efecte
vizuale), iar partea de comand (controller) corespunde prelurii cererilor HTTP i transmiterii rspunsurilor
corespunztoare (care include de obicei date din model ).
Unele componente Swing sunt construite dup schema MVC i au n componena lor un obiect model:
JButton, JList, JTable, JTree .a. Modelul este o structur de date care poate genera evenimente i este
specific fiecrei componente: DefaultButtonModel, DefaultListModel, DefaultTableModel, etc.
- 117 -
INFORMATIC*I*
Exemplul care urmeaz folosete dou butoane i un cmp text i este structurat dup arhitectura MVC.
Componenta model (clasa Model) conine ca date un numr ntreg i genereaz evenimente la modificarea
valorii acestui numr. Clasa Controller trateaz evenimentele de la butoane, prin care operatorul aplicaiei
poate modifica starea modelului (valoarea din model). Clasa View reunete toate componentele de interfa
grafic (butoane i cmp text) i modific afisarea din cmpul text ca urmare a modificrii modelului
(reflect starea modelului).
Clasa controler reunete asculttorii la butoanele care controleaz starea modelului:
class Controller implements ActionListener {
private Model m;
public Controller (Model m) { this.m=m; }
public void actionPerformed (ActionEvent evt){
JButton b = (JButton) evt.getSource();
String txt = b.getActionCommand();
int x= m.getElement();
if (txt.equals("+")) m.setElement (x+1);
if (txt.equals("-")) m.setElement (x-1);
}
}
Clasa model genereaz eveniment PropertyChangeEvent la modificarea valorii memorate n model:
class Model extends JComponent {
int value;
// date din model (proprietate a componentei JFC)
public void setElement (int x) {
// la modificare proprietate
firePropertyChange ("value",value,value=x);
}
public int getElement () {return value; } // valoare proprietate
}
Clasa cu imaginea aplicaiei:
class View extends JFrame implements PropertyChangeListener {
JButton b1,b2;
JTextField t = new JTextField (6);
Model m; Controller c;
public View(Model m, Controller c) {
this.m=m; this.c=c;
b1= new JButton("+"); b2= new JButton("-");
m.addPropertyChangeListener ("value",this);
setLayout (new FlowLayout());
add (t); add(b1); add(b2);
b1.addActionListener (c); b2.addActionListener (c);
setSize(200,100); setVisible(true);
}
// reactioneaza la evenimente din model
public void propertyChange (PropertyChangeEvent ev) {
int x = m.getElement(); t.setText (""+x);
}
}
- 118 -
INFORMATIC*I*
Dependentele dintre clase sunt transmise aici prin contructori, dar n general se folosesc metode care transmit
unui obiect adresa unui alt obiect (injectare dependente prin metode):
public static void main (String args[]) {
Model m = new Model ();
Controller c = new Controller (m);
View v = new View(m,c);
v.go();
// pornire aplicatie
}
- 119 -
INFORMATIC*I*
}
// utilizare
public class DaoClient {
public List findBy (Filter f) {
Dao dao= DaoFactory.getInstance();
List all = dao.findAll ();
}
}
- 120 -
INFORMATIC*I*
Obiectul care injecteaz dependenele trebuie s dispun de informaii despre obiectele pe care le injecteaz
(nume i referine); aceste informaii se transmit de ctre persoana care dezvolt aplicaia fie prin fisiere de
configurare XML, fie prin adnotri Java introduse n codul surs.
Injectarea ntr-un obiect a unei
referine ctre un alt obiect se poate face n trei moduri:
- Prin constructorul clasei (Constructor Injection)
- Printr-o metod a clasei (Setter Injection)
- Printr-o interfa de injectare (Interface Injection)
Injectarea prin constructor necesit adugarea unui constructor cu argument de tip Dao:
public class DaoClient {
private Dao dao;
public DaoClient (Dao dao) { this.dao=dao;}
public List findBy (Filter filter) , }
Injectarea printr-o metod necesit adugarea unei metode de modificare a variabilei dao:
public class DaoClient {
private Dao dao;
public setDao (Dao dao) { this.dao=dao;}
public List findBy (Filter filter) , }
Injectarea prin interfa necesit definirea unei interfee, iar clasa client va implementa aceast interfa:
public interface InjectDao { void injectDao (Dao dao); }
public class DaoClient implements InjectDao {
private Dao dao;
public void injectDao(Dao dao){ this.dao=dao; }
public List findBy (Filter f) , }
Exemplu de transmitere ctre container a informaiilor necesare injeciei (Spring framework):
<beans>
<bean id="DaoClient" class="spring.DaoClient">
<property name="dao">
<ref local="Dao"/>
</property>
</bean>
<bean id="Dao" class="spring.DaoImpl">
<property name="file">
<value>date.txt</value>
</property>
</bean>
</beans>
- 121 -
INFORMATIC*I*
Produsele de tip container (Framework) cu injectare dependene pentru aplicaii Java pot fi clasificate n:
- Produse simple, pentru verificarea aplicabilitii schemei DI pe aplicaii cu cerine reduse:
Pico Container & NanoContainer, Avalon (Apache), HiveMind (Apache), s.a.
- Produse complexe, folosite efectiv pentru aplicaii reale:
Spring Framework, Guice (Google), Weld/ Web Beans (JBoss)
Pe platforma .Net (C#) exist de asemenea mai multe containere DI:
StructureMap, ObjectBuilder, SpringFramework.Net, Unity , MEF (Microsoft Extensibility Framework).
Tendinele mai noi n realizarea unor containere cu injectarea de dependene sunt:
- Utilizarea de adnotri Java n locul fiierelor de configurare pentru specificarea obiectelor care vor fi
injectate de container i a punctelor de injectare (JEE6, Guice, Spring Framework);
- Asocierea unui context (unui ciclu de viat) obiectelor injectate i gestiunea strii obiectelor de ctre
container, de unde i denumirea de CDI (Context and Dependency Injection) din JEE6.
- Inseria de ctre container a unor secvene de cod n puncte specificate, deci programare orientat pe
aspecte (AOP) sau interceptori sau orthogonal concerns. AOP (Aspect Oriented Programming) nseamn
pe scurt injectarea unor secvene pentru apelarea unor servicii n diverse puncte specificate de programator,
dar care nu corespund n general cu fluxul logic. Un exemplu este inseria de apeluri ctre un serviciu de
logger pentru nscrierea de mesaje ntr-un fiier de tip log (jurnal de evenimente).
- 122 -
INFORMATIC*I*
INFORMATIC*I*
INFORMATIC*I*
In final, se stabilesc dimensiunile ferestrei principale (metoda setSize() sau pack()) si se comand afiarea
ferestrei principale (metoda setVisible() sau show()). Fereastra principal a aplicaiei este n general de tipul
JFrame i conine o bar de titlu i trei butoane standard n colul dreapta-sus al ferestrei pentru operaii
de micsorare (minimizare), mrire (maximizare) i nchidere fereastr(X) .
Exemplul urmtor afieaz 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.setSize(500,300)
// sau frm.pack();
frm.setVisible (true);
// sau frm.show();
}
}
In lipsa unui apel al metodelor setSize() sau pack() , fereastra principal se afieaz iniial ntr-o form
redus la bara de titlu cu cele 3 butoane generale, dup care poate fi mrit. Metoda setSize() permite
afiarea ferestrei JFrame de la nceput cu dimensiunile dorite.
Adugarea de componente atomice la containerul JFrame se face cu metoda add(), aceeai metod de
adugare a unui nou element la un vector (obiect de tip Vector), deoarece clasa JFrame folosete un vector
ca o colecie de obiecte grafice. Exemplu de interfa cu o etichet JLabel:
public static void main (String args[ ]){
JFrame frame = new JFrame();
JLabel label = new JLabel ("Folder");
frame.add(label);
frame.setSize(200,200);
frame.setVisible(true);
}
// fereastra aplicatiei
// creare eticheta
// adauga eticheta la fereastr
// dimensiuni fereastra
// afisare continut fereastr
Dup apelul metodei setVisible() sau show() nu mai trebuie create i adugate alte componente vizuale
ferestrei JFrame, chiar dac se modific datele prezentate n unele din aceste componente (prin metode ale
claselor Swing respective) .
Efectuarea unui clic pe butonul de nchidere al ferestrei principale (X) are ca efect nchiderea ferestrei, dar
nu se termin aplicaia 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. O soluie simpl de terminare a
aplicaiei la nchiderea ferestrei principale este apelul unei metode din JFrame:
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
Adugarea mai multor obiecte grafice la JFrame ridic problema modului de dispunere (Layout) a acestor
componente unele fa de altele, n fereastr.
INFORMATIC*I*
Manager), obiect selectat prin metoda setLayout() din clase container (JFrame,JPanel, .a.) i care
stabilete automat dimensiunile i poziia fiecrei componente ntr-un panou. Pentru fereastra JFrame este
implicit modul de asezare BorderLayout, mod care folosete un al doilea parametru n metoda add() pentru
poziia componentei. Exist 5 poziii posibile: central, sus (nord), jos (sud), stnga (vest) i dreapta (est).
Poziia poate fi specificat fie printr-o constant din clasa BorderLayout, fie printr-un sir de caractere:
Center, North, South, East, West. Exemplu cu o etichet i un cmp text:
class DefaultLayout {
public static void main (String args[ ]) {
JLabel lbl1 = new JLabel ("Directory");
JTextField txt1 = new JTextField (16);
JFrame frm = new JFrame("Simple GUI");
frm.add (lbl1,"West"); frm.add(txt1,"Center");
frm.setVisible(true);
}
}
Dac nu se specific poziia la adugare, atunci componenta este centrat n fereastr, iar dac sunt mai
multe componente, atunci ele sunt suprapuse pe centrul ferestrei i nu se vede dect ultima adugat.
Pentru exemplul anterior este preferabil, ca aspect, s folosim modul de asezare FlowLayout:
public static void main (String arg[]) {
JLabel lbl1 = new JLabel ("Directory");
JTextField txt1 = new JTextField (16);
JFrame frm = new JFrame("Simple GUI");
frm.setLayout (new FlowLayout());
frm.add (lbl1); frm.add(txt1);
frm.setSize (300,80);
frm.show();
}
Modul FlowLayout plaseaz componentele una dup alta de la stnga la dreapta i de sus n jos n funcie de
dimensiunile lor i ale ferestrei principale, ceea ce este foarte comod pentru nceput. Dezavantajul acestui
mod este acela c asezarea componentelor se modific automat atunci cnd se modific dimensiunile
panoului, dimensiunile sau numrul componentelor. Exist diverse metode de a menine poziia relativ a
dou sau mai multe componente vizuale, indiferent de dimensiunile ferestrei.
Alte modaliti de dispunere a componentelor ntr-un panou sunt GridLayout (o matrice de componente
egale ca dimensiune), GridBagLayout, BoxLayout ( aezare compact pe vertical sau pe orizontal, la
alegere) si CardLayout ( componente / panouri care ocup alternativ acelai spaiu pe ecran).
Modurile de aezare mentionate sunt moduri utilizabile n programarea manual a interfeelor grafice,
deoarece un mediu vizual ca NetBeans folosete alte clase (GroupLayout, SpringLayout) pentru plasarea
automat a componentelor ntr-o fereastr, ca urmare a aciunilor de tragere (dragging) a componentelor
de ctre operator pe suprafaa vizual. In cazul unor interfee grafice cu un numr mai mare de componente
este preferabil utilizarea unui mediu vizual pentru aezarea componentelor, codul Java fiind generat
automat.
Utilizarea de containere intermediare JPanel este una din metodele pentru controlul plasrii de obiecte
grafice. Un panou JPanel are modul implicit de plasare FlowLayout, dar care poate fi modificat. Exemplu
de afiare a unui mic formular cu dou rubrici, fiecare cu o eticheta n stnga casetei text unde se vor
- 126 -
INFORMATIC*I*
- 127 -
INFORMATIC*I*
- 128 -
INFORMATIC*I*
INFORMATIC*I*
acionarea unui buton de mouse, deplasare mouse s.a. Noiunea de eveniment a aprut ca o abstractizare a
unei ntreruperi externe .
Un program controlat prin evenimente nu iniiaz momentul introducerii datelor, dar poate reaciona prompt
la orice eveniment produs de o aciune a operatorului. Funciile care preiau date nu sunt apelate direct i
explicit de alte funcii din program, ci sunt apelate ca urmare a producerii unor evenimente.
Structura unui program dirijat prin evenimente difer de structura unui program obisnuit prin existena
funciilor speciale de tratare a evenimentelor (Event handlers), care nu sunt apelate direct din program. Intrun program dirijat de evenimente exist dou tipuri principale de obiecte:
- Obiecte generatoare de evenimente (sursa unui eveniment);
- Obiecte receptoare de evenimente (obiecte asculttor).
Clasele generator sunt n general clase JFC sau AWT si ele creeaz obiecte eveniment ca efect al aciunii
operatorului pe suprafaa componentei respective.
Clasele receptor de evenimente sunt scrise de ctre
programatorul aplicatiei pentru c metodele de tratare a evenimentelor observate sunt specifice fiecrei
aplicaii. Aceste clase trebuie s implementeze anumite interfete JFC, deci trebuie s conin anumite metode
cu nume i semntur impuse de clasele JFC.
In Java un eveniment este un obiect de un tip clas derivat din clasa EventObject. Declanarea unui
eveniment are ca efect apelarea de ctre obiectul generator a unei metode din obiectul asculttor, care
primete ca argument un obiect eveniment. Tipul obiectului eveniment este determinat de tipul componetei
GUI care a generat evenimentul dar i de actiunea operatorului uman. De exemplu, un clic pe butonul de
nchidere a unei ferestre JFrame genereaz un alt eveniment dect un clic pe butonul de micorare a
ferestrei.
Evenimentele JFC pot fi clasificate astfel:
- Evenimente asociate fiecrei componente vizuale (buton, cmp text, etc.), generate fie prin mouse, fie din
taste (apsare buton, tastare Enter, .a.).
- Evenimente asociate dispozitivelor de introducere ( mouse sau tastatur).
La fiecare obiect generator de evenimente se pot nregistra (se pot nscrie) mai multe obiecte asculttor
interesate de producerea evenimentelor generate. Operaia de nregistrare se face prin apelarea unei metode
de forma addXListener (din obiectul generator), unde X este numele (tipul) evenimentului si care este
totodat i numele unei interfee.
Un eveniment Swing poate avea mai muli asculttori i un asculttor poate asculta la mai muli generatori
de evenimente. De exemplu, terminarea introducerii ntr-un cmp text se poate face fie prin tasta Enter
(eveniment ActionEvent), fie prin taste cu sgei sau prin mutarea cursorului pe un al cmp (eveniment
FocusLost); ambele evenimente pot avea un singur asculttor.
- 130 -
INFORMATIC*I*
- 131 -
INFORMATIC*I*
Pentru a obine acelai efect si prin apsarea unei taste asociate butonului (de exemplu combinaia de taste
Ctrl-C) este suficient s adugm o instruciune: buton.setMnemonic (KeyEvent.VK_C);
Se observ c am folosit clasa asculttor o singur dat, pentru a crea un singur obiect. De aceea se practic
frecvent definirea unei clase asculttor anonime acolo unde este necesar:
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
Toolkit.getDefaultToolkit().beep();
}
});
Diferena dintre evenimentul generat de un buton i un eveniment generat de clic pe mouse este c primul
apare numai dac dispozitivul mouse este poziionat pe aria ocupat de buton, n timp ce al doilea apare
indiferent de poziia cursorului mouse pe ecran. In plus, evenimentele generate de componente JFC conin n
ele sursa evenimentului i alte informaii specifice fiecrei componente.
- 132 -
INFORMATIC*I*
INFORMATIC*I*
O variant cu numr minim de clase este definirea clasei cu fereastra aplicaiei ca asculttor la evenimente,
ceea ce elimin clasele separate cu rol de asculttor :
class MFrame extends JFrame implements ActionListener {
JButton b1 = new JButton (" + ");
JButton b2 = new JButton (" - ");
JTextField text = new JTextField (6);
int n=0;
public MFrame() {
Container c = getContentPane();
b1.addActionListener (this);
b2.addActionListener (this);
c.setLayout (new FlowLayout());
c.add(b1); c.add(b2);
text.setText(" "+n); c.add (text);
}
public void actionPerformed (ActionEvent ev) {
Object source =ev.getSource();
if (source==b1) ++n;
else if (source==b2) --n;
text.setText(" "+n);
}
}
Pentru reducerea numrului de clase de nivel superior i pentru simplificarea comunicrii ntre clasele
asculttor la evenimente putem include clasele receptor n clasa cu fereastra aplicaiei:
class MFrame extends JFrame {
JButton b1 = new JButton (" + "), b2 = new JButton (" - ");
JTextField text = new JTextField (6);
int n= 0;
public MFrame() {
Container c = getContentPane();
b1.addActionListener (new B1L()); b2.addActionListener (new B2L());
c.setLayout (new FlowLayout());
c.add(b1); c.add(b2);
text.setText(" "+n);
c.add (text);
}
class B1L implements ActionListener {
// clasa inclusa in MFrame
public void actionPerformed (ActionEvent ev) {
text.setText(" "+ ++n);
}
}
class B2L implements ActionListener {
// clasa inclusa in MFrame
public void actionPerformed (ActionEvent ev) {
text.setText(" "+ --n);
}
}
}
- 134 -
INFORMATIC*I*
Utilizarea de clase incluse anonime reduce i mai mult lungimea programelor, dar ele sunt mai greu de citit i
de extins n cazul unor interaciuni mai complexe ntre componentele vizuale.
In examinarea unor variante pentru aplicaia anterioar am urmrit reducerea lungimii codului surs i al
numrului de clase din aplicaie, dar acestea nu reprezint indicatori de calitate ai unui program cu obiecte i
arat o proiectare fr perspectiva extinderii aplicaiei sau reutilizrii unor pri i n alte aplicaii. Un
dezavantaj comun soluiilor anterioare este cuplarea prea strns ntre obiectele asculttor la butoane i
obiectul cmp text unde se afieaz rezultatul modificrii ca urmare a acionrii unui buton: metoda
actionPerformed() apeleaz direct o metod dintr-o alt clas (setText() din JTextField). Dei programarea
este mai simpl, totui tratarea evenimentului de buton este specific acestei aplicatii iar clasele asculttor nu
pot fi reutilizate si n alte aplicaii.
In cazul programelor Java cu interfa grafic generat de un mediu vizual (soluie recomandat) structura
este stabilit automat de IDE i nu este alegerea programatorului.
INFORMATIC*I*
O clas aplet poate fi transformat ntr-o aplicaie prin scrierea unei funcii 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[ ]) {
JFrame f = new JFrame();
JAplet aplet = new JAplet();
f.getContentPane().add (aplet);
aplet.init();
f.setVisible (true);
}
Din punct de vedere funcional un aplet contine cteva funcii, care trebuie (re)definite de utilizator i sunt
apelate de programul gazd. Un aplet care trateaz evenimente externe trebuie s conin i 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 instruciuni de forma urmtoare
comp.addXXXListener(this);
Exemplul urmtor este un aplet care afieaz un buton n centrul ferestrei puse la dispoziie de programul
gazd i emite un semnal sonor (beep) la "apsarea" pe buton, adic la acionarea 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 i marcajul
<applet ...>. Metoda "paint" are un parametru de tip Graphics, iar clasa Graphics contine metode pentru
desenarea de figuri geometrice diverse i pentru afiarea de caractere cu diverse forme i mrimi:
public void paint (Graphics g) {
g.drawRect (0, 0, getSize().width - 1, getSize().height - 1); // margini fereastra
g.drawString ("text in aplet", 10, 30);
// afisare text
}
INFORMATIC*I*
- Preia aciunile transmise prin mouse sau prin tastatur obiectului respectiv.
- Permite modificarea datelor ca rspuns la aciuni ale operatorului uman sau la cereri exprimate prin
program si actualizeaz imaginea de pe ecran a acestor date.
Arhitectura MVC (Model-View-Controller) separ n cadrul unei componente vizuale cele trei funcii
eseniale ale unui program sau ale unui fragment de program: intrri (Controller), date i prelucrri (Model),
iesiri (View).
Partea numit model reprezint datele i funcionalitatea componentei, deci definete starea i logica
componentei. Imaginea (view) red ntr-o form vizual modelul, iar partea de comand (controller)
interpreteaz gesturile utilizatorului i acioneaz asupra modelului (defineste comportarea componentei).
Poate exista i o legtur direct ntre partea de control i partea de redare, n afara legturilor dintre model i
celelalte dou pri (imagine i comand).
Comunicarea dintre cele trei pri ale modelului MVC se face fie prin evenimente, fie prin apeluri de
metode. Modelul semnaleaz prii de prezentare orice modificare n starea sa, prin evenimente, iar partea de
imagine poate interoga modelul, prin apeluri de metode. Partea de comand este notificat prin evenimente
de aciunile (gesturile) operatorului uman si modific starea modelului prin apeluri de metode ale
obiectului cu rol de model; n plus poate apela direct i metode ale obiectului de redare (pentru modificarea
imaginii afisate).
Clasele JFC folosesc o variant a modelului MVC cu numai doi participani: o clas model i o clas
delegat care reunete funciile de redare i de control pentru a usura sarcina proiectantului, deoarece
comunicarea dintre controler i imagine poate fi destul de complex.
Componentele JComboBox, JList, JTable, JTree, JMenuBar includ ntotdeauna un obiect model care
poate fi extras printr-o metod getModel() pentru a se opera asupra lui. Obiectul model poate fi creat automat
n constructor, pe baza unor colectii, sau poate fi creat de programator i transmis clasei care asigur
prezentarea
datelor
din
model.
Exist
clase
predefinite
pentru
obiecte
model
(DefaultComboBoxModel,DefaultListModel, DefaultTreeModel) dup cum se pot defini i alte clase
model care s respecte interfete impuse.
Un model este o structur de date care poate genera evenimente la modificarea datelor i conine metode
pentru adugarea i eliminarea de asculttori la evenimentele generate de model. Clasele JList,JTable .a.
sunt asculttori la evenimente generate de clasele model respective, deci modificarea datelor din model va
modifica automat i afisarea pe ecran (se vor afia noile date din obiectul model).
Datele prezentate ntr-un obiect JList, JTable, JTree s.a. se pot modifica n cursul execuiei programului,
iar afiarea trebuie s reflecte aceste modificri. De exemplu, se afieaz date din directoare sau din fiiere al
cror nume se introduce sau se modific de ctre operator dup afiarea interfeei grafice (prin introducere n
cmpuri text, de exemplu).
Ca tehnic general, nu se construiesc alte obiecte vizuale (JList, JTable, JTree) cu noile date dup afisarea
interfeei grafice i nici nu se apeleaz metode de reafiare (repaint() sau altele), ci se apeleaz metode care
transmit la obiectul vizual un alt model (setModel()), sau se apeleaz metode de modificare a obiectului
model.
Modificrile asupra obiectului model sunt redate automat pe ecran deoarece obiectul vizual este asculttor la
evenimente generate de model. In general, clasele model contin i metode de modificare a datelor (interfeele
claselor model nu impun astfel de metode).
- 137 -
INFORMATIC*I*
O alt posibilitate este s se creeze un nou model (de exemplu dup reordonarea unui vector sau unui tabel)
i s se retransmit noul model la acelai obiect vizual din interfaa grafic.
Utilizatorii au posibilitatea s-i defineasc alte clase model, dar aceste clase trebuie s respecte anumite
interfee Java (ListModel, ComboBoxModel, TableModel, TreeModel). Pentru facilitarea definirii de noi
clase model (model de list sau de tabel) exist clase abstracte care implementeaz parial interfeele
mentionate: AbstractListModel, AbstractTableModel. Clasele model gata definite au un nume care ncepe
cu Default (DefaultListModel, DefaultTableModel, DefaultTreeModel) i constructori ce primesc ca
argument un vector (model de list), o matrice (model de tabel) sau un nod rdcin (model de arbore).
Clasele cu model au att constructor cu argument model, ct i constructori cu diverse structuri de date, pe
baza crora se construiesc automat obiecte model; de aceea exist metode getModel() n toate aceste clase,
indiferent dac s-a transmis un model creat separat sau nu (model creat implicit).
Componenta vizual JList afiseaz pe ecran o list de valori, sub forma unei coloane, i permite selectarea
uneia dintre valorile afiate, fie prin mouse, fie prin tastele cu sgei. Datele afiate pot fi de orice tip clas i
sunt memorate ntr-un obiect colecie (Vector) sau model; o referin la acest obiect este memorat n
obiectul JList de constructorul obiectului JList. Exist mai muli constructori, cu parametri de tipuri diferite:
vector intrinsec, Vector sau ListModel. Exemple:
String v =,unu,doi,trei-;
JList list1 = new JList (v);
// vector intrinsec
Vector vec = new Vector ( Arrays.asList(v));
JList list2 = new JList(vec);
// obiect de tip Vector
JList list3 = new JList (new DefaultListModel());
Clasa predefinit DefaultListModel pentru model de list are un singur constructor, fr argumente, dar are
metode de adugare si de modificare a obiectelor din vectorul folosit de obiectul model. Un obiect JList cu
coninut variabil se poate folosi i fr obiect model creat explicit, prin retransmiterea unui vector cu date la
obiectul JList (metoda setListData()). Exemplu de afiare ntr-un obiect JList a numelor fiierelor dintr-un
director al crui nume se introduce (sau se modific) ntr-un cmp text:
class FileList extends JFrame implements ActionListener{
JList jlist = new JList();
// pentru continut director
JTextField tf = new JTextField(12);
// pentru nume director
public FileList() {
Container cp = getContentPane();
cp.add (tf,"North"); cp.add (new JScrollPane(jlist));
tf.addActionListener (this);
setSize(300,600); show();
}
public void actionPerformed (ActionEvent ev) {
File d=new File(tf.getText());
// creare ob. File cu nume director
if (! d.isDirectory ()) return;
String files[] = d.list();
// vector cu nume de fisiere
Vector v = new Vector (Arrays.asList(files));
jlist.setListData(v);
// transmite vector la JList
}
}
- 138 -
INFORMATIC*I*
Modificarea datelor din vector (vector intrinsec sau obiect Vector) nu are efect asupra datelor afiate n
obiectul JList dect dac se apeleaz la fiecare modificare i metoda setListData(), care apeleaz metoda
setModel() din JList. Modificarea datelor din model (prin metode ca addElement(), setElementAt(),
removeElementAt()) se reflect automat pe ecran, deoarece obiectul JList este asculttor la evenimentele
generate de model. Exemplu:
class FileList extends JFrame implements ActionListener{
DefaultListModel model = new DefaultListModel();
JList jlist = new JList(model);
JTextField tf = new JTextField(12);
public FileList() {
Container cp = getContentPane();
cp.add (tf,"North");
cp.add (new JScrollPane(jlist));
tf.addActionListener (this);
setSize(300,600); show();
}
public void actionPerformed (ActionEvent ev) {
File d=new File(tf.getText());
if (! d.exists()) return;
String files[] = d.list();
model.clear();
// sterge continut anterior model
for (int i=0;i<files.length;i++)
// adauga nume fisiere la model
model.addElement(files[i]);
// modelul modifica si obiectul JList !
}
}
- 139 -
INFORMATIC*I*
INFORMATIC*I*
</project>
Gruparea mai multor operaii n aciuni numite target, permite reutilizarea acestora n diferite proiecte.
Multe dintre aplicaiile Java sunt aplicaii Web, care necesit instalarea pe un server Web pentru testare.
Timpul necesar dezvoltrii de aplicaii mai mari poate fi mult redus prin utilizarea unui mediu integrat de
dezvoltare (IDE= Integrated Development Environment), care asigur o serie de faciliti pentru editarea
programelor, pentru depanarea lor, pentru construirea de aplicaii i pentru testarea lor, toate cu o interfa
grafic prietenoas i usor de utilizat. Un astfel de produs integreaz mai multe programe diferite utilizate n
linie de comand: editor de texte, compilator, depanator, suport pentru teste unitare (JUnit), constructor de
aplicaii, server Web, etc.
Pentru editare (scrierea i modificarea surselor) un IDE este de ajutor prin semnalarea unor erori nainte de
compilare (se face o analiz sintactic pe msur ce se introduc instruciuni), prin autocompletarea unor
nume de clase sau metode, prin ajutor (Help) cu documentaia claselor, prin refactorizare s.a. Pentru
construirea de aplicaii se creeaz automat fiiere build Ant pentru fiecare proiect. Unitatea de lucru a unui
IDE este un proiect (project), care grupeaz ntr-o structur de foldere fiiere diverse: surse, fiiere
rezultate din compilare, biblioteci, fiiere de proprieti, fisiere build, s.a. In general un proiect corespunde
unei aplicaii dar este posibil ca o aplicaie s fie dezvoltat prin cteva proiecte, unele pentru biblioteci de
clase folosite n aplicaie i un proiect pentru pornirea aplicaiei (cu metoda main()).
Cele mai utilizate produse IDE pentru Java sunt Eclipse, NetBeans si Intellij IDEA care permit dezvoltarea
de aplicaii n mai multe limbaje: Java, C, C++, PHP, Groovy, .a. In plus, ele pot fi folosite i ca medii
vizuale, pentru crearea interfeelor grafice ale aplicaiilor fr programare manual: utilizatorul alege
componentele grafice, le configureaz proprietile i le plaseaz pe o suprafa ce reprezint fereastra
principal a aplicaiei, iar IDE genereaz codul surs pentru a realiza interfaa grafic desenat de utilizator i
ajut la tratarea evenimentelor generate de componentele grafice.
- 141 -
INFORMATIC*I*
Urmeaz alegerea tipului de proiect; vom alege un proiect standard Java (Java Application):
Urmeaz stabilirea numelui i locaiei folderului care va conine proiectul; se pot accepta propunerile IDE
sau se pot modifica numele i locul proiectului.
- 142 -
INFORMATIC*I*
Dac s-a ales generarea clasei Main, atunci NetBeans va crea un schelet care va fi completat de utilizator.
Exemplu:
/* To change this template, choose Tools | Templates and open the template in the editor. */
package helloworldapp;
/* @author <your name> */
public class HelloWorldApp {
/* @param args the command line arguments */
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
Ecranul unui proiect activ (n lucru) conine mai multe seciuni, aa cum se vede din figura urmtoare
- 143 -
INFORMATIC*I*
Compilarea se face automat la salvarea textului introdus. Execuia se comand fie din meniu
(Run>RunMainProject), fie cu tasta F6. Rezultatele apar n fereastra cu titlul Output, deschis automat:
Aplicaiile reale conin multe clase i alte fiiere. Beneficiarul aplicaiei primete de obicei o arhiva de tip
jar n care se mpacheteaz toate fiierele necesare folosirii aplicaiei. Crearea arhivei se face fie din
meniu: Run > Clean and Build Main Project sau cu scurttura Shift-F11. Arhiva este plasat n subdirectorul dist (pentru distribuie) din directorul proiect.
Pentru includerea n proiect a unor biblioteci de clase Java (altele dect cele standard SDK):
Extindere nod proiect > clickdreapta pe Libraries > Add JAR/Folder > selectare nume/cale fiier de tip
jar sau folder.
Depanarea programelor (Debugging) se face prin selectarea opiunii Debug din meniul principal sau prin
Ctrl-F5. In acest mod se pot utiliza urmtoarele procedee: Stablilirea unor puncte de oprire temporar
(Breakpoints), Execuia pas-cu-pas (Step-Over) a instruciunilor surs, Execuia pn la poziia cursorului,
Inspectarea coninutului unor variabile din program, Modificarea valorii unor variabile din panoul de
variabile.
Comenzi utilizabile n modul depanare:
Continue : se continu execuia pn la urmtorul punct de oprire sau pn la terminarea programului
Step-into : se intr n execuia pas cu pas a instruciunilor unei funcii (metode) apelate; n mod normal
funciile apelate nu se execut pas cu pas.
Step-out : se iese din execuia pas-cu-pas a unei funcii i se trece la funcia care a facut apelul.
Run-to-cursor : alternativ la breakpoint pentru execuia unei secvene de instruciuni (pn la poziia
cursorului)
Finish : ieire din modul debugging
Stabilirea unor puncte de oprire (Breakpoints) se poate face si prin clic pe bara din marginea din stnga a
ferestrei cu sursa programului:
- 144 -
INFORMATIC*I*
Instruciunea din program care urmeaz a fi executat este marcat cu o linie verde:
- 145 -
INFORMATIC*I*
Marcare erori (Hints for Syntax errors) : instruciunile cu erori sunt marcate cu un punct rou la stnga;
dac se apas pe becul afiat sau se folosesc tastele Alt-Enter atunci se afieaz cauza erorii.Erorile de
compilare sunt semnalate chiar de la introducerea textului surs n fereastra de editare, prin subliniere cu
rou. Exemplu:
O eroare frecvent este absenta instructiunii import la introducerea unui nume de clas; pentru corectare se
face clic dreapta pe numele clasei i se alege Fix Import.:
Completare automat (Auto-complete ) : dup introducerea parial a numelui unei clase sau metode se
apas Ctrl-space pentru afiarea opiunilor posibile de completare automat a ntregului nume. Exemplu:
- 146 -
INFORMATIC*I*
Sugestiile editorului se aplic i pentru cuvinte cheie, variabile i parametrii de funcii. Exemple:
Editorul NetBeans semnaleaz clasele din pachetele neimportate i adaug la cerere instruciunile import
necesare (cu combinaia de taste Ctrl-Shift-I). Exemplu:
La selectarea unei clase pentru completare automat se adaug si instruciunea import necesar.
Editorul NetBeans poate genera automat cod Java pentru constructori, metode, metode suprascrise, metode
delegate s.a. folosind tastele Alt-Insert . In exemplul urmtor se cere generarea unui constructor
Afiare documentaie Javadoc: Clic dreapta pe numele metodei > Show Javadoc sau Alt-F1. Alt variant:
se pune cursorul pe numele unei metode sau clase i se apas Ctrl-space pentru afiarea parial a
documentaiei referitoare la metoda sau clasa respectiv. In continuare se poate tasta din nou Ctrl-space
pentru lista metodelor sau se folosesc butoanele afiate n fereastr pentru a alege afiarea documentaiei
pentru clasa respectiv ntr-un browser Web
sau afiarea sursei clasei
(dac este disponibil mediului
NetBeans). Exemplu:
- 147 -
INFORMATIC*I*
abloane de cod (Code templates) : se pot defini forme prescurtate pentru secvene uzuale folosind tasta
TAB; de ex. sout n loc de System.out.println sau fori pentru un ciclu for. Exemplu:
Butoanele New si Remove permit adugarea sau eliminarea unui ablon de cod
Numerotare linii (Line Numbers): click-dreapta pe marginea din stnga a unei instruciuni pentru afiare
numere linii surs
Format: Formatare cod surs prin Alt-Shift-F sau click-dreapta (Source din meniu) i Format pentru
indentare i formatare cod surs
Comentare temporar bloc de cod surs : selectare bloc i Source->Toggle Comment din meniu
- 148 -
INFORMATIC*I*
- 149 -
INFORMATIC*I*
Inlocuirea unui bloc de cod cu o metod prin introducerea definiiei metodei i apelului metodei
(Introduce Method).
Generarea metodelor get()i set() pentru un cmp i nlocuirea referinelor la cmp prin apeluri de
metode (Encapsulate Fields)
Mutarea unei clase ntr-un alt pachet (Move Class)
tergere sigur (Safely Delete), dac nu mai sunt referinte la acel element.
Modificarea parametrilor unei metode (Change Method Parameters)
Fiecare metod de tip assert poate avea un parametru suplimentar de tip String care este un mesaj afiat n
caz c aseriunea (condiia) este fals.
In funciile de test se folosesc n general obiecte ale clasei testate; aceste obiecte pot fi create n interiorul
metodei sau n afara metodelor de test. In acest scop sunt prevzute n clasa de test metodele cu numele
setUp() si tearDown() (JUnit 3) sau metode cu orice nume dar adnotate cu @Before si @After. Metoda
setUp() creeaz obiectele necesare metodelor de test i initializeaz variabile ale clasei care vor fi folosite n
metodele de test, nainte de fiecare test. Metoda pereche tearDown() anuleaz operatiile din setUp()i reface
starea iniial, dac e necesar.
Testele unitare pot fi clasificate n trei categorii:
- 150 -
INFORMATIC*I*
INFORMATIC*I*
@Override
protected void tearDown() throws Exception {
super.tearDown();
}
public void testDiv() {
System.out.println("div");
int a = 0;
int b = 0;
Calc instance = new Calc();
int expResult = 0;
int result = instance.div(a, b);
assertEquals(expResult, result);
// TODO review the generated test code and remove the default call to fail.
fail("The test case is a prototype.");
}
}
. . . // testMul
Aa cum indic i comentariile vom elimina liniile finale care apeleaz metoda fail() si vom cere execuia
testelor n una din variantele:
- Din meniul principal: Run > Test Project
- Click dreapta pe numele proiectului > Test (sau Alt-F6)
Deoarece se genereaz automat date de test (a,b) cu valoarea zero se va produce o excepie neprevzut i
netratat n metoda div(), iar testul eueaz (Test failed) din cauza acestei erori.
Pentru testarea metodelor care pot genera excepii se poate verifica n metoda de test dac excepia a fost
tratat sau nu n metoda verificat. Dac adugm metodei div tratarea exceptiei aritmetice atunci testul va
trece cu bine Exemplu:
public void testDiv() {
int a = 0; int b = 0;
int result=0;
Calculator instance = new Calculator();
int expResult = 0;
try {
result = instance.div(a, b);
} catch (ArithmeticException ex){
fail ("ArithmeticException");
}
assertEquals(expResult, result);
}
Teste generate n varianta JUnit 4:
public class CalcTest {
public CalcTest() { }
@BeforeClass
public static void setUpClass() { }
@AfterClass
public static void tearDownClass() { }
@Before
public void setUp() { }
@After
- 152 -
INFORMATIC*I*
INFORMATIC*I*
Design (cu suprafata JFrame, paleta de componente Palette si foile de proprietti) i ecranul Source cu codul
surs generat de NetBeans pe baza desenului din ecranul Design.
Primul exemplu este un buton care genereaz un semnal sonor la actionarea sa. Se creeaz un nou proiect, se
accept numele propuse pentru proiect i pentru clasa JFrame i se trage pe suprafata de proiectare o
component JButton. Se editeaz proprietatea text (Edit Text) pentru modificarea inscripiei de pe buton:
Clic dreapta pe buton > Edit Text > Click Me
- 154 -
INFORMATIC*I*
In modul Design se pot muta (trage) imaginile componentelor, se pot trage de marginile lor pentru
modificarea dimensiuilor i se pot ajusta spaiile libere (intervalele) dintre componente.
Imaginea din ecranul Design nu este identic cu interfaa afisat la execuia codului generat; pentru a vedea
cum va arta la execuie fie executm aplicaia, fie afim un Preview folosind butonul .
Pentru asocierea unei aciuni la acionarea butonului trebuie tratat evenimentul generat de apsarea sa prin:
Clic dreapta pe buton > Events > Action > actionPerformed.
- 155 -
INFORMATIC*I*
IDE genereaz scheletul metodelor asculttor i asocierea lor cu componentele respective, iar utilizatorul
poate s completeze manual, n modul Source, instruciunile din metodele asculttor specifice aplicaiei.
- 156 -
INFORMATIC*I*
Codul surs generat are seciuni care nu pot fi modificate manual (marcate cu un fond colorat) i seciuni
care pot fi modificate direct de utilizator. Seciunile nemodificabile sunt cele care conin declaraii de
variabile i funcia initComponents() care iniializeaz aceste variabile i care stabilete modul de dispunere
(GroupLayout). Numele variabilelor sunt generate de IDE pe baza numelor componentelor Swing (jButton1,
jButton2,...) i pot fi schimbate numai din modul Design (clic dreapta > Change Variable Name).
In al doilea exemplu se dezvolt o aplicaie cu interfa grafic
pentru simularea unui calculator de buzunar cu patru operaii
aritmetice. Interfaa grafic conine urmtoarele componente:
trei cmpuri text pentru introducerea celor dou numere i
pentru rezultat, trei etichete pentru fiecare cmp text i patru
butoane pentru comanda operaiilor.
Se va crea un proiect nou de tip Java Application dar fr bifarea
casetei Create Main Class. Se va aduga acestui proiect o
fereastra JFrame astfel:
Clic dreapta pe nume proiect > New > Other > JFrame Form
Se aduc apoi succesiv pe suprafaa de lucru JFrame componentele din paleta de componente: clic stnga pe
numele i desenul componentei din fereastra Palette, deplasare n poziia dorit din fereastra central i clic
stnga pentru aducerea componentei selectate (n forma i cu numele propuse de IDE). Pentru modificarea
unei proprieti se face clic dreapta pe componenta respectiv i se alege proprietatea; de exemplu pentru a
schimba numele etichetei (din jLabel1, jLabel2, etc) se alege Edit Text i se introduce alt nume pe desenul
componentei.
- 157 -
INFORMATIC*I*
Pentru tratarea evenimentelor generate de o component se face clic dreapta pe component, se alege Events
i apoi tipul de eveniment i numele metodei asculttor. Pentru butonul de adunare se va alege Events >
Action > actionPerformed, dup care IDE comut pe ecranul Source n poziia unde trebuie inserate
instruciuni n metoda actionPerformed(). Exemplu de instruciuni introduse:
float num1 = Float.parseFloat(jTextField1.getText());
float num2 = Float.parseFloat(jTextField2.getText());
float result = num1+num2;
jTextField3.setText(String.valueOf(result));
Pentru verificarea se execut aplicaia:
Dac se cere numele clasei cu metoda main() atunci se accept sau se introduce numele clasei cu interfaa
grafic (de ex. NewJFrame).
Pentru execuia aplicaiei fr NetBeans (n linie de comand) trebuie creat o arhiv jar cu rol de fiier
executabil (n subdirectorul dist al proiectului, pentru distribuirea aplicaiei catre clieni):
Run > Clean and Build Main Project (Shift-F11)
- 158 -
INFORMATIC*I*
Limbaje statice, cu tipuri de date declarate i verificate la compilare : Java, Scala .a.
Limbaje dinamice, cu tipuri de date stabilite i modificate la execuie: Javascript, PHP, .a.
Limbajele dinamice sunt de obicei interpretate, fr o compilare anterioar, fiind numite si limbaje de
scripting (script= program scurt direct interpretat pentru a produce rapid rezultate). Limbajul Groovy
poate fi folosit att ca limbaj compilat ct si ca limbaj interpretat (de scripting); el permite i tipurile de date
statice din Java dar i tipuri dinamice, deduse din modul lor de utilizare.
INFORMATIC*I*
Groovy este un limbaj utilizabil pe maina virtual Java, cu o sintax asemntoare cu Java dar mai simpl,
care poate fi utilizat ca limbaj de scripting sau ca limbaj compilat. Codul surs Groovy este mult mai
compact dect codul Java echivalent dar i mai uor de citit pentru c are constructii mai naturale.
Groovy are faciliti inspirate din Ruby, Python, Smalltalk si ilustreaz multe concepte moderne n
programare: programare orientat pe obiecte avansat (fr tipuri primitive, cu obiecte colecie, .a.) lucrul
cu expresii regulate, faciliti de prelucrare si de creare documente XML, functori (closure), obiecte colecie
cu o sintax simpl (fr instaniere de clase), tipuri definite implicit prin valoarea atribuit (duck types) si
dinamice (o variabil i poate modifica tipul), metaprogramare (Meta Object Protocol) care permite
adugarea de noi metode la execuie fie unei clase fie unui obiect, definirea i utilizarea de limbaje
specializate n Groovy (DSL=Domain Specific Languages), adugarea simpl de teste unitare i de obiecte
surogat (Mock Objects), integrarea n limbaj a unor instrumente de gestiune a proiectelor software (Ant,
Maven), .a.
Groovy folosete i extinde JDK: bibliotecile de clase Java, unele extinse cu noi metode, la care se adaug
clase specifice Groovy (GDK). Oricare alte biblioteci de clase Java pot fi folosite n Groovy.
Cele mai importante medii integrate pentru dezvoltare de programe Java (Eclipse, NetBeans, IDEA) au
suport i pentru limbajul Groovy (recunoatere sintax, documentaie, integrare cu JUnit).
INFORMATIC*I*
// are rezultat null daca book este null, dar nu produce exceptie
- Constantele numerice sunt tratate n Groovy ca obiecte (din clasele Integer, Float, Double) i nu mai
exist tipurile primitive din Java. Clasa Integer are si metode noi, cum ar fi metoda times care repet o
secven de cod ( un functor, mai exact) de un numr de ori egal cu valoarea obiectului ntreg. Exemplu:
3.times {println}
- Instruciunea for poate avea si o form care foloseste cuvntul cheie in i repet ciclul pentru valori ale
variabilei contor dintr-un subdomeniu (range) sau dintr-o colecie. Exemple:
- 161 -
INFORMATIC*I*
INFORMATIC*I*
Variabilele dintr-un obiect, numite i "proprieti", pot fi accesate ca i n Java, prin metode get() i set()
(generate automat) sau mai simplu, ca membri ai unei structuri (ca i cum ar fi variabile publice). Selectarea
unei proprieti se poate face i cu operatorul [ ].
// varianta de utilizare
def b = new Book()
b.title='POO'
// b.setTitle ('POO')
b.price=10
println b.title+' '+ b.price
// println b.getTitle()+ ' '+ b.getPrice()
b['title']='POO'
b['price']=10
// [2,3,4,7,8]
- Argumente cuvinte cheie n funcii: La apelul constructorilor si metodelor se pot folosi parametri cu nume.
In cazul unui constructor numele parametrului efectiv este acelai cu numele cmpului din clas care este
iniializat cu valoarea parametrului (este numele unei proprieti). Exemplu:
def b= new Book (price:10, title:'POO')
- Chiar dac se folosesc tipuri generice n colecii, Groovy le trateaz ca pe colecii de obiecte Object i pot
primi date de orice tip. Exemplu:
TreeSet<String> a = new TreeSet<String>()
a.add(123)
- In Groovy este posibil suprancrcarea operatorilor, prin asocierea fiecrui operator (ce poate fi redefinit)
cu un nume de metod. Exemple de operatori i metode echivalente:
a+b
a%b
a++,++a
a.plus(b)
a.mod(b)
a.next()
Definirea acestor metode ntr-o clas permite apelarea lor (i) prin operatori. Ex:
class Complex {
private double re,im
Complex (double re, double im) { this.re=re;this.im=im }
def plus (Complex x) { this.re+=x.re; this.im+=x.im; return this }
String toString() {" ($re,$im)" }
}
Complex a=new Complex(3,4), b= new Complex(7,6)
println a+b
// sau a.plus(b)
- Utilizarea de functori, adic scrierea operaiilor care realizeaz o prelucrare acolo unde este nevoie de acea
prelucrare, fr a defini i instania o clas cu o funcie care s conin acel cod. Exemplu:
println ( [3,7,2,8,4]. findAll { x-> x< 6} )
// [3,2,4]
- 163 -
INFORMATIC*I*
Cnd exist un singur argument ntr-un functor, atunci se poate folosi cuvntul it pentru acest argument, care
nu se mai declar explicit. Exemplu:
println ( [3,7,2,8,4]. findAll { it < 6} )
- Existena unor metode suplimentare n clase, multe din ele avnd ca argument un functor. Ex:
println ([1,2,3,2,3,3].unique())
// [1,2,3]
new File(.).eachFileRecurse ,println it- Cuvinte cheie noi n Groovy: def, as, in, it
it este folosit ca argument unic implicit n functori
def folosete la declararea unor variabile/metode de un tip neprecizat
as folosete la definirea de nume alternative (alias) pentru clase i metode (ntr-o instruciune import) dar i
pentru atribuirea de tip unui bloc. Exemple:
import static Math.random as rand
import groovy.lang.ExpandoMetaClass as EMC
double value=rand()
def metaClass= new EMC(Integer)
Nume
Metoda
Operanzi
Plus
a.plus(b)
Minus
a.minus(b)
Star
a.multiply(b)
Divide
a.div(b)
Modulo
a.mod(b)
Increment
a.next()
Decrement
a.previous()
Power
a.power(b)
Or
a.or(b)
And
a.and(b)
Xor
a.xor(b)
Complement a.negate()
Subscript
a.getAt(b)
Subscript
a.putAt(b, c)
a.leftShift(b)
- 164 -
INFORMATIC*I*
Prima litera din sir devine litera mare, daca nu era deja
Centrare sir pe o lungime data, cu adaugare de spaii
Aplica un functor pe fiecare linie din sir
Extrage caracterul din pozitia k (alternativa la operator de indexare)
Adauga spaii la stnga pn la lungimea size
Adauga spaii la dreapta pn la lungimea size
Lungime sir (echivalent cu length())
Eliminare spatii din fata fiecarei linii din sir
Creare lista de cuvinte separate prin spaii n sir
Creare lista de cuvinte separate prin caractere c n sir
Creare lista de cuvinte separate prin sirul delim n sir
Exist metode pentru citire linii n toate clasele Reader i InputStream deci i pentru System.in.
Metode din clasele PrintWriter, PrintStream:
void print (Object obj) Scrie valoarea obiectului obj formatat n stil Groovy
void println (Object obj) Scrie valoarea obiectului obj formatat n stil Groovy si trecere la linie nou
Exemple de citire linii de la consol i creare multime de cuvinte distincte extrase din aceste linii:
String text= System.in.getText()
Set words = text.tokenize() as Set
orice caracter
orice cifra zecimala
orice caracter diferit de cifre zecimale
oricte caractere x consecutive (eventual zero)
Groovy foloseste clasele Java pentru expresii regulate, la care adaug civa operatori:
- 165 -
INFORMATIC*I*
~String
=~
==~
Pentru a evita utilizarea de secvente escape n irul sablon, n Groovy o constant ir poate fi delimitat i
de caractere slash (/), pe lng delimitatorii ghilimele () folositi n Java. Exemplu:
n loc de \\d\\w
/\d\w/
Operatorii =~ i ==~ au ca rezultat un obiect de tip java.util.regex.Matcher, iar pentru acest obiect,
considerat ca o colecie de secvente potrivite cu ablonul, putem folosi metodele:
size() ca s aflm numrul de potriviri.
each() ca s aflm fiecare potrivire a ablonului n irul unde se caut.
replaceAll() ca s nlocuim toate secvenele care s-au potrivit cu un alt ir
Exemple:
pattern = ~"(G|g)roovy"
text = 'Groovy is groovy really groovy'
def m= (text =~ ~/Ggr/)
println m.size()? 'gasit' : 'negasit'
assert (text ==~ pattern) == false
pattern
// mai multe potriviri ale sablonului in text
m= (text =~ pattern)
m.each {println it}
for ( i in 0..m.size()-1) println m[i][0]
println ((text =~ /groovy/).replaceAll('nice'))
// negasit
// nu este o potrivire completa text cu
- 166 -
INFORMATIC*I*
Dup contextul n care este definit un functor putem avea urmtoarele situaii:
- 167 -
INFORMATIC*I*
// roman-englez
// afiare
// englez-roman
// afiare cu Map.toString
Exemplu de functor cu dou argumente folosit drept comparator la sortarea unei liste:
fruit = [ "apple", "Orange", "Avocado", "pear", "cherry" ]
fruit.sort { a,b -> a.size()- b.size() }
// odonare dupa lungime siruri
Este posibil s definim i tipul argumentelor, pentru a face codul mai usor de neles. Exemplu:
def word = { String s, int k -> s.split(' ')[k] }
- 168 -
INFORMATIC*I*
// calcul factorial 5!
nf=1
(1..5).each {nf *= it }
1.upto(5) {nf *=it}
// varianta 1
// varianta 2
boolean prim(int n) {
// definire functie de test daca numar prim
este=true
3.upto(n-1){ if(n%it==0) este=false}
return este
}
5.upto(50), print prim(it)? it+' ':''- // utilizare functie prim
- 169 -
INFORMATIC*I*
Deseori apare situaia n care o colecie de obiecte este parcurs pentru a prelucra elementele coleciei sau
pentru a cuta un anumit obiect sau pentru a selecta obiectele care satisfac anumite condiii. Pentru fiecare
element din colecie se execut o actiune (calcule, comparaii). In Java se foloseste de obicei un iterator pe
colecie sau un ciclu cu regsire prin indici asociai elementelor coleciei (numai pentru liste):
for (Iterator it=list.iterator(); it.hasNext();) {
Integer x = (Integer) it.next();
System.out.println ( x%2==0 ? x+" par" : x+" impar");
}
Incepnd cu Java 5 este simplificat iterarea pe o colecie generic:
for (Integer x: list)
System.out.println ( x%2==0 ? x+" par" : x+" impar");
In practic exist anumite prelucrri tipice pe colecii : cutarea primului obiect sau tuturor obiectelor care
satisfac anumite condiii, aplicarea unor operaii (transformri) fiecrui element din colecie, pe loc sau cu
crearea unei noi colecii cu rezultatele operaiilor. Pentru aceste prelucrari s-au definit n Groovy noi metode
aplicabile tuturor coleciilor: find(), findAll(), each(), collect(), sum() .a. Aceste metode primesc ca
argument un functor ce defineste fie criteriul de cutare, fie operaia aplicat elementelor coleciei.
Argumentul implicit din functor desemneaz elementul curent din colecie. Exemple:
int[] a =[1,2,3,4,5,6,7,8,9]
a.each { println " $it ${it%2==0?'even':'odd'}" }
a.findAll { it % 2 } // scrie numerele impare (1 3 5 7 9)
Anumite metode din interfaa Java Collection au fost suprancrcate cu o form care admite ca argument un
functor. Exemplu de eliminare a duplicatelor dintr-o list :
a =[1,2,3,1,1,4,4,2] ; a.removeAll { a.count (it)>1 }
println a.sort() // [ 1 2 3 4 ]
Metoda each() este aplicabil i caracterelor dintr-un ir. Exemplu:
'abcd'.each { print it+' ' } // scrie: a b c d
In clasa File s-au introdus metode de enumerare a fisierelor dintr-un director (eachFile) si de enumerare a
subdirectoarelor (eachDir) cu parametri functor. Exemplu de definire a unui functor recursiv:
def listFiles
// declaratie necesara inaintea apelului recursiv
listFiles = {
println "Dir ${it}"
it.eachDir (listFiles)
// parametru nume de functor
it.eachFile { if(! it.isDirectory()) println " File ${it}" }
}
// utilizare
listFiles ( new File ("d:/groovy"))
Variant de funcie pentru listare recursiv fiiere dintr-un director:
- 170 -
INFORMATIC*I*
Un domeniu este specificat folosind operatorul .. (sau ..<) ntre dou valori ce reprezint limita inferioar
i limita superioar a domeniului de valori. Exemple:
1..10
1..<10
10..1
def a=1..9
// inclusiv limitele 1 si 10
// exclusiv limita superioar 10
// domeniu inversat
// obiect domeniu cu nume
Ca obiecte dintr-o clas implicit (Range), domeniile suport anumite metode: contains, size, each, s.a. Un
domeniu se poate folosi ntr-o instruciune for astfel:
def log = '' "
for (x in 1..9)
log += x
// for ( x in a)
O alt utilizare este ntr-o instructiune switch pentru mprtirea unor valori n (sub)clase:
age = 36
switch(age) {
case 16..20 : insuranceRate = 0.05 ; break
case 21..50 : insuranceRate = 0.06 ; break
case 51..65 : insuranceRate = 0.07 ; break
default: throw new IllegalArgumentException()
}
Metoda each() aplic un functor fiecrei valori din domeniu. Exemplu:
def cls= {x-> println " $x ${x%2==0?'impar':'par'}" }
// definire functor cu nume
(1..9).each (cls)
// utilizare functor cls
- 171 -
INFORMATIC*I*
Limitele unui domeniu pot fi obiecte de tip Date sau din orice clas care conine metoda compareTo()
(implementeaz interfata Comparable) sau care implementeaz metodele next() si previous() (echivalente
operatorilor ++ si ).
O list Groovy este o colecie cu elemente accesibile prin indici, i care se extinde automat prin atribuire
ctre elemente cu indici mai mari ca dimensiunea listei. O list Groovy combin avantajele vectorilor cu
dimensiune fix (selecia de elemente prin indici) cu avantajele obiectelelor din clasa ArrayList (extindere
automat, compactare dup eliminare de elemente, s.a.). Listele au implicit tipul ArrayList si se pot construi
prin includerea elementelor listei ntre paranteze drepte si separate prin virgule. O list vid se noteaz ca [ ].
Exemple de definire si utilizare a unei liste de siruri:
def a = ['zero','unu','doi','trei']
// un obiect de tip List
println a[1]
// scrie unu
println a.size()
// scrie 4 (dimensiune lista)
a << 'patru'
// adaugare la sfarsit de lista
println a
// afisare continut lista
a[8]='opt'
// extindere lista
println a.size()
// scrie 9 (dimensiune lista)
Pentru afiarea elementelor unei liste, cte unul pe o linie, avem mai multe posibiliti:
a.each { println it }
println a.join('\n')
Se poate folosi selecia prin indici i extinderea prin folosirea de indici mai mari ca cei existeni i pentru
obiectele de tip LinkedList. Exemplu:
def a=['a','b','c'] ; def list= new LinkedList (a)
list[3]='d' ; println list
Operatorul de indexare [ ] este echivalent metodelor getAt() (n dreapta) i putAt() (n stnga). Folosind acest
operator se pot insera sau elimina mai multe elemente dintr-o list. Exemple:
myList = ['a','b','c','d','e','f']
myList [0..2] = ['x','y','z']
myList[3..5]=[ ]
// devine *x,y,z,d,e,f+
// devine *x,y,z+
Pentru adugare de elemente la o list se pot folosi i operatorii + sau <<, iar pentru eliminare de
elemente se poate folosi operatorul -. Exemple:
a=[]; a+=*1,2+ ; a<<3<<4
a -= *4+
// sau a-=2
Ca i domeniile, listele se pot folosi n instruciuni for,switch i n expresii regulate. Exemplu:
def isPrime = {n->
def primes=[2,3,5,7,11,13]
if (n in primes ) return 'prim'
for (m in primes) {if (n%m==0) return 'neprim'}
return 'prim'
}
(6..20).each {println it+": "+ isPrime(it)}
- 172 -
INFORMATIC*I*
In Groovy exist o mulime de metode noi aplicabile listelor: sort, reverse, intersect, disjoint, remove,
removeAll, findAll, unique, collect etc. Metoda collect() aplicat unei liste va crea o nou list cu rezultatele
aplicrii unui functor asupra elementelor listei iniiale. Exemplu:
println ((2..20).asList().collect (isPrime))
Orice list se poate folosi ca stiv prin metodele pop() i push() (echivalent cu <<). Exemplu:
def stiva=[]
(1..5).each { stiva.push(it)}
while (stiva.size()>0)
print stiva.pop()+" "
Un exemplu de utilizare a listelor este si sortarea quicksort recursiv:
def quickSort(list) {
if (list.size() < 2) return list
def pivot = list[list.size().intdiv(2)]
def left = list.findAll {item -> item < pivot }
def middle = list.findAll {item -> item == pivot }
def right = list.findAll {item -> item > pivot }
return (quickSort(left) + middle + quickSort(right))
} Dictionare (Asocieri)
IE.11.10.1 Dicionare
O asociere (sau dicionar ="Map") este o list de perechi cheie-valoare, la care cheia poate fi folosit ca
indice. Se poate extinde ca i o list. Un obiect dicionar vid are forma [:]
Sintaxa general a unui obiect dicionar este:
[ key1:value1, key2:value2,..]
Exemple:
def b=['unu':1,'doi':2,'trei':3]
// un obiect asociere
println b['doi']
// scrie 2
println b.doi
// scrie 2
b.patru=4
// extindere dictionar
b['sase']=6
// extindere dictionar
println b
// ["unu":1, "doi":2, "trei":3, "patru":4, "sase":6]
println b.size()
// scrie 5 (dimensiune dictionar)
Pentru chei de tip String se pot omite ghilimelele, dac nu sunt cuvinte cheie ale limbajului i nu conin
caractere speciale. Exemplu:
def b=[unu:1,doi:2,trei:3]
Selectarea valorii asociate unui chei se poate face n mai multe feluri:
- prin indexare nume dicionar cu valoarea cheii: println b['doi']
- prin notatia map.key : println b.doi
- prin metoda get(): println b.get(doi)
- 173 -
INFORMATIC*I*
Metoda get() poate primi dou argumente; dac nu se gseste cheia (primul argument) n dicionar atunci se
adaug la dicionar o pereche cu argumentele metodei get().
Pentru asocierile Groovy se pot folosi metodele din interfaa Map din Java: entrySet, keySet, values,
containsKey, containsValue, .a. plus metode noi ca any() i every().
Pentru a itera pe un dictionar putem folosi un ciclu for ( x in map) sau metoda each().
Alte metode noi pentru asocieri sunt:
- subMap() pentru extragerea perechilor care au anumite chei:
myMap = [a:1, b:2, c:3] ; def abMap = myMap.subMap(['a','b'])
- findAll() caut perechile care satisfac un functor: def found = myMap.find { entry -> entry.value < 2}
- find() caut o pereche care satisface un functor
- collect() adun ntr-o list perechile care satisfac un functor:
def doubled = myMap.collect { entry -> entry.value *= 2}
In cazul unei metode folosirea unui parametru cu nume este de fapt transmiterea unui dicionar, n care cheile
sunt nume de argumente iar valorile asociate sunt valorile argumentelor. Pot lipsi parantezele drepte dar nu i
parantezele rotunde care ncadreaz lista de argumente (care este un dicionar). Exemplu:
def f (Map args) { ['a','b','c'].each {args.get(it,0) }
println f ([b:4, a:2])
println f (a:3, b:1, c:2)
println f ( c:1)
Exemplul urmtor folosete un dictionar pentru afiarea frecvenei cuvintelor distincte dintr-un text:
def text="unu doi trei trei doi trei"
def words = text.tokenize()
def wordFrequency = [:]
words.each { word ->
wordFrequency[word] = wordFrequency.get(word,0) + 1
}
def wordList = wordFrequency.keySet().toList()
wordList.sort { wordFrequency[it] }
def statistic = "\n"
def n=wordList.size()
wordList[0..n-1].each { word ->
statistic += word.padLeft(12) + ': '
statistic += wordFrequency[word] + "\n"
}
println statistic
INFORMATIC*I*
List sort ()
Collection unique()
Collection unique(Closure f)
Exemple de utilizare:
def odd = [1,2,3,4,5].findAll { item ->item % 2 == 1 }
assert [1,2,3,4,5] == [1,[2,3],[[4]],[],5].flatten()
// odd=[1,3,5]
Object pop()
List reverse()
List reverseEach(Closure f)
INFORMATIC*I*
In Java se poate explora n cursul executiei, prin reflecie, structura unui program, astfel c putem afla
numele claselor folosite, ce metode i constructori au, .a. Aceast structur este stabilit la compilare (la
scrierea programului) i poate fi folosit la execuie (de ex. pentru a instania clase obtinue la execuie), dar
nu mai poate fi modificat.
Fiecare clas Java are asociat un obiect de tip Class care conine informaii despre clasa respectiv i care
constituie suportul pentru utilizarea de clase cunoscute numai la execuie i pentru alte operaii de reflecie
sau introspectie. Exemplu de instaniere clas cu nume necunoscut la scrierea programului dar care respect
o interfa (numele poate fi citit la execuie dintr-un fiier de proprieti):
public static Comparator ObjectFactory (String className) {
Comparator obj=null;
try {
Class cls = Class.forName(className);
obj =(Comparator) cls.newInstance();
} catch (Exception e) { e.printStackTrace() ;}
return obj;
}
// utilizare
String className="MyComp";
// se putea citi dintr-un fisier
Comparator comp = Factory.ObjectFactory(className);
// obiect comparator
Collections.sort (list,comp);
In Java legarea (Binding) corpului metodei de un apel al metodei (nestatice) se face la execuie si nu la
compilare, ceea ce permite polimorfismul. Se spune c pentru metodele obiectelor are loc o legare
ntrziat (Late Binding), spre deosebire de legarea timpurie (la compilare) de metodele statice.
Facilitile de metaprogramare din Groovy pot fi privite ca o extindere a refleciei din Java prin adugarea
unei metaclase fiecrei clase sau obiect. Spre deosebire de obiectul Class din Java, metaclasa Groovy
permite adugarea de noi proprieti si metode claselor Groovy (sau Java). In plus se pot intercepta apelurile
de metode existente sau inexistente, pentru adugarea de noi operaii sau pentru nlocuirea operaiilor
existente. In timp ce obiectul Class descrie comportarea unei clase la compilare, metaclasa Groovy descrie
comportarea la execuie a obiectelor unei clase.
Exemplul urmtor pune n evident diferenta Groovy-Java la implementarea polimorfismului. In Groovy se
va scrie string iar n Java programul urmtor va scrie object:
class Foo {
// Groovy
def print (Object o) , println object" def print (String s) , println string" }
Object obj = "string"
new Foo().print(obj)
class Foo {
// Java
void print (Object o) { System.out.println ("object"); }
void print (String s) { System.out.println ("string"); }
public static void main (String a[]){
Object obj = "string" ;
new Foo().print(obj);
}
}
- 176 -
INFORMATIC*I*
INFORMATIC*I*
INFORMATIC*I*
poate folosi metode getMetaClass() sau proprietatea metaClass. Exemplu de acces la metaclasa unei clase
JDK:
def ByteMeta = Byte.metaClass
println ByteMeta.methods.join ('\n')
// java.lang.Byte
// metode din clasa Byte si metode din GroovyObject
INFORMATIC*I*
String.metaClass.trimAll = {
// elimina toate blancurile dintr-un sir
result=new StringBuffer()
delegate.each { if (it !=' ')result.append(it) }
result
}
println 'a b c d'.trimAll()
// abcd
Exemplu de adugare la clasa String a metodei "word(int)" pentru extragerea unui cuvnt cu indice dat
dintr-un ir (ir cu mai multe cuvinte separate prin cte un spaiu):
String.metaClass.word={ delegate.split(" ")[it] }
str="unu doi trei patru "
for (k in 0..3) println str.word(k) // unu,doi,trei,patru
Pentru a folosi noua metod fr paranteze (facilitate util la definirea de limbaje specializate), se va defini
aceast metod ca o metod de acces la o proprietate (iar numele metodei devine nume de proprietate).
Exemplu :
String.metaClass.getTrimAll = { ... } // aceeasi definitie ca mai sus
String.metaClass.getSize = { delegate.size() }
assert s1.trimAll.size == s2.trimAll.size
De asemenea se pot aduga metode statice unor clase Groovy. Exemplu:
Integer.metaClass.static.isEven={val->val%2==0}
- 180 -
INFORMATIC*I*
- 181 -
INFORMATIC*I*
O utilizare posibil a metaprogramrii este delegarea executrii unor metode ctre metode din alte clase, fr
a scrie aceste apeluri explicit n clasa care face delegarea. Versiunea Groovy pentru aceast clas (i pentru
altele cu mai multe apeluri la delegat):
- 182 -
INFORMATIC*I*
class StaffMemberUsingMOP {
private delegate = new Person()
private hasLocalProperty(name) {
metaClass.properties.collect{ it.name }.contains(name)
}
def salary
StaffMemberUsingMOP(Map map) {
map.each{ k, v -> setProperty(k, v) }
}
void setProperty(String name, value) {
if (hasLocalProperty(name)) this.@"$name" = value
else delegate.setProperty(name, value)
}
def getProperty(String name) {
if (hasLocalProperty(name)) return this.@"$name"
else return delegate.getProperty(name)
}
}
Metaprogramarea este folosit n Grails pentru generarea unor metode de cutare (dynamic finders) cu nume
specific clasei domeniu (se caut ntr-un tabel al bazei de date cu acelai nume ca i clasa domeniu). Fie o
clas Person cu cmpurile firstName,lastName, age (care corespund unor coloane din tabelul SQL);
fr
s fi fost declarate anterior se pot apela metode cu nume ca findByFirstName(),
findByFirstNameAndAge() .a. Corpul metodei se genereaz n functie de numele metodei: find() implic o
cutare, update() va efectua o salvare n baza de date.
In aplicaiile Web dar i n alte aplicaii este nevoie ca s atam mai multor funcii (metode) anumite
operaii executate fie nainte, fie dup instruciunile din functia respectiv. Aceste operaii se numesc i
method advice sau cross-cutting concern n AOP, pentru c ele se execut oarecum perpendicular pe fluxul
de apelare a funciilor din program (pe logica programului).
Utilizrile tipice sunt: jurnalizarea (logging) ntr-un fisier a apelurilor de metode, efectuarea de validri
asupra datelor primite de metode, autentificarea dreptului de acces la metode (log-in) sau aplicarea altor
"filtre" la execuia unor metode.
Aceast augmentare a codului se poate face manual (cu riscul
introducerii unor erori) sau automat, prin interceptarea apelurilor si insertia de cod n punctele dorite.
- 183 -
INFORMATIC*I*
Bibliografie
[B01] Bloch J. Effective Java, Editura Addison-Wesley, 2008
[C01] Cooper J: Design Patterns-Java Companion, Editura Addison-Wesley, 1998
[E01] Eckel B. Thinking in Java, Editura Prentice Hall, 2003
[M01] Moraru F., Odubteanu C. Programare orientat pe obiecte, Editura Bren, Buc., 2005
[N01] NetBeans General Java Development Learning Trail https://netbeans.org/kb/trails/java-se.html
[O01] Oracle, The Java Tutorials, http://docs.oracle.com/javase/tutorial/
[R01] Reed P. Developing Applications with Java and UML, Editura Pearson Education, 2002
[T01] Tnas S., .a., Java de la zero la expert, Editura Polirom, 2005
- 184 -