Administratie | Alimentatie | Arta cultura | Asistenta sociala | Astronomie |
Biologie | Chimie | Comunicare | Constructii | Cosmetica |
Desen | Diverse | Drept | Economie | Engleza |
Filozofie | Fizica | Franceza | Geografie | Germana |
Informatica | Istorie | Latina | Management | Marketing |
Matematica | Mecanica | Medicina | Pedagogie | Psihologie |
Romana | Stiinte politice | Transporturi | Turism |
Java, ca limbaj OO
Clasele reprezinta elementele de baza ale unei aplicatii in limbajul Java. O clasa este un model de obiect. Ea contine campurile interne si metodele prin care se poate interactiona cu obiectul. Declararea unei clase este similara cu declararea unui nou tip de date, adica se descrie numai cum 'arata ' si ce 'stie' sa faca un obiect de tipul (clasa) respectiva, fara sa se creeze un astfel de obiect. Limbajul Java permite supraincarcarea (overloading) metodelor. Adica, se pot defini mai multe metode cu acelasi nume, care au acelasi tip de rezultat dar care difera prin signatura (numarul si tipul parametrilor). Existenta acestui mecanism este utila pentru a defini comportari diferite la primirea aceluiasi mesaj (numele metodei) in functie de modul (contextul) de apelare.
Deoarece limbajul Java a fost definit pornind de la C++, majoritatea conceptelor legate de obiecte si clase sunt similare cu cele din C++. Se regasesc notiunile de: incapsulare, supraincarcare pentru metode, mostenire si legare dinamica. Cu exceptia valorilor definite cu tipuri primitive, orice altceva este considerat in limbajul Java obiect. Ca si in alte limbaje orientate obiect, toate clasele formeaza o ierarhie. Radacina acestei ierarhii se numeste Object. Limbajul Java este 'strong typed', adica orice constructie are un tip stabilit la momentul compilarii, din acest motiv nu exista tipuri implicite pentru metode si nici numar variabil de argumente pentru apelul metodelor.
Daca asocierea apelului unei metode cu metoda respectiva se face in faza de executie (cu alte cuvinte in codul generat de catre compilator este continut numele metodei si la momentul executiei se cauta in interfata respectiva o metoda care sa se 'potriveasca'), se spune ca limbajul are o legare dinamica, altfel legarea este statica. In limbajul Java legarea este dinamica, ceea ce permite substituirea unui obiect cu alt obiect in timpul executiei programului, cu conditia ca cele doua obiecte sa aiba aceeasi interfata.
O clasa poate sa fie extinsa prin redefinirea unor metode si / sau adaugarea unor metode si campuri noi. Tot ce nu este redefinit este mostenit. O metoda care inlocuieste o metoda din supraclasa are acelasi nume, acelasi tip de rezultat (sau o subclasa a acestuia) si aceeasi lista de parametrii. In caz contrar, va fi vorba de o noua metoda.
Nu pot sa fie inlocuite metodele sau campurile care sunt declarate cu atributul final. Introducerea conceptului de camp sau metoda finala a fost necesar din considerente de securitate, in acest mod programatorul poate sa interzica redefinirea anumitor informatii sau metode considerate critice. De asemenea, avand in vedere ca legarea metodelor este dinamica, utilizarea unor metode finale permite compilatorului sa faca o serie de optimizari care altfel nu ar fi posibile.
Clasa care este extinsa se numeste supraclasa. Clasa obtinuta prin extindere se numeste subclasa. Limbajul Java permite doar mostenire simpla: fiecare clasa are o singura supraclasa, toate clasele avand un 'stramos' comun: clasa Object. Pentru situatiile in care in limbajul C++ s-ar fi utilizat mostenire compusa, in limbajul Java se utilizeaza interfete si mostenirea simpla.
Utilizand definitia clasei, se pot face instantieri de obiecte avand comportarea descrisa de catre clasa. Practic, clasa reprezinta un tipar dupa care se creeaza un obiect, prin instantiere. La creare se face alocarea de memorie pentru campuri si se executa prelucrarile indicate de constructorul clasei. Un constructor este o metoda care are acelasi nume cu clasa respectiva si este apelata implicit la crearea obiectului. Singurul context in care un constructor poate sa fie referit (apelat ca metoda) este din alt constructor si numai in anumite conditii speciale. Constructorii nu au rezultate si, ca urmare, nu se specifica tipul rezultatului in definirea lor.
Radacina ierarhiei de clase in limbajul Java, clasa Object contine o serie de metode finale care vor fi mostenite de orice clasa definita in Java. Unele dintre metodele clasei Object nu sunt scrise in Java, deoarece implementarea lor depinde de masina pe care se face executia, despre aceste metode se spune ca sunt native. In momentul in care o clasa este necesara (pentru ca se instantiaza un obiect de tipul respectiv sau pentru ca este apelata o metoda statica din clasa respectiva) aceasta se va incarca in masina virtuala. Incarcarea este realizata de catre un obiect numit 'class loader'. Definitia clasei Object este continuta in biblioteca java.lang si va fi incarcata de pe masina pe care se executa programul care o utilizeaza. In general, nu se pune problema aducerii unor clase din biblioteca java.lang din Internet. Din doua motive - in primul rand clasele din aceasta biblioteca contin metode native (scrise special pentru tipul de masina-sistem de operare pe care se executa) iar in al doilea rand din motive de securitate. Prin construirea unui incarcator de clase (classloader) nou, programatorul poate sa evite aceasta restrictie, problemele de securitate vor fi in continuare evitate pentru ca masina virtuala 'stie' ca incarcarea claselor respective s-a facut din retea.
Sa consideram de exemplu definitia unei clase care descrie o pisica:
class Pisica extends Object
Deoarece noua clasa extinde direct clasa Object se poate scrie si sub forma:
class Pisica
In absenta clauzei extend,s extinderea clasei Object este implicita.
In clasa Pisica au fost declarate variabilele nume si numarSoareci. Prima variabila este de tip referinta la un obiect de tip String (in limbajul Java sirurile de caractere sunt obiecte) iar a doua este de tipul elementar int. Daca nu se prevede altceva, (prin intermediul unui constructor, de exemplu) initializarea celor doua variabile se face cu null (o valoare care indica faptul ca variabila respectiva nu indica spre un obiect) si respectiv zero. Nu se considera insa ca o practica buna de programare bazarea codului programului pe astfel de valori initiale.
Deoarece nu a fost prevazut nici un mecanism pentru a face acces la aceste variabile prin intermediul unor metode, deocamdata variabilele au fost declarate cu atributul public (acest tip de atribut, numit si modificator de acces, precizeaza domeniul de valabilitate al variabilelor respective). In mod corespunzator, variabilele nume si numarSoareci sunt accesibile din orice metoda, din orice clasa.
Se poate declara acum o variabila in care se poate obtine o referinta la un obiect de tip Pisica:
Pisica pisica;
Ca efect al acestei declaratii, se creeaza o variabila care poate sa contina o referinta la un obiect de tip Pisica, fara sa se creeze si un obiect instanta pentru clasa Pisica. In urma declararii, variabila este initializata la null. Pentru a crea un obiect se va executa instructiunea:
pisica = new Pisica();
Instructiunea precedenta creeaza un obiect care reprezinta o instantiere a clasei Pisica. Adresa (referinta) la obiectul respectiv o sa fie memorata in variabila pisica. In continuare, variabila pisica poate sa fie utilizata pentru referirea campurilor sau metodelor corespunzatoare obiectului creat. Astfel, pisica.numarSoareci reprezinta campul respectiv din obiectul creat. Instantierea obiectului se poate face si o data cu declararea variabilei:
Pisica pisica = new Pisica();
Constructori
La crearea obiectului indicat de variabila pisica, s-a considerat ca nu sunt necesare prelucrari speciale, in mod corespunzator se considera ca se executa implicit numai un constructor al superclasei (in cazul nostru se creeaza un obiect de tip Object) si se adauga campurile curente (nume si numarSoareci) la obiectul astfel construit.
Daca la instantierea obiectului sunt necesare prelucrari specifice clasei curente (nu superclasei), trebuie sa se defineasca un constructor. Un constructor este o metoda speciala care este apelata la crearea obiectului. Metoda are acelasi nume ca si clasa careia ii apartine, si nu intoarce nici un rezultat. Un constructor nu poate sa fie apelat din alte metode, cu exceptia altor constructori (dar utilizand notatii speciale). Prima instructiune dintr-un constructor este in mod implicit (in sensul ca nu este necesar sa specificata) un apel al constructorului fara argumente al superclasei. Singura abatere de la aceasta regula are loc atunci cand prima instructiune din constructor este un apel explicit la alt constructor al clasei sau la un constructor al superclasei. In acest mod, la construirea (instantierea) unui obiect se incepe intotdeauna, de fapt, cu construirea unui obiect de tip Object care este apoi extins conform ierarhiei de clase. Daca intr-o clasa nu se defineste nici un constructor (ca in prima declarare a clasei Pisica), se considera ca exista un constructor fara argumente (numit si constructor implicit) al carui corp contine numai un apel al constructorului, implicit al superclasei. Daca intr-o clasa se defineste cel putin un constructor, constructorul fara argumente nu mai este furnizat automat. Constructorii nu pot avea modificatori de acces ca abstract, native, static, synchronized sau final.
In Exemplul 1 fost adaugati constructori pentru clasa Pisica.
Exemplul 1
class Pisica
Pisica()
}
Au fost definiti doi constructori, care difera prin numarul de argumente. In functie de apel, se va utiliza un constructor sau altul. Astfel, daca se executa o instructiune ca: Pisica pisica = new Pisica('Miti');
se va utiliza primul constructor care creeaza un obiect de tip Pisica cu numele 'Miti'.
Daca se executa instructiunea: Pisica pisica = new Pisica(); atunci se va utiliza al doilea constructor. Cel de al doilea constructor nu initializeaza nici un camp. Daca cel de-al doilea constructor nu ar fi fost specificat, atunci pentru clasa Pisica nu ar exista un constructor fara argumente (deoarece exista deja un constructor definit). Selectia constructorilor se face in faza de compilare pe baza signaturii apelului.
Pentru a referi campurile obiectului, in cadrul metodelor dintr-o clasa se poate utiliza referinta generica this, care desemneaza obiectul curent. Astfel, pentru a referi campul nume in corpul unei metode se poate folosi numele nume sau notatia this.nume (atunci cand datorita definirii unei variabile locale sau a unui parametru cu acelasi nume domeniul de valabilitate al campului respectiv este intrerupt). Dintr-un obiect se pot referi variabile din supraclasa, utilizand notatia super.nume sau referind explicit numele supraclasei (in cazul variabilelor statice)
Utilizarea notatiei bazate pe this este necesara si in alte contexte, de exemplu daca este necesara transmiterea obiectului curent ca argument in invocarea unei metode (eventual) pentru un alt obiect.
Intr-un constructor se poate referi ca prima instructiune un constructor al clasei curente sau un constructor al clasei pe care clasa curenta o extinde. Este singura pozitie din definitia unui constructor in care poate sa apara o referire la un alt constructor. Un astfel de apel apare sub forma
this(listaDeArgumente); // constructor din aceeasi clasa
Unde ListaDeArgumente reprezinta lista valorilor parametrilor constructorului invocat. In instructiunea:
super(ListaDeArgumente); // constructor din super clasa
este apelat constructorul superclasei.
De exemplu, al doilea constructor din exemplul clasei Pisica poate sa fie redefinit sub forma
Pisica(
daca se doreste stabilirea numelui implicit 'Pety' pentru orice obiect de tip Pisica. A fost posibila definirea a doi constructori pentru clasa Pisica, deoarece limbajul Java permite supraincarcarea metodelor, adica existenta mai multor metode cu acelasi nume dar cu signaturi diferite.
Modificatori de acces
Pentru clasa Pisica cele doua variabile componente au fost declarate publice, cu alte cuvinte ele pot sa fie modificate explicit. Dar, astfel de prelucrari nu sunt in spiritul programarii orientate obiect. Ideea ar fi ca variabilele continute in obiecte trebuie sa fie ascunse si modificarea lor sa se poata face numai prin intermediul metodelor definite odata cu clasa respectiva. Sa consideram o alta declaratie pentru clasa Pisica.
Exemplul 2. Declaratie clasa Pisica
class Pisica extends Object
Pisica()
void aMancat(int n)
int catiSoareci()
String numePisica()
}
Variabilele nume si numarSoareci nu mai sunt accesibile din exterior decat prin intermediul metodelor de acces. Atributele publice si private descriu nivelul de acces la variabilele respective, ele nu sunt singurele posibile.
Daca pentru o clasa se defineste cel putin un constructor (pentru a nu fi considerat un constructor implicit) si toti constructorii sunt declarati cu atributul private, atunci nu se pot crea instante ale clasei din afara sa. O astfel de situatie poate sa fie interesanta daca, de exemplu, o clasa este definita pentru a declara o serie de variabile globale (campuri cu atributul static) sau daca sunt necesare metode statice care sa fie utilizate similar unor functii C (fara sa se faca o instantiere). Exemplul tipic este clasa System pentru care este definit un singur constructor care are atributul private .
Aceasta clasa contine campuri statice cum sunt in sau out care sunt utilizate fara sa existe o instantiere a clasei System. In Exemplul 3 este prezentat modul in care se poate, totusi, obtine o instantiere a unui obiect dintr-o clasa pentru care s-a declarat numai un constructor cu atributul private. Instantierea obiectului se face in metoda creare() a clasei testO. O incercare de instantiere directa a unui obiect ar produce o eroare in faza de compilare. Apelul metodei statice creare() are ca rezultat o referinta la un obiect de tipul testO. Pentru acest obiect, se pot invoca metode nestatice (prelucrare()).
Exemplul 3.
Instantierea unui obiect care are numai constructori definiti cu atributul private.
class testO
void prelucrare()
static testO creare()
}
class utilizareTestO
}
In Exemplul 3, variabilele au fost declarate cu atributul private. Asta inseamna ca variabilele respective nu pot sa fie referite decat din metodele clasei respective.
Distrugerea obiectelor
Daca la crearea unui obiect se pot executa initializari, se pune problema cum se pot asocia operatii cu distrugerea unui obiect. Astfel de operatii sunt necesare, de exemplu pentru a inchide fisiere sau conexiuni care au fost deschise in timpul executiei obiectului care se distruge.
Mediul Java utilizeaza tehnica numita garbage collection (colectarea spatiului disponibil) pentru a elibera memoria ramasa ocupata de obiectele care nu mai sunt referite la un moment dat. Inainte de a se sterge un obiect din memorie, este apelata metoda finalize():
protected void finalize() throws throwable
Se garanteaza ca metoda finalize() va fi activata inainte de eliberarea memoriei ocupate de obiect, dar nu se poate stii cand (sau daca vreodata) va fi apelata in raport cu executia altor metode.
In metoda finalize() pot sa apara instructiuni care sa reactiveze obiectul respectiv (adica sa il faca din nou accesibil). In acest caz, memoria ocupata de obiect nu va mai fi eliberata.
La crearea unui obiect se activeaza o ierarhie de constructori, incepand cu constructorul pentru clasa Object. Si la distrugerea unui obiect se poate pune problema activarii unui lant de metode finalize(). Aceste apeluri trebuie sa se execute in ordine inversa fata de constructori. Adica metoda finalize() se poate incheia cu un apel al metodei finalize() pentru supraclasa:
protected void finalize() throws Throwable
Dupa cum s-a mentionat, apelul metodei finalize() este legat de colectarea memoriei disponibile. Este posibil sa se ajunga la terminarea executiei unei aplicatii fara sa se activeze algoritmul de colectare a memoriei disponibile. Din acest motiv, daca este necesara executia anumitor operatii in momentul in care un obiect nu mai este util, trebuie sa se prevada aceste operatii intr-o metoda care va fi apelata explicit, pentru ca nu exista garantia ca metoda finalize() va fi apelata.
Conversii de tip
Putem sa continuam cu extinderea ierarhiei, pornind de la clasa care descrie o pisica. In Exemplul 4 se defineste o clasa care descrie o pisica de rasa:
Exemplul 4. Definitia clasei PisicaDeRasa.
class PisicaDeRasa extends Pisica
String ceCuloare()
}
Clasa PisicadeRasa contine un camp nou: culoare.
Intre supraclase si subclase se pot realiza conversii cu ajutorul notatiilor de tip 'cast'. Sa consideram ca in clasele Pisica si respectiv PisicaDeRasa sunt definite metodele:
void xx()
si respectiv:
void xx()
Se observa ca metoda xx() din clasa Pisica a fost redefinita in clasa PisicaDeRasa.
In Exemplul 5 se prezinta modul cum se pot face conversii intre variabile de tip Pisica si respectiv PisicaDeCasa.
Exemplul 5. Utilizare cast intre obiecte de tip Pisica si PisicaDeCasa.
class testPisica
}
Se definesc variabilele p si pr care sunt initializate cu referinte la obiecte de tip Pisica si respectiv PisicaDeCasa. In instructiunea: p = pr; nu este nevoie sa se mai faca nici precizare, pentru ca Pisica este supraclasa clasei PisicaDeCasa si deci compilatorul 'stie' cum sa lucreze cu obiectul respectiv. La conversia inversa:
pr = (PisicaDeRasa)p; compilatorul trebuie sa fie informat asupra modului in care trebuie tratata referinta. De notat ca atribuirea pr = (PisicaDeRasa)p va functiona numai in cazul in care in momentul executiei variabila referinta p refera un obiect de tip PisicaDeCasa. Daca refera un obiect apartinand unei alte subclase pentru tipul Pisica atribuirea va fi sanctionata cu o exceptie de conversie de tip. Evident, astfel de erori nu pot fi detectate in timpul compilarii.
Ca rezultat al executiei acestui program se va afisa textul din Rezultat 1.
Rezultat 1
Pisica obisnuita
Pisica de rasa
Pisica de rasa
Pisica de rasa
Se observa ca dupa ce p a primit ca valoare o referinta la un obiect care este dintr-o subclasa, daca exista o metoda care este definita atat in subclasa cat si in clasa, atunci se va executa metoda din subclasa. Daca exista o metoda care nu exista decat in subclasa, ea nu va putea sa fie accesibila prin intermediul referintei memorata intr-o variabila definita ca o referinta la clasa. Explicatia consta in faptul ca limbajul Java realizeaza legarea dinamica a claselor. Corespunzator, pentru o clasa, in faza de executie se pastreaza signaturile metodelor, identificarea codului care trebuie sa se execute pentru o metoda referita se realizeaza pe baza numelui si parametrilor. Rezulta ca un nume care este definit numai in subclasa nu este 'vizibil' pentru supraclasa.
Utilizarea mecanismului de cast se poate face numai intre o supraclasa si subclasele sale (sau subclasele subclaselor sale, etc.). Daca prin extinderea clasei Pisica se obtine si o noua clasa PisicaNoua, executia urmatoarei secvente de instructiuni:
PisicaDeRasa pr;
Pisica p = new Pisica();
PisicaNoua pn = new PisicaNoua();
p = pn;
pr = (PisicaDeRasa)p;
va produce la executie o eroare:
java.lang.ClassCastException: PisicaNoua
deoarece nu exista nici o relatie intre PisicaNoua si PisicaDeRasa.
Pentru a verifica corectitudinea cast-ului, se recomanda verificarea apartenentei obiectului la clasa respectiva cu ajutorul operatorului instanceof. Acest operator poate sa fie utilizat ca in Exemplul 6.
Exemplul 6. Utilizare operator instanceof.
class ExempluInstanceOf
}
Ca rezultat se va afisa 'nu da ', adica p indica spre un obiect care nu este o instanta a clasei PisicaNoua. In schimb, pn care indica spre un obiect care este o instanta a clasei PisicaNoua, este in acelasi timp o instanta a claseiPisica (PisicaNoua este o extensie a clasei Pisica).
Redefinirea metodelor
Daca intr-o clasa se defineste o metoda care are acelasi nume si aceeasi signatura cu o metoda din supra clasa, ambele metode pot sa fie referite dintr-o metoda din clasa. Sa consideram urmatoarea definitie pentru clasa PisicaNoua:
class PisicaNoua extends Pisica
void yy()
}
Daca se executa instructiunile:
PisicaNoua pn = new PisicaNoua();
pn.yy();
Se va obtine:
Pisica noua
Pisica obisnuita
Adica, apelarea in subclasa a unei metode care inlocuieste o metoda din supra clasa se face utilizand notatia super.metoda() (cu metoda() numele metodei respective).
Variabile si metode statice
Pornind de la o definitie de clasa, se pot obtine instantieri pentru mai multe obiecte. Fiecare dintre aceste obiecte contine cate un exemplar din fiecare variabila, pe baza carora a fost definita clasa. Ce se intampla daca sunt necesare variabile globale clasei, adica variabile a caror valoare comuna sa poata fie referite de catre orice obiect din clasa respectiva ? Limbajul permite pentru astfel de cazuri declararea unor variabile statice. O astfel de variabila este locala unei intregi clase de obiecte, adica ea este creata o singura data la incarcarea clasei respective. Si o metoda poate sa fie declarata cu atributul static. In acest caz, metoda este si finala in mod implicit, in sensul ca nu poate sa fie modificata. O astfel de metoda este considerata ca apartine clasei si nu instantierilor clasei. O metoda statica poate sa refere numai variabile sau metode statice (pentru ca numai acestea exista fara sa se fi instantiat un obiect din clasa respectiva), dar poate sa fie apelata din orice metoda a clasei.
Sa consideram urmatoarea secventa de cod:
class Aplicatie
A fost declarata o clasa Aplicatie. Orice obiect instantiat din clasa Aplicatie va avea acces la variabila globala versiune. Valoarea variabilei versiune (definita cu atributul final) nu poate sa fie modificata. In schimb, variabila numarObiecte poate sa fie actualizata de fiecare obiect instantiat, astfel incat sa poata, de exemplu, sa fie utilizata pentru a actualiza numarul de obiecte instantiate din clasa Aplicatie.
Revenind la clasa System, despre care am mentionat faptul ca nu poate fi instantiata (deoarece constructorul ei este declarat cu atributul private) se poate pune problema cum si mai ales cand s-ar putea face o initializare a variabilelor continute in aceasta clasa, avand in vedere ca nu se va executa un constructor al clasei. Limbajul permite, pentru astfel de situatii, declararea unei secvente de cod ca fiind statica in modul urmator:
static
Astfel de secvente se pot declara numai in afara metodelor. Corespunzator, in momentul in care clasa respectiva ajunge sa fie incarcata (pentru ca ceva in legatura cu ea a fost referit) se vor executa toate secventele de cod declarate in acest mod la nivelul clasei. Sa consideram, de exemplu, ca in definitia clasei PisicaNoua apare codul:
class PisicaNoua extends Pisica
Daca se executa aplicatia:
class testPisicaAltNou
Se va afisa textul: ' Executat la referirea clasei' care a fost executat la incarcarea clasei PisicaNoua pentru instantierea obiectului referit de catre pn.
Variabilele si metodele statice pot sa fie referite utilizand numele clasei (se considera ca ele sunt asociate clasei si nu obiectelor ca in cazul campurilor 'obisnuite'). Metodele cu atributul static sunt de fapt similare functiilor 'obisnuite' din limbajul C. Ele pot sa fie apelate fara a instantia un obiect de tipul respectiv. Utilizarea lor se poate face pe baza numelui clasei si a metodei respective.
Clase abstracte si interfete in limbajul Java
Una dintre deciziile de proiectare care diferentiaza limbajul Java de limbajul C++ este renuntarea la mostenirea multipla. Exista mai multe explicatii referitoare la motivul pentru care s-a luat aceasta decizie. De exemplu, procesul de compilare in cazul mostenirii multiple este mult mai complicat decat in cazul mostenirii simple. De asemenea, intelegerea unui program in prezenta mostenirii multiple este mult mai dificila decat in absenta ei.
In limbajul Java au fost introduse notiunile de clasa abstracta si interfata. In ambele cazuri sunt precizate modele de functionare care urmeaza sa fie implementate. O clasa abstracta este o superclasa pentru o ierarhie de clase. Ea contine campuri si metode 'normale' dar si modele de metode care urmeaza sa fie implementate in mod obligatoriu de clasele care extind clasa abstracta. O interfata este numai o colectie de signaturi (modele de metode) care urmeaza sa fie implementate. Utilizand interfetele se pot realiza constructii care se aseamana cu mostenirea multipla.
Clase abstracte
Dupa cum s-a mai mentionat, limbajul Java opereaza cu o ierarhie de clase, orice clasa este o subclasa pentru alta clasa (cu exceptia radacinii Object). Limbajul permite definirea unor clase abstracte pornind de la care se pot crea noi ierarhii. O astfel de ierarhie este formata din clase care au o parte de functionalitate comuna iar restul este realizat prin implementarea diferita a unor metode care au insa aceleasi signaturi in toate clasele.
O clasa abstracta reprezinta un 'model' de clasa, pentru care se definesc o parte din metode (care definesc functionarea comuna, a claselor care formeaza ierarhia respectiva) si se fixeaza numele si modul de utilizare (signatura) pentru metodele declarate cu atributul abstract (care definesc pentru fiecare clasa din ierarhie specificul de functionalitate). Se poate considera ca atributele final si abstract sunt opuse unul altuia, in sensul ca o clasa (metoda) care are atributul final nu poate sa mai fie extinsa (inlocuita), in timp ce o metoda cu atributul abstract trebuie sa fie redefinita inainte de a putea sa fie utilizata, printr-o metoda care nu are acelasi atribut (chiar daca prelucrarea specificata in corpul metodei nu realizeaza nimic). O clasa abstracta poate sa fie extinsa de o alta clasa abstracta sau de o clasa 'normala'. Deoarece metodele abstracte reprezinta numai modele de metode, nu se pot face instantieri de obiecte pentru clasele abstracte. Un exemplu de clasa abstracta poate sa fie:
abstract class PisicaAbstracta extends Pisica
Definitia clasei PisicaAbstracta contine metoda afiseazaInfo() pentru care nu s-a precizat decat modul de apel. Prin extinderea acestei clase se pot construi noi subclase care vor mosteni tot ce este declarat in PisicaAbstracta si vor substitui metodele abstracte cu implementari concrete. Constructorii, metodele statice, private, sau cele care inlocuiesc o metoda care nu este abstracta a superclasei nu pot sa fie declarate abstracte. In general, o clasa trebuie sa fie declarata abstracta daca: contine cel putin o metoda abstracta, mosteneste o metoda abstracta de la superclasa sa si nu o implementeaza sau reprezinta o implementare a unei interfete pentru care exista cel putin o metoda care nu este implementata. Clasele abstracte nu pot sa fie instantiate. Daca se scrie ceva de forma:
PisicaAbstracta pa = new PisicaAbstracta(); se va semnala o eroare la compilarea programului, se poate insa declara o variabila care sa aiba ca tip o referinta catre o clasa abstracta:
PisicaAbstracta pa; urmand ca valorile pe care le primeste aceasta variabila sa reprezinte referinte la obiecte care reprezinta 'concretizari' ale clasei abstracte. De exemplu, sa consideram o extindere a clasei PisicaAbstracta:
class PisicaConcreta extends PisicaAbstracta
}
acum se poate face o instantiere a clasei PisicaConcreta:
pa = new PisicaConcreta();
pa.afiseazaInfo();
Interfete
Limbajul Java implementeaza notiunea de interfata din terminologia programarii orientate pe obiecte. Printr-o analogie cu limbajul C++, o interfata poate sa fie privita ca o clasa virtuala pura, adica o clasa formata numai din metode abstracte si constante.
O interfata defineste un tip. Interfetele sunt elemente de ordin intai ('first order object'), adica variabilele de tip interfata pot fi trimise ca argumente metodelor si pot fi obtinute ca rezultat, se pot crea ierarhii de interfete similar ierarhiilor de clase.
O interfata reprezinta o promisiune pe care orice clasa care implementeaza interfata trebuie sa o respecte. Metodele care apar intr-o interfata sunt, in mod implicit, publice. Ar fi contrar notiunii de interfata (prin care se poate accesa din exterior un obiect) ca interfetele sa fie private - deci interne unui obiect. Variabilele declarate sunt de fapt constante (chiar daca nu se specifica atributele corespunzatoare - public respectiv static -, compilatorul asuma (presupune) aceste atribute in mod implicit).
O interfata poate sa fie definita ca in Exemplul 7.
Exemplul 7. Declararea unei interfete
interface Exemplu
O clasa care implementeaza aceasta interfata trebuie sa specifice codul corespunzator implementarii metodelor prelucrare1() si prelucrare2(), dar poate desigur sa declare variabile si metode care nu apar in interfata.
Exemplul 8. Implementarea interfetei 'Exemplu'
class Implementare1 implements Exemplu
void prelucrare2(Pisica p)
void altaPrelucrare()
}
class Implementare2 implements Exemplu
void prelucrare2(Pisica p)
void prelucrareDiferita()
}
Ca si in cazul metodelor abstracte, o variabila poate sa fie declarata de tipul unei interfete. Valoarea unei astfel de variabile poate sa fie orice referinta la un obiect care apartine oricarei clase ce realizeaza implementarea interfetei respective sau care extinde o astfel de clasa. Reluand exemplul anterior, in urma executiei secventei de instructiuni din Exemplul 10, se va executa o varianta sau alta de implementare a metodei prelucrare1() in functie de o conditie care se testeaza.
Exemplul 9. Utilizarea unei variabile referinta la o interfata.
Exemplu e;
Implementare1 i1 = new Implementare1();
Implementare2 i2 = new Implementare2();
if( )
e = i1;
else
e = i2;
e.prelucrare1();
O interfata poate sa reprezinte extensia unei alte interfete, adica sa adauge noi forme de metode la o interfata care exista. Implementarea unei interfete poate sa fie si o clasa abstracta. In acest caz, implementarea efectiva va fi realizata de catre subclasele acesteia.
O clasa poate sa implementeze mai mult decat o singura interfata. Adica, sa defineasca modul de implementare al metodelor ale caror modele sunt continute in mai multe interfete. De exemplu - interfetele: DataInput si DataOutput continute in biblioteca java.io descriu metodele corespunzatoare citirii respectiv scrierii intr-un format independent de masina. Aceste interfete sunt utilizate de exemplu pentru definirea clasei RandomAccessFile :
public
class RandomAccessFile implements DataOutput, DataInput
Un alt exemplu de utilizare a interfetelor este prezentat in continuare si este inspirat din modul in care se utilizeaza interfata Runnable utilizata pentru instantierea firelor de executie.
Sa consideram un exemplu de interfata:
interface executabila
In definitia unei clase Exemplu sa definim o metoda prelucrare().
public class Exemplu
Cele doua definitii (interfata + clasa) pot sa fie compilate si memorate intr-o biblioteca. Se poate defini acum o clasa care reprezinta o implementare a interfetei executabila:
class Implementare inmplements executabila
Daca se executa instructiunea:
new Exemplu().prelucrare(new Implementare());
se va apela de fapt metoda Implementare.executie(). Aceasta tehnica se numeste 'call-back'.
Folosind tipul interfata, se elimina astfel necesitatea folosirii pointerilor catre metode, care sunt folositi in implementarea acestui mecanism in C++.
Intr-o biblioteca, o interfata este asemanatoare cu o clasa. Daca este declarata public, atunci este vizibila si in afara bibliotecii respective. Altfel, ea poate fi folosita doar in cadrul claselor si interfetelor din biblioteca curenta.
Sa consideram situatia in care trebuie construita o ierarhie de clase care implementeaza o interfata grafica. Clasa care va forma baza acestei ierarhii este clasa Fereastra. Este necesar sa se defineasca ferestre cu diferite proprietati: cu defilare, cu margini speciale, cu meniuri, etc. Un obiect instantiat va putea sa aiba orice combinatie de proprietati. La prima vedere, ar trebui sa se defineasca subclase corespunzatoare tuturor combinatiilor de proprietati posibile. De multe ori, o astfel de solutie nu este de acceptat. In schimb, se poate utiliza o solutie in care se adauga proprietati obiectelor definite. In Exemplul 11 sunt definite: o interfata care defineste modul in care trebuie sa se comporte o fereastra si o implementare posibila pentru aceasta interfata.
Exemplul 10. Definitie FereastraAbstracta si FereastraConcreta
interface FereastraAbstracta
class FereastraConcreta implements FereastraAbstracta
}
O alta implementare a interfetei FereastraAbstracta este clasa Decorator prezentata in Exemplul 12. Aceasta clasa contine un camp de tip FereastraAbstracta care va fi initializat de catre constructorul clasei cu o referinta la un obiect care reprezinta o instantiere a unei clase care implementeaza clasa FereastraAbstracta. Implementarea metodei prelucrare() este reprezentata de apelul metodei prlucrare() pentru obiectul referit de campul f.
Exemplul 11. Definitie clasa Decorator.
class Decorator implements FereastraAbstracta
public void prelucrare()
}
Pentru fiecare proprietate care se poate adauga ferestrei se va extinde clasa Decorator:
Exemplul 12. Diferiti 'decoratori'.
class DecoratorCuMargini extends Decorator
public void prelucrare()
}
class DecoratorCuScroll extends Decorator
public void prelucrare()
}
Crearea unei ferestre care sa aiba si margini si proprietatea de defilare se poate face sub forma:
FereastraConcreta c = new FereastraConcreta();
DecoratorCuScroll dcs = new DecoratorCuScroll(
new DecoratorCuMargini(c, 5), true);
dcs.prelucrare();
S-a initializat o variabila c care indica spre un obiect de tip FereastraConcreta. Acest obiect va fi utilizat ca argument pentru crearea unui obiect de tip DecoratorCuMargini (care va avea comportarea unei ferestre cu margini). Deoarece obiectul rezultat este o instantiere a unei clase care implementeaza interfata FereastraAbstracta, el va putea sa fie utilizat ca argument pentru constructorul care produce un obiect de tip DecoratorCuScroll. Cand se refera metoda prelucrare(), se vor executa pe rand prelucrarile corespunzatoare DecoratorCuMargini, DecoratorCuScroll si FereastraConcreta.
Acest document nu se poate descarca
E posibil sa te intereseze alte documente despre:
|
Copyright © 2024 - Toate drepturile rezervate QReferat.com | Folositi documentele afisate ca sursa de inspiratie. Va recomandam sa nu copiati textul, ci sa compuneti propriul document pe baza informatiilor de pe site. { Home } { Contact } { Termeni si conditii } |
Documente similare:
|
ComentariiCaracterizari
|
Cauta document |