|
Politica de confidentialitate |
|
• domnisoara hus • legume • istoria unui galban • metanol • recapitulare • profitul • caract • comentariu liric • radiolocatia • praslea cel voinic si merele da aur | |
Obiecte Java | ||||||
|
||||||
In primul rand sa observam ca, atunci cand scriem programe in Java nu facem altceva decat sa definim noi si noi clase de obiecte. Dintre acestea, unele vor reprezenta insasi aplicatia noastra in timp ce altele vor fi necesare pentru rezolvarea problemei la care lucram. Ulterior, atunci cand dorim sa lansam aplicatia in executie nu va trebui decat sa instantiem un obiect reprezentand aplicatia in sine si sa apelam o metoda de pornire definita de catre aplicatie, metoda care de obicei are un nume si un set de parametri bine fixate. Totusi, numele acestei metode depinde de contextul in care este lansata aplicatia noastra. Aceasta abordare a constructiei unei aplicatii ne spune printre altele ca vom putea lansa oricate instante ale aplicatiei noastre dorim, pentru ca fiecare dintre acestea va reprezenta un obiect in memorie avand propriile lui valori pentru variabile. Executia instantelor diferite ale aplicatiei va urma desigur cai diferite in functie de interactiunea fiecareia cu un utilizator, eventual acelasi, si in functie de unii parametri pe care ii putem defini in momentul crearii fiecarei instante. 6.1 Declaratia unei noi clase de obiectePasul 1: Stabilirea conceptului reprezentat de clasa de obiecteSa vedem ce trebuie sa definim atunci cand dorim sa cream o noua clasa de obiecte. In primul rand trebuie sa stabilim care este conceptul care este reprezentat de catre noua clasa de obiecte si sa definim informatiile memorate in obiect si modul de utilizare a acestuia. Acest pas este cel mai important din tot procesul de definire al unei noi clase de obiecte. Este necesar sa incercati sa respectati doua reguli oarecum antagonice. Una dintre ele spune ca nu trebuiesc create mai multe clase de obiecte decat este nevoie, pentru a nu face dificila intelegerea modului de lucru al aplicatiei la care lucrati. Cea de-a doua regula spune ca nu este bine sa mixati intr-un singur obiect functionalitati care nu au nimic in comun, creand astfel clase care corespund la doua concepte diferite. Medierea celor doua reguli nu este intotdeauna foarte usoara. Oricum, va va fi mai usor daca pastrati in minte faptul ca fiecare clasa pe care o definiti trebuie sa corespunda unui concept real bine definit, necesar la rezolvarea problemei la care lucrati. Si mai pastrati in minte si faptul ca este inutil sa lucrati cu concepte foarte generale atunci cand aplicatia dumneavoastra nu are nevoie decat de o particularizare a acestora. Riscati sa pierdeti controlul dezvoltarii acestor clase de obiecte prea generale si sa ingreunati dezvoltarea aplicatiei. Pasul 2: Stabilirea numelui clasei de obiecteDupa ce ati stabilit exact ce doriti de la noua clasa de obiecte, sunteti in masura sa gasiti un nume pentru noua clasa, nume care trebuie sa urmeze regulile de constructie ale identificatorilor limbajului Java definite in capitolul anterior. Stabilirea unui nume potrivit pentru o noua clasa nu este intotdeauna un lucru foarte usor. Problema este ca acest nume nu trebuie sa fie exagerat de lung dar trebuie sa exprime suficient de bine destinatia clasei. Regulile de denumire ale claselor sunt rezultatul experientei fiecaruia sau al unor conventii de numire stabilite anterior. De obicei, numele de clase este bine sa inceapa cu o litera majuscula. Daca numele clasei contine in interior mai multe cuvinte, aceste cuvinte trebuie de asemenea incepute cu litera majuscula. Restul caracterelor vor fi litere minuscule. De exemplu, daca dorim sa definim o clasa de obiecte care implementeaza conceptul de motor Otto vom folosi un nume ca MotorOtto pentru noua clasa ce trebuie creata. La fel, vom defini clasa MotorDiesel sau MotorCuReactie. Daca insa avem nevoie sa definim o clasa separata pentru un motor Otto cu cilindri in V si carburator, denumirea clasei ca MotorOttoCuCilindriInVSiCarburator nu este poate cea mai buna solutie. Poate ca in acest caz este preferabila o prescurtare de forma MotorOttoVC. Desigur, acestea sunt doar cateva remarci la adresa acestei probleme si este in continuare necesar ca in timp sa va creati propria conventie de denumire a claselor pe care le creati. Pasul 3: Stabilirea superclaseiIn cazul in care ati definit deja o parte din functionalitatea de care aveti nevoie intr-o alta superclasa, puteti sa derivati noua clasa de obiecte din clasa deja existenta. Daca nu exista o astfel de clasa, noua clasa va fi automat derivata din clasa de obiecte predefinita numita Object. In Java, clasa Object este superclasa direct sau indirect pentru orice alta clasa de obiecte definita de utilizator. Alegerea superclasei din care derivati noua clasa de obiecte este foarte importanta pentru ca va ajuta sa refolositi codul deja existent. Totusi, nu alegeti cu usurinta superclasa unui obiect pentru ca astfel puteti incarca obiectele cu o functionalitate inutila, existenta in superclasa. Daca nu exista o clasa care sa va ofere doar functionalitatea de care aveti nevoie, este preferabil sa derivati noua clasa direct din clasa Object si sa apelati indirect functionalitatea pe care o doriti. Pasul 4: Stabilirea interfetelor pe care le respecta clasaStabilirea acestor interfete are dublu scop. In primul rand ele instruiesc compilatorul sa verifice daca noua clasa respecta cu adevarat toate interfetele pe care le-a declarat, cu alte cuvinte defineste toate metodele declarate in aceste interfete. A doua finalitate este aceea de a permite compilatorului sa foloseasca instantele noii clase oriunde aplicatia declara ca este nevoie de un obiect care implementeaza interfetele declarate. O clasa poate sa implementeze mai multe interfete sau niciuna. Pasul 5: Stabilirea modificatorilor claseiIn unele cazuri trebuie sa oferim compilatorului informatii suplimentare relative la modul in care vom folosi clasa nou creata pentru ca acesta sa poata executa verificari suplimentare asupra descrierii clasei. In acest scop, putem defini o clasa ca fiind abstracta, finala sau publica folosindu-ne de o serie de cuvinte rezervate numite modificatori. Modificatorii pentru tipurile de clase de mai sus sunt respectiv: abstract, final si public. In cazul in care declaram o clasa de obiecte ca fiind abstracta, compilatorul va interzice instantierea acestei clase. Daca o clasa este declarata finala, compilatorul va avea grija sa nu putem deriva noi subclase din aceasta clasa. In cazul in care declaram in acelasi timp o clasa de obiecte ca fiind abstracta si finala, eroarea va fi semnalata inca din timpul compilarii pentru ca cei doi modificatori se exclud. Pentru ca o clasa sa poata fi folosita si in exteriorul contextului in care a fost declarata ea trebuie sa fie declarata publica. Mai mult despre acest aspect in paragraful referitor la structura programelor. Pana atunci, sa spunem ca orice clasa de obiecte care va fi instantiata ca o aplicatie trebuie declarata publica. Pasul 6: Scrierea corpului declaratieiIn sfarsit, dupa ce toti ceilalti pasi au fost efectuati, putem trece la scrierea corpului declaratiei de clasa. In principal, aici vom descrie variabilele clasei impreuna cu metodele care lucreaza cu acestea. Tot aici putem preciza si gradele de protejare pentru fiecare dintre elementele declaratiei. Uneori numim variabilele si metodele unei clase la un loc ca fiind campurile clasei. Subcapitolele urmatoare vor descrie in amanunt corpul unei declaratii. Stop: Forma generala a unei declaratii de clasaSintaxa exacta de declarare a unei clase arata in felul urmator:
6.2 Variabilele unei claseIn interiorul claselor se pot declara variabile. Aceste variabile sunt specifice clasei respective. Fiecare dintre ele trebuie sa aiba un tip, un nume si poate avea initializatori. In afara de aceste elemente, pe care le-am prezentat deja in sectiunea in care am prezentat variabilele, variabilele definite in interiorul unei clase pot avea definiti o serie de modificatori care altereaza comportarea variabilei in interiorul clasei, si o specificatie de protectie care defineste cine are dreptul sa acceseze variabila respectiva. 6.2.1 ModificatoriModificatorii sunt cuvinte rezervate Java care precizeaza sensul unei declaratii. Iata lista acestora: static final transient volatile Dintre acestia, transient nu este utilizat in versiunea curenta a limbajului Java. Pe viitor va fi folosit pentru a specifica variabile care nu contin informatii care trebuie sa ramana persistente la terminarea programului. Modificatorul volatile specifica faptul ca variabila respectiva poate fi modificata asincron cu rularea aplicatiei. In aceste cazuri, compilatorul trebuie sa-si ia masuri suplimentare in cazul generarii si optimizarii codului care se adreseaza acestei variabile. Modificatorul final este folosit pentru a specifica o variabila a carei valoare nu poate fi modificata. Variabila respectiva trebuie sa primeasca o valoare de initializare chiar in momentul declaratiei. Altfel, ea nu va mai putea fi initializata in viitor. Orice incercare ulterioara de a seta valori la aceasta variabila va fi semnalata ca eroare de compilare. Modificatorul static este folosit pentru a specifica faptul ca variabila are o singura valoare comuna tuturor instantelor clasei in care este declarata. Modificarea valorii acestei variabile din interiorul unui obiect face ca modificarea sa fie vizibila din celelalte obiecte. Variabilele statice sunt initializate la incarcarea codului specific unei clase si exista chiar si daca nu exista nici o instanta a clasei respective. Din aceasta cauza, ele pot fi folosite de metodele statice. 6.2.2 ProtectieIn Java exista patru grade de protectie pentru o variabila apartinand unei clase:
O variabila publica este accesibila oriunde este accesibil numele clasei. Cuvantul rezervat este public. O variabila protejata este accesibila in orice clasa din pachetul careia ii apartine clasa in care este declarata. In acelasi timp, variabila este accesibila in toate subclasele clasei date, chiar daca ele apartin altor pachete. Cuvantul rezervat este protected. O variabila privata este accesibila doar in interiorul clasei in care a fost declarata. Cuvantul rezervat este private. O variabila care nu are nici o declaratie relativa la gradul de protectie este automat o variabila prietenoasa. O variabila prietenoasa este accesibila in pachetul din care face parte clasa in interiorul careia a fost declarata, la fel ca si o variabila protejata. Dar, spre deosebire de variabilele protejate, o variabila prietenoasa nu este accesibila in subclasele clasei date daca aceste sunt declarate ca apartinand unui alt pachet. Nu exista un cuvant rezervat pentru specificarea explicita a variabilelor prietenoase. O variabila nu poate avea declarate mai multe grade de protectie in acelasi timp. O astfel de declaratie este semnalata ca eroare de compilare. 6.2.3 Accesarea unei variabileAccesarea unei variabile declarate in interiorul unei clasei se face folosindu-ne de o expresie de forma:
Referinta catre o instanta trebuie sa fie referinta catre clasa care contine variabila. Referinta poate fi valoarea unei expresii mai complicate, ca de exemplu un element dintr-un tablou de referinte. In cazul in care avem o variabila statica, aceasta poate fi accesata si fara sa detinem o referinta catre o instanta a clasei. Sintaxa este, in acest caz: 6.2.4 VizibilitateO variabila poate fi ascunsa de declaratia unei alte variabile cu acelasi nume. De exemplu, daca intr-o clasa avem declarata o variabila cu numele unu si intr-o subclasa a acesteia avem declarata o variabila cu acelasi nume, atunci variabila din superclasa este ascunsa de cea din clasa. Totusi, variabila din superclasa exista inca si poate fi accesata in mod explicit. Expresia de referire este, in acest caz:
sau
in cazul in care superclasa este imediata. La fel, o variabila a unei clase poate fi ascunsa de o declaratie de variabila dintr-un bloc de instructiuni. Orice referinta la ea va trebui facuta in mod explicit. Expresia de referire este, in acest caz: 6.2.5 Variabile predefinite: this si superIn interiorul fiecarei metode non-statice dintr-o clasa exista predefinite doua variabile cu semnificatie speciala. Cele doua variabile sunt de tip referinta si au aceeasi valoare si anume o referinta catre obiectul curent. Diferenta dintre ele este tipul. Prima dintre acestea este variabila this care are tipul referinta catre clasa in interiorul careia apare metoda. A doua este variabila super al carei tip este o referinta catre superclasa imediata a clasei in care apare metoda. In interiorul obiectelor din clasa Object nu se poate folosi referinta super pentru ca nu exista nici o superclasa a clasei de obiecte Object. In cazul in care super este folosita la apelul unui constructor sau al unei metode, ea actioneaza ca un cast catre superclasa imediata. 6.3 Metodele unei claseFiecare clasa isi poate defini propriile sale metode pe langa metodele pe care le mosteneste de la superclasa sa. Aceste metode definesc operatiile care pot fi executate cu obiectul respectiv. In cazul in care una dintre metodele mostenite nu are o implementare corespunzatoare in superclasa, clasa isi poate redefini metoda dupa cum doreste. In plus, o clasa isi poate defini metode de constructie a obiectelor si metode de eliberare a acestora. Metodele de constructie sunt apelate ori de cate ori este alocat un nou obiect din clasa respectiva. Putem declara mai multe metode de constructie, ele diferind prin parametrii din care trebuie construit obiectul. Metodele de eliberare a obiectului sunt cele care elibereaza resursele ocupate de obiect in momentul in care acesta este distrus de catre mecanismul automat de colectare de gunoaie. Fiecare clasa are o singura metoda de eliberare, numita si finalizator. Apelarea acestei metode se face de catre sistem si nu exista nici o cale de control al momentului in care se produce acest apel. 6.3.1 Declararea metodelorPentru a declara o metoda, este necesar sa declaram numele acesteia, tipul de valoare pe care o intoarce, parametrii metodei precum si un bloc in care sa descriem instructiunile care trebuiesc executate atunci cand metoda este apelata. In plus, orice metoda are un numar de modificatori care descriu proprietatile metodei si modul de lucru al acesteia. Declararea precum si implementarea metodelor se face intotdeauna in interiorul declaratiei de clasa. Nu exista nici o cale prin care sa putem scrie o parte dintre metodele unei clase intr-un fisier separat care sa faca referinta apoi la declaratia clasei. In forma generala, declaratia unei metode arata in felul urmator:
Modificatorii precum si clauzele throws pot sa lipseasca. 6.3.1.1 Numele si parametrii metodelorRecunoasterea unei anumite metode se face dupa numele si tipul parametrilor sai. Pot exista metode cu acelasi nume dar avand parametri diferiti. Acest fenomen poarta numele de supraincarcarea numelui unei metode. Numele metodei este un identificator Java. Avem toata libertatea in a alege numele pe care il dorim pentru metodele noastre, dar in general este preferabil sa alegem nume care sugereaza utilizarea metodei. Numele unei metode incepe de obicei cu litera mica. Daca acesta este format din mai multe cuvinte, litera de inceput a fiecarui cuvant va fi majuscula. In acest mod numele unei metode este foarte usor de citit si de depistat in sursa. Parametrii metodei sunt in realitate niste variabile care sunt initializate in momentul apelului cu valori care controleaza modul ulterior de executie. Aceste variabile exista pe toata perioada executiei metodei. Se pot scrie metode care sa nu aiba nici un parametru. Fiind o variabila, fiecare parametru are un tip si un nume. Numele trebuie sa fie un identificator Java. Desi avem libertatea sa alegem orice nume dorim, din nou este preferabil sa alegem nume care sa sugereze scopul la care va fi utilizat parametrul respectiv. Tipul unui parametru este oricare dintre tipurile valide in Java. Acestea poate fi fie un tip primitiv, fie un tip referinta catre obiect, interfata sau tablou. In momentul apelului unei metode, compilatorul incearca sa gaseasca o metoda in interiorul clasei care sa aiba acelasi nume cu cel apelat si acelasi numar de parametri ca si apelul. Mai mult, tipurile parametrilor de apel trebuie sa corespunda cu tipurile parametrilor declarati ai metodei gasite sau sa poata fi convertiti la acestia. Daca o astfel de metoda este gasita in declaratia clasei sau in superclasele acesteia, parametrii de apel sunt convertiti catre tipurile declarate si se genereaza apelul catre metoda respectiva. Este o eroare de compilare sa declaram doua metode cu acelasi nume, acelasi numar de parametri si acelasi tip pentru parametrii corespunzatori. Intr-o asemenea situatie, compilatorul n-ar mai sti care metoda trebuie apelata la un moment dat. De asemenea, este o eroare de compilare sa existe doua metode care se potrivesc la acelasi apel. Acest lucru se intampla cand nici una dintre metodele existente nu se potriveste exact si cand exista doua metode cu acelasi nume si acelasi numar de parametri si, in plus, parametrii de apel se pot converti catre parametrii declarati ai ambelor metode. Rezolvarea unei astfel de probleme se face prin conversia explicita (cast) de catre programator a valorilor de apel spre tipurile exacte ale parametrilor metodei pe care dorim sa o apelam in realitate. In fine, forma generala de declaratie a numelui si parametrilor unei metode este:
6.3.1.2 Modificatori de metodeModificatorii sunt cuvinte cheie ale limbajului Java care specifica proprietati suplimentare pentru o metoda. Iata lista completa a acestora in cazul metodelor:
6.3.1.2.1 Metode staticeIn mod normal, o metoda a unei clase se poate apela numai printr-o instanta a clasei respective sau printr-o instanta a unei subclase. Acest lucru se datoreaza faptului ca metoda face apel la o serie de variabile ale clasei care sunt memorate in interiorul instantei si care au valori diferite in instante diferite. Astfel de metode se numesc metode ale instantelor clasei. Dupa cum stim deja, exista si un alt tip de variabile, si anume variabilele de clasa sau variabilele statice care sunt comune tuturor instantelor clasei respective si exista pe toata perioada de timp in care clasa este incarcata in memorie. Aceste variabile pot fi accesate fara sa avem nevoie de o instanta a clasei respective. In mod similar exista si metode statice. Aceste metode nu au nevoie de o instanta a clasei sau a unei subclase pentru a putea fi apelate pentru ca ele nu au voie sa foloseasca variabile care sunt memorate in interiorul instantelor. In schimb, aceste metode pot sa foloseasca variabilele statice declarate in interiorul clasei. Orice metoda statica este implicit si finala. 6.3.1.2.2 Metode abstracteMetodele abstracte sunt metode care nu au corp de implementare. Ele sunt declarate numai pentru a forta subclasele care vor sa aiba instante sa implementeze metodele respective. Metodele abstracte trebuie declarate numai in interiorul claselor care au fost declarate abstracte. Altfel compilatorul va semnala o eroare de compilare. Orice subclasa a claselor abstracte care nu este declarata abstracta trebuie sa ofere o implementare a acestor metode, altfel va fi generata o eroare de compilare. Prin acest mecanism ne asiguram ca toate instantele care pot fi convertite catre clasa care contine definitia unei metode abstracte au implementata metoda respectiva dar, in acelasi timp, nu este nevoie sa implementam in nici un fel metoda chiar in clasa care o declara pentru ca nu stim pe moment cum va fi implementata. O metoda statica nu poate fi declarata si abstracta pentru ca o metoda statica este implicit finala si nu poate fi rescrisa. 6.3.1.2.3 Metode finaleO metoda finala este o metoda care nu poate fi rescrisa in subclasele clasei in care a fost declarata. O metoda este rescrisa intr-o subclasa daca aceasta implementeaza o metoda cu acelasi nume si acelasi numar si tip de parametri ca si metoda din superclasa. Declararea metodelor finale este utila in primul rand compilatorului care poate genera metodele respective direct in codul rezultat fiind sigur ca metoda nu va avea nici o alta implementare in subclase. 6.3.1.2.4 Metode nativeMetodele native sunt metode care sunt implementate pe o cale specifica unei anumite platforme. De obicei aceste metode sunt implementate in C sau in limbaj de asamblare. Metoda propriu-zisa nu poate avea corp de implementare pentru ca implementarea nu este facuta in Java. In rest, metodele native sunt exact ca orice alta metoda Java. Ele pot fi mostenite, pot fi statice sau nu, pot fi finale sau nu, pot sa rescrie o metoda din superclasa si pot fi la randul lor rescrise in subclase. 6.3.1.2.5 Metode sincronizateO metoda sincronizata este o metoda care contine cod critic pentru un anumit obiect sau clasa si nu poate fi rulata in paralel cu nici o alta metoda critica sau cu o instructiune synchronized referitoare la acelasi obiect sau clasa. Inainte de executia metodei, obiectul sau clasa respectiva sunt blocate. La terminarea metodei, acestea sunt deblocate. Daca metoda este statica atunci este blocata o intreaga clasa, clasa din care face parte metoda. Altfel, este blocata doar instanta in contextul careia este apelata metoda. 6.3.1.3 Protejarea metodelorAccesul la metodele unei clase este protejat in acelasi fel ca si accesul la variabilele clasei. In Java exista patru grade de protectie pentru o metoda apartinand unei clase:
O metoda declarata publica este accesibila oriunde este accesibil numele clasei. Cuvantul rezervat este public. O metoda declarata protejata este accesibila in orice clasa din pachetul careia ii apartine clasa in care este declarata. In acelasi timp, metoda este accesibila in toate subclasele clasei date, chiar daca ele apartin altor pachete. Cuvantul rezervat este protected. O metoda declarata privata este accesibila doar in interiorul clasei in care a fost declarata. Cuvantul rezervat este private. O metoda care nu are nici o declaratie relativa la gradul de protectie este automat o metoda prietenoasa. O metoda prietenoasa este accesibila in pachetul din care face parte clasa in interiorul careia a fost declarata la fel ca si o metoda protejata. Dar, spre deosebire de metodele protejate, o metoda prietenoasa nu este accesibila in subclasele clasei date daca aceste sunt declarate ca apartinand unui alt pachet. Nu exista un cuvant rezervat pentru specificarea explicita a metodelor prietenoase. O metoda nu poate avea declarate mai multe grade de protectie in acelasi timp. O astfel de declaratie este semnalata ca eroare de compilare. 6.3.2 Clauze throwsDaca o metoda poate arunca o exceptie, adica sa apeleze instructiunea throw, ea trebuie sa declare tipul acestor exceptii intr-o clauza throws. Sintaxa acesteia este:
Numele de tipuri specificate in clauza throws trebuie sa fie accesibile si sa fie asignabile la tipul de clasa Throwable. Daca o metoda contine o clauza throws, este o eroare de compilare ca metoda sa arunce un obiect care nu este asignabil la compilare la tipurile de clase Error, RunTimeException sau la tipurile de clase specificate in clauza throws. Daca o metoda nu are o clauza throws, este o eroare de compilare ca aceasta sa poata arunca o exceptie normala din interiorul corpului ei. 6.3.3 Apelul metodelorPentru a apela o metoda a unei clase este necesar sa dispunem de o cale de acces la metoda respectiva. In plus, trebuie sa dispunem de drepturile necesare apelului metodei. Sintaxa efectiva de acces este urmatoarea:
In cazul in care metoda este statica, pentru a specifica o cale de acces este suficient sa furnizam numele clasei in care a fost declarata metoda. Accesul la numele clasei se poate obtine fie importand clasa sau intreg pachetul din care face parte clasa fie specificand in clar numele clasei si drumul de acces catre aceasta. De exemplu, pentru a accesa metoda random definita static in clasa Math apartinand pachetului java.lang putem scrie: double aleator = Math.random(); sau, alternativ: double aleator = java.lang.Math.random(); In cazul claselor definite in pachetul java.lang nu este necesar nici un import pentru ca acestea sunt implicit importate de catre compilator. Cea de-a doua cale de acces este existenta unei instante a clasei respective. Prin aceasta instanta putem accesa metodele care nu sunt declarate statice, numite uneori si metode ale instantelor clasei. Aceste metode au nevoie de o instanta a clasei pentru a putea lucra, pentru ca folosesc variabile non-statice ale clasei sau apeleaza alte metode non-statice. Metodele primesc acest obiect ca pe un parametru ascuns. De exemplu, avand o instanta a clasei Object sau a unei subclase a acesteia, putem obtine o reprezentare sub forma de sir de caractere prin: Object obiect = new Object(); String sir = obiect.toString(); In cazul in care apelam o metoda a clasei din care face parte si metoda apelanta putem sa renuntam la calea de acces in cazul metodelor statice, scriind doar numele metodei si parametrii. Pentru metodele specifice instantelor, putem renunta la calea de acces, dar in acest caz metoda acceseaza aceeasi instanta ca si metoda apelanta. In cazul in care metoda apelanta este statica, specificarea unei instante este obligatorie in cazul metodelor de instanta. Parametrii de apel servesc impreuna cu numele la identificarea metodei pe care dorim sa o apelam. Inainte de a fi transmisi, acestia sunt convertiti catre tipurile declarate de parametri ai metodei, dupa cum este descris mai sus. Specificarea parametrilor de apel se face separandu-i prin virgula. Dupa ultimul parametru nu se mai pune virgula. Daca metoda nu are nici un parametru, parantezele rotunde sunt in continuare necesare. Exemple de apel de metode cu parametri: String numar = String.valueOf( 12 ); // 12 -> String double valoare = Math.abs( 12.54 ); // valoare absoluta String prima = numar.substring( 0, 1 ); // prima litera ^ 6.3.4 Valoarea de retur a unei metodeO metoda trebuie sa-si declare tipul valorii pe care o intoarce. In cazul in care metoda doreste sa specifice explicit ca nu intoarce nici o valoare, ea trebuie sa declare ca tip de retur tipul void ca in exemplul: void a() A . S In caz general, o metoda intoarce o valoare primitiva sau un tip referinta. Putem declara acest tip ca in: Thread cautaFirulCurent() A . S long abs( int valoare ) A . S Pentru a returna o valoare ca rezultat al executiei unei metode, trebuie sa folosim instructiunea return, asa cum s-a aratat in sectiunea dedicata instructiunilor. Instructiunea return trebuie sa contina o expresie a carei valoare sa poata fi convertita la tipul declarat al valorii de retur a metodei. De exemplu: long abs( int valoare ) A return Math.abs( valoare ); S Metoda statica abs din clasa Math care primeste un parametru intreg returneaza tot un intreg. In exemplul nostru, instructiunea return este corecta pentru ca exista o cale de conversie de la intreg la intreg lung, conversie care este apelata automat de compilator inainte de iesirea din metoda. In schimb, in exemplul urmator: int abs( long valoare ) A return Math.abs( valoare ); S compilatorul va genera o eroare de compilare pentru ca metoda statica abs din clasa Math care primeste ca parametru un intreg lung intoarce tot un intreg lung, iar un intreg lung nu poate fi convertit sigur la un intreg normal pentru ca exista riscul deteriorarii valorii, la fel ca la atribuire. Rezolvarea trebuie sa contina un cast explicit: int abs( long valoare ) A return ( int )Math.abs( valoare ); S In cazul in care o metoda este declarata void, putem sa ne intoarcem din ea folosind instructiunea return fara nici o expresie. De exemplu: void metoda() A . if( . ) return; . S Specificarea unei expresii in acest caz duce la o eroare de compilare. La fel si in cazul in care folosim instructiunea return fara nici o expresie in interiorul unei metode care nu este declarata void. 6.3.5 VizibilitateO metoda este vizibila daca este declarata in clasa prin care este apelata sau intr-una din superclasele acesteia. De exemplu, daca avem urmatoarea declaratie: class A A . void a() A . S S class B extends A A void b() A a(); c(); . S void c() A .. S . S Apelul metodei a in interiorul metodei b din clasa B este permis pentru ca metoda a este declarata in interiorul clasei A care este superclasa pentru clasa B. Apelul metodei c in aceeasi metoda b este permis pentru ca metoda c este declarata in aceeasi clasa ca si metoda a. Uneori, o subclasa rescrie o metoda dintr-o superclasa a sa. In acest caz, apelul metodei respective in interiorul subclasei duce automat la apelul metodei din subclasa. Daca totusi dorim sa apelam metoda asa cum a fost ea definita in superclasa, putem prefixa apelul cu numele superclasei. De exemplu: class A A . void a() A . S S class B extends A A void a() A .. S void c() A a();// metoda a din clasa B A.a();// metoda a din clasa A . S . S Desigur, pentru a "vedea" o metoda si a o putea apela, este nevoie sa avem drepturile necesare. 6.4 Initializatori staticiLa incarcarea unei clase sunt automat initializate toate variabilele statice declarate in interiorul clasei. In plus, sunt apelati toti initializatorii statici ai clasei. Un initializator static are urmatoarea sintaxa:
Blocul de instructiuni este executat automat la incarcarea clasei. De exemplu, putem defini un initializator static in felul urmator: class A A static double a; static int b; static A a = Math.random(); // numar dublu intre 0.0 si 1.0 b = ( int )( a * 500 ); // numar intreg intre 0 si 500 S . S Declaratiile de variabile statice si initializatorii statici sunt executate in ordinea in care apar in clasa. De exemplu, daca avem urmatoarea declaratie de clasa: class A A static int i = 11; static A i += 100; i %= 55; S static int j = i + 1; S valoarea finala a lui i va fi 1 ( ( 11 + 100 ) % 55 ) iar valoarea lui j va fi 2. 6.5 Constructori si finalizatori6.5.1 constructoriLa crearea unei noi instante a unei clase sistemul aloca automat memoria necesara instantei si o initializeaza cu valorile initiale specificate sau implicite. Daca dorim sa facem initializari suplimentare in interiorul acestei memorii sau in alta parte putem descrie metode speciale numite constructori ai clasei. Putem avea mai multi constructori pentru aceeasi clasa, acestia diferind doar prin parametrii pe care ii primesc. Numele tuturor constructorilor este acelasi si este identic cu numele clasei. Declaratia unui constructor este asemanatoare cu declaratia unei metode oarecare, cu diferenta ca nu putem specifica o valoare de retur si nu putem specifica nici un fel de modificatori. Daca dorim sa returnam dintr-un constructor, trebuie sa folosim instructiunea return fara nici o expresie. Putem insa sa specificam gradul de protectie al unui constructor ca fiind public, privat, protejat sau prietenos. Constructorii pot avea clauze throws. Daca o clasa nu are constructori, compilatorul va crea automat un constructor implicit care nu ia nici un parametru si care initializeaza toate variabilele clasei si apeleaza constructorul superclasei fara argumente prin super(). Daca superclasa nu are un constructor care ia zero argumente, se va genera o eroare de compilare. Daca o clasa are cel putin un constructor, constructorul implicit nu mai este creat de catre compilator. Cand construim corpul unui constructor avem posibilitatea de a apela, pe prima linie a blocului de instructiuni care reprezinta corpul constructorului, un constructor explicit. Constructorul explicit poate avea doua forme:
Cu aceasta sintaxa apelam unul dintre constructorii superclasei sau unul dintre ceilalti constructori din aceeasi clasa. Aceste linii nu pot aparea decat pe prima pozitie in corpul constructorului. Daca nu apar acolo, compilatorul considera implicit ca prima instructiune din corpul constructorului este:
Si in acest caz se va genera o eroare de compilare daca nu exista un constructor in superclasa care sa lucreze fara nici un parametru. Dupa apelul explicit al unui constructor din superclasa cu sintaxa super( . ) este executata in mod implicit initializarea tuturor variabilelor de instanta (non-statice) care au initializatori expliciti. Dupa apelul unui constructor din aceeasi clasa cu sintaxa this( . ) nu exista nici o alta actiune implicita, deci nu vor fi initializate nici un fel de variabile. Aceasta datorita faptului ca initializarea s-a produs deja in constructorul apelat. Iata si un exemplu: class A extends B A String valoare; A( String val ) A // aici exista apel implicit // al lui super(), adica B() valoare = val; S A( int val ) A this( String.valueOf( val ) );// alt constructor S S ^ 6.5.2 FinalizatoriIn Java nu este nevoie sa apelam in mod explicit distrugerea unei instante atunci cand nu mai este nevoie de ea. Sistemul ofera un mecanism de colectare a gunoaielor care recunoaste situatia in care o instanta de obiect sau un tablou nu mai sunt referite de nimeni si le distruge in mod automat. Acest mecanism de colectare a gunoaielor ruleaza pe un fir de executie separat, de prioritate mica. Nu avem nici o posibilitate sa aflam exact care este momentul in care va fi distrusa o instanta. Totusi, putem specifica o functie care sa fie apelata automat in momentul in care colectorul de gunoaie incearca sa distruga obiectul. Aceasta functie are nume, numar de parametri si tip de valoare de retur fixe: void finalize() Dupa apelul metodei de finalizare (numita si finalizator), instanta nu este inca distrusa pana la o noua verificare din partea colectorului de gunoaie. Aceasta comportare este necesara pentru ca instanta poate fi revitalizata prin crearea unei referinte catre ea in interiorul finalizatorului. Totusi, finalizatorul nu este apelat decat o singura data. Daca obiectul revitalizat redevine candidat la colectorul de gunoaie, acesta este distrus fara a i se mai apela finalizatorul. Cu alte cuvinte, un obiect nu poate fi revitalizat decat o singura data. Daca in timpul finalizarii apare o exceptie, ea este ignorata si finalizatorul nu va mai fi apelat din nou. 6.5.3 Crearea instantelorO instanta este creata folosind o expresie de alocare care foloseste cuvantul rezervat new. Iata care sunt pasii care sunt executati la apelul acestei expresii:
Exemple de creare: A o1 = new A(); B o2 = new B(); class C extends B A String valoare; C( String val ) A // aici exista apel implicit // al lui super(), adica B() valoare = val; S C( int val ) A this( String.valueOf( val ) ); S S C o3 = new C( "Vasile" ); C o4 = new C( 13 ); O alta cale de creare a unui obiect este apelul metodei newInstance declarate in clasa Class. Iata pasii de creare in acest caz:
valoare a metodei newInstance. Tipul acestei referinte va fi Object in timpul compilarii si tipul clasei reale in timpul executiei. 6.6 Derivarea claselorO clasa poate fi derivata dintr-alta prin folosirea in declaratia clasei derivate a clauzei extends. Clasa din care se deriva noua clasa se numeste superclasa imediata a clasei derivate. Toate clasele care sunt superclase ale superclasei imediate ale unei clase sunt superclase si pentru clasa data. Clasa nou derivata se numeste subclasa a clasei din care este derivata. Sintaxa generala este:
O clasa poate fi derivata dintr-o singura alta clasa, cu alte cuvinte o clasa poate avea o singura superclasa imediata. Clasa derivata mosteneste toate variabilele si metodele superclasei sale. Totusi, ea nu poate accesa decat acele variabile si metode care nu sunt declarate private. Putem rescrie o metoda a superclasei declarand o metoda in noua clasa avand acelasi nume si aceiasi parametri. La fel, putem declara o variabila care are acelasi nume cu o variabila din superclasa. In acest caz, noul nume ascunde vechea variabila, substituindu-i-se. Putem in continuare sa ne referim la variabila ascunsa din superclasa specificand numele superclasei sau folosindu-ne de variabila super. Exemplu: class A A int a = 1; void unu() A System.out.println( a ); S S class B extends A A double a = Math.PI; void unu() A System.out.println( a ); S void doi() A System.out.println( A.a ); S void trei() A unu(); super.unu(); S S Daca apelam metoda unu din clasa A, aceasta va afisa la consola numarul 1. Acest apel se va face cu instructiunea: ( new A() ).unu(); Daca apelam metoda unu din clasa B, aceasta va afisa la consola numarul PI. Apelul il putem face de exemplu cu instructiunea: B obiect = new B(); obiect.unu(); Observati ca in metoda unu din clasa B, variabila referita este variabila a din clasa B. Variabila a din clasa A este ascunsa. Putem insa sa o referim prin sintaxa A.a ca in metoda doi din clasa B. In interiorul clasei B, apelul metodei unu fara nici o alta specificatie duce automat la apelul metodei unu definite in interiorul clasei B. Metoda unu din clasa B rescrie metoda unu din clasa A. Vechea metoda este accesibila pentru a o referi in mod explicit ca in metoda trei din clasa B. Apelul acestei metode va afisa mai intai numarul PI si apoi numarul 1. Daca avem declarata o variabila de tip referinta catre o instanta a clasei A, aceasta variabila poate sa contina in timpul executiei si o referinta catre o instanta a clasei B. Invers, afirmatia nu este valabila. In clipa in care apelam metoda unu pentru o variabila referinta catre clasa A, sistemul va apela metoda unu a clasei A sau B in functie de adevaratul tip al referintei din timpul executiei. Cu alte cuvinte, urmatoarea secventa de instructiuni: A tablouai = new Aa2i; tabloua0i = new A(); tabloua1i = new B(); for( int i = 0; i < 2; i++ ) A tablouaii.unu(); S va afisa doua numere diferite, mai intai 1 si apoi PI. Aceasta din cauza ca cel de-al doilea element din tablou este, in timpul executiei, de tip referinta la o instanta a clasei B chiar daca la compilare este de tipul referinta la o instanta a clasei A. Acest mecanism se numeste legare tarzie, si inseamna ca metoda care va fi efectiv apelata este stabilita doar in timpul executiei si nu la compilare. Daca nu declaram nici o superclasa in definitia unei clase, atunci se considera automat ca noua clasa deriva direct din clasa Object, mostenind toate metodele si variabilele acesteia. 6.7 InterfeteO interfata este in esenta o declaratie de tip ce consta dintr-un set de metode si constante pentru care nu s-a specificat nici o implementare. Programele Java folosesc interfetele pentru a suplini lipsa mostenirii multiple, adica a claselor de obiecte care deriva din doua sau mai multe alte clase. Sintaxa de declaratie a unei interfete este urmatoarea:
Modificatorii unei interfete pot fi doar cuvintele rezervate public si abstract. O interfata care este publica poate fi accesata si de catre alte pachete decat cel care o defineste. In plus, fiecare interfata este in mod implicit abstracta. Modificatorul abstract este permis dar nu obligatoriu. Numele interfetelor trebuie sa fie identificatori Java. Conventiile de numire a interfetelor le urmeaza in general pe cele de numire a claselor de obiecte. Interfetele, la fel ca si clasele de obiecte, pot avea subinterfete. Subinterfetele mostenesc toate constantele si declaratiile de metode ale interfetei din care deriva si pot defini in plus noi elemente. Pentru a defini o subinterfata, folosim o clauza extends. Aceste clauze specifica superinterfata unei interfete. O interfata poate avea mai multe superinterfete care se declara separate prin virgula dupa cuvantul rezervat extends. Circularitatea definitiei subinterfetelor nu este permisa. In cazul interfetelor nu exista o radacina comuna a arborelui de derivare asa cum exista pentru arborele de clase, clasa Object. In corpul unei declaratii de interfata pot sa apara declaratii de variabile si declaratii de metode. Variabilele sunt implicit statice si finale. Din cauza faptului ca variabilele sunt finale, este obligatoriu sa fie specificata o valoare initiala pentru aceste variabile. In plus, aceasta valoare initiala trebuie sa fie constanta (sa nu depinda de alte variabile). Daca interfata este declarata publica, toate variabilele din corpul sau sunt implicit declarate publice. In ceea ce priveste metodele declarate in interiorul corpului unei interfete, acestea sunt implicit declarate abstracte. In plus, daca interfata este declarata publica, metodele din interior sunt implicit declarate publice. Iata un exemplu de declaratii de interfete: public interface ObiectSpatial A final int CUB = 0; final int SFERA = 1; double greutate(); double volum(); double raza(); int tip(); S public interface ObiectSpatioTemporal extends ObiectSpatial A void centrulDeGreutate( long moment, double coordonateai ); long momentInitial(); long momentFinal(); S Cele doua interfete definesc comportamentul unui obiect spatial respectiv al unui obiect spatio-temporal. Un obiect spatial are o greutate, un volum si o raza a sferei minime in care se poate inscrie. In plus, putem defini tipul unui obiect folosindu-ne de o serie de valori constante predefinite precum ar fi SFERA sau CUB. Un obiect spatio-temporal este un obiect spatial care are in plus o pozitie pe axa timpului. Pentru un astfel de obiect, in afara de proprietatile deja descrise pentru obiectele spatiale, trebuie sa avem in plus un moment initial, de aparitie, pe axa timpului si un moment final. Obiectul nostru nu exista in afara acestui interval de timp. In plus, pentru un astfel de obiect putem afla pozitia centrului sau de greutate in fiecare moment aflat in intervalul de existenta. Pentru a putea lucra cu obiecte spatiale si spatio-temporale este nevoie sa definim diverse clase care sa implementeze aceste interfete. Acest lucru se face specificand clauza implements in declaratia de clasa. O clasa poate implementa mai multe interfete. Daca o clasa declara ca implementeaza o anumita interfata, ea este obligatoriu sa implementeze toate metodele declarate in interfata respectiva. De exemplu, putem spune ca o minge este un obiect spatial de tip sfera. In plus, mingea are o pozitie in functie de timp si un interval de existenta. Cu alte cuvinte, mingea este chiar un obiect spatio-temporal. Desigur, in afara de proprietatile spatio-temporale mingea mai are si alte proprietati precum culoarea, proprietarul sau pretul de cumparare. Iata cum ar putea arata definitia clasei de obiecte de tip minge: import java.awt.Color; class Minge extends Jucarie implements ObiectSpatioTemporal A int culoare = Color.red; double pret = 10000.0; double raza = 0.25; long nastere; long moarte; // metodele din ObiectSpatial double greutate() A return raza * 0.5; S double raza() A return raza; S double volum() A return ( 4.0 / 3.0 ) * Math.PI * raza * raza * raza; S int tip() A return SFERA; S// metodele din interfata ObiectSpatioTemporal boolean centrulDeGreutate( long moment, double coordonateai ) A if( moment < nastere || moment > moarte ) A return false; S . coordonatea0i = x; coordonatea1i = y; coordonatea2i = z; return true; S long momentInitial() A return nastere; S long momentFinal() A return moarte; S int ceCuloare() A return culoare; S double cePret() A return pret; S S Observati ca noua clasa Minge implementeaza toate metodele definite in interfata ObiectSpatioTemporal si, pentru ca aceasta extinde interfata ObiectSpatial, si metodele definite in cea din urma. In plus, clasa isi defineste propriile metode si variabile. Sa presupunem in continuare ca avem si o alta clasa, Rezervor, care este tot un obiect spatio-temporal, dar de forma cubica. Declaratia acestei clase ar arata ca: class Rezervor extends Constructii implements ObiectSpatioTemporal A . S Desigur, toate metodele din interfetele de mai sus trebuiesc implementate, plus alte metode specifice. Sa mai observam ca cele doua obiecte deriva din clase diferite: Mingea din Jucarii iar Rezervorul din Constructii. Daca am putea deriva o clasa din doua alte clase, am putea deriva Minge din Jucarie si ObiectSpatioTemporal iar Rezervor din Constructie si ObiectSpatioTemporal. Intr-o astfel de situatie, nu ar mai fi necesar ca ObiectSpatioTemporal sa fie o interfata, ci ar fi suficient ca acesta sa fie o alta clasa. Din pacate, in Java, o clasa nu poate deriva decat dintr-o singura alta clasa, asa ca este obligatoriu in astfel de situatii sa folosim interfetele. Daca ObiectSpatioTemporal ar fi putut fi o clasa, am fi avut avantajul ca puteam implementa acolo metodele cu functionare identica din cele doua clase discutate, acestea fiind automat mostenite fara a mai fi nevoie de definirea lor de doua ori in fiecare clasa in parte. Putem crea in continuare metode care sa lucreze cu obiecte spatio-temporale, de exemplu o metoda care sa afle distanta unui corp spatio-temporal fata de un punct dat la momentul sau initial. O astfel de metoda se poate scrie o singura data, si poate lucra cu toate clasele care implementeaza interfata noastra. De exemplu: . double distanta( double punctai, ObiectSpatioTemporal obiect ) A double coordonateai = new doublea3i; obiect.centrulDeGreutate( obiect.momentInitial(), coordonate ); double x = coordonatea0i - puncta0i; double y = coordonatea1i - puncta1i; double z = coordonatea2i - puncta2i; return Math.sqrt( x * x + y * y + z * z ); S Putem apela metoda atat cu un obiect din clasa Minge cat si cu un obiect din clasa Rezervor. Compilatorul nu se va plange pentru ca el stie ca ambele clase implementeaza interfata ObiectSpatioTemporal, asa ca metodele apelate in interiorul calculului distantei (momentInitial si centruDeGreutate) sunt cu siguranta implementate in ambele clase. Deci, putem scrie: Minge minge; Rezervor rezervor; double punctai = A 10.0, 45.0, 23.0 S; distanta( punct, minge ); distanta( punct, rezervor ); Desigur, in mod normal ar fi trebuit sa proiectam si un constructor sau mai
multi care sa initializeze obiectele noastre cu valori rezonabile. Acesti constructori
ar fi stat cu siguranta in definitia claselor si nu in definitia interfetelor.
Nu avem aici nici o cale de a forta definirea unui anumit constructor cu ajutorul
interfetei.
|
||||||
|
||||||
|
||||||
Copyright© 2005 - 2024 | Trimite document | Harta site | Adauga in favorite |
|