Documente online.
Zona de administrare documente. Fisierele tale
Am uitat parola x Creaza cont nou
 HomeExploreaza
upload
Upload




Tot despre clase si obiecte

java


Tot despre clase si obiecte 



Simboluri speciale: null si this

Asa cum am vazut in capitolele precedente, numele unei clase poate fi utilizat la fel ca numele unui tip predefinit, pentru a declara variabile. In Java variabilele al caror tip este o clasa sunt reprezentate ca referinte (adrese)  spre obiecte ale clasei. 
Ca sa putem initializa o asemenea referinta trebuie sa generam un obiect nou sau sa folosim o alta referinta la un obiect deja existent. De exemplu, folosind clasa Punct definita in modulul 2.1, putem scrie: 

Punct p1 = new Punct( ); // se creaza un obiect nou
Punct p2 = p1; // p2 va referi acelasi obiect ca si p1

In legatura cu referintele exista o valoare speciala, null, care poate fi atribuita unei variabile-referinta. Cand o referinta are valoarea null, aceasta inseamna ca ea nu refera nici un obiect. Cu alte cuvinte, null este echivalent cu "nici-o-adresa". In programe se poate testa daca o variabila-referinta are valoarea null

Punct p2 = null;
// alte prelucrari asupra lui p2
if ( p2 == null ) FaCeva( );
else FaAltceva( ); // de exemplu p2.move(...)


Observatie: valoarea null nu este atribuita automat tuturor variabilelor referinta la declararea lor. Regula este urmatoarea: daca referinta este o data-membru a unei clase si ea nu este initializata in nici un fel (vezi capitolul despre initializari), la crearea unui obiect al clasei respective referinta va primi implicit valoarea null. Daca insa referinta este o variabila locala a unei metode  initializarea implicita nu mai functioneaza. De aceea se recomanda ca programatorul sa realizeze INTOTDEAUNA o initializare explicita a variabilelor.

class OClasa  

class AltaClasa  

Simbolul this

Am vazut pana acum ca obiectele unei clase pot fi accesate de clientii lor prin intermediul referintelor. Se pune problema cum putem obtine o referinta la un obiect din INTERIORUL lui insusi, mai precis din interiorul functiilor membru ale obiectului. Pentru aceasta exista simbolul this
Simbolul this este o referinta care poate fi utilizata doar in cadrul functiilor membru non-statice ale unei clase. this este de fapt, din punctul de vedere al unei functii membru, referinta spre obiectul receptor, "posesor" al acelei functii. Se poate spune ca this reprezinta intr-un fel "constiinta de sine" a unui obiect, in sensul ca obiectul isi cunoaste adresa la care este localizat in memorie. 
Practic orice referire a unui membru non-static v in interiorul unei metode apartinand aceleiasi clase ca si v poate fi considerata ca echivalenta cu this.v. De exemplu, metoda move din clasa Punct definita in lucrarea precedenta poate fi scrisa sub forma:
 

class Punct  

In realitate toate referirile la membrii non-statici ai unui obiect, din interiorul unei metode non-statice sunt considerate de catre compilatorul de Java ca si cum ar fi prefixate de this

Cand FOLOSIM explicit referinta this

Ea este necesara in primul rand acolo unde ar putea exista conflicte de nume cu datele membru ale unui obiect. De exemplu: 

class Punct  

  • O alta categorie de situatii in care este necesara referinta this apare atunci cand: 

A. O metoda trebuie sa returneze ca rezultat o referinta la obiectul ei receptor: 

class Rational  

public Rational aduna(int n)  

// exemplu de utilizare a clasei
class ClientRational  

In exemplul de mai sus apelul in lant al metodei adauga se desfasoara astfel: mai intai se executa primul apel, avand ca obiect receptor obiectul indicat de referinta a; rezultatul obtinut este returnarea unei referinte spre un obiect al clasei Rational; acest obiect va fi in continuare obiectul receptor pentru al doilea apel al 17217t1924r metodei adauga

B. Referinta la obiectul receptor trebuie transmisa ca parametru la apelul unei alte metode: 
 

class Context  

public int Calcul()  

public int getX()  

class Algoritm  

/* exemplu de utilizare a claselor */ 
class Client  

In exemplul de mai sus se observa ca acelasi obiect Algoritm deserveste 2 obiecte Context. De aceea, fiecare dintre obiectele c1 si c2 se va pasa pe sine ca parametru la apelul metodei Calcul din Algoritm
In fine, simbolul this reprezinta mijlocul prin care poate fi apelat un constructor al unei clase din interiorul altui constructor al aceleiasi clase: 
 

class Punct  

public Punct(Punct p)  

Atentie:

  • Apelul unui constructor din interiorul altui constructor al clasei in cauza NU inseamna crearea unui nou obiect, ci pur si simplu, pentru obiectul receptor curent se va executa codul constructorului apelat, la fel ca in cazul apelarii unei functii-membru obisnuite; efectul va fi atribuirea de valori in campurile obiectului, conform codului constructorului apelat. Cu alte cuvinte, putem considera ca, privind din interiorul unei clase, numele constructorilor ei sunt this(lista_parametri)
  • Un constructor nu poate fi apelat cu ajutorul simbolului this DECAT din interiorul altui constructor si nu al altor metode. In felul acesta se asigura indeplinirea conditiei ca pentru un obiect initializarea datelor prin constructor sa se execute O SINGURA data. 

Cum NU TREBUIE SA FOLOSIM referinta this

Referinta this nu poate fi modificata (nu poate aparea ca membru stang intr-o atribuire: 

class OClasa  

Ceea ce vrea exemplul de mai sus sa scoata in evidenta este faptul ca un obiect nu-si poate muta singur locul in memorie. Intuitiv, o atribuire ca cea din exemplu seamana cu isprava baronului Munchausen care se lauda ca odata s-a salvat dintr-o mlastina tragandu-se singur in sus de propriile urechi (chestie care contravine legii conservarii impulsului :-)). 

Referinta this nu poate sa apara in afara corpului de instructiuni al metodelor non-statice ale unei clase sau in afara blocurilor de initializare asociate cu variabilele membru ale unei clase. Referinta this NU poate fi utilizata in interiorul unei functii-membru statice. DE CE oare ? Putem comenta in Conferinta Software Consulting. 
 

Colectorul de reziduuri (Garbage Collector)

In legatura cu obiectele claselor stim acum ca ele se creaza dinamic, folosind operatorul new. Nu am spus insa nimic referitor la eliberarea memoriei ocupate de obiecte. 
Si in limbajele Pascal si C exista posibilitatea alocarii dinamice a memoriei, dar programatorul are obligativitatea de a gestiona singur aceasta memorie, in sensul ca, la un moment dat mai trebuie sa si elibereze ceea ce a alocat. 
In Java programatorul este scutit de sarcina de a face curat in memoria dinamica. De acest lucru se ocupa o componenta a masinii virtuale (interpreterului) numita Garbage Collector (GC). Principiul de lucru al GC-ului este urmatorul: 

Daca spre un anumit obiect nu mai exista nici o referinta externa, in nici o functie activa, acel obiect ramane "orfan" si devine candidat la eliminare din memoria heap.

Nu este obligatoriu ca un obiect sa fie sters imediat dupa ce dispare si ultima referinta spre el. Este posibil ca obiectul sa ramana alocat pana cand, in decursul executiei programului, se ajunge in situatia ca spatiul liber din heap sa nu mai fie suficient. De asemenea, este posibil ca un program sa nu aiba nevoie de memorie multa si ca urmare, sa nu trebuiasca sa se colecteze reziduurile. In cazul programelor didactice, in care numarul total al obiectelor create nu depaseste cateva zeci, practic GC-ul nici nu intervine. 
In exemplul de mai jos sunt ilustrate situatiile in care un anumit obiect ar putea ajunge sa fie candidat la stergere: 
 

public void oMetoda( )  

public void altaMetoda()

Pentru utilizator actiunea GC-ului este transparenta. Singurul efect care ar putea fi sesizat in unele programe ar fi o anumita incetinire a lor. 
Pentru a vizualiza insa activitatea GC-ului, vom incerca sa simulam o situatie de umplere a memoriei heap cu obiecte orfane, astfel incat sa-l fortam sa intre in actiune. Se pune problema cum vom vedea noi ca intr-adevar GC-ul elimina obiecte ? Aici ne bazam pe faptul ca GC nu actioneaza "fara preaviz", ci, inainte de a elimina un obiect din memorie, el incearca sa apeleze o anumita metoda speciala detinuta de obiectul respectiv. 
Aceasta metoda are antetul urmator: 

protected void finalize() throws Throwable;

si ea va fi executata chiar inainte ca obiectul sa dispara (incercati sa ignorati cuvantul protected , precum si clauza throws Throwable, pentru ca despre ele vom invata la capitolul cu mostenirea, respectiv cu tratarea exceptiilor). 

Am putea de exemplu sa definim metoda finalize astfel incat aceasta sa afiseze pe ecran un anumit mesaj. Atunci cand vom vedea pe ecran mesajul respectiv vom sti ca in momentul acela un obiect a fost eliminat de catre GC din memorie. Daca prevedem si in constructor afisarea unui mesaj, putem urmari practic traseul vietii unui obiect. In secventa de mai jos este propus un program in care sa surprindem interventiile GC-ului: 
 

import java.io.*; 

class OClasa  

protected void finalize() throws Throwable  

class ClientOClasa 

public static void main(String[] arg) 
/* in bucla for de mai sus am "inundat" memoria heap cu obiecte orfane; cand memoria se umple, urmatoarea operatie de creare de obiect nu se va mai putea face decat dupa ce intervine GC sa faca curatenie; atunci vom constata aparitia pe ecran a mesajului tiparit de metoda finalize */ 

oMetoda(); 
System.out.println("am revenit din metoda"); /* deci am pierdut referinta la obiectul local de acolo */ 
System.out.println("acum ies din main"); 

Pentru a putea analiza in liniste mesajele afisate prin executia programului de mai sus, se recomanda ca in comanda de lansare in executie sa se faca o redirectare a iesirii spre un fisier care apoi va putea fi vizualizat cu un editor oarecare. Pentru aceasta, comanda de executie se va da astfel: 

java ClientOClasa > fis.txt

Metode 

Asa cum am aratat cand am vorbit despre componenta unei clase, functiile care fac parte dintr-o clasa se mai numesc metode sau operatii. 
Definitia unei metode respecta in general urmatoarea sintaxa: 

tip_returnat nume_metoda (lista_parametri_formali) 

unde tip_returnat poate fi un tip primitiv, o referinta de obiect sau void. In acest din urma caz metoda nu returneaza de fapt rezultate. 
In Java metodele nu pot sa apara decat in interiorul claselor. 

In ceea ce priveste transmiterea parametrilor, aceasta se face NUMAI prin VALOARE. La transmiterea prin valoare metoda primeste o copie a parametrilor de apel. Ca urmare, orice modificare efectuata asupra parametrilor in interiorul metodei se produce asupra copiei respective, care dispare cand metoda isi incheie executia. Astfel, la revenirea in apelant modificarile nu se mai "vad". Exemplu: 
 

class OClasa 

public static void metoda2() 

In exemplul dat parametrul metodei a fost de tip primitiv. Aspecte mai interesante apar cand parametrul este o referinta la un obiect. Practic si in acest caz transmisia se face prin valoare, dar cea care se transmite asa este referinta, adica adresa obiectului. Prin urmare, daca in interiorul metodei modificam valoarea referintei, modificarea nu va fi resimtita in apelant. In schimb, daca modificam interiorul obiectului indicat de referinta, modificarea respectiva ramane: 
 

class Masina 

public void schimbaCuloare(String c) 

public String ceCuloare() 

public static void metoda1(Masina m) 

public static void metoda2(Masina m) 

public static void metoda3() 

Ca sa intelegem mai bine ce se intampla si de ce, intr-un caz ca cel din exemplul de mai sus, sa ne uitam putin pe figura urmatoare: 

In figura este surprins momentul in care s-a intrat in metoda metoda1, inainte de a se efectua prima instructiune din ea. Se observa ca parametrul m al metodei este o copie a parametrului de apel, a, ceea ce inseamna ca ambele referinte indica spre acelasi obiect. Asa se explica de ce modificarea adusa de metoda metoda1 asupra interiorului obiectului se vede si in metoda3, la revenirea din apel. 

Sa vedem acum figura urmatoare: 

Aici este surprins momentul de dupa executia primei instructiuni din metoda metoda2, adica modificarea referintei m. Acum m indica alt obiect decat a. La revenirea din metoda2 se pierd variabilele locale ale acesteia, deci si m. Ca urmare, in metoda metoda3 lucrurile se regasesc asa cum au fost lasate. E adevarat ca in heap a ramas obiectul creat de metoda metoda2, dar spre el nu mai exista nici o referinta vizibila si ca atare, acest obiect va constitui un candidat la stergere de catre colectorul de reziduuri. 

Setul de instructiuni 

In cele expuse pana acum am considerat o abordare cumva intuitiva a instructiunilor din metode, in ideea ca participantii la acest curs au deja notiuni elementare de programare (si chiar mai mult :-)) si prin urmare nu intampina probleme in a intelege o instructiune de atribuire, o instructiune de test sau o instructiune ciclica. Am accentuat mai mult aspectele legate strict de lucrul cu obiecte, care se presupune a fi ceva nou pentru cursanti. 
E bine insa sa dedicam un paragraf si prezentarii sistematice a setului de instructiuni Java. El se bazeaza pe setul de instructiuni din limbajele C/C++, fiind format din urmatoarele categorii principale: 

  • Atribuire

Sintaxa: 

variabila = expresie;

Semantica: se evalueaza expresia din membrul drept, iar rezultatul se depune la variabila specificata in membrul stang. In general este necesar ca cei 2 membri sa aiba acelasi tip. Expresiile din membrul drept pot fi: 

  • aritmetice, in care operatorii pot fi: + (adunare), - (scadere), * (inmultire), / (impartire) si % (modulo); 
  • logice, in care operatorii pot fi: || (sau logic), && (si logic) sau ! (negare - operator unar); operanzii expresiilor logice nu pot fi decat expresii care dau ca rezultat una din constantele booleene true sau false; 
  • de comparare, in care operatorii pot fi: == (test egalitate), != (test inegalitate), < (test mai mic), <= (test mai mic sau egal), > (test mai mare), >= (test mai mare sau egal); aceste expresii dau ca rezultat una din constantele booleene true sau false; 
  • de tip referinta; o asemenea expresie poate fi formata din: 
    • o variabila referinta; 
    • un apel de metoda care returneaza ca rezultat o referinta; 
    • o creare de obiect prin aplicarea operatorului new
  • de selectie; o asemenea expresie are forma: 

conditie ? expresie1 : expresie2

unde conditie este o expresie logica sau de comparare, iar expresie1 si expresie2 sunt expresii oarecare. 
Rezultatul expresiei de selectie este expresie1 daca evaluarea conditiei da rezultatul true, respectiv expresie2 in caz contrar. Practic, o atribuire in care membrul drept este expresia de selectie poate fi inlocuita cu o instructiune de testare de forma: 

if(conditie) 

variabila=expresie1;

else 

variabila=expresie2;

Operanzii din expresii pot fi variabile, constante ale tipurilor primitive sau apeluri de metode, cu conditia ca acestea sa nu aiba void ca tip returnat. 

  • Instructiuni de test

Sintaxa: 

if(conditie) 

instructiune1;

sau 

if(conditie) 

instructiune1;

else 

instructiune2;

In loc de instructiune1, respectiv instructiune2, putem avea secvente formate din mai multe instructiuni, terminate prin caracterul ;, dar atunci secventele respective trebuie incluse intre acolade. 

Semantica instructiunilor de test: se evalueaza conditia si daca rezultatul obtinut este true, se executa ramura instructiune1; in caz contrar se executa instructiunea ce urmeaza dupa instructiunea de test (cazul fara ramura esle) sau ramura instructiune2 (daca avem ramura else). 

  • Instructiuni de ciclare

In Java avem 3 tipuri de instructiuni de ciclare: 

  • Ciclu cu test la inceput


Sintaxa: 

while(conditie) 

instructiune1;

sau 

while(conditie) 

Semantica: se evalueaza conditia si daca rezultatul este true, se executa instructiunile din corpul ciclului, dupa care se reia evaluarea conditiei; cand rezultatul evaluarii conditiei este false se trece la instructiunea care urmeaza instructiunii while. De aici deducem ca exista situatii in care o bucla while poate sa nu se execute niciodata, anume atunci cand conditia da false inca de la prima evaluare. 
 

  • Ciclu cu test la sfarsit


Sintaxa: 

do 

instructiune1;

while(conditie); 

sau 

dowhile(conditie); 

Semantica: se executa instructiunile din corpul ciclului, apoi se evalueaza conditia si daca rezultatul este true se reia corpul ciclului; cand rezultatul evaluarii conditiei este false se trece la instructiunea care urmeaza instructiunii do. De aici deducem o bucla do se executa cel putin o data. 
 

  • Ciclu cu contor


Asa cum vom vedea in continuare, ciclul cu contor din Java este de fapt o generalizare a ceea ce se intelege de obicei prin ciclu cu contor, in sensul ca executia lui nu este neaparat controlata de valoarea unui contor. Denumirea provine insa de la faptul ca acest tip de ciclu este cel mai adesea folosit in contextul cu contor. 

Sintaxa: 

for(instr1;conditie;instr2) 

instructiune3;

sau 

for(instr1;conditie;instr2) 

Semantica: se executa instructiunea instr1, apoi se evalueaza conditia; daca rezultatul este true, se executa corpul ciclului, apoi se executa instr2 si se reia evaluarea conditiei; cand rezultatul conditiei este false se trece la instructiunea de dupa ciclu. Se observa ca si aici, ca si in cazul buclei while pot exista situatii cand bucla nu se executa nici o data. 
In particular, oricare dintre elementele instr1, conditie si instr2 pot sa lipseasca. Daca lipseste conditie, se considera ca e ca si cum am avea acolo tot timpul rezultat true. 

Exemple: 

/* caz clasic de folosire a buclei for */ 
for(int i=0;i<arg.length;i++) 

System.out.println(arg[i]); /* se afiseaza elementul de index i al tabloului arg */ 

/* varianta echivalenta cu cea de mai sus */ 
int i=0; 
for(;;) 

/* caz in care bucla for nu are contor */ 
double a=0.005; 
double b; 
for(b=a; b<1; a=a*2) 

  • Instructiuni de control al ciclurilor

Sunt instructiuni care pot sa apara in corpul unui ciclu. 

  • Instructiunea de iesire fortata din ciclu


Sintaxa: 

break;

Semantica: executia acestei instructiuni presupune parasirea ciclului si trecerea la instructiunea urmatoare celei de buclare. Asa cum vom vedea imediat, instructiunea break poate sa apara si intr-o structura switch
Un exemplu tipic de folosire a lui break putem vedea in exemplul de mai sus, in cadrul instructiunii for
 

  • Instructiunea de revenire la inceputul ciclului


Sintaxa: 

continue;

Semantica: executia acestei instructiuni presupune neglijarea eventualelor instructiuni de dupa continue si trecerea la evaluarea conditiei, apoi, in functie de rezultatul evaluarii reluarea obisnuita a ciclului sau parasirea lui. 

Exemplu: 

/* presupunem ca avem o bucla in care trebuie sa citim 10 numere cuprinse in intervalul [1,100] si sa calculam media lor aritmetica; in cazul in care utilizatorul introduce o valoare eronata, este invitat sa mai tasteze o data; pentru citire vom pune in comentariu secventa, pentru ca nu stim inca sa citim de la tastatura; presupunem ca cele 10 valori trebuie depuse intr-un tablou tab declarat in prealabil */ 

double med=0; 
for(int i=0;i<10;i++) 
med=med+tab[i]; 


med=med/10;

Sa vedem cum interpretam secventa de mai sus: in cazul in care utilizatorul introduce un numar eronat, i se afiseaza un mesaj corespunzator, dupa care instructiunea continue determina ca executia sa se reia de la testul conditiei, adica i<10, neglijandu-se portiunea din bucla care cuprinde actualizarea variabilei med, precum si incrementarea contorului i (va aduceti aminte ca la bucla de tip for, ultima instructiune din corpul buclei este de fapt instructiunea care apare ca al treilea element intre parantezele ce urmeaza dupa cuvantul for). Deci contorul ramane la aceeasi valoare, ceea ce inseamna ca se va citi acelasi element al tabloului tab pana cand se va tasta o valoare corecta. Abia atunci ramura instructiunii if va fi "sarita", executia continuand cu actualizarea lui med si a lui i
 

  • Instructiunea de revenire din metode

Sintaxa: 

return expresie;

sau 

return;

Prima forma este admisa doar in metodele al caror tip returnat NU este void, iar tipul expresiei trebuie sa fie compatibil cu tipul returnat. 
A doua forma este admisa doar in metodele care au ca tip returnat void, adica acele metode care nu returneaza de fapt rezultate. 
Semantica: executia unei instructiuni return are ca efect parasirea metodei curente si revenirea in apelant. Pentru metodele care returneaza rezultat, are loc copierea in stiva, pe spatiul apelantului a rezultatului returnat, care poate fi astfel utilizat in calcule sau pentru atribuire. 

  • Operatii speciale
  • Operatii speciale de atribuire


Sunt o categorie aparte de atribuiri, care utilizeaza urmatorii operatori: +=,-=,*=,/= si %=, in loc de obisnuitul =. Exemplu: 

a+=expr;

O asemenea instructiune este echivalenta cu: 

a=a+expr;

Similar se interpreteaza si ceilalti operatori speciali de atribuire. 
 

  • Operatii de incrementare/decrementare


Presupun utilizarea operatorilor ++, respectiv --
Cu operatorul ++ ne-am mai intalnit in exemplele de pana acum si l-am preluat "din mers", in mod intuitiv. Acum i-a venit randul la o explicatie mai detaliata. 
In primul rand trebuie spus ca acesti operatori sunt operatori unari (au un singur operand) si se pot aplica in 2 moduri: prefix si postfix. Cu ajutorul unor exemple vom vedea care este diferenta dintre cele 2 moduri. Inainte de aceasta, insa, vom preciza ca efectul acestor operatori consta in cresterea/scaderea valorii operandului lor cu valoarea 1. 
Sa consideram ca in cele ce urmeaza vom aplica operatorul de incrementare asupra unei variabile i care initial are valoarea 5. 
In varianta prefix sintaxa operatiei este: 

++i

iar in varianta postfix: 

i++


Operatia de incrementare poate constitui o instructiune in sine, daca este urmata de caracterul ;. In acest caz diferenta dintre forma postfix si cea prefix nu este sesizabila. 
Daca insa incrementarea apare ca operand intr-o expresie, atunci cele 2 forme au efecte diferite. Fie instructiunea: 

a=++i;

In acest caz efectul este: se mareste valoarea lui i cu 1, deci se ajunge la 6 si apoi aceasta valoare se atribuie lui a
Daca insa avem: 

a=i++;

lucrurile se petrec asa: mai intai se ia valoarea veche a lui i, adica 5 si se atribuie lui a si dupa aceea se mareste valoarea lui i. Deci in ambele situatii i va avea in final valoarea 6, dar a va avea valori diferite.

Lucrul cu tablouri in Java 

Ca si alte limbaje de programare de nivel inalt, limbajul Java ofera ca tip predefinit tipul tablou, cu ajutorul caruia se pot crea colectii de variabile de acelasi tip. Componentele tabloului pot fi accesate cu ajutorul unui index. Acesta trebuie sa apartina unui tip intreg, iar numerotarea elementelor intr-un tablou incepe intotdeauna de la 0. Pentru a declara un tablou se utilizeaza notatia: 

tip_element[ ] nume_tablou;

O simpla declaratie ca aceasta nu presupune alocare de spatiu. In Java elementele unui tablou se aloca DINAMIC, specificandu-se dimensiunea tabloului: 

tip_element[ ] nume_tablou = new tip_element[numar_elemente];

In Java numele unui tablou este considerat ca o REFERINTA DE OBIECT, iar tabloul propriu-zis este un obiect. Dupa ce s-a rezervat spatiu pentru elemente NU mai putem modifica dimensiunea tabloului, decat daca cream un alt tablou, asa ca in exemplul de mai jos: 
 

int [ ] tab = new int[10]; // am creat un tablou de 10 intregi 

/* acum ii completez cumva elementele si constat ca imi trebuie un tablou mai mare 
iata cum procedez: 
mai intai creez un alt tabou, cu dimensiunile dorite (de ex. 20) */ 

int [ ] tab1 = new int[20]; 
/* apoi mut elementele vechiului tablou in cel nou: */ 
for(int i=0; i<10; i++) tab1[ i ] = tab[ i ]; 

/* dupa care actualizez referinta tab pt. a indica spre noul tablou */ 
tab = tab1; 
/* acum vechiul tablou se pierde si devine un candidat la stergere */ 

La crearea unui tablou se memoreaza dimensiunile sale, programatorul putand in orice moment sa le cunoasca, prin intermediul atributului length
 

static void oFunctie(int[ ] tabp)  

public static void main(String[ ] arg)  

Din acest exemplu se observa ca dimensiunea unui tablou este cunoscuta oriunde in program, chiar si cand tabloul este transmis ca parametru. 
Putem sa ne imaginam ca un obiect de tip tablou arata asa ca in figura de mai jos: 

Faptul ca pentru fiecare tablou se memoreaza dimensiunea permite ca la executie sa se detecteze orice tentativa de depasire a limitelor tablourilor. Asemenea tentative se sanctioneaza prin emiterea cate unei exceptii IndexOutOfBoundsException
In Java tipul tablou este considerat ca o clasa, iar in aceasta calitate, asa ca oricare alta clasa Java, el mosteneste implicit clasa predefinita Object (mai multe despre acest subiect in modulul cu mostenirea). 

Ceea ce deosebeste un tablou fata de alte obiecte este, in esenta, posibilitatea de a aplica asupra lui operatorul de indexare pentru a-i accesa elementele. Nici o alta clasa din Java nu are definita operatia de indexare.

Daca elementele unui tablou sunt la randul lor de tip clasa, aceasta inseamna ca de fapt elementele respective vor fi referinte la obiecte ale acelei clase. La crearea tabloului se rezerva spatiu DOAR pentru acele referinte si ele sunt initializate cu valoarea null. Daca dorim ca acele referinte sa indice spre obiecte propriu-zise, trebuie fie sa cream obiectele in mod dinamic, asa cum am vazut in modulele precedente, fie sa atribuim elementelor tabloului valoarea altor referinte nenule: 
 

Punct [ ] tab = new Punct[10]; // am creat un tablou de 10 REFERINTE la Punct 

for(int i =0; i< tab.length; i++) 

tab[i] = new Punct( ); // acum creez si OBIECTELE de tip Punct 

Tablouri bidimensionale 

Java accepta si declaratii de tablouri bidimensionale: 

tip_element[ ] [ ] nume_matrice;

Un tablou bidimensional este considerat de fapt ca un tablou de tablouri. Deci, elementele unui tablou bidimensional sunt referinte la tablouri unidimensionale. Rezervarea de spatiu pentru elementele unui tablou bidimensional se poate face in urmatoarele variante: 

  • toate liniile tabloului contin ACELASI numar de elemente: 

nume_matrice = new tip_element[nr_linii][nr_coloane];

  • fiecare linie a tabloului contine un numar DIFERIT de elemente: 

nume_matrice = new tip_element[nr_linii][ ]; 
for(int i = 0; i< nume_matrice.length; i++) 

nume_matrice[i] = new tip_element[i+2]; 


in aceasta secventa s-a creat o matrice in care prima linie are 2 elemente, a 2-a linie 3 elemente, s.a.m.d; in continuare, pentru a prelucra elementele matricii vom proceda ca in secventa: 

for(int i=0; i < nume_matrice.length; i++) 

for(int j=0; j < nume_matrice[i].length; j++) 

// prelucreaza nume_matrice[ i ][ j ] 

In secventele prezentate nume_matrice.length ne da numarul de linii, iar nume_matrice[i].length ne da numarul de elemente de pe linia i

Lucrul cu siruri de caractere: clasa String

Clasa String este una dintre clasele predefinite fundamentale, care modeleaza lucrul cu siruri de caractere. Modul cel mai simplu de manipulare a obiectelor de tip String il constituie constantele String, care sunt siruri de caractere incadradate de ghilimele ("). 
Orice referire a unei asemenea constante in cadrul unui program Java are ca efect crearea unui obiect String care contine secventa de caractere respectiva. Exemplu: 

String s1 = "un sir"; 

Efectul frazei de mai sus este urmatorul : 

Putem crea un obiect String si prin aplicarea unui constructor: 

String s2 = new String(s1); 

In acest exemplu obiectul indicat de s2 va avea acelasi continut ca si cel indicat de s1

Intrebare: ce ar fi trebuit sa facem pentru ca s1 si s2 sa indice unul si acelasi obiect?

Alti constructori ai clasei String mai sunt: 

  • constructorul no-arg care creaza un sir vid: 

String svid = new String( ); 

  • constructorul care ia ca parametru un tablou cu elemente de tip char: 

char[ ] tc = ; 
String stc = new String(tc); 

Sirurile de caractere se pot obtine si prin diverse prelucrari ale unor siruri existente. In acest scop clasa String ofera un set de metode dintre care enumeram: 

String toUpperCase(); 
String toLowerCase(); 
String substring(int); 
String substring(int, int); 

Metodele clasei String includ si operatii care permit consultarea stringurilor, respectiv obtinerea de informatii cu privire la continutul lor: 

int length(); 
char charAt(int); 
int indexOf(char); 
int indexOf(String); 
int lastIndexOf(char); 
int lastIndexOf(String); 

Semnificatia acestor metode, precum si alte informatii legate de clasa String se gasesc in documentatia API si cursantii sunt rugati sa cerceteze aceasta documentatie (a se vedea adresa in rubrica Linkuri). 

In afara de cele enumerate mai sus, clasa String mai este dotata si cu operatii de uz general, existente si in alte clase de altfel, printre care amintim: operatiile de comparare si cele de conversie. Despre acestea vom vorbi in modulul urmator. 

O proprietate foarte importanta a metodelor clasei String este aceea ca ele nu modifica NICIODATA obiectul receptor, ci, atunci cand este necesar, creaza obiecte noi in care este inclus sirul de caractere modificat. Fie spre exemplu urmatoarea secventa: 

String s1 = "un sir"; 
String s2 = s1.toUpperCase(); 

Desi am fi tentati sa credem ca efectul acestei secvente consta in transformarea sirului continut in obiectul indicat de s1, astfel incat toate literele sa devina majuscule, in realitate ceea ce se intampla este ilustrat in figura urmatoare: 

Deci obiectul indicat de s1 si-a pastrat continutul nemodificat. Metoda toUpperCase a creat un obiect NOU in care a plasat sirul cu litere mari si a returnat ca rezultat o referinta la acest nou obiect. 

In concluzie, putem spune ca un obiect String odata creat nu mai poate fi modificat in nici un fel. 
 

Concatenarea stringurilor 

In Java aceasta operatie se realizeaza cu ajutorul operatorului + . Efectul este crearea unui obiect nou, al carui continut va fi sirul rezultat prin concatenare. Exemplu: 

String s1 = "codul "; 
String s2 = "penal"; 
String s3 = s1 + s2; 


Efectul secventei de mai sus este ilustrat in urmatoarea figura: 

Sa rescriem acum exemplul de mai sus sub forma: 

String s1 = "codul "; 
String s2 = s1 + "penal"; 


Efectul acestei secvente este ilustrat mai jos: 

Deosebirea fata de cazul anterior o constituie faptul ca obiectul care contine sirul "penal" nu este indicat de nici o referinta. Acest obiect a fost creat ca urmare a regulii ca orice constanta sir de caractere dintr-un program Java presupune crearea unui obiect nou, dar adresa lui nu a fost memorata explicit in nici o variabila a utilizatorului. Ca atare, dupa evaluarea expresiei de concatenare, obiectul ramane izolat in heap si nu mai poate fi accesat. Asemenea obiecte "orfane" vor fi eliminate din memorie la prima "razie" a colectorului de reziduuri. 

In legatura cu operatia de concatenare a stringurilor, trebuie facute urmatoarele observatii: 
 

Clasa String este singura asupra careia se poate aplica operatorul +. In afara de ea, doar tipurile primitive numerice si tipul char mai au definit acest operator (cu alta semnificatie insa). 

Intr-o expresie de concatenare a stringurilor pot sa apara, pe langa obiecte string, si valori ale tipurilor primitive, acestea fiind automat convertite la string: 

int a = 6; 
String s1 = "tab[" + a + "]="; /* s1 va contine sirul "tab[6]=" */ 
String s2 = s1+8.5; /* s2 va contine sirul "tab[6]=8.5" */ 


Artificiu: o metoda foarte simpla de a converti o valoare primitiva in string este aceea de a o concatena cu sirul vid; de exemplu: 

String s1 = "" + a; 

Intr-o expresie de concatenare putem sa avem ca operanzi si obiecte ale altor clase decat String, acestea fiind convertite la String, dar intr-un mod mai special decat valorile primitive. In capitolul despre conversii de date vom vedea cum. Cu alte cuvinte, operatorul + folosit pentru obiecte are un singur sens: concatenare de siruri. 
 

Stringurile si tablourile de caractere

Desi aparent un string si un tablou de caractere (tipul char[ ] ) modeleaza acelasi lucru, adica o secventa de caractere, cele doua tipuri se comporta diferit. In primul rand, asa cum am vazut, unui obiect String nu i se poate modifica continutul. Unui tablou de caractere i se poate modifica oricare dintre elemente: 

String vs; 
char[ ] tc; 

tc[pos] = 'x'; //corect 
//pentru vs nu avem o posibilitate similara 

Pentru a obtine caracterul aflat la o anumita pozitie pos intr-un String, vs de exemplu, nu putem sa folosim notatia vs[pos] ca în cazul tablourilor, ci trebuie sa utilizam metoda charAt(pos)

String vs; 
char[ ] tc; 

char c1 = tc[pos]; //corect 
char c2 = vs.charAt(pos); //corect 
char c3 = tc.charAt(pos); //eroare 
char c4 = vs[pos]; //eroare 

Atributul length aplicabil pentru tablouri nu are tocmai aceeasi semnificatie cu metoda length( ) de la stringuri, asa dupa cum va rezulta din urmatoarea secventa: 

String vs = "ababu"; 
char[ ] tc = new char[10]; /* am creat un tablou cu 10 elemente de tip char */ 
for(int i=0; i<vs.length(); i++) 

tc[i] = vs.charAt(i); /* am pus in tc aceleasi caractere ca si in vs */ 

System.out.println("Lungimea lui vs = "+vs.length()); 
System.out.println("Lungimea lui tc = "+tc.length);} 

Rezultatul afisat de acest program va fi: 

Lungimea lui vs = 5 
Lungimea lui tc = 10 

Cu alte cuvinte, nu putem sa aflam cate caractere semnificative se afla intr-un tablou de caractere, decat daca parcurgem tabloul element cu element si le testam. 

Intre tipurile String si char[ ] exista totusi o legatura, in sensul ca limbajul Java ofera metode de conversie prin care putem sa tranformam un String în tablou de caractere si viceversa: 
 

String vs="blabla"; 
char[ ] tc = vs.toCharArray(); /* crearea unui tablou dintr-un String */ 
char[ ] atc = new char[5]; 
for(int i=0; i < atc.length; i++)> atc[i] = '0'+i; /* completez elementele tabloului cu caracterele '0','1','2',... */ 
String avs = new String(atc, 0, atc.length); /* crearea unui String pe baza unui tablou */ 

Metoda toCharArray creaza un tablou ale carui elemente sunt caracterele stringului sursa. 
Operatia inversa se realizeaza cu ajutorul unui constructor al clasei String. Acesta în principiu poate initializa un string preluand un subsir din tabloul de caractere. Parametrii necesari sunt: referinta tabloului, indicele primului caracter din subsirul dorit si lungimea acestui subsir. In exemplul nostru subsirul cuprinde tot tabloul. 

Aplicatii


/**
* Aplicatia afiseaza tripletele Pitagora in care numerele sunt <= o valoare maxima
* Valorile in triplete vor fi in ordine crescatoare
* Se face observatia ca nu exista triplete cu doua valori egale
*/

// Modificati programul astfel incat valoarea maxima sa se citeasca si
// la sfarsit sa se afiseze numarul de solutii

import javax.swing.JOptionPane;


public class NumerePitagora // if contor
} // if relatie

// iesire for-uri incuibate

if (contor != 0 ) // mai sunt solutii netiparite
JOptionPane.showMessageDialog(null,mesaj,
"Numere Pitagora",JOptionPane.INFORMATION_MESSAGE);

System.exit(0);

}

}

Alte informatii

  • Control Flow Statements
  • Cleaning Up Unused Objects
  • Characters and Strings

Tema

1. Rulati aplicatiile din modul. Vizitati resursele propuse la Alte informatii..

Observatii, comentarii sunt binevenite in Conferinta Software Consulting. 

2. Sa se scrie un program care: primeste ca argument la lansarea in executie un sir reprezentand un numar intreg si converteste numarul respectiv la tipul int. 

Pentru conversie se parcurge sirul cifra cu cifra, de la stanga la dreapta aplicand formula de sintetizare a unui numar in baza 10: 

valoare = valoare * 10 + cifra_curenta
, unde valoare este initial 0. 

3. Sa se scrie un program care genereaza primele N numere Fibonacci, memorandu-le intr-un tablou. Elementele tabloului se vor afisa apoi unul sub altul, iar valorile pare vor fi marcate in dreapta cu cate un asterisc. Valoarea N va fi furnizata ca argument la lansarea in executie a programului si pentru conversia ei la tipul int se va folosi metoda de la punctul anterior. ( Varianta: se citeste N folosind ferestre de dialog si se converteste folosind metoda parseInt ).

Indicatie: sirul Fibonacci are ca prime 2 elemente valorile 1 si 1; apoi, fiecare element este suma precedentelor doua. Astfel, elementele al 3-lea, al 4-lea si al 5-lea sunt: 2 , 3 si 5. 

4. Se cere sa se testeze practic programul dat in paragraful Garbage Collector pentru diferite valori ale lui nr_ob. 

5. Sa se scrie un program care, primind ca argumente un sir de caractere si un caracter, va afisa numarul de pozitii aflate intre prima si respectiv ultima aparitie in cadrul sirului a caracterului dat ca parametru. De exemplu, la lansarea in executie cu 

comanda: java nume_program pompieristicomilitienesc m

rezultatul afisat va trebui sa fie: 10 pozitii. 

6. Se citesc numere intregi, pana la tastarea lui 0, care nu se prelucreaza.

Pentru fiecare numar sa se tipareasca:

  • cifrele sale, separate prin cate doua blancuri
  • mesajul Este palindrom sau Nu este palindrom; un numar palindrom este identic cu inversul sau.

Exemplu:

Pentru numarul 1231 se va tipari:

1 2 3 1
Nu este palindrom

Pentru numarul 75657 se va tipari:

7 5 6 5 7
Este palindrom


Document Info


Accesari: 2919
Apreciat: hand-up

Comenteaza documentul:

Nu esti inregistrat
Trebuie sa fii utilizator inregistrat pentru a putea comenta


Creaza cont nou

A fost util?

Daca documentul a fost util si crezi ca merita
sa adaugi un link catre el la tine in site


in pagina web a site-ului tau.




eCoduri.com - coduri postale, contabile, CAEN sau bancare

Politica de confidentialitate | Termenii si conditii de utilizare




Copyright © Contact (SCRIGROUP Int. 2024 )