Sunteți pe pagina 1din 786

Salut,

Ti-am expus mai jos notiunile necesare formarii unei baze in


programare si dezvoltarii unei gandiri logice cu ajutorul carora sa
putem purcede catre un limbaj de programare pe cat de cautat pe atat
de complex cum este JAVA.
Have fun!!!

Algoritmi

Ce este un algoritm?
In general, rezolvarea unei probleme presupune:
-formulare corecta a problemei, incadrarea ei intr-o clasa de probleme
-reprezentarea ei in vederea rezolvarii cu ajutorul calculatorului
Asadar, Algoritmul este o metoda de rezolvare a mai multor probleme de acelasi tip,
caracterizata prin generalitate (nu exista problema din clasa respectiva
nerezolvabila), corectitudine(solutia furnizata este corecta) si finitudinea(solutia se
furnizeaza dupa un numar finit de operatii).
O descriere mult mai simpla este aceea ca: Algoritmul reprezinta procesul prin
care obtinem anumite date de iesire in functie de anumite date de intrare( date
ce pot fi introduse de la tastatura, citite dintr-un fisier, etc.)

Tipuri de Structuri Algoritmice:


-Structura liniara
-Structura alternativa
-Structura repetitiva
Structura liniara
V-ati gandit cum faceti extragerea radacinii patrate dintr-un numar, sau
descompunerea unui numar in factori primi? Algoritmul consta dintr-o insiruire de
operatii numite instructiuni, efectuate una dupa alta. Numim aceasta insiruire
structura liniara (secventiala).
Structura liniara cuprinde doar instructiuni de citire, scriere, calcul si atribuire

Un algoritm cu structura liniara va arata deci sub forma:

Start >> citeste date intrare >> calcul >> afisare >> Stop

Mai jos este prezentat un algoritm cu structura liniara, algoritm ce realizeaza


interschimbarea a doua numare a si b prin folosirea unei variabile
auxiliare(ajutatoare c)

Ce se intampla mai exact?


Se citesc, pe rand, valori numerice pentru a si b ( 15 si 9)
Se foloseste o a treia data( variabila) c in care se memoreaza valoarea lui a >>
in acest moment a=15, b=9, c=15
Valorii lui a i se atribuie valoarea lui b >>
avem a=9, b=9, c=15
Valorii lui b i se atribuie valoarea lui c >>
avem a=9, b=15, c=15
Se afiseaza a si b

Assigment
Realizati un alt algoritm pentru interschimbarea celor doua
variabile fara a folosi a treia variabila.
Structura alternativa
Auzim in viata de zi cu zi afirmatii de genul: DACA am promovat
examenul, ATUNCI ma voi imbata, ALTFEL stau sa invat pentru
toamna.
Se remarca trei cuvinte ce au un rol deosebit:
DACA, ATUNCI, ALTFEL.
Propozitia are trei
componente si anume:
1.o conditie, transcrisa prin “am promovat la toate materiile”,
conditie pe care o notam cu c;
2.o actiune transcrisa prin “ma voi duce in tabara”, notata cu p,
actiune asociata cu ATUNCI, adica se executa doar daca “am
promovat la toate materiile”;
3.o actiune transcrisa prin “stau sa invat”, notata cu q, actiune
asociata cu ALTFEL, adica se executa daca NU
“am promovat la toate materiile”;
Folosind notatiile facute, afirmatia se poate scrie astfel:
DACA c, ATUNCI p, ALTFEL q.
Pentru a fi foarte clar, afirmatia se poate scrie sub forma:
DACA c,
ATUNCI p,
ALTFEL q,
SFARSIT

Secventa de instructiuni se numeste structura alternativa si se


poate reprezenta si grafic.
Structura alternative admite si o forma particulara, caz in care
avem o ramura vida (adica nu se executa nici o operatie):
Exista cazuri in care conditia poate fi mai
complexa, de genul:
DACA am promovat la toate materiile SI toate mediile sunt
peste 7, ATUNCI imi voi putea inlocui computerul cu unul
mai performant, ALTFEL raman cu cel vechi
Structura repetitiva

Exista trei tipuri de structuri repetitive:


1) Structura cu numar cunoscut de repetitii (FOR - pentru)
2) Structura cu numar necunoscut de repetitii si cu test
initial (WHILE – cat timp)
3) Structura cu numar necunoscut de repetitii si cu test
final (DO-WHILE – executa pana cand)

Structura cu numar necunoscut de


repetitii si cu test initial (WHILE )
Sa consideram urmatoarea problema:
Se cere sa se cantareasca un sac cu grau (avem
suficiente greutati de 1 Kg).
Rezolvarea se reduce la a cantari sacul. Solutia se poate
exprima astfel:

CAT TIMP balanta este in dezechilibru, EXECUTA


adaugarea unei noi greutati de 1 Kg in talerul cu greutati.

Solutia are 2 componente:


1.o conditie, trascrisa prin “balanta este in dezechilibru”,
conditie pe care o notam cu c;
2.o actiune transcrisa prin “adaugarea unei noi greutati de
1 Kg in talerul cu greutati.”, notata cu a, actiune asociata
cu EXECUTA;
Folosind notatiile facute, solutia se poate scrie astfel:

CAT TIMP c (conditie), EXECUTA a (actiune)


Notand greutatea sacului cu g si cu i numarul de greutati
puse in balanta, algoritmul complet va fi:

Ce se intampla mai exact?


• i primeste valoarea 0, deoarece cantarirea incepe de la 0 kg
• cat timp i!=g ( i este diferit de g, adica valoarea kilogramelor puse sunt diferite
de de greutatea sacului) mergem la pasul 3
• efectuam i=i+1( i va devenii i+1, adica se mai pune un kg pe balanta)
cand i va fi egal cu g, adica am pus exact valoarea sacului in kg nu se va mai
indeplini conditia de la pasul 2 si vom iesi spre pasul 4
• afisam valoarea lui i
Structura repetitiva cu numar cunoscut
de pasi (FOR)

Se foloseste in cazurile cand stim numarul de repetitii.


Forma generala a structurii FOR este urmatoarea:

FOR i = val.initial, val.finala, pas


.......

De exemplu, daca vrem sa calculam suma primelor n


numere naturale:

S=1 + 2 + 3 + ... + n

vom repeta instructiunea S=S+i, pentru fiecare i, intre 1 si


n.
Ce se intampla mai exact?
Deosebirea principala dintre For si While este ca la For vom cunoaste inainte de executie
numarul de pasi, in acest caz pasul 3 executandu-se de n-1 ori
• ne initializam i cu 1, valoarea de unde vom incepe, prima ce va intra in conditia de la
pasul 2
• verificam conditia. Daca i este mai mic ca n vom merge pe 3 si 4 in caz contrar vom afisa
S la pasul 5
• sumei S se adauga o unitatea, valoarea ei implicita fiind 0. Daca conditia 2 nu era
indeplinita de la prima executie s-ar fi afisat „0”
• i creste cu o unitate. Vom vedea mai tarziu ca aceasta incrementare a lui i este automata
in structura For
• Se afiseaza s
Structura repetitiva cu text final
(Do-While)
Se mai numeste strucutra repetitiva REPEAT – UNTIL (in
Pseudocod si in Pascal)
Se foloseste in cazurile in care nu este necesara testarea initiala
pentru a realiza repetitia. Principala diferenta intre structura DO-
WHILE si WHILE ar fi ca DO-WHILE repeta cel putin odata
instructiunile, pe cand in structura
WHILE se poate intampla sa nu se execute niciodata. Din acest
motiv, este important sa alegem cu grija ce tip de structura
repetitiva folosim.
• instructiunea Do-While este o instructiune repetitiva
conditionata posterior (sau cu test final)
• intai executa instructiunea de repetat si apoi verifica
necesitatea repetarii; instrutiunea se executa macar o data
• secventa de operatii este: instructiune, conditie,
instructiune, … , conditie, instructiune, conditie
Puteti folosi aceasta instructiune pentru algortimul de prelucrare a
cifrelor unui numar natural N:
executa {cif<- n%10; prelucrez cifra ; n<-n/10} cat timp (n!=0);
Diferenta principala este ca DO-While se executa cel putin o
data!!!
Mai jos am copiat schema logica de la While pe care am facut
cateva marcaje pentru a va arata cum functioneaza Do-While
Practic la Do While se intra mai intai pe pasul 3 si apoi pe pasul 2,
executandu-se secventa in prima faza iar apoi punandu-se
conditia!!!

Limbajul de scriere al algoritmilor

Un algoritm se poate realiza in doua moduri: prin pseudocod(


limbaj liber) si prin schema logica.
Pseudocod
Cuvantul pseudocod provine din pseudo, care inseamna fals, si cod care
se refera la textul scris intr-un limbaj de programare. Deci pseudocod
inseamna un asa-zis limbaj de programare.
Pseudocodul foloseste aceeasi operatori si o exprimare la relaxata fata
de un limbaj consacrat , care cere rigurozitate.
Date
Datele cu care lucreaza un algoritm (scris in pseudocod) sunt:
• valori intregi: 12, -5, 17
• valori reale: 3.14, -1005.25, … ; observati ca folosim punct
zecimal si nu virgula ca in notatia de la matematica
• valori logice: adevarat (true) si fals (false)
• siruri de caractere: “introdu valoarea:”, “rezultatul este:”
Variabile
O variabila este un simbol care se caracterizeaza prin:
• nume; se noteaza cu combinatii de litere sau cifre dar intotdeauna
primul caracter este litera: a, Beta, nr1, nr2
• tip de data: intreg, real, sir de caractere, logic
• valoare: functie de tipul de data asociat, o variabila poate avea
valori din cele de mai sus; valoarea memorata se poate schimba, de
unde si numele de variabila;
Practic, o variabila se comporta ca o cutie ce poate fi folosita doar
pentru ceva anume: valorile intregi in cutii pentru valori intregi si valori
reale in cutii pentru valori reale; doar nu puneti zahar intr-o cutie de
pantofi. :).
Din acest motiv, la inceputul algoritmului nostru in pseudocod trebuie sa
specificam cu ce variabile lucram si ce tip au, ca in exemplul de mai jos:
intreg m,n
real x,y.z
logic ok, exista, este
Expresii
Expresiile sunt formate din operatori si operanzi. Formeaza expresie
urmatoarele
• o variabila
• variabila operator variabila
• expresie operator expresie
• operator expresie (cazul operatorilor unari de genul – (5+3) )
Dintre operatorii folositi vom vorbi acum numai de cei intregi (care se
folosesc numai pentru operanzi intregi):
• semnul ” – ” se foloseste pentru scaderi sau ca operator unar
• semnul ” + ” se foloseste pentru adunari
• semnul ” * ” se foloseste pentru inmultiri
• semnul ” / ” se foloseste pentru impartiri
• semnul ” % ” se foloseste pentru a obtine restul impartirii primului
operand la cel de al doilea
• a % b = restul impartirii lui a la b
• a % 2 = restul impartirii lui a la 2, care este 1 daca a este
impar si 0 daca a este par
• a % 10 = restul impartirii lui a la 10, care este intotdeauna
ultima cifra a lui a, cifra unitatilor
• prioritatea operatiilor este aceeasi ca in matematica; mai intai
inmultirile si impartirile si apoi adunarile si scaderile
• se pot folosi si paranteze pentru expresiile mai complicate, dar
numai perechide paranteze rotunde
• atentie la ordinea operatiilor si folosirea parantezelor rotunde (ex.
ecuatia de gradul 2):
• x1=-b+ radical(b*b-4*a*c)/2*a
• x1=(-b+ radical(b*b-4*a*c))/2*a
• x1=(-b+ radical(b*b-4*a*c))/(2*a)
• care din variantele de mai sus este corecta?
• prima imparte numai radicalul la 2, rezultatul este inmultit cu
a si apoi se efectueza scaderea
• al doilea exemplu pune parantezele pentru numarator dar
imaprte numai la 2, rezultatul impartirii fiind inmultit cu a
• abia ultima varianta separa numitorul si numaratorul prin
paranteze
Instructiuni intrare/iesire. Atribuirea
Instructiuni de intrare iesire
Asa cum am vazut in lectiile anterioare schema fluxului de date in
rezolvarea unei probleme este urmatoarea:
Date de intrare -> Algoritm -> Date de iesire (Rezultate)
Deducem ca aven nevoie de o metoda de a prelua datele initiale (ale
problemei reale) pentru a le putea prelucra in algoritm. De asemenea,
avem nevoie de o metoda de a transmite rezultatul calculului nostru.
Pentru preluarea datelor vom folosi instructiunea Citeste.
• Sintaxa: Citeste variabila;
• Exemplu: Citeste a;
• Efect: Se citeste o valoarea care va fi memorata in variabila a
• Observatii:
• pentru ” intreg n; citeste n ” , nu putem introduce o valoare
reala (3.14) deoarece variabila n este declarata ca fiind
intreaga
Pentru afisarea rezultatului vom folosi instructiunea Scrie:
• Sintaxa: Scrie expresie
• Exemplu: Scrie a
• Efect:
• instructiunea Scrie afiseaza valoarea expresiei
• presupunand ca ceea ce se scrie este transmis care monitor,
atunci prima instructiune scrie afiseaza sirul de caractere
Rezultatul problemei iar cea de a doua afiseaza valoarea
memorata in variabila a la acel moment
• Observatii
• pentruafisarea unor texte folosim text ” acesta este mesajul”
• instructiunea scrie afiseaza valoarea expresiei; atunci o
instructiune de tipul Scrie a+b va afisa valoarea calculata a
expresiei a+b; adica se aduna valoarea lui a cu valoarea lui b
si se afiseaza rezultatul expresiei
Pana acum, cel mai complicat algoritm pe care il putem scrie este cel de
adunare a doua valori intregi:
Intreg a, b;
Citeste a, b;
Scrie a+b.
Daca ar fi doar atat… :P
Atribuirea
Pentru a schimba valoarea unei variabile pot folosi citirea. Daca totusi
doresc ca variabila sa primeasca valoarea unei expresii calculate pe
parcursul algoritmului, atunci am nevoie de atribuire:
• Sintaxa: variabila<- expresie;
• Efect: In “cutia” variabilei se memoreaza valoarea expresiei;
• Exemplu:
• intreg a; a<- 10;
• real b; fie b<- 3.14;
• sir c; fie c<- ‘totul e ok’;
• Observatie:
• noua valoare se memoreaza peste vechea valoare care se va
pierde; adica, se inloceste vechea valoare cu cea noua
• in fiecare din exemple in variabila se memoreaza o valoare
de acelasi tip cu variabila; nu putem memora intr-o variabila
o valoare de alt tip:
• secventa intreg a; a<-3.14; scrie a; nu este corecta;
variabila a este de tip intreg si nu poate memora o
valoare reala.
• secventa real a; a<- +10; este corecta deoarece
valoarea intreaga 10 este si valoare reala, conform
incluziunii matematice.
• greselile frecvente sunt cele in care valori obtinute in
urma unor impartiri sau radicali (valori reale) sunt
atribuite unor variabile intregi
• operatiile cele mai de intalnite la atribuire sunt incrementarea
(marirea cu 1 a valorii variabilei) si decrementarea
(micsorarea cu 1 a valorii variabilei)
• incrementarea: a<- a+1;
• asa cum stim , intai se calculeaza valaorea expresiei
(cresterea cu 1 a lui a) si apoi se memoreaza in
variabila, peste vechea valoare
• decrementarea: a<- a-1;
• se calculeaza valoarea expresiei (scadereacu 1 a lui a) si
apoi se memoreaza in variabila, peste vechea valoare
Exemplul 1: calculul vitezei, atunci cand stim valoarea distantei
parcurse si timpul necesar;
• real viteza;
• intreg timp, distanta;
• citeste timp, distanta;
• viteza <- distanta/timp;
• scrie viteza.
Exemplul 2 (interschimbarea a doua variabile folosind auxiliar). Fie
doua variabile intregi. Sa se interschimbe valorile variabilelor. Daca
variabila a are valoarea 5 si variabila b are valoarea 7, dupa executarea
algorimului, variabila a sa aiba valoare 7 si variabila b sa aiba valoare 5.
• intreg a, b, aux;
• citesc a, b;
• aux<-a;
• a<-b;
• b<-aux;
• scrie a, b.
• observatie: problema este similara cu urmatoarea problema. Aveti
un pahar de bere (rolul variabilei a) si o halba de suc (rolul
variabilei b). Pentru a schimba bauturile aveti nevoie de o cana
goala (rolul variabilei aux);
• turnam in cana goala continutul paharului
• turnam in pahar continutul halbei
• turnam in halba continutul canii
• acum toate sunt in ordine; berea in halba si sucul in pahar si
gana este goala; NU BEM DECAT SUCUL!
Exemplul 2 (interschimbarea valorii a doua variabile fara auxiliar).
Avem aceeasi problema dar trebuie rezolvata fara o variabila in plus.
• intreg a, b;
• citeste a,b;
• a <- a+b; b<- a-b; a<- a-b;
• scrie a,b.
Instructiunea de decizie
Fara instructiunea de decizie sau de test (in nici un caz detest) algorimul
ar fi linear. Aceasta instructiune ne ajuta in cazul in care avem o
intrebare privind natura datelor folosite, a valorii lor la un moment dat.
Practic, trebuie sa punem intrebari la care algoritmul sa raspunda cu
adevarat/fals; ce operatori folosim pentru a obtine raspunsuri
adevarat/fals?
Expresii cu valoare logica (adevarat/fals)
Operatori de relatie:
• “>” inseamna mai mare
• a>b are valoarea adevarat numai daca valoarea variabilei a
este mai mare decat valoarea variabilei b
• “<” inseamna mai mic
• a<b are valoarea adevarat numai daca valoarea variabilei a
este mai mica decat valoarea variabilei b
• “>=” inseamna mai mare egal
• a>b are valoarea adevarat numai daca valoarea variabilei a
este mai mare sau egala cu valoarea variabilei b
• “<=” inseamna mai mic sau egal
• a>b are valoarea adevarat numai daca valoarea variabilei a
este mai mica sau egala cu valoarea variabilei b
• ” =” inseamna egal
• a=b are valoarea adevarat numai daca variabila a are aceeasi
valoare cu variabila b
• “!=” inseamna diferit
• a!=b are valoarea adevarat numai daca variabila a NU are
aceeasi valoare cu variabila b
Operatori logici
Operatorii logici (cu care lucreaza si logica matematica) sunt SI, SAU si
NEGARE:
• Operatorul SI este un operator binar, cu doi operanzi cu valoare
logica. Rezultatul operatorului SI este adevarat numai daca ambii
operanzi au valoarea ADEVARAT.
• “Mie imi place fata bruneta si cu ochi albastri!”
• daca fata nu este bruneta sau nu are ochi albastri nici nu ma
uit la ea (:P), ceea ce inseamna fals; daca macar una dintre
conditii este falsa atunci intreaga expresie este falsa
• Operatorul SAU este un operator binar cu doi operanzi cu valoare
logica. Rezultatul operatiei SAU este adevarat daca macar unul din
operanzi are valoarea ADEVARAT.
• “Mie imi place fata bruneta SAU cu ochi albastri!“
• Fata nu trebuie sa aiba ambele calitati. Daca este bruneta imi
place (ADEVARAT). Daca este cu ochi albastri imi place
(ADEVARAT).
• Daca macar una din conditii este adevarata atunci intreaga
expresie este adevarata
• Operatorul de negatie (vom folosi semnul ” ! ” ) schimba
valoarea de adevar a expresiei: !ADEVARAT==FALS si !FALS
==ADEVARAT
Exemple:
• X apartine [-5, -2]
• (x>=-5) SI (X<=-2)
• x apartine (0, 10)
• (x>0) SI (x<10)
• x nu apartine (-7,9]
• (x<=-7) SAU (x>9)
• !(x>-7) SAU !(x<=9)
• !((X>-7) SI (x<=9))
Observatii
• prioritatea operatorilor logici: negare, SI, SAU
• in cazul expresiilor logice se pot aplica regulile lui De Morgan:
• !( exp1 si exp2) = !exp1 sau !exp2
• !(exp1 sau exp2) = !exp1 si !exp2
Instructiunea de decizie (de test; sau detest :P)
Sintaxa:
daca (exp_logica) atunci instructiune1
[altfel instructiune2;]
Efect: Se evalueaza expresia logica; daca valoarea logia ca expresiei
este ADEVARAT se executa intructiunea 1; Daca exista sectiunea
ALTFEL si valoarea logica a expresiei este FALS se executa
intructiunea 2; in ambele cazuri, dupa executarea intructiunii
corespunzatoare si executa ce instructiune urmeaza dupa testul nostru.
Observatie. Un algoritm trebuie sa fie clar si sa poata fi inteles intr-un
singur fel. Din acest punct de vedere, exista doua moduri de a scrie un
algoritm:
• cate o instructiune pe linie si cu marcatori la sfarsitul
instructiunilor de test si repetitive (cum cere manualul)
• mai multe instructiuni pe line, separate printr-un marcator; pentru
cazul cand un grup de instructiuni trebuie executate impreuna pe
unul din cazurile intrtuctiunii de decizie (de exemplu ), acestea pot
fi incadrate cu acolade (in limbajul C++) sau cu cuvinte rezervate
(begin … end);
• Pentru ca pdeudocodul nu este un limbaj in sine ci doar o
conventie de notatii, sugerez sa folosim deja notatiile din C++;
• cand o secventa de instructiuni trebuie executate una dupa alta,
datorita logicii algoritmului, acea secventa o vom incadra in
acolade (de exemplu: {instructiune1; instructiune2;
instructiune3; …}); vom reveni cu amanunte.
Exemple:
• Sa se afiseze maximul a doua valori citite.
intreg a,b;
citeste a,b;
daca (a>b) atunci scrie a
altfel scrie b.
• Sa se afiseze modulul (matematic) al unui numar intreg.
intreg a;
citeste a;
daca (a>0) atunci scrie a
altfel scrie -a.
• Sa memoram valoarea maxima intr-o variabila si sa o afisam.
intreg a,b,max;
citeste a,b;
daca (a>b) atunci max<-a
altfel max<-b;
Scrie max.
• Sa se citeasca o valoare intreaga si sa se stabileasca daca s-a citit
o valoare para sau nu.
intreg a;
citeste a;
daca (a%2 ==0) atunci scrie “Valoarea citita este para!”
altfel scrie “Valoarea citita este impara!”.
• Fie a si b coeficientii unei ecuatii de grad 1. Sa se afiseze solutia
ecuatiei a*x + b =0;
• evident x=-b/a daca a!=0;
intreg a,b;
real x;
citeste a,b;
daca (a!=0) atunci { x<- -b/a; scrie “Solutia ecuatiei este “; Scrie x;}
altfel scrie “Nu se poate calcula solutia ecuatiei”.
Secventa { x=-b/a; scrie “Solutia ecuatiei este “; Scrie x;} trebuie
gandita ca un intreg deoarece este valabila numai pentru cazul in care a
este nenul; de aceea am incadrat-o intre acolade.
• Fie a, b si c coeficientii unei ecuatii de grad 2 (a*x^2+b*x+c=0).
Sa se calculeze valorile radacinii, daca exista.
• conditia pentru a fi ecuatie de grad 2: coeficientul a trebuie sa
fie nenul (a!=0)
• trebuie calculata valoarea DELTA=b*b-4*a*c
• pentru a avea solutii, valoarea DELTA trebuie sa fie pozitiva,
cel putin nula;
• vom folosi radical (expr) pentru a calcula valoarea
radicalului unei expresii reale/intregi
intreg a,b,c,delta;
real x1,x2;
citeste a,b,c;
daca (a==0) atunci scrie ” Coeficientii nu formeaza ecuatie de gradul
2″;
altfel {delta=b*b-4*a*c;
daca (delta<0) atunci scrie “Nu se pot calcula radacini reale ”
altfel {x1<- (-b+radical(delta))/(2*a); x2<- -b-
radical(delta))/(2*a);
scrie “Solutiile sunt :”, x1,” “, x2; }
}
Observatii:
• x1 si x2 sunt declarati reali pentru ca se obtin in urma radicalului si
a unei impartiri
• urmariti daca algoritmul scrie concide cu termenii problemei
• pentru cazul coeficientului a nenul trebuie efectuate doua operatii
(calculul lui delta si testul lui delta), motiv pentru care acestea au
fost incadrate intre acolade
• la calculul solutiilor au fost puse paranteze pentru separarea
numitorului si a numaratorului
• de asemenea, aici au trebuit puse acolade pentru calculul celor
doua solutii si a afisarii solutiilor
• Fie o functie matematica data pe intervale. Sa se calculeze
valoarea functiei intr-un punct x oarecare daca expresia functiei
este:
• x^2+2*x+1 daca x<0
• 3*x+5 daca x apartine [0,5)
• -x+2 daca x>=5
• este clar ca valoarea functie difera pe cele trei intervale; va
trebui sa verificam, pe rand, carui interval apartine valoarea
citita pentru x;
real x,f;
citeste x;
daca (x<0) atunci f<- x*x+2*x+1
altfel daca (x>=5) atunci f<- -x+2
altfel f<- 3*x+5;
scrie f.

Acum ca am vazut cum sta treaba cu


pseudocodul, vom incerca sa scriem si cateva
instructiuni repetitive.
Haide sa ni le amintim!

Instructiunea repetitiva CAT TIMP (While)


Sintaxa: cat timp (expresie_logica) executa instructiunea;
Efect:
• se stabileste valoarea de adevar a expresiei logice
• daca valoarea expresiei logice este ADEVARAT atunci se executa
instructiunea si se reia de la pasul 1
• daca valoarea expresiei logice este FALS atunci se continua cu
instructiunea de dupa CAT TIMP
Observatii:
• CAT TIMP este repetitiva conditionata anterior deoarece intai se
evaluaeza conditia si apoi se executa instructiunea
• Practic, succesiunea de etape este exp logica, instructiune,exp
logica, instructiune,exp logica, instructiune,…exp
logica;succesiunea se incheie cu exp logica in momentul in care
valoarea expresiei este FALS.
• daca pe cazul ADEVARAT trebuie sa scriem mai multe
instructiuni, acestea vor fi grupate cu acolade
Exemplul 1
• Fie a si b doua valori naturale. Se se simuleze inmultirea a*b prin
adunari repetate.
• P=a*b=a+a+….+a de b ori
• Expresia de mai sus spune ca a trebuie adunat la P de b ori;
adica, la fiecare adunare la P a lui a putem sa scadem un 1
din b (decrementam), pentru a pastra numarul de adunari ce
mai trebuie efectuat; cand b va fi zero , se va adunat a de b
ori
intreg a,b,p;
citeste a,b;
p<-0;
cat timp (b>0) executa {p<- p+a; b<- b-1;};
scrie p.
Observatii:
• pentru fiecare adunare a lui a la p, scadem un 1 din b
• aceste operatii trebuie executate pentru fiecare caz in care b este
pozitiv; de aceea au fost grupate cu acolade
• este algoritmul corect? Sa verificam cazurile cu zero:
• daca a este zero, atunci la P se aduna zero de b ori; practic P
ramane la valoarea zero. CORECT!
• daca b este zero, conditia din CAT TIMP este falsa si atunci
nu se executa instructiunile; in consecinta se afiseaza direct
valoarea lui P,adica zero; CORECT!
Exemplul 2
• Fie a si b doua valori naturale. Sa se simuleze impartirea lui a la b
prin scaderi repetate si sa se afiseze catul si restul.
• vom scadea din a valoarea lui b de mai multe ori, numarand
intr-o variabila contor de cate ori am facut scaderea (practic
variabila contor va fi catul impartirii)
• operatia se va repeta cat timp din a se mai poate scadea un b,
adica cat timp a mai mare decat b
• Ce reprezinta valoarea ramasa in a? O valoare mai mica
decat b? Evident, restul impartirii.
intreg a, b, contor;
contor<- 0;
daca (b=0) atunci scrie “nu se realizeaza impartiri la zero!”;
altfel {cat timp (a>=b) executa {a<- a-b; contor<-contor+1;};
scrie “catul este “, contor;
scrie “restul este “, a;
}.
Observatii:
• am pus intre acolade secventa in care numaram de cate ori am
scazut pe b din a; erau doua instructiuni;
• este corect algoritmul? Daca incercati cazurile normale, va merge.
Sa verificam cazurile speciale:
• daca a este mai mic decat b atunci cat timp nu va functiona si
se va afisa direct contor (zero) si a (restul); CORECT!
• daca a este zero, atunci nu se executa cat timp si se afiseaza
contor si a (0 si 0 ) CORECT!
• daca b este zero, se afiseaza mesajul de eroare si algoritmul
se incheie CORECT!
Exemplul 3.
• Sa se calculeze cel mai mare divizor comun a doua valori a si b
naturale.
• asa cum spune definitia, cel mai mare divizor comun trebuie
sa fie o valoare, care sa divida atat pe a cat si pe b; aceasta
valoare poate fi in cel mai bun caz a sau b
intreg a,b;
citeste a,b;
cmmdc<- a;
cat timp (a%cmmdc +b%cmmdc !=0) executa cmmdc<- cmmdc-1;
scrie cmmdc.
Observatii:
• expresia a%cmmdc +b%cmmdc !=0 este nula doar daca atat
a%cmmdc cat si b%cmmdc sunt nule, adica cmmdc divide
simultan a si b; atat timp cat aceasta conditie nu se realizeaza
scadem cmmdc-ul
• ultima valoare panaa la care se poate scadea este 1, divizorul
tuturor numerelor; in acest caz a si b se numesc numere prime intre
ele.
Exemplul 4
• Ghiceste-mi numarul! Eu imi aleg un numar intre 1 si 100. In cati
pasi il poti ghici? La fiecare incercare raspund cu SUCCES, MIC
sau MARE.
• prima varianta este sa intrebam aleator; cam multi pasi!
• sa parcugem valorile de la 1 la 100; cam multi pasi!
• sa gandim! vom testa intodeauna mijlocul intervalului; in
acest fel, la fiecare intrebare, elimin jumatate din cazuri.
intreg x,m1,m2, mij, pasi;
citesc x; m1 <- 1; m2<- 100;
mij<- 50; pasi<- 0;
cat timp (mij!=x) executa
{daca (x<mij) atunci m2<- mij;
altfel m1<- mij;
mij<-(m1+m2)/2;
pasi<- pasi +1;};
scrie pasi.
Observatie:
• dupa fiecare test fara succes intervalul in care cautam se ajusteaza
la stanga (daca mij este mai mic decat x) sau la dreapta (daca mij
este mai mare decat x)
Exemplul 5.
• Fie N un numar natural. Sa se calculeze suma cifrelor lui N.
• vom initializa o variabila suma cu zero
• trebuie ca delimitam pe rand cifrele care formeaza numarul
N; putem determina rapid ultima cifra: n%10; daca o stergem
(n<- n/10) putem deternima penultima cifra; s.a.m.d.
• cand ne oprim? … cand N nu mai are cifre; deci , cand N este
zero.
intreg n, suma;
citeste n;
suma <-0;
cat timp (n!=0) executa {suma<-suma+n%10; n<-n/10;};
scrie suma.
Observatie:
• instructiunea suma<-suma+n%10 creste suma cifrelor deja
obtinute cu valoarea ultimei cifre a lui n.
• pentru cazul in care n este nul, cat timp nu se mai executa si se
afiseza suma cu valoarea 0; Suma cifrelor lui 0 este 0. CORECT!

Instructiunea PENTRU – EXECUTA (FOR)


Sintaxa: PENTRU contor<-exp_init,exp_fin EXECUTA instructiune
Efect: pentru fiecare valoare a contorului intre expresia initiala si
expresia finala se executa instructiunea;
Exemplu: pentru i <- 1,10 executa scrie ” Nu ma prinzi!”;
• pentru fiecare valoarea a variabile i, de la 1 la 10, se afiseaza ” Nu
ma prinzi!”
• de 10 ori se afiseaza ” Nu ma prinzi!”
• daca secventa ce trebuie repetata contine mai multe instructiuni,
acestea se vor grupa cu acolade
Observatii:
• practic, pentru fiecare valoare a lui i, intai se testeaza daca nu s-a
depasit valoarea finala 10 si apoi se executa instructiunea;
• algoritmic, propozitia de mai sus este :
• i<-1; cat timp (i<=10) {scrie ” Nu ma prinzi!”; i<-i+1; };
• practic , secventa de mai sus me explica faptul ca instructiunea
pentru este o clona a instructiunii cat timp.
• instructiunea este “ceruta” daca descrierea algorimului spune “de
la valoarea X la valoarea Y”, “pentru primele X valori”, “de X
ori”, …
Exemplul 1.
• Sa se afiseze numerele pare pana la o valaore N, naturala.
intreg n,i;
citeste n;
pentru i<- 0,n executa
daca (i%2==0) atunci scrie i.
Observatii:
• algoritmul ia fiecare valoare intre 0 si n si o testeaza daca este para
(restul impartirii lui i la 2 sa fie nul : i%2==0)
• se efectueaza n pasi din care jumatate sunt gresiti; trebuie o
varianta mai buna
Exemplul 2.
• Aceeasi problema dar incercam sa mergem din doi in doi
• intreg n,i; citeste n; pentru i<-0,n,2 executa scrie i.
• intreg n,i; citeste n; pentru i<-0,n/2 executa scrie i*2.
Observatie:
• in primul caz, 2-ul de dupa n (i<-0,n,2 ) stabileste cresterea lui i cu
2 si nu cu 1 asa cum este implicit
• in al doilea caz, ne folosim de faltul ca valorile cautate sunt pare,
divizibile cu 2;
Exemplul 3
• Sa se calculeze suma primelor N numere naturale.
• evident, stim formula n*(n+1)/2 dar sa incercam un algoritm;
• va trebui sa adunam, la o suma , toate valoarile de la 1 la n
intreg n,i,suma;
citeste n;
suma<-0;
pentru i<- 0 ,n executa suma<- suma +i;
scrie suma.
Exemplul 3.
• Se citeste un sir de N valori intregi. Sa se determine cea mai mare
valoare citita (valoarea maxima dintr-un sir).
intreg n,i,max,val;
citeste n;
citeste max;
pentru i<-2,n executa {citeste val; daca val>max atunci max<-val;};
scrie val.

Instructiunea EXECUTA CAT TIMP (DO While)


Sintaxa: executa instructiune cat timp (expr_logica)
Efect:
• se executa instructiunea
• se stabileste valoarea de adevar a expresiei logice
• daca valoarea conditiei este ADEVARAT atunci se revine la pasul
1
• daca valoarea conditiei este FALSA atunci se continua cu
instructiunea de dupa EXECUTA CAT TIMP
Observatii:
• instructiunea EXECUTA CAT TIMP este o instructiune repetitiva
conditionata posterior (sau cu test final)
• intai executa instructiunea de repetat si apoi verifica necesitatea
repetarii; instrutiunea se executa macar o data
• secventa de operatii este: instructiune, conditie, instructiune, … ,
conditie, instructiune, conditie
Putewti folosi aceasta instructiune pentru algortimul de prelucrare a
cifrelor unui numar natural N:
executa {cif<- n%10; prelucrez cifra ; n<-n/10} cat timp (n!=0);
Schema logica
Schema logica reprezinta insiruirea etapelor necesare parcurgerii
algoritmului sub forma grafica.
Schema logica este formata din:

Ob. Pseudocodul este un limbaj liber, unele dintre blocuri


reprezentandu-se in mai multe moduri. De exemplu blocul de
prelucrare(executare) poate fi desenat ca si blocul de atribuire (
atribuirea in sine fiind o executie). Un alt exemplu il dau blocurile
de intrare si de iesire, ele pot fi reprezentate si printr-un trapez
astfel:
pentru blocul de citire(intrare) – trapez cu baza mare deasupra
pentru blocul de afisare(iesire) – trapez cu baza mica deasupra

Exemplu
EXERCITII
Ob. Exercitiile se vor rezolva prin scrierea algoritmului dar si
prin realizarea schemei logice specifice acestuia!!!

1.Sa se verifice daca un numar a, citit de la tastatura este par. Sa se


afiseze „Da, numarul este par” sau „Nu, numarul nu este par”
2.Sa se calcuze suma a doua numere a si b, citite de la tastatura.
3.Sa se verifice daca un numar este par.Sa se afiseze cate un mesaj
corespunzator pentru fiecare caz.
4.Sa se verifice daca un numar este prim. Sa se afiseze cate un mesaj
corespunzator pentru fiecare caz.
5.Sa se verifice daca suma a doua numere este divizibila cu 3. In caz
afirmativ sa se afiseze oglinditul sumei.( oglinditul lui 45 este 54)
6.Sa se determine suma primelor 10 numere naturale
7.Sa se determine suma primelor 100 numere naturale
8.Sa se determine suma primelor n numere naturale
9.Sa se determine suma primelor n numere pare
10.Sa se determine suma primelor n numere impare
11.Sa se calculeze suma inverselor primelor n numere naturale
12.Dandu-se un numar n sa se afiseze daca este par sau nu
13.Se considera trei numere a, b, c. Sa se afiseze cel mai mare dintre ele.
14.Dandu-se un numar n sa se afiseze daca este impar sau nu
15.Sa se afiseze toate numerele prime mai mici ca n, dat
Dezvoltarea Programelor în Java
1. Instalarea uneltelor pentru dezvoltare
Pentru a putea dezvolta aplicaţii în Java, este nevoie de:

 JDK (Java Development Kit) – pentru a putea rula programele


 Eclipse IDE – mediu de dezvoltare care ajută la scrierea programelor Java
2. Crearea primului proiect
 Pornirea Eclipse
 Selectarea unui workspace
 Selectarea unui proiect nou
 File → New → Other → Java Project → Next
 Configurare
 Project Name: First Project → Next → Finish
 Generarea unui proiect nou
 Împarţirea spaţiului de lucru: caseta cu fişiere sursă şi editorul de text
 Folder-ul src
 Fişiere sursă
 Crearea primei clase
 Introducere de nume si bifare ”public static void main(String[] args)”
 Punctul de intrare în program: metoda main
 public static void main(String args[]) – execuţia unui program în Java începe
de la metoda main()
 Folosirea metodei System.out.println();
 Compilarea şi rularea proiectului folosind săgeata verde din bară (CTRL + F11)
 Observarea rezultatelor în consolă
 Sintaxa
 Case Sensitivity - Java este case sensitive, ceea ce înseamnă că „hello” şi
„Hello” au semnificaţii diferite
 Numele claselor – prima literă ar trebui să fie literă mare şi restul mici. Dacă o
clasă are mai multe cuvinte, acestea vor începe cu literă mare, fără spaţii între
ele
 Numele metodelor şi variabilelor – toate metodele şi variabilele trebuie să
înceapă cu literă mică şi să conţină litere mici. Dacă acestea conţin mai multe
cuvinte, celelalte cuvinte vor începe cu literă mare, fără să aibă spaţii între ele.
Acest mod de denumire poartă numele de CamelCase. Examplu: metodaMea
 Numele fişierelor – Trebuie să fie exact la fel cu numele claselor
 Folosirea caracterelor escape
Exemplu

System.out.println(“Hello, world!”);

Assignment

1. Să se afişeze următoarele mesaje, în acelaşi program:

Hello World!

Hello Again

I like typing this.

This is fun.

Yay! Printing.

I'd much rather you 'not'.

I "said" do not touch this.

2. Să se mai afişeze încă o linie la programul anterior.

3. Să se adauge două bare (/ /) la începutul unei instrucţiuni. Ce diferenţe observaţi?

3. Cod. Comentarii
 Indentarea codului
 Instrucţiuni vide
 Linii goale
 Folosirea ctrl+shift+F în Eclipe pentru aşezarea codului
 Comentarii
 Pe un singur rând - / /
 Pe mai multe rânduri - /*…*/
 Pentru documentaţie (javadoc) - /** … */
Exemplu
public class CommentsAndSlashes {

/**

The entry point of a program

*/

public static void main (String[] args) {

// A comment, this is so you can read your program


later.

// Anything after the // is ignored by Java.

/* This is a multiline comment

And will be totally ignored */

/*This is also a multiline comment on a single line*/

System.out.println( "I could have code like this." );


// and the comment after is ignored.

// You can also use a comment to "disable" or comment


out a piece of code:
// System.out.println("This won't run.");

System.out.println( "This will run." );

Pentru documentație Javadoc:

/**

* Write a description of class VictoryScreen here.

* @author (your name)

* @version (a version number or a date)

*/

public class Test {

Assingment

1. Scrieţi un program care afişează următoarele caractere:

*****

*****
*****

*****

*****

2. Să se printeze informaţiile de pe un plic. Rezultatul trebuie să fie asemănător cu


cel de jos. Nu este nevoie să coloraţi fundalul în negru. Nu uitaţi de marginile plicului!

3.

Printaţi iniţialele voastre după exemplul dat. Folosiţi tabelul de mai jos ca şi sugestie.

4. Aritmetică şi Comparaţii
Operatori aritmetici şi de comparaţie:
 + plus
 - minus
 * înmulţit
 / împărţit
 % modulo (restul împărţirii)
 == egal cu
 != diferit de
 < mai mic ca
 > mai mare ca
 ⇐ mai mic ca sau egal cu
 >= mai mare ca sau egal cu
Exemplu

System.out.println( "I will now count my chickens:" );

System.out.println( "Hens " + ( 25 + 30 / 6 ) );

System.out.println( "Roosters " + ( 100 - 10 * 3 % 4 ) );

System.out.println( "Now I will count the eggs:" );

System.out.println( 3 + 2 + 1 - 5 + 4 % 2 - 1 / 4 + 6 );

System.out.println( "Cat face? " + ( -1 % 4 ) );

System.out.println( "Care este ordinea operatiilor? " + ( 6 +


-11 / 4 ) );

System.out.println( "Care este ordinea operatiilor? " + ( 6 +


-11 % 4 ) );
System.out.println( "Is it true that 3 + 2 < 5 - 7?" );

System.out.println( 3 + 2 < 5 - 7 );

System.out.println( "What is 3 + 2? " + ( 3 + 2 ) );

System.out.println( "What is 5 - 7? " + ( 5 - 7 ) );

System.out.println( "Oh, that's why it's false." );

System.out.println( "How about some more." );

System.out.println( "Is it greater? " + ( 5 > -2 ) );

System.out.println( "Is it greater or equal? " + ( 5 >= -2 )


);

System.out.println( "Is it less or equal? " + ( 5 <= -2 ) );

System.out.println( "Hai sa facem suma lui 3 + 2: " + 3 + 2


);

System.out.println( "De ce nu a iesit?");

System.out.println( 3 + 2 + " Ce s-a intamplat acum?" );

Assignment

1. Folosiţi comentarii pentru explicarea instrucţiunilor.


Variabile. Identificatori. Literali
1. Variabile. Tipuri de Date
Variabilele sunt folosite pentru a da nume unor valori. Valorile lor se pot schimba de-
alungul codului şi de cele mai multe ori nu ne sunt cunoscute.

În Java există mai multe tipuri de date, dar pentru moment vom folosi doar int,
double, boolean şi String.

int

 folosit pentru numere întregi


 poate lua valori între aproximativ -2 miliarde şi +2 miliarde
 valoarea implicită este 0
 exemplu: int a = 10000, b = -235;
double

 folosit pentru numere reale (cu virgulă)


 valoarea implicită este 0.0
 exemplu: double x = -34.5221, k = 123.45;
boolean

 folosit pentru a reţine valorile de adevăr true sau false


 valoarea implicită este false
 exemplu: boolean first = true;
String

 folosit pentru a reprezenta şiruri de caractere (text)


 valoarea unui String trebuie să fie înconjurată de “”
 exemplu: String name = “Vasile”;
 putem avea şi şiruri goale: String emptyString = “”;
Folosirea variabilelor

 declarare: int x;
 iniţializare: x = 20;
 atribuire cu operatorul de atribuire: x = 145;
 afişare (sau alte operaţii): System.out.println(x);
Operatorul de atribuire "="

 int i = 45;
 int j = 6 + 5;
 int k = i + j;
 int m = 056; (baza 8)
 int n = 0x5f; (baza 16)
Observaţie A nu se confunda operatorul de atribuire "=" care schimbă valoarea unei
variabile cu operatorul de comparaţie "==" care ne arată dacă 2 variabile sunt egale
sau nu.

Exemplu

public class VariablesAndNames {

public static void main(String[] args) {

int cars, drivers, passengers, cars_not_driven,


cars_driven;

double space_in_a_car, carpool_capacity,


average_passengers_per_car;

cars = 100;

space_in_a_car = 4.0;
drivers = 30;

passengers = 90;

cars_not_driven = cars - drivers;

cars_driven = drivers;

carpool_capacity = cars_driven * space_in_a_car;

average_passengers_per_car = passengers / cars_driven;

System.out.println( "There are " + cars + " cars


available." );

System.out.println( "There are only " + drivers + "


drivers available." );

System.out.println( "There will be " + cars_not_driven


+ " empty cars today." );

System.out.println( "We can transport " +


carpool_capacity + " people today." );

System.out.println( "We have " + passengers + " to


carpool today." );

System.out.println( "We need to put about " +


average_passengers_per_car + " in each car." );

}
}

Assignment

1. Ce reguli învăţate sunt încălcate în codul anterior?


2. De obicei, variabilele se vor declara câte una pe linie, pentru lizibilitate. Cum
se va schimba codul?
3. Pentru variabila space_in_car, valoarea este 4.0. Ce se întâmplă dacă o
schimbăm la valoarea 4?
Exemplu

public class MoreVariablesAndPrinting {

public static void main(String[] args) {

String myName, myEyes, myTeeth, myHair;

int myAge, myHeight, myWeight;

myName = "Zed A. Shaw";

myAge = 35; // not a lie

myHeight = 74; // inches

myWeight = 180; // lbs


myEyes = "Blue";

myTeeth = "White";

myHair = "Brown";

System.out.println( "Let's talk about " + myName + "."


);

System.out.println( "He's " + myHeight + " inches


tall." );

System.out.println( "He's " + myWeight + " pounds


heavy." );

System.out.println( "Actually, that's not too heavy."


);

System.out.println( "He's got " + myEyes + " eyes and


" + myHair + " hair." );

System.out.println( "His teeth are usually " + myTeeth


+ " depending on the coffee." );

// This line is tricky; try to get it exactly right.

System.out.println( "If I add " + myAge + ", " +


myHeight + ", and " + myWeight
+ " I get " + (myAge + myHeight + myWeight) + "."
);

Assignment

1. Ştergeţi cuvântul “my” din faţa variabilelor. Ce altceva ar trebui să mai


schimbaţi?
2. Convertiţi inches şi pounds în cm şi kg folosind variabile. Căutaţi metodele de
conversie pentru acestea şi afişaţi ambele variante.
3. Explicaţi diferenţa de afişare în următorul cod:

int num = 4;

System.out.println(num);

System.out.println(“num”);

System.out.println(4);

2. Identificatori. Literali
Identificatori

Toate componentele Java necesită nume. Numele folosite pentru clase, variabile şi
metode sunt identificatori. În Java, există câteva noţiuni care trebuiesc reţinute
pentru identificatori:

 Primul caracter trebuie sa fie un caracter valid (literă, , _)


 Un cuvânt cheie nu poate fi utilizat ca identificator
 Identificatorii sunt case-sensitive
 Exemple de identificatori legali: age, $salary, _value, _1_value
 Exemple de identificatori ilegali: 123abc, -salary
Literali

Un literal este o reprezentare în codul sursă a unei valori fixe. Aceştia sunt
reprezentaţi direct în cod şi pot fi atribuiţi oricărui tip de date primitive. De exemplu:

int a =68; char a ='A';

byte, int, long, şi short pot fi exprimaţi în baza 10 (zecimal), baza 16 (hexazecimal)
sau baza 8 (octal). Prefixul 0 este folosit pentru a indica un prefix pentru numerele în
octal, iar 0x este prefixul ce indică literalii în hexazecimal. De exemplu:

int decimal=100; int octal =0144; int hexa =0x64;

Literalii de tip String sunt specificaţi ca şi în alte limbaje de programare prin scrierea
unor secvenţe de caractere între perechi de ghilimele. Exemple de literali String:

"Hello World"

"two\nlines"

"\"This is in quotes\""

Literalii de tip String sau char pot conţine orice caractere în Unicode. De exemplu:

char a ='\u0001'; String a ="\u0001";

Assignment

1. Folosind 3 variabile, afişaţi următorul text. Variabilele sunt subliniate. Alegeţi tipul
corespunzător.
This is room #113
e is close to 2.71828
I am learning a bit about Computer Science

2. Folosind 2 variabile, una pentru nume şi una pentru anul naşterii, să se printeze un
mesaj. Să se aleagă tipuri de date corespunzătoare şi nume potrivite pentru
variabile. Mesajul va arăta ca în exemplul de mai jos:

Mă numesc Ion Ionică Ionescu şi m-am născut în anul 1820.

3. Să se printeze un orar pentru o zi de liceu, ce va conţine numele orei şi profesorul


care o predă. Să se folosească variabile corespunzătoare acolo unde este cazul.
Variabile. Identificatori. Literali
1. Variabile. Tipuri de Date
Variabilele sunt folosite pentru a da nume unor valori. Valorile lor se pot schimba de-
alungul codului şi de cele mai multe ori nu ne sunt cunoscute.

În Java există mai multe tipuri de date, dar pentru moment vom folosi doar int,
double, boolean şi String.

int

 folosit pentru numere întregi


 poate lua valori între aproximativ -2 miliarde şi +2 miliarde
 valoarea implicită este 0
 exemplu: int a = 10000, b = -235;
double

 folosit pentru numere reale (cu virgulă)


 valoarea implicită este 0.0
 exemplu: double x = -34.5221, k = 123.45;
boolean

 folosit pentru a reţine valorile de adevăr true sau false


 valoarea implicită este false
 exemplu: boolean first = true;
String

 folosit pentru a reprezenta şiruri de caractere (text)


 valoarea unui String trebuie să fie înconjurată de “”
 exemplu: String name = “Vasile”;
 putem avea şi şiruri goale: String emptyString = “”;
Folosirea variabilelor

 declarare: int x;
 iniţializare: x = 20;
 atribuire cu operatorul de atribuire: x = 145;
 afişare (sau alte operaţii): System.out.println(x);
Operatorul de atribuire "="

 int i = 45;
 int j = 6 + 5;
 int k = i + j;
 int m = 056; (baza 8)
 int n = 0x5f; (baza 16)
Observaţie A nu se confunda operatorul de atribuire "=" care schimbă valoarea unei
variabile cu operatorul de comparaţie "==" care ne arată dacă 2 variabile sunt egale
sau nu.

Exemplu

public class VariablesAndNames {

public static void main(String[] args) {

int cars, drivers, passengers, cars_not_driven,


cars_driven;

double space_in_a_car, carpool_capacity,


average_passengers_per_car;

cars = 100;

space_in_a_car = 4.0;
drivers = 30;

passengers = 90;

cars_not_driven = cars - drivers;

cars_driven = drivers;

carpool_capacity = cars_driven * space_in_a_car;

average_passengers_per_car = passengers / cars_driven;

System.out.println( "There are " + cars + " cars


available." );

System.out.println( "There are only " + drivers + "


drivers available." );

System.out.println( "There will be " + cars_not_driven


+ " empty cars today." );

System.out.println( "We can transport " +


carpool_capacity + " people today." );

System.out.println( "We have " + passengers + " to


carpool today." );

System.out.println( "We need to put about " +


average_passengers_per_car + " in each car." );

}
}

Assignment

1. Ce reguli învăţate sunt încălcate în codul anterior?


2. De obicei, variabilele se vor declara câte una pe linie, pentru lizibilitate. Cum
se va schimba codul?
3. Pentru variabila space_in_car, valoarea este 4.0. Ce se întâmplă dacă o
schimbăm la valoarea 4?
Exemplu

public class MoreVariablesAndPrinting {

public static void main(String[] args) {

String myName, myEyes, myTeeth, myHair;

int myAge, myHeight, myWeight;

myName = "Zed A. Shaw";

myAge = 35; // not a lie

myHeight = 74; // inches

myWeight = 180; // lbs


myEyes = "Blue";

myTeeth = "White";

myHair = "Brown";

System.out.println( "Let's talk about " + myName + "."


);

System.out.println( "He's " + myHeight + " inches


tall." );

System.out.println( "He's " + myWeight + " pounds


heavy." );

System.out.println( "Actually, that's not too heavy."


);

System.out.println( "He's got " + myEyes + " eyes and


" + myHair + " hair." );

System.out.println( "His teeth are usually " + myTeeth


+ " depending on the coffee." );

// This line is tricky; try to get it exactly right.

System.out.println( "If I add " + myAge + ", " +


myHeight + ", and " + myWeight
+ " I get " + (myAge + myHeight + myWeight) + "."
);

Assignment

1. Ştergeţi cuvântul “my” din faţa variabilelor. Ce altceva ar trebui să mai


schimbaţi?
2. Convertiţi inches şi pounds în cm şi kg folosind variabile. Căutaţi metodele de
conversie pentru acestea şi afişaţi ambele variante.
3. Explicaţi diferenţa de afişare în următorul cod:

int num = 4;

System.out.println(num);

System.out.println(“num”);

System.out.println(4);

2. Identificatori. Literali
Identificatori

Toate componentele Java necesită nume. Numele folosite pentru clase, variabile şi
metode sunt identificatori. În Java, există câteva noţiuni care trebuiesc reţinute
pentru identificatori:

 Primul caracter trebuie sa fie un caracter valid (literă, , _)


 Un cuvânt cheie nu poate fi utilizat ca identificator
 Identificatorii sunt case-sensitive
 Exemple de identificatori legali: age, $salary, _value, _1_value
 Exemple de identificatori ilegali: 123abc, -salary
Literali

Un literal este o reprezentare în codul sursă a unei valori fixe. Aceştia sunt
reprezentaţi direct în cod şi pot fi atribuiţi oricărui tip de date primitive. De exemplu:

int a =68; char a ='A';

byte, int, long, şi short pot fi exprimaţi în baza 10 (zecimal), baza 16 (hexazecimal)
sau baza 8 (octal). Prefixul 0 este folosit pentru a indica un prefix pentru numerele în
octal, iar 0x este prefixul ce indică literalii în hexazecimal. De exemplu:

int decimal=100; int octal =0144; int hexa =0x64;

Literalii de tip String sunt specificaţi ca şi în alte limbaje de programare prin scrierea
unor secvenţe de caractere între perechi de ghilimele. Exemple de literali String:

"Hello World"

"two\nlines"

"\"This is in quotes\""

Literalii de tip String sau char pot conţine orice caractere în Unicode. De exemplu:

char a ='\u0001'; String a ="\u0001";

Assignment

1. Folosind 3 variabile, afişaţi următorul text. Variabilele sunt subliniate. Alegeţi tipul
corespunzător.
This is room #113
e is close to 2.71828
I am learning a bit about Computer Science

2. Folosind 2 variabile, una pentru nume şi una pentru anul naşterii, să se printeze un
mesaj. Să se aleagă tipuri de date corespunzătoare şi nume potrivite pentru
variabile. Mesajul va arăta ca în exemplul de mai jos:

Mă numesc Ion Ionică Ionescu şi m-am născut în anul 1820.

3. Să se printeze un orar pentru o zi de liceu, ce va conţine numele orei şi profesorul


care o predă. Să se folosească variabile corespunzătoare acolo unde este cazul.
Citirea de la tastatură
Majoritatea programelor sunt făcute pentru a interacţiona cu utilizatorul, nu doar
pentru afişare. Cea mai simplă modalitate este să introducem ceva de la tastatură,
apoi să prelucrăm valorile şi să întoarcem rezultatul.

I/O:
Input = datele introduse în program
Output = datele afişate / rezultate din program

Pentru a folosi citirea de la tastatură, avem nevoie de un obiect al clasei Scanner.


Pentru a folosi această clasă, avem nevoie de următoarea instrucţiune în afara
clasei, la începutul fişierului:

import java.util.Scanner;

Astfel, îi vom spune programului că vrem să folosim un Scanner. Pentru a folosi


Scanner-ul, acesta trebuie declarat şi instanţiat ca şi restul variabilelor şi obiectelor.
De exemplu:

Scanner keyboard = new Scanner(System.in);

Exemplu

import java.util.Scanner;

public class AskingQuestions {

public static void main(String[] args) {


Scanner keyboard = new Scanner(System.in);

int age;

String height;

double weight;

System.out.print( "How old are you? " );

age = keyboard.nextInt();

System.out.print( "How tall are you? " );

height = keyboard.next();

System.out.print( "How much do you weigh? " );

weight = keyboard.nextDouble();

System.out.println( "So you're " + age + " old, " +


height + " tall and " + weight + " heavy." );

}
}

Clasa Scanner dispune de o serie de metode care permit introducerea datelor. Puteţi
consulta API-ul pentru detalii, cele mai uzuale fiind cele care încep cu “next”. Acestea
aşteaptă ca utilizatorii să introducă un anumit tip de dată de la tastatură.

Assignment

Schimbaţi programul anterior pentru a schimba citirea înălţimii cu două variabile: feet
şi inches. Acestea sunt reprezentate prin numere întregi.

Assignment

1. Cereţi utilizatorului să introducă de la tastatură 2 cuvinte şi 2 numere, iar la sfarşit


afişaţi un mesaj generic. Nu reţineţi valorile introduse în variabile. Singura variabilă
de care veţi avea nevoie va fi de tipul Scanner.

2. Întrebaţi utilizatorul cum îl cheamă. Afişaţi numele şi întrebaţi câţi ani are. Afişaţi
vârsta şi întrebaţi cât câştigă. Afişaţi salariul. Folosiţi variabile corespunzătoare
pentru valorile introduse.

3. Cereţi utilizatorului câteva informaţii şi afişaţi-le la sfârşit. De exemplu:

Please enter the following information so I can sell it for a


profit!

First name: Helena

Last name: Bonham-Carter


Grade (9-12): 12

Student ID: 453916

Login: bonham_453916

GPA (0.0-4.0): 3.73

Your information:

Login: bonham_453916

ID: 453916

Name: Bonham-Carter, Helena

GPA: 3.73

Grade: 12

4. Refaceţi următoarea conversaţie, atfel încât să aflaţi numele şi vârsta utilizatorilor.


Afişaţi vârsta pe care aceştia o să o aibă în 5 ani, şi vârsta care au avut-o acum 5
ani.

Hello. What is your name? Percy_Bysshe_Shelley

Hi, Percy_Bysshe_Shelley! How old are you? 34

Did you know that in five years you will be 39 years old?
And five years ago you were 29! Imagine that!

5. Creaţi un calculator simplu care cere 3 numere reale de la tastatură (a, b, c) şi


calculează suma lor, apoi o împarte la 2. Afişaţi rezultatul pe ecran.

6. Rezolvaţi următoarea problemă:

BMI Calculator

The body mass index (BMI) is commonly used by health and


nutrition professionals to estimate human body fat in
populations.

It is computed by taking the individual's weight (mass) in


kilograms and dividing it by the square of their height in
meters.

Sample Output

Your height in m: 1.75

Your weight in kg: 73

Your BMI is 23.83673


Then, input their weight and height using pounds and inches,
and convert to kilograms and meters to figure the BMI.

Your height in inches: 69

Your weight in pounds: 160

Your BMI is 23.625289

Then, input their height in feet and inches.

Your height (feet only): 5

Your height (inches): 9

Your weight in pounds: 160

Your BMI is 23.625289

7. Creați un calculator complex. Se introduc 2 numere de la tastatură și semnul


operației ce se dorește efectuată și se cere să se afi șeze rezultatul. Exemplu de
execuție:

Introduceți primul număr:


5

Introduceți al doilea număr:

Ce operație doriți să efectuați, introduceți unul din semnele:


+, -, *, /:

Rezultatul este: 5 * 2 = 10.

Pentru a compara 2 string-uri se folosește metoda equals().


Detalii:http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html
Tipuri de date primitive în Java
byte
 Datele de tip byte sunt numere întregi cu semn pe 8 biţi (1 octet).
 Valoarea minimă este -128 (-27 )
 Valoarea maximă este 127 (inclusiv) (27 -1)
 Tipul de date byte este de obicei folosit pentru economisirea spaţiului în
vectori mari, de obicei în locul numerelor întregi, având în vedere că un byte este
de 4 ori mai mic decât un int.
 Valoarea implicită este 0
 Exemple: byte a = 100, byte b = -50
short
 Datele de tip short sunt numere întregi cu semn pe 16 biţi (2 octe ți = 2 bytes).
 Valoarea minimă este -32,768 (-215 )
 Valoarea maximă este 32,767(inclusiv) (215 -1)
 Tipul de date short poate fi folosit de asemenea pentru a economisi spaţiul
precum byte-ul. Un short este de 2 ori mai mic decât un int.
 Valoarea implicită este 0.
 Exemple: short s= 10000, short r = -20000
int
 Datele de tip int sunt numere întregi cu semn pe 32 de biţi (4 octe ți = 4 bytes).
 Valoarea minimă este - 2,147,483,648 (-2 31 )
 Valoarea maximă este 2,147,483,647 (inclusiv) (2 31 -1)
 Este folosit ca tipul implicit pentru numere întregi dacă nu există probleme de
spaţiul de memorare.
 Valoarea implicită este 0.
 Exemple: int a = 100000, int b = -200000
long
 Datele de tip long sunt numere întregi pe 64 de biţi (8 octe ți = 8 bytes).
 Valoarea minimă este -9,223,372,036,854,775,808 (-2^63)
 Valoarea maximă este 9,223,372,036,854,775,807 (inclusiv) (2^63 -1)
 Acest tip este folosit când sunt necesare numere mai mari decât int.
 Valoarea implicită este 0L.
 Exemple: int a = 100000L, int b = -200000L
float
 Datele de tip float sunt numere reale de simplă precizie pe 32 de biţi (4 octe ți).
 Acest tip de date este de obicei folosit pentru economisirea spaţiului in vectorii
de numere reale.
 Valoarea implicită este 0.0f.
 Datele de tip float nu sunt niciodată folosite pentru valori precise, cum ar fi
banii.
 Exemple: float f1 = 234.5f
double
 Datele de tip double sunt numere reale pe 64 de biţi (8 octe ți).
 Acest tip de date este folosit ca tipul implicit pentru numere reale.
 Datele de tip double nu sunt niciodata folosite pentru valori precise, cu ar fi
banii.
 Valoarea implicită este 0.0d.
 Exemple: double d1 = 123.4
boolean
 Datele de tip boolean reprezintă un bit de informaţie.
 Există doar două valori posibile: true şi false.
 Acest tip de date este folosit pentru flag-uri simple care urmăresc condiţii
adevărate/false.
 Valoarea implicită este false.
 Exemple: boolean one = true
char
 Datele de tip char sunt caractere Unicode pe 16 biţi (2 octe ți).
 ASCII este un subset al Unicode cu doar 128 de valori
 Valoarea minimă este '\u0000' (sau 0).
 Valoarea maximă este '\uffff' (sau 65,535 inclusiv).
 Acest tip de date este folosit pentru a reţine orice caracter.
 Exemple: char letterA ='A'
Assignments
0. Să se declare şi să se afişeze diferitele tipuri de date. Să se citească câte 2 valori
de la tastatură pentru fiecare tip. Să se introducă valori foarte mari (> 2 miliarde) sau
foarte mici (-2 miliarde) pentru toate. Ce observaţi?

1. Scrieţi un program care să ceară 2 numere de la tastatură şi să afişeze rezultatele


adunării, scăderii, împarţirii, înmulţirii şi modulo. Folosiţi pe rând byte, short, int, long.

Exemplu

For the numbers 30 and 10.

The result of adding is 40.

The result of subtracting is 20.

The result of multiplying is 300.

The result of dividing is 3.

The result of module is 0.

2. Să se verifice dacă un oraş este metropolă. Se vor folosi 3 variabile:

 isCapitalCity is true if and only if the city is a capital city.


 numberOfCitizen is the number of citizens in this city.
 taxPerCitizen is the average tax per month a citizen of the city pays.
O metropolă este un oraş care este ori capitală cu mai mult de 100 000 de
cetăţeni ori un oraş cu mai mult de 200 000 de cetăţeni şi un venit mediu de 72 000.
Să se introducă de la tastatură cele 3 valori, să se reţină într-o expresie booleană
rezultatul şi să se afişeze.
3. Să se calculeze rezistenţa unui fir, ştiind că formula este:

R = P (l / A)

Unde:

P – rezistivitatea (exemplu de valori: 1.78*10-8, 2300)


l – lungimea firului în metrii
A – aria în metrii părtraţi

Iar

A = pi*r*r
D = 2*r

Unde:

r – raza firului pentru care se cere rezistenţa


D – diametrul firului pentru care se cere rezistenţa

Să se introduca de la tastatură următoarele valori: rezistivitatea, lungimea şi


diametrul firului. Să se afişeze rezistenţa. Să se testeze cu valori implicite (P =
1.78*10-8, l = 1m, D = 1mm). Să se folosească exprimarea ştiinţifică cu E.
ASCII și Unicode

Hai sa facem o mica istorisire a evenimentelor.

Initial americanii au inventat sistemul ASCII care inseamna American Standard Code

for Information Interchange. El avea 128 de caractere codificate in numere de la 0 la

127. Erau principalele litere mari, mici, cifre si alte cateva semne.

Ulterior ASCII a fost extins la 256 de caractere, adica de la 0 la 255.. (=2 8 , adica

inmagazinarea inforamtiei in fix 8 biti = 1 octet.)

Cu timpul, insa s-a dovedit a fi prea mic, si a aparut sistemul unicode pe 2 bytes (2

octeti) de la 0 la 65535. adica 216 caractere. Java foloseste exact acest sistem.

primitiva char ocupa exact 2 octeti si are exact 65536 caractere, adica de la 0 la

65535.

char ch1 = 'J'; se mai poate scrie si ca

ch1 = 74; in sistem zecimal (~ sa zicem la asta se referea

problema cand zicea de ASCII)

Putem atribui valori lui ch1 pana la maxim 65535.

ch1 = '\u004a'; in sistem hexazecimal (unicode)

Dar cele trei inseamna exact acelasi lucru, si anume litera J mare.

Daca facem System.out.println (ch1); obtinem J.

Ca sa obtinem valoarea numerica a ei facem:


int val = (int)ch1;

Merge si direct:

int val = ch1;

caci primitiva char este de fapt un fel de numar.

Apoi System.out.println(val); si ne da 74.

Ca sa printeze valoarea in format unicode hexazecimal fie transform pe 74 intr-o

reprezentare in baza 16, fie folosim o metoda a clasei String, pe care nu am invatat-

o, si anume String hex = String.format ("\\ux", (int)a);

Ideea e ca ambele inseamna reprezentari diferite ale aceleasi valori. Aveti mai jos un

link cu tabelul caracterelor. Ascii Table - ASCII character codes and html, octal, hex

and decimal chart conversion www.asciitable.com

Eclipse în Windows nu afiseaza caracterele a căror valoare numerică depă șe ște 127.

O afișează ca semnul intrebarii '?'.

Dar nu este eroare. Pur si simplu, Eclipse nu poate afisa alte caractere.

Exemplu:

char caracter = 65000; //MERGE!

System.out.println(caracter); => Eclipse afiseaza semnul

intrebarii, pentru nu poate afisa valoarea reala.

Sub Ubuntu afișează toate caracterele.


Concluzie: daca facem System.out.println(ch1); ne da J. Ca sa obtinem valoarea

numerică facem System.out.println( (int)ch1 ); Daca avem mai multe variabile

de tip char si facem:

char ch1 = 'I';

char ch2 = 'o';

char ch3 = 'n';

System.out.println( ch1 + ch2 + ch3); //

calculatorul ne transforma in valori numerice si face adunare

calculatorul ne transforma in valori numerice si face adunare

Daca vrem sa obtinem scris "Ion" trebuie sa fortam operatorul "+" sa facă

concatenare, nu adunare: System.out.println( "Numele meu este: " + ch1 + ch2

+ ch3 ); ⇒ Ion

Ca să se complice puțin lucrurile, între timp Unicode a trecut la o reprezentare pe 3

bytes (octeți), adică pe 24 biți, căci se pare că nici 65536 caractere nu au fost

suficiente.

Java, însă, a rămas pe 2 bytes în ce îl privește pe char, (unicodul vechi) și se pare că

s-a îngreunat puțin transformarea între char si unicode-ul actual, pe 3 bytes.

Assignment
Sa se definească două caractere. Sa se afiseze valorile lor in Unicode si in ASCII,

apoi sa se faca suma acestora si sa afiseze rezultatul. Ce diferente observati? Care

este deosebirea intre ASCII si Unicode?

Afișare din baza 10 în baza 16:

Integer.toString(int, 16) sau Integer.toHexString(int)

Din baza 16 in baza 10:

Integer.valueOf(string, 16)
Operatori in Java
Operatorul de atribuire
int i = 45;

int j = 6 + 5;

int k = i + j;

int m = 056; //baza 8

int n = 0x5f; //baza 16

Operatori aritmetici
1. binari (adică necesită 2 operanzi):

+, -, *, /, %

Exemplu:

int c = a+b;

int d = a-b;

int d = a*b;

int e = a/b; //Dacă împărțirea dă rest, variabila stochează


numai partea întreagă

int f = 17%5; (modulo)

//f stochează restul împărțirii lui 17 la 5, adică 2. 17 = 5 x


3 + 2.

2. unari (adică implică un singur operand) :


int a = 3;

a+=5; e totuna cu a = a + 5; \\

Atribuim lui a valoarea anterioara la care adunam 5, a va fi 8, adica 3 + 5.

La fel si

a-=4 (scadem 4)

a*=6 (inmultim cu 6)

a/=3 (impartim la 3)

i *= 2; e totuna cu i = i * 2;

int b = 13; b%=5: la fel ca b = b%5; adica 3.

i++; e totuna cu i+=1; sau i=i+1; i–; e totuna cu i-=1; sau


i=i-1;

Nu exista i** sau i//.

</note>

Exista si ++i și ––i.

i++ diferă de ++i în sensul că al doilea se efectuează imediat.


i–- diferă de -–i în sensul că al doilea se efectuează imediat.
Daca facem System.out.println(i++); Il afisează mai intai pe i si apoi il incrementeaza. Daca
facem System.out.println(++i); Mai intai il incremeteaza pe i, si apoi face afisarea. La fel si cu
i– si –i int a = 4; int b = 10; System.out.println(a++ + ++b); b devine 11. Se aduna a+b=15. Se
afiseaza 15. Apoi a devine 5. la fel si cu z= a++ + ++b; z va fi 15, nu 16.

Dacă facem int i = 5; si apoi i = i++; i ramane 5, nu se incrementează la 6. Petnru că i se


atribuie lui i vechea lui valoare. Dacă facem i = ++i; atunci i se incrementează.

3. ternari
boolean bool;

a > 5 ? bool = true : bool = false);

System.out.println(a> 5 ? "a este mai mare decât 5" : "a nu


este mai mare decât 5");

Se verifică dacă a este mai mare decât 5. Dacă da, se efectuează prima operație după semnul
întrebării. Dacă nu, se efectuează cea de a doua.

int a = 5;

int b = 4;

System.out.println(a > b ? “a e mai mare decât b” : “a e mai


mic decât b”);

E echivalent cu:

if (a > b) {

System.out.println(“a e mai mare decât b”);

} else {

System.out.println(“a e mai mic decât b”);

boolean bool = (a > b ? true : false);

E echivalent cu :

boolean bool;

if (a > b) {

bool = true;

} else {
bool = false;

Operatori de comparație
>, >=, <, ⇐, ==, !=

Se folosesc pentru a evalua o expresie.

if (a> 5) {...}

>= < ⇐ == != Ei returnează totdeauna doar true sau false. (adică un boolean)
!= înseamnă diferit de, Not Equal

if (a != b) {...}

if ( ch != 'e' ) {...}

== if (a ==0){..} este egal cu 0? Atenție nu confundați pe = (atribuire) cu == (verificare


egalitate)

if și while evaluează condiții. De aceea, putem spune if (true) sau if (false). if(false) nu are
sens. while (false) dă eroare de compilare.

Operatori de comparație se folosesc numai pentru


primitive
Urmatoarele informații vor fi înțelese mai bine în viitorul apropiat:

Dacă folosim == la obiecte, se compară adresa din memorie. Din întâmplare merge la String,
pentru a compara șirurile, dar doar în anumite cazuri.

Vă recomand foarte insistent să nu le folosiți pe == și pe != la String !!!

În viitorul apropiat o să vedem de ce uneori merge la String.

Egalitatea dintre 2 obiecte se face cu metoda equals() Exemple de obiecte: String, array, racul
din Greenfoot. Pentru a verifica egalitatea dintre 2 obiecte, metoda equals trebuie redefinită
pentru obiectul respectiv (o să vedem asta săptămâna asta), pentru ca calculatorul să știe cum
să verifice egalitatea. Cum verifici egalitatea între 2 raci? Sau 2 ursuleți? Depinde ce vrei să
compari.
Dacă nu o redefinim, equals() testează egalitatea adreselor acestor obiecte în memorie, ca și
==. La Array nu are sens să verificăm egalitatea. equals() testează egalitatea adreselor acestor
obiecte în memorie, nu dacă au elemente identice. Dacă vrem să testăm dacă au elemente
identice, trebuie să verificăm noi manual element cu element.

String a redefinit metoda equals(). De accea o putem folosi pentru a verifica egalitatea dintre
2 șiruri de caractere. (Mai multe despre equals() săptpmâna asta.) În cazui clasei String putem
compara două șiruri din punct de vedere alfabetic (asemenea unui dicționar, care cuvânt se
situează în fața altuia din punct de vedere alfabetic) cu metoda .compareTo(alt șir);

Operatori logici
Când vrem să verificăm mai multe condiții odată.

&- ȘI ,
|- ORI,
^- XOR (unul sau celălalt, dar nu ambele),
!- NOT

if ( a>0 & b<0 ) {

........

if ( a> 10 & a <20) { ... }

if ( a <0 | a> 100) { ... }

if ( ! str1.equals("catel") ) {...}

// Dacă str1 este diferit de "catel"

if ( ! (a>5) ) {...}

//Dacă "a" nu e mai mare decât 5

boolean bool = true;


if ( bool ) {...}

// Dacă bool

// Nu are sens să scriem if (bool==true) căci bool e deja un


boolean: true sau false. If primește ca condiție un boolean.

if ( ! bool ) {...}

// Dacă Not bool

// Nu are sens să scriem if (bool==false), bool e deja un


boolean. If primește ca condiție un boolean.

& true fals


true true false
false false false
| true false
true true true
false true false
^ true false
true false true
false true false
true false
! false true
Operatori de scurtcircuitare
Operatorii && și ||.

Operatorii de scurtcircuitare se folosesc cel mai adesea din obișnuință, dar, totuși, uneori ei
sunt imperativ necesari pentru a sări, în anumite condiții, evaluarea celei de a doua condiții,
în sensul de a nu genera o eroare:

String str; //definirea unei variabile fără a o instanția,


(fără a crea un obiect, sau atribui un obiect).
diverse linii de cod.........în care variabila poate sau nu să
fie inițializată

if (str.length() == 7){….......} ==>> EROARE LA RULAREA


PROGRAMULUI

Eroarea apare pentru că str nu are nici o referință către nici un obiect (Mai mult depre
obiecte în zilele următoare Atenție: String este o CLASĂ, nu o PRIMITVĂ. Cu ajutorul
clasei String creăm obiecte de tip string, așa cum ați creat în Greenfoot). Nu putem apela
metoda length() la un obiect care nu există.

Așa cum nu se poate apela medota turn(grade) la un lobster care nu exsită.

Cum rezolvăm problema?

if (str != null && str.length()==7 ) {.........}

cu ajutorul operatorului AND de scurtcircuitare

Dac ă str e null, cea de a doua condiție nici nu mai este evaluată și nu se mai generează
eroarea!

Assignment
Aflarea dimensiunii unui șir de caractere.

String string3 = ”Marioara”; //vedeți că Eclipse

protestează la aceste ghilimele din Office. Dacă copiați

codul, rescrieți ghilimelele.

if ( string3.length() == 8 ) {….}

Dar, atenție, dacă string nu e instanțializat/inițializat/definit? Dacă string3 e null?

Verificăm:
if ( string3 != null ) {

if (string3.length() == 8) {

System.out.println (“Great”);

//condiția a doua se execută numai dacă prima condiție e true.

//În felul acesta ne asigurăm că nu va crăpa aplicația la

rulare.

Sau, prescurtat:

if ( string3 != null && string3.length() == 8 )

{..........}

&& - operatorul logic AND de scurtcircuitare.

Dacă prima condiție e false, nu se mai verifică cea de a doua condi ție, ca nu are

sens. false & true tot false dă. Cea de a doua condi ție se evaluează numai dacă

prima e true.
Cast (transformare) dintr-o primitivă în
alta
Putem atribui unui int valoarea unui short sau byte, pentru că 2, respectiv 1 byte

(octet) încap în 4:

short sh = 45;

int i = sh;

Dar nu putem atribui unui

 byte valoarea unui short sau int sau long

 short valoarea unui int sau long

 int valoarea unui long

 float valoarea unui double

 int valoarea unui float sau double

pentru că pur și simplu spațiul cu mai mulți octeți nu încape într-un spațiu cu mai

puțini octeți.

Obținem o eroare de compilare:

int i = 32;

String. API-ul pentru String


Un String este un şir de caractere care conţine mai multe caractere. Fiecărui caracter

din String îi este asociată o poziţie (sau index). Numărătoarea poziţiilor începe de la

0. Astfel, şirul “JAVA” conţine 4 caractere care au următorii indecşi:

 J=0

 A=1

 V=2
 A=3

Deci, pentru un şir de 4 caractere, poziţiile lor vor fi notate de la 0 la 3. Pentru

generalizare:

 lungime = n

 poziţie minimă = 0

 poziţie maximă = n-1

Assignment
Consultaţi API-ul pentru String pentru a rezolva următoarele probleme:

1. Să se citească un şir de caractere de la tastatură. Să se afişeze primul

caracter.

2. Să se întrebe utilizatorul dacă mai vrea suc. Acesta trebuie să răspundă cu Y

sau N. Să se folosească o variabilă de tip char.

3. Să se citească 2 şiruri de caractere. Să se concateneze folosind o metodă şi

să se afişeze.

4. Se citesc 2 şiruri de caractere. Să se verifice dacă primul şir îl conţine pe al

doilea (Ex: “Programare” conţine “mare”).

5. Se citeşte un şir de caractere de la tastatură. Să se verifice dacă acesta se

termină cu “ala”.

6. Să se compare 2 şiruri de caractere citite de la tastatură. Comparaţia să se

facă ţinând cont de caracterele majuscule (MARE este egal cu MARE, dar nu

este egal cu Mare), apoi ignorând-ule (Mare este egal şi cu MARE şi cu mArE).

7. Se citeşte un şir de caractere şi un caracter. Să se verifice dacă caracterul se

află în şir, prin poziţia sa.

8. Să se citească un şir de caractere şi să se afişeze câte caractere conţine.


9. Să se inlocuiască un caracter dintr-un şir cu altul. Se vor citi pe rând şirul şi

cele 2 caractere.

10. Să se verifice dacă un şir de caractere începe cu un alt şir de caractere (Ex:

Lanterna începe cu Lan). Se vor citi cele 2 şiruri de caractere.

11. Să se returneze un subşir dintr-un şir introdus de la tastatură în funcţie de 2

indecşi (introduşi la rândul lor de la tastatură). De exemplu: pentru şirul “avion” şi

indecşii 1 şi 3 se va afişa “vi”. Indexul de început va fi inclus, iar cel de sfârşit

exclus.

12. Să se convertească caracterele unui şir de caractere introdus în litere mari,

apoi în litere mici. (Ex: pentru “MiercuRi” se va afişa pe rând “MIERCURI” şi

“miercuri”).

13. Să se şteargă spaţiile albe de la începutul şi sfârşitul unui şir de caractere

introdus. De exemplu: “ asd ” va deveni “asd”.

String. API-ul pentru String (2)


Metode frecvent folosite din clasa String:

String string1 = "Avioane de hartie, iiee, ooee.";

char ch1 = string1.charAt(0); //primul caracter

String string2 = "Avioane de hartie";

String string3 = string1.concat(string2); //concatenare

boolean bool1 = string1.contains(string2); //daca il contine pe

string2
boolean bool2 = string1.endsWith("ooee."); //daca se termina cu...

String string4 = "aVioAne de HarTIe";

boolean bool3 = string2.equals(string4); //daca sunt egale

boolean bool4 = string2.equalsIgnoreCase(string4); //daca sunt egale

ignorand Case-ul

int indexul = string1.indexOf('h'); //prima aparitie a lui h

int lungime = string1.length(); //numarul de caractere

String string5 = string1.replace('.', '!'); //inlocuieste

boolean bool5 = string1.startsWith("Avioane"); //daca incepe cu

String string6 = string1.substring(19, 23); // iiee

String string7 = string4.toLowerCase();

String string8 = string4.toUpperCase();

String string9 = " andra@avioanedehartie.ro ";

String string10 = string9.trim(); // eliminarea spatiilor albe de

la inceput si sfarsit, pt emailuri, numere de telefon etc


Clasele din Java SE (standard edition)
Accesati documentatia clasei String. In locul cifrei 7 din cadrul link-ului puteți tasta 6

sau 5 sau 8 pentru a vedea celelalte versiuni.

JavaSE a ajuns la versiunea 8.

Dati click pe Frame pentru a vedea toate clasele. Nu va speriati. Nu trebuie sa stim

decat cateva.

Clasa String

Coloana din stânga arată return-type-ul respectivelor metode.

De exemplu:

 .charAt(int index) returnează char-ul de la indexul respectiv

 .concat(String str) returnează stringul rezultat din concatenarea a două

stringuri

 .equals(Object object) returnează un boolean (true sau false)

 .getBytes() returnează un array de byte byte[]

 .getChars(..) nu returnează nimic (void).

 .indexOf(int ch) returnează un int (indexul). Observați că parametrul ch nu a

fost scris ca char ci ca int, pentru că așa cum am văzut, char poate fi asociat cu

un int. În fond este un număr: char ch = 345;

Assignment
Folosind bucla while, să se implementeze metoda (funcția) indexOf(char) din

clasa String:

1. Avem un șir de caractere.


2. Afișăm indexul primei apariții a unui caracter, dacă există în șirul nostru, iar

dacă nu, afișăm -1.

3. Nu știm câte caractere are șirul.

Varianta 1

Pe care poate că unii dintre voi o preferați.

Bucla While care conține un if.

Condiția din while verifică dacă am gasit 'e'-ul.

Condiția din if verifică dacă am ajuns la capătul șirului. Dacă nu ieștim din buclă,

index va depăși capătul șirului și vom avea o eroarea la rulare de tipul.

StringIndexOutOfBoundsException pe linia sir.charAt(index).

public class Main {

public static void main(String[] args) {

String sir= "abcdfghij";

int index = 0;

while (sir.charAt(index) != 'e'){

index++;

if (index == sir.length()){

break;
}

//acum verificam care a fost conditia care ne-a scos din bucla.

//Am ajuns la capat sau am gasit 'f'-ul ?

if (index != sir.length()){

System.out.println("e se afla la indexul: " + index);

} else {

System.out.println("Returnam -1 pentru ca 'e' nu exista.");

Varianta 2

Bucla while are ambele condiții.

Verificăm totodată atât dacă am gasit caracterul, cât și dacă am depașit capătul

șirului.

public class Main {

public static void main(String[] args) {


String sir= "abcdeghij";

int index = 0;

while ( index != sir.length() && sir.charAt(index) != 'e' ){

index++;

//acum verificam care a fost conditia care ne-a scos din bucla.

//Am ajuns la capat sau am gasit 'f'-ul?

if (index != sir.length()){

System.out.println("'e' se afla la indexul: " + index);

} else {

System.out.println("Returnam -1 pentru ca 'e' nu exista.");

}
Structura Condiţională. If

Este folosită pentru a verifica dacă o condiţie este adevarată. Sintaxa de bază este următoarea:

if (conditie) {

instructiuni;

Unde:

 Condiţie – valoare booleană


 Instrucţiuni – oricât de multe instrucţiuni executate
 {} – opţionale pentru o singură instrucţiune
Instrucţiunile vor fi executate doar dacă condiţia are valoarea de adevăr true.

Exemplu
public class WhatIf {

public static void main(String[] args) {

int people = 20;

int cats = 30;

int dogs = 15;

if (people <cats) {

System.out.println( "Too many cats! The world is


doomed!" );

if (people> cats) {

System.out.println( "Not many cats! The world is


saved!" );

}
if (people <dogs) {

System.out.println( "The world is drooled on!" );

if (people> dogs) {

System.out.println( "The world is dry!" );

dogs += 5;

if (people>= dogs) {

System.out.println( "People are greater than or equal to


dogs." );

if (people <= dogs) {

System.out.println( "People are less than or equal to


dogs." );

if (people == dogs) {

System.out.println( "People are dogs." );

} } }

Assignment

Assignment
Schimbaţi valorile iniţiale ale variabilelor astfel încât să nu fie afişat doar un singur

mesaj. Adăugaţi if-uri astfel încât programul să afişeze minim 3 mesaje. Adăugaţi

comentarii. Ştergeţi acoladele şi observaţi diferenţele.

Assignment

1. Creaţi un program care să afişeze unul din următoarele mesaje pentru o vârstă

introdusă de la tastatură. Testaţi programul cu toate variantele.

 vârsta e mai mică ca 16, scrie "Nu poţi conduce."

 vârsta e mai mică ca 18, scrie "Nu poţi vota."

 vârsta e mai mică ca 25, scrie "Nu poţi închiria maşini."

 vârsta e 25 sau mai mare, scrie "Poţi să faci orice este legal."

2. Introduceţi un număr între 1 şi 7 de la tastatură. Afişaţi denumirea zilei

corespuzătoare numelui introdus (1 – Luni, 2 – Marti, etc.)

3. Scrieţi un program în Java prin care introduceţi vârsta. În funcţie de aceasta, se

vor face următoarele verificări şi se va afişa un mesaj:

 Pentru o vârstă mai mică de 16 ani, “Nu poţi conduce.”

 Pentru o vârstă între 16 şi 17 ani: “Poţi conduce, dar nu poţi vota.”

 Pentru o vârstă între 18 şi 24 de ani: “Poţi vota, dar nu poţi închiria maşini.”

 Pentru o vârstă de 25 de ani sau mai mare: “Poţi să faci cam orice.”

4. Se cunoaşte gravitaţia relativă pentru fiecare planetă. Să se introducă greutatea

de pe Pământ şi planeta dorită şi să se afle greutatea pe planeta respectivă, ştiind

că:
 1 Venus – 0.78

 2 Marte – 0.39

 3 Jupiter – 2.65

 4 Saturn – 1.17

 5 Uranus – 1.05

 6 Neptun – 1.23

Pentru calcul, se va înmulţi greutatea introdusă cu gravitaţia de pe planeta cerută.

Ex: 128 * 0.39 = 49.92.

Exemplu consolă:

Please enter your current earth weight: 128

I have information for the following planets:

1. Venus 2. Mars 3. Jupiter

4. Saturn 5. Uranus 6. Neptune

Which planet are you visiting? 2

Your weight would be 49.92 pounds on that planet.

5. Creaţi un formular cu răspunsuri multiple, în care doar un răspuns este corect.

Reţineţi numărul de răspunsuri corecte şi afişaţi-l la final.

Exemplu consolă:
Are you ready for a quiz? Y

Okay, here it comes!

Q1) What is the capital of Alaska?

1) Melbourne

2) Anchorage

3) Juneau

That's right!

Q2) Can you store the value "cat" in a variable of type int?

1) yes

2) no

Sorry, "cat" is a string. ints can only store numbers.


Q3) What is the result of 9+6/3?

1) 5

2) 11

3) 15/3

That's correct!

Overall, you got 2 out of 3 correct.

Thanks for playing!

6. Două întrebări

Creaţi un program care să pună 2 întrebări. Prima întrebare ar trebui să fie "animal,

vegetal sau mineral?" şi a doua întrebare "este mai mare decât o pâine?". Apoi

afişaţi una din cele şase combinaţii posibile, în funcţie de răspunsurile primite. Puteţi

alege ce răspunsuri să daţi din cele 6 posibile.

Sugestie:

Mărime\Tip Animal Vegetal Mineral


Mai mic decât o pâine Veveriţă Morcov Agrafă
Mărime\Tip Animal Vegetal Mineral
Mai mare decât o pâine Elan Pepene Ford

Creaţi un program cu if-uri imbricate şi încă unul cu condiţii compuse (care folosesc

&&).

Exemplu consolă:

TWO QUESTIONS!

Think of an object, and I'll try to guess it.

Question 1) Is it animal, vegetable, or mineral?

animal

Question 2) Is it bigger than a bread?

no

My guess is that you are thinking of a moose.

I would ask you if I'm right, but I don't actually care.

7. Scrieţi un program prin care să ghiciţi un număr. Reţineţi un număr într-o variabilă,

apoi introduceţi un număr de la tastatură. Dacă numărul introdus este egal cu cel
reţinut, afişaţi un mesaj de genul “Ai ghicit numărul!”. Dacă este diferit, afişaţi mesajul

“Nu ai ghicit numărul…”.

8. Introduceţi un număr întreg de la tastură. Afişaţi toate proprietăţile acestuia: par

sau impar, pozitiv sau negativ.

If Else
Atunci când condiţia de la if este falsă, putem avea alt set de instrucţiuni pe care îl

vom executa puse în clauza else. Mai mult, se pot face verificări succesive, astfel

încât să executăm doar instrucţiunile potrivite pentru o anumită condiţie.Structura:

if (condiție) {

//bloc de instrucțiuni

} else if {

//bloc de instrucțiuni

} else {

//bloc de instrucțiuni

else nu este obligatoriu

Exemplu
int weekday = 2;

String result = “”;

if ( weekday == 1 ) {

result = "Luni";

} else if ( weekday == 2 ) {

result = "Marti";

} else if (weekday == 3) {

result = “Miercuri”;

} else {

result = “Alta zi”;

Assignment

Refaceţi exerciţiile de pe pagina cu if astfel încât să folosiţi o structură if-else şi

operatorii logici && sau ||.

Assignment

1. Calculator IMC
Creaţi un calculator BMI (body mass index) (IMC – indice de masă corporală). Acesta

este folosit în sănătate şi nutriţie pentru a estima obezitatea populaţiei. Se cere

înălţimea în metrii şi greutatea în kg, apoi se împarte greutatea la înalţimea la patrat.

Ex: kg / (m * m).

Se cere să se afişeze indicele BMI şi categoria din care face parte.

Categorii:

 IMC sub 18.5 - sunteţi o persoană subponderală

 IMC între 18.5 şi 24.9 - sunteţi o persoană normală

 IMC între 25.0 şi 29.9 - sunteţi o persoană supraponderală

 IMC peste 30.0 - sunteţi o persoană obeza

2. Două întrebări

Să se folosească 2 întrebări pentru a ghici un obiect. Se dă următorul exemplu:

Întrebarea 1: Poate fi din interior, din exterior sau ambele?

Întrebarea 2: Este în viaţă?

Cu următoarele variante:

înăuntru afară ambele


în viaţă plantă de ghiveci bou câine
nu e în viaţă perdea de duş panou publicitar smartphone

Exemplu consolă

Inca doua intrebari!!!


Gandeste-te la ceva si o sa incerc sa il ghicesc!

Intrebarea 1: Este din interior, din exterior sau poate fi in

ambele? exterior

Intrebarea 2: Este in viata? da

Atunci ce altceva ar putea fi in afara de un bou?!?

3. Pentru exerciţiul anterior să se adauge şi a treia întrebare!

4. Testaţi metoda compareTo() a clasei String.

System.out.print("Comparing \"axe\" with \"dog\" produces ");

int i = "axe".compareTo("dog");

System.out.println(i);

System.out.print("Comparing \"applebee's\" with \"apple\" produces

");

System.out.println( "applebee's".compareTo("apple") );
Această metodă va produce un rezultat negativ dacă folosim un cuvânt care este

alfabetic înaintea cuvântului cu care se compară şi un rezultat pozitiv, dacă cele 2

cuvinte sunt în ordine alfabetică inversă.

Creaţi un program prin care să introduceţi cuvinte. După primul cuvânt introdus, se

mai cere un cuvânt şi se va afişa dacă acesta este din punct de vedere alfabetic

înainte sau după cuvântul anterior. Apoi se va mai introduce un cuvânt şi se va afişa

dacă este înainte sau după al doilea cuvânt. Pentru al 4-lea cuvânt introdus se va

compara cu al 3-lea, al 5-lea cu al 4-lea, etc. Programul se va opri când vom

introduce acelaşi cuvânt de 2 ori.

Atenţie Limbajul Java face distincţie între literele mari şi literele mici. Creaţi

programul astfel încât să nu ţineţi cont de acest aspect.


Switch
Instrucţiunea switch va verifica o variabilă cu o serie de valori. Atunci când întâlneşte

o valoare egală, va executa toate instrucţiunile până la întâlnirea unui break. În cazul

în care nicio valoare nu este egală, va executa instrucţiunile din clauza default.

Exemplul de mai jos este echivalent cu exemplul de la if-else.

Exemplu 1

int weekday = 5;

String result = "";

switch (weekday) {

case 1:

result = "Sunday";

break;

case 2:

result = "Monday";

break;

case 3:

result = "Tuesday";

break;
default:

result = "Other day";

Exemplu 2

int month = 5;

int daysInMonth = 0;

switch (month) {

case 1: // Ianuarie

case 3: // Martie

case 5: // Mai

case 7: // Iulie

case 8: // August

case 10: // Octombrie

case 12: // Decembrie

daysInMonth = 31;

break;
case 4: // Aprilie

case 6: // Iunie

case 9: // Septembrie

case 11: // Noiembrie

daysInMonth = 30;

break;

case 2: // Februarie

daysInMonth = 28;

break;

Exemplu 3

switch (number) { // un oarecare numar intreg

case 0:

System.out.println("This is my first message!");

case 2:

System.out.println("This is my second message!");


case 15:

System.out.println("This is my 3333333 message!!!!");

break;

case 13:

System.out.println("4444444444444444");

case 4:

System.out.println("No more messages after this 5th one!");

break;

case 3:

System.out.println("And one default, please!");

case 8:

System.out.println("A default that will come after this");

default:

System.out.println("a message by default...");

break;

}
Assignment
Assignment 1

Rescrieţi exemplul 2 şi 3 de la switch folosind un if-else.

Observaţie

Varabila folosită la switch poate fi doar byte, short, char, int sau String. Poate

funcţiona şi cu enumeraţii (Enum) sau clase analoage pentru tipurile de date primitive

menţionate (Character, Byte, Short, Integer). Valorile folosite la clauzele case nu se

pot repeta.

Assignment 2

Refaceţi exerciţiile 2 şi 4 de pe pagina if folosind un switch.

Assignment 3 <

1. Calculator IMC

Creati un calculator BMI (body mass index) (IMC – indice de masa corporala). Acesta

este folosit in sanatate si nutritive pentru a estima obezitatea populatiei. Se cere

inaltimea in metrii si greutatea in kg, apoi se imparte greutatea la inaltimea la patrat.

Ex: kg / (m * m). Se cere sa se afiseze indicele BMI si categoria din care face parte.

Categorii:

 IMC sub 18.5 - sunteţi o persoană subponderală

 IMC între 18.5 şi 24.9 - sunteţi o persoană normală

 IMC între 25.0 şi 29.9 - sunteţi o persoană supraponderală

 IMC peste 30.0 - sunteţi o persoană obeza


2. Două întrebări

Să se foloseasca 2 întrebări pentru a ghici un obiect. Se dă urmatorul exemplu:

 Întrebarea 1: Locul său este în interior, afară sau ambele?

 Question 2: Este viu?

Cu urmatoarele variante:

în interior afară ambele


viu plantă bizon câine
nu e viu perdea de duș panou telefon

3. Pentru exercițiul anterior să se adauge si o a treia intrebare.

4. Testati metoda compareTo() a clasei String.

System.out.print("Comparing \"axe\" with \"dog\" produces ");

int i = "axe".compareTo("dog");

System.out.println(i);

System.out.print("Comparing \"applebee's\" with \"apple\" produces

");

System.out.println( "applebee's".compareTo("apple") );

Aceasta metoda va produce un rezultat negativ daca folosim un cuvant care este

alfabetic inaintea cuvantului cu care se compara si un rezultat pozitiv, daca cele 2

cuvinte sunt in ordine alfabetica inversa.


Creati un program prin care sa introduceti cuvinte. Dupa primul cuvant introdus, se

mai cere un cuvant si se va afisa daca acesta este din punct de vedere alfabetic

inainte sau dupa cuvantul anterior. Apoi se va mai introduce un cuvant si se va afisa

daca este inainte sau dupa al doilea cuvant. Pentru al 4-lea cuvant introdus se va

compara cu al 3-lea, al 5-lea cu al 4-lea, etc. Programul se va opri cand vom

introduce acelasi cuvant de 2 ori.

Limbajul Java face distinctie intre literele mari si literele mici. Creati programul astfel

incat sa nu tineti cont de acest aspect.

Exemplu toString()

Maria a obținut nota 10, Anca 9, iar restul 7. Dar îl avem și pe domnul profesor. Ce

facem ca să afișăm nota?

if ( nume.equals(”Maria”) ) {

System.out.println (“Nota 10”);

} else if ( nume.equals(”Anca”) ) {

System.out.println (“Nota 9”);

} else if ( nume.equals(”profesor”) ) {

// nu scriem nimic aici

} else {

System.out.println (“Nota 7”);


}

switch nu e potrivit pentru String. Folosim numărul elevului din catalog.

switch (nume) {

case (16): syso (“Nota 10”);

break;

case (15): syso (“Nota 9”);

break;

case (-1): //Nu scriem nimic aici.

break;

default: syso (“Nota 7”);

Sau:

if ( !nume.equals(“Maria”) && !nume.equals(“Anca”) &&

!nume.equals(“profesor”) ) {

syso(“Nota 7”);

}
//adică dacă numele nu e nici Maria, nici Anca și nici profesor

//inlocuiti ghilimelele cu ghilimelele de la Eclipse

//se poate si cu paranteze

if ( (!nume.equals(“Maria”) ) && (!nume.equals(“Anca”)) &&

(!nume.equls(“profesor”)) ) ....

if ( (x!=16) && (x!=15) && (x!=-1) ) ......


Structura Repetitivă. While
while este o structură repetitivă cu număr necunoscut de pași.

Structura repetitivă while este folosită pentru a executa o secvenţă de instrucţiuni cât

timp o condiţie este adevarată.

Sintaxa generală este:

while (conditie) {

instructiuni;

La fel ca la structura if, condiţia trebuie să fie o valoare booleană (true sau false), iar

instrucţiunile pot fi oricât de multe.

NU uitați să introduceți o linie de cod care să poată facă condiția false, pentru a

nu intra într-o buclă infinită fără scăpare. Sau folosiți break (detalii mai încolo).

Exemplu

import java.util.Scanner;

public class EnterPIN {

public static void main(String[] args) {

Scanner keyboard = new Scanner(System.in);


String pin = “1234”;

System.out.println("WELCOME TO THE BANK OF MITCHELL.");

System.out.print("ENTER YOUR PIN: ");

String entry = keyboard.next();

while (!entry.equals(pin)) {

System.out.println("\nINCORRECT PIN. TRY AGAIN.");

System.out.print("ENTER YOUR PIN: ");

entry = keyboard.next();

System.out.println("\nPIN ACCEPTED. YOU NOW HAVE ACCESS TO

YOUR ACCOUNT.");

}
Variante de folosire a buclei while cu 1 condiție
1. Afișarea primelor 10 numere.

int i = 1;

while (true){

System.out.println(i);

if (i == 10) {

break;

i++;

int i = 1;

boolean flag = true;

while (flag){
System.out.println(i);

if (i == 10) {

flag = false;

i++;

2. Ghicește numărul

nt numar = 6;

int ghici;

boolean flag = true;

while (flag){

System.out.println(

”Ghiceste numarul de la 1

a 10”);
ghici = keyboard.nextInt();

if (ghici == numar) {

flag = false;

System.out.println(”Felicitari”);

int numar = 6;

int ghici;

while (true){

System.out.println(

”Ghiceste numarul de la 1
a 10”);

ghici = keyboard.nextInt();

if (ghici == numar) {

break;

System.out.println(”Felicitari”);

La ultima variantă a trebuit să îl inițializăm pe ghici = 0.

Dar dacă și 0 putea fi un posibil număr de ghicit? Sau -1?

Atunci procedăm astfel:

int numar = 6;

System.out.println("Ghiceste numarul de la 1 a 10");

int ghici = keyboard.nextInt();


while (ghici != numar){

System.out.println( "Ghiceste numarul de la 1 a 10");

ghici = keyboard.nextInt();

System.out.println("Felicitari");

Aici vedem că se repetă codul.

Nu e bine.

int numar = 6;
int ghici;

do {

System.out.println( "Ghiceste

numarul de la 1 a 10");

ghici = keyboard.nextInt();

} while (numar != ghici);

System.out.println("Felicitari");

Bucla do…while o folosim atunci când trebuie să executăm iterația cel puțin o dată.

Assignment
Assignment 1

Ce asemănări şi deosebiri sunt între while şi if? În interiorul buclei while, de ce nu

avem cuvântul String în faţa lui entry? Ce se întamplă dacă ştergeţi linia entry =

keyboard.next(); din interiorul buclei while?

Assignment 2

1. Investigaţi următoarea secvenţă de cod. Este validă? De ce? Ce afişează

aceasta?

while (true)

System.out.println(“printing”);

2. Salvaţi o valoare între 1 şi 10 într-o variabilă. Încercaţi să ghiciţi numărul prin

introducerea unei valori de la tastatură. Afişaţi mesaje diferite dacă numărul este mai

mare, mai mic sau dacă l-aţi ghicit.

Exemplu consolă

Ghiciti un numar intre 1 si 10.

Introduceti numarul: 3

Numarul cautat este mai mare!

Introduceti numarul: 8

Numarul cautat este mai mic!


Introduceti numarul: 6

Felicitari! Ati ghicit numarul!

3. Pentru programul anterior, afişaţi şi numărul de încercări care au fost necesare.

4. Introduceţi un mesaj de la tastatură. Afişaţi mesajul de 5 ori.

Secvenţă de cod ajutătoare:

int n = 0;

while (n <5) {

System.out.println( (n+1) + ". " + message );

n++;

Pentru a putea finaliza problema avem nevoie de un counter care să numere de câte

ori am afişat mesajul. În acest caz, n ţine locul acestui counter. De obicei, folosim un

while pentru a repeta o serie de instrucţiuni cât timp o condiţie este adevărată.

Această problemă este indicată pentru un alt tip de structură repetitivă, dar se poate

folosi şi un while.

++ este un operator de incrementare. Acesta adaugă 1 la valoarea curentă a

variabilei. Opusul său este –, operator de decrementare.

Modificaţi codul astfel încât:


 Mesajul să fie afişat de 10 ori.

 Afişaţi în faţa mesajului numerele 10, 20, 30, 40, etc… Rezolvaţi în 2 moduri.

 Cereţi numărul de afişări de la tastatură.

 Să fie folosit operatorul de decrementare în locul celui de incrementare.

5. Modificaţi programul dat ca exemplu, astfel încat dacă introduceţi un pin greşit de

3 ori, să nu mai aveţi acces la cont.

6. Scrieţi un program care să facă suma unor numere întregi introduse de la

tastatură. Suma va fi afişată la fiecare pas. Programul se va opri când se va

introduce valoarea 0.

7. Scrieţi un program care să verifice dacă 3 numere introduse de la tastatură pot fi

laturile unui triunghi. Forţaţi programul pentru a introduce numerele în ordine

crescătoare. Numere egale sunt admise.

8. Recreati secventa Lothar Collatz.

Reguli:

 Se consideră un număr natural n.

 Dacă n este par, împărţiţi-l la 2, pentru a obţine n / 2

 Dacă n este impar, înmulţiţi-l cu 3 şi adăugaţi 1, pentru a obţine 3n + 1

 Repetaţi procesul la infinit pentru noul număr

Scrieţi un program care cere un număr de la utilizator, şi afişează secvenţa Collatz

începând de la acel număr. Opriţi-vă când aţi ajuns la 1.

Examplu

Starting number: 6
6 3 10 5 16 8 4 2 1

Mai mult:

 Afişaţi numărul total de paşi din secvenţă.

 Afişaţi cel mai mare număr care apare în secvenţă.

9. Introduceţi un număr întreg de la tastatură. Afişati cifrele acestuia.

10. Introduceţi un număr întreg de la tastatură. Verificaţi dacă este palindrom. Un

număr este palindrom dacă este egal cu opusul său.

11. Scrieți un protgram în care voi vă gândiți la un număr între 1 și 100, iar

computerul trebuie să îl ghicească.

12. Să se afiseze primele 100 de numere naturale folosind o bucla while.

13. Să se realizeze un program în care se aruncă cu zarul de mai multe ori. Sa se

afiaseze punctele. Dacă iese 6 să se termine programul.

Assignment 3

While cu o condiție

1. Realizați un program care primește un număr și afișează cifrele lui. (Indiciu,

dacă folosim număr obținem ultima cifră).

2. Realizați un program care primește un număr de la tastatură și calculează

suma cifrelor lui.

3. Folosind bucla while, transformați un număr din baza 10 în baza 16.

4. Calculatorul ghiceste numarul. Calculatorul trebuie sa ghiceasca un numar la

care v-ati gandit voi (intre 1 si 100). Hint: el propune un numar aleator. Intreaba
daca numarul la care s-a gandit userul este mai mare sau mai mic decat numarul

propus de el. Daca este mai mare, va cauta un numar aleator intre numarul

propus anterior si 100. Daca este mai mic, va cauta un numar aleator intre

numarul propus anterior si 0. Bonus pentru eficienta programului: cel de al doilea

numar propus sa fie la jumatatea intervalului.

5. Rescrierea lui scanner.nextLine() folosind while: Un program care citește

caractere de la tastatură și se oprește la Enter.

6.
While cu două condiții

1. Realizați un program care primește un număr de la tastatură și verifică dacă

este putere a lui 2. (se poate si cu o conditie)

2. Realizati o metodă care face ce face metoda indexOf('char') din clasa String.

Ea prmiește un cuvânt și returnează indexul unei litere, să zicem 'a' sau

returnează -1 dacă litera nu se află în cuvânt.

3. Realizati un program care simuleaza un bancomat. Se verifica pinul. Daca

userul introduce greșit pinul de 3 ori, se iese din bucla while.

4. Realizati o metoda care suprascrie metoda replace(char1, char2) din clasa

String. Programul va inlocui prima aparitie a subsirului “dra” cu “dru” din cuvantul

“Alexandra”. Vom presupune că șirul nostru are un singur “d”. Indiciu: Vom afla

pozitia literei “d”.

5. Modificați programul de la Exercitiul 7, folosind tot o bucla while, astfel încât să

înlocuiească a cincia apariție a literei “a”, dacă există, cu litera “i”. Exemplu: Rareș

a incercat sa prinda pasica, dar aceasta a fost mult mai rapida decat el.

Bineinteles, ca in acest caz se poate face: sir.replace(“pasica”,”pisica”); - dar

vrem sa folosim bucla while.

6. Bonus 1. Modificați programul de la exercitiul 9, pentru a înlocui toate literele

“a” cu “i”. Practic implementăm noi metoda replaceAll(sir, sir) din clasa String.
7. Bonus 2 - Spânzuratoarea

Calculatorul se gandeste la un numar, iar noi trebuie sa il ghicim. Nu exista un număr

maxim de pași. Indiciu: Noi propunem o litera. Calculatorul spune daca exista sau

nu. Daca exista, va afisa pozitiile literei. (Va inlocui _ cu litera.)

Exemplu:

M-am gandit la un cuvănt. (sa zicem spanzuratoare) Poți să îl

ghicești? Zii o literă:

a este. Cuvăntul este __a____a__a__.

Zii o litera

s este. Cuvantul este s_a____a__a__.

Zii o litera.

o este. Cuvantul este s_a___a_oa__.

Zii o litera.

f nu este.

Zii o litera.

p este. Cuvantul este spanzuratoare

Bravo! Ai ghicit!

Structura repetitivă do-while


do…while este o structură repetitivă cu număr necunoscut de paşi.

Do-while se comportă asemănător cu un while. Singura diferenţă este verificarea

condiţiei care este făcută la final pentru structura do-while. Din acest motiv,

instrucţiunile din do-while se vor executa cel puţin o singură dată.

Sintaxa

do {

instructiuni;

} while (conditie);

Unde

 do – cuvânt cheie

 instrucţiuni – oricâte instrucţiuni pe care vrem să le executăm

 while – cuvânt cheie

 condiţie – o condiţie oricât de complexă din care rezultă ceva adevărat sau

fals; atât timp cât condiţia este adevărată, se vor executa instrucţiunile din

interiorul lui do-while

Exemplu
Scanner sc = new Scanner(System.in);

int sum = 0;

do {

int num = sc.nextInt();

sum += num;

} while (num != 0);

System.out.println(sum);

Assignment
Assignment 1

Rulaţi următorul program:

import java.util.Scanner;

public class DoWhileSwimming {

public static void main( String[] args ) {

Scanner keyboard = new Scanner(System.in);


String swimmer1 = "GALLANT";

String swimmer2 = "GOOFUS ";

double minimumTemperature = 79.0; // degrees Fahrenheit

double currentTemperature;

double savedTemperature;

int swimTime;

System.out.print("What is the current water temperature? ");

currentTemperature = keyboard.nextDouble();

savedTemperature = currentTemperature; // saves a copy of

this value so we can get it back later.

System.out.println( "\nOkay, so the current water

temperature is " + currentTemperature + "F." );

System.out.println( swimmer1 + " approaches the lake...." );


swimTime = 0;

while ( currentTemperature>= minimumTemperature ) {

System.out.print( "\t" + swimmer1 + " swims for a bit."

);

swimTime++;

System.out.println( " Swim time: " + swimTime + " min."

);

currentTemperature -= 0.5; // subtracts 1/2 a degree

from the water temperature

System.out.println( "\tThe current water temperature is

now " + currentTemperature + "F." );

System.out.println( swimmer1 + " stops swimming. Total swim

time: " + swimTime + " min." );

currentTemperature = savedTemperature; // restores original

water temperature
System.out.println( "\nOkay, so the current water

temperature is " + currentTemperature + "F." );

System.out.println( swimmer2 + " approaches the lake...." );

swimTime = 0;

do {

System.out.print( "\t" + swimmer2 + " swims for a bit."

);

swimTime++;

System.out.println( " Swim time: " + swimTime + " min."

);

currentTemperature -= 0.5;

System.out.println( "\tThe current water temperature is

now " + currentTemperature + "F." );

} while ( currentTemperature>= minimumTemperature );

System.out.println( swimmer2 + " stops swimming. Total swim

time: " + swimTime + " min." );


}

Rulaţi programul pentru o valoare de 80.5 pentru temperatura curentă a apei. Înoată

cei doi sportivi pentru aceeaşi perioada de timp? Rulaţi programul pentru 78 de

grade. Ce diferenţe sunt? Se aruncă cei doi sportivi în apă fără să verifice

temperatura acesteia?

Assignment 2

1. Se da următorul cod.

Scanner keyboard = new Scanner(System.in);

Random rng = new Random();

String again;

while ( again.equals("y") ) {

int flip = rng.nextInt(2);

String coin;

if ( flip == 1 )

coin = "HEADS";
else

coin = "TAILS";

System.out.println( "You flip a coin and it is... " + coin );

System.out.print( "Would you like to flip again (y/n)? " );

again = keyboard.next();

Ce probleme are? De ce nu se compilează? Rezolvaţi problema, apoi rescrieţi codul

folosind do-while. Stergeţi modificarea care aţi făcut-o la început. Se mai compilează

codul? De ce?

2. Să se scrie un program care să simuleze aruncarea a două zaruri. Să se arate

valorile de pe zaruri, şi suma acestora. Programul se va opri când va întâlni o dublă.

Folosiţi do-while.

3. Să se scrie un program care generează un număr aleator. Să se ghicească

numărul respectiv din mai multe încercări. După fiecare încercare să se afişeze dacă

numărul introdus este mai mare sau mai mic decât numărul care trebuie ghicit. Să se

folosească do-while.
4. Să se creeze un calculator care face operaţii simple de adunare şi înmulţire cu o

cifră. Se introduc numerele cu operaţia între ele, apoi se afişează rezultatul.

Programul se opreşte la adunarea 0 + 0. Folosiţi do-while.

5. Se citesc numere până la întâlnirea numărului 0. Să se facă suma numerelor pare

şi produsul numerelor impare. Implementaţi o rezolvare cu while şi do-while.

6. Să se numere câte cifre are un număr introdus de la tastatură. Implementaţi cu

while şi do-while.

Ex:

 123 are 3 cifre

 2994 are 4 cifre

Greșeli în programare
Fie următorul program ce conține 2 bucle:

int i=0;

while (i<100) {

i++;

//dar atenție, dacă facem o altă buclă

while (i<15) {
i++;

Nu se intră în cea de a doua buclă! Pentru că i a rămas 100!

Fie folosim altă variabilă, fie îi atribuim valoarea 0 înaintea buclei.

Exemple de utilizare
Exemplu:

Folosind bucla while, să se realizeze un bancomat care cere codul pin de un inifinit

de ori până când îl obține pe cel corect:

Varianta 1: cu break, mai puțin folosită:

package automat;

import java.util.Scanner;

public class MyClass{

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

String parola;

while (true){
// E suficient să scriem doar while (bool)

System.out.println("Introduceti codul pin");

parola = scanner.nextLine();

if (parola.equals("9759")){

System.out.println("Bravo");

break;

Varianta 2: cu variabilă booleană:

package automat;

import java.util.Scanner;

public class MyClass{

public static void main(String[] args) {


Scanner scanner = new Scanner(System.in);

String parola;

boolean bool = true;

while (bool){

System.out.println("Introduceti codul pin");

parola = scanner.nextLine();

if (parola.equals("9759")){

System.out.println("Bravo");

bool = false;;

Varianta 3: clasică
package automat;

import java.util.Scanner;

public class MyClass{

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

String parola ="0000"; //trebuie sa o initializam

pentru ca ne trebuie pe randul urmator,

//altfel ne va da eroare la

executare

while ( ! parola.equals("9759")){ //NOT

equals. Observati caracterul "!"

System.out.println("Introduceti codul pin");

parola = scanner.nextLine();

}
System.out.println("Daca ati ajuns aici inseamna ca

ati descoperit parola.");

Varianta a 4-a: De preferat în acest caz.

Totuși, obervați că a trebuit să inițializăm parola cu “0000” ca să nu ne dea eroare pe

linia cu while. E cam forțat. Ce făceam dacă parola era chiar “0000” ? Există pentru

acest caz varianta :

do {

// bloc de instructiuni

} while (conditie) ; // <<==observați punctul si virgula “;”

În acest caz, blocul de instrucțiuni se execută cel puțin o dată. Utilizatorul

oricum va încerca să întroducă măcar o dată parola.

package automat;

import java.util.Scanner;

public class MyClass{


public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

String parola;

do {

System.out.println("Introduceti codul pin");

parola = scanner.nextLine();

} while (! parola.equals("9759")); //Observati

"!" //Observati ”;”

System.out.println("Daca ati ajuns aici inseamna ca

ati ghicit codul pin.");

Structura repetitivă - for


for este o tructura repetitivă cu număr cunoscut de paşi.
Un for este folosit atunci când cunoaştem de câte ori vrem să repetăm o operaţie sau

când vrem să numărăm.

Sintaxa

for (initializare; conditie; increment) {

instructiuni;

Unde

 iniţializare – sunt oricât de multe iniţializări necesare

 condiţie – orice condiţie cât de complicată care rezultă în adevărat sau fals;

instrucţiunile se vor executa cât timp condiţia este adevărată

 increment – instrucţiuni care modifică valori şi care se execută la fiecare pas,

după instrucţiunile din for

Exemplu 1

for (int i = 0; i <7; i++) {

System.out.println(“mesaj ” + i);

Exemplu 2
Să se afişeze un mesaj introdus de 5 ori.
import java.util.Scanner;

public class CountingFor {

public static void main(String[] args) {

Scanner keyboard = new Scanner(System.in);

System.out.println( "Type in a message, and I'll display it

five times." );

System.out.print( "Message: " );

String message = keyboard.nextLine();

for (int n = 1 ; n <= 5 ; n = n+1) {

System.out.println( n + ". " + message );

}
}

Variante de for

for (int i=0; i<100; i++) {.....}

for (int i=100; i>0; i--) {.....}

for (int i=1; i<100; i*=2) {.....}

for (int i=3500; i>100; i/=2) {.....}

Deosebire între for şi while


for este folosit atunci când ştim de câte ori vrem să repetăm ceva (de 5 ori, de 10

ori); while este folosit atunci când ştim până când vrem să repetăm ceva (cât timp nu

ghicim un număr, cât timp nu am am introdus 0).

Un for poate fi scris in felul urmator:

for (int i = 0; i <6; i++) {

// …

}
Un while care îndeplineşte aceeaşi funcţionalitate arată în felul următor:

int i = 0;

while (i <6) {

// …

i++;

Assignment
Assignment 1

Folosind codul dat ca exemplu, modificaţi for-ul în felul următor:

for (int n = 0; n <5; n++)

Ce modificări observaţi? Ce se întâmplă dacă în loc de n++ scriem n = n + 2?

Assignment 2

1. Scrieţi un program care afişează mesajul “I KNOW JAVA!!!!” de 10 ori. Numerotaţi

fiecare afişare a mesajului.

2. Scrieţi un program care citeşte un număr întreg şi numără de la 0 la acesta. (0 1 2

3…)
3. Scrieţi un program care citeşte 3 numere: un număr de la care pornim

numărătoarea, un număr până la care numărăm şi din cât în cât numărăm. Afişaţi

numărătoarea. (0 2 4…)

4. Scrieţi un program care foloseşte o variabilă x pentru a număra de la -10 la +10

din 0.5 în 0.5 (Rezultatul va fi -10, -9.5, -9, -8.5, …).

5. Pentru programul de la punctul 4. afişaţi şi pătratul fiecărui număr folosind o

variabilă y.

6. Scrieţi un program care citeşte un număr şi afişează toate numele până la acesta.

Programul va afişa un semn în dreptul numerelor pare şi alt semn în dreptul

numerelor impare.

7. Scrieţi un program care afişează toate numerele de la 0 la 100. Pentru multiplii de

3 afişaţi mesajul “Tres”, pentru multiplii de 5 afişaţi mesajul “Cinco”, iar pentru

numerele care se împart şi la 3 şi la 5 afişaţi “TresCinco”.

8. Citiţi un şir de caractere de la tastatură. Afişaţi lungimea lui, poziţia şi valoarea

primului caracter, poziţia şi valoarea ultimului caracter şi fiecare caracter cu poziţia

lui. Puteţi folosi funcţia charAt(int) a clasei String. Număraţi de câte ori apare litera „a‟

(fie a, fie A). Afişaţi mesaje pentru fiecare parte.

9. Scrieţi un program care face suma primelor n numere, unde n este citit de la

tastatură (adică 1 + 2 + 3 + 4 + … + n).

10. Scrieţi un program care adună n numere citite de la tastatură (n este şi el citit de

la tastatură). De exemplu: a + b + c + d + …. + k.
11. Scrieţi un program care citeşte un cuvânt de la tastatură şi îl afişează de 10 ori. În

cazul în care cuvântul începe cu o vocală, acesta trebuie afişat doar de 5 ori. Folosiţi

UN SINGUR if şi UN SINGUR for.

12. Scrieţi un program care afişează tabla înmulţirii cu 2.

13. Scrieţi un program care citeşte de la tastatură un număr de la 1 la 9 şi afişează o

piramidă după următorul model:

2 1 2

3 2 1 2 3

4 3 2 1 2 3 4

5 4 3 2 1 2 3 4 5

Programul se poate modifica apoi să primească orice număr, iar afişarea să se facă

cu metoda printf.

For Each
O altă variantă pentru structura for învaţată este for each sau “enhanced for loop”

folosită special pentru colecţiile de date pe care le parcurge element cu element. De

exemplu, pentru a afişa elementele unui tablou din exemplul anterior, putem scrie:

for (int x : tablou1) {

System.out.println(x);

În acest caz, nu mai avem acces la index-ul pe care se află elementul pe care vrem

să îl afişăm. Este mai util să folosim un for-each, ba chiar indicat acolo unde putem.

Assignments

1. Creaţi un vector cu 5 elemente, puneţi valori în toate poziţiile şi afişaţi elementele

de pe poziţiile 1, 2 şi 4.

2. Creaţi un vector de 10 elemente, puneţi valori pe toate poziţiile. La final afişaţi-l de

două ori, folosind cele 2 for-uri diferite.

3. Se citeşte un număr n care reprezintă lungimea unui vector. Creaţi vectorul de

lungime n, puneţi valori aleatoare pe fiecare poziţie (între 1 şi 100) şi afişaţi vectorul.

4. Creaţi un vector care reţine 1000 de valori. Puneţi în el valori aleatoare între 10 şi

99. Afişaţi vectorul, apoi afişaţi de câte ori apar numere cu 0 în coadă (ex: 10, 20, 30,

etc…).

5. Creaţi un vector de 5 elemente, apoi citiţi elementele lui de la tastatură. Copiaţi

apoi conţinutul în alt vector. Schimbaţi ultimul element cu valoarea -5. Afişaţi vectorii.
6. Creaţi un vector de 10 elemente cu numere aleatoare între 0 şi 99. Creaţi un alt

vector care să reţină o copie inversă a acestuia. Afişaţi rezultatul. Exemplu:

10 11 12 13 14 15 16 17 18 19 – vector initial

19 18 17 16 15 14 13 12 11 10 – vector final

7. Creaţi un vector care să reţină 10 numere aleatoare. Introduceţi un număr de la

tastatură şi afişaţi de câte ori găsiţi numărul respectiv în vector. Afişaţi şi un mesaj

când aţi găsit numărul. Schimbaţi programul astfel încât să afişaţi şi indexul pe care

numărul a fost găsit. Schimbaţi programul astfel încât să reţineţi toţi indecşii pentru

numărul respectiv într-un alt vector pe care să îl afişaţi la final.

8. Generaţi un vector cu 10 numere aleatoare. Creaţi un program care cere un număr

de la tastatură, caută numărul în vector şi afişează un mesaj cu numărul şi poziţia

atunci când acesta a fost găsit. Utilizaţi break sau o variabilă booleană.

9. Creaţi un vector cu n numere aleatoare între 0 şi 99 (n introdus de la tastatură).

Afişaţi vectorul, elementul minim, elementul maxim, suma elementelor, produsul

elementelor diferite de 0, media aritmetică a elementelor. Schimbaţi programul astfel

încât să afişaţi pe ce poziţie se află minimul si maximul.

10. Creaţi un array de String ce conţine iniţializări pentru elemente la declarare.

Afişaţi vectorul.

11. Recreaţi programul anterior pentru a face căutarea în funcţie de numele

persoanei.
12. Creaţi apoi un nou program prin care să alegeţi dacă faceţi căutarea după nume,

notă, sau id.

Exemplu

Values:

Mitchell 99.5 123456

Ortiz 78.5 813225

Luu 95.6 823669

Zimmerman 96.8 307760

Brooks 82.7 827131

ID number to find: 307760

Found in slot 3

Name: Zimmerman

Average: 96.8

ID: 307760

Bucle Imbricate
Buclele imbricate apar atunci când o buclă conţine ca şi instrucţiune o altă buclă.
Exemplu

for (int i = 0; i <4; i++) {

for (int j = 0; j <4; j++) {

System.out.print(i + "," + j+" ");

System.out.println();

Assignment

Rulaţi exemplul anterior. Ce afişează acesta? De câte ori se execută fiecare buclă?

De câte ori se execută afişarea?

Exemplu

public class NestingLoops {

public static void main(String[] args) {

// this is #1 - I'll call it "CN"

for (char c='A'; c <= 'E'; c++) {

for (int n=1; n <= 3; n++) {

System.out.println(c + " " + n);


}

System.out.println("\n");

// this is #2 - I'll call it "AB"

for ( int a=1; a <= 3; a++ ) {

for ( int b=1; b <= 3; b++ ) {

System.out.print( a + "-" + b + " " );

// * You will add a line of code here.

System.out.println("\n");

Assignment

Ce afişează exemplul anterior? La setul #1, care variabilă creşte mai repede?

Schimbaţi pentru setul #1 ordinea buclelor – puneţi bucla din interior în exterior şi
invers. Cum se modifică programul? Pentru setul #2 schimbaţi instrucţiunea print în

println. Ce modificări apar? De ce? Adaugaţi un System.out.println(); dupa finalul

buclei interioare. Ce se intâmplă?

Assignment

1. Scrieţi un program care afişează următorul desen folosind instrucţiunea

“System.out.print(“*”);” şi 2 for-uri.

*****

*****

*****

*****

*****

*****

2. Scrieţi un program care afişează următorul desen folosind instrucţiunea

“System.out.print(“*”);” şi 2 for-uri.

**

***

****
*****

3. Scrieţi un program care afişează tabla înmulţirii cu 10. Exemplu:

* 0 1 2 3 …

0 0 0 0 0

1 0 1 2 3

2 0 2 4 6

3 0 3 6 9

4. Scrieţi un program care citeşte un număr de la tastatură şi afişează tabla înmulţirii

(aşa cum este mai sus) pentru acesta.

5. Scrieţi un program care afişează coordonatele x şi y de pe ecran în felul următor:

(0,0) (0,1) (0,2) (0,3) (0,4) (0,5)

(1,0) (1,1) (1,2) (1,3) (1,4) (1,5)

(2,0) (2,1) (2,2) (2,3) (2,4) (2,5)

(3,0) (3,1) (3,2) (3,3) (3,4) (3,5)


(4,0) (4,1) (4,2) (4,3) (4,4) (4,5)

(5,0) (5,1) (5,2) (5,3) (5,4) (5,5)

6. Generaţi o serie de numere care au suma egală cu 60 şi diferenţa egală cu 14.

7. Generaţi numere între un interval introdus de la tastatură. Pentru fiecare număr,

afişaţi produsul cifrelor.

8. Găsiţi numere care au proprietatea Armstrong. Ex:

 153 = 13 + 53 + 33

Adică numărul este egal cu cubul cifrelor sale adunate.

4LABELS – atribuirea unei denumiri


unor bucle
BuclaMare:
for (int i=0; i<100; i++) {

for (int j=0; j<100; j++) {

if ( condiție ) {

break BuclaMare;

Labeled loops:

outer:

for (int i=0; i<10; i++){

for (int j=0; j<10; j++){

if (conditie1){

break; //iese din bucla mica

if (conditie2){
break outer; //iese din bucla mare

Să se realizeze următorul program: Chestionarul despre calitățile unui candidat la

președinție are 6 secțiuni. Prima secțiune are o întrebare. Cea de a doua: două

întrebări. Cea de a treia: trei întrebări, ș.a.m.d. Intervievatul poate răspunde cu: „DA”,

“NU”, “Nu stiu” sau “Nu raspund”. Programul va genera aleator răspunsul

intervievatului. La o întrebare, șansa să răspundă cu „DA”este de 30%, șansa să

răspundă cu “NU” este tot de 30%, șansa să răspundă cu “Nu stiu” este de 20%, iar

șansa să răspundă cu “Nu raspund” este de 20%.

Dacă intervievatul răspunde cu “Nu stiu” la o intrebare, el va trece la următoarea

secțiune. Daca raspunde cu “Nu raspund”, se încheie interviul.

Să se afiseze Sectiunea numarul, Intrebarea numărul si ce raspunde.

Peste testare la inceput faceti sa raspunda doar cu DA sau NU cu probabilitatea de

50% - 50%.

Sfaturi:

1. Ori de câte ori deschideți o paraneteză sau o acoladă închideți-o imediat,

intrați între paranteze / acolade și tastați Enter și scrieți acolo codul. - pentru a nu

uita să închideți paranteza / acolada.


2. Chiar dacă după if / else / while există doar o singură instrucțiune, puneți totuși

acoladele. S-ar putea ca pe viitor să mai doriți să introduceți o instrucțiune în

acest bloc și veți uita să puneți atunci acoladele.

3. Puteți comenta un mai multe linii de cod odată, selectându-le și apăsând

Control + slash (/). Decomentarea se face tot așa.

4. Puteți face tab la mai multe linii de cod odată, selectându-le și apăsând TAB

(sau Shift+Tab pt stânga).

Numere Aleatoare. Random


Pentru a genera numere aleatoare ne putem folosi de clasa Random. Pentru a folosi

clasa avem nevoie de următorul import:

import java.util.Random;

Apoi avem nevoie de un obiect pe care să îl folosim:

Random r = new Random();

Iar pentru a genera un număr întreg:

int x = r.nextInt(10);

Ceea ce ne va da un numar întreg între 0 şi 9.

Assignment 1

Scrieţi un program care generează 5 numere de la 1 la 10. Modificaţi programul

astfel încât limita superioară să fie introdusă de la tastatură (vom avea numere de la

1 la n). Modificaţi din nou programul astfel încat şi limita inferioară să fie introdusă de

la tastatură (vom avea numere de la m la n).

Assignment 2

1. Scrieţi un program care generează 2 numere între 1 şi 10. Verificaţi dacă numerele

sunt egale sau nu şi afişaţi un mesaj corespunzător.

2. Modificaţi programul de mai sus astfel încât să genereze numere până când găsiţi

2 numere aleatoare egale.


3. Modificaţi programul anterior astfel încât să afişaţi şi câte încercări au fost

necesare până când s-au găsit cele 2 numere egale.

4. Modificaţi programul anterior astfel încât să găsiţi 3 numere aleatoare egale. Ce

observaţi? De câte încercări este nevoie spre deosebire de celălalt program?

5. Scrieţi un program în care să reţineţi 10 mesaje. La rulare, veţi afişa aleator unul

din mesaje.

6. Scrieţi un program pentru a ghici un număr generat aleator. Veţi afişa la început

intervalul între care numărul este ghicit, apoi veţi lăsa utilizatorul să aleagă un număr

până ghiceşte numărul iniţial. De fiecare dată când este introdus un număr,

utilizatorul va trebui să afişeze dacă acesta este mai mic, mai mare sau egal cu

numărul ghicit. La sfârşit, afişaţi şi numărul de încercări.

7. Creaţi un program care să genereze numerele aruncate de 2 zaruri. Afişaţi

numerele şi suma acestora. În cazul în care numerele sunt egale, afişaţi mesajul

“dublu 6”, unde în loc de 6 veţi pune numărul care se află pe ambele zaruri.

8. Modificaţi programul anterior, astfel încât să fie aruncate zarurile de mai multe ori,

iar programul să se oprească când acestea vor avea dubla.

9. Se dă următorul scenariu: Avem 3 cărţi cu faţa în jos, una dintre ele este As.

Celelalte 2 cărţi sunt Dama şi Popa. Fiecare carte va fi stabilită aleator. Utilizatorul

trebuie să ghicească care din cărţi este Asul dintr-o singură încercare. Simulaţi

scenariul în consolă astfel incat sa arate similar cu:

You slide up to Fast Eddie's card table and plop down your cash.

He glances at you out of the corner of his eye and starts shuffling.
He lays down three cards.

Which one is the ace?

# # #

1 2 3> 2

Ha! Fast Eddie wins again! The ace was card number 3.

K Q A

1 2 3

10. Recreaţi scenariul anterior în felul următor: Jocul începe cu o sumă de bani (de

ex.: 1000 dolari). Utilizatorul poate juca de mai multe ori, şi va fi întrebat înainte de

fiecare joc dacă vrea să joace. La fiecare joc acesta va paria o suma de bani. În

cazul în care pierde, nu va primi nimic înapoi. În cazul în care câştigă, acesta va

primi 33% în plus din suma pariată (cu un minim de $1).

11. Modificaţi programul anterior, astfel încât să reţineţi impozite pe câştig de 16%

care vor fi deduse atunci când jucătorul nu mai doreşte să continue.

Break şi Continue
Instrucţiunile break şi continue sunt folosite pentru a modifica flow-ul normal al

programului. Acestea sunt folosite în special la bucle. Spre exemplu break întrerupe
o buclă (cea mai interioară pentru care este aplicat), iar continue sare la iteraţia

următoare din bucla respectivă (cea mai interioară buclă care o găseşte). De exmplu,

avem următoarele cerinţe:

1. Să se adune numere până la întâlnirea lui 0.

2. Să se înmulţească numere şi să se evite înmulţirea cu 0.

public static void main(String[] args) {

int suma = 0;

int produs = 1;

Scanner sc = new Scanner(System.in);

int num;

// 1.

while (true) {

num = sc.nextInt();

if (num == 0) {

break; // opreste la indtroducerea lui 0

}
suma += num;

System.out.println("suma: " + suma);

System.out.println("suma final: " + suma);

// 2.

while (true) {

num = sc.nextInt();

if (num == 0) {

continue; // sare peste inmultirea cu 0

} else if (num == 1) {

break; // oprire la introducerea lui 1

produs *= num;

System.out.println("produs: " + produs);

}
System.out.println("produs final: " + produs);

Assignment
Assignment 1

Rulaţi programul anterior şi observaţi cum funcţionează. Schimbaţi, pe rând, break cu

continue şi continue cu break.

Ce diferenţe apar?

Assignment 2

1. Se citesc numere la infinit. Să se facă suma numerelor citite. Să se oprească

structura repetitivă prin folosirea instrucţiunii break la introducerea unui număr

negativ.

2. Se citesc numere la infinit. Să se afişeze numerele impare. Să se folosească

instrucţiunea break la introducerea lui 0.

3. Se citesc numere. Să se facă produsul numerelor citite, doar dacă sunt pozitive.

Se va afişa produsul la fiecare iteraţie (pas). Să se evite înmulţirea cu 0 prin folosirea

instrucţiunii continue. Să se evite şi înmulţirea cu 1 prin aceeaşi metodă.

4. Se citesc numere. Să se adune numerele negative. Să se evite adunarea cu 0 prin

folosirea instrucţiunii continue. Să se oprească structura repetitivă la introducerea

numărului 7 prin instrucţiunea break. Să se afişeze suma la fiecare pas.


5. Rescrieţi programele anterioare astfel încât să folosiţi variabile booleene în loc de

break şi / sau continue, acolo unde este posibil.

Exemplu - bucla while


Folosind bucla while, să se determine dacă caracterul e există în șirul de caractere

dat.

Varianta 1:

Pe care poate unii dintre voi o preferați. Bucla While care conține un if.

Condiția din while verifică dacă am gasit 'e'-ul. Condiția din if verifică dacă am ajuns

la capătul șirului. Dacă nu ieștim din buclă, index va depăși capătul șirului și vom

avea o eroarea la rulare de tipul StringIndexOutOfBoundsException pe linia

sir.charAt(index).

public class Main {

public static void main(String[] args) {

String sir= "abcdfghij";

int index = 0;

while (sir.charAt(index) != 'e'){

index++;
if (index == sir.length()){

break;

//acum verificam care a fost conditia care ne-a scos din

bucla.

//Am ajuns la capat sau am gasit 'e'-ul ?

if (index != sir.length()){

System.out.println("e se afla la indexul: " + index);

} else {

System.out.println("Returnam -1 pentru ca 'e' nu

exista.");

Varianta 2: while are ambele condiții.


Verificăm totodată atât dacă am gasit caracterul, cât și dacă am depașit capătul

șirului.

public class Main {

public static void main(String[] args) {

String sir= "abcdeghij";

int index = 0;

while ( index != sir.length() && sir.charAt(index) != 'e'

){

index++;

//acum verificam care a fost conditia care ne-a scos din

bucla.

//Am ajuns la capat sau am gasit 'e'-ul?

if (index != sir.length()){

System.out.println("'e' se afla la indexul: " + index);

} else {
System.out.println("Returnam -1 pentru ca 'e' nu

exista.");

Este foarte important ca ordinea să fie aceasta:

while ( index != sir.length() && sir.charAt(index) != 'e' ){...}

și să folosim operatorul && de scurtcircuitare, pentru că dacă index ajunge să

aibă valoarea sir.length() atunci sir.charAt(index) ne va da eroare la rulare:

StringIndexOutOfBoundsException.

Folosind operatorul de scurtcircuitare && nu se mai verifică și condiția a doua,

care oricum nu mai e relevantă dacă am depășit capărul șirului.

Exerciții
Keywords: Switch, For, While, Do…While, Math.random()

Switch
1. Să se realizeze un program în care utilizatorul introduce de la tastatură un număr

între 0 și 6 reprezentând ziua din săptămână, astfel: 0 = Duminică, 1 = Luni … 6 =

Sâmbătă. Folosind switch-ul să se afișeze un mesaj specific zilei din săptămână.

Math.random()

double nrAleator = Math.random();

Math.random() // generează un număr aleator zecimal astfel încât: 0

<= număr generat aleator < 1.

//Dacă vrem să generăm aleator un număr natural între 1 și 6:

int nrAleator = (int)(Math.random()*6 + 1); // facem cast forțat

din double în int. Se pierd zecimalele.

For
2. Să se arunce cu zarul de 5 ori. Să se afișeze suma punctelor obținute în cele 5

aruncări folosind Math.random().

3. Afișați în consolă următorul output:

0 1 2 3 4 5 6 7 8 9

0 1 2 3 4 5 6 7 8 9

0 1 2 3 4 5 6 7 8 9

0 1 2 3 4 5 6 7 8 9

0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9

0 1 2 3 4 5 6 7 8 9

0 1 2 3 4 5 6 7 8 9

0 1 2 3 4 5 6 7 8 9

0 1 2 3 4 5 6 7 8 9

4. Afișați în consolă următorul output:

1 0 0 0 0 0 0 0 0 0

0 1 0 0 0 0 0 0 0 0

0 0 1 0 0 0 0 0 0 0

0 0 0 1 0 0 0 0 0 0

0 0 0 0 1 0 0 0 0 0

0 0 0 0 0 1 0 0 0 0

0 0 0 0 0 0 1 0 0 0

0 0 0 0 0 0 0 1 0 0

0 0 0 0 0 0 0 0 1 0

0 0 0 0 0 0 0 0 0 1
5. Afișați în consolă următorul output:

0 1 0 0 0 0 0 0 0 0

0 0 1 0 0 0 0 0 0 0

0 0 0 1 0 0 0 0 0 0

0 0 0 0 1 0 0 0 0 0

0 0 0 0 0 1 0 0 0 0

0 0 0 0 0 0 1 0 0 0

0 0 0 0 0 0 0 1 0 0

0 0 0 0 0 0 0 0 1 0

0 0 0 0 0 0 0 0 0 1

0 0 0 0 0 0 0 0 0 0

6. Afișați în consolă următorul output:

0 0 0 0 0 0 0 0 0 1

0 0 0 0 0 0 0 0 1 0

0 0 0 0 0 0 0 1 0 0

0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 1 0 0 0 0

0 0 0 0 1 0 0 0 0 0

0 0 0 1 0 0 0 0 0 0

0 0 1 0 0 0 0 0 0 0

0 1 0 0 0 0 0 0 0 0

1 0 0 0 0 0 0 0 0 0

7. Să se realizeze următorul output:

11

222

3333

44444

555555

8. Să se realizeze următorul output:

000000

11111
2222

333

44

Metode
Să presupunem că în flow-ul programului, la un moment dat vrem să executăm un

task foarte specific, bine definit, care se întinde pe mai multe linii de cod, cum ar fi, în

cazul citirii unei variabile de la utilizator sau calcularea unui rezultat folosind niște

operații complexe.

Pentru că este un task bine definit, pentru a ușura citirea codului programului, putem

muta tot acest bloc de linii de cod în altă parte și să îl referim prin apelarea unui

denumiri pe care să i-o dăm. În acest fel codul nostru este mai bine structurat, mai

citeț și poate rula mai eficient.

În cazul în care apare un bug în programul nostru, va fi mult mai ușor de a face

„debug” pentru că separând codurile între ele în funcție de taskurile pe care le

efectuează, codul este foarte ușor de citit.

Ideea generală este ca fiecare task specific să fie bine individualizat.

Există, de asemenea, și situația în care un anumit cod poate fi apelat din zone

diferite ale programului. De exemplu, citirea unei valori de la utilizator poate fi


necesară atât pentru intrarea în contul bancar, cât și pentru aflarea unor numere

prime sau comunicarea numărului de pizza pe care utilizatorul le comandă.

Deși intrarea în contul bancar nu are nimic în comun cu comunicarea numărului de

pizze, este de observat că ambele folosesc aceeși bucată de cod pentru a primi date

de la utilizator.

Astfel au apărut metodele (funcțiile). Acestea sunt blocuri de cod care efectuează

sarcini bine definite. Iată câteva exemple:

 Generarea unui număr aleator

 Citirea unei variabile de la utilizator

 Găsirea tuturor divizorilor unui număr

 etc

și care sunt apelate între ele, sau din metoda public static void main(String[]args)

Funcțiile au următoarea sintaxă:


public static int înregistreazăNumărDeLaUtilizator(){

----------------------------------------

public static void afiseazaInConsola(String str){

----------------------------------------

public static double genereazaNumarAleator() {

----------------------------------------

public static int getInaltime(){

----------------------------------------

}
Sintaxa:

 public – access modifier, modificator de acces care menționează că metoda

poată fi văzută din toate clasele din aplicație.

 static – un keyword pe care il vom intelege în viitorul apropiat

 void / int / double / String / boolean - return-type, adică ce returnează

metoda

 getInaltime() - denumirea metodei. Parantezele sunt obligatorii

 În paranteze putem specifica niste variabile (parametri) care să primească

valoarea unor variabile din metoda care o apelează.

Exemple de apelare:

public static void main (String[] args) {

System.ouyt.println(); //println() e o metodă

int nrAleator = getNumarAleator();

….....str.length():

afiseazaInConsolaNumarAleator(nrAleator);

….........

daDrumulLaRadio();

Metodele care întorc ceva (diferite de void), trebuie să menioneze:


return x; // de exemplu

Exemplu:

//varianta 1:

public static double genereazaNumarAleator() {

double d = Math.random(); //presupunem ca e un calcul

complex.

return d:

//varianta 2:

public static double genereazaNumarAleator() {

//presupunem ca e un calcul complex.

return Math.random();
}

Ceea ce intoarce trebuie să fie de același tip cu key-wordul din definiția metodei:

int/double/String

Alte exemple

public void myFunction () {

….........

public void myFunction(String str) {…..................}

public void myFunction(int myInt) {…..................}

public void myFunction(String str, int myInt) {…..................}

public void myFunction(int myInt , String str) {…..................}

public int myFunction2(String str) {…........}

public String myFunction3(String str) {…........}

private String myFunction4(String str) {…..............}


Recapitulare metode

definirea unei metode

public static return-type denumire (parametrii){

 denumirea + parametrii se numește Semnătura (Signuture-ul) metodei.

Return type-ul poate fi:

 void, int, boolean, String, int[], String[], char[], int[][], int[][][], orice alt obiect,

etc…. double, etc….

 static nu e totdeauna necear. O să învățăm când trebie să îl menționăm, și

când nu.

Apelarea unei metode Dacă returnează ceva, putem scrie

double d = genereazaNumarAleator();

Metoda genereazăNumarAleator() returnează un double, pe care noi îl captăm

(stocăm) în variabila d. Putem scrie doar

genereazaNumarAleator();

dar în cazul acesta nu captăm numărul pe care îl întoarece metoda.

System.out.println(d);
Pe scurt, putem scrie:

System.out.println ( genereazaNumarAleator() );

În felul acesta facem economie de memorie și timp, nu mai definim o variabilă. Chiar

și câteva nanosecunde contează într-o apicație mare.

Cele 4 tipuri de metode

Fără parametrii

1. nu întoarce nimic

public static void nume(){

//diverse operații

//nu returneaza nimic

//observati keywordul void

2. întoarce ceva

public static String nume() {

//diverse operații complexe

String str = "abc";

//alte operatii
return str;

//observati keywordul String din prima linie

//ceea ce returneaza metoda trebuie sa se //potriveasca cu acest

keyword

Cu parametrii

3. nu întoarce nimic

public static void nume(int x){

//diverse operații

//nu returneaza nimic

//observati keywordul void

4. întoarce ceva

public static boolean nume(String s, int i){

..................

if (conditie){
return true;

} else {

return false;

//observati keywordul boolean din prima linie

//ceea ce returneaza metoda trebuie sa se //potriveasca cu acest

keyword

Exerciţii
1. Realizati o metodă care primește un număr și returnează numărul de divizori.

Exemplu: 3 este divizorul lui 30 pentru că 30 se împarte exact la

3.

La această metodă este suficient să parcurgem doar până la radicalul numărului.

2. Realizați o altă metodă care primește un număr și returnează dacă numărul este

prim.

Indiciu: un număr prim are doar 2 divizori (pe 1 și pe el însuși).


Alt indiciu: această metodă poate apela apela prima metodă.

3. Realizați un program care primește de la tastatură un număr și afișează toate

numerele prime până la acest număr.

Indiciu: programul poate apela cea de a doua metodă.

Exercitii extra
1. Să se creeze o metoda public static void sayHello() care afișează „Hello”. Să se

invoce metoda.

2. Să se creeze o metoda public static void sayHello100Times() care afișează

„Hello” de 100 de ori. Să se invoce metoda.

3. Să se creeze o metoda care public static void afiseaza() care afișează primele

100 de numere. Să se invoce metoda.

4. Să se creeze o metoda public static double genereazaNrAleator() care

returneaza un numar generat aleator. Să se invoce metoda. Sa se afiseze numarul.

5. Să se creeze o metoda public static String returneazaDenumireaLunii() care

citeste un numar intre 1-12 de la tastatura si returneaza un string – denumirea lunii.

Să se invoce metoda.

6. Să se creeze o metoda public static void afiseazaNr(int nr) care afiseaza

numarul primit ca parametru. Numarul este introdus de la tastatura. Să se invoce

metoda.
7. Să se creeze o metoda public static int ridicaLaPatrat(int nr) care returnează

patratul unui numar. Numarul este introdus de la tastatura. Să se invoce metoda.

8. Să se creeze o metoda public static int lungimeSir(String sir) care primește un

sir de caractere introdus de la tastatura si returneaza lungimea lui. Să se invoce

metoda. Sa se afiseze lungimea sirului in metoda public static void main(String[]args)

9. Să se creeze o metoda public static boolean isFood() care citește un cuvant de

la tastatura si returneaza true daca e cuvantul = Food și false dacă nu. Să se afiseze

rezultatul.

Tablouri (Arrays)
Un tablou poate conţine mai multe elemente care aparţin aceluiaşi tip de date, spre

deosebire de variabilele simple care pot reţine un singur element. De exemplu,

putem folosi variabile care sunt tablouri pentru a reţine 10 numere întregi.

10, 20, 45, 12, 60, -120, 14

Fiecare element din tablou are asociată o poziţie. Numerotarea poziţiilor începe de la

0 şi merge până la lungimea acestuia -1. De exemplu:

 n = numărul de elemente ale tabloului

 0 = prima poziţie

 n-1 = ultima poziţie


În tabloul prezentat mai sus elementul 10 este pe poziţia 0, elementul 20 pe poziţia 1,

etc… Numărul total de elemente este 7, iar ultimul element, adică 14 este pe poziţia

6.

Un tablou care are o singură dimensiune, cum e cel de mai sus mai poartă

denumirea de tablou unidimensional sau vector. Un tablou cu 2 dimensiuni poartă

denumirea de tablou bidimensional sau matrice.

10 15 43 28

24 35 46 30

18 -100 3 48

Tabloul de mai sus este o matrice cu 4 coloane (column) şi 3 linii (row), adică 4 pe 3.

Putem de asemenea spune că avem un tablou de tablouri, în care fiecare element

este o coloană, nu un singur număr. Fiecare element din acest tablou are de data

aceasta 2 indecşi. Ordinea indecşilor este de obicei stabilită de programator şi

menţinută în cod. Istoric, ordinea folosită a fost aceea inversă coordonatelor

geometrice. Primul index este folosit pentru a indica rândul (axa Y) şi al doilea pentru

a indica linia (axa X). De exemplu, pentru 46 avem coordonatele (1,2). Mai exact,

putem spune că avem următorul vector:

A
B

Unde A este prima linie, B a doua şi C a treia. Deci mai întăi indicăm linia (adică

elementul A B sau C), apoi pentru vectorul obţinut trebuie să indicăm noua poziţie.

Coordonatele pot fi reprezentate ca în partea dreaptă.

A= 10 15 43 28 (0, 0)

(0, 1) (0, 2) (0, 3)

B= 24 35 46 30 (1, 0)

(1, 1) (1, 2) (1, 3)

C= 18 -100 3 48 (2, 0)

(2, 1) (2, 2) (2, 3)

Pentru a folosi un vector în java, vom adăuga un set de [] la declarare şi vom iniţializa

folosind din nou [], împreună cu cuvântul new şi numărul de elemente, ca în exemplu.

Dacă un tablou a fost iniţializat, elementele lui încă nu există, deci trebuie să atribuim

şi acestora valori.
Exemplu

// INITIALIZARI

int[] tablou1 = new int[3];

int tablou2[] = new int[3];

int[] tablou3 = { 5, 86, -123 }; // contine si valorile pentru

elemente

// pentru tablou3 suntem obligati sa folosim aceeasi instructiune

// VALORI ELEMENTE - CITIRE

Scanner s = new Scanner(System.in);

tablou1[0] = s.nextInt();

tablou1[1] = s.nextInt();

tablou1[2] = s.nextInt();

// SAU

for (int i = 0; i <tablou2.length; i++) {

tablou2[i] = s.nextInt();
}

// AFISARE

System.out.println(tablou3[0]);

System.out.println(tablou3[1]);

System.out.println(tablou3[2]);

for (int i = 0; i <tablou2.length; i++) {

System.out.println(tablou3[i]);

Assignment
1.Să se găsească numărul cel mai mare dintr-un Array.

2. Se citește un număr de la tastatură. Să se creeze un array ce conține cifrele

acestui număr.

3. Să se creeze o metodă ce primește un array în care se construiește un alt array

identic cu acesta, în care se introduce o valoarea suplimentară la indexul 3. Nu

uitatați să verificați ca array-ul să nu fie null, pentru ca să nu crape programul.


4. Se citește un număr de la tastatură. Să se construiască un array cu divizorii

numărului.

5. În metoda main() se inițializează un array de 20 de elemente cu numere de tip int

aleatoare între 1 și 10. Se citește un numar de tipul int de la tastatură între 1 și 10.

Să se construiască o metodă care primește arrayul si numărul ca parametrii și

returnează indexul primei apariții în array, sau -1 dacă numărul nu se găseste.

Varianta I: se va folosi bucla while cu un if nested in el, iar if-ul conține break.

Varianta II: se va folosi doar un singur while pentru ambele condiții.

6. Varianta 1: O metodă primește arrayul 4 2 -3 3 1 -2 -5. Ea parcurge arrayul astfel:

începe cu indexul 0. Apoi merge x căsuțe în față sau în spate în funcție de valoarea

din index. Dacă pe indexul 0 scrie 4, merge 4 căsuțe în față. Dacă scrie -3 merge 3

căsuțe înapoi. Se presupune că metoda nu știe câte elemente are array-ul. Să se

afișeze de câte ori face salturi până când iese din array, sau -1 dacă nu iese din

array niciodată.

Varianta 2: se primește arrayul: 4 4 1 3 -2 2 -5

7. Să se sorteze un array (vector) în ordine crescătoare. Practic implementăm noi

metoda Arrays.sort(int[] vectorulMeu) varianta 1: Utilizînd o buclă for parcurgem

vectorul si luam valoarea cea mai mică și o punem pe prima poziție într-un alt vector.

Modificăm valoarea la cea mai mare valoare pe care o poate lua un integer:

Integer.MAX_VALUE Și tot așa.

Varianta 2: folosind metoda bubbling. Se compara valorile din primii doi indexi. Dacă

nu sunt în ordine, se inversează. Apoi se compară indexul 2 cu indexul 3. Dacă nu

sunt în ordine, se inversează. Tot așa până la final. Astfel, valorile urcă, asemenea

bulelor de soda într-un pahar. (bubbling). Se parcurge din nou vectorul de mai multe

ori, până când nu mai au loc inversări. Pentru a ști dacă nu au mai avut loc inversări
folosim o variabilă de tip boolean. Mai multe detalii

pe: http://mathbits.com/MathBits/Java/arrays/Bubble.htm Încercați să rezolvați fără să

vă uitați la răspuns.

Varianta 3: Prin recursivitate. (backtracking) O să facem asta în viitor.

8. codility.com este un site cu probleme de algoritmi (de regulă arrayuri) în care

angajatorii testează candidații. În fie lună se pune câte un test gratuit. Faceți testul

lunii gratuit pe codility.com

Tablouri cu mai multe dimensiuni


În java, putem folosi tablouri cu mai multe dimensiuni. Spre exemplu, atunci când avem 2
dimensiuni putem spune că avem un tablou de tablouri. Pentru a înţelege acest concept,
urmăriţi următoarea schemă:

Practic, fiecare tablou conţine un tablou care conţine elemente. Null ne indică faptul că nu
avem niciun tablou.

Exemplu
// DECLARATII

int[][] t0;

int[] t1[];

int t2[][];

// INITIALIZARI

int[][] t3 = { { 1, 6, 3 }, { 2, 3, 5 } };

// 1 6 3

// 2 3 5

// echivalent cu:

t0 = new int[2][3];

t0[0][0] = 1;

t0[0][1] = 6;

t0[0][2] = 3;

t0[1][0] = 2;
t0[1][1] = 3;

t0[1][2] = 5;

Scanner scanner = new Scanner(System.in);

t1 = new int[2][3];

for (int i = 0; i <t1.length; i++) { // pentru fiecare linie

for (int j = 0; j <t1[i].length; j++) { // pentru fiecare

pozitie de pe linie

t1[i][j] = scanner.nextInt();

// pentru tablouri care nu sunt complete:

t2 = new int[2][]; // 2 linii si numar necunoscut de coloane

t2[0] = new int[4]; // 4 coloane pe prima linie

t2[1] = new int[2]; // 2 coloane pe a doua linie


// initializare ca mai sus: element cu element sau cu for

for (int i = 0; i <t2.length; i++) { // pentru fiecare linie

for (int j = 0; j <t2[i].length; j++) { // pentru fiecare

pozitie de pe linie

t2[i][j] = scanner.nextInt();

// pentru afisare

for (int i = 0; i <t2.length; i++) { // pentru fiecare linie

for (int j = 0; j <t2[i].length; j++) { // pentru fiecare

pozitie de pe linie

System.out.print(t2[i][j] + " ");

System.out.println();

}
for (int[] vector : t1) { // ia vectorul de pe fiecare linie

for (int element : vector) { // ia elementul din vector

System.out.print(element + " ");

System.out.println();

Assignment

1. Să se introducă numere aleatoare într-o matrice de 3 x 4. Să se afişeze.

2. Să se introducă numere aleatoare într-o matrice de n x m, unde n şi m sunt citite

de la tastatură. Să se afişeze.

3. Să se introducă numere aleatoare într-o matrice de 5 x 5. Să se afişeze

elementele de pe prima linie. Să se afişeze elementele de pe ultima coloană. Să se

afişeze elementele de pe diagonala principală şi cele de pe diagonala secundară.

4. Se introduc numere aleatoare într-o matrice de dimensiuni aleatoare (între 1 şi 10

linii şi coloane). Să se afişeze minimul şi maximul din matrice şi poziţiile pe care

acesta se află.

5. Să se introducă numere aleatoare într-o matrice de 4 x 4. Să se facă suma pe

fiecare linie, produsul elementelor pe fiecare coloană, să se afle maximul de pe

diagonala principală şi minimul de pe diagonala secundară.


6. Se dă o matrice cu elemente aleatoare de dimensiuni aleatoare (între 3 şi 10 linii şi

coloane). Să se găsească elementul maxim din matrice. Să se afişeze vecinii

acestuia (afişarea va fi făcută aşa cum apar ei în matrice). Să se ţină cont de cazurile

extreme (când maximul este pe prima sau ultima linie, prima sau ultima coloană, sau

în unul din colţuri).

7. Se consideră catalogul pentru o facultate. Acesta cuprinde materii, studenţi şi câte

o notă pentru fiecare student la fiecare materie. Exemplu:

Maria George Adrian


Programare 5 8 3
Matematică 10 4 9

Pe fiecare coloană se găsesc toate notele pentru un student, iar pe fiecare linie se

găsesc toate notele de la materia respectivă. Să se facă un program care afişează

tabelul de mai sus. Veţi avea nevoie de un vector pentru nume, un vector pentru

materii şi o matrice pentru note. Iniţializaţi cele 3 tablouri din cod.

8. Pentru problema anterioară, să se caute o notă în funcţie de numele studentului şi

materia la care se doreşte nota. Programul să nu fie case-sensitive.

Excepţii
Excepţiile sunt evenimente nedorite care se întâmplă în timp ce programul rulează şi

care perturbă execuţia normală a unui program. De exemplu:

 când vrem să citim un număr şi utilizatorul introduce un şir de caractere

 când apelăm o poziţie prea mare sau prea mică dintr-un vector

 când folosim un obiect care nu a fost iniţializat


Excepţiile se pot trata pentru a evita ca programul nostru să dea erori şi să se

termine sau pentru a trimite utilizatorului un mesaj de eroare mai estetic. Acest lucru

poate fi făcut folosind un try-catch.

Exemplu de excepţii şi modul general de tratarea lor:

public class Exceptii {

public static void main(String[] args) {

// 1)

Scanner s = new Scanner(System.in);

int n;

try {

n = s.nextInt();

// utilizatorul introduce un sir de caractere

} catch (Exception e) {

n = 0; // se foloseste o valoare default

System.out.println("That makes no sense!!! I will use

0.");

}
// 2)

Random rand = null;

try {

n = rand.nextInt();

// rand are valoarea null

} catch (Exception e) {

System.out.println("You probably missed rand!");

// 3)

int[] vect = new int[n];

try {

System.out.println(vect[n]);

// incercam sa folosim o pozitie inexistenta in vector

} catch (Exception e) {

System.out.println("These is no such position as n!");


}

Pentru prima exceptie, nu putem şti ce va introduce utilizatorul, deci prinderea

excepţiei este folosită perfect în acest caz.

Pentru exemplul 2, putem evita folosirea obiectelor cu valoarea null, astfel încât să

nu mai fie aruncate excepţii la rularea programelor. Codul poate fi scris în următorul

fel:

// 2)

rand = null;

if (rand != null) {

n = rand.nextInt();

// rand are valoarea null

Pentru al treilea exemplu, putem evita direct folosirea poziţiilor inexistente în cod.

Sau, dacă nu ne dăm seama de acest lucru, putem face o verificare ca mai sus.

vect = new int[n];


int k = 1000;

if (k <n) {

System.out.println(vect[k]);

Sfaturi:

1. Ori de câte ori deschideți o paraneteză sau o acoladă închideți-o imediat, intrați

între paranteze / acolade și tastați Enter și scrieți acolo codul. - pentru a nu uita să

închideți paranteza / acolada.

2. Chiar dacă după if / else / while există doar o singură instrucțiune, puneți totuși

acoladele. S-ar putea ca pe viitor să mai doriți să introduceți o instrucțiune în acest

bloc și veți uita să puneți atunci acoladele.

3. Puteți comenta mai multe linii de cod odată, selectându-le și apăsând Control +

slash (/). Decomentarea se face tot așa.

4. Puteți face tab la mai multe linii de cod odată, selectându-le și apăsând TAB (sau

Shift+Tab pt stânga).

JVM
JVM = Java Virtual Machine
Java Virtual Machine este un program care simulează procesorul calculatorului.

Aplicațiile java rulează pe JVM ca și cum ar rula pe procesorul calculatorului.

JVM vine în 2 ”flavours” (variante): JDK și JRE.

 JRE – Java Runtime Environment rulează aplicațiile java. Dacă nu avem

instalată java pe calculator și navigăm pe vreo pagină web, întâlnim situația în

care pagina ne roagă să instalăm Java pentru a rula o anumită aplicație. Atunci

se instalează JRE.

 JDK – Java Development Kit – conține tot ce conține JRE, precum și alte tool-

uri (instrumente) pentru dezvoltarea de aplicații java, cum ar fi compilatorul,

împachetatorul etc.

Să presupunem că am scris o clasă public class Main. Ea este, de fapt, scrisă în

fișierul Main.java. Uitați-vă în folderul proiectului pe care l-ați creat. (Acesta se află în

folderul de workspace cu care lucrați în Eclipse). Acolo, în folderul proiectului, veți

găsi 2 subfoldere: src și bin. În subfolderul src veți găsi Main.java.

Denumirea clasei trebuie să fie identică, case-sensitive, cu cea a fișierului.

Compilarea fișierului scris de noi


Pentru a rula un program scris de noi, ele trebuie mai întâin compilat, adică

transformat din text, în instrucțiuni (cod binar) pe care să le înțeleagă calculatorul.

Eclipse face acest lucru automat. El apelează automat

programul javac.exe (compilatorul) din JDK din C:\Program

Files\Java\jdk1.8.0_31\bincare ne compilează fișerul, creând un fișier

binar, Main.class. Eclipse face acest lucru automat, ori de câte ori dăm Save la o

clasă. Eclipse îl salvează în subfolderul bin din aplicația noastră.


Rularea (executarea) programului nostru
Ori de câte ori noi dăm din Eclipse RUN, Eclipse apelează automat java.exe care

rulează Main.class.

Acest fișier binar se execută cu programul java.exe care se află în una din

următoarele locaţii:

 C:\Program Files\Java\jdk1.8.0_31\bin (dacă avem JDK)

 C:\Program Files\Java\jre1.8.0_31\bin (dacă avem doar JRE)

care practic lansează JVM (dacă nu era lansată) și rulează programul nostru

(Main.class).

Traducerea (interpretarea) fișierului binar Main.class într-un fișier pe care să îl poată

înțeleagă sistemul de operare.

Când limbajul Java a fost gândit, el a fost gândit din start atfel încât aplicațiile scrise

în java să poată rula pe orice sistem de operare (platform independent) pentru a veni

în sprijinul programatorilor să nu fie nevoie ca aceeași aplicație să fie scrisă în mai

multe variante pentru fiecare sistem de operare în parte.

JVM a fost astfel creată pentru a putea traduce (interpreta) orice aplicație java în

varianta specifică sistemului de operare.

JVM se poartă ca un simulator de procesor. De fapt, ea intrepretează (traduce)

fișierul Main.class într-un fișier care să poată să îl înțeleagă sistemul de operare.

Astfel, îl traduce într-un .exe pentru Windows (sau într-un executabil pentru Linux,

într-un alt executabil pentru Mac, într-un alt executabil pentru Solaris, etc.).

Împachetarea
Dacă aplicația noastră are mai multe clase, pentru a putea rula, ele trebuie

împachetate într-un singur fișier, un fel de arhivă, care are extensia .jar.

Acest lucru îl face împachetatorul – jar.exe tot din C:\Program

Files\Java\jdk1.8.0_31\bin Avem opțiunea de a include în programulMeu.jar atât

fișierele .class, cât și fișierele cu codul sursă .java.

În plus, se mai crează un fișier în interiorul acestui programulMeu.jar care specifică în

ce clasă adică în ce fișier.class se află metoda pubic static void main (String[] args)

pentru ca JVM să știe de unde să înceapă să ruleze o aplicație.

Eclipse face acest lucru automat când dăm RUN, dacă aplicația noastră are mai

multe clase. Fișierul programulMeu.jar rulează pe java.exe la fel ca Main.class.

Pe scurt:

 JVM (varianta JDK) include atât un compilator, cât și un interpretator.

 Face atât compilare (javac.exe) + împachetare (jar.exe) cât și interpretare

(java.exe)

La rulare
Când rulează un program, JVM încarcă automat (din folderele proprii JRE sau JDK)

toate clasele Java din pachetul java.lang, adică clasele String, System, Object, etc.

Ea nu încarcă toate clasele din Java Standard Edition (Java SE). Nu încarcă automat

clasele precum Scanner, ArrayList. Sunt sute de clase în tot pachetul Java SE și ar

însemna o supraîncărcare degeaba a memoriei calculatorului cu clase care nu sunt

folosite.
În momentul citirii și executării unei aplicații scrise de noi, se încarcă doar clasele

folosite, atunci când se citesc instrucțiunile:

import java.util.Scanner;

import java.util.ArrayList;

Clase. Obiecte
Clasele reprezintă tipuri de date definite de utilizator sau deja existente în sistem. O

clasă poate conține:

 câmpuri (variabile membru, care definesc starea obiectului)

 metode (funcţii membru, ce reprezintă operații asupra stării)

Prin instanțierea unei clase se înțelege crearea unui obiect care corespunde tiparului

definit de clasa respectivă. În cazul general, acest lucru se realizează prin intermediul

cuvântului cheie new.

Un exemplu de clasă predefinită este clasa String. Ea se instanțiază astfel (nu este

necesară utilizarea new):

String s1, s2;

s1 = "Primul meu string";

s2 = "Al doilea string";

Această este varianta preferată pentru instanțierea string-urilor. De remarcat că și

varianta următoare este corectă, dar ineficientă, din motive ce vor fi explicate ulterior.
s = new String("str");

Pentru a crea o clasă nouă, cu funcționalități dorite, vom adăuga în fişierul nostru

câmpuri şi metode.

Câmpuri
Un câmp este un obiect având tipul unei clase sau o variabilă de tip primitiv. Dacă

este un obiect atunci trebuie inițializat înainte de a fi folosit (folosind cuvântul cheie

new).

public class DataOnly {

int i;

float f;

boolean b;

String s;

Declarăm un obiect de tip DataOnly și îl inițializăm:

DataOnly d = new DataOnly();

// setăm câmpul i al obiectului d la valoarea 1


d.i = 1;

// folosim valoarea setată

System.out.println("Câmpul i al obiectului d este " + d.i);

Observăm că pentru a utiliza un câmp/funcție membru dintr-o funcție care nu

aparține clasei respective, folosim sintaxa:

instantaClasei.numeCamp

Metode
O metodă este o acţiune care poate fi executată de clasa noastră. Pentru a crea o

metodă nouă, vom avea nevoie de tipul şi numele acesteia. Exemplu:

public class Matematica {

public void afiseazaSumaPrimelor10() {

int suma = 0;

for (int i = 1; i <= 10; i++) {

suma += i;

System.out.println("Suma calculata in metoda este " + suma);

}
}

Pentru a apela o metodă putem folosi următoarea clasă de test:

public class MatematicaTest {

public static void main(String[] args) {

Matematica mate = new Matematica();

mate.afiseazaSumaPrimelor10();

Exemplul dat mai sus conţine o metodă de tipul void. Aceasta înseamnă că metoda

se execută şi nu întoarce nimic, deci nu ne oferă nicio informaîie. Metodele care ne

oferă informaţii au alt tip înafară de void şi întorc la finalul acestora un răspuns.

Metoda dată ca exemplu mai jos poate fi implementată în clasa Matematica:

public int getSumaPrimelor10() {

int suma = 0;

for (int i = 1; i <= 10; i++) {


suma += i;

return suma;

Iar pentru a folosi metoda, putem avea următoarea implementare în clasa de test:

int suma = mate.getSumaPrimelor10();

System.out.println("Suma returnata de metoda este " + suma);

Unde metoda poate fi apelată ca în primul exemplu sau poate fi folosită oriunde

putem folosi un număr întreg. Dacă metoda are alt tip, aceasta poate fi folosită

oriunde putem folosi o valoare sau o variabilă de tipul respectiv.

System.out.println("Suma returnata de metoda este " +

mate.getSumaPrimelor10());

Metodele pot fi customizate astfel încât să se comporte diferit. Pentru aceasta, e

nevoie de parametrii. O metodă poate avea oricâţi parametrii de orice tip. În schimb,

dacă metoda are prea mulţi parametrii e foarte probabil ca aceasta să poată fie

rescrisă cu mai multe metode. Următoarea metodă va calcula suma primelor numere

naturale, având ca parametru numărul la care să se oprească:

public int getSumaPrimelorNumere(int n) {


int suma = 0;

for (int i = 1; i <= n; i++) {

suma += i;

return suma;

Nu este obligatoriu ca o metodă cu parametrii să returneze ceva. De exemplu, putem

avea următoarea metodă care afişează suma primelor numere:

public void afiseazaSumaPrimelorNumere(int n) {

int suma = 0;

for (int i = 1; i <= n; i++) {

suma += i;

System.out.println("Suma primelor " + n + " numere este " +

suma);

}
Putem folosi şi metode din clasele date ca exemplu în Java. De exemplu pentru

clasa String:

String s1, s2;

s1 = "Primul meu string";

s2 = "Al doilea string";

System.out.println(s1.length());

System.out.println(s2.length());

Va fi afișată lungimea în caractere a șirului respectiv. Se observă că pentru a aplica o

funcție a unui obiect, se folosește sintaxa:

instantaClasei.numeDeFunctie(param1, param2, ..., paramn);

Iar pentru a crea o metodă se foloseşte sintaxa:

public tipReturnat numeMetoda(param1, param2, …, paramn) {

// lista instructiuni

Pentru a utiliza un câmp într-o funcție a clasei respective, folosim direct numele

câmpului, ca în exemplul următor.


Clasa VeterinaryReport este o versiune micșorată a clasei care permite unui

veterinar să țină evidența animalelor tratate, pe categorii (câini/pisici):

public class VeterinaryReport {

int dogs;

int cats;

public int getAnimalsNo() {

return dogs + cats; // utilizare câmp din același obiect

pentru care s-a făcut apelul funcției

public void displayStatistics() {

System.out.println("Total number of animals is " +

getAnimalsNo()); // apel metodă din aceeași clasă

Clasa VeterinaryTest ne permite să testăm funcționalitatea oferită de clasa

anterioară.
public class VeterinaryTest {

public static void main(String[] args) {

VeterinaryReport vr = new VeterinaryReport(); // se creează

un obiect de tipul VeterinaryReport

vr.cats = 99; // se setează valori pentru variabilele membre

ale obiectului vr (obiectul este specificat exlpicit!)

vr.dogs = 199;

vr.displayStatistics(); // se apelează funcția membra a

obiectului creat

System.out.println("The class method sais there are " +

vr.getAnimalsNo() + " animals");

Observație Dacă nu inițializăm valorile câmpurilor explicit, mașina virtuală va seta

toate referințele (la obiecte) la null și tipurile primitive la 0 (pentru tipul boolean la

false).

În Java fișierul trebuie să aibă numele clasei (publice) care e conținută în el. Cel mai

simplu și mai facil din punctul de vedere al organizării codului este de a avea fiecare

clasă în propriul fișier. În cazul nostru, VeterinaryReport.java și VeterinaryTest.java.


Obiectele au fiecare propriul spațiu de date: fiecare câmp are o valoare separată

pentru fiecare obiect creat. Codul următor arată această situație:

public class VeterinaryTest {

public static void main(String[] args) {

VeterinaryReport vr = new VeterinaryReport();

VeterinaryReport vr2 = new VeterinaryReport();

vr.cats = 99;

vr.dogs = 199;

vr2.dogs = 2;

vr.displayStatistics();

System.out.println("The class method says there are " +

vr.getAnimalsNo() + " animals");

Se observă că, deși câmpul dogs aparținând obiectului vr2 a fost actualizat la

valoarea 2, câmpul dogs al obiectului vr1 a rămas la valoarea inițiala (199). Fiecare

obiect are spațiul lui pentru date!

Assignment
Assignment

1. Numere complexe

Creați un proiect nou cu numele NumereComplexe.

Creați clasa NumarComplex.java. Aceasta va avea două campuri: real şi imaginar,

ambele de tip float.

2. Operații

Creați clasa Operatii.java. Această clasă va implementa operațiile de adunare și

înmulțire pentru numere complexe. Definiți în clasa Operatii câte o metodă pentru

fiecare operație. Testați metodele implementate.

3. Biblioteca

Creați un proiect nou cu numele Biblioteca.

Creați clasa Carte.java cu următoarele atribute: titlu, autor, editura, numarPagini.

Creați clasa Test.java, pentru a testa viitoarele funcționalități ale bibliotecii.

Completați această clasă, așa cum considerați necesar. Apoi, creați un obiect de tip

carte și setați atributele introducând date de la tastatură. Pentru această folosiți clasa

Scanner:

Scanner s = new Scanner(System.in);


String titlu = s.nextLine();

Verificați ca numărul de pagini introdus să fie diferit de zero. Puteți consulta

documentația claselor String și Integer.

4. Verificări cărți

Creați o clasă nouă, Verificari, ce va conține două metode, cu câte 2 parametri de

tipul Carte.

Prima metodă va verifica dacă o carte este în dublu exemplar, caz în care va

întoarce adevărat.

A doua metodă va verifica dacă o carte este mai groasă decât altă, și va întoarce

cartea mai groasă.

Testați aceste două metode.

5. Formatare afișare

Afișați informația despre o carte în felul următor: TITLUL_CARTII, Numele_Autorului,

numele_editurii (titlul cărții va fi scris în întregime cu majuscule, iar numele editurii va

fi scris în întregime cu minuscule, numele autorului rămânând neschimbat).


Cuvântul cheie ''this''
Apelurile de funcții membru din interiorul unei funcții aparținând aceleiași clase se fac

direct prin nume. Apelul de funcții aparținând unui alt obiect se face prefixând apelul

de funcție cu numele obiectului (exemplul VeterinaryTest/VeterinaryReport). Situația

este aceeași pentru datele membru.

Totuși, unele funcții pot trimite un parametru cu același nume ca și un câmp membru.

În aceste situații, se folosește cuvântul cheie this pentru dezambiguizare, el prefixând

denumirea câmpului când se dorește utilizarea acestuia. Acest lucru este necesar

pentru că în Java este comportament default ca un nume de parametru să ascundă

numele unui câmp.

Adăugăm clasei VeterinaryTest o metodă care permite setarea numărului de animale

tratate:

public class VeterinaryReport {

int dogs;

int cats;

[...]
public void setAnimalsNo(int cats, int dogs) {

this.dogs = dogs;

this.cats = cats;

}
Constructori
Există uneori restricții de integritate care trebuie îndeplinite pentru crearea unui

obiect. Java permite acest lucru prin existența noțiunii de constructor.

Astfel, la crearea unui obiect al unei clase se apelează automat o funcție numită

constructor. Constructorul are numele clasei, nu returnează explicit un tip anume (nici

măcar void!) și poate avea oricâți parametri.

Crearea unui obiect se face cu sintaxa:

class MyClass {

// ...

în dreapta semnului egal avem apel la constructor

MyClass instanceObject = new MyClass(param_1, param_2, ...,

param_n);

De reținut că, în terminologia POO, obiectul creat în urma apelului unui constructor al

unei clase poartă numele de instanță a clasei respective. Astfel, spunem că

instanceObject reprezintă o instanță a clasei MyClass.


Exemplu

String myFirstString, mySecondString;

myFirstString = new String();

mySecondString = "This is my second string";

Acesta creează întâi un obiect de tip String folosind constructorul fără parametru

(alocă spațiu de memorie și efectuează inițializările specificate în codul

constructorului), iar apoi creează un alt obiect de tip String pe baza unui șir de

caractere constant.

Clasele pe care le-am creat până acum însă nu au avut nici un constructor. În acest

caz, Java crează automat un constructor implicit (în terminologia POO, default

constructor) care face inițializarea câmpurilor neinițializate, astfel:

 referințele la obiecte se inițializează cu null

 variabilele de tip numeric se inițializează cu 0

 variabilele de tip logic (boolean) se inițializează cu false

Pentru a exemplifica acest mecanism, să urmărim exemplul:

class SomeClass {

String name = "Some Class";

public String getName() {


return name;

class Test {

public static void main(String[] args) {

SomeClass instance = new SomeClass();

System.out.println(instance.getName());

La momentul execuției, în consolă se va afișa “Some Class” și nu se va genera nicio

eroare la compilare, deși în clasa SomeClass nu am declarat explicit un constructor

de forma:

public SomeClass() {

// Eventuale initializari

Să analizăm acum un exemplu general:

Student.java
class Student {

String name;

int averageGrade;

// (1) constructor fără parametri

public Student() {

name = "Necunoscut";

averageGrade = 5;

// (2) constructor cu 2 parametri, folosit dacă știm numele și

media

public Student(String n, int avg) {

name = n;

averageGrade = avg;

}
// (3) constructor cu un singur parametru, folosit atunci când

cunoaștem doar numele studentului

public Student(String n) {

this(n, 5); // Astfel, se va apela constructorul (2)

// (4) metoda set pentru câmpul name al clasei Student

public void setName(String n) {

name = n;

averageGrade = 5;

// (5) metoda getter pentru câmpul name

public String getName() {

return name;

}
Declararea unui obiect de tip Student se face astfel:

Student st;

Crearea unui obiect Student se face prin apel la unul din cei 3 constructori de mai

sus:

st = new Student(); // apel al constructorului 1

st = new Student("Gigel", 6); //apel al constructorului 2

st = new Student("Gigel"); //apel al constructorului 3

Atenție! Dacă într-o clasă se definesc doar constructori cu parametri, constructorul

default, fără parametri, nu va mai fi vizibil! Exemplul următor va genera eroare la

compilare:

class Student {

String name;

int averageGrade;

public Student(String n, int avg) {

name = n;

averageGrade = avg;
}

public static void main(String[] args) {

Student s = new Student(); // EROARE: constructorul implicit

este ascuns de constructorul cu parametri

Putem identifica mai multe tipuri de constructori, printre care: fără parametrii (sau

implicit), cu parametrii şi de copiere.

public class Floare {

String culoare;

int petale;

// constructor fara parametrii

public Floare() {

culoare = “”;

petale = 4;
}

// constructor cu parametrii

public Floare(String culoare, int petale) {

this.culoare = culoare;

this.petale = petale;

// constructor de copiere

public Floare(Floare f) { // creaza in memorie un obiect identic

this.nume = f.nume; // in general: this.nume = new

String(f.nume);

this.petale = f.petale;

Assignment
1. Să se implementeze o clasă Punct care să conțină:

 un constructor care să primească cele două numere reale (de tip float) ce

reprezintă coordonatele.

 metodă changeCoords ce primește două numere reale și modifică cele două

coordonate ale punctului.

 funcție de afișare a unui punct astfel: (x, y).

2. Să se implementeze o clasă Poligon cu următoarele:

 un constructor care preia ca parametru un singur număr n (reprezentând

numărul de colțuri al poligonului) și alocă spațiu pentru puncte (un punct

reprezentând o instanță a clasei Punct).

 un constructor care preia ca parametru un vector, cu 2n numere reale

reprezentând colțurile. Acest constructor apelează constructorul de la punctul de

mai sus și completează vectorul de puncte cu cele n instanțe ale clasei Punct

obținute din parametrii primiți.

 La final, afișați coordonatele poligonului utilizând metoda de afișare a clasei

Punct.

3. Să se implementeze o clasă RandomStringGenerator ce generează un String de o

anumită lungime fixată, conținând caractere alese aleator dintr-un alfabet. Această

clasă o să conțină următoarele:

 un constructor care primește lungimea șirului și alfabetul sub formă de String.

Exemplu de utilizare:

myGenerator = new RandomStringGenerator(5, "abcdef");

 metodă next() care va returna un nou String random folosind lungimea și

alfabetul primite de constructor.


 Pentru a construi String-ul, este utilă reprezentarea sub formă de șir de

caractere char[] (char array). Pentru a construi un String pornind de la un char

array procedăm ca în exemplul următor:

char[] a = {'a','b','c'};

String s = new String(a);

 Conversia unui String la char array se face apelând metoda toCharArray() a

String-ului de convertit.

 Pentru a genera valori aleatoare din domeniul [0, N - 1], utilizați:

import java.util.Random;

// ...

Random generator = new Random();

int value = generator.nextInt(N);

4. Reformulare exercițiu 3 Să se creeze o clasă RandomStringGenerator care are 2

câmpuri: lungimea unui șir de caractere și un string ce conține literele alfabetului. Să

se creeze:

 un constructor cu parametrii pentru inițializarea câmpurilor

 o metodă numită next() care să returneze un nou String random folosind

lungimea și alfabetul primite de constructor.

Exemplu:

lungimeSir = 4;

alfabet = "abcdefgh";
Rezultat posibil: dfah

Obiecte
 OOP = Object Oriented Programming

Scurtă istorie a programării


Instrucțiunile pentru un calculator trebuie să fie într-un limbaj pe care acesta îl

înțelege. La nivel de hardware, calculatorul nu este nimic altceva decât milioane de

switchuri care sunt on/off, 1/0, plus/minus sau deschis/inchis (cum vreți voi). 1 este

reprezentat pentru on, iar 0 pentru off.

În primii ani ai computerul, în anii 40, un panel de swithcuri era tot ce se introducea

ca instrucțune. Acesta este un limbaj timpuriu de de programare și codul se numește

binar.

Limba vorbită este mult mai complexă decât cea a unui computer. În timp ce creierul

uman înțelege propoziții, computerul nu înțelege decât starea unor swithuri. Pentru

că primele calculatoare nu includeau limbaje, programatorii trebuiau să introducă

coduri (instrucțuini) în format binar, zerouri și unu.

Dar este foaret dificil să scrii instrucțuni în binar. Limbajele de programare, au fost

create ca punte între limbajul binar și cuvinte. Limbajele timpurii foloseau cuvinte

pentru a reprezenta un set de instrucțiuni binare pe care computerul să le înțeleagă.

Aceste cuvinte se numesc mnemonice. Aceste prime limbaje se numesc machine-

languages sau assembler languages.

Un program software traducea assembly language în limbaj binar. Aceste programe

de traducere se numesc assemblers.


Mai multă lume a început să folosească acum calculatoarele, inclusiv companiile. Dar

și limbajele mașină s-au dovedit greu de utilizat, pentru că forțau programatorul să

gândească ca un computer, să separe fiecare task într-o lungă listă de instrucțiuni.

Curând au fost create primele limbaje evoluate, pentru a scrie programe de calculator

repede și eficient pentru a rezolva probleme din lumea reală. Aceste limbaje au fost

dezvoltate pentru ca programatorii să nu trebuiască să își facă griji despre pașii pe

care un computer trebuie să le facă pentru a executa un program. Acest lucru le-a

permis să se focuseze pe design și construirea aplicației. Primele astfel de limbaje

evoluate sunt:

 FORTRAN (Formula Translator)

 COBOL (Common Business Oriented Language).

 Alte limbaje au apărut ulterior: Logo, Pascal.

Aceste limbaje se numesc procedurale. Ele folosesc un approach liniar, cu un singur

punct de intrare în program și un singur punct de exit pentru a rezolva o problemă.

Programul definește toate datele și toate instrucțiunile pentru a procesa aceste date.

Instrucțiunile se desfășoară în ordinea în care sunt scrise în program. Această

metodologie necesită multă muncă de mantenență. Orice modificare în mărimea,

tipul sau locația datelor necesită modificarea programului.

Funțiile au fost introduse acum pentru că programatorii au realizat că un bloc de cod

paote fi apelat de mai multe ori: pentru a scrie fișiere pe hard disk, trimite mesaje

altor coputere, calcularea unor probleme de matematică. Acesta au apărut în diverse

biblioteci. Programatorii puteau folosi aceste funcții în aplicațiile lor. Acest lucru a

redus timpul necesar scrierii programelor. Aceste biblioteci se

numesc API (Application Program Interfaces).


Totuși, și aceste limbaje evoluate obligau programatorul să gândească ca un

computer. Programatorii trebuiau să separe problemele în mulți pași pe care

computerul să îi efectueze. Trebuiau să descrie un program în termenii cum să le

rezolve coimputerul.

Programarea orientată pe obiecte - Object Oriented


Programming (OOP)
Programarea oientată pe obiecte este diferită de cea procedurală. În timp ce în

programarea procedurală taskurile sunt descrise ca o secvență logică, în OOP

taskurile ce trebie efectuate sunt descrise în obiecte. Obiectele sunt create și stocate

în memoria computerului. Fiecare obiect conține datele și instrucțiunile pentru

efectuarea taskurilor asupra acestor date. Când un program rulează, sistemul de

operare încarcă obiectele și efectuează operații asupra lor când este instruit.

Programul este văzut ca o colecție de obiecte care interacționează unele cu altele.

Avantajele OOP:

 Obiectele fiind mici, pot rula pe deviceuri mici la viteze mari

 Obiectele deja definite pot fi refolosite de alti programatori în alte aplicații,

scăzând costurile

În limbajele procedurale, procedurile sunt reprezentate ca o rețea de rutine (funcții,

proceduri) care se apelează unele pe altele ca într-un call tree. De asemenea,

structurile de date și instrucțiunile asupra lor (comportamentele) sunt slab conectate.

Limbajul OOP: o colecție de obiecte discrete care incorporează structuri de date și

comportamente, strâns legate.

Un obiect conține variabile și metode. Obiectele se construiesc pe baza unui clase.

Clasa este matrița de construire de obiecte.


De exemplu, avem clasa Student.

Student student1 = new Student();

Student student2 = new Student();

Am construit 2 obiecte de tip Student.

Keywordul new este folsit pentru a construi un nou obiect.

Variabilele student1 și student2 sunt stocate pe STACK. Ele conțin informații

referitore la adresele în memorie unde se găsesc obiectele create.

Toate obiectele se stochează pe HEAP.

public class Student {

// ATRIBUTELE CLASEI - se mai numesc fields, câmpuri,

attributes sau instance variables / variabile de instanță


private String nume;

private int varsta;

private int nota;

// CONSTRUCTOR

public Student() {

// CONSTRUCTOR

public Student(String nume, int varsta, int nota) {

this.nume = nume;

this.varsta = varsta;

this.nota = nota;

}
// ACCESSORS / GETTERS

public String getNume() {

return nume;

// MUTATORS / SETTERS

public void setNume(String nume) {

this.nume = nume;

public int getVarsta(){

return varsta;

public void setVarsta(int varsta){

this.varsta = varsta;
}

public int getNota(){

return nota;

public void setNota(int nota){

if (nota>=1 && nota<=10){

this.nota = nota;

//....alte metode

Atributele sunt variabile care stochează informațiile despre obiectul respectiv.

Atributele se trec de regulă private, pentru a nu putea fi modificate de alte


obiecte/clase. Modificarea atributelor se face cu metodele mutators (setteri). Aceștia

conțin o validare a datelor primite. În exemplul, nostru, dacă nota nu se încadreză

între 1-10, nu se face modificarea atributului notă.

Metodele accessors (getteri) returneaza informația stocată în atribute.

 Atributele private se văd numai din interiorul clasei.

 Atributele cu fără access specifier (default) se văd numai din clasele care sunt

în același pachet (package).

 Atributele protected se văd din clasele din același pachet și din clasele care

extind respectiva clasă din alte pachete.

 Atributele public se văd de peste tot.

Mai multe despre package și extindere imediat.

Constructorul este metoda care construiește un nou obiect din clasa respectivă. El

trebuie să poarte același nume cu clasa și cu fișierul – obligatoriu! Dacă clasa se

numește public class Student, fișierul trebuie să se numească tot Student.java, iar

constructorul: public Student(). Constructorul este tot o metdodă, dar observați că nu

are return-type.
Chiar dacă noi nu specificăm un constructor, compilerul va pune el în codul binar

constructorul default:

public Student(){

Cu constructorul default se construiesc obiecte a căror atribute pot să nu fie

inițializate.

Dacă în medtoda main() scriem Student student1 = new Student(); vom avea un

obiect de tip Student creat, dar el nu are nici numele, nici nota și nici vârsta setate.

Dacă vrem să inițializăm anumite atribute, trebuie să folosim un alt constructor, pe

care să îl scriem:

// CONSTRUCTOR

public Student(String nume, int varsta, int nota) {

this.nume = nume;

this.varsta = varsta;

this.nota = nota;

Apoi, în metoda main() putem scrie:


Student student1 = new Student(”Ionel”, 20, 9);

Student student2 = new Student(”George”, 21, 8);

Am creat 2 obiecte de tip Student și am și inițializat atributele.

 this se referă la această instanță, la acest obiect.

this.nume = nume;

 this.nume este atribut al clasei (= variabilă de instanță) – nume.

 nume este o variabilă locală în constructor, primită ca parametru.

O dată creat obiectul putem apela atribute și metode din el. În principiu, am putea

scrie următorul cod:

student1.nume = "Ghoeorghita";

System.out.println(student1.nume);

Numai că noi am declarat variabila (atributul) nume ca având access specifier

private. Deci, nu îl putem apela din altă clasă. Declarându-l private am făcut ceea ce

se numește încapsulare, adică să nu poată fi modificat direct din exteriorul clasei

pentru că adesea este nevoie să facem niște validări. De exemplu, ce facem dacă se

pasează stringul „sfd#$%&4456gfsg” ca nume? Noi trebuie să verificăm că sunt

numai litere. De aceea avem metoda setNume() pe care o facem publică și în care

punem și validarea numelui primit.

Vom apela atunci setterul și getterul dacă vrem să modificăm sau să afişăm numele:
student1.setNume(”John”);

System.out.println(student1.getNume());

public class Student {

// ATRIBUTELE CLASEI

private String nume;

private int varsta;

private int nota = 10;

// CONSTRUCTOR

public Student(String nume, int varsta, int nota) {

this.nume = nume;

this.varsta = varsta;

this.nota = nota;

}
....getter, setteri, alte metode

În metoda main() avem:

Student student1 = new Student(”Ionel”, 20, 9);

Student student2 = new Student(”George”, 21, 8);

Dacă nu scriem în cod constructorul default, dar specificăm altul, compilerul nu îl va

mai adăuga pe cel default (fără parametri).

Pașii de creare a unui obiect:


1. Declararea variabilei de referință (student1) pentru obiect pe STACK (RAM).

Se inițializează un request pentru alocarea de memorie în HEAP (RAM).

2. Default initialization. Se setează valorile default pentru atribute în funcție de

tipul acestora, astfel: nume = null, varsta = 0, nota = 0

3. Explicit initialization of values. nota = 10.

4. Executarea constructorului: nume = ”Ionel”, varsta = 20, nota = 9.

5. Asignarea adresei obiectului din HEAP în variabila de referință student1 din

STACK.

Atributele există atâta vreme cât obiectul există. Dacă obiectul nu mai are referințe

(variabile de pe STACK care să pointeze către el, va fi șters din memorie de către

garbage collector).

Orice variabilă are următoarele proprietăți: int nr = 0;


1. int – data type

2. nr – identifier (denumire)

3. 0 – valoarea

4. o adresă în memorie pe care noi nu o știm

5. scope/life of the variable. Variablilele locale (din metode există numai atâta

vreme cât se execută metoda). Variabilele de tip instanță sau statice (de tip clasă)

– există atâta timp cât există obiectul.

Obiectul există atâta timp cât există variabile pe STACK care pointează către el.

De exmplu:

Student student1 = new Student(”Ionel”, 20, 9);

Student student2 = new Student(”George”, 21, 8);

student2 = student1;

student2 acum pointeză către același obiect ca și student1. Studnetul cu numele Ionel

nu mai are nici o variabilă de pe stack care să pointeze către el și va fi șters cu

garbage collector.
Life cycle of an object
Obiectul este creat cu keywordul new care invocă constructorul. Obiectul există în

memoria RAM a calculatorului (pe HEAP) atâta vreme cât există variabile care să

aibă referință către el. Dacă nu mai există nici o referință către el, obiectul este la

latitudinea garbage collector-ului. Acesta este un program care rulează din când în

când pentru a șterge obiectele fără referință și face rost de memorie. În programele

mici e posibil ca el să nu ruleze niciodată. În aplicațiile mari, va rula când consideră

necesar. Noi nu îi putem spune când să ruleze, putem doar cel mult să îi

recomandăm să ruleze, prin comanda:

System.gc();

Un obiect fără referințe nu mai poate fi recuperat, pentru că nu mai ai cum să faci

referire la el.
În alte limbaje, cum ar fi C++, programatorul trebuie să aibă grijă să distrugă

obiectele de care nu mai are nevoie, altfel apar memory leaks și programme failure.

În Java, de memorie se ocupă JVM (Java Virtual Machine), care ruleză garbage

collector ori de câte ori consideră necesar.

static
Un obiect poate avea și un alt tip de atribute (variabile) și metode: statice. Atributele

statice se mai numesc static fields sau class fields. Dacă în exemplul nostru, atributul

nume putea lua valori diferite pentru fiecare obiect în parte, atributele statice vor avea

aceeași valoare pentru toate obiectele de tip Student.

public class Student {

private String nume;

private int varsta;

private int nota;

private static String grupa = “grupa 2”;

//........ constructor, metode

}
Dacă modificăm valoarea atributului static la un obiect, ea se modifică la toate

obiectele.

Atributele și metodele statice sunt stocate pe o zonă specială de memorie care se

numește static storage.

Dacă avem:

Student student1 = new Student(”Ionel”, 8, 21, “grupa 2”);

Student student2 = new Student(”Geroge”, 9, 20, “grupa 2”);

Și facem:

student1.setGrupa(”grupa 1”);

System.out.println(student2.getGrupa());

Vom constata că s-a modificat și grupa la student2.

Deși atributele statice se pot apela și așa, oficial ele se apelează așa:
Student.setGrupa(”grupa 1”);

System.out.println(Student.getGrupa());

Se folosește numele clasei. Pentru că nu contează despre ce obiect este vorba.

Metodele setGrupa() și getGrupa() trebuie și ele definite ca statice ca să le apelăm

așa, cu Student.getGrupa() !!!!

1. O metodă statică nu poate apela un atribut de instanță (de obiect, nestatic)

sau o metodă de instanță (de obiect, nestatică) – pentru că pur și simplu nu știe

despre ce obiect este vorba.

2. O metodă ne-statică (adică de instanță) poate apela atribute statice, precum și

metode statice.

Putem pune metoda main() în interiorul clasei Student. Dar, atentie, nu putem apela

direct nici un atribut sau metoda din clasă, pentru că sunt de instanță, iar noi ne

aflăm într-un context static. Va trebui să facem un obiect pentru a le apela.

public class Student {

private String nume;

public Student(String nume) {

this.nume = nume;

}
public void setNume(String nume){

this.nume = nume;

public String getNume() {

return nume;

public static void main(String [] args){

nume = “Ionel”; // nu se poate apela, care

variabilă nume? A cărui obiect?

setNume(“Ionel”); // nu se poate apela. Metoda

setNume() de la care obiect?

// suntem in context static. Nu

putem apela atribute și metode nestatice

//trebuie să facem un obiect:

Student student1 = new Student(”Ionel”);

student1.setNume(“Gigel”);
// aici am puteam face si direct

student1.nume = “Gigel”;

//pentru că suntem în interiorul clasei și se văd

atributele private.

//dar nu este recomandat,

//pentru că sărim validarea pe care o putem implementa

in setNume()

Nu e obligatoriu ca studentului Ionel să îi atriubuim o variabilă de referință. Putem

face așa:

System.out.println(new Student(”Ionel”).getNume());

Singurul dezavantaj e că o dată trecut la linia următoare, nu mai avem cum să facem

referire la acest obiect. El există în memorie, undeva, dar nu mai putem să îl folosim,

căci nu mai avem nici o referință la el.


ClasaSystem

Aici putem observa obiectele in și out pe care le-am folosit. După cum vedeți, ele

sunt obiecte statice. Se apelează cu System.in și System.out

in e un obiect de tipul InputStream, iar out este un obiect de tipul PrintStream.

Dacă dați click pe PrintStream, veți putea observa metoda println() pe care o folosim

frecvent: System.out.println().
În clasa System mai putem observa câteva metode:

System.arraycopy(………..) - copiere de arrayuri. Pentru copiere arrayuri mai bine

folosim multele metode copyOf(…) din clasa Arrays, care sunt tot

statice: Arrays.copyOf(…….)

 System.currentTimeMilis() - returnează data în milisecunde începând cu 1 ian

1970

 System.gc() - recomandăm lui JVM să ruleze garbace collectorul

 System.exit() - se încheie aplicația.

După cum vedem, toate sunt metode statice. Se apelează folosind denumirea

clasei: System.metodă(). Nu este nevoie să creăm cun obiect pentru a folosi aceste

metode statice.
Obiecte
O aplicație OOP = colecție de obiecte distincte care incorporează date și

comportamente

Limbajele OOP = descriu taskuri pentru obiecte

Exemple de obiecte:

 clienți

 conturi bancare

 animale

 obiecte care comunică cu baza de date,

 obiecte care primesc requesturi de tip HTTP

 obiecte care returnează răspunsuri de tip HTTP

 obiecte care scriu fișiere, obiecte care citesc fișiere

 obiecte de tip service care manipulează alte obiecte (de tip domain: animale,

clienți, conturi bancare)

Clasa = un șablon pentru crearea de obiecte de un anumit tip.

Cu excepția primitivelor, toate în java sunt obiecte. Metoda public static void main

(String []args) este şi ea o metoda a unui obiect – al obiectului generic al clasei în

care se află.

Clasa String este și ea un șablon care construiește obiecte de tip String. Este o clasă

puțin aparte, în sensul că poate crea obiecte atât pe Heap cât și pe String pool.

Pentru a înţelege detaliile legate de String pool urmăriţi următorul exemplu:


String s1 = ”Maria”; //JVM caută obiectul în String pool. Dacă nu

îl găsește îl crează.

String s2 = “Maria”; //JVM caută obiectul în String pool. L-a

găsit, nu îl mai crează.

String s3 = new String(“Maria”); //JVM crează un noi obiect pe

Heap

String s4 = new String(“Maria”); //JVM crează un noi obiect pe

Heap

String pool are rostul de a face economie de memorie.

System.out.println( s1 == s2 ); // au aceeași adresă, afișează

true

System.out.println( s3 == s4 ); // au adrese diferite,

afișează false

System.out.println( s1 == s3 ); // au adrese diferite,

afișează false
Și noi o să impelentăm această metodă pentru ca să verifice egalitatea a două

obiecte în funcție de datele pe care le stochează. Metoda .equals(Object object)

primește un obiect generic, căruia trebuie să îi facem cast la obiectul cu care îl

comparăm.

Mai sus vedem că Object mai are o metodă care se moștenește la toate

obiectele public String toString() - care returnează un String.

Când suprascriem metoda şi o apelăm în main(), de obicei aceasta ne va da toate

datele obiectului respectiv. Aceasta ne ajută pentru a nu mai scrie fiecare câmp în

parte apelat.
Obiectele se constuiesc utilizând keywordul new. Acesta apelează constructorul din

clasa respectivă.

Constructorul este o metodă specială care nu are return-type (nici void, din nimic). El

trebuie să poarte denumirea clasei, și implicit al fișierului.

Pot exista mai mulți constructori într-o clasă, care să difere între ei prin numărul, tipul

și ordinea parametrilor pe care îi primesc. Nu pot exista 2 constructori identici –

trebuie să difere ceva: fie numărul parametrilor, fie tipul lor, fie ordinea lor.

Computer computer = new Computer();

Computer computer2 = new Computer(String marca, int procesor, int

ram);

iar în clasa Computer:

public class Computer {

String marca;

int procesor;

int ram;

public Constructor() {

//constructorul default
}

public Constructor(String marca, int procesor, int ram) {

this.marca = marca;

this.procesor = procesor;

this.ram = ram;

Unde this face referire la atributul clasei.

Dacă nu introducem nici un constructor, compilatorul va introduce constructorul

default. Dacă introducem un alt constructor, compilatorul nu va mai introduce

constructorul default, iar dacă noi vom construi un obiect cu acest constructor fără să

îl definim manual vom avea o eroare.


Asociere între 2 obiecte

Cont cont1 = new Cont(“argghwsd”, 'r', 0.0) ;

Client c3 = new Client (”Ionel Popescu”, 1990101123456L, cont1);

Client c3 = new Client (”Ionel Popescu”,

1990101123456L,

new Cont(“argghwsd”, 'r', 0.0) ) ;

Cum apelăm soldul?

c3.cont.sold = 24.87; //dacă atributele sunt public, ceea ce nu

e de dorit.

c3.getCont().setSold(24.87);
Există mai multe tipuri de asocieri, le vom discuta în viitor.

Pachete
Uneori, în aplicațiile foarte mari putem avea aceeași denumire pentru mai multe

clase. Pentru a evita vreo confuzie, dar și pentru a avea o ordine a claselor noastre,

vom pune clasele în pachete diferite.

De exemplu, clasele Client și Cont sunt obiecte clasice cu care lucrăm. Le putem

pune într-un pachet comun pe care să îl numim domain (adică domenii).

Clasa Banca este o clasă operațională. O putem pune într-un pachet care să se

numeasacă client (în engleză).

package domain; // prima linie într-o clasă

public class Cont {......}

Clasele care nu sunt în același packet nu se văd unele pe altele și trebuie importate,

așa cum am importat și clasa Scanner, chiar dacă are access-specifierpublic.

Într-adevăr, aparent nu are logică, dar ideea este că adesea (chiar foarte des) o

aplicație are biblioteci uriașe cu mii de clase atașate în aplicație. Pentru a face

economie de memorie, JVM nu încarcă toate aceste clase în memorie. S-ar umple

memoria cu clase pe care nu le folosește. De aceea, scriind import, JVM va încărca

în memorie doar clasele cu care va lucra.

În clasa Bancă vom scrie:

package client; // prima linie într-o clasă


import java.util.Scanner;

import domain.Client; //clasa clientului nostru de bancă

import domain.Cont;

public class Banca {

public static void main (String [] args) {

........

}
Exerciţii clase şi obiecte
1. Scoala
Realizati un program:

Clasa Scoala

- denumire

- adresa

- nrElevi

- nrSali

- profesor1

- profesor2

- profesor3

- profesor4

- director

Clasa Profesor

- numele

- materia

Clasa Adresa

- strada

- numarul

- orasul

Nu setati pe profesor4.

Directorul este si el un profesor dar nu preda nici o materie.

Cerinţe:

1. Afisati numele scolii


2. Afisati numarul de elevi al scolii

3. Afisati numarul de sali al scolii

4. Afisati strada pe care se afla scoala Geo Bogza

5. Afisati numele primului profesor al scolii

6. Materia profesor2 al scolii

7. Numele profesorului 4 al scolii – de ce da eroare?

8. Afisam materia pe care o preda directorul scolii;

9. Afisam numele profesorului de matematica al scolii

Realizati schema variabilelor si a obiectelor pe stack si pe heap.

2. My Mall
Clasa Mall

- denumire

- nrEtaje = 2 default

- nrMagazine = 150 default

- Administrator

- Cinema

Clasa Administrator

- denumire

Clasa Cinema

- denumire

- nrSali

Cerinţe:

1. Creati 2 obiecte de tip Mall care vor avea acelasi adminsitrator.

2. Setati denumirea administratorului primului mall.


3. Care este denumirea administratorului celui de al doilea mall?

4. Pe cate cai se poate ajunge la denumirea administratorului? Afisati denumirea

administratorului folosind toate caile posibile.

5. Pe cate cai se poate ajunge la denumirea cinema-ului de la promenada? Afisati

denumirea cinemaului din promenada folosind toate caile posibile.

6. Pe cate cai se poate ajunge la denumirea cinemaului de la Afi? Afisati denumirea

cinamaului din afi folosind toate caile posibile.

7. Realizati schema variabilelor si a obiectelor pe stack si pe heap.

3. Agentii de turism
Realizati un program în care vor exista 2 agentii de turism. Numiți-le cum vreți voi.

Prima agenție de turism oferă 2 sejururi, iar cea de a doua – 3.

Există 3 hotele: Hotel 1 – Perla, Hotel 2 – Aeolis, Hotel 3 - Acrotiris

Agentia 1:

Sejurul 1 (circuit)

 Hotel 1 Perla 1 noapte

 Hotel 2 Aeolis 2 nopți

 Hotel 3 Acrotiris 1 noapte

Sejurul 2: Hotel 2 Aeolis 7 nopţi

Sejurul 3: Hotel 3 Acrotiris 7 nopți

Agentia 2:

Sejurul 4 (circuit):

 Hotel 1 Perla 1 noapte


 Hotel 2 Aeolis 2 nopți

 Hotel 3 Acrotiris 1 noapte

Sejurul 5: Hotel 1 Perla 7 nopți

Preturi hoteluri:

Hotel 1 : 50euro/noapte

Hotel 2: 40 euro / noapte

Hotel 3: 60 euro/noapte.

Sa se afiseze cat costa fiecare sejur.

Realizati schema variabilelor si a obiectelor pe stack si pe heap.

4. Agentii de turism II
Cerinţe:

1. Realizaţi un program în care o agenție de turism prezintă clientului mai multe

sejururi posibile, prestabilite.

2. Agenția colaborează cu 10 hoteluri care au prețurile fixe.

3. Programul va afișa clientului toate datele sejurului.

4. Folosiți constructorii.

5. Folosiţi array-uri.

6. Clientul poate alege un sejur prestabilit sau își poate configura sejurul.

7. Când clientul își va configura sejurul, veți crea un obiect de tip Sejur.

5. Echipe
Scop: Crearea unui proiect care să reflecte una sau mai multe echipe sportive.

1. Creați un proiect nou în Eclipse numit MyTeam;

2. Creați clasele Team, Player, BasketballTeam, FootballTeam și Test;


3. Clasa Player conține câmpurile name, age și number ce vor fi inițializate în

constructor. De asemenea, mai conține și metoda toString care afișează numele,

vârsta și numărul jucătorului.

4. Clasa Team conține un vector de jucători (adică un vector de Player, numit

players) și un număr ce reprezintă numărul de jucători (noPlayers).

Creaţi în clasa Team:

5. un constructor cu un singur parametru ce realizează inițializarea variabilei

noPlayers și a vectorului players;

6. un constructor cu 2 parametrii (un vector de String-uri de dimensiune noPlayers

numit names și numărul de jucători noPlayers).

7. Apelați în acest constructor constructorul cu un singur parametru creat anterior. De

ce se dorește acest lucru?

8. Tot în acest constructor inițializați elementele vectorul players. Folosiți-vă de

vectorul names pentru nume și generați vârsta și numărul random.

9. Clasele BasketballTeam și FootballTeam conțin câte o metodă ce afișează numele

unei echipe de baschet, respectiv de fotbal. Metodele se pot numi

printBasketballName, respectiv printFootballName.

10. Realizați modificările necesare astfel încât clasele BasketballTeam și

FootballTeam să moștenească elementele clasei Team.

11. În clasa Test creați 2 echipe, una de fotbal și una de baschet. Alișați la fiecare

numele echipei și jucătorii.

12. Realizați modificările necesare astfel încât numele echipei pentru clasele

BasketballTeam și FootballTeam să fie introdus de la consolă.


Agregare și Compunere
Agregarea și compunerea se referă la prezența unei referințe pentru un obiect într-o

altă clasă. Acea clasă practic va refolosi codul din clasa corespunzatoare obiectului.

Exemplu:

class Page {

private String content;

public int no;

public Page(String c, int no) {

this.content = c;

this.no = no;

class Book {

private String title; // compunere - trebuie

sa existe

private Page[] pages; // compunere - trebuie

sa existe
private LibraryRow libraryRow = null; // agregare - poate

lipsi

public Book(int dim, String title, LibraryRow libraryRow) {

this.libraryRow = libraryRow;

this.title = title;

pages = new Page[dim];

for (int i=0; i <dim; i++)

pages[i] = new Page("Pagina " + i, i);

class LibraryRow {

private String rowName = null; // agregare

public LibraryRow(String rowName){

this.rowName = rowName;

}
}

class Library {

// members: array of library rows

public void addRow(LibraryRow row) {...}

public static void main(String args[]) {

LibraryRow row = new LibraryRow("a1");

Book book = new Book(100, "title", row);

book = null;

Agregarea (aggregation) - obiectul-container (Cartea) poate exista și în absența

obiectelor agregate (Raftul). În exemplul de mai sus, un raft de bibliotecă poate

exista și fără cărți.


Compunerea (composition) - este o agregare strong, indicând că existența unui

obiect este dependentă de un alt obiect. În exemplul de mai sus, o carte nu poate

exista fără pagini.

Moștenire (Inheritance)
Numită și derivare, moștenirea este un mecanism de refolosire a codului specific

limbajelor orientate obiect și reprezintă posibilitatea de a defini o clasă care extinde o

altă clasă deja existentă. Ideea de bază este de a prelua funcționalitatea existentă

într-o clasă și de a completa sau de a o modela.

Clasa existentă este numită clasa-părinte, clasa de bază sau super-clasă. Clasa care

extinde clasa-părinte se numește clasa-copil (child), clasa derivatăsau sub-clasă.

Pentru detalii și implementare în Java vă rugăm să citiți un document pe acest

subiect.

class Animal {

public void eat() {

System.out.println("Animal eating");

class Wolf extends Animal {

public void howl() {

System.out.println("Wolf howling");
}

public void eat() {

System.out.println("Wolf eating");

class Snake extends Animal {

public void bite() {

System.out.println("Snake biting");

class Test {

public static void main(String[] args) {

Wolf wolf = new Wolf();

Snake snake = new Snake();


wolf.howl();

snake.bite();

snake.eat();

wolf.eat();

Agregare vs. moștenire


Diferența dintre moștenire și agregare este de fapt diferența dintre cele 2 tipuri de

relații majore prezente între obiectele unei aplicații :

 is a- indică faptul că o clasă este derivată dintr-o clasă de bază (intuitiv, dacă

avem o clasă Animal și o clasă Caine, atunci ar fi normal să avem Caine derivat

din Animal, cu alte cuvinte Caine is an Animal)

 has a - indică faptul că o clasă-container are o clasă conținută în ea (intuitiv,

dacă avem o clasă Masina și o clasă Motor, atunci ar fi normal să avem Motor

referit în cadrul Masina, cu alte cuvinte Masina has a Motor)

Super
super este un cuvânt cheie, asemănător cu this. Singura diferenţă este că super este

folosit atunci când ne referim la clasa părinte. De exemplu, putem să apelăm un

constructor din clasa părinte sau o metodă folosind super în următorul fel:
class Animal {

int age;

public Animal(int age) {

this.age = age;

public void eat() {

System.out.println("Animal eating");

class Wolf extends Animal {

public Wolf(int age) {

super(age);

}
public void eat() {

super.eat();

System.out.println("Wolf eating");

Assignment
1. Întrucât în ierarhia de clase Java, clasa Object se află în rădăcina arborelui de

moștenire pentru orice clasă, ceea ce înseamnă că orice clasă va avea acces la o

serie de facilități oferite de Object. Una dintre ele este metoda toString(), al cărei

scop este de a oferi o reprezentare a unei instanțe de clasă sub forma unui șir de

caractere.

Definiți clasa Persoana cu un membru nume de tip String, și o metodă toString() care

va întoarce acest nume. Clasa va avea, de asemenea:

 un constructor fără parametri

 un constructor ce va inițializa numele.

Din ea derivați clasele Profesor și Student:

 Clasa Profesor va avea membrul materie de tip String.

 Clasa Student va avea membrul nota de tip int.

Clasele Profesor și Student vor avea:

 constructori fără parametri


 constructori care permit inițializarea membrilor. Identificați o modalitate de

reutilizare a codului existent.

Instanţiati clasele Profesor și Student:, și apelați metoda toString() pentru fiecare

instanță.

2. Adăugați metode toString() în cele două clase derivate, care să returneze tipul

obiectului, numele și membrul specific. De exemplu:

 pentru clasa Profesor, se va afișa: “Profesor: Ionescu, POO”

 pentru clasa Student, se va afișa: “Student: Popescu, 5”

Modificați implementarea toString() din clasele derivate astfel încât aceasta să

utilizeze implementarea metodei toString() din clasa de bază.

3. Adăugați o metodă equals în clasa Student. Justificați criteriul de echivalenţă ales.

Hint: vedeți metodele clasei Object, moștenită de toate clasele. Object oferă

metoda equals, a cărei implementare verifică echivalența obiectelor comparând

referințele. Acest comportament nu este suficient dacă avem si alte criterii de

echivalență pentru obiectele pe care le implementăm.


Moștenire (Inheritance)
Relația de tip is-a
Uneori, în calitate de programatori, suntem puși în situația de a avea nevoie de clase

care au unele atribute și metode similare, dar care au și unele atribute și metode

distincte.

Să presupunem că trebuie să realizăm o aplicație care să stocheze diferite localități

din România. Vom stoca localități de toate tipurile, toate dimensiunile, din toate

județele.

La o primă vedere, putem crea clasa Localitate care să aibă următoarele atribute:

String denumire;

int numărDeLocuitori;

String județ;

String tipulLocalitatii;

String adresaPrefectura;

String adresaPrimarie;

String adresaSpital;

Ne dăm seama imediat, că nu toate localitățile au primărie, și nici prefectură și nici

spital și că trebuie să creăm clase separate. Prefectură au doar reședințele de județ,

Primărie nu au unele sate, ci doar satele reședință de comună, iar comunele nu au


spitale. Totuși, există și atribute pe care le au toate localitățile: denumire, număr de

locuitori, județ etc.

Soluția: facem o clasă generică Localitate și mai multe subclase care să

moștenească această clasă, dar care să aibă și unele atribute distincte. Renunțăm la

atributul tipulLocalitatii.

public class Localitate {

private String denumire;

private int numărDeLocuitori;

private String județ;

public Localitate(String denumire, int numarDeLocuitori,

String judet) {

this.denumire = denumire;

this.numarDeLocuitori = numarDeLocuitori;

this.judet = judet;

// getteri, setteri
}

public class ResedintaJudet extends Localitate {

private String adresaPrefectura;

private String adresaPrimarie;

private String adresaSpital;

public ResedintaJudet(String denumire, int numarDeLocuitori,

String judet,

String prefectura, String primarie,

String spital){

super(denumire, numarDeLocuitori, judet);

this.adresaPrefectura = prefectura;

this.adresaPrimarie = primarie;

this.adresaSpital = spital;

//getteri, setteri pentru cele 3 atribute specifice clasei


}

public class Oras extends Localitate{

private String adresaPrimarie;

private String adresaSpital;

public Oras (String denumire, int numarDeLocuitori, String

judet,

String primarie, String spital) {

super(denumire, numarDeLocuitori, judet);

this.adresaPrimarie = primarie;

this.adresaSpital = spital;

// getteri, setteri pentru cele 2 atribute specifice clasei

public class SatResedintaComuna extends Localitate {

private String adresaPrimarie;


public SatResedintaComuna(String denumire, int

numarDeLocuitori, String judet,

String primarie){

super(denumire, numarDeLocuitori, judet);

this.adresaPrimarie = primarie;

// getteri, setteri pentru adresaPrimarie

public class Sat extends Localitate{

public Sat (String denumire, int numarDeLocuitori, String

judet){

super(denumire, numarDeLocuitori, judet);

}
Deși clasa ResedintaJudet pare să aibă doar 3 atribute, în realitate are 6, pentru că

le moștenește și pe cele ale clasei Localitate. De asemenea, moștenește și toate

metodele superclasei, inclusiv getteri și setteri.

Atentie: Dacă cele 3 atribute ale clasei Localitate sunt private tot le moștenșete, dar

nu le vom putea folosi direct, ci prin intermediul getterilor (=accesorilor) și setterilor

(=mutatorilor) pe care îi moștenește.

Oras oras = new Oras (”Galati”, 300000, “Galati”, “...”, “.....”);

oras.getDenumire(); // mosteneste aceasta metoda de la clasa mama. O

putem folosi direct.

Constructorul ResedintaJudet va contine toate cele 6 argumente. Prima linie în

constructorul unei subclase care moștenește o altă clasă, trebuie să fie trimiterea la

constructorul superclasei utilizând instructiuneasuper();.

 super() = apelarea constructorului superclasei

Dacă noi nu scriem super();, acest lucru îl va facem automat compilatorul care va

insera pe prima linie în codul compilat, comanda super();. În cazul nostru, noi vom

apela constructorul superclasei care are cele 3 argumente:

public Localitate(String denumire, int numarDeLocuitori, String judet)

folosind comanda :

super(denumire, numarDeLocuitori, judet);

Atenție, dacă superclasa nu are constructorul default (fără argumente) ci un alt

constructor, iar noi nu vom menționa keywordul super, vom avea o eroarea de
compilare, întrucât compilatorul va introduce keywordul super() automat care va bate

către un constructor care nu există.

Dacă există deja un constructor scris de noi cu argumente, compilatorul nu mai

introduce constructorul default.

Ca o dovadă că se moștenesc și atributele private, apelaţi în metoda main():

oras.setDenumire(”Braila”);

Faceți o metodă toString() în clasa mamă (Localitate) și apoi, în

metoda main() scrieți:

System.out.println(oras);

Ce obținem?

Localitate [denumire=Braila, ….]

Deși moștenește atributele private, nu vom putea, totuși, scrie direct într-o metodă a

clasei Oras următorul cod: this.denumire = ”Iasi”;. Dar, vom putea folosi getterul

care si el se mosteneste si care are access specifier

public: this.setDenumire(“Iasi”);. this, se referă la instanța (obiectul) cu care

lucrăm, care are atribute și metode specifice.

Aplicaţie:

Faceți o aplicație care să conțină clasa Animal și mai multe subclase (tipuri de

animale) care să aibă și atribute și metode specifice cum ar

fi latra(),necheaza(), ciripeste(), miauna() etc.


Construiti mai multe animale dintr-un fel.

Apelati metodele, încercați să folosiți atributele private ale superclasei în clasa

derivată, creați o metodă toString() și afisati atributele diferitelor animale.

public class Animal {

public void scoateSunet(){

System.out.println(“Animalul scoate un sunet de animal

(sunet generic).”);

public class Pisica extends Animal{

public void scoateSunet(){ //OVERRIDING

(SUPRASCRIERE)

System.out.println(“Pisica miauna.”);

public void toarce(){

System.out.println(“Pisica toarce.”);

}
}

Spunem că clasa Pisică a implementat metoda scoateSunet() de la clasa Animal.

Pisica pisica = new Pisica();

pisica.toarce();

pisica.scoateSunet(); // ce se va afisa?

Animal animal = new Animal();

animal.toarce(); //este posibil?

Aplicatie

Modificați aplicația de mai devreme pentru a vedea ce se întâmplă dacă

apelăm pisica.scoateSunet();.

Nivele de acces
Există 4 nivele de acces:

 private – se vede numai din interiorul clasei

 fără menționarea vreunui access specifier (varianta default) – se vede numai

din interiorul pachetului

 protected – se pot vedea și din exteriorul pachetului, numai dacă clasa care

vrea să folosească acel atribut sau acea metodă este o subclasă a clasei care
deține acel atribut/metodă (atenție, dacă este subclasă, oricum moștenește, dar

dacă e în afara pachetului, iar dacă metoda nu este protected, nu o poate folosi)

 public – se văd de peste tot.

private String nume;

String denumire;

protected void calculează(){.....}

public void setLungime(int lungime){...}

Cuvântul cheie final


Putem atribui unei clase / atribut / metode kywordul final. Atributele final nu mai pot fi

modificate. Pentru a le recunoaște ușor în cod, ele se scriu cu majuscule:

private final String CULOARE = ”Alb”;

private final String CULOARE;

//constructor:

public Masina (String marca, int cc, String culoare){

this.marca = marca;

this.cc = cc;

CULOARE = culoare;

}
Odată setată, culoarea nu mai poate fi modificată niciodată în cod.

 folosit la declararea unei clase, implică faptul că acea clasă nu poate fi

derivată (moștenită)

(de exemplu clasa String) public final class Animal {…} - clasa Animal nu poate fi

moștenită

 folosit la declararea unei metode, implică faptul că metoda nu poate fi

suprascrisă în clasele derivate

Pe lângă reutilizarea codului, moștenirea dă posibilitatea de a dezvolta pas cu pas o

aplicație (procedeul poartă numele de incremental development). Astfel, putem folosi

un cod deja funcțional.

Polimorfism

Animal animal = new Pisica();

O variabilă de tip Animal de pe Stack pointează către un obiect de tip Pisică de

pe Heap.

Ce afisează acum:

animal.scoateSunet(); // ?

Putem face oare acum:

animal.toarce(); // este posibil?


Polimormismul ne ajută foarte mult când vrem să definim un Array sau

un ArrayList (de exmplu) cu Animale generice dar în el să punem animale specifice:

câini, pisici, tigri etc.

De asemenea, ne ajută când avem o metodă care primește ca argument un Telefon,

iar noi îi pasăm fie un TelefonMobil, fie un TelefonFix, etc.

public void afiseazaCaracteristici (Telefon telefon) {

System.out.println(telefon);

// sau

telefon.setRingTone(“melodia preferata”);

Polimorfismul stă la baza tehnologiei de Dependency Injection când pe o metodă nu

o interesează ce clasă primește. (Dependency Injection folosește polimorfismul la

interfețe)

instanceof
Clasa Catel extinde clasa Animal.

Catel catel = new Catel();

if (catel instanceof Catel) {


System.out.println(true); // catel este un Catel

if (catel instanceof Animal) {

System.out.println(true); //catel este si un Animal

Up-casting și down-casting
Upcasting

Localitate loc = (Localitate) oras;

System.out.println(loc);

Variabila loc pointează acum către un obiect de tip Oras. - polimorfism

Convertirea unei referințe la o clasă derivată într-una a unei clase de bază poartă

numele de upcasting. Upcasting-ul este facut automat și nu trebuie declarat explicit

de către programator.

Exemplu de upcasting:

class Instrument {

public void play() {}

}
// Wind objects are instruments //

public class Wind extends Instrument {

public static void main(String[] args) {

Wind flute = new Wind();

tune(flute); // !! Upcasting automat

static void tune(Instrument i) {

i.play();

Deși obiectul flute este o instanță a clasei Wind, acesta este pasat ca parametru în

locul unui obiect de tip Instrument, care este o superclasa a clasei Wind. Wind este

un (is-a) Instrument, deci, argumentul Instrument poate primi un Wind. Upcasting-ul

se face la pasarea parametrului. Termenul de upcasting provine din diagramele de

clase (în special UML) în care moștenirea se reprezintă prin 2 blocuri așezate unul
sub altul, reprezentând cele 2 clase (sus este clasa de bază iar jos clasa derivată),

unite printr-o săgeată orientată spre clasa de bază.

Downcasting:

Localitate loc = new Localitate("Bucuresti");

Oras o = (Oras) loc; //eroare

Nu se poate transforma o localitate în oras.

Totuși, dacă Localitatea era creată ca Oraș, se putea face downcasting.

Localitate loc = new Oras("Bucuresti", "...","...","...","...");

Oras o = (Oras) loc;

Animal animal = new Pisica();

(Pisica) animal.toarce(); // acum este posibil?

Downcasting este operația inversă upcast-ului și este o conversie explicită de tip în

care se merge în jos pe ierarhia claselor (se convertește o clasă de bază într-una

derivată). Acest cast trebuie făcut explicit de către programator. Downcasting-ul este

posibil numai dacă obiectul declarat ca fiind de o clasă de bază este, de fapt,

instanță clasei derivate către care se face downcasting-ul. Iată un exemplu în care

este folosit downcasting:

class Animal {
public void eat() {

System.out.println("Animal eating");

class Wolf extends Animal {

public void howl() {

System.out.println("Wolf howling");

public void eat() {

System.out.println("Wolf eating");

class Snake extends Animal {

public void bite() {


System.out.println("Snake biting");

class Test {

public static void main(String[] args) {

Animal a [] = new Animal[2];

a[0] = new Wolf();

a[1] = new Snake();

for (int i = 0; i < a.length; i++) {

a[i].eat(); // 1

if (a[i] instanceof Wolf) ((Wolf)a[i]).howl();

// 2

if (a[i] instanceof Snake)

((Snake)a[i]).bite(); // 3

}
}

În liniile marcate cu 2 și 3 se execută un downcast de la Animal la Wolf, respectiv

Snake pentru a putea fi apelate metodele specifice definite în aceste clase. Înaintea

execuției downcast-ului (conversia de tip la Wolf respectiv Snake) verificăm dacă

obiectul respectiv este de tipul dorit (utilizând operatorul instanceof). Dacă am

încerca să facem downcast către tipul Wolf al unui obiect instantiat la Snake mașina

virtuală ar semnala acest lucru aruncând o excepție la rularea programului.

Apelarea metodei eat() (linia 1) se face direct, fără downcast, deoarece această

metodă este definită și în clasa de bază. Datorită faptului că Wolf suprascrie

(overrides) metoda eat(), apelul a[0].eat() va afișa “Wolf eating”. Apelul a[1].eat() va

apela metoda din clasă de bază (la ieșire va fi afișat “Animal eating”) deoarece a[1]

este instantiat la Snake, iar Snake nu suprascrie metoda eat().

Upcasting-ul este un element foarte important. De multe ori răspunsul la întrebarea:

este nevoie de moștenire? este dat de răspunsul la întrebarea: am nevoie de

upcasting? Aceasta deoarece upcasting-ul se face atunci când pentru unul sau mai

multe obiecte din clase derivate se execută aceeași metodă definită în clasa părinte.
Clasa Object
Suprascrierea metodei equals()
Toate clasele moștenesc clasa Object, și implicit toate metodele acesteia (sunt public

și protected)

Modifier and Method and Description


Type
protected Object Creates and returns a copy of this object.
clone()
boolean Indicates whether some other object is "equal to" this one.
equals(Object obj)
protected void Called by the garbage collector on an object when garbage collection
finalize() determines that there are no more references to the object.
Class<?> Returns the runtime class of this Object.
getClass()
int hashCode() Returns a hash code value for the object.
void notify() Wakes up a single thread that is waiting on this object's monitor.
void notifyAll() Wakes up all threads that are waiting on this object's monitor.
String toString() Returns a string representation of the object.
void wait() Causes the current thread to wait until another thread invokes the
notify() method or the notifyAll() method for this object.
void wait(long Causes the current thread to wait until either another thread invokes the
timeout) notify() method or the notifyAll() method for this object, or a specified
amount of time has elapsed.
void wait(long Causes the current thread to wait until another thread invokes the
timeout, int nanos) notify() method or the notifyAll() method for this object, or some other
thread interrupts the current thread, or a certain amount of real time has
elapsed.

Metoda equals(Object o) în mod default verifică dacă adresele a două obiectele sunt

aceleași.

Având în vedere că toate clasele moștenesc clasa Object, deci implicit și

metoda equals(), noi putem se o suprascriem (override), să o redefinim:


 pentru a verifca egalitatea între caracteristicile a două obiecte, cum ar fi

frecvența procesorului, memoria, marca în cazul a două Computere.

Eclipse poate genera această metodă: Source – generate equals() și hashCode()

@Override //însemană că am rescris/suprascris/redefinit

metoda

public boolean equals(Object obj) {

if (this == obj)

return true;

if (obj == null)

return false;

if (getClass() != obj.getClass())

return false;

Computer other = (Computer) obj;

if ( marca == null) {
if (other.marca != null)

return false;

} else if (!marca.equals(other.marca))

return false;

if (frecventa == null) {

if (other.frecventa != null)

return false;

} else if (frecventa != other.frecventa)

return false;

if (memorie == null) {

if (other. memorie != null)

return false;

} else if ( memorie != other.memorie )

return false;
return true;

Metoda equals() se suprascrie de fiecare dată numai împreună cu

metoda hashCode().

Totuși, pentru Array sau ArrayList, nu avem nevoie momentan de hashCode().

Moștenire (Inheritance)
Numită și derivare, moștenirea este un mecanism de refolosire a codului și reprezintă

posibilitatea de a defini o clasă care extinde o altă clasă deja existentă. Ideea de

bază este de a prelua funcționalitatea existentă într-o clasă. Putem de asemenea să

adăugăm noi funcţionalităţi şi să le modifcăm pe cele existente.

Clasa existentă este numită clasa-părinte, clasa de bază sau super-clasă. Clasa care

extinde clasa-părinte se numește clasa-copil (child), clasa derivată sau sub-clasă.

Moștenirea este un mecanism care permite crearea unor versiuni “specializate” ale

unor clase existente (de bază). Moștenirea este folosită în general atunci când se

dorește construirea unui tip de date care să reprezinte o implementare specifică (o

specializare oferită prin clasa derivată) a unui lucru mai general. Un exemplu simplu

ar fi clasa Dacia care moștenește clasa Mașina.

public class Animal {

public class Mammal extends Animal {

public class Reptile extends Animal {

}
public class Dog extends Mammal {

Pe baza exemplului de mai sus, următoarele sunt adevărate în termeni de orientat

obiect:

 Animal este o super-clasă a clasei Mammal.

 Animal este o super-clasă a clasei Reptile.

 Mammal şi Reptile sunt sub-clase ale clasei Animal.

 Dog este o subclasă atât pentru clasa Mammal cât şi pentru clasa Animal.

Având în vedere relaţia IS-A, putem spune că:

 Mammal IS-A Animal

 Reptile IS-A Animal

 Dog IS-A Mammal

 Atunci : Dog IS-A Animal de asemenea


Implicații
În Java, clasele și membrii acestora (metode, variabile, clase interne) pot avea

diverși specificatori de acces.

 specificatorul de acces protected - specifică faptul că membrul sau metoda

respectivă poate fi accesată doar din cadrul clasei însăși sau din clasele derivate

din această clasă. Clasele nu pot avea acest specificator, doar membrii acestora!

 specificatorul de acces private - specifică faptul că membrul sau metoda

respectivă poate fi accesată doar din cadrul clasei însăși, nu și din clasele
derivate din această clasă. Clasele nu pot avea acest specificator, doar membrii

acestora!

Cuvântul cheie final

 folosit la declararea unei metode, implicând faptul că metoda nu poate fi

suprascrisă în clasele derivate

 folosit la declararea unei clase, implicând faptul că acea clasă nu poate fi

derivată (de exemplu clasa String)

Pe lângă reutilizarea codului, moștenirea dă posibilitatea de a dezvolta pas cu pas o

aplicație (procedeul poartă numele de incremental development). Astfel, putem folosi

un cod deja funcțional.

Overriding / Suprascriere. Super


Suprascrierea este mecanismul de rescriere a unei metode într-o clasă derivată.

Acest lucru ne permite să definim o implementare mai specializată pentru clasa

respectivă.

Beneficiile suprascrierii includ: abilitatea de a defini un comportament specific

subclasei, ceea ce înseamnă că subclasa poate implementa metodele super-clasei

în funcţie de cerinţele specifice ale acesteia. În termeni orientat obiect, suprascrierea

înseamna să suprascriem funcţionalitatea unei metode existente.

class Animal {

public void move() {

System.out.println("Animals can move");

}
}

class Dog extends Animal {

public void move(){

System.out.println("Dogs can walk and run");

public class TestDog {

public static void main(String args[]) {

Animal a = new Animal(); // Animal reference and object

Animal b = new Dog(); // Animal reference but Dog object

a.move(); // runs the method in Animal class

b.move(); //Runs the method in Dog class

This would produce the following result:

Animals can move


Dogs can walk and run

În exemplul de mai sus, putem observa că obiectul b, chiar dacă este de tipul Animal

apelează metoda din clasa Dog. Motivul este următorul: la compilare, verificarea este

făcută pe tipul referinţei. Cu toate acestea, la rulare, JVM rulează metoda care

aparţine tipului instanţiat. Aşadar, în exemplul de mai sus, programul se va compila

corect, clasa Animal având metoda move. Apoi, la rulare se va executa metoda

specifică clasei Dog

Putem considera următorul exemplu:

class Animal {

public void move() {

System.out.println("Animals can move");

class Dog extends Animal {

public void move(){

System.out.println("Dogs can walk and run");

}
public void bark(){

System.out.println("Dogs can bark");

public class TestDog {

public static void main(String args[]){

Animal a = new Animal(); // Animal reference and object

Animal b = new Dog(); // Animal reference but Dog object

a.move(); // runs the method in Animal class

b.move(); //Runs the method in Dog class

b.bark();

Codul de mai sus va produce următorul rezultat:

TestDog.java:30: cannot find symbol

symbol : method bark()


location:class Animal

b.bark();

Acest program va arunca o eroare de compilare, deoarece variabila b este de tipul

Animal, care nu are nicio metodă bark().

Reguli pentru suprascrierea metodelor


 Lista argumentelor ar trebui să fie exact la fel ca şi lista din metoda

suprascrisă.

 Tipul returnat ar trebui să fie la fel sau un subtip a tipului declarat în metoda

originală din superclasă.

 Nivelul de acces nu poate fi mai restrictiv decât nivelul de acces al metodei

suprascrise. De exemplu, dacă metodele din superclasă sunt declarate public,

atunci metoda din subclasă nu poate fi declarată nici private nici protected.

 Metodele de instanţă pot fi suprascrise doar dacă sunt moştenite de subclasă.

 O metodă declarată final nu poate fi suprascrisă.

 O metodă declarată static nu poate fi suprascrisă, dar poate fi re-declarată.

 Dacă o metodă nu poate fi moştenită, atunci nu poate fi suprascrisă.

 O subclasă din acelaşi pachet ca şi clasa de bază poate suprascrie orice

metodă care nu este declarată private sau final.

 O subclasă dintr-un pachet diferit poate declara doar metodele non-final care

sunt declarate public sau protected.

 Constructorii nu pot fi suprascrişi.

Super
Folosirea cuvântului cheie super se face de obicei când apelăm o metodă, un

constructor sau un câmp din superclasă.

class Animal {

public void move() {

System.out.println("Animals can move");

class Dog extends Animal {

public void move() {

super.move(); // invokes the super class method

System.out.println("Dogs can walk and run");

public class TestDog {

public static void main(String args[]) {

Animal b = new Dog(); // Animal reference but Dog object

b.move(); // Runs the method in Dog class


}

Rezultatul programului din exemplu este următorul:

Animals can move

Dogs can walk and run

1. Cuvântul-cheie "static"
De fiecare dată când cream o instanță a unei clase, valorile câmpurilor din cadrul

instanței sunt unice pentru aceasta. Ele pot fi utilizate fără ca altă instanță să le

modifice în mod implicit. Următoarea clasă conţine 2 câmpuri statice care pot fi

utilizate la comun cu toate insţantele clasei respective. De obiecei, câmpurile statice

se apelează cu numele clasei. Ele se pot apela şi cu oricare instanţă, dar acest lucru

poate crea confuzii.

public class Cow {

String color;

String name;

double weight;

static String sound = "MUUUUUUUU!";


public Cow() {

color = "White";

name = "Joana";

weight = 0;

public class CowsTest {

public static void main(String[] args) {

System.out.println("All cows make the sound " + Cow.sound);

Cow myCow = new Cow();

System.out.println(myCow.name + " makes the sound " +

myCow.sound);

}
Un alt exemplu este folosirea unui câmp static pentru a numără câte instanţe s-au

creat dintr-o clasă respectivă.

public class Dog {

static int numberOfDogs = 0;

public Dog() {

numberOfDogs++;

public class DogsTest {

public static void main(String[] args) {

Dog dog1 = new Dog();

Dog dog2 = new Dog();

System.out.println("My program has " + Dog.numberOfDogs);

Atunci când folosim câmpuri statice, ele pot fi folosite în orice loc al clasei (în orice

metodă), cu precizarea că ele vor avea aceleaşi valori.


În cazul metodelor statice, ele au aceeaşi semnificaţie pentru toate instanţele clasei

şi pot fi apelate cu numele acesteia. Într-o metodă statică se pot utiliza doar câmpuri

statice, nu şi câmpurile clasei.

public class Persoana {

static void displayAverageAge() {

System.out.println("A person may leave up to 60 years!");

Câmpurile şi metodele statice ne indică ceva care reprezintă clasa respectivă, nu

doar o instanţă a acesteia. Acestea pot fi apelate cu instanţele clasei, însă nu vor

avea efect direct asupra lor.

Un atribut ne-static nu poate fi apelat decât dacă avem un obiect. Atributele ne-

statice sunt atribute specifice ale obiectelor. Nu poate exista o memorie ram fără

computer.

public class Computer {

int ram;

//constructor

//getteri, setteri, alte metode


public static void go(){

System.out.println ( ram );

public class Main {

public static void main (String [] args){

Computer c1 = new Computer(4);

System.out.println (c1.getRam());

Computer.go();

Dintr-o metodă statică (= context static) nu putem apela DIRECT un atribut ne-static,

pentru că metoda statică poate se fie apelată fără să existe vreun obiect. Dacă nu

avem obiect, nu putem apela un atribut sau o metodă ne-statică.

Soluția:

public class Main {


public static void main (String [] args){

Computer c1 = new Computer(4);

Computer.go(c1); //go() fiind statică, o apelăm cu

ajutorul clasei

//NU ne trebuie nici un obiect

pentru a o apela, ex: c1.go().

//pasăm însă obiectul c1 ca

parametru.

public class Computer{

int ram;

//constructor

//getteri, setteri, alte metode

public static void go( Computer computer){ //computer

preia practic pe c1
System.out.println( computer.ram ); //computer.ram

este de fapt c1.ram

//afișăm ram-ul

lui c1.

Atributele ne-statice

 instance fields / instance attributes / instance variables

 câmpuri de instanță / atribute de instanță / variabile de instanță

Atributele statice

 class fields / class attributes / class variables

 câmpuri de clasă / atribute de clasă / variabile de clasă

Modul de apelare

atribute și metode ne-statice atribute și metode statice


din context ne-static √ √
din context static X √

Assignment

Consultaţi documentaţia clasei Math. Încercaţi să apelaţi o serie de metode şi

câmpuri din aceasta.


2. Cuvântul-cheie "final"
Variabilele declarate cu atributul final pot fi inițializate o singură dată. Observăm că

astfel unei variabile de tip referință care are atributul final îi poate fi asignată o

singură valoare (variabila poate face referinţă către un singur obiect). O încercare

nouă de asignare a unei astfel de variabile va avea ca efect generarea unei erori la

compilare. Totuși, obiectul către care face referinţă o astfel de variabilă poate fi

modificat (prin apeluri de metode sau acces la câmpuri).

public class Cow {

final String color; // nu putem picta vaca!

static String sound = "MUUUUUUUU!";

public Cow() {

color = "White"; // pentru ca e final, trebuie sa existe in

constructor

public Cow(String color) {

this.color = color;

}
}

Variabilele primitive De
referință
Locale (în Definite în √ √ Nu au access specifiers (public,
metode) metodă private, protected); Nu au
static; Pot fi final
Definite ca √ √
parametri în
paranteza
metodei
câmpuri De instanță √ √ Pot avea access specifiers
(nestatice) (public, private, protected); pot
fi statice; pot fi final; pot fi
De clasă (statice) √ √ static + final

O variabilă declarată ca final nu mai poate fi modificată a doua oară.

Atributele de instantă (nestatice) care sunt declarate final trebuie inițializate /

instanțializate în constructor (de preferat).

Atriburele de clasă (statice) care sunt declarate final NU POT fi inițializate /

instanțializate în constructor. Ele trebuie inițializate / instanțializate pe loc atunci când

sunt declarate.

3. Constante
Constantele sunt definite folosind atât static, cât şi final. Identificatorul lor se scrie cu

majuscule şi cu semnul _ între cuvinte. Exemplu:

public static final int DEFAULT_TRIES = 3;

public static final int RAM = 8;

public static final int NUME = ”Maria”;


public static final Cont CONT1 = new Cont();

4. Primitive Wrapper Classes


Sunt clase immutable care ne dau metode pentru cele 8 tipuri de date primitive în

Java. Conversia între o dată primitivă şi clasa ei se face automat – acest mecanism

poartă numele de boxing şi unboxing.

Problemă:

Vrem să introducem într-un ArrayList numere. ArrayListul nu primește decât obiecte.

Cum facem?

Soluție:

Ne vin în ajutor clasele Wrapper.

Clasele Wrapper sunt asemenea lui String. String împachetează un șir de caractere

asupra căruia putem apela o mulțime de metode specifice clasei String.

În mod asemănător, și Wrapper classes împachetează o primitivă (de exemplu, un

număr), asupra căruia putem efectua multe operații. (Primitiva este stocată într-un

atribut la care noi nu avem acces.)

Există câte o clasă wrapper pentru fiecare tip de primitivă:

Primitivă Clasă Wrapper


byte Byte
short Short
int Integer
long Long
float Float
double Double
Primitivă Clasă Wrapper
char Character
boolean Boolean

Toate aceste clase se află în pachetul java.lang, și nu trebuie importate. Ele se

încarcă automat.

Construim un obiect Integer:

int nr = 8;

Integer interger1 = new Integer(nr);

// alternativ

Integer interger1 = new Integer(8);

// sau

String str = ”10”;

Integer integer2 = new Integer(str);

// alternativ

Integer interger2 = new Integer(“10”);


Al doilea constructor poate arunca o eroare de tip NumberFormatException în cazul

în care stringul nu poate fi transformat într-un număr.

Putem efectua o mulțime de operații cu aceste obiecte. A se vedea API pentru clasa

Integer.

int compareTo(Integer anotherInteger) Compares two Integer objects numerically.

System.out.println(integer1.compareTo(integer2)); // returnează -1,

0 sau 1.

boolean equals(Object obj) Compares this object to the specified object.

Metoda equals() din Object este suprascrisă pentru a compara numerele, nu

adresele.

System.out.println(integer1.equals(integer2)); // returnează true

sau false.

String toString() Returns a String object representing this Integer's value.

Transformă numărul într-un String

System.out.println(integer1.toString());

Există o muțime de metode utile care sunt statice și care manipulează int-uri:

static int compare(int x, int y) Compares two int values numerically.

System.out.println(Integer.compare(3355, 545747));

static int compareUnsigned(int x, Compares two int values numerically treating the values
int y) as unsigned.

System.out.println(Integer.compareUnsigned(3355, -545747));

static int max(int a, int b) Returns the greater of two int values as if by calling Math.max.

System.out.println(Integer.max(3355, -545747));

static int signum(int i) Returns the signum function of the specified int value.

Returnează semnul.

System.out.println(Integer.max(-545747));

static int sum(int a, int b) Adds two integers together as per the + operator.

System.out.println(Integer.sum(3355, -545747));

static String Returns a string representation of the integer argument as an


toBinaryString(int i) unsigned integer in base 2.

Transformă numărul în baza 2. Idem și baza 8 și baza 16.

System.out.println(Integer.toBinaryString(5747));

static int Returns the number of zero bits preceding the highest-order
numberOfLeadingZeros(int i) ("leftmost") one-bit in the two's complement binary
representation of the specified int value.

Spune câte zeroruri sunt în față: Numărul 3 este

reprezentat: 000000000000000000000000000000011. Prin urmare sunt 30 de zerouri. Idem

pentru câte zerouri sunt în coadă.

System.out.println(Integer.numberOfLeadingZeros(3));
static int Returns the value obtained by reversing the order of the bits in the two's
reverse(int i) complement binary representation of the specified int value.

Inversează biții.

static String toString(int i) Returns a String object representing the specified integer.

Transformă un int într-un String

System.out.println(Integer.toString(3));

Există metode care transformă un int ↔ Integer ↔ String. Metode similare există și în

celelalte clase wrapper (Double, Float, Short, Long, Boolean, Character etc).

Atribute:
 Integer.MAX_VALUE → 2 miliarde și ceva

 Integer.MIN_VALUE → -2 miliarde și ceva

 Interger.SIZE → numărul de biți (32)


Autoboxing
Începând cu Java 1.5 (adică 5), compilatorul face automat transformarea între un int

și un Integer și între o primitivă și un obiect de tip wrapper. Se poate scrie:

int nr1 = new Integer(4); // compilatorul face automat transformarea

// sau

Integer nr2 = 5; // compilatorul face automat transformarea

De asemenea, putem scrie și:

ArrayList<Integer> arr = new ArrayList<Integer>();

arr.add(4); // compilatorul face automat transformarea

5. Obiecte imutabile
Dacă toate atributele unui obiect sunt finale, spunem că obiectul respectiv este

immutable în sensul că starea lui internă nu se poate modifica. Exemple de astfel de

obiecte sunt instanțele clasei String. Odată create, prelucrările asupra lor (ex.:

toUpperCase()) se fac prin instanţierea de noi obiecte și nu prin alterarea obiectelor

înseși.

String s = "abc";
s.toUpperCase(); // s-ul nu se modifică. Metoda întoarce o referință

către un nou obiect

s = s.toUpperCase(); // s este acum o referință către un nou obiect

Observăm că în acest exemplu am folosit un String literal. Literalii sunt păstrați într-

un String pool pentru a limita memoria utilizată. Asta înseamnă că dacă mai

declarăm un alt literal “abc”, nu se va mai aloca memorie pentru încă un String, ci

vom primi o referință către s-ul inițial. În cazul în care folosim constructorul pentru

String se aloca memorie pentru obiectul respectiv și primim o referință nouă.

String a = "abc";

String b = "abc";

System.out.println(a == b); // true

String c = new String("abc");

String d = new String("abc");

System.out.println(c == d); // false

Important: operatorul "==" compară referințele. Dacă am fi vrut să comparăm șirurile

în sine am fi folosit metoda equals. Același lucru este valabil și pentru oricare alt tip

referință: operatorul "==" testează egalitatea referințelor (i.e. dacă cei doi operanzi

sunt de fapt același obiect).


Exemplu

String str = new String (”Maria”); // sau String str = “Maria”;

Integer i1 = new Integer(4);

Odată aceste obiecte create, conținutul lor (Maria sau 4) nu se mai schimbă

niciodată!!! Aceste clase nu au metode de shimbare a conținului lor (setteri).

Variabilele de referință str și i1 pot să pointeze către alte obiecte, dar, obiectele de pe

heap: new String("Maria"), new Integer(4) sau string pool "Maria" nu se mai schimbă

niciodată.

String str = "Maria";

str = "Ioana";

Integer i1 = new Integer(4);

i1 = new Integer(8);

Obiectul nu s-a schimbat!! Variabila de referință doar pointează către un alt obiect!
String str = "Mac";

str = str + "Ham";

// Obiectul referit prin str nu se schimbă!!!!

// Ci se crează și obiectul Ham, obiectul MacHam, iar str va pointa

către acesta
Semnătura metodelor. Supraîncărcare
(Overloading)
Semnătura metodei este dată de numele acesteia şi lista de parametrii (numărul şi

tipul fiecăruia). Într-o clasă nu putem avea metode cu aceeaşi semnătură, dar putem

avea metode cu nume diferite dacă semnătura lor diferă.

Antetul metodei, este format din modificatori, tipul metodei şi semnatura acesteia.

Corpul metodei reprezintă toate instrucţiunile pe care le vom executa în interiorul

acesteia.

Dacă într-o clasă există mai multe metode cu acelaşi nume, dar cu o listă de

parametrii care diferă, putem spune că vorbim de supraîncărcare.

public void eat(String where) {

// implementation

public void eat(int howMuch) {

// implementation

public void eat(String where, int howMuch) {


// implementation

Deci pot exista într-o clasă mai multe metode cu același nume dacă:

1. au număr de parametri diferit

2. diferă tipul parametrilor

3. diferă ordinea lor

4. nu contează return-type-ul

Cu alte cuvinte, dacă semnătura metodelor este diferită. Semnătura unei metode

(method signature) înseamnă denumirea + parametrii. Return-type-ul nu intră în

semnătura metodei.

Aceleași reguli se aplică și la supraîncărcarea constructorului. Constructorii se mai

pot apela unii pe alții:

public Computer (String marca, int ram, int procesor){

this (ram, procesor); // prima linie din constructor

this.marca = marca;

public Computer (int ram, int procesor){

super(); // prima linie din constructor


this.ram = ram;

this.procesor = procesor;

Număr variabil de argumente (var-args)


JDK 1.5 ne permite să transmitem un număr variabil de argumente de acelaşi tip

metodelor. Parametrul din metodă este declarat în următorul mod:

type... parameternName

În declaraţia metodei, se specifică tipul parametrului urmat de 3 puncte (…). Numai

un singur parametru cu număr variabil de argumente este permis într-o metodă, şi

acesta trebuie să fie ultimul din lista de parametrii. Ceilalţi parametrii normali trebuie

să fie înaintea lui.

Acest parametru se comportă ca un vector în interiorul metodei şi poate fi folosit ca

atare.

Exemplu

public class VarargsDemo {

public static void main(String args[]) {

// Call method with variable args

printMax(34,3,3,2,56.5);
printMax(new double[]{1,2,3});

public static void printMax(double... numbers) {

if (numbers.length == 0) {

System.out.println("No argument passed");

return;

double result = numbers[0];

for(int i = 1; i <numbers.length; i++)

if(numbers[i]> result)

result = numbers[i];

System.out.println("The max value is "+ result);

}
Care va afişa următorul rezultat:

The max value is 56.5

The max value is 3.0

Exemplu 2

public static void main (String [] args){

metoda1(34, 363, 546, 235345);

metoda1(45, 43543, 577, 655, 2134, 21, 67, 8747, 134234,

25372);

metoda2(”asdg”, “fdgfd”, “hhads”, “hsed”);

metoda2(”asdg”, “fdgfd”, “hhads”, “hsed”, “dfgv”, “fdgr”,

“fdyerfa”, “dsgjkse”);

metoda3(telefon1, computer1, carte1, carte2, computer2,

“fsdgsfg”, bicicleta1);

metoda3(telefon3, bicicleta2 , telefon4, computer23);


}

public static void metoda1 (int... numere){ //adică număr variabil

de int-uri

//la rulare se formeaza un array de tip int[] numere care

contine parametrii

for (int numar : numere){

System.out.println(numar);

public static void metoda2 (String... siruri){ //adică număr

variabil de string-uri

for (int i = 0; i < siruri.length; i++){

System.out.println(siruri[i]);

}
public static void metoda3 (Object... obiecte){ //adică număr

variabil de obiecte

//la rulare se formeaza un array de tip Object[] obiecte care

contine parametrii

for (Object obiect : obiect){

System.out.println(obiect);

Return pentru metodele de tip void


public void afiseaza(int nr){

if (nr == 0){

System.out.println (“nr e 0. Eroare!”);

return;

System.out.println (“nr = ” + nr);

}
return; nu face altceva decat sa termine metoda. Dar el nu returneaza nimic, pentru

ca nu avem return nr; ci doar return;.

Metode. Recapitulare
Assignment

1. Pentru următorul program, apelaţi cele 2 funcţii lipsă şi schimbaţi structura if-else

într-un switch.

import java.util.Scanner;

public class PictureMenu {

public static void main(String[] args) {

Scanner kb = new Scanner(System.in);

int choice;

System.out.println( "1. Butterfly " );

System.out.println( "2. Elephant " );

System.out.println( "3. Teddy Bear" );


System.out.println( "4. Snake " );

System.out.print( "\nWhich animal to draw? " );

choice = kb.nextInt();

System.out.println();

if (choice == 1) {

butterfly();

} else if (choice == 2) {

elephant();

} else if (choice == 3) {

// * write code here to call the function named

'teddybear'

} else if (choice == 4) {

// * write code here to call the function named 'snake'

} else {
System.out.println( "Sorry, that wasn't one of the

choices." );

System.out.println( "\nGoodbye!" );

public static void butterfly() {

System.out.println(" .==-. .-==. ");

System.out.println(" \\()8`-._ `. .' _.-'8()/ ");

System.out.println(" (88\" ::. \\./ .:: \"88)

");

System.out.println(" \\_.'`-::::.(#).::::-'`._/ ");

System.out.println(" `._... .q(_)p. ..._.' ");

System.out.println(" \"\"-..-'|=|`-..-\"\" ");

System.out.println(" .\"\"' .'|=|`. `\"\". ");

System.out.println(" ,':8(o)./|=|\\.(o)8:`. ");

System.out.println(" (O :8 ::/ \\_/ \\:: 8: O) ");


System.out.println(" \\O `::/ \\::' O/ ");

System.out.println(" \"\"--' `--\"\" hjw");

public static void elephant() {

System.out.println(" _..--\"\"-. .-

\"\"--.._ ");

System.out.println(" _.-' \__...----...__ /

'-._");

System.out.println(" .' .:::...,'

',...:::. '.");

System.out.println("( .'``'''::;

;::'''``'. )");

System.out.println(" '-) (-'

/");

System.out.println(" /

/");

System.out.println(" .'.-. .-.'.

/");
System.out.println(" | \\0|0/ | /");

System.out.println(" | .-==-. | / |");

System.out.println(" `/`; ;`\\`

/");

System.out.println(" '.._ (_ | .-==-. | _)

_..'");

System.out.println(" `\"`\"-`/ `/' '\\` \\`-

\"`\"`");

System.out.println(" / /`; .==. ;`\");

System.out.println(" .---./_/ .==. /\");

System.out.println(" / '. `-.__) | `\"");

System.out.println(" | =(`-. '==. ;");

System.out.println(" jgs '. `-. /");

System.out.println(" \\_:_) `\"--.....-'");

public static void teddybear() {

System.out.println(" ___ .--. ");


System.out.println(" .--.-\" \"-' .- |");

System.out.println(" / .-,` .'");

System.out.println("

` \\");

System.out.println(" '. ! \\");

System.out.println(" | ! .--. |");

System.out.println("

'--' /.____");

System.out.println(" /`-. \\__,'.' `\\");

System.out.println(" __/ \\`-.____.-' `

/");

System.out.println(" | `---`'-'._/-` \\----' _");

System.out.println(" |,-'` / | _.-'

`\\");

System.out.println(" .' / |--'` /

|");

System.out.println(" / /
` |");

System.out.println(" | .\\/

.--. __ \|");

System.out.println(" '-' '._ / `

/");

System.out.println(" jgs `

' |------'`");

System.out.println("

|");

System.out.println("

/");

System.out.println(" '._ _.'");

System.out.println(" ``");

public static void snake() {


System.out.println(" ,,'6''-,.");

System.out.println(" <====,.;;--.");

System.out.println(" _`---===. \"\"\"==__");

System.out.println(" //\"\"@@-\\===\\@@@@ \"\"\\");

System.out.println(" |( @@@ |===| @@@ ||");

System.out.println(" \@@ |===| @@ //");

System.out.println(" \@@ |===|@@@ //");

System.out.println("

|===| //");

System.out.println("___________\\|===| //_____,----

\"\"\"\"\"\"\"\"\"\"-----,_");

System.out.println(" \"\"\"\"---,__`\\===`/ _________,-----

----,____ `,");

System.out.println(" |==||

\\");

System.out.println(" |==| pb

) |");
System.out.println(" |==| _____

______,--' '");

System.out.println(" |=| `----\"\"\"

`\"\"\"\"\"\"\"\" _,-'");

System.out.println(" `=

__,---\"\"\"-------------\"\"\"''");

System.out.println(" \"\"\"\" ");

2. În următorul program, căutaţi o funcţie care "returnează" o valoare. Când apelaţi

funcţia respectivă, vă va da un rezultat. Compilaţi şi rulaţi ambele clase. Produc

ambele acelaşi rezultat?

Există un bug în formulele din ambele secvenţe de cod. Când (a + b + c) este un

număr impar, împărţirea sa la 2 va da un rezultat care se termină în .5, pe care îl

pierdem. Reparaţi ambele fişiere astfel încât împarţirea "(a + b + c) / 2" să devină "(a

+ b + c) / 2.0" peste tot unde apare. Este mai uşor să reparaţi fişierul care conţine

metode sau fişierul care nu conţine metode?

Adăugaţi încă un test pentru ambele fişiere: găsiţi aria unui triunghi cu laturile de 9, 9,

şi 9. Cât de dificilă a fost această modificare pentru fişierul care foloseşte funcţii?

public class HeronsFormula {


public static void main(String[] args) {

double a;

a = triangleArea(3, 3, 3);

System.out.println("A triangle with sides 3,3,3 has an area

of " + a );

a = triangleArea(3, 4, 5);

System.out.println("A triangle with sides 3,4,5 has an area

of " + a );

a = triangleArea(7, 8, 9);

System.out.println("A triangle with sides 7,8,9 has an area

of " + a );

System.out.println("A triangle with sides 5,12,13 has an

area of " + triangleArea(5, 12, 13) );


System.out.println("A triangle with sides 10,9,11 has an

area of " + triangleArea(10, 9, 11) );

System.out.println("A triangle with sides 8,15,17 has an

area of " + triangleArea(8, 15, 17) );

public static double triangleArea(int a, int b, int c) {

// the code in this function computes the area of a triangle

whose sides have lengths a, b, and c

double s, A;

s = (a+b+c) / 2;

A = Math.sqrt( s*(s-a)*(s-b)*(s-c) );

return A;

// ^ after computing the area, "return" it

}
public class HeronsFormulaNoFunction {

public static void main(String[] args) {

int a, b, c;

double s, A;

a = 3;

b = 3;

c = 3;

s = (a + b + c) / 2;

A = Math.sqrt(s * (s - a) * (s - b) * (s - c));

System.out.println("A triangle with sides 3, 3, 3 has an

area of " + A );

a = 3;

b = 4;

c = 5;

s = (a + b + c) / 2;
A = Math.sqrt(s * (s - a) * (s - b) * (s - c));

System.out.println("A triangle with sides 3, 4, 5 has an

area of " + A );

a = 7;

b = 8;

c = 9;

s = (a + b + c) / 2;

A = Math.sqrt(s * (s - a) * (s - b) * (s - c));

System.out.println("A triangle with sides 7, 8, 9 has an

area of " + A );

a = 5;

b = 12;

c = 13;

s = (a + b + c) / 2;

A = Math.sqrt(s * (s - a) * (s - b) * (s - c));
System.out.println("A triangle with sides 5, 12, 13 has an

area of " + A );

a = 10;

b = 9;

c = 11;

s = (a + b + c) / 2;

A = Math.sqrt(s * (s - a) * (s - b) * (s - c));

System.out.println("A triangle with sides 10, 9, 11 has an

area of " + A );

a = 8;

b = 15;

c = 17;

s = (a + b + c) / 2;

A = Math.sqrt(s * (s - a) * (s - b) * (s - c));

System.out.println("A triangle with sides 8, 15, 17 has an

area of " + A );
}

3. Scrieţi o funcţie care să calculeze distanţa dintre 2 puncte. Având 2 puncte (x1, y1)

şi (x2, y2), distanţa dintre cele 2 este dată de formula:

d = radical( ( x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) )

Metoda trebuie să aibă numele getDistance() şi să returneze distanţa dintre cele 2

puncte. Folosiţi clasa Math pentru a calcula puterea unui număr şi rădăcina acestuia.

public class DistanceFormula {

public static void main(String[] args) {

// test the formula a bit

double d1 = distance(-2,1 , 1,5);

System.out.println(" (-2,1) to (1,5) => " + d1 );

double d2 = distance(-2,-3 , -4,4);

System.out.println(" (-2,-3) to (-4,4) => " + d2 );


System.out.println(" (2,-3) to (-1,-2) => " + distance(2,-

3,-1,-2) );

System.out.println(" (4,5) to (4,5) => " + distance(4,5,4,5)

);

public static double getDistance(int x1, int y1, int x2, int y2)

// put your code up in here

4. Scrieţi o funcţie. Aceasta va returna numele unei luni dintr-un an, în funcţie de

numărul acesteia. Asiguraţi-vă că nu aveţi instrucţiuni de citire sau afişare în interiorul

funcţiei. Numărul lunii va fi dat funcţiei ca parametru, iar un String va fi întors ca

rezultatul care conţine numele.

Funcţia trebuie apelată folosind monthName(), şi trebuie să aibă un singur parametru

(un număr întreg), şi să returneze un String.

public class MonthName {

public static String monthName(int month) {


String result;

// Your code goes in here.

return result;

public static void main(String[] args) {

System.out.println("Month 1: " + monthName(1));

System.out.println("Month 2: " + monthName(2));

System.out.println("Month 3: " + monthName(3));

System.out.println("Month 4: " + monthName(4));

System.out.println("Month 5: " + monthName(5));

System.out.println("Month 6: " + monthName(6));

System.out.println("Month 7: " + monthName(7));

System.out.println("Month 8: " + monthName(8));


System.out.println("Month 9: " + monthName(9));

System.out.println("Month 10: " + monthName(10));

System.out.println("Month 11: " + monthName(11));

System.out.println("Month 12: " + monthName(12));

System.out.println("Month 43: " + monthName(43));

5. Scrieţi un program care calculează aria a patru forme geometrice diferite:

triunghiuri, pătrate, dreptunghiuri, şi cercuri. Trebuie să creaţi şi să folosiţi

următoarele metode:

public static double areaCircle(int radius) // returns

the area of a circle

public static int areaRectangle(int length, int width) // returns

the area of a rectangle

public static int areaSquare(int side) // returns

the area of a square

public static double areaTriangle(int base, int height) // returns

the area of a triangle


Programul va prezenta un meniu din care utilizatorul să aleagă care din volume să le

calculeze, apoi îi va cere valorile necesare pentru forma respectivă (lungime, lăţime,

raza, etc.). Apoi va trebui să transmiteţi parametrii respectivi în metodă şi să afişeze

aria rezultată.

Observaţi că nu trebuie să faceţi citirea de valori în interiorul funcţiilor, şi nici nu

trebuie să afişaţi valorile rezultate în interiorul acestora. Toate citirile şi afişările se

vor face în metoda main(), iar valorile trebuie trimise prin parametrii, respectiv

întoarse din funcţie.

Forma Geometrică Formula


Pătrat A=s*s
Dreptunghi A=l*w
Triunghi A=b*h/2
Cerc A = pi * r * r

Veţi avea nevoie de valorea lui pi pentru aria cercului. Aceasta poate fi folosită prin

constanta Math.PI.

Programul va trebui să ruleze la infinit până când utilizatorul decide să iasă din

acesta.

6. Funcţii pentru apelul alfabetului - un alt program care conţine doar apeluri de

metode.

public class FunctionCallAlphabet {

public static void main(String[] args) {

// ???? a( ???? ); // displays a word beginning with A


// ???? b( ???? ); // returns the word to be displayed

// ???? c( ???? ); // pass it 'true' and it will display

a word beginning with C

// ???? d( ???? ); // displays a word beginning with D

// ???? e( ???? ); // pass it the number of letters to

display (9)

// ???? f( ???? ); // displays the word you pass it

beginning with "F"

// ???? g( ???? ); // returns the word to be displayed

// ???? h( ???? ); // tell it how many times to display

the word (1)

System.out.println();

// ???? i( ???? ); // pass it any integer and it will

display a word beginning with I

// ???? j( ???? ); // returns a different word depending

on what you pass it (1-3)

// ???? k( ???? ); // displays a word beginning with K


// ???? l( ???? ); // displays a different word depending

on the two booleans you pass it

// ???? m( ???? ); // displays a different word depending

on the two booleans you pass it

// ???? n( ???? ); // displays the word you pass it

beginning with "N"

// ???? o( ???? ); // displays a word beginning with O

and returns a useless value

// ???? p( ???? ); // returns the word to be displayed

// ???? q( ???? ); // displays the word

System.out.println();

// ???? r( ???? ); // returns a different word depending

on if you pass it 'true' or 'false'

// ???? s( ???? ); // pass it the number of letters to

display (6)

// ???? t( ???? ); // displays the word you pass it

beginning with "T"


// ???? u( ???? ); // returns the word to be displayed

// ???? v( ???? ); // tell it how many times to display

the word (1)

// ???? w( ???? ); // pass it any integer and it will

display a word beginning with W

// ???? x( ???? ); // returns a different word depending

on what you pass it (1-2)

// ???? y( ???? ); // displays a word beginning with Y

// ???? z( ???? ); // returns a different word depending

on which two boolean values you pass it

System.out.println();

/**************************************

* Don't change anything below here!! *

*************************************/

public static void a() {


System.out.print("Ant ");

public static String b() {

return "Banana ";

public static void c(boolean doit) {

if (doit)

System.out.print("Crocodile ");

public static void d() {

System.out.print("Doggie ");

}
public static void e(int howmany) {

String s;

s = "Elephant ";

int x = 0;

while (x <howmany) {

System.out.print(s.charAt(x));

x = x + 1;

public static void f(String word) {

System.out.print(word + " ");

public static String g() {

return "Gorilla ";


}

public static void h(int reps) {

int x = 0;

while (x <reps) {

System.out.print("Horseradish ");

x = x+1;

public static void i(int ignoredparameter) {

ignoredparameter = 32;

String space = Character.toString((char) ignoredparameter);

System.out.print("Ice_cream" + space);

}
public static String j(int whichone) {

if (whichone == 1)

return "Jambalaya ";

else if (whichone == 2)

return "Juniper ";

else

return "Jackrabbit ";

public static void k() {

// the bird OR the fruit

System.out.print("Kiwi ");

public static void l(boolean a, boolean b) {

if (a && b)
System.out.print("Lettuce ");

else

System.out.print("Lhasa_apso ");

public static void m(boolean a, boolean b) {

if (a || b)

System.out.print("Mango ");

else

System.out.print("Monkey! ");

public static void n(String word) {

System.out.print(word + " ");

}
public static int o() {

System.out.print("Orangutan ");

return 1; // just for kicks; the return value

doesn't mean anything

public static String p() {

return "Parrot ";

public static void q() {

System.out.print("Quail ");

public static String r(boolean first) {

if (first)

return "Rabbit ";


else

return "Radish ";

public static void s(int howmany) {

String s;

s = "Snake ";

int x = 0;

while (x <howmany) {

System.out.print( s.charAt(x) );

x = x+1;

public static void t(String word) {

System.out.print(word + " ");


}

public static String u() {

return "Ugli_fruit ";

public static void v(int reps) {

int x = 0;

while (x <reps) {

System.out.print("Valentine_candy ");

x = x + 1;

public static void w(int ignoredparameter) {

ignoredparameter = 32;
String space = Character.toString((char) ignoredparameter);

System.out.print("Walrus" + space);

public static String x(int whichone) {

if (whichone == 1)

return "X_files ";

else

return "X_men ";

public static void y() {

System.out.print("Yams ");

public static String z(boolean a, boolean b) {


if (a || b)

return "Zanahorias ";

else

return "Zebra ";

7. Scrieţi un program care vă afişează un meniu cu 4 opţiuni. Ar putea arăta în felul

următor:

Ye Olde Keychain Shoppe

1. Add Keychains to Order

2. Remove Keychains from Order

3. View Current Order

4. Checkout
Please enter your choice: 1

ADD KEYCHAINS

1. Add Keychains to Order

2. Remove Keychains from Order

3. View Current Order

4. Checkout

Please enter your choice: 3

VIEW ORDER

1. Add Keychains to Order

2. Remove Keychains from Order

3. View Current Order


4. Checkout

Please enter your choice: 4

CHECKOUT

Va trebui să creaţi metode pentru fiecare dintre cele patru opţiuni din meniu.

Introducerea unuia din numere va chema funcţia respectivă. Acest exerciţiu nu

necesită să aveţi funcţionalităţi în niciuna din fucnţii, cu excepţia funcţionalităţii din

meniu. Nu este necesar să programaţi logica necesară pentru adăugarea sau

ştergerea breloacelor, pentru a vizualiza comanda sau pentru checkout.

Funcţiile ar trebui să aibă numele addKeychains(), removeKeychains(), viewOrder()

şi checkout(). Fiecare din ele trebuie să afişeze un mesaj corespunzător.

Utilizatorul ar trebui să poată introduce alegeri până la apelul funcţiei checkout().

Când funcţia checkout() este apelată, programul trebuie să se termine.

8. Următorul pas este să facem magazinul de breloace să funcţioneze.

Ye Olde Keychain Shoppe

1. Add Keychains to Order


2. Remove Keychains from Order

3. View Current Order

4. Checkout

Please enter your choice: 1

You have 0 keychains. How many to add? 3

You now have 3 keychains.

1. Add Keychains to Order

2. Remove Keychains from Order

3. View Current Order

4. Checkout

Please enter your choice: 2


You have 3 keychains. How many to remove? 1

You now have 2 keychains.

1. Add Keychains to Order

2. Remove Keychains from Order

3. View Current Order

4. Checkout

Please enter your choice: 3

You have 2 keychains.

Keychains cost $10 each.

Total cost is $20.

1. Add Keychains to Order

2. Remove Keychains from Order


3. View Current Order

4. Checkout

Please enter your choice: 4

CHECKOUT

What is your name? Biff

You have 2 keychains.

Keychains cost $10 each.

Total cost is $20.

Thanks for your order, Biff!

Veţi avea nevoie de 2 noi variabile în funcţia main, una pentru stocarea numărului de

breloace, şi unul pentru a stoca preţul pentru un breloc. Preţul ar trebui să fie de 10

RON.

 addKeychains() va primi ca parametru un int şi va returna un int. Va cere

utilizatorului numărul de breloace pe care să îl adauge la comandă, şi va returna

noul număr de breloace.


 removeKeychains() va primi ca parametru un int şi va avea tipul returnat int.

Va cere utilizatorului numărul de breloace care să îl şteargă din comandă şi va

returna numărul de breloace rămase.

 viewOrder() va primi ca parametru 2 variabile de tipul int şi va avea ca tip

returnat void. Va afişa, pe 3 linii diferite, numărul de breloace din comandă, preţul

pentru fiecare breloc şi costul total al comenzii.

 checkout() va avea ca parametrii 2 variabile de tip int, iar tipul returnat va fi

void. Va cere utilizatorului numele acestuia pentru a face corect livrarea, va afişa

informaţii referitoare la comandă, iar la final va mulţumi utilizatorului folosind

numele acestuia pentru comanda făcută.

9. Funcţii pentru paritate

Scrieţi o funcţie care arată în modul următor:

public static boolean isEven(int n)

Funcţia va trebui să returneze valoarea true dacă n este un număr par (se divide

perfect la 2) şi false altfel.

Scrieţi şi metoda următoare:

public static boolean isDivisibleBy3(int n)

Funcţia va întoarce valoarea true dacă numărul este divizibil cu 3, şi false altfel.

Scrieţi în funcţia main() o buclă for care generează numere de la 1 la 20. Folosiţi

instrucţiuni if în interiorul acestei bucle pentru a marca numerele care sunt pare prin
semnul "<" şi cu un "=" numerele divizibile cu 3 şi cu "both" dacă numerele sunt

divizibile şi cu 2 şi cu 3.

10. Găsirea numerelor prime.

Scrieţi o funcţie de tipul următor:

public static boolean isPrime(int n)

Această funcţie va trebui să returneze valoarea true dacă n este prim şi false altfel.

Un număr este prim dacă acesta este divizibil doar cu 1 şi cu el însăşi. Putem rezolva

problema prin folosirea unei bucle for în interiorul funcţiei.

Faceţi bucla for să ruleze de la 2 până la n. În interiorul buclei, folosiţi o instrucţiune if

pentru a determina dacă n este divizibil prin contorul din bucla.

Dacă găsiţi orice număr la care numărul dat este divizibil, puteţi returna false fără a

termina bucla.

Dacă bucla se termină şi nu găseşte niciun număr cu care să îl dividă, atunci putem

returna valoarea true.

După ce aţi terminat e scris funcţia, scrieţi metoda main() care conţine o altă buclă

for. Afişaţi toate numerele de la 2 la 20, şi afişaţi în dreptul numerelor prime semnul

"<".

1. Pachete
Pachetele sunt folosite pentru a grupa clasele în funcţie de modul în care acestea comunică.
Pentru a folosi pachete, se va declara pachetul pe prima linie din fişierul clasei. Pachetele
grupează clasele folosite în foldere în interiourul proiectului, în folderul de fişiere surse.

Pentru a denumi un pachet, formatul standard este numele paginii web specificat invers
(exemplu pentru jademy.ro, avem pachetul ro.jademy) (mai ales în Android). Dacă avem
pachetul ro.jademy şi câteva clase în acest pachet (Test.java), atunci structura proiectului va fi
în felul următor: în folderul src, vom avea subfolderul ro, în subfolderul ro, vom avea
subfolderul jademy, iar în subfolderul jademy vom avea clasa Test.java

Pentru a folosi o clasă din acelaşi pachet, aceasta este recunoscută automat. Pentru a folosi
clase din pachete diferite, trebuie să importăm clasa respectivă în clasa în care vrem s-o
folosim. Deci vom folosi un import.

2. Nivele de Acces
Pentru a accesa câmpurile şi metodele în alte clase şi pachete, dar şi pentru a restricţiona
accesul la acestea, putem folosi o serie de modificatori.

Modificator Clasă Pachet Subclasă Restul Lumii


public Y Y Y Y
protected Y Y Y N
fără modificator Y Y N N
private Y N N N
Tabelul de mai sus ne indică unde putem să folosim câmpurile şi metodele declarate cu
modificatorii respectivi.

În cazul modificatorului public, elementele respective pot fi accesate din orice alta clasă
(indiferent de pachetul în care se află). În cazul indentificatorului private, acestea nu pot fi
accesate decat în clasa în care au fost declarate.

Câmpurile şi metodele care au modificatorul protected sunt vizibile în clasa în care au fost
create, în subclase şi în acelaşi pachet, iar cele care nu au niciun modificator sunt vizibile doar
în clasă şi în pachet. Acest modificator se mai numeşte şi modificator de pachet.

Un atribut sau o metodă cu modificator de acces protected poate fi văzut/ă și din alt pachet,
dacă clasa care vrea să îl/o vadă este subclasă a clasei care conține acest atribut/metodă, adică
dacă o moștenește.

În superclasă :

 notăm atributele cu private


 notăm unele metode specifice cu protected.
Astfel, aceste metode se vor putea vedea din alte clase din pachet, dar nu se vor putea vedea
de alte clase din alte pachete, cu excepția claselor care moștenesc superclasa.

3. Încapsulare
În mod normal, câmpurile unei clase sunt private pentru a nu avea acces direct la

ele. Pentru a avea acces la ele, vom folosi metode publice de set şi de get.

public class Employee {


private String name;

private String hireDate;

private double salary;

public String getName() {

return name;

public void setName(String name) {

this.name = name;

public String getHireDate() {

return hireDate;

}
public void setHireDate(String hireDate) {

this.hireDate = hireDate;

// we do not want to modify the salary directly

public void giveRaise(double percentage) {

salary = salary + salary * percentage / 100;

public double getSalary() {

return salary;

Definirea claselor
Clasele se definesc de obicei în fişiere diferite. Putem defini 2 clase în acelaşi fişier,

cu condiţia ca doar una din ele să fie publică. Clasa care este publică trebuie să aibă

acelaşi nume cu fişierul în care este definită. Putem defini o clasă în interiorul altei

clase sau după aceasta.

În cazul claselor care se află în interiorul altei clase, ele pot fi statice. Dacă sunt

statice, se denumesc clase statice imbricate. Dacă sunt clase imbricate ne-statice se

mai numesc şi clase interioare. Putem avea clase şi în interiorul metodelor. Acestea

poartă denumirea de clase locale.

public class Employee {

private HireDate date;

String getDate() {

return date.year + "-" + date.month + "-" + date.day;

// metoda nu poate fi statica pentru ca foloseste o clasa

interioara nestatica

Mug getMug(String color, int size) {

Mug mug = new Mug();


mug.color = color;

mug.size = size;

return mug;

// metoda poate fi statica sau nestatica

// foloseste o clasa statica

static OfficeSpace getOfficeSpace() {

return new OfficeSpace();

public void createTie() {

// clasa locala

class Tie {

String color = "White";

}
System.out.println(new Tie().color);

// fiecare angajat are o cana

class Mug {

String color;

int size;

// toti angajatii impart acelasi birou

static class OfficeSpace {

double size;

int color;

}
// data de angajare pentru fiecare angajat

class HireDate {

int day;

int month;

int year;

Clase în Acelaşi Fişier


Putem scrie mai multe clase într-un singur fișier. Atunci, o singură clasă poate să fie

public, și anume clasa care poartă și numele fișierului. Restul claselor trebuie să aibă

modificator de acces default (adică nimic). Dezavantajul este că clasa nu se vede din

alte pachete.

Dacă vă uitați în folderul unde este proiectul, veți vedea în subfolderul src în pachetul

respectiv un singur fișier .java, iar în subfolderul bin în pachetul respectic mai multe

fișiere .class.

Apelul claselor interioare


Exemplu pentru clasa de mai sus:

public class EmployeeTest {

public static void main(String[] args) {


Employee e = new Employee();

// un Mug nestatic e creat cu o instata a clasei Employee

Employee.Mug mug = e.new Mug();

// un OfficeSpace nestatic este creat folosind clasa

Employee

Employee.OfficeSpace space = new Employee.OfficeSpace();

// putem crea oricate OfficeSpace, chiar daca clasa este

statica

Employee.OfficeSpace space2 = new Employee.OfficeSpace();

// HireDate e in acelasi fisier cu Employee, dar nu e legat

de clasa

HireDate date = null;

}
}

Blocuri
într-o clasă putem avea blocuri care nu sunt legate de nicio metodă în interiorul

clasei. Acestea se execută când instanţiăm o clasă, înainte de a apela constructorul.

În cazul în care acestea sunt statice, se execută o singură dată, atunci când folosim

pentru prima oară clasa.

public class Duck {

static String staticSound = "MAC!";

String classSound = "maca";

static {

System.out.println("static block: " + staticSound);

System.out.println("instance block: " + classSound);


}

public Duck() {

System.out.println("Duck Constructor");

System.out.println("Block within constructor");

public class DuckTest {

public static void main(String[] args) {

Duck duck1 = new Duck();

Duck duck2 = new Duck();

}
Pentru exemplul de mai sus, se vor afişa următoarele linii în consolă:

static block: MAC!

instance block: maca

Duck Constructor

Block within constructor

instance block: maca

Duck Constructor

Block within constructor

Cum se crează un obiect și variabilele acestuia:

La prima întâlnire a clasei la parcurgerea codului la rulare:

 Se crează variabilele statice pe static storage și se inițializează conform

blocului static de inițializare (dacă există)

Apoi, la întâlnira comenzii new:

 Se crează variaibla de referință pe stack

 Se alocă memorie pe heap

 Toate variabilele de instanță se inițializează la valoarea default (0, 0.0, false,

'\u0000' sau null)

 Dacă în linia de declarare a unei variabile de instanță scrie int a = 10; se

inițilizează la 10
 Rulează blocul nestatic de inițializare

 Abia acum rulează constructorul: toate variabilele de instanță primesc

valoarea din constructor

 La sfârșit este trecută adresa obiectului nou creat în variabila de referință de

pe stack (se face pointarea către obiectul de pe heap)

This – referinţă pentru clasa curentă


Keywordul this face referire la obiectul în cauză.

Dacă avem clasa Autoturism.

public class Autoturism {

private String marca;

private String model;

//constructor / constructori

//getteri, setteri

public setMarca (String m) {


this.marca = m;

Şi metoda main().

public static void main(String [] args){

//......

Autoturism masina1 = new Autoturism();

masina1.setMarca( ”Dacia” );

Atunci când se execută linia masina1.setMarca( ”Dacia” ); se va apela metoda

setMarca(String m) a obiectului masina1 în care se va seta atributul marca a

obiectului masina1 la valoarea ”Dacia”.

Deci în cazul nostru, this se referă la obiectul masina1.

Alt exemplu este cazul în care Eclipse ne genereaza metoda equals() pentru o clasă:

public class Computer {

private String marca;


private int ram;

private int frecventaProcesor;

//constructor / constructori

//getteri, setteri

@Override

public boolean equals(Object obj) {

if (this == obj) return true;

//........

Apoi, în metoda main() avem:

public static void main (String[] args) {


Computer computer1 = new Computer (“Acer”, 4, 2000);

Computer computer2 = new Computer (“Acer”, 4, 2000);

//Acum dacă facem

System.out.println(computer1 == computer2);

// ne va da false pentru că sunt 2 obiecte (de tip computer)

diferite, cu adrese diferite în memorie.

// == verifică adresele din memorie.

//Dar dacă facem

System.out.println ( computer1.equals(computer2) );

// vom obține true, pentru că acum (după ce am suprascris

metoda equals()

// în clasa Computer), equals() compară caracteristicile

celor 2 computere.

// Dacă nu suprascriam metoda equals() ca să compare

caracteristicile lor,
// am fi obținut tot false, ca în cazul lui == pentru că

// by default, equals() în clasa Object face exact la fel ca

și ==,

// adică compară adresele din memorie a celor 2 obiecte.

Observați linia if (this == obj) return true; din metoda equals().

Când executăm linia computer1.equals(computer2) practic, apelăm metoda equals()

a obiectului computer1. Deci, this, este însuși computer1.

obj este o variabilă care preia ca referință pe computer2. Adică, obj este o variabilă

locală de pe stack care va pointa către același obiect către care pointează și

computer2.

Deci, ce însemană if (this == obj)?

Se testează dacă computer1 și computer2 conțin aceeași adresă, cu alte cuvinte

dacă pointează către același obiect. Adică dacă computer1 și computer2 sunt unul și

același obiect. (În cazul nostru, nu sunt unul și același obiect, sunt obiecte diferite,

dar au aceleași caracteristici).

Pasarea unei instanțe cu ajutorul lui this. Asocierea


Presupunem că avem următorul sistem.

 Furnizorul conține mai mulți clienți.

 Un client conține mai multe magazine.


 Clientul are și el o referință către furnizor.

 Magazinul are și el o referință către client.

 Clientul știe și el cine e Furnizorul lui, are o referință către furnizor.

 Magazinele știu și ele cine este proprietarul (clientul) şi au o referință către

client.

public class Main {

public static void main (String[] args){


Furnizor furnizor1 = new Furnizor (“Pepsi”);

public class Furnizor {

String nume;

Client client1;

Client client2;

public Furnizor (String nume) {

this.nume = nume;

client1 = new Client (“La Mama”, this); // pasăm

instanța Furnizorului

client2 = new Client (“Mega Image”, this);

public class Client {


String nume;

Magazin magazin1;

Magazin magazin2:

Furnizor furnizor; // clientul știe cine e Furnizorul

public Client(String nume, Furnizor furnizor) {

this.nume = nume;

magazin1 = new Magazin (this); // pasăm instanța

clientului, adică pe c1 sau c2.

magazin2 = new Magazin (this);

this.furizor = furnizor;

public class Magazin {

Client client; //magazinul știe cine e proprietarul.


public Magazin (Client client) {

this.client = client;

Iată un exemplu în care vom extinde clasa Student pentru a cunoaște grupa din care

face parte:

class Grupa {

private int crt;

private Student[] studenti;

Grupa (){

crt = 0;

studenti = new Student[10];

}
public boolean addStudent(String nume, int media) {

if (crt <studenti.length) {

studenti[crt++] = new Student(this, nume, media);

return true;

return false;

class Student {

private String name;

private int averageGrade;

private Grupa grupa;

public Student(Grupa grupa, String name, int averageGrade) {


this.grupa = grupa;

this.name = name;

this.averageGrade = averageGrade;

Tranferul parametrilor prin valoare şi


referinţă
 Referințe. Implicații în transferul de parametri

După cum știți, obictele se alocă pe heap. Pentru ca un obiect să poată fi folosit, este

necesară cunoașterea adresei lui. Această adresă, se reține într-o referinţă. Limbajul

Java nu permite lucrul direct cu adresele de memorie, deoarece s-a considerat că

această facilitate introduce o complexitate prea mare de care programatorul poate fi

scutit. Totuși, în Java există noțiunea de referințe, oferind un mecanism de gestiune

(management) transparent.

Astfel, declararea unui obiect:

Student st;

creează o referință care poate indica doar către o zonă de memorie inițializată cu

patternul clasei Student fără ca memoria respectivă să conțină date utile. Astfel, dacă

după declarație facem un acces la un câmp sau apelăm o funcție-membru,

compilatorul va semnala o eroare, deoarece referința nu indică încă spre vreun


obiect din memorie. Alocarea efectivă a memoriei și inițializarea acesteia se

realizează prin apelul constructorului împreună cu cuvântul-cheie new.

Managementul transparent al pointerilor implică un proces automat de alocare și

eliberare a memoriei. Eliberarea automată poartă și numele de Garbage Collection și

există o componentă separată a JRE-ului care se ocupă cu eliberarea memoriei ce

nu mai este utilizată.

Un fapt ce merită discutat este semnificația atribuirii de referințe. În exemplul de mai

jos:

Student s1 = new Student("Gigel", 6);

s2 = s1;

s2.averageGrade = 10;

System.out.println(s1.averageGrade);

se va afișa 10. Motivul este că s1 și s2 sunt două referințe către ACELAŞI obiect din

memorie. Orice modificare făcută asupra acestuia prin una din referințele sale va fi

vizibilă în urma accesului prin orice altă referință către el. În concluzie, atribuirea de

referințe nu creează o copie a obiectului, cum s-ar fi putut crede inițial.

Transferul parametrilor la apelul de funcții este foarte importat de înțeles. Astfel:

 pentru tipurile primitive se transferă prin COPIERE pe stivă: orice modificare în

funcția apelată NU VA FI VIZIBILĂ în urma apelului.

 pentru tipurile definite de utilizator și instanțe de clase în general, se COPIAZĂ

REFERINȚA pe stivă: referința indică spre zona de memorie a obicetului, astfel


că schimbările asupra câmpurilor vor fi vizibile după apel, dar reinstanțieri

(expresii de tipul: st = new Student()) în apelul funcției și modificările făcute după

ele, NU VOR FI VIZIBILE după apel, deoarece ele modifică o copie a referinței

originale.

class TestParams {

void static modificaReferinta(Student st) {

st = new Student("Gigel", 10)

st.averageGrade = 10;

void static modificaObiect(Student s) {

s.averageGrade = 10;

public static void main(String[] args) {

Student s = new Student("Andrei", 5);

modificaReferinta(s); // 1

System.out.println(s.getName()); // 1'
modificaObiect(s); // 2

System.out.println(s.averageGrade); // 2'

Astfel, apelul (1) nu are niciun efect în metoda main pentru că metoda

modificaReferinta are ca efect asignarea unei noi valori referinței s, trimisă prin

valoare. Linia (1') va afișa textul: "Andrei".

Apelul (2) metodei modificaObiect are ca efect modificarea obiectului referit de s cu

ajutorul metodei setName sau prin acces direct. Linia (2') va afișa textul: 10.

Exemplu

public static void modifica(int nr) {

nr = 10;

public static void modifica(Bicicleta bicicleta) {

bicicleta.marca = "BMX";

}
public static void main(String[] args) {

int nr = 5;

modifica(nr);

Bicicleta bici = new Bicicleta();

bici.marca = "RK";

modifica(bici);

Enum
Enum-urile au fost introduse în Java 5.0. Scopul lor este să restricţioneze o variabilă

la câteva valori predefinite. Valorile din această listă enumerată poartă numele de

enumeraţii.
Cu ajutorul enum-urilor, este posibil să reduceţi numărul bug-urilor din cod. De

exemplu, daca considerăm o aplicaţie pentru un magazin de sucuri naturale, ar fi

posibil să restricţionăm mărimea paharelor la mic, mediu şi mare. Acest lucru ne va

asigura că nimeni nu va putea comanda altceva înafară de aceste 3 mărimi.

Class FreshJuice{

enum FreshJuiceSize{ SMALL, MEDUIM, LARGE }

FreshJuiceSize size;

public class FreshJuiceTest{

public static void main(String args[]){

FreshJuice juice =new FreshJuice();

juice.size =FreshJuice.FreshJuiceSize.MEDUIM ;

Observaţie: enumurile pot fi declarate în fişiere proprii sau în interiorul unei clase.

Putem defini şi pentru enum-uri metode, câmpuri şi constructori.

Constructori
this() sau this(parametri)

se apeleaza un alt constructor din aceeași clasă (overloading =

supraîncărcarea)

super() sau super(parametri)

se apelează un constructor din clasa mamă pe care noi o extindem

Atribute
this.atribut = valoare / variabilă locală;

ex: this.ram = ram;

referire la un atribut al clasei.

super.atribut

Referirea unui atribut din clasa mamă pe care noi o extindem

Metode
this.metoda() / this.metroda(parametri)

apelarea unei metode din această clasă, cum ar fi o metodă

overloaded / suprascrisă

practic, this nu este obligatoriu

super.metoda() / super.metoda(parametri)
apelarea unei metode din clasa mamă pe care noi o extindem.

Referinţa this
super nu se comportă ca o referinţă.

În cadrul unei clase, this se referă la obiectul în cauză.

Exemplu 1
În main():

Class Catel extends Animal{...}

Catel toto = new Catel();

toto.scoateSunet();

iar in metoda scoateSunet() din clasa Animal scriem:

if (this instanceof Catel){

System.out.println("Ham");

} else if (this istanceof Pisica){

System.out.println("Miau");

Exemplu 2
În metoda main():
Furnizor furnizor1 = new Furnizor (“Pepsi”);

Clasa Furnizor:

public class Furnizor {

String nume;

Client client1;

Client client2;

public Furnizor (String nume) {

this.nume = nume;

client1 = new Client (“La Mama”, this);

// clinentul va avea si el o referire catre furnizor

//pasăm instanța Furnizorului

client2 = new Client (“Mega Image”, this);

}
Polimorfismul
Polimorfismul este abilitatea unui obiect de a lua mai multe forme. Cea mai întâlnită

formă de utilizare a polimorfismului în POO apare când o clasă părinte e folosită ca

variabilă referinţă pentru a reţine un obiect dintr-o clasă copil.

Fiecare obiect din Java care poate trece un test de IS-A este considerat ca fiind

polimorfic. În Java, toate obiectele sunt polimorfice, având în vedere că pot trece

testul IS-A atât pentru tipul lor cât şi pentru clasa Object.

Este important de ştiut că singura modalitate de a accesa un obiect este printr-o

variabilă referinţă. O referinţă poate fi de un singur tip. O dată declarată, tipul

variabile referinţă nu mai poate fi schimbat.

Variabilei referinţă îi pot fi reatribuite alte obiecte dacă aceasta nu a fost declarată ca

final. Tipul variabilei referinţă va determina metodele pe care le poate apela din

obiect.

O variabilă referinţă poate face referire la oricare obiect de tipul declarat, sau la unul

din subtipurile tipului declarat. O variabilă referinţă poate fi declarată ca o clasă sau o

interfaţă.

public interface Vegetarian{}

public class Animal{}

public class Deer extends Animal implements Vegetarian{}

Acum, clasa Deer poate fi considerată ca fiind polimorfică având în vedere că are o

moştenire multiplă. Următoarele sunt valabile pentru exemplul de mai sus:


 A Deer IS-A Animal

 A Deer IS-A Vegetarian

 A Deer IS-A Deer

 A Deer IS-A Object

Când aplicăm regulile pentru variabilele referinţă pentru o referinţă de tipul Deer,

următoarele operaţii sunt legale:

 Deer d =new Deer();

 Animal a = d;

 Vegetarian v = d;

 Object o = d;

Toate variabilele referinţă d, a, v, o fac referire la acelaşi obiect Deer din heap.

Reţinerea în Array
Se consideră clasele următoare:

public class Animal{

public void scoateSunet(){

System.out.println(“Sunet generic”);

public class Vrabie extends Animal{

@Override //= SUPRASCRISĂ


public void scoateSunet(){

System.out.println(“Cip cirip cirip”);

public void ciripeste(){

System.out.println(“Vrabiuta ciripeste.”);

Animal a;

a = new Vrabie();

Definirea unei variabile de tip Animal poate fi utilă, deoarece, uneori îi putem pasa

diferite Animale.

Poate exemplul următor este mai elocvent :

//crearea unui array de animale

Animal[] animale = new Animal[5];

// animale[0] este un Animal. Cu toate acestea putem introduce o

Vrabie, un Tigru, o Pisica


animale[0] = new Vrabie();

animale[1] = new Tigru();

animale[2] = new Pisica();

animale[3] = new Leu();

animale[4] = new Vrabie();

Animal animal = new Vrabie();

Deși poinetază către un obiect de tip Vrabie, totuși variabila animal este un Animal.

Gândiți-vă, ea poate pointa către diferite animale. De aceea, animal nu vede

pe ciripeste(): animal.ciripeste();.

În schimb, dacă apelăm animal.socateSunet(), compilatorul se va referi la

metoda scoateSunet() din clasa Animal, dar la rulare, JVM va sesiza că

metoda socateSunet() este SUPRASCRISĂ (OVERRIDEN) în clasa Vrabie și o va

selecta pe aceasta.

Alegerea metodei suprascrise la rulare, se numește Dynamic / Virtual Method

Invocation.

animal.socateSunet(); // Cip cirip cirip


Instanceof
Prin folosirea cuvântului cheie extends, subclasele vor fi capabile să moştenească

toate proprietăţile superclasei, cu excepţia celor private. Ne putem asigura că

Mammal chiar este un Animal folosind operatorul instanceof.

public class Dog extends Mammal{

public static void main(String args[]){

Animal a = new Animal();

Mammal m = new Mammal();

Dog d = new Dog();

System.out.println(m instanceof Animal);

System.out.println(d instanceof Mammal);

System.out.println(d instanceof Animal);

}
Upcasting și Downcasting
Convertirea unei referințe la o clasă derivată într-una a unei clase de bază poartă

numele de upcasting. Upcasting-ul este făcut automat și nu trebuie declarat explicit

de către programator.

Exemplu de upcasting:

class Instrument {

public void play() {}

// Wind objects are instruments

// because they have the same interface:

public class Wind extends Instrument {

public static void main(String[] args) {

Wind flute = new Wind();

tune(flute); // !! Upcasting automat

static void tune(Instrument i) {


i.play();

Deși obiectul Flute este o instanță a clasei Wind, acesta este pasat ca parametru în

locul unui obiect de tip Instrument, care este o superclasă a clasei Wind. Upcasting-

ul se face la pasarea parametrului. Termenul de upcasting provine din diagramele de

clase (în special UML) în care moștenirea se reprezintă prin 2 blocuri așezate unul

sub altul, reprezentând cele 2 clase (sus este clasa de bază iar jos clasa derivată),

unite printr-o săgeată orientată spre clasa de bază.

Deci, oriunde ni se cere un tip de data, putem folosi tipul respectiv, sau orice

subclasă derivată din tipul cerut. Ca regulă generală, obiectul transmis, trebuie sa fie

cel puţin derivat din clasa respectivă.

Downcasting este operația inversă upcast-ului și este o conversie explicită de tip în

care se merge în jos pe ierarhia claselor (se convertește o clasă de bază într-una

derivată). Acest cast trebuie făcut explicit de către programator. Downcasting-ul este

posibil numai dacă obiectul declarat ca fiind de o clasă de bază este, de fapt,

instanța clasei derivate către care se face downcasting-ul.

Iată un exemplu în care este folosit downcasting:

class Animal {

public void eat() {


System.out.println("Animal eating");

class Wolf extends Animal {

public void howl() {

System.out.println("Wolf howling");

public void eat() {

System.out.println("Wolf eating");

class Snake extends Animal {

public void bite() {

System.out.println("Snake biting");

}
}

class Test {

public static void main(String[] args) {

Animal a [] = new Animal[2];

a[0] = new Wolf();

a[1] = new Snake();

for (int i = 0; i <a.length; i++) {

a[i].eat(); // 1

if (a[i] instanceof Wolf)

((Wolf)a[i]).howl(); // 2

if (a[i] instanceof Snake)

((Snake)a[i]).bite(); // 3

}
}

În liniile marcate cu 2 și 3 se execută un downcast de la Animal la Wolf, respectiv

Snake pentru a putea fi apelate metodele specifice definite în aceste clase. Înaintea

execuției downcast-ului (conversia de tip la Wolf respectiv Snake) verificăm dacă

obiectul respectiv este de tipul dorit (utilizând operatorul instanceof). Dacă am

încerca să facem downcast către tipul Wolf al unui obiect instanţiat la Snake mașina

virtuală ar semnala acest lucru aruncând o excepție la rularea programului.

Apelarea metodei eat() (linia 1) se face direct, fără downcast, deoarece această

metodă este definită și în clasa de bază. Datorită faptului că Wolf suprascrie

(overrides) metoda eat(), apelul a[0].eat() va afișa “Wolf eating”. Apelul a[1].eat() va

apela metoda din clasă de bază (la ieșire va fi afișat “Animal eating”) deoarece a[1]

este instanţiat la Snake, iar Snake nu suprascrie metoda eat().

Upcasting-ul este un element foarte important. De multe ori răspunsul la întrebarea:

este nevoie de moștenire? este dat de răspunsul la întrebarea: am nevoie de

upcasting? Aceasta deoarece upcasting-ul se face atunci când pentru unul sau mai

multe obiecte din clase derivate se execută aceeași metodă definită în clasa părinte.

Se consideră următoarea ierarhie de clase:


//clasa Oras moștenește clasa Localitate

public class Oras extends Localitate{

//atribute în plus față de clasa Localitate

Up-casting

Oras oras = new Oras();

Localitate localitate = (Localitate) oras;

Pentru că orice Oraș este (is-a) Localitate, acest cast se face implicit de către

compilator. De aceea, se poate scrie și:

Localitate localitate = oras;


Localitatea poate fi un Oraș. localitate poate prelua un Oras. În această

„transformare”, localitate va avea mai puține atribute și metode decât Oras. Practic

„se pierd” atribute și metode, dar este o transformare posibilă.

Down-casting
Din exact același motiv, cazul invers nu este posibil:

Localitate localitate = new Localitate();

Oras oras = localitate;

localitate are mai puține atribute și metode. Variabila oras care preia obiectul

localitate nu are de unde să adauge atribute suplimentare necesare pentru Oras,

care nu există în obiectul nostru new Localitate().

În plus, deși orice Oraș este o Localitate, nu orice Localitate este neapărat un Oraș.

Nici castul Oras oras = (Oras)localitate; nu va funcționa. Chiar dacă trece de

compilare, va crăpa la rulare. localitate este un obiect de tip Localitate. Oras trebuie

să aibă atribute suplimentare, care, pur și simplu, nu exsită în cadrul lui localitate

care este de tip new Localitate().

Down-castingul este posibil, numai dacă, localitate pointează către un obiect de tip

Oraș! Precum în cazul în care avem un array sau arraylist de tip Localitate care este

populat cu diverse obiecte definite prin relația is-a Localitate, adică: Sate, Comune,

Orașe, Municipii. În acest caz, unele din localitățile din arraylist pointează către

obiecte de tip Oras:

localitati[3] = new Oras();


// adica

Localitate localitate = new Oras();

// doar în acest caz de poate face down-casting:

Oras orasDeLucru = (Oras)localitate;

Programul nu va crăpa la rulare, pentru că localitate pointa către un obiect de tip

Oras, și, deci, orasDeLucru va pointa și el către acesta.

În cazul parcurgerii unui arraylist de localități care pot fi sate sau comune sau orașe

sau municipii, vom face:

for (Localitate localitate : localitati){

if (localitate instanceof Oras){

Oras orasDeLucru = (Oras)localitate;

// acum putem apela metodele si atributele specifice

orasului

// pe care nu le puteam apela cu variabila localitate

de tip Localitate

// (variabila localitate vedea numai atributele

generice ale unei Localități)

System.out.println(orasDeLucru.getNumarDeLocuitori());
}

Clase abstracte
Fie urmǎtorul exemplu (Thinking in Java) care propune o ierarhie de clase pentru a

descrie o suitǎ de instrumente muzicale, cu scopul demonstrǎrii polimorfismului.

Instrument
Clasa Instrument nu este instanţiatǎ niciodată pentru cǎ scopul sǎu este de a stabili

o interfaţǎ comunǎ pentru toate clasele derivate. În acelaşi sens, metodele clasei de

bazǎ nu vor fi apelate niciodatǎ. Apelarea lor este ceva greşit din punct de vedere

conceptual.

Clase abstracte
Dorim sǎ stabilim interfaţa comunǎ pentru a putea crea funcţionalitate diferitǎ pentru

fiecare subtip şi pentru a şti ce anume au clasele derivate în comun. O clasǎ cu

caracteristicile enumerate mai sus se numeşte abstractǎ. Creǎm o clasǎ abstractǎ

atunci când dorim sǎ:

 manipulǎm un set de clase printr-o interfaţǎ comunǎ

 reutilizǎm o serie metode şi membrii din aceastǎ clasǎ în clasele derivate.

Metodele suprascrise în clasele derivate vor fi apelate folosind dynamic binding (late

binding). Acesta este un mecanism prin care compilatorul, în momentul în care nu

poate determina implementarea unei metode în avans, lasǎ la latitudinea JVM-ului

(maşinii virtuale) alegerea implementǎrii potrivite, în funcţie de tipul real al obiectului.

Aceastǎ legare a implementǎrii de numele metodei la momentul execuţiei stǎ la baza

polimorfismului.

Nu existǎ instanţe ale unei clase abstracte, aceasta exprimând doar un punct de

pornire pentru definirea unor instrumente reale. De aceea, crearea unui obiect al unei

clase abstracte este o eroare, compilatorul Java semnalând eroare in acest caz.

Metode abstracte
Pentru a exprima faptul cǎ o metodǎ este abstractǎ (adicǎ se declarǎ doar interfaţa

ei, nu si implementarea), Java foloseşte cuvântul cheie abstract:

abstract void f() { }

O clasǎ care conţine metode abstracte este numitǎ clasă abstractǎ. Dacǎ o clasǎ are

una sau mai multe metode abstracte atunci ea trebuie sǎ conţinǎ în definiţie cuvântul

abstract.
abstract class Instrument {

// ...

Deoarece o clasǎ abstractǎ este incompletǎ (existǎ metode care nu sunt definite),

crearea unui obiect de tipul clasei este împiedicatǎ de compilator.

Clase abstracte în contextul moştenirii


O clasǎ care moşteneşte o clasǎ abstractǎ este ea însǎşi abstractǎ dacă nu

implementeazǎ toate metodele abstracte ale clasei de bazǎ. Putem defini clase

abstracte care moştenesc alte clase abstracte şi tot aşa. O clasǎ care poate fi

instanţiatǎ (nu este abstractǎ) şi care moşteneşte o clasǎ abstractǎ trebuie sǎ

implementeze (defineascǎ) toate metodele abstracte pe lanţul moştenirii (ale tuturor

claselor abstracte care îi sunt “pǎrinţi”).

Este posibil sǎ declarǎm o clasǎ abstractǎ fǎrǎ ca ea sǎ aibǎ metode abstracte.

Acest lucru este folositor când declarǎm o clasǎ pentru care nu dorim instanţe (nu

este corect conceptual sǎ avem obiecte de tipul acelei clase, chiar dacǎ definiţia ei

este completǎ).
Interfeţe
INTERFETE

O interfaţă este o colecţie de metode abstracte. O clasă implementează o interfaţă,

moştenind aşadar metodele abstracte din interfaţă. O interfaţă nu e o clasă. Scrierea

unei interfeţe e similară cu scrierea unei clase, dar sunt concepte diferite. O clasă

descrie starea şi comportamentul unui obiect. O interfaţă conţine comportamentul pe

care o clasă îl implementează.

Interfeţele duc conceptul abstract un pas mai departe. Se poate considera cǎ o

interfaţǎ este o clasǎ abstractǎ purǎ: permite programatorului sǎ stabileascǎ o

“formǎ” pentru o clasǎ: numele metodelor, lista de argumente, valori întoarse, dar

farǎ nicio implementare. O interfaţǎ poate conţine câmpuri dar acestea sunt în mod

implicit static şi final.


Interfaţa este folositǎ pentru a descrie un protocol între clase: o clasǎ care

implementează o interfaţǎ va implementa metodele definite în interfaţǎ. Astfel orice

cod care foloseşte o anumitǎ interfaţǎ ştie ce metode pot fi apelate pentru acea

interfaţǎ.

Pentru a crea o interfaţǎ folosim cuvântul cheie interface în loc de class. La fel ca în

cazul claselor, interfaţa poate fi declaratǎ public doar dacǎ este definitǎ într-un fişier

cu acelaşi nume ca şi interfaţa. Dacǎ o interfaţǎ nu este declaratǎ public atunci

specificatorul ei de acces este package-private.

Pentru a defini o clasǎ care este conformǎ cu o interfaţǎ anume folosim cuvântul

cheie implements. Aceastǎ relaţie este asemǎnǎtoare cu moştenirea, cu diferenţa cǎ

nu se moşteneşte comportament, ci doar “interfaţa”.

Pentru a defini o interfaţǎ care moşteneşte altǎ interfaţǎ folosim cuvântul cheie

extends. După ce o interfaţǎ a fost implementatǎ, acea implementare devine o clasǎ

obişnuitǎ care poate fi extinsǎ prin moştenire.

Iată exemplul dat la clase abstracte, modificat pentru a folosi interfeţe:


interface Instrument {

// Compile-time constant:

int i = 5; // static & final

// Cannot have method definitions:

void play(); // Automatically public

String what();

void adjust();
}

class Suflat implements Instrument {

public void play() {

System.out.println("Suflat.play()");

public String what() {

return "Suflat";

public void adjust() {}

class Trompeta extends Suflat {


public void play() {

System.out.println("Trompeta.play()");

public void adjust() {

System.out.println("Trompeta.adjust()");

Implicit, specificatorul de acces pentru membrii unei interfeţe este public. Atunci când

implementǎm o interfaţǎ trebuie sǎ specificǎm cǎ funcţiile sunt public chiar dacǎ în

interfaţǎ ele nu au fost specificate explicit astfel. Acest lucru este necesar deoarece

specificatorul de acces implicit în clase este package-private, care este mai restrictiv

decât public.

O interfaţă e similară cu o clasă din umrătoarele puncte de vedere:

 O interfaţă poate conţine oricât de multe metode

 O interfaţă e scrisă într-un fişier cu extensia .java, iar numele interfeţei publice

trebuie să fie la fel cu numele clasei

 Bytecodeul interfeţei este generat în fişiere .class


 Interfeţele apar în pachete, iar bytecodul corespunzător trebuie să existe într-o

structură de directoare care este la fel cu numele pachetului

Dar, o interfaţă diferă de o clasă în următoarele moduri:

 Nu putem instanţia o interfaţă

 O interfaţă nu conţine constructori

 Toate metodele din interfaţă sunt abstracte

 O interfaţă nu poate conţine câmpuri de instanţă. Toate câmpurile care apar în

aceasta trebuie să fie atât static cât şi final.

 O interfaţă nu poate fi extinsă de o clasă; aceasta este implementată de clasă

 O interfaţă poate extinde mai multe interfeţe


Interfeţe pentru Tagging
Cel mai folosit caz de extindere a interfeţelor este atunci când interfaţa părinte nu

conţine nicio metodă. De exemplu, interfaţa MouseListener din pachetul

java.awt.event extinde java.util.EventListener, care este definită în modul următor:

package java.util;

public interface EventListener {}

O interfaţă fără metode mai este cunoscută şi ca tagging interface. Există două

principii de design pentru interfeţele pentru tagging:

 Creează un părinte comun: La fel ca interfaţa EventListener, care este extinsă

de multe interfeţe din Java, putem folosi interfeţele pentru tagging pentru a crea

un părinte comun pentru un grup de interfeţe. De exemplu, când o interfaţă

extinde EventListener, JVM ştie că interfaţa respectivă va fi folosită într-un

scenariu orientat pe evenimente.


 Adaugă un tip de date la o clasă: Acest caz este şi provenineţa termenului

tagging. O clasă care implementează o interfaţă pentru tagging, nu trebuie să

definească nicio metodă (având în vedere că interfaţa nu conţine niciuna), dar

clasa devine acum de tipul interfeţei prin polimorfism.


Moştenire multiplǎ în Java
Interfaţa nu este doar o formǎ “purǎ” a unei clase abstracte, ci are un scop mult mai

înalt. Deoarece o interfaţǎ nu specificǎ niciun fel de implementare (nu existǎ nici un

fel de spaţiu de stocare pentru o interfaţǎ) este normal sǎ “combinǎm” mai multe

interfeţe. Acest lucru este folositor atunci când dorim sǎ afirmǎm cǎ “X este un A, un

B si un C”. Acest deziderat este moştenirea multiplǎ.

interface Fighter {

void fight();

interface Swimmer {

void swim();

interface Flyer {

void fly();

class ActionCharacter {
public void fight() {}

class Hero extends ActionCharacter implements Fighter, Swimmer,

Flyer {

public void swim() {}

public void fly() {}

public class Adventure {

static void callFighter(Fighter fighter) {

fighter.fight();

static void callSwimmer(Swimmer swimmer) {

swimmer.swim();

}
static void callFlyer(Flyer flyer) {

flyer.fly();

static void callActionChar(ActionCharacter character) {

character.fight();

public static void main(String[] args) {

Hero hero = new Hero();

callFighter(hero); // Treat it as a Fighter

callSwimmer(hero); // Treat it as a Swimmer

callFlyer(hero); // Treat it as a Flyer

callActionChar(hero); // Treat it as an ActionCharacter

}
Se observǎ cǎ Hero combinǎ clasa ActionCharacter cu interfeţele Swimmer etc.

Acest lucru se realizeazǎ specificând prima dată clasa concretǎ (sau abstractǎ)

(extends) şi abia apoi implements.

Metodele clasei Adventure au drept parametri interfeţele Swimmer etc. si clasa

ActionCharacter. La fiecare apel de metodǎ din Adventure se face upcast de la

obiectul Hero la clasa sau interfaţa doritǎ de metoda respectivǎ.

Coliziuni de nume la combinarea interfeţelor

Combinarea unor interfeţe care conţin o metodǎ cu acelaşi nume este posibilǎ doar

dacǎ metodele nu au tipuri întoarse diferite şi aceeaşi listǎ de argumente. Totuşi este

preferabil ca în interfeţe diferite care trebuie combinate sǎ nu existe metode cu

acelaşi nume deoarece acest lucru poate duce la confuzii evidente (sunt amestecate

în acest mod 3 concepte: overloading, overriding si implementation).

Extinderea interfeţelor
Se pot adǎuga cu uşurinţǎ metode noi unei interfeţe prin extinderea ei într-o altǎ

interfaţǎ:

interface Monster {

void menace();

interface DangerousMonster extends Monster {

void destroy();
}

Deoarece câmpurile unei interfeţe sunt automat static şi final, interfeţele sunt un mod

convenabil de a crea grupuri de constante.

Extinderea interfeţelor multiple

O clasă Java poate extinde doar o clasă părinte. Moştenirea multiplă nu este

permisă. O interfaţă nu este o clasă, şi aceasta poate extinde mai multe interfeţe

părinte. Cuvântul extends e folosit o singură dată, iar interfeţele părinte sunt

declarate într-o listă separată prin virgulă. De exemplu, dacă avem o interfaţă Hochei

care extinde şi Sport şi Event, ea va fi declarată în modul următor:

public interface Hockey extends Sports,Event {}

Iniţializarea câmpurilor în interfeţe


În interfeţe nu pot exista blank finals (câmpuri final neiniţializate) însǎ pot fi iniţializate

cu valori neconstante. Câmpurile fiind statice, ele vor fi iniţializate prima oarǎ când

clasa este iniţializatǎ.

Exemplu Practic
O clasă nu poate extinde 2 clase. Să presupunem că 2 clase extind o superclasă

care are metoda turn() și că ambele o suprascriu.

Dacă o altă clasă ar extinde ambele aceste clase, această clasă ar moșteni 2 metode

turn() diferite – lucru imposibil. Și dacă am vrea să apelăm această metodă, care din

cele două variante va rula?


Atributele unei interfețe sunt static + final

Atribute de instanță Atribute de clasă (statice)


(ne-statice)
ne- Fiecare calculator poate Dacă se modifică RAM-ul la un calculator, se va modifica
final avea RAM diferit. la toate.
final RAM-ul poate diferi de Toate calculatoarele vor avea aceeași valoare de RAM
la un calculator la altul, care nu se mai poate modifica ulterior. = CONSTANTE;
dar nu se poate modifica. ex: Integer.MAX_VALUE = 2^31-1 = aprox 2 miliarde

Constantele se scriu cu CAPSLOCK pentru a fi ușor de recunoscut.

Integer.MAX_VALUE = 100; // Eroare


Clase interne. Moştenirea lor
Introducere
Clasele declarate în interiorul unei alte clase se numesc clase interne (nested

classes) și reprezintă o funcționalitate importantă deoarece permit gruparea claselor

care sunt legate logic și controlul vizibilității uneia din cadrul celorlalte.

Clasele interne sunt de mai multe tipuri, în funcție de modul de a le instanția și de

relația lor cu clasa exterioră:

 clase interne normale (regular inner classes)

 clase interne statice (static nested classes)

 clase anonime (anonymous inner classes)

 clase interne metodelor (method-local inner classes) sau blocurilor

Unul din avantajele claselor interne este comportamentul acestora ca un membru al

clasei. Asta face ca o clasa internă sa poata avea acces la toți membrii clasei de care

aparține (outer class), inclusiv cei private. În plus, aceasta poate avea modificatorii

permiși metodelor și variabilelelor claselor. Astfel, o clasa internă poate fi nu numai

public, final, abstract dar și private, protected și static.

Clase interne "normale"


O clasă internă este definită în interiorul unei clase și poate fi accesată doar la

runtime printr-o instanță a clasei externe (la fel ca metodele și variabilele ne-statice).

Compilatorul creează fișiere .class separate pentru fiecare clasă internă, în exemplul

de mai jos generând fișierele Outer.class și Outer Inner.class nu este

permisă.

class Outer {
class Inner {

private int i;

public Inner (int i) {

this.i = i;

public int value () {

return i;

public Inner getInnerInstance () {

Inner in = new Inner (11);

return in;
}

public class Test {

public static void main(String[] args) {

Outer out = new Outer ();

Outer.Inner in1 = out.getInnerInstance();

Outer.Inner in2 = out.new Inner(10);

System.out.println(in1.value());

System.out.println(in2.value());

}
În exemplul de mai sus, o dată ce avem o instanță a clasei Outer, sunt folosite două

modalități de a obține o instanță a clasei Inner (definită în interiorul clasei Outer):

 definim o metodă getInnerInstance, care creează și întoarce o astfel de

instanță;

 instanțiem efectiv Inner; observați cu atentie sintaxa folosită! Pentru a instanția

Inner, avem nevoie de o instanta Outer: out.new Inner(10);

Dintr-o clasă internă putem accesa referința la clasa externă (în cazul nostru Outer)

folosind numele acesteia și keyword-ul this:

Outer.this;

Modificatorii de acces pentru clase interne


Așa cum s-a menționat și în secțiunea Introducere, claselor interne le pot fi asociați

orice identificatori de acces, spre deosebire de clasele top-level Java, care pot fi doar

public sau package-private. Ca urmare, clasele interne pot fi, în plus, private și

protected, aceasta fiind o modalitate de a ascunde implementarea.

interface Hidden {

public int value();

class Outer {

private class HiddenInner implements Hidden {


private int i;

public HiddenInner (int i) {

this.i = i;

public int value () {

return i;

public Hidden getInnerInstance () {

HiddenInner in = new HiddenInner(11);

return in;

}
public class Test {

public static void main(String[] args) {

Outer out = new Outer();

Outer.HiddenInner in1 = out.getInnerInstance(); // va genera

eroare, tipul Outer.HiddenInner nu este vizibil

Outer.HiddenInner in2 = new Outer().new HiddenInner(10); //

din nou eroare

Hidden in3 = out.getInnerInstance(); // acces corect la o

instanta HiddenInner

System.out.println(in3.value());

Observați definirea interfeței Hidden. Ea este utilă pentru a putea asocia clasei

HiddenInner un tip, care să ne permită folosirea instanțelor acesteia, altfel tipul ei nu


ar fi fost vizibil pentru că a fost declarată private. Observați, de asemenea, încercările

eronate de a instanția HiddenInner. Cum clasa internă a fost declarată private, acest

tip nu mai este vizibil în exteriorul clasei Outer.

Clase interne în metode și blocuri


Primele exemple prezintă modalitățile cele mai uzuale de folosire a claselor interne.

Totuși, design-ul claselor interne este destul de complet și există modalităţi mai

“obscure” de a le folosi: clasele interne pot fi definite și în cadrul metodelor sau ale

unor blocuri arbitrare de cod.

Clase interne în metode

În exemplul următor, clasa internă a fost declarată în interiorul funcției

getInnerInstance. În acest mod, vizibilitatea ei a fost redusă pentru ca nu poate fi

instanțiată decât în această funcție.

Singurii modificatori care pot fi aplicați acestor clase sunt abstract și final (binențeles,

nu amândoi deodată).

interface Hidden {

public int value ();

class Outer {

public Hidden getInnerInstance() {


class FuncInner implements Hidden {

private int i = 11;

public int value () {

return i;

return new FuncInner();

public class Test {

public static void main(String[] args) {

Outer out = new Outer ();

Outer.FuncInner in2 = out.getInnerInstance(); // EROARE:

clasa FuncInner nu este vizibila


Hidden in3 = out.getInnerInstance();

System.out.println(in3.value());

Clasele interne declarate în metode nu pot folosi variabilele declarate în metoda

respectivă și nici parametrii metodei. Pentru a le putea accesa, variabilele trebuie

declarate final, ca în exemplul următor. Această restricție se datorează faptului că

variabilele si parametrii metodelor se află pe segmentul de stivă (zonă de memorie)

creat pentru metoda respectivă, ceea ce face ca ele să nu fie existe la fel de mult cât

clasa internă. Dacă variabila este declarată final, atunci la runtime se va stoca o

copie a acesteia ca un câmp al clasei interne, în acest mod putând fi accesată și

după execuția metodei.

public void f() {

final Student s = new Student(); // s trebuie declarat final

ca sa poata fi accesat din AlterStudent

class AlterStudent {
public void alterStudent() {

s.name = ... // OK

s = new Student(); // GRESIT!

Clase interne în blocuri

Exemplu de clasa internă declarata într-un bloc:

interface Hidden {

public int value ();

class Outer {

public Hidden getInnerInstance(int i) {

if (i == 11) {
class BlockInner implements Hidden {

private int i = 11;

public int value() {

return i;

return new BlockInner();

return null;

În acest exemplu, clasa internă BlockInner este defintă în cadrul unui bloc if, dar

acest lucru nu înseamnă că declarația va fi luată în considerare doar la rulare, în

cazul în care condiția este adevarată.


Semnificația declarării clasei într-un bloc este legată strict de vizibilitatea acesteia. La

compilare clasa va fi creată indiferent care este valoarea de adevăr a condiției if.

Clase anonime
Există multe situații în care o clasă internă este instanțiată într-un singur loc (si este

folosita prin upcasting la o clasă de bază sau interfață), ceea ce face ca numele

clasei să nu mai fie important, iar tipul ei poate fi un subtip al unei clase sau o

implementare a unei interfețe. Singurele metode care pot fi apelate pe o clasa

anonimă sunt cele are tipului pe care îl extinde sau implementează.

În Java putem crea clase interne anonime (făra nume) ca în exemplul următor:

interface Hidden {

public int value();

class Outer {

public Hidden getInnerInstance(int i) {

return new Hidden() {

private int i = 11;

public int value() {

return i;
}

};

public class Test {

public static void main(String[] args) {

Outer out = new Outer();

Hidden in3 = out.getInnerInstance(11);

System.out.println(in3.value());

Observați modalitatea de declarare a clasei anonime. Sintaxa return new Hidden() {

… } reprezintă următoarele:

 dorim să întoarcem un obiect de tip Hidden

 acest obiect este instanțiat imediat după return, folosind new (referința

întoarsă de new va fi upcast la clasa de bază: Hidden)


 numele clasei instanțiate este absent (ea este anonimă), însă ea este de tipul

Hidden, prin urmare, va implementa metoda/metodele din interfață(cum e metoda

value). Corpul clasei urmează imediat instanțierii.

Construcția return new Hidden() { … } este echivalentă cu a spune: creează un obiect

al unei clase anonime ce implementeaza Hidden.

O clasă internă anonimă poate extinde o clasă sau să implementeze o singură

interfață, nu poate face pe ambele împreună ca la clasele ne-anonime (interne sau

nu), și nici nu poate să implementeze mai multe interfețe.

Constructori
Clasele anonime nu pot avea constructori din cauză că nu au nume (nu am ști cum

să numim constructorii). Această restricție asupra claselor anonime ridică o

problemă: în mod implicit, clasă de bază este creată cu constructorul default.

Ce se întâmplă dacă dorim să invocăm un alt constructor al clasei de bază? În

clasele normale acest lucru era posibil prin apelarea explicită, în prima linie din

constructor a constructorului clasei de bază cu parametrii doriți, folosind super. În

clasele interne acest lucru se obține prin transmiterea parametrilor către constructorul

clasei de bază direct la crearea obiectului de tip clasă anonimă:

new Student("Andrei") {

// ...

În acest exemplu, am instanțiat o clasă anonimă, ce extinde clasa Student, apelând

constructorul clasei de bază cu parametrul “Andrei”.


Clase interne statice
În secțiunile precedente, s-a discutat doar despre clase interne a căror instanțe

există doar în contextul unei instanțe a clasei exterioare, astfel că poate accesa

membrii obiectului exterior direct. De asemenea, am menționat că fiind membri ai

claselor exterioare, clasele interne pot avea modificatorii disponibili pentru metode și

variabile, dintre care și static (clasele exterioare nu pot fi statice!). Așa cum pentru a

accesa metodele și variabilele statice ale unei clase nu este nevoie de o instanță a

aceteia, putem obține o referință către o clasă internă fără a avea nevoie de o

instanță a clasei exterioare.

Pentru a înțelege diferența dintre clasele interne statice și cele nestatice trebuie să

reținem următorul aspect: clasele nestatice țin legătura cu obiectul exterior în vreme

ce clasele statice nu păstrează această legătură.

Pentru clasele interne statice:

 nu avem nevoie de un obiect al clasei externe pentru a crea un obiect al clasei

interne

 nu putem accesa câmpuri nestatice ale clasei externe din clasă internă (nu

avem o instanță a clasei externe)

class Outer {

public int outerMember = 9;

class NonStaticInner {
private int i = 1;

public int value() {

return i + Outer.this.outerMember; // OK, putem accesa

un membru al clasei exterioare

static class StaticInner {

public int k = 99;

public int value() {

k += outerMember; // EROARE, nu putem accesa un membru

nestatic al clasei exterioare

return k;

}
public class Test {

public static void main(String[] args) {

Outer out = new Outer ();

Outer.NonStaticInner nonSt = out.new NonStaticInner(); //

instantiere CORECTA pt o clasa nestatica

Outer.StaticInner st = out.new StaticInner(); // instantiere

INCORECTA a clasei statice

Outer.StaticInner st2 = new Outer.StaticInner(); //

instantiere CORECTA a clasei statice

În exemplul de mai sus se observă că folosirea membrului nestatic outerMember în

clasa statică StaticInner este incorectă. De asemenea, se observă modalitățile

diferite de instanțiere a celor două tipuri de clase interne (statice și nestatice):

 folosim o instanță a clasei exterioare - out (ca și în exemplele anterioare)

pentru a instanția o clasă nestatică.


 folosim numele claselor pentru a instanția o clasă statică. Folosirea lui out este

incorectă.

Clasele interne statice nu au nevoie de o instanță a clasei externe → atunci de ce le

facem interne acesteia?

 pentru a grupa clasele, dacă o clasă internă statică A.B este folosită doar de

A, atunci nu are rost să o facem top-level.

Avem o clasă internă A.B, când facem o statică?

 în interiorul clasei B nu avem nevoie de nimic specific instanței clasei externe

A, deci nu avem nevoie de o instanță a acesteia → o facem statică


Terminologia nested classes vs inner classes:
Clasele interne normale, cele anonime si cele interne blocurilor si metodelor sunt

inner classes datorită relației pe care o au cu clasa exterioară (depind de o instanță a

acesteia). Termenul de nested classes se referă la definirea unei clase în interiorul

altei clase, și cuprinde atât inner classes cât și clasele statice interne. De aceea,

claselor statice interne li se spune static nested classes și nu static inner classes.

Moștenirea claselor interne


Deoarece constructorul clasei interne trebuie sa se atașeze de un obiect al clasei

exterioare, moștenirea unei clase interne este puțin mai complicată decât cea

obișnuită. Problema rezidă în nevoia de a inițializa legătura (ascunsă) cu clasa

exterioară, în contextul în care în clasa derivată nu mai există un obiect default

pentru acest lucru (care era NumeClasaExterna.this).

class WithInner {

class Inner {
public void method() {

System.out.println("I am Inner's method");

class InheritInner extends WithInner.Inner {

InheritInner() {} // EROARE, avem nevoie de o legatura la

obiectul clasei exterioare

InheritInner(WithInner wi) { // OK

wi.super();

public class Test {

public static void main(String[] args) {

WithInner wi = new WithInner();


InheritInner ii = new InheritInner(wi);

ii.method();

Observăm ca InheritInner moșteneste doar WithInner.Inner însa sunt necesare:

 parametrul constructorului InheritInner trebuie sa fie de tipul clasei externă

(WithInner)

 linia din constructorul InheritInner: wi.super().


Utilizarea claselor interne
Clasele interne pot părea un mecanism greoi și uneori artificial. Ele sunt însă foarte

utile în următoarele situații:

 Rezolvăm o problemă complicată și dorim să creăm o clasă care ne ajută la

dezvoltarea soluției dar:

 nu dorim să fie accesibilă din exterior sau

 nu mai are utilitate în alte zone ale programului

 Implementăm o anumită interfață și dorim să întoarcem o referință la acea

interfață, ascunzând în același timp implementarea.

 Dorim să folosim/extindem funcționalități ale mai multor clase, însă în JAVA nu

putem extinde decât o singură clasă. Putem defini însă clase interioare. Acestea

pot moșteni orice clasă și au, în plus, acces la obiectul clasei exterioare.

 Implementarea unei arhitecturi de control, marcată de nevoia de a trata

evenimente într-un sistem bazat pe evenimente. Unul din cele mai importante
sisteme de acest tip este GUI (graphical user interface). Bibliotecile Java Swing,

AWT, SWT sunt arhitecturi de control care folosesc intens clase interne. De

exemplu, în Swing, pentru evenimente cum ar fi apăsarea unui buton se poate

atașa obiectului buton o tratare particulară al evenimentului de apăsare în felul

următor:

button.addActionListener(new ActionListener() { //interfata

implementata e ActionListener

public void actionPerformed(ActionEvent e) {

numClicks++;

});

Exerciţiu enum şi încapsulare


Folosind următoarele 2 exerciţii de la secţiunea de metode:

7. Scrieţi un program care vă afişează un meniu cu 4 opţiuni. Ar putea arăta în felul

următor:

Ye Olde Keychain Shoppe

1. Add Keychains to Order

2. Remove Keychains from Order


3. View Current Order

4. Checkout

Please enter your choice: 1

ADD KEYCHAINS

1. Add Keychains to Order

2. Remove Keychains from Order

3. View Current Order

4. Checkout

Please enter your choice: 3

VIEW ORDER
1. Add Keychains to Order

2. Remove Keychains from Order

3. View Current Order

4. Checkout

Please enter your choice: 4

CHECKOUT

Va trebui să creaţi metode pentru fiecare dintre cele patru opţiuni din meniu.

Introducerea unuia din numere va chema funcţia respectivă. Acest exerciţiu nu

necesită să aveţi funcţionalităţi în niciuna din fucnţii, cu excepţia funcţionalităţii din

meniu. Nu este necesar să programaţi logica necesară pentru adăugarea sau

ştergerea breloacelor, pentru a vizualiza comanda sau pentru checkout.

Funcţiile ar trebui să aibă numele addKeychains(), removeKeychains(), viewOrder()

şi checkout(). Fiecare din ele trebuie să afişeze un mesaj corespunzător.

Utilizatorul ar trebui să poată introduce alegeri până la apelul funcţiei checkout().

Când funcţia checkout() este apelată, programul trebuie să se termine.

8. Următorul pas este să facem magazinul de breloace să funcţioneze.


Ye Olde Keychain Shoppe

1. Add Keychains to Order

2. Remove Keychains from Order

3. View Current Order

4. Checkout

Please enter your choice: 1

You have 0 keychains. How many to add? 3

You now have 3 keychains.

1. Add Keychains to Order

2. Remove Keychains from Order

3. View Current Order

4. Checkout
Please enter your choice: 2

You have 3 keychains. How many to remove? 1

You now have 2 keychains.

1. Add Keychains to Order

2. Remove Keychains from Order

3. View Current Order

4. Checkout

Please enter your choice: 3

You have 2 keychains.

Keychains cost $10 each.

Total cost is $20.


1. Add Keychains to Order

2. Remove Keychains from Order

3. View Current Order

4. Checkout

Please enter your choice: 4

CHECKOUT

What is your name? Biff

You have 2 keychains.

Keychains cost $10 each.

Total cost is $20.

Thanks for your order, Biff!


Veţi avea nevoie de 2 noi variabile în funcţia main, una pentru stocarea numărului de

breloace, şi unul pentru a stoca preţul pentru un breloc. Preţul ar trebui să fie de 10

RON.

 addKeychains() va primi ca parametru un int şi va returna un int. Va cere

utilizatorului numărul de breloace pe care să îl adauge la comandă, şi va returna

noul număr de breloace.

 removeKeychains() va primi ca parametru un int şi va avea tipul returnat int.

Va cere utilizatorului numărul de breloace care să îl şteargă din comandă şi va

returna numărul de breloace rămase.

 viewOrder() va primi ca parametru 2 variabile de tipul int şi va avea ca tip

returnat void. Va afişa, pe 3 linii diferite, numărul de breloace din comandă, preţul

pentru fiecare breloc şi costul total al comenzii.

 checkout() va avea ca parametrii 2 variabile de tip int, iar tipul returnat va fi

void. Va cere utilizatorului numele acestuia pentru a face corect livrarea, va afişa

informaţii referitoare la comandă, iar la final va mulţumi utilizatorului folosind

numele acestuia pentru comanda făcută.

Cerinţa
Să se implementeze enum-ul TipBreloc.

public enum TipBreloc {

AUR,

ARGINT,

TINICHEA
}

Să se creeze clasa Breloc care conţine următoarele câmpuri, să se implementeze 2

constructori înlănţuiţi şi să se aplice principiul de încapsulare. Să se aleagă

corespunzător tipurile de date pentru câmpuri.

public class Breloc {

// denumire

// pret

// tip

Opţional, să se implementeze şi clasa Client care să conţină diverse câmpuri şi

acelaşi principiu de încapsulare. Mai mult, puteţi implementa şi clasa Magazin

Pentru operaţiile de add şi remove ţineţi cont că pe lângă numărul de breloce veţi

avea nevoie şi de tipul acestora.

Pentru vizualizare, veţi afişa o listă cu toate breloacele comandate, preţul parţial

pentru fiecare tip comandat, precum şi preţul total.

Pentru checkout veţi face afişarea completă, împreună cu numele utilizatorului,

folosind instrucţiunile de mai sus.


Sortare
Assignment

1. Completaţi codul acolo unde vă este indicat astfel încât să schimbaţi Pokemonul

de pe poziţia 0 cu cel selectat.

import java.util.Scanner;

public class PokeTrader {

public static void main(String[] args) {

Scanner keyboard = new Scanner(System.in);

String[] pokeParty = { "PIKACHU", "CHARMELEON", "GEODUDE",

"GYARADOS", "BUTTERFREE", "MANKEY" };

int x;

do {

System.out.println("EXCHANGE POKEMON\n");

System.out.println("0. " + pokeParty[0]);


for ( int i=1; i<pokeParty.length; i++ )

System.out.println("\t" + i + ". " + pokeParty[i]);

System.out.println("\nChoose a Pokemon to exchange with

" + pokeParty[0] + ". (Or enter 0 to quit.)");

System.out.print("> ");

x = keyboard.nextInt();

// add code here to swap the Pokemon in slot 0 with the

Pokemon in slot x

} while ( x> 0 );

2. Completaţi codul acolo unde vă este indicat pentru a interschimba 2 Pokemoni

selectaţi.

import java.util.Scanner;
public class PokeTrader2 {

public static void main(String[] args) {

Scanner keyboard = new Scanner(System.in);

String[] pokeParty = { "PIKACHU", "CHARMELEON", "GEODUDE",

"GYARADOS", "BUTTERFREE", "MANKEY" };

int a;

int b;

do {

System.out.println("EXCHANGE POKEMON\n");

for ( int i=0; i<pokeParty.length; i++ )

System.out.println(" " + i + ". " +

pokeParty[i]);
System.out.print("\nChoose a Pokemon (or -1 to quit). =>

");

a = keyboard.nextInt();

if (a>= 0) {

System.out.print("Choose a Pokemon to exchange with

" + pokeParty[a] + ". => ");

b = keyboard.nextInt();

System.out.println("\nExchanging " + pokeParty[a] +

" with " + pokeParty[b] + ".\n");

// add code here to swap the Pokemon in slot a with

the Pokemon in slot b

} while (a>= 0);

3. Folosind bucle imbricate, o instrucţiune if şi cod pentru interschimbare pentru a

reordona valorile din array astfel încât să fie în ordine crescătoare (de la mic la

mare).
public class SortingValues {

public static void main(String[] args) {

int[] arr = { 45, 87, 39, 32, 93, 86, 12, 44, 75, 50 };

// Display the original (unsorted values)

System.out.print("before: ");

for (int i = 0; i <arr.length; i++)

System.out.print(arr[i] + " ");

System.out.println();

// Swap the values around to put them ascending order.

/*

for ( ; ; ) {

for ( ; ; ) {

if ( ) {
// swap the values in two slots

*/

// Display the values again, now (hopefully) sorted.

System.out.print("after : ");

for (int i = 0; i <arr.length; i++)

System.out.print(arr[i] + " ");

System.out.println();

4. Implementaţi metoda sortării prin selecţie pentru următorul cod. Vă puteţi folosi de

informaţiile următoare:http://www.algolist.net/Algorithms/Sorting/Selection_sort

import java.util.Random;
public class SelectionSort {

public static void selectionSort(int[] a) {

// Your code goes here

public static void swap(int[] a , int i, int j) {

// Your code goes here

public static void main(String[] args) {

Random r = new Random();

int[] arr = new int[10];

int i;

// Fill up the array with random numbers


for (i = 0; i <arr.length; i++)

arr[i] = 1 + r.nextInt(100);

// Display it

System.out.print("before: ");

for (i = 0; i <arr.length; i++)

System.out.print(arr[i] + " ");

System.out.println();

// Sort it

selectionSort(arr);

// Display it again to confirm that it's sorted

System.out.print("after : ");

for (i = 0; i <arr.length; i++)

System.out.print(arr[i] + " ");


System.out.println();

Recursivitate
Recursivitatea este o tehnică prin care putem divide problema în subprobleme de

acelaşi tip. Putem folosi exemplul de mai jos pentru a o înţelege mai bine.

Calculul factorial
Factorial de n (notat n!) reprezintă produsul numerelor întregi între 1 şi n. De

exemplu, 5! = 1 * 2 * 3 * 4 * 5 = 120.

Recursivitatea este o tehnică prin care putem calcula factorialul. Mai exact, 5! = 4! *

5. Pentru a calcula factorial de n, ar trebui să calculăm factorial de (n - 1). Pentru a

calcula factorial de (n - 1) algortimul ar trebui să găsească factorial de (n - 2), şi aşa

mai departe. Procesul descris mai sus, ar putea dura la infinit, neavând un caz de

bază la care să ne oprim. Acest caz de bază ne defineşte când ar trebui oprit

procesul. În exemplul cu factorialul, condiţia de oprire este n = 1, pentru care

rezultatul este cunoscut.

int factorial (int n) {

if (n <= 1)

return 1;
else

return n * factorial (n - 1);

Detaliile de calcul pentru 3!


Avantaje şi dezavantaje pentru recursivitate
Principalul avantaj al recursivităţii este simplitatea programării. Când folosim

recursivitatea, programatorul poate uita de problemă şi se poate concentra doar pe

cazul curent. Apoi, la întoarcerea la problemă, cazurile de bază (este posibil să avem

mai mult de un caz) şi punctele de pronire pentru recursivitate sunt dezvoltate.

Ca şi dezavantaj, recirsivitatea are un dezavantaj serios în sensul folosirii unui spaţiu

mare de memorie. Mai mult, pentru majoritatea limbajelor de programare,

recirsivitatea foloseşte stiva pentru a stoca toate stările tuturor apelurilor recursive

active. Mărimea stivei poate fi foarte mare, dar este mereu limitată. Aşadar, apelurile

recursive prea adânci pot rezulta în Stack Overflow. Pentru a rezolva această

problemă, recursivitatea poate fi simulată folosind o buclă şi o structură de date stivă.

Assignment
1. Dacă avem un n care este mai mare sau egal ca un, să se calculeze factorial de n,

care este dat de formula n * (n - 1) * (n - 2) * … * 1. Calculaţi rezultatul recursiv (fără

bucle).

factorial(1) = 1

factorial(2) = 2

factorial(3) = 6

2. Avem un număr de iepuraşi, fiecare cu două urechi mari şi pufoase. Vrem să

calculăm recursiv numărul total de urechi de la toţi iepuraşii (fără bucle sau înmulţiri).

bunnyEars(0) = 0
bunnyEars(1) = 2

bunnyEars(2) = 4

Hint: În primul rând stabiliţi cazul de bază (bunnies == 0), şi returnaţi 0. Altfel, faceţi

un apel recursiv la bunnyEars(bunnies - 1). Presupunând că apelul recursiv întoarce

valoarea corectă, îi mai adăugăm 2 urechi pentru iepuraşul curent.

3. Sevenţa lui fibonacci este o parte importantă a matematicii, şi are o definiţie

recursivă. Primele două valori sunt 0 şi 1 (două cazuri de bază). Fiecare număr

ulterior este suma a două valori anterioare, iar secvenţa completă este 0, 1, 1, 2, 3,

5, 8, 13, 21, etc. Definiţi o metodă recursivă fibonacci(n) care să întoarcă al n-lea

număr fibonacci, care are n = 0 ca punct de plecare a secvenţei.

fibonacci(0) = 0

fibonacci(1) = 1

fibonacci(2) = 1

4. Avem o serie de iepuraşi care stau în linie, numerotaţi cu 1, 2, … Iepuraşii impari

(1, 3, …) au 2 urechi normale. Iepuraşii pari (2, 4, …) putem spune că au trei urechi,

deoarece fiecare a ridicat o lăbuţă. Întoarceţi recursiv numărul de "urechi" din linia de

iepuraşi 1, 2, … n (fără a folosi bucle sau înmulţiri).

bunnyEars2(0) = 0

bunnyEars2(1) = 2
bunnyEars2(2) = 5

5. Avem un triunghi construit din cărămizi. Cel mai de sus rând are o cărămidă, al

doilea rând are 2 cărămizi, al treilea rând are 3 cărămizi, şi aşa mai departe. Calculaţi

recursiv (fără bucle sau multiplicări) numărul total de cărămizi într-un asemenea

triunghi în care cunoaşteţi numărul de rânduri.

triangle(0) = 0

triangle(1) = 1

triangle(2) = 3

6. Dacă avem un număr pozitiv întreg n, calculaţi recursiv suma cifrelor sale (fără a

folosi bucle). Ţineţi cont că operaţia modulo (%) 10 ne va da cifra cea mai din

dreapta (126 % 10 este 6), iar diviziunea (/) la 10 va şterge cifra cea mai din dreapta

(126 / 10 este 12).

sumDigits(126) = 9

sumDigits(49) = 13

sumDigits(12) = 3

7. Dacă avem un număr pozitiv întreg n, calculaţi de câte ori apare cifra 7. De

exemplu, 717 are trebui să dea rezultatul 2. Calculaţi recursiv, fără a folosi bucle.

Ţineţi cont că operaţia modulo (%) 10 ne va da cifra cea mai din dreapta (126 % 10

este 6), iar diviziunea (/) la 10 va şterge cifra cea mai din dreapta (126 / 10 este 12).
count7(717) = 2

count7(7) = 1

count7(123) = 0

8. Dacă avem un număr pozitiv întreg n, calculaţi de câte ori apare cifra 8, cu

excepţia faptului că dacă cifra 8 este urmată imediat de un alt 8, o vom număra de

două ori. Deci 8818 va da rezultatul 4. Calculaţi recursiv, fără a folosi bucle. Ţineţi

cont că operaţia modulo (%) 10 ne va da cifra cea mai din dreapta (126 % 10 este 6),

iar diviziunea (/) la 10 va şterge cifra cea mai din dreapta (126 / 10 este 12).

count8(8) = 1

count8(818) = 2

count8(8818) = 4

9. Se dă o bază şi un n care au valoarea egală cu 1 sau mai mare, şi să se calculeze

recursiv (fără bucle) valoarea bazei la puterea n, astfel încât powerN(3, 2) este 9 (3

la pătrat).

powerN(3, 1) = 3

powerN(3, 2) = 9

powerN(3, 3) = 27
10. Se dă un şir de caractere, să se calculeze recursiv (fără bucle) numărul de

apariţii al caracterului 'x' din şir.

countX("xxhixx") = 4

countX("xhixhix") = 3

countX("hi") = 0

11. Se dă un array de numere întregi, să se calculeze recursiv dacă array-ul conţine

numărul 6. Vom folosi o convenţie pentru a considera doar o parte din array care

porneşte de la un index dat. În acest mod, un apel recursiv poate trimite index + 1 ca

parametru pentru a avansa în array. Apelul îniţial se va face cu valoarea 0.

array6({1, 6, 4}, 0) = true

array6({1, 4}, 0) = false

array6({6}, 0) = true

12. Afişaţi următoarea formă din steluţe folosind o metodă recursivă care desenează

un rând de steluţe:

*****

****

***

**
*

13. Creaţi o metodă care să deseneze un pătrat într-un pătrat, în alt pătrat în mod

recursiv.

14. Creaţi o metodă care să deseneze un triunghi, într-un pătrat, intr-un pentagon,

într-un hexagon, şi aşa mai departe recursiv.

15. Calculaţi cel mai mare divizor comun între 2 numere, folosind algoritmul lui

Euclid.

cmmdv (a, b) = a, dacă a=b

= cmmdc(a-b, b), dacă a>b

= cmmdc(a, b-a), daca b>a

cmmdc(25,35) = cmmdc(25,10) = cmmdc(15,10) = cmmdc(5,10) =

cmmdc(5,5) = 5

16. Un ArrayList poate contine elemente de tip ArrayList sau Integer. Dacă elementul

este de tip ArrayList, elementele sale pot fi tot de tip ArrayList sau Integer. Dacă un

element al acestuia este de tip ArrayList, elementele acestuia din urmă pot fi tot de

tip ArrayList sau Integer. Tot așa. La un moment dat, un ArrayList va conține doar

Integer. Folosind o funcție recursivă, să se facă suma tuturor elementelor. Se

presupune că avem o metodă care ne returneaza dacă un ArrayList este null

(nedefinit).
17. O metoda primeste un array de numere naturale. Sa se creeze o functie (metodă)

recursivă care returnează suma elementelor din array.

18. Se da un numar natural n. Sa se scrie o metodă recursivă care să calculeze și

returneze suma divizorilor. Indiciu: se merge pas cu pas înapoi scăzând 1. Dacă e

divizor se returneaza numărul + suma divizorilor numărului cu 1 mai mic. Dacă nu e

divizor se returnează 0 + suma divizorilor numărului cu 1 mai mic. Dacă numnărul e

0, returnăm 0.

Clasic s-ar face cu un while.

19. Se da un numar natural. Sa se calculeze recursiv suma cifrelor.

20. Să se sorteze un array (vector) în ordine crescătoare.

21. Permutări de 2 elemente:

12

21

Permutari de 3 elemente

123

132

213

231

312

321

Permutari de n elemente

Total n! Factorial elemente (= 1*2*3*4*…*(n-1)*n)

Pentru un n dat, sa se afiseze toate permutările.


ArrayList
Java pune la dispoziție o clasă din pachetul java.util care spre deosebire de array-ul

classic, ArrayList își poate modifca dinamic dimensiunea. Elementele dintr-un

ArrayList sunt indexate asemenea unui array, fiind 0-based. Într-un ArrayList se pot

adăuga elemente sau scoate.

Spre deosebire de array, ArrayList poate conține numai obiecte. (nu și primitive)

ArrayList lista = new ArrayList(); // s-a creat un ArrayList care nu

are nici un element.

lista.size(); // returnează numărul de elemente. În acest caz =0.

Metode uzuale
Modifier and Type Method and Description
boolean add(E e) Appends the specified element to the end of this list.
void add(int index, E element) Inserts the specified element at the specified position in
this list.
boolean addAll(Collection<? Appends all of the elements in the specified collection to
extends E> c) the end of this list, in the order that they are returned by
the specified collection's Iterator.
boolean addAll(int index, Inserts all of the elements in the specified collection into
Collection<? extends E> c) this list, starting at the specified position.
void clear() Removes all of the elements from this list.
Object clone() Returns a shallow copy of this ArrayList instance.
boolean contains(Object o) Returns true if this list contains the specified element.
void ensureCapacity(int Increases the capacity of this ArrayList instance, if
minCapacity) necessary, to ensure that it can hold at least the number
of elements specified by the minimum capacity
argument.
E get(int index) Returns the element at the specified position in this list.
int indexOf(Object o) Returns the index of the first occurrence of the specified
element in this list, or -1 if this list does not contain the
Modifier and Type Method and Description
element.
boolean isEmpty() Returns true if this list contains no elements.
Iterator<E> iterator() Returns an iterator over the elements in this list in proper
sequence.
int lastIndexOf(Object o) Returns the index of the last occurrence of the specified
element in this list, or -1 if this list does not contain the
element.
ListIterator<E> listIterator() Returns a list iterator over the elements in this list (in
proper sequence).
ListIterator<E> listIterator(int Returns a list iterator over the elements in this list (in
index) proper sequence), starting at the specified position in the
list.
E remove(int index) Removes the element at the specified position in this list.
boolean remove(Object o) Removes the first occurrence of the specified element
from this list, if it is present.
boolean removeAll(Collection<?> Removes from this list all of its elements that are
c) contained in the specified collection.
protected void removeRange(int Removes from this list all of the elements whose index is
fromIndex, int toIndex) between fromIndex, inclusive, and toIndex, exclusive.
boolean retainAll(Collection<?> Retains only the elements in this list that are contained in
c) the specified collection.
E set(int index, E element) Replaces the element at the specified position in this list
with the specified element.
int size() Returns the number of elements in this list.
List<E> subList(int fromIndex, Returns a view of the portion of this list between the
int toIndex) specified fromIndex, inclusive, and toIndex, exclusive.
Object[] toArray() Returns an array containing all of the elements in this list
in proper sequence (from first to last element).
<T> T[] toArray(T[] a) Returns an array containing all of the elements in this list
in proper sequence (from first to last element); the
runtime type of the returned array is that of the specified
array.
void trimToSize() Trims the capacity of this ArrayList instance to be the
list's current size.

//aduagarea de elemente
lista.add(computer1); //pe poziția 0

lista.add(”Alina”); //pe pozitia 1

lista.add(new String (”Ioana”)); // pe pozitia 2

lista.add(telefon1); //pe pozitia 3

lista.add(new Cont(“Alex”, 1234, 0.00)); //pe pozitia 4

//vizualizarea tuturor elementelor:

System.out.println(lista);

lista.clear(); //șterge toate elementele din listă.

Începând cu versiunea 6 Java, ArrayList-ul poate fi parametrizat (adică să conțină

numai elemente de un anumit tip)

ArrayList<Computer> lista = new ArrayList<Computer>(); // nu se mai

pot adauga decat Computere.

// Obtinerea unui element de pe o anumita pozitie:

Computer comp = lista.get(4);

// returnarea primei pozitii în care se găsește un element


int index = lista.indexOf(computer1);

// returnarea ultimei pozitii in care se gaseste un element

int index = lista.lastIndexOf(computer1);

//stergerea unui element

Computer computer = lista.remove(3); // sterge elementul de pe

pozitia 3 și îl returnează

boolean bool = lista.remove(computer1);

Iterare
Un ArrayList se poate itera în 3 moduri:

1. cu for classic

for (int i = 0; i<lista.size(); i++){

if (lista.get(i).getMarca().equals(“Acer”)){

lista.remove(i);

}
}

2. cu for-each

for (Computer computer : lista) {

if (computer.getMarca().equals(“Toshiba”)){

lista.remove(computer);

Pentru a sterge un element dinamic din lista, in timp ce parcurgem lista, nu sunt,

totusi, indicate variantele de mai sus, întrucât pot apărea crashuri, ci varianta cu

iterator.

3. folosind un iterator lista.listIterator() returneaza un iterator

ListIterator<Computer> iter = lista.listIterator();

Acest iterator este ca un cursor care se pozitioneaza la inceput inaintea primului

element si cu ajutorul metodei next() avanseaza inainte sauprevious() inapoi si

returneaza elementul respectiv.

Iteratorul nu se pozitioneaza pe un element, ci intre elemente. A se vedea

in API ListIterator.
Nu apelam metoda next() sau previous() decat daca sunt siguri ca exista un element

in fata, respectiv in spate. Acest lucru il vedem cu hasNext() sauhasPrevious().

while (iter.hasNext()){

iter.next();

while (iter.hasNext()){

if (iter.next().getMarca().equals(“HP”) {

iter.remove(); //se scoate din ArrayList elementul

returnat de iter.next()

Dacă avem rescrisă metoda equals(), putem face următorul lucru:

Computer c1 = new Computer (”Toshiba”, 1800, 4);

Computer c2 = new Computer (”Acer”, 1400, 2);

Computer c3 = new Computer (”HP”, 2800, 8);

Computer c4 = new Computer (”Intel”, 2000, 4);

ArrayList<Computer> computere = new ArrayList<Computer>();


computere.add(c1);

computere.add(c2);

computere.add(c3);

computere.add(c4);

Computer altComputer = new Computer(“Acer”, 1400, 2);

boolean bool = computere.contains(altComputer); // va afișa true

computere.remove(altComputer);

Deși altComputer nu se regăsește în lista, dar are aceleași caracteristici cu c2, va

da true, iar la următoarea linie de cod practic va fi scos c2 din arraylistul de

computere.

Exerciţiu
Se consideră clasa Avion cu următoarele câmpuri: companie, marca+model, tip de

avion, nr de locuri, nr de clase pasageri şi metoda toString().

1. Creați o listă (arrrayList) de avioane folosind variabile pe stack.

2. Afișați avionul de pe indexul 3.

3. La un moment dat, un avion este înlocuit cu altul nou.

4. Verificați dacă lista de avioane conține un anumit avion.

5. Afișați indexul unui avion.


6. Ștergeți un avion după index

7. Stergeți un avion.

Assignment
1. Creaţi un ArrayList care reţine valori de tipul Integer. Adăugaţi în acesta 10 copii

ale numărului -113. Afişaţi conţinutul acestuia pe ecran.

Nu folosiţi o buclă şi nici o variabilă pentru index. Va trebui să vă folosiţi de numere

literali pentru a face referire la fiecare poziţie.

2. Creaţi un ArrayList care reţine valori de tipul Integer. Adăugaţi în acesta 10 copii

ale numărului -113 şi afişaţi conţinutul acestuia pe ecran.

De data aceasta, folosiţi o buclă pentru a umple array-ul cu valori şi apoi pentru a-l

afişa. De asemenea, în condiţia de la buclă, nu număraţi până la un număr literal, ci

va trebui să folosiţi metoda size() din ArrayList.

3. Creaţi un ArrayList care să reţină valori de tipul Integer. Adăugaţi în acesta 10

numere aleatoare, cu valori între 1 şi 100. Apoi afişaţi conţinutul acestui ArrayList.

Folosiţi o buclă pentru a genera valori. Pentru afişare puteţi folosi metodă simplă,

fără buclă:

System.out.println("ArrayList: " +

whateverYourArrayListVariableIsCalled);

4. Creaţi un ArrayList care să reţină valori de tipul Integer. Populaţi lista cu 1,000 de

valori aleatoare, fiecare între 10 şi 99. Apoi afişaţi conţinutul acestui ArrayList pe

ecran cu o modalitate preferată.


5. Copierea unui ArrayList.

Scrieţi un program care să creeze un ArrayList ce conţine valori de tipul Integer.

Puneţi 10 numere aleatoare în acesta între 1 şi 100. Acesta ar trebui să copieze

toate elementele din primul ArrayList într-un alt ArrayList de aceeaşi dimensiune.

Afişaţi apoi valorile din cei doi ArrayList folosind modalitatea dorită.

Creaţi un ArrayList care să reţină valori de tipul Integer. Populaţi ArrayList-ul cu 10

numere aleatoare între 1 şi 100. Copiaţi fiecare valoare din ArrayList într-un alt

ArrayList cu aceeaşi capacitate. Schimbaţi ultima valoare din primul ArrayList la -7.

Afişaţi conţinutul celor doi ArrayList.

6. Căutarea unei valori într-un ArrayList.

Creaţi un ArrayList care reţine valori de tipul Integer şi populaţi-l cu valori aleatoare

între 1 şi 50. Afişaţi aceste valori pe ecran, şi apoi cereţi utilizatorului un număr întreg

între aceste valori. Căutaţi în ArrayList numărul introdus şi dacă acesta este prezent,

afişaţi un mesaj. Nu este necesar să afişaţi un mesaj dacă valoarea nu a fost găsită.

Dacă găsiţi de mai multe ori numărul respectiv, nu este nicio problemă dacă mesajul

va fi afişat de mai multe ori.

7. Există sau nu?

Creaţi un ArrayList care să reţină valori de tipul Integer şi adăugaţi numere aleatoare

între 1 şi 50. Afişaţi aceste valori pe ecran, apoi cereţi utilizatorului un număr întreg.

Căutaţi în ArrayList, şi dacă itemul respectiv este prezent, afişaţi un singur mesaj

(chiar dacă este prezent de mai multe ori). Dacă valoarea nu se află în ArrayList,

atunci afişaţi un alt mesaj care să indice acest lucru.

8. Unde există?
Creaţi un ArrayList care să reţină valori de tipul Integer şi umpleţi-l cu numere

aleatoare între 1 şi 50. Afişaţi aceste valori şi cereţi utilizatorului un număr întreg.

Căutaţi în ArrayList şi dacă gasiţi acest număr, afişaţi poziţia lui. Dacă numărul nu

este în ArrayList, afişaţi un mesaj corenspunzător. Dacă numărul apare de mai multe

ori, afişaţi mesajul cu poziţia de câte ori este necesar, sau afişaţi o singură dată

ultima poziţie pe care acesta apare.

9. Găsirea celei mai mari valori.

Scrieţi un program în care să creaţi un ArrayList ce poate reţine valori de tipul

Integer. Populaţi ArrayList-ul cu numere aleatoare între 1 şi 100. Afişaţi valorile din

ArrayList. Folosiţi o căutare liniară pentru a găsi valoarea maximă din ArrayList şi

afisaţi-o.

10. Locarea valorii maxime din ArrayList.

Creaţi un ArrayList care să reţină valori de tipul Integer. Populaţi ArrayList-ul cu

numere aleatoare între 1 şi 100. Afişaţi valorile pe ecran. Folosiţi o căutare liniară

pentru a găsi valoarea maximă şi poziţia acesteaia şi afişaţi-le.

11. Sortare.

Creaţi un ArrayList care să reţină valori de tipul Integer. Populaţi ArrayList-ul cu valori

aleatoare între 10 şi 99. Afişaţi valorile din ArrayList pe ecran. Apoi, folosind o

metodă de sortare la alegere, aranjaţi valorile de la cel mai mare la cel mai mic şi

afişaţile din nou.

Creaţi o funcţie separată pentru codul folosit la sortare.

12. Sortarea unui ArrayList de String-uri.


Creaţi un ArrayList care să reţina valori de tipul String. Adăugaţi 10 cuvinte aleatoare

în ArrayList (toate cu litere mici) într-o oarecare ordine. Afişaţi-le pe ecran. Folosind o

modalitate de sortare la alegere, aranjaţi valorile respective în ordine alfabetică şi

afişaţi-le din nou.

La fel ca la exerciţul anterior, creaţi o funcţie de sortare.

13. Sortarea propoziţiilor.

Creaţi un ArrayList care conţine valori de tipul String. Lăsaţi utilizatorul să introducă

valorile prin folosirea metodei nextLine() din Scanner. Folosind String.split(), împărţiţi

propoziţia introdusă de la tastatură şi apoi salvaţi şirurile de caractere respective într-

un ArrayList. Transformaţi toate şirurile de caractere în litere mici.

Folosind funcţia de sortare de la exerciţiul anterior, puneţi cuvintele în ordine

alfabetică şi afişaţi-le din nou.

14. Sortarea unui ArrayList de obiecte.

Creaţi o clasă care să stocheze informaţii legate de o maşină. Aceasta ar trebui să

conţină câmpuri pentru:

 marca (String)

 model (String)

 anul fabricaţiei (int)

 numărul de înmatriculare (String)

Creaţi un ArrayList cu obiecte de tipul Car şi adăugaţi valori în acesta.

În final, sortaţi ArrayList-ul de maşini după anul fabricaţiei şi afişaţi-le.


Suprascrierea metodei equals() şi
toString() pentru colecţii
Acem clasa Computer

package pack2;

public class Computer {

private String marca;

private int memorieRam;

private int frecventaProcesor;

public Computer(String marca, int memorieRam, int

frecventaProcesor) {

super();

this.marca = marca;

this.memorieRam = memorieRam;

this.frecventaProcesor = frecventaProcesor;
}

//getteri si setteri

În metoda main() creăm 2 computere.

package pack2;

public class Main {

public static void main(String[] args) {

Computer computer1 = new Computer("Acer", 4, 2500);

Computer computer2 = new Computer("Acer", 4, 2500);

}
}

Sunt cele două computere egale?

Din punct de vedere al obiectelor ele nu sunt egale. Ele sunt doua obiecte diferite pe

Heap, fiecare cu adresa proprie.

Dacă apelăm:

System.out.println(computer1);

System.out.println(computer2);

Obținem 2 adrese diferite.

pack2.Computer@1db9742

pack2.Computer@106d69c

Dacă acum în clasa Computer suprascriem metoda toString() din clasa Object: (Right

click > Source > generate toString())

@Override

public String toString() {

return "Computer [marca=" + marca + ", memorieRam=" +

memorieRam
+ ", frecventaProcesor=" +

frecventaProcesor + "]";

Iar acum rulăm din nou programul:

System.out.println(computer1);

System.out.println(computer2);

ne va da:

Computer [marca=Acer, memorieRam=4, frecventaProcesor=2500]

Computer [marca=Acer, memorieRam=4, frecventaProcesor=2500]

Au valori egale, însă asta, în continuare, nu înseamnă că sunt obiecte egale. Sunt 2

obiecte diferite, a căror atribute au valori identice.

Putem afla asta, dacă facem în metoda main():

if (computer1 == computer2){

System.out.println("Sunt unul si acelasi

obiect");

} else {
System.out.println("Sunt obiecte diferite.");

//sau

System.out.println( computer1 == computer2 );

//sau

boolean bool = computer1 == computer2;

System.out.println(bool);

Obținem:

Sunt obiecte diferite.

false

false
Cum putem face să scriem un bloc de cod care să afișeze true dacă cele două

computere au același proprietăți și să afișeze false dacă nu sunt la fel?

if (computer1.getMarca().equals(computer2.getMarca())

&& computer1.getMemorieRam() ==

computer2.getMemorieRam()

&& computer1.getFrecventaProcesor() ==

computer2.getFrecventaProcesor()) {

System.out.println( "Cele doua computere sunt la fel.

");

} else {

System.out.println( "Cele doua computere nu sunt la

fel.");

Acum obținem:
Cele doua computere sunt la fel.

În principiu ne-am atins scopul. Doar că în felul acesta de fiecare dată când vrem să

comparăm 2 calculatoare, trebuie să scriem de fiecare dată tot codul acesta. De

asemenea, dacă suntem un magazin care vinde 100 de obiecte diferite (computere,

telefoane, tablete, procesoare, cabluri, routere, swithcuri etc) metoda noastră main()

trebuie să știe de fiecare dată toate proprietățile tuturor obiectelor.

Și mai e o problemă. Dacă trebuie vreodată, în viitor, să adăugăm un nou atribut la

un computer, de exemplu: culoarea sau să stergem un atribut. Atunci, trebuie să

mergem peste tot în aplicație și să căutăm toate locurile în care am făcut această

verificare a similariății a două computere. Dacă uităm într-un singur loc să facem

updatarea codului, am compromis programul.

În fond, poate vreodată, vom scrie un alt program care să folosească clasa

Computer. (Apropos, și în Eclipse, puteți copia clase dintr-un program în altul cu

Copy + Paste în Package Explorer (coloana din stânga) sau muta clase dintr-un

program în altul cu Drag And Drop. )

Și în fond, ce trebuie să știe clasa Main toată bucătăria internă a clasei Computer?

Nu era mai simplu dacă clasa Computer avea o metodă care să returneze dacă 2

computere sunt similare?

În clasa Computer:

public String suntSimilare(Computer altComputer){

if ( this.marca.equals(altComputer.marca)
&& this.memorieRam == altComputer.memorieRam

&& this.frecventaProcesor ==

altComputer.frecventaProcesor){

return "Cele doua computere sunt similare. ";

} else {

return "Cele doua computere nu sunt

similare.";

iar în metoda main():

System.out.println(computer1.suntSimilare(computer2));

Obținem:

Cele doua computere sunt similare.

Dar putem face ca metoda noastră să returneze un boolean. În clasa Computer:

public boolean suntSimilare(Computer altComputer) {


if (this.marca.equals(altComputer.marca)

&& this.memorieRam ==

altComputer.memorieRam

&& this.frecventaProcesor ==

altComputer.frecventaProcesor) {

return true;

} else {

return false;

iar în metoda main():

System.out.println( computer1.suntSimilare(computer2)

);

//sau

boolean bool2 = computer1.suntSimilare(computer2) ;

System.out.println( bool2);

Obținem:
true

Dar dacă în clasa Telefon scriem o metoda suntAsemanatoare(Telefon altTelefon), in

clasa Tableta scriem metoda suntLaFel(Tableta altaTableta) iar în clasa Computer

avem metoda suntSimialare(Computer alt Computer) ? Va trebuie să ținem minte toate

aceste denumiri. Bine, e adevarat, că Eclipse ne ajută, oferindu-ne toate metodele

diponibile unui obiect când scriem telefon..

Mai există o soluție.

Toate clasele moștenesc automat clasa Object – care este superclasa tuturor

claselor. În clasa Object există metoda equals(Object altObiect) care se moștenește,

așadar, automat, de către toate clasele.

În mod normal, această metodă compară adresele din memorie a două obiecte,

returnând true dacă cele două obiecte au aceeași adresă (adică sunt unul și același

obiect) și false dacă au adrese diferite (adică sunt două obiecte distincte, care pot

avea însă valori identice la toate proprietățile lor).

Hai să vedem:

System.out.println(computer1.equals(computer2));

System.out.println(computer2.equals(computer1));

Obținem:

false
false

Clasa String a suprascris această metodă să nu mai compare adresele, ci să

compare conținutul, adică efectiv șirul de caractere:

String str1 = new String("Maria");

String str2 = new String("Maria");

System.out.println( str1.equals(str2));

Obținem:

true

De ce să nu facem și noi ceva similar? În clasa Computer, scriem:

public boolean equals(Object altObiect){

if (altObiect instanceof Computer){

Computer altComputer = (Computer)altObiect;

if (this.marca.equals(altComputer.marca)
&& this.memorieRam == altComputer.memorieRam

&& this.frecventaProcesor ==

altComputer.frecventaProcesor) {

return true;

}//end if

//aici se ajunge numai daca altObiect nu este de tip

Computer

//sau daca cele doua computere nu sunt similare.

return false;

Pentru că cei de la Sun când au creat Java (care ulterior a fost cumpărată de Oracle)

nu știau că noi la jademy vrem să comparăm 2 Computere, au făcut o metodă care

să compare orice fel de obiect Object.

Noi, însă, trebuie să facem verificarea, dacă obiectul pe care îl primește metoda este,

într-adevăr un Computer. Ca nu cumva să se compare un Computer cu un Telefon.


Dacă este de tip Computer să transformăm obiectul altObiect în Computer, pentru că

altObiect nu vede atributele clasei Computer.

if (altObiect instanceof Computer) {

Computer altComputer = (Computer) altObiect;

A cui este metoda equals()? A obiectului care a apelat-o. În main() computer1 o

apelează.

System.out.println(computer1.equals(computer2));

Obținem:

true

Să vedem acum cu două obiecte care nu sunt similare:

Computer computer4 = new Computer("Toshiba", 2, 1500);

System.out.println(computer1.equals(computer4));

Obținem:

false
Dar dacă computerul pe care îl pasăm ca argument în metodă este null? Adică

nenstanțializat?

Computer computer3 = null;

System.out.println( computer1.equals(computer3));

Obținem:

false

Metoda funcționează.

Metoda equals() se poate genera automat și de către Eclipse:

 Click drepata > Source > generate hashCode si equals

Eclipse va genera codul propriu.

ArrayList
Metodele contains(), indexOf() și remove() se ajută de equals().
În main()

Computer c1 = new Computer("Dell", 4, 2200);

ArrayList<Computer> computere = new

ArrayList<Computer>();

computere.add(c1);

computere.add(new Computer("Intel", 4, 2000));

computere.add(new Computer("Mac", 4, 1600));

computere.add(new Computer("Acer", 2, 1600));

computere.add(new Computer("Toshiba", 8, 2600));

Am adăugat 5 computere.

Cum căutăm dacă lista nostră conține un anumit computer? Pentru c1 este simplu:

System.out.println(computere.contains(c1));

Dar cum facem pentru celelalte? Că nu avem vreo variabilă de referință către ele.

Putem parcurge toată lista de computer și verifica pentru fiecare în parte dacă

computerul nostru se regăsește printre acestea, adică verificănd dacă marca e egală,

ram-ul e egal și frecvența procesorului este egală.


Dar există o metodă mult mai simplă. Creăm un obiect nou de tip Computer:

Computer computerDeLucru = new Computer("Mac", 4,

1600);

Este adevărat că computerDeLucru este un obiect nou, cu adresă proprie în memorie,

și care nu se găește efectiv în lista de computere.

Dar pentru că avem metoda equals(Object obj) suprascrisă în clasa noastră

Computer computere.contains() care face verificarea ajutându-se deequals(), chiar

dacă obiectul nu se regăesște efectiv în listă, va returna true, dacă există un obiect

similar cu vreunul dintre cele din listă.

În main()

computere.contains(computerDeLucru);

Obținem

true

La fel se întâmplă și cu indexOf(). indexOf(Obiect obiect) din clasa AraryList (a se

vedea API) returnează indexul primei apariții în listă a respectivului obiect pe care îl

căutăm.

int index = computere.indexOf(computerDeLucru);

System.out.println(index);
Obținem:

Pentru că un computer din marca Dell, cu 4 Gb RAM și 1600 GHz se găsește pe

indexul 2 (căsuța 3) din listă.

La fel se întâmplă și cu remove(). remove(Object obj) scoate din ArrayList obiectul

similar cu cel primit ca argument (parametru).

În main()

computere.remove(computerDeLucru);

System.out.println(computere);

Obținem:

[Computer [marca=Dell, memorieRam=4, frecventaProcesor=2200],

Computer [marca=Intel, memorieRam=4, frecventaProcesor=2000],

Computer [marca=Acer, memorieRam=2, frecventaProcesor=1600],

Computer [marca=Toshiba, memorieRam=8, frecventaProcesor=2600]]

După cum vedem, computerul Mac nu se mai află în listă.

Putem face și verificare cu contains():


System.out.println(

computere.contains(computerDeLucru));

Obținem:

false

Vectori (Arrays). Şiruri de caractere


(Strings).
Vectorul este o structură de dată foarte primitivă care reprezintă un grup de elemente

similare, accesate printr-un index. Structurile de date de tipul array pot fi stocate

eficient în calculator şi ne oferă o modalitate rapidă de a accesa toate elementele lor.

Array alocaţi static şi dinamic


Există două tipuri de array, care diferă prin modul de alocare a spaţiului. Tipul de

array static are o mărime constantă şi există pe toată durata de rulare a aplicaţiei.

Tipul de array dinamic este creat în momentul rulării programului şi pot fi ştersi când

nu mai sunt necesari. Aceştia pot fi destul de mari, chiar depăşind memoria fizică. Nu

putem însă modifica mărimea acestora, decât prin aplicarea următorului mecanism:

 Crearea unui nou array cu o dimensiune mai mare

 Copierea datelor existente din array-ul iniţial în noul array

Vectori de mărime fixă şi dinamică


Aşa cum este menţionat şi mai sus, vectorii nu pot fi redimensionaţi. În acest caz, ei

se numesc şi vectori de mărime fixă. Dar, ne putem crea un simplu mecanism prin

care să avem un vector care se poate redimensiona.

Mecanismul e simplu. Se alocă spaţiu pentru un vector dinamic şi îl împărţim

imaginar în două părţi. Prima parte conţine datele efective şi a doua spaţiu liber.

Când un nou element este adăugat, spaţiul liber este redus şi vice-versa. Această

abordare rezultă într-o risipă a spaţiului liber, dar avem toate avantajele unui vector şi

capabilitatea de a-i schimba mărimea dinamic. Mai jos sunt prezentate câteva definiţii

pentru acest tip de vector.

Vectorul dinamic are o capacitate, care conţine numărul maxim de elemente ce pot fi

reţinute. De asemenea, un asemenea array are o mărime logică, ce ne indică

numărul de elemente conţinute efectiv. De exemplu, trebuie să găsim numărul minim

din mai multe numere introduse de utilizator. Alocăm un spaţiu pentru a reţine 15

elemente, dar utilizatorul introduce doar 5 numere. Aşadar, capacitatea acestui array

este de 15 elemente, dar mărimea logică doar de 5. Când vectorii dinamici nu mai au

spaţii goale, va trebui să fie mărit prin crearea unui nou vector mai mare şi prin

copierea tuturor elementelor din vechiul vector în cel nou.

Conexiunea cu şirurile de caractere


Şirurile de caractere sunt asemănătoare cu vectorii dinamici. Mărimea logică a unui

String este indicată prin length.

Exemplu. String-ul ASCII "Hello!", reprezentat în interiorul calculatorului.

H e l l o !
72 101 108 108 111 33

Următorul program găseşte valoarea minimă dintr-o serie de valori introduse.

Observaţi că Java permite doar vectorii alocaţi dinamic.

import java.util.Scanner;

public class Arrays {

public static void main(String[] args) {

Scanner keyboard = new Scanner(System.in);

// dynamically allocated array

int arr[] = new int[15];

int n = 0;

int value = 0;

System.out.println("Enter values. Type \"-1\" to stop: ");

while (n <15 && value != -1) {

value = keyboard.nextInt();
keyboard.nextLine();

if (value != -1) {

arr[n] = value;

n++;

if (n == 0) {

System.out.println("You have entered no values, bye!");

} else {

int minimum = arr[0];

for (int i = 1; i <n; i++) {

if (arr[i] <minimum)

minimum = arr[i];

System.out.print("The minimal value is " + minimum);


}

Coada de priorităţi
În practică, avem deseori de a face cu priorităţile. De exemplu, într-o listă de TO-DO,

fiecare task are asociată o anumită importanţă. Este absolut necesar să ne luăm

maşina de la service (cea mai mare prioritate), şi este posibil să ne uităm la un film

(cea mai joasă prioritate). În afară de exemplele din viaţa reală, multe task-uri din

calculator funţionează pe baza priorităţilor. Cel mai uzual exemplu este algoritmul lui

Dijkstra pentru găsirea celei mai rapide căi. Lista de priorităţi ne permite să lucrăm cu

obiecte care au asociată o prioritate.

În aplicaţie avem o pereche (prioritate, item) unde un item este o informaţie

suplimetară asociată cu prioritatea. Pentru menţinerea simplităţii, vom omite

priorităţile şi vom considera că pentru itemele e1, e2: e1 < e2 înseamnă că e1 are o

prioritate mai mare decât e2.

Operaţii
 PriorityQueue create() - crează o coadă de priorităţi goală

 boolean isEmpty() - ne zice dacă avem coadă goală sau nu

 insert(PriorityQueue pq, Item e) - inserează itemul e la coada de priorităţi pq

 Item minimum(PriorityQueue pq) - ne dă itemul minim din lista de priorităţi pq

(precondiţie: pq să nu fie goală)


 removeMin(PriorityQueue pq) - şterge itemul minim din lista de priorităţi pq

(precondiţie: pq să nu fie goală)

 destroy(PriorityQueue pq) - distruge coada de priorităţi pq

Stiva
Stiva este o structură de date fundamentală şi este folosită în mulţi algoritmi şi

aplicaţii, ca de exemplu:

 implicit în recursivitate

 pentru a evalua expresii

 pentru a verifica corectitudinea secvenţelor de paranteze

 etc

În primul rând vom descrie structura de date abstractă stiva şi apoi o vom

exemplifica.

Tipul de date abstract Stiva


Exemple din lumea reală includ o stivă de cărţi, unde putem folosi doar cartea de

deasupra. Putem avea şase operaţii pe stiva:

 punem o carte deasupra (push)

 vedem ce carte este deasupra (peek)

 scoatem cartea de deasupra (pop)

 verificăm dacă există cărţi în stiva (isEmpty)

 două operaţii pentru a crea şi a distruge stiva

Acestea pot fi structurate în următoarele metode.


Operaţii pe Stivă
 Stack create()

 creează un Stack gol

 boolean isEmpty(Stack s)

 ne indică dacă stiva s este goală

 push(Stack s, Item e)

 adaugă elementul e deasupra stivei s

 Item peek(Stack s)

 întoarce elementul de deasupra

 Precondiţie: stiva să nu fie goală

 pop(Stack s)

 şterge elementul de deasupra stivei s

 Precondiţie: stiva să nu fie goală

 destroy(Stack s)

 distruge stiva s
Axiome
 stivele noi create sunt goale

 după ce adăugăm un element la stivă, aceasta nu mai este goală

 peek ne indică cel mai recent element adăugat

 stiva rămâne "neatinsă" după ce executăm o pereche de comenzi push şi pop,

una după cealaltă


Exemlple
Având definiţiile de mai sus, o stivă poate fi schiţată în modul următor:
Implementări
Există două implementări pentru o stivă. Fiecare din ele are avantaje şi dezavantaje:

 implementare pe bază de array

 implementare pe bază de listă simplu înlănţuită


Exerciţiu
Putem implementa un Stack folosind o listă simplu îlănţuită la modul următor:
Dicţionarul
Dicţionarul (map, listă de asocieri) este o structură de date, care reprezintă asocierea

unei chei unice cu o valoare. Putem să legăm o cheie de o valoare, să ştergem o

cheie (şi totodată valoarea asociată cu aceasta) şi să căutăm o cheie după valoare.

Valorile nu sunt necesare să fie unice. Un exemplu simplu este dicţionarul pentru

explicaţii. În acest exemplu, cuvintele sunt chei, iar explicaţiile lor sunt valorile.

Operaţii
 Dictionary create()

 crează un dicţionar gol

 boolean isEmpty(Dictionary d)

 ne indică dacă dicţionarul d este gol

 put (Dictionary d, Key k, Value v)

 asiciază cheia k cu valoarea v

 dacă cheia k este prezentă în dicţionar, valoare veche este înlocuită de

 Value get(Dictionary d, Key k)

 întoarce valoarea asociată cu cheia k sau null, dacă dicţionarul nu

conţine cheia respectivă

 remove(Dictionary d, Key k)

 şterge cheia k cu valoarea asociată

 destroy(Dictionary d)

 şterge dicţionarul d

Hash Table
Hash table (sau hash map) este una din implementările posibile ale dicţionarului.

Acesta mapează practic chei unice la valori asociate. Din perspectiva implementării,

un hash table este o structură de tip array, care foloseşte funcţii hash pentru a face
conversia unei chei în indexul elementului din array, unde valoarea asociată este

regăsită.

Funcţii hash
Funcţiile hash sunt o parte importantă din tabelul hash. O funcţie hash este bună

dacă implementează o distribuţie uniformă a valorilor hash. Alte proprietăţi ale

funcţiei hash, necesare pentru un hash de calitate sunt detaliate mai jos. Principalul

motiv pentru care sunt foarte importante este că pot cauza coliziuni şi alte efecte

nedorite, ce pot afecta performanţa.

Tabelul hash şi factorul de încărcare


Structura de date pe care este implementat un hash table este array-ul. Factorul de

încărcare este rata între numărul de elemente stocate în array şi mărimea acestuia.

Hash table poate avea o mărime constantă sau poate fi redimensionat dinamic, când

factorul de încărcare trece de un anume prag. Redimensionarea este făcută înainte

ca tabelul să devină plin pentru a menţine numărul coliziunilor sub un anume nivel şi

pentru a preveni degradarea.

Coliziuni
În cazul în care funcţia ne întoarce aceeaşi valoare hash pentru chei diferite, se

produce un efect denumit coliziune. Coliziunile sunt practic inevitabile şi ar trebui

considerate la implementarea tabelului. Datorită acestora, cheile sunt stocate şi ele

în tabel, pentru a face diferenţe între perechile cheie-valoare cu acelaşi hash. Există

mai multe modalităţi de rezolvare a coliziunilor. Practic, există două strategi diferite:

 Adresarea închisă (hashing deschis). Fiecare slot din tabel conţine o legătură

către o altă structură de date, cum ar fi o listă simplu înlănţuită, care stochează
perechile cheie-valoare cu acelaşi hash. Când apare coliziunea, această structură

de date caută liniar perechea cheie-valoare, care se potriveşte cu cheia.

 Adresare deschisă (hashing închis). Fiecare slot conţine perechea cheie-

valoare. La apariţia coliziunii, algoritmul de adreasare deschisă calculează o nouă

locaţie (de exemplu, următoarea disponibilă) pentru un slot liber. Tabelele hash,

bazate pe strategia adresării deschise au performanţe drastic scăzute, când

tabelul este foarte plin (un factor de umplere de 0.7 sau mai mult).

Exemplu simplu de hash table


Vom exemplifica un hash table foarte simplu. Foloseşte o funcţie hash simplă,

coliziunile sunt rezolvate prin adresare deschisă şi mărimea tabelului este constantă.

Exemplul demonstrează bazele tehnicii de hashing.

Hash Table

Array-ul folosit la implementare are o dimensiune de 128 elemente, iar fiecare slot

conţine perechea cheie-valoare. Cheia este stocată pentru a diferenţia între perechile

cheie-valoare cu acelaşi hash.

Funcţia Hash

Tabelul ne permită să înregistrăm numere întregi. Funcţia hash folosită calculează

restul împărţirii la 128. Din perspectiva implementării, aceasta poate folosi operatorul

modul sau operatorul pe bit "şi" cu valoarea 127.

Rezolvarea coliziunilor

Pentru a rezolva coliziunile este folosită inserarea lineară. În cazul în care slot-ul

indicat de funcţia hash este deja ocupat, algoritmul încearcă să găsească un slot

liber prin căutarea consecutivă în sloturile din array.


Exemplu de cod

Această implementare are un bug. Dacă nu mai există niciun loc liber, bucla care

caută un loc liber va rula la infinit. Acest lucru nu se va întâmpla în cazul unei

implementări reale, deoarece se foloseşte de obicei un array dinamic. Pentru

simplitate, a fost eliminată ştergerea.

public class HashEntry {

private int key;

private int value;

HashEntry (int key, int value) {

this.key = key;

this.value = value;

public int getKey() {

return key;

}
public int getValue() {

return value;

public class HashMap {

private final static int TABLE_SIZE = 128;

HashEntry[] table;

HashMap() {

table = new HashEntry[TABLE_SIZE];

for (int i = 0; i <TABLE_SIZE; i++)

table[i] = null;

}
public int get(int key) {

int hash = (key % TABLE_SIZE);

while (table[hash] != null && table[hash].getKey() != key)

hash = (hash + 1) % TABLE_SIZE;

if (table[hash] == null)

return -1;

else

return table[hash].getValue();

public void put(int key, int value) {

int hash = (key % TABLE_SIZE);

while (table[hash] != null && table[hash].getKey() != key)

hash = (hash + 1) % TABLE_SIZE;

table[hash] = new HashEntry(key, value);


}

Colecţii Java
Înainte de Java 2, existau clase precum Dictionary, Vector, Stack sau Properties care

erau independente una de cealaltă. Acesteora le lipsea un sistem de uniformizare,

iar modul de folosire a lor diferea de la o clasă la alta.

Framework-ul pentru colecţii din Java a fost proiectat pentru a atinge următoarele

scopuri:

 Framework-ul a trebuit sa aducă performanţe mari. Implementările pentru

colecţiile fundamentale (array dinamici, liste simplu înlănţuite, arbori şi

hashtables) sunt foarte eficiente.

 Framework-ul permite colecţiilor diferite să funcţioneze într-un mod similar şi

cu un grad înalt de interoperabilitate.

 Extinderea sau adaptarea unei colecţii a trebuie să fie facilă

Pentru a îndeplini aceste cerinţe, întregul framework de colecţii este proiectat pe un

set de interfeţe standard. Câteva implementări standard precum LinkedList, HashSet

şi TreeSet ale acestor interfeţe ne sunt puse la dispoziţie pentru a le folosi, dar putem

implementa propriile colecţii dacă avem nevoie.

Framework-ul de colecţii este o arhitectură unificată pentru reprezentarea şi

manipularea colecţiilor. Toate colecţiile conţin următoarele:


 Interfeţe: Acestea sunt tipuri abstracte de date care reprezintă colecţiile.

Interfeţele permit manipularea colecţiilor independent de implementarea acestora.

În limbajele orientate pe obiect, interfeţele formează o ierarhie

 Implementări, sau mai exact clase: Acestea sunt implementări concrete ale

interfeţelor din colecţii. În esenţă, sunt structuri de date refolosibile.

 Algoritmi: Acestea sunt metodele prin care facem diverse operaţii, cum ar fi

căutări şi sortări, pe obiecte care implementează intefeţele din colecţii. Algoritmii

sunt polimorfici: mai exact, aceeaşi metodă poate fi folosită pe mai multe

implementări ale interfeţei de colecţie corespunzătoare.

Pe lângă colecţii, framework-ul defineşte câteva interfeţe şi clase pentru map. Map-

urile pot stoca perechi de cheie/valoare. Cu toate că map-urile nu sunt colecţii în

adevăratul sens al cuvântului, acestea sunt complet integrate cu restul colecţiilor.

Framework-ul

 Clasele care implementează interfața List sunt ordonate după index.

 Clasele care implementează interfața Set nu sunt ordonate (nu au index), dar

nu acceptă duplicate. La un Set tipic nu se garantează ordinea elementelor.


 Clasele care implementează interfața Map au cheie și valoare, asemenea unui

dicționar. Cheile sunt obiecte. Cheile nu pot avea duplicate și nu sunt ordonate

(Cheile formează un Set).

În următorul tabel termeul ordonat înseamnă că ordinea elementelor sunt stabilite în

funcţie de index. Pentru colecţiile neordonate,System.out.prinln(colectie); afişează

aleatoriu elementele.

Colecţia Ordonată Sortată Acceptă Duplicate Cheie-Valoare


Vector Ordonată Nesortată Da Nu
ArrayList Ordonată Nesortată Da Nu
LinkedList Ordonată Nesortată Da Nu
Stack Ordonată Nesortată Da Nu
HashSet Neordonată Nesortată Nu Nu
LinkedHashSet Neordonată Nesortată Nu Nu
TreeSet Ordonată Sortată Nu Nu
HashMap Neordonată Nesortată Nu (la cheie) Da
LinkedHashMap Neordonată Nesortată Nu (la cheie) Da
Hashtable Neordonată Nesortată Nu (la cheie) Da
TreeMap Ordonată Sortată Nu (la cheie) Da

Collection Framework a fost implementată în java în versiunea

1.2. Vector și Hashtable au fost scrise în versiunea 1.0 și au fost introduse în

Collection Framework în versiunea 1.2.

Colecţia Descriere
Vector Primul tip de vector, din versiunea java 1.0. În versiunea 1.2 a fost
introdus în Collection Framework. Are metodele sincronizate pentru firele
de execuție.
ArrayList Un List tipic. Nu are metodele sincronizate.
LinkedList Asemenea lui ArrayList dar fiecare element are câte o referință către
elementul precendent și câte una către elementul următor. Este ideal pentru
inserarea sau ștergerea de elemente la mijlocul unei liste, la listele mari.
Stack Elementele formează o stivă. Nu poate fi accesat un element, decât dacă
Colecţia Descriere
sunt scoase cele de deasupra lui.
HashSet Un Set tipic. Nu are metodele sincronizate.
LinkedHashSet Are tehnologie asemenea lui LinkedList, elementele au 2 referințe: către
elementul precendent și cel următor.
TreeSet Elementele sunt sortate după ordinea naturală (în cazul wrapper classes
sau stringuri) sau după metodologia scrisă de programator. Un nou
element se așează din start la locul lui.
HashMap Un Map tipic. Nu are metodele sincronizate.
LinkedHashMap Moștenește pe HashMap. Are tehnologie asemenea lui LinkedList,
elementele au 2 referințe: către elementul precendent și cel următor.
Hashtable Primul tip de dicționar din versiunea 1.0. În versiunea 1.2 a fost introdus în
Collection Framework. Are metodele sincronizate pentru firele de
execuție.
TreeMap Cheile sunt sortate după ordinea naturală (în cazul wrapper classes sau
stringuri) sau după metodologia scrisă de programator. Un nou element se
așează din start la locul lui.
Interfeţele pentru colecţii
Framework-ul de colecţii defineşte o serie de interfeţe. Această secţiune descrie pe

scurt fiecare din acestea.

 Collection

 Aceasta vă dă posibilitatea să lucraţi cu un grup de obiecte; este la

baza ierarhiei de colecţii.

 List

 Extinde Collection şi un List stochează o colecţie ordonată de

elemente.

 Set

 Extinde Collection pentru a manipula seturi, care trebuie să conţină

elemente unice.

 SortedSet

 Extinde Set pentru a manipula seturi sortate.


 Map

 Mapează o cheie unică la o valoare.

 Map.Entry

 Descrie un element (o pereche cheie/valoare) într-un map. Aceasta

este o clasă interioară lui Map.

 SortedMap

 Extinde Map pentru a menţine cheile în ordine crescătoare

 Enumeration

 Este o interfaţă care defineşte metodele folosite pentru a enumera

(obţine pe rând) elementele dintr-o colecţie de obiecte. Această interfaţă are

ca succesor Iterator.
Clasele pentru Colecţii
Java ne oferă o serie de clase pentru colecţii care implementează interfeţele

Collection. Unele clase ne dau implementare completă care poate fi folosită ca atare,

iar unele sunt clase abstracte pentru care avem o implementare schelet ce poate fi

folosită ca punct de plecare pentru a implementa colecţii concrete.

 AbstractCollection

 Implementează majoritatea interfeţei Collection.

 AbstractList

 Extinde AbstractCollection şi implementează majoritatea interfeţei List.

 AbstractSequencialList

 Extinde AbstractList pentru a fi folosită de o colecţie care foloseşte

accesul secvenţial şi nu aleator al elementelor.

 LinkedList

 Implementează o listă înlănţuită prin extinderea clasei

AbstractSequencialList.
 ArrayList

 Implementează un array dinamic prin extinderea AbstractList.

 AbstractSet

 Extinde AbstractCollection şi implementează majoritatea interfeţei Set.

 HashSet

 Extinde AbstractSet pentru a fi folosit cu un tabel hash.

 LinkedHashSet

 Extinde HashSet pentru a permite iteraţii la ordinea inserării

elementelor.

 TreeSet

 Implementează un set sortat într-un arbore. Extinde AbstractSet.

 AbstractMap

 Implementează majoritatea interfeţei Map.

 HashMap

 Extinde AbstractMap pentru a fi folosită cu un tabel hash.

 TreeMap

 Extinde AbstractMap pentru a folosi un arbore.

 WeakHashMap

 Extinde AbstractMap pentru a folosi un tabel hash cu chei slabe.

 LinkedHashMap

 Extinde HashMap pentru a permite iteraţii la ordinea inserării

elementelor.

 IdentityHashMap

 Extinde AbstractMap şi foloseşte egalitatea de referinţă la comparaţia

documentelor.
Clasele AbstractCollection, AbstractSet, AbstractList, AbstractSequencialList şi

AbstractMap ne oferă implementări de bază ale interfeţelor din colecţii pentru a

minimiza efortul necesar implementării lor.

 Vector

 Implementează un array dinamic. Este similar cu ArrayList.

 Stack

 Este o subclasă a Vector care implementează o stivă standard last-in,

first-out (LIFO).

 Dictionary

 Este o clasă abstractă care reprezintă un sistem de stocare cheie-

valoare şi funcţionează ca un Map.

 Hashtable

 A făcut parte din pachetul original java.util şi este o implementare

concretă a Dictionary.

 Properties

 Este o subclasă a Hashtable. Este folosită pentru a menţine o listă de

valori în care cheia este un String, iar valoarea tot un String.

 BitSet

 Creează un tip special de array care reţine valori de tipul bit. Acest

array poate creşte în dimensiune, dacă este necesar.


Algoritmi pentru Colecţii
Framework-ul de colecţii defineşte câţiva algoritmi care pot fi aplicaţi colecţiilor şi

map-urilor. Aceşti algoritmi sunt definiţi ca metode statice în clasa Collections.

Unele din metode pot arunca ClassCastException, care apare când încercăm să

comparăm tipuri incompatibile, sau UnsuportedOperationException, care apare când

încercăm să modificăm o colecţie care nu poate fi modificată.


Collections defineşte trei variabile statice: EMPTY_SET, EMPTY_LIST şi

EMPTY_MAP.

Sortate HashSet sau HashMap


Un HashSet sau un HashMap nu pot fi sortate, pentru că sunt colecții neordonate. Un

Set nu are metode cu acces randomizat (cum ar fi metoda .get(index) de la

ArrayList), ceea ce este necesară pentru algoritmii de sorate.

Un HashSet nu garantează nici o odrine a elementelor. Dacă avem nevoie de

această garanție, este recomandat a se folosi un TreeSet (SortedSet).

Dar, dacă avem nevoie de o sortare de ocazie, putem transforma Set-ul într-un List

(ArrayList) și sorta această listă.

Folosirea directă a unei interfeţe


În java Api, la Clasa LinkedList vedem că există metoda listIterator(), dar nu există și

iterator(). Scrie mai jos că metoda iterator() este moștenită de la interfața List. Totuși,

o putem folosi. Cum o putem folosi? Folosim direct metoda din interfața List? Putem

folosi o metodă dintr-o interfață?

Dacă ne uităm atent, observăm că metoda iterator() este moștenită și de la clasa

AbstractSequantialList, care la rândul ei și ea implementează interfața List. Deci

observăm că practic sunt mai multe clase în această ierarhie de clase și interfețe.

Genericitate
Metodele generice în Java ne permit aplicarea aceluiaşi algoritm pentru mai multe

tipuri de date. De exemplu, am putea folosi aceeaşi modalitate de sortare pentru un


array de Integer, String sau orice alt tip. Metodele şi clasele generice în Java lasă

programatorii să specifice cu o singură declaraţie o serie de tipuri de date

asemănătoare.

Un alt avantaj al genericităţii este siguranţa tipurilor la compilare care permit

programatorilor să găsească tipurile de date invalide în momentul compilării.

Metode Generice
Putem scrie o singură declaraţie de metodă generică care poate fi apelată cu

parametrii de diferite tipuri. În funcţie de parametrii trimişi, compilatorul gestionează

fiecare apel de metodă într-un mod corespunzător.

Regulile pentru definirea metodelor generice:

 Toate declaraţiile metodelor generice au ca şi tip pentru unul din parametrii o

secţiune delimitată prin diamant (< >) care trebuie scris întainte de tipul returnat.

 Fiecare secţiune conţie unul sau mai multe tipuri separate prin virgule. Un tip

parametric, cunoscut şi ca o variabilă tip este un identificator care specifică tipul

generic al parametrului.

 Parametrul tip poate fi folosit pentru a declara tipul returnat şi se comportă ca

un înlocuitor pentru tipurile argumentelor trimise la metoda generică, cunoscute şi

ca tipuri de parametrii efective.

 Corpul unei metode generice este declarat ca la oricare altă metodă. Tipul

parametric poate fi doar un tip referinţă, nu şi primitiv (int, double, char, etc).

Exemplu

Următorul exemplu ilustrează modul în care putem afişa un array printr-o metodă

generică:
public class GenericMethodTest {

// generic method printArray

public static<E>void printArray( E[] inputArray ) {

// Display array elements

for (E element : inputArray) {

System.out.printf("%s ", element );

System.out.println();

public static void main(String args[]) {

// Create arrays of Integer, Double and Character

Integer[] intArray ={1, 2, 3, 4, 5};

Double[] doubleArray ={1.1, 2.2, 3.3, 4.4};

Character[] charArray ={'H', 'E', 'L', 'L', 'O'};


System.out.println("Array integerArray contains:");

printArray(intArray);// pass an Integer array

System.out.println("\nArray doubleArray contains:");

printArray( doubleArray );// pass a Double array

System.out.println("\nArray characterArray contains:");

printArray( charArray );// pass a Character array

Ceea ce ar produce următorul rezultat:

Array integerArray contains:

123456

Array doubleArray contains:

1.12.23.34.4
Array characterArray contains:

H E L L O

Tipul generic legat


De multe ori putem să vrem să restricţionăm tipul parametrilor pe care îl folosim la

apelul metodei. De exemplu, o metodă care foloseşte doar numere, ar putea necesita

doar instanţe ale clasei Number şi ale subclaselor ei. Pentru această restricţionare

putem folosi parametrii legaţi.

Pentru a declara un astfel de parametru, scriem numele parametrului tip, urmat de

cuvântul cheie extends şi de clasa din care trebuie să aparţină / să derive.

Exemplu

Următorul exemplu ilustrează modul în care extends este folosit la modul general,

atât în sensul de "extends" (pentru clase), cât şi în sensul "implements" (pentru

interfeţe). Acest exemplu foloseşte o metodă generică pentru a întoarce cel mai mare

din trei obiecte de tipul Comparable.

public class MaximumTest {

// determines the largest of three Comparable objects

public static <T extends Comparable<T>> T maximum(T x, T y, T z)

T max = x; // assume x is initially the largest


if (y.compareTo(max)> 0) {

max = y; // y is the largest so far

if (z.compareTo(max)> 0) {

max = z; // z is the largest now

return max;// returns the largest object

public static void main(String args[]) {

System.out.printf("Max of %d, %d and %d is %d\n\n", 3, 4, 5,

maximum(3, 4, 5));

System.out.printf("Maxm of %.1f, %.1f and %.1f is %.1f\n\n",

6.6, 8.8, 7.7, maximum(6.6, 8.8, 7.7));

System.out.printf("Max of %s, %s and %s is %s\n", "pear",

"apple", "orange", maximum("pear", "apple", "orange"));

}
}

Ceea ce ar produce următorul rezultat:

Maximum of 3, 4 and 5 is 5

Maximum of 6.6, 8.8 and 7.7 is 8.8

Maximum of pear, apple and orange is pear

Clase generice
Declaraţia unei clase generice este asemănătoare cu declaraţia unei clase normale,

cu excepţie că numele clasei este urmat de o secţiune pentru parametru.

La fel ca şi metodele generice, secţiunea pentru tipul parametric al unei clase

generice poate conţine unul sau mai mulţi parametrii separaţi prin virgule. Aceste

clase sunt cunoscute sub denumirea de clase parametrizate sau tipuri parametrizate

pentru că accepta unul sau mai mulţi parametrii.

Exemplu

Exemplul următor ilustrează modul în care definim o clasă generică:

public class Box<T> {

private T t;
public void add(T t){

this.t = t;

public T get(){

return t;

public static void main(String[] args) {

Box<Integer> integerBox = new Box<Integer>();

Box<String> stringBox = new Box<String>();

integerBox.add(newInteger(10));

stringBox.add(new String("Hello World"));

System.out.printf("Integer Value :%d\n\n",

integerBox.get());

System.out.printf("String Value :%s\n", stringBox.get());


}

Acest exeplu poate produce rezultatul următor:

IntegerValue: 10

StringValue: HelloWorld

Parameterized collection = generics


collection
ArrayList neparametrizat
Un ArrayList neparametrizat primește orice obiect de tip Object. Pentru că toate

clasele în java moștenesc pe Object, putem introduce în el orice tip de obiect.

ArrayList ar = new ArrayList();

ar.add("sas");

ar.add(new Integer(4));

ar.add(new Double(345.56));

ar.add(34543.7); // autoboxing automat de la double la Double,

introdus în java 1.5


String str = ar.get(2); // eroare de compilare: Cannot convert

from Object to String

// ar.get(2) returnează un Object.

// e ca si cum am fi scris:

// ArrayList<Object> ar = new

ArrayList<Object>();

// aici trebuie un down-cast.

String str = (String) ar.get(2); // nu mai avem eroare de compilare

// dar o sa avem o eroare la

rulare

// la indexul 2 avem un Double

// solutia:

if (ar.get(2) instanceof String){

String str = (String) ar.get(2);

ArrayList parametrizat – începând cu java 1.5


ArrayList-ul este un tip de Collection. O colecție parametrizată (parameterized

collection) se mai numește și generics collection.

List<Computer> lista = new ArrayList<Computer>();

Într-o astfel de colecție (parameterized / generics) nu se pot introduce decât

Computere.

Și iteratorul poate fi parametrizat (generics):

Iterator<Computer> iter = lista.iterator<Computer>();

ListIterator<Computer> iter = lista.listIterator<Computer>();

Diferența dintre Iterator și ListIterator este că ListIterator poate merge și înainte

și înapoi în iterararea unei liste, în timp ce Iterator doar înainte. Pe

lângă next() și hasNext(), ListIterator are și metodele previous() și hasPrevious().

O colecție, cum ar fi ArrayList-ul poate fi definită cu mai mulți parametrii:

ArrayList <Integer, Double> listaDeNumere = new ArrayList <Integer,

Double>();

listaDeNumere.add( new Integer(23) );

listaDeNumere.add( new Double(578.32) );

Clase parametrizate
Putem crea clase parametrizate (generice) care pot avea atribute diferite în funcție

de parametrul primit la rulare.

public class ClasaMeaGenerica <A> {

// A nu este un type. Va fi înlocuit cu un type real la runtime.

private A atribut;

public void setAtribut ( A atribut) {

this.atribut = atribut;

public A getAtribut(){

return this.atribut;

public static void main (String[] args) {


ClasaMeaGenerica<String> cg1 = new ClasaMeaGenerica

<String>();

cg1.setAtribut (“Hello”);

System.out.println(cg1.getAtribut());

ClasaMeaGenerica<Computer> cg2 = new

ClasaMeaGenerica<Computer>();

cg2.setAtribut(new Computer());

System.out.println(cg2.getAtribut());

// cg1.setAtribut(new Computer()); // eroare de compilare

// cg2.setAtribut("Hello"); // eroare de compilare

Iteratorul
De multe ori este necesar să parcurgem elementele unei colecţii. De exemplu, vrem

să afişăm fiecare element. Cea mai uşoară modalitate este să folosim un iterator,

care este un obiect ce implementează una din interfeţele Iterator sau ListIterator.
Iterator ne permite să ciclăm prin colecţie, pentru a prelua sau şterge elemente.

ListIterator extinde Iterator şi permite traversarea bidirecţională a unei liste şi

modificarea elementelor.

Înainte de a accesa o colecţie printr-un iterator, trebuie să îl obţinem. Fiecare din

clasele pentru colecţii ne pun la dispoziţie o metodă iterator() care ne întoarce un

iterator de la începutul colecţiilor. Prin folosirea acestui obiect, putem accesa fiecare

element din colecţii pe rând.

De obicei, pentru a folosi un iterator, parcurgem următorii paşi:

 Obţinem iteratorul de la începutul colecţiei prin metoda iterator().

 Pregătim o buclă ce verifică hasNext(). Aceasta va rula atât timp cât hasNext()

întoarce true.

 În buclă, obţinem fiecare element prin apelul next().

Pentru colecţiile care implementează List, putem obţine chiar un ListIterator.

Exemplu
Exemplul de mai jos demonstrează atât Iterator cât şi ListIterator. Acesta foloseşte

un obiect de tipul ArrayList, dar principiul general se aplică la orice tip de colecţie.

Desigur, ListIterator este disponibil doar pentru acele colecţii care implementează

interfaţa List.

import java.util.*;

public class IteratorDemo {


public static void main(String args[]) {

// Create an array list

ArrayList al = new ArrayList();

// add elements to the array list

al.add("C");

al.add("A");

al.add("E");

al.add("B");

al.add("D");

al.add("F");

// Use iterator to display contents of al

System.out.print("Original contents of al: ");

Iterator itr = al.iterator();


while(itr.hasNext()) {

Object element = itr.next();

System.out.print(element + " ");

System.out.println();

// Modify objects being iterated

ListIterator litr = al.listIterator();

while(litr.hasNext()) {

Object element = litr.next();

litr.set(element + "+");

System.out.print("Modified contents of al: ");

itr = al.iterator();
while(itr.hasNext()) {

Object element = itr.next();

System.out.print(element + " ");

System.out.println();

// Now, display the list backwards

System.out.print("Modified list backwards: ");

while(litr.hasPrevious()) {

Object element = litr.previous();

System.out.print(element + " ");

System.out.println();

Ceea ce ar produce următorul rezultat:


Original contents of al: C A E B D F

Modified contents of al: C+ A+ E+ B+ D+ F+

Modified list backwards: F+ D+ B+ E+ A+ C+

Comparatorul
Atât TreeSet cât şi TreeMap stochează elementele sortate. Dar, comparatorul este

cel care determină excat ce înseamnă să fie sortat. Interfaţa Comparator defineşte

două metode: compare() şi equals().

Metoda compare()

 int compare(Object obj1, Object obj2)

 obj1 şi obj2 sunt obiectele ce trebuie comparate.

 Această metodă întoarce zero dacă cele două sunt egale.

 Metoda întoarce o valoare pozitivă dacă obj1 este mai mare ca obj2.

 O valoare negativă este întoarsă dacă obj1 este mai mic ca obj2.

 Prin suprascrierea lui compare(), putem modifica ordinea stocării obiectelor.

De exemplu, pentru a sorta în ordine inversă, putem crea un comparator care

inversează rezultatul comparării.

Metoda equals()

 boolean equals(Object obj)

 Testează dacă un obiect este egal cu comparatorul apelant.

 obj1 este obiectul care va fi testat pentru egalitate.

 Această metodă întoarce true dacă obj şi obiectul care a apelat metoda sunt

obiecte Comparator şi folosesc aceeaşi ordonare.


 Altfel, întoarce false.

 Suprascrierea nu este necesară, iar mulţi comparatori nu o suprascriu.


Exemplu

class Dog implements Comparator<Dog>, Comparable<Dog>{

private String name;

private int age;

Dog() {

Dog(String n, int a) {

name = n;

age = a;

public String getDogName(){


return name;

public int getDogAge(){

return age;

// Overriding the compareTo method

public int compareTo(Dog d){

return (this.name).compareTo(d.name);

// Overriding the compare method to sort the age

public int compare(Dog d, Dog d1){

return d.age - d1.age;

}
}

public class Example{

public static void main(String args[]){

// Takes a list o Dog objects

List<Dog> list = new ArrayList<Dog>();

list.add(new Dog("Shaggy", 3));

list.add(new Dog("Lacy", 2));

list.add(new Dog("Roger", 10));

list.add(new Dog("Tommy", 4));

list.add(new Dog("Tammy", 1));

Collections.sort(list);// Sorts the array list

for(Dog a : list)//printing the sorted list of names

System.out.print(a.getDogName() + ", ");


// Sorts the array list using comparator

Collections.sort(list, new Dog());

System.out.println(" ");

for(Dog a : list)//printing the sorted list of ages

System.out.print(a.getDogName() +" : "+

a.getDogAge() + ", ");

Ceea ce ar produce următorul rezultat.

This would produce the following result:

Lacy, Roger, Shaggy, Tammy, Tommy,

Tammy : 1, Lacy : 2, Shaggy : 3, Tommy : 4, Roger : 10,


Sortarea unui List (ArrayList,
LinkedList, Vector, Stack) și a unui
array
 Sortarea unui array fromat din primitive sau Stringuri

 Sortarea unui ArrayList fromat din wrapper classes sau Stringuri

În ordine crescătoare
Practic implementăm noi metodele: Arrays.sort(int[]

vectorulMeu) și Collections.sort(ArrayList arraylist).

Algortimi de sortare

Varianta 1: Utilizînd o buclă for parcurgem vectorul si luam valoarea cea mai mică și

o punem pe prima poziție într-un alt vector. Modificăm valoarea la cea mai mare

valoare pe care o poate lua un integer: Integer.MAX_VALUE Și tot așa.

Varianta 2: folosind metoda bubbling. Se compara valorile din primii doi indexi. Dacă

nu sunt în ordine, se inversează. Apoi se compară indexul 2 cu indexul 3. Dacă nu

sunt în ordine, se inversează. Tot așa până la final. Astfel, valorile urcă, asemenea

bulelor de soda într-un pahar. (bubbling). Se parcurge din nou vectorul de mai multe

ori, până când nu mai au loc inversări. Pentru a ști dacă nu au mai avut loc inversări

folosim o variabilă de tip boolean. Mai multe detalii

pe: http://mathbits.com/MathBits/Java/arrays/Bubble.htm. Încercați să

rezolvați fără să vă uitați la răspuns.

Varianta 3: Prin recursivitate (backtracking).

Folosind clasele din java API:


 Arrays.sort(int[] vectorulMeu)

 Collections.sort(ArrayList arraylist)

Această sortare se face după ordinea naturală a elementelor Este posibilă pentru că

clasele Wrapper și String implementează niște interfețe.

Dar dacă vrem să sortăm niște computere sau orice alte clase, trebuie să îi spunem

noi calculatorului după ce metodologie să facă sortarea.

Acest lucru se face:

1. prin implementarea de către clasa Computer a interfeței Comparable din

pachetul java.lang (dacă vrem să sortăm calculatoarele după o singură

metodologie). Trebue implementată metoda compareTo().

int compareTo(T o) Compares this object with the specified object for order.

public class Computer implements Comparable<Computer>{

...........

@Override

public int compareTo(Computer obj) {

//comparare dupa ram

if ( this.getRam() < obj.getRam() ){


//trebuie sa returnam un numar negativ

return -1;

} else if ( this.getRam() == obj.getRam() ){

//trebuie sa returnam 0

return 0;

} else {

//trebuie sa returnam un numar pozitiv

return 1;

Apoi scriem Collections.sort(computere);.

public static void main(String[] args) {

.........
Collections.sort(listaComputere);

2. Prin crearea unei clase care implementează interfața java.util.Comparator (dacă

vrem să sortăm calculatoarele după mai multe metodologii: de exemplu: ram,

procesor, marcă, etc). Trebue implementată metoda compare().

int compare(T o1, T o2) Compares its two arguments for order.

import java.util.Comparator;

public class RamComparator implements Comparator<Computer> {

@Override

public int compare(Computer obj1, Computer obj2) {

// sa compare dupa RAM

if ( obj1.getRam() < obj2.getRam() ){

// trebuie sa returnam un numar negativ


return -1;

} else if ( obj1.getRam() == obj2.getRam() ){

// trebuie sa returnam 0

return 0;

} else {

// trebuie sa returnam un numar pozitiv

return 1;

Apoi scriem:

ComparatorDupaRam comparator = new ComparatorDupaRam();

sau Comparator comparator = new ComparatorDupaRam();

și Collections.sort( computere, comparator );

public static void main(String[] args) {

.......
Comparator ramComparator = new RamComparator();

Collections.sort(listaComputere, ramComparator);

În java 1.7 cele două interfețe aveau decât 2 metode de implementat. În java 1.8 s-

au adăugat mai multe metode.

http://docs.oracle.com/javase/8/docs/api/index.html?java/util/Compar

able.html

Interface Comparable<T>

• Type Parameters:

T - the type of objects that this object may be compared to

All Known Subinterfaces:

ChronoLocalDate, ChronoLocalDateTime<D>, Chronology,

ChronoZonedDateTime<D>, Delayed, Name,

Path,RunnableScheduledFuture<V>, ScheduledFuture<V>
All Known Implementing Classes:

AbstractChronology, AbstractRegionPainter.PaintContext.CacheMode,

AccessMode, AclEntryFlag, AclEntryPermission,AclEntryType,

AddressingFeature.Responses, Authenticator.RequestorType,

BigDecimal, BigInteger, Boolean, Byte, ByteBuffer,Calendar,

CertPathValidatorException.BasicReason, Character,

Character.UnicodeScript, CharBuffer, Charset,

ChronoField,ChronoUnit, ClientInfoStatus, CollationKey,

Collector.Characteristics, Component.BaselineResizeBehavior,

CompositeName,CompoundName, CRLReason, CryptoPrimitive, Date, Date,

DayOfWeek, Desktop.Action,

Diagnostic.Kind,Dialog.ModalExclusionType, Dialog.ModalityType,

DocumentationTool.Location, Double, DoubleBuffer, DropMode,

Duration,ElementKind, ElementType, Enum, File, FileTime,

FileVisitOption, FileVisitResult, Float, FloatBuffer,

FormatStyle,Formatter.BigDecimalLayoutForm,

FormSubmitEvent.MethodType, GraphicsDevice.WindowTranslucency,

GregorianCalendar,GroupLayout.Alignment, HijrahChronology,

HijrahDate, HijrahEra, Instant, IntBuffer, Integer, IsoChronology,

IsoEra,JapaneseChronology, JapaneseDate, JavaFileObject.Kind,

JDBCType, JTable.PrintMode,

KeyRep.Type,LayoutStyle.ComponentPlacement, LdapName, LinkOption,

LocalDate, LocalDateTime, Locale.Category,


Locale.FilteringMode,LocalTime, Long, LongBuffer, MappedByteBuffer,

MemoryType, MessageContext.Scope, MinguoChronology, MinguoDate,

MinguoEra,Modifier, Month, MonthDay,

MultipleGradientPaint.ColorSpaceType,

MultipleGradientPaint.CycleMethod, NestingKind,Normalizer.Form,

NumericShaper.Range, ObjectName, ObjectStreamField, OffsetDateTime,

OffsetTime, PKIXReason,PKIXRevocationChecker.Option,

PosixFilePermission, ProcessBuilder.Redirect.Type, Proxy.Type,

PseudoColumnUsage, Rdn,ResolverStyle, Resource.AuthenticationType,

RetentionPolicy, RoundingMode, RowFilter.ComparisonType,

RowIdLifetime,RowSorterEvent.Type, Service.Mode, Short, ShortBuffer,

SignStyle, SOAPBinding.ParameterStyle,

SOAPBinding.Style,SOAPBinding.Use, SortOrder, SourceVersion,

SSLEngineResult.HandshakeStatus, SSLEngineResult.Status,

StandardCopyOption,StandardLocation, StandardOpenOption,

StandardProtocolFamily, String, SwingWorker.StateValue,

TextStyle,ThaiBuddhistChronology, ThaiBuddhistDate, ThaiBuddhistEra,

Thread.State, Time, Timestamp, TimeUnit,

TrayIcon.MessageType,TypeKind, URI, UUID, WebParam.Mode,

Window.Type, XmlAccessOrder, XmlAccessType, XmlNsForm, Year,

YearMonth, ZonedDateTime,ZoneOffset, ZoneOffsetTransition,

ZoneOffsetTransitionRule.TimeDefinition

Vedem că toate clasele wrapper (Integer, Long, Double, Character, Boolean, etc),

precum și clasa String implementează pe Comparable. De accea a mers să facem

Collections.sort(listă) fără să implementăm noi interfața Comparable.


public interface Comparable<T>

This interface imposes a total ordering on the objects of each class

that implements it. This ordering is referred to as the class's

natural ordering, and the class's compareTo method is referred to as

its natural comparison method.

Lists (and arrays) of objects that implement this interface can be

sorted automatically by Collections.sort (and Arrays.sort). Objects

that implement this interface can be used as keys in a sorted map or

as elements in a sorted set, without the need to specify a

comparator.

The natural ordering for a class C is said to be consistent with

equals if and only if e1.compareTo(e2) == 0 has the same boolean

value ase1.equals(e2) for every e1 and e2 of class C. Note that null

is not an instance of any class, and e.compareTo(null) should throw

aNullPointerException even though e.equals(null) returns false.

It is strongly recommended (though not required) that natural

orderings be consistent with equals. This is so because sorted sets

(and sorted maps) without explicit comparators behave "strangely"


when they are used with elements (or keys) whose natural ordering is

inconsistent with equals. In particular, such a sorted set (or

sorted map) violates the general contract for set (or map), which is

defined in terms of the equalsmethod.

For example, if one adds two keys a and b such that (!a.equals(b) &&

a.compareTo(b) == 0) to a sorted set that does not use an explicit

comparator, the second add operation returns false (and the size of

the sorted set does not increase) because a and b are equivalent

from the sorted set's perspective.

A fi consistent with equals() este mai ales necesar în cazul claselor TreeSet și

TreeMap (a se vedea materialul următor).

A se vedea şi: interface Comparator<T>.

Există și metoda Collections.shuffle() care amestecă elementele.

Clasa LinkedList
LinkedList este asemănător cu ArrayList, cu deosebirea tehnologiei de stocare

utilizată. Fiecare element are câte două referințe către elementele precendent și

următor.

La ArrayList dacă vrem să introducem un nou element la mijloc (cu .add(index,

element)), se copiază tot ArrayList-ul punând noul element la locul lui. Din acest

motiv, ArrayList-ul este mai încet. Dacă lucrăm cu arraylisturi foarte mari (milioane de
elemente) și vrem să introducem și să ștergem obiecte de la mijloc frecvent, nu este

recomandat a folosi ArrayList, ci mai degrabă LinkedList.

În LinkedList, pentru că fiecare element are câte o referință către elementele vecine,

la inserarea unui nou element la mijloc, se „rupe” linkedlistul, se adaugă noul

element, se refac referințele și gata. Nu trebuie copiat tot linkedlistul.

Vector
Vector este o clasă din versiunea 1.0, în timp ce majoritatea claselor din Collection

Framework au fost introduse în versiunea 1.2. Clasa Vector este asemănătoare cu

un array clasic din Java, cu excepţia faptului că se poate redimensiona.

Ca şi un array, elementele unui Vector pot fi accesate folosind indexul lor. La crearea

acestuia nu trebuie să setăm o dimensiune iniţială; acesta va creşte şi se va micşora

când este necesar.

Vectorul implementează un array dinamic. Este similar cu ArrayList, având

următoarele diferenţe:

 Vectorul este sincronizat (util pentru firele de execuţie); acest lucru îl face să

fie mai încet ca un ArrayList

 Vectorul conţine multe metode proprii care nu fac parte din framework-ul de

colecţii.

Vectorul se dovedeşte a fi folositor dacă nu cunoaştem dimensiunea array-ului de la

început sau dacă avem nevoie să îşi modifice dimensiunea pe timpul programului.

Vectorul suportă 4 constructori. Prima formă creează un vector care are o mărime

iniţială de 10 elemente:
 Vector()

A doua formă creează un vector a cărei capacitate este specificată prin parametrul

size:

 Vector(int size)

A treia formă crează un vector a cărui capacitate este specificată de parametrul size,

iar incrementul de incr. Incrementul reprezintă numărul de elemente care va fi alocat

de fiecare dată când vectorul este mărit:

 Vector(int size, int incr)

A patra formă crează un vector care conţine toate elementele colecţiei c:

 Vector(Collection c)
Exemplu

import java.util.*;

public class VectorDemo {

public static void main(String args[]) {

// initial size is 3, increment is 2

Vector v = new Vector(3, 2);

System.out.println("Initial size: " + v.size());


System.out.println("Initial capacity: " + v.capacity());

v.addElement(newInteger(1));

v.addElement(new Integer(2));

v.addElement(new Integer(3));

v.addElement(new Integer(4));

System.out.println("Capacity after four additions: " +

v.capacity());

v.addElement(new Double(5.45));

System.out.println("Current capacity: "+v.capacity());

v.addElement(new Double(6.08));

v.addElement(new Integer(7));

System.out.println("Current capacity: " + v.capacity());

v.addElement(new Float(9.4));

v.addElement(new Integer(10));
System.out.println("Current capacity: " + v.capacity());

v.addElement(new Integer(11));

v.addElement(new Integer(12));

System.out.println("First element: " + (Integer)

v.firstElement());

System.out.println("Last element: " + (Integer)

v.lastElement());

if (v.contains(new Integer(3)))

System.out.println("Vector contains 3.");

// enumerate the elements in the vector.

Enumeration vEnum = v.elements();

System.out.println("\nElements in vector:");

while(vEnum.hasMoreElements())

System.out.print(vEnum.nextElement() + " ");


System.out.println();

Ceea ce va genera următorul mesaj:

Initial size:0

Initial capacity:3

Capacity after four additions:5

Current capacity:5

Current capacity:7

Current capacity:9

First element:1

Last element:12

Vector contains 3.

Elements in vector:

12345.456.0879.4101112
Stack
Clasa Stack implementează o stivă de elemente de tipul LIFO (last-in-first-out). Vă

puteţi gândi la acesta exact ca la mai multe obiecte puse unul peste celălalt. La

adăugarea unui nou element, acesta va fi pus deasupra celorlalte. Când scoatem un

element de pe stivă, acesta va fi luat de deasupra. În alte cuvinte, ultimul element

adăugat este primul care va fi scos.

Stack este o subclasă a Vector care implementează o structură standard LIFO.

Acesta defineşte doar un cosntructor implicit prin care creăm un Stack gol. Include

metodele definite de Vector, la care mai adaugă alte câteva.

Vă puteţi gândi că Stack este asemenea unei stive de cărți. O carte nouă se adaugă

deasupra, iar pentru a accesa cărțile de mai jos trebuie date cele de deasupra la o

parte. Totuși, pentru că Stack moștenește pe Vector, are acces si cu ajutorul

indexului.

Exemplu

import java.util.*;

public class StackDemo {

static void showpush(Stack st,int a) {

st.push(new Integer(a));

System.out.println("push(" + a + ")");
System.out.println("stack: " + st);

static void showpop(Stack st) {

System.out.print("pop -> ");

Integer a = (Integer) st.pop();

System.out.println(a);

System.out.println("stack: "+ st);

public static void main(String args[]) {

Stack st = new Stack();

System.out.println("stack: "+ st);

showpush(st,42);

showpush(st,66);

showpush(st,99);
showpop(st);

showpop(st);

showpop(st);

try {

showpop(st);

} catch(EmptyStackException e) {

System.out.println("empty stack");

Ceea ce ar produce următorul rezultat:

stack:[]

push(42)

stack:[42]
push(66)

stack:[42,66]

push(99)

stack:[42,66,99]

pop ->99

stack:[42,66]

pop ->66

stack:[42]

pop ->42

stack:[]

pop -> empty stack

Metode uzuale:

 push(element): adaugă deasupra

 pop(): scoate elementul de deasupra

 peek(): returnează elementul de deasupra, fără să îl scoată


Queue
Dacă Stack (stivă) implementa tehnica Last-In-First-Out, ultimul element adăugat la

lista este primul care este scos, interfața Queue (coadă) implementează tehnica

First-In-First-Out, primul element adăugat, primul scos, asemenea unei cozi.

Clasă principală este PriorityQueue<E>.

Metode importante:

boolean add(E Inserts the specified element into this priority queue.
e)
E poll() Retrieves and removes the head of this queue, or returns null if this
queue is empty.
E peek() Retrieves, but does not remove, the head of this queue, or returns null if
this queue is empty.
Set-urile
Set-ul este o colecție care nu acceptă duplicate, adică 2 elemente care au aceeaşi

valoare.

Setul este o pungă (bag) în care se pun obiectele la grămadă. Obiectele nu sunt

ordonate după index. Nu se garantează nici o ordine, nici la afișare și nici la

parcurgere.

Setul poate fi parcurs

 cu bucla for-each

for (String string : set1) { }

 cu iterator

Iterator<String> iter = set1.iterator();

while (iter.hasNext()) {

String string = iter.next();

...

 nu poate fi parcurs cu for-ul clasic pentru că setul nu are index.

for (int i = 0; i < set1.size(); i++) { }

Clasa tipică definitorie pentru Set este clasa HashSet.


Set<String> set1 = new HashSet<String>();

set1.add("casa");

boolean bool = set1.add("casa");

System.out.println("S-a adaugat casa? " + bool ); // false –

e duplicat

System.out.println("S-a adaugat Casa? " + set1.add("Casa"));

// true

Set neparametrizat
Putem adăuga în el orice fel de obiecte.

public static void main(String[] args) {

Set set = new HashSet();

System.out.println("4 ?" + set.add(new Integer(4)));

System.out.println("4.0 ?" + set.add(new Double(4.0)));

System.out.println("4.0 ?" + set.add(new Double(4))); // aici

e duplicat şi nu se adaugă pentru că există deja

System.out.println("4L ?" + set.add(new Long(4L)));

System.out.println("4 ?" + set.add(new Byte((byte) 4)));


System.out.println("4 ?" + set.add(new Short((short) 4)));

Aici trebuie cast pentru că 4 este de tip integer, iar nouă ne trebuie un byte.

new Byte((byte) 4)

Set de Computere
Cum ne asigurăm că nu adăugăm obiecte duplicate?

Să facem un joc. Literelor de la A-L să le dăm valoarea 1, iar literelor de la M-Z să le

dăm valoarea 100. Să calculăm valoarea cuvintelor.


Dacă am avea milioane de cuvinte, de fiecare dată când am adăuga un alt cuvânt ar

trebui să verificăm toate cuvintele și să vedem dacă nu e duplicat. Acest lucru e

foarte ineficient. Mai eficient este să punem cuvintele în „găleți” (bucket). Fiecare

găleată va avea astfel o valoare. Calculăm valoarea cuvntului nostru și apoi căutăm

doar în găleata care are această valoare și verificăm, astfel, doar cuvintele din

această găleată.

Valoarea găleții este dată de metoda hashCode().

Regula 1: Două obiecte identice ca valoare după .equals() (nu neapărat și ca adresă

în memorie), trebuie să aibă același hashCode.

Regula 2: Dacă două obiecte au același hashCode, nu înseamnă că sunt identice ca

valoare.

Regula 3: Dacă două obiecte au hashCode-uri diferite, obiectele trebuie să fie diferite

ca valoare (metoda equals() să returneze false).

Algortim:

 Mai întâi java caută după hashcode.

 Apoi, caută după equals()

Clasa Object are metoda hashCode() care se moștenește de către toate clasele.

Clasa String a suprascris această metodă și fiecărui cuvânt îi dă valoarea

s[0] * 31 ^ (n-1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1]

(citiți metoda hashCode() de la String)


Metoda hashCode() din clasa Object generează un număr hashCode după adresa

obiectului din memorie. În principiu, două obiecte diferite au hashCoduri diferite, dar

poate fi și la fel.

Pentru a putea adăuga obiecte într-un Set, în clasa Computer trebuie să suprascriem

atât metoda equals() cât și hashCode(). Dacă nu le suprascriem vom putea adăuga

obiecte identice ca valoare, și, astfel, nu beneficiem de avantajele Set-ului, care ne

ajută să nu adăugăm duplicate.

Set<Computer> setComputere = new HashSet<Computer>();

setComputere.add(new Computer("Lenovo", 4, 3.6));

setComputere.add(new Computer("Lenovo", 4, 3.6));

De asemenea, dacă suprascriem doar pe equals() tot nu este suficient. Tot vom

putea adăuga ambele aceste obiecte. Pentru că vor avea hashCoduri diferite, și la

adăugarea celui de al doilea java va căuta în găleată greșită.

Dar dacă le suprascriem pe ambele, java va căuta în găleata potrivită. Acolo va mai

găsi un obiect de tip Computer. Acum va testa egalitatea lor cu equals(). Dacă

equals() acum dă true, cel de al doilea computer nu mai este adăugat în set.

@Override

public int hashCode() {

final int prime = 31;

int result = 1;
long temp;

temp = Double.doubleToLongBits(frecventa);

result = prime * result + (int) (temp ^ (temp >>>

32));

result = prime * result + ((marca == null) ? 0 :

marca.hashCode());

result = prime * result + ram;

return result;

@Override

public boolean equals(Object obj) {

if (this == obj)

return true;

if (obj == null)

return false;

if (getClass() != obj.getClass())
return false;

Computer other = (Computer) obj;

if (Double.doubleToLongBits(frecventa) !=

Double.doubleToLongBits(other.frecventa))

return false;

if (marca == null) {

if (other.marca != null)

return false;

} else if (!marca.equals(other.marca))

return false;

if (ram != other.ram)

return false;

return true;

Într-un set se poate adăuga și valoarea null, dar o singură dată.

Set sortat
Există interfața SortedSet care este implemntată de clasa TreeSet.
Această clasă ține toate obiectele sortate după o anumită ordine. Stringurile –

alfabetic , numerele – crescător etc. ( String și wrapper classes gen Integer

implemtează pe Comparable).

Ori de câte ori se adaugă un nou obiect, el se pune direct la locul lui. Clasa rulează

mai încet din această cauză.

Pentru a putea introeduce un obiect Computer într-un TreeSet, java trebuie să știe

cum să le sorteze: adică cum să le compare 2 câte 2.

Avem două variante:

 implementăm pe Comparable în clasa Computer

 facem un Comparator pe care îl pasăm în constructorul de la TreeSet

Varianta 1: Folosim constructorul fără argumente.

TreeSet<Computer> setDeComputereSortate = new TreeSet<Computer>();

Varianta 2: Pasăm un obiect Comparator în constructor.

Comparator comparator = new ComputerComparator();

TreeSet<Computer> setDeComputereSortate = new

TreeSet<Computer>(comparator);

În clasa ComputerComparator implementăm metoda compare().

Dacă nu folosim nici un comparator (nici Comparable și nici Comparator), atunci

când vom adăuga un computer în sortedSet la rulare vom avea această eroare:
setDeComputereSortate.add(new Computer("Lenovo", 4, 3.5));

Exception in thread "main" java.lang.ClassCastException:

pack1.Computer cannot be cast to java.lang.Comparable

Într-un set sortat putem folosi metoda .higher(computer) care ne returnează

următorul computer din set. Metoda higher() este specifică luiTreeSet, nu apare nici

în SortedSet, deci, dacă definim variabila noastră folosind polimorfismul va trebui să

facem down-cast.

Set<Computer> setDeComputereSortate = new

TreeSet<Computer>(comparator);

TreeSet<Computer> set = (TreeSet<Computer>) setDeComputereSortate;

System.out.println(set.higher(new Computer(“Lenovo”, 4, 3.0)));

Dacă ne trebuie setul să fie sortat doar ocazional, putem folosi un HashSet clasic,

care rulează mai repede. Apoi când ne trebuie sortarea, putem face un TreeSet,

folosind acest HashSet.

HashSet hashSet = new HashSet();

// ..... adăugăm obiecte

// .....

// la final, facem un TreeSet folosind hashSetul:


TreeSet treeSet = new TreeSet (hashSet);

Constructor

TreeSet(Collection<? extends E> c)

Constructs a new tree set containing the elements in the specified collection, sorted

according to the natural ordering of its elements.

Dcitionary
Clasa Dictionary este o clasă abstractă care defineşte o structură de date pentru

maparea cheilor la valori. Acest lucru este folositor în cazurile în care vrem să

accesăm datele folosind o anumită cheie în loc de un index.

Fiind o clasă abstractă, aceasta ne pune la dispoziţie doar metodele necesare pentru

a mapa cheile, neavând o implementare specifică. Aceasta ne oferă opţiuni de

stocare a datelor sub formatul cheie/valoare şi funţionează pe aceleaşi principii ca şi

un Map. Având o cheie şi o valoare, puteam stoca valoarea într-un obiect Dictionary.

Odată stocată, valoarea poate fi găsită folosind cheia. Deci, ca şi un map, un

dicţionar poate fi văzut ca o listă de perechi cheie/valoare.

Această clasă nu mai este folosită. Este recomandată implementarea interfeţei Map

pentru a obţine funcţionalitatea de stocare cheie/valoare.

Map
Map este o interfață care este implementată de mai multe clase. Aceasta este

asemenea unui dicționar. Este reprezentat printr-un obiect care este cheia și un al
obiect care este valoarea. Asemenea celorlaltor clase din Collection Framework, nu

se acceptă primitive.

Interfaţa Map mapează chei unice la valori. O cheie este un obiect care îl putem

folosi pentru a afla o valoare stocată la un moment dat. Chei foarte uzual folosite sunt

Stringurile. Cheile trebuie să fie unice, să nu fie duplicate. - Ele formează un Set.

Poate exista și cheia null, o singură dată.

 Având o cheie şi o valoare, putem stoca valoarea în obiectul Map. După ce

valoarea este stocată, poate fi aflată prin folosirea cheii.

 Multe metode aruncă NoSuchElementException când nu există niciun element

în map-ul care face apelul.

 ClassCastException este aruncată când un obiect este incompatibil cu

elementele unui map.

 NullPointerException este aruncat dacă încercăm să folosim obiecte null, iar

null nu este permis în map.


 UnsupportedOperationException este aruncat când încercăm să facem

schimbări la un map care nu poate fi modificat.

Interface Map<K,V> K = key, V = Value

Exemplu: Map<String, Computer>

Obiectele cheie e bine să fie immutable (nemodificabile), să nu aibă setteri pentru a

nu se modifica cheia. Mapul nu permite introducerea de chei duplicate, dar dacă

obiectul cheie are setteri care permit modificarea obiectului, se poate obține un obiect

egal cu un altul, ceea ce nu e bine. Se compromite aflarea valorii.

Deoarece cheile formează un Set, se comportă asemenea acestuia: trebuie să

suprascrie metodele .equals() și .hashCode().

Exemplu

import java.util.*;

public class CollectionsDemo{

public static void main(String[] args){

Map m1 =new HashMap();

m1.put("Zara","8");

m1.put("Mahnaz","31");

m1.put("Ayan","12");
m1.put("Daisy","14");

System.out.println();

System.out.println(" Map Elements");

System.out.print("\t"+ m1);

Ce ar produce următorul rezultat:

MapElements

{Mahnaz=31,Ayan=12,Daisy=14,Zara=8}

HashMap
Un Map clasic este clasa HashMap. Aceasta nu are metodele sincronizate.

Acesta are următoarele metode uzuale.

V put(K key, V value) adăugarea unei noi perechi


V get(Object key) Returnează valoarea pe baza cheii.
V remove(Object key) Removes the mapping for the specified key
from this map if present.
boolean remove(Object key, Removes the entry for the specified key only if
Object value) it is currently mapped to the specified value.
V replace(K key, V value) Replaces the entry for the specified key only if
it is currently mapped to some value.
boolean replace(K key, V Replaces the entry for the specified key only if
oldValue, V newValue) currently mapped to the specified value.
Set<K> keySet() returnează setul de chei
Collection<V> values() returnează valorile

TreeMap
Clasa TreeMap implementează interfața SortedMap. Cheile sunt sortate, formând un

TreeSet.

Hashtable
Clasa Hashtable ne pune la dispoziţie o modalitate de organizare a datelor folosind o

structură cheie definită de utilizator. De exemplu, într-o listă de adrese dintr-un hash

table putem stoca şi sorta datele pe baza unei chei, cum ar fi codul poştal dau după

numele unei persoane.

Semnificaţia cheilor dintr-un hashtable depinde de modul de folosire a acestuia şi de

datele pe care le conţine.

Hashtable a făcut parte din implementarea iniţială a pachetului java.util (versiunea

1.0) şi este o implementare concretă a Dictionary. Din Java 2, această clasă a fost

redefinită pentru a implementa interfaţa Map, iar acum este integrată în framework-ul

de colecţii. Este asemănătoare cu HashMap, dar este sincronizată.

Ca şi HashMap, Hashtable stochează perechi de cheie/valoare într-un tabel hash.

Când folosim un Hashtable, specificăm un obiect care este folosit ca şi cheie, iar

valoarea cerută este legată de cheia respectivă. Se aplică funcţia hash pe cheie, iar

codul rezultat este folosit ca şi index pentru valoarea stocată în tabel.

Hashtable defineşte 4 constructori. Prima variantă este constructorul implicit:


 Hashtable()

A doua variantă creează un hashtable care are o dimensiune iniţială specificată prin

size:

 Hashtable(int size)

A treia variantă creează un hashtable care are o mărime iniţiala specificată prin size

şi o rată de umplemere specificată prin fillRatio. Această raţie trebuie să fie un număr

între 0.0 şi 1.0, şi determină cât de mare să fie tabelul înainte să fie redimensionat.

 Hashtable(int size, float fillRatio)

A patra variantă creează un hashtable care este iniţializat folosind elementele din

Map-ul m. Capacitatea tabelului este de două ori mai mare decât a lui m. Factorul

implicit de încărcare folosit este de 0.75.

 Hashtable(Map m)
Exemplu

import java.util.*;

public class HashTableDemo {

public static void main(String args[]) {

// Create a hash map

Hashtable balance = new Hashtable();


Enumeration names;

String str;

double bal;

balance.put("Zara", new Double(3434.34));

balance.put("Mahnaz", new Double(123.22));

balance.put("Ayan", new Double(1378.00));

balance.put("Daisy", new Double(99.22));

balance.put("Qadir", new Double(-19.08));

// Show all balances in hash table.

names = balance.keys();

while (names.hasMoreElements()) {

str = (String) names.nextElement();

System.out.println(str + ": " + balance.get(str));

}
System.out.println();

// Deposit 1,000 into Zara's account

bal = ((Double) balance.get("Zara")).doubleValue();

balance.put("Zara", new Double(bal + 1000));

System.out.println("Zara's new balance: " +

balance.get("Zara"));

Ceea ce produce următorul rezultat:

Qadir:-19.08

Zara:3434.34

Mahnaz:123.22

Daisy:99.22

Ayan:1378.0

Zara's new balance: 4434.34


Properties
Properties este o subclasă a Hashtable. Aceasta este folosită pentru a menţine liste

de valori în care atât cheile cât şi valorile sunt de tipul String. Clasa Properties este

folosită de multe clase din Java. De exemplu, atunci când dorim să obţinem valorile

mediului apelăm System.Properties() care întoarce un obiect de tipul Properties.

Properties defineşte câmpul defaults care reţine o listă de proprietăţi implicite

asociate cu obiectul Properties.

 Properties defaults;

Properties are doi constructori. Primul (cel implicit) creează un obiect fără valori:

 Properties()

Al doilea constructor primeşte un parametru de tipul Properties pentru valorile

implicite. În ambele cazuri, lista de proprietăţi este goală:

 Properties(Properties propDefaults)
Exemplu

import java.util.*;

public class PropDemo {

public static void main(String args[]) {

Properties capitals = new Properties();

Set states;
String str;

capitals.put("Illinois", "Springfield");

capitals.put("Missouri", "Jefferson City");

capitals.put("Washington", "Olympia");

capitals.put("California", "Sacramento");

capitals.put("Indiana", "Indianapolis");

// Show all states and capitals in hashtable.

states = capitals.keySet(); // get set-view of keys

Iterator itr = states.iterator();

while(itr.hasNext()) {

str = (String) itr.next();

System.out.println("The capital of " + str + " is " +

capitals.getProperty(str) + ".");

}
System.out.println();

// look for state not in list -- specify default

str = capitals.getProperty("Florida","Not Found");

System.out.println("The capital of Florida is "+ str +".");

Ceea ce va produce următorul rezultat:

The capital of Missouri is JeffersonCity.

The capital of Illinois is Springfield.

The capital of Indiana is Indianapolis.

The capital of California is Sacramento.

The capital of Washington is Olympia.

The capital of Florida is NotFound.

Exerciții
1. Se dă un map care are câteva înregistrări:

Limba - engleza

Culoare - rosu

Sa se implementeze următoarele funcționalități:

1. utilizatorul poate vedea toate înregistrările din map

2. utilizatorul poate schimba o înregistrare, doar dacă aceasta există

3. utilizatorul poate adăuga o înregistrare, doar dacă cheia nu există

4. utilizatorul poate șterge o înregistrare

2. Să se construiască o interfață grafică ce conține 2 câmpuri pentru introducerea

textului - unul pentru cheie și unul pentru valoare; un buton; și o listă.

Când se va da click pe buton, se va adauga într-un map o înregistrare în funcție de

valorile introduse - desigur, dacă cheia există deja își va modifica valoarea. De

fiecare dată când informația din map se schimbă, se va afișa în listă înregistrările așa

cum sunt trecute în map.

StringBuffer şi StringBuilder
Clasele StringBuffer şi StringBuilder sunt folosite când există o necesitate pentru a

face multe modificări la un String, prin concatenări repetate. Spre deosebire de

obiectele de tipul String, StringBuffer şi StringBuilder pot fi modificate de mai multe

ori, fără a lăsă în urmă multe obiecte noi nefolosite.


Clasa StringBuffer a fost introdusă în Java 5, şi principala diferenţă între StringBuffer

şi StringBuilder este că metodele din StringBuilder nu sunt thread-safe (nu sunt

sincronizate).

Așa cum am vazut, obiectele String sunt imutable (odată create nu se pot modifica)

(A nu se confunda cu variabila care pointează către ele, ea se poate modifica!).

String a = "Alin ";

a = a + "are ";

a = a + "4 ";

a = a + "mere.";

Crează mulțime de obiecte de tip String pe String Pool. Acesta rămân acolo până se

închide calculatorul. Garbage Collector nu are acces la String Pool.

Soluția: Folosirea clasei StringBuffer.

StringBuffer sb = new StringBuffer();

sb.append("Alin ");

sb.append("are ");

sb.append("4 ");

sb.append("mere.");
Se crează un singur obiect care se modifică. Obiectele StringBuffer sunt modificabile.

Vedeți API StringBuffer (constructorii și metodele care semană mult cu cele de la

clasa String), unele din metode fiind enumerate mai jos.

Clasa StringBuffer este thread-safe (este bună pentru lucrul cu firele de execuție).

Dar din acest motiv este mai lentă. Dacă nu lucrați cu mai multe fire de execuție, ni

se recomandă să folosim clasa StringBuilder care este asemănătoare cu

StringBuffer.

Este recomandată folosirea unui StringBuilder când este posibilă, fiind mai rapidă

decât un StringBuffer. Dar, dacă este necesară implementarea thread-safe, atunci

opţiunea mai bună este StringBuffer.

public class Test{

public static void main(String args[]){

StringBuffer sBuffer = new StringBuffer(" test");


sBuffer.append(" String Buffer");

System.out.println(sBuffer);

Aceasta ar produce următorul rezultat:

test String Buffer

Metode uzuale
Cele mai importante metode suportate de clasa StringBuffer sunt prezentate mai jos.

 public StringBuffer append(String s)

 Modifică valoarea obiectului care a apelat metoda. Metoda poate lua ca

parametru un boolean, char, int, long, String, etc.

 public StringBuffer reverse()

 Metoda inversează valoarea unui obiect StringBuffer care a apelat-o.

 public delete(int start, int end)

 Şterge din şir caracterele dintre cei doi indecşi.

 public insert(int offset, int i)

 Această metodă inserează un şir de caractere la poziţia specificată prin

offset.

 public replace (int start, int end, String str)

 Această metodă înlocuieşte caracterele dintr-un subşir a acestui

StringBuffer cu caracterele din şirul specificat.


Raționament Pedeștri si Călăreți
Reminder this / super
Constructor

 this

this();

// sau

this(nume);

 super

super();

// sau

super(nume);

Atribute

 this

this.nume = nume;

 super

super.nume = nume;

Metode

 this
this.deseneazaCerc(); // nu are sens caci poate fi apelat direct cu

deseneazaCerc()

// ...

public void deseneazaCerc(){

...

pubilc void deseneaza(){

this.deseneazaCerc();

// nu are sens

// se poate mai simplu:

deseneazaCerc();

 super

public void deseneaza(){


super.deseneaza();

// apelam metoda deseneaza() din superclasa

public String getNume(){

super.getNume();

// apelam metoda getNume() din superclasa

Studiu de caz
Clasa Ostas este superclasa. Clasele Pedestru și Calaret extind (moștenesc) pe

Ostas. Clasa Ostas are atributul String nume și atributul Rege rege.

Varianta 1

În superclasa facem aceste atribute protected. În acest fel, ele vor fi vizibile în

subclase. Atenție, nu declarăm noi atribute String nume și Rege rege în clasa fiică!!!!

Clasa Ostas

protected String nume;

protected Rege rege;


public Ostas(String nume, Rege rege) {

this.nume = nume;

this.rege = rege;

public String getNume() {

return nume;

public void setNume(String nume) {

this.nume = nume;

public Rege getRege() {

return rege;
}

public void setRege(Rege rege) {

this.rege = rege;

Clasa Pedestru extends Ostas

// fără atribute

// Se moșentesc atributele pt că sunt protected, deci, vizibile în

subclasa.

public Pedestru(String nume, Rege rege) {

super(nume, rege);

public String getNume() {

return nume;
}

public void setNume(String nume) {

this.nume = nume;

public Rege getRege() {

return rege;

public void setRege(Rege rege) {

this.rege = rege;

Dar dacă facem atributele protected, ele vor putea fi modificate direct, fără setter,

ceea ce nu este bine, atât din subclase fiică, cât și din alte clase din același pachet,

care le vor vedea.

Varianta 2
În superclasa facem aceste atribute private. Ele, însă, nu vor fi vizibile în clasele fiică,

dar le putem seta cu ajutorul getterilor, setterilor, constructorului. Atenție, nu

declarăm noi atribute String nume și Rege rege în clasa fiică!!!!

Clasa Ostasu

private String nume;

private Rege rege;

public Ostas(String nume, Rege rege) {

this.nume = nume;

this.rege = rege;

public String getNume() {

return nume;

public void setNume(String nume) {


this.nume = nume;

public Rege getRege() {

return rege;

public void setRege(Rege rege) {

this.rege = rege;

Clasa Pedestru extends Ostas

// fără atribute

// nu se moșentesc pt că sunt private în clasa mamă, dar le folosim

prin intermediul getterilor și setterilor

public Pedestru(String nume, Rege rege) {


super(nume, rege);

public String getNume(){

return super.getNume();

public void setNume(String nume) {

super.setNume(nume);

public Rege getRege(){

return super.getRege();

public void setRege(Rege rege) {


super.setRege(rege);

public class Rege {

private String nume;

private Set<Ostas> armata = new TreeSet<Ostas>();

...

Se dorește includerea tuturor ostașilor într-un TreeSet pentru ca ei să fie sortați.

Pentru că e un Set, trebuie să suprascriem pe equals() și pe hashCode() în clasa

Ostaș.

Dar pentru că trebuie să fie sortatați, vom implementa pe Comparable și vom

implementa metoda compareTo(). Sortăm astfel încât, călăreții să fie înaintea

pedeștrilor. Deci primul criteriu de sortare va fi clasa. Apoi, al doilea va fi numele.

public class Ostas implements Comparable<Ostas> {

@Override

public int compareTo(Ostas altOstas) {

// ordonam mai intai calaretii si apoi pedestrii


if (this instanceof Calaret && altOstas instanceof

Pedestru) {

return -1;

} else if (this instanceof Pedestru && altOstas

instanceof Calaret) {

return 1;

// cazurile in care sunt la fel, comparam dupa nume

else if (this instanceof Pedestru && altOstas

instanceof Pedestru) {

return

this.getNume().compareToIgnoreCase(altOstas.getNume());

} else {

return

this.getNume().compareToIgnoreCase(altOstas.getNume());

}
}

Observăm că nu am inclus nici o referire la Rege. Nu are sens să sortăm armata unui

rege după rege, cacă toți ostașii unui rege vor avea o referire la acest rege, nu la

altul.

Deci, în compareTo() nu am făcut nici o referire la Rege.

Mai țineți minte de consistent with equals? Ce înseamnă?

1. Dacă equals() dă true, compareTo() trebuie să dea 0.

2. Dacă equals() dă false, compereTo() trebuie să dea diferit de 0.

3. Dacă compareTo() dă 0, equals() trebuie să dea true.

4. Dacă compareTo() dă diferit de 0, equals() trebuie să dea false.

Când se întâmplă asta? Când equals() și compareTo() folosesc aceleași atribute. Cu

alte cuvinte, dacă nu am inclus regele la compareTo(), nu îl vom include nici la

equals(), și nici la hashCode().

public class Ostas implements Comparable<Ostas> {

@Override

public int hashCode() {

final int prime = 31;

int result = 1;
result = prime * result + ((nume == null) ? 0 :

nume.hashCode());

return result;

@Override

public boolean equals(Object obj) {

if (this == obj)

return true;

if (obj == null)

return false;

if (getClass() != obj.getClass())

return false;

Ostas other = (Ostas) obj;

if (nume == null) {

if (other.nume != null)

return false;
} else if (!nume.equals(other.nume))

return false;

return true;

Un alt aspect:

În urma unei bătălii, un ostași trece de la o armată la alta.

El trebuie șters din TreeSetul unui rege, și adăugat în TreeSetul altui rege.

Armata1 Armata2
Călăreț Călăreț
Călăreț Pedestru
Călăreț Pedestru
Pedestru Pedestru
Pedestru
Pedestru
Pedestru

Parcurgem TreeSetul armatei mai mici (Armata2), și pentru fiecare dintre ostașii ei,

purtăm câte o luptă. Călărețul se va lupta cu un primul Călăreț din Armata1, dar primii

doi Pedeștri din Armata2 se vor lupta cu câte un Călăreț, iar cel de al treilea Pedestru

se va lupta cu primul Pedestru din Armata1.


Vom parcurge TreeSetul și în timp ce îl parcurgem, vor avea loc lupte individuale. În

timp ce parcurgem TreeSetul, va trebui să ștergem și să adăugăm ostașii care pierd

și de mută de la un rege la altul.

Pentru că trebuie să facem ștergerea elementelor în timp ce parcurgem lista, vom

folosi un Iterator.

Iterator iterUser;

...

if (user.getArmata().size() < computer.getArmata().size()) {

// Armata userului este mai mica decat armata computerului

iterUser = user.getArmata().iterator();

iterComputer = computer.getArmata().iterator();

// Parcurgem Armata Userului, pentru ca este mai mică

while (iterUser.hasNext()) {

Ostas ostasUser = iterUser.next();


// in principiu nu ne trebuie un .hasNext() pentru ca nu

riscam sa iesim din Set

// setul computerului este mai mare decat setul userului.

// dar pentru mai multa siguranta:

if (iterComputer.hasNext()) {

Ostas ostasComputer = iterComputer.next();

lupta(ostasUser, ostasComputer);

iar în cadrul luptei, vom avea:

/**

* Metoda trece un ostas de la user la computer

*/

public static void treceLaComputer(Ostas ostasUser){


// Nu e o clona, e chiar ostasul.

// In timpul unei batalii, ostasii capturati nu intra

din nou in lupta,

// pentru ca ar putea sa avem mai multe batalii decat

ostasi sunt

// si crapa programul

computer.getOstasiCapturati().add(ostasUser);

// nu trebuie asa, ci cu iterul

// user.getArmata().remove(ostasUser);

iterUser.remove();

ostasUser.setRege(computer);

Exercițiu
Se dă următoarea ierarhie de clase:

Autovehicul cu câmpurile viteza, marca, culoare


Autobuz cu câmpurile numarPasageri

MasinaDeCurse cu câmpurile numePilot și anFabricatie

Sa se implementeze:

1. ierarhia de clase

2. principiul de incapsulare

3. constructori înlănțuiti - vid, cu parametrii și de copiere

4. o listă care conține diverse autovehicule

5. suprasceierea metodei equals

6. suprasceierea metodei toString

7. afișarea listei cu un ierator

8. definirea regulei de comparație - după autobuze apoi mașini de curse, după

marcă și după viteză

9. să se implementeze un algoritm de sortare

Se consideră 2 proprietari care au diverse vehicule. Aceștia se întrec cu mașini. La o

întrecere se folosește un autovehicul aleator din garajul fiecăruia. Câștigă cursa cel

cu mașina mai rapidă, caz în care va "fura" mașina oponentului. Dacă ambele mașini

au aceeași viteză, atunci aleator unul din participanți va lua 2 mașini aleatoare de la

competiție.

Java Streams
Pachetul java.io are în componență clase, numite I/O Streams, care ajuta, la nevoie,

să se efectueze fluxuri de intrări și ieșiri în Java.

Standard Streams
În Java sunt următoarele stream-uri standard:
 Standard Input: Crează fluxuri de intrări, cu ajutorul cărora se pot citi date de

la o sursă externă. Cel mai des întâlnit StandardInput este cel creat pentru

tastatură si are ca referință System.in;

 Standard Output: Crează fluxuri de ieșiri, cu ajutorul cărora se pot trimite

informații secvențial la o destinație externă. Cel mai des întâlnit StarndardOutput

este cel creat pentru monitor și are ca referință System.out;

 Standard Error: Acesta este folosit pentru a transmite erorilor produse de un

program. Ca și în cazul anterior, pentru afișarea erorilor se folosește tot un ecran,

dar referința, de aceasta dată este System.err.

Iată un exemplu în care vom crea o instanță a InputStreamReader care v-a citi

fluxurile primite de la tastatură până când userul v-a apasa litera “q”:

import java.io.IOException;

import java.io.InputStreamReader;

public class ReadConsole {

public static void main(String args[]) throws IOException {

InputStreamReader cin = null;

try {

cin = new InputStreamReader(System.in);


System.out.println("Enter characters, 'q' to quit.");

char c;

do {

c = (char) cin.read();

System.out.print(c);

} while (c != 'q');

} finally {

if (cin != null) {

cin.close();

} }

Citirea și Scrierea Fișierelor


FileInputStream
Acest stream este folosit la citirea datelor dintr-un fișier. Obiectele de acest tip, pot fi

create utilizând cuvântul cheie new și sunt definiți urmatorii constructori:


 Un constructor care primiește un parametru de tip String în care se reține

numele unui fișier:

InputStream f = new FileInputStream("C:/java/hello");

 Un constructor care primește un parametru de tip File:

File f = new File("C:/java/hello");

InputStream f = new FileInputStream(f);

DataInputStream
Acest stream este utilizat în citirea primitivelor. Ca și streamul anterior, se pot creea

obiecte de acest tip, utilizând cuvântul cheie new și constructorul:

InputStream in = DataInputStream(InputStream in);

Metode

 public final int read(byte[] r, int off, int len)throws IOException

 Citește maxim len biți din streamul de date și le scrie într-un array de

tipul byte. Returnează numărul total de biți citiți sau -1 dacă este finalul

fișierului.

 public final int read(byte [] b)throws IOException

 Citește numărul de biți din stream și le scrie într-un array de tipul byte.

Returnează numărul total de biți citiți sau -1 dacă este finalul fișierului.

 public final Boolean readBooolean() throws IOException,

 public final byte readByte() throws IOException,

 public final short readShort() throws IOException

 public final Int readInt() throws IOException


 Aceste metode citesc biții din InpuntStream și returnează următorii 2 biți

specifici tipurilor de date primitive solicitate.

 public String readLine() throws IOException

 Citește următorul rând de text din InputStream. Citește succesiv fiecare

bit și îl convertește într-un character. Citirea se oprește atunci când stream-ul

întâlneşte sfârșitul de linie sau de fișier. Caracterele citite sunt returnate ca un

String.

Exemplu

În următorul exemplu se utilizează câte un obiect de tip DataInputStream și

DataOutputStream. Programul realizează citirea a primelor 5 rânduri dintr-un fișier

dat test.txt și scrie aceste rânduri în majuscule într-un alt fișier test1.txt.

import java.io.*;

public class Test {

public static void main(String args[]) throws IOException {

DataInputStream d = new DataInputStream(new

ileInputStream("test.txt"));

DataOutputStream out = new DataOutputStream(new

FileOutputStream("test1.txt"));

String count;

while ((count = d.readLine()) != null) {


String u = count.toUpperCase();

System.out.println(u);

out.writeBytes(u + " ,");

d.close();

out.close();

FileOutputStream
FileOutputStream este folosit în crearea fișierelor și pentru scrierea datelor în ele.

Acest stream verifică dacă fisierul solicitat pentru scriere există, iar dacă nu există

crează unul.

Doi constructori ce pot fi folosiți în crearea acestui tip de obiect sunt:

 Un constructor care primește ca parametru denumirea fișierului sub forma

unui String

OutputStream f = new FileOutputStream("C:/java/hello");

 Un constructor care primește un parametru de tip File:

File f = new File("C:/java/hello");


OutputStream f = new FileOutputStream(f);

Metode

 public void close() throws IOException{}

 Acestă metodă închide fluxul de date și eliberează resursele sistemului

asociate cu fișierul deschis.

 protected void finalize() throws IOException {}

 Această metodă curăţă conexiunea către fișierul din stream. Asigură că

metoda close() a fost apelată când nu mai există referințe la acest stream.

 public void write(int w) throws IOException{}

 Această metodă scrie în fişier bitul specificat la output-ul stream-ului.

 public void write(byte[] w)

 Această metodă scrie o lungime w de biți din array-ul de tip byte

specificat la output-ul stream-ului.

DataOutputStream
Cu acest stream se pot scrie date primitive intr-un output speficat. Constructorul

folosit este:

DataOutputStream out = DataOutputStream(OutputStream out);

Metode

 public final void write(byte[] w, int off, int len) throws IOException

 Scrie o lungime len de biți din array-ul specificat și are ca punct de

pornire indexul off.

 public final int write(byte [] b) throws IOException


 Scrie array-ul specificat și returnează numărul total de biți scriși în

buffer.

 public final void writeBooolean() throws IOException

 public final void writeByte() throws IOException

 public final void writeShort() throws IOException

 public final void writeInt() throws IOException

 Aceste metode scriu în output stream tipul primitiv de date specificat.

 public void flush() throws IOException

 Această metodă eliberează output-ul specificat de stream.

 public final void writeBytes(String s) throws IOException

 Această metodă scrie şirul de caractere s în output stream ca o

secvență de biți. Fiecare caracter din string este scrie separat prin eliminarea

primilor 8 biți.

Exemplu

public class Test {

public static void main(String args[]) {

try {

byte bWrite[] = { 11, 21, 3, 40, 5 };

OutputStream os = new FileOutputStream("test.txt");

for (int x = 0; x <bWrite.length; x++) {

os.write(bWrite[x]); // writes the bytes


}

os.close();

InputStream is = new FileInputStream("test.txt");

int size = is.available();

for (int i = 0; i <size; i++) {

System.out.print((char) is.read() + " ");

is.close();

} catch (IOException e) {

System.out.print("Exception");

File Navigation and I/O


Clasa File
În clasa File se reține adresa de pe disc a unui fișier anume sau mulțimea fișierelor

dintr-un director. Această clasă se folosește în crearea fișierelor şi a directoarelor, în

căutarea acestora, în ştergerea fişierelor, etc.

 Următoarea sintaxă crează o nouă instantă a clasei File dintr-o cale a fișierului

abstractizată de tip parinte și dintr-o cale abstractizată de tip copil:

 File(File parent, String child);

 Următoarea sintaxă creză o nouă instantă a clasei File prin convertirea unei

adrese de fișier date într-o cale abstractizată:

 File(String pathname);

Exemplu

import java.io.File;

public class Main {

public static void main(String[] args) {

File f = null;

String[] strs = { "test1.txt", "test2.txt" };

try {

// for each string in string array

for (String s : strs) {

// create new file


f = new File(s);

// true if the file is executable

boolean bool = f.canExecute();

// find the absolute path

String a = f.getAbsolutePath();

// prints absolute path

System.out.print(a);

// prints

System.out.println(" is executable: " + bool);

} catch (Exception e) {

// if any I/O error occurs

e.printStackTrace();

}
Considerând fișierul test1.txt executabil și fișierul test2.txt neexecutabil, în urma

executării programului de mai sus obținem următorul rezultat:

test1.txt is executable: true

test2.txt is executable: false

Clasa FileReader
Această clasă moștenește clasa InputStreamReader. Clasa FileReader este folosită

în citirea fluxurilor de caractere și cei mai uzuali constructori sunt prezentaţi mai jos.

 Următoarea sintaxă crează o nouă instantă a clasei FileReader având ca input

un obiect de tip File, obiect din care se execută citirea.

 FileReader(File file);

 Următoarea sintaxă crează o nouă instanţă a clasei FileReader având ca input

un obiect de tip FileDescritor, obiect din care se execută citirea.

 FileReader(FileDescriptor fd);

 Următoarea sintaxă crează o nouă instanţă a clasei FileReader având ca input

numele căii unui fișier transmis ca String, fişier din care se va executa citirea.

 FileReader(String fileName);

Exemplu

import java.io.*;

public class FileRead{

public static void main(String args[]) throws IOException {


File file = new File("Hello1.txt");

// creates the file

file.createNewFile();

// creates a FileWriter Object

FileWriter writer = new FileWriter(file);

// Writes the content to the file

writer.write("This\n is\n an\n example\n");

writer.flush();

writer.close();

// Creates a FileReader Object

FileReader fr = new FileReader(file);

char[] a = new char[50];

fr.read(a); // reads the content to the array

for (char c : a)

System.out.print(c); // prints the characters one by one

fr.close();
}

În urma executării programului se afișează:

This

is

an

example

Clasa FileWriter
Această clasă moștenește clasa OutputStreamWriter. Această clasă este folosită în

scrierea fluxurilor de caractere. Constructorii cei mai uzuali sunt prezentaţi mai jos.

 Următoarele sintaxe crează o nouă instanţă a clasei FileWriter având ca input

un obiect de tip File, obiect în care se execută scrierea.

 FileWriter(File file);

 FileWriter(File file, boolean append);

 Următoarea sintaxă crează o nouă instantă a clasei FileWriter având ca input

un obiect de tip FileDescriptor, obiect în care se execută scrierea.

 FileWriter(FileDescriptor fd);

 Următoarea sintaxă crează o nouă instanţă a clasei FileWriter având ca input

numele căii unui fișier transmis ca String, fişier în care se execută scrierea.

 FileWriter(String fileName);
 Următoarea sintaxă crează o nouă instanță a clasei FileWriter având ca input

numele căii unui fișier transmis ca String și o variabilă boolean în care este

reținută decizia de a concatena (true) în fișier datele scrise sau a suprascrie

fişierul (false).

 FileWriter(String fileName, boolean append)

Exemplu

import java.io.*;

public class FileWriter{

public static void main(String args[]) throws IOException {

File file = new File("Hello1.txt");

// creates the file

file.createNewFile();

// creates a FileWriter Object

FileWriter writer = new FileWriter(file);

// Writes the content to the file

writer.write("This\n is\n an\n example\n");

writer.flush();
writer.close();

// Creates a FileReader Object

FileReader fr = new FileReader(file);

char[] a = new char[50];

fr.read(a); // reads the content to the array

for (char c : a)

System.out.print(c); // prints the characters one by one

fr.close();

În urma execuţiei programului se va afişa:

This

is

an

example
Directoare în Java
Un director este un fișier care conține o listă de alte fișiere și directoare. Cu ajutorul

clasei File, putem să manipulam și să creăm direcotoare și fisiere. Unele dintre cele

mai utilizate metode ale acestei clase sunt:

Crearea Directoarelor
Există doua metode cu care putem crea directoare:

 Metoda mkdir() creză un fișiere și returnează un parametru de tip boolean.

Dacă parametrul returnat este true, atunci directorul a fost creat cu succes. Dacă

parametrul returnat este false atunci, directorul nu a fost creat pentru că, ori există

deja un director creat cu numele specificat, ori calea specificată nu există.

 Metoda mkdirs() creză atăt directorul speficificat cât și parinții acestuia.

Exemplu

Următorul exemplu crează un director la adresa "/tmp/user/java/bin":

import java.io.File;

public class CreateDir {

public static void main(String args[]) {

String dirname = "/tmp/user/java/bin";

File d = new File(dirname);

// Create directory now.


d.mkdirs();

Notă: În Java se pot folosi ca separatori în compunerea adreselor de pe disc, "/" ori

"\", indiferent de sistemul de operare folosit. De exemplu, în programul de mai sus, la

initializarea parametrului String "dirname" se putea utiliza și sintaxa:

String dirname = "\tmp\user\java\bin";

Afișarea Directoarelor
Pentru lista de fișiere și directoare dintr-un director se poate folosi metoda list()

evidențiată în următorul exemplu.

Exemplu

import java.io.File;

public class ReadDir {

public static void main(String[] args) {

File file = null;

String[] paths;
try {

// create new file object

file = new File("/tmp");

// array of files and directory

paths = file.list();

// for each name in the path array

for (String path : paths) {

// prints filename and directory name

System.out.println(path);

} catch (Exception e) {

// if any error occurs

e.printStackTrace();

}
În urma executării programului de mai sus, se afiseză conținutul fișierului "/tmp";

Assignment

1. Scrieți un program care afisează următorul desen într-un fișier text:

+———————————————————————————————————+

| #### |

| #### |

| #### |

| |

| |

| Bill Gates |

| 1 Microsoft Way |

| Redmond, WA 98104 |

| |

+———————————————————————————————————+

2. Creați un fișier în care să salvați numele vostru (nume și prenume separate prin

spaţiu). Scrieți un program care să citească fișierul și să afișeze informațiile pe ecran.


3. Scrieți un program care cere numele utilizatorului și un număr. Salvați informațiile

într-un fișier text. Suprascrieţi informația din fișier dacă programul rulează din nou și

se introduce un număr mai mare.

4. Creați un fișier în care să scrieţi 3 numere, fiecare pe un alt rând. Scrieți un

program care să citească numerele din fișier, să facă suma lor și să o afiseze pe

ecran. (afisați și numerele pentru care faceți suma!)

5. Creați mai multe fișiere (numere1.txt, numere2.txt, numere3.txt, etc) și rețineți în

fiecare fișier mai multe numere, fiecare pe câte un rând. Scrieți un program care cere

utilizatorului numele fișierului de la tastatură, apoi face suma numerelor din fisierul

respectiv.

6. Cereți utilizatorului un nume de fișier pe care vrea sa îl deschidă. Afișați conținutul

fișierului pe ecran.

7. Se dă următorul cod:

import java.net.URL;

import java.util.Scanner;

public class FileStuff {

public static void main(String[] args) {

URL mcool = new URL("http://google.com");

Scanner webIn = new Scanner(mcool.openStream());


String one = webIn.nextLine();

webIn.close();

System.out.println(one);

Adăugați verificări pentru excepții acolo unde este cazul. Asiguraţi-vă că programul

poate citi mai multe linii și afișați-le în consolă.

Deschideți o pagină la alegere și salvaţi conținul acesteia într-un fişier html.

Deschideţi apoi fișierul într-un mod normal.

Cereți utilizatorului să introducă numele unei pagini web. Salvați conținutul paginii

într-un fișier html.

Creați un program care deschide acest fișier și citește literele din trei în trei. Afișați

rezultatul.

Serializare
Serializarea obiectelor este un mecanism prin care un obiect poate fi reprezentat ca

o secvență de biți în care se includ datele obiectului, precum și informații despre tipul

obiectului și tipurile de date stocate de acesta.

După scrierea unui obiect serializat într-un fișier, acesta poate fi citit, iar prin

deserializare se reconstruiește obiectul în memorie.


Mai mult, tot procesul de serializare / deserializare este independent de JVM, acest

lucru permițând serializarea unui obiect într-o platformă și deserializarea lui în alta.

Clasele ObjectInputStream şi ObjectOutputStream sunt streamuri avansate care au

în componenţă metode de serializare şi deserializare a unui obiect.

Clasa ObjectOutputStream are în componenţă multe metode de scriere a variabilelor,

dar următoarea metodă iese în evidenţă:

public final void writeObject(Object x) throws IOException

Metoda menţionată mai sus seriliazează un obiect şi îl trimite ca output în stream.

Similar, clasa ObjectInputStream are în componenţă următoarea metodă de

deserializare a obiectelor:

public final Object readObject()throws IOException,

ClassNotFoundExceptio

Această metodă primeşte un obiect serializat şi în output îl deserializează. Obiectul

returnat trebuie convertit (cast) la tipul de date corespunzător.

Exemplu

public class Employee implements java.io.Serializable {

public String name;

public String address;


public transient int SSN;

public int number;

public void mailCheck() {

System.out.println("Mailing a check to " + name + " " +

address);

Pentru ca o clasă să fie serializată trebuie să se îndeplinească următoarele condiţii:

 Clasa trebuie să implementeze interfaţa java.io.Serializable

 Toate variabilele instanţei trebuie să fie serializabile. Dacă un câmp nu este

serializabil, trebuie să fie marcat ca transient.

Pentru a afla dacă o clasă din librăria standard Java este serializabilă, trebuie

verificat în documentaţia clasei dacă implementează intefaţa java.io.Serializable.

Dacă implementează această interfaţă, atunci clasa este serializabilă.

Serializarea unui obiect


Clasa ObjectOutputStream este folosită la serializarea obiectelor. Următorul program

instanţiază un obiect de tip Employee şi îl serializează într-un fişier.

După executarea programului, un fişier employee.ser este creat. Programul nu

generează niciun output, dar studiaţi codul şi determinaţi cum funţionează. Atunci
când se serializează un obiect într-un fişier, Java sufixează fişierului rezultat extensia

.ser.

Exemplu

import java.io.*;

public class SerializeDemo {

public static void main(String[] args) {

Employee e = new Employee();

e.name = "Reyan Ali";

e.address = "Phokka Kuan, Ambehta Peer";

e.SSN = 11122333;

e.number = 101;

try {

FileOutputStream fileOut = new

FileOutputStream("employee.ser");

ObjectOutputStream out = new

ObjectOutputStream(fileOut);

out.writeObject(e);
out.close();

fileOut.close();

} catch (IOException i) {

i.printStackTrace();

Deserializarea unui obiect


Următorul program deserializează obiectul serializat în exemplul anterior. Încercaţi să

determinaţi outputul acestui program.

Exemplu

import java.io.*;

public class DeserializeDemo {

public static void main(String[] args) {

Employee e = null;

try {
FileInputStream fileIn = new

FileInputStream("employee.ser");

ObjectInputStream in = new ObjectInputStream(fileIn);

e = (Employee) in.readObject();

in.close();

fileIn.close();

} catch (IOException i) {

i.printStackTrace();

return;

} catch (ClassNotFoundException c) {

System.out.println("Employee class not found");

c.printStackTrace();

return;

System.out.println("Deserialized Employee...");

System.out.println("Name: " + e.name);

System.out.println("Address: " + e.address);


System.out.println("SSN: " + e.SSN);

System.out.println("Number: " + e.number);

În urma executării programului obţinem:

DeserializedEmployee...

Name:ReyanAli

Address:PhokkaKuan,AmbehtaPeer

SSN:0

Number:101

De reţinut:

 Blocul try/catch din exemplele de mai sus încearcă să prindă excepţia

ClassNotFoundException, excepţie declarată de metoda readObject(). Pentru ca

JVM să poată deseriliza un obiect, trebuie să aibă acces la bytecodul clasei. Dacă

acest cod nu este accesibil în timpul procesului de deserializare, obţiem excepţia

ClassNotFoundException.

 Obiectul returnat de metoda readObject() este transformat printr-un cast

explicit într-un obiect de tip Employee.


 Valoarea câmpului SSN (iniţial 1122333) a fost pierdută în timpul serializării,

deoarece câmpul este transient, şi valoarea lui nu a mai fost serializată împreună

cu restul obiectului. După deserializare, valoarea câmpului SSN este 0 (valoarea

implicită câmpurilor care conţin numere).

Excepţii
O excepţie este o problemă care apare în timpul execuţiei programului. O excepţie

poate să apară din diverse motive, inclusiv:

 Un utilizator introduce date invalide

 Un fişier care vrem sa îl deschidem, nu poate fi găsit

 O conexiune la internet a fost pierdută în timpul comunicării sau JVM a rămas

fără memorie

Unele dintre aceste excepţii sunt cauzate de erorile utilizatorilor, alte de erori ale

rogramatorilor şi altele de resurse fizice care au eşuat într-un anume mod.

Pentru a înţelege modul în care tratarea excepţiilor funcţionează în Java, trebuie să

înţelegeţi cele 3 categorii de excepţii:

 Excepţii la compilare (checked) - este o excepţie care de obicei reprezintă o

eroare utilizator sau o problemă care nu poate fi prevăzută de programator. De

exemplu, dacă un fişier trebuie deschis, dar nu este găsit, se aruncă o excepţie.

Acest tip de excepţii nu pot fi ignorate la compilare.

 Excepţii la execuţie (runtime) - este o excepţie care ar putea să apară şi care

ar fi putut fi evitată cu ajutorul programatorului. Spre deosebire de excepţiile

checked, cele runtime sunt ignorate în momentul compilării.

 Erori - acestea nu sunt excepţii, ci probleme care pot apărea, fără ca

programatorul sau utilizatorul să aibă control asupra lor. Erorile sunt în general
ignorate în cod pentru că în general nu există soluţii pentru ele. De exemplu, dacă

se întâmplă un stack overflow, va apărea o eroare. De asemenea, sunt ignoarate

şi la compilare.

Ierarhia de Excepţii
Toate clasele de excepţii sunt subtipuri ale clasei java.lang.Exception. Aceasta este o

subclasă a clasei Throwable. În afară de clasa Exception, mai există o clasă numită

Error care este derivată din Throwable.

Erorile nu sunt în mod normal condiţionate de programele Java. Acestea se petrec în

condiţii de căderi majore, care nu pot fi tratate în codul Java. Acestea sunt generate

pentru a indica erori ale mediului de execuţie. De exemplu: JVM a rămas fără

memorie. În mod normal, programele nu se pot recupera după o eroare.

Clasa Exception are două clase principale: IOException şi RuntimeException. Mai jos

este o listă cu cele mai întâlnite excepţii.

Tipuri de Excepţii

Java defineşte o serie de clase de excepţii în pachetul standard java.lang.

Cele mai generale dintre acestea sunt subclase ale tipului standard

RuntimeException. Deoarece java.lang este importat automat în toate proiectele,

majoritatea excepţiilor derivate din aceasta sunt disponibile automat.

Java defineşte o serie de alte excepţii care au legătură cu diverse librării.


Excepţii de Execuţie (Runtime)

 ArithmeticException - eroare aritmetică, de exemplu împărţirea la 0

 ArrayIndexOutOfBoundsException - indexul unui array nu este în limitele

array-ului

 ArrayStoreException - atribuirea unui element din array a unui tip

incompatibil

 ClassCastException - cast invalid

 IllegalArgumentException - argument ilegal folosit pentru apelul unei metode

 IllegalMonitorStateException - operaţie de monitor ilegală, cum ar fi

aşteptarea unui thread în stare unlocked

 IllegalStateException - mediul sau aplicaţia sunt în stări incorecte

 IllegalThreadStateException - operaţia cerută nu este compatibilă cu starea

curentă a thread-ului

 IndexOutOfBoundsException - un index nu este în limitele cerute

 NegativeArraySizeException - un array este creat cu o dimensiune negativă

 NullPointerException - utilizare invalidă a unei referinţe null

 NumberFormatException - conversie invalidă a unui şir de caractere în

format numeric

 SecurityException - tentativă de a încălca securitatea

 StringIndexOutOfBounds - folosirea unui index dincolo de limitele normale a

unui şir de caractere

 UnsupportedOperationException - la întâlnirea unei operaţii care nu este

suportată

Excepţii de Compilare (Checked)

 ClassNotFoundException - clasa nu a fost găsită


 CloneNotSupportedException - încercarea de a clona un obiect care nu

implementează interfaţa Cloneable

 IllegalAccessException - accesul la o clasă nu este permis

 InstantiationException - încercare de a crea un obiect dintr-o clasă abstractă

sau o interfaţă

 InterruptedException - un fir de execuţie este întrerupt de un altul

 NoSuchFieldException - apelarea unui câmp care nu există

 NoSuchMethodException - apelarea unei metode care nu există

Metode pentru Excepţii

Metodele următoare sunt disponibile din clasa Throwable:

 public String getMessage() - întoarce un mesaj detaliat legat de excepţia

care s-a produs. Acest mesaj este îniţializat în constructorul Throwable

 public Throwable getCause() - întoarce cauza excepţiei aşa cum este

reprezentată de obiectul Throwable

 public String toString() - întoarce numele clasei concatenate cu rezultatul

metodei getMessage()

 public void printStackTrace() - afişează rezultatul metodei toString()

înpreună cu stack trace-ul ei către System.err, stream-ul de ieşire pentru erori

 public StackTraceElement[] getStackTrace() - întoarce un array ce conţine

fiecare element din stack trace. Elementul de la indexul 0 reprezintă vârful stivei

de apeluri, iar ultimul element din array reprezintă metoda de la finalul stivei de

apeluri.

 public Throwable fillInStackTrace() - umple stack trace-ul a acestui obiect

Throwable cu stack trace-ul curent, şi adaugă informaţii anterioare la acesta.

Exemple de excepţii
int a = 10 / 0; // ArithmeticException.

Computer comp = null;

comp.ruleaza(); // NullPointerException

public class Main {

public static void main(String[] args) {

Animal animal = new Animal();

Dog dog = (Dog) animal; // ClassCastException; Animal

nu poate fi niciodata Dog

if (animal instanceof Dog) { // nu intra aici

niciodata

Dog dog2 = (Dog)animal;

Animal animal2 = new Dog(); // Asa se poate


Dog dog3 = (Dog)animal2; //Asa se poate

class Animal { }

class Dog extends Animal { }

String string = "34hggh";

int nr = Integer.parseInt(string); // NumberFormatException

int[] numere = {1, 2, 3};

System.out.println(numere[10]); // ArrayIndexOutOfBoundsException

String string = "abc";

string.charAt(20); // StringIndexOutOfBoundsException

Prinderea excepţiilor
O excepţie poate fi prinsă prin folosire multiplelor blocuri de try/catch. Acesta este

amplasat în jurul blocului care poate genera una sau mai multe excepţii. Codul din

blocul try/catch este denumit cod protejat, iar sintaxa pentru acesta arată în felul

următor:
try {

// Protected code

} catch (ExceptionName e1) {

//Catch block

Instrucţiunea catch implică declararea tipului de excepţii pe care dorim să îl prindem.

Dacă o excepţie este aruncată într-un cod protejat, blocul (sau blocurile) catch care

ţin de blocul try va fi verificat. Dacă tipul excepţiei care s-a produs este listat în blocul

catch, atunci excepţia este trimisă în blocul catch, aşa cum se transmit şi parametrii

metodelor.

Exemplu

Următorul cod conţine un array declarat cu două elemente. Codul încearcă să

acceseze un al treilea element din array, ceea ce va arunca o excepţie.

import java.io.*;

public class ExcepTest {

public static void main(String args[]) {


try {

int a[] = new int[2];

System.out.println("Access element three: " + a[3]);

} catch(ArrayIndexOutOfBoundsException e) {

System.out.println("Exception thrown: " + e);

System.out.println("Out of the block");

Ceea ce va produce următorul rezultat:

Exception thrown: java.lang.ArrayIndexOutOfBoundsException:3

Out of the block

Blocuri multiple de catch


Un bloc try poate fi urmat de mai multe blocuri catch. Sintaxa pentru mai multe

blocuri catch este următoarea:

try {

// Protected code

} catch(ExceptionType1 e1) {

// Catch block

} catch(ExceptionType2 e2) {

// Catch block

} catch(ExceptionType3 e3) {

// Catch block

Exemplul anterior conţine trei blocuri catch, dar putem avea oricât de multe blocuri

după un singur try. Dacă o excepţie este aruncată în cod protejat, aceasta este

trimisă în primul bloc din listă. Dacă tipul de date al excepţiei aruncate este acelaşi

(sau o superclasă) cu cel declarat în blocul catch, atunci excepţia este prinsă acolo.

Dacă nu, este aruncată mai departe la următoarele blocuri până când unul din catch-

uri poate să o prindă. Dacă niciunul din blocuri nu reuşeste să o prindă, metodă

curentă se opreşte din execuţie, iar excepţia este aruncată la metoda anterioară din

stiva de apeluri.
De regulă, ordinea excepţiilor în blocurile catch sunt de la cele mai specifice la cele

generale (mai exact, de la subclase, la superclase).

try {

// ... cod care poate arunca NumberFormatException

} catch (RuntimeException re) {

// ...

} catch (Exception e) {

// ...

Exemplu

try {

file = new FileInputStream(fileName);

x = (byte) file.read();

} catch(IOException i) {

i.printStackTrace();

return -1;

} catch(FileNotFoundException f) { // Not valid!


f.printStackTrace();

return -1;

Aruncarea excepţiilor: throw şi throws


Dacă o metodă nu poate trata o excepţie checked, atunci aceasta va trebui să o

declare folosind cuvântul cheie throws. Acesta va apărea la finalul semnăturii.

Putem arunca excepţii, fie una abia instanţiată ori una care o prindem, folosind

cuvântul cheie throw. În cercaţi să înţelegeţi diferenţa dintre acestea: practic throw

este punctul de plecare al excepţiei, în timp ce throws ajută aruncarea excepţiei mai

departe.

import java.io.*;

public class className {

public void deposit(double amount) throws RemoteException {

// Method implementation

throw new RemoteException();

}
//Remainder of class definition

O metodă poate declara aruncarea mai multor excepţii, caz în care sunt declarate în

semnătura metodei, după cuvântul cheie throws, cu virgulă între ele. De exemplu, o

metodă care declară că poate arunca atât RemoteException cât şi

InsufficientFundsException.

import java.io.*;

public class className {

public void withdraw (double amount) throws RemoteException,

InsufficientFundsException {

// Method implementation

//Remainder of class definition


}

Cea mai comună excepţie aruncată de programatori este: throw new

IllegalArgumentException().

Cuvântul Cheie finally


Cuvântul cheie finally este folosit pentru a crea un bloc de cod la sfărşitul unui

try/catch. Acest bloc se va executa mereu, indiferent dacă se aruncă sau nu o

excepţie.

Prin folosirea blocului finally, putem executa orice instrucţiuni de curăţare a codului,

indiferent de ce se va întămpla în codul protejat.

Finally se execută înainte de returnarea valorii dintr-o metodă.

Blocul finally are următoarea sintaxă:

try {

// Protected code

} catch(ExceptionType1 e1) {

// Catch block

} catch(ExceptionType2 e2) {

// Catch block

} catch(ExceptionType3 e3) {
// Catch block

} finally {

// The finally block always executes.

Exemplu

public class ExcepTest{

public static void main(String args[]) {

int a[] = new int[2];

try {

System.out.println("Access element three: " + a[3]);

} catch(ArrayIndexOutOfBoundsException e) {

System.out.println("Exception thrown: " + e);

} finally {

a[0] = 6;

System.out.println("First element value: " + a[0]);


System.out.println("The finally statement is executed");

Ceea ce va produce următorul rezultat:

Exception thrown: java.lang.ArrayIndexOutOfBoundsException: 3

First element value: 6

The finally statement is executed

Definirea excepţiilor
Putem defini excepții proprii. Creăm o clasă care extinde o clasă de excepții

(Exception, RuntimeException, etc). În caz că nu ne convine ceva, întrerupem fluxul

normal al programului cu aruncare unei excepții.

if (/*ceea ce nu ne convine*/) {

throw new MyException();

}
public void setNota (int nota) {

if (nota > 10) {

throw new MyCustomException (“Nota e mai mare

de 10”);

this.nota = nota;

public static void main (String [] args){

// ...

try {

student.setNota(11);

} catch (MyCustomException mce) {

System.out.println(mce.getMessage());

Excepţii Runtime (la rulare)


Dacă majoritatea excepțiilor pot fi evitate prin simple verificări, o parte nu se pot

evita:

// cerem de la un utilizator să introducă un string care să fie

transformat în număr şi utilizatorul introduce următorul şi de

caractere:

String string = "34hggh";

int nr = Integer.parseInt(string); // NumberFormatException

Ce facem în cazul acesta?

1. Putem înghiți eroarea pentru a nu crăpa programul.

try {

String string = "34hggh";

int nr = Integer.parseInt(string);

} catch (NumberFormatException nfc) {

System.out.println( "Ati introdus un sir care nu reprezinta

un numar" );

// sau, pentru noi, putem afisa eroarea:

nfc.printStackTrace(); // printarea intregii stive a erorii


}

2. Declarăm metoda noastră pentru a arunca un NumberFormatException.

public static int introduce() {

String string = "34hggh";

return Integer.parseInt(string); // posibil să arunce o

excepție

public static void main (String [] args) {

try {

System.out.println (introduce());

} catch (NumberFormatException nfe) {

System.out.println("Ati introdus un sir care nu

reprezinta un numar");

Excepţii Checked (la compilare)


Codul care poate genera erori standard (checked exceptions, așteptate a avea loc)

trebuie obligatoriu inclus într-un bloc try/catch sau declarată metoda pentru a arunca

eroarea.

public void metoda () throws IOException {

// cod care poate genera o exceptie de tip IOException

public void main (String [] args){

try {

metoda();

} catch (IOException ioe){

System.out.println("Ai Grija!");

Oricum, dacă nu scriem acest lucru ne ne va lăsa în pace compilatorul.

De reţinut
 Fiecare clauză catch nu poate exista fără un try

 Nu este obligatoriu sa avem un finally după un try/catch

 Blocul try poate fi prezent ori fără clauza catch ori fără clauza finally
 Nu putem avea cod între blocurile try, catch, finally
Important!
 Nu lăsați niciodată blocul catch gol. Blocul catch este pentru avertisment!

Dacă nu scriem nimic în el, nu vom ști că a avut loc o eroare.

 Putem prinde și o excepție de tipul superclasei exceției noastre.

 Putem prinde mai multe excepții. Regula este să pornim de la ochiuri mai mici

ale sitei către ochiurile mai mari. Dacă punem mai întâi ochiurile mari care prind

tot, nu mai sunt cu nimic de ajutor ochiurile mici puse ulterior.

 O metodă suprascrisă nu poate arunca alte checked excepții decât cele pe

care le aruncă metoda originală. Poate arunca checked subexcepții. În schimb,

poate arunca orice unchecked excepții (runtime).

 În codul de catch putem arunca o nouă excepție.

Fire de execuţie
Java este un limbaj de programare pe mai multe fire de execuţie, ceea ce înseamnă

că putem dezvolta programe multithreaded folosind Java. Un program multithreaded

conţine două sau mai multe părţi care pot rula în acelaşi timp, iar fiecare parte se va

ocupa de un task diferit în acelaşi timp, utilizându-se astfel în mod optim de resursele

disponibile, mai ales când folosim procesoare cu mai multe core-uri.

Prin definiţie, multitasking-ul se întâmplă atunci când mai multe procese împart

aceleaşi resurse, cum ar fi procesorul.

Multithreading extinde ideea de multitasking în aplicaţii care pot subdiviza operaţii

specifice dintr-o aplicaţie în fire de execuţie individuale. Fiecare fir de execuţie poate

rula în paralel. Sistemul de operare divizează timpul de procesare nu numai între

aplicaţii diferite, ci şi între firele de execuţie ale aceleiaşi aplicaţii.


Multithreading-ul ne permite să scriem într-un mod în care mai multe activităţi pot rula

concurent în acelaşi program.

Ciclul de Viaţă al unui Thread


Un fir de execuţie (thread) trece prin mai multe stagii pe durata vieţii sale. De

exemplu, acesta este creat, începe, rulează şi apoi moare.

Următoarea diagramă prezintă ciclul de viaţă complet al unui fir de execuţie.

Stagiile menţionate mai sus pot fi explicate în modul următor:

 New: un thread începe ciclul de viaţă în starea new. Rămâne în această stare

până când programul începe să ruleze firul de execuţie. Mai poate fi referit şi ca

un thread care se naşte.

 Runnable: după ce un thread abia creat este pornit, acesta intră în starea

runnable. Un thread în această stare este considerat că îşi execută task-ul.


 Waiting: câteodată, un thread trece în starea de waiting în timp ce aşteaptă un

alt thread să îşi termine task-ul. Acesta trece în starea runnable doar când alt

thread îi semnalează că poate continua.

 Timed waiting: un thread runnable poate intra în starea timed waiting pentru

un interval de timp specificat. Un thread în această stare trece înapoi în starea

runnable când expiră intervalul respectiv de timp sau când se petrece

evenimentul aşteptat.

 Terminated: un thread runnable intră în starea terminated când îşi termină de

executat toate instrucţiunile.


Priorităţile unui fir de execuţie
Fiecare fir de execuţie Java are ataşată o prioritate care ajută sistemul de operare să

determine ordinea în care thread-urile sunt programate pentru excecuţie.

Priorităţile sunt in gama de valori MIN_PRIORITY (constantă cu valoarea 1) şi

MAX_PRIORITY (constantă cu valoarea 10). Implicit, fiecare fir de execuţie primeşte

o prioritate NORM_PRIORITY (constantă cu valoarea 5).

Crearea unui Fir de Execuţie prin Implementarea


Interfeţei Runnable
Dacă avem o clasă care conţine operaţii ce vrem să le executăm pe un fir de

execuţie, putem obţine acest lucru implementând interfaţa Runnable.

Pentru aceasta, putem urmării paşii următori:

1. Implementăm metoda run() oferită de interfaţa Runnable. Aceată metodă este

folosită ca punct de intrare pentru firul de execuţie. În aceasta vom pune toată logica

care vrem să o executăm. Sintaxa metodei este:


public void run()

2. Va trebui apoi să instanţiem un obiect Thread folosind următorul constructor:

Thread(Runnable runObj, String threadName)

Unde threadObj este o instanţă a clasei care implementează interfaţa Runnable, iar

threadName este numele pe care îl dăm firului de execuţie.

3. O dată ce obiectul de tip Thread este creat, îl vom porni prin apelarea metodei

start(), care execută un apel către metoda run(). Sintaxa metodei este următoarea:

void start();

Exemplu

class RunnableDemo implements Runnable {

private Thread t;

private String threadName;

RunnableDemo( String name){

threadName = name;
System.out.println("Creating " + threadName );

public void run() {

System.out.println("Running " + threadName );

try {

for (int i = 4; i> 0; i--) {

System.out.println("Thread: " + threadName + ", " +

i);

// Let the thread sleep for a while.

Thread.sleep(50);

} catch (InterruptedException e) {

System.out.println("Thread " + threadName + "

interrupted.");

System.out.println("Thread " + threadName + " exiting.");


}

public void start () {

System.out.println("Starting " + threadName );

if (t == null) {

t = new Thread (this, threadName);

t.start ();

public class TestThread {

public static void main(String args[]) {

RunnableDemo r1 = new RunnableDemo("Thread-1");

r1.start();

RunnableDemo r2 = new RunnableDemo("Thread-2");


r2.start();

Ceea ce ar putea produce următorul rezultat:

Creating Thread-1

Starting Thread-1

Creating Thread-2

Starting Thread-2

Running Thread-1

Thread: Thread-1, 4

Running Thread-2

Thread: Thread-2, 4

Thread: Thread-1, 3

Thread: Thread-2, 3

Thread: Thread-1, 2

Thread: Thread-2, 2
Thread: Thread-1, 1

Thread: Thread-2, 1

Thread Thread-1 exiting.

Thread Thread-2 exiting.

Crearea unui Fir de Execuţie prin Extinderea Clasei


Thread
A doua modalitate de a crea un fir de execuţie este crearea unei noi clase care

extinde clasa Thread. Puteţi folosi următorii paşi pentru implementare. Această

modalitate vă permite să aveţi mai multă flexibilitate în manipularea firelor de

execuţie prin folosirea metodelor din clasa Thread.

1. Va trebui să suprascriem metoda run() disponibilă în clasa Thread. Această

metodă ne pune la dispoziţie un punct de plecare pentru firul de execuţie. În ea vom

scrie toate instrucţiunile pe care dorim să le executăm într-un fir de execuţie separat.

Sintaxa pentru metoda run() este următoarea:

public void run();

2. O dată creat obiectul pentru firul de execuţie, îl putem porni prin apelul metodei

start(), care ca executa metoda de run(). Sintaxa este următoarea:

void start();

Exemplu
Mai jos este programul anterior rescris folosind clasa Thread.

class ThreadDemo extends Thread {

private Thread t;

private String threadName;

ThreadDemo(String name) {

threadName = name;

System.out.println("Creating " + threadName );

public void run() {

System.out.println("Running " + threadName );

try {

for (int i = 4; i> 0; i--) {

System.out.println("Thread: " + threadName + ", " +

i);
// Let the thread sleep for a while.

Thread.sleep(50);

} catch (InterruptedException e) {

System.out.println("Thread " + threadName + "

interrupted.");

System.out.println("Thread " + threadName + " exiting.");

public void start () {

System.out.println("Starting " + threadName );

if (t == null) {

t = new Thread (this, threadName);

t.start ();

}
}

public class TestThread {

public static void main(String args[]) {

ThreadDemo t1 = new ThreadDemo("Thread-1");

t1.start();

ThreadDemo t2 = new ThreadDemo("Thread-2");

t2.start();

Ce ar putea produce următorul rezultat:

Creating Thread-1

Starting Thread-1

Creating Thread-2

Starting Thread-2

Running Thread-1
Thread: Thread-1, 4

Running Thread-2

Thread: Thread-2, 4

Thread: Thread-1, 3

Thread: Thread-2, 3

Thread: Thread-1, 2

Thread: Thread-2, 2

Thread: Thread-1, 1

Thread: Thread-2, 1

Thread Thread-1 exiting.

Thread Thread-2 exiting.

Exemplu de Folosire a Metodelor din Thread


Următorul program ce conţine clasa ThreadClassDemo demonstrează folosirea a

câtorva dintre metodele clasei Thread. Să considerăm clasa DisplayMessage care

implementează Runnable, o altă clasă care extinde Thread şi o clasă în care găsim

programul principal.

// File Name : DisplayMessage.java


// Create a thread to implement Runnable

public class DisplayMessage implements Runnable {

private String message;

public DisplayMessage(String message) {

this.message = message;

public void run() {

while(true) {

System.out.println(message);

}
// File Name : GuessANumber.java

// Create a thread to extentd Thread

public class GuessANumber extends Thread {

private int number;

public GuessANumber(int number){

this.number = number;

public void run() {

int counter = 0;

int guess = 0;

do {

guess = (int) (Math.random() * 100 + 1);

System.out.println(this.getName() + " guesses " +

guess);
counter++;

} while(guess != number);

System.out.println("** Correct! " + this.getName() + " in "

+ counter + " guesses.**");

// File Name : ThreadClassDemo.java

public class ThreadClassDemo {

public static void main(String [] args) {

Runnable hello = new DisplayMessage("Hello");

Thread thread1 = new Thread(hello);

thread1.setDaemon(true);

thread1.setName("hello");

System.out.println("Starting hello thread...");

thread1.start();
Runnable bye = new DisplayMessage("Goodbye");

Thread thread2 = new Thread(bye);

thread2.setPriority(Thread.MIN_PRIORITY);

thread2.setDaemon(true);

System.out.println("Starting goodbye thread...");

thread2.start();

System.out.println("Starting thread3...");

Thread thread3 = new GuessANumber(27);

thread3.start();

try {

thread3.join();

} catch(InterruptedException e) {

System.out.println("Thread interrupted.");

}
System.out.println("Starting thread4...");

Thread thread4 = new GuessANumber(75);

thread4.start();

System.out.println("main() is ending...");

Acesta ar putea produce următorul rezultat. Puteţi rula codul anterior de mai multe

ori, şi de fiecare dată puteţi obţine alt output.

Starting hello thread...

Starting goodbye thread...

Hello

Hello

Hello

Hello
Hello

Hello

Goodbye

Goodbye

Goodbye

Goodbye

Goodbye

.......

Exerciţii
1. Să se creeze o interfaţă grafică care are următoarea compoziţie:

- un câmp pentru introducerea datelor

- un buton cu mesajul "GO"

- un text pe care vor fi afişate mesaje

Funcţionalitatea trebuie să corespundă în modul următor:

- se introduce un număr în câmpul pentru introducerea datelor

- la apăsarea pe buton se va porni un nou fir de execuţie care va funcţiona ca mai jos

- când se porneşte firul de execuţie, se va prelua numărul din câmp ca un BigInteger

- sa va face suma tuturor numerelor de la 0 până la numărul introdus

- la fiecare pas se va afişa suma parţială pe textul de pe ecran


După ce aţi realizat implementarea iniţială, gestionaţi creearea threadurilor astfel

încât să nu fie mai multe fire de execuţie care rulează în acelaşi timp.

2.

Să se creeze o interfaţă grafică care să conţina mai multe componente (de care vreţi,

cel puţin 5), aliniate toate în stânga ecranului.

La rularea programului fiecare componentă va începe să "meargă" spre dreapta cu o

valoare aleatoare. Acest lucru se va întâmpla într-un Thread. Când cel puţin una din

componente va ajunge în partea dreaptă, firul de execuţie trebuie să se oprească.

System.out.printf() și String.format()
În java api clasa Formatter la rubrica Format String Syntax gasiti toate codurile %s

%d %c etc.
PrintStream (System.out.printf())
printf(String format, A convenience method to write a formatted string to this output
Object… args) stream using the specified format string and arguments.

Integer i1 = new Integer(4);

String str = "Gelu";

String str2 = "frati";

Character ch = '!';

System.out.printf("Hello, %s are %d %s%c", i1, str, str2, ch);

Printează:
Hello, Gelu are 4 frati!

 %d înseamnă un număr întreg în baza 10 (decimal)

 %c înseamnă cu caracter

 %s înseamnă un string

Notațiile acestea nu sunt din java, ci din limbajele mai vechi.

String.format()
static String format(String format, Returns a formatted string using the specified format
Object… args) string and arguments.

La fel se comportă și metoda String.format()

Character ch = '?';

String str = String.format("Știați că %s are %d %s%c", i1, str,

str2, ch);

System.out.println(str);

Printează:

Știați că Gelu are 4 frati?

Găsiti toate codurile %s %d %c etc. în java api clasa Formatter la rubrica Format

String Syntax.

BIGINTEGER
import java.math.BigInteger;

public class Main {

public static void main(String[] args) {

System.out.println(Long.MAX_VALUE);

//9,223,372,036,854,775,807

BigInteger bi1 = new

BigInteger("495885852095809859458205885");

BigInteger bi2 = new

BigInteger("558853485345834584589345805");

BigInteger suma = bi1.add(bi2);

System.out.println(suma);

BigInteger multiplicare = bi1.multiply(bi2);

System.out.println(multiplicare);
BigInteger bi3 = new BigInteger("2673");

BigInteger putere = bi3.pow(500);

System.out.println(putere);

BigInteger impartire = bi1.divide(bi3);

System.out.println(impartire);

System.out.println("Next probable prime: " +

multiplicare.nextProbablePrime());

import java.math.BigDecimal;

public class Main {


public static void main(String[] args) {

System.out.println(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 +

0.1 + 0.1 + 0.1 + 0.1);

// Rezultatul este : 0.9999999999999999 !!!!!!!

// double si float renunta la (precizie/acuratete) in

favoarea

// (acuratete / precizie).

//Si BigDecimal cu constructructorul care primeste un

double

//are aceeasi problema.

BigDecimal bd1 = new BigDecimal(0.1);

BigDecimal suma = new BigDecimal(0);

for (int i = 0; i < 10; i++) {

suma = suma.add(bd1);
}

System.out.println(suma);

//

1.0000000000000000555111512312578270211815834045410156250

// in loc de 1.0

// Constructorul care primeste un String afiseaza

corect

BigDecimal bd2 = new BigDecimal("0.1");

BigDecimal suma2 = new BigDecimal("0");

for (int i = 0; i < 10; i++) {

suma2 = suma2.add(bd2);

System.out.println(suma2); // 1.0

}
}

Formatarea Numerelor
 NumberFormat (superclasă) și DecimalFormat (subclasă)

Clasa NumberFormat se foloseste pentru formatarea numerelor pentru anumite țări, de

exemplu in cazul in care se foloseste virgula in loc de punct, sau se mai foloseste

pentru formatarea procentului, puterii, menezilor.NumberFormat este o clasa abstractă,

deci nu o putem instanția.

Formatarea numerelor reale


Locale înseamnă totalitatea caracteristicilor de formatare specifice unui număr,

monezi, calendar, etc pentru o țară și o limbă.

Locale.setDefault(Locale.GERMAN); // am stabilit lui JVM localele

GERMAN

NumberFormat nf = NumberFormat.getInstance();

Pentru că NumberFormat este o clasă abstractă, folosim această metodă

statică .getInstance() care practic returnează o instanță a unei subclase:

DecimalFormat.

// Metoda getInstance() arată așa:

public static NumberFormat getInstance() {


return new DecimalFormat();

DecimalFormat extinde pe NumberFormat

DecimalFormat is-a NumberFormat

// Verificare:

System.out.println(nf.getClass().getName()); //

java.text.DecimalFormat

String str1 = nf.format(1234.5);

System.out.println(str1); // 1.234,5 in sistem german și român

Formatarea procentului

Locale.setDefault(Locale.US);

NumberFormat nf2Procent = NumberFormat.getPercentInstance();

System.out.println(nf2Procent.format(0.47)); // 47%

Parsarea unui string


Metoda .getInstance() este supraîncărcată (overloading) și

avem .getInstance(Locale locale).


NumberFormat nf3 = NumberFormat.getInstance(Locale.GERMAN);

// Daca va uitati in API-ul de la Clasa Locale, veti vedea ca

// GERMAN (un atribut al clasei Locale) nu este un int,

// ci este chiar de tipul clasei Locale

// Deci avem o clasa care are atribute de tipul aceleasi clase

// nu este neovie sa scriem new Locale(.....)

try {

Number number = nf3.parse("1.234,5"); // sistem german

// Clasa Number este superclasa tuturor claselor de tip

Wrapper numere

// Byte, Double, Float, Integer, Long, Short, BigDecimal,

BigInteger

System.out.println(number); // 1234.5

Number number2 = nf3.parse("111.234,5678909876"); // sistem

german
System.out.println(number2); // 111234.5678909876

} catch (ParseException e) {

System.out.println( "Eroare la parsare" );

e.printStackTrace();

Metoda .parse(String string) poate arunca o excepție de tip

checked, ParseException, care trebuie declarată.

Clasa DecimalFormat
Clasa DecimalFormat este o subclasă a clasei NumberFormat. Ea moștenește toate

metodele acesteia.

Clasa DecimalFormat are diverse patternuri care pot fi folosite pentru formatarea unui

numar, de exemplu: vrem zerouri in față, vrem un anumit număr de zecimale, etc.

Constructor and Description


DecimalFormat() Creates a DecimalFormat using the default pattern and symbols
for the default locale.
DecimalFormat(String Creates a DecimalFormat using the given pattern and the
pattern) symbols for the default locale.
În afară de constructorul default, fără parametri, clasa mai are un constructor care

primește un String pattern. Cu ajutorul acestui pattern putem să construim un stil

propriu de formatare a numerelor.

Pentru patternuri: vedeți API.

Symbol Location Localized? Meaning


0 Number Yes Digit
# Number Yes Digit, zero shows as absent
. Number Yes Decimal separator or monetary decimal separator
- Number Yes Minus sign
, Number Yes Grouping separator
E Number Yes Separates mantissa and exponent in scientific
notation. Need not be quoted in prefix or suffix.
; Subpattern Yes Separates positive and negative subpatterns
boundary
% Prefix or Yes Multiply by 100 and show as percentage
suffix
\u2030 Prefix or Yes Multiply by 1000 and show as per mille value
suffix
¤(\u00A4) Prefix or No Currency sign, replaced by currency symbol. If
suffix doubled, replaced by international currency
symbol. If present in a pattern, the monetary
decimal separator is used instead of the decimal
separator.
' Prefix or No
suffix

Set<Currency> set = Currency.getAvailableCurrencies();

for (Currency currency : set) {

System.out.println( currency + ", " + currency.getSymbol()


+ ", " + currency.getDisplayName());

ETB, ETB, Ethiopian Birr

CRC, CRC, Costa Rican Colón

CVE, CVE, Cape Verdean Escudo

TMT, TMT, Turkmenistani Manat

ADP, ADP, Andorran Peseta

GIP, GIP, Gibraltar Pound

DEM, DEM, German Mark

BGN, BGN, Bulgarian Lev

ZWL, ZWL, Zimbabwean Dollar (2009)

HKD, HKD, Hong Kong Dollar

DOP, DOP, Dominican Peso

QAR, QAR, Qatari Rial

BOB, BOB, Bolivian Boliviano

XBD, XBD, European Unit of Account (XBD)


HRK, HRK, Croatian Kuna

THB, THB, Thai Baht

MYR, MYR, Malaysian Ringgit

NIO, NIO, Nicaraguan Córdoba

TPE, TPE, Timorese Escudo

BND, BND, Brunei Dollar

UAH, UAH, Ukrainian Hryvnia

CLP, CLP, Chilean Peso

CSD, CSD, Serbian Dinar (2002-2006)

SZL, SZL, Swazi Lilangeni

GBP, GBP, British Pound Sterling

PYG, PYG, Paraguayan Guarani

UZS, UZS, Uzbekistan Som

ZMW, ZMW, ZMW

USD, $, US Dollar

SKK, SKK, Slovak Koruna


INR, INR, Indian Rupee

RWF, RWF, Rwandan Franc

CUC, CUC, Cuban Convertible Peso

XOF, XOF, CFA Franc BCEAO

GMD, GMD, Gambian Dalasi

LAK, LAK, Laotian Kip

FIM, FIM, Finnish Markka

LRD, LRD, Liberian Dollar

GHS, GHS, Ghanaian Cedi

XTS, XTS, Testing Currency Code

CUP, CUP, Cuban Peso

XCD, XCD, East Caribbean Dollar

KYD, KYD, Cayman Islands Dollar

RSD, RSD, Serbian Dinar

LTL, LTL, Lithuanian Litas

MOP, MOP, Macanese Pataca


SGD, SGD, Singapore Dollar

SBD, SBD, Solomon Islands Dollar

AOA, AOA, Angolan Kwanza

XSU, XSU, Sucre

ZWN, ZWN, ZWN

MDL, MDL, Moldovan Leu

SRD, SRD, Surinamese Dollar

SAR, SAR, Saudi Riyal

IQD, IQD, Iraqi Dinar

KMF, KMF, Comorian Franc

JPY, JPY, Japanese Yen

PTE, PTE, Portuguese Escudo

GNF, GNF, Guinean Franc

VUV, VUV, Vanuatu Vatu

VEF, VEF, Venezuelan Bolívar

VND, VND, Vietnamese Dong


LYD, LYD, Libyan Dinar

GRD, GRD, Greek Drachma

ITL, ITL, Italian Lira

OMR, OMR, Omani Rial

AED, AED, United Arab Emirates Dirham

MGA, MGA, Malagasy Ariary

RON, RON, Romanian Leu

ISK, ISK, Icelandic Króna

EGP, EGP, Egyptian Pound

MAD, MAD, Moroccan Dirham

MWK, MWK, Malawian Kwacha

WST, WST, Samoan Tala

SCR, SCR, Seychellois Rupee

KPW, KPW, North Korean Won

LBP, LBP, Lebanese Pound

CDF, CDF, Congolese Franc


ARS, ARS, Argentine Peso

FJD, FJD, Fijian Dollar

BSD, BSD, Bahamian Dollar

NPR, NPR, Nepalese Rupee

TOP, TOP, Tongan Pa?anga

ZAR, ZAR, South African Rand

MUR, MUR, Mauritian Rupee

SHP, SHP, Saint Helena Pound

JOD, JOD, Jordanian Dinar

CHF, CHF, Swiss Franc

CZK, CZK, Czech Republic Koruna

SEK, SEK, Swedish Krona

SLL, SLL, Sierra Leonean Leone

JMD, JMD, Jamaican Dollar

XAG, XAG, Silver

BGL, BGL, Bulgarian Hard Lev


SRG, SRG, Surinamese Guilder

BRL, BRL, Brazilian Real

LVL, LVL, Latvian Lats

AYM, AYM, AYM

NOK, NOK, Norwegian Krone

BHD, BHD, Bahraini Dinar

MTL, MTL, Maltese Lira

BMD, BMD, Bermudan Dollar

BTN, BTN, Bhutanese Ngultrum

YER, YER, Yemeni Rial

LSL, LSL, Lesotho Loti

MZM, MZM, Mozambican Metical (1980-2006)

KGS, KGS, Kyrgystani Som

XAF, XAF, CFA Franc BEAC

BOV, BOV, Bolivian Mvdol

FKP, FKP, Falkland Islands Pound


ROL, ROL, Romanian Leu (1952-2006)

AUD, AUD, Australian Dollar

PEN, PEN, Peruvian Nuevo Sol

CNY, CNY, Chinese Yuan

HNL, HNL, Honduran Lempira

HTG, HTG, Haitian Gourde

XUA, XUA, ADB Unit of Account

XPT, XPT, Platinum

XAU, XAU, Gold

GEL, GEL, Georgian Lari

ESP, ESP, Spanish Peseta

TND, TND, Tunisian Dinar

ATS, ATS, Austrian Schilling

IEP, IEP, Irish Pound

GHC, GHC, Ghanaian Cedi (1979-2007)

PGK, PGK, Papua New Guinean Kina


UGX, UGX, Ugandan Shilling

TRY, TRY, Turkish Lira

TRL, TRL, Turkish Lira (1922-2005)

SDG, SDG, Sudanese Pound

XFU, XFU, French UIC-Franc

MGF, MGF, Malagasy Franc

KHR, KHR, Cambodian Riel

UYU, UYU, Uruguayan Peso

EEK, EEK, Estonian Kroon

NLG, NLG, Dutch Guilder

COP, COP, Colombian Peso

LKR, LKR, Sri Lankan Rupee

CLF, CLF, Chilean Unit of Account (UF)

AWG, AWG, Aruban Florin

BAM, BAM, Bosnia-Herzegovina Convertible Mark

XBB, XBB, European Monetary Unit


IDR, IDR, Indonesian Rupiah

NZD, NZD, New Zealand Dollar

XFO, XFO, French Gold Franc

AZN, AZN, Azerbaijani Manat

MZN, MZN, Mozambican Metical

ANG, ANG, Netherlands Antillean Guilder

BYR, BYR, Belarusian Ruble

CAD, CAD, Canadian Dollar

RUR, RUR, Russian Ruble (1991-1998)

XPF, XPF, CFP Franc

MKD, MKD, Macedonian Denar

USS, USS, US Dollar (Same day)

KWD, KWD, Kuwaiti Dinar

XDR, XDR, Special Drawing Rights

TMM, TMM, Turkmenistani Manat (1993-2009)

AMD, AMD, Armenian Dram


MMK, MMK, Myanma Kyat

BWP, BWP, Botswanan Pula

ALL, ALL, Albanian Lek

TTD, TTD, Trinidad and Tobago Dollar

DKK, DKK, Danish Krone

PLN, PLN, Polish Zloty

EUR, EUR, Euro

GWP, GWP, Guinea-Bissau Peso

CYP, CYP, Cypriot Pound

ZWR, ZWR, Zimbabwean Dollar (2008)

MXN, MXN, Mexican Peso

ZWD, ZWD, Zimbabwean Dollar (1980-2008)

HUF, HUF, Hungarian Forint

TWD, TWD, New Taiwan Dollar

BDT, BDT, Bangladeshi Taka

XPD, XPD, Palladium


AZM, AZM, Azerbaijani Manat (1993-2006)

SDD, SDD, Sudanese Dinar (1992-2007)

USN, USN, US Dollar (Next day)

KZT, KZT, Kazakhstani Tenge

BBD, BBD, Barbadian Dollar

AFN, AFN, Afghan Afghani

SYP, SYP, Syrian Pound

PAB, PAB, Panamanian Balboa

XBA, XBA, European Composite Unit

KRW, KRW, South Korean Won

MRO, MRO, Mauritanian Ouguiya

AFA, AFA, Afghan Afghani (1927-2002)

ERN, ERN, Eritrean Nakfa

LUF, LUF, Luxembourgian Franc

BYB, BYB, Belarusian New Ruble (1994-1999)

SVC, SVC, Salvadoran Colón


BEF, BEF, Belgian Franc

KES, KES, Kenyan Shilling

SSP, SSP, South Sudanese Pound

XXX, XXX, Unknown Currency

VEB, VEB, Venezuelan Bolívar (1871-2008)

ZMK, ZMK, Zambian Kwacha

XBC, XBC, European Unit of Account (XBC)

NGN, NGN, Nigerian Naira

DZD, DZD, Algerian Dinar

MNT, MNT, Mongolian Tugrik

IRR, IRR, Iranian Rial

FRF, FRF, French Franc

TJS, TJS, Tajikistani Somoni

NAD, NAD, Namibian Dollar

MXV, MXV, Mexican Investment Unit

YUM, YUM, Yugoslavian New Dinar (1994-2002)


MVR, MVR, Maldivian Rufiyaa

STD, STD, São Tomé and Príncipe Dobra

RUB, RUB, Russian Ruble

DJF, DJF, Djiboutian Franc

PKR, PKR, Pakistani Rupee

PHP, PHP, Philippine Peso

BIF, BIF, Burundian Franc

TZS, TZS, Tanzanian Shilling

SIT, SIT, Slovenian Tolar

GYD, GYD, Guyanaese Dollar

ILS, ILS, Israeli New Sheqel

SOS, SOS, Somali Shilling

BZD, BZD, Belize Dollar

GTQ, GTQ, Guatemalan Quetzal

// si clasa Currency este tot abstracta

Currency dollar = Currency.getInstance(Locale.US); // merge si "USD"


System.out.println(dollar.getDisplayName());

NumberFormat nfUS = NumberFormat.getCurrencyInstance(Locale.US);

System.out.println(nfUS.format(12000.4)); // $12,000.40

NumberFormat nfGer =

NumberFormat.getCurrencyInstance(Locale.GERMANY);

System.out.println(nfGer.format(12000.4)); // 12.000,40 ℬ

// A pus punct in loc de virugula si virgula in loc de punct.

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