45
17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş- tinţe despre programarea în limbajul Java. Există deja suficientă literatură legată de programarea în Java [1, 5, 67, 4, 34]. În acest capitol venim în sprijinul "aducerii aminte" prin mai multe exemple de programe Java. Vom prezenta mai întâi câteva exemple simple. Apoi facem un sumar al limbajului Java prin comparaţii cu C++. Prezentăm apoi câteva aspecte Java legate de tratarea excepţiilor şi de interfeţe. Capitolul se va încheia cu alte câteva exemple simple de programe Java. 1.1. Primele exemple Specificul Java oferă două tipuri de programe: aplicaţii de sine stătătoare, numite în terminologia Java standalone; programe activabile prin intermediul navigatoarelor Web, numite applet-uri. 1.1.1. Programul standalone Salut Fişierul sursă poartă numele Salut.java şi este prezentat în progra- mul 1.1. public class Salut{ public static void main(String a[]) { System.out.println("Salut"); } // Salut.main } // Salut Programul 1.1. Salut.java Mai întâi câteva precizări, care reprezintă de fapt şi primele reguli Java. Un program Java constă din definirea şi instanţierea unei clase. Numele textului sursă este format din numele clasei urmat de sufixul .java. Unele programe pot conţine în acelaşi text sursă mai multe clase, dar atunci numai una dintre ele va fi vizibilă în exterior, celelalte fiind numai de uz intern.

1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

  • Upload
    ngodan

  • View
    246

  • Download
    7

Embed Size (px)

Citation preview

Page 1: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

17

1. COMPENDIU JAVA

Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe despre programarea în limbajul Java. Există deja suficientă literaturălegată de programarea în Java [1, 5, 67, 4, 34].

În acest capitol venim în sprijinul "aducerii aminte" prin mai multeexemple de programe Java. Vom prezenta mai întâi câteva exemple simple.Apoi facem un sumar al limbajului Java prin comparaţii cu C++. Prezentămapoi câteva aspecte Java legate de tratarea excepţiilor şi de interfeţe. Capitolulse va încheia cu alte câteva exemple simple de programe Java.

1.1. Primele exempleSpecificul Java oferă două tipuri de programe:

• aplicaţii de sine stătătoare, numite în terminologia Java standalone;• programe activabile prin intermediul navigatoarelor Web, numite

applet-uri.

1.1.1. Programul standalone Salut

Fişierul sursă poartă numele Salut.java şi este prezentat în progra-mul 1.1.

public class Salut{

public static void main(String a[]) { System.out.println("Salut"); } // Salut.main

} // Salut

Programul 1.1. Salut.java

Mai întâi câteva precizări, care reprezintă de fapt şi primele reguli Java.Un program Java constă din definirea şi instanţierea unei clase. Numele textuluisursă este format din numele clasei urmat de sufixul .java. Unele programepot conţine în acelaşi text sursă mai multe clase, dar atunci numai una dintre eleva fi vizibilă în exterior, celelalte fiind numai de uz intern.

Page 2: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

18

Un program standalone trebuie să conţină metoda statică main. Ea areca parametru un tablou având ca şi elemente şiruri de caractere. Fiecare elemental tabloului conţine un argument transmis în linia de comandă ce lanseazăprogramul în execuţie.

Tipărirea este executată de către metoda println a obiectului out dinclasa System.

Compilatorul Java poartă numele de javac.

Lansarea compilării programului se face cu comanda:javac Salut.java

Rezultatul compilării este depus în fişierul Salut.class, careconţine codul JVM echivalent.

Interpretorul standard Java poartă numele java. El preia codul JVM dinSalut.class şi-l execută (interpretativ).

Execuţia interpretativă se lansează prin comanda:java Salut

Ca efect, pe ecran se va afişa, pe linie nouă:Salut

1.1.2. Applet-ul SalutAp

Exemplul din secţiunea precedentă, scris ca un applet, este prezentat înprogramul 1.2. Textul sursă conţine şi patru linii de comentarii, scrise ca înC++.

import java.applet.*;import java.awt.*;

public class SalutAp extends Applet { public void paint(Graphics g) { g.drawString("Salut",10,50); } // SalutAp.paint} // SalutAp

Programul 1.2. SalutAp.java

Mai întâi se declară folosirea de metode ale pachetelor applet şi awt.Apoi se indică faptul că clasa SalutAp extinde (este o subclasă pentru)

clasa Applet.

Page 3: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

19

Scrierea textului se face prin intermediul metodei paint. Aceastarealizează rescrierea metodei paint din java.awt.Component, avândrolul de a "picta" un obiect grafic g. Concret, este vorba de scrierea unui stringîncepând de la pozitia (x= 10 pixeli spre dreapta, y = 50 pixeli în jos).

Compilarea sursei se face folosind aceeaşi comandă de compilare:javac SalutAp.java

care generează codul de octeţi echivalent sursei în fişierul SalutAp.class.Pentru lansarea în execuţie a acestui applet este nevoie de un navigator

Web: Netscape, Mozilla, Konqueror, InternetExplorer etc.Aceste navigatoare operează peste o platformă grafică oferită de către sistemulde operare: Windows [100] (platformele Microsoft), respectiv X-window [58](platformele Unix). În funcţie de platformă, Pachetul Java oferă pentru testareaapplet-urilor, interpretorul appletviewer care este, de fapt, un navigatorWeb specializat.

În textul sursă HTML [59, 85] transmis navigatorului Web trebuieinserată sursa din programul 1.3.

<APPLET code="SalutAp.class" width="500" height="100"> </APPLET>

Programul 1.3. SalutAp.html

Numele fişierului .java trebuie să coincidă cu numele clasei, însănumele fişierului HTML poate fi diferit (noi l-am luat acelaşi din comoditate).

Prin acest text, care este un tag HTML, se cere navigatorului să-şiîncarce codul JVM din fişierul SalutAp.class şi să-l execute (interpre-tativ).

Figura 1.1. Imaginea SalutAp prin Mozilla

Page 4: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

20

În fig. 1.1 este prezentat rezultatul execuţiei folosind navigatorulMozilla.

Dacă se doreşte utilizarea interpretorului standard Java appletviewer, seva lansa comanda:

appletviewer SalutAp.html

Imaginea ferestrei este prezentată în fig. 1.2

Figura 1.2. Imaginea SalutAp prin appletviewer

1.1.3. Un program ce poate rulaalternativ standalone sau applet

Pare cel puţin interesant să vedem un program care să poată rula, dupăpreferinţă, ori ca aplicaţie standalone ori ca applet. Unul dintre cele mai simpleprograme de acest tip este programul 1.4.

import java.awt.*;import java.awt.event.*;import java.applet.*;

public class PrimCompus extends Applet { public static void main(String a[]) { PrimCompus oPrimCompus = new PrimCompus(); Frame oFrame = new Frame("Fereastra standalone"); WindowListener l=new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }; oFrame.addWindowListener(l); oFrame.setSize(250,350); oFrame.add("Center", oPrimCompus); oFrame.show(); oPrimCompus.init(); } // PrimCompus.main

Page 5: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

21

public void init() { } // PrimCompus.init

public void paint(Graphics g) { g.drawString("Un sir in fereastra",50,60); } // PrimCompus.paint

} // PrimCompus

Programul 1.4. PrimCompus.java

Clasa PrimCompus extinde clasa Applet spre a se putea comporta caşi un applet. De asemenea, în cadrul ei se defineşte metoda main, care esteinvocată la lansarea ca şi aplicaţie standalone.

Să urmărim mai întâi comportarea ca şi applet. În momentul lansăriidintr-un navigator, acesta (navigatorul) caută metoda init pe care o lanseazăîn execuţie. În exemplul nostru metoda init este vidă. În continuare (dupăcum am arătat şi la exemplul SalutAp), dacă în cadrul clasei este definitămetoda paint, atunci se lansează în execuţie aceasta. În cazul nostru va afişaun string în fereastră.

Pentru lansarea din navigator, este suficient ca documentul html să aibăconţinutul din programul 1.5.

<html> <title>Fereastra applet </title> <Applet code="PrimCompus.class" width=250 height=350> </Applet></html>

Programul 1.5. PrimCompus.html

Spre a se deosebi de aplicaţia standalone, am precizat şi titlul ferestrei.Execuţia programului din Mozilla este ilustrată în fig. 1.3

Figura 1.3. Imaginea PrimCompus prin Mozilla

Page 6: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

22

Pentru ca programul să poată fi rulat, cu (aproape) acelaşi efect, în cadrulmetodei main trebuie executate câteva acţiuni, de altfel specifice interfeţelorgrafice de tip standalone.

Mai întâi am definit şi construit obiectul oPrimCompus de tipul clasei.Apoi am definit un obiect oFrame de tip Frame (tipul clasic de

fereastră din pachetul awt). Am fixat titlul acestei ferestre şi dimensiunile ei (înpixeli).

Am construit obiectul l, de tip WindowListener, definit printr-o clasăinternă, anonimă, de tip WindowsAdapter. Rolul acestui obiect este de aintercepta evenimentul de apăsare a butonului de distrugere a ferestrei şi de atermina apoi programul. (Ca rezultat al compilării, din această clasă anonimă seva crea fişierul PrimCompus$1.class)

Am adăugat, în poziţia centrală a obiectului oFrame obiectuloPrimCompus şi am cerut ferestrei să devină vizibilă.

În sfârşit, am lansat metoda init a obiectului oPrimCompus, dupăcare comportarea va fi ca şi la applet: va căuta metoda paint şi va afişa string-ul în fereastră. Rezultatul execuţiei va fi cel din fig. 1.4.

Figura 1.4. Imaginea PrimCompus ca aplicaţie standalone

1.1.4. Acces la argumentele liniei de comandă

Un program standalone constă din una sau mai multe definiţii de clase,fiecare dintre acestea aflându-se în propriul fişier *.class. Una dintre acesteclase trebuie să conţină metoda main().

Pentru a lansa programul, trebuie lansat interpretorul java cu numeleclasei care conţine metoda main. Aceasta are prototipul:

public static void main ( String argv[] ) .

Interpretorul Java execută codul până când metoda main execută unapel System.exit sau până la sfârşitul codului metodei (atingerea acoladeide închidere a metodei). Din acel moment, se aşteaptă terminarea tuturor

Page 7: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

23

thread-urilor (firelor de execuţie) care încă nu s-au terminat şi apoi execuţia setermină.

În momentul lansării în execuţie, parametrii transmişi prin linia decomandă sunt depuşi în vectorul de string-uri notat mai sus prin argv. De aiciutilizatorul le poate accesa. De exemplu, programul 1.6 afişează toate acesteargumente (tabloul de string-uri din linia de comandă l-am notat cu a).

public class Echo { public static void main(String a[]){ System.out.println("Urmeaza cele "+a.length+" argumente:"); for(int i=0;i<a.length;i++) System.out.println(a[i]); } // Echo.main} // Echo

Programul 1.6. Echo.java

Trebuie observate diferenţele faţă de C. În C, funcţia main are douăargumente, un întreg şi un tablou de pointeri, notate de obicei de obicei argcargv, indicând numărul de argumente şi pointerii la string-urile transmise caargumente. În Java apare doar tabloul de string-uri, notat de obicei argv,pentru că numărul de elemente este dat de argv.length, câmpul ce indicăpentru un obiect tablou numărul de elemente. Tot spre deosebire de C, dupăcum se poate vedea dacă se rulează programul, numele comenzii nu esteconsiderat argument al liniei de comandă.

Accesul la variabilele de mediuJava nu permite accesul la variabilele de mediu ale sistemului de operaresub care se rulează. Această interdicţie este impusă deoarece acestevariabile depind de sistemul concret, deci nu asigură independenţa deplatformă. Mediul Java oferă o modalitate proprie, independentă deplatformă, de a defini o listă de proprietăţi sistem. Cititorul interesatpoate să consulte [44, 10] în acest scop.

1.2. Scurtă prezentare a limbajului Java

1.2.1. Java comparat cu C şi C++

După cum se ştie, Java s-a născut din C şi C++. Este deci normal caaceste limbaje să aibă trăsături comune, precum şi diferenţe. Deoarece nu avemîn intenţie o prezentare exhaustivă a limbajului Java, preferăm o prezentare a sarelativă la aceste limbaje. Lista similarităţilor şi a deosebirilor este actualizată

Page 8: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

24

cu fiecare nouă distribuţie Java. De exemplu, lucrările [44, 4] prezintă acesteelemente. Noi vom prezenta deosebirile şi asemănările în ipoteza că cititorulcunoaşte, la un nivel minimal, limbajele C++ şi (eventual) Java.

Java nu acceptă mecanismul typedef, nici noţiunea de preprocesor. Înconsecinţă, construcţiile #define, #if #include ş.a. nu sunt admise.Dispare deci noţiunea de fişier header. De asemenea, Java nu suportă tipul dedate enum. În locul lui lui define, în Java se pot declara constante cu ajutorulcuvântului cheie final. Aceste constante pot fi folosite ca înlocuitori pentrutipul enum.

Ca şi C++, Java operează cu tipul class, dar nu suportă nici tipulstruct, nici union.

Programele standalone C++ cer o funcţie numită main. Pe lângăaceasta, în programele C++ pot să apară numeroase alte funcţii, atâtindependente, cât şi funcţii membre ale claselor (metode). În Java, funcţiile suntnumai membre ale claselor (metode), deci nu există funcţii independente,neaparţinând claselor. De asemenea, Java nu admite funcţii globale şi dateglobale.

În Java, toate clasele sunt, în ultimă instanţă, descendente din clasaObject. În schimb, în C++ este posibilă crearea unor arbori de clase fără nicio legătură între ei.

Toate definiţiile de funcţii (metode) în Java sunt conţinute în interiorulclaselor cărora le aparţin. În C++ este permisă definirea lor în afara claselor. Deasemenea, în C++ este permisă definirea explicită de funcţii inline, în timpce în Java această opţiune este lăsată pe seama interpretorului JVM.

Atât C++ cât şi Java permit definirea de variabile şi metode clasă(static), invocabile independent de existenţa sau nu a unor instanţieri aleclasei respective.

Java permite utilizarea conceptului interface. Prin intermediul ei sepot defini prototipuri de metode şi eventual declararea de constante. Într-ointerfaţă nu sunt permise definirea de metode, nici declararea de variabilemembru. C++ nu suportă conceptul de interfaţă. Atât Java, cât şi C++ admitclase abstracte. Vom reveni asupra acestui concept.

Java nu permite moştenirea multiplă. Într-o oarecare măsură, utilizareainterfeţelor suplineşte în Java această absenţă. Moştenirea simplă este,conceptual la fel în Java ca şi în C++, însă în partea de implementare diferenţelesunt mari.

Java nu acceptă instrucţiunea goto, deşi goto este cuvânt rezervat (darnefolosit)! În schimb, Java permite break şi continue, atât în formaexistentă în C++, cât şi extinderile break şi continue etichetate. Acesteadin urmă nu sunt suportate de C++. În forma simplă ele comandă părăsirea,respectiv reiterarea celui mai interior corp al unui ciclu. Cele etichetateacţionează în acelaşi mod, dar asupra ciclurilor (mai exterioare) marcate printr-o

Page 9: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

25

etichetă. Aceste extinderi pentru break şi continue permit înlocuirea, întoate cazurile de structuri de control fireşti, a instrucţiunii goto [16].

Java nu permite suprapunerea operatorilor. De asemenea, nu permiteconversia implicită (automată) de tip.

Spre deosebire de C++, Java are obiecte de tip String, obiectenemodificabile. Obiectele Java de tip StringBuffer permit şi modificări,precum şi conversii facile între cele două tipuri de string-uri.

Tablourile în Java sunt obiecte. Într-un tablou Java, elementele lui suntindexate de la 0 la length-1. length este o variabilă a unui obiect tablou,indicând lungimea maximă a domeniului de indexare. Elementele unui tablousunt (evident) toate de acelaşi tip, care poate fi orice, inclusiv un alt tablou.Astfel se implementează tablourile multidimensionale. Java permite operaţia deatribuire la nivel de tablou. Dimensiunea unui tablou se fixează dinamic, nustatic ca în C++. Spre deosebire de C++, în Java se controlează, la oriceinvocare, limitele indicilor, evitându-se astfel indexarea în afara domeniului.

Java nu permite operarea cu pointeri, implicit nu există nici aritmeticade pointeri. Automat, a dispărut şi echivalenţa dintre pointeri, nume de tablourietc. Natural, absenţa pointerilor implică absenţa operatorului de selecţie dinpointer "->".

Operatorul rezoluţie scope "::" din C++ nu este folosit în Java. Toateselecţiile se fac cu operatorul ".".

Pentru expresiile condiţionale Java s-a introdus tipul boolean, inexistentîn C++. A dispărut astfel statutul de TRUE pentru o valoare nenulă şi FALSEpentru o valoare nulă, aşa cum stau lucrurile în C.

În ceea ce priveşte tipurile de date primitive, ele sunt aproape identicecu cele din C. Vom reveni cu detalii într-o secţiune ulterioară.

Operatorii sunt, practic cei din C. Operatorii pe biţi acţionează numaiasupra întregilor. Operatorul >> face deplasarea la dreapta cu propagarea bituluide semn, iar un nou operator, >>>, face aceeaşi deplasare cu completarea dezerouri pentru biţii eliberaţi. Singura suprapunere, implicită, este += ce poateindica şi concatenarea de string-uri.

Cât despre spaţiile de nume: în Java orice variabilă (câmp) şi oricemetodă este declarată numai în interiorul unei clase şi face parte din structuraacesteia. De asemenea, fiecare clasă face parte dintr-un pachet. Pachetele Javasunt astăzi cele care permit o mare dezvoltare şi extindere a platformelor Java.O colecţie de clase înrudite, cu ţinta spre un anumit tip de prelucrări, constituieîn fapt o bibliotecă ce poate fi integrată în orice aplicaţie Java. Este suficient săamintim aici că, dacă la Java 1.0 ([10]) erau opt pachete standard, la Java 1.2erau 58 [44] şi numărul pachetelor, standard sau de aplicaţie este în continuăcreştere.

Accesul la nume se poate face:

Page 10: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

26

• implicit - între clasele aceluiaşi pachet;• declarat public - accesibil de oriunde;• declarat protected - accesibil din clasă sau clasele subordonate;• declarate private - accesibil numai în interiorul clasei.

În C++ orice nume (variabilă, constantă, nume de funcţie etc.) trebuiemai întâi declarat sau definit şi abia apoi utilizat. În Java această restricţie numai există.

Constructorii, atât în Java cât şi în C++ pot fi suprapuşi. În Java, toateobiectele sunt transmise prin referinţă, eliminându-se astfel constructorii decopiere din C++. În Java nu există destructori! Interpretorul JVM are un threadspecializat, care rulează în continuu (cu prioritatea cea mai mică), numitgarbage collector. Acesta face oficiile de destructor atunci când un obiect numai este referit de nicăieri.

Garbage collectionObiectele Java îşi ocupă memoria necesară în mod dinamic, în faza deexecuţie. Alocarea de memorie este făcută automat, deci lipsesc apelurilesimilare lui malloc. De asemenea, nu se face eliberarea explicită aspaţiului ocupat, deci nu există apeluri similare lui free din C şi C++.Distrugerea obiectelor se face automat, printr-un mecanism clasic degarbage collection [76, 17]. Eliberarea spaţiului ocupat de un obiect seface, fără ştirea utilizatorului, după ce obiectul nu mai este vizibil şi niciîn viaţă.

Funcţiile pot fi suprapuse, atât în C++ cât şi în Java. Spre deosebire deC++, în Java nu sunt admişi parametri cu valori implicite, nici funcţii cu unnumăr variabil de parametri. La fel ca şi în C++, Java permite metode native.

Spre deosebire de C++, Java nu suportă template, nici funcţii generice.Cuvântul virtual nu apare în Java. Toate metodele nestatice din Java suntlegate dinamic, aşa că virtual din C++ îşi pierde semnificaţia.

Asupra tratării excepţiilor vom reveni.Spre deosebire de C++, Java oferă o serie de structuri specializate,

numite containere de obiecte. Relevante în acest sens sunt containerele Vectorşi Hashtable.

În fine, comentariile speciale /** … */ sunt procesate prin javadocşi permit elaborarea automată de documentaţii.

Page 11: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

27

1.2.2. Modificatorii static, final, syncronized şi native

Modificatorul staticPoate preceda un câmp (variabilă) sau o metodă a unei clase, pentru aspecifica faptul că elementul respectiv este propriu clasei respective şi căel nu depinde de una sau alta dintre obiectele rezultate din instanţierileclasei respective. Deci câmpul (variabila) are aceeaşi valoare indiferentde instanţiere, iar metoda funcţionează independent de instanţiere. Unelement static poate fi accesat indiferent de faptul că există sau nu oinstanţiere a clasei respective.De exemplu, variabila out din clasa System poate fi accesat chiar dacănu există o instanţiere a acestei clase, motiv pentru care putem invocametoda println a lui: System.out.println("…");

Modificatorul finalAtaşat unei variabile sau unei metode indică faptul că elementulrespectiv este în ultima lui formă. Din această cauză, nici variabila, nicimetoda finală nu mai pot fi suprascrise în cadrul subclaselor derivate. Ovariabilă finală se comportă ca şi o constantă.

Modificatorul syncronizedPoate să caracterizeze o metodă (există şi instrucţiunea syncronizedasupra căreia vom reveni). Dacă două sau mai multe thread-uri invocă oaceeaşi metodă sincronizată pentru un acelaşi obiect, atunci doar unuldintre ele o execută, celelalte aşteaptă ca acesta să îşi termine invocareaei, după care un altul îşi porneşte exclusiv execuţia ş.a.m.d. Deci, dacădouă thread-uri solicită pentru acelaşi obiect a o metodă sincronizatămet, adică ambele invocă a.met, atunci ele vor fi executate unuldupă celălalt. Dacă însă există două obiecte diferite a şi b, atunci douăthread-uri pot să execute simultan apelurile a.met() b.met().

Modificatorul nativeIndică faptul că metoda respectivă este descrisă într-un fişier externprovenit dintr-o altă compilare. De exemplu dintr-o compilare C. Evi-dent, acest gen de metode nu mai păstrează independenţa de platformă.

Page 12: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

28

1.2.3. Cuvinte cheie

Java utilizează următoarele cuvinte cheie:

abstract boolean break byte casecatch char class const continuedefault do double else extendsfinal finally float for gotoif implements import instanceof intinterface long native new nullpackage private protected public returnstatic short super switch synchronizedthis throw throws transient tryvoid volatile while

1.2.4. Tipuri de date primitive

Unicode: codificarea şi evitarea caracterelorJava este unul dintre puţinele limbajele de programare care “încalcă” un

principiu care părea statuat de facto: caracterele Java şi string-urile suntcodificate în codul Unicode pe 16 biţi. Asta face ca setul de caractere să fiepotrivit şi pentru alte caractere, neexistente în alfabetul englez. Setul Unicodeeste efectiv un supraset al lui ASCII, deci textele ASCII au aceeaşi reprezentare,dar fiecare octet ASCII este completat cu câte un octet semnificativ cu valoarea0 (neinterpretat de maşinile ce văd doar ASCII).

Cititorii care doresc să cunoască întregul set de caractere Unicode, potconsulta http://unicode.org. Specificarea caracterelor ASCII tipăribilese face ca şi în C. Mecanismul de evitare a caracterelor din C se păstrează şi semai introduce un mod suplimentar de specificare, astfel:

\uxxxx unde xxxx sunt patru cifre hexa;\xxx unde xxx sunt trei cifre octale.\n \r \t \f \b \” \’ \\ sunt cele cunoscute din C.

Tipul charSe reprezintă pe doi octeţi în Unicode şi respectă convenţiile deconversie la întregi din C.

String-urileSe scriu ca şi în C (între ghilimele). Spre deosebire de C, în Java nuexistă continuare (linie terminată cu \ în C sau C++) pentru string-urilelungi. În schimb, operatorul + Java este extins şi pentru concatenarea de

Page 13: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

29

string-uri. Deci şirurile lungi se scriu folosindu-se operatorul + de conca-tenare. Există tipul de date String.

Tipul BooleanEste nou introdus în Java (nu este în C). În schimb dispare convenţia Ccu 0 având valoarea false şi diferit de 0 având valoarea true. Un tipBoolean nu este un întreg şi nici nu poate fi convertit la acesta.

Tipurile întregiLa tipurile întregi, împărţirea cu 0 activează excepţia numităArithmeticException. Constantele întregi lungi au după şirul decifre litera l sau L. Constantele octale încep cu 0, iar cele hexazecimalecu 0x sau 0X.

Tipurile flotantePentru tipurile flotante există constantele predefinite:POSITIVE_INFINITY, NEGATIVE_INFINITY, NaN (Not a Number).

La tipurile flotante depăşirile nu se semnalează, ci rezultatul poate fiunul dintre constantele de mai sus. Există, de asemenea, zero negativ şizero pozitiv. Constantele flotante se pot specifica plasând la sfârşitulscrierii numărului litera f sau F pentru simplă precizie, respectiv cu dsau D pentru dublă precizie.Tabelul următor descrie tipurile de date primitive.

Tip Conţinut Valoare implicită Lungime

Boolean true sau false false 1 bit

Char caracter Unicode \u0000 16 biţi

Byte întreg cu semn 0 8 biţi

Short întreg cu semn 0 16 biţi

Int întreg cu semn 0 32 biţi

Long întreg cu semn 0 64 biţi

Float standard IEEE simplă precizie 0.0 32 biţi

Double standard IEEE dublă precizie 0.0 64 biţi

Page 14: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

30

1.2.5. Tipul referinţă obiect

Tipurile de date neprimitive sunt obiecte, în care includem şi tablouri.Ele sunt numite în Java “tipuri referinţă”, deoarece sunt manevrate prin adresă:compilatorul pune adresa unui obiect sau tablou într-o variabilă şi aşa estetransmis către metode. Prin contrast, tipurile primitive sunt manevrate “prinvaloare”.

Deoarece obiectele sunt transmise prin referinţă, este posibil ca douăvariabile diferite să se refere la acelaşi obiect. De exemplu:

Button p, q;p = new Button();q = p;p.setLabel(“Ok”);String s = q.getLabel();

Aici, s va avea valoarea “Ok”.

int i = 3;int j = i;i = 2;

Aici, i este 2 şi j este 3 (deci nu e valabilacelaşi lucru la datele primitive!).

Copierea obiectelor şi “compararea” lorDin cauza referinţei, atribuirea între obiecte nu face copiere. De

exemplu,Button a = new Button(“Ok”);Button b = new Button(“Cancel”);a = b;

Aici, a şi b punctează ambele la butonul Cancel, iar butonul Ok estepierdut.

Pentru a se face o atribuire veritabilă, trebuie executată o operaţie decopiere a componentelor de la sursă la destinaţie. Majoritatea tipurilor de datestandard au definită o metodă clone(), care execută efectiv copierea, compo-nentă cu componentă. O astfel de atribuire funcţionează ca în exemplul următor,în care variabila c se va referi la un duplicat al obiectului b:

Vector b = new Vector;c = b.clone();

Pentru copierea de tablouri, se foloseşte metoda:System.arraycopy(sursa,inceput,destinatie,inceput,lungime)

Obiectele nu pot fi comparate nici măcar cu egalitate. Utilizatorul îşipoate defini proceduri proprii de comparare. Unele clase au, în acest scop, ometodă numită equals().

Page 15: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

31

Din raţiuni de portabilitate, de independenţă de platformă şi de siguranţă,nu există pointeri! Deci, nu se pune problema referirii, dereferirii, conversieireferinţelor la tablouri în întregi etc.

Constanta null indică inexistenţa unui obiect sau a unui tablou.

1.3. Tratarea excepţiilorO excepţie Java este un obiect care descrie o condiţie (situaţie)

excepţională ce are loc într-o secvenţă de cod. Atunci când are loc o condiţieexcepţională Java creează un obiect reprezentând eroarea şi aruncă excepţiametodei care a cauzat eroarea. Metoda poate să aleagă între a prelua excepţia(şi a o trata), sau a o ignora. În oricare din cazuri la un anumit moment excepţiaeste prinsă şi tratată.

Excepţiile pot fi generate de mediul de execuţie Java, sau de pro-gramator. Excepţiile generate de Java se referă la erori fundamentale careviolează regulile limbajului sau ale mediului de execuţie. Excepţiile aruncate deprogramator sunt folosite de regulă pentru a raporta nesatisfacerea precon-diţiilor apelului unei metode. Pentru rezolvarea problemelor legate de excepţiisunt folosite cinci cuvinte rezervate: try, catch, throw, throwsşi finally. Iată cum funcţionează mecanismul:

• secvenţa de instrucţiuni care este susceptibilă de erori este pusă într-un bloc try; dacă se generează o excepţie, datorită execuţieiblocului de instrucţiuni, atunci Java creează un obiect excepţie şi îlaruncă metodei (spre secvenţa respectivă);

• dacă dorim să prindem excepţia, atunci folosim clauza catch,imediat după blocul try;

• dacă vrem să provocăm (aruncăm) o anumită excepţie atunci folo-sim throw; excepţiile fundamentale sunt provocate automat decătre Java;

• dacă anumite instrucţiuni trebuie să fie executate înainte de a ieşi(normal sau anormal) din metodă, atunci punem instrucţiunile într-un bloc finally;

• în antetul de definire a unei metode, pentru a specifica că metodaeste posibil să arunce o excepţie, trebuie specificată excepţia(excepţiile) folosind o clauză throws

Forma generală pentru manipularea excepţiilor este:try { . . . // Bloc de instructiuni in care pot apare exceptii}

Page 16: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

32

catch (TipExceptie1 objExceptie1) { . . . // Tratare exceptie TipExceptie1}catch (TipExceptie2 objExceptie2) { . . . // Tratare exceptie TipExceptie2} . . . ...finally { . . // Bloc de instructiuni ce se executa // inainte de a se incheia blocul try}

Pentru comoditate, în multe dintre programele noastre de test evitămfolosirea lui try - catch adăugând la definirea lui main:

public static void main(String a[]) throws Exception {

Aceasta provoacă tratarea automată a excepţiilor de către JVM.Tot pentru comoditate, spre a nu "ne bate capul" cu prea multe catch şi

a inventaria toate excepţiile posibile de aruncat dintr-un bloc try, plasăm unsingur catch sub forma:

catch(Exception e) { System.out.println(e.getMessage()); // eventual lipseşte e.printStackTrace(); System.exit(1); // eventual lipseşte}

Atragem însă atenţia că în cadrul programelor reale trebuie tratată fiecareexcepţie în parte în conformitate cu cerinţele problemei de rezolvat! În cazcontrar pot apărea conflicte serioase la interfaţa cu alte componente aleaplicaţiei! Într-unul dintre capitolele următoare vom prezenta o aplicaţie maimare în care vom aborda "cu simţ de răspundere" tratarea excepţiilor.

Aruncarea unei excepţii se face prin:new ExceptieAruncata (... );

Toate tipurile de excepţii sunt subclase ale claseijava.lang.Throwable cu două subclase: java.lang.Exception şijava.lang.Error. Pentru fiecare eroare apărută se instanţiază un obiect aluneia dintre cele două subclase. Printre altele, fiecare obiect excepţie conţine unstring ce are ca valoare mesajul explicativ al excepţiei sau erorii respective.Excepţiile generate (aruncate) prin program trebuie să fie de tip Exception.

Excepţiile de tipul Error sunt generate de mediu şi nu pot fi prinse deprogram (de exemplu blocarea mediului Java datorită memoriei insuficiente).

O parte din ierarhia de excepţii şi erori este:Throwable• Exception

• IOException

Page 17: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

33

• FileNotFoundException• ...

• RuntimeError• ArithmeticException• IndexOutOfBoundsException

• ArrayIndexOutOfBoundsException• StringIndexOutOfBoundsException

• NullPointerException• ...

• ...• Error

• VirtualMachineError• ...

1.3.1. Istanţierea şi distrugerea obiectelor. Constructori

InstanţiereaInstanţierea (crearea) unui obiect se face, după cum am mai arătat,

folosind new. Rutina care se execută în momentul instanţierii poartă numele deconstructor. Din punct de vedere formal, un constructor este o metodă care:poartă acelaşi nume cu cel al clasei, nu se specifică void la tipul întors, şi nuconţine return în interiorul corpului.

Dacă nu se specifică nici un constructor, atunci se generază automat unconstructor vid, fără parametri.

E posibil să se definească mai mulţi constructori. Mai mult, unii pot să-ifolosească pe alţii. Avem astfel de-a face cu o suprapunere (suprascriere) aconstructorilor.

În continuare exemplificăm prin câteva variante de constructori pentruclasa Cerc. La unele dintre ele vom folosi variabile locale - parametri şicâmpuri de date - cu aceleaşi nume, diferenţierea dintre ele făcându-se princuvântul rezervat this.

public class Cerc {public double x, y, r;public Cerc (double x, double y, double r) // Centrul (x,y), raza r

{this.x = x; this.y = y; this.r = r;}public Cerc (double r) // Centrul (0,0), raza r

{x = 0.0; y = 0.0;this.r = r;}public Cerc (Cerc c) // Copiere a altui cerc

{x = c.x; y = c.y; r = c.r;}public Cerc () // Centrul (0,0), raza 1

{x = 0.0; y = 0.0; r = 1.0;}- - -}

Page 18: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

34

Singura restricţie la constructorii multipli este aceea că orice doi cons-tructori diferiţi trebuie să aibă liste de parametri la care cel puţin o pereche deparametri corespunzători să difere. De exemplu, primul şi al doilea constructordiferă prin parametrul al doilea, prezent doar la primul constructor; al doilea şial treilea constructor au numai câte un parametru dar aceştia sunt de tipuridiferite ş.a.m.d.

Pentru a apela un constructor, în cadrul unui new numele clasei va aveaargumentele dorite, din care compilatorul va deduce despre care constructor estevorba. De exemplu, următoarele linii au acelaşi efect:

Cerc c = new Cerc(); c.x = 1.0; c.r = 0.5;

Cerc c = new Cerc (1.0, 0.0, 0.5);

Cuvântul this poate să apară într-un constructor pentru a desemna unalt constructor din aceeaşi clasă. De exemplu, cei patru constructori de mai susse pot defini şi astfel:public class Cerc {

public double x, y, r;public Cerc (double x, double y, double r) // Centrul (x,y), raza r

{this.x = x; this.y = y; this.r = r;}public Cerc (double r) // Centrul (0,0), raza r

{this(0.0,0.0,r);}public Cerc (Cerc c) // Copiere a altui cerc

{this(c.x,c.y,c.r);}public Cerc () // Centrul (0,0), raza 1

{this(0.0,0.0,1.0);}- - -

}

Singura restricţie de folosire a lui this pe post de constructor într-un altconstructor este aceea că apelul this trebuie să apară ca primă instrucţiune încorpul noului constructor. După acest apel pot să urmeze şi alte instrucţiuni.

Distrugerea obiectelorDistrugerea obiectelor se face automat prin mecanismul de Garbage

Collection care este un thread (fir de execuţie) de prioritate minimă. El culegezonele de memorie după ce acestea nu mai sunt folosite şi eliberează spaţiulocupat de ele. Aceasta este, după părerea noastră, cea mai mare diferenţă de stilde programare între Java şi C sau C++. Urmează ca cititorii să se convingăsinguri: va părea ciudat, cel puţin la început, să nu gestionezi eliberarea spaţiilorde memorie!

Este însă posibil ca eliberarea de spaţiu să se petreacă uneori târziu.Uneori (poate) este utilă forţarea eliberării mai rapide. Iată o posibilă schemă:

Page 19: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

35

public static void main() {int T = new int[100000];

- - -calcule(T);T = null; // Din acest moment, tabloul poate fi distrus- - -restul calculelor- - -}

Finalizator de obiecteDe multe ori, anumite obiecte sau resurse, cum ar fi spre exemplu,

descriptorii de fişiere şi socket-urile, sunt păstrate indefinit. Pentru a forţaeliberarea acestora, se pot folosi metode finalizatori, după modelul de mai jos.

protected void finalize() throws IOException{ if (fd != null) close(); // fd este un descriptor }

Există câteva reguli privitoare la finalizatori:

• dacă un obiect are o metodă finalizator, atunci ea este invocatăînainte de Garbage Collection;

• nu se garantează ordinea de intervenţie a lui Garbage Collection, deaceea nu se garantează momentele în care intră în lucru finaliza-toarele;

• după finalizator, obiectul nu mai este disponibil!• de multe ori, finalizatorul se asociază cu excepţiile.

1.3.2. Definiri statice (clasă):variabile, metode, iniţializatori

Variabile clasă (statice)Variabilele definite într-o clasă au câte o copie pentru fiecare instanţiere

a unui obiect. Există însă situaţii în care sunt necesare variabile proprii claselor,ale căror valori nu depind de instanţiere. Acestea vor fi numite variabile clasăsau variabile statice.

Metode clasă (statice)Analog cu variabilele clasă, există şi metode clasă, metode independente

de instanţiere. În secvenţa de mai jos vom defini două metode de comparare adouă cercuri. Una va compara cercul curent cu un alt cerc, iar alta va compara

Page 20: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

36

două cercuri primite prin parametri. Evident, prima va fi dependentă deinstanţiere, dar a doua va fi independentă de instanţiere.

public class Cerc {

public double x, y, r;- - -

public Cerc maxim (Cerc c){ if (c.r > r) return c; else return this;}

public static Cerc maxim (Cerc a, Cerc b){if (a.r > b.r) return a; else return b;}

- - -}

Apelurile lor pot fi invocate astfel:Cerc a = new Cerc (2.0);Cerc b = new Cerc (3.0);- - -// Doua utilizari ale primei metodeCerc c = a.maxim (b);Cerc d = b.maxim (a);- - -// Utilizare a celei de-a doua metodeCerc c = Cerc.maxim (a,b);- - -

În funcţie de context, programatorul va prefera una sau alta dintremetodele maxim.

Precizăm câteva caracteristici ale metodelor statice:

• sunt declarate cu prefixul static;• sunt invocate din clase în loc de a fi invocate din instanţe;• în astfel de metode nu se foloseşte this ! (fapt normal, deoarece

this desemnează instanţierea curentă).

Iniţializatori staticiAtât variabilele instanţă, cât şi cele statice, pot fi iniţializate, prin

mecanismele cunoscute din C şi C++. De exemplu,static int nr_cercuri = 0;

Java oferă posibilitatea de a face iniţializări mai complexe. Acestea sedesemnează prin construcţia static{. . .}, unde între acolade poate săapară o secvenţă de instrucţiuni. Iată de exemplu cum se poate iniţializa untablou cu valori ale funcţiilor trigonometrice, pentru a evita calcularea lor defiecare dată:

Page 21: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

37

public class Cerc {- - -

static private double[] sinus = new double[1000];static private double[] cosinus = new double[1000];

- - -static {

double x, deltax;int i;deltax = (Math.PI / 2) / (1000-1);for (i=0, x=0.0; i<1000; i++, x+=deltax) {

sinus[i] = Math.sin(x);cosinus[i] = Math.cos(x);

}- - -}

1.4. Interfeţe JavaInterfeţele Java abstractizează complet metodele unei clase (interfaţa

clasei) faţă de orice implementare. Folosind cuvântul rezervat interface (înloc de class) specificăm ce poate face o clasă şi nu cum realizează clasa acellucru.

1.4.1. Definirea unei interfeţe

Forma generală a definirii unei interfeţe Java esteinterface nume { tipRezultat1 numeMetoda1(listaParametri1); - - - tipRezultatn numeMetodan(listaParametrin);

tipVariabilaFinala1 numeVariabilaFinala1 = valoare1; - - - tipVariabilaFinalak numeVariabilaFinalak = valoarek;}

1.4.2. Implementarea interfeţelor

Odată ce o interfaţă a fost definită, una sau mai multe clase pot să ofere oimplementare a interfeţei. Pentru a implementa o interfaţă, o clasă trebuie săincludă cuvântul rezervat implements în antetul definiţiei sale:

class NumeClasa [ extends ClasaSuperioara ] implements numeInterfata1 [ , numeInterfata2 . . .] {

Page 22: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

38

după care să definească toate metodele declarate de interfaţă. De asemenea, seimpune ca toate metodele interfeţelor să fie declarate public. În consecinţă,clasele care implementează interfaţa trebuie să fie declarate public.

1.4.3. Apelul metodelor prinvariabile referinţe la interfeţe

Deoarece metodele unei interfeţe sunt implementate de o clasă, evidentcă le putem apela prin variabile referinţă la clasa care implementează interfaţa.Java tratează interfeţele ca şi clase pur abstracte. Astfel,

• putem să definim variabile de tip interfaţă (referinţe la interfaţă);• o variabilă interfaţă poate referi o instanţă a unei clase ce imple-

mentează interfaţa;• dacă o variabilă interfaţă referă o instanţă care implementează inter-

faţa atunci la apelul unei metode din interfaţă se va apela metodainstanţei (apel conform obiectului referit şi nu conform referinţei - caşi la redefinirea metodelor în cazul moştenirii).

1.5. Exemple de programe JavaÎn cele ce urmează prezentăm câteva aplicaţii standalone scrise în Java

prin care exemplificăm cele prezentate mai sus.

1.5.1. Calcule aritmetice simple

1.5.1.1. Distanţa între două puncte

Se cere determinarea distanţei euclidiene dintre două puncte ale cărorcoordonate se dau în linia de comandă sub forma a patru numere. Sursa estedată prin programul 1.7.

public class Distanta {

public static void main(String a[]) throws Exception { // Determina distanta intre punctele de coordonate (a[0],a[1]), (a[2],a[3]) double x1 = Double.parseDouble(a[0]); double y1 = Double.parseDouble(a[1]); double x2 = Double.parseDouble(a[2]); double y2 = Double.parseDouble(a[3]); System.out.println("Distanta este: "+ // Afiseaza distanta Math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)));

Page 23: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

39

} // Distanta.main

} // Distanta

Programul 1.7. Distanta.java

1.5.1.2. Calculul unor medii ponderate

Se cere un program care calculează succesiuni de medii ponderate aleunor note. Ponderile sunt date ca şi argumente în linia de comandă. Notele suntcerute pe rând de la intrarea standard. Sursa este dată în programul 1.8

import java.io.*;

public class MediiPonderate {

public static void main(String a[]) throws Exception { int i; String s; BufferedReader in = new BufferedReader( new InputStreamReader(System.in)); double[] p = new double [a.length]; double[] n = new double [a.length]; double medie, total; for (i=0, total=0; i<a.length; i++) { p[i] = Double.parseDouble(a[i]); total += p[i]; } // for for (;; ) { for (i=0, medie=0; i<a.length;i++) { System.out.print("Nota "+(i+1)+": "); s = in.readLine(); if (s == null) System.exit(0); medie += (p[i]*Double.parseDouble(s)); } // for medie /= total; System.out.println("Media: "+medie); } // for } // MediiPonderate.main

} // MediiPonderate

Programul 1.8. MediiPonderate.java

Page 24: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

40

Figura 1.5. Utilizarea MediiPonderate

În fig. 1.5 este prezentat un exemplu de utilizare a programului, pentrumedii ponderate cu ponderile 5, 7.5, 2.5 şi 5. (Altfel spus, prima şi ultima notăcontează la fel şi împreună cât celelalte două; nota a doua mai mult, a treia maipuţin ...):

Dacă din greşeală la lansarea programului de mai sus nu se dau ponderiîn linia de comandă în momentul lansării, atunci programul va intra în cicluinfinit!

1.5.2. Generarea de elemente aleatoare

1.5.2.1. Generarea de parole

În administrarea utilizatorilor din reţele este necesară o “rezervă” deparole cu care să opereze administratorii. Programul care urmează genereazăaleator 2000 parole de câte 8 caractere, litere mari, litere mici şi cifre zecimale.Sursa este dată în programul 1.9.

import java.io.*;import java.util.*;

public class GenPasswd {

public static void main(String a[]) { String car = "abcdefghijklmnopqrstuvwxyz"+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ "0123456789"; // Alfabetul StringBuffer lin = new StringBuffer(" "); // Spatiu pentru // o parola Random r = new Random(); // Obiect numar aleator for (int n=0; n<2000; n++) { for(int i=0; i<8; i++) lin.setCharAt(i, car.charAt(

Page 25: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

41

Math.abs(r.nextInt())%car.length())); // Genereaza un caracter System.out.println(lin); // Tipareste o parola } // for: cele 2000 parole generate } // GenPasswd.main

} // GenPasswd

Programul 1.9. GenPasswd.java

1.5.2.2. Generarea de expresii booleene

Pentru o culegere de probleme a fost necesară scrierea unor expresiibooleene. Acestea trebuie simplificate şi să se construiască circuitele combina-ţionale echivalente. Autorii au decis generarea aleatoare a unui anumit număr devariabile, a unui număr oarecare de termeni conjunctivi şi o manieră aleatoarede negare a variabilelor. Programul 1.10 face acest lucru.

import java.io.*;import java.util.*;

public class GenExpr { public static void main(String a[]) { String var="abcde"; // Variabilele String neg=" -"; // Negare sau nu String s; int v, n, t, i, j, k; Random r = new Random(); // Obiectul numar aleator for (i=0; i<20; i++) { v = 3 + Math.abs(r.nextInt())%3; // Numar de variabile t = 4 + Math.abs(r.nextInt())%8; // Numar de termeni conjunctivi for (s = "f(",k=0; k<v; k++) { s += var.substring(k,k+1); s += (k<v-1)?",":") = "; } // for: Partea stanga a definirii functiei for(j=0; j<t; j++) { for (k=0; k<v; k++) { n = Math.abs(r.nextInt())%2; // Alege negare sau nu s +=neg.substring(n,n+1)+var.substring(k,k+1); // Lipeste variabila negata sau nu } // for: generat un termen conjunctiv if (j < t-1) s += " V "; // Pune operatorul SAU } // for: Expresia booleeana System.out.println(s); // Tipareste expresia generata } // for: cele 20 expresii } // GenExpr.main} // GenExpr

Programul 1.10. GenExpr.java

Page 26: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

42

Iată, în fig. 1.6, cum arată rezultatul execuţiei acestui program.

Figura 1.6. Generarea unor expresii booleene

1.5.3. Ordonare liniiProgramul 1.11 prezintă un exemplu simplu de operare cu string-uri. Se

citeşte de la intrarea standard o succesiune de linii. Programul reţine aceste liniişi apoi le afişează în ordine alfabetică a conţinutului lor. Terminarea transmiteriiliniilor de la intrarea standard trebuie marcată prin CTRL/Z (cazul Windows),respectiv CTRL/D (cazul Unix), pentru a marca sfârşitul fişierului de intrarestandard.

import java.io.*;import java.util.*;

public class Linii {

public static void main(String a[]) { BufferedReader in = new BufferedReader( new InputStreamReader(System.in)); String s, d; // String-uri de serviciu Vector ts = new Vector(); // Tabloul de stringuri int n; for(n=0; ; ) { // Citeste linie cu linie si adauga la ts s=null; try { s = in.readLine(); } // try catch (IOException e) { e.printStackTrace(); } // catch if (s==null) break; // S-a tastat sfarsitul intrarii ts.add(s); n++; } // for for(int i=0; i<n-1; i++){ // Ordoneaza tabloul

Page 27: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

43

for(int j=i+1; j<n; j++) { d=(String)ts.elementAt(j); s=(String)ts.elementAt(i); if (s.compareTo(d) > 0) { ts.set(i, d); ts.set(j, s); } // if } // for j } // for i System.out.println("\nLiniile ordonate lexicografic:\n"); for (int i=0; i<n;i++){ s=(String)ts.elementAt(i); System.out.println(s); // Afiseaza liniile } // for } // Linii.main

} // Linii

Programul 1.11. Linii.java

Iată, în fig. 1.7 o mostră de execuţie a acestui program:

Figura 1.7. Ordonarea liniilor

1.6. Thread-uri JavaÎn prezent, tendinţele în programarea concurentă utilizează în loc de

proces conceptul de thread [3, 6, 14]. Pachetul java.lang oferă clasaThread. Aceasta, împreună cu unele metode moştenite de la clasa Object,permit crearea, execuţia, controlul şi sincronizarea thread-urilor. În particular,

Page 28: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

44

sincronizarea se bazează pe conceptul de monitor. Este cunoscut faptul căoperarea multithreading dă multă bătaie de cap programatorilor C şi C++.Programatorii trebuie să-şi implementeze mecanisme proprii de blocare a unorsecţiuni critice şi de partajare a unor resurse critice. Primitivele furnizate deJava reduc esenţial acest efort.

1.6.1. Elemente de limbaj Java în contextul thread-urilor

Utilizarea thread-urilor Java este deja bine cunoscută în literatură [14,32]. Având în vedere faptul că ne vom referi frecvent la o serie de metodespecifice lucrului cu thread-uri, considerăm oportună o trecere în revistă aacestora. Pentru detalii se poate consulta [14].

1.6.1.1. Obiecte versus thread-uri

Având în vedere importanţa conceptului de thread, prezentăm pe scurtmetodele din clasa Object care se referă la thread-uri. Reţinem din clasă doarceea ce, într-un fel sau altul, poate fi util în context multithreading. Prototipurilemetodelor se găsesc în documentaţiile standard [91].

Metodele wait()Pun obiectul în aşteptare până la apariţia unui eveniment. Ultimele douăforme indică o durată maximă de aşteptare, fie numai înmilisecunde, fie în milisecunde plus nanosecunde.

Metodele notify() şi notifyAll()Sunt duale metodelor wait(), ele anunţă alte obiecte apariţia unuieveniment.

Metoda getClass()Întoarce clasa din care s-a instanţiat obiectul apelant al metodei.

Metoda equals()Oferă, prin suprascriere în subclase, posibilitatea de a se defini ceînseamnă egalitatea a două obiecte din clasa respectivă. Implementareaacestei metode trebuie să respecte, pentru orice referiri nenule x, y,z, următoarele condiţii:

• reflexivitate, adică x.equals(x) trebuie să întoarcă true;

Page 29: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

45

• simetrie, adică x.equals(y) este true dacă şi numai dacăy.equals(x) este true;

• tranzitivitate, adică x.equals(y) este true şi y.equals(z) estetrue implică x.equals(z) este true;

• consistenţă, adică datele utilizate în metoda equals să nu modificerelaţia între două obiecte; deci, pentru două obiecte x şi y, apelurilemultiple x.equals(y) trebuie să întoarcă mereu fie true, fiefalse;

• referinţa null: x.equals(null) să fie mereu false.

Metoda clone()Creează o copie a obiectului. Înţelesul exact al “copiei” depinde de clasă.În general următoarele condiţii sunt adevărate:

• x.clone() != x este true;• x.clone90.getClass() == x.getClass() este true;• x.clone().equals(x) este true.

Sunt extrem de rare cazurile în care apar mici abateri de la aceste reguli.Este vorba de situaţiile în care suprascrierea lui clone realizează o“copiere superficială”.

Metoda finalize()Poate fi suprascrisă în subclase şi ea conţine acţiunile pe care compo-nenta “garbage collection” din JVM le va executa imediat înainte de adistruge un obiect. Trebuie remarcat faptul că programul utilizator nupoate stabili cu exactitate momentul şi ordinea de execuţie a metodelorfinalize legate de obiecte expirate.

Metoda toString()Întoarce reprezentarea textuală a obiectului. Este utilă uneori în activi-tatea de depanare. Se recomandă suprascrierea ei dacă se intenţionează afi utilizată.

1.6.1.2. Clasa Thread

În acelaşi stil ca şi în secţiunea precedentă, prezentăm principalelemetode ale clasei Thread.

Dacă se specifică în constructor un obiect tinta , atunci corpul thread-ului (corpul metodei run) este ataşat acestui obiect. Dacă nu se specifică,

Page 30: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

46

atunci corpul este ataşat thread-ului nou creat. Prin constructor, sau mai târziufolosind metoda setName(), se poate atribui thread-ului un nume. Dacă nu sespecifică altă valoare, atunci numele implicit va fi “Thread-“+n, unde n esteun întreg. Metoda getName() întoarce numele curent al thread-ului.

Metoda start()Lansează în execuţie noul thread. Din acest moment, execuţia progra-mului este controlată de (cel puţin) două thread-uri: pe de o partefuncţionează thread-ul curent - cel care execută start(), iar pe de altăparte se lansează în execuţie noul thread, ale cărui instrucţiuni suntprecizate în metoda run().

Metoda runConţine corpul efectiv al thread-ului. Întreaga activitate curentă a nouluithread trebuie descrisă prin suprascrierea acestei metode.

Metodele sleep()Pun thread-ul curent în aşteptare un anumit interval de timp.

Metodele join()Aşteaptă ca obiectul thread care le apelează să moară. Ultimele douăforme limitează timpul maxim de aşteptare.

Metoda yield()Cedează controlul de la obiectul thread la planificatorul JVM, pentru apermite unui alt thread (green) să ruleze.

Metoda interrupt()Trimite o întrerupere obiectului thread care o invocă.

Metodele interrupted() şi isInterrupted()Testează dacă thread-ul apelant a fost întrerupt sau nu. Metodainterrupted() modifică statutul de întrerupt, aşa că la un dublu apelal ei starea thread-ului revine la vechea ei formă. MetodaisInterrupted nu modifică această stare.

Metoda isAlive()Răspunde dacă obiectul thread apelant este în viaţă sau nu.

Page 31: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

47

Metoda activeCount()Întoarce numărul de thread-uri active din grupul curent. MetodacurrentThread() întoarce numărul thread-ului curent. Metodaenumerate() întoarce în tabloul t thread-urile membre ale grupuluicurent şi ale celor din subgrupuri. Metoda getThreadGroup()întoarce grupul curent de thread-uri. Metoda dumpStack() afişează laieşirea standard stiva curentă de thread-uri.

Metodele stop(), suspend() şi resume()Au fost definite în primele versiuni Java. Ulterior s-a dovedit că folosirealor poate conduce, în cazul unei proiectări neatente a programului, laimpas şi din această cauză începând cu versiunea 1.2 au fost declarate“deprecated”, motiv pentru care nici noi nu le vom acorda prea mareatenţie. Metoda stop() provoacă oprirea execuţiei unui thread. Metodasuspend() suspendă temporar execuţia thread-ului, reluarea şi conti-nuarea execuţiei făcându-se cu metoda resume().

1.6.2. Operaţii asupra thread-urilor Java;creare, terminare

1.6.2.1. Crearea unui thread

Crearea unui thread folosind Java API se poate face în două moduri:

• implementând o clasă derivată din clasa predefinită Thread, clasăcare face parte din pachetul java.lang şi ca urmare este importatăautomat în orice program Java;

• definind o clasă care implementează interfaţa Runnable.

Primul dintre ele constă în a declara o clasă ca subclasă a claseiThread. În interiorul acesteia se redefineşte metoda run(), metodă careconţine “corpul” de instrucţiuni ale thread-ului. După aceea, se poate defini şiinstanţia noua subclasă. Schematic, crearea se face conform modelului de maijos.class ThreadPropriu extend Thread { // - - - datele subclasei - - - ThreadPropriu ( /* parametrii constructorului */ ) { // - - - descrierea constructorului } public void run() { // - - - defineste corpul thread-ului ThreadPropriu - - - }

Page 32: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

48

}

A doua metodă constă în implementarea clasei Runnable. Aceastăinterfaţă conţine o unică metodă, şi anume metoda run:

interface Runnable { public void run ();

}

Astfel, o clasă care implementează interfaţa Runnable, evită descen-denţa din clasa standard Thread. Instanţele acestei clase sunt create ca obiecteactive, în thread-uri diferite de thread-ul curent. Schematic, acest mod de crearea unui thread apare aproape ca mai sus, cu excepţia primei linii:

class ThreadPropriu - - - implements Runnable { // - - - datele subclasei - - - ThreadPropriu ( /* parametrii constructorului */ ) { // - - - descrierea constructorului } public void run() { // - - - defineste corpul thread-ului ThreadPropriu - - - }}

După definirea clasei ThreadPropriu, indiferent de modalitate,urmează crearea thread-ului şi lansarea lui în execuţie:

ThreadPropriu thr = new ThreadPropriu(- - -);thr.start();

Mai întâi se creează obiectul thread, după care se lansează metodastart() pentru efectuarea iniţializărilor.

Execuţia curentă a unui thread constă în execuţia metodei run(),similară din acest punct de vedere cu funcţia main() a unui programstandalone. Spre deosebire de main(), metoda run() nu primeşte argumentedin linia de comandă. Eventualii parametri pot fi transmişi thread-ului princonstructor, variabile statice sau prin alte alte metode stabilite de programator.

1.6.2.2. Terminarea unui thread

JVM execută un thread, precum şi pe cele pe care acesta le creează, pânăcând apare una dintre următoarele situaţii:

• Este apelată metoda exit() a clasei Runtime, iar managerul desecuritate a permis îndeplinirea cu succes a acestui apel.

• Toate thread-urile care nu sunt daemon s-au terminat, fie prinîntoarcerea din apelul metodei run(), fie printr-un apel al metodei

Page 33: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

49

stop() din partea altui thread, fie prin trimiterea unei întreruperi(interrupt()) din partea altui thread.

• S-a invocat metoda stop() a obiectului thread (metodă depăşită).

La terminarea unui thread, resursele specifice acestuia sunt eliberate.Obiectul thread nu va fi şters din memorie de către garbage collector decât ladereferenţierea manuală a instanţei (atribuirea valorii null pentru referinţă)sau la terminarea programului.

1.6.2.3. Aşteptarea terminării unui thread

Dacă se doreşte aşteptarea terminării unui thread, se poate folosi metodajoin() a obiectului thread după care se aşteaptă. Thread-ul a cărui terminarese aşteaptă nu este afectat de acest apel. Funcţia join() are acelaşi efect ca şicombinaţia de metode sleep() şi isAlive().

Apelul this.join() înseamnă “wait forever” pentru că thread-ulcurent nu se va termina niciodată datorită acestui apel de aşteptare.

Funcţia join() apelată pentru un thread, care nu şi-a început execuţiasau s-a terminat, returnează fără să aştepte.

1.6.3. Sincronizarea thread-urilor Java

1.6.3.1. Conceptul de monitor Java; synchronized

Mecanismele de sincronizare ale thread-urilor Java au la bază conceptulde monitor (vezi [14]). Obiectele monitor aplică principiul excluderii mutualepentru grupul de proceduri sincronizate. Excluderea mutuală la datele partajatese obţine prin accesul serializat al thread-urilor: "un thread odată".

Limbajul Java a implementat noţiunea de monitor ca un element asociatfiecărui obiect Java. Utilizarea directă a monitoarelor este permisă prinintermediul cuvântului rezervat synchronized aplicat funcţiilor membre sauunor secţiuni de cod din interiorul metodelor.

Ca urmare, pentru a proteja o secţiune critică, se poate declara sincro-nizată a) o metodă completă, sau b) un bloc (o secvenţă de instrucţiuni) dintr-ometodă. În prima situaţie, mecanismul de sincronizare este asociat obiectuluicurent (mai corect spus, monitorului acestuia) iar în al doilea caz, se poate alegeobiectul de sincronizare, de exemplu, un obiect declarat static în clasa curentăsau din nou, obiectul curent. Ca regulă, nu se alege ca obiect de sincronizare unobiect a cărui valoare se modifică în interiorul blocului. Deci, pentru un obiect,statutul de monitor se poate obţine fie pe întreaga durată a execuţiei unei meto-de, fie pe durata execuţiei unei anumite secvenţe de instrucţiuni.

Page 34: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

50

Schematic, aplicarea declaraţiilor synchronized se face după una dinurmătoarele două scheme:

public - - - synchronized - - - numeMetoda (… ){ - - - }sau

synchronized (obiect) { bloc (secvenţă de instrucţiuni) }

În prima situaţie, un apel obiect.numemetoda(…) face ca obiectsă primească statutul de monitor. Deci orice apel obiect.altaMetoda(…),unde altaMetoda este şi ea sincronizată, va rămâne în starea de aşteptare aexclusivităţii până când apelul obiect.numemetoda(…) îşi încheie activi-tatea.

În a doua situaţie, se conferă obiectului obiect statutul de monitorpână la terminarea secvenţei de instrucţiuni specificată între acoladele instruc-ţiunii synchronized. Trebuie remarcat faptul că obiectul argument al ins-trucţiunii synchronized nu este obligatoriu să coincidă cu obiectul curent.

În legătură cu metodele/secţiunile de program sincronizate trebuie făcuteurmătoarele precizări:

Când este apelată o procedură sincronizată (care conţine atributulsynchronized) pentru un obiect, se verifică monitorul asociat obiectului.Dacă există alte metode sincronizate care se execută, prima metodă se va bloca,până la terminarea lor. Deci se pot executa în paralel: o singura metodă sincro-nizată cu mai multe metode nesincronizate

Metode sincronizate staticeÎn cazurile precedente de sincronizare, am precizat că acest mecanismeste posibil prin obţinerea monitorului asociat obiectului curent desincronizare. Metodele statice nu sunt particulare unei anumite instanţe aclasei curente, motiv pentru care la sincronizarea acestora se foloseşte unobiect denumit class lock. Şi în acest caz, sistemul este destul deinteligent ca să suporte blocări imbricate, deci dacă o metodă staticăsincronizată este apelată din interiorul altei metode statice sincronizate,nu mai este cerut obiectul class lock asociat.

1.6.3.2. Mecanismul wait/notify

Un thread care are exclusivitatea asupra unui obiect cu statut de monitorpoate ceda temporar exclusivitatea. Această cedare temporară se face apelândmetoda wait(). Ca efect, este cedată exclusivitatea asupra obiectului, iarthread-ul curent îşi opreşte temporar execuţia instrucţiunilor lui, adică trece înstarea Waiting.

Page 35: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

51

Reluarea exclusivităţii cedate de către un thread care a executat un waitare loc atunci când thread-ul respectiv “se trezeşte” ca urmare a receptării unuianunţ trimis de către un alt thread activ care execută una dintre metodelenotify() sau notifyAll(). În urma acestui eveniment, thread-ul îşirecapătă exclusivitatea cedată anterior şi îşi continuă execuţia instrucţiunilor deunde şi-a oprit-o anterior (după apelul wait()).

Anunţul efectuat prin notify() se adresează tuturor thread-urilor caresunt în starea Waiting, dar numai unul dintre ele îşi va relua activitatea, celelaltevor rămâne în continuare în starea Waiting. În schimb, anunţul efectuat prinnotifyAll() este recepţionat de către toate thread-urile aflate în stareaWaiting. În consecinţă toate thread-urile ies din această stare şi îşi continuăactivitatea.

Este posibil ca un thread să solicite intrarea în starea Waiting doar pentruun interval limitat de timp. În acest caz el va specifica în apelul wait duratamaximă pe care o admite.

De asemenea, un thread poate ieşi din starea Waiting dacă un alt threadapelează metoda interrupt() a thread-ului aflat în starea Waiting. În acestcaz execuţia lui se reia cu secvenţa catch ce interceptează întreruperea.

În sumar, prezentăm scenariul de comunicare între thread-uri, folosindfuncţiile wait şi notify:

1. Un thread are nevoie în timpul execuţiei de o condiţie despre carepresupune că va fi realizată în alt thread.

2. Primul thread aşteaptă realizarea condiţiei cu ajutorul metodeiwait(), oferind astfel fiecărui thread suport pentru acest meca-nism de comunicare.

3. Thread-ul care realizează condiţia în timpul execuţiei, anunţă acestlucru unui thread care aşteaptă, prin apelul funcţiei notify().

Metodele wait() şi notify() trebuie apelate din interiorul uneimetode sau bloc sincronizat, pentru a preveni eventuale rezultate incorecte,generate de lipsa sincronizării. Înainte de a intra în aşteptare folosind wait(),thread-ul curent eliberează obiectul de sincronizare blocat şi-l obţine din noucând thread-ul este anunţat că a fost realizată condiţia aşteptată.

Metodele wait() şi notify() sunt metode native, scrise în C şi nueste posibilă reimplementarea lor folosind numai limbajul Java.

Mai facem câteva precizări în legătură cu comportarea mecanismuluiwait/notify:

• Dacă notify() este apelată dintr-un thread, dar nu este nici unthread care să aştepte, atunci metoda notify() returnează fără nicio eroare.

Page 36: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

52

• Funcţia wait() eliberează obiectul de sincronizare la intrarea înaşteptare şi îl reobţine la recepţionarea unei notificări. Sistemulasigură atomicitatea acestor operaţii critice.

• Dacă există mai multe thread-uri care aşteaptă o anumită condiţie, nuse poate ştii care thread primeşte notificarea după apelul funcţieinotify(). Acest lucru depinde de JVM şi de mecansimul deplanificare a thread-urilor.

• Dacă se doreşte apelul funcţiilor wait() şi notify() din metodestatice, atunci se declară în interiorul clasei un obiect de sincronizarestatic, iar funcţiile wait() şi notify() se vor apela pentru acestobiect static.

Diferenţa esenţială dintre funcţiile wait şi sleep constă în faptul cămetoda sleep nu este obligatoriu să fie apelată dintr-un bloc sincronizat şi nuimplică blocarea sau deblocarea unui obiect de sincronizare.

1.6.3.3. O schemă exemplu de sincronizări

În cele ce urmează, pentru a exemplifica utilizarea synchronized,wait(), notify(), notifyAll(), prezentăm schematic părţile esenţialeale unei clase Bd care poate fi accesată în regim de cititor/scriitor [14].

class Bd { static int cititori = 0; - - - - - - - - - - - - public void citeste ( - - - ){ - - - synchronized (this) { cititori++; } - - - // citirea propriuzisa synchronized (this) { cititori--; notify(); } } public synchronized void scrie( - - - ) { while (cititori > 0) { - - - - - - - - try { wait(); } catch (InterruptedException e) { - - - } } - - - // scrierea propriu-zisa notifyAll(); }

Page 37: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

53

Partea esenţială pentru sincronizare constă din definirea variabileicititori şi a metodelor citeste şi scrie.

Variabila clasă cititori reţine de fiecare dată câţi cititori sunt activila un moment dat. După cum se poate observa, instanţa curentă a clasei Bd esteblocată (pusă în regim de monitor) pe parcursul acţiunilor asupra variabileicititori. Aceste acţiuni sunt efectuate numai în interiorul metodelor scrieşi citeste.

Metoda citeste incrementează (în regim monitor) numărul de cititori.Apoi, posibil concurent cu alţi cititori, îşi efectuează activitatea, care aici constădoar în afişarea stării curente. La terminarea acestei activităţi, în regim monitordecrementează şi anunţă thread-urile aşteptătoare. Acestea din urmă sunt, cusiguranţă, numai scriitori.

Metoda scrie este declarată synchronized (regim monitor), deoa-rece întreaga ei activitate se desfăşoară fără ca celelalte procese să acţionezeasupra Bd.

1.6.4. Un exemplu de multithreading:alinierea cuvintelor într-un fişier text

Enunţul problemei este simplu:

Se cere rearanjarea paragrafelor dintr-un text aşa încât toate liniile, cuexcepţia ultimei din paragraf, să aibă exact aceeaşi lungime, iar cuvintelesă fie dispuse (cât mai) echidistant în linie.

Abordăm problema multithreading, în sensul următor. Se defineşte unrecipient de cuvinte, care să funcţioneze după disciplina FIFO şi să sesincronizeze ca şi buffer-ul de la problema producătorului şi a consumatorului[14]. Programul este descris cu ajutorul a patru clase:

• RecipientCuvinte, care înmagazinează un număr limitat decuvinte şi le livrează la cerere;

• ProducătorCuvinte, este un thread care citeşte textul dereformatat de la intrarea standard şi îl depune înRecipientCuvinte;

• ConsumatorCuvinte, este un thread care extrage pe rând cuvinte dinRecipientCuvinte, le pune în linii şi distanţează uniformcuvintele între ele;

• AliniereCuvinte, care este programul principal.

Vom prezenta pe scurt fiecare dintre clase.

Page 38: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

54

1.6.4.1. Clasa RecipientCuvinte

Clasa RecipientCuvinte foloseşte un Vector pentru a memoramaximum RECIPIENT cuvinte. Constructorul primeşte şi reţine valoareaRECIPIENT. (Se ştie că tipul Vector nu impune limitări asupra numărului deobiecte din el. Noi am introdus această limitare numai pentru a rămâne încondiţiile problemei clasice a producătorului şi a consumatorului.)

Metoda get, asigură, în regim de monitor, livrarea următorului cuvântdin recipient. Dacă nu are cuvânt de livrat, atunci thread-ul solicitant este pus înaşteptare până când producătorul livrează (cel puţin) un cuvânt.

Metoda put, asigură, în regim de monitor, preluarea de la producător aurmătorului cuvânt şi depunerea lui în recipient. Dacă nu mai este spaţiu (amlimitat spaţiul doar cu scop didactic), atunci thread-ul producător este pus înaşteptare până când consumatorul extrage (cel puţin) un cuvânt.

Textul sursă al clasei este prezentat în programul 1.12.

import java.util.*;

class RecipientCuvinte { private Vector b = new Vector(); private String continut; private int RECIPIENT;

public RecipientCuvinte(int recipient) { RECIPIENT = recipient; } // RecipientCuvinte.RecipientCuvinte

public synchronized String get() { // Extrage urmatorul cuvant din recipient, // daca are ce extrage while (b.size()==0) { try { wait(); } // try catch (Exception e) { e.printStackTrace(); } // catch } // while continut = (String)b.elementAt(0); // Extrage cuvantul b.remove(0); // Sterge-l notify(); return continut; } // RecipientCuvinte.get

public synchronized void put(String continut) { // Depune inca un // cuvant in recipient, daca are loc while (b.size()>=RECIPIENT) { try { wait(); } // try catch (Exception e) {

Page 39: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

55

e.printStackTrace(); } // catch } // while b.add(continut); notify(); } // RecipientCuvinte.put

} // RecipientCuvinte

Programul 1.12. RecipientCuvinte.java

1.6.4.2. Clasa ProducătorCuvinte

ProducatorCuvinte este un thread care citeşte linii dintr-un fişier,le desparte în cuvinte şi depune cuvintele în recipient. Constructorul thread-uluiprimeşte şi reţine referinţa la recipient şi la fişierul de unde îşi citeşte liniile.

Metoda run, corpul thread-ului, citeşte fişierul linie după linie. Laîntâlnirea unei linii goale sau a unei linii care începe cu caracterul '\t' (TAB),consideră că paragraful precedent s-a terminat şi introduce, numai pentru uzullui ConsumatorCuvinte, caracterul '\t' ca şi marcaj de terminare aparagrafului.

La întâlnirea sfârşitului de fişier, de asemenea numai pentru uzul luiConsumatorCuvinte, introduce '\n' ca şi marcaj de sfârşit de fişier.

În mod normal, fiecare linie este împărţită în cuvinte cu ajutorul unuiobiect StringTokenizer, iar apoi fiecare dintre cuvinte este depus înrecipient cu ajutorul metodei put a acestuia. Este posibil (şi probabil) ca dincând în când thread-ul să aştepte extragerea de către ConsumatorCuvinte aunui cuvânt din recipient înainte de a depune cuvântul curent.

Sursa clasei este prezentată în proogramul 1.13.

import java.util.*;import java.io.*;

class ProducatorCuvinte extends Thread { private RecipientCuvinte r; private BufferedReader in;

ProducatorCuvinte(RecipientCuvinte r, BufferedReader in) { this.r = r; this.in = in; } // ProducatorCuvinte.ProducatorCuvinte

public void run() { String s = ""; StringTokenizer st; r.put("\t"); for (;;) {

Page 40: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

56

try { s = in.readLine(); } // try catch (Exception e) { e.printStackTrace(); } // catch if (s == null) break; // Sfarsit fisier intrare if (s.equals("")) { r.put("\t"); // Depune marcaj de paragraf continue; } // if if (s.charAt(0) == '\t') r.put("\t"); // Depune marcaj de paragraf st = new StringTokenizer(s.trim()); for ( ; st.hasMoreTokens(); ) r.put(st.nextToken()); // Depune cuvant } // for r.put("\n"); // Depune sfarsit de fisier } // ProducatorCuvinte.run

} // ProducatorCuvinte

Programul 1.13. ProducătorCuvinte.java

1.6.4.3. Clasa ConsumatorCuvinte

ConsumatorCuvinte este un thread care extrage succesiv cuvintedin recipient, le aranjează (frumos) în linii şi le depune pe fişierul de ieşire.Constructorul thread-ului primeşte şi reţine referinţa la recipient, referinţa lafişierul de ieşire şi lungimea curentă a liniei de ieşire.

Metoda run, corpul thread-ului, citeşte din recipient cuvânt dupăcuvânt, folosind metoda get şi reţine într-un string cuvintele citite, unul dupăaltul, separate printr-un spaţiu. La întâlnirea unui caracter '\t' (TAB), caresemnifică sfârşit de paragraf, scrie la ieşire ultima linie a paragrafului.

La întâlnirea caracterului '\n' (marcaj de sfârşit de fişier), scrie ultimalinie (a ultimului paragraf) şi thread-ul se termină.

În celelalte cazuri, verifică dacă următorul cuvânt mai încape în liniacurentă (nu a depăşit încă LUNGIME). În caz afirmativ depune cuvântul în liniade ieşire. Dacă cuvântul curent nu mai încape, atunci transmite linia curentă(fără cuvântul curent) metodei distantare care aliniază cuvintele din linie.Alinierea se face plasând primul cuvânt aliniat la stânga în linie, ultimul cuvântaliniat la dreapta, iar celelalte cuvinte sunt deplasate aşa încât spaţiile dintre elesă fie plasate cât mai uniform.

Page 41: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

57

Extragerile din recipient se fac prin invocarea metodei get a recipien-tului. Este posibil (şi probabil) ca din când în când thread-ul să aştepte extra-gerea din recipient înainte de a depune.

Sursa clasei este prezentată în programul 1.14.

import java.io.*;import java.util.*;

class ConsumatorCuvinte extends Thread { private RecipientCuvinte r; private PrintStream out; private int LUNGIME;

ConsumatorCuvinte(RecipientCuvinte r, PrintStream out, int lungime) { this.r = r; this.out = out; LUNGIME = lungime; } // ConsumatorCuvinte.ConsumatorCuvinte

private String distantare(String s) { int i, nrSeparatori, nrSeparatoriMaiLungi=0, pas=0, start, x; String separator; StringTokenizer st; for (i=0, nrSeparatori=0; i<s.length(); i++) if (s.charAt(i) == ' ') nrSeparatori++; if (nrSeparatori == 0) return s; nrSeparatoriMaiLungi = (LUNGIME - s.length()) % nrSeparatori; if (nrSeparatoriMaiLungi != 0) pas = nrSeparatori / nrSeparatoriMaiLungi; start = pas / 2; x = (pas+1)*(nrSeparatoriMaiLungi-1)+1; if (x <= nrSeparatori) { pas++; start = (nrSeparatori - x) /2; } // if for (i=0, separator=" ";

i<(LUNGIME-s.length())/nrSeparatori; i++, separator+= " ");

st = new StringTokenizer(s); for (i=0; st.hasMoreTokens(); i++) if (i==0) s = st.nextToken(); else if (nrSeparatoriMaiLungi == 0) s += separator+st.nextToken(); else if ((((i-1)%pas)!=start)) s += separator+st.nextToken(); else {

Page 42: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

58

s += " "+separator+st.nextToken(); nrSeparatoriMaiLungi--; } // if return s; } // ConsumatorCuvinte.distantare

public void run() { String linie = "", cuvant; for (; ; ) { cuvant = r.get(); if (cuvant.equals("\n")) break; // Sfarsitul intrarii de cuvinte if (cuvant.equals("\t")) { if (linie.equals("")) continue; out.println(linie); out.println(); // Încheie vechiul paragraf linie = ""; continue; } // if if (linie.length()+1+cuvant.length() >= LUNGIME) { out.println(distantare(linie)); // Scrie o linie din paragraf linie = ""; } // if if (linie.equals("")) linie = cuvant; else linie += " "+cuvant; } // for out.println(linie); } // ConsumatorCuvinte.run

} // ConsumatorCuvinte

Programul 1.14. ConsumatorCuvinte.java

1.6.4.4. Clasa AliniereCuvinte

Clasa principală a programului este AliniereCuvinte. Mai întâi sedefinesc constantele lungime a unei linii de ieşire şi capacitate a recipientului.Apoi se definesc şi construiesc câte un obiect recipient, un thread producător şiunul consumator. Intrarea pentru producător este intrarea standard, iar ieşireapentru consumator este ieşirea standard. Programul se încheie cu lansarea înlucru a celor două thread-uri.

Sursa clasei este prezentată în programul 1.15.

Page 43: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

59

import java.io.*;

class AliniereCuvinte { final public int LUNGIME = 60, RECIPIENT = 100; RecipientCuvinte r; ProducatorCuvinte p; ConsumatorCuvinte c;

public static void main(String[] a) { AliniereCuvinte ac = new AliniereCuvinte(); ac.r = new RecipientCuvinte(ac.RECIPIENT); ac.p = new ProducatorCuvinte(ac.r, new BufferedReader( new InputStreamReader(System.in))); ac.c = new ConsumatorCuvinte(ac.r, System.out, ac.LUNGIME); ac.p.start(); ac.c.start();

} // AliniereCuvinte.main

} // AliniereCuvinte

Programul 1.15. AliniereCuvinte.java

1.7. Procese externe lansate din JavaFacilităţile independente de platformă oferite de limbajul Java sunt

completate de posibilitatea utilizării de cod extern, specific platformei. Estevorba fie de includerea în codul Java a unor funcţii native, fie de existenţa uneiinterfeţe simple la aplicaţii şi utilitare specifice platformei gazdă. În acestparagraf, vom prezenta cea de-a doua facilitate dependentă de platformă.

Astfel, limbajul Java permite lansarea în execuţie din interiorul unuiprogram, a unei aplicaţii executabile externe JVM. De exemplu, se poate lansaun fişier de tip .bat sau .exe sub Windows, sau un fişier shell, sub Unix.Interfaţa de execuţie a unor comenzi externe din programe Java, este realizatăcu ajutorul metodei statice exec, din clasa Runtime [91]. Există patruvariante de apel ale acestei metode, prezentate în continuare:

public Process exec (String command);public Process exec (String [] cmdArray);public Process exec (String command, String [] envp);public Process exec (String [] cmdArray, String [] envp);

Metoda exec primeşte ca parametru un şir (sau mai multe şiruri) decaractere, reprezentând comanda (comenzile) externă de executat. Dacă esteprezent al doilea parametru, acest şir de caractere conţine setările variabilelor demediu, sub forma nume=valoare.

Page 44: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

60

Metoda exec lansează un thread separat pentru execuţia comenzii(comenzilor) respective şi returnează un obiect de tip Process, asociatprocesului creat, astfel, în sistem. Dacă aplicaţia lansată din programul Java arenevoie de date de intrare, acestea sunt transmise aplicaţiei externe cu ajutorulobiectului OutputStream, ataşat obiectului Process:

OutputStream getOutputStream();

Similar, eventualele date de ieşire ale aplicaţiei, se pot obţine folosindobiectul InputStream, al aceluiaşi obiect Process:

InputStream getInputStream();

Se recomandă ca înainte de a obţine rezultatele aplicaţiei, să se aştepteterminarea procesului creat folosind metoda waitFor(), din clasa Process.De asemenea, codul de retur al aplicaţiei se poate obţine cu ajutorul metodeiexitValue(). Dacă întregul întors are valoarea 0, atunci aplicaţia s-aterminat cu succes, altfel a apărut o eroare.

În programul 1.16 prezentăm un exemplu de program Java, care afişeazăconţinutul directorului curent folosind o comandă externă, mai precis un fişierde comenzi shell.

import java.io.*;

public class ExecJava {

static String com="fisier.bat"; static int SMAX=1000;

public static void main(String argv[]) { try { Process p; p=Runtime.getRuntime().exec(com); p.waitFor();

// obtine codul de retur al aplicatiei externe int exval=p.exitValue(); System.out.println(exval);

// obtine datele de iesire InputStream in=p.getInputStream(); byte blin[]=new byte[SMAX]; int noct=in.read(blin,0,SMAX); String s=new String(blin,0,noct); System.out.println(s); } catch(IOException e1) { System.out.println(e1); } catch(InterruptedException e2){

Page 45: 1. COMPENDIU JAVA - cs.ubbcluj.rovcioban/MateInfo/Anul2/Map/CursJava/01Compen… · 17 1. COMPENDIU JAVA Prezenta lucrare presupune că cititorul deţine un nivel minimal de cunoş-tinţe

61

System.out.println(e2);} }// ExecJava.main

}// ExecJava

Programul 1.16. ExecJava.java

Fişierul fisier.bat, dacă este sub Windows poate conţine numaicomenzi DOS externe! Comenzile interne, cum ar fi dir, copy etc. nu pot firulate, deoarece acestea presupun încărcarea fişierului DOS command.com.

Pentru execuţia unui fişier shell sub Unix dintr-un program Java esteobligatorie includerea, ca prim comentariu în fişier, a specificării interpretoruluide comenzi folosit. De exemplu, pentru un fişier executat de către shell-ulBourne (sh) [65, 75, 99], prima linie din fişier va fi:

# !/bin/sh

Folosind facilitatea de apel a unor comenzi externe din programe Java,se pot compila şi executa noi programe Java, din interiorul altor programe.Compilarea şi/sau execuţia acestor noi programe se realizează dinamic, înmomentul apelurilor de metode exec.

Iată, spre exemplu, partea esenţială dintr-un program Java care lanseazădin interiorul lui o compilare Java, după care lansează crearea unei arhive jardin fişierul class obţinut:

String[] javac = {"javac", "FisierProgram.java"};Process p=Runtime.getRuntime().exec(javac);p.waitFor();String[] jar = {"jar", "cf", "FisierProgram.jar", "FisierProgram.class"};p=Runtime.getRuntime().exec(jar);p.waitFor();