Programarea in Java - 2014 - An II ID PDF
Programarea in Java - 2014 - An II ID PDF
FACULTATEA DE INFORMATIC
PROGRAMAREA N JAVA
-suport curs -
- 2014 -
Introducere
Acest curs se adreseaz studenilor anului II, specializarea Informatic, forma de invatamant la
distan. Modul de prezentare ine cont de particularitile nvmntului la distan, la care studiul
individual este determinant. n timp ce profesorul sprijin studentul prin coordonarea nvrii i
prin feedback periodic asupra acumulrii cunotinelor i a deprinderilor, studentul alege locul,
momentul i ritmul pentru studiu, dispune de capacitatea de a studia independent i totodat i
asum responsabilitatea pentru iniierea i continuarea procesului educaional.
Disciplina Programarea n Java utilizeaz noiunile predate la disciplinele Bazele Informaticii,
Programare procedural, Programare orientata pe obiecte, discipline studiate n anul I i n anul
II.
Competenele dobndite de ctre studeni prin nsuirea coninutului cursului sunt folosite la
disciplinele de specialitate precum Tehnici avansate de programare, Proiectarea interfeelor
grafice etc. O nenelegere a noiunilor fundamentale prezentate n acest curs poate genera
dificulti n asimilarea conceptelor mai complexe ce vor fi introduse n aceste cursuri de
specialitate.
Cunoaterea pachetelor de clase implicite Java i utilizarea lor pentru dezvoltarea unor
aplicaii Java;
Cuprins
Unitatea de nvare Nr.1.5
STRUCTUR APROGRAMELO JAVA5
1.1 Caracteristici de baz ale limbajului Java.5
1.2 Tehnologia Java si componentele sale..6
1.2.1 Platforme de lucru Java..6
1.2.2 Tipuri de programe implementate de Java ..6
1.2.3 Etapele dezvoltarii unei aplicaii Java.7
1.2 Structura lexicala a limbajului Java..7
1.3 Tipuri de date si variabile n Java..9
1.4 Expresii si operatori ..11
1.5 Controlul executiei..14
1.6 Afisarea datelor pe ecran17
1.7 Siruri de caractere18
Tema de autoinstruire nr. 1..20
Teste de control.20
Unittea de nvare nr. 223
CLASE I OBIECTE23
2.1 Ciclul de viata al unui obiect.23
2.1.1 Crearea obiectelor..24
2.2 Crearea claselor.25
2.2.1 Corpul unei clase..26
2.2.2 Constructorii unei clase27
2.2.3 Declararea variabilelor.31
2.3 Implementarea metodelor33
2.3.1 Declararea metodelor..33
2.3.2 Metode de instanta si metode de clasa.35
2.3.3 Domeniul de vizibilitate (acces) al metodelor unei clase. Modificatori de acces..35
Tema de autoinstruire nr. 2..38
Teste de control.39
Unittea de nvare nr. 3..42
EXTINDEREA CLASELOR. POLIMORFISMUL IN JAVA ..42
3.1 Mostenirea - concept fundamental al programarii orientata obiect42
3.2 Caracteristicile unei ierarhi de clase...44
3.3 Rescrierea metodelor i ascunderea cmpurilor45
3.4 Metode-constructor pentru clase derivate si cuvantul-cheie super48
3.5 Implementarea polimorfismului..49
3.6 Metode si clase abstracte..52
Testul de autoevaluare nr. 3.58
Tema de autoinstruire nr. 3..59
Teste de control.59
Unittea de nvare nr. 461
INTERFEE JAVA..61
4. 1 Metode i clase abstracte 61
4.2 Crearea si folosirea interfetelor Java.62
4
Obiective:
Dup ce vei parcurge aceast unitate de nvare, vei reui s:
cunoatei noiunile de baz ale limbajului Java;
cunoatei structura unei aplicaii scrise n Java;
utilizai clasele de obiecte din pachetele predefinite (pachete
API) Java; directiva import ; instruciunea package
cunoatei tipuri de date; instructiuni de control.
Tehnologiile Java sunt grupate n aa numitele platforme de lucru. Acestea reprezint seturi de
librarii scrise n limbajul Java, precum i diverse programe utilitare, utilizate pentru dezvoltarea de
aplicaii sau componente destinate unei anume categorii de utilizatori.
J2SE (Standard Edition)
Este platforma standard de lucru ce ofer suport pentru crearea de aplicaii independente i
appleturi. De asemenea, aici este inclus i tehnologia JavaWeb Start ce furnizeaz o modalitate
extrem de facil pentru lansarea i instalarea local a programelor scrise n Java direct de peWeb,
oferind cea mai comod soluie pentru distribuia i actualizarea aplicaiilor Java.
J2ME (Micro Edition)
Folosind Java, programarea dispozitivelor mobile este extrem de simpl, platforma de lucru J2ME
oferind suportul necesar scrierii de program dedicate acestui scop.
J2EE (Enterprise Edition)
Aceasta platforma ofera API-ul necesar dezvoltarii de aplicaii 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 aplicaii 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.
1.2.2 Tipuri de programe implementate de Java
O aplicaie care se execut pe partea de server este o aplicaie care este rulat de server ca urmare a
unei cererei primate de acesta, iar rezultatul este trimis programului solicitant.
Un servlet este o componenta Web, scrisa n 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 aplicaii Java
1.Editarea setului de instructiuni de programare cu ajutorul unui editor de texte. n acest fel este
creat un fisier sursa, care are extensia .java.
2. Compilarea programului. Pentru aceasta operatie se lanseaza n 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 n codul masina pentru masina virtuala Java
Rezultatul compilarii este unul sau mai multe fisiere de tip bytecode. Un fisier de tip bytecode
este o secventa de instructiuni de asamblare pentru masina virtuala Java cu extensia .class.
3. Conversia (transformarea), de ctre interpretorul Java, a instructiunilor bytecode n
instructiuni inteligibile masinii gazda care apoi sunt executate.
O aplicaie Java este compus din una sau mai multe clase care interactioneaza intre ele prin
intermediul metodelor. n grupul de clase, care formeaza o aplicaie Java, trebuie sa existe o clasa
care sa contina o metoda statica avand numele main.
Atunci cand se execut o aplicaie 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:
Exemplu 1
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
Limbajele de programare "clasice" utilizeaz setul de caractere ASCII. Limbajul Java folosete
setul de caractere Unicode, care este reprezentabil pe 16 bii, deci are 65 536 caractere, cuprinznd
caractere i simboluri proprii aproape tuturor limbilor existente.
Primele 256 caractere din Unicode sunt cele din setul de caractere Latin-1, dintre care primele 128
sunt cele din codul ASCII.
Un caracter poate fi reprezentat oriunde n textul surs i printr-o aa numit "secven Escape",
avnd forma \uhhhh sau \Uhhhh, unde am notat prin h o cifr hexazecimal (cifrele hexazecimale
corespunztoare lui 10-15 sunt identificate prin a-f sau A-F). O secven Escape poate s apar deci
nu numai n caractere i iruri de caractere, dar i n identificatori. Numrul hexazecimal dintr-o
astfel de secven corespunde numrului de ordine al caracterului n setul Unicode.
Ne mrginim la a indica secvenele Escape pentru semnele diacritice din limba romn:
= \u00c3
= \u00c2
= \u00aa
= \u00de
= \u00ce
= \u00e3
= \u00e2
= \u00ca
= \u00fe
= \u00ee
n continuare vom nelege prin spaii albe caracterele blanc, tab, form-feed, precum i terminatorii
de linie: return, line-feed (linie nou) i return + line-feed.
Anumite caractere pot fi reprezentate printr-o secven Escape de o form aparte:
\n = \u000a (linie nou)
\t = \u0009 (tab)
\b = \u0008 (backspace)
\r = \u000d (return)
\\ = \u005c (backslash)
\' = \u0027 (apostrof)
\f = \u000c (form-feed)
\" = \u0022 (ghilimele)
Motivaia utilizrii setului Unicode este integrarea n limbaje de tip HTML care s permit
circulaia pe Internet.
Identificatori
Identificatorii, ntalniti si sub denumirea de nume simbolice, au rolul de a denumi elemente ale
programului Java: constante, variabile, clase metode etc.
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 ntre literele mici si literele mari.
Cuvintele-cheie (keywords) sunt identificatori speciali, cu nteles predefinit, care pot fi utilizati
numai n constructii sintactice n care sunt specificati. De exemplu: if, while etc. Toate cuvintelecheie 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 folosii, de exemplu, pentru a separa unele constructii sintactice: variabilele
sunt separate prin caracterul virgula (,). Alti separatori specifici sunt ( ) { } [ ] .
Delimitatorii sunt folosii pentru:
- a delimita sfarsitul unei instructiuni sau al unei declaratii - caracterul punct si virgula (;);
- a delimita o constanta de tip caracter - caracterul apostrof ();
9
menionm metoda isNaN din clasele Float i Double: metoda primete o valoare de tipul
corespunztor i ntoarce o valoare boolean, care este true dac i numai dac argumentul nu este o
valoare numeric valid.
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 ntorc nici un rezultat, cum a fost cazul metodei main ().
Constante
O constanata este folosita pentru a exprima n program o valoare pe care o poate lua tipurile
primitive de date si tipul sir de caractere.
Constantele intregi pot fi reprezentate n baza 10, 16 sau 8. Constantele ntregi pot fi ntregi
normale sau lungi. Constantele lungi se recunosc prin faptul ca se termina cu sufixul l sau L.
Pentru a reprezenta o constanta ntreaga n baza 16 trebuie sa se adauge prefixul 0x sau 0X n fata
numarului. Pentru a reprezenta o constanta ntreaga n baza 8 trebuie sa se adauge prefixul 0 (cifra
zero) n fata numarului.
Constantele reale care se pot reprezenta n memoria calculatorului sunt numere rationale din
intervalele specificate la tipurile float si double. Constantele reale pot fi specificate n notatia
obisnuita sau n 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 ntre apostroafe. Pentru cele care nu au
reprezentare grafica, se folosesc secvenele escape sau secvenele 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 n caracterul
corespunzator.
Constantele de tip sir de caractere sunt cuprinse ntre ghilimele. Caracterele care formeaza sirul de
caractere pot fi caractere grafice sau secvene escape ca cele prezentate la constantele de tip
caracter. Daca se doreste introducerea de caractere terminatoare de linie ntr-un sir de caractere,
atunci se foloseste secventa escape \n n sirul de caractere respectiv.
Observatie: Un sir de caractere este, de fapt, o instanta a clasei de obiecte String declarata standard
n pachetul java.lang. Vom reveni asupra sirurilor de caractere ntr-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 n momentul declararii; sunt optionale si sunt folosite pentru a atribui unor variabile
anumite valori initiale.
11
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 ntr-o lectie separata destinata descrieri atributelor statice.
Exemple de declaratii de variabile ce pot fi folosite ntr-un program:
int a, b=3, c=4;
char g;
float x=b*5.6, y;
1.4 Expresii si operatori
O expresie este compus dintr-o succesiune de operanzi, legati prin operatori.
Un operand poate fi o constanta, o variabila, un apel de metoda, o expresie ncadrata ntre paranteze
rotunde.
Operatorii desemneaza operatiile care se execut asupra operanzilor si pot fi grupati pe categorii, n
functie de tipul operatiilor realizate.
Operatorii limbajului Java sunt unari (se aplica unui singur operand) sau sunt binari (se aplica
asupra a doi operanzi).
Menionm c Java pune la dispoziia utilizatorului aproximativ 40 de operatori, dintre care unii
sunt descrii n continuare. Trebuie precizat c programatorul poate defini metode noi, dar nu i
operatori noi.
Operatori aritmetici
Aceti operatori sunt urmtorii:
+ - * / %.
De asemenea este folosit operatorul unar - pentru schimbarea semnului, precum i operatorul unar +
(introdus pentru simetrie).
n aritmetica ntreag trebuie s inem cont de urmtoarele reguli:
- orice valoare ce depete limita admis este redus modulo aceast limit; de aceea nu exist
depiri (overflow i underflow);
- mprirea ntreag se face prin trunchiere; de exemplu -8/3==-2, iar 8/3==2 ;
- operatorul % este definit prin:
(x/y)*y+x%y==x
De exemplu 8%3==2, iar -8%3==-2.
Operatorii de incrementare i decrementare
Este vorba de operatorii unari ++ i --, care pot fi aplicai operanzilor numerici (ntregi sau n
virgul mobil) att n form prefixat ct i n form postfixat.
Expresiile x++ i ++x sunt echivalente cu x=x+1, cu dou excepii:
- sunt atomice (considerate ca o unic operaie, nu ca o incrementare urmat de o atribuire). Acest
aspect este relevant doar cnd mai multe fire de executare acioneaz asupra unei aceleiai
variabile;
12
- operandul x este evaluat o singur dat. Aceast excepie are n vedere instruciuni de tipul
a[indice()]=a[indice()]+1, n care este folosit o metod ce ntoarce un ntreg; astfel, n exemplul de
mai sus, invocarea metodei indice va avea loc o singur dat (n caz contrar este posibil ca n
membrul stng i cel drept s nu se refere la aceeai component a tabloului a, de exemplu dac
metoda ntoarce n mod aleator un indice).
Diferena ntre x++ i ++x const n faptul c incrementarea este realizat dup, respectiv nainte de
utilizarea lui x n contextul n care apare. Astfel, dac valoarea curent a lui x este 4, atunci:
- evaluarea expresiei 2 * ++x conduce la rezultatul 10,
- evaluarea expresiei 2 * x++ conduce la rezultatul 8, dup care valoarea lui x va fi n ambele cazuri
5.
Evident, operatorul -- se supune unor reguli analoage.O construcie de tipurile x++,x--,++x,--x
reprezint o expresie aritmetic, dar i o instruciune (valoarea expresiei nu este folosit, ns x este
incrementat sau decrementat).
Operatorii de mai sus pot fi aplicai i pentru tipul char, semnificnd trecerea la precedentul,
respectiv urmtorul caracter din setul de caractere Unicode.
Operatorii de atribuire
Pe lng operatorul = folosit standard pentru atribuire, mai pot fi folosii i operatorii:
+= -= /= *= %=
care reprezint scrieri prescurtate. Astfel x+=y este echivalent cu x=x+y, cu excepia faptului c
evaluarea adresei lui x se face o singur dat (vezi paragraful precedent). Desigur, operatorul %= va
fi folosit doar pentru aritmetica ntreag.
Operatorii relaionali
Este vorba de urmtorii operatori:
> (mai mare ca)
< (mai mic ca)
Operatori booleeni
Este vorba de urmtorii operatori:
(disjuncia logic, sau)
&& (conjuncia logic, i)
! (negaie)
cu meniunea c la evaluare se face scurtcircuitare (pe scurt, dac la evaluarea unei expresii
booleene devine la un moment dat clar valoarea ei, nu se mai continu n mod inutil evaluarea; de
exemplu dac expresia este disjuncia logic a trei termeni i valoarea primului este true, nu se mai
evalueaz ceilali termeni).
Operatori pe bii
Este vorba de operatorii binari i & care realizeaz disjuncia logic i conjuncia logic la nivel de
bit, pentru fiecare pereche de bii de pe aceeai poziie din reprezentarea operanzilor.
Operatorul condiional ?:
Acest operator se utilizeaz n expresii sub forma:
( cond ? e1 : e2 )
13
a crei valoare este e1 dac cond este true, respectiv e2 dac cond este false; cond trebuie s fie o
expresie de tip boolean.
Pentru a nu intra n amnunte, vom impune deocamdat regula ca e1 i e2 s aib acelai tip.
Operatori postfix
Includem aici:
- cuprinderea ntre paranteze a indicilor (cu []);
- operatorul de calificare (.) folosit pentru accesarea membrilor claselor;
- parantezele rotunde folosite pentru specificarea listei de parametri din invocri;
- operatorii postfix de incrementare/decrementare ++ i -- de mai sus.
Operatorul de conversie a tipurilor
Ne mrginim la a preciza c o conversie explicit de tip are forma:
(tip) expresie
urmnd s discutm ulterior condiiile n care poate avea loc o astfel de conversie.
Operatorul + pentru lucrul cu iruri
Acest operator este folosit pentru concatenarea irurilor. Trebuie menionat c dac un membru al
unei sume este un ir de caractere, atunci are loc o conversie implicit a celorlali membri ai sumei
(devenit acum concatenare) la iruri de caractere; aceast facilitate a fost de altfel des folosit n
programele prezentate anterior, de exemplu n cadrul invocrii System.out.print(...). Adugm doar
c printre membrii sumei pot aprea i variabile referin!
Operatorii pentru referine
Sunt utilizai urmtorii operatori:
- accesul la cmpuri (prin calificare);
- invocarea metodelor (prin calificare);
- operatorul de conversie;
- operatorii == i != ;
- operatorul condiional;
- operatorul instanceof , folosit n contextul:
Precedena operatorilor
Ordinea n care are loc efectuarea prelucrrilor determinate de operatori este dat n urmtorul tabel
de prioriti ale operatorilor (de la prioritate maxim la prioritate minim):
- operatorii postfix
- operatorii unari de incrementare/decrementare, operatorii + i - unari, operatorul de negaie !
- operatorul new de creare de obiecte i cel de conversie: (tip) expresie
- operatorii multiplicativi: * / %
- operatorii aditivi: + - operatorii relaionali i instanceof
- operatorii de egalitate: == !=
- operatorul &
- operatorul
- conjuncia logic &&
- disjuncia logic
- operatorul condiional ( ? : )
14
- operatorii de atribuire.
Observaii:
- la prioritate egal, operatorii "vecini" acioneaz conform regulilor de asociativitate prezentate n
continuare;
- utilizarea parantezelor este mai puternic dect prioritatea operatorilor. Astfel, spre deosebire de
x+y*z, n (x+y)*z prima operaie care va fi executat este adunarea.
Asociativitate
Regula general o constituie asociativitatea la stnga. Fac excepie urmtorii operatori, pentru care
este folosit asociativitatea la dreapta:
- operatorii unari;
- operatorii de atribuire.
- operatorul ( ? : ).
Exemple :
1) n expresia x-y+z nti se va efectua scderea i apoi adunarea;
2) Instruciunea:
x = y = z = 0;
este echivalent cu:
x = ( y = (z = 0));
i are ca efect atribuirea valorii 0 variabilelor x,y,z. Explicaia const n aceea c efectuarea unei
atribuiri conduce i la obinerea unui rezultat (valoarea care a fost atribuit) i care poate fi folosit
ca n exemplul de mai sus.
3) Secvena de program:
int a=5, b=10, c=15;
a += b -=c %= 4;
System.out.println(a + " " + b + " " + c);
produce la ieire:
12 7 3
1.5 Controlul executiei
Instructiunile Java pentru controlul executiei sunt foarte asemanatoare celor din limbajul C si pot fi
impartite n urmatoarele categorii:
Instructiuni de decizie: if-else, switch-case
Instructiuni de salt: for, while, do-while
Instructiuni pentru tratarea exceptiilor: try-catch-finally, throw
Alte instructiuni: break, continue, return, label;
A. Structuri alternative (de decizie)
Instruciunea 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.
15
Nota: Instruciunea if poate sa faca parte dintr-o alta instruciune if sau else, adica instructiunile if
pot fi incluse (imbricate) n alte instructiuni if.
Exemplu 2
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.
16
Instruciunea switch
Sintaxa instructiunii este:
switch (<expresie>)
{ case <constanta_1> : <grup_de_instructiuni_1>;
case <constanta_2> : <grup_de_instructiuni_2>;
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.7 iruri de caractere
n Java, un ir 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 ir 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 irului, cum ar fi: append, insert, delete, reverse.
Uzual, cea mai folosita modalitate de a lucra cu iruri este prin intermediul clasei String, care are si
unele particularitati fata de restul claselor menite sa simplifice cat mai mult folosirea irurilor de
caractere. Clasa StringBuffer va fi utilizata predominant in aplicaii dedicate procesarii textelor cum
ar fi editoarele de texte.
Exemple echivalente de declarare a unui ir:
String s = "abc";
String s = new String("abc");
char data[] = {a, b, c};
String s = new String(data);
Concatenarea irurilor de caractere se face prin intermediul operatorului + sau, in cazul irurilor de
tip StringBuffer, folosind metoda append.
String s1 = "abc" + "xyz";
String s2 = "123";
String s3 = s1 + s2;
In Java, operatorul de concatenare + este extrem de flexibil, in sensul ca permite concatenarea
irurilor cu obiecte de orice tip care au o reprezentare de tip ir de caractere. Mai jos, sunt cateva
exemple:
System.out.print("Vectorul v are" + v.length + " elemente");
String x = "a" + 1 + "b"
19
20
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;
21
22
Formular de feedback
n dorina de ridicare continu a standardelor desfurrii activitatilor dumneavoastra, va rugm s completai acest chestionar i s-l
transmitei indrumatorului de an.
Disciplina: ________________________
Unitatea de invatare/modulul:__________________
Anul/grupa:__________________________
Tutore:_________________________
a) Coninut / Metoda de predare
Partea I
1. Care dintre subiectele tratate in aceasta unitate/modul considerai c este cel mai util i eficient? Argumentati raspunsul.
4. La care aplicaii practice ati intampinat dificultati in realizare? Care credeti ca este motivul dificultatilor intalnite?
5. Daca ar fi sa va evaluati, care este nota pe care v-o alocati, pe o scala de la 1-10?. Argumentati.
n mare msur
n mic msur
Nu
Obiective:
Dup ce vei parcurge aceast unitate de nvare, vei reui s:
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 initializarea 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 ctre constructorul clasei Rectangle care este responsabil cu
initializarea 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 aceiai 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 ofera
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
25
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 variabila 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 execut urmatoarele:
- se creaza o noua instanta a clasei date;
- se aloca memorie pentru aceasta instanta;
- se apeleaza o metoda speciala a clasei numita constructor.
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 dispozitie 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 aplicaie Java, cealalta
modalitate fiind prin intermediul interfeelor.
Declararea unei clase respecta urmatorul 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;
26
- <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.
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();
27
int metoda2() {
// Implementare
}
}
A::metoda1() {
// Implementare
}
// Java
class A {
void metoda1(){
// Implementare
}
void metoda2(){
// Implementare
}
}
Variabilele unei clase pot avea acelasi 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.
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 folosii pentru initializarea obiectelor acelei clase in momentul instantierii lor.
class NumeClasa {
[modificatori] NumeClasa([argumente]) { // Constructor}}
O clasa poate avea unul sau mai multi constructori care trebuie insa sa difere prin lista de argumente
primite. In felul acesta sunt permise diverse tipuri de initializari 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");
28
}
Dreptunghi() {
// Constructor fara argumente
x=0; y=0; w=0; h=0;
System.out.println("Instantiere dreptunghi");
}
}
Constructorii sunt apelati automat la instantierea 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 secvenele de cod scrise deja la constructorii cu mai multe argumente (mai
generali). Mai eficient, fara repeta aceleasi secvene de cod in toti constructorii (cum ar fi afisarea
mesajului Instantiere dreptunghi), clasa de mai sus poate fi rescrisa astfel:
Exemplu 1
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 instantierea 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
ctre 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
29
30
import java.util.*;
class elem {
char c; elem leg; static elem p,u;
elem() { }
elem(char ch) { c = ch; }
elem adaug(char ch) {
elem x = new elem(ch); leg = x; return x;
}
void creare() {
Scanner sc = new Scanner(System.in);
char ch = sc.next().charAt(0); p = new elem(ch); u = p;
ch = sc.next().charAt(0);
while (ch!='$') {
u = u.adaug(ch); ch = sc.next().charAt(0);
}
}
String direct(elem x) {
if (x==null) return "";
else return x.c + direct(x.leg);
}
String invers(elem x) {
if (x==null) return "";
else return invers(x.leg)+x.c;
}
}
class Lista {
public static void main(String[] s) {
elem Ob = new elem();
Ob.creare();
System.out.println( Ob.direct(elem.p) );
System.out.println( Ob.invers(elem.p) );
}
}
unde operatorul != are sensul "diferit de". S observm c metodele direct i invers puteau fi
invocate i cu argumentul Ob.p.
S analizm pe un exemplu modul de funcionare a programului de mai sus. S presupunem c la
intrare apare: abc$c1
Este citit caracterul 'a' i este creat un nou obiect de tipul elem, al crui cmp c este caracterul 'a' i
al crui cmp leg este null. Variabila u de tip elem pstreaz referina ctre acest obiect. Pentru
claritate, s notm cu P1 acest obiect. Variabilele p i u memoreaz o referin la obiectul P1.
n continuare este citit caracterul 'b'. Apoi obiectul P1 (referit prin u) este "pus la lucru". El creeaz
un nou obiect de tip elem, al crui cmp c este caracterul 'b' i al crui cmp leg este null; fie P2
acel obiect. Cmpul leg al lui P1 este actualizat i devine referina la P2. Variabila u va conine
acum o referin la obiectul P2.
31
n continuare este citit caracterul 'c'. Apoi obiectul P2 este "pus la lucru". El creeaz un nou obiect
de tip elem, al crui cmp c este caracterul 'c' i al crui cmp leg este null; fie P3 acel obiect.
Cmpul leg al lui P2 este actualizat i devine referina la P3. Variabila u va conine acum o referin
la obiectul P3.
Cum urmtorul caracter citit de la intrare este '$', crearea listei de obiecte este ncheiat.
S analizm efectul executrii instruciunii
System.out.println(Ob.direct(p));
care prevede scrierea la ieire a irului de caractere ntors de invocarea p.direct(p).
ntruct variabila p memoreaz o referin la obiectul P1, obiectul P1 va invoca metoda direct cu
parametrul P1. Este ntors irul format din caracterul 'a' la care se concateneaz irul ntors de
invocarea metodei direct efectuat pentru referina P2. Acest apel ntoarce irul format din
caracterul 'b', la care se concateneaz irul ntors de invocarea metodei direct efectuat pentru
referina P3. Cum aceast din urm invocare ntoarce irul vid (deoarece cmpul leg al lui P3
conine null), rezult c la ieire va aprea:
abc
n mod analog, executarea instruciunii:
System.out.println(Ob.invers(p));
face ca la ieire s apar:
cba
32
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;
33
35
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.
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; }
36
37
2.
Dac C este o clas fr un constructor definit, avnd un cmp x de tip ntreg, scriei
instruciunile prin care se creeaz un obiect ob de tip C i se afieaz valoarea cmpului x al
obiectului ob.
3.
Cum se numete posibilitatea de a exista mai muli constructori, respectiv mai multe metode
cu acelai nume, dar cu signaturi diferite? Exemplificai.
4.
38
Teste de control
2.1 Declararea constructorilor trebuie sa tina cont de:
A. relatia de motenire 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 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 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);
}}
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);
}
}
39
40
Formular de feedback
n dorina de ridicare continu a standardelor desfurrii activitatilor dumneavoastra, va rugm s completai acest chestionar i s-l
transmitei indrumatorului de an.
Disciplina: ________________________
Unitatea de invatare/modulul:__________________
Anul/grupa:__________________________
Tutore:_________________________
a) Coninut / Metoda de predare
Partea I
1. Care dintre subiectele tratate in aceasta unitate/modul considerai c este cel mai util i eficient? Argumentati raspunsul.
4. La care aplicaii practice ati intampinat dificultati in realizare? Care credeti ca este motivul dificultatilor intalnite?
5. Daca ar fi sa va evaluati, care este nota pe care v-o alocati, pe o scala de la 1-10?. Argumentati.
n mare msur
n mic msur
Nu
Obiective:
Dup ce vei parcurge aceast unitate de nvare, vei reui s:
interesai de noile faciliti; clasa (firma care a elaborat-o) i va pierde astfel vechii clieni. De
aceea vechea clas trebuie s rmn nemodificat, iar actualizarea ei trebuie fcut prin
mecanismul de extindere a claselor, prezentat n continuare.
S reinem deci ca o regul general de programare faptul c nu trebuie modificat o clas testat i
deja folosit de muli utilizatori; cu alte cuvinte, nu trebuie modificat "contractul" ce a dus la
scrierea clasei.
Exemplu 1
S presupunem c dorim s urmrim micarea unui punct n plan. Vom ncepe prin a considera
clasa:
class Punct {
int x,y; Punct urm;
Punct(int x, int y) { this.x=x; this.y=y; }
void Origine() { x=0; y=0; }
Punct Miscare(int x, int y) {
Punct p = new Punct(x,y); urm=p; return p;
}
}
Clasa Punct conine:
- cmpurile x, y, urm ;
- constructorul Punct cu signatura (int, int);
- metoda Origine cu signatura () i metoda Miscare cu signatura (int,int).
Vom extinde clasa Punct astfel nct punctul s aib i o culoare:
class Pixel extends Punct {
String culoare;
Pixel(int x, int y, String culoare) {
super(x,y); this.culoare=culoare;
}
void Origine() { super.Origine(); culoare="alb"; }
}
Clasa Pixel conine:
- cmpurile x,y,urm (motenite de la clasa Punct) i culoare (care a fost adugat prin extindere);
- constructorul Pixel cu signatura (int,int,String);
- metoda Miscare cu signatura (int,int), motenit de la clasa Punct, precum i metoda Origine cu
signatura (), care redefinete metoda Origine a clasei Punct.
Este adoptat urmtoarea terminologie: clasa Punct este superclas a lui Pixel, iar Pixel este
subclas (clas extins) a lui Punct.
n Java, clasele formeaz o structur de arbore n care rdcina este clasa Object, a crei definiie
apare n pachetul java.lang. Orice clas extinde direct (implicit sau explicit) sau indirect clasa
Object. tim c n informatic arborii "cresc n jos", deci rdcina (clasa Object) se afl pe cel mai
de sus nivel.
43
extindere, n arborele de clase. Este incorect de a interpreta aceti termeni n sensul de incluziune!
n exemplul de mai sus, apelurile super.Origine() i super(x,y) se refer respectiv la metoda Origine
i la constructorul din superclasa Punct a lui Pixel. Vom reveni ns cu toate detaliile legate de
super.
Fie Sub o clas ce extinde clasa Super. Sunt importante urmtoarele precizri:
- obiectele de tip Sub pot fi folosite oriunde este ateptat un obiect de tipul Super. De exemplu dac
un parametru al unei metode este de tipul Super, putem invoca acea metod cu un argument de tipul
Sub;
- spunem c Sub extinde comportarea lui Super;
- o clas poate fi subclas a unei singure clase (motenire simpl) deoarece, reamintim, clasele
formeaz o structur de arbore. Drept consecin o clas nu poate extinde dou clase, deci nu exist
motenire multipl ca n alte limbaje (exist ns mecanisme pentru a simula aceast facilitate i
anume interfeele);
- o metod din superclas poate fi rescris n subclas. Spunem c metoda este redefinit (dac nu
este static) sau ascuns (dac este static). Evident, ne referim aici la o rescriere a metodei folosind
aceeai signatur, pentru c dac scriem n subclas o metod cu o signatur diferit de signaturile
metodelor cu acelai nume din superclas, atunci va fi vorba de o metod nou. La rescriere nu
putem modifica tipul valorii ntoarse de metod. Accesul la metoda cu aceeai signatur din
superclas se face, dup cum vom vedea, prin precalificare cu super;
- un cmp redeclarat n Sub ascunde cmpul cu acelai nume din Super;
- cmpurile neascunse i metodele nerescrise sunt automat motenite de subclas (n exemplul de
mai sus este vorba de cmpurile x i y i de metoda Miscare);
- apelurile super din constructorii subclasei trebuie s apar ca prim aciune.
3.2 Caracteristicile unei ierarhi de clase
Organizarea unei aplicaii 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 aplicaiei.
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 motenire;
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.
44
Cmpurile pot fi ascunse prin redeclararea lor ntr-o subclas. Cmpul cu acelai nume din
superclas nu mai poate fi accesat direct prin numele su, dar ne putem referi la el folosind super
(vezi subcapitolul urmtor) sau o referin la tipul superclasei.
O metod declarat ntr-o clas poate fi rescris ntr-o subclas prin declararea ei n subclas cu
acelai nume, aceeai signatur i acelai tip al valorii ntoarse.
n metoda rescris putem schimba modificatorul de acces, cu condiia ca dreptul de acces s
creasc; reamintim c modificatorii de acces, n ordine de la cel mai restrictiv la cel mai permisiv,
sunt: private, protected, cel implicit (package) i public.
Spunem c metodele rescrise sunt ascunse (dac e vorba de metode statice) sau redefinite (n cazul
metodelor nestatice). Nu este vorba numai de o diferen de terminologie, deoarece metodele statice
pot fi rescrise (ascunse) numai de metode statice, iar metodele nestatice pot fi rescrise (redefinite)
numai de metode nestatice. Alte precizri vor fi fcute n continuare.
Fie A o clas i fie B o subclas a sa. S considerm urmtoarele patru aciuni echivalente din
punctul de vedere al obiectului a ce ia natere:
1) A a; a = new B(...);
2) A a = new B(...);
3) A a; B b; b = new B(...); a = b;
4) A a; B b; b = new B(...); a = (A) b;
S observm c este vorba de o conversie de la o subclas la o superclas, conversie numit
upcasting ; ea poate fi implicit (ca n primele trei aciuni) sau explicit (ca n cea de a patra
aciune).
Vom spune c obiectul a are tipul declarat A i tipul real B, ceea ce pune n eviden noiunea de
polimorfism.
Tipul real al unui obiect coincide cu tipul su declarat (este cazul obiectului b) sau este o subclas a
tipului declarat (vezi obiectul a).
Fie camp un cmp al clasei A, ce este redeclarat (ascuns) n subclasa B. Dac obiectul a face
referire la cmpul camp, atunci este vorba de cmpul declarat n clasa A, adic este folosit tipul
declarat al obiectului.
Fie met o metod a clasei A, care este rescris n subclasa B. La invocarea metodei met de ctre
obiectul a, este folosit fie implementarea corespunztoare metodei ascunse (dac este static), fie
cea corespunztoare metodei redefinite (dac este nestatic). Cu alte cuvinte, pentru metode statice
este folosit tipul declarat (la fel ca pentru cmpuri), iar pentru metode nestatice este folosit tipul real
al obiectului. n ambele cazuri, se pleac de la tipul indicat mai sus i se merge n sus spre rdcin
(clasa Object) n arborele de clase pn cnd se ajunge la primul tip n care apare metoda respectiv
(evident cu signatura corespunztoare); inexistena unui astfel de tip este semnalat chiar n faza de
compilare.
45
Exemplu 2
Urmtorul program:
class Super {
static void met1() { System.out.println("static_Super"); }
void met2() { System.out.println("Super"); } } class Sub extends Super { static void
met1() { System.out.println("static_Sub"); } void met2() { System.out.println("Sub"); } }
class Test1 {
public static void main(String[] s) {
Super Ob = new Sub(); Ob.met1(); Ob.met2();
}
}
produce la ieire:
static_Super
Sub
Exemplu 3
class A {
int va=1, v;
A() { v=va; System.out.print(""+met()); }
int met() { return va; }
}
class B extends A {
int vb=2, v;
B() { v=va+vb; }
int met() { return vb; }
}
class Constr {
public static void main (String[] s) {
A a = new B(); System.out.println("\t"+a.met());
}
}
static { vb=2; }
atunci la ieire vor aprea numerele 2 i 2.
Exemplu 4
class A {
String s="Super";
void scrie() { System.out.println("A : "+s); }
}
class B extends A {
String s="Sub";
void scrie() { System.out.println("B : "+s); }
}
class AB1 {
public static void main(String[] s) {
B b = new B(); A a = b;
a.scrie(); b.scrie();
System.out.println(a.s + "\t" + b.s);
}
La executare sunt tiprite urmtoarele:
B : Sub
B : Sub
Super Sub
adic rezultatele ateptate, innd cont c:
- pentru obiectul b: tipul declarat coincide cu cel real i este B;
- pentru obiectul a: tipul declarat este A, iar tipul real este B.
S considerm o clas C i dou subclase X i Y ale sale, precum i urmtoarea secven de
instruciuni:
C Ob;
. . . Ob = new X(...);
. . . Ob = new C(...);
. . . Ob = new Y(...);
...
n care Ob are mai nti tipul real X, apoi tipul real C, apoi tipul real Y. Spunem c Ob este o
variabil polimorfic, deoarece are pe rnd forma (comportamentul) a mai multor clase.
Din exemplul de mai sus rezult clar c doar la momentul executrii putem determina necesarul de
memorie pentru obiectul Ob, ceea ce justific de ce la creare obiectele sunt "stocate" n heap-ul
memoriei. Mai precis, la crearea unui obiect este alocat spaiu pentru toate cmpurile nestatice din
clas, inclusiv pentru cele ascunse.
47
}
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.
Exemplu 5
Programul urmtor:
class A {
String s="Super";
void scrie() { System.out.println("A : "+s); }
void metoda() { System.out.println("A:"); }
}
class B extends A {
48
String s="Sub";
void scrie() { System.out.println("B : "+s+" "+super.s); }
void metoda() { scrie(); super.scrie(); super.metoda(); }
}
class AB2 {
public static void main(String[] x) {
B b = new B(); b.metoda(); System.out.println(b.s);
System.out.println("****************");
A a; a = b; a.metoda(); System.out.println(a.s);
System.out.println("****************");
A c = new A(); c.metoda(); System.out.println(c.s);
}
}
produce la ieire urmtorul rezultat:
B : Sub Super
A : Super
A:
Sub
****************
B : Sub Super
A : Super
A:
Super
****************
A:
Super
Sunt incorecte, nencadrndu-se n formele prezentate mai sus de folosire a lui super, construcii de
forma b.super.metoda() (care ar inteniona ca pentru obiectul b s invocm metoda metoda din
superclasa clasei de tipul creia este) sau urcarea mai multor niveluri n arborele de clase prin
super.super. Pentru a avea acces la un cmp ascuns aflat cu dou niveluri mai sus n arborele de
clase putem proceda de pild ca n exemplul urmtor.
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
selectata depinde de tipul obiectului pe care variabila referinta x il indica in acel moment. Se spune
ca variabila referinta este polimorfica.
49
Exemplu 6
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 i 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()
50
{
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 aceiai semnatura (nume, tip valoare returnata i numar i tip parametrii) i trei metode cu
numele hranesc() care au, de asemenea, aceeai semnatura. Dar, aceste metode se afla in clase
diferite i anume, intr-o superclasa numita AnimalDeCasa i in doua clase derivate numite Pisica i
Caine care extind clasa AnimalDeCasa.
Spunem ca metodele caracteristici() i hranesc() din clasele derivate redefinesc (override)
metodea caracteristici() i, respectiv, hranesc() din superclasa.
Sa analizam acum instruciunea urmatoare:
51
In concluzie:
- pe de o parte, motenirea 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 aceiai secventa de cod
sa fie folosita de ctre 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 aceiai 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.
52
Clasele abstracte pot contine aceleasi tipuri de membri ca o clasa normala, inclusiv metodeleconstructor, 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.
53
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.
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 instruciunea System.out.println(). Practic, compilatorul Java apeleaza
automat toString() pentru fiecare obiect care se afiseaza cu metoda print().
Exemplu 7
}}
}
}
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
57
58
59
Formular de feedback
n dorina de ridicare continu a standardelor desfurrii activitatilor dumneavoastra, va rugm s completai acest chestionar i s-l
transmitei indrumatorului de an.
Disciplina: ________________________
Unitatea de invatare/modulul:__________________
Anul/grupa:__________________________
Tutore:_________________________
a) Coninut / Metoda de predare
Partea I
1. Care dintre subiectele tratate in aceasta unitate/modul considerai c este cel mai util i eficient? Argumentati raspunsul.
4. La care aplicaii practice ati intampinat dificultati in realizare? Care credeti ca este motivul dificultatilor intalnite?
5. Daca ar fi sa va evaluati, care este nota pe care v-o alocati, pe o scala de la 1-10?. Argumentati.
n mare msur
n mic msur
Nu
Obiective:
Dup ce vei parcurge aceast unitate de nvare, vei reui s:
4.1
Se ntmpl frecvent ca atunci cnd lucrm cu clase extinse, pentru o clas s nu putem preciza
implementarea unei metode, deoarece n subclasele sale ea va fi specific fiecreia dintre ele.
Atunci metodele din aceast categorie vor fi marcate cu cuvntul cheie abstract i se vor reduce la
antetul lor; n acelai timp i clasa trebuie nsoit de atributul abstract. Fiecare metod abstract
trebuie implementat, adic (re)definit n orice subclas care nu este la rndul su abstract.
Nu este posibil s crem obiecte ca instane ale unei clase abstracte. Pe de alt parte, ntr-o subclas
putem s redefinim o metod a superclasei transformnd-o ntr-o metod abstract; aceasta are sens
de exemplu dac n subarborele avnd ca rdcin subclasa, comportamentul materializat prin acea
clas nu mai este valabil n subarbore i este specific fiecrei extensii a subclasei.
Exemplu 1
Urmtoarea clas are ca scop s msoare timpul necesitat de executarea unei metode oarecare
fr parametri i care nu ntoarce vreun rezultat:
abstract class C {
abstract void met();
public long timp() {
long t0 = System.currentTimeMillis();
met();
return System.currentTimeMillis()-t0;
}
}
61
unde a fost folosit metoda static currentTimeMillis() a clasei System, metod care ntoarce
timpul curent n milisecunde. ntr-o clas ce extinde C, se poate redefini metoda met i apoi,
folosind un obiect ce este o instaniere a noii clase, putem apela metoda timp pentru a determina
timpul de executare a metodei met, ce are acum o implementare precis:
class C1 extends C {
void met () {
int x=0;
for (int i=0; i<1000000; i++) x=x+1-1;
}
}
class Abstr {
public static void main(String[] s) {
C1 Ob = new C1();
System.out.println( "durata="+Ob.timp() );
}
}
Legat de exemplul de mai sus, nu trebuie s ne mire c e posibil ca la executri diferite s obinem
timpi diferii: aceasta este o consecin a faptului c pe perioada executrii programului nostru
sistemul gazd "mai face i altceva", sau c a intervenit colectorul de reziduuri.
Aa cum s-a menionat anterior, este posibil s declarm clase abstracte n care toate metodele sunt
abstracte. Acest lucru poate fi realizat i prin intermediul interfeelor. n plus, aa cum vom vedea n
continuare, ele reprezint mecanismul propus de Java pentru tratarea problemelor ridicate de
motenirea multipl.
Motenirea multipl este facilitatea oferit de unele limbaje de programare ca o clas s
moteneasc (prin extindere) membri ai mai multor clase. Evident, aceast caracteristic important
a programrii orientate pe obiecte nu este neglijat n Java. Modul n care aceast facilitate poate fi
implementat n Java va fi tratat n continuare.
Interfeele reprezint o modalitate de a declara un tip constnd numai din constante i din metode
abstracte. Ca sintax, o interfa este asemntoare unei clase, cu deosebire c n loc de class trebuie
precizat interface, iar metodele nu au corp, acesta fiind nlocuit cu ';'.
Putem spune c interfeele reprezint numai proiecte, pe cnd clasele sunt o combinaie de proiecte
i implementri.
Ca i n cazul claselor abstracte, este evident c nu pot fi create obiecte de tipul unei interfee.
Cmpurile unei interfee au n mod implicit modificatorii static i final, deci sunt constante.
Metodele interfeelor sunt totdeauna publice i au n mod implicit modificatorul abstract. n plus ele
nu pot fi statice, deoarece fiind abstracte, nu pot fi specifice claselor.
Orice interfa este gndit pentru a fi ulterior implementat de o clas, n care metodele interfeei
s fie redefinite, adic s fie specificate aciunile ce trebuie ntreprinse. Faptul c o clas C
implementeaz o interfa I trebuie specificat prin inserarea informaiei implements I n antetul
clasei.
62
64
Derivarea interfeelor
Ca si in cazul claselor, interfeele 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 interfeelor nu exista o radacina comuna a arborelui de derivare asa cum exista
pentru arborele de clasa, clasa Object. Interfeele pot exista independent sau pot mosteni o alta
interfata.
Ierarhia de interfee este cu motenire multipla. O singura interfata poate mosteni oricate clase are
nevoie.
Exemplu 3
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 interfee. 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 () {
65
this (100) ;
}
public void push ( Object item ) throws StackException {
if (n == items . length )
throw new StackException (" Stiva este plina !");
items [n++] = item ;
}
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 !");
66
67
a)
Apare eroare la compilare deoarece metoda f din interfata I1 intr n contradicie cu metoda f din
interfaa I2;
b) Apare eroare la compilare deoarece n clasa C nu este definit variabila a.
c) Apare eroare la compilare deoarece nu se pot defini obiecte avnd ca tip o interfa.
d) Este corect i afieaz la execuie : 6 0.15
e) Este corect i afieaz la execuie : 6 0.15f
68
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 n Java o interfa poate extinde:
a)
b)
c)
d)
o clas
oricte clase
o interfa
oricte interfee
{
super(ns,g);
curs=c;
nota=n;
}
public void afisare()
{
..................................
System.out.println(curs+" "+nota);
}
}
public class test
{
public static void main(String[] args)
{
Student_2 s=new Student_2("Popescu",314,"Java",10);
s.afisare();
}
}
Dup executarea programului, pentru a se afisa Popescu 314 Java 10, spaiile punctate din metoda
afisare a clasei Student_2 trebuie nlocuite cu:
afisare();
Student_1.afisare();
super.afisare();
nimic, deoarece se apeleaz automat metoda afisare a clasei Student_1.
70
Formular de feedback
n dorina de ridicare continu a standardelor desfurrii activitatilor dumneavoastra, va rugm s completai acest chestionar i s-l
transmitei indrumatorului de an.
Disciplina: ________________________
Unitatea de invatare/modulul:__________________
Anul/grupa:__________________________
Tutore:_________________________
a) Coninut / Metoda de predare
Partea I
1. Care dintre subiectele tratate in aceasta unitate/modul considerai c este cel mai util i eficient? Argumentati raspunsul.
4. La care aplicaii practice ati intampinat dificultati in realizare? Care credeti ca este motivul dificultatilor intalnite?
5. Daca ar fi sa va evaluati, care este nota pe care v-o alocati, pe o scala de la 1-10?. Argumentati.
n mare msur
n mic msur
Nu
Obiective:
Dup ce vei parcurge aceast unitate de nvare, vei reui s:
nelegei conceptul de excepie i utilitatea folosirii excepiilor
pentru a asigura robusteea programelor;
lansai o excepie prin instruciunea throw i s tratai o excepie
prin construcia try;
scriei programe ce trateaz excepii
5.1 Excepii
Un aspect important de care trebuie inut cont ori de cte ori scriem un program este de a asigura
robusteea sa. Prin aceasta nelegem identificarea situaiilor nedorite (de exemplu introducerea unor
date eronate, eecul la ncercarea de a avea acces la Internet etc.) i specificarea aciunile
corespunztoare.
Exist ns i reversul aspectului descris mai sus. Introducerea de verificri prin instruciuni de
control face programul greu de citit i afecteaz buna sa structurare.
Ceea ce dorim este s avem un mecanism care:
- s permit ca fiecare condiie legat de robusteea programului s fie verificat exact acolo unde
poate s apar;
- atunci cnd condiia apare (suntem n situaia c s-a ntmplat ceva nedorit), controlul s fie trecut
automat unei seciuni de cod aflat n program "ntr-un loc potrivit" (care poate fi "departe" de locul
unde condiia a fost detectat), cod care realizeaz aciunea ce trebuie ntreprins. Acest cod va fi
numit handler (mnuitor) de excepie.
Limbajul Java rezolv problema de mai sus printr-un mecanism numit excepie. Excepia
semnaleaz faptul c a intervenit o anumit condiie special (nedorit); spunem c segmentul de
cod care a detectat condiia lanseaz o excepie de tipul corespunztor. Lansarea unei excepii
atrage dup sine captarea ei de ctre un handler de excepie; n lipsa acestuia, programul se va
ncheia cu un mesaj de eroare.
Mai precis, trebuie s realizm urmtoarele:
- s definim tipurile de excepie care s permit lansarea excepiilor dorite;
- s lansm o excepie la "locul potrivit" n program;
72
prin care are loc terminarea programului pentru toate excepiile prevzute de Java. Nu este ns
indicat s procedm n acest mod, deoarece n general nu vom obine suficiente informaii pentru
depanarea programului.
74
75
De obicei clauza finally este folosit pentru a elibera anumite resurse, ca de exemplu resurse grafice
sau fiiere (prin nchiderea lor).
Apoi controlul este trecut clauzei catch care capteaz (primete n parametrul su) obiectul creat,
ceea ce permite invocarea metodei MetExc din Exceptie.
Acum discuia de la nceputul capitolului capt consisten: o excepie de tipul Exceptie este
lansat din clasa Clasa i este captat n clasa C.
Exemplu 2
Vom aplica cele de mai sus la rezolvarea urmtoarei probleme:
Un vector a de lungime n conine n caractere. Ele trebuie mutate
ntr-o ordine oarecare n vectorul b, prin intermediul unui vector auxiliar stack de lungime lung,
care implementeaz o stiv. Operaiile permise sunt:
- mutarea unui element din a n vectorul stack : elementul curent (cel cu numrul de ordine cel mai
mic) din a este mutat dup elementele deja existente n stiv;
- extragerea (inclusiv tergerea) unui element din vrful stivei i nscrierea sa n b, dup ultimul
element nscris n b.
Dificultatea problemei const n tratarea cazului n care se ncearc extragerea unui element din
stiva vid i a cazului n care se ncearc introducerea unui element n stiva plin.
Prezentm n continuare programul, urmat de explicaiile necesare.
Unitatea de compilare Plina.java
class Plina extends Exception {
void print(String s) { System.out.print(s); }
}
Clasa principal este Stiva. Metoda sa main folosete, n afar de vectorii a i b, urmtoarele:
- variabila na desemneaz poziia din a de pe care va fi mutat un element n stiv;
- variabila nb desemneaz poziia din b pe care va fi nscris urmtorul element;
- obiectul R de tipul Random (clas din pachetul java.util) este folosit pentru apelarea metodei
nextInt fr argumente, ce ntoarce n mod aleator un numr ntreg;
- obiectul Ob de tipul S, unde clasa S implementeaz operaiile asupra stivei: metoda pune are un
parametru de tip char pe care l adaug stivei, iar metoda ia fr parametri ntoarce elementul din
vrful stivei.
Metoda main din Stiva repet, atta timp ct n b nu au fost nscrise n caractere, urmtoarele aciuni:
- este generat aleator un nou numr ntreg;
- dac valoarea sa este negativ i mai exist elemente n a, este invocat metoda pune din clasa S cu
intenia de a muta un element din a n stiv; invocarea este urmat de mrirea lui na cu o unitate. n
caz contrar este invocat metoda ia cu intenia de a obine un element din stiv i de a-l muta n
vectorul b;
- n final sunt listate elementele lui b.
Observm c nu sunt tratate aici cazurile de excepie (adugare la stiv plin sau extragere din stiva
vid), aceste verificri cznd n sarcina clasei S. Utilizarea clasei Random permite un
nedeterminism n succesiunea de operaii asupra stivei, ceea ce va conduce la vectori b diferii de la
o executare la alta.
n clasa S este folosit vectorul stack de lungime lung pentru implementarea stivei. n ns este
memorat numrul de elemente din stiv. n antetul metodei pune este declarat tipul de excepie
plina, cruia i corespunde clasa plina ce extinde clasa Exception. Deosebim dou situaii:
- dac stiva este plin (ns=lung), atunci prin throw este creat o excepie (un obiect de tip Plina).
Controlul este trecut acelei clauze catch din Stiva care este identificat prin tipul Plina al excepiei.
Obiectul creat este transmis n e. Prin invocarea e.print(...) este apelat metoda print a clasei Plina
care tiprete mesajul primit la invocare. S observm c n acest mod se evit incrementarea lui na;
- dac stiva nu este plin, atunci este nscris un nou element n stiv.
Modul n care acioneaz metoda ia este analog.
Este uor de observat c s-a folosit exact schema descris anterior, cu clasele Plina i Vida n rolul
clasei Exceptie, cu S n postura clasei Clasa i cu Stiva n locul clasei C.
78
Exemplu 3
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 excepii 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++)
79
}}
Exemplul, descris mai sus, prezinta modul de interceptare a unui anumit tip de exceptie. Deoarece
clasele de excepii 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 excepii va intercepta si trata si clasele de excepii 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 {
return Integer.parseInt(citesteString()); }
catch(Exception nfe) {
System.out.println("Nu este numar intreg.");
return 0; }
}
}
In aceasta situatie, blocul catch intercepteaza toate excepiile Exception, dar si clase de excepii
derivate din clasa Exception, cum este si cazul clasei de excepii 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 excepii 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 excepii
semnalate in blocul try, care nu sunt inrudite prin motenire, 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) excepii ctre alte metode (transmiterea
excepiilor ctre baza stivei de apel)
Observatii:
1.Excepiile specificate in clauza throws nu trebuie sa fie interceptate (prinse) si tratate in cadrul
metodei respective care foloseste clauza throws.
2.Semnalarea (aruncarea) excepiilor incepe cu metoda originala care daca nu intercepteaza si nu
trateaza excepiile 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
80
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 excepii 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 excepiilor aruncate de metoda originala nu se poate omite,
daca metoda respectiva genereaza excepii 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 trycatch) sau specificarea lor in clauza throws.
Exemplul prezentat anterior, al clasei CitesteDate, poate fi modificat astfel incat excepiile de tipul
IOException care sunt semnalate (aruncate) in metoda citesteString (), de ctre 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 {
81
Sa ne reamintim ca, toate excepiile 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 excepii, aceasta devine o exceptie Java si este
semnalata (aruncata) folosind instruciunea throw.
Instruciunea throw are un singur argument, care trebuie sa fie o instanta a unei clase de excepii
derivate din clasa Exception.
Forma instructiunii throw este:
throw <instanta_clasa_de_exceptii>;
De exemplu, pentru semnalarea (aruncarea) unei excepii 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
instruciunea throw, ctre 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 aplicaiei (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));
83
}
//scoatem elementele din stiva si le afisam
System.out.print("Continutul stivei este: ");
try
{
while (true)
{
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.
84
3. O metoda poate lansa mai multe excepii? Cum se specific ce excepii lanseaz o metod?
4. Presupunem c avem urmtoarea clas cu o metod afisFisier care citete datele dintr-un
fiier linie cu linie i le afieaz pe ecran. Metoda lanseaz excepiile
FileNotFoundException dac fiierul nu este gsit i IOException dac apar probleme la
citire.
Scriei o clas care s conina metoda main n care sa afiai datele dintr-un fiier cu numele
"exceptii.txt" folosind metoda afisFisier i care s trateze erorile lansate de aceasta, afind
un mesaj corespunzator: "Fisier inexistent" sau "Eroare la citire".
import java.util.*;
import java.io.*;
class Fisier{
void afisFisier(String numeFisier)
throws FileNotFoundException,IOException{
Scanner sc=new Scanner(new File(numeFisier));
while(sc.hasNextLine())
System.out.println(sc.nextLine());
sc.close();
}
}
Teste de control
5.1 Fie urmatorul 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 exceptie:
"ArithmeticExpetion");
C. Codul este corect si va afisa Infnit;
D. Codul este corect si va afisa NaN
85
5.2 Prin modalitatea sa de tratare a excepiilor, 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 excepii 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. excepii;
86
Formular de feedback
n dorina de ridicare continu a standardelor desfurrii activitatilor dumneavoastra, va rugm s completai acest chestionar i s-l
transmitei indrumatorului de an.
Disciplina: ________________________
Unitatea de invatare/modulul:__________________
Anul/grupa:__________________________
Tutore:_________________________
a) Coninut / Metoda de predare
Partea I
1. Care dintre subiectele tratate in aceasta unitate/modul considerai c este cel mai util i eficient? Argumentati raspunsul.
4. La care aplicaii practice ati intampinat dificultati in realizare? Care credeti ca este motivul dificultatilor intalnite?
5. Daca ar fi sa va evaluati, care este nota pe care v-o alocati, pe o scala de la 1-10?. Argumentati.
n mare msur
n mic msur
Nu
Obiective:
Dup ce vei parcurge aceast unitate de nvare, vei reui s:
88
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 inclus 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 ctre 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 ctre 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 ctre 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.
89
Exemplu 1
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 ctre 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).
91
Crearea (deschiderea) unui flux de iesire ctre 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.
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 instruciune deschide un flux de iesire ctre fisierul test1.dat:
FileOutputStream fis = new FileOutStream(test1.dat);
Urmatoarea instruciune deschide un flux de iesire ctre 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.
92
Exemplu 2
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.
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 aplicaie Java. Prin folosirea unui
tampon se pot accesa (citi sau scrie) date fara a folosi sursa originala (fisierul) sau destinatia
originala (fisierul) de date.
93
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 aplicaiei Java.
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.
94
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 ctre 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:
95
Exemplu 3
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;
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;
96
}
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 aplicaii Java depinde de platforma pe care se
execut 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 aplicaiei 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 aplicaii Java depinde de platforma pe care se
execut 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.
97
In cadrul aplicaiei 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,
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:
98
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.
Exemplu 4
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 {
99
100
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());
}
101
}
}
- <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.
103
Exemplu 5
import java.io.*;
class CitesteSiruri {
public static void main(String[] args)
{
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.
104
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.
105
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.*;
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());
}
}
}
106
Formular de feedback
n dorina de ridicare continu a standardelor desfurrii activitatilor dumneavoastra, va rugm s completai acest chestionar i s-l
transmitei indrumatorului de an.
Disciplina: ________________________
Unitatea de invatare/modulul:__________________
Anul/grupa:__________________________
Tutore:_________________________
a) Coninut / Metoda de predare
Partea I
1. Care dintre subiectele tratate in aceasta unitate/modul considerai c este cel mai util i eficient? Argumentati raspunsul.
4. La care aplicaii practice ati intampinat dificultati in realizare? Care credeti ca este motivul dificultatilor intalnite?
5. Daca ar fi sa va evaluati, care este nota pe care v-o alocati, pe o scala de la 1-10?. Argumentati.
n mare msur
n mic msur
Nu
Bibliografie
1. Horia
108