You are on page 1of 102

PROGRAMARE OBIECT

2
ORIENTATA
Iolu Mihai - S
tefan
19 noiembrie 2008

ii

Cuprins

1 Introducere
1.1 Mediul de programare Java

. . . . . . . . . . . . . . . . . . .

2 Structuri fundamentale de programare n Java


2.1 Primul program Java . . . . . . . . . . . . . . .
2.2 Transmiterea parametrilor din linie de comanda
2.3 Comentarii . . . . . . . . . . . . . . . . . . . . .
2.4 Tipuri de date . . . . . . . . . . . . . . . . . . .
2.4.1 Tipuri de date primitive . . . . . . . . .
2.4.2 Tipuri de date referinta . . . . . . . . .
2.5 Operatori . . . . . . . . . . . . . . . . . . . . .
2.6 Instructiuni decizionale . . . . . . . . . . . . . .
2.7 Instructiuni repetitive . . . . . . . . . . . . . .
2.8 Vectori si matrici . . . . . . . . . . . . . . . . .
2.8.1 Vectori . . . . . . . . . . . . . . . . . . .
2.8.2 Matrici . . . . . . . . . . . . . . . . . . .
2.9 Siruri de caractere . . . . . . . . . . . . . . . .
2.9.1 Clasa String . . . . . . . . . . . . . . . .
2.9.2 Clasa StringBuffer . . . . . . . . . . . .
2.9.3 Clasa StringTokenizer . . . . . . . . . .
2.9.4 Expresii regulate n Java . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

1
3
5
5
8
9
12
12
13
13
14
15
16
16
20
23
23
27
28
29

3 Concepte si principii n POO


31
3.1 Concepte n programarea obiect orientata . . . . . . . . . . . . 31
3.2 Principii ale programarii obiect orientate . . . . . . . . . . . . 32
iii

iv

CUPRINS
3.3

Solutii pentru utilizarea eficienta a principiilor POO . . . . . . 34

4 Obiecte si clase
4.1 Definirea unei clase . . . . . . . . . . . . . .
4.2 Declararea atributelor unei clase . . . . . . .
4.2.1 Modificatorii de vizibilitate . . . . . .
4.2.2 Modificatorul final . . . . . . . . . .
4.2.3 Modificatorul static . . . . . . . . . .
4.2.4 Modificatorii transient si volatile . .
4.3 Declararea metodelor unei clase . . . . . . .
4.3.1 Modificatorul static . . . . . . . . . .
4.3.2 Transmiterea parametrilor la metode
4.4 Declararea constructorilor unei clase . . . .
4.5 Initializatorul static al unei clase . . . . . .
4.6 Probleme rezolvate . . . . . . . . . . . . . .
4.7 Probleme propuse . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

5 Mostenirea n Java
5.1 Notiuni generale . . . . . . . . . . . . . . . . . . .
5.2 Exemplu de aplicatie folosind mostenirea . . . . .
5.3 Polimorfismul . . . . . . . . . . . . . . . . . . . .
5.4 Metode si clase finale . . . . . . . . . . . . . . . .
5.5 Operatia de casting . . . . . . . . . . . . . . . . .
5.6 Clase abstracte . . . . . . . . . . . . . . . . . . .
5.7 Clasa Object . . . . . . . . . . . . . . . . . . . .
5.7.1 Metoda toString() . . . . . . . . . . . . .
5.7.2 Metoda equals() . . . . . . . . . . . . . . .
5.7.3 Metoda hashCode() . . . . . . . . . . . . .
5.7.4 Metoda clone() . . . . . . . . . . . . . . .
5.8 Implementarea colectiilor generice folosind Object
5.9 Utilizarea colectiilor generice cu template-uri . . .
5.9.1 Boxing si unboxing n Java . . . . . . . . .
6 Tratarea exceptiilor n Java
6.1 Clasificarea exceptiilor . . . . . . . . . . .
6.2 Crearea claselor exceptie . . . . . . . . . .
6.3 Aruncarea exceptiilor . . . . . . . . . . . .
6.4 Prinderea exceptiilor . . . . . . . . . . . .
6.5 Indicatii referitoare la utilizarea exceptiilor
6.6 Exemple . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

37
37
38
39
40
41
42
42
43
43
44
46
47
65

.
.
.
.
.
.
.
.
.
.
.
.
.
.

67
67
68
73
75
75
76
82
83
83
84
84
85
89
90

.
.
.
.
.
.

93
94
95
96
96
98
98

CAPITOLUL

Introducere

Acest curs se refera la programarea obiect orientata si principalele caracteristici ale acesteia. Acestea vor fi prezentate n contextul unui limbaj
care ofera un foarte bun suport din acest punct de vedere, respectiv limbajul
Java.
Java este, n mod indiscutabil, unul din cele mai populare limbaje de
programare obiect orientate (alaturi de limbaje precum C++, C# sau Visual
Basic). Acest lucru a facut ca el sa fie folosit de milioane de dezvoltatori din
lumea ntreaga mai ales n contextul aplicatiilor web.
El a aparut la sfarsitul anului 1995 si a avut parte de o mare publicitate
nca de la nceput. Este un limbaj care ofera suport pentru toate tehnicile
moderne de programare precum: programarea n retea, lucrul cu fire de
executie, internationalizare, lucrul cu baze de date si multe altele.
De-a lungul timpului cei de la Sun Microsystems, care se ocupa de evolutia
limbajului, au scos 7 revizii majore ale acestuia. Cea mai importanta a fost
cea care a dus la aparitia versiunii JDK 5.0. Momentan s-a ajuns la versiunea
JDK 6.0.
Evolutia este impresionanta daca tinem cont ca s-a pornit de la un numar
de aproximativ 200 de clase si s-a ajuns acum la peste 3000 de clase.
Java este mult mai mult decat un simplu limbaj. Java este o platforma
care are o librarie imensa ce contine foarte mult cod reutilizabil precum si
un mediu de executie care ofera servicii precum: securitate, portabilitate pe
diverse medii de executie precum si garbage collection. Unele din principalele
avantaje ale acestui mediu Java sunt urmatoarele:
1

CAPITOLUL 1. INTRODUCERE
Limbajul Java este un limbaj relativ simplu. Dupa cum vom vedea n
cele ce urmeaza n Java nu avem numeroase constructii care existau n
C/C++ precum: pointeri, structuri, fisiere header, directive de preprocesor, suprancarcare de operatori, mostenire virtuala etc.
Limbajul Java este obiect orientat. In ziua de azi, majoritatea limbajelor care exista pe piata si au o foarte larga raspandire au la baza
principiile programarii obiect orientate (vezi C++, C#, Visual Basic).
Java are un bogat suport pentru aplicatiile distribuite. Exista numeroase
clase care permit crearea unor aplicatii care sa ruleze n retea, folosind
diverse modalitati de lucru: sockets, RMI, CORBA etc.
Java este un limbaj robust. Cei care au dezvoltat acest limbaj au avut
ca scop foarte important ncercarea de a evita pe cat posibil aparitia
erorilor, prin depistarea acestora cat mai rapida sau prin usurarea modului de a programa. Un astfel de exemplu este lipsa pointerilor care face
ca programarea sa fie mai usoara si practic face imposibila coruperea
memoriei care se putea produce destul de usor n C/C++.
Java este portabil. Aceasta nseamna ca daca scriem un cod pe platforma Windows, exact acelasi cod fara modificari va putea rula si pe o
platforma Linux de exemplu. Nu acelasi lucru se ntampla, de exemplu,
n cazul C++. Tipul int putea avea dimensiuni diferite n functie de
platforma tinta.
Java este neutru din punct de vedere arhitectural. In momentul n care
se compileaza codul Java, rezultatul produs este un bytecode. Acesta
este rulat cu ajutorul unei masini virtuale Java (JVM - Java Virtual
Machine). Din cauza acestui mod de gandire este posibil ca acelasi
cod sa ruleze pe orice sistem de operare, doar implementarea masinii
virtuale diferind. Un mecanism asemanator a fost dezvoltat si de catre
cei de la Microsoft pentru platforma .NET, care are la baza tot o masina
virtuala.

Voi ncerca n cele ce urmeaza sa prezint un curs n care accentul se va


pune mai ales pe partea practica a limbajului si pe facilitatile oferite de
acesta pentru programarea obiect orientata. Din cauza timpului limitat, nu
vom prezenta facilitatile pentru lucrul cu interfete grafice, pentru programare
web sau pentru realizarea aplicatiilor pe mobil. Acestea sunt subiecte de mare
interes care pot fi studiate ntr-un curs viitor.
Totusi trebuie precizat nca de la nceput ca se foloseste mediul Java
pentru a realiza mai multe tipuri de aplicatii:

1.1. MEDIUL DE PROGRAMARE JAVA

Aplicatii consola (n linie de comanda): se pot rula din linie de comanda, neavand o interfata grafica
Aplicatii grafice (folosesc librarii AWT sau Swing)
Applet-uri - sunt un anumit tip de aplicatii grafice care pot fi introduse
n pagini web, facand ca o pagina web sa aiba un aspect mult mai
dinamic asemanator aplicatiilor desktop
Pagini web dinamice - sunt create cu ajutorul unor tehnologii care s-au
dezvoltat peste Java si care adauga functionalitati noi limbajului (vezi
JSP, Servlets, Spring etc.)
Aplicatii pe mobil (se realizeaza cu ajutorul unei distributii Java numita
J2ME - Java 2 Microedition).
Accentul n continuare se va pune mai ales pe primul tip de aplicatie,
doar tangential se vor atinge celelalte tipuri.
Trebuie spus ca majoritatea ideilor prezentate vis-a-vis de programarea
obiect orientata sunt aplicabile si n cazul altor limbaje obiect orientate precum C++ (deja studiat) si C# (care va fi studiat n semestrul urmator).
Voi ncerca sa prezint n continuare urmatoarele lucruri:
Structurile de programare fundamentale n Java
Lucrul cu obiecte si clase, mostenirea si lucrul cu interfete
Tratarea exceptiilor
Stream-uri Java
Programarea generica si colectiile
Lucrul cu baze de date
Annotations
Java Reflection

1.1

Mediul de programare Java

In aceasta sectiune vom prezenta acele softuri necesare pentru a rula


aplicatiile Java pe care le vom prezenta n continuare precum si diversele
moduri n care se poate lucra folosind mai mult sau mai putin editoare dedicate pentru lucrul cu Java.

CAPITOLUL 1. INTRODUCERE

Pentru a folosi Java, n functie de tipul aplicatiilor care se doreste a fi


create, se poate descarca una din urmatoarele variante:
J2ME - Java 2 Micro Edition (se foloseste pentru crearea aplicatiilor
pentru mobile)
J2SE - Java 2 Standard Edition (se foloseste pentru crearea aplicatiilor
consola sau grafice obisnuite)
J2EE - Java 2 Enterprise Edition (se foloseste pentru crearea aplicatiilor
web).
Pentru a putea compila si rula aplicatii Java este bine sa ncepem prin a
instala JDK -ul (Java Development Kit).
Acesta poate fi descarcat gratuit la urmatoarea adresa web:
http://java.sun.com/javase/downloads/index.jsp.
In acest moment se poate alege ntre o versiune beta care este mai noua
sau o versiune stabila.
Este recomandabil, de asemenea, sa se downloadeze si documentatia pentru JDK care se gaseste n sectiunea Java SE 6 Documentation.
Dupa ce se instaleaza JDK (procesul este foarte usor, motiv pentru care
nici nu va fi descris) se poate dezarhiva directorul docs si, eventual, se poate
copia n directorul n care a fost salvat JDK.

CAPITOLUL

Structuri fundamentale de programare n Java

2.1

Primul program Java

In aceasta sectiune vom crea primul nostru program Java, pe care-l vom
rula din linie de comanda. Acest program arata astfel:
public class Welcome
{
public static void main(String[] args)
{
System.out.println("Hello world!");
}
}
Pentru a rula acest program din linie de comanda se parcurg mai multi
pasi:
1. Se introduce programul ntr-un editor si apoi se salveaza cu extensia
.java. Uneori numele trebuie sa respecte anumite conditii dar asupra
acestui aspect vom reveni. In cazul nostru vom salva programul cu
numele Welcome.java.
2. Intram n linie de comanda prin Start+Run...+cmd (sau command, n
cazul calculatoarelor care ruleaza Win 98 sau Win ME ), dupa care ne
pozitionam n directorul n care a fost salvat fisierul anterior.
5

6CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE IN JAVA


3. Tastam SET PATH=%PATH%;c:\program files\Java\jdk1.6.0 07\bin
pentru a adauga directorul bin din distributia Java n cale. Aceasta
cale poate fi diferita n functie de locul n care a fost instalat Java sau
de versiunea de JDK instalata.
4. Se compileaza aplicatia cu javac Welcome.java. Daca apar erori, acestea trebuie corectate, dupa care fisierul trebuie salvat si se recompileaza
aplicatia pana cand nu avem erori.
5. In final se ruleaza aplicatia cu java Welcome.
Observatii:
Limbajul Java este case-sensitive, ceea ce nseamna ca se face diferenta
ntre litere mici si mari. Astfel, daca cuvintele class sau System nu
sunt scrise exact ca n exemplu s-ar genera o eroare de compilare.
Numele fisierului trebuie sa fie chiar Welcome.java deoarece n program
avem o clasa publica numita Welcome.
In directorul n care am instalat Java exista mai multe directoare precum: bin, include, lib etc. O semnificatie deosebit o are directorul bin
n care sunt pozitionate mai multe fisiere executabile precum java.exe
si javac.exe. Este necesar pasul 3 deoarece aceste fisiere executabile nu
sunt implicit n calea sistemului de operare. De altfel, la pasii 4 si 5,
compilarea respectiv rularea programelor se face folosind aceste fisiere
executabile.
In faza de compilare utilitarul javac primeste ca intrare fisierul Welcome.java si, n cazul n care compilarea reuseste, genereaza fisierul
Welcome.class. In faza de rulare, utilitarul java.exe foloseste acest
fisier generat n faza de compilare.
In Java tot codul care va fi scris trebuie sa fie introdus n cadrul unor
clase, motiv pentru care si aici am scris codul n cadrul unei clase. Aceasta
clasa are plasata naintea definitiei sale cuvntul cheie public ceea ce nseamna
ca ea poate fi accesata de oriunde. Vom vorbi mai multe despre indicatorii
de acces atunci cand vom prezenta modul n care se lucreaza cu clase.
In interiorul acestei clase avem definita o metoda main() care este publica
si statica. Aceasta metoda este mai speciala, ea fiind ntotdeauna publica si
statica. Ce o face mai speciala este faptul ca ea constituie punctul de intrare
n program. Aceasta nseamna ca atunci cand rulam o aplicatie, executia
acesteia ncepe cu aceasta metoda.

2.1. PRIMUL PROGRAM JAVA

Metoda main() primeste un sir de siruri de caractere, care reprezinta


parametrii din linie de comanda. Aceasta constructie este asemanatoare cu
ceea ce stiam din limbajul C++.
Dupa cum se poate usor observa n Java, instructiunile sunt separate prin
;, la fel ca si n limbajul C/C++.
In acest exemplu avem un obiect, System.out, asupra caruia se apeleaza o
metoda println(), care primeste ca parametru un sir de caractere si realizeaza
afisarea acestuia pe ecran precum si saltul la linie noua. Daca nu doream
salt la linie noua foloseam metoda print().
In Java delimitarea blocurilor de cod se face folosind caracterele { si
}.
Cuvantul cheie void are aceeasi semnificatie ca si n C/C++ si anume
faptul ca metoda main nu returneaza nimic.
Foarte important atunci cand lucram cu limbajul Java este sa respectam
regulile de denumire a claselor, obiectelor, metodelor si constantelor.
Astfel, numele unei clase ncepe ntotdeauna cu litera mare iar n cazul
n care cuvantul care se refera la numele clasei este alcatuit din mai multe
cuvinte, fiecare cuvant este scris cu litera mare. Numele unei clase trebuie
sa fie un substantiv la singular. Ex: Stack, StackOverflowException etc.
Numele metodelor ncep cu litera mica iar n cazul n care sunt alcatuite
din mai multe cuvinte, numele primului cuvant se scrie cu litera mica iar
numele celorlalte cuvinte cu litera mare. Ex: println(), printWords(), assertEquals() etc.
Numele obiectelor respecta exact aceleasi reguli ca numele metodelor.
Numele constantelor se scriu ntotdeauna numai cu litere mari. Ex:
MAX VALUE, MIN etc.
Observatii:
Compilatorul nu genereaza eroare daca aceste reguli nu sunt respectate
ntocmai, dar este recomandabila respectarea lor.
Numele de clase, metode, variabile nu pot contine caracterul spatiu.
In momentul n care se scrie cod Java, acesta trebuie aliniat corespunzator. Compilatorul nu va plange daca nu se respecta aceasta
regula, dar aceasta indica un programator care de multe ori nu ntelege
ceea ce programeaza.

8CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE IN JAVA

2.2

Transmiterea parametrilor din linie de comand


a

In aceasta sectiune vom prezenta un program foarte asemanator cu cel


prezentat nainte, program care va folosi parametri transmisi din linie de
comanda.
public class Welcome2
{
public static void main(String[] args)
{
for(int i=0;i<args.length;i++)
System.out.println("Hello "+args[i]+"!");
}
}
Acesta este un program foarte simplu care are ca efect afisarea unui mesaj
de ntampinare pentru fiecare nume transmis din linie de comanda.
Astfel, vom rula programul cu urmatoarea comanda:
java Welcome2 radu aurel "ana maria"
efectul lui fiind afisarea urmatoarelor mesaje:
Hello radu!
Hello aurel!
Hello ana maria!
In acest fel am exemplificat utilitatea parametrului args al metodei main.
Acesta este un sir de siruri de caractere. Asemenea oricarui vector n Java
el are un atribut length care reprezinta lungimea vectorului.
Anticipam un pic prezentarea vectorilor prin a spune ca n Java vectorii
sunt indexati ncepand de la pozitia 0.
Mai prezentam n continuare un alt exemplu de aplicatie care rezolva
ecuatia de gradul al doilea care are coeficientii transmisi din linie de comanda:
public class EcGr2
{
public static void main(String[] args) {
if(args.length<3)
{
System.out.println("Nu sunt suficienti parametri");

2.3. COMENTARII

System.exit(0);
}
double a=Double.parseDouble(args[0]);
double b=Double.parseDouble(args[1]);
double c=Double.parseDouble(args[2]);
double delta=b*b-4*a*c;
if(delta<0)
System.out.println("Ecuatia nu are solutii reale");
else
if(delta==0)
System.out.println("Solutie unica: "+ (-b)/(2*a));
else
{
double x1=(-b-Math.sqrt(delta))/(2*a);
double x2=(-b+Math.sqrt(delta))/(2*a);
System.out.println("x1="+x1);
System.out.println("x2="+x2);
}
}
}
Observatii:
Metoda statica parseDouble() a clasei Double are rolul de a transforma
un sir de caractere ntr-un numar de tip double. In mod asemanator
exista Integer.parseInt() si Float.parseFloat().
Functiile matematice gata definite n limbajul Java sunt implementate n clasa Math, ele fiind functii statice. Un exemplu este functia
Math.sqrt() folosita pentru a calcula radicalul unui numar.

2.3

Comentarii

In Java se pot realiza trei tipuri de comentarii care sunt toate prezentate
n exemplul urmator. Aceasta aplicatie calculeaza suma cifrelor unui numar
ntreg:
/**
*
* @author mihai

10CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE IN JAVA


* @version 1.0
*/
public class ExempluComentariu
{
/**
* @param n
*
numarul pentru care se calculeaza suma cifrelor.
*
Trebuie sa fie un numar mai mare decat 0.
* @return suma cifrelor numarului
*/
public static int sumaCif(int n)
{
/*
* Metoda sumaCif este o metoda recursiva
* care calculeaza suma cifrelor unui numar
*/
if(n<9)
return n;
return n%10+sumaCif(n/10); //aici are loc apelul recursiv
}
/**Calculeaza suma cifrelor unui numar folosind o metoda recursiva
* @param args reprezinta argumentele din linie de comanda pentru
* programul meu
*/
public static void main(String[] args)
{
int n=234;
System.out.println("Suma cifrelor este: "+sumaCif(n));
}
}
Cele trei tipuri de comentarii care se pot realiza sunt urmatoarele:
Comentarii pe o singura linie, care se realizeaza la fel ca n C/C++
folosind caracterele //
Comentarii pe mai multe linii, care sunt cuprinse ntre /* si */
Comentarii care ajuta la generarea documentatiei, care sunt cuprinse
ntre /** si */

2.3. COMENTARII

11

Primele doua moduri de lucru sunt identice cu ceea ce ntalnim n C/C++,


motiv pentru care ne vom axa n principal pe a treia modalitate de generare
a documentatiei.
Comentariile care ajuta la generarea documentatiei sunt folosite pentru
a genera niste fisiere HTML care descriu clasa pe care am scris-o. Aceste
fisiere vor arata exact la fel ca documentatia furnizata mpreuna cu mediul
JDK.
Generarea acestor fisiere se poate face utilizand utilitarul javadoc astfel:
javadoc ExempluComentariu.java
Vom remarca aparitia n directorul n care avem fisierul a mai multor fisiere
HTML. Daca deschidem fisierul ExempluComentariu.html vom putea vedea
documentatia.
Un rol foarte important n cadrul documentatiei l au tag-urile predefinite,
cu ajutorul carora se pot specifica anumite informatii mai speciale:
@name - reprezinta numele celui care a scris codul pentru aceasta clasa
@version - reprezinta versiunea clasei
@return - specifica ce returneaza metoda respectiva
@param - descrie un anumit parametru
Exista si alte tag-uri care sunt foarte utile. Cel mai bine se poate studia
modul n care sunt ele folosite daca examinam codul sursa Java prezent
n fisierul src.zip aflat n directorul n care este instalat Java.
Aceste comentarii vor conduce la obtinerea unor fisiere HTML, motiv
pentru care este posibil ca n interiorul lor sa folosim cod HTML.
Scrierea documentatiei pentru clasele pe care le creem este necesara deoarece,
de multe ori, se ntampla ca cel care foloseste clasa sa nu fie aceeasi persoana
cu cea care a scris codul pentru clasa. Nu este normal ca pentru a folosi o
clasa sa trebuiasca neaparat sa umblam n codul ei.
Acesta idee poarta numele de ncapsulare. Practic, putem sa ne gandim
la o clasa ca la un fel de cutie neagra (black-box) care are o interfata pe care
o putem folosi fara a cunoaste continutul ei.
Generarea documentatiei n Eclipse se poate face prin alegerea optiunii
Generate JavaDoc din meniul Project. La un moment dat trebuie introdusa
calea catre utilitarul javadoc, dupa care se urmeaza pasii sugerati de wizard.

12CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE IN JAVA

2.4

Tipuri de date

Un tip de date reprezinta o plaja de valori pe care o variabila le poate


lua precum si operatiile care se pot efectua asupra acesteia.
Java este un limbaj strong typed ceea ce nseamna ca orice variabila are
un tip foarte bine definit. Acest lucru face posibila determinarea mai usoara
a erorilor din program la momentul compilarii.
In Java avem trei tipuri de date: tipuri de date primitive (byte, short, int,
long, char, float, double, boolean), tipuri de date referinta (class, interface,
array) si tipul de date null (corespunzator literalului null).

2.4.1

Tipuri de date primitive

Tipurile de date primitive sunt de mai multe feluri:


tipuri numerice: byte, short, int, long, float, double
tipul caracter
tipul boolean
Tipurile de date numerice sunt prezentate n urmatorul tabel:
Tabela 2.1: Tipuri de date numerice
Tip
byte
short
int
long

Dimensiune
1 byte
2 bytes
4 bytes
8 bytes

float

4 bytes

double

8 bytes

Valori admise
-128...127
-32768 ... 32767
-2,147,483,648 ... 2,147,483, 647
-9,223,372,036,854,775,808
...
9,223,372,036,854,775,807
aproximativ 3.40282347E+38F (6-7 cifre zecimale semnificative)
aproximativ 1.79769313486231570E+308 (15
cifre zecimale semnificative)

Numerele scrise n baza 16 au prefixul 0x iar numerele scrise n baza 8 au


prefixul 0.
Observatii:
Ceea ce este foarte important n Java si un mare avantaj fata de C/C++
este faptul ca indiferent de platforma, tipurile de date vor avea exact
aceeasi dimensiune de reprezentare.

2.5. OPERATORI

13

In Java se pot reprezenta valori speciale gen Double.NaN, Double.


POSITIVE INFINITY sau Double.NEGATIVE INFINITY (precum
si corespondentul lor relativ la tipul float). Ele au urmatoarele semnificatii:
NaN se refera la faptul ca putem avea o operatie care sa nu
genereze un numar. Ex: radical dintr-un numar negativ sau
mpartirea 0/0.
POSITIVE INFINITY se poate obtine daca mpartim un numar
pozitiv la 0. Aceste constante se folosesc foarte rar n practica.
Pentru a vedea daca un numar este de tipul NaN se procedeaza
astfel:
if (Double.isNaN(x))
....
Un tip de date mai deosebit este tipul char. In Java variabilele de tip
char se reprezinta ntre caractere . La fel ca si n C/C++ exista caractere
mai speciale care sunt tratate folosind secvente escape: \b, \t, \n, \r, \, \,
\\.
In Java tipul boolean are doua valori true si false. Nu se mai respecta
conventia din C/C++ care spunea ca valoarea 0 nseamna false si valoare
diferita de 0 reprezinta true.

2.4.2

Tipuri de date referint


a

In plus fata de tipurile de date primitive, n Java avem si tipurile de


date referinta care se refera la obiecte (instante ale unor clase) precum si
la vectori. Nu vom prezenta acest subiect aici, urmand a reveni ntr-un
subcapitol ulterior.
Vom preciza doar un singur lucru, si anume faptul ca diferenta ntre cele
2 tipuri de date consta n faptul ca pentru tipurile de date primitive se retine
exact valoarea lor n timp ce pentru tipurile de date referinta se retine n
variabila corespunzatoare lor adresa unei zone de memorie unde se retine de
fapt valoarea lor.
Acest lucru va avea un impact foarte important la transmiterea parametrilor.

2.5

Operatori

Un operator utilizeaza unul sau mai multi operanzi pentru a produce o


noua valoare. Un operator poate, de asemenea, sa schimbe valoarea unui
operand.

14CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE IN JAVA


Operatorii din Java se pot clasifica astfel:
Operatorul de atribuire =
Operatorii aritmetici:
Operatorii binari: adunare(+), scadere(-), nmultire(*), mpartire(/),
restul mpartirii(%)
Operatorii unari: semnul minus(-), semnul plus(+), incrementare(++),
decrementare()
Operatorii relationali: egal(==), diferit(!=), mai mare(>), mai mic(<),
mai mare sau egal(>=), mai mic sau egal(<=)
Operatori logici:
Conditionali: si(&&), sau(||), negatie(!), xor()
Pe biti: si(&), sau(|), xor(), complementare(), deplasare la
stanga(<<), deplasare la dreapta (>>)
Operatorii de atribuire combinati (atat aritmetici cat si pe biti: +=,
-=, /=, %=, &=, |=, ==, <<==, >>==
Operatorul conditional: expr logica ? val1 : val2
Operatorul adunare de siruri de caractere
Operatorul de cast
Operatorul instanceof

2.6

Instructiuni decizionale

In Java avem 2 instructiuni conditionale: if-else si switch.


Instructiunea if-else are urmatoarea structura:
if (conditie)
secventa1
[else
secventa2]
Instructiunea switch permite executarea uneia din mai multe secvente de
instructiuni n functie de valoarea unei expresii. Forma generala a acesteia
este:

2.7. INSTRUCT
IUNI REPETITIVE

15

switch(expresie)
{
case val1: secventa1
[break;]
case val2: secventa2
[break;]
....
case valN: secventaN
[break;]
[default:
secventa]
}
Instructiunea se evalueaza astfel: se calculeaza valoarea expresiei dupa
care se gaseste case-ul corespunzator expresiei. Incepand de la acesta se
evalueaza toate secvenele pana la ntalnirea lui break. In cazul n care nici e
expresie nu se potriveste se executa instructiunea din clauza default.
Vom prezenta exemple atunci cand vom prezenta lucrul cu siruri de caractere.

2.7

Instructiuni repetitive

In Java avem trei tipuri de structuri repetitive: while, do-while si for.


Instructiunea while are urmatoarea forma:
while (conditie)
instructiune
Ideea acestei instructiuni este ca, atta timp cat este adevaarata conditia,
se executa instructiunea cuprinsa n ciclul while.
Instructiunea do-while are urmatoarea forma:
do
instructiune
while (conditie);
Ideea acestei instructiuni este ca se tot executa instructiunea (care poate
fi simpla sau compusa) atata timp cat este adevarata conditia.
Cea de-a treia instructiune repetitiva, care de asemenea exista si n limbajul C/C++ este instructiunea for :

16CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE IN JAVA


for (expresie initializare; conditie continuare; secventa incrementare)
instructiune
Ideea acestei instructiuni este ca ntai se executa initializarile prevazute
n expresie initializare, dupa care se evalueaza conditia de continuare. Atata
timp cat conditia este adevarata se executa instructiunea simpa sau compusa, dupa care se executa secventa de incrementare si se revine la evaluarea
conditiei de continuare.
Singura diferenta fata de limbajul C/C++ este ca, daca declaram si
initializam o variabila n expresia de initializare, atunci aceasta variabila
nu este vizibila decat n corpul instructiunii for.
Un rol important n cadrul instructiunilor repetitive l are instructiunea
break care odata folosita are ca efect iesirea din ciclul repetitiv cel mai interior
si trecerea la urmatoarea instructiune de executat dupa acesta.

2.8
2.8.1

Vectori si matrici
Vectori

Un tablou (vector) este o lista de elemente de acelasi tip plasate ntr-o


zona continua de memorie, care pot fi accesate printr-un nume comun. Toate
elementele dintr-un tablou trebuie sa aiba acelasi tip. Tipurile admise sunt
toate tipurile primitive sau orice tip referinta.
In Java tablourile sunt obiecte. Un tablou are toate metodele pe care
le are clasa Object si n plus are un membru length care ne spune care este
dimensiunea tabloului.
Declararea si alocarea de memorie pentru un tablou
Un tablou se poate declara ntr-unul din urmatoarele doua moduri echivalente:
tip[] nume;
tip nume[];
Doar declarand un tablou, nu putem face nimic cu el. Pentru a-l putea
folosi trebuie sa si alocam memorie pentru el. Aceasta se poate face n doua
moduri:
Prin folosirea operatorului new :
nume=new tip[dimensiune];

2.8. VECTORI SI MATRICI

17

Prin initializarea directa a elementelor tabloului:


tip[] nume={element1, element2, element3};

Dupa cum se poate observa, n acest al doilea caz declararea si alocarea


de memorie se fac n aceeasi instructiune.
Observatii:
Se poate realiza atat declararea tabloului cat si alocarea de memorie
ntr-o singura instructiune:
tip[] nume=new tip[dimensiune];
Atunci cand se aloca memorie pentru un tablou, elementele acestuia vor
fi initializate cu valoarea implicita corespunzatoare tipului elementelor
tabloului (0 - valori numerice intregi, 0.0 pentru valori reale, false pentru tipul boolean, null - pentru obiecte).
Tablourile n limbajul Java sunt 0 indexate ceea ce nseamna ca se
ncepe de la elementul cu indicele 0 pana la elementul cu indicele n 1
daca tabloul are n elemente. In cazul n care vom accesa un element al
tabloului care nu exista vom observa faptul ca se va produce o eroare
si executia programului se va opri.
Orice tablou are o data membra length care returneaza lungimea tabloului
respectiv, ca n exemplul urmator:
int[] a=new int[10];
System.out.println(a.length); //afiseaza valoarea 10
Pentru a accesa elementele unui tablou vom folosi, la fel ca si n C/C++
numele tabloului si numarul elementului n cadrul tabloului:
nume[indice];
Tipul tablou este un tip referinta ceea ce nseamna ca variabila cu ajutorul
careia am declarat tabloul, de fapt retine adresa zonei efective de memorie
unde se retin elementele tabloului.
Acesta este motivul pentru care daca vom transmite ca parametru un
tablou la o metoda modificarile efectuate asupra elementelor acestuia se vor
pastra.

18CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE IN JAVA


Probleme rezolvate
Problema 1: Se citeste de la tastatura un sir de numere ntregi. Sa se
sorteze crescator elementele sirului si sa se afiseze sirul astfel obtinut.
Rezolvarea acestei probleme ar putea arata astfel:
import java.util.*;
public class SortareSir
{
public static void main(String args[])
{
Scanner s=new Scanner(System.in);
System.out.print("n=");
int n=s.nextInt();
int[] a=new int[n];
for(int i=0;i<a.length;i++)
{
System.out.print("a["+i+"]=");
a[i]=s.nextInt();
}
sortare(a);
afisare(a);
}
static void sortare(int[] a)
{
for(int i=0;i<a.length-2;i++)
for(int j=i+1;j<a.length;j++)
if(a[i]>a[j])
{
int aux=a[i];
a[i]=a[j];
a[j]=aux;
}
}
static void afisare(int[] a)
{
for(int i=0;i<a.length;i++)
System.out.print(a[i]+" ");

2.8. VECTORI SI MATRICI

19

}
}
Observatii:
Se putea realiza afisarea sirului si n alt mod, folosind capabilitatile
oferite de JDK 1.6:
for(int x:a)
System.out.println(x+" ");
Pentru lucrul cu siruri avem clasa Arrays care are definite cateva
metode foarte utile. Astfel, puteam sorta elementele sirului folosind
urmatorul cod:
Arrays.sort(a);
Se recomanda consultarea API-ului Java pentru studierea celorlalte
metode utile pentru lucrul cu siruri.
Problema 2: Sa se scrie o metoda care primeste ca parametru un sir de
numere ntregi si returneaza sirul obtinut prin inserarea ntre fiecare 2 elemente a sumei lor.
Metoda ar putea arata astfel:
static int[] generareSir(int[] a)
{
int[] b=new int[2*a.length-1];
for(int i=0;i<a.length;i++)
b[2*i]=a[i];
for(int i=1;i<b.length;i+=2)
b[i]=b[i-1]+b[i+1];
return b;
}
Problema 3: Sa se scrie o metoda care primeste ca parametru un sir a,
o pozitie poz si o valoare val si returneaza ntr-un sir, vectorul obtinut prin
inserarea elementului val pe pozitia poz n sirul initial.
In Java exista o metoda prin care putem copia elemente dintr-un sir in
alt sir elegant si eficient, folosind metoda arraycopy a clasei System. Antetul
acestei metode este urmatorul:

20CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE IN JAVA


public static native void arraycopy(Object src, int srcPosition,
Object dst, int dstPosition, int length)
Efectul apelului acestei metode consta n faptul ca se copiaza din sirul
src, ncepand de la pozitia srcPosition, length elemente, care se vor plasa n
sirul dst ncepand de la pozitia dstPosition.
Aceasta metoda va fi folosita la rezolvarea problemei:
static int[] inserare(int[] a, int poz, int val)
{
int[] b=new int[a.length+1];
System.arraycopy(a, 0, b, 0, poz);
b[poz]=val;
System.arraycopy(a, poz, b, poz+1, a.length-poz);
return b;
}
Metoda poate fi apelata scriind cod precum urmatorul:
a=inserare(a, 2, 3);

2.8.2

Matrici

In Java, o matrice este retinuta sub forma unui sir de siruri. O matrice
se poate declara si initializa n trei feluri.
Cea mai simpla varianta este sa declaram matricea si sa alocam memorie
pentru ea ca n exemplul urmator:
int[][] a=new int[nrLinii][nrColoane];
Matricile se pot si initializa direct. Prezentam modul n care se poate
initializa o matrice cu 2 linii si 3 coloane:
int[][] a= {{2, 3, 4},
{1, 4, 2}};
Totusi, n Java o matrice poate avea un numar variabil de elemente pe
fiecare linie:
int[][] a= {{2, 3},
{1, 4, 2, 5}};
In acest caz, numarul de linii ale matricii este a.length iar numarul de
coloane ale liniei i este a[i].length.
Daca dorim sa citim o astfel de matrice de la tastatura putem proceda
astfel:

2.8. VECTORI SI MATRICI

21

int[][] a=new int[nrLinii][];


for(int i=0;i<a.length;i++)
/
// citire dimensiune m
a[i]=new int[m];
for(int j=0;j<a[i].length;j++)
//citire element a[i][j]
/
Cel mai bine se va vedea cum se lucreaza cu astfel de matrici n exemplele
urmatoare.
Problema 1 Se citeste de la tastatura o matrice a, patratica de ordin n.
Sa se verifice daca toate elementele de pe diagonala principala sunt mai mari
decat suma elementelor de pe liniile corespunzatoare lor.
Rezolvarea problemei ar putea arata astfel:
import java.util.*;
public class VerificareMatrice
{
public static void main(String args[])
{
Scanner s=new Scanner(System.in);
System.out.print("n=");
int n=s.nextInt();
int[][] a=new int[n][n];
for(int i=0;i<a.length;i++)
for(int j=0;j<a.length;j++)
{
System.out.print("a["+i+"]["+j+"]=");
a[i][j]=s.nextInt();
}
if(verificare(a))
System.out.println("Se respecta conditia");
else
System.out.println("Nu se respecta conditia");
}
static boolean verificare(int[][] a)
{
for(int i=0;i<a.length;i++)

22CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE IN JAVA


{
int s=0;
for(int j=0;j<a[i].length;j++)
s+=a[i][j];
if(2*a[i][i]<=s)
return false;
}
return true;
}
}
Problema 2 Se citeste o matrice a cu n linii si numar variabil de elemente
pe fiecare linie. Sa se calculeze si sa se afiseze minimul dintre maximele de
pe fiecare linie.
Rezolvarea ar putea arata n felul urmator:
import java.util.*;
public class MatriceNeregulata
{
public static void main(String args[])
{
Scanner s=new Scanner(System.in);
System.out.print("Numarul de linii: ");
int n=s.nextInt();
int[][] a=new int[n][];
for(int i=0;i<a.length;i++)
{
System.out.print("Numarul de coloane ale liniei "+i+":");
int m=s.nextInt();
a[i]=new int[m];
for(int j=0;j<a[i].length;j++)
{
System.out.print("a["+i+"]["+j+"]=");
a[i][j]=s.nextInt();
}
}
System.out.println(minMax(a));
}
static int maximSir(int[] a)
{

2.9. SIRURI DE CARACTERE

23

int max=a[0];
for(int i=1;i<a.length;i++)
if(a[i]>max)
max=a[i];
return max;
}
static int minMax(int[][] a)
{
int min=maximSir(a[0]);
for(int i=1;i<a.length;i++)
if(maximSir(a[i])<min)
min=maximSir(a[i]);
return min;
}
}

2.9

S
iruri de caractere

Avem mai multe clase care ne ajuta sa lucram cu siruri de caractere.


Cele mai cunoscute si care vor fi prezentate n continuare sunt: String,
StringBuf f er si StringT okenizer.

2.9.1

Clasa String

In principiu, pentru a retine un sir de caractere se foloseste clasa String.


Aceasta retine un sir de caractere de tip char.
Un sir de caractere se poate initializa astfel:
String nume="Adi";
Pentru a vedea lungimea unui sir de caractere folosim metoda length() a
clasei String astfel:
System.out.println(nume.length());
Efectul acestei instructiuni va fi afisarea valorii 3.
Se poate obtine caracterul de pe pozitia i al unui sir de caractere folosind
metoda charAt() a clasei String care are urmatorul antet:
char charAt(int index)

24CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE IN JAVA


Pentru a compara daca doua siruri de caractere s1 si s2 sunt egale, nu
folosim operatorul == ci mai degraba metoda equals ca n exemplul urmator:
if (s1.equals(s2))
...
Principalele metode pentru lucrul cu siruri de caractere sunt:
char charAt(int index)
returneaza caracterul de pe pozitia index
int compareTo(String other)
returneaza o valoare mai mica decat 0 daca sirul de caractere asupra
caruia se face operatia este mai mic decat cel transmis ca parametru,
0 daca sunt egale si o valoare mai mare decat 0 n celelalte cazuri.
boolean endsWith(String suffix)
returneaza true daca sirul de caractere se termina cu caracterele sirului
suf f ix.
boolean equals(Object other)
boolean equalsIgnoreCase(String other)
int indexOf(String str)
int indexOf(String str, int fromIndex)
int lastIndexOf(String str)
int length()
boolean startsWith(String prefix)
String substring(int beginIndex, int endIndex)
String toLowerCase()
String toUpperCase()
String trim()

2.9. SIRURI DE CARACTERE

25

Trebuie spus ca pentru lucrul cu siruri de caractere exista numeroase


alte metode ale clasei String. Pentru a le putea cunoaste pe toate este
recomandata studierea API-ului Java.
O operatie foarte importanta n lucrul cu siruri de caractere este concatenarea care am vazut n mai multe exemple ca se face cu operatorul +.
Vom prezenta n continuare cateva exemple de lucru cu siruri de caractere:
Problema 1 Se citeste de la tastatura un sir de caractere. Sa se verifice
daca acest sir are aspect de palindrom.

import java.util.*;
public class StringPalindrom
{
static boolean palindrom(String s)
{
for(int i=0;i<=(s.length()-2)/2;i++)
if(s.charAt(i)!=s.charAt(s.length()-i-1))
return false;
return true;
}
public static void main(String args[])
{
Scanner s=new Scanner(System.in);
System.out.print("Sirul de caractere: ");
String str=s.nextLine();
if(palindrom(str))
System.out.println("Are aspect de palindrom");
else
System.out.println("Nu are aspect de palindrom");
}
}
Problema 2 Se citeste un sir de siruri de caractere. Sa se sorteze crescator
sirul si sa se afiseze sirul astfel obtinut.

import java.util.*;
public class SortareStringuri
{
public static void main(String args[])

26CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE IN JAVA


{
Scanner s=new Scanner(System.in);
System.out.print("n=");
int n=s.nextInt();
s.nextLine();
String[] str=new String[n];
for(int i=0;i<str.length;i++)
{
System.out.print("str["+i+"]=");
str[i]=s.nextLine();
}
sortare(str);
afisare(str);
}
static void sortare(String str[])
{
for(int i=0;i<str.length-1;i++)
for(int j=i+1;j<str.length;j++)
if(str[i].compareTo(str[j])>0)
{
String aux=str[i];
str[i]=str[j];
str[j]=aux;
}
}
static void afisare(String[] str)
{
for(String s:str)
System.out.print(s+" ");
}
}

Observatie importanta:
Clasa String este immutable ceea ce nseamna ca niciodata o variabila
de tip String nu se poate modifica. Se poate doar ca ea sa retina o alta
referinta. Daca vrem sa putem modifica un sir de caractere folosim clasa
StringBuf f er.

2.9. SIRURI DE CARACTERE

2.9.2

27

Clasa StringBuffer

Problema 1 Se citeste un sir de caractere. Sa se nlocuiasca fiecare vocala


cu urmatorul caracter din alfabet si sa se afiseze sirul obtinut.
import java.util.Scanner;
public class InlocuireVocale
{
static boolean vocala(char c)
{
/*switch(Character.toLowerCase(c))
{
case a:
case e:
case i:
case o:
case u: return true;
}
return false;*/
c=Character.toLowerCase(c);
return ("aeiou".indexOf(c)>=0);
}
static String inlocuireVocaleBun(String s)
{
StringBuffer sb=new StringBuffer(s);
for(int i=0;i<sb.length();i++)
if(vocala(sb.charAt(i)))
sb.setCharAt(i,(char)(s.charAt(i)+1));
return sb.toString();
}
static String inlocuireVocaleNuAsaBun(String s)
{
String s2="";
for(int i=0;i<s.length();i++)
if(vocala(s.charAt(i)))
s2+=(char)(s.charAt(i)+1);
else
s2+=s.charAt(i);
return s2;

28CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE IN JAVA


}
public static void main(String args[])
{
Scanner s=new Scanner(System.in);
System.out.print("Sirul de caractere: ");
String str=s.nextLine();
System.out.println("Sirul transformat: "+inlocuireVocaleBun(str));
}
}
Problema s-ar mai fi putut rezolva si n alt mod, si anume prin folosirea
metodei append() a clasei StringBuffer:
static String inlocuireVocaleNuAsaBun2(String s)
{
StringBuffer s2=new StringBuffer("");
for(int i=0;i<s.length();i++)
if(vocala(s.charAt(i)))
s2.append(s.charAt(i)+1);
else
s2.append(s.charAt(i));
return s2.toString();
}

2.9.3

Clasa StringTokenizer

Clasa StringT okenizer se foloseste pentru a sparge un sir de caractere


n mai multi tokeni. Exemplificam folosirea ei printr-o problema:
Problema 1 Se citeste o expresie matematica. Sa se afiseze toti operanzii
din aceasta expresie.

import java.util.*;
public class AfisareOperanzi
{
public static void main(String args[])
{
Scanner s=new Scanner(System.in);
System.out.print("Expresia matematica: ");
String expr=s.nextLine();

2.9. SIRURI DE CARACTERE

29

StringTokenizer st=new StringTokenizer(expr,"+-*/% ");


while(st.hasMoreTokens())
System.out.println(st.nextToken());
}
}

2.9.4

Expresii regulate n Java

Una din cele mai importante facilitati legate de lucrul cu siruri de caractere n Java o constituie lucrul cu expresii regulate.
Acesta ne permite sa ne definim un anumit sablon si sa vedem daca sirul
de caractere respecta acel sablon sau sa gasim toate aparitiile ntr-un text a
unor subsiruri de caractere care verifica acel sablon.
Problema 1 De exemplu, pentru a verifica daca un sir de caractere este
alcatuit din exact 3 cifre, prima fiind diferita de zero putem proceda astfel:
import java.util.regex.Pattern;
import java.util.*;
public class ExpresiiRegulate {
public static void main(String args[]) {
Scanner s = new Scanner(System.in);
System.out.print("Introduceti sirul");
String str=s.nextLine();
if(Pattern.matches("[\\d&&[^0]]\\d\\d", str))
System.out.println("Respecta");
else
System.out.println("Nu respecta");
}
}
Tabela 2.2: Clase caracter si clase predefinite
Reprezentare
[abc]
[abc]
[a-zA-Z]
[a-d[m-p]]
[a-z&&[def]]
[a-z&&[bc]]
[a-z&&[m-p]]

Semnificatie
a, b sau c
orice caracter cu exceptia lui a, b sau c
caractere ntre a-z si A-Z
caractere ntre a-d si m-p
d, e sau f (intersectie)
de la a la z, cu exceptia lui b si c
de la a la z, cu exceptia lui m-p

30CAPITOLUL 2. STRUCTURI FUNDAMENTALE DE PROGRAMARE IN JAVA

Reprezentare
.
\d
\D
\s
\S
\w
\W

Tabelul 2.2 (continuare)


Semnificatie
Orice caracter
O cifra: [0-9]
Un caracter care nu e cifra: [0-9]
Un caracter spatiu: [ \t\n\r]
Un caracter non-spatiu [\s]
Un caracter dintr-un cuvant: [a-zA-Z 0-9]
Un caracter care nu face parte dintr-un cuvant: [\w]

Am prezentat n tabelul anterior principalele clase de caractere si clase


predefinite care sunt definite pentru lucrul cu expresii regulate n Java.
Se poate ca pentru un astfel de caracter sa specificam numarul exact de
aparitii n text, folosind cuantificatorii prezentati n tabelul urmator.
Problema 2 Sa se scrie fragmentul de cod care verifica daca un sir de
caractere citit de la tastatura este alcatuit din concatenarea a ntre 2 si 4
numere de cate doua cifre, prima fiind diferita de 0.
if(Pattern.matches("([\\d&&[^0]]\\d)2,4", str))
System.out.println("Respecta");
else
System.out.println("Nu respecta");

Tabela 2.3: Cuantificatori pentru lucrul cu expresii regulate


Reprezentare
X?
X*
X+
X{n}
X{n,}
X{n,m}

Semnificatie
X, odata sau deloc
X, de zero sau mai multe ori
X, odata sau de mai multe ori
X, exact de n ori
X, de cel putin n ori
X, de cel putin n ori dar nu mai mult de m ori

CAPITOLUL

Concepte si principii n POO

Programarea obiect orientata (POO) este o paradigma de programare


care foloseste obiecte precum si interactiunile ntre ele pentru a construi
aplicatii si programe. Tehnicile de programare specifice POO se bazeaza pe
notiuni precum: ncapsulare, modularitate, polimorfism si mostenire.
Programarea obiect orientata a ajuns n prim plan la nceputul anilor 90
odata cu folosirea pe scara larga a limbajelor C++ si Java.
Aproape toate limbajele de programare importante din zilele noastre
folosesc programarea obiect orientata. Este cazul limbajelor C++, Java,
C#, Visual Basic, Object Pascal si lista ar putea continua.
POO poate fi vazuta ca o colectie de obiecte care coopereaza, spre deosebire de programarea procedurala care era vazuta ca o grupare de task-uri de
efectuat (subrutine). In POO, fiecare obiect este capabil de a primi mesaje,
de a procesa date si de a trimite mesaje la alte obiecte.

3.1

Concepte n programarea obiect orientat


a

Clasa defineste caracteristicile comune mai multor lucruri (obiecte), incluzand partea informationala a acestora (atribute, campuri sau proprietati)
si comportamentul lor (lucrurile pe care le pot face - reprezentate prin metode).
Clasa este un tip de date deoarece, la fel ca si tipul float are o parte
de informatie precum si o parte de comportament. Practic, lucrul cu clase
permite definirea de noi tipuri de date n loc de a folosi doar tipurile care
sunt deja existente n limbaj.
31

CAPITOLUL 3. CONCEPTE SI PRINCIPII IN POO

32

Astfel se mbogateste limbajul prin adaugarea de tipuri de date necesare programatorului. Limbajul nu numai ca permite acest lucru dar l si
ncurajeaza puternic.
Odata creata o clasa se pot crea oricate obiecte de tipul acelei clase,
obiecte care vor corespunde obiectelor din domeniul problemei.
Clasele ofera de asemenea modularitate si structura ntr-un program bazat
pe programarea obiect orientata.
Obiectul este un exemplar al unei clase (o instanta a unei clase).
Instant
a este un obiect creat la momentul rularii pe baza unei clase.
Setul de valori ale atributelor unui obiect reprezinta starea acelui obiect.
Metodele se refera la abilitatile unui obiect. In cadrul unui program
prin folosirea unei metode este afectat un singur obiect, cel asupra caruia
este apelata metoda respectiva (exceptie fac aici metodele statice).
Transmiterea de mesaje este procesul prin care un obiect trimite date
altui obiect sau i cere altui obiect sa apeleze o metoda.
In urma cu ceva timp un programator descria limbajul Smalltalk astfel:
Totul este un obiect (Un obiect este o variabila mai deosebita care are
atat stare cat si comportament)
Un program este alcatuit dintr-un set de obiecte care-si spun unul altuia
ce sa faca.
Fiecare obiect are propria sa memorie alcatuita eventual din alte obiecte.
Fiecare obiect are un tip (este o instanta a unei clase).
Toate obiectele de acelasi tip pot primi aceleasi mesaje (chiar si n
contextul mostenirii).
Dupa cum putem usor observa toate aceste lucruri sunt valabile si acum
n contextul limbajului Java.

3.2

Principii ale program


arii obiect orientate

Abstractizarea reprezinta simplificarea realitatii complexe prin modelarea de clase potrivite problemei de rezolvat precum si lucrul la cel mai
potrivit nivel de mostenire pentru un anumit aspect al unei probleme.
Toate limbajele de programare ofera un anumit grad de abstractizare.
Complexitatea problemei de abstractizat depinde foarte mult de tipul abstractizarii si de calitatea acesteia.


3.2. PRINCIPII ALE PROGRAMARII
OBIECT ORIENTATE

33

Limbajul de asamblare este o mica abstractizare fata de limbajul cod


masina.
Urmatoarele limbaje care au aparut sunt abstractizari ale limbajului de
asamblare. Ne referim aici la limbaje gen Basic si C.
Aceste limbaje reprezinta o mbunatatire majora fata de limbajul de
asamblare dar tot ne fac sa gandim n termen de structura a calculatorului, nu numai n termeni referitori la domeniul problemei de rezolvat.
Astfel, avem programe destul de greu de scris si greu de mentinut.
Abordarea obiect-orientata ofera programatorului instrumente pentru a
reprezenta elementele din spatiul problemei.
POO ne permite sa exprimam problema n termenii domeniului corespunzator ei. Vom folosi obiecte care au fiecare o stare si un comportament.
Incapsularea se refera la detaliile functionale ale unei clase din punct
de vedere al obiectelor care trimit mesaje catre acea clasa. Incapsularea se
obtine prin specificarea clara a claselor care pot folosi membrii unui obiect.
Rezultatul consta n faptul ca fiecare obiect expune unei anumite clase o
anumita interfata - acei membri accesibili acelei clase.
Motivatia folosirii ncapsularii o constituie prevenirea folosirii de catre
clienti a acelor parti ale unei clase care s-ar putea sa se schimbe n viitor.
Astfel, metodele sunt publice, private sau private, determinand daca sunt
valabile tuturor claselor, numai claselor derivate sau numai n clasa n care
sunt definite.
Din punctul de vedere al modului n care folosesc clasele programatorii
se mpart n doua mari categorii:
cei care creeaza clasa - acestia sunt cei care cunosc toate detaliile despre
clasa respectiva, fiind cei care realizeaza crearea unui nou tip de date
cei care sunt doar clienti ai clasei - acestia sunt acei programatori care
doar folosesc clasa respectiva nefiind interesati de modul n care clasa
este implementata efectiv, ci doar de serviciile pe care aceasta le ofera.
Acestea sunt specificate prin interfat a clasei respective.
Putem practic afirma ca interfata unei clase reprezinta un contract ntre
prima categorie de programatori si ceea de-a doua, n sensul n care creatorii clasei le ofera o asigurare celor care folosesc acea clasa ca metodele din
interfata nu se vor modifica n viitor chiar daca implementarile vor fi altele.
Mostenirea se refera la anumite clase care partajeaza anumite informatii
precum si comportament. Subclasele sunt versiuni mai specializate ale unor
clase, care mostenesc atribute si comportament de la clasele lor parinte si isi
introduc propriile lor atribute si metode.
Fiecare subclasa si poate altera caracteristicile mostenite.

CAPITOLUL 3. CONCEPTE SI PRINCIPII IN POO

34

In unele limbaje, nu si n Java, putem avea mostenire multipla. Aceasta


nseamna sa mostenesti atribute si comportament din mai multe clase de
baza.
Polimorfismul permite programatorului sa trateze metodele claselor
derivate ca si pe metodele claselor lor parinte. Mai exact, polimorfismul
reprezinta capabilitatea obiectelor care apartin unor tipuri de date diferite
sa raspunda la apeluri de metode cu acelasi nume, fiecare corespunzatoare
unui comportament specific tipului lor.

3.3

Solutii pentru utilizarea eficienta a principiilor POO

Exista un anumit numar de provocari pe care un dezvoltator le ntalneste


n mod regulat atunci cand face designul unui sistem obiect orientat. Exista
de asemenea si solutii acceptate la scara larga pentru aceste probleme.
Cele mai cunoscute astfel de solutii sunt cele oferite de sabloanele de
proiectare documentate n GoF (cartea Design patterns - Elements of Reusable
Object Oriented Software scrisa de Gamma, Helm, Johnsson si Vlissides).
Acestea pot fi vazute ca niste solutii generale, aplicabile n mod repetat unor
probleme care tot apar n designul de software.
Toate aceste sabloane de proiectare au la baza cateva idei foarte importante:
1. Cand un anumit lucru variaza acel lucru trebuie ncapsulat. Aceasta
nseamna ca atunci cand ntalnim o anumita parte a sistemului soft,
care este foarte probabil sa se modifice n viitor, aceasta ar trebui izolata n sensul n care sa se specifice o interfata cat mai stabila a ei,
implementarea putand fi modificata ulterior.
2. Folosirea compozitiei n dauna mostenirii. Chiar daca mostenirea este
un instrument foarte puternic, adesea se abuzeaza de ea, n situatii n
care ar fi mai bine daca nu ar fi folosita. Decizia de a folosi mostenirea
trebuie sa fie o decizie bine argumentata luata numai dupa ce s-au
studiat si alternativele.
general este mai bine sa lucram pe interfete nu pe implementari.
3. In
Acest lucru este necesar deoarece daca la un moment dat se alege sa se
lucreze cu o alta implementare si ne-am bazat pe prima implementare,
va trebui sa schimbam codul. De asemenea, lucrul pe interfete n dauna
mostenirii favorizeaza utilizarea polimorfismului.

3.3. SOLUT
II PENTRU UTILIZAREA EFICIENTA A PRINCIPIILOR POO35
O alta idee extrem de sugestiva este cea enuntata de cunoscutul informatician Bertrand Meyer si care poarta numele de Principiul Open-Closed :
Toate entitatile software trebuie sa fie deschise pentru extensie si nchise
pentru modificare.
Atunci cand o schimbare ntr-o aplicatie conduce la numeroase alte schimbari
n cascada n alte zone ale aplicatiei, aceste este un semn ca avem de-a face
cu un design slab. Programul devine fragil, greu de stapanit si imprevizibil.
Acest principiu spune ca trebuie sa construim module care nu se schimba,
n sensul n care daca vrem sa adaugam noi facilitati putem scrie cod nou,
dar fara a modifica codul deja existent.
In mod evident, nu se poate ca un modul sa fie complet nchis. In anumite situatii este necesar sa modificam cod existent. Totusi aplicatia trebuie
gandita astfel ncat sa permita acest lucru ntr-un mod cat mai flexibil si
premeditat.
Exista nca doua principii importante referitoare la designul modulelor
ntr-o aplicatie (ele fac parte dintr-o suita mai mare de astfel de sfaturi care
poarta numele GRASP - General Responsabilities Assignmnet Software Patterns):
1. Intre componentele unui modul (clasa, pachet, etc.) trebuie sa existe
o coeziune nalta. Aceasta nseamna ca nu este bine, spre exemplu, ca
ntr-o clasa sa avem mai multe informatii si metode ntre care nu exista
legaturi stranse. Exista metrici specializate pentru masurarea acestei
caracteristici.
2. Intre diversele parti componente ale unei aplicatii trebuie sa existe o
cuplare slaba. Aceasta nseamna ca atunci cand modificam un modul,
modificarile asupra lui sa nu genereze modificari n cascada asupra altor
module.
In final vom spune cateva cuvinte despre un alt principiu foarte important
referitor la programarea obiect orientata si anume principiul numit Inversion
of Control (IoC). Acesta este un principiu abstract care descrie un anumit
aspect referitor la designul sistemelor soft n care fluxul evenimentelor este
invers fata de cursul normal ntr-o aplicatie traditionala.
El a fost asemanat cu principiul numit Hollywood Principle: dont call
us, well call you.
Se poate spune ca IoC este un mod de a scrie software n care un cod
generic si reutilizabil poate fi aplicat la diverse probleme.
Exista doua moduri de lucru care au la baza principiul IoC:
programarea orientata pe evenimente (event-driven programming)

36

CAPITOLUL 3. CONCEPTE SI PRINCIPII IN POO


injectarea de dependenta (dependency injection) - aceasta din urma
nseamna, spre exemplu, ca avem o componenta care deja are definit
un schelet de utilizare, dar care pentru a fi utila are nevoie ca noi sa
injectam un anumit comportament.

CAPITOLUL

Obiecte si clase

In limbajul Java pentru a crea pana si cea mai simpla aplicatie este nevoie
sa cream macar o clasa.
Dupa cum vom vedea n continuare o aplicatie Java este alcatuita, n
principiu, din mai multe clase care colaboreaza ntre ele. De obicei exista o
clasa care contine si o metoda main() si care constituie punctul de intrare n
aplicatie, clasa care foloseste alte clase la rularea aplicatiei.
Se poate ntampla ca o aplicatie sa aiba mai multe metode main(),
situatie n care, la rularea programului putem specifica care este clasa a
carei metoda se va executa.

4.1

Definirea unei clase

Pentru definirea unei clase se foloseste urmatoarea sintaxa:


[modificatori] class NumeClasa [extends ClasaBaza]
[implements Interfata1[, Interfata2...]]
{
//definire atribute clasa
//definire constructori
//definire metode clasa
}
37

38

CAPITOLUL 4. OBIECTE SI CLASE

Modificatorii aplicabili unei clase sunt urmatorii: public, abstract, final


si static.
Modificatorul public poate fi folosit pentru a specifica ca o clasa poate
fi folosita peste tot n aplicatie daca este inclus pachetul n care ea este
declarata sau clasa care o foloseste face parte din acelasi pachet. Daca acest
modificator lipseste atunci nseamna ca avem vizibilitate la nivel de pachet
(numai clasele din acelasi pachet pot utiliza aceasta clasa).
Modificatorul abstract specifica faptul ca avem de-a face cu o clasa abstracta. Acest lucru nseamna ca nu se poate instantia aceasta clasa (nu se
pot crea obiecte de acest tip). Vom discuta mai mult despre acest modificator
n contextul mostenirii.
Modificatorul f inal aplicat unei clase specifica faptul ca aceasta clasa nu
mai poate fi derivata.
Modificatorul static nu se poate aplica decat n contextul claselor imbricate. Vom studia acest lucru ntr-o sectiune special dedicata acestor clase.
Despre folosirea lui extends si implements vom discuta atunci cand vom
prezenta mostenirea si modul n care o clasa poate implementa interfete.

4.2

Declararea atributelor unei clase

In primul rand trebuie facuta clar distinctia ntre atribute si variabile


locale.
O variabila locala este definita n interiorul unei metode sau a unui constructor.
Un atribut al clasei este definit n afara oricarei metode. Dupa cum vom
vedea n continuare atributele pot fi de doua feluri: statice (atribute ale
clasei) si nestatice (atribute ale instantei).
Un exemplul din care se poate vedea acest lucru este urmatorul:
class Stiva
{
public final static int MAX=100; //atribut static
private int nrElemente; //atribut nestatic
public void push(Object o)
{
int a=0; //variabila locala
....
}
}

4.2. DECLARAREA ATRIBUTELOR UNEI CLASE

39

Pentru a declara (si eventual initializa explicit) un atribut n Java se


foloseste urmatoarea sintaxa:
[modificatori] tip numeAtribut[=val implicita][,...]
Daca nu initializam n mod explicit un atribut el are niste valori implicite:
0 pentru tipurile ntregi, 0.0 pentru tipurile reale, boolean pentru caractere
si null pentru obiecte.
Modificatorii aplicabili atributelor n Java sunt: public, private, protected, final, static, transient si volatile.

4.2.1

Modificatorii de vizibilitate

Modificatorul public aplicat unui atribut semnifica faptul ca acel atribut


este accesibil de oriunde, folosind notatia cu punct, ca n exemplul urmator:
class Complex
{
public double re;
public double im;
public Complex(double r, double i)
{
re=r;
im=i;
}
//alte atribute si metode
}
class UseComplex
{
public static void main(String args[])
{
Complex c=new Complex(3,4);
c.re=6; //se poate folosi atributul din afara clasei
}
}
Modificatorul private semnifica faptul ca un atribut este accesibil numai
din interiorul clasei n care este definit. Astfel, exemplul anterior de folosire
a atributului nu ar fi functionat daca s-ar fi declarat atributul ca fiind privat.

40

CAPITOLUL 4. OBIECTE SI CLASE

Modificatorul protected specifica faptul ca un atribut este vizibil atat n


clasele derivate din clasa n care este declarat cat si n toate clasele care fac
parte din acelasi pachet cu clasa n care a fost declarat atributul.
Daca nu este folosit nici un modificator de vizibilitate se presupune implicit ca avem vizibilitate la nivel de pachet (package). Acest lucru nseamna
ca acel atribut este accesibil doar n interiorul pachetului n care este definita
clasa respectiva.

4.2.2

Modificatorul final

Un atribut care este declarat ca final nu mai poate sa-si schimbe valoarea
ntr-o atribuire ulterioara.
Acest lucru nseamna pentru tipurile primitive ca valoarea lor este constanta, iar pentru tipurile referinta ca nu se mai poate modifica adresa catre
obiectul catre care pointeaza.
Iata si cateva exemple:
class Test
{
final int a=100;
final String s="test";
final int[] b={2, 4, 5};
void metoda()
{
//genereaza eroare deoarece nu se poate modifica un atribut de
//un tip primitiv declarat constant
a=1000;
//va genera eroare deoarece cand se modifica valoarea unui
//obiect de tip String automat se aloca memorie pt un nou sir
s="alt test";
//functioneaza deoarece nu se modifica adresa retinuta de sir
b[2]=4;
//nu functioneaza deoarece se modifica adresa retinuta de sir
b=new int[5];
}
}

4.2. DECLARAREA ATRIBUTELOR UNEI CLASE

4.2.3

41

Modificatorul static

In mod implicit, daca modificatorul static nu este folosit asupra unui


atribut, acel atribut poate avea valori diferite pentru obiecte diferite. Astfel,
daca avem definita o clasa P ersoana care are un atribut nume, acest atribut
va avea valori diferite pentru fiecare obiect de tipul P ersoana.
Modificatorul static aplicat unui atribut nseamna ca pentru acel atribut
se aloca memorie o singura data indiferent cate obiecte de tipul clasei avem.
Spunem despre acel atribut ca este un atribut al clasei, practic acea valoare
unica nu tine de obiecte ci de clasa.
Prezentam si un exemplu din care se poate vedea modul n care se pot
folosi atributele statice si nestatice. Remarcati faptul ca un atribut static
poate fi accesat si prin intermediul numelui clasei, mai degraba decat prin
numele unui obiect.

class Test
{
public static int atributStatic;
public int atributNestatic;
}
public class ExempluStatic
{
public static void main(String args[])
{
Test t1=new Test();
Test t2=new Test();
t1.atributNestatic=3;
t2.atributNestatic=6;
System.out.println(t1.atributNestatic+" "+t2.atributNestatic);
//afiseaza valorile 3 si 6
t1.atributStatic=1;
t2.atributStatic=2;
System.out.println(t1.atributStatic+" "+Test.atributStatic);
//afiseaza valorile 2 si 2
}
}

42

CAPITOLUL 4. OBIECTE SI CLASE

4.2.4

Modificatorii transient si volatile

Modificatorul transient aplicat unui atribut specifica faptul ca acel atribut


nu este un atribut care ar trebui pastrat pe suport extern atunci cand se va
serializa o instanta a clasei.
Modificatorul volatile este folosit pentru atribute care pot fi accesate
simultan din mai multe fire de executie. Acest modificator avertizeaza compilatorul ca asupra lor nu trebuie facute anumite optimizari si ca de fiecare
data trebuie calculata valoarea lor actualizata.

4.3

Declararea metodelor unei clase

Declararea unei metode n Java are urmatoarea sintaxa:


[modificatori] tip returnat numeMetoda( parametri)
[throws Exceptie1[, Exceptie2[...]]]
{
//corpul metodei
}
Ca o prima observatie importanta, trebuie spus ca o clasa poate avea mai
multe metode cu acelasi nume. Totusi este evident nevoie ca metodele sa
difere macar prin tipul sau numarul parametrilor transmisi la metoda.
Modificatorii aplicabili metodelor n Java sunt: public, private, protected,
final, static, abstract, native si synchronized.
Nu vom mai prezenta n cele ce urmeaza modificatorii de vizibilitate
public, private si protected deoarece ei au aceeasi semnificatie ca si n cazul
declararii atributelor.
Modificatorul f inal aplicat unei metode specifica faptul ca acea metoda
nu mai poate fi redefinita ntr-o clasa derivata. Mai multe despre acest lucru
n capitolul dedicat mostenirii.
Modificatorul abstract aplicat unei metode specifica faptul ca acea metoda
exista pentru tipul respectiv, dar nu se cunoaste nca implementarea ei. Ea
va fi implementata ntr-o clasa derivata.
Modificatorul synchronized aplicat unei metode specifica faptul ca apelul
asupra acelei metode trebuie sincronizat. Acest lucru se foloseste n cazul
aplicatiilor care utilizeaza fire de executie.
Vom prezenta n subsectiunea urmatoare rolul modificatorului static asupra
metodelor.
Nu vom prezenta deocamdata facilitatile care tin de tratarea exceptiilor.

4.3. DECLARAREA METODELOR UNEI CLASE

4.3.1

43

Modificatorul static

Prin specificarea unei metode ca fiind o metoda statica, practic, i comunicam calculatorului ca acea metoda nu a fost creata pentru apelarea ei
asupra obiectelor ci mai degraba avem o metoda a unei clase, care poate
folosi numai atribute si metode statice ale clasei.
Acesta este motivul pentru care pana acum, de fiecare data cand scriam
o metoda trebuia ca acea metoda sa fie statica (din metoda main() care este
statica nu se pot apela decat metode statice).
Prezentam n continuare un exemplu de clasa pentru care are sens sa
definim metode statice:
class Mathematica
{
private Mathematica()
{
}
public static int max(int a, int b)
{
return a>=b?a:b;
}
public static int abs(int a)
{
return a>=0 ? a : -a;
}
}
public class Testare
{
public static void main(String args[])
{
System.out.println(Mathematica.max(3,4));
}
}

4.3.2

Transmiterea parametrilor la metode

In limbajul Java toti parametri care se transmit unei metode se


transmit prin valoare.

44

CAPITOLUL 4. OBIECTE SI CLASE

Acest lucru nseamna ca modificarile asupra lor nu se pastreaza. Totusi


este important de nteles ce se transmite de fapt unei metode.
In cazul n care se transmite o variabila de un tip primitiv se transmite
de fapt valoarea ei si modificarile asupra acesteia nu se pastreaza.
Daca se transmite un sir, matrice sau un alt obiect, atunci de fapt se
transmite adresa obiectului respectiv. Acest lucru nseamna ca acea adresa
nu se poate modifica. Totusi modific
arile asupra continutului aflat la
aceea adres
a se p
astreaz
a.

4.4

Declararea constructorilor unei clase

Un rol foarte important atunci cand lucram cu clase si obiecte l au


constructorii. Putem sa ne gandim la un constructor ca la o metoda mai speciala (nu se poate zice ca este chiar metoda deoarece pentru un constructor
nu se specifica tipul returnat).
Acestia intervin n momentul n care dorim sa creem noi obiecte, folosind
instructiunea new().
Daca scriem codul:
String s=new String("Rodica");
atunci, pentru variabila s se va apela un constructor al clasei String care
va realiza initializarea tuturor atributelor obiectului s.
Constructorii respecta doua caracteristici importante:
1. Au ntotdeauna acelasi nume cu clasa pentru care se declara.
2. Pentru un constructor nu se specifica nici un tip returnat.
Observatii:
Se poate ntampla ca un constructor sa fie privat, ca n exemplul prezentat la sectiunea precedenta. In acel exemplu, dupa cum am putut observa este imposibila crearea obiectelor de tipul M athematica.
Daca nu avem nici un constructor definit, nu nseamna ca acel constructor lipseste. Specificatia Java prevede faptul ca daca nu definim
nici un constructor, automat este creat unul care initializeaza toate
atributele cu valorile lor implicite (0, 0.0, false si null). Acesta se
numeste constructorimplicit. Acest constructor implicit, dar si orice
alt constructor apeleaza chiar daca nu este specificat, constructorul fara
parametri din clasa de baza.

4.4. DECLARAREA CONSTRUCTORILOR UNEI CLASE

45

Daca pentru o clasa este specificat un constructor, atunci nu va mai fi


generat constructorul implicit. Acest lucru se ntampla indiferent de
tipul parametrilor constructorului creat de programator.
O clasa poate avea mai multi constructori. Acest lucru va fi prezentat
n exemplul urmator.
Prezentam n continuare un exemplu de definire partiala a unei clase
Complex care ar permite lucrul cu numere complexe:
public class Complex
{
private double re, im;
/*public Complex(double r, double i)
{
re=r;
im=i;
}*/
public Complex(double re, double im)
{
this.re=re;
this.im=im;
}
public Complex(double re)
{
this(re, 0);
}
public Complex()
{
this(0);
}
//alte metode...
public static void main(String args[])
{
Complex c1=new Complex(3, 4); //retine pe 3+4i
Complex c2=new Complex(6); //retine pe 6+0i

46

CAPITOLUL 4. OBIECTE SI CLASE


Complex c3=new Complex(); //retine pe 0+0i
}

}
Observatii:
Daca ntr-o metoda numele unui parametru al metodei este acelasi cu
numele atributului clasei, atunci pentru a se putea face distinctia ntre
cele doua se foloseste cuvantul cheie this pentru a specifica un atribut
al clasei.
Se poate ca dintr-un constructor sa se apeleze un alt constructor al
aceleiasi clase. Am facut acest lucru cu ajutorul cuvantului cheie this,
care primeste n paranteza parametri catre acel constructor. Apelul
acesta trebuie sa fie prima instructiune din cadrul constructorului apelant.

4.5

Initializatorul static al unei clase

Initializatorul static al unei clase este codul care este executat prima oara
cand se foloseste o clasa. Acesta este marcat prin cuvantul cheie static urmat
de acolade ntre care se specifica codul de executat.
Iata si un exemplu:
class Test
{
static int max=10;
static
{
max=100;
System.out.println("In initializatorul static");
}
}
public class FolosireInitializatorStatic
{
public static void main(String args[])
{
Test t=new Test();
}
}

4.6. PROBLEME REZOLVATE

4.6

47

Probleme rezolvate

Problema 1 Sa se construiasca o clasa pentru lucrul cu numere complexe


(constructori, metode get/set, implementarea modulului unui numar complex, adunarea, nmultirea, mpartirea si scaderea). Sa se citeasca de la
tastatura n numere complexe. Sa se rezolve urmatoarele subprobleme:
Sa se sorteze crescator elementele sirului n functie de modulul lor si sa
se afiseze pe ecran.
Sa se calculeze suma elementelor sirului.

public class Complex


{
private double re, im;
public Complex(double re, double im)
{
this.re=re;
this.im=im;
}
public Complex(double re)
{
this(re, 0);
}
public Complex()
{
this(0);
}
public double getRe()
{
return re;
}
public void setRe(double re)
{
this.re=re;
}

48

CAPITOLUL 4. OBIECTE SI CLASE

public double getIm()


{
return im;
}
public void setIm(double im)
{
this.im=im;
}
public double getModul()
{
return Math.sqrt(re*re+im*im);
}
public Complex adunare(Complex c)
{
return new Complex(re+c.re, im+c.im);
}
public Complex inmultire(Complex c)
{
return new Complex(re*c.re-im*c.im, re*c.im+im*c.re);
}
public static Complex scadere(Complex c1, Complex c2)
{
return new Complex(c1.re-c2.re, c1.im-c2.im);
}
public static Complex impartire(Complex c1, Complex c2)
{
//de implementat :-) (de catre studenti)
return null;
}
public String toString()
{
if(im<0)
return re+""+im+"*i";

4.6. PROBLEME REZOLVATE


return re+"+"+im+"*i";
}
}
import java.util.*;
public class TestComplex
{
public static void main(String args[])
{
Scanner s=new Scanner(System.in);
System.out.print("n=");
int n=s.nextInt();
Complex[] c=new Complex[n];
for(int i=0;i<c.length;i++)
{
System.out.print("partea reala: ");
double re=s.nextDouble();
System.out.print("partea imaginara: ");
double im=s.nextDouble();
c[i]=new Complex(re, im);
}
sortare(c);
afisare(c);
System.out.println("Suma elementelor sirului: "+suma(c));
}
static void sortare(Complex[] c)
{
for(int i=0;i<c.length-1;i++)
for(int j=i+1; j<c.length;j++)
if(c[i].getModul()>c[j].getModul())
{
Complex aux=c[i];
c[i]=c[j];
c[j]=aux;
}
}

49

50

CAPITOLUL 4. OBIECTE SI CLASE


static void afisare(Complex[] c)
{
for(Complex nr: c)
System.out.print(nr+" ");
System.out.println();
}
static Complex suma(Complex[] c)
{
Complex sum=new Complex();
for(int i=0;i<c.length;i++)
sum=sum.adunare(c[i]);
return sum;
}

Problema 2 Sa se implementeze o clasa pentrul lucrul cu o stiva care


contine numere ntregi si sa se foloseasca aceasta clasa ntr-un program simplu.
Codul pentru aceasta problema ar putea arata astfel:
public class Stack
{
private int[] elemente;
private int top;
public Stack(int dim)
{
top=0;
elemente=new int[dim];
}
public Stack()
{
this(10);
// implicit se creeaza o stiva care are exact
// 10 elemente
}
private void expandare()

4.6. PROBLEME REZOLVATE

51

{
int[] newElemente=new int[2*elemente.length];
System.arraycopy(elemente, 0, newElemente, 0, elemente.length);
elemente=newElemente;
}
private boolean isFull()
{
return top==elemente.length;
}
public boolean isEmpty()
{
return top==0;
}
public void push(int x)
{
if(isFull())
expandare();
elemente[top++]=x;
}
public int top()
{
return elemente[top-1];
}
public int pop()
{
return elemente[--top];
}
public static void main(String args[])
{
Stack s=new Stack();
s.push(3);
s.push(2);
System.out.println(s.top());
}
}

52

CAPITOLUL 4. OBIECTE SI CLASE

Problema 3 Sa se creeze o clasa care sa simuleze jocul Spanzuratoarea.


Aceasta clasa trebuie sa fie o clasa cat mai generica pentru a permite reutilizarea ei n mai multe situatii, indiferent daca avem o aplicatie grafica sau
n linie de comanda.
Pentru inceput scriem codul care va folosi aceasta clasa:

import java.util.Scanner;
public class TestSpanzuratoare
{
private static void afisare(char[] c)
{
for(int i=0;i<c.length-1;i++)
System.out.print(c[i]+" ");
System.out.println(c[c.length-1]);
}
public static void main(String args[])
{
Scanner s=new Scanner(System.in);
Spanzuratoare joc=new Spanzuratoare();
while(!joc.isOver())
{
System.out.print("Cuvantul de ghicit: ");
afisare(joc.cuvantCurent());
System.out.print("Introduceti o litera: ");
char c=s.nextLine().charAt(0);

if(joc.ghiceste(c))
System.out.println("BRAVO!!! Ai ghicit !");
else
System.out.println("GRESEALA!!! Mai ai "+joc.incercariRamase()+" ince
}
if(joc.isWinner())
System.out.println("AI CASTIGAT!!!");
else
System.out.println("AI PIERDUT!!!");
}
}

4.6. PROBLEME REZOLVATE

53

Dupa cum se poate usor observa, metodele si constructorii necesari pentru


aceasta clasa sunt:
Spanzuratoare
--------------------------public Spanzuratoare()
public char[] cuvantCurent()
public boolean ghiceste(char c)
public int incercariRamase()
public boolean isOver()
public boolean isWinner()
Avand interfata clasei, adica metodele publice ale clasei, putem scrie codul pentru aceasta:
import java.util.*;
public class Spanzuratoare
{
private String[] cuvinteDeAles={"abecedar", "calculator", "masina"};
private char[] cuvantCurent;
private char[] cuvantAles;
private int nrGreseli;
private String ghicite;
private final static int MAX_NR_GRESELI=5;
public Spanzuratoare()
{
Random r=new Random();
cuvantAles=cuvinteDeAles[r.nextInt(cuvinteDeAles.length)].toCharArray();
cuvantCurent=cuvantAles.clone();
char c1=cuvantAles[0];
char c2=cuvantAles[cuvantCurent.length-1];
cuvantCurent[0]=c1;
cuvantCurent[cuvantCurent.length-1]=c2;
for(int i=0;i<cuvantAles.length;i++)
if(cuvantAles[i]==c1 || cuvantAles[i]==c2)
cuvantCurent[i]=cuvantAles[i];
else

54

CAPITOLUL 4. OBIECTE SI CLASE


cuvantCurent[i]=_;
ghicite=""+c1+c2;
}
public char[] cuvantCurent()
{
return cuvantCurent.clone();
}
public boolean isOver()
{
if(nrGreseli==MAX_NR_GRESELI)
return true;
for(int i=0;i<cuvantCurent.length;i++)
if(cuvantCurent[i]==_)
return false;
return true;
}
public boolean ghiceste(char c)
{
if(ghicite.indexOf(c)>=0)
return false;
ghicite+=c;
boolean gasit=false;
for(int i=0;i<cuvantAles.length;i++)
if(cuvantAles[i]==c)
{
cuvantCurent[i]=c;
gasit=true;
}
if(!gasit)
nrGreseli++;
return gasit;
}
public int incercariRamase()
{
return MAX_NR_GRESELI-nrGreseli;
}

4.6. PROBLEME REZOLVATE

55

public boolean isWinner()


{
return isOver() && nrGreseli<5;
}
}
Problema 4 Sa se construiasca o clasa MyStringTokenizer care sa lucreze
ntr-un mod asemanator cu clasa StringTokenizer.
Pentru a construi aceasta clasa ne gandim, n primul rand cum ar trebui
ea sa fie folosita ntr-un program. Codul poate arata n felul urmator:
public class TestMyStringTokenizer
{
public static void main(String args[])
{
MyStringTokenizer mst=new MyStringTokenizer("Ce mai faci? Eu sunt aici."
," ?!.");
System.out.println("Avem "+mst.countTokens()+" tokeni");
while(mst.hasMoreTokens())
{
String str=mst.nextToken();
System.out.println(str);
}
}
}
Se poate observa destul de usor ca aceasta clasa trebuie sa aiba urmatoarele
metode:
MyStringTokenizer
--------------------------public MyStringTokenizer(String str, String sep)
public MyStringTokenizer(String str)
public String getSep()
public void setSep(String sep)
public int countTokens()
public boolean hasMoreTokens()
public String nextToken()
Codul pentru aceasta clasa poate arata astfel:

56

CAPITOLUL 4. OBIECTE SI CLASE

public class MyStringTokenizer


{
private String s;
private String sep;
public MyStringTokenizer(String s, String sep)
{
this.s=s;
this.sep=sep;
}
public MyStringTokenizer(String s)
{
this(s," \t\n\r\f");
}
public String getSep()
{
return sep;
}
public void setSep(String sep)
{
this.sep=sep;
}
private boolean isSeparator(char c)
{
return sep.indexOf(c)>=0;
}
public int countTokens()
{
int i=0, tokens=0;
while(i<s.length() && isSeparator(s.charAt(i)))
i++;
for(int j=i;j<s.length();j++)
if(j==0 || !isSeparator(s.charAt(j)) && isSeparator(s.charAt(j-1)))
tokens++;
return tokens;
}

4.6. PROBLEME REZOLVATE

57

public boolean hasMoreTokens()


{
for(int i=0;i<s.length();i++)
if(sep.indexOf(s.charAt(i))<0)
return true;
return false;
}
public String nextToken()
{
int i=0;
while(i<s.length() && isSeparator(s.charAt(i)))
i++;
if(i==s.length())
return null;
int j=i+1;
while(j<s.length() && !isSeparator(s.charAt(j)))
j++;
String cuv=s.substring(i,j);
s=s.substring(j);
return cuv;
}
}
Problema 5 Sa se construiasca o clasa Queue cu ajutorul careia sa se poata
lucra cu o coada de elemente numere ntregi, alocata folosind liste nlantuite.
Codul care ar folosi aceasta clasa ar putea arata astfel. Mentionam ca
am folosit n continuare tratarea exceptiilor chiar daca ea va fi prezentata
ntr-un capitol urmator:
import com.mihai.datastructures.*;
public class TestQueue
{
public static void main(String args[])
{
Queue q=new Queue();
q.push(3);
q.push(5);

58

CAPITOLUL 4. OBIECTE SI CLASE


q.push(7);
try
{
System.out.println("In varful cozii: "+q.top());
q.pop();
System.out.println("Continutul cozii: "+q);
}
catch(EmptyQueueException e)
{
System.out.println("Coada goala !!!");
}
}

}
Dupa cum se poate usor observa vom avea nevoie de doua clase: Queue
si EmptyQueueException.
Clasa Queue are urmatoarea interfata:
Queue
--------------------------public Queue()
public
public
public
public

void push(int x)
int top()
int pop()
boolean isEmpty()

public String toString()


Clasele necesare acestei aplicatii arata astfel:
package com.mihai.datastructures;
public class EmptyQueueException extends Exception{
}
---------------------------------------package com.mihai.datastructures;
class Node {
private int value;

4.6. PROBLEME REZOLVATE


private Node next;
Node(int value)
{
this.value=value;
this.next=null; //putea lipsi
}
Node(int value, Node next) {
this.value = value;
this.next = next;
}
int getValue() {
return value;
}
void setValue(int value) {
this.value = value;
}
Node getNext()
{
return next;
}
void setNext(Node next)
{
this.next=next;
}
}
---------------------------------------package com.mihai.datastructures;
public class Queue {
private Node first, last;
public boolean isEmpty()
{
return first==null;

59

60

CAPITOLUL 4. OBIECTE SI CLASE


}
public void push(int x)
{
Node node=new Node(x);
if(isEmpty())
first=node;
else
last.setNext(node);
last=node;
}
public int top() throws EmptyQueueException
{
if(isEmpty())
throw new EmptyQueueException();
return first.getValue();
}
public int pop() throws EmptyQueueException
{
if(isEmpty())
throw new EmptyQueueException();
int value=first.getValue();
first=first.getNext();
return value;
}
public String toString()
{
StringBuffer sb=new StringBuffer("[");
Node current=first;
if(!isEmpty())
{
while(current.getNext()!=null)
{
sb.append(current.getValue()+",");
current=current.getNext();
}
sb.append(current.getValue());
}

4.6. PROBLEME REZOLVATE

61

sb.append("]");
return sb.toString();
}
}
Problema 6 Sa se construiasca o clasa HeapMin care sa ne permita sa
lucram cu heapuri minime.
Ea ar trebui sa poata fi folosita ntr-unul din modurile urmatoare:
import java.util.Random;
public class TestHeapMin
{
public static void main(String args[])
{
/*Random r=new Random();
int[] a={0, 2, 4, 8, 7};
HeapMin heap=new HeapMin(a);
for(int i=0;i<3;i++)
heap.add(r.nextInt(10));
System.out.println(heap);
while(!heap.isEmpty())
System.out.print(heap.extractMin()+" ");*/
Random r=new Random();
HeapMin heap=new HeapMin();
for(int i=0;i<5;i++)
heap.add(r.nextInt(10));
System.out.println(heap);
System.out.print("Elementul minim: "+heap.getMin());
}
}

62

CAPITOLUL 4. OBIECTE SI CLASE


Interfata acestei clase arata astfel:

HeapMin
--------------------------public HeapMin(int[] a)
public HeapMin()
public void add(int x)
public void add(int[] a)
public int extractMin()
public int getMin()
public boolean isEmpty()
public String toString()
Implementarea ei propriu-zisa poate arata ca n exemplul urmator:
public class HeapMin
{
private int count;
private int[] elements;
public HeapMin()
{
elements=new int[3];
}
public HeapMin(int[] a)
{
elements=new int[a.length+5];
System.arraycopy(a, 0, elements, 1, a.length);
count=a.length;
construiesteHeap();
}
private void expand()
{
int newSize;
if(elements.length<10)
newSize=elements.length*2;

4.6. PROBLEME REZOLVATE

63

else
newSize=elements.length*4/3;
int[] newElements=new int[newSize];
System.arraycopy(elements, 1, newElements, 1, elements.length-1);
elements=newElements;
}
private boolean isFull()
{
return count==elements.length-1;
}
public void add(int x)
{
if(isFull())
expand();
elements[++count]=x;
int j=count;
while(j!=1 && elements[j/2]>elements[j])
{
int aux=elements[j];
elements[j]=elements[j/2];
elements[j/2]=aux;
j=j/2;
}
}
public void add(int[] a)
{
for(int x:a)
add(x);
}
private void construiesteHeap()
{
for(int i=count/2;i>=1;i--)
reconstituieHeap(i);
}
private void reconstituieHeap(int i)
{

64

CAPITOLUL 4. OBIECTE SI CLASE


int left=2*i, right=2*i+1, minim;
if(left<=count && elements[left]<elements[i])
minim=left;
else
minim=i;
if(right<=count && elements[right]<elements[minim])
minim=right;
if(minim!=i)
{
int aux=elements[i];
elements[i]=elements[minim];
elements[minim]=aux;
reconstituieHeap(minim);
}
}
public int extractMin()
{
int min=elements[1];
elements[1]=elements[count--];
reconstituieHeap(1);
return min;
}
public int getMin()
{
return elements[1];
}
public boolean isEmpty()
{
return count==0;
}
public String toString()
{
StringBuffer sb=new StringBuffer("");
for(int i=1;i<=count;i++)
sb.append(elements[i]+" ");
return sb.toString();
}

4.7. PROBLEME PROPUSE

65

4.7

Probleme propuse

Propunem spre rezolvare urmatoarele probleme:


1. Sa se construiasca o clasa pentru lucrul cu fractii, n mod asemanator
cu cea pentru lucrul cu numere complexe si sa se foloseasca ntr-un
program.
2. Sa se construiasca o clasa Polinom si sa se implementeze operatiile
pentru polinoame.
3. Sa se creeze o clasa Punct care contine coordonatele unui punct n plan.
Se citesc de la tastatura n puncte n plan. Sa se rezolve urmatoarele
subprobleme:
Sa se determine 2 puncte ntre care avem distanta minima.
Sa se verifice daca n sir avem 3 puncte coliniare.
Sa se afiseze toate punctele situate cel mai la nord.
4. Sa se construiasca o clasa Matrice pentru lucrul cu matrici.
5. Sa se construiasca o clasa Queue care sa simuleze modul de functionare
al unei cozi. Elementele cozii vor fi retinute cu ajutorul vectorilor.
Putem folosi chiar si vectori circulari.

66

CAPITOLUL 4. OBIECTE SI CLASE

CAPITOLUL

Mostenirea n Java

5.1

Notiuni generale

Unul din cele mai importante concepte folosite n programarea obiect


orientata este conceptul de mostenire. Folosirea mostenirii ncurajeaza:
refolosirea claselor deja existente
tratarea n mod uniform a unor obiecte care sunt de tipuri diferite
(ncurajeaza polimorfismul)
Modul de lucru n Java seamana oarecum cu ceea ce cunoastem deja din
limbajul C++, numai ca n Java sintaxa este mult mai usoara. In plus,
n Java nu exista conceptul de mostenire multipla (n Java o clasa poate fi
derivata dintr-o singura alta clasa).
Pentru a indica faptul ca o clasa este derivata din alta clasa folosim
cuvantul cheie extends, ca n exemplul urmator:
class ClasaDerivata extends ClasaBaza
{
...
}
Clasa din care se deriveaza se numeste clasa de baza (sau clasa parinte,
superclasa) iar clasa care este derivata din ea se numeste clasa derivata (sau
clasa copil sau subclasa).
67

68

CAPITOLUL 5. MOSTENIREA IN JAVA

O subclasa descrie obiecte care au aceleasi proprietati ca si cele ale superclasei si care, eventual, au si alte atribute sau metode.
Atributele si metodele din superclasa sunt accesibile si n subclasa, cu
exceptia atributelor si metodelor care sunt declarate private n superclasa.
De multe ori, atributele private din superclasa pot fi accesate prin intermediul
unor metode din superclasa.
Reamintim ca n limbajul Java exista modificatorul de vizibilitate protected care are urmatoarea semnificatie: acele atribute sau metode cu aceasta
vizibilitate sunt vazute oriunde n subclase precum si n toate clasele din pachetul curent.
Dupa cum stim si din C++, atunci cand avem mostenire, trebuie sa putem
apela din constructorul unei clase constructorul clasei de baza. Acest lucru
se poate face cu ajutorul cuvantului cheie super. Apelul constructorului din
clasa de baza trebuie sa fie prima instructiune din constructor.
Un astfel de exemplu este prezentat n continuare:
public class Profesor extends Persoana
{
private String gradDidactic;
public Profesor(String nume, String prenume, String grad)
{
super(nume, prenume);
this.gradDidactic=grad;
}
...
}
Observatie: Daca nu apelam dintr-un constructor dintr-o clasa derivata
un constructor din clasa de baza, atunci automat se apeleaza constructorul
fara parametri din clasa de baza. De aceea, trebuie sa fim atenti, pentru ca
n situatia n care avem doar constructori cu parametri n clasa de baza si
n constructorul din clasa derivata nu apelam pe niciunul din ei, vom obtine
eroare.

5.2

Exemplu de aplicatie folosind mostenirea

In cele ce urmeaza vom prezenta un program care ilustreaza principiile


prezentate anterior.
Exemplul 1 Sa se scrie un program care citeste de la tastatura informatii
despre n persoane (numele si prenumele). Persoanele sunt de 2 tipuri: profesori si studenti. Pentru fiecare profesor se cunoaste n plus gradul didactic pe

5.2. EXEMPLU DE APLICAT


IE FOLOSIND MOSTENIREA

69

care-l are, iar pentru fiecare student se cunoaste daca este bursier sau nu. Sa
se sorteze crescator sirul de persoane dupa nume si sa se afiseze sirul obtinut.
In primul rand definim o clasa Persoan
a astfel:
public class Persoana
{
private String nume;
private String prenume;
public Persoana(String nume, String prenume)
{
this.nume = nume;
this.prenume = prenume;
}
public String getNume()
{
return nume;
}
public void setNume(String nume)
{
this.nume = nume;
}
public String getPrenume()
{
return prenume;
}
public void setPrenume(String prenume)
{
this.prenume = prenume;
}
public String toString()
{
return nume + " " + prenume;
}
}
Definim o clasa Profesor derivata din clasa Persoana:

70

CAPITOLUL 5. MOSTENIREA IN JAVA

public class Profesor extends Persoana


{
private String gradDidactic;
public Profesor(String nume, String prenume, String gradDidactic)
{
//daca nu apelez constructorul din clasa de baza
//am eroare
super(nume, prenume); //apel constructor din clasa de baza
this.gradDidactic=gradDidactic;
}
public String getGradDidactic()
{
return gradDidactic;
}
public void setGradDidactic(String gradDidactic)
{
this.gradDidactic = gradDidactic;
}
//suprascriere metoda din clasa de baza
public String toString()
{
//apel metoda din clasa de baza
return super.toString()+" este "+gradDidactic;
}
}
Clasa Student se construieste asemanator cu clasa Profesor:
public class Student extends Persoana
{
private boolean bursier;
public Student(String nume, String prenume, boolean bursier)
{
super(nume, prenume);
this.bursier = bursier;
}

5.2. EXEMPLU DE APLICAT


IE FOLOSIND MOSTENIREA

71

public boolean isBursier()


{
return bursier;
}
public void setBursier(boolean bursier)
{
this.bursier = bursier;
}
public String toString()
{
if(bursier)
return super.toString()+" este bursier";
return super.toString()+" nu este bursier";
}
}
In cele din urma prezentam si codul pentru aplicatia care va folosi clasele
create anterior:
import java.util.Scanner;

public class TestPersoane


{
public static void main(String[] args)
{
Scanner s=new Scanner(System.in);
System.out.print("n=");
int n=Integer.parseInt(s.nextLine());
Persoana[] p=new Persoana[n];
for(int i=0;i<n;i++)
{
System.out.print("Numele: ");
String nume=s.nextLine();
System.out.print("Prenumele: ");
String prenume=s.nextLine();

CAPITOLUL 5. MOSTENIREA IN JAVA

72

System.out.print("Profesor sau student (p/s)");


char tip=s.nextLine().charAt(0);
if(tip==p)
{
System.out.print("Gradul didactic: ");
String gradDidactic=s.nextLine();
p[i]=new Profesor(nume, prenume, gradDidactic);
}
else
{
System.out.print("Bursier? (d/n)");
if(s.nextLine().charAt(0)==d)
p[i]=new Student(nume, prenume, true);
else
p[i]=new Student(nume, prenume, false);
}
}
sort(p);
afisare(p);
}
private static void sort(Persoana [] p)
{
for(int i=0;i<p.length-1;i++)
for(int j=i+1;j<p.length;j++)
if(p[i].getNume().compareToIgnoreCase(p[j].getNume())>0)
{
Persoana aux=p[i];
p[i]=p[j];
p[j]=aux;
}
}
private static void afisare(Persoana [] p)
{
for(Persoana pers:p)
System.out.println(pers); //POLIMORFISM !!!!
}
}

5.3. POLIMORFISMUL

73

Trebuie remarcat ca n metoda de afisare, atunci cand se parcurge sirul


de obiecte de tipul Persoana, n functie de tipul obiectelor, Profesor sau
Student se apeleaza metoda toString() corecta.
Acest mecanism poarta numele de polimorfism.
Solutia acestei probleme poate fi prezentata sub forma diagramei UML
din figura 5.1.

Figura 5.1: Diagrama de clase a aplicatiei cu persoane

5.3

Polimorfismul

Polimorfismul se refera la faptul ca atunci cand se apeleaza o metoda


asupra unui obiect care la prima vedere pare de un tip mai generic, de fapt
se apeleaza metoda corespunzatoare clasei efective a obiectului.
Acest lucru este posibil deoarece n Java se foloseste apelul dinamic al
metodelor.
Atunci cand se apeleaza o metoda asupra unui obiect, se parcurg urmatorii
pasi:
In lista de metode ale clasei se identifica acele metode care se potrivesc
ca nume cu metoda apelata.

74

CAPITOLUL 5. MOSTENIREA IN JAVA


Intre aceste metode se cauta o metoda pentru care parametrii acesteia
sa se potriveasca cu parametri metodei apelate (overloading resolution).
Daca nu se gaseste o astfel de metoda atunci se genereaza o eroare.
Daca avem de-a face cu o metoda privata, statica, finala sau un constructor, atunci compilatorul stie exact care este metoda care trebuie
apelata. In acest caz, avem de-a face cu legarea statica la momentul
rularii.
Daca nu suntem n una din aceste situatii, atunci metoda apelata se
alege la momentul rularii de catre masina virtuala Java. In primul rand
se cauta o metoda n clasa curenta, dupa care n clasa ei de baza, si
tot asa pana cand se gaseste o clasa n care metoda este implementata.
De fapt, n realitate lucrurile nu se ntampla chiar asa, ci mai degraba,
pentru fiecare clasa se construieste cate un tabel cu metodele care se
pot apela din clasa respectiva si doar se face o cautare n acea tabela.

De exemplu, pentru clasa Profesor o tabela de metode ar putea arata


astfel:
PROFESOR
getNume() => Persoana.getNume()
setNume() => Persoana.setNume()
getPrenume() => Persoana.getPrenume()
setPrenume() => Persoana.setPrenume()
getGradDidactic() => Profesor.getGradDidactic()
setGradDidactic() => Profesor.setGradDidactic()
toString() => Profesor.toString()
Daca se apeleaza o metoda polimorfic, atunci se parcurg urmatorii pasi:
In primul rand masina virtuala obtine tabela cu metode corespunzatoare
tipului actual al obiectului.
Masina virtuala parcurge lista cu metode si identifica metoda corespunzatoare.
In final este rulata metoda.
Observatie: Atunci cand suprascriem ntr-o clasa derivata o metoda care
a fost declarata ntr-o clasa de baza, aceasta noua metoda trebuie sa aiba un
nivel de vizibilitate cel putin cat era n clasa de baza, altfel se obtine eroare.

5.4. METODE SI CLASE FINALE

5.4

75

Metode si clase finale

Atunci cand vrem ca o clasa sa nu mai poata fi derivata, putem preciza


acest lucru compilatorului prin folosirea modificatorului final, ca n exemplul
urmator:
public final class Utils
{
...
}
Pentru a nu mai permite suprascrierea unei metode ntr-o clasa derivata,
putem marca acea metoda ca metoda finala cu ajutorul modificatorului final.
Daca programatorul ncearca suprascrierea metodei, atunci compilatorul va
genera o eroare de compilare.

5.5

Operatia de casting

Se poate sa cream obiecte care sa fie retinute printr-o referinta la un tip


de baza al lor si sa fie de un tip derivat. Urmatoarele instructiuni sunt valide:
Persoana s=new Student("Ion","Ion",true);
s.setNume("Ionescu");
//urmatoarea instructiune nu functioneaza pentru ca nu putem apela
//asupra obiectului decat metode ale clasei Persoana
//s.setBursier(false);
Si n Java, la fel ca n toate limbajele moderne de programare este suportata operatia de cast.
Putem avea nevoie de operatia de ea, de exemplu, atunci cand avem un
sir de obiecte de un tip mai generic si vrem sa folosim anumite facilitati particulare ale unui obiect care este de un tip mai specializat. Iata un exemplu:
for(int i=0;i<p.length;i++)
if(p[i] instanceof Profesor)
{
Profesor prof=(Profesor)p[i];
prof.setGradDidactic("profesor universitar");
}
Cu ajutorul operatorului instanceof am verificat daca obiectul p[i] este
de tipul Profesor, si daca este am retinut acel obiect ntr-un obiect de tip

CAPITOLUL 5. MOSTENIREA IN JAVA

76

profesor. Acest lucru a fost posibil deoarece am facut o operatie de cast.


In general nu este bine sa scriem programe care sa foloseasca cast-uri, daca
acest lucru nu este neaparat necesar.
Observatie: Chiar daca un obiect p[i] este de tipul efectiv Profesor,
asupra lui nu se pot apela decat metodele corespunzatoare tipului sirului. A
fost nevoie de cast pentru a putea apela o metoda specifica subclasei.

5.6

Clase abstracte

Cu cat aplicatiile noastre devin mai complexe cu atat vom remarca ca si


numarul de nivele la care avem mostenire se poate mari. La un moment dat
vom ajunge la clase care sunt atat de generice, ncat e si greu sa implementam
anumite functionalitati pentru ele.
Cel mai bine vom ntelege necesitatea utilizarii claselor abstracte printrun exemplu. Sa consideram urmatoarea problema pe care dorim sa o rezolvam:
Exemplul 2 Se citesc de la tastatura informatii despre n figuri n plan.
Fiecare figura geometrica are o culoare. Figurile geometrice sunt de 2 tipuri:
cercuri si dreptunghiuri. Pentru un cerc, n plus fata de culoare mai cunoastem
si raza lui, iar pentru un dreptunghi mai cunoastem lungimea si latimea. Se
doreste ordonarea celor n figuri geometrice crescator dupa arie si afisarea lor
pe ecran.
In primul rand definim o clasa Figura care va arata astfel:
public abstract class Figura
{
private String culoare;
public Figura(String culoare)
{
this.culoare=culoare;
}
public String getCuloare()
{
return culoare;
}
public void setCuloare(String culoare)
{
this.culoare = culoare;

5.6. CLASE ABSTRACTE

77

}
public abstract double aria();
public abstract double perimetru();
}
Observam ca aceasta clasa este abstracta si ca ea are doua metode abstracte, cele care calculeaza aria si respectiv perimetrul unei figuri geometrice.
O clasa abstracta este o clasa care nu poate fi instantiata n mod direct.
O metoda abstracta a unei clase este o metoda care are sens pentru acea
clasa dar a carei implementare nu poate fi oferita la momentul respectiv.
O clasa care are o metoda abstracta automat trebuie sa fie abstracta. Pe
de alta parte, putem avea clase abstracte care nu au nici o metoda abstracta.
Prima afirmatie este foarte naturala, deoarece ar fi greu de imaginat sa
putem crea instante ale unei clase care are niste metode neimplementate. In
situatia n care programatorul ar apela acele metode compilatorul nu ar stii
ce trebuie sa faca.
In cele ce urmeaza ne definim clasele Cerc si Dreptunghi care sunt
derivate din clasa Figura. Ele arata astfel:
public class Cerc extends Figura
{
private double raza;
public Cerc(String culoare, double raza)
{
//apel constructor din clasa de baza
super(culoare);
this.raza=raza;
}
public double getRaza()
{
return raza;
}
public void setRaza(double raza)
{
this.raza = raza;
}

78

CAPITOLUL 5. MOSTENIREA IN JAVA


public double aria()
{
return Math.PI*Math.pow(raza, 2);
}
public double perimetru()
{
return 2*Math.PI*raza;
}
public String toString()
{
return "Cerc cu raza "+raza+" si de culoarea "+super.getCuloare();
}

public class Dreptunghi extends Figura


{
private double lungime, latime;
public Dreptunghi(String culoare, double lungime, double latime)
{
super(culoare);
this.lungime = lungime;
this.latime = latime;
}
public double getLungime()
{
return lungime;
}
public void setLungime(double lungime)
{
this.lungime = lungime;
}
public double getLatime()
{
return latime;

5.6. CLASE ABSTRACTE

79

}
public void setLatime(double latime)
{
this.latime = latime;
}
public double aria()
{
return lungime*latime;
}
public double perimetru()
{
return 2*(lungime+latime);
}
public String toString()
{
return "Dreptunghi de lungime "+lungime+", latime de "+
latime+"si culoare: "+super.getCuloare();
}
}
Observam ca n exemplul de mai sus clasele prezentate implementeaza
metodele aria() si perimetru() care erau declarate abstracte n clasa Figura.
Daca macar una din cele doua metode nu era implementata ar fi trebuit ca
clasa care nu o implementa sa fie abstracta (conform regulii care zice ca o
clasa care are metode abstracte este obligatoriu abstracta).
Programul care foloseste cele 3 clase reprezentate anterior este prezentat
n cele ce urmeaza:
import java.util.Scanner;
public class TestFiguri
{
public static void main(String[] args)
{
Scanner s=new Scanner(System.in);
System.out.print("Numarul de figuri geometrice: ");
int n=Integer.parseInt(s.nextLine());

CAPITOLUL 5. MOSTENIREA IN JAVA

80

Figura[] f=new Figura[n];


for(int i=0;i<n;i++)
{
System.out.print("Tipul figurii? (c/d)");
if(s.nextLine().charAt(0)==c)
{
System.out.print("Culoarea: ");
String culoare=s.nextLine();
System.out.print("Raza: ");
double raza=Integer.parseInt(s.nextLine());
f[i]=new Cerc(culoare, raza);
}
else
{
System.out.print("Culoarea: ");
String culoare=s.nextLine();
System.out.print("Lungime: ");
double lungime=Integer.parseInt(s.nextLine());
System.out.print("Latime: ");
double latime=Integer.parseInt(s.nextLine());
f[i]=new Dreptunghi(culoare, lungime, latime);
}
}
sortare(f);
afisare(f);
}
private static void sortare(Figura[] f)
{
for(int i=0;i<f.length-1;i++)
for(int j=i+1;j<f.length;j++)
if(f[i].aria()>f[j].aria())
{
Figura aux=f[i];
f[i]=f[j];
f[j]=aux;
}

5.6. CLASE ABSTRACTE

81

}
private static void afisare(Figura[] f)
{
for(Figura fig:f)
System.out.println(fig);
}
}
Putem remarca ca n acest exemplu metoda aria() este tratata polimorfic,
deoarece n metoda main() ea este utilizata ntr-un asa mod ncat nu se poate
spune la momentul compilarii care metoda se va apela.
Solutia acestei probleme se poate reprezenta cu ajutorul diagramei de
clase reprezentata n figura 5.2.

Figura 5.2: Solutia problemei cu figuri geometrice


S-ar putea ca unii cititori sa se ntrebe de ce este necesar ca metodele
aria() si perimetru() sa fie abstracte.
Acest lucru este destul de simplu. Faptul ca ele sunt definite n clasa
Figura mi permite sa le apelez uniform pentru toate obiectele din sirul de

CAPITOLUL 5. MOSTENIREA IN JAVA

82

figuri geometrice. Totusi, nu puteam sa le definesc n cazul clasei Figura


deoarece nu stiu cum sunt implementate. Din acest motiv, metodele au fost
declarate ca abstracte.

5.7

Clasa Object

In Java, chiar daca acest lucru nu este vizibil n mod direct, toate clasele
sunt derivate din clasa Object. Acest lucru nseamna ca ele au deja o anumita
functionalitate deja definita, si ca au, de asemenea, anumite metode pe care
le pot suprascrie.
Metodele clasei Object sunt sintetizate n diagrama de clase din figura
5.3.

Figura 5.3: Metodele clasei Object


Faptul ca toate obiectele sunt derivate din clasa Object nseamna ca
urmatorul cod este perfect valabil:
String s="test";
Object o=s;
s=(String)o;
Se poate observa ca pentru a transforma o referinta la un tip mai general,
catre o referinta la un tip specializat, trebuie sa facem o operatie de cast.
Faptul ca toate clasele sunt derivate direct sau indirect din clasa Object
ne permite sa scriem clase pentru colectii care sa retina elemente de tip
referinta de orice tip. Aceasta modalitate de lucru a fost folosita foarte mult
nainte de aparitia lucrului cu tipuri generice n Java.

5.7. CLASA OBJECT

5.7.1

83

Metoda toString()

Metoda toString() poate fi implementata pentru o clasa pentru a putea


obtine cu ajutorul ei o reprezentare folosind un sir de caractere a unui obiect.
Aceasta reprezentare poate fi foarte usor folosita pentru realizarea operatiei
de logging.
In general nu este bine sa ne bazam n interfata grafica pe metoda toString()
ci mai degraba sa avem propriile noastre metode care sa realizeze afisarea.
Este deja binecunoscut ca atunci cand pentru un obiect apelam metoda
println(), ca n exemplul urmator, de fapt pentru afisarea obiectului se foloseste
metoda toString().
Complex c=new Complex(3,4);
// automat se apeleaza metoda toString()
System.out.println(x);

5.7.2

Metoda equals()

Metoda equals() a clasei Object testeaza daca doua obiecte au aceeasi


stare. Cateodata nu are sens sa comparam doua obiecte, motiv pentru care
aceasta metoda poate sau nu sa fie implementata. Totusi, daca avem obiecte
care pot fi comparate pentru egalitate, este important sa o scriem.
Este important, de asemenea, sa scriem aceasta metoda deoarece, daca
folosim clase pentru lucrul cu colectii n Java, aceasta metoda este folosita
atunci cand se cauta un anumit element ntr-o colectie.
De exemplu, putem scrie metoda equals() pentru clasa Complex n felul
urmator:
class Complex
{
// alte atribute si metode
public boolean equals(Object otherObject)
{
if (this == otherObject)
return true;
if (otherObject == null)
return false;
if (getClass() != otherObject.getClass())
return false;

CAPITOLUL 5. MOSTENIREA IN JAVA

84

Complex other = (Complex) otherObject;


return re==other.re && im==other.im;
}
}
Implementarea metodei equals() este destul de tricky atunci cand avem
de-a face cu compararea unor obiecte care fac parte din aceeasi ierarhie de
obiecte. In anumite situatii, n locul apelului metodei getClass() se prefera
folosirea operatorului instanceof.
Pentru ntelegerea mai buna a modului n care se lucreaza cu metoda
equals() este recomandata studierea n amanunt a documentatiei Java.

5.7.3

Metoda hashCode()

Metoda hashCode() returneaza un numar ntreg care este folosit n situatia


n care un obiect este retinut ntr-o tabela de dispersie. Este recomandabil ca,
pe cat posibil, la valori diferite ale continutului obiectelor si valorile returnate
de metoda sa fie diferite.
De cele mai multe ori, metoda returneaza o valoare bazata pe valorile
campurilor care sunt continute de clasa.
Iata si un exemplu:
class Persoana
{
private String nume, prenume;
//alte atribute si metode
public int hashCode()
{
return 3*nume.hashCode()+5*prenume.hashCode();
}
}
Observatie: Daca doua obiecte prin folosirea metodei equals() sunt egale,
atunci si metodele lor hashCode() trebuie sa returneze aceleasi valori.

5.7.4

Metoda clone()

Metoda clone() este folosita pentru a crea o copie a unui obiect. Trebuie
ca atunci cand realizam copia unui obiect sa creem un nou obiect care nu
partajeaza absolut nimic cu vechiul obiect.
In primul rand trebuie spus ca exista situatii n care nu vrem ca o instanta
a unui obiect sa poata fi clonata. Daca suntem n aceasta situatie nu trebuie

5.8. IMPLEMENTAREA COLECT


IILOR GENERICE FOLOSIND OBJECT85
sa facem nimic, pentru ca aceasta este implementarea implicita n Java. Daca
totus vrem sa putem
Daca obiectul initial are numai campuri de tipuri primitive atunci este
suficient sa copiem campurile din vechiul obiect n cel nou. La fel se ntampla
si n cazul campurilor de tip String, deoarece aceasta clasa este immutable.
Prezentam un exemplu n cele ce urmeaza:
class Complex implements Cloneable
{
private double re, im;
public Object clone()
{
return new Complex(re, im);
}
}
Totusi n unele situatii nu este bine sa facem acest lucru. Acesta ar fi
cazul unei stive, n care codul ar arata mai degraba astfel:
class Stack implements Cloneable
{
private int[] elements;
private int count;
public Object clone()
{
Stack clonedStack=new Stack();
clonedStack.elements=elements.clone();
clonedStack.count=count;
}
}

5.8

Implementarea colectiilor generice folosind


Object

Datorita faptului ca toate clasele n Java sunt derivate din clasa Object
se poate ca sa definim colectii care sa poata fi utilizate pentru a retine obiecte
de orice tip.
Acest lucru se face prin retinerea elementelor care alcatuiesc colectia n
structuri bazate pe clasa Object.

CAPITOLUL 5. MOSTENIREA IN JAVA

86

In cele ce urmeaza prezentam un astfel de exemplu. Clasa Queue este cea


prezentata anterior, n plus adaugand doar faptul ca ea poate retine obiecte
de orice tip.
package com.mihai.datastructures;
public class EmptyQueueException extends Exception{
}
package com.mihai.datastructures;
class Node {
private Object value;
private Node next;
Node(Object value)
{
this.value=value;
this.next=null; //putea lipsi
}
Node(Object value, Node next) {
this.value = value;
this.next = next;
}
Object getValue() {
return value;
}
void setValue(Object value) {
this.value = value;
}
Node getNext()
{
return next;
}
void setNext(Node next)
{

5.8. IMPLEMENTAREA COLECT


IILOR GENERICE FOLOSIND OBJECT87
this.next=next;
}
}
package com.mihai.datastructures;
public class Queue {
private Node first, last;
public boolean isEmpty()
{
return first==null;
}
public void push(Object x)
{
Node node=new Node(x);
if(isEmpty())
first=node;
else
last.setNext(node);
last=node;
}
public Object top() throws EmptyQueueException
{
if(isEmpty())
throw new EmptyQueueException();
return first.getValue();
}
public Object pop() throws EmptyQueueException
{
if(isEmpty())
throw new EmptyQueueException();
Object value=first.getValue();
first=first.getNext();
return value;
}
public String toString()
{

CAPITOLUL 5. MOSTENIREA IN JAVA

88

StringBuffer sb=new StringBuffer("[");


Node current=first;
if(!isEmpty())
{
while(current.getNext()!=null)
{
sb.append(current.getValue()+",");
current=current.getNext();
}
sb.append(current.getValue());
}
sb.append("]");
return sb.toString();
}
}
package com.mihai.main;
import java.util.StringTokenizer;
import com.mihai.datastructures.*;
public class TestQueue
{
public static void main(String args[])
{
Queue q=new Queue();
q.push("mihai");
q.push("ioana");
q.push("ana maria");
try
{
System.out.println("In varful cozii: "+q.top());
String str=(String)q.pop();
System.out.println("Continutul cozii: "+q);
}
catch(EmptyQueueException e)
{
System.out.println("Coada goala !!!");
}

5.9. UTILIZAREA COLECT


IILOR GENERICE CU TEMPLATE-URI 89
}
}
In exemplul anterior am vazut cum se poate implementa o colectie generica
avand la baza clasa Object.
Totusi aceasta abordare are anumite defecte, ceea ce a condus la implementarea n versiunile mai noi ale limbajului ale unor astfel de colectii cu
ajutorul template-urilor. Aceste defecte sunt:
Se poate ca din greseala un anumit programator, care nu stie exact ce
tip au elementele dintr-o astfel de colectie sa introduca elemente de un
alt tip.
La parcurgerea apelul metodelor care obtin elemente din colectie, trebuie facute casturi care pot sa genereze erori din cauza unor greseli
prezentate anterior. In plus, acest mecanism de obtinere a elementelor
dintr-o colectie nu este elegant.
Practic, daca ne gandim mai bine o sa observam ca aceste colectii nu
ofera siguranta n utilizare si pot produce erori care nu pot fi depistate la
momentul rularii.
Din acest motiv, s-au introdus colectiile care folosesc template-uri, care
sunt prezentate pe scurt n sectiunea urmatoare.

5.9

Utilizarea colectiilor generice cu templateuri

Cel mai bine pentru a ntelege cum se folosesc aceste liste, prezentam un
exemplu de folosire a lor.
import java.util.*;
public class TestLista {
public static void main(String[] args) {
List<String> lista=new LinkedList<String>();
lista.add("test1");
lista.add("test2");
lista.add("test3");
for(String s:lista)

CAPITOLUL 5. MOSTENIREA IN JAVA

90

System.out.println(s);
System.out.println("-----------------------------");
Iterator<String> iterator=lista.iterator();
while(iterator.hasNext())
{
String str=iterator.next();
System.out.println(str);
}
lista.remove("test2");
lista.add("test4");
System.out.println("-----------------------------");
for(int i=0;i<lista.size();i++)
{
String str=lista.get(i);
System.out.println(str);
}
System.out.println("-----------------------------");
System.out.println("Rezultat contains: "+lista.contains("test2"));
}
}
Se poate testa faptul ca n noua lista nu se mai pot introduce elemente
de alte tipuri decat cel declarat. Se poate observa, de asemenea, ca nu mai
este nevoie de operatii de cast pentru a obtine un anumit obiect din colectie.
Deocamdata nu intram n mai multe amanunte relativ la folosirea colectiilor
n Java, urmand a reveni n capitolul special dedicat colectiilor Java.

5.9.1

Boxing si unboxing n Java

In mod normal n colectii nu se pot retine decat obiecte. Acest lucru era
valabil la versiunile mai vechi de Java. In versiunile mai noi s-a introdus
conceptul de boxing care face posibil urmatorul cod:
ArrayList<Integer> lista=new ArrayList<Integer>();

5.9. UTILIZAREA COLECT


IILOR GENERICE CU TEMPLATE-URI 91
lista.add(3);
lista.add(4);
for(int x:lista)
System.out.print(x+" ");
Acest cod este posibil deoarece atunci cand este apelata metoda add()
parametrul transmis este automat convertit la un obiect de tip Integer (boxing).
Exact opusul se ntampla atunci cand realizam afisarea, n acest caz
putand afirma ca avem de-a face cu conceptul de unboxing.
Cel mai bine se poate vedea acest lucru pe urmatorul cod:
Integer intObject = 100;
//echivalent cu apelul Integer intObject = new Integer(100);
int x = intObject;
//echivalent cu apelul int x = intObject.getValue();
Observatie: La fel ca si clasa String, clasa Integer este o clasa immutable
(nu si poate schimba niciodata valoarea).

92

CAPITOLUL 5. MOSTENIREA IN JAVA

CAPITOLUL

Tratarea exceptiilor n Java

Unul din cele mai importante lucruri pentru orice programator care foloseste
programarea obiect orientata este ntelegerea mecanismului care sta la baza
lucrului cu exceptii.
Evident, ar fi de dorit ca n practica sa nu apara exceptii, dar dupa cum
fiecare dintre noi a observat, acest lucru este imposibil. Astfel, este important
sa le tratam corespunzator.
Mecanismul de lucru cu exceptii n Java seamana foarte mult cu mecanismul pe care deja l cunoastem din C/C++.
In momentul n care apare o exceptie, cel care utilizeaza programul sau
programatorul trebuie sa fie nstiintat pentru a o putea trata corespunzator.
Astfel, aplicatia trebuie sa fie capabila sa faca unul din urmatoarele doua
lucruri:
Sa termine aplicatia ntr-un mod civilizat (cu salvarea eventual a lucrurilor care trebuie salvate, nchiderea conexiunilor care se folosesc n
aplicatie)
Sa permita utilizatorului sa se ntoarca la o stare din care sa poata
continua aplicatia (de exemplu, daca o citire nu reuseste, trebuie sa mi
se permita sa reiau acest pas sau sa tratez altfel aceasta situatie).
Teoretic, chiar daca acest lucru este mult mai complicat si mai putin elegant, se pot ncerca si alte abordari ale tratarii exceptiilor decat cele folosite
n limbajul Java. Astfel, se poate de exemplu, ca fiecare metoda sa returneze
un anumit cod, care sa ne spuna daca metoda a reusit sau nu.
93

CAPITOLUL 6. TRATAREA EXCEPT


IILOR IN JAVA

94

Totusi, pe termen lung, aceasta abordare este greu de utilizat pentru


programator. Ea mai are un mare dezavantaj, si anume faptul ca are ca
efect amestecarea codului care prezinta cazul de dorit n care totul merge
bine cu codul care se ocupa de tratarea erorii.
Mai mult, unele erori ar fi greu de tratat folosind aceasta metoda.
Din aceasta cauza, vom vedea ca limbajul Java acorda o mare importanta
tratarii erorilor. Astfel, avem o ntreaga ierarhie de clase destinata acestui
scop.

6.1

Clasificarea exceptiilor

Exceptiile care pot fi ntalnite ntr-un program Java sunt de doua feluri:
unchecked exceptions - sunt acele exceptii care apar la momentul rularii
dar pe care programatorul nu este obligat sa le vaneze (sa le trateze
n mod explicit). Exemple de astfel de exceptii sunt: ArrayIndexOutOfBoundsException, DivisionByZeroException etc.
checked exceptions - exceptiile pe care programatorul este obligat sa le
ia n considerare (el trebuie sa fie constient de faptul ca acestea pot
aparea ntr-un anumit loc). Astfel de exceptii sunt: FileNotFoundException, IOException etc.
Cele mai importante clase care sunt folosite n Java pentru tratarea
exceptiilor sunt Throwable, Exception, Error si RuntimeException. Ele
se afla n relatiile prezentate n figura 6.1.

Figura 6.1: Diagrama claselor pentru tratarea exceptiilor

6.2. CREAREA CLASELOR EXCEPT


IE

95

Toate exceptiile sunt derivate din clasa Throwable care se comporta pentru exceptii in modul n care Object se comporta pentru toate clasele. Avem
doua tipuri derivate din Throwable si anume Error (erori care apar din cauza
masinii virtuale si care n principiu sunt foarte greu de tratat; tot ceea ce
se poate face este sa ncheiem programul ntr-un mod elegant) si Exception
(erori care apar din cauze care nu tin de implementarea masinii virtuale).
Un caz mai deosebit de erori sunt cele de tipul RuntimeException care
sunt erorile unchecked de tipul Exception. Aceste erori sunt acele erori care
nu tin de masina virtuala ci mai degraba de programator si care daca ar trebui
tratate de fiecare data ar ngreuna foarte mult scrierea codului. Astfel,ar fi
greu daca de fiecare data cand folosim un sir sau o matrice am verifica daca
nu cumva se foloseste un element din afara matricii.
Totusi, n aceste situatii se poate trata o eroare de acest tip.
Erorile de tip Exception care nu sunt de tipul RuntimeException sunt
erori care trebuie neaparat tratate ntr-un fel de programator (checked errors).

6.2

Crearea claselor exceptie

Pentru a crea o noua exceptie n Java, trebuie sa scriem o clasa care este
derivata direct sau indirect din clasa Exception.
De exemplu, pentru a crea o exceptie de tipul checked exception putem
scrie urmatorul cod:
class MatriciIncompatibileException extends Exception
{
public MatriciIncompatibileException()
{
}
public MatriciIncompatibileException(String message)
{
super(message);
}
}
Noua clasa pentru tratarea unei exceptii poate fi derivata si dintr-o alta
clasa de tipul exceptie.
Daca dorim crearea unei exceptii unchecked derivam clasa exceptie din
RuntimeException sau dintr-o clasa derivata din aceasta:

CAPITOLUL 6. TRATAREA EXCEPT


IILOR IN JAVA

96

class StackEmptyException extends RuntimeException


{
public StackEmptyException()
{
}
public StackEmptyException(String message)
{
super(message);
}
}

6.3

Aruncarea exceptiilor

Pentru a arunca o exceptie procedam n felul urmator folosim cuvantul


cheie throw dupa care trebuie sa folosim un obiect de tip exceptie:
public int pop()
{
if(isEmpty())
throw new StackEmptyException();
//codul pentru eliminarea unui element din stiva
}
Observatie: Daca avem de-a face cu o exceptie de tipul checked exception trebuie sa specificam ca aceasta metoda arunca o astfel de exceptie:
public static Matrice adunare(Matrice m1, Matrice m2)
throws MatriciIncompatibileException
{
if ((m1.noLines()!=m2.noLines()) ||
((m1.noCols()!=m2.noCols()))
throw new MatriciIncompatibileException();
//continua codul pentru aceasta metoda
}
Daca o metoda poate arunca mai multe exceptii de tipul checked atunci
acestea trebuie scrise toate n sectiunea throws separate prin virgula.

6.4

Prinderea exceptiilor

Prinderea exceptiilor n Java respecta urmatoarea sintaxa:

6.4. PRINDEREA EXCEPT


IILOR

97

try {
instr1;
instr2;
....
}
catch(Exception1 e){
//tratare exceptie 1
}
catch(Exception2 e){
//tratare exceptie 2
}
finally{
//aici am cod care se executa mereu
//indiferent daca a aparut exceptie sau nu
}
Daca ntre instructiunile din blocul try nu apare nici o eroare atunci se
executa toate aceste instructiuni dupa care se executa toate instructiunile
din blocul finally.
Daca la un moment dat apare eroare cand se executa o instructiune n
blocul try atunci executia instructiunilor din acel bloc se opreste si se executa
instructiunile doar din primul catch care prinde eroarea potrivita, dupa care
se executa instructiunile din blocul finally.
Dupa cum se poate observa se poate sa avem mai multe blocuri catch
dar un singur bloc finally. Trebuie remarcat, de asemenea, ca blocul finally
poate lipsi.
In momentul n care scriem cod n care la un moment dat poate aparea o
exceptie de tipul checked putem sa rezolvam problema n unul din urmatoarele
moduri:
Daca nu stim cum sa tratam eroarea si vrem ca ea sa fie tratata n
alta parte, putem sa aruncam eroarea mai departe. Pentru acest lucru
trebuie sa specificam ca acea metoda poate arunca eroarea respectiva
n clauza throws.
Daca nu stim cum sa tratam eroarea dar vrem sa aruncam mai departe
o alta eroare, putem folosi un bloc try-catch n care pe ramura de catch
corespunzatoare exceptiei sa aruncam o alta exceptie.
Putem trata exceptia direct n sectiunea catch corespunzatoare.

CAPITOLUL 6. TRATAREA EXCEPT


IILOR IN JAVA

98

6.5

Indicatii referitoare la utilizarea exceptiilor

Atunci cand se lucreaza cu tratarea exceptiilor este bine sa se urmeze


regulile prezentate n continuare:
Tratarea exceptiilor nu trebuie sa nlocuiasca testele care verifica daca
se poate efectua o operatie (motive de eficienta, n primul rand)
Nu trebuie sa separam codul ntr-o serie de blocuri try-catch atunci
cand este mai bine ca ele sa fie n acelasi bloc try.
Nu trebuie sa se foloseasca numai exceptii de tipul RuntimeException.
Trebuie de fiecare data sa identificam exceptia corecta si sa derivam
din acea exceptie.
Nu trebuie sa nabusim tratarea unei exceptii prin prinderea ntr-un
bloc catch care trateaza direct o exceptie de tipul Exception.
Nu toate exceptiile trebuie tratate n blocuri catch. In unele situatii
este de dorit sa se arunce mai departe acea exceptie.

6.6

Exemple

You might also like