February 9, 2017 | Author: Badiu Mircea Gabriel | Category: N/A
Download Limbajul Java-carte in limba romana...
Limbajul JAVA -- Cuprins
Cuprins Cuvânt înainte Introducere
PARTEA I-A: Fundamentele programãrii Calculatorul este agenda, telefonul ºi televizorul secolului urmãtor. ªtiþi sã-l folosiþi? Capitolul I: Arhitectura calculatoarelor Modul în care programãm un calculator depinde direct de modul în care acesta este construit. Nu putem gândi reprezentarea ºi prelucrarea informaþiilor fãrã sã ºtim care sunt principiile constructive de bazã ale calculatorului pentru care gândim. De aceea, înainte de toate, sã încercãm sã aflãm cum funcþioneazã un calculator modern. Capitolul II: Limbaje de programare Pentru a putea programa, este necesar sã avem cu calculatorul un limbaj comun, numit limbaj de programare, cu ajutorul cãruia sã descriem informaþiile pe care dorim sã le prelucrãm ºi modul în care acestea trebuiesc prelucrate. Limbajele de programare actuale, deºi foarte diferite între ele, oferã în principiu aceleaºi tipuri de informaþie primitivã ºi aceleaºi tipuri de operaþii. Capitolul III: Reprezentarea informaþiilor cu obiecte
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/index.html (1 of 3)12.01.2006 23:07:41
Limbajul JAVA -- Cuprins
Programarea orientatã pe obiecte este cea mai nouã tehnologie de proiectare a programelor de calculator. Aceasta ne permite, în esenþã, sã modelãm informaþiile cu care lucreazã calculatorul într-un mod similar cu acela în care percepem obiectele reale: fiecare obiect este un uniune între proprietãþi ºi comportament.
PARTEA A II-A: Descrierea limbajului Java Despre un om care ºtie douã limbi se spune cã este de douã ori mai deºtept. Dar ce spuneþi despre un programator care ºtie douã limbaje? Capitolul IV: Structura lexicalã Java La începutul prezentãrii fiecãrui limbaj de programare trebuie sã definim exact setul de caractere cu care lucreazã limbajul, modul de construcþie a identificatorilor, comentariilor ºi literalilor specifici, care sunt operatorii ºi separatorii limbajului. În plus, trebuie sã avem o listã completã a cuvintelor rezervate Java. Capitolul V: Componente de bazã ale programelor Java Componentele de bazã ale oricãrui limbaj de programare sunt variabilele, expresiile ºi instrucþiunile. Ca orice alt limbaj de programare, Java îºi defineºte propriile tipuri de date, implementeazã principalele instrucþiuni ale programãrii structurate ºi îºi defineºte propriii operatori ºi modul în care pot fi folosiþi aceºtia la construcþia expresiilor. Capitolul VI: Obiecte Java Pentru a fi un limbaj orientat obiect, Java trebuie sã defineascã o cale de a crea noi clase de obiecte ºi de a le instanþia. În plus, limbajul trebuie sã ofere suport pentru principalele trãsãturi ale limbajelor orientate obiect: încapsulare ºi derivare.
PARTEA A III-A: Tehnici de programare în Java A cunoaºte o limbã nu este totul. Mai trebuie sã ai ºi ceva de spus. Capitolul VII: Modele de programare Modul în care este lansat un program ºi modul în care evolueazã execuþia
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/index.html (2 of 3)12.01.2006 23:07:41
Limbajul JAVA -- Cuprins
sa ulterioarã depinde de limbajul în care a fost scris programul ºi de mediul hardware ºi software în care ruleazã programul. Limbajul Java defineºte douã moduri diferite pentru execuþia programelor: aplicaþiile Java ºi apleturile Java. Capitolul VIII: Structura programelor În mod normal, sursele unui program complex sunt împãrþite în mai multe fiºiere pentru o administrare mai uºoarã. În Java, existã în mod normal câte un fiºier sursã pentru fiecare clasã de obiecte în parte. Limbajul Java defineºte o serie de structuri sintactice care permit conectarea codului rezultat în urma compilãrii diferitelor clase precum ºi organizarea acestor clase într-o structurã ierarhicã de pachete. Capitolul IX: Fire de execuþie ºi sincronizare În contextul sistemelor de operare moderne, lucrul cu fire multiple de execuþie este în acelaºi timp o necesitate ºi o modã. Din pãcate, fiecare sistem de operare îºi defineºte propria sa bibliotecã de funcþii pentru a suporta aceastã facilitate. Limbajul Java, pentru a fi portabil cu adevãrat, este obligat sã-ºi defineascã propriul sãu suport pentru firele multiple de execuþie. Bibliografie
JavaRo (C) IntegraSoft 1996-1998
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/index.html (3 of 3)12.01.2006 23:07:41
Limbajul JAVA -- IntegraSoft
Cuvânt înainte Deºi trãim într-o societate în care rata de schimb a tehnologiei a ajuns sã ne depãºeascã, existã domenii care se schimbã mult prea lent faþã de aºteptãrile noastre. Sã luãm de exemplu calculatoarele. Nu existã zi în care sã nu auzim de noutãþi în ceea ce priveºte viteza, numãrul de culori sau miniaturizarea. Nu existã zi în care sã nu auzim de noi aplicaþii ºi de noi domenii în care a fost introdusã informatica. ªi totuºi, nimic esenþial nu s-a schimbat în partea de fundamente. Aceeaºi arhitecturã numericã guverneazã întreg spectrul de calculatoare aflate azi pe piaþã ca ºi acum jumãtate de secol. În ceea ce priveºte comunicaþia om-calculator, lucrurile nu stau cu mult mai bine. Deºi nu mai comunicãm folosindu-ne de cifre binare ºi nici în limbaje apropriate de maºinã, comunicãm în continuare folosindu-ne de câteva primitive structurale de tip bucle sau condiþii plus directive de calcul ºi transfer al informaþiilor. Abstracþii precum programarea logicã, funcþionalã sau orientatã obiect nu extind blocurile de bazã cu care acþionãm asupra maºinii, le fac doar mai accesibile pentru modul nostru de a gândi. Într-un fel, programarea face exact aceeaºi greºealã pe care a fãcut-o ºi logica clasicã statuând cã orice enunþ nu poate fi decât adevãrat sau fals, fãrã nici o altã nuanþare. În pasul imediat urmãtor s-au stabilit câteva enunþuri adevãrate fãrã demonstraþie ºi s-a considerat cã toate celelalte enunþuri trebuie deduse din ele. Programarea a fãcut aceleaºi presupuneri în ceea ce priveºte comunicaþia om-calculator: existã câteva primitive funcþionale de bazã ºi toatã comunicaþia trebuie sã poatã fi exprimatã cu ajutorul acestora. În aceste condiþii, este normal ca apariþia fiecãrui nou limbaj de programare sã trezeascã un interes major în lumea informaticã. De fiecare datã, sperãm cã noul limbaj ne va permite o exprimare mai uºoarã, mai flexibilã, mai bogatã. De aceea, la apariþia fiecãrui limbaj de programare care promite sã iasã din anonimat, ne grãbim sã aflãm care sunt principalele facilitãþi care ni se oferã. Apariþia limbajului Java a fost însoþitã de multã publicitate ºi de mult scepticism. Pãrerile au variat de la a spune cã Java este o revoluþie în programarea calculatoarelor ºi, mai ales, a reþelelor de calculatoare pânã la afirmaþii care neagã orice caracter novator al noului limbaj ºi care pun succesul Java în exclusivitate pe seama renumelui de care se bucurã firma Sun ºi a campaniei publicitare inteligent condusã de marketingul acestei firme. Indiferent însã de motivul pentru care limbajul Java a ajuns la cota extraordinarã de popularitate pe care o simþim cu toþii, sentimentul general este acela cã Java a devenit deja o realitate ºi va rãmâne aºa pentru suficient de mult timp. Suficient de mult pentru a fi interesaþi sã apreciem în cunoºtinþã de cauzã care este adevãrul despre acest nou limbaj. Oricum, de la apariþia limbajului C++, acum mai bine de un deceniu în urmã, nici un alt limbaj nu a înnegrit atâta hârtie ºi nu a adus atâtea beneficii vânzãtorilor de servicii Internet. Sper ca aceastã carte sã ofere suportul de care aveþi nevoie pentru a vã putea forma propria pãrere: avem file:///C|/Documents%20and%20Settings/Luminita/Desktop...20romana/Carte%20JAVA%20in%20romana/cuvantinainte.html (1 of 2)12.01.2006 23:07:42
Limbajul JAVA -- IntegraSoft
în faþã o revoluþie sau un alt fapt comun? Târgu Mureº, 24 aprilie 1996 [cuprins] (C) IntegraSoft 1996-1998
file:///C|/Documents%20and%20Settings/Luminita/Desktop...20romana/Carte%20JAVA%20in%20romana/cuvantinainte.html (2 of 2)12.01.2006 23:07:42
Limbajul JAVA -- IntegraSoft
Introducere Scurt istoric Ce este Java? Despre aceastã carte Convenþii utilizate în aceastã carte Sugestii ºi reclamaþii Alte surse de informaþii Mulþumiri
Scurt istoric Limbajul Java împreunã cu mediul sãu de dezvoltare ºi execuþie au fost proiectate pentru a rezolva o parte dintre problemele actuale ale programãrii. Proiectul Java a pornit cu scopul declarat de a dezvolta un software performant pentru aparatele electronice de larg consum. Aceste echipamente se definesc ca: mici, portabile, distribuite ºi lucrând în timp real. De la aceste aparate, ne-am obiºnuit sã cerem fiabilitate ºi uºurinþã în exploatare. Limbajul luat iniþial în considerare a fost C++. Din pãcate, atunci când s-a încercat crearea unui mediu de execuþie care sã respecte toate aceste condiþii s-a observat cã o serie de trãsãturi ale C++ sunt incompatibile cu necesitãþile declarate. În principal, problema vine din faptul cã C++ este prea complicat, foloseºte mult prea multe convenþii ºi are încã prea multe elemente de definiþie lãsate la latitudinea celor care scriu compilatoare pentru o platformã sau alta. În aceste condiþii, firma Sun a pornit proiectarea unui nou limbaj de programare asemãnãtor cu C++ dar mult mai flexibil, mai simplu ºi mai portabil. Aºa s-a nãscut Java. Pãrintele noului limbaj a fost James Gostling care vã este poate cunoscut ca autor al editorului emacs ºi al sistemului de ferestre grafice NeWS. Proiectul a început încã din 1990 dar Sun a fãcut publicã specificaþia noului limbaj abia în 1995 la SunWorld în San Francisco. Numele iniþial al limbajului a fost Oak, numele unui copac care creºte în faþa biroului lui James Gostling. Ulterior, s-a descoperit cã numele fusese deja folosit în trecut pentru un alt limbaj de programare aºa cã a fost abandonat ºi înlocuit cu Java, spre deliciul programatorilor care iubesc cafenelele ºi aromele exotice.
Ce este Java? În primul rând, Java încearcã sã rãmânã un limbaj simplu de folosit chiar ºi de cãtre programatorii neprofesioniºti, programatori care doresc sã se concentreze asupra aplicaþiilor în principal ºi abia apoi asupra tehnicilor de implementare a acestora. Aceastã trãsãturã poate fi consideratã ca o reacþie directã file:///C|/Documents%20and%20Settings/Luminita/Deskto...%20romana/Carte%20JAVA%20in%20romana/introducere.html (1 of 7)12.01.2006 23:07:42
Limbajul JAVA -- IntegraSoft
la complexitatea considerabilã a limbajului C++. Au fost îndepãrtate din Java aspectele cele mai derutante din C++ precum supraîncãrcarea operatorilor ºi moºtenirea multiplã. A fost introdus un colector automat de gunoaie care sã rezolve problema dealocãrii memoriei în mod uniform, fãrã intervenþia programatorului. Colectorul de gunoaie nu este o trãsãturã nouã, dar implementarea acestuia în Java este fãcutã inteligent ºi eficient folosind un fir separat de execuþie, pentru cã Java are încorporate facilitãþi de execuþie pe mai multe fire de execuþie. Astfel, colectarea gunoaielor se face de obicei în timp ce un alt fir aºteaptã o operaþie de intrare-ieºire sau pe un semafor. Limbajul Java este independent de arhitectura calculatorului pe care lucreazã ºi foarte portabil. În loc sã genereze cod nativ pentru o platformã sau alta, compilatorul Java genereazã o secvenþã de instrucþiuni ale unei maºini virtuale Java. Execuþia aplicaþiilor Java este interpretatã. Singura parte din mediul de execuþie Java care trebuie portatã de pe o arhitecturã pe alta este mediul de execuþie cuprinzând interpretorul ºi o parte din bibliotecile standard care depind de sistem. În acest fel, aplicaþii Java compilate pe o arhitecturã SPARC de exemplu, pot fi rulate fãrã recompilare pe un sistem bazat pe procesoare Intel. Una dintre principalele probleme ale limbajelor interpretate este viteza de execuþie, considerabil scãzutã faþã de cea a limbajelor compilate. Dacã nu vã mulþumeºte viteza de execuþie a unei astfel de aplicaþii, puteþi cere mediului de execuþie Java sã genereze automat, plecând de la codul maºinii virtuale, codul specific maºinii pe care lucraþi, obþinându-se astfel un executabil nativ care poate rula la vitezã maximã. De obicei însã, în Java se compileazã doar acele pãrþi ale programului mari consumatoare de timp, restul rãmânând interpretate pentru a nu se pierde flexibilitatea. Mediul de execuþie însuºi este scris în C respectând standardele POSIX, ceea ce îl face extrem de portabil. Interpretorul Java este gândit sã lucreze pe maºini mici, precum ar fi procesoarele cu care sunt dotate aparatele casnice. Interpretorul plus bibliotecile standard cu legare dinamicã nu depãºesc 300 Kocteþi. Chiar împreunã cu interfaþa graficã totul rãmâne mult sub 1 Moctet, exact ca-n vremurile bune. Limbajul Java este orientat obiect. Cu el se pot crea clase de obiecte ºi instanþe ale acestora, se pot încapsula informaþiile, se pot moºteni variabilele ºi metodele de la o clasã la alta, etc. Singura trãsãturã specificã limbajelor orientate obiect care lipseºte este moºtenirea multiplã, dar pentru a suplini aceastã lipsã, Java oferã o facilitate mai simplã, numitã interfaþã, care permite definirea unui anumit comportament pentru o clasã de obiecte, altul decât cel definit de clasa de bazã. În Java orice element este un obiect, în afarã de datele primare. Din Java lipsesc funcþiile ºi variabilele globale. Ne rãmân desigur metodele ºi variabilele statice ale claselor. Java este distribuit, având implementate biblioteci pentru lucrul în reþea care ne oferã TCP/IP, URL ºi încãrcarea resurselor din reþea. Aplicaþiile Java pot accesa foarte uºor reþeaua, folosindu-se de apelurile cãtre un set standard de clase. Java este robust. În Java legarea funcþiilor se face în timpul execuþiei ºi informaþiile de compilare sunt file:///C|/Documents%20and%20Settings/Luminita/Deskto...%20romana/Carte%20JAVA%20in%20romana/introducere.html (2 of 7)12.01.2006 23:07:42
Limbajul JAVA -- IntegraSoft
disponibile pânã în momentul rulãrii aplicaþiei. Acest mod de lucru face ca sistemul sã poatã determina în orice moment neconcordanþa dintre tipul referit la compilare ºi cel referit în timpul execuþiei evitânduse astfel posibile intruziuni rãuvoitoare în sistem prin intermediul unor referinþe falsificate. În acelaºi timp, Java detecteazã referinþele nule dacã acestea sunt folosite în operaþii de acces. Indicii în tablourile Java sunt verificaþi permanent în timpul execuþiei ºi tablourile nu se pot parcurge prin intermediul unor pointeri aºa cum se întâmplã în C/C++. De altfel, pointerii lipsesc complet din limbajul Java, împreunã cu întreaga lor aritmeticã, eliminându-se astfel una din principalele surse de erori. În plus, eliberarea memoriei ocupate de obiecte ºi tablouri se face automat, prin mecanismul de colectare de gunoaie, evitându-se astfel încercãrile de eliberare multiplã a unei zone de memorie. Java este un limbaj cu securitate ridicatã. El verificã la fiecare încãrcare codul prin mecanisme de CRC ºi prin verificarea operaþiilor disponibile pentru fiecare set de obiecte. Robusteþea este ºi ea o trãsãturã de securitate. La un al doilea nivel, Java are incorporate facilitãþi de protecþie a obiectelor din sistem la scriere ºi/sau citire. Variabilele protejate într-un obiect Java nu pot fi accesate fãrã a avea drepturile necesare, verificarea fiind fãcutã în timpul execuþiei. În plus, mediul de execuþie Java poate fi configurat pentru a proteja reþeaua localã, fiºierele ºi celelalte resurse ale calculatorului pe care ruleazã o aplicaþie Java. Limbajul Java are inclus suportul nativ pentru aplicaþii care lucreazã cu mai multe fire de execuþie, inclusiv primitive de sincronizare între firele de execuþie. Acest suport este independent de sistemul de operare, dar poate fi conectat, pentru o performanþã mai bunã, la facilitãþile sistemului dacã acestea existã. Java este dinamic. Bibliotecile de clase în Java pot fi reutilizate cu foarte mare uºurinþã. Cunoscuta problemã a fragilitãþii superclasei este rezolvatã mai bine decât în C++. Acolo, dacã o superclasã este modificatã, trebuie recompilate toate subclasele acesteia pentru cã obiectele au o altã structurã în memorie. În Java aceastã problemã este rezolvatã prin legarea târzie variabilelor, doar la execuþie. Regãsirea variabilelor se face prin nume ºi nu printr-un deplasament fix. Dacã superclasa nu a ºters o parte dintre vechile variabile ºi metode, ea va putea fi refolositã fãrã sã fie necesarã recompilarea subclaselor acesteia. Se eliminã astfel necesitatea actualizãrii aplicaþiilor, generatã de apariþia unei noi versiuni de bibliotecã aºa cum se întâmplã, de exemplu, cu MFC-ul Microsoft (ºi toate celelalte ierarhii C ++).
Despre aceastã carte Aceastã carte a fost scrisã pentru a veni în sprijinul acelora care doresc sã programeze în limbajul Java. Spre deosebire de majoritatea cãrþilor existente la ora actualã pe piaþa internaþionalã, nu prea multe de altfel, cartea de faþã se focalizeazã asupra facilitãþilor pe care le oferã noul limbaj, lãsând pe planul al doilea descrierea bibliotecilor standard, impresionante de altfel, definite de cãtre Sun ºi colaboratorii acestuia. Alegerea a fost fãcutã din convingerea cã bibliotecile vin ºi trec, se dezvoltã, în timp ce limbajul rãmâne.
file:///C|/Documents%20and%20Settings/Luminita/Deskto...%20romana/Carte%20JAVA%20in%20romana/introducere.html (3 of 7)12.01.2006 23:07:42
Limbajul JAVA -- IntegraSoft
Cartea se adreseazã în acelaºi timp începãtorilor ºi programatorilor profesioniºti. Începãtorii vor gãsi în prima parte noþiuni fundamentale necesare oricãrui programator. Profesioniºtii, în schimb, vor gãsi o referinþã completã a limbajului Java. Prima parte a cãrþii îºi introduce cititorul pas cu pas în fundamentele funcþionãrii ºi programãrii calculatoarelor tratând la un nivel accesibil noþiuni precum memoria ºi procesorul, datele ºi instrucþiunile, clasele de obiecte împreunã cu trãsãturile fundamentale ale programãrii orientate obiect. Partea a doua a cãrþii prezintã limbajul Java în detaliu împreunã cu o serie de exemple simple. Aceastã parte este conceputã pentru a fi în acelaºi timp o introducere în sintaxa Java pentru cei care au programat deja ºi o introducere în constrângerile sintactice ºi semantica unui limbaj de programare pentru începãtori. Partea a treia a cãrþii prezintã câteva dintre aspectele fundamentale ale programãrii în Java precum aplicaþiile, apleturile, pachetele de clase, firele de execuþie ºi tratarea excepþiilor. Aceastã parte este conceputã de sine stãtãtoare ºi poate servi ca exemplu de programare în Java. Fiecare secþiune din aceastã parte conþine exemple extinse de aplicaþii scrise în Java, comentate în sursã ºi în afara acesteia. Cititorul începãtor în ale programãrii trebuie sã parcurgã cartea de la început pânã la sfârºit pentru a putea intra treptat în tainele programãrii în general ºi ale limbajului Java. Pentru cititorii avansaþi, prima parte nu constituie un interes major ºi poate fi sãritã fãrã implicaþii majore în înþelegerea materialului din pãrþile urmãtoare. Aceºti cititori pot sã treacã direct la partea a treia pentru a urmãri exemplele ºi abia dupã aceea sã revinã la partea a doua pentru informaþii detaliate asupra sintaxei ºi facilitãþilor Java. Partea a doua poate fi folositã ºi pe post de referinþã. O singurã excepþie: ultima secþiune din prima parte introduce noþiunea de interfaþã, puþin cunoscutã programatorilor în alte limbaje decât Java. Este util ca aceastã secþiune sã fie consultatã de cãtre toþi cititorii, indiferent de nivelul de pregãtire în care se aflã.
Convenþii utilizate în aceastã carte Pentru descrierea sintacticii limbajului Java, am apelat la urmãtoarele convenþii obiºnuite de reprezentare a regulilor gramaticale: ● ●
Cuvintele rezervate ale limbajului Java sunt scrise în caractere îngroºate, ca: while, do, final. Cuvintele care þin locul construcþiilor reale ale programului sunt scrise cu caractere oblice, ca în: if(Condiþie) Instrucþiune1 else Instrucþiune2 Condiþie, Instrucþiune1 ºi Instrucþiune2, care apar în descriere, trebuiesc înlocuite cu condiþiile adevãrate, respectiv cu instrucþiunile adevãrate care trebuiesc executate în funcþie de condiþie. De exemplu, o construcþie realã, care respectã descrierea de mai sus ar putea fi:
file:///C|/Documents%20and%20Settings/Luminita/Deskto...%20romana/Carte%20JAVA%20in%20romana/introducere.html (4 of 7)12.01.2006 23:07:42
Limbajul JAVA -- IntegraSoft
if( i < j ) min = i; else min = j; ●
O listã de termeni separaþi prin caractere |, se poate înlocui cu oricare dintre termenii listei. De exemplu: public | private | protected înseamnã cã în locul acestui termen poate sã aparã oricare dintre cuvintele rezervate specificate în listã, public, private sau protected.
●
Un termen pus între paranteze pãtrate este opþional. De exemplu, în descrierea: Tip NumeVariabilã [Iniþializator] ; Partea de Iniþializator poate sã lipseascã.
●
Dacã, dupã un termen apare caracterul *, acesta reprezintã faptul cã termenul se poate repeta de un numãr arbitrar de ori, eventual niciodatã. De exemplu, în: class NumeClasã ClauzãImplements* caracterul * reprezintã faptul cã termenul notat prin ClauzãImplements se poate repeta de un numãr arbitrar de ori, eventual niciodatã. Pe marginea din stânga a paginii, veþi întâlni o serie de simboluri grafice, cu specificaþie precisã, care vã vor permite sã navigaþi mai uºor în interiorul cãrþii. Iatã semnificaþia acestora:
Semnul din stânga reprezintã faptul cã în paragraful marcat introduce definiþia unui nou termen. Termenul nou introdus este reprezentat în interiorul semnului ºi este scris cu caractere oblice în textul paragrafului. În cazul de faþã cuvântul nou introdus este cuvântul definiþia. Atunci când gãsiþi o trimitere la un paragraf pentru explicaþii, cãutaþi acest semn pe margine pentru a gãsi mai repede definiþia noþiunii dorite. Semnul din stânga reprezintã o trimitere înapoi cãtre o noþiune deja prezentatã în partea de fundamente. Aceastã trimitere conþine deasupra simbolului grafic numele noþiunii pe care trebuie sã îl cãutaþi într-un simbol de definiþie, ºi sub simbolul grafic numãrul paragrafului unde se aflã definiþia. Cititorul file:///C|/Documents%20and%20Settings/Luminita/Deskto...%20romana/Carte%20JAVA%20in%20romana/introducere.html (5 of 7)12.01.2006 23:07:42
Limbajul JAVA -- IntegraSoft
începãtor ca ºi cel avansat poate urma aceste trimiteri pentru a-ºi împrospãta cunoºtinþele sau pentru a se familiariza cu cuvintele sau expresiile folosite pentru a desemna noþiuni greu traductibile. Semnul din stânga reprezintã o referire în avans a unei noþiuni care va fi introdusã mai târziu. Acest semn este destul de rar în interiorul cãrþii, dar este totuºi nevoie de el pentru a face referire la anumite facilitãþi care îºi gãsesc cel mai bine locul în paragraful în care apare semnul. Puteþi sãri fãrã probleme aceste precizãri la prima citire a capitolului ºi sã le luaþi în seamã doar atunci când folosiþi cartea pe post de referinþã pentru limbajul Java. Semnul din stânga reprezintã referirea unei noþiuni care nu este descrisã în carte. Acest semn apare atunci când se face o referinþã la una din clasele standard ale mediului Java. Puteþi gãsi informaþii exacte despre clasa respectivã la adresa www.javasoft.com, sau puteþi încãrca din Internet o documentaþie completã prin ftp de la adresa ftp.javasoft.com, directorul /docs. Semnul din stânga reprezintã o trimitere cãtre altã carte, în care noþiunile din paragraf sunt prezentate mai pe larg. Am folosit acest semn doar de douã ori, pentru a trimite cãtre o carte disponibilã la aceeaºi editurã cu cartea prezentã, despre HTML, scrisã de Dumitru Rãdoiu. O puteþi gãsi pe prima poziþie în bibliografie.
Sugestii ºi reclamaþii Posibilele erori care s-au strecurat în aceastã carte cad în sarcina exclusivã a autorului ei care îºi cere pe aceastã cale scuze în avans. Orice astfel de eroare sau neclaritate cu privire la conþinutul cãrþii poate fi comunicatã direct autorului pe adresa
[email protected] sau: Eugen Rotariu Computer Press Agora Str. Tudor Vladimirescu, Nr. 63/1, Cod. 4300, Târgu Mureº România În Internet, puteþi sã contactaþi editura Computer Press Agora la adresa www.agora.ro. Pe server existã o paginã separatã dedicatã acestei cãrþi. Puteþi verifica periodic aceastã paginã pentru eventuale corecturi, exemple suplimentare sau adãugiri la conþinutul cãrþii.
Alte surse de informaþii Subiectul Java este departe de a fi încheiat în aceastã carte aºa cã, dacã reacþia dumneavoastrã este pozitivã, voi încerca sã construiesc o a doua carte dedicatã claselor standard Java, mediilor de dezvoltare, din ce în ce mai numeroase, ierarhiilor de bazã de date, obiecte distribuite ºi aºa mai departe care ºi-au fãcut apariþia de la Sun sau din altã parte în ultimul timp. Nu ezitaþi sã-mi daþi de ºtire dacã veþi considera utilã o asemenea continuare. file:///C|/Documents%20and%20Settings/Luminita/Deskto...%20romana/Carte%20JAVA%20in%20romana/introducere.html (6 of 7)12.01.2006 23:07:42
Limbajul JAVA -- IntegraSoft
Pânã atunci însã, puteþi consulta cãrþile listate în secþiunea de bibliografie de la sfârºitul acestei cãrþi. Numai cãrþi în limba englezã în acest domeniu deocamdatã, dar mã aºtept ca situaþia sã se schimbe dramatic în perioada imediat urmãtoare. Noi cãrþi de Java dedicate cititorilor români nu vor întârzia sã aparã. Internet-ul este de asemenea o sursã interminabilã de informaþii, situri Java existã ºi vor continua sã aparã în întreaga lume. Adresa de bazã este probabil www.javasoft.com, adresã care vã pune în legãturã directã cu firma care a creat ºi întreþine în continuare dezvoltarea Java. În plus, puteþi consulta revista electronicã JavaWorld de la adresa www.javaworld.com care conþine întotdeauna informaþii fierbinþi, cursuri, apleturi ºi legãturi cãtre alte adrese unde vã puteþi îmbogãþi cunoºtinþele despre Java. Pentru documentaþii, exemple ºi noutãþi în lumea Java puteþi consulta ºi www.gamelan.com sau www. blackdown.org. Pentru cei care doresc sã dezvolte apleturi pe care sã le insereze în propriile pagini HTML, recomand în plus citirea cãrþii lui Dumitru Rãdoiu, HTML - Publicaþii Web, editatã de asemenea la Computer Press Agora. În fine, nu încetaþi sã cumpãraþi revistele PC Report ºi Byte România ale aceleiaºi edituri, pentru cã ele vor conþine ca de obicei informaþii de ultimã orã, cursuri ºi reportaje de la cele mai noi evenimente din lumea calculatoarelor.
Mulþumiri Mulþumesc tuturor celor care, voit sau nu, au fãcut posibilã aceastã carte. Mulþumesc celor de la editura Computer Press Agora ºi managerului ei Romulus Maier pentru cã mi-au facilitat publicarea acestei cãrþi. Fãrã munca lor, cartea s-ar fi aflat în continuare în vitrina proprie cu visuri nerealizate. Mulþumesc celor care mi-au dãruit o parte din timpul lor preþios pentru a citi ºi comenta primele versiuni ale cãrþii: Iosif Fettich, Mircea ºi Monica Cioatã, Alexandru Horvath. Mulþumesc celor cu care am discutat de atâtea ori despre soarta calculatoarelor, programelor, României ºi lumii în general. Ei au fãcut din mine omul care sunt acum: Mircea Sârbu, Dumitru Rãdoiu, Szabo Laszlo, Mircea Pantea. Mulþumesc celor care s-au ocupat de designul ºi tehnoredactarea acestei cãrþi în frunte cu Adrian Pop ºi Octav Lipovan. Cel dintâi lucru care v-a atras la aceastã carte este munca lor. Carmen, îþi mulþumesc cã nu te-ai dat bãtutã. Va veni ºi vremea în care sãrbãtorim cu calculatoarele oprite. [cuprins] (C) IntegraSoft 1996-1998
file:///C|/Documents%20and%20Settings/Luminita/Deskto...%20romana/Carte%20JAVA%20in%20romana/introducere.html (7 of 7)12.01.2006 23:07:42
Capitolul I -- Arhitectura calculatoarelor
Capitolul I Arhitectura calculatoarelor 1.1 Modelul Von Neumann de arhitecturã a calculatoarelor 1.2 Organizarea memoriei interne 1.3 Reprezentarea informaþiilor în memoria internã 1.4 Modelul funcþional al calculatoarelor
1.1 Modelul Von Neumann de arhitecturã a calculatoarelor Descrierea care urmeazã este o descriere a modelului Von Neumann de construcþie a calculatoarelor. Se pot aduce destule critici acestui model care dominã sever încã de la începuturile maºinilor de calcul electronic, dar el continuã sã fie singurul model funcþional. Modelul Von Neumann defineºte calculatorul ca pe un ansamblu format dintr-o unitate centralã ºi o memorie internã. Unitatea centralã sau procesorul este responsabilã cu administrarea ºi prelucrarea informaþiilor în timp ce memoria internã serveºte la depozitarea acestora. În terminologia calculatoarelor, depozitarea informaþiilor în memoria internã a calculatorului se numeºte memorare. Acest ansamblu unitate centralã plus memorie internã comunicã cu exteriorul prin intermediul unor dispozitive periferice. Dispozitivele periferice pot fi de intrare sau de ieºire în funcþie de direcþia în care se miºcã datele. Dacã datele sunt furnizate de dispozitivul periferic ºi transferate spre unitatea centralã, atunci dispozitivul este de intrare precum sunt tastatura sau mausul. Dacã datele sunt generate de unitatea centralã ºi transmise spre dispozitivul periferic atunci dispozitivul este de ieºire precum sunt ecranul sau imprimanta. Existã ºi dispozitive mixte de intrare/ieºire precum sunt discurile pentru memorarea externã a informaþiilor. Tastatura calculatorului reprezintã un set de butoane (taste) inscripþionate care ne permite transmiterea cãtre unitatea centralã a unor litere, cifre, semne de punctuaþie, simboluri grafice sau comenzi funcþionale. Mausul reprezintã un dispozitiv simplu, mobil, care ne permite indicarea unor regiuni ale ecranului cu ajutorul unui cursor. În plus, mausul ne permite activarea regiunilor respective cu ajutorul celor 1-2-3 butoane ale sale. Ecranul este un dispozitiv cu ajutorul cãruia calculatorul comunicã informaþii spre exterior. Aceste informaþii apar sub formã de litere, cifre, semne de punctuaþie, simboluri grafice sau desene oarecare întro varietate mai mare sau mai micã de culori. Informaþia de pe ecran se pierde odatã cu redesenarea acestuia. Pentru transferul acestor informaþii pe hârtie ºi îndosarierea lor s-au creat alte dispozitive periferice, numite imprimante.
file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap1.html (1 of 5)12.01.2006 23:07:43
Capitolul I -- Arhitectura calculatoarelor
Memoria internã pierde informaþiile odatã cu oprirea alimentãrii calculatorului. Pentru a salva informaþiile utile, precum ºi programele de prelucrare ale acestora este nevoie de dispozitive de memorare permanente. Din aceastã categorie fac parte discurile calculatorului. Existã mai multe modele de discuri precum discurile fixe, discurile flexibile sau compact-discurile, fiecare dintre acestea având caracteristici, viteze de acces ºi capacitãþi de memorare diferite. Informaþiile salvate pe discuri pot fi încãrcate din nou în memoria internã la o pornire ulterioarã a calculatorului. Vã veþi întreba desigur de ce este nevoie de douã tipuri distincte de memorie: pentru cã discurile au viteze de acces mult prea mici pentru a putea fi folosite direct de cãtre unitatea centralã.
Figura 1.1 Modelul constructiv al calculatoarelor Von Neuman.
Deºi modelul constructiv de bazã al calculatoarelor nu a evoluat prea mult, componenta tehnologicã a acestora s-a aflat într-o permanentã evoluþie. Transformarea vizeazã la ora actualã viteza de lucru ºi setul de instrucþiuni ale unitãþii centrale, capacitatea ºi viteza de stocare a memoriei interne precum ºi tipurile ºi calitatea dispozitivelor periferice.
1.2 Organizarea memoriei interne Memoria calculatoarelor actuale este, din punct de vedere logic, o înºiruire de cifre binare, 0 sau 1. Alegerea bazei 2 de numeraþie are în principal raþiuni constructive: este mult mai uºor ºi mult mai fiabil
file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap1.html (2 of 5)12.01.2006 23:07:43
Capitolul I -- Arhitectura calculatoarelor
sã reprezinþi un principiu binar ca absenþa/prezenþa sau plus/minus decât unul nuanþat. O cifrã binarã este numitã, în termeni de calculatoare, bit. Biþii sunt grupaþi, opt câte opt, în unitãþi de memorare numite octeþi. Iarãºi, alegerea cifrei opt are raþiuni istorice: era nevoie de o putere a lui doi care sã fie cât mai mare, pentru a putea permite transferuri rapide între diversele componente ale calculatorului (unitate centralã, memorie, dispozitive periferice), dar totodatã suficient de micã pentru ca realizarea dispozitivelor implicate, cu tehnologia existentã, sã fie posibilã. Cifra opt avea în plus avantajul cã permitea reprezentarea tuturor caracterelor tipãribile necesare la ora respectivã precum: literele, cifrele sau semnele de punctuaþie. Într-un octet se pot reprezenta pânã la 256 (28) astfel de caractere. În prezent octetul este depãºit datoritã necesitãþii de reprezentare a caracterelor tuturor limbilor scrise din lume. Pentru a accesa o informaþie în memorie este nevoie de un mod de a referi poziþia acesteia. Din acest motiv, octeþii memoriei au fost numerotaþi unul câte unul începând de la 0 pânã la numãrul maxim de octeþi în memorie. Numãrul de ordine al unui octet îl vom numi pentru moment adresã. Noþiunea de adresã ºi-a extins semnificaþia în ultimul timp dar, pentru înþelegerea acestui capitol, explicaþia de mai sus este suficientã. Zona fizicã de memorie rezervatã unei anumite informaþii se numeºte locaþia informaþiei respective în memorie. În unele dintre locaþiile din memorie putem pãstra chiar adresa unor alte locaþii din memorie. Informaþia memoratã în aceste locaþii se numeºte referinþã. Cu alte cuvinte, o referinþã este o informaþie memoratã într-o locaþie de memorie care ne trimite spre (se referã la) o altã locaþie de memorie. O locaþie de memorie se poate întinde pe mai mult decât un octet.
1.3 Reprezentarea informaþiilor în memoria internã Dupã cum aþi putut observa din cele prezentate pânã acum, în memoria calculatorului nu se pot înscrie decât numere naturale. Mai precis, fiecare octet de memorie poate memora un numãr de la 0 la 28-1, adicã 255. Orice altã informaþie pe care dorim sã o reprezentãm în memoria calculatorului trebuie redusã la unul sau mai multe numere naturale mici. Aceastã trãsãturã a modelului actual de memorare introduce un pas suplimentar de abstractizare în procesul de proiectare de aplicaþii, ºi anume pasul în care se construieºte un model de reprezentare în memorie a datelor, necesar aplicaþiei. Sã presupunem, de exemplu, cã o aplicaþie necesitã reprezentarea în memoria calculatorului a unui set de culori. Pentru memorarea acestor culori este nevoie de o convenþie care sã stabileascã o corespondenþã biunivocã între setul de culori ºi setul de numere naturale folosite la reprezentarea acestora. Corespondenþa este biunivocã pentru cã ea trebuie sã ofere posibilitatea de a regãsi în mod unic o culoare plecând de la un numãr ºi, în acelaºi timp, sã ofere o reprezentare unicã, sub formã de numãr, pentru fiecare culoare. De exemplu, putem pune în corespondenþã culoarea neagrã cu numãrul 0, culoarea roºie file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap1.html (3 of 5)12.01.2006 23:07:43
Capitolul I -- Arhitectura calculatoarelor
cu numãrul 1, culoarea albastrã cu numãrul 2 ºi aºa mai departe. Ori de câte ori vom memora 2 vom memora albastru ºi ori de câte ori vom dori sã memorãm roºu, vom memora 1. Ca rezultat al folosirii acestei abstractizãri, datele aplicaþiei devin dependente de convenþia de reprezentare utilizatã. Presupunând cã o aplicaþie construieºte în memorie o imagine graficã folosindu-se de o anumitã corespondenþã dintre culori ºi numere, oricare altã aplicaþie care vrea sã utilizeze imaginea respectivã trebuie sã foloseascã aceeaºi convenþie. O cale similarã de rezolvare vom întâlni ºi la reprezentarea caracterelor. Caracterele sunt denumirea întrun singur cuvânt a literelor, cifrelor, semnelor de punctuaþie sau simbolurilor grafice reprezentate în memorie. Este nevoie de o convenþie prin care ataºãm fiecãrui caracter câte un numãr natural memorabil într-un octet. În cazul reprezentãrii caracterelor, existã chiar un standard internaþional care defineºte numerele, reprezentabile pe un octet, corespunzãtoare fiecãrui caracter în parte, numit standardul ASCII. Alte standarde, cum ar fi standardul Unicode, reprezintã caracterele pe doi octeþi, ceea ce le dã posibilitatea sã ia în considerare o gamã mult mai largã de caractere.
Caracter
Reprezentare pe un octet (ASCII)
A-Z
65-90
a-z
97-122
0-9
48-57
Ã,ã
195,227
Î,î
206,238
Â,â
194,226
ª,º
170,186
Þ,þ
222,254
Tabelul 1.1 Un fragment din codurile ASCII ºi Unicode de reprezentare a caracterelor grafice în memoria calculatoarelor.
Desigur, este greu sã þinem minte codul numeric asociat fiecãrui caracter sau fiecãrei culori. Este nevoie de paºi suplimentari de codificare, care sã punã informaþia în legãturã cu simboluri mai uºor de þinut minte decât numerele. De exemplu, este mult mai uºor pentru noi sã þinem minte cuvinte sau imagini. Dar sã nu uitãm niciodatã cã, pentru calculator, cel mai uºor este sã memoreze ºi sã lucreze cu numere.
1.4 Modelul funcþional al calculatoarelor file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap1.html (4 of 5)12.01.2006 23:07:43
Capitolul I -- Arhitectura calculatoarelor
Fiecare calculator defineºte un numãr de operaþii care pot fi executate de unitatea sa centralã. Aceste operaþii sunt în principal destinate memorãrii sau recuperãrii informaþiilor din memoria internã, calculelor aritmetice sau logice ºi controlului dispozitivelor periferice. În plus, existã un numãr de instrucþiuni pentru controlul ordinii în care sunt executate operaþiile. O instrucþiune este o operaþie elementarã executabilã de cãtre unitatea centralã a unui calculator. O secvenþã de mai multe instrucþiuni executate una dupã cealaltã o vom numi program. Instrucþiunile care compun un program trebuiesc ºi ele reprezentate în memorie, la fel ca orice altã informaþie, din cauza faptului cã unitatea centralã nu are posibilitatea sã-ºi pãstreze programele în interior. Pentru memorarea acestor instrucþiuni este nevoie de o nouã convenþie de reprezentare care sã asocieze un numãr sau o secvenþã de numere naturale fiecãrei instrucþiuni a unitãþii centrale. Execuþia unui program de cãtre calculator presupune încãrcarea instrucþiunilor în memoria internã ºi execuþia acestora una câte una în unitatea centralã. Unitatea centralã citeºte din memorie câte o instrucþiune, o executã, dupã care trece la urmãtoarea instrucþiune. Pentru pãstrarea secvenþei, unitatea centralã memoreazã în permanenþã o referinþã cãtre urmãtoarea instrucþiune într-o locaþie internã numitã indicator de instrucþiuni. Modelul de execuþie liniarã a instrucþiunilor, în ordinea în care acestea sunt aºezate în memorie, este departe de a fi acceptabil. Pentru a fi util, un program trebuie sã poatã sã ia decizii de schimbare a instrucþiunii urmãtoare în funcþie de informaþiile pe care le prelucreazã. Aceste decizii pot însemna uneori comutarea execuþiei de la o secvenþã de instrucþiuni la alta. Alteori, este necesar sã putem executa o secvenþã de instrucþiuni în mod repetat pânã când este îndeplinitã o anumitã condiþie exprimabilã cu ajutorul informaþiilor din memorie. Numãrul de repetãri ale secvenþei de instrucþiuni nu poate fi hotãrât decât în momentul execuþiei. Aceste ramificãri ale execuþiei se pot simula destul de uºor prin schimbarea valorii referinþei memorate în indicatorul de instrucþiuni. [cuprins] (C) IntegraSoft 1996-1998
file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap1.html (5 of 5)12.01.2006 23:07:43
Capitolul II -- Limbaje de programare
Capitolul II Limbaje de programare 2.1 Comunicaþia om-maºinã 2.2 Tipuri de numere reprezentabile în calculator 2.3 Valori de adevãr 2.4 ªiruri de caractere 2.5 Tipuri primitive de valori ale unui limbaj de programare 2.6 Tablouri de elemente 2.7 Expresii de calcul 2.8 Variabile 2.9 Instrucþiuni
2.1 Comunicaþia om-maºinã Pentru a executa un program de calculator este necesar sã putem comunica cu unitatea centralã pentru a-i furniza instrucþiunile necesare. Cel mai simplu mod de a le comunica este înscrierea codului instrucþiunilor direct în memoria calculatorului de unde pot fi citite de cãtre unitatea centralã. Aceastã cale este însã extrem de anevoioasã pentru programator pentru cã el trebuie sã înveþe sã se exprime coerent într-un limbaj ale cãrui componente de bazã sunt coduri numerice. O alternativã mai bunã este folosirea unui limbaj de comunicaþie format dintr-un numãr foarte mic de cuvinte ºi caractere speciale împreunã cu un set de convenþii care sã ajute la descrierea numerelor ºi a operaþiilor care trebuiesc executate cu aceste numere. Limbajul trebuie sã fie atât de simplu încât calculatorul sã poatã traduce singur, prin intermediul unui program numit compilator, frazele acestui limbaj în instrucþiuni ale unitãþii centrale. Ori de câte ori vom imagina un nou limbaj de comunicaþie cu calculatorul va trebui sã creãm un nou program compilator care sã traducã acest limbaj în instrucþiuni ale unitãþii centrale, numite uneori ºi instrucþiuni maºinã. În realitate folosirea termenului de limbaj de comunicaþie nu este extrem de fericitã pentru cã de obicei noi doar instruim calculatorul ce are de fãcut ºi cum trebuie sã facã acel lucru, fãrã sã-i dãm vreo ºansã acestuia sã comenteze sarcinile primite. În continuare vom numi aceste limbaje simplificate limbaje de programare pentru a le deosebi de limbajele pe care le folosim pentru a comunica cu alþi oameni ºi pe care le vom numi limbaje naturale. Limbajele de programare trebuie sã ne ofere o cale de descriere a modulului în care dorim sã reprezentãm informaþiile cu care lucreazã programul, o cale de a specifica operaþiile care trebuiesc executate cu aceste informaþii ºi o cale de a controla ordinea în care sunt executate aceste operaþii. În plus, limbajele de programare trebuie sã respecte urmãtorul principiu fundamental: rezultatul file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap2.html (1 of 13)12.01.2006 23:07:45
Capitolul II -- Limbaje de programare
execuþiei unei comenzi dintr-un limbaj de programare trebuie sã fie complet determinat. Asta înseamnã cã în limbajele de programare nu este permisã nici o formã de ambiguitate a exprimãrii. Informaþiile reprezentate în memoria calculatorului ºi prelucrate de cãtre un program scris într-un limbaj de programare se numesc date. Tipurile de date care se pot descrie direct cu un anumit limbaj de programare se numesc tipurile de date elementare ale limbajului. Operaþiile elementare posibil de exprimat într-un limbaj de programare se numesc instrucþiuni ale limbajului. Limbajele de programare au evoluat foarte mult de la începuturile calculatoarelor. Tendinþa generalã este aceea de apropiere treptatã de limbajele naturale ºi de modul în care acestea descriu ºi utilizeazã informaþiile. Abstracþiile exprimabile cu ajutorul limbajelor de programare sunt din ce în ce mai multe ºi cuprind o gamã din ce în ce mai largã a noþiunilor fundamentale cu care opereazã mintea umanã. Un alt aspect în care se reflectã evoluþia limbajelor de programare este reprezentat de creºterea profunzimii ºi calitãþii controlului pe care compilatoarele îl fac în momentul traducerii din limbaj de programare în limbaj maºinã, astfel încât ºansa de a greºi la descrierea programului sã fie cât mai micã. Cu toate cã limbajele de programare au fãcut paºi esenþiali în ultimul timp, ele au rãmas totuºi foarte departe de limbajele naturale. Pentru a programa un calculator, trebuie sã poþi gândi ºi sã poþi descrie problemele într-unul dintre limbajele pe care acesta le înþelege. Din acest motiv, scrierea programelor continuã sã rãmânã o activitate rezervatã unui grup de iniþiaþi, chiar dacã acest grup este în continuã creºtere.
2.2 Tipuri de numere reprezentabile în calculator Deºi toate informaþiile reprezentabile direct în memoria calculatorului sunt doar numere naturale, constructorii calculatoarelor de astãzi au diversificat tipurile acestora prin stabilirea unor convenþii de reprezentare pentru numerele negative, reale ºi pentru caractere. Aceste convenþii folosesc numerele naturale pentru reprezentarea celorlalte tipuri de numere. Uneori, reprezentãrile ocupã mai mult de un octet în memoria calculatorului, dimensiunile obiºnuite fiind 1, 2, 4 sau 8 octeþi. Sau, echivalent, 8, 16, 32 sau 64 de biþi. Numerele naturale sunt întotdeauna pozitive. Pentru reprezentarea unui numãr întreg cu semn pot fi folosite douã numere naturale. Primul dintre acestea, având doar douã valori posibile, reprezintã semnul numãrului ºi se poate reprezenta folosind o singurã cifrã binarã. Dacã valoarea acestei cifre binare este 0, numãrul final este pozitiv, iar dacã valoarea cifrei este 1, numãrul final este negativ. Al doilea numãr natural folosit în reprezentarea numerelor întregi cu semn conþine valoarea absolutã a numãrului final. Aceastã convenþie, deºi are dezavantajul cã oferã douã reprezentãri pentru numãrul zero, un zero pozitiv ºi altul negativ, este foarte aproape de reprezentarea numerelor cu semn folositã de calculatoarele moderne. În realitate, convenþia care se foloseºte pentru reprezentarea numerelor întregi cu semn este aºa numita file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap2.html (2 of 13)12.01.2006 23:07:45
Capitolul II -- Limbaje de programare
reprezentare în complement faþã de doi. Aceasta reprezintã numerele negative prin complementarea valorii lor absolute bit cu bit ºi apoi adunarea valorii 1 la numãrul rezultat. Complementarea valorii unui bit se face înlocuind valoarea 1 cu 0 ºi valoarea 0 cu 1. Dacã avem de exemplu numãrul 1, reprezentat pe un octet ca un ºir de opt cifre binare 00000001, complementarea bitcu bit a acestui numãr este numãrul reprezentat pe un octet prin11111110. Pentru a reprezenta valoarea -1 nu ne mai rãmâne altceva de fãcut decât sã adunãm la numãrul rezultat în urma complementãrii un 1 ºi reprezentarea finalã a numãrului întreg negativ -1 pe un octet este 11111111. În aceastã reprezentare, numãrul 00000000 binar reprezintã numãrul 0 iar numãrul 10000000, care mai înainte reprezenta numãrul 0 negativ, acum reprezintã numãrul -128. Într-adevãr, numãrul 128 se poate reprezenta în binar prin 10000000. Complementat, acest numãr devine 01111111 ºi dupã adunarea cu 1, 10000000. Observaþi cã numerele 128 ºi -128 au aceeaºi reprezentare. Convenþia este aceea cã se pãstreazã reprezentarea pentru -128 ºi se eliminã cea pentru 128. Alegerea este datoratã faptului cã toate numerele pozitive au în primul bit valoarea 0 ºi toate cele negative valoarea -1. Prin transformarea lui 10000000 în -128 se pãstreazã aceastã regulã. Folosind reprezentarea în complement faþã de doi, numerele întregi reprezentabile pe un octet sunt în intervalul -128 pânã la 127, adicã -27 pânã la 27 -1. În general, dacã avem o configuraþie de n cifre binare, folosind reprezentarea în complement faþã de doi putem reprezenta numerele întregi din intervalul închis -2n-1 pânã la 2n-1-1. În practicã, se folosesc numere întregi cu semn reprezentate pe 1 octet, 2 octeþi, 4 octeþi ºi 8 octeþi, respectiv 8, 16, 32 ºi 64 de biþi. Dacã dorim sã reprezentãm un numãr real în memoria calculatorului, o putem face memorând câteva cifre semnificative ale acestuia plus o informaþie legatã de ordinul sãu de mãrime. În acest fel, deºi pierdem precizia numãrului, putem reprezenta valori foarte aproape de zero sau foarte departe de aceastã valoare. Soluþia de reprezentare este aceea de a pãstra douã numere cu semn care reprezintã cifrele semnificative ale numãrului real respectiv un exponent care dã ordinul de mãrime. Cifrele reprezentative ale numãrului se numesc împreunã mantisã. Numãrul reprezentat în final este 0.mantisaEexponent. E are valoarea 256 spre deosebire de exponentul 10 pe care îl folosim în uzual. Dacã valoarea exponentului estefoarte mare ºi pozitivã, numãrul real reprezentat este foarte departe de 0, înspre plus sau înspre minus. Dacã exponentul este foarte mare în valoare absolutãºi negativ, numãrul real reprezentat este foarte aproape de zero. În plus, pentru a ne asigura de biunivocitatea corespondenþei, avem nevoie de o convenþie care sã stabileascã faptul cã virgula este plasatã imediat în faþa cifrelor semnificative ºi cã în faþa acesteia se gãseºte o singurã cifrã 0. Numerele care respectã aceastã convenþie se numesc numere normalizate. Aceastã mutare a virgulei imediat în faþa cifrelor semnificative poate sã presupunã modificarea exponentului care pãstreazã ordinul de mãrime al numãrului. Numerele fracþionare se numesc în file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap2.html (3 of 13)12.01.2006 23:07:45
Capitolul II -- Limbaje de programare
limbajul calculatoarelor numere în virgulã mobilã sau numere flotante tocmai din cauza acestei eventuale ajustãri a poziþiei virgulei. Cu aceastã convenþie nu se poate reprezenta orice numãr real, dar se poate obþine o acoperire destul de bunã a unui interval al axei numerelor reale cu valori. Atunci când încercãm sã reprezentãm în memoria calculatorului un numãr real, cãutãm de fapt cel mai apropiat numãr real reprezentabil în calculator ºi aproximãm numãrul iniþial cu acesta din urmã. Ca rezultat, putem efectua calcule complexe cu o precizie rezonabilã. Descrierea convenþiei exacte de reprezentare a numerelor reale în calculator depãºeºte cadrul acestei prezentãri. Unele dintre detalii pot fi diferite de cele prezentate aici, dar principiul este exact acesta. Convenþia de reprezentare a numerelor reale este standardizatã de IEEE în specificaþia 754. Desigur, în unele probleme, lipsa de precizie poate sã altereze rezultatul final, mai ales cã, uneori, erorile se cumuleazã. Existã de altfel o teorie complexã, analiza numericã, conceputã pentru a studia cãile prin care putem þine sub control aceste erori de precizie. Putem creºte precizia de reprezentare a numerelor reale prin mãrirea spaþiului rezervat mantisei. În acest fel mãrim numãrul de cifre semnificative pe care îl pãstrãm. În general, unitãþile centrale actuale lucreazã cu douã precizii: numerele flotante simple, reprezentate pe 4 octeþi ºi numerele flotante duble, reprezentate pe 8 octeþi.
2.3 Valori de adevãr Uneori, avem nevoie sã memorãm în calculator valori de adevãr. Existã doar douã valori de adevãr posibile: adevãrat ºi fals. Uneori, aceste valori de adevãr se mai numesc ºi valori booleene. Deºi pentru memorarea acestor valori este suficientã o singurã cifrã binarã, în practicã aceste valori sunt reprezentate pe un întreg octet pentru cã operaþiile la nivel de bit sunt de obicei prea lente. Valorile booleene se pot combina între ele prin operaþii logice: “ºi”, “sau”, "negaþie”.
2.4 ªiruri de caractere Dupã cum spuneam mai înainte, deºi orice informaþie reprezentatã în calculator este în final un numãr, mintea umanã este obiºnuitã sã lucreze cu cuvinte ºi imagini mai mult decât cu numere. De aceea, calculatorul trebuie sã aibã posibilitatea sã simuleze memorarea informaþiilor de acest fel. În ceea ce priveºte cuvintele, ele pot fi reprezentate în memoria calculatorului prin caracterele care le formeazã. Înºiruirea acestor caractere în memorie duce la noþiunea de ºir de caractere. Pe lângã caracterele propriu-zise care construiesc cuvântul, un ºir de caractere trebuie sã poatã memora ºi numãrul total de caractere din ºir, cu alte cuvinte lungimea sa. În realitate, un ºir de caractere nu conþine doar un singur cuvânt ci este o înºiruire oarecare de caractere printre care pot exista ºi caractere spaþiu. De exemplu, urmãtoarele secvenþe sunt ºiruri de caractere: file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap2.html (4 of 13)12.01.2006 23:07:45
Capitolul II -- Limbaje de programare
"Acesta este un ºir de caractere", "Eugen", "ABCD 0123", "HGkduI;.!". Fiecare limbaj de programare trebuie sã ofere o convenþie de reprezentare a ºirurilor de caractere în calculator precum ºi o convenþie de scriere a acestora în program. De obicei, cea de-a doua convenþie este aceea cã ºirul de caractere trebuie închis între apostroafe sau ghilimele. În paragraful anterior de exemplu, am folosit ghilimele pentru delimitarea ºirurilor de caractere. Convenþia de reprezentare în memorie diferã de la un limbaj de programare la altul prin modul în care este memoratã lungimea ºirului, precum ºi prin convenþia de reprezentare în memorie a caracterelor: ASCII, Unicode sau alta. Împreunã cu fiecare tip de datã, limbajele de programare trebuie sã defineascã ºi operaþiile ce se pot executa cu datele de tipul respectiv. Pentru ºirurile de caractere, principala operaþie este concatenarea. Prin concatenarea a douã ºiruri de caractere se obþine un ºir de caractere care conþine caracterele celor douã ºiruri puse în prelungire. De exemplu, prin concatenarea ºirurilor de caractere "unu" ºi ", doi", rezultã ºirul de caractere: "unu, doi".
2.5 Tipuri primitive de valori ale unui limbaj de programare Vom numi tipuri primitive de valori ale unui limbaj acele tipuri de valori care se pot reprezenta direct într-un anumit limbaj de programare. Pentru ca informaþia reprezentabilã sã fie independentã de calculatorul pe care ruleazã programele, un limbaj de programare trebuie sã-ºi defineascã propriile sale tipuri primitive, eventual diferite de cele ale unitãþii centrale, tipuri care sã generalizeze tipurile primitive ale tuturor unitãþilor centrale. Pentru fiecare dintre aceste tipuri primitive de valori, limbajul trebuie sã defineascã dimensiunea locaþiei de memorie ocupate, convenþia de reprezentare ºi mulþimea valorilor care pot fi reprezentate în aceastã locaþie. În plus, limbajul trebuie sã defineascã operaþiile care se pot executa cu aceste tipuri ºi comportarea acestor operaþii pe seturi de valori diferite. Tipurile primitive ale unui limbaj sunt de obicei: numere de diverse tipuri, caractere, ºiruri de caractere, valori de adevãr ºi valori de tip referinþã. Totuºi, acest set de tipuri primitive, denumirea exactã a tipurilor ºi operaþiile care se pot executa cu ele variazã mult de la un limbaj de programare la altul.
2.6 Tablouri de elemente Tipurile primitive împreunã cu referinþele, adicã tipurile de date elementare, indivizibile ale unui limbaj, sunt insuficiente pentru necesitãþile unei aplicaþii reale. De obicei este nevoie de organizãri de date mai complicate în care avem structuri de date create prin asocierea mai multor tipuri de date elementare. Aceste structuri de organizare a informaþiilor pot conþine date de acelaºi tip sau date de tipuri diferite.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap2.html (5 of 13)12.01.2006 23:07:45
Capitolul II -- Limbaje de programare
În cazul în care dorim sã reprezentãm o structurã conþinând date de acelaºi tip, va trebui sã folosim o structurã clasicã de reprezentare a datelor numitã tablou de elemente. Practic, un tablou de elemente este o alãturare de locaþii de memorie de acelaºi fel. Alãturarea este continuã în sensul cã zona de memorie alocatã tabloului nu are gãuri. De exemplu, putem gândi întreaga memorie internã a calculatorului ca fiind un tablou de cifre binare sau ca un tablou de octeþi. Informaþiile de acelaºi fel reprezentate într-un tablou se pot prelucra în mod unitar. Referirea la un element din tablou se face prin precizarea locaþiei de început a tabloului în memorie ºi a numãrului de ordine al elementului pe care dorim sã-l referim. Numãrul de ordine al unui element se numeºte indexul elementului. De aceea, tablourile se numesc uneori ºi structuri de date indexate. Sã presupunem cã, la adresa 1234 în memorie, deci începând cu octetul numãrul 1234, avem un tablou de 200 de elemente de tip întregi cu semn reprezentaþi pe 4 octeþi. Pentru a accesa elementul numãrul 123 din tablou, trebuie sã îi aflãm adresa în memorie. Pentru aceasta, trebuie sã calculãm deplasamentul acestui element faþã de începutul tabloului, cu alte cuvinte, la câþi octeþi distanþã faþã de începutul tabloului se gãseºte elementul numãrul 123. Pentru calcului deplasamentului, este nevoie sã ºtim cu ce index începe numerotarea elementelor din tablou. De obicei aceasta începe cu 0 sau cu 1, primul element fiind elementul 0 sau elementul 1. Sã presupunem, în cazul nostru, cã numerotarea ar începe cu 0. În acest caz, elementul cu indexul 123 este de fapt al 124-lea element din tablou, deci mai are încã 123 de elemente înaintea lui. În aceste condiþii, pentru a afla adresa elementului dat, este suficient sã adãugãm la adresa de început a tabloului, deplasamentul calculat ca numãrul de elemente din faþã înmulþit cu dimensiunea unui element din tablou. Adresa finalã este 1234 + 123 * 4 = 1726. Pentru a putea lucra cu elementele unui tablou este deci suficient sã memorãm adresa de început a tabloului ºi indexul elementelor pe care dorim sã le accesãm. Alternativa ar fi fost sã memorãm adresa locaþiei fiecãrui element din tablou. Unul dintre marile avantaje ale utilizãrii tablourilor este acela cã elementele dintr-un tablou se pot prelucra în mod repetitiv, apelându-se aceeaºi operaþie pentru un subset al elementelor din tablou. Astfel, într-un program putem formula instrucþiuni de forma: pentru elementele tabloului T începând de la al treilea pânã la al N-lea, sã se execute operaþia O. Numãrul N poate fi calculat dinamic în timpul execuþiei programului, în funcþie de necesitãþi. Elementele unui tablou pot fi de tip primitiv, referinþã sau pot fi tipuri compuse, inclusiv alte tablouri.
2.7 Expresii de calcul Sarcina principalã a calculatoarelor este aceea de a efectua calcule. Pentru a putea efectua aceste calcule, calculatorul trebuie sã primeascã o descriere a operaþiilor de calcul pe care le are de executat. Calculele
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap2.html (6 of 13)12.01.2006 23:07:45
Capitolul II -- Limbaje de programare
simple sunt descrise cel mai bine prin expresii de calcul. Expresiile sunt formate dintr-o serie de valori care intrã în calcul, numite operanzi ºi din simboluri care specificã operaþiile care trebuiesc efectuate cu aceste valori, numite operatori. Operatorii reprezintã operaþii de adunare, înmulþire, împãrþire, concatenare a ºirurilor de caractere, etc. Operanzii unor expresii pot fi valori elementare precum numerele, ºirurile de caractere sau pot fi referiri cãtre locaþii de memorie în care sunt memorate aceste valori. Tot operanzi pot fi ºi valorile unor funcþii predefinite precum sinus, cosinus sau valoarea absolutã a unui numãr. Calculul complex al valorii acestor funcþii pentru argumentele de intrare este astfel ascuns sub un nume uºor de recunoscut.
Figura 2.1 Un exemplu de expresie ºi componentele acesteia
2.8 Variabile Uneori, atunci când calculele sunt complexe, avem nevoie sã pãstrãm rezultate parþiale ale acestor calcule în locaþii temporare de memorie. Alteori, chiar datele iniþiale ale problemei trebuie memorate pentru o folosire ulterioarã. Aceste locaþii de memorie folosite ca depozit de valori le vom numi variabile. Variabilele pot juca rol de operanzi în expresii. Pentru a regãsi valoarea memoratã într-o anumitã variabilã, este suficient sã memorãm poziþia locaþiei variabilei în memorie ºi tipul de datã memoratã la aceastã locaþie, numit ºi tipul variabilei. Cunoaºterea tipului variabilei este esenþialã la memorarea ºi regãsirea datelor. La locaþia variabilei gãsim întotdeauna o configuraþie de cifre binare. Interpretarea acestor cifre binare se poate face numai cunoscând convenþia de reprezentare care s-a folosit la memorarea acelor cifre binare. Mai mult, tipul de variabilã ne ºi spune câþi octeþi de memorie ocupã locaþia respectivã. Pentru a putea referi variabilele în interiorul unui program, trebuie sã atribuim câte un nume pentru file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap2.html (7 of 13)12.01.2006 23:07:45
Capitolul II -- Limbaje de programare
fiecare dintre acestea ºi sã rezervãm locaþiile de memorie destinate lor. Aceastã rezervare a locaþiilor se poate face fie la pornirea programului fie pe parcurs. Putem împãrþi variabilele în funcþie de perioada lor de existenþã în variabile statice, care existã pe tot parcursul programului ºi variabile locale care se creeazã doar în secþiunea de program în care este nevoie de ele pentru a fi distruse imediat ce se pãrãseºte secþiunea respectivã. Variabilele statice pãstreazã de obicei informaþii esenþiale pentru execuþia programului precum numele celui care a pornit programul, data de pornire, ora de pornire sau numãrul p . Variabilele locale pãstreazã valori care au sens doar în contextul unei anumite secþiuni din program. De exemplu, variabilele care sunt utilizate la calculul sinusului dintr-un numãr, sunt inutile ºi trebuiesc eliberate imediat ce calculul a fost terminat. În continuare, doar valoarea finalã a sinusului este importantã. În plus, existã variabile care se creeazã doar la cererea explicitã a programului ºi nu sunt eliberate decât atunci când programul nu mai are nevoie de ele. Aceste variabile se numesc variabile dinamice. De exemplu, se creeazã o variabilã dinamicã atunci când utilizatorul programului introduce un nou nume într-o listã de persoane. Crearea ºi ºtergerea acestui nume nu are legãturã cu faza în care se aflã rularea programului ci are legãturã cu dorinþa celui care utilizeazã programul de a mai adãuga sau ºterge un nume în lista persoanelor cu care lucreazã, pentru a le trimite, de exemplu, felicitãri de anul nou.
2.9 Instrucþiuni Operaþiile care trebuiesc executate de un anumit program sunt exprimate în limbajele de programare cu ajutorul unor instrucþiuni. Spre deosebire de limbajele naturale, existã doar câteva tipuri standard de instrucþiuni care pot fi combinate între ele pentru a obþine funcþionalitatea programelor. Sintaxa de scriere a acestor tipuri de instrucþiuni este foarte rigidã. Motivul acestei rigiditãþi este faptul cã un compilator recunoaºte un anumit tip de instrucþiune dupã sintaxa ei ºi nu dupã sensul pe care îl are ca în cazul limbajelor naturale. Tipurile elementare de instrucþiuni nu s-au schimbat de-a lungul timpului pentru cã ele sunt conþinute în însuºi modelul de funcþionare al calculatoarelor Von Neumann. În primul rând, avem instrucþiuni de atribuire. Aceste instrucþiuni presupun existenþa unei locaþii de memorie cãreia dorim sã-i atribuim o anumitã valoare. Atribuirea poate sã parã o operaþie foarte simplã dar realitatea este cu totul alta. În primul rând, valoarea respectivã poate fi rezultatul evaluãrii (calculului) unei expresii complicate care trebuie executatã înainte de a fi memoratã în locaþia de memorie doritã. Apoi, însãºi poziþia locaþiei în memorie ar putea fi rezultatul unui calcul în urma cãruia sã rezulte o anumitã adresã. De exemplu, am putea dori sã atribuim o valoare unui element din interiorul unui tablou. Într-o astfel de situaþie, locaþia de memorie este calculatã prin adunarea unui deplasament la adresa de început a tabloului.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap2.html (8 of 13)12.01.2006 23:07:45
Capitolul II -- Limbaje de programare
În fine, locaþia de memorie ºi valoarea pe care dorim sã i-o atribuim ar putea avea tipuri diferite. În acest caz, operaþia de atribuire presupune ºi transformarea, dacã este posibilã, a valorii într-o nouã valoare de acelaºi tip cu locaþia de memorie. În plus, aceastã transformare trebuie efectuatã în aºa fel încât sã nu se piardã semnificaþia valorii originale. De exemplu, dacã valoarea originalã era -1 întreg iar locaþia de memorie este de tip flotant, valoarea obþinutã dupã transformare trebuie sã fie -1.0. Transformarea valorilor de la un tip la altul se numeºte în termeni informatici conversie. Conversiile pot apãrea ºi în alte situaþii decât instrucþiunile de atribuire, de exemplu la transmiterea parametrilor unei funcþii, în calculul unei expresii. Forma generalã a unei instrucþiuni de atribuire este: Locaþie = Valoare Locaþia poate fi o adresã cu tip, un nume de variabilã sau o expresie în urma cãreia sã rezulte o adresã cu tip. Printr-o adresã cu tip înþelegem o adresã de memorie pentru care a fost specificat tipul valorilor care pot fi memorate în interior. O adresã simplã, fãrã tip, nu poate intra într-o atribuire pentru cã nu putem ºti care este convenþia de reprezentare care trebuie folositã la memorarea valorii. Al doilea tip de instrucþiune elementarã este instrucþiunea condiþionalã. Aceastã instrucþiune permite o primã formã de ramificare a ordinii în care se executã instrucþiunile. Ramificarea depinde de o condiþie care poate fi testatã pe setul de date cu care lucreazã aplicaþia. În funcþie de valoarea de adevãr care rezultã în urma evaluãrii condiþiei se poate executa o instrucþiune sau alta. Modelul de exprimare a unei expresii condiþionale este urmãtorul: Dacã condiþia este adevãratã executã instrucþiunea 1 altfel executã instrucþiunea 2. Cele douã ramuri de execuþie sunt disjuncte în sensul cã dacã este executatã instrucþiunea de pe o ramurã atunci cu siguranþã instrucþiunea de pe cealaltã ramurã nu va fi executatã. Dupã execuþia uneia sau a celeilalte instrucþiuni ramurile se reunificã ºi execuþia îºi continuã drumul cu instrucþiunea care urmeazã dupã instrucþiunea condiþionalã.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap2.html (9 of 13)12.01.2006 23:07:45
Capitolul II -- Limbaje de programare
Figura 2.2 Schema de funcþionare a unei instrucþiuni condiþionale
Condiþia care hotãrãºte care din cele douã instrucþiuni va fi executatã este de obicei un test de egalitate între douã valori sau un test de ordonare în care o valoare este testatã dacã este mai micã sau nu decât o altã valoare. Condiþiile pot fi compuse prin conectori logici de tipul “ºi”, “sau” sau “non”, rezultând în final condiþii de forma: dacã variabila numitã Temperaturã este mai micã decât 0 ºi mai mare decât -10, atunci… Instrucþiunile condiþionale au ºi o variantã în care, în funcþie de o valoare întreagã alege o instrucþiune dintr-un set de instrucþiuni ºi apoi o executã. Aceastã formã este în realitate derivatã din instrucþiunea condiþionalã clasicã, ea putând fi exprimatã prin: Dacã Variabila este egalã cu Valoarea 1 atunci executã instrucþiunea 1 altfel, dacã Variabila este egalã cu Valoarea 2 atunci executã instrucþiunea 2 altfel, dacã Variabila … … altfel
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap2.html (10 of 13)12.01.2006 23:07:45
Capitolul II -- Limbaje de programare
executã instrucþiunea implicitã. Un al treilea tip de instrucþiuni elementare sunt instrucþiunile de ciclare sau repetitive sau buclele. Acestea specificã faptul cã o instrucþiune trebuie executatã în mod repetat. Controlul ciclurilor de instrucþiuni se poate face pe diverse criterii. De exemplu se poate executa o instrucþiune de un numãr fix de ori sau se poate executa instrucþiunea pânã când o condiþie devine adevãratã sau falsã. Condiþia de terminare a buclei poate fi testatã la începutul buclei sau la sfârºitul acesteia. Dacã condiþia este testatã de fiecare datã înainte de execuþia instrucþiunii, funcþionarea ciclului se poate descrie prin: Atâta timp cât Condiþia este adevãratã executã Instrucþiunea Acest tip de instrucþiuni de ciclare poatã numele de cicluri while, cuvântul while însemnând în limba englezã "atâta timp cât”. În cazul ciclurilor while, existã posibilitatea ca instrucþiunea din interiorul ciclului sã nu se execute niciodatã, dacã condiþia este de la început falsã.
Figura 2.3 Schema de funcþionare a unui ciclu while
Dacã condiþia este testatã de fiecare datã dupã execuþia instrucþiunii, funcþionarea ciclului se poate descrie prin: Executã Instrucþiunea atâta timp cât Condiþia este adevãratã. Acest tip de instrucþiuni de ciclare poartã numele de cicluri do-while, cuvântul do însemnând în limba englezã "executã”. În cazul ciclurilor do-while instrucþiunea din interiorul ciclului este garantat cã se executã cel puþin o datã, chiar dacã valoare condiþiei de terminare a buclei este de la început fals.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap2.html (11 of 13)12.01.2006 23:07:45
Capitolul II -- Limbaje de programare
Figura 2.4 Schema de funcþionare a unui ciclu do-while
Dupã cum aþi observat, probabil, dacã instrucþiunea din interiorul buclei nu afecteazã în nici un fel valoarea de adevãr a condiþiei de terminare a buclei, existã ºansa ca bucla sã nu se termine niciodatã. Al patrulea tip de instrucþiuni sunt apelurile de proceduri. O procedurã este o secvenþã de instrucþiuni de sine stãtãtoare ºi parametrizatã. Un apel de procedurã este o instrucþiune prin care forþãm programul sã execute pe loc instrucþiunile unei anumite proceduri. Dupã execuþia procedurii, se continuã cu execuþia instrucþiunii de dupã apelul de procedurã. Procedurile sunt utile atunci când, în zone diferite ale programului, dorim sã executãm aceeaºi secvenþã de instrucþiuni. În aceste situaþii, putem sã scriem secvenþa de instrucþiuni o singurã datã ºi s-o apelãm apoi oriunde din interiorul programului. De exemplu, ori de câte ori vom dori sã ºtergem toate semnele de pe ecranul calculatorului, putem apela la o procedurã, pentru a nu fi nevoiþi sã scriem de fiecare datã secvenþa de instrucþiuni care duce la ºtergerea ecranului. Procedura în sine, o vom scrie o singurã datã ºi îi vom da un nume prin care o vom putea apela ulterior ori de câte ori avem nevoie. Uneori secvenþa de instrucþiuni dintr-o procedurã depinde de niºte parametri adicã de un set de valori care sunt precizate doar în momentul apelului procedurii. În aceste cazuri, procedura se poate comporta în mod diferit în funcþie de valorile de apel ale acestor parametri. În funcþie de tipul declarat al unor parametri ºi de tipul real al valorilor care sunt trimise ca parametri într-un apel de procedurã, se poate întâmpla ca înainte de apelul propriu-zis sã fie necesarã o conversie de tip. De exemplu, în cazul procedurii care calculeazã valoarea sinusului unui numãr, parametrul de intrare este însuºi numãrul pentru care trebuie calculat sinusul. Rezultatul acestui calcul va fi diferit, în funcþie de valoarea la apel a parametrului. În mod normal, parametrul va fi un numãr flotant iar dacã la apel vom transmite ca parametru o valoare întreagã, ea va fi convertitã spre o valoare flotantã.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap2.html (12 of 13)12.01.2006 23:07:45
Capitolul II -- Limbaje de programare
În urma apelului unei proceduri poate rezulta o valoare pe care o vom numi valoare de retur a procedurii. De exemplu, în urma apelului procedurii de calcul a sinusului unui numãr, rezultã o valoare flotantã care este valoarea calculatã a sinusului. Acest tip de proceduri, care întorc valori de retur se mai numesc ºi funcþii. Funcþiile definesc întotdeauna un tip al valorii de retur pe care o întorc. În acest fel, apelurile de funcþii pot fi implicate ºi în formarea unor expresii de calcul sub formã de operanzi, expresia cunoscând tipul de valoare care trebuie aºteptatã ca valoare a apelului unei anumite funcþii. Analogia cu funcþiile pe care le-aþi studiat la matematicã este evidentã dar nu totalã. În cazul funcþiilor scrise într-un limbaj de programare, douã apeluri consecutive ale aceleiaºi funcþii cu aceleaºi valori ca parametri se pot comporta diferit datoritã dependenþei de condiþii exterioare sistemului format din funcþie ºi parametrii de apel. În plus, se poate întâmpla ca modul de comportare al funcþiei ºi valoarea de retur sã nu fie definite pentru anumite valori de apel ale parametrilor. Deºi scrierea acestui tip de funcþii este nerecomandabilã, în practicã întâlnim astfel de funcþii la tot pasul. [cuprins] (C) IntegraSoft 1996-1998
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap2.html (13 of 13)12.01.2006 23:07:45
Capitolul III -- Reprezentarea informaþiilor cu obiecte
Capitolul III Reprezentarea informaþiilor cu obiecte 3.1 Obiecte 3.2 Încapsularea informaþiilor în interiorul obiectelor 3.3 Clase de obiecte 3.4 Derivarea claselor de obiecte 3.5 Interfeþe spre obiecte
3.1 Obiecte Informaþiile pe care le reprezentãm în memoria calculatorului sunt rareori atât de simple precum culorile sau literele. În general, dorim sã reprezentãm informaþii complexe, care sã descrie obiectele fizice care ne înconjoarã sau noþiunile cu care operãm zilnic, în interiorul cãrora culoarea sau o secvenþã de litere reprezintã doar o micã parte. Aceste obiecte fizice sau noþiuni din lumea realã trebuiesc reprezentate în memoria calculatorului în aºa fel încât informaþiile specifice lor sã fie pãstrate la un loc ºi sã se poatã prelucra ca un tot unitar. Sã nu uitãm însã cã, la nivelul cel mai de jos, informaþia ataºatã acestor obiecte continuã sã fie tratatã de cãtre compilator ca un ºir de numere naturale, singurele informaþii reprezentabile direct în memoria calculatoarelor actuale. Pentru a reprezenta în memoria internã obiecte fizice sau noþiuni, este nevoie sã izolãm întregul set de proprietãþi specifice acestora ºi sã îl reprezentãm prin numere. Aceste numere vor ocupa în memorie o zonã compactã pe care, printr-un abuz de limbaj, o vom numi, într-o primã aproximare, obiect. Va trebui însã sã aveþi întotdeauna o imagine clarã a deosebirii fundamentale dintre un obiect fizic sau o noþiune ºi reprezentarea acestora în memoria calculatorului. De exemplu, în memoria calculatorului este foarte simplu sã creãm un nou obiect, identic cu altul deja existent, prin simpla duplicare a zonei de memorie folosite de obiectul pe care dorim sã-l dedublãm. În realitate însã, este mult mai greu sã obþinem o copie identicã a unui obiect fizic, fie el o simplã foaie de hârtie sau o bancnotã de 10000 de lei. Sã revenim însã la numerele naturale. Din moment ce ele sunt singurele entitãþi reprezentabile în memoria calculatorului, este firesc ca acesta sã fie echipat cu un set bogat de operaþii predefinite de prelucrare a numerelor. Din pãcate, atunci când facem corespondenþa dintre numere ºi litere de exemplu, nu ne putem aºtepta la un set la fel de bogat de operaþii predefinite care sã lucreze cu litere. Dar, þinând cont de faptul cã în cele din urmã lucrãm tot cu numere, putem construi propriile noastre operaþii specifice literelor, combinând în mod corespunzãtor operaþiile numerice predefinite. De exemplu, pentru a obþine dintr-o literã majusculã, sã spunem ‘A’, corespondenta ei minusculã ‘a’, este suficient sã adunãm un deplasament numeric corespunzãtor, presupunând cã literele mari ºi cele file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap3.html (1 of 9)12.01.2006 23:07:46
Capitolul III -- Reprezentarea informaþiilor cu obiecte
mici sunt numerotate în ordine alfabeticã ºi imediat una dupã cealaltã în convenþia de reprezentare folositã. În setul ASCII deplasamentul este 32, reprezentarea lui ‘A’ fiind 65 iar reprezentarea lui ‘a’ fiind 97. Acest deplasament se pãstreazã pentru toate literele din alfabetul englez ºi român, la cel din urmã existând totuºi o excepþie în cazul literei ‘ª’. Putem sã extindem cerinþele noastre mai departe, spunând cã, atunci când analizãm un obiect fizic sau o noþiune pentru a le reprezenta în calculator, trebuie sã analizãm nu numai proprietãþile acestora dar ºi modul în care acestea pot fi utilizate ºi care sunt operaþiile care pot fi executate asupra lor sau cu ajutorul lor. Acest set de operaþii va trebui ulterior sã-l redefinim în contextul mulþimii de numere care formeazã proprietãþile obiectului din memoria calculatorului, ºi sã îl descompunem în operaþii numerice preexistente. În plus, trebuie sã analizãm modul în care reacþioneazã obiectul atunci când este supus efectului unor acþiuni exterioare. Uneori, setul de operaþii specifice unui obiect împreunã cu modul în care acesta reacþioneazã la stimuli exteriori se numeºte comportamentul obiectului. De exemplu, dacã dorim sã construim un obiect care reprezintã o minge de formã sfericã în spaþiu, este necesar sã definim trei numere care sã reprezinte coordonatele x, y ºi z relativ la un sistem de axe dat, precum ºi o valoare pentru raza sferei. Aceste valori numerice vor face parte din setul de proprietãþi ale obiectului minge. Dacã mai târziu vom dori sã construim o operaþie care sã reprezinte mutarea în spaþiu a obiectului minge, este suficient sã ne folosim de operaþiile cu numere pentru a modifica valorile coordonatelor x, y ºi z. Desigur, obiectul minge este insuficient descris prin aceste coordonate ºi, pentru a simula în calculator obiectul real este nevoie de multe proprietãþi suplimentare precum ºi de multe operaþii în plus. Dar, dacã problema pe care o avem de rezolvat nu necesitã aceste proprietãþi ºi operaþii, este preferabil sã nu le definim în obiectul folosit pentru reprezentare. Rezultatul direct al acestui mod de abordare este acela cã vom putea defini acelaºi obiect real în mai multe feluri pentru a-l reprezenta în memoria internã. Modul de definire depinde de problema de rezolvat ºi de programatorul care a gândit reprezentarea. De altfel, aceste diferenþe de percepþie ale unui obiect real existã ºi între diverºi observatori umani. Din punctul de vedere al programãrii, un obiect este o reprezentare în memoria calculatorului a proprietãþilor ºi comportamentului unei noþiuni sau ale unui obiect real.
file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap3.html (2 of 9)12.01.2006 23:07:46
Capitolul III -- Reprezentarea informaþiilor cu obiecte
Figura 3.1 Modelul de reprezentare al unui obiect în memorie. Stratul exterior reprezintã doar operaþiile care oferã calea de a interacþiona cu proprietãþile obiectului ºi nu are corespondent direct în zona de memorie ocupatã de obiect.
3.2 Încapsularea informaþiilor în interiorul obiectelor Existã situaþii în care accesul din exterior la proprietãþile unui obiect poate sã punã probleme acestuia. Ce s-ar întâmpla de exemplu dacã s-ar putea accesa direct valorile care definesc funcþionalitatea corpului uman? Desigur, existã cazuri în care acest lucru ar fi îmbucurãtor. N-ar mai fi nevoie sã acþionãm indirect asupra concentraþiilor de enzime în corp ci am putea sã modificãm aceste valori în mod direct. Dar, în acelaºi timp, am putea provoca mari necazuri în cazul în care am modifica aceste valori în afara pragului suportabil de cãtre organism. Din aceste motive, este preferabil sã lãsãm modificarea acestor parametri în sarcina exclusivã a unor operaþii definite de cãtre obiect, operaþii care vor verifica noile valori înainte de a le schimba în interiorul obiectului. În lipsa acestui filtru, putem sã stricãm coerenþa valorilor memorate în interiorul unui obiect, fãcându-l inutilizabil. Din acest punct de vedere, putem privi obiectul ca pe un set de valori care formeazã miezul obiectului ºi un set de operaþii care îmbracã aceste valori, protejându-le. Vom spune cã proprietãþile obiectului sunt încapsulate în interiorul acestora. Mai mult, obiectul încapsuleazã ºi modul de funcþionare a operaþiilor lui specifice, din exterior neputându-se observa decât modul de apelare a acestor operaþii ºi rezultatele apelurilor. Cu alte cuvinte, procesul de încapsulare este procesul de ascundere a detaliilor neimportante sau sensibile de construcþie a obiectului. Dar nu numai proprietãþile unui obiect trebuiesc protejate ci ºi operaþiile definite de cãtre acesta. Unele dintre operaþiile definite pentru un obiect, cum ar fi de exemplu procesul respirator al omului, sunt periculos de lãsat la dispoziþia oricui. Este preferabil sã putem controla foarte exact cine ce operaþii poate apela pentru un anumit obiect. În acest mod, din punctul de vedere al unui observator strãin, omul este perceput ca un obiect mult mai simplu decât este în realitate, pentru cã acel observator nu poate file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap3.html (3 of 9)12.01.2006 23:07:46
Capitolul III -- Reprezentarea informaþiilor cu obiecte
vedea decât acele valori ºi nu poate apela decât acele operaþii care sunt fãcute publice. Desigur, în practicã este nevoie de o oarecare rafinare a gradului de protejare a fiecãrei operaþii sau proprietãþi în aºa fel încât însuºi accesul observatorilor exteriori sã poatã fi nuanþat în funcþie de gradul de similitudine ºi apropiere al observatorului faþã de obiectul accesat. Rafinarea trebuie sã meargã pânã la a putea specifica pentru fiecare proprietate ºi operaþie în parte care sunt observatorii care au acces ºi care nu. Aceastã protejare ºi încapsulare a proprietãþilor ºi operaþiilor ce se pot executa cu ajutorul unui obiect are ºi o altã consecinþã ºi anume aceea cã utilizatorul obiectului respectiv este independent de detaliile constructive ale obiectului respectiv. Structura internã a obiectului poate fi astfel schimbatã ºi perfecþionatã în timp fãrã ca funcþionalitatea de bazã sã fie afectatã.
3.3 Clase de obiecte În lumea realã se pot identifica uºor familii de obiecte. Este greu sã descriem într-un limbaj de programare fiecare minge din lume dar, pentru a putea folosi orice minge din lume, este suficient sã descriem o singurã datã care sunt proprietãþile unei mingi în general, precum ºi operaþiile care pot fi executate cu aceasta. Aceasta nu înseamnã cã toate obiectele minge din lume sunt identice. Diferenþa dintre ele se aflã reprezentatã în primul rând în valorile proprietãþilor lor care sunt mãrimi numerice variabile, adicã diferã de la un obiect de acelaºi fel la altul. De exemplu, în fiecare obiect minge vom avea un numãr natural care reprezintã culoarea mingii. Acest numãr poate sã difere de la o minge la alta exact aºa cum, în realitate, culoarea diferã de la o minge la alta. La fel coordonatele poziþiei mingii la un moment dat sau raza mingii precum ºi materialul din care este confecþionatã au valori care variazã de la o minge la alta. Cu alte cuvinte, fiecare minge din lume are acelaºi set de proprietãþi, dar valorile acestora pot sã difere de la o minge la alta. Modelul de reprezentare în memorie a unui obiect este întotdeauna acelaºi, dar valorile memorate în locaþiile corespunzãtoare proprietãþilor sunt în general diferite. În ceea ce priveºte operaþiile, acestea sunt întotdeauna aceleaºi dar rezultatul aplicãrii lor poate sã difere în funcþie de valorile proprietãþilor obiectului asupra cãruia au fost aplicate. De exemplu, atunci când aruncãm o minge spre pãmânt ea va ricoºa din acesta ridicându-se din nou în aer. Înãlþimea la care se va ridica însã, este dependentã de dimensiunile ºi materialul din care a fost confecþionatã mingea. Cu alte cuvinte, noua poziþie în spaþiu se va calcula printr-o operaþie care va þine cont de valorile memorate în interiorul obiectului. Se poate întâmpla chiar ca operaþia sã hotãrascã faptul cã mingea va strãpunge podeaua în loc sã fie respinsã de cãtre aceasta. Sã mai observãm cã operaþiile nu depind numai de proprietãþile obiectului ci ºi de unele valori exterioare acestuia. Atunci când aruncãm o minge spre pãmânt, înãlþimea la care va ricoºa aceasta depinde ºi de viteza cu care a fost aruncatã mingea. Aceastã vitezã este un parametru al operaþiei de aruncare. Nu are nici un rost sã transmitem ca parametrii ai unei operaþii valorile proprietãþilor unui file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap3.html (4 of 9)12.01.2006 23:07:46
Capitolul III -- Reprezentarea informaþiilor cu obiecte
obiect pentru cã acestea sunt întotdeauna disponibile operaþiei. Nici o operaþie nu se poate aplica asupra unui obiect fãrã sã ºtim exact care este obiectul respectiv ºi ce proprietãþi are acesta. Este absurd sã ne gândim la ce înãlþime se va ridica o minge în general, fãrã sã facem presupuneri asupra valorilor proprietãþilor acesteia. Sã mai observãm însã cã, dacã toate mingile ar avea aceleaºi valori pentru proprietãþile implicate în operaþia descrisã mai sus, am putea sã calculãm înãlþimea de ricoºeu în general, fãrã sã fim dependenþi de o anumitã minge. În concluzie, putem spune cã obiectele cu care lucrãm fac parte întotdeauna dintr-o familie mai mare de obiecte cu proprietãþi ºi comportament similar. Aceste familii de obiecte le vom numi în continuare clase de obiecte sau concepte în timp ce obiectele aparþinând unei anumite clase le vom numi instanþe ale clasei de obiecte respective. Putem vorbi despre clasa de obiecte minge ºi despre instanþele acesteia, mulþimea tuturor obiectelor minge care existã în lume. Fiecare instanþã a clasei minge are un loc bine precizat în spaþiu ºi în timp, un material ºi o culoare. Aceste proprietãþi diferã de la o instanþã la alta, dar fiecare instanþã a aceleiaºi clase va avea întotdeauna aceleaºi proprietãþi ºi aceleaºi operaþii vor putea fi aplicate asupra ei. În continuare vom numi variabile aceste proprietãþi ale unei clase de obiecte ºi vom numi metode operaþiile definite pentru o anumitã clasã de obiecte. Pentru a clarifica, sã mai reluãm încã o datã: O clasã de obiecte este o descriere a proprietãþilor ºi operaþiilor specifice unui nou tip de obiecte reprezentabile în memorie. O instanþã a unei clase de obiecte este un obiect de memorie care respectã descrierea clasei. O variabilã a unei clase de obiecte este o proprietate a clasei respective care poate lua valori diferite în instanþe diferite ale clasei. O metodã a unei clase este descrierea unei operaþii specifice clasei respective. Sã mai precizãm faptul cã, spre deosebire de variabilele unei clase, metodele acesteia sunt memorate o singurã datã pentru toate obiectele. Comportarea diferitã a acestora este datã de faptul cã ele depind de valorile variabilelor. O categorie aparte a claselor de obiecte este categoria acelor clase care reprezintã concepte care nu se pot instanþia în mod direct, adicã nu putem construi instanþe ale clasei respective, de obicei pentru cã nu avem destule informaþii pentru a le putea construi. De exemplu, conceptul de om nu se poate instanþia în mod direct pentru cã nu putem construi un om despre care nu ºtim exact dacã este bãrbat sau femeie. Putem în schimb instanþia conceptul de bãrbat ºi conceptul de femeie care sunt niºte subconcepte ale conceptului om. Clasele abstracte, neinstanþiabile, servesc în general pentru definirea unor proprietãþi sau operaþii comune ale mai multor clase ºi pentru a putea generaliza operaþiile referitoare la acestea. Putem, de exemplu sã definim în cadrul clasei de obiecte om modul în care acesta se alimenteazã ca fiind independent de apartenenþa la conceptul de bãrbat sau femeie. Aceastã definiþie va fi valabilã la amândouã subconceptele definite mai sus. În schimb, nu putem decât cel mult sã precizãm faptul cã un om trebuie sã aibã un comportament social. Descrierea exactã a acestui comportament trebuie fãcutã în cadrul conceptului de bãrbat ºi a celui de femeie. Oricum, este interesant faptul cã, indiferent care ar fi clasa acestuia, putem sã ne bazãm pe faptul cã acesta va avea definit un comportament social, specific clasei lui. file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap3.html (5 of 9)12.01.2006 23:07:46
Capitolul III -- Reprezentarea informaþiilor cu obiecte
Cele douã metode despre care am vorbit mai sus, definite la nivelul unui superconcept, sunt profund diferite din punctul de vedere al subconceptelor acestuia. În timp ce metoda de alimentaþie este definitã exact ºi amândouã subconceptele pot sã o foloseascã fãrã probleme, metoda de comportament social este doar o metodã abstractã, care trebuie sã existe, dar despre care nu se ºtie exact cum trebuie definitã. Fiecare dintre subconcepte trebuie sã-ºi defineascã propriul sãu comportament social pentru a putea deveni instanþiabil. Dacã o clasã de obiecte are cel puþin o metodã abstractã, ea devine în întregime o clasã abstractã ºi nu poate fi instanþiatã, adicã nu putem crea instanþe ale unei clase de obiecte abstracte. Altfel spus, o clasã abstractã de obiecte este o clasã pentru care nu s-au precizat suficient de clar toate metodele astfel încât sã poatã fi folositã în mod direct.
3.4 Derivarea claselor de obiecte O altã proprietate interesantã a claselor de obiecte este aceea de ierarhizare. Practic, ori de câte ori definim o nouã clasã de obiecte care sã reprezinte un anumit concept, specificãm clasa de obiecte care reprezintã conceptul original din care provine noul concept împreunã cu diferenþele pe care le aduce noul concept derivat faþã de cel original. Aceastã operaþie de definire a unei noi clase de obiecte pe baza uneia deja existente o vom numi derivare. Conceptul mai general se va numi superconcept iar conceptul derivat din acesta se va numi subconcept. În acelaºi mod, clasa originalã se va numi superclasã a noii clase în timp ce noua clasã de obiecte se va numi subclasã a clasei derivate. Uneori, în loc de derivare se foloseºte termenul de extindere. Termenul vine de la faptul cã o subclasã îºi extinde superclasa cu noi variabile ºi metode. În spiritul acestei ierarhizãri, putem presupune cã toate clasele de obiecte sunt derivate dintr-o clasã iniþialã, sã-i spunem clasa de obiecte generice, în care putem defini proprietãþile ºi operaþiile comune tuturor obiectelor precum ar fi testul de egalitate dintre douã instanþe, duplicarea instanþelor sau aflarea clasei de care aparþine o anumitã instanþã. Ierarhizarea se poate extinde pe mai multe nivele, sub formã arborescentã, în fiecare punct nodal al structurii arborescente rezultate aflându-se clase de obiecte. Desigur, clasele de obiecte de pe orice nivel pot avea instanþe proprii, cu condiþia sã nu fie clase abstracte, imposibil de instanþiat.
file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap3.html (6 of 9)12.01.2006 23:07:46
Capitolul III -- Reprezentarea informaþiilor cu obiecte
Figura 3.2 O ierarhie de clase de obiecte în care clasele sunt reprezentate în câmpuri eliptice iar instanþele acestora în câmpuri dreptunghiulare. Clasele abstracte de obiecte au elipsa dublatã.
Desigur, este foarte dificil sã construim o ierarhie de clase de obiecte completã, care sã conþinã clase de obiecte corespunzãtoare fiecãrui concept cunoscut. Din fericire, pentru o problemã datã, conceptele implicate în rezolvarea ei sunt relativ puþine ºi pot fi uºor izolate, simplificate ºi definite. Restrângerea la minimum a arborelui de concepte necesar rezolvãrii unei anumite probleme fãrã a se afecta generalitatea soluþiei este un talent pe care fiecare programator trebuie sã ºi-l descopere ºi sã ºi-l cultive cu atenþie. De alegerea acestor concepte depinde eficienþa ºi flexibilitatea aplicaþiei. O clasã de obiecte derivatã dintr-o altã clasã pãstreazã toate proprietãþile ºi operaþiile acesteia din urmã aducând în plus proprietãþi ºi operaþii noi. De exemplu, dacã la nivelul clasei de obiecte om am definit forma bipedã a acestuia ºi capacitatea de a vorbi ºi de a înþelege, toate acestea vor fi moºtenite ºi de cãtre clasele derivate din clasa om, ºi anume clasa bãrbaþilor ºi cea a femeilor. Fiecare dintre aceste clase de obiecte derivate îºi vor defini propriile lor proprietãþi ºi operaþii pentru a descrie diferenþa dintre ele ºi clasa originalã. Unele dintre proprietãþile ºi operaþiile definite în superclasã pot fi redefinite în subclasele de obiecte derivate. Vechile proprietãþi ºi operaþii sunt disponibile în continuare, doar cã pentru a le putea accesa va trebui sã fie specificatã explicit superclasa care deþine copia redefinitã. Operaþia de redefinire a unor operaþii sau variabile din interiorul unei clase în timpul procesului de derivare o vom numi rescriere. Aceastã redefinire ne dã de fapt o mare flexibilitate în construcþia ierarhiei unei probleme date pentru cã nici o proprietate sau operaþie definitã într-un punct al ierarhiei nu este impusã definitiv pentru conceptele derivate din acest punct direct sau nu. Revenind pentru un moment la protejarea informaþiilor interne ale unui obiect sã precizãm faptul cã gradul de similitudine de care vorbeam mai sus este mãrit în cazul în care vorbim de douã clase derivate file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap3.html (7 of 9)12.01.2006 23:07:46
Capitolul III -- Reprezentarea informaþiilor cu obiecte
una din cealaltã. Cu alte cuvinte, o subclasã a unei clase are acces de obicei la mult mai multe informaþii memorate în superclasa sa decât o altã clasã de obiecte oarecare. Acest lucru este firesc þinând cont de faptul cã, uneori, o subclasã este nevoitã sã redefineascã o parte din funcþionalitatea superclasei sale.
3.5 Interfeþe spre obiecte Un obiect este o entitate complexã pe care o putem privi din diverse puncte de vedere. Omul de exemplu poate fi privit ca un mamifer care naºte pui vii sau poate fi privit ca o fiinþã gânditoare care învãþã sã programeze calculatoare sau poate fi privit ca un simplu obiect spaþio-temporal care are propria lui formã ºi poziþie în funcþie de timp. Aceastã observaþie ne spune cã trebuie sã dãm definiþii despre ce înseamnã cu adevãrat faptul cã un obiect poate fi privit ca un mamifer sau ca o fiinþa gânditoare sau ca un obiect spaþio-temporal. Aceste definiþii, pe care le vom numi în continuare interfeþe, sunt aplicabile nu numai clasei de obiecte om dar ºi la alte clase de obiecte derivate sau nu din acesta, superclase sau nu ale acesteia. Putem sã gãsim o mulþime de clase de obiecte ale cãror instanþe pot fi privite ca obiecte spaþio-temporale dar care sã nu aibã mare lucru în comun cu omul. Practic, atunci când construim o interfaþã, definim un set minim de operaþii care trebuie sã aparþinã obiectelor care respectã aceastã interfaþã. Orice clasã de obiecte care declarã cã respectã aceastã interfaþã va trebui sã defineascã toate operaþiile. Operaþiile însã, sunt definite pe cãi specifice fiecãrei clase de obiecte în parte. De exemplu, orice obiect spaþial trebuie sã defineascã o operaþie de modificare a poziþiei în care se aflã. Dar aceastã operaþie este diferitã la un om, care poate sã-ºi schimbe singur poziþia, faþã de o minge care trebuie ajutatã din exterior pentru a putea fi mutatã. Totuºi, dacã ºtim cu siguranþã cã un obiect este o instanþã a unui clase de obiecte care respectã interfaþa spatio-temporalã, putem liniºtiþi sã executãm asupra acestuia o operaþie de schimbare a poziþiei, fãrã sã trebuiascã sã cunoaºtem amãnunte despre modul în care va fi executatã aceastã operaþie. Tot ceea ce trebuie sã ºtim este faptul cã operaþia este definitã pentru obiectul respectiv. În concluzie, o interfaþã este un set de operaþii care trebuiesc definite de o clasã de obiecte pentru a se înscrie într-o anumitã categorie. Vom spune despre o clasã care defineºte toate operaþiile unei interfeþe cã implementeazã interfaþa respectivã. Cu alte cuvinte, putem privi interfeþele ca pe niºte reguli de comportament impuse claselor de obiecte. În clipa în care o clasã implementeazã o anumitã interfaþã, obiectele din clasa respectivã pot fi privite în exclusivitate din acest punct de vedere. Interfeþele pot fi privite ca niºte filtre prin care putem privi un anumit obiect, filtre care nu lasã la vedere decât proprietãþile specifice interfeþei, chiar dacã obiectul în vizor este mult mai complicat în realitate. Interfeþele creazã o altã împãrþire a obiectelor cu care lucrãm. În afarã de împãrþirea normalã pe clase, putem sã împãrþim obiectele ºi dupã interfeþele pe care le implementeazã. ªi, la fel cu situaþia în care definim o operaþie doar pentru obiectele unei anumite clase, putem defini ºi operaþii care lucreazã doar file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap3.html (8 of 9)12.01.2006 23:07:46
Capitolul III -- Reprezentarea informaþiilor cu obiecte
cu obiecte care implementeazã o anumitã interfaþã, indiferent de clasa din care acestea fac parte. [cuprins] (C) IntegraSoft 1996-1998
file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap3.html (9 of 9)12.01.2006 23:07:46
Capitolul IV -- Structura lexicalã Java
Capitolul IV Structura lexicalã Java 4.1 Setul de caractere 4.2 Unitãþi lexicale 4.2.1 Cuvinte cheie 4.2.2 Identificatori 4.2.3 Literali 4.2.3.1 Literali întregi 4.2.3.2 Literali flotanþi 4.2.3.3 Literali booleeni 4.2.3.4 Literali caracter 4.2.3.5 Literali ºir de caractere 4.2.4 Separatori 4.2.5 Operatori 4.3 Comentarii
4.1 Setul de caractere Limbajului Java lucreazã în mod nativ folosind setul de caractere Unicode. Acesta este un standard internaþional care înlocuieºte vechiul set de caractere ASCII. Motivul acestei înlocuiri a fost necesitatea de a reprezenta mai mult de 256 de caractere. Setul de caractere Unicode, fiind reprezentat pe 16 biþi are posibilitãþi mult mai mari. Vechiul standard ASCII este însã un subset al setului Unicode, ceea ce înseamnã cã vom regãsi caracterele ASCII cu exact aceleaºi coduri ca ºi mai înainte în noul standard. Java foloseºte setul Unicode în timpul rulãrii aplicaþiilor ca ºi în timpul compilãrii acestora. Folosirea Unicode în timpul execuþiei nu înseamnã nimic altceva decât faptul cã o variabilã Java de tip caracter este reprezentatã pe 16 biþi iar un ºir de caractere va ocupa fizic în memorie de doua ori mai mulþi octeþi decât numãrul caracterelor care formeazã ºirul. În ceea ce priveºte folosirea Unicode în timpul compilãrii, compilatorul Java acceptã la intrare fiºiere sursã care pot conþine orice caractere Unicode. Se poate lucra ºi cu fiºiere ASCII obiºnuite în care putem introduce caractere Unicode folosind secvenþe escape. Fiºierele sursã sunt fiºiere care conþin declaraþii file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap4.html (1 of 12)12.01.2006 23:07:47
Capitolul IV -- Structura lexicalã Java
ºi instrucþiuni Java. Aceste fiºiere trec prin trei paºi distincþi la citirea lor de cãtre compilator: 1. ªirul de caractere Unicode sau ASCII, memorat în fiºierul sursã, este transformat într-un ºir de caractere Unicode. Caracterele Unicode pot fi introduse ºi ca secvenþe escape folosind doar caractere ASCII. 2. ªirul de caractere Unicode este transformat într-un ºir de caractere în care sunt evidenþiate separat caracterele de intrare faþã de caracterele de sfârºit de linie. 3. ªirul de caractere de intrare ºi de sfârºit de linie este transformat într-un ºir de cuvinte ale limbajului Java. În primul pas al citirii fiºierului sursã, sunt generate secvenþe escape. Secvenþele escape sunt secvenþe de caractere ASCII care încep cu caracterul backslash \. Pentru secvenþele escape Unicode, al doilea caracter din secvenþã trebuie sã fie u sau U. Orice alt caracter care urmeazã dupã backslash va fi considerat ca fiind caracter nativ Unicode ºi lãsat nealterat. Dacã al doilea caracter din secvenþa escape este u, urmãtoarele patru caractere ASCII sunt tratate ca ºi cifre hexazecimale (în baza 16) care formeazã împreunã doi octeþi de memorie care reprezintã un caracter Unicode. Se pot folosi la intrare ºi fiºiere ASCII normale, pentru cã ASCII este un subset al Unicode. De exemplu, putem scrie: int f\u0660 = 3; Numele variabilei are douã caractere ºi al doilea caracter este o cifrã arabic-indic. Exemple de secvenþe Unicode: \uaa08 \U0045 \uu6abe În al doilea pas al citirii fiºierului sursã, sunt recunoscute ca ºi caractere de sfârºit de linie caracterele ASCII CR ºi ASCII LF. În acelaºi timp, secvenþa de caractere ASCII CR-ASCII LF este tratatã ca un singur sfârºit de linie ºi nu douã. În acest mod, Java suportã în comun standardele de terminare a liniilor folosite de diferite sisteme de operare: MacOS, Unix ºi DOS. Este important sã separãm caracterele de sfârºit de linie de restul caracterelor de intrare pentru a ºti unde se terminã comentariile de o singurã linie (care încep cu secvenþa //) precum ºi pentru a raporta odatã cu erorile de compilare ºi linia din fiºierul sursã în care au apãrut acestea. În pasul al treilea al citirii fiºierului sursã, sunt izolate elementele de intrare ale limbajului Java, ºi anume: spaþii, comentarii ºi unitãþi lexicale. Spaþiile pot fi caracterele ASCII SP (spaþiu), FF (avans de paginã) sau HT (tab orizontal) precum ºi orice caracter terminator de linie. Comentariile le vom discuta în paragraful 4.3. file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap4.html (2 of 12)12.01.2006 23:07:47
Capitolul IV -- Structura lexicalã Java
4.2 Unitãþi lexicale Unitãþile lexicale sunt elementele de bazã cu care se construieºte semantica programelor Java. În ºirul de cuvinte de intrare, unitãþile lexicale sunt separate între ele prin comentarii ºi spaþii. Unitãþile lexicale în limbajul Java pot fi: ● ● ● ● ●
Cuvinte cheie Identificatori Literali Separatori Operatori
4.2.1 Cuvinte cheie Cuvintele cheie sunt secvenþe de caractere ASCII rezervate de limbaj pentru uzul propriu. Cu ajutorul lor, Java îºi defineºte unitãþile sintactice de bazã. Nici un program nu poate sã utilizeze aceste secvenþe altfel decât în modul în care sunt definite de limbaj. Singura excepþie este aceea cã nu existã nici o restricþionare a apariþiei cuvintelor cheie în ºiruri de caractere sau comentarii. Cuvintele cheie ale limbajului Java sunt: abstract boolean break byte case cast catch char class const continue default do double else extends final finally float
for future generic goto if implements import inner instanceof intinterface long native new null operator outer package private protected
public rest return short static super switch synchronized this throw throws transient try var void volatile while byvalue
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap4.html (3 of 12)12.01.2006 23:07:47
Capitolul IV -- Structura lexicalã Java
Dintre acestea, cele îngroºate sunt efectiv folosite, iar restul sunt rezervate pentru viitoare extensii ale limbajului.
4.2.2 Identificatori Identificatorii Java sunt secvenþe nelimitate de litere ºi cifre Unicode, începând cu o literã. Identificatorii nu au voie sã fie identici cu cuvintele rezervate. Cifrele Unicode sunt definite în urmãtoarele intervale: Reprezentare Unicode
Caracter ASCII
Explicaþie
\u0030-\u0039
0-9
cifre ISO-LATIN-1
\u0660-\u0669
cifre Arabic-Indic
\u06f0-\u06f9
cifre Eastern ArabicIndic
\u0966-\u096f
cifre Devanagari
\u09e6-\u09ef
cifre Bengali
\u0a66-\ u0a6f
cifre Gurmukhi
\u0ae6-\u0aef
cifre Gujarati
\u0b66-\u0b6f
cifre Oriya
\u0be7-\u0bef
cifre Tamil
\u0c66-\u0c6f
cifre Telugu
\u0ce6-\u0cef
cifre Kannada
\u0d66-\u0d6f
cifre Malayalam
\u0e50-\u0e59
cifre Thai
\u0ed0-\u0ed9
cifre Lao
\u1040-\u1049
cifre Tibetan
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap4.html (4 of 12)12.01.2006 23:07:47
Capitolul IV -- Structura lexicalã Java
Tabelul 4.1 Cifrele Unicode.
Un caracter Unicode este o literã dacã este în urmãtoarele intervale ºi nu este cifrã: Reprezentare Unicode
Caracter ASCII
Explicaþie
\u0024
$
semnul dolar (din motive istorice)
\u0041-\u005a
A-Z
litere majuscule Latin
\u005f
_
underscore (din motive istorice)
\u0061-\u007a
a-z
litere minuscule Latin
\u00c0-\u00d6
diferite litere Latin cu diacritice
\u00d8-\u00f6
diferite litere Latin cu diacritice
\u00f8-\u00ff
diferite litere Latin cu diacritice
\u0100-\u1fff
alte alfabete ºi simboluri non-CJK
\u3040-\u318f
Hiragana, Katakana, Bopomofo, ºi Hangul
\u3300-\u337f
cuvinte pãtratice CJK
\u3400-\u3d2d
simboluri Hangul coreene
\u4e00-\u9fff
Han (Chinez, Japonez, Corean)
\uf900-\ufaff
compatibilitate Han
Tabelul 4.2 Literele Unicode.
4.2.3 Literali Un literal este modalitatea de bazã de exprimare în fiºierul sursã a valorilor pe care le pot lua tipurile primitive ºi tipul ºir de caractere. Cu ajutorul literalilor putem introduce valori constante în variabilele de tip primitiv sau în variabilele de tip ºir de caractere. În limbajul Java existã urmãtoarele tipuri de literali: file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap4.html (5 of 12)12.01.2006 23:07:47
Capitolul IV -- Structura lexicalã Java
● ● ● ● ●
literali întregi literali flotanþi literali booleeni literali caracter literali ºir de caractere
4.2.3.1 Literali întregi Literalii întregi pot fi reprezentaþi în baza 10, 16 sau 8. Toate caracterele care se folosesc pentru scrierea literalilor întregi fac parte din subsetul ASCII al setului Unicode. Literalii întregi pot fi întregi normali sau lungi. Literalii lungi se recunosc prin faptul cã se terminã cu sufixul l sau L. Un literal întreg este reprezentat pe 32 de biþi iar unul lung pe 64 de biþi. Un literal întreg în baza 10 începe cu o cifrã de la 1 la 9 ºi se continuã cu un ºir de cifre de la 0 la 9. Un literal întreg în baza 10 nu poate sã înceapã cu cifra 0, pentru cã acesta este semnul folosit pentru a semnaliza literalii scriºi în baza 8. Exemple de literali întregi în baza 10: 12356L234871234567890l Exemplul al doilea ºi al patrulea sunt literali întregi lungi. Pentru a exprima un literal întreg în baza 16 trebuie sã definim cifrele de la 10 la 15. Convenþia va fi urmãtoarea: 10 - a, A13 - d, D 11 - b, B14 - e, E 12 - c, C15 - f, F În plus, pentru cã un literal întreg în baza 16 poate începe cu o literã, vom adãuga prefixul 0x sau 0X. Dacã nu am adãuga acest sufix, compilatorul ar considera cã este vorba despre un identificator. Exemple de literali întregi în baza 16: 0xa340X1230x2c45L0xde123abccdL Ultimele douã exemple sunt literali întregi lungi.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap4.html (6 of 12)12.01.2006 23:07:47
Capitolul IV -- Structura lexicalã Java
În fine, pentru a reprezenta un literal întreg în baza 8, îl vom preceda cu cifra 0. Restul cifrelor pot fi oricare între 0 ºi 7. Cifrele 8 ºi 9 nu sunt admise în literalii întregi în baza 8. Exemple de literali întregi în baza 8: 0234500123001234567712345677L Valoarea maximã a unui literal întreg normal este de 2147483647 (231-1), scrisã în baza 10. În baza 16, cel mai mare literal pozitiv se scrie ca 0x7fffffff iar în baza 8 ca 017777777777. Toate trei scrierile reprezintã de fapt aceeaºi valoare, doar cã aceasta este exprimatã în baze diferite. Cea mai micã valoare a unui literal întreg normal este -2147483648 (-231), respectiv 0x80000000 ºi 020000000000. Valorile 0xffffffff ºi 037777777777 reprezintã amândouã valoarea -1. Specificarea în sursã a unui literal întreg normal care depãºeºte aceste limite reprezintã o eroare de compilare. Cu alte cuvinte, dacã folosim în sursã numãrul: 21474836470 de exemplu, fãrã sã punem sufixul de numãr lung dupã el, compilatorul va genera o eroare la analiza sursei. Valoarea maximã a unui literal întreg lung este, în baza 10, 9223372036854775807L (263-1). În octal, asta înseamnã 0777777777777777777777L iar în baza 16 0x7fffffffffffffffL. În mod asemãnãtor, valoarea minimã a unui literal întreg lung este -9223372036854775808L (-2631), în octal aceastã valoare este 0400000000000000000000L iar în baza 16 este 0x8000000000000000L. La fel ca ºi la literalii întregi normali, depãºirea acestor limite este o eroare de compilare. 4.2.3.2 Literali flotanþi Literalii flotanþi reprezintã numere reale. Ei sunt formaþi dintr-o parte întreagã, o parte fracþionarã, un exponent ºi un sufix de tip. Exponentul, dacã existã, este introdus de litera e sau E urmatã opþional de un semn al exponentului. Este obligatoriu sã existe mãcar o cifrã fie în partea întreagã fie în partea zecimalã ºi punctul zecimal sau litera e pentru exponent. Sufixul care indicã tipul flotantului poate fi f sau F în cazul în care avem o valoare flotantã normalã ºi d sau D dacã avem o valoare flotantã dublã. Dacã nu este specificat nici un sufix, valoarea este implicit dublã. Valoarea maximã a unui literal flotant normal este 3.40282347e+38f iar valoarea cea mai micã file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap4.html (7 of 12)12.01.2006 23:07:47
Capitolul IV -- Structura lexicalã Java
reprezentabilã este 1.40239846e-45f, ambele reprezentate pe 32 de biþi. Valoarea maximã reprezentabilã a unui literal flotant dublu este de 1.79769313486231570e+308 iar valoarea cea mai micã reprezentabilã este 4.94065645841246544e-324, ambele reprezentate pe 64 de biþi. La fel ca ºi la literalii întregi, este o eroare sã avem exprimat în sursã un literal mai mare decât valoarea maximã reprezentabilã sau mai mic decât cea mai micã valoare reprezentabilã. Exemple de literali flotanþi: 1.0e45f-3.456f0..01e-3 Primele douã exemple reprezintã literali flotanþi normali, iar celelalte literali flotanþi dubli. 4.2.3.3 Literali booleeni Literalii booleeni nu pot fi decât true sau false, primul reprezentând valoarea booleanã de adevãr iar celãlalt valoarea booleanã de fals. True ºi false nu sunt cuvinte rezervate ale limbajului Java, dar nu veþi putea folosi aceste cuvinte ca identificatori. 4.2.3.4 Literali caracter Un literal de tip caracter este utilizat pentru a exprima caracterele codului Unicode. Reprezentarea se face fie folosind o literã, fie o secvenþã escape. Secvenþele escape ne permit reprezentarea caracterelor care nu au reprezentare graficã ºi reprezentarea unor caractere speciale precum backslash ºi însãºi caracterul apostrof. Caracterele care au reprezentare graficã pot fi reprezentate între apostrofe, ca în exemplele: 'a' 'ª' ',' Pentru restul caracterelor Unicode trebuie sã folosim secvenþe escape. Dintre acestea, câteva sunt predefinite în Java, ºi anume: Secvenþã escape
Caracterul reprezentat
'\b'
caracterul backspace BS \u0008
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap4.html (8 of 12)12.01.2006 23:07:47
Capitolul IV -- Structura lexicalã Java
'\t'
caracterul tab orizontal HT \u0009
'\n'
caracterul linefeed LF \u000a
'\f'
caracterul formfeed FF \u000c
'\r'
caracterul carriage return CR \u000d
'\"'
caracterul ghilimele \u0022
'\''
caracterul apostrof \u0027
'\\'
caracterul backslash \u005c
Tabelul 4.3 Secvenþe escape predefinite în Java.
În formã generalã, o secvenþã escape se scrie sub una din formele: '\o' '\oo''\too' unde o este o cifrã octalã iar t este o cifrã octalã între 0 ºi 3. Nu este corect sã folosiþi ca valori pentru literale caracter secvenþa '\u000d' (caracterul ASCII CR), sau altele care reprezintã caractere speciale, pentru cã acestea fiind secvenþe escape Unicode sunt transformate foarte devreme în timpul procesãrii sursei în caractere CR ºi sunt interpretate ca terminatori de linie. Exemple de secvenþe escape: '\n''\u23a''\34' dacã dupã caracterul backslash urmeazã altceva decât: b, t, n, f, r, ", ', \, 0, 1, 2, 3, 4, 5, 6, 7 se va semnala o eroare de compilare. În acest moment secvenþele escape Unicode au fost deja înlocuite cu caractere Unicode native. Dacã u apare dupã \, se semnaleazã o eroare de compilare. 4.2.3.5 Literali ºir de caractere Un literal ºir de caractere este format din zero sau mai multe caractere între ghilimele. Caracterele care
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap4.html (9 of 12)12.01.2006 23:07:47
Capitolul IV -- Structura lexicalã Java
formeazã ºirul de caractere pot fi caractere grafice sau secvenþe escape ca cele definite la literalii caracter. Dacã un literal ºir de caractere conþine în interior un caracter terminator de linie va fi semnalatã o eroare de compilare. Cu alte cuvinte, nu putem avea în sursã ceva de forma: "Acesta este greºit!” chiar dacã aparent exprimarea ar reprezenta un ºir format din caracterele A, c, e, s, t, a, spaþiu, e, s, t, e, linie nouã, g, r, e, º, i, t, !. Dacã dorim sã introducem astfel de caractere terminatoare de linie într-un ºir va trebui sã folosim secvenþe escape ca în: “Acesta este\ngreºit” Dacã ºirul de caractere este prea lung, putem sã-l spargem în bucãþi mai mici pe care sã le concatenãm cu operatorul +. Fiecare ºir de caractere este în fapt o instanþã a clasei de obiecte String declaratã standard în pachetul java.lang. Exemple de ºiruri de caractere: """\"""ªir de caractere""unu" + "doi" Primul ºir de caractere din exemplu nu conþine nici un caracter ºi se numeºte ºirul vid. Ultimul exemplu este format din douã ºiruri distincte concatenate.
4.2.4 Separatori Un separator este un caracter care indicã sfârºitul unei unitãþi lexicale ºi începutul alteia. Separatorii sunt necesari atunci când unitãþi lexicale diferite sunt scrise fãrã spaþii între ele. Acestea se pot totuºi separa dacã unele dintre ele conþin caractere separatori. În Java separatorii sunt urmãtorii: ( ) { } [ ] ; , . Exemple de separare: a[i]sin(56) În primul exemplu nu avem o singurã unitate lexicalã ci patru: a, [, i, ]. Separatorii [ ºi ] ne dau aceastã
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap4.html (10 of 12)12.01.2006 23:07:47
Capitolul IV -- Structura lexicalã Java
informaþie. În al doilea exemplu, unitãþile lexicale sunt tot 4 sin, (, 56, ). Atenþie, separatorii participã în acelaºi timp ºi la construcþia sintaxei limbajului. Ei nu sunt identici cu spaþiile deºi, ca ºi acestea, separã unitãþi lexicale diferite.
4.2.5 Operatori Operatorii reprezintã simboluri grafice pentru operaþiile elementare definite de limbajul Java. Despre operatori vom discuta mai mult atunci când vom prezenta expresiile. Deocamdatã, iatã lista tuturor operatorilor limbajului Java: =>=!=&&||++-+-*/&|^% >>> +=-=*=/=&=|=^=%==>>>= Sã mai precizãm deocamdatã cã toþi operatorii joacã ºi rol de separatori. Cu alte cuvinte, din secvenþa de caractere: vasile+gheorghe putem extrage trei unitãþi lexicale, vasile, + ºi gheorghe.
4.3 Comentarii Un comentariu este o secvenþã de caractere existentã în fiºierul sursã dar care serveºte doar pentru explicarea sau documentarea sursei ºi nu afecteazã în nici un fel semantica programelor. În Java existã trei feluri de comentarii: ●
●
●
Comentarii pe mai multe linii, închise între /* ºi */. Toate caracterele dintre cele douã secvenþe sunt ignorate. Comentarii pe mai multe linii care þin de documentaþie, închise între /** ºi */. Textul dintre cele douã secvenþe este automat mutat în documentaþia aplicaþiei de cãtre generatorul automat de documentaþie. Comentarii pe o singurã linie care încep cu //. Toate caracterele care urmeazã acestei secvenþe pânã la primul caracter sfârºit de linie sunt ignorate.
În Java, nu putem sã scriem comentarii în interiorul altor comentarii. La fel, nu putem introduce comentarii în interiorul literalilor caracter sau ºir de caractere. Secvenþele /* ºi */ pot sã aparã pe o linie dupã secvenþa // dar îºi pierd semnificaþia. La fel se întâmplã cu secvenþa // în comentarii care încep cu / file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap4.html (11 of 12)12.01.2006 23:07:47
Capitolul IV -- Structura lexicalã Java
* sau /**. Ca urmare, urmãtoarea secvenþã de caractere formeazã un singur comentariu: /* acest comentariu /* // /* se terminã abia aici: */
[cuprins] (C) IntegraSoft 1996-1998
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap4.html (12 of 12)12.01.2006 23:07:47
Capitolul V -- Componente de bazã ale programelor Java
Capitolul V Componente de bazã ale programelor Java 5.1 Variabile 5.2 Expresii 5.3 Instrucþiuni
[cuprins] (C) IntegraSoft 1996-1998
file:///C|/Documents%20and%20Settings/Luminita/Desktop/...JAVA%20in%20romana/Carte%20JAVA%20in%20romana/cap5.html12.01.2006 23:07:47
Capitolul VI -- Obiecte Java
Capitolul VI Obiecte Java 6.1 Declaraþia unei noi clase de obiecte Pasul 1: Stabilirea conceptului reprezentat de clasa de obiecte Pasul 2: Stabilirea numelui clasei de obiecte Pasul 3: Stabilirea superclasei Pasul 4: Stabilirea interfeþelor pe care le respectã clasa Pasul 5: Stabilirea modificatorilor clasei Pasul 6: Scrierea corpului declaraþiei Stop: Forma generalã a unei declaraþii de clasã 6.2 Variabilele unei clase 6.2.1 Modificatori 6.2.2 Protecþie 6.2.3 Accesarea unei variabile 6.2.4 Vizibilitate 6.2.5 Variabile predefinite: this ºi super 6.3 Metodele unei clase 6.3.1 Declararea metodelor 6.3.1.1 Numele ºi parametrii metodelor 6.3.1.2 Modificatori de metode 6.3.1.2.1 Metode statice 6.3.1.2.2 Metode abstracte 6.3.1.2.3 Metode finale 6.3.1.2.4 Metode native 6.3.1.2.5 Metode sincronizate 6.3.1.3 Protejarea metodelor 6.3.2 Clauze throws 6.3.3 Apelul metodelor 6.3.4 Valoarea de retur a unei metode
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (1 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
6.3.5 Vizibilitate 6.4 Iniþializatori statici 6.5 Constructori ºi finalizatori 6.5.1 constructori 6.5.2 Finalizatori 6.5.3 Crearea instanþelor 6.6 Derivarea claselor 6.7 Interfeþe În primul rând sã observãm cã, atunci când scriem programe în Java nu facem altceva decât sã definim noi ºi noi clase de obiecte. Dintre acestea, unele vor reprezenta însãºi aplicaþia noastrã în timp ce altele vor fi necesare pentru rezolvarea problemei la care lucrãm. Ulterior, atunci când dorim sã lansãm aplicaþia în execuþie nu va trebui decât sã instanþiem un obiect reprezentând aplicaþia în sine ºi sã apelãm o metodã de pornire definitã de cãtre aplicaþie, metodã care de obicei are un nume ºi un set de parametri bine fixate. Totuºi, numele acestei metode depinde de contextul în care este lansatã aplicaþia noastrã. Aceastã abordare a construcþiei unei aplicaþii ne spune printre altele cã vom putea lansa oricâte instanþe ale aplicaþiei noastre dorim, pentru cã fiecare dintre acestea va reprezenta un obiect în memorie având propriile lui valori pentru variabile. Execuþia instanþelor diferite ale aplicaþiei va urma desigur cãi diferite în funcþie de interacþiunea fiecãreia cu un utilizator, eventual acelaºi, ºi în funcþie de unii parametri pe care îi putem defini în momentul creãrii fiecãrei instanþe.
6.1 Declaraþia unei noi clase de obiecte Pasul 1: Stabilirea conceptului reprezentat de clasa de obiecte Sã vedem ce trebuie sã definim atunci când dorim sã creãm o nouã clasã de obiecte. În primul rând trebuie sã stabilim care este conceptul care este reprezentat de cãtre noua clasã de obiecte ºi sã definim informaþiile memorate în obiect ºi modul de utilizare a acestuia. Acest pas este cel mai important din tot procesul de definire al unei noi clase de obiecte. Este necesar sã încercaþi sã respectaþi douã reguli oarecum antagonice. Una dintre ele spune cã nu trebuiesc create mai multe clase de obiecte decât este nevoie, pentru a nu face dificilã înþelegerea modului de lucru al aplicaþiei la care lucraþi. Cea de-a doua regulã spune cã nu este bine sã mixaþi într-un singur obiect funcþionalitãþi care nu au nimic în comun, creând astfel clase care corespund la douã concepte diferite. Medierea celor douã reguli nu este întotdeauna foarte uºoarã. Oricum, vã va fi mai uºor dacã pãstraþi în minte faptul cã fiecare clasã pe care o definiþi trebuie sã corespundã unui concept real bine definit, necesar la rezolvarea problemei la care lucraþi. ªi mai pãstraþi în minte ºi faptul cã este inutil sã lucraþi cu concepte foarte generale atunci când aplicaþia dumneavoastrã nu are nevoie decât de o particularizare a acestora. Riscaþi sã pierdeþi controlul dezvoltãrii acestor clase de obiecte prea generale ºi sã îngreunaþi dezvoltarea file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (2 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
aplicaþiei.
Pasul 2: Stabilirea numelui clasei de obiecte Dupã ce aþi stabilit exact ce doriþi de la noua clasã de obiecte, sunteþi în mãsurã sã gãsiþi un nume pentru noua clasã, nume care trebuie sã urmeze regulile de construcþie ale identificatorilor limbajului Java definite în capitolul anterior. Stabilirea unui nume potrivit pentru o nouã clasã nu este întotdeauna un lucru foarte uºor. Problema este cã acest nume nu trebuie sã fie exagerat de lung dar trebuie sã exprime suficient de bine destinaþia clasei. Regulile de denumire ale claselor sunt rezultatul experienþei fiecãruia sau al unor convenþii de numire stabilite anterior. De obicei, numele de clase este bine sã înceapã cu o literã majusculã. Dacã numele clasei conþine în interior mai multe cuvinte, aceste cuvinte trebuie de asemenea începute cu literã majusculã. Restul caracterelor vor fi litere minuscule. De exemplu, dacã dorim sã definim o clasã de obiecte care implementeazã conceptul de motor Otto vom folosi un nume ca MotorOtto pentru noua clasã ce trebuie creatã. La fel, vom defini clasa MotorDiesel sau MotorCuReacþie. Dacã însã avem nevoie sã definim o clasã separatã pentru un motor Otto cu cilindri în V ºi carburator, denumirea clasei ca MotorOttoCuCilindriÎnVªiCarburator nu este poate cea mai bunã soluþie. Poate cã în acest caz este preferabilã o prescurtare de forma MotorOttoVC. Desigur, acestea sunt doar câteva remarci la adresa acestei probleme ºi este în continuare necesar ca în timp sã vã creaþi propria convenþie de denumire a claselor pe care le creaþi.
Pasul 3: Stabilirea superclasei În cazul în care aþi definit deja o parte din funcþionalitatea de care aveþi nevoie într-o altã superclasã, puteþi sã derivaþi noua clasã de obiecte din clasa deja existentã. Dacã nu existã o astfel de clasã, noua clasã va fi automat derivatã din clasa de obiecte predefinitã numitã Object. În Java, clasa Object este superclasã direct sau indirect pentru orice altã clasã de obiecte definitã de utilizator. Alegerea superclasei din care derivaþi noua clasã de obiecte este foarte importantã pentru cã vã ajutã sã refolosiþi codul deja existent. Totuºi, nu alegeþi cu uºurinþã superclasa unui obiect pentru cã astfel puteþi încãrca obiectele cu o funcþionalitate inutilã, existentã în superclasã. Dacã nu existã o clasã care sã vã ofere doar funcþionalitatea de care aveþi nevoie, este preferabil sã derivaþi noua clasã direct din clasa Object ºi sã apelaþi indirect funcþionalitatea pe care o doriþi.
Pasul 4: Stabilirea interfeþelor pe care le respectã clasa Stabilirea acestor interfeþe are dublu scop. În primul rând ele instruiesc compilatorul sã verifice dacã noua clasã respectã cu adevãrat toate interfeþele pe care le-a declarat, cu alte cuvinte defineºte toate metodele declarate în aceste interfeþe. A doua finalitate este aceea de a permite compilatorului sã foloseascã instanþele noii clase oriunde aplicaþia declarã cã este nevoie de un obiect care implementeazã interfeþele declarate.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (3 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
O clasã poate sã implementeze mai multe interfeþe sau niciuna.
Pasul 5: Stabilirea modificatorilor clasei În unele cazuri trebuie sã oferim compilatorului informaþii suplimentare relative la modul în care vom folosi clasa nou creatã pentru ca acesta sã poatã executa verificãri suplimentare asupra descrierii clasei. În acest scop, putem defini o clasã ca fiind abstractã, finalã sau publicã folosindu-ne de o serie de cuvinte rezervate numite modificatori. Modificatorii pentru tipurile de clase de mai sus sunt respectiv: abstract, final ºi public. În cazul în care declarãm o clasã de obiecte ca fiind abstractã, compilatorul va interzice instanþierea acestei clase. Dacã o clasã este declaratã finalã, compilatorul va avea grijã sã nu putem deriva noi subclase din aceastã clasã. În cazul în care declarãm în acelaºi timp o clasã de obiecte ca fiind abstractã ºi finalã, eroarea va fi semnalatã încã din timpul compilãrii pentru cã cei doi modificatori se exclud. Pentru ca o clasã sã poatã fi folositã ºi în exteriorul contextului în care a fost declaratã ea trebuie sã fie declaratã publicã. Mai mult despre acest aspect în paragraful referitor la structura programelor. Pânã atunci, sã spunem cã orice clasã de obiecte care va fi instanþiatã ca o aplicaþie trebuie declaratã publicã.
Pasul 6: Scrierea corpului declaraþiei În sfârºit, dupã ce toþi ceilalþi paºi au fost efectuaþi, putem trece la scrierea corpului declaraþiei de clasã. În principal, aici vom descrie variabilele clasei împreunã cu metodele care lucreazã cu acestea. Tot aici putem preciza ºi gradele de protejare pentru fiecare dintre elementele declaraþiei. Uneori numim variabilele ºi metodele unei clase la un loc ca fiind câmpurile clasei. Subcapitolele urmãtoare vor descrie în amãnunt corpul unei declaraþii.
Stop: Forma generalã a unei declaraþii de clasã Sintaxa exactã de declarare a unei clase aratã în felul urmãtor: { abstract | final | public }* class NumeClasã [ extends NumeSuperclasã ] [ implements NumeInterfaþã [ , NumeInterfaþã ]* ] { [ CâmpClasã ]* }
6.2 Variabilele unei clase În interiorul claselor se pot declara variabile. Aceste variabile sunt specifice clasei respective. Fiecare dintre file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (4 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
ele trebuie sã aibã un tip, un nume ºi poate avea iniþializatori. În afarã de aceste elemente, pe care le-am prezentat deja în secþiunea în care am prezentat variabilele, variabilele definite în interiorul unei clase pot avea definiþi o serie de modificatori care altereazã comportarea variabilei în interiorul clasei, ºi o specificaþie de protecþie care defineºte cine are dreptul sã acceseze variabila respectivã.
6.2.1 Modificatori Modificatorii sunt cuvinte rezervate Java care precizeazã sensul unei declaraþii. Iatã lista acestora: static final transient volatile Dintre aceºtia, transient nu este utilizat în versiunea curentã a limbajului Java. Pe viitor va fi folosit pentru a specifica variabile care nu conþin informaþii care trebuie sã rãmânã persistente la terminarea programului. Modificatorul volatile specificã faptul cã variabila respectivã poate fi modificatã asincron cu rularea aplicaþiei. În aceste cazuri, compilatorul trebuie sã-ºi ia mãsuri suplimentare în cazul generãrii ºi optimizãrii codului care se adreseazã acestei variabile. Modificatorul final este folosit pentru a specifica o variabilã a cãrei valoare nu poate fi modificatã. Variabila respectivã trebuie sã primeascã o valoare de iniþializare chiar în momentul declaraþiei. Altfel, ea nu va mai putea fi iniþializatã în viitor. Orice încercare ulterioarã de a seta valori la aceastã variabilã va fi semnalatã ca eroare de compilare. Modificatorul static este folosit pentru a specifica faptul cã variabila are o singurã valoare comunã tuturor instanþelor clasei în care este declaratã. Modificarea valorii acestei variabile din interiorul unui obiect face ca modificarea sã fie vizibilã din celelalte obiecte. Variabilele statice sunt iniþializate la încãrcarea codului specific unei clase ºi existã chiar ºi dacã nu existã nici o instanþã a clasei respective. Din aceastã cauzã, ele pot fi folosite de metodele statice.
6.2.2 Protecþie În Java existã patru grade de protecþie pentru o variabilã aparþinând unei clase: ● ● ● ●
privatã protejatã publicã prietenoasã
O variabilã publicã este accesibilã oriunde este accesibil numele clasei. Cuvântul rezervat este public.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (5 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
O variabilã protejatã este accesibilã în orice clasã din pachetul cãreia îi aparþine clasa în care este declaratã. În acelaºi timp, variabila este accesibilã în toate subclasele clasei date, chiar dacã ele aparþin altor pachete. Cuvântul rezervat este protected. O variabilã privatã este accesibilã doar în interiorul clasei în care a fost declaratã. Cuvântul rezervat este private. O variabilã care nu are nici o declaraþie relativã la gradul de protecþie este automat o variabilã prietenoasã. O variabilã prietenoasã este accesibilã în pachetul din care face parte clasa în interiorul cãreia a fost declaratã, la fel ca ºi o variabilã protejatã. Dar, spre deosebire de variabilele protejate, o variabilã prietenoasã nu este accesibilã în subclasele clasei date dacã aceste sunt declarate ca aparþinând unui alt pachet. Nu existã un cuvânt rezervat pentru specificarea explicitã a variabilelor prietenoase. O variabilã nu poate avea declarate mai multe grade de protecþie în acelaºi timp. O astfel de declaraþie este semnalatã ca eroare de compilare.
6.2.3 Accesarea unei variabile Accesarea unei variabile declarate în interiorul unei clasei se face folosindu-ne de o expresie de forma: ReferinþãInstanþã.NumeVariabilã Referinþa cãtre o instanþã trebuie sã fie referinþã cãtre clasa care conþine variabila. Referinþa poate fi valoarea unei expresii mai complicate, ca de exemplu un element dintr-un tablou de referinþe. În cazul în care avem o variabilã staticã, aceasta poate fi accesatã ºi fãrã sã deþinem o referinþã cãtre o instanþã a clasei. Sintaxa este, în acest caz: NumeClasã.NumeVariabilã
6.2.4 Vizibilitate O variabilã poate fi ascunsã de declaraþia unei alte variabile cu acelaºi nume. De exemplu, dacã într-o clasã avem declaratã o variabilã cu numele unu ºi într-o subclasã a acesteia avem declaratã o variabilã cu acelaºi nume, atunci variabila din superclasã este ascunsã de cea din clasã. Totuºi, variabila din superclasã existã încã ºi poate fi accesatã în mod explicit. Expresia de referire este, în acest caz: NumeSuperClasã.NumeVariabilã sau super.NumeVariabilã
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (6 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
în cazul în care superclasa este imediatã. La fel, o variabilã a unei clase poate fi ascunsã de o declaraþie de variabilã dintr-un bloc de instrucþiuni. Orice referinþã la ea va trebui fãcutã în mod explicit. Expresia de referire este, în acest caz: this.NumeVariabilã
6.2.5 Variabile predefinite: this ºi super În interiorul fiecãrei metode non-statice dintr-o clasã existã predefinite douã variabile cu semnificaþie specialã. Cele douã variabile sunt de tip referinþã ºi au aceeaºi valoare ºi anume o referinþã cãtre obiectul curent. Diferenþa dintre ele este tipul. Prima dintre acestea este variabila this care are tipul referinþã cãtre clasa în interiorul cãreia apare metoda. A doua este variabila super al cãrei tip este o referinþã cãtre superclasa imediatã a clasei în care apare metoda. În interiorul obiectelor din clasa Object nu se poate folosi referinþa super pentru cã nu existã nici o superclasã a clasei de obiecte Object. În cazul în care super este folositã la apelul unui constructor sau al unei metode, ea acþioneazã ca un cast cãtre superclasa imediatã.
6.3 Metodele unei clase Fiecare clasã îºi poate defini propriile sale metode pe lângã metodele pe care le moºteneºte de la superclasa sa. Aceste metode definesc operaþiile care pot fi executate cu obiectul respectiv. În cazul în care una dintre metodele moºtenite nu are o implementare corespunzãtoare în superclasã, clasa îºi poate redefini metoda dupã cum doreºte. În plus, o clasã îºi poate defini metode de construcþie a obiectelor ºi metode de eliberare a acestora. Metodele de construcþie sunt apelate ori de câte ori este alocat un nou obiect din clasa respectivã. Putem declara mai multe metode de construcþie, ele diferind prin parametrii din care trebuie construit obiectul. Metodele de eliberare a obiectului sunt cele care elibereazã resursele ocupate de obiect în momentul în care acesta este distrus de cãtre mecanismul automat de colectare de gunoaie. Fiecare clasã are o singurã metodã de eliberare, numitã ºi finalizator. Apelarea acestei metode se face de cãtre sistem ºi nu existã nici o cale de control al momentului în care se produce acest apel.
6.3.1 Declararea metodelor Pentru a declara o metodã, este necesar sã declarãm numele acesteia, tipul de valoare pe care o întoarce, parametrii metodei precum ºi un bloc în care sã descriem instrucþiunile care trebuiesc executate atunci când metoda este apelatã. În plus, orice metodã are un numãr de modificatori care descriu proprietãþile metodei ºi file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (7 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
modul de lucru al acesteia. Declararea precum ºi implementarea metodelor se face întotdeauna în interiorul declaraþiei de clasã. Nu existã nici o cale prin care sã putem scrie o parte dintre metodele unei clase într-un fiºier separat care sã facã referinþã apoi la declaraþia clasei. În formã generalã, declaraþia unei metode aratã în felul urmãtor: [Modificator]* TipRezultat Declaraþie [ClauzeThrows]* CorpulMetodei Modificatorii precum ºi clauzele throws pot sã lipseascã. 6.3.1.1 Numele ºi parametrii metodelor Recunoaºterea unei anumite metode se face dupã numele ºi tipul parametrilor sãi. Pot exista metode cu acelaºi nume dar având parametri diferiþi. Acest fenomen poartã numele de supraîncãrcarea numelui unei metode. Numele metodei este un identificator Java. Avem toatã libertatea în a alege numele pe care îl dorim pentru metodele noastre, dar în general este preferabil sã alegem nume care sugereazã utilizarea metodei. Numele unei metode începe de obicei cu literã micã. Dacã acesta este format din mai multe cuvinte, litera de început a fiecãrui cuvânt va fi majusculã. În acest mod numele unei metode este foarte uºor de citit ºi de depistat în sursã. Parametrii metodei sunt în realitate niºte variabile care sunt iniþializate în momentul apelului cu valori care controleazã modul ulterior de execuþie. Aceste variabile existã pe toatã perioada execuþiei metodei. Se pot scrie metode care sã nu aibã nici un parametru. Fiind o variabilã, fiecare parametru are un tip ºi un nume. Numele trebuie sã fie un identificator Java. Deºi avem libertatea sã alegem orice nume dorim, din nou este preferabil sã alegem nume care sã sugereze scopul la care va fi utilizat parametrul respectiv. Tipul unui parametru este oricare dintre tipurile valide în Java. Acestea poate fi fie un tip primitiv, fie un tip referinþã cãtre obiect, interfaþã sau tablou. În momentul apelului unei metode, compilatorul încearcã sã gãseascã o metodã în interiorul clasei care sã aibã acelaºi nume cu cel apelat ºi acelaºi numãr de parametri ca ºi apelul. Mai mult, tipurile parametrilor de apel trebuie sã corespundã cu tipurile parametrilor declaraþi ai metodei gãsite sau sã poatã fi convertiþi la aceºtia. Dacã o astfel de metodã este gãsitã în declaraþia clasei sau în superclasele acesteia, parametrii de apel sunt convertiþi cãtre tipurile declarate ºi se genereazã apelul cãtre metoda respectivã.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (8 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
Este o eroare de compilare sã declarãm douã metode cu acelaºi nume, acelaºi numãr de parametri ºi acelaºi tip pentru parametrii corespunzãtori. Într-o asemenea situaþie, compilatorul n-ar mai ºti care metodã trebuie apelatã la un moment dat. De asemenea, este o eroare de compilare sã existe douã metode care se potrivesc la acelaºi apel. Acest lucru se întâmplã când nici una dintre metodele existente nu se potriveºte exact ºi când existã douã metode cu acelaºi nume ºi acelaºi numãr de parametri ºi, în plus, parametrii de apel se pot converti cãtre parametrii declaraþi ai ambelor metode. Rezolvarea unei astfel de probleme se face prin conversia explicitã (cast) de cãtre programator a valorilor de apel spre tipurile exacte ale parametrilor metodei pe care dorim sã o apelãm în realitate. În fine, forma generalã de declaraþie a numelui ºi parametrilor unei metode este: NumeMetodã( [TipParametru NumeParametru] [,TipParametru NumeParametru]* ) 6.3.1.2 Modificatori de metode Modificatorii sunt cuvinte cheie ale limbajului Java care specificã proprietãþi suplimentare pentru o metodã. Iatã lista completã a acestora în cazul metodelor: ● ● ● ● ●
static - pentru metodele statice abstract - pentru metodele abstracte final - pentru metodele finale native - pentru metodele native synchronized - pentru metodele sincronizate
6.3.1.2.1 Metode statice
În mod normal, o metodã a unei clase se poate apela numai printr-o instanþã a clasei respective sau printr-o instanþã a unei subclase. Acest lucru se datoreazã faptului cã metoda face apel la o serie de variabile ale clasei care sunt memorate în interiorul instanþei ºi care au valori diferite în instanþe diferite. Astfel de metode se numesc metode ale instanþelor clasei. Dupã cum ºtim deja, existã ºi un alt tip de variabile, ºi anume variabilele de clasã sau variabilele statice care sunt comune tuturor instanþelor clasei respective ºi existã pe toatã perioada de timp în care clasa este încãrcatã în memorie. Aceste variabile pot fi accesate fãrã sã avem nevoie de o instanþã a clasei respective. În mod similar existã ºi metode statice. Aceste metode nu au nevoie de o instanþã a clasei sau a unei subclase pentru a putea fi apelate pentru cã ele nu au voie sã foloseascã variabile care sunt memorate în interiorul instanþelor. În schimb, aceste metode pot sã foloseascã variabilele statice declarate în interiorul clasei.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (9 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
Orice metodã staticã este implicit ºi finalã. 6.3.1.2.2 Metode abstracte
Metodele abstracte sunt metode care nu au corp de implementare. Ele sunt declarate numai pentru a forþa subclasele care vor sã aibã instanþe sã implementeze metodele respective. Metodele abstracte trebuie declarate numai în interiorul claselor care au fost declarate abstracte. Altfel compilatorul va semnala o eroare de compilare. Orice subclasã a claselor abstracte care nu este declaratã abstractã trebuie sã ofere o implementare a acestor metode, altfel va fi generatã o eroare de compilare. Prin acest mecanism ne asigurãm cã toate instanþele care pot fi convertite cãtre clasa care conþine definiþia unei metode abstracte au implementatã metoda respectivã dar, în acelaºi timp, nu este nevoie sã implementãm în nici un fel metoda chiar în clasa care o declarã pentru cã nu ºtim pe moment cum va fi implementatã. O metodã staticã nu poate fi declaratã ºi abstractã pentru cã o metodã staticã este implicit finalã ºi nu poate fi rescrisã. 6.3.1.2.3 Metode finale
O metodã finalã este o metodã care nu poate fi rescrisã în subclasele clasei în care a fost declaratã. O metodã este rescrisã într-o subclasã dacã aceasta implementeazã o metodã cu acelaºi nume ºi acelaºi numãr ºi tip de parametri ca ºi metoda din superclasã. Declararea metodelor finale este utilã în primul rând compilatorului care poate genera metodele respective direct în codul rezultat fiind sigur cã metoda nu va avea nici o altã implementare în subclase. 6.3.1.2.4 Metode native
Metodele native sunt metode care sunt implementate pe o cale specificã unei anumite platforme. De obicei aceste metode sunt implementate în C sau în limbaj de asamblare. Metoda propriu-zisã nu poate avea corp de implementare pentru cã implementarea nu este fãcutã în Java. În rest, metodele native sunt exact ca orice altã metodã Java. Ele pot fi moºtenite, pot fi statice sau nu, pot fi finale sau nu, pot sã rescrie o metodã din superclasã ºi pot fi la rândul lor rescrise în subclase. 6.3.1.2.5 Metode sincronizate
O metodã sincronizatã este o metodã care conþine cod critic pentru un anumit obiect sau clasã ºi nu poate fi rulatã în paralel cu nici o altã metodã criticã sau cu o instrucþiune synchronized referitoare la acelaºi obiect sau clasã.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (10 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
Înainte de execuþia metodei, obiectul sau clasa respectivã sunt blocate. La terminarea metodei, acestea sunt deblocate. Dacã metoda este staticã atunci este blocatã o întreagã clasã, clasa din care face parte metoda. Altfel, este blocatã doar instanþa în contextul cãreia este apelatã metoda. 6.3.1.3 Protejarea metodelor Accesul la metodele unei clase este protejat în acelaºi fel ca ºi accesul la variabilele clasei. În Java existã patru grade de protecþie pentru o metodã aparþinând unei clase: ● ● ● ●
privatã protejatã publicã prietenoasã
O metodã declaratã publicã este accesibilã oriunde este accesibil numele clasei. Cuvântul rezervat este public. O metodã declaratã protejatã este accesibilã în orice clasã din pachetul cãreia îi aparþine clasa în care este declaratã. În acelaºi timp, metoda este accesibilã în toate subclasele clasei date, chiar dacã ele aparþin altor pachete. Cuvântul rezervat este protected. O metodã declaratã privatã este accesibilã doar în interiorul clasei în care a fost declaratã. Cuvântul rezervat este private. O metodã care nu are nici o declaraþie relativã la gradul de protecþie este automat o metodã prietenoasã. O metodã prietenoasã este accesibilã în pachetul din care face parte clasa în interiorul cãreia a fost declaratã la fel ca ºi o metodã protejatã. Dar, spre deosebire de metodele protejate, o metodã prietenoasã nu este accesibilã în subclasele clasei date dacã aceste sunt declarate ca aparþinând unui alt pachet. Nu existã un cuvânt rezervat pentru specificarea explicitã a metodelor prietenoase. O metodã nu poate avea declarate mai multe grade de protecþie în acelaºi timp. O astfel de declaraþie este semnalatã ca eroare de compilare.
6.3.2 Clauze throws Dacã o metodã poate arunca o excepþie, adicã sã apeleze instrucþiunea throw, ea trebuie sã declare tipul acestor excepþii într-o clauzã throws. Sintaxa acesteia este: throws NumeTip [, NumeTip]* Numele de tipuri specificate în clauza throws trebuie sã fie accesibile ºi sã fie asignabile la tipul de clasã file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (11 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
Throwable. Dacã o metodã conþine o clauzã throws, este o eroare de compilare ca metoda sã arunce un obiect care nu este asignabil la compilare la tipurile de clase Error, RunTimeException sau la tipurile de clase specificate în clauza throws. Dacã o metodã nu are o clauzã throws, este o eroare de compilare ca aceasta sã poatã arunca o excepþie normalã din interiorul corpului ei.
6.3.3 Apelul metodelor Pentru a apela o metodã a unei clase este necesar sã dispunem de o cale de acces la metoda respectivã. În plus, trebuie sã dispunem de drepturile necesare apelului metodei. Sintaxa efectivã de acces este urmãtoarea: CaleDeAcces.Metodã( Parametri ) În cazul în care metoda este staticã, pentru a specifica o cale de acces este suficient sã furnizãm numele clasei în care a fost declaratã metoda. Accesul la numele clasei se poate obþine fie importând clasa sau întreg pachetul din care face parte clasa fie specificând în clar numele clasei ºi drumul de acces cãtre aceasta. De exemplu, pentru a accesa metoda random definitã static în clasa Math aparþinând pachetului java. lang putem scrie: double aleator = Math.random(); sau, alternativ: double aleator = java.lang.Math.random(); În cazul claselor definite în pachetul java.lang nu este necesar nici un import pentru cã acestea sunt implicit importate de cãtre compilator. Cea de-a doua cale de acces este existenþa unei instanþe a clasei respective. Prin aceastã instanþã putem accesa metodele care nu sunt declarate statice, numite uneori ºi metode ale instanþelor clasei. Aceste metode au nevoie de o instanþã a clasei pentru a putea lucra, pentru cã folosesc variabile non-statice ale clasei sau apeleazã alte metode non-statice. Metodele primesc acest obiect ca pe un parametru ascuns. De exemplu, având o instanþã a clasei Object sau a unei subclase a acesteia, putem obþine o reprezentare sub formã de ºir de caractere prin: Object obiect = new Object(); String sir = obiect.toString();
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (12 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
În cazul în care apelãm o metodã a clasei din care face parte ºi metoda apelantã putem sã renunþãm la calea de acces în cazul metodelor statice, scriind doar numele metodei ºi parametrii. Pentru metodele specifice instanþelor, putem renunþa la calea de acces, dar în acest caz metoda acceseazã aceeaºi instanþã ca ºi metoda apelantã. În cazul în care metoda apelantã este staticã, specificarea unei instanþe este obligatorie în cazul metodelor de instanþã. Parametrii de apel servesc împreunã cu numele la identificarea metodei pe care dorim sã o apelãm. Înainte de a fi transmiºi, aceºtia sunt convertiþi cãtre tipurile declarate de parametri ai metodei, dupã cum este descris mai sus. Specificarea parametrilor de apel se face separându-i prin virgulã. Dupã ultimul parametru nu se mai pune virgulã. Dacã metoda nu are nici un parametru, parantezele rotunde sunt în continuare necesare. Exemple de apel de metode cu parametri: String numar = String.valueOf( 12 ); // 12 -> String double valoare = Math.abs( 12.54 ); // valoare absolutã String prima = numar.substring( 0, 1 ); // prima litera ^
6.3.4 Valoarea de retur a unei metode O metodã trebuie sã-ºi declare tipul valorii pe care o întoarce. În cazul în care metoda doreºte sã specifice explicit cã nu întoarce nici o valoare, ea trebuie sã declare ca tip de retur tipul void ca în exemplul: void a() { … } În caz general, o metodã întoarce o valoare primitivã sau un tip referinþã. Putem declara acest tip ca în: Thread cautaFirulCurent() { … } long abs( int valoare ) { … } Pentru a returna o valoare ca rezultat al execuþiei unei metode, trebuie sã folosim instrucþiunea return, aºa cum s-a arãtat în secþiunea dedicatã instrucþiunilor. Instrucþiunea return trebuie sã conþinã o expresie a cãrei valoare sã poatã fi convertitã la tipul declarat al valorii de retur a metodei. De exemplu: long abs( int valoare ) { return Math.abs( valoare ); } Metoda staticã abs din clasa Math care primeºte un parametru întreg returneazã tot un întreg. În exemplul file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (13 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
nostru, instrucþiunea return este corectã pentru cã existã o cale de conversie de la întreg la întreg lung, conversie care este apelatã automat de compilator înainte de ieºirea din metodã. În schimb, în exemplul urmãtor: int abs( long valoare ) { return Math.abs( valoare ); } compilatorul va genera o eroare de compilare pentru cã metoda staticã abs din clasa Math care primeºte ca parametru un întreg lung întoarce tot un întreg lung, iar un întreg lung nu poate fi convertit sigur la un întreg normal pentru cã existã riscul deteriorãrii valorii, la fel ca la atribuire. Rezolvarea trebuie sã conþinã un cast explicit: int abs( long valoare ) { return ( int )Math.abs( valoare ); } În cazul în care o metodã este declaratã void, putem sã ne întoarcem din ea folosind instrucþiunea return fãrã nici o expresie. De exemplu: void metoda() { … if( … ) return; … } Specificarea unei expresii în acest caz duce la o eroare de compilare. La fel ºi în cazul în care folosim instrucþiunea return fãrã nici o expresie în interiorul unei metode care nu este declaratã void.
6.3.5 Vizibilitate O metodã este vizibilã dacã este declaratã în clasa prin care este apelatã sau într-una din superclasele acesteia. De exemplu, dacã avem urmãtoarea declaraþie: class A { … void a() { … } } class B extends A { void b() { a(); file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (14 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
c(); … } void c() { .. } … } Apelul metodei a în interiorul metodei b din clasa B este permis pentru cã metoda a este declaratã în interiorul clasei A care este superclasã pentru clasa B. Apelul metodei c în aceeaºi metodã b este permis pentru cã metoda c este declaratã în aceeaºi clasã ca ºi metoda a. Uneori, o subclasã rescrie o metodã dintr-o superclasã a sa. În acest caz, apelul metodei respective în interiorul subclasei duce automat la apelul metodei din subclasã. Dacã totuºi dorim sã apelãm metoda aºa cum a fost ea definitã în superclasã, putem prefixa apelul cu numele superclasei. De exemplu: class A { … void a() { … } } class B extends A { void a() { .. } void c() { a();// metoda a din clasa B A.a();// metoda a din clasa A … } … } Desigur, pentru a "vedea" o metodã ºi a o putea apela, este nevoie sã avem drepturile necesare.
6.4 Iniþializatori statici La încãrcarea unei clase sunt automat iniþializate toate variabilele statice declarate în interiorul clasei. În plus, sunt apelaþi toþi iniþializatorii statici ai clasei. Un iniþializator static are urmãtoarea sintaxã: static BlocDeInstrucþiuni Blocul de instrucþiuni este executat automat la încãrcarea clasei. De exemplu, putem defini un iniþializator static în felul urmãtor:
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (15 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
class A { static double a; static int b; static { a = Math.random(); // numãr dublu între 0.0 ºi 1.0 b = ( int )( a * 500 ); // numãr întreg între 0 ºi 500 } … } Declaraþiile de variabile statice ºi iniþializatorii statici sunt executate în ordinea în care apar în clasã. De exemplu, dacã avem urmãtoarea declaraþie de clasã: class A { static int i = 11; static { i += 100; i %= 55; } static int j = i + 1; } valoarea finalã a lui i va fi 1 ( ( 11 + 100 ) % 55 ) iar valoarea lui j va fi 2.
6.5 Constructori ºi finalizatori 6.5.1 constructori La crearea unei noi instanþe a unei clase sistemul alocã automat memoria necesarã instanþei ºi o iniþializeazã cu valorile iniþiale specificate sau implicite. Dacã dorim sã facem iniþializãri suplimentare în interiorul acestei memorii sau în altã parte putem descrie metode speciale numite constructori ai clasei. Putem avea mai mulþi constructori pentru aceeaºi clasã, aceºtia diferind doar prin parametrii pe care îi primesc. Numele tuturor constructorilor este acelaºi ºi este identic cu numele clasei. Declaraþia unui constructor este asemãnãtoare cu declaraþia unei metode oarecare, cu diferenþa cã nu putem specifica o valoare de retur ºi nu putem specifica nici un fel de modificatori. Dacã dorim sã returnãm dintr-un constructor, trebuie sã folosim instrucþiunea return fãrã nici o expresie. Putem însã sã specificãm gradul de protecþie al unui constructor ca fiind public, privat, protejat sau prietenos. Constructorii pot avea clauze throws.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (16 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
Dacã o clasã nu are constructori, compilatorul va crea automat un constructor implicit care nu ia nici un parametru ºi care iniþializeazã toate variabilele clasei ºi apeleazã constructorul superclasei fãrã argumente prin super(). Dacã superclasa nu are un constructor care ia zero argumente, se va genera o eroare de compilare. Dacã o clasã are cel puþin un constructor, constructorul implicit nu mai este creat de cãtre compilator. Când construim corpul unui constructor avem posibilitatea de a apela, pe prima linie a blocului de instrucþiuni care reprezintã corpul constructorului, un constructor explicit. Constructorul explicit poate avea douã forme: this( [Parametri] ); super( [Parametri] ); Cu aceastã sintaxã apelãm unul dintre constructorii superclasei sau unul dintre ceilalþi constructori din aceeaºi clasã. Aceste linii nu pot apãrea decât pe prima poziþie în corpul constructorului. Dacã nu apar acolo, compilatorul considerã implicit cã prima instrucþiune din corpul constructorului este: super(); ªi în acest caz se va genera o eroare de compilare dacã nu existã un constructor în superclasã care sã lucreze fãrã nici un parametru. Dupã apelul explicit al unui constructor din superclasã cu sintaxa super( … ) este executatã în mod implicit iniþializarea tuturor variabilelor de instanþã (non-statice) care au iniþializatori expliciþi. Dupã apelul unui constructor din aceeaºi clasã cu sintaxa this( … ) nu existã nici o altã acþiune implicitã, deci nu vor fi iniþializate nici un fel de variabile. Aceasta datoritã faptului cã iniþializarea s-a produs deja în constructorul apelat. Iatã ºi un exemplu: class A extends B { String valoare; A( String val ) { // aici existã apel implicit // al lui super(), adicã B() valoare = val; } A( int val ) { this( String.valueOf( val ) );// alt constructor } } ^
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (17 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
6.5.2 Finalizatori În Java nu este nevoie sã apelãm în mod explicit distrugerea unei instanþe atunci când nu mai este nevoie de ea. Sistemul oferã un mecanism de colectare a gunoaielor care recunoaºte situaþia în care o instanþã de obiect sau un tablou nu mai sunt referite de nimeni ºi le distruge în mod automat. Acest mecanism de colectare a gunoaielor ruleazã pe un fir de execuþie separat, de prioritate micã. Nu avem nici o posibilitate sã aflãm exact care este momentul în care va fi distrusã o instanþã. Totuºi, putem specifica o funcþie care sã fie apelatã automat în momentul în care colectorul de gunoaie încearcã sã distrugã obiectul. Aceastã funcþie are nume, numãr de parametri ºi tip de valoare de retur fixe: void
finalize()
Dupã apelul metodei de finalizare (numitã ºi finalizator), instanþa nu este încã distrusã pânã la o nouã verificare din partea colectorului de gunoaie. Aceastã comportare este necesarã pentru cã instanþa poate fi revitalizatã prin crearea unei referinþe cãtre ea în interiorul finalizatorului. Totuºi, finalizatorul nu este apelat decât o singurã datã. Dacã obiectul revitalizat redevine candidat la colectorul de gunoaie, acesta este distrus fãrã a i se mai apela finalizatorul. Cu alte cuvinte, un obiect nu poate fi revitalizat decât o singurã datã. Dacã în timpul finalizãrii apare o excepþie, ea este ignoratã ºi finalizatorul nu va mai fi apelat din nou.
6.5.3 Crearea instanþelor O instanþã este creatã folosind o expresie de alocare care foloseºte cuvântul rezervat new. Iatã care sunt paºii care sunt executaþi la apelul acestei expresii: ●
●
●
Se creeazã o nouã instanþã de tipul specificat. Toate variabilele instanþei sunt iniþializate pe valorile lor implicite. Se apeleazã constructorul corespunzãtor în funcþie de parametrii care sunt transmiºi în expresia de alocare. Dacã instanþa este creatã prin apelul metodei newInstance, se apeleazã constructorul care nu ia nici un argument. Dupã creare, expresia de alocare returneazã o referinþã cãtre instanþa nou creatã.
Exemple de creare: A o1 = new A(); B o2 = new B(); class C extends B { String valoare; C( String val ) {
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (18 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
// aici existã apel implicit // al lui super(), adicã B() valoare = val; } C( int val ) { this( String.valueOf( val ) ); } } C o3 = new C( "Vasile" ); C o4 = new C( 13 ); O altã cale de creare a unui obiect este apelul metodei newInstance declarate în clasa Class. Iatã paºii de creare în acest caz: ●
● ●
Se creeazã o nouã instanþã de acelaºi tip cu tipul clasei pentru care a fost apelatã metoda newInstance. Toate variabilele instanþei sunt iniþializate pe valorile lor implicite. Este apelat constructorul obiectului care nu ia nici un argument. Dupã creare referinþa cãtre obiectul nou creat este returnatã ca
valoare a metodei newInstance. Tipul acestei referinþe va fi Object în timpul compilãrii ºi tipul clasei reale în timpul execuþiei.
6.6 Derivarea claselor O clasã poate fi derivatã dintr-alta prin folosirea în declaraþia clasei derivate a clauzei extends. Clasa din care se derivã noua clasã se numeºte superclasã imediatã a clasei derivate. Toate clasele care sunt superclase ale superclasei imediate ale unei clase sunt superclase ºi pentru clasa datã. Clasa nou derivatã se numeºte subclasã a clasei din care este derivatã. Sintaxa generalã este: class SubClasã extends SuperClasã … O clasã poate fi derivatã dintr-o singurã altã clasã, cu alte cuvinte o clasã poate avea o singurã superclasã imediatã. Clasa derivatã moºteneºte toate variabilele ºi metodele superclasei sale. Totuºi, ea nu poate accesa decât acele variabile ºi metode care nu sunt declarate private. Putem rescrie o metodã a superclasei declarând o metodã în noua clasã având acelaºi nume ºi aceiaºi parametri. La fel, putem declara o variabilã care are acelaºi nume cu o variabilã din superclasã. În acest caz, noul nume ascunde vechea variabilã, substituindu-i-se. Putem în continuare sã ne referim la variabila ascunsã din superclasã specificând numele superclasei sau folosindu-ne de variabila super.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (19 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
Exemplu: class A { int a = 1; void unu() { System.out.println( a ); } } class B extends A { double a = Math.PI; void unu() { System.out.println( a ); } void doi() { System.out.println( A.a ); } void trei() { unu(); super.unu(); } } Dacã apelãm metoda unu din clasa A, aceasta va afiºa la consolã numãrul 1. Acest apel se va face cu instrucþiunea: ( new A() ).unu(); Dacã apelãm metoda unu din clasa B, aceasta va afiºa la consolã numãrul PI. Apelul îl putem face de exemplu cu instrucþiunea: B obiect = new B(); obiect.unu(); Observaþi cã în metoda unu din clasa B, variabila referitã este variabila a din clasa B. Variabila a din clasa A este ascunsã. Putem însã sã o referim prin sintaxa A.a ca în metoda doi din clasa B. În interiorul clasei B, apelul metodei unu fãrã nici o altã specificaþie duce automat la apelul metodei unu definite în interiorul clasei B. Metoda unu din clasa B rescrie metoda unu din clasa A. Vechea metodã este accesibilã pentru a o referi în mod explicit ca în metoda trei din clasa B. Apelul acestei metode va afiºa mai întâi numãrul PI ºi apoi numãrul 1. Dacã avem declaratã o variabilã de tip referinþã cãtre o instanþã a clasei A, aceastã variabilã poate sã conþinã în timpul execuþiei ºi o referinþã cãtre o instanþã a clasei B. Invers, afirmaþia nu este valabilã.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (20 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
În clipa în care apelãm metoda unu pentru o variabilã referinþã cãtre clasa A, sistemul va apela metoda unu a clasei A sau B în funcþie de adevãratul tip al referinþei din timpul execuþiei. Cu alte cuvinte, urmãtoarea secvenþã de instrucþiuni: A tablou[] = new A[2]; tablou[0] = new A(); tablou[1] = new B(); for( int i = 0; i < 2; i++ ) { tablou[i].unu(); } va afiºa douã numere diferite, mai întâi 1 ºi apoi PI. Aceasta din cauzã cã cel de-al doilea element din tablou este, în timpul execuþiei, de tip referinþã la o instanþã a clasei B chiar dacã la compilare este de tipul referinþã la o instanþã a clasei A. Acest mecanism se numeºte legare târzie, ºi înseamnã cã metoda care va fi efectiv apelatã este stabilitã doar în timpul execuþiei ºi nu la compilare. Dacã nu declarãm nici o superclasã în definiþia unei clase, atunci se considerã automat cã noua clasã derivã direct din clasa Object, moºtenind toate metodele ºi variabilele acesteia.
6.7 Interfeþe O interfaþã este în esenþã o declaraþie de tip ce constã dintr-un set de metode ºi constante pentru care nu s-a specificat nici o implementare. Programele Java folosesc interfeþele pentru a suplini lipsa moºtenirii multiple, adicã a claselor de obiecte care derivã din douã sau mai multe alte clase. Sintaxa de declaraþie a unei interfeþe este urmãtoarea: Modificatori interface NumeInterf [ extends [Interfaþã][, Interfaþã]*] Corp Modificatorii unei interfeþe pot fi doar cuvintele rezervate public ºi abstract. O interfaþã care este publicã poate fi accesatã ºi de cãtre alte pachete decât cel care o defineºte. În plus, fiecare interfaþã este în mod implicit abstractã. Modificatorul abstract este permis dar nu obligatoriu. Numele interfeþelor trebuie sã fie identificatori Java. Convenþiile de numire a interfeþelor le urmeazã în general pe cele de numire a claselor de obiecte. Interfeþele, la fel ca ºi clasele de obiecte, pot avea subinterfeþe. Subinterfeþele moºtenesc toate constantele ºi declaraþiile de metode ale interfeþei din care derivã ºi pot defini în plus noi elemente. Pentru a defini o subinterfaþã, folosim o clauzã extends. Aceste clauze specificã superinterfaþa unei interfeþe. O interfaþã file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (21 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
poate avea mai multe superinterfeþe care se declarã separate prin virgulã dupã cuvântul rezervat extends. Circularitatea definiþiei subinterfeþelor nu este permisã. În cazul interfeþelor nu existã o rãdãcinã comunã a arborelui de derivare aºa cum existã pentru arborele de clase, clasa Object. În corpul unei declaraþii de interfaþã pot sã aparã declaraþii de variabile ºi declaraþii de metode. Variabilele sunt implicit statice ºi finale. Din cauza faptului cã variabilele sunt finale, este obligatoriu sã fie specificatã o valoare iniþialã pentru aceste variabile. În plus, aceastã valoare iniþialã trebuie sã fie constantã (sã nu depindã de alte variabile). Dacã interfaþa este declaratã publicã, toate variabilele din corpul sãu sunt implicit declarate publice. În ceea ce priveºte metodele declarate în interiorul corpului unei interfeþe, acestea sunt implicit declarate abstracte. În plus, dacã interfaþa este declaratã publicã, metodele din interior sunt implicit declarate publice. Iatã un exemplu de declaraþii de interfeþe: public interface ObiectSpatial { final int CUB = 0; final int SFERA = 1; double greutate(); double volum(); double raza(); int tip(); } public interface ObiectSpatioTemporal extends ObiectSpatial { void centrulDeGreutate( long moment, double coordonate[] ); long momentInitial(); long momentFinal(); } Cele douã interfeþe definesc comportamentul unui obiect spaþial respectiv al unui obiect spaþio-temporal. Un obiect spaþial are o greutate, un volum ºi o razã a sferei minime în care se poate înscrie. În plus, putem defini tipul unui obiect folosindu-ne de o serie de valori constante predefinite precum ar fi SFERA sau CUB. Un obiect spaþio-temporal este un obiect spaþial care are în plus o poziþie pe axa timpului. Pentru un astfel de obiect, în afarã de proprietãþile deja descrise pentru obiectele spaþiale, trebuie sã avem în plus un moment iniþial, de apariþie, pe axa timpului ºi un moment final. Obiectul nostru nu existã în afara acestui interval de timp. În plus, pentru un astfel de obiect putem afla poziþia centrului sãu de greutate în fiecare moment aflat în intervalul de existenþã. Pentru a putea lucra cu obiecte spaþiale ºi spaþio-temporale este nevoie sã definim diverse clase care sã file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (22 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
implementeze aceste interfeþe. Acest lucru se face specificând clauza implements în declaraþia de clasã. O clasã poate implementa mai multe interfeþe. Dacã o clasã declarã cã implementeazã o anumitã interfaþã, ea este obligatoriu sã implementeze toate metodele declarate în interfaþa respectivã. De exemplu, putem spune cã o minge este un obiect spaþial de tip sferã. În plus, mingea are o poziþie în funcþie de timp ºi un interval de existenþã. Cu alte cuvinte, mingea este chiar un obiect spaþio-temporal. Desigur, în afarã de proprietãþile spaþio-temporale mingea mai are ºi alte proprietãþi precum culoarea, proprietarul sau preþul de cumpãrare. Iatã cum ar putea arãta definiþia clasei de obiecte de tip minge: import java.awt.Color; class Minge extends Jucarie implements ObiectSpatioTemporal int culoare = Color.red; double pret = 10000.0; double raza = 0.25; long nastere; long moarte; // metodele din ObiectSpatial double greutate() { return raza * 0.5; } double raza() { return raza; } double volum() { return ( 4.0 / 3.0 ) * Math.PI * raza * raza * raza; } int tip() { return SFERA; }
{
// metodele din interfaþa ObiectSpatioTemporal boolean centrulDeGreutate( long moment, double coordonate[] ) { if( moment < nastere || moment > moarte ) { return false; } … coordonate[0] = x; coordonate[1] = y; coordonate[2] = z; return true; } long momentInitial() { file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (23 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
return nastere; } long momentFinal() { return moarte; } int ceCuloare() { return culoare; } double cePret() { return pret; } } Observaþi cã noua clasã Minge implementeazã toate metodele definite în interfaþa ObiectSpatioTemporal ºi, pentru cã aceasta extinde interfaþa ObiectSpatial, ºi metodele definite în cea din urmã. În plus, clasa îºi defineºte propriile metode ºi variabile. Sã presupunem în continuare cã avem ºi o altã clasã, Rezervor, care este tot un obiect spaþio-temporal, dar de formã cubicã. Declaraþia acestei clase ar arãta ca: class Rezervor extends Constructii implements ObiectSpatioTemporal { … } Desigur, toate metodele din interfeþele de mai sus trebuiesc implementate, plus alte metode specifice. Sã mai observãm cã cele douã obiecte derivã din clase diferite: Mingea din Jucãrii iar Rezervorul din Construcþii. Dacã am putea deriva o clasã din douã alte clase, am putea deriva Minge din Jucarie ºi ObiectSpatioTemporal iar Rezervor din Constructie ºi ObiectSpaþioTemporal. Într-o astfel de situaþie, nu ar mai fi necesar ca ObiectSpaþioTemporal sã fie o interfaþã, ci ar fi suficient ca acesta sã fie o altã clasã. Din pãcate, în Java, o clasã nu poate deriva decât dintr-o singurã altã clasã, aºa cã este obligatoriu în astfel de situaþii sã folosim interfeþele. Dacã ObiectSpaþioTemporal ar fi putut fi o clasã, am fi avut avantajul cã puteam implementa acolo metodele cu funcþionare identicã din cele douã clase discutate, acestea fiind automat moºtenite fãrã a mai fi nevoie de definirea lor de douã ori în fiecare clasã în parte. Putem crea în continuare metode care sã lucreze cu obiecte spaþio-temporale, de exemplu o metodã care sã afle distanþa unui corp spaþio-temporal faþã de un punct dat la momentul sãu iniþial. O astfel de metodã se poate scrie o singurã datã, ºi poate lucra cu toate clasele care implementeazã interfaþa noastrã. De exemplu: … double distanta( double punct[], ObiectSpatioTemporal obiect ) { file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (24 of 25)12.01.2006 23:07:49
Capitolul VI -- Obiecte Java
double coordonate[] = new double[3]; obiect.centrulDeGreutate( obiect.momentInitial(), coordonate ); double x = coordonate[0] - punct[0]; double y = coordonate[1] - punct[1]; double z = coordonate[2] - punct[2]; return Math.sqrt( x * x + y * y + z * z ); } Putem apela metoda atât cu un obiect din clasa Minge cât ºi cu un obiect din clasa Rezervor. Compilatorul nu se va plânge pentru cã el ºtie cã ambele clase implementeazã interfaþa ObiectSpaþioTemporal, aºa cã metodele apelate în interiorul calculului distanþei (momentInitial ºi centruDeGreutate) sunt cu siguranþã implementate în ambele clase. Deci, putem scrie: Minge minge; Rezervor rezervor; double punct[] = { 10.0, 45.0, 23.0 }; distanþa( punct, minge ); distanþa( punct, rezervor ); Desigur, în mod normal ar fi trebuit sã proiectãm ºi un constructor sau mai mulþi care sã iniþializeze obiectele noastre cu valori rezonabile. Aceºti constructori ar fi stat cu siguranþã în definiþia claselor ºi nu în definiþia interfeþelor. Nu avem aici nici o cale de a forþa definirea unui anumit constructor cu ajutorul interfeþei. [cuprins] (C) IntegraSoft 1996-1998
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap6.html (25 of 25)12.01.2006 23:07:49
Capitolul VII -- Structura programelor
Capitolul VII Structura programelor ● ● ● ●
8.1 Pachete de clase 8.2 Importul claselor 8.3 Fiºiere sursã 8.4 Compilare ºi execuþie
8.1 Pachete de clase Clasele Java sunt organizate pe pachete. Aceste pachete pot avea nume ierarhice. Numele de pachete au forma urmãtoare: [NumePachet.]* NumeComponentãPachet Numele de pachete ºi de componente ale acestora sunt identificatori Java. De obicei, aceste nume urmeazã structura de directoare în care sunt memorate clasele compilate. Rãdãcina arborelui de directoare în care sunt memorate clasele este indicatã de o variabilã sistem CLASSPATH. În DOS aceasta se seteazã în felul urmãtor: set CLASSPATH=.;c:\java\lib În Unix se poate seta cu comanda: CLASSPATH=.:/usr/local/lib/java ; export CLASSPATH dacã lucraþi cu bash . Din aceastã rãdãcinã, fiecare pachet are propriul director. În director existã codul binar pentru componentele pachetului respectiv. Dacã pachetul conþine subpachete, atunci acestea sunt memorate într-un subdirector în interiorul directorului pachetului. Creatorii Java recomandã folosirea unei reguli unice de numire a pachetelor, astfel încât sã nu aparã conflicte. Convenþia recomandatã de ei este aceea de a folosi numele domeniului Internet aparþinând producãtorului claselor. Astfel, numele de pachete ar putea arãta ca în: COM.Microsoft.OLE COM.Apple.quicktime.v2
file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap8.html (1 of 4)12.01.2006 23:07:50
Capitolul VII -- Structura programelor
ºi aºa mai departe.
8.2 Importul claselor Desigur, este nevoie ca o clasã sã poatã folosi obiecte aparþinând unei alte clase. Pentru aceasta, definiþia clasei respective trebuie sã importe codul binar al celeilalte clase pentru a ºti care sunt variabilele ºi metodele clasei respective. Importul se face cu o instrucþiune specialã: import numeClasã ; unde numele clasei include ºi pachetul din care aceasta face parte. De exemplu: import java.awt.Graphics; import java.applet.Applet; Se poate importa ºi un pachet întreg, adicã toate clasele aparþinând acelui pachet, printr-o instrucþiune de forma: import numePachet.*; De exemplu: import java.awt.*;
8.3 Fiºiere sursã Codul sursã Java trebuie introdus cu un editor într-un fiºier text pe care îl vom numi în continuare fiºier sursã. Un fiºier sursã poate sã conþinã declaraþia mai multor clase ºi interfeþe, dar doar una dintre acestea poate fi declaratã publicã. Utilizarea celorlalte clase este limitatã la fiºierul respectiv. Mai mult, nu putem avea în acelaºi timp o interfaþã publicã ºi o clasã publicã declarate în acelaºi fiºier sursã. Dacã dorim sã înregistrãm codul clasei într-un anumit pachet, putem sã includem la începutul fiºierului sursã o declaraþie de forma: package numePachet; dacã aceastã declaraþie lipseºte, clasa va fi plasatã în pachetul implicit, care nu are nume.
file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap8.html (2 of 4)12.01.2006 23:07:50
Capitolul VII -- Structura programelor
Structura generalã a unui fiºier sursã este urmãtoarea: [ DeclaraþiePachet ][ InstrucþiuneImport ]*[ DeclaraþieDeTip ]* unde declaraþia de tip poate fi o declaraþie de clasã sau de interfaþã.
8.4 Compilare ºi execuþie Fiºierele sursã Java au obligatoriu extensia .java . Numele lor este identic cu numele clasei sau interfeþei publice declarate în interior. În urma compilãrii rezultã fiºiere cu nume identice cu numele claselor dar cu extensia .class indiferent dacã este vorba de o clasã sau o interfaþã. Fiºierul .class este generat în directorul local ºi nu direct la locaþia pachetului. Compilarea se face cu o comandã de forma: javac FiºierSursã .java Comanda aceasta, ca ºi celelalte descrise în acest paragraf este specificã mediului de dezvoltare Java pus la dispoziþie de Sun, numit JDK (Java Development Kit). În viitor este probabil sã aparã multe alte medii de dezvoltare care vor avea propriile lor compilatoare ºi interpretoare ºi, posibil, propriile linii de comandã. La compilare, variabila sistem CLASSPATH trebuie sã fie deja setatã pentru cã însuºi compilatorul Java actual este scris în Java. Pentru lansarea în execuþie a unei aplicaþii Java, trebuie sã introduceþi comanda: java NumeClasã unde numele clasei este numele aplicaþiei care conþine metoda main . Interpretorul va cãuta un fiºier cu numele NumeClasã.class ºi va încerca sã instanþieze clasa respectivã. Pentru lansarea unui aplet veþi avea nevoie de un document HTML care conþine tagul APPLET ºi ca parametru al acesteia name=NumeClasã.class La lansarea unui aplet, clasele care sunt apelate de clasa principalã sunt mai întâi cãutate pe sistemul pe care ruleazã navigatorul. Dacã nu sunt acolo, ele vor fi transferate în reþea. Asta înseamnã cã transferul de cod este relativ mic, trebuie transferat doar codul specific aplicaþiei.
file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap8.html (3 of 4)12.01.2006 23:07:50
Capitolul VII -- Structura programelor
[cuprins] (C) IntegraSoft 1996-1998
file:///C|/Documents%20and%20Settings/Luminita/Deskt...A%20in%20romana/Carte%20JAVA%20in%20romana/cap8.html (4 of 4)12.01.2006 23:07:50
Capitolul IX -- Fire de execuþie ºi sincronizare
Capitolul IX Fire de execuþie ºi sincronizare ● ● ● ● ● ● ● ●
9.1 Crearea firelor de execuþie 9.2 Stãrile unui fir de execuþie 9.3 Prioritatea firelor de execuþie 9.4 Grupuri de fire de execuþie 9.5 Enumerarea firelor de execuþie 9.6 Sincronizare 9.7 Un exemplu 9.8 Un exemplu Runnable
O aplicaþie Java ruleazã în interiorul unui proces al sistemului de operare. Acest proces constã din segmente de cod ºi segmente de date mapate într-un spaþiu virtual de adresare. Fiecare proces deþine un numãr de resurse alocate de cãtre sistemul de operare, cum ar fi fiºiere deschise, regiuni de memorie alocate dinamic, sau fire de execuþie. Toate aceste resurse deþinute de cãtre un proces sunt eliberate la terminarea procesului de cãtre sistemul de operare. Un fir de execuþie este unitatea de execuþie a unui proces. Fiecare fir de execuþie are asociate o secvenþã de instrucþiuni, un set de regiºitri CPU ºi o stivã. Atenþie, un proces nu executã nici un fel de instrucþiuni. El este de fapt un spaþiu de adresare comun pentru unul sau mai multe fire de execuþie. Execuþia instrucþiunilor cade în responsabilitatea firelor de execuþie. În cele ce urmeazã vom prescurta uneori denumirea firelor de execuþie, numindule pur ºi simplu fire . În cazul aplicaþiilor Java interpretate, procesul deþine în principal codul interpretorului iar codul binar Java este tratat ca o zonã de date de cãtre interpretor. Dar, chiar ºi în aceastã situaþie, o aplicaþie Java poate avea mai multe fire de execuþie, create de cãtre interpretor ºi care executã, seturi distincte de instrucþiuni binare Java. Fiecare dintre aceste fire de execuþie poate rula în paralel pe un procesor separat dacã maºina pe care ruleazã aplicaþia este o maºinã cu mai multe procesoare. Pe maºinile monoprocesor, senzaþia de execuþie în paralel a firelor de execuþie este creatã prin rotirea acestora pe rând la controlul unitãþii centrale, câte o cuantã de timp fiecare. Algoritmul de rotire al firelor de execuþie este de tip round-robin. Mediul de execuþie Java executã propriul sãu control asupra firelor de execuþie. Algoritmul pentru planificarea firelor de execuþie, prioritãþile ºi stãrile în care se pot afla acestea sunt specifice aplicaþiilor Java ºi implementate identic pe toate platformele pe care a fost portat mediul de execuþie Java. Totuºi, acest mediu ºtie sã profite de resursele sistemului pe care lucreazã. Dacã sistemul gazdã lucreazã cu mai multe procesoare, Java va folosi toate aceste procesoare pentru a-ºi planifica firele de execuþie. Dacã sistemul oferã multitasking preemptiv, multitaskingul Java va fi de asemenea preemptiv, etc. În cazul maºinilor multiprocesor, mediul de execuþie Java ºi sistemul de operare sunt responsabile cu repartizarea firelor de execuþie pe un procesor sau altul. Pentru programator, acest mecanism este complet transparent, neexistând nici o diferenþã între scrierea unei aplicaþii cu mai multe fire pentru o maºinã cu un singur procesor sau cu mai multe. Desigur, existã însã diferenþe în cazul scrierii aplicaþiilor pe mai multe fire de execuþie faþã de acelea cu un singur fir de execuþie, diferenþe care provin în principal din cauza necesitãþii de sincronizare între firele de execuþie aparþinând aceluiaºi proces.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap9.html (1 of 14)12.01.2006 23:07:51
Capitolul IX -- Fire de execuþie ºi sincronizare
Sincronizarea firelor de execuþie înseamnã cã acestea se aºteaptã unul pe celãlalt pentru completarea anumitor operaþii care nu se pot executa în paralel sau care trebuie executate într-o anumitã ordine. Java oferã ºi în acest caz mecanismele sale proprii de sincronizare, extrem de uºor de utilizat ºi înglobate în chiar sintaxa de bazã a limbajului. La lansarea în execuþie a unei aplicaþii Java este creat automat ºi un prim fir de execuþie, numit firul principal. Acesta poate ulterior sã creeze alte fire de execuþie care la rândul lor pot crea alte fire, ºi aºa mai departe. Firele de execuþie dintr-o aplicaþie Java pot fi grupate în grupuri pentru a fi manipulate în comun. În afarã de firele normale de execuþie, Java oferã ºi fire de execuþie cu prioritate micã care lucreazã în fundalul aplicaþiei atunci când nici un alt fir de execuþie nu poate fi rulat. Aceste fire de fundal se numesc demoni ºi executã operaþii costisitoare în timp ºi independente de celelalte fire de execuþie. De exemplu, în Java colectorul de gunoaie lucreazã pe un fir de execuþie separat, cu proprietãþi de demon. În acelaºi fel poate fi gândit un fir de execuþie care executã operaþii de încãrcare a unor imagini din reþea. O aplicaþie Java se terminã atunci când se terminã toate firele de execuþie din interiorul ei sau când nu mai existã decât fire demon. Terminarea firului principal de execuþie nu duce la terminarea automatã a aplicaþiei.
9.1 Crearea firelor de execuþie Existã douã cãi de definire de noi fire de execuþie: derivarea din clasa Thread a noi clase ºi implementarea într-o clasã a interfeþei Runnable . În primul caz, noua clasã moºteneºte toate metodele ºi variabilele clasei Thread care implementeazã în mod standard, în Java, funcþionalitatea de lucru cu fire de execuþie. Singurul lucru pe care trebuie sã-l facã noua clasã este sã reimplementeze metoda run care este apelatã automat de cãtre mediul de execuþie la lansarea unui nou fir. În plus, noua clasã ar putea avea nevoie sã implementeze un constructor care sã permitã atribuirea unei denumiri firului de execuþie. Dacã firul are un nume, acesta poate fi obþinut cu metoda getName care returneazã un obiect de tip String . Iatã un exemplu de definire a unui nou tip de fir de execuþie: class FirNou extends Thread { public FirNou( String nume ) { // apeleazã constructorul din Thread super( nume ); } public void run() { while( true ) { // fãrã sfârºit System.out.println( getName() + " Tastati ^C" ); } } } Dacã vom crea un nou obiect de tip FirNou ºi îl lansãm în execuþie acesta va afiºa la infinit mesajul "Tastaþi ^C". Întreruperea execuþiei se poate face într-adevãr prin tastarea caracterului ^C, caz în care întreaga aplicaþie este terminatã. Atâta timp însã cât noul obiect nu va fi întrerupt din exterior, aplicaþia va continua sã se execute pentru cã file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap9.html (2 of 14)12.01.2006 23:07:51
Capitolul IX -- Fire de execuþie ºi sincronizare
mai existã încã fire de execuþie active ºi indiferent de faptul cã firul de execuþie principal s-a terminat sau nu. Iatã ºi un exemplu de aplicaþie care foloseºte aceastã clasã: public TestFirNou { public static void main( String[] ) { new FirNou( "Primul" ).start(); } } Metoda start , predefinitã în obiectul Thread lanseazã execuþia propriu-zisã a firului. Desigur existã ºi cãi de a opri execuþia la nesfârºit a firului creat fie prin apelul metodei stop , prezentatã mai jos, fie prin rescrierea funcþiei run în aºa fel încât execuþia sa sã se termine dupã un interval finit de timp. A doua cale de definiþie a unui fir de execuþie este implementarea interfeþei Runnable într-o anumitã clasã de obiecte. Aceastã cale este cea care trebuie aleasã atunci când clasa pe care o creãm nu se poate deriva din clasa Thread pentru cã este important sã fie derivatã din altã clasã. Desigur, moºtenirea multiplã ar rezolva aceastã problemã, dar Java nu are moºtenire multiplã. Aceastã nouã cale se poate folosi în modul urmãtor: class Oclasa { … } class FirNou extends Oclasa implements Runnable { public void run() { for( int i = 0; i < 100; i++ ) { System.out.println( "pasul " + i ); } } … } public class TestFirNou { public static void main( String argumente[] ) { new Thread( new FirNou() ).start(); // Obiectele sunt create ºi folosite imediat // La terminarea instrucþiunii, ele sunt automat // eliberate nefiind referite de nimic } } Dupã cum observaþi, clasa Thread are ºi un constructor care primeºte ca argument o instanþã a unei clase care implementeazã interfaþa Runnable . În acest caz, la lansarea în execuþie a noului fir, cu metoda start , se apeleazã metoda run din acest obiect ºi nu din instanþa a clasei Thread . Atunci când dorim sã creãm un aplet care sã ruleze pe un fir de execuþie separat faþã de pagina de navigator în care ruleazã pentru a putea executa operaþii în fereastra apletului ºi în acelaºi timp sã putem folosi în continuare navigatorul, suntem obligaþi sã alegem cea de-a doua cale de implementare. Aceasta pentru cã apletul nostru trebuie sã fie derivat din clasa standard Applet . Singura alternativã care ne rãmâne este aceea de a implementa în aplet interfaþa file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap9.html (3 of 14)12.01.2006 23:07:51
Capitolul IX -- Fire de execuþie ºi sincronizare
Runnable .
9.2 Stãrile unui fir de execuþie Un fir de execuþie se poate afla în Java în mai multe stãri, în funcþie de ce se întâmplã cu el la un moment dat. Atunci când este creat, dar înainte de apelul metodei start, firul se gãseºte într-o stare pe care o vom numi Fir Nou Creat . În aceastã stare, singurele metode care se pot apela pentru firul de execuþie sunt metodele start ºi stop . Metoda start lanseazã firul în execuþie prin apelul metodei run . Metoda stop omoarã firul de execuþie încã înainte de a fi lansat. Orice altã metodã apelatã în aceastã stare provoacã terminarea firului de execuþie prin generarea unei excepþii de tip IllegalThreadStateException . Dacã apelãm metoda start pentru un Fir Nou Creat firul de execuþie va trece în starea Ruleazã . În aceastã stare, instrucþiunile din corpul metodei run se executã una dupã alta. Execuþia poate fi opritã temporar prin apelul metodei sleep care primeºte ca argument un numãr de milisecunde care reprezintã intervalul de timp în care firul trebuie sã fie oprit. Dupã trecerea intervalului, firul de execuþie va porni din nou. În timpul în care se scurge intervalul specificat de sleep , obiectul nu poate fi repornit prin metode obiºnuite. Singura cale de a ieºi din aceastã stare este aceea de a apela metoda interrupt . Aceastã metodã aruncã o excepþie de tip InterruptedException care nu este prinsã de sleep dar care trebuie prinsã obligatoriu de metoda care a apelat metoda sleep . De aceea, modul standard în care se apeleazã metoda sleep este urmãtorul: … try { sleep( 1000 ); // o secundã } catch( InterruptedException ) { … } … Dacã dorim oprirea firului de execuþie pe timp nedefinit, putem apela metoda suspend . Aceasta trece firul de execuþie într-o nouã stare, numitã Nu Ruleazã . Aceeaºi stare este folositã ºi pentru oprirea temporarã cu sleep . În cazul apelului suspend însã, execuþia nu va putea fi reluatã decât printr-un apel al metodei resume . Dupã acest apel, firul va intra din nou în starea Ruleazã . Pe timpul în care firul de execuþie se gãseºte în starea Nu Ruleazã , acesta nu este planificat niciodatã la controlul unitãþii centrale, aceasta fiind cedatã celorlalte fire de execuþie din aplicaþie. Firul de execuþie poate intra în starea Nu Ruleazã ºi din alte motive. De exemplu se poate întâmpla ca firul sã aºtepte pentru terminarea unei operaþii de intrare/ieºire de lungã duratã caz în care firul va intra din nou în starea Ruleazã doar dupã terminarea operaþiei. O altã cale de a ajunge în starea Nu Ruleazã este aceea de a apela o metodã sau o secvenþã de instrucþiuni sincronizatã dupã un obiect. În acest caz, dacã obiectul este deja blocat, firul de execuþie va fi oprit pânã în clipa în care obiectul cu pricina apeleazã metoda notify sau notifyAll . În fine, atunci când metoda run ºi-a terminat execuþia, obiectul intrã în starea Mort . Aceastã stare este pãstratã pânã în clipa în care obiectul file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap9.html (4 of 14)12.01.2006 23:07:51
Capitolul IX -- Fire de execuþie ºi sincronizare
este eliminat din memorie de mecanismul de colectare a gunoaielor. O altã posibilitate de a intra în starea Mort este aceea de a apela metoda stop . Atunci când se apeleazã metoda stop , aceasta aruncã cu o instrucþiune throw o eroare numitã ThreadDeath . Aceasta poate fi prinsã de cãtre cod pentru a efectua curãþenia necesarã. Codul necesar este urmãtorul: … try { firDeExecutie.start(); … } catch( ThreadDeath td ) { … // curãþenie throw td; // se aruncã obiectul mai departe // pentru a servi la distrugerea // firului de execuþie } Desigur, firul de execuþie poate fi terminat ºi pe alte cãi, caz în care metoda stop nu este apelatã ºi eroarea ThreadDeath nu este aruncatã. În aceste situaþii este preferabil sã ne folosim de o clauzã finally ca în: … try { firDeExecutie.start(); … } finally { ..// curãþenie } În fine, dacã nu se mai poate face nimic pentru cã firul de execuþie nu mai rãspunde la comenzi, puteþi apela la calea disperatã a metodei destroy . Din pãcate, metoda destroy terminã firul de execuþie fãrã a proceda la curãþirile necesare în memorie. Atunci când un fir de execuþie este oprit cu comanda stop , mai este nevoie de un timp pânã când sistemul efectueazã toate operaþiile necesare opririi. Din aceastã cauzã, este preferabil sã aºteptãm în mod explicit terminarea firului prin apelul metodei join : firDeExecutie.stop() try { firDeExecutie.join(); } catch( InterruptedException e ) { … } Excepþia de întrerupere trebuie prinsã obligatoriu. Dacã nu apelãm metoda join pentru a aºtepta terminarea ºi metoda stop este de exemplu apelatã pe ultima linie a funcþiei main , existã ºansa ca sistemul sã creadã cã firul auxiliar de execuþie este încã în viaþã ºi aplicaþia Java sã nu se mai termine rãmânând într-o stare de aºteptare. O puteþi desigur termina tastând ^C.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap9.html (5 of 14)12.01.2006 23:07:51
Capitolul IX -- Fire de execuþie ºi sincronizare
9.3 Prioritatea firelor de execuþie Fiecare fir de execuþie are o prioritate cuprinsã între valorile MIN_PRIORITY ºi MAX_PRIORITY. Aceste douã variabile finale sunt declarate în clasa Thread . În mod normal însã, un fir de execuþie are prioritatea NORM_PRIORITY, de asemenea definitã în clasa Thread . Mediul de execuþie Java planificã firele de execuþie la controlul unitãþii centrale în funcþie de prioritatea lor. Dacã existã mai multe fire cu prioritate maximã, acestea sunt planificate dupã un algoritm round-robin. Firele de prioritate mai micã intrã în calcul doar atunci când toate firele de prioritate mare sunt în starea Nu Ruleazã . Prioritatea unui fir de execuþie se poate interoga cu metoda getPriority care întoarce un numãr întreg care reprezintã prioritatea curentã a firului de execuþie. Pentru a seta prioritatea, se foloseºte metoda setPriority care primeºte ca parametru un numãr întreg care reprezintã prioritatea doritã. Schimbarea prioritãþii unui fir de execuþie este o treabã periculoasã dacã metoda cu prioritate mare nu se terminã foarte repede sau dacã nu are opriri dese. În caz contrar, celelalte metode nu vor mai putea primi controlul unitãþii centrale. Existã însã situaþii în care putem schimba aceastã prioritate fãrã pericol, de exemplu când avem un fir de execuþie care nu face altceva decât sã citeascã caractere de la utilizator ºi sã le memoreze într-o zonã temporarã. În acest caz, firul de execuþie este în cea mai mare parte a timpului în starea Nu Ruleazã din cauzã cã aºteaptã terminarea unei operaþii de intrare/ieºire. În clipa în care utilizatorul tasteazã un caracter, firul va ieºi din starea de aºteptare ºi va fi primul planificat la execuþie din cauza prioritãþii sale ridicate. În acest fel utilizatorul are senzaþia cã aplicaþia rãspunde foarte repede la comenzile sale. În alte situaþii, avem de executat o sarcinã cu prioritate micã. În aceste cazuri, putem seta pentru firul de execuþie care executã aceste sarcini o prioritate redusã. Alternativ, putem defini firul respectiv de execuþie ca un demon. Dezavantajul în aceastã situaþie este faptul cã aplicaþia va fi terminatã atunci când existã doar demoni în lucru ºi existã posibilitatea pierderii de date. Pentru a declara un fir de execuþie ca demon, putem apela metoda setDaemon. Aceastã metodã primeºte ca parametru o valoare booleanã care dacã este true firul este fãcut demon ºi dacã nu este adus înapoi la starea normalã. Putem testa faptul cã un fir de execuþie este demon sau nu cu metoda isDemon .
9.4 Grupuri de fire de execuþie Uneori avem nevoie sã acþionãm asupra mai multor fire de execuþie deodatã, pentru a le suspenda, reporni sau modifica prioritatea în bloc. Din acest motiv, este util sã putem grupa firele de execuþie pe grupuri. Aceastã funcþionalitate este oferitã în Java de cãtre o clasã numitã ThreadGroup . La pornirea unei aplicaþii Java, se creeazã automat un prim grup de fire de execuþie, numit grupul principal, main . Firul principal de execuþie face parte din acest grup. În continuare, ori de câte ori creãm un nou fir de execuþie, acesta va face parte din acelaºi grup de fire de execuþie ca ºi firul de execuþie din interiorul cãruia a fost creat, în afarã de cazurile în care în constructorul firului specificãm explicit altceva.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap9.html (6 of 14)12.01.2006 23:07:51
Capitolul IX -- Fire de execuþie ºi sincronizare
Într-un grup de fire de execuþie putem defini nu numai fire dar ºi alte grupuri de execuþie. Se creeazã astfel o arborescenþã a cãrei rãdãcinã este grupul principal de fire de execuþie. Pentru a specifica pentru un fir un nou grup de fire de execuþie, putem apela constructorii obiºnuiþi dar introducând un prim parametru suplimentar de tip ThreadGroup . De exemplu, putem folosi urmãtorul cod: ThreadGroup tg = new ThreadGroup( "Noul grup" ); Thread t = new Thread( tg, "Firul de executie" ); Acest nou fir de execuþie va face parte dintr-un alt grup de fire decât firul principal. Putem afla grupul de fire de execuþie din care face parte un anumit fir apelând metoda getThreadGroup , ca în secvenþa: Thread t = new Thread( "Firul de Executie" ); ThreadGroup tg = t.getThreadGroup(); Operaþiile definite pentru un grup de fire de execuþie sunt clasificabile în operaþii care acþioneazã la nivelul grupului, cum ar fi aflarea numelui, setarea unei prioritãþi maxime, etc., ºi operaþii care acþioneazã asupra fiecãrui fir de execuþie din grup, cum ar fi stop , suspend sau resume . Unele dintre aceste operaþii necesitã aprobarea controloarelor de securitate acest lucru fãcându-se printr-o metodã numitã checkAccess . De exemplu, nu puteþi seta prioritatea unui fir de execuþie decât dacã aveþi drepturile de acces necesare.
9.5 Enumerarea firelor de execuþie Pentru a enumera firele de execuþie active la un moment dat, putem folosi metoda enumerate definitã în clasa Thread precum ºi în clasa ThreadGroup . Aceastã metodã primeºte ca parametru o referinþã cãtre un tablou de referinþe la obiecte de tip Thread pe care îl umple cu referinþe cãtre fiecare fir activ în grupul specificat. Pentru a afla câte fire active sunt în grupul respectiv la un moment dat, putem apela metoda activeCount din clasa ThreadGroup . De exemplu: public listeazaFire { ThreadGroup grup = Thread.currentThread().getThreadGroup(); int numarFire = grup.activeCount(); Thread fire[] = new Thread[numarFire]; grup.enumerate( fire ); for( int i = 0; i < numar; i++ ) { System.out.println( fire[i].toString() ); } } Metoda enumerate întoarce numãrul de fire memorate în tablou, care este identic cu numãrul de fire active.
9.6 Sincronizare În unele situaþii se poate întâmpla ca mai multe fire de execuþie sã vrea sã acceseze aceeaºi variabilã. În astfel de situaþii, se pot produce încurcãturi dacã în timpul unuia dintre accese un alt fir de execuþie modificã valoarea variabilei.
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap9.html (7 of 14)12.01.2006 23:07:51
Capitolul IX -- Fire de execuþie ºi sincronizare
Limbajul Java oferã în mod nativ suport pentru protejarea acestor variabile. Suportul este construit de fapt cu granulaþie mai mare decât o singurã variabilã, protecþia fãcându-se la nivelul obiectelor. Putem defini metode, în cadrul claselor, care sunt sincronizate. Pe o instanþã de clasã, la un moment dat, poate lucra o singurã metodã sincronizatã. Dacã un alt fir de execuþie încearcã sã apeleze aceeaºi metodã pe aceeaºi instanþã sau o altã metodã a clasei de asemenea declaratã sincronizatã, acest al doilea apel va trebui sã aºtepte înainte de execuþie eliberarea instanþei de cãtre cealaltã metodã. În afarã de sincronizarea metodelor, se pot sincroniza ºi doar blocuri de instrucþiuni. Aceste sincronizãri se fac tot în legãturã cu o anumitã instanþã a unei clase. Aceste blocuri de instrucþiuni sincronizate se pot executa doar când instanþa este liberã. Se poate întâmpla ca cele douã tipuri de sincronizãri sã se amestece, în sensul cã obiectul poate fi blocat de un bloc de instrucþiuni ºi toate metodele sincronizate sã aºtepte, sau invers. Declararea unui bloc de instrucþiuni sincronizate se face prin: synchronize ( Instanþã ) { Instrucþiuni } iar declararea unei metode sincronizate se face prin folosirea modificatorului synchronize la implementarea metodei.
9.7 Un exemplu Exemplul urmãtor implementeazã soluþia urmãtoarei probleme: Într-o þarã foarte îndepãrtatã trãiau trei înþelepþi filozofi. Aceºti trei înþelepþi îºi pierdeau o mare parte din energie certându-se între ei pentru a afla care este cel mai înþelept. Pentru a tranºa problema o datã pentru totdeauna, cei trei înþelepþi au pornit la drum cãtre un al patrulea înþelept pe care cu toþii îl recunoºteau cã ar fi mai bun decât ei. Când au ajuns la acesta, cei trei i-au cerut sã le spunã care dintre ei este cel mai înþelept. Acesta, a scos cinci pãlãrii, trei negre ºi douã albe, ºi li le-a arãtat explicându-le cã îi va lega la ochi ºi le va pune în cap câte o pãlãrie, cele douã rãmase ascunzându-le. Dupã aceea, le va dezlega ochii, ºi fiecare dintre ei va vedea culoarea pãlãriei celorlalþi dar nu ºio va putea vedea pe a sa. Cel care îºi va da primul seama ce culoare are propria pãlãrie, acela va fi cel mai înþelept. Dupã explicaþie, înþeleptul i-a legat la ochi, le-a pus la fiecare câte o pãlãrie neagrã ºi le-a ascuns pe celelalte douã. Problema este aceea de a descoperi care a fost raþionamentul celui care a ghicit primul cã pãlãria lui este neagrã. Programul urmãtor rezolvã problema datã în felul urmãtor: Fiecare înþelept priveºte pãlãriile celorlalþi doi. Dacã ambele sunt albe, problema este rezolvatã, a lui nu poate fi decât neagrã. Dacã vede o pãlãrie albã ºi una neagrã, atunci el va trebui sã aºtepte puþin sã vadã ce spune cel cu pãlãria neagrã. Dacã acesta nu gãseºte soluþia, înseamnã cã el nu vede douã pãlãrii albe, altfel ar fi gãsit imediat rãspunsul. Dupã un scurt timp de aºteptare, înþeleptul poate sã fie sigur cã pãlãria lui este neagrã. În fine, dacã ambele pãlãrii pe care le vede sunt negre, va trebui sã aºtepte un timp ceva mai lung pentru a vedea dacã unul dintre concurenþii sãi nu ghiceºte pãlãria. Dacã dupã scurgerea timpului nici unul nu spune nimic, înseamnã cã
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap9.html (8 of 14)12.01.2006 23:07:51
Capitolul IX -- Fire de execuþie ºi sincronizare
nici unul nu vede o pãlãrie albã ºi una neagrã. Înseamnã cã propria pãlãrie este neagrã. Desigur, raþionamentul pleacã de la ideea cã ne putem baza pe faptul cã toþi înþelepþii gândesc ºi pot rezolva probleme uºoare. Cel care câºtigã a gândit doar un pic mai repede. Putem simula viteza de gândiri cu un interval aleator de aºteptare pânã la luarea deciziilor. În realitate, intervalul nu este aleator ci dictat de viteza de gândire a fiecãrui înþelept. Cei trei înþelepþi sunt implementaþi identic sub formã de fire de execuþie. Nu câºtigã la fiecare rulare acelaºi din cauza caracterului aleator al implementãrii. Înþeleptul cel mare este firul de execuþie principal care controleazã activitatea celorlalte fire ºi le serveºte cu date, culoarea pãlãriilor, doar în mãsura în care aceste date trebuie sã fie accesibile. Adicã nu se poate cere propria culoare de pãlãrie. Culoarea iniþialã a pãlãriilor se poate rescrie din linia de comandã. import java.awt.Color; // clasa Filozof implementeazã comportamentul // unui concurent class Filozof extends Thread { // pãrerea concurentului despre culoarea // pãlãriei sale. Null dacã încã nu ºi-a // format o pãrere. Color parere = null; Filozof( String nume ) { super( nume ); } public void run() { // concurentii firului curent Filozof concurenti[] = new Filozof[2]; // temporar Thread fire[] = new Thread[10]; int numarFire = enumerate( fire ); for( int i = 0, j = 0; i < numarFire && j < 2; i++ ) { if( fire[i] instanceof Filozof && fire[i] != this ) { concurenti[j++] = (Filozof)fire[i]; } } while( true ) { Color primaCuloare = Concurs.culoare( this, concurenti[0] ); Color adouaCuloare = Concurs.culoare( this, concurenti[1] ); if( primaCuloare == Color.white && adouaCuloare == Color.white ) { synchronized( this ) { parere = Color.black; } } else if( primaCuloare == Color.white ){ try{ sleep( 500 );
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap9.html (9 of 14)12.01.2006 23:07:51
Capitolul IX -- Fire de execuþie ºi sincronizare
} catch( InterruptedException e ){ }; if( Concurs.culoare( this, concurenti[1]) != concurenti[1].aGhicit()) { synchronized( this ) { parere = Color.black; }; } } else if( adouaCuloare == Color.white ) { try{ sleep( (int)( Math.random()*500)); } catch( InterruptedException e ) { }; if( Concurs.culoare(this, concurenti[0] ) != concurenti[0].aGhicit()) { synchronized( this ) { parere = Color.black; }; } } else { try { sleep( (int)( Math.random()*500)+500 ); } catch( InterruptedException e ) { }; if( Concurs.culoare(this, concurenti[0]) != concurenti[0].aGhicit() && Concurs.culoare( this, concurenti[1] ) != concurenti[1].aGhicit() ) { synchronized( this ) { parere = Color.black; }; } } } } public synchronized Color aGhicit() { return parere; } } public class Concurs { private static Color palarii[] = { Color.black, Color.black, Color.black }; private static Filozof filozofi[] = new Filozof[3]; public static void main( String args[] ) { for( int i = 0; i < args.length && i < 3; i++ ) { if( args[i].equalsIgnoreCase( "alb" ) ) { palarii[i] = Color.white; } else if(args[i].equalsIgnoreCase("negru")) { palarii[i] = Color.black; } } file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap9.html (10 of 14)12.01.2006 23:07:51
Capitolul IX -- Fire de execuþie ºi sincronizare
for( int i = 0; i < 3; i++ ) { filozofi[i] = new Filozof( "Filozoful " + ( i + 1 ) ); } for( int i = 0; i < 3; i++ ) { filozofi[i].start(); } System.out.println( "Concurenti:" ); for( int i = 0; i < 3; i++ ) { System.out.println( "\t" + filozofi[i].getName() + " " + (( palarii[i] == Color.white ) ? "alb":"negru" ) ); } gata: while( true ) { for( int i = 0; i < 3; i++ ) { if( filozofi[i].aGhicit()==palarii[i] ) { System.out.println( filozofi[i].getName() + " a ghicit." ); break gata; } } } for( int i = 0; i < 3; i++ ) { filozofi[i].stop(); try { filozofi[i].join(); } catch( InterruptedException e ) {}; } } public static Color culoare( Filozof filozof, Filozof concurent ) { if( filozof != concurent ) { for( int i = 0; i < 3; i++ ) { if( filozofi[i] == concurent ) { return palarii[i]; } } } return null; } } ^
9.8 Un exemplu Runnable Exemplul urmãtor implementeazã problema celor 8 dame, ºi anume: gãseºte toate posibilitãþile de a aºeza pe o tablã de ºah 8 regine în aºa fel încât acestea sã nu se batã între ele. Reginele se bat pe linie, coloanã sau în diagonalã. file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap9.html (11 of 14)12.01.2006 23:07:51
Capitolul IX -- Fire de execuþie ºi sincronizare
Soluþia de faþã extinde problema la o tablã de NxN cãsuþe ºi la N regine. Parametrul N este citit din tagul HTML asociat apletului. import java.awt.*; import java.applet.Applet; public class QueensRunner extends Applet implements Runnable { int n; int regine[]; int linie; Image queenImage; Thread myThread; public void start() { if( myThread == null ) { myThread = new Thread( this, "Queens" ); myThread.start(); } } public void stop() { myThread.stop(); myThread = null; } public void run() { while( myThread != null ) { nextSolution(); repaint(); try { myThread.sleep( 1000 ); } catch ( InterruptedException e ){ } } } boolean isGood() { for( int i = 0; i < linie; i++ ) { if( regine[linie] == regine[i] || Math.abs( regine[i] regine[linie] ) == Math.abs( i - linie ) ) { return false; } } return true; }
void nextSolution() { while( true ) { if( linie < 0 ) { linie = 0; } file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap9.html (12 of 14)12.01.2006 23:07:51
Capitolul IX -- Fire de execuþie ºi sincronizare
regine[linie]++; if( regine[linie] > n ) { regine[linie] = 0; linie--; } else { if( isGood() ) { linie++; if( linie >= n ) { break; } } } } }
public void init() { String param = getParameter( "Dimension" ); if( param == null ) { n = 4; } else { try { n = Integer.parseInt( param ); } catch( NumberFormatException e ) { n = 4; } if( n < 4 ) { n = 4; } } regine = new int[n + 1]; for( int i = 0; i < n; i++ ) { regine[i] = 0; } linie = 0; queenImage = getImage(getCodeBase(), "queen.gif" } public void paint( Graphics g ) { Dimension d = size(); g.setColor( Color.red ); int xoff = d.width / n; int yoff = d.height / n; for( int i = 1; i < n; i++ ) { g.drawLine( xoff * i, 0, xoff * i, d.height ); g.drawLine( 0, yoff * i, d.width, yoff * i ); } for( int i = 0; i < n; i++ ) { for( int j = 0; j < n; j++ ) { if( regine[i] - 1 == j ) { g.drawImage(queenImage,
);
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap9.html (13 of 14)12.01.2006 23:07:51
Capitolul IX -- Fire de execuþie ºi sincronizare
i*xoff + 1, j*yoff + 1, this); } } } } public String getAppletInfo() { return "Queens by E. Rotariu"; } } ^
[cuprins] (C) IntegraSoft 1996-1998
file:///C|/Documents%20and%20Settings/Luminita/Desk...%20in%20romana/Carte%20JAVA%20in%20romana/cap9.html (14 of 14)12.01.2006 23:07:51
Limbajul JAVA -- IntegraSoft
Bibliografie 1. Dumitru Rãdoiu, HTML - Publicaþii Web, Computer Press Agora SRL 2. James Gostling, Henry McGilton, The Java Language Environment, A white paper, Sun Microsystems, Inc. 3. David Flanagan, Java in a Nutshell: A Desktop Quick Reference for Java Programmers, O'Reilly & Associates, Inc. 4. Ed Anuff, The Java Sourcebook , Wiley Computer Publishing 5. Mike Daconta, Java for C/C++ Programmers, Wiley Computer Publishing 6. Arthur van Hoff, Sami Shaio, Orca Starbuck, Hooked on Java, Addison-Wesley Publishing Company 7. Laura Lemay, Charles L. Perkins, Teach your self Java in 21 days, Sams.net Publishing 8. *** - The Java Language Specification, Sun Microsystems, Inc.
[cuprins] (C) IntegraSoft 1996-1998
file:///C|/Documents%20and%20Settings/Luminita/Desktop/bu...0in%20romana/Carte%20JAVA%20in%20romana/bibliografie.html12.01.2006 23:07:51
Capitolul V -- 1. Variabile
Capitolul V 5.1 Variabile 5.1.1 Declaraþii de variabile 5.1.1.1 Tipul unei variabile 5.1.1.2 Numele variabilelor 5.1.1.3 Iniþializarea variabilelor 5.1.2 Tipuri primitive 5.1.2.1 Tipul boolean 5.1.2.2 Tipul caracter 5.1.2.3 Tipuri întregi 5.1.2.3.1 Tipul octet 5.1.2.3.2 Tipul întreg scurt 5.1.2.3.3 Tipul întreg 5.1.2.3.4 Tipul întreg lung 5.1.2.4 Tipuri flotante 5.1.2.4.1 Tipul flotant 5.1.2.4.2 Tipul flotant dublu 5.1.2.4.3 Reali speciali definiþi de IEEE 5.1.3 Tipuri referinþã 5.1.3.1 Tipul referinþã cãtre o clasã 5.1.3.2 Tipul referinþã cãtre o interfaþã 5.1.3.3 Tipul referinþã cãtre un tablou 5.1.4 Clasa de memorare 5.1.4.1 Variabile locale 5.1.4.2 Variabile statice 5.1.4.3 Variabile dinamice
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (1 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
5.1.5 Tablouri de variabile 5.1.5.1 Declaraþia variabilelor de tip tablou 5.1.5.2 Iniþializarea tablourilor. 5.1.5.3 Lungimea tablourilor 5.1.5.4 Referirea elementelor din tablou 5.1.5.5 Alocarea ºi eliberarea tablourilor 5.1.6 Conversii 5.1.6.1 Conversii de extindere a valorii 5.1.6.2 Conversii de trunchiere a valorii 5.1.6.3 Conversii pe tipuri referinþã 5.1.6.4 Conversii la operaþia de atribuire 5.1.6.5 Conversii explicite 5.1.6.6 Conversii de promovare aritmeticã
5.1.1 Declaraþii de variabile O variabilã în limbajul Java este o locaþie de memorie care poate pãstra o valoare de un anumit tip. În ciuda denumirii, existã variabile care îºi pot modifica valoarea ºi variabile care nu ºi-o pot modifica, numite în Java variabile finale. Orice variabilã trebuie sã fie declaratã pentru a putea fi folositã. Aceastã declaraþie trebuie sã conþinã un tip de valori care pot fi memorate în locaþia rezervatã variabilei ºi un nume pentru variabila declaratã. În funcþie de locul în sursa programului în care a fost declaratã variabila, aceasta primeºte o clasã de memorare localã sau staticã. Aceastã clasã de memorare defineºte intervalul de existenþã al variabilei în timpul execuþiei. În forma cea mai simplã, declaraþia unei variabile aratã în felul urmãtor: Tip NumeVariabilã [, NumeVariabilã]*; 5.1.1.1 Tipul unei variabile Tipul unei variabile poate fi fie unul dintre tipurile primitive definite de limbajul Java fie o referinþã. Creatorii limbajului Java au avut grijã sã defineascã foarte exact care sunt caracteristicile fiecãrui tip primitiv în parte ºi care este setul de valori care se poate memora în variabilele care au tipuri primitive. În plus, a fost exact definitã ºi modalitatea de reprezentare a acestor tipuri primitive în memorie. În acest fel, variabilele Java devin independente de file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (2 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
platforma hardware ºi software pe care lucreazã. În acelaºi spirit, Java defineºte o valoare implicitã pentru fiecare tip de datã, în cazul în care aceasta nu a primit nici o valoare de la utilizator. În acest fel, ºtim întotdeauna care este valoarea cu care o variabilã intrã în calcul. Este o practicã bunã însã aceea ca programele sã nu depindã niciodatã de aceste iniþializãri implicite. 5.1.1.2 Numele variabilelor Numele variabilei poate fi orice identificator Java. Convenþia nescrisã de formare a numelor variabilelor este aceea cã orice variabilã care nu este finalã are un nume care începe cu literã minusculã în timp ce variabilele finale au nume care conþin numai majuscule. Dacã numele unei variabile care nu este finalã conþine mai multe cuvinte, cuvintele începând cu cel de-al doilea se scriu cu litere minuscule dar cu prima literã majusculã. Exemple de nume de variabile care nu sunt finale ar putea fi: culoarea numãrulDePaºi urmãtorulElement Variabilele finale ar putea avea nume precum: PORTOCALIUVERDEALBASTRUDESCHIS 5.1.1.3 Iniþializarea variabilelor Limbajul Java permite iniþializarea valorilor variabilelor chiar în momentul declarãrii acestora. Sintaxa este urmãtoarea: Tip NumeVariabilã = ValoareIniþialã; Desigur, valoarea iniþialã trebuie sã fie de acelaºi tip cu tipul variabilei sau sã poatã fi convertitã într-o valoare de acest tip. Deºi limbajul Java ne asigurã cã toate variabilele au o valoare iniþialã bine precizatã, este preferabil sã executãm aceastã iniþializare în mod explicit pentru fiecare declaraþie. În acest fel mãrim claritatea propriului cod. Regula ar fi deci urmãtoarea: nici o declaraþie fãrã iniþializare.
5.1.2 Tipuri primitive 5.1.2.1 Tipul boolean file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (3 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
Tipul boolean este folosit pentru memorarea unei valori de adevãr. Pentru acest scop, sunt suficiente doar douã valori: adevãrat ºi fals. În Java aceste douã valori le vom nota prin literalii true ºi respectiv false. Aceste valori pot fi reprezentate în memorie folosindu-ne de o singurã cifrã binarã, adicã pe un bit. Valorile booleene sunt foarte importante în limbajul Java pentru cã ele sunt valorile care se folosesc în condiþiile care controleazã instrucþiunile repetitive sau cele condiþionale. Pentru a exprima o condiþie este suficient sã scriem o expresie al cãrui rezultat este o valoare booleanã, adevãrat sau fals. Valorile de tip boolean nu se pot transforma în valori de alt tip în mod nativ. La fel, nu existã transformare nativã dinspre celelalte valori înspre tipul boolean. Cu alte cuvinte, având o variabilã de tip boolean nu putem memora în interiorul acesteia o valoare întreagã pentru cã limbajul Java nu face pentru noi nici un fel de presupunere legatã de ce înseamnã o anumitã valoare întreagã din punctul de vedere al valorii de adevãr. La fel, dacã avem o variabilã întreagã, nu îi putem atribui o valoare de tip boolean. Orice variabilã booleanã nou creatã primeºte automat valoarea implicitã false. Putem modifica aceastã comportare specificând în mod explicit o valoare iniþialã true dupã modelul pe care îl vom descrie mai târziu. Pentru a declara o variabilã de tip boolean, în Java vom folosi cuvântul rezervat boolean ca în exemplele de mai jos: boolean terminat; boolean areDreptate; Rândurile de mai sus reprezintã declaraþia a douã variabile de tip boolean numite terminatrespectiv areDreptate. Cele douã variabile au, dupã declaraþie, valoarea false. Adicã nu e terminat dar nici n-are dreptate. 5.1.2.2 Tipul caracter Orice limbaj de programare ne oferã într-un fel sau altul posibilitatea de a lucra cu caractere grafice care sã reprezinte litere, cifre, semne de punctuaþie, etc. În cazul limbajului Java acest lucru se poate face folosind tipul primitiv numit tip caracter. O variabilã de tip caracter poate avea ca valoare coduri Unicode reprezentate pe 16 biþi, adicã doi octeþi. Codurile reprezentabile astfel sunt foarte multe, putând acoperi caracterele de bazã din toate limbile scrise existente. În Java putem combina mai multe caractere pentru a forma cuvinte sau ºiruri de caractere mai lungi.
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (4 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
Totuºi, trebuie sã precizãm cã aceste ºiruri de caractere nu trebuiesc confundate cu tablourile de caractere pentru cã ele conþin în plus informaþii legate de lungimea ºirului. Codul nu este altceva decât o corespondenþã între numere ºi caractere fapt care permite conversii între variabile întregi ºi caractere în ambele sensuri. O parte din aceste transformãri pot sã altereze valoarea originalã din cauza dimensiunilor diferite ale zonelor în care sunt memorate cele douã tipuri de valori. Convertirea caracterelor în numere ºi invers poate sã fie utilã la prelucrarea în bloc a caracterelor, cum ar fi trecerea tuturor literelor minuscule în majuscule ºi invers. Atunci când declarãm un caracter fãrã sã specificãm o valoare iniþialã, el va primi automat ca valoare implicitã caracterul nullal codului Unicode, \u0000?. Pentru a declara o variabilã de tip caracter folosim cuvântul rezervat char ca în exemplele urmãtoare: char primaLiterã; char prima, ultima; În cele douã linii de cod am declarat trei variabile de tip caracter care au fost automat iniþializate cu caracterul null. În continuare, vom folosi interschimbabil denumirea de tip caracter cu denumirea de tip char, care are avantajul cã este mai aproape de declaraþiile Java. 5.1.2.3 Tipuri întregi 5.1.2.3.1 Tipul octet
Între tipurile întregi, acest tip ocupã un singur octet de memorie, adicã opt cifre binare. Într-o variabilã de tip octet sunt reprezentate întotdeauna valori cu semn, ca de altfel în toate variabilele de tip întreg definite în limbajul Java. Aceastã convenþie simplificã schema de tipuri primitive care, în cazul altor limbaje include separat tipuri întregi cu semn ºi fãrã. Fiind vorba de numere cu semn, este nevoie de o convenþie de reprezentare a semnului. Convenþia folositã de Java este reprezentarea în complement faþã de doi. Aceastã reprezentare este de altfel folositã de majoritatea limbajelor actuale ºi permite memorarea, pe 8 biþi a 256 de numere începând de la -128 pânã la 127. Dacã aveþi nevoie de numere mai mari în valoare absolutã, apelaþi la alte tipuri întregi. Valoarea implicitã pentru o variabilã neiniþializatã de tip octet este valoarea 0 reprezentatã pe un octet. Iatã ºi câteva exemple de declaraþii care folosesc cuvântul Java rezervat byte: byte octet; byte eleviPeClasa;
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (5 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
În continuare vom folosi interschimbabil denumirea de tip octet cu cea de tip byte. 5.1.2.3.2 Tipul întreg scurt
Tipul întreg scurt este similar cu tipul octet dar valorile sunt reprezentate pe doi octeþi, adicã 16 biþi. La fel ca ºi la tipul octet, valorile sunt întotdeauna cu semn ºi se foloseºte reprezentarea în complement faþã de doi. Valorile de întregi scurþi reprezentabile sunt de la -32768 la 32767 iar valoarea implicitã este 0 reprezentat pe doi octeþi. Pentru declararea variabilelor de tip întreg scurt în Java se foloseºte cuvântul rezervat short, ca în exemplele urmãtoare: short i, j; short valoareNuPreaMare; În continuare vom folosi interschimbabil denumirea de tip întreg scurt ºi cea de tip short. 5.1.2.3.3 Tipul întreg
Singura diferenþã dintre tipul întreg ºi tipurile precedente este faptul cã valorile sunt reprezentate pe patru octeþi adicã 32 biþi. Valorile reprezentabile sunt de la -2147483648 la 2147483647 valoarea implicitã fiind 0. Cuvântul rezervat este int ca în: int salariu; În continuare vom folosi interschimbabil denumirea de tip întreg ºi cea de tip int. 5.1.2.3.4 Tipul întreg lung
În fine, pentru cei care vor sã reprezinte numerele întregi cu semn pe 8 octeþi, 64 de biþi, existã tipul întreg lung. Valorile reprezentabile sunt de la -9223372036854775808 la 9223372036854775807 iar valoarea implicitã este 0L. Pentru cei care nu au calculatoare care lucreazã pe 64 de biþi este bine de precizat faptul cã folosirea acestui tip duce la operaþii lente pentru cã nu existã operaþii native ale procesorului care sã lucreze cu numere aºa de mari. Declaraþia se face cu cuvântul rezervat long. În continuare vom folosi interschimbabil denumirea de tip întreg lung cu cea de tip long.
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (6 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
5.1.2.4 Tipuri flotante Acest tip este folosit pentru reprezentarea numerelor reale sub formã de exponent ºi cifre semnificative. Reprezentarea se face pe patru octeþi, 32 biþi, aºa cum specificã standardul IEEE 754. 5.1.2.4.1 Tipul flotant
Valorile finite reprezentabile într-o variabilã de tip flotant sunt de forma: sm2e unde s este semnul +1 sau -1, m este partea care specificã cifrele reprezentative ale numãrului, numitã ºi mantisã, un întreg pozitiv mai mic decât 224 iar e este un exponent întreg între -149 ºi 104. Valoarea implicitã pentru variabilele flotante este 0.0f. Pentru declararea unui numãr flotant, Java defineºte cuvântul rezervat float. Declaraþiile se fac ca în exemplele urmãtoare: float procent; float noi, ei; În continuare vom folosi interschimbabil denumirea de tip flotant ºi cea de tip float. 5.1.2.4.2 Tipul flotant dublu
Dacã valorile reprezentabile în variabile flotante nu sunt destul de precise sau destul de mari, puteþi folosi tipul flotant dublu care foloseºte opt octeþi pentru reprezentare, urmând acelaºi standard IEEE 754 Valorile finite reprezentabile cu flotanþi dubli sunt de forma: sm2e unde s este semnul +1 sau -1, m este mantisa, un întreg pozitiv mai mic decât 253 iar e este un exponent întreg între -1045 ºi 1000. Valoarea implicitã în acest caz este 0.0d. Pentru a declara flotanþi dubli, Java defineºte cuvântul rezervat double ca în: double distanþaPânãLaLunã; În continuare vom folosi interschimbabil denumirea de tip flotant dublu ºi cea de tip double. În afarã de valorile definite pânã acum, standardul IEEE defineºte câteva valori speciale reprezentabile file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (7 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
pe un flotant sau un flotant dublu. 5.1.2.4.3 Reali speciali definiþi de IEEE
Prima dintre acestea este NaN (Not a Number), valoare care se obþine atunci când efectuãm o operaþie a cãrei rezultat nu este definit, de exemplu 0.0 / 0.0. În plus, standardul IEEE defineºte douã valori pe care le putem folosi pe post de infinit pozitiv ºi negativ. ªi aceste valori pot rezulta în urma unor calcule. Aceste valori sunt definite sub formã de constante ºi în ierarhia standard Java, mai precis în clasa java. lang.Float ºi respectiv în java.lang.Double. Numele constantelor este POSITIVE_INFINITY, NEGATIVE_INFINITY, NaN. În plus, pentru tipurile întregi ºi întregi lungi ºi pentru tipurile flotante existã definite clase în ierarhia standard Java care se numesc respectiv java.lang.Integer, java.lang.Long, java.lang.Float ºi java.lang. Double. În fiecare dintre aceste clase numerice sunt definite douã constante care reprezintã valorile minime ºi maxime care se pot reprezenta în tipurile respective. Aceste douã constante se numesc în mod uniform MIN_VALUE ºi MAX_VALUE.
5.1.3 Tipuri referinþã Tipurile referinþã sunt folosite pentru a referi un obiect din interiorul unui alt obiect. În acest mod putem înlãnþui informaþiile aflate în memorie. Tipurile referinþã au, la fel ca ºi toate celelalte tipuri o valoare implicitã care este atribuitã automat oricãrei variabile de tip referinþã care nu a fost iniþializatã. Aceastã valoare implicitã este definitã de cãtre limbajul Java prin cuvântul rezervat null. Puteþi înþelege semnificaþia referinþei nule ca o referinþã care nu trimite nicãieri, a cãrei destinaþie nu a fost încã fixatã. Simpla declaraþie a unei referinþe nu duce automat la rezervarea spaþiului de memorie pentru obiectul referit. Singura rezervare care se face este aceea a spaþiului necesar memorãrii referinþei în sine. Rezervarea obiectului trebuie fãcutã explicit în program printr-o expresie de alocare care foloseºte cuvântul rezervat new. O variabilã de tip referinþã nu trebuie sã trimitã pe tot timpul existenþei sale cãtre acelaºi obiect în memorie. Cu alte cuvinte, variabila îºi poate schimba locaþia referitã în timpul execuþiei. 5.1.3.1 Tipul referinþã cãtre o clasã
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (8 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
Tipul referinþã cãtre o clasã este un tip referinþã care trimite cãtre o instanþã a unei clasei de obiecte. Clasa instanþei referite poate fi oricare clasã validã definitã de limbaj sau de utilizator. Clasa de obiecte care pot fi referite de o anumitã variabilã de tip referinþã la clasã trebuie declaratã explicit. De exemplu, pentru a declara o referinþã cãtre o instanþã a clasei Minge, trebuie sã folosim urmãtoarea sintaxã: Minge mingeaMea; Din acest moment, variabila referinþã de clasã numitã mingeaMea va putea pãstra doar referinþe cãtre obiecte de tip Minge sau cãtre obiecte aparþinând unor clase derivate din clasa Minge. De exemplu, dacã avem o altã clasã, derivatã din Minge, numitã MingeDeBaschet, putem memora în referinþa mingeaMea ºi o trimitere cãtre o instanþã a clasei MingeDeBaschet. În mod general însã, nu se pot pãstra în variabila mingeaMea referinþe cãtre alte clase de obiecte. Dacã se încercã acest lucru, eroarea va fi semnalatã chiar în momentul compilãrii, atunci când sursa programului este examinatã pentru a fi transformatã în instrucþiuni ale maºinii virtuale Java. Sã mai observãm cã o referinþã cãtre clasa de obiecte Object, rãdãcina ierarhiei de clase Java, poate pãstra ºi o referinþã cãtre un tablou. Mai multe lãmuriri asupra acestei afirmaþii mai târziu. 5.1.3.2 Tipul referinþã cãtre o interfaþã Tipul referinþã cãtre o interfaþã permite pãstrarea unor referinþe cãtre obiecte care respectã o anumitã interfaþã. Clasa obiectelor referite poate fi oricare, atâta timp cât clasa respectivã implementeazã interfaþa cerutã. Declaraþia se face cu urmãtoarea sintaxã: ObiectSpaþioTemporal mingeaLuiVasile; în care tipul este chiar numele interfeþei cerute. Dacã clasa de obiecte Minge declarã cã implementeazã aceastã interfaþã, atunci variabila referinþã mingeaLuiVasile poate lua ca valoare referinþa cãtre o instanþã a clasei Minge sau a clasei MingeDeBaschet. Prin intermediul unei variabile referinþã cãtre o interfaþã nu se poate apela decât la funcþionalitatea cerutã în interfaþa respectivã, chiar dacã obiectele reale oferã ºi alte facilitãþi, ele aparþinând unor clase mai bogate în metode. 5.1.3.3 Tipul referinþã cãtre un tablou
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (9 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
Tipul referinþã cãtre un tablou este un tip referinþã care poate pãstra o trimitere cãtre locaþia din memorie a unui tablou de elemente. Prin intermediul acestei referinþe putem accesa elementele tabloului furnizând indexul elementului dorit. Tablourile de elemente nu existã în general ci ele sunt tablouri formate din elemente de un tip bine precizat. Din aceastã cauzã, atunci când declarãm o referinþã cãtre un tablou, trebuie sã precizãm ºi de ce tip sunt elementele din tabloul respectiv. La declaraþia referinþei cãtre tablou nu trebuie sã precizãm ºi numãrul de elemente din tablou. Iatã cum se declarã o referinþã cãtre un tablou de întregi lungi: long numere[]; Numele variabilei este numere. Un alt exemplu de declaraþie de referinþã cãtre un tablou: Minge echipament[]; Declaraþia de mai sus construieºte o referinþã cãtre un tablou care pãstreazã elemente de tip referinþã cãtre o instanþã a clasei Minge. Numele variabilei referinþã este echipament. Parantezele drepte de dupã numele variabilei specificã faptul cã este vorba despre un tablou. Mai multe despre tablouri într-un paragraf urmãtor.
5.1.4 Clasa de memorare Fiecare variabilã trebuie sã aibã o anumitã clasã de memorare. Aceastã clasã ne permite sã aflãm care este intervalul de existenþã ºi vizibilitatea unei variabile în contextul execuþiei unui program. Este important sã înþelegem exact aceastã noþiune pentru cã altfel vom încerca sã referim variabile înainte ca acestea sã fi fost create sau dupã ce au fost distruse sau sã referim variabile care nu sunt vizibile din zona de program în care le apelãm. Soluþia simplã de existenþã a tuturor variabilelor pe tot timpul execuþiei este desigur afarã din discuþie atât din punct de vedere al eficienþei cât ºi a eleganþei ºi stabilitãþii codului. 5.1.4.1 Variabile locale Aceste variabile nu au importanþã prea mare în contextul întregii aplicaþii, ele servind la rezolvarea unor probleme locale. Variabilele locale sunt declarate, rezervate în memorie ºi utilizate doar în interiorul unor blocuri de instrucþiuni, fiind distruse automat la ieºirea din aceste blocuri. Aceste variabile sunt vizibile doar în interiorul blocului în care au fost create ºi în subblocurile acestuia.
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (10 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
5.1.4.2 Variabile statice Variabilele statice sunt în general legate de funcþionalitatea anumitor clase de obiecte ale cãror instanþe folosesc în comun aceste variabile. Variabilele statice sunt create atunci când codul specific clasei în care au fost declarate este încãrcat în memorie ºi nu sunt distruse decât atunci când acest cod este eliminat din memorie. Valorile memorate în variabile statice au importanþã mult mai mare în aplicaþie decât cele locale, ele pãstrând informaþii care nu trebuie sã se piardã la dispariþia unei instanþe a clasei. De exemplu, variabila în care este memorat numãrul de picioare al obiectelor din clasa Om nu trebuie sã fie distrusã la dispariþia unei instanþe din aceastã clasã. Aceasta din cauzã cã ºi celelalte instanþe ale clasei folosesc aceeaºi valoare. ªi chiar dacã la un moment dat nu mai existã nici o instanþã a acestei clase, numãrul de picioare ale unui Om trebuie sã fie accesibil în continuare pentru interogare de cãtre celelalte clase. Variabilele statice nu se pot declara decât ca variabile ale unor clase ºi conþin în declaraþie cuvântul rezervat static. Din cauza faptului cã ele aparþin clasei ºi nu unei anumite instanþe a clasei, variabilele statice se mai numesc uneori ºi variabile de clasã. 5.1.4.3 Variabile dinamice Un alt tip de variabile sunt variabilele a cãror perioadã de existenþã este stabilitã de cãtre programator. Aceste variabile pot fi alocate la cerere, dinamic, în orice moment al execuþiei programului. Ele vor fi distruse doar atunci când nu mai sunt referite de nicãieri. La alocarea unei variabile dinamice, este obligatoriu sã pãstrãm o referinþã cãtre ea într-o variabilã de tip referinþã. Altfel, nu vom putea accesa în viitor variabila dinamicã. În momentul în care nici o referinþã nu mai trimite cãtre variabila dinamicã, de exemplu pentru cã referinþa a fost o variabilã localã ºi blocul în care a fost declaratã ºi-a terminat execuþia, variabila dinamicã este distrusã automat de cãtre sistem printr-un mecanism numit colector de gunoaie. Colectorul de gunoaie poate porni din iniþiativa sistemului sau din iniþiativa programatorului la momente bine precizate ale execuþiei. Pentru a rezerva spaþiu pentru o variabilã dinamicã este nevoie sã apelãm la o expresie de alocare care foloseºte cuvântul rezervat new. Aceastã expresie alocã spaþiul necesar pentru un anumit tip de valoare. De exemplu, pentru a rezerva spaþiul necesar unui obiect de tip Minge, putem apela la sintaxa: Minge mingeaMea = new Minge(); iar pentru a rezerva spaþiul necesar unui tablou de referinþe cãtre obiecte de tip Minge putem folosi declaraþia:
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (11 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
Minge echipament[] = new Minge[5]; Am alocat astfel spaþiu pentru un tablou care conþine 5 referinþe cãtre obiecte de tip Minge. Pentru alocarea tablourilor conþinând tipuri primitive se foloseºte aceeaºi sintaxã. De exemplu, urmãtoarea linie de program alocã spaþiul necesar unui tablou cu 10 întregi, creând în acelaºi timp ºi o variabilã referinþã spre acest tablou, numitã numere: int numere[] = new int[10];
5.1.5 Tablouri de variabile Tablourile servesc, dupã cum spuneam, la memorarea secvenþelor de elemente de acelaºi tip. Tablourile unidimensionale au semnificaþia vectorilor de elemente. Se poate întâmpla sã lucrãm ºi cu tablouri de referinþe cãtre tablouri, în acest caz modelul fiind acela al unei matrici bidimensionale. În fine, putem extinde definiþia ºi pentru mai mult de douã dimensiuni. 5.1.5.1 Declaraþia variabilelor de tip tablou Pentru a declara variabile de tip tablou, trebuie sã specificãm tipul elementelor care vor umple tabloul ºi un nume pentru variabila referinþã care va pãstra trimiterea cãtre zona de memorie în care sunt memorate elementele tabloului. Deºi putem declara variabile referinþã cãtre tablou ºi separat, de obicei declaraþia este fãcutã în acelaºi timp cu alocarea spaþiului ca în exemplele din paragraful anterior. Sintaxa Java permite plasarea parantezelor drepte care specificã tipul tablou înainte sau dupã numele variabilei. Astfel, urmãtoarele douã declaraþii sunt echivalente: int[] numere; int numere[]; Dacã doriþi sã folosiþi tablouri cu douã dimensiuni ca matricile, puteþi sã declaraþi un tablou de referinþe cãtre tablouri cu una dintre urmãtoarele trei sintaxe echivalente: float[][] matrice; float[] matrice[]; float matrice[][]; De precizat cã ºi în cazul dimensiunilor multiple, declaraþiile de mai sus nu fac nimic altceva decât sã rezerve loc pentru o referinþã ºi sã precizeze numãrul de dimensiuni. Alocarea spaþiului pentru elementele tabloului trebuie fãcutã explicit.
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (12 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
Despre rezervarea spaþiului pentru tablourile cu o singurã dimensiune am vorbit deja. Pentru tablourile cu mai multe dimensiuni, rezervarea spaþiului se poate face cu urmãtoarea sintaxã: byte [][]octeti = new byte[23][5]; În expresia de alocare sunt specificate în clar numãrul elementelor pentru fiecare dimensiune a tabloului.
5.1.5.2 Iniþializarea tablourilor. Limbajul Java permite ºi o sintaxã pentru iniþializarea elementelor unui tablou. Într-un astfel de caz este rezervat automat ºi spaþiul de memorie necesar memorãrii valorilor iniþiale. Sintaxa folositã în astfel de cazuri este urmãtoarea: char []caractere = { a, b, c, d }; Acest prim exemplu alocã spaþiu pentru patru elemente de tip caracter ºi iniþializeazã aceste elemente cu valorile dintre acolade. Dupã aceea, creeazã variabila de tip referinþã numitã caractere ºi o iniþializeazã cu referinþa la zona de memorie care pãstreazã cele patru valori. Iniþializarea funcþioneazã ºi la tablouri cu mai multe dimensiuni ca în exemplele urmãtoare: int [][]numere = { { 1, 3, 4, 5 }, { 2, 4, 5 }, { 1, 2, 3, 4, 5 } }; double [][][]reali = { { { 0.0, -1.0 }, { 4.5 } }, { { 2.5, 3.0 } } }; Dupã cum observaþi numãrul iniþializatorilor nu trebuie sã fie acelaºi pentru fiecare element. 5.1.5.3 Lungimea tablourilor Tablourile Java sunt alocate dinamic, ceea ce înseamnã cã ele îºi pot schimba dimensiunile pe parcursul execuþiei. Pentru a afla numãrul de elemente dintr-un tablou, putem apela la urmãtoarea sintaxã: float []tablou = new float[25]; int dimensiune = tablou.length; // dimensiune primeºte valoarea 25 file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (13 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
sau float [][]multiTablou = new float[3][4]; int dimensiune1 = multiTablou[2].length; // dimensiune1 primeºte valoarea 4 int dimensiune2 = multiTablou.length; // dimensiune2 primeºte valoarea 3 ^ 5.1.5.4 Referirea elementelor din tablou Elementele unui tablou se pot referi prin numele referinþei tabloului ºi indexul elementului pe care dorim sã-l referim. În Java, primul element din tablou este elementul cu numãrul 0, al doilea este elementul numãrul 1 ºi aºa mai departe. Sintaxa de referire foloseºte parantezele pãtrate [ ºi ]. Între ele trebuie specificat indexul elementului pe care dorim sã-l referim. Indexul nu trebuie sã fie constant, el putând fi o expresie de complexitate oarecare. Iatã câteva exemple: int []tablou = new int[10]; tablou[3] = 1; // al patrulea element primeºte valoarea 1 float [][]reali = new float[3][4]; reali[2][3] = 1.0f; // al patrulea element din al treilea tablou // primeºte valoarea 1 În cazul tablourilor cu mai multe dimensiuni, avem în realitate tablouri de referinþe la tablouri. Asta înseamnã cã dacã considerãm urmãtoarea declaraþie: char [][]caractere = new char [5][];
Figura 5.1 Elementele tabloului sunt de tip referinþã, iniþializate implicit la valoarea null.
Variabila referinþã numitã caractere conþine deocamdatã un tablou de 5 referinþe la tablouri de caractere. Cele cinci referinþe sunt iniþializate cu null. Putem iniþializa aceste tablouri prin atribuiri de
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (14 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
expresii de alocare: caractere[0] = new char [3]; caractere[4] = new char [5];
Figura 5.2 Noile tablouri sunt referite din interiorul tabloului original. Elementele noilor tablouri sunt caractere.
La fel, putem scrie: char []tablouDeCaractere = caractere[0];
Figura 5.3 Variabilele de tip referinþã caractere[0] ºi tablouDeCaractere trimit spre acelaºi tablou rezervat în memorie.
Variabila tablouDeCaractere trimite cãtre acelaºi tablou de caractere ca ºi cel referit de primul element al tabloului referit de variabila caractere. Sã mai precizãm cã referirea unui element de tablou printr-un index mai mare sau egal cu lungimea tabloului duce la oprirea execuþiei programului cu un mesaj de eroare de execuþie corespunzãtor. 5.1.5.5 Alocarea ºi eliberarea tablourilor
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (15 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
Despre alocarea tablourilor am spus deja destul de multe. În cazul în care nu avem iniþializatori, variabilele sunt iniþializate cu valorile implicite definite de limbaj pentru tipul corespunzãtor. Aceasta înseamnã cã, pentru tablourile cu mai multe dimensiuni, referinþele sunt iniþializate cu null. Pentru eliberarea memoriei ocupate de un tablou, este suficient sã tãiem toate referinþele cãtre tablou. Sistemul va sesiza automat cã tabloul nu mai este referit ºi mecanismul colector de gunoaie va elibera zona. Pentru a tãia o referinþã cãtre un tablou dãm o altã valoare variabilei care referã tabloul. Valoarea poate fi null sau o referinþã cãtre un alt tablou. De exemplu: float []reali = new float[10]; ? reali = null; // eliberarea tabloului sau reali = new float[15]; // eliberarea în alt fel sau { float []reali = new float[10]; ? }// eliberare automatã, variabila reali a fost // distrusã la ieºirea din blocul în care a // fost declaratã, iar tabloul de 10 flotanþi // nu mai este referit ^
5.1.6 Conversii Operaþiile definite în limbajul Java au un tip bine precizat de argumente. Din pãcate, existã situaþii în care nu putem transmite la apelul acestora exact tipul pe care compilatorul Java îl aºteaptã. În asemenea situaþii, compilatorul are douã alternative: fie respinge orice operaþie cu argumente greºite, fie încearcã sã converteascã argumentele cãtre tipurile necesare. Desigur, în cazul în care conversia nu este posibilã, singura alternativã rãmâne prima. În multe situaþii însã, conversia este posibilã. Sã luãm de exemplu tipurile întregi. Putem sã convertim întotdeauna un întreg scurt la un întreg. Valoarea rezultatã va fi exact aceeaºi. Conversia inversã însã, poate pune probleme dacã valoarea memoratã în întreg depãºeºte capacitatea de memorare a unui întreg scurt. În afarã de conversiile implicite, pe care compilatorul le hotãrãºte de unul singur, existã ºi conversii explicite, pe care programatorul le poate forþa la nevoie. Aceste conversii efectueazã de obicei operaþii în care existã pericolul sã se piardã o parte din informaþii. Compilatorul nu poate hotãrî de unul singur în aceste situaþii.
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (16 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
Conversiile implicite pot fi un pericol pentru stabilitatea aplicaþiei dacã pot sã ducã la pierderi de informaþii fãrã avertizarea programatorului. Aceste erori sunt de obicei extrem de greu de depistat. În fiecare limbaj care lucreazã cu tipuri fixe pentru datele sale existã conversii imposibile, conversii periculoase ºi conversii sigure. Conversiile imposibile sunt conversiile pe care limbajul nu le permite pentru cã nu ºtie cum sã le execute sau pentru cã operaþia este prea periculoasã. De exemplu, Java refuzã sã converteascã un tip primitiv cãtre un tip referinþã. Deºi s-ar putea imagina o astfel de conversie bazatã pe faptul cã o adresã este în cele din urmã un numãr natural, acest tip de conversii sunt extrem de periculoase, chiar ºi atunci când programatorul cere explicit aceastã conversie. 5.1.6.1 Conversii de extindere a valorii În aceste conversii valoarea se reprezintã într-o zonã mai mare fãrã sã se piardã nici un fel de informaþii. Iatã conversiile de extindere pe tipuri primitive: ● ● ● ● ● ●
byte la short, int, long, float sau double short la int, long, float sau double char la int, long, float sau double int la long, float sau double long la float sau double float la double
Sã mai precizãm totuºi cã, într-o parte din aceste cazuri, putem pierde din precizie. Aceastã situaþie apare de exemplu la conversia unui long într-un float, caz în care se pierd o parte din cifrele semnificative pãstrându-se însã ordinul de mãrime. De altfel aceastã observaþie este evidentã dacã þinem cont de faptul cã un long este reprezentat pe 64 de biþi în timp ce un float este reprezentat doar pe 32 de biþi. Precizia se pierde chiar ºi în cazul conversiei long la double sau int la float pentru cã, deºi dimensiunea zonei alocatã pentru cele douã tipuri este aceeaºi, numerele flotante au nevoie de o parte din aceastã zonã pentru a reprezenta exponentul. În aceste situaþii, se va produce o rotunjire a numerelor reprezentate. 5.1.6.2 Conversii de trunchiere a valorii Convenþiile de trunchiere a valorii pot produce pierderi de informaþie pentru cã ele convertesc tipuri mai bogate în informaþii cãtre tipuri mai sãrace. Conversiile de trunchiere pe tipurile elementare sunt urmãtoarele: ●
byte la char
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (17 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile ● ● ● ● ● ●
short la byte sau char char la byte sau short int la byte, short sau char long la byte, short char, sau int float la byte, short, char, int sau long double la byte, short, char, int, long sau float.
În cazul conversiilor de trunchiere la numerele cu semn, este posibil sã se schimbe semnul pentru cã, în timpul conversiei, se îndepãrteazã pur ºi simplu octeþii care nu mai încap ºi poate rãmâne primul bit diferit de vechiul prim bit. Copierea se face începând cu octeþii mai puþin semnificativi iar trunchierea se face la octeþii cei mai semnificativi. Prin octeþii cei mai semnificativi ne referim la octeþii în care sunt reprezentate cifrele cele mai semnificative. Cifrele cele mai semnificative sunt cifrele care dau ordinul de mãrime al numãrului. De exemplu, la numãrul 123456, cifrele cele mai semnificative sunt primele, adicã: 1, 2, etc. La acelaºi numãr, cifrele cele mai puþin semnificative sunt ultimele, adicã: 6, 5, etc. 5.1.6.3 Conversii pe tipuri referinþã Conversiile tipurilor referinþã nu pun probleme pentru modul în care trebuie executatã operaþia din cauzã cã, referinþa fiind o adresã, în timpul conversiei nu trebuie afectatã în nici un fel aceastã adresã. În schimb, se pun probleme legate de corectitudinea logicã a conversiei. De exemplu, dacã avem o referinþã la un obiect care nu este tablou, este absurd sã încercãm sã convertim aceastã referinþã la o referinþã de tablou. Limbajul Java defineºte extrem de strict conversiile posibile în cazul tipurilor referinþã pentru a salva programatorul de eventualele necazuri care pot apare în timpul execuþiei. Iatã conversiile posibile: ●
●
●
●
O referinþã cãtre un obiect aparþinând unei clase C poate fi convertit la o referinþã cãtre un obiect aparþinând clasei S doar în cazul în care C este chiar S sau C este derivatã direct sau indirect din S. O referinþã cãtre un obiect aparþinând unei clase C poate fi convertit cãtre o referinþã de interfaþã I numai dacã clasa C implementeazã interfaþa I. O referinþã cãtre un tablou poate fi convertitã la o referinþã cãtre o clasã numai dacã clasa respectivã este clasa Object. O referinþã cãtre un tablou de elemente ale cãrui elemente sunt de tipul T1 poate fi convertitã la o referinþã cãtre un tablou de elemente de tip T2 numai dacã T1 ºi T2 reprezintã acelaºi tip primitiv sau T2 este un tip referinþã ºi T1 poate fi convertit cãtre T2.
5.1.6.4 Conversii la operaþia de atribuire Conversiile pe care limbajul Java le executã implicit la atribuire sunt foarte puþine. Mai exact, sunt file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (18 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
executate doar acele conversii care nu necesitã validare în timpul execuþiei ºi care nu pot pierde informaþii în cazul tipurilor primitive. În cazul valorilor aparþinând tipurilor primitive, urmãtorul tabel aratã conversiile posibile. Pe coloane avem tipul de valoare care se atribuie iar pe linii avem tipurile de variabile la care se atribuie: boolean
char
byte
short
int
long
float
double
boolean
Da
Nu
Nu
Nu
Nu
Nu
Nu
Nu
char
Nu
Da
Da
Da
Nu
Nu
Nu
Nu
byte
Nu
Da
Da
Nu
Nu
Nu
Nu
Nu
short
Nu
Da
Da
Da
Nu
Nu
Nu
Nu
int
Nu
Da
Da
Da
Da
Nu
Nu
Nu
long
Nu
Da
Da
Da
Da
Da
Nu
Nu
float
Nu
Da
Da
Da
Da
Da
Da
Nu
double
Nu
Da
Da
Da
Da
Da
Da
Da
Tabloul 5.1 Conversiile posibile într-o operaþie de atribuire cu tipuri primitive. Coloanele reprezintã tipurile care se atribuie iar liniile reprezintã tipul de variabilã cãtre care se face atribuirea.
Dupã cum observaþi, tipul boolean nu poate fi atribuit la o variabilã de alt tip. Valorile de tip primitiv nu pot fi atribuite variabilelor de tip referinþã. La fel, valorile de tip referinþã nu pot fi memorate în variabile de tip primitiv. În ceea ce priveºte tipurile referinþã între ele, urmãtorul tabel defineºte situaþiile în care conversiile sunt posibile la atribuirea unei valori de tipul T la o variabilã de tipul S:
S este o clasã care nu este finalã
T este o clasã care nu este finalã
T este o clasã care este finalã
T este o interfaþã
T = B[] este un tablou cu elemente de tipul B
T trebuie sã fie subclasã a lui S
T trebuie sã fie o subclasã a lui S
eroare la compilare
S trebuie sã fie Object
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (19 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
S este o clasã care este finalã
T trebuie sã fie aceeaºi clasã ca ºi S
T trebuie sã fie aceeaºi clasã ca ºi S
eroare la compilare
eroare la compilare
S este o interfaþã
T trebuie sã implementeze interfaþa S
T trebuie sã implementeze interfaþa S
T trebuie sã fie o subinterfaþã a lui S
eroare la compilare
S = A[] este un tablou cu elemente de tipul A
eroare la compilare
eroare la compilare
eroare la compilare
A sau B sunt acelaºi tip primitiv sau A este un tip referinþã ºi B poate fi atribuit lui A
Tabloul 5.2 Conversiile posibile la atribuirea unei valori de tipul T la o variabilã de tipul S.
5.1.6.5 Conversii explicite Conversiile de tip cast, sau casturile, sunt apelate de cãtre programator în mod explicit. Sintaxa pentru construcþia unui cast este scrierea tipului cãtre care dorim sã convertim în paranteze în faþa valorii pe care dorim sã o convertim. Forma generalã este: ( Tip ) Valoare Conversiile posibile în acest caz sunt mai multe decât conversiile implicite la atribuire pentru cã în acest caz programatorul este prevenit de eventuale pierderi de date el trebuind sã apeleze conversia explicit. Dar, continuã sã existe conversii care nu se pot apela nici mãcar în mod explicit, dupã cum am explicat înainte. În cazul conversiilor de tip cast, orice valoare numericã poate fi convertitã la orice valoare numericã. În continuare, valorile de tip boolean nu pot fi convertite la nici un alt tip. Nu existã conversii între valorile de tip referinþã ºi valorile de tip primitiv. În cazul conversiilor dintr-un tip referinþã într-altul putem separa douã cazuri. Dacã compilatorul poate decide în timpul compilãrii dacã conversia este corectã sau nu, o va decide. În cazul în care compilatorul nu poate decide pe loc, se va efectua o verificare a conversiei în timpul execuþiei. Dacã conversia se dovedeºte greºitã, va apare o eroare de execuþie ºi programul va fi întrerupt. file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (20 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
Iatã un exemplu de situaþie în care compilatorul nu poate decide dacã conversia este posibilã sau nu: Minge mingeaMea; ? MingeDeBaschet mingeaMeaDeBaschet; // MingeDeBaschet este o clasã // derivatã din clasa Minge mingeaMeaDeBaschet=(MingeDeBaschet)mingeaMea; În acest caz, compilatorul nu poate fi sigur dacã referinþa memoratã în variabila mingeaMea este de tip MingeDeBaschet sau nu pentru cã variabilei de tip Minge i se pot atribui ºi referinþe cãtre instanþe de tip Minge în general, care nu respectã întru totul definiþia clasei MingeDeBaschet sau chiar referinþã cãtre alte tipuri de minge derivate din clasa Minge, de exemplu MingeDePolo care implementeazã proprietãþi ºi operaþii diferite faþã de clasa MingeDeBaschet. Iatã ºi un exemplu de conversie care poate fi decisã în timpul compilãrii: Minge mingeaMea; MingeDeBaschet mingeaMeaDeBaschet; ? mingeaMea = ( Minge ) mingeaMeaDeBaschet; În urmãtorul exemplu însã, se poate decide în timpul compilãrii imposibilitatea conversiei: MingeDeBaschet mingeaMeaDeBaschet; MingeDePolo mingeaMeaDePolo; ? mingeaMeaDePolo = ( MingeDePolo ) mingeaMeaDeBaschet; În fine, tabelul urmãtor aratã conversiile de tip cast a cãror corectitudine poate fi stabilitã în timpul compilãrii. Conversia încearcã sã transforme printr-un cast o referinþã de tip T într-o referinþã de tip S.
S este o clasã care nu este finalã
T este o clasã care nu este finalã
T este o clasã care este finalã
T este o interfaþã
T = B[] este un tablou cu elemente de tipul B
T trebuie sã fie subclasã a lui S
T trebuie sã fie o subclasã a lui S
Totdeauna corectã la compilare
S trebuie sã fie Object
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (21 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
S este o clasã care este finalã
S trebuie sã fie subclasã a lui T
T trebuie sã fie aceeaºi clasã ca ºi S
S trebuie sã implementeze interfaþa T
eroare la compilare
S este o interfaþã
Totdeauna corectã la compilare
T trebuie sã implementeze interfaþa S
Totdeauna corectã la compilare
eroare la compilare
S = A[] este un tablou cu elemente de tipul A
T trebuie sã fie Object
eroare la compilare
eroare la compilare
A sau B sunt acelaºi tip primitiv sau A este un tip referinþã ºi B poate fi convertit cu un cast la A
Tabloul 5.3 Cazurile posibile la convertirea unei referinþe de tip T într-o referinþã de tip S.
5.1.6.6 Conversii de promovare aritmeticã Promovarea aritmeticã se aplicã în cazul unor formule în care operanzii pe care se aplicã un operator sunt de tipuri diferite. În aceste cazuri, compilatorul încearcã sã promoveze unul sau chiar amândoi operanzii la acelaºi tip pentru a putea fi executatã operaþia. Existã douã tipuri de promovare, promovare aritmeticã unarã ºi binarã. În cazul promovãrii aritmetice unare, existã un singur operand care în cazul cã este byte sau short este transformat la int altfel rãmâne nemodificat. La promovarea aritmeticã binarã se aplicã urmãtorul algoritm: 1. 2. 3. 4.
Dacã un operand este double, celãlalt este convertit la double. Altfel, dacã un operand este de tip float, celãlalt operand este convertit la float. Altfel, dacã un operand este de tip long, celãlalt este convertit la long Altfel, amândoi operanzii sunt convertiþi la int.
De exemplu, în urmãtoarea operaþie amândoi operanzii vor fi convertiþi la float prin promovare aritmeticã binarã:
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (22 of 23)12.01.2006 23:07:53
Capitolul V -- 1. Variabile
float f; double i = f + 3; Dupã efectuarea operaþiei, valoarea obþinutã va fi convertitã implicit la double. În urmãtorul exemplu, se produce o promovare unarã la int de la short. short s, r; ? int min = ( r < -s ) ? r : s; În expresia condiþionalã, operandul -s se traduce de fapt prin aplicarea operatorului unar - la variabila s care este de tip short. În acest caz, se va produce automat promovarea aritmeticã unarã de la short la int, apoi se va continua evaluarea expresiei. [capitolul V] [cuprins] (C) IntegraSoft 1996-1998
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap51.html (23 of 23)12.01.2006 23:07:53
Capitolul V -- 2. Expresii
Capitolul V 5.2 Expresii 5.2.1 Valoarea ºi tipul unei expresii 5.2.2 Ordinea de evaluare 5.2.3 Operatori 5.2.3.1 Operatori unari 5.2.3.1.1 Operatorii de preincrementare ºi postincrementare 5.2.3.1.2 Operatorul + unar 5.2.3.1.3 Operatorul - unar 5.2.3.1.4 Operatorul de complementare 5.2.3.1.5 Operatorul de negare logicã 5.2.3.1.6 Casturi 5.2.3.2 Operatori binari 5.2.3.2.1 Operatori multiplicativi: *, /, % 5.2.3.2.2 Operatori aditivi: +, -, concatenare pentru ºiruri de caractere 5.2.3.2.3 Operatori de ºiftare: >>, > 5.2.3.2.4 Operatori relaþionali: , =, instanceof 5.2.3.2.5 Operatori de egalitate: ==, != 5.2.3.2.6 Operatori la nivel de bit: &, |, ^ 5.2.3.2.7 Operatori logici: &&, || 5.2.3.3 Operatorul condiþional ?: 5.2.3.4 Operaþii întregi 5.2.3.5 Operaþii flotante 5.2.3.6 Apeluri de metode
5.2.1 Valoarea ºi tipul unei expresii Fiecare expresie a limbajului Java are un rezultat ºi un tip. Rezultatul poate fi: ● ●
o valoare o variabilã
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap52.html (1 of 13)12.01.2006 23:07:54
Capitolul V -- 2. Expresii ●
nimic
În cazul în care valoarea unei expresii are ca rezultat o variabilã, expresia poate apare în stânga unei operaþii de atribuire. De exemplu expresia: tablou[i] este o expresie care are ca rezultat o variabilã ºi anume locaþia elementului cu indexul i din tabloul de elemente numit tablou. O expresie nu produce nimic doar în cazul în care este un apel de metodã ºi acest apel nu produce nici un rezultat (este declarat de tip void). Fiecare expresie are în timpul compilãrii un tip cunoscut. Acest tip poate fi o valoare primitivã sau o referinþã. În cazul expresiilor care au tip referinþã, valoarea expresiei poate sã fie ºi referinþa neiniþializatã, null.
5.2.2 Ordinea de evaluare În Java operanzii sunt evaluaþi întotdeauna de la stânga spre dreapta. Acest lucru nu trebuie sã ne îndemne sã scriem cod care sã depindã de ordinea de evaluare pentru cã, în unele situaþii, codul rezultat este greu de citit. Regula este introdusã doar pentru a asigura generarea uniformã a codului binar, independent de compilatorul folosit. Evaluarea de la stânga la dreapta implicã urmãtoarele aspecte: ●
în cazul unui operator binar, operandul din stânga este întotdeauna complet evaluat atunci când se trece la evaluarea operandului din dreapta. De exemplu, în expresia: ( i++ ) + i dacã i avea valoarea iniþialã 2, toatã expresia va avea valoarea 5 pentru cã valoarea celui de-al doilea i este luatã dupã ce s-a executat incrementarea i++.
●
în cazul unei referinþe de tablou, expresia care numeºte tabloul este complet evaluatã înainte de a se trece la evaluarea expresiei care dã indexul. De exemplu, în expresia: ( a = b )[i]
●
●
indexul va fi aplicat dupã ce s-a executat atribuirea valorii referinþã b la variabila referinþã de tablou a. Cu alte cuvinte, rezultatul va fi al i-lea element din tabloul b. în cazul apelului unei metode, expresia care numeºte obiectul este complet evaluatã atunci când se
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap52.html (2 of 13)12.01.2006 23:07:54
Capitolul V -- 2. Expresii
●
●
trece la evaluarea expresiilor care servesc drept argumente. în cazul unui apel de metodã, dacã existã mai mult decât un parametru, la evaluarea parametrului numãrul i, toþi parametrii de la 1 la i-1 sunt deja evaluaþi complet. în cazul unei expresii de alocare, dacã avem mai multe dimensiuni exprimate în paranteze drepte, dimensiunile sunt de asemenea evaluate de la stânga la dreapta. De exemplu, în expresia: new int[i++][i]
●
tabloul rezultat nu va fi o matrice pãtraticã ci, dacã i avea valoarea 3, de dimensiune 3 x 4.
5.2.3 Operatori 5.2.3.1 Operatori unari Operatorii unari se aplicã întotdeauna unui singur operand. Aceºti operatori sunt, în general exprimaþi înaintea operatorului asupra cãruia se aplicã. Existã însã ºi douã excepþii, operatorii de incrementare ºi decrementare care pot apare ºi înainte ºi dupã operator, cu semnificaþii diferite. Operatorii unari sunt urmãtorii: ● ● ● ● ● ● ● ● ●
++ preincrement -- predecrement ++ postincrement -- postdecrement + unar - unar ~ complementare ! negaþie logicã cast
5.2.3.1.1 Operatorii de preincrementare ºi postincrementare
Operatorii ++ preincrement ºi postincrement au acelaºi rezultat final, ºi anume incrementeazã variabila asupra cãreia acþioneazã cu 1. Operandul asupra cãruia sunt apelaþi trebuie sã fie o variabilã de tip aritmetic. Nu are sens sã apelãm un operand de incrementare asupra unei valori (de exemplu valoarea unei expresii sau un literal), pentru cã aceasta nu are o locaþie de memorie fixã în care sã memorãm valoarea dupã incrementare. În cazul operatorului prefix, valoarea rezultat a acestei expresii este valoarea variabilei dupã incrementare în timp ce, la operatorul postfix, valoarea rezultat a expresiei este valoarea de dinainte de incrementare. De exemplu, dupã execuþia urmãtoarei secvenþe de instrucþiuni:
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap52.html (3 of 13)12.01.2006 23:07:54
Capitolul V -- 2. Expresii
int i = 5; int j = i++; valoarea lui j este 5, în timp ce, dupã execuþia urmãtoarei secvenþe de instrucþiuni: int i = 5; int j = ++i; valoarea lui j va fi 6. În ambele cazuri, valoarea finalã a lui i va fi 6. În cazul operatorilor -? predecrement ºi postdecrement, sunt valabile aceleaºi consideraþii ca mai sus, cu diferenþa cã valoarea variabilei asupra cãreia se aplicã operandul va fi decrementatã cu 1. De exemplu, urmãtoarele instrucþiuni: int i = 5; int j = i--; fac ca j sã aibã valoarea finalã 5 iar urmãtoarele instrucþiuni: int i = 5; int j = --i; fac ca j sã aibã valoarea finalã 4. În ambele cazuri, valoarea finalã a lui i este 4. Operatorii de incrementare ºi de decrementare se pot aplica ºi pe variabile de tip flotant. În asemenea cazuri, se converteºte valoarea 1 la tipul variabilei incrementate sau decrementate, dupã care valoarea rezultatã este adunatã respectiv scãzutã din vechea valoare a variabilei. De exemplu, urmãtoarele instrucþiuni: double f = 5.6; double g = ++f; au ca rezultat final valoarea 6.6 pentru f ºi pentru g. 5.2.3.1.2 Operatorul + unar
Operatorul + unar se aplicã asupra oricãrei valori primitive aritmetice. Valoarea rãmâne neschimbatã. 5.2.3.1.3 Operatorul - unar
Operatorul - unar se aplicã asupra oricãrei valori primitive aritmetice. Rezultatul aplicãrii acestui operand este negarea aritmeticã a valorii. În cazul valorilor întregi, acest lucru este echivalent cu file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap52.html (4 of 13)12.01.2006 23:07:54
Capitolul V -- 2. Expresii
scãderea din 0 a valorii originale. De exemplu, instrucþiunile: int i = 5; int j = -i; îi dau lui j valoarea -5 . În cazul valorilor speciale definite de standardul IEEE pentru reprezentarea numerelor flotante, se aplicã urmãtoarele reguli: ● ● ●
Dacã operandul este NaN rezultatul negãrii aritmetice este tot NaN pentru cã NaN nu are semn. Dacã operandul este unul dintre infiniþi, rezultatul este infinitul opus ca semn. Dacã operandul este zero de un anumit semn, rezultatul este zero de semn diferit. ^
5.2.3.1.4 Operatorul de complementare
Operatorul de complementare ~ se aplicã asupra valorilor primitive de tip întreg. Rezultatul aplicãrii operandului este complementarea bit cu bit a valorii originale. De exemplu, dacã operandul era de tip byte având valoarea, în binar, 00110001, rezultatul va fi 11001110. În realitate, înainte de complementare se face ºi extinderea valorii la un întreg, deci rezultatul va fi de fapt: 11111111 11111111 11111111 11001110. 5.2.3.1.5 Operatorul de negare logicã
Operatorul de negare logicã ! se aplicã în exclusivitate valorilor de tip boolean. În cazul în care valoarea iniþialã a operandului este true rezultatul va fi false ºi invers. 5.2.3.1.6 Casturi
Casturile sunt expresii de conversie dintr-un tip într-altul, aºa cum deja am arãtat la paragraful destinat conversiilor. Rezultatul unui cast este valoarea operandului convertitã la noul tip de valoare exprimat de cast. De exemplu, la instrucþiunile: double f = 5.6; int i = ( int )f; double g = -5.6; int j = ( int )g; valoarea variabilei f este convertitã la o valoare întreagã, anume 5, ºi noua valoare este atribuitã variabilei i. La fel, j primeºte valoarea -5. Sã mai precizãm cã nu toate casturile sunt valide în Java. De exemplu, nu putem converti o valoare întreagã într-o valoare de tip referinþã.
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap52.html (5 of 13)12.01.2006 23:07:54
Capitolul V -- 2. Expresii
5.2.3.2 Operatori binari Operatorii binari au întotdeauna doi operanzi. Operatorii binari sunt urmãtorii: ● ● ● ● ● ● ●
Operatori multiplicativi: *, /, % Operatori aditivi: +, -, + (concatenare) pentru ºiruri de caractere Operatori de ºiftare: >>, > Operatori relaþionali: , =, instanceof Operatori de egalitate: ==, != Operatori la nivel de bit: &, |, ^ Operatori logici: &&, ||
5.2.3.2.1 Operatori multiplicativi: *, /, %
Operatorii multiplicativi reprezintã respectiv operaþiile de înmulþire (*), împãrþire (/) ºi restul împãrþirii (%). Prioritatea acestor operaþii este mai mare relativ la operaþiile aditive, deci aceºti operatori se vor executa mai întâi. Exemple: 10 * 5 == 50 10.3 * 5.0 == 51.5 10 / 2.5 == 4.0// împãrþire realã 3 / 2 == 1// împãrþire întreagã 7 % 2 == 1// restul împãrþirii întregi 123.5 % 4 == 3.5 // 4 * 30 + 3.5 123.5 % 4.5 == 2.0 // 4.5 * 27 + 2.0 Dupã cum observaþi, operanzii sunt convertiþi mai întâi la tipul cel mai puternic, prin promovare aritmeticã, ºi apoi se executã operaþia. Rezultatul este de acelaºi tip cu tipul cel mai puternic. În cazul operatorului pentru restul împãrþirii, dacã lucrãm cu numere flotante, rezultatul se calculeazã în felul urmãtor: se calculeazã de câte ori este cuprins cel de-al doilea operand în primul (un numãr întreg de ori) dupã care rezultatul este diferenþa care mai rãmâne, întotdeauna mai micã strict decât al doilea operand. 5.2.3.2.2 Operatori aditivi: +, -, concatenare pentru ºiruri de caractere
Operatorii aditivi reprezintã operaþiile de adunare (+), scãdere (-) ºi concatenare (+) de ºiruri. Observaþiile despre conversia tipurilor fãcute la operatorii multiplicativi rãmân valabile. Exemple: 2 + 3 == 5 2.34 + 3 == 5.34 34.5 - 23.1 == 11.4 file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap52.html (6 of 13)12.01.2006 23:07:54
Capitolul V -- 2. Expresii
"Acesta este" + " un sir" == "Acesta este un sir" "Sirul: " + 1 == "Sirul: 1" "Sirul: " + 3.4444 == "Sirul: 3.4444" "Sirul: " + null = "Sirul: null" "Sirul: " + true = "Sirul: true" Object obiect = new Object(); "Sirul: " + obiect == "java.lang.Object@1393800" La concatenarea ºirurilor de caractere, lungimea ºirului rezultat este suma lungimii ºirurilor care intrã în operaþie. Caracterele din ºirul rezultat sunt caracterele din primul ºir, urmate de cele dintr-al doilea ºir în ordine. Dacã cel de-al doilea operand nu este de tip String ci este de tip referinþã, se va apela metoda sa toString, ºi apoi se va folosi în operaþie rezultatul. Metoda toString este definitã în clasa Object ºi este moºtenitã de toate celelalte clase. Dacã cel de-al doilea operand este un tip primitiv, acesta este convertit la un ºir rezonabil de caractere care sã reprezinte valoarea operandului. 5.2.3.2.3 Operatori de ºiftare: >>, >
Operatorii de ºiftare se pot aplica doar pe valori primitive întregi. Ei reprezintã respectiv operaþiile de ºiftare cu semn stânga () ºi operaþia de ºiftare fãrã semn spre dreapta (>>>). ªiftãrile cu semn lucreazã la nivel de cifre binare. Cifrele binare din locaþia de memorie implicatã sunt mutate cu mai multe poziþii spre stânga sau spre dreapta. Poziþia binarã care reprezintã semnul rãmâne neschimbatã. Numãrul de poziþii cu care se efectueazã mutarea este dat de al doilea operand. Locaþia de memorie în care se executã operaþia este locaþia în care este memorat primul operand. ªiftarea cu semn la stânga reprezintã o operaþie identicã cu înmulþirea cu 2 de n ori, unde n este al doilea operand. ªiftarea cu semn la dreapta reprezintã împãrþirea întreagã. În acest caz, semnul este copiat în mod repetat în locurile rãmase goale. Iatã câteva exemple: 255 > 5 == // 00000000
2040 11111111 -> 00000111 11111000 7 11111111 -> 00000000 00000111
ªiftarea fãrã semn la dreapta, mutã cifrele binare din operand completând spaþiul rãmas cu zerouri: 0xffffffff >>> -1 == 0x00000001 0xffffffff >>> -2 == 0x00000003
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap52.html (7 of 13)12.01.2006 23:07:54
Capitolul V -- 2. Expresii
0xffffffff >>> -3 == 0x00000007 0xffffffff >>> 3 == 0x1fffffff 0xffffffff >>> 5 == 0x07ffffff ^ 5.2.3.2.4 Operatori relaþionali: , =, instanceof
Operatorii relaþionali întorc valori booleene de adevãrat sau fals. Ei reprezintã testele de mai mic (), mai mic sau egal (=) ºi testul care ne spune dacã un anumit obiect este sau nu instanþã a unei anumite clase (instanceof). Iatã câteva exemple: 1 < 345 == true 1 >>=,
&=, |=, ^= ^
5.3.2.1.2 Atribuiri implicite
Sã mai observãm cã, în cazul folosirii operatorilor de incrementare ºi decrementare se face ºi o atribuire implicitã pentru cã pe lângã valoarea rezultatã în urma operaþiei, se modificã ºi valoarea memoratã la locaþia pe care se aplicã operatorul. La fel ca ºi la operaþiile mixte de atribuire, calculul locaþiei asupra cãreia se aplicã operatorul se efectueazã o singurã datã. 5.3.2.2 Instrucþiuni etichetate Unele dintre instrucþiunile din interiorul unui bloc de instrucþiuni trebuie referite din altã parte a programului pentru a se putea direcþiona execuþia spre aceste instrucþiuni. Pentru referirea acestor instrucþiuni, este necesar ca ele sã aibã o etichetã asociatã. O etichetã este deci un nume dat unei instrucþiuni din program prin care instrucþiunea respectivã poate fi referitã din alte pãrþi ale programului. Eticheta cea mai simplã este un simplu identificator urmat de caracterul : ºi de instrucþiunea pe care dorim sã o etichetãm: Etichetã: Instrucþiune ca în exemplul urmãtor: int a = 5; Eticheta: a = 3; Pentru instrucþiunea switch, descrisã mai jos, sunt definite douã moduri diferite de etichetare. Acestea au forma:
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (7 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
case Valoare: Instrucþiune default: Instrucþiune Exemple pentru folosirea acestor forme sunt date la definirea instrucþiunii. 5.3.2.3 Instrucþiuni condiþionale Instrucþiunile condiþionale sunt instrucþiuni care selecteazã pentru execuþie o instrucþiune dintr-un set de instrucþiuni în funcþie de o anumitã condiþie. 5.3.2.3.1 Instrucþiunea if
Instrucþiunea if primeºte o expresie a cãrei valoare este obligatoriu de tipul boolean. Evaluarea acestei expresii poate duce la doar douã valori: adevãrat sau fals. În funcþie de valoarea rezultatã din aceastã evaluare, se executã unul din douã seturi de instrucþiuni distincte, specificate de instrucþiunea if. Sintaxa acestei instrucþiuni este urmãtoarea: if( Expresie ) Instrucþiune1 [else Instrucþiune2] Dupã evaluarea expresiei booleene, dacã valoarea rezultatã este true, se executã instrucþiunea 1. Restul instrucþiunii este opþional, cu alte cuvinte partea care începe cu cuvântul rezervat else poate sã lipseascã. În cazul în care aceastã parte nu lipseºte, dacã rezultatul evaluãrii expresiei este false se va executa instrucþiunea 2. Indiferent de instrucþiunea care a fost executatã în interiorul unui if, dupã terminarea acesteia execuþia continuã cu instrucþiunea de dupã instrucþiunea if, în afarã de cazul în care instrucþiunile executate conþin în interior o instrucþiune de salt. Sã mai observãm cã este posibil ca într-o instrucþiune if sã nu se execute nici o instrucþiune în afarã de evaluarea expresiei în cazul în care expresia este falsã iar partea else din instrucþiunea if lipseºte. Expresia booleanã va fi întotdeauna evaluatã. Iatã un exemplu de instrucþiune if în care partea else lipseºte: int x = 3; if( x == 3 ) x *= 7;
file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (8 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
ºi iatã un exemplu în care sunt prezente ambele pãrþi: int x = 5; if( x % 2 == 0 ) x = 100; else x = 1000; Instrucþiunea 1 nu poate lipsi niciodatã. Dacã totuºi pe ramura de adevãr a instrucþiunii condiþionale nu dorim sã executãm nimic, putem folosi o instrucþiune vidã, dupã cum este arãtat puþin mai departe. În cazul în care dorim sã executãm mai multe instrucþiuni pe una dintre ramurile instrucþiunii if, putem sã în locuim instrucþiunea 1 sau 2 sau pe amândouã cu blocuri de instrucþiuni, ca în exemplul urmãtor: int x = 5; if( x == 0 ) { x = 3; y = x * 5; } else { x = 5; y = x * 7; } Expresia booleanã poate sã fie ºi o expresie compusã de forma: int int if( x =
x = 3; y = 5; y != 0 && x / y == 2 ) 4;
În acest caz, aºa cum deja s-a specificat la descrierea operatorului &&, evaluarea expresiei nu este terminatã în toate situaþiile. În exemplul nostru, dacã y este egal cu 0, evaluarea expresiei este opritã ºi rezultatul este fals. Aceastã comportare este corectã pentru cã, dacã un termen al unei operaþii logice de conjuncþie este fals, atunci întreaga conjuncþie este falsã. În plus, acest mod de execuþie ne permite sã evitãm unele operaþii cu rezultat incert, în cazul nostru o împãrþire prin 0. Pentru mai multe informaþii relative la operatorii && ºi || citiþi secþiunea destinatã operatorilor. 5.3.2.3.2 Instrucþiunea switch
Instrucþiunea switch ne permite saltul la o anumitã instrucþiune etichetatã în funcþie de valoarea unei file:///C|/Documents%20and%20Settings/Luminita/Deskt...%20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (9 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
expresii. Putem sã specificãm câte o etichetã pentru fiecare valoare particularã a expresiei pe care dorim sã o diferenþiem. În plus, putem specifica o etichetã la care sã se facã saltul implicit, dacã expresia nu ia nici una dintre valorile particulare specificate. Sintaxa instrucþiunii este: switch( Expresie ) { [case ValoareParticularã: Instrucþiuni;]* [default: InstrucþiuniImplicite;] } Execuþia unei instrucþiuni switch începe întotdeauna prin evaluarea expresiei dintre parantezele rotunde. Aceastã expresie trebuie sã aibã tipul caracter, octet, întreg scurt sau întreg. Dupã evaluarea expresiei se trece la compararea valorii rezultate cu valorile particulare specificate în etichetele case din interiorul blocului de instrucþiuni. Dacã una dintre valorile particulare este egalã cu valoarea expresiei, se executã instrucþiunile începând de la eticheta case corespunzãtoare acelei valori în jos, pânã la capãtul blocului. Dacã nici una dintre valorile particulare specificate nu este egalã cu valoarea expresiei, se executã instrucþiunile care încep cu eticheta default, dacã aceasta existã. Iatã un exemplu de instrucþiune switch: int x = 4; … int y = 0; switch( x + 1 ) { case 3: x += 2; y++; case 5: x = 11; y++; default: x = 4; y += 3; } Dacã valoarea lui x în timpul evaluãrii expresiei este 2 atunci expresia va avea valoarea 3 ºi instrucþiunile vor fi executate una dupã alta începând cu cea etichetatã cu case 3. În ordine, x va deveni 4, y va deveni 1, x va deveni 11, y va deveni 2, x va deveni 4 ºi y va deveni 5.
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (10 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
Dacã valoarea lui x în timpul evaluãrii expresiei este 4 atunci expresia va avea valoarea 5 ºi instrucþiunile vor fi executate pornind de la cea etichetatã cu case 5. În ordine, x va deveni 11, y va deveni 1, x va deveni 4 ºi y va deveni 4. În fine, dacã valoarea lui x în timpul evaluãrii expresiei este diferitã de 2 ºi 4, se vor executa instrucþiunile începând cu cea etichetatã cu default. În ordine, x va deveni 4 ºi y va deveni 3. Eticheta default poate lipsi, caz în care, dacã nici una dintre valorile particulare nu este egalã cu valoarea expresiei, nu se va executa nici o instrucþiune din bloc. În cele mai multe cazuri, aceastã comportare a instrucþiunii switch nu ne convine, din cauza faptului cã instrucþiunile de dupã cea etichetatã cu case 5 se vor executa ºi dacã valoarea este 3. La fel, instrucþiunile de dupã cea etichetatã cu default se executã întotdeauna. Pentru a schimba aceastã comportare trebuie sã folosim una dintre instrucþiunile de salt care sã opreascã execuþia instrucþiunilor din bloc înainte de întâlnirea unei noi instrucþiuni etichetate. Putem de exemplu folosi instrucþiunea de salt break care va opri execuþia instrucþiunilor din blocul switch. De exemplu: char c = '\t'; … String mesaj = "nimic"; switch( c ) { case '\t': mesaj = "tab"; break; case '\n': mesaj = "linie noua"; break; case '\r': mesaj = "retur"; default: mesaj = mesaj + " de"; mesaj = mesaj + " car"; } În acest caz, dacã c este egal cu caracterul tab, la terminarea instrucþiunii switch, mesaj va avea valoarea "tab". În cazul în care c are valoarea CR, mesaj va deveni mai întâi "retur" apoi "retur de" ºi apoi "retur de car". Lipsa lui break dupã instrucþiunea mesaj = "retur";
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (11 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
face ca în continuare sã fie executate ºi instrucþiunile de dupã cea etichetatã cu default. 5.3.2.4 Instrucþiuni de ciclare Instrucþiunile de ciclare (sau ciclurile, sau buclele) sunt necesare atunci când dorim sã executãm de mai multe ori aceeaºi instrucþiune sau acelaºi bloc de instrucþiuni. Necesitatea acestui lucru este evidentã dacã ne gândim cã programele trebuie sã poatã reprezenta acþiuni de forma: executã 10 întoarceri, executã 7 genoflexiuni, executã flotãri pânã ai obosit, etc. Desigur, sintaxa instrucþiunilor care se executã trebuie sã fie aceeaºi, pentru cã ele vor fi în realitate scrise în Java o singurã datã. Totuºi, instrucþiunile nu sunt neapãrat aceleaºi. De exemplu, dacã executãm în mod repetat instrucþiunea: int tablou[] = new int[10]; int i = 0; tablou[i++] = 0; în realitate se va memora valoarea 0 în locaþii diferite pentru cã variabila care participã la calculul locaþiei îºi modificã la fiecare iteraþie valoarea. La primul pas, se va face 0 primul element din tablou ºi în acelaºi timp i va primi valoarea 1. La al doilea pas, se va face 0 al doilea element din tablou ºi i va primi valoarea 2, ºi aºa mai departe. Lucrurile par ºi mai clare dacã ne gândim cã instrucþiunea executatã în mod repetat poate fi o instrucþiune if. În acest caz, În funcþie de expresia condiþionalã din if se poate executa o ramurã sau alta a instrucþiunii. De exemplu instrucþiunea din buclã poate fi: int i = 0; if( i++ % 2 == 0 ) … else … În acest caz, i este când par când impar ºi se executã alternativ cele douã ramuri din if. Desigur, comportarea descrisã este valabilã dacã valoarea lui i nu este modificatã în interiorul uneia dintre ramuri.
5.3.2.4.1 Instrucþiunea while
Aceastã instrucþiune de buclare se foloseºte atunci când vrem sã executãm o instrucþiune atâta timp cât o anumitã expresie condiþionalã rãmâne adevãratã. Expresia condiþionalã se evalueazã ºi testeazã înainte file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (12 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
de execuþia instrucþiunii, astfel cã, dacã expresia era de la început falsã, instrucþiunea nu se mai executa niciodatã. Sintaxa acestei instrucþiuni este: while( Test ) Corp Test este o expresie booleanã iar Corp este o instrucþiune normalã, eventual vidã. Dacã avem nevoie sã repetãm mai multe instrucþiuni, putem înlocui corpul buclei cu un bloc de instrucþiuni. Iatã ºi un exemplu: int i = 0; int tablou[] = new int[20]; while( i < 10 ) tablou[i++] = 1; Bucla while de mai sus se executã de 10 ori primele 10 elemente din tablou fiind iniþializate cu 1. În treacãt fie spus, celelalte rãmân la valoarea 0 care este valoarea implicitã pentru întregi. Dupã cei 10 paºi iterativi, i devine 10 ºi testul devine fals (10 < 10). În exemplul urmãtor, corpul nu se executã nici mãcar o datã: int i = 3; while( i < 3 ) i++; Putem sã creãm un ciclu infinit (care nu se terminã niciodatã) prin: while( true ) ; Întreruperea execuþiei unui ciclu infinit se poate face introducând în corpul ciclului o instrucþiune de salt. 5.3.2.4.2 Instrucþiunea do
Buclele do se folosesc atunci când testul de terminare a buclei trebuie fãcut dupã execuþia corpului buclei. Sintaxa de descriere a instrucþiuni do este: do Corp while( Test ) ;
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (13 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
Test este o expresie booleanã iar Corp este o instrucþiune sau un bloc de instrucþiuni. Execuþia acestei instrucþiuni înseamnã execuþia corpului în mod repetat atâta timp cât expresia Test are valoarea adevãrat. Testul se evalueazã dupã execuþia corpului, deci corpul se executã cel puþin o datã. De exemplu, în instrucþiunea: int i = 3; do i++; while( false ); valoarea finalã a lui i este 4, pentru cã instrucþiunea i++ care formeazã corpul buclei se executã o datã chiar dacã testul este întotdeauna fals. În instrucþiunea: int i = 1; do { tablou[i] = 0; i += 2; } while( i < 5 ); sunt setate pe 0 elementele 1 ºi 3 din tablou. Dupã a doua iteraþie, i devine 5 ºi testul eºueazã cauzând terminarea iteraþiei. 5.3.2.4.3 Instrucþiunea for
Instrucþiunea for se foloseºte atunci când putem identifica foarte clar o parte de iniþializare a buclei, testul de terminare a buclei, o parte de reluare a buclei ºi un corp pentru buclã. În acest caz, putem folosi sintaxa: for( Iniþializare Test ; Reluare ) Corp Corp ºi Iniþializare sunt instrucþiuni normale. Test este o expresie booleanã iar Reluare este o instrucþiune cãreia îi lipseºte caracterul ; final. Execuþia unei bucle for începe cu execuþia instrucþiunii de iniþializare. Aceastã instrucþiune stabileºte de obicei niºte valori pentru variabilele care controleazã bucla. Putem chiar declara aici noi variabile. Aceste variabile existã doar în interiorul corpului buclei ºi în instrucþiunile de test ºi reluare ale buclei. În partea de iniþializare nu putem scrie decât o singurã instrucþiune fie ea declaraþie sau instrucþiune normalã. În acest caz instrucþiunea nu se poate înlocui cu un bloc de instrucþiuni. Putem însã sã file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (14 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
declarãm douã variabile cu o sintaxã de forma: int i = 0, j = 1; Dupã execuþia pãrþii de iniþializare se porneºte bucla propriu-zisã. Aceasta constã din trei instrucþiuni diferite executate în mod repetat. Cele trei instrucþiuni sunt testul, corpul buclei ºi instrucþiunea de reluare. Testul trebuie sã fie o expresie booleanã. Dacã aceasta este evaluatã la valoarea adevãrat, bucla continuã cu execuþia corpului, a instrucþiunii de reluare ºi din nou a testului. În clipa în care testul are valoarea fals, bucla este opritã fãrã sã mai fie executat corpul sau reluarea. Iatã un exemplu: int x = 0; for( int i = 3; i < 30; i += 10 ) x += i; În aceastã buclã se executã mai întâi crearea variabilei i ºi iniþializarea acesteia cu 3. Dupã aceea se testeazã variabila i dacã are o valoare mai micã decât 30. Testul are rezultat adevãrat (i este 0 < 30) ºi se trece la execuþia corpului unde x primeºte valoarea 3 (0 + 3). În continuare se executã partea de reluare în care i este crescut cu 10, devenind 13. Se terminã astfel primul pas al buclei ºi aceasta este reluatã începând cu testul care este în continuare adevãrat (13 < 30). Se executã corpul, x devenind 16 (3 + 13), ºi reluarea, unde x devine 23 (13 + 10). Se reia bucla de la test care este în continuare adevãrat (23 < 30). Se executã corpul unde x devine 39 (16 + 23) ºi reluarea unde i devine 33 (23 + 10). Se reia testul care în acest caz devine fals (33 < 30) ºi se pãrãseºte bucla, continuându-se execuþia cu prima instrucþiune de dupã buclã. Dupã ieºirea din buclã, variabila i nu mai existã, deci nu se mai poate folosi ºi nu putem vorbi despre valoarea cu care iese din buclã, iar variabila x rãmâne cu valoarea 39. Pentru a putea declara variabila i în instrucþiunea de iniþializare a buclei for este necesar ca în blocurile superioare instrucþiunii for sã nu existe o altã variabilã i, sã nu existe un parametru numit i ºi nici o etichetã cu acest nume. Dacã dorim un corp care sã conþinã mai multe instrucþiuni, putem folosi un bloc. Nu putem face acelaºi lucru în partea de iniþializare sau în partea de reluare. Oricare dintre pãrþile buclei for în afarã de iniþializare poate sã lipseascã. Dacã aceste pãrþi lipsesc, se considerã cã ele sunt reprezentate de instrucþiunea vidã. Dacã nu dorim iniþializare trebuie totuºi sã specificãm implicit instrucþiunea vidã. Putem de exemplu sã scriem o buclã infinitã prin: int x; for( ;; )
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (15 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
x = 0; Putem specifica funcþionarea instrucþiunii for folosindu-ne de o instrucþiune while în felul urmãtor: Iniþializare while( Test ) { Corp Reluare ; } În fine, schema urmãtoare reprezintã funcþionarea unei bucle for:
Figura 5.6 Schema de funcþionare a buclei for.
5.3.2.5 Instrucþiuni de salt Instrucþiunile de salt provoacã întreruperea forþatã a unei bucle, a unui bloc de instrucþiuni sau ieºirea dintr-o metodã. Instrucþiunile de salt sunt destul de periculoase pentru cã perturbã curgerea uniformã a programului ceea ce poate duce la o citire ºi înþelegere eronatã a codului rezultat. 5.3.2.5.1 Instrucþiunea break
Instrucþiunea break produce întreruperea unei bucle sau a unui bloc switch. Controlul este dat instrucþiunii care urmeazã imediat dupã bucla întreruptã sau dupã blocul instrucþiunii switch. file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (16 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
De exemplu în secvenþa: int i = 1, j = 3; int tablou[] = new int[10]; while( i < 10 ) { tablou[i++] = 0; if( i == j ) break; } i += 2; sunt setate pe 0 elementele 1 ºi 2 ale tabloului. Dupã a doua iteraþie i devine 3 ºi testul i == j are valoarea adevãrat, producându-se execuþia instrucþiunii break. Instrucþiunea break cauzeazã ieºirea forþatã din buclã, chiar dacã testul buclei i < 10 este în continuare valid. La terminarea tuturor instrucþiunilor de mai sus, i are valoarea 5. În exemplul urmãtor: int i, j = 3; int tablou[] = new tablou[10]; do { tablou[i] = 0; if( i++ >= j ) break; } while( i < 10 ); sunt setate pe 0 elementele 0, 1, 2 ºi 3 din tablou. Dacã vã întrebaþi la ce mai foloseºte testul buclei, atunci sã spunem cã este o mãsurã suplimentarã de siguranþã. Dacã cumva j intrã cu o valoare greºitã, testul ne asigurã în continuare cã nu vom încerca sã setãm un element care nu existã al tabloului. De fapt, pericolul existã încã, dacã valoarea iniþialã a lui i este greºitã, deci testul ar trebui mutat la început ºi bucla transformatã într-un while, ca cel de mai sus. În cazul buclelor for, sã mai precizãm faptul cã la o ieºire forþatã nu se mai executã instrucþiunea de reluare. Instrucþiunea break poate avea ca argument opþional o etichetã, ca în: break Identificator; În acest caz, identificatorul trebuie sã fie eticheta unei instrucþiuni care sã includã instrucþiunea break. file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (17 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
Prin faptul cã instrucþiunea include instrucþiunea break, înþelegem cã instrucþiunea break apare în corpul instrucþiunii care o include sau în corpul unei instrucþiuni care se gãseºte în interiorul corpului instrucþiunii care include instrucþiunea break. Controlul revine instrucþiunii de dupã instrucþiunea etichetatã cu identificatorul specificat. De exemplu, în instrucþiunile: int i, j; asta: while( i < 3 ) { do { i = j + 1; if( i == 3 ) break asta; } while( j++ < 3 ); } j = 10; instrucþiunea break terminã bucla do ºi bucla while controlul fiind dat instrucþiunii de dupã while, ºi anume atribuirea: j = 10; 5.3.2.5.2 Instrucþiunea continue
Instrucþiunea continue permite reluarea unei bucle fãrã a mai termina execuþia corpului acesteia. Reluarea se face ca ºi cum corpul buclei tocmai a fost terminat de executat. Bucla nu este pãrãsitã. De exemplu, în instrucþiunea: int i; while( i < 10 ) { i++; continue; i++; } corpul buclei se executã de 10 ori, pentru cã a doua incrementare a lui i nu se executã niciodatã. Execuþia începe cu testul ºi apoi urmeazã cu prima incrementare. Dupã aceasta se executã instrucþiunea continue care duce la reluarea buclei, pornind de la test ºi urmând cu incrementarea ºi din nou instrucþiunea continue. În exemplul urmãtor: file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (18 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
int i; do { i++; continue; i++; } while( i < 10 ); corpul se executã de 10 ori la fel ca ºi mai sus. Instrucþiunea continue duce la evitarea celei de-a doua incrementãri, dar nu ºi la evitarea testului de sfârºit de buclã. În sfârºit, în exemplul urmãtor: for( int i = 0; i < 10; i++ ) { continue; i++; } corpul se executã tot de 10 ori, ceea ce înseamnã cã reluarea buclei duce la execuþia instrucþiunii de reluare a buclei for ºi apoi a testului. Doar ceea ce este în interiorul corpului este evitat. Instrucþiunea continue poate avea, la fel ca ºi instrucþiunea break, un identificator opþional care specificã eticheta buclei care trebuie continuatã. Dacã existã mai multe bucle imbricate una în cealaltã buclele interioare celei referite de etichetã sunt abandonate. De exemplu, în secvenþa: asta: for( int i, j = 1; i < 10; i++ ) { while( j < 5 ) { j++; if( j % 2 == 0 ) continue asta; } } instrucþiunea continue provoacã abandonarea buclei while ºi reluarea buclei for cu partea de reluare ºi apoi testul. 5.3.2.5.3 Instrucþiunea return
Instrucþiunea return provoacã pãrãsirea corpului unei metode. În cazul în care return este urmatã de o expresie, valoarea expresiei este folositã ca valoare de retur a metodei. Aceastã valoare poate fi eventual convertitã cãtre tipul de valoare de retur declarat al metodei, dacã acest lucru este posibil. Dacã file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (19 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
nu este posibil, va fi semnalatã o eroare de compilare. Este o eroare de compilare specificarea unei valori de retur într-o instrucþiune return din interiorul unei metode care este declaratã void, cu alte cuvinte care nu întoarce nici o valoare. Instrucþiunea return fãrã valoare de retur poate fi folositã ºi pentru a pãrãsi execuþia unui iniþializator static. Exemple de instrucþiuni return veþi gãsi în secþiunea care trateazã metodele unei clase de obiecte. 5.3.2.5.4 Instrucþiunea throw
Instrucþiunea throw este folositã pentru a semnaliza o excepþie de execuþie. Aceastã instrucþiune trebuie sã aibã un argument ºi acesta trebuie sã fie un tip obiect, de obicei dintr-o subclasã a clasei de obiecte Exception. La execuþia instrucþiunii throw, fluxul normal de execuþie este pãrãsit ºi se terminã toate instrucþiunile în curs pânã la prima instrucþiune try care specificã într-o clauzã catch un argument formal de acelaºi tip cu obiectul aruncat sau o superclasã a acestuia. 5.3.2.6 Instrucþiuni de protecþie Aceste instrucþiuni sunt necesare pentru tratarea erorilor ºi a excepþiilor precum ºi pentru sincronizarea unor secvenþe de cod care nu pot rula în paralel. 5.3.2.6.1 Instrucþiunea try
Instrucþiunea try iniþiazã un context de tratare a excepþiilor. În orice punct ulterior iniþializãrii acestui context ºi înainte de terminarea acestuia, o excepþie semnalatã prin execuþia unei instrucþiuni throw va returna controlul la nivelul instrucþiunii try, abandonându-se în totalitate restul instrucþiunilor din corpul acestuia. La semnalarea unei excepþii aceasta poate fi prinsã de o clauzã catch ºi, în funcþie de tipul obiectului aruncat, se pot executa unele instrucþiuni care repun programul într-o stare stabilã. De obicei, o excepþie este generatã atunci când s-a produs o eroare majorã ºi continuarea instrucþiunilor din contextul curent nu mai are sens. În finalul instrucþiunii, se poate specifica ºi un bloc de instrucþiuni care se executã imediat dupã blocul try ºi blocurile catch indiferent cum s-a terminat execuþia acestora. Pentru specificarea acestor instrucþiuni, trebuie folositã o clauzã finally. Iatã sintaxa unei instrucþiuni try: file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (20 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
try Bloc1 [catch( Argument ) Bloc2]*[finally Bloc3] Dacã, undeva în interiorul blocului 1 sau în metodele apelate din interiorul acestuia, pe oricâte nivele, este apelatã o instrucþiune throw, execuþia blocului ºi a metodelor în curs este abandonatã ºi se revine în instrucþiunea try. În continuare, obiectul aruncat de throw este comparat cu argumentele specificate în clauzele catch. Dacã unul dintre aceste argumente este instanþã a aceleiaºi clase sau a unei superclase a clasei obiectului aruncat, se executã blocul de instrucþiuni corespunzãtor clauzei catch respective. Dacã nici una dintre clauzele catch nu se potriveºte, obiectul este aruncat mai departe. Dacã excepþia nu este nicãieri prinsã în program, acesta se terminã cu o eroare de execuþie. Indiferent dacã a apãrut o excepþie sau nu, indiferent dacã s-a executat blocul unei clauze catch sau nu, în finalul execuþiei instrucþiunii try se executã blocul specificat în clauza finally, dacã aceasta existã. Clauza finally se executã chiar ºi dacã în interiorul blocului 1 s-a executat o instrucþiune throw care a aruncat un obiect care nu poate fi prins de clauzele catch ale acestei instrucþiuni try. În astfel de situaþii, execuþia instrucþiunii throw se opreºte temporar, se executã blocul finally ºi apoi se aruncã mai departe excepþia. Exemple de utilizare a instrucþiunii try gãsiþi în paragraful 9.2 5.3.2.6.2 Instrucþiunea synchronized
Instrucþiunea synchronized introduce o secvenþã de instrucþiuni criticã. O secvenþã criticã de instrucþiuni trebuie executatã în aºa fel încât nici o altã parte a programului sã nu poatã afecta obiectul cu care lucreazã secvenþa datã. Secvenþele critice apar de obicei atunci când mai multe pãrþi ale programului încearcã sã acceseze în acelaºi timp aceleaºi resurse. Gândiþi-vã, de exemplu, ce s-ar întâmpla dacã mai multe pãrþi ale programului ar încerca sã incrementeze în acelaºi timp valoarea unei variabile. Una dintre ele ar citi vechea valoare a variabilei, sã spunem 5, ar incrementa-o la 6 ºi, când sã o scrie înapoi, sã presupunem cã ar fi întreruptã de o altã parte a programului care ar incrementa-o la 6. La revenirea în prima parte, aceasta ar termina prima incrementare prin scrierea valorii 6 înapoi în variabilã. Valoarea finalã a variabilei ar fi 6 în loc sã fie 7 aºa cum ne-am aºtepta dacã cele douã incrementãri s-ar face pe rând. Spunem cã cele douã regiuni în care se face incrementarea aceleiaºi variabile sunt regiuni critice. Înainte ca una dintre ele sã se execute, ar trebui sã ne asigurãm cã cealaltã regiune criticã nu ruleazã deja. Cea mai simplã cale de a face acest lucru este sã punem o condiþie de blocare chiar pe variabila incrementatã. Ori de câte ori o regiune criticã va încerca sã lucreze, va verifica dacã variabila noastrã este liberã sau nu.
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (21 of 22)12.01.2006 23:07:56
Capitolul V -- 3. Instrucþiuni
Instrucþiunea synchronized îºi blocheazã obiectul pe care îl primeºte ca parametru ºi apoi executã secvenþa criticã. La sfârºitul acesteia obiectul este deblocat înapoi. Dacã instrucþiunea nu poate bloca imediat obiectul pentru cã acesta este blocat de o altã instrucþiune, aºteaptã pânã când obiectul este deblocat. Mai mult despre aceastã instrucþiune precum ºi exemple de utilizare veþi gãsi în partea a treia, capitolul 9. Pânã atunci, iatã sintaxa generalã a acestei instrucþiuni: synchronized ( Expresie ) Instrucþiune Expresia trebuie sã aibã ca valoare o referinþã cãtre un obiect sau un tablou care va servi drept dispozitiv de blocare. Instrucþiunea poate fi o instrucþiune simplã sau un bloc de instrucþiuni. 5.3.2.7 Instrucþiunea vidã Instrucþiunea vidã este o instrucþiune care nu executã nimic. Ea este folositã uneori, atunci când este obligatoriu sã avem o instrucþiune, dar nu dorim sã executãm nimic în acea instrucþiune. De exemplu, în cazul unei instrucþiuni if, este obligatoriu sã avem o instrucþiune pe ramura de adevãr. Dacã însã nu dorim sã executãm nimic acolo, putem folosi un bloc vid sau o instrucþiune vidã. Sintaxa pentru o instrucþiune vidã este urmãtoarea: ; Iatã ºi un exemplu: int x = 3; if( x == 5 ) ; else x = 5; Caracterul ; care apare dupã condiþia din if reprezintã o instrucþiune vidã care specificã faptul cã, în cazul în care x are valoarea 5 nu trebuie sã se execute nimic. [capitolul V] [cuprins] (C) IntegraSoft 1996-1998
file:///C|/Documents%20and%20Settings/Luminita/Desk...20in%20romana/Carte%20JAVA%20in%20romana/cap53.html (22 of 22)12.01.2006 23:07:56