This is an old revision of the document!
* Responsabil laborator: Mihai Nan * Profesor titular: Carmen Odubasteanu
Presupunem ca dorim sa descriem, uzitand un limbaj de programare, un obiect carte. In general, o carte poate fi caracterizata prin titlu, autor si editura. Cum am putea realiza aceasta descriere formala?
Daca descriem acest obiect, tip abstract de date, intr-un limbaj de programare structural, spre exemplu limbajul C, atunci vom crea, ca mai jos, o structura Carte impreuna cu o serie de functii cuplate de aceasta structura. Cuplajul este realizat prin faptul ca orice functie care opereaza asupra unei carti contine in lista sa de parametri o variabila de tip Carte.
typedef struct carte { char *titlu, *autor; int nr_pagini; }*Carte; void initializare(Carte this, char* titlu, char* autor, int nr_pagini) { this->titlu = strdup(titlu); this->autor = strdup(autor); this->nr_pagini = nr_pagini; } void afisare(Carte this) { printf("%s, %s - %d\n", this->autor, this->titlu, this->nr_pagini); }
Daca modelam acest obiect intr-un limbaj orientat pe obiecte (in acest caz, Java), atunci vom crea o clasa Carte ca mai jos.
Se poate observa cu usurinta, in cadrul exemplului de mai jos, ca atat datele cat si metodele (functiile) care opereaza asupra acestora se gasesc in interiorul aceleiasi entitati, numita clasa. Evident, in codul din exemplu sunt folosite concepte care nu au fost inca explicate, dar cunoasterea si intelegerea reprezinta scopul principal al acestui laborator.
class Carte { String nume, autor; int nr_pagini; public Carte(String nume, String autor, int nr_pagini) { this.nume = nume; this.autor = autor; this.nr_pagini = nr_pagini; } public Carte() { this("Enigma Otiliei", "George Calinescu", 423); } public String toString() { return this.autor + ", " + this.nume + " - " + this.nr_pagini; } public static void main(String args[]) { Carte carte; carte = new Carte("Poezii", "Mihai Eminescu", 256); System.out.println(carte); } }
* Atunci cand un producator creaza un produs, mai intai acesta specifica toate caracteristicile produsului intr-un document de specificatii, iar pe baza acelui document se creaza fizic produsul. De exemplu, calculatorul este un produs creat pe baza unui astfel de document de specificatii. La fel stau lucrurile si intr-un program orientat pe obiecte: mai intai se creaza clasa obiectului (documentul de specificatii) care inglobeaza toate caracteristicile unui obiect (instanta a clasei), dupa care, pe baza acesteia, se creaza (instantiaza) obiectul in memorie.
* In general, putem spune ca o clasa furnizeaza un sablon ce spcifica datele si operatiile ce apartin obiectelor create pe baza sablonului - in documentul de specificatii pentru un calculator se mentioneaza ca acesta are un monitor si o serie de periferice.
* Din cele de mai sus deducem ca o clasa descrie un obiect, in general, un nou tip de data. Intr-o clasa gasim date si metode ce opereaza asupra datelor respective.
* Pentru a defini o clasa, trebuie folosit cuvantul cheie class urmat de numele clasei.
* Datele “nume”, “autor”, “nr_pagini” definite in clasa Carte se numesc atribute, date-membru, variabile-membru sau campuri, iar operatiile toString si main se numesc metode.
* Fiecare clasa are un set de constructori care se ocupa cu instantierea (initializarea) obiectelor nou create. De exemplu, clasa Carte are doi constructori: unul cu trei parametri si unul fara parametri care il apeleaza pe cel cu trei parametri.
* Spuneam mai sus ca un obiect reprezinta o instanta a unei clasa. In Java, instantierea sau crearea unui obiect se face dinamic, folosind cuvantul cheie new si are ca efect crearea efectiva a obiectului cu alocarea spatiului de memorie corespunzator.
* Asa cum fiecare calculator construit pe baza documentului de specificatii are propriile componente, fiecare obiect de tip Calculator are propriile sale atribute.
* Initializarea se realizeaza prin intermediul constructorilor clasei respective. Initializarea este, de fapt, parte integranta a procesului de instantiere, in sensul ca imediat dupa alocarea memoriei ca efect al operatorului new este apelat constructorul specificat. Parantezele rotunde dupa numele clasei indica faptul ca acolo este de fapt un apel la unul din constructorii clasei si nu simpla specificare a numelui clasei.
* In Java, este posibila si crearea unor obiecte anonime, care servesc doar pentru initializarea altor obiecte, caz in care etapa de declarare a referintei obiectului nu mai este prezenta.
class Point { int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class Dimension { int width, height; public Dimension(int width, int height) { this.width = width; this.height = height; } } class Rectangle { Point p; Dimension d; public Rectangle(Point p, Dimension d) { this.p = p; this.d = d; } public static void main(String args[]) { Rectangle patrat = new Rectangle(new Point(0, 0), new Dimension(10, 10)); } }
* In sectiunea anterioara, am vazut cum se defineste o clasa si cum se creaza un obiect. In aceasta sectiune vom vedea cum putem executa operatiile furnizate de obiecte. Pentru a putea avea acces la operatiile furnizate de catre un obiect, trebuie sa detinem o referinta spre acel obiect. * 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. * Declarea unei referinte numite carte spre un obiect de tip black|Carte se face in felul urmator: blue|Carte carte;
* Dupa cum am observat in exemplul oferit in prima sectiune, apelul metodei toString nu este toString(carte), ci carte.toString() intrucat metoda toString apartine obiectului referit de carte - se apeleaza metoda toString pentru obiectul referit de variabila carte din fata lui. * Pentru o intelegere mai buna a conceptului de referinta a unui obiect, consideram exemplul de mai jos in care cream doua obiect de tip Carte precum si trei referinte spre acest tip de obiecte. * Fiecare dintre obiectele Carte are alocata o zona proprie de memorie, in care sunt stocate valorile campurilor “nume”, “autor”, “nr_pagini”. Ultima referinta definita in exemplul de mai jos, “c3”, va referi si ea exact acelasi obiect ca si “c2”, adica al doilea obiect creat.
class Test { public static void main(String args[]) { Carte c1 = new Carte("Poezii", "Mihai Eminescu", 326); Carte c2 = new Carte("Camil Petrescu", "George Calinescu", 426); Carte c3 = c2; c3.autor = "George Calinescu"; System.out.println(c1); System.out.println(c2); System.out.println(c3); } }
* Atribuirea “c3 = c2” nu a facut altceva decat sa ataseze referintei “c3” obiectul avand aceeasi identitate ca si cel referit de “c2”, adica obiectul secund creat.
Clasele, asa cum am vazut deja, sunt definite folosind cuvantul cheie class. In urmatoarele sectiuni, vom vorbi despre diferite categorii de membri care pot apare in interiorul unei clase.
* In multe cazuri, atunci cand instantiem un obiect, ar fi folositor ca obiectul sa aiba anumite atribute initializate. * Initializarea atributelor unui obiect se poate face in mod automat, la crearea obiectului, prin intermediul unui constructor. Principalele caracteristici ale unui constructor sunt: - un constructor are acelasi nume ca si clasa in care este declarat; - un constructor nu are tip returnat; - un constructor se apeleaza automat la crearea unui obiect; - un constructor se executa la crearea obiectului si numai atunci.
Atunci cand definim o clasa, specificam felul in care obiectele de tipul acelei clase arata si se comporta. Dar pana la crearea efectiva a unui obiect folosind new nu se aloca nicio zona de memorie pentru atributele definite in cadrul clasei, iar la crearea unui obiect se aloca acestuia memoria necesara pentru fiecare atribut existent in clasa instantiata. Tot pana la crearea efectiva a unui obiect nu putem beneficia de serviciile definite in cadrul unei clase. Ei bine, exista si o exceptie de la regula prezentata anterior - membrii statici (atribute si metode) ai unei clase. Acesti membri ai unei clase pot fi folositi direct prin intermediul numelui clasei, fara a detine instante a respectivei clase.
In Java, se pot gasi doua sau mai multe metode, in cadrul aceleiasi clase, care sa aiba acelasi nume, atata timp cat parametrii lor sunt diferiti. In acest caz, se spune ca metoda este supraincarcata, iar procedeul se numeste supraincarcarea metodelor. Pentru o mai buna intelegere a acestui principiu POO, se va oferi, in continuare, un exemplu pentru o metoda care determina maximul.
class Test { public int maxim(int a, int b) { if(a > b) return a; else return b; } public int maxim(String s1, String s2) { if(s1.compareTo(s2) < 0) return 2; else return 1; } public int maxim(int a, int b, int c) { if(maxim(a, b) < c) return c; else return maxim(a, b); } }
Un alt exemplu elocvent, pentru acest prinicpiu POO, este operatorul ”+” care executa operatii diferite in cotexte diferite.