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 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.