Documente Academic
Documente Profesional
Documente Cultură
Curs 1
Introducere
Incepand cu anul 1977 incepe utilizarea pe scara larga a calculatoarelor
personale, pretul acestora facandu-le accesibile tuturor. In 1981 IBM, cel mai mare
producator de computere, lanseaza pe piata modelul sau de calculator personal – IBM
PC (XT). Aproape peste noapte calculatoarele personale patrund in intreprinderi,
organizatii si chiar in casele oamenilor. Initial aceste calculatoare erau folosite ca
unitati independente, transferul informatiilor de la un calculator la altul se facea prin
intermediul dischetelor. Pentru a eficientiza schimbul informatiilor intre calculatoarele
personale acestea au fost interconectate fie prin legaturi telefonice fie in retele locale
in cadrul aceleiasi cladiri (LANs – local area networks). Aceasta a dus la raspandirea
aplicatiilor de calcul distribuit (distributed computing). In loc de a fi adunate si
prelucrate centralizat, intr-un centru de calcul dotat cu calculatoare foarte
performante, datele se procesau distribuit, pe calculatoare personale conectate in
retea, amplasate in locurile in care informatia era obtinuta. Puterea de calcul a unui
PC era suficienta pentru a asigura cerintele unui utilizator individual si comunicatia in
retea. La ora actuala un PC de 1000$ are o putere de calcul impresionanta, fiind la fel
de performant ca un calculator mare din anii 70, avand pretul de un milion de dolari.
Informatia este distribuita prin retea unde unele calculatoare numite file servere au
sarcina principala de a stoca programe si date iar altele numite clienti apeleaza la
serviciile serverelor pentru a accesa si procesa distribuit aceste date. Prelucrarea
distribuita a datelor presupune “cooperarea” intre aplicatiile executate pe
calculatoarele client si cele executate de servere. Aplicatia client solicita un anumit
serviciu aplicatiei server, aceasta din urma efectueaza prelucrarile necesare satisfacerii
cererii clientului si ii transmite rezultatele solicitate. O aplicatie data de prelucrare
distribuita consta nu dintr-un singur program executat pe un singur calculator ci din
mai multe programe executate pe doua sau mai multe calculatoare interconectate in
retea. O astfel de aplicatie se numeste aplicatie client/server. In anii 70 si 80
limbajele de programare uzuale in elaborarea sistemelor de operare si a aplicatiilor
client /server de prelucrare distribuita in retea erau C si C++. Odata cu
interconectarea retelelor locale LAN in retele WAN (wide area network) si
dezvoltarea tehnologiilor Internet, limbajul de programare Java castiga rapid teren
oferind perspective noi pentru dezvoltarea aplicatiilor bazate pe aceste tehnologii.
masina. Fiecare comanda este reprezentata printr-un numar. Operanzii comenzii sunt
si ele numere reprezentand fie valori ale datelor prelucrate de comanda fie registrul
sau adresa locatiei de memorie in care datele prelucrate se afla sau unde va fi depus
rezultatul. O instructiune masina ce poate fi executata de procesor consta deci din
numarul asociat comenzii urmat eventual de numere desemnand operanzii comenzii.
Programul masina consta dintr-o succesiune de instructiuni masina a caror
executie duce la prelucrarea dorita a datelor initiale si obtinerea rezultatelor. Acest
program masina, constand deci dintr-un sir de numere – cod si date, se poate incarca
sub forma binara in memoria calculatorului si executat. Este evident ca un program
masina nu poate fi executat decat pe un calculator dotat cu procesorul al carui set de
comenzi a fost folosit la scrierea programului. Spunem deci ca programul masina nu
este portabil fiind dependent de masina. Odata cu raspandirea calculatoarelor cererea
tot mai mare de aplicatii programarea direct in limbaj masina a devenit ineficienta
fiind prea greoaie. Din acest motiv, pentru a facilita elaborarea programelor, fiecarei
comenzi elementare (numar) a procesorului i s-a asociat o succesiune de litere
reprezentand abrevierea din limba engleza a comenzii respective. De exemplu pentru
comanda de adunare s-a folosit mnemonica ADD (abreviere de la addition). Folosind
mnemonicile programele puteau fi concepute, intelese, corectate si modificate mai
usor. Setul de mnemonici ale comenzilor si regulile de constructie a instructiunilor
constitue asa numitul limbaj de asamblare. Un program scris in limbaj de asamblare
nu poate fi executat direct de calculator. Desi inteligibil pentru om, el nu poate fi
“inteles” de calculator care asa cum am vazut “intelege” numai limbajul masina al
procesorului cu care este echipat. In aceste conditii programul scris in limbaj de
asamblare, pentru a putea fi executat, trebuie in prealabil “tradus” in limbaj masina.
Aceasta “traducere” este o operatie de rutina prin care fiecare mnemonica a unei
comenzi este substituita cu codul masina asociat. Fiind o operatie de rutina, aceasta
“traducere” poate fi realizata automat de un program de calculator numit asamblor. Ca
si programele masina, programele scrise in limbaj de asamblare se adreseaza unui
anumit procesor. Fiecare tip de procesor are propriul sau set de comenzi masina si
deci si propriul sau limbaj de asamblare. Mai mult, daca programul apeleaza functii
ale sistemului de operare executia sa pe un calculator va fi conditionata nu numai de
tipul procesorului ci si de prezenta sistemului de operare ale carui functii le apeleaza
programul. Spunem in acest caz ca programul este dependent de platforma (procesor
+ sistem de operare). Pentru a nu rescrie programele pentru fiecare tip de calculator in
parte si pentru a usura elaborarea, depanarea si modificarea lor s-au conceput asa
numitele limbaje de nivel inalt. Acestea sunt limbaje artificiale, avand un vocabular si
o sintaxa care permit descrierea unor operatii complexe. Primul limbaj de acest tip a
fost FORTRAN (FORmula TRANslator). Acesta permitea in principal formularea
unor instructiuni de calcul a expresiilor algebrice (formule) continand paranteze,
variabile, constante, operatori, functii, intro forma apropiata de cea folosita in algebra.
Datele puteau fi scalari – numere reale sau intregi - sau structuri similare vectorilor si
matricilor. De asemenea limbajul prevedea instructiuni puternice de introducere si
afisare a datelor de la si spre diferite echipamente periferice si pentru lucrul cu fisiere.
Programele scrise intr-un limbaj de nivel inalt nu sunt dependente de platforma
si din acest motiv pot fi usor insusite de nespecialisti in tehnica de calcul (ingineri,
fizicieni, economisti, medici, etc.). Pentru a scrie un program intr-un limbaj de nivel
inalt nu este necesar sa cunosti detalii despre calculatorul sau sistemul de operare sub
care va fi executat acesta. Ca si programele in limbaj de asamblare, programele scrise
in limbaj de nivel inalt nu pot fi “intelese” de calculator trebuind sa fie “traduse” in
cod masina pentru a fi executate. Aceasta “traducere” se face prin substituirea fiecarei
PROGRAMARE IN JAVA - Note de curs 3
Limbajul C si C++
In anul 1970 la Bell Laboratories s-a inceput dezvoltarea unui nou sistem de
operare pentru minicalculatoare, numit UNIX. O prima versiune a acestui sistem a
fost scrisa pentru minicalculatorul DEC – PDP7 in limbajul B (creat de Martin
Richards) orientat pentru programare de sistem. UNIX se dorea sa fie un sistem de
operare portabil, scris in limbaj de nivel inalt, putand astfel sa fie compilat si instalat
pe orice tip de minicalculator, cu conditia sa fie disponibil un compilator pentru
limbajul de nivel inalt folosit. In 1972 pentru elaborarea unei noi versiuni UNIX,
Dennis Ritchie pornind de la limbajul B il dezvolta creand limbajul C si compilatorul
pentru DEC - PDP11. Odata cu raspandirea in mediul universitar a sistemului de
operare UNIX si limbajul C devine din ce in ce mai cunoscut si folosit ca instrument
de dezvoltare a UNIX. In prezent compilatorul limbajului C este disponibil pe toate
tipurile de calculatoare ceea ce permite scrierea de aplicatii portabile mai ales dupa ce
in 1983 limbajul a fost standardizat de American National Standards Committee on
Computers and Information Processing. In 1989 standardul este aprobat de ANSI in
cooperare cu ISO (International Standards Organization)(ANSI C).
In 1980, tot la Bell Laboratories, Bjarne Stroustrup extinde limbajului C
adaugandu-i facilitati necesare programarii orientate pe obiecte. Noua versiune a
limbajului este numita C++.
Conceptul de programare orientata pe obiecte revolutioneaza tehnologia de
elaborare aprogramelor. Obiectele sunt componente software refolosibile, modeland
obiectele din lumea reala. In aceasta abordare programele se construesc cu ajutorul
acestor componente ca intr-un joc cu cuburi. Lucrul in echipa la proiectarea si
dezvoltarea programelor folosind aceste componente modulare este mult mai
productiva decat in cazul folosirii tehnologiilor utilizate anterior (modularitate,
programare structurata). C++ permite scrierea programelor atat in stilul C clasic cat si
in stilul orientat pe obiecte. Ulterior au aparut si alte limbaje de programare orientata
pe obiecte cum ar fi Smaltalk elaborat de Xerox Palo Alto Research Center (PARC) si
limbajul Java elaborat de Sun. Practic toate sistemele de operare aparute dupa 1972 au
fost scrise in C/C++.
4 CURS 1
Limbajul Java
Numarul de calculatoare personale este de sute de milioane in toata lumea si
numarul acesta este in crestere. Totodata interconectarea acestora intr-o retea globala
(Internet) este tot mai accentuata. Descoperirea microprocesorului a revolutionat si
productia de sisteme numerice de conducere cu utilizarea lor in numeroase aplicatii
industriale, comerciale sau casnice. Utilizarea microprocesoarelor face posibila
aparitia unor utilaje si aparate inteligente – casa inteligenta nu mai este o fantezie sci-
fi. Ca si calculatoarele personale si aceste aparate inteligente pentru a functiona
eficient vor trebui sa fie interconectate in retea. De exemplu nu este greu de imaginat
un sistem inteligent care sa – mentina constanta umiditatea pamantului in ghivecele cu
flori si sa schimbe apa si sa mentina temperatura la pestisori in timp ce noi suntem
plecati in vacanta. Mai mult, prin Internet vom putea de la orice distanta sa intram in
legatura cu sistemul si sa aducem corectii la valorile prescrise pentru umiditate si
temperatura, sa vedem imaginea acvariului cu pestisori pe displayul PC-ului si chiar
sa le vorbim.
Intrucat diversele aparate inteligente care vor fi comercializate nu vor fi
echipate cu acelasi tip de procesor apare problema elaborarii unor aplicatii
independente de platforma.
Prevazand aceste evolutii, firma Sun a lansat in anul 1991 un proiect de
cercetare numit Green. Rezultatul a fost un limbaj de programare derivat din C si
C++, botezat de autorul sau James Gosling Oak dupa numele varietatii unui arbore ce
crestea in fata geamului biroului sau. S-a descoperit mai tarziu ca un limbaj de
programare cu acest nume exista deja si atunci, dupa ce au fost la o cafeanea la o
cafea, cercetatorii au ales pentru noul limbaj de programare numele Java.
Deoarece piata aparatelor inteligente nu se dezvolta asa de repede si datorita
pierderii unui contract pentru care Sun concurase, programul Green a inceput sa aibe
dificultati de finantare fiind pe cale sa fie abandonat. Din fericire pentru proiect si
Java, in anul 1993 a aparut si a capatat o evolutie exploziva in Internet serviciul
WorldWideWeb (WWW). Cercetatorii Sun implicati in proiect au sesizat imediat
potentialul oferit de utilizarea limbajului Java in crearea asa numitelor pagini Web cu
continut dinamic. Noile perspective au revitalizat proiectul Green astfel ca in Mai
1995 firma Sun face cunoscute rezultatele cercetarilor la o conferinta SunWorld.
Aparitia java este imediat sesizata si apreciata de cercurile de afaceri interesate
de aspectele utilizarii comerciala a serviciului WorldWideWeb. In aceste conditii Java
nu se prezinta ca un limbaj academic cum a fost Pascal si nici ca un limbaj destinat
uzului individual sau al unui grup de programatori caum este C. Java este un limbaj
promovat de interesul cercurilor de afaceri pentru utilizarea comerciala a Internet in
conexiune cu WWW.
Tehnologii de programare
La inceputurile sale programarea se facea “dupa ureche” in functie de talentul,
experienta si de capacitatea de analiza si sinteza a programatorului. La proiectarea
programului se pornea de la reprezentare grafica (numita schema logica) a
algoritmului de rezolvare a problemei. O astfel de schema logica este de fapt un graf
orientat ale carui noduri sunt operatiile de prelucrare a datelor reprezentate prin
simboluri grafice adecvate. Nodurile sunt conectate prin arce orientate care stabilesc
succesiunea de efectuare a operatiilor. O astfel de abordare ofera prea multa libertate
in proiectarea algoritmului. Nu intamplator, in acea vreme cea mai importanta
instructiune discutata in manualele de programare era instructiunea de salt GOTO.
PROGRAMARE IN JAVA - Note de curs 5
class HalloWorld {
//Aici se vor adauga membrii clasei
}
class HalloWorld {
public static void main(String[] arguments){
//Aici se va adauga codul functiei main
}
}
class HalloWorld {
public static void main(String[] arguments){
System.out.println(“Hallo world!”);
}
}
Sumar
Pe parcursul acestui curs am discutat aspectele introductive legate de limbajul
Java si POO. Am elaborat de asemenea un prim program java si am parcurs etapele
necesare pentru executia acestuia:
1. Se editeaza programul cu un editor de text;
2. Se compileaza programul;
3. Se apeleaza interpretorul pentru executarea byte-codului generat de
compilator.
Parcurgand aceste etape am vazut cum se defineste o clasa si cum se declara in
cadrul acesteia o metoda. De asemenea am vazut cum se apeleaza la serviciile unui alt
obiect existent – in speta pentru afisarea unui mesaj am apelat metoda
System.out.println(<mesaj>). Unde <mesaj> este un sir de caractere incadrat intre
ghilimele. Aceasta metoda afiseaza sirul de caractere la consola sistemului (in
fereastra DOS in care am rulat aplicatia) si trece cursorul pe randul urmator.
Trebuiesc de asemenea remarcate urmatoarele detalii de sintaxa:
• clasa se declara cu instructiunea class;
• Programul Java este instantierea unei clase care contine metoda main;
• Metoda main este de tip public static void si are ca parametru String[];
• Numele clasei trebuie sa fie acelasi cu numele fisierului in care se salveaza codul
acesteia;
• In textul programului pot fi introduse comentarii precedate de perechea de
caractere “//” (la fel ca in C), compilatorul ignorand textul ce urmeaza acestor
caractere pana la sfarsitul randului.
• Instructiunile programului se termina cu caracterul “;”.
Mai multe detalii privind cele de mai sus vor prezentate in cursurile urmatoare.
12 CURS 1
Exercitii
1. Cand compilati un program, ce faceti de fapt?
a. Il salvati pe disc
b. Il convertiti intr-o forma pe care calculatorul poate sa o inteleaga
c. Il adaugati la colectia voastra de programe
2. Ce este o variabila?
a. O valoare necunoscuta ce este precizata in timpul executiei
programului
b. Un text intr-un program ignorat de compilator
c. Un loc unde vor fi pastrate de catre program informatii
Curs 2
Algoritmi si structuri de control
Inainte de a incepe scrierea unui program care sa rezolve o anumita problema,
programatorul trebuie sa analizeze si sa inteleaga pe deplin in ce consta problema care
sunt datele initiale, care sunt rezultatele ce trebuiesc obtinute si ce prelucrari trebuie
sa sufere datele initiale pentru a se obtine rezultatele cerute. La proiectarea
programului este esential atat sa determinam ce blocuri componente vom folosi pentru
a construi programul cat si ce metodologie de elaborare vom utiliza pentru aceste
blocuri. Metodologia prezentata in continuare de proiectare a structurii programelor
este aplicabila nu numai limbajului java ci si in cazul majoritatii limbajelor de
programare de nivel inalt. orice problema de calcul poate fi rezolvata prin efectuarea
unor anumite operatii asupra datelor intr-o succesiune data. O astfel de succesiune de
efectuare a operatiilor asupra datelor initiale care duce la solutionarea unei probleme
date ( prin obtinerea rezultatelor dorite) se numeste algoritm. Sa analizam pe un
exemplu importanta succesiunii corecte a efectuarii a operatiilor in rezolvarea corecta
a problemei. Fie algoritmul de desteptare si plecare la cursuri a unui student:
1. Trezirea si jos din pat
2. Dezbraca-ti pijamaua
3. Fa un dus
4. Imbraca-te
5. Ia micul dejun
6. Pleaca la cursuri
Executarea in aceasta ordine a operatiilor descrise mai sus face ca studentul
nostru sa ajunga la cursuri intr-o forma corespunzatoare. Acum sa presupunem ca
studentul ar executa aceleasi operatii intr-o ordine putin modificata:
1. Trezirea si jos din pat
2. Dezbraca-ti pijamaua
3. Imbraca-te
4. Ia micul dejun
5. Fa un dus
6. Pleaca la cursuri
Aplicand acest algoritm, studentul nostru va ajunge la cursuri ud fleasca.
Specificarea ordinii in care se executa diferitele instructiuni ale programului se face
prin instructiuni de control.
Inainte de atrece la scrierea programului este necesar deci sa determinam
operatiile ce trebuiesc efectuate de acesta si succesiunea acestora adica sa determinam
algoritmul de rezolvare a problemei care urmeaza sa fie implementat in final in
limbajul de programare ales. Inainte de a fi transpus in program, algoritmul trebuie
reprezentat intr-o forma sau alta pe hartie. Folosirea limbajului natural nu este cea mai
buna solutie pentru descrierea algoritmului. O solutie o constitue utilizarea
pseudocodului – un limbaj artificial neformal apropiat de un limbaj de programare dar
14 CURS 2
Secventa
Structura secventa este implicita in Java. Daca nu se specifica altfel,
instructiunile sunt executate secvential, una dupa alta in ordinea in care apar in
program. Fragmentul de schema logica din figura 2.1 exemplifica o astfel de structura
de control de tip secventa tipica, in care doua operatii de calcul sunt executate
succesiv.
O schema logica este o reprezentare grafica a unui algoritm folosind diferite
simboluri grafice pentru reprezentarea operatiilor (dreptunghiuri, romburi, cercuri,
elipse). In schema logica din figura 2.1 dreptunghiurile reprezinta blocuri de calcul (
specificand o actiune/operatie executata de program) iar cercurile noduri de conectare
la restul schemei logice. Sagetile indica ordinea in care operatiile sunt efectuate.
PROGRAMARE IN JAVA - Note de curs 15
Intai valoarea variabilei unghi este adaugata la variabila total iar apoi variabila
contor este incrementata cu 1. Java ne permite sa prevedem oricate instructiuni
succesive intr-o secventa.
Selectia
Selectia este o instructiune care introduce o ramificatie in fluxul de executie al
instructiunilor programului. Executia instructiunilor se va desfasura pe o ramura sau
alta in functie de indeplinirea unei conditii specificate.
Java prevede trei tipuri de instructiuni de tip selectie: structura de selectie if ,
selectia dubla if/else si selectia multipla switch. Selectia simpla if determina executia
unei operatii daca este este adevarata o conditie data. In cazul in care conditia nu este
indeplinita (este falsa) se sare peste operatia conditionata trecandu-se la executia
operatiei imediat urmatoare. O astfel de structura se numeste selectie simpla si poate
fi reprezentata grafic prin schema logica din figura 2.2.
Aici simbolul romb este un bloc de decizie. Daca valoarea de adevar a expresiei
relationala inscrisa in acest bloc este adevarat ( variabila unghi are valoarea mai mare
sau egala cu 60), se executa actiunea din blocul urmator (afisarea mesajului “Mai
mare”). Daca valoarea de adevar este fals, atunci se sare peste aces bloc. de remarcat
ca si aceasta structura de control if ca si secventa are un singur punct de intrare si un
singur punct de iesire. Astfel de structuri cu un singur punct de intrare si un singur
punct de iesire prezinta avantajul ca programatorul le poate conecta unul la altul
folosindu-le ca elemente de constructie a programului la fel ca intr-un joc de cuburi.
Aceasta confera algoritmului o structura clara, usor de inteles si modificat.
16 CURS 2
Selectia dubla
Structura de selectie dubla if/else permite programatorului sa specifice o actiune
alternativa care se executa in cazul conditia nu este indeplinita. Aceasta structura se
scrie in pseudocod astfel
...
if(unghi >= 60)
print “Mai mare”
else
print “Mai mic”
...
Schema logica corespunzatoare acestei structuri este prezentata in figura 2.3.
PROGRAMARE IN JAVA - Note de curs 17
...
if(x > 10)
if(y > 10)
System.out.println(“x si y sunt mai mari ca 10”);
else
System.out.println(“x este mai mic sau egal cu 10”);
...
va functiona incorect afisand mesajul x este mai mic sau egal cu 10 chiar daca x are
valoarea 12 dar y este 5.
Aceasta se datoreste faptului ca else este asociat cu if( y > 10) si nu cu if(x > 10),
secventa corespunzand schemei logice din figura 2.5.
Versiunea corecta se obtine folosind acoladele:
PROGRAMARE IN JAVA - Note de curs 19
...
if(x > 10){
if(y > 10)
System.out.println(“x si y sunt mai mari ca 10”);
}else
System.out.println(“x este mai mic sau egal cu 10”);
...
Acoladele {} indica compilatorului ca cel de al doilea if este incuibat in corpul
primului si deci componenta else apartine primului if.
Instructiuni compuse
In mod normal instructiunea if accepta in corpul sau o singura instructiune.
Daca prelucrarea ce trebuie executata este mai complexa si necesita mai multe
instructiuni, acestea pentru a fi incluse in corpul lui if trebuiesc grupate intr-un bloc
de instructiuni prin incadrarea intre acolade. Un astfel de bloc de instructiuni se
numeste instructiune compusa. Exemplul urmator prezinta utilizarea instruciunilor
compuse in corpul unei selectii duble:
...
if(unghi >= 60)
System.out.println(“Mai mare”);
else{
System.out.println(“Mai mic”);
System.out.println(“Mai mariti unghiul!”);
}
...
In acest exemplu daca variabila unghi are valoarea mai mica decat 60 este executat
blocul de doua instructiuni incadrate de acolade, afisandu-se pe doua randuri mesajele
Mai mic si Mai mariti unghiul! .
Daca nu s-ar fi folosit acoladele mesajul Mai mariti unghiul! ar fi fost afisat in
oricare din situatii.
Selectia multipla
Fie urmatoarea instructiune formata din mai multe instructiuni de selectie
incuibate:
...
if(unghi == 30)
System.out.println(“Unghi=30”);
else if (unghi == 45)
System.out.println(“Unghi=45”);
else if (unghi == 60)
System.out.println(“Unghi=60”);
else if (unghi == 90)
System.out.println(“Unghi=90”);
else
System.out.println(“Alte valori”);
...
Pentru astfel de cazuri in care, in functie de valoarea intreaga pe care o ia o
variabila, trebuie selectata si executata o anumita actiune din mai multe posibile
(selectie multipla) limbajul java prevede instructiunea switch avand sintaxa:
20 CURS 2
Aici daca valoarea intreaga obtinuta prin evaluarea <expresie intreaga> este
<val 1> se va efectua <actiune 1>, daca este <val 2> se va efectua <actiune 2>,
etc.Daca valoarea obtinuta nu este egala cu nici una din valorile <val1>,
<val2>,...,<val n> se va efectua <actiune implicita> specificata cu eticheta default.
Folosind aceasta instructiune, instructiunea compusa din exemplul precedent
poate fi inlocuita cu:
...
switch(unghi){
case 30: System.out.println(“Unghi=30”);
break;
case 45: System.out.println(“Unghi=45”);
break;
case 60: System.out.println(“Unghi=60”);
break;
case 90: System.out.println(“Unghi=90”);
break;
default: System.out.println(“Alte valori”);
break;
}
...
valoarea rezultata din evaluarea conditiei este invariant adevarat). Ciclul se numeste
ciclu cu testul la inceput deoarece evaluarea conditiei se face inainte de a se executa
actiunea din corpul ciclului. Astfel este posibil ca aceasta actiune sa nu se execute nici
o data (daca conditia are din start valoarea fals). Sintaxa instructiunii while este:
while (<expresie booleana>)
<instructiune>
Instructiunea <instructiune> se repeta cat timp valoarea booleana obtinuta din
evaluarea expresiei <expresie booleana> este adevarat. Instructiunea <instructiune>
poate fi simpla sau compusa (bloc de instructiuni).
Exemplul urmator prezinta utilizarea instructiunii while:
...
while(unghi < 360)
unghi = unghi + 10;
...
In acest exemplu variabila unghi este marita ciclic cu 10 pana cand valoarea acesteia
depaseste 360. Daca valoarea initiala a acestei variabile a fost 5, valoarea la iesirea
din ciclu va fi 365. Daca valoarea initiala este un multiplu de zece dar mai mica de
360, valoarea finala obtinuta este 360. Daca insa valoarea initiala este egala sau mai
mare ca 360, actiunea de incrementare a sa cu 10 nu are loc, ciclul terminandu-se fara
a se mai executa corpul sau deoarece conditia unghi < 360 este evaluata la valoarea
fals de la inceput.
Schema logica din figura 2.5 reprezinta structura repetitiva while din exemplul
de mai sus.
Sumar
Exercitii
PROGRAMARE IN JAVA - Note de curs 25
Curs 3
Date, variabile, expresii
Asa cum am aratat, programele sunt sisteme care prelucreaza date. Desi in
esenta aceste date sunt valori numere stocate in memoria calculatorului sub forma de
numere intregi codificate in binar, la nivelul programului scris in limbaj de nivel inalt
ele modeleaza obiecte ale lumii reale. Fiecare limbaj de nivel inalt pune la dispozitia
programatorului o colectie mai mult sau mai putin bogata de tipuri de date uzuale prin
care acesta sa poata reprezenta in program informatia supusa procesarii. In mod
traditional calculatoarele sunt destinate sa prelucreze informatie numerica efectuand
calcule cu numere intregi si reale. Din acest motiv din nici un limbaj de programare
nu lipsesc aceste tipuri de date. De asemenea calculatoarele sunt frecvent folosite
pentru prelucrarea informatiei de tip text numita informatie alfanumerica – caractere.
Caracterele, sunt simboluri grafice cum ar fi litere, cifre, semne speciale, sunt
codificate numeric prin asocierea fiecarui astfel de simbol a unui cod numeric
standardizat (de exemplu codul ASCII – American Standard Code for Information
Interchange). Si pentru acest gen de informatie limbajele de programare de nivel inalt
prevad tipuri de date corespunzatoare. De asemenea, asa cum am constatat in cursul
trecut, programele trebuiesc adesea sa opereze cu valori logice de tip adevarat si fals,
motiv pentru care si pentru astfel de valori, numite booleene dupa matematicianul
englez Bool, unele limbaje de programare (Pascal, Java) prevad un tip de date
specific. Tipurile de date discutate sunt tipuri elementare, numite tipuri de date
scalare. Pe langa tipurile de date scalare toate limbajele de nivel inalt permit definirea
unor structuri omogene de date cum ar fi vectorii si matricile, formate din mai multe
elemente de acelsi tip. In aceasta categorie ar putea intra si sirurile de caractere –
privite ca un vector ale carui elemente sunt date de tip caracter. Totusi, unele limbaje
(Pascal, Java) trateaza sirurile de caractere ca un tip distinct de date. Limbajele de
nivel inalt moderne permit programatorului sa defineasca propriile tipuri de date sub
forma de structuri eterogene (compuse din elemente de tipuri diferite) prin care acesta
sa poata reprezenta si alte informatii decat cele uzuale.
Asa cum in algebra pe fiecare multime de numere exista definit un set de
operatii si limbajele de nivel inalt prevad pentru fiecare tip de date un set de operatori.
Astfel avem operatori ce implementeaza operatiile cu numere intregi, alti
operatori pentru operatii cu date de tip real, operatori ce opereaza cu date de tip
boolean, cu caractere sau cu siruri de caractere. Exista de asemenea operatori
relationali care permit compararea a doua date, evident ca de acelasi tip. Similar
operatiilor din algebra, operatorii pot fi unari actionand asupra unui singur operand
sau binari avand doi operanzi.
Limbajele de nivel inalt permit construirea cu ajutorul operatorilor a unor
expresii asemanatoare expresiilor algebrice. Termenii expresiei sunt date de acelasi tip
sau de tipuri compatibile. La executia programului expresia este evaluata prin
aplicarea operatorilor asupra termenilor dupa niste reguli precise. De exemplu
26 CURS 3
int scorMaxim;
Instructiunea:
float punctajMediu;
class Variabile{
public static void main(String[] arguments){
// Declararea si initializarea variabilelor
int scorMaxim=40000;
double punctajMediu = 123.456;
char key = ‘C’;
String mesaj = “Hallo world!”;
boolean sfirsitJoc = false;
// Afisarea valorii variabilelor
System.out.println(scorMaxim);
System.out.println(punctajMediu);
System.out.println(key);
System.out.println(mesaj);
System.out.println(sfirsitJoc);
}
}
class MotanulPacepa{
public static void main(String[] arguments){
int greutate = 3;
System.out.println(“Motanul Pacepa cintareste “ + greutate);
System.out.println(“Motanul Pacepa viziteaza ulcica cu smintana“);
greutate = greutate + 1;
System.out.println(“Motanul Pacepa cintareste acum “ + greutate);
System.out.println(“Motanul Pacepa descopera gimnastica aerobica“);
30 CURS 3
greutate = greutate - 2;
System.out.println(“Motanul Pacepa cintareste acum “ + greutate);
System.out.println(“Motanul Pacepa cade in masina automata de ” +
“spalat rufe“);
greutate = greutate/2;
System.out.println(“Motanul Pacepa cintareste acum “ + greutate);
System.out.println(“Motanul Pacepa este clonat de 12 ori“);
greutate = greutate + (greutate*12);
System.out.println(“Cei 13 motani Pacepa cintaresc acum “ +
greutate);
}
}
potrivit, termenul de tip int este convertit la tipul String dupa care se aplica operatorul
de concatenare. Astfel in secventa:
...
int greutate = 3;
System.out.println(“Motanul Pacepa cintareste “ + greutate);
...
expresia
“Motanul Pacepa cintareste “ + greutate
Precedenta Operatorilor
Asa cum am mai spus expresiile numerice se evalueaza de la stanga spre
dreapta, intai efectuandu-se operatiile multiplicative (*, /,%) iar apoi cele aditive (+,-).
Astfel de exemplu ordinea efectuarii operatiilor la evaluarea expresiei
6*2/4+5*3
este prezentata in figura 3.3.
Trebuie avute in vedere urmatoarele aspecte:
1. Daca se codifica o expresie algebrica in program trebuie avuta in atentie ordinea
de efectuarea operatiilor. Astfel de exemplu pentru a codifica expresia:
x
y ⋅ z
x/y*z
x
⋅ z
y
x/y/z
x/(y*z)
2
⋅ 12
3
Daca aceasta expresie o codificam in program:
2 / 3 * 12
2 * 12 / 3
class CicluCuContor{
public static void main(String[] arguments){
int contor = 1;
while(contor <= 10){
System.out.println(contor);
contor++;
}
}
}
Asa cum am stabilit, variabila contor folosita in programul nostru pentru a tine
evidenta numarului de procesari a fost initializata cu valoarea 1.
La fiecare procesare valoarea acestei variabile este afisata dupa care aceasta este
incrementata cu 1 (pasul de incrementare este 1). Procesarea se repeta pana cand
variabila contor atinge valoarea 11, momentul in care conditia de ciclare contor<= 10
nu mai este satisfacuta.
Tinand cont de proprietatile operatorului de postincrementare, programul poate
fi simplificat comasand instructiunile:
...
System.out.println(contor);
contor++;
...
intr-o singura instructiune:
...
System.out.println(contor++);
...
PROGRAMARE IN JAVA - Note de curs 35
Sumar
¾ Pe parcursul acestui curs am tratat problemele fundamentale privind tipurile
elementare de date ale limbajului Java, declararea variabilelor, operatori si
utilizarea lor in expresii.
¾ Tipurile de date discutate au fost int, float, char, String si boolean.
¾ Au fost prezentati operatorii aritmetici +, -, *, /, %, ++, --, +=, -=, *=, /= si
%=, operatorul de concatenare al sirurilor +, utilizarea parantezelor ( ) si
operatorul de atribuire =.
¾ S-a discutat de asemenea ordinea de efectuare a operatiilor si precedenta
operatorilor in evaluarea expresiilor.
¾ S-a prezentat structura si implementarea unui ciclu cu contor folosind
structura repetitiva while.
Exercitii
1. Determinati valorile fiecarei variabile dupa efectuarea calculelor. Se
presupune ca initial fiecare variabila contine valoarea 10:
a) produs *= x++;
b) fractie /= ++x;
2. Gasiti si corectati erorile din instructiunile de mai jos:
a) while(c <= 10){
produs *= c;
++c;
36 CURS 3
Curs 6
Generarea numerelor aleatoare
Vom studia in continuare metoda random a clasei Math. Aceasta metoda genereaza
o valoare aleatoare de tip double, n ∈[0,1). Programul urmator genereaza 10 astfel de
valori aleatoare:
class RandomTest{
public static void main(String[] arguments){
for(int i=1;i<=10;i++)
System.out.println(Math.random());
}
}
Programul va fi editat in fisierul RandomTest.java. Comenzile de compilare si
executie a programului continut de acest fisier precum si rezultatele afisate sunt redate
in figura 6.1.
class Zar{
public static void main(String[] arguments){
int val;
for(int i=1;i<=10;i++){
val = 1+(int) (Math.random()*6);
System.out.println(val);
}
}
}
Variabile automate
O variabila este creata in momentul declararii acesteia. La creerea variabilei ei i
se aloca un bloc de memorie dimensionat sa pastreze date de tipul specificat la
declararea variabilei. Acest bloc de memorie este asociat de catre compilator cu
identificatorul (numele) variabilei si este accesat (inscris/citit) prin intermediul
acestuia.
Variabilele locale ale unei metode sunt create in momentul in care aceasta este
apelata si incepe sa fie executata. Ele insa sunt distruse prin eliberarea blocurilor de
memorie alocata, incetand sa mai existe in momentul in care executia metodei
inceteaza, la revenirea in metoda apelanta. Astfel de variabile care au o durata de viata
limitata se numesc automate. Ele sunt create automat la executia blocului de
instructiuni care constitue corpul metodei in care au fost declarate si sunt distruse
automat la iesirea din blocul respectiv de instructiuni. Acest mecanism este valabil nu
numai pentru variabilele declarate in corpul unei functii ci si pentru orice variabila
declarata in cadrul unui bloc de instructiuni ( incadrat de acolade ). Denumim
domeniu de existenta al variabilei portiunea de program in care aceasta variabila
exista si poate fi accesata. Ca o consecinta a acestui mecanism valorile continute de
variabilele locale nu se pastreaza de la un apel la altul al metodei nefiind garantat ca la
al doilea apel i se va aloca acelasi bloc de memorie ca la primul apel sau ca acel bloc
nu a fost intre timp folosit de o alta variabila la executia unei alte metode. Din acest
motiv valoarea continuta de o variabila automata la creere este arbitrara si trebuie sa
fie explicit initializata inainte de a fi folosita.
Portiunea de program in care o variabila poate fi accesata se numeste domeniu
de vizibilitate a variabilei. Acest domeniu este stabilit de cateva reguli stricte. Astfel,
o variabila declarata in cadrul unui bloc de instructiuni nu este vizibila din afara
blocului fiind insa vizibila din interiorul blocurilor de instructiuni incuibate in blocul
respectiv.
Recursivitate
Existenta variabilelor automate permite implementarea in limbajul java
recursivitatii. Acest mecanism consta in apelarea succesiva a unei metode de catre ea
insasi. Asa cum o metoda poate apela o alta metoda, o metoda se poate apela pe ea
insasi. Acest lucru este posibil numai daca in urma acestui apel variabilele sale nu
sunt suprascrise prin executarea din nou a codului metodei astfel ca la revenirea din
apel, variabilele locale ale metodei apelante nu mai contin valorile pe care le
contineau inainte de apel. Deoarece, asa cum am vazut variabilele automate sunt
create la apelul metodei si distruse la terminarea executiei, fiecare apel recursiv va
crea un nou set de variabile locale propriu. Astfel codul metodei apelate va prelucra
propriul set de variabile fara a altera valorile setului de variabile proprietate a
metodei apelante. Astfel, desi codul executat la fiecare apel recursiv este acelasi,
memoria de date prelucrata de fiecare apel este diferita.
Sa exemplificam cele prezentate mai sus prin rezolvarea urmatoarei probleme:
Sa se elaboreze un program care sa calculeze si sa afiseze valoarea
factorialului numarului 10.
Factorialul este definit prin produsul:
n!=1 ⋅ 2 ⋅... (n-1) ⋅ n
66 CURS 6
n! = (n-1)! ⋅ n
intoarca valorile calculate care au fost afisate incepand cu primul factorial calculat
complet (1!) si terminand cu ultimul (10!).
Figura 6.8- Compilarea si executia aplicatiei FactorialRecursiv.java
Pe baza celor doua exemple prezentate putem face o comparatie intre algoritmii
recursivi si cei iterativi.
Structuri de date
Programele elaborate de noi pana acum rezolvau probleme de natura
computationala, prelucrand date ce modelau obiecte specifice acestui domeniu.
Calculatoarele pot insa prelucra informatii apartinand nu numai domeniului
matematici. Exista astfel programe de gestiune a bazelor de date care prelucreaza
informatii privind inventarul unor obiecte sau gestioneaza personalul unei
intreprinderi clientii unei firme sau pacientii unui spital. Datele prelucrate de astfel de
70 CURS 6
Declaratia clasei ca fiind publica permite accesul unei metode din exteriorul
modulului la datele “incapsulate” in ea. Aceasta declaratie de clasa nu se refera de
fapt la un automobil anume, continand doar caracteristicile specifice obiectelor din
categoria automobilelor. Ea descrie de fapt atributele unei clase de obiecte, ceea ce
justifica utilizarea cuvantului rezervat class si constitue de fapt definirea unui tip nou
de data care modeleaza un obiect din lumea reala. Aceasta descriere este folosita la
alocarea de memorie cand se creeaza date de acest tip. Evident ca pentru a memora
atributele unui obiect real intr-o data de acest tip este nevoie de un bloc de memorie
suficient de mare pentru a putea stoca toata informatia privind obiectul.
Dimensionarea acestui bloc se face pe baza descrierii facute in declaratia clasei (prin
sumarea dimensiunii locatiilor de memorie necesare memorarii tuturor componentelor
structurii).
In cazul claselor, declararea unei variabile de tipul respectiv nu duce si la
alocarea memoriei necesare stocarii informatiilor privind atributele obiectului. Astfel
declaratia:
Automobil masinaMea;
se face cu operatorul new. Acesta are ca argument tipul de data care urmeaza sa fie
continut de blocul de memorie alocat. Operatorul va intoarce referinta la blocul de
memorie dimensionat pe baza tipului specificat ca argument. Un astfel de mod de
alocare a memoriei se numeste alocare dinamica. Deci pentru a aloca memorie pentru
pastrarea atributelor autoturismului meu, trebuie folosita secventa :
...
masinaMea.tip = “Dacia”;
masinaMea.culoare=”Alb13”;
masinaMea.capacitate = 1300;
...
Al doilea modul este constituit din clasa Structura care contine metoda main(). Codul
acestei clase este continut de fisierul Structura.java:
class Structura{
public static void main(String[] arguments){
Automobil masinaMea = new Automobil();
masinaMea.marca="Dacia";
masinaMea.capacitate = 1300;
masinaMea.culoare="Alb 13";
System.out.println("Masina mea");
System.out.println("marca:"+masinaMea.marca);
System.out.println("capacitate:"+masinaMea.capacitate);
System.out.println("culoare:"+masinaMea.culoare);
}
}
Sumar
In cadrul acestui curs am abordat urmatoarele probleme
¾ utilizarea metodei random a clasei Math pentru generarea numerelor aleatoare. S-a
prezentat modul de scalare a acestor valori pentru a se incadra intr-un anumit
domeniu. Am elaborat un program prin care am testat repartizarea uniforma a
probabilitatii de aparitie a valorilor generate de functia random.
¾ am abordat aspecte legate de domeiul de definitie si de vizibilitate a variabilelor.
¾ s-au discutat tipurile de variabile automate, globale si constante.
¾ s-a definit si exemplificat modul proiectarea algoritmilor recursivi si s-au
comparat performantele acestora in comparatie cu algoritmii iterativi
¾ s-a prezentat mecanismul de supradefinire a metodelor in limbajul java si s-a
exemplificat utilitatea acestui mecanism.
¾ in finalul cursului s-a definit conceptul de structura de date si s-a exemplificat
modul de implementare si utilizare a structurilor de date in limbajul Java. S-a
prezentat si operatorul new si mecanismul alocarii dinamica a memoriei.
Exercitii
1. Elaborati un program Java care calculeaza si afiseaza volumul a 10 sfere folosind
instructiunea:
...
volum = (4/3)*Math.PI*Math.pow(raza,3);
...
Valorile razei vor fi aleatoare, situandu-se in domeniul [5,25].
2. Pentru fiecare set de numere de mai jos scrieti cate o singura instructiune care
genereaza la intamplare si afiseaza unul din numerele setului.
a) 2, 4, 6, 8, 10;
b) 3, 5, 7,9, 11;
c) 6, 10, 14, 18, 22;
Curs 8
Tablourile
Java prevede o clasa “prefabricata” Array (tablou) de obiecte care
implementeaza o structura de date compusa din mai multe elemente de acelasi tip.
Clasa Array defineste de fapt un grup de locatii succesive de memorie in care vor fi
stocate date. Accesul la o anumita locatie se face prin intermediul numelui tabloului si
a unui index intreg cuprins intre paranteze drepte, care specifica numarul locatiei la
care se face accesul:
<nume tablou>[<index>]
In figura 8.1 este prezentat un exemplu al unui tablou cu 10 elemente.
Aceasta instructiune contine de fapt in prima parte declararea unei variabile de tip
referinta la un obiect tablou iar in a doua parte alocarea de memorie pentru acest
obiect. Putem deci sa descompunem instructiunea in doua:
...
int x[];// declararea variabilei de tip refrinta la un tablou de int
x = new int[10];// Alocarea de memorie
...
Vom folosi un tablou reg pentru realizarea unei noi versiuni a clasei Stack elaborata
in cursul precedent.
De data aceasta stiva va fi implementata folosind un tablou de 4 elemente de tip
double. Elementele tabloului vor corespunde celor 4 registri astfel:
reg[0] - registrul x
reg[1] – registrul y
reg[2] - registrul z
reg[3] – registrul t
Metodele push, pop si top vor fi adaptate pentru a lucra cu noua structura de date:
class Stack{
private double reg[];
public Stack(){// constructor stiva
reg=new double[4];
for(int i=0;i<reg.length;i++)
reg[i]=0.0;
}
public void push(double val){// ridica stiva
for(int i=reg.length –1;i>0;i--)
reg[i]=reg[i-1];
reg[0]=val;
}
public double pop(){//coboara stiva
double val=reg[0];
for(int i=0;i<reg.length-1;i++)
reg[i]=reg[i+1];
reg[3]=0.0
return val;
}
public double top(){return reg[0];}//intoarce varful stivei
}
Inlocuind fisierele Register.java si Stack.java cu acest modul editat intr-un nou fisier
Stack.java programul CalcTest va functiona fara sa-i aducem modificari si fara a
modifica nici modulul din Calculator.java. Listingul din figura 8.2 demonstraeaza
acest lucru. Vom sterge in prealabil fisierele Stack.class si Register.class de pe disc.
La incercarea de a executa programul CalcTest.class acesta va incarca modulul
Calculator.class care la randul sau va incerca sa incarce modulul Stack.class.
Deoarece nu il gaseste fiind sters, interpretorul va semnala eroarea (clasa Stack nu
este definita) si isi va inceta activitatea. Acum vom edita fisierul Stack.java in noua sa
versiune si il vom compila rezultand modulul Stack.class necesar functionarii
modulului Calculator.class. Executand acum programul CalcTest.class acesta va
functiona normal afisand aceleasi rezultatele ca si in versiunea sa precedenta.
Acest exemplu demonstreaza cat de usor pot fi facute modificari in programele
proiectate orientat pe obiecte. Schimbarea unei componente cu alta mai performanta
dar compatibila la nivel de interfata nu a necesitat modificari in restul programului pe
care nici nu a fost nevoie nici macar sa-l mai recompilam. Operatia a decurs similar
inlocuirii unui procesor pe placa de baza cu altul de acelasi tip (compatibil la nivel de
pini) dar mai performant.
PROGRAMARE IN JAVA - Note de curs 87
}
}
Apelul inlantuit:
...
t.setOra(14).setMin(7).setSec(30)
...
se evalueaza de la stanga spre dreapta (operatorul de acces . se asociaza de la stanga la
dreapta). Astfel intai se executa metoda t.setOra(14) . Aceasta intoarce o referinta
la obiectul t astfel incat in continuare se executa t.setMin(7).setSec(30). Deci se
va executa metoda t.setMin(7). Intrucat si aceasta intoarce o referinta la t, in final
se va executa si t.setSec(30). Cele de mai sus sunt valabile si pentru cel de al
doilea apel inlantuit din program. Listingul compilarii si executiei acestui program
este redat in figura 8.4:
Mostenirea
Vom defini in continuare o clasa Mamifer:
public class Mamifer{
public String nume;
public Mamifer(String _nume){nume = _nume;}//Constructorul
public void dormi()
{System.out.println(nume +“: ZZZZ ZZZZZZZZ ZZZZ”);}
}
Aceasta clasa o vom folosi pentru a defini noi clase derivate care mostenesc
atributele si comportamentul obiectelor descrise de aceasta clasa dar le extind cu noi
atribute si actiuni posibile:
public class Dulau extends Mamifer{
public Dulau(String nume){super(nume);}//Constructorul
public void vorbeste(){System.out.println(nume+“: Ham!”);}
}
public class Motan extends Mamifer{
public Motan(String nume){super(nume);}//Constructorul
public void vorbeste(){System.out.println(nume +“: Miau!”);}
}
public class Postas extends Mamifer{
public Postas(String nume){super(nume);}//Constructorul
public void vorbeste(){System.out.println(nume +“: Posta!”);}
}
Constructorii aceste subclase invoca constructorul superclasei referit de super.
Sa folosim aceste clase in programul urmator:
class Specii{
public static void main(String[] arguments){
Dulau dulau=new Dulau (“Grivei”);
Motan motan = new Motan (“Pacepa”);
Postas postas = new Postas (“Nae”);
System.out.println(“Intai cerem motanului sa vorbeasca:”);
motan.vorbeste();
System.out.println(“Acum este momentul sa vorbeasca postasul:”);
postas.vorbeste();
System.out.println(“Iar acum sa vorbeasca dulaul:”);
dulau.vorbeste();
System.out.println(“Este timpul ca toti sa faca nani.”);
motan.dormi();dulau.dormi();postas.dormi();
}
}
PROGRAMARE IN JAVA - Note de curs 93
Programul care efectueaza calcul acestei expresii folosind o instanta a aceastei clase
derivate este:
class CalcStTest{
public static void main(String[] arguments){
CalculatorSt c = new CalculatorSt();// constructor
c.enter(9); c.cs();c.enter(5);c.sqr(); c.mul();
c.enter(2); c.enter(5);c.mul();c.exp();c.sum();
}
}
Comenzile de compilare si executie a fisierelor programului precum si rezultatele
afisate sunt redate in figura 8.7.
Polimorfismul
In sectiunea anterioara am definit clasa Mamifer al carui scop nu era creerea de
obiecte ci de a servi ca baza pentru definirea unor clase derivate. Stiind ce
comportament general vor avea obiectele din aceasta clasa o putem defini astfel:
abstract public class Mamifer{
public String nume;
public Mamifer(String _nume){nume = _nume;}//Constructorul
public void dormi()
{System.out.println(nume +“: ZZZZ ZZZZZZZZ ZZZZ”);}
abstract public void vorbeste();
}
Stiind ca obiectele din toate subclasele derivate vor “vorbi”, numai ca o vor face
fiecare in felul specific subclasei careia ii apartin, am adaugat clasei de baza metoda
abstracta vorbeste fara insa a concretiza implementa algoritmul concret al acestei
actiuni. Aceasta metoda abstracta va fi definita concret in fiecare subclasa derivata:
public class Dulau extends Mamifer{
public Dulau(String nume){super(nume);}//Constructorul
public void vorbeste(){System.out.println(nume+“: Ham!”);}
}
public class Motan extends Mamifer{
public Motan(String nume){super(nume);}//Constructorul
public void vorbeste(){System.out.println(nume +“: Miau!”);}
}
public class Postas extends Mamifer{
public Postas(String nume){super(nume);}//Constructorul
public void vorbeste(){System.out.println(nume +“: Posta!”);}
}
Obiecte apartinand acestor subclase pot fi grupate toate intr-un tablou cu
elementele de tip Mamifer. La apelul metodei vorbeste a elementelor tabloului va fi
PROGRAMARE IN JAVA - Note de curs 95
Finalizatori
Distrugerea unui obiect al unei clase se poate face prin atribuirea valorii null referintei
catre acesta. Blocul de memorie alocat obiectului va fi astfel eliberat si va fi colectat
de interpretor pentru a fi folosit la alte cereri de alocare de memorie. Inainte de a
“muri” interpretorul va executa automat metoda finalize() care este publica si de tip
void (nu intoarce nimic).
Ca si constructorul clasei aceasta nu trebuie neaparat declarata. Asa cum
constructorul clasei exista implicit si are numele clasei, nu are parametri nu face
nimic si nu intoarce nimic, putand fi supradefinit de programator daca este cazul, si
finalizatorul clasei se va defini numai daca sunt necesare niste procesari inainte de
“decesul” obiectului. Spre deosebire de constructori, metoda public void finalize() a
unei clase nu poate fi supradefinita. O clasa nu poate avea decat un singur finalizator.
Ca exemplu vom modifica clasa Nava care definea comportamentul navelor
spatiale ale flotei Rebelilor. Daca un obiect din aceasta clasa este distrus, numarul de
nave al flotei continut de variabila comuna (statica) nrNave va fi decrementata.
Daca numarul de nave in timpul atacului scade sub 8, flota se va retrage.
Decizia asupra retragerii va fi luata de fiecare nava in parte prin evaluarea la comanda
actioneaza a variabilei nrNave. Vom adauga deci clasei Nava metodele private void
pleaca(), public void finalize() si vom simula distrugerea unor nave prin atribuirea
valorii null referintelor catre obiectele care le modeleaza.
96 CURS 8
class xWing{
private static int nrNave=0;
private boolean inAtac=false;
private String nume;
public xWing(String _nume){ //Constructor
nrNave++;nume=_nume;
System.out.println(nume+" in formatie");
}
public void actiune(){
if (inAtac)
if( nrNave<9){
inAtac=false;
retragere();
}else ataca();
else if(nrNave >= 10){
inAtac=true;
ataca();
}else asteapta();
}
private void ataca(){System.out.println(nume + " ataca baza");}
private void asteapta(){
System.out.println(nume + " gata de lupta");
}
private void retragere(){
System.out.println(nume + " se retrage");
}
public void finalize(){
--nrNave;
System.out.println("Au mai ramas "+ nrNave +" nave");
}
}
class StarWars2{
public static void main(String[] arguments){
xWing flota[]=new xWing[10];
for(int i=1;i<10;i++)
flota[i]=new xWing("Red"+i);
flota[0]= new xWing("Red Leader");
for(int k=0;k<3;k++){
for(int i=0;i<10;i++)
if(flota[i]!=null)flota[i].actiune();
flota[k]=null;
System.gc();
}
}
}
In acest program, oride cate ori o nava este distrusa ( se atribuie referintei catre
obiectul care o modeleaza valoarea null ), este solicitata expres colectarea memoriei
eliberate cu instructiunea de apel a metodei gc() (garbage collection – colectarea
gunoiului in engleza) a clasei System:
...
System.gc();
...
Daca colectarea blocurilor de memorie eliberate nu s-ar forta prin acest apel,
interpretorul ar fi facut aceasta operatie cand ar fi vrut el (dupa terminarea
programului nostru) si nu am fi obtinut efectul scontat. Chiar si asa daca analizam
rezultatele afisate la executia programului (figura 8.9) observam ca aceasta colectare
este efectuata cu o oarecare intarziere – pe la mijlocul pasului urmator al ciclului for
PROGRAMARE IN JAVA - Note de curs 97
Curs 9
Interfete
Un principiu de baza in ingineria software il constitue separarea specificatiilor unui
modul de implementarea interna a acestuia. In cazul claselor, iterfata este constituita
din setul de metode publice ce pot fi apelate de un modul exterior, constituind puntea
dintre lumea exterioara si implementarea interna a clasei.
Fie de exemplu cele doua implementari elaborate de noi ale stivei folosite de clasa
Calculator. Prima dintre ele folosea 4 obiecte din clasa Register:
class Stack{
private Register x,y,z,t;
public Stack(){
x=new Register();// constructor obiect
y=new Register();
z=new Register();
t=new Register();
}
public void push(double val){// ridica stiva
t.sto(z.get());
z.sto(y.get());
y.sto(x.get());
x.sto(val);
}
public double pop(){//coboara stiva
double val=x.get();
x.sto(y.get());
y.sto(z.get());
z.sto(t.get());
t.sto(0.0);
return val;
}
public double top(){return x.get();}//intoarce varful stivei
}
iar cea de a doua folosea un tablou:
class Stack{
private double reg[];
public Stack(){// constructor stiva
reg=new double[4];
for(int i=0;i<reg.length;i++)
reg[i]=0.0;
}
public void push(double val){// ridica stiva
for(int i=reg.length –1;i>0;i--)
reg[i]=reg[i-1];
reg[0]=val;
}
public double pop(){//coboara stiva
double val=reg[0];
PROGRAMARE IN JAVA - Note de curs 99
for(int i=0;i<reg.length-1;i++)
reg[i]=reg[i+1];
reg[3]=0.0
return val;
}
public double top(){return reg[0];}//intoarce varful stivei
}
Amandoua variantele pot fi reprezentate ca in figura 9.1.
}
public double top(){
... implementare interna top ...
}
}
Acum dispunem atat de specificatia metodelor publice ale clasei adica definitia
interfetei acesteia in clasa abstracta StackInterface si separat de implementarea
particulara a clasei definita de clasa Stack. Clasa poate fi folosita si pentru realizarea
unor alte implementari ale interfetei.
class Stack extends StackInterface
{ ... implementare clasa Stack ... }
100 CURS 9
Limbajul Java ofera insa in locul artificiului declararii unei clase abstracte un
mecanism propriu pentru definirea interfetelor. Astfel in locul definitiei clasei
abstracte StackInterface putem defini explicit o interfata echivalenta:
public interface StackInterface{
void push(double val);
double pop();
double top();
}
Acum putem defini o clasa Stack care implementeaza interfata StackInterface si nu
extinde prin supradefinirea metodelor clasa abstracta StackInterface ca in cazul
precedent.
class Stack implements StackInterface
{ ... implementare clasa Stack ... }
Sa exemplificam cele prezentate mai sus printr-un exemplu. Vom defini interfata
Patrulater.
interface Patrulater{
double calculArie();
String toString();
}
Aceasta interfata poate fi folosita la definirea claselor Dreptunghi si Trapez:
class Dreptunghi implements Patrulater{
private double latime, inaltime;
public Dreptunghi(double l,double h){latime=l;inaltime=h;}
public double calculArie(){return latime*inaltime;}
public String toString(){
return “Dreptunghi(“+latime+”,”+inaltime+”)”;
}
}
class Trapez implements Patrulater{
private double bMare,bMica,inaltime ;
public Trapez(double b,double _b,double h ){
bMare=b;bMica=_b;inaltime=h;
}
public double calculArie(){return (bMare+bMica)*inaltime/2;}
public String toString(){
return “Trapez(“+bMare+”,”+bMica+”,”+inaltime+”)”;
}
}
Ambele clase Dreptunghi si Trapez implementeaza (diferit) aceeasi interfata
Patrulater. Programul propus de testare a acestor doua clase este:
class Figuri{
public static void main(String[] arguments){
Dreptunghi d=new Dreptunghi(5,10);
Trapez t=new Trapez(10,5,4);
System.out.println(“Arie “+d.toString()+”=”+d.calculArie());
System.out.println(“Arie “+t.toString()+”=”+t.calculArie());
}
}
Listingul din figura 9.2 ne arata comenzile de compilare si executie a acestui program
precum si rezultatele afisate de acesta.
Interfetele pot contine si variabile statice finale (constante) ca in exemplul urmator:
interface ZileSaptamina{
static final int LUNI = 0;
static final int MARTI = 1;
static final int MIERCURI = 2;
static final int JOI = 3;
static final int VINERI = 4;
static final int SIMBATA = 5;
static final int DUMINICA = 6;
}
class Zi implements ZileSaptamina {
public static void main(String[] arguments){
int zi=4;
switch(zi){
case LUNI: System.out.println(“LUNI”);break;
case MARTI: System.out.println(“MARTI”);break;
case MIERCURI: System.out.println(“MIERCURI”);break;
case JOI: System.out.println(“JOI”);break;
case VINERI: System.out.println(“VINERI”);break;
case SIMBATA: System.out.println(“SIMBATA”);break;
case DUMINICA: System.out.println(“DUMINICA”);break;
}
}
}
Listingul compilarii si executiei acestui program este redat in figura 9.3:
Interfete multiple
O clasa poate implementa in acelasi timp mai multe interfete ca mai jos:
public class ViataMea extends ViataGrea
implements Nastere,Scoala,Armata, Servici,Pensie,Moarte{
... implementare ...
}
Clasa ViataMea trebuie sa prevada definitii pentru toate metodele specificate de
interfetele Nastere,Scoala,Servici,Pensie si Moarte. Unele din aceste metode pot fi
mostenite de la superclasa ViataGrea. In caz contrar, daca vreuna din metodele
specificate de interfata ramane nedefinita, se va genera o eroare la compilare.
O interfata poate mosteni metodele specificate de la alte interfete:
public interface Servici extends Desteptare, Munca, Somn{
...
}
O clasa care implementaeaza interfata Servici va trebui sa defineasca pe langa
metodele specificate de aceasta si toate metodele specificate de interfetele pe care
aceasta le-a mostenit: Desteptare, Munca, Somn. Trebuie facuta o distinctie clara intre
clasele abstracte si interfete. O clasa poate mosteni numai o singura clasa abstracta dar
poate implementa mai multe interfete. In cazul implementarii unei interfete, clasa
trebuie sa prevada obligatoriu definitii pentru toate metodele specificate de aceasta.
102 CURS 9
Input/output
In programele elaborate de noi pana in prezent am folosit pentru afisarea
rezultatelor la consola componenta out iar pentru citirea datelor de la consola
componenta in a clasei System. Componenta out este o instanta a fluxului standard de
iesire date definita in clasa System:
...
static PrintStream out;
...
Similar, componenta in este o instanta a fluxului standard de intrare date
definita in clasa System:
...
static InputStream in;
...
Atat PrintStream cat si InputStream sunt obiecte predefinite in Java API.
Deoarece afisarea prin metodele println si print nu ne-a creat probleme, sa ne
ocupam de componenta in. Aceasta este o instanta a clasei InputStream care defineste
un flux de date de intrare. Clasa InputStream este definita in Java API ca o clasa
abstracta. Fluxul de date este generat de consola sistemului fiind constituit din perechi
de octeti reprezentand codurile caracterelor introduse de la tastatura. (Caracterele in
Java sunt codificate conform codului Unicode – o extensie pe 2 octeti a codului
ASCII). Programul poate extrage cate o pereche de octeti de date (codul unui caracter)
din flux apeland metoda int read() a obiectului out.
int read() throws IOException;
Am folosit acest mecanism in exemplul din sectiunea “Definirea metodelor” a
cursului 5. Am spus atunci ca in cazul in care se detecteaza o eroare la citirea din
fluxul de date este generata o exceptie putand fi executata o procedura de tratare a
exceptiei respective. Pentru a ignora exceptiile generate la citire, se poate specifica
clauza throws. Intrucat exceptiile generate la operatiile de I/O sunt continute in
pachetul java.io, programul trebuie sa contina la inceput instructiunea de a incarca
clasele predefinite din acest pachet:
import java.io.*;
...
Pentru a “sari” peste caracterele de control ‘\n’ -“new line” generate de apasarea
tastei ENTER am apelat metoda skip() care elimina din fluxul de date urmatoarea
pereche de octeti. Clasa InputStream prevede si o metoda mai performanta, prin
supradefinirea metodei read():
int read(byte b[]) throws IOException;
Aceasta citeste octetii in tabloul specificat de referinta b[] si intoarce numarul de
octeti extrasi din flux.
Aceste trei metode prezentate mai sus nu ofera decat un suport foarte primitiv de
introducere a datelor. Ar trebui sa elaboram metode proprii de prelucrare a octetilor
preluati din fluxul de date de intrare care sa transformam aceste date primare in valori
de tip int, double, String, etc. Dar de ce sa inventam roata? Pachetul java.io din Java
API prevede clase mult mai puternice pentru gestiunea fluxurilor de date de intrare
cum ar fi clasa DataInputStream definita ca:
class DataInputStream extends FilterInputStream implements DataInput
Clasa FilterInputStream este o versiune ne-abstarcta a clasei InputStream definita in
java.io din Java API ca:
class FilterInputStream extends InputStream
PROGRAMARE IN JAVA - Note de curs 103
...
creeaza un obiect String al carui continut este preluat din tabloul de caractere referit
de charArray. Referinta la acest obiect este atribuita variabilei s.
4. Instructiunile:
...
char charArray[] = new char[10];
...
String s;
s = new String(charArray,5,4);
...
creeaza un obiect String al carui continut este initializat prin preluaarea a 4 caractere
din tabloul referit de charArray incepand cu deplasamentul 5. Daca deplasamentul sau
numarul de caractere preluate sunt prea mari astfel incat este depasita limita tabloului,
este generata o exceptie StringIndexOutOfBoundsException.Referinta la acest obiect
este atribuita variabilei s.
5. Instructiunile:
...
char charArray[] = new char[10];
...
String s;
s = new String(charArray,0,5,4);
...
creeaza un obiect String al carui continut este initializat prin preluarea a 4 caractere
din tabloul referit de charArray incepand cu deplasamentul 5. Daca deplasamentul sau
numarul de caractere preluate sunt prea mari astfel incat este depasita limita tabloului,
este generata o exceptie StringIndexOutOfBoundsException. Al doilea argument al
constructorului (0), numit hibyte reprezinta un octet de mascare a caracterelor preluate
din tablou. In Java caracterele sunt codate pe 2 octeti (Unicode). Mascand octetul
superior (hibyte=0) codul caracterelor preluate este echivalent codului ASCII.
Referinta la acest obiect este atribuita variabilei s.
6. Instructiunile:
...
char charArray[] = new char[10];
...
String s;
s = new String(charArray,0);
...
creeaza un obiect String al carui continut este initializat prin preluarea caracterelor din
tabloul referit de charArray. Al doilea argument al constructorului (0), este octetul de
mascare hibyte care se aplica tuturor caracterelor preluate. Referinta la acest obiect
este atribuita variabilei s.
7. Instructiunile:
...
StringBuffer buffer;
String s;
buffer = new StringBuffer();
buffer.append(“Sursa”);
s = new String(buffer);
...
creeaza un obiect String al carui continut este copiat din obiectul din clasa
StringBuffer referit de buffer. Un obiect StringBuffer este un String care poate fi
redimensionat dinamic. Astfel la creere, obiectul referit de buffer este un sir de
caractere vid, de lungime 0. Obiectul este redimensionat dinamic la adaugarea cu
metoda append a caracterelor din String-ul anonim “Sursa”. Referinta la acest obiect
este atribuita variabilei s.
106 CURS 9
}
System.out.println("}");
}
public static void main(String[] arguments){
int x[]=new int [10];
for(int i=0;i<10;i++)
x[i]=1+(int)(Math.random()*10);
System.out.println("Secventa initiala:");
print(x);
sort(x);
System.out.println("Secventa sortata:");
print(x);
}
}
}
if(arguments[0].equalsIgnoreCase("hallo"))
System.out.println(arguments[0]+"== hallo");
else
System.out.println(arguments[0]+"!= hallo");
}
}
Rezultatele executiei programului sunt prezentate in figura 9.9.
Curs 14
Exceptii
In timpul executiei unui program pot sa apara situatii exceptionale care sa duca la
oprirea programului sau la obtinerea unor rezultate eronate. Sa analizam urmatorul
exemplu:
import java.awt.*;
class Divizor extends Frame{
private Label l1=new Label("Introduceti deimpartitul"),
l2=new Label("Introduceti impartitorul"+
" si apasati tasta ENTER");
public Divizor(){
super("Test impartire");
out.setEditable(false);
setLayout(new GridLayout(5,1));
add(l1);add(in1);
add(l2);add(in2);
add(out);
resize(300,150);
show();
}
public boolean handleEvent(Event e){
if(e.id==Event.WINDOW_DESTROY){
hide();
dispose();
System.exit(0);
return true;
}
return super.handleEvent(e);
}
public boolean action(Event e,Object o){
String s1,s2;
if(e.target==in2){
p=Integer.parseInt(s1=in1.getText());
in1.setText("");
q=Integer.parseInt(s2=in2.getText());
in2.setText("");
out.setText(s1+" / "+s2+" = "+String.valueOf(p/q));
}
return true;
}
public static void main(String[] arg){
Divizor f=new Divizor();
}
}
programul va crea doua campuri de editare in1 si in2 care vor servi pentru
introducerea a doua stringuri de intrare s1 si s2 ce vor fi convertite in numere intregi
si stocate in variabilele intergi p si q. De asemenea este creat un camp de editare “read
170 CURS 14
}
return true;
}
Metoda div(int x,int y) apelata pentru efectuarea impartirii celor doua argumente face
si validarea datelor afisand mesajul corespunzator:
private String div(int x, int y){
String rezultat=””;
if(y==0)
if(x==0)
rezultat= “Rezultat nedefinit”;
else
rezultat= x<0? ”Eroare: Infinit negativ”:
”Eroare: Infinit pozitiv”;
return rezultat;
}
Mesajele afisate de in fereastra aplicatiei la introducerea unei valori nule pentru
divizor sunt redate in figura 14.3.
permite unui program sa “prinda” (sa detecteze) o exceptie produsa intr-o sectiune a
sa si sa o proceseze prin executia unei proceduri de recuperare numita exception
handler. Acest mecanism permite “prinderea” tuturor exceptiilor posibile, a mai
multor exceptii inrudite sau numai a unei anume exceptii. Aceasta permite realizarea
unor programe robuste, reducand probabilitatea aparitiei unor erori fatale la executia
programului fara ca aceasta sa fie detectata si tratata corespunzator astfel incat sa se
asigure recuperarea erorii, permitand ca executia programului sa poata fi continuata.
In mod normal exceptia produsa intr-o metoda va fi tratata in metoda apelanta. In
momentul in care s-a produs exceptia, metoda este terminata fortat, revenindu-se la
metoda apelanta in procedura de tratare a exceptiei. Daca aceasta metoda nu prevede
o procedura de tratare a exceptiei respective, si aceasta metoda va fi terminata,
revenindu-se fortat la metoda care a apelat metoda apelanta a metodei care a provocat
exceptia, exceptia urmand a fi tratata de handlerul metodei apelante a metodei
apelante, etc. Astfel revenirea in lantul apelurilor are loc pana la metoda care prevede
handler-ul de tratare a exceptiei respective. Un program poate sa ignore aparitia
exceptiilor dar aceasta poate avea un efect devastator in cazul aplicatiilor comerciale
sau critice. in cazul unor aplicatii pentru uz personal este uzual ca multe erori sa fie
ignorate sau sa fie tratate prin terminarea executiei programului pentru a evita
obtinerea unor rezultate eronate. O astfel de abordare nu este insa acceptabila in cazul
aplicatiilor critice care vor necesita o procesare corespunzatoare a exceptiei
asigurandu-se executia in continuare a programului in conditii de siguranta.
exceptia “impartire prin 0”. Pentru inceput vom cauta o clasa Exception care
corespunde tipului nostru de exceptie. In pachetul java.lang avem definite urmatoarele
clase de exceptii :
Exception
ClassNotFoundException
CloneNotSupportedException
IllegalAccessException
InstantiationException
InterruptedException
NoSuchMethodException
RunTimeException
ArithmeticException
ArrayStoreException
ClassCastException
IllegalArgumentException
IllegalThreadStateException
NumberFormatException
IllegalMonitorStateException
IndexOutOfBoundsException
ArrayIndexOutOfBoundsException
StringIndexOutOfBoundsException
NegativeArraySizeException
NullPointerException
SecurityException
Dintre acestea vom alege clasa ArithmeticException care corespunde cel mai bine
tipului de eroare ce dorim sa-l tratam in aplicatia noastra (vezi si mesajul afisat la
consola – fig.14.2). Modificarile aduse programului anterior vor afecta doar metodele
action() si div():
public boolean action(Event e,Object o){
String s1,s2;
if(e.target==in2){
p=Integer.parseInt(s1=in1.getText());
in1.setText("");
q=Integer.parseInt(s2=in2.getText());
in2.setText("");
try{// Detectia exceptiei
out.setText(s1+" / "+s2+" = "+ div(p,q));
}
catch(ArithmeticException exception) {//Prinderea exceptiei
out.setText(exception.toString());
}
}
return true;
}
private String div(int x, int y)
throws ArithmeticException
{ if(y==0) throw new ArithmeticException();//Aruncarea exceptiei
return String.valueOf(x/y);
}
174 CURS 14
Mesajele afisate de program in acest caz pentru cele trei situatii pentru care sunt
“aruncate” de catre metoda div() exceptii din clasa ArithmeticException sunt cele din
figura 14.6.
public Divizor(){
super("Test impartire");
out.setEditable(false);
setLayout(new GridLayout(5,1));
add(l1);add(in1);
add(l2);add(in2);
add(out);
resize(300,150);
show();
}
public boolean handleEvent(Event e){
if(e.id==Event.WINDOW_DESTROY){
hide();
dispose();
System.exit(0);
return true;
}
return super.handleEvent(e);
}
public boolean action(Event e,Object o){
String s1,s2;
if(e.target==in2){
p=Integer.parseInt(s1=in1.getText());
in1.setText("");
q=Integer.parseInt(s2=in2.getText());
in2.setText("");
PROGRAMARE IN JAVA - Note de curs 177
// Detectia exceptiei
try{
out.setText(s1+" / "+s2+" = "+String.valueOf(p/q));
}
catch(ArithmeticException exception) {//Prinderea exceptiei
out.setText(exception.toString());
}
}
return true;
}
public static void main(String[] arg){
Divizor f=new Divizor();
}
}
Lantul de apeluri din care se face “aruncarea” fiind similar cu cel din figura 14.2.
Striva de apeluri poate fi afisata prin apelul printStackTrace() definita in clasa
Thowable si mostenita de toate clasele derivate. De asemenea poate fi utila si metoda
getMessage() care intoarce un String continand mesajul setat la instantierea
obiectului. Utilizarea acestor metode poate fi exemplificata modificand ca mai jos
blocul catch din exemplul anterior:
...
catch(ArithmeticException exception) {//Prinderea exceptiei
out.setText(exception.getMessage ());
exception.printStackTrace();
}
...
Vom obtine la generarea unei exceptii aritmetice mesajele de la consola si din
fereastra aplicatiei prezentate in figura 14.8
Multithreading
Daca incercam sa privim procesarea datelor de catre o aplicatie ca pe o activitate
similara unei activitati productive, atunci programele elaborate de noi pina in prezent
corespund activitatii unei intreprinderi cu un singur lucrator. Acest lucrator este si
patron si executant. Sa consideram urmatorul exemplu. Aplicatia noastra trebuie sa
“planteze” 80 de puncte pe ecran. Programul va fi urmatorul:
class Plantator{
private int nrP;
public Plantator(int n){nrP=n;}
private void planteaza(){
for(int i=0;i<100000;i++); // temporizare pe durata
// plantarii unui punct
System.out.print(".");
System.out.flush();
}
private void lucreaza(){
for(int i=0;i<nrP;i++)
planteaza();
}
public static void main(String[] argv){
Plantator p=new Plantator(80);
p.lucreaza();
}
}
class Lucrator{
private int nrP;
public Lucrator(int n){nrP=n;}
private void planteaza(){
for(int i=0;i<100000;i++); // temporizare pe durata
// plantarii unui punct
System.out.print(".");
System.out.flush();
}
public void lucreaza(){
for(int i=0;i<nrP;i++)
planteaza();
}
}
Daca insa plantatorul nostru ar dori sa puna la munca doi lucratori impartind munca
intre ei:
class Plantator{
public static void main(String[] argv){
Lucrator l1=new Lucrator(40),
l2=new Lucrator(40);
l1.lucreaza();
l2.lucreaza();
PROGRAMARE IN JAVA - Note de curs 179
}
}
O stfel de diviziunea muncii nu ar avea sens deoarece cele doua obiecte Lucrator l1
si l2 nu ar lucra simultan. Intai ar lucra l1, executandu-se instructiunea l1.lucreaza() si
abia dupa ce s-ar termina activitatea acestuia s-ar executa si instructiunea
l2.lucreaza(). Altfel ar sta treaba daca cei doi lucratori ar lucra in acelasi timp, in
paralel. Acest lucru devine posibil daca definim clasa Lucrator ca fiind subclasa a
clasei Thread. Aceasta clasa implementeaza un “fir de executie” paralela. Ea este
definita ca implementand interfata Runable:
class Thread implements Runable
Interfata Runable defineste o singura metoda – metoda run():
interface Runable{
void run();
}
Metoda run() joaca rolul metodei main() din aplicatiile cu un singur fir de executie
(thread) cum au fost cele prezentate de noi pana in prezent. Cand se lanseaza un nou
fir de executie, incepe sa fie executata metoda run(). Aceasta se executa in paralel cu
celalalte fire de executie (cum ar fi cel care executa metoda main()). Din aceasta
metoda, daca ea este supradefinita intr-o subclasa, pot fi apelate celalalte metode ale
clasei. Lansarea firului de executie se face cu metoda start() definita in clasa Thread.
Aceasta creaza firul de executie si apeleaza metoda run() a acestuia. Vom modifica
programul anterior astfel incat sa fie folositi doi lucratori. De data aceasta ( ca sa
punem in evidenta activitatea paralela a celor doi lucratori) unul va “planta” puncte iar
celalalt stelute. Programul este urmatorul:
class Plantator{
public static void main(String[] argv){
Lucrator l1=new Lucrator(40,"."),
l2=new Lucrator(40,"*");
l1.start();l2.start();
}
}
Curs 15
Ciclul de viata al unui fir de executie
Un obiect din clasa Thread din momentul instantierii sale pana la oprirea si
distrugerea sa se poate afla in diferite stari reprezentate in figura 15.1
¾ Born – thread-ul a fost creat. El ramane in aceasta stare pana la apelul de catre alt
thread a metodei start().
¾ Ready – threadul este gata pentru executie, asteptand sa i se cedeze controlul
procesorului pe durata unei cuante de timp
¾ Running – threadul a preluat controlul procesorului si se executa pe durata unei
cuante de timp
¾ Waiting – la fel ca si un automobil care se opreste in coada unui sir de masini
asteptand la un semafor culoarea verde pentru a porni mai departe, in urma unei
instructiuni wait(s) threadul, este dezactivat fiind amplasat , eventual alaturi de
alte threaduri, intr-o coada de asteptare. Starea “semaforului” s este supravegheata
de un thread dedicat care in momentul in care “semaforul” ajunge in starea
necesara (are culoarea verde) va activa primul thread din coada de asteptare
apeland metoda notify() sau apeland metoda notifyAll(), va activa toate threadurile
din coada. In momentul in care un trhread aflat in starea Waiting este astfel
notificat, el trece in starea Ready, asteptand sa ii vina randul sa preia controlul
procesorului.
182 CURS 15
prioritatea threadului principal care le-a creat (in cadrul metodei main()).
Fig.15.3 Planificarea pe niveluri de prioritate a executiei threadurilor
Prioritatea threadurilor poate fi obtinuta prin apelul metodei getPriority() si
modificata cu metoda setPriority() care “arunca” o exceptie din clasa
java.lang.IllegalArgumentException daca argumentul are o valoare in afara
domeniului admis [1,10]. Clasa Thread defineste trei constante corespunzand valorilor
uzuale ale nivelurilor de prioritate:
¾ Thread.MIN_PRIORITY = 1;
¾ Thread.MAX_PRIORITY=10;
¾ Thread.NORM_PRIORITY = 5;
PROGRAMARE IN JAVA - Note de curs 185
class Plantator{
public static void main(String[] argv){
Lucrator l1=new Lucrator(),
l2=new Lucrator(),
l3=new Lucrator(),
l4=new Lucrator();
l1.start();l2.start();l3.start();l4.start();
while(!(l1.isReady()&&l2.isReady()&&l3.isReady()&&l4.isRe
ady()));
l1.stop();l2.stop();l3.stop();l4.stop();
}
}
Rezultatele afisate de program in trei executii succesive sunt redate in figura 15.4
while(!(l1.isReady()&&l2.isReady()&&l3.isReady()&&l4.isRe
ady()));
l1.stop();l2.stop();l3.stop();l4.stop();
}
}
try{
sleep(500);
}
catch(InterruptedException e){
System.err.println("Exception"+ e.toString());
}
System.out.println(getName());
ready=true;
}
}
Rezultatele afisate de program in trei executii succesive sunt redate in figura 15.5
Interfata Runnable
Asa cum am vazut, clasa Thread implementeaza interfata Runnable care specifica
metoda publica void run(). Putem folosi aceasta interfata pentru a defini propriile
noastre clase care o implementeaza. Aceste clase vor defini fiecare, pe langa alti
diversi membri (variabile si metode), o varianta proprie a metodei public void run().
Aceste clasele proprii care implementeaza interfata Runnable nu sunt threaduri dar pot
fi folosite pentru a crea instante ale clasei Thread. In acest scop clasa Thread are
definiti urmatorii doi constructori:
¾ Thread(Runnable target)
¾ Thread(Runnable target, String nume)
La instantierea unui obiect Thread cu unul din acesti constructori va fi creat un fir de
executie a carei metoda run() nu va mai fi cea implicita (care nu face nimic) fiind
inlocuita cu metoda run() a clasei tinta (target) folosita la instantiere. Sa exemplificam
cele aratate mai sus prin urmatorul program avand aceeasi functionalitate ca si cel din
exemplul cu doi lucratori care plantau puncte si stelute la consola.
class Plantator{
public static void main(String[] argv){
Lucrator l1=new Lucrator(40,"."),
l2=new Lucrator(40,"*");
Thread t1=new Thread(l1),
t2=new Thread(l2);
t1.start();t2.start();
while(!(l1.isReady()&&l2.isReady()));
t1.stop();t2.stop();
PROGRAMARE IN JAVA - Note de curs 189
}
}
class Lucrator implements Runnable{
private int nrP;
private boolean ready;
private String sP;
public Lucrator(int n, String
s){nrP=n;sP=s;ready=false;}
private void planteaza(){
try{
Thread.sleep(500);
}
catch(InterruptedException e){}
System.out.print(sP);
System.out.flush();
Thread.yield();
}
private void lucreaza(){
for(int i=0;i<nrP;i++)
planteaza();
ready=true;
}
public boolean isReady(){return ready;}
public void run(){lucreaza();}
}
Mecanismul descris mai sus este preferabil celui bazat pe mostenire (folosit in
exemplele anterioare ). Spre deosebire de C++ care permitea mostenirea multipla,
limbajul Java nu permite mostenirea decat a unei singure clase. Astfel ca o clasa care
mosteneste clasa Thread nu mai poate mosteni o alta clasa ceea ce este un dezavantaj.
In schimb o clasa care implementeaza interfata Runnable poate mosteni o alta clasa,
putand in acelasi timp sa serveasca pentru instantierea unui Thread.
clasa Lucrator care este mostenita de doua clase diferite Lucrator1 si Lucrator2 care in
plus implementeaza interfata Runnable. Cele doua clase derivate au codul metodei
run() diferit, astfel ca si threadurile create pe baza acestor doua clase difera. datele
procesate de threaduri sunt si ele diferite, t1 prelucrand datele continute de variabilele
membru ale obiectului l1 iar t2 ale obiectului l2.
class Plantator{
public static void main(String[] argv){
Lucrator1 l1=new Lucrator1(40,".");
Lucrator2 l2=new Lucrator2(40);
Thread t1=new Thread(l1),
t2=new Thread(l2);
t1.start();t2.start();
while(!(l1.isReady()&&l2.isReady()));
t1.stop();t2.stop();
}
}
class Lucrator {
protected int nrP;
protected boolean ready;
protected String sP;
public Lucrator(int n){nrP=n;ready=false;}
protected void planteaza(){
try{
Thread.sleep(100);
}
catch(InterruptedException e){}
System.out.print(sP);
System.out.flush();
Thread.yield();
}
public boolean isReady(){return ready;}
}
class Lucrator1 extends Lucrator implements Runnable{
public Lucrator1(int n,String s){super(n);sP=s;}
public void run(){
for(int i=0;i<nrP;i++)
planteaza();
ready=true;
}
}
class Lucrator2 extends Lucrator implements Runnable{
public Lucrator2(int n){super(n);}
public void run(){
for(int i=0;i<nrP;i++){
sP=String.valueOf((int)(Math.random()*10));
planteaza();
}
ready=true;
}
192 CURS 15
}
Rezultatul afisat la consola de program este prezentat in figura 15.7.
Thread.currentThread().getName()+"="+produs);
Thread.yield();
}
ready=true;
}
public boolean isReady(){return ready;}
}
Cele doua threaduri au codul metodei run() diferit fiind construiti pe baza a doua
obiecte Runnable diferite. Primul calculeaza suma elementelor unui tablou de intregi
iar celalalt produasul elementelor unui tablou de intregi. Tabloul de intregi prelucrat
de fiecare thread este referit de variabila membru data initializata de constructorul
clasei cu o referinta primita ca argument. Metoda main() creaza o instanta s a clasei
Suma si o instanta p a clasei Prod, specificand ca argument al constructorilor o
referinta la acelasi tablou de date data, astfel incat metodele run() ale celor doua
obiecte s si p, desi au codul diferit vor procesa de fapt acelasi date. Cele doua obiecte
Runnable, s si p sunt folosite fiecare la instantierea a cate unui thread – t1 si respectiv
t2. La instantierea threadurilor s-au folosit constructori in varianta care seteaza si
numele threadului. Astfel threadul t1 va avea numele “Suma” si metoda run() a lui s
calculand suma iar t2 va avea numele “Produs” si metoda run() a lui p calculand
produsul elementelor aceluiasi tablou de intregi data. Pentru a pune in evidenta
functionarea concurenta a threadurilor, metodele run ale acestora afiseaza si
rezultatele intermediare obtinute pe parcursul procesarii datelor. In figura 15.8 sunt
prezentate rezultatele afisate de program.
Curs 16
Sincronizarea firelor de executie
Fie programul urmator care utilizeaza doua threaduri Lucrator pentru a “planta”
puncte si stelute pe ecran:
class Plantator{
public static void main(String[] argv){
Lucrator l1=new Lucrator(40,"."),
l2=new Lucrator(40,"*");
Thread t1=new Thread(l1),
t2=new Thread(l2);
t1.start();t2.start();
while(!(l1.isReady()&&l2.isReady()));
t1.stop();t2.stop();
}
}
va trebui sa astepte ca acesta sa capete valoarea true pentru a-si “planta” punctul. La
fel, daca t2 gaseste “flagul” avand valoarea true va trebui sa astepte ca acesta sa
capete valoarea false pentru a-si “planta” steluta. Dupa ce “plantarea” a fost efectuata,
threadul curent va comuta “flagul” pe pozitia complementara pentru a-si interzice o
noua “plantare” si in acelasi timp pentru a autoriza celalalt thread sa isi faca
“plantarea”. Programul este urmatorul:
class Plantator{
public static void main(String[] argv){
Semafor semafor=new Semafor();
Lucrator l1=new Lucrator(semafor,40,"."),
l2=new Lucrator(semafor,40,"*");
Thread t1=new Thread(l1,"T1"),
t2=new Thread(l2,"T2");
t1.start();t2.start();
while(!(l1.isReady()&&l2.isReady()));
t1.stop();t2.stop();
}
}
textul sau, una apasand o tasta, apoi cealalta, textul imprimat ar fi un mixaj
neinteligibil al celor doua texte. Solutia este ca sa se asigure accesul la masina de scris
numai a unei singure dactilografe, cealalta trebuind sa astepte eliberarea masinii de
scris pentru a putea incepe dactilografierea propriului text. Dactilografa care a ajuns
prima la masina de scris si a ocupat-o, nu o elibereaza decat atunci cand a terminat de
dactilografiat textul propriu. Mecanismul care asigura un astfel de mod de lucru a
acestei sectiuni critice din activitatea dactilografelor este cel descris anterior.
In program sectiunea critica a unui thread se inscrie intr-un bloc care este
“sincronizat” cu un obiect ( de regula cu obiectul careia ii apartine codul):
class Office{
public static void main(String[] argv){
MasinaDeScris ms=new MasinaDeScris();
String doc1="Directiva Nr.1/06.1999\n",
doc2="Nota Contabila Nr.1/06.1999\n";
Dactilografa ana=new Dactilografa(ms,doc1),
maria=new Dactilografa(ms,doc2);
Thread t1=new Thread(ana,"Ana"),
t2=new Thread(maria,"Maria");
t1.start();t2.start();
while(!(ana.isReady()&&maria.isReady()));
t1.stop();t2.stop();
}
}
catch(InterruptedException exception){}
System.out.print(c);
System.out.flush();
}
}
In figura 16.3 sunt redate rezultatele a trei executii succesive ale programului care
confirma necesitatea accesului sincronizat al dactilografelor la masina de scris.
timp, executia fiind reluata de threadul t1 din punctul in care a fost intrerupt adica in
corpul instructiunii de selectie. El va inscrie in variabila zi valoarea 1 si va incrementa
variabila luna. Intr-o astfel de executie, variabila luna a fost incrementata de doua ori
in loc sa fie incrementata odata ceea ce evident duce la un rezultat eronat(s-a sarit
peste o luna). Pentru a evita astfel de situatii secventa data trebuie sincronizata cu un
obiect prevenind astfel executia concurenta a metodei de catre threadul t2 in timp ce
ea este executata si de t1. Threadul t2 va fi trecut in starea Waiting si pus intr-o coada
de asteptare la obiectul folosit pentru sincronizare pana cand t1 va termina executia
metodei. Pentru sincronizare se va folosi chiar obiectul partajat referit cu this, care
face parte din clasa Resursa, subclasa a clasei Object:
class Resursa extends Object{
private int zi,luna,an;
...
public void up(){
synchronized(this){
zi++;
if(zi > nrZileInLuna(luna,an)){
zi=1;
luna++;
...// procesare an
}
}
}
...
}
In acest caz, cand intreg corpul metodei este sincronizat chiar cu obiectul caruia
metoda ii apartine (referinta la acesta fiind this), limbajul Java prevede o scriere
prescurtata echivalenta:
class Resursa extends Object{
private int zi,luna,an;
...
public synchronized void up(){
zi++;
if(zi > nrZileInLuna(luna,an)){
zi=1;
luna++;
...// procesare an
}
}
...
}
Se spune despre metoda up ca este sincronizata. Un obiect care contine cel putin o
metoda sincronizata se numeste monitor.
Interblocarea threadurilor
La proiectarea programelor Java multithread trebuie avut grija sa se evite
interblocarea threadurilor. Fenomenul are loc in urmatoarele conditii. Sa ne imaginam
urmatorul scenariu. Sa presupunem ca avem doua monitoare m1 si m2. Fiecare
dispune de metodele sincronizate f1() si f2(). Metoda m1.f1() apeleaza metoda m2.f2()
iar metoda m2.f1() apeleaza metoda m1.f2(). Metodele monitoarelor m1 si m2 sunt
apelate de doua threaduri t1 si t2 astfel: t1 apeleaza m1.f1() iar t2 pe m2.f1(). Apeland
metoda m1.f1(), threadul t1 isi insusseste cheia de la monitorul m1. Daca in acest
moment ii expira cuanta de timp alocata, el este intrerupt si intra in executie threadul
t2. Acesta la randul sau apeleaza metoda m2.f1() si isi insuseste cheia de la monitorul
200 CURS 16
m2. Continuind executia, acestei metode, aceasta va apela metoda m1.f2(). Threadul
t2 nu detine cheia de la monitorul m1 (aceasta fiind inca in posesia threadului
intrerupt t1) asa ca se va bloca asteptand ca t1 sa elibereze cheia. La reactivarea
threadului t1 pentru o noua cuanta de timp, acesta va continua executia din punctul in
care a fost intrerupt adica din interiorul metodei m1.f1(). Executand aceasta metoda, la
un moment dat va apela metoda m2.f2(). Deoarece nu detine cheia de la monitorul m2
(aceasta fiind in posesia threadului t2) se va bloca asteptand ca t2 sa elibereze cheia.
Dar t2 se afla de asemenea in starea Waiting de asteptare pana cand threadul t1 va
elibera cheia de la m1 si in consecinta cele doua threaduri raman blocate definitiv
asteptandu-se reciproc.
dispose();
System.exit(0);
return true;
}
return super.handleEvent(e);
}
public boolean action(Event e, Object o){
for(long i=0;i<10000000;i++);
return true;
}
public static void main(String[] args){
MyFrame f=new MyFrame();
}
}
Acest program creaza o fereastra cu un buton avand eticheta “Procesare” (figura
16.6) .
apelul metodei sale suspend() . Din aceasta stare nu va iesi decat in urma executiei de
catre threadul AWT a metodei acction() la tratarea evenimentului de actionare a
butonului “Procesare”. La executia acestei metode threadul AWT va apela metoda
resume() a threadului server reactivandu-l. Treadul server reactivat va relua executia
concurenta a metodei run() din punctul in care a fost suspendat, afisiand un mesaj de
stare adecvat si efectuand procesarea (cele 10.000.000 de iteratii ale ciclului for ). La
terminarea iteratiilor, threadul va relua executia ciclului infinit al metodei, afisand din
nou mesajul de suspenadare si apeland metoda sa suspend() care il va aduce iar in
starea Suspended pana la o noua apasare a butonului “Procesare”. La apasarea
butonului de sistem “x” threadul AWT executa metoda handleEvent() in cadrul careia
detecteaza evenimentul WINDOW_DESTROY asociat acestei actiuni, termina
executia threadului server apeland metoda stop() a acestuia, inchide fereastra si
termina aplicatia. In figura 16.7 este redata ferastra aplicatiei cu mesajele de stare a
threadului.
Curs 17
Comunicatia intre threaduri
Asa cum am vazut in exemplele anterioare programele multithread se proiecteaza
pornind de la ideia cooperarii a doua sau mai multe threaduri la rezolvarea problemei.
Activitatea unui program singlethread putea fi asemuita cu munca din atelierul unui
mestesugar in carea acesta face singur toate operatiile de prelucrare asupra materiei
prime pentru obtinerea in final a produsului finit. De exemplu pentru a fabrica ace,
mestesugarul va reitera succesiv urmatorul lant de operatii – va debita dintr-un colac
de sarma de otel o bucata de lungimea necesara, va ascuti aceasta bucata de sarma la
unul din capete si ii va executa un orificiu la celalalt capat.
Activitatea unui program multhithread poate fi comparata cu productia dintr-o
intreprindere industriala in care se aplica diviziunea muncii, avand mai multi lucratori
specializati intr-o singura operatie. Astfel vom avea trei categorii de muncitori – unii
care efectueaza operatia de debitare, altii care ascut bucatile de sarma la un capat si
altii care executa orificiile de la celalalt capat obtinand produsul finit. Intr-o astfel de
organizare a productiei muncitorii de specializari diferite trebuie sa coopereze
transmitand de la unul la altul produsele intermediare. Intr-o astfel de cooperare unii
muncitori joaca rolul de producatori iar altii de consumator. De exemplu un muncitor
care face operatia de debitare joaca rolul de producator. Muncitorul care ia bucatile de
sarma debitate de colegul sau si le ascute la un capat joaca rolul de consumator. La
randul sau acelasi muncitor joaca rolul de producator in relatia sa cu muncitorul care
executa gaurirea acelor ascutite de el.
Activitatile executate de producatori si consumatori au durate diferite ele
desfasurandu-se asincron. Astfel operatia de debitare a sarmei s-ar putea sa aibe o
durata mai mica decat cea de ascutire a unei astfel de bucati. In consecinta, stocul de
bucati debitate ar creste continuu deoarece muncitorul care efectueaza ascutirea nu are
timp sa consume intreaga productie a colegului sau. Activitatea producator-
consumator trebuie deci sincronizata.
In cazul exemplului nostru sincronizarea se desfasoara astfel. Producatorul executa
produsul si incearca sa-l depuna intr-o cutie. Daca cutia este deja plina el se opreste
asteptand ca partenerul sau sa consume din stoc, facand astfel loc pentru depunerea a
noi produse. In a doua varianta in care durata operatiei de prelucrare efectuata de
consumator este mai mica decat cea efectuata de producator, consumatorul va gasi
cutia goala si va astepta ca producatorul sa depuna in ea un produs.
Pentru mari eficienta prin reducerea timpilor de asteptare pot fi prevazuti in prima
varianta mai multi consumatori la un singur producator si mai multi producatori la un
singur consumator in cea de a doua.
Cutia in care producatorii depun iar consumatorii ridica produsele joaca rol de tampon
(buffer in limba engleza) intre acestia servind la sincronizarea activitatii lor. In cazul
programelor multithreading proiectarea acestora se poate face pornind de la analogia
cu o astfel de intreprindere descrisa mai sus, threadurile avand rolul producator-
consumator similar muncitorilor specializati pe operatii diferite din cazul prezentat.
La elaborarea programului trebuie tinut cont de regulile de sincronizare a threadurilor
si de partajare a datelor discutate in cursul precedent.
Sa analizam programul urmator:
206 CURS 17
import java.awt.*;
class MyFrame extends Frame{
private Producator p;
private Consumator c;
private Thread prod,cons;
private Button b;
private TextField msg1,msg2;
private Buffer buf;
public MyFrame(){
super("Test Producator - Consumator");
b=new Button("Start");
msg1=new TextField(30);
msg2=new TextField(3);
msg1.setEditable(false);
msg2.setEditable(false);
setLayout(new FlowLayout());
add(b);add(msg1);add(msg2);resize(300,100);show();
}
public boolean handleEvent(Event e){
if(e.id==e.WINDOW_DESTROY){
prod.stop();
cons.stop();
hide();
dispose();
System.exit(0);
return true;
}
return super.handleEvent(e);
}
public boolean action(Event e, Object o){
b.disable();
prod.resume();
return true;
}
public static void main(String[] args){
MyFrame f=new MyFrame();
f.buf=new Buffer();
f.p=new Producator(f.msg1,f.buf);
f.c=new Consumator(f.msg2,f.buf,f.b);
f.prod=new Thread(f.p);
f.cons=new Thread(f.c);
f.p.setPartner(f.cons);
f.c.setPartner(f.prod);
f.prod.start();
f.cons.start();
}
}
class Producator implements Runnable{
private TextField msg;
private Buffer buf;
private Thread partner;
private int k;
private StringBuffer sb;
class Buffer{
private int data;
public void store(int val){data=val;}
public int get(){ return data;}
}
Aici producatorul este modelat de threadul prod construit pe baza unui obiect
Runnable din clasa Producator. Consumatorul este modelat printr-un thread cons,
construit pe baza unui obiect Runnable din clasa Consumator. Odata creati si activati
in metoda main() prin apelul metodelor start() ale threadurilor, producatorul si
consumatorul incep executia metodelor run() definite in clasele Runnable Producator,
respectiv Consumator. Aceste metode contin doua cicluri incuibate for – ciclul
exterior fiind un ciclu infinit iar al cel interior un ciclu cu contor cu 5 iteratii.
Amandoua threadurile intra imediat in starea Suspended prin executia in prima
instructiune a ciclului cu contor a apelului Thread.currentThread().suspend(). La
actionarea butonului “Start”, threadul AWT executa metoda action() dezactivand
208 CURS 17
public MyFrame(){
super("Test Producator - Consumator");
b=new Button("Start");
msg1=new TextField(30);
msg2=new TextField(3);
msg1.setEditable(false);
msg2.setEditable(false);
setLayout(new FlowLayout());
add(b);add(msg1);add(msg2);resize(300,100);show();
}
public boolean handleEvent(Event e){
if(e.id==e.WINDOW_DESTROY){
prod.stop();
cons.stop();
hide();
dispose();
System.exit(0);
return true;
}
return super.handleEvent(e);
210 CURS 17
}
public boolean action(Event e, Object o){
b.disable();
prod.resume();
return true;
}
public static void main(String[] args){
MyFrame f=new MyFrame();
f.buf=new Buffer();
f.p=new Producator(f.msg1,f.buf);
f.c=new Consumator(f.msg2,f.buf,f.b);
f.prod=new Thread(f.p);
f.cons=new Thread(f.c);
f.prod.start();
f.cons.start();
}
}
class Producator implements Runnable{
private TextField msg;
private Buffer buf;
private int k;
private StringBuffer sb;
}
}
}
class Buffer extends Object{
private int data;
public synchronized void store(int val){
data=val;
notify();
}
public synchronized int get(){
boolean isException;
do{
try{
wait();
isException=false;
}
catch(InterruptedException exception){isException=true;}
}while(isException)
return data;
}
}
Deosebirile fata de programul anterior constau in urmatoarele:
La pornire threadul productor este suspendat dar threadul consumator este activ si
incearca sa preia o data din Buffer. in acest scop el apeleaza metoda sincronizata get().
Executand aceasta metoda, el apeleaza la inceputul ei metoda wait() si intra intr-o
stare de asteptare. La actionarea butonului “Start” este executata de catre thradul
AWT metoda action care apeleaza metoda resume() a threadului producator care este
astfel activat. Threadul consumator genereaza numarul aleator, il afiseaza si apeleaza
metoda sincronizata store a bufferului pentru a-l inscrie. Dupa ce data a fost inscrisa,
threadul producator executand metoda store() apeleaza metoda notify() care “trezeste”
treadul consumator din starea de asteptare. Threadul producator revine din metoda
store si incepe sa genereze o noua data in timp ce threadul consumator revine in
metoda sa run(), actualizeaza suma si o afiseaza dupa care trece la urmatoarea iteratie
procesul repetandu-se de 5 ori. Dupa a cincea iteratie ciclul cu contor se termina,
ciclul producator reseteaza StringBufferul si intra din nou in starea Suspended.
Consumatorul reinitializeaza controrul si suma la 0 intrand in asteptarea furnizarii
unei noi date in buffer. Procesul descris mai sus se reia dupa o noua actionare a
butonului.
Clasa Vector
Clasa Vector definita in pachetul java.util implementeaza o structura de tip tablou cu
alocare dinamica de memorie. Aceasta clasa este utila atunci cand numarul de
elemente ale tabloului nu este dinainte cunoscut. Un obiect din aceasta clasa va creste
automat (alocindu-si dinamic memoria necesara) atunci cand ii sunt adaugate noi
elemente. In plus clasa permite operatii suplimentare cum ar fi eliminarea unui
element din vector sau inserterea unui nou element in interiorul vectorului.
Elementele vectorului sunt instante ale clasei Object sau subclase ale acesteia si deci
atunci cand se doreste adaugarea in vector a unor date primitive (cum ar fi date de
tipul int ) se va recurge tot la obiecte din clasele corespunzatoare (cum ar fi Integer
pentru date de tip int ).
Constructorii clasei Vecor sunt:
¾ Vector() – construieste un vector cu capacitatea de 10 elemente.
¾ Vector(int cp) – construieste un vector cu capacitatea de cp elemente.
212 CURS 17
Curs 18
Controlul activarii threadurilor
Asa cum am vazut, threadurile care intra in asteptare prin apelul metodei white() a
unui monitor fiind plasate intr-o coada de asteptare din care sunt reactivate prin apelul
metodei notify() a monitorului. Aceasta metoda activeaza un singur thread din cele
aflate in asteptare. Limbajul Java nu specifica pe care dintre ele. daca dorim ca
threadurile sa fie activate in ordinea sosirii, trebuie sa introducem un mecanism
ajutator cum ar fi cel din exemplul urmator:
import java.util.*;
class FiFo{// controleaza ordinea FIFO de activare a thredurilor
private Vector lacate = new Vector();
public void oprire(){
Object lacat=new Object();// un nou lacat
synchronized (lacat){// ia cheia de la lacat
lacate.addElement(lacat);// adauga lacatul in oprite
System.out.println(
Thread.currentThread().getName()+" in asteptare");
try{
lacat.wait();//elibereaza cheia si asteapta activarea
}
catch(InterruptedException e){}
}
}
public void restart(){
Object lacat=null;
synchronized(lacate){
if(lacate.size()!=0){// exista threaduri in asteptare
lacat=lacate.firstElement();// ia primul lacat
lacate.removeElementAt(0);// elimina-l din de vector
}
}
if(lacat!=null){
synchronized(lacat){ // ia cheia si...
lacat.notify(); // activeaza thredului care asteapta
// la acest lacat
}
}
}
}
catch (InterruptedException e){}
System.out.println(
"Reactivare un thread din coada FIFO");
fifoQueue.restart();
}
}
}
class Run implements Runnable{
FiFo queue;
public Run(FiFo fifoQueue){queue=fifoQueue;}
public void run(){
System.out.println(
Thread.currentThread().getName()+" in executie");
queue.oprire();
System.out.println(
Thread.currentThread().getName()+" restartat");
}
}
lacate. Dupa aceea threadul care a creat un obiect lacat apeleaza metoda wait() a
acestuia intrand in starea de asteptare. In acest fel toate cele 10 threaduri se vor afla in
stare de asteptare atasate fiecare la propriul sau obiect lacat plasat in ordinea crearii
(si deci in ordinea opririi theadurilor) in vectorul lacate. Pentru a reactiva threadurile
exact in ordinea in care acestea au intrat in starea de asteptare threadul principal va
apela succesiv metoda restart() a clasei FiFo. Aceasta va extrage din vector obiectele
lacat incepand cu primul (deci exact in ordinea in care au fost introduse) si apeland
metoda notify() pentru fiecare astfel de element. In acest fel cele 10 threaduri vor fi
restartate exact in ordinea in care au fost oprite. Mesajele afisate de aplicatie si
prezentate in figura 18.1 ne confirma functionarea acestui mecanism in conformitate
cu cele aratate anterior. In acest exemplu ordinea de intrare in starea Waiting a
threadurilor a fost 1, 3, 4, 2, 5, 6, 7, 8, 9, 10. Din mesajele afisate la reactivare se
observa ca threadurile au fost restartate in aceeasi ordine.
Pornind de la aceste clase abstracte, limbajul Java defineste printre altele subclasele
PipedInputStream respectiv PipedOutputStream. Prin conectarea ca sursa de date a
unui astfel stream de iesire a unui thread producator cu un stream de intrare al unui
thread consumator se poate realiza comunicarea intre cele doua threaduri asa cum
Atunci cand un thread citeste dintr-un stream de intrare apeland metoda read() a
acestuia, el este blocat pana cand operatia de citire nu este terminata ( pana octetul sau
blocul de octeti nu a fost efectiv preluat de la sursa de date care s-ar putea sa intarzie
furnizarea acestora).
}
}
class Producator implements Runnable{
private PipedOutputStream out;
private boolean running;
public Producator(PipedOutputStream out){
this.out=out;
222 CURS 18
running=true;
}
public void connectTo(PipedInputStream in){
try{
out.connect(in);
}
catch (Exception ex){
System.out.println(ex.toString());
ex.printStackTrace();
}
}
public boolean isRunning(){ return running;}
public void run(){
byte val;
try{
for(int i=0; i<5;i++){
val=(byte)(Math.random()*256);
System.out.print("Scriu "+ val+" ");
out.write(val);
System.out.println("...OK...");
Thread.currentThread().yield();
Thread.sleep((long)(Math.random()*1000));
}
out.close();
running=false;
}
catch(Exception ex){
System.out.println(ex.toString());
ex.printStackTrace();
}
}
}
class Consumator implements Runnable{
private PipedInputStream in;
private boolean running;
public Consumator(PipedInputStream in){
this.in=in;
running=true;
}
public PipedInputStream getStream(){
return (in);
}
public boolean isRunning(){ return running;}
public void run(){
byte val;
try{
while((val=(byte)in.read()) !=-1){
System.out.print("Citesc "+(byte)val +" ");
System.out.println("...OK...");
Thread.currentThread().yield();
Thread.sleep((long)(Math.random()*1000));
}
System.out.println("Legatura inchisa.");
running=false;
}
catch(Exception ex){
System.out.println(ex.toString());
ex.printStackTrace();
}
}
}
PROGRAMARE IN JAVA - Note de curs 223
Fig.18.4 – Transferul de date intre threaduri prin intermediul unui obiect buffer
In acest caz threadurile se folosesc de obiectul buffer asa cum muncitorii se folosesc
de un utilaj. Threadurile Prod si Cons sunt componente active ale aplicatiei,
executanti, iar obiectul Buffer este o componente pasive folosita de executanti pentru
226 CURS 18
realizarea unor operatii asupra datelor. Componentele active le-am reprezentat prin
paralelograme pentru a sublinia caracterul lor paralel (concurent). Componenta
pasiva, l-am reprezentat asa cum ne-am obisnuit deja printr-un dreptunghi. In cazul pe
care ni-l propunem sa-l analizam vom inlocui obiectul pasiv buffer cu un al treilea
thread care va fi solicitat de catre producator sa primeasca si sa pastreze temporar
datele produse iar de catre consumator sa-i inmaneze datele detinute, primite de la
threadul producator. Acest al treilea thread joaca rolul de server in timp ce thredurile
producator si consumator joaca rolul clientilor, apeland la serviciile serverului.
Schematic, cooperarea dintre cele trei threaduri se poate reprezenta ca in figura 18.5.
Spre deosebire de threadurile client care sunt autonome, threadul server Buffer are
doua intrari – store si get fiecare intrare corespunzind unui serviciu oferit de server.
In acest exemplu threadurile client nu au intrari, fiind dupa cum am mai spus
autonome. Cele mai frecvente cazuri sunt insa cele in care un thread server apeleaza la
randul lui la serviciile altor threaduri pentru care el este client.
In cazul nostru, cooperarea dintre threaduri se desfasoara dupa urmatorul scenariu.
Spre exemplul threadul Prod a generat o data si vrea sa o transmita spre pastrare
bufferului. El incearca sa acceseze intrarea store a acestuia. Daca serverul este in acel
moment ocupat cu alte treburi, clientul va gasi aceasta intrare inchisa si va incepe sa
astepte ca ea sa se deschida. Dupa un anumit interval de timp serverul isi termina
treburile cu care era ocupat si accepta cereri de servicii pe intrarea store, descizand-o.
Aici va gasi clientul care a adormit asteptandu-l cu cererea in mana. Aici incepe
practic rendez-vous –ul dintre client si server. Serverul va prelua cererea clientului
care continua sa astepte terminarea rendez-vous-ului, va stoca data intr-o variabila
interna si il va “trezi” pe client, moment in care care rendez-vous-ul se termina.
Clientul se va intoarce la treburile sale ( generarea unei noi date). Serverul la randul
sau isi va continua activitatea acceptand o cerere la intrarea get. Pentru aceasta el
deschide intrarea get unde sa presupunem ca nu gaseste nici un client in asteptare. In
aceste conditii serverul se aseaza comod si incepe sa astepte el sosirea la aceasta
intrare a unui client cu o cerere de extragere a unei date. Cand threadul Cons se
hotaraste sa preia date de buffer el va accesa intrarea get. Intrucat o gaseste deschisa
cu serverul in asteptare de clienti, rendez-vous-ul dintre client si server incepe
imediat. Clientul trezeste serverul si incepe sa astepte sa fie servit. Serverul preia
cererea clientului, extrage data si o inmineaza cluientului moment in care care rendez-
vous-ul se termina. Clientul isi termina asteptarea si isi reia activitatea (prelucrarea
datei obtinute de la server). Serverul se intoarce si el la treburile sale putand de
PROGRAMARE IN JAVA - Note de curs 227
exemplu accepta din nou o cerere la intrarea store. Diagrama desfasurarii in timp a
executiei celor trei threaduri este prezentata in figura 18.6.
Curs 19
Fisiere
Datele (date initiale, rezultate intermediare si rezultatele finale) memorate in variabile
pe perioada executiei programului sunt pierdute in momentul in care variabilele
respective expira si sunt distruse sau la terminarea programului. Fisierele sunt folosite
pentru pastrarea pentru un timp indelungat a unui volum mare de date, chiar si dupa
ce programul care le-a creat isi termina executia. Datele pastrate in fisiere sunt numite
si date persistente. In claculator, fisierele sunt pastrate in dispozitivele de memorie
auxiliara cum ar fi discuri si benzi magnetice sau discuri optice si gestionate de
sistemul de operare. Aceasta inseamna ca operatii cu fisiere precum cautarea, crearea,
stergerea, redenumirea, accesarea (scrierea/citirea in/din fisiere) si altele asemenea se
fac de aplicatie prin apelul rutinelor corespunzatoare ale sistemului de operare. Dupa
modul de acces al aplicatiilor la datele pastrate in fisiere, acestea se pot clasifica in
fisiere cu acces secvential si fisiere cu acces aleator.
Ierarhizarea datelor
Datorita faptului ca dispozitivele de memorie ale calculatoarelor sunt realizate cu
elemente cu doua stari (avand capacitatea de un bit), datele sunt stocate in aceste
dispozitive sub forma binara, fiind reprezentate prin prin succesiuni de valori 1 si 0.
Fiecare astfel de valoare este memorata intr-un element de memorie prin setarea starii
sale in una din cele doua posibile, corespunzator valorii memorate. pentru memorarea
unei date vor fi deci necesare mai multe elemente binare de memorie. Pentru pastrarea
datelor, memoria se aloca in blocuri de avand capacitatea de 1 byte = 8 bit ( 8
elemente binare). Astfel pentru memorarea unei date se vor aloca unul su mai multe
blocuri cu capacitatea de 1 byte, atat cat este necesar pentru memorarea tipului
respectiv de data. De exemplu pentru memorarea unei date de tip caracter este necesar
un singur bloc cu capacitatea de 1 byte. La scrierea programelor, si la introducerea de
la tastatura/citirea de pe ecran a datelor in timpul executiei aplicatiei,
programatorului/operatorului i-ar fi foarte dificil sa lucreze cu datele reprezentate in
format binar. Este mult mai usor ca datele sa fie prezentate in aceasta etapa sub forma
simbolica folosind cifre - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, litere – A, B, C, ... a, b, c, ...
precum si alte semne - @, #, $, %, “, ?, !, ..., etc. Toate aceste simboluri folosite la
scrierea programelor si reprezentarea datelor se numesc caractere si formeaza setul de
caractere al calculatorului respectiv. Deoarece calculatorului nu poate memora si
procesa decat date binare, aceste caractere sunt codate, fiecarui caracter fiindu-i
asociat un cod binar, o combinatie unica de 1 si 0. La introducerea lor in calculator se
face codarea sub forma binara iar la afisare sunt decodate, pe ecran afisandu-se
simbolul corespunzator codului binar. Astfel cand se apasa pe tastatura o tasta avand
inscriptionata pe ea simbolul 1, catre calculator este transmisa data 00110001, acesta
fiind codul binar al cifrei 1. De asemenea cind un program trimite spre placa grafica a
calculatorului codul binar 00110001, aceasta va determina afisarea pe display a
simbolului 1. Limbajul Java foloseste pentru codarea caracterelor codul Unicode.
Acest cod foloseste doi byte pentru reprezentarea codului. In acest cod, toate
caracterele litere, cifre si semne (de pe tastatura ) contin in octetul superior 00000000.
230 CURS 19
Pentru a usura cautarea in fisier a unei anumite inregistrari cel putin unul din campuri
al inregistrarii se alege drept cheie de cautare. Campul cheie trebuie astfel ales incat sa
identifice in mod unic inregistrarea cautata. Cheia de cautare poate sa fie de exemplu
numarul de legitimatie al unui anumit angajat deoarece identifica in mod unic
inregistrarea acestuia ( nu exista doi angajati cu acelasi numar de legitimatie). Exista
mai multe moduri de a organiza inregistrarile in fisier. Un mod este de a inscrie
inregistrarile intr-un fisier secvential, una dupa alta, in ordinea valorilor din campul
cheie ales. De regula intreprinderile stocheaza informatiile privind activitatea lor in
mai multe fisiere de date - date de personal, date privind stocurile, date privind
situatia facturilor, date privind operatiile contabile, etc. Unele date sunt
interdependente. Un grup de fisiere continand date interdependente astfel incat
modificarea unor date dintr-un fisier implica modificarea datelor din restul de fisiere
din grup formeaza o baza de date. Un pachet de programe destinat gestiunii fisierelor
unei baze de date constitue un sistem de gestiune a bazelor de date (SGBD).
¾ abstract void write(byte b[],int offset, int len ) throws IOException – scrie in
stream len octeti din bufferul b, incepand cu elementul de la indexul offset.
¾ abstract void write(byte b[] ) throws IOException – scrie in stream octetii
continuti de bufferul b.
¾ abstract void write(int b ) throws IOException – scrie in stream o valoarea de tip
int transmisa ca argument.
¾ abstract void writeBoolean(boolean v) throws IOException – scrie in stream o
valoarea de tip boolean transmisa ca argument.
¾ abstract void writeByte(int v) throws IOException – scrie in stream un byte cu
valoarea transmisa ca argument.
¾ abstract void writeBytes(String s) throws IOException –scrie in stream octetii
continuti de stringul s.
¾ abstract void writeChar(int v) throws IOException – scrie in stream o valoarea de
tip char transmisa ca argument.
¾ abstract void writeChars(String s) throws IOException –scrie in stream
caracterele continute de stringul s.
¾ abstract void writeDouble(double v) throws IOException – scrie in stream o
valoarea de tip double transmisa ca argument.
¾ abstract void writeFloat(float v) throws IOException – scrie in stream o valoarea
de tip float transmisa ca argument.
¾ abstract void writeInt(int v) throws IOException – scrie in stream o valoarea de tip
240 CURS 19
¾ final int read(byte b[]) throws IOException – citeste octeti din stream pana cand
tabloul b si intoarce numarul de octeti citit.
¾ final int read(byte b[],int offset,int length) throws IOException – citeste un numar
de length octeti din stream pana cand tabloul b incepand cu indexul offset. Metoda
intoarce numarul de octeti citit.
¾ final boolean readBoolean() throws IOException – citeste din stream si intoarce o
valoare de tip boolean.
¾ final byte readByte() throws IOException – citeste din stream si intoarce o valoare
de tip byte.
¾ final char readChar() throws IOException – citeste din stream 2 octeti si intoarce
o valoare de tip char pe 16 biti.
¾ final double readDouble() throws IOException – citeste din stream 8 octeti si
intoarce o valoare de tip double pe 64 de biti.
¾ final float readFloat() throws IOException – citeste din stream 4 octeti si intoarce
o valoare de tip float pe 32 de biti.
¾ final void readFully(byte b[]) throws IOException – citeste octeti din stream si nu
revine pana cand tabloul b de este completat in intregime.
¾ final void readFully(byte b[],int offset,int length) throws IOException – citeste
octeti din stream si nu revine pana cand tabloul b nu este completat incepand cu
pozitia offset cu un numar de length bytes.
¾ final int readInt() throws IOException – citeste din stream 4 octeti si intoarce o
valoare de tip int pe 32 de biti.
¾ final String readLine () throws IOException – citeste succesiv octeti din stream
pana la primul caracter de control ‘\n’ sau ‘\r’, perechea “\r\n” sau pana este
detectat sfarsitul de fisier (EOF). Intoarce un String continand caracterele
respective.
¾ final long readLong() throws IOException – citeste din stream 8 octeti si intoarce
o valoare de tip long pe 64 de biti.
¾ final short readShort () throws IOException – citeste din stream 4 octeti si
intoarce o valoare de tip short pe 32 de biti.
¾ final int readUnsignedByte () throws IOException – citeste din stream si intoarce
PROGRAMARE IN JAVA - Note de curs 241
Clasa defineste variabila protected int written continand numarul de octeti inscrisi in
stream si metodele:
¾ void flush() throws IOException – are ca efect golirea bufferului streamului la care
este conectat (numai daca acesta este bufferizat).
¾ final int size() throws IOException – intoarce valoarea continuta de variabila
written reprezentand numarul de octeti expediati.
¾ synchronized void write(byte b[],int offset, int len ) throws IOException – scrie in
stream len octeti din bufferul b, incepand cu elementul de la indexul offset.
¾ synchronized void write(int b ) throws IOException – scrie in stream o valoarea de
tip int transmisa ca argument.
¾ final void writeBoolean(boolean v) throws IOException – scrie in stream o
valoarea de tip boolean transmisa ca argument.
¾ final void writeByte(int v) throws IOException – scrie in stream un byte cu
valoarea transmisa ca argument.
¾ final void writeBytes(String s) throws IOException –scrie in stream octetii
continuti de stringul s.
¾ final void writeChar(int v) throws IOException – scrie in stream o valoarea de tip
char transmisa ca argument.
¾ final void writeChars(String s) throws IOException –scrie in stream caracterele
continute de stringul s.
¾ final void writeDouble(double v) throws IOException – scrie in stream o valoarea
de tip double transmisa ca argument.
¾ final void writeFloat(float v) throws IOException – scrie in stream o valoarea de
tip float transmisa ca argument.
¾ final void writeInt(int v) throws IOException – scrie in stream o valoarea de tip int
transmisa ca argument.
¾ final void writeLong(long v) throws IOException – scrie in stream o valoarea de
tip long transmisa ca argument.
¾ final void writeShort(int v) throws IOException – scrie in stream o valoarea de tip
short transmisa ca argument.
¾ final void writeUTF (String s) throws IOException – scrie in stream un String in
format UTF (Unicode).
242 CURS 19
class File2Con{
public static void main(String[] args) throws IOException
{ String s;
if(args.length==0){
System.out.println("Usage: File2Con <file_name>");
System.exit(0);
}
DataInputStream in=new DataInputStream(
new FileInputStream(args[0]));
while((s=in.readLine())!=null){
System.out.println(s);
System.out.flush();
}
}
}
PROGRAMARE IN JAVA - Note de curs 243
Curs 20
Clasa PrintStream
Aceasta clasa defineste un tip de stream de iesire derivat din FilterOutputStream
destinat transmiterii de date sub forma de text. Am folosit frecvent acest tip de stream
de iesire apeland metodele lui System.out deoarece obiectul System.out este o instanta
a clasei PrintStream. Clasa are doi constructori:
¾ PrintStream(OutputStream out) – care construieste un obiect PrintStream conectat
la un stream de iesire out ca destinatie a datelor. Acest stream poate fi oricare din
streamurile de intrare primitive discutate (FileOutputStream,
ByteArrayOutputStream, PipedOutputStream).
¾ PrintStream(OutputStream out, boolean autoflush) – care construieste un obiect
PrintStream conectat la un stream de iesire out ca destinatie a datelor. Acest
stream poate fi oricare din streamurile de intrare primitive discutate
(FileOutputStream, ByteArrayOutputStream, PipedOutputStream). Daca
parametrul autoflush are valoarea true bufferul va fi golit dupa fiecare linie de text
transmisa (treminata cu caracterul de control ‘\n’).
public Evidenta(){
super("Fisier secvential");
lNume=new Label("Nume");
lPNume=new Label("Prenume");
lGrupa=new Label("Grupa");
PROGRAMARE IN JAVA - Note de curs 245
lNota=new Label("Nota");
tNume=new TextField(20);
tPNume=new TextField(20);
tGrupa=new TextField(4);
tNota=new TextField(2);
bAdd=new Button("Adaugare");
bTerm=new Button("Terminare");
setLayout(gbl=new GridBagLayout());
gbc=new GridBagConstraints();
gbc.fill=gbc.NONE;
gbc.weightx=1;
gbc.weighty=1;
gbc.anchor=gbc.EAST;
addComponent(lNume,gbl,gbc,0,0,1,1);
addComponent(lPNume,gbl,gbc,1,0,1,1);
addComponent(lGrupa,gbl,gbc,2,0,1,1);
addComponent(lNota,gbl,gbc,3,0,1,1);
gbc.fill=gbc.HORIZONTAL;
gbc.weightx=2;
addComponent(tNume,gbl,gbc,0,1,1,1);
addComponent(tPNume,gbl,gbc,1,1,1,1);
addComponent(tGrupa,gbl,gbc,2,1,1,1);
addComponent(tNota,gbl,gbc,3,1,1,1);
gbc.weightx=1;
gbc.fill=gbc.BOTH;
addComponent(bAdd,gbl,gbc,4,0,1,1);
gbc.weightx=2;
addComponent(bTerm,gbl,gbc,4,1,1,1);
resize(300,150);
show();
}
private void addComponent(Component c,
GridBagLayout gbl,
GridBagConstraints gbc,
int row,
int col,
int width,
int height)
{
gbc.gridx=col;
gbc.gridy=row;
gbc.gridwidth=width;
gbc.gridheight=height;
gbl.setConstraints(c,gbc);
add(c);
}
public boolean handleEvent(Event e){
if(e.id== Event.WINDOW_DESTROY){
quit();
return true;
}
return super.handleEvent(e);
}
private void quit(){
try{
out.close();
DataInputStream in=new DataInputStream(
new FileInputStream("Studenti.dat"));
while((nume=in.readUTF())!=null){
System.out.println(nume+"; "+in.readUTF()+"; "+
in.readUTF()+"; "+in.readInt()+";");
246 CURS 20
}
in.close();
}
catch(Exception ex){}
dispose();
hide();
System.exit(0);
}
public boolean action(Event e, Object o){
if(e.target==bTerm) quit();
else if(e.target==bAdd){
try{
out.writeUTF(tNume.getText());
out.writeUTF(tPNume.getText());
out.writeUTF(tGrupa.getText());
out.writeInt(Integer.parseInt(tNota.getText()));
tNume.setText(""); tPNume.setText("");
tGrupa.setText("");tNota.setText("");
}
catch(Exception ex){}
}
return true;
}
public static void main(String[] args)throws IOException
{
Evidenta e=new Evidenta();
e.out=new DataOutputStream(
new FileOutputStream("Studenti.dat"));
}
}
utilzarii fisierelor cu acces secvential pot fi evitate daca se folosesc fisiere cu acces
aleator.
Daca este actionat butonul bNew va fi creata si afisata o fereastra de dialog in cre
utilizatorul va introduce informatiile necesare deschiderii unui cont nou. Fereastra de
dialog va fi modala si va contine etichete, campuri text pentru introducerea numelui
utilizatorului, a numelui de user ales a parolei si a parolei repetate (pentru evitarea
unei tastari gresite) si afisare de mesaje, un buton “Ok”, un buton “Reset” si un buton
“Cancel”. Amplasarea acestor controale in fereastra de dialog va fi gestionata de un
GridBagLayout manager ca in figura 20.5.
Pe perioada cat fereastra de dialog este afisata, toate controalele din panoul nr.1 vor fi
dezactivate. La actionarea butonului bOk informatiile din fereastra de dialog vor fi
citite, validate si inscrise sub forma de inregistrare in fisierul bazei de date.
Cel de al doilea panou (gestionat de managerul implicit BorderLayout) va contine un
252 CURS 20
3. Calculul identificatorului
Valoarea intreaga din campul id al unei inregistrari din usr.ndx trebuie sa identifice in
mod unic utilizatorul. Am fi putut sa inscriem in acest camp numele de user ales de
utilizator dar aceasta ar fi implicat la cautarea in fisier a informatiei despre un anumit
user compararea octet cu octet a unor stringuri, operatie consumatoare de timp. Din
acest motiv, am preferat ca pe baza primelor maxim opt caractere ale numelui userului
sa formam un intreg lung reprezentat pe 8 bytes. Operatia de comparare a intregilor se
face prin scadere, deci printr-o singura instructiune masina, fiind mult mai rapida
decat compararea a doua siruri de 8 octeti fiecare.
Identificatorul se calculeaza ca fiind intregul lung avand ca reprezentare in memorie
aceiasi 8 octeti care compun si stringul.
Reprezentarea si formula de calcul pentru stringul de 8 caractere “ABCDEFGH” a
acestui identificator este exemplificata in figura 20.7.
Astfel codul literei A va fi octetul cel mai semnificativ al intregului (octetul 7) iar
codul literei H va fi octetul cel mai putin semnificativ (octetul 0).
PROGRAMARE IN JAVA - Note de curs 253
4. Criptarea parolei
Pentru ca parola unui utilizator sa ramana secreta chiar si pentru cineva care are acces
la fisierul de date usr.dat aceasta trebuieste criptata inainte de a fi inscrisa in fisier.
Am ales un algoritmul de criptare foarte simplu bazat pe shift-are codului fiecarui
caracter cu o valoare intreaga cuprinsa intre 1 si 9 aceasta fiind una din cifrele cheii de
criptare. Cheia de criptare este formata din mai multe cifre fiecare din acestea fiind
folosita ciclic la criptarea unui caracter. Cifrul ales pentru criptarea parolelor este 123.
Mecanismul de criptare este descris in figura 20.9.
254 CURS 20
Curs 21
Exemplu de utilizare a fisierelor cu acces aleator
(continuare)
5. Algoritmul de inregistrare a unui utilizator nou in baza de date
Dupa completarea campurilor text din fereastra de dialog cu informatiile necesare si
actionarea butonului “Ok” programul va face intai validarea numelui utilizatorului
prin preluarea in variabila String usrName a sirului din campul tName. Se verifica
daca stringul nu este vid. Daca este vid se afiseaza un mesaj de eroare in campul
tMsg. Daca numele utilizatorului este valid se continua cu validarea celor doua
stringuri introduse ca parola in campurile tPsw1 si tPsw2. Daca ele nu sunt identice se
afiseaza un mesaj de eroare in campul tMsg. Daca sunt identice parola din campul
tPsw1 va fi criptata aplicandu-i algoritmul prezentat la punctul 4 si memorata intr-o
variabila String psw. Se preia in variabila usr stringul din campul tUsr. Se verifica
daca stringul nu este vid. Daca este vid se afiseaza un mesaj de eroare in campul
tMsg. Daca numele de user nu este vid se genereaza identificatorul utilizatorului,
aplicand asupra sa algoritmul prezentat la punctul 3. Valoarea astfel obtinuta este
memorata in variabila long usrID. Se deschide/creaza fisierul cu acces aleator usr.ndx
si se citesc succesiv inregistrarile din acesta in variabilele long id si long pos. Dupa
fiecare citire se compara valoarea din id cu valoarea din usrID. Daca cele doua valori
sunt egale inseamna ca deja exista un utilizator cu acest nume. Ca urmare se inchide
fisierul si se afiseaza un mesaj de eroare in campul tMsg. Daca se ajunge la sfarsitul
fisierului fara a gasi un identificator egal cu cel din variabila usrID inseamna ca
numele de user ales este valid si se scrie in fisier valoarea din usrID. Se deschide
fisierul cu acces aleator usr.dat si se inscrie lungimea acestuia (obtinuta apeland
metoda length()) in fisierul usr.ndx ca pointer la noua inregistrare ce va fi facuta in
usr.dat. Se pozitioneaza pointerul in fisierul usr.dat la sfarsitul acestuia (apeland
metoda seek()) si se inscriu pe rand informatiile privind utilizatorul continute de
variabilele usrName, usr, usrID si se initializeaza contorul de sesiuni de lucru ale
utilizatorului inscriind in fisier (int) 0. Se inchid fisierele bazei de date si fereastra de
dialog.
In diagrama de structura din aceasta figura sunt puse in evidenta legaturile dintre
PROGRAMARE IN JAVA - Note de curs 257
obiecte componente ale aplicatiei apartinand unor clase a caror descriere este data in
continuare:
¾ Clasa principala RAFTest care contine metoda main() va fi derivata din clasa
Frame. Metoda statica main() va crea fereastra aplicatiei RAFTest wnd=new
RAFTest() la creere, constructorul clasei RAFTest() va seta textul din bara de titlu,
va crea un manager de amplasare CardLayout mainLayout=new CardLayout(), il
va seta ca manager de amplasare a ferestrei principale, va crea cele doua panouri
Panel1 p1=new Panel1() si Panel2 p2=new Panel2(), le va adauga ferestrei, va
redimensiona si va face vizibila fereastra. Pentru comanda managerului de
amplasare este prevazuta metoda public void showPanel(int panelNr) care va
aduce in varful stivei formate din cele doua panouri pe cel indicat de argument.
¾ Clasa Panel1 defineste variabila membru Panel2 p2. Constructorul clasei Panel1
va initializa referinta p2 cu o valoare primita ca argument. Va crea etichetele
lUsr, lPwd campurile text tUsr, tPsw, tMsg si butoanele, bOk, bRst, bNew, bQuit
din figura 20.4. Campul text tMsg va fi setat read-only. Va fi creat managerul de
amplasare GridBagLayout pLayout cu obiectul asociat GridbagConstraints gbc.
Controalele vor fi adaugate la panou conform proiectului, folosind metoda
addComponent() definita si utilizata in unele exemple anterioare. Va fi redefinita
metoda action() astfel incat sa trateze evenimentele generate de butoane. Daca
butonul actionat este bOk se va incepe procedura de autentificare a utizatorului
descrisa la punctul 6. Daca autentificarea a reusit se sterg informatiile din
campurile text ale panoului se se deschide sesiunea de lucru apeland metoda
startSesiune()a clasei Panel2. Daca butonul actionat este bRst se sterg informatiile
din campurile text ale panoului. Daca se actioneaza butonul bQuit se termina
aplicatia. Daca butonul actionat este bNew se trece la procedura de inregistrare a
unui user prin creerea unei fereastre de dialog NewUserDialog dlg=
NewUserDialog ().
¾ Clasa Panel2 defineste variabilele String usr, int cnt si boolean sesiune al carei rol
a fost discutat la punctul anterior. Constructorul clasei Panel2 va crea un obiect
258 CURS 21
¾ Clasa Display extends Canvas redefineste metoda paint() astfel aceasta ori de cate
ori este apelata sa testeze starea flagului sesiune a clasei parinte apeland metoda
isSesiune() a acesteia. Daca valoarea intoarsa de aceasta este true metoda va
“desena” pe suprafata componentei un mesaj de salut continand numele
utilizatorului curent. Aceste informatii sunt obtinute prin apelul metodele
getUser() si getCount() ale clasei parinte.
9. Implementare
Mai jos este redat listingul complet al programului care implementeaza clasele
proiectate la punctul precedent:
import java.awt.*;
import java.io.*;
class RAFTest extends Frame{
private boolean canClose;
private Panel1 p1;
private Panel2 p2;
private CardLayout mainLayout;
public RAFTest(){//Constructorul clasei
super("Random Access File Demo");// titlu fereastra
canClose=true;//autorizare inchidere fereastra
mainLayout=new CardLayout();
setLayout(mainLayout);// setare manager de amplasare
// creare celor doua panouri si adaugarea lor la fereastra
p2=new Panel2();
p1=new Panel1(p2);
add("Panel 1",p1);add("Panel 2",p2);
// dimensionarea si afisarea ferestrei
resize(300,120);
show();
}
public boolean handleEvent(Event e){// procesarea evenimentelor
if(e.id==e.WINDOW_DESTROY){// cerere sistem de inchidere
// a fereastrei
quit();// inchiderea fereastrei si terminarea aplicatiei
return true;
}
return super.handleEvent(e);// procesarea celorlalte evenimente
}
public void quit(){//inchiderea fereastrei si terminarea
// aplicatiei
if(canClose){// daca terminarea aplicatiei este autorizata
hide();// ascunde fereastra
PROGRAMARE IN JAVA - Note de curs 259
addComponent(tMsg,gl,gc,3,0,4,1);
//Amplasarea butoanelor
gc.weighty=1;
gc.fill=gc.BOTH;
addComponent(bOk,gl,gc,2,0,1,1);
addComponent(bRst,gl,gc,2,1,1,1);
addComponent(bNew,gl,gc,2,2,1,1);
addComponent(bQuit,gl,gc,2,3,1,1);
}
/*******************************************************************\
* Metoda addComponent este folosita de constructor pentru adaugarea *
* unei componente c la panou. Componenta va fi amplasata in celula *
* din randul row si coloana col si va avea latimea width si inalti- *
* mea hight celule. Ocuparea spatiului astfel alocat se face conform*
* specificatiilor din obiectul GridBagConstraints – parametrul gc. *
\*******************************************************************/
private void addComponent(Component c, GridBagLayout gl,
GridBagConstraints gc,
int row,int col,
int width, int height){
gc.gridy=row;
gc.gridx=col;
gc.gridwidth=width;
gc.gridheight=height;
gl.setConstraints(c,gc);
add(c);
}
// Procesarea evenimentelor generate la actionarea butoanelor
public boolean action(Event e, Object o){
RAFTest parent=(RAFTest)getParent();// fereastra parinte
if(e.target==bOk){// actionarea butonului “Ok”
if(autentificareUser()){// Daca userul a fost acceptat
parent.showPanel(2);// Adu in varf panoul nr. 2
p2.startSesiune(name, cnt);// Incepe sesiunea
}
}else if(e.target==bRst){ // actionarea butonului “Reset”
tUsr.setText("");// sterge continutul campurilor
tPsw.setText("");// user, parola si mesaje
tMsg.setText(“”);
}else if(e.target==bNew){ // actionarea butonului “New”
disableComponents();// dezactiveaza toate componentele
disable();// panoului si panoul in sine.
dlg=new NewUserDialog(this);//creaza fereastra de dialog
}else if(e.target==bQuit){ // actionarea butonului “Quit”
parent.quit();// Solicita terminarea aplicatiei
}
return true;
}
/*******************************************************************\
* Metoda error este apelata de metoda de autentificare a utiliza- *
* torului dupa numele de user si parola. In timpul acestei operatii *
* pot apare diferite erori. Fiecarui tip de eroare i-a fost asociat *
* un numar intreg err.Daca nu a aparut nici o eroare, err=0. Metoda *
* error() afiseaza mesajul corespunzator tipului de eroare si retur-*
* neaza true daca err=0 sau false daca r!=0. *
\*******************************************************************/
private boolean error(int err){
// tablou de stringuri continand mesajele de eroare
String msg[]={"",
"Trebuie completat campul User!",
PROGRAMARE IN JAVA - Note de curs 261
"User neinregistrat!",
"Parola incorecta!",
"Eroare la accesarea bazei de date"};
// afiseaza mesajul asociat erorii nr.err
tMsg.setText(msg[err]);
// Intoarce true daca err=0 sau false in caz contrar
return (err==0? true:false);
}
/*******************************************************************\
* Metoda autentificareUser() citeste si valideaza numele de user *
* introdus de utilizator acesta trebuind sa nu fie un string vid. *
* Daca numele de user este valid se genereaza pe baza lui identifi- *
* catorul usrID folosit drep cheie de cautare in fisierul cu aces *
* aleator usr.ndx. Daca inregistrarea cu campul id=usrID este gasita*
* valoarea din campul pos a acesteia este folosita pentru a accesa *
* inregistrarea cu datele utilizatorului din fisierul usr.dat. Se *
* cripteaza parola introdusa de utilizator in usrPsw si se compara *
* cu stringul citit din fisierul usr.dat din campul parola in varia-*
* bila psw. Daca stringurile sunt identice utilizatorul este accep- *
* tat si metoda intoarce true. In caz contrar, sau daca pe parcursul*
* executiei apar alte erori metoda intoarce false. *
\*******************************************************************/
private boolean autentificareUser(){
int err=0,len,i;
long usrID=0,id,pos;
String psw,usr,usrPsw;
byte b[]=new byte[8];;
RandomAccessFile ndx,dat;
usr=tUsr.getText();
if((len=usr.length())==0)return error(1);
// calculeaza id-ul userului
if(len<8){
usr.getBytes(0,len,b,0);
for(i=len;i<8;i++)b[i]=0;
}else usr.getBytes(0,7,b,0);
for(i=0;i<8;i++){
usrID *=256;
usrID +=b[i];
}
// validare nume user
try{
pos=0;
// deschide fisierele usr.ndx si usr.dat
ndx=new RandomAccessFile("usr.ndx","rw");
dat=new RandomAccessFile("usr.dat","rw");
// cauta inregistrarea cu campul id=usrID
try{
while((id=ndx.readLong())!=usrID){
ndx.skipBytes(8);
}// Daca s-a gasit, citeste campul pos
pos=ndx.readLong();
}catch(EOFException e){err=2;}// EOF ->nu s-a gasit
ndx.close();// inchide fisierul
if(err!=0) {
dat.close();
return error(err);
}
usrPsw=tPsw.getText();// citeste parola introdusa
// Criptare parola
b=new byte[usrPsw.length()];
usrPsw.getBytes(0,b.length,b,0);
262 CURS 21
for(i=0;i<b.length;i++)b[i]+=(byte)(i%3+1);
usrPsw=new String(b,0);
// Citeste inregistrarea din fisierul usr.dat
dat.seek(pos);
name=dat.readUTF();// nume utilizator
usr =dat.readUTF();// nume de user
psw =dat.readUTF();// parola
cnt =dat.readInt();// contorul de sesiuni
cnt++;// actualizeaza contorul
dat.seek(pos);// repozitioneaza pointerul in usr.dat
// inscrie inregistrarea cu contorul actualizat
dat.writeUTF(name); dat.writeUTF(usr);
dat.writeUTF(psw); dat.writeInt(cnt);
dat.close();// inchide fisierul usr.dat
if(!usrPsw.equals(psw))err=3;// compara parolele
}catch(IOException e){// eroare la accesarea fisierelor
err=4;
System.out.println(e.toString());
}
return error(err);
}
/*******************************************************************\
* metodele enableComponent() si disableComponent() activeaza respec-*
* tiv dezactiveaza campurile de editare si butoanele panoului nr.1 *
\*******************************************************************/
public void enableComponents(){
tUsr.enable(); tUsr.enable();
bOk.enable(); bRst.enable();
bNew.enable(); bQuit.enable();
}
public void disableComponents(){
tUsr.disable(); tUsr.disable();
bOk.disable(); bRst.disable();
bNew.disable(); bQuit.disable();
}
}
class NewUserDialog extends Dialog{//fereastra de dialog
private Panel1 panel;// referinta la panoul nr1
// creare etichete
private Label lName=new Label("Name :"),
return true;
}
private void cancel(){// procedura de terminare dialog
panel.enableComponents();// reactiveaza componentele
panel.enable(); // panoului 1 si panoul insusi
// autorizare terminare aplicatie
((RAFTest)getParent()).closeEnable();
hide();// ascunde fereastra de dialog
dispose();// elibereaza resursele alocate ferestrei
}
private void reset(){// sterge continutul campurilor text
tName.setText(""); tUsr.setText("");
tPsw1.setText(""); tPsw2.setText(""); tMsg.setText(“”);
}
/*******************************************************************\
* Metoda error este apelata de metoda addUser() de inregistrare a *
* utilizatorului in baza de date. In timpul acestei operatii pot *
* apare diferite erori. Fiecarui tip de eroare i-a fost asociat un *
* numar intreg err.Daca nu a aparut nici o eroare, err=0. Metoda *
* error() afiseaza mesajul corespunzator tipului de eroare si retur-*
* neaza true daca err=0 sau false daca r!=0. *
\*******************************************************************/
private boolean error(int err){
String msg[]={"",
"Trebuie completat campul Nume!",
"Eroare la introducerea parolei!",
"Trebuie completat campul User!",
"User existent. Alegeti alt nume de user!",
"Eroare la accesarea bazei de date"};
tMsg.setText(msg[err]);
return (err==0? true:false);
}
/*******************************************************************\
* Metoda addUser() implementeaza procedura de inregistrare a utili- *
* zatorului in baza de date formata din fisierele usr.ndx si usr.dat*
\*******************************************************************/
private boolean addUser(){
String name,usr,psw;
int len,i;
long usrID=0,id;
int err=0;
RandomAccessFile ndx,dat;
byte b[]=new byte[8];
name=tName.getText();
// validarea numelui utilizatorului
if(name.length()==0)return error(1);
// validare parola1 prin comparare cu parola2
psw=tPsw1.getText();
if(!psw.equals(tPsw2.getText()))return error(2);
// validare nume user
usr=tUsr.getText();
if((len=usr.length())==0)return error(3);
// calculeaza id-ul userului
if(len<8){
usr.getBytes(0,len,b,0);
for(i=len;i<8;i++)b[i]=0;
}else usr.getBytes(0,7,b,0);
for(i=0;i<8;i++){usrID *=256; usrID +=b[i];}
// verificare daca userul cu identificatorul usrID exista
try{// deschide fisierele bazei de date
ndx=new RandomAccessFile("usr.ndx","rw");
PROGRAMARE IN JAVA - Note de curs 265
dat=new RandomAccessFile("usr.dat","rw");
try{
while(true){// citeste o inregistrare din usr.ndx
id=ndx.readLong();// citeste id
if(id==usrID){// daca user existent
err=4;// eroare
break;// terminare citire
}
ndx.skipBytes(8);// urmatoarea inregistrare
}
}catch(EOFException e){}// sfirsit de fisier
if(err!=0) {// eroare la inregistrare user
ndx.close();// inchide fisierul usr.ndx
dat.close();//inchide fisierul usr.dat
return error(err);// intoarce false
}
// Criptare parola
b=new byte[psw.length()];
psw.getBytes(0,b.length,b,0);
for(i=0;i<b.length;i++)b[i]+=(byte)(i%3+1);
psw=new String(b,0);
// inregistreaza user in fisierul usr.ndx
ndx.writeLong(usrID);// inscrie identificatorul
long pos=dat.length();//calculeaza pozitia in usr.dat
ndx.writeLong(pos);// inscrie pointerul in usr.dat
ndx.close();// inchide fisierul index usr.ndx
dat.seek(pos);// pozitioneaza pointerul in usr.dat
dat.writeUTF(name);// inscrie numele utilizatorului
dat.writeUTF(usr);// inscrie numele de user
dat.writeUTF(psw);// inscrie parola
dat.writeInt(0);//inscrie contorul de sesiuni = 0
dat.close();// inchide fisierul usr.dat
}catch(IOException e){// eroare la accesarea fisierelor
err=5;
System.out.println(e.toString());
}
return error(err);
}
public boolean handleEvent(Event e){//procesarea evenimentelor
if(e.id==e.WINDOW_DESTROY){// cerere sistem de inchidere
cancel();// terminare dialog
return true;
}
return super.handleEvent(e);// tratarea altor evenimente
}
}
// panoul numarul 2
class Panel2 extends Panel{
private boolean sesiune=false;
private String name;
private int cnt;
// crearea componentelor panoului nr.2
private Display dsp=new Display(this);// componenta display
private Button bExit=new Button("Exit");// butonul “Exit”
public Panel2(){
setLayout(new BorderLayout());// managerul de amplasare
add("Center",dsp); // adaugare componente
add("South",bExit);
}
/*******************************************************************\
* Metoda startSesiune() initiaza operatiile de incepere a unei sesi-*
266 CURS 21
Curs 22
Animatie
In cadrul cursurilor 12 si 13 am discutat aspecte legate de desenarea pe suprafata unui
control derivat din clasa Canvas a primitivelor grafice, al textului si a unor imagini
incarcate din fisiere in format GIF sau JPG. In exemplele din acele cursuri desenarea
era executata de metoda callback paint() ori de cate ori era necesara
“reamprospatarea” ferestrei . Alterarea totala sau partiala a imaginii ferestrei pe
display era sesizata de sistemul de operare ( care gestioneaza sistemul de ferestre) si
acesta ere cel care determina executia metodei paint() ori de cate ori considera ca este
necesar. Ca urmare imaginea desenata era statica (cel mult redimensionata in cazul
modificarii dimensiunii “canvasului” de catre managerul de amplasare) modificandu-
se doar ocazional la redimensionarea ferestrei. Animatia consta in reprezentarea
succesiva a unor cadre, fiecare cadru continand o alta imagine (vezi figura 22.1).
Daca succedarea cadrelor are loc la intervale de timp suficient de mici astfel incat
ochiul uman sa nu perceapa pauza dintre cadre, este creata senzatia de miscare –
animatia imaginii.
Animatia presupune redesenarea periodica a imaginii, la fiecare redesenare fiind “pus
pe tapet” un cadru nou, diferit de cel precedent. Am vazut ca metoda paint() poate fi
initiata nu numai de sistemul de operare ci si de aplicatie prin apelul metodei
repaint(). In acest fel un thread al aplicatiei poate modifica periodic un set de date
folosite de metoda paint() la desenarea imaginii, dupa care sa solicite redesenarea
acesteia pentru noile date prin apelul metodei repaint(). In acest fel, imaginile afisate
se vor modifica periodic realizand astfel animatia. Sa analizam urmatorul exemplu
care deplaseaza o discheta in fereastra aplicatiei:
import java.awt.*;
class Play extends Frame{
private int rataRefresh=100;
private Film film=new Film();
public Play(){
super("Demo Animatie");
film.setBackground(Color.white);
add("Center",film);
resize(200,200);
268 CURS 22
show();
}
public boolean handleEvent(Event e){
if(e.id==e.WINDOW_DESTROY){
hide();
dispose();
System.exit(0);
return true;
}
return super.handleEvent(e);
}
public static void main(String[] args){
Play win=new Play();
if(args.length!=0)
win.rataRefresh=Integer.parseInt(args[0]);
for(;;){
try{
Thread.sleep(win.rataRefresh);
}
catch(InterruptedException e){}
win.film.cadruNou();
}
}
}
class Film extends Canvas{
static final byte UP=0;
static final byte DOWN=1;
static final byte LEFT=2;
static final byte RIGHT=3;
static final byte WIDTH=80;
static final byte HEIGHT=80;
static final byte STEP=2;
private boolean firstTime=true;
private int locx,locy;
private byte state=UP;
public void paint(Graphics g){
if(firstTime){
locx=(size().width-WIDTH)/2;
locy=(size().height-HEIGHT)/2;
firstTime=false;
}
String s="MICRO ";
int x[]=new int[6];
int y[]=new int[6];
x[0]=0+locx;x[1]=80+locx;x[2]=80+locx;
x[3]=10+locx;x[4]=0+locx;x[5]=0+locx;
y[0]=0+locy;y[1]=0+locy;y[2]=80+locy;
y[3]=80+locy;y[4]=70+locy;y[5]=0+locy;
g.setColor(Color.gray);
g.fillPolygon(x,y,6);
g.setColor(Color.black);
g.drawPolygon(x,y,6);
g.setColor(Color.white);
g.fillRoundRect(10+locx,0+locy,60,45,10,10);
g.setColor(Color.black);
g.drawRoundRect(10+locx,0+locy,60,45,10,10);
g.draw3DRect(72+locx,2+locy,6,6,false);
g.setColor(Color.darkGray);
g.fillRect(25+locx,50+locy,45,30);
g.setColor(Color.black);
g.drawRect(25+locx,50+locy,45,30);
PROGRAMARE IN JAVA - Note de curs 269
g.setColor(Color.lightGray);
g.fillRect(20+locx,50+locy,40,30);
g.setColor(Color.black);
g.drawRect(20+locx,50+locy,40,30);
g.setColor(Color.darkGray);
g.fillRect(30+locx,52+locy,7,25);
g.setColor(Color.black);
g.drawRect(30+locx,52+locy,7,25);
g.drawString(s,25+locx,15+locy);
}
public void cadruNou(){
Dimension d=size();
switch(state){
case DOWN:
locy+=STEP;
if(locy > (d.height-HEIGHT))state=UP;
break;
case UP:
locy-=STEP;
if(locy < 0)state=RIGHT;
break;
case RIGHT:
locx+=STEP;
if(locx > (d.width-WIDTH))state=LEFT;
break;
case LEFT:
locx-=STEP;
if(locx < 0)state=DOWN;
break;
}
repaint();
}
}
In figura 22.2 sunt redate cateva imagini ale ferestrei aplicatiei la momente diferite de
timp.
Metoda paint() a clasei Film deseneaza discheta din figura pe o componenta Canvas,
aceasta fiind amplasata in centrul ferestrei si redimensionata de managerul de
270 CURS 22
Bucle de imagini
Am vazut ca pe o componenta derivata din clasa Canvas se poate desena si o imagine
continuta intr-un fisier. Mai multe astfel de imagini (cum ar fi cele din figura 22.1) pot
fi afisate succesiv, in bucla realizandu-se astfel animatia acestora. Dupa ce si ulima
imagine a fost desenata, se reia succesiunea cu desenarea primei imagini. Programul
urmator exemplifica implementarea unei astfel de bucle de imagini.
import java.awt.*;
class Play extends Frame{
private int rataRefresh=100;
private Film film=new Film();
private Button b=new Button("Start");
public Play(){
super("Demo Bucla de Imagini");
add("Center",film);
add("South",b);
resize(200,200);
setResizable(false);
show();
}
public boolean handleEvent(Event e){
if(e.id==e.WINDOW_DESTROY){
hide();
dispose();
System.exit(0);
return true;
}
return super.handleEvent(e);
}
PROGRAMARE IN JAVA - Note de curs 271
}
try{
Thread.sleep(300);
}
catch(InterruptedException e){}
repaint();
}
}
In aceasta versiune, metoda paint() apeleaza la sfarsitul sau metoda repaint(). Aceasta
va apela metoda update() care va sterge “canvas”-ul si va apela metoda paint() care la
randul sau va apela metoda repaint() etc. Aparent se formeaza un lant de apeluri
infinit care in final va umple stiva si va bloca executia aplicatiei. In realitate threadul
AWT care executa metoda paint() va apela numai metoda repaint(), lantul de apeluri
oprindu-se aici. Aceasta metoda nu apeleaza direct metoda update() ci produce si
transmite sistemului de operare un mesaj-cerere de reimprospatare a ferestrei
aplicatiei revenind apoi in repaint() de unde va reveni inapoi in paint() si de aici in
update(). Sistemul de operare care gestioneaza mesajele, va plasa mesajul in coada de
mesaje a aplicatiei. La terminarea executiei metodei update(), threadul AWT va intra
intr-o bucla de preluare a mesajelor primite de la sistemul de operare. Mesajele din
coada sunt de fapt notificari ale sistemului de operare asupra evenimentelor privitoare
la fereastra aplicatiei – redimensionare, alterarea unei parti din fereastra prin
acoperirea de o alta ereastra, minimizare/maximizare, cerere de inchidere, miscare
mouse, actionare controale, apasare taste, etc. Daca coada de mesaje este goala,
threadul va astepta sosirea unui mesaj. Daca contine mesaje, va extrage si va prelucra
primul mesaj, va stabili despre ce eveniment este vorba si va incepe tratarea acestuia.
Pana la urma, el va extrage din coada de mesaje si mesajul-cerere de redesenare
generatsi expediat tot de el. In consecinta, va incepe executia metodei update().
Procesul se reia astfel la infinit, fara a incarca insa stiva threadului cu adrese de
revenire. Aceasta are insa si o consecinta neplacuta. Daca in momentul in care
threadul AWT incepe desenarea, utilizatorul actioneaza butonul sistem de terminare a
executiei, sistemul de operare va plasa imediat mesajul de notificare corespunzator in
coada de mesaje. Intre timp threadul WT este ocupat in continuare cu desenarea unei
imagini. Dupa desenare, se dezactiveaza pentru 300 de ms, dupa care se “trezeste” si
incepe executia metodei repaint() din care va expedia un mesaj-cerere de initiere a
executiei metodei update(). Dupa expedierea mesajului-cerere de redesenare, revine in
repaint() din care revine in paint() si inapoi in update(). La revenirea din update(),
threadul preia un mesaj din coada de mesaje. Intrucit mesajul de terminare a fost
plasat de sistem in coada de mesaje inaintea mesajului cerere de redesenare, el va fi
extras inaintea celuilalt, generat si transmis si deci si sosit mai tarziu. Threadul AWT
va apela metoda handleEvent() transmitandu-i acest mesaj ca eveniment
WINDOW_DESTROY. Metoda handleEvent() va trata acest eveniment inchizand
fereastra, eliberand resursele alocate si terminand in final executia aplicatiei.
Problema este ca raspunsul la comanda a venit cu o intarziere de peste 300 ms. Daca
rata de refresh ar fi fost mai mare, si intarzierea ar fi fost mai mare programul
devenind incomod de folosit.
Clasa MediaTracker
Pentru a elimina fenomenul neplacut de la inceputul executiei buclei de imagini, cand
desenarea cadrelor incepe inainte de a se incarca toate imaginile, poate fi folosita
urmatoarea tehnica. Aplicatia este blocata pana cand nu au fost incarcate toate
PROGRAMARE IN JAVA - Note de curs 275
imaginile din fisiere. Monitorizarea incarcarii imaginilor din fisiere se face folosind
un obiect din clasa MediaTracker. Un astfel de obiect se creaza cu constructorul:
MediaTracker(ImageObserver component)
Argumentul constructorului este un obiect tinta al afisarii imaginii apartinand de
exemplu unei subclase a clasei Canvas. De exemplu in constructorul clasei Film se
poate declara variabila:
MediaTracker imageTracker=new MediaTracker(this);
Dupa ce s-au facut toate apelurile getImage(), se inregistreaza imaginile in curs de
incarcare cu metoda addImage() definita in clasa MediaTracker:
imageTracker.addImage(img[i],i);
unde i este identificatorul folosit de pentru inregistrarea obiectului(nu este obligatoriu
sa fie neaparat i, putand fi o valoare intreaga distincta). Acest identificator va fi folosit
pentru a determina aplicatia sa astepte pana cand imaginea inregistrata cu acest numar
va fi citita integral din fisier. De exemplu pentru a se astepta incrcarea completa a
imaginii inregistrate sub identificatorul 0, se va apela metoda waitForID():
imageTracker.waitForID(0);
Acesta apel blocheaza programul a carui executie va fi reluata numai dupa ce
incarcarea imaginii cu identificatorul 0 din fisierului va fi terminata.
Clasa MediaTracker mai prevede metoda waitForAll() care blocheaza aplicatia pana
ce toate imaginile vor fi incarcate. Ambele metode “arunca” o exceptie
InterruptedException care trebuie prinsa sau aruncata mai departe de catre metoda in
cadrul careia se face apelul.
Pentru a nu bloca aplicatia pe perioada incarcarii unei imagini, in acest rastimp ea
putand sa faca alte prelucrari, se foloseste metoda clasei MediaTracker , checkID().
Aceasta metoda are doi parametri:
boolean checkID(int id, boolean b) – intoarce true daca incarcarea imaginii
inregistrate sub identificatorul id s-a terminat si false daca nu a inceput inca sau inca
nu s-a terminat. Al doilea parametru b daca are valoarea true forteaza inceperea
imediata a incarcarii imaginii din fisier daca aceasta operatie inca nu a inceput. Vom
folosi in continuare metoda waitAll() pentru a elimina afisarea a numai unora dintre
imagini in primele cicluri ale buclei din programul anterior. Modificarile afecteaza
doar constructorul clasei Film astfel:
...
public Film(){
setBackground(Color.black);
MediaTracker imageTracker=new MediaTracker(this);
for(int i=0;i<15;i++){
String imageFile="exp"+(i+1)+".gif";
img[i]=Toolkit.getDefaultToolkit().getImage(imageFile);
imageTracker.addImage(img[i],i);
}
try{
imageTracker.waitForAll();
}
catch(InterruptedException e){}
}
...
}
g.drawImage(diskImage,locx,locy,this);// deseneaza imaginea
}
...
}
g.setColor(Color.lightGray);
g.fillRect(20+locx,50+locy,40,30);
g.setColor(Color.black);
g.drawRect(20+locx,50+locy,40,30);
g.setColor(Color.darkGray);
g.fillRect(30+locx,52+locy,7,25);
g.setColor(Color.black);
g.drawRect(30+locx,52+locy,7,25);
g.drawString(s,25+locx,15+locy);
}
public void paint(Graphics gContext){
if(firstTime){
Dimension d=size();
locx=(d.width-WIDTH)/2;
locy=(d.height-HEIGHT)/2;
diskImage=createImage(d.width,d.height);
g=diskImage.getGraphics();
firstTime=false;
}
createDiskImage();
gContext.drawImage(diskImage,0,0,this);
}
public void update(Graphics g){paint(g);}
...
}
PROGRAMARE IN JAVA - Note de curs 279
Curs 23
Trasarea graficelor in planul real
Reproducerea pe suprafata unei componente a unei portiuni din spatiul bidimensional
real este foarte asemanatoare cu realizarea unei fotografii. Din intreg spatiul, aparatul
va surprinde doar cadrul vizat de obiectiv. Imaginea reala din acest cadru va fi scalata
de obiectiv si reprodusa la scara pe suprafata filmului prin impresionarea unor granule
fotosensibile cu care este acoperita aceasta suprafata. Cu cat granulatia materialului
fotosensibil care acopera suprafata filmului este mai fina cu atat rezolutia imaginii
obtinute este mai buna. In cazul suprafetei componentei pixelii sunt echivalentii
granulelor fotosensibile care acopera suprafata filmului. Spatiul real bidimensional se
intinde de la plus la minus infinit. Pentru a trasa un grafic in acest spatiu vom alege un
punct O ca origine a sistemului de coordonate astfel incat orice punct de pe suprafata
plana va putea fi reprezentat in sistemul de coordonate ales printr-o pereche de
numere reale (x,y). Originea sistemului cartezian de coordonate xOy are coordonatele
(0,0). Din intreg planul infinit pe care s-a realizat graficul, nu putem reproduce decat o
portiune finita pe care o incadram intr-o fereastra dreptunghiulara de latime w.width
si inaltime w.height ca in figura 23.1.
img.width
kx =
img.width
img.height
ky =
img.height
A doua problema este sistemul de coordonate x”Oy” al imaginii a carei ordonata este
in oglinda fata de sistemul de coordonate x’Oy’ al ferestrei-cadru. aceasta ar face ca
reprezentarea graficului din fereastra cadru pe suprafata imaginii sa apara si el in
oglinda (rasturnat). Pentru ca acest lucru sa nu se intample, ordonatelor y’ punctelor
proiectate li se va aplica dupa scalare si o translformare corectiva. Formulele de calcul
ale coordonatelor punctelor proiectate pe suprafata imaginii vor fi astfel:
x′′ = x′ ⋅ k x
y ′′ = img.height − y ′ ⋅ k y
A treia problema este ca valorile coordonatelor in fereastra cadru sunt numere reale
iar ale coordonatelor imagine sunt numere intregi. deci coordonatele x” si y” calculate
cu formulele de mai sus vor fi convertite in valori intregi.
In concluzie, odata stabilite dimensiunile ferestrei-cadru si ale imaginii se pot calcula
factorii de scalare pe abscisa si pe ordonata. Cunoscind si coordonatele (x0,y0) in
planul xOy ale coltului din stanga jos al ferestrei-cadru vom deduce formulele de
transformare ale coordonatelor (x,y) unui punct din planul xOy in coordonate imagine
(x”,y”):
x′′ = (int)(( x − x0 ) ⋅ k x )
y ′′ = img.height − (int)(( y − y0 ) ⋅ k y )
g.drawLine(ix1,iy1,ix2,iy2);
}
public void drawOval(double x, double y,
double width, double height)
{
int ix=(int)((x-xo)*kx),
iy=h-(int)((y-yo)*ky),
iw=(int)(width*kx),
ih=(int)(height*ky);
g.drawOval(ix,iy,iw,ih);
}
public void drawRect(double x, double y,
double width, double height)
282 CURS 23
{
int ix=(int)((x-xo)*kx),
iy=h-(int)((y-yo)*ky),
iw=(int)(width*kx),
ih=(int)(height*ky);
g.drawRect(ix,iy,iw,ih);
}
public void drawRoundRect(double x, double y,
double width, double height,
double arcWidth,double arcHeight)
{
int ix=(int)((x-xo)*kx),
iy=h-(int)((y-yo)*ky),
iw=(int)(width*kx),
ih=(int)(height*ky),
iaw=(int)(arcWidth*kx),
iah=(int)(arcHeight*ky);
g.drawRoundRect(ix,iy,iw,ih,iaw,iah);
}
public void drawPolygon(double xPoints[],double yPoints[],int
nPoints)
{
int xP[]=new int[nPoints],
yP[]=new int[nPoints];
for(int i=0;i<nPoints;i++){
xP[i]=(int)((xPoints[i]-xo)*kx);
yP[i]=h-(int)((yPoints[i]-yo)*ky);
}
g.drawPolygon(xP,yP,nPoints);
xP=null;yP=null;
System.gc();
}
public void drawString(String s,double x,double y)
{
int ix=(int)((x-xo)*kx),
iy=h-(int)((y-yo)*ky);
g.drawString(s,ix,iy);
}
public void fill3DRect(double x, double y,
double width, double height,
boolean raised)
{
int ix=(int)((x-xo)*kx),
iy=h-(int)((y-yo)*ky),
iw=(int)(width*kx),
ih=(int)(height*ky);
g.fill3DRect(ix,iy,iw,ih,raised);
}
public void fillArc(double x, double y,
double width, double height,
int startAngle,int arcAngle)
{
int ix=(int)((x-xo)*kx),
iy=h-(int)((y-yo)*ky),
iw=(int)(width*kx),
ih=(int)(height*ky);
g.fillArc(ix,iy,iw,ih,startAngle,arcAngle);
}
public void fillOval(double x, double y,
double width, double height)
{
PROGRAMARE IN JAVA - Note de curs 283
int ix=(int)((x-xo)*kx),
iy=h-(int)((y-yo)*ky),
iw=(int)(width*kx),
ih=(int)(height*ky);
g.fillOval(ix,iy,iw,ih);
}
public void fillRect(double x, double y,
double width, double height)
{
int ix=(int)((x-xo)*kx),
iy=h-(int)((y-yo)*ky),
iw=(int)(width*kx),
ih=(int)(height*ky);
g.fillRect(ix,iy,iw,ih);
}
public void fillRoundRect(double x, double y,
double width, double height,
double arcWidth,double arcHeight)
{
int ix=(int)((x-xo)*kx),
iy=h-(int)((y-yo)*ky),
iw=(int)(width*kx),
ih=(int)(height*ky),
iaw=(int)(arcWidth*kx),
iah=(int)(arcHeight*ky);
g.fillRoundRect(ix,iy,iw,ih,iaw,iah);
}
public void fillPolygon(double xPoints[],double yPoints[],int
nPoints)
{
int xP[]=new int[nPoints],
yP[]=new int[nPoints];
for(int i=0;i<nPoints;i++){
xP[i]=(int)((xPoints[i]-xo)*kx);
yP[i]=h-(int)((yPoints[i]-yo)*ky);
}
g.fillPolygon(xP,yP,nPoints);
xP=null;yP=null;
System.gc();
}
public Rectangle getClipRect(){return g.getClipRect();}
public Color getColor(){return g.getColor();}
public Font getFont(){return g.getFont();}
public FontMetrics getFontMetrics(){return g.getFontMetrics();}
public void setColor(Color c){g.setColor(c);}
public void setFont(Font f){g.setFont(f);}
public void setPaintMode(){g.setPaintMode();}
public void setXORMode(Color
otherColor){g.setXORMode(otherColor);}
public void translate(double x, double y)
{
int ix=(int)((x-xo)*kx),
iy=h-(int)((y-yo)*ky);
g.translate(ix,iy);
}
public String toString(){return g.toString();}
}
Programul urmator exemplifica utilizarea acestei clase pentru generarea desenului din
figura 23.2
284 CURS 23
a1=i*72;
a2=a1+36;
a3=a1-36;
drawStarPoint(rg,0,0,r1*Math.sin(Math.PI*a1/180),
r1*Math.cos(Math.PI*a1/180),
r2*Math.sin(Math.PI*a2/180),
r2*Math.cos(Math.PI*a2/180),10);
drawStarPoint(rg,0,0,r1*Math.sin(Math.PI*a1/180),
r1*Math.cos(Math.PI*a1/180),
r2*Math.sin(Math.PI*a3/180),
r2*Math.cos(Math.PI*a3/180),10);
}
r3=r2*Math.cos(Math.PI/5);
for(int i=0;i<5;i++){
a1=i*72;
a2=a1+108;
a3=a1-108;
drawStarPoint(rg,r1*Math.sin(Math.PI*a1/180),
r1*Math.cos(Math.PI*a1/180),0,0,
r2*Math.sin(Math.PI*a2/180),
r2*Math.cos(Math.PI*a2/180),10);
drawStarPoint(rg,r1*Math.sin(Math.PI*a1/180),
r1*Math.cos(Math.PI*a1/180),0,0,
r2*Math.sin(Math.PI*a3/180),
r2*Math.cos(Math.PI*a3/180),10);
}
for(int i=0;i<5;i++){
a1=i*72;
a2=a1+36;
a3=a2+36;
drawStarPoint(rg,r3*Math.sin(Math.PI*a1/180),
r3*Math.cos(Math.PI*a1/180),
r2*Math.sin(Math.PI*a2/180),
r2*Math.cos(Math.PI*a2/180),
r3*Math.sin(Math.PI*a3/180),
r3*Math.cos(Math.PI*a3/180),10);
}
}
private void drawStarPoint(RealGraphics rg,
double x1,double y1,
double x2,double y2,
double x3,double y3,int n)
{
double k;
for(int i=0;i<=n;i++){
k=(double)i/n;
rg.drawLine(x1+(x2-x1)*k,y1+(y2-y1)*k,
x2+(x3-x2)*k,y2+(y3-y2)*k);
}
}
}
Programul de mai sus creaza un obiect rg din clasa RealGraphics definita de noi
anterior si il foloseste pentru desenarea imagini din figura 23.2. Desenarea se face
intr-un buffer intern al lui rg. Generarea si afisarea desenului se face in metoda paint()
a clasei StarPicture, subclasa a clasei Canvas. Metoda paint() apeleaza metoda
drawStar() transmitandu-i acesteia instrumentul de desenare, rg. Metoda sterge
bufferul imagine a lui rg, seteaza culoarea de desenare neagra si genereaza din figura
apeland de mai multe ori metoda drawStarPoint(), transmitandu-i la randul sau, pe
286 CURS 23
Coltul stelei este format din doua segmente de dreapta d1 si d2. Dreapta d1 uneste
centrul cercului cu un punct de coordonate polare (r1,a1)(aflat pe cercul de raza r1) iar
d2 uneste acest punct de un punct de coordonate polare (r2,a2) )(aflat pe cercul de
raza r2). Amble segmente se impart in acelasi numar n de semente egale, rezultand pe
dreapta d1 punctele echidistante p0, p1, ...pn iar pe dreapta d2 punctele echidistante
q0, q1, ...qn. Punctul p0 se afla in centrul cercului iar pn pe cercul de raza r1. Punctul
q0 este identic cu pn iar qn se afla pe cercul de raza r2. Se unesc perechile de puncte
(pi,qi) prin segmente de dreapta rezultand un colt al stelei desenate de program.
Coordonatele punctelor pi si qi se calculeaza relativ la un sistem de coordonate xOy
carteziene cu originea in centrul cercului (punctul p0 are coordonatele (0,0)) Valorile
coordonatelor sunt valori reale si se calculeaza ca proiectii ale punctelor pe abscisa si
ordonata folosind functiile trigonometrice sin() si cos() implementate de clasa Math.
Trasarea segmentelor de dreapta se face in planul real folosind pentru desenare
obiectul rg din clasa RealGraphics. Acesta va face toate transformarile necesare,
generand desenul in bufferul-imagine. Dupa ce intreg desenul a fost generat, la fel
cum fotograful scoate filmul din aparat, metoda paint() “extrage” din obiectul rg acest
buffer-imagine invocand metoda getImage() a claseiRealGraphics si il deseneaza pe
suprafata componentei starPicture.
drawStarPoint(0,0,r1*Math.sin(Math.PI*a1/180),
288 CURS 23
r1*Math.cos(Math.PI*a1/180),
r2*Math.sin(Math.PI*a3/180),
r2*Math.cos(Math.PI*a3/180),10);
}
r3=r2*Math.cos(Math.PI/5);
for(int i=0;i<5;i++){
a1=i*72-phi;
a2=a1+108;
a3=a1-108;
drawStarPoint(r1*Math.sin(Math.PI*a1/180),
r1*Math.cos(Math.PI*a1/180),0,0,
r2*Math.sin(Math.PI*a2/180),
r2*Math.cos(Math.PI*a2/180),10);
drawStarPoint(r1*Math.sin(Math.PI*a1/180),
r1*Math.cos(Math.PI*a1/180),0,0,
r2*Math.sin(Math.PI*a3/180),
r2*Math.cos(Math.PI*a3/180),10);
}
for(int i=0;i<5;i++){
a1=i*72-phi;
a2=a1+36;
a3=a2+36;
drawStarPoint(r3*Math.sin(Math.PI*a1/180),
r3*Math.cos(Math.PI*a1/180),
r2*Math.sin(Math.PI*a2/180),
r2*Math.cos(Math.PI*a2/180),
r3*Math.sin(Math.PI*a3/180),
r3*Math.cos(Math.PI*a3/180),10);
}
}
private void drawStarPoint(double x1,double y1,
double x2,double y2,
double x3,double y3,int n)
{
double k;
for(int i=0;i<=n;i++){
k=(double)i/n;
rg.drawLine(x1+(x2-x1)*k,y1+(y2-y1)*k,
x2+(x3-x2)*k,y2+(y3-y2)*k);
}
}
public void run(){
for(;;){
if(!ready){
phi+=10;
phi%=360;
drawStar(phi);
ready=true;
}
repaint();
try{
Thread.sleep(100);
}
catch(InterruptedException e){}
}
}
public void update(Graphics g){ paint(g);}
}
PROGRAMARE IN JAVA - Note de curs 289
La receptie, aceasta suma ciclica de control (CRC-ul) este folosita pentru verificarea
integritatii pachetului. Daca CRC-ul calculat la receptie nu coincide cu cel din pachet,
pachetul este retransmis.
Fiecare modul de retea are un numar de serie unic, astfel ca in retea nu pot exista doua
module cu acelasi cod de identificare. Acest numar serveste ca adresa a modulului in
reteaua locala. Toate modulele din retea sunt legate la acelasi cblu astfel incat au
acces la toate pachetele de date care sunt transmise pe cablu.
Interfetele de retea sunt module inteligente care pot fi comandate de un program prin
inscrierea unui cuvant de comanda intr-unul din porturile prin care modulul este
conectat la magistrala de date. Programul poate sa transmita interfetei un bloc de
octeti (date) pentru a fi expediati la destinatie. Inerfata se ocupa de impachetarea si
transmiterea blocului de date, asteptand eliberarea liniei, rezolvand eventualele
coliziuni, etc. In tot acest timp, programul care a solicitat transmiterea datelor este
liber sa se ocupe de altceva. In momentul in care iterfata a reusit sa transmita datele,
ea genereaza o intrerupere a activitatii curente a programului acesta fiind astfel
atentionat ca poate sa solicite transmisia unui nou bloc de date. Pe de alta parte, pe un
alt calculator din reteaua locala (conectat la acelasi cablu de date printr-o interfata de
retea similara) poate fi executat un program care indeplineste functia de destinatar (de
exemplu functia lui poate fi sa afiseze datele receptionate din retea). Acest program
este intrerupt din activitatea curenta de catre interfata de retea in momentul in care
aceasta a receptionat un bloc de date expediat de intrfata de retea a unui alt calculator.
La tratarea intreruperii programul va prelua blocul de date si va reveni la activitatea
curenta. Problema cu acest model simplist de comunicatie este ca programele din
acest exemplu sunt proiectate sa dialogheaza cu un anumit tip de module hardware
comandate printr-un set de coduri de comanda specific, adresand anumite porturi cu o
anumita adresa, si fiind activate de o anumita intrerupere. Daca un astfel de program
va fi executat pe un calculator echipat cu o interfata de retea a unui alt producator,
avand un set de comenzi diferit, evident ca el nu va mai comunica corect cu modulul
si deci nu va functiona corect. Programele trebuiesc proiectate sa fie cat mai
independente de structura hardware a calculatoarelor pe care urmeaza sa fie instalate.
Solutia consta in utilizarea unor module software numite drivere care ofera o interfata
standard in comunicatia dintre programe si modulele de retea. Modulele hardware
sunt livrate de producator insotite de driverele aferente. Pentru a dialoga cu modulul
de retea, programele apeleaza functiile standard de citire/scriere/configurare oferite de
driver. Programatorul nu trebuie sa cunoasca amanunte privind arhitectura hardware a
placii. Implementarea functiilor de catre driver tine insa seama de specificul
modulului, inscriind cuvintele de comanda corespunzatoare si datele la porturile
modulului astfel incat sa realizeze operatia solicitata.
PROGRAMARE IN JAVA - Note de curs 291
Curs 24
Retele de tip Token Ring
In continuare este prezentat un mecanism de comunicatie in retea total diferit de cel
dat ca exemplu in cursul precedent(Ethernet). Am ales pentru aceasta modelul de retea
Token Ring. O astfel de retea este constituita din mai multe calculatoare
interconectate prin legaturi punct-la-punct astfel in cat fiecare calculator este legat de
doua calculatoare vecine cu cite o legatura punct-la-punct rezulta structura inelara
(ring) din figura 24.1.
Din cele 4 calculatoare din aceasta figura are dreptul sa transmita calculatorul care a
receptionat un pachet de date special numit token. Tokenul poate fi deci asemuit cu o
cutie care se transmite din mana in mana, de la un calculator la altul. Daca un
calculator are de transmis altuia un mesaj, si cutia-token este goala(tokenul este liber)
il va depune in cutie si o va transmite mai departe. Cand cutia ajunge la destinatar
acesta isi citeste mesajul si il marcheaza corespunzator. Dupa aceasta transmite cutia
cu mesajul marcat mai departe pana ajunge inapoi la expeditor. Acesta extrage
mesajul din cutie verificand daca este marcat de destinatar, avand astfel confirmarea
de primire. Cutia goala o va transmite mai departe pentru a fi folosita de celalalte
calculatoare din retea pentru transmiterea altor mesaje.
292 CURS 23
Pachetul token are in structura sa un flag numit T. Daca acest bit are valoarea 1
inseamna ca tokenul este liber. Daca este 0 tokenul este ocupat. Daca tokenul este
liber (T=1)si calculatorul nu are de transmis nimic, il transmite urmatorului calculator
din inel. Daca tokenul este ocupat (T=0), chiar daca calculatorul are ceva de transmis,
el va astepta sa primeasca un token liber. Astfel ca se limiteaza sa transmita tokenul
calculatorului vecin care se afla in asteptare, “ascultand” linia pentru a sesiza
inceputul transmisiei tokenului. Daca calculatorul vecin nu este pe receptie, fiind
oprit, el este bypasat hardware de contactul inchis al unui releu care sesizeaza
disparitia tensiunii de alimentare. Astfel tokenul va calculatorului urmator din inel
care asculta linia asteptand inceputul transmisiei unui token. In momentul in care
calculatorul care asculta sesizeaza prezenta semnalului de transmisie pe linie el trece
in starea de receptie, primind si procesind informatia continuta de token. In concluzie,
un calculator dintr-o astfel de retea se poate afla in una din cele patru stari:
¾ transmisie
¾ ascultare
¾ bypasat (oprit)
¾ receptie date
Daca unul din calculatoare (de exemplu A) tocmai a receptionat de la calculatorul
vecin D tokenul liber (T=1). Sa presupunem ca el are de transmis un bloc de date unui
alt calculator (sa zicem calculatorului D). In acest caz, A va seta bitul T in 0 (tokenul
devenind un token ocupat), va inscrie in token adresa sa si adresa destinatarului (de
exemplu calculatorul D), va adauga blocul de date , va calcula si adauga CRC-ul
pachetului si va transmite tokenul astfel construit calculatorului vecin B. Calculatorul
B, aflat in starea de ascultare, va primi tokenul, si va constata ca este ocupat si va
vedea daca nu cumva el este cel care urmeaza sa primeasca date. Constatand ca
destinatarul nu este el, va trimite mai departe tokenul calculatorului vecin. Inainte de
a-l trimite mai departe, ii va calcula CRC-ul si il va compara cu CRC-ul inscris in
token si va seta un flag E (Eroare) in 1 daca cele doua valori nu sunt identice. Sa
presupunem ca acesta (C) este oprit. In acest caz releul de bypass este dezexcitat, si
contactul sau inchis. Semnalul purtator de informatie va trece astfel de calculatorul C
pe linia vecina, ajungand la D. D va receptiona tokenul, il va citi si va constata ca
urmeaza sa primeasca niste date de la A. In aceste conditii el va trece in regim de
receptie in care nu numai va transmite ma departe tokenul dar, in plus va stoca in
memorie pachetul primit pentru a extrage blocul de date transmis de A. Inainte de a
transmite tokenul mai departe va seta un flag corespunzator urmatoarelor situatii:
¾ adresa recunoscuta – flagul A
¾ Pachetul copiat – flagul C
¾ Eroare – flagul E
In final tokenul va ajunge la emitent – calculatorul A. Acesta va examina strea
flagurilor tragand concluziile corespunzatoare:
AC=00 – adresa destinatarului nu a fost recunoscuta de nici o statie si deci datele nu
au fost copiate de destinatar in memorie. Aceasta situatie apare atunci cand
destinatarul este oprit.
AC=10 – adresa a fost recunoscuta dar datele nu au fost copiate in memoria
destinatarului. daca flagul E=1, inseamna ca motivul refuzarii datelor il constitue
altwerarea pachetului (CRC –ul calculat nu coincide cu cel inscris in pachet). Daca
E=0 motivul este necunoscut.
AC=11 Adresa a fost recunoscuta si datele au fost copiate in memoria destinatarului.
Daca flagul E=1 inseamna ca datele au ajuns cu bine la destinatar si au fost copiate in
memorie si au fost alterate ulterior, o alta statie sesizand eroarea.
PROGRAMARE IN JAVA - Note de curs 293
Daca AC=01 inseamna ca o alta statie decat destinatarul a copiat neautorizat datele in
memoria sa.
Este sarcina statiei A care a adaugat blocul de date in token sa le si extraga, elaborand
si transmitand mai departe un token liber pentru a permite si altor statii sa transmita
date. Structura unui pachet token este redata in figura 24.2.
In timpul functionarii, in reteaua Token Ring pot aparea evenimente care perturba
functionarea mecanismului descris mai sus. De exemplu daca una din statiile retelei
este oprita in momentul in care detine tokenul. Ea nu va mai transmite tokenul si deci
celalalte statii din retea vor astepta degeaba sa-l primeasca. O alta situatie de avarie
este oprirea statiei care a transmis un token ocupat de un bloc de date catre o alta
statie din retea si a fost oprita inainte de a apuca sa extraga datele si sa elibereze
tokenul. In aceste conditii tokenul va circula la infinit intre celalalte statii ale retelei
fara a mai fi elibert de niciuna dintre ele. In ambele cazuri comunicatia in retea este
blocata.
Pentru a preantampina aceste fenomene mecanismul de comunicatie in reteaua de tip
Token Ring prevede proceduri de detectare a evenimentelor de mai sus si de
restabilire a functionarii normale a comunicatiei.
Desi aparent toate calculatoarele din retea sunt egale, unele dintre acestea sunt mai
egale decat altele. Astfel, pe langa functiunile descrise mai sus unul din calculatoare,
la initializarea retelei este desemnat sa joace rolul de monitor activ al comunicatiei
(Active Monitor). In aceasta calitate el marcheaza un token ocupat care trece prin
dreptul sau prin setarea unui flag M din structura acestuia in starea 1(expeditorul
blocului din date din token pozitioneaza acest flag in 0 inainte de a trimite tokenul
spre destinatie). Daca monitorul activ va receptiona a doua oara acelasi token ocupat
el isi va putea da seama dupa starea flagului M=1 ca aceasta este deja a doua a
“calatorie” a datelor si deci dintr-un motiv sau altul expeditorul sau nu a reusit sa il
elibereze(fiind probabil oprit). In acest moment, monitorul activ intervine oprind
tokenul “orfan” si construind unul nou, liber pe care il va transmite spre calculatorul
vecin. Pe de alta parte monitorul activ cronometreaza timpul scurs din momentul in
care a expediat un toke. Daca dupa un anumit interval de timp TRT (Token Rotation
Time) considerat ca fiind suficient ca un token sa efectueze o calatorie completa in
inelul retelei (de exemplu 10 microsecunde), acesta nu ajunge din nou la monitor,
acesta va considera ca tokenul s-a pierdut pe traseu fiind “inghitit” de zgomotul retelei
sau in urma deconectarii unei statii inainte ca aceasta sa il transmita mai departe. In
consecinta el genereaza si trimite in retea un nou token care sa-l inlocuiasca pe cel
pierdut. Ce se intampla insa daca calculatorul care indeplineste functia de monitor
activ este oprit? Functia de monitor activ nu ridica cerinte hardware speciale pentru
calculatorul care o indeplineste. Deci oricare din statiile din retea poate prelua aceasta
functie daca calculatorul care o indeplineste la un moment dat este oprit. Astfel sunt
definite in retea calculatoarele care vor inlocui monitorul activ in cazul deconectarii
acestuia. Statiile din aceasta “rezerva de cadre” se numesc monitoare standby
294 CURS 23
¾ va primi un pachet CL_TK cu adresa expeditorului mai mare decat adresa proprie.
Criteriul de promovare in functia de monitor activ este adresa. Va deveni activ
monitorul standbay cu adresa cea mai mare. Deci daca un monitror standby a
receptionat un pachet CL_TK cu adresa expeditorului mai mare decat adresa
proprie, el se va retrage din cursa, existand un candidat mai potrivit.
¾ va primi un pachet de control PURGE emis de monitorul standby care a castigat
concursul constatand ca nu exista in retea un alt monitor standby cu adresa mai
mare si s-a auto-promovat in consecinta monitor activ.
Sa analizam acum cum se modifica problema daca vrem sa interconectam doua retele
locale aflate in cladiri diferite aflate orase diferite. De data aceasta va trebui sa apelam
la serviciile unei retele de comunicatii (cel mai frecvent publice dar sunt cazuri cand
este folosita o retea de comunicatii privata – cazul sistemelor de transfer bancar sau al
sistemelor informatice militare). In acest caz legatura intre retele trebuie facuta printr-
un modem ca in figura 24.4
hostul care detine la un moment dat pachetul, va alege o alta ruta, poate mai lunga, dar
in schimb disponibila. Un calculator host care realizeaza functia de rutare a pachetelor
de date se numeste dupa cum banuiti ruter (router). Ruterele formeaza o retea de
hosturi ca cea din figura 24.5. Ele sunt interconectate prin canale de comunicatie
inchiriate de mare viteza care asigura posibilitatea transferului rapid a unui volum
urias de date (cabluri cu fibre optice, canale satelit, etc).
In cazul din aceasta figura, daca un calculator din reteaua A trebuie sa transmita un
pachet de date unui calculator din reteaua B, hostul A va hotara ca pachetul sa fie
transmis la destinatie pe ruta cea mai scurta, respectiv transmitandu-l hostului B. Daca
insa comunicatia intre hostul A si hostul B nu functioneaza, hostul A va trimite
pachetul pe ruta a doua, spre hostul B. Acesta va ruta pachetul spre C care il va
transmite in reteaua locala C spre calculatorul de destinatie.
Problema care apare la rutarea pachetelor consta in determinarea rutei spre reteaua de
destinatie pe baza adresei destinatarului. Adresa hardware a acestuia specificata in
PROGRAMARE IN JAVA - Note de curs 297
campul MAC (Media Access Control) corespunzator al pachetului este, dupa cum am
mai spus, un numar arbitrar care desi identifica unic calculatorul intr-o retea locala nu
contine informatie care sa permita ruterului sa stabileasca despre care dintre cele 4
posibile este vorba. Solutia este de a organiza calculatoarele din retelele locale
interconectate intr-o retea logica globala, instituind un sistem unitar de adresare a
acestora. Astfel, fiecare calculator va primi o adresa unica in reteaua logica globala
constituita prin interconectarea retelelor locale. Deoarece ruterul “poarta de iesire”
(gateway) din reteaua locala a pachetului il va despacheta, extragind blocul de date
ceea, ce se transmite de la o retea la alta de la ruter la ruter este doar acest blocul de
date. Pentru ca blocul de date sa poata fi rutat el trebuie sa contina adresele logice ale
expeditorului si destinatarului precum si alte informatii cum ar fi lungimea sa in
octeti. In concluzie blocul insusi va avea o structura de genul celei din figura 24.6. Un
astfel de bloc il vom numi in continuare Datagrama.
- Daca bitul 31 este 1 si bitul 30 este 0 este vorba despre o retea medie din clasa
B cu netid- ul reprezentat pe 16 biti. Daca primii doi biti din cei 16 au valoarea
10 desemnand clasa retelei, ceilalti 14 biti vor identifica reteaua. Restul de 16
biti ai adresei vor contine hostid-ul claculatorului. Vom putea astfel
interconecta intr-o retea logica, alaturi de cele maximum 128 de retele mari,
214 = 16.384 de retele medii, a cate 65.536 calculatoare fiecare.
- Daca bitii 31 si 30 au valoarea 11 iar bitul 29 este 0 este vorba despre o retea
mica din clasa C cu netid- ul reprezentat pe 24 de biti. Daca primii trei biti din
cei 24 au valoarea 110 desemnand o retea din clasa C, ceilalti 21 de biti vor
identifica reteaua. Restul de 8 biti ai adresei vor contine hostid-ul
claculatorului. Vom putea astfel interconecta intr-o retea logica, alaturi de
cele maximum 128 de retele mari si cele 16.384 retele medii, inca 221 =
2.097.152 de retele mici, a cate 256 de calculatoare fiecare.
Pe baza unei astfel de adrese ruterul va putea sa identifice reteaua de destinatie a
datagamei. El dispune de o tabela de rutare ale carei intrari contin fiecare cate o ruta.
O ruta asociaza unui identificator netid al unei retele cu ruterul catre care trebuie
trimisa datagrama ca sa ajunga la reteaua de destinatie. Datagrama adresata unui
calculator din reteaua cu identificatorul netid va fi trimisa catre ruterul asociat in
tabela de rutare. Acesta la riandul sau va consulta tabela proprie de rutare, va
determina catre ce ruter trebuie transmisa datagrama in continuare si o va transmite
acestuia. Procesul continua pana cand datagrama va ajunge la ruterul poarta de intrare
in reteaua local careia ii apartine calculatorul destinatar.
Cand datagrama ajunge la ruterul poarta de intrare in reteaua de destinatie, acesta o va
impacheta in formatul specific retelei locale respective si va transmite pachetul spre
calculatorul de destinatie. Cu aceasta inca problema nu este definitiv rezolvata. Faptul
ca am asociat fiecarui calculator din retea o adresa unica nu inseamna ca am raspuns
la intrebarea – ce va inscrie in campurile de adresa hardware (campurile MAC– Media
Acess Control) ale noilor pachete la reimpchetarea blocului de date? Este clar ca in
campul expeditorului, ruterul va inscrie propria sa adresa hardware din reteaua
respectiva. Pentru destinatar insa ruterul va trebui sa determine adresa hardware a
acestuia din reteaua locala pe baza adresei globale din datagrama. In acest scop el
detine o tabela de corespondenta a adreselor in care sunt definite perechile adresa
globala – adresa hardware. Aceasta tabela poate fi definita manual – de exempolu
editata intr-un fisier. La initializarea ruterului, acesta va citi tabela de rezolutie a
adreselor din fisier in memorie si o va accesa ori de cate ori detecteaza un pachet de
date care trebuie transbordat.
In aceste conditii activitatea ruteruluiului se va desfasura astfel:
¾ Ori de cate ori receptioneaza o datagrama, el citeste din aceasta adresa logica a
destinatarului.
¾ Dupa componenta netid a adresei, ruterul dtermina daca destinatarul este in
reteaua locala deservita de el sau daca trebuie sa ruteze datagrama mai departe.
¾ Daca datagrama nu este adresata retelei locale deservite de ruter el va consulta
tbela de rutare, va determina carui ruter trebuie sa o trimita mai departe si o va
transmite acestuia. Daca datagrama este adresata retelei deservite de el, va cauta in
tabela de rezolutie a adresei adresa hardware asociata adresei logice a
destinatarului.
¾ Impacheteaza datagrama conform protocolului retelei deastrvite si o expediaza in
reteaua locala de destinatie pe adresa hardware gasita la pasul anterior. La adresa
hardware a expeditorului va figura adresa interfetei sale cu aceasta retea.
PROGRAMARE IN JAVA - Note de curs 299
Aceasta solutie prezinta un dezavantaj. Ori de cate ori este adaugat un nou calculator
in retea sau se inlocuieste interfata de retea a unui calculator (cu una mai performanta
sau in urma defectarii interfetei vechi) tabelul de rezolutie a adresei trebuie actualizat.
S-a gasit o solutie care elimina acest inconvenient. Ruterul trimite in reteaua locala
deservita de el un pachet de control de un tip special, reprezentand o cerere de
rezolutie de adresa (pachetul este numit ARP request – Address Resolution Protocol
request). Pachetul contine in blocul de date adresa logica a calculatorului a a carui
adresa fizica ruterul doreste sa o afle. In campul de adresa hardware, ruterul trece
adresa hardwre a interfetei sale de retea. Pachetul este adresat tuturor calculatoarelor
din reteaua locala (broadcast). Sunt posibile trei situatii.
1. Calculatorul cu adresa logica specificata in pachet isi recunoaste adresa logica si
raspunde cu un pachet ARP Reply, trimis pe adresa hardware a ruterului (aflata
din campul de adresa hardware a expeditorului din pachetul ARP Request).
2. Calculatorul cu adresa logica specificata in pachet sa fie oprit. In acest caz ruterul
va astepta un interval de timp prestabilit si daca nu primeste in acest interval de
timp raspunsul abandoneaza pachetul.
3. Sa existe doua sau mai multe calculatoare in retea care sa aiba aceiasi adresa
logica. Situatia este ilegala dar este posibila. Ruterul va primi mai multe
raspunsuri ARP Reply.
Ruterul va extrage din campul de adresa expeditor al pachetului ARP Reply primit
adresa hardware cautata si o va inscrie in campul de adresa destinatar al pachetului
generat si il va expedia in reteaua locala.
Analizand mecanismul de comunicatie descris mai sus constatam ca si aceasta se
desfasoara dupa un set de reguli precis care defineste o structura a datagramei, un mod
de adresare in reteaua logica, niste reguli precise de rutare a datagramelor si de
rezolutie a adreselor hardware. Acest set de reguli constitue si el un protocol de
comunicatie. Acest protocol se bazeaza insa pe protocoalele de comunicatie de la
nivelul legaturii de date cum ar fi Ethernet, Token Ring sau PPP (Point to Point
Protocol) care descriu regulile de comunicatie intre interfetele de retea. Protocolul
discutat mai sus descrie structurile si regulile de manipulare a datelor pentru
transmiterea lor in reteaua logica constituita din interconectarea retelelor fizice de
tipuri diferite situate la distanta. Modulele software si programele care implementeaza
acest protocol nu acceseaza direct interfetele de retea ci interactioneaza cu ele prin
intermediul driverelor acestor interfete. Astfel acelasi modul software care
implementeaza acest protocol va putea fi rulat si pe un PC intr-o retea Ethernet si pe
un PC intr-o retea Token Ring, si pe un PC conectat pe o linie telefonica la un ruter
printr-o legatura PPP, deoarece driverele acestor module de retea diferite ca
constructie si principiu de functionare asigura o interfata standardizata cu programele
utilizatoare ascunzand particularitatile hardware specifice fiecarui tip in parte. (Este
situatia similara celei de la automobile unde interfata este aceiasi si la un trabant si la
Lamborghini – un volan, trei pedale si o maneta pentru schimbarea vitezelor. Oricand,
un sofer care a condus toata viata numai automobile Lamborghini, poate pilota un
Trabant si invers. Cu toate acestea hardware-ul de sub capota celor doua masini este
foarte diferit.) Spunem despre acest protocol ca este definit la nivelul retea logica
(Network) si il putem amplasa pe o scara ierarhica ca fiind deasupra protocoalelor de
la nivelul legaturii de date (Data Link). La acest nivel, un program care are de
transmis date unui alt program care ruleaza pe un alt calculator va solicita modulului
software care implementeaza protocolul IP sa transmita mesajul respectiv
specificandu-i doar adresa logica a destinatarului, fara sa se preocupe de pozitia
acestuia si de tipul retelei careia ii apartine. Destinatarul poate sa fie calculatorul pe
300 CURS 23
destinatie. Acesta va sesiza ca datagrama este adresata retelei sale locale si va face
rezolutia adresei hardware dupa care va impacheta datagrama conform protocolului de
la nivelul legaturii de date expediind-o spre calculatorul B. Aici pachetul de date
receptionat va fi despachetat extragandu-se datagrama care se paseaza modulului
software receptor care implementeaza protocolul IP. Datagrama este despachetata
extragandu-se din ea informatia utila continand mesajul trimis de pe calculatorul A.
In tot acest timp aplicatiile B1 si B2 asteapta mesaje de la baietii din A. Datele
receptionate trebuie sa ajunga la aplicatia B1 daca sunt expediate de A1 sau la B2
daca expeditorul este A2.
Din experienta acumulata deja din descrierea protocoalelor probabil ca intuiti ca
blocul de date care a fost receptionat si extras din datagrama trebuie sa fie la randul
sau un “plic” avand inscriptionate pe el adresele expeditorului (A1 sau A2) si al
destinatarului (B1 sau B2) si continand in interiorul sau mesajul transmis. Partial aveti
dreptate dar apare aici o mare problema – aplicatiile, spre deosebire de calculatoarele
din retea nu pot fi caracterizate printr-o “adresa” fixa. Nu se poate prevedea la scrierea
unor rutine de serviciu ale sistemului de operare cum sunt modulele care
implementeaza protocoalele de comunicatie in retea ce aplicatii vor apela la serviciile
lor pentru transmiterea sau receptia unor mesaje. Pentru a iesi din acest impas s-a
optat pentru solutia urmatoare. Canalul de date IP va fi interfatat cu un modul
software prevazut cu un numar de “prize” numite porturi fiecare avand asociat un
numar intreg ca identificator. Aplicatiile pot sa se conecteze la un astfel de port pentru
a transmite sau receptiona date din canalul de comunicatie IP. Aceste porturi se
comporta ca niste streamuri de intrare la receptia si de iesire la transmisia de date. O
aplicatie poate inscrie octeti in portul respectiv sau extrage octeti din port ( numai
daca sunt disponibili, in caz contrar trebuind sa astepte sosirea pe canalul IP a unei noi
datagrame adresate portului). La scrierea de octeti intr-un port, interfata nu ii
expediaza imediat ci ii cumuleaza intr-un buffer de date. In momentul in care buffer-
ul s-a umplut, ea il va goli, transmitand continutul lui incapsulat intr-un pachet in care
este specificat numarul portului expeditor si numarul portului destinatar. Acest pachet
este pasat canalului IP care il va incapsula la randul lui intr-o datagrama si il va
expedia pe canalul IP la destinatie. Aici pacetul va fi extras din datagrama si pasat
interfetei care va extrage din pachet datele si le va directa spre bufferul portul cu
numarul specificat pe pachet la care asteapta aplicatia destinatar. Aceasta va citi din
buffer octetii mesajului din port.
Daca mesajul este mai lung decat capacitatea bufferului, el va fi trimis la destinatie
sub forma mai multor pachete, fiecare pachet continand numai o secventa din mesaj.
Lungimea secventei este data de capacitatea bufferului portului. Datorita rutarii
(datagramele continand secvente ale mesajului pot ajunge la destinatie pe rute diferite
si deci cu intarzieri diferite)ordinea de sosire a pachetelor poate sa fie diferita. Astfel
datagrama continand prima secventa, desi a fost expediata prima, poate sa ajunga la
destinatie a treia, a doua sa ajunga prima iar a treia sa ajunga a doua mergand pe o
ruta mai scurta decat prima. In aceste conditii pachetele continand secventele de mesaj
trebuie sa contina un camp care sa specifice numarul secventei din mesaj continute.
Pe baza acestui numar secventele sunt “reasamblate” de interfata in ordinea corecta.
Astfel a secventa 2, desi sosita prima nu va fi transmisa spre port pana cand nu este
citita de aplicatia destinatar secventa 1 sosita a treia.
Pe langa multiplexarea porturilor si reasamblarea pachetelor de date, interfata cu
canalul IP mai implementeaza un mecanism care asigura o fiabilitate ridicata a
canalului virtual de comunicatie intre doua porturi. Astfel, interfata care expediaza un
pachet, il completeaza la sfarsit cu un camp CRC calculat pe baza continutaului
302 CURS 23
Curs 25
Reprezentarea zecimala a adreselor IP
Am vazut ca o adresa IP este un numar intreg pe 4 octeti memorat de calculator in
forma binara, de exemplu 10001101.01010101.00101100.00000001. Daca ar fi
reprezentat in zecimal ar fi dificil sa determinam pe baza valorii 2371169281 carei
clase apartine reteaua locala, si pornind de la aceasta sa determinam identificatorul
retelei sau a calculatorului. Desi s-ar fi putut reprezenta adresa in hex 8D552C01 s-a
optat pentru reprezentarea zecimala a valorii fiecarui octet, aceste valori fiind separate
prin puncte: 141.85.44.1. Stiind adresele IP ale calculatoarelor din retelele mari din
clasa A au bitul cel mai semnificativ 0 si deci adresele lor vor incepe cu un numar
cuprins intre 1 ( primul octet al adresei este 00000001) si 126 (primul octet al adresei
este 0111110) valorile 0 si 127 fiind rezervate, avand o semnificatie speciala.
Adresele IP ale calculatoarelor din retelele medii din clasa B incep cu bitii 10 si in
consecinta primul numar zecimal al adresei va fi cuprins intre 128 (10000000) si
191(10111111). Adresele IP ale calculatoarelor din retelele mici din clasa C incep cu
bitii 110 si in consecinta primul numar zecimal al adresei va fi cuprins intre
192(11000000) si 223(110111111). Astfel, avand adresa IP 141.85.44.1 vom trage
imediat concluzia ca reteaua careia ii apartine calculatorul cu aceasta adresa de IP
este o retea de dimensiune medie din clasa B. Deoarece identificatorul unei retele din
clasa B este reprezentat pe doi octeti, vom putea obtine acest identificator: 141.85.
Rezulta de aici ca identificatorul calculatorului cu aceasta adresa de retea este 44.1.
Sa luam un alt exemplu in care adresa IP a calculatorului este 195.65.4.2. Dupa
primul numar al adresei deducem ca este vorba de o retea din clasa C, avand
identificatorul reprezentat pe 3 octeti: 195.65.4. Rezulta ca identificatorul
calculatorului este 2.
Protocolul UDP
Exista aplicatii care nu necesita robustetea (fiabilitatea) protocolului TCP. Pentru
aceste aplicatii este suficient un protocol la nivelul transport care sa asigure
comunicatia intre aplicatii rulate pe calculatoare diferite intr-o retea IP si un
mecanism minimal de validare a integritatii mesajelor receptionate. Protocolul UDP
(User Datagram Protocol) satisface aceste cerinte. Spre deosebire de protocolul TCP
bazat pe stabilirea unui canal virtual de comunicatie intre aplicatii, protocolul UDP
opereaza intr-un mod orientat pe transmiterea datagramelor. El nu incearca sa
stabileasca o legatura intre aplicatii ci doar incapsuleaza datele intr-un pachet UDP
avand structura din figura 25.1 si il paseaza nivelului IP. La nivelul IP pachetul UDP
este ambalat intr-o datagrama IP si transmis nivelului inferior pentru expeditie.
Aceste caracteristici fac ca protocolul UDP sa fie mai simplu si mai eficient decat
protocolul TCP cu singurul dezavantaj ca este mai putin sigur. El este recomandat
pentru aplicatii care lucreaza in cooperare, pe principiu cerere-raspuns. Este bine ca
atat cererea cat si raspunsul trebuie sa incapa intr-o singura datagrama. Este sarcina
aplicatiei sa repete cererea daca nu a primit raspunsul intr-un timp dat sau sa renunte
daca nu primeste raspuns la mai multe cereri succesive.
De asemenea protocolul UDP este util in cazul aplicatiilor care difuzeaza un mesaj in
retea simultan la mai multe calculatoare. De exemplu daca o aplicatie are de trimis
acelasi mesaj la 1000 de aplicatii ruland pe alte calculatoare, daca va folosi
protocolulea va trebui sa stabileasca 1000 de conexiuni prin canale virtuale. Dupa
terminarea difuzarii mesajului prin transmiterea lui pe fiecare conexiune in parte,
aplicatia va trebui sa inchida aceste 100 de conexiuni. Costul in resurse ale sistemului
pentru crearea si mentinerea conexiunilor pe perioada difuzarii mesajelor este mare.
Folosind protocolul UDP aplicatia poate expedia cele 1000 de mesaje cu costuri mult
mai mici si intr-un timp mult mai scurt. De asemenea cele 1000 de calculatoare de
destinatie pot fi grupate intr-o retea de tip D iar mesajul poate fi transmis o singura
data, in regim de multicasting, pe adresa de IP a grupului. In pus, o aplicatie poate
plasa o singura datagrama cu adresa de IP de forma 255.255.255.255 cerand
distribuirea acesteia in regim de broadcasting in reteaua locala sau de forma {netid -
1} pentru difuzarea catre toate calculatoarele din reteaua netid.
127.0.0.1 localhost
195.65.4.1 big_crash.hacker.ro
PROGRAMARE IN JAVA - Note de curs 307
Socluri (Sockets)
In cadrul nucleului unui sistem de operare este contine de module API – Application
Programming Interface care ofera accesul programatorului la o serie de servicii ale
sistemului de operare. Unul din aceste module asigura servicii de comunicatie in retea
TCP/IP. Ca standard pentru serviciile oferite de acest modul a fost adoptat cel
prevazut in S.O. BSD Unix. S.O. Windows ofera astfel servicii de comunicatie in
retea TCP/IP printr-un modul - biblioteca cu legare dinamica DLL (Dinamic Link
Library) cunoscut sub numele WinSock API (fisierul WINSOCK.DLL in cazul
versiunilor Windows pe 16 bit si WSOCK32.DLL pentru versiunile pe 32 de bit).
Aplicatiile acceseaza serviciile de retea oferite de sistem (trimitere si primire de date
intr-o retea TCP/IP) prin intermediul interfetei oferite de WinSock API. Aplicatia
dialogheaza cu modulul WinSock API apeland procedurile oferite de acesta iar
310 CURS 25
Functiile reprezentate in acest graf sunt proceduri ale WinSock API care pot fi apelate
de catre aplicatiile client-server pentru realizarea comunicatiei intr-o retea IP.
312 CURS 25
La creerea unui obiect din clasa Socket se deschide un canal virtual de comunicatie
duplex intre aplicatia client de pe calculatorul local si aplicatia server de pe
calculatorul corespondent. Clasa Socket defineste metode care permit scrierea/citirea
in/din canalul virtual de comunicatie asociat. Canalul virtual de comunicatie este
constituit din doua streamuri – unul de intrare si altul de iesire. Metodele
getInputStream() si getOutputStream() intorc referintele la aceste streamuri.
La terminarea comunicatiei cu aplicatia client trebuiesc inchise streamurile de I/O si
apoi soclul apeland metoda close().
Programul din exemplul urmator se conecteaza la o aplicatie server de e-mail pe
portul TCP dedicat 25 si ii transmite un mesaj pe adresa user@nexus.home.ro .
Dialogul intre aplicatia noastra client si aplicatia server este reprezentat in figura 25.7
si se desfasoara prin transmiterea de catre aplicatia client a unor comenzi aplicatiei
server si receptionarea unor raspunsuri de confirmare de la aceasta.
Regulile de dialog, setul de comenzi, codurile si mesajele de raspuns sunt definite de
protocolul SMTP (Simple Mail Transfer Protocol).
Curs 26
Aplicatii server
O aplicatie server trebuie sa poata accepta cereri de servire de la mai multi clienti
simultan. Pentru aceasta ea trebuie sa stabileasca cate un canal virtual de comunicatie
TCP ori de cate ori un client solicita conectarea. Asa cum am vazut, o aplicatie server
care ofera o anumita categorie de servicii este contactata de aplicatia client pe un
numar de port unic convenit dinainte. De exemplu am vazut in cursul anterior ca
serverul de e-mail (SMTP) accepta conexiuni pe portul 25. Aplicatiile client care au
de expediat mesaje e-mail solicita conectarea la server fara sa stie daca acesta este
deja in legatura cu o alta aplicatie client sau nu. Chiar daca este deja conectat printr-
un canal virtual cu o aplicatie client, serverul trebuie sa detecteze cererea de conectare
si sa creeze un al doilea canal virtual de comunicatie cu cel de al doilea client care a
solicitat conectarea la serviciile sale. Stim ca un canal virtual de comunicatie are doua
capete si are la fiecare capat cate un port. Odata creat un astfel de canal, porturile de la
capetele sale devin indisponibile pentru crearea unui alt canal. Se pune intrebarea
care este mecanismul prin care mai multe aplicatii client se pot conecta la acelasi port
cu un numar fixat al aplicatiei server? Acest mecanism a fost deja prezentat cand am
prezentat in cursul trecut modulul WinSock API graful din figura 25.4 explicand
functionarea lui.
Sa analizam exemplul din figura 26.1.
Avem o aplicatie server X si mai multe aplicatii client Y,Z,W, etc. Aplicatia server X
creaza un soclu pe portul cu numar fixat x (de exemplu 25 - cunoscut de toate
aplicatiile client SMTP) si "asculta" la acest port asteptand cereri de conectare.
In momentul in care un client Y solicita prin portul sau y conectarea pe portul x al
serverului, acesta receptioneaza cererea si poate accepta stabilirea legaturii. In acest
caz, el va crea un al doilea soclu cu un numar arbitrar x1 al portului si va stabili
legatura cu clientul pe acest soclu. In acest fel, soclul cu numarul de port x ramane
316 CURS 26
liber si serverul se poate intoarce la "ascultarea" lui pentru stabilirea altor legaturi cu
alti clienti. Canalul virtual de comunicatie stabilit intre clientul Y si serverul X are
deci la capatul clientului portul cu numarul y iar la capatul dinspre server portul x1.
Daca un alt client Z va cere prin portul sau z conectarea la portul x al serverului,
acesta va stabili legatura pe un port cu numar arbitrar x2 diferit de x1 alocat anterior.
Serverul va continua sa accepte conexiuni la cerere pentru clientii U,V,W,... alocand
la fiecare solicitare un nou port xk si creind canale virtuale de comunicatie cu fiecare
client in parte u-x3, v-x4, w-x5, etc.(vezi figura 26.1).
Soclul pe care "asculta" serverul este implementat in Java printr-o clasa speciala
ServerSocket. Ea permite programatorului sa faca asocieze un soclu de "ascultare" cu
un port. Odata creat acest soclu, apeland metoda accept() serverul este blocat, intrand
intr-o stare de asteptare a unei cereri de conectare din partea unui client. Serverul
revine din executia acestei metode numai atunci cand primeste o cerere de conectare
din partea unui client pe portul soclului "ascultat". In momentul in care primeste o
cerere de conectare, metoda accept() creeaza un canal virtual de comunicatie cu
clientul care a solicitat conectarea pe un port disponibil alocat de S.O. (de exemplu de
WinSockAPI) si returneaza soclul (obiect din clasa Socket) de la capatul dinspre
server al canalului. Odata revenit din metoda accept() serverul poate sa dialogheze cu
clientul prin intermediul soclului returnat de metoda. Clientii ale caror cereri de
conectare sosesc in timp ce serverul este ocupat cu servirea unui client se aseaza intr-o
coada de asteptare. Cand serverul termina servirea clientului curent si executa din nou
metoda accept() va prelua cererea primul client din coada si va crea un canal virtual
de comunicatie cu acesta.
Clasa ServerSocket are trei constructori:
¾ public ServerSocket(int port) throws IOException – creaza un soclu server care
asculta la portul specificat. Soclul admite o coada cu maximum 50 de clienti.
¾ public ServerSocket(int port, int count) throws IOException - creaza un soclu
server care asculta la portul specificat. Soclul admite o coada cu maximum count
clienti.
¾ public ServerSocket(int port, int count, InetAddress localAddr) throws
IOException – Acest constructor se foloseste in cazul aplicatiilor server care
ruleaza pe un calculator cu mai multe interfete de retea fiecare cu o adresa IP
proprie. El creaza un soclu server care asculta la portul specificat pe adresa IP
locala a uneia din interfete. Soclul admite o coada cu maximum count clienti.
In programul urmator este prezentata o aplicatie server care accepta conexiuni pe
portul 2222. In momentul in care conexiunea este stabilita, serverul solicita aplicatiei
client o parola. Dupa receptionarea parolei, o compara cu stringul "sesam deschide-
te". Daca cele doua stringuri sunt egale afiseaza mesaj de bun venit "Bine ai venit
Alibaba! Exprima-ti o dorinta (QUIT, EXIT,CLOSE):" asteptand apoi receptia unui
sir de caractere din cele trei. Daca sirul receptionat este unul din cele trei va afisa
mesajul "Ascult si ma supun stapane!" dupa care daca comanda este "QUIT" sau
"EXIT" inchide conexiunea iar daca este "CLOSE" inchide in plus si soclul server si
se termina. In caz contrar va relua receptionarea unei comenzi. Daca parola
receptionata nu este corecta reia inca de doua ori citirea parolei dupa care inchide
conexiunea. Pentru a testa aplicatia server ne vom folosi de aplicatia de sistem telnet.
Aceasta emuleaza un terminal VT100 si permite crearea unei conexiuni la un server a
carui adresa si port se configureaza (figura 26.2). Terminalul telnet trimite catre server
liniile de caractere tastate de utilizator (afisate in ecou si in fereastra aplicatiei) si
afiseaza liniile de text primite de la aplicatia server. In figura 26.1 mai sunt redate si
mesajele afisate de aplicatia server la consola si cele afisate in fereastra telnet.
PROGRAMARE IN JAVA - Note de curs 317
import java.io.*;
import java.net.*;
class Gate{
public static void main(String[] args){
try{
String psw,cmd;
Socket s;
DataInputStream in;
PrintStream out;
ServerSocket ss=new ServerSocket(2222);
close:{
for(;;){
System.out.println("Waiting for connection request...");
s=ss.accept();
System.out.println("New connection...");
in=new DataInputStream(s.getInputStream());
out=new PrintStream(s.getOutputStream());
disconect:{
int k=0;
do{
out.print("password:");
psw=in.readLine();
if(psw.equals("sesam deschide-te")){
System.out.println("Alibaba access granted...");
out.print("Bine ai venit Alibaba!"+
" Vre-o dorinta (QUIT/EXIT/CLOSE)?");
for(;;){
318 CURS 26
cmd=in.readLine();
System.out.println(cmd);
if(cmd.equals("QUIT")||cmd.equals("EXIT")){
out.println("Ascult si ma supun stapine!\r");
break disconect;
}
if(cmd.equals("CLOSE")) break close;
out.print(cmd+"!!!!!(QUIT/EXIT/CLOSE)?");
}
}
out.println("Wrong password...\r");
k++;
}while(k<3);
}
out.println("Connection closed...\r");
in.close();
out.close();
s.close();
System.out.println("Connection closed...");
}
}
in.close();
out.close();
s.close();
ss.close();
System.out.println("Connection closed...");
System.out.println("Server shut down...");
}
catch(Exception e){ System.err.println(e.toString());}
}
}
thread care se ocupa cu expedierea mesajelor din buffer spre toti ceilalti clienti
conectati.
Pe de alta parte si clientii trebuie sa fie multithread deoarece un singur thread nu poate
gestiona singur si trimiterea si receptia mesajelor. Daca de exemplu el se afla blocat in
executia metodei readLine() asteptand sosirea unui mesaj in bufferul streamului de
intrare, el nu va putea sa execute in acelasi timp metoda de citire a mesajului introdus
de utilizator de la tastatura. De aceea si aplicatia client trebuie sa aibe doua threaduri
– unul care sa se ocupe cu receptia mesajelor sosite de la server si altul care sa
citeasca o linie de text de la tastatura si sa o expedieze sub forma de mesaj serverului
pentru difuzare. In figura 26.3 este reprezentata schematic cooperarea dintre
threadurile aplicatiilor client si server pentru realizarea acestei scheme de
comunicatie.
Aplicatia server, dupa cum se vede din schema, este bazata pe activitatea a trei clase
de threaduri, s, Tx si Rx. Threadul s este cel care gestioneaza cererile de conectare. El
"asculta" la soclul server x pe portul cu numarul 2222 (ales arbitrar). In momentul in
care primeste o cerere de conectare el va crea un "canal" de comunicatie duplex prin
soclul intors de metoda accept() a soclului server x, si-l va "implanta" in primul "slot"
i liber al tabloului de conexiuni. La creerea canalului de comunicatie duplex se va crea
si un thred din clasa Rx care va incepe receptia mesajelor sosite prin soclul canalului
din slotul i. Dupa aceste operatii, threadul s va reincepe "ascultarea" portului 2222
asteptand o noua cerere de conectare.
320 CURS 26
Coada are doua variabile head (cap) si tail (coada), referinte la obiecte de tip element
al listei. Variabila head refera primul element al listei iar tail pe ultimul. Initial coada
este vida si deci cele doua elemente contin valorile null. Elementele listei sunt obiecte
care au doua campuri, unul continand informatia utila iar celalalt o "legatura"-
referinta la urmatorul element din lista. Daca elementul este ultimul element din lista,
aceasta "legatura" este null.
La adaugarea elementelor noi in coada se executa metoda pop(obj) care daca lista este
vida inscrie referinta obj in ambele variabile head si tail. Daca lista nu este vida,
ultimul element din lista se leaga la elementul nou adaugat prin inscrierea in campul
sau de legatura a referintei la acesta. Referinta la elementul adaugat se inscrie si in
variabila tail, devenind astfel ultimul element din lista. La extragerea elementelor din
lista se apeleaza metoda push() care "dezleaga" primul element al listei inscriind in
variabila head referinta continuta de campul de legatura al primului elementului din
lista (referit de head). Metoda pop() intoarce referinta la obiectul extras din coada.
Daca elementul extras este ultimul ( are in campul de legatura referinta null) se inscrie
null si in variabila tail, lista devenind din nou vida. Metodele push() si pop() sunt
sincronizate, lista fiind accesata de mai multe threaduri executate concurent
(Threadurile Rx inscriu pachete in lista iar Tx extrage pachete din lista). In plus, daca
lista este vida threadul Tx este blocat la apelul metodei pop() pina la adaugarea de
catre un thread Rx a unui pachet nou in lista.
PROGRAMARE IN JAVA - Note de curs 321
btnStat=false;
btn.setLabel("Start");
}catch(IOException ex){}
}else{
try{
s=new Socket("141.85.44.10",PORT);
in=new DataInputStream(s.getInputStream());
out=new DataOutputStream(s.getOutputStream());
btnStat=true;
btn.setLabel("Stop");
}catch(IOException ex){}
}
else{
try{
msg=send.getText();
send.setText("");
out.write(Packet.newPacket(msg));
}
catch(IOException ex){}
}
return true;
}
private void quit(){
System.out.println("Client shut down...");
hide();dispose();System.exit(0);
}
public static void main(String[] args){
String msg="";
Client client=new Client();
for(;;)
if(client.btnStat){
try{
if((msg=client.in.readLine())!=null)
client.recv.setText(msg);
else{
client.in.close();
client.out.close();
client.s.close();
client.s=null;
System.gc();
client.btnStat=false;
client.btn.setLabel("Start");
client.recv.setText("");
}
}
catch(IOException ex){
try{
client.in.close();
client.out.close();
client.s.close();
client.s=null;
System.gc();
client.btnStat=false;
client.btn.setLabel("Start");
client.recv.setText("");
}catch(IOException e){}
}
}
}
}
326 CURS 26
In aceasta aplicatie, threadul receptor este chiar thredul principal iar cel emitator este
threadul AWT. Ferestrele afisate de trei aplicatii client si mesajele afisate de server
sunt prezentate in figura 25.6.
Curs 28
Structura documentelor HTML
Fie fisierul text3.txt copiat in text4.html:
<IMG SRC=javamatic.gif>
<P>Javamatic</P>
<P>Internet/Intranet Software</P>
<P></P>
<P><B>SERVICES</B></P>
<P><I>- IT Professional Services</P>
<P>- System Integration</P>
<P>- Software applications</P>
<P>- Consulting hw/sw</P>
<P>- Networking</P>
<P>- Custom Internet/Intranet
client-server applications</P>
<P><B>Call Now!</B></I>
Incarcand cu browserul MSIE fisierul text4.html acesta va afisa in fereastra sa
documentul executand procesarile specificate de marcajele prevazute in textul
acestuia (figura 28.1).
scrierea programului nu au fost alese arbitrar, fiind chiar cele din setul de marcaje
HTML.
Documentul HTML original, numit si sursa, a carui reprezentare apare in figura 27.9
este redat in listingul 28.1.
Listing 28.1 - Cybertyse.html
<HTML>
<HEAD>
<TITLE>Cybertise's Virtual Gallery</TITLE>
</HEAD>
<BODY BGCOLOR=#FFFFFF TEXT=#000000 LINK=#0000FF VLINK=#008000
ALINK=#FFFF00>
<CENTER><img src="images/gallery2.jpg" alt="Virtual
Gallery"></CENTER>
<br>
<center><tt><h2>Welcome to the Cybertise Virtual
Gallery.</h2></tt></center>
<center>This is a collection of design and artwork done by Cybertise
artists for clients, fun, or friends.</center>
<center>All of the graphics on this Web site were done by Cybertise
(except for obvious icons), and there are more to come in the
future.</center>
<br>
<center><tt>Just click on a designer's name and away you
go...</tt></center>
<br>
<center>
<table border=3>
<tr>
<td>
<a href="gallery/index.html"><img border=0 src="images/williams.gif"
alt="Ken Williams"></a>
</td>
<td>
<a href="gallery/index2.html"><img border=0 src="images/wing.jpg"
alt="Chris Wing"></a>
</td>
</tr>
</table>
</center>
</body>
</html>
Se vede ca este vorba de un simplu fisier text. Ceea ce face ca acest document sa fie
afisat intr-o forma mult mai atragatoare sunt codurile de marcaj sau tag-urile ( acele
portiuni din text scrise intre perechi de paranteze unghiulare < > ) care controleaza
formatarea si aspectul reprezentarii sale in fereastra navigatorului. Acestea reprezinta
de fapt niste instructiuni, comenzi date browserului privind structura documentului si
modul de afisare al textului incadrat de aceste marcaje.
Cea mai eficienta cale de a invata cum se construieste o pagina de Web este de a crea
una. Pentru aceasta folositi un program de editare de text, de exemplu Notepad din
accesoriile Windows. Se editeaza un fisier cu numele Prima.html continand textul din
listingul 28.2.
Pentru a personaliza intr-un fel exemplul nostru, vom presupune ca fiind conectat la
Internet prin sistemul subspatial de comunicatii galactice la mare distanta, capitanul
navei spatiale Entreprise, Jean-Luc Picard, s-a hotarat sa creeze pe WWW o pagina
Web a acesteia pentru ca toti klingonii, romulanii si ferengii dar si celelalte fiinte la
fel de inteligente si conectate la Internet din Galaxie sa aibe informatii despre
misiunea echipajului sau .
PROGRAMARE IN JAVA - Note de curs 343
Marcaje de structura
Sa analizam un pic documentul HTML creat de noi. Observam ca el, pe langa textul
ce va fi afisat de browser, contine diferite marcaje care, in acest caz, definesc structura
documentului. Astfel indiferent de continutul documentului HTMl, acesta trebuie sa
inceapa cu marcajul <HTML> si sa se termine cu marcajul complementar </HTML>:
<HTML>
… Continutul documentului …
344 CURS 28
</HTML>
<HTML>
<HEAD>
… Antetul documentului …
</HEAD>
<BODY>
… Corpul documentului …
</BODY>
</HTML>
Antetul documentului HTML poate sa contina la rindul sau mai multe elemente dar
obligatoriu trebuie sa fie prevazut numai titlul acestuia - un text incadrat de marcajele
pereche <TITLE> si </TITLE>. Acest text este afisat de majoritatea browserelor in
bara de titlu a ferestrei aplicatiei ca in figura 2.1.
<HTML>
<HEAD>
<TITLE>
… Titlul documentului …
<\TITLE>
</HEAD>
<BODY>
… Corpul documentului …
</BODY>
</HTML>
Corpul documentului poate sa contina la rindul sau mai multe elemente pe care le
vom analiza in continuare.
Crearea subtitlurilor
Limbajul HTML prevede marcaje pentru a crea subtitluri de diverse dimensiuni. Sunt
posibile sase niveluri de subtitluri de la cele mai mari marcate la inceput cu <H1>
pana la cele mai mici marcate cu <H6>.
Ca si marcajele discutate inainte marcajele de subtitluri se termina cu marcajele
pereche de incheiere </H1> pana la </H6>. Pe majoritatea sistemelor subtitlurile de
nivel 6 au literele foarte mici, fiind dificil de citit.
In figura 28.3 se poate observa efectul introducerii subtitlurilor in document.
Documentul sursa HTML corespunzator paginii afisate in figura 28.3 este cel din
Listingul 28.3. In acest listing se observa ca marcajele de sfirsit de subtitlu au fost
PROGRAMARE IN JAVA - Note de curs 345
aliniate prin introducere de caractere TAB. Prin aceasta nu este influentat cu nimic
modul de afisare al paginii dar se imbunatateste lizibilitatea programului, marcajele de
sfarsit sunt mai usor de vazut in listing.
Paragrafe
Analizand cu atentie listingul si imaginea afisata, vom constata ca browserul a ignorat
la afisarea textului despartirea pe randuri facuta de capitanul Picard si a facut propria
sa despartire in functie de latimea ferestrei de afisare. Astfel in listing apare textul
privind nava Entreprise pe 8 randuri iar browserul o afiseaza numai pe 5:
textul sursa:
The USS Entreprise, NCC -1701-D, is a Galaxy class starship
346 CURS 28
built at the Utopia Plannitia Fleet Yards above Mars. It was
comissioned in 2363, and is currently under command of
Captain Jean-Luc Picard.
This latest starship is Starfleet‘s flagship and has already
distingueshed in an impressive number of significant missions
of exploration, as well as in several crucial incidents
defending the security of the Federation.
Daca fereastra de afisare ar fi fost mai lata, numarul de randuri al textului ar fi fost
mai mic iar daca ar fi fost mai ingusta , mai mare.
Pentru a putea desparti textele din corpul documentului HTML in paragrafe, fortand
browserul sa afiseze textul paragrafului de la inceput de rand, se folosesc marcajele
<P> si </P> care delimiteaza continutul unui paragraf.
Listingul 28.4 si Fig.28.4 arata modul de introducere si efectul acestor marcaje intr-un
document HTML.
Delimitarea randurilor
PROGRAMARE IN JAVA - Note de curs 347
Acum textul arata ceva mai bine. Problema este ca spatiul dintre cele doua fragmente
de text este cam mare. Daca vreti ca browserul sa intrerupa linia de text afisata dar sa
nu lase spatiu liber pana la linia urmatoare, se foloseste marcajul <BR> (Break line).
Marcajul <BR> nu are un marcaj pereche </BR> - este un marcaj vid. Utilizarea
acestui marcaj si rezultatul obtinut se vede in listingul 28.5 respectiv in Fig.28.5.
Comentarii
Pe langa informatia ce urmeaza a fi afisata, in textul documentului HTML este util sa
fie introduse comentarii, adica blocuri de text care nu sunt afisate de browser dar pot
servi pentru o mai buna documentare a documentului. Pot fi astfel inscrise informatii
privind autorul documentului, data si natura modificarilor aduse in timp acestuia, alte
explicatii privind documentul respectiv. Marcajul folosit pentru a indica inceputul
comentariului este <!--. Sfirsitul comentariului se indica cu marcajul - ->. In listingul
28.7 se poate vedea utilizarea acestor marcaje.
Imagini
Pentru a face pagina de Web mai atractiva ea poate fi prevazuta cu imagini. O
imagine este continuta de un fisier intr-un format dat. Majoritatea browserelor
recunosc formatele GIF si JPEG. Pentru a adauga o imagine paginii de Web se
foloseste marcajul <IMG> avand sintaxa <IMG SRC=”fisier_imagine”>. In listingul
28.8 se vede cum a folosit capitanul Picard acestui marcaj pentru a adauga la pagina
Web a navei sale imaginea continuta de fisierul StarTrek.gif . In acest caz fisierul se
gaseste in acelasi director cu fisierul Images.html, marcajul imagine fiind
…
<IMG SRC="StarTrek.gif">
…
Daca fisierul imagine s-ar fi gasit intr-un director diferit de cel al fisierului sursa,
marcajul ar fi trebuit sa contina si calea pana la fisier. Astfel daca fisierul se gaseste in
directorul C:\IMAGES\GIF_URI, marcajul ar fi fost:
…
<IMG SRC="C:\Images\Gif_uri\StarTrek.gif">
…
Rezultatul obtinut la afisarea acestui document de catre browser este redat in figura
28.8:
Listing 28.8 - Images.html
<HTML>
<!-- This is the new and iprouved HTML page of USS Entreprise 1701-D
PROGRAMARE IN JAVA - Note de curs 351
Tabele
Desi aduce un plus de culoare in pagina Web, totusi imaginea este prost amplasata in
raport cu textul. Ramine un spatiu nefolosit in dreapta imaginii.
Lucrurile pot fi ameliorate folosind marcajele pentru tabele. HTML foloseste
marcajele <TABLE> si </TABLE> pentru a defini un tabel.
In cadrul blocului de text incadrat de aceste doua marcaje se definesc optional antetul
si celulele tabelului cu ajutorul marcajelor <TH>,</TH> pentru antet (Table Heading),
<TR> ,</TR> pentru delimitarea unui rand al tabelului (Table Row) si <TD>,</TD>
pentru celulele unui rand (Table Data). Pentru ca tabelul sa aibe margini vizibile
marcajul de inceput de tabel va fi <TABLE BORDER>
De exemplu pentru a introduce intr-o pagina Web tabelul 28.1, documentul HTML
corespunzator va fi cel din listingul 28.9
Tabel 28.1
Warp speed table
Speed miles per Times Earth to Across solar Between two Across Across To nearby
hour speed of moon system nearby stars one Federa- Galaxy
light sector tion
250.000 7,440,000,000 5 light years 20 light 10,000 2,000,000
miles miles years light light years
years
Warp 263 Bilion 382 0.00342634 2 minutes 5 days 18 days 25 years 5,096
factor 6 seconds years
Rezultatul incarcarii acestui document de catre browser este cel din figura 28.9
In celulele tabelului poat fi introduse blocuri de text sau imagini. Folosind acest
mecanism poate fi aliniata o imagini cu un bloc de text in celule de tabel alaturate,
procedeu la care a apelat si capitanul navei Entreprise in listingul 28.10 pentru a alinia
imaginea cu mesajul de salut, rezultand pagina Web din figura 28.10.
Curs 29
Ancore si legaturi
Spatiul de afisare in fereastra browserului este limitat. Daca informatia continuta in
document este mare, regasirea unui anume subiect prin cautare in toata pagina este
dificila. Este ca si cand am avea o carte fara cuprins pe care am dori sa o deschidem la
un anumit capitol. Pentru aceasta ar trebui sa o rasfoim cautand titlul capitolului dorit.
HTML permite organizarea documentului prin definirea a hiperlegaturilor, puse in
evidenta in textul paginii printr-o culoare distincta. Utilizatorul poate “naviga” prin
document clicaind cu mouseul pe hiperlegaturi, browserul deruland automat pagina
pana cind informatia asociata acestora apare in fereastra. In plus culoarea
hiperlegaturii se schimba pentru ca utilizatorul sa stie ca a “vizitat-o” deja cel putin
odata.
Hiperlegaturile se bazeaza pe marcajul HTML tip ancora <A>,</A>(Anchor) avand
sintaxa:
…
<A HREF =”#zona”>Text_evidentiat</A>
…
Aceasta este o trimitere catre o zona din document marcata la randul sau cu o ancora
avand sintaxa:
…
<A NAME=”zona”>
…
Fig.29.1a
Fig.29.1b
Fig.29.1c
PROGRAMARE IN JAVA - Note de curs 359
…
<A HREF =”document_tinta”>Text_evidentiat</A>
…
…
<A HREF =”document_tinta#zona”>Text_evidentiat</A>
…
…
<A NAME =”zona”>Text_evidentiat</A>
…
<H3>SPECIFICATIONS</H3><HR>
<A NAME="PROP"><H4>PROPULSION</H4>
<P>Sustainable cruise velocity of Warp Factor 9.2. Ability to
maintain speeds of up to Warp 9.6 for periods of up to twelve hours.
Fifth-phase dilithium controlled matter/antimatter reactor primary
power.Sustainable field output to exceed 1,650 cochranes, peak
transitional surge reserve to exceed 4,225% of nominal output (170 ns
phase). Warp driver coils efficiency to meet or exceed 88% at speeds
up to Warp 7.0. Minimum efficiency of 52% to be maintained through
Warp 9.1. Life cycle of all primary coil elements to meet or exceed
1,200,000 cochrane-hours between neutron purge refurbishment.
Secondary coil elements to meet or exceed 2,000,000 cochrane-hours
between neutron purge refurbishment. warp field geometry to
incorporate modified 55¡ Z-axis compression characteristics on
forward warp lobe for increased peak transitional efficiency. Warp
nacelle center-lines to conform to 2.56:1 ratio of separation to
maximum field strength. Secondary (impulse) propulsion system to
provide sublight velocities up to and including 0.92 lightspeed (c).
Engine systems of choice to include but are not limited to at least
two YPS 8063 fusion drive motors. All units to be equipped with
subspace driver accelerators, field output not less than 180
millicochranes at 1.02 x 10¦K. Reactor modules to be field-
replaceable. Independent impulse propulsion system of choice for
primary hull to include but not be limited to YPS 8055 fusion drive
motors.</P>
<A HREF="Index.HTML">Go to home page</A><HR>
<A NAME="MISS"> <H4>MISSION</H4>
<P> Ability to operate independent of starbase refurbishment for
extended periods. Independent exploration mode capability of seven
Standard years at nominal Warp 6 velocity for docked configuration.
Ability to execute deep- space exploration missions including
charting and mapping, first cultural contact scenarios, and full
biologic and ecologic studies. </P>
<P>Space allocation for mission-specific facilities: Habitable area
to include 800,000 m® for mission-adaptable facilities including
living quarters for mission-specific attached personnel.</P>
<P>Ability to support a wide range of mission-related ongoing
research and other projects (including sufficient habitable volume
and power generation for facilities and operations) without impact
on primary mission operations.
<P>Full spectrum EM, optical, subspace flux, gravimetric, particle,
and quark population analysis sensor capability. Multimode neutrino
interferometry instrumentation. Wide-band life sciences analysis
capability pursuant to Starfleet life contact policy directive. Two-
meter diameter gamma ray telescope. Upgradable experiment and sensor
array design. Ability to support both on-board and probe-mounted
science instrumentation.</P>
<P>Support facilities for auxiliary spacecraft and instrumented
probes needed for short-range operations to include at least two
independent launch, resupply, and repair bays.</P>
<A HREF="Index.HTML">Go to home page</A><HR>
<A NAME="ENVC"> <H4> ENVIRONMENT/CREW</H4>
<P>Environmental systems to conform to Starfleet Regulatory Agency
(SFRA)- standard 102.19 for Class M compatible oxygen-breathing
personnel. All life- critical systems to be triply redundant. Life
support modules to be replaceable at major starbase layover to
permit vehiclewide adaptation to Class H, K, or L environmental
conditions.</P>
<P>Ability to support up to 5,000 non-crew personnel for mission-
related operations.</P>
<P>Facilities to support Class M environmental range in all
individual living quarters, provisions for 10% of quarters to
support Class H, K, and L environmental conditions. Additional 2% of
living quarters volume to be equipped for Class N and N(2)
environmental adaptation.</P>
PROGRAMARE IN JAVA - Note de curs 363
Fig.29.4a
364 CURS 29
Fig.29.4b
In listingul 29.5 se vede cum a adaugat Picard referirea la aceasta resursa in
Index.HTML iar in figura 29.4a si b rezultatul incarcarii acestei resurse de catre
navigator.
Listing 29.5 - Index.html
<HTML>
<HEAD>
<TITLE>USS Entreprise - Home Page</TITLE>
</HEAD>
<BODY>
<TABLE>
<TR>
<TD> <IMG SRC="StarTrek.gif"></TD>
<TD> <H3><B><I>
WELCOME TO GALAXY CLASS STARSHIP,USS Entreprise 1701-D,
TO EXPLORE STRANGE, NEW WORLDS, TO SEEK OUT
NEW LIFE, AND NEW CIVILISATIONS,TO BOLDLY GO
WHERE NO ONE HAS GONE BEFORE.</I></B><H3></TD></TR>
</TABLE><HR>
<H5><B><I>Stardate 48030.4581</I></B><H5><HR>
The <B>USS Entreprise, NCC -1701-D</B>, is a Galaxy class starship
built at the <B><I>Utopia Plannitia Fleet Yards</I></> above Mars. It
was
comissioned in 2363, and is currently under command of Captain Jean-
Luc Picard.<BR>
This latest starship is <B>Starfleet</B>‘s flagship and has already
distingueshed in an impressive number of significant missions of
exploration, as well as in several crucial incidents defending the
security of the <B>Federation</B>.<HR>
<A HREF="http://www.Fed.guv/DataBank.HTML">FEDERATION DATABANK
</A><BR>
<A HREF="MissionObj.HTML"> MISSION OBJECTIVES</A><BR>
<A HREF="Specs.HTML">SPECIFICATIONS</A><BR>
<A HREF=" Specs.HTML #PROP"> PROPULSION</A><BR>
<A HREF=" Specs.HTML #MISS"> MISSION</A><BR>
<A HREF=" Specs.HTML #ENVC"> ENVIRONMENT/CREW </A><BR>
<A HREF=" Specs.HTML #TACT"> TACTICAL </A><BR>
<A HREF=" Specs.HTML #DSGN"> DESIGN LIFE </A><HR>
</BODY>
</HTML>
Liste
Se observa ca in documentul Index.HTML apare o insiruire de legaturi la alte resurse.
Limbajul HTML permite gruparea a mai multor blocuri de text in liste formatate. Si in
cazul documentului nostru, este rational din punct de vedere structural sa grupam
PROGRAMARE IN JAVA - Note de curs 365
legaturile din pagina Web intr-o astfel de lista. HTML asigura mai multe formate
diferite pentru liste:
• neordonate (marcate cu buline)
• ordonate (numerotate secvential)
• de tip director (cu elementele aliniate pe orizontala)
• de tip meniu (simple fara buline sau numerotare)
Listele neordonate se definesc cu marcajele <UL></UL>, cele ordonate cu marcajele
<OL></OL>, cele director delimitate cu <DIR>,</DIR> iar cele meniu cu
<MENU>,</MENU>. Elementele listei se delimiteaza cu marcajele <LI>,</LI>.
Fig.29.5a
Fig.29.5b
Elementele listei pot sa fie tot liste rezultand astfel liste incuibate. Variantele de
combinatii utile sunt doua:
• liste incuibate neordonate - o lista neordonata ale carei elemente sunt tot liste
neordonate.
• liste incuibate mixte ordonate/neordonate - o lista ordonata ale carei elemente sunt
liste neordonate.
In listingul 29.7 si in figura 29.7 este prezentata utilizarea unei liste mixte.
In cadrul sau chiar locul textului acentuat dintr-o legatura, poate fi prevazuta o
imagine care poate fi selectata cu mouse-ul. De exemplu primului element al meniului
poate sa -i fie adaugata imaginea grafica a comunicatoarelor folosite de echipajul
navei pentru a sugera comunicatia la distanta cu computerul Federatiei (listingul 29.8
si figura 29.8).
Frame
HTML permite (in cazul browserelor care accepta acest lucru) divizarea unei ferestre
in sub-ferestre numite frame (cadru). Divizarea se poate face fie pe verticala, pe
coloane, fie pe orizontala, pe randuri. Sub-ferestrele se pot si ele diviza la randul lor
fiecare pe coloane sau randuri, etc. In fiecare cadru astfel creat poate fi incarcat cate
un document HTML distinct.
Un document cu frame-uri se aseamana foarte mult cu un document HTML obisnuit
cu deosebirea ca dupa sectiunea HEAD, in loc de sectiunea BODY din documentele
HTML obisnuite, aparte sectiunea FRAMESET. Aceasta sectiune este delimitata de
perechea de marcaje <FRAMESET>,</FRAMESET> care contine definirea sub-
ferestrelor sau frame-urilor. Deci structura unui document HTML cu frame-uri este
urmatoarea:
<HTML>
<HEAD> </HEAD>
<FRAMESET>
… definirea frame-urilor …
</FRAMESET>
</HTML>
370 CURS 29
ROWS="lista_de_inaltimi_ale_randurilor"
COLS="lista_de_latimi_ale_coloanelor"
SRC=”url”
PROGRAMARE IN JAVA - Note de curs 371
unde url specifica locatia resursei ce va fi afisata in frame. Frame-urile fara atributul
SRC sunt afisate fara continut.
• Atributul NAME este folosit pentru a asocia un nume frame-ului astfel ca acesta
sa
poata fi accesat de legaturi din alte documente (de regula incarcate in alte frame-uri
ale aceleiasi ferestre). Sintaxa atributului NAME este:
NAME="nume_fereastra"
MARGINWIDTH="n"
unde n este latimea in pixeli. Aceasta valoare nu poate fi mai mica decat 1. Atributul
este optional.
Atributul MARGINHEIGHT e asemanator ca sintaxa cu atributul MARGINWIDTH
descris mai sus, cu exceptia faptului ca el stbileste marginile superioara si inferioara
ale frame-ului in timp ce MARGINWIDTH stabileste dimensiunea marginilor
laterale.
SCROLLING="yes|no|auto"
SCROLLING este setat pe auto, browserul va afgisa bara de defilare numai daca este
cazul. Atributul SCROLLING este optional., valoarea implicita fiind auto.
• Atributul NORESIZE nu primeste valoare. Daca este specificat, browserul nu ii va
permite utilizatorului sa redimensioneze frameul “tragand” cu mouse-ul de marginea
acestuia. Daca frame-ul adiacent are atributul NORESIZE specificat atunci nici
marginea framului vecin nu poate fi deplasata. Atributul este optional. Daca nu este
specificat, implicit frame-ul este redimensionabil.
Documentul HTML care a generat aceste trei frame-uri este cel din listingul 29.9
<NOFRAMES>
<H2> Aceasta pagina se vede mai bine cu browserele
Netscape sau InternetExplorer <H2>
</NOFRAMES>
</HTML>
A doua coloana are o dimensiune scalata relativ si este prevazuta cu o bara de defilare
daca este cazul. In el se incarca documentul din fisierul Info.HTML din listingul
29.13
Listing 29.13 – Info.HTML
<HTML>
<HEAD>
<TITLE> USS Entreprise - Home Page introduction</TITLE>
</HEAD>
<BODY>
<H5><B><I>Stardate 48030.4581</I></B><H5><HR>
The <B>USS Entreprise, NCC -1701-D</B>, is a Galaxy class starship
built at the <B><I>Utopia Plannitia Fleet Yards</I></> above Mars. It
was
comissioned in 2363, and is currently under command of Captain Jean-
Luc Picard.<BR>
This latest starship is <B>Starfleet</B>'s flagship and has already
distingueshed in an impressive number of significant missions of
exploration, as well as in several crucial incidents defending the
security of the <B>Federation</B>.<HR>
</BODY>
</HTML>
In figura 29.10 este prezentata pagina Web afisata de browser la incarcarea
documentului MainPage.HTML. Daca browserul nu implementeaza framuri, pagina
PROGRAMARE IN JAVA - Note de curs 375
afisata este identica cu cea din figura 29.8, textul din sectiunea NOFRAMES fiind
identic cu cel din sectiunea BODY al fisierului ImageLink.HTML din listingul 2.16.
Selectand din lista afisata optiunea FEDERATION DATABANK browserul va crea o
fereastra noua (TARGET=”_blank”) in care va incarca fisierul afisand aceeasi pagina
Web ca cea din figura 2.14b.
Selectand optiunea SPECIFICATIONS in continutul frame-ului index va fi inlocuit cu
cel specificat de fisierul Index2.HTML din listingul 2.22, incarcat in acest frame.
Imaginea afisata va fi cea din figura 2.20.
Formulare HTML
Formularele permit colectarea datelor de la utilizator ceea ofera posibilitatea de a
asigura documentelor HTML un feedback, datele preluate putand fi transmise unor
programe de pe calculatorul server HTTP pentru prelucrari diverse ( generare de
comenzi, actualizarea unor baze de date, evaluare/testare de cunostinte, etc.).
Marcajele folosite pentru realizarea formularelor sunt:
studiile, casete de validare (check box) folosite pentru specificarea limbilor straine
cunoscute, casete de editare pe un rand folosite pentru specificarea vechimii sau pe
mai multe randuri folosite pentru specificarea altor limbi straine in afara celor
explicitate in formular. De asemenea formularul contine doua butoane inscriptionate
“Submit” “si Reset”. Actionarea butonului "Submit” are ca efect expedierea datelor
inscrise in formular iar apasarea butonului “Reset” duce la stergerea informatiilor
introduse si reluarea editarii formularului.
• marcajele <FORM>,</FORM>
Aceste marcaje delimiteaza textul de definire al formularului de colectare de la
utilizator a datelor. Atributele marcajului <FORM> specifica unde, cum si sub ce
forma sa fie expediate datele colectate.
¾ · atributul ACTION este un URL care specifica locatia unde va fi expediata
informatia inscrisa in formular pentru a fi prelucrata si se va obtine raspunsul.
daca atributul ACTION este omis, atunci implicit se considera URL-ul
documentului care contine formularul.
¾ · atributul METHOD selecteaza protocolul folosit la transmiterea informatiei. In
cazul in care atributul ACTION specifica un URL HTTP, protocoalele de
transmitere a datelor din formular pot fi GET sau POST. In cazul in care atributul
METHOD nu este specificat, protocolul implicit este GET.
Daca METHOD=”GET” atunci datele codificate sub forma unui sir de
interogare (query string)se adauga la URL -ul specificat de ACTION. De
exemplu daca ACTION=”http://www.host.org/procesare.pl”, atunci la apasarea
butonului “Submit”, browserul va acesa locatia
http://www.host.org/procesare.plsir_de_interogare
“procesare.pl” este numele unui program aflat pe calculatorul server HTTP care
va prelucra sirul de interogare primit ca parametru. Serverul HTTP separa
locatia programului de sirul de interogare, lanseaza programul “procesare.pl” si
ii transmite sirul de interogare ca parametru.
Daca METHOD=”POST”, sirul de interogare este expediat de browser catre
serverul HTTP intr-un bloc de date separat de URL-ul care contine cererea de
prelucrare.
Sirul de interogare contine informatia introdusa de utilizator in formular, codata
cu ajutorul unor caractere speciale de separare. aceste caractere speciale sunt
caracterul Spatiu, Procent(%), semnul intrebarii (?), ampersand (&), egal (=) si
plus(+). Revenind la exemplu nostru se vede ca formularul este de fapt o
grupare de “controale”. Fiecare control are asociat la definire un nume. Dupa
completarea formularului, fiecarui control ii este atribuita o “valoare” constind
din textul introdus de utilizator in cazul casetelor de editare sau in textul setat la
definirea controlului in document in cazul casetelor de validare si abutoanelor
interblocate. Codificarea informatiilor inscrise in formular se face astfel:
1. datele se grupeaza in perechi nume=valoare. In cazul casetelor de validare si a
butoanelor interblocate se iau in considerare numai controalele validate de
utilizator. Spatiile din textul valorii sunt inlocuite cu semnul +. O serie de
caractere care nu au voie sa apara in sirul de interogare fiind rezervate se
inlocuesc cu valoarea lor codata in HEX si prefixata de semnul Procent(%)
conform tabelului 29.1.
2. perechile nume=valoare se separa inter ele prin caracterul &.
3. sirul astfel obtinut se prefixeaza cu semnul ?.
Tabel 29.1
PROGRAMARE IN JAVA - Note de curs 379
Astfel in cazul exemplului din figura 29.13, browserul va transmite sirul de interogare
cu cererea de acces:
http://www.host.org/sample.cgi?nume=BigCrash&studii=superioare&vechim
e=20&limbi=en&limbi=fr&limbi=ru&altele=C%0D%0AC%2B%2B%0D%0AJava%0D%0A
Pascal%0D%0ABasic&adresa=Starbase+3%2C+Level+23
<SELECT NAME="aroma">
<OPTION>Vanilie</OPTION>
<OPTION>Capsuni</OPTION>
<OPTION>Rom</OPTION>
<OPTION>Piersica</OPTION>
</SELECT>
Curs 30
JavaScript
Sub MS DOS daca vrem sa automatizam o serie de operatii vom edita un fisier de
comenzi cu extensia .BAT (de la Batch file) Comenzile din fisier vor fi citite
interpretate si executate pe rand, una cate una, de catre interpretorul de comenzi
command.com In listingul 30.1 este prezentat un exemplu al unui astfel de fisier de
comenzi care afiseaza un meniu, si in functie de optiunea utilizatorului lanseaza in
executie un program sau altul.
Listing 30.1 - start.bat
@echo off
cls
echo.
echo A Microsoft Editor
echo B Microsoft Anti-Virus
echo C Microsoft Backup
echo.
choice /c:abc Selectati o optiune
if errorlevel 3 goto MSBAckup
if errorlevel 2 goto MSAv
if errorlevel 1 goto Edit
:Edit
edit
goto End
:MSAv
msav
goto End
:MSBackup
msbackup
goto End
:End
Un script este deci o succesiune de instructiuni date unui program, pe care acesta le
interpreteaza si le executa. Din acest punct de vedere si documentele HTML sunt niste
script-uri, marcajele fiind niste comenzi date browserului cum sa afiseze blocul de
text pe care il delimiteaza sau ce alta resursa sa incarce ca raspuns la selectarea de
catre utilizator a unei hiperlegaturi. Asa cum am vazut, posibilitatile oferite de HTML
sunt limitate. Pentru a le imbunatati functionalitatea, Compania Netscape
Comunications si-a inzestrat browserele cu un interpretor al unor comenzi mai
complexe decat cele HTML. Aceste comenzi se prezinta sub forma unor instructiuni
scrise intr-un limbaj botezat de Netscape LiveScript in prima sa versiune iar mai tarziu
JavaScript. Instructiunile scrise in acest limbaj sunt grupate in cadrul unui document
HTML intre perechea de marcaje <SCRIPT>,</SCRIPT> constituind un asa numit
script.Atunci cand este cazul, browserul interpreteaza si executa una cate una
instructiunile scriptului delimitat de aceste marcaje. Nu toate browserele au
implementat interpretorul de script-uri JavaScript, mai precis numai Netscape
Navigator produs Netscape Comunications de si Internet Explorer produs de
Microsoft si integrat in sistemul de operare Windows 95 prevad acest mecanism. De
exemplu chiar browserul HotJava, produs de SUN Microsystems- compania care a
elaborat limbajul Java, nu interpreteaza scripturile JavaScript. Totusi, statisticile arata
ca 90% din utilizatori folosesc pe calculatoarele lor fie Netscape Navigator fie
Internet Explorer. Limbajul JavaScript este destinat scrierii unor aplicatii simple,
fiind destinat celor care doresc sa adauge paginilor lor de Web elemente interactive
fara a fi programatori avansati. Din punctul nostru de vedere, in contextul acestei
carti, JavaScript va fi o punte ce va va netezi trecerea de la HTML la programarea in
Java.
Marcajul <SCRIPT>
Intr-un document HTML, un script Java se incadreaza intre marcajele
<SCRIPT>,</SCRIPT> avand sintaxa:
<SCRIPT LANGUAGE=”JavaScript”>
… codul JavaScript …
</SCRIPT>
Fisierul script referit in marcaj prin URL contine codul scriptuli Java ce va fi incarcat
de browser si executat. Acest fisier are extensia .JS ca in exemplul urmator:
<SCRIPT SRC=”http://www.host.com/program.js”>
…
</SCRIPT>
PROGRAMARE IN JAVA - Note de curs 385
In listingul 30.2 este prezentat un prim exemplu de introducere a unui script Java intr-
un document HTML iar in figura 30.1a si 30.1b rezultatul incarcarii si executiei
acestuia de catre browser.
Se observa ca desi documentul HTML a fost incarcat (titlul sau apare in bara de titlu a
ferestrei si scriptul Java se executa )el nu a fost afisat pana cind nu s-a terminat
executia scriptului.
<HTML>
<HEAD>
<TITLE> Pagina Web cu functii JavaScript</TITLE>
<SCRIPT LANGUAGE=”JavaScript”>
function func1(a,b,c){
… instructiuni JavaScript ale functiei func1 …
}
function func2(x,y,z){
… instructiuni JavaScript ale functiei func2 …
}
………………………………………………..
function funcN(u,v,w){
… instructiuni JavaScript ale functiei funcN …
}
</SCRIPT>
</HEAD>
<BODY>
… corpul documentului HTML …
</BODY>
• Functiiile trebuiesc definite inainte sa fie apelate. Functiile sunt sectiuni de cod
JavaScript care realizeaza la executie o anumita prelucrare a datelor transmise ca
argument (a,b,c in cazul functiei func1 sau x,y,z, in cazul lui func2 sau u,v,w in
cazul lui funcN) ca si cand ar fi un fel de mini-programe. Ulterior, in alte sectiuni
ale codului JavaScript din document, atunci cand este nevoie de o astfel de
procesare a unor date, se apeleaza la serviciile functiei corespunzatoare. Apelul
functiei se face prin numele acesteia transmitandu-I-se si argumente concrete pe
care ea sa le prelucreze(de exemplu func2(3,5,7) este un apel la functia func2. In
urma apelului, func2 incepe sa fie executata de browser prelucrand argumentele x,
y, si z ale caror valori sunt initializate cu valorile specificate la apelul functiei,
respectiv x=3, y=5 si y=7) Conditia ca acest mecanism sa functioneze este ca
definitia functiei sa se gaseasca in documentul HTML inainte de sectiunea in care
se produce apelul ei ca in exemplul din listimngul 30.3. Rezultatul executiei
acestui script este redat in figura 30.2.
function bunVenit(unde){
document.write(“<H1>Bine ati venit”+unde+”!<H1>”);
}//-->
</SCRIPT>
</HEAD>
<BODY><HR>
<SCRIPT LANGUAGE=”JavaScript”>
<!—Ascunde scriptul de browserele non-Java
bunVenit(“in Java Land”);// Apelul functiei cu argumentul actual
//-->
</SCRIPT><HR>
</BODY>
• Tot codul JavaScript, inclusiv definitiile functiilor exista si sunt vizibile numai in
cadrul paginii in care sunt prevazute. Daca vrem sa folosim acelasi cod si in alte
pagini, trebuie sa il transcriem in toate paginile.
• Scripturile Java este bine sa fie incadrate intre marcajele HTML de comentarii
<!—si -- > pentru ca sa nu fie afisate ca simplu text de browserele non-JavaScript
ca in cazul documentului din listingul 30.4
Listing 30.4 – BunVenit.HTML
<HTML>
<HEAD>
<TITLE> Bun venit </TITLE>
<SCRIPT LANGUAGE=”JavaScript”>
function bunVenit(unde){
document.write(“<H1>Bine ati venit”+unde+”!<H1>”);
}
</SCRIPT>
</HEAD>
<BODY><HR>
<SCRIPT LANGUAGE=”JavaScript”>
bunVenit(“la noi”);
</SCRIPT>
<HR>
</BODY>
Obiecte
In viata de zi cu zi suntem obisnuiti sa avem de a face cu diferite obiecte, unele avand
o structura simpla, altele destul de complexe, cum ar fi diferite echipamente
electronice, masini, avioane, etc. sa luam ca obiect de referinta un televizor. Acest
obiect cand este in functiune, are o serie de caracteristici, o sa le numim proprietati,
cum ar fi starea (pornit/standby), luminozitatea (0..100%), contrastul (0..100%),
saturatia de culoare (0..100%), volum sonor(0..100%), ton sonor (0..100%), 40 de
canale avand fiecare asociata o frecventa de receptie. Setarea, acestor proprietati nu
PROGRAMARE IN JAVA - Note de curs 389
se face umbland cu surubelnita si letconul in maruntaiele sale (desi acest lucru este
posibil, nu este recomandabil). Pentru aceasta televizorul nostru este prevazut cu
telecomanda sau cu butoane de reglaj. Actionand asupra tastelor telecomenzii sau
asupra butoanelor de reglaj, putem activa niste functii interne ale televizorului care
actioneaza asupra proprietatilor modificandu-le la valorile dorite. Avind la dispozitie
acest mecanism, utilizatorul nu trebuie sa cunoasca schema de principiu si de montaj a
televizorului si sa fie un priceput electronist pentru a-l putea folosi. Iata un alt
exemplu – un joc electronic ca cel din figura 30.5.
Jocul este de fapt o cutie despre continutul careia nu stim nimic, o asa numita cutie
neagra prevazuta cu o interfata cu butoane de comanda. Aceasta cutie conectata la un
televizor, genereaza o imagine reprezentand pe ecran niste obiecte virtuale, de
exemplu doua nave cosmice, avand si ele proprietati ca si obiectele reale:
coordonatele fiecarei nave, cantitatea de combustibil in rezervoarele acestora, munitia
disponibila, gradul de distrugere, directia si viteza de deplasare, etc.
Proprietatile acestor obiecte virtuale se modifica actionand asupra comenzilor:
accelereaza, franeaza, schimba directia, trage un proiectil, realimenteaza cu
combustibil, etc. Apasarea tastelor de comanda determina cutia neagra sa initieze
actiuni al caror rezultat consta in modificarea proprietatilor obiectelor virtuale
reprezentate pe ecran.
Datorita imaginatiei cu care este inzestrat, omul interpreteaza imaginea de pe ecran a
navei spatiale ca pe un obiect asemanator celor din lumea reala – doar ca acest obiect
evolueaza (isi modifica proprietatile) in spatele ecranului, intr-o lume virtuala.
Aceasta nu il impiedica pe utilizator sa foloseasca comenzile pentru a manevra
obiectul in fel si chip, ore intregi, ca si cand ar fi o jucarie reala, palpabila. Este drept
ca acest obiect nu este intrutotul virtual fiind conectat la lumea reala printr-o interfata
reala, pe care utilizatorul o tine strins si o butoneaza cu frenezie.
390 CURS 30
In figura 30.6 este reprezentata schematic aceasta legatura intre obiectul virtual si
lumea reala prin intermediul interfetei.
Evolutia pe ecran a avionului virtual este rezultatul afisarii imaginii sale in urma
modificarii proprietatilor obiectului software (cum ar fi coordonatele ecran) prin
executarea unei actiuni comandate prin interfata de catre un modul exterior. Actiunile
de care este capabil obiectul se numesc metode asa cum datele sale interne se numesc
proprietati. In programarea obiect stricta, proprietatile obiectului nu pot fi modificate
direct din exterior (se spune ca sunt proprietati private ale obiectului). Modificarea lor
din exteriorul obiectului este permisa numai indirect, prin apel la metodele publice
ale obiectului (pot exista si metode private ce pot fi solicitate numai de alte metode ale
obiectului - publice sau private – dar nu pot fi apelate din exterior).
Fereastra Browserului
Ferestrele afisate de browserele HTML sunt obiecte avand proprietati si metode
publice. Fereastra afisata de browser poate fi comandat prin instructiuni JavaScript
incluse in documentul HTML incarcat in sensul modificarii proprietatilor si apelarii
metodelor oferite de obiectul fereastra. In codul JavaScript, obiectul fereastra este
desemnata prin numele window. Sa analizam intai care sunt proprietatile obiectului
window.
• Proprietatea status
In partea de jos a ferestrei se afla bara de stare in care browserul afiseaza diferite
mesaje care de regula il informeaza pe utilizator asupra desfasurarii procesului de
incarcare a documentului HTML solicitat (vezi figura 30.10). Un astfel de mesaj este
de exemplu Document Done inscris de browser in bara de stare pentru a semnala ca a
terminat de incarcat si afisat o pagina Web.
Ca proprietar al ferestrei in care este afisat documentul in care este incorporat, codul
JavaScript are dreptul sa-si afiseze propriile
PROGRAMARE IN JAVA - Note de curs 393
mesaje in bara de stare. Pentru aceasta nu are decat sa seteze proprietatea status a
obiectului window astfel ca aceasta sa contina sirul de caractere al mesajului. Sintaxa
instructiunii care realizeaza aceasta operatie este:
Din fiecare fereastra la randul sau se pot deschide alte ferestre, etc. Proprietatea
opener a unei ferestre date contine o referinta la fereastra parinte a acesteia. Asa cum
vom vedea mai departe o fereastra noua poate fi deschisa si prin program apeland
metoda open. Folosind informatia din proprietatea opener, un script java executat de
aceasta noua fereastra poate accesa proprietatile si metodele ferestrei parinte. De
exemplu un script java dintr-o fereastra copil poate afisa un mesaj in bara de titlu a
ferestrei parinte cu instructiunea :
• Proprietatatile frames
exemplu daca fereastra este divizata in dou frame-uri, primul este referit de frames[0]
si al doilea de frames[1]. Numarul de frame-uri al ferestrei se poate obtine din
proprietatea frames.length care contine indexul in tablou al ultimului frame(in cazul
exemplului nostru frames.length are valoarea 1). Frame-urile sunt si ele obiecte cu
proprietatile si metodele lor. Prin intermediul referintei la frame aceste proprietati si
metode pot fi accesate de un script Java. Accesul la o proprietate sau o metoda a
frame-ului prin referinta din tabloul frames se face cu apelul:
Window.frames[I].proprietate_sau_metoda_obiect_frame
• Obiectul self
Referinta self este un sinonim al referintei window prin care un script Java se poate
referi la obiectul fereastra in cadrul careia se executa. Astfel apelurile
self.status(“Hallo!”) si window.status(“Hallo!”) vor sunt echivalente.
• Metoda alert( )
Apelul la aceasta metoda a obiectului fereastra determina afisarea unei ferestere de
mesaj de avertizare, prevazuta cu un buton OK. Metoda afiseaza textul transmis ca
argument si asteapta ca utilizatorul sa dea un clic pe buton confirmand ca a citit
mesajul.
Astfel in urma executiei instructiunii JavaScript:
• Metoda confirm( )
Aceasta metoda afiseaza un alt tip de fereastra de mesaj prevazuta cu doua butoane,
OK si Reset-. Metoda afiseaza in aceasta fereastra textul mesajului si asteapta
apasarea unuia dintre butoane. Daca se apasa Butonul OK, metoda intoarce valoarea
booleana true (adevarat) iar daca se apasa pe Reset- atunci intoarce valoarea booleana
false (fals). Apelul metodei se face cu:
• Metoda prompt ( )
Aceasta metoda afiseaza o fereastra prevazuta cu un camp de editare in care
utilizatorul poate tasta un sir de caractere si cu doua butoane - OK si Reset-. Metoda
este apelata cu:
window.prompt(“mesaj de afisat in fereastra”,”text implicit in campul de editare”);
Metoda afiseaza in fereastra prompt mesajul si initializeaza campul de editare cu
textul implicit dupa care asteapta apasarea pe unul din butoane. Utilizatorul poate
edita textul implicit din campul de editare dupa care apasa pe unul din butoane. Daca
apasa pe OK metoda va returna sirul de caractere introdus de utilizator. Daca apasa
butonul Reset-, sirul intors este vid - “”, nu contine nici un caracter. Incarcand de
exemplu documentul din listingul 30.6 browserul va afisa fereastra prompt din figura
30.14.
Se editeaza textul din campul de editare astfel incat in locul adresei de e-mail
picard@uss1701D.starfleet.fed sa apara worf@uss1701D.starfleet.fed ca in figura
30.15.
396 CURS 30
Daca se apasa pe butonul Reset- fereastra de confirmare afisata va fi cea din figura
30.17.
• Metoda scroll( )
Fie documentul HTML din listingul 30.7. Incarcand acest document, browserul va
afisa imaginea din figura 30.18.
PROGRAMARE IN JAVA - Note de curs 397
Defilarea se poate face si din document printr-o instructiune JavaScript care apeleaza
metoda scroll ( ) a ferestrei.
Aceasta metoda permite defilarea ferestrei la un punct de coordonate (x,y) in pixeli.
Ca referinta este luat coltul din stanga sus al ferestrei care are coordonatele (0,0).
Astfel de exemplu instructiunea
window.scroll(0,0);
va face ca fereastra curenta sa arate pagina Web incarcata incepand cu coltul din
stanga sus. Fereastra curenta poate determina defilarea imaginii in fereastra parinte
prin referinta window.opener:
window.opener.scroll(0,150);
In figura 30.19 este reprezentat efectul instructiunii window.scroll(122,162) constand
in
defilarea imaginii la coordonatele (122,162).
• Metodele open( ) si close( )
398 CURS 30
Metoda open( ) permite o noua fereastra a browserului. Cand aceasta metoda este
apelata fereastra curenta (in care se executa scriptul Java ramane pe loc si o noua
fereastra este deschisa pe ecran. Sintaxa instructiunii de apel a acestei metode este:
window.open(“url”,”nume_fereastra”,”caracteristica1, caracteristica2, ….”);
unde url desmneaza locatia resursei ce trebuie incarcata in noua fereastra,
nume_fereastra, numele intern, asociat ferestrei, care poate fi folosit pentru referirea
ulterioara a acesteia.Lista de caracteristici caracteristica1, caracteristica2,… contine
specificatii privind atributele ferestrei nou deschise cum ar fi dimensiunea, titlul,
butoane de navigatie, etc.
Metoda close( ) determina atunci cand este apelata inchiderea ferestrei. Sintaxa
instructiunii de apel este:
window.close( );
Locatia
In fereastra afisata de browser apare o caseta de editare unde utilizatorul poate sa
inscrie URL-ul resursei Web pe care vrea sa o incarce (figura 30.20). Aceasta caseta
este si ea un obiectavand numele location.
• Proprietatea location.href
Aceasta proprietate contine sirul de caractere al URL-ului resursei accesate de
browser. Astfel pentru a determina browserul sa incarce documentul avand URL-ul
http://www.server.ro/document.html, un script Java il va memora in proprietatea href
a obiectului locatie:
location.href=" http://www.server.ro/document.html"
Odata inscrisa noua locatie, browserul va incarca documentul specificat in fereastra
curenta. Astfel daca browserul incarca documentul href.html din listingul 30.8 va
intalni in cadrul acestuia instructiunea JavaScript
location.href="http://www.nexus3.org/mydir/location.html";
Executia aceastei instructiuni il va determina sa incarce documentul location.html al
carui continut este redat in listingul 30.8, afisandu-l in fereastra imediat ce a terminat
de afisat href.html. Fereastra afisata de browser este cea din figura 30.21.
<HTML>
<HEAD>
<TITLE>OBIECTUL "location"</TITLE>
</HEAD>
<BODY LINK="#0000ff" VLINK="#800080">
<FONT SIZE=6><P>OBIECTUL "<I>location</I>"</P>
</FONT><P>Aceasta pagina a fost incarcata in urma executiei unei</P>
<P>instructiuni JavaScript:</P>
<FONT FACE="Courier,Courier New" SIZE=3 COLOR="#ff0000">
<P>location.href="http://www.nexus3.org/MYDIR/location.html";</P>
</BODY>
</HTML>
• Proprietatea location.host
Aceasta proprietate contine doar o parte din sirul de caractere al URL-ului paginii
curente afisate de browser si anume cea care localizeaza calculatorul gazda. Astfel in
cazul URL -ului din exemplul precedent, proprietatea host contine doar subsirul
subliniat care desemneaza calculatorul gazda pentru serverul HTTP:
http:// http://www.nexus3.org/mydir/location.html
¾ location.host contine sirul de caractere "http://www.server.ro"
http://www.server.ro:6001/document.html
PROGRAMARE IN JAVA - Note de curs 401
http://www.nexus3.org:6001/mydir/location.html
¾ location.host contine sirul de caractere "http:// www.nexus3.org:6001".
http:// http://www.nexus3.org:6001/mydir/location.html
¾ location.host contine sirul de caractere "http:// www.nexus3.org:6001".
¾ location.host contine sirul de caractere "6001".
¾ location.protocol contine sirul de caractere "http:".
• Proprietatea location.pathname
Aceasta proprietate contine parte din sirul de caractere al URL-ului paginii curente
afisate de browser care specifica calea spre documentul accesat in sistemul de fisiere
al calculatorul gazda:
http:// http://www.nexus3.org:6001/mydir/location.html
¾ location.pathname contine sirul de caractere "/mydir/location.html ".
Atribuind o valoare noua acestei proprietati, de exemplu o cale spre un alt document
HTML, browserul va accesa serverul pentru a incarca noul document. Astfel
instructiunea JavaScript:
location.pathname = "/mydir/altele/document.html";
http://www.nexus3.org/mydir/ altele/document.html
• Proprietatea location.hash
http://www.nexus3.org/mydir/ altele/document.html#ancora
¾ location.hash contine sirul de caractere "ancora ".
• Proprietatea location.search
402 CURS 30
Am vazut in capitolul precedent ca atunci cand intr-o pagina Web care contine un
formular, utilizatorul apasa pe butonul submit, browserul emite o cerere catre serverul
HTTP de a executa un program specificat de URL-ul transmis. URL-ul contine pe
langa adresa programului si un sir de interogare pe care serverul il va pasa ca
parametru programului la serviciile caruia a apelat browserul. Proprietatea
location.search contine tocmai aceasta componenta din URL. Astfel in cazul cand
URL-ul este:
http://www.nexus3.org/mydir/program?DateFormular
¾ location.search contine sirul "DateFormular".
3.6 ISTORICUL NAVIGATIEI
Browserele tin istoricul navigatiei de la un document la altul intr-un obiect numit
hystory. Acest istoric este tinut sub forma unei liste de URL-uri ale paginilor vizitate.
In figura 3.22 este prezentata fereastra history la browserul Netscape Navigator iar in
figura 3.23 folder-ul history al lui Internet Explorer.
Proprietatile obiectului history sunt history.current care contine URL-ul documentului
curent - afisat in fereastra browserului - (avand deci acelasi continut cu location.href)
si history.length care contine lungimea actuala a listei de pagini Web vizitate
(numarul de elemente ale listei). Astfel in cazul exemplului din figura 30.22 acest
numar este 4 iar in cazul celui din figura 30.23 este 3. Metodele obiectului history
permit navigarea spre documaentele ale caror URL-uri sunt inscrise in lista istoricului
navigatiei.
• metodele history.back() si history.forward()
Aceste doua metode au efecte similare apasarii butoanelor Back si respectiv Forward
din bara de butoane a browserului, permitind navigarea inapoi in pagina precedenta si
respectiv inainte la pagina care a fost parasita cu Back.
• metodele history.go(deplasament_lista) si history.go(sub_sir)
Aceste doua metode permit saltul la URL-ul unui element arbitrar din lista istoricului
navigatiei. Astfel metoda history.go(deplasament) determina accesarea URL-ului aflat
la deplasament-ul transmis ca argument, relativ la pozitia curenta in lista.
Argumentul deplasament poate avea valori pozitive sau negative. Daca este pozitiv,
navigarea se face inainte (forward) iar daca este pozitiv, inapoi (back). De exemplu
instructiunea: history.go(1) este echivalenta cu history.forward( ) iar history.go(-1)
este echivalenta cu history.back( ).
In cea de a doua varianta, argumentul poate sa fie si un sir de caractere. Browserul va
cauta intrarea in lista in care se regaseste partial sirul de caractere transmis ca
argument si va accesa documentul respectiv. De exemplu pentru lista din figura 3.22
apelul history.go("href.html") va determina accesul la documentul href.html avand
URL-ul http://www.nexus3.org/mydir/href.html aflat in lista la pozitia 2.
Obiectul Document
Documentul incarcat de browser este si el tratat ca un obiect avand proprietati si
metode. Numele atribuit de browser paginii Web curente este document.
Fig.30.24 - BGCOLOR="#FFD700"
document.bgColor = "#008080"
determina ca culoarea de fond a documentului sa devina cea din figura 30.25
Fig.30.25 - document.bgColor="#008080"
document.fgColor="#FFFFFF"
va face ca textul sa fie afisat cu culoarea alba (trebuie sa fim atenti la alegerea culorii
de fond pentru ca textul sa fie vizibil).
In pagina afisata, textul accentuat apare evidentiat prin subliniere si culoare distincta
de cea a textului obisnuit. Odata afisate in pagina, legaturile se pot gasi in trei stari:
¾ legatura neselectata;
¾ legatura activata(selectata);
¾ legatura deja vizitata;
Pentru ca utilizatorul sa distinga usor legaturile deja vizitate de cele pe care urmeaza
eventual sa le viziteze si sa vada si ce legatura tocmai a activat, starea legaturii este
evidentiata printr-o culoare distincta a textului accentuat.
Browserul pastreaza informatia privind legaturile deja vizitate un numar de zile
prestabilit dupa care le readuce la starea de legaturi nevizitate. Astfel, chiar si atunci
PROGRAMARE IN JAVA - Note de curs 405
cand revine la pagina respectiva dupa o perioada mai lunga de timp, utilizatorul va sti
ce legaturi a vizitat la accesarile anterioare.
Culoarea celor trei tipuri de legaturi este stabilita implicit de browser dar poate fi
stabilita si prin atributele LINK, VLINK si ALINK ale marcajului <BODY>:
Atributul LINK stabileste culoarea pentru legaturile nevizitate, VLINK pentru cele
vizitate si ALINK pentru legatura activata. Culorile implicite sunt : LINK=blue,
VLINK=purple, and ALINK=red.
Proprietatile document.linkColor, document.vlinkColor si document.alinkColor au
valoarile setata de aceste atribute ale marcajului <BODY>.
Culoarea legaturilor poate fi modificata prin program prin atribuirea unei alte valori
diferite de cea originala. Astfel atribuirile:
document.linkColor="#00FFFF";
document.vlinkColor="#FF0000";
document.alinkColor="#00FF00";
• proprietatea document.title
Intr-un document HTML, in sectiunea HEAD, marcajele <TITLE>,</TITLE>
incadreaza un text ce reprezinta titlul documentului si este afisat de browser in bara de
titlu a ferestrei. Sirul de caractere al titlului este accesibil intr-un script Java prin
proprietatea document.title. Textul afisat in bara de titlu a ferestrei poate fi schimbat
prin atribuirea unei noi valori acestei proprietati:
• proprietatea document.anchors
Am vazut in capitolul precedent ca intr-un document HTML, in sectiunea BODY,
marcajul <A NAME="nume_ancora"> amplaseaza in textul acestuia o "ancora" ce
poate fi referita intr-o legatura din alt document sau din documentul curent.
Proprietatea document.anchors este un tablou continand valorile (numele) tuturor
"ancorelor" din pagina, in ordinea in care acestea au fost inscrise in textul
documentului. accesul la elementele tabloului se face prin intermediul unui index care
specifica numarului de ordine al ancorei:
document.anchors[0]
document.anchors[1]
……………………..
document.anchors[6]
Fiecare din elementele de mai sus contine numele unei ancore din textul
documentului. Astfel daca in document au fost definite in ordine ancorele cu numele
"Luni","Marti","Miercuri", "Joi","Vineri","Sambata","Duminica" aceste siruri de
406 CURS 30
• proprietatea document.links
Aceasta proprietate este similara cu cea precedenta cu deosebirea ca document.links
este un tablou ce contine toate legaturile specificate cu marcajul <A
HREF="legatura">, in ordinea in care acestea apar in document. Accesul la
elementele tabloului se face printr-un index:
document.links[0]
document. links [1]
……………………..
document.links[3] = "http://www.nexus3.org/";
• proprietatea document.lastModified
Aceasta proprietate contine un sir de caractere care specifica data calendaristica a
ultimei modificari aduse documentului curent. Un script poate folosi aceasta
informatie pentru a arata utilizatorului cat de "proaspata" este informatia continuta de
pagina Web curenta.
• proprietatea document.referrer
Daca pagina curenta a fost incarcata prin selectarea unei legaturi dintr-o alta pagina,
aceasta proprietate contine URL-ul paginii din care s-a facut referinta. Aceasta
informatie poate fi folosita pentru a tine o evidenta statistica a locurilor din care a fost
accesata pagina.
• proprietatea document.URL
Aceasta proprietate poate fi folosita pentru a determina browserul sa incarce in locul
paginii curente un alt document, prin atribuirea:
document.URL = "http://www.nexus3.org/alta_pagina.html";
• metoda document.clear( )
Aceasta metoda sterge continutul ferestrei curente. Aceasta operatie nu afecteaza
continutul documentul curent sau a variabilelor si proprietatilor obiectelor. Este o
operatie pur cosmetica care doar sterge imaginea afisata in fereastra browserului
pregatind locul pentru ca o noua afisare de informatii sa nu se suprapuna peste
imaginea anterioara.
• document.write("<H1>Hallo World!</H1>");
Diferenta dintre write si writeln consta in faptul ca dupa afisarea textului, metoda
writeln adauga la sfarsitul sirului de caractere afisat un terminator de linie new_line
(linie noua) de altfel ignorat de browser (daca chiar se doreste trecerea la o linie noua,
se va inscrie la sfarsitul sirului de caractere marcajul HTML <BR> ). Aceasta
caracteristica a functiei writeln este utila numai in cazul in care se afiseaza text
preformatat incadrat de marcajele <PRE>,</PRE>. In acest caz browserul ia in
consideratie la afisare si caracterele de formatare ASCII - SPACE, TAB, LINE_FEED
si CARRIAGE_RETURN, ignorate in mod normal.
In listingul 30.9 este prezentata utilizarea metodei document.write( ) pentru afisarea in
fereastra browserului. Imaginea obtinuta este redata in figura 30.26.
Obiectele Formular
Formularele definite intr-un document cu marcajele <FORM>,</FORM> sunt la
randul lor obiecte. Asa cum am vazut, ele sunt referite de proprietatea forms a
obiectului document. Accesul la un obiect formular se face prin intermediul referintei
document.forms[i] unde i este numarul de ordine al formularului in document. In
continuare sunt discutate proprietatile obiectelor formular astfel refrerite.
• proprietatea document.forms[i].action
Aceasta proprietate contine URL-ul programului apelat pentru prelucrarea sirului de
interogare obtinut dupa completarea de catre utilizator a formularului si apasarea
butonului Submit. Valoarea ei poate fi modificata prin atribuirea unei noi valori sub
forma unui sir de caractere. Astfel atribuirea
document.forms[0].action="Alt_URL";
• proprietatea document.forms[i].method
Aceasta proprietate contine metoda de transfer a sirului de interogare a serverului
stabilita cu atributul METHOD din marcajul <FORM>. Am vazut in capitolul
precedent ca metodele posibile sunt doua: GET si POST. Metoda stabilita de marcajul
<FORM METHOD=…> poate fi modificata prin atribuirea unei noi valori proprietatii
method ca in exemplul urmator:
document.forms[0].method="get";
Aceasta atribuire stabileste pentru primul formular din document metoda de transfer
GET.
• proprietatea document.forms[i].elements
Proprietatea elements reprezinta un tablou ale carui elemente sunt referinte la
componentele formularului (casete de editare, de validare, butoane, etc.) inscrise in
ordinea definirii lor in formular.
Numarul de elemente ale tabloului este dat de proprietatea length a acestuia. De
exemplu numarul componente ale primul formular din document este dat de
document.forms[0].elements.length unde document este documentul curent, forms[0]
este referinta la primul formular din tabloul forms (proprietate a obiectului document),
iar elements este proprietatea obiectului formular referit de forms[0]. Acesta fiind la
randul sau un tablou, lungimea sa (numarul de elemente pe care le cuprinde) este data
de proprietatea length.
Componentele formularului pot fi acesate prin referintele continute de tabloul
elements. Astfel prima componenta este referita de elements[0], a doua de
elements[1] si asa mai departe.
Obiectele Buton
Pe langa butoanele Sumbit si Reset in cadrul unui formular pot fi definite si alte
butoane fara o functionalitate precisa. Unui astfel de buton i se poate asocia o functie
JavaScript. Apasarea de catre utilizator a unui astfel de buton declanseaza automat
executia functiei JavaScript asociate. In cadrul formularului un astfel de buton se
defineste tot prin marcajul <INPUT>:
NAME="TEST"
VALUE="Test"
CHECKED="true"
onClick="mesaj()">
</H2>
</FORM>
</HEAD>
</BODY>
</HTML>
Proprietatile obiectului sunt name - numele listei, type = "select" - tipul elementului,
length care specifica numarul de optiuni din lista, options[] - un tablou ale carui
elemente sunt obiecte corespunzand optiunilor definite cu marcajele
<OPTION>,</OPTION> si selectedIndex care contine indexul optiunii selectate in
tabloul options[] .
Elementele tabloului options[] sunt la randul lor obiecte avand proprietatile text - un
sir de caractere care contine textul optiunii delimat in document de marcajele
<OPTION>,</OPTION>, value - valoarea setata cu atributul VALUE sau implicit
textul etichetei optiunii, acelasi cu cel continut de proprietatea text, defaultSelected -
continand o valoare booleana true daca optiunea era implicit selectata cu atributul
SELECTED sau false in caz contrar.
Obiectului i se pot asocia functii JavaScript ce vor fi executate la producerea unor
evenimente cum ar fi onFocus - controlul este activat devenind tinta intrarilor de la
tastatura, onBlur - controlul este dezactivat (acesta nu mai este tinta intrarilor de la
tastatura) si onChange - produs cand controlul este dezactivat (nu mai este tinta
intrarilor de la tastatura) si optiunea selectata initial la activare in momentul onFocus
s-a schimbat.
Listingul 30.13 si figura 30.30 exemplifica definirea si utilizarea unui formular
continand o lista de tip menu.
</SELECT>
</FORM>
</HEAD>
</BODY>
</HTML>
Revenind la obiectul select sa mai analizam un exemplu care de aceasta data creaza
un element de formular de tip lista-menu cu defilare. Lista-menu cuprinde ca optiuni
mai multe denumiri de culori. Selectarea uneia dintre culori determina executarea
functiei JavaScript schimbaCuloarea() care face ca fondul documentului sa capete
culoarea aleasa din lista-menu.
Folosind attributul SIZE al marcajului <SELECT> lista-menu poate fi afisata ca o
lista menu cu defilare. In exemplul din listingul 30.14 lista menu cu defilare astfel
construita permite selectarea culorii de fond a documentului afisat. In figura 30.32
este prezentata pagina afisata de browser dupa ce din lista-menu cu defilare s-a
selectat optiunea a 3-a (avand textul "bleuciel" si valoarea "#00FFFF").
</BODY>
</HTML>
Obiectul This
Am vazut ca accesul la valoarea unui element j al formularului i din document se face
cu referinta
document.forms[i].elements[j].value
Obiectul Navigator
Acest obiect este folosit de un script Java pentru a obtine informatii despre browserul
care il executa. Proprietatile obiectului sunt:
• proprietatea navigator.appCodeName
Aceasta contine numele de cod al browserului. de exemplu pentru browserul Netscape
aceasta proprietate este mozilla.
• proprietatea navigator.appName
Aceasta contine c real al browserului.
PROGRAMARE IN JAVA - Note de curs 419
• proprietatea navigator.appVersion
Aceasta contine numarul de versiune al browserului.
• proprietatea navigator.appAgent
Aceasta contine informatia completa asupra browserului inclusiv numele de cod,
numele, numarul de versiune si platforma (de exemplu Win95).
• proprietatea navigator.plugins[]
Aceasta proprietate este un tablou ale carui elemente specifica modulele plug-in
instalate (de sunet, animatie, etc.).
• proprietatea navigator.mimeType[]
Aceasta proprietate este un tablou ale carui elemente specifica ce tipuri MIME de date
este capabil sa afiseze browserul (text HTML, imagini GIF, JPG, sunet MID/WAVE
etc.)
Obiectul are si o metoda - navigator.javaEnabled() care daca este apelata, returneaza
o valoare booleana true daca browserul are activata interpretarea codului Java si false
in caz contrar.
Evenimente
La discutarea obiectelor JavaScript discutate in acest capitol in unele cazuri ne-am
folosit de termenul eveniment pentru a desemna momentele in care utilizatorul
interactioneaza cu obiectul modificandu-i starea (onClick, onFocus, onBlur si
onChange). Acestor evenimente le-am asociat in exemplele noastre functii JavaScript
activate automat de browser la sesizarea producerii unui eveniment. Pentru unele
obiecte cum ar fi ferestrele si documentele, evenimente sunt si incarcarea/creerea
(onLoad) sau distrugerea (onUnload) lor ( asa cum si pentru noi nasterea si moartea
sunt evenimente majore). Si acestor evenimente li se pot asocia functii JavaScript care
sa fie activate la survenirea lor. In tabelul 30.1 sunt enumerate toate obiectele
discutate pe parcursul acestui capitol cu proprietatile, metodele si evenimentele lor.
420 CURS 30