1. Mediul Java pentru Internet 1.1. Descrierea succintã a mediului 1.1.1.Ce este Java? Java este un limbaj de programare orientat obiect, asemãnator cu C++, dar cu un alt conţinut important ceea ce il face un instrument foarte robust pentru dezvoltarea de aplicaţii WWW. Menirea limbajului Java este de a fi un standard pentru transferul de conţinut executabil pe Internet prin WWW. Datorită limbajului Java se schimbă natură pasivă a reţelei Internet, deoarece un program binar portabil generat de compilatorul Java, poate fi încarcat şi executat în mod interpretativ pe orice calculator legat la Internet. Proiectarea orientată pe obiecte este un instrument puternic, deoarece facilitează definirea clară a interfeţelor şi permite reutilizarea unor module de program identificate ca şi obiecte. Fiind conceput ca un limbaj obiectual, tot ce există într-un program este incapsulat în obiecte, care în Java poartă denumirea de clase, limbajul permiţâd moştenirea şi extinderea unei clase şi reutilizarea oricărui program sau modul în alte programe. O clasa Java este o colecţie de variabile şi de metode, care încapsulează o anumită funcţionalitate, într-un obiect reutilizabil şi încărcabil în mod dinamic. Mediul Java încarcã în mod dinamic clasele, ceea ce înseamnă ca un program Java care se execută poate adăuga funcţionalităţi noi în mod dinamic, prin încãrcarea claselor necesare la un moment dat. Portabilitatea programelor este un deziderat major, care nu se poate realiza numai printr-o execuţie interpretativa şi în acest sens în mediul Java pentru Internet un program sursă este compilat mai intâi în cod binar portabil (codul binar), care este apoi adaptat cerinţelor codului nativ al calculatorului pe care se execută. Aceast mecanism este o maşină Java virtuală (JVM), care la rândul ei este implementată pe orice calculator ca şi interpretorul Java. Portabilitatea este asigurată şi prin standardizarea tipurilor fundamentale.[LEMA-96] Robusteţea mediului permite scrierea unor programe suple şi puternice, care funcţionează corect astfel încât totul se decide dinamic, în timpul execuţiei, pentru a evita conflicte. Gestiunea memoriei se realizează automat, nu prin instrucţiuni scrise în programul sursă ci printr-un sistem complex implementat astfel încât să gestioneze clasele şi obiectele utilizate, interfeţele necesare şi să elibereze în mod dinamic spaţiul de memorie neutilizat. Avand în vedere ca mediul Java este un mediu de programare şi utilizare distribuit pe Internet, care este implementat pe principiile tehnologiei Corba, se impun metode de protecţie atât pentru sistemele clienţilor, cât şi pentru servere. Java permite crearea unor sisteme protejate, realizate prin:[An-97] Eliminarea pointerilor ca punctele slabe ale securităţii sistemelor împreună cu aritmetica pointerilor, recunoscută ca o cauză importantă ce determină distrugeri accidentale ale sistemelor. Gestiunea automată a memoriei prin alocarea spaţiului la crearea un obiect şi urmărirea pe parcursul execuţiei appletului până în momentul în care acesta nu mai este referit, caz în care memoria este dezalocată automat. In opoziţie, gestiunea memoriei in C şi C++ este realizată explicit de programatori cu ajutorul instrucţiunilor malloc şi free, precum şi cu alte funcţii standard de gestiune a memoriei.
1
Alocarea dinamicã a memoriei este realizată automat în timpul execuţiei pentru clasele create, eliminând alocarea statică a memoriei. Gestionarea spaţiului alocat separat pentru fiecare clasă este soluţionată de interpretorul Java care oferă spaţiu separat de variabile pentru clasele create. Verificarea codului binar înainte de execuţie de către interpretorul Java, care testeazã anumite convenţii ale limbajului şi ale mediului Java. Astfel se verifică dacă nu se va execută o acţiune daunatoare pentru sistem: violarea restrictiilor de acces, efectuarea unor conversii ilegale de date, depãşirea superioară sau inferioară a stivei, etc. Reţea, clurul cu protocoale TCP/IP (Transmission Control , UDP(User Datagram protocol), IP (Internet Protocol) Internaţionalizare prin adaptarea programelor la setări locale de limbă şi fus orar. Securitate la diverse niveluri, gestiunea cheilor publice şi private, restricţii selective de acces, control şi securitate,etc. Componente software reutilizabile -JavaBeans integrabile în arhitectura existentă. Serializare/ deserializare de obiecte, care permite persistenţa şi comunicaţia între obiecte la distanţă folosind Remote Method Invocation (RMI) Acces la baze de date relaţionale, de diverse tipuri, folosint Java Database Connectivity (JDBC) Grafică bi/tridimensională, animaţie, etc. Performanţele mediului sunt date şi de codul binar portabil rezultat în urma compilãrii, care este adaptat în timpul execuţiei la codul nativ al calculatorului real pe care se rulează aplicaţia. In anumite situaţii, compilarea este amânată pană în momentul execuţiei. Java este un sistem dinamic extensibil, care poate achizitiona fişiere din orice punct al retelei Internet. Fiind orientat pe obiecte, extensibilitatea dinamică se răsfrânge şi asupră obiectelor, caz în care un program poate achizitiona în mod dinamic, din orice punct al reţelei, clasele de care are nevoie în timpul execuţiei. In Java legarea variabilelor şi a metodelor se face tarziu (late binding), chiar în momentul execuţiei iar interpretorul este cel care rezolvă referinţele simbolice şi determină alocarea dinamică a memoriei. Regăsirea variabilelor şi a metodelor se face prin nume, nu prin adrese relative. Orice clasa poate fi modificată in orice moment, fără a fi nevoie să se recompileze toate subclasele sale. Avantajul referinţelor în momentul execuţiei este dat de posibilitatea utilizării dinamice a claselor, care se pot actualiza tot timpul. Limbajul Java a fost proiectat cu scopul de a se realiza un mijloc de transfer a conţinutului executabil, dinamic, pe Internet iar funcţiile de reţea sunt cele care trebuie implementate în mediul Java astfel încât să poată fi exploatat ca un mediu distribuit în Internet. Odată cu paradigma business object, Enterprise1 Java s-a răspândit rapid în lumea calculatoarelor desemnând acele aplicaţii Java dezvoltate pentru intranet-ul unei companii, care foloseşte tehnologii Internet ca:e-mail, ftp, telnet, news, servicii Web,etc. Facilitatea de multifir (multithreading) reprezintă posibilitatea de a incorpora mai multe fire de execuţie, performanţă ce conferă mediului atuuri suplimentare. In timpul execuţiei unui program, timpul în care acesta ocupă unitatea centrală este relativ redus faţă de restul timpului în care 1
Enterprise=întreprindere, companie, organizaţie, firmă, în IT este organizaţie care utilizează sisteme de calcul
2
programul aşteaptă intervenţia utilizatorului, sau este ocupat cu accesul la fişiere sau la reţea. In aplicaţiile cu un singur fir de aşteptare, unitatea centralã rãmâne neocupată intervale lungi de timp. In mediul Java, acest timp este ocupat imediat de alte fire de execuţie, prin care se realizează anumiţe acţiuni specifice.[Naugh-96] Deşi este un limbaj de programare destinat WWW, permite definirea de aplicaţii independente, fiind executate în mod interpretativ. Astfel se pot realiza aplicaţii pentru domenii ca: editoare de texte, foi de calcul, baze de date, proiectare asistată de calculator. Utilizarea aplicaţiilor independente în contextul unui mediu distribuit pe Internet oferă posibilitatea lansării de aplicaţii care se află pe alte calculatoare dar pentru utilizarea applet-urilor java este necesară accesarea unor site-uri Web cu browsere dotate cu interpretoare Java. Pentru programatori, limbajul Java oferă o platformă unică pentru dezvoltarea de aplicaţii - un calculator virtual, maşină virtuală Java şi aplicaţiile care se dezvoltă sau se distribuie pe reteaua Internet. Pentru utilizatori, conţinutul executabil Java asigură integrarea perfectă a datelor cu funcţiile şi interactiunile programelor, precum şi cu lucrul în retea, fără a fi sunt necesare aplicaţii ajutatoare (helper applications) sau fişiere ajutătoare pentru diferite tipuri de protocoale, deoarece Java permite încapsularea datelor şi a metodelor în clase. 1.1.2. Avantajele programãrii în Java Appleturile Java (sau programele interactive) permit abordarea de la nivelul superficial - imagini animate, text în mişcare, etc- la nivelul substanţial - educaţie interactivă continuă, aplicaţii orientate în afaceri. etc. Java este proiectat ca limbaj cross-platform, ceea ce înseamnă că poate rula pe orice maşină, folosind orice sistem de operare. Codul sursă Java este compilat în instrucţiuni pentru o maşină virtuală (un cip imaginar în exenţă simulat soft) iar aceste instrucţiuni sunt interpretate de interpreterul Java, ce translatează codul binar în instrucţiuni în limbaj maşină, care vor fi executate pe calculatorul virtual. Setul de instrucţiuni pentru maşina virtuală şi codul binars în care acestea vor fi convertite sunt întotdeauna aceleaşi, fără a ţine seama de diferenţele date de suportul hardware pe care rulează interpreterul. Interpreterul în sine, pentru a-şi realiza obiectivele, trebuie să cunoască limbajul maşină al dispozitivului hardware specific pe care rulează. De acceea softul scris în Java este independent de platformă, fără a necesita portare sau recompilare, interpreterul Java portând platforma înainte de a rula programul pe calculator. Animaţia este un beneficiu simplu pe care îl aduce Java permiţând paginilor Web să conţină imagini în mişcare. Appleturile Java oferă abilitatea animaţiei astfel încât paginile sunt continuu interactive, pentru a răspunde direct, în timp real, intrărilor utilizatorului. Interactivitatea pe partea clientului este realizată diferit faţă de paginile Web tradiţionale, unde procesul este preluat pe partea serverului. Utilizând un browser disponibil java, pagina Web conţine URL-ul appletului Java. Accesând această pagină, appletul este trimis pe calculatorul propriu, care îl execută. Marea majoritate a prelucrărilor este transferată pe partea clientului, de la serverul la distanţă. Aceasta permite interactivitate, fără întârzierile datorate timpului de răspuns ce apar când informaţia este trimisă înapoi pe Internet. Intrările utilizatorului sunt trimise direct programului responsabil pentru procesarea lor, ceea ce aduce interactivitatea paginilor Web. Dezvoltarea API (Application Programming Interface) oferă programatorului acces la clase, furnizând structuri comune de date, rutine I/O, tabele, fişiere, dispozitive reţea, grafică şi audoi, funcţii matematice, etc. Java API furnizează elemente predefinite (butoane, bare de defilare, casete de dialog, etc.) necesare construcţiei GUI (Graphic User Interface) şi referite în AWT (Abstract Window Toolkit).[java_4] 3
Aplicaţiile de sine stătătoare (standalone) sunt foarte uşor de realizat deoarece Java este un limbaj de programare complet, configurat OO. Când appleturile rulează fără browser disponibil java, aplicaţiile standalone sunt complet autorizate, putând rula pe un calculator, sau preluâd avantajele suportului Internet oferit de Java API. Spre exemplu Sun HotJava este scris complet în limbajul Java. Un alt exemplu este cel în care există o bază de date (BD) centrală pe un calculator şi toţi utilizatorii din firmă au acces la ea, chiar dacă birourile sunt plasate în diverse locuri din oraş sau chiar în oraşe diferite. Practic întregul program front-end care accesează BD este conceput ca aplicaţie client ce nu trebuie portată şi recompilată pentru diverse platforme, avantaj resimţit mai ales la construcţia interfeţei utilizator. Acelaşi program client este compilat o singură dată şi poate rula pe toate platformele, iar funcţiile interfeţei utilizator sunt aceleaşi. Chiar dacă HTML este independent de platformă, apar probleme când se introduce o cerere incompletă sau impropriu formulată în cadrul unui formular. Java oferă posibilitatea de a crea “forme inteligente” (smart form) care verifică legitimitatea cererilor de intrare ale clientului, eliminând consumurile inadecvate de lăţime de bandă din scenariul anterior. Din punct de vedere teoretic nu sunt restricţii cu privire la aplicaţiile de sine stătătoare, respectiv tot ce realizează acestea poate realiza un applet rezident pe pagina Web. Se preferă appleturile deoarece oferă securitate a structurilor construite pe un browser.Java poate utiliza structuri de date gestionate de Security Manager şi indiferent de localizarea calculatorului poate activa sau inhiba programe Java pentru a prelucra operaţii certe: încărcarea automată a unui program sau formă dintr-o locaţie Internet, închiderea/deschiderea unui fişier,etc.Acest lucru este utilizat pentru a preveni încărcarea unui applet infectat (ca şi virulsul Troian Horse), care poate crea prejudicii acestui applet (sau sistemului local de fişiere) ori să faciliteze curiozităţi neautorizate din exterior. Unele caracteristici de securitate sunt accesibile din caseta de dialog a meniului Security Preferences dar nu pot fi modificate de utilizator şi nici nu pot fi alterate de codul încărcat de un host la distanţă. Netscape ca şi alte browsere impune doar limitări extinse la appleturile ce beneficiază de înteaga gama de facilităţi Java, dar este necesară dezvoltarea browserelor pentru a limita extensiile de către acei utilizatori care pot relaxa restricţiile de securitate locală ale appleturilor. Interacţiunea utilizator-utilizator prin reţea poate fi necesară pentru a avea aplicaţii Java de sine stătătoare, care sunt implementate cu politici diferite de securitate pe Netscape, luând măsuri specifice pentru drepturi, pentru a se asigura că nu pot apare breşe de securitate.[An_96] Protocol handler este un progam care aparţine JDK şi permite unui browser disponibil Java interpretarea unui nou tip de protocol. Acest lucru elimină inconvenientul browserelelor tradiţionale, unde apariţia unui nou protocol implica dezvoltarea browserului, astfel încât să conţină tot codul ce permite manipularea solicitărilor făcute de protocol, ceea ce implică realizarea unui program monilitic, care consumă multă memorie RAM. Dezvoltarea browserelor disponibile Java este modulară şi extensibilă iar browserul în sine este un program cu flux liniar, care utilizează un set de protocoale ce reprezintă repertoriul curent ce poate fi interpretat. Manipulatorul poate fi încărcat "transparent" astfel încât utilizatorul nu trebuie să fie preocupat de detalii, singura sarcină fiind accea de a naviga în URL-ul propice. Protocul handler încarcă în background tot ce trimite browserul iar browserul îşi încarcă doar handler-ele necesare realizând o optimizare a memoriei RAM ocupate. Content handlers are rolul de a furniza o alternativă a ideii de aplicaţii standallone, permiţând programatorului Java să scrie codul ce extinde browserul, dându-I abiliatea de a interpreta conţinutul inline fără a solicita apelul unor aplicaţii externe. In dezvoltarea browserelor programatorii şi-au dat seama că nu se poate determina tipul conţinutului trimis prin MIME şi apoi să se lege browserul de "helper-e" prezente pe calculatorul local al utilizatorului. Content 4
handler este încărcat automat la fel ca şi protocul handler, atunci când se accesează pagina Web, ce conţine un nou tip (video, audio, conţinut special de pe Web, etc) deşi Netscape nu suportă modelul Java pentru content handler. 1.2. Primii paşi în programarea Java Pentru a realiza o aplicaţie Java sunt necesare un editor de texte, JDK (Java Development Kit) şi un browser disponibil Java.Indiferent de platforma utilizată, la despachetarea JDK se crează directorul JAVA şi o serie de subdirectoare (lib - ce conţine fişierul classes.zip, bin - ce conţine codul binar executabil pentru unelte Java memorate, javac - interpretor java de sine stătător şi appletviewer). Acestea nu se despachetează pentru a nu consuma spaţiu de memorie pe disc. In final se setează variabilele de mediu CLASSPATH care identifică locaţia unde se caută uneltele Java pentru orice clasă care nu este găsită în partea standard, inclusă în JDK. In momentul de fată sunt întâlnite mai multe tipuri de aplicaţii scise în limbajul Java: ♦ appleturi - aplicaţiile ce rulează într-un browser ce suportă Java şi respectă convenţii clar definite; ♦ aplicaţiide sine stătătoare (standalone)- foarte uşor de realizat deoarece Java este un limbaj de programare complet, configurat OO. Când appleturile rulează fără browser disponibil java, aplicaţiile standalone sunt complet autorizate, putând rula pe un calculator, sau preluâd avantajele suportului Internet oferit de Java API. Spre exemplu Sun HotJava este scris complet în limbajul Java. ♦ appletcation sau applet-aplicaţiile care sunt tratate în context, fie ca aplicaţii grafice standalone de către interpretorul Java, fie appleturi de către browser disponibil java, sau appletviewer. Aplicaţii specifice J2EE ♦ servlet - aplicaţii similare cu appleturile dar care rulează într-o aplicaţie ce rezidă pe un Web server care suportă Java şi respectă conveţiile impuse. Practic servle-urile rezidă în aplicaţii Web interactive şi înlocuiesc CGI-urile, mărind performanţele aplicaţiilor. Pentru a extinde funcţionalitatea serverelor Web se folosesc Java Servlets care sunt programe Java independente de platformă, care fac parte din componentele server-side şi care interacţionează cu motorul servlet-ului, suportat de serverul Web, bazându-se pe protocolul de comunicaţie tip cerere-răspuns, axat la rândul lui pe protocolul HTTP. Spre deosebire de acestea, appleturile fac parte din componentele client-side care rulează într-un browser Web, în interiorul unei pagini HTML sau a interfeţei grafice. Java Server Pages (JSP) extind funcţionalitatea paginlior Web asigurând manipulare şi generare dinamică de conţinut. ♦ Obiectele de business sunt implementate ca şi enterprise beans sau componente EJB (Enterprise Java Bean):entity beans şi session beans, driven messanger. Abordate din prisma clientului, session beans sunt fie resurse private fie destinate clienţilor care le-au creat, fiind privite ca şi anonime. Entity beans au în schimb o identitate unică prezentată sub forma unei chei primare. Inafara acestor obiecte, enterprise bean defineşte în plus trei entităţi: servere, conteinere şi clienţi. EJB există în interiorul unor conteinere care gestionează ciclul lor de viaţă şi furnizează o serie de alte servicii. Crearea şi editarea codului Java presupune încapsularea în clase Java. In general fiecare clasă trebuie să meargă cu propriul fişier sursă, numit prealabil înaintea clasei (spre exemplu: Primul.java). De fapt se pot edita mai multe clase într-un fişier sursă, depinzând de declaraţiile public realizate şi se poate utiliza un editor standard, pentru Windows sau Unix. 5
Dacă se utilizează IDE (Java Integrated Development Environment) ca şi Symatecs Café pentru Windows NT sau Natural Intelligence Roaster pentru Macintosh, se apelează la editorul încorporat. IDE are avantajul creării documentului sursă, compilării, rulării şi depanării într-un mediu încorporat. Dacă appletul este integrat într-un fişier HTML, apelul se face din secţiunea BODY astfel:
… Fişierul sursă (primul.java) se salvează în acelaşi director ca şi fişierul HTML şi conţine: import java.awt.Graphics; public class Primul extends java.applet.Applet { public void paint(Graphics g) { g.drawString("debut de programare",40,30); } Explicaţiile codului: Pentru a avea acces la metodele de desenare pe ecran se importă clasa/clasele în care sunt declarate aceste metode (Graphics), din pachetul java.awt. Pachetul este o legătură de clase aflate în relaţie. Appletul este declarat ca şi clasa public, pentru a putea fi accesibilă interpreterului construit în Web browser sau aplletviewer, de oricâte ori rulează. Când codul sursă este compilat, numele fişierelor class este luat din numele clasei din codul sursă şi nu din numele fişierului ce conţine codul sursă, (spre exemplu: Primul.class) iar dacă în codul sursă sunt încă două clase declarate (A şi B), atunci vor apare 3 fişiere: Primul.class, A.class şi B.class. Menţinerea paralelismului între fişierul sursă şi fişierul de ieşire este făcută uşor, permiţând recompilarea automată a oricărei clase necesare. Metoda paint este apelată din exteriorul appletului ori de câte ori interpreterul java are nevoie să afişeze sau să reafişeze un applet. Atunci când este apelată, se trece obiectul în context grafic, (clasa Graphics) ca parametru. De aceea se importă clasa Graphics pentru a furniza compilatorului caracteristicile clasei. AWT este legat de limbajul Java şi nu este construit în acesta astfel încât ori de câte ori g.drawString() este apelat, se apelează metoda definită în clasa Graphics pentru a desena în context grafic. Primul parametru este şirul desenat iar ceilalţi doi sunt coordonatele verticale şi orizontale ale punctului de unde începe linia de bază a primului caracter din şir. Punctul este măsurat în pixeli din marginea stânga-sus a appletului pe pagină, astfel încât y creşte ca şi cum s-ar muta în jos pe ecran.[Ramb_96] Compilarea appletului presupune că directorul ce conţine codul binar Java este în PATH iar uneltele JDK sunt în CLASSPATH. Comanda de compilare este: javac nume_fişier.java Compilatorul rulează şi apoi se întoarce la prompter iar în cazul erorilor de compilare se fac corecţiile necesare, relansând compilatorul. De obicei apelul compilatorului se face din directorul unde se află fişierul sursă, rezultând, conform exemplului anterior: Primul.class Primul.html Primul.java 6
Dacă se doreşte vizualizarea sursei compilate se foloseşte: javap -c Primul şi apare reprezentarea în instrucţiuni a codul binar generat pentru acest fişier. Execuţia appletului solicită un browser disponibil Java sau appletviewer oferit de JDK. Browserul permite vizualizarea întregii pagini ce conţine appletul iar appletviewer vizualizează doar appletul, ignorând comenzile HTML ce nu privesc execuţia appletului. Exemple: file://../Primul/Primul.html sau appletviewer Primul.html Dacă apelul se face din alt director se indică şi calea completă către fişier astfel: C:>appletviewer file://../Primul/Primul.html (la Windows NT) sau: appletviewer file://../classes/Primul/Primul.html (la UNIX) Observaţie: Argumentul din appletviewer nu este fişierul.class sau .java ci mai degrabă URL-ul fişierului html, care include protocolul utilizat pentru acest fişier (file sau http dacă este accesat la distanţă). Aplicaţiile standalone (de sine stătătoare) sunt semnificativ diferite faţă de appleturi în termeni de structură. Appleturile moştenesc avantajele atributelor şi comportamentului claselor din AWT şi ca rezultat, sunt capabile să iasă "din tipar pentru a utiliza facilităţi ale GUI din AWT. Aplicaţiile standalone pot fi utilizate cu GUI din AWT dar solicită efort suplimentar pentru a le realiza. Toate aplicaţiile au definită o metodă main(), care indică interpretorului de unde începe execuţia programului. Exemplu : class NumeClasa{ public static void main (String args[]) { System.out.println ("Test de programare Java"); … } } Daca în clasa care se defineşte se vor folosi elemente din alte clase disponibile în mediul Java, este nesesară realizarea unui import de clase iar declaraţia de import trebuie să apară înaintea claselor care folosesc elementele respective. Exemplu : import NumeClasa Metoda main() trebuie să accepte argumente din linia de comandă, sub forma şirului de caractere args[ ] şi nu returneaza nici o valoare (tipul void). Metoda este aceeaşi pentru toate instanţele clasei, fapt specificat prin cuvântul cheie static, şi poate fi invocată de orice altă clasa, fiind declarată public. Funcţia main() ia parametri din tabloul de obiecte String trecute în linia de comandă. Apelând aplicaţia Java ClasaIn master 9 9.5 Constatăm că tabloul are 3 elemente ("master","9", "9.5") iar dimensiunea acestuia este dată de variabila args.lenght System.out.println ("Test de programare Java"); echivalent cu printf din C şi cont din C++ apelând tipărirea la consolă. Termenul de aplicaţie stand alone este greşit utilizat pentru că aplicaţia însuşi necesită interpreterul Java pentru a o utiliza deci nu este de sine stătătoare. 7
Exemplu: java Clasa 1.3. Structurarea programului După scrierea unui program, se impune utilizarea unui compilator Java (javac) pentru a traduce programul sursa într-un cod binar portabil. Acestă preia programul sursă Java scris în format Unicode şi identifică elementele lexicale ale limbajului (tokens). Aceste elemente lexicale pot fi identificatori, cuvinte cheie, literali, operatori şi separatori. In programul sursă Java, elementele lexicale trebuie să fie separate între ele cu ajutorul următorilor separatori lexicali: spatiu (blanc), sfârşit de linie (Enter), sau comentariu. Compilatorul traduce programul sursa Java într-un cod binar, independent de calculator, neglijând separatorii lexicali.[java_1] 1.3.1.Comentariile Comentariile sunt secvenţe de caractere care nu afectează semantica programelor, ele având rolul de a explica secvenţe de instrucţiuni din programul sursă. In limbajul Java există trei stiluri de comentarii: • Stilul /* … */, utilizat iniţial în limbajul C standard, a fost preluat apoi in C++, iar acum este disponibil în Java şi se foloseste în cazul comentariilor pe mai multe linii. • Stilul // … a fost adaugat în limbajul C++ pentru a uşura scrierea comentariilor într-o linie, acest tip de comentariu fiind disponibil şi în Java. • Stilul /** … */ este nou adaugat de limbajul Java şi este utilizat pentru întocmirea automată a documentaţiei pentru programele Java. Acesta se poate plasa numai în faţa declaraţiilor, textul comentariului fiind copiat automat în documentaţia aplicaţiei de către generatorul automat al documentatiei (javadoc). 1.3.2. Unităţile de compilare Conform normelor de programare tradiţionale, un program este o succesiune de structuri sintactice a căror execuţie este coerentă şi acest lucru rămâne valabil şi pentru Java, cu deosebirea că o succesiune de declaraţii şi instrucţiuni devine un program Java numai dacă este încapsulată într-o clasă. Un program Java este în mod obligatoriu o clasă, care poate fi alcatuită la rândul ei din alte clase sau interfeţe şi cuprinde cel putin un fişier sursa cu extensia .java. Acest fişier numit unitate de compilare (compilation unit) este alcatuit cu ajutorul următoarelor patru tipuri de declaraţii: declaraţii de import, declaraţii de pachet, declaraţii de clasă şi declaraţii de interfaţă. Unitatea de compilare tipică este alcătuită dintr-o singură instrucţiune package, multiple instrucţiuni de import şi multiple declaraţii de clasă, unele dintre acestea putând fi de tip public. Declaraţiile reprezintă instrucţiuni sintactice, alcatuite din structuri sintactice de tip instructiuni, declaraţii de tip sau declaraţii de clasă iar declaraţia de clasă este alcatuiă din variabile şi metode. 1.3.3. Pachete şi interfeţe Daca o clasă va fi folosită ulterior în mai multe aplicaţii, aceasta se poate include într-un pachet, conceput ca structură superioară sau un grup de clase, tratate ca o entitate. Spre exemplu există pachetul de clase java.lang, care conţine câteva clase de baza, folosite frecvent, cum ar fi System, String, precum şi pachetul de clase java.io, care conţin clasele InputStream, PrintStream. Pachetele restricţionează accesul la variabile şi metode pe baza nivelelor de protecţie definite pentru clase. Variabilele sau metodele unui pachet care nu sunt declaraţe explicit public, private sau protected, pot fi văzute doar de clasele din acelasi pachet şi prin aceasta se permite claselor din 8
acelaşi pachet să aibă acces la oricare din metode, fără a ţine cont de compromisul încapsularii oferit de programarea orientată pe obiecte. Declararea unui pachet se face prin inserarea la începutul fişierului a unei declaraţii de forma: package nume_pachet, unde numele pachetelor sunt identificatori Java. Grupurile de instrucţiuni package leagă clasele şi interfeţele pentru a evita conflictele şi pentru o mai bună organizare. Spre exemplu definim clasa ecran utilizată atât de appletul aplic1 cât şi de aplic2. Acestea se disting prin pachetul unde sunt plasate astfel: aplic1_pack.ecran aplic2_pack.ecran unde aplic1_pack şi aplic2_pack sunt directoare din CLASSPATH. Exemplul sugerează faptul că fiecare clasă şi interfaţă are nume unic, clasele şi interfeţele din aceaşi pachet au nume unic şi pachetele la rândul lor au nume unic. Problema gravă apare când 2 persoane au clase pe acelaşi host, caz în care se recomandă numirea ierarhică a pachetelor. Exemplu: NumeOrganizaţie.divizie.utilizator.numePachet Numele pachetelor este separat cu punct în componente, nefiind limitat numărul componentelor dintr-un pachet. Fiecare componentă reprezintă un element din structura ierarhică, mapat direct pe structura de directoare a sistemului unde se află memorat pachetul. Instrucţiunea package este prima linie din unitatea de compilare, toate clasele şi interfeţele din unitate fiind legate de acest pachet. O unitate de compilare poate avea mai multe clase şi interfeţe membre, dar numai o instrucţiune package. Fiecare clasă aparţine unui pachet iar dacă o unitate de compilare nu are instrucţiunea package este plasată în pachetul implicit (fără nume). Pentru aplicaţii simple nu este recomandată dar dacă apar aplicaţii complexe cu multiple clase şi interfeţe este de recomandat organizarea acestora într-un pachet, conform figurii 1.1. 1 2
FIRMA FURNIZOR I
CLIEN TIFIRM A
3 Figura 1.1. Structura de directoare pentru o aplicaţie complexă Considerăm pachetele Furnizori şi Clienţi (nivel 2) care corespund celor 2 subdirectoare create ca făcând parte din directorul FIRMA (nivel 1), sub CLASSPATH. Fiecare din cele 2 subdirectoare vor conţine unităţile de compilare (nivel 3) proprii. Astfel includem: package Firma.FurnizoriPack; package Firma.ClientiPack;
9
Respectând convenţia standard de creare a pachetelor, pachetele utilizator încep cu majusculă şi li se dă nume unic bazat e DNS (Internet Domain Name).Pachetele ce fac parte din API standard încep cu componenta java (litere mici). Exemple: COM.bettersoft.AmazingJavaStuff EDU.ryu.cs.students.phd.jontstudent.IndependentStudy 1.3.4. Instrucţiunea import Pentru a utiliza pachete compilate anterior este necesară importarea cu ajutorul unei comenzi de import:[Proda-97] Exemple: import NumePachet.NumeClasa; // import de clasă import NumePachet.NumeInterfata; // import de interfaţă import NumePachet.*; // import de clase şi interfeţe publice din //pachetul specificat Instrucţiunea import permite claselor utilizarea numelor abreviate pentru referirea claselor şi interfeţelor declarate în exteriorul acestei unităţi de compilare, ceea ce conduce la un cod uşor de citit. In schimb aceasta nu permite alte avantaje decât convenţia sintactică utilă pentru programatori. Import se poate utiliza o singură dată într-un pachet cu nume complet de calificare astfel: import java.applet.Applet; şi odată plasată se poate referi clasa importată simplu: Applet fără calificare completă java.applet.Applet. Instrucţiunea nu determină încărcarea clasei în memorie ci înregistrează doar o legătură simbolică ce mapează direct către clasa unde poate fi găsită. Clasa se încarcă doar când este referită. La importul tuturor claselor şi interfeţelor ( spre exemplu: import java.applet.*) se cere din partea programatorului anticiparea claselor particulare ce vor fi utilizate. Aceasta înseamnă că interpreterul sau compilatorul caută în fiecare fişier .class la acestă instrucţiune import pentru a vedea dacă aparţine pachetului sau nu şi pentru a face referinţă la o utilizare ulterioară. Rezultă astfel o încetinire substanţială a vitezei de execuţie dar o siguranţă maximă în depanare. Ulterior finalizării unităţii de compilare se înlocuieşte această instrucţiune cu importuri specifice explicite pentru fiecare clasă şi interfaţă utilizată. O potenţială problemă de import este dată de stil, prin care se pot introduce inadvertenţe de nume şi implicit conflicte. Spre exemplu, există în două pachete diferite clasa Conturi şi acestea se importă astfel: import Pach1.*; import Pach2.*; In momentul în care compilatorul încearcă să redenumească local clasa Conturi ambele clase au acelaşi nume. Nu este o eroare de utilizare a ambelor clase în aceeaşi unitate de compilare pentru că pot fi referite complet calificate prin nume unic dar apare o eroare când se importă ambele clase. Erori de utilizare: import java.* /* eroare- java.net şi java.awt sunt pachete*/ import java.lang.* //implicită
10
Fiecare pachet sau program java importă automat toate clasele şi interfeţele exenţiale din java şi nu este necesar importul explicit de interfeţe ca java.lang.Object, java.lang.Boolean, java.lang.Float, java.lang.Runable,etc. Instrucţiunile package şi import nu generează cod ci au un rol bine definit în programarea OO. 1.3.5. Declaraţii de clasă şi interfaţă Moştenirea claselor va permite construcţia unei noi clase pe baza alteia existente, iar prin intermediul cuvântului cheie extends asupra unei clase existente. Astfel pentru defini moştenirea unei clase (exemplu: MyClass), este necesară importarea clasei respective şi referirea la ea in clasa nou creată: Exemplu: import Mclasa; class Nouaclasa extends Mclasa { // mostenitoarea clasei MClasa // codul sursă al noi clase } Relaţia 1-1 între clasele public şi numele fişierului sursă permite compilartorului căuarea, verificarea şi recompilarea fişierului sursă ce este memorat, cu menţiunea că numele clasei şi fişirului sunt case-sensitive. 1.3.6. Biblioteci de clase în mediul Java In mediul Java există mai multe pachete care conţin biblioteci de clase, unele având caracter general, altele fiind orientate spre diferite tipuri de aplicaţii. Acestea conţin metode care se pot folosi în programe dar pentru a putea folosi anumite clase dintr-o bibliotecă, trebuie să importăm clasele respective sau întreaga biblioteca. Singura bibliotecă prezentă tot timpul este biblioteca de baza a limbajului Java numită java.lang.[Laur-98] Bibliotecile de clase se împart în două categorii: cele destinate a fi folosite în programe care, după ce sunt compilate, urmează să fie executate cu ajutorul interpretorului Java. In a doua categorie sunt incluse applet-uri, adică subclase ale clasei Applet din pachetul java.applet. Aceste clase sunt folosite în programe care urmează să fie executate de către browser pentru conţinut dinamic (executabil) Java. Mediul Java oferă pachete cu biblioteci de clase în care se gasesc metode pentru functiile de baza ale modulelor sistemului. Compilatorul Java creaza o structură de directoare sub formă de arbore, pentru fiecare nume de pachet fiind creat un director. In denumirea clasei se gaseste operatorul punct (.) pentru a lega directorul java de subdirectorul care conţine o biblioteca de clasa. Pentru a se referi aceste biblioteci sau anumite clase din biblioteci este necesară folosirea aceluiaşi operator astfel: java.biblioteca.clasa unde biblioteca poate fi ună din clasele lang, io, util, applet, awt sau net, iar clasa este clasa din biblioteca respectivă.[Proda-97] Java Runntime Environment (JRE) conţine maşina virtuală Java, clasele de bază şi alte fişiere necesare rulării, Java 2 SDE (Software Development Kit) incluzând pe lângă JRE compilatorul javac, debugger-ul şi alte instrumente de dezvoltare. O trecere în revistă a bibilotecilor existente în limbajul Java:
11
java.lang – aici se pot gasi clase în care sunt implementate funcţiile de bază din mediul Java şi acest pachet va fi încărcat automat, odată cu compilatorul, fără a fi necesară o declaraţie explicită de import. Această bibliotecă conţine clase pentru extensia tipurilor elementare de date, pentru prelucrarea sirurilor de caractere, pentru funcţii matematice, pentru interactiuni de baza cu sistemul, pentru fire de executie, clase abstracte şi pentru biblioteci. java.lang.reflect se utilizează pentru verificarea entităţilor Java, a claselor şi obiectelor, foarte utilă în realizarea unor aplicaţii dinamice şi flexibile. java.applet – cu rol de implementare a applet-urilor, care sunt destinate a fi executate de un browser pentru conţinut executabil Java. Pentru a crea un applet executabil într-o pagină Web sau rulat cu appletviewer, se poate extinde această clasa cu metode de includere a efectelor sonore, legături cu alte pagini Web, pentru definirea unor formate. java.awt (Abstract Window Toolkit) – în această biblioteca se afla clasele de bază care oferă instrumente pentru crearea elementelor de interfaţă grafica. Prin includerea acestor elemente de interfaţă grafica, appletul va deveni o altă intefată grafica. Clasele din acest pachet includ java.awt.datatransfer, java.awt.event, java.awt.image java.awt.font, java.awt.color, java.awt.geom metode pentru crearea de ferestre, meniuri, butoane, comutatoare, liste, bare de defilare verticale şi orizontale, operarea cu fonturi, imagini 2D şi 3D, java.awt.im ce permite definirea de clase şî metode de intrare. java.io – conţine clase de bază pentru implementarea operaţiilor de intrare-ieşire în mediul Java. Se pot efectua citiri de la dispozitivul de intrare, scrieri la unitatea standard de ieşsire, citiri de pe disc sau din retea. Pentru efectuarea acestor operatii este necesară folosirea metodelor gată pregatite pentru diferite structuri de informaţii: fişiere, zone tampon (buffere), filtre, pipes. java.util – este destinată funcţii speciale în mediul Java, ca suport pentru lucrul cu date calendaristice, operaţii cu stive, operaţii cu tabele sau operaţii cu vectori. Dacă appleturile folosesc algoritmi de compresie/ decompresie au la dispoziţie java.util.zip şi java.util.jar java.net – cuprinde clase de baza pentru crearea de interfeţe pentru protocoale de reţea (telnet, ftp, nttp, WWW) şi legături de tip socket. java.math se foloseşte pentru diverse funcţii matematice standard implementate. java.beans este destinată creării de componente reutilizabile, denumite beans-uri. java.rmi, java.rmi.dcg, java.rmi.registry, java.rmi.server asigură accesul la obiectele distribuite şi facilitează acces la metode ale acestora. Pachetele sunt destizate dezvoltării aplicaţiilor distribuite folosind Remote Method Invocation (RMI). java.security, java.security.acl, java.security.interfaces asigură mecanismele de securitate a softului în care sunt incluse. org.omg.CORBA facilitează maparea API OMG CORBA în limbajul Java, incluzând clasa ORB pe care programatorul o poate utiliza ca şi un Object Request Broker. javax.namimg, javax.namimg.directory, javax.namimg.event, javax.namimg.ldap, javax.namimg.spi permit dezvoltarea metodelor de intrare care pot fi utilizate cu orice JRE.
12
Platforma Java conţine JVM şi JAVA API 1) Java Basic API conţine applet, AWT, I/0 (sau fluxuri inclusiv serializarea obiectelor), language (inclusiv reflection) şi utility incluzând data structures and events). 2) Java Enterprise API – asigură suport pentru database entreprise şi aplicaţiile legacy şi conţine : - JDBC – standard SQL - Java RMI – invocarea la distanţă client-server; - Java IDL – furnizează interoperabilitatea şi conectivitatea cu CORBA ; - JNDI – interfaţa standard cu nume multiple şi servicii de directoare în entreprise. 3) Java Bean API 4) Java Security API – include criptografia, semnătura digitală, criptare, autentificare. 5) Java Foundation Clases (JFC) API este GUI pentru programare şi faţă de AWT include: • SWING SET – reflectă un grup de componente legate în AWT, extinzându-le prin butoane simple şi completându-le cu arii text şi arii vizuale şi folder ; • Accesibility API – furnizează interfaţa clar în cititoare de ecou, sisteme de recunoaştere a vocii, extinzând JFC şi AWT 6) Java Server API – permite crearea de Servleţi Java şi încorporarea în aplicaţii Web, furnizează acces consistent şi uniform la server Web şi resursele administrative ale sistemului ; 7) Java Commerce API – este un API pentru comerţ şi management financiar. Java Wallet – specifică un cadrul client-side pentru cărţi de credit, debit şi tranzacţia cu bani electronici. 8) Java Media and Communication API asigură integrarea clipuri audio-video, prezentări animate, grafice, imagini 3D, modele 3D. - Java 2D API - set de clase pentru grafică şi imagini BD incluzând linii text şi imagini în model unic. - Java 3D API – pentru aplicaţii 3D şi applets - Java Media Framework API - dă arhitectură unificată, protocolul mesajelor, programarea interfeţelor pentru media players, capturi media şi conferinţe. - Java SOUND API dezvoltă 32 chanel audio cu calitate bună şi asigură control MIDI al sintezei sunetului. - Java Telephony API – interfaţă portabilă în aplicaţii computer –telephony. - Java Speech API – cross-platform interface, ce permite controlul recunoaşterii, sistemului de dictare şi sinteza vorbirii. 9) Java Management API – set de obiecte extensibile şi metode destinate reţelelor, serviciilor de management în reţele eterogene. 10) Personal Java API – destinată conectării la distanţă a dispozitivelor personale: mobile, calculatoare, hand-held, boxe Set-top, console de jocuri, smart phone. 11) Embeded Java API - pentru dispozitive complexe ca: mobil, pagere, instrumentare de control al proceselor, periferice de control, rutere reţea şi switchuri reţea. CORE API – conţine 2 categorii: - CORE – adică JDR plus Personal Java şi Embeded Java; - Standard Extension – din exteriorul lui CORE. Java CORE conţine: Java Basic API, Entreprise API (fără JNDI), Java Beans API, Java Security API, Swing Set, Java 2D. Java Reflection API 13
Dacă politica de securitate permite, API poate fi folosit la: - descoperirea informaţiilor despre câmpuri, metode şi constructori din clasele încărcate; - construcţia de noi clase şi noi tablouri; - accesul şi modificarea câmpurilor, obiectelor şi claselor ; - accesul şi modificarea elementelor de tablou ; - invocarea metodelor obiectelor şi claselor. Spre exemplu Java Beans şi Entreprise Java Beans, solicită acces la public members pe un obiect ţintă iar Java ObjectSerialization are nevoie de acces la toţi membri declaraţi pentru o clasă dată. Java Reflection API conţine : • Class – furnizând instanţe pentru Field, Method şi Constructor; • Field, Method, Constructor – furnizează informaţii reflexive despre membri asociaţi, implementează intefaţa Member. Numai JVM poate crea instanţe ale acestei clase. o Field - poate fi o clasă statică sau o instanţă variabilă; o Method – metodă abstractă, metodă (statică), a clasei sau instanţă a metodei; • Array- furnizează metode de construcţie dinamică şi acces la tablouri. Sunt clase finale şi de aceea neinstanţiabile. • Modifier – permite modificarea informaţiilor (static şi public) despre clase şi membri. Sunt clase neinstanţabile. Security Model Java Security manager are 2 nivele de verificare : - metodele din clasa Class ce dau acces reflexiv sau set de membri surse pentru instanţele Field Method, Constructor. Aceste metode leagă verificările de securitate către system security manager: - o dată ce system security manager dă acces la membri, orice membru, reflectat poate apela pe obiecte Java (protected, default la package şi clase private pentru membri). Pentru a folosi clasele din bibliotecile de mai sus este posibil importul pachetului în întregime, folosind declaraţia de import corespunzătoare. Exemple: import java.lang.*; import java.applet.*; Dacă se doreşte includerea selectivă a unor clase, pentru a reduce dimensiunile programului binar, se precizează în declaraţiile respective. Exemple: import java.awt.Image; import java.net.URL; Prezentăm câteva detalii ale clasei applet, destinată a fi utilizată în realizarea unei pagini HTML, care pune accentul mai ales pe o interfaţă grafică atractivă şi interactivitate mare. In proiectarea de applet-uri trebuie urmărit scopul aplicaţiei respective şi modul în care acesta poate fi atins de potenţialii utilizatori pentru că un applet Java este mai mult decât un cod care să realizeze operaţii elementare de calcul şi afişare - un mod de realizare a interfeţei cu utilizatorii. Biblioteca de clase applet conţine peste 40 de clase şi interfeţe pentru dezvoltarea de programe de tip applet iar functia de baza a acestora este de a realiza, prin intermediul metodelor obţinute, un control asupră applet-urilor din mediu. Clase cum sunt Basicstyle, Document ajută in controlul vizualizarii documentelor HTML şi a oricarui applet la care se face referire din aceste documente. Clasa applet este superclasa pentru 14
toate applet-urile nou create şi pot fi vizualizate de browser iar un applet creat moşteneste toate metodele clasei applet, care constau nu numai din clasa applet şi din metodele superclaselor acestei clase, care fac parte din awt (nu din clasa applet).[Naugh-96] 1.4. Elemente lexicale şi structuri sintactice 1.4.1. Tipuri, operatori şi expresii Setul de caractere Unicode, care se foloseşte acum în locul setului de caractere standard ASCII permite reprezentarea caracterelor pe 16 biti, în timp ce setul de caractere ASCII foloseşte numai 8 biti. Cele doua subseturi de caractere sunt compatibile, vechiul standard ASCII fiind un subset al setului Unicode. Această necesitate de extindere a setului de caractere - determinată de nevoia de a reprezenta mai mult de 256 de caractere - a apărut din dorinţa de a reprezenta literele ce există în diferite alfabete de pe glob. Cuvintele cheie, sau cuvintele rezervate, sunt identificatori utilizati în limbajul Java într-un mod bine precizat prin gramatica limbajului, fiind interzisăa utilizarea lor în alte scopuri. Un identificator este o secventă de litere, cifre şi alte caractere în format Unicode, primul caracter fiind obligatoriu o literă, un caracter underscore (_) sau simbolul dolar ($). Aceştia trebuie să fie diferiţi de cuvintele cheie şi se utilizează pentru a desemnă variabile, clase şi metode. Literalii se folosesc pentru a desemna valori constante în cadrul unui program Java şi au la baza doua categorii de date: numere şi caractere. Literalii numerici se împart în două subcategorii: întregi ( cu format: zecimal, hexazecimal, octal) şi flotanţi (numere cu parte fracţionară). Un caracter literal se referă la o singură valoare din setul de caractere Unicode, în timp ce un şir de caractere se referă la o succesiune de unul să mai multe caractere. Pe lângă aceştia mai există caracterul boolean, literal considerat numeric pentru compatibilitate cu limbajele C şi C++, iar valorile de adevar true şi false în C şi C++, sunt reprezentate prin valorile intregi 1 şi 0. Constantele literale pot fi exemplificate astfel: Intregi 5 întreg 5l (intreg scurt) 5L(întreg cu tip lung) O5 întreg octal OxaBa (hexazecimal case-sensitive) Double şi Float 4.5, 6.7d, 7.8D - constante duble 3.4E8 - constante duble cu exponent 6.7f sau 6.7F - tip float Booleene: true, false Constante caracter (Escape) \ continuare în linia următoare \n linie nouă \t tab orizontal \r carriage return \f form feed \\ backslash \' apostrof simplu \" apostrof dublu 15
\ddd formă octal \xdd formă hexa \uddd caracter unicode "exemplu" sir de caractere Separatorii, delimitatorii şi terminatorii sunt caractere utile compilatorului pentru a detecta elemente lexicale dar înafară de spaţii, tab-uri mai există şi alti separatori, denumiti delimitatori sau terminatori, care participă la construcţiile sintactice ale limbajului. O parte dintre aceştia au un rol activ în structurile sintactice ale limbajului, şi ar putea fi considerati operatori. De exemplu punctul (.) se foloseşte pentru a obţine un nume unic pentru clasa care aparţine unei biblioteci, prin concatenarea numelui clasei cu numele bibliotecii: biblio.clasa spre exemplu. O variabilă reprezintă o adresa de memorie în care se păstrează o valoare de un tip prestabili, fiecare variabilă fiind inclusă în declaraţii de forma: tip identificator[, identificator];. La întâlnirea unei declaraţii de acest fel, compilatorul crează pentru fiecare identificator din listă câte o variabilă având numele identificator şi tipul tip. După crearea unei variabile, i se poate atribui o valoare iar tipul variabilelor specifică natura valorii variabilelelor şi ce fel de operaţii sunt admise asupra acestora. In limbajul Java există doua categorii de tipuri: simple – care nu sunt construite cu ajutorul altor tipuri, intreg, flotant, boolean şi caracter şi tipurile compuse – care se crează pe baza unor agregări de tipuri simple (tablourile, clasele şi intefeţele).[Naugh_96] Domeniul de aplicabilitate al variabilei este în zona de program unde este recunoscută, de la declararea acesteia până la finalul blocului Exemplu: Class Vedem { int a /*zona a */ void vedemMethod (double c) { //zona c int x,y; // zona x,y …. } void vedemMethod2 (int a) { //zona a unde variabila este locala pt.vedemMethod2 // instanţierea variabilei din clasa Vedem şi traterea ca //variabila globală this.a=a; // zona z; float z; …. } } Tipulile întregi sunt tip de dată şi pot fi caracterizate: byte ( 8 biţi), short (16 biţi), int (32 biţi) şi long (64 biţi) dar spre deosebire de clasă nu există un comportament asociat tipului întreg. Clasa Integer din java.lang are declaraţia: class IntegerClass // conţine variabile intregi //si set de metode de activare 16
int MAX_VALUE; int MIN_VALUE; public static String toString(int i) //metoda {…} } Spre deosebire de C sau C++ unde dimensiunea diferitelor tipuri de primitive este dependentă de calculator la Java tipurile de primitive au dimensiune fixă, indiferent de hard-ul unde rulează maşina virtuală Java. Tipurile întregi sunt cu semn, fiecare variabilă declarată întreg putând lua valori +/- iar tipul determină nivelul valorii şi proprietăţile aritmetice ale variabilei ce-l deţine. Dacă o valoare depăşeşte dimensiunea de reprezentare se trunchiază automat la modulo rang. Exemplu: Dacă byte b=-129; => b=-129-(-256)=127 Dacă byte b=128; => b=-128-256=128 Tipul Floating-Point (IEEE 754) permite reprezenarea float (32 biţi) şi double (64 biţi) implicit fiind double. Pentru testarea depăşirii superioare sau inferioare se utilizează variabila NaN (not a number). Tipul caracter este definit conform reprezentării Unicode la 16 biţi/caracter (spre deosebire de C unde se reprezintă la 8 biţi/caracter) iar la iniţializare se foloseşte apostof simplu. Exemplu: char a, b='parola', c='\n'; Tipul boolean are două valori (true sau false) iar maşina virtuală Java îl gestionează în mod propriu, manipulând biţi individuali ai caracterelor întregi, astfel încât se pot declara mai multe variabile boolean, fiecare solicitând un bit. In Java valorile booleene nu sunt numere şi nu pot fi convertite sau operate ca întregi. Tablourile sunt liste de elemente separate prin virgulă, crete cu operatorul new. Exemple: int col[ ]= new int [3]; int [ ]col= new int [15]; int col[ ]; // echivalent cu int [ ] col … col = new int[3]; //alocarea dinamică a memoriei Variabila col are lungime 3 (întreagă) indexată [0-2] şi poate beneficia de alocare dinamică ceea ce permite programatorului să rezerve spaţiu optim pentru memoria alocată. Exemple: int n=2; double d=5.5; double Dtablou1[ ]=new float[n+1] //corect double Dtablou2[ ]=new float[d] //eronat double Dtablou3[ ]=new float[int(d+2)] //corect //iniţializarea elementelor Dtablou[0]=0.1; Dtablou[1]=0.12; Dtablou[2]=-0.13; 17
//tablouri multidimensionale int mat1[ ] [ ] = new int [2] [3]; //sau int [ ] [ ]mat1 = new int [3] [5]; La tablourile multidimensionale precizarea ultimei dimensiuni este opţională: int [ ] [ ]mat1 = new int [3] [ ]; Este necesară însă definirea celei de a n-a dimensiuni prealabil utilizării ei: Exemplu: for (i=1, i<3, i++) { mat1[I]=new int [5]; } Alocarea memoriei pentru tablouri se face tot cu operatorul new, cu excepţia iniţializării în timpul declarării: char litere[ ]={'a','j', 'p','l','m','n'};//tablou de 6 elemente Operatorul new este utilizat pentru memorarea oricărui tip compus (tablou, obiecte din clasă, interfeţe) iar alocarea explicită pentru tipurile de bază nu este necesară. Exemplu: char a; // nu trebuie new char char Tab1[ ] = new char[3]; In timpul compilării se crează clasa tablou are marchează clasa compilată şi este un obiect ce nu trebuie subclasat explicit (figura 1.2).[Hoff_96] Obiect Array … byte[ ] short[ ] int[ ] long[ ]
float[ ] double[ ] char[ ]
boolean[ ]
Figura 1.2. Ierarhia de tipuri în Java 1.4.2. Tablouri versus pointeri în C Este deja cunoscut faptul că în C pointerul este adresa unei variabile, orice porţiune de memorie fiind accesibilă prin pointeri. Pointerul aritmetic oferă unul din cele mai puternice concepte C oferind posibilitatea calculelor (+,-,/,*) şi putând fi utilizat la accesarea memoriei. In Java s-a elinminat accest concept recunoscută fiind periculozitatea utilizării eronate a pointerilor aritmetici şi s-a introdus handler (manipulator).[Chan_95] Un handler Java este asociat cu un obiect şi nu cu o adresă. Exemple: int col [ ];//handler null int mat1[ ][ ]= new int [4] [ ];
18
Instrucţiunea precedentă alocă memorie pentru cei 4 manipulatori (mat1[0], mat1[1], mat1[2], mat1[3]) şi fiecare punct la null. Alocarea dinamică pentru tabloul actual se realizează cu secvenţa: for (i=1, i<3, i++){ mat1[I]=new int [3]; } matricea mat1 este: 0 3 int int int 2
1 2... int int int
int int int
int int int
Figura 1.3. Alocarea dinamică la tablouri 1.4.3. Operatori Operatorii sunt folosiţi pentru a realiza anumite operaţii elementare între obiecte, fiecare operator joacă şi rolul de separator între două elemente lexicale şi au prorpietăţi similare cu cei din C: precedenţa (ordinea în care sunt interpretaţi) şi asociativitatea (direcţia de evaluare). [Laur-98] Operatori
Precedenţa
[] () ++ -! ~ Instance of * / % + << >> >>> < > <= >= ==
Accesoriu de clasă, variabilă, metodă Accesoriu de tablou Casting Increment Decrement Not Complement unar Test subclasă Multiplicare Impartire Modul Adunare Scădere Deplasare stânga Deplasare dreapta Deplasare dreapta cu umplere cu 0 Mai mic Mai mare Mai mic sau egal Mai mare sau egal Test de egalite
.
Asociativitatea
Stânga la dreapta
Stânga la dreapta Stânga la dreapta Stânga la dreapta Stânga la dreapta Stânga la dreapta Stânga la dreapta
19
Operatori != & ^ | && || ?! =,+,=,=,*=,/+, %=,&=,^=,| =,<<=,>>= a operand b
Precedenţa Test not equal Si binar XOR binar OR binar And OR Operator ternar condiţional Atribuiri
Asociativitatea
Dreapta la stânga
Echivalent cu a=a operand b a+=b echivalent cu a=a+b
Tabelul 1.1. Operatori Java Dacă rezultatul calculelor este de tip întreg şi operandul este long se realizează conversia la tipul long, înrest rezultatul este int. Exemplu: class Test1{ public static void main(String args[ ]) { short i=10, j=11,k; int m; k=i+j; //eroare, trebuie forţată explicitarea k=(short) (i+j); // k=21 m=i+j; // m=21 } } Dacă rezultatul produce o depăşire superioară sau inferioară el va fi trunchiat la dimensiunea de reprezentare. Exemplu: class Test2 { public static void main(String args[ ]) { Integer i= new Integer(0); int overflowN=i.MAX_VALUE+1; //valoarea maximă posibilă System.out.println ("valoarea minimă posibilă=" +i.MIN_VALUE); System.out.println ("valoarea care depăşeşte="+overflowN); if(i. MIN_VALUE=overflowN) System.out.println ("truncherea"); } } Rezultatul programului: 20
valoarea minimă posibilă=-2147483648 valoarea care depăşeşte=-2147483649 truncherea Operatorii acceptaţi pentru tipuri întregi sunt =,-,*,/,%,++,-- şi op= Operatorii ~,<<,>>,>>>,&,^,| iau operanzi de tip întreg şi întorc valori întregi iar operatorii <,>,<=,>= iau operanzi de tip întreg şi întorc valori booleene. Pentru tipurile de virgulă flotantă sunt acceptaţi operatorii de tip întreg mai puţin cei la nivel de bit. Definiţia modulo este diferită de cea dată pentru întregi. Dacă x şi y sunt double sau float: x%= x-Math.rint(x/y)*y Dacă numărul are parte fracţionară 5 este convertit la întregul superior. Math.rint(7.0/2.0)= Math.rint(3.5)=4 Spre deosebire de tipul întreg la cele float depăşirea superioară generează Inf(infinit) şi cea inferioară generează 0 iar rezultatul împărţirii cu o generează Inf. Exemple: double depsup=1.0/0.0 // overflow double depinf=27/0.0 // underflow Operatorii relaţionali se utilizează cu precauţii în comparaţii pentru că dacă a=b sau a!=b nu înseamnă a>b || a0)?true:false; echivalent cu: 21
if (mask>0) return true; else return false; Operatorii de tablou [ ] se folosesc pentru a crea o dimensiune specificată sau pentru a regăsi o parte din informaţiile din tablou. Exemple: Tip NumeTablou [ ] = new tip[dimensiune]; int [ ] [ ] = new int[3][ ]; NumeTablou[expresieIntreaga]; a[1]; a[n+5];// n intreg Tablourile pot fi accesate cu valori cuprinse între [0,dimensiune-1] iar încercarea de accesare a tabloului înafara acestor limite generează : Array Index Out Of Bounds Exceptions. Operatorii pentru obiecte şir sunt + şi += deşi mult timp metodele String.concat() au fost metode aplicate pentru concatenarea şirurilor. Operatorii pentru obiecte sunt: new, necesar pentru instanţierea obiectului şi instanceof, utilizat pentru test dacă obiectul specificat este instanţiat unei subclase/clase. Exemple: String str1=new string(); …. if(str1 instanceof String) System.out.println ('str1 este instanţiat'); In Java orice tip numeric poate fi transformat (casting) cu excepţia transformărilor tipurilor de bază în tipuri compuse. Exemple: float f=float(3.7); int i;; i=int(f);//i=3 f=float(i);//f=3 Transformarea subclaselor şi superclaselor este permisă dar deşi o instanţă poate fi transformată în superclasă, transformarea unei subclase generează erori de execuţie. Interpreterul Java verifică dacă subclasa este extensie a unei superclase transformate şi dacă un obiect nu este instanţă de subclasă (sau una din subclase) se rulează rutina Class CastException. Expresiile sunt combinaţii de variabile, literali şi operatori ce determină un rezultat. Atribuirea este procesul de memorare a valorii iar variabila căreia I se atribuie se numeşte variabila stângă. Separatorii Java din expresii sunt ( ), { }, [ ] ;,. Ordinea de evaluare este dată de dictată de precedenţele operatorilor şi de separatorii incluşi. 1.5. Instrucţiuni de control al fluxului de execuţie 1.5.1 Instrucţiunea label label: Instrucţiunea label serveşte la etchetarea blocului în care se află. Ea nu modifică fluxul de prelucrare ci serveşte ca marker pentru eventuale salturi în secvenţe normale. Java procedează la 22
scurtcircuitarea evaluărilor astfel încât pentru un operator de tip && la prima evaluare de expresie falsă renunţă la evaluarea expresiei rămase. Pentru o mai bună optimizare, dacă în compilare se identifică valoarea falsă întotdeauna, la o evaluare de expresie dintr-un if, nu se generează cod pentru ramura true. 1.5.2. Instrucţiunea IF if { Instrucţiuni1 ; [else } if { instrucţiuni2; }else { instrucţiuni3 ;] } Instrucţiunea execută primul set de instrucţiuni dacă =true (adevarată) şi daca =false (falsă) se execută setul de după else (adică unul sau mai multe teste if imbricate, condiţionate , sau setul de instrucţiuni . Când else nu este specificat se execută instrucţiunea de după }. Se pot imbrica mai multe conenzi if succesive, marcate între paranteze {} iar evaluarea se face de sus în jos. 1.5.3. Instrucţiunea switch switch (expr) { case const1: instrucţiuni1; break; case const2: instrucţiuni2; break; … default: instrucţiuniN; } Instrucţiunea switch este în realitate echivalentul unui if imbricat, unde: expr - este o expresie tip caracter, byte, short sau int; const1, const2, constN - valori constante ce sunt asociate execuţiei grupurilor de instructiuni; instrucţiuni1, instrucţiuni2, … - grupuri de instrucţiuni care se execută condiţionate de const1, const2, constN; default -- grup de instrucţiuni (instrucţiuniN) care se execută dacă nici una din condiţiile de const1, const2, constN nu este îndeplinită. Exemplu: import java.io.*; class UnDictionar { public static void main (String args [ ] ){ char alege; System.out.println ("Selectati una din lectiile de instruire:"); System.out.println ("AWT"); System.out.println ("API"); 23
System.out.println ("Corba"); … System.out.flush(); try { switch (alege=(char)System.in.read(); { case "W": System.out.println ("am selectat AWT"); break; case "P": System.out.println ("am selectat API"); break; case "C": System.out.println ("am selectat Corba"); … default: System.out.println ("Selectie eronata"); } catch(IOException e) { System.out.println (e.toString()); } } }
1.5.4. Instrucţiunea while while (exprl) { instrucţiuni; } Instrucţiunea while permite execuţia grupului de instrucţiuni până când expresia logică expl evaluată este adevărată. Exemplu: int i; boolean test=false; while (test == false){ i=(int)(Math.random()/500); } if (i%2==0) test=true; } 1.3.5. Instrucţiunea do do { instrucţiuni; } while (exprl) ; Spre deosebire de instrucţiunea while, instrucţiunea do permite execuţia grupului de instrucţiuni cel puţin odată, prealabil evaluării expresiei logice expl . Exemplu: int i; boolean test=false; do { 24
i=(int)(Math.random()/500); } if (i%2==0) test=true; } while (test == false) 1.5.6. Instrucţiunea for for (expresie1, expresie booleană, expresie2) { instrucţiuni; } Instrucţiuea for (de ciclare) execută grupul de instrucţiuni, pornind de la expresia1, până când este îndeplinită condiţiă dată de expresia booleană, cu pasul (incrementul) dat de expresie2. Exemplu: int k=7; int []vo= new int[k];// vector for (int i=0, i
Instrucţiunea continue este similară cu break şi se foloseşte în corpul instrucţiunilor iterarive ca: while, do, for, mai puţin switch.In absenţa etichetei controlul este redat la sfârşitul blocului unde se află aceasta. Dacă este plasată într-un bloc finally se execută blocul finally şi se predă contolul la sfârşitul blocului. Intr-o buclă do sau while, expresia booleană este evaluată imediat după continue, iar dacă este plasată într-o buclă for, înaintea expresiei booleene se execută expressionexe_after_one_iteration. Dacă instrucţiunea este etichetată, eticheta trebuie să fie inclusă în instrucţiuni iterative, altfel se trece controlul la cel mai apropiat bloc iterativ. Exemplu: class Lot ; int numMatrice [ ]; //constructorul Lot(int [ ]nP){ //alocarea memorieii NumMatrice= new int[nP]; } //generare numere aleatoare public void pick(){ int num; for (int i=0, i
26
forţa întoarcerea, ca mod de prevenire a execuţiei restului metodei, ceea ce salvează nivelul indentărilor, permiţând un cod uşor de citit (în aceste cazuri este inclusă în if). Exemplu: void Lot (int x) { if (x<=20) { return; } else return; void altLot (int x){ if (x>20) { return; } } In cazul în care metoda sau constructorul este declarat cu tip de valoare întoarsă, instrucţiunea return trebuie să întoarcă tipul corespunzător declaraţiei. Dacă return este inclus într-un bloc try, urmat de finally, instrucţiunile finally din bloc sunt executate înainte de a reda controlul apelantului. 1.6. Manipularea şirurilor Crearea şirurilor în Java se realizează cu operatorul new iar concatenarea lor cu append Exemplu: String d = new StringBuffer ("Am obtinut"); .append (calif) .append ("la testul de baza") .toString(); Secvenţa de mai sus este echivalentă cu String d = "Am obtinut"+calif+"la testul de baza"; Caracteristicile precedenţei operatorilor In exemplul de mai sus, valoarea int este trecută metodei supraîncărcate append în StringBuffer, care o converteşte în reprezentare şir la sfârşitul instanţei StringBuffer. La expresii mixte, ce conţin mai multe valori, operatorul + are precedenţe diferite. Exemplu: String d2 = "Am calculat:"+2+2; //ceea ce conduce la d2= Am calculat:22 1.6.1. Conversia şirurilor StringBuffer este o versiune supraîncărcată a metodei append pentru orice tip posibil. La fiecare concatenare (operatorul +) este apelată versiunea potrivită a metodei append. Metoda cheamă de fapt metoda String denumită valueOf pentru a construi reprezentarea şir. Pentru tipuri simple, valueOf crează reprezentări şir vizibile de int sau float iar la obiecte apelează metoda toString a obiectului respectiv. Fiecare clasă implementează toString în virtutea implementării moştenite din clasa Object. Exemplu: class Point { int x,y; 27
Point (int x, int y); this.x=x; this.y=y; } public String toString() { return "punctul ("+x+","+y+")"; } } class DemoSir() { public static void main ( String args [ ] ) { Point p = new Point (20,30); System.out.println("Obtinem"+p); } } Versiunea clasei Point include suprascrierea metodei toString din object, formată dintr-un şir conţinând valorile curente (x,y) ale instanţei Point. Ieşirea este dată de: Obtinem punctul (20,30). 1.6.2. Extragerea caracterelor Extragerea caracterelor se realizează cu metoda charAt pentru un singur caracter şi care presupune parametri cuprinşi în intervalul (o, length()) ai şirului dat.Dacăe doreşte extragerea mai multor caractere se foloseşte metoda getChars ce permite specificarea indexului de început şi de sfârşit al subşirului, precum şi tabloul de tip char în care s eva plasa rezultatul. Exemplu: class DemosubSir() { public static void main ( String args [ ] ) { String s= "Exemplu de utilizare a metodei getChars"); int start=6; int sfarsit=20; char buf[ ] = new char[sfarsit-start]; s.getChars(start, sfarsit, buf, 0); System.out.println("Obtinem subsirul"+buf); } } Metoda getChars nu include char la sfârşitul indexului şi este foarte utilă în special la exportul String într-un mediu ce nu suportă 16-biţi Unicode. Protocoalele Internet folosesc 8-biţi ASCII pentru text. Faţă de această metodă, apar particularităţi de xtragere a caracterelor la metodele: toCharArray, care întoarce un tablou de caractere pentru întregul şir; getBytes care ia caractere dintr-un tablou de biţi, în ordinea apariţiei acestora şi este similară cu getChars cu deosebirea că al treilea parametru este tablou de byte. 1.6.3. Comparaţia şirurilor
28
Comparaţia prin egalitate la tipul string se realizează cu metodele equal sau equalIgnoreCase. Metoda regionMatches este folosită pentru compararea unei regiuni specifice din interiorul unui şir cu o altă regiune specifică din alt şir, ignorând sau nu UpperCase şi LowerCase. Prototipul funcţional este: boolean regionMatches (int tooffset, String altsir, int aoffset, int len); boolean regionMatches (boolean ignoreCase, int tooffset, String altsir, int aoffset, int len); unde int tooffset - regiunea specifică String altsir al doilea şir int aoffset - regiunea specifică din al doilea şir int len - dimensiunea de comparaţie. Cele două şiruri sunt comparate pe dimensiunea len caractere, începând de la cele două offset-uri specificate. Pentru verificări speciale impuse şirurilor se folosesc două metode pereche:startsWith, care verifică dacă şirul începe cu parametrul transmis şi endWith, care verifică dacă şirul se termină cu parametrul transmis şi sunt deosebit de utile în programarea appleturilor.[Chan_95] Exemple: "Categorie.endsWith("ie") returnează true "Categorie.startsWith("Cat") returnează true "Categorie.startsWith("eg",3) returnează true Egalitatea dintre şiruri poate fi testată fie cu metoda equal, fie cu operatorul ==, deosebirea fiind fundamentală deoarece metoda equal compară caracterele din interiorul tipului String pe când operatorul == compară două referinţe obiect. Exemplu: class DemoEgalSir() { public static void main ( String args [ ] ) { String s1= "Exemplu de utilizare a metodei equal"); String s2= new String(s1); System.out.println(s1+"egal cu sirul s2"+s2+'->"+ s1.equal(s2)); System.out.println(s1+"==cu sirul s2"+s2+'->"+ (s1==s2)); } } In exemplul de mai sus, s1 este creat ca instanţă de şir iar s2 este creată prin construtor String care ia intrarea şi o întoarce caracter. Fiind obiecte distincte ele nu sunt == dar sunt equal. Ordinea întrun şir este determinată cu metoda compareTo. Exemplu: class DemoBubbleSortlSir() { static String arr[ ] = {"acum", "facem","ordonare", "prin", "comparatie", "de",..} public static void main ( String args [ ] ) { for (int j=0, j
arr[i]=t; } System.out.println(arr[j]); } } } Căutarea în şir se poate face cu indexOf sau lastIndexOf care sunt supraîncărcate în orice mediu, pentru a căuta caractere sau subşiruri, începând de la offset până la sfârşitul sursei String. Exemple de utilizare: int indexOf ( int ch); int lastIndexOf ( int ch); int indexOf ( String str); int lastIndexOf ( String str); int indexOf ( int ch, int index); - cautare de la indexul precizat int lastIndexOf ( int ch, int index); int indexOf ( String str, int index ); int lastIndexOf ( String str, int index); Modificatorii de copiere ai şirurilor De vreme ce String nu este modificabil, pentru a realiza totuşi operaţii de modificare, se copiază în StringBuffer sau se foloseşte una din metodele String de construire a unei noi copii, cu modificări complete. Metodele uzuale folosite sunt: • substring permite extragerea unui subşir: • concat crează un nou obiect prin concatenare; • replace ia două caractere ca parametri şi toate apariţiile primului caracter din şir sunt înlocuite cu al doilea caracter; • toLowerCase şi toUpperCase converteşte şirurile; • valueOf este folosită pentru acele tipuri ce pot fi converite în şir. Metoda este apelată de operatorul + al tipului String, prin metoda append a lui StringBuffer şi este practic echivalentă cu toString. Exemple: "Pentru un sir".substring(4) 1.7. Clase frecvent utilizate în programare 1.7.1. Clasa Math Această clasă oferă o gamă diversificată de metode necesare efectuării operaţiilor de bază la nivelul tipurilor int, double, float, etc.Fără a intra în detalii de funcţionare, cunoscute de altfel la limbajele de generaţia a treia şi a patra, prezentăm câteva funcţii utile:[Naugh_96] Funcţiile trigonometrice sunt realizate cu: sin/cos/tan (double a) - sinus, cosinus, tangentă de unghi exprimat în radiani; asin/acos/atan (double a) - arcsinus, arccosinus, arctangentă; atan2 (double a, double b) - arctangentă(a/b); Funcţii exponenţiale şi logaritmice: 30
pow(double x, double y);- puterea unui număr yx Exemplu: x=pow(2,3); //x=8 exp (double x); log (double x); sqrt (double x); Rotunjirea numerelor se realizează cu funcţiile: ceil (double x); //cel mai mic întreg >=a floor (double x); //cel mai mare număr <=a rint (double x); //valoarea trunchiată double a lui a round (float x); //valoarea trunchiată a lui a round (double x); //valoarea trunchiată double a lui a Alte funcţii matematice des utilizate: abs(a); //valoarea absolută min (a,b); //valoarea minimă dintre a şi b max (a,b); //valoarea maximă dintre a şi b 1.7.2. Clasa Random Random este clasa ce generează 5 tipuri de numere aleatoare care pot fi extrase din obiectul Random:[java_1] int, uniform distribui în rang int, extras cu metoda nextInt; long, uniform distribui în rang long, extras cu metoda nextLong; float, uniform distribui în rang float, extras cu metoda nextFloat; double, uniform distribui în rang double, extras cu metoda nextDouble; nextGaussian, care întoarce distribuţia Gaussiană double, centrată 0.0, cu deviaţie standard de 1.0 (curba clopot- bell curve); 1.7.3. Clasa Date Clasa Date este utilizată la reprezentarea orei şi/sau timpului şi are câţiva constructori de bază: Date(year, month, date); Date(year, month, date, hours, minutes); Date(year, month, date, hours, minutes, seconds). Metodele implementate getYear, getMonth, getDate, getDay, getHours, getMinutes, getSeconds întorc valori întregi date de anul/luna/data/ziua/ora/minutul/secunda curentă iar metodele setYear, setMonth, setDate, setDay, setHours, setMinutes, setSeconds iau valori întregi ca parametru şi permit setarea datei. Reprezentarea long poate fi realizată prin getTime iar comparaţia dintre două obiecte de tip Date se face cu una din metodele: Boolean before (Data cand); Boolean after (Data cand); Boolean equals (alta Data ). 31
Obiectele Date pot fi convertite în String prin metoda toString sau toLocalString respectiv toGMTString. Dacă se doreşte obţinerea unui offset în minute faţă de GMT se foloseşte getTimezoneOffset. 1.7.4. Clasa Runtime Runtime este clasă încapsulată în procesul de rulare a interpreterului Java. Practic nu se poate instanţia Runtime, se pot doar referi obiecte ce rulează prin metoda statică Runtime.getRuntime. Sunt câteva metode de control a stării şi comportamentului maşinii virtuale prin SecurityException. Metoda exit (int cod) dă o ieşire cu codul erorii întâlnmite la execuţia programului.[java_8] Managementul memoriei se face prin GC iar metodele totalMemory() şi freeMemory() din Runtime dau memoria totală ocupată respectiv memoria liberă. Metoda exec() permite apelul altor programe cu nume specificat la fel ca şi parametri de intrare. Problema este că programul se execută independent de sistem. Metoda exec() întoarce obiectul Process care poate fi utilizat la controlul modului în care programul java interacţionează cu noul proces executabil. Exemplu: class ExeDemo { public static void main (String args [ ] ) { Runtime s = Runtime.getRuntime(); Process p=null; Sting cmd[ ] = {"notepad", "java/lang/Runtime.java"}; try { p=r.exec(cmd); } catch (Exception e) { System.out.printn ("eroare la execuţie"+cmd[0]); } }} Procesul returnat de exec poate fi manipulat după ce noul program a început rularea şi se poate distruge procesul cu metoda destroy(). Metoda waitfor() determină aşteptarea programului până când a terminat subprocesul iar metoda exitValue() întoarce valoarea de ieşire din subproces ( număr natura sau 0 dacă ieşirea a avut loc fără probleme).
2. Programarea orientată obiect în Java 2.1.Clasele Java 2.1.1. Caracteristicile claselor în Java Arhitectura limbajului Java este bazată pe metodologia orientată spre obiecte. Inafară de tipurile fundamentale (int, float, char, boolean), în Java totul se reprezintă prin obiecte denumite clase. Prin încapsularea caracteristicilor limbajului în clase, Java devine un mediu extensibil în care programatorii pot folosi rutinele din alte programe sau aplicaţii. Limbajul Java evalueaza structura
32
claselor în mod dinamic în momentul executiei şi datorită dinamicii, bibliotecile de clase pot fi reutilizate cu uşurinţă. Moştenirea multiplă - caracteristica specifică limbajelor orientate pe obiecte nu este prezentă în limbajul Java dar pentru a suplini acest neajuns, limbajul Java pune la dispoziţia utilizatorilor o facilitate numită interfaţă, care permite definirea unui anumit comportament pentru o clasa de obiecte, altul decât cel definit de clasa de bază. Implementarea unui obiect în Java poartă denumirea de clasă iar declaraţia unei clase este văzută ca un model pentru crearea obiectelor. Ulterior declarării unei clase, aceasta se poate utiliza pentru crearea mai multor obiecte de tipul respectiv sau se pot defini în program noi tipuri de variabile iar dacă tipurile fundamentale sunt predefinite, tipurile noi se pot crea după nevoia utilizatorului. Clasa Object
Clasa 1
Clasa 11
Clasa n
Clasa 1m
Clasa n1
Clasa np
Figura 2.1. Reprezentarea ierarhiei de clase în Java Limbajul Java pune la dispozitie metode şi operatori pentru crearea variabilelor de clasă, dar este necesară definirea unei metode de creare a clasei şi ulterior se poate crea o altă clasa că şi copie a obiectului (adică a definiţiei de clasă) utilizând operatorul new. [Lema_98] Metoda de creare are acelaşi nume cu numele clasei şi nu returnează nici o valoare dar prelabil apelului metodei de creare trebuie alocată memoria necesară instanţierii şi initializată cu valorile specificate sau implicite. Pentru a crea o clasă care foloseşte o altă clasă (initial construită), este necesară o instanţiere a clasei cu operatorul new. Exemplu: clasa varClasa= new clasa() unde clasa este numele clasei, varClasa este numele variabilei care se declară, iar clasa este constructorul variabilei respective. In continuare, variabila varClasa va putea conţine o referintă la un obiect de clasa clasa sau la un obiect derivat din clasa creată.[Lema-96] La executia unui applet sau o aplicaţie de sine stătătoare într-un browser, în mediul Java se creeaza o instanţiere (adica o variabilă) a clasei încărcate. Toate clasele in limbajul Java sunt descendente dintr-o clasa superioară numită Object, clasele proprii putând fi create prin derivare sau moştenire din Object sau din orice altă clasa, care la rândul ei moşteneste direct sau indirect clasa Object. In figura de mai jos se prezintă structură ierarhică a claselor din mediul Java. Pe fiecare ramură din ierahia de mai sus, subclasa moşteneşte proprietăţile superclasei şi astfel, chiar dacă o clasă nu succede direct din clasa Object, ci în urma unei şir de succesiuni, această 33
clasă este totusi o subclasă a lui Object. Clasa Object este singură clasa care nu are o superclasa şi poate fi considerată ca un tip fundamental dar dacă pentru clasă nu se declară explicit ascendentul, în mod implicit acea clasă este o succesoare directă a clasei Object.[Naugh-96] In Java declararea unei clase se realizază prin: [modificatorii clasei] Class nume [ extends superclasa ] implements Interfaţa1, interfaţa2, …, interfaţaN { //corpul clasei (cu declaraţii de variabile şi metode) } Dacă se declară o clasa cu public class NumeClasa, această clasă nou creată va fi o subclasă a clasei obiect, deoarece nu este specificată în declaraţie clasa din care este derivata. Clasa se declară public pentru ca apoi să fie plasată intr-un pachet şi exportata. In cazul in care clasa este declaraţă in acelasi fişier in care este şi utilizata, declaraţia public nu este necesară. După crearea unei clase este necesară definirea variabilelor clasei. Pentru fiecare variabila din interiorul unei clase, se va specifica tipul precum şi identificatorul variabilei după modelul: tip nume. In clase se includ şi metode, pentru a realiza anumite calcule, iar rezultatul să poată fi returnat sub forma unei valori de tipul tipReturnat, conform declaraţiei de metodă: TipReturnat nume(parametru1, parametru2,…) { // corpul funcţiei } In alte cazuri o metoda indică faptul ca s-au incheiat cu succes anumite operaţii, nefiind necesară returnarea unui rezultat iar o astfel de metodă (care nu returnează o valoare în urma execuţiei), are în definiţie cuvântul void. Alături de metoda de creare există şi o metoda de finalizare care are rol de a închide fişierele care au fost deschise şi de curăţare a obiectului pentru care este finalizator, pentru ca ulterior colectorul de reziduri să elimine obiectul din memorie. Nu există nici o garanţie că limbajul Java va invoca metoda de finalizare definită, considerand metoda ca şi o optimizare a codului, motiv pentru care este indicat ca aplicaţia să nu depinda prea mult de această metodă pentru a evita anomalii în execuţie. Pentru a se şterge un obiect, trebuie apelată metoda de finalizare care distruge variabila respectivă, şi ulterior se apeleaza colectorul de reziduri. In limbajul Java nu este necesară apelarea în mod implicit a distrugerii unei variabile de clasă, deoarece sistemul are un mecanism de colectare a rezidurilor care descoperă situaţiile în care o variabilă de clasa ( sau un obiect) nu mai este referită şi o distruge automat. Se recomandă însă ca metoda de finalizare să fie apelată explicit pentru a nu apărea manifestări accidentale în execuţie. 2.1.2. Modificatorii clasei Aceesibilitatea clasei şi informaţiile despre variabilele şi metodele definite în clasă sunt date prin modificatorii clasei. Dacă aceştia sunt omişi în întregime, obiectele din clasă sunt definite şi accesate de alte clase din acelaşi pachet iar această clasă poate avea la rândul ei subclase în acelaşi pachet. Sunt cunoscuţi trei modificatori de clasă: public, abstract şi final. O clasă declarată public este global accesibilă, ceea ce însemnă că orice clasă definită în acelaşi pachet poate accesa obiectele din această clasă. Dacă nu este declarată public, accesibilitatea se limitată la nivelul pachetului sau claselor din care derivă. Dacă o clasă este declarată public, ea
34
trebuie să fie publică în codul sursă şi mai mult, numele fişierului sursă trebuie să fie acelaşi cu numele clasei public, cu extensie java. Exemplu public class Clasa1 { Corpul clasei …} Observaţie: In fişierul Clasa1.java există doar o clasă declarată public. In general se declară o clasă public, atunci când se doreşte a fi global accesibilă, astfel încât orice applet public permite utilizatorului care navighează în pagină, să îl execute. Suplimenar se pot declara pachete relative la clase, ce au intenţia de a furniza funcţionalitate generală, adesea pentru a fi reutilizate în programe diferite. Pentru a face clasele accesibile în exteriorul pachetului, acestea trebuie declarate public. Totuşi nu toate clasele dintr-un asemenea pachet pot fi utilizate în exterior; unele sunt destinate doar uzului intern deşi se poate declara o clasă public, care poate fi invizibilă în exteriorul pachetului. O clasă abstract este cea care conţine una sau mai multe metode abstract. Uneori se declară o metodă în clasă fără a preciza detaliile interne ce presupun metode de implementare diferite pentru fiecare subclasă. Spre exemplu, dacă declarăm clasa java.awt.Image, are sens să se definească metoda de afişare pentru fiecare tip de format al fişierului (JPEG,GIF,BMP, etc.) pentru că este cunoscut faptul că există în plus informaţii legate de dimensiunea fişierelor, care determină implementări diferite. Intr-un asemenea caz este necesară structurarea definiţiilor acestei metode ale subclaselor. Pentru a realiza acest lucru, se lasă definiţia blank şi se declară metoda abstractă. Dacă o clasă conţine una sau mai multe metode abstract, atunci clasa însuşi se declară abstract, pentru a evita erori de complilare. Nu trebuie instanţiat direct un obiect din clasa abstract. Pentru a-l utiliza este necesară crearea unei subclase ce include definiţiile pentru metodele lăsate blank în clasa abstract, iar această clasă trebuie apoi instanţiată. Clasele abstract permit implementarea polimorfismului pur, în care aceste clase abstract nu pot fi ele însele instanţiate ci au rol de a servi ca şi clase de bază pentru un grup de clase aflate în relaţie. Aceste clase de bază sunt generice astfel încât este greu sau aproape imposibil de definit detaliile sau comportamentul acestora. Delatiile sunt lăsate subclaselor şi fiecare subclasă implementează comportamente diferite. De aceea se începe fără implementări finalizând apoi cu implementări distincte, proprii fiecărei subclase. Clasele abstracte se pot declara public sau fără acces implicit, caz în care sunt vizibile doar în interiorul pachetului.[Ramb_96] Clasele final nu pot fi subclase, sunt opuse lui abstract, motiv pentru care nu pot fi simultan final şi abstract pentru că generează erori de complilare. Există două motive pentru care se folosesc aceste clase: eficienţa şi posibilitatea de a extinde metodele definite în clasele părinte. De aceea când o metodă dintr-o clasă este apelată, nu este posibil să se determine la momentul compliării ce versiune a metodei se execută, deoarece versiunea metodei definită în clasă poate fi extinsă într-o subclasă, cu acelaşi nume şi tip de parametri. Acest lucru trebuie determinat dinamic, în timpul execuţiei de fiecare dată când se rulează acea secţiune de cod, depinzând de segmentele generate când obiectul este instanţiat.
35
De fiecare dată când se fac determinări, se poate solicita încărcarea unei clase ce nu a fost anterior rezidentă în memorie. Aceste performanţe se asociază cu costul încărcării clasei. Cum o clasă finală nu are subclase, pentru a garanta faptul că aceste metode nu vor fi estinse, înstrucţiunile ce le compun sunt incluse în codul compilat, eliminând necesitatea de a lua o decizie şi posibilitatea încărcării unei noi clase, în timp ce programul rulează. Al doilea motiv de declarare a acestor clase final este dat de securitatea şi claritatea asigurată, deoarece se poate specifica o clasă cu comportament cert, se poate dori ca nimeni să nu aibă posibilitatea de modificare a comportamentului unei clase, extinzându-I metodele. Declarând-o final, se previn subclase asociate, respectând funcţionalitatea şi securitatea ei.[java_8] Numele clasei este identificator uni în acelaşi pachet. La importul claselor din alte pachete, numele claselor din pachetul propriu nu trebuie să intre în conflict cu numele celor din pachetele importate. Prin convenţie, programatorii Java scriu primul caracter din clasă cu majuscule, pentru a asigura numele clasei şi variabilelor distincte, făcând codul uşor de citit. 2.1.3. Extensiile superclaselor Clasele extends specifică clasa din care se moştenesc, fiind superclase imediate sau clase părinte. O subclasă estinde funcţionalitatea clasei părinte, în Java existând doar moşteniri simple. Clasele final nu apar în clase extends iar în absenţa clauzei extends, clasa moşteneşte implicit caracteristicile clasei Object (rădăcina tuturor claselor). Moştenirea, conceptul central OOP este implementat în Java cu un puternic mecanism de control ceea ce însemnă că se pot specifica acele caracteristici ale clasei, care vor fi/ nu vor fi vizibile în subclase şi acele metode care pot/ nu pot fi extinse. Implicit o clasă moşteneşte toate câmpurile (variabile şi metode), exceptând constructorii, din superclasă. Inafara constructorilor, variabilele şi metodele declarate private, vor fi invizibile oricărei clasă ce o moşteneşte. implements Interfata1, Interfata2,…InterfaţaN Mecanismul interface oferă unele funcţionalităţi ce permit implementarea moştenirilor multiple, evitând caracteristici circulare şi alte elemente periculoase ce implică moştenirile multiple. [An_96] O clasă poate extinde doar o superclasă dar poate implementa oricâte interfeţe (nelimitate ca număr). Interfeţele se specifică în declaraţia de clasă după cuvântul rezervat implements ca şi listă separată prin virgulă.Clauza implements este opţională, prezentă doar dacă se implementează interfeţe. 2.1.4. Supraîncărcarea metodelor Studiu de caz: Creăm o subclasă Point3D care moşteneşte implementările distanţei dintre două puncte din superclasă (Point), pe care o supraîncarcă2 apoi. Exemplu: Class Point() int x,y; Point( int x, int y){ this.x=x; this.y=y; } 2
Sursa: Java Handbook 36
double distance(int x, int y){ int dx=this.x-x; int dy=this.y-y; return Math.sqrt(dx*dx+dy*dy); } double distance(Point p){ return distance(p.x,p.y); } } class Point3D extends Point { int z; Point3D (int x, int y, int z) { super(x,y); this.z; double distance(int x, int y, int z){ int dx=this.x-x; int dy=this.y-y; int dz=this.z-z; return Math.sqrt(dx*dx+dy*dy+dz*dz); } double distance (Point3D other){ return distance(other.x, other.y, other.z); } double distance(int x, int y){ double dx=(this.x/z)-x; double dy=(this.y/z)-y; return Math.sqrt(dx*dx+dy*dy); } } class Point3Ddist { public static void main (String args[ ])[ point3D p1= new Point3D(30,40,10); point3D p2= new Point3D(0,0,0); Point p= new Point(4,9); System.out.println ("p1 este "+p1.x,p1.y,p1.z); System.out.println ("p2 este "+p2.x,p2.y,p2.z); System.out.println ("p este "+p.x,p.y); System.out.println (" distanta p1-p2"+p1.distance(p2); System.out.println ("p1.distance(8,5)"+p1.distance(8,5)); System.out.println ("p1.distance(p)"+p1.distance(p)); } } Definind punctele p1 şi p2 tridimensionale, am solicitat distanţa între un punct tridimensional şi unul bidimensional apelând distanţa din 3D cu un obiect de tip Point din 2D şi executând metoda distance moştenită din Point, deoarece Poit3D nu supraîncarcă distance(Point p) ca metodă în Point. Apelul distance(int x, int y ) se face din Point3D şi nu din Point (2D) dat fiind faptul că în 37
Java, metoda este selectată bazându-se pe tipul instanţei la un moment dat şi nu pe clasa în care metoda curentă este executată. 2.1.5. Polimorfismul dinamic Când se apelează la o metodă utilizând operatorul punct (.) şi referinţa obiect, declararea tipului obiectului referit este verificată la compilare, pentru a se asigura că metoda apelată există în declaraţia de clasă. In rulare, obiectul referinţă poate fi referit ca şi o instanţă a unei sublclase a tipului de referinţă declarat. In acest caz, Java foloseşte actuala instanţă pentru a decide ce metodă apelează evenimentul pe care subclasa supraîncarcă metoda ce a fost apelată3. [Hoff_96] Exemplu: Presupunem două clase care au o superclasă cu o singură metodă supraîncărcată în clasă. class A { void apel(){ System.out.println.("Metoda de apel a lui A"); } } class B extends A { void apel(){ System.out.println.("Metoda de apel a lui B"); } } class disp () { public static void main(String args [ ]){ A a = new B(); A apel(); } } Această formă de polimorfism dinamic al execuţiei este cel mai puternic mecanism pe care îl oferă programarea OO atât pentru codul reutilizat cât şi pentru robusteţea programelor. In clasa disp definim metoda main() în interiorul căreia se declară variabila a de tip A, care memorează o referinţă la o instanţă din clasa B. In linia următoare apelăm apel() ca metodă a lui a dar când compilatorul Java verifică dacă a are această metodă rezultatul este corect. In schimb Java runtime notează că referinţa este de fapt instanţa lui B şi astfel cheamă metoda apel a clesei B. Vom obţine astfel: Metoda de apel a lui B 2.1.6. Finalizarea In timp ce Java runtime este un sistem colector de deşeuri (garbage collector system) nu este necesar managementul memoriei însă apar unele situaţii de excepţie în care se impune efectuarea unui cod special atunci când un obiect este solicitat de garbage collector. Spre exemplu dacă o clasă foloseşte resurse non-Java ( spre exemplu: fişiere manipulate sau fonturile ferestrei sistem) 3
Dynamic method dispatch 38
trebuie utilizată metoda de finalizare pentru a ne asigura că sunt eliberate toate resursele. Această metodă funcţionează similar cu destructorul din C şi nu se poate confunda cu final. Rezolvarea acestor excepţii se face adăugând metoda finalize în orice clasă caz în care Java runtime o apelează de oricâte ori se reclamă eliberarea saţiului pentru un obiect. Metoda trebuie să ia explicit orice acţiune solicitată pentru a elibera resursele deoarece garbage collector rulează periodic, verificând obiectele care nu mai sunt referite de alte stări în lucru, sau referite indirect, prin alte obiecte. Colectorul de reziduri elibereaza resursele de memorie utilizate de obiectele pe care le distruge dar obiectele mai păstrează şi alte tipuri de resurse cum ar fi descriptorii sau socket-urile. Colectorul de reziduri nu poate elibera aceste resurse, astfel încât este necesară o metodă de finalizare proprie fiecarui obiect care să realizeze operaţii specifice cum ar fi închiderea de fişiere care ramân deschise accidental sau terminarea unei conexiuni în reţea. 2.2.Variabile şi metode 2.2.1. Variabilele In Java variabilele înmagazinează dată iar conform paradigmei OO înglobează starea unui obiect dat. Acestea sunt divizate în 2 categorii: variabile de instanţă, ce permit individualizarea obiectelor din clasă şi variabile statice, care se referă la toate obiectele dintr-o clasă dată. Fiecare obiect individual dintr-o clasă dată este numit instanţă sau instanţiere a clasei. Când se creează un obiect specific al unei clase, se instanţiază un obiect din clasă şi spaţiul este alocat pentru toate variabilele de instanţă ale obiectului. Fiecare obiect ce este instanţiat are un spaţiu alocat independent de variabilele de instanţă. Variabilele static, pe de altă parte, sunt create odată şi sunt folosite de toate obiectele din clasa dată şi de clasele moştenite. Nu are importanţă câte obiecte se creează dintr-o clasă, se face o copie a variabilelor statice ce aparţin clasei. Spre exemplu, în clasa java.lang.Integer, variabila MAX_VALUE este statică şi conţine valoarea maximă a unui întreg. Deoarece acest număr este unic, nu are importanţă câte obiecte întregi sunt instanţiate, se face o singură copie a acestei variabile şi astfel se alocă memorie minimă. Referirea variabilelor de instanţă a unui obiect se face prin: numele_instanţei_specifice.numele_variabilei_de_instanţă. Exemplu: In clasa Clasa1 definim variabile de instanţă por şi două obiecte care instanţiază clasa: ap2 şi ap3. Referinţa la variabilă se va face: ap2.por ap3.por Pentru a referi variabilele statice se apelează la numele_clasei.numele_variabilei. Exemplu: Pentru variabila statica suprafata vom apela: Clasa1.suprafata sau ap1.suprafata sau ap2.suprafata In definiţia de clasă varibilele statice şi de instanţă se pot referi şi fără calificare (suprafata, por, etc)
39
2.2.2. This, super şi null Fiecare obiect are încapsulate trei variabile: this, super şi null. This este pseudonimul obiectului însuşi dat fiind faptul că în interiorul definiţiei de clasă se pot referi instanţe ale clasei fără a specifica un obiect; this.n este spre exemplu o variabilă de instanţă a clasei în timp ce fără punct (n) este variabilă a metodei. Explicitarea this este necesară pentru a distinge între variabila locală n şi varibila de instanţă this.n. Super este pseudonimul obiectelor din superclasa imediată, numită în clauza extends. Deoarece variabilele din superclasă sunt moştenite în subclasă (dacă nu se limitează accesul prin private) se referă simplu fără calificarea ca variabile de instanţă a clasei locale. Totuşi este posibil să se declare ca variabilele de instanţă într-o subclasă, cu acelaşi nume ca şi variabilele de instanţă din superclasă. Variabila definită în subclasă "maschează" sau "umbreşte" variabila definită în superclasă. Pentru a o referi, se foloseşte cuvântul cheie super. Exemplu: Class SuperClasa { int n=10; // se iniţializează n } class Clasa1 extends Superclasa { int i,n; public void main Method (int n) { this.n=n; // necesar this, altfel apare conflict de nume i=super.n; //i=10 } } This şi super sunt folosite numai în interiorul metodelor non-statice. Inainte de a iniţializa un obiect el nu are valoare dar după iniţializare el are valoare implicită null. Există situaţii în care atribuirea explicită a valorii null este necesară pentru obiecte, în special la determinarea structurii dinamice de date ca arbori, liste, etc. Aceste structuri pot creşte şi se pot extinde fără limite iar valoarea null marchează sfârşitul structurii. Un alt caz este thread(firul) de execuţie, care se opreşte setându-l pe null. [Lema_98] Observaţie: Se poate atribui valoarea null obiectelor sin orice clasă dar nu şi metodelor şi variabilelor unor tipuri primitive de dată ( int, float, char, etc). Exemplu: String str; După această declaraţie str, este manipulat de obiectul String. Manipulatorul (handle) este similar cu pointerul C, C++. După declaraţie nu se alocă memorie lui str, considerat ca manipulator null. Handle nu are sens de "poiter la pointer" ca şi în C sau C++ ci mai degrabă ca referinţă la obiect şi nu implică adresă fizică. 2.2.3. Declaraţiile de variabile Declaraţia de variabile se realizează conform sintaxei: [modificatori de variabile] tip numevariabilă [=Valoare initială] Modificatorii de variabile specifică accesibilitatea variabilelor declarate la nivelul clasei. Implicit variabilele dintr-o clasă pot fi: Moştenite şi vizibile în orice clasă declarată în interiorul pachetului ce extinde această clasă. 40
Accesibile tuturor claselor din interiorul pachetului; Inaccesibile din exteriorul pachetului; Dacă nu există alte intenţii se indică specificând unul din cuvintele rezervate: public, protected, private, static, sau final. Variabila declarată public are aceeaşi aceesibilitate ca şi clasa dar nu depăşeşte accesibilitatea clasei. Intr-o clasă cu acces implicit variabila public este accesibilă numai în interiorul pachetului dar într-o clasă cu acces public varibila public va fi accesibilă oriunde.Variabilele public sunt moştenite în orice subclasă derivată din lcasa în care sunt declarate. Variabilele declarate protected sunt aceesibile tuturor claselor din pachetul ce conţine clasa în care sunt declarate. In exteriorul pachetului variabilele protected sunt accesibile doar subclaselor derivate din clasa în care sunt declarate.Variabilele protected sunt eliberate de restricţii implicite de moştenire dar reţin restricţiile de acees la clasele fără legătură din exteriorul pachetului . Observaţie: Spre deosebire de C++ unde variabilele protected sunt aceesibile nimai în clasa dată şi sublcasele ei, Java extinde conceptul la nivel de pachet, în interiorul căruia variabilele sunt aceesibile tuturor claselor, chiar dacă aceste clase nu derivă din clasa în care au fost create. Variabilele private sunt accesibile numai în clasele în care sunt declarate şi restricţiile se extind şi la clasele derivate. Variabilele static sunt instanţiate odată şi împărţite între toate obiectele clasei în care sunt declarate şi în toate subclasele moştenite. Există distincţie între variabilele statice (care aparţin clasi ca întreg) şi varibilele de instanţă care sunt create separat pentru fiecare obiect al clasei şi este instanţiat ca aparţinând obiectului. Toate variabilele ce nu sunt explicit declarate static sau final sunt variabile de instanţă.. Static poate fi combinat cu orice specificator de acces: public, protected, private sau implicit. Variabilele final sunt de fapt constantele şi de aceea la declararea lor se atribuie şi valoare, altfel valoarea nu mai poate fi modificată. Deseori se declară constante ca final static. Tip-ul poate fi tip primitiv, tablou, tip obiect, incluzând obiectele din această clasă. Nume variabilă începe de obicei cu literă mică.[java_1], [java_8] 2.2.4. Metodele Comenzile executate de Java sunt conţinute în metode, excepţie făcând blocurile statice. Toate metodele sunt aflate în clase şi esistă o strictă similitudine cu OO din C++, unde funcţiile pot exista independente de obiecte dacă programatorul doreşte. Blocurile statice conţin cod executabil în interiorul metodei şi sunt excepţii. Aceste coduri sunt folosite pentru a iniţializa variabilele statice ce nu pot fi complet iniţializate sau liniile de declaraţii (ca şi tablourile). Blocul static se referă doar la variabile statice şi la cele locale decarate în interiorul blocului. Exemplu: class Repar{ static double[] vcos= new double[100]; // variabile statice static { // blocul static for (int i=1, i<100, i++) { vcos[I]=Math.cos((double)i/100.0); } …} 41
} } Chiar variabila locală I este implicit statică deoarece este declarată în bloc static şi nu este accesibilă din esteriorul acestui bloc.In interiorul clasei Repar sunt date valori specifice pentru repar.vcos. 2.2.5. Declaraţiile de metodă O metodă se declară folosind sintaxa generală: [modificatoriMetodă] Tiprezultat numeMetodă([listă argumente]) [Throws Exceptia1, …ExceptiaN { … //corpul metodei } Modificatorii metodei pot fi public, protected şi private, cu aceeaşi semnificaţie ca şi modificatorii de variabilă. Apar câţiva modificatori adiţionali, definiţi pentru metode şi în absenţa modificatorilor accesul implicit este similar cu cel al variabilelor. Metodele fără modificatori au câteva caracteristici: sunt moştenite de orice clasă declarată în interiorul pachetului, vizibile în orice clasă şi disponibile pentru a fi supraacoperite în orice clasă ce o extinde în interiorul pachetului şi sunt inaccesibile în exteriorul pachetului Metodele static sunt în acord cu noţiunea de OO încapsulat şi constau în general din metode de instanţiere care au acces la variabilele de instanţă ale unui obiect instanţiat în particular.Metodele de instanţiere sunt invocate folosint numele obiectului ca şi codificator. Exemplu: Obiect.MetodaLui(argumente); Metodele statice la fel ca şi variabilele statice sunt instanţiate odată pentru toate obiectele din clasă şi de aceea nu pot referi variabile de instanţă, care aparţin obiectelor specifice, nu clasei ca întreg.Metodele statice pot accesa variabile statice şi sunt implicit final fără a putea fi supraîncărcate de clase derivate. Invocarea se face cu numele clasei ca şi calificator sau numele obiectului instanţiat ca şi calificator de clasă. Exemple: Metoda static din java.lang.String String unSir= new String(); String altSir= new String(); UnSir =String.valueOf(5);// echivalente UnSir =UnSir.valueOf(5);// echivalente UnSir =altSir.valueOf(5);// echivalente Metodele statice nu pot utiliza this şi super şi sunt împărţite de toate instanţele clasei, motiv pentru care nu aparţin unei instanţe specifice.Referind o metodă cu this pot fi sute de obiecte ce instanţiază clasa şi nu se poate determina la care se face referire. La super, care este nivelul moştenirii la care se află metoda? Care clasă e superclasă?- Intrebări fără răspuns. De aceea metodele statice nu pot face referire la câmpuri non-statice. Exemplu: public class Test1{ 42
String str="un sir"// constructor apelat implicit public static void main (String args[ ]) { System.out.println (str);//eroare, str nu este variabilă non-statică System.out.println (this.str);//eroare, nu se foloseşte this în metodă } } Metoda final nu poate fi acoperită de clase derivate şi se poate combina cu public, private, protected sau static sau specificată însuşi cu combinaţiile acestora, în concordanţă cu accesul implicit precizat. Metoda abstact este declarată dar are implementări diferite în subclase, având inteles contextual. Ea nu poate fi combinată cu metode ce restrâng moştenirea sau acoperirea (private, static sau final) iar deoarece un constructor nu poate fi moştenit, nu poate fi final. Native este metodă proprie în esenţă un apel exterior la funcţii scrise în alt limbaj de programare (C, C++, etc.). Dearece Java este multithread (multifir) este necesară furnizarea mecanismului de control al concurenţei pentru că sunt câteva operaţii care se execută simultan iar alte operaţii (citire, scriere de date comune implică manipulări delicate. Declarând metoda sycronized se garantează că doar un fir va fi executat la un moment dat.[Naugh_96] 2.2.6. Transferul argumentelor Apelul unei metode presupune şi transferul parametrilor (argmentelor) care sunt substituiţi în interiorul metodei în timpul execuţiei acesteia. Parametri de tip primitiv (int, float, char, boolean) sunt transferaţi prin valoare, ceea ce înseamnă că se crează o copie locală cu acceaşi valoare ca parametrul în interiorul metodei iar în exteriorul metodei nu se resimt modificările făcute în copia locală. Obiectele de tip tablou sunt transferate prin referinţă, ceea ce înseamnă că în interiorul metodei nu există o copie locală ci poate fi chemat prin valoare. Modificările afectează obiectul actual şi de aceea ele persistă şi după metodă. [An_96] La declararea metodei se cunosc explicit tipurile de parametri ce urmează a fi primiţi din lista de argumente a metodei. In OOP nu este necesară precizarea tuturor informaţiilor solicitate în lista de parametri deoarece metoda ştie deja despre ele şi are acces la toate variabilele de instanţă precum şi la cele statice în care este definită.In lista de termeni se precizează doar acele informaţii la care metoda nu are acces, ca şi instanţele variabilelor pentru alte obiecte, care pot fi diferite de fiecare dată când metoda este apelată. Exemplu: class Fargum{ double d=3.14; public void fMetoda (double d, Fargum other){ x=x*2.0; d=other.d; other.d=0.0; //efect global } } pblic class Principala { public static void main(String args[ ] ){ double noud=28.8; 43
Fargum nouFargum= new Fargum; Fargum altFargum= new Fargum; nouFargum.d=14.4; altFargum.d=19.2; System.out.println ('Inainte de apel"); System.out.println ("noud "+noud); System.out.println ('nouFargum.d "+ nouFargum.d); System.out.println ('altFargum.d "+ altFargum.d); //apel metodă nouFargum.fMetoda (noud, altFargum); System.out.println ('După apel"); System.out.println ("noud "+noud); System.out.println ('nouFargum.d "+ nouFargum.d); System.out.println ('altFargum.d "+ altFargum.d); } } Rezultatele obţinute: Inainte de apel noud 28.8 nouFargum.d 14.4 altFargum.d 19.2 După apel noud 28.8 nouFargum.d 1105.92 altFargum.d 0 Când se apelează nouFargum.fMetoda() nu este necesar transferul lui d pentru că obiectul nouFargum nu-l reclamă, dat fiind faptul că d este variabilă de instanţă a clasei Fargum şi orice instanţiere a clasei cunoaşte valoarea lui d. Mofificările făcute asupra lui x în interiorul metodei nu au efect în noud pentru că ea este de tip primitiv şi se transferă prin valoare.Modificări făcute asupra lui other.d au efect global asupra stării obiectului altFargum pentru că el este transferat ca orice obiect prin referinţa sa . 2.2.7. Metodele statice De multe ori se doreşte crearea unei metode utilizată în exteriorul contextului oricărei instanţe, caz în care se folosesc metodele statice, care pot apela direct doar metode static şi nu pot fi referite ca this şi super .In cadrul acestor metode se declară doar variabile static. Exemplu: class Static { static int a=3; static int b; static void metoda (int x) { System.out.println ("x="+x); System.out.println ("a="+a); System.out.println ("b="+b); } 44
static { System.out.println ("Initializarea blocului static"); b=a*8; } public static void main (String args[] ){ metoda (33); } } Un alt mod de a utiliza static este crearea metodelor ce pot fi apelate direct, referind numele clasei în care sunt declarate. Similar cu apelul metodelor de instanţă prin variabilele de referinţă ale obiectului, se apelează metoda static pentru a referi variabile static, utilizând . (punct) în numele clasei. Exemplu: class StaticClass { static int a=23; static int b=90; static void apel () { System.out.println ("a="+a); } class StaticprinNume { public static void main (String args[] ){ StaticprinNume.apel (); System.out.println ("b="+StaticprinNume.b); } } Modificatorii sunt cuvinte cheie ale limbajului Java care specifică proprietăţi speciale pentru unele variabile sau metode. Un astfel de modificator este cuvântul rezervat static iar variabilele şi metodele declarate static într-o clasă, sunt acelaşi pentru toate clasele, adică pentru toate variabilele de tipul acelei clase. Variabilele statice pot fi accesate fără a fi nevoie o instanţiere a clasei respective şi analog, nici metodele statice nu au nevoie de o instanţiere a clasei pentru a fi folosite. Metodele statice pot utiliza variabile statice declarate în interiorul clasei. 2.2.8. Supraîncărcarea metodelor Semnătura unei metode este definită prin lista de argumente specificată (număr, ordine şi tip) al fiecărui argument.Metodele se disting prin prin combinaţii de nume şi semnătură. In Java, la fel ca şi în C++, este posibilă crearea metodelor supraîncărcate (overloading), care au acelaşi nume şi semnături diferite. Deseori este utilă oferirea unor funcţionalităţi similare pentru diferite tipuri de argumente, ceea ce permit metodele cu acelaşi nume iar când acestea sunt apelate, nu apar ambiguităţi la execuţie, pentru că metodele sunt distinse prin semnătură. Exemplu: In clasa java.lang.String sunt metodele: public static String valueOf(int i) {…} public static String valueOf(float i) {…} Metoda String valueOf(i) ia de fiecare dată tipul de parametru transferat şi în converteşte în şir. Este normal ca paşii ce se execută la conversia întregului să difere de cei executaţi a 45
conversia float. Compilatorul foloseşte semnătura pentru a determina versiunea ce trebuie executată. Metoda supraîncărcării este mecanismul prin care Java furnizează polimorfismul adhoc în care compilatorul decide versiunea metodei prin semnătura ei, la momentul apelului metodei, în contrast cu polimorfirmul pur, furnizat de clasele abstracte. [Chan_96] Metode supraacoperite (overriding) In multe aplicaţii este utilă existenţa unei subclase care îşi defineşte propria versiune a metodei date, cu acelaşî nume şi semnătură ca şi în superclasă, ceea ce reprezintă în fapt supraacoperirea. Noua metodă din subclasă se numeşte metodă overriding (de supraacoperire) şi metoda din superclasă este overridden (supraacoperită).Metoda supraacoperirii oferă moştenirii flexibilitatea necesară deoarece prin aceasta se reţin acele metode care sunt potrivite subclasei şi furnizează o nouă versiune particulară pentru acea implementare. Clasele abstract se bazează pe această metodă pentru a furniza polimorfismul pur. Mai mult, orice metodă non-statică sau non-finală dintr-o metodă non-statică sau clasă non-finală poate fi supraacoperită (când clasa are subclase). 2.2.9. Valoarea întoarsă de metode Toate metodele cu exceptia constructorilor, trebuie declarate cu valoare întoarsă şi în caz contrar cu void, deoarece în Java, omiterea tipului întors este o eroare, spre deosebire de C, C++ care declară implicit tip int. Spre deosebire de metodele care întorc void, instrucţiunea return se include în corpul metodelor în orice punt de execuţie; aceasta opreşte execuţia metodei şi redă controlul metodei apelante. Compilatorul se asigură de acest lucru şi trateaza fiecare if..then…else din metodă pentru a se asigura că pe orice ramură metoda este terminată. Exemplu: Class MulteIntoarceri { void maIntorc(String s){ if s.equals("M-am intors"){ return;//intoarcere rapida } …// alte instructiuni return //optional } int eronat (Boolean b, Boolean t} { int i; double d; if (b) { … return; //eroare, trebuie intors int }else if (t){ … return d;// eroare, d este double }else if (i>0t){ return i;//bine }else{ 46
… // lipseste return pentru ca nu este declarat void } } } Numele metodei urmează regulile anterior descrise şi se recomandă scrierea cu literă mică pentru a o distinge de constructorii apelaţi. Lista de argumente se include între paranteze, solicitate chiar în lipsa parametrilor, pentru a fi distinse de declaraţii de variabile şi metode. Fiecare parametru este precedat de tip Exemplu: void diM(String s, int[ ] unTablou, double altTablou[ ]) //sir, tablou de intregi, tablou de reale duble In general se aşteaptă ca excepţiile să fie rare iar în lipsa lor throws poate fi omis. In rest fiecare exceptie tratată este separată prin virgulă. 2.3. Constructorii Declaraţiile pure ale unui obiect nu alocă spaţii pentru variabile de instanţă ale acestora. Când un obiect este delcarat, se instanţiază valoarea null. Instanţierea obiectului se face cu operatorul new. Exemplu: Contul c=new Contul(); Constructorul este în fapt apelul unei metode speciale definită în orice clasă şi acesta împarte acelaşi nume cu clasa în care este definit, având rolul de a iniţializa variabilele de instanţă ale unui obiect, atunci când obiectul este iniţializat.[Lema_98] Faţă de alte metode, constructorii posedă anumite caracteristici speciale: Nu au valoare întoarsă, implicit întorc tipul void, motiv pentru care în interiorul constructorului nu este necesară instrucţiunea return; Nu sunt moşteniţi de subclase şi fiecare clasă utilizează propriul constructor, cu acelaşi nume ca şi clasa.Chiar în lipsa moştenirii, este posibil ca în prima linie a constructorului să aibă loc apelul explicit al constructorului de superclasă, utilizând cuvântul cheie super în apel. Sunt apelaţi diferit faţă de metodele obişnuite, invocarea constructorului necesitând calificarea prin nume, precedat de operatorul new. Operatorul new indică faptul că o nouă memorie va fi alocată. Exemplu: String str=new String("Utilizator"); Nu poate trata excepţii chiar dacă apelează o metodă ce tratează excepţii, o face cu ajutorul unui constructor. Din alt punt de vedere constructorii sunt trataţi ca şi metodele şi pot fi supraîncărcaţi. Supraîncărcarea constructorului este distinsă prin semnătura conţinută în lista de arumente şi aceasta permite furnizarea flexibilităţii în construcţia obiectelor din clasă. Exemple: java.lang.String are 7 constructori supraîncărcaţi şi vom prezenta diverse variante de utilizare: String() construieşte şir vid în absenţa parametrilor. 47
String(String) construieşte şir nou, copie a celui transmis String(Char[ ]) construieşte şir nou, din tablou de caractere Deoarece constructorii pot fi supraîncărcaţi, este convenabil ca în interiorul unui constructor să se apeleze un alt constructor din acceaşi clasă. In acest sens este util să se declare un constructor pentru clasă ca şi un constructor fără argumente. Presupunem că se defineşte constructorul: Aclass (int i, int j) iar în caz implicit: Aclass(0,0), echivalent cu: Aclass(){ // constructor implicit fără argumente this(0,0); // prima instrucţiune obligatorie Apelul this(0,0) invocă un constructor din aceeaşi clasă, cu acceaşi semnătură (int, int) şi este unicul loc în care se utilizează cuvântul cheie this, ca şi constructor apelat în interiorul constructorului; în caz contrar, apar erori de compilare. Când se declară clase derivate, se moştenesc variabilele de instanţă din superclasă. Deoarece constructorul din clasa derivată nu este el însuşi moştenit, este necesară o iniţializare a variabilelor de instanţă moştenite. Acest lucru se poate face manual, ceea ce ia mult timp, însă dacă sarcina este preluată de superclasă este mult mai comod, mai ales dacă se moştenesc varibile private la care utilizatorul nu are acces. Din fericire, apelând la constructorul super cu condiţia că apare ca primă instrucţiune în corpul constructorului. Exemplu: class Aclass extends AsuperClass { double unDublu; Aclass (int I, int j, double d){ super(i,j);//apel Aclass(i,j) UnDublu=d; //nu a fost iniţializat de super } … } Se poate declara o clasă fără a specifica constructorul, fapt mai comod în special când o clasă este moştenită direct din altă clasă. Omiţând constructorul din clasă, compilatorul Java, generează automat constructorul implicit din superclasă. Exemplu: class FinalAclass extends B { // nu se furnizează constructor // compilatorul generează automat FinalAclass{ super();//apel B() } … } Dacă superclasa imediată (B în exemplu) nu are constructor care să preia argumentele, apar erori de compilare.
48
In programare se pot declara constructori private, furnizând metode public static care apelează constructorul şi întoarce obiectul construit. Pentru crearea unui obiect din exteriorul pachetului se apelează metoda static însă este util să se verifice argumentele din constructor şi să se trateze excepţiile care pot apare. Constructorul în sine nu tratează excepţiile dar metodele static pot analiza excepţiile şi să apeleze constructorul cu argumente corecte. 2.4. Rolul interfeţelor în programare 2.4.1. Definirea interfeţelor Limbajul Java pune la dispozitie o metoda pentru crearea claselor complet abstracte, cunoscute ca şi interfete. Interfeţele sunt destinate pentru a suporta rezoluţiile metodelor dinamice în timpul execuţiei. Pentru ca o metodă să poată fi apelată dintr-o clasă în alta, ambele clase trebuie să fie prezente la un moment dat în timpul compilării, pentru a ne asigura că semnăturile metodelor sunt compatibile. Mecanismul de deconectare a metodei/metodelor din ierarhia moştenirii este protocol în C obiectual şi interfaţă în Java.[Chan_95], [java_1] public inteface nume_intefată { // listă metodelor abstracte } Exemplu: interface P(){ byte[]pack(); void unpack(byte b[]); } class Punct { int x,y; Punct (int x, int y) { this.x=x; this.y=y; } } class AltPunct extends Punct implements P { AltPunct (int x, int y){ super (x,y); } AltPunct(){ this (0,0); } private byte p(int t, int n) { return (byte)((t>>n)&Oxff); //octal } public byte pack()[]{ byte ret[]=new byte[8]; ret[0]=p(x,24); ret[1]=p(x,16); 49
ret[2]=p(x,8); ret[3]=p(x,0); ret[4]=p(y,24); ret[5]=p(y,16); ret[6]=p(y,8); ret[7]=p(y,0); return ret; } private int n(byte b, int n){ return ((b&Oxff)
int a=0; int a1=1; int a2=2; int a3=3; int a4=5; } class Prima implements ConstanteImpartite { Random r=new Random(); int intreb{ int prob= (int)(100*rand.nextDouble()); if (prob<30) return a; else if prob(<60) return a1; else if prob(<75) return a2; else if prob(<90) return a3; } } class Doi implements ConstanteImpartite { static void rasp (int rez) { switch(rez) { case a: System.out.println ('Da"); case a1: System.out.println ('Nu"); case a2: System.out.println ('Poate"); case a3: System.out.println ('Nu sunt sigur"); } } public static void main (String args[]){ Prima q1= new Prima(); rasp(q1.intreb); rasp(q1.intreb); rasp(q1.intreb); rasp(q1.intreb); } } In interiorul celor două clase codul care referă constantele este dat ca şi cu ele ar fi definite sau moştenite direct. Toate metodele declarate într-o interfaţă sunt implicit abstract şi similar, toate variabilele dintr-o interfaţă sunt implicit static şi final deoarece sunt formate ca şi constante. Spre deosebire de clasă, o interfaţă poate extinde multiple interfeţe moştenind variabilele şi metodele
51
definite în interior. Orice clasă ce implementează o asemenea interfaţă trebuie să ofere definiţii pentu toate metodele definite în interfaţă şi toate interfeţele esenţiale. Exemplu: interface Simplu1 { int a; void absolM1(){ } interface Simplu2 { String str; void absolM2(){ } Interface Combinata extends Simplu1,Simplu2 { void absolM3(){ } } //ImpClasa cere implementarea metodelor absolM1() // absolM2(), absolM3() class ImpClass implements Combinata { public absolM1 (){ // definitia metodei 1 } public absolM2 (){ // definitia metodei 2 } public absolM3 (){ // definitia metodei 3 } // alte metode ale clasei ImpClasa } Dacă o clasă implementează o interfaţă sublcasele moştenite pot extinde aceste metode implementate, respectând restricţiile impuse de specificatorii de acces. In plus dacă o clasă implementează o interfaţă sau un set de interfeţe nu este necesară includerea acestora în clauzele implements ale subclaselor. 2.4.5. Specificatorii de acces Incercăm să sintetizăm în cele ce urmează caracteristicile specificatorilor de acces la nivelul claselor, variabilelor şi metodelor. Public Clasa - accesibil tuturor claselor din exteriorul pachetului; Variabile - vizibile tuturor claselor ce acceseză această clasă şi din exteriorul pachetului; - vizibile în toate clasele derivate din această clasă în şi din exteriorul pachetului; Metode - vizibile tuturor claselor ce acceseză această clasă şi din exteriorul pachetului; - vizibile în toate clasele derivate din această clasă în şi din exteriorul pachetului; - pot fi suprascrise în toate clasele derivate din aceasta în şi din exteriorul pachetului. 52
Protected Clasa - inaplicabilă; Variabile - vizibile tuturor claselor ce acceseză această clasă în interiorul pachetului; - vizibile numai în clasele derivate în exteriorul pachetului; Metode - vizibile tuturor claselor ce pot extinde această clasă în interiorul pachetului; - vizibile doar claselor derivate din această clasă în exteriorul pachetului; [default] (implicit) Clasa - în interiorul pachetului accesibile tuturor claselor; - în exteriorul pachetului inaccesibile; Variabile - vizibile tuturor claselor în interiorul pachetului; - vizibile numai în clasele derivate în exteriorul pachetului; Metode - vizibile tuturor claselor ce pot suprascrie această clasă în interiorul pachetului; - vizibile doar claselor derivate din această clasă şi pot fi suprascrise de aceste clase, în exteriorul pachetului; Private protected Clasa - inaplicabilă; Variabile - vizibile doar claselor derivate din aceasta în interiorul şi exteriorul pachetului; Metode - vizibile doar claselor derivate din această clasă în interiorul/exteriorul pachetului - pot fi acoperite de clase derivate Private Clasa - inaplicabilă; Variabile - vizibile doar în interiorul clasei; Metode - - vizibile doar în interiorul clasei. Final Clasa - nu pot fi extinse; Variabile - atribute cu valoare permanentă în declaraţiile lor; - valoarea nu poate fi schimbată în orice clasă chiar dacă sunt sau nu sunt derivate din acestă clasă; metode - nu poate fi acoperită. Static Clasa - inaplicabilă; Variabile - numai copia variabilelor se împarte de toate subclasele; Metode - - metoda este împărţită de toate subclasele dar nu poate fi extinsă. Abstract Clasa - conţine una sau mai multe metode abstracte; - nu poate fi instanţiată; Variabile inaplicabilă; Metode - nu este definită şi va fi pusă la dipoziţie de subclasă; - trebuie acoperită când este subclasată sau subclasa însuşi este abstract. Native Clasa - inaplicabilă; Variabile inaplicabilă; Metode - este definită în alt limbaj (exterior) ca şi C, C++, etc. Syncronized Clasa - inaplicabilă; Variabile inaplicabilă; Metode - la momentul dat, numai un fir poate spune obiectului instanţat să 53
execute această metodă; - dacă este static, numai un fir poate executa această metodă la un timp dat, periodic. 2.4.6. Moştenirea multiplă şi interfeţele Moştenirea multiplă (multiple inheritance-MI) este un acronim al OO, care porneşte de a ideea că o clasă poate fi derivată din mai multe superclase. Cum Java nu suportă MI, oferă o alternativă prin interfeţe. Utilizarea eronată a Mi conduce la două categorii de erori grave: ambiguitatea şi circularitatea. Ambiguitatea rezultă când unul sau mai mulţi ascendenţi ai clasei definesc aceeaşi variabilă v sau metodă(variabilă) cu acelaşi nume. Spre exemplu în C++ declarăm clasa Ascend_1 cu variabila v şi metoda asMetoda() şi altă clasa Ascend_2 cu variabila v proprie şi metoda asMetoda() şi altă clasă Descen ce moşteneşte cele 2 varibile şi metode. Apare întrebarea: care dintre variabile este moştenită şi de cine? Greu de răspuns. Circularitatea apare când o clasă se moşteneşte direct sau indirect pe ea însuşi (simultan este bunic şi nepot). Cum nu se poate şti care este ascendentul şi descendentul se distruge claritatea noţiunii de moştenire. Cele două inconveniente sunt soluţionate în Java prin definirea interfeţelor, în care este cunoscut faptul că variabilele sunt definite static şi final deci nu crează ambiguităţi pentru că servesc drept constante. In plus, datorită distincţiei între clase şi interfeţe sunt evitate circularităţi ascunse; chiar dacă se implementează oricâte interfeţe, o clasă nu poate fi regăsită în ea însuşi. 2.5. Rolul pachetelor în programare In disponibilitatea pachetelor, Java poate adresa 4 categorii de vizibilităţi între elementele clasei: subclase ale aceluiaşi pachet; clase care nu sunt subclase ale aceluiaşi pachet; sublclase în pachete diferite; clase ce nu sunt în acelaşi pachet şi nici nu sunt subclase. Ca şi o regulă generală orice se declară public este accesibil oriunde iar orice clasă sau variabilă declarată private nu este vizibilă în exteriorul clasei. Private Aceaşi clasă Subclasă în acelaşi pachet Acelaşi pachet nonsubclasă Subclasă din pachete diferite Diferite pachete nonsubclasă
Private Protected Da Da
Protected
Public
Da Nu
Nomodifie r Da Da
Da Da
Da Da
Nu
Da
Nu
Da
Da
Nu
Nu
Da
Da
Da
Nu
Nu
Nu
Nu
Da
Tabelul 2.1. Vizibilitatea şi modificatorii de acces In definirea unei clase se pot include şi nivele de protecţie pentru variabile şi metode, cu ajutorul unor cuvinte rezervate numite modificatori de acces: protected, public sau private. In lipsa acestor nivele de protecţie implicit se consideră nivelul prietenos (friendley).[An_96]
54
In Java protected induce comportament similar cu friend din C++ iar private protected ca şi protected din c++. Semnificatiile acestor nivele de protecţie sunt diferite, astfel: • Protected – metodele şi variabilele sunt declarate protected, sunt disponibile în clasa în care sunt definite şi în subclasele din această clasă. • Public - metodele şi variabilele sunt declarate public, fiind disponibile în toate clasele. • Private - metodele şi variabilele sunt declarate private, fiind disponibile numai în clasa în care sunt definite; nici măcar subclasele derivate din această clasă nu au acces la ele. • Prietenos(friendly) – nivelul implicit, ele sunt disponibile pentru toate clasele din pachetul curent. O variabilă sau o metodă nu poate avea declaraţe mai multe nivele de protecţie simultan, sau în discordanţă cu pachetul din care face parte, acestea determinând erori la compilare. Exemplu: package p1; public class Protectia { int n=1; private int n_pri=2; protected int n_pro=3; private protected int n_pripro=4; public n_pub=5; public Protectia(){ System.out.println ("Constructorii de bază"); System.out.println (n); System.out.println (n_pri); System.out.println (n_pro); System.out.println (n_pripro); System.out.println (n_pub); } } class Derivata extends Protectia { Derivata(){ System.out.println ("Constructori derivati"); System.out.println (n); //numai în clasă //System.out.println (n_pri); System.out.println (n_pro); System.out.println (n_pripro); System.out.println (n_pub); } } class acPachet { acPachet(){ Protectia p=new Protectia(); System.out.println ("Constructori in acelaşi pachet"); System.out.println (p.n); 55
//numai în clasă //System.out.println (p.n_pri); System.out.println (p.n_pro); //in clasa si subclasa System.out.println (p.n_pripro); System.out.println (p.n_pub); } } In pachetul p2 două definiţii de clasă acoperă două condiţii ce sunt afectate de controlul accesului. Prima clasă este subclasă a lui p1.Protectia ce garantează acces la toate variabilele p1.Protectia, cu excepţia lui n_pri (deoarece ea este private) şi n declarată prin protecţie implicită (în interiorul clasei sau în pachet nu în subclase extrapachet).Clasa AltPachet are acces doar la o variabilă n_pub declarată public. Exemplu: package p2; class Protectia2 extends p1.Protectia { Protectia2 () { System.out.println ("Constructor derivat din alt pachet"); // numai in clasa sau pachet //System.out.println (n); //numai in clasa //System.out.println (n_pri); System.out.println (n_pro); System.out.println (n_pripro); System.out.println (n_pub); } } class AltPachet{ AltPachet (){ p1.Protectia p= new p1.Protectia(); System.out.println ("Constructor in alt pachet"); // numai in clasa sau pachet System.out.println (p.n); //numai in clasa, subclasa si pachet //System.out.println (p.n_pro); //numai in clasa sau sbclasa //System.out.println (p.n_pripro); System.out.println (p.n_pub); } }
56