100% au considerat acest document util (1 vot)
168 vizualizări

Curs Java

Încărcat de

MARIUS DANIEL GANEA
Drepturi de autor
© © All Rights Reserved
Formate disponibile
Descărcați ca DOCX, PDF, TXT sau citiți online pe Scribd
100% au considerat acest document util (1 vot)
168 vizualizări

Curs Java

Încărcat de

MARIUS DANIEL GANEA
Drepturi de autor
© © All Rights Reserved
Formate disponibile
Descărcați ca DOCX, PDF, TXT sau citiți online pe Scribd
Sunteți pe pagina 1/ 169

aplicaţiilor Java.

• Forma de evaluare: Media notelor obtinute la Examen si la Prezentarea

Referatelor

Modalitatea de desfasurare

• Motivatie

• Curs

• Laborator

• Platforma de programare

• Documentatia

• Evaluarea

Ce reprezinta “JAVA” ?

• Platforma de lucru

• Limbaj de programare

• Lansare: 1995 - Sun Microsystems

• Oracle: 2010

• 2015: Java 8 u...


• James Gosling

De ce Java?

Limbajul de programare Java

• Usurinta in crearea de aplicatii complexe

• Robustete: administrarea automata a memoriei, GC, fara pointeri

• Complet orientat pe obiecte

• Simplitate

• Portabilitate

• Securitate

• Neutralitate arhitecturala

• Performanta

Java: compilat + interpretat

• Limbaje interpretate

– simplitate, portabilitate

– viteza de executie redusa


• Limbaje compilate

– viteza de executie sporita

– lipsa portabilitatii

Java Virtual
Curs

2. Elemente de bază ale limbajului Java 

2.1 Setul de caractere 


Limbajului Java lucrează în mod nativ folosind standardul Unicode. Unicode înlocuiește
vechiul standard ASCII pentru reprezentarea caracterelor. Astfel devine posibilă  reprezentarea a
mai mult de 256 de caractere pentru ca Unicode utilizează 16 biți în acest scop.  Vechiul standard
ASCII se regăseşte însă ca un subset al setului Unicode.  
Java folosește standardul Unicode atât în timpul rulării aplicaţiilor cât şi în timpul 
compilării acestora. Compilatorul Java acceptă la intrare fişiere sursă care pot conţine orice 
caractere Unicode.  
Detalii: https://www.unicode.org/standard/WhatIsUnicode.html 

2.2 Cuvinte cheie. Cuvinte rezervate 

Cuvintele cheie sunt şiruri de caractere rezervate de limbaj pentru uzul propriu. Nici un 
program nu poate să utilizeze aceste cuvinte pentru un alt scop decât cel definit de limbaj. 
Cuvintele cheie însa pot aparea în şiruri de caractere sau comentarii. Cuvintele îngroşate din 
lista următoare reprezintă cuvintele cheie ale limbajului. Celelalte sunt cuvinte rezervate. Ele  nu
au nici o semnificaţie în Java însa nu pot fi folosite pe post de identificatori. 
abstract boolean break byte 
case cast catch char 
class const continue default 
do double else extends 
final finally float for 
future generic goto if 
implements import inner instanceof 
intinterface long native new 
null operator outer package 
private protected public rest 
return short static super 
switch synchronized this throw 
throws transient try var 
void volatile while byvalue
2.3 Identificatori 

Identificatorii Java sunt şiruri de caractere (litere şi cifre) Unicode, nelimitate, care încep 
obligatoriu cu o literă. Se interzice folosirea unui cuvânt cheie pe post de identificator. 
Exemple: 
contor 
f2 
index 

2.4 Literali 

Un literal este o valoare specificată în fişierul sursă pentru tipurile de date primitive sau şir de 
caractere. În limbajul Java există următoarele tipuri de literali: 
• literali booleeni 
• literali caracter 
• literali întregi 
• literali flotanţi  
• literali şir de caractere 

Literalii booleeni nu pot fi decât true sau false, primul reprezentând valoarea booleană de 
adevăr iar celălalt valoarea booleană de fals. true şi false nu sunt cuvinte rezervate ale 
limbajului Java, cu toate acestea ele nu vor putea fi folosite ca identificatori. 

Un literal de tip caracter poate fi formulat simplu prin încadrarea caracterului dorit între 
apostrofuri. Această tehnică este valabilă numai dacă respectivul caracter se regăseşte pe 
tastatură. Exemplu: 
char c = 'w'; 

Este posibil să formulăm literali de tip caracter specificând codul Unicode al caracterului dorit 
sub forma a 4 cifre hexazecimale precedate de secvenţa \u. Exemplu: 
char c = '\u23a7';
Java suportă de asemenea şi câteva secvenţe speciale, numite secvenţe de escape, pentru 
denotarea unor caractere speciale: 
'\b' caracterul Backspace  
'\t' caracterul TAB orizontal  
'\n' caracterul LineFeed (LF) 
'\f' caracterul FormFeed (FF) 
'\r' caracterul Carriage Return (CR)  

'\"' caracterul ghilimele  


'\'' caracterul apostrof  
'\\' caracterul Backslash  

Literalii întregi pot fi reprezentaţi în baza 10, 16 sau 8. Baza de numeraţie zecimală este 
considerată implicit. Pentru a indica un literal octal acesta se prefixează cu 0 (zero). Literalii 
hexazecimali se prefixează cu 0x sau 0X. Cifrele hexazecimale litere pot fi scrise atât cu litere 
mici cât şi cu majuscule (case insensitive). Exemple: 
• 28 – literal întreg exprimat în baza de numeraţie zecimală 
• 034 – literal întreg exprimat în baza de numeraţie octală 
• 0x1c – literal întreg exprimat în baza de numeraţie hexazecimală 
• 0x1C – literal întreg exprimat în baza de numeraţie hexazecimală 
• 0X1c – literal întreg exprimat în baza de numeraţie hexazecimală 
• 0X1C – literal întreg exprimat în baza de numeraţie hexazecimală 

Implicit un literal întreg este reprezentat pe 32 de biţi. Pentru a indica faptul că este vorba de  un
literal reprezentat pe 64 de biţi (long) se adaugă sufixul L. Poate fi utilizată şi litera mică l,  însa
acest lucru nu este de dorit datorită faptului ca adesea se confundă cu cifra 1. Exemple: 1234L
sau 5678l 

Literalii flotanţi (sau virgulă mobilă) reprezintă numere reale. Pentru ca o expresie să fie 
interpretată ca literal flotant ea trebuie să conţină unul din următoarele: 
• punct zecimal. Exemplu: 3.1415926 
• litera E sau e. Exemplu: 4.1E+10 
• sufixul F sau f, indicând o reprezentare pe 32 de biţi. Exemplu: 1.56f •
sufixul D sau d, indicând o reprezentare pe 64 de biţi. Exemplu: 1234d
Dacă nu este specificat nici un sufix, valoarea este implicit reprezentată pe 64 de biţi. 

Un literal şir de caractere constă într-o secvenţă de caractere încadrată de ghilimele. Secvenţa 
de caractere poate conţine caractere obişnuite sau secvenţe de escape ca cele definite la literalii 
caracter. Exemplu: 
String s = ”Secventa de caractere”; 

2.5 Separatori 
Separatorii sunt caractere folosite pentru a delimita diversele elemente (unităţi lexicale) ale 
limbajului. Separatorii participă în acelaşi timp şi la construcţia sintaxei limbajului. Ei nu au un 
rol identic cu spaţiile deşi, ca şi acestea, separă unităţi lexicale diferite. În Java separatorii sunt 
următorii: 
( ) { } [ ] ; , . 

Exemple: 
v[i] 
functie(x) 

2.6 Comentarii 

Un comentariu este o secvenţă de caractere existentă în fişierul sursă care serveşte numai la 
explicarea sau documentarea programului şi nu afectează în nici un fel funcţionarea acestuia.  În
Java există trei feluri de comentarii: 
• Comentarii care se întind pe mai multe linii. Acestea sunt încadrate de secvenţele /* şi  */ 
• Comentarii pe mai multe linii care vor fi prelucrate de către generatorul automat de 
documentaţie. Acestea sunt încadrate de secvenţele /** şi */ 
• Comentarii pe o singură linie. Acestea încep cu // şi conţin caracterele care urmează  acestei
secvenţe până la sfârşitul liniei.
În Java, nu putem să scriem comentarii în interiorul altor comentarii. De asemenea, nu putem 
introduce comentarii în interiorul literalilor caracter sau şir de caractere. Secvenţele /* şi */
pot să apară pe o linie după secvenţa // dar îşi pierd semnificaţia. La fel se întâmplă cu
secvenţa  // în comentarii care încep cu /* sau /**. Exemple: 

/* Comentariu 
 care se intinde  
 pe mai multe randuri */ 

/** Comentariu ce va fi preluat 


 de generatorul de documentatie */ 

// Comentariu pe un singur rand 

2.7 Operatori 
Operatorii reprezintă simboluri grafice pentru operaţiile elementare definite de limbajul Java. 
Totodată operatorii joacă şi rol de separatori. În tabelul următor sunt prezentaţi operatorii 
elementari ai limbajului Java în ordinea descrescătoare a precedenţei. 
Unari  ++ -- + - ! ~ ()

Aritmetici  * / % 
+ -

Deplasare  << >> >>>

Comparare < <= > >= 


== !=

Pe bit  & ^ |

Logici  && ||

Condiţionali  ?:

Atribuire  = op=

În Java, spre deosebire de alte limbaje de programare, ordinea de evaluare a operanzilor într-o 
expresie este fixă. Astfel întotdeauna toţi operanzii sunt evaluaţi de la stânga la dreapta.
2.7.1 Operatorii unari 

Operatorii unari lucrează asupra unui singur operand. In continuare sunt descrişi operatorii  unari
elementari ai limbajului Java. 

Operatorii de incrementare şi decrementare: ++ respectiv -- 


Modifică valoarea unei expresii adăugandu-i sau scăzându-i valoarea 1. Spre exemplu dacă x
are valoarea 10, atunci ++x va face ca x să aiba valoarea 11, in vreme ce --x va face ca x sa 
aiba valoarea 9. 

Operatorii ++ şi -- pot fi plasaţi atât înaintea expresiei (ca în exemplul precedent) cat şi după 
aceasta. Pentru a înţelege efectul plasării într-un fel sau altul al acestor operatori să considerăm 
exemplele următoare: 

(1) y = x++ 
(2) y = ++x 

Presupunând ca x are iniţial valoarea 10, valoarea lui y va fi în primul caz 10, iar în al doilea 
11 (vezi ordinea evaluarii operanzilor). În ambele cazuri însă, valoarea lui x va deveni 11. 
Operatorii + şi – 
Trebuie facută distincţie între aceşti operatori şi operatorii aritmetici de adunare, respectiv 
scădere. Operatorul + nu are nici un efect asupra valorii operandului său, în vreme ce operatorul 
– va schimba semnul acestuia. 

Operatorul de complementare pe bit ~ 


Acest operator acţionează la nivelul reprezentării binare a operandului său transformând cifrele 
1 ale reprezentarii sale în cifre 0 şi invers. Astfel dacă am aplica acest operator asupra unui 
operand reprezentat pe un octet cu valoarea 00110011 rezultatul obţinut ar fi 11001100. 

Operatorul boolean de complementare ! 


Complementează valoarea unei expresii booleene. Astfel !true va fi da false iar !false
va  da true.
2.7.2 Operatorii aritmetici 

Operatorii de înmulţire şi împarţire * şi / 


Aşa cum arată chiar numele lor efectuează operaţiile de înmulţire, respectiv împarţire între doi 
operanzi. Folosirea lor presupune înţelegerea limitărilor impuse de formatul de reprezentare al 
operanzilor. De pildă dacă vom înmulţi doi operanzi întregi rezultatul se va calcula conform 
regulilor aritmeticii numerelor întregi, însă având în acelaşi timp grija ca rezultatul obţinut să  nu
depăşească formatul de reprezentare. Dacă acest lucru se întâmplă atunci valoarea obţinută 
pentru rezultat nu va avea semnificaţia corectă. 

Operatorul modulo % 
Semnificaţia rezultatului unei operaţii modulo este aceea de rest al unei împărţiri întregi. Astfel 
dacă, de exemplu, vom efectua împărţirea întreagă a lui 7 la 4 vom obţine câtul 1 şi restul 3. 
Acest lucru îl scriem in Java sub forma expresiei 7 % 4, iar valoarea acesteia va fi 3. 

Deşi pare natural să aplicăm acest operator numai asupra unor operanzi întregi, în Java este 
posibil să îl folosim şi cu operanzi numere reale.  

Regula după care se obţine rezultatul operaţiei modulo în Java este următoarea:  1. Se micşorează
valoarea absolută a primului operand (din stânga) cu valoarea absolută a  celui de-al doilea
operand (din dreapta) 
2. Se repeta pasul 1 până când valoarea absolută a rezultatului obţinut la pasul 1 este este  mai
mică decât valoarea absolută a celui de-al doilea operand (cel din dreapta). Această  ultimă
valoare este rezultatul operaţiei modulo. 
Pentru exemplificare considerăm următoarele cazuri: 
(1) 17 % 5 
17 – 5 = 12 
12 – 5 = 7 
7 – 5 = 2 
2 < 5 STOP. Rezulta ca 17 % 5 = 2
(2) 7.6 % 2.9 
7.6 – 2.9 = 4.7 
4.7 – 2.9 = 1.8 
1.8 < 2.9 STOP. Rezulta ca 7.6 % 2.9 = 1.8 

(3) -5 % 2 
Ca sa micsoram valoarea absoluta cu 2 trebuie sa  
efectuam adunari 
-5 + 2 = -3 
-3 + 2 = -1 
Valoarea absoluta a lui -1 este 1, 
iar 1 < 2. STOP. Rezulta ca -5 % 2 = -1 

(4) -5 % -2 
-5 + 2 = -3 
-3 + 2 = -1 
Valoarea absoluta a lui -1 este 1, 
iar 1 < 2. STOP. Rezulta ca -5 % -2 = -1 

Operatorii de adunare şi scadere + şi – 


Efectuează adunarea, respectiv scăderea operanzilor. Se aplică operanzilor de tip numeric 
indiferent de tip. De asemenea folosirea acestor operatori presupune înţelegerea limitărilor 
impuse de formatul de reprezentare al operanzilor, altfel rezultatul obţinut este unul fară o 
semnificaţie corectă. 

2.7.3 Operatorii de deplasare 

Deplasarea este o operaţie care acţionează la nivelul reprezentării unui număr. Pe scurt 
reprezentarea binară operandului număr este deplasata spre stânga sau spre dreapta cu un  anumit
numar de poziţii (ranguri binare). Operatorii de deplasare se pot aplica numai  operanzilor de tip
întreg. Mecanismul de bază al operaţiilor de deplasare este ilustrat in figurile  următoare. 

Valoare operand: 192


Reprezentare   00000000  00000000  00000000  11000000
binară 

Deplasare spre  stânga 0  00000000  00000000  00000001  1000000?


cu 1  
poziţie

Deplasare spre  dreapta ?0000000  00000000  00000000  01100000  0


cu 1  
poziţie

Deplasare spre  stânga 0000  00000000  00000000  00001100  0000????


cu 4  
poziţii

Valoare operand: –192 


Reprezentare   11111111  11111111  11111111  01000000
binară 

Deplasare spre  stânga cu 1   1  11111111  11111111  11111110  1000000?


poziţie

Deplasare spre  dreapta cu ?1111111  11111111  11111111  10100000  0


1  
poziţie

Urmărind exemplele de mai sus se ridică două întrebări: 


(1) Pentru că rezultatul operaţiei de deplasare va avea acelaşi număr de biţi la nivelul 
reprezentării, ce se întîmplă cu biţii care „părăsesc” formatul de reprezentare (valorile  0 sau
1 îngroşate) ? 
(2) Ce valori vor avea biţii care sunt introduşi in reprezentarea rezultatului (cei marcaţi prin 
semne de întrebare) ? 
Prima întrebare are un răspuns foarte simplu, biţii care „părăsesc” formatul de reprezentare sunt 
ignoraţi, deci se pierd. Cea de-a doua însă presupune discutarea mai multor situaţii. Astfel, în 
cazul operatorilor de deplasare spre stânga << şi deplasare spre dreapta fără semn >>>, biţii cei 
noi vor avea valoarea 0. În cazul deplasării spre dreapta cu semn >>, biţii cei noi vor lua 
valoarea avută de cel mai semnificativ bit din reprezentare, înainte de operaţia de deplasare. 
Revenind la exemplele precedente, în cazul deplasării spre dreapta a numarului 192 valoarea 
bitului nou introdus va fi 0, în vreme ce aceeaşi operaţie de deplasare efectuată asupra lui –192 
va determina introducerea unui bit 1 în formatul de reprezentare.
2.7.4 Operatorii de comparare 
Aplicarea operatorilor de comparare (<,<=,>,>=,== şi !=) duce la obţinerea unui rezultat 
boolean; cu alte cuvinte expresia care conţine aceşti operatori poate avea valoarea true sau 
false. Semnificaţia acestor operatori este: 
• < – mai mic 
• <= – mai mic sau egal 
• > – mai mare 
• >= – mai mare sau egal 
• == – egal 
• != – diferit 
Există trei tipuri de comparare. Primul tip este compararea ordinală cu ajutorul căreia testăm 
relaţia între valorilor operanzilor numerici. Al doilea tip este comparare obiectuală utilizat 
atunci când dorim să testăm, de pildă, dacă un obiect este de un anumit tip. Al treilea tip îl 
reprezintă testul de egalitate cu ajutorul căruia testăm identitatea a două valori. Testul de 
egalitate poate fi aplicat şi asupra operanzilor ne-numerici. 
Exemple: 
int p = 9; 
int q = 65; 
int r = -12; 
float f = 9.0; 
char c = 'A'; 
următoarele operaţii de comparare vor avea rezultatul true. 
p < q 
f < q 
f <= c 
c > r 
c >= q 

2.7.5 Operatori pe bit 


Operatorii pe bit &, ^ şi | se utilizează împreună cu operanzi întregi. Aceşti operatori oferă 
posibilitatea de a efectua operaţiile AND, XOR respectiv OR între reprezentările binare ale 
operanzilor. Exemple: 
00110011  
11110000 
AND –––––––– 
00110000 
00110011  
11110000
XOR –––––––– 
11000011 
00110011  
11110000 
OR –––––––– 
11110011 

2.7.6 Operatori logici 


Operatorii logici sau booleeni, && şi ||, se utilizează împreună cu operanzi booleeni efectuând 
operaţiile logice AND respectiv OR între aceştia. Nu trebuie confundaţi cu operatorii pe bit  care
implementează aceleaşi operaţii logice pentru că tipul operanzilor este diferit. 

2.7.7 Operatorul condiţional 


Operatorul condiţional ?:, cunoscut şi sub numele de operator ternar, oferă un mijloc simplu şi 
eficient de a codifica in Java o condiţie sub forma unei singure expresii. Forma acesteia este: e1
? e2 : e3 
Mai întâi este evaluată expresia e1, care trebuie să fie o expresie booleană. Dacă aceasta este 
true, rezultatul expresiei de mai sus este dat de valoarea expresiei e2. În caz contrar expresia 
va avea ca rezultat valoarea lui e3. Exemplu: 
a = x ? b : c; 

2.7.8 Operatorul de atribuire 


Operatorul de atribuire asignează o valoare unei variabile. Operatorul simplu de atribuire este  =,
însă în Java există si operatorii compuşi de tip „Calculează şi atribuie” de forma op=. Pentru 
două expresii compatibile x şi y, expresia x op= y este echivalentă cu x = x op y. 
Toţi operatorii discutaţi până acum produceau o valoare ca urmare a aplicarii lor asupra unor 
operanzi. În Java atribuirea este de asemenea considerată operator deoarece are o valoare 
rezultat. Astfel în Java instrucţiunea a=b=c=0; este perfect valabilă. Ea este executată de la 
dreapta la stînga începând deci prin a asigna valoarea 0 lui c. După executarea acesteia expresia 
c = 0 ia o valoare (care este evident 0 în acest exemplu) ce va fi atribuită mai departe lui b.
2.8 Variabile. Declaraţii de variabile 

2.8.1 Declararea unei variabile 


O variabilă identifică o zonă de memorie care poate stoca o valoare de un anumit tip. Chiar 
dacă se numesc variabile există cazuri în care nu este permisă modificarea valorii acestora, 
situaţie în care avem de-a face cu aşa-numitele variabile finale. Orice variabilă trebuie să fie 
declarată pentru a putea fi folosită. Această declaraţie trebuie să specifice tipul valorilor care  pot
fi stocate în zona de memorie rezervată variabilei şi de asemenea un nume pentru variabila 
declarată. În forma cea mai simplă, declaraţia unei variabile arată în felul următor: 
Tip NumeVariabila [, NumeVariabila] ; 
Numele variabilei declarate este un identificator Java. Tipul unei variabile poate fi fie unul 
dintre tipurile primitive definite de limbajul Java fie o referinţă. În Java este definită foarte exact 
modalitatea de reprezentare a acestor tipuri primitive în memorie realizându-se astfel 
independenţa de platforma hardware şi software pe care se lucrează. De asemenea sunt definite 
valori implicite pentru fiecare tip de dată, în cazul în care aceastea nu au fost iniţializate de  către
utilizator, cunoscându-se astfel întotdeauna valoarea cu care o variabilă intră în calcul. În  Java
putem iniţializa variabilele chiar în momentul declarării acestora: 
Tip NumeVariabila = ValoareInitiala ; 
Valoarea iniţială trebuie să fie de acelaşi tip cu tipul variabilei sau să poată fi convertită într-o 
valoare de acest tip. 

2.8.2 Tipuri primitive 


O varibilă de un tip primitiv conţine o singură valoare cu dimensiune şi format corespunzător 
tipului său de date: numeric, caracter sau boolean. De exemplu, o valoare întreagă este 
reprezentată pe 32 de biţi în cod complement faţă de 2. 

În tabelul următor sunt prezentate toate tipurile primitive de date, suportate de Java, incluzând 
formatul de reprezentare al acestora. 

Cuvânt  cheie 
Descriere  Format de reprezentare

(numere întregi)

byte  întreg reprezentat pe octet  8-biţi, complement faţă de 2


short  întreg scurt  16-biţi, complement faţă de 2

int  întreg  32-biţi, complement faţă de 2


long  întreg lung  64-biţi, complement faţă de 2

(numere reale)
float  virgulă mobilă – simplă 32-biţi, IEEE 754
precizie 
double  virgulă mobilă – dublă precizie  64-biţi, IEEE 754

(alte tipuri)
char  caracter  16-biţi, cod Unicode
boolean  boolean (logic)  true sau false

Valorile implicite pe care le vor avea variabilele neiniţializate sunt: 


Tip   Valoare   Interval de valori
primitiv  implicită

0 -128 
byte 
... 
+127
short  0 -32767 
... 
+32768
int  0 -2147483648 
... 
2147483647
long  0 -9223372036854775808 
... 
9223372036854775807
float  0  conform standard IEEE 754
double  0  conform standard IEEE 754
char  \u0000  –
boolean  false  {true, false}

Observaţii: 
1. Standardul IEEE 754 defineşte pe lângă formatul de reprezentare în virgulă mobilă  simplă şi
dublă precizie şi o serie de valori speciale. Prima dintre acestea este NaN (Not  A Number),
valoare care se obţine atunci când efectuăm o operaţie a cărei rezultat nu  este definit, de
exemplu 0.0/0.0. De asemenea sunt definite două valori pe care le putem  folosi pe post de
infinit pozitiv şi negativ. Aceste valori sunt prezente în limbajul Java 
sub formă de constante predefinite cu numele NaN, POSITIVE_INFINITY, respectiv 
NEGATIVE_INFINITY. 
2. Pentru tipurile de date întregi, întregi lungi şi pentru tipurile flotante există definite câte  două
constante care reprezintă valorile minime şi maxime care se pot reprezenta în  tipurile
respective. Aceste două constante se numesc în mod uniform MIN_VALUE şi  MAX_VALUE. 
Exemple de declaraţii de variabile: 
boolean gata; 
boolean corect; 
char caracterCitit; 
byte nrCulori; 
short i=0, j, varstă; 
int salariu = 10000000; 
float raport; 
double nrMolecule; 

2.8.3 Domeniul de existenţă al variabilelor 


Domeniul de existenţă reprezintă o secţiune de program în care o variabilă poate fi referită  direct
numai prin numele său. Totodată, domeniul de existenţă determină modul în care sistemul  alocă
sau şterge zonele de memorie rezervate variabilelor precum şi regulile de vizibilitate ale 
acestora. Astfel va fi precizat dacă şi cum o variabilă poate fi utilizată în afara domeniului de 
existenţă.
2.9 Expresii, instrucţiuni, blocuri 
Variabilele şi operatorii descrişi în subcapitolele anterioare reprezintă elementele de bază cu 
care ne elaborăm programele. Astfel, combinăm literali, variabile şi operatori pentru a forma 
expresii (fragmente de cod care efectuează calcule şi returnează valori), anumite expresii se pot 
constitui ca instrucţiuni (unităţi de execuţie complete), iar prin gruparea mai multor instrucţiuni 
obţinem blocuri de instrucţiuni sau de cod. 

2.9.1 Expresii 
Expresiile sunt folosite pentru a calcula şi atribui valori variabilelor şi pentru a facilita controlul 
execuţiei programului. Astfel rezultă dublul rol pe care îl are de îndeplinit o expresie: efectuarea 
calculelor indicate de elementele expresiei şi returnarea unei valori ca rezultat al calculelor 
efectuate. Tipul valorii returnate de o expresie depinde de elementele utilizate în cadrul 
expresiei.  

Limbajul de programare Java permite construirea de expresii compuse atâta vreme cât tipurile 
de date necesare unei părţi a expresiei se potrivesc cu tipurile de date ale restului acesteia. 
Exemplu:  
x * y * z 
În acest exemplu particular ordinea în care expresia este evaluată nu este importantă pentru că 
operaţia de înmulţire este una asociativă. Acest lucru nu este valabil însă pentru orice expresie. 
Rezultatul poate diferi mult în funcţie de ordinea de evaluare a expresiei. Această ordine poate  fi
controlată riguros prin utilizarea parantezelor rotunde. Exemplu: 
x + y / 100 
(x + y) / 100 
Dacă nu se indică explicit ordinea de evaluare prin utilizarea parantezelor rotunde, atunci 
aceasta se determină ţinând cont de precedenţa operatorilor utilizaţi; cei cu precedenţa cea mai 
mare fiind evaluaţi primii. De exemplu cele două expresii de mai jos sunt echivalente: 
x + y / 100 
x + (y / 100) 

Când în aceeaşi expresie avem de-a face cu operatori cu aceeaşi precedenţă atunci regula de 
evaluare este: 
• Toţi operatorii binari, cu excepţia atribuirii, se evaluează în ordine de la stânga la  dreapta.
• Operatorii de atribuire sunt evaluaţi în ordine de la dreapta la stânga 

2.9.2 Instrucţiuni 
Instrucţiunile formează unităţi de execuţie complete. Următoarele tipuri de expresii pot fi 
transformate în instrucţiuni prin adăugarea la sfârşitul expresiei a separatorului ;. • atribuirea  
• incrementarea ++ sau decrementarea -- 
• apelurile de subprograme  
• crearea de obiecte  
Exemple: 
valoare = 1234.56; 
valoare++; 
System.out.println(valoare); 

În plus, există instrucţiuni de declarare cu ajutorul cărora se efectuează declaraţiile de variabile. 


Exemplu: 
double valoare = 1234.56; 

În sfârşit, există şi instrucţiunile pentru controlul execuţiei programului care vor determina 
ordinea în care vor fi executate instrucţiunile programului. Aici intră instrucţiunile de ramificare 
şi instrucţiunile de ciclare (sau de buclare). 

2.9.3 Blocuri 
Un bloc reprezintă un grup de zero sau mai multe instrucţiuni cuprinse între acolade. Un bloc 
poate fi utilizat oriunde este permisă utilizarea unei instrucţiuni simple. Exemplu: 

 valoare = 1234.56; 
 valoare++; 
 System.out.println(valoare); 
}
2.10 Instrucţiuni pentru controlul execuţiei programului 
Fără a efectua nici un control al execuţiei programului, instrucţiunile acestuia ar fi executate 
secvenţial în ordinea în care ele apar de sus în jos şi de la dreapta la stânga. Instrucţiunile pentru  
controlul execuţiei programului pot schimba ordinea în care se execută instrucţiunile funcţie de 
anumite condiţii specificate. Limbajul Java furnizează astfel de instrucţiuni, rezumate în tabelul 
următor: 
Tipul instrucţiunii  Cuvinte cheie

de ciclare  while, do-while, for

de ramificare  if-else, switch-case

de tratare a try-catch-finally, throw


excepţiilor 

de salt  break, continue, label:, return

2.10.1 Instrucţiunea while 

Are ca efect executarea repetată a unui bloc cât timp o condiţie rămâne adevarată. Sintaxa 
generală a instrucţiunii while este: 
while (expresieBooleana) { 
 instructiuni 

Execuţia acestei instrucţiuni începe cu evaluarea expresiei booleene. Dacă această valoare este 
true, atunci se execută instrucţiunile din blocul asociat lui while care constituie corpul 
ciclului. Procesul se repetă până când expresieBooleana va returna valoarea false. 
Instrucţiunea while face parte din categoria instrucţiunilor de ciclare cu număr necunoscut de 
paşi. De asemenea ţinând cont de momentul în care se face evaluarea expresiei booleene se 
spune despre instrucţiunea while că este instrucţiune de ciclare cu test iniţial. Datorită acestui 
lucru este posibil ca blocul de instrucţiuni care compune corpul ciclului să nu se execute 
niciodată, dacă la primul pas expresia booleană returnează valoarea false. 

Exemplu: 
int i = 0; 
while (i++ < 5) 
 System.out.println("Text");
2.10.2 Instrucţiunea do-while 

Sintaxa generală a instrucţiunii do-while este: 


do { 
 instructiuni 
} while (expresieBooleana); 
Spre deosebire de instrucţiunea while care începea cu evaluarea expresiei booleene, 
instrucţiunea do-while trece o dată prin corpul ciclului după care evaluează expresia 
booleană. Dacă expresia booleană returnează valoarea true ciclul se reia. Procesul continuă 
până când valoarea returnată de expresia booleană devine false. Şi instrucţiunea do-while 
face parte din categoria instrucţiunilor de ciclare cu număr necunoscut de paşi, însă spre 
deosebire de while, aceasta este în acelaşi timp o instrucţiune de ciclare cu test final. Acest 
lucru face ca blocul de instrucţiuni care compune corpul ciclului să fie executat cel puţin o dată.
Exemplu: 
int i = 0; 
do { 
 System.out.println("Text"); 
 i++; 
} while (i < 5) 

2.10.3 Instrucţiunea for 

Instrucţiunea for ne oferă un mijloc de a efectua iteraţii peste un interval de valori. Forma 
generală a instrucţiunii for este: 
for (instructiune; conditie; expresie) { 
 instructiuni 

Execuţia instrucţiunii for se bazează pe următoarele reguli: 


• se incepe prin executarea instrucţiunii instructiune. Aceasta este utilizată de obicei  pentru
a iniţializa variabilele contor pe baza cărora se face iterarea. 
• conditie trebuie să fie o expresie booleană şi este tratată exact ca  expresieBooleana
din instrucţiunea while, adică se va executa repetat corpul  ciclului cât timp conditie
rămâne true. Şi aici, la fel ca la instrucţiunea while este  posibil ca să nu se execute
niciodată corpul ciclului dacă la începutul ciclului conditie 
este deja false.
• expresie este executată imediat după executarea corpului ciclului dar înainte de  testarea din
nou a expresiei conditie. De obicei expresie se foloseşte pentru a  incrementa
variabilele contor. 

Exemplu: 
int i; 
for (i = 0; i < 10; i++) { 
 System.out.println("Text"); 

Observaţii: 
1. Oricare dintre instructiune, conditie sau expresie poate fi omisă. Dacă partea  care
lipseşte este conditie atunci se obţine un ciclu infinit. Exemplu: 
for ( ; ; ) { 
 ... 

2. instructiune şi expresie pot conţine în loc de o expresie, secvenţe de expresii. 
Separarea acestor expresii din secvenţă se face cu ajutorul separatorului virgulă şi nu cu 
punct-şi-virgulă deja folosit in sintaxa lui for pentru a delimita cele trei părţi ale sale 
instructiune, conditie respectiv expresie. 
Exemplu: 
int j,k; 
for (j=3, k=6; j+k < 20; j++, k+=2) { 
 ... 

3. Instrucţiunea for este o instrucţiune de ciclare cu număr cunoscut de paşi şi în acelaşi  timp
instrucţiune de ciclare cu test iniţial. 

2.10.4 Instrucţiunea if/else 

Instrucţiunea if/else permite execuţia selectivă a secvenţelor de program Java, în funcţie de 
anumite criterii. Există două forme pentru instrucţiunea if/else. Prima dintre acestea are 
următoarea sintaxă: 
if (expresieBooleana) { 
 instructiuni 
}
Secvenţa instructiuni se va executa dacă expresieBooleana are valoarea true. 

Cea de-a doua formă a instrucţiunii if/else permite executarea unei secvenţe de instrucţiuni 
şi în cazul în care expresia booleană are valoarea false. Sintaxa acesteia este: 
if (expresieBooleana) { 
 instructiuni_1 
} else { 
 instructiuni_2 

Dacă expresieBooleana are valoarea true se va executa secvenţa instructiuni_1. 


Dacă dimpotrivă expresieBooleana ia valoarea false se va executa secvenţa 
instrucţiuni_2. 
Exemplu: 
if (raspuns == OK) { 
 System.out.println("Raspunsul a fost OK"); 
} else { 
 System.out.println("Raspunsul nu a fost OK"); 

2.10.5 Instrucţiunea switch 

Atunci când avem nevoie să efectuăm o alegere şi aceasta se poate face în funcţie de o valoare 
întreagă rezolvarea o constituie instrucţiunea switch. Sintaxa instrucţiunii este: 
switch(expresieIntreaga) { 
case valoareIntreaga1: 
 instructiuni_1 
 break; 
case valoareIntreaga2: 
 instructiuni_2 
 break; 
... 
case valoareIntreagaK: 
 instructiuni_K 
 break; 
default: 
 instructiuni 
 break; 

Executarea instrucţiunii switch începe prin a evalua expresieIntreaga. Se identifica apoi 


case-ul a cărui eticheta valoareIntreaga este egală cu valoarea lui expresieIntreaga
şi se execută secvenţa de instrucţiuni asociată acestuia.
Observaţii: 
1. Execuţia secvenţei de instrucţiuni se opreşte la prima instrucţiune break întâlnită. 2.
Eticheta default are asociată secvenţa de instrucţiuni care se execută atunci când  niciunul
dintre case-uri nu a fost selectat. 
3. Instrucţiunea switch este echivalentă cu o succesiune de instrucţiuni if/else.
Exemplu: 
switch(zi) { 
case 1: 
 System.out.println("Luni"); 
 break; 
case 2: 
 System.out.println("Marti"); 
 break; 
case 3: 
 System.out.println("Miercuri"); 
 break; 
case 4: 
 System.out.println("Joi"); 
 break; 
case 5: 
 System.out.println("Vineri"); 
 break; 
case 6: 
 System.out.println("Sambata"); 
 break; 
case 7: 
 System.out.println("Duminica"); 
 break; 

2.10.6 Instrucţiunile break, continue 

Instrucţiunea break are două forme: etichetată şi neetichetată. Forma neetichetată a 


instrucţiunii break face ca execuţia celei mai din interior construcţie switch, for, while
sau  do-while, care conţine break-ul, să se incheie. 

Exemplu: Instrucţiunea break determină incheierea execuţiei ciclului for 2. 


for ( ; ; i++) { // ciclu for 1 
 for ( ; ; j++) { // ciclu for 2 
 if (gata) { 
 .... 
 break; 
 } 
 } 
}
Forma etichetată a instrucţiunii break face ca să se încheie execuţia instrucţiunii identificată 
printr-o eticheta specificată ca argument al lui break. O etichetă constă într-un identificator 
plasat înaintea unei instrucţiuni separată de aceasta prin caracterul ´:´. 
eticheta: instructiuneJava; 

Exemplu: Instrucţiunea break etichetată determină incheierea execuţiei ciclului for 1. 
aici: 
for ( ; ; i++) { // ciclu for 1 
 for ( ; ; j++) { // ciclu for 2 
 if (gata) { 
 .... 
 break aici; 
 } 
 } 

Instrucţiunea continue are ca efect trecerea peste iteraţia curentă a unei instrucţiuni for, 
while sau do-while. continue are de asemenea două forme: etichetată respectiv 
neetichetată. Forma neetichetată are efect asupra celei mai din interior instrucţiuni de ciclare 
care o conţine, în vreme ce forma etichetată va acţiona asupra instrucţiunii de ciclare identificată 
prin eticheta specificată ca argument al instrucţiunii continue. 

Exemplu: 
for (int i = 0; i < max; i++) { 
 ... 
 if (nuEsteZero) 
 continue; 

 // contorizare 0-uri 


 numarZerouri++; 
}

2. Clase şi obiecte în limbajul Java 

Există mai multe paradigme de programare:  


• programare procedurală 
• programare orientată obiect 
• programare logică 
• programare bazată pe reguli 
• programare cu constrângeri 
Prin utilizarea procedurilor putem atinge un nivel de abstractizare care se referă numai la 
descrierea operaţiilor şi care nu oferă o soluţie atunci când se pune problema abstractizării 
obiectelor. Acest fapt constituie o piedică majoră pentru multe aplicaţii unde complexitatea 
datelor care se manipulează contribuie substanţial la creşterea complexităţii generale a 
problemei.  
Programarea orientată obiect este o metodă de implementare în care programele sunt organizate 
sub forma unor colecţii de obiecte care cooperează între ele, fiecare dintre acestea reprezentând
o  instanţă a unei anumite clase de obiecte, clase care la randul lor sunt membrii ale unei ierarhii
de  clase unite printr-o relaţie de moştenire.  
Cadrul conceptual pentru programarea orientată obiect îl reprezintă modelul obiect. Modelul 
obiect dispune de 4 elemente principale:  
• Abstractizarea 
• Încapsularea 
• Modularitatea 
• Ierarhia 

şi de asemenea de 3 elemente secundare:  


• Constrângerile de tip 
• Concurenţa 
• Persistenţa
 

Abstractizarea denotă caracteristicile esenţiale ale unui obiect, care îl disting de alte feluri de 
obiecte, furnizând astfel graniţe conceptuale clar definite. Aspectul exterior al unui obiect 
defineşte un contract, de care pot depinde alte obiecte. Acesta este asigurat de partea internă a 
obiectului şi adesea prin colaborare cu alte obiecte. Contractul cuprinde responsabilităţile 
obiectului.  

Încapsularea este procesul de compartimentare al elementelor unei abstractizări prin care se 
constituie structura şi comportamentul său. Încapsularea serveşte la separarea interfeţei 
contractuale de implementarea unei abstractizări. Nici o parte a unui sistem complex nu trebuie 
să depindă de detaliile interne ale vreunei alte părţi. În vreme ce abstractizarea ne ajută să ne 
gândim la ceea ce dorim să facem, încapsularea facilitează modificarea programelor fără efort şi 
fără riscuri.  
Modularitatea reprezintă proprietatea unui sistem de a se descompune într-o mulţime de
module  coerentă (prin gruparea logică a abstractizărilor) şi în acelaşi timp cât mai puţin legate
unele de  altele (prin minimizarea dependenţelor între module).  

Ierarhia reprezintă o ordonare a abstractizărilor.  

Constrângerile de tip definesc în ce măsură obiectele de diferite tipuri sunt

interschimbabile.  Concurenţa este proprietatea prin care se deosebeşte un obiect activ de

unul inactiv.  

Persistenţa este proprietatea unui obiect de a transcende (a depaşi limitele) în timp (de exemplu 
un obiect continuă să existe după ce creatorul său şi-a încetat existenţa) şi/sau spaţiu (locaţia 
obiectului se mută din spaţiul de adrese în care a fost creat).  

Unificarea conceptelor de concurenţă şi obiect a dus la apariţia limbajelor de programare 


orientate obiect concurente. Într-o manieră asemănătoare persistenţa şi modelul obiect a dus la 
apariţia bazelor de date orientate obiect. 
 

Obiecte şi clase  
Un obiect are o stare, un comportament şi o identitate.  

Starea unui obiect cuprinde toate proprietăţile unui obiect şi valorile curente ale fiecăreia dintre 
aceste proprietăţi. De exemplu un automat de bomboane are proprietatea de a accepta fise de o 
anumită valoare. Această proprietate are un caracter static. Numărul de fise pe care le acceptă la 
un moment de timp descrie tot starea obiectului însă are un caracter dinamic. Toate proprietăţile 
au valori însă acestea pot fi simple valori sau obiecte. Valorile simple (cum sunt valorile 
numerice) sunt atemporale, nu se pot schimba şi nu se instanţiază în vreme ce obiectele au o 
existenţă temporală, se pot schimba, se pot instanţia şi pot fi create, distruse şi partajate.  

Comportamentul reprezintă felul în care obiectul acţionează şi reacţionează. Altfel spus, 


comportamentul reprezintă ceea ce este vizibil şi testabil din exteriorul său. Comportamentul
este  dat de o funcţie care depinde de propria stare a obiectului precum şi de operaţiile efectuate  
asupra lui. O operaţie denotă un serviciu pe care o clasă îl oferă clienţilor săi. Pentru un client 
sunt caracteristice 5 tipuri de operaţii pe care acesta le efectuează asupra unui obiect:  
• Modificarea – Operaţie care alterează starea unui obiect  
• Selectarea – Operaţie prin care se accesează starea unui obiect fără a o modifica în vreun  fel  
• Iterarea – Operaţie care permite tuturor părţilor unui obiect de a fi accesate într-o anumită
ordine, bine precizată 
• Construcţia – Operaţie care crează un obiect şi/sau iniţializează starea acestuia 
• Distrugerea – Operaţie care distruge obiectul asupra căruia acţionează 
Protocolul asociat unui obiect defineşte comportamentul permis al obiectului. Acest spaţiu 
comportamental al obiectului se divizează în funcţie de nişte criterii logice, rezultând astfel 
rolurile pe care un obiect le poate juca.  

Responsabilităţile unui obiect includ două lucruri esenţiale: cunoaşterea pe care obiectul o 
gestionează şi acţiunile pe care obiectul le poate efectua. Responsabilităţile unui obiect înseamnă
în acelaşi timp toate serviciile pe care le asigură pentru toţi contractorii pe care îi suportă. 
 

Identitatea este acea proprietate a unui obiect care îl face deosebit de alte obiecte.  

O clasă reprezintă o mulţime de obiecte cu aceeaşi structură şi comportament. Clasa dispune de 
o interfaţă care descrie aspectul exterior al acesteia accentuând noţiunea de abstractizare prin 
faptul că ascunde structura clasei şi detaliile privind comportamentul acesteia. Pe scurt interfaţa 
constă în declararea tuturor operaţiilor aplicabile unei instanţe a clasei respective. Prin 
comparaţie implementarea unei clase oferă o privire asupra interiorului clasei cuprinzând aici 
„intimităţile” comportamentului acesteia şi include implementările fiecărei operaţii definite în 
interfaţă. Interfaţa poate fi de mai multe feluri:  
• public – declaraţiile sunt accesibile tuturor clienţilor  
• protected – declaraţiile sunt accesibile clasei insăşi, subclaselor sale şi claselor  „prietene”.  
• private – declaraţiile sunt accesibile numai clasei insăşi şi claselor „prietene”.  

Relaţii între clase. Relaţii între obiecte  

Există trei tipuri principale de relaţii între clase:  


• Generalizarea/Specializarea – este o relaţie de tip „is a”  
• Întreg/Parte – este o relaţie de tip „part of”  
• Asocierea – este o relaţie de dependenţă semantică 

Cele mai multe limbaje de programare orientate obiect oferă suport pentru următoarele tipuri de 
relaţii:  
• de asociere – denotă o relaţie între două clase, altminteri fără legatură între ele,  determinată de
un anumit context particular. Există trei tipuri de cardinalitate pentru acest  tip de relaţie:
One-To-One, One-To-Many şi Many-To-Many.  
• de moştenire – este o relaţie între clase în care o clasă împarte structura şi/sau 
comportamentul cu o singură clasă (moştenire simplă) sau cu mai multe alte clase 
(moştenire multiplă). Defineşte o ierarhie de tip „is a” între clase în care o subclasă
moşteneşte una sau mai multe superclase. 
 

• de agregare – indică direcţia unei relaţii de tip „part of”  

Numim legatură (link) o asociere prin care un obiect (clientul) apelează la serviciile altui obiect 
(furnizorul). Legătura presupune de asemenea, că între cele două obiecte pot circula mesaje, de 
regulă unidirecţionale. De fiecare dată când un mesaj circulă între două obiecte de-a lungul unei 
legături spunem despre cele două obiecte că sunt sincronizate.  

Ca participant la o astfel de legătură un obiect poate juca unul din următoarele roluri:  • Actor – este
un obiect care poate acţiona asupra altor obiecte dar asupra căruia nici un alt  obiect nu
acţionează 
• Server – este un obiect care niciodată nu acţionează asupra altor obiecte ci se acţionează
asupra lui de către alte obiecte  
• Agent – este un obiect care acţionează asupra altor obiecte şi asupra căruia pot acţiona  alte
obiecte.  

În afară de relaţia de tip client/furnizor descrisă mai sus, între obiecte există şi relaţia de
agregare  care determină o ierarhie de tip întreg/parte în care există posibilitatea de a naviga
dinspre întreg  (numit si agregat) spre parţile sale (numite şi atribute). 
 

În Java o clasă este o colecţie de date şi metode care operează cu aceste date. Luate împreună
1

datele şi metodele definesc conţinutul şi capabilităţile unui anumit tip de obiect. De exemplu, un 
monitor poate fi descris prin dimensiunea diagonalei ecranului, dacă este pornit sau nu şi prin 
valorile pe care le au parametrii de luminozitate şi contrast. Cu acest monitor se pot face o serie 
de lucruri cum ar fi: pornirea şi oprirea lui, modificarea luminozităţii sau contrastului. Fiecare 
monitor este diferit (de pilda are o altă dimensiune a digonalei ecranului) însă o clasă
monitoare,  are anumite proprietăţi intrinseci pe care le putem captura într-o definiţie.  
public class Monitoare {  
 public int diagonalaEcran;  
 public int luminozitate, contrast;  
 public boolean pornit;  
 // Metode  
 public boolean stareMonitor() { return pornit; }  
 ...  
}  

Acum pentru că am definit (chiar dacă numai parţial) clasa Monitoare dorim să o folosim la 
ceva. Este evident că nu putem face nimic cu clasa însăşi astfel că o sa avem nevoie de un 
monitor particular ca să lucrăm cu el. Practic avem nevoie de o instanţă a clasei, de un singur 
obiect monitor. Prin definirea clasei Monitoare în Java am creat un nou tip de date şi putem 
declara variabile de acest tip:  
Monitoare mon;  

Variabila mon este un simplu identificator care referă un obiect monitor. Nu este ea însăşi un 
obiect. Simpla declarare nu crează obiecte. Această declaraţie notifică de fapt compilatorul 
asupra faptului că utilizatorul va folosi numele mon pentru a se referi la o dată de tip 
Monitoare. În esenţă clasele sunt tipuri de date referinţă. În contrast cu tipurile de date 
primitive, valoarea unei variabile de tip referinţă este adresa unei valori sau a unei mulţimi de 
valori reprezentate de variabila respectivă. În alte limbaje de programare acest tip de date este 
numit pointer sau adresă de memorie. Limbajul de programare Java nu suportă utilizarea 
explicită a adreselor aşa cum o fac alte limbaje. Aici singurul mecanism permis pentru aceasta  

                                                            
1
Metodă este un termen utilizat în teoria programării orientate obiect pentru a desemna o procedură sau o funcţie. 
 

ramâne numele variabilei. Revenind la declaraţia varibilei mon, ceea ce se obţine este o referinţă
vidă, care indică spre nimic pentru că, repet, obiectul nu este creat:  

În Java toate obiectele se crează în mod dinamic. În aproape toate cazurile acest lucru se 
realizează prin intermediul cuvânatului cheie new.  
Monitoare mon;  
mon = new Monitoare();  

Acum am creat o instanţă a clasei Monitoare – un obiect monitor – şi l-am asignat variabilei 
mon, care este de tip Monitoare. Având obiectul putem ne folosi de câmpurile de date pe care 
le conţine:  
Monitoare mon;  
mon = new Monitoare();  
mon.diagonalaEcran = 17;  
mon.pornit = true;  
Efectul executării precedentelor linii de cod poate fi ilustrat astfel:  

Obiect de tip Monitoare 


mon 

diagonalaEcran pornit 

17 

true 

Se observă ca variabila mon indică acum spre un obiect de tip Monitoare.  Pentru a accesa
metodele obiectului vom folosi aceeaşi sintaxă ca pentru cîmpurile de date ale  acestuia: 
 

Monitoare mon;  
boolean s;  

mon = new Monitoare();  


mon.diagonalaEcran = 17;  
mon.pornit = true;  
s = mon.stareMonitor();  

Se observă că nu este nevoie să transmitem nici un argument funcţiei mon.stareMonitor()


pentru că obiectul asupra căruia operează este înglobat în sintaxă. Totuşi funcţia operează cu 
anumite câmpuri de date şi chestiunea aceasta nu mai este evidentă. De fapt, chiar dacă metoda 
nu are nici un argument vizibil, lucrează totuşi cu unul implicit. Acest argument implicit se 
numeşte this şi este o referinţă spre obiectul însuşi, în cazul nostru spre obiectul de tip 
Monitoare a cărui metodă este invocată. Argumentul this (numit şi referinţa this) nu este
vizibil  în descrierea metodelor pentru că acest lucru nu este necesar. De fiecare dată când o
metodă Java  accesează câmpurile de date ale clasei sale, se subînţelege că accesează câmpurile
referite de  this. Referinţa this poate fi utilizată şi explicit:  
public class Monitoare {  
 public int diagonalaEcran;  
 public int luminozitate, contrast;  
 public boolean pornit;  
 // Metode  
 public boolean stareMonitor() {return this.pornit;}
...  
}  

În descrierea metodelor simple folosirea explicită a referinţei this nu se justifică. În alte situaţii, 
unde metodele au o descriere mai complicată, folosirea explicită a referinţei this poate mari 
claritatea codului sursă.  

Crearea obiectelor  

Revenim asupra următoarei secvenţe de program:  


Monitoare mon;  
boolean s;  

mon = new Monitoare(); 


 

unde putem constata cele două paranteze pereche care sugerează un apel de funcţie. De fapt, 
chiar acest lucru se şi întâmplă. Fiecare clasă în Java are cel puţin o metodă, numită
constructor,  care are acelaşi nume cu clasa. Scopul constructorului este de a efectua orice
iniţializări necesare  noului obiect. Dacă nu specificăm explicit un constructor în descrierea
clasei, atunci Java va  folosi unul implicit care nu are argumente şi nu efectuează nici o
iniţializare. Ceea ce se întâmplă 
în exemplul precedent poate fi rezumat astfel: new alocă spaţiu pentru obiectul care se crează, 
apoi este apelat constructorul căruia i se transmit argumentele dorite.  

Declararea constructorilor trebuie să ţină cont de următoarele:  


• numele constructorului trebuie să fie identic cu numele clasei  
• tipul valorii returnate de constructor este implicit o instanţă a clasei  
• nu se specifică in declararea constructorului nici un tip de date pentru valoarea returnată şi nici
nu se utilizează void în acest scop.  
• constructorul nu trebuie să folosească instrucţiunea return pentru a întoarce o valoare. 
Exemplu:  
public class Monitoare {  
 public int diagonalaEcran;  
 public int luminozitate, contrast;  
 public boolean pornit;  
 // Constructor  
 public Monitoare(int diag)  
 {  
 diagonalaEcran = diag;  
 }  
 // Metode  
 public boolean stareMonitor() {return pornit;}  
 ...  
}  

Cu această declaraţie a clasei Monitoare, secvenţa de instrucţiuni:  


Monitoare mon;  

mon = new Monitoare();  


mon.diagonalaEcran = 17;  

poate fi înlocuită cu:  


Monitoare mon = new Monitoare(17); 
 

Există situaţii, şi nu puţine la număr, în care am dori să iniţializăm un obiect în moduri diferite, 
funcţie de anumite circumstanţe. De pildă, pe lângă dimensiunea diagonalei monitorului în 
anumite situaţii dorim să putem iniţializa şi valoarea controalelor de luminozitate şi constrast. 
Acest lucru se realizează prin declararea mai multor constructori pentru aceeaşi clasă. În Java o 
clasă poate avea oricâţi constructori. Să luam ca exemplu declaraţia următoare:  
public class Monitoare {  
 public int diagonalaEcran;  
 public int luminozitate, contrast;  
 public boolean pornit;  
 // Constructori  
 public Monitoare(int diag)  
 {  
 diagonalaEcran = diag;  
 luminozitate = 50;  
 contrast = 50;  
 pornit = false;  
 }  
 public Monitoare(int luminozitate, int contrast)  
 {  
 diagonalaEcran = 15;  
 this.luminozitate = luminozitate;  
 this.contrast = contrast;  
 pornit = false;  
 }  
 public Monitoare()  
 {  
 diagonalaEcran = 15;  
 luminozitate = 50;  
 contrast = 50;  
 pornit = false;  
 }  
 // Metode  
 public boolean stareMonitor() {return pornit;}  
 ...  
}  

Prima observaţie pe care o facem este că toţi constructorii au acelaşi nume şi totoşi compilatorul 
îi poate diferenţia. În Java o metodă se identifică atât prin numele său cât şi prin numărul, tipul
şi  poziţia argumentelor sale. Acest lucru este valabil pentru orice metodă, nu numai pentru 
constructori. Două metode nu sunt la fel decât dacă au acelaşi nume, acelaşi număr de
argumente  de acelaşi tip şi situate în aceleaşi poziţii în lista de argumente. Procedeul prin care
definim  metode cu acelaşi nume însă cu liste de argumente diferite se numeşte overloading (sau 
supraîncărcarea metodelor). 
 

Putem folosi acum constructorii declaraţi mai sus pentru crearea şi iniţializarea diferitelor 
obiecte:  
Monitoare monSamsung, monLCD, monPhilips;  

monSamsung = new Monitoare(17);  


monLCD = new Monitoare();  
monPhilips = new Monitoare(55,60);  

Efectul instrucţiunilor precedente este ilustrat în figura următoare:  

monSamsung 

diagonalaEcran luminozitate 50 contrast 50 

17 

pornit 

monLCD 
15 
false 

diagonalaEcran 

luminozitate 50 

contrast 50 
monPhilips 

diagonalaEcran luminozitate 55 contrast 60 

pornit 

15 
false 

pornit 
false 

Definiţia clasei Monitoare conţine declaraţiile a patru variabile:  •


diagonalaEcran 
• luminozitate 
• contrast
 

• pornit.  
Fiecare instanţă a clasei Monitoare va avea propria copie a celor patru variabile. Limbajul
Java  nu permite utilizarea de variabile globale. Toate variabilele trebuie declarate în interiorul
unei  clase. Pentru a indica faptul că o variabilă este o variabilă clasă şi nu una instanţă în Java 
utilizăm cuvântul cheie static. Asta înseamnă că va exista o singură copie a acestui tip de 
variabilă indiferent de numărul de instanţe ale clasei. În exemplul următor am declarat o 
variabilă clasă numarMonitoare pe care am incrementat-o de fiecare dată când un obiect de
tip  Monitoare este creat.  
public class Monitoare {  
 static int numarMonitoare = 0;  
 public int diagonalaEcran;  
 public int luminozitate, contrast;  
 public boolean pornit;  
 // Constructori  
 public Monitoare(int diag)  
 {  
 diagonalaEcran = diag;  
 luminozitate = 50;  
 contrast = 50;  
 pornit = false;  
 numarMonitoare++;  
 }  
 public Monitoare(int luminozitate, int contrast)  
 {  
 diagonalaEcran = 15;  
 this.luminozitate = luminozitate;  
 this.contrast = contrast;  
 pornit = false;  
 numarMonitoare++;  
 }  
 public Monitoare()  
 {  
 diagonalaEcran = 15;  
 luminozitate = 50;  
 contrast = 50;  
 pornit = false;  
 numarMonitoare++;  
 }  
 // Metode  
 public boolean stareMonitor() {return pornit;}  
 ...  

 

Accesul la o variabilă clasă va fi efectuat prin intermediul clasei pentru că acest tip de variabilă
este asociat clasei şi nu instanţelor acesteia.  

Exemplu:  
Monitoare monSamsung, monLCD, monPhilips;  

monSamsung = new Monitoare(17);  


monLCD = new Monitoare();  
monPhilips = new Monitoare(55,60);  
System.out.println("Numar monitoare="+  
 Monitoare.numarMonitoare);  
Variabilele clasă reprezintă un „surogat” de variabile globale. Diferenţa faţă de variabilele 
globale constă în faptul că nu pot apărea conflicte de nume. Astfel chiar dacă ar mai exista o 
clasă care ar conţine o variabila clasă cu numele numarMonitoare nu ar fi nici o problemă
pentru că accesul la acestea trebuie facut prin intermediul claselor respective şi astfel cele două
variabile clasă se pot deosebi.  

Revenind la exemplul clasei Monitoare, să presupunem că am dori să exprimăm diagonala 


ecranului şi în centimetri, prin intermediul unei metode, caz în care trebuie să cunoaştem câţi 
centimetri are un inch. Pentru că această valoare va fi utilizată frecvent, nu dorim ca de fiecare 
dată când ne interesează transformarea unităţilor de măsură respective să o scriem explicit, adică 
2.54, ci am dori să o declarăm sub forma unei variabile cu un nume sugestiv.  
public class Monitoare {  
 public static final double cmPerInch = 2.54;  
 static int numarMonitoare = 0;  
 public int diagonalaEcran;  
 public int luminozitate, contrast;  
 public boolean pornit;  
 // Constructori  
 ...  
 // Metode  
 public boolean stareMonitor() {return pornit;}  
 public double diagonalaEcranCm()  
 {  
 return diagonalaEcran*cmPerInch;  
 }  
 ...  

Observăm că pe lângă cuvântul cheie static apare şi final. Utilizarea acestuia semnifică
faptul că variabila nu îşi poate modifica valoarea. Acest lucru previne atribuiri eronate (şi 
stupide) de tipul:  
Monitoare.cmPerInch = 4.2;  
Astfel am identificat şi declarat o categorie aparte de variabile clasă şi anume constantele. 
Compilatorul se comportă inteligent atunci când întâlneşte variabile declarate static şi final pe 
care le recunoaşte ca fiind constante. Astfel are posibilitatea de a efectua toate preprocesările 
posibile încă din etapa de compilare reducând efortul interpretorului. De pildă dacă am dori să 
avem diagonala exprimată în milimetri şi am modifica clasa Monitoare astfel:  
public class Monitoare {  
 public static final double cmPerInch = 2.54;  
 static int numarMonitoare = 0;  
 public int diagonalaEcran;  
 public int luminozitate, contrast;  
 public boolean pornit;  
 // Constructori  
 ...  
 // Metode  
 public boolean stareMonitor() {return pornit;}  
 public double diagonalaEcranMm()  
 {  
 return diagonalaEcran*10*cmPerInch;  
 }  
 ...  
}  
compilatorul va preprocesa secvenţa 10*cmPerInch şi o va înlocui cu valoarea 25.4 scutind 
astfel interpretorul de o operaţie de înmulţire.  

Distrugerea obiectelor  
Spre deosebire de limbajul C++, în limbajul Java nu există un operator delete pentru a distruge 
un obiect. De fapt Java nu mai lasă distrugerea obiectelor în seama programatorului. Tehnica 
prin care se realizează acest lucru se numeşte „Garbage collection”. Interpretorul Java ştie ce 
obiecte are alocate, ce variabile referă aceste obiecte şi ce obiecte referă alte obiecte. Astfel
poate  afla cu uşurinţă când un obiect alocat nu mai este referit de cineva (variabilă sau alt
obiect). Când  un astfel de obiect este identificat el poate fi distrus în deplină siguranţă fără a
afecta aplicaţia. În Java Garbage Collector-ul rulează ca un fir de execuţie (thread) cu prioritate
mică şi care îşi  desfăşoară cea mai mare parte a activităţii atunci când nimic altceva nu rulează
(de exemplu în  perioadele când aplicaţia aşteaptă ca utilizatorul să introducă date).  
Deşi această metodă ar putea sugera faptul că lucrurile se întâmplă lent şi neoptimal (în privinţa 
consumului de memorie) garbage collector-ul reprezintă o soluţie foarte eficientă. Sigur că nu
va  fi la fel de eficient ca un cod în care se programează explicit alocarea şi dealocarea
memoriei.  Însă prin utilizarea acestei tehnici munca programatorului devine mai simplă pentru
că se  scurtează sensibil codul sursă prin eliminarea dealocărilor explicite. În acelaşi timp prin 
eliminarea dealocărilor explicite se reduc substabţial sursele de erori.  

Subclase şi moştenire  
Să revenim la declaraţia iniţială a clasei Monitoare:  
public class Monitoare {  
 public int diagonala;  
 public int luminozitate, contrast;  
 public boolean pornit;  
 // Metode  
 public boolean stareMonitor() { return pornit; }  
 ...  
}  
Pornind de la aceasta ne propunem să definim o noua clasă cu numele LCD. Faţă de datele deja 
prezente în clasa Monitoare dorim să mai adaugăm una care se referă la latenţa afişării 
echipamentului. Putem să facem acest lucru astfel:  
public class LCD {  
 public Monitoare m;  
 // Aici sunt vechile metode ale clasei Monitoare  
 public boolean stareMonitor()  
 {  
 return m.stareMonitor();  
 }  
 ...  
 // Noile date si metode  
 int latenta;  
 public void reglajContrast(int c)  
 {  
 m.contrast = c;  
 }  

Implementarea de mai sus va funcţiona însă nu este deloc elegantă. Observăm că trebuie
să scriem iar metoda stareMonitor pentru noua clasă. Nu aceasta va fi soluţia pe care
o adoptăm.  Java permite declararea acestei noi clase ca o extensie (subclasă) a clasei
Monitoare.  
public class LCD extends Monitoare {  
 // Sunt mostenite toate variabilele  
 // si metodele clasei Monitoare  

 int latenta;  
 public void reglajContrast(int c)  
 {  
 contrast = c;  
 }  
}  

Cuvântul cheie extends arată compilatorului Java că LCD este o subclasă a lui Monitoare, 
moştenind de la aceasta toate datele şi metodele. Modul de definire al metodei 
reglajContrast ne arată felul în care avem acces la variabilele moştenite. Observăm că
utilizăm varibila contrast ca şi cum ar fi a clasei LCD deşi ea este definită în clasa 
Monitoare.  
Să considerăm exemplul următor:  
LCD sony = new LCD();  
boolean sm;  

sm = sony.stareMonitor();  
...  
Aici este ilustrată moştenirea metodelor. Remarcăm faptul că avem acces la metodele clasei 
Monitoare, în speţă la stareMonitor. Aceasta funcţionează ca şi cum ar fi definită în clasa 
LCD.  

O altă trăsătură a relaţiei de subclasă este aceea că un obiect de tip LCD este în acelaşi timp un 
obiect perfect corect de tip Monitoare:  
LCD sony = new LCD();  
Monitoare m;  
...  
m = sony;  
... 
Atribuirea din exemplul precedent este corectă însă obiectul m nu va dispune de capabilităţile 
suplimentare ale obiectului sony.  
Dacă o clasă este declarată utilizând şi cuvântul cheie final, atunci ea nu poate fi extinsă (nu 
poate avea o subclasă).  

În exemplul nostru vorbeam de LCD ca fiind subclasa lui Monitoare. În acelaşi timp spunem
că Monitoare este superclasa lui LCD. Superclasa este specificată în clauza extends. În Java 
orice clasă are o superclasă. Dacă declaraţia unei clase nu conţine clauza extends superclasa 
acesteia este clasa Objects. Objects este singura clasă care nu are o superclasă iar metodele 
acesteia pot fi apelate de orice obiect Java. Datorită acestui fapt clasele în Java formează o 
ierarhie de clase care poate fi reprezentată sub forma unui arbore în a carui radacină se găseşte 
Objects.  

Clasa LCD definită mai sus nu are definit un constructor. Putem face acest lucru astfel:  
public class LCD extends Monitoare {  
 int latenta;  
 public LCD(int diag, int lt)  
 {  
 diagonala = diag;  
 luminozitate = 50;  
 contrast = 50;  
 latenta = lt;  
 pornit = false;  
 }  
 public void reglajContrast(int c)  
 {  
 contrast = c;  
 }  

Constructorul definit astfel ilustrează faptul că sunt moştenite toate variabilele clasei 
Monitoare şi în cel mai simplu mod cu putinţă le iniţializează. Această metodă însă dublează
codul pentru că există deja secvenţa pentru iniţializarea variabilelor diagonală, 
luminozitate, contrast şi pornit în constructorul clasei Monitoare. Avem nevoie
deci  
de o metodă de a apela constructorul clasei Monitoare din constructorul clasei LCD:  
public class LCD extends Monitoare {  
 int latenta;  
 public LCD(int diag, int lt)  
 {  
 super(diag);  
 latenta = lt;  
 }  
 ...  
}  
În Java super este cuvânt rezervat. Utilizarea acestuia trebuie să tină cont de următoarele:  •
super se poate folosi în acest mod numai în cadrul unui constructor  
• apelul constructorului superclasei trebuie să apară ca primă instrucţiune în cadrul 
constructorului subclasei. Trebuie să apară chiar şi înaintea eventualelor declaraţii de 
variabile  

Odată definită o clasă, Java garantează că una din metodele constructor va fi apelată atunci când 
o instanţă a clasei respective este creată. Acest lucru este valabil şi în cazul în care o instanţă a 
unei sublase este creată. Pentru ca acest ultim aspect să fie rezolvat, Java se asigură că fiecare 
metodă constuctor a subclasei apelează constuctorul superclasei sale. Astfel dacă prima 
instrucţiune a constructorului subclasei nu este un apel explicit al constructorului superclasei 
(prin utilizarea lui super), Java va insera ea însăşi instrucţiunea super(), adică va apela 
constructorul superclasei fară argumente. Dacă superclasa nu are definit un constructor fară
argumente atunci compilatorul va semnala o eroare.  

Dacă într-o clasă nu este definit un constructor fără argumente atunci este obligatoriu ca în toate 
subclasele sale constructorii sa apeleze explicit constructorii superclasei cu argumentele 
necesare. 

Dacă definim o clasă fără a declara un constructor, Java va insera un constructor implicit care va 
apela constructorul superclasei fără argumente. De exemplu dacă nu am fi definit pentru clasa 
LCD nici un constructor Java ar fi inserat automat constructorul implicit:  
 public LCD() { super(); }  
Datorită acestui fapt nedeclararea în clasa Monitoare a unui constructor fără argumente va 
cauza o eroare de compilare. 

Variabile „shadow”  
Considerăm următorul exemplu:  
public class A {  
 int x;  
 public A()  
 {  
 // Cod sursa constructor  
 ...  
 }  
}  

public class B extends A {  


 int x;  
 public B()  
 {  
 // Cod sursa constructor  
 ...  
 }  
}  

Observăm declaraţia în clasa B a unei variabile cu numele x. Această variabilă declarată în


cadrul  clasei B, „umbreşte” variabila cu acelaşi nume declarată în clasa A. Accesând numele x
sau  sinonimul this.x în cadrul clasei B vom accesa variabila x declarată în B. Accesul la
variabila  x definită în superclasă se face prin construcţia super.x. O altă soluţie este să îl
distribuim  (cast) pe this clasei corespunzătoare şi apoi să accesăm variabila shadow:  
((A) this).x  

Această tehnică este utilă atunci când în cadrul unei clase dorim să accesăm o variabilă shadow 
dintr-o clasă care nu este superclasa acesteia. Dacă, de exemplu, C este o subclasă a lui B care
la  rândul ei este subclasă a lui A şi x este o variabilă shadow declarata în toate cele trei clase
atunci  putem accesa aceste variabile din clasa C în felul următor:  
x // variabila x din clasa C  
this.x // variabila x din clasa C  
super.x // variabila x din clasa B  
((B)this).x // variabila x din clasa B  
((A)this).x // variabila x din clasa A  
super.super.x // constructie neacceptata 

Faptul că o variabilă poate „umbri” o altă variabilă face ca să ne întrebăm dacă acest lucru este 
valabil şi în cazul metodelor. Într-un anumit sens acest lucru este posibil iar tehnica se numeşte 
suprascrierea (overriding) metodelor.  

Suprascrierea metodelor  

Când o clasă defineşte o metodă folosind acelaşi nume, tip şi argumente ca o altă metodă definită
în superclasă sa spunem că metoda definită în clasă suprascrie metoda definită în superclasă. 
Când această metoda este invocată pentru un obiect al clasei este apelată metoda clasei şi nu cea 
a superclasei.  

Suprascrierea metodelor nu trebuie confundată cu supraîncărcarea metodelor. Deşi în ambele 


cazuri este vorba de mai multe metode cu acelaşi nume, nu trebuie omis faptul că în cazul 
supraîncărcării metodelor este vorba despre mai multe metode cu acelaşi nume dar cu liste de 
argumente diferite.  

De asemenea deşi pare asemănătoare suprascrierea metodelor nu reprezintă la nivelul metodelor 


acelaşi lucru ca şi variabilele shadow. Diferenţa este ilustrată în exemplul următor:  
class A {  
 int x = 1;  
 int f() { return x; }  
}  

class B extends A {  


 int x = 2;  
 int f() { return -x; }  
}  

public class exemplu {  


 public static void main (String args[]) {  
 B b = new B();  
 System.out.println(b.x); // 1  
 System.out.println(b.f()); // 2  

 A a = (A) b; // 3  
 System.out.println(a.x); // 4  
 System.out.println(a.f()); // 5  

Instrucţiunea 1 va avea ca efect afişarea valorii 2 pentru că referirea se face la variabila x 


declarată în B. Instrucţiunea 2 va avea ca efect afişarea valorii -2 pentru că referirea se face la 
metoda f declarată de asemenea în B. În instrucţiunea 3 se declară un obiect de tip A căruia i se 
atribuie obiectul b, atribuirea fiind corectă dată fiind relaţia de moştenire dintre cele două clase. 
În acest moment instrucţiunea 4 va avea ca efect afişarea valorii 1 pentru că referirea se face la 
variabila x declarată în A (vezi regulile de acces la variabilele shadow). În schimb instrucţiunea
5  va avea ca efect afişarea valorii -2 pentru că referirea se face la funcţia f declarată în B
(datorită atribuirii) care la randul ei va accesa variabila this.x adica pe cea declarată în B. Se
observă de  aici ca variabilele shadow şi metodele suprascrise nu au acelaşi comportament.  

Identificarea metodelor ce vor fi apelate nu poate fi facută la nivelul compilatorului. Datoriată


acestui fapt compilatorul va produce un cod care va determina cautarea dinamică a metodelor 
(dynamic method lookup) în momentul rulării. Asta înseamnă că dacă se întalneşte o expresia 
b.f() interpretorul va căuta metoda f() asociată obiectului particular referit de variabila b şi 
nu va utiliza metoda f() care este asociată definiţiei clasei care dă tipul lui b. Căutarea
dinamica  a metodelor nu este la fel de rapidă ca apelarea directă a metodelor însă acest lucru nu
este  posibil întotdeauna. Există şi cazuri când invocarea se poate face direct cum este cazul
metodelor  declarate static sau final (prezentate în cele ce urmează).  

Acest comportament aduce după sine nişte avantaje. Să presupunem ca am avea o aplicaţie în 
care sunt definite clasa Cerc şi clasa Elipsă (ca subclasă a lui Cerc) şi o mulţime de obiecte
de  tip Cerc şi Elipsă cu care trebuie să operăm. Toate obiectele se pot stoca într-o listă de
tip  Cerc şi putem să calculăm aria fiecărui element al listei apelând la metoda arie (definită
în  fiecare din cele 2 clase). Exemplul prezentat anterior ne arată că aria va fi calculată corect 
indiferent dacă obiectul curent este un cerc sau o elipsă datorită suprascrierii metodei arie.  
  
Dacă o metodă este declarată folosindu-se cuvântul cheie final atunci aceasta nu poate fi 
suprascrisă.  
Sintaxa utlizată de Java pentru a invoca metodele suprascrise este similară celei utilizate pentru 
variabilele shadow. 

class A {  
 int x = 1;  
 int f() { return x; }  
}  

class B extends A {  


 int x = 2; // variabila shadow  
 int f()  
 {  
 x = super.x+1; // x=A.x+1  
 return super.f()+x; // se invoca A.f()  
 }  
}  
Considerăm un alt exemplu în care clasa A defineşte metoda f, B este o subclasă a lui A şi C
este  o subclasă a lui B şi care suprascrie metoda f. Apelul super.f() efectuat din C va
invoca  metoda suprascrisă (definită în clasa A) pentru că B moşteneşte metoda f de la A. Dacă
toate  clasele ar defini metoda f atunci apelul super.f() efectuat de asemenea în C va invoca
metoda  f() definită în B, neexistând vreo posibilitate să invocăm A.f().  

Ascunderea şi încapsularea datelor  

Una din tehnicile cele mai importante ale programării orientate-obiect o reprezintă ascunderea 
(hiding) datelor în cadrul unei clase şi accesarea lor numai prin intermediul metodelor. Această
tehnică este cunoscută sub numele de încapsulare deoarece „sigilează” datele (şi anumite 
metode) într-o „capsulă” reprezentată de clasa în care sunt definite, accesul la acestea fiind 
permis numai prin intermediul anumitor metode ale clasei. Motivul principal pentru aceasta îl 
reprezintă dorinţa de a ascunde detaliile de implementare ale clasei. Astfel se poate modifica 
implementarea unei astfel de clase fără a afecta codul scris de un alt programator care utilizează 
această clasă, pentru că programatorul nu s-a folosit de detaliile de implementare deoarece nu a 
avut acces la ele.  

Un alt motiv pentru utilizarea încapsulării este prevenirea folosirii greşite a clasei respective. 
Astfel o clasă poate conţine un număr de variabile interdependente care trebuie să asigure clasei 
o stare consistentă. Dacă se permite programatorilor accesul direct la aceste variabile atunci, din 

greşeală, el poate modifica valoarea unei variabile fără a mai actualiza şi valorile celorlalte 
variabile legate de aceasta, lasând clasa într-o stare inconsistentă.  

Dacă toate variabilele unei clase sunt ascunse, atunci numai metodele definesc toate operaţiile 
care pot fi efectuate cu obiectele acelei clase. In acest caz efortul privind testarea funcţională se 
concentrează numai asupra metodelor. Dacă este permis accesul direct la variabilele clasei 
numărul de cazuri pe care trebuie să le testăm devine de negestionat.  

S-a putut remarca în toate exemplele de până acum a folosirea cuvântului cheie public. Când 
este aplicat asupra unei clase înseamnă ca această clasă este vizibilă peste tot. Are aceeaşi 
semnificaţie (vizibil peste tot) şi dacă este aplicat asupra unei variabile sau a unei metode.
Pentru  a ascunde o variabilă sau o metodă trebuie utilizat cuvântul cheie private:  
public class A {  
 private int x;  
 public int f() { return x; }  
}  
Un câmp al unei clase declarat cu private este vizibil numai pentru metodele definite în
cadrul  acestei clase. Similar o metodă declarată cu private poate fi invocată numai de
metodele  definite în cadrul acestei clase. Membrii private nu sunt vizibili în subclase şi nu
sunt  moşteniti de către subclase.  

În afară de public şi private, Java mai dispune de încă două nivele de vizibilitate: protected
şi  nivelul implicit (sau package). Un membru al unei clase declarat protected este vizibil în
clasa  în care este definit şi în toate subclasele clasei respective şi de asemenea în toate clasele
care se  găsesc în acelaşi pachet (package) cu clasa respectivă. Nivelul de vizibilitate
2

protected ar  trebui utilizat atunci când se doreşte ascunderea datelor şi a metodelor pentru un
cod care  utilizează clasa, şi în acelaşi timp acces complet pentru un cod care extinde clasa.  

O subclasă moşteneşte membrii protected ai superclasei sale însă îi poate accesa numai prin 
intermediul instanţelor sale şi niciodată prin intermediul instanţelor superclasei. Să presupunem  
                                                            
2
Un pachet (package) reprezintă un grup de clase înrudite şi posibil care cooperează. 

că A, B şi C sunt clase public fiecare definită în alt pachet şi că a, b respectiv c sunt instanţe
ale  acestor clase. Fie B subclasă a lui A şi C subclasă a lui B. Acum dacă A are o variabilă
membru x,  protected atunci clasa B o moşteneşte şi metodele sale pot utiliza construcţiile
this.x, b.x şi  c.x însă nu poate accesa a.x. Similar dacă A are o metodă f() protected
atunci metodele clasei  B pot invoca this.f(), b.f() şi c.f() însă nu pot invoca a.f().  

Nivelul de vizibilitate implicit (sau package) se aplică atunci cînd nu este specificat nici unul 
dintre public, private sau protected şi este mai restrictiv decât protected însă mai
puţin  restrictiv decât private. Dacă un membru al unei clase se găseşte în această situaţie
atunci el  este vizibil în cadrul clasei respective şi în clasele care aparţin aceluiaşi pachet. Nu este
vizibil în  subclase în afara cazului în care aceste subclase fac parte din acelaşi pachet.  

Vizibilitate membrii  
Accesibil
public protected package private  Aceeaşi clasă DA DA DA DA 
Clasă, acelaşi pachet DA DA DA NU  Subclasă, pachete diferite DA DA NU
NU  Non-subclasă, pachete diferite DA NU NU NU  

Rezumând se pot deprinde următoarele reguli pentru utilizarea modificatorilor de acces:  

• Se utilizează public numai pentru metodele şi constantele care fac parte din interfata de 
programare a clasei (API). Anumite câmpuri foarte importante sau frecvent utilizate pot  fi
facute de asemenea public însă este bine ca acestea să fie declarare non-public şi să fie 
accesate prin intermediul unor metode public.  
• Se utilizează protected pentru date şi metode la care nu este necesar un acces direct  atunci
când clasa este utilizată dar care prezintă interes atunci când cineva creează o  subclasă a
acesteia ca parte a unui pachet diferit, în vederea extinderii ei. 

• Se utilizează nivelul de vizibilitate implicit atunci când dorim ca membrii respectivi să fie 
ascunşi pentru clasele din afara pachetului dar vizibili pentru cele din interiorul lui.  

• Se foloseşte private pentru datele şi metodele care se folosesc numai în interiorul clasei  şi
care trebuie ascunse de oricine altcineva. 

Clase abstracte. Interfeţe  

Considerăm următorul exemplu, în care definim o clasă cu numele PlayerMP3, cu ajutorul 


căreia dorim să implementăm un „player” pentru fişiere audio în format MP3:  
public class PlayerMP3 {  
 private int Status;  
 ...  
 public PlayerMP3()  
 {  
 // Constructor  
 ...  
 }  
 public void Play()  
 {  
 // Implementare metoda Play  
 ...  
 }  
 public void Pause()  
 {  
 // Implementare metoda Pause  
 ...  
 }  
 public void Stop()  
 {  
 // Implementare metoda Stop  
 ...  
 }  
 public void Exit()  
 {  
 // Implementare metoda Exit  
 ...  
 }  
}  
unde variabila Status, va stoca starea în care se găseşte player-ul: STOPPED, PAUSED, 
PLAYING.  
Dacă dorim însă să implementăm în aplicaţia noastră şi un player pentru fişiere video în format 
DivX, soluţia aleasă nu mai este una eficientă pentru ca rapid vom constata că şi player-ul DivX
va avea aceleaşi metode de implementat. Practic avem de-a face cu două clase similare ca şi 
conţinut. Este mai elegant să surprindem aceste aspecte comune celor două player-e într-o clasă
mai generală cu numele Player şi să derivăm din aceasta cele doua tipuri pe care dorim să le 
implementăm. 

Numai că în cazul acestei abordări vom avea de întâmpinat unele dificultăţi. Astfel nu vom
putea  implementa la nivelul clasei Player metodele Play, Pause, Stop pentru că redarea şi 
manipularea celor două tipuri de fişiere (audio, respectiv video) se face diferit. În Java această
problemă se va rezolva prin utilizarea metodelor abstracte.  

Java permite simpla definire a unei metode, fără implementare, declarând metoda abstract. O 
metodă abstractă nu are corp ci numai o simplă definiţie a acesteia (un prototip) urmată de 
caracterul ;. Utilizarea metodelor abstracte trebuie să ţină cont de următoarele reguli:  
• Orice clasă care conţine o metodă abstractă este automat abstractă ea însăşi şi trebuie  declarată
ca atare.  
• O clasă poate fi declarată abstractă chiar dacă nu conţine nici o metodă abstractă. Acest  lucru
previne instanţierile nedorite ale clasei respective.  
• O clasă abstractă nu poate fi instanţiată.  
• O subclasă a unei clase abstracte poate fi instanţiată numai dacă suprascrie fiecare  metodă
declarată abstractă în superclasa sa, şi furnizează implementări pentru toate  acestea.  
• Dacă o subclasă a unei clase abstracte nu furnizează implementări pentru toate metodele 
abstracte ale superclasei pe care le moşteneşte atunci devine ea însăşi abstractă.  Iată cum va
arăta exemplul de mai sus modificat, pentru a utiliza clase abstracte:  
public abstract class Player  
{  
 public abstract void Play();  
 public abstract void Pause();  
 public abstract void Stop();  
 public void Exit()  
 {  
 // Implementare metoda Exit  
 }  
}  

class PlayerMP3 extends Player {  


 private int Status;  
 ...  
 public PlayerMP3()  
 {  
 // Constructor  
 Status = STOPPED;  
 } 

 public void Play()  


 {  
 // Implementare metoda Play pt. fisiere MP3  
 ...  
 }  
 public void Pause()  
 {  
 // Implementare metoda Pause pt. fisiere MP3  
 ...  
 }  
 public void Stop()  
 {  
 // Implementare metoda Stop pt. fisiere MP3  
 ...  
 }  
}  

class PlayerDivX extends Player {  


 private int Status;  
 ...  
 public PlayerDivX()  
 {  
 // Constructor  
 Status = STOPPED;  
 }  
 public void Play()  
 {  
 // Implementare metoda Play pt. fisiere DivX  
 ...  
 }  
 public void Pause()  
 {  
 // Implementare metoda Pause pt. fisiere DivX  
...  
 }  
 public void Stop()  
 {  
 // Implementare metoda Stop pt. fisiere DivX  
 ...  
 }  
}  
.....  
Player p1 = new PlayerMP3();  
PlayerDivX p2 = new PlayerDivX();  
p1.Play();  
p2.Play();  
p2.Pause();  
p1.Stop(); 

Relativ la exemplul anterior vom face următoarele observaţii:  


• Metodele abstracte din clasa Player se termină cu ; imediat după parantezele listei de 
argumente. Nu există definit pentru acestea un corp al metodelor  
• Subclasele lui Player pot fi asignate obiectelor de tip Player.  
• Se pot invoca metodele Play(), Pause(), Stop() pentru obiecte de tip Player chiar  dacă
clasa Player nu defineşte un corp pentru aceste metode. Acest lucru este posibil  pentru că
metodele respective sunt declarate abstract. Nedeclararea lor în acest fel  atrage după sine
o eroare de compilare.  

Presupunem acum că dorim să implementăm o variantă grafică a player-ului nostru. Una din 
soluţii ar fi să declarăm o clasă GraphicPlayer şi din aceasta să derivăm clasele 
GraphicPlayerMP3 respectiv GraphicPlayerDivX. În acelaşi timp varianta grafică a player
elor noastre au nevoie de metodele Play(), Pause(), Stop(). Cum nu dorim să le 
reimplementăm, am putea să gândim clasele GraphicPlayerMP3 şi GraphicPlayerDivX
ca  subclase ale lui PlayerMP3, respectiv PlayerDivX, situaţie ilustrată în figura următoare:  

Acest tip de derivare, însă, nu este permis în Java, deoarece o clasă poate avea o singură
superclasă. Dacă de exemplu, GraphicPlayerMP3 extinde clasa PlayerMP3 nu o poate 
extinde în acelaşi timp şi pe GraphicPlayer. 

Soluţia limbajului Java la această problemă se cheamă interfaţă (interface). O interfaţă arată
exact ca o clasă abstractă numai că în declararea ei se foloseşte cuvântul cheie interface în 
locul lui abstract şi class. Exemplu:  
public interface Graphic  
{  
 public void SetPosition();  
 public void draw ();  
 ....  
}  

Toate metodele definite în cadrul unei interfeţe sunt implicit abstracte chiar dacă nu folosim 
explicit cuvântul cheie abstract pentru declararea lor. Chiar dacă abstract a fost omis nu 
este interzisă utilizarea explicită a acestuia pentru declararea metodelor unei interfeţe.  

Acum putem spune că pe lângă posibilitatea ca o clasă să extindă superclasa sa, opţional aceasta 
poate să implementeze o interfaţă. Acest lucru se realizează prin utilizarea cuvântului cheie 
implements în declaraţia clasei respective imediat după clauza extends. implements este 
urmat de numele interfeţei pe care clasa o implementează. Pentru a implementa o interfaţă, o 
clasă trebuie să declare acest lucru în clauza implements şi să furnizeze implementări (corpul 
metodelor) pentru toate metodele abstracte ale interfeţei. Iată cum arată acum exemplul nostru 
după introducerea claselor GraphicPlayerMP3 şi GraphicPlayerDivX.  

public class GraphicPlayerMP3 extends PlayerMP3  


 implements Graphic  
{  

 private int x,y;  


 private int backgroundColor;  
 ...  

 public GraphicPlayerMP3()  
 {  
 // Constructor  
 super();  
 }  

 public void SetPosition(int x, int y)  


 {  
 this.x = x;  
 this.y = y;  
 } 

 public void Draw()  


 {  
 // Implementare metoda pentru desenarea  
 // aspectului grafic al player-ului  
 ...  
 }  
}  

public class GraphicPlayerDivX extends PlayerDivX  


 implements Graphic  
{  

 private int x,y;  


 private int backgroundColor;  
 ...  

 public GraphicPlayerDivX()  
 {  
 // Constructor  
 super();  
 }  

 public void SetPosition(int x, int y)  


 {  
 this.x = x;  
 this.y = y;  
 }  

 public void Draw()  


 {  
 // Implementare metoda pentru desenarea  
 // aspectului grafic al player-ului  
 ...  
 }  
}  

.....  
GraphicPlayerMP3 p1 = new GraphicPlayerMP3();  
GraphicPlayerDivX p2 = new GraphicPlayerDivX();  
p1.SetPosition(100,100);  
p1.Draw();  
p1.Play();  

Presupunem acum că dorim ca să putem scala (mărire/micşorare) imaginea grafică a player-ului 


nostru. Putem realiza acest lucru definind încă o interfaţă cu numele Zoom care să fie 
implementată de către GraphicPlayerMP3 şi GraphicPlayerDivX, pentru că Java admite 

implementarea mai multor interfeţe de către aceeaşi clasă. Clauza implements conţine în
acest  caz lista de interfeţe pe care le implementează clasa respectivă.  

Exemplu:  
public class GraphicPlayerMP3 extends PlayerMP3  
 implements Graphic,Zoom  {  

 ...  

}  

Când o clasă implementează mai multe interfeţe este obligatoriu ca să furnizeze implementări 
(corpul metodelor) pentru toate metodele abstracte ale tuturor interfeţelor implementate.  

O interfaţă poate conţine declaraţii de variabile numai dacă ele sunt declarate static şi
final,  pe scurt dacă sunt constante. O clasă care implementează o interfaţă care conţine
constante, poate  utiliza aceste constante ca şi cum ar fi declarate direct în clasa respectivă. Nu
este nevoie ca  aceste constante să fie prefixate de numele interfeţei. Exemplu:  
class A  
{  
 static final int Constanta1 = 1;  
}  

interface B  
{  
 static final int Constanta2 = 2;  
}  

class C implements B  


{  
 void f()  
 {  
 int i = A.Constanta1;  
 int j = Constanta2;  
 }  
}  

Se observă că a fost necesară prefixarea cu numele clasei în cazul utilizării constantei 


Constanta1 deoarece a fost declarată într-o clasă. În cazul constantei Constanta2 ea a fost 

folosită ca şi cum ar fi fost declarată direct în clasa C deoarece Constanta2 a fost definită
într-o  interfaţă.  

Dacă în cadrul unui pachet se utilizează un set de constante de către mai multe clase 
(componente ale pachetului) atunci este convenabil ca aceste constante să fie definite într-o 
interfaţă care nu mai conţine şi altceva în afară de setul de constante. Orice clasă care doreşte să
se folosească de setul de constante respectiv trebuie să implementeze această interfaţa unde ele 
sunt definite.  
Interfeţele pot avea sub-interfeţe aşa cum clasele pot avea subclase. O sub-interfaţă moşteneşte 
toate metodele abstracte şi constantele super-interfeţei sale şi poate defini noi metode abstracte
şi  noi constante. O deosebire foarte importantă între clase şi interfeţe rezidă în faptul că o
interfaţă poate extinde mai multe interfeţe, în vreme ce o clasă poate extinde numai o singură
clasă.  Exemplu:  
public interface Transforms extends Graphic,Zoom  
{  
 ....  
}  

O clasă care implementează o astfel de interfaţă (de fapt o sub-interfaţă) trebuie să


implementeze  toate metodele abstracte ale interfeţei şi toate metodele abstracte ale tuturor
super-interfeţelor  sale. 

3. Excepţii şi tratarea acestora în Java 


Termenul excepţie este o prescurtare pentru ”eveniment excepţional” şi poate fi definit
ca  un eveniment ce se produce în timpul execuţiei unui program şi care provoacă întreruperea 
cursului normal al execuţiei acestuia.  
Excepţiile pot apărea din diverse cauze şi pot avea nivele diferite de gravitate: de la erori fatale 
cauzate de echipamentul hardware până la erori ce ţin strict de codul programului, cum ar fi 
accesarea unui element din afara spaţiului alocat unui vector. In momentul când o asemenea 
eroare se produce în timpul execuţiei va fi  
generat un obiect de tip excepţie ce conţine:  
• informaţii despre excepţia respectivă; 
• starea programului în momentul producerii acelei excepţii. 

public class Exemplu {  


public static void main(String args[]) {  
int v[] = new int[10];  
v[10] = 0; //Exceptie !   
System.out.println("Aici nu se mai ajunge...");  

La rularea programului va fi generată o excepţie, programul se va opri la instrucţiunea care a 


cauzat excepţia şi se va afişa un mesaj de eroare de genul:  

"Exception in thread "main"   


java.lang.ArrayIndexOutOfBoundsException :10  
at Exceptii.main (Exceptii.java:4)" 

Crearea unui obiect de tip excepţie se numeşte aruncarea unei excepţii (”throwing an 
exception”). In momentul în care o metodă generează (aruncă) o excepţie sistemul de execuţie 
este responsabil cu găsirea unei secvenţe de cod dintr-o metodă care să o trateze. Căutarea se 
 

face recursiv, începând cu metoda care a generat excepţia şi mergând înapoi pe linia apelurilor 
către acea metodă. 
Secvenţa de cod dintr-o metodă care tratează o anumită excepţie se numeşte analizor de excepţie 
1

(”exception handler”) iar interceptarea şi tratarea ei se numeşte prinderea excepţiei (”catch the 
exception”). 
Cu alte cuvinte, la apariţia unei erori este ”aruncată” o excepţie iar cineva trebuie să o ”prindă” 
pentru a o trata. Dacă sistemul nu gaseşte nici un analizor pentru o anumită excepţie, atunci 
programul Java se opreşte cu un mesaj de eroare (în cazul exemplului de mai sus mesajul ”Aici 
nu se mai ajunge...” nu va fi afişat). 

Tratarea exceptiilor 
Tratarea excepţiilor se realizează prin intermediul blocurilor de instrucţiuni try, catch şi finally. 
O secvenţă de cod care tratează anumite excepţii trebuie să arate astfel: 

try { 
// Instructiuni care pot genera exceptii 

catch (TipExceptie1 variabila) { 
// Tratarea exceptiilor de tipul 1 

catch (TipExceptie2 variabila) { 
// Tratarea exceptiilor de tipul 2 

. . . 
finally { 
// Cod care se executa indiferent 
// daca apar sau nu exceptii 

  
1
In Java tratarea erorilor nu mai este o opţiune ci o constrângere. În aproape toate situaţile, o secvenţă de cod
care  poate provoca excepţii trebuie să specifice modalitatea de tratare a acestora.
 

Să considerăm următorul exemplu: citirea unui fişier octet cu octet şi afisarea lui pe ecran. Fără
a  folosi tratarea excepţiilor metoda responsabilă cu citirea fişierului ar arăta astfel: 

public static void citesteFisier(String fis) { 


FileReader f = null; 
// Deschidem fisierul 
System.out.println("Deschidem fisierul " + fis); 
f = new FileReader(fis); 
// Citim si afisam fisierul caracter cu caracter 
int c; 
while ( (c=f.read()) != -1) 
System.out.print((char)c); 
// Inchidem fisierul 
System.out.println("\\nInchidem fisierul " + fis); 
f.close(); 

Această secvenţă de cod va furniza erori la compilare deoarece în Java tratarea erorilor este 
obligatorie. Folosind mecanismul excepţiilor metoda citeste îşi poate trata singură erorile care 
pot surveni pe parcursul execuţiei sale. Mai jos este codul complte şi corect al unui program ce 
afişează pe ecran conţinutul unui fişier al cărui nume este primit ca argument de la linia de 
comandă. Tratarea excepţiilor este realizată complet chiar de către metoda citeste.
import java .io .*; 
public class CitireFisier { 
public static void citesteFisier ( String fis) { 
FileReader f = null ; 
try { 
// Deschidem fisierul 
System . out. println (" Deschidem fisierul " + fis); 
f = new FileReader (fis ); 
// Citim si afisam fisierul caracter cu caracter 
int c;
 

while ( (c=f. read ()) != -1) 


System . out. print (( char )c); 
} catch ( FileNotFoundException e) { 
// Tratam un tip de exceptie 
System . err. println (" Fisierul nu a fost gasit !"); 
System . err. println (" Exceptie : " + e. getMessage ()); 
System . exit (1); 
} catch ( IOException e) { 
// Tratam alt tip de exceptie 
System . out. println (" Eroare la citirea din fisier !"); 
e. printStackTrace (); 
} finally { 
if (f != null ) { 
// Inchidem fisierul 
System . out. println ("\ nInchidem fisierul ."); 
try { 
f. close (); 
} catch ( IOException e) { 
System . err. println (" Fisierul nu poate fi inchis !"); 
e. printStackTrace (); 




public static void main ( String args []) { 
if ( args . length > 0) 
citesteFisier ( args [0]) ; 
else 
System . out. println (" Lipseste numele fisierului !"); 

}
 

Blocul ” try ” contine instrucţiunile de deschidere a unui fişier şi de citire dintr-un fişier, ambele 
2

putând produce excepţii. Excepţiile provocate de aceste instrucţiuni sunt tratate în cele două
blocuri ”catch”, câte unul pentru fiecare tip de excepţie. Inchiderea fişierului se face în blocul 
”finally”, deoarece acesta este sigur că se va executa Fără a folosi blocul ”finally”, închiderea 
fişierului ar fi trebuit facută în fiecare situaţie în care fişierul ar fi fost deschis, ceea ce ar fi dus
la  scrierea de cod redundant. 

try { 
... 
// Totul a decurs bine. 
f.close(); 

... 
catch (IOException e) { 
... 
// A aparut o exceptie la citirea din fisier 
f.close(); // cod redundant 

O problemă mai delicată care trebuie semnalata în aceasta situaţie este faptul că metoda close, 
responsabilă cu închiderea unui fişier, poate provoca la rândul său excepţii, de exemplu atunci 
când fişierul mai este folosit şi de alt proces şi nu poate fi închis. Deci, pentru a avea un cod 
complet correct trebuie să tratăm şi posibilitatea apariţiei unei excepţii la metoda close. 

  
2
Obligatoriu un bloc de instruţiuni ”try” trebuie să fie urmat de unul sau mai multe blocuri ”catch”, în funcţie de 
excepţiile provocate de acele instrucţiuni sau (opţional) de un bloc ”finally”.
  

“Aruncarea” exceptiilor 
În cazul în care o metodă nu îşi asumă responsabilitatea tratării uneia sau mai multor excepţii pe
3

care le pot provoca anumite instrucţiuni din codul său atunci ea poate să ”arunce” aceste
excepţii  către metodele care o apelează, urmând ca acestea să implementeze tratarea lor sau, la
rândul lor,  să ”arunce” mai departe excepţiile respective. Acest lucru se realizează prin
specificarea în  declaraţia metodei a clauzei throws: 

[modificatori] TipReturnat metoda([argumente]) 


throws TipExceptie1, TipExceptie2, ... 

... 

În exemplul de mai sus dacă nu facem tratarea excepţiilor în cadrul metodei citeste atunci 
metoda apelantă (main) va trebui să facă acest lucru: 
import java .io .*; 
public class CitireFisier { 
public static void citesteFisier ( String fis) 
throws FileNotFoundException , IOException { 
FileReader f = null ; 
f = new FileReader (fis ); 
int c; 
while ( (c=f. read ()) != -1) 
System . out. print (( char )c); 
f. close (); 

public static void main ( String args []) { 
if ( args . length > 0) { 
try { 

3
O metodă care nu tratează o anumită exceţie trebuie obligatoriu să o ”arunce”.
 

citesteFisier ( args [0]) ; 


} catch ( FileNotFoundException e) { 
System . err. println (" Fisierul nu a fost gasit !"); 
System . err. println (" Exceptie : " + e); 
} catch ( IOException e) { 
System . out. println (" Eroare la citirea din fisier !"); 
e. printStackTrace (); 

} else 
System . out. println (" Lipseste numele fisierului !"); 

Observaţi că, în acest caz, nu mai putem diferenţia excepţiile provocate de citirea din fişier şi de 
inchiderea fişierului, ambele fiind de tipul IOException. 
De asemenea, inchiderea fişierului nu va mai fi facută în situatia în care apare o excepţie la 
citirea din fişier. Este situaţia în care putem folosi blocul finally fără a folosi nici un bloc catch:
public static void citesteFisier(String fis) 
throws FileNotFoundException, IOException { 
FileReader f = null; 
try { 
f = new FileReader(numeFisier); 
int c; 
while ( (c=f.read()) != -1) 
System.out.print((char)c); 

finally { 
if (f!=null) 
f.close(); 

}
 

Metoda apelantă poate arunca la rândul sâu excepţiile mai departe către metoda care a apelat-o
la  rândul ei. Această înlănţuire se termină cu metoda main care, dacă va arunca excepţiile ce pot 
apărea în corpul ei, va determina trimiterea excepţiilor către maşina virtuală Java. 

public void metoda3 throws TipExceptie { 


... 

public void metoda2 throws TipExceptie { 
metoda3(); 

public void metoda1 throws TipExceptie { 
metoda2(); 

public void main throws TipExceptie { 
metoda1(); 

Tratarea excepţiilor de către JVM se face prin terminarea programului şi afişarea informaţiilor 
despre excepţia care a determinat acest lucru. Pentru exemplul nostru, metoda main ar putea fi 
declarată astfel: 

public static void main(String args[]) 


throws FileNotFoundException, IOException { 
citeste(args[0]); 

Intotdeauna trebuie găsit compromisul optimîntre tratarea locală a excepţiilor şi aruncarea lor 
către nivelele superioare, astfel încât codul să fie cât mai clar şi identificarea locului în care a 
apărut excepţia să fie cât mai uşor de făcut. Aruncarea unei excepţii se poate face şi implicit prin 
instrucţiunea throw ce are formatul: throw exceptie, ca în exemplele de mai jos:
 

throw new IOException("Exceptie I/O"); 


... 
if (index >= vector.length) 
throw new ArrayIndexOutOfBoundsException(); 
... 
catch(Exception e) { 
System.out.println("A aparut o exceptie); 
throw e; 

Avantaje privind tratarea exceptiilor 


Prin modalitatea sa de tratare a excepţiilor, Java are următoarele avantaje faţă de mecanismul 
tradiţional de tratare a erorilor: 
• Separarea codului pentru tratarea unei erori de codul în care ea poate să apară.
• Propagarea unei erori până la un analizor de excepţii corespunzător. 
• Gruparea erorilor după tipul lor. 

In programarea tradiţională tratarea erorilor se combină cu codul ce poate produce apariţia lor 
producând aşa numitul ”cod spaghetti”. Să considerăm următorul exemplu: o funcţie care încarcă
un fişier în memorie: 

citesteFisier { 
deschide fisierul; 
determina dimensiunea fisierului; 
aloca memorie; 
citeste fisierul in memorie; 
inchide fisierul; 
}
 

Problemele care pot apărea la aceasta funcţie, aparent simplă, sunt de genul: ”Ce se întâmplă
dacă: ... ?” 
• fişierul nu poate fi deschis 
• nu se poate determina dimensiunea fişierului 
• nu poate fi alocată suficientă memorie 
• nu se poate face citirea din fişier 
• fişierul nu poate fi închis 

Un cod tradiţional care să trateze aceste erori ar arăta astfel: 

int citesteFisier() { 
int codEroare = 0; 
deschide fisierul; 
if (fisierul s-a deschis) { 
determina dimensiunea fisierului; 
if (s-a determinat dimensiunea) { 
aloca memorie; 
if (s-a alocat memorie) { 
citeste fisierul in memorie; 
if (nu se poate citi din fisier) { 
codEroare = -1; 

} else { 
codEroare = -2; 

} else { 
codEroare = -3; 

inchide fisierul; 
if (fisierul nu s-a inchis && codEroare == 0) { 
codEroare = -4;
 

} else { 
codEroare = codEroare & -4; 

} else { 
codEroare = -5; 

return codEroare; 
} // Cod "spaghetti" 

Acest stil de progamare este extrem de susceptibil la erori şi îngreunează extrem de mult 
înţtelegerea sa. În Java, folosind mecansimul excepţiilor, codul ar arata, schematizat, astfel: 

int citesteFisier() { 
try { 
deschide fisierul; 
determina dimensiunea fisierului; 
aloca memorie; 
citeste fisierul in memorie; 
inchide fisierul; 

catch (fisierul nu s-a deschis) 
{trateaza eroarea;} 
catch (nu s-a determinat dimensiunea) 
{trateaza eroarea;} 
catch (nu s-a alocat memorie) 
{trateaza eroarea} 
catch (nu se poate citi din fisier) 
{trateaza eroarea;} 
catch (nu se poate inchide fisierul) 
{trateaza eroarea;} 
}
  

Diferenta de claritate este evidentă. 

Propagarea unei erori se face până la un analizor de excepţii corespunzător. Să presupunem că


apelul la metoda citesteFisier este consecinţa unor apeluri imbricate de metode:  int metoda1()
{  
metoda2();   
... 

int metoda2() {  
metoda3;   
... 

int metoda3 {  
citesteFisier();  
... 

Să presupunem de asemenea că dorim să facem tratarea erorilor doar în metoda1. Tradiţional, 


acest lucru ar trebui făcut prin propagarea erorii produse de metoda citesteFisier până la 
metoda1:  

int metoda1() {  


int codEroare = metoda2();  
if (codEroare != 0)  
//proceseazaEroare;  
... 

int metoda2() {  
int codEroare = metoda3();  
if (codEroare != 0) 
  

return codEroare;  
... 

int metoda3() {  
int codEroare = citesteFisier();  
if (codEroare != 0)  
return codEroare;  
... 

După cum am vazut, Java permite unei metode să arunce excepţiile apărute în cadrul ei la un 
nivel superior, adică funcţiilor care o apelează sau sistemului. Cu alte cuvinte, o metodă poate să
nu îşi asume responsabilitatea tratării excepţiilor apărute în cadrul ei:  

int metoda1() {  


try {   
metoda2();   

catch (TipExceptie e) {  
//proceseazaEroare;  

... 

int metoda2() throws TipExceptie {  
metoda3();   
... 

int metoda3() throws TipExceptie {  
citesteFisier();  
... 
}
 

In Java există clase corespunzătoare tuturor excepţiilor care pot apărea la execuţia unui program. 
Acestea sunt grupate în funcţie de similarităţile lor într-o ierarhie de clase. De exemplu, clasa 
IOException se ocupă cu excepţiile ce pot apărea la operaţii de intrare/iesire şi diferenţiază la 
rândul ei alte tipuri de excepţii, cum ar fi FileNotFoundException, EOFException, etc. 
La rândul ei, clasa IOException se încadrează într-o categorie mai largă de excepţii şi anume 
clasa Exception. Radacia acestei ierarhii este clasa Throwable. 
Pronderea unei excepţii se poate face fie la nivelul clasei specifice pentru acea excepţie, fie la 
nivelul uneia din superclasele sale, în funcţie de necesităţile programului, însă, cu cât clasa 
folosită este mai generică cu atât tratarea excepţiilor programul îşi pierde din flexibilitate. 

try { 
FileReader f = new FileReader("input.dat"); 
/* Acest apel poate genera exceptie de tipul FileNotFoundException Tratarea ei 
poate fi facuta in unul din modurile de mai jos: 
*/ 

catch (FileNotFoundException e) { 
// Exceptie specifica provocata de absenta 
// fisierului ’input.dat’ 
} // sau 
catch (IOException e) { 
// Exceptie generica provocata de o operatie IO 
} // sau 
catch (Exception e) { 
// Cea mai generica exceptie soft 
} //sau 
catch (Throwable e) { 
// Superclasa exceptiilor 
}
  

Ierarhia claselor ce descriu exceptii 


Rădăcina claselor ce descriu excepţii este clasa Throwable iar cele mai importante subclase ale 
sale sunt Error, Exception şi RuntimeException, care sunt la rândul lor superclase pentru o serie 
întreagă de tipuri de excepţii. 

Erorile, obiecte de tip Error, sunt cazuri speciale de excepţii generate de funcţionarea anormală a 
echipamentului hard pe care rulează un program Java şi sunt invizibile programatorilor. Un 
program Java nu trebuie să trateze apariţia acestor erori şi este improbabil ca o metodă Java să
provoace asemenea erori.  
Excepţiile, obiectele de tip Exception, sunt excepţiile standard (soft) care trebuie tratate de către 
programele Java. După cum am mai zis tratarea acestor excepţii nu este o opţiune ci o 
constrângere. Excepţiile care pot ”scăpa” netratate descind din subclasa RuntimeException şi se 
numesc excepţii la execuţie.  
Metodele care sunt apelate uzual pentru un obiect excepţie sunt definite în clasa Throwable şi 
sunt publice, astfel încât pot fi apelate pentru orice tip de excepţie. Cele mai uzuale sunt:  •
getMessage - afişează detaliul unei excepţii; 
• printStackTrace - afişează informaţii complete despre excepţie şi localizarea ei;
• toString - metodă moştenită din clasa Object, care furnizează reprezentarea ca şir de
caractere a excepţiei. 
  

Excepţii speciale 
In general, tratarea excepţiilor este obligatorie în Java. De la acest principu se sustrag însă aşa 
numitele excepţii la execuţie sau, cu alte cuvinte, excepţiile care provin strict din vina 
programatorului şi nu generate de o anumită situaţie externă, cum ar fi lipsa unui fişier. Aceste 
excepţii au o superclasă comună RuntimeException şi în acesata categorie sunt incluse
excepţiile  provocate de:  
• operaţii aritmetice ilegale (împârţirea întregilor la zero); ArithmeticException •
accesarea membrilor unui obiect ce are valoarea null; NullPointerException •
accesarea eronată a elementelor unui vector. ArrayIndexOutOfBoundsException 
Excepţiile la execuţie pot apărea uriunde în program şi pot fi extreme de numeroare iar 
încercarea de ”prindere” a lor ar fi extrem de anevoioasă. Din acest motiv, compilatorul permite 
ca aceste excepţii să rămână netratate, tratarea lor nefiind însă ilegală. Reamintim însă că, în 
cazul apariţiei oricărui tip de excepţie care nu are un analizor corespunzător, programul va fi 
terminat.  

int v[] = new int[10];  


try {   
v[10] = 0;   
} catch (ArrayIndexOutOfBoundsException e) {  
System.out.println("Atentie la indecsi!");  
e.printStackTrace(); 
} // Corect, programul continua  
v[11] = 0;   
/* Nu apare eroare la compilare dar apare exceptie la executie si programul va
fi  terminat.  
*/  
System.out.println("Aici nu se mai ajunge..."); 

Impărţirea la 0 va genera o excepţie doar dacă tipul numerelor împărţite este aritmetic întreg. In 
cazul tipurilor reale (float şi double) nu va fi generată nici o excepţie, ci va fi furnizat ca rezultat 
o constantă care poate fi, funcţie de operatie, Infinity, -Infinity, sau Nan.
 

int a=1, int b=0; 


System.out.println(a/b); // Exceptie la executie ! 
double x=1, y=-1, z=0; 
System.out.println(x/z); // Infinity 
System.out.println(y/z); // -Infinity 
System.out.println(z/z); // NaN

Curs 5 – Tehnologii Java  


Structura programelor Java. Tipuri de date. Instructiuni. Citirea/Scrierea datelor 
formatate cu ajutorul clasei Scanner  

1. Fluxuri de date 

Majoritatea aplicatiilor necesita citirea unor informatii care se gasesc pe o sursa  externa sau
trimiterea unor informatii catre o destinatie externa. 
Informatia se poate gasi oriunde:  
⮚ intr-un fisier pe disc, 
⮚ in retea, 
⮚ in memorie 
⮚ sau in alt program 
si poate fi de orice tip: date primitive, obiecte, imagini, sunete, etc.  
Pentru a aduce informatii dintr-un mediu extern, un progam Java trebuie sa deschida un
canal de comunicatie (flux) de la sursa informatiilor (fisier, memorie, socket,  etc) si sa citeasca
secvential informatiile respective.  

Similar, un program poate trimite informatii catre o destinatie externa deschizand  un


canal de comunicatie (flux) catre acea destinatie si scriind secvential informatiile  respective.  
Indiferent de tipul informatiilor, citirea/scrierea de pe/catre un mediu extern  respecta
urmatorul algoritm:  

deschide canal comunicatie 


while (mai sunt informatii)  

citeste/scrie informatie; 

inchide canal comunicatie;

Pentru a generaliza, atat sursa externa a unor date cat si destinatia lor sunt vazute  ca fiind
niste procese care produc, respectiv consuma informatii. 

Definitii: 
Un flux este un canal de comunicatie unidirectional intre doua procese.  Un proces care
descrie o sursa externa de date se numeste proces producator.  Un proces care descrie o
destinatie externa pentru date se numeste proces consumator.  Un flux care citeste date se
numeste flux de intrare.  
Un flux care scrie date se numeste flux de iesire. 
Observatii: 
Fluxurile sunt canale de comunicatie seriale pe 8 sau 16 biti. 

1  
Fluxurile sunt unidirectionale, de la producator la consumator. Fiecare flux are un  singur
proces producator si un singur proces consumator. Intre doua procese pot exista  oricate fluxuri,
orice proces putand fi atat producator cat si consumator in acelasi timp,  dar pe fluxuri diferite.
Consumatorul si producatorul nu comunica direct printr-o interfata  de flux ci prin intermediul
codului Java de tratare a fluxurilor.  
Clasele si intefetele standard pentru lucrul cu fluxuri se gasesc in pachetul java.io.  Deci,
orice program care necesita operatii de intrare sau iesire trebuie sa contina  instructiunea de
import a pachetului java.io:  
import java.io.*;  
2. Clasificarea fluxurilor 

Exista trei tipuri de clasificare a fluxurilor:  


1) Dupa directia canalului de comunicatie deschis fluxurile se impart in: –
fluxuri de intrare (pentru citirea datelor) 
– fluxuri de iesire (pentru scrierea datelor) 
2) Dupa tipul de date pe care opereaza: 
– fluxuri de octeti (comunicarea seriala se realizeaza pe 8 biti) 
– fluxuri de caractere (comunicarea seriala se realizeaza pe 16 biti) 
3) Dupa actiunea lor: 
– fluxuri primare de citire/scriere a datelor (se ocupa efectiv cu citirea/scrierea datelor) –
fluxuri pentru procesarea datelor 

3. Intrari si iesiri formatate 

Incepand cu versiunea 1.5, limbajul Java pune la dispozitie modalitati simplificate  pentru
afisarea formatata a unor informatii, respectiv pentru citirea de date formatate de  la tastatura. 

3.1 Intrari formatate  

Clasa java.util.Scanner ofera o solutie simpla pentru formatarea unor informatii citite de
pe un flux de intrare fie pe octeti, fie pe caractere, sau chiar dintr-un obiect de tip  File. Pentru a
citi de la tastatura vom specifica ca argument al constructorului fluxul  System.in:  
Scanner s = Scanner.create(System.in); 
String nume = s.next(); 
int varsta = s.nextInt(); 
double salariu = s.nextDouble(); 
s.close();
3.2 Iesiri formatate  

Clasele PrintStream si PrintWriter pun la dispozitiie, pe langa metodele print, println care 
ofereau posibilitatea de a afisa un sir de caractere, si metodele format, printf  (echivalente) ce
permit afisarea formatata a unor variabile.  
System.out.printf("%s %8.2f %2d %n", nume, salariu, varsta); 
Formatarea sirurilor de caractere se bazeaza pe clasa java.util.Formatter.  

2  
Probleme rezolvate:  

Scrieti, compilati si rulati toate exemplele din acest laborator:  

1. Se citeste un numar n natural. Sa se calculeze suma cifrelor lui. 


Exemplu: Pentru n=124 obtinem s=7. 

package poo;  
import java.util.Scanner;  
public class lab_2_1 {  

public static void main(String[] args) {  


int n,s=0;  
System.out.print("dati numarul natural = "); 
n = new Scanner(System.in).nextInt();  
while(n!=0)  

 s+=n%10;  
 n/=10; 

System.out.println ("Suma cifrelor numarului date este egala cu = " + s); } 


2. Se citeste un numar n natural. Sa se afiseze numarul obtinut prin inversarea cifrelor lui.
Exemplu: Pentru n=841 obtinem m=148 

package poo;  
import java.util.Scanner;  
public class lab2_2 {  

public static void main(String[] args) {  


int n;  
System.out.print("dati numarul natural = "); 
n = new Scanner(System.in).nextInt();  
int m=0;  
while(n!=0)  

m = m*10+n%10;  
n/=10; 

 System.out.println ("Numarul obtinut prin inversarea cifrelor este egal cu = " +  m);  
}

3  

3. Se citeste un numar n natural. Sa se afiseze descompunerea sa in factori primi.


Exemplu: Pentru n=12 obtinem 2 *3. 
2

package poo;  
import java.util.Scanner;  
public class lab2_3 {  

public static void main(String[] args) {  


int n;  
System.out.print("dati numarul natural = "); 
n = new Scanner(System.in).nextInt();  
int i=2, fm;  
do{ 
fm=0;  
while(n%i==0)  

fm++;  
n=n/i;  

if(fm!=0) System.out.println (i+" la puterea "+fm);  
i++;  
}while(n>=1);  

4. Se citeste un numar n natural. Sa se afiseze toti divizorii numarului dat.


Exemplu: Pentru n=12 obtinem {1,2,3,4,6,12}. 

package poo;  
import java.util.Scanner;  
public class lab2_4 {  

public static void main(String[] args) {  


int n;  
System.out.print("dati numarul natural = "); 
n = new Scanner(System.in).nextInt();  
int i=1;  
while(i<=n)  

 if(n%i==0) System.out.print(i+", "); 
i++;  


}

4  
5. Se citeste un numar n natural. Sa se verifice daca este numar prim sau nu. Exemplu: Pentru
n=12 obtinem NU ESTE numar PRIM, iar pentru n=11 obtinem ESTE numar PRIM. 

package poo;  
import java.util.Scanner;  
public class lab2_5 {  

public static void main(String[] args) {  


int n;  
System.out.print("dati numarul natural = "); 
n = new Scanner(System.in).nextInt();  
int i=2;  
boolean prim=true;  
while(i<=n/2)  

 if(n%i==0) prim=false;  
i++;  

if(prim==true) System.out.println(n+" ESTE numar PRIM! "); 
else System.out.println(n+" NU ESTE numar PRIM! "); 

Probleme propuse spre rezolvare  

Problema 1 :  
Se dau trei numere nenule a,b si k. Sa se verifice daca fractia a/b se simplifica prin  k. In
caz afirmativ se va afisa si fractia simplificata. 

Problema 2 :  
Sa se verifice daca trei numere naturale a,b si c sunt pitagorice sau nu. Numim  numere
pitagorice, trei numere care indeplinesc una din conditiile a*a=b*b+c*c,  b*b=a*a+c*c,
c*c=a*a+b*b.  

Problema 3 :  
Se citesc trei numere a,b,c. Sa se verifice daca aceste numere (puse in orice  ordine) sunt
in progresie aritmetica si sa se afiseze ratia progresiei in caz afirmativ.  

Problema 4 :  
Se dau trei numere a,b,c. Sa se verifice daca pot reprezenta laturile unui triunghi.  In caz
afirmativ sa se precizeze ce tip de triunghi este: echilateral, isoscel, dreptunghic  sau oarecare.  

Problema 5 :  
Un punct in plan este dat prin coodonatele sale (x,y). Sa se scrie un program care 
determina daca punctul este in origine, intr-un cadran (1,2,3 sau 4), sau pe una din  semiaxe (Ox,
Ox’, Oy, Oy’).

5  
Exemplu:  
(1,1) – cadranul 1  
(0,3) – axa Oy  
(-2,4)- cadranul 2  

Problema 6 :  
Sa se calculeze valoarea functiei matematice f(x), pentru o valoare a lui x  introdusa de la
tastatura:  
 x*x+1, pentru x <=-3  
f:R->R, f(x) = x – 2, pentru -3<x<3  
 x*x - 4*x + 5, pentru x>=3  

Problema 7 :  
Sa se determine cel mai mare divizor comun (c.m.m.d.c.) si cel mai mic multiplu  comun
(c.m.m.m.c.) a doua numere intregi citite de tastatura. Cmmdc se va calcula  folosind cele doua
variante:  
⮚ algoritmul lui Euclid 
⮚ folosind relatia de mai jos: 
cmmdc(a-b,b), daca a > b  
cmmdc(x,y) = cmmdc(a,b-a), daca a < b  
a, daca a = b  

Problema 8 :  
Sa se verifice daca un numar este numar perfect sau nu. Spunem ca un numar este  numar
perfect daca este egal cu suma divizorilor lui, mai putin el insusi. (Exemplu:  numarul 6 este
perfect, deoarece este egal cu suma divizorilor sai 1,2,3).  

Problema 9 :  
Se citesc n numere intregi. Sa se determine minimul si maximul lor.  

Problema 10 :  
Sa se verifice daca un numar este palindrom sau nu. Spunem ca un numar este 
palindrom daca este egal cu rasturnatul sau (adica numarul format din cifrele de la  dreapta la
stanga ale numarului initial – exemplu : n = 25652). 
6  

5. Interfeţe grafice în Java 

Pachetul de clase Swing reprezintă soluţia furnizată de Sun pentru crearea unor
interfeţe  utilizator grafice complet portabile pe orice platformă.  
Pachetul Swing este un subset JFC (Java Foundation Classes) şi constă dintr-o serie de 
componente vizuale care extind (îmbunătăţesc) componentele AWT, şi furnizează noi facilităţi 
precum tabele şi arbori. Structura de clase din Swing este asemănătoare cu cea din AWT 
(Abstract Window Toolkit), în sensul că toate componentele interfeţei grafice sunt derivate dintr 
un singur părinte numit JComponent (care este derivat din clasa AWT Container).  Printre
îmbunătăţirile aduse de Swing (altele faţă de noile componente adăugate) se  numără: 

∙ Posibilitatea de a avea componente su forme nerectangulare; 


∙ Accesibilitate pentru persoanele cu deficienţe; 
∙ Prezentare şi comportare pentru interfaţă independentă de platformă; 
Apariţia pachetului Swing a fost determinata şi de următoarea concepţie: pentru implementarea 
unor lucruri simple este nevoie de scrierea de cod puţin, iar pentru lucruri mai complexe 
mărimea codului creşte proporţional. În Swing se folosesc convenţii logice pentru denumirea 
claselor, a metodelor etc; odată învăţate acestea va rezulta o crestere a vitezei de lucru. 

Interfeţe grafice utilizator  


Interactiunea om-calculator (Human-Computer Interaction – HCI) este stiinta care se 
ocupă cu proiectarea, evaluarea şi implementarea sistemelor de calcul interactive destinate 
uzului uman, şi cu studiul fenomenelor importante existente în acest context.  
Din perspectiva ştiintei calculatoarelor, accesul este pus pe interacţiune; mai precis se 
referă la interacţiunea uneia sau mai multor persoane cu una sau mai multe maşini de calcul. 
Luând în considerare noţiunea de maşină, putem avea de-a face în locul clasicelor staţii  de lucru
cu maşini de calcul încorporate (embedded), ca parţi ale bordurilor avioanelor sau ale 
obişnuitelor cuptoare cu microunde. Tehnicile de proiectare a interfeţelor acestor dispozitive
sunt  similare celor de proiectare a interfeţelor grafice utilizator ale unei staţii de lucru.  Având în
vedere înţelesul noţiunii de uman, din punctul de vedere al specialistului în  ştiinţa
calculatoarelor, s-ar putea crede ca persoanele care interacţionează cu calculatoarele sunt 
motivate, deţin suficiente cunoştinţe despre acestea şi sunt dispuse să treacă peste neajunsurile 
 

interfeţelor. O scurtă privire în jurul nostru ne arată că utilizatorii de calculatoare pot fi de la 
foarte tineri la utilizatori în vârstă, de la utilizatori frecvenţi la utilizatori ezitanţi sau de la 
utilizatori perfect sănătoşi la utilizatori cu deficienţe. Mai mult în ciuda multelor talente de care 
dispunem avem şi o multime de limitări de care proiectanţii de interfeţe trebuie să tină cont. De 
pildă, atenţia şi motivaţia sunt oscilante, suntem uşor de păcălit de iluzii optice şi/sau auditive, 
învătăm încet, obosim şi devenim nervoşi. De aceea trebuie privit tandemul om-maşină ca un 
întreg a cărui bună funcţionare depinde atât de bună funcţionare a maşinii cât şi de erorile 
umane.  
În ceea ce priveşte interfaţa, cele mai multe dintre cele considerate foarte bune, rezolvă
corect probleme simple cum ar fi procesarea textelor. Însă când începem să vorbim de sisteme 
complexe, cum ar fi gestiunea unor cantităţi uriaşe de informaţie sau controlul traficului aerian, 
suntem încă neajutoraţi.  
Sunt bine înţelese multe aspecte ale interacţiunii om-calculator. Dispunem de o 
taxonomie care pune ordine la nivelul activităţilor din acest domeniu. Există patru subiecte 
(topics) principale (Figura 1.1):  
Utilizarea calculatoarelor şi probleme specifice ale acestora (Use and Context of 
Computers). Se referă la integrarea calculatoarelor în mediile sociale, utilizarea lor pentru 
rezolvarea problemelor din diverse domenii şi la îmbunătăţirea relaţiei om-calculator.  
Caracteristici specifice fiintelor umane (Human Characteristics). Sunt studiate 
aspecte referitoare la procesarea informaţiei de catre fiinţele umane (memorie, percepţie, atenţie, 
capacitatea de a rezolva probleme, învăţare, motivaţie, modele conceptuale, diversitate etc.), la 
limbaj şi comunicare (sintaxa, semantica, limbaje specializate etc.) şi la ergonomie
(caracteristici  fiziologice şi antropometrice, interacţiunea cu mediul de lucru etc.)  
Sisteme de calcul şi arhitecturi de interfaţă (Computer System and Interface 
Architecture). Se ocupă cu componentele hardware şi software, specializate în interacţiunea cu 
utilizatorii umani (dispozitive de intrare-ieşire, tehnici si tipuri de dialog, etc.)  
Procesul de dezvoltare (Development Process). Are în vedere modul de abordare al 
proiectării, uneltele şi tehnicile de implementare, metodele de evaluare şi studiul proiectelor de 
succes care vor servi ca model. 
 
Fig. 1.1: Interacţiunea Om-Calculator 

Concluzionând, interfaţa utilizator este partea unei aplicaţii software care permite 
utilizatorului:  
∙ să interacţioneze cu calculatorul  
∙ să-şi îndeplinească îndatoririle sau sarcinile ce-i sunt cerute (task).  

Interfeţele utilizator (IU) au devenit obiect de studiu pentru că ele fac parte aproape din 
orice lucru (nu sunt neapărat apanajul produselor software). De asemenea, IU nereuşite (Figurile 
1.2, 1.3) costă mulţi bani şi nu de puţine ori chiar vieţi omeneşti. IU bune sunt greu de obţinut 
chiar dacă aplicăm întocmai ce spune teoria, pentru că fiinţele umane nu sunt predictibile. IU 
sunt realizate de către echipe de proiectanţi care includ specialişti în design grafic, studiul 
interacţiunilor, elaborarea de documentaţie tehnică, marketing, testare şi nu în ultimul rând în 
ingineria software. 
 
Fig. 1.2: IU proastă. Utilizarea greşită a elementelor de control
checkbox

Fig. 1.3: IU proastă.  


Utilizarea greşită a metaforei video-recorder pentru controlul imprimantei. Butoanele de 
STOP şi PAUSE au sens, însă ce semnificaţie are „rebobinarea” REWIND.  Nu toate IU se
“bucură” de critici. Iată câteva exemple de realizări valoroase (Figurile  1.4, 1.5, 1.6). 
 

Fig. 1.4: Posibilitatea de a vedea conţinutul fişierului selectat prin activarea opţiunii 
„Preview”. Astfel utilizatorul nu este nevoit să încerce identifcarea imaginii pe baza numelui 
fişierului. 
Fig. 1.5: Afişarea dimensiunilor imaginii selectate ajută la o identificare mai precisă a 
acesteia, dacă există fişiere imagine cu acelaşi conţinut însă de dimensiuni diferite

Fig. 1.6: Conceptul de “fişiere recent utilizate”. Introducerea acestuia se bazează pe 
observaţia că majoritatea utilizatorilor este înclinată să deschidă unul din ultimele fişiere 
procesate.
 

Interfaţa cu utilizatorul, sau UI (User Interface), reprezintă totalitatea mecanismelor care 


permit interacţiunea dintre o aplicaţie şi utilizatorii ei. O particularizare a interfeţei cu
utilizatorul  o reprezintă interfaţă grafică, sau GUI (Graphic User Interface), care se referă strict
la  comunicarea vizuală dintre utilizator şi aplicaţie. Se poate spune că interfaţa grafică este
„modul  de comunicare” dintre utilizatori şi aplicaţia care le este destinată.  
Adesea aplicaţiile sunt percepute de către utilizatorii finali doar prin intermediul 
interfeţelor acestora şi de aceea o interfaţă realizată prost poate compromite întreaga aplicaţie 
(chiar dacă funcţional aceasta este foarte bine realizată). În acest sens, o problemă în procesul de 
realizare a unei interfeţe grafice o reprezintă posibilitatea ca utilizatorul să nu înţeleagă cum să o 
folosească după ce a fost creată. Acest lucru se întâmplă mai ales datorită faptului că, în cele mai 
multe cazuri, utilizatorii unei aplicaţii (destinatarii aplicaţiei) nu sunt si programatori având 
intenţia de a crea interfaţa conform propriului model mental, diferit de cel al utilizatorilor. Din 
acest motiv, dezvoltatorii aplicaţiei trebuie să intuiească modalităţile prin care utilizatorii 
modelează conceptual diferitele funcţionalităţi sau, în general, aspecte ale aplicaţiei, şi să 
încorporeze aceste modele în viitoarea interfaţă grafică. Metoda folosită în acest proces este 
analogia concretizată prin metaforă. Acolo unde analogiile sunt greu de realizat, sunt folosite 
idiomurile care sunt convenţii între creatorii şi utilizatorii interfeţei, usor de reţinut de aceştia
din  urmă. De exemplu funcţia de ştergere dintr-o aplicaţie poate fi mai usor modelată de către 
utilizatori ca fiind aruncarea unui obiect nefolositor la coş. Astfel pentru a sterge un obiect 
(reprezentat vizual), utilizatorul îl trage dreasupra coşului de gunoi (trash), reprezentat şi el 
grafic de către un icon. Acest mod de a vedea lucrurile poarta numele de „înglobarea semanticii 
în sintaxă”, deoarece se încearcă o flosire a simblurilor care sunt mai uşor de înţeles şi de
reţinut.  Alt exemplu în acest sens este folosirea de iconuri in barele de meniuri, care ascund
printr-o  reprezentare grafică uneltele care se vor folosi după selectarea lor (spre exemplu o lupa 
(reprezentată grafic) folosită ca unealtă pentru a marii sau micşora o imagine). Se poate observa 
însă că există un număr limitat de astfel de metaforeşi simboluri care se repetăîn mai toate 
aplicaţiile şi pe toate platformele. 
Din puncul de vedere al ingineriei programării, momentul în care se pune cu adevarat 
problema interfeţei cu utilizatorii este în procesul de proiectare arhitecturală a aplicaţiei, dar 
creatorii aplicaţiei vor face apreceri chiar în procesul de analiză a cerinţelor utilizatorilor. Aşa 
cum se realizează un plan al produsului informatic, se recomandă realizarea planului interfeţei
cu 
 

utilizatorul prin care se clarifică cum va arăta interfaţa grafică înainte de a trece la
implementarea  efectivă a acesteia. 

Etapele priectării interfeţelor utilizator 


În procesul de proiectare a intefeţelor grafice se poate folosi prototipizarea, care 
presupune realizarea de prototipuri ale interfeţei grafice (eventual, fără funcţionalităţi, care se 
pot adăuga ulterior), prototipuri care sunt evaluate şi reactualizate, dacă este cazul, pe întreg 
parcursul dezvoltării aplicaţiei. Prototipurile interfeţei grafice clarifică interactiunile aplicaţiei
cu  utilizatorii sau cu alte aplicaţii, respectiv funcţionalităţile aplicaţiei. 
Proiectarea IU este dirijată de cerinţe. Întrebarea la care trebuie să răspundem este “La ce 
serveşte cutare lucru?” şi nu “Cum va fi implementat un lucru?”. Pentru organizarea proiectării 
se cunosc mai multe modele din care vom prezenta două: modelul cascadă (Fig. 2.1), respectiv 
modelul iterativ (Fig. 2.2).  
Modelul iterativ are un mare avantaj; utilizarea acestuia duce la descoperirea eventualelor erori 
la nivelul specificaţiilor mult mai devreme, economisindu-se astfel resurse. 
 

Fig. 2.2: Modelul iterativ 

Proiectarea IU este una dirijată de utilizator. Asta înseamnă că se realizează avându-se în vedere 
cerinţele utilizatorului şi nu pe cele ale producătorului. Proiectarea dirijată de utilizator are ca 
temă centrală sloganul “Cunoaste-ti utilizatorul!”. Pentru aceasta este important să le ştim 
abilităţile cognitive (perceptie, memorie etc.), organizatorice si sa îi implicam în proiect pe toata 
durata de realizare a acestuia.  
Un ciclu de proiectare include urmatoarele etape:  
∙ Analiza task-urilor  
∙ Realizarea prototipului  
∙ Evaluarea  
∙ Implementarea  
∙ Iterarea  
IU trebuie sa se integreze absolut natural în lumea si activitatea utilizatorului. De aceea este 
necesara aceasta etapa în care echipa de proiectanti:  
∙ observa practicile curente, existente la locul de munca al utilizatorului 
 
∙ creaza exemple si scenarii de utilizare pentru interfata ce va fi  proiectata 
∙ probeaza idei noi, chiar înainte ca interfta sa fie implementata.  

Informatiile culese în aceasta etapa sunt extrem de importante. Examinati de exemplu, influenta 
pe care o are nivelul de pregatire al utilizatorilor asupra interfetei.  
În aceasta etapa echipa de proiectanti va construi o macheta a produsului. Aceasta se poate 
realiza fie prin intermediul modelarii Low-Fi (Low-Fidelity), care presupune construirea unei 
machete utilizând colaje de hârtie colorata si inscriptionata, desene sau succesiuni de slide-uri, 
fie prin utilizarea de unelte interactive (Visual Basic, HyperCard etc.)  
Indiferent cât de atent se desfasoara procesul de analiza, experienta demonstreaza ca anumite 
probleme nu apar decât în momentul în care testarea se face de catre utilizatori. Pentru ca 
utilizatorii reali ai produsului nu sunt disponibili întotdeauna, se efectueaza astfel de teste si 
evaluari cu ajutorul unor potentiali utilizatori, pe care îi vom numi participanti. Dintre metodele 
de evaluare amintim exprimarea verbala a modului de gândire (think aloud) sau prelucrarea 
informatiilor de sinteza.  

Implementarea trebuie facuta astfel încât orice eventuala ajustare a proiectului sa nu implice 
modificari majore în secventele de program. Drept urmare, codul obtinut trebuie sa fie modular 
si în general, sa respecte toate regulile elaborate pentru scrierea de programe. Pentru 
implementare se utilizeaza pachete pentru dezvoltarea de programe care au si facilitati de 
realizare a interfetelor (Visual Basic, Visual C++, Java, Deplhi etc.).  

Evaluarea duce aproape întotdeauna la relevarea de probleme legate de proiect. Proiectantii 


trebuie sa efectueze corectiile de rigoare, revizuind proiectul si implementarea, dupa care 
urmeaza o noua evaluare. Succesiunea de operatii “Proiectare-Evaluare-Implementare” 
reprezinta în fapt, procesul de iterare. Acest proces se opreste în momentul în care sunt atinse 
obiectivele privind usurinta utilizarii interfetei. 
 

Java Foundation Classes (JFC) 


Java Foundation Classes reprezintă un grup de API-uri (Application Programming Interface) 
incluse în platforma J2SE (Java 2 Standard Edition) care permit dezvoltatorilor să creeze 
interfeţe grafice (GUI) aplicaţiilor Java. Java Foundation Classes consta din cinci pachete:
AWT,  SWING, Accessibility, Java 2D si Drag and Drop, care se întrepătrund, neavând in
general o  existenţă de sine stătătoare. Java 2D a devenit o parte integrantă a AWT, Swing este
construit  peste AWT (se asteaptă ca Swing să se îmbine mult mai puternic cu AWT în
următoarele  distribuţii Java), suportul Accessibility este construit în Swing, iar API-ul Drag and
Drop este  integrat în AWT şi Swing. API.-urile Java 2D şi Drag and Drop sunt disponibile doar
pentru  platforma Java 2.  

∙ AWT (Abstract Window Toolkit) este o componentă a JFC proiectată pentru a oferii  suport
pentru crearea interfeţelor grafice folosind modul grafic. Oferă o ierarhie de clase  suficiente
pentru proiectarea unei interfeţe grafice nesofisticate pentru un applet sau o  aplicaţie. AWT
reprezintă nucleul JFC şi fundamentul pe care s-a construit pachetul  Swing; 
∙ Swing reprezintă un set vast de componente grafice, de la cele foarte simple, cum ar fi 
etichetele sau butoanele, la cele foarte complexe ca, de exemplu, tabelele sau arborii.  Oferă
independenţă faţă de platforma peste care este instalată maşina virtuală şi  posibilitatea
peronalizării aspectului interfeţei şi a comportamentului acesteia la actiunile  utilizatorilor; 
∙ Accessibility reprezintă modalitatea de a oferii ajutor utilizatorilor cu deficienţe, cum ar  fi
posibilitatea de a descifra interfaţa folosind mesaje vocale, Braille sau dispozitive  exterioare
ataşate calculatorului. Accessibility oferă informaţiile de care au nevoie  programatorii care
scriu soft pentru astfel de dispozitive auxiliare sau look-and-fell-uri.  Pachetul
javax.accessibility este cel care stă la originea acestui sistem, dar cei care scriu  programe
obişnuite nu au nevoie sa-l folosească ; 
∙ Java 2D API reprezintă API-ul care permite utilizatorilor să încorporeze în aplicaţiile lor 
grafică 2D complexă, text şi imagini; 
∙ Drag and Drop oferă posibilitatea de a realiza operaţii de drag and drop între aplicaţia  curentă
şi aplicaţiile native, sau între două aplicaţii Java. La originea acestei facilitaţi stau 
 

pachetele java.awt.dnd şi java.awt.datatransfer, dar în general nu este nevoie de folosirea 


lor de către programatori, deoarece sistemul drag and drop funcţionează implicit. 

Diferenţa majoră dintre Swing si AWT este aceea că, spre deosebire de AWT, componentele 
Swing sunt scrise 100% în Java având ca bază API-ul JDK 1.1, necontinând cod nativ 
(dependent de sistemul de operare). Aceasta înseamnă că butoanele Swing, spre exemplu, vor 
arăta şi se vor comporta identic pe platformele Macintosh, Solaris, Linux sau Windows. Spre 
deosebire, butoanele AWT luau mereu aspectul platformei pe care rula aplicaţia. Deasemenea, 
prin această independenţă faţă de platformă se obţine o îmbunătăţire a vitezei de execuţie a 
aplicaţiilor având interfeţe realizate în Swing. 
API-ul JFC a fost perfecţionat şi optimizat pe măsura aparoţiei de noi distribuţii ale platformei 
Java şi îl regăsim ca parte integrantă în platforma Java 2 Standard Edition versiunile 1.2, 1.3, 1.4 
şi următoarele. Dacă se va folosi vechiul JDK 1.1 va trebui să se instaleze şi suportul pentru
JFC,  spre exemplu, distribuţia JFC 1.1 care se poate găsi la adresa: java.sun.com/products/jfc.
Unele  clase şi metode s+au învechit (deprecated) şi s-a renunţat la folosirea lor efectivă pe
parcursul  apariţiei noilor distribuţii de platforme Java, din cauza ineficienţei sau greşelilor pe
care le  conţineau. Pentru o listă detaliată a acestora, se poate consulta javadoc-ul la sectiunea 
Deprecated. 
În particular, librăria Swing se poate folosi în aplicaţii dezvoltate pentru platforme care suportă 
JFC. Prin intermediul unui plug-in pentru browserele Web (purtând numele Java Plug-in) se
poat  crea interfeţe folosind Swing şi pentru applet-uri Java.  
 
 

Modelul MVC(Model View Controller) 


Model View Controller este filosofia (nivelul conceptual) clasică pentru proiectarea interfeţelor 
grafice în limbajele obiectuale care datează din anii 1970. Reprezintă structurarea
componentelor  în trei părţi: un model, o vizualizare şi un controler (controller), fiecare avand
propria utilitate şi  funcţionalitate şi interacţionând cu celelalte părţi, aşa cum se poate observa în
figura care  urmează: 

 C 

 V M

Componenta devine un mediator între model, vizualizare şi controler. Unul dintre avantajele 
acestei modalităţi de a structura componentele este acela că ne permite să personalizăm look
and-feel-ul unei componente fără a modifica modelul. Putem să asociem unui acelaşi model
(deci  aceloraşi date) mai multe moduri de vizualizare chiar în acelaşi timp. 
Rolurile parţilor unei componente sunt prezentate mai jos: 
∙ Modelul păstrează în general datele cărora se doreşte a li se da o vizualizare numindu-se,  în
acest caz, modele de date (data model). Spre exemplu, pentru o componentă text,  datele
reprezintă textul ce se vrea vizualizat. Modelele comunică indirect cu vizualizarea şi
controlerul prin intermediul sistemului de evenimente şi nu deţin referinţela nici una  din
acestea. Pot exista modele care să nu ţină neapărat datele componentei, ci stări ale 
vizualizării grafice pentru componentă (GUI- state). Un exemplu în acest sens ar putea fi  un
model pentru o listă care încapsulează modul de selecţie sau, pentru un buton, un  model care
încapsulează starea butonului: armat, apăsat etc. În cazul acestui ultim tip de  
 
modele, componentele dispun de metode pentru manipularea stării lor fără a face apel la 
modele. Trebuie precizat că deseori modelele încorporează atât datele componentei, cât
şi  stări ale vizualizării componentelor. 
∙ Vizualizarea este reprezentarea vizuală a modelului descriind pentru componentă modul  cum
arată (look). Vizualizarea este responsabilă pentru a păstra pe ecran reprezentarea 
actualizată, primind în acest scop mesaje de la model şi controler. 
∙ Controlerul ia datele de intrare ale utilizatorilor din vizualizare şi le transformă în  schimbări
corespunzătoare în model. Controlerul este astfel responsabil pentru a  determina când şi cum
va reacţiona componenta la evenimentele de intrare dintre  dispozitivele de intrare cum ar fi
tastatura şi mouse-ul. În acest sens reprezintă  comportamentul (feel) componentei. 

Interfețe Grafice
in Java 
Human Machine Interface 
Modalitățile prin care
un sistem (software) 
interacționează cu
utilizatorii săi (umani). 
Linia de comandă 
Grafice (Graphical
User Interface -
GUI) Tactile (Touch
User Interface - TUI)
Multimedia (voce,
animație, etc.) 
Inteligente
(recunoașterea
gesturilor, 
conversaționale,
etc)
Interfețe Grafice
in Java
Comunicarea vizuală între un
program și utilizatori. 
AWT(Abstract Windowing
Toolkit) 
Swing - parte din JFC
(Java Foundation Classes)
SWT
(IBM) 

Java FX 
XUL 
... 

Java 2D 

Java 3D 
Etapele creării
unei aplicații
cu  interfață

grafică 
Design 

Crearea suprafețelor
de afișare
(containere) – Crearea
și asezarea
componentelor 
Funcționalitate 

Definirea unor acțiuni 

“Legarea” componentelor de
acțiuni 

”Ascultarea” și
tratarea
evenimentelor
Considerente 

Programatic – Declarativ –
Vizual 

Separare dintre nivelul GUI și
logica aplicației
Modelul AWT(Abstrat
Window Toolkit) 
import java.awt.*; 
public class ExempluAWT1 { 
 public static void main (String
args []) { 
 // Crearea ferestrei -
un obiect de tip Frame 
Frame f = new Frame("O
fereastra"); 

 // Setarea modului de


dipunere a componentelor
f.setLayout (new
FlowLayout()); 

 // Crearea a doua butoane 


 Button b1 = new Button("OK"); 
 Button b2 = new Button("Cancel"); 
 // Adaugarea

butoanelor 
f.add(b1); 
f.add(b2); 
f.pack(); 

 // Afisarea fereastrei 


f.setVisible(true); 
 } 
}


Button 
Componente AWT ✔
List 

Canvas 

Checkbox 

CheckBoxGroup Choice  ✔


Container 

Label 

Scrollbar 

TextComponent TextField 


TextArea 

Dependente de sistemul de
operare (peer )
Infrastructura
AWT 

Component 

proprietăți comune tuturor
componentelor 
(location, x, y, size,
height, width, bounds, 
foreground, background,
font, visible, enabled,...) 

Container 

metode comune tuturor
containerelor 

LayoutManager 
interfața pentru orice

gestionar de poziționare ●

AWTEvent 

superclasa pentru evenimente
Ferestre și
panel-uri 
Frame f = new Frame("O fereastra"); 
// Adaugam un buton direct pe fereastra 
f.add(new Button("Hello")); 

// Adaugam doua componente pe un panel 


Panel panel = new Panel(); 
panel.add(new Label("Nume:"));  
panel.add(new TextField()); 

// Adaugam panel-ul pe fereastra 


// si, indirect, cele doua componente 
f.add(panel); 

class Fereastra extends


Frame { 
 // Constructorul 
 public Fereastra(String
titlu) { 
 super(titlu); 
 ... 
 } 

... 
Fereastra f = new Fereastra("O
fereastra"); 
f.setVisible(true);

Gestionarea
poziționării 
import java.awt.*; 
public class TestLayout { 
 public static void main ( String args []) { 

 Frame f = new Frame("Grid Layout"); 


 f.setLayout (new GridLayout (3, 2)); 
 Button b1 = new Button (" Button 1"); 
 Button b2 = new Button ("2"); 
 Button b3 = new Button (" Button 3"); 
 Button b4 = new
Button ("Long - Named
Button 4");  Button b5
= new Button (" Button
5"); 
 f.add(b1); f.add (b2); f.
add(b3); f.add(b4);
f.add(b5);  f.pack (); 
 f.setVisible(true); 
 } 

 Frame f = new Frame("Flow Layout"); 


 f.setLayout (new FlowLayout ());

LayoutMana
ger 
Un gestionar de
poziționare este un
obiect care  controlează
dimensiunea și aranjarea
(poziția) componentelor
unui container. 
Fiecare obiect de tip Container
are asociat un gestionar  de
poziționare. 
Toate clasele care instanțiaza
obiecte pentru gestionarea 
poziționării implementează
interfață LayoutManager. 
La instanțierea unui
container se creează implicit
un  gestionar de poziționare
asociat acestuia: 

ferestre: BorderLayout 

panel-uri: FlowLayout
Gestionari de
poziționare
“clasici” FlowLayout,
BorderLayout,
GridLayout, CardLayout,
GridBagLayout

Metoda setLayout 
  container.setLayout(new
FlowLayout()); 

Dimensionarea componentelor 
 preferredSize, minimumSize,
maximumSize 

Poziționarea absolută 
 container.setLayout(null); 
 Button b = new Button("Buton"); 
 b.setSize(10, 10);  
 b.setLocation (0, 0); 
 container.add(b); 

BorderLay
out 
import java.awt .*; 
public class TestBorderLayout { 
 public static void main ( String args
[]) { 

 Frame f = new
Frame (" Border
Layout ");  //
Apelul de mai
jos poate sa
lipseasca 
f.setLayout (new
BorderLayout()); 

 f.add(new Button(" Nord "),


BorderLayout.NORTH ); 
f.add(new Button(" Sud"),
BorderLayout.SOUTH ); 
f.add(new Button(" Est"),
BorderLayout.EAST ); 
f.add(new Button(" Vest "),
BorderLayout.WEST ); 
f.add(new Button(" Centru
"), BorderLayout.CENTER ); 
f.pack (); 
 f.setVisible(true); 
 } 
}

GridbagLay
out 
GridBagLayout
gridBag = new
GridBagLayout();
container.setLay
out(gridBag); 

GridBagConstraints c
= new
GridBagConstraints()
; //Specificam
restrictiile 
c.fill =
GridBagCons
traints.HOR
IZONTAL;
c.gridx =
0; 
c.gridy = 0; 

 • gridx, gridy 
 • gridwidth, gridheight 
 • fill 
 • insets 
 • anchor 
 • weigthx, weighty 

. . . 

gridBag.se
tConstrain
ts(compone
nta, c);
container.
add(compon
enta);

Interacțiun
ea cu
utilizatorul
Event-Driven
Programming

Eveniment: apăsarea
unui buton,
modificarea 
textului,închiderea unei
ferestre, etc. 
Sursă: componenta care
generează un eveniment.
Listener: responsabil cu
interceptarea evenimentelor
(consumator de
evenimente). 

Observer Design
Pattern Observarea stării unei
entități în cadrul unui sistem 
(Publish-Subscribe)
Button - ActionEvent -
ActionListener 
class Fereastra extends Frame { 
 public Fereastra ( String titlu ) { 
 super (titlu); 
 setLayout (new FlowLayout ()); 
 setSize (200, 100) ; 
 Button b1 = new Button ("OK"); 
 Button b2 = new Button ("Cancel"); 
 add(b1); add(b2); 
 listener = new MyButtonListener (this); 
 b1.addActionListener ( listener ); 
 b2.addActionListener ( listener ); 
 // Ambele butoane sunt
"ascultate" de obiectul
listener,  // instanta a
clasei MyButtonListener,
definita mai jos  } 

class MyButtonListener implements
ActionListener { 
 private Fereastra frame; 
 public MyButtonListener (Fereastra frame) { 
 this.frame = frame; 
 } 
 // Metoda interfetei ActionListener 
 public void actionPerformed (ActionEvent e)

 frame.setTitle ("Ati apasat
" + e. getActionCommand ()); 

}

Tipuri de
evenimente
Low-level  Semantice

ComponentEvent  ActionEvent 
ascundere, deplasare, apăsareau unui buton,
redimensionare,  afișare apăsarea tastei  enter într-un
componente textfield, etc.
ContainerEvent  AdjustmentEvent 
adăugare, eliminare ajustarea valorii unei
componente în/din  componente, de  exemplu un
containere scrollbar
FocusEvent  ItemEvent 
obținere, pierdere focus schimbarea stării unei
componente:  selectarea
unor articole într-o listă, 
apăsarea unui checkbox
KeyEvent  TextEvent 
apăsare, eliberare taste schimbarea textului într-o
componentă  de tipul textfield,
textarea
MouseEvent  ...
operațiuni cu mouse-ul:
click, drag, etc.
WindowEvent 
operațiuni asupra
ferestrelor:  
minimizare,
redimensionare, etc.

Relația
“Componentă -
Listener” many-to-
many
Component  ComponentListener 
FocusListener 
KeyListener 
MouseListener
Container  ContainerListener
Window  WindowListener
Button  ActionListener
List  
MenuItem 
TextField
Choice  ItemListener
Checkbox  
List
Scrollbar  AdjustmentListener

TextField   TextListener
TextArea

Metode
handler
ActionListener  actionPerformed(ActionEven
t e)

ItemListener  itemStateChanged(ItemEvent
e)

TextListener  textValueChanged(TextEvent
e)

MouseListener  mouseClicked(MouseE
vent e)
mouseEntered(MouseE
vent e) 
mouseExited(MouseEv
ent e) 
mousePressed(MouseE
vent e)
mouseReleased(Mouse
Event e)

MouseMotionList mouseDragged(Mouse
ener  Event e)
mouseMoved(MouseEv
ent e)
WindowListener  windowActivated(WindowEv
ent e)
windowClosed(WindowEvent
e)
windowClosing(WindowEven
t e) 
windowDeactivated(Window
Event e)
windowDeiconified(Window
Event e)
windowIconified(WindowEv
ent e)
windowOpened(WindowEvent
e)

...  ...

Adaptori și clase
anonime 
class Fereastra extends
Frame implements
WindowListener {  public
Fereastra (String titlu)

 super (titlu); 
 this.addWindowListener(this); 
 } 
 // Metodele interfetei WindowListener 
 public void windowOpened ( WindowEvent
e) {} 
 public void windowClosing
( WindowEvent e) { 
 // Terminare program 
 System.exit (0); 
 } 
 public void windowClosed ( WindowEvent
e) {} 
 public void windowIconified
( WindowEvent e) {} 
 public void windowDeiconified
( WindowEvent e) {} 
 public void windowActivated
( WindowEvent e) {} 
 public void windowDeactivated
( WindowEvent e) {} 

Un adaptor este o clasă abstractă care implementează o
anumită interfață fără a specifica cod nici  unei metode a
interfeței.
this.addWindowListener(new
WindowAdapter() { 
 public void
windowClosing(WindowEvent e) { 
 System.exit(0); 
 } 
}); 

Folosirea
meniurilor
Swing 
Extinde tehnologia AWT,
preluând conceptele și 
mecanismele de bază  
Aduce un set nou de
componente, mult mai 
complex, înlocuindu-l
complet pe cel din
AWT 
Aduce portabilitatea la
nivelul interfeței grafice; 
aceasta nu mai depinde
de sistemul de operare 
Introduce o arhitectură
cu model separabil
Dezvoltarea interfeței
grafice devine:
"Programare orientată
pe componente"
Java Foundation
Classes 
Java Foundation Classes (JFC)
reprezintă un cadru de lucru  pentru
dezvoltarea de aplicații portabile
având interfață grafică  desktop.
Conține mai multe biblioteci, toate
incluse în platorma  Java SE: 

Swing 

Look-and-Feel 

Accessibility API 

Java 2D API 

Drag-and-Drop 

Internaționalizare
Paleta de
componente
Swing 
Componente atomice 
JLabel, JButton, JCheckBox,
JRadioButton, JToggleButton,
JScrollBar, JSlider, JProgressBar,
JSeparator 
Componente complexe 
JTable, JTree, JComboBox, JSpinner,
JList, JFileChooser, JColorChooser,
JOptionPane 
Componente pentru editare de text 
JTextField, JFormattedTextField,
JPasswordField, JTextArea, JEditorPane,
JTextPane 
Meniuri 
JMenuBar, JMenu, JPopupMenu, JMenuItem, 
JCheckboxMenuItem, JRadioButtonMenuItem 
Containere intermediare 
JPanel, JScrollPane, JSplitPane,
JTabbedPane, JDesktopPane, JToolBar 
Containere de nivel înalt 
JFrame, JDialog, JWindow, JInternalFrame, JApplet
Asemănări și
deosebiri cu AWT 
Convenția "J" 
java.awt.Frame – javax.swing.JFrame 
java.awt.Button - javax.swing.JButton 
java.awt.Label - javax.swing.JLabel 

Noi gestionari de poziționare: 


BoxLayout, SpringLayout, GroupLayout, OverlayLayout, etc. 

Folosirea HTML 
JButton simplu = new JButton("Text simplu"); 
JButton html = new JButton("<html><u>Text</u>
<i>formatat</i></html>");

Folosirea ferestrelor
în Swing
Frame f = new Frame(); 
f.setLayout(new FlowLayout()); 
f.add(new Button("OK")); 

JFrame jf = new JFrame(); 


jf.getContentPane().set
Layout(new
FlowLayout());
jf.getContentPane().add
(new JButton("OK"));

Ferestre
interne 
Aplicațiile pot fi clasificate ca: 
SDI (Single
Document
Interface)
MDI
(Multiple
Document
Interface)
JInternalFrame
, DesktopPane
Clasa
JComponent 
JComponent este superclasa
tuturor componentelor Swing,  mai
puțin JFrame, JDialog, JApplet.  
JComponent extinde clasa Container. 
ToolTips - setToolTip 

Chenare - setBorder 
Suport pentru plasare și dimensionare  
setPreferredSize, ... 
Controlul opacității - setOpaque 
Asocierea de acțiuni tastelor 
Double-Buffering
Arhitectura
Swing 
MVC (Model-View-
Controller) 
Model - datele aplicației 
Prezentare - reprezentarea
vizuală 
Control - transformarea acțiunilor
în evenimente 

Arhitectură cu model
separabil 
Model + (Prezentare, Control)
Reprezentare -
Model 
Componentă  Interfața care descrie modelul
JList  ListModel 
ListSelectionModel
JTable  TableModel 
TableColumnModel 
ListSelectionModel
JTree  TreeModel 
TreeSelectionModel
JEditorPane  Document
JTextPane 
JTextField
...  ...

Crearea unui model =


implementarea interfeței 
JList - ListModel, DefaultListModel,
AbstractListModel
Exemplu
JList 
Componentele sunt instanțiate pe
baza unui model. Acesta poate fi
reprezentat fie de o structură de
date simplă: Object elemente[] = {"Unu",
"Doi", new Integer(3), 4.0}; JList lista = new
JList(elemente); 

fie de o instanță model specifică 


DefaultListModel model =
new DefaultListModel();
model.addElement("Unu"); 
model.addElement("Doi"); 
model.addElement(new Integer(3)); 
model.addElement(4.0); 
JList lista = new JList(model);

Exemplu
JTable 
class MyTableModel extends
AbstractTableModel {  private String[]
coloane = {"Nume", "Varsta", "Student"}; 
private Object[][] elemente = { 
 {"Ionescu", new Integer(20),
Boolean.TRUE},  {"Popescu",
new Integer(80),
Boolean.FALSE}}; 

 public int getColumnCount() { 


 return coloane.length; 
 } 
 public int getRowCount() { 
 return elemente.length; 
 } 
 public Object
getValueAt(int row, int
col) {  return elemente[row]
[col]; 
 } 
 public String getColumnName(int col) { 
 return coloane[col]; 
 } 
 public boolean isCellEditable(int
row, int col) {  // Doar numele este
editabil 
 return (col == 0); 
 } 
}
Personalizarea
reprezentării
Conceptul de
CellRenderer 
Un renderer este responsabil
cu afișarea articolelor unei
componente, de exemplu
înregistrările dintr-o listă. 
class MyCellRenderer extends JLabel implements
ListCellRenderer {  public MyCellRenderer() { 
 setOpaque(true); 
 } 
 public Component getListCellRendererComponent( 
 JList list, Object value, int index, 
 boolean isSelected, boolean
cellHasFocus) { 
setText(value.toString()); 
 setBackground(isSelected ? Color.red :
Color.white);  setForeground(isSelected ?
Color.white : Color.black);  return this; 
 } 

... 
list.setCellRenderer(new MyCellRenderer());

Conceptul de
CellEditor 
Un editor este responsabil cu
editarea articolelor unei
componente, de exemplu
informația din celulele unui
tabel. 
public class MyCellEditor extends
AbstractCellEditor  implements
TableCellEditor {  public Component
getTableCellEditorComponent(...) { 
// Returneaza componenta 
 // de tip editor 
 ... 
 } 

 public Object getCellEditorValue() { 


 // Returneaza valoarea editata 
 ... 
 } 
}

Containere
Swing 
Containere de nivel înalt  
JFrame, JDialog, JApplet 

Containere intermediare 
JPanel 
JScrollPane 
JTabbedPane 
JSplitPane 
JLayeredPane 
JDesktopPane 
JRootPane
Look and
Feel 
Schimbarea aspectului
general al interfeței cu
utilizatorul prin intermediul
unor teme grafice: 
javax.swing.plaf.metal.MetalLookAndFeel 
com.sun.java.swing.plaf.window
s.WindowsLookAndFeel
com.sun.java.swing.plaf.mac.Ma
cLookAndFeel 
com.sun.java.swing.plaf.motif.MotifLookAndFeel 
com.sun.java.swing.plaf.gtk.GTKLookAndFeel 
... 

UIManager.setLookAndFeel("com.sun.java.swing.plaf.mot

if.MotifLookAndFeel");

SwingUtilities.updateComponentTreeUI(f); 

f.pack();
The Java
Tutorial 

Trail: Graphical User
Interfaces
http://docs.oracle.com/jav
ase/tutorial/ui/index.html 


Trail: Creating a GUI
With JFC/Swing
http://docs.oracle.com/javase/
tutorial/uiswing/index.html
Componentele şi pachetele librăriei
Swing - Partea a II-a - 

1. Componente atomice complexe 


Meniurile oferă utilizatorilor posibilitatea alegerii uneia sau a mai multor opţiuni. O 
bară de meniu conţine unul sau mai multe meniuri. Un meniu popup este un meniu care
este  invizibil pana când utilizatorul apasă butonul drept al mouse-ului deasupra popup-ului 
neactiv. Meniul popup apare astfel sub cursor.  
Iată care este ierarhia de clase pentru un meniu în Java Swing: 

Figura următoare arata componentele Swing care implementează fiecare parte a


unui  meniu. 
 

Pentru a seta componenta JMenuBar, se foloseşte metoda setJMenuBar(). Pentru a 


adăuga un JMenu la componenta JMenuBar , se foloseşte metoda add(JMenu). Pentru 
adăugarea de itemi şi submeniuri la un meniu, se foloseşte metoda add(JMenuItem).  
Aşadar meniurile sunt folosite de programatori pentru a realiza o navigare mai
uşoara  în cadrul unei aplicaţii, de pildă o interfaţă Java Swing, oferind o soluţie elegantă şi
totodată  eficientă.  
JTextComponent este o clasă de text generalizată care conţine toate metodele de 
lucru cu un simplu editor de text. Iată câteva exemple de astfel de metode:  - copy() 
- cut() 
- paste() 
- getSelectedText() 
- setSelectionStart() 
- setSelectionEnd() 
- selectAll() 
- replaceSelection()  
- getText() 
- setText() 
- setEditable() 
- setCaretPosition() 

Să observăm ierarhia de clase corespunzătoare componentelor text din Java Swing:  


 
 
Chiar dacă nu se foloseşte direct o instanţă a unui obiect de tip JTextComponent, se  
folosesc adesea aceste metode, multe dintre ele nefiind disponibile în AWT. 
 

Obiectele de tip JTextComponent pot fi plasate într-un panel asemănător cu cele din 
AWT.  
Mai jos sunt descrise subclasele clasei JTextComponent: JTextField, JTextArea, 
JEditorPane, JPasswordField şi JTextPane.  
JTextField şi JTextArea 
JTextField şi JTextArea se comportă asemănător cu clasele corespunzătoare din 
AWT: java.awt.TextField şi java.awt.TextArea.  
JTextField suportă de asemenea setarea textului cu ajutorul metodei 
setHorizontalAlignment(). Cele trei setări valabile sunt LEFT, CENTER, şi RIGHT, setarea 
implicită fiind LEFT.  
Componenta JTextField este folosită pentru introducerea (editarea) informaţiilor de 
tip text, numerice. 
JPasswordField 
JPasswordField este un JTextField care refuză să afişeze componentele sale care
sunt  deschise. Iniţial, caracterul mască este '*'. Totuşi, aceasta se poate schimba folosind
metoda setEchoChar(). Spre deosebire de java.awt.TextField, un caracter alb nu resetează 
masca. 
Componenta JPasswordField se foloseşte, de pildă, atunci când se doreşte
verificarea  accesului la anumite informaţii (de exemplu se pot crea grupuri de utilizatori
care pot avea  anumite restricţii). 

Clasa JTable are o clasă asociată numită DefaultTableModel care foloseşte un


vector  de vectori pentru a păstra datele interne. Datele pentru fiecare rând sunt păstrate într-
un  singur obiect de tip vector, în timp ce un alt obiect de tip vector păstrează fiecare din
aceste  rânduri ca elemente componente ale sale. Obiectul DefaultTableModel poate fi
iniţializat cu  date în mai multe feluri diferite. Codul de mai jos arată un obiect
DefaultTableModel creat cu  un şir bidimensional şi cu un şir care reprezintă capetele
coloanelor. Acest obiect  DefaultTableModel transformă(converteşte) obiectele de tip array
în vector de obiecte:
 

 Object[][] data = new Object[][]{  


{"row 1 col1", "Row 1 col2" },  
{"row 2 col 1", "row 2 col 2"}  
}; 
 Object[] headers = new Object[] {"first header", "second header"}; 
 DefaultTableModel model = new DefaultTableModel(data,
headers);  table = new JTable(model); 
 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 
Se poate implementa un model de tabel scriind o metodă ce returnează numărul de 
intrări în model, şi o metodă care întoarce un element de la o poziţie specificată din model. 
De exemplu, modelul JTable poate fi implementat din
javax.swing.table.AbstractTableModel scriind metodele getColumnCount(), getRowCount()
şi getValueAt(). 
Se poate crea un tabel în care se pot modifica datele prin adăugarea metodei de 
verificare isCellEditable(), care este folosită de editorul de celule predefinit, şi metodei 
AbstractTableModel pentru a seta o valoare la o anumită poziţie. Deoarece modelul 
AbstractTableModel nu recunoaşte o modificare în tabelul de date, trebuie apelată metoda 
fireTableCellUpdated(). 

JTree este o altă componentă care foloseşte tehnicile modelelor de date aleatoare.
Cel  mai simplu mod de a folosi JTree este de a crea un obiect de tipul
DefaultMutableTreeNode care se comportă ca nodurile unui arbore. Nodurile care nu au
copii(descendenţi) vor fi  afişate ca “frunze”. Se înlocuieşte o valoare, cunoscută sub
numele de “user object”, cu  constructorul DefaultMutableTreeNode, care se comportă ca
valoare pentru fiecare nod.  Metoda toString() a acestui “user object” este folosită pentru a
afişa fiecare nod. 
În momentul în care sunt create câteva noduri, acestea se leagă împreuna într-o 
structură de arbore folosind parentNode.add(childNode). La final fiecare nod este trecut în 
constructorul JTree. 
Observaţie: Deoarece arborii îşi pot modifica dimensiunea pe baza datelor de
intrare,  ei sunt de obicei plasaţi într-un JScrollPane(este indicat dar nu este obligatoriu).
Manipularea evenimentelor JTree 
Pentru a lucra cu evenimente JTree se ataşează TreeSelectionListener. Interfaţa 
TreeSelectionListener necesită o singură metodă: valueChanged(). Se extrage nodul curent 
folosind tree.getLastSelectedPathComponent, apoi se transformă acesta în tipul nodului cu 
care se lucrează(de obicei DefaultMutableTreeNode), apoi se extrage „user object” folosind 
 

getUserObject. Totuşi, în cazul în care se doreşte doar eticheta nodului, se apelează metoda 
toString() rezultatului obţinut prin apelarea metodei tree.getLastSelectedPathComponent().
Modele aleatoare şi JTree dinamic 
Un JTree foloseşte un TreeModel pentru a-şi lua datele. Ca şi pentru JList se poate 
înlocui modelul specificând modul de extragere a datelor din modelul aleator. TreeModel-
ul  predefinit foloseşte TreeNode pentru a păstra datele asociate arborelui, şi este mai
elegant să  laşi neschimbat TreeModel-ul şi să faci în schimb un TreeNode aleator. Cel mai
uşor mod de  abordare a acestei probleme este să se înceapă cu DefaultMutableTreeNode. În
exemplul  următor arborele este potenţial infinit, fiecare nod descriind o secţiune într-o linie
de ieşire.  Rădăcina arborelui va fi „1”, primul nivel va fi 1.1, 1.2., 1.3., etc, al doilea nivel
va fi 1.1.1,  1.1.2, etc, şi aşa mai departe. Cheia construirii unui JTree dinamic este să
observi că metoda  getChildCount() va fi apelată înaintea determinării descendenţilor. Deci
se păstrează un  indicator care arată dacă a fost construit odată vreun descendent. Se
apelează întâi metoda  getChildCount(), apoi dacă indicatorul este fals sunt construiţi
descendenţii şi apoi adăugaţi.  
Metodele statice ale clasei JOptionPane permit crearea cu uşurinţa a dialogurilor 
modale pentru afişarea mesajelor (JOptionPane.showMessageDialog), a întrebărilor care 
aşteaptă confirmare (JOptionPane.showConfirmDialog), permit utilizatorului introducerea
de  texte sau alegerea unei opţiuni predefinite (JOptionPane.showInputDialog), permit
alegerea  unui buton (JOptionPane.showOptionDialog). Fiecare dintre aceste metode
returnează un int  prin care specifică ce buton s-a apăsat, sau un string prin care specifică
opţiunea selectată. 

JFileChooser permite utilizatorilor să aleagă interactiv un nume de fişier


parcurgând  structura de directoare. Folosirea normală a JFileChooser-ului presupune
alocarea unui  JfileChooser (foloseşte un string pentru directorul constructorului sau „.”
pentru directorul  curent, sau blank pentru directorul rădăcina), setarea unui titlu folosind
metoda  setDialogTitle(), opţional specificarea unei alegeri predefinite (setSelectedFile),
opţional  scrierea şi ataşarea unui FileFilter pentru a limita tipurile de fişiere afişate, creând
un pop-up  folosind metoda showOpenDialog, trecând în Frame-ul principal(aceasta
returnează un int),  apoi în final, dacă acest int se potriveşte cu
JFileChooser.APPROVE_OPTION se apelează  metoda getSelectedFile().  
JColorChooser este o componentă nouă pentru Swing, ea neavând echivalent în 
AWT. Această componentă permite utilizatorului să selecteze interactiv o culoare. Setarea 
 

iniţială prezintă o casetă de dialog care conţine componentă swing tabbed pane ce permite 
utilizatorului să aleagă sau să schimbe valori RGB sau HSB. 
Se apelează JColorChooser.showDialog() folosind trei argumente: 
- primul argument este de tipul Component; 
- al doilea argument este titlul; 
- al treilea argument este iniţial culoarea aleasă. 
Dacă utilizatorul alege “OK”, valoarea returnată este culoarea aleasă. Dacă 
utilizatorul renunţă, valoarea returnată este null. 
Se poate aloca de asemenea JColorChooser folosind un constructor. Se preferă 
această metodă când se doreşte ca afişarea să nu se realizeze printr-un pop-up dialog, sau 
dacă se doreşte realizarea unui pop-up de mai multe ori. În acest ultim caz se trece instanţa
lui  JColorChooser în JColorChooser.createDialog.  

JProgressBar este o componentă vizibilă grafic care arată cât din task-ul total a
fost  realizat.  

∙ ProgressMonitor este o componentă nevizibilă. O instanţă a acestei clase  monitorizează


progresul task-urilor. 
∙ ProgressMonitorInputStream reprezintă un fişier de intrare cu un monitor de  progres
ataşat care monitorizează citirea din fişier. Se foloseşte o instanţă a acestui  fişier la fel
cum se foloseşte orice alt fişier de intrare. Se poate obţine monitorul de  progres al
fişierului prin apelarea metodei getProgressMonitor().
 
Evenimente, Listener 
http://java.sun.com/docs/books/tutorial/uiswing/events/index.html 
De fiecare dată când utilizatorul scrie un caracter sau apasă un buton al mouse-ului 
are loc un eveniment. Fiecare obiect poate fi notat ca şi eveniment. Tot ceea ce trebuie
făcut  este să se implementeze o interfaţă potrivită şi aceasta să se înregistreze ca un “event
listener”  pentru “event source”. Componentele swing pot genera multe tipuri de
evenimente. Iată  câteva exemple: 

Evenimente 

Se apasa un buton, se apasa Return in timp ce se crie intr-un text field, sau  se alege un item
din meniu

Se inchide un Frame (main window) 

Se apasa un buton al mouse-ului in timp ce cursorul este pe o componenta MouseListener

Se deplaseaza mouse-ul deasupra unei componente 

Componenta swing devine vizibila 

Se modifica tabelele sau listele de selectie 

Tipuri de Listener

ActionListener
WindowListener

MouseMotionListener

ComponentListener

ListSelectionListener

Fiecare eveniment este reprezentat printr-un obiect care dă informaţii despre 


eveniment şi identifică sursa evenimentului. Sursa evenimentului este o componentă tipică, 
dar alte tipuri de obiecte pot fi de asemenea surse de evenimente. Aşa cum se vede în figura 
de mai jos, fiecare sursă de eveniment poate avea mai mulţi “ascultători”(listeners) 
înregistraţi. Un singur “ascultător”(listener) poate înregistra mai multe surse de evenimente.

Ori de câte ori se doreşte ştergerea evenimentelor pentru o anume componentă, mai 
întâi se consultă secţiunea “how-to” pentru acea componentă. Secţiunea “how-to” oferă 
exemple de lucru cu evenimente. 
Fiecare tratare de evenimente necesită trei secvenţe de cod: 
1. În declararea clasei de tratare a evenimentului, codul specifică faptul că întreaga clasă 
implementează o interfaţa Listener sau extinde o clasă care implementează o interfaţă 
Listener. De exemplu: 
public class MyClass implements ActionListener {...} 
2. Codul înregistrează o instanţă a clasei de tratare a evenimentului ca un Listener peste  una
sau mai multe componente. De exemplu: 
someComponent.addActionListener(instanceOfMyClass); 
3. Codul implementează metodele din interfaţa Listener. De exemplu: 
public void actionPerformed(ActionEvent e) {...} 

Să analizăm un scenariu tipic de tratare de evenimente uitându-ne la modul în care 


butoanele (JButton) tratează click-urile mouse-ului. Pentru a detecta când utilizatorul apasă 
click pe un buton, un program trebuie să aibă un obiect care să implementeze interfaţa 
ActionListener. Programul trebuie să înregistreze obiectul ca un ActionListener asupra 
butonului, folosind metoda addActionListener. Când utilizatorul apasă pe buton, acesta 
lansează un eveniment de tip Action. Singurul argument al metodei este un obiect de tipul 
ActionEvent care oferă informaţii despre eveniment şi despre sursa sa. 

Manipulatorii de evenimente pot fi instanţe ale oricărei clase. Adesea, un


manipulator  de evenimente care are doar câteva linii de cod este implementat folosind un
anonymous inner-class (o clasă fără nume definită în interiorul altei clase).  

Tratarea evenimentelor 
 

Fiecare manipulator de evenimente îşi va încheia execuţia înainte de a începe


celălalt.  De exemplu, în timp ce metoda actionPerformed se execută interfaţa grafică
utilizator (GUI)  este “îngheţată” (nu va realiza repaint sau nu va răspunde la un click al
mouse-ului). public class Beeper ... implements ActionListener { 
 ... 
 //au loc iniţializările: 
 button.addActionListener(this); 
 ... 
 public void actionPerformed(ActionEvent e) { 
 ...//se aude un beep... 
 } 

Observaţie: 
Codul manipulatorului de evenimente trebuie să se execute foarte repede. Altfel, 
performanţele programului vor fi reduse. 

1. INTRODUCERE 

In ultimii ani, tehnologiile pentru realizarea aplicatiilor Web au cunoscut o evolutie 


spectaculoasa, intrucat rapiditatea cu care Internetul se dezvolta conduce la aparitia unor 
domenii din ce in ce mai variate de activitate pentru care aplicatiile Web ofera solutii
complete.  O aplicatie web reprezinta un sistem software bazat pe tehnologiile si standardele
consortiului  W3C care ofera resurse web specifice cum ar fi continut si servicii prin intermediul
unei  interfete. Prin definitie, aceste aplicatii sunt complexe si mai greu de proiectat, gratie
naturii  acestora de aplicatii distribuite, acestea putand fi accesate simultan de catre mai multi 
utilizatori care vor partaja resurse in comun. Prin intermediul unei aplicatii online se pot stoca, 
gestiona, modifica, trimite, receptiona si procesa informatie folosind conectivitatea 
internetului, folosind mai multi utilizatori, mai multe statii de lucru si mai multe procese in  
acelasi timp. Deoarecere aplicatia este bazata pe internet nu exista incompatibilitati hardware, 
virusi, aplicatia si informatia fiind stocate pe un server securizat. 

Aplicatiile web care furnizeaza multe din caracteristicile de baza ale aplicatiilor Desktop, 
oferind aceeasi flexibilitatea in functionalitate ca si acestea, dar fiind in acelasi timp inzestrate
si  cu capabilitatile aplicatiilor Web clasice sunt numite RIA ( Rich Internet Applications). 

In prezent, aplicatiile web nu ruleaza exclusiv in browser, acestea putand fi gasite pe o 


multitudine de dispozitive mobile sau intr-un mod offline sub forma unor plugin-uri de
browser,  precum Google Gears sau Mozilla Prism. Dar in continuare, aplicatiile web bazate pe
JavaScript  si AJAX nu pot oferi acelasi nivel de performanta precum framework-urile sub forma
de plugin.  Acestea din urma pot manipula in mod direct elementele grafice, pot executa calcule
multi 
thread si pot suporta in intregime formate audio si video. Companii majore precum Adobe, 
Microsoft si Sun Microsystem ofera solutii propii in acest domeniu si exista un trend general de 
crestere a aplicatiilor web de tip RIA. 

In lucrarea de fata, vor fi prezentate noi framework-uri de dezvoltare impreuna cu 


noutatile pe care acestea le aduc fata de cele clasice, precum si evolutia acestora. Va fi 
prezentata de asemenea si metoda de incapsulare a continutului in pagini web si mecanismele
de includere in aplicatii, cu accent pe tehnologia SOAP.
2. EVOLUTIA FRAMEWORK-URILE CLASICE DE DEZVOLTARE 

Prin framework sau platforma se intelege un anumit tip de arhitectura atat hardware cat si software 
care asigura executia aplicatiilor software. In cele ce urmeaza, sunt prezentate o serie de framework-uri 
clasice performante utilizate in dezvoltarea de aplicatii RIA. 

Microsoft Silverlight 

Microsoft Silverlight a reprezentat o tehnologie inovatoare in momentul aparitiei, prin 


intermediul acesteia putand fi realizate aplicatii RIA cu foarte multe componente grafice si 
functionalitati. Inca de la prima versiune puteau fi realizate aplicatii interactive si complexe, 
existand suport pentru streamingul audio si video, dar nu si pentru CLR(Common Language 
Runtime) si pentru limbajele platformei .NET. Versiunile ulterioare au oferit un vast suport, 
putand fi executate programe scrise in orice limbaj .NET, existand suport pentru conectivitatea 
la retea prin intermediul Windows Communication Foundation si putand fi realizate apeluri 
sincrone si asincrone cu serverul. 

Ultima versiune a acestui framework are menirea de a interactiona din ce in ce mai mult cu alt  
framework Windows, WPF, facand ca granita dintre aplicatiile web si cele desktop sa devina
din  ce in ce mai subtire. Astfel, ultima versiune de Silverlight deschide calea catre un nou tip
de  aplicatii numite Trusted Apllications, acestea putand fi instalate direct de pe un website si 
accesa fisierele locale, resursele de retea locale sau alte resurse live de pe web fara a exista 
necesitatea unui fisier de legatura, precum exista in aplicatiile web Silverlight prezente. 
Aplicatiile Silverlight pot recunoaste daca rularea acestora are loc in contextul unui navigator 
Web sau nu, prin verificarea existentei suportului de citire pentru HTML. De asemenea, 
platforma Silverlight ofera algoritmi de compresie mult mai performanti pentru imagini, 
rezolutia informatiilor multimedia transmise prin streaming fiind mult mai buna decat in cazul  
platformei Flash. 

Functionalitatea de baza a Silverlight este oferita de un plug-in in cadrul unui browser, care 
reda XAML, afiseaza DOM-ul intern si expune un model de programare bazat pe evenimente 
intr-un mod care poate fi interpretat de JavaScript. Aceasta arhitectura este ilustrata in figura 
de mai jos, unde se poate observa faptul ca principala interfata de programare care este
oferita  in Silverlight este bazata pe JavaScript DOM API. Acest lucru ofera posibilitatea
interceptarii  evenimentelor cauzate de utilizator( de ex. efectuarea de click-uri pe un element
specific), si de  a executa cod ca raspuns la acestea. De asemenea, se pot apela metode de
JavaScript DOM  pentru a manipula elemente XAML, de genul redarii media sau declansarii
animatiilor.

   Figura 1 :
Arhitectura Silverlight 
Pentru o aplicatie simpla care ruleaza in browser folosind Silverlight, arhitectura
este  urmatoarea: 
Figura 2: Arhitectura unei aplicatii ce foloseste Silverlight 

Aplicatiile Silverlight pot fi scrise in orice limbaj de programare .NET. Orice instrument de 
dezvoltare care poate fi utilizat cu limbajele .NET, poate lucra cu Silverlight, cu conditia ca 
acestea sa poata apela CoreCLR-ul Silverlight, in loc de .NET Framework CLR, pentru
gazduirea  aplicatiei. Un proiect Silverlight va contine fisierele Silverlight.js si
CreateSilverlight.js care  initializeaza plug-in-ul Silverlight utilizat in paginile HTML, un fisier
XAML pentru Interfata  Utilizator, si fisierele care contin cod. Se poate face debug pe
aplicatiile Silverlight in acelasi  mod ca si pentru aplicatiile ASP.NET.
JavaFX 

Alaturi de Microsoft, principala alternativa in realizarea de aplicatii RIA o reprezinta tehnologia 


JavaFX. Limbajul JavaFX este un limbaj declarativ si foarte tipizat, specific domeniului de 
dezvoltare de aplicatii cu grad avansat de grafica integrata, scopul acestuia fiind crearea de 
aplicatii care pot functiona pe o multitudine de dispozitive, de la PC la smartphone-uri,
utilizand  un model de dezvoltare si implementare unificat. Tehnologia JavaFX ofera posibilitati 
multimedia puternice, prin integrarea in aplicatie de continut audio si video si prin manipularea 
de grafica 3D. De asemenea, permite realizarea de applet-uri Java care ruleaza in afara 
browserului Web, prin intermediul unui nou plugin din JSE. 

JavaFX manipuleaza elementele grafice printr-un mod numit „retained”, folosind metode 
desprinse din API-ul Java2D realizand impingerea pixelilor in vecinitati imediate. Acest
procedeu  reprezinta o tehnica avansata de realizare a animatiilor pentru Web, JavaFX fiind
dezvoltat cu  precadere pentru modelarea aplicatiilor multimedia si pentru facilitarea
dezvoltarii de aplicatii  grafice destinate Web-ului. 

Acest framework a evoluat mult in ultima perioada, chiar daca putem spune ca este unul fara o 
mare vechime. Ultima versiune majora, 2.0, aparuta in 2011 a adus o serie intreaga de 
imbunatatiri in librariile JavaFX pentru multithreading, un nou set de API-uri ce pune la 
dispozitie capabilitatile JavaFX pentru toti dezvoltatorii Java eliminand necesitatea invatarii
unui  nou limbaj de programare si eliminarea suportului pentru JavaFX Mobile. 

Principalele avantaje care recomanda JavaFX pentru crearea de aplicatii RIA tin de : 

-posibilitatea rularii pe orice calculator si in orice browser ce are Java Runtime Environment
-usurinta de utilizare a mediului de dezvoltare Netbeans, ce permite realizarea de functii
grafice  complexe si adaugarea de componente in doar cativa pasi simpli 
-permisiunea instalarii pachetului Production Suite, ce faciliteaza lucrul intre programator si 
grafician, prin instalarea de plugin-uri in Adope Photoshop/Illustrator care convertesc un fisier 
Adobe intr-un fisier JavaFX 
-sustinerea HttpRequest, ce joaca un rol important in lucrul cu serverul 

Beneficiind de raspandirea limbajului Java, acest framework poate distribui usor aplicatiile RIA 
peste o gama foarte diversa de dispozitive, micsorand totodata ciclul de productie pentru 
proiectare si dezvoltare.
Ajax 

Ajax reprezinta un grup de metode de dezvoltare folosite pe partea de client pentru crearea de 
aplicatii web. Prin intermediul acestei platforme, aplicatiile pot transmite si obtine date de la 
server, in mod asincron, fara a afecta afisarea si comportamentul paginii web. Ajax se executa 
pe calculatorul client, permitand integrarea rapida intre mediul client si cel server, si face ca 
programul sa fie mai eficient si mai usor de gestionat. De asemenea, Ajax incorporeaza facilitati 
de afisare si interactiune dinamica utilizand DOM(Document Object Model), capabilitati de 
interschimb si manipulare a datelor folosind XML si XSLT(Extensible Stylesheet Language 
Transformation), functionalitati de regasire asincrona a datelor utilizand obiectul 
XMLHttpRequest si functionalitati de consolidare utilizand JavaScript. 

Exista mai multe framework-uri de dezvoltare ce au la baza Ajax, iar printre acestea se numara: -
Spry Framework - proiectat sa mai elimine din complexitatea Ajax si sa permite  dezvoltarea
mai usoara a paginilor Web 2.0; este centrat pe HTML si necesita doar  cunostinte de baza din
HTML, CSS si JavaScript pentru a proiecta aplicatii interactive si  bogate in continut 
- Taconite Framework – simplifica crearea aplicatiilor ce au la baza Ajax, fiind un  framework
simplist ce automatizeaza anumite task-uri in dezvoltarea Ajax si care sunt  consumatoare
de timp, precum crearea si managementul obiectelor de tip  XMLHttpRequest sau crearea
de continut dinamic. 
- Salto Framework – este un framework bazat pe tehnologiile Ajax, Struts si J2EE, care  aduce in
plus rapiditatea in crearea aplicatiilor Web prin oferirea unei serii de  componente grafice
precum tabele de date, vizualizari de arbori si liste si prin  permiterea reimprospatarii unor
portiuni patiale dintr-o pagina. 
Printr-o aplicatie bazata pe Ajax nu putem deschide socket-uri si nu se pot utiliza
componentele  periferice ale unui computer(camera Web si microfon) pentru a face streaming.
Insa acest tip  de aplicatii sunt mult mai rapide decat cele care necesita masini virtuale sau
diverse alte plugin uri. Probleme care intervin in cazul utilizarii Ajax tin de motivele de
securitate, deoarece codul  JavaScript poate fi interceptat de catre alte persoane, continutul
acestuia putand fi modificat.  Acest lucru imprima inca de la inceput o viziune clara asupra
proiectarii fluxului de informatii  vehiculat in cadrul aplicatiei. 

Avantajele folosirii AJAX constau in eliminarea refresh-ului de pagina, micsorarea traficului de 
retea, separarea datelor, a functionalitatii si a formatarii paginii, precum si o mare
imbunatatire 
a interfetei cu utilizatorul. Printre dezavantaje se numara faptul ca paginile care folosesc AJAX 
nu vor rula corespunzator pe browsere mai vechi sau pe browsere fara JavaScript. 

   Figura 3 :
Structura unei aplicatii Ajax 

3 FRAMEWORK-URI NOI IN DEZVOLTAREA APLICATIILOR WEB 

Popularitatea in continua crestere a aplicatiilor RIA conduce in mod obligatoriu la necesitatea 


stabilirii unei strategii si a unor directii de dezvoltare. Este necesar sa se analizeze produsele 
folosite pentru dezvoltarea acestor aplicatii in scopul determinarii acelor produse care vor 
supravietui competitiei in viitorul apropiat. De asemenea, trebuiesc identificate si evaluate 
modificarile ce pot aparea in ceea ce priveste platformele de dezvoltare de aplicatii de tip RIA. 

Pe langa solutiile clasice oferite de catre marile companii din domeniu, au aparut si alternative  
open-source, care pot oferi un nivel similar de performante si capabilitati in realizarea de 
aplicatii RIA. Printre acestea, cele mai notabile sunt framework-urile Vaadin, Ext JS si Google 
Web Toolkit. 
3.1 Google Web Toolkit(GWT) 

Google Web Toolkit reprezinta un set de instrumente ce permite crearea de aplicatii RIA, fara a 
fi necesare cunostinte complexe de HTML/Javascript, datorita suportului oferit de compilator
in  translatarea codului Java in cod HTML/JS. Promisiunea pe care Google o face prin GWT este 
similara cu promisiunea pe care Sun a facut-o cu Java: ”Write once, run everywhere”. Astfel, 
daca Java promitea independenta fata de platforma pe care ruleaza un program, Google Web 
Toolkit promite independenta fata de browserul pe care ruleaza aplicatia web.  

GWT contine in mod implicit componente grafice si reutilizabile ce pot fi extinse si modificate  
dupa necesitati. De asemenea, in cadrul acestui framework este prezent si un sistem de 
gestionare a istoriei aplicatiei si se asigura suport pentru debug standard in Java. Mai exact,  
aplicatia ruleaza intr-un browser web sub forma unui cod JavaScript, dar in codul Java se pot 
inspecta valori, se pot insera breakpoint-uri, exact ca in cazul in care aplicatia ar fi fost  
executata in Java. Google Web Toolkit contine un mod de a rula aplicatiile, numit DevMode, in 
care codul scris in Java e translatat in JavaScript la momentul executarii, linie cu linie. Drept 
urmare, tot timpul, aplicatia va rula ultimul cod scris, ceea ce inseamna ca se pot face
modificari  in cod. 

GWT ofera componente de interfata dinamice, reutilizabile, metode RPC pentru comunicarea 
intre server si aplicatie, gestionare a istoriei browserului si compatibilitatea cu toate 
browserele majore. Clasele de baza reprezinta o abstractizare foarte apropiata de DOM, iar 
conceptele de baza pentru interfata cu utilizatorul sunt foarte asemanatoare cu Java Swing. O 
aplicatie scrisa cu ajutorul acestui framework elimina complet timpul petrecut pentru testarea 
compatibilitatii intre browsere, este foarte usor de dezvoltat ulterior, iar codul rezultat este 
extrem de redus ca si dimensiune. Compilatorul reduce dimensiunea JS-ului deoarece 
minimizeaza spatiile albe, produce variabile care au cea mai mica dimensiune posibila si 
produce versiuni de cod fiecarui browser ce sunt descarcate dupa ce un script initial a verificat 
versiunea browserului. Folosirea Google Web Toolkit nu presupune automat si inlocuirea 
completa a JavaScript. De asemenea, exista suport pentru multe API-uri Google de genul 
Google Gears. 

Google Web Toolkit cuprinde ca si componente majore: 

- Un compilator Java – JavaScript, ce translateaza limbajul Java in cel JavaScript - Un mod de


dezvoltare – ce permite programatorului sa ruleze si sa execute aplicatii  GWT in JVM(Java
Virtual Machine) fara a le compila in JavaScript;  
- O librarie de emulatie JRE - ce reprezinta implementarea JavaScript a claselor din  librariile
comune Java 
- O librarie pentru elemente grafice – ce contine un set de interfete si clase customizate   pentru
crearea de widgeturi; chiar daca majoritatea componentelor grafice puse la  dispozitie de
catre GWT au un aspect bun din punct de vedere vizual, de cele mai multe 
ori se doreste modificarea aspectului aplicatiei web, caz in care se pot folosi elemente si 
librarii de CSS.  

A lucra cu Google Web Toolkit nu presupune a fi constrans a folosi doar un anumit set de 
componente web, deoarece in final va fi generat tot cod HTML si prin urmare exista suport si 
pentru componentele HTML native. De asemenea, nu este obligatorie scrierea aplicatiei in 
intregime in GWT, putand fi introdusa o singura componenta GWT pe o pagina web. 

Un proiect care foloseste GWT este compus din trei pachete: Client, Shared si Server. Codul din 
pachetele Client si Shared este translatat de compilatorul GWT in cod HTML si JavaScript. Din  
acest motiv, codul din aceste doua pachete este limitat la un subset de clase din Java, numit JRE
Emulation Library. Codul din pachetul Server este un cod pur Java si poate folosi toate clasele 
disponibile. In mare parte, in pachetul Client se afla codul care descrie interfata grafica si prin 
urmare din acest pachet nu se pot folosi direct clasele definite in pachetul Server. In pachetul 
Shared se afla obiectele de transfer de date, prin intermediul carora se transfera informatia 
intre aplicatia web si server. Clasele din acest pachet pot fi accesate si de catre codul din Server 
si de catre codul din Client. Ele sunt privite drept clase Java de catre codul de pe Server si de 
catre codul din Client. Ele sunt privite drept clase Java de catre codul de pe Server dar, in
acelasi  timp, sunt translatate in JavaScript de catre compilator, pentru folosirea in codul din
interfata  grafica. In pachetul Server sunt continute clasele ce asigura incarcarea datelor din
alte  surse(baze de date, integrari cu alte produse). Acestea pot sa lipseasca complet, caz in
care  aplicatia web generata de compilator poate fi instalata pe orice server web, chiar si fara
suport  Java.  

GWT este folosit cu precadere in mediul Enterprise datorita folosirii tehnologiilor precum Java, 
Apache Tomcat, Eclipse IDE. Aplicatiile RIA dezvoltate cu ajutorul Google Web Toolkit sunt usor 
de testat folosind frameworkurile existente de test(JUnit) sau instrumente de verificare a 
codului. 

3.2 Vaadin  

GWT se afla la baza a numeroase framework-uri, printre care se numara si Vaadin. Vaadin 
reprezinta un framework Java open-source ce se concentreaza mai mult pe partea de server.
Pe  partea de browser web, este folosita cu precadere tehnologia Ajax, pentru a asigura o
interfata  bogata si interactiva pentru utilizator.
Acest framework integreaza widget-uri si o programare pe baza de eveniment, ce pemite un 
model de programare ce se apropie mai mult de dezvoltarea software GUI, decat de 
dezvoltarea web traditionala cu HTML si JavaScript. Vaadin este distribuit ca o colectie de
fisiere  JAR ce pot fi incluse in orice tip de proiect web Java dezvoltat cu ajutorul instrumentelor 
standard. In plus, exista o serie de plugin-uri pentru EclipseIDE si NetBeans pentru o mai usoara 
dezvoltare a aplicatiilor RIA avand la baza acest framework.  
Din punct de vedere al securitatii, deoarece folosirea GWT pe partea de client poate ridica 
probleme de securitate, Vaadin adauga o validarea a datelor pe partea de server.  

Figura 4 : Arhitectura de baza a unei aplicatii realizate in framework-ul Vaadin 

Vaadin ofera doua modele de dezvoltare a aplicatiilor web: un model pe partea de 
client(browser) si un model pentru partea de server. Acesta din urma este cel mai puternic, 
permitand dezvoltarea de aplicatii exclusiv server-side, utilizand o versiune a Vaadin Client-
Side  Engine bazat pe Ajax pentru a realiza interfata de utilizator in cadrul browserului.
Modelul  pentru partea client permite dezvoltarea de widget-uri si aplicatii in Java, ce sunt
compilate in  JavaScript si executate in cadrul browser-ului. Cele doua modele pot impartasi
widget-urile,  temele, codul back-end si serviciile pentru interfata cu utilizatorul, putand fi
combinate cu  usurinta.
Framework-ul Vaadin consta intr-un API pe partea de server, un alt API pe partea de client, un 
grup de componente/widget-uri pentru interfata, teme pentru a controla aspectul interfetei,
si  un model de date ce permite maparea componentele server-side direct catre date. O
aplicatie  
Vaadin ce ruleaza pe partea de server ruleaza ca un servlet intr-un server web Java,
raspunzand  la request-uri HTTP. 

Vaadin ofera un set complet de componente ale interfetei, permitand de asemenea si


definirea  unor elemente customizabile. In figura de mai jos sunt ilustrate interfetele cu
culoarea gri,  clasele abstracte in portocaliu si clasele normale in culoarea albastra. 
Figura 5: Ierarhia componentelor UI
3.3 Ext JS 

Platforma Ext JS reprezinta o platforma JavaScript folosita pentru construirea unor aplicatii
Web  interactive ce au la baza tehnologii precum Ajax, DHTML si DOM.La origine, a aparut ca
fiind o  librarie extensie a YUI(Yahoo User Interface Library) incluzand interoperabilitatea cu
jQuery si  Prototype. Aceasta din urma reprezinta tot un framework bazat pe JavaScript ce este 
implementat ca un singur fisier de cod JavaScript numit uzual prototype.js. Acesta poate fi 
distribuit atat ca un proiect de sine statator, cat si ca parte a unor proiecte mai mari. Incepand  
cu versiuni mai noi, Ext JC nu mai prezinta dependente de librarii externe, acestea ramanand  
optionale. 

Aceasta platforma include un set de elemente grafice de control(widget-uri) ce se pot folosi in  


cadrul aplicatiilor(spre exemplu: campuri de text si date, controale radio, control de tip arbore, 
diverse toolbar-uri sau meniuri de tip aplicatie desktop, etc.). Majoritatea acestor elemente de 
control pot comunica cu server Web folosind Ajax. 
Versiunile mai noi ale acestei platforme asigura o interfata si noi functionalitati asemanatoare 
cu cele gasite in aplicatiile de tip desktop. Ultima varianta aparuta in 2012, vine cu o structura 
revizuita de clase, un pachet nou de date, un set nou de animatii si desene ce foloseste 
SVG(Scalable Vector Graphics) si VML(Vector Markup Language – format grafic bazat pe XML). 
De asemenea, include si o arhitectura optionala ce asigura vizualizare MVC(model-view 
controller) a codului. 

4 INTEGRAREA DATELOR IN PAGINI WEB. TEHNOLOGIA SOAP 

Integrarea datelor se poate realiza fie prin migrarea datelor dintr-un sistem informatic in altul,  
fie prin utilizarea unor produse software ce permit accesul la diferite surse de date fara a mai fi 
nevoie de extragerea, transportul, transformarea si validarea datelor in sistemele sursa si 
incarcarea lor in sistemele destinatie. Principalul standard folosit pentru interschimbul de 
informatii este eXtensible Markup Language(XML), iar cel mai important protocol de 
comunicatii bazat pe acesta si care sta la baza serviciilor Web este Simple Object Access 
Protocol(SOAP). 

Pentru integrarea aplicatiilor trebuie ca schimbul de informatii sa apara intre bazele de 
date(proprietari API ce produc informatie), astfel incat bazele de date trebuiesc privite ca 
puncte principale de integrare. Solutiile de integrare a datelor pot fi grupate in trei categorii 
principale: copierea multipla a bazelor de date, federalizarea datelor si procesarea interfetei.  

Realizand copii ale bazelor de date si distribuindu-le la nivelul aplicatiilor, permite aplicatiei 
detinerea propriului stoc dedicat de date. Replicarea datelor este o varianta a copierii datelor, 
presupunand simpla mutare a datelor intre doua sau mai multe baze de date. Bazele de date 
pot avea proveniente diferite sau modele diferite, insa este fundamental ca acesteia sa i se 
ofere o infrastructura pentru schimbul de date. Avantajele replicarii bazelor de date tin de 
simplitate si de costurile scazute pe care le presupun. 

Aplicaţie Aplicaţie Aplicaţie 

Replicarea datelor Replicarea datelor 


Baza de  
date 1 

  
Baza de  
date 2 

Replicarea  
datelor 

Figura 6 : Replicarea unei baze de date 

Baza de  date 3

O alta modalitate tine de federalizarea datelor, aceasta presupunand integrarea mai multor 
baze de date si a modelelor asociate intr-o singura baza de date, cu o vedere unificata. 
Instrumentele pentru federalizarea datelor plaseaza un nivel software intre bazele de date 
distribuite fizic si aplicatiile care vizualizeaza datele. Acest nivel conecteaza bazele de date 
folosind interfete si mapeaza bazele de date fizice intr-o baza de date virtuala. Avantajul 
folosirii acestui software este ca poate lega tipuri diferite de date intr-un model unificat care 
suporta schimbul de informatie, fiind cea mai eleganta solutie pentru integrarea datelor 
intrucat permite accesul la orice baza de date conectata la sistem printr-o singura interfata
bine  definita. 

In ceea ce priveste solutiile de procesare a interfetei, acestea folosesc interfete ale unor 
aplicatii bine definite pentru a se axa atat pe integrarea aplicatiilor pachet, cat si pe a celor 
obisnuite.Producatorii de astfel de solutii sustin implementari bazate pe procesarea interfetei 
prin oferirea de adaptori, care sa conecteze cat mai multe aplicatii obisnuite sau aplicatii 
pachet, externalizand informatia. O integrare eficienta a mai multor tipuri de aplicatie
defineste  avantajul principal al utilizarii produselor de integrare a aplicatiilor. 
Standardul principal pentru interschimbul de informatie pe Internet este XML, acesta fiind un 
standard robust, ce poate sustine schimbul de informatii si de semantici ale aplicatiilor. XML 
ofera un format comun de schimb de informatie, incapsuland atat datele cat si metadatele, 
permitand aplicaitilor si bazelor de date sa comunice fara a avea informatii una despre cealalta. 
Pentru a comunica, sistemul sursa reformateaza un mesaj, care stie sa citeasca XML. Pentru ca 
aplicatiile ce folosesc XML sa poata sa fie integrate, ele trebube sa externalizeze informatia sub 
forma de XML. Tehnologia middleware-XML gestioneaza extragerea informatiei din sistemul 
sursa, conversia ei in XML si plasarea informatiei in sistemul destinatie, tot procesul fiind 
automat si transparent pentru utilizator. 

Simple Object Access Protocol(SOAP) defineste un format XML bazat pe mesaje, care este 
folosit de aplicatiile bazate pe servici Web pentru a comunica si interopera intre ele pe Web. 
Acesta este un standard pentru codificarea mesajelor in XML si care poate apela functii in alte 
aplicatii. Este similar cu Remote Procedure Calls(RPC) folosit de tehnologii ca DCOM sau
CORBA,  dar elimina o parte din complexitatea utilizarii acestor interfete.  

Standardul SOAP ofera o serie de mecanisme prin care doreste sa acopere o gama foarte
larga  de aspecte aferente calculului distribuit: 

-un mecanism de legatura a mesajelor SOAP la HTTP, acesta fiind cel mai utilizat protocol de 
comunicatie 
-un mecanism de gestionare a erorilor, in cazul producerii unei erori acesta putand transmite 
informatii despre natura erorii si locul producerii acesteia 
-un mecanism de definire a unitatii de comunicatie, acesta incapsuleaza orice mesaj transmis 
intr-un pachet cu structura prestabilita 
-un mecanism de extensibilitate ce ofera posibilitatea de a adauga noi functionalitati pe
aceeasi  structura de baza 
-un mecanism flexibil de reprezentare a datelor ce permite schimbul de date deja formatate
cat  si unele conventii pentru reprezentarea structurilor de date abstracte intr-un limbaj de 
programare 
-un mecanism pentru reprezentarea RPC, implicand definirea unor structuri standard pentru
un  apel de procedura la distanta si de asemenea pentru comunicarea raspunsului -un
mecanism pentru schimbul de documente, ca o alternativa la RPC, pentru evitarea 
granularizarii interfetelor

   Figura 7 :
Mecanisme SOAP de comunicare intre client si server 
Modul de impachetare reprezinta cea mai importanta parte specificata de SOAP. Acesta
consta  in doar cateva elemente XML, dar care ofera o structura si mecanismele de
extensibilitate care  fac din SOAP un fundament pentru calculul distribuit pe baza de XML.
Structura unui mesaj  SOAP contine: 

-un element de inceput numit SOAP envelope, folosit pentru identificarea documentului XML
ca  fiind un mesaj SOAP 
-un antet SOAP, acesta avand o prezenta optionala si care contine informatii de tip header -un
corp SOAP (SOAP body) ce cuprinde datele transmise si receptionate prin intermediul 
mesajului 
-un element EROARE(SOAP fault), ce contine erori si informatii despre starea mesajului
CONCLUZII 

Dezvoltarea accentuata a aplicatiilor de tip RIA a condus la aparitia a numeroase platforme de 
dezvoltare, care sa tina pas cu tendintele moderne si care sa ofere un grad de interactivitate si  
continut multimedia similar cu cel ce se regaseste in aplicatiile traditionale de tip desktop. Pe 
langa solutiile clasice cum ar fi Adobe Flash, care reprezinta chiar si in momentul de fata cea 
mai populara platforma de dezvoltare RIA, au aparut numeroase framework-uri open source ce 
au un suport puternic din partea dezvoltatorilor, au cunoscut o evolutie foarte rapida si
suporta  numeroase formate media de tip “cross-platform”. De cele mai multe ori o aplicatie de
tip RIA  are o complexitate ridicata, astfel incat se impune o serie de restrictii si cerinte privind
felul in  care aplicatia este accesata si utilizata si modul in care aceasta acceseaza resursele 
calculatorului(este necesar suport JavaScript sau existenta unui plug-in).  

Dintre noile alternative de dezvoltare a aplicatiilor Web, prezinta un interes mai ridicat setul de 
instrumente pus la dispozitie de catre Google, ce permite crearea de aplicatii fara cunostine 
prea avansate de scripting HTML sau JavaScript. Acest lucru reprezinta o prioritate pentru 
framework-urile noi aparute, intrucat acestea incearca sa ofere pe langa functionalitatile deja 
existente pe platformele clasice, si o usurinta a mediului de dezvoltare, pastrand in acelasi timp 
acelasi nivel de interactivitate si continut ca in cazul aplicatiilor traditionale. Alte framework-uri 
bazate pe GWT, cum este si Vaadin, vin cu imbunatatiri pe anumite puncte cheie cum ar fi 
securitatea(prin validarea datelor pe partea de server) sau interfata cu utilizatorul(prin 
intermediul unei interfete GUI). 

In ceea ce priveste incapsularea datelor in pagini web, principalele standarde ce intervin in 
interschimbul de informatii sunt XML si SOAP. Similar cu standardul RPC, SOAP elimina o parte 
din complexitatea acestuia, oferind o serie de mecanisme ce rezolva diferile probleme ce apar 
in sistemele distribuite. Astfel, se asigura mecanisme de legatura catre protocolul HTTP, de 
detectare si eliminare a erorilor sau de reprezentare a datelor folosind formate de date 
adaptabile.
BIBLIOGRAFIE 
Carl-David Granback, ”Rich Internet Applications(RIAs)”, University of Gothenburg,

2009 Irene Garrigos, Santiago Melia, „Personalizing the Interface in Rich Internet

Applications” http://www.tutorialeonline.net/ro/article/protocolul-soap 

http://en.wikipedia.org/wiki/Google_Web_Toolkit 

http://en.wikipedia.org/wiki/Ext_JS

Fire de execuţie (Thread) în Java  

1. Introducere 
Firele de execuţie fac trecerea de la programarea secvenţiala la programarea concurentă.
Un program secvenţial reprezintă modelul clasic de program: are un început, o secvenţă de 
execuţie a instrucţiunilor sale şi un sfârşit. Cu alte cuvinte, la un moment dat programul are un 
singur punct de execuţie. Un program aflat în execuţie se numeste proces. Un sistem de operare 
multitasking (UNIX, Windows) poate rula oricâte procese în acelasi timp (concurent), alocând 
periodic cuante din timpul de lucru al CPU fiecărui proces.  
O aplicaţie Java rulează în interiorul unui proces al sistemului de operare. Procesul
constă în segmente de cod şi segmente de date mapate într-un spatiu virtual de adresare. Fiecare
proces  deţine un număr de resurse alocate de către sistemul de operare, cum ar fi fişiere
deschise, zone  de memorie alocate dinamic sau fire de execuţie. Resursele alocate procesului
sunt eliberate la  terminarea execuţiei procesului. Procesul nu execută instrucţiuni, este un spaţiu
de adresare  comun pentru unul sau mai multe fire de execuţie. Firele de execuţie sunt cele care
execută instrucţiunile.  
Un fir de execuţie poate fi asemănat cu o versiune redusă a unui proces, ambele rulând 
simultan şi independent pe o structură secvenţiala de execuţie a instrucţiunilor lor. De asemenea 
execuţia simultană a firelor de execuţie în cadrul unui proces este similară cu execuţia 
concurentă a proceselor: sistemul de operare va aloca ciclic cuante din timpul procesorului 
fiecărui fir de execuţie până la terminarea lor. Din acest motiv firele de execuţie mai sunt
numite  şi procese uşoare. Un program îşi poate defini însă nu doar un fir de execuţie ci oricâte,
ceea ce  înseamnă că în cadrul unui proces se pot executa simultan mai multe fire de execuţie,
permiţând  execuţia concurentă a sarcinilor independente ale acelui program.  
Java fiind un limbaj interpretat, procesul deţine codul interpretorului, iar codul binar
Java  (bytecode) este tratat ca o zonă de date de către interpretor. Deci firele de excuţie sunt
create de  fapt de către interpretorul Java. La lansarea în execuţie a unei aplicaţii Java este creat,
automat, şi  un prim fir de execuţie, numit firul principal. Acesta poate să creeze alte fire de
execuţie, care la  randul lor pot crea alte fire, şi aşa mai departe. 
  

Deosebirea majoră între un fir de execuţie si un proces constă în faptul că firele de 
execuţie nu pot rula decât în cadrul unui proces. O altă deosebire rezultă din faptul că fiecare 
proces are propria sa memorie (propriul sau spatiu de adrese) iar la crearea unui nou proces este 
realizată o copie exactă a procesului părinte; la crearea unui fir de execuţie nu este copiat decât 
codul procesului părinte; toate firele de execuţie au deci acces la aceleaşi date, datele procesului 
original. Aşadar un fir de execuţie mai poate fi privit şi ca un context de execuţie în cadrul unui 
proces părinte.  

2. Stările unui fir de execuţie  


Fiecare fir de executie are propriul său ciclu de viaţă: este creat, devine activ prin lansarea sa în 
execuţie şi, la un moment dat, se termină. În continuare vom vedea mai îndeaproape stările în 
care se poate găsi un fir de execuţie.  
Un fir de execuţie (thread) se poate afla la un moment dat în una din următoarele stări:  •
running (starea spre care aspiră toate firele de execuţie),  
• blocked (blocare, adormire, suspendare, aşteptare),  
• ready (gata de execuţie),  
• dead (terminat).  
Fiecare fir de execuţie (thread) are o prioritate de execuţie. În general, thread-ul cu prioritatea 
cea mai mare este cel care va accesa primul resursele sistem. O clasă aparte de thread-uri este 
aceea care conţine thread-urile de tip Daemon care sunt thread-uri “de serviciu” (sunt în 
serviciul altor thread-uri). Când se porneşte maşina virtuală Java există un singur thread care nu 
este de tip Daemon şi care de obicei apelează metoda main(). JVM rămâne pornită atâta vreme 
cât există activ un thread care să nu fie de tipul Daemon. În figura de mai jos se pot observa 
stările unui fir de execuţie, precum şi trecerea de la o stare la alta: 
  
start()  
 New  
(instantierea unui tread)  

Executie  
yield()  

stop()  

 destroy() 
 stop(), exit()  
 exceptie  
 normal: run() 
Running Ready  

primul la rand  
sf. I/O  

resume() 
 I/O  
sf. sleep()  
sleep(), wait(), join()  
notify()  
suspend()  

Dead Blocked  
destroy(), stop()  

Un thread instanţiat cu new se află într-o stare iniţială. El poate trece în starea de execuţie 
apelând start() sau se poate termina apeland stop() (de fapt, metoda stop() este învechită – se
foloseşte deprecated()). Starea de execuţie are de fapt două substări: execuţia propriu-zisă
(running) şi aşteptarea în coadă (ready). JVM alocă fiecărui thread câte o cuantă de timp pentru 
execuţie, făcând execuţia aparent simultană a mai multor thread-uri. După ce un thread îşi 
epuizează timpul alocat, se întrerupe execuţia lui şi se asează în coada de aşteptare, cedându-se 
locul altui thread. Cand îi vine randul, thread-ul pus în coadă îşi va relua execuţia din punctul în 
care a fost oprit. Un thread poate fi forţat să fie pus în coadă folosind functia yield(). Din starea 
de execuţie, thread-ul poate trece în 'dead' fie prin terminarea normala run(), fie prin aruncarea 
unei exceptii, fie prin metoda stop() sau fie prin stoparea JVM (exit()). Din starea de execuţie,
thread-ul poate trece într-o stare de blocare temporară (blocked). Unul din aceste cazuri este 
apelul suspend(), revenirea făcându-se cu apelul lui resume(). Tot în starea de blocare, thread-ul 
poate trece prin apelul metodei sleep(). Se revine înapoi când se termină timpul dat prin 
parametrii metodei sau în momentul în care se aruncă o excepţie de tipul InterruptedException
prin apelul lui interrupt().  
Tot în starea blocat (blocked) se poate ajunge şi prin apelul metodei join(), caz în care revenirea 
se face în momentul în care thread-ul si-a terminat execuţia. Cazul wait()-notify() se referă la
  

sincronizarea thread-urilor (vezi paragraful următor). Blocarea are loc şi în cazul unei operatii
de  I/O. Thread-ul rămâne blocat pâna la terminarea operaţiei. Tot în această stare se poate trece 
printr-un apel al lui synchronized. Din această stare de blocare temporară, se trece în starea
'dead'  apelând stop(). Metoda destroy() distruge thread-ul făra a face dealocările necesare.  
Observaţii:  
• Un fir de execuţie aflat în starea dead nu mai poate fi restartat.  
Această încercare este privită ca o eroare de execuţie furnizând excepţia:  
java.lang.IllegalThreadStateException  
• Putem afla starea unui thread folosind funcţia:  
 public final native boolean isActive();  
Cuvântul rezervat native precizează că funcţia este implementată într-o bibliotecă externă. 
Aceasta returnează true dacă thread-ul este runnable (în execuţie) sau blocat şi false dacă
thread-ul este nou creat sau terminat (dead).  

3. Lucrul cu firele de execuţie în Java  


Ca orice alt obiect Java, un fir de executie este o instanta a unei clase. Firele de executie definite 
de o clasa vor avea acelasi cod si, prin urmare, aceeasi secventa de instructiuni. Crearea unei 
clase care sa defineasca fire de excutie poate fi facuta prin doua modalitati:  • prin extinderea
clasei Thread  
• prin implementarea interfetei Runnable   
Orice clasa ale carei instante vor fi executate într-un fir de executie trebuie declarata ca fiind 
Runnable. Aceasta este o interfata care contine o singura metoda, si anume metoda run. Asadar, 
orice clasa ce descrie fire de executie va contine o metoda run în care este implementat codul ce 
va fi executat de firul de executie.  
Interfata Runnable este conceputa ca fiind un protocol comun pentru obiectele care doresc sa 
execute un cod pe durata existentei lor (care reprezinta fire de executie). Cea mai importanta 
clasa care implementeaza interfata Runnable este clasa Thread. Clasa Thread implementeaza un 
fir de executie generic care, implicit, nu face nimic. Cu alte cuvinte metoda run nu contine nici 
un cod.  
Orice fir de executie este o instanta a clasei Thread sau a unei subclase a sa. 
  

3.1. Extinderea clasei Thread  


Cea mai simpla metoda de a crea un fir de executie care sa realizeze ceva este prin extinderea 
clasei Thread si supradefinirea metodei run a acesteia. Formatul general al unei astfel de clase 
este:  
public class Fir extends Thread {  

 public Fir(String nume) {  


 super(nume);   
 //apelez constructorul superclasei Thread  
 }  
 public void run() {  
 //codul executat de firul de executie  
 }  
 }  
Prima metoda a clasei este constructorul, care primeste ca argument un sir ce va reprezenta 
numele firului de executie creat în momentul când constructorul este apelat.   Fir t = new
Fir("Java")  
 //creeaza un fir de executie cu numele Java  
In cazul în care nu vrem sa dam nume firelor de executie pe care le cream atunci putem renunta 
la definirea acestui constructor si sa ramânem doar cu constructorul implicit, fara argumente, 
care creeaza un fir de executie fara nici un nume. Ulterior acesta poate primi un nume cu
metoda  setName(String).  
A doua metoda este metoda run, în care scriem efectiv codul pe care trebuie sa-l execute firul de 
executie.  
 Un fir de executie creat nu este automat pornit, lansarea sa în executie se realizeaza prin
metoda  start, definita de asemenea în clasa Thread.  

 Fir t = new Fir("Java")  


 t.start()  
 //creeaza si lanseaza un fir de executie 
  

3.2. Implementarea interfetei Runnable  


Ce facem însa când dorim sa cream o clasa care instantiaza fire de executie dar aceasta are deja
o  superclasa, stiind ca în Java nu este permisa mostenirea multipla, de tipul:  class Fir extends
Parinte, Thread // nu este permis  
In acest caz nu mai putem extinde clasa Thread ci trebuie sa implementam direct în clasa
noastra  interfata Runnable. Clasa Thread implementeaza ea însasi interfata Runnable si, din
acest motiv,  la extinderea ei obtineam o implementare implicita a interfetei. Asadar, interfata
Runnable permite unei clase sa fie active, fara a extinde clasa Thread.  
Interfata Runnable se gaseste în pachetul java.lang si este definita astfel:  
 public interface Runnable {  
 pulic abstract void run( );   
 }  
Prin urmare, o clasa care instantiaza fire de executie prin implementarea interfetei Runnable
trebuie obligatoriu sa implementeze metoda run.  
Formatul general al unei clase care implementeaza interfata Runnable

este:  public class SimpleThread implements Runnable {  

 private Thread simpleThread = null;  

 public SimpleThread() {  


 if (simpleThread == null) {  
 simpleThread = new Thread(this);  
 simpleThread.start();  
 }  
 public void run() {  
 //codul executat de firul de executie  
 }  
 } 
  

Spre deosebire de modalitatea anterioara, se pierde însa tot suportul oferit de clasa Thread pentu 
crearea unui fir de executie. Simpla instantiere a unei clase care implemeneaza interfata 
Runnable nu creeaza nici un fir de executie. Din acest motiv crearea firelor de executie prin 
instantierea unei astfel de clase trebuie facuta explicit.  
In primul rând trebuie declarat un obiect de tip Thread ca variabila membra a clasei 
respective. Acest obiect va reprezenta firul de executie propriu zis al carui cod se gaseste în
clasa  noastra.  
 private Thread simpleThread = null;  
Urmatorul pas este instantierea si initializarea firului de executie. Acest lucru se 
realizeaza ca pentru orice alt obiect prin instructiunea new, urmata de un apel la un constructor
al  clasei Thread, însa nu la oricare dintre acestia. Trebuie apelat constructorul care sa primeasca 
drept argument o instanta a clasei noastre. Dupa creare, firul de executie poate fi lansat printr-un 
apel la metoda start. Aceste operatiuni sunt scrise de obicei în constructorul clasei noastre
pentru  a fi executate la initializarea unei instante, dar pot fi scrise oriunde în corpul clasei sau
chiar în  afara ei:  
 simpleThread = new Thread( this );  
 simpleThread.start();  
Specificarea argumentului this în constructorul clasei Thread determina crearea unui fir 
de executie care la lansarea sa va cauta în clasa noastra metoda run si o va executa. Acest 
constructor accepta ca argument orice instanta a unei clase "Runnable". Asadar metoda run nu 
trebuie apelata explicit, acest lucru realizându-se automat la apelul metodei start.  
 Apelul explicit al metodei run nu va furniza nici o eroare, însa aceasta va fi executata ca  orice
alta metoda, deci nu într-un fir de executie.  
Vom vedea, în exemplul de mai jos, că implementarea interfetei Runnable permite o flexibilitate 
sporita în lucrul cu fire de executie:  
public class Fir  
{  
 public static void main(String args[])  
 {  
 FirdeExecutie fir=new FirdeExecutie();  
 Thread thread=new Thread(fir); 
  

 thread.start();  
 System.out.println("Revenim la main");  
 }  
}   
class A   
{  
 public void afis()  
 {  
 System.out.println("Este un exemplu simplu");  
 }  
}   
class FirdeExecutie extends A implements Runnable  
{  
 public void run()  
 {  
 for(int i=0;i<5;i++)   
 System.out.println("Pasul "+i);   
 afis();  
 System.out.println("Run s-a terminat");  
 }  
}   

4. Sincronizarea firelor de execuţie  


Fiecare fir de execuţie are o viaţa proprie şi nu este interesat de ceea ce fac celelalte fire de 
execuţie. În cazul în care calculatorul este dotat cu mai multe procesoare, atunci diferitele fire
de  execuţie ale aceluiaş proces pot fi planificate pentru execuţie pe diferite procesoare. Pentru
cel  care proiecteaza aplicaţia, acest lucru este nesemnificativ, deoarece toate aceste lucruri cad
în  sarcina maşinii virtuale şi nu în sarcina programatorului. Totuşi apare o problemă în cazul 
aplicaţiilor care se rulează pe mai multe fire, şi anume accesul resurselor comune. Ca să nu
apara  probleme, accesul la resursele utilizate în comun trebuie sincronizat. Sincronizarea se
bazeaza pe  conceptul de monitor introdus de către C.A. Hoare. Monitor înseamnă un obiect
special, care 
  

permite numai şi numai unui singur thread să apeleze o metodă a lui, la un moment dat. Deci un 
monitor poate fi considerat un “lacăt” ataşat unei resurse pentru a preveni utilizarea ei în
paralel.  Un fir de execuţie ocupă un monitor dacă apelează o metodă sincronizată. Daca un fir
ocupă un  monitor, un alt fir care încearcă ocuparea aceluiaş monitor, este blocat şi asteaptă pâna
la  eliberarea monitorului.  
Oricare obiect, care conţine unul sau mai multe metode sincronizate, are şi un monitor ataşat. 
Metodele sincronizate se definesc în modul următor:  
 public synchronized void my_method() {...}  
Cu ajutorul cuvantului-cheie synchronized putem serializa executia anumitor metode. Metodele 
wait() şi notify() ale clasei Object extind aceasta capabilitate. Utilizand wait() şi notify(), un fir
de  execuţie poate elibera monitorul ocupat, aşteptând un semnal pentru reocuparea acestuia. 
Metodele pot fi utilizate doar în cadrul metodelor sincronizate sau în blocuri de instrucţiuni 
sincronizate. Executând wait() într-un bloc sincronizat, firul eliberează monitorul şi “adoarme”. 
De obicei firul recurge la această posibilitate când trebuie să aştepte apariţia unui eveniment într 
o altă parte a aplicaţiei. Mai târziu, când apare evenimentul, firul, în care a apărut evenimentul, 
apelează notify() pentru a trezi firul adormit. Firul trezit ocupă din nou monitorul şi îşi continuă
activitatea.  

4.1 Exemplu: Problema producător-consumator  


Pentru a înţelege mai bine modalitatea de sincronizare a firelor de execuţie vom considera 
problema producatorului/consumatorului, în care producătorul genereaza un flux de date care 
este preluat şi prelucrat de către consumator.  

Să considerăm de exemplu o aplicaţie Java în care să presupunem că producatorul generează


nişte numere şi le plasează, pe rând, într-un buffer iar consumatorul citeşte numerele din acel 
buffer pentru a le interpreta. Avem de-a face cu fire de execuţie concurente care folosesc o 
resursă comună: respectiv un vector şi, din acest motiv, ele trebuie sincronizate într-o manieră
care să permită decurgerea normală a activităţii lor.  
Să considerăm următoarea situaţie: 
  

• Producătorul generează numerele întregi de la 1 la 10, fiecare la un interval neregulat  cuprins


între 0 si 100 de milisecunde. Pe masură ce le generează încearcă sa le plaseze  într-o zonă
de memorie (o variabilă întreagă) de unde să fie citite de către consumator.  
• Consumatorul va prelua, pe rând, numerele generate de către producător şi va afişa  valoarea
lor pe ecran.  
Pentru a fi accesibilă ambelor fire de execuţie, vom încapsula variabila ce va conţine numerele 
generate într-un obiect descris de clasa Buffer şi care va avea doua metode put (pentru punerea 
unui numar în buffer) şi get (pentru obţinerea numărului din buffer).  
Fără a folosi nici un mecanism de sincronizare clasa Buffer arată astfel:  
class Buffer {  
 private int number = -1;  
 public int get() {  
 return number;   
 }  
 public void put(int number) {  
 this.number = number;  
 }  
}   

Vom implementa acum clasele Producator şi Consumator care vor descrie cele două fire de 
execuţie. Ambele vor avea o referinţă comună la un obiect de tip Buffer prin intermediul căruia 
îşi comunică valorile.  
class Producator extends Thread {  
 private Buffer buffer;   
 public Producator(Buffer b) {  
 buffer = b;  
 }  
 public void run() {  
 for (int i = 0; i < 10; i++) {  
 buffer.put(i);  
 System.out.println("Producatorul a pus:\t" + i); 
 try {  
 sleep((int)(Math.random() * 100));  
 } catch (InterruptedException e) { }  
 }  
 }  
}   
class Consumator extends Thread {  
 private Buffer buffer;   
 public Consumator(Buffer b) {  
 buffer = b;  
 }  
 public void run() {  
 int value = 0;  
 for (int i = 0; i < 10; i++) {  
 value = buffer.get();  
 System.out.println("Consumatorul a primit:\t" + value);   }  
 }  
}   
//Clasa principala  
public class TestSincronizare1 {   
 public static void main(String[] args) {  
 Buffer b = new Buffer();  
 Producator p1 = new Producator(b);  
 Consumator c1 = new Consumator(b);  
 p1.start();  
 c1.start();  
 }  

După cum ne aşteptam rezultatul rulării acestui program nu va rezolva nici pe departe problema 
propusă, motivul fiind lipsa oricărei sincronizări între cele două fire de execuţie. Mai precis, 
rezultatul va fi de forma:  
 Producatorul a pus: 0  
 Consumatorul a primit: 0  
 Consumatorul a primit: 0  
 Consumatorul a primit: 0  
 Consumatorul a primit: 0  
 Consumatorul a primit: 0  
 Consumatorul a primit: 0  
 Consumatorul a primit: 0  
 Consumatorul a primit: 0  
 Producatorul a pus: 1  
 Producatorul a pus: 2  
 Producatorul a pus: 3  
 Producatorul a pus: 4  
 Producatorul a pus: 5  
 Producatorul a pus: 6  
 Producatorul a pus: 7  
 Producatorul a pus: 8  
 Producatorul a pus: 9  
Ambele fire de execuţie accesează resursa comună, adică obiectul de tip Buffer, într-o manieră
haotică şi acest lucru se întâmplă din două motive:  
• consumatorul nu asteaptă înainte de a citi ca producatorul sa genereze un număr şi va  prelua
de mai multe ori acelaşi număr.  
• producătorul nu aşteaptă consumatorul să preia numărul generat înainte de a produce un  altul,
în felul acesta consumatorul va "rata" cu siguranţă unele numere (în cazul nostru  aproape pe
toate).  
Problema care se ridică în acest moment este: cine trebuie să se ocupe de sincronizarea celor 
două fire de execuţie: clasele Producator şi Consumator sau resursa comună Buffer? Răspunsul
este: resursa comuna Buffer, deoarece ea trebuie să permită sau nu accesul la  conţinutul său şi
nu firele de execuţie care o folosesc. În felul acesta efortul sincronizarii este  transferat de la
producator/consumator la un nivel mai jos, cel al resursei critice. 

Activitaţile producătorului şi consumatorului trebuie sincronizate la nivelul resursei comune în 


două privinţe:  
1. Cele două fire de execuţie nu trebuie să acceseze simultan buffer-ul; acest lucru se  realizează
prin blocarea obiectului Buffer atunci când este accesat de un fir de execuţie,  astfel încât
nici nu alt fir de execuţie să nu-l mai poată accesa.  
2. Cele două fire de execuţie trebuie să se coordoneze, adică producătorul trebuie să gasească o
modalitate de a "spune" consumatorului că a plasat o valoare în buffer, iar  consumatorul
trebuie să comunice producătorului că a preluat aceasta valoare, pentru ca  acesta să poata
genera o alta. Pentru a realiza aceasta comunicare, clasa Thread pune la  dispozitie metodele
wait(), notify(), notifyAll().  

Folosind sincronizarea clasa Buffer va arata astfel:  


class Buffer {  
 private int number = -1;  
 private boolean available = false;   
 public synchronized int get() {  
 while (!available) {  
 try {   
 wait();   
 //asteapta producatorul sa puna o valoare  
 } catch (InterruptedException e) { }  
 }  
 available = false;  
 notifyAll(); 
return number;  
 }  
 public synchronized void put(int number) {  
 while (available) {  
 try {   
 wait();   
 //asteapta consumatorul sa preia valoarea  
 } catch (InterruptedException e) { }  
 }  
 this.number = number;  
 available = true;  
 notifyAll();  
 }  
}   
Rezultatul obtinut va fi cel scontat:  
 Producatorul a pus: 0  
 Consumatorul a primit: 0  
 Producatorul a pus: 1  
 Consumatorul a primit: 1  
 . . .  
 Producatorul a pus: 9  
 Consumatorul a primit: 9 

Java Servelets 

1. Introducere 
Servleturile Java (Java Servelets) reprezintă componente Web gestionate de un container
care poate genera conţinut dinamic sau care poate controla fluxul unei aplicaţii Web. Servletul 
reprezinta o clasa Java care extinde o aplicatie aflata pe un server de web, generând in mod 
dinamic fisiere HTML bazate pe cererea clientilor. 
Servleturile Java interactionează cu clienţii printr-o paradigmă de tip cerere-raspuns şi 
sunt specificate în "Java Servlet API Specification".  
Servleturile Java sunt parte integrantă a platformei J2EE. Java Platform, Enterprise 
Edition (Java EE) defineşte standardul pentru dezvoltarea de aplicaţii distribuite, multi
platformă, bazate pe componente sau pe mai multe niveluri. Printre cele mai importante 
tehnologii ale platformei J2EE amintim: 
• JAXP Java API for XML Processing 
• Servlet 
• JSP Java Server Pages 
• EJB Enterprise Java Beans 
• JDBC Java Database Connectivity 
• JNDI Java Naming and Directoy Interface 
• JTA Java Transaction 
• JSM Java Message Service 
Această tehnologie are ca parteneri: Sun GlassFish, IBM WebSphere, Oracle Application 
Server, Apache Tomcat, Geronimo, RedHat (JBoss) Application Server, BEA WebLogic 
Application Server, SAP NetWeave, Macromedia JRun, etc. iar cei mai mari competitori ai săi 
sunt: Microsoft .NET şi "LAMP" (Linux, Apache, MySQL, PHP/Python/Perl).
 

2. Proprietati ale servleturilor 


1. Eficienta 
∙ poate folosi thread-uri din Java pentru a rula mai multe procese odata. ∙
raman activi si dupa ce raspunsul a fost intors. 
∙ ofera o infrastructura pentru decodificare datelor trimise dintr-un HTML.
2. Portabilitate 
∙ poate rula pe mai multe masini de calcul. 
3. Securizare buna a datelor 
4. Java 
∙ Au la dispoziţie întreg API-ul Java 
5. Procesarea sau stocarea datelor informatiilor date intr-o forma HTML. 6. Proceseaza intrarile
utilizatorului si intoarce un raspuns corespunzator sub forma unui fiser  HTML – poate intoarce
raspunsul primit printr-un query la o baza de date. 7. Faciliteaza comunicarea (realizarea unui
forum). 
8. Suporta interactiuni intre clienti (jocuri multiplayer).
 

3. Modul de lucru cu servleturi 


În figura de mai jos este sugerat modul de funcţionare al unui servlet: 
Cererea si raspunsul HTTP (HyperText Transfer Protocol) reprezinta un protocol folosit de
catre  WWW. In fiecare cerere HTTP este specificata o metoda: 
∙ Metoda GET – este folosita in principal pentru a primi informatii, iar cererea este 
transmisa serverului sub forma unui URL. 
∙ Metoda POST – folosita pentru a trimite date catre server; datele sunt incapsulate  intr-un
pachet si trimise catre server. 
Clasele şi interfeţele necesare pentru dezvoltarea componentelor servlet se găsesc în 
pachetele javax.servlet şi javax.servlet.http. Orice servlet trebuie să implementeze interfaţa 
Servlet fie direct, fie prin extinderea clasei HttpServlet care implementează această interfaţă, 
dacă se prelucrează numai cereri HTTP.  
Un servlet poate fi instalat (deployed) în orice container de servleţi, cum este serverul de 
aplicaţii J2EE sau serverul de Web Tomcat. Pentru instalare sunt prevăzute anumite convenţii de 
stocare a claselor, care sunt respectate de toate containerele Web, dar mai există şi unele aspecte 
particulare fiecărui container, care trebuie să fie citite din documentaţia respectivă. De exemplu, 
serverul Tomcat ascultă cererile de la clienţi pe portul 8080 şi pentru fiecare aplicaţie Web 
(componentă) se defineşte un subdirector de bază (document root) în directorul webapps al 
instalării care conţine:  
∙ Fişiere HTML, JSP, imagini 
∙ /WEB-INF/web.xml: descriptorul de deployment al aplicaţiei 
 

∙ /WEB-INF/classes: clasele Java ale aplicaţiei /WEB/INF/lib – bibliotecile jar folosite  de


aplicaţie 
In continuare vom descrie cele mai importante etape pentru dezvoltarea servleţilor: 

1. Utilitare 
Pentru a crea si a rula servleti, este nevoie de instalarea JavaServerTM Web
Development Kit. 
Alte posibilitati de a rula servleti constau in instalarea: 
∙ Apache Tomcat 
∙ Allaire JRun 
∙ New Atlanta’s ServletExec 

2. Configurarea se poate face astfel: 


∙ Dupa descarcarea arhivei JSWDK, dezarhivam intr-un director, sa zicem E:\jswdk. ∙ Daca
JDK este instalat in E\jdk, pentru a ne asigura ca servletii vor functiona, avem  nevoie sa
copiem din E:\jswdk\lib pachetul servlet.jar in G:\jdk\jre\lib\ext. ∙ Mai departe, putem
compila un servlet fara sa ne mai apara o sumedenie de erori.  
4. Structura unui servlet 
Pentru a fi un servlet, o clasă trebuie să moştenească HTTPServlet şi să suprascrie doGet sau 
doPost sau ambele. 
Metodele doGet şi doPost au două argumente: 
∙ HttpServletRequest – are metode care permit primirea datelor; 
∙ HttpServletResponse – are metode cu care specificam tipul de raspuns pe care il dam si,  mai
ales, permite obtinerea unui obiect de tip PrintWriter pe care il folosim pentru a  trimite
raspunsuri pentru clienti. 
Mai jos este descrisă structura generală a unui servlet: 

import javax.servlet.*; 
import javax.servlet.http.*;
 

import java.io.*; 

public class MyServlet extends HttpServlet { 


public void init { ... } 
public void service { ... } 
public void doGet { ... } 
public void doPost { ... } 
public String getServletInfo { ... } 
public void destroy { ... } 

3. Pregatirea rularii 
∙ Compilarea fisierului se face ca pentru o aplicatie java obisnuita. 
∙ Clasa rezultata de la compilare trebuie copiata apoi in directorul:  
G:\jswdk\webpages\WEB-INF\servlets 
∙ Inainte de a rula aplicatia, avem nevoie sa pornim serverul: in directorul G:\jswdk rulam 
startserver.bat 

4. Rularea 
∙ Copiem clasa rezultata din compilare in directorul  
G:\jswdk\webpages\WEB-INF\servlets 
∙ Deschidem un browser (Internet Explorer) si scriem:  
http://localhost:8080/servlet/UnServlet 
∙ Dacă se foloseşte serverul Tomcat, setările generale sunt: 
Activăm servletul InvokerServlet, decomentând liniile ce conţin elementul <servlet
name>invoker</servlet-name> din fişierul conf/web.xml 
Activăm opţiunea de reîncărcare a claselor servleturilor, sepcificând în fişierul 
conf/server.xml: 
<Host name="localhost" debug="0" appBase="webapps" ... > 
<DefaultContext reloadable="true"/>
 

5. Exemplu 
import java.io.*; 
import javax.servlet.*; 
import javax.servlet.http.*; 
public class UnServlet extends HttpServlet { 
  public void doGet(HttpServletRequest cerere, 
  HttpServletResponse raspuns) 
  throws ServletException, IOException { 
  PrintWriter out = raspuns.getWriter(); 
  out.println(“Uite ca a realizat afisarea!!"); 
  } 

Observaţie: 
Majoritatea servletilor genereaza HTML, si nu text precum cel anterior – pentru aceasta este 
nevoie de doi pasi in plus: 
∙ Sa spunem browser-ului ca generam un HTML – aceasta se face prin metoda  setContentType a
obiectului de tip HttpServletResponse. 
∙ Al doilea pas constă în modificarea datelor pe care le afişăm pentru a construi corect o  pagina
HTML 
Exemplu 
public void doGet(HttpServletRequest cerere, HttpServletResponse raspuns) throws
ServletException, IOException { 
raspuns.setContentType("text/html"); 
PrintWriter out = raspuns.getWriter(); 
out.println("<HTML>\n<HEAD><TITLE>” + 
“HTML generat din Servlet” + 
“</TITLE></HEAD>\n<BODY>\n" + 
"<H1>Iata ca functioneaza si asta!</H1>\n" + 
"</BODY></HTML>"); 
 }
 

5. Ciclul de viaţă al unui servlet 


Ciclul de viaţă al unui servlet constă în urmatoarele etape: 
∙ Serverul încarcă şi iniţializează servletul. 
∙ Servletul tratează cereri venite din partea unor clienţi Web. 
∙ Serverul elimină servletul din memorie. 

Iniţializarea 
După încărcarea în memorie a servletului, serverul va apela metoda init a acestuia. Iniţializarea 
este executată o singură dată pe toată durata de viaţă a servletului. Nici o cerere nu va fi tratată
până la terminarea iniţializării. 
Servletul nu va deveni funcţional dacă: 
∙ Pe parcursul iniţializării va fi aruncată o excepţie de tipul ServletException. ∙
Metoda init nu se termină într-o perioadă specificată de server. 
Cele mai uzuale acţiuni executate în metoda init sunt: 
∙ Citirea unor parametri de iniţializare a servletului. 
∙ Deschiderea unor fişiere. 
∙ Realizarea unor conexiuni la baze de date. 
∙ Realizarea unor conexiuni în reţea. 
Dacă apare o eroare la realizarea acestor operaţiuni care compromite capacitatea servletului de a 
satisface cererile clienţilor trebuie aruncată o excepţie de tipul UnavailableException. 

Distrugerea 
La distrugerea unui servlet serverul apelează automat metoda destroy a acestuia. Uzual, în 
metoda destroy vor fi inchise fişierele sau conexiunile deschise în metoda init. Metoda destroy 
este apelată doar după ce firele de execuţie service s-au terminat sau o anumită perioadă de timp 
specificată in parametrii de configurare ai serverului s-a scurs. Metoda este executată o singură 
dată şi, după apelul ei, nu va mai fi realizat nici un apel al metodei service. Metoda service 
trebuie să verifice în permanenţă dacă nu trebuie să se oprească, chiar dacă nu terminase cu 
generarea raspunsului, şi să elibereze resursele folosite. Aşteptarea, în metoda destroy,
terminării  tuturor metodelor service active.
 

6. Rescrierea metodelor doGet() si doPost() 


Get si Post sunt denumite astfel din punctul de vedere al clientului: 
∙ Daca un client cere date de la server, este indicat ca browserul sa trimita o cerere de tip  GET
catre servlet care face ca metoda doGet sa fie apelata. 
∙ Cand clientul doreste doar sa trimita date catre servlet, este indicat ca browserul sa trimita  o
cerere POST catre servlet care face ca metoda doPost sa fie cea apelata. 

Exemplu de utilizare a metodei doPost 


<HTML>  
<BODY>   
<FORM ACTION=http://localhost:8080/servlet/PostEx 
METHOD="POST">  
Nume: 
<INPUT TYPE="text" NAME="nume"><br> 
E-mail: 
<INPUT TYPE="text" NAME="email"><br> 
<INPUT TYPE=reset>  
<INPUT TYPE=submit VALUE="Send"> 
</FORM>  
</BODY>  
</HTML>   

Metoda doPost 
public void doPost(HttpServletRequest cerere, HttpServletResponse raspuns) throws
ServletException, IOException { 
raspuns.setContentType("text/html"); 
PrintWriter out = raspuns.getWriter(); 
out.println("<html> <head><title> Forma Post </title>" ); 
out.println("<body>");  
out.println("<h3>Salutare, " ); 
out.println(raspuns.getParameter("nume") + "</h3>"); 
 

out.println(“Adresa ta de e-mail… "); 


out.println(raspuns.getParameter("email"));  
out.println("</body></html>");  
out.close();  

7. Exemplu de implementare  
Cream un DSN astfel: 
∙ Start – Control Panel – Administrative Tools – Data Sources (ODBC) – Add.. – SQL  Server –
Finish. 
∙ La name, specificam numele DSN-ului (asa cum il specificam din programul Java) ∙
La server trebuie completat cu numele calculatorului unde este instalat serverul. ∙
Continuăm apasând butonul next. 
∙ Inregistram DSN-ul specificând baza de date 
Structura tabelei pe care o vom accesa este de forma: 
id_user 
nume 
parola 
Data 
Vom citi dintr-un browser un ID si vom afişa toate datele persoanei cu ID-ul respectiv. Pagina 
HTML va fi de forma: 
<html> 
<head> 
<title>Accesam baza de date!</title> 
</head> 
<body> 
<FORM ACTION="http://localhost:8080/servlet/ServletBD" 
METHOD="GET">  
I.D.<input type="text" name="id"><br> 
<input type="submit" value = "Afiseaza tot!"> 
</form>
 

</body> 
</html> 

Metoda init a servletului este următoarea: 


public void init(){ 
try{ 
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");   
con = DriverManager.getConnection("jdbc:odbc:student", "test",
"test"); stmt = con.createStatement();  
}catch(Exception e){System.out.println(e);} 

Metoda doGet a servletului este următoarea: 


public void doGet(HttpServletRequest cerere, HttpServletResponse raspuns) 
throws ServletException, IOException { 
try{  
tab = stmt.executeQuery("Select * from tblUser where id_user = " + 
cerere.getParameter("id")); 
while(tab.next()) 

out.println("Nume: " + tab.getString(2) + "<BR>"); 
out.println("Parola: " + tab.getString(3) + "<BR>"); 
out.println("Data: " + tab.getDate(4)); 

stmt.close(); 
con.close();  
}catch(Exception e){System.out.println(e);} 
out.println("</body>\n</html>");  
out.close();  
}

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