Sunteți pe pagina 1din 88

UNIVERSITATEA TITU MAIORESCU

FACULTATEA DE INFORMATICA

Programarea in Java
- suport curs -

Lector univ. drd. Dascalescu Ana Cristina

- 2010 -

1
Cuprins

Capitolul 1 Introducere in limajul Java................................................................................................................................................. 3


1.1Caracteristici de baza ale limbajului Java ................................................................................................................................... 3
1.2 Tehnologia Java si componentele sale...................................................................................................................................... 4
1.2.2 Platforme de lucru Java ........................................................................................................................................................ 4
1.2.3 Tipuri de programe implementate de Java............................................................................................................................. 5
1.2.3Etapele dezvoltarii unei aplicatii Java..................................................................................................................................... 6
1.2 Structura lexicala a limbajului Java .............................................................................................................................................. 7
1.3 Tipuri de date si variabile in Java ................................................................................................................................................. 8
1.4 Expresii si operatori....................................................................................................................................................................11
1.5 Controlul executiei ......................................................................................................................................................................14
1.6 Siruri de caractere........................................................................................................................................................................17
Capitolul 2 Clase si obiecte .................................................................................................................................................................19
2.1 Ciclul de viata al unui obiect .......................................................................................................................................................20
2.1.1 Crearea obiectelor.................................................................................................................................................................20
2.2 Crearea claselor ...........................................................................................................................................................................22
2.2.1 Corpul unei clase ..................................................................................................................................................................23
2.2.2 Constructorii unei clase ........................................................................................................................................................24
2.2.3 Declararea variabilelor..........................................................................................................................................................25
2.3 Implementarea metodelor ............................................................................................................................................................27
2.3.1 Declararea metodelor............................................................................................................................................................27
2.3.2 Metode de instanta si metode de clasa ..................................................................................................................................29
2.3.3 Domeniul de vizibilitate (acces) al metodelor unei clase. Modificatori de acces..................................................................29
Capitolul 3 Extinderea claselor. Polimorfismul in Java.......................................................................................................................33
3.1 Mostenirea - concept fundamental al programarii orientata obiect..............................................................................................33
3.2 Caracteristicile unei ierarhi de clase ............................................................................................................................................35
3.3 Mostenirea simpla in Java ...........................................................................................................................................................35
3.4 Supraincarcarea (overloading) metodelor. Redefinirea (override) metodelor..............................................................................37
3.5 Implementarea polimorfismului .................................................................................................................................................39
3.6 Metode si clase abstracte .............................................................................................................................................................42
Capitolul 4 Interfete Java...................................................................................................................................................................50
4.1 Interfete .......................................................................................................................................................................................50
4.2 Crearea si folosirea interfetelor Java ...........................................................................................................................................50
4.3 Implementarea unei interfete .......................................................................................................................................................51
4.4 Implementarea unor interfete multiple.........................................................................................................................................52
Capitolul 5 Tratarea exceptiilor............................................................................................................................................................57
5.1 Tratarea erorilor...........................................................................................................................................................................57
5.2 Ierarhia de exceptii predefinite de platforma Java .......................................................................................................................58
5.3 Crearea si semnalarea propriilor exceptii ....................................................................................................................................64
Capitolul 6 Intarari si iesiri...................................................................................................................................................................68
6.1 Tipuri de fluxuri de intrare/iesire Java.........................................................................................................................................68
6.2 Fluxuri de octeti si clase predefinite Java ....................................................................................................................................69
6.3 Fluxuri de caractere si clase predefinite Java utilizate.................................................................................................................81
Capitolul 1 Introducere in limajul Java

Obiective:

- implementarea firmei Sun a limbajului Java: Platforma J2SE (Java 2 Platform, Standard Edition);
- tipuri de programe implementate de Java;
- etapele dezvoltarii unei aplicatii Java; structura unei aplicatii Java; metoda main( );
- utilizarea claselor de obiecte din pachetele predefinite (pachete API) Java; directiva import ;
instructiunea package;
- elemente de baza ale limbajului Java: identificatori, variabile, tipuri primitive de date;
constant,operatori, controlul executiei;
- afisarea datelor pe ecran: metodele print, println, readLine, read.

1.1 Caracteristici de baza ale limbajului Java

Scurt istoric

Inceptul limbajului Java este in toamna anului 1991, cand firma Sun Microsystems a finantat un
proiect cu numele Green condus de James Gosling. Tehnologia propusa a avut un impact
remarcabil asupra intregii comunitati a dezvoltatorilor de software, impunandu-se prin calitati
deosebite cum ar fi simplitate, robustete si nu in ultimul rand portabilitate.
Dupa patru ani de lucru, echipa Green finalizeaza specificatiile limbajului Java, astfel ca in anul
1995 firma Sun Microsystems vinde licenta firmelor IBM, Microsoft, Silicon Graphic, Adope si
Netscape.

Incepand cu anul 1998, cand a aparut versiunea 2 a limbajului (Java 2 Platform), Java a fost extins,
acoperind si mai multe directii de dezvoltare: programarea aplicatiilor enterprise (aplicatii de tip
server) precum si a celor adresate dispozitivelor cu resurse limitate, cum ar fi telefoanele mobile,
pager-e sau PDA-uri (mici dispozitive de calcul, de dimensiuni putin mai mari decat ale unui
telefon mobil, cu facilitati de agenda si capabile sa execute aplicatii intr-un mod relativ asemanator
cu cel al unui PC).

Caracteristicile de baza ale limbajului Java:

- Java este un limbaj simplu de folosit. Aceasta caracteristica poate fi considerata ca o reactie
directa la complexitatea considerabila a limbajului C/C++. Au fost indepartate din Java aspectele
cele mai derutante din C++ precum supraincarcarea operatorilor, mostenirea multipla sau pointer-
rii.

- Java este independent de arhitectura calculatorului pe care lucreaza si foarte portabil.


Compilatorul Java genereaza o secventa de instructiuni ale unei masini virtuale Java. Executia
aplicatiilor Java este interpretata. Singura parte din mediul de executie Java (JRE) care trebuie
portata de pe o arhitectura pe alta cuprinde interpretorul Java si o parte din bibliotecile standard care
depind de sistem. In acest fel aplicatii Java compilate pe o arhitectura SPARC de exemplu, pot fi

3
rulate fara recompilare pe un sistem bazat pe procesoare Intel. De altfel, mediul de executie Java
(JRE) este scris in C ceea ce il face foarte portabil.

- Interpretorul Java este gandit sa lucreze pe masini mici (memorie mica de ordinul sutelor de
KB), precum ar fi procesoarele cu care sunt dotate aparatele casnice si telefoanele mobile.

- Limbajul Java este orientat obiect. Cu el se pot crea clase de obiecte si instante ale acestora, se
pot incapsula informatiile, se pot mosteni variabilele si metodele de la o clasa la alta etc. Singura
caracteristica care lipseste este mostenirea multipla, dar pentru a suplini aceasta lipsa, Java ofera o
facilitate mai simpla, numita interfata, care permite definirea unui anumit comportament pentru o
clasa de obiecte, altul decat cel definit de clasa de baza. In Java orice element este un obiect, in
afara de datele primare. Din Java lipsesc functiile si variabilele globale.
- Limbajul Java este distribuit, avand implementate biblioteci pentru lucrul in retea.

- Limbajul Java este un limbaj cu securitate ridicata.

- Java are inclus suport nativ pentru aplicatii care lucreaza cu mai multe fire de executie
(programare paralela si concurenta). Acest suport este independent de sistemul de operare.

1.2 Tehnologia Java si componentele sale

In tehnologia Java sunt cuprinse patru componente:

1. Masina virtuala, denumita JVM (Java Virtual Machine) care interpreteaza fisierele cu extensia
.class ce contine “bytecode”.
“Bytecode” reprezinta cod compilat care este procesat de un program, denumit masina virtuala, si
nu cod executabil care ruleaza pe masina reala ce dispune de un procesor harwarde. “Bytecode”
este format din instructiuni generice care sunt convertite (transformate) de masina virtuala in
instructiuni specifice procesorului masinii reale. De aceea se spune ca aplicatiile Java sunt
independente de platforma (hardware si software). Masina virtuala Java mai este denumita si
interpretor Java.

2. Limbajul Java propriu-zis. Limbajul Java este orientat pe obiecte si se aseamana din punct de
vedere al sintaxei cu C++.

3. Un compilator care genereaza fisiere cu extensia .class. Compilatorul Java traduce


instructiunile scrise in limbajul Java (stocate in fisiere cu extensia .java) in instructiuni generice
“bytecode”(stocate in fisiere cu extensia .class) care sunt “intelese” de masina virtuala.

4. Biblioteca de clase Java, denumita si Java API. Aceasta biblioteca contine un set de
componente utile care pot fi reutilizate de programatori in diverse aplicatii informatice.

1.2.1 Platforme de lucru Java

Limbajul de programare Java a fost folosit la dezvoltarea unor tehnologii dedicate rezolvarii unor
probleme din cele mai diverse domenii. Aceste tehnologii au fost grupate in asa numitele platforme
de lucru, ce reprezinta seturi de librarii scrise in limbajul Java, precum si diverse programe utilitare,
folosite pentru dezvoltarea de aplicat¸ii sau componente destinate unei anume categorii de
utilizatori.
¾ J2SE (Standard Edition)
Este platforma standard de lucru ce ofera suport pentru crearea de aplicatii independente si
appleturi. De asemenea, aici este inclusa si tehnologia JavaWeb Start ce furnizeaza o modalitate
extrem de facila pentru lansarea si instalarea locala a programelo scrise in Java direct de peWeb,
oferind cea mai comoda solutie pentru distributia si actualizarea aplicatiilor Java.
¾ J2ME (Micro Edition)
Folosind Java, programarea dispozitivelor mobile este extrem de simpla, platforma de lucru J2ME
oferind suportul necesar scrierii de program dedicate acestui scop.
¾ J2EE (Enterprise Edition)
Aceasta platforma ofera API-ul necesar dezvoltarii de aplicatii complexe, formate din componente
ce trebuie sa ruleze in sisteme eterogene, cu informatiile memorate in baze de date distribuite, etc.
Tot aici gasim si suportul necesar pentru crearea de aplicatii si servicii Web, bazate pe componente
cum ar fi servleturi, pagini JSP, etc.

Toate distributiile Java sunt oferite gratuit si pot fi descarcate de pe Internet de la adresa
”http://java.sun.com”.

Platforma J2SE (Java 2 Platform, Standard Edition)

Produsul informatic (software-ul) care implementeaza platforma J2SE si care este folosit pentru
dezvoltarea de aplicatii specifice acesteia este J2SDK (Java 2 Software Development Kit,
Standard Edition) in diverse versiuni pe care le scoate firma si care este livrat gratuit (este un soft
liber).
Pentru a fi la curent cu ultimele versiuni ale produsului J2SDK se poate accesa adresa de internet
http://java.sun.com.
Produsul J2SDK include uneltele de creare a programelor Java (compilatorul si alte unelte necesare
pentru dezvoltare de programe) si unelte de executie ale acestora (masina virtuala, biblioteca de
clase Java API etc). Cele mai importante unelte incluse in J2SDK sunt:
- javac - compilatorul Java;
- java - interpretorul Java;
- appletviewer - folosit pentru testarea applet-urilor (aplicatii care ruleaza dintr-un navigator
de internet).
Pachetul Java 2 SDK este un set de programe care ruleaza, din lina de comanda a sistemului de
operare, utilitare bazate pe text, care nu au nevoie de o interfata grafica pentru utilizator.

1.2.2 Tipuri de programe implementate de Java

Cu ajutorul limbajului Java se pot dezvolta trei tipuri de programe:


1. Programe Java care se executa individual prin intermediul unui interpretor Java. Acestea se
incadreaza in programele “clasice” scrise in diverse limbaje de programare, cum ar
fi:C/C++, Pascal etc. Acest tip de programe Java sunt denumite aplicatii de sine statatoare.

2. Programe Java care se executa in interiorul unui navigator Internet, dintr-un document
HTML. Acest tip de programe Java sunt denumite applet-uri.

3. Aplicatii care se executa pe partea de server numite serveturi.

O aplicatie de sine statatoare este un program Java care se executa in afara browswer-ului Web .
Principlala caracteristica a acestui tip de aplicatie este incapsularea in cadrul unei clase principale a
unuei functii main() asemanatoare cu cea folosita in limbajul C, cu urmatoarea signatura:

5
public static void main(String args[] )

Aplicatiile care se executa pe partea de client sunt cele incarcate de pe un server si apoi executate
de programe speciale cum ar fi navigatoarele Web.
Un applet este un program Java care respecta o multime de reguli astfel incat sa poata rula in cadrul
unui navigator Web.

O aplicatie care se executa pe partea de server este o aplicatie care este rulata de server ca urmare a
unei cererei primate de acesta, iar rezultatul este trimis programului solicitant.
Un servlet este o componenta Web, scrisa in java, si care poate interactiona cu diferiti clienti
folosind o implementare a paradigmei cerere/raspuns bazata pe protocolul HTTP. Servturile extend
functionalitatea unui server, de obicei a unui server HTTP.

1.2.3 Etapele dezvoltarii unei aplicatii Java

1.Editarea setului de instructiuni de programare cu ajutorul unui editor de texte. In acest fel este
creat un fisier sursa, care are extensia .java.

2. Compilarea programului. Pentru aceasta operatie se lanseaza in executie un program special,


denumit compilator Java. Compilatorul analizeaza textul sursa al programului din punct de vedere
sintactic, semnaland eventualele erori. Daca programul nu contine erori sintactice, compilatorul
traduce acest program in codul masina pentru masina virtuala Java (un fel de calculator intr-un alt
calculator).
Rezultatul compilarii este unul sau mai multe fisiere de tip “bytecode”- o secventa de instructiuni
de asamblare pentru masina virtuala Java. Fisierul “bytecode” nu depinde de masina gazda si de
sistemul de operare pe care va fi executat programul. Masina virtuala mai este cunoscuta si ca
interpretor Java sau executor (runtime) Java.
Extensia fisierului “bytecode” rezultat in urma compilarii are extensia .class.

3. Conversia (transformarea), de catre interpretorul Java, a instructiunilor “bytecode” in


instructiuni inteligibile masinii gazda care apoi sunt executate. Conversia are loc la lansarea
executiei si anume instructiune cu instructiune.
In acest mod este asigurata portabilitatea si independenta de platforma.Un dezavantaj poate fi
considerat timpul mai mare de executie.
Procesul de complilare si executie poate fi reprezentat ca in figura 1.
O aplicatie Java este compusa din una sau mai multe clase care interactioneaza intre ele prin
intermediul metodelor. In grupul de clase, care formeaza o aplicatie Java, trebuie sa existe o clasa
care sa contina o metoda statica avand numele main.
Atunci cand se executa o aplicatie Java, masina virtuala va cauta si invoca automat metoda statica
avand numele main.
Observatie: Metoda main poate fi considerata ca fiind echivalentul Java a functiei main din
C/C++.

Cel mai simplu program Java (care nu face nimic) arata astfel:

class aplicatie
{
public static void main (String [ ] args)
{System.out.println(“Primul program Java”);}
}
Observatie: Tipul parametrilor metodei (functiei) main cat si tipul metodei, static void, sunt
obligatorii.

1.2 Structura lexicala a limbajului Java

Setul de caractere

Limbajului Java lucreaza in mod nativ folosind setul de caractere Unicode. Acesta este un standard
international care inlocuieste vechiul set de caractere ASCII si care foloseste pentru reprezentarea
caracterelor 2 octeti, ceea ce inseamna ca se pot reprezenta 65536 de semne, spre deosebire de
ASCII, unde era posibila reprezentarea a doar 256 de caractere. Primele 256 caractere Unicode
corespund celor ASCII, referirea la celelalte facandu-se prin \uxxxx, unde xxxx reprezinta codul
caracterului.

O alta caracteristica a setului de caractere Unicode este faptul ca intreg intervalul de reprezentare a
simbolurilor este divizat in subintervale numite blocuri, cateva exemple de blocuri fiind: Basic
Latin, Greek, Arabic, Gothic, Currency, Mathematical, Arrows, Musical, etc.
Mai jos sunt oferite cateva exemple de caractere Unicode.
• \u0030 - \u0039 : cifre ISO-Latin 0 - 9
• \u0660 - \u0669 : cifre arabic-indic 0 - 9
• \u03B1 - \u03C9 : simboluri grecesti _ − !
• \u2200 - \u22FF : simboluri matematice (8, 9, ;, etc.)
• \u4e00 - \u9fff : litere din alfabetul Han (Chinez, Japonez, Coreean)
Mai multe informatii legate de reprezentarea Unicode pot fi obtinute la adresa
”http://www.unicode.org”

Identificatori

Identificatorii, intalniti si sub denumirea de nume simbolice, au rolul de a denumi elemente ale
programului Java: constante, variabile, clase metode etc.
7
Din punct de vedere sintactic, un identificator este constituit dintr-o succesiune nelimitata de litere
si cifre Unicode, primul caracter fiind obligatoriu o litera (inclusiv ‘_’).
Limbajul Java este “case-sensitive”, adica face diferenta intre literele mici si literele mari. De aceea,
identificatorii sunt diferiti in functie daca sunt scrisi cu litere mici si mari.
Cuvintele-cheie (keywords) sunt identificatori speciali, cu inteles predefinit, care pot fi utilizati
numai in constructii sintactice in care sunt specificati. De exemplu: if, while etc. Toate cuvintele-
cheie se scriu numai cu litere mici.

Separatori

Separatorii au rolul de a separa unitatile sintactice:


- Ca separatori “generali” se utilizeaza caracterele albe: spatiu (‘ ‘), TAB (‘\t’), sfarsit de linie (‘\n’)
si comentariile.
- Separatorii specifici sun folositi, de exemplu, pentru a separa unele constructii sintactice:
variabilele sunt separate prin caracterul virgula (‘,’). Alti separatori specifici sunt ( ) { } [ ] .
- Delimitatorii sunt folositi, de exemplu, pentru:
- a delimita sfarsitul unei instructiuni sau al unei declaratii - caracterul punct si virgula (‘;’);
- a delimita o constanta de tip caracter - caracterul apostrof (‘);
- a delimita constantele sir de caractere(ghilimelele).

Comentarii

Comentariile sunt texte care vor fi ignorate de compilator, dar au rolul de a explicita si de a face
mai lizibil pentru programator anumite secvente de program.
In Java exista trei tipuri de comentarii:
- o succesiune de caractere incadrata intre /* si */ ; aceste comentarii pot fi formate din mai multe
linii;
- o succesiune de caractere pe mai multe linii care tin de documentatie, incadrate intre /** si */;
textul dintre cele doua secvente este automat mutat in documentatia aplicatiei de catre generatorul
automat de documentatie (javadoc);
- o succesiune de caractere care incepe cu // si se termina la sfarsitul unei singure linii.

1.3 Tipuri de date si variabile in Java

Variabila este o zona temporara de stocare, rezidenta in memoria RAM, care are un nume simbolic
(identificator) si stocheaza un anumit tip de date. Ea poate fi modificata pe parcursul executiei
programului.
In ciuda denumirii, in Java exista variabile care isi pot modifica valoarea si variabile care nu si-o
pot modifica, numite variabile finale. Asupra variabilelor finale vom reveni ulterior dupa
intelegerea conceptelor de clasa de obiecte.
Pentru utilizarea unei variabile intr-un program Java trebuie ca aceasta sa fie declarata. La
declararea variabilei trebuie specificat numele simbolic al variabilei, tipul acesteia si, eventual, o
valoare initiala care se atribuie variabilei.

Un tip de date defineste multimea de valori pe care variabila poate sa le stocheze, modul de
reprezentare a acestora in memorie, ca si setul de operatii pe care programul poate sa le realizeze cu
aceste date.
In limbajul Java a fost exact definita modalitatea de reprezentare a tipurilor primitive de date in
memorie. In acest fel, variabilele Java devin independente de platforma hardware si software pe
care lucreaza.
De asemenea, Java defineste o valoare implicita pentru fiecare tip de date, in cazul in care variabila
de un anumit tip nu a primit nici o valoare de la utilizator. Este o practica buna insa aceea ca
programele sa nu depinda niciodata de aceste initializari implicite. Regula ar fi deci urmatoarea:
nici o declararie fara initializare.
Limbajul Java accepta urmatoarele tipuri de baza: byte, short, int, long, float, double, char,
boolean, void.

Tabelul de mai jos prezinta o descrie generala a tipurilor primitive de date utilizate de Java.

Tip Valori Reprezentare in memorie

byte [-128, 127] Intreg pe 1 byte

short [-32768, 32767] Intreg pe 2 bytes

int [2.147.483.648, 2.147.483.648] Intreg pe 4 bytes

long [-263, 263 -1] Intreg pe 8 bytes

float 6 cifre semnificative [10-46, 1038] Virgula mobila pe 4 bytes

double 15 cifre semnificative [10-324, Virgula mobila pe 8 bytes


10308]

char coduri Unicod Pe 2 byte

boolean false sau true Pe un bit

Tipul boolean este folosit pentru memorarea unei valori de adevar sau fals. In Java aceste doua
valori le vom nota prin constantele (literali) true si respectiv false.
Orice variabila booleana nou creata primeste automat valoarea implicita false.
Tipul char este folosit pentru a reprezenta caractere de tip Unicode.
Orice variabila de tip caracter nou creata primeste automat ca valoare implicita caracterul null al
codului Unicode, “\u0000’.
Tipurile de date intregi sunt folosite pentru memorarea unor valori intregi cu semn. Conventia
folosita de Java pentru valorile intregi cu semn este reprezentarea in complement fata de doi.
Tipurile de date intregi sunt: byte, short, int, long.
Orice variabila de tip intreg (byte, short, int si long) nou creata primeste automat valoarea implicita
0.

9
Tipurile de date reale sunt folosite pentru memorarea unor valori reale sub forma de mantisa si
caracteristica. Tipurile de date reale sunt: float si double.
Valoarea implicita pentru variabilele de tip float este 0.0f, iar pentru variabilele de tip double este
0.0d.
In Java sunt definite cateva valori reale speciale:
1.Valoarea NaN (Not a Number) se obtine atunci cand se efectueaza o operatie a carei rezultat nu
este definit, de exemplu 0.0 / 0.0.

2. Valori folosite pe post de infinit pozitiv si negativ. Aceste valori pot rezulta in urma unor calcule.
Valorile descrise la pct. 1 si 2 sunt definite sub forma de constante si in ierarhia de clase predefinite
Java, si anume in clasa java.lang.Float si respectiv in clasa java.lang.Double. Numele constantelor
este: NaN, POSITIVE_INFINITY, NEGATIVE_INFINITY.
Pentru tipurile intregi si intregi lungi, precum si pentru tipurile flotante exista definite clase in
ierarhia de clase predefinite Java si anume:
- java.lang.Integer - pentru tipul int;
- java.lang.Long - pentru tipul long;
- java.lang.Float - pentru tipul float;
- java.lang.Double - pentru tipul double.

In fiecare dintre clase numerice prezentate sunt definite doua constante care reprezinta valorile
minime si maxime pentru tipurile respective. Aceste doua constante se numesc in mod uniform
MIN_VALUE si MAX_VALUE.

Tipul void
Tipul void este un tip special, pentru care multimea valorilor este vida. Acest tip se utilizeaza cand
este necesar sa se specifice absenta oricarei valori. De exemplu: pentru tipul de data a metodelor
care nu intorc nici un rezultat, cum a fost cazul metodei main ().

Constante
O constanata este folosita pentru a exprima in program o valoare pe care o poate lua tipurile
primitive de date si tipul sir de caractere.

Constantele intregi pot fi reprezentate in baza 10, 16 sau 8. Constantele intregi pot fi intregi
normale sau lungi. Constantele lungi se recunosc prin faptul ca se termina cu sufixul l sau L.
Pentru a reprezenta o constanta intreaga in baza 16 trebuie sa se adauge prefixul 0x sau 0X in fata
numarului. Pentru a reprezenta o constanta intreaga in baza 8 trebuie sa se adauge prefixul 0 (cifra
zero) in fata numarului.

Constantele reale care se pot reprezenta in memoria calculatorului sunt numere rationale din
intervalele specificate la tipurile float si double. Constantele reale pot fi specificate in notatia
obisnuita sau in format stiintific. Sufixul care indica tipul float poate fi f sau F iar sufixul care
indica tipul double poate fi d sau D. Daca nu este specificat nici un sufix, valoarea implicita este de
tip double.

Constantele de tip caracter sunt utilizate pentru a reprezenta caracterele Unicode.


Reprezentarea se face fie folosind o litera sau o cifra, fie o secventa escape.
Caracterele care au reprezentare grafica pot fi scrise intre apostroafe. Pentru cele care nu au
reprezentare grafica, se folosesc secventele escape sau secventele escape predefinite prezentate
deja. Intern, Java interpreteaza constantele de tip caracter ca pe un numar (codul Unicode al
caracterului respectiv). Ulterior, functiile de scriere vor transforma acest numar in caracterul
corespunzator.
Constantele de tip sir de caractere sunt cuprinse intre ghilimele. Caracterele care formeaza sirul de
caractere pot fi caractere grafice sau secvente escape ca cele prezentate la constantele de tip
caracter. Daca se doreste introducerea de caractere terminatoare de linie intr-un sir de caractere,
atunci se foloseste secventa escape \n in sirul de caractere respectiv.
Observatie: Un sir de caractere este, de fapt, o instanta a clasei de obiecte String declarata standard
in pachetul java.lang. Vom reveni asupra sirurilor de caractere intr-o lectie separata.

Sintaxa folosita pentru declararea de variabile este:


<tip> <nume_v1> [= <expresie>] [, <nume_v2> [=
<expresie2>] … ];
unde:
- <tip> - specifica tipul de data al variabilelor;
- <nume_v1>, <nume_v2>, … - specifica numele simbolic al variabilelor care se declara (adica,
identificatorii);
- <expresie1>, <expresie2>, … - specifica o expresie de initializare; expresia trebuie sa fie
evaluabila in momentul declararii; sunt optionale si sunt folosite pentru a atribui unor variabile
anumite valori initiale.

Observatie: Se pot declara simultan mai multe variabile de acelasi tip, separand numele lor prin
virgula.
O variabila trebuie sa fie declarata imediat inainte de a fi folosita. Locul unde este declarata o
variabila determina domeniul de vizibilitate si semnificatia ei.
Limbjul Java permite si definirea de constante. Modul cum se face definirea constantelor va fi
prezentata intr-o lectie separata destinata descrieri atributelor statice.
Exemple de declaratii de variabile ce pot fi folosite intr-un program:

int a, b=3, c=4;


char g;
float x=b*5.6, y;

1.4 Expresii si operatori

O expresie este compusa dintr-o succesiune de operanzi, legati prin operatori.


Un operand poate fi o constanta, o variabila, un apel de metoda, o expresie incadrata intre paranteze
rotunde.
Operatorii desemneaza operatiile care se executa asupra operanzilor si pot fi grupati pe categorii, in
functie de tipul operatiilor realizate.
Operatorii limbajului Java sunt unari (se aplica unui singur operand) sau sunt binari (se aplica
asupra a doi operanzi).

A. Operatorii aritmetici

Operatorii aritmetici sunt: ‘*’ - inmultirea; ‘/’ - impartirea; ‘%’ - restul impartirii intregi; ‘+’ -
adunarea; ‘-’ - scaderea.
De asemenea este folosit operatorul unar ‘-’ (minus) pentru schimbarea semnului, precum si
operatorul unar ‘+’ (plus) (introdus pentru simetrie).
Observatii:
1. Operatorul ‘%’ nu poate fi aplicat decat operanzilor intregi.
2. Operatorul ‘/’ poate fi aplicat atat operanzilor intregi, cat si operanzilor reali, dar functioneaza
diferit pentru operanzii intregi, fata de operanzii reali. Daca cei doi operanzi sunt numere intregi,
operandul ‘/’ are ca rezultat catul impartirii intregi (fara parte fractionara). Daca cel putin unul

11
dintre cei doi operanzi este un numar real, operandul ‘/’ furnizeaza rezultatul impartirii reale (cu
parte fractionara).
De exemplu pentru declaratiile de variabile:
int a=5, b=7
float x=3.5
se pot construi urmatoarele expresii: b%2 =1;
a/2=2;

B. Operatorii de incrementare/decrementare

Operatorul de incrementare este ‘++’. Operatorul de decrementare este ‘--’. Acesti operatori sunt
unari si au ca efect marirea (respectiv micsorarea) valorii operandului cu 1.
Limbajul Java permite doua forme pentru operatorii de incrementare / decrementare: forma
prefixata (inaintea operandului) si forma postfixata (dupa operand).
In cazul cand se foloseste operatorul de incrementare / decrementare in forma prefixata (inaintea
operandului), limbajul Java va incrementa / decrementa mai intai valoarea variabilei si apoi va
utiliza variabila intr-o alta expresie.
In cazul cand se foloseste operatorul de incrementare / decrementare in forma postfixata (dupa
operand), limbajul Java va utiliza mai intai valoarea variabilei intr-o alta expresie si apoi va efectua
operatia de incrementare / decrementare.
De exemplu, daca valoarea curenta a lui x este 5, atunci:
- evaluarea expresiei 3 * ++x conduce la rezultatul 18;
- evaluarea expresiei 3 * x++ conduce la rezultatul 15,
dupa care valoarea lui x va fi in ambele cazuri 6.
Operatorii de incrementare / decrementare pot fi aplicati operanzilor intregi, operanzilor in virgula
mobila si operanzilor de tipul char.

C. Operatori relationali

Operatorii relationali sunt operatori binari si desemneaza relatia de ordine in care se gasesc cei doi
operanzi: <, >, <=, >=.
Rezultatul aplicarii unui operator relational este true daca cei doi operanzi sunt in relatia indicata de
operator, si false, altfel.
De exemplu, expresiile logice:
4 > 6 are ca rezultat valoarea false,
8 <= 3+13 are ca rezultat valoarea true.

D. Operatori de egalitate

Operatorii de egalitate sunt folositi pentru testarea unei egalitati sau inegalitati intre doua valori.
Sunt operatori binari si arata relatia de egalitate (==) sau inegalitate (!=). Rezultatul aplicarii unui
operator de egalitate este true, daca cei doi operanzi sunt in relatia indicata de operator si false
altfel.
De exemplu, expresiile logice:
5 == 2+3 are ca rezultat valoarea true,
5 != 2+3 are ca rezultat valoarea false.

E. Operatori logici se aplica asupra unor operanzi de tip boolean.


Exista trei operatori logici globali:
- negatia logica (not) reprezentata cu !;
- conjunctie logica (si) reprezentata cu &&;
- disjunctie logica (sau) reprezentata cu | | .
F. Operatori de atribuire
Operatorii de atribuire sunt operatori binari care permit modificarea valorii unei variabile. Exista
un operator de atribuire simplu (=) si 10 operatori de atribuire compusi cu ajutorul operatorului ‘=‘
si al unui alt operator binar (aritmetic sau logic pe biti).
O varianta de sintaxa folosita este:
<nume_variabila> = <expresie>
Efectul aplicarii operatorului este: Se evalueaza <expresie>, apoi se atribuie variabilei
<nume_variabila> valoarea expresiei.
Observatie : <expresie> poate fi la randul ei o expresie de atribuire, caz in care se realizeaza o
atribuire multipla. Atunci cand compilatorul intalneste o operatie de atribuire multipla, el atribuie
valorile de la dreapta la stanga.
<nume_variabila1> = <nume_variabila2> = … = <nume_variabilan> = <expresie>;
Se foloseste atunci cand se doreste sa se atribuie aceeasi valoare mai multor variabile.
De exemplu:
total = 0;
suma = 0;
a = 0;
folosind atribuirea multipla rezulta:
total = suma = a = 0
O alta varianta de sintaxa folosita este:
<nume_variabila> <operator_binar> = <expresie>;
unde:
- <operator_binar> - este din multimea {*, /, %, +, -, <<, >>, &, |, ^}.
Efectul aplicarii operatorilor de atribuire compusi este echivalent cu instructiunea:
<nume_variabila> = <nume_variabila> <operator_binar> <expresie>;
De exemplu instructiunile:
total = total + 100;
jumatate = jumatate/2;
b = a = a*2;
sunt echivalente cu:
total+ =100;
jumatate/ =2;
b = a* = 2;
G. Operatorul de concatenare ( + ) de siruri de caractere este un operator binar folosit pentru
lipirea mai multor siruri de caractere
La concatenarea sirurilor de caractere, lungimea sirului rezultat este suma lungimii sirurilor care
intra in operatie. Carcterele din sirul rezultat sunt caracterele din primul sir, urmate de cele dintr-al
doilea sir in ordine. Daca cel de-al doilea operand este un tip primitiv de data, acesta este convertit
la un sir de caractere care sa reprezinte valoarea operandului.
De exemplu:
“Acesta este” + “un sir” este echivalent cu “Acesta este un sir”
“Variabila a are valoarea ” + 3 este echivalent cu “Variabila are valoarea 3”

Evaluarea unei expresii presupune calculul valorii expresiei, prin inlocuirea in expresie a fiecarei
variabile cu valoarea ei si a fiecarei functii cu valoarea returnata de functia respectiva si efectuarea
operatiilor specificate de operatori. In timpul evaluarii expresiei se tine cont de existenta
parantezelor, de asociativitate si de prioritatea operatorilor:
1. In cadrul unei expresii fara paranteze, se efectueaza operatiile in ordinea prioritatii operatorilor;
2. La prioritate egala, operatorii “vecini” actioneaza conform regulilor de asociativitate prezentate
in tabelul de mai sus.
3. Utilizarea parantezelor rotunde este mai puternica decat prioritatea operatorilor.

13
1.4 Controlul executiei

Instruct¸iunile Java pentru controlul executiei sunt foarte asemanatoare celor din limbajul C si pot fi
impartite in urmatoarele categorii:
¾ Instructiuni de decizie: if-else, switch-case
¾ Instructiuni de salt: for, while, do-while
¾ Instructiuni pentru tratarea except¸iilor: try-catch-finally, throw
¾ Alte instructiuni: break, continue, return, label;

A. Structuri alternative (de decizie)


Instructiunea de decizie if
Sintaxa instructiunii este:
if (<expresie>) <instructiune_1>;
[else <instructiune_2>];
unde:
- <expresie> - specifica expresia de evaluat;
- <instructiune_1>, <instructiune_2> - specifica instructiunile (simple sau compuse) de
executat.
Semantica: se evalueaza <expresie> si daca valoarea expresiei este true, se executa
<instructiune_1>, altfel se executa <instructiune_2>.
Nota: Instructiunea if poate sa faca parte dintr-o alta instructiune if sau else, adica instructiunile if
pot fi incluse (imbricate) in alte instructiuni if.
De exemplu, urmatorul program (denumit arie_triunghi.java) testeaza daca trei numere pot forma
laturile unui triunghi si daca da calculeaza aria triunghiului folosind formula lui Heron.
/** Utilizarea instructiunii if pentru determinarea ariei triunghiului*/
import java.io.*;
public class arie_triunghi
{
public static void main(String[] args) throws IOException
{
double x, y, z, p, aria;
String s;
System.out.print ("Introduceti x= ");
InputStreamReader isrx = new InputStreamReader(System.in);
BufferedReader brx = new BufferedReader(isrx);
s = brx.readLine();
x = Double.parseDouble(s);
System.out.print ("Introduceti y= ");
InputStreamReader isry = new InputStreamReader(System.in);
BufferedReader bry = new BufferedReader(isry);
s = bry.readLine();
y = Double.parseDouble(s);
System.out.print ("Introduceti z= ");
InputStreamReader isrz = new InputStreamReader(System.in);
BufferedReader brz = new BufferedReader(isrz);
s = brz.readLine();
z = Double.parseDouble(s);
if (x<=0 || y<=0 || z<=0)
System.out.println("Numerele introduse nu sunt laturi ale unui triunghi");
else
if (x+y<=z || x+z<=y || y+z<=x)
System.out.println("Numerele introduse nu sunt laturi ale unui triunghi");
else
{
p = (x+y+z)/2;
aria = Math.sqrt(p*(p-x)*(p-y)*(p-z));
System.out.println("Aria triunghiului = " + aria);
}
}
}
Instructiunea switch
Sintaxa instructiunii este:
switch (<expresie>)
{ case <constanta_1> : <grup_de_instructiuni_1>;
case <constanta_2> : <grup_de_instructiuni_2>;

case <constanta_n> : <grup_de_instructiuni_n>;
[default: <grup_de_instructiuni_n+1>;]
}
unde:
- <expresie> - specifica variabila sau expresia de evaluat;
- <constanta_1>, <constanta_2>, …, <constanta_n> - specifica valorile constantelor cu care se face
compararea rezultatului evaluarii expresiei;
- <grup_de_instructiuni_1>, … - o instructiune sau un grup de instructiuni care se executa in cazul
in care o alternativa case se potriveste.
Semantica: se evalueaza <expresie>; se compara succesiv valoarea expresiei cu valorile
constantelor <constanta_1>, <constanta_2>, …, <constanta_n> din alternativele case:
- daca se intalneste o constanta din alternativa case cu valoarea expresiei, se executa secventa de
instructiuni corespunzatoare si toate secventele de instructiuni care urmeaza, pana la intalnirea
instructiunii break sau pana la intalnirea acoladei inchise (}) care marcheaza sfarsitul instructiunii
switch;
- daca nici una dintre valorile constantelor din alternativa case nu coincide cu valoarea expresiei, se
executa secventa de instructiuni din alternativa default (alternativa implicita sau prestabilita).
Observatii:
1. Spre deosebire de if-else, care permite selectarea unei alternative din maximum doua posibile,
switch permite selectarea unei alternative din maximum n+1 posibile.
2. In instructiunea if-else se executa instructiunea (instructiunile) corespunzatoare valorii expresiei
si atat, in timp ce in instructiunea switch se executa si toate secventele de instructiuni ale
alternativelor case urmatoare.

B. Structuri repetitive (iterative)


Instructiunea while
Sintaxa instructiunii este:
while (<expresie>)
<instructiune>;
unde:
- <expresie> - specifica expresia de testat;
Semantica: se evalueaza <expresie>:
- daca valoarea expresiei este false se iese din ciclul while;

15
- daca valoarea expresiei este true, se executa instructiunea atita tip cat valoarea expresiei
este true.

Instructiunea do-while
Sintaxa instructiunii este:
do
<instructiune>;
while (<expresie>);
unde:
- <instructiune> - o instructiune simpla de executat;
- <expresie> - specifica expresia de testat (de evaluat);
Semantica: se executa instructiunea si apoi se evalueaza expresia:
- daca valoarea expresiei este false se iese din ciclul do-while;
- daca valoarea expresiei este true se executa instructiunea (din ciclul do-while) atita tip cat
valoarea expresiei este adevarata.

Instructiunea for
Este folosita pentru efectuarea unor prelucrari de un anumit numar de ori.
Sintaxa instructiunii este:
for (<valoare_initiala>; <conditie_sfarsit>; <valoare_increment>)
<instructiune>;
Instructiunea for foloseste, de obicei, o variabila denumita variabila de control care indica de cate
ori s-a executat instructiunea (<instructiune>) din corpul ciclului. Instructiunea for contine patru
sectiuni:
- sectiunea <valoare_initiala> atribuie variabilei de control o valoare initiala, care, de cele mai
multe ori, este 0 sau 1;
- sectiunea <conditie_sfarsit> testeaza valoarea variabilei de control pentru a stabili daca programul
a executat instructiunea de atatea ori cat s-a dorit;
- sectiunea <valoare_increment> adauga (scade), de obicei, valoarea 1 la variabila de control, de
fiecare data, dupa ce se executa instructiunea din corpul ciclului; valoarea de incrementare sau
decrementare poate fi diferita de 1;
- sectiunea <instructiune> reprezinta instructiunea (sau instructiunile) care se doreste (doresc) a fi
repetata (repetate).

1.6 Afisarea datelor pe ecran


In limbajul Java nu exista instructiuni specializate pentru citirea/scrierea datelor. Aceste operatii se
realizeaza prin intermediul unor metode existente in pachetele API ale limbajului. Intrarea si iesirea
in Java se realizeaza cu ajutorul claselor de obiecte din pachetul predefinit java.io. Orice program
care foloseste rutinele de intrare/iesire trebuie sa cuprinda instructiunea:
import java.io.*
Conceptul fundamental in operatiile de intrare/iesire in limbajul Java este fluxul de intrare/iesire
(stream).

Daca stream-ul este de intrare, succesiunea de biti “curge” dinspre exterior (in acest caz, de la
tastatura) catre memoria calculatorului.
Daca stream-ul este de iesire, secventa de biti “curge” dinspre memoria calculatorului catre exterior
(in acest caz, catre ecran).
Java ofera trei fluxuri predefinite pentru operatii I/O standard:
- System.in pentru intrarea standard de la tastatura;
- System.out pentru iesirea standard la ecranul calculatorului;
- System.err pentru fluxul de erori.
Pentru afisarea datelor la ecranul calculatorului se folosesc metodele print si println. Spre
deosebire de C/C++ care dispun de un numar foarte mare de optiuni de formatare, afisarea in Java
se face exclusiv prin concatenare de String-uri fara nici o optiune de formatare.
Observatie: String-urile sunt obiecte Java care descriu sirurile de caractere si le vom studia separat
intr-o lectie viitoare. Sa retinem ca prin concatenarea a doua siruri se obtine un nou sir de caractere
care uneste cele doua siruri initiale. Operatorul de concatenare a doua siruri de caractere folosit de
Java este semnul + (plus).
Sintaxa folosita la apelul metodei print este:
System.out.print (<expresie>);
unde:
- <expresie> - este numele unei variabile de un tip de data sau este o expresie care foloseste
operatorul de concatenare pentru siruri de caractere; daca nu toti operanzii din expresie sunt siruri
de caractere, ci alte tipuri primitive de date atunci Java face o conversie temporara la tipul String.
Efectul apelului metodei print este acela ca se realizeaza afisarea la ecran a variabilei data ca
parametru si nu se face salt la o linie noua.
Sintaxa folosita la apelul metodei println este:
System.out.println (<expresie>);
unde:
- <expresie> - este numele unei variabile de un tip de data sau este o expresie care foloseste
operatorul de concatenare pentru siruri de caractere.
Efectul apelului metodei println este acela ca se realizeaza afisarea la ecran a variabilei data ca
parametru si se face salt la o linie noua.
Metoda println se poate apela si fara parametrii, adica in forma:
System.out.println( ); caz in care se face numai un salt la o linie noua fara sa se afiseze nimic.

1.5 Siruri de caractere


In Java, un sir de caractere poate fi reprezentat printr-un vector format din elemente de tip char, un
obiect de tip String sau un obiect de tip StringBuffer.

Daca un ¸sir de caractere este constant (nu se doreste schimbarea continutului sau pe parcursul
executiei programului) atunci el va fi declarat de tipul String, altfel va fi declarat de tip
StringBuffer. Diferenta principala intre aceste clase este ca StringBuffer pune la dispozitie metode
pentru modificarea continutului sirului, cum ar fi: append, insert, delete, reverse.

Uzual, cea mai folosita modalitate de a lucra cu siruri este prin intermediul clasei String, care are ¸si
unele particularitati fata de restul claselor menite sa simplifice cat mai mult folosirea sirurilor de
caractere. Clasa StringBuffer va fi utilizata predominant in aplicatii dedicate procesarii textelor cum
ar fi editoarele de texte.
Exemple echivalente de declarare a unui sir:
String s = "abc";
String s = new String("abc");
char data[] = {’a’, ’b’, ’c’};
String s = new String(data);

Concatenarea ¸sirurilor de caractere se face prin intermediul operatorului + sau, in cazul ¸sirurilor
de tip StringBuffer, folosind metoda append.
String s1 = "abc" + "xyz";
String s2 = "123";
String s3 = s1 + s2;

17
In Java, operatorul de concatenare + este extrem de flexibil, in sensul ca permite concatenarea
sirurilor cu obiecte de orice tip care au o reprezentare de tip ¸sir de caractere. Mai jos, sunt cateva
exemple:
System.out.print("Vectorul v are" + v.length + " elemente");
String x = "a" + 1 + "b"

Teste de control:

1.1 Fie urmatoarea declaratie Java:

public private int h;

Care armatii sunt adevarate:


A. Variabila h va fi accesata in mod public, deoarece se ia in considerare primul modicator de
acces;
B. Variabila h va fi accesata in mod private, deoarece se ia in considerare ultimul modicator de
acces;
C. Va fi o eroare la compilare deoarece o variabila nu poate fi in acelasi timp accesata public si
private;
D. Nici una din variantele de mai sus;

1.2 Ce rezulta din urmatorul fragment de cod Java?


int x=1;
String []names={"Andreea","Matei","Ana"};
names[--x]+=".";
for(int i=0;i<names.length;i++)
System.out.println(names[i]);
A. Output-ul include Andreea;
B. Output-ul include Matei;
C. Output-ul include Ana;
D. Nimic din cele de mai sus;

1.3 Fie urmatorul program Java:


public class Program{
static void f(int k){
switch(k){
default: System.out.print("i "); break;
case1: System.out.print("1 "); break;
case 2:case3: System.out.print("21 "); break;
case 4:case5: System.out.print("26 ");
}}
public static void main(String []args){
for(int i=0;i<6;i++)
f(i);
}}
Care afrmatii sunt false?
A. Eroare la compilare;
B. Programul se compileaza si la executie afiseaza i 1 21 21 26 26 ;
C. Programul se compileaza si la executie afiseaza i 1 21 26 ;
D. Programul se compileaza si la executie afiseaza i 1 21 21 26 26 i;
1.4 Fie urmatorul cod Java:
byte b=-7 >>> 1;
System.out.println(b);

Ce se poate spune despre acest cod, daca este integrat intr-un program Java?
A. Va produce eroare la compilare;
B. Va produce eroare la executie;
C. Programul se compileaza si la executie afiseaza -3;
D. Programul se compileaza si la executie afiseaza -4;

1.5 Fie urm¸atorul program Java:


public class Program{
static void f(int k){
switch(k){
default: System.out.print("i "); break;
case1: System.out.print("1 "); break;
case 2:case3: System.out.print("23 "); break;
case 4:case5: System.out.print("45 ");
}
}
public static void main(String []args){
for(int i=0;i<6;i++)
f(i);
}
}
Care armatii sunt adevarate?
A. Programul produce eroare la compilare;
B. Programul se compileaza si la executie afiseaza i 1 23 23 45 45 ;
C. Programul se compileaza si la executie afiseaza i 1 23 45 ;
D. Programul se compileaza si la executie afiseaza i 1 23 23 45 45 i;

1.6 Secvenţa urmatoare:


public class test {
public static void main(String args[])
{int a=3;
int b=(a=2)*a;
int c=b*(b=5);
System.out.println(“a=”+a+”b=”+b+”c=”+c);}}
Afisează la execuţie:
a). a=2 b=4 c=20;
b). a=2 b=5 c=20;
c). a=3 b=6 c=30;
d). a=2 b=5 c=25;

1.7 Secvenţa uramatoare:


public class test {
public static void main(String args[])
{int v[ ]={-2,4,-5,-6,0,2},suma=0;
for(i=0;i<5;i++)
if(v[i]<-2) suma+=v[i];
System.out.println(“suma”+suma);}}
Afiseaza:
19
a). eroare la compilare;
b). afiseaza 0;
c) afiseaza -11;
d). afiseaza -13;

Capitolul 2 Clase si obiecte

Obiective:

- ciclul de viata al obiectelor;


- definirea claselor;
- structura unei clase Java;
- metode membre si constructori;

2.1 Ciclul de viata al unui obiect

2.1.1 Crearea obiectelor

In Java, ca in orice limbaj de programare orientat-obiect, crearea obiectelor se realizeaza prin


instantierea unei clase si implica urmatoarele lucruri:
¾ Declararea
Presupune specificarea tipului acelui obiect, cu alte cuvinte specificarea clasei acestuia
NumeClasa numeObiect;

¾ Instantierea
Se realizeaza prin intermediul operatorului new si are ca efect crearea efectiva a obiectului cu
alocarea spatiului de memorie corespunzator.
numeObiect = new NumeClasa();

¾ Initializarea
Se realizeaza prin intermediul constructorilor clasei respective. Initializarea este de fapt parte
integranta a procesului de instantiere, in sensul ca imediat dupa alocarea memoriei ca efect al
operatorului new este apelat constructorul specificat. Parantezele rotunde de dupa numele clasei
indica faptul ca acolo este de fapt un apel la unul din constructorii clasei si nu simpla specificare a
numelui clasei.
Mai general, instantierea si init¸ializarea apar sub forma:
numeObiect = new NumeClasa([argumente constructor]);

Sa consideram urmatorul exemplu, in care declaram si instantiem doua obiecte din clasa Rectangle,
clasa ce descrie suprafete grafice rectangulare, definite de coordonatele coltului stanga sus
(originea) ¸si latimea, respective inaltimea.
Rectangle r1, r2;
r1 = new Rectangle();
r2 = new Rectangle(0, 0, 100, 200);
In primul caz Rectangle() este un apel catre constructorul clasei Rectangle care este responsabil cu
init¸ializarea obiectului cu valorile implicite. Dupa cum observam in al doilea caz, initializarea se
poate face si cu anumiti parametri, cu conditia sa existe un constructor al clasei respective care sa
accepte parametrii respectivi.
Fiecare clasa are un set de constructori care se ocupa cu initializare obiectelor nou create. De
exemplu, clasa Rectangle are urmatorii constructori:
public Rectangle()
public Rectangle(int latime, int inaltime)
public Rectangle(int x, int y, int latime, int inaltime)
public Rectangle(Point origine)
public Rectangle(Point origine, int latime, int inaltime)
public Rectangle(Point origine, Dimension dimensiune)

Declararea, instantierea si initializarea obiectului pot aparea pe aceeasi linie (cazul cel mai uzual):
Rectangle patrat = new Rectangle(0, 0, 100, 100);

Folosirea obiectelor

Odata un obiect creat, el poate fi folosit in urmatoarele sensuri: aflarea unor informatii despre
obiect, schimbarea starii sale sau executarea unor actiuni. Aceste lucruri se realizeaza prin aflarea
sau schimbarea valorilor variabilelor sale, respectiv prin apelarea metodelor sale.
Referirea valorii unei variabile se face prin obiect.variabila De exemplu clasa Rectangle are
variabilele publice x, y, width, height, origin.
Aflarea valorilor acestor variabile sau schimbarea lor se face prin constructii de genul:
Rectangle patrat = new Rectangle(0, 0, 100, 200);
System.out.println(patrat.width); //afiseaza 100
patrat.x = 10;
patrat.y = 20; //schimba originea
patrat.origin = new Point(10, 20); //schimba originea
Accesul la variabilele unui obiect se face in conformitate cu drepturile de acces pe care le ofer˘a
variabilele respective celorlalte clase.

Apelul unei metode se face prin obiect.metoda([parametri]).


Rectangle patrat = new Rectangle(0, 0, 100, 200);
patrat.setLocation(10, 20); //schimba originea
patrat.setSize(200, 300); //schimba dimensiunea
Se observa ca valorile variabilelor pot fi modificate indirect prin intermediul metodelor sale.
Programarea orientata obiect descurajeaza folosirea directa a variabilelor unui obiect deoarece
acesta poate fi adus instari inconsistente (ireale). In schimb, pentru fiecare variabil˘a care descrie
starea obiectului trebuie sa existe metode care sa permita schimbarea/aflarea valorilor variabilelor
sale. Acestea se numesc metode de accesare, sau metode setter - getter si au numele de forma
setVariabila, respectiv getVariabila.
patrat.width = -100; //stare inconsistenta
patrat.setSize(-100, -200); //metoda setter
//metoda setSize poate sa testeze daca noile valori sunt
//corecte si sa valideze sau nu schimbarea lor
Rolul operatorului new
La folosirea operatorului new se executa urmatoarele:
- se creaza o noua instanta a clasei date;
- se aloca memorie pentru aceasta instanta;
- se apeleaza o metoda speciala a clasei numita constructor.

21
Constructorii reprezinta metode speciale pentru crearea si initializarea noilor instante ale claselor.
Constructorii initializeaza noul obiect si variabilele sale, creaza orice alte obiecte de care are nevoie
obiectul creat si realizeaza orice alte operatii de care obiectul are nevoie la initializarea sa.
Intr-o clasa pot exista mai multe definitii de constructori, fiecare avand un numar diferit de
argumente sau de tipuri.
Cand se foloseste operatorul new, se pot specifica diferite argumente in lista de argumente si va fi
apelat constructorul corespunzator pentru acele argumente.
Intr-o clasa pot fi definiti oricati constructori se doresc pentru a implementa comportamentul clasei.

Distrugerea obiectelor

Multe limbaje de programare impun ca programatorul sa tina evidenta obiectelor create si sa le


distruga in mod explicit atunci cand nu mai este nevoie de ele, cu alte cuvinte sa administreze
singur memoria ocupata de obiectele sale.

In Java programatorul nu mai are responsabilitatea distrugerii obiectelor sale intrucat, in momentul
rularii unui program, simultan cu interpretorul Java, ruleaza si un proces care se ocupa cu
distrugerea obiectelor care nu mai sunt folosite. Acest proces pus la dispozit¸ie de platforma Java de
lucru se numeste garbage collector (colector de gunoi), prescurtat gc.

2.2 Crearea claselor

Clasele reprezinta o modalitate de a introduce noi tipuri de date intr-o aplicatie Java, cealalta
modalitate fiind prin intermediul interfetelor.
Declararea unei clase respecta urm˘atorul format general:
[<modificatori_acces>] [<modificatori_clasa>] class <nume_clasa> [<clauze_s>]
{<corpul_clasei>}
unde:
- <modificatori_acces> - specifica domeniul de vizibilitate (folosire sau acces) al clasei;
modificatorul de acces este optional si poate fi: public;
- <modificatori_clasa> - specifica tipul clasei definite; modificatorul clasei este optional si poate fi:
abstract, final;
- <nume_clasa> - specifica numele clasei de obiecte; este de preferat ca numele clasei sa inceapa cu
o litera majuscula si daca numele clasei contine in interior mai multe cuvinte, aceste cuvinte sa
inceapa cu o litera majuscula;
- <clauze_s> - specifica anumite clauze referitoare la pozitia pe care o ocupa clasa in ierarhia de
clase din care face parte (clauza extends) sau daca aceasta clasa foloseste o interfata (clauza
inplements); despre aceste clauze vom vorbi intr-o lectie viitoare.
- <corpul_clasei> - variabilele clasei (de instana si de clasa) si metodele care lucreaza cu acestea,
numite la un loc membrii clasei.
Observatie: Continutul (corpul) unei clase nu este o succesiune de instructiuni.

Modificatorii pentru tipurile de clasa


O clasa poate fi:
- abstracta, caz in care folosim modificatorul abstract;
- finala, caz in care folosim modificatorul final.
In cazul in care declaram o clasa de obiecte ca fiind abstracta, compilatorul va interzice instantierea
acestei clase, adica nu se permite crearea de obiecte din aceasta clasa.
In cazul in care declaram o clasa de obiecte ca fiind finala, compilatorul va interzice ca pornind de
la aceasta clasa sa se defineasca subclase. Vom reveni intr-o lectie viitoare.
Nota:In cazul in care se declara, in acelasi timp, o clasa de obiecte ca fiind abstracta si finala,
eroarea va fi semnalata la compilare, pentru ca cei doi modificatori se exclud.

Modificatorii de acces
In cazul in care declaram o clasa de obiecte ca fiind publica, atunci aceasta clasa poate fi folosita
(accesata) si din exteriorul pachetului din care face parte.
Daca o clasa nu este declarata ca fiind de tip public atunci ea va putea fi folosita (accesata) doar de
clasele din cadrul aceluiasi pachet. Acest tip de acces la o clasa se numeste package-friendly si este
implicit in Java.
Nota: Toate clasele care nu fac parte din nici un pachet, sunt considerate automat ca facand parte
din acelasi pachet implicit. Ca o consecinta, accesul de tip friendly se aplica pentru toate aceste
clase. Acesta este motivul pentru care vizibilitatea nu este afectata daca se omite modificatorul
public pentru clasele care nu fac parte dintr-un pachet. Totusi, aceasta modalitate de folosire a
accesului de tip friendly nu este recomandata.

2.2.1 Corpul unei clase

Corpul unei clase urmeaza imediat dupa declararea clasei si este cuprins intre acolade. Continutul
acestuia este format din:
¾ Declararea si, eventual, initializarea variabilelor de instanta si de clasa (cunoscute impreuna
ca variabile membre).
¾ Declararea ¸si implementarea constructorilor.
¾ Declararea ¸si implementarea metodelor de instanta si de clasa (cunoscute impreuna ca
metode membre).
¾ Declararea unor clase imbricate (interne).

Spre deosebire de C++, nu este permisa doar declararea metode in corpul clasei, urmand ca
implementare sa fie facuta in afara ei. Implementarea metodelor unei clase trebuie sa se faca
obligatoriu in corpul clasei.
// C++
class A {
void metoda1();
int metoda2() {
// Implementare
}
}
A::metoda1() {
// Implementare
}
// Java
class A {
void metoda1(){
// Implementare
}
void metoda2(){
// Implementare
}
}
Variabilele unei clase pot avea acela¸si nume cu metodele clasei, care poate fi chiar numele clasei,
fara a exista posibilitatea aparitiei vreunei ambiguitati din punctul de vedere al compilatorului.
Acest lucru este insa total nerecomandat daca ne gandim din perspectiva lizibilitatii codului,
dovedind un stil ineficient de progamare.
23
class A {
int A;
void A() {};
// Corect pentru compilator
// Nerecomandat ca stil de programare
}

2.2.2 Constructorii unei clase

Constructorii unei clase sunt metode speciale care au acelasi nume cu cel al clasei, nu returneaza
nici o valoare si sunt folositi pentru initializarea obiectelor acelei clase in momentul instantierii lor.
class NumeClasa {
[modificatori] NumeClasa([argumente]) { // Constructor}}
O clasa poate avea unul sau mai mult¸i constructori care trebuie insa sa difere prin lista de
argumente primite. In felul acesta sunt permise diverse tipuri de init¸ializ˘ari ale obiectelor la
crearea lor, in functie de numarul parametrilor cu care este apelat constructorul.

Sa consideram ca exemplu declararea unei clase care descrie notiunea de dreptunghi ¸si trei posibili
constructori pentru aceasta clasa.
class Dreptunghi {
double x, y, w, h;
Dreptunghi(double x1, double y1, double w1, double h1) {
// Cel mai general constructor
x=x1; y=y1; w=w1; h=h1;
System.out.println("Instantiere dreptunghi");
}
Dreptunghi(double w1, double h1) {
// Constructor cu doua argumente
x=0; y=0; w=w1; h=h1;
System.out.println("Instantiere dreptunghi");
}
Dreptunghi() {
// Constructor fara argumente
x=0; y=0; w=0; h=0;
System.out.println("Instantiere dreptunghi");
}
}
Constructorii sunt apelat¸i automat la instant¸ierea unui obiect. In cazul in care dorim sa apelam
explicit constructorul unei clase folosim expresia
this( argumente ),
care apeleaza constructorul corespunzator (ca argumente) al clasei respective.
Aceasta metoda este folosita atunci cand sunt implementati mai multi constructori pentru o clasa,
pentru a nu repeta secventele de cod scrise deja la constructorii cu mai multe argumente (mai
generali). Mai eficient, fara repeta aceleasi secvente de cod in toti constructorii (cum ar fi afisarea
mesajului ”Instantiere dreptunghi”), clasa de mai sus poate fi rescrisa astfel:
class Dreptunghi {
double x, y, w, h;
Dreptunghi(double x1, double y1, double w1, double h1) {
// Implementam doar constructorul cel mai general
x=x1; y=y1; w=w1; h=h1;
System.out.println("Instantiere dreptunghi");
}
Dreptunghi(double w1, double h1) {
this(0, 0, w1, h1);
// Apelam constructorul cu 4 argumente
}
Dreptunghi() {
this(0, 0);
// Apelam constructorul cu 2 argumente
}
}

Constructorul implicit

Constructorii sunt apelati automat la instant¸ierea unui obiect. In cazul in care scriem o clasa care
nu are declarat nici un constructor, sistemul ii creeaza automat un constructor implicit, care nu
primeste nici un argument si care nu face nimic. Deci prezenta constructorilor in corpul unei clase
nu este obligatorie. Daca insa scriem un constructor pentru o clasa, care are mai mult de un
argument, atunci constructorul implicit (fara nici un argument) nu va mai fi furnizat implicit de
catre sistem. Sa consideram, ca exemplu, urmatoarele declaratii de clase:
class Dreptunghi {
double x, y, w, h;
// Nici un constructor
}
class Cerc {
double x, y, r;
// Constructor cu 3 argumente
Cerc(double x, double y, double r) { ... };
}
S˘a consider˘am acum dou˘a instant¸ieri ale claselor de mai sus:
Dreptunghi d = new Dreptunghi();
// Corect (a fost generat constructorul implicit)
Cerc c;
c = new Cerc();
// Eroare la compilare !
c = new Cerc(0, 0, 100);
// Varianta corecta
Constructorii unei clase pot avea urmatorii modificatori de acces:
public, protected, private si cel implicit.
• public: in orice alta clasa se pot crea instante ale clasei respective.
• protected : doar in subclase pot fi create obiecte de tipul clasei respective.
• private :in nici o alta clasa nu se pot instantia obiecte ale acestei clase. O astfel de clasa poate
contine metode publice care s˘a fie responsabile cu crearea obiectelor, controland in felul acesta
diverse aspecte legate de instant¸ierea clasei respective.
• implicit : doar in clasele din acelasi pachet se pot crea instante ale clasei respective.

2.2.3 Declararea variabilelor

Variabilele membre ale unei clase se declara de obicei inaintea metodelor, desi acest lucru nu este
impus de catre compilator.
class NumeClasa {
// Declararea variabilelor

25
// Declararea metodelor
}
Variabilele membre ale unei clase se declara in corpul clasei si nu in corpul unei metode, fiind
vizibile in toate metodele respectivei clase. Variabilele declarate in cadrul unei metode sunt locale
metodei respective.

Declararea unei variabile presupune specificarea urm˘atoarelor lucruri:


• numele variabilei
• tipul de date al acesteia
• nivelul de acces la acea variabila din alte clase
• daca este constanta sau nu
• daca este variabila de instanta sau de clasa
• alti modificatori
Generic, o variabila se declara astfel:
[modificatori] Tip numeVariabila [ = valoareInitiala ];
unde un modificator poate fi :
• un modificator de acces : public, protected, private;
• unul din cuvintele rezervate: static, final, transient, volatile;

Exemple de declaratii de variabile membre:


class Exemplu {
double x;
protected static int n;
public String s = "abcd";
private Point p = new Point(10, 10);
final static long MAX = 100000L;
}

Modificatori:
• static
Prezenta lui declara ca o variabila este variabila de clasa si nu de instanta.
int variabilaInstanta ;
static int variabilaClasa;
• final
Indica faptul ca valoarea variabilei nu mai poate fi schimbata, cu alte cuvinte este folosit pentru
declararea constantelor.
final double PI = 3.14 ;
...
PI = 3.141; // Eroare la compilare !

Prin conventie, numele variabilelor finale se scriu cu litere mari. Folosirea lui final aduce o
flexibilitate sporita in lucrul cu constante, in sensul ca valoarea unei variabile nu trebuie specificata
neaparat la declararea ei (ca in exemplul de mai sus), ci poate fi specificata ¸si ulterior intr-un
constructor, dupa care ea nu va mai putea fi modificata.
class Test {
final int MAX;
Test() {
MAX = 100; // Corect
MAX = 200; // Eroare la compilare !
}
}
2.3 Implementarea metodelor

2.3.1 Declararea metodelor

Metodele sunt responsabile cu descrierea comportamentului unui obiect. Intrucat Java este un
limbaj de programare complet orientat-obiect, metodele se pot gasi doar in cadrul claselor. Generic,
o metoda se declara astfel:
[modificatori] TipReturnat numeMetoda ( [argumente] )
[throws TipExceptie1, TipExceptie2, ...]
{
// Corpul metodei
}
unde un modificator poate fi :
• un specificator de acces : public, protected, private (
• unul din cuvintele rezervate: static, abstract, final, native, synchronized;

Modificatorii care pot fi specificati pentru o metoda:


¾ static
Prezenta lui declara ca o metoda este de clasa si nu de instanta.
void metodaInstanta();
static void metodaClasa();
¾ abstract
Permite declararea metodelor abstracte. O metoda abstracta este o metoda care nu are implementare
si trebuie obligatoriu sa faca parte dintr-o clasa abstracta.
¾ final
Specifica faptul ca acea metoda nu mai poate fi supradefinita in subclasele clasei in care ea este
definita ca fiind finala. Acest lucru este util daca respectiva metoda are o implementare care nu
trebuie schimbata sub nici o forma in subclasele ei, fiind critica pentru consistent starii unui obiect.
De exemplu, studentilor unei universitati trebuie sa li se calculeze media finala, in functie de notele
obtinute la examene, in aceeasi maniera, indiferent de facultatea la care sunt.
class Student {
...
final float calcMedie(float note[], float ponderi[]) {
...
}
...
}
class StudentInformatica extends Student {
float calcMedie(float note[], float ponderi[]) {
return 10.00;
}
}// Eroare la compilare !

27
Apelul metodelor
Apelul unei metode definita intr-o clasa de obiecte se realizeaza in mai multe moduri:
- prin crearea si utilizarea unei instante a clasei in care a fost definita metoda sau a unei subclase a
clasei respective (ca regula generala de invocare a unei metode); in acest caz se foloseste operatorul
punct ( . ), in stinga acestuia punandu-se numele instantei, iar in dreapa acestuia punandu-se numele
metodei;
- prin simpla folosire a numelui sau, in cazul in care clasa in care este apelata metoda este aceeasi
cu clasa in care a fost definita; aceasta modalitate este folosita daca atat metoda apelanta cat si
metoda apelata sunt fie numai metode de instanta, fie numai metode de clasa;
- prin folosirea operatorului punct ( . ), in stanga acestuia punandu-se numele clasei in care a fost
definita, iar in dreapta acestuia punandu-se numele metodei; aceasta modalitate este folosita numai
daca metoda este definita ca metoda de clasa.

Programele urmatoare (ClasaTablou1.java si ClasaTablou2.java) prezinta un exemplu de creare a


unei clase care defineste o metoda numita creareTablou. Acesta preia doua numere naturale (o
limita inferioara si una superioara) si creaza un tablou unidimensional care contine toate numerele
naturale aflate intre cele doua limite, inclusiv aceste limite.
Varianta de apel a unei metode prin crearea si utilizarea unei instante a clasei in care a fost definita
metoda.
public class ClasaTablou1 {
int [] creareTablou(int inf, int sup) {
int [] tabl = new int[(sup - inf) +1];
for (int i = 0 ; i < tabl.length; i++)
tabl[i] = inf++;
return tabl; }
public static void main(String args[]) {
ClasaTablou1 unTablou = new ClasaTablou1();
int [] tablou = unTablou.creareTablou(1,10);
System.out.print("Tabloul: [ ");
for (int i = 0; i < tablou.length; i++)
System.out.print(tablou[i] + " ");
System.out.println("]");}
}

Varianta de apel a unei metode prin simpla folosire a numelui metodei, deoarece metoda este
definita si apelata in aceeasi clasa. Totusi metoda creareTablou trebuie sa fie declarata ca metoda de
clasa (modificatorul static) pentru a putea fi apelata dintr-o alta metoda de clasa.
public class ClasaTablou2 {
static int [] creareTablou(int inf, int sup)
{
int [] tabl = new int[(sup - inf) +1];
for (int i = 0 ; i < tabl.length; i++)
tabl[i] = inf++;
return tabl;
}
public static void main(String args[])
{
int [] tablou = creareTablou(1,10);
System.out.print("Tabloul: [ ");
for (int i = 0; i < tablou.length; i++)
System.out.print(tablou[i] + " ");
System.out.println("]");
}
}

2.3.2 Metode de instanta si metode de clasa

Metode de instanta
Ca regula generala, o metoda definita intr-o clasa se poate apela prin crearea unei instante a clasei
respective sau a unei subclase a clasei respective. Aceasta se datoreaza faptului ca metoda lucreaza
cu o serie de variabile ale clasei care sunt memorate in interiorul instantei si care au valori diferite
in instante diferite (numite variabile de instanta). Astfel de metode se numesc metode ale
instantelor clasei.

Metodele de instanta sunt aplicate unui anume obiect, nu unei clase intregi. Majoritatea metodelor
definite intr-o clasa sunt metode de instanta.

Metode de clasa
Dupa cum stim deja, exista si un alt tip de variabile si anume variabilele de clasa sau variabilele
statice care sunt comune tuturor instantelor clasei respective. Aceste variabile pot fi accesate fara a
avea nevoie de o instanta a clasei in care au fost declarate.
In mod similar exista si metode de clasa sau metode statice. Pentru a fi apelate, aceste metode,
definite intr-o clasa, nu au nevoie sa fie creata o instanta a clasei respective sau a subclasei derivata
din clasa respectiva. Metodele de clasa sunt disponibile oricarei instante a clasei.

2.3.3 Domeniul de vizibilitate (acces) al metodelor unei clase. Modificatori de acces

O metoda este accesibila (apelabila) daca este definita in clasa din care este apelata sau intr-una din
subclasele acesteia. Atunci cand se apeleaza metoda unui obiect, Java cauta definitia metodei
respective in clasa obiectului. Daca nu o gaseste, cauta mai sus in ierarhia de clase pana cand
gaseste o definitie.
In acelasi timp pentru a “vedea” o metoda si pentru a o putea apela, este nevoie sa avem drepturile
de acces necesare (date de modificatorii de acces).

Modificatorii de acces (vizibilitate) ai metodelor unei clase


In Java exista trei modificatori de vizibilitate ai variabilelor unei clase:
- modificatorul public;
- modificatorul protected;
- modificatorul private.

Modificatorul public face ca metoda respectiva sa fie accesibila oriunde este accesibila clasa din
care face parte metoda.

Modificatorul protected face ca metoda respectiva sa fie accesibila in orice clasa din pachetul careia
ii apartine clasa in care a fost definita. In acelasi timp, metoda este accesibila in toate subclasele
clasei date, chiar daca ele apartin altor pachete.

Modificatorul private face ca metoda respectiva sa fie accesibila doar in interiorul clasei in care a
fost definita.

29
Daca pentru o metoda a unei clase nu se precizeaza nici un modificator de acces din cei descrisi mai
sus, atunci metoda respectiva devine package-friendly. O metoda friendly este accesibila in
pachetul din care face parte clasa in interiorul careia a fost definita, dar nu este accesibila in
subclasele clasei date daca acestea apartin altor pachete.

Nota: Modificatorii de acces (public, protected, private) sunt plasati primii in definitia metodei,
urmeaza apoi modificatorii care determina felul metodei (static, abstract, final) si apoi semnatura
metodei.

Urmatorul program (TestCerc.java) ilustreaza modul de folosire al variabilelor de instanata, precum


si al metodelor de instanta. In clasa Cerc variabila de instanta este raza care este vizibila numai in
clasa in care a fost declarata (are modificatorul private). De aceea, accesul la aceasta variabila
(pentru examinare si modificare) se face numai prin intermediul metodelor setRaza si getRaza care
sunt publice.
class Cerc
{
private double raza;
public void setRaza(double r) {
raza = r; }
public double getRaza() {
return raza; }
public double arie() {
return Math.PI * raza * raza; }
public double lungime() {
return 2 * Math.PI * raza; }
}
public class TestCerc
{
public static void main(String[] args) {
Cerc cerculMeu = new Cerc();
cerculMeu.setRaza(10);
System.out.println("Raza=" + cerculMeu.getRaza());
System.out.println("Aria=" + cerculMeu.arie());
System.out.println("Lungimea=" + cerculMeu.lungime());
}
}
Observatie: Deoarece fisierul-sursa TestCerc.java contine o clasa publica, TestCerc, numele
fisierului trebuie sa fie identic cu numele clasei publice, altfel compilarea nu se va face cu succes.
Intr-un fisier-sursa nu se poate defini decat o singura clasa publica.

Referinta this
Cuvantul-cheie this se refera la obiectul curent, adica obiectul a carei metoda a fost apelata. Metoda
poate folosi variabilele de instanta ale obiectului curent sau poate transmite obiectul curent ca
parametru unei alte metode. Iata cateva exemple de folosire a cuvantului this:
t = this.x; // variabila de instanta x pentru acest obiect
this.resetRaza(this); // apeleaza metoda resetRaza, definita in clasa curenta si
// transmite obiectul curent
return this; // returneaza obiectul curent

In cele mai multe cazuri nu este nevoie sa se foloseasca explicit cuvantul-cheie this, deoarece este
presupus. De exemplu, ne putem referi atat la variabilele de instanta, cat si la apelurile de metode
definite in clasa curenta prin simpla folosire a numelui lor, deoarece this este implicit folosit de
aceste referinte.
De aceea, primele doua exemple se pot rescrie astfel:
t = x; // variabila de instanta x pentru acest obiect
resetRaza(this); // apeleaza metoda resetRaza, definita in clasa curenta

Nu se omite cuvantul-cheie this daca in domeniul de vizibilitate al obiectului curent au fost definite
variabile locale cu acelasi nume ca cel al unei variabile de instanta sau au fost transmisi unei
metode, a obiectului curent, parametrii cu acelasi nume ca cel al unei variabile de instanta. Aceste
aspecte au fost explicate la paragraful referitor la domeniul de vizibilitate al variabilelor clasei.
Nota: Deoarece this este o referinta a instantei curente a clasei, trebuie sa se foloseasca doar in
corpul unei definitii de metoda de instanta. Metodele de clasa, declarate cu modificatorul static, nu
pot folosi this.
Teste de control

2.1 Declararea constructorilor trebuie sa tina cont de:


A. relatia de mostenire dintre clase;
B. numele constructorului, care trebuie sa fi identic cu numele clasei;
C. comportamentul obiectelor pe care le instantiaza;
D. o metoda prin care poate fi accesat de toate tipurile din Java sau de tipuri mostenite din tipul care
contine membrul in discutie;

2.2 Ce puteti spune despre urmatorul program Java?


class C1{
int x=1;
void f(int x){
this.x=x;
}
int getX_C1(){
Return x;}}
Class C2 extens C1{
float x=5.0f;
int f(int x){
super.f((int)x);}
float getX_C2(){
return x;}}
public class Test{
public static void main(String []args){
C2 obiect = new C2();
obiect.f(4);
System.out.print(obiect.getX_C2() + " ");
System.out.println(obiect.getX_C1());}}
A. Programul este corect si va afisa la executie 5 4;
B. Programul este corect si va afisa la executie 4.0 4;
C. Va aparea eroare la compilare deoarece in clasa C2 s-a suprascris gresit atributul x din clasa C1;
D. Va aparea eroare la compilare deoarece metoda suprascrisa f() din clasa C2 intoarce un tip diferit
de void;

2.3 Ce putet i spune despre urmatorul program Java?


public class Test{
public static void main(String []args){
C1 obiect =new C1();
obiect.f(4,3);
31
}}
class C1{
public void f(int xx, final int yy){
int a=xx+yy;
final int b=xx-yy;
class C2{
public void g(){
System.out.print("a= "+a);
System.out.print(", b= "+b);
}
}
C2 obiect2 = new C2();
obiect2.g();}}
A. Programul este corect si va afisa la executie a=4, b=3;
B. Va aparea eroare la compilare, deoarece clasa C2 nu poate fi defnita in metoda f() din clasa C1;
C. Va aparea eroare la compilare deoarece in metoda g() nu putem accesa variabila locala a din
metoda f();
D. Va aparea eroare la compilare deoarece nu se creeaza in clasa Test un obiect de tip C1.C2

2.4 Ce putet»i spune despre urm¸atorul program Java?


class C1{
int x=1;
void f(int x){
this.x=x;
}
int getX_C1(){
Return x;
}
}
Class C2 extens C1{
float x=5.0f;
int f(int x){
super.f((int)x);
}
float getX_C2(){
return x;
}
}
public class Subiect9{
public static void main(String []args){
C2 obiect = new C2();
obiect.f(4);
System.out.print(obiect.getX_C2() + " ");
System.out.println(obiect.getX_C1());
}}
A. Programul este corect si va afisa la executie 5 4;
B. Programul este correct si va afisa la executie 4.0 4;
C. Va aparea eroare la compilare deoarece in clasa C2 s-a suprascris gresit atributul x din clasa C1;
D. Va aparea eroare la compilare deoarece metoda suprascrisa f() din clasa C2 intoarce un tip diferit
de void;
Capitolul 3 Extinderea claselor. Polimorfismul in Java

Obiective:

- mostenire: derivare; superclase si subclase; ierarhie de clase; clasa Object; mostenire simpla si
multipla; sintaxa pentru derivarea unei clase dintr-o superclasa; controlul accesului si mostenirea;
metode-constructor pentru clase derivate si cuvantul-cheie super; supraincarcarea (overloading)
metodelor; redefinirea (override) metodelor;
- polimorfism: caracteristicile polimorfismului; legarea statica si legarea dinamica;
- metode si clase finale; metode si clase abstracte;
- exemplu de folosire a mostenirii, a polimorfismului, a claselor si metodelor finale si abstracte.

3.1Mostenirea - concept fundamental al programarii orientata obiect

Conceptul de mostenire este folosit in Java la organizarea claselor si a comportamentului acestora.


Mostenirea este mecanismul fundamental pentru refolosirea codului.
Mostenirea este un mecanism care permite unei clase sa mosteneasca atributele si comportamentul
unei alte clase cu care este inrudita. Mostenirea modeleaza relatii de tipul ESTE-UN (sau ESTE-O).
Intr-o relatie de tip ESTE-UN, spunem despre o clasa nou creata ca ESTE-O variatiune a unei clase
existente (originala, din care provine). De exemplu, Masina ESTE-UN Vehicul.

Practic, prin mostenire o clasa noua dobandeste toate atributele si comportamentul unei clase
existente (clasa originala). Din acest motiv, noua clasa poate fi creata prin simpla specificare a
diferentelor fata de clasa originala din care provine.

Datorita relatiilor de mostenire clasele de obiecte sunt organizate intr-o ierarhie bine precizata.
In cadrul ierarhiei claselor de obiecte, operatia de definire a unei noi clase de obiecte pe baza uneia
deja existente se numeste derivare. Clasa originala (mai generala) se va numi superclasa a noii
clase, iar noua clasa de obiecte se va numi subclasa a clasei din care deriva.
Uneori, in loc de derivare se foloseste termenul de extindere. Termenul vine de la faptul ca o
subclasa isi extinde superclasa cu noi variabile si metode.
De exemplu, putem extinde clasa Masina la MasinaStraina (pentru care se plateste vama) si
MasinaAutohtona (pentru care nu se plateste vama) etc.

Observatie importanta: Un alt tip de relatie intre obiecte este relatia ARE-UN sau ESTE-
COMPUS-DIN. De exemplu, Masina ARE-UN volan. Aceasta relatie nu este o relatie de mostenire,

33
ci este de agregare. Componentele care pot fi agregate devin simple campuri (atribute) de tip
private ale clasei nou create.
Datorita relatiilor de mostenire clasele de obiecte sunt organizate intr-o ierarhie bine precizata.
In cadrul ierarhiei claselor de obiecte, operatia de definire a unei noi clase de obiecte pe baza uneia
deja existente se numeste derivare. Clasa originala (mai generala) se va numi superclasa a noii
clase, iar noua clasa de obiecte se va numi subclasa a clasei din care deriva.

Uneori, in loc de derivare se foloseste termenul de extindere. Termenul vine de la faptul ca o


subclasa isi extinde superclasa cu noi variabile si metode.
De exemplu, putem extinde clasa Masina la MasinaStraina (pentru care se plateste vama) si
MasinaAutohtona (pentru care nu se plateste vama) etc.
Observatie importanta: Un alt tip de relatie intre obiecte este relatia ARE-UN sau ESTE-
COMPUS-DIN. De exemplu, Masina ARE-UN volan. Aceasta relatie nu este o relatie de mostenire,
ci este de agregare. Componentele care pot fi agregate devin simple campuri (atribute) de tip
private ale clasei nou create.

Clasa A este superclasa a clasei B. Clasa B este subclasa a clasei A.


Clasa B este superclasa pentru clasele C, D si E. Clasele C, D si E sunt subclase ale clasei B.

In varful ierarhiei de clase Java se afla clasa Object, iar toate clasele de obiecte, care se creaza, sunt
derivate din aceasta unica superclasa.
Object reprezinta clasa initiala, sa-i spunem clasa de obiecte generice, care defineste atributele si
comportamentul (metodele) mostenite de toate clasele din biblioteca de clase Java. In varful
ierarhiei se definesc concepte (clase) abstracte, foarte generale. Aceste concepte generale devin din
ce in ce mai concrete, mai particularizate, o data cu “coborarea” spre subclasele de pe nivelele de
mai jos in ierarhie.
Cand clasa nou creata defineste un comportament complet nou si nu este o subclasa a unei alte
clase, atunci ea mosteneste direct clasa Object. Astfel, noua clasa se integreaza corect in ierarhia
claselor Java.
De asemenea, daca se defineste o clasa care nu specifica o superclasa, Java presupune ca noua clasa
mosteneste direct clasa Object. Clasele definite in exemplele de pana acum au mostenit direct clasa
Object.

De exemplu, se poate construi o ierarhie Om care deriva direct din clasa generica Object si are doua
clase derivate: clasa barbatilor (Barbat) si clasa femeilor (Femeie).
Daca la nivelul clasei de obiecte Om am definit forma bipeda a acestuia si capacitatea de a vorbi si
de a intelege, toate aceste caracteristici vor fi mostenite si de clasele derivate din clasa Om, si
anume clasa barbatilor si cea a femeilor. Fiecare din aceste clase de obiecte derivate isi vor defini
propriile lor proprietati si operatii pentru a descrie diferenta dintre ele si clasa originala.

3.2 Caracteristicile unei ierarhi de clase

Organizarea unei aplicatii informatice intr-o ierarhie de clase presupune o planificare atenta.
Proiectarea arborelui de clase de obiecte necesare rezolvarii unei anumite probleme este un talent pe
care fiecare programator trebuie sa si-l descopere si sa si-l cultive cu atentie. De alegerea claselor si
de proiectarea arborelui acestor clase depinde eficienta si flexibilitatea aplicatiei.
Principalele caracteristici ale unei ierarhie de clase sunt:
- atributele si metodele comune mai multor clase pot fi definite in superclase, care permit folosirea
repetata a acestora pentru toate clasele aflate pe nivelele mai joase din ierarhie;
- modificarile efectuate in superclasa se reflecta automat in toate subclasele ei, subclasele acestora
si asa mai departe; nu trebuie modificat si recompilat nimic in clasele aflate pe nivelurile inferioare,
deoarece acestea primesc noile informatii prin mostenire;
- clasa derivata poate sa adauge noi atribute si metode si poate modifica semnificatia metodelor
mostenite (prin polimorfism, despre care vom vorbi mai incolo in acesta lectie);
- superclasa nu este afectata in nici un fel de modificarile aduse in clasele derivate;
- o clasa derivata este compatibila ca tip cu superclasa, ceea ce inseamna ca o variabila referinta de
tipul superclasei poate referi un obiect al clasei derivate, dar nu si invers; clasele derivate dintr-o
superclasa nu sunt compatibile ca tip.

3.3 Mostenirea simpla in Java

Forma de mostenire folosita in Java este denumita mostenire simpla (single inheritance), deoarece
fiecare clasa de obiecte Java nou creata poate fi derivata dintr-o singura superclasa (poate avea o
singura superclasa).

Mostenirea multipla inseamna ca o clasa nou creata pot fi derivata din doua sau mai multe
superclase, mostenind variabilele si metodele combinate din toate aceste superclase. Mostenire
multipla (multiple inferitance) ofera mijloace de creare a unor clase care cuprind aproape orice
comportament imaginabil. Acest lucru complica semnificativ definitia clasei si a codului necesar
acesteia.
Mostenirea simpla poate fi restrictiva, mai ales atunci cand exista un comportament similar, care
trebuie duplicat pe diferite “ramuri” ale ierarhiei de clase (nu pe aceeasi ramura a ierarhiei).
De exemplu, omul poate fi privit ca un mamifer care naste pui vii sau poate fi privit ca un obiect
spatio-temporal care are propria lui forma si pozitie in functie de timp. Aceasta inseamna ca trebuie
sa dam definitii de metode despre ce inseamna faptul ca un obiect poate fi privit ca un mamifer sau
ca un obiect spatio-temporal. Dar, aceste definitii de metode sunt comune nu numai clasei de
obiecte Om dar si altor clase de obiecte derivate sau nu din clasa Om, superclase sau nu ale clasei
Om. Putem sa gasim o multime de clase de obiecte ale caror instane pot fi privite ca obiecte spatio-
temporale dar care sa nu aiba mare lucu in comun cu omul (de exemplu clasa Minge).

Pentru rezolvarea problemei mostenirii multiple, Java foloseste interfetele. Declaratiile de metode
si atribute comune mai multor clase de obiecte care nu sunt mostenite de la superclasele lor poarta
denumirea de interfeta. Interfetele vor fi prezentate in totalitate intr-o lectie viitoare.

Sintaxa folosita pentru a deriva o clasa noua dintr-o superclasa

35
O clasa derivata (numita si subclasa) mosteneste toate atributele (variabilele de instanta si de clasa)
superclasei din care provine. Clasa derivata poate apoi sa adauge noi atribute, sa redefineasca
metode ale superclasei sau sa adauge noi metode. Fiecare clasa derivata este o clasa complet noua.
Pentru a declara o clasa derivata se foloseste clauza extends, astfel:
[<modificatori_acces>] [<modificatori_clasa>] class <nume_subclasa> extends
<nume_superclasa>
{<corpul_clasei}
unde:
- <modificatori_acces> - specifica domeniul de vizibilitate (folosire sau acces) al subclasei;
modificatorul de acces poate fi: public;
- <modificatori_clasa> - specifica tipul subclasei definite; modificatorul clasei poate fi: abstract,
final;
- <nume_subclasa> - specifica numele clasei derivate dintr-o superclasa;
<nume_superclasa> - specifica numele unei superclase din care deriva subclasa;
- <corpul_clasei> - variabilele clasei si metodele care lucreaza cu acestea, adaugate sau redefinite in
subclasa .

Observatii:
1. Orice metoda neprivata (adica publica, protejata sau prietenoasa) din superclasa, care nu
este redefinita (suprascrisa) in clasa derivata, este mostenita nemodificat, cu exceptia
constructorilor. Metoda poate fi apoi apelata ca si cum ar face parte din clasa derivata.
2. Clasa derivata contine atribute si metode suplimentare (fata de cele mostenite din
superclasa) care pot fi declarate: private, protected, public sau fara modificator de acces.

Controlul accesului si mostenirea


Ca regula generala, toti membrii de tip public ai superclasei devin membrii de tip public ai clasei
derivate.
De asemenea, membrii de tip private ai superclasei sunt mosteniti, dar acestia nu sunt accesibili in
mod direct (adica, folosind operatorul “punct” sau direct numele membrului) in clasa derivata, ci
prin intermediul altor metode publice mostenite de la superclasa care fac posibil accesul.

Metodele speciale care examineaza si modifica valoarea fiecarui atribut de tip private sunt denumite
“accesori” si, respectiv, “modificatori”.
Se foloseste conventia ca numele metodelor “accesor” sa inceapa cu get, cum ar fi getRaza() din
programul TestCerc.java prezentat in capitolul 2. De asemenea, se foloseste conventia ca numele
metodelor “modificator” sa inceapa cu set, cum ar fi setRaza() din programul TestCerc.java
prezentat in capitolul 2.

Folosirea metodelor “accesori” si “modificatori” este foarte raspandita in programarea orientata


obiect. Acest mod de abordare mareste gradul de reutilizare a codului, evitand folosirea lui
necorespunzatoare.

Metode-constructor pentru clase derivate si cuvantul-cheie super

Metodele-constructor nu se mostenesc. Fiecare clasa derivata trebuie sa isi defineasca propriile


metode-constructor.

Daca nu se defineste nici un constructor, Java va genera un constructor implicit (fara parametri).
Acest constructor implicit al clasei derivate:
- va apela automat constructorul implicit (fara parametrii) al superclasei (aflata pe nivelul imediat
superior) pentru membrii care au fost mosteniti, apoi
- va aplica initializarea implicita pentru atributele adaugate in clasa derivata (adica 0/false pentru
tipurile primitive numerice/booleene si null pentru tipurile referinta).

Asadar, construirea unui obiect al unei clase derivate are loc prin construirea prealabila a portiunii
mostenite (constructorul clasei derivate apeleaza automat constructorul superclasei aflata pe nivelul
imediat superior). Acest lucu este normal, deoarece mecanismul incapsularii afirma ca partea
mostenita este un “intreg”, iar constructorul superclasei ne spune cum sa initializam acest “intreg”.
Metodele-constructor ale superclasei pot fi apelate explicit in clasa derivata prin metoda super().
Metoda super poate sa apara doar in prima linie dintr-o metoda-constructor.

Apelul unei metode-constructor a superclasei dintr-o clasa derivata, folosind super se face astfel:
super(<arg1>, <arg2>, <arg3>, …)
unde:
- <arg1>, <arg2>, <arg3>, … - specifica parametrii metodei-constructor a superclasei.
De exemplu, sa presupunem ca o superclasa (ClasaSuper) are un constructor cu doi parametri de tip
int. Constructorul clasei derivate va avea, in general, forma:
public class ClasaDerivata extends ClasaSuper
{
public ClasaDerivata(int x, int y);
{
super(x, y);
// alte instructiuni
}

}
Observatie: Daca in superclasa este definit explicit cel putin un constructor, atunci trebuie apelat
explicit (prin super) constructorul superclasei respective din interiorul constructorului clasei
derivate. Altfel se obtine eroare de compilare.

3.4 Supraincarcarea (overloading) metodelor. Redefinirea (override) metodelor

Supraincarcarea metodelor permite existenta in interiorul aceleiasi clase a mai multor metode cu
acelasi nume, dar cu lista diferita de parametrii (ca numar si tip), deci cu semnatura diferita.
Supraincarcarea metodelor este permisa si daca unele dintre metode sunt definite intr-o superclasa
si altele in clasele derivate din superclasa respectiva.

De exemplu, putem avea o metoda int max(int a, int b) si o metoda int max(int a, int b, int c),
ambele in cadrul aceleasi clase sau putem defini prima metoda in cadrul unei superclase si cea de a
doua metoda in cadrul unei clase derivate din superclasa.
Atunci cand se face un apel al unei metode supraincarcate, compilatorul alege definitia de metoda
examinand lista parametrilor de apel (adica, semnatura).

Redefinirea (suprascriere) metodelor permite existenta in interiorul claselor derivate dintr-o


superclasa a mai multor metode cu acelasi nume, tip de rezultat si aceeasi lista de parametrii, deci
cu aceeasi semnatura.
Atunci cand se face un apel al unei metode redefinite, compilatorul foloseste definitia metodei care
este gasita prima (incepand din partea de jos a ierarhiei de clase si mergand in sus).

Programul urmator (AfisareSubClasa.java) prezinta o clasa ce contine o metoda afisareDate(), care


afiseaza numele clasei si valorile variabilelor de instanta. De asemenea, in acelasi fisier-sursa este
inclusa si clasa denumita AfisareSubClasa derivata din clasa AfisareClasa. A fost creat un obiect de

37
tip AfisareSubClasa si a fost apelata metoda afisareDate(). Deoarece clasa AfisareSubClasa nu
defineste aceasta metoda, Java o cauta in superclasele clasei AfisareSubClasa, incepand cu
superclasa AfisareClasa, unde gaseste metoda afisareDate() si o executa.
class AfisareClasa {
int x = 0;
int y = 1;
void afisareDate() {
System.out.println("x este " + x + ", y este " + y);
System.out.println("Sunt un obiect al clasei " +
this.getClass().getName());
}
}
class AfisareSubClasa extends AfisareClasa {
int z = 3;
public static void main(String [] args) {
AfisareSubClasa ob = new AfisareSubClasa();
ob.afisareDate();
}
}
Rezultatul programului este:
x este 0, y este 1
Sunt un obiect al clasei AfisareSubClasa
Deoarece metoda afisareDate() a superclasei AfisareClasa nu afiseaza si variabila de instanta z
specifica subclasei AfisareSubClasa, metoda afisareDate() va fi redefinita (suprascrisa) in interiorul
subclasei. Iata noul program (AfisareSubClasa.java):
class AfisareClasa {
int x = 0;
int y = 1;
void afisareDate() {
System.out.println("x este " + x + ", y este " + y);
System.out.println("Sunt un obiect al clasei " +
this.getClass().getName()); } }
class AfisareSubClasa extends AfisareClasa {
int z = 3;
void afisareDate() {
System.out.println("x este " + x + ", y este " + y + ", z este " + z);
System.out.println("Sunt un obiect al clasei " +
this.getClass().getName()); }
public static void main(String [] args) {
AfisareSubClasa ob = new AfisareSubClasa();
ob.afisareDate();} }
Rezultatul programului este:
x este 0, y este 1, z este 3
Sunt un obiect al clasei AfisareSubClasa
Nota: Apelul de metoda:
this.getClass().getName());
este folosit pentru aflarea numelui clasei din care face parte un obiect, in cazul de fata obiectul
curent. Vom reveni cu amanunte in ce priveste obtinerea de informatii despre o clasa.

Redefinirea partiala a unei metode


Redefinirea partiala inseamna ca metoda din clasa derivata nu redefineste complet metoda cu
aceeasi semnatura din superclasa, ci extinde operatiile pe care aceasta le realizeaza. Cu alte cuvinte,
metoda din clasa derivata face ceva in plus fata de cea originala din superclasa.
Pentru a apela metoda din superclasa in interiorul metodei din clasa derivata se foloseste referinta
super.

Urmatorul program (AfisareSubClasa1.java) ilustreaza modul de redefinire partiala a metodei


afisareDate() din subclasa AfisareSubClasa1 si apelul in cadrul acesteia a metodei afisareDate()
din superclasa AfisareClasa.
class AfisareClasa {
int x = 0;
int y = 1;
void afisareDate() {
System.out.println("Sunt un obiect al clasei " +
this.getClass().getName());
System.out.println("x este " + x);
System.out.println("y este " + y);
}
}
class AfisareSubClasa1 extends AfisareClasa {
int z = 3;
void afisareDate() {
super.afisareDate();
System.out.println("z este " + z);
}
public static void main(String [] args) {
AfisareSubClasa1 ob = new AfisareSubClasa1();
ob.afisareDate();
}
}

Accesul la metodele din superclasa si redefinirea metodelor in clasele derivate

Atunci cand se creaza o clasa derivata si se redefineste (suprascrie) o metoda declarata intr-o
superclasa, trebuie sa se ia in considerare tipul de acces dat pentru metoda originala.
Astfel, in cazul metodelor mostenite, pentru care se doreste redefinirea (suprascrierea) in clasa
derivata se impun urmatoarele reguli:
- metodele de tip public dintr-o superclasa trebuie sa fie, de asemenea, de tip public in toate clasele
derivate; ele nu pot fi redefinite de tip private in clasele derivate;
- metodele de tip protected dintr-o superclasa pot fi de tip protected sau de tip public in clasele
derivate; ele nu pot fi redefinite de tip private in clasele derivate;
- metodele de tip private dintr-o superclasa nu pot fi redefinite (suprascrise) in clasele derivate.

3.5 Implementarea polimorfismului

Polimorfismul reprezinta capacitatea unui obiect de a aparea sub diferite forme. De exemplu, in
lumea reala, apa apare sub forma solida, sub forma lichida sau sub forma gazoasa.
In Java, polimorfismul inseamna ca o singura variabila referinta x de tipul unei superclase poate fi
folosita pentru a construi diferite obiecte (instante) ale claselor derivate, direct sau indirect din acea
superclasa, in momente diferite ale executiei unui program. Cand variabila referinta x este folosita
pentru a apela o metoda a unui obiect apartinand unei clase derivate, metoda adecvata care va fi

39
selectata depinde de tipul obiectului pe care variabila referinta x il indica in acel moment. Se spune
ca variabila referinta este polimorfica.
De exemplu, sa presupunem ca s-au definit clasele: AnimalDeCasa, Pisica, Caine. Se doreste sa se
afiseze la ecran (programul Polimorfism.java) caracteristicile principale ale claselor Pisica si Caine,
atunci cand instantele lor sunt infometate.
class AnimalDeCasa
{
private boolean stareSuparare;
public boolean flamand;
protected void hranesc()
{
System.out.println("Nu se cunoaste");
}
public void caracteristici()
{
System.out.println("Caracteristici necunoscute");
}
public boolean getStare()
{
return stareSuparare;
}
public void setStare(boolean stare)
{
stareSuparare = stare;
}
}
class Pisica extends AnimalDeCasa
{
public void caracteristici()
{
String starePisica;
if (getStare() == true)
starePisica = "miauna";
else
starePisica = "nu miauna";
if (flamand == true)
System.out.println("Pisica " + starePisica + ". Este flamanda.");
else
System.out.println("Pisica " + starePisica + ". Este satula.");
}
public void hranesc()
{
if (flamand==true)
{System.out.println("Pisica mananca lapte.");
flamand = false;
setStare(false);}
else
System.out.println("Pisica a mancat deja.");}
}
class Caine extends AnimalDeCasa
{
public void caracteristici()
{
String stareCaine;
if (getStare() == true)
stareCaine = "latra";
else
stareCaine = "nu latra";
if (flamand == true)
System.out.println("Cainele " + stareCaine + ". Este flamand.");
else
System.out.println("Cainele " + stareCaine + ". Este satul.");
}
public void hranesc()
{
if (flamand==true)
{System.out.println("Cainele mananca oase.");
flamand = false;
setStare(false);}
else
System.out.println("Cainele a mancat deja.");}
}
public class Polimorfism
{
public static void main(String args[])
{
AnimalDeCasa a = new Pisica();
a.flamand = true;
a.setStare(true);
System.out.println("Caracteristicile primului animal de casa: ");
a.caracteristici();
a.hranesc();
a.caracteristici();
a.hranesc();
a.caracteristici();
a = new Caine();
a.flamand = true;
a.setStare(true);
System.out.println("Caracteristicile celui de al doilea animal de casa: ");
a.caracteristici();
a.hranesc();
a.caracteristici();
}
}
Ceeace este specific acestui program este ca el contine trei metode cu numele caracteristici() care
au aceeasi semnatura (nume, tip valoare returnata si numar si tip parametrii) si trei metode cu
numele hranesc() care au, de asemenea, aceeasi semnatura. Dar, aceste metode se afla in clase
diferite si anume, intr-o superclasa numita AnimalDeCasa si in doua clase derivate numite Pisica si
Caine care extind clasa AnimalDeCasa.

Spunem ca metodele caracteristici() si hranesc() din clasele derivate redefinesc (override)


metodea caracteristici() si, respectiv, hranesc() din superclasa.

Sa analizam acum instructiunea urmatoare:

41
AnimalDeCasa a = new Pisica();
Variabila referinta a care este de tipul AnimalDeCasa desemneaza un obiect de tipul Pisica, care
ESTE-UN AnimalDeCasa prin mostenire.
Sa analizam si instructiunea:
a = new Caine();

Variabila a refera (la momente diferite de timp) obiecte de tipuri diferite, dar similare (adica
derivate din aceeasi superclasa: AnimalDeCasa).

In concluzie:
- pe de o parte, mostenirea permite tratarea unui obiect ca fiind de tipul propriu sau de tipul de baza
(din care este derivat tipul propriu). Aceasta caracteristica permite mai multor tipuri (derivate din
acelasi tip de baza) sa fie tratate ca si cum ar fi un singur tip, ceea ce face ca aceeasi secventa de
cod sa fie folosita de catre toate aceste tipuri. In cazul din exemplul prezentat, clasa Pisica si clasa
Caine au doua atribute (stareSuparare si flamand) si doua metode setStare() si getStare() mostenite
de la superclasa AnimalDeCasa din care deriva.
- pe de alta parte, polimorfismul permite unui tip de obiect sa exprime distinctia fata de un alt tip de
obiect similar, atata timp cat amandoua sunt derivate din aceeasi superclasa. Distinctia este
exprimata prin redefinirea (suprascrierea) metodelor care pot fi apelate prin intermediul unei
variabile-referinta de tipul superclasei (in exemplul nostru este vorba despre metodele
caracteristici() si hranesc()).

Legarea statica si legarea dinamica


Conectarea unui apel de metoda de un anumit corp de metoda poarta numele de legare (binding).
Cand legarea are loc inainte de rularea programului respectiv (adica, in faza de compilare) spunem
ca este vorba despre o legare statica(early binding). Termenul este specific programarii orientate pe
obiecte. In programarea procedurala (vezi limbajul C sau Pascal) notiunea de legare statica nu
exista, pentru ca toate legaturile se fac in mod static.
Cand legarea are loc in faza de executie a programului respectiv, spunem ca este vorba despre o
legare tarzie (late binding) sau legare dinamica. Legarea tarzie permite determinarea in faza de
executie a tipului obiectului referit de o variabila referinta si apelarea metodei specifice acestui tip
(in exemplul prezentat, metodele caracteristici() si hranesc() din clasa Pisica sau din clasa Caine).
Polimorfisul apare doar atunci cand are loc legarea tarzie a metodelor.

3.6 Metode si clase abstracte

O metoda abstracta este o metoda din superclasa care are sens doar pentru clasele derivate direct
din superclasa, nu are implementare (nu are corp) ci numai antet, in superclasa si care in mod
obligatoriu trebuie definita (completata cu corpul ei) in clasele derivate (altfel rezulta eroare de
compilare).
O metoda abstracta este declarata cu modificatorul abstract.
Intr-o ierarhie de clase, cu cat clasa se afla pe un nivel mai inalt, cu atat definirea sa este mai
abstracta. O clasa aflata ierarhic deasupra altor clase poate defini doar atributele si comportamentul
comune celor aflate sub ea pe ramura respectiva a ierarhiei.

In procesul de organizare a unei ierarhi de clase, se poate descoperi, uneori, cate o clasa care nu se
instantiaza direct (adica din ea nu se pot crea direct obiecte). De fapt, aceasta serveste, doar ca loc
de pastrare a unor metode si atribute pe care le folosesc in comun subclasele sale. Acesta clasa se
numeste clasa abstracta si este creata folosind modificatorul abstract.
Clasele abstracte pot contine aceleasi tipuri de membri ca o clasa normala, inclusiv metodele-
constructor, deoarece subclasele lor pot avea nevoie sa mosteneasca aceste metode-constructor.
Clasele abstracte pot contine, de asemenea, metode abstracte. O clasa care are cel putin o metoda
abstracta este o clasa abstracta. Nu se poate declara o metoda abstracta intr-o clasa non-abstracta.

Exemplu de folosire a mostenirii, a polimorfismului, a claselor si metodelor finale si abstracte


Un exemplu simplu de clasa abstracta este clasa formelor geometrice. Din clasa formelor
geometrice pot deriva forme specifice cum ar fi: cerc, dreptunghi. Se poate, apoi, deriva clasa
patratului ca un caz particular de dreptunghi. Figura de mai jos prezinta ierarhia de clase care
rezulta:

Clasa FormaGeo poate sa aiba membri care sa fie comuni pentru toate subclasele, cum ar fi, de
exemplu tipul formei geometrice, afisarea caracteristicilor formelor geometrice concrete (cerc,
dreptunghi etc).

De asemenea, clasa poate defini metode care se aplica fiecarui obiect in parte, cum ar fi, calculul
ariei unui obiect oarecare (dreptunghi, cerc etc). In consecinta, metoda arie() va fi declarata ca
abstracta.

Clasa FormaGeo fiind abstracta nu se poate instantia. Deci, nu se va putea crea un obiect de tip
FormaGeo, ci numai obiecte din clasele derivate. Totusi, o referinta de tip FormaGeo poate sa
refere orice forma concreta derivata, cum ar fi: Dreptunghi, Cerc, Patrat.
Fisierul-sursa urmator (FormaGeo.java) prezinta clasa abstracta FormaGeo. Constructorul acestei
clase nu va fi apelat niciodata direct, deoarece FormaGeo este o clasa abstracta. Avem totusi nevoie
de constructor, care sa fie apelat din clasele derivate pentru a initializa atributul nume de tip private
care specifica tipul figurii geometrice.

Metoda cu numele arie() este o metoda abstracta, deoarece nu putem furniza nici un calcul implicit
al ariei pentru o clasa derivata care nu isi defineste propria metoda de calcul a ariei.
Metoda maiMic() de comparatie a ariei unui obiect curent de tip FormaGeo cu un alt obiect de tip
FormaGeo (preluat prin parametrul rhs) nu este abstracta, deoarece ea poate fi aplicata la fel pentru
toate clasele derivate. De fapt, definirea ei este invarianta de-a lungul ierarhiei, de aceea am
declarat-o cu tipul final.

Variabila rhs poate sa refere orice instanta a unei clase derivate din FormaGeo (de exemplu, o
instanta a clasei Dreptunghi). Astfel, este posibil sa folosim aceasta metoda pentru a compara aria
obiectului curent (care poate fi, de exemplu, o instanta a clasei Cerc) cu aria unui obiect de alt tip,
derivat din FormaGeo. Acesta este un exemplu de folosire a polimorfismului.
Metoda toString afiseaza numele formei geometrice si aria ei. Ea este invarianta de-a lungul
ierarhiei si de aceea a fost declarata cu tipul final.

43
Observatie: Vom face cateva precizari asupra folosirii metodei toString() in orice clasa in care se
doreste afisarea starii instantelor unei clase. In general, afisarea starii unui obiect se face utilizand
metoda print() din clasa System.out. Pentru a putea face acest lucru, trebuie ca in clasa obiectului
care se doreste a fi afisat sa existe o metoda cu numele de toString(). Aceasta metoda trebuie sa
intoarca o valoare de tip String (reprezentand starea obiectului) care poate fi afisata. Astfel, in cazul
nostru, am definit o metoda toString care permite sa afisam un obiect oarecare de un tip derivat din
clasa FormaGeo folosind instructiunea System.out.println(). Practic, compilatorul Java apeleaza
automat toString() pentru fiecare obiect care se afiseaza cu metoda print().

/* Superclasa abstracta pentru forme FormeGeo


*
* CONSTRUIREA: nu este permisa, FormaGeo fiind abstracta.
* Constructorul cu un parametru este necesar ptr. clasele
* derivate.
* ------------------- metode publice ---------------------
* double arie() --> Intoarce aria (abstracta)
* boolean maiMic --> Compara doua forme dupa arie
* String toString --> Metoda uzuala pentru afisare
*/
abstract class FormaGeo {
private String nume;
abstract public double arie();
public FormaGeo(String numeForma) {
nume = numeForma;
}
final public boolean maiMic(FormaGeo rhs) {
return arie() < rhs.arie();
}
final public String toString() {
return nume + ", avand aria " + arie();
}
}

Pe baza clasei abstracte FormaGeo definite ne propunem sa rezolvam urmatoarea problema:


Se citesc N forme geometrice (patrate, dreptunghiuri, cercuri) de la tastatura. Sa se afiseze
formele geometrice ordonate dupa arie.
Mai intai, trebuie sa definim subclasele Cerc, Dreptunghi si Patrat. Definirea lor se face distinct in
fisiere-sursa separate (Cerc.java, Dreptunghi.java si Patrat.java).
/* clasa Cerc
* derivata din FormaGeo
* CONSTRUCTORI: cu raza ptr. cerc
* ------------------- metode publice ---------------------
* double arie()-->Implementeaza metoda abstracta din FormaGeo
*/
public class Cerc extends FormaGeo {
private double raza;
public Cerc(double rad) {
super("Cerc");
raza = rad;
}
public double arie() {
return Math.PI * raza * raza;}
}

/* clasa Dreptunghi;
* derivata din FormaGeo
* CONSTRUCTORI: cu lungime si latime ptr. dreptunghi
* ------------------- metode publice ---------------------
* double arie()-->Implementeaza metoda abstracta din FormaGeo
*/
public class Dreptunghi extends FormaGeo {
private double lungime;
private double latime;
public Dreptunghi(double lg, double lat) {
this(lg, lat, "Dreptunghi");
}
Dreptunghi(double lg, double lat, String nume) {
super(nume);
lungime = lg;
latime = lat;
}
public double arie() {
return lungime * latime;
}
}
/* clasa Patrat
* derivata din Dreptunghi
* CONSTRUCTORI: cu latura ptr. patrat
* ------------------- metode publice ---------------------
* double arie()-->Implementeaza metoda abstracta din FormaGeo
*/
public class Patrat extends Dreptunghi
{
public Patrat(double latura)
{
super(latura, latura, "Patrat");
}
}
Observatie: Clasa Patrat mosteneste de la clasa Dreptunghi metoda arie() si de aceea nu o mai
defineste in interiorul ei.
Pentru rezolvarea problemei ordonarii formelor geometrice dupa aria lor, se foloseste un tablou de
tip FormaGeo cu numele forme[] cu o dimensiune citita de la tastatura. Sa retinem ca tablou
forme[] este un tablou de referinte de tip FormaGeo pentru care se aloca zona de memorie. Acest
tablou nu stocheaza obiecte din clasele derivate ale clasei FormaGeo (de tip Cerc, Dreptunghi,
Patrat) ci numai referinte catre aceste obiecte.
Fisierul-sursa (TestForma.java) care realizeaza ordonarea si punerea in executie a aplicatiei se
prezinta in continuare.

Metoda citesteForma() citeste de la tastatura atributele obiectelor de tipul Cerc, Dreptunghi, Patrat,
pe baza unor optiuni care precizeaza tipul figurii, creaza un nou obiect de un tip derivat (Cerc,
Dreptunghi, Patrat) din tipul FormaGeo si returneaza o referinta catre obiectul creat.
In metoda main(), referinta la obiecte este apoi stocata in tabloul forme[].

45
Metoda sortare() este folosita pentru a sorta formele geometrice referite prin tabloul forme[] in
functie de aria calculata a fiecarui tip de figura geometrica. Metoda arie() este apelata prin
intermediul metodei maiMic() care la randul ei este apelata in metoda sortare().
import java.io.* ;

class TestForma {
private static BufferedReader in;
public static void main(String[] args) throws IOException {
//Citeste numarul de figuri
in = new BufferedReader(new
InputStreamReader(System.in));
System.out.print("Numarul de figuri: ");
int numForme = Integer.parseInt(
in.readLine());
//citeste formele
FormaGeo[] forme = new FormaGeo[numForme];
for (int i = 0; i < numForme; ++i)
{forme[i] = citesteForma();}
//sortare si afisare
sortare(forme);
System.out.println("Sortarea dupa arie: ");
for (int i = 0; i < numForme; ++i)
{System.out.println(forme[i]);}
}

//creaza un obiect adecvat de tip FormaGeo. Functie de datele de intrare.


//utilizatorul introduce 'c', 'p' sau 'd' pentru a indica forma, apoi introduce dimensiunile
//in caz de eroare se intoarce un cerc de raza 0
private static FormaGeo citesteForma() throws IOException {
double rad;
double lg;
double lat;
String s;
System.out.println("Introduceti tipul formei: ");
do
{
s = in.readLine();
} while (s.length() == 0);
switch (s.charAt(0))
{
case 'c':
System.out.println("Raza cercului: ");
rad = Integer.parseInt(in.readLine());
return new Cerc(rad);
case 'p':
System.out.println("Latura patratului: ");
lg = Integer.parseInt(in.readLine());
return new Patrat(lg);
case 'd':
System.out.println("Lungimea si latimea "
+ " dreptunghiului pe linii separate: ");
lg = Integer.parseInt(in.readLine());
lat= Integer.parseInt(in.readLine());
return new Dreptunghi(lg, lat);
default:
System.err.println("Tastati c, p sau d: ");
return new Cerc(0);
}
}
//sortare
private static void sortare(FormaGeo [] a) {
FormaGeo temp;
for (int i = 0; i <= a.length - 2; i++)
{for (int j = i+1; j <= a.length - 1; j++)
{if (a[j].maiMic(a[i]))
{temp = a[i];
a[i] = a[j];
a[j] = temp;}
}}
}
}
Dupa executia programului TestForma.class pe ecran se afiseaza:
Sortarea dupa arie:
Cerc, avand aria 3.141592653589793
Dreptunghi, avand aria 6.0
Dreptunghi, avand aria 8.0
Dreptunghi, avand aria 24.0
Patrat, avand aria 36.0
Cerc, avand aria 78.53981633974483
Patrat, avand aria 100.0
Cerc, avand aria 314.1592653589793
Cerc, avand aria 452.3893421169302
Cerc, avand aria 452.3893421169302

47
Teste de control:

3.1Ce puteti spune despre urmatorul program Java?


class C1{
int x=1;
void f(int x){
this.x=x;
}
int getX_C1(){
Return x;
}
}
Class C2 extens C1{
float x=5.0f;
int f(int x){
super.f((int)x);
}
float getX_C2(){
return x;
}
}
public class Test{
public static void main(String []args){
C2 obiect = new C2();
obiect.f(4);
System.out.print(obiect.getX_C2() + " ");
System.out.println(obiect.getX_C1());
}
}
A. Programul este corect si va afisa la executie 5 4;
B. Programul este correct si va afisa la executie 4.0 4;
C. Va aparea eroare la compilare deoarece in clasa C2 s-a suprascris gresit atributul x din clasa C1;
D. Va aparea eroare la compilare deoarece metoda suprascrisa f() din clasa C2 intoarce un tip diferit
de void;

3.2 O subclasa a unei clase abstracte poate fi instantiata numai daca:


A. Se foloseste cuvantul cheie abstract;
B. Suprascrie fiecare metoda declarata abstracta in superclasa sa, si furnizeaza implementari pentru
toate acestea;
C. Se foloseste mostenirea multipla;
D. O subclasa abstracta nu poate fi instantiata;

3.3 Secventa de cod:


class x{
private int x=1;
void x( )
{ System.out.println(x+” “);}}
class y extends x{
private int x=2;
void x( )
{ super.x();
System.out.println(x);}
public class test
{ public static void main( )
{ y obiect=new y( );
obiect.x();}}
Determină:
a).eroare la compilare: nu se poate defini un atribut si o metoda cu aceelasi nume (x) ;
b).eroare la compilare: nu se poate defini un atribut cu numele clasei (x) ;
c). eroare : nu este corect realizată moştenirea;
d). programul este corect si va afisa 1 2.

49
Capitolul 4 Interfete Java

Obiective:

- interfata Java: crearea interfetelor; folosirea interfetelor; implementarea unei interfete;


implementarea unor interfete multiple; derivarea interfetelor;
- implementarea de componente generice: programarea generica; clase generice.

4.1 Mostenirea multipla. Interfete

In cazul mostenirii multiple o clasa este derivata din doua sau mai multe superclase. De exemplu,
pot exista in ierarhie clase precum Student si Angajat. Din aceste doua clase ar putea fi derivata o
clasa cu numele AngajatStudent care sa contina atribute si metode combinate din clasele Student si
Angajat.
Mostenirea multipla poate conduce insa la dificultati de proiectare. De exemplu, cele doua
superclase din care deriveaza subclasa ar putea contine metode care au aceeasi semnatura, dar
implementari diferite sau ar putea avea atribute cu acelasi nume. Dificultatea rezolvarii unor astfel
de probleme a facut ca Java sa nu permita mostenirea multipla.
Alternativa oferita de Java pentru mostenirea multipla este folosirea interfetelor.

O interfata Java este o colectie de comportamente abstracte, care pot fi combinate in orice clasa
pentru a introduce in acea clasa comportamente care nu pot fi mostenite de la superclasa.
Tehnic, interfata Java este cea mai abstracta clasa posibila. Ea consta doar din metode publice
abstracte si din atribute statice si finale.

O clasa implementeaza o anumita interfata daca furnizeaza definitii pentru toate metodele abstracte
din cadrul interfetei. O clasa care implementeaza o interfata se comporta ca si cand ar fi extins o
clasa abstracta specificata de acea interfata.

4.2 Crearea si folosirea interfetelor Java

Sintaxa de definire a unei interfete este urmatoarea:


[<modificatori] interface <nume_interfata> [extends <nume_interfata1> [, <nume_interfata2>][,
<nume_interfata3>], …]]
{
<corpul interfetei>
}
unde:
- <modificatori> - sunt specificati prin cuvintele-cheie public si abstract; o interfata publica poate fi
accesata si de alte pachete decat cel care a definit-o; fiecare interfata este in mod implicit abstracta
deci cuvantul-cheie abstract poate lipsi;
- <nume_interfata> - specifica numele interfetei; este de preferat ca numele interfetei sa respecte
aceeasi conventie de numire ca si cea de numire a claselor;
- <nume_interfata1>, <nume_interfata2>, .. - specifica numele superinterfetelor din care poate
deriva interfata;
- <corpul_clasei> - contine declaratii de variabile si declaratii de metode (numai antetul acestora).
Variabilele interfetei sunt implicit statice si finale si deci trebuie specificate valori initiale pentru
acestea.
Metodele sunt declarate in interfata numai cu antetul lor, fara corp. Ele sunt implicit abstracte.
Observatii:
1. Interfetele nu se pot instantia, adica nu se poate crea o instanta a unei interfete; deci, o
interfata nu are o metoda constructor.
2. Nu pot exista implementari diferite pentru aceeasi metoda declarata in doua interfete.
Interfetele trebuie sa posede o protectie de pachet sau publica. Interfetele care nu contin
modificatorul public nu-si vor converti automat metodele la accesul public si abstract si nici
constantele la accesul public. O interfata nepublica are metode si constante nepublice, acestea
neputand fi folosite decat in clasele sau interfetele din acelasi pachet.

Interfetele, ca si clasele, pot apartine unui pachet daca se foloseste instructiunea package in prima
linie din fisierul-sursa. De asemenea, interfetele pot importa interfete sau clase din alte pachete.
Un exemplu de interfata este Compara, prezentata mai jos:
/* INTERFATA folosita de mai multe clase pentru comparatii
* --------------------Metode publice---------------
* int comparaCu --> Compara doua obiecte de tip Compara
* metoda trebuie definita in clasa care o implementeaza
*/
package clasegenerice;
public interface Compara
{
int comparaCu(Compara rhs);
}
Interfata Compara declara o metoda pe care orice clasa derivata din ea (in declaratia clasei se
foloseste cuvantul-cheie implements) trebuie sa o implementeze. Metoda comparaCu () se va
comporta similar cu metoda compareTo() din clasa String. Metoda este implicit publica si abstracta
si deci nu trebuie sa folosim modificatorii de acces public si de metoda abstract.

4.3 Implementarea unei interfete

O clasa implementeaza o interfata in doi pasi:


- declara ca implementeaza interfata, folosind cuvantul-cheie implements in antetul sau;
- defineste implementari pentru toate metodele din interfata.
Pentru a exemplifica implementarea unei interfete ne vom referi la clasa FormaGeo descrisa in
lectia 6.
Aceasta clasa implementeaza interfata Compara si, deci, trebuie sa definim si metoda comparaCu()
din interfata Compara. Implementarea metodei in clasa FormaGeo trebuie sa fie identica cu
declaratia din interfata si, din acest motiv, metoda comparaCu() are ca parametru un obiect de tip

51
Compara si nu un obiect de tip FormaGeo. Codul clasei este ilustrat in continuare
(FormaGeo.java):
/* Superclasa abstracta pentru forme */
package formegeometrice;
import clasegenerice.*;
public abstract class FormaGeo implements Compara
{
private String nume;
abstract public double arie();
public FormaGeo(String numeForma) {
nume = numeForma;
}
public int comparaCu(Compara rhs) {
if (arie() < ((FormaGeo) rhs).arie())
return -1;
else
if (arie() == ((FormaGeo) rhs).arie())
return 0;
else
return 1;
}

final public String toString() {


return nume + ", avand aria " + arie();
}
}
Nota:
1. O clasa care implementeaza o interfata poate sa extinda si o alta clasa. In acest caz in declaratia
clasei se foloseste mai intai cuvantul-cheie extends urmat de numele clasei din care deriveaza si
apoi cuvantul-cheie implements urmat de numele interfetei pe care o implementeaza.
2. Retinem ca definirea metodelor declarate intr-o interfata trebuie obligatoriu sa fie facuta in
fiecare clasa in parte, care implementeaza interfata respectiva.
3. Metodele interfetei definite in clasa care o implementeaza sunt mostenite de toate subclasele
clasei respective. Aceste metode pot fi redefinite (suprascrise) in subclase.
4. Daca o clasa extinde o superclasa care implementeaza o interfata, atunci si clasa respectiva
mosteneste interfata.

4.4 Implementarea unor interfete multiple


O clasa poate sa implementeze mai mult decat o singura interfata. O clasa poate implementa mai
multe interfe in doi pasi:
- declara toate interfetele pe care le implementeaza, folosind cuvantul-cheie implements in
antetul sau urmat de numele tuturor interfetelor separate prin virgula;
- defineste implementari pentru toate metodele din toate interfetele pe care le
implementeaza.
Folosirea unor interfete multiple poate crea totusi complicatii.

Derivarea interfetelor

Ca si in cazul claselor, interfetele pot fi organizate intr-o ierarhie. Atunci cand o interfata
mosteneste o alta interfata, subinterfata primeste toate metodele si constantele definite in
superinterfata.
Pentru a deriva (extinde) o interfata se foloseste tot cuvantul-cheie extends, la fel ca in cazul
definitiilor de clasa:
public interface <nume_interfata> extends <nume_superinterfata> {
…..
}
Nota: In cazul interfetelor nu exista o radacina comuna a arborelui de derivare asa cum exista
pentru arborele de clasa, clasa Object. Interfetele pot exista independent sau pot mosteni o alta
interfata.
Ierarhia de interfete este cu mostenire multipla. O singura interfata poate mosteni oricate clase are
nevoie.

Exemplu: implementarea unei stive

Dorim sa implementam un nou tip dedate numit Stack, care sa modeleze notiunea de stiva de
obiecte. Obiectelede tip stiva, indiferent de implementarea lor, vor trebui sa contina metodele:
• push - adauga un nou element in stiva
• pop - elimina elementul din varful stivei
• peek - returneaza varful stivei
• empty - testeaza daca stiva este vida
• toString - returneaza continutul stivei sub forma unui sir de caractere.

Din punctul de vedere al structurii interne, o stiva poate fi implementata folosind un vector sau o
lista inlantuita, ambele solutii avand avantaje si dezavantaje. Prima solutie este mai simplu de
inteles, in timp ce a doua este mai eficienta din punctul de vedere al folosirii memoriei. Deoarece nu
dorim sa legam tipul de date Stack de o anumita implementare structurala, il vom defini prin
intermediul unei interfete. Vom vedea imediat avantajele acestei abordari.

public interface Stack {


void push ( Object item ) throws StackException ;
void pop () throws StackException ;
Object peek () throws StackException ;
boolean empty ();
String toString ();
}
// Implementarea stivei folosind un vector de obiecte .
public class StackImpl1 implements Stack {
private Object items [];
// Vectorul ce contine obiectele
private int n=0;
// Numarul curent de elemente din stiva
public StackImpl1 (int max ) {
// Constructor
items = new Object [ max ];
}
public StackImpl1 () {
this (100) ;
}
public void push ( Object item ) throws StackException {
if (n == items . length )
throw new StackException (" Stiva este plina !");
items [n++] = item ;
}

53
public void pop () throws StackException {
if ( empty ())
throw new StackException (" Stiva este vida !");
items [--n] = null ;
}
public Object peek () throws StackException {
if ( empty ())
throw new StackException (" Stiva este vida !");
return items [n -1];
}
public boolean empty () {
return (n ==0) ;
}
public String toString () {
String s="";
for (int i=n -1; i >=0; i --)
s += items [i]. toString () + " ";
return s;
}
}
// Implementarea stivei folosind o lista inlantuita .
public class StackImpl2 implements Stack {
class Node {
// Clasa interna ce reprezinta un nod al listei
Object item ; // informatia din nod
Node link ; // legatura la urmatorul nod
Node ( Object item , Node link ) {
this . item = item ;
this . link = link ;
}
}
private Node top= null ;
// Referinta la varful stivei
public void push ( Object item ) {
Node node = new Node (item , top);
top = node ;
}
public void pop () throws StackException {
if ( empty ())
throw new StackException (" Stiva este vida !");
top = top . link ;
}
public Object peek () throws StackException {
if ( empty ())
throw new StackException (" Stiva este vida !");
return top. item ;
}
public boolean empty () {
return (top == null );
}
public String toString () {
String s="";
Node node = top;
while ( node != null ) {
s += ( node . item ). toString () + " ";
node = node . link ;
}
return s;
}
}
In continuare este prezentata o mica aplicatie demonstrativa care foloseste tipul de date nou creat ¸si
cele doua implementari ale sale:
public class TestStiva {
public static void afiseaza ( Stack s) {
System . out. println (" Continutul stivei este : " + s);
}
public static void main ( String args []){
try {
Stack s1 = new StackImpl1 ();
s1. push ("a");
s1. push ("b");
afiseaza (s1);
Stack s2 = new StackImpl2 ();
s2. push ( new Integer (1));
s2. push ( new Double (3.14) );
afiseaza (s2);
} catch ( StackException e) {
System . err. println (" Eroare la lucrul cu stiva !");
e. printStackTrace ();
}
}
}

55
Teste de control:

4.1 Ce se va afisa la executia urmatorului program Java?


interface I1{
float x=2.3f;
}
public class Test implements I1{
public static void main(String [] args){
System.out.print(x+" ");
x=6.7f;
System.out.print(x);
}
}

A. Va aparea eroare la compilare deoarece valoarea variabilei x nu se mai poate


modifica;
B. La executie se va afisa: 2.3f 6.7f;
C. La executie se va afisa: 2.3f 2.3f;
D. La executie se va afisa: 2.3 6.7;

4.2 Secventa de cod :


interface I1{int x=3;
public void afisare( );
}
class c1 implements I1
{int y;
c1( int y) {{this.y=y;}}
public class test {
public static void main(String args[])
{c1 obiect=new c1(5);
System.out.println(“y=”+obiect.y);}}
Determină:
a).eroare la compilare: metoda abstactă nu este implementată;
b). afiseaza y=5;
c). nu este corect definită interfaţa;
d). o clasa nu poate implementa o interfaţa;

4. Secvenţa uramatoare:
interface I1{ int x=2;}
interface I2 extends I1{ int y=3};
class C1 (int y=4;}
public class extends C1 implements I1
{public static void main(String args[])
{ System.out.println(x+” “+y);}}
a). programul afiseaza 2 3;
b). programul afiseaza 2 4;
c). eroare la compilare: arbitrul y este ambiguu petru C1 si I2;
d). o interfata nu poate extinte o alta interfata;

Capitolul 5 Tratarea exceptiilor

Obiective:

-tratarea erorilor: exceptii Java; ierarhia de exceptii predefinite de platforma Java; verificarea
consistentei exceptiilor Java;
- interceptarea (“prinderea”) si tratarea exceptiilor in cadrul aceleiasi metode: blocul try … catch;
- declararea metodelor care pot semnala (“arunca”) exceptii catre alte metode: clauza throws;
- crearea si semnalarea propriilor exceptii: definirea propriilor clase de exceptii; semnalarea
(“aruncarea”) propriilor exceptii; instructiunea throw;
- cum sa folosim exceptiile?

5.1 Tratarea erorilor

Erorile care apar in faza de executie a unei aplicatii informatice sunt produse din diverse cauze cum
ar fi:
- programatorul nu a anticipat toate situatiile posibile in care ar putea ajunge aplicatia informatica;
- existenta unor situatii aflate in afara controlului programatorului (date eronate primite de la
utilizatori, fisiere care nu mai contin date corecte, dispozitive hardware care nu mai functioneaza
etc).

Exceptii Java

In Java, exceptiile sunt evenimente anormale care au loc in timpul executiei unei aplicatii si care pot
duce la intreruperea executiei normale a acesteia.
Atunci cand aplicatia informatica isi interupe executia normala, inseamna ca a fost semnalata
(thrown - “aruncata”) o exceptie. Faptul ca o exceptie a fost semnalata inseamna, in Java, ca “a
aparut o eroare”.
Exceptiile pot fi semnalate, implicit, de sistem sau, explicit, de aplicatia informatica realizata.
Exceptia poate fi si interceptata (caught - “prinsa”). Interceptarea unei exceptii inseamna tratarea
situatiilor speciale (erorilor) astfel incat aplicatia informatica proiectata sa nu se mai termine
anormal.

57
5.2 Ierarhia de exceptii predefinite de platforma Java

In Java, exceptiile sunt instante ale unor clase derivate din clasa Throwable.
Clasa Throwable, definita in pachetul java.lang, reprezinta clasa de baza pentru intreaga familie de
clase de exceptii. Cand se semnaleaza (este “aruncata”) o exceptie este creata o instanta a unei clase
de exceptii. Figura de mai jos ilustreaza ierarhia claselor de exceptii, pornind de la clasa de baza
Throwable si continuand cu cateva dintre subclasele cele mai importante.

Clasa Throwable are doua subclase directe:


- Error;
- Exception.
Clasa Error, impreuna cu toate clasele derivate din ea, descrie exceptii grave, numite erori. Aceste
erori sunt rare si, de obicei, fatale. In general,ele nu pot fi interceptate de catre aplicatia informatica
in care s-au produs si determina intreruperea executiei programului (de exemplu,
OutOfMemoryError). Java este cea care foloseste acest tip de erori daca are nevoie de ele.

Clasa Exception, impreuna cu toate clasele derivate din ea, reprezinta exceptiile propriu-zise Java.
Exceptiile semnalate descriu conditii anormale, care sunt, de cele mai mute ori, tratate de aplicatie,
desi sunt anumite exceptii care nu pot fi tratate si atunci aplicatia va fi oprita din executie.

Clasa Exception are multe subclase definite in cadrul pachetului java.lang. Aceste subclase indica
diverse tipuri de exceptii care pot aparea in cadrul unei aplicatii Java. De exemplu, tipul de exceptie
NoSuchFieldException semnaleaza ca programul a incercat sa foloseasca o variabila de instanta sau
de clasa care nu exista.

Clasa RuntimeException este foarte des folosita de programatori, deoarece ea este clasa de baza
pentru exceptiile care apar in timpul executiei unui program. De exemplu, tipul de exceptie
ClassCastException (prezentat la inceputul acestei lectii) indica faptul ca aplicatia noastra a incercat
sa faca o conversie explicita de tip pentru un obiect ce nu poate fi convertit la tipul specificat.

Nu toate exceptiile sunt definite in pahetul java.lang. De exemplu, toate exceptiile I/O (de
intrare/iesire) sunt derivate din clasa java.io.IOException si sunt definite in pachetul java.io.

Verificarea consistentei exceptiilor Java


In Java, o metoda poate indica tipurile de exceptii pe care le poate semnala (“arunca”).
Compilatorul Java cat si masina virtuala Java verifica daca aceste exceptii “aruncate” in cadrul unei
metode sunt fie tratate in cadrul acelei metode, fie specificate ca fiind “aruncate” de metoda
respectiva la alte metode in care urmeaza a fi tratate. Aceste tipuri de exceptii se numesc exceptii
verificate.

De exemplu, metodele care ciresc din fisiere sau de la tastatura pot “arunca” exceptii de tipul
IOException. Atunci cand se folosesc aceste metode in aplicatia Java, programatorul trebuie sa aiba
grija sa intercepteze (“sa prinda”) si sa trateze exceptiile de tipul IOException.Compilatorul este cel
care il obliga pe programator sa intercepteze si sa trateze exceptiile de tipul IOException.
In cazul exceptiilor neverificate de catre compilator, programatorul poate decide daca “prinde”
(semnaleaza) si trateaza exceptia sau trece cu vederea exceptia.
Exceptiile verificate sunt toate tipurile de exceptii derivate din clasa Exception (inclusiv clasa
Exception), mai putin clasa RuntimeException si subclasele ei.
Exceptiile neverificate sunt de tipul clasei Throwable, de tipul clasei Error si al subclaselor ei,
precum si de tipul clasei RuntimeException si al subclaselor ei.

Interceptarea (“prinderea”) si tratarea exceptiilor in cadrul aceleiasi metode


In fiecare situatie de exceptie exista doua parti:
- partea care semnaleaza exceptia si
- partea care o intercepteaza si o trateaza.

O exceptie poate fi aruncata de la o metoda la alta de mai multe ori inainte de a fi interceptata, insa
pana la urma ea va fi interceptata si tratata.
Pentru a intercepta si trata o exceptie trebuie parcursi doi pasi:
- gruparea codului care contine metoda ce poate semnala (“arunca”) exceptia in cadrul unui bloc
try;
- interceptarea si tratarea exceptiei in cadrul unui bloc catch.

In cazul exceptiilor neverificate de catre compilator, programatorul poate decide daca “prinde”
(semnaleaza) si trateaza exceptia sau trece cu vederea exceptia.
Exceptiile verificate sunt toate tipurile de exceptii derivate din clasa Exception (inclusiv clasa
Exception), mai putin clasa RuntimeException si subclasele ei.
Exceptiile neverificate sunt de tipul clasei Throwable, de tipul clasei Error si al subclaselor ei,
precum si de tipul clasei RuntimeException si al subclaselor ei.

Interceptarea (“prinderea”) si tratarea exceptiilor in cadrul aceleiasi metode

In fiecare situatie de exceptie exista doua parti:


- partea care semnaleaza exceptia si
- partea care o intercepteaza si o trateaza.

O exceptie poate fi aruncata de la o metoda la alta de mai multe ori inainte de a fi interceptata, insa
pana la urma ea va fi interceptata si tratata.
Pentru a intercepta si trata o exceptie trebuie parcursi doi pasi:
- gruparea codului care contine metoda ce poate semnala (“arunca”) exceptia in cadrul unui bloc
try;
- interceptarea si tratarea exceptiei in cadrul unui bloc catch.

In cazul exceptiilor neverificate de catre compilator, programatorul poate decide daca “prinde”
(semnaleaza) si trateaza exceptia sau trece cu vederea exceptia.

59
Exceptiile verificate sunt toate tipurile de exceptii derivate din clasa Exception (inclusiv clasa
Exception), mai putin clasa RuntimeException si subclasele ei.
Exceptiile neverificate sunt de tipul clasei Throwable, de tipul clasei Error si al subclaselor ei,
precum si de tipul clasei RuntimeException si al subclaselor ei.

Interceptarea (“prinderea”) si tratarea exceptiilor in cadrul aceleiasi metode

In fiecare situatie de exceptie exista doua parti:


- partea care semnaleaza exceptia si
- partea care o intercepteaza si o trateaza.
O exceptie poate fi aruncata de la o metoda la alta de mai multe ori inainte de a fi interceptata, insa
pana la urma ea va fi interceptata si tratata.
Pentru a intercepta si trata o exceptie trebuie parcursi doi pasi:
- gruparea codului care contine metoda ce poate semnala (“arunca”) exceptia in cadrul unui bloc
try;
- interceptarea si tratarea exceptiei in cadrul unui bloc catch.
Blocurile catch de instructiuni sunt asociate cu un bloc try astfel:
try
{…}
catch (…)
{

}
catch (…)
{

}

Forma generala a unui bloc catch este:
catch(<nume_clasa_de_exceptii> <nume_variabila>)
unde:
- <nume_clasa_de_exceptii> - specifica tipul de exceptie care poate fi tratat, tip dat de numele
clasei de exceptie;
- <nume_variabila> - specifica numele prin care poate fi referita instanta ce reprezinta exceptia
tratata de blocul catch; aceasta variabila este recunoscuta si poate fi folosita doar in cadrul blocului
catch.

Instructiunile din blocul catch se executa doar daca instructiunile din blocul try genereaza exceptii
si aceste exceptii sunt instante ale clasei cu numele <nume_clasa_de exceptii> precizat in antetul
instructiunii catch.
Dupa ce exceptia a fost tratata, se executa in continuare urmatoarea linie de cod aflata dupa
secventa catch.

Urmatorul exemplu (cireste un sir de caractere si “incearca”, intr-un bloc try, sa il converteasca la
un numar intreg. In caz de nereusita, se “intercepteaza”, in blocul catch, o exceptie de tip
NumberFormatException, care este “aruncata” de metoda Integer.parseInt() atunci cand numarul
introdus nu este un numar intreg. Tratarea acestui tip de eroare se face printr-un mesaj de avertizare
si returnarea valorii implicite 0 pentru un numar intreg.

De asemenea, atunci cand “se incearca” citirea sirului de caractere de la tastatura, intr-un bloc tray,
“se pot intercepta”, intr-un bloc catch, eventualele exceptii de tip IOException, care sunt “aruncate”
de metoda readLine(). Tratarea acestui tip de eroare se face prin returnarea valorii implicite null
pentru un String.
package intrareiesire;
import java.io.*;
/* cieste un sir de caractere si incerca sa il converteasca la tipul int */
public class CitesteDate
{
public static String citesteString() {
BufferedReader br = new BufferedReader(new
InputStreamReader(System.in));
try {
return br.readLine(); }
catch(IOException ioe) {
return null; }
}
public static int citesteNrInt() {
try {
return Integer.parseInt(citesteString()); }
catch(NumberFormatException nfe) {
System.out.println("Nu este numar intreg.");
return 0; }
} }
Programul urmator (TestCiesteNumere.java) apeleaza metodele din clasa CitesteDate, (care
semnaleaza, intercepteaza si trateaza erorile care apar in cadrul lor) pentru a citi un sir de numere de
tastatura.

import intrareiesire.*;
class TestCitesteNumere {
public static void main(String[] args) {
citesteDate valIntreg = new citesteDate();
System.out.println("Introduceti numarul de elemente ale sirului:");
int n = valIntreg.citesteNrInt();
int [] numere = new int [n];
System.out.println ("Introduceti elementele sirului: ");
for (int i = 0; i < numere.length; i++)
numere [i] = valIntreg.citesteNrInt();
System.out.println ("Sirul de numere introdus este: ");
for (int i = 0; i < numere.length; i++)
System.out.print(numere[i] + " "); } }

Exemplul, descris mai sus, prezinta modul de interceptare a unui anumit tip de exceptie. Deoarece
clasele de exceptii sunt organizate ierarhic, nu este necesar ca tipul de exceptie tratat de blocul
catch si tipul de exceptie “aruncat” de o metoda din blocul try sa fie identice. Un bloc catch, care
trateaza un tip de exceptii va intercepta si trata si clasele de exceptii derivate din exceptia
respectiva.

In exemplul de fata, se poate intercepta in blocul catch, exceptia creata de tipul


NumberFormatException, folosind clasa Exception care este superclasa pentru
NumberFormatException. Astfel, codul ar putea arata astfel:
public static int citesteNrInt()
{
try {

61
return Integer.parseInt(citesteString()); }
catch(Exception nfe) {
System.out.println("Nu este numar intreg.");
return 0; }
}
}
In aceasta situatie, blocul catch intercepteaza toate exceptiile Exception, dar si clase de exceptii
derivate din clasa Exception, cum este si cazul clasei de exceptii FormatNumberException.

Se prefera totusi, utilizarea blocurilor catch specializate, care ofera mai multe detalii despre situatia
de eroare, si nu a celor generale.

Nota: Toate clasele de exceptii mostenesc de la clasa Throwable cateva metode utile pentru aflarea
de informatii despre situatia de eroare. O folosire uzuala are metoda getMessage(). Ea afiseaza un
mesaj detaliat referitor la ce s-a intamplat.

Daca se doreste interceptarea si tratarea, in blocul catch, a unor tipuri foarte diferite de exceptii
semnalate in blocul try, care nu sunt inrudite prin mostenire, atunci se asocieaza blocului try mai
multe blocuri catch.
Blocurile catch sunt examinate in ordinea in care ele apar in fisierul sursa. In timpul procesului de
examinare, primul bloc catch care va corespunde exceptiei semnalate va fi executat, iar celelalte
ignorate.

Declararea metodelor care pot semnala (“arunca”) exceptii catre alte metode (transmiterea
exceptiilor catre baza stivei de apel)

In paragraful anterior, am descris cazul in care exceptiile sunt interceptate (“prinse”) si tratate in
cadrul aceleiasi metode, nefiind transmise altor metode care apeleaza metoda respectiva.
A doua posibilitate este aceea de a specifica exceptiile care nu sunt tratate in cadrul metodei
respective, fiind semnalate (“aruncate”) mai departe catre alte metode care le vor intercepta
(“prinde”) si le vor trata.

Atunci cand programatorul decide ca o metoda, din aplicatia informatica, sa nu intercepteze o


anumita exceptie care poate fi semnalata de codul din interiorul ei, trebuie sa se precizeze ca
metoda respectiva poate semnala (“arunca”) la randul ei exceptia respectiva catre alte metode care o
apeleaza.

Pentru a indica faptul ca o metoda poate semnala (“arunca”) o exceptie, catre alte metode care o
apeleaza, se foloseste in antetul metodei respective clauza throws in care se specifica numele
exceptiilor pe care le poate semnala (“arunca”) metoda respectiva.

Observatii:
1.Exceptiile specificate in clauza throws nu trebuie sa fie interceptate (“prinse”) si tratate in cadrul
metodei respective care foloseste clauza throws.

2.Semnalarea (“aruncarea”) exceptiilor incepe cu metoda originala care daca nu intercepteaza si nu


trateaza exceptiile generate, le semnaleaza (“le arunca”) mai departe metodei care o apeleaza, care
la randul ei daca nu le intercepteaza si nu trateaza le semnaleaza (“le arunca”) mai departe unei alte
metode care o apeleaza pe aceasta din urma si asa mai departe. In final exceptia va trebui
interceptata (“prinsa”) si tratata in cadrul unei metode. Astfel, masina virtuala Java cauta in stiva de
apel metoda care intercepteaza si trateaza exceptia, pornind de la metoda in care a aparut exceptia,
deci dinspre varful stivei spre baza stivei.
Forma antetului metodei care specifica tipurile de exceptii semnalate (“aruncate”) de metoda este:
[<modificatori_acces>] [<modificatori_metoda>] <tip_returnat> <nume_metoda>
([<param1>, <param2>, …]) throws <nume_clasa_de_exceptii1>[,
<nume_clasa_de_exceptii2>, …]

Nota: Specificarea prin clauza throws a exceptiilor aruncate de metoda originala nu se poate omite,
daca metoda respectiva genereaza exceptii pe care nu le trateaza, deoarece compilatorul detecteaza
aceasta “scapare” si o raporteaza ca eroare de compilare, propunand doua solutii: tratarea lor in
cadrul metodei (prin blocuri try…catch) sau specificarea lor in clauza throws.

Exemplul prezentat anterior, al clasei CitesteDate, poate fi modificat astfel incat exceptiile de tipul
IOException care sunt semnalate (“aruncate”) in metoda citesteString (), de catre metoda
readLine(), sa fie semnalate (“aruncate”) mai departe in metoda citesteNrInt(), care apoi sa le
semnaleaza metodei main() din clasa TestCitesteNumereThrows (care reprezinta aplicatia Java).

Astfel, s-a creat o noua clasa CitesteDateThrows care are definitia:


package intrareiesire;
import java.io.*;
/* cieste un sir de caractere si incerca sa il converteasca la tipul int */
public class CitesteDateThrows
{
public static String citesteString() throws IOException
{
BufferedReader br = new BufferedReader(new
InputStreamReader(System.in));
return br.readLine();
}
public static int citesteNrInt() throws IOException
{
try
{
return Integer.parseInt(citesteString());
}
catch(NumberFormatException nfe)
{
System.out.println("Nu este numar intreg.");
return 0;
}
}
}
Clasa TestCitesteNumereThrows (care reprezinta programul principal) are definitia
data de secventa de cod:
import intrareiesire.*;
import java.io.*;
class TestCitesteNumereThrows
{
public static void main(String[] args) throws IOException {
CitesteDateThrows valIntreg = new CitesteDateThrows();
System.out.println("Introduceti numarul de elemente ale sirului:");
int n = valIntreg.citesteNrInt();
int [] numere = new int [n];

63
System.out.println ("Introduceti elementele sirului: ");
for (int i = 0; i < numere.length; i++)
numere [i] = valIntreg.citesteNrInt();
System.out.println ("Sirul de numere introdus este: ");
for (int i = 0; i < numere.length; i++)
System.out.print(numere[i] + " ");
}
}

5.3 Crearea si semnalarea propriilor exceptii

Pe langa exceptiile definite de platforma Java, programatorul poate defini propriile clase de
exceptii. Aceste clase proprii de exceptii descriu situatii de exceptii particulare aplicatiei respective
care nu exista in ierarhia de clase de exceptii oferita de platforma Java.
Clasele proprii de exceptii sunt derivate (direct sau indirect) din clasa Throwable.
De regula, se creaza propriile clase de exceptii derivate din clasa Exception.
Nota: In definirea noilor clase de exceptii, se recomanda adaugarea la numele clasei a sufixului
Exception, pentru toate clasele derivate (direct sau indirect) din clasa Exception (de exemplu,
MyNewException).
Clasele proprii de exceptii sunt clase Java obisnuite.
Clasele proprii de exceptii poseda, de obicei, doi constructori:
- primul constructor nu primeste nici un parametru;
- al doilea constructor primeste ca parametru un sir de caractere care reprezinta un mesaj ce
evidentiaza situatia de eroare.

Definirea propriilor clase de exceptii


De regula, o clasa proprie de exceptii este definita ca in exemplul de mai jos:
package exceptii;
public class IndexException extends Exception
{
public IndexException()
{
super();
}
public IndexException(String msg)
{
super(msg);
}
}

Clasa IndexException definita mai sus nu aduce practic nimic nou clasei Exception, deoarece
defineste doar doi constructori care apeleaza constructorii superclasei Exception. Totusi, tipul
exceptiei in sine este important pentru ca ajuta programatorul sa intercepteze tip respectiv de
exceptie atunci cand este necesar.

Semnalarea (“aruncarea”) propriilor exceptii


Sa ne reamintim ca, toate exceptiile sunt instante ale unei clase de exceptie oarecare, definite fie in
biblioteca Java standard, fie de programator.
Pentru a semnala (“arunca”) un tip de exceptie definita de programator, trebuie sa se creeze, mai
intai, o instanta a sa.
Folosind definirea de clasa de exceptie prezentata, crearea unei instante a clasei de excepie
IndexException poate fi facuta in doua moduri:
new IndexException();
new IndexException("Stiva vida. ");

In cel de al doilea caz situatia de eroare transmite si o informatie, sub forma mesajului “Stiva
vida.”, care poate fi folosit de codul care trateaza exceptia aparuta pentru a afisa un mesaj
informativ.

Odata creata o instanta a unei clase proprii de exceptii, aceasta devine o exceptie Java si este
semnalata (“aruncata”) folosind instructiunea throw.
Instructiunea throw are un singur argument, care trebuie sa fie o instanta a unei clase de exceptii
derivate din clasa Exception.
Forma instructiunii throw este:
throw <instanta_clasa_de_exceptii>;

De exemplu, pentru semnalarea (“aruncarea”) unei exceptii din metoda topAndPop() (exceptie care
poate fi generata cand o stiva este vida) se poate folosi urmatoarea secventa de cod:
public Object topAndPop() throws IndexException
{
if (isEmpty())
{
throw new IndexException("Stiva vida. ");
}
return elemStiva[pozitieVarf--];
}
Pentru transmiterea exceptiei semnalate (“aruncate”) in metoda topAndPop(), cu ajutorul
instructiunea throw, catre o alta metoda apelanta se foloseste clauza throws in antetul metodei
topAndPop(). In metoda apelanta exceptia IndexException se poate, eventual intercepta (“prinde”)
si trata sau poate fi transmisa mai departe la o alta metoda apelanta.

In exemplul prezentatat, interceptarea si tratarea exceptiei de tip IndexException este realizata in


metoda main() a aplicatiei (clasa TestStiva1). Iata codul-sursa in care se realizeaza acest lucru:
/* Clasa de test simpla pentru o stiva, care adauga 10 numere
* dupa care le extrage in ordine inversa */
import clasegenerice.*;
import exceptii.*;
public class TestStiva1
{
public static void main(String[] args) {
Stiva1 s = new StivaArr(10);

//introducem elemente in stiva


for (int i = 0; i < 10; i++)
{
s.push(new Integer(i));
}
//scoatem elementele din stiva si le afisam
System.out.print("Continutul stivei este: ");
try
{
while (true)

65
{
System.out.print(s.topAndPop() + " ");
}
}
catch(IndexException ie)
{
System.out.println();
System.out.print("Eroare: " + ie.getMessage());
}
}
}
Observatie: In secveta de mai sus, iesirea din bucla while se realizeaza in momentul in care stiva
devine vida si metoda topAndPop() semnaleaza (“arunca”) exceptia IndexException care este
interceptata si tratata in blocul catch.

Concluzii privind folosirea eficienta a exceptiilor

Cum sa folosim exceptiile?


1. Daca o metoda din aplicatia Java apeleaza o alta metoda in care s-a folosit clauza throws, avem
trei posibilitati:
- se trateaza exceptia folosind blocurile try si catch;
- se transmite exceptia mai departe pe lant (spre baza stivei de apel), adaugand in metoda respectiva
clauza throws;
- se realizeaza ambele metode descrise mai sus, atat pentru a intercepta (“prinde”) si trata exceptia
folosind blocurile try … catch, cat si pentru a o resemnala explicit folosind instructiunea throw.

2. Daca o metoda din aplicatia Java semnaleaza (“arunca”) propriile exceptii, aceasta metoda
trebuie sa fie declarata folosind clauza throws. De asemenea, exceptia trebuie semnalata
explicit in corpul metodei, folosind instructiunea throw.

3. Daca metoda din aplicatia Java suprascrie metoda unei subclase care contine clauza throws,
atunci se pot semnala numai aceleasi tipuri de exceptii sau subclase ale exceptiilor respective; nu se
pot semnala alte tipuri de exceptii.

Modul incorect de folosire a exceptiilor

1. Sistemul de exceptii al Java a fost proiectat in asa fel incat, daca apare o eroare, sa fim avertizati
in legatura cu aceasta. Ignorarea acestor avertismente si nerespectarea lor duce la posibila aparitie a
unor erori fatale in cadrul aplicatiilor.
Mai mult, folosirea de clauze throws in propriile metode in scopul de a evita tratarea exceptiilor
face ca utilizatorii acestor metode (adica obiectele aflate mai sus pe lantul de apeluri) sa fie nevoiti
sa le trateze. Astfel metodele noastre se complica nejustificat. Erorile semnalate de compilator cu
privire la exceptii au rolul de a ne atrage atentia asupra acestor probleme.

2. Ca regula generala, nu este recomandata utilizarea exceptiilor in cazul in care situatia este
previzibila in aplicatia Java (de exemplu, sfarsitul unui fisier, depasirea dimensiunii unui tablou
etc).
Teste de control

5.1 Fie urm¸atorul cod Java:


int x=0;
if (double.isInfinite(2/x))
System.out.println("Infinit");
else
System.out.println("2/0");
Ce puteti spune despre acest cod, daca este integrat in cadrul unui program Java?
A. Va produce eroare la compilare din cauza impartirii la 0;
B. Va produce eroare la executie din cauza impartirii la 0 (se arunca o except»ie:
"ArithmeticExpetion");
C. Codul este corect si va afisa Infnit;
D. Codul este corect si va afisa NaN

5.2 Prin modalitatea sa de tratare a exceptiilor, Java are urmatoarele avantaje fata de mecanismul
traditional de tratare a erorilor:
A. Exista o metoda care se ocupa cu acest lucru;
B. Separarea codului pentru tratarea unei erori de codul in care ea poate sa apara;
C. Propagarea unei erori pana la un analizor de exceptii corespunzator;
D. Gruparea erorilor dupa tipul lor;

5.3 Metodele care sunt apelate uzual pentru un obiect de tip exceptie sunt defnite in clasa
Throwable si sunt:
A. Declarate cu modicatorul de acces private;
B. dinamice;
C. publice;
D. exceptii;

67
Capitolul 6 Intarari si iesiri

Obiective:

-tipuri de fluxuri de intrare/iesire Java: fluxuri de octeti, fluxuri de caractere; System.in, System.out,
System.err; fluxuri-filtre;
- fluxuri de octeti si clase predefinite Java utilizate: fluxuri nefiltrate de intrare care folosesc ca
sursa un fiser; fluxuri nefiltrate de iesire care folosesc ca destinatie un fisier;
- filtrarea fluxurilor de octeti si clase predefinite Java utilizate: zona tampon (“buffer”); fluxuri de
intrare cu o zona tampon; fluxuri de iesire cu o zona tampon; fluxuri filtrate cu date de tipuri
primitive; fluxuri filtrate cu date formatate;
- fluxuri de caractere si clase predefinite Java utilizate: fluxuri nefiltrate de intrare care folosesc ca
sursa un fisier; fluxuri filtrate de intrare cu o zona tampon; fluxuri filtrate de intrare cu o zona
tampon care tin evidenta numerelor de linie.

6.1 Tipuri de fluxuri de intrare/iesire Java

Toate datele din Java sunt scrise sau citite folosind fluxuri.
Un flux (stream) reprezinta o succesiune de octeti (bytes) sau de caractere transportat de la sau catre
memoria RAM in care se afla programul Java.

Un flux de intrare transporta datele de la o sursa externa (tastatura, fisier pe hard-disc etc) catre
programul Java.

Un flux de iesire transporta datele din programul Java catre o destinatie externa (ecranul
calculatorului, fisier pe hard-disc etc).

Fluxurile de intrare/iesire in Java sunt create si manevrate cu ajutorul claselor din pachetul
predefinit java.io. Din acest motiv orice program care executa operatii de intrare/iesire trebuie sa
includa instructiunea:
import java.io.*;
Numarul claselor si a metodelor lor, definite in pachetul java.io, este foarte mare. De aceea se
recomanda programatorului sa consulte in permanenta documentatia ce insoteste versiunea de Java
folosita.
Java foloseste doua tipuri principale de fluxuri:
- fluxuri de octeti;
- fluxuri de caractere.

Fluxurile de octeti pot pastra valori intregi din domeniul 0 - 255. In acest format pot fi reprezentate
o multitudine de date, cum ar fi: date numerice, programe executabile, comunicatii Internet sau cod
Java (bytecode) etc.
Generic, fluxurile de octeti sunt subclase ale claselor abstracte predefinite:
- InputStream - pentru fluxuri de octeti de intrare si
- OutputStream - pentru fluxuri de octeti de iesire.

Fluxurile de caractere reprezinta un tip special de flux de octeti, care se folosesc numai pentru date
de tip text (tiparibile). Ele difera de fluxurile de octeti prin faptul ca setul de caractere Java suporta
codificarea Unicode (cu doi octeti pe caracter).
Toate datele de tip text, cum ar fi fisierele text, paginile Web, sau alte formate de text, este necesar
sa foloseasca fluxurile de caractere.

Generic, fluxurile de caractere sunt subclase ale claselor abstracte predefinite:


- Reader - pentru fluxuri de caractere de intrare si
- Writer - pentru fluxuri de caractere de iesire.

Java ofera trei fluxuri predefinite pentru operatii de I/O standard (de la tastatura, la ecranul
calculatorului sau pentru erori) care sunt campuri ale clasei System inclusa in pachetul java.lang:
- System.in - reprezinta intrarea standard (implicit, tastatura calculatorului); acest flux este de tip
InputStream;
- System.out - reprezinta iesirea standard (implicit, ecranul calculatorului); acest flux este de tip
PrintStream;
- System.err - reprezinta fluxul standard de iesire pentru mesaje de eroare (implicit, ecranul
calculatorului); acest flux este de tip PrintStream.

Dintre metodele clasei System, referitoare la fluxurile predefinite descrise mai sus, mentionam:
- metoda setIn, care redirecteaza fluxul de intrare standard catre o alta sursa decat tastatura (de
exemplu, un fisier pe disc); aceasta metoda are antetul:
void setIn(InputStream <in>)

- metoda setOut, care redirecteaza fluxul de iesire standard catre o alta destinatie decat ecranul
calculatorului (de exemplu, un fisier pe disc); aceasta metoda are antetul:
void setOut(PrintStream <out>)

- metoda setErr, care redirecteaza fluxul de iesire standard al mesajelor de eroare catre o alta
destinatie decat ecranul calculatorului (de exemplu, un fisier pe disc); aceasta metoda are antetul:
void setErr(PrintStream <out>)

Asocierea fluxului de intrare sau de iesire cu un filtru

Majoritatea claselor Java folosite in prezent permit realizarea de operatii de intrare/iesirmai


performante prin asocierea fluxului cu un filtru, inainte de a citi sau scrie date.

Un filtru este un tip de flux care schimba modul in care se lucreaza cu un flux existent.
Procedura de folosire a unui filtru pentru un flux presupune urmatorii pasi:
1. crearea (deschiderea) unui flux asociat cu sursa de date sau cu destinatia datelor;
2. crarea (deschiderea) unui flux de tip filtru asociat cu fluxul deschis la pasul 1;
3. citirea/scrierea datelor de la /in filtru si nu direct in flux.

6.2 Fluxuri de octeti si clase predefinite Java

A. Fluxuri nefiltrate de octeti si clase predefinite utilizate

69
In schema de mai jos se prezinta doar o parte a structurii de clase ce poate fi folosita in operatiile cu
fluxuri de intrare si iesire nefiltrate.

Fluxuri nefiltrate de octeti de intrare


care folosesc ca sursa un fisier

Sunt folosite pentru transferul de date de la fisierele aflate pe hard-discuri, pe CD-ROM sau pe alte
dispozitive de stocare (ce pot fi referite printr-o cale de director si un nume) catre aplicatia Java.

Crearea (deschiderea) unui flux de intrare de la un fisier se realizeaza cu ajutorulconstructorului


clasei FileInputStream, care are forma:
FileInputStream(String <sir>)
unde:
- <sir> - reprezinta numele, si eventual calea, fisierului; la specificarea caii fisierului trebuie sa se
foloseasca doua caractere backslash pentru a nu se confunda cu o secventa escape.

Daca fisierul nu poate fi deschis, atunci este lansata exceptia FileNotFoundException.


De exemplu, urmatoarea instructiune deschide un flux de intrare de la fisierul “test.dat”:

FileInputStream fis = new FileInputStream(“test.dat”);

Dupa deschiderea fluxului de intrare din fisier se pot folosi metodele acestui flux pentru realizarea
diverselor operatii de intrare. Descriem cateva dintre ele.
Metoda read() citeste un octet din fluxul de intrare; rezultatul intors este un intreg din intervalul 0 -
255. Daca s-a detectat sfarsitul de fisier, rezultatul intors este -1.
Metoda read cu trei parametrii are forma:
read (byte [] <b>, int <poz_ini>, int <lungime>)
unde:
- <b> - un tablou de octeti in care se vor memora datele citite;
- <poz_ini> - pozitia elementului din cadrul tabloului unde se va stoca primul octet de date;
- <lungime> - numarul de octeti care se vor citi din fluxul de intrare repetand metoda read fara
parametri.

Metoda returneaza un intreg care reprezinta numarul de octeti cititi sau -1 daca, de la inceput, fluxul
de intrare este la sfarsitul sau.

Metoda skip avanseaza cu n pozitii (octeti) in fluxul de intrare, nedepasind bineinteles sfarsitul
fluxului. Rezultatul intors de metoda este numarul de pozitii peste care s-a trecut efectiv.
Metoda skip are forma:

skip(long <n>)
unde :
<n> - nr de octeti sariti in fluxul de intrare.

Metoda available() este folosita pentru a returna numarul de octeti ce mai pot fi cititi la momentul
curent din fluxul de intrare.
Metoda close() inchide fluxul de intrare respectiv si elibereaza resursele sistem asociate cu acesta.
De exemplu, programul “CitesteFiserOcteti .java” citeste octeti dintr-un flux de intrare al unui fisir
cu numele “test.doc”. Dupa citirea ultimului octet din fisier, pentru inchiderea fluxului de intrare se
foloseste metoda close(). Aceasta operatie trebuie realizata pentru a elibera resursele sistemului
asociate fisierului deschis.
Fisierul de intrare cu numele “test.dat” din care se citesc date in forma binara contine doua linii:
0123456789
Ionescu Florin
Acest fisier a fost creat cu utilitarul Notepad.
import java.io.*;
public class CitesteFisierOcteti
{
public static void main(String [] args)
{
try {
FileInputStream fis = new FileInputStream("test.dat");
int index = 0;
int octet = fis.read();
while (octet != -1)
{
System.out.print(octet + " ");
index++;
octet = fis.read();
}
fis.close();
System.out.println("\nOcteti cititi: " + index);
}
catch (IOException e) {
System.out.println("Eroare - " + e.getMessage());
}
}
}
Dupa executia programului va fi afisat fiecare octet din fisierul “test.dat”, urmat de numarul total de
octeti cititi, ca mai jos:
48 49 50 51 52 53 54 55 56 57 13 10 73 111 110 101 115 99 117 32 70 108 111 114
105 110
Octeti cititi: 26
Observatie: Se observa ca valorile octetilor afisati sunt codurile ASCII ale caracterelor din cele
doua linii existente in fisier.

Fluxuri nefiltrate de octeti de iesire care folosesc ca destinatie un fisier

Sunt folosite pentru transferul de date de la aplicatia Java catre fisierele aflate pe hard-discuri, pe
CD-ROM sau pe alte dispozitive de stocare (ce pot fi referite printr-o cale de director si un nume).

Crearea (deschiderea) unui flux de iesire catre un fisier se realizeaza cu ajutorul constructorului
clasei FileOutputStream, care are forma:
FileOutputStream(String <sir>)
unde:
- <sir> - reprezinta numele, si eventual calea, fisierului; la specificarea caii fisierului trebuie sa se
foloseasca doua caractere backslash pentru a nu se confunda cu o secventa escape.

71
Trebuie sa se aiba o grija deosebita atunci cand se specifica numele fisierului in care se scrie. Daca
se foloseste numele unui fisier deja existent, odata cu inceperea scrierii datelor, acesta va fi sters
definitiv.

Constructorul cu doi parametrii are forma:


FileOutputStream(String <sir>, boolean <adauga>)
unde:
- <sir> - reprezinta numele, si eventual calea, fisierului; la specificarea caii fisierului trebuie sa se
foloseasca doua caractere backslash pentru a nu se confunda cu o secventa escape.
- <adauga> - poate avea valoare true, caz in care se face adaugarea datelor la sfarsitul fisierului,
sau poate avea valoarea false, caz in care nu se face adaugarea de date la sfarsitul fisierului ci
suprascrierea datele existente; daca fisierul nu poate fi deschis, atunci este lansata exceptia
FileNotFoundException.

De exemplu, urmatoarea instructiune deschide un flux de iesire catre fisierul “test1.dat”:


FileOutputStream fis = new FileOutStream(“test1.dat”);

Urmatoarea instructiune deschide un flux de iesire catre fisierul “test1.dat” pentru a adauga octeti la
sfarsitul fisierului:
FileOutputStream fis = new FileOutStream(“test1.dat”, true);

Dupa deschiderea fluxului de iesire din fisier se pot folosi metodele acestui flux pentru realizarea
diverselor operatii de iesire. Descriem cateva dintre ele.
Metoda write cu un parametru scrie un octet in fluxul de iesire. Aceasta metoda are forma:
write(int <b>)
unde: <b> - variabila care contine octetul de scris in fluxul de iesire.

Metoda write cu doi parametrii scrie un tablou de octeti in fluxul de iesire si are forma:
write (byte [] <b>)
unde:
- <b> - specifica un tablou de octeti care va fi scris in fluxul de iesire; in fisier vor fi scrisi
b.length octeti.
Metoda write cu trei parametrii are forma:
write (byte [] <b>, int <poz_ini>, int <lungime>)
unde:
- <b> - specifica un tablou de octeti care va fi scris in fluxul de iesire;
- <poz_ini> - pozitia elementului din cadrul tabloului de la care se incepe scrierea in fluxul
de iesire;
- <lungime> - numarul de octeti care se vor scrie in fluxul de iesire.

Metoda close() inchide fluxul de iesire respectiv.


Programul urmator (ScrieFisierOcteti.java) scrie un tablou de octeti intr-un flux de iesire intr-un
fisier date cu numele “test1.gif”. Dupa scrierea ultimului octet in fisier, pentru inchiderea fluxului
de iesire se foloseste metoda close(). Aceasta operatie trebuie realizata pentru a elibera resursele
sistemului asociate fisierului deschis.
import java.io.*;
public class ScrieFisierOcteti
{
public static void main (String [] args)
{
int [] octeti = { 71, 73, 70, 56, 57, 97, 15, 0, 15, 0,
128, 0, 0, 111, 111, 111, 0, 0, 0, 44, 0, 0, 0,
0, 15, 0, 15, 0, 0, 2, 33, 132, 127, 161, 200,
185, 205, 84, 128, 241, 81, 35, 175, 155, 26,
228, 25, 105, 33, 102, 0, 165, 201, 145, 169,
154, 142, 112, 0, 200, 200, 0, 200, 200, 200, 200 } ;
try {
FileOutputStream fis = new FileOutputStream("test1.gif");
for (int i = 0; i < octeti.length; i++)
fis.write(octeti[i]);
fis.close();
}
catch(IOException e) {
System.out.println("Eroare - " + e.getMessage());
}
}
}

B. Filtrarea fluxurilor de octeti si clase predefinite Java utilizate

Fluxurile filtrate sunt fluxuri care selecteaza si, astfel, modifica informatia transmisa printr-un flux
existent (de tip InputStream sau OutputStream). Acestea sunt create folosind subclase ale claselor
FilterInputStream si FilterOutputStream.

Fluxurile FilterInputStream si FilterOutputStream nu realizeaza in mod direct nici un fel de


operatie de filtrare. Dar, din ele deriveaza subclase (de exemplu, BufferedInputStream) care sunt
folosite pentru anumite tipuri de filtrari.
In schema de mai jos se prezinta doar o parte a structurii de clase ce poate fi folosita in operatiile cu
fluxuri de intrare si iesire filtrate.

B.1 Fluxuri de octeti cu o zona tampon (“buffer”)

Un tampon (“buffer”) reprezinta o zona de memorie RAM in care se pot pastra date din fluxul de
intrare sau de iesire pana la momentul citirii sau scrierii lor intr-o aplicatie Java. Prin folosirea unui
tampon se pot accesa (citi sau scrie) date fara a folosi sursa originala (fisierul) sau destinatia
originala (fisierul) de date.

Zona tampon este folosita ca o zona intermediara pentru citirea/ scrierea datelor din/in fluxul de
intrare/iesire. Pentru memoria tampon se mai foloseste si termenul de “memorie de lucru”.
Aceasta tehnica este mai eficienta intrucat utilizarea zonei tampon evita accesarea fisierului pentru
fiecare citire/scriere, in acest mod micsorandu-se timpul de lucru al aplicatiei Java.

73
Nota: Deoarece fluxurile de intrare/iesire cu tampon semnaleaza exceptia IOException la aparitia
unei erori, toate operatiile legate de fluxuri se incadreaza in blocuri try … catch, care sa
intercepteze aceasta exceptie.

Fluxuri de octeti de intrare cu o zona tampon

Un flux de intrare cu tampon poseda un tampon cu date din care se face citirea. Cand aplicatia Java
citeste date, acestea sunt cautate mai intai in zona tampon si apoi in sursa originala de intrare.
Fluxurile de octeti de intrare cu tampon folosesc clasa BufferedInputStream.
Crearea (deschiderea) unui flux de intrare cu tampon se realizeaza cu ajutorul constructorilor clasei
BufferedInputStream. Cand este deschis un flux de intrare cu tampon se creaza si o zona tampon,
sub forma unui tablou de octeti, atasata acestuia.
Constructorii clasei BufferedInputStream au una din formele:
a) BufferedInputStream(InputStream <in>)
unde:
- <in> - reprezinta fluxul original de tip InputStream.
Nota: Zona tampon are o lungime care este aleasa implicit (automat).

b)BufferedInputStream(InputStream <in>, int <lg_buffer>)


unde:
- <in> - reprezinta fluxul original de tip InputStream;
- <lg_buffer> - reprezinta lungimea zonei tampon a fluxului de intare.
Dupa deschiderea fluxului de intrare cu tampon se pot folosi metodele acestui flux pentru realizarea
diverselor operatii de intrare. Descriem cateva dintre ele.

Metoda read() citeste un octet din fluxul de intrare cu tampon; rezultatul intors este un intreg din
intervalul 0 - 255. Daca s-a detectat sfarsitul de fisier, rezultatul intors este -1.
Metoda read cu trei parametrii are forma:
read (byte [] <b>, int <poz_ini>, int <lungime>)
unde:
- <b> - un tablou de octeti in care se vor memora datele citite;
- <poz_ini> - pozitia elementului din cadrul tabloului unde se va stoca primul octet de date;
- <lungime> - numarul de octeti care se vor citi din fluxul de intrare cu tampon repetand metoda
read fara parametri.

Metoda returneaza un intreg care reprezinta numarul de octeti cititi sau -1 daca, de la inceput, fluxul
de intrare este la sfarsitul sau.
Metoda skip avanseaza cu n pozitii (octeti) in fluxul de intrare cu tampon si descarca n octeti din
flux, nedepasind, bineinteles, sfarsitul fluxului. Rezultatul intors de metoda este numarul de pozitii
peste care s-a trecut efectiv.
Metoda skip are forma:
skip(long <n>)
unde :
<n> - nr de octeti sariti in fluxul de intrare si descarcati din fluxul de intare.

Metoda available() este folosita pentru a returna numarul de octeti ce mai pot fi cititi la momentul
curent atat din zona tampon, cat si din fluxul de intrare.

Metoda close() inchide fluxul de intrare cu tampon si elibereaza resursele sistem asociate cu acesta.

Fluxuri de octeti de iesire cu o zona tampon


Un flux de iesire cu tampon poseda un tampon cu date care nu au fost inca scrise in destinatia
originala a fluxului. Atunci cand datele sunt directionate intr-un flux de iesire cu tampon, continutul
acestuia nu va fi transmis catre destinatie decat dupa ce zona tampon atasata s-a umplut. Fluxurile
de octeti de iesire cu tampon folosesc clasa BufferedOutputStream.

Crearea (deschiderea) unui flux de iesire cu tampon se realizeaza cu ajutorul constructorilor clasei
BufferedOutputStream. Cand este deschis un flux de iesire cu tampon se creaza si o zona tampon,
sub forma unui tablou de octeti, atasata acestuia.
Constructorii clasei BufferedOutputStream au una din formele:
a) BufferedOuputStream(OutputStream <out>)
unde:
- <out> - reprezinta fluxul de iesire de tip OutputStream.
Nota: Zona tampon are o lungime care este aleasa implicit (automat).
b)BufferedOutputStream(OutputStream <out>, int <lg_buffer>)
unde:
- <out> - reprezinta fluxul de iesire de tip OutputStream;
- <lg_buffer> - reprezinta lungimea zonei tampon a fluxului de iesire.

Dupa deschiderea fluxului de iesire cu tampon se pot folosi metodele acestui flux pentru realizarea
diverselor operatii de iesire. Descriem cateva dintre ele.
Metoda write cu un parametru scrie un octet in fluxul de iesire cu tampon. Aceasta metoda are
forma:
write(int <b>)
unde: <b> - variabila care contine octetul de scris in fluxul de iesire cu tampon; valoarea acestei
variabile trebuie sa apartina intervalului 0 - 255; daca se incearca scrierea unei valori care este mai
mare decat 255 va fi stocat numai restul impartirii acestei valori la 256.

Metoda write cu trei parametrii are forma:


write (byte [] <b>, int <poz_ini>, int <lungime>)
unde:
- <b> - specifica un tablou de octeti care va fi scris in fluxul de iesire cu tampon;
- <poz_ini> - pozitia elementului din cadrul tabloului de la care se incepe scrierea in fluxul de iesire
cu tampon;
- <lungime> - numarul de octeti care se vor scrie in fluxul de iesire cu tampon.

Metoda flush() transmite (scrie) continutul zonei tampon la destinatia fluxului de iesire original
chiar daca aceasta zona tampon nu s-a umplut inca. Cu ajutorul acestei metode se realizeaza golirea
zonei tampon chiar daca aceasta nu s-a umplut inca.

Metoda close() inchide fluxul de iesire cu tampon si elibereaza resursele sistem asociate cu acesta.
Urmatorul program (IOBinarTampon.java) scrie o serie de octeti intr-un flux de iesire cu tampon
asociat cu un fisier pe disc. Limita inferioara si limita superioara din seria de numere sunt
specificate in doua argumente transmise prin linia de comanda, ca in exemplul urmator:
java IOBinarTampon 2 30
Daca nu se transmit argumente prin linia de comanda, implicit limita inferioara este 0 iar
limita superioara este 255.
Dupa scrierea in fisier, programul deschide un flux de intrare cu tampon si citeste octetii
scrisi in fisier.
import java.io.*;
public class IOBinarTampon {
public static void main (String [] args) {
int limInf = 0;

75
int limSup = 255;
try {
if (args.length > 1)
{
limInf = Integer.parseInt(args[0]);
limSup = Integer.parseInt(args[1]);
}
else
if(args.length > 0)
limInf = Integer.parseInt(args[0]);
FluxOcteti fo = new FluxOcteti(limInf, limSup);
System.out.println("\nScrie: ");
boolean succesScrie = fo.scrieFlux();
System.out.println("\nCiteste: ");
boolean succesCiteste = fo.citesteFlux();
}
catch(NumberFormatException nfe) {
System.out.println("Eroare: " + "Nu ati introdus un numar intreg");
}
}
}
class FluxOcteti {
private int inceput = 0;
private int sfarsit = 255;
public FluxOcteti(int inceput, int sfarsit) {
this.inceput = inceput;
this.sfarsit = sfarsit;
}
boolean scrieFlux() {
try {
BufferedOutputStream fluxTampon = new BufferedOutputStream(
new FileOutputStream("numere.dat"));
for (int i = inceput; i <= sfarsit; i++)
{
fluxTampon.write(i);
System.out.print(" " + i);
}
fluxTampon.close();
return true;
}
catch(IOException e) {
System.out.print("Eroare: " + e.getMessage());
return false;
}
}
boolean citesteFlux() {
try {
BufferedInputStream fluxTampon = new BufferedInputStream(
new FileInputStream("numere.dat"));
int i = 0;
do
{
i = fluxTampon.read();
if (i != -1)
System.out.print(" " + i);
} while (i != -1);
fluxTampon.close();
return true;
}
catch(IOException e) {
System.out.print("Eroare: " + e.getMessage());
return false;
}
}
}

Observatie: Aplicatia Java prezentata mai sus poate primi unul sau doua argumente atunci cand este
lansata. Modul de transmitere a unor argumente la aplicatii Java depinde de platforma pe care se
executa Java. In Windows si Unix se pot transmite argumente prin intermediul liniei de comanda.

Argumentele trebuie adaugate la executie dupa numele programului si daca sunt mai multe decat
unul, atunci argumentele sunt separate prin spatii.

In cadrul aplicatiei Java, argumentele din linia de comanda sunt preluate de metoda main() sub
forma unui tablou de siruri:
public static void main(String [] args) {
//corpul metodei
}

Argumentele din linia de comanda sunt stocate in tabloul de siruri de caractere incepand cu primul
element din tablou (care are indexul 0). Deci, in tabloul de siruri de caractere al metodei main() nu
se preia si numele programului ca la C/C++.

Observatie: Aplicatia Java prezentata mai sus poate primi unul sau doua argumente atunci cand este
lansata. Modul de transmitere a unor argumente la aplicatii Java depinde de platforma pe care se
executa Java. In Windows si Unix se pot transmite argumente prin intermediul liniei de comanda.

Argumentele trebuie adaugate la executie dupa numele programului si daca sunt mai multe decat
unul, atunci argumentele sunt separate prin spatii.

In cadrul aplicatiei Java, argumentele din linia de comanda sunt preluate de metoda main() sub
forma unui tablou de siruri:
public static void main(String [] args) {
//corpul metodei
}

Argumentele din linia de comanda sunt stocate in tabloul de siruri de caractere incepand cu primul
element din tablou (care are indexul 0). Deci, in tabloul de siruri de caractere al metodei main() nu
se preia si numele programului ca la C/C++.

B2. Fluxuri filtrate cu date de tipuri primitive

Clasele DataInputStream si DataOutputStream ofera, ca facilitate suplimentara, posibilitatea ca


fluxurile sa nu mai fie privite strict la nivel de octet, ci ca succesiuni de date primitive. Prin aceasta,

77
datele vor fi scrise in fluxul de iesire intr-un format independent de modul de reprezentare al datelor
in sistemul pe care se lucreaza.

Constructorul clasei DataInputStream creaza un nou flux de intrare ce suprapune pe cel existent
primit ca argument. Constructorul are forma:
DataInputStream(InputStream <in>) unde:
- <in> - specifica fluxul de intrare existent, de exemplu, un flux de intrare cu tampon sau un
flux de intrare din fisier.
Constructorul clasei DataOutputStream creaza un nou flux de iesire ce suprapune pe cel existent
primit ca argument. Constructorul are forma:
DataOutputStream(OutputStream <out>) unde:
- <out> - specifica fluxul de iesire existent, de exemplu, un flux de iesire cu tampon sau un flux de
iesire in fisier.

Deoarece fluxurile filtrate de intrare/iesire cu date de tipuri primitive semnaleaza exceptia


IOException la aparitia unei erori, toate operatiile legate de fluxuri se incadreaza in blocuri try …
catch, care sa intercepteze aceasta exceptie.

Urmatoarea lista prezinta metodele de citire si de scriere ce pot fi folosite pentru fluxurile de tipul
DataInputStream, si respectiv de tipul DataOutputStream:
- readBoolean(); writeBoolean(boolean <v>);
- readChar(); writeChar(char <v>);
- readDouble(); writeDouble(double <v>);
- readFloat(); writeFloat(float <v>);
- readInt(); writeInt(int <v>);
- readLong(); writeLong(long <v>);
- readShort(); writeShort(short <v>);

Fiecare dintre metodele de citire prezentate incearca sa citeasca un numar de octeti egal cu
lungimea pe care sunt reprezentate tipurile respective indicate in numele metodei. De exemplu,
metoda readBoolean() citeste un octet si returneaza true daca octetul citit este diferit de zero.

Fiecare dintre metodele de scriere prezentate incearca sa scrie un numar de octeti egal cu lungimea
pe care sunt reprezentate tipurile respective indicate in numele metodei. De exemplu, metoda
writeBoolean() scrie intr-un octet valoarea 1 sau valoarea 0, dupa cum parametrul <v> este true sau
false.

Observatii:
1. Datele citite folosind metodele clasei DataInputStream trebuie sa fi fost scrise in fisier cu
metodele complementare ale clasei DataOutputStream.
2. Nu toate metodele de citire dintr-un flux de tip DataInputStream returneaza o valoare ce poate
indica faptul ca s-a ajuns la sfarsitul fluxului. In astfel de cazuri se semnaleaza exceptia
EOFException care indica faptul ca s-a ajuns la sfarsitul fluxului de intrare.

Urmatorul program (IODataTampon.java) scrie o serie de valori de tip float intr-un flux de iesire
DataOutputStream cu tampon asociat cu un fisier pe disc. Limita inferioara si limita superioara din
seria de numere reale sunt specificate in doua argumente transmise prin linia de comanda, ca in
exemplul urmator:
java IOBinarTampon 2 30

Daca nu se transmit argumente prin linia de comanda, implicit limita inferioara este 0 iar limita
superioara este 255.
Dupa scrierea in fisier, programul deschide un flux de intrare cu tampon si citeste datele de tip float
scrise in fisier.
import java.io.*;
public class IODataTampon {
public static void main (String [] args) {
float limInf = 0;
float limSup = 255;
try {
if (args.length > 1)
{
limInf = Float.parseFloat(args[0]);
limSup = Float.parseFloat(args[1]);
}
else
if(args.length > 0)
limInf = Float.parseFloat(args[0]);
FluxDatePrimitive fd = new FluxDatePrimitive(limInf, limSup);
boolean succesScrie = fd.scrieFlux();
System.out.println("\nCiteste: ");
boolean succesCiteste = fd.citesteFlux();
}
catch(NumberFormatException nfe) {
System.out.println("Eroare: " + "Nu ati introdus un numar real");
}
}
}
class FluxDatePrimitive {
private float inceput = 0;
private float sfarsit = 255;
public FluxDatePrimitive(float inceput, float sfarsit) {
this.inceput = inceput;
this.sfarsit = sfarsit;
}
boolean scrieFlux() {
try {
DataOutputStream fluxDate = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream("numere.dat")));
for (float i = inceput; i <= sfarsit; i+=0.5)
fluxDate.writeFloat(i);
fluxDate.close();
return true;
}
catch(IOException e) {
System.out.print("Eroare: " + e.getMessage());
return false;
}
}
boolean citesteFlux() {
try {
DataInputStream fluxDate = new DataInputStream(
new BufferedInputStream(new FileInputStream("numere.dat")));
float i = 0;

79
try {
while (true)
{
i = fluxDate.readFloat();
System.out.print(" " + i);
}
}
catch(EOFException eof) {
fluxDate.close();
}
return true;
}
catch(IOException e) {
System.out.print("Eroare: " + e.getMessage());
return false;
}
}
}

Aplicatia prezentata este un exemplu de suprapunere a mai multor filtre peste un flux original de tip
FileInputStream. Fluxul filtrat este construit in trei etape:
- se creaza un flux de iesire in fisier, asociat cu fisierul “numere.dat”;
- se asocieaza fluxului de fisier un flux de iesire filtrat cu tampon;
- se asociaza fluxului de iesire cu tampon un flux de tip DataInputStream.

Instructiunea while(true) din metoda citesteFlux() creaza un ciclu infinit in aparenta, pentru ca la un
moment dat va fi atins sfarsitul fluxului si va fi semnalata o exceptie EOFException. Metoda
readFloat() citeste valorile intregi din flux.

B3. Fluxuri filtrate cu date formatate

Clasa PrintStream furnizeaza un flux filtru de iesire care permite afisarea de numere, valori
boolean, String si alte tipuri de obiecte in format text. Filtrul de iesire PrintStream converteste
numerele si alte tipuri de date la reprezentari text, inainte de transmiterea datelor la fluxul de iesire.

De exemplu, atunci cand programul afiseaza valoarea intreaga 42, clasa PrintStream converteste
numarul la caracterele “4” si “2” si apoi transmite caracterele mai departe in flux.
In plus, fluxul PrintStream da posibilitatea (optional) de a goli automat zona tampon dupa ce a fost
scris un tablou de octeti sau un octet egal cu ‘\n’. Metodele acestei clase nu semnaleaza niciodata
exceptia IOException.

Fluxul System.out, pe care l-am folosit aproape in toate lectiile de pana acum pentru afisarea
mesajelor la ecranul calculatorului, este de fapt un flux de tip PrintStream.
Prezentam doi dintre constructorii acestei clase:
PrintStream(OutputStream <out>)
PrintStream(OutputStream <out>, boolean <auto_golire>)
unde:
- <out> - specifica fluxul de iesire existent;
<auto_golire> - specifica daca se goleste automat zona tampon dupa ce a fost scris un tablou de
octeti sau un octet egal cu ‘\n’ (daca valoarea este true).
Mentionam cateva metode ale acestei clase:
- print (String <s>)
- print (char <c>)
- print (boolean <b>)
- print (char <c>)
- print (int <i>)
- print (long <l>)
- print (float <f>)
- print (double <d>)
Pentru fiecare metoda print exista si perechea ei println, care adauga caracterul ‘\n’ in fluxul de
iesire.

In plus, exista si metoda println fara parametrii, care scrie doar caracterul de sfarsit de linie ‘\n’.
Metodele print si println scriu argumentul lor intr-un format de tip text si anume identic cu cel
produs de apelurile metodei String.valueOf(x).

Un flux PrintStream nu are ca destinatie numai ecranul calculatorului ci orice destinatie de flux,
inclusiv un fisier text ASCII. De exemplu, urmatoul program (PrintStreamFile.java) afiseaza un
String, un intreg si un numar in virgula mobila intr-un fisier, “print.txt”.
import java.io.*;
public class PrintStreamFile {
public static void main(String [] args) {
double nr1 = 234.5;
int nr2 = 120;
try {
PrintStream ps = new PrintStream(new FileOutputStream("print.txt"));
ps.println ("Date pentru testarea fluxului PrintStream");
ps.print(nr1);
ps.print(' ');
ps.print(nr2);
ps.println();
ps.println(Math.PI);
ps.close();
}
catch(IOException e) {
System.out.println("Eroare la scrierea fisierului: " + e.getMessage());
}
}
}

6.3 Fluxuri de caractere si clase predefinite Java utilizate

Fluxurile de caractere se folosesc pentru lucrul cu orice text reprezentat in format ASCII sau
Unicode (set de caractere international care include si ASCII). Ele opereaza asupra sirurilor de
caractere si tablourilor de caractere.

Exemple de fisiere cu care putem lucra prin intermediul fluxurilor de caractere sunt fisierele de text
simplu, documentele HTML sau fisiere care contin cod sursa Java.

A. Fluxuri de caractere de intrare nefiltrate si filtrate

81
In schema de mai jos se prezinta doar o parte a structurii de clase ce poate fi folosita in operatiile cu
fluxuri de caractere de intrare nefiltrate si filtrate.

Clasa Reader este o clasa abstracta si din ea deriveaza toate celelalte clase care creaza fluxuri de
caractere de intrare si realizeaza operatii de intrare pe aceste fluxuri.
Nota: Deoarece fluxurile de intrare semnaleaza exceptia IOException la aparitia unei erori, toate
operatiile legate de fluxuri se incadreaza in blocuri try … catch, care sa intercepteze aceasta
exceptie.
Este indicat sa folosim clasa Reader pentru lucru cu text si nu fluxuri de octeti.

A.1 Fluxuri nefiltrate de caractere de intrare care folosesc ca sursa un fisier

Sunt folosite pentru transferul de date de la fisierele aflate pe hard-discuri, pe CD-ROM sau pe alte
dispozitive de stocare (ce pot fi referite printr-o cale de director si un nume) catre aplicatia Java.
Principala clasa folosita pentru citirea de fluxuri de caractere dintr-un fisier este FileReader.
Aceasta clasa mosteneste clasa InputStreamReader, care citeste un flux de octeti si ii converteste in
valori intregi corespunzatoare caracterelor Unicode.

Crearea (deschiderea) unui flux de intrare de la un fisier se realizeaza cu ajutorul constructorului


clasei FileReader, care are forma:
FileReader(String <sir>)
unde:
- <sir> - reprezinta numele, si eventual calea, fisierului; la specificarea caii fisierului trebuie sa se
foloseasca doua caractere backslash pentru a nu se confunda cu o secventa escape.
Daca fisierul nu poate fi deschis, atunci este lansata exceptia FileNotFoundException.

De exemplu, urmatoarea instructiune deschide un flux de intrare de la fisierul “test.doc”:


FileReader document = new FileReader(“test.doc”);

Dupa deschiderea fluxului de caractere de intrare din fisier se pot folosi metodele acestui flux
pentru realizarea diverselor operatii de intrare. Descriem cateva dintre ele.

Metoda read() citeste un caracter din fluxul de intrare; rezultatul intors este un intreg din intervalul
0 - 65535. Daca s-a detectat sfarsitul de fisier, rezultatul intors este -1.
Metoda read cu trei parametrii are forma:
read (char [] <c>, int <poz_ini>, int <lungime>)
unde:
- <c> - un tablou de caractere in care se vor memora datele citite;
- <poz_ini> - pozitia elementului din cadrul tabloului unde se va stoca primul caracter de date;
- <lungime> - numarul de caractere care se vor citi din fluxul de intrare repetand metoda read fara
parametri.
Metoda returneaza un intreg care reprezinta numarul de caractere citite sau -1 daca, de la inceput,
fluxul de intrare este la sfarsitul sau.
Nota: Deoarece metoda read() a unui flux de caractere returneaza o valoare intreaga, trebuie sa se
converteasca explicit prin cast aceasta valoare inainte de a o afisa sau de a o salva intr-un tablou de
caractere. Fiecare caracter poseda un cod numeric care reprezinta pozitia sa in setul de caractere
Unicode. Valoarea intreaga citita din flux reprezinta chiar acest cod numeric.

Metoda skip avanseaza cu n pozitii (caractere) in fluxul de intrare, nedepasind bineinteles sfarsitul
fluxului. Rezultatul intors de metoda este numarul de pozitii peste care s-a trecut efectiv.
Metoda skip are forma:
skip(long <n>)
unde :
<n> - nr de caractere sarite in fluxul de intrare.

Metoda ready() specifica daca acest flux de caractere este disponibil pentru a fi citit. Un flux de
caractere dintr-un fisier este disponibil pentru a fi citit daca zona tampon de intrare nu este goala (in
caz ca ea exista) sau daca exista octeti disponibili de a fi cititi de la fluxul de octeti de tip
InputStreamReader.

Metoda close() inchide fluxul de intrare respectiv si elibereaza resursele sistem asociate cu acesta.

A.2 Fluxuri filtrate de caractere de intrare cu o zona tampon (“buffer”)

Fluxurile de caractere cu o zona tampon (“buffer”) permit utilizarea unei zone tampon pentru
cresterea eficientei operatiilor de citire.
Fluxurile de caractere de intrare cu tampon folosesc clasa BufferedReader.

Crearea (deschiderea) unui flux de caractere cu tampon se realizeaza cu ajutorul constructorilor


clasei BufferedReader.
Constructorii clasei BufferedReader au una din formele:
a) BufferedReader(Reader <in>)
unde:
- <in> - reprezinta fluxul original de tip Reader.
Nota: Zona tampon are o lungime care este aleasa implicit (automat).
b) BufferedReader(Reader <in>, int <lg_buffer>)
unde:
- <in> - reprezinta fluxul original de tip Reader;
- <lg_buffer> - reprezinta lungimea zonei tampon a fluxului de intare.

Dintr-un flux de caractere cu tampon se poate citi folosind metodele read() si


read (char [] <c>, int <poz_ini>, int <lungime>) , asemanatoare celor descrise pentru clasa
FileReader.

Metodele skip(), ready() si close() sunt asemanatoare celor descrise pentru clasa FileReader.
Programul urmator (CitesteSiruri.java) citeste doua siruri de caractere de la tastatura si le stocheaza
intr-un tablou de caractere de lungime 20. Daca dupa citirea primului sir, in zona tampon mai exista
caractere necitite (ceea ce inseamna ca s-a introdus un sir de lungime mai mare de 20) atunci
acestea vor fi sarite din fluxul de intrare pentru a permite eliberarea zonei tampon si citirea de la
tastatura a celui de al doilea sir.
import java.io.*;
class CitesteSiruri {
public static void main(String[] args)

83
{
char[] caractere1 = new char[20];
char[] caractere2 = new char[20];
for (int i=0; i <=19; i++)
caractere1[i] = ' ';
for ( int i=0; i <=19; i++)
caractere2[i] = ' ';
try {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println ("Introduceti primul sir");
br.read(caractere1, 0, 20);
String sir1 = new String(caractere1);
System.out.println(sir1);
while (br.ready() )
br.skip (1);
System.out.println ("Introduceti al doilea sir");
br.read(caractere2, 0, 20);
String sir2 = new String(caractere2);
System.out.println(sir2);
boolean rezultat = false;
rezultat = sir1.equals(sir2);
if (rezultat ==true)
System.out.println ("siruri egale");
else
System.out.println ("siruri diferite");
}
catch(IOException e) {
System.out.println ("Eroare la sirul citit" + e.getMessage());
}
}
}

Metoda readLine() citeste din fluxul de intrare o linie de text. Metoda returneaza un obiect de tip
String care contine linia de text citita din flux , fara a include si caracterul (sau caracterele) care
reprezinta sfarsitul de linie. Daca se ajunge la sfarsitul fluxului, valoarea sirului returnat va fi null.
Sfarsitul de linie este indicat astfel:
- un caracter de linie noua (newline - ‘\n’);
- un caracter de retur de car (carriage return - ‘\r’);
- un retur de car urmat de o linie noua.

Ca exemplu, putem ilustra deschiderile de flux de intare cu tampon si citirile de linii de text de la
tastatura, facute in aproape toate programele Java prezentate pana acum.
Dintr-un fisier se poate citi o intreaga linie de text, si nu caracter cu carecter, daca se foloseste un
flux de caractere cu tampon de tip BufferedReader suprapus peste un flux de tip FileReader.
Programul urmator (CitesteLiniiFisier.java) ilustreaza modul de citire linie cu linie dintr-un fisier
care contine instructiuni Java (numit “CitesteLiniiFisier.java”), folosind un flux de caractere cu
zona tampon.

import java.io.*;
public class CitesteLiniiFisier {
public static void main (String [] args) {
try {
BufferedReader fisier = new BufferedReader(
new FileReader("CitesteLiniiFisier.java"));
boolean eof = false;
while(!eof)
{
String linie = fisier.readLine();
if (linie == null)
eof = true;
else
System.out.println(linie);
}
fisier.close();
}
catch(IOException e) {
System.out.println("Eroare: " + e.getMessage());
}
}
}

A.3 Fluxuri filtrate de caractere de intrare cu o zona tampon care tin evidenta numerelor de
linie

Fluxurile de caractere de intrare cu tampon care tin evidenta numerelor de linie dintr-un fisier
folosesc clasa LineNumberReader. Acest filtru al fluxului de intrare urmareste numarul de linie pe
masura citirii din fluxul de intrare.
Crearea (deschiderea) unui flux de caractere cu tampon care tin evidenta numerelor de linie se
realizeaza cu ajutorul constructorilor clasei LineNumberReader.
Constructorii clasei LineNumberReader au una din formele:
a) LineNumberReader(Reader <in>)
unde:
- <in> - reprezinta fluxul original de tip Reader.
Nota: Zona tampon are o lungime care este aleasa implicit (automat).
b) LineNumberReader(Reader <in>, int <lg_buffer>)
unde:
- <in> - reprezinta fluxul original de tip Reader;
- <lg_buffer> - reprezinta lungimea zonei tampon a fluxului de intare.
Metodele pentru citirea, inchiderea, testarea unui flux de intrare cu tampon sunt mostenite de la
clasa BufferedReader si ele au fost prezentate deja.

In plus, aceasta clasa ofera metoda getLineNumber() care este folosita pentru a obtine numarul
liniei curente din fluxul de intrare atunci cand acesta este citit cu metoda readLine().
Metoda returneaza un intreg care reprezinta numarul liniei curente din flux.

Dintr-un fisier se poate citi o intreaga linie de text si se poate tine evidenta numerelor citite daca se
foloseste un flux de caractere cu tampon de tip LineNumberReader suprapus peste un flux de tip
FileReader.

Programul urmator (CitesteLiniiFisier.java) afiseaza numerele liniilor citite dintr-un fisier care
contine instructiuni Java (numit “CitesteLiniiFisier1.java”), folosind un flux de caractere cu zona
tampon tip LineNumberReader.

import java.io.*;

85
public class CitesteLiniiFisier1 {
public static void main (String [] args) {
try {
LineNumberReader fisier = new LineNumberReader(new

FileReader("CitesteLiniiFisier1.java"));
boolean eof = false;
while(!eof)
{
String linie = fisier.readLine();
if (linie == null)
eof = true;
else
System.out.println(fisier.getLineNumber() + " " + linie);
}
fisier.close();
}
catch(IOException e) {
System.out.println("Eroare: " + e.getMessage());
}
}
}
Aplicatii propuse :

1.Implementati tipul rational cu atributele numarator si numitor si trei constructori : implicit,


cu un parametru si cu doi parametrii. Scrieti o metoda pentru adunarea a doua numere rationale.
Fractia retinuta de un obiect trebuie sa fie ireductibila.
-construiti doua obiecte rationale si afisati suma lor in fisierul “date.in”.
- construiti o interfata care modeleaza notiunea de stiva de obiecte. Obiectele de tip stiva contin
metodele (adaugare, extragere, afisare, testare daca stiva este vida).
- Construiti o stiva cu numere rationale.

2. Implementati o clasa punct_plan care defineste un punct in plan cu atributele abcisa, ordonata si
trei constructori : implicit, cu un parametru, doi parametrii. Scrieti metode care prelucreaza
puncte_plan cu date trimise de la tastatura.(setarea valorilor, calculul distantei intre doua
puncte,conversie String).
- construiti doua obiecte punct_plan si afisati distanta dintre ele in fisierul “date.in”.
- construiti o interfata care modeleaza notiunea de stiva de obiecte. Obiectele de tip stiva contin
metodele (adaugare, extragere, afisare, testare daca stiva este vida).
- construiti o stiva care contine obiecte de tip punct_plan.

3. Implementati o clasa pentru numere complexe cu atributele real si imaginar si trei constructori :
implicit, cu un parametru si cu doi parametrii. Scrieti metode pentru prelucrarea obiectelor de tip
complex (adunarea a doua numere complexe, calculul modului unui numar complex, setarea datelor
unui obiect de tip complex cu date trimise de la tastatura).
- construiti doua obiecte de tip complex si afisati in fisierul ”date.in” modulele si suma lor.
- construiti o interfata care modeleaza notiunea de stiva de obiecte. Obiectele de tip stiva contin
metodele (adaugare, extragere, afisare, testare daca stiva este vida).
- construiti o stiva cu numere complexe.

4. Construiti tipul automobil definit prin datele membre : marca, an_fabricatie, capcitate. Introduceti
in clasa cel putin doi constructori pentru initializarea obiectelor de tip automobil.
- construiti 2 obiecte din clasa definita mai sus;
- realizati o clasa extinsa automobil_leasing cu data membra rata si definiti doua obiecte din
aceasta clasa;

5.Implementati o clasa angajat cu atributele nume, functie, salariul si cel putin doi constructori
pentru intitializarea obiectelor. Scrieti metode pentru setarea datelor de la tastatura, afisarea datelor.
Construiti doua obiecte de tip angajat si afisati datele lor in fisierul “date.in”.
Extineti clasa definita mai sus la clasa angajat_determinat cu data membra perioada si construiti
doua obiecte de acest tip.

87
Bibliografie

1. Horia Georescu, “Introducere in universul Java”, Editura Tehnica,2002.

2. Kevin Taylor, Java Programming Tutorial, http://java.about.com/od/beginningjava/a/beginjavatutor.htm

3. Bruce Eckel, Thinking in Java, 3rd ed. Revision 4.0, http://arh.pub.ro/mirrors/eckelbooks/TIJ3/TIJ3.htm

4. Sun Microsystems, The Java™ Tutorials, http://java.sun.com/docs/books/tutorial/java/TOC.html

5. Stefan Tanasa, Cristian Olaru, Stefan Andrei, “JAVA de la 0 la expert”, Editura Polirom, 2003.

6. Bruce Eckel, “Thinking in Java, 3rt Edision”, Editura Prentice – Hall, 2002.

7. Doina Logofatu, “Algoritmi fundamentali in Java. Aplicatii “, Editura Polirom 2007.