Curs Java
Curs Java
Referatelor
Modalitatea de desfasurare
• Motivatie
• Curs
• Laborator
• Platforma de programare
• Documentatia
• Evaluarea
Ce reprezinta “JAVA” ?
• Platforma de lucru
• Limbaj de programare
• Oracle: 2010
De ce Java?
• Simplitate
• Portabilitate
• Securitate
• Neutralitate arhitecturala
• Performanta
• Limbaje interpretate
– simplitate, portabilitate
– lipsa portabilitatii
Java Virtual
Curs
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)
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 */
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 * / %
+ -
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 ++ ş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 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
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.
Î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)
(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
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.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 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
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
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
}
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.
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
}
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;
}
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;
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).
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).
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.
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”.
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.
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:
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;
Î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
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.
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
17
pornit
monLCD
15
false
diagonalaEcran
luminozitate 50
contrast 50
monPhilips
pornit
15
false
pornit
false
• 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;
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.
0
Variabile „shadow”
Considerăm următorul exemplu:
public class A {
int x;
public A()
{
// Cod sursa constructor
...
}
}
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
1
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.
A a = (A) b; // 3
System.out.println(a.x); // 4
System.out.println(a.f()); // 5
}
2
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.
3
class A {
int x = 1;
int f() { return x; }
}
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
4
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ă.
5
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
• 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.
6
• 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.
7
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
}
}
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.
1
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 GraphicPlayerMP3()
{
// Constructor
super();
}
public GraphicPlayerDivX()
{
// Constructor
super();
}
.....
GraphicPlayerMP3 p1 = new GraphicPlayerMP3();
GraphicPlayerDivX p2 = new GraphicPlayerDivX();
p1.SetPosition(100,100);
p1.Draw();
p1.Play();
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;
}
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
{
....
}
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:
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:
3
O metodă care nu tratează o anumită exceţie trebuie obligatoriu să o ”arunce”.
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.
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:
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:
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
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;}
}
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:
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
}
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.
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.
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.
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
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.
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:
package poo;
import java.util.Scanner;
public class lab_2_1 {
}
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 {
3
}
package poo;
import java.util.Scanner;
public class lab2_3 {
package poo;
import java.util.Scanner;
public class lab2_4 {
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 {
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
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ă:
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.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.
utilizatorul prin care se clarifică cum va arăta interfaţa grafică înainte de a trece la
implementarea efectivă a acesteia.
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.).
∙ 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
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.
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");
butoanelor
f.add(b1);
f.add(b2);
f.pack();
✔
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"));
Gestionarea
poziționării
import java.awt.*;
public class TestLayout {
public static void main ( String args []) {
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());
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
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"));
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
... ...
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}};
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
...
}
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 -
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).
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ă.
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.
Evenimente
Se apasa un buton, se apasa Return in timp ce se crie intr-un text field, sau se alege un item
din meniu
Tipuri de Listener
ActionListener
WindowListener
MouseMotionListener
ComponentListener
ListSelectionListener
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) {...}
Tratarea evenimentelor
1. INTRODUCERE
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).
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
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
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
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.
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.
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.
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.
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.
Baza de
date 2
Replicarea
datelor
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
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.
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).
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");
}
}
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.
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.
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).
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
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
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>");
}
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.
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>");
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>