Documente Academic
Documente Profesional
Documente Cultură
Curs Java PDF
Curs Java PDF
Curs Java PDF
PROGRAMAREA ORIENTATĂ PE
OBIECTE
ÎN LIMBAJUL JAVA
1
Severin Bumbaru
Severin Bumbaru
Programarea orientată pe obiecte în limbajul Java
ISBN 973-8352-53-3
2
Programarea orientata pe obiecte în limbajul Java
CUPRINSUL
0. Introducere 4
1. Scurtă prezentare a platformei Java şi a programării orientate pe
obiecte 7
2. Şiruri. Elemente lexicale ale limbajului Java. Variabile şi tipuri de
date primitive 27
3. Expresii. Instrucţiuni simple şi instrucţiuni structurate. Tratarea
excepţiilor în limbajul Java 66
4. Tipul referinţă. Utilizarea claselor din pachetul java.lang. Tablouri.
Utilizarea parametrilor din linia de comnadă 99
5. Declararea claselor 137
6. Caracteristicile obiectelor şi claselor şi reflectarea lor în declararea 163
claselor. Interfete. Clase imbricate
7. Interfeţe utilizator grafice şi programarea orientată pe evenimente 198
8. Componentele grafice din JFC/Swing 240
9. Utilizarea ferestrelor de dialog. Desenarea 267
10. Introducere în HTML. Crearea şi utilizarea de appleturi 291
11. Fluxuri de intrare/ieşire şi fişiere 306
12. Fire de execuţie 333
3
Severin Bumbaru
Introducere
Obiectivele cursului 4
Cum este structurat acest curs 5
Recomandări privind modul de lucru 5
Obiectivele cursului
Cursul "Programare orientată pe obiecte în limbajul Java" se adresează studenţilor profilelor
"Ingineria sistemelor şi calculatoarelor" şi "Electronic" de la Universitatea "Dunărea de Jos"
din Galaţi şi îşi propune următoarele obiective:
Cursul poate fi folosit şi de toţi cei care doresc să se iniţieze în utilizarea limbajului Java şi în
programarea orientată pe obiecte.
4
Programarea orientata pe obiecte în limbajul Java
Cursul se prezintă în format electronic (hipertext), fiind alcătuit dintr-un ansamblu de pagini
legate între ele. Din punct de vedere al obligativităţii citirii şi însuşirii cunoştinţelor conţinute,
aceste pagini sunt situate pe patru niveluri, care difera între ele prin culoarea fundalului
(background):
Cursul conţine, de asemenea, un index de clase si interfeţe, în care se dau legături către pagini
care prezintă unele clase şi interfeţe din bibliotecile platformei Java (Java API) utile în
realizarea aplicaţiilor.
să lucraţi în echipă;
fiecare membru al echipei, după ce şi-a însuşit conceptele de bază ale unei lecţii:
o propune şi realizează experimente pentru clarificarea acestor concepte;
o discută cu colegii cele constatate în urma experimentelor efectuate;
5
Severin Bumbaru
6
Programarea orientata pe obiecte în limbajul Java
un limbaj de programare, ale cărui calităţi i-au permis răspândirea rapidă, fiind în
prezent unul din limbajele cele mai larg folosite. Limbajul este simplu, orientat pe
obiecte, robust, sigur, portabil, interpretat, neutru faţă de arhitectură, concurent,
dinamic si distribuit;
un mediu de execuţie pentru aplicaţiile Java, numit în prezent în engleză "Java 2
Runtime Environment", care conţine maşina virtuală Java şi un nucleu al bibliotecilor
de clase Java;
o platformă pentru dezvoltarea de aplicaţii în care se foloseşte limbajul Java, care se
numeşte în prezent în engleză "Java 2 Platform" şi conţine:
o compilatorul Java (Java Compiler);
o maşina virtuală Java (Java Virtual Machine);
o bibliotecile de clase Java (Java Class Libraries);
o vizualizatorul de appleturi Java (Java AppletViewer);
o depanatorul Java (Java Debbuger) şi alte instrumente de dezvoltare;
o documentaţia;
o tehnologie software puternică şi modernă, care corespunde cerinţelor lucrului în
reţele de calculatoare.
Există, desigur, şi alte semnificaţii ale cuvântului Java, care însă nu au legatură directă cu
informatica.
Limbajul Java
Java este un limbaj de programare de utilizare largă, care are următoarele proprietăţi:
7
Severin Bumbaru
este simplu: deşi sintaxa se aseamănă cu cea a limbajelor C/C++, au fost eliminate
acele aspecte care constituiau surse de erori sau produceau confuzii;
este orientat pe obiecte: în limbajul Java nu este posibil să se scrie aplicaţii care nu
respectă normele programării orientate pe obiecte;
este robust: programele scrise în Java sunt foarte fiabile, deoarece sunt prevăzute
numeroase verificări atât la compilare, cât şi în timpul executării;
este sigur: fiind destinat lucrului în reţele de calculatoare, la realizarea sistemului Java
s-a avut în vedere necesitatea ca programele să nu poată aduce daune calculatoarelor
pe care rulează, cum ar fi accesul neautorizat la informaţie sau chiar distrugerea
acesteia;
este portabil, adică programul poate fi mutat de pe un calculator pe altul, de alt tip
hardware şi/sau cu alt sistem de operare;
este compilat şi interpretat: programul sursă, scris în limbajul Java, este translatat de
către compilatorul Java într-un program intermediar sub formă de cod de octeţi
(engleza: bytecode). În timpul execuţiei, acest cod de octeţi este interpretat de către
maşina virtuală Java, care conţine un interpretor;
este neutru faţă de arhitectură: codul de octeţi generat de compilatorul Java nu
depinde de arhitectura echipamentului pe care acesta va fi executat, deoarece el nu
este executat direct de către procesorul hardware al acestui echipament, ci este
interpretat de către maşina virtuală Java;
permite programarea concurentă: în limbajul Java se pot scrie programe cu mai multe
fire de execuţie (engleza: multithreading), care pot fi executate simultan şi
sincronizate;
este dinamic, deoarece legarea între ele a claselor şi obiectelor nu se face static (la
compilare), ci dinamic (în momentul execuţiei);
este distribuit, adică permite realizarea de programe utilizabile în reţele heterogene
(care conţin calculatoare de tipuri diferite);
În enumerarea de mai sus, care nu este nicidecum completă apar, probabil, şi unii termeni
care vă sunt, deocamdata, neclari sau necunoscuţi. Unii din aceşti termeni vor fi clarificaţi
chiar în acest capitol, alţii - în capitolele următoare.
Atenţie! Deşi sintaxa limbajului Java se aseamănă cu cea a limbajelor C sau C++, Java nu
este C. Pe parcursul lecţiilor următoare vom atrage atenţia programatorilor de C/C++ asupra
principalelor deosebiri dintre Java şi aceste limbaje.
Deşi sintaxa limbajului Java a fost inspirată de cea a limbajelor C şi C++, autorii limbajului
Java au eliminat acele aspecte care produceau dificultăţi programatorilor, constituind surse de
erori şi confuzii. Dintre acestea menţionăm:
reducerea numărului de tipuri de date primitive prin eliminarea tipurilor fără semn
(unsigned);
introducerea tipului de date boolean, astfel încât datele logice să nu se mai confunde
cu cele întregi;
şirurile de caractere nu mai sunt tablouri, ci obiecte ale clasei String;
tablourile sunt ele însele obiecte;
în timpul execuţiei se verifică dacă indicele se încadrează în dimensiunea tabloului şi
se generează o excepţie în caz contrar;
8
Programarea orientata pe obiecte în limbajul Java
Neutralitatea arhitecturală
În mod ideal, un program sub forma de cod de octeţi (bytecode) ar trebui să poată fi executat
pe orice tip de calculator, indiferent de arhitectura acestuia, tipul procesorului, capacitatea de
memorie etc, deci să fie independent de platformă. În realitate, calculatorul trebuie să aibă o
memorie disponibilă suficient de mare pentru a conţine cel puţin maşina virtuală Java (JVM).
Apare deci o contradicţie: pentru a executa programe mari, JVM trebuie să aiba o memorie
mare, deci şi calculatorul gazdă trebuie să aibă o memorie mare. Pe de altă parte, pentru a
putea executa programe Java pe calculatoare de capacitate foarte mică (cum sunt cele
încorporate în diverse aparate electrocasnice, aparate de automatizare etc) este necesară o
JVM cu memorie mică. Un program destinat unui calculator mic poate fi executat şi pe unul
cu memorie mare, dar nu şi invers. Aceasta a condus la necesitatea de a avea mai multe
"medii de execuţie Java", în funcţie de dimensiunile şi destinaţia calculatorului pe care se
instalează, iar între caracteristicile programelor Java nu se mai mentioneaza "independenţa de
platformă" ci "neutralitatea faţă de arhitectură".
Neutralitatea faţă de arhitectură înseamnă, de fapt, că arhitectura maşinii virtuale Java, care
este ea însăşi un calculator abstract (organizarea memoriei, convenţiile de reprezentare a
datelor în memorie, setul de instrucţiuni al procesorului etc) nu depinde de arhitectura
calculatorului pe care aceasta se instaleaza. Întrucat codul de octeţi obţinut din compilarea
programului Java nu se execută nemijlocit pe calculatorul gazdă, ci pe maşina virtuală Java,
el poate fi acelaşi pe orice calculator pe care este instalata o astfel de maşină, deci este neutru
faţă de arhitectura calculatorului gazda.
Este însa necesar să precizăm că maşina virtuală Java însăşi este un produs software, care este
instalat şi funcţionează direct pe calculatorul gazdă, deci respectă convenţiile arhitecturale ale
acestuia. În consecinţă, pe fiecare tip de calculator trebuie instalată o maşină virtuală Java
corespunzătoare. Se poate, deci, vorbi de neutralitatea faţă de arhitectura a programelor Java,
dar nu şi a maşinii virtuale Java.
9
Severin Bumbaru
Modelul de memorie
Memoria calculatorului abstract este o succesiune de locaţii de memorie. Fiecare locaţie de
memorie este privită ca o "casetă" care conţine un numar fix de cifre binare (biţi). Numărul
de ordine al locaţiei (care arată poziţia acesteia în cadrul memoriei) se numeste adresă. În
consecinţă, memoria calculatorului este adresabilă. Locaţia este cea mai mică zonă de
memorie care poate fi citită sau înregistrată. La orice operaţie de citire/scriere se transferă un
numar întreg de locaţii.
În principiu, modelele de memorie pot să difere între ele prin numărul de biţi conţinut în
locaţia de memorie (prin "lungimea" locaţiei). Totuşi, la majoritatea calculatoarelor actuale,
locaţia de memorie conţine opt biţi şi se numeşte octet (engl.: byte). Iată un exemplu de
model de memorie organizată pe octeţi:
10
Programarea orientata pe obiecte în limbajul Java
În acest model, în coloana din stânga apar adresele locaţiilor. Le-am scris în sistemul de
numeratie zecimal, pentru a fi mai uşor de urmărit. De obicei însă, ele se exprimă în sistemul
binar, sau în cel hexazecimal. Numărul de locaţii este însă în realitate, desigur, mult mai
mare, exprimandu-se de regulă în megaocteţi. În coloana din dreapta au fost reprezentate
locaţiile de memorie, conţinând fiecare exact opt cifre binare. Conţinutul a fost aici ales,
desigur, arbitrar.
Capacitatea de memorie
Capacitatea memoriei calculatorului se poate exprima în biţi. Totuşi, întrucât majoritatea
calculatoarelor moderne au memoria organizată pe octeţi, se obisnuieşte să se folosească
drept unitate de masură octetul sau multiplii acestuia.
Reamintim că nu trebuie sa confundam un bit cu un octet (engl.: byte). Primul este unitatea
binara, exprimându-se prin cifrele binare 0 si 1. Numele este o prescurtare de la denumirea
din engleză a unităţii binare: binary unit, de la care s-au luat începutul şi sfârşitul. Un octet
(byte) este însă un număr format din 8 cifre binare, deci din 8 biţi.
Procesorul abstract
Procesorul abstract este caracterizat printr-un set de regiştri şi un set de instrucţiuni.
11
Severin Bumbaru
Instrucţiunile sunt codificate intern tot prin numere binare, formate din cel puţin două părţi:
un cod al operaţiei şi adresa operandului (uneori adresele operanzilor). Exemple de operaţii:
- încărcarea, adică transferul unui operand din memorie într-un registru; instrucţiunea
conţine codul operaţiei de încarcare, adresa din memorie a operandului şi numărul registrului
în care se încarcă;
- memorarea, adică inregistrarea la o anumită adresă din memorie a conţinutului unui
registru; se dau: codul operaţiei, numărul registrului de origine şi adresa din memorie a
destinaţiei;
- operaţii de calcul (aritmetice sau logice), care se efectueaza fie între operanzii din
registre, fie între un operand dintr-un registru şi unul din memorie. În instrucţiune se dau
codul operaţiei şi numerele registrelor între care se face aceasta, sau numărul unui registru şi
o adresă din memorie. Rezultatul operaţiei rămane, de regulă, într-un registru;
- instrucţiuni de salt, prin care se modifică fluxul normal (secvenţial) al executării
instrucţiunilor. În mod normal, după ce s-a executat o instrucţiune din program, se trece la
cea imediat următoare, în ordinea în care acestea sunt situate în memorie. Instrucţiunea de
salt indică trecerea la executarea unei instrucţiuni situată la o altă adresă din memorie.
Fiecare instrucţiune este înregistrată în memorie pe o lungime unul sau mai multi octeţi.
12
Programarea orientata pe obiecte în limbajul Java
Maşina von Neuman lucrează numai cu numere întregi. Unitatea aritmetică şi logică
efectuează operaţiile de calcul (adunare, scădere, înmulţire, împărţire întreagă, valoare
absoluta). Ea conţine doi regiştri, dintre care unul se numeste registru acumulator. Orice
operaţie se face folosind regiştrii unităţii centrale:
- încărcarea este transferul unui operand din memorie în unul din registri;
- memorarea este transferul continutului registrului acumulator la o anumită adresă din
memorie;
- operaţiile de calcul (adunare, scădere, înmulţire, împărţire) se fac între numerele din cei
doi regiştri, iar rezultatul rămâne în registrul acumulator;
- operaţiile de intrare/ieşire se fac între registrul acumulator şi unitatea de intrare/ieşire.
Dacă, de exemplu, dorim să se efectueze calculul c=a+b, aceasta înseamnă că se vor executa
următoarele instructiuni:
încarcă în registrul A operandul situat în memorie la adresa a;
încarcă în registrul R operandul situat în memorie la adresa b;
adună operanzii din cei doi regiştri (rezultatul rămâne în registrul acumulator A);
memorează conţinutul registrului A la adresa c din memorie.
Bineînţeles, aceste instrucţiuni vor fi reprezentate în cod binar, adica atât operaţiile, cât şi
adresele din memorie vor fi exprimate prin numere în sistemul de numeraţie cu baza 2.
Primele generaţii de calculatoare construite au respectat destul de fidel modelul maşinii von
Neuman. Chiar dacă, ulterior, evoluţia calculatoarelor şi a sistemelor de operare a suferit
abateri de la acest model (s-a introdus accesul direct la memorie al unităţilor de intrare/ieşire,
au aparut calculatoare cu mai multe procesoare, care funcţionează în paralel, etc.), limbajele
de programare au continuat, în majoritatea lor, să fie făcute pentru calculatoare abstracte de
tip von Neumann. Exemple bine cunoscute de astfel de limbaje sunt Fortran, Cobol, Basic,
Pascal, C, C++ ş.a.
Limbajul Java face parte din categoria limbajelor pentru procese concurente, în sensul că el
permite să existe în paralel mai multe fire de executie, ale căror operaţii pot, în principiu, să
se realizeze pe procesoare diferite. Este unul din motivele pentru care acest limbaj nu mai are
la bază maşina von Neumann, ci pentru el a fost conceput un alt calculator abstract, numit
maşina virtuală Java.
Maşina virtuală Java (engleză: JVM - Java Virtual Machine) este, în general, implementată
software sub forma unui produs program adecvat calculatorului pe care acesta se instalează.
13
Severin Bumbaru
Ideea de bază a limbajului şi tehnologiei Java, este ca - pe baza acestora - să se poată creea
produse software neutre faţă de arhitectura sistemului de calcul, deci care să poată fi
executate pe orice echipament, de la produse de uz casnic comandate numeric (televizoare,
telefoane, maşini de spălat, frigidere, etc) pană la supercalculatoare. Ea se exprimă prin
sloganul "Write Once, Run Anywhere" (scrie o singură dată şi rulează oriunde), care arată că
un program, după ce a fost scris şi compilat, poate fi executat (rulat) pe orice calculator.
Pentru realizarea acestui obiectiv, s-a decis ca în tehnologia Java portabilitatea programelor
sa se realizeze la nivel de cod de octeţi (bytecode), adică sub forma de cod binar destinat unui
calculator abstract, numit maşina virtuală Java. În acest scop:
s-a întocmit o descriere riguroasă numită specificaţia maşinii virtuale Java, (The
Java Virtual Machine Specification), în care se prezintă în detaliu arhitectura şi
funcţionarea acestei maşini;
pe fiecare calculator, pe care se execută programe Java, trebuie sa existe o
implementare (o realizare concretă sub forma de produs software sau hardware) a
maşinii virtuale Java, care execută efectiv programul dat sub forma de cod de octeţi.
Nucleul de clase: Limbajul Java este orientat pe obiecte. Orice program este un ansamblu de
clase şi de obiecte, care sunt instanţieri ale claselor. În mediul de execuţie Java este inclusă şi
o bibliotecă de clase predefinite. Este vorba, în special, de acele clase care asigură
comunicarea dintre programul Java şi sistemul de operare al calculatorului gazdă. Deşi
interfaţa acestor clase cu programul Java nu depinde de tipul calculatorului gazdă,
implementarea lor este dependenta de platformă, la fel ca şi cea a maşinii virtuale Java.
Dacă este realizată software (aşa cum se întamplă în majoritatea cazurilor), maşina virtuală
Java este ea însăşi un produs program, care este scris şi compilat special pentru tipul de
calculator pe care se instalează, deci foloseşte setul de instrucţiuni nativ al acestuia. Acest
produs program trebuie să respecte specificaţia maşinii virtuale Java, dar realizarea concretă
depinde de tipul de calculator pe care se instalează. Mai mult, pentru acelaşi calculator pot fi
realizate mai multe maşini virtuale Java, care toate respecta specificaţia, dar diferă între ele
prin modul de realizare.
14
Programarea orientata pe obiecte în limbajul Java
Implementările moderne ale maşinii virtuale Java se caracterizează prin mărirea sensibilă a
vitezei de executie, care se apropie deja de cea a programelor compilate. Aceasta se
realizează, în special, prin introducerea unei compilări "just in time": codul de octeţi este
compilat, în momentul execuţiei, in cod nativ pentru calculatorul gazdă şi abia apoi este
executat.
Desigur că cea mai eficientă reducere a duratei de execuţie se produce atunci când maşina
virtuală Java este implementata hardware, astfel încât codul de octeti Java (bztecode) este cod
nativ al acestei maşini. În acest caz, durata de execuţie a programului este aceeaşi cu cea a
unui program compilat tradiţional.
Platforma Java 2
Un mare avantaj al programatorilor in limbajul Java este că au la dispoziţie un set puternic de
instrumente de dezvoltare a programelor: compilator, depanator, bibliotecă de clase,
documentaţie etc. La început, acesta s-a numit "setul de dezvoltare Java" (în engleza: JDK -
Java Development Kit). Au existat mai multe versiuni succesive: JDK 1.0, JDK 1.1, JDK 1.2
aparute, respetiv, in anii 1996, 1997 si 1998.
În anul 1998 s-a introdus denumirea de "Platforma Java 2", care se referă la întregul complex
de specificaţii, resurse software şi documentaţie puse la dispoziţie programatorilor şi
utilizatorilor de Java. Ca urmare, JDK 1.2 a fost redenumit "Java 2 Platform SDK, SE v1.2"
(SDK - Software Development Kit, SE - Standard Edition, v - version). In prezent, se
utilizează deja "Java 2 Platform SDK, SE v1.4".
În cursul nostru vom folosi J2SE, iar din documentaţia oferită de firma Sun vom folosi în
special documentaţia API pentru această platformă.
15
Severin Bumbaru
Tehnologia Java
Datorită caracteristicilor sale, Java nu mai poate fi considerat doar un limbaj de programare
asociat cu o platformă de dezvoltare de aplicaţii, ci a devenit o veritabilă tehnologie software.
Când s-a trecut de la programarea procedurală la programarea orientată pe obiecte, s-a arătat
că aceasta poate fi comparată cu trecerea de la fabricaţia artizanală la cea industrială:
programul nu mai trebuie creat "de la zero", ci poate fi conceput ca un ansamblu de
componente (obiecte) "prefabricate". Programatorii se pot împărţi în două categorii: cei care
creeaza clasele de obiecte şi le oferă "pe piaţa" şi cei care, din aceste componente, crează
aplicaţii (produse software finite) oferite utilizatorilor.
Limbajele orientate pe obiecte existente anterior (C++, Object Pascal, etc) nu au reuşit, totuşi,
să stea la baza unei astfel de "industrializări". Cauza principală este că, fiind limbaje
compilate, nu puteau sta la baza producerii unor componente utilizabile pe orice platformă
hardware şi sub orice sistem de operare şi - deci - nu puteau fi folosite fara dificultăţi într-o
reţea de calculatoare eterogenă.
Deşi a apărut recent, răspândirea foarte rapidă a platformei Java se datoreşte tocmai faptului
că apariţia ei a corespuns cu dezvoltarea amplă a reţelelor de calculatoare, în special a
Internet-ului. Un număr foarte mare de firme producătoare de software şi numeroşi
programatori individuali din intreaga lume s-au angajat în dezvoltarea de clase, componente
şi aplicaţii programate în Java. Numărul de clase din Java SDK creşte continuu, fiind
utilizabile în domenii din ce în ce mai variate: prelucrări de date numerice, prelucrări de
texte, interfaţa grafică cu utilizatorul, lucrul cu fişiere, comunicatii în reţele, legătura cu baze
de date, securitatea datelor, calcul distribuit, etc. În afară de clasele din SDK (puse la
dispoziţie de firma Sun Microsystems), programatorii pot folosi un numar mare de clase şi
componente Java ("Java beans") oferite de alte firme, multe din ele disponibile liber pe
Internet. Ca urmare, se poate spune că baza tehnologică a programării în Java creşte
exponenţial.
Limbajul Java în sine este simplu şi uşor de învăţat. Forţa tehnologiei Java constă nu în
limbajul folosit, ci în numărul din ce în ce mai mare de clase reutilizabile, pe care
programatorii le au la dispoziţie, şi în faptul că programele realizate pot fi utilizabile practic
oriunde, fără a fi necesar sa fie refăcute când se trece de la un tip de calculator la altul.
Aceasta presupune însă, ca în orice alta tehnologie, şi necesitatea unei standardizări, a
introducerii unor norme pe care să le respecte întreaga comunitate a programatorilor. Acest
rol il îndeplinesc acum specificatiile: specificatia limbajului Java, specificatia maşinii virtuale
Java, Java API, etc. Întreţinerea şi dezvoltarea acestor specificaţii este facută, deocamdată,
de firma Sun Microsystems. Nu este exclus ca, cu timpul, acest rol sa fie conferit unui
organism de standardizare internaţional, pentru a nu se creea avantaje unui anumit producător
de software.
Dintre resursele cele mai importante puse la dispoziţia programatorilor de tehnologia Java,
menţionăm:
16
Programarea orientata pe obiecte în limbajul Java
JFC (Java Foundation Classes) - biblioteca de clase pentru realizarea unor interfeţe
grafice cu utilizatorul independente de sistemul de operare utilizat. Începând cu
Platforma Java 2 este inclusă în Java API;
Java Beans - o arhitectură de componente reutilizabile, neutre faţă de tipul
calculatorului şi sistemul de operare, care pot fi utilizate în medii de programare
vizuale. Se ofera şi instrumente de dezvoltare pentru astfel de componente: BDK
(Beans Development Kit) pentru dezvoltarea de componente, Java Plug-in (pentru
legătura cu Active-X de la Microsoft), JAF (JavaBeans Activation Framework) pentru
identificarea, localizarea componentelor şi altele;
JDBC (Java Data Base Connectivity) - componente pentru legătura cu baze de date;
JINI - tehnologie bazată pe Java, pentru asigurarea conectivităţii echipamentelor în
reţele de calculatoare;
tehnologia agenţilor software - produse software care se pot deplasa de la un
calculator la altul într-o reţea şi pot îndeplini în mod autonom pe calculatorul de
destinaţie sarcinile pentru care au fost trimise. Platforma Java este foarte convenabilă
pentru crearea şi utilizarea agenţilor în reţele eterogene. Exemple sunt tehnologia
agleţilor oferita de firma IBM, cadrul de dezvoltare a agentilor Java Java Agents
Framework (JAF) de la Universitatea din Massachutess, etc.
şi multe altele.
Există, de asemenea, numeroase medii de programare pentru Java, oferite de diverse firme,
cum sunt:
Forte for Java - un mediu de programare vizual, realizat si oferit gratuit pentru
utilizări necomerciale de firma Sun MicroSystems.
WebGain VisualCafe - un mediu de programare vizual, realizat de firma WebGain;
Borland JBuilder - un mediu de programare vizual, realizat de firma Borland;
VisualAge for Java - un mediu de programare vizual realizat de firma IBM;
Visual J# .NET - un mediu de dezvoltare vizual al firmei Microsoft
şi altele.
În acest curs, ne vom rezuma la însuşirea limbajului Java şi a unor componente din Java API,
folosind numai Java 2 SDK, Standard Edition.
17
Severin Bumbaru
Desi s-a realizat o cantitate mare de software, s-a constatat că, în vremea respectivă,
orientarea către aparatura electrocasnică ("de consum"), nu corespundea încă cerinţelor pieţei.
După o serie de alte încercări, s-a ajuns la concluzia că cea mai buna utilizare a noului limbaj
este realizarea de software pentru reţelele de calculatoare. Întrucat denumirea "Oak" nu a
putut fi înregistrată oficial, în timpul unei discuţii "la o ceaşcă de cafea", autorii au decis sa-i
dea limbajului numele Java, care a fost şi înregistrat ca marcă comerciala (TM - Trade Mark).
Iată dece acest nume apare frecvent scris sub forma JavaTM.
Prima utilizare comercială a acestui limbaj a fost includerea unui interpretor Java în
navigatorul (browserul) de Web al firmei Netscape. Introducerea de appleturi (miniaplicaţii)
Java în paginile de Web a permis ca acestea sa devina interactive, deci să capete o
componentă dinamică.
Clasa este o extensie a conceptului de tip de date şi conţine o structură de date, împreună cu
metodele (functiile) care se aplica acestor date.
Obiectul este o instantiere (o instanta) a clasei. In acelasi program se pot folosi mai multe
obiecte apartinand aceleeasi clase, sau unor clase diferite. Fiecare obiect se caracterizeaza
prin stare si comportament. Starea obiectului depinde de datele pe care acesta le contine, in
timp ce comportamentul este dat de metodele clasei respective.
In general, comunicarea prin mesaje consta in invocarea de metode. Daca obiectul a invoca o
metoda a obiectului b, aceasta poate avea ca efect modificarea starii obiectului b (adica
modificarea unor date continute in structura de date a lui b) si/sau poate primi o valoare
intoarsa de metoda respectiva. Se considera ca, prin invocarea metodei, obiectul a a transmis
un mesaj obiectului b, ceeace a provocat din partea acestuia un anumit raspuns (deci b a
manifestat o anumita comportare).
18
Programarea orientata pe obiecte în limbajul Java
Exemplu
Sa consideram clasa poligoanelor regulate. Structura de date contine doua variabile: numarul
de laturi (care este o variabila de tip intreg) si lungimea unei laturi (care este o variabila de tip
real). Metodele pot fi, de exemplu, mici programe (functii) prin care se calculeaza aria,
perimetrul, apotema, raza cercului circumscris etc. Pot exista si metode prin care se modifica
numarul de laturi sau lungimea laturii, deci se modifica starea poligonului. Clasa are un
nume (de ex. PoligonRegulat). Pot exista, evident, mai multe instante (obiecte) ale acestei
clase, care toate sunt poligoane regulate, dar difera intre ele prin numarul de laturi si/sau prin
lungimea laturii.
Fie p un PoligonRegulat cu 4 laturi de lungime 3.5. Invocarea metodei p.arie() are ca efect
calcularea functiei arie()pentru obiectul p, deci se va obtine ca raspuns valoarea 12.25. Prin
invocarea acestei metode, s-a transmis obiectului p mesajul ca se cere calcularea ariei, iar
comportamentul acestui obiect a constat in punerea in executie a metodei invocate si
intoarcerea valorii calculate a ariei. Valoarea intoarsa depinde, evident, de starea in care se
gaseste p in momentul invocarii metodei, adica de numarul de laturi si de lungimea laturii.
De exemplu, daca clasa Pasare contine variabila numarAripi, aceasta variabila va avea
valoarea 2 pentru toate pasarile, deci pentru toate instantele clasei respective, fiind o variabila
statica sau a clasei. In schimb, variabila varsta va avea valori diferite pentru fiecare pasare,
deci este o variabila a instantei (nestatica).
Metodele statice (ale clasei) pot folosi numai variabilele statice ale clasei respective, in timp
ce metodele nestatice pot folosi atat variabilele statice, cat si pe cele ale instantei.
Din punct de vedere al modului de acces, datele si metodele unei clase pot fi publice sau
private. Cele publice sunt accesibile din orice alta clasa, in timp ce cele private sunt
accesibile numai din clasa careia ii apartin.
19
Severin Bumbaru
applet (miniaplicaţie) - este un program care se transmite sub formă de cod de octeţi
(bytecode) prin reţeaua de calculatoare şi este executat în cadrul unui navigator (browser) de
Web, fără a avea acces la fişierele sau sistemul de intrare/ieşire al calculatorului pe care se
execută;
servlet - un program care se execută pe un server dein reţea.
class <nume_clasa> {
public static void main(String args[]) {
// corpul metodei main
}
}
Părţile scrise cu negru (inclusiv parantezele şi acoladele) le vom considera obligatorii, iar cele
scrise cursiv cu roşu sunt la latitudinea programatorului.
Părţile componente ale acestui şablon pot fi explicate pe baza cunoştintelor pe care le avem
deja despre programarea orientată pe obiecte şi despre limbajele de programare.
- descrierea (definirea) unei clase începe prin cuvantul class, urmat de numele clasei;
- corpul clasei este cuprins între acolade { ... }
- în cazul nostru, corpul clasei conţine o singura metodă;
- metoda are forma unei funcţii, care se numeşte main şi are un singur argument numit args.
Vom vedea ulterior că acesta este un tablou de obiecte din clasa String, adică un tablou de
şiruri de caractere. Tot atunci vom vedea că acest argument serveşte pentru preluarea
parametrilor din linia de comandă;
20
Programarea orientata pe obiecte în limbajul Java
- metoda main (metoda principală) este publică, adică poate fi utilizată de obiecte din alta
clasă (în cazul nostru, ea este apelată de maşina virtuală Java la punerea în execuţie a
aplicaţiei);
-metoda main este statică, deci apartine clasei şi nu instanţelor acestei clase;
- privită ca o funcţie, metoda main trebuie, în principiu, să întoarcă o valoare. În acest caz,
metoda nu întoarce o valoare, deci tipul valorii întoarse este void (loc gol, lipsă).
class PrimaAplicatie {
public static void main(String args[]) {
System.out.println("Prima noastra aplicatie a reusit!");
}
}
Remarcăm urmatoarele:
Efectul executării acestei aplicaţii este că se afişează pe ecran textul Prima noastra
aplicatie a reusit!
21
Severin Bumbaru
dacă lucrati sub Windows, veţi folosi o fereastră MS-DOS (Command Prompt);
dacă lucraţi sub Linux sau Unix, veţi folosi o fereastră de X-Terminal. Sub Linux vă
recomandăm sa folosiţi KDE, sub care să selectaţi din bara de scule pictograma
"Terminal Emulation", sau din meniul Applications sa alegeti optiunea "X Terminal".
Pentru crearea fişierului se va folosi un editor de text simplu, care generează numai text
neformatat, de exemplu "Notepad" daca lucraţi sub Windows sau sub Linux cu WinLinux99,
respectiv "Text Editor" sau "Advanced Editor", daca lucraţi sub Linux cu KDE.
După ce aţi dat această comandă, veţi obţine unul din următoarele rezultate:
1. Pe ecran apare din nou promptul sistemului de operare, fără a se afişa un mesaj de
eroare. În acest caz, compilarea a decurs normal, iar dacă daţi comanda dir veţi
constata ca în subdirectorul curent a apărut un nou fişier, care poartă numele clasei şi
extensia class. Acesta este fişierul care conţine bytecode-ul clasei compilate.
2. Obţineţi un mesaj de eroare, care indică fie că există erori în program, care au fost
sesizate de compilator, fie că există erori în linia de comandă prin care aţi cerut
compilarea. În ambele cazuri, este necesar să eliminaţi erorile şi să reluaţi compilarea.
22
Programarea orientata pe obiecte în limbajul Java
23
Severin Bumbaru
Puteţi introduce intenţionat şi mai multe din erorile de mai sus simultan. Compilaţi fişierul
astfel modificat şi interpretati mesajele de eroare obţinute.
Executarea aplicaţiei
Dacă în directorul curent există fişierul <nume_aplicaţie>.class, executarea acestei
aplicaţii se solicită prin comanda
java <nume_aplicaţie>
Remarcăm că se dă ca parametru numai numele clasei, fără extensia class. Efectul acestei
comenzi este, fie executarea aplicatiei, fie aparitia unui mesaj de eroare de execuţie.
Prin comanda java se pune în execuţie interpretorul maşinii virtuale Java. Acest interpretor
verifică dacă în directorul curent există fişierul <nume_aplicaţie>.class. Daca acesta
există, încarcă în memorie codul de octeţi pe care îl conţine şi pune în execuţie metoda
public static void main(). Dacă fişierul nu există, sau dacă el nu conţine o astfel de
metodă, se semnalează printr-un mesaj de eroare.
Exemplul 1
Considerăm că a reuşit compilarea fişierului corect PrimaAplicatie.java şi s-a obţinut
astfel fişierul PrimaAplicatie.class, după care se dă comanda
java PrimaAplicatie
Ca rezultat, pe ecran apare textul
Prima noastra aplicatie a reusit!
Exemplul 2
Să dăm acum comanda
java PimaApicatie
în care numele clasei este dat greşit. Ca rezultat, obţinem mesajul de eroare
Exception in thread "main" java.lang.NoClassDefFoundError: PimaApicatie
Acest mesaj arată că nu s-a găsit definiţia clasei PimaAplicatie (Eroarea constatată se deduce
din numele acesteia: NoClassDefFoundError provine de la "no class definition found error",
adica nu a fost găsită definiţia clasei PimaApicatie.
Exemplul 3
Să considerăm acum că am eliminat erorile de compilare din fişierul PrimaAplicatie1.java
şi am obţinut fişierul PrimaAplicatie1.class, dar că metoda main a rămas fără
modificatorii public static. Am arătat că lipsa acestor modificatori nu dă eroare de
compilare. Dând acum comanda
java PrimaAplicatie1
se obţine mesajul de eroare
24
Programarea orientata pe obiecte în limbajul Java
Întrebări
Nivel 1
1. Ce este Java?
2. Ce calităţi are limbajul de programare Java?
3. Ce conţine mediul de execuţie Java?
4. Ce conţine platforma Java?
5. Ce este programarea orientată pe obiecte?
6. Ce este clasa?
7. Ce este obiectul şi ce legatură există între clasă şi obiect?
8. Prin ce se determină starea obiectului?
9. Prin ce se defineşte comportamentul obiectului?
10. Ce deosebire există între variabilele statice şi cele nestatice?
11. Ce deosebire există între metodele statice şi cele nestatice?
12. Ce deosebire există între datele sau metodele publice şi cele private?
13. Care sunt principalele tipuri de produse software care pot fi scrise în limbajul Java?
14. Ce deosebire există între applet şi aplicatie?
15. Ce deosebire exista între applet şi servlet?
16. Care este metoda care trebuie să existe în mod obligatoriu în orice aplicaţie?
17. Ce este un fişier sursă Java şi cum se stabileşte numele lui?
18. Ce fel de editor se foloseşte pentru crearea fişierului sursă?
19. Prin ce comandă se cere compilarea unui fişier sursă Java?
20. Ce se obţine în cazul în care compilarea a decurs corect?
21. Ce se obţine dacă compilarea nu a reuşit?
22. Prin ce comandă se cere executarea unei aplicaţii?
Nivel 2
1. Dece limbajul Java este mai simplu decât C/C++?
2. Limbajul Java este compilat sau interpretat?
3. Ce fel de cod generează compilatorul Java?
4. Ce se înţelege prin portabilitatea programelor?
5. Dece limbajul Java este neutru faţă de arhitectură?
6. Dece limbajul Java permite programarea concurentă?
7. Ce este maşina virtuală Java?
8. Ce deosebire este între specificaţia maşinii virtuale Java şi implementarea acesteia?
25
Severin Bumbaru
26
Programarea orientata pe obiecte în limbajul Java
Primele două forme de comentariu există şi în limbajul C++, în timp ce ultima este specifică
numai limbajului Java.
27
Severin Bumbaru
Exemplu
Dăm aici ca exemplu fişierul Comentarii.java, care are la bază fişierul
PrimaAplicatie.java, dar acesta fost completat cu comentarii.
/** Aplicatie in care se exemplifica folosirea celor trei tipuri de
comentarii permise in limbajul Java */
Dacă vom compila şi pune în executie această aplicaţie, vom constata că efectul este acelaşi
ca în cazul clasei PrimaAplicaţie, deoarece comentariile introduse în fişierul sursă nu au efect
în timpul execuţiei programului.
Şiruri
Şirul se reprezintă în program printr-o succesiune de caractere cuprinsă între ghilimele. Iată
câteva exemple de şiruri:
"sir de caractere"
"ABCDefgh"
"1A23bc7"
"*+_/?"
""
Ultimul exemplu este un şir vid (care nu conţine nici un caracter).
Concatenarea şirurilor
Asupra şirurilor se poate aplica operaţia de concatenare, reprezentata prin operatorul +.
Expresia şir1+şir2, în care operatorul + este plasat între două şiruri, are ca rezultat un nou şir,
care conţine cele două şiruri-operanzi puse unul după altul. De exemplu, expresia
"abcde"+"fgh"
dă ca rezultat şirul "abcdefgh".
28
Programarea orientata pe obiecte în limbajul Java
Numele metodei println provine de la print line, care se traduce prin "tipăreşte o linie".
Metodele println siprint aparţin obiectului out din clasa System.
În limbajul Java, clasa System conţine metodele prin care se comunică cu sistemul de
operare al calculatorului, iar obiectul out al acestei clase reprezintă dispozitivul de ieşire
standard al sistemului, care este de obicei unitatea de afişare de la consolă (ecranul).
class AfisareSiruri {
public static void main(String args[]) {
System.out.println("sirul 1");
System.out.println("sirul 2"); // se afiseaza sub sirul 1
System.out.println("AB"+"CDE"); // se afiseaza ABCDE
System.out.println("ab"+"cd"+"ef"); // se afiseaza abcdef
System.out.println(("ab"+"cd")+"ef"); // asociativitate
System.out.println("ab"+("cd"+"ef"));
/* Urmatoarele trei instructiuni afiseaza in continuare,
pe o singura linie. */
System.out.print("pqrst"); // nu se trece la linie noua
System.out.print("UVW"); // se afiseaza in continuare
System.out.print("xyz\n"); // echivalent cu println("xyz")
/* Trecerea la linia urmatoare se face datorita prezentei
29
Severin Bumbaru
Vom analiza acum separat fiecare din aceste categorii de unităţi lexicale.
Identificatori
Numele date programelor sau componentelor acestora (clase, variabile, metode etc.) se
numesc identificatori. Identificatorii se aleg de către programator, respectând anumite reguli.
În limbajul Java, identificatorii sunt şiruri formate din litere, cifre şi caractere de subliniere
('_'), care încep cu o literă. Lungimea identificatorului nu prezintă importanţă, însă acesta nu
poate conţine spaţii libere sau alte caractere, decât cele menţionate aici.
30
Programarea orientata pe obiecte în limbajul Java
PrimaClasa
aplha
viteza
v15XB7
pretDeVanzare
pret_de_vanzare
Este clar acum că şi exemplele date anterior ( Afisari, main, String, args, System,
out, print, println) sunt, de asemenea, identificatori.
Programatorul poate adopta orice identificatori care respectă regulile şi convenţiile de mai sus
şi care nu sunt cuvinte cheie sau cuvinte rezervate. Desigur însă că folosirea unor
identificatori care au semnificaţie pentru om, cum ar fi viteza sau PretDeVanzare este
preferabilă celor fără semnificaţie, cum ar fi v15XB7, deoarece uşurează înţelegerea şi
urmărirea programului. Amintim însa că, pentru calculator, identificatorii nu au nici o alta
semnificaţie, deci, din acest punct de vedere, toate exemplele de identificatori date aici sunt la
fel de bune.
Cuvinte cheie
În orice limbaj de programare, există un set de cuvinte, numite cuvinte cheie, care sunt
considerate simboluri sintactice şi nu pot fi folosite în program ca identificatori.
Dintre acestea, const şi goto nu sunt folosite în prezent, dar ele au fost introduse în tabela
cuvintelor cheie în vederea unei eventuale utilizări viitoare.
Observăm acum că toate exemplele de cuvinte cheie date la începutul acestei secţiuni
(class, public, static, void) sunt prezente în tabela de mai sus.
31
Severin Bumbaru
Cuvinte rezervate
Se consideră cuvinte rezervate acele cuvinte, care nu pot fi folosite ca identificatori, având
semnificaţii speciale. Cuvintele cheie sunt şi ele considerate în majoritatea limbajelor,
inclusiv Java, drept cuvinte rezervate. În afară de acestea, în limbajul Java există urmatoarele
cuvinte rezervate: true, false, null.
Primele două sunt valorile logice adevărat şi fals, iar al treilea are semnificaţia de referinţă
nulă. De fapt, aceste cuvinte rezervate sunt forme speciale de literali.
Literali
Literalii sunt reprezentările în fişierele sursă ale valorilor constante. Exemple de literali:
- caractere: 'a', 'A', '+', '$', '5';
- şiruri de caractere: "sir de caractere", "abc$79.28#^z";
- numere întregi: 14726, -25413;
- numere reale: 12.7389, -0.05673, 2.3075E12, -1.4237E-5;
- valori logice: true, false;
- referinţa nulă: null.
Am subliniat faptul că literalul este forma sub care o anumita valoare este reprezentată în
fişierul sursă, deci în programul scris în limbaj de nivel superior (în cazul nostru în limbajul
Java). Vom arăta ulterior că forma de reprezentare a aceloraşi valori în memoria internă a
calculatorului (forma internă) este diferită de cea externă. Vom reveni asupra regulilor de
scriere a literalilor când vom prezenta tipurile de date din limbajul Java.
Separatori
Separatorul este un caracter care delimitează formele sintactice sau le separă între ele. În
limbajul Java se folosesc următorii separatori:
{ } ( ) [ ] ; , .
Spaţiul liber şi operatorii indeplinesc, de asemenea, rolul de separatori.
Aproape toţi aceşti separatori au fost deja folosiţi în exemplele date în acest capitol.
Operatori
Operatorii sunt simboluri ale unor operaţii. Am folosit deja simbolul + ca operator de
concatenare (deci simbol al operaţiei de concatenare). Operatorul poate fi format din unul sau
mai multe caractere. Entitatea asupra căreia se aplică operatorul se numeşte operand. După
numărul de operanzi, operatorii pot fi unari, binari sau ternari.
32
Programarea orientata pe obiecte în limbajul Java
operatori binari, care se aplică asupra a doi operanzi, operatorul fiind situat între aceştia;
de ex. operatorul de concatenare + în expresia "acesta este " + "un exemplu" sau
operatorul de adunare a două numere + în expresia 17+28.Operatorii binari se folosesc sub
forma "infix"
operand1 operator operand2
în care operatorul este situat între cei doi operanzi;
operatori ternari, care se aplică asupra a trei operanzi; în limbajul Java există un singur
operator ternar ( ? :) folosit în expresiile condiţionale. Operatorul ternar se scrie sub forma
operand1 ? operand2 : operand3
în care operand1 are o valoare logică (true sau false), iar ceilalti doi operanzi sunt
expresii aritmetice sau logice (dar de acelaşi tip).
Din exemplele de mai sus, observăm că semnificaţia unui operator poate să depindă de
context, ca în cazul operatorului +, care a fost folosit atât pentru operaţia de concatenare a
şirurilor, cât şi pentru cea de adunare a numerelor.
Din punct de vedere matematic, operatorii sunt funcţii cu unul, două sau trei argumente
(argumentele fiind operanzii). De exemplu, expresia a+b, în care + este un operator binar, iar
a şi b sunt operanzi, este o funcţie de argumente a si b, care are ca valoare suma valorilor
celor două argumente.
După efectul operatorului asupra operanzilor, operatorii pot fi fără efect lateral, care lasa
valorile operanzilor nemodificate, şi cu efect lateral, care modifică valorile operanzilor.
Astfel, operatorul + din exemplul anterior, este un operator fără efect lateral. În schimb, în
expresia ++a operatorul de incrementare ++ are efect lateral deoarece, în urma efectuarii
operaţiei, valoarea operandului a creşte cu o unitate.
Operatori unari:
+ - ++ -- new ! ~
() [] {}
Operatori binari
+ - * / %
== <= >= !=
& | ^
&& ||
<< >> >>>
= += -= *= /= &= |= ^= ~= <<= >>= >>>=
. instanceof
Operator ternar
?:
Remarcăm că operatorii + şi - pot fi atât unari ( ca în expresiile +a sau -a), cât şi binari (ca
33
Severin Bumbaru
în expresiile a+bsaua-b).
Semnificaţiile operatorilor vor fi arătate când se vor prezenta tipurile de date şi expresiile.
Comentarii
După cum s-a aratat deja, în fişierele sursă pot fi introduse comentarii, care au rolul de a da
omului,care citeşte programul respectiv, anumite explicaţii necesare pentru o mai buna
înţelegere a acestuia. Din punct de vedere sintactic, întregul comentariu este privit ca o
singură unitate lexicală, care este ignorată de către compilator, deci nu are efect asupra
codului de octeţi generat de acesta.
Spaţii
Între unităţile lexicale ale programului pot fi introduse oricât de multe spaţii libere, fără ca
acestea să aibă influenţă asupra sintaxei sau semanticii programului. Mai multe spaţii libere
succesive sunt tratate de compilator ca şi când ar fi un singur spaţiu.
Variabile
In matematică, variabila este un simbol dat unei valori, care aparţine unei mulţimi de valori
ce constituie domeniul de definiţie al variabilei respective.
În programare, variabila este un nume căruia i se asociază o valoare. Numele variabilei este
un identificator, iar valoarea variabilei trebuie să aparţină unui anumit tip de date. Asupra
valorilor variabilelor pot fi efectuate prin program anumite operaţii.
Exemplul 1
In expresia x=a+2, x şi a sunt variabile. Operaţiile efectuate sunt următoarele: se adună
valoarea variabilei a cu valoarea 2 (dată aici sub formă de literal), iar rezultatul se atribuie
ca valoare variabilei x; în consecinţă, valoarea variabilei a ramâne nemodificată, în schimb
variabila x primeşte o nouă valoare. Operatorul = nu exprima aici relaţia de egalitate, ci
operaţia de atribuire a unei valori unei variabile.
Exemplul 2
Expresia System.out.println(a) are ca efect afişarea pe ecran a valorii variabilei a.
Programatorul care foloseşte un limbaj de nivel înalt, cum este şi limbajul Java, nu trebuie să
cunoască adresa de memorie, la care este plasată valoarea variabilei, şi nici reprezentarea
34
Programarea orientata pe obiecte în limbajul Java
internă a acesteia, fiind suficient să-i cunoasca numele şi tipul. Alocarea de spaţiu în memorie
pentru fiecare variabilă se face, după caz, de către compilator sau interpretor. În schimb,
numele şi tipul variabilei trebuie declarate de către programator.
Remarcăm că în matematică operaţiile cu variabile se fac, de cele mai multe ori, la nivel
abstract, asupra simbolurilor variabilelor şi nu asupra valorilor acestora. De exemplu, în
identitatea a+a=2.a nu are importanţa ce valoare are variabila a, egalitatea fiind
întotdeauna adevarată.
În programare se are în vedere faptul că, atât timp cât ea exista în memorie, variabila are
întotdeauna o valoare (deoarece zona de memorie aferentă nu poate fi vidă), iar operaţiile se
fac asupra valorilor variabilelor şi nu asupra numelor acestora.
Din exemplul de mai sus, se observă că declaraţia de tip are urmatoarea formă:
în care:
tip - numele tipului de date căruia îi aparţin variabilele declarate;
variabila_i - specificarea numelui unei variabile, urmat opţional de valoarea
variabilei respective precedată de simbolul =.
Remarcam că:
declaraţia de tip este o instructiune care se termină obligatoriu prin simbolul ; (punct
şi virgulă);
este posibil ca, într-o singură declaraţie, să apară mai multe variabile; în acest caz,
35
Severin Bumbaru
În declaraţia de tip, valoarea iniţială a variabilei poate fi dată sub forma unui literal sau a
unei expresii. În ultimul caz, este necesar ca expresia să fie calculabilă, deci toate variabilele
pe care le conţine să aiba deja valori date anterior.
Prin convenţie, în limbajul Java numele de variabile încep întotdeauna cu literă mică. Este
permis însă ca, în interiorul numelui, sa existe şi litere mari. Aceasta se întamplă atunci când
numele variabilei este format din mai multe cuvinte ale limbii naturale, de exemplu
vitezaMedie.
Variabile finale
În limbajul Java, se numesc variabile finale acele "variabile", ale căror valori nu pot fi
modificate prin program. Acestea sunt deci, de fapt, nişte constante cu nume. Ele se
aseamănă cu variabilele propriu-zise prin faptul că sunt tot perechi nume - valoare, numai că
valoarea lor se dă o singură dată, sub forma de iniţializare în declaraţia de tip sau sub forma
de atribuire, după care nu mai poate fi modificată. Se obişnuieşte ca numele de variabile
finale să fie scrise în întregime cu majuscule.
Declaraţia de tip este la fel cu cea pentru variabile obişnuite, dar are in faţă modificatorul
final, care este un cuvânt cheie.
De exemplu, declaraţia
final int ALPHA=17, BETA=-1453;
serveşte pentru a specifică faptul că ALPHA şi BETA suntvariabile finale de tip int, ale
caror valori sunt, respectiv, 17 si -1453 şi nu mai pot fi ulterior modificate (deci ALPHA
şi BETA sunt, de fapt, nişte constante).
36
Programarea orientata pe obiecte în limbajul Java
Tipurile de date primitive sunt predefinite în limbaj. Aceasta înseamnă că numele, mulţimea
de valori, mulţimea de operaţii şi tipul rezultatului operaţiilor pentu fiecare tip primitiv sunt
impuse prin limbaj şi, deci, nu trebuie definite şi nu pot fi modificate de programator.
tipul boolean;
tipurile numerice
o tipuri intregi: byte, short, int, long;
o tipuri reale: float si double;
o tipul char
Pentru fiecare tip de date vom arăta reprezentarea externă, reprezentarea internă, operaţiile
şi operatorii corespunzători.
Prin reprezentare externă, înţelegem regulile după care se scriu valorile datelor respective în
programe, în documente sau pe ecranul calculatorului. Reprezentarea externă a valorii într-un
program se numeşte literal.
Prin reprezentare internă, înţelegem forma sub care datele respective apar în memoria
maşinii virtuale Java. O proprietate foarte importantă a reprezentării interne a datelor este că
aceasta, fiind destinată maşinii virtuale Java, nu depinde de calculatorul concret pe care se va
executa programul.
Vom începe studiul cu tipul de date boolean, apoi vom studia tipurile numerice propriu-zise
(intregi şi reale), după care vom studia tipul char, ca un tip numeric special.
Operaţii şi operatori
Pentru fiecare tip primitiv de date vom arăta, de asemenea, operaţiile specifice şi operatorii
corespunzători. Pentru început, vom prezenta aici operaţia de atribuire şi operatorii
relaţionali == şi != care se aplică tuturor tipurilor de date. Celelalte operaţii şi operatorii
corespunzători se vor fi prezenta odată cu tipurile de date cărora li se aplică.
Operaţia de atribuire
Prin operaţia de atribuire se dă (se atribuie) unei variabile o nouă valoare, care o înlocuieşte
pe cea deja existentă. Operatorul de atribuire este = (semnul egal, care însă aici se citeşte "se
atribuie") este un operator binar cu efect lateral. Expresia a=b, în care a este o variabila, iar b
este un operand care poate fi un literal, o variabilă sau o expresie, are semnificaţia "se
atribuie variabilei a valoarea operandului b". Atribuirea este posibilă numai daca valoarea
operandului b este de acelasi tip cu variabila a, sau dacă se poate converti implicit la acest
tip. Atribuirea este o operaţie cu efect lateral, deoarece produce modificarea valorii
operandului situat în partea stânga a operatorului de atribuire.
Exemplu
Fie x o variabilă de tip int. Expresia x=-17 se citeste "se atribuie lui x valoarea -17".
Efectul operaţiei de atribuire este, în acest caz, că valoarea anterioară a variabilei x se
înlocuieşte în memorie prin noua valoare. În partea dreaptă a operatorului de atribuire putea fi
nu numai un literal, ca în cazul nostru, ci orice altă expresie cu valoare de tip int.
37
Severin Bumbaru
Operatorii == şi !=
Pentru toate tipurile de date se pot aplica operatorii relaţionali == si !=. Aceştia sunt
operatori binari fără efect lateral. La aplicarea unui astfel de operator, rezultatul operaţiei
este valoarea booleană true (adevărat) sau false (fals).
Operatorul == (se citeşte "este egal cu") exprimă relaţia de egalitate. Expresia a==b, unde a
şi b sunt doi operanzi care pot fi literali, variabile sau expresii, are valoarea logică (booleana)
true dacă valoarile celor doi operanzi sunt egale, sau are valoarea logică false, dacă
egalitatea nu este satisfacută.
Cei doi operanzi trebuie sa fie comparabili, deci fie ambii de tip boolean, fie ambii numerici.
Operatorul != (se citeşte "este diferit de") exprimă relaţia de inegalitate a celor doi
operanzi. Expresia a!=b, unde a şi b sunt doi operanzi care pot fi literali, variabile sau
expresii, are valoarea logică (booleană) false dacă valoarile celor doi operanzi sunt diferite,
sau are valoarea logică true, dacă operanzii au valori egale.
Declaraţii de tip
Declararaţiile de tip sunt instrucţiuni prin care se specifică tipul, numele şi, dacă este necesar,
valoarea iniţială a variabilelor folosite în program. În limbajul Java, declaraţiile de tip au
forma:
tip variabila1, variabila2, ..., variabilaN;
în care:
tip - tipul variabilelor care se declară;
variabila- numele variabilei sau, daca este necesar, numele şi valoarea iniţială date
sub forma nume_variabilă = valoare_iniţială
nume_variabila este un identificator; în limbajul Java se obişnuieşte ca numele de
variabile să înceapă cu literă mică, deşi aceasta nu este o regulă de sintaxă;
valoare_initiala este o valoare de acelaşi tip cu variabila, care se dă variabilei la
iniţializare (în momentul când i se alocă spaţiu în memorie). Această valoare poate fi dată sub
forma unui literal sau a unei expresii calculabile (în care toate variabilele au valori atribuite
anterior).
Remarcăm că specificaţiile variabilelor se separă între ele prin virgule, iar la sfârşitul
declaraţiei se pune simbolul ; (punct şi virgulă) care, în limbajul Java, este terminatorul de
38
Programarea orientata pe obiecte în limbajul Java
instructiune.
Limbajul Java este strict tipizat, deci orice variabilă trebuie să fie declarată înainte de a fi
folosită. În plus, compilatorul Java verifică dacă, în momentul primei ei utilizări într-o
expresie, variabila are deja o valoare.
Exemplu
Prin instrucţiunea
int alpha, beta=-723, gamma=beta+7;
se declară că variabilele alpha, beta şi gamma sunt de tip int. Variabila alpha nu este
iniţializată, beta primeşte valoarea iniţială -723, iar gamma primeşte ca valoare rezultatul
calculării expresiei beta+7. Această expresie este calculabilă, întrucât beta are deja o
valoare.
Tipul boolean
Mulţimea de valori a acestui tip este {true, false}. Ea conţine cele două valori admise de
logica booleană: true înseamnăadevărat, iar false înseamnă fals. Asupra datelor din acest
tip pot fi aplicate operaţiile de atribuire, de comparaţie (== si !=) şi operaţiile algebrei
booleene (operaţiile logice).
Operatorii booleeni
Operatorul de negaţie este un operator unar fără efect lateral şi se reprezintă prin simbolul !
(semnul exclamării). Expresia !a, în care a este un operand boolean, se citeşte non-a şi se
interpretează ca negaţia lui a: daca a are valoarea true, atunci !a are valoarea false şi
invers.
Operatorii logici binari sunt operatori fără efect lateral, prin care se realizează operaţiile
logice ŞI, SAU şi SAU-EXCLUSIV.
- Operatorii & si && realizeaza operatia logica ŞI. Expresiile a&b şi a&&b ,în care a şi
b sunt operanzi de tip boolean, are valoarea true(adevărat) dacă şi numai dacă atât a cât şi
b au valoarea true. În celelalte cazuri expresia are valoarea false.
- Operatorii | si || realizează operaţia logică SAU. Expresiile a|b şi a||b , în care a
şi b sunt operanzi de tip boolean, are valoarea false dacă şi numai dacă ambii operanzi au
valoarea false. În celelalte cazuri expresia are valoarea true.
- Operatorul ^ realizează operatia logică SAU-EXCLUSIV. Expresia a^b , în care a şi
b sunt operanzi de tip boolean, are valoarea true dacă şi numai dacă cei doi operanzi au
valori diferite (unul este adevărat, iar celălalt fals). Dacă cei doi operanzi au valori identice,
valoarea expresiei este false.
39
Severin Bumbaru
Vom reveni asupra acestor deosebiri când vom arăta cum sunt tratate în Java expresiile logice
mai complicate.
Acţiunea operatorilor logici este prezentată sintetic în tabela de mai jos, în care a şi b sunt
doi operanzi logici.
În programul din fişierul TipBoolean.java se testează unele din aceste operaţii booleene.
class TipBoolean {
public static void main(String args[]) {
boolean alpha=true, beta=false, p, q, r,s;
p=!alpha;
q=alpha&β
r=alpha||beta;
s=alpha^beta;
System.out.println(" alpha="+alpha+" beta="+beta+" p="+p+
" q="+q+" r="+r+" s="+s);
System.out.println("alpha&&beta="+(alpha&&beta)+
"alpha||beta="+(alpha||beta));
System.out.println("alpha==beta: "+(alpha==beta));
System.out.println("alpha!=beta: "+(alpha!=beta));
}
}
40
Programarea orientata pe obiecte în limbajul Java
În limbajele C/C++ nu există tipul de date boolean, astfel că în locul acestuia se folosesc
datele întregi. În consecinţă, în aceste limbaje, operatorii & si | nu sunt consideraţi
operatori booleeni ci operatori logici pe biţi.
În Java nu este permis să se utilizeze expresii aritmetice în locul unor expresii logice
(booleene), aşa cum se întâmplă în C/C++.
Tipuri numerice
Sub aspect conceptual, datele care aparţin acestor tipuri sunt numere, asupra cărora pot fi
aplicate operaţiile aritmetice (adunare, scădere, înmulţire, împărţire) şi operaţiile de
comparaţie aritmetică (mai mic, mai mare, egal, diferit de).
Din punct de vedere matematic, aceste date pot fi numere întregi sau reale. Existenţa mai
multor tipuri în cadrul fiecăreia din aceste două categorii se datoreşte particularităţilor de
reprezentare a datelor în memorie.
Inainte de a trece la studierea fiecărui tip de date în parte, vom prezenta unele operaţii care se
aplică tuturor tipurilor de date numerice: atribuirea, conversia de tip, operatiile ariţmetice şi
comparaţia. Exemple pentru aplicarea acestor operaţii se vor da la studierea diferitelor tipuri
concrete de date numerice.
Operaţia de atribuire
Operaţia de atribuire se poate aplica tuturor tipurilor de date, deci şi celor numerice. În
expresia
variabilă = expresie
daca variabila din partea stângă aparţine unuia din tipurile numerice, atunci valoarea
expresiei din partea dreaptă trebuie sa fie, de asemenea, numerică şi să aibă un tip compatibil
cu cel al variabilei din partea stângă. Prin tip compatibil înţelegem fie acelaşi tip cu cel al
variabilei din stânga, fie un tip numeric care poate fi convertit implicit la acesta.
Dacă tipul operandului din dreapta este numeric, dar nu se converteşte implicit la cel din
stânga, se poate folosi conversia de tip explicită prin operatorul cast. În acest caz, însă, există
pericolul ca valoarea să se altereze prin conversie.
Conversia de tip
Dacă este necesar, datele pot fi convertite dintr-un tip în altul. După caz, conversia se poate
face implicit, sau poate fi cerută explicit prin program.
41
Severin Bumbaru
În limbajul Java, conversia de tip implicită se face atunci când prin conversie nu se pierde
informaţie. De exemplu, dacă în expresia a=b variabila a este de tip int, iar b este de tip
short sau byte, valoarea variabilei b va fi automat convertită la tipul int înainte de
atribuire.
În tabela de mai jos sunt indicate cu X toate conversiile de tip care se pot realiza inplicit. În
coloana din stânga este tipul datei care este supusa conversiei, iar în capul tabelei (pe prima
linie) tipul către care se face conversia.
De exemplu, tipul int se poate converti implicit în oricare din tipurile long, float sau double,
dar nu şi în tipurile byte sau short.
Conversia de tip explicită se face prin operatorul unar numit cast, care are forma (tip),
adică este format din numele tipului către care se face conversia, cuprins între paranteze.
Acesta este un operator fără efect lateral, deci care nu modifică valoarea operandului. De
exemplu, expresia (byte)a se va folosi pentru a converti valoarea operandului a la tipul
byte. Aceasta înseamnă că valoarea variabilei a rămâne neschimbată, însă valoarea expresiei
(byte)a se obţine din cea a lui a prin convertirea ei la tipul byte.
Utilizarea operatorului cast se justifică atunci când, în situaţia respectivă, conversia implicită
nu este posibilă. Nu este însă greşit dacă folosim acest operator chiar şi când conversia
respectivă se poate face şi implicit.
Operaţiile aritmetice
Operaţiile aritmetice sunt cele care se aplică unor operanzi numerici, având ca rezultate tot
numere. După numărul de operanzi, ele pot fi unare sau binare. Unele operaţii aritmetice
unare au şi efect lateral.
42
Programarea orientata pe obiecte în limbajul Java
În exemplele de expresii din această secţiune, vom considera că a şi b sunt doi operanzi
numerici.
Operatori binari
Operatorii binari nu au efect lateral - deci nu modifică valorile operanzilor - şi sunt daţi în
tabela de mai jos.
43
Severin Bumbaru
Prin împărţire întreagă înţelegem împărţirea făcută astfel, încât câtul sa fie un număr întreg
(fără extragerea părţii fracţionare (situate dupa virgulă).
Operatorii de atribuire compusă sunt următorii: +=, -=, *=, /=, %=, &=, |=, ^=, <<=,
>>=, >>>=.
Se observă că fiecare din aceşti operatori are forma op= în care op este un operator aritmetic
binar. Expresia
variabila op= operand
în care op este un operator aritmetic binar, este echivalentă cu expresia
variabila = variabila op operand
şi se evaluează astfel:
- se calculează mai întâi valoarea expresiei (variabila op operand)în care variabila
intră cu valoarea sa anterioară;
- valoarea astfel calculată se atribuie ca noua valoare a variabilei din partea stânga.
Aceasta nouaă valoare este, totodată, şi valoare a expresiei.
Constatăm astfel că operatorii de atribuire compusă au efect lateral, la fel cu cei de atribuire.
Exemplu
Fie x=3.72 si y=0.19 două variabile de tip double. Expresia x+=y se calculeaza, la fel ca
expresia x=x+y, în modul următor:
- se calculează valoarea expresiei x+y, care este 3.91;
- se atribuie variabilei x valoarea 3.91 (efectul lateral);
- valoarea astfel atribuită (3.91) este considerată şi drept valoare a expresiei x+=y.
Comparaţia
Comparaţiile sunt operaţii binare fără efect lateral, în care se compară două numere,
obţinându-se ca rezultat o valoare de tip boolean. Operatorii prin care se efectuează
comparaţia a două numere se numesc operatori relaţionali şi sunt daţi în tabela de mai jos.
Operator Semnificatie
< mai mic decât
<= mai mic decât sau egal cu
> mai mare decât
>= mai mare decât sau egal cu
== este egal cu
!= este diferit de
Operatorii relaţionali se aplică tuturor tipurilor de date numerice, inclusiv celor de tip char.
Se permite ca cei doi operanzi sa fie de tipuri diferite, de exemplu să se compare o valoare de
tip byte cu una de tip double sau cu una de tip char.
44
Programarea orientata pe obiecte în limbajul Java
Să consideram, de exemplu, expresia a<b, unde a şi b sunt operanzi de tipuri întregi. Dacă
valoarea operandului a este mai mică decât cea a operandului b, atunci valoarea acestei
expresii este true (adevărat). Dacă însă valoarea lui a nu este mai mică decât cea a lui b, ca
rezultat se obţine valoarea false (fals). Menţionăm că valorile logice true şi false sunt cele
două valori ale tipului de date boolean
Întrucât caracterele se codifică în memoria calculatorului prin numere întregi fără semn, în
limbajul Java asupra lor se pot aplica toate operaţiile pentru numere întregi. Totusi, datorită
particularităţilor pe care le prezintă, noi vom trata tipul char separat.
Se observă, deci, că deosebirea dintre diferitele tipuri de date întregi constă în lungimea
reprezentării lor interne, care condiţionează şi mulţimea de valori a tipului respectiv. Este
evident că mulţimea de valori a fiecăruia din aceste tipuri este numai o submulţime a
mulţimii numerelor întregi.
Reprezentarea internă a datelor de tip byte, short, int si long se face sub forma de
numere întregi cu semn, în sistemul de numeraţie binar. Primul bit al reprezentării interne
este interpretat drept semn (0 pentru + si 1 pentru -). Numerele întregi pozitive se
reprezintă, deci, prin numere binare care încep cu cifra 0. Numerele întregi negative se
reprezintă prin complementul la doi al modulului lor.
Limitele domeniilor de valori pentru fiecare tip întreg se pot exprima în binar în funcţie de
numărul de biţi astfel:
- tipul byte: -27 ... 27-1;
- tipul short: -215 ... 215-1;
- tipul int: -231 ... 231-1;
- tipul long: -264 ... 264-1.
45
Severin Bumbaru
Sa luăm ca exemplu numerele de tip byte, care se reprezintă intern pe o lungime de 8 biţi.
Cel mai mic numar pozitiv este, în acest caz, 00000000 (deci 0 extins pe toţi cei 8 biţi), iar
cel mai mare numar pozitiv este01111111, care este în sistemul zecimal 27-1, adica 127.
Remarcam că numarul -1 se reprezinta intern pe 8 biţi prin 11111111, iar numărul -128 prin
10000000.
Nu există in limbajul Java literali de tip byte sau short. Literalii de tip long se deosebesc de
cei de tip int prin faptul ca se termină prin litera L sau l. Se prefera litera majuscula L,
deoarece litera mică l se poate confunda cu cifra 1.
În sistemul zecimal, literalii întregi sunt şiruri de cifre zecimale care pot fi precedate de
semn şi nu conţin în interiorul lor separatori, cum sunt punctul, virgula sau apostroful (care se
folosesc de noi în scrierea uzuală a numerelor) şi nu incep cu cifra 0.
În sistemul octal, literalii întregi sunt numere cu sau fără semn, scrise în sistemul de
numeraţie octal (cu baza opt) şi care încep cu cifra 0. Amintim că cifrele sistemului octal
sunt 0, 1, 2, 3, 4, 5, 6, 7.
46
Programarea orientata pe obiecte în limbajul Java
În sistemul hexazecimal, literalii întregi sunt numere cu sau fără semn, scrise în sistemul de
numeraţie hexazecimal (cu baza 16) şi care încep cu prefixul 0x. Amintim că cifrele
sistemului hexazecimal sunt: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F. În locul majusculelor
se pot folosi şi minusculele corespunzătoare.
Sistemele octal şi hexazecimal prezintă avantajul că au ca baze puteri ale lui 2. Din această
cauză, conversia din aceste sisteme de numeraţie în binar pentru numerele pozitive se face
simplu: fiecare cifra octală se reprezintă în binar prin trei biţi, iar fiecare cifră fexazecimală
prin patru biţi.
Exemplu
Să considerăm urmatoarea aplicaţie din fişierul CastInt.java.
class CastInt {
47
Severin Bumbaru
Compilând şi rulând pe calculator această aplicaţie obţinem următorul rezultat afişat pe ecran:
i4=73 i5=-107
b3=91 b4=-103 b5=-77
Pentru cei interesaţi, putem urmări cum s-au facut efectiv aceste conversii, luând în
consideraţie reprezentările interne.
48
Programarea orientata pe obiecte în limbajul Java
c/ Atribuirea b3=(byte)i1 unde i1 are valoarea 91. Reprezentarea internă a lui i1 este
00000000000000000000000001011011 şi se extinde pe 4 octeţi. Prin conversia de la int la
byte se elimină cei trei octeţi din stânga, obţinându-se reprezentarea pe un octet 01011011.
Întrucât s-au eliminat numai zerouri din stânga, valoarea a rămas neschimbată.
Remarcăm, deci, că singurele operaţii aritmetice care pot da rezultat de tip byte sau short
sunt cele de incrementare sau decrementare.
Împarţirea întreagă
O consecinţă a modului de stabilire a rezultatului operaţiilor aritmetice este că, atunci când
se face o operaţie de împărţire (/) între două numere întregi, rezultatul este un numar întreg
(de tip int sau long, după caz). La împărţire se calculează, deci, numai partea întreagă a
câtului.
Este posibil ca, în operatia de împarţire întreagă, al doilea operand (împărţitorul) să fie egal
cu zero. În acest caz, maşina virtuală Java generează o excepţie de împărţire la zero, care face
parte din clasa ArithmeticException. În programul de mai jos, din fişierul
ImpartireZero.java, se testează o astfel de situaţie.
49
Severin Bumbaru
class ImpartireZero {
public static void main(String args[]) {
int a=5, b=0, c=0;
System.out.println(a/b);
}
}
Se observă că în expresia a/b opeandul b are valoarea zero. La executarea acestui program se
afişeaza pe ecran următorul mesaj:
Acest mesaj se interpretează astfel: s-a produs o excepţie din clasa ArithmeticException în
firul de execuţie "main"; excepţia constă în împărţire (/) la zero şi a apărut în metoda main a
clasei ImpartireZero, fiind localizată în fişierul sursă ImpartireZero.java linia 6. Se poate
constata cu uşurinţă că, într-adevar, operaţia care a produs excepţia se găseşte în locul indicat.
Înlocuind în acest fişier operaţia a/b prin c/b (astfel încât ambii operanzi sunt nuli)
caonstatăm că, dacă repetăm compilarea şi execuţia, obţinem aceeaşi excepţie. Aşa dar, la
depistarea excepţiei de împărţire la zero, maşina virtuală Java testează numai împărţitorul, nu
şi deîmpărţitul.
Depăşirea binară
La efectuarea unor operaţii cu numere întregi este posibil ca rezultatul să fie un numar binar
mai lung decât spaţiul alocat în memorie pentru tipul de date respectiv. De exemplu, dacă se
înmulţesc două numere de tip int (reprezentate pe 32 biţi fiecare), rezultatul poate fi un
numar mai lung de 32 biţi, deci a cărui valoare nu se mai încadrează în mulţimea de date a
tipului int. O astfel de situaţie se numeşte depăşire binară. În limbajul Java, apariţia
depăşirii binare nu este considerată o excepţie, ci calculul continuă, dar se reţin numai octeţii
din dreapta ai rezultatului, atâţi cât corespund reprezentării interne a tipului de date respectiv
(4 octeţi pentru int şi 8 octeţi pentru long). În consecinţa se pierd octeţii cei mai
semnificativi şi are loc, deci, modificarea valorii şi chiar a semnului rezultatului, la fel ca în
cazul conversiei de la un tip cu lungime mai mare la unul cu lungime mai mică.
Exemplu
În următoarea aplicaţie din fişierul Intregi.java se fac unele teste privind operaţiile cu numere
întregi.
class Intregi {
public static void main(String args[]) {
byte b1=73, b2=-109, b3, b4, b5;
short s1=9000, s2=-11000, s3;
int i1=900000, i2=-1100000, i3, i4, i5;
long m1=10000000000L, m2=-200000000000L, m3, m4;
b3=(byte)(-b2);
50
Programarea orientata pe obiecte în limbajul Java
b4=(byte)(b1+b2);
b5=++b1;
s3=(short)(s2/s1);
s4=(short)(s2%s1); // restul impartirii s2/s1
i3=i1+i2;
i4=i1*i2;
i5=(int)(m2/i1);
m3=m2-m1;
m4=m2*m1;
System.out.println("b3="+b3+" b4="+b4+" b5="+b5);
System.out.println("s3="+s3+" s4="+s4);
System.out.println("i3="+i3+" i4="+i4+" i5="+i5);
System.out.println("m3="+m3+" m4="+m4);
System.out.println("b5*b2="+(b5*b2)+ "s1*s2="+(s1*s2));
}
}
- s-a obţinut s3=-1 si nu s3=-1.222222... deoarece în expresia s2/s1 ambii operanzi sunt
de tipuri întregi, deci rezultatul este întreg (împărţire întreagă). Din acelaşi motiv s-a obţinut
i5=-222222 si nu -222222.222222....;
- la calcularea lui i4 nu s-a obţinut valoarea corectă -990000000000, ci valoarea
2137445376. Cauza este că valoarea depăşeşte marginea superioară a valorilor de tip int,
deci s-a produs o depăşire binară şi s-au trunchiat octeţii cei mai semnificativi. Tot o depăşire
binară s-a produs şi la calcularea lui m4 (de data aceasta pentru tipul long);
- valorile produselor b5*b2 şi s1*s2 au fost calculate şi afisate corect, deşi ele depăşesc
limitele mulţimilor de valori de tip byte, respectiv de tip short. Aceasta s-a produs deoarece
expresiile respective sunt de tip int.
Comparaţia
Operatorii de comparaţie pot fi aplicaţi pentru orice fel de operanzi numerici. Ca exemplu, în
fişierul ComparInt.java se da un program, în care se compară diferite numere întregi şi se
51
Severin Bumbaru
afişează rezultatele.
class ComparInt {
public static void main(String args[]) {
byte b1=48, b2=-17;
short s1=2765, s2=-12970;
int i1=762983, i2=48, i3=-12970;
long m1=876432906528L, m2=48;
System.out.println(b1==b2);
System.out.println(b1==i2);
System.out.println(s1!=i2);
System.out.println(i2!=m2);
System.out.println(m2>i2);
System.out.println(m2>=i2);
System.out.println(s2<i3);
System.out.println(i3<=s2);
}
}
Executând acest program se obţin pe ecran următoarele rezultate sub forma de valori
booleene:
false
true
true
talse
true
talse
true
52
Programarea orientata pe obiecte în limbajul Java
Operatorii de deplasare şi efectele lor sunt prezentate în tabela de mai jos, în care a şi s sunt
operanzi care aparţin unor tipuri întregi.
Deplasarea la stânga cu s poziţii este echivalenta cu înmulţirea numărului cu 2s. Dacă s este
suficient de mare, poate avea loc o depăşire binară, la fel ca în cazul înmulţirii aritmetice (cu
operatorul *).
Deplasarea biţilor la dreapta cu s pozitii cu operatorul >>> are asupra numerelor pozitive
acelaşi efect ca cel al operatorului >>. În schimb, în cazul operanzilor negativi, în cazul
operatorului >>> nu se mai conservă semnul, iar modulul numărului se modifică. Aceasta se
întâmplă, întrucât pe poziţiile eliberate din partea stângă se înserează bitul 0.
Exemplu
În fişierul Deplasari.java se dă un exemplu de aplicaţie, în care se testează acţiunea
operatorilor de deplasare binară.
class Deplasari {
public static void main(String args[]) {
byte b1=15, b2;
int i1=1024, i2=-1024;
b2=(byte)(b1<<3);
System.out.println("b1="+b1+" b2="+b2);
System.out.println("i1<<4="+(i1<<4)+" i1>>4="+(i1>>4)+
" i1>>>4="+(i1>>>4));
53
Severin Bumbaru
System.out.println("i2<<4="+(i2<<4)+" i2>>4="+(i2>>4)+
" i2>>>4="+(i2>>>4));
}
}
b1=15 b2=120
i1<<4=16384 i1>>4=64 i1>>>4=64
i2<<4=-16384 i2>>4=-64 i2>>>4=268435392
Având în vedere că 23=8 şi 24=16, rezultatele obţinute pot fi verificate cu uşurinţă. Constatăm
ca singura deosebire dintre efectele operatorilor >> şi >>> apare când primul operand este
negativ.
După aplicarea operatorului >>> (deplasare la dreapta fără conservarea semnului) se obţine:
- pentru operaţia i1>>>4: 00000000000000000000000001000000
- pentru operaţia i2>>>4: 00001111111111111111111111000000
Pentru operandul pozitiv i1 s-a obţinut un rezultat identic cu cel precedent. În schimb, în
cazul operandului negativ i2, pe poziţiile eliberate din partea stângă s-a înserat bitul 0,
ceeace a dus la schimbarea semnului şi a valorii rezultatului faţă de cazul deplăsarii cu
conservarea semnului.
54
Programarea orientata pe obiecte în limbajul Java
întregi şi operatorii logici pe biţi ~, &, | si ^. Aceştia sunt operatori fără efect lateral.
Operaţiile logice se fac la nivel de bit, adică între fiecare bit al operandului din stânga şi bitul
corespunzător al operandului din dreapta, considerându-se ca 0 este echivalent cu false, iar
1 este echivalent cu true.
Acţiunea operatorilor este dată în tabela de mai jos, în care a şi b sunt cei doi operanzi, iar ai
si bisunt biţii de pe pozitia i a acestor operanzi. În tabelă se dă efectul fiecărei operaţii asupra
bitului i al rezultatului.
Exemplu
În fişierul LogicaBiti.java este dat un exemplu de program, în care se testează operaţiile
logice pe biţi. Pentru a se verifica manual mai uşor, am considerat că opraţiile se fac asupra
datelor de tip byte. Este bine să avem însă in vedere că rezultatele operaţiilor sunt de tip
int. Aceasta se poate constata cu usurinţa dacă încercăm să eliminăm castul din instrucţiunea
în care se calculează b3.
class LogicaBiti {
public static void main(String args[]) {
byte b1=17, b2=-95, b3;
b3=(byte)(b1&b2); // este necesara conversie de la int la byte
System.out.println("~b1="+(~b1)+" b1&b2="+b3+" b1|b2="+(b1|b2)+
" b1^b2="+(b1^b2));
}
}
Iată şi cum decurg operaţiile din exemplul de mai sus la nivel de bit:
55
Severin Bumbaru
Exemplu
În programul din fişierul AtribComp.java se testează unele operaţii de atribuire compusă. Iată
acest program:
class AtribComp {
public static void main(String args[]) {
int m=176, b=-15, c=16, d=-28;
System.out.println(m+" "+(m+=b)+" "+m);
System.out.println((m/=d)+" "+m);
System.out.println(b+" "+(b<<=2)+" "+b);
System.out.println(c+" "+(c|=b)+" "+c);
}
}
56
Programarea orientata pe obiecte în limbajul Java
Conceptual, datele care aparţin acestor tipuri sunt numere reale. În limbajul Java există două
tipuri de date reale (numite şi tipuri de date flotante sau în virgulă mobilă):
Reprezentarea internă a datelor în virgulă mobilă adoptată pentru maşina virtuală Java
corespunde standardului ANSI/IEEE 754-1985.
Cifrele semnificative sunt cifrele mantisei numărului, care încep de la prima cifră diferită de
zero şi se termină cu ultima cifra diferita de zero. De exemplu, numărul 000102.3015000 are
7 cifre semnificative (cele subliniate), deoarece zerourile de la început şi de la sfârşit nu
influenţează valoarea numărului.
Acelaşi număr poate avea diferite reprezentări externe, de exemplu: 102.3015,
10.23015E1, 1.023015E2, 0.1023015E3, 0.01023015E4 etc. Pentru toate aceste numere,
reprezentarea internă va fi aceeaşi.
Menţionăm că numerele de mai sus, deşi au numai 7 cifre semnificative, vor fi reprezentate
intern ca numere de tip double, deci pe 8 octeţi fiecare. Pentru a fi reprezentate intern ca
date de tip float (pe 4 octeti) trebuie scrise cu litera f sau F la sfârşit, de exemplu 102.3015f.
În afară de valorile numerice, pentru datele în virgulă mobilă sunt prevăzute şi următoarele
valori speciale:
57
Severin Bumbaru
Atenţie: valorile Infinity, -Infinity şi NaN sunt formele externe sub care acestea se
afişează pe ecran, se tipăresc la imprimantă sau se scriu într-un fişier de text. Fiecăreia dintre
ele îi corespunde o reprezentare internă, sub forma unui şir de 4 sau 8 octeţi, corespunzător
tipului float sau double. Aceste reprezentări interne respectă regula, conform căreia, dacă
sunt interpretate ca numere în virgulă mobilă, îndeplinesc următoarele condiţii:
Infinity este mai mare decat orice valoare de tipul respectiv;
-Infinity este mai mică decât orice valoare de tipul respectiv.
În acest fel, valorile menţionate păstrează consistenţa operaţiilor de comparaţie.
Atât la tipul float, cât şi la tipul double, valoarea 0.0 are semn: poate exista atât +0.0 cât şi
-0.0. Un 0 fără semn este echivalent cu +0.0. Pentru tipul float, aceste valori sunt scrise
sub forma +0.0f şi -0.0f.
Valorile speciale Infinity, -Infinity şi NaN nu sunt literali, ci doar forme externe de
afişare a valorilor interne corespunzătoare. În consecinţă, ele nu pot fi folosite în programele
sursă. De exemplu, daca x este o variabilă în virgulă mobilă, nu putem face atribuirea
x=Infinity.
58
Programarea orientata pe obiecte în limbajul Java
astfel că vom indica aici numai unele particularităţi ale aplicării lor în cazul datelor în virgulă
mobilă.
Conversia din double în float nu conduce niciodata la alterarea valorii, ci numai la pierderea
de precizie. Aceasta se produce, întrucat se trece de la o mantisă de 52 de biţi la una de 24 biţi
, deci de la cca 17 cifre semnificative la numai cca 7 cifre semnificative în sistemul zecimal.
În schimb, conversia explicită de la date în virgulă mobilă la oricare din datele de tip întreg
poate duce la denaturarea valorii şi a semnului numărului, la fel ca în cazul tipurilor intregi.
Aceasta se întâmplă atunci când valoarea care trebuie convertită nu se încadrează în mulţimea
de valori specifică tipului în care se face conversia.
Unei variabile în virgulă mobilă i se pot atribui orice valori de tipuri numerice.
Exemplu
În programul din fişierul VirgulaMobila.java, pe care îl reproducem mai jos, se testează
diferite operaţii aritmetice în virgulă mobilă, inclusiv cele care au ca rezultate valori speciale.
class VirgulaMobila {
public static void main(String args[]) {
double a=12.7865, b=-158.07, c=0.0, d=-0.0, inf1, inf2, n;
float p=3476.15f, q=0.00237621f, r=0.0f, s=-0.0f;
System.out.println("a/b="+(a/b)+" b/a="+(b/a)+" b%a="+(b%a));
System.out.println("a/c="+(a/c)+" b/c="+(b/c));
System.out.println("a/d="+(a/d)+" b/d="+(b/d));
59
Severin Bumbaru
System.out.println("c/d="+(c/d)+" d/c="+(d/c));
System.out.println("p/q="+(p/q)+" q/p="+(q/p)+" p%q="+(p%q));
System.out.println("p/r="+(p/r)+" p/s="+(p/s));
System.out.println("a/q="+(a/q)+" p/b="+(p/b));
inf1=a/c; inf2=a/d; n=c/d;
System.out.println("a/inf1="+(a/inf1)+" a/inf2="+(a/inf2));
System.out.println("inf1/inf2="+(inf1/inf2)+" inf2/inf1="+
(inf2/inf1));
System.out.println("a*inf1="+(a*inf1)+" c*inf1="+(c*inf1));
}
}
Comparaţia
Pentru datele in virgula mobilă se aplică aceiaşi operatori de comparatie (relaţionali) ca şi
pentru numerele întregi.
Atribuirea compusă
În cazul tipurilor de date reale se aplică următorii operatori de atribuire compusă:+=, -=,
*=, /=, %=.
60
Programarea orientata pe obiecte în limbajul Java
Tipul char
În limbajul Java, reprezentarea internă a caracterelor se face pe 2 octeţi (16 biţi), în sistemul
Unicode. In acest sistem, caracterele sunt codificate în memoria internă prin numere întregi
pozitive în intervalul [0, 65535]. În fluxurile de date de intrare/ieşire şi în fişiere, caracterele
pot fi reprezentate şi în alte coduri. Codul cel mai frecvent folosit în aceste scopuri este
ASCII, în care reprezentarea caracterelor se face pe un singur octet, deci prin numere întregi
fără semn în intervalul [0, 255].
Remarcăm ca datele de tip char sunt singurul tip de date întregi fără semn din limbajul Java,
mulţimea de valori a acestui tip de date fiind intervalul de numere naturale [0, 65535].
Datele de tip char pot fi folosite în operaţii numerice, în care caz ele sunt interpretate drept
numere intregi fără semn, conform cu reprezentarea lor internă prin numere binare. Din acest
motiv, în limbajul Java datele de tip char sunt incluse în categoria celor de tipuri întregi.
În programele Java, secvenţele escape pot fi folosite pentru a reprezenta caracterele în orice
loc din textul sursa. Peste tot unde întâlneşte în textul sursă caracterul \ (bara inversă,
backslash), compilatorul consideră că urmeaza o secvenţă escape, pe care o înlocuieşte cu
caracterele Unicode corespunzătoare.
Exemplul 1
Şirul de caractere "abc\"def" conţine secventa escape \", care se va inlocui cu codul
61
Severin Bumbaru
caracterului ", obţinându-se şirul abc"def. Acelasi şir de caractere putea fi scris sub forma
"abc\u0022def", în care în loc de secvenţa escape \" s-a folosit direct cea care conţine
valoarea numerica din Unicode a caracterului ", respectiv \u0022.
Exemplul 2
Ştiind că reprezentările în Unicode ale literelor e şi t sunt, respectiv, \u0065 şi
\u0074, instrucţiunea
System.out.println("Exemplu");
poate fi scrisă sub forma
Sys\u0074\u0065m.ou\u0074.prin\u0074ln("Ex\u0065mplu");
Desigur că o astfel de înlocuire a caracterelor prin secvenţe escape nu este justificată în
cazul caracterelor obişnuite, dar este utilă în cazul folosirii caracterelor speciale, cum sunt
caracterul de trecere la lini noua '\n' şi altele.
Exemplu
În programul de mai jos, dat in fişierul TestChar.java, se testează unele operaţii cu date de tip
char.
class TestChar {
public static void main(String args[]) {
char c1='A', c2='a', c3='b', c4='1', c5='*', c6='\n';
System.out.println(c1+" "+c2+" "+c6+" "+c4+" "+c5+" "+c3);
System.out.println((int)c1+" "+(int)c2+" "+(int)c6+" "+
(int)c4+" "+(int)c5);
System.out.println(c1+c2);
System.out.println(c1*c2);
System.out.println((char)107);
System.out.println('2'+" "+(int)'2'+" "+'3'+" "+(int)'3'+
" "+('2'+'3'));
}
}
La executarea acestui program, rezultatele afişate pe ecran sunt cele de mai jos:
A a
1 * b
65 97 10 49 42
162
6305
k
2 50 3 51 101
62
Programarea orientata pe obiecte în limbajul Java
După afişarea caracterelor A şi a s-a transmis către dispozitivul de afişare caracterul '\n',
care a fost interpretat drept comanda de trecere la linie nouă. După aceasta au fost afşsate
caracterele 1, * şi b. Pe linia următoare sunt afişate caracterele deja afişate anterior, convertite
în numere întregi. Se observă că în Unicode caracterul 'A' este reprezentat prin numarul 65,
caracterul 'a' prin numarul 97, caracterul '\n' prin 10 etc.
Pe următoarele două linii sunt afişate rezultatele operaţiilor 'c1'+'c2' si 'c1'*'c2',
obţinându-se respectiv valorile numerice 162 si 6305. Se observă că, întrucât caracterele au
fost folosite ca operanzi ai unor operatori aritmetici, rezultatele obţinute sunt numerice (de tip
int).
Pe linia următoare se afişează numărul 107 convertit din int în char şi interpretat deci drept
'k';
Pe ultima linie putem observa deosebirea dintre forma externă a caracterului (literalul de tip
char) şi codificarea lui interna. Literalul de tip char '2' este caracterul 2 si nu numarul 2. În
consecinţă, acest caracter nu se reprezinta intern prin numarul întreg 2, ci prin numărul întreg
50. În mod corespunzator, caracterul '3' se reprezinta intern prin numarul 51. Suma
codurilor numerice ale celor doua caractere este, evident, numărul 101.
Întrebări
Nivel 1
1. Ce este un comentariu?
2. Ce fel de comentarii pot să apară în fişierele sursă Java?
3. Cum se reprezintă un şir în program?
4. Ce este concatenarea şi care este operatorul prin care se realizează?
5. Prin ce metode se afişează un şir de caractere pe ecran?
6. Ce sunt identificatorii şi cum se alcătuiesc ei în Java?
7. Ce sunt cuvintele cheie?
8. Ce este un literal?
9. Ce separatori se folosesc în limbajul Java?
10. Ce sunt operatorii?
11. Ce este efectul lateral al operatorului?
12. Toţi operatorii au efect lateral?
13. Ce sunt variabilele?
14. Ce este numele variabilei?
15. Ce se înţelege prin declararea variabilei?
16. Ce este o variabilă finală?
17. Ce este un tip de date?
18. Ce tipuri de date primitive există în limbajul Java?
19. Care este mulţimea de valori a tipului boolean?
20. Care este operatorul de negaţie, asupra cărui tip de date se aplică şi ce efect are?
21. Ce sunt operatorii logici & si &&?
22. Ce sunt operatorii logici | si ||?
23. Care sunt tipurile de date numerice?
24. Ce este operaţia de atribuire?
63
Severin Bumbaru
Nivel 2
1. Ce proprietăţi are operatorul de concatenare?
2. Este concatenarea asociativă? Dar comutativă?
3. Se poate oare folosi metoda print() în loc de println()?
4. Cu ce fel de caracter încep în Java numele de clase?
5. Ce deosebire este între literali şi identificatori?
6. De câte feluri sunt operatorii după numărul de operanzi?
7. Cum se plasează operatorii unari în raport cu operanzii lor? Dar cei binari?
8. Există operatori ternari?
9. Daţi o definiţie conceptului de variabilă.
10. Prin ce simbol se termina o declaraţie de variabile?
11. În ce mod se iniţializează o variabilă?
12. Ce sunt tipurile de date primitive?
13. Ce se stabileşte la definirea unui tip de date primitive?
14. Ce sunt tipurile de date derivate?
15. Ce este o clasă şi în ce fel de limbaje se foloseşte?
16. Ce deosebire există între modurile în care acţionează operatorii logici & şi &&?
17. Ce deosebire există între modurile în care acţionează operatorii logici | şi ||?
18. Ce deosebiri există între operatorii = şi ==?
19. În ce situaţii este obligatorie folosirea castului la conversiile datelor de tipuri
primitive?
20. Care sunt operatorii aritmetici unari?
21. Ce deosebire este între aşezarea operatorului de incrementare sau decrementare în faţa
operandului şi după acesta?
64
Programarea orientata pe obiecte în limbajul Java
65
Severin Bumbaru
În limbajul Java, expresia poate conţine literali, variabile, operatori, operanzi, funcţii şi
paranteze şi trebuie să poată fi evaluată (calculată), astfel încât să se obţină o valoare.
66
Programarea orientata pe obiecte în limbajul Java
De exemplu:
alpha+3.765 (s-a folosit operatorul binar +)
alpha++ (s-a folosit operatorul unar postfix ++)
-alpha (s-a folosit operatorul unar prefix -)
- o expresie cuprinsă între paranteze constituie o nouă expresie, a cărei valoare este
identică cu cea a expresiei din interior;
De exemplu:
(-176)
(alpha++)
(alpha+3.765)
De exemplu:
(alpha+3.765)*(alpha++)
Se observă că, procedând astfel, se pot construi expresii din ce în ce mai complicate.
67
Severin Bumbaru
Expresia poate fi reprezentată ca un arbore sintactic, care are ca frunze literali sau variabile,
iar ca rădăcină are valoarea expresiei. Fiecare nod al arborelui sintactic conţine un operator
sau o funcţie, care are ca operanzi valorile subexpresiilor din nodurile-fii. De exemplu,
expresia
a*b+3*c
corespunde următorului arbore:
Precedenţa operatorilor
La evaluarea unei expresii, prezinta o importanţă deosebită ordinea în care se aplică
operatorii pe care îi conţine, deoarece de această ordine poate să depindă valoarea expresiei.
În toate limbajele de programare, în care se folosesc expresii, se stabilesc şi anumite reguli de
precedenţă şi de asociativitate, pe baza cărora se stabileşte ordinea de evaluare.
68
Programarea orientata pe obiecte în limbajul Java
. [] ()
+ unar - unar
++ -- ! ~ (<tip>)
new
* / %
+ binar - binar
<< >> >>>
< <= > >=
instanceof
== !=
&
^
|
&&
||
?:
= += -= *= /= %=
<<= >>= >>>= &=
|= ^=
,
Exemplu
În programul de mai jos, a cărui sursă se găseşte în fişierul EvExpresii.java, se calculează
unele expresii cu numere întregi şi se afişează rezultatele.
/* Evaluarea expresiilor */
class EvExpresii {
public static void main(String args[]) {
int a=23, b=500, c=17, d;
System.out.println((a+b*c)+" "+((a+b)*c)+" "+(b/a*c)+
" "+(b/(a*c)));
System.out.println((-a+b*c++/3+(d=c--%4))+" "+c+" "+d);
System.out.println((d<1 && c--==0)+" "+c+" "+d);
System.out.println((d<1 & ++d<c)+" "+c+" "+d);
}
}
69
Severin Bumbaru
* are precedenţa superioară operatorului +, şi s-a obţinut ca valoare numărul întreg 8523;
- următorul operator, +, are ca operanzi numărul întreg 8523 şi şirul de caractere " ". În
consecinţă, s-a convertit numarul 8523 într-un şir (trecându-se de la forma internă a acestuia
la cea externă) şi s-a obţinut şirul "8523", care s-a concatenat cu şirul " ", obţinându-se şirul
"8523 ";
- s-a calculat expresia ((a+b)*c) obţinându-se numărul întreg 8891; de data aceasta, s-a
calculat mai întâi subexpresia a+b, deoarece este între paranteze;
- s-a convertit numărul 8891 din forma internă în cea externă şi s-a concatenat cu şirul
obtinut anterior şi cu sirul " ", rezultând şirul "8523 8891 ";
- s-a calculat expresia (b/a*c). Întrucît operatorii / şi * au acelaşi nivel de precedenţă,
calculul s-a făcut de la stânga la dreapta, astfel:
. s-a efectuat împărţirea întreagă b/a, obţinându-se valoarea 21;
. s-a efectuat înmulţirea 21*c, obţinându-se rezultatul 357.
- s-a concatenat forma externă a numărului 357 cu şirul obţinut anterior şi cu şirul " ",
obţinându-se şirul "8523 8891 357 ";
- s-a calculat expresia (b/(a*c)), efectuându-se acum mai întâi înmultirea a*c, apoi
împărţirea întreagă a valorii lui b la rezultatul înmulţirii şi s-a obţinut valoarea 1;
- s-a concatenat forma externă a acestei valori cu şirul obţinut anterior, obţinându-se şirul
"8523 8891 357 1" care a fost afişat pe ecran, după care s-a trecut la linie nouă.
Recomandăm să urmăriţi în acelaşi mod cum s-au calculat şi afisat celelalte expresii.
Menţionăm că:
- la evaluarea expresiei (-a+b*c++/3+(d=c--%4))prezinta interes cu ce valori intră în
calcul variabila c şi ce valoare are operandul din dreapta al ultimului operator +;
- avand în vedere că valoarea expresiei d<1 este false, la aplicarea operatorului && nu s-a
mai evaluat al doilea operand, deci variabila c a rămas la valoarea ei anterioară, in timp ce la
aplicarea operatorului & a fost evaluat şi al doilea operand, astfel că variabila d a fost
incrementată.
Expresia condiţională
Urmând "tradiţia" limbajului C, în limbajul Java există operatorul ternar (cu trei operanzi) ?:
numit operatorul condiţional, care este utilizat în cadrul următoarei expresii condiţionale:
operand1 ? operand2 : operand3
unde:
operand1 - expresie booleană;
operand2 şioperand3 - expresii de tipuri compatibile: fie ambii operanzi de tip boolean, fie
ambii de tipuri numerice, fie ambii de tipuri referinţă.
Exemplu
În programul de mai jos, dat şi în fişierul ExprCond.java, se exemplifică mai multe cazuri de
utilizare a expresiei condiţionale.
70
Programarea orientata pe obiecte în limbajul Java
class ExprCond {
public static void main(String args[]) {
int m=17, n=-6, p=0, q=0;
System.out.println(m>n ? m+1 : 2*n-2);
System.out.println(m<=n ? 3*m : m-n);
/* Se evalueaza numai operandul 2 */
System.out.println((m>=6 ? ++p*n : ++q-m)+" "+p+" "+q);
/* Se evalueaza numai operandul 3 */
System.out.println((m<n+3 ? m-- : n++)+" "+m+" "+n);
/* Un exemplu in care operanzii 2 si 3 sunt tot expresii
conditionale
*/
System.out.println(m>p ? (n<0? m+1 : m-2) : (m>n ? m : n));
}
}
18
23
-6 1 0
-6 17 -5
18
Din aceste rezultate putem constata cu uşurinţă că s-au evaluat de fiecare dată numai acei
operanzi, care erau necesari în evaluarea expresiei condiţionale. În ultima instrucţiune se
ilustrează şi un caz de utilizare drept operanzi a unor expresii condiţionale.
Instrucţiuni simple
În limbajul Java, procesul de calcul este controlat de instrucţiuni (enunţuri). Fiecare
instrucţiune indică una sau mai multe acţiuni pe care trebuie să le execute calculatorul.
Corpul oricărei metode este constituit dintr-o succesiune de instrucţiuni. Executarea metodei
constă în executarea acestor instrucţiuni într-o ordine determinată. Instrucţiunile pot fi
grupate în blocuri.
Instrucţiunile simple nu conţin în interiorul lor alte instrucţiuni. Există trei categorii
principale de instrucţiuni (enunţuri) simple: declaraţiile de variabile locale, instrucţiunile-
expresie şi instrucţiunea vidă. La scrierea lor se respectă următoarele reguli:
71
Severin Bumbaru
Instrucţiunile de atribuire
sunt instrucţiuni prin care unei variabile i se atribuie o nouă valoare, modificându-se
astfel valoarea avută anterior. Instrucţiunea de atribuire este o expresie de atribuire
sau de atribuire compusă urmată de caracterul ';' (punct şi virgulă) deci are
următoarea formă sintactică:
expresie_de_atribuire;
72
Programarea orientata pe obiecte în limbajul Java
Instrucţiunile de incrementare/decrementare
sunt instrucţiuni formate dintr-o expresie de incrementare sau decrementare, urmată
de caracterul ';' (punct şi virgulă). De exemplu, a++ este o expresie de
postincrementare, în timp ce
a++;
este o instrucţiune de postincrementare.
Instrucţiunile de invocare de metodă
sunt instrucţiuni prin care se cere executarea unei metode, transmiţându-i-se acesteia
valorile parametrilor. Instrucţiunea constă din numele metodei, urmat de o pereche de
paranteze care conţine valorile parametrilor şi se termină prin caracterul ';' (punct şi
virgulă). Acţiunea cerută de instrucţiune constă în executarea metodei respective.
Daca metoda este o funcţie, valoarea funcţiei se ignoră, menţinându-se numai efectele
laterale ale metodei respective. De exemplu:
System.out.println("şir de caractere");
este o instrucţiune prin care se invocă metoda println a câmpului out din clasa
System, transmiţându-i-se parametrul "şir de caractere". Acţiunea care se
realizează astfel constă din afişarea pe ecran a şirului primit ca parametru.
Exemplu
În fişierul Instructiuni.java se dă un exemplu simplu de program care conţine o secvenţa de
instrucţiuni simple: o declaraţie de variabile, două instrucţiuni de atribuire, o instrucţiune de
incrementare şi o invocare de metodă.
class Instructiuni {
public static void main(String args[]) {
double a=3.76, b=-15, c=2*a-b, d, e; // declaratie de variabile
d=3*a-7; // instructiune de atribuire
b++; // instructiune de incrementare
e=b-17; // instructiune de atribuire
/* invocare de metoda */
System.out.println("a="+a+" b="+b+" c="+c+" d="+d+" e="+e);
}
}
Pentru mai multa claritate, fiecare instrucţiune a fost scrisa pe o linie de program separată şi a
fost însoţită de un comentariu. Se permite, însă, scrierea mai multor instrucţiuni pe o singură
linie sau, invers, o instrucţiune se poate extinde pe mai multe linii. Singura restricţie este ca
trecerea de la o linie la alta să se facă la nivelul unui operator sau separator (să nu se rupă in
doua un simbol format din mai multe caractere, un identificator sau un literal). Remarcăm că
la declararea variabilei c s-a luat în consideratie faptul că variabilele a şi b au deja valori
iniţializate anterior.
73
Severin Bumbaru
1. Teorema de structură: orice program poate fi întocmit folosind numai trei structuri de
control fundamentale: structura secvenţială, structura alternativă şi structura repetitivă.
2. La conceperea programelor se recomandă să se aplice tehnica de elaborare descendentă
(în engleză Top-Down), numită şi tehnica rafinărilor succesive.
3. Domeniile de valabilitate (de vizibilitate) ale variabilelor şi structurilor de date trebuie să
fie limitate.
1. Structura secvenţială
În mod "natural", se consideră că instrucţiunile se execută în ordinea în care
acestea figurează în program. Înlănţuirea instrucţiunilor (transmiterea
"controlului" de la o instrucţiune la alta) se face, în acest caz, conform
schemei logice din figura 1.
74
Programarea orientata pe obiecte în limbajul Java
<instrucţiunea_1>
<instrucţiunea_2>
....
<instrucţiunea_n>
Există, deci, un număr oarecare (n) de instrucţiuni, care se execută una dupa alta, în ordinea
în care sunt scrise în program.
În această schemă logică, Condiţie este o expresie booleană. Executarea acestui fragment de
program decurge astfel: se verifică mai întâi dacă este satisfăcută condiţia. Dacă ea este
satisfăcută, deci expresia Condiţie are valoarea true, se execută Instrucţiunea 1, iar în caz
contrar se execută Instrucţiunea 2.
Dacă <condiţie>
atunci <instrucţiunea_1>
75
Severin Bumbaru
altfel <instrucţiunea_2>
Sfarşit_dacă
Se observă, deci, că se va executa instrucţiunea în mod repetat, cât timp este satisfăcută
condiţia. În pseudocod, această structură de control se programează astfel:
76
Programarea orientata pe obiecte în limbajul Java
Deşi, din punct de vedere al teoriei programării structurate, ciclul cu test iniţial este suficient
ca structură repetitivă, în multe limbaje de programare (inclusiv Java), din motive de
comoditate a programării se admite şi o a doua formă de astfel de structură, numită ciclu cu
test final. Schema logică a acestui ciclu este dată în figura 4.
Deosebirea dintre cele două forme de cicluri este că, în ciclul cu test iniţial, condiţia este
evaluată înainte de a se executa instrucţiunea din corpul ciclului, în timp ce în ciclul cu test
final evaluarea condiţiei se face după ce instrucţiunea a fost deja executată. Aceasta este o
deosebire importantă, deoarece, în cazul ciclului cu test initial, este posibil ca instrucţiunea să
nu se execute niciodată (dacă chiar de la prima evaluare condiţia nu este satisfacută), în timp
ce la ciclul cu test final instrucţiunea din corpul ciclului va fi executată cel puţin o dată. Aşa
dar, ciclul cu test final poate fi înlocuit prin unul echivalent cu test iniţial, dar nu şi invers.
Iată dece, în teoria programării structurate, ciclul cu test iniţial este considerat ca structură de
control fundamentală, în timp ce cel cu test final este o structură de control admisă, dar nu
fundamentală.
În pseudocod, ciclul cu test final reprezentat prin schema logică din figura 4 se scrie sub
forma generală:
execută
<instrucţiune>
cât_timp <condiţie>
În unele limbaje (de exemplu în Pascal) pentru ciclul cu test final se adoptă o schemă logică
în care singura deosebire faţă de cea din figura 4 este doar prin faptul că cele două ramuri DA
şi NU işi schimbă locurile între ele, ceeace în pseudocod se scrie sub forma:
execută
<instrucţiune>
până_când <condiţie>
77
Severin Bumbaru
În acest caz, instrucţiunea din corpul ciclului se va repeta cât timp NU este satisfacuta
condiţia, deci până când se va constata pentru prima oară că ea este satisfăcută.
În ambele cazuri, în unele variante de pseudocod instrucţiunea din corpul ciclului poate fi
înlocuită printr-o secvenţa de instrucţiuni. Dacă se are în vedere utilizarea limbajului Java,
utilizarea unei secvenţe nu este necesară, deoarece ea se poate înlocui printr-o singură
instrucţiune compusă, aşa cum se va arăta ulterior.
Tehnica rafinărilor succesive oferă o cale de a parcurge acest drum de la CE la CUM în mai
mulţi paşi, astfel încât să se pornească de la un număr mic de enunţuri (pseudoinstrucţiuni) de
nivel de abstractizare ridicat, după care - la fiecare pas de rafinare - fiecare din aceste
instrucţiuni se descompune în mai multe instrucţiuni de nivel de abstractizare mai coborât
(mai "concrete"), până când se ajunge la instrucţiuni scrise direct în limbajul de programare
folosit (de exemplu în Java). Se pune deci un accent deosebit pe abstractizarea datelor si a
instrucţiunilor. Dupa cum arăta E.W.Dijkstra, abstractizarea este calea prin care omul
stapâneşte complexitatea.
Dacă privim schemele logice ale structurilor de control fundamentale, permise de metoda
programării structurate, constatăm cu uşurinţă că atât blocurile care conţin instrucţiuni, cât şi
structurile de control admise de metoda programarii structurate au fiecare un singur punct de
intrare şi un singur punct de ieşire. Aceasta înseamnă că orice bloc care conţine o
instrucţiune poate fi înlocuit în schema respectivă printr-o structură de control. Ca urmare, se
poate aplica următoarea cale de elaborare a programului, specifică tehnicii rafinărilor
succesive:
- iniţial se consideră că programul este alcatuit dintr-o singură pseudoinstrucţiune, de înalt
78
Programarea orientata pe obiecte în limbajul Java
nivel de abstractizare, care constă chiar din specificaţia problemei pe care o va "rezolva"
programul respectiv;
- la pasul de rafinare următor, această "problemă" se descompune în două sau mai multe
"subprobleme"; aceasta înseamnă că unica pseudoinstrucţiune existentă iniţial se înlocuieşte
prin una din structurile admise de metoda programării structurate (structura secvenţială,
alternativă sau repetitivă); această structură conţine în ea una sau mai multe instrucţiuni sau
pseudoinstrucţiuni;
- la paşii de rafinare următori, fiecare pseudoinstrucţiune, care nu are corespondenta directă
în limbajul de programare utilizat, se inlocuieşte, de asemenea, prin una din structurile de
control admise; textul pseudoinstrucţiunii înlocuite poate fi, totusi, menţinut sub formă de
comentariu, astfel încât să se înţeleagă mai uşor programul;
- se continuă acest proces de rafinări succesive, până când toate instrucţiunile astfel
obţinute au corespondent în limbajul de programare utilizat.
Instrucţiuni structurate
Limbajul Java a fost alcătuit astfel, încât programatorul este obligat sa respecte principiile
programării structurate. Aceasta se datoreşte faptului ca instrucţiunile structurate ale
limbajului sunt cele prevăzute de această metodă de programare.
În plus, în limbajul Java există o structură de control dedicată tratării excepţiilor care apar în
timpul executării programului, realizată prin instrucţiunile try .. catch
Blocuri
Blocul este o secvenţă de instrucţiuni cuprinsă între acolade. Forma sintactică a blocului este,
deci, următoarea:
{
secvenţă_de_instrucţiuni
}
unde secvenţă_de_instrucţiuniconstă dintr-un număr oarecare de instrucţiuni succesive,
putând fi şi vidă. Instrucţiunile din această secvenţă pot fi atât simple, cât şi structurate,
inclusiv alte blocuri.
Din punct de vedere al programării structurate, blocul este realizarea structurii de control
secvenţiale. Aceasta inseamnă că instrucţiunile dintr-un bloc se execută una dupa alta, în
ordinea în care ele sunt scrise în program. Tocmai de aceea se şi foloseşte termenul secvenţă
79
Severin Bumbaru
Exemplu
În programul de mai jos, conţinut în fişierul Blocuri.java, se exemplifică folosirea unor
blocuri imbricate (cuprinse unul în altul). Prin comentarii s-a semnalat începutul şi sfârşitul
fiecărui bloc, deşi aceasta nu se face în mod curent. Remarcăm că însuşi corpul metodei main
(şi al oricărei alte metode) este tot un bloc. Fiecare bloc conţine atât declaraţii de variabile,
cât şi instrucţiuni-expresie
Remarcăm că:
- în expresiile prin care se calculează valoarea iniţială a variabilei u s-a luat în consideraţie
valoarea variabilei m determinată anterior;
- în blocul B, la calcularea valorii iniţiale a variabilei p s-au folosit valorile variabilelor m
si w calculate la executarea unor instrucţiuni-expresie anterioare; se confirmă astfel că
iniţializarea variabilelor nu se face la compilare, când aceste valori nu sunt cunoscute, ci la
executarea declaraţiei.
Variabilele declarate într-un bloc sunt variabile locale ale blocului respectiv. Domeniul de
vizibilitate al unei variabile locale începe în locul din program în care aceasta a fost declarată
şi se încheie la sfârşitul blocului care conţine declaraţia respectivă. Variabila este deci
"vizibilă" (poate fi utilizată) în propriul său bloc şi în toate blocurile interioare acestuia, în
domeniul de vizibilitate.
80
Programarea orientata pe obiecte în limbajul Java
Exemplu:
În programul din fişierul Blocuri.java, dat ca exemplu mai sus, variabilele m, n, u, v şi w
sunt declarate în blocul A (în însuşi corpul metodei main) şi sunt vizibile (pot fi utilizate) atât
în acest bloc, cât şi în blocurile interioare B şi C. În schimb variabilele a, p şi q pot fi
utilizate numai în blocul B, în care sunt declarate, şi în blocul C situat în interiorul acestuia.
În memoria maşinii virtuale Java, variabilele locale sunt plasate pe stiva procesului curent. La
executarea unei instrucţiuni-declaraţie, variabilele conţinute în aceasta sunt puse pe stivă, în
ordinea în care ele apar în declaraţia respectivă. Când se ajunge la sfârşitul blocului în care au
fost declarate, variabilele sunt scoase de pe stivă. Cât timp se află pe stivă, variabilele sunt
vizibile şi deci pot fi utilizate.
Structuri alternative
Instrucţiunea if
Instrucţiunea if serveşte pentru realizarea structurii alternative din programarea structurată.
Sub forma ei cea mai simplă, această instrucţiune se scrie astfel:
if(expresie_booleană) instrucţiune
unde instrucţiunepoate fi orice instrucţiune valabilă în limbajul Java: instrucţiune simplă
sau structurată, inclusiv un bloc sau un alt if. Remarcăm că în forma generală de mai sus nu
am pus la sfârşit caracterul ';' (punct şi virgulă). Dacă instrucţiune este o instrucţiune
simplă, ea include şi acest simbol.
Semnificaţia instrucţiunii if este următoarea: dacă valoarea expresiei booleene din paranteză
este true, atunci se execută instrucţiune, iar altfel nu se execută nimic, continuându-se
programul.
Exemple
1. Fie instrucţiunea
if(alpha<beta) x=2*alpha+beta;
În acest caz, instrucţiune este instrucţiunea-expresie
x=2*alpha+beta;
care se termină ea însăşi prin punct şi virgulă. Înstrucţiunea if se interpretează astfel: dacă
expresia booleană alpha<beta are valoarea true, atuncivariabileix i se atribuie valoarea
expresiei aritmetice 2*alpha+beta. Altfel (dacă expresia booleana are valoarea false)
variabila x rămâne la valoarea pe care a avut-o anterior.
2. Fie instrucţiunea
if(alpha<beta) {
int gamma=alpha-3*beta;
x=2*gamma;
y=gamma-3;
}
În acest caz, instrucţiune este un bloc, care se execută numai dacă expresia booleană
alpha<betaare valoarea true. Remarcăm ca după acolada prin care se închide blocul nu s-a
mai pus punct şi virgulă.
81
Severin Bumbaru
3. Fie instrucţiunea
if(alpha<beta)
if(beta>0) System.out.println(alpha+" "+beta);
În acest caz, instrucţiune este un alt if.
Deşi cuvântul then nu apare explicit, în specificaţia limbajului Java această forma a lui if este
cunoscută sub denumirea "instrucţiunea if-then" (if-then statement), care se traduce prin dacă
.. atunci.
Remarcăm că această instrucţiune realizează un caz particular al structurii alternative, unde
pe ramura altfel nu se efectuează nici o acţiune.
Instrucţiunea if .. else
Instrucţiunea if .. else realizează ambele ramuri ale structurii alternative şi are forma
if(expresie_booleană)
instrucţiune_1
else
instrucţiune_2
Deşi cuvântul then nu apare explicit, în specificaţia limbajului Java această instrucţiune se
numeşte if-then-else.
Exemple
1. Fie instucţiunea
if(a<b) x=2*a-b;
else x=a+b;
În acest caz, atât instrucţiune_1, cât şi instrucţiune_2 sunt instrucţiuni-expresii şi se
termină prin punct şi virgulă.
2. Fie instrucţiunea
if(a<b) {
x=2*a-b;
y=a+1;
}
else {
x=a+b;
y=b;
}
În acest caz, instrucţiune_1 şi instrucţiune_2 sunt blocuri. Remarcăm că după acolada
închisă a blocului nu s-a mai pus punct şi virgulă.
82
Programarea orientata pe obiecte în limbajul Java
Exemplu
Fie instrucţiunea
if(a<b)
if(b<c) System.out.println("A");
else System.out.println("B");
else System.out.println("C");
Interpretarea corectă este, în acest caz, că primul else aparţine celui de al doilea if, iar
ultimul else aparţine primului if. Această structură se poate întări scriind instrucţiunea
astfel:
if(a<b) {
if(b<c) System.out.println("A");
else System.out.println("B");
} else System.out.println("C");
Întrucât s-au folosit acoladele pentru a pune instrucţiunea if..else interioară sub formă de
bloc, a devenit clar pentru oricine cărui if îi apartine fiecare din cei doi else.
Instrucţiunea switch
switch (expresie) {
case valoare_1: secvenţă_1
[break;]
case valoare_2: secvenţă_2
[break;]
..........................
case valoare_N: secvenţă_N
[default: secvenţă ]
}
83
Severin Bumbaru
în care:
- switch, case si default sunt cuvinte cheie ale limbajului Java;
- expresie este o expresie de tip byte, short, int sau char;
- valoare este fie un literal de tip byte, short, int sau char, fie o expresie constantă
de tip int, adică o expresie care conţine numai literali sau variabile finale din tipurile
menţionate;
- secvenţaeste o secvenţă de instrucţiuni simple sau structurate;
- parantezele drepte roşii []nu apar în program, ci indică faptul că conţinutul lor este
opţional (poate sa lipsească).
În limba engleză, switch înseamnă comutator. Conform acestei forme generale, după cuvantul
cheie switch, există o expresie de tip int sau compatibilă cu aceasta (deci poate fi şi de tip
char, byte sau short, dar nu de tip long), a cărei valoare serveşte drept "comutator". Se
deschide apoi acolada corpului instrucţiunii, în care există mai multe "cazuri". Fiecare "caz"
începe prin cuvantul cheie case, urmat de o valoare de tip întreg, după care apar una sau mai
multe instrucţiuni (simple sau compuse) şi opţional intrucţiunea break. După ce s-au
epuizat toate cazurile, opţional se poate scrie cuvântul cheie default urmat de una sau mai
multe instrucţiuni. La sfârşit, se închide acolada corpului instrucţiunii switch.
Executarea instrucţiunii switch decurge astfel: se evaluează mai întâi expresie şi se obţine
o valoare de tip întreg, care serveşte drept comutator. Această valoare se compară, pe rând de
sus în jos, cu fiecare din valorile indicate după cuvintele cheie case, până când se găseşte
prima valoare care coincide cu cea a comutatorului. Dacă s-a găsit o astfel de valoare, se
execută toate instrucţiunile care încep de la cazul respectiv, până la prima instrucţiune break
întalnită sau, în lipsa acesteia, până la acolada de închidere a corpului instrucţiunii switch.
Dacă însă nici unul din cazuri nu conţine valoarea potrivită a comutatorului, atunci se execută
instrucţiunile care urmează după cuvântul cheie default sau, în lipsa acestuia, nu se execută
nimic.
Exemplu
În fişierul TestSwitch.java se dă programul de mai jos, în care se testează instrucţiunea switch
în două situaţii: în primul caz nu este utilizată instrucţiunea break, iar în al doilea caz este
pusă această instrucţiune la sfârşitul secvenţei de instrucţiuni corespunzătoare fiecărui caz.
Remarcăm, de asemenea, că în al doilea caz, întrucât comutatorul este de tip char, valorile
numerice corespunzătoare fiecărui caz au trebuit să se încadreze în acest tip, adică să fie
caractere sau numere întregi pozitive în intervalul [0, 65535].
class TestSwitch {
public static void main(String args[]) {
int a=7;
char c='@';
double u=15.3782;
System.out.println("Switch fara break si cu cazuri vide");
switch(a) {
case 143: u++;
System.out.println("A "+u);
case 2: u+=a;
System.out.println("B "+u);
84
Programarea orientata pe obiecte în limbajul Java
case 7: u--;
System.out.println("C "+u);
case '@':
case 148:
case 15:
case -87: u-=a;
System.out.println("D "+u);
case -12: u*=a;
System.out.println("E "+u);
case 'F': u%=a;
System.out.println("F "+u);
default: u=15.3782;
System.out.println("G "+u);
}
System.out.println("Switch cu break dupa fiecare caz
nevid");
switch(c) {
case 143: u++;
System.out.println("A "+u);
break;
case 2: u+=a;
System.out.println("B "+u);
break;
case 7: u--;
System.out.println("C "+u);
break;
case '@':
case 148:
case 15:
case 87: u-=a;
System.out.println("D "+u);
break;
case 12: u*=a;
System.out.println("E "+u);
break;
case 'F': u%=a;
System.out.println("F "+u);
break;
default: u=15.3782;
System.out.println("G "+u);
}
}
}
85
Severin Bumbaru
Se observă cu uşurinţă că, în primul caz (fără instrucţiuni break), s-a început executarea
secvenţei de instrucţiuni în punctul "case 7" şi s-a continuat până la sfârşitul blocului
instrucţiunii switch (inclusiv cazul default). Când s-a folosit instrucţiunea break, ieşirea
s-a făcut la întalnirea acestei instrucţiuni.
while (condiţie)
instrucţiune
în care
condiţie - este o expresie de tip boolean;
instrucţiune- poate fi orice instrucţiune (simplă sau structurată) a limbajului Java,
inclusiv altă instrucţiune while.
86
Programarea orientata pe obiecte în limbajul Java
cât_timp k<=n
afişează_valoarea_expresiei 2e-0.35ksin(0.17k-0.08);
k=k+1; // se trece la valoarea următoare a lui k
sfârşit_ciclu
S=1+x+x2/(2!)+x3/(3!)+...+xk/(k!)+...
în care k! este factorialul lui k. Demonstrarea existentei acestei sume (deci a convergenţei
seriei) este de domeniul analizei matematice. Întrucât, însă, noi ştim că această serie este
87
Severin Bumbaru
convergentă, putem să scriem un program pentru calcularea sumei S. Seria are un număr
infinit de termeni, deci valoarea ei exactă nu poate fi calculată direct, prin adunarea lor.
Totuşi, valoarea aproximativă a sumei S se poate obţine prin "trunchiere", adică luând în
consideraţie numai un număr finit de termeni. Vom calcula deci, de fapt, suma parţială:
Sn=1+x+x2/(2!)+x3/(3!)+...+xn/(n!)
Se ştie (si se observă cu uşurinţă) că termenii seriei sunt descrescători ca modul. Ne putem
deci opri din evaluarea sumei, în momentul în care constatăm că valoarea termenului curent
este mult mai mică decât suma parţială deja calculata. Putem deci adopta, pentru calcularea
sumei, următorul program în pseudocod:
tk=tk-1*x/k.
Întrucât în program nu este necesar să folosim indicii paşilor, vom scrie această relaţie sub
forma t=t*x/k, adică se atribuie ca nouă valoare a termenului curent t valoarea precedentă a
acestui termen, înmulţită cu raportul x/k.
88
Programarea orientata pe obiecte în limbajul Java
Programul corespunzător în limbajul Java este dat în fişierul Serie1.java. Pentru a înţelege
mai bine acest program, recomandăm să calculaţi 3-4 termeni manual pentru a urmări cum se
înlănţuie aceştia.
Forma generală a instrucţiunii de control, prin care se realizează în limbajul Java ciclul cu test
final este următoarea:
do
instrucţiune
while (condiţie);
unde, la fel ca în cazul instrucţiunii while,condiţie este o expresie de tip boolean, iar
instrucţiune este orice instrucţiune (simplă sau structurată) din limbajul Java. Remarcăm
că la sfârşitul acestei instrucţiuni (după condiţie) se pune obligatoriu ';' (punct şi virgulă).
Principala deosebire faţă de instrucţiunea while pentru ciclul cu test iniţial este că
testarea condiţiei se face după ce a fost executată instrucţiunea din corpul ciclului. În
consecinţă, corpul ciclului va fi executat cel puţin o dată.
89
Severin Bumbaru
În unele limbaje de programare, pentru realizarea ciclurilor la care numărul de paşi este
dinainte cunoscut, se foloseşte o instrucţiune de control specială, numită "ciclu cu contor". În
pseudocod, această instrucţiune poate fi scrisă în modul următor:
în care, de regulă, variabila este o variabilă (de regulă de tip întreg), numită şi contorul
ciclului,val_init şi val_fin sunt respectiv valorile iniţială şi finală ale acestei variabile,
iar pas este pasul de variaţie la fiecare parcurgere a ciclului. Pasul implicit este 1.
P=1;
pentru k de_la 5 la n execută
P=P*(a+k);
sfârşit_ciclu
Remarcăm că, în acest exemplu, instrucţiunea P=1 nu face parte din ciclu, dar ea a fost
necesară pentru a da valoarea iniţială a variabilei P.
S=0;
pentru j de_la 7 la 31 cu_pasul 3 execută
S=S+j;
sfârşit_ciclu
Remarcăm că, pentru aceleaşi calcule, se pot folosi şi ciclurile cu test iniţial sau cu test final.
Astfel, primul din aceste exemple, poate fi rescris folosind ciclul cu test iniţial astfel:
P=1;
k=5;
cât_timp k<=n execută
P=P*(a+k);
k=k+1;
sfârşit_ciclu
Remarcăm că, la trecerea de la ciclul cu contor la ciclul cu test iniţial, s-au introdus
următoarele instrucţiuni suplimentare:
- înainte de intrarea în ciclu, s-a pus instrucţiunea k=5, prin care se atribuie variabilei-
contor valoarea iniţială;
- la sfârşitul corpului ciclului s-a introdus instrucţiunea k=k+1, prin care se trece la
90
Programarea orientata pe obiecte în limbajul Java
În limbajul Java, urmând tradiţia limbajului C de la care acesta a preluat o bună parte a
regulilor de sintaxă, se foloseşte o instructiune generalizată, prin care se realizează atât ciclul
cu contor, cât şi ciclul cu test iniţial. Această instrucţiune are forma:
în care:
Respectând aceasta formă generală, calcularea produsului dat ca exemplu mai sus în
pseudocod, se programează în limbajul Java în modul urmator:
După cum se vede în forma generală, diferitele componente ale acesteia sunt opţionale; în
consecinţă, instrucţiunea for poate fi scrisă sub diferite forme echivalente, de exemplu:
a)
P=1;
k=5;
for(;k<=n;){
P*=a+k;
k++;
}
Observăm că, de data aceasta, instrucţiunea for(;k<=n;) are exact acelaşi efect, pe care l-
ar fi avut instrucţiunea while(k<=n). Remarcăm, de asemenea, că cei doi separatori ';' din
interiorul parantezei au fost mentinuţi, chiar dacă lipsesc zonele de iniţializare şi de
91
Severin Bumbaru
incrementare.
b)
for(P=1,k=5;k<=n;P*=a+k,k++);
În ultimul caz, corpul ciclului este constituit numai dintr-o instrucţiune vidă, reprezentată prin
carecterul ';' pus dupa paranteza închisă. Aceste exemple sunt date şi în programul din
fişierul TestFor.java. Tot acolo se arată că variabila care serveşte drept contor al ciclului
poate fi, în limbajul Java, şi de tip real, iar creşterea acesteia de la o parcurgere la alta a
ciclului poate fi de asemenea de tip real şi nu este obligatoriu să fie constantă. Considerăm că
exemplele din acest program sunt suficiente pentru a arăta ca instrucţiunea for permite
programatorului posibilităţi de utilizare foarte diverse, cu condiţia ca acesta să înţeleaga
corect modul ei de funcţionare.
x=0;
for(int k=0; k<n; k++)
S+=a*k;
În acest caz, variabila k a fost atât declarată, cât şi iniţializată, în zona de iniţializare a
instrucţiunii for. În consecinţă această variabilă este disponibilă în interiorul acestei
instrucţiuni, inclusiv în corpul ciclului, dar îşi pierde valabilitatea la ieşirea din acest ciclu.
Daca în zona de iniţializare apare o declaraţie, ea trebuie să fie singura instrucţiune din
această zonă. Astfel, în exemplul precedent nu ar fi fost permis, de exemplu, să se scrie
for(x=0, int k=0; k<n; k++) S+=a*k;
Este însă permis să se declare în zona de iniţializare mai multe variabile de acelaşi tip. Astfel,
exemplul anterior poate fi modificat după cum urmează:
x=0;
for(int k=0,i=9; k<n; k++, i--)
S+=(a+i)*k;
92
Programarea orientata pe obiecte în limbajul Java
Instrucţiuni etichetate
În principiu, orice instrucţiune în limbajul Java poate purta o etichetă. Instrucţiunile etichetate
au forma
etichetă : instrucţiune
unde
etichetă este un identificator;
instrucţiuneeste o instrucţiune simplă sau structurată.
Exemplu
alpha: x=2*a+b;
Se permite să se folosească drept etichete chiar şi identificatori care mai sunt folosiţi în
acelasi program ca nume de variabile, clase, metode etc., fară a se crea prin aceasta confuzii,
deoarece compilatorul face distincţie între etichete şi nume în funcţie de contextul în care
acestea sunt utilizate.
break [etichetă];
continue [etichetă];
unde etichetăeste eticheta unei instrucţiuni şi este un identificator. Eticheta este opţională
(de aceea a fost scrisă de noi între paranteze drepte).
S-a arătat deja cum se foloseşte instrucţiunea break în corpul unei instrucţiuni switch. Iată
acum un exemplu de folosire într-un ciclu cu test iniţial:
k=1; S=0;
while(S+t!=S) {
93
Severin Bumbaru
t*=a/k;
S+=t;
if(t<1.0e-10*S) break;
k++;
}
În acest caz, în momentul în care este satisfacută condiţia (t<1.0e-10*S) din instructiunea
if se iese forţat din ciclu, chiar dacă condiţia principală de continuare a ciclului (S+t!=S)
este încă satisfacută.
k=1; S=0;
for(;;) {
t*=a/k;
if(S+t==S) break;
S+=t;
k++;
}
Întrucât iniţializarea ciclului s-a făcut înainte de instructiunea for, iar condiţia de ieşire din
ciclu şi pregatirea continuarii ciclului se găsesc în corpul acestuia, în paranteza lui for toate
cele trei zone au ramas vide.
s=0;
for(k=0; k<20; k++) {
x*=a;
s+=x;
if(k<16) continue;
System.out.println("k="+k+" x="+x+" s="+s);
}
În acest caz, corpul ciclului se va repeta pentru valori ale lui k de la 0 la 19, dar instrucţiunea
System.out.println(...) va fi "sărită" pentru valori ale lui k mai mici decât 16, trecându-se
direct la acolada de închidere a corpului ciclului, astfel că afişările se vor face numai
începand cu k=16.
Necesitatea utilizării unei etichete apare atunci când există două astfel de structuri imbricate
una în alta, iar noi dorim ca prin break să se iasa forţat din cea exterioară, ca în exemplul
urmator:
ciclu1: while(w>0.05){
w/=a;
94
Programarea orientata pe obiecte în limbajul Java
Testarea a diferite situaţii de utilizare a instructiunii breakse face în programul din fişierul
TestBreak.java.
Tratarea excepţiilor
În timpul executării programului, pot apare anumite situaţii care altereaza desfăşurarea
normală a acestuia. Când apare o astfel de situaţie, de regulă, se generează o excepţie sau o
eroare prin care se semnalează incidentul care a avut loc. Excepţiile şi erorile pot fi generate
atât de echipamente (excepţii sau erori hardware), cât şi de programe (excepţii software).
95
Severin Bumbaru
În limbajul Java, există posibilitatea de a se trata prin program diferitele excepţii care apar în
timpul execuţiei. În acest fel, programatorul poate să prevadă căi alternative de continuare a
executării programului, fără a mai fi necesară oprirea executării lui. În mediul de lucru Java,
la apariţia unei excepţii se generează un obiect special numit excepţie, care conţine informaţia
despre excepţia respectivă. Acest obiect poate fi captat prin program, iar informaţia conţinută
în el poate fi utilizată pentru a decide calea pe care se va merge în continuare în derularea
programului. Toate obiectele de excepţie care se referă la acelaşi tip de incident formează o
clasă de excepţii.
Tratarea prin program a excepţiilor se face, în limbajul Java, folosind instrucţiunea try
urmată de una sau mai multe clauze catch şi, opţional, de o clauză finally sub forma
urmatoare:
try {
secventa
}
catch (ClasaExceptie1variabila1) {
secventa1
}
catch (ClasaExceptie2 variabila2) {
secventa2
}
...............
catch (ClasaExceptieN variabilaN) {
secventaN
}
[finally {
secventa_finalizatoare
}]
În engleza, try înseamnă încearcă, iar catch înseamnă prinde. În corpul instrucţiunii try se
pune secvenţa de instrucţiuni în care este posibil ca, la executarea programului, să apară
excepţii. Dacă excepţia a apărut într-un punct situat în corpul unei instrucţiuni try, aceasta
instrucţiune se termină în mod abrupt, astfel că restul instrucţiunilor nu se mai execută,
trecându-se direct la acolada de închidere a blocului. Se parcurg apoi, una după alta, clauzele
catch, până se ajunge la cea care are între paranteze numele clasei excepţiei care s-a produs şi
se execută secvenţa din corpul acestei clauze.
Clauza finally este opţională şi conţine o secvenţă de instructiuni care se vor executa în final,
adică după ce a fost tratată excepţia prin una din clauzele catch, sau dacă nu a fost tratată prin
96
Programarea orientata pe obiecte în limbajul Java
Exemplul 2: În fişierul TestExcept3.java se reia programul din exemplul anterior, dar a fost
suprimată prima din clauzele catch. Executând acest program, se poate observa că şi de data
aceasta excepţia aritmetică a fost captata, însa de clauza catch(Exception e).
Exemplul 3: În fişierul TestExcept4.java se reia programul din Exemplul 1, dar după captarea
excepţiei s-a introdus si clauza finally. Se observă ca secvenţa din aceasta clauză este
executată chiar daca excepţia a fost captată prin catch. Mai mult, în fişierul
TestExcept5.java s-a reluat acelaşi program, dar s-a modificat valoarea numitorului fracţiei,
astfel că nu mai are loc o împărţire la zero. Se observă ca secvenţa din clauza finallyeste
executată şi în acest caz.
Întrebări
Nivel 1
1. Ce este o expresie?
2. Cum se stabileşte tipul unei expresii?
3. Fie a si b variabile de tip byte, c de tip int, u de tip float şi v de tip double.
Care sunt tipurile următoarelor expresii: a+b, a*c, a*c+u, u-2*v.
4. Ce este precedenţa operatorilor?
5. În ce ordine se aplică operatorii cu acelaşi nivel de precedenţă?
6. În ce ordine se evaluează operanzii unui operator binar?
7. Ce forma sintactică are expresia condiţională şi care este semnificaţia acestei
expresii?
8. Ce este o instrucţiune?
9. Ce deosebire este între instrucţiunile simple şi cele structurate?
10. Ce este instrucţiunea vidă şi cum se scrie?
11. Ce este o instrucţiune-expresie?
12. Ce deosebiri există între o expresie şi o instrucţiune-expresie?
13. În ce constă teorema de structură din metoda programării structurate?
14. Care sunt categoriile de instrucţiuni de control admise de metoda programarii
structurate?
97
Severin Bumbaru
15. Ce fel de structură de control este un bloc şi care este forma lui sintactică?
16. Ce sunt variabilele locale şi care este domeniul lor de vizibilitate?
17. Prin ce instrucţiuni se realizează structura de control alternativă şi care este forma lor
sintactică?
18. Ce sunt excepţiile?
19. Ce deosebire este între excepţii şi erori?
20. Care este forma sintactică a instrucţiunii de tratare a exceptiilor şi cum trebuie ea
interpretată?
Nivel 2
1. Construiţi arborele sintactic al următoarei expresii: (a+2*b/c)*(2*d-3)+3*e+5
2. Construiţi arborele sintactic al următoarei expresii: a=(b+=2*a)-c++*(d-=c+1)
3. Construiţi arborii sintactici ai tuturor expresiilor din fişierul EvExpresii.java.
4. Explicaţi rezultatele obţinute la executarea programului din fişierul ExprCond.java.
5. Ce este o instrucţiune de atribuire şi care este efectul ei?
6. Care este deosebirea dintre valorile expresiilor de preincrementare şi
postincrementare? Dar între efectele instrucţiunilor de preincrementare şi
postincrementare?
7. Ce este o instructiune de invocare de metodă şi care este efectul ei?
8. Care este schema logică a structurii de control secvenţiale şi cum trebuie interpretată?
9. Care este schema logică a structurii de control alternative şi cum trebuie interpretată?
10. Care este schema logică a ciclului cu test iniţial şi cum trebuie interpretată?
11. Care este schema logică a ciclului cu test final şi cum trebuie interpretată?
12. Unde sunt plasate variabilele locale în memoria maşinii virtuale Java?
13. Când sunt eliminate din memorie variabilele locale?
14. Cum se stabileşte cărui if îi aparţine clauza else în cazul instructiunilor if
imbricate?
15. Care este forma sintactică a instrucţiunii switch şi cum este ea interpretată?
16. Ce rol are instrucţiunea break în cadrul unui bloc switch?
17. Ce este o etichetă şi ce formă sintactică are o instrucţiune etichetată?
18. Poate o etichetă să aibă acelaşi identificator cu cel al unui nume de variabilă sau de
metodă?
19. Cum acţionează o instrucţiune break fără etichetă în corpul unui ciclu?
20. Cum acţionează o instrucţiune continue fără etichetă în corpul unui ciclu?
21. În ce situaţie se foloseşte o instrucţiune break cu etichetă?
22. În ce situaţie se foloseşte o instrucţiune continue cu etichetă?
23. La ce serveşte clauza catch şi unde se plasează?
24. La ce serveşte clauza finally şi unde se plasează?
98
Programarea orientata pe obiecte în limbajul Java
Tipul referinţă
În limbajul Java există două categorii de tipuri de date: tipuri primitive şi clase.
Fiecare variabilă are un nume, un tip şi o valoare şi este plasată într-o anumita locaţie de
memorie. În locaţia de memorie respectivă se găseşte chiar valoarea variabilei. Pe
programator nu il interesează insă adresa de memorie la care se găseşte variabila, ci doar
numele, tipul şi valoarea acesteia.
99
Severin Bumbaru
Clasele sunt tipuri de date structurate specifice programării orientate pe obiecte. Clasele se
definesc de programatori şi se grupează în pachete de clase. La crearea unui program nou,
programatorul poate utiliza clasele din pachetele deja existente, sau poate creea propriile sale
clase. În program, fiecare clasă poate avea mai multe instanţe, numite obiecte.
În memoria maşinii virtuale Java, obiectele sunt plasate într-o zonă de memorie specială,
numită memorie dinamică (în engleză: heap, adică "gramadă"). Localizarea în memorie a
obiectelor se face cu ajutorul unor variabile speciale, numite variabile referinţă. Valoarea
unei variabile referinţă nu este obiectul însuşi, ci o referinţă la acest obiect. Din această
cauză, se consideră că variabilele referintă aparţin unui tip de date numit tip referinţă.
Mulţimea de valori a acestui tip este mulţimea referinţelor, adică mulţimea locaţiilor
(adreselor) din memoria dinamică. Operaţiile permise asupra datelor de tip referinţa sunt cele
date de operatorii de atribuire (=), egalitate (==) şi inegalitate (!=) care au aceleaşi
semnificaţii şi mod de utilizare ca pentru toate celelalte tipuri de date.
Referinţele din Java sunt similare pointerilor din unele limbaje de programare tradiţionale,
cum sunt Pascal şi C. Există, totuşi, deosebiri importante:
- valorile variabilelor-pointer sunt adrese din memorie care pot fi cunoscute de programator
(de exemplu pot fi afişate) De asemenea, unei variabile-pointer i se poate atribui ca valoare o
adresa dată explicit. În schimb, adresele la care se găsesc în memorie obiectele indicate de
referinţele din Java nu pot fi cunoscute de utilizator;
- în limbajele tradiţionale, pointerii pot indica nu numai adresele în memorie ale unor
structuri de date, ci şi pe cele ale unor variabile simple. În java, referinţele pot fi folosite
numai pentru obiecte;
- în limbajele traditionale (de exemplu în C/C++) se pot face asupra pointerilor operatii
aritmetice, ceeace nu este permis asupra referinţelor în Java.
Atentie: s1, s2 si s3 nu sunt şiruri de caractere (adică obiecte din clasa String) ci sunt doar
referinţe către şiruri de caractere. Aceasta înseamnă că valoarea fiecăreia deintre ele nu este
un obiect, ci o referinţă la obiect. Obiectul propriu-zis nu se găseşte în memorie în acelaşi loc
100
Programarea orientata pe obiecte în limbajul Java
Datele unei clase se numesc câmpuri, sau variabile membre. Câmpurile pot fi statice (ale
clasei) sau nestatice (ale instanţei). Când clasa este instanţiată, în memoria maşinii virtuale
Java se construieşte un obiect (o instanţă a clasei respective). Obiectul conţine numai câmpuri
nestatice. Câmpurile statice se păstreaza în memorie într-un singur loc, care este rezervat
clasei respective.
Metoda este o funcţie, care întoarce o valoare şi poate avea, de asemenea, efect lateral. Ca şi
câmpurile, metodele pot fi statice(ale clasei) şi nestatice (ale instanţei). Metodele statice pot
invocă numai câmpurile statice ale clasei respective, în timp ce metodele nestatice pot invoca
atât câmpurile statice, cât şi pe cele nestatice (ale unei instanţe a clasei respective). Dacă
valoarea întoarsă de metodă este void, metoda respectivă este o procedură şi trebuie să aibă
obligatoriu efect lateral.
Invocarea unei metode statice (a clasei) se face printr-o expresie de forma
nume_clasa.nume_metoda(parametri_efectivi)
a cărei valoare este valoarea întoarsă de funcţia respectivă. O astfel de invocare de funcţie
poate fi deci folosită ca o componentă într-o altă expresie. De exemplu, expresia
Math.sqrt(a) serveste pentru a calcula rădăcina patrată a lui a, în care scop este invocată
funcţia sqrt, care este o metodă statică a clasei Math (clasa funcţiilor matematice uzuale).
Putem folosi această invocare de metodă într-o expresie mai complicată, de exemplu
x=2*Math.sin(a)+3;
Invocarea unei metode nestatice (a instanţei) se face sub forma
referinţa_la_obiect.nume_metodă(parametri_efectivi)
deci numele metodei nu mai este calificat prin (însoţit de) numele clasei, ci prin cel al
variabilei referinţă la obiectul respectiv, sau prin o expresie care are ca valoare o astfel de
referinţă. De exemplu, daca r1 este o variabilă referinţă care indică un anumit obiect din
memorie, iar met(a)este o metodă nestatică a clasei căreia îi aparţine acest obiect, atunci
r1.met(a) are ca efect invocarea metodei met pentru obiectul indicat de r1. În acest fel,
metoda met(a) va folosi atât câmpurile (nestatice ale) obiectului indicat de referinţa r1, cât şi
câmpurile (statice ale) clasei căreia îi aparţine acest obiect.
Dacă o metoda are efect lateral, ea poate fi invocată şi sub forma de instrucţiune. Această
instrucţiune constă numai din expresia de invocare a metodei, urmată de punct_şi_virgulă. În
acest caz, valoarea întoarsă de metodă (valoarea expresiei) este ignorată, folosindu-se numai
101
Severin Bumbaru
Este evident că metodele care întorc void (procedurile) pot fi invocate numai sub formă de
instrucţiuni, fiind folosite pentru efectul lor lateral.
Constructori
Constructorul este o procedură specială, prin care se construiesc obiecte dintr-o anumită
clasă. Constructorul are întotdeauna acelaşi nume cu clasa. În schimb, o clasă poate avea
mai mulţi constructori, care pot să difere între ei prin numărul şi/sau tipul argumentelor.
Constructorul alocă în memoria dinamică spaţiul necesar pentru un obiect din clasa căreia îi
aparţine şi iniţializează câmpurile acestui obiect cu valori care depind de argumentele sale.
Efectul lateral al acestei expresii este construirea în memorie a unui obiect din clasa căreia îi
aparţine constructorul, iar valoarea acestei expresii este referinţa la obiectul nou construit.
De exemplu, expresia
new String("acesta este un sir")
are ca efect (lateral) construirea în memorie a şirului de caractere "acesta este un sir", iar
valoarea expresiei este referinţa către acest şir. Să considerăm acum următoarele două
instrucţiuni:
String s1=new String("abcd_1234"), s2;
s2=new String("alpha");
Prima dintre ele este o declaraţie, prin care se specifică faptul că s1 şi s2 sunt variabile
referinţă, ale căror valori sunt referinţe la obiecte din clasa String. Totodată, se alocă în
memorie şirul "abcd_1234" şi se atribuie ca valoare iniţială a variabilei s1 referinţa la acest
şir.
A doua dintre instrucţiunile de mai sus, este o instrucţiune de atribuire, în care variabilei s2 i
se atribuie valoarea expresiei new String("alpha"). La executarea acestei expresii, se
obţine ca efect lateral alocarea în memorie a şirului "alpha", iar referinţa la acest şir se
atribuie ca valoare a variabilei s2
Moştenirea şi polimorfismul
Moştenirea este una din proprietăţile fundamentale ale claselor în programarea orientată pe
obiecte. Ea constă în faptul că dintr-o clasă se pot deriva alte clase. Clasa de baza se mai
numeste şi superclasă, iar clasele derivate se numesc şi subclase.
102
Programarea orientata pe obiecte în limbajul Java
Polimorfismul este o alta proprietate fundamentală a claselor. În limbajul Java este posibil ca
o metodă a superclasei să fie redefinită în subclasă. Aceasta înseamnă că ea va avea în
subclasă acelaşi nume şi aceeasi lista de parametri şi acelaşi tip de valoare întoarsă ca în
superclasă, dar va avea un comportament diferit.
Să considerăm, de exemplu, că exista clasa Poligon, în care există metoda double arie().
Aceasta metodă este o funcţie, a cărei valoare este aria poligonului. Sa considerăm acum că
Triunghi şi Patrat sunt doua clase derivate din clasa Poligon. În ambele clase derivate va
exista metoda double arie(), dar este evident că modul de calcul al ariei triunghiului diferă
de cel al calculării ariei patratului şi, cu atât mai mult, de modul de calcul al ariei unui
poligon oarecare.
Vom arata ulterior că lipsa moştenirii multiple este compensată în Java prin faptul că fiecare
clasa poate avea mai multe interfeţe.
De exemplu, având în vedere că clasa String este derivată din clasa Object, urmatoarele
instrucţiuni sunt corecte:
Object ob;
String s=new String("abcd");
ob=s;
În primele două instrucţiuni, s-au declarat variabilele-referinţă ob din clasa Object şi s din
clasa String, ultima fiind iniţializată cu o referinţă către şirul "abcd", care a fost creat
folosind operatorul new. În a treia instrucţiune, variabilei-referinţă ob i se atribuie valoarea
variabilei-referinţă s. În consecinţă, acum variabila ob din clasa Object are ca valoare o
referinţa către obiectul "abcd" din clasa String.
103
Severin Bumbaru
Pachetul poate avea subpachete. Daca pachetul p are subpachetul q, atunci p.q este numele
complet (numele calificat) al subpachetului q. Acest subpachet poate avea, la rândul sau, alte
subpachete.
Declaraţia import
Pentru a putea utiliza într-un fişier-sursa Java un anumit pachet (subpachet) de clase, la
începutul fişierului respectiv trebuie pusă declaraţia
import nume_pachet.*;
încare nume_pachet este numele calificat al pachetului respectiv. De exemplu, pentru a
utiliza orice clase din pachetul java.io se pune declaraţia
import java.io.*;
Pentru clasele din pachetul java.lang nu este necesară o declaraţie de import, acestea fiind
importate implicit.
Declaraţia
import nume_pachet.*;
se numeşte declaraţie de import la cerere. Ea nu are semnificaţia că se importă toate clasele
din pachetul (subpachetul) respectiv, ci numai acele clase care sunt utilizate efectiv în
program.
104
Programarea orientata pe obiecte în limbajul Java
Clasa Object
Clasa Object este rădăcina ierarhiei de clase a platformei Java. Este singura clasă care nu
are o superclasă. Orice altă clasa este derivată direct sau indirect din clasa Object.
Conceptual, instanţele clasei Object sunt obiecte oarecare, fără atribute precizate. Variabilele
referinţă la Object se folosesc atunci când, la elaborarea programului, se consideră că lor li
se pot da ca valori referinţe la orice fel de obiecte, indiferent de clasa căreia îi aparţin.
În clasa Object sunt declarate, de asemenea, metode care se consideră ca trebuie să existe în
toate celelalte clase. Unele din aceste metode vor trebui redefinite în clasele derivate, pentru a
efectua acţiuni specifice acestor clase.
Metodele clasei Object sunt descrise în Java API. Noi vom prezenta aici numai o parte dintre
ele, care vor fi utilizate în cele ce urmează.
ceeace înseamnă că nivelul de acces este public, valoarea întoarsă (valoarea obţinută la
evaluarea acestei funcţii) este de tip boolean, iar unicul argument aparţine clasei Object.
Expresia a.equals(b) în care a şi b sunt două obiecte, întoarce valoarea true dacă a
este identic cu b şi valoarea false în caz contrar.
Aşa cum este ea definită în clasa Object, metoda equals() întoarce valoarea true numai
dacă cele doua obiecte comparate sunt cu adevărat identice, adică au aceeaşi adresă în
memorie, ceeace se poate scrie şi sub forma expresiei a==b. În clasele derivate, această
metoda poate fi redefinită, respectând principiul polimorfismului, astfel încât să compare cele
două obiecte după conţinut, chiar dacă ele nu au aceeaşi referinţă.
105
Severin Bumbaru
Metoda hashCode() este utilă atunci când obiectele trebuie plasate în tabele de dispersie,
cum sunt cele care fac parte din clasa java.util.Hashtable şi java.util.HashMap.
şi întoarce reprezentarea sub forma de şir de caractere (de obiect din clasa String) a
obiectului căruia i se aplică. Asa dar, expresia a.toString(), în care a este un obiect,
întoarce reprezentarea sub forma de şir a obiectului a. În cazul obiectelor aparţinând clasei
Object, aceasta metodă întoarce un şir de forma java.lang.Object@<cod_dispersie> în
care <cod_dispersie> este codul de dispersie al obiectului respectiv, exprimat în
hexazecimal. În clasele derivate aceasta metodă este redefinită, astfel încât să se obţină o
reprezentare sub formă de şir a conţinutului obiectului respectiv.
şi are rolul de a întoarce o clonă a obiectului căruia i se aplică, deci întoarce un obiect identic
cu acesta, dar situat la o altă adresă de memorie şi deci având o altă referinţă. În consecinţă,
după ce se execută instrucţiunea
b=a.clone();
în care a şi b sunt referinţe la obiecte, expresia a==b va avea valoarea false, deoarece
valorile variabilelor referinţă a şi b sunt diferite, în timp ce expresia a.equals(b) va
întoarce în mod normal valoarea true, deoarece cele două obiecte comparate au conţinuturi
identice.
106
Programarea orientata pe obiecte în limbajul Java
b=a;
Întrucât metoda clone() este protejată (are nivelul de acces protected) ea nu poate fi
utilizată decât pentru obiectele din clasa Object. În schimb, ea poate fi redefinită la clasele
descendente, astfel încât sa devină publică.
Metodele prezentate aici (exceptând, desigur, metoda clone()) sunt testate în programul din
fişierul TestObject.java.
Clase de excepţii
În pachetul java.lang există şi numeroase clase de excepţii. Instanţele acestor clase sunt
creeate de către maşina virtuală Java atunci când se produce o excepţie, adică o situaţie
anormală în procesul de calcul. Toate aceste clase sunt descrise în documentatia Java API.
Vom menţiona aici numai două dintre ele, celelalte urmând să fie indicate la descrierea
claselor care conţin metode care pot genera excepţii.
În limbajul Java se face distincţie între excepţie şi eroare. Se consideră ca excepţiile sunt
incidente care pot fi captate prin mecanismul try .. catch şi pot fi deci tratate prin
program, în timp ce erorile sunt incidente grave, care - de regulă - nu pot fi tratate prin
program ci produc oprirea executării acestuia.
Clasa Exception
Această clasă este rădăcina ierarhiei claselor de excepţii. În consecinţă, atunci când dorim ca
in clauza catch sa fie captată orice fel de excepţie, scriem această clauză sub forma
catch(Exception e) {
instructiuni_de_tratare_a_exceptiei e
}
unde e este numele simbolic (identificatorul) dat excepţiei captate. Clasa are doi constructori:
public Exception()
creează un obiect din clasa Exception (deci "o excepţie") care nu conţine nici un mesaj.
107
Severin Bumbaru
public Exception(String s)
creează un obiect din clasa Exception, care conţine un mesaj sub forma şirului s. Prin acest
mesaj se indică, de regulă, ce incident a condus la generarea excepţiei respective.
Vom arăta ulterior cum putem folosi aceşti constructori pentru a genera propriile noastre
excepţii.
Clasa Exception nu are propriile ei metode.Totuşi, atât pentru clasa Exception, cât şi pentru
toate celelalte clase de excepţii se pot folosi metodele superclasei Object. În particular, se
poate folosi pentru instanţele oricărei clase de excepţii metoda toString(), care pune
instanţa respectivă sub forma unui şir de caractere. Acest şir conţine, de regulă, un mesaj
privind felul exceptiei respective.
Clasa ArithmeticException
Instanţele acestei clase sunt generate de maşina virtuală Java atunci când are loc o situaţie
anormală la efectuarea unei operaţii aritmetice, cum ar fi, de exemplu, împărţirea la zero a
unui număr întreg. Instanţa respectivă conţine un mesaj care indică ce fel de excepţie s-a
produs.
Exemple de utilizare a claselor de excepţii s-au dat deja în capitolul "Tratarea excepţiilor".
Clasa String
Toate şirurile în limbajul Java, inclusiv literalii-şir, de exemplu "ABCdef123", sunt obiecte
ale clasei String. Obiectele din această clasă sunt constante, adică şirurile conţinute în ele
nu pot fi modificate. Dacă este necesar să se folosească şiruri modificabile, se recurge la clasa
StringBuffer.
În afară de metodele pe care le oferă, clasa String conţine şi suportul necesar pentru
operatorul de concatenare'+'. Prin concatenarea a două şiruri se obţine un nou şir, rezultat
din punerea celor două unul în continuarea celuilalt. De exemplu, expresia
"ABC"+"defg" are ca valoare şirul "ABCdefg".
Dăm aici o parte din constructorii şi metodele mai frecvent utilizate ale clasei String. O
prezentare mai amplă există în indexul de clase. Vă recomandăm să o consultaţi, pentru a
cunoaşte ce constructori şi metode conţine. Amintim că prezentarea completă se găseşte în
documentaţia Java API de pe Internet.
108
Programarea orientata pe obiecte în limbajul Java
Construirea unui obiect din clasa şir se face cel mai frecvent cu constructorul String(String
s). Acest constructor creeaza în memorie o copie a şirului s primit ca argument.
int length()
Întoarce lungimea acestui şir (numărul de caractere conţinute).
String trim()
Întoarce un nou şir, obţinut din acest şir prin eliminarea spaţiilor de la început şi de la
sfârşit.
109
Severin Bumbaru
Întoarce un nou şir, care conţine caracterele acestui şir, începând de la poziţia
beginIndex, până la sfârşit.
Exemplu
În programul din fişierul TestStr.java se exemplifică utilizarea clasei String.
Clasa StringBuffer
Obiectele clasei StringBuffer implementează şiruri care pot fi modificate atât ca lungime,
cât şi sub aspectul caracterelor pe care le conţin. În engleză buffer înseamnă zonă tampon.
Aşa dar, un "StringBuffer" este modelul unei zone tampon de memorie, în care se pot adăuga
noi caractere la şirul existent şi în care se pot înlocui total sau parţial caracterele existente cu
altele.
Principalele operaţii asupra unei astfel de "zone tampon" sunt metodele append() şi
insert(), prin care se poate adăuga un şir nou în coada celui existent, sau se înserează acest
şir nou în interiorul celui existent.
Construirea unui nou StringBuffer se poate face cu unul din constructorii StringBuffer(),
StringBuffer(int length) sau StringBuffer(String str). Primul dintre ei
construieşte o zonă tampon de caractere, având o capacitate iniţială predefinită; al doilea una
de capacitate iniţială length, iar al treilea construieşte o zonă tampon care conţine iniţial
şirul str.
Există, de fapt, o familie de metode append(), care diferă între ele prin tipul argumentului:
append(boolean b), append(byte b), append(short s), append(int i),
append(long l), append(char c), append(float f), append(double
d),append(String str), append(Object obj).
110
Programarea orientata pe obiecte în limbajul Java
Toate aceste metode adaugă în coada zonei tampon argumentul lor, convertit într-un şir.
Există, de asemenea, o familie de metode insert(), care diferă prin tipul celui de al doilea
argument:
insert(int offset, boolean b), insert(int offset, byte b), insert(int
offset, short s), insert(int offset, int i), insert, int offset, long l),
insert(int offset, char c), insert(int offset, float f), insert(int offset,
double d), insert(int offset, String str), insert(int offset, Object obj).
Toate aceste metode înserează în zona tampon, pe poziţia offset, cel de al doilea argument
convertit în şir.
O prezentare mai amplă a clasei StringBuffer este dată în indexul de clase, iar descrierea
completă se găseşte pe Internet în documentaţia Java API.
Exemplu
În fişierul TestStrB.java este dat un exmplu de program în care se testează clasa StringBuffer.
Clasa Class
O caracteristică importantă a limbajului şi platformei Java este că clasele şi interfeţele
utilizate în program sunt prezente în memoria maşinii virtuale Java în timpul executării
programului, sub forma de instanţe ale clasei Class. În consecinţă, se pot obţine în timpul
executării unui program informaţii despre clasele cărora le aparţin obiectele din memorie.
Clasa Class nu are un constructor public. În schimb, putem obţine un obiect din această clasă
folosind metoda getClass() a clasei Object. Există şi instanţe ale clasei Class pentru
tipurile de date primitive. Acestea sunt conţinute sub forma de câmpuri statice în clasele
acoperitoare ale tipurilor primitive respective.
public String getName() - întoarce numele calificat al unei entităţi (clase, interfeţe,
tip primitiv) reprezentată de un obiect din clasa Class;
public boolean isAssignableFrom(Class cls) - întoarce true dacă clasa căreia i
se aplică metoda este o superclasă a clasei cls, primită ca argument;
public boolean isInterface() - întoarce true dacă metoda este aplicată unei
instanţe a clasei Class care reprezintă o interfaţă;
public boolean isPrimitive() - întoarce true dacă metoda este aplicată unui obiect
din clasa Class care reprezintă un tip de date primitiv;
public Class getSuperclass() - întoarce o instanţă a clasei Class care reprezintă
superclasa obiectului căruia i se aplică această metodă.
111
Severin Bumbaru
- Figura 1 -
În aceasta figură, clasele acoperitoare de tip sunt reprezentate cu negru. Clasa Number este o
clasa abstractă, din care sunt derivate toate clasele acoperitoare pentru tipuri de date
numerice. Fiecare din celelalte clase acoperă un anumit tip de date primitiv. Clasa Character
acoperă tipul char, iar clasa Integer acoperă tipul int. Toate celelalte clase acopera tipul
primitiv al carui nume îl poartă. Spre deosebire de numele tipurilor primitive, care sunt scrise
cu litere mici, numele claselor acoperitoare încep (ca la orice alte clase) cu majuscule.
Toate clasele acoperitoare sunt clase finale, deci din ele nu mai pot fi derivate alte clase.
Fiecare instanţă a unei clase acoperitoare conţine un câmp de date nestatic în care se
pastrează o valoare aparţinând tipului primitiv de date corespunzator clasei respective. De
exemplu, un obiect din clasa Boolean conţine un câmp de tip boolean care, la rândul lui,
conţine valoarea logică true sau false. În mod similar, un obiect din clasa Double conţine
un câmp de tip double în care există un număr în virgulă mobilă în dublă precizie.
În afară de câmpul de date nestatic, fiecare clasă acoperitoare conţine mai multe câmpuri de
date statice finale, în care se păstrează diferite constante (variabile finale) specifice tipului de
date corespunzător.
Unele dintre metode sunt prezente în toate clasele acoperitoare, altele sunt specifice fiecărei
clase sau unui grup de astfel de clase. Prezentăm aici succint principalele metode prezente în
toate clasele:
112
Programarea orientata pe obiecte în limbajul Java
Clasele acoperitoare oferă, de asemenea, metode prin care se pot converti datele din tipurile
respective din forma externă (de şiruri de caractere) în cea interna şi invers.
Clasa Boolean
Clasa Boolean acoperă tipul primitiv boolean. Clasa are doi constructori publici:
Boolean(boolean value) şi Boolean(String s). Primul primeşte ca argument o
expresie cu valoare de tip boolean, iar al doilea primeşte un şir de caractere, care conţine
forma externă a unei valori de tip boolean.
113
Severin Bumbaru
Remarcăm că aceste metode se aplica tuturor claselor acoperitoare pentru numere, realizând
conversia numărului conţinut într-o instanţă a unei clase acoperitoare numerice către tipul
primitiv dorit de utilizator.
Clasa Integer este clasa acoperitoare pentru tipul primitiv int. Clasa conţine următoarele
câmpuri de date statice:
public static final int MAX_VALUE - conţine valoarea maximă pentru tipul de
date int, adică valoarea 2147483647;
public static final int MIN_VALUE - conţine valoarea minimă pentru tipul de date
int, adică -2147483648;
public static final Class TYPE - conţine un obiect din clasa Class cu informaţii
despre tipul primitiv int.
Clasa oferă un numeroase metode, atât de instanţă, cât şi statice, care sunt utile când se
lucrează cu numere întregi. Dintre acestea menţionăm:
114
Programarea orientata pe obiecte în limbajul Java
d/ metode ale instanţei (în plus, faţă de cele menţionate la punctele a/ şi b/):
Exemplu:
Programul din fişierul TestInt.java testează utilizarea unora dintre metodele clasei Integer.
Executând acest program se poate constata că analiza sintactică a şirului primit ca argument
decurge în acelaşi mod la metodele parseInt(String), valueOf(String) şi la constructorul
Integer(String). Se acceptă numai şirurile care reprezintă cu adevărat numere întregi cuprinse
în domeniul de valori al tipului int. Se admite ca numărul să fie precedat de semnul - (minus),
dar nu de semnul +. Se observă, de asemenea, că la conversia de la Integer către byte sau
short este posibil să se piardă cifrele cele mai semnificative prin trunchiere.
115
Severin Bumbaru
numere reale (cu analiza sintactică corespunzătoare) şi a numerelor reale în şiruri. Vom
prezenta aici clasa Double, dar aceleaşi facilităţi există şi în clasa Float.
Clasa Double (şi similar clasa Float) oferă următoarele câmpuri de date statice:
public static final double MAX_VALUE - cea mai mare valoare pozitivă de tip
double;
public static final double MIN_VALUE - cea mai mică valoare pozitivă de tip
double;
public static final double NaN - valoarea NaN (Not a Number) pentru tipul
double;
public static final double POSITIVE_INFINITY - valoarea Infinitypentru tipul
double;
public static final double NEGATIVE_INFINITY - valoarea -Infinity pentru
tipul double;
public static final Class TYPE - un obiect din clasa Class cu informaţii despre
tipul primitiv double.
Constructori:
Metode:
Clasa Double oferă numeroase metode utile în lucrul cu date de tip double, dintre care
mentionăm:
116
Programarea orientata pe obiecte în limbajul Java
d/ metode ale instanţei (în plus, faţă de cele menţionate la punctele a/ şi b/):
Exemplu:
Programul din fişierul TestDouble.java testează principalele metode ale clasei Double.
Remarcăm că, în acest caz, a fost considerat corect şi un sir care reprezintă un număr real
precedat de semnul +. Pentru reprezentarea formatului intern al numărului s-a folosit metoda
doubleToLongBits(), combinată cu metodele toBynaryString() şi toHexString()din
clasa Long.
Clasa Character
Fiecare obiect al clasei Character "acoperă" o valoare primitiva de tip char. În plus, clasa
Character oferă metode utile în lucrul cu caractere. Reamintim că, în limbajul Java,
caracterele sunt reprezentate pe 16 biţi, în sistemul Unicode.
Câmpuri statice:
Clasa Character conţine numeroase câmpuri de date statice, prezentate în documentaţia Java
API. Cele mai multe dintre ele sunt coduri numerice ale diferitelor categorii de caractere
(litere majuscule, litere minuscule, cifre zecimale, simboluri matematice etc), folosite în
metodele care verifică dacă un anumit caracter corespunde categoriei respective. Printre ele
există însă şi câmpul
public static final Class TYPE
care conţine un obiect din clasa Class cu informaţii despre tipul primitiv char.
117
Severin Bumbaru
Constructori:
Metode statice:
Clasa Character oferă numeroase metode statice utile în lucrul cu caractere, dintre care
menţionăm:
public static boolean isDigit(char ch) - întoarce true dacă argumentul ch
este cifră;
public static boolean isLetter(char ch) - întoarce true dacă argumentul este
literă;
public static boolean isLowerCase(char ch) - întoarce true dacă ch este literă
mică;
public static boolean isUpperCase(char ch) - întoarce true daca ch este literă
majusculă;
public static boolean isLetterOrDigit(char ch) - verifică dacă ch este literă
sau cifră;
public static boolean isWhitespace(char ch) - verifică dacă ch este un spaţiu
liber sau un alt caracter asimilabil acestuia, de exemplu caracter de trecere la linie nouă, de
întoarcere a carului, de tabulare orizontală sau verticală, de separare a fişierelor sau
înregistrărilor etc (vezi documentaţia).
public static boolean isSpaceChar(char ch) - verifică dacă ch este caracterul
spaţiu;
public static char toLowerCase(char ch) - întoarce caracterul ch convertit în
litera mică; dacă nu este literă, îl lasă neschimbat;
public static char toUpperCase(char ch) - întoarce caracterul ch convertit în
litera majusculă; dacă nu este literă, îl lasă neschimbat;
public static int getNumericValue(char ch) - întoarce valoarea numerica
Unicode a caracterului ch ca un întreg nenegativ;
public static int digit(char ch, int radix) - întoarce valoarea numerică a
caracterului ch considerat ca cifra a sistemului de numeraţie cu baza radix; dacă în sistemul
respectiv nu există o astfel de cifră, întoarce -1;
public static char forDigit(int digit, int radix) - întoarce caracterul prin
care este reprezentată cifra de valoare digit în sistemul de numeraţie cu baza radix; daca în
sistemul respectiv nu exista o asemenea cifră, întoarce caracterul nul ('\u0000');
Metode nestatice:
118
Programarea orientata pe obiecte în limbajul Java
public int compareTo(Object obj) - dacă argumentul obj este un obiect din clasa
Character, actionează similar cu metoda precedentă; altfel generează excepţia
ClassCastException;
Exemplu:
În fişierul TestChar.java este dat un program de testare a metodelor oferite de clasa
Character.
Clasa Void
Clasa Void nu este instanţiabilă (nu poate avea obiecte) şi "acoperă" tipul primitiv void. Ea
conţine numai un singur câmp static
public static final Class TYPE
care conţine un obiect din clasa Class cu informaţii despre tipul primitiv void.
Clasa Math
Clasa Math este foarte utilă în calcule ştiinţifice şi inginereşti. Ea conţine un număr mare de
funcţii matematice (funcţii trigonometrice, logaritmice, exponenţiale etc) şi două constante
matematice: numărul e şi numărul pi.
Constantele sunt reprezentate sub forma următoarelor două câmpuri statice finale ale clasei:
public static final double E - numărul e (baza logaritmilor naturali);
public static final double PI - numarul pi (raportul dintre perimetrul şi
diametrul cercului).
Aceste constante se folosesc în expresiile din program sub forma Math.E şi Math.PI.
Funcţiile matematice se prezintă în această clasă sub forma de metode statice. Menţionăm
aici numai câteva din aceste funcţii, cu precizarea că arcele (unghiurile) se exprimă în radiani:
public static double sin(double a) - sinusul trigonometric sin a.
public static double cos(double a) - cosinusul trigonometric cos a.
public static double tan(double a) - tangenta trigonometrică tg a.
public static double asin(double a) - arcsin a.
public static double acos(double a) - arccos a.
public static double atan(double a) - arctg a.
a
public static double exp(double a) - funcţia exponentială e .
public static double log(double a) - logaritmul natural ln a.
public static double sqrt(double a) - radacina patrată a lui a.
119
Severin Bumbaru
Utilizarea în expresiile din program a acestor funcţii se face, ca la toate metodele statice,
calificând numele funcţiei prin numele clasei. De exemplu, sin(2*u+3) se va scrie
Math.sin(2*u+3).
Clasa System
Clasa System conţine câmpuri şi metode utile pentru realizarea legăturii dintre aplicaţie şi
sistemul de execuţie Java (cel care implementează maşina virtuală Java). Această clasă nu
poate fi instanţiată.
Câmpuri statice:
Clasa System are trei câmpuri statice care reprezintă unităţile standard de intrare/ieşire ale
sistemului:
public static final InputStream in - este intrarea standard a sistemului. De
regulă, aceasta este tastatura, dar poate fi şi alt dispozitiv indicat de utilizator.
public static final PrintStream out - este ieşirea standard a sistemului. De
regulă este unitatea de afişare standard (ecranul) dar poate fi şi alt dispozitiv indicat de
utilizator. Afişarea datelor pe ecran se face, de regulă folosind metoda
System.out.println(sir) sau System.out.print(sir).
public static final PrintStream err - unitatea standard de ieşire pentru erori. De
regulă este aceeaşi ca pentru obiectul out, dar poate fi şi alt dispozitiv indicat de utilizator.
Metode:
Dăm aici numai metodele care sunt utilizate de noi în acest curs. Descrierea completă a
tuturor metodelor clasei System poate fi gasită în documentaţia Java API.
public static void exit(int status)- provoaca incheierea executării
programului. Argumentul acestei metode este un cod de stare care se transmite maşinii
virtuale Java. Prin convenţie, 0 înseamnă încheiere normală a executării aplicaţiei, iar un cod
diferit de zero indică încheiere anormală (cu cod de eroare). Metoda se foloseşte în program
sub forma instructiunii System.exit(stare);.
120
Programarea orientata pe obiecte în limbajul Java
Tablouri
Conceptul de tablou
Tabloul (în engleză Array) este o structură de date de acelasi tip, numite componente ale
tabloului, care sunt specificate prin indici. În programare, tabloul poate fi privit ca o colecţie
indexată de variabile de acelaşi tip.
Exemple tipice de tablouri sunt vectorul şi matricea din matematică. Un vector este o colecţie
indexată de componente cu un singur indice. De exemplu x=[x0, x1, ... , xn-1] este un
vector cu n componente. Componentele au acelaşi "nume" cu vectorul, dar se disting prin
indici, care specifică poziţia componentei respective în cadrul tabloului. În limbajul Java, la
fel ca în limbajele C/C++, indicii încep de la 0. Întrucât componentele vectorului sunt dispuse
pe o singură direcţie în spaţiu, spunem că este un tablou unidimensional. La nivel conceptual,
se consideră că tabloul ocupă o zonă compactă de memorie, în care componentele sale sunt
aşezate în ordinea crescătoare a indicilor, din care cauză mai este numit şi masiv.
Matricea este un tablou bidimensional. Componentele matricei sunt ordonate pe două direcţii
în spaţiu, iar poziţia fiecărei componente este indicată prin doi indici: primul specifică linia,
iar al doilea coloana în care se găseşte componenta respectivă. Iată un exemplu de matrice cu
4 linii şi 5 coloane:
În acest exemplu, numele matricei, ca şi numele fiecărui element al ei, este a. Poziţia
componentei în cadrul matricei este specificată prin cei doi indici. Conform convenţiei de
indexare din limbajul Java, indicii incep de la zero.
Pot exista şi tablouri cu mai mult de două dimensiuni. Astfel, un tablou tridimensional poate
fi imaginat ca un volum (o carte), având mai multe pagini, fiecare pagină fiind un tablou
bidimensional (o matrice). În acest caz, primul indice specifică linia, al doilea - coloana, iar al
treilea - pagina în care se găseşte componenta respectivă. În mod similar, un tablou cu patru
dimensiuni poate fi privit ca o serie de volume; fiecare componentă, în acest caz, are patru
indici, cel de al patrulea fiind numărul volumului în cadrul seriei. Putem, desigur, continua
raţionamentul şi pentru tablouri cu mai mulţi indici.
121
Severin Bumbaru
Tipul tabloului coincide cu tipul componentelor sale. Componentele pot aparţine unor tipuri
de date primitive, sau unor clase.
Întrucât tablourile sunt obiecte, pentru indicarea lor în program se folosesc variabile
referinţă.
122
Programarea orientata pe obiecte în limbajul Java
Iniţializarea unei variabile referinţă la tablou cu componente aparţinând unei anumite clase se
poate face atât cu tablouri din clasa respectivă, cât şi din clase descendente ale acesteia. De
exemplu, în declaraţia
Object tab1[]=new Object[2], tab2[]=new String[3],
tab3[]={"aaa","bbb","ccc"};
variabila tab1 este initializata cu o referinţă la un tablou de componente din clasa Object,
în timp ce variabilele tab2 şi tab3 sunt iniţializate cu referinţe la tablouri de şiruri, clasa
String(ca orice alta clasă) fiind descendentă a clasei Object.
123
Severin Bumbaru
Utilizarea tablourilor
Componentele tablourilor pot fi utilizate ca orice variabile simple. Referinţa la o componentă
de tablou se face prin numele tabloului, insoţit de indicele componentei pus între paranteze
drepte. De exemplu, u[3] este componenta de indice 3 a tabloului u, iar aa[i] este
componenta de indice i a tabloului aa. Indicele poate fi orice expresie de tip întreg, cu
condiţia ca valoarea acesteia să nu iasa din cadrul domeniului de indici admis pentru tabloul
respectiv.
Un exemplu de utilizare a variabilelor indexate s-a dat deja în programul din fişierul
InitTab1.java , când au fost afişate valorile componentelor tablourilor. Alte exemple se dau în
programul din fişierul Tab1.java. Este instructiv să urmărim în figurile următoare cum
evoluează datele din memorie în timpul executării acestui program.
- Fig. 1 -
Remarcăm deosebirea importantă dintre tabloul de tip double şi cel de tip String. Primul
dintre ele are drept componente date de tip primitiv. În consecinţă, "celulele" tabloului conţin
chiar valorile de tip double ale componentelor corespunzătoare. În schimb, cel de al doilea
este un tablou de obiecte din clasa String, deci componentele lui sunt, de fapt, variabile
referinţă la obiecte String, iar aceste obiecte sunt reprezentate în memorie separat. În
ambele cazuri, componentele tabloului sunt tratate ca nişte variabile al căror tip este
corespunzător declaraţiei. Ştim însă că variabilele de tipuri primitive au ca valori chiar date
primitive, în timp ce pentru obiecte se folosesc variabile referinţă.
În figura 2 este reprezentată situaţia creeată după ce s-au executat instrucţiunile de atribuire
124
Programarea orientata pe obiecte în limbajul Java
b[0]=-12.7; b[1]=283.6;
- Fig. 2 -
Întrucât variabilele referinţă a[] şi b[] indică acelaşi tablou, este normal ca valorile
componentelor a[i] sunt şi acum aceleaşi cu ale componentelor b[i], ceeace se constată şi din
afişarea prin program a datelor respective.
În figura 3 este reprezentată situaţia creată după executarea instrucţiunii b=new double[4].
- Fig. 3 -
Prin operatorul new s-a alocat în memorie un nou tablou cu 4 componente double, iar lui
b[] i s-a dat ca valoare referinţa la acest tablou. Imediat după iniţializare componentele
noului tablou au valoarea zero, deoarece aceasta este valoarea implicită pentru tipurile de
date numerice. În schimb, valoarea variabilei referinţă a[] a ramas aceeaşi, pe care a avut-o
anterior. Acestor componente putem sa le dăm acum valori prin program.
125
Severin Bumbaru
În schimb, atribuirile următoare necesită conversie explicită (prin cast), deoarece se fac de la
superclasă la clasă:
str2=(String[])tob1;
b=(int[])ob1;
str3=(String[])ob3;
Pentru a face referinţă la componente din tablourile referite de variabilele ob1 sau ob3 este
necesară, de asemenea, conversie explicită, deoarece ob1 siob3 nu au fost declarate ca
tablouri. Se va scrie deci: ((int[])ob1).length, ((int[])ob1)[k],
((String[])ob3).length, ((String[])ob3)[j].
Nu trebuie, insa, facuta conversie explicită în cazul componentelor tabloului referit prin
tob1[], deoarece vsrisbila tob1 a fost declarată ca referinţă la tablou, iar clasa Object este
superclasă a clasei String. Este, deci permisă referinţa tob1[k].
Exemplele de mai sus, si altele, sunt testate în programul din fişierul ConvTip1.java. În
acelaşi program, se testează şi numele claselor-tablouri întoarse de metoda getName() a
clasei Class. Explicarea codificărilor respective este dată în documentaţia java API, la
descrierea acestei metode.
126
Programarea orientata pe obiecte în limbajul Java
numărul de componente poate fi diferit de la o linie la alta (deci liniile "matricei" pot avea
lungimi diferite). Tabloul cu doi indici este privit ca un tablou ale cărui componente sunt
referinţe la tablouri cu câte un singur indice. Putem, deci, să ne imaginăm un "tablou
coloană" care conţine în fiecare componentă o referinţă către un "tablou linie". Fiecare din
aceste "tablouri linie" are propria sa variabilă length şi deci propria sa lungime. În schimb,
variabila length a "tabloului coloană" reprezintă lungimea acestui tablou, adică numărul de
linii. "Tipul tabloului" este, de fapt, tipul componentelor "liniilor" acestuia.
127
Severin Bumbaru
128
Programarea orientata pe obiecte în limbajul Java
- Figura 1 -
Se observă că variabila w conţine numai o referinţă către un tablou cu trei componente care,
la rândul lor, conţin referinţe către trei tablouri care conţin cele trei linii ale tabloului
bidimensional indicat de variabila w. In aceasta situaţie, cele trei "linii" ale tabloului
bidimensional pot să fie situate în locuri diferite din memorie, fără a mai forma o zonă
compactă. În schimb, fiecare "linie" este, în acest caz, un tablou compact, ale cărui
componente sunt valori primitive de tip double.
- Figura 2 -
Componentele tabloului sunt aici, de fapt, referinţe la obiecte din clasa String. La nivel
conceptual însă, noi privim acest tablou ca şi când ar avea drept componente însăşi aceste
obiecte. Pentru a le distinge mai uşor, în figura 2 obiectele din clasa String au fost trasate cu
culoare albastră.
Numărul de componente
Ştim că fiecărui tablou unidimensional îi este asociată o variabilă length, care are ca valoare
"lungimea" tabloului, adică numărul de componente ale acestuia. Ce reprezintă variabila
length în cazul unui tablou bidimensional? Sa privim din nou figura 1. Întrucat w.length
este numărul de componente din tabloul unidimensional referit de variabila w, înseamnă că el
este egal cu numărul de linii al tabloului bidimensional. În schimb, numărul de componente
129
Severin Bumbaru
din linia referită de componenta w[i] a tabloului w este dat de variabila w[i].length. De
exemplu, numărul de componente din linia de indice 0 este w[0].length. În programul din
fisierul Tab2.java se determină astfel numărul de linii şi numărul de componente din fiecare
linie pentru fiecare din tablourile bidimensionale iniţializate în programul respectiv.
Componentele de tablou astfel referite pot fi folosite în orice expresii din program, la fel ca
variabilele simple. Iată doua exemple de instrucţiuni, în care se folosesc componentele
tabloului bidimensional w:
t=2*w[1][0]*Math.cos(3.5*w[1][1]-0.23);
w[j][k+1]=2*w[j-1][k]-w[j][k-1];
Bineînţeles, trebuie avut grijă ca indicii să nu iasă din domeniile admise pentru fiecare din ei
deoarece, în caz contrar, vor apare excepţii la executarea programului.
Să urmarim programul din fişierul Tab2a.java, făcând şi schemele tablourilor din memorie in
diferite etape de execuţie a acestuia. Se fac mai intâi următoarele declaraţii cu iniţializări de
tablouri:
int a[][]={{-5,12,52},{8,-4},{},{105}}, b[]={31,-3,17,24}
130
Programarea orientata pe obiecte în limbajul Java
- Figura 3-
Situatia creată în memorie după această secvenţă de atribuiri este reprezentată in figura 4.
- Figura 4 -
În aceasta figură, pentru a fi urmărite mai uşor, modificarile au fost făcute cu culoare roşie. A
fost creat, prin operatorul new, un nou tablou cu trei elemente, care a devenit noua linie a[2].
S-au atribuit valori pentru a[2][0] si a[2][2], iar componenta a[2][1] a rămas la valoarea
implicită 0 cu care a fost iniţializată. Fosta linie a[3], formată numai dintr-o singură
componentă cu valoarea 105, a rămas fără referinţă şi va fi eliminată de către colectorul de
reziduuri de memorie. Noua linie a[3] este acum fostul tablou b, iar tabloul unidimensional
indicat de variabila referinta b este acum acelasi cu linia a[1]. Toate aceste modificari pot fi
verificate executând programul din fişierul Tab2a.java .
131
Severin Bumbaru
Tablouri eterogene
Prin definiţie, componentele unui tablou trebuie să fie toate de acelaşi tip. În programarea
orientată pe obiecte, această restricţie a fost "relaxată", în sensul că un tablou poate avea drept
componente şi obiecte aparţinând claselor descendente din clasa de bază. Faptul că clasa
Object este superclasă a oricărei alte clase din limbajul Java, inclusiv a claselor de tablouri,
permite să se creeze tablouri eterogene, adică tablouri cu componente care aparţin unor clase
foarte diferite. Acestea pot fi structuri complicate de date, realizate pornind de la structura de
"tablou de obiecte". Să consideram, de exemplu, structura de date din figura 5.
132
Programarea orientata pe obiecte în limbajul Java
- Figura 5 -
class Parametri {
public static void main(String args[]) {
if(args.length==0)
System.out.println("Nu ati introdus parametri in linia de
comanda");
else {
System.out.println("Aplicatia are urmatorii parametri:);
for(int i=0; i<args.length; i++)
System.out.println(args[i]);
}
}
}
Lungimea tabloului args (numărul de componente) este, după cun ştim, args.length. În
acest program, dacă numărul de parametri este nul, se afişează mesajul "Nu aţi introdus
133
Severin Bumbaru
Constatăm, deci, că unicul separator între parametri este spaţiul liber, iar numerele sunt
preluate tot sub forma de şiruri de caractere. Putem rula acum acest program în mod repetat
cu diferite seturi de parametri, pentru a urmări cum se comportă. Putem constata de asemenea
că, dacă între doi parametri există mai multe spaţii libere succesive, ele sunt tratate ca şi un
singur spaţiu.
Întrebări
Nivel 1
134
Programarea orientata pe obiecte în limbajul Java
Nivel 2
1. Ce se găseşte în zona de memorie afectată unei variabile care aparţine unui tip
primitiv?
2. Ce se găseşte în zona de memorie afectată unei variabile referinţă?
135
Severin Bumbaru
136
Programarea orientata pe obiecte în limbajul Java
Declararea claselor
Declararea claselor;
Declararea câmpurilor. Câmpuri statice şi câmpuri ale
instanţei. Iniţializarea câmpurilor. Valori iniţiale implicite;
Declararea metodelor. Metode statice şi metode ale
instanţei;
Metode cu acelaşi nume; signatura metodei;
Transferul parametrilor către metode la invocarea
metodelor;
Metode care întorc o referinţă la un obiect construit în
corpul lor;
Metode care au ca argumente şi/sau ca valori întoarse
referinţe la tablouri;
Metode recursive; comparaţie între iteraţie şi recursie;
Metode care generează excepţii; instrucţiunea throw şi
clauza throws;
Clase publice
Un exemplu: clasa complex
Declararea clasei complex
Utilizarea clasei complex
Distrugerea obiectelor de către colectorul de reziduuri;
Metoda finalize
Întrebări.
Declararea claselor
Până în prezent, s-a arătat modul în care putem utiliza în programul nostru clase existente în
biblioteci (în pachetele de clase). Vom studia în continuare cum putem crea propriile noastre
clase.
class NumeClasa {
declaratii_de_membri
}
137
Severin Bumbaru
Corpul clasei cuprinde declaraţii de membri ai clasei respective. Acestea pot fi:
- declaraţii de câmpuri;
- declaraţii de constructori;
- declaraţii de metode.
Nu este obligatoriu ca într-o clasă să existe toate aceste categorii de declaraţii. Pot exista, de
exemplu, clase în care apar numai declaraţii de metode. În principiu, pot exista şi clase care
conţin numai câmpuri şi nu conţin metode, deşi astfel de situaţii apar foarte rar în practică.
Declararea câmpurilor
Declaraţiile de câmpuri servesc pentru a descrie structura de date specifică clasei respective.
Câmpurile se mai numesc şi variabile membre şi pot fi ale clasei sau ale instanţei (ale
obiectului). Se preferă denumirea de câmpuri, pentru a le deosebi de variabilele locale ale
metodelor.
Câmpurile instanţei se declară la fel ca variabilele locale ale metodelor, numai că declaraţia
respectivă nu apare în blocul unei metode, ci în corpul clasei. De exemplu:
int m=172, n=2*m-4, r;
Aceste câmpuri pot avea valori diferite pentru fiecare instanţă a clasei respective. În
consecinţă, câmpurile instanţei sunt plasate în zona de memorie rezervată instanţei
respective, astfel că ele sunt distincte pentru fiecare instanţă.
Câmpurile clasei se numesc şi câmpuri statice. Declararea unor astfel de câmpuri se face
asemănător cu cea a câmpurilor de instanţă, dar declaraţia are în faţă, în acest caz,
modificatorul static.De exemplu:
static double u=3.65, v=2.87*u-3.1, x;
La iniţializarea câmpurilor de instanţă se pot folosi atât valori ale câmpurilor statice, cât şi ale
altor câmpuri de instanţă. În schimb, la iniţializarea câmpurilor statice se pot folosi numai
valori ale altor câmpuri statice.
Câmpurile statice (ale clasei) sunt plasate în memorie în zona rezervată clasei căreia îi aparţin
şi nu în cea rezervata instanţelor. În consecinţă, câmpurile clasei există în memorie într-un
singur exemplar, care este accesibil fiecărei instanţe.
Valorile iniţiale implicite ale câmpurilor: dacă nu sunt iniţializate explicit, câmpurile
statice şi cele nestatice primesc valori implicite astfel:
- câmpurile booleene primesc valoarea false;
- câmpurile numerice primesc valoarea 0 (chiar şi cele de tip char, care este tot tip
numeric!);
- câmpurile referinţa primesc valoarea null.
138
Programarea orientata pe obiecte în limbajul Java
explicit. Dacă o variabilă locală apare într-o expresie fără să aibă o valoare atribuită anterior,
compilatorul Java semnaleaza această situaţie ca o eroare de programare.
Acest exemplu este dat în scop didactic, pentru a ilustra declararea, iniţializarea şi utilizarea
câmpurilor. Crearea unei clase fără metode nu corespunde principiului de baza al
programării orientate pe obiecte, conform căruia o clasă conţine atât date, cât şi metodele
prin care acestea sunt tratate. În consecinţă, declararea unor clase fără metode se evită în
practica programării orientate pe obiecte, chiar dacă o astfel de declaraţie este permisă. De
altfel, chiar daca declaraţia de clasă nu conţine constructori sau metode, ea are un
constructor implicit (fără parametri) şi moşteneşte metodele superclasei, în particular
metodele clasei Object.
class Proba1 {
static int m=9, n=m-3;
int a=7, b=m*a+1, c, d;
char c1='@', c2;
String s1="un sir", s2=s1+" extins", s3;
}
În această clasă, s-au definit câmpurile statice (ale clasei) m şi n şi câmpurile nestatice (de
instanţă) a, b, c şi d - toate de tip int. S-au declarat, de asemenea, câmpurile c1 şi c2 de
tip char şi câmpurile s1, s2 şi s3 care contin referinţe la instanţe ale clasei String. Unele
din aceste câmpuri sunt iniţializate, altele au valori iniţiale implicite. La iniţializarea
câmpului de instanţă b s-a folosit şi valoarea câmpului static a. Câmpurile c şi d sunt
iniţializate implicit cu valoarea 0.
Amintim o deosebire importantă între modul cum sunt plasate în memorie valorile primitive
şi obiectele (instanţele claselor): valorile primitive sunt plasate chiar în zona de memorie
rezervată variabilelor respective, în timp ce instanţele claselor sunt plasate în memoria
139
Severin Bumbaru
dinamică. În consecinţă:
- valorile câmpurilor primitive statice m şi n sunt plasate în memorie o singură dată, în
spaţiul rezervat clasei Proba1;
- valorile câmpurilor primitive nestatice a, b, c, d, c1 şi c2 sunt plasate în câmpurile
corespunzătoare ale instanţelor clasei Proba1, deci acestea vor avea câte o valoare pentru
fiecare instanţă;
- în campurile s1, s2 si s3 (care, în cazul nostru, sunt tot nestatice, deci se plaseaza în
fiecare instanţă), ca valori se pun numai referinţe la instanţe ale clasei String. În consecinţă,
şirurile "un şir" şi "un şir extins" se vor creea în memoria dinamică, sub forma de
obiecte ale clasei String, iar în câmpurile s1 şi s2 ale fiecărei instanţe a clasei Proba1 se vor
pune numai referinţe la aceste şiruri. Câmpul s3 va fi iniţializat cu referinţa null.
Clasa Proba1 poate fi utilizată în alte clase Java la fel ca o structură de date (înregistrare)
"tradiţională" cum ar fi struct în limbajul C, sau record în limbajul Pascal. Un exemplu de
aplicaţie, în care se utilizează clasa Proba1, este clasa TestClasa1 din fişierul
TestClasa1.java, pe care o reproducem în continuare.
class TestClasa1 {
public static void main(String args[]) {
/* Se declara doua referinte catre instante ale clasei Proba1,
iar prima din ele se si initializeaza
*/
Proba1 p1=new Proba1(), p2;
/* Se mai creaza o instanta si se atribuie lui p2
*/
p2=new Proba1();
/* Se afiseaza unele campuri ale instantelor p1 si p2; */
System.out.println("Campuri din p1: n="+p1.n+" m="+p1.m+" a="+
p1.a+" b="+p1.b+" c="+p1.c+" c1="+p1.c1+" c2="+p1.c2+
" (int)p1.c2="+(int)p1.c2);
System.out.println("Campuri din p2: n="+p2.n+" m="+p2.m+" a="+
p2.a+" b="+p2.b+" c="+p2.c+"\n s1="+p2.s1+" s2="+p2.s2+
" s3="+p2.s3);
/* Afisarea campurilor statice calificandu-le cu numele clasei */
System.out.println("Afisarea campurilor statice: m="+Proba1.m+
" n="+Proba1.n);
/* Modificam atribuim p1.a si p2.a valori diferite, apoi reafisam
toate campurile
*/
p1.a=12; p2.a=-9;
System.out.println("Dupa modificarea valorilor campurilor a:");
System.out.println("Campuri din p1: n="+p1.n+" m="+p1.m+" a="+
p1.a+" b="+p1.b+" c="+p1.c);
System.out.println("Campuri din p2: n="+p2.n+" m="+p2.m+" a="+
p2.a+" b="+p2.b+" c="+p2.c);
/* Modificam campul static p1.m si reafisam p1.m, p2.m si Proba1.m
*/
p1.m=-12;
System.out.println("Dupa modificare: p1.m="+p1.m+" p2.m="+p2.m+
" Proba1.m="+Proba1.m);
140
Programarea orientata pe obiecte în limbajul Java
La compilarea fişierului sursă TestClasa1.java, vor fi create două fişiere bytecode, câte unul
pentru fiecare clasă, numite în mod corespunzător Proba1.class şi TestClasa1.class. Pentru
executarea aplicaţiei se va folosi comanda
java TestClasa1
Clasa Proba1 nu poate fi pusă în execuţie în acest mod, deoarece ea nu conţine metoda main.
Această clasă poate fi folosită numai în cadrul altei clase.
Campuri din p1: n=6 m=9 a=7 b=64 c=0 c1=@ c2= (int)p1.c2=0
Campuri din p2: n=6 m=9 a=7 b=64 c=0
s1=un sir s2=un sir extins s3=null
Afisarea campurilor statice: m=9 n=6
Dupa modificarea valorilor campurilor a:
Campuri din p1: n=6 m=9 a=12 b=64 c=0
Campuri din p2: n=6 m=9 a=-9 b=64 c=0
Dupa modificare: p1.m=-12 p2.m=-12 Proba1.m=-12
Dupa modificare: p1.n=-25 p2.n=-25 Proba1.n=-25
Dupa atribuire: p1.c=1234 p2.c=0
Declararea metodelor
În programarea orientată pe obiecte, clasa conţine, în mod normal, nu numai câmpuri de date,
ci şi metodele prin care se tratează aceste câmpuri.
141
Severin Bumbaru
Sub aspect conceptual, metoda este o funcţie sau o procedură, care foloseşte drept date atât
valorile argumentelor sale, cât şi câmpurile clasei căreia îi aparţine metoda respectivă.
Instrucţiunea return
Dacă funcţia întoarce o valoare (diferită de void), aceasta se indică prin instrucţiunea
return expresie;
Efectul acestei instrucţiuni este următorul: se evalueaza expresia expresie şi se încheie
executarea funcţiei respective, întorcând valoarea astfel obţinută. În consecinţă, chiar dacă
după instrucţiunea return mai apar în corpul funcţiei respective şi alte instrucţiuni, acestea
nu vor mai fi executate.
Dacă metoda nu întoarce o valoare (întoarce void), folosirea instrucţiunii return nu este
absolut necesară, încheierea execuţiei făcându-se când se ajunge la acolada prin care se
142
Programarea orientata pe obiecte în limbajul Java
sfârseşte blocul funcţiei. Totuşi, dacă este necesar să se încheie în mod abrupt executarea
corpului funcţiei, se poate folosi instrucţiunea return fără expresie.
Metode statice
La declararea metodelor statice, în faţa tipului valorii întoarse se pune modificatorul static.
Metodele care conţin în declaraţie acest modificator se numesc statice sau ale clasei, spre
deosebire de metodele obişnuite care sunt ale instanţei. În corpul metodelor statice se pot
folosi numai câmpurile statice ale clasei respective şi se pot invoca numai alte metode statice
ale acestei clase.
Exemplu
În fişierul Cercuri.java se declară două clase pentru cercuri: clasa Cerc1, în care aria şi
circumferinţa se calculeaza prin metode ale instanţei, şi clasa Cerc2, care conţine metode
statice.
class Cerc1 {
static final double PI=3.141592653589793;
double r;
double arie() {
return PI*r*r;
}
double circumferinta() {
return 2*PI*r;
}
}
class Cerc2 {
static final double PI=3.141592653589793;
Clasa Cerc1 conţine câmpul static final PI şi câmpul de instanţă r (raza cercului). Aceasta
întrucat numărul PI este o constantă valabilă pentru orice cerc, în timp ce raza cercului diferă
de la o instanţă la alta. Cele doua metode de instanţă, arie() şi circumferinta(), nu au
argumente, dar folosesc ca date câmpurile statice şi de instanţă declarate în clasa respectivă.
143
Severin Bumbaru
Clasa Cerc2 conţine, de asemenea, câmpul static final PI, dar nu conţine câmpul de instanţă r.
În schimb, metodele sale sunt statice şi primesc raza cercului ca argument. Invocarea acestor
metode se face calificându-le cu numele clasei.
În fişierul Cercuri.java există şi clasa Cercuri, în care se folosesc clasele Cerc1 şi Cerc2
declarate anterior. Iată această aplicaţie:
class Cercuri {
public static void main(String args[]) {
double r1=1, r2=7.32;
Cerc1 c1=new Cerc1(), c2=new Cerc1();
c1.r=r1; c2.r=r2;
System.out.println("Folosind metodele de instanta din clasa Cerc1:");
System.out.println("Pentru cercul c1: aria="+c1.arie()+
" circumferinta="+c1.circumferinta());
System.out.println("Pentru cercul c2: aria="+c2.arie()+
" circumferinta="+c2.circumferinta());
System.out.println("Folosind metodele statice din clasa Cerc2:");
System.out.println("Pentru raza r1: aria="+Cerc2.arie(r1)+
" circumferinta="+Cerc2.circumferinta(r1));
System.out.println("Pentru raza r2: aria="+Cerc2.arie(r2)+
" circumferinta="+Cerc2.circumferinta(r2));
}
}
Remarcăm că:
- instanţierea clasei Cerc1 s-a făcut folosind constructorul implicit Cerc1(); vom explica
aceasta la capitolul "declararea constructorilor";
- atribuirea de valoare razei r a instanţelor din clasa Cerc1 s-a făcut calificând numele
câmpului cu numele referinţei la instanţă, de exemplu în instrucţiunea de atribuire c1.r=r1;
- invocarea metodelor de instanţă s-a făcut calificând numele metodei cu referinţa la instanţa
respectivă: de exemplu c1.arie() calculează aria cercului cu referinţa c1 si, deci, va folosi
implicit raza c1.r1;
- invocarea metodelor statice ale clasei Cerc2 s-a făcut calificându-le cu numele clasei şi
transmiţându-le valoarea razei cercului ca argument, de exemplu Cerc2.arie(r1);
- la compilarea fişierului sursă Cercuri.java se obţin trei fişiere de bytecode: Cerc1.class,
Cerc2.class si Cercuri.class, corespunzătoare celor trei clase declarate.
În aceeaşi clasă pot exista mai multe metode cu acelaşi nume, cu condiţia ca ele să difere prin
numărul şi/sau tipul argumentelor. Pentru a deosebi între ele astfel de metode, s-a introdus
conceptul de signatură.
144
Programarea orientata pe obiecte în limbajul Java
Signatura metodei constă din numele acesteia, însoţit de lista de argumente. În consecinţă,
două metode pot avea acelaşi nume, dacă diferă între ele prin signatură. Putem da exemple
din clasele existente în pachetele deja studiate.
La invocarea unei metode, este necesar să se transmită de la metoda care invocă la metoda
invocată parametrii (argumentele) acesteia. De exemplu, la executarea invocării
Math.sin(a), este necesar să se transmită către metoda sin argumentul acesteia, a.
În teoria şi practica programării se cunosc diferite moduri în car se poate face transmiterea
argumentelor către o funcţie sau procedură:
- transmitere prin valoare: de la programul apelant către funcţie (procedură) se transmit
valorile argumentelor;
- transmitere prin adresa: de la programul apelant către funcţie (procedură) se transmit
adresele la care se găsesc în memorie valorile argumentelor;
- transmitere prin nume: de la programul apelant către funcţie (procedură) se transmit
numele argumentelor;
- transmitere prin referinţă: de la programul apelant la funcţie (procedură) se transmit
referinţe către argumente.
Exemplu
Să considerăm metoda int indexOf(String str, int fromIndex) din clasa String.
Primul argument al acestei metode este o referinţă la un obiect din clasa String, iar al doilea
argument este o valoare primitivă de tip int. În limbajul Java, cei doi parametri str si
fromIndex sunt consideraţi variabile locale ale metodei indexOf, iar transmiterea
parametrilor este echivalentă cu operaţia de atribuire. În consecinţă, dacă se invocă această
metodă sub forma indexOf("abc", 3), variabilei-referinţă str i se atribuie ca valoare
referinţa la sirul "abc" (şi nu insuşi şirul "abc", care rămâne la locul lui în memoria
145
Severin Bumbaru
Exemplu
În fişierul TestParam.java este dat următorul program, în care se testează un caz de metodă
care îşi modifică parametrii:
class TestParam {
/* O metoda in care se modifica valorile propriilor parametri formali
*/
static void modParam(int k, Proba2 p1, Proba2 p2) {
System.out.println("La intrarea in modParam k="+k+" p1.a="+p1.a+
" p2.a="+p2.a);
k=-111; // S-a modificat valoarea parametrului k de tip int
p1.a=-222; // S-a modificat valoarea unui camp al obiectului cu
// referinta p1, dar nu insasi referinta p1
p2=new Proba2(); // S-a modificat insasi referinta p2, catre
// o noua instanta a clasei Proba2
p2.a=-333; // S-a atribuit valoare campului a al noii
// instante referite prin p2
System.out.println("In modParam dupa modificarile de parametri:\n"+
"k="+k+" p1.a="+p1.a+" p2.a="+p2.a);
}
146
Programarea orientata pe obiecte în limbajul Java
/* Metoda principala */
public static void main(String args[]) {
// Se declara si se initializeaza variabilele
int m=123;
Proba2 pr1=new Proba2(), pr2=new Proba2();
pr1.a=333; pr2.a=444;
System.out.println("In main inainte de a invoca modParam:\n"+
"m="+m+" pr1.a="+pr1.a+" pr2.a="+pr2.a);
// Se invoca metoda modParam
modParam(m, pr1, pr2);
// Se afiseaza valorile parametrilor dupa revenirea din modParam
System.out.println("In main dupa ce s-a invocat modParam:\n"+
"m="+m+" pr1.a="+pr1.a+" pr2.a="+pr2.a);
}
}
Remarcăm că, deşi în metoda modParam s-au modificat valorile lui k, p1.a si p2.a, la
revenirea din modParam în main valorile lui m şi pr2.a (care corespund respectiv lui k şi
p2.a din modParam) au rămas cele anterioare invocării acestei metode, în timp ce pr1.a
(corespunzătoare lui p1.a) s-a modificat. Iată cum se explică cele constatate:
1/ Parametrul formal k este de tipul primitiv int. În consecinţă, la invocarea metodei
modParam, parametrul efectiv corespunzator se transmite prin valoare, adică se poate
considera că variabilei locale k din modParam i s-a atribuit valoarea lui m, respectiv 123. În
schimb, valoarea variabilei m din main a rămas nemodificată.
2/ Parametrul formal p1 este de tip referinţă la o instanţă a clasei Proba2. La invocarea
metodei modParam, acestui parametru i s-a atribuit valoarea variabilei referinţa pr1 din
main, adică o referinţă către obiectul din clasa Proba2 care a fost creat în main şi are
câmpul a=333. În modParam se modifică valoarea câmpului a din acest obiect. Este deci
normal să constatăm că, la revenirea din modParam, valoarea câmpului pr1.a s-a modificat,
deoarece pr1.a==pr2.a.
3/ Parametrul formal p2, la fel ca p1, este o referinţă la o instanţă a clasei Proba2. La
intrarea în modParam, lui p2 i se atribuie valoarea parametrului efectiv pr1 din main, adică o
referinţă la obiectul în care campul pr1.a are valoarea 444. În metoda modParam se
modifică valoarea parametrului p2, adică se creeaza o nouă instanţă a clasei Proba2 şi se
atibuie lui p2 ca valoare o referinţă la aceasta nouă instanţă. Când se face apoi atribuirea
p2.a=-333, se modifică valoarea câmpului a din această nouă instanţă, fără a se modifica
valoarea câmpului pr2.a al obiectului referit iniţial. În consecinţă, la revenirea în main
constatăm că pr2.a a rămas la valoarea anterioară invocarii metodei modParam.
147
Severin Bumbaru
class TestRef {
În metoda1 se creează prin operatorul new un nou şir, iar referinţa str la acesta este
"întoarsă" de această metodă la executarea instrucţiunii return. În metoda2 se creează de
asemenea un nou şir, dar referinţa la acesta se atribuie argumentului s al metodei. La
executarea acestui program se obţin următoarele rezultate:
Se observă că metoda1 a întors corect referinţa către şirul "un sir" care a fost creat în
interiorul ei. În schimb, metoda2 nu a avut ca efect lateral transmiterea catre variabila-
referinţă sir2 din main a referinţei către obiectul "alt sir" creat în această metodă
deoarece, după cum s-a arătat anterior, transmiterea parametrilor se face prin valoare şi deci
modificarea în corpul metodei a valorii parametrului formal s nu afectează valoarea
parametrului efectiv str2 prin care aces
148
Programarea orientata pe obiecte în limbajul Java
În limbajul Java, tablourile sunt obiecte. În consecinţă, dacă parametrii formali ai unei
metode sunt tablouri, numele lor sunt, de fapt, variabile-referinţă. De exemplu, în signatura
metodei main
main(String args[])
parametrul formal args[] este o referinţă la un tablou, ale cărui elemente aparţin clasei
String (sunt şiruri de caractere). În consecinţă, tot ce s-a prezentat în secţiunea anterioară cu
privire la folosirea ca parametri formali a variabilelor- referinţă este valabil şi pentru tablouri.
Pentru exemplificare, considerăm programul următor, din fişierul TestTab.java.
class TestTab {
149
Severin Bumbaru
Remarcăm că, la ieşirea din metoda alpha, variabila locală c este eliminată de pe stiva
sistemului. Cu toate acestea, tabloul creat în metoda alpha şi referit de această variabilă locală
nu este eliminat din memorie, deoarece către el indică în continuare variabila-referinţă q din
metoda main.
O metodă (funcţie sau procedură) care se invocă pe sine însăşi se numeşte metodă recursivă.
Două sau mai multe metode care se invocă una pe alta (metoda A invocă metoda B şi
reciproc) se numesc mutual recursive. Limbajul Java permite utilizarea metodelor recursive
şi mutual recursive. Vom ilustra aceasta prin exemple. Vom arăta, de asemenea că, de regulă,
aceleaşi funcţii pot fi calculate atât recursiv, cât şi iterativ (folosind cicluri). În general, în
limbajele funcţionale se utilizează predominant funcţiile recursive, în timp ce în limbajele
procedurale se prefera iteraţia, deşi în unele dintre ele (cum este şi limbajul Java) se pot folosi
atât iteraţia, cât şi recursia.
150
Programarea orientata pe obiecte în limbajul Java
Remarcam că, la fel ca în cazul ciclurilor iterative, în metodele recursive trebuie să existe o
condiţie de oprire a repetării. În caz contrar recursia ar continua până la depăşirea spaţiului de
memorie alocat pentru memorarea datelor intermediare (numit stivă).
Exemplul 1:
Un exemplu tipic de funcţie recursivă este calcularea factorialului. Din punct de vedere
matematic, factorialul este o funcţie
factorial(n) = 1*2*3*...*n
factorial(0)=1;
factorial(n)=n*factorial(n-1) pentru n>0;
Pentru n<0 funcţia factorial nu este definită. Calcularea acestei funcţii poate fi facută prin
metoda următoare:
În corpul metodei se verifică, mai întâi dacă argumentul n se încadrează în domeniul admis,
iar în caz contrar se generează o excepţie. Dacă argumentul este valabil, se aplică formulele
de calcul recursiv al factorialului date mai sus. Recursia se încheie când se ajunge la n==0.
Atunci când o metodă invoca alta metodă (fie ea recursivă sau nu), datele metodei care
invocă, inclusiv adresa instrucţiunii care urmeaza celei care a făcut invocarea, sunt puse într-
o structură de memorie numita stivă, după care se transmit către metoda invocată argumentele
şi i se dă acesteia controlul. Stiva (engleza: stack) este o structura de memorie în care ultima
dată introdusă este prima extrasă (în engleză: LIFO - Last In First Out). La revenirea din
metoda invocată se obţine valoarea întoarsă de aceasta şi se extrag din stivă datele puse acolo
înainte de invocare, continuându-se calculul.
151
Severin Bumbaru
Se observa că, cu cât recursia este mai "profundă" (funcţia recursivă se invocă de mai multe
ori pe sine însăşi), cu atât este necesară o stivă de capacitate mai mare. Daca recursia este
prea "profundă", este posibil ca stiva să nu aibă capacitate suficientă. În acest caz se obţine
eroarea de depăşire de stivă StackOverflowError (atenţie: este o eroare, nu o excepţie).
Exemplul 2:
Un alt exemplu tipic de funcţie recursivă este funcţia lui Fibonacci definită astfel:
fibonacci(0)=0;
fibonacci(1)=1;
fibonacci(n)=fibonacci(n-1)+fibonacci(n-2) pentru n>1.
În fişierul TestRecursii.java sunt declarate două metode statice pentru funcţia Fibonacci: una
recursiva, în clasa Recursii, şi alta iterativă, în clasa Iteratii. Cele două metode sunt apoi
testate şi comparate în clasa TestRecursii. Se poate constata că, la valori mici ale
152
Programarea orientata pe obiecte în limbajul Java
argumentului n, metoda recursivă este chiar mai rapidă decât cea iterativă. În schimb, la
valori mari ale lui n, timpul de calcul al metodei recursive creşte foarte rapid, devenind
sensibil mai mare decât al celei iterative.
Exemplul 3:
În clasa Recursii din fisierul TestRecursii.java sunt declarate şi două funcţii mutual-
recursive:
fct2(0,y)=y
fct2(n,y)=y*fct1(n-1, 1.27*y-0.89)-1 pentru n>0
Instrucţiunea throw
Cunoaştem deja că, la apariţia anumitor anomalii în executarea programului, maşina virtuală
Java generează excepţii. Excepţiile sunt obiecte din clasa Exception s-au dintr-o subclasă a
acesteia. Este posibil ca programatorul să prevadă, în anumite puncte ale programului,
generarea unor excepţii, folosind în acest scop instrucţiunea throw, care are forma următoare:
Exemplu
Instrucţiunea
"aruncă" o excepţie din clasa Exception, care contine un mesaj sub forma argumentului
furnizat constructorului.
Clauza throws
În mod normal, excepţiile generate într-o metodă sunt tratate prin structuri try .. catch. chiar
în metoda în care au fost generate. Este însă posibil ca metoda respectivă să "arunce" mai
departe excepţiile generate în corpul ei. Pentru a indica această proprietate, la declararea
metodei, după paranteza care conţine lista declaraţiilor argumentelor formale se pune clauza
153
Severin Bumbaru
throws ClasaDeExceptii, în care se indică numele clasei excepţiei care este "aruncată"
către metoda invocatoare (Cuvantul throws este persoana a treia singular a verbului to throw,
"a arunca").
Exemplu
În programul din fişierul TestExceptie.java există metoda static int factorial(int n),
care calculează factorialul argumentului n. În această metodă este folosită instrucţiunea
throw de doua ori: pentru cazul în care argumentul este negativ şi pentru cel în care
argumentul este prea mare (rezultatul depăşeşte valoarea maximă pentru tipul int al valorii
întoarse). În ambele cazuri, în instrucţiunea throw se foloseşte constructorul clasei
Exception, furnizându-i ca argument un mesaj care arată ce eroare s-a produs. Întrucat
aceste excepţii nu sunt tratate în metoda factorial, în declaraţia metodei factorial s-a
folosit clauza throws. În metoda main se captează şi se afişează atât aceste excepţii, cât şi
excepţia NumberFormatException generată de metoda int Integer.parseInt(String s)
atunci când argumentul acesteia nu este forma externă a unui număr întreg.
class TestExceptie {
154
Programarea orientata pe obiecte în limbajul Java
}
}
Clase publice
Clasele publice sunt clase care pot fi utilizate şi în alte pachete, decât cel din care fac parte.
Fiecare clasă publică se declară într-un fişier separat, care are obligatoriu acelaşi nume cu
cel al clasei şi extensia java. În declaraţia de clasă, în faţa numelui clasei se pune
modificatorul public. Dăm în continuare ca exemplu clasa Complex.
155
Severin Bumbaru
/* Constructori */
public Complex() {
re=0; im=0;
}
public Complex(Complex z) {
re=z.re; im=z.im;
}
/* Metode */
156
Programarea orientata pe obiecte în limbajul Java
z.re=modul*Math.cos(argument);
z.im=modul*Math.sin(argument);
return z;
}
157
Severin Bumbaru
z.re=(re*a.re+im*a.im)/w;
z.im=(im*a.re-re*a.im)/w;
return z;
}
Declaraţia clasei incepe prin public class Complex, deci este o clasa publică. Clasa
conţine două câmpuri de tip double, reprezentând respectiv partea reală şi partea imaginară a
numărului complex. S-au prevăzut mai mulţi constructori. Primul dintre aceştia nu are
argumente şi creează un număr complex la care, atât partea reală, cât şi cea imaginară sunt
nule.
Având în vedere că s-a declarat un constructor cu două argumente de tip double, reprezentând
partea reală şi cea imaginară a numărului complex nou creat, nu a mai fost posibil să se
creeze încă un constructor, care să aibă ca argumente modulul şi argumentul noului număr
complex (acestea fiind tot numere reale). Din această cauză, el a fost înlocuit prin metoda
statică
public static Complex complex(double modul double argument)
al cărei nume începe cu literă mică.
Au fost redefinite metodele toString, equals şi hashCode ale superclasei Object, pentru a
ţine seama de specificul clasei Complex. Au fost declarate, de asemenea, metode pentru
158
Programarea orientata pe obiecte în limbajul Java
efectuarea de calcule între două numere complexe, între un număr complex şi unul real şi
intre unul real şi unul complex. Ultimele au fost declarate ca metode statice, deoarece
operandul din stânga nu aparţine clasei complex.
În toate metodele, în care pot să apară situaţii anormale, s-a prevazut generarea de excepţii.
Colectorul de reziduuri
Dacă un obiect nu mai este necesar, el poate fi distrus, adică eliminat din memorie. În maşina
virtuală Java, există un colector de reziduuri de memorie (engleză: garbage collector) care
eliberează automat spaţiul de memorie ocupat de obiectele către care nu mai există nici o
referinţă. în consecinţă, programatorul nu mai este pus în situaţia să prevadă explicit în
program distrugerea obiectelor şi, deci clasele nu mai conţin destructori, ca în alte limbaje de
POO.
- Figura 3 -
159
Severin Bumbaru
Metoda finalize
În clasa Object exista metoda
Aceasta metodă este invocată de colectorul de reziduuri, atunci când acesta determină că nu
mai există referinţe către obiectul respectiv. În clasa Object, această metodă nu efectuează
nimic.
Metoda finalize poate fi redefinită în orice altă clasă, pentru a elibera resurse sau a efectua
orice alte acţiuni necesare înainte de distrugerea obiectului respectiv. De exemplu, dacă
obiectul respectiv a deschis anumite fişiere sau conexiuni externe, în metoda finalize se poate
efectua închiderea lor.
Metoda finalize nu este apelată explicit în programul de aplicaţie. Apelarea metodei finalize
se face numai de către colectorul de reziduuri (garbage collector), dar nu imdeiat ce un obiect
a rămas fără referinţă, ci abia atinci când acest obiect a "intrat în atenţia" colectorului. Este
posibil ca executarea aplicaţiei să se incheie înainte ca "finalizarea" unor obiecte să aibă loc.
Exemplu
În aplicaţia din fişierul Finalizari.java este declarată clasa ProbaFinaliz, în care este
redefinită metoda finalize() din clasa Object. În clasa Finalizari din acelaşi fisier se
construiesc două obiecte din clasa ProbaFinaliz, după care se elimină referinţele către
aceste obiecte. Având în vedere că imediat după aceea se încheie executarea aplicaţiei, cel
mai probabil este că nu va avea loc invocarea de către colectorul de reziduuri a metodei
finalize().
Întrebări
Nivel 1
1. Care este cea mai simplă formă a unei declaraţii de clasă?
2. Ce sunt membrii unei clase?
3. Ce este numele clasei din punct de vedere sintactic?
4. Cu ce începe numele unei clase?
160
Programarea orientata pe obiecte în limbajul Java
Nivel 2
1. Care sunt valorile iniţiale implicite ale câmpurilor?
2. Ce deosebire este între iniţializarea câmpurilor şi iniţializarea variabilelor locale?
3. Ce deosebire este intre o funcţie şi o procedură?
4. Prin ce se deosebeşte o metodă prin care se realizează o procedură de una care
realizează o funcţie?
5. În ce mod se invocă, în mod normal, o metodă prin care se realizează o funcţie
propriu-zisă (a carei valoare întoarsă nu este void)?
6. În ce mod poate fi invocata o metoda care întoarce void?
7. Există în limbajul Java variabile globale?
8. Ce deosebire este între parametrii formali ai unei metode şi cei efectivi?
9. Ce se întâmplă dacă în corpul unei metode se modifică valoarea unui argument
formal?
10. Ce se întâmplă dacă în corpul unei metode se modifică conţinutul unui obiect referit
de către un parametru formal al metodei respective?
11. Ce se întâmplă dacă în corpul unei metode se modifică valoarea unui parametru
formal de tip referinţă (în sensul că i se dă ca valoare o referinţă la alt obiect)?
12. În ce situaţie, la revenirea dintr-o metodă, poate să aibă loc un efect lateral?
13. Este posibil ca o metodă să întoarcă o referinţă la un obiect construit în corpul
acesteia?
14. Ce se întâmplă dacă, în corpul unei metode care are ca argument formal un tablou, se
modifică elementele acestui tablou?
15. Ce se întâmplă dacă, în corpul unei metode care are ca argument formal un tablou, i se
161
Severin Bumbaru
162
Programarea orientata pe obiecte în limbajul Java
Identitatea (engleză: Identity) se referă la faptul că datele sunt grupate în entităţi discrete,
numite obiecte. Fiecare obiect din POO modelează starea şi comportamentul unui anumit
163
Severin Bumbaru
obiect din lumea reală, care poate fi un obiect fizic (de exemplu automobil, calculator, furnal,
om, animal etc.) sau unul conceptual (de exemplu figură geometrică, orar etc.). Fiecare obiect
are propria lui identitate, astfel că două obiecte sunt considerate distincte, chiar daca
atributele lor (cum ar fi numele, culoarea etc.), sunt identice. Pentru a face aceasta distincţie,
obiectul este indicat printr-o referinţă unică. Modul în care este reprezentata această referinţă
poate sa difere în diverse limbaje de programare (de ex. adresă de memorie, nume etc.),
important însă este că fiecare obiect are o singură referinţă şi nu există două obiecte distincte
cu aceeaşi referinţă.
Încapsularea (engleză: encapsulation) este proprietatea obiectelor de a-şi ascunde o parte din
date şi metode. Din exteriorul obiectului sunt accesibile ("vizibile") numai datele şi metodele
publice. Putem deci sa ne imaginăm obiectul ca fiind format din două straturi, ca în Figura 1.
- Figura 1 -
Obiectul se comportă ca şi când ar avea doua "învelişuri": unul "transparent", care permite
accesul la datele şi metodele publice ale obiectului, şi un al doilea inveliş "opac", care
cuprinde datele şi metodele invizibile (inaccesibile) din exterior. Starea obiectului depinde
atât de datele publice, cât şi de cele încapsulate. Metodele publice ale obiectului au acces la
datele şi metodele încapsulate (ascunse) ale acestuia. In consecinţă, starea obiectului poate fi
modificata atât prin modificarea directă, din exterior, a valorilor variabilelor publice, fie prin
utilizarea unor metode publice care modifica valorile variabilelor încapsulate. În mod similar,
valorile variabilelor încapsulate pot fi obţinute numai utilizand metode publice ale obiectului
respectiv.
Partea vizibilă (publică) a obiectului constituie interfaţa acestuia cu "lumea exterioară". Este
posibil ca două obiecte diferite să aibă interfeţe identice, adică să prezinte în exterior aceleaşi
date şi metode. Datorită faptului că partea încapsulată diferă, astfel de obiecte pot avea
comportament diferit.
164
Programarea orientata pe obiecte în limbajul Java
- Figura 2 -
Cu cat o clasă se afla în această ierarhie pe un nivel mai înalt, cu atât ea este mai abstractă,
deci descrie un număr mai mic de atribute ale obiectelor care îi aparţin. Clasa A este rădăcina
acestei ierarhii, fiind situată pe cel mai înalt nivel de abstractizare. Clasele B, C şi D sunt
subclase ale lui A, iar clasele E si F sunt subclase ale clasei B. În acelaşi timp, clasa
Aestesuperclasă a clasei B, iar aceasta este superclasă a clasei F. În programarea orientată pe
obiecte se spune, de asemenea, că clasa B este derivată din clasa A, iar clasa F este derivată
din clasa B sau, în general, o subclasă este derivată din superclasa sa.
Un obiect care aparţine unei clase se numeşte şi instanţă a clasei (este o instanţiere, o
realizare particulara a clasei respective). De exemplu, clasa Barbat şi clasa Femeie sunt
subclase ale clasei Om. În schimb, Ion_Popescu este o instanţăa clasei Barbat (un obiect care
aparţine acestei clase), iar Maria_Preda este o instanţă a clasei Femeie.
În principiu, o clasă poate avea o infinitate de instanţe. Practic, desigur, numărul lor este finit.
Toate instanţele unei clase au aceleaşi atribute (aceleaşi "câmpuri"), dar valorile acestora pot
să difere de la o instanţă la alta.
165
Severin Bumbaru
Moştenirea (engleză: inheritance) este proprietatea unei clase de a conţine toate atributele
(câmpurile) şi metodele superclasei sale. În consecinţă, trecerea de la clasă la subclasă se face
prin adăugarea de atribute şi/sau de metode. De exemplu, clasa Barbat şi clasa Femeie au
ambele toate atributele clasei Om, dar fiecare din acestea are şi atribute specifice.
- Figura 3 -
Încapsularea
Încapsularea este una din proprietăţile fundamentale ale claselor în programarea orientată pe
obiecte.
Câmpurile, constructorii şi metodele dintr-o clasă pot fi încapsulate, astfel încât să nu fie
vizibile din exteriorul clasei sau instanţei în care se află. Aceasta se realizează folosind la
declararea câmpului, constructorului sau metodei respective modificatorul private.
166
Programarea orientata pe obiecte în limbajul Java
Declararea constructorilor
Constructorii sunt utilizati impreuna cu operatorul new pentru a creea instanţe ale claselor.
Constructorii pot fi impliciţi sau expliciţi.
Constructorii, ca şi metodele, sunt niste subprograme. Faţă de metode, constructorii prezintă
următoarele trăsături specifice:
- numele constructorului este întotdeauna acelaşi cu al clasei căreia îi aparţine;
- constructorul nu întoarce o valoare. În consecinţă, la declararea constructorului nu se
specifică tipul valorii întoarse, ca la metode;
- constructorii nu pot fi statici;
- invocarea constructorilor se face numai prin operatorul new.
Ca şi în cazul metodelor, o clasă poate avea mai multi constructori, care sa difere între ei prin
signatură.
Constructorul implicit
Dacă într-o clasă nu este declarat explicit nici un constructor, ea are un constructor implicit.
Acest constructor nu are argumente, iar efectul lui constă în iniţializarea tuturor câmpurilor
instanţei care se creează cu valorile implicite corespunzătoare tipurilor câmpurilor respective.
167
Severin Bumbaru
ATENŢIE: dacă clasa are unul sau mai multi constructori declaraţi explicit, ea nu mai are
constructor implicit.
Exemplu: în aplicaţia Cercuri din fişierul Cercuri.java, la crearea instanţelor clasei Cerc1 s-a
folosit constructorul implicit al acestei clase.
Constructori expliciţi
Declararea constructorului se face sub forma
modificator_acces nume_clasa(declaratii_de_argumente) {
corpul_constructorului
}
Se observă imediat că declararea constructorului se face la fel cu a unei metode, cu
deosebirea că lipseşte tipul valorii întoarse, iar numele constructorului este cel al clasei.
Modificatorul de acces al constructorului este, cel mai frecvent, public. El poate, totuşi, sa
lipsească dacă se consideră că instanţierea clasei se va face numai în metode ale claselor din
acelaşi pachet.
În corpul constructorului pot fi programate orice fel de acţiuni care trebuie executate imediat
după ce se alocă în memorie spaţiu pentru o instanţă a clasei respective. Cel mai frecvent, în
corpul constructorilor se iniţializează valorile câmpurilor instanţei nou create.
Exemplu
class Cerc {
public static final double PI=3.141592653589793;
private double r;
168
Programarea orientata pe obiecte în limbajul Java
}
}
Clasa Cerc are atât metodele de instanţă, cât şi metodele statice pentru calcularea ariei şi
circumferinţei, care existau în clasele Cerc1 şi Cerc2 din exemplul dat în fişierul Cercuri.java.
În plus, remarcăm următoarele:
- câmpul r1 (raza cercului) a fost declarat privat, deci el nu este accesibil din exteriorul
clasei;
- s-a declarat un constructor care, la crearea unui obiect din clasa Cerc, iniţializează câmpul
r1. Întrucât clasa Cerc nu conţine nici o metodă prin care să se modifice valoarea acestui
câmp, raza cercului nu mai poate fi modificată după ce acesta a fost creat, aşa cum era posibil
în cazul instanţelor clasei Cerc1.
În acelaşi fişier sursă, este declarata şi clasa Cercuri1, în care se utilizeaza clasa Cerc.
Aplicaţia menţionată este declarată astfel:
class Cercuri1 {
public static void main(String args[]) {
double r1=1, r2=7.32;
/* Instantierea a doua cercuri */
Cerc c1=new Cerc(r1), c2=new Cerc(r2);
/* Utilizarea metodelor de instanta */
System.out.println("Utilizand metodele de instanta:");
System.out.println("Pentru cercul c1:\n aria="+c1.arie()+
" circumferinta="+c1.circumferinta()+" raza="+c1.raza());
System.out.println("Pentru cercul c2:\n aria="+c2.arie()+
" circumferinta="+c2.circumferinta()+" raza="+c2.raza());
/* Utilizarea metodelor statice */
System.out.println("Folosind metodele statice ale clasei Cerc:");
System.out.println("Pentru raza r1: aria="+Cerc.arie(r1)+
" circumferinta="+Cerc2.circumferinta(r1));
System.out.println("Pentru raza r2: aria="+Cerc.arie(r2)+
" circumferinta="+Cerc2.circumferinta(r2));
System.out.println("Pentru raza r=9.23 aria="+Cerc.arie(9.23)+
" circumferinta="+Cerc.circumferinta(9.23));
/* Acelasi calcul, facut calificand numele metodelor statice prin
referinte la instante ale clasei Cerc
*/
System.out.println("Aria cercului cu raza 9.23: "+
c1.arie(9.23)+" "+c2.arie(9.23));
}
}
Se observă că iniţializarea celor două cercuri (instanţe ale clasei Cerc) c1 şi c2 s-a făcut
folosind constructorul Cerc(double raza). După ce a fost construit cercul, raza lui nu mai
poate fi modificată. Calculara ariei cercului c1 se face invocând metoda c1.arie(), care
aparţine instanţei c1. În acest caz, metoda nu are argumente, deoarece se va folsi raza
cercului c1.
Aria unui cerc oarecare poate fi calculată însă şi invocând metoda staticăCerc.arie(raza),
169
Severin Bumbaru
căreia însă i se dă ca argument raza cercului. Chiar dacă această metodă statică este
invocată prin intermediul referinţei la o instanţă sub forma c1.arie(raza), nu se va folosi
în calcul raza instanţei c1, ci raza primită ca argument. Compilatorul face distincţie intre
metodele arie() şi arie(double raza) întrucât au semnături diferite.
Agregarea
În limbajul Java, la declararea claselor, este permis ca atât câmpurile clasei, cât şi ale
instanţei să aibă ca valori obiecte din alte clase. Se poate porni astfel de la clase simple, care
au drept câmpuri date din tipuri primitive, şi construi pas cu pas clase cu structuri din ce in ce
mai complicate, realizate prin agregarea claselor cu structuri mai simple.
Un exemplu de agregare este clasa Pers din fisierul Pers.java, pe care îl reproducem aici.
class Pers {
private String numePers, prenumePers;
private int anNasterePers;
Instanţele clasei Pers conţin datele unei persoane. Se observă că obiectele din clasa Pers
conţin câmpurile nume şi prenume, care sunt referinţe la obiecte ale clasei String. Remarcăm
de asemenea că, întrucât câmpurile de date sunt private, iniţializarea lor se face folosind un
constructor. După crearea obiectului, aceste câmpuri nu mai pot fi modificate. Testarea clasei
Pers se face în aplicaţia din fişierul TestPers.java.
Moştenirea
Conceptul de moştenire
170
Programarea orientata pe obiecte în limbajul Java
Există trei moduri de acces la membrii claselor (atât la câmpuri, cât şi la metode): public,
privat si protejat. Până în prezent am folosit numai modificatorii de acces public şi private,
deoarece nu am declarat clase derivate. Introducem acum şi modificatorul de acces
protected. Câmpurile sau metodele declarate cu acest modificator de acces sunt vizibile
(accesibile) în propria clasă şi în clasele derivate din aceasta. Iată, deci, care sunt cei trei
modificatori de acces la membrii unei clase folositi în declaraţiile de clase:
- public - pentru câmpuri şi metode care sunt accesibile din orice clasă (inclusiv din clase
aparţinând altor pachete);
- protected - pentru câmpuri şi metode care sunt accesibile în propria clasă şi în
subclasele acesteia, dar nu sunt vizibile din alte clase;
- private - pentru câmpurile şi metodele care sunt accesibile numai din propria clasă.
Acestea nu sunt accesibile din nici o alta clasă (nici chiar din subclasele propriei lor clase).
Amintim că, în lipsa modificatorului de acces, câmpurile şi metodele respective sunt
accesibile din clasele aceluiaşi pachet. Acest mod de acces este cunoscut sub numele de
prietenos (engleză: friendly) sau de pachet (engleză: package).
În declaraţia clasei derivate (subclasei), numele clasei care se declară este urmat de clauza
extends, în care se indică numele superclasei. În consecinţă, clasa derivată poate fi declarată
astfel:
171
Severin Bumbaru
Amintim că în limbajul Java orice clasă are o superclasă şi numai una. Excepţie face clasa
Object, care este rădăcina ierarhiei de clase. Daca lipseşte clauza extends, superclasa
implicită este Object.
Redefinirea metodelor
Metodele de instanţă (nestatice) ale unei clase pot fi redefinite în subclasele acesteia.
Redefinirea unei metode se face declarând în subclasă o metodă având aceeaşi signatură cu
una din superclasă. Atunci când se redefineşte o metoda protejată, modificatorul de acces al
acesteia poate fi menţinut, sau poate fi transformat în public.
În subclasă pot fi folosite, totuşi, şi metodele superclasei care au fost redefinite, dacă la
invocarea acestor metode se foloseste referinţa super.
Exemplu:
În fişierul S1.java este declarata clasa S1, iar in fişierul CD1.java este declarata clasa CD1,
care extinde clasa S1 (este, deci, derivată din aceasta). Reproducem aici declaraţiile celor
două clase:
public class S1 {
public int a;
protected double b;
private int c;
public final static int alpha=12;
172
Programarea orientata pe obiecte în limbajul Java
Remarcăm următoarele:
- în metodele clasei S1 pot fi folosite toate câmpurile declarate în această clasă, fie ele
publice, protejate sau private; de asemenea, pot fi invocate toate metodele din această clasă;
- în clasa S1 a fost redefinită metoda toString din clasa Object, astfel incât să se întoarca un
şir care conţine valorile câmpurilor a, b şi c;
- metoda statică f4() din clasa S1 utilizează numai câmpul static alpha al acestei clase;
- în constructorul clasei CD1 a fost invocat constructorul clasei S1 sub forma
super(a,b,c). În acest mod au fost iniţializate toate câmpurile din superclasa S1, inclusiv
câmpul privat c, care nu este direct accesibil din clasa CD1;
- în clasa CD1 au fost redefinite metodele f2 şi f3 din superclasa S1 şi a fost iarăşi redefinită
metoda toString;
- în mod normal, în clasa CD1 se folosesc metodele f2 şi f3 redefinite în această clasă. Este
totuşi posibil ca în clasa CD1 să se apeleze metodele superclasei, dacă în expresia de invocare
se foloseşte referinta super. Astfel, în metoda f3 din clasa CD1 există expresia super.f3(),
prin care se invoca metoda f3 din superclasa S1;
- la redefinirea metodei f2, modul de acces a fost modificat din protected in public;
- în metodele clasei CD1 nu au putut fi folosite câmpurile şi metodele private ale superclasei
S1.
Testarea claselor S1 si CD1 se face în clasa TestCD1, care se găseşte în fişierul TestD1.java.
Iată aceasta clasă:
173
Severin Bumbaru
class TestCD1 {
public static void main(String args[]) {
S1 s=new S1(1, 1.1, 2);
CD1 cd=new CD1(10, 10.2, 20);
System.out.println("s="+s+" cd="+cd);
System.out.println("s.f2()="+s.f2()+" s.f3()="+s.f3());
System.out.println("cd.f2()="+cd.f2()+" cd.f3()="+cd.f3());
System.out.println("CD2.f4(3)="+CD2.f4(3)+" S1.f4(3)="+S1.f4(3));
System.out.println("cd.f4(3)="+cd.f4(3)+" s.f4(3)="+s.f4(3));
}
}
Remarcăm că:
- crearea instanţelor claselor S1 şi CD1 s-a făcut aplicând operatorul new asupra
constructorilor claselor respective;
- în instrucţiunea
System.out.println("s="+s+" cd="+cd);
este de două ori invocată implicit metoda toString, pentru a converti în şiruri afişabile
obiectele s şi cd;
prima dată este invocată metoda s.toString() din clasa S1. A doua oară ar trebui sa fie
invocată metoda cd.toString() din clasa CD1 dar, intrucât metoda nu a fost redefinită în
aceasta clasă, a fost aplicată efectiv metoda toString() din superclasa S1;
- în expresiile s.f2() si s.f3() sunt invocate metodele din clasa S1, careia îi aparţine s, iar
in expresiile cd.f2() şi cd.f3() sunt invocate metodele corespunzătoare din clasa CD1;
- metoda statică f4() din clasa S1 a fost invocată în patru moduri: calificând numele
metodei f1 cu numele clasei S1 (căreia îi aparţine de fapt această metodă), cu numele
subclasei CD1 (care moşteneşte această metodă) şi cu numele instanţelor s şi cd ale acestor
clase. Cum era de aşteptat, în toate cele patru cazuri s-a obţinut acelaşi rezultat.
Puteti compila cele trei fişiere menţionate şi pune în executie aplicaţia TestCD1 pentru a
verifica respectarea afirmaţiilor de mai sus. Puteţi, de asemenea, face modificări în metodele
celor trei clase menţionate, astfel încât să verificaţi:
- ce se întâmplă dacă într-o metoda a clasei CD1 se încearcă folosirea câmpurilor sau
metodelor private ale superclasei S1;
- ce se întâmplă dacă în clasa TestDC1 se încearcă folosirea câmpurilor sau metodelor
private sau protejate din clasele S1 şi CD1.
Ascunderea câmpurilor
174
Programarea orientata pe obiecte în limbajul Java
Câmpurile declarate într-o clasă pot fi ascunse prin câmpuri cu acelaşi nume declarate în
subclasă, chiar dacă acestea au tipuri diferite. Aceasta înseamna că, în mod normal, în
metodele clasei se folosesc câmpurile declarate în clasa respectivă, şi nu cele cu acelaşi nume
ale superclasei. În subclasă pot fi, totuşi, folosite şi câmpurile superclasei, dacă sunt calificate
cu referinţa super.
Metodele statice nu aparţin instanţelor, ci clasei. Din această cauză, dacă într-o subclasă se
declara o metoda statică cu aceeaşi signatură ca o metodă a superclasei, atunci se spune că
metoda din subclasă o ascunde pe cea din superclasă (nu o redefineşte). Modul de lucru cu
metodele ascunse este similar cu cel în care se lucrează cu câmpurile ascunse. Vom înţelege
mai bine deosebirea dintre redefinire şi ascundere când vom studia polimorfismul.
Exemplu
În fişierul CD2.java este declarată clasa CD2, care extinde clasa S1 din exemplul precedent.
În clasa CD2 există declaraţia public double a, b, m; prin care se creează câmpuri ale
instanţelor acestei clase, dintre care a şi b ascund câmpurile cu aceleaşi nume ale superclasei.
De asemenea, în clasa CD2 este declarată metoda statică f4(int h), care ascunde metoda
statică cu aceeaşi signatura din superclasa S1.
Remarcăm că:
- în constructorul clasei CD2 este mai întâi invocat constructorul superclasei, prin care se
iniţializează câmpurile din S1, după care se iniţializează şi câmpurile din CD2;
- întrucât metoda afisare() este declarată în clasa CD2, ea operează cu câmpurile a, b şi m
175
Severin Bumbaru
declarate în CD2. Totuşi, în acesastă metodă se poate lucra şi cu câmpurile a şi b din S1,
folosindu-se în mod corespunzător expresiile super.a şi super.b;
- în redefinirea metodei toString() s-a folosit expresia super.toString() pentru a
invoca metoda cu aceeasi signatură din superclasa S1. Observăm că pentru a utiliza în
aceasta metodă în mod direct câmpurile a şi b din S1 (care sunt ascunse în clasa CD2), ar fi
trebuit utilizate expresiile super.a şi super.b;
Testarea clasei CD2 se face în aplicaţia din fişierul TestCd2.java, care are conţinutul următor:
class TestCD2 {
public static void main(String args[]) {
CD2 cd=new CD2(1, 2.45, 3, 10.23, 12.5, 17.08);
System.out.println("Imediat dupa ce s-a creat instanta clasei CD2:");
cd.afisare();
System.out.println("cd.toString(): "+cd.toString());
cd.a=-17.83; cd.b=28.16; cd.m=127.9;
System.out.println("dupa ce s-au schimbat valorile "+
"campurilor din CD2:");
cd.afisare();
System.out.println("cd.toString(): "+cd.toString());
System.out.println("S1.f4(3)="+S1.f4(3)+" CD2.f4(3)="+CD2.f4(3)+"
CD1.f4(3)=cd1.f4(3));
}
}
Remarcăm că:
- în metoda afişare() din clasa CD2 se operează corect atât cu câmpurile a şi b din clasa
CD2, cât şi cu cele ascunse de acestea din clasa S1;
- în metoda toString() din CD2 se operează direct cu câmpurile a şi b declarate în această
clasă şi cu metodele declarate în CD2 şi se opereaza indirect (folosind referinţa super) cu
câmpurile şi metodele din superclasa S1 care sunt ascunse sau redefinite în CD2;
- în expresiile S1.f4(3) şi CD1.f4(3) este invocată metoda statică a clasei S1, în timp ce în
expresia CD2.f4(3) este invocată metoda cu aceeaşi signatură a clasei CD2, care o ascunde
pe cea din clasa S1.
176
Programarea orientata pe obiecte în limbajul Java
Metode finale
Metodele finale sunt metode care nu mai pot fi redefinite în clasele derivate. Astfel de
metode se declară cu modificatorul final. De exemplu, în clasa Object, metoda getClass
este o metoda finală, fiind declarată sub forma public final Class getClass(). Aceasta
inseamnă că în nici o altă clasă nu este posibil ca aceasta metodă să fie redefinită. Când
declarăm propriile noastre clase, avem şi noi posibilitatea să declarăm că unele din metodele
lor sunt finale.
Deşi Java API oferă o varietate destul de mare de clase de excepţii, uneori programatorul
poate simţi nevoia să işi creeze propriile sale clase de exceptii. Aceasta se poate realiza
derivând clasele respective din clasa Exception, existentă în pachetul java.lang. Corpul
declaraţiei clasei de excepţii conţine numai declaraţii de constructori, în care se apelează
constructorul corespunzător al superclasei.
Exemplu:
Clase finale
Polimorfismul
177
Severin Bumbaru
Se pune întrebarea: dacă o metodă din clasa A, fie aceasta metoda1(), a fost redefinită în
clasa B, care din ele va fi executata în expresia a2.metoda1()? Observăm că, în această
expresie, variabila a2 aparţine clasei A, în timp ce obiectul indicat de această variabilă
aparţine efectiv clasei B. Conform principiului polimorfismului, va fi executata metoda1()
din clasa B.
Având în vedere că B este subclasă a lui A, iar variabila ob1 indică efectiv un obiect din clasa
B, atât în expresia ((B)ob1).metoda1(), cât şi în expresia ((A)ob1).metoda1() se va
invoca metoda1() din clasa B, adică din clasa căreia îi aparţine efectiv obiectul indicat de
variabila-referinţă ob1.
Exemplu
În fişierul Polimorf1.java se da următorul exemplu de aplicaţie în care se testează
polimorfismul, folosind clasa S1 şi subclasele ei CD1 şi CD2 definite anterior:
class Polimorf1 {
public static void main(String args[]) {
178
Programarea orientata pe obiecte în limbajul Java
System.out.println("cd1a.f2()="+cd1a.f2()+" cd1a.f3()="+cd1a.f3());
System.out.println("cd2a.f2()="+cd2a.f2()+" cd2a.f3()="+cd2a.f3());
System.out.println("((S1)ob2).f2()="+((S1)ob2).f2()+
" ((S1)ob2).f3()="+((S1)ob2).f3());
System.out.println("((S1)ob3).f2()="+((S1)ob3).f2()+
" ((S1)ob3).f3()="+((S1)ob3).f3());
System.out.println("((CD1)ob3).f2()="+((CD1)ob3).f2()+
" ((CD1)ob3).f3()="+((CD1)ob3).f3());
System.out.println("((S1)ob4).f2()="+((S1)ob4).f2()+
" ((S1)ob4).f3()="+((S1)ob4).f3());
System.out.println("((CD2)ob4).f2()="+((CD2)ob4).f2()+
" ((CD2)ob4).f3()="+((CD2)ob4).f3());
}
}
Remarcăm următoarele:
- atribuiri de forma s1b=cd1a sau ob1=s1 sunt corecte, întrucât unei variabile-referinţă dintr-
o clasă i se pot da ca valori referinţe la instanţe ale subclaselor acesteia;
- expresiile s1a.f2() şi ((S1)ob2).f2() au aceeaşi valoare (7), intrucat in ambele cazuri
este invocata metoda f2() din clasa S1;
179
Severin Bumbaru
- în expresia ((S1)ob2).f2() s-a folosit castul pentru a converti referinţa la Object intr-o
referinţă la S1, întrucat în clasa Object nu există o metoda f2();
- expresiile s1b.f2(), cd1a.f2(), ((S1)ob3).f2() şi ((CD1)ob3).f2()au aceeasi
valoare (-2), deoarece în toate cele trei cazuri metoda f2() se aplică asupra aceluiaşi obiect;
- în expresia s1b.f2() nu a fost necesar să se folosească o conversie explicită (cast) de
forma ((CD1)s1b).f2(), deoarece o metoda cu signatura f2() există şi în clasa S1, căreia îi
aparţine variabila s1b. Totuşi, în momentul execuţiei, se aplică efectiv metoda f2() din clasa
CD2, căreia îi aparţine obiectul indicat de s1b;
- în mod similar, expresiile s1c.f2(), cd2a.f2(), ((S1)ob4).f2() si
((CD2)ob4).f2() au aceeaşi valoare (70);
- în instrucţiunea System.out.println("s1a="+s1a+" s1b="+s1b); se foloseşte implicit
metoda toString()pentru a se converti obiectele indicate de variabilele-referinţă s1a şi s1b
în şiruri. Se foloseşte în ambele cazuri metoda toString() din clasa S1, deoarece în clasa
CD1 nu a fost redefinită o astfel de metodă;
- în instrucţiunea System.out.println("s1c="+s1c); se foloseşte implicit metoda
toString() din clasa CD2, căreia îi aparţine efectiv obiectul indicat de variabila s1c;
Studentul are toate atributele unei persoane, dar are şi atribute şi metode specifice. În
consecinţă, clasa Student poate fi derivată din clasa Persoana. În continuare dăm exemple de
declarare şi de testare a celor două clase.
180
Programarea orientata pe obiecte în limbajul Java
Clasa contine trei câmpuri private: două referinţe la String (nume şi prenume) şi un câmp de
tip int (anNaştere). Întrucât câmpurile sunt private, ele nu sunt vizibile din alte clase (nici
chiar din subclasele clasei Persoana).
Clasa Persoana are doi constructori. Primul constructor primeşte ca argumente valorile celor
trei câmpuri: nume, prenume, anNaştere. Întrucât argumentele au aceleaşi nume cu câmpurile
corespunzătoare, în corpul constructorului numele de câmpuri sunt calificate cu referinţa
this. Aceasta se putea evita schimband numele argumentelor. Al doilea constructor are ca
argument o instanţă a clasei Persoana, fiind ceeace se numeşte un "constructor de copiere".
Vom vedea utilitatea lui in continuare, la declararea clasei Student.
Metoda varsta(int anCurent) întoarce vârsta persoanei, fiind dat ca argument anul pentru
care se calculează. Este un exemplu de metodă care întoarce un atribut al persoanei (vârsta),
care nu este memorat într-un câmp, ci se obţine prin calcul.
Clasa publică Student este declarată în fişierul Student.java şi este reprodusă aici.
181
Severin Bumbaru
Au fost declaraţi trei constructori. Primul are ca argumente valorile tuturor câmpurilor, atât
ale celor moştenite de la superclasă, cât şi ale celor proprii. Întrucât câmpurile superclasei
sunt private, nu a fost posibilă iniţializarea lor directă, prin operaţii de atribuire, ci prin
apelarea constructorului superclasei sub forma
super(nume, prenume, anNastere);
Al doilea constructor are ca prim argument o referinţă la persoană, iar ultimele trei argumente
sunt aceleaşi ca la constructorul precedent. Pentru iniţializarea câmpurilor superclasei a fost
invocat constructorul acesteia sub forma
super(pers);
în care pers este referinţa la o Persoana. S-a utilizat aici constructorul de copiere
Persoana(Persoana pers). Remarcăm că nu am fi putut utiliza instrucţiunea
super(pers.nume, pers.prenume, pers.anNastere);
deoarece câmpurile clasei Persoana sunt private şi nu sunt, deci, accesibile din clasa Student.
În lipsa constructorului de copiere, am fi putut utiliza in schimb instructiunea
super(pers.nume(), pers.prenume(), pers.anNastere());
deoarece metodele nume(), prenume() şi anNastere() sunt publice.
182
Programarea orientata pe obiecte în limbajul Java
Aplicaţia din fişierul TestStud.java are ca obiectiv testarea claselor Persoana şi Student.
class TestStud {
public static void main(String args[]) {
Persoana p1=new Persoana("Vasiliu","George",1981), p2;
Student s1=new Student(p1, "NIE",2,"2221a"),
s2=new Student("Ionescu","Maria", 1980, "NIE", 2, "2322b"), s3;
Object ob1, ob2, ob3;
Persoana tabPers[]=new Persoana[3];
ob1=s1;
ob2=p2=s2;
ob3=p1;
System.out.println("p1="+p1);
System.out.println("s1="+s1);
System.out.println("s2="+s2);
System.out.println("ob1="+ob1);
System.out.println("p2="+p2);
System.out.println("ob2="+ob2);
System.out.println("ob3="+ob3);
System.out.println("Numele lui p1: "+p1.nume());
System.out.println("Numele lui s1: "+s1.nume());
System.out.println("Numele lui ob1: "+((Persoana)ob1).nume());
s3=(Student)ob2;
System.out.println("s3="+s3);
System.out.println("In anul 2000, s3 are "+s3.varsta(2000)+" ani");
System.out.println("p2.hashCode()="+p2.hashCode());
System.out.println("s1.hashCode()="+s1.hashCode());
tabPers[0]=p1; tabPers[1]=s1; tabPers[2]=s2;
System.out.println("Tabloul de persoane tabPers contine:");
System.out.println("Clasa lui p1: "+p1.getClass().getName());
System.out.println("Clasa lui ob2: "+ob2.getClass().getName());
for(int i=0; i<tabPers.length; i++)
System.out.println(tabPers[i]);
}
}
183
Severin Bumbaru
Persoana. Aceasta înseamnă că, la construirea instanţei Student1, a fost invocat implicit
constructorul fără argumente Persoana1() al superclasei. Atribuirea de valori câmpurilor
protejate ale superclasei se face, apoi, direct în corpul constructorului clasei Persoana1. S-au
folosit, în acest scop, instrucţiuni de forma this.nume=nume, întrucât numele câmpului
coincide cu cel al argumentului. Referinţa this este utilizată corect, intrucât câmpul
superclasei Persoana1 a fost moştenit de clasa Student1, ne fiind acoperit în aceasta. Se putea
folosi, însă, şi referinţa super sub forma super.nume=nume, deoarece câmpul nume aparţine,
de fapt, superclasei;
- în al doilea constructor al clasei Student1 nu a fost invocat explicit, de asemenea,
constructorul superclasei. Atribuirea de valori câmpurilor protejate ale superclasei Persoana1
s-a făcut direct în corpul clasei Student1, prin instrucţiuni de forma nume=pers.nume, unde
Pers este argumentul din clasa Persoana1;
- s-au înlocuit peste tot, în cele trei fişiere, numele de clase Persoana şi Student prin
Persoana1 şi Student1.
Clasa care conţine metoda main este clasa principală a unei aplicaţii, deci este prima clasă
care se încarcă în maşina virtuală Java la punerea în execuţie a aplicaţiei respective. În acelaşi
timp, ea este o clasă ca oricare alta, care poate avea şi instanţe. Metoda main(), fiind statică,
nu poate utiliza direct decât câmpurile statice şi metodele statice ale acestei clase. Totuşi, în
metoda main() sau în alte metode, pot fi create instanţe ale acestei clase, putându-se astfel
utiliza şi câmpurile şi metodele de instanţă ale acestora.
Exemplu
În fişierul Aplic.java este dat un exemplu de aplicaţie, în care clasa Aplic, care conţine
metoda main(), are câmpuri şi metode statice şi de instanţă şi constructor.
class Aplic {
static int a=10; // un camp static (al clasei)
double b; // camp al instantei
String s; // camp al instantei
184
Programarea orientata pe obiecte în limbajul Java
Remarcăm urmatoarele:
- clasa Aplic are câmpuri statice şi de instanţă şi alte metode statice şi de instanţă, pe lângă
metoda main();
- clasa Aplic are şi un constructor (putea avea chiar mai mulţi, sau numai pe cel implicit);
- în metoda main() se creează două instanţe ale clasei Aplic, cu referinţele a0 şi a1;
- în metodele statice main()şi f2(), câmpul static a şi metoda statica f2() pot fi utilizate
direct, sau calificându-le cu numele clasei sau cu o referinţă la o instanţă a acesteia. În toate
cazurile se obtine acelaşi rezultat;
- câmpurile şi metodele de instanţă nu pot fi utilizate direct în metoda statică main(), dar
pot fi utilizate cele ale instanţelor, calificandu-le cu referinţele la aceste instanţe, de exemplu
a0.b sau a0.f1(1.5);
- în metodele de instanţă pot fi utilizate direct atât câmpurile clasei, cât şi ale instanţei.
Astfel, în metoda de instanţă f1() se foloseşte expresia a*b+1, în care apar câmpul static a şi
câmpul de instanţă b.
185
Severin Bumbaru
Clase abstracte
Clasele abstracte conţin în declaraţia lor modificatorul abstract. Clasele abstracte nu pot fi
instanţiate. Remarcăm însa că pot exista clase care nu pot fi instanţiate deşi nu sunt abstracte,
cum sunt, de exemplu, clasele care nu au decât constructori privaţi.
Cu toate că clasa abstractă nu poate fi instanţiată, se pot declara variabile aparţinând unor
clase abstracte. Acestor variabile li se pot da însa ca valori numai referinţe către instanţe ale
unor subclase concrete. De exemplu, daca A este o clasă abstracta, iar B este o subclasă
concreta a clasei A, atunci este corectă declaraţia
A a1=new B();
Din punct de vedere conceptual, clasa abstractă modelează un concept general, care trebuie
apoi dezvoltat prin subclase. Subclasele unei clase abstracte pot fi, la rândul lor, abstracte sau
concrete. De exemplu, clasa abstractă FiguraPlana poate avea ca subclase clasele Cerc,
Elipsa, Poligon etc.
Din punct de vedere al progrămarii, clasa abstractă conţine cel puţin o metodă abstractă, adică
o metodă pentru care s-a declarat numai antetul, fără să i se definească şi corpul. O clasă
poate să conţină o metodă abstractă în mai multe moduri:
a/ în corpul clasei este declarată explicit o metodă abstractă;
b/ clasa mosteneşte de la superclasa ei o metodă abstractă, care nu este definita nici în
corpul clasei curente;
c/ clasa implementeaza o interfaţă, dar nu defineşte una sau mai multe din metodele
acesteia.
Dacă apare o astfel de situaţie, iar clasa nu este declarată în mod explicit abstractă, se
genereaza o eroare la compilare.
Exemplu:
În fişierul FiguriPlane.java sunt declarate clasa abstractă FiguraPlana şi clasele instanţiabile
Cerc şi Patrat. Este declarată, de asemenea clasa-aplicaţie în care se testează aceste clase.
186
Programarea orientata pe obiecte în limbajul Java
Interfeţe
Conceptul de interfaţă
Conform principiului încapsulării, fiecare clasă are "interfaţa" sa intrinsecă, prin care poate fi
accesata din exterior. Aceasta "interfaţă" conţine toate datele şi metodele publice ale clasei
respective.
Pentru a compensa lipsa moştenirii multiple, în limbajul Java s-a admis că o clasă poate avea
mai multe interfeţe şi că mai multe clase pot implementa aceeaşi interfaţă. S-a introdus astfel
o nouă categorie de componente, numite interfeţe, care se declară în mod asemănător cu nişte
clase abstracte, dar nu sunt înglobate, aşa cum sunt clasele abstracte, în ierarhia unică de
clase .
Interfaţa este o specificaţie care descrie metodele publice şi variabilele finale publice pe care
trebuie sa le aibă o clasă care implementeaza interfaţa respectivă. Dacă o clasă
implementează mai multe interfeţe, ea conţine toate metodele publice şi variabilele finale
publice ale acestora.
în care interfaţa este numele unei interfeţe, iar celelalte elemente ale declaraţiei sunt
aceleaşi ca în cazul declarării de referinţe la obiecte. Interfeţele pot fi şi ele organizate
ierarhic, aplicându-se principiul moştenirii. În schimb, pentru interfeţe, ierarhia nu mai este
unică, aşa cum este în cazul claselor, şi se admite moştenirea multiplă.
Remarcam că, la fel ca şi clasele abstracte, interfeţele nu pot fi instanţiate. În schimb, unei
variabile referinţă la o interfaţă i se pot da ca valori referinţe către obiecte din orice clasă care
implementează acea interfaţă sau este descendentă a unei astfel de clase.
Pentru a le distinge mai uşor, în documentaţia Java API numele interfeţelor sunt scrise cu
litere cursive (stil italic), în timp ce numele claselor sunt scrise cu litere normale (drepte). De
exemplu, în pachetul java.lang există interfaţa Cloneable care este implementată de toate
clasele din SDK care implementează metoda clone(), deci ale căror obiecte pot fi clonate.
187
Severin Bumbaru
- Figura 1 -
În acest exemplu, interfetele I1 şi I2 sunt derivate din interfata I0 deci moştenesc variabilele
finale şi metodele acesteia, putând avea fiecare, de asemenea, date finale, câmpuri şi metode
proprii. Clasa B implementează interfaţa I1, deci conţine variabilele finale şi metodele
interfeţei I1 şi pe cele mostenite de aceasta de la interfaţa I0. Clasa C implementează
interfeţele I1 şi I2, deci conţine variabilele finale şi metodele interfeţelor I1 şi I2, inclusiv pe
cele moştenite de acestea de la interfaţa I0. Totodată, clasele A, B şi C moştenesc datele şi
metodele clasei A. Faptul că variabilele finale şi metodele interfeţei I0 se propagă la clasa C
atât pe traseul I0-I1-C, cât şi pe traseul I0-I2-C nu mai produce dificultăţi, ca în cazul
moştenirii multiple a claselor, deoarece implementarea efectivă a acestor variabile şi metode
se face în clasa C şi nu in interfeţe.
Constatăm că variabilele referinţă la interfeţe (w1, w2, w3 etc.) sunt utilizate la fel ca şi
variabilele referinta la obiecte ale claselor. Dacă, insă, ştiind că variabila w1 are ca valoare o
referinţă la un obiect din clasa B, vom dori sa folosim instrucţiunea
v4=w1;
vom constata că apare o eroare de compilare, deoarece v4 este referinţă la un obiect al unei
clase, în timp ce w1 este o referinţă la interfaţă. În această situaţie este necesară o conversie
explicită (cast), astfel că vom folosi instrucţiunea
v4=(B)w1;
188
Programarea orientata pe obiecte în limbajul Java
Declararea interfeţelor
O declaraţie de interfaţă introduce un nou tip referinţă, ai cărui membri sunt câmpuri statice
finale şi metode abstracte. În consecinţă, interfaţa se aseamănă cu o clasa abstractă pură,
care nu conţine decât metode abstracte şi câmpuri statice finale şi nu se încadrează în ierarhia
unică de clase descendente din Object (amintim ca o clasa abstractă, în afară de una sau mai
multe metode abstracte, poate conţine şi câmpuri de date şi metode concrete şi este
descendentă a clasei Object).
Cel mai important avantaj al folosirii interfeţelor este că mai multe clase, de pe diferite
ramuri ale arborelui ierarhic al claselor, pot fi "văzute" printr-o singură interfaţă. Se pot
declara variabile referinţă la interfaţă la fel cum se pot declara variabile referinţă la clasă.
Interfaţa este abstractă şi deci nu poate fi instanţiată. În schimb, unei variabile referinţă la
interfaţă i se pot atribui ca valori referinţe la obiecte din orice clasă care implementează
interfaţa respectivă.
Spre deosebire de clase, la care moştenirea multiplă este interzisă (o clasă poate avea numai o
singura superclasă), în cazul interfeţelor moştenirea multiplă este permisă. În consecinţă,
clauza extends poate conţine o listă de nume de interfeţe separate prin virgule.
Corpul interfeţei conţine una sau mai multe declaraţii de membru al interfeţei.
în care tip este un tip primitiv. Modificatorii public, static şi final pentru câmpurile
interfeţei sunt impliciţi, deci folosirea lor este de prisos. Aceasta înseamnă că toate
câmpurile unei interfeţe sunt publice, statice şi finale, deci ele trebuie să fie iniţializate la
declarare şi nu mai pot fi modificate ulterior. Se obisnuieşte ca numele de câmp ale
interfeţelor să fie scrise numai cu majuscule.
Exemplu:
interface CuloareDeBaza {
int ROSU=1, VERDE=2, ALBASTRU=4;
}
189
Severin Bumbaru
b/ Declaraţia de metodă abstractă, care constă din antetul metodei urmat de simbolul ';'
(punct şi virgulă). Ea este deci la fel ca o declaraţie de metodă abstractă din corpul unei clase,
cu următoarele observaţii:
- modificatorii public şi abstract sunt impliciti, deci folosirea lor este de prisos (dar
este permisă);
- modificatorul final nu poate fi folosit, deoarece se declară o metodă abstractă;
- corpul metodei este înlocuit prin simbolul punct şi virgulă, ca la orice metodă abstractă.
Exemplu:
interface Interf1 {
double metoda1();
int metoda2(int a, int b, double c);
}
Ambele metode din exemplul de mai sus sunt implicit publice şi abstracte. Orice clasă, care
implementează aceasta interfaţă, trebuie sa conţină declaraţii de metode cu aceeaşi semnătură
cu cele din interfaţă. La definirea acestor metode îm cadrul claselor, folosirea
identificatorului public este obligatorie.
Se recurge la aceasta tehnică atunci când utilizarea unei clase are sens numai în cadrul altei
clase. Aşadar, prin imbricare se creează o legatură strânsa între două clase. Este posibil,
desigur, ca o clasa să conţină mai multe clase imbricate. Remarcăm că declaraţia clasei
imbricate este tratata ca oricare alta declaraţie de membru al clasei, putând avea şi
modificatori de câmp (de exemplu static).
Fiind membru al clasei, clasa imbricată are acces la toţi membrii clasei care o conţine, chiar şi
la cei privaţi. Dacă clasa imbricată este statică, ea nu poate referi direct decât membrii statici
190
Programarea orientata pe obiecte în limbajul Java
ai clasei care o conţine. Clasa imbricată statică există, la fel ca şi câmpurile sau metodele
statice, numai în cadrul clasei care o conţine, nu şi în instanţele acesteia. În consecinţă, pentru
a referi membrii acestei clase, se foloseşte pentru calificare numele clasei.
Clasa interioară (clasa imbricată nestatică) are câte o instanţă în interiorul fiecărei instanţe a
clasei care o conţine. În consecinţă, accesul la membrii ei se poate face folosind drept
calificator referinţa la o instanţă. Clasa interioară are acces la toţi membrii clasei care o
conţine, atât la cei statici, cât şi la cei de instanţă (nestatici).
Exemplu:
În secţiunea Interfeţe a acestui curs, a fost dat un exemplu de aplicaţie (din fişierul
Integrare.java), în care se face integrarea funcţiilor prin metoda trapezelor. În fişierul
Integrare.java, toate clasele sunt declarate în mod obişnuit, ca nişte clase autonome. În
consecinţă, după compilare se obţin urmatoarele fişiere cu extensia class: Functie1.class,
FunctieReala.class, Integrare.class, Integrator.class, IntegrareTrapeze.class.
Clasa IntegrareTrapeze poate fi folosită în diferite aplicaţii, deci are sens să fie declarată ca o
clasa separată. În schimb, clasa Funcţie1 este specifica numai unei singure aplicaţii. Este deci
preferabil să fie declarata ca o clasă interioară a acestei aplicaţii, aşa cum se face în fisierul
Integrare1.java.
Clasa Functie1, din acest exemplu, a fost declarată ca o clasa interioară (clasă imbricată
nestatică) a clasei-aplicaţie Integrare1. În consecinţă, accesul la membrii ei se poate face
numai printr-o instanţă a clasei Integrare1. În acest scop, s-a procedat astfel:
a/ s-a creat în metoda main() o instanţă a clasei Integrare1 prin instrucţiunea
Integrare1 int1=new Integrare1();
b/ s-a creat o instanţă a clasei Functie1 prin instrucţiunea
Functie1 f1=int1.new Functie1(arg1,arg2,arg3,arg4);
c/ Referinţa f1 la o instanţă a clasei interioare f1 a fost apoi utilizată în mod obişnuit, ca
argument al metodei integrare();
Atragem atenţia asupra modului cum s-a făcut instanţierea clasei interioare Functie1:
operatorul new a fost calificat cu referinta int1 la o instanţă a clasei Integrare1, care o
conţine. Procedeul de mai sus a fost necesar, întrucat metoda main() este statică, astfel că nu
poate accesa direct un membru nestatic al clasei, ci numai prin crearea unei instanţe a
acesteia.
Pentru comparaţie, în acelaşi fişier s-a declarat (tot în clasa-aplicaţie Integrare1) şi o clasa
imbricată statică numită Functie2. Remarcăm că instanţierea acesteia s-a făcut în mod
"obişnuit" prin instrucţiunea
Functie2= func2=new Functie2(arg1,arg2,arg3);
iar utilizarea funcţiei statice f2() conţinute în aceasta s-a facut sub forma "obişnuită"
Functie2.f2(arg1,arg2) . De asemenea, utilizarea funcţiei nestatice f() a acestei clase s-a
făcut "obişnuit", sub forma func2.f(arg), iar instanţa func2 a fost transmisă şi ca argument
al metodei de integrare.
191
Severin Bumbaru
192
Programarea orientata pe obiecte în limbajul Java
în care inf şi sup sunt, respectiv, marginea inferioara şi cea superioara a intervalului de
integrare, iar func este o referinţă la un obiect care contine funcţia de integrat f(x). Având în
vedere că nu avem, deocamdată, în vedere o metoda de integrare numerică particulară, vom
declara interfaţa
interface Integrator {
double integrala(double inf, double sup, FunctieReala func);
}
Cu aceasta ocazie remarcam că, spre deosebire de limbajele de programare tradiţionale, cum
sunt Pascal sau C/C++, în limbajul Java nu există posibilitatea ca o funcţie (metodă) să aibă
ca argument o referinţa la altă funcţie (metodă). În schimb, putem să-i oferim ca argument o
referinţa la un obiect care conţine funcţia respectivă. Astfel, în cazul nostru, func este
referinţă la un obiect care conţine funcţia de integrat f(x). Având în vedere că dorim ca
integratoarele noastre să fie valabile pentru orice funcţie reală f(x), vom declara o interfaţă:
interface FunctieReala {
double f(double x);
}
care conţine metoda abstractă f(x) pe care trebuie să o conţină orice obiect pe care îl oferim
ca argument metodei de integrare din interfaţa Integrator.
193
Severin Bumbaru
- Figura 1 -
Înlocuind pe fiecare din aceste subintervale curba f(x) prin coarda ei, obtinem n trapeze.
Considerand că ariile cuprinse între curbă şi coarda curbei în fiecare din subintervale sunt
mici, aproximăm aria de sub curbă prin suma ariilor celor n trapeze. Se obţine astfel pentru
calcularea integralei I cunoscuta formulă a integrării numerice prin metoda trapezelor dată în
Figura 2.
- Figura 2 -
Pentru calcularea integralei este necesar să se impună şi numărul de paşi n, pe care îl vom
nota în continuare cu nrPasi. Putem astfel declara clasa IntegrareTrapeze, care
implementeaza interfaţa Integrator, în modul următor:
194
Programarea orientata pe obiecte în limbajul Java
Putem acum să scriem o aplicaţie în care se calculează integrala acestei funcţii. În fişierul
Integrare.javasunt date atât declaraţiile de interfeţe şi clase prezentate mai sus, cât şi cea a
clasei-aplicaţie în care se folosesc acestea.
Întrebări
Nivel 1
1. Ce este încapsularea?
2. Care sunt modificatorii de acces pentru câmpuri şi metode?
3. Ce sunt constructorii?
4. Ce particularităţi prezintă declaraţiile de constructori?
5. Poate avea o clasă mai multi constructori? cum se disting aceştia?
6. Ce este constructorul implicit?
7. Ce este agregarea?
8. Ce este moştenirea?
195
Severin Bumbaru
Nivel 2
196
Programarea orientata pe obiecte în limbajul Java
19. Daţi un exemplu de situaţie în care trebuie folosit castul pentru a converti o referinţă.
20. În ce scop au fost definite în clasa Object metodele equals() şi toString()?
21. Să considerăm că clasa A conţine atât metoda main(), cât şi un câmp nestatic a şi o
metodă nestatica met(). Cum putem utiliza în main câmpul a şi metoda met()?
22. Ce asemănări şi deosebiri există între interfeţe şi clasele abstracte?
23. Poate exista moştenire multiplă în cazul interfeţelor?
24. Care sunt proprietăţile implicite ale câmpurilor unei interfeţe?
25. Care sunt proprietăţile implicite ale metodelor unei interfeţe?
26. Poate fi declarată ca finală o metodă a unei interfeţe? Justificati răspunsul.
27. Ce avantaj prezintă declatrarea interfeţei Integrator ca o interfaţă şi nu ca o clasă?
28. Ce avantaj prezintă folosirea unei interfeţe ca argument al unei metode?
29. Ce deosebire există între clasa imbricată şi clasa interioară?
197
Severin Bumbaru
198
Programarea orientata pe obiecte în limbajul Java
- interfaţa grafică.
Interfaţa utilizator grafică (engleză: GUI - graphical user interface) este o interfaţă om-
maşină care permite operatorului să dea comenzi şi să introducă date prin acţionarea asupra
unor obiecte grafice vizualizate pe ecran: butoane, pictograme, meniuri etc.
Interfaţa utilizator grafică a fost introdusă pentru prima dată la Palo Research Center al
firmei Xerox în anii 1970 şi a fost apoi aplicată de firma Apple pe calculatoarele Macintosh
în 1984. În prezent, cele mai răspândite interfeţe utilizator grafice sunt sistemul Windows al
firmei Microsoft şi cele de tip X-Window folosite în sistemele de operare din categoria Unix.
Un avantaj important al platformei Java 2 este că oferă pachete de clase, care permit
programatorului realizarea comodă şi eficientă a interfeţelor grafice. În prezent, există două
grupuri de pachete de clase pentru interfeţe grafice, care corespund la două principii diferite
de realizare a interfeţei:
2. JFC (engleză: Java Foundation Classes - clase de bază Java), este un set de pachete de
clase, care extind posibilităţile oferite de AWT, pentru realizarea de interfeţe utilizator de
calitate superioară. Dintre acestea, cele mai larg folosite constituie setul cunoscut sub numele
de cod Swing (sau JFC/Swing), care oferă posibilitatea de a realiza interfeţe grafice al căror
aspect nu depinde de platforma pe care se execută programul.
199
Severin Bumbaru
Se ştie că autorii limbajului Java şi-au propus ca programele scrise în acest limbaj să poată fi
executate pe orice platformă. Sub acest aspect, realizarea interfeţei grafice a ridicat probleme
dificile deoarece, pe diferite platforme, atât setul de obiecte grafice utilizate, cât şi aspectul
acestora, pot fi diferite.
La realizarea setului de dezvoltare AWT s-a acceptat ideea că în program se lucrează cu nişte
obiecte grafice abstracte, care sa fie comune tuturor platformelor. Obiectul grafic abstract, aşa
cum este el luat în consideraţie în AWT, se caracterizează prin anumite atribute şi un anumit
comportament, dar aspectul său pe ecran poate sa difere de la o platformă la alta.
Avantajul utilizării AWT este că utilizatorul vede pe ecran obiectele care compun interfaţa
grafică sub forma cu care el este obişnuit pe platforma pe care lucrează. La aceasta se adaugă
şi faptul că AWT se înţelege şi se utilizează relativ usor. Dezavantajul este că o astfel de
interfaţă nu poate folosi decât obiectele grafice existente pe toate platformele (mulţimea de
clase de obiecte grafice din AWT este "intersecţia" mulţimilor de clase grafice de pe diferite
platforme), ceeace restrânge posibilitatea programatorilor de a realiza interfeţe utilizator
diversificate şi originale.
Autorii setului de dezvoltare JFC/Swing au decis să realizeze un set de clase de interfaţă care
sunt complet independente de platformă. În acest scop, s-a renunţat la metodele native,
clasele Swing fiind programate total în Java. Unele din aceste clase le extind sau le dublează
pe cele din AWT, iar altele sunt complet noi. Mai mult, se dă posibilitatea programatorului să
dezvolte propriile sale clase de obiecte grafice prin extinderea celor existente. Este drept insă
că aceasta se plăteşte prin creşterea complexităţii interfeţei grafice şi a nivelului de
competenţă cerut de la programator.
Clasa Component
Având în vedere că întreaga ierarhie de clase de obiecte de interfaţă utilizator grafică folosite
în Java (atât cele din AWT, cât şi din JFC/Swing) are ca rădăcină clasa Component, este util
să începem studiul cu această clasă.
Componenta este un obiect grafic care este afişabil pe ecran şi poate interacţiona cu
utilizatorul.
200
Programarea orientata pe obiecte în limbajul Java
Clasa Component este o clasă abstractă, derivată direct din clasa Object.
Dintre numeroasele metode ale acestei clase, prezentăm aici numai câteva mai frecvent
folosite:
public boolean isVisible() - testează dacă această componentă este vizibilă, atunci
când este vizibil şi containerul în care se găseşte;
public void setVisible(boolean visible) - face componenta sa fie vizibilă sau
nu, după cum argumentul este true sau false;
public boolean isShowing() - determină dacă componenta este efectiv afişată pe
ecran;
public Point getLocation() - întoarce locaţia componentei (poziţia colţului din
stânga sus în containerul-părinte) sub forma unei instanţe a clasei Point;
public void setLocation(Point location) - setează noua poziţie a componentei
în cadrul containerului părinte;
public Dimension getSize() - întoarce dimensiunea componentei;
public void setSize(int latime, int inaltime) - setează noile dimensiuni ale
componentei.
În aceste metode se utilizează şi clasele auxiliare Point şi Dimension din pachetul java.awt.
Instanţele clasei Point conţin coordonatele unui punct. Se consideră că originea sistemului de
coordonate se află în colţul din stânga sus, axa Ox este orientată către dreapta, iar axa Oy este
orientata în jos. Instanţele clasei Dimension conţin dimensiunile componentei: lăţimea şi
înălţimea.
O prezentare mai amplă a metodelor acestei clase este dată în indexul de clase. Descrierea
completă este dată în documentaţia Java API.
Întrucât interfaţa grafică conţine componente, ea este un container. Vom studia ulterior
diferite clase de containere dar, deocamdată, ne vom opri asupra clasei JFrame, care se
foloseşte în aplicaţiile Java.
În sistemul JFC/Swing, interfaţa utilizator grafică a unei aplicaţii este o instanţă a clasei
JFrame din pachetul javax.swing, sau este o subclasă a acesteia. În mod obişnuit, instanţele
clasei JFrame sunt ferestre care au la partea superioara o bară de titlu. În partea stângă a barei
de titlu există un buton, la apăsarea căruia se desfaşoară un menu cu urmatoarele opţiuni:
Restore, Move,Size, Minimize, Maximize, Close. Cînd meniul este desfăşurat, acste opţiuni
pot fi selectate cu mouse-ul sau acţionând tasta corespunzătoare literei subliniate. La partea
dreaptă a barei de titlu exista trei butoane, care au acelasi efect ca opţiunile Minimize,
Maximize şi Close din meniul menţionat.
Selecţionarea opţiunilor sau acţionarea butoanelor din bara de titlu produce asupra ferestrei
efectul corespunzător. Fereastra poate fi deplasată pe ecran dacă punem cursorul mouse-lui
201
Severin Bumbaru
pe bara de titlu şi o tragem, ţinând butonul de mouse apăsat. Dimensiunile ferestrei pot fi
modificate trăgând cu mouse-ul oricare din laturile sau din colţurile ei.
Modul cel mai simplu de a realiza o aplicaţie cu interfaţă grafică este să folosim direct o
instanţă a clasei javax.swing.JFrame, care extinde clasa Frame din AWT. Clasa are o
structură destul de complicată şi este descrisă complet în documentaţia Java.API. Pentru a nu
intra de la început în detalii, vom da, deocamdată, numai cateva informaţii strict necesare
pentru folosirea acestei clase, urmând să revenim ulterior cu indicaţii suplimentare.
Clasa JFrame are numeroase metode, atât proprii, cât şi moştenite de la superclasele ei. În
afară de metodele moştenite de la clasa Component, deocamdată, vom mai menţiona aici
numai două, care sunt moştenite de la clasa Frame:
La crearea ei, instanţa clasei JFrame nu este vizibilă. Pentru a o face vizibilă se foloseşte
metoda public void setVisible(boolean visible) a clasei Component.
În fişierul TestJFrame1.java este dat un exemplu de aplicaţie care are o interfaţă utilizator din
clasa JFrame. Iată conţinutul acestui fişier:
import java.awt.*;
import javax.swing.*;
class TestJFrame1 {
static JFrame iug;
202
Programarea orientata pe obiecte în limbajul Java
În prima linie este importat pachetul javax.swing, care conţine principalele clase ale
JFC/Swing, inclusiv clasa JFrame.
În clasa TestJFrame1 (care este aplicaţia noastră), s-a declarat câmpul static iug, care este o
referinţă la JFrame. În prima instrucţiune din metoda main se creeaza o instanţă a clasei
JFrame şi se dă câmpului iug ca valoare referinţa la instanţa nou creată. Se folosesc apoi
metodele setSize(), setLocation() şi setVisible() din clasa Component, pentru a
stabili dimensiunile ferestrei şi pozitia ei pe ecran şi a o face vizibilă. Se afişeaza apoi în
fereastra terminalului de la care s-a lansat aplicaţia (la ieşirea standard a sistemului) titlul,
coordonatele şi dimensiunile ferestrei, folosind metodele adecvate din clasele JFrame şi
Component.
La lansarea în execuţie a acestei aplicaţii, în colţul din stânga sus al ecranului apare o
fereastră de dimensiuni 300x100 pixeli, cu titlul "Exemplu de JFrame", iar la ieşirea standard
apar toate mesajele afişate prin instrucţiunile System.out.println(...) din metoda main.
Putem acum să acţionăm asupra ferestrei cu mouse-ul, având grijă să nu dam comanda Close
(nici din menu, nici acţionând butonul din dreapta sus marcat cu X): putem să deplasăm
fereastra tragând cu mouse-ul de bara de titlu, să o iconificăm (minimizăm), deiconificăm,
maximizam, restaurăm. Putem, de asemenea, să modificăm dimensiunile ferestrei, trăgând cu
mouse-ul de marginile acesteia.
La sfârşit, putem da comanda Close (din meniul ferestrei sau apasând butonul marcat cu X) şi
fereastra se închide. Constatăm însă că închiderea ferestrei nu are drept consecinţa şi
încheierea executării aplicaţiei: în fereastra terminalului din care am lansat aplicaţia nu a
apărut promptul sistemului de operare. Pentru a încheia executarea aplicaţiei, trebuie să dăm
de la terminal comanda <Control>-C.
Se numeste eveniment orice modificare care are loc, fie în starea dispozitivelor de intrare, fie
în cea a obiectelor grafice de pe ecran: apăsarea sau eliberarea unei taste, deplasarea mouse-
ului, apăsarea sau eliberarea unui buton al mouse-ului, deschiderea sau închiderea unei
ferestre, efectuarea unui clic de mouse pe un obiect de control (buton, caseta de validare, bara
de defilare etc.), intrarea cursorului de mouse în câmpul activ al unui obiect grafic sau
203
Severin Bumbaru
părăsirea acestuia etc. Pot exista, desigur, şi alte tipuri de evenimente, dar aici ne interesează
numai cele legate de interfaţa grafică a aplicaţiei.
Interacţiunea dintre operator şi aplicaţie într-un sistem bazat pe evenimente decurge astfel:
operatorul provoacă generarea unui eveniment, acţionând asupra tastaturii, mouse-ului sau a
altui dispozitiv de intrare, iar programul "răspunde" la acest eveniment prin executarea unei
anumite acţiuni. Acest mod de lucru impune o nouă concepţie în proiectarea programelor,
numită programarea orientată pe evenimente.
Apariţia interfeţelor grafice a permis introducerea unei noi concepţii în interacţiunea dintre
operator şi aplicatie, astfel ca iniţiativa să îi revină operatorului uman, iar programul să
execute comenzile acestuia. S-a trecut, astfel, de la programarea procedurală tradiţională la
programarea orientată pe evenimente (engleză: Event-Oriented Programming), cunoscuta şi
sub numele de programare ghidată de evenimente (engleză: Event Driven Programming).
In JDK 1.0 s-a folosit un model de evenimente bazat pe moştenire, reprezentat prin clasa
Event. Această clasă este menţinuta în continuare, din motive de compatibilitate a
programelor din JDK 1.0 cu cele din versiunile ulterioare, dar nu mai este recomandată.
Începând cu JDK 1.1 s-a introdus un nou model de evenimente, bazat pe delegare (engleză:
Delegation Event Model). Conform acestui model, distingem trei catedorii de obiecte care au
relaţii cu evenimentele:
- obiecte care generează evenimente, numite surse de evenimente (engleza: Event Source);
- obiecte care captează şi tratează evenimentele, numite ascultători de evenimente
(engleză: Event Listener);
- evenimentele propriu-zise (engleză: Event), care sunt tot obiecte, generate de surse şi
captate de ascultători.
Un eveniment generat de o sursă poate fi interceptat de mai mulţi ascultători. Este posibil ca
un obiect să se "asculte" pe sine însuşi, deci să îşi trateze propriile evenimemte. Tocmai
această legatură strânsa între sursele şi ascultătorii de evenimente este caracteristica esenţiala
a "modelului delegării": ea constă în faptul că sursa transmite evenimentele generate de ea
204
Programarea orientata pe obiecte în limbajul Java
numai către ascultătorii care au fost ataşaţi la sursa respectivă, iar ascultătorul primeşte
evenimente numai de la sursele la care a fost ataşat.
În modelul de evenimente din JDK 1.0 sursa transmitea evenimentele fără o destinaţie
anume, iar consumatorii de evenimente trebuiau sa depisteze singuri ce fel de evenimente
sunt acestea şi de la ce sursă provin şi, în funcţie de aceasta, să decidă dacă interceptează sau
nu evenimentul respectiv.
Remarcăm, deci, că se aplică cu consecvenţă principiul programării orientate pe obiecte, în
sensul că aplicaţia este constituită dintr-un ansamblu de obiecte care interacţionează. Putem
spune că evenimentul este o nouă forma de mesaj, care se transmite între obiectele-sursă şi
obiectele-ascultători. Fiecare ascultator trebuie "adăugat" la sursă. În acest fel, sursa "ştie"
căror ascultători să le transmită evenimentele pe care le generează.
Este important sa reţinem faptul că, în acest model de evenimente, sursele sunt întotdeauna
componente ale interfeţei grafice. Când cursorul mouse-ului intră în interiorul unei
componente sau iese din aceasta, sau când facem click de mouse pe o componenta,
evenimentul nu este generat direct de către mouse, ci de către componenta respectivă a
interfeţei. În mod similar, când apăsăm sau eliberăm o tastă, evenimentul nu este generat
direct de către tastă, ci de către acea componentă a interfeţei grafice, care este activă în
momentul respectiv. Evenimentele pot fi generate de o componentă şi atunci când au loc
modificari ale stării acesteia ca urmare a executării unor instrucţiuni din program (de
exemplu când se deschide sau se închide o fereastră, etc.).
Clase de evenimente
public Object getSource() - care întoarce o referinţă către obiectul care a generat
evenimentul respectiv;
public String toString() - care întoarce o reprezentare sub forma de şir a
obiectului.
Orice eveniment AWT (generat de componente AWT sau JFC/Swing) conţine un câmp
protejat de tip int numit id, a cărui valoare indică tipul evenimentului. Toate clasele de
evenimente conţin câmpuri statice finale de tip int, al căror nume indică tipul de eveniment şi
a căror valoare este valoarea corespunzătoare a câmpului id. Valoarea acestui câmp se poate
obţine prin metoda
public int getId()
Evenimentele pot fi de nivel coborât (low level event) sau semantice. Numele claselor de
evenimente de nivel coborât indică fie componenta, fie dispozitivul de intrare care le-a
generat, de exemplu: ComponentEvent, WindowEvent, MouseEvent, KeyEvent. Numele
claselor de evenimente semantice indică mai curând tipul de eveniment, decât sursa acestuia,
205
Severin Bumbaru
Există câte o interfaţă de ascultător pentru fiecare clasă de eveniment. De exemplu, pentru
ComponentEvent există interfaţa ComponentListener, pentru WindowEvent exista intefaţa
WindowListener etc.
Pentru a se uşura munca programatorilor, pentru unele interfeţe care conţin mai multe
metode, se oferă şi prototipuri de clase care implementează interfaţa respectivă, numite
adaptoare. De exemplu, clasa WindowAdapter implementează interfaţa WindowListener,
clasa MouseAdapter implementează interfaţa MouseListener etc.
Evenimentele generate de fereastră sunt instanţe ale clasei WindowEvent şi sunt ascultate de
instanţe ale unor clase care implementează interfaţa WindowListener sau extind clasa
WindowAdapter. Toate aceste clase şi interfeţe se găsesc în pachetul java.awt.event.
206
Programarea orientata pe obiecte în limbajul Java
Pentru a se trata evenimentele generate de fereastră, este necesar să se declare o clasă care
implementează interfaţa WindowListener. În această clasă se definesc toate metodele
interfeţei, astfel încât acestea să execute acţiunile adecvate evenimentelor corespunzătoare.
Exemplu
În fişierul Evenim1.java este dat ca exemplu următorul program, în care se urmăresc
evenimentele generate de o fereastră din clasa JFrame.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Evenim1 {
static JFrame iug; // referinta la interfata grafica
static AscultFereastra af; // referinta la ascultator
/* Metoda principala */
public static void main(String args[]) throws Exception {
af=new AscultFereastra(); // instantierea ascultatorului
iug=new JFrame("Urmarire evenimente fereastra");
iug.setSize(300,100);
iug.setLocation(new Point(100,50));
iug.setVisible(true);
207
Severin Bumbaru
Pentru ascultătorul de fereastră a fost creată clasa imbricată statică AscultFereastra, în care
sunt implementate toate metodele interfeţei WindowListener. În cazul de faţă, aceste metode
nu fac altceva, decât că afişează pe terminal un mesaj privind evenimentul care s-a produs.
Acest mesaj conţine şi evenimentul interceptat e, convertit în şir. În metoda main(), se
construiesc instanţele claselor JFrame şi AscultFereastra, după care se adaugă ascultătorul af
la fereastra iug prin metoda addWindowListener(). Se stabilesc, de asemenea, dimensiunea
şi poziţia ferestrei şi se face fereastra vizibilă.
După ce a fost pusă aplicaţia în execuţie, pe ecran apare o fereastră cu titlul "Urmarire
evenimente fereastra". La terminal putem urmări succesiunea evenimentelor care se produc.
Ca şi în exemplul precedent, ieşirea din aplicaţie se face de la tastatura, prin comanda
<Control>-C.
Remarcăm că, în exemplul de mai sus, a fost necesar ca, în clasa care implementează interfata
WindowListener, să se definească toate metodele acesteia. Dacă ne sunt necesare numai
unele din aceste metode, este preferabil sa obţinem clasa AscultFereastra prin extinderea
clasei WindowAdapter. Această ultimă clasă conţine toate metodele interfeţei
WindowListener, dar corpurile lor sunt vide, deci metodele nu fac nimic. În acest fel, în clasa
derivată este suficient să redefinim numai metodele care ne sunt necesare, ca în exemplul din
secţiunea următoare.
Exemplu
În fişierul Inchidere.java se dă o modificare a clasei Evenim1 din exemplul precedent.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Inchidere {
static JFrame iug; // referinta la fereastra
static AF af; // referinta la ascultator
208
Programarea orientata pe obiecte în limbajul Java
/* Metoda principala */
public static void main(String args[]) throws Exception {
af=new AF(); // instantierea ascultatorului
iug=new JFrame("Urmarire evenimente fereastra");
iug.setSize(300,100);
iug.setLocation(new Point(100,50));
iug.setVisible(true);
iug.addWindowListener(af); // adaugarea ascultatorului
System.out.println("Titlul ferestrei este: "+iug.getTitle());
System.out.println("Coltul din stanga sus este in punctul: "+
iug.getLocation());
System.out.println("Dimensiunile ferestrei: "+iug.getSize());
}
}
În acest program, în afară de modificarea numelor claselor, s-au mai făcut următoarele
modificări:
- clasa de ascultare a evenimentelor AF extinde clasa WindowAdapter, deci implementează
in mod indirect interfaţa WindowListener, prin intermediul acesteia;
- s-au definit numai trei din cele şapte metode ale intefeţei WindowListener; celelalte
metode sunt moştenite de la clasa WindowAdapter, deci nu fac nimic;
- în metoda windowClosing()s-a introdus instrucţiunea System.exit(0); În consecinţă,
când acţionăm butonul de închidere al ferestrei, sau selectăm optiunea Close din meniu, se
încheie executarea aplicaţiei.
Evenimente de mouse
209
Severin Bumbaru
javax.swing.event.MouseInputAdapter.
Exemplu
În fişierul EvMouse.java este dat un exemplu de aplicaţie în care se ascultă evenimentele de
mouse generate de o instanţă a clasei JFrame şi se afişează la terminal conţinutul acestor
evenimente. În acest scop, în clasa imbricată AMouse au fost implementate toate metodele
interfeţei MouseListener, dar aceste metode nu fac altceva, decât să afişeze la terminal
evenimentul recepţionat.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class EvMouse {
static JFrame iug; // referinta la fereastra
static AF af; // referinta la ascultator de fereastra
static AMouse am; // referinta la ascultator de mouse
210
Programarea orientata pe obiecte în limbajul Java
/* Metoda principala */
public static void main(String args[]) throws Exception {
af=new AF(); // instantierea ascultatorului
am=new AMouse(); // instantierea ascultatorului de mouse
iug=new JFrame("Urmarire evenimente de mouse");
iug.setSize(300,100);
iug.setLocation(new Point(100,50));
iug.setVisible(true);
iug.addWindowListener(af); // adaugarea ascultatorului de fereastra
iug.addMouseListener(am); // adaugarea ascultatorului de mouse
}
}
Executând acest program, putem urmări pe ecran succesiunea de evenimente care se produc
atunci când acţionăm cu mouse-ul asupra ferestrei aplicaţiei.
Exemplu
În fişierul EvMouse1.java este dat un exemplu de aplicaţie, în care se urmăresc situaţiile în
care este apăsat unul dintre butoanele mouse-ului, atunci când cursorul acestuia se găseşte
pe suprafaţa ferestrei aplicaţiei. Întrucât nu se folosesc toate metodele interfeţei
MouseListener, clasa ascultătorului de mouse AMouse s-a creat prin extinderea clasei
java.awt.event.MouseAdapter.
211
Severin Bumbaru
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class EvMouse1 {
static JFrame iug; // referinta la fereastra
static AF af; // referinta la ascultator de fereastra
static AMouse am; // referinta la ascultator de mouse
/* Metoda principala */
public static void main(String args[]) throws Exception {
af=new AF(); // instantierea ascultatorului
am=new AMouse(); // instantierea ascultatorului de mouse
iug=new JFrame("Urmarire evenimente de mouse");
iug.setSize(300,100);
iug.setLocation(new Point(100,50));
iug.setVisible(true);
iug.addWindowListener(af); // adaugarea ascultatorului de fereastra
iug.addMouseListener(am); // adaugarea ascultatorului de mouse
}
}
212
Programarea orientata pe obiecte în limbajul Java
&. Butonul de mouse a cărui mască, intersectată cu codul mod, dă rezultat nenul este cel
apăsat.
Codul modificatorilor mod poate fi folosit şi pentru a determina daca, în timp ce s-a apăsat
butonul de mouse, era apăsată şi una din tastele auxiliare Ctrl, Alt sau Shift, folosind
măştile corespunzătoare acestor taste. Totuşi, aici s-a preferat utilizarea metodelor
isControlDown(), isAltDown() şi isShiftDown() ale clasei InputEvent.
Evenimente de tastă
De câte ori se apasă sau se eliberează o tastă, componenta activă a interfeţei grafice
generează un eveniment de tastă din clasa java.awt.event.KeyEvent. Acesta poate fi tratat cu
un "ascultător de taste" care implementează interfaţa java.awt.event.KeyListener sau care
extinde clasa java.awt.event.KeyAdapter.
Evenimentul generat de tastă conţine, în afară de codul tastei acţionate, şi informaţii privind
starea tastelor auxiliare Ctrl, Alt şi Shift în momentul producerii evenimentului respectiv.
Aceste stări pot fi detectate cu metodele corespunzătoare ale interfeţei superclasei
java.awt.event.InputEvent.
Exemplu
În fişierul EvTaste.java se dă un exemplu de aplicaţie, în care se urmăreşte apariţia unor
evenimente de tastă, care sunt generate de fereastra aplicaţiei, atunci când ea este activă şi
se actionează o tastă.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class EvTaste {
static AF af=new AF(); // ascultatorul de fereastra
static ATaste at=new ATaste(); // ascultatorul de taste
static IUG iug=new IUG("Urmarirea evenimentelor de tasta");
213
Severin Bumbaru
IUG(String titlu) {
super(titlu);
setSize(300,100);
setLocation(new Point(100,50));
setVisible(true);
addWindowListener(af); // adaugarea ascultatorului de fereastra
addKeyListener(at); // adaugarea ascultatorului de taste
}
}
/* Metoda principala */
public static void main(String args[]) {
}
}
Ascultătorul de taste este realizat prin clasa imbricată ATaste, care implementează interfaţa
KeyListener. Aşa cum sunt ele redefinite aici, metodele ascultătorului de taste afişează
evenimentul respectiv la terminal.
Pentru a detecta în ce condiţii a fost generat evenimentul (codul tastei care a fost apasată,
inscripţia de pe tastă, ce taste auxiliare erau apăsate), putem utiliza metodele claselor
KeyEvent şi superclasei acesteia InputEvent.
Exemplu
În fişierul EvTaste1.java este dat un exemplu de aplicaţie în care se folosesc metodele din
clasele KeyEvent şi InputEvent pentru a detecta situaţiile în care au apărut evenimentele
captate de către ascultătorul de taste. Acesta a fost realizat prin extinderea clasei
214
Programarea orientata pe obiecte în limbajul Java
KeyAdapter.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class EvTaste1 {
static AF af=new AF(); // ascultatorul de fereastra
static ATaste at=new ATaste(); // ascultatorul de taste
static IUG iug=new IUG("Urmarirea evenimentelor de tasta");
/* Metoda principala */
public static void main(String args[]) {
}
}
215
Severin Bumbaru
O prezentare succintă a clasei JFrame este dată în Indexul de clase, iar cea completă se
găseşte în Java API, completată cu cea din Tutorialul Java.
Exemplul 1
În fişierul AdComp.java este un exemplu de aplicaţie, în care se testează adăugarea la
fereastra principală a unui buton şi se face contorizarea numărului de apăsări pe butonul
respectiv. În acest exemplu se arată:
- cum se adaugă la fereastra aplicaţiei un buton (o instanţă a clasei JButton);
- cum se tratează evenimentele de acţiune generate la apasarea butonului.
În locul unui buton se putea folosi orice altă componentă.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class AdComp {
static String textButon="Numar de actionari: ";
static int contorActionari=0;
static AF af=new AF();
static AB ab=new AB();
static JButton buton=new JButton(textButon+contorActionari);
static IUG iug=new IUG("Un buton de contorizare a apasarilor");
216
Programarea orientata pe obiecte în limbajul Java
Remarcăm următoarele:
- S-au declarat trei clase imbricate:
. clasa IUG, care este interfaţa utilizator grafică;
. clasa AF, care este ascultătorul evenimentelor de fereastră, necesar pentru închiderea
aplicaţiei;
. clasa AB, care asculta şi tratează evenimentele de acţiune generate de buton.
- În constructorul clasei IUG se adaugă la containerul de conţinut al ferestrei principale
(contentPane) butonul button, din clasa JButton. În acest scop, se foloseşte instrucţiunea
getContentPane().add(buton);
- În acelaşi constructor, se adaugă la buton ascultătorul de evenimente de fereastră af şi
ascultătorul de evenimente de acţiune ab. Obiectele indicate de referinţele buton af şi ab au
fost construite înainte de a se construi interfaţa grafică.
- Metoda actionPerformed(Event e) din clasa imbricată AB este invocată atunci când se
produce un "eveniment de acţiune". Întrucat în programul nostru sursa evenimentelor de
acţiune (la care a fost inregistrată instanţa ab a clasei AB) este butonul buton, evenimentele
de acţiune vor fi generate atunci când se apasă pe buton (se face click de mouse pe suprafaţa
butonului). În această metodă se incrementează variabila contorAcţionări şi se pune în buton
un nou text, care indică valoarea contorului. În acest scop se foloseşte instrucţiunea
buton.setText(text).
Când punem în execuţie această aplicaţie, pe ecran apare o fereastră cu titlul "Un buton de
217
Severin Bumbaru
Având în vedere ca butonul de contorizare a apăsărilor poate fi, în principiu, folosit şi în mai
multe aplicaţii, putem să îl declarăm ca o clasa publică într-un fişier separat, ca în fişierul
ButonContor.java, reprodus aici:
import java.awt.event.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.*;
218
Programarea orientata pe obiecte în limbajul Java
class AdComp1 {
static AF af=new AF();
static ButonContor buton=new ButonContor("Contor apasari");
static IUG iug=new IUG("Un buton de contorizare a apasarilor");
Fiecare clasă de container are un gestionar de poziţionare implicit. Acesta este BorderLayout
pentru Frame şi JFrame.contentPane şi FlowLayout pentru clasa Panel. Modificarea
gestionarului de pozitionare se face prin metoda
public void setLayout(LayoutManager manager)
219
Severin Bumbaru
Clasa BorderLayout
Începem cu această clasă, deoarece este gestionarul de poziţionare implicit pentru conţinutul
instanţelor clasei JFrame. În acest caz, se consideră că suprafaţa containerului este imparţită
în cinci zone numite, respectiv, NORTH, SOUTH, WEST, EAST si CENTER. În fiecare din
aceste zone se poate plasa numai o singură componentă, care poate fi însă ea însăşi un
container. Adăugarea de componente la container se face, în acest caz, folosind metoda
add(componenta, BorderLayout.ZONA), unde componenta este referinţa la componenta
adaugată, iar ZONA este una din cele cinci zone menţionate mai sus. Dacă se foloseşte
metoda add(componenta), fără a indica zona, componenta respectivă este plasată implicit în
zona CENTER, aşa cum s-a procedat în secţiunea precedenta, în exemplul din fişierul
AdComp.java.
Exemplu
În fişierul Butoane.java este dat un exemplu, în care se plasează în fereastra aplicaţiei câte un
buton în fiecare din zonele NORTH,WEST, CENTER şi EAST şi o etichetă (instanţă a clasei
javax.swing.JLabel) în zona SOUTH. Pe suprafaţa fiecărui buton este afişat numărul de
apăsări succesive. În eticheta de la partea de jos se afişează numărul total de acţionări asupra
tuturor butoanelor.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Butoane {
static AF af=new AF(); // ascultatorul de fereastra
static AA aa=new AA(); // ascultatorul de actiuni
static IUG iug=new IUG("Adaugare de butoane");
static int totalActionari=0;
IUG(String titlu) {
super(titlu);
Container cp=getContentPane();
setSize(400,300);
setLocation(200,150);
addWindowListener(af);
b1=new ButonContor("N");
b1.addActionListener(aa);
cp.add(b1, BorderLayout.NORTH);
b2=new ButonContor("E");
b2.addActionListener(aa);
cp.add(b2, BorderLayout.EAST);
b3=new ButonContor("W");
b3.addActionListener(aa);
cp.add(b3, BorderLayout.WEST);
b4=new ButonContor("C");
220
Programarea orientata pe obiecte în limbajul Java
b4.addActionListener(aa);
cp.add(b4, BorderLayout.CENTER);
cp.add(lab, BorderLayout.SOUTH);
setVisible(true);
}
}
Remarcăm următoarele:
- clasa ButonContor extinde clasa JButton şi implementează interfaţa ActionListener, fiind
definită de noi ca o clasa publică în fişierul ButonContor.java. În constructorul acestei clase
există instrucţiunea addActionListener(this), astfel că fiecare buton îşi ascultă propriile
evenimente de acţiune;
- în metoda actionPerformed(ActionEvent e) din clasa ButonContor se incrementează
contorul intern al butonului respectiv şi se afişează valoarea contorului pe suprafaţa
butonului;
- în constructorul clasei imbricate IUG se adaugă câte un ButonContor în fiecare din zonele
NORTH, WEST, EAST si CENTER ale ferestrei principale.
- pentru a fi totalizat numărul de apăsări al tuturor butoanelor, la fiecare buton se adaugă, de
asemenea, ascultătorul de acţiune ac, instanţă a clasei AC, care incrementează câmpul total
al clasei Butoane;
- eticheta din zona SOUTH face parte din clasa javax.swing.JLabel. Instanţele acestei clase
au ca principal rol afişarea pe suprafaţa lor a unui text şi/sau a unei pictograme. În exemplul
nostru se afişează numarul total de acţionări ale butoanelor (valoarea câmpului total al
clasei Butoane);
- adăugarea la fereastra aplicaţiei a butoanelor şi a etichetei se face în constructorul clasei
IUG.
Punând în execuţie această aplicaţie, putem vedea cum se plasează cele cinci componente pe
suprafaţa ferestrei. Putem urmări, de asemenea, cum se modifică automat dimensiunile
componentelor atunci când modificăm cu mouse-ul dimensiunile ferestrei. Dacă facem click
de mouse pe oricare din butoane, se modifică atât contorul afişat pe butonul respectiv, cât şi
numărul total de acţionari afişat pe eticheta din partea inferioară a ferestrei.
221
Severin Bumbaru
Clasa FlowLayout
Exemplul 1
În fişierul ButoaneF.java este dat un exemplu de aplicaţie, în care se testează gestionarul de
poziţionare FlowLayout. În fereastra aplicaţiei sunt plasate mai multe butoane din clasa
ButonContor şi un buton de anulare a tuturor contoarelor. Numărul de butoane de contorizare
este dat la punerea în execuţie a aplicatiei, ca parametru în linia de comanda. Modificând cu
mouse-ul dimensiunile ferestrei, putem constata cum se modifică în mod corespunzator
dimensiunile şi amplasarea butoanelor. Îtrucât la crearea gestionarului de poziţionare nu s-a
precizat alinierea componentelor, modul de aliniere este implicit CENTER.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ButoaneF {
static AF af=new AF(); // ascultatorul de fereastra
static Anulare anulare=new Anulare(); // ascultatorul
// butonului de anulare
static IUG iug; // fereastra aplicatiei
static int numarButoane;
222
Programarea orientata pe obiecte în limbajul Java
Container cp=getContentPane();
setSize(300,150);
setLocation(200,150);
addWindowListener(af);
cp.setLayout(new FlowLayout()); // setarea gestionarului
// de pozitionare
bc=new ButonContor[numarButoane];
for(int i=0; i<numarButoane; i++) {
bc[i]=new ButonContor("B"+(i+1)); // crearea butoanelor
cp.add(bc[i]); // adaugarea butoanelor la contentPane
}
cp.add(br); // adaugarea butonului de anulare
br.addActionListener(anulare); // adaugarea ascultatorului
setVisible(true);
}
}
223
Severin Bumbaru
Exemplul 2
În fişierul Aliniere.java este dată o modificare a aplicaţiei din exemplul precedent, în care s-
au introdus trei butoane suplimentare, care au ca efect modificarea alinierii butoanelor pe
suprafaţa containerului. În acest scop:
- Pentru a se afla care buton a fost acţionat, în metoda actionPerformed() a clasei
imbricate de ascultare a evenimentelor de acţiune Actiuni s-a invocat metoda
getActionCommand()a clasei ActionEvent. Această metodă întoarce un String care, în mod
obisnuit, este textul de pe butonul respectiv. Este posibil, totuşi, să se modifice acest şir
folosind metoda setActionCommand(String command), care există în toate clasele care
generează evenimente de acţiune, de exemplu în clasa AbstractButton, din care este derivată
şi clasa JButton.
- Modificarea alinierii se face invocând pentru gestionarul de poziţionare metoda
setAlignment(int align) a clasei FlowLayout. Dupa ce s-a aplicat această metodă, se
invocă pentru conţinutul ferestrei (contentPane) metoda doLayout() a clasei Container,
pentru a se aplica efectiv noua poziţionare.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Aliniere {
static AF af=new AF(); // ascultatorul de fereastra
static Actiuni actiuni=new Actiuni(); // ascultatorul
// butoanelor de aliniere si de anulare
static IUG iug; // fereastra aplicatiei
static int numarButoane;
static FlowLayout layout=new FlowLayout();
224
Programarea orientata pe obiecte în limbajul Java
Container cp=getContentPane();
setSize(300,150);
setLocation(200,150);
addWindowListener(af);
cp.setLayout(layout); // setarea gestionarului de pozitionare
bc=new ButonContor[numarButoane];
for(int i=0; i<numarButoane; i++) {
bc[i]=new ButonContor("B"+(i+1)); // crearea butoanelor
cp.add(bc[i]); // adaugarea butoanelor la contentPane
}
cp.add(bas); cp.add(bac); cp.add(bad); // adaugare butoane
// de aliniere
cp.add(br); // adaugarea butonului de anulare
br.addActionListener(actiuni); // adaugarea ascultatorului
bas.addActionListener(actiuni);
bac.addActionListener(actiuni);
bad.addActionListener(actiuni);
setVisible(true);
}
}
225
Severin Bumbaru
Clasa GridLayout
Deşi în constructor se indică atât numărul de linii, cât şi cel de coloane ale grilei, în realitate
numai numărul de linii este respectat, în timp ce numărul de coloane este practic ignorat. La
stabilirea amplasării componentelor în container, dacă numarul lor total este mai mic sau egal
cu cel de linii, toate componentele vor fi aşezate vertical, una sub alta. Dacă numărul de
componente este mai mare decât numărul de linii, numărul de coloane se stabileşte automat,
prin împărţirea numărului de componente la cel de linii, cu rotunjire în plus. Plasarea efectivă
a componentelor în celulele grilei astfel creeate se face apoi de la stânga la dreapta şi de sus
in jos, la fel ca în cazul gestionarului FlowLayout, dar respectând alinierea verticală şi
orizontală impusă de grilă.
Exemplu
În fişierul Grila.java se dă un exemplu de aplicaţie, în care se testează gestionarul de
poziţionare GridLayout. La punerea în execuţie a aplicaţiei, se dau ca parametri în linia de
comandă numărul total de butoane de contorizare, numărul de linii al grilei şi numărul de
coloane. Se poate observa ordinea în care sunt butoanele aşezate pe grilă şi modul în care se
schimbă aspectul ferestrei, dacă se modifică cu mouse-ul dimensiunile acesteia. Se poate
verifica şi ce se întâmplă dacă numărul de coloane dat ca parametru este mai mic sau mai
mare decât cel necesar.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Grila {
static AF af=new AF(); // ascultatorul de fereastra
226
Programarea orientata pe obiecte în limbajul Java
227
Severin Bumbaru
Clasa BoxLayout
Exemplul 1
În fişierul ButoaneBox.java se dă un exemplu de aplicaţie, în care se testează clasa
BoxLayout. În acest caz, gestionarul BoxLayout a fost folosit direct în containerul
contentPane al ferestrei aplicaţiei. La lansarea aplicaţiei în execuţie, se indică numărul de
butoane de contorizare şi direcţia după care acestea vor fi plasate (x sau y). Se poate urmări
efectul modificării cu mouse-ul a dimensiunilor ferestrei. Se observă că dimensiunile
componentelor rămân constante, fără a mai fi influenţate de dimensiunile ferestrei.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ButoaneBox {
static AF af=new AF(); // ascultatorul de fereastra
static Anulare anulare=new Anulare(); // ascultatorul
// butonului de anulare
static IUG iug; // fereastra aplicatiei
static int numarButoane;
static int axa;
228
Programarea orientata pe obiecte în limbajul Java
ButonContor bc[];
JButton br=new JButton("Reset");
229
Severin Bumbaru
Exemplul 2
În fişierul TestBox.java se dă o variantă a aplicaţiei din exemplul precedent, în care se
utilizează drept container pentru butoane o instanţă a clasei Box. În constructorul ferestrei
aplicaţiei, după ce se creeaza această instanţă a clasei Box, se adaugă la ea succesiv toate
componentele, după care este ea însăşi adaugată la conţinutul ferestrei aplicaţiei (la
contentPane). În această situaţie, gestionarul de poziţionare BoxLayout este folosit prin
intermediul instanţei clasei Box, în care sunt plasate componentele.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class TestBox {
static AF af=new AF(); // ascultatorul de fereastra
static Anulare anulare=new Anulare(); // ascultatorul
// butonului de anulare
static IUG iug; // fereastra aplicatiei
static int numarButoane;
static int axa;
230
Programarea orientata pe obiecte în limbajul Java
// contentPane
setVisible(true);
}
}
231
Severin Bumbaru
din clasa java.awt.Component. În acest mod, fiecare componentă este plasată în container cu
colţul stânga-sus în punctul de coordonate (x,y)şi are dimensiunile date prin lăţimea width
şi înălţimea height.
În locul metodei setBounds se pot folosi şi metodele
public void setLocation(Point location)
public void setSize(int latime, int inaltime)
care există, de asemenea, în clasa Component şi permit setarea separată a poziţiei şi
dimensiunilor componentelor.
Poziţia şi dimensiunile astfel stabilite ale componentelor rămân neschimbate, chiar dacă se
modifică dimensiunile containerului care le conţine.
Exemplu:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class PozAbs {
static AF af=new AF(); // ascultatorul de fereastra
static AA aa=new AA(); // ascultatorul de actiuni
static IUG iug=new IUG("Pozitionare absoluta");
232
Programarea orientata pe obiecte în limbajul Java
IUG(String titlu) {
super(titlu);
Container cp=getContentPane();
setSize(420,220);
setLocation(200,150);
addWindowListener(af);
cp.setLayout(null); // se elimina gestionarul de pozitionare din
// contentPane
et1.setBounds(150, 5, 160,10); // pozitia si dimensiunile etichetei
cp.add(et1); // adaugarea etichetei
for(int i=0; i<numarContoare; i++) {
bc[i]=new ButonContor("B"+i); // crearea butoanelor-contor
bc[i].setBounds(10+100*i, 20, 90,70); // stabilirea pozitiei
// si dimensiunilor butoanelor-contor
cp.add(bc[i]); // adaugarea butoanelor-contor la contentPane
}
et2.setBounds(10, 140, 200,10); // locul si dimens. etichetei 2
cp.add(et2); // adaugarea etichetei 2 la contentPane
br.setBounds(210, 120, 70, 60); // pozitia si dimensiunile
// butonului de anulare
br.addActionListener(aa); // ascultarea butonului de anulare
cp.add(br); // adaugarea butonului de anulare la contentPane
setVisible(true);
}
}
Gruparea componentelor
Pentru a poziţiona componentele în structuri mai complicate decât cele oferite de clasele de
gestionare a poziţiei existente, se poate recurge la gruparea mai multor componente în
containere auxiliare, care se plasează apoi în containerul principal. Drept containere auxiliare,
se folosesc cel mai frecvent instanţe ale claselor java.awt.Panel, javax.swing.JPanel şi
javax.swing.Box.
233
Severin Bumbaru
Clasa java.awt.Panel (panou) reprezinta cel mai simplu container, care este o suprafaţă
dreptunghiulară fără bordură. Gestionarul de poziţionare implicit este FlowLayout, dar se
poate pune oricare altul. Panourile sunt frecvent folosite pentru a plasa pe ele diferite
componente, formând un grup care se plaseaza apoi într-un alt container. Clasa
javax.swing.JPanel este varianta de Panel folosită în JFC/Swing, fiind un container genreric.
Exemplu
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Panouri {
static ButonContor bc[]=new ButonContor[8];
static JButton br1=new JButton("Reset 1"),
br2=new JButton("Reset 2");
static Anulare anulare=new Anulare();
static IUG iug=new IUG("Gruparea componentelor pe panouri");
IUG(String titlu) {
super(titlu);
Container cp=getContentPane();
setSize(500,400);
addWindowListener(new AF());
cp.setLayout(new GridLayout(2,2));
/* Crearea butoanelor */
for(int i=0; i<8; i++) bc[i]=new ButonContor("B"+i);
/* Completarea cu butoane a panoului p2 */
p2.add(bc[0], BorderLayout.NORTH);
p2.add(bc[1], BorderLayout.WEST);
p2.add(bc[2], BorderLayout.CENTER);
p2.add(bc[3], BorderLayout.EAST);
p2.add(bc[4], BorderLayout.SOUTH);
/* Completarea panoului p1 */
for(int i=5; i<8; i++) p1.add(bc[i]);
/* Adaugarea de ascultatori la butoanele de anulare */
br1.addActionListener(anulare);
br2.addActionListener(anulare);
/* Adaugarea de panouri si butoane la controlPane */
cp.add(p2);
cp.add(p1);
cp.add(br1);
234
Programarea orientata pe obiecte în limbajul Java
cp.add(br2);
setVisible(true);
}
}
Clasa Box
Clasa javax.swing.Box are ca instanţe containere speciale, care nu pot folosi decât
gestionarul de poziţionare javax.swing.BoxLayout. Orice incercare de a-i pune alt gestionar
produce o eroare de execuţie din clasa AWTError.
Într-un container din clasa Box, componentele pot fi amplasate numai pe o singură direcţie:
orizontală sau verticală.
Clasa dispune de metode statice pentru crearea de "casete" (instanţe ale clasei Box) orizontale
sau verticale:
public static Box createHorizontalBox()
public static Box createVerticalBox()
Se pot creea, de asemenea "componente invizibile" de dimensiuni fixe sau variabile, care pot
fi folosite la distanţarea componentelor vizibile. În acest scop, se folosesc metodele
public static Component createRigidArea(Dimension d) - creeaza o
"componentă ascunsă" de dimensiune fixă;
public static Component createHorizontalStrut(int width) - creeaza o
"componentă ascunsă" de lăţime fixă, dar de înălţime variabilă;
public static Component createVerticalStrut(int height) - creeaza o
"componentă ascunsă" de înalţime fixă, dar de lăţime variabilă;
public static Component createGlue() - creaza o "componentă ascunsă" cu
ambele dimensiuni variabile;
235
Severin Bumbaru
Exemplu
În fişierul Casete.java este dată o aplicaţie în care se folosesc trei casete din clasa Box: două
verticale şi una orizontală.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Casete {
static ButonContor bc[]=new ButonContor[5];
static JButton br1=new JButton("Reset 1"),
br2=new JButton("Reset 2");
static Anulare anulare=new Anulare();
static IUG iug=new IUG("Gruparea componentelor in casete");
IUG(String titlu) {
super(titlu);
setSize(280,160);
addWindowListener(new AF());
/* Crearea butoanelor */
for(int i=0; i<5; i++) bc[i]=new ButonContor("B"+i);
/* Completarea cu butoane a casetei c1 */
c1.add(Box.createVerticalStrut(18)); // deplasare in jos
c1.add(bc[0]);
c1.add(bc[1]);
c1.add(Box.createVerticalStrut(10)); // deplasare in jos
c1.add(br1);
/* Completarea casetei c2 */
for(int i=2; i<5; i++) c2.add(bc[i]);
c2.add(Box.createVerticalStrut(5)); // deplasare in jos
c2.add(br2);
/* Adaugarea de ascultatori la butoanele de anulare */
br1.addActionListener(anulare);
br2.addActionListener(anulare);
/* Adaugarea casetelor c1 si c2 la caseta c3 */
c3.add(Box.createGlue()); // pentru alinierea la centru
c3.add(c1);
c3.add(Box.createHorizontalStrut(10));
c3.add(c2);
/* Adaugarea casetei c3 la contentPane */
getContentPane().add(c3);
setVisible(true);
236
Programarea orientata pe obiecte în limbajul Java
}
}
Remarcăm următoarele:
- butoanele au fost plasate în cele două casete verticale, c1 şi c2;
- întrucât numărul de butoane din cele două casete este diferit, în caseta c1 s-a introdus o
componentă ascunsă pentru alinierea la partea inferioară. De asemenea, în ambele casete
verticale s-au introdus în faţa ultimului buton componente ascunse pentru distanţare;
- cele două casete verticale au fost introduse în cea orizontală;
- componenta ascunsă cu dimensiuni variabile, introdusă în faţa primei casete din c3, a fost
pusă pentru a se obţine alinierea pe centru a casetelor c1 şi c2. În lipsa acesteia, caseta c1 ar fi
fost aliniată la stânga;
- caseta c3 a fost introdusă în contentPane.
Urmăriţi ce se întâmplă când modificaţi cu mouse-ul dimensiunile ferestrei aplicaţiei.
Întrebări
Nivel 1
1. Ce este interfaţa utilizator?
2. Ce este interfaţa utilizator grafică?
3. Ce sunt obiectele grafice?
4. Ce este AWT?
5. Ce este JFC/Swing?
6. Ce rol are clasa Component şi din ce pachet face parte?
7. Ce deosebire este între componentele atomice şi containere?
8. Care este clasa folosită în JFC/Swing pentru fereastra principală a aplicaţiei?
9. Ce conţin la partea superioară instanţele clasei JFrame?
237
Severin Bumbaru
Nivel 2
1. Ce categorii de interfeţe utilizator cunoasteţi?
2. Ce este un limbaj de comandă şi ce astfel de limbaje cunoasteţi?
3. Care sunt în prezent cele mai larg folosite interfeţe utilizator grafice?
4. Prin ce pachete se realizează AWT pe platformele Java?
5. Prin ce pachete se realizează JFC/Swing pe platforma Java 2?
6. In ce constă deosebirea de abordare între AWT şi JFC/Swing?
7. Ce metode ale clasei Component cunoasteţi?
8. Ce metode ale clasei JFrame cunoasteţi?
9. Cum este ghidat procesul de calcul în programarea procedurală tradiţională?
10. Cine are iniţiativa acţiunilor în programarea orientată pe evenimente?
11. Care sunt obiectele care intervin atunci când se generează un eveniment?
12. Ce sunt evenimentele de mouse şi cum sunt ele generate?
13. Ce sunt evenimentele de tastă şi cum sunt ele generate?
14. Care sunt evenimentele generate de o fereastră şi cum sunt ele ascultate?
15. Care sunt evenimentele de mouse?
16. Cum sunt ascultate evenimentele de mouse?
17. Care sunt evenimentele de tasta?
18. Cum sunt ascultate evenimentele de tasta?
19. În ce stare trebuie sa fie fereastra pentru a genera evenimente de tastă?
238
Programarea orientata pe obiecte în limbajul Java
239
Severin Bumbaru
1. Componente de control: butoane, butoane radio, casete de validare, liste, liste ascunse,
meniuri, rigle ajustabile, câmpuri de text.
240
Programarea orientata pe obiecte în limbajul Java
with JFC/Swing din Java Tutorial. Noi nu vom face aici, din lipsă de timp, decât o scurta
prezentare a unor componente mai larg folosite şi a modului lor de utilizare.
Majoritatea claselor de componente din JFC/Swing sunt derivate din clasa JComponent,
moştenind caracteristicile acestei clase.
Clasa JComponent
Clasa abstracta javax.swing.JComponent extinde clasa java.awt.Container. In consecinţă,
orice componenta Swing are şi caracteristici de container.
Aspectul este dat de figura prin care este reprezentată pe ecran componenta respectivă.
Fiecare clasă de componente oferă un aspect tipic al instanţelor sale. În JFC/Swing
programatorului i se pun însa la dispoziţie metode şi clase prin care poate modifica aspectul
componentelor.
Starea componentei este dată, ca la orice obiect, de valorile câmpurilor sale. Starea se poate
modifica fie datorită acţiunilor utilizatorului, fie datorită invocării prin program a unor
metode ale acesteia. În general, la modificarea stării componenta generează un eveniment, iar
uneori îşi schimbă şi aspectul.
241
Severin Bumbaru
Exemplu
În fişierul TestComp.java este dat un exemplu de testare a posibilităţii de a stabili aspectul
242
Programarea orientata pe obiecte în limbajul Java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
class TestComp {
static AF af=new AF();
static IUG iug=new IUG("Butoane cu diferite aspecte");
243
Severin Bumbaru
}
}
Remarcăm următoarele:
- pentru a lucra cu borduri, a fost necesar să se importe pachetul javax.swing.border;
- s-au folosit bordurile standard din clasa BorderFactory;
- butonului al doilea (b2, cel cu bordura roşie groasă) i s-a ataşat un text explicativ volant;
dacă punem cursorul de mouse deasupra acestui buton fără să apăsăm şi aşteptăm câteva
secunde, apare textul volant;
- în penultimul buton (b6) s-a introdus imaginea care se găseşte în fişierul
buton_go.gif. Dacă doriţi sa testati programul, această imagine trebuie să existe în acelaşi
director cu clasa TestComp (deci în directorul curent al dvs). Pentru a realiza acest lucru,
puneţi cursorul mouse-ului pe această imagine, apasaţi butonul din dreapta şi va apare un
meniu din care selectaţi opţiunea "Save image as ..." sau "Save picture as ...", apoi salvaţi
imaginea în acelaşi director cu fişierul TestComp.java.
Componente de control
Componentele de control sunt dispozitive de intrare virtuale, uşor de utilizat, prin intermediul
cărora utilizatorul poate introduce anumite comenzi. Din această categorie fac parte
butoanele, riglele ajustabile, listele, listele ascunse, meniurile şi câmpurile de text.
Butoane
Butoanele sunt obiecte grafice, al căror aspect este similar celor al butoanelor folosite în
aparatura electronică şi electrică. Acţionarea butonului se face punând deasupra lui cursorul
mouse-ului şi apasând un buton al acestuia. În JFC/Swing există mai multe tipuri de butoane:
butonul simplu, caseta de validare, butonul radio şi articolul de meniu. Toate acestea sunt
subclase ale clasei abstracte javax.swing.AbstractButton.
Clasa AbstractButton
Clasa javax.swing.AbstractButton defineşte caracteristicile comune ale diferitelor tipuri de
butoane din JFC/Swing.
244
Programarea orientata pe obiecte în limbajul Java
- orice buton poate avea un text, care poate fi pus prin metoda void setText(String
text) şi poate fi aflat prin metoda String getText();
- orice buton poate avea cel puţin o pictogramă, care poate fi pusă prin metoda void
setIcon(Icon defaultIcon) şi obţinută prin metoda Icon getIcon();
- orice buton poate avea o mnemonică, adică i se poate asocia o tastă, a cărei apasare are
acelaşi efect cu apăsarea cu mouse-ul a butonului respectiv. Mnemonica se pune cu metoda
void setMnemonic(int mnemonic), al cărei argument este codul tastei care trebuie
acţionată;
- butonul generează trei tipuri de evenimente:
. ActionEvent, când se acţioneaza asupra lui;
. ChangeEvent, când îşi modifică starea;
. ItemEvent, când butonul este selectat sau deselectat.
Există metode de adăugare a ascultătoarelor pentru aceste trei tipuri de evenimente:
void addActionListener(ActionListener a),
void addChangeListener(ChangeListener c),
void addItemListener(ItemListener i);
- butonul are un nume al acţiunii de comandă pe care o exercită, sub forma unui şir de
caractere; implicit, acest nume este identic cu textul butonului, dar el poate fi modificat prin
metoda
void setActionCommand(String command) şi poate fi aflat prin metoda
String getActionCommand();
245
Severin Bumbaru
Caseta de validare
Caseta de validare este un obiect de control cu două stări stabile. La fiecare click de mouse
pe suprafata casetei de validare, aceasta trece dintr-o stare în cealaltă. În mod obişnuit, caseta
de validare este reprezentată grafic printr-un patrat, în care apare un simbol de bifare (de
validare), cum ar fi simbolul V sau X, când caseta se găseşte în starea "selectat". În starea
opusă (deselectat), caseta este vidă. Imaginea casetei este insoţită şi de un text explicativ.
Butonul radio
Butonul radio este un buton cu două stări stabile, care face parte dintr-un grup de butoane,
astfel că la un moment dat numai unul dintre ele poate fi "selectat". În momentul în care este
selectat ("apăsat") un buton al grupului, se deselectează automat cel care era selectat anterior.
Exemplu
În fişierul TestButoane.java se dă un exemplu de aplicaţie, în care se testează butoanul
simplu, caseta de validare şi butonul radio.
import java.awt.*;
import java.awt.event.*;
246
Programarea orientata pe obiecte în limbajul Java
import javax.swing.*;
class TestButoane {
static AF af=new AF();
static Ascultator ascult=new Ascultator();
static IUG iug=new IUG("Diferite tipuri de butoane");
247
Severin Bumbaru
box3.add(rb1);
box3.add(rb2);
box3.add(rb3);
grup=new ButtonGroup();
grup.add(rb1);
grup.add(rb2);
grup.add(rb3);
/* box1, box2 si box3 se pun in box4 , iar aceasta
se pune in controlPane
*/
box4=Box.createHorizontalBox();
box4.add(Box.createHorizontalGlue());
box4.add(box1);
box4.add(Box.createHorizontalStrut(15));
box4.add(box2);
box4.add(Box.createHorizontalStrut(15));
box4.add(box3);
box4.add(Box.createHorizontalGlue());
cp.add(box4, BorderLayout.CENTER);
label=new JLabel("Actionati butoanele si urmariti mesajul");
cp.add(label, BorderLayout.SOUTH);
setVisible(true);
}
}
248
Programarea orientata pe obiecte în limbajul Java
Detectarea butonului care a fost apăsat, fie că este vorba de un buton simplu sau unul radio,
se face în metoda actionPerformed(ActionEvent action) folosind metoda String
getActionCommand() din clasa ActionEvent. Pentru a constata care caseta de validare care a
fost selectată sau deselectată, în metoda itemStateChanged(ItemEvent item) se
procedează astfel: se determină mai întâi obiectul selectabil (în cazul nostru caseta de
validare) care a generat evenimentul, folosind metoda Object getItem() din clasa
ItemEvent, după care se aplică metoda boolean isSelected() a acestui obiect.
Fiecărui buton (sau casetă de validare) i-a fost ataşată o mnemonică. Pentru comoditatea
utilizatorului, tasta corespunzătoare a fost menţionată între paranteze, în textul fiecărui buton.
Dacă se dă comanda Alt-<Tastă mnemonică> se obţine acelaşi efect ca atunci când se face
click de mouse pe butonul respectiv. De exemplu, în loc de a apăsa cu mouse-ul butonul B2,
se apasă simultan tastele Alt şi F2. Simbolurile tastelor se găsesc în clasa
java.awt.event.KeyEvent.
Pentru aprofundarea folosirii diferitelor tipuri de butoane, recomandăm capitolul How to use
Buttons, Check Boxes and Radio Buttons din Java Tutorial.
Liste
În cazul interfeţelor utilizator grafice, se numeşte listă o componentă care conţine articole
selectabile. Selectarea lor se face de către utilizator, prin click de mouse pe articolul
respectiv. Lista poate fi cu selecţie simplă sau cu selecţie multiplă, după cum pot fi selectate
simultan unul sau mai multe articole.
Daca lista este mai lunga decât spaţiul de afişare disponibil, ea poate fi pusă pe un panou
glisant, din clasa javax.swing.JScrollPane.
Selectarea simplă a unui articol se face prin click de mouse pe articolul respectiv.
249
Severin Bumbaru
Shift şi se face click pe ultimul articol din această zonă. În acest fel se selectează toate
articolele din zona respectivă;
- se face click de mouse pe primul articol din zonă, apoi se apasă tasta Shift şi se acţionează
tastele cu săgeţi sus/jos până când sunt selectate toate articolele din zona respectivă.
De câte ori se face o selecţie de articole din listă, se generează un eveniment din clasa
javax.swing.event.ListSelectionEvent, care este ascultat cu ajutorul unei clase care
implementează interfaţa javax.swing.event.ListSelectionListener.
Punerea articolelor în listă poate fi făcută astfel:
- la crearea listei, folosind unul din constructorii
public JList(Object[] listData) - primeşte ca argument un tablou de obiecte, care
sunt articolele din listă
public JList(Vector listData)- primeşte ca argument o instanţă a clasei
java.util.Vector, care are ca elemente articolele listei;
- în timpul utilizării listei, invocându-se una din metodele
public void setListData(Object[] listData)
public void setListData(Vector listData)
Clasa are numeroase alte metode. Pentru aprofundare, recomandăm capitolul How to Use
Lists din Java Tutorial.
Liste ascunse
Lista ascunsă este o listă din care se vede un singur articol (cel care este selectat), deci ocupa
pe ecran spaţiu mult mai putin decât una obişnuită. Celelalte articole ale listei sunt "ascunse"
şi devin vizibile numai dacă se face click pe articolul vizibil. Dacă acum facem click de
mouse pe un alt articol din lista devenită vizibilă, acest nou articol este selectat (rămâne
vizibil), iar celelalte dispar. Într-o listă ascunsă nu este posibilă selecţia multiplă.
Când este selectat un nou articol din lista ascunsă, aceasta generează un eveniment de articol
din clasa java.awt.event.ItemEvent, care este ascultat cu un java.awt.event.ItemListener.
La fiecare acţionare asupra listei ascunse, se generează, de asemenea, un
java.awt.ActionEvent.
În mod implicit, instanţele clasei JComboBox nu sunt editabile. Totuşi, este posibil ca acestea
sa fie făcute editabile, în care caz ele se comportă ca o combinaţie între listă şi câmpul de
text: utilizatorul poate să introducă textul manual, sau poate să aleagă unul din listă.
Punerea articolelor în listă se poate face astfel:
- la crearea listei ascunse, folosind unul din constructorii
250
Programarea orientata pe obiecte în limbajul Java
Pentru aprofundarea folosirii listelor ascunse recomandăm capitolul How to Use Combo
Boxes din Java Tutorial.
Exemplu
În fişierul Liste.java este dat un exemplu de aplicaţie în care se folosesc instanţe ale claselor
JList şi JComboBox.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
class Liste {
static AF af=new AF();
static AAL ascult=new AAL();
static AEA ascult1=new AEA();
static IUG iug=new IUG("Diferite tipuri de liste");
251
Severin Bumbaru
setSize(350, 240);
setLocation(200, 50);
Container cp=getContentPane();
addWindowListener(af); // adaugarea ascultatorului de fereastra
lista2.addListSelectionListener(ascult);
JScrollPane scroll=new JScrollPane(lista2); // panoul glisant
// care contine lista nou creata, lista2
cp.add(scroll, BorderLayout.CENTER);
252
Programarea orientata pe obiecte în limbajul Java
Câmpul de text
Câmpul de text este principalul instrument al interfeţei grafice prin care utilizatorul introduce
date de la tastatură. El se prezintă sub forma unei ferestre dreptunghiulare editabile, în care se
poate introduce o singură linie de text. De obicei, prin intermediul câmpului de text se
introduce o singură valoare numerică sau un şir de caractere.
253
Severin Bumbaru
convertită din forma externă (de şir de caractere) în forma internă. În acest scop, se folosesc
metodele de analiză şi conversie din clasele acoperitoare. De exemplu, pentru un numar de tip
double se va folosi metoda
public double parseDouble(String str)
din clasa acoperitoare java.lang.Double, unde str este şirul care trebuie analizat. Întrucât
aceste metode pot să genereze excepţii de format incorect (NumberFormatException), ele
trebuie utilizate în secvenţe try .. catch;
Pentru aprofundarea utilizării câmpurilor de text, recomandăm capitolul How to Use Text
Fields din Java Tutorial.
Exemplu
În fişierul CâmpuriText.java este dat un exemplu de aplicaţie în care se utilizează trei
câmpuri de text, pentru a introduce, respectiv, un şir de caractere, un număr întreg şi un
număr în virgulă mobilă. În clasa imbricată Actiuni, prin care este realizat ascultătorul de
evenimente de acţiune, se poate observa cum se determină care câmp de text a generat
evenimentul de acţiune şi cum se preia şi analizează textul din câmpul respectiv, în funcţie de
tipul valorii pe care acest câmp trebuie să o conţină.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class CampuriText {
static AF af=new AF();
static Actiuni act=new Actiuni();
static IUG iug=new IUG("Campuri de text");
254
Programarea orientata pe obiecte în limbajul Java
tf1=new JTextField();
tf1.addActionListener(act);
panel.add(tf1);
panel.add(new JLabel("Un numar intreg"));
tf2=new JTextField();
tf2.addActionListener(act);
panel.add(tf2);
panel.add(new JLabel("Un numar real: "));
tf3=new JTextField();
tf3.addActionListener(act);
panel.add(tf3);
/* Adaugarea la contentPane a panoului cu campuri de text */
cp.add(panel, BorderLayout.CENTER);
setVisible(true);
}
}
255
Severin Bumbaru
Rigla cu cursor
Clasa javax.swing.JSlider oferă o componentă care are aspectul unei rigle prevăzute cu
cursor. Prin deplasarea cursorului cu ajutorul mouse-ului se modifică valoarea indicată de
riglă. Valoarea minimă şi cea maximă de pe scala riglei şi valoarea unei diviziuni se indică
prin program. Rolul riglei poate fi asemănat cu cel al unui dispozitiv de ajustare continuă,
cum este potenţiometrul din aparatura electronică.
Crearea riglei se face folosind unul dintre constructorii ei, dintre care cel mai complet este
public JSlider(int orientation, int min, int max, int value)
în care
orientation - orientarea riglei, care poate fi JSlider.HORIZONTAL sau
JSlider.VERTICAL;
min, max - valorile minimă şi maximă de pe scala riglei;
value - valoarea indicată iniţial de cursorul riglei (trebuie să fie în intervalul [min,max]).
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
256
Programarea orientata pe obiecte în limbajul Java
import javax.swing.event.*;
class TestRigla {
static AF af=new AF();
static AscultRigla ascult=new AscultRigla();
static IUG iug=new IUG("O rigla ajustabila");
cp.add(rigla, BorderLayout.CENTER);
setVisible(true);
}
}
257
Severin Bumbaru
Meniuri
La fel ca listele sau butoanele radio, meniurile permit utilizatorului să aleagă una din mai
multe opţiuni posibile. În JFC/Swing se disting două categorii de meniuri:
- meniuri cu bară, care pornesc dintr-o bară situată la partea superioară a ferestrei aplicaţiei;
- meniuri derulante (pop-up), care apar în poziţia în care se găseşte cursorul de mouse.
Meniuri cu bară
Bara de meniu se poate plasa, daca este necesara, numai la partea superioara a ferstrei
aplicatiei si se realizeaza ca o instanta a clasei javax.swing.JMenuBar.
Bara de meniu poate sa conţină unul sau mai multe meniuri, care se realizează ca instanţe ale
clasei javax.swing.JMenu. La rândul său, fiecare meniu poate conţine unul sau mai multe
articole, care sunt instanţe ale claselor javax.swing.JMenuItem sau
javax.swing.JCheckBoxMenuItem. Este însă posibil ca un articol de meniu să fie el însuşi
un meniu (din clasa JMenu). Dacă utilizatorul alege cu mouse-ul un aricol de meniu, sunt
posibile deci două situaţii:
- acesta este un articol propriu-zis (un JMenuItem), în care caz opţiunea respectivă este
selectată;
- articolul ales este el însuşi un meniu (un JMenu), în care caz noul (sub)meniu se
desfăşoară şi căutarea continuă.
Atunci când este selectat un articol de meniu simplu (din clasa JMenuItem), acesta acţionează
ca un buton obişnuit, adică generează un eveniment de acţiune (ActionEvent). Dacă se
selectează un articol de meniu sub forma de casetă de validare (din clasa
JCheckBoxMenuItem), acesta se comporta ca o caseta de validare, adică generează un
258
Programarea orientata pe obiecte în limbajul Java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Meniuri {
static AF af=new AF();
static AA aa=new AA();
static IUG iug=new IUG("O fereastra cu bara de menu");
259
Severin Bumbaru
setVisible(true);
}
}
Spre deosebire de meniurile cu bară, meniurile volante sunt tratate ca nişte componente
obişnuite, care pot să fie afişate prin program, atunci când este necesar. În JFC/Swing,
meniurile volante se realizează ca instanţe ale clasei javax.swing.JPopupMenu. În rest,
meniul pop-up se comportă la fel ca un meniu cu bară.
Afişarea meniului pop-up trebuie programată, folosindu-se în acest scop metoda din clasa
JPopupMenu
public void show(Component invoker, int x, int y)
în care invoker este referinţa la componenta in spaţiul căreia trebuie să apară meniul, iar x şi
y sunt coordonatele punctului în care acesta va fi afişat.
260
Programarea orientata pe obiecte în limbajul Java
Exemplu
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Popup {
static AF af=new AF();
static AA aa=new AA();
static AfisPopup afisare=new AfisPopup();
static IUG iug=new IUG("Exemplu de menu pop-up");
261
Severin Bumbaru
*/
label.addMouseListener(afisare);
setVisible(true);
}
}
Componente de afişare
Componentele de afişare sunt cele care servesc pentru a afişa anumite texte sau imagini şi pot
fi needitabile sau editabile.
- javax.swing.JLabel - pentru realizarea unor "etichete" în care pot fi afişate texte sau
imagini, fără a putea fi editate de utilizator;
- javax.swing.JTooltip - pentru afişarea de "inscripţii volante", când se pune cursorul de
mouse deasupra unei alte componente;
- javax.swing.JProgressBar - pentru realizarea unor "bare de progres" adică a unor bare de
lungime variabilă, care arată cum evoluează realizarea unei anumite activităţi (de la 0% la
262
Programarea orientata pe obiecte în limbajul Java
100%).
Vom prezenta aici numai clasa JTextArea, iar pentru celelalte recomandăm folosirea
documentaţiei indicate.
Pentru aprofundare recomandăm folosirea capitolelor Using Text Components, How to Use
Tables, How to Use File Choosers, How to Use Trees, si How to Use Color Choosers din
Java Tutorial.
Clasa JtextArea
Instanţele clasei javax.swing.JTextArea (arie de text) sunt suprafeţe de afişare a unor texte
editabile cu mai multe linii. Componenta are comportamentul unui editor de text simplu:
permite să se introducă text de la tastatură, să se şteargă textul în întregime sau pe porţiuni, să
se adauge sau să se însereze text. Prin metode speciale se poate seta componenta astfel, încât
să se realizeze automat trecerea de la un rând la altul. Se oferă metode prin care se poate
adăuga text prin program, sau se poate obţine sub forma de String textul existent.
263
Severin Bumbaru
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ZonaText {
static AF af=new AF();
// static AA aa=new AA();
//static AfisPopup afisare=new AfisPopup();
static IUG iug=new IUG("O zona de text");
setVisible(true);
}
}
264
Programarea orientata pe obiecte în limbajul Java
Întrebări
Nivel 1
265
Severin Bumbaru
Nivel 2
266
Programarea orientata pe obiecte în limbajul Java
Pentru afişarea pe ecran a ferestrei de dialog se invocă metoda public void show(), iar
pentru ascunderea ei se invocă metoda public void hide(). În fine, pentru a elimina
fereastra de dialog, eliberând toate resursele pe care le foloseşte, se invocă metoda public
void dispose(). Aceste trei metode sunt moştenite de JDialog de la superclasa
java.awt.Dialog. Când fereastra de dialog este deschisă, ea trece în prim plan sau în plan
secund împreună cu proprietarul ei.
Ferestrele de dialog pot fi modale sau nemodale. Când este afişată pe ecran o fereastră
modală, toate celelalte ferestre existente pe ecran sunt blocate (nu se poate acţiona asupra
lor), până când se încheie lucrul cu fereastra de dialog modală respectivă.
267
Severin Bumbaru
Clasele Dialog şi JDialog sunt prezentate ceva mai detaliat în indexul de clase.
Clasa JDialog este folosită pentru realizarea unor ferestre de dialog configurate de
programator după dorinţa sa. În multe situaţii, este însă suficient să se folosească ferestre de
dialog standard, preconfigurate, ceeace uşurează programarea. În acest scop, în JFC/Swing a
fost creată clasa javax.swing.JOptionPane care este prezentată în indexul de clase.
Clasa JOptionPane oferă metode statice pentru a creea patru tipuri de ferestre de dialog:
- pentru dialog de mesaj (showMessageDialog) - fereastra conţine o informaţie şi un buton
OK care se apasă pentru a închide fereastra;
- pentru dialog de confirmare (showConfirmDialog) - în fereastra se afişează un mesaj (o
întrebare) şi trei butoane de confirmare: Yes/No/Cancel; la apăsarea unuia dintre ele, metoda
întoarce valoarea corespunzatoare şi fereastra de dialog dispare;
- pentru dialog de intrare (showInputDialog) - fereastra conţine un mesaj, un câmp de text
şi două butoane: OK şi Cancel. Utilizatorul introduce în câmpul de text o valoare (un şir de
caractere sau un număr) şi apasa tasta <Enter> sau actioneaza cu mouse-ul butonul OK. În
ambele cazuri fereastra de dialog dispare, iar metoda întoarce şirul introdus în câmpul de text.
Dacă se apasă butonul Cancel, fereastra de dialog dispare, iar metoda întoarce null.
- pentru dialog de opţiune (showOptionDialog), care întruneşte caracteristicile celor trei
tipuri de mai sus.
Pentru fiecare din aceste patru tipuri de ferestre de dialog există mai multe metode cu
numele corespunzător, care diferă între ele prin lista de argumente. Pentru detalii trimitem la
documentaţia Java API.
Exemplu
În fişierul Dialog1.java este dat un exemplu de aplicaţie, în care se testează diferitele tipuri de
ferestre de dialog create cu metodele statice ale clasei javax.swing.JOptionPane. Fereastra
principală a aplicaţiei conţine un buton cu inscripţia "Introducere text" şi o arie de text
(JTextArea). Când este acţionat butonul, apare o fereastră de dialog de intrare, cu mesajul
"Introduceţi un text". Dacă se acţioneaza butonul OK fără să fi introdus nimic în câmpul de
text, apare o noua fereastră de dialog, care conţine mesajul de avertisment "Nu se permite un
şir vid". Dacă se introduce un şir în câmpul de text şi se acţioneaza butonul OK sau tasta
Enter, apare o nouă fereastră de dialog, care cere confirmarea că acest text trebuie introdus în
aria de text. Dacă se răspunde prin acţionarea butonului Yes, se face adăugarea, iar dacă se
răspunde prin apăsarea butonului No sau Cancel, fereastra de dialog dispare fără să se facă
adăugarea textului.
Întrucât întregul dialog este declanşat, în cazul de faţă, prin acţionarea butonului "Introducere
text", programul prin care se realizează dialogul a fost plasat în metoda actionPerformed a
clasei IntroducereText care ascultă evenimentele de acţiune generate de butonul menţionat.
268
Programarea orientata pe obiecte în limbajul Java
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
//import javax.swing.event.*;
class Dialog1 {
static AF af=new AF();
static IntroducereText it=new IntroducereText();
static IUG iug=new IUG("Utilizarea unei ferestre de dialog");
269
Severin Bumbaru
terminat=true;
}
}
else terminat=true;
}
}
}
Alegerea culorii
În multe aplicaţii, la proiectarea interfeţei utilizator grafice este necesar să se impună culorile
de fond şi de primplan ale diferitelor componente, folosind metodele clasei Component
public void setBackground(Color color) - pentru culoarea de fond;
public void setForeground(Color color) - pentru culoarea de prim-plan (a
textului).
De exemplu, culoarea albastru este reprezentată prin câmpul static Color.blue, iar culoarea
galben prin Color.yellow. În consecinţă, dacă dorim să punem componentei comp culoarea
270
Programarea orientata pe obiecte în limbajul Java
Exemplu
În fişierul Culori.java se dă un exemplu de aplicaţie, în care se demonstrează utilizarea
culorilor predefinite din clasa java.awt.Color.
Fereastra aplicaţiei conţine o listă cu denumirile culorilor predefinite din clasa Color şi un
panou pe care se afişează culorile respective, prin setarea corespunzătoare a culorii de fond a
panoului. Când se selectează o culoare din listă, se observa imediat modificarea
corespunzătoare a culorii panoului. În acest scop, în program au fost folosite două tablouri:
numeCulori - este un tablou de şiruri (String[]) care conţin numele culorilor;
tablouCulori - este un tablou de instanţe ale clasei Color, care conţine toate culorile
predefinite din aceasta clasă.
Lista de culori listaCulori din clasa JList este construită pe baza tabloului numeCulori şi
deci conţine ca articole numele culorilor (denumirile puteau fi date în limba română, dar am
preferat ca cititorii să se familiarizeze cu cele existente în clasa Color).
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
class Culori {
static AF af=new AF();
static IUG iug=new IUG("Alegerea culorii din lista");
271
Severin Bumbaru
JList listaCulori;
setVisible(true);
}
Clasa Color
Clasa Color încapsulează informaţiile despre culoare. În Java AWT se foloseşte modelul de
culori RGB (engleză: red, green, blue) în care fiecare culoare este considerată ca fiind
compusă din trei culori fundamentale: roşu, verde şi albastru. În consecinţă, culoarea este
dată prin trei numere cuprinse în intervalul [0, 255], reprezentând ponderile celor trei culori
fundamentale. În figura de mai jos este reprezentată schema de combinare a acestor trei culori
fundamentale.
272
Programarea orientata pe obiecte în limbajul Java
Prin modificarea ponderilor celor trei culori se pot obţine, în principiu, toate culorile posibile,
de la negru [r=0,g=0,b=0] la alb [r=255,g=255,b=255]. Dacă ponderile celor trei culori sunt
egale, se obţin diferite nuanţe de gri. Culoarea galbenă are ponderile [r=255,g=255,b=0],
culoarea cyan (azuriu) are ponderile [r=0,g=255,b=255] iar culoarea magenta are ponderile
[r=255,g=0,b=255]. Alte culori: orange: [r=255,g=200,b=0], pink (roz):
[r=255,g=175,b=175].
Clasa Color conţine şi un al patrulea câmp, numit alpha, care poate avea, de asemenea, valori
întregi în intervalul [0..255] şi exprima opacitatea culorii respective. Daca alpha=255,
culoarea este complet opaca. Aceasta înseamnă că, dacă o figură cu această culoare se
suprapune peste alta, o maschează complet. Dacă alpha=0, culoarea este complet
transparentă, deci practic invizibilă. Între aceste valori extreme putem avea diferite opacităţi
intermediare, cuprinse intre 0 si 255.
Culoarea în sistemul RGB poate fi exprimată nu numai printr-o instanţă a clasei Color, ci şi
printr-un număr întreg (de tip int), în care cei patru octeţi au, respectiv, valorile
componentelor alpha, r, g, b exprimate în intervalul [0..255]. Componentele sunt plasate
astfel: alpha în biţii 24..31, r în biţii 16..23, g în biţii 8..15 şi b în biţii 0..7.
Clasa Color conţine, de asemenea, constructori şi metode care suportă sistemele de culori
sRBG şi HSB. Standardul sRGB defineşte o variantă standardizată a sistemului RGB,
destinată monitoarelor cu tub catodic. În acest sistem, parametrii r, g, b şi alpha se exprimă
prin numere reale în intervalul [0.0...1.0].
Sistemul de culori HSB (engleză: Hue, Saturation and Brightness) ofera o descriere a
culorilor independentă de sistemul de afişare, folosind următorii trei parametri:
Nuanţa (engleză: Hue) care se exprimă în grade, în intervalul [0..360], unde 0 este rosu, 60
este galben, 120 este verde etc.
Saturaţie (engleză: Saturation) este puritatea culorii, exprimată în procente, în intervalul
[0..100]. La valoarea 0, nuanţa (Hue) nu are semnificatie, iar la valoarea 100 culoarea este
pură.
Strălucire (engleză: Brightness) este exprimată de asemenea în procente, în intervalul
[0..100]. Valoarea 0 înseamnă negru (iluminare 0%), deci parametrii Hue şi Saturation nu au
273
Severin Bumbaru
sens. Valoarea 100 reprezintă strălucirea maximă. Diagrama de mai jos dă o reprezentare a
culorilor exprimate în sistemul HSB.
În clasa Color, parametrii Hue, Saturation şi Brightness se exprimă prin numere reale (tipul
float) în intervalul [0.0 .. 1.0].
Dacă valorile sunt în afara acestui interval, se ia în consideraţie numai partea fracţionara: de
exemplu valoarea 2.73 se consideră echivalentă cu 0.73. În aceste condiţii, dacă se consideră
saturaţia şi strălucirea egale cu 1.0 (100%), valorile parametrului Hue (nuanţă) pentru
principalele culori sunt:
red: h=0.0;
yellow: h=0.6666 (600);
green: h=0.3333 (1200);
cyan: h=0.5 (1800);
blue: h=0.6666 (2400);
magenta: h=0.8333 (3000)
Culoarea neagra (black) se obtine punând strălucirea Brightness=0, iar culoarea albă
(white) se obţine punand saturaţia Saturation=0 şi strălucirea Brightness=1. Dacă se menţine
saturaţia la valoarea 0 şi se dau strălucirii diferite valori între 0 şi 1 se obţin nuanţele de gri.
Variabile:
Clasa Color conţine variabile statice finale (constante) care sunt instanţieri ale clasei pentru
diferite culori tipice:
black, blue, cyan, darkGray, gray, green, lightGray, magenta, orange,
pink, red, white yellow.
Acestea sunt, respectiv, culorile negru, albastru, azuriu, gri inchis, gri, verde, gri deschis,
mov, roz, rosu, alb şi galben. De exemplu, culoarea verde va fi reprezentata prin
Color.green. Toate aceste culori sunt opace, deci au componenta alpha cu valoarea 255.
274
Programarea orientata pe obiecte în limbajul Java
Constructori:
Clasa Color are mai mulţi constructori, dintre care menţionăm aici pe cei mai frecvent
folosiţi:
public Color(int r, int g, int b) - în care argumentele r,g,b sunt numere
întregi în intervalul [0..255], reprezentând ponderile culorilor red, green, blue, iar
componenta alpha are implicit valoarea 255;
public Color(int r, int g, int b, int alpha) - similar cu cel precedent,
indicându-se în plus gradul de opacitate alpha, de asemenea în intervalul [0..255];
public Color(int rgb) - creează o culoare opacă cu parametrii r,g,b daţi ultimii trei
octeţi ai argumentului rgb;
public Color(int rgba, boolean hasalpha) - creează o culoare pornind de la
parametrii alpha,r,g,b împacetaţi în cei patru octeţi ai argumentului rgba. Daca al doilea
argument este true, parametrul alpha (bitii 24..31 ai argumentului) este luat în consideraţie;
public Color(float r, float g, float b) - creează o culoare opacă în sistemul
sRGB, în care r,g,b sunt ponderile culorilor fundamentale în intervalul [0.0...1.0], iar alpha
are valoarea implicită 1.0;
public Color(float r, float g, float b, float alpha) - creează o culoare în
sistemul sRGB, fiind daţi toti cei patru parametri.
Metode:
Menţionăm aici metodele cel mai frecvent utilizate.
O prezentare mai completă a câmpurilor, constructorilor şi metodelor clasei Color este dată în
Indexul de clase.
Exemplul 1:
275
Severin Bumbaru
Exemplul 2:
În fişierul TestHSB.java este dat un exemplu de aplicaţie în care se testează alegerea culorilor
în sistemul HSB. Fereastra se aseamană cu cea din exemplul precedent, dar cele trei rigle
servesc pentru ajustarea componentelor HSB: nuanţa, saturaţie şi strălucire. Valorile
corespunzătoare sunt indicate în câmpurile de text alăturate, ca numere reale în intervalul
[0.0, 1.0]. La partea inferioară a ferestrei sunt afişate componentele culorii în sistemul RGB.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
class TestHSB {
static AF af=new AF();
static IUG iug=new IUG("Alegerea culorii in sistemul HSB"+
" (Hue, Saturation, Brightness)");
276
Programarea orientata pe obiecte în limbajul Java
AjustCuloare(String culoare) {
super(BoxLayout.X_AXIS);
add(new JLabel(culoare));
add(Box.createHorizontalGlue());
ajustare=new JSlider(JSlider.HORIZONTAL, 0, 100,100);
ajustare.setMajorTickSpacing(20);
ajustare.setMinorTickSpacing(10);
ajustare.setPaintTicks(true);
ajustare.addChangeListener(this);
add(ajustare);
add(Box.createHorizontalStrut(5));
valoare=new JTextField("1.00",4);
valoare.setHorizontalAlignment(JTextField.RIGHT);
valoare.setEditable(false);
valoare.setBackground(Color.white);
valoare.setMaximumSize(valoare.getMinimumSize());
add(valoare);
add(Box.createHorizontalStrut(5));
}
277
Severin Bumbaru
}
}
Exemplu
În fişierul SelectCulori.java se dă un exemplu de aplicaţie în care, pentru alegerea culorii unui
panou, se foloseşte o fereastră de dialog, în care se găseşte un JColorChooser.
import java.awt.*;
import java.awt.event.*;
278
Programarea orientata pe obiecte în limbajul Java
import javax.swing.*;
class SelectCulori {
static AF af=new AF();
static AscultDialog ascultDialog=new AscultDialog();
static AscultButon ascultButon=new AscultButon();
static IUG iug=new IUG("Alegerea culorii folosind JColorChooser");
279
Severin Bumbaru
Elemente de grafică
Java API ofera programatorilor posibilităţi ample privind realizarea de desene, text stilizat şi
alte construcţii grafice. Ne vom rezuma aici numai la câteva noţiuni elementare, utile în
realizarea unor aplicaţii grafice simple.
Pentru aprofundare recomandăm lectia Working with Graphics din Java Tutorial. Studiul
poate fi apoi continuat cu secţiunea 2D Graphics a aceluiaşi tutorial.
In principiu, desenarea se poate face pe orice componentă grafică. Există însă clasa
java.awt.Canvas, ale cărei instanţe sunt simple panouri destinate desenării (în engleză
Canvas este pânza pictorului). Pentru desenare se mai folosesc frecvent şi clasele JPanel şi
JLabel.
Sistemul de coordonate folosit pentru grafică are originea în colţul din stânga sus al
componentei, axa Ox este orientată catre dreapta, iar axa Oy este orientată în jos.
Coordonatele se exprimă în numere întregi (int), având ca unitate de măsură pixelul (punctul
de pe ecran).
Contextul grafic
Modul de realizare a imaginilor şi desenelor este strâns dependent atât de echipament
(hardware) cât şi de sistemul de operare. Pentru a se asigura independenţa de platformă, în
Java API a fost introdusă clasa abstractă java.awt.Graphics. Aceasta este clasa de bază a
tuturor contextelor grafice, care permit trasarea de desene pe suprafaţa componentelor
grafice realizate pe diverse dispozitive fizice. Pe fiecare platformă, în mediul de execuţie
Java, trebuie să existe o implementare a contextului grafic, adică o extindere a clasei
Graphics, care conţine toate câmpurile şi metodele acestei clase, dar este specifică platformei
respective.
Un obiect din clasa Graphics încapsulează informaţia de stare a contextului grafic la care se
referă şi anume:
- referinţa la obiectul din clasa Component (sau dintr-o subclasa a acesteia) pe care se
desenează;
- o translaţie a originii sistemului de coordonate; toate coordonatele din desen sunt
raportate la această origine;
- decupajul curent (dreptunghiul în interiorul căruia se trasează desenul);
- culoarea curentă;
- fontul curent;
- operaţia logică pe pixeli curentă (XOR sau paint);
- alternarea curentă de culori pentru operaţia pe pixeli XOR.
280
Programarea orientata pe obiecte în limbajul Java
Originea sistemului de axe (punctul de coordonate 0,0) se găseşte în colţul din stânga-sus al
dreptunghiului de desenare. Axa 0x este îndreptată spre dreapta, iar axa 0y - in jos.
Practic, clasa abstractă Graphics conţine acele metode, care trebuie să existe în orice context
grafic. Conţinutul concret al acestor metode, deci modul efectiv în care se realizează funcţiile
respective, depinde de contextul grafic real, deci de dispozitivul fizic pe care se face
desenarea şi de sistemul de operare folosit. Pe programatorul de aplicaţii sau miniaplicaţii în
Java nu îl interesează însă acest lucru, deoarece el foloseşte în programele sale metodele
clasei abstracte Graphics, fără să se preocupe de modul în care acestea vor fi executate.
public abstract Graphics create() - creează şi întoarce un nou obiect din clasa
Graphics, care este o copie a obiectului pentru care se aplică această metodă;
public abstract Graphics create(int x, int y, int width, int height) -
creează şi întoarce o copie a obiectului din clasa Graphics căruia i se aplică, însă cu o noua
translaţie a originii (x,y) şi cu valori noi valori ale lăţimii şi înălţimii dreptungiului de
desenare (suprafeţei de decupare);
public abstract void translate(int x, int y) - translatează originea sistemului
de coordonate în punctul (x,y) al sistemului de coordonate curent;
public abstract Color getColor() - întoarce culoarea de desenare curentă;
public abstract void setColor(Color c) - setează culoarea de desenare curentă;
public abstract Font getFont() - întoarce fontul curent;
public abstract void setFont(Font f) - setează fontul curent;
public abstract FontMetrics getFontMetrics() - întoarce metrica fontului
curent;
public abstract void setFontMetrics(FontMetrics fm) - setează metrica
fontului curent;
public abstract Rectangle getClipBounds() - întoarce dreptunghiul de decupare
curent;
public abstract void setClip(int x, int y, int width, int height) -
setează dreptunghiul de decupare curent;
public abstract void copyArea(int x, int y, int width, int height, int
dx, int dy) - copiaza suprafaţa dreptunghiulara cu originea (x,y), lăţimea width şi
înălţimea height într-o noua zonă de aceleaşi dimensiuni, având originea (x+dx, y+dy)..
public abstract void setPaintMode() - setează operaţia logică pe pixeli curentă la
modul paint, adică desenarea se face peste fondul existent, folosinduse culoarea de desenare
curentă, fără a lua în consideraţie culoarea fondului.
public abstract void setXORMode(color c1) - setează operaţiile pe pixeli la
modul XOR, adică se alternează pixelii de culoare curentă cu cei din culoarea c1; aceasta
înseamnă că, dacă un pixel nou plasat are aceeaşi culoare curentă cu cea existentă anterior în
acelaşi loc, ea va fi înlocuita cu c1; invers, dacă pixelul existent anterior avea culoarea c1, ea
va fi înlocuită cu culoarea curentă.
public abstract void drawLine(int x1, int y1, int x2, int y2) - se
traseaza o linie dreaptă din punctul de coordonate (x1,y1) până în punctul (x2,y2);
public abstract void drawRect(int x, int y, int width, int height) - se
trasează un dreptunghi cu colţul din stânga sus în punctul (x,y), având lăţimea width şi
281
Severin Bumbaru
înălţimea height;
public abstract void fillRect(int x, int y, int width, int height) -
umple cu culoarea curentă interiorul dreptunghiului cu colţul din dreapta sus în punctul (x,y)
şi cu dimensiunile (width, height);
public abstract void clearRect(int x, int y, int width, int height) -
şterge conţinutul dreptunghiului de coordonate şi dimensiuni specificate, umplându-l cu
culoarea de fond;
public abstract void drawRoundRect(int x, int y, int width, int height,
int arcWidth, int arcHeight) - trasează un dreptunghi cu colţurile rotunjite, unde
arcWidth si arcHeight sunt respectiv diametrul orizontal şi diametrul vertical al arcelor de
rotunjire;
public abstract void fillRoundRect(int x, int y, int width, int height,
int arcWidth, int arcHeight) - umple cu culoarea curentă interiorul unui dreptunghi cu
colţurile rotunjite;
public abstract void draw3DRect(int x, int y, int width, int height,
boolean raised) - desenează un dreptunghi astfel luminat, încât el apare că iese din
suprafaţa de baza, dacă raised=true, sau este scufundat în aceasta suprafaţă, dacă
raised=false;
public abstract void fill3dRect(int x, int y, int width, int height,
boolean raised) - umple cu culoarea curentă interiorul unui dreptunghi tridimensional,
luând în consideraţie şi iluminarea;
public abstract void drawOval(int x, int y, int width, int height) - se
desenează ovalul înscris în dreptunghiul cu colţul din stânga sus în punctul (x,y) şi cu
dimensiunile (width, height);
public abstract void fillOval(int x, int y, int width, int height) - se
umple cu culoarea curentă conţinutul ovalului înscris în dreptunghiul cu colţul stânga sus în
punctul (x,y) şi de dimensiuni (width, height);
public abstract void drawArc(int x, int y, int width, int height, int
startAngle, int arcAngle) - se desenează un arc circular sau eliptic, care se înscrie în
dreptunghiul specificat. Putem să ne imaginăm că din elipsa înscrisă în acest dreptunghi şi
având centrul în centrul dreptunghiului, se trasează efectiv numai arcul care începe de la
unghiul startAngle şi se extinde pe un unghi egal cu arcAngle. Unghiurile sunt măsurate în
grade. Unghiul 0 este corespunzator poziţiei de la ora 3 a acului de ceasornic, iar sensul
pozitiv al unghiurilor este cel contra acelor de ceasornic;
public abstract void fillArc(int x, int y, int width, int height, int
startAngle, int arcAngle) - umple cu culoarea curentă sectorul mărginit de arcul
specificat prin parametri şi de razele de la capete;
public abstract void drawPolyline(int[] xPoints, int[] yPoints, int
nPoints) - trasează o linie frântă, care trece prin punctele ale căror coordonate (x,y) sunt
date în tabelele xPoints şi yPoints; numărul de puncte este nPoints;
public abstract void drawPoligon(int[] xPoints, int[] yPoints, int
nPoints) - trasează un poligon cu nPoints vârfuri, situate în punctele ale căror coordonate
(x,y) sunt date în tabelele xPoints şi yPoints;
public abstract void drawPoligon(Poligon p) - trasează poligonul specificat ca
argument;
public abstract void fillPoligon(int[] xPoints, int[] yPoints, int
nPoints) - umple cu culoarea curenta un poligon cu nPoints vârfuri, situate în punctele
ale
căror coordonate (x,y) sunt date în tabelele xPoints şi yPoints;
public abstract void fillPoligon(Poligon p) - umple cu culoarea curentă
poligonul specificat ca argument;
public abstract void drawString(String str, int x, int y) - trasează şirul
282
Programarea orientata pe obiecte în limbajul Java
de caractere str, folosind fontul şi culoarea curente; baza primului caracter al şirului este în
punctul de coordonate (x,y);
public abstract void drawString(AttributedCharacterIterator iterator,
int x, int y) - trasează un şir de caractere conţinute în obiectul iterator, care specifică nu
numai caracterele propriuzise ci şi fontul fiecărui caracter; baza primului caracter este în
punctul de coordonate (x,y);
public abstract void drawChars( char[] data, int offset, int length,
int x, int y) - trasează length caractere din tabloul data, începând cu caracterul cu
indicele offset; baza primului caracter se găseşte în punctul de coordonate (x,y); fontul şi
culoarea sunt cele curente;
Metoda paint nu este invocată explicit în program. Ea este invocată implicit (de către maşina
virtuală Java) atunci când componenta respectivă este afişată pe ecran sau îşi modifică
dimensiunile şi/sau poziţia. Dacă, totuşi, programatorul doreşte să solicite explicit desenarea,
foloseşte metoda
public void repaint()
Aceasta metodă care există, de asemenea, în clasa java.awt.Component, nu trebuie
redefinită, singurul ei rol fiind de a apela metoda paint.
Exemplul 1:
În fişierul Desen.java este dat un exemplu de aplicaţie simplă, în care se testează diferite
metode ale clasei Graphics. În acest scop, s-a creat clasa PanouDesen ca o extensie a clasei
Canvas. În această clasă, a fost redefinită metoda paint(), astfel încât să se traseze diferite
desene: un dreptunghi gol, un dreptunghi plin, un dreptunghi gol cu colţurile rotunjite, un
dreptunghi plin cu colţurile rotunjite, un oval gol, un oval plin, o linie frântă şi un poligon. S-
au testat în acest fel metodele clasei Graphics. În fereastra aplicaţiei s-a introdus o instanţă a
clasei PanouDesen.
283
Severin Bumbaru
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Desen {
static Sfarsit sfarsit=new Sfarsit();
static IUG iug=new IUG("Exemplu de desenare");
IUG(String titlu) {
super(titlu);
addWindowListener(sfarsit);
panouDesen=new PanouDesen();
setLocation(200,150);
getContentPane().add(panouDesen);
setSize(220,200);
setVisible(true);
}
}
284
Programarea orientata pe obiecte în limbajul Java
g.setColor(Color.yellow);
g.fillPolygon(xp,yp,5);
}
Exemplul 2:
În fişierul GraficeFunctii.java este dat un exemplu de aplicaţie pentru trasarea graficelor unor
funcţii. În fereastra aplicaţiei s-au pus:
- panoul fct, care conţine o listă de selectie a funcţiei de reprezentat şi trei câmpuri de text,
în care se introduc de la tastatură marginile inferioară şi superioară a intervalului în care se
trasează funcţia (sub forma de numere reale) şi numărul de subintervale în care se împarte
acesta;
- suprafaţa de afişare gr, pe care se trasează graficul;
- eticheta mesaj, în care se afişează eventualele mesaje de eroare.
Pentru panoul fct a fost creată clasa Functii, derivată din clasa JPanel şi s-a folosit gestionarul
de poziţionare GridLayout.
Pentru suprafaţa de afişare s-a creat clasa Grafic, derivată din clasa Canvas.
Clasa Actiuni interceptează evenimentele generate de fereastra principală, câmpurile de text
şi lista de selecţie a funcţiilor. În acest scop, ea este derivată din clasa WindowAdapter şi
implementează interfeţele ActionListener şi ItemListener.
Trasarea graficului se face sub forma unei linii frânte, folosind metoda drawPoliline(int
x[],int y[],int nrPuncte), în care vectorii x şi y conţin coordonatele punctelor prin care
trece curba. Scările de reprezentare pe cele două axe se aleg automat, astfel încât graficul să
ocupe întreaga suprafaţă de desenare. În acest scop, se calculează mai întâi valorile reale ale
funcţiei de reprezentat, completându-se cu ele vectorul real valy. Se determină apoi ymax şi
ymin, iar dimensiunile suprafeţei de desenare se determină prin metodele getWidth() şi
getHeight(), iar xmin şi xmax sunt date. Folosind aceste date se calculează scările pe x şi y,
după care se calculează coordonatele pe desen ale punctelor, completându-se astfel vectorii x
şi y. Aceste calcule se fac în metoda calcul() din clasa Actiuni, iar trasarea graficului se
face în metoda paint() din clasa Grafic.
285
Severin Bumbaru
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class GraficeFunctii {
static Actiuni act=new Actiuni();
static Functii fct=new Functii();
static Grafic gr=new Grafic();
static Label mesaj=new Label();
static boolean trasare=false;
static int nrPasi=200; // numarul de subintervale
static int nrNoduri=nrPasi+1; // numarul de puncte ale graficului
static int indFunc; // indicele functiei care se reprezinta grafic
// Tabele de coordonate ale punctelor graficului functiei
static int x[]=new int[nrNoduri], y[]=new int[nrNoduri];
// inaltimea si latimea suprafetei de desenare si pozitiile axelor
static int inaltime, latime, xord, yabsc;
286
Programarea orientata pe obiecte în limbajul Java
287
Severin Bumbaru
trasare=false;
gr.repaint();
// Preluarea datelor introduse in campurile de text
int nrP=Integer.parseInt(fct.pasi.getText());
if(nrP<1) throw new Exception("Numar de intervale incorect");
if(nrP != nrPasi) {
nrPasi=nrP;
nrNoduri=nrPasi+1;
x=new int[nrNoduri];
y=new int[nrNoduri];
}
xmin=Double.parseDouble(fct.inf.getText()); // marginea din stanga
xmax=Double.parseDouble(fct.sup.getText()); // marginea din dreapta
if(xmin==xmax) throw new Exception("xmin==xmax");
pas=(xmax-xmin)/nrPasi; // Lungimea subintervalului
double valy[]=new double[nrNoduri];// tabloul ordonatelor
// Preluarea indicelui functiei selectate
indFunc=fct.f.getSelectedIndex(); // se afla indicele functiei
// Determinarea dimensiunilor suprafetei de desenare
inaltime=gr.getHeight(); // inaltimea suprafetei de desenare
latime=gr.getWidth(); // latimea suprafetei de desenare
// Calcularea ordonatelor punctelor graficului
for (int i=0; i<nrNoduri; i++) valy[i]=fct.func(xmin+i*pas);
// Determinarea valorilor minima si maxima ale lui y
ymin=valy[0];
ymax=valy[0];
for (int i=1; i<nrNoduri; i++) {
if(valy[i]>ymax) ymax=valy[i];
if(valy[i]<ymin) ymin=valy[i];
}
// Determinarea scarilor de reprezentare pe cele doua directii
scarax=latime/(xmax-xmin);
scaray=inaltime/(ymax-ymin);
// Calcularea coordonatelor de pe desen ale punctelor graficului
for(int i=0; i<nrNoduri; i++) {
y[i]=inaltime-(int)Math.round((valy[i]-ymin)*scaray);
x[i]=(int)Math.round(i*pas*scarax);
}
// Determinarea pozitiilor pe desen ale axelor de coordonate
yabsc=inaltime+(int)Math.round(ymin*scaray);
xord=(int)Math.round(-xmin*scarax);
trasare=true;
}
}
288
Programarea orientata pe obiecte în limbajul Java
}
}
Întrebări
Nivel 1
1. Ce sunt ferestrele de dialog?
2. Ce deosebire există între ferestrele de dialog modale şi cele nemodale?
3. Ce sunt ferestrele de dialog standard?
4. Cum se creeaza ferestrele de dialog standard?
5. Cum se stabileşte culoarea de fond a unei componente?
6. Ce este culoarea de prim plan a unei componente şi prin ce metodă se modifică?
7. Ce sunt instanţele clasei Color?
8. Care este principalul model de culori folosit în Java?
9. Prin ce tip de date se exprimă culorile în sistemul RGB şi care este intervalul în care
acestea iau valori?
10. Care este culoarea pentru care cele trei culori fundamentale RGB au valoarea 0?
11. Care este culoarea pentru care toate cele trei culori fundamentale au valoarea 255?
12. La ce foloseşte clasa JColorChooser?
13. Pe ce fel de componente se poate face desenarea?
14. Care este sistemul de coordonate pentru reprezentări grafice?
15. Care este unitatea de măsură folosită pe axele de coordonate pentru reprezentări
grafice?
16. Ce este contextul grafic?
17. Prin ce clasă abstractă este reprezentat contextul grafic în Java API?
18. În ce clasă este declarată metoda paint şi ce rol are ea?
19. Cum este invocată metoda paint?
20. În ce scop este redefinită metoda paint?
21. Ce trebuie să conţină corpul metodei paint pentru a se trasa un desen?
22. La ce foloseşte metoda repaint?
23. Se redefineste în program metoda repaint?
Nivel 2
1. Prin ce clasă se realizează ferestrele de dialog în JFC/Swing?
2. Ce relaţie există între fereastra de dialog şi proprietarul ei?
3. Prin ce metodă se afişează pe ecran o fereastră de dialog?
4. Prin ce metode se închide o fereastră de dialog?
5. Care tipuri de ferestre pot fi create folosind metodele clasei JOptionPane?
6. Ce conţine o fereastră de dialog de mesaj?
7. Ce conţine o fereastră de dialog de confirmare?
8. Ce conţine o fereastră de dialog de intrare şi cum este ea utilizată?
9. Ce este modelul HSB?
10. Prin ce tip de date se exprimă componentele modelului HSB şi în ce interval de
valori?
289
Severin Bumbaru
290
Programarea orientata pe obiecte în limbajul Java
Introducere în HTML
Deşi prezentarea limbajului HTML nu constituie un obiectiv al acestui manual, vom da aici
unele noţiuni introductive asupra acestui limbaj, fiind utile pentru o mai bună înţelegere a
utilizării appleturilor.
Ce este un hipertext
Hipertextul (engleză: hypertext) este o colecţie de documente, numite şi noduri sau pagini,
unite între ele prin legăturica în Figura 1.
291
Severin Bumbaru
- Figura 1 -
Este necesar, desigur, să se adopte o convenţie privind modul în care se însereaza în pagina
de hipertext legăturile către alte pagini. Pentru utilizatorul care vede pe ecran pagina
respectivă, această legătură apare, de regulă, sub forma unui cuvânt sau unui grup de cuvinte
puse în evidenţă prin subliniere, colorare sau prin ambele procedee. Dacă se face click cu
mouse-ul pe o astfel de "legătura", pagina curentă se schimbă cu pagina către care indică
legătura respectivă. Citirea unui asemenea hipertext nu se face cu un editor de texte obişnuit,
ci cu un program special numit navigator sau browser.
Paginile (nodurile) hipertextului sunt stocate sub forma de fişiere, situate pe un singur
calculator, sau pe mai multe calculatoare legate în reţea.
Ce este WWW
World-Wide Web (WWW) este un sistem de regăsire a informaţiei distribuite pe Internet. În
limba engleză, web înseamnă "plasa", "reţea", "pânză de păianjen", ceeace sugerează
legaturile existente între noduri într-un hipertext. Asadar, WWW este o astfel de "pânză de
păianjen" care acopera întreaga lume şi ale cărei noduri sunt documente.
Documentele sunt stocate sub formă de fişiere pe diferite calculatoare, care acţioneaza ca
servere de web. Utilizatorul poate naviga pe această reţea, folosind un program numit
browser de web (navigator). Cele mai răspândite browsere de web sunt, în prezent, Netscape
Navigator şi Internet Explorer. Putem deci considera că WWW este perceput de utilizator ca
un hipertext, ale cărui noduri sunt raspândite în întreaga lume. Transmiterea prin Internet a
paginilor WWW se face folosind un protocol special, numit HTTP (Hypertext Transfer
Protocol), iar pentru marcarea paginilor de WWW, cu scopul de a însera în ele legăturile
necesare, se foloseşte un limbaj numit HTML.
Ce este HTML
HyperText Markup Language (HTML) este un limbaj de marcare a hipertextelor. În estenţă,
o pagină HTML este un fişier de text ASCII, care conţine nişte simboluri speciale, numite
marcaje sau taguri. Prin aceste marcaje se indică legăturile către alte documente şi către
292
Programarea orientata pe obiecte în limbajul Java
appleturi, imagini sau secvenţe audio incluse în document, dându-se, de asemenea, informaţii
privind formatarea documentului şi altele.
Fiecare marcaj (tag) este de forma <tag> ... </tag>, deci începe printr-un nume de marcaj
(tag) cuprins între paranteze unghiulare şi se termină prin acelaşi marcaj, având în faţa
numelui simbolul / (slash). Astfel, fiecare document HTML începe cu marcajul <html> şi se
termină cu </html>. În limbajul HTML nu se face distincţie în marcaje între literele mari şi
cele mici, deci tagul <HTML> ... </HTML> este echivalent cu tagul <html> ... </html>.
Aceste marcaje pot fi cuprinse unul în altul, ca în Figura 2.a, dar nu este permis ca
domeniile lor să se intersecteze, ca în Figura 2.b.
- Figura 2 -
Pentru crearea de documente HTML se poate folosi un editor de text simplu, în mod ASCII,
dar - în acest caz - cel care creează documentul trebuie să cunoască bine sintaxa HTML şi
toate tagurile acestuia. Mult mai comoda este folosirea unor editoare speciale de pagini Web,
cum sunt Netscape Composer sau Front Page, care prezintă interfaţă utilizator grafică, iar pe
ecran se vede documentul sub forma în care acesta apare şi in browser.
<HTML>
<HEAD>
Antetul documentului
</HEAD>
<BODY>
Corpul documentului
</BODY>
</HTML>
Modul de punere în pagină nu are importanţă, deoarece întreaga informaţie despre structură şi
formatare este conţinuta în marcaje (taguri). Acelaşi document poate fi pus în pagina, de
exemplu, astfel:
Antetul documentului conţine, opţional, titlul acestuia şi informaţii despre autor, tipul de
document etc. O componentă importantă a antetului este titlul documentului, care are forma:
293
Severin Bumbaru
<TITLE>Titlul documentului</TITLE>
Titlul este un text, care apare în bara de titlu a browserului care vizualizează documentul
respectiv.
În marcajul <P> se pot introduce şi indicaţii privind culoarea de fond, culoarea caracterelor
şi forma caracterelor din paragraful respectiv. Aceste indicaţii se dau prin parametri de forma
nume=valoare.
Culoarea caracterelor se indică prin parametrul color=culoare, unde culoare poate fi:
black (negru), gray (gri), silver (argintiu), white (alb), red (rosu), green (verde), blue
(albastru), yellow (galben), lime (verde deschis), aqua (albastru deschis), fuchsia (roz),
purple (purpuriu), maroon (maron), olive (oliv), navy (bleu marin), teal (verde inchis).
Remarcăm că sunt alte nume de culori decât cele din clasa java.awt.Color.
Culoarea fondului se indică prin parametrul bgcolor=culoare, unde numele culorilor sunt
aceleaşi ca pentru caractere.
Forma caracterelor cu care este scris paragraful este indicată prin parametrul face=forma.
În funcţie de platformă, se pot folosi diferite forme de caractere. Există, totuşi, trei forme care
sunt disponibile pe orice platformă: Serif, sansSerif si Monospace. Primele două au caractere
de lăţime variabilă (de exemplu, m este mai lat decat i), iar ultimul are toate caracterele de
aceeasi lăţime (exemplu: în cuvantul lăţime, scris cu astfel de caractere, m şi i au aceeaşi
lăţime). Deosebirea între Serif şi SansSerif este prezentă, respectiv absenţa serifurilor.
Acestea sunt liniuţele mici care delimitează unele linii ale literelor, cum se poate observa
comparând caracterele din cuvintele de mai jos:
Serif SansSerif
Indicarea fontului
Se numeşte font tipul de literă folosit într-un text. Termenul este preluat din tipografie şi se
referă la reprezentarea formei şi mărimii unui caracter. S-a menţionat mai sus cum pot fi
indicate forma şi culoarea caracterelor dintr-un paragraf ca parametri în marcajul <P>. Exista
însă şi situaţii în care un anumit font se foloseşte numai într-o porţiune de paragraf, sau se
extinde pe mai multe paragrafe. În acest scop, se foloseşte marcajul <font parametri>
text</font>.
Parametrii din marcajul <font > sunt face=formă, color=culoare şi size=mărime. Forma
şi culoarea se indică în acelaşi mod ca în marcajul <P>. Mărimea este un număr întreg cu sau
294
Programarea orientata pe obiecte în limbajul Java
fără semn. Dacă se foloseşte un număr întreg fără semn, acesta indică mărimea absolută a
fontului. Mărimea indicată ca numar intreg cu semn în intervalul [-3, +3] arată mărimea
relativă, faţă de cea pentru care este setat browserul utilizat. Aşa dar, de exemplu, parametrii
size=3 si size=+3 indică fonturi de mărimi diferite.
Exemplu
Textul HTML următor:
<font face=serif color=purple size=+1> proba de text </font>
va apare scris pe ecran sub forma
proba de text
Nu este obligatoriu să se indice toţi cei trei parametri. Se folosesc numai cei prin care noul
font diferă de cel anterior.
Indicarea stilului
În afară de forma, marimea şi culoarea fontului, textul se caracterizează şi prin stil: păstrând
forma fontului, textul poate să apară îngroşat, cursiv, subliniat etc. În HTML, stilul textului se
indică prin marcaje speciale:
<B> ... </B> pentru text aldin, îngrosat (engleză: bold);
<I> ... </I> pentru text cursiv (engleză: italic);
<U> ... </U> pentru text subliniat (engleză: underline);
<strike> ... </strike> pentru text tăiat cu o linie (engleză: strikethrough);
<sub> ... </sub> text scris mai mic şi mai jos decât textul de bază (engleză: subscript);
<sup> ... </sup> text scris mai mic şi mai sus decât cel de bază (engleză: superscript).
Aceste stiluri pot fi şi combinate. De exemplu, textul HTML
<B><I><U>text aldin, cursiv şi subliniat</U></I></B>
apare pe ecran sub forma:
text aldin, cursiv şi subliniat
Primul titlu
Al doilea titlu
Al treilea titlu
295
Severin Bumbaru
Text preformatat
S-a arătat mai sus că aşezarea textului în pagină se face de către browser, respectând
marcajele de formatare din textul HTML. În consecinţă, dacă se modifică dimensiunile
ferestrei, se modifica şi aşezarea în pagină a textului. Spaţiile şi caracterele speciale din textul
HTML, cum sunt caracterul de trecere la linie nouă sau de întoarcere a carului sunt ignorate
de browser şi nu au efect la afişarea pe ecran. Există, totuşi un marcaj care impune ca textul
sa fie aşezat pe ecran aşa cum este el în pagina HTML. Acest marcaj este <PRE> text
preformatat </PRE>. De exemplu, textul
<PRE>
un exemplu de text
preformatat
scris pe trei linii
</PRE>
va apare pe ecran sub forma
un exemplu de text
preformatat
scris pe trei linii
296
Programarea orientata pe obiecte în limbajul Java
un URL (engleză: Uniform Resource Locator), care este modul de adresare caracteristic
pentru WWW. Iată exemple de URL-uri:
"http://www.w3.org/default.html"
"http://java.sun.com/docs/books/jls/html/index.html"
"http://lib.cs.ugal.ro/~sbumbaru/CursJava/Sapt01/poo.html"
Prima parte a URL-ului (în cazul de faţă http:) reprezintă protocolul de transmisie folosit.
Cele două bare (//) indică modul de acces (în cazul de faţă - adresa pe Internet a unui
calculator, de exemplu java.sun.com). Urmeaza calea absolută parcursă pe calculatorul
respectiv (de exemplu /docs/books/jls/html/) şi numele documentului (index.html).
Marcajul APPLET
Pentru a introduce într-un document HTML o referinţă la un applet, se foloseşte marcajul
(tagul) APPLET, care are forma următoare:
în care:
fişier_class - fişierul cu extensia .class, în care se află bytecode-ul appletului (indicarea
extensiei nu este obligatorie), de exemplu:
CODE=TestFlowAp.class sau CODE=TestFlowAp
localizare - referinţa la directorul în care se găseşte bytecode-ul appletului; această
referinţă poate fi absolută, relativă sau sub forma de URL, la fel ca în cazul legăturilor către
alte documente, cu observaţia că nu se mai termină prin numele documentului ţintă, acesta
fiind cel din parametrul CODE; dacă fişierul bytecode al appletului se găseşte în acelaşi
director cu fişierul HTML în care acesta este invocat, parametrul CODEBASE lipseşte;
lăţime si înălţime - numere întregi, reprezentând lăţimea şi înălţimea appletului;
aliniere - alinierea appletului în pagina de browser, poate fi una din următoarele:
left | right | top | texttop | middle | absmiddle | baseline | bottom | absbottom
unde left şi right indică aliniere la stânga sau la dreapta, top - aliniere cu elementul cel
mai înalt din linie (fie el text, imagine sau alt applet), texttop - aliniere cu cel mai înalt
element de text din linia respectivă, middle - mijlocul appletului se aliniază cu mijlocul liniei
de bază a textului, absmiddle - mijlocul appletului se aliniază cu mijlocul elementului cel mai
mare din linie, baseline sau bottom - baza appletului se aliniază cu linia de bază a textului,
absbottom - baza appletului va fi elementul cel mai de jos din linie.
text_de_înlocuire- un text care va apare în locul appletului, dacă browserul folosit pentru
vizualizarea documentului HTML nu este capabil sa utilizeze appleturi.
297
Severin Bumbaru
Dacă este necesar, marcajul APPLET poate să conţină unul sau mai mulţi parametri, care
se vor transmite appletului la lansarea acestuia. Aceşti parametri apar în subtagul PARAM,
pentru fiecare din ei indicându-se un nume şi o valoare.
Exemplul 1: marcajul
<APPLET CODE="CBGroupAp" WIDTH=180 HEIGHT=80> Testarea clasei
CheckboxGroup </APPLET>
se foloseşte pentru a include în documentul HTML a unui applet, al cărui fişier de bytecode
este CBGroupAp.class şi se găseşte în acelasi director cu documentul HTML în care există
acest tag. Appletul va avea lăţimea 180 şi înălţimea 80. Dacă browserul folosit nu suportă
appleturi java, în locul rezervat appletului va apare textul de înlocuire "Testarea clasei
CheckboxGroup".
Exemplul 2: marcajul
<APPLET CODE=PrimApplet CODEBASE=../Sapt01/surse WIDTH=200
HEIGHT=100>Primul applet</APPLET>
se foloseşte pentru a include în documentul HTML appletul al cărui fişier bytecode este
PrimApplet.class şi care se găseşte în alt director decât fişierul HTML, dar pe acelaşi disc. În
CODEBASE s-a folosit forma relativa a căii.
Exemplul 3: marcajul
<APPLET CODE=PrimApplet CODEBASE=/CursJava/Sapt11/surse WIDTH=200
HEIGHT=100> Primul applet </APPLET>
are aceeaşi semnificaţie ca cel din exemplul anterior, dar în CODEBASE s-a folosit forma
absolută a căii.
Exemplul 4: marcajul
<APPLET CODE=PrimApplet
CODEBASE=http://lib.cs.ugal.ro/~sbumbaru/CursJava/Sapt11/surse WIDTH=200
HEIGHT=100> Primul applet </APPLET>
are aceeaşi semnificaţie ca cel din exemplul anterior, dar în CODEBASE se foloseşte un
URL la care poate fi găsit appletul respectiv.
Avantajul este că valorile parametrilor pot fi modificate direct în fişierul HTML, fără să mai
298
Programarea orientata pe obiecte în limbajul Java
Exemplu:
În fişierul DouaTexte.java este dat un exemplu de applet în care sunt preluaţi doi parametri,
cu numele textul1 si textul2. Aceşti parametri sunt şiruri de caractere, care se afişează
alternativ într-o etichetă. Schimbarea textului se face la apăsarea pe buton. Acest applet este
inclus în pagina HTML din fişierul DouaTexte.html prin următorul marcaj:
Pentru a se afişa alte texte, este suficient să se modifice în mod corespunzător valorile
parametrilor din acest marcaj, fără a se face modificări în programul appletului.
Exemplu
În fişierul TextHTML.java este dat un exemplu de aplicaţie, în care se experimentează
folosirea textelor HTML în componentele interfeţei grafice. În fereastra aplicaţiei sunt plasate
următoarele componente:
- o arie de text (JTextArea), în care se afişează un text iniţial, care poate fi apoi modificat de
utilizator;
- o etichetă (JLabel) pe suprafaţa căreia se afişează textul existent în aria de text din partea
stângă;
- un buton cu inscripţia Vizualizare text, a cărui acţionare are ca efect afişarea pe suprafaţa
etichetei a textului din aria de text;
- un al doilea buton, cu inscripţia Reset, a cărui acţionare are ca efect revenirea la textul
299
Severin Bumbaru
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
buton.addActionListener(new Vizualizare());
buton.setBorder(BorderFactory.createRaisedBevelBorder());
hb.add(buton);
JButton reset=new JButton("<html><font color=blue size=4>"+
"<b>Reset</b></font>"); // butonul de revenire la textul initial
reset.setBorder(BorderFactory.createRaisedBevelBorder());
reset.addActionListener(new Reset());
hb.add(reset);
vb.add(hb);
300
Programarea orientata pe obiecte în limbajul Java
cp.add(vb,BorderLayout.WEST);
label=new JLabel(); // eticheta pe care se vizualizeaza textul HTML
label.setBorder(BorderFactory.createTitledBorder(
"Vizualizarea textului HTML"));
label.setOpaque(true);
label.setBackground(Color.white);
cp.add(label,BorderLayout.CENTER);
setLocation(50,50);
setSize(450,300);
setVisible(true);
}
Remarcăm că, în această aplicaţie, s-au introdus următoarele abordări noi faţă de aplicaţiile
anterioare:
- s-a considerat că interfaţa grafică este însăşi clasa principala TextHTML, derivata din clasa
JFrame. În consecinţă, adăugarea componentelor la fereastra principală se face în
constructorul clasei TextHTML;
- clasele interioare şi câmpurile clasei TextHTML nu au mai fost declarate statice, în
schimb, în metoda main() s-a creat o instanţă a acestei clase.
Desigur că acest mod de construire a aplicaţiei nu are legătura cu tema acesteia, fiind doar o
ilustrare a unui alt mod de lucru posibil în Java.
301
Severin Bumbaru
Applet-uri
Miniaplicaţia (engleză: applet) este un mic program cu interfaţă utilizator grafică, care nu
poate rula în mod independent, ci este înglobat într-o altă aplicaţie, numită context de applet.
Pentru executarea unui applet trebuie să existe deci două entităţi: applet-ul propriu-zis şi
contextul în care acesta se execută (engleză: applet context). În mod normal, contextul de
executare a unui applet este un navigator de Web (engleză: web browser), cum sunt Netscape
Navigator, Internet Explorer sau HotJava. Pentru testare, appletul poate fi executat, de
asemenea, folosind drept context un program de vizualizare special, existent in SDK, numit
appletviewer.
Orice applet este realizat ca o clasă, derivată din clasa Applet, existentă în pachetul
java.applet, sau derivată din clasa JApplet, care extinde clasa Applet şi există în pachetul
javax.swing.
Întrucât utilizarea clasei JApplet este mai complicată, ne vom rezuma aici la descrierea şi
exemplificarea utilizării clasei Applet.
Clasa Applet
Clasa Applet se găseşte în pachetul java.applet şi este o subclasă a clasei Panel din
pachetul java.awt. În consecinţă, applet-ul este, de fapt, un caz special de container.
Clasa Applet este superclasa tuturor miniaplicaţiilor care sunt încorporate în pagini Web sau
pot fi vizualizate cu un Java Applet Viewer.
Clasa Applet moşteneşte metodele superclaselor sale Component, Container şi Panel, oferind
şi metode specifice. Dintre acestea, cele mai importante sunt : init(), start(), stop() şi
destroy(). Aceste metode sunt apelate de către browser în momentele importante ale
ciclului de viaţă al unui applet, respectiv în momentul încărcării acestuia în memorie, în
momentul începerii sau întreruperii execuţiei şi înainte ca appletul să fie distrus. Aşa cum
sunt ele oferite de clasa Applet, aceste metode nu fac nimic. Ele pot fi însă redefinite de
programatori în subclasele clasei Applet, astfel încât să execute anumite acţiuni specifice
momentelor în care sunt invocate. Metoda init() este utilizată pentru a crea partea "statică"
a applet-ului: adăugarea de componente la applet, înregistrarea ascultătorilor de evenimente
etc. Ea este redefinită în marea majoritate a applet-urilor. Metodele start() şi stop() se
folosesc numai pentru lansarea şi oprirea proceselor dinamice (de exemplu animaţie şi/sau
sunete) care nu trebuie să continuie când appletul nu este vizibil pe ecran. În fine, metoda
destroy() se foloseşte în special pentru a distruge firele de execuţie paralele care au fost
create de către applet, sau a elibera anumite resurse ocupate de acesta.
Vom prezenta aici metodele mai frecvent utilizate. Pentru celelalte recomandăm consultarea
documentaţiei originale.
public void init() - metoda este invocată de către browser sau appletviewer atunci
302
Programarea orientata pe obiecte în limbajul Java
când appletul este încărcat în memorie, deci înainte de prima invocare a metodei start();
public void start() - metoda este invocată de către browser atunci când appletul
trebuie să îşi înceapă execuţia, adică atunci când este vizitată sau revizitată pagina Web în
care acesta este inclus;
public void stop() - metoda este invocată de către browser atunci când pagina de
Web, în care se găseşte appletul, este înlocuită de alta, fără a fi însă eliminată din memorie.
Întrucât appletul nu mai este vizibil pe ecran, acesta poate sa îşi înceteze temporar execuţia;
metoda este invocată, de asemenea, înaintea invocării metodei destroy();
public void destroy() - metoda este invocată de către browser înainte de
distrugerea appletului;
public boolean isActive() - determină dacă appletul este activ;
public String getAppletInfo() - dacă este redefinită în subclase, metoda poate
intoarce informaţii despre applet: autorul, versiunea, drepturi de autor etc. În clasa Applet
metoda întoarce null;
public AppletContext getAppletContext() - întoarce o referinţă către contextul
appletului;
public URL getCodeBase() - întoarce URL-ul (locaţia pe Internet) la care se găseşte
bytecode-ul appletului;
public URL getDocumentBase() - întoarce URL-ul la care se găseşte documentul
care conţine appletul;
public String getParameter(String name) - întoarce valoarea parametrului cu
numele dat ca argument, conţinut în tagul APPLET al documentului HTML.
Dăm în continuare trei exemple de applet-uri, împreună cu fişierele HTML în care se afişeaza
acestea. Pentru compatibilitate, s-au utilizat numai componente din pachetele java.applet şi
java.awt.
Exemplul 1:
În introducere a fost deja dat un exemplu de applet simplu, în care nu se redefineşte nici o
metodă a clasei Applet, folosindu-se numai o invocare de metodă prin care se scrie un text pe
suprafaţa appletului. Programul acestui applet se găseşte în fişierul PrimApplet.java, iar
fişierul HTML în care este folosit acest applet este PrimApplet.html.
Exemplul 2:
În fişierul CBGroupAp.java este dat un applet de testare a unui grup de casete de validare
(butoane radio) din fişierul TestCheckboxGroup.java. Remarcăm următoarele deosebiri faţă
de o aplicaţie similară:
- nu mai există o fereastră principală sub formă de cadru (Frame sau JFrame) ca în cazul
aplicaţiei, acest rol fiind îndeplinit chiar de către applet;
- în consecinţă, nu mai este necesară o clasă care sa intercepteze evenimentele generate de
fereastra principală;
- adăugarea componentelor la applet, setarea culorilor şi înregistrarea interceptorului de
evenimente, care în cazul aplicaţiei se fac în metoda main() sau în constructorul interfeţei
utilizator grafice, acum se fac în metoda init();
- având în vedere că, pentru clasa Applet, gestionarul de poziţionare implicit este
303
Severin Bumbaru
Exemplul 3:
Oricare din aceste applet-uri poate fi vizionat şi într-un appletviewer, dacă (din fereastra X-
window sau MS-DOS) se dă comanda:
appletviewer fisierHTML
De exemplu, pentru a vizualiza applet-ul TestFlowAp, se dă comanda
appletviewer TestFlow.html
unde TestFlow.html este fişierul HTML în care este invocat acest applet.
Întrebări
Nivel 1
1. Ce este un hipertext?
2. Ce sunt nodurile hipertextului?
3. Ce este WWW?
4. Ce este un browser de Web?
5. Ce este HTTP?
6. Ce este HTML?
7. Ce formă au marcajele HTML?
8. Care este marcajul cu care începe şi se termină un document HTML?
9. Care este structura unui document HTML?
10. În ce zonă a documentului HTML se specifică titlul acestuia?
11. Cum se specifică titlul unui document HTML?
12. Prin ce se marchează, într-un document HTML, legăturile către alte pagini ale
hipertextului?
13. La ce serveşte marcajul APPLET şi ce conţine el?
14. Cum sunt folosite marcajele HTML în componentele JFC/Swing?
15. Ce este un applet?
16. Ce clase se folosesc pentru realizarea applet-urilor?
17. Ce este un context de applet?
18. Ce contexte de applet cunoaşteţi?
19. Din ce clasă este derivată clasa Applet? dar clasa JApplet?
20. Care sunt principalele metode ale unui applet şi de către ce program sunt invocate ele?
21. La ce serveşte metoda init() în cazul unui applet?
22. Când sunt invocate metodele start() şi stop() ale unui applet?
23. În ce scop se foloseşte metoda destroy() a unui applet?
304
Programarea orientata pe obiecte în limbajul Java
Nivel 2
1. Cum se marchează paragrafele într-un document HTML?
2. Este absolut necesar ca un paragraf să se încheie cu marcajul </P>?
3. Cum se marchează într-un document HTML trecerea forţată la linie nouă?
4. Cum se specifică, într-un document HTML, culoarea fondului unui paragraf?
5. Cum se specifică culoarea caracterelor într-un document HTML?
6. Cum se specifică forma caracterelor într-un document HTML?
7. Care sunt principalele tipuri (forme) de caractere folosite în documentele HTML?
8. Ce conţine marcajul <font> şi ce semnificaţie are?
9. Ce este stilul textului?
10. Cum se marchează stilul textului într-un document HTML?
11. Cum se marcheaza titlurile şi subtitlurile de capitole într-un document HTML?
12. Ce este un text preformatat şi cum se marchează el într-un document HTML?
13. Cum se reprezinta, într-o legatură HTML, o referinţă absoluta la altă pagină de pe
acelaşi calculator?
14. Cum se reprezintă, într-o legatură HTML, o referinţă relativă la altă pagina de pe
acelaşi calculator?
15. Cum se reprezintă, într-o legatură HTML, o referinţă la o pagină situată pe alt
calculator de pe Internet?
16. Cum pot fi introduşi parametri în marcajul APPLET şi la ce folosesc?
17. Ce componente ale interfeţei grafice permit folosirea textelor HTML?
18. Prin ce metodă se introduce un text HTML într-o componentă JFC/Swing?
19. Ce se întamplă dacă, într-un text care se pune într-o componentă JFC/Swing, există o
eroare de sintaxa HTML?
20. Ce efect au metodele init(), start() şi stop() ale unui applet, aşa cum sunt ele
conţinute în clasa Applet?
21. Este obligatoriu ca, la crearea unei noi clase de applet, să fie redefinite toate cele patru
metode principale init(), start(), stop() şi destroy()?
305
Severin Bumbaru
306
Programarea orientata pe obiecte în limbajul Java
- Figura 1 -
Fluxul este un concept situat pe un nivel înalt de abstractizare, fiind privit ca o simplă
succesiune de octeţi sau de caractere care se transmite între sursă şi destinaţie. Nu prezintă
importanţă nici natura sursei sau a destinaţiei, nici modul în care trebuie interpretată secvenţa
de octeti sau de caractere respectivă. De exemplu, un grup de 32 de octeţi transmişi între
sursă şi destinaţie poate să reprezinte 32 de caractere ASCII sau 16 caractere Unicode sau 8
numere de tip int sau 4 numere de tip double etc. Această abstractizare permite să se trateze
în mod unitar toate tipurile de transmisii de date.
Se disting doua feluri de fluxuri: de ieşire şi de intrare. Pentru un proces dat, toate fluxurile
transmise de acesta către exterior se numesc fluxuri de ieşire, iar cele primite din exterior se
numesc fluxuri de intrare. În consecinţă, acelaşi flux este de ieşire în raport cu sursa şi de
intrare în raport cu destinaţia.
Programatorului procesului sursă îi revine obligaţia de a pregăti, sub forma unei succesiuni de
caractere sau de octeţi, datele care urmează a fi transmise pe fluxul de ieşire. Interpretarea şi
tratarea datelor conţinute în flux se face de către procesul de destinaţie, deci întreaga
responsabilitate îi revine celui care programează acest proces. Desigur că este necesar ca
interpretarea datelor din flux la destinaţie să corespundă cu cea de la sursă. Programarea
operaţiilor de intrare/ieşire poate fi destul de complicată, dar în limbajul Java ea este totuşi
uşurată de existenţa unui număr destul de mare de clase cu această destinaţie, grupate în
pachetul java.io.
Pachetul java.io
În Java 2 SDK se consideră că fluxurile pot fi de caractere saude octeţi. În primul caz, de la
sursă la destinaţie se transmite o succesiune de caractere Unicode (de câte 16 biţi), iar în al
doilea caz - o succesiune de octeţi (de 8 biţi). În mod corespunzător, pentru fiecare din cele
două categorii de fluxuri există câte o ierarhie de clase de fluxuri de intrare şi o ierarhie de
clase de fluxuri de iesire. Pentru fluxurile de caractere, rădăcinile ierarhiilor de clase sunt
clasele abstracte Reader şi Writer.Pentru fluxurile de octeţi, rădăcinile acestor ierarhii sunt
clasele abstracte InputStream şi OutputStream. Aceste ierarhii de clase sunt reprezentate
în figurile 2, 3, 4 şi 5. În afară de cele patru ierarhii menţionate, în pachetul java.io există şi
clase auxiliare şi interfete. Distingem trei tipuri de clase, care sunt reprezentate în aceste
figuri prin culori diferite:
- clase abstracte (culoare albastră);
- clase care efectuează operaţiile de intrare sau de ieşire propriu-zise (culoare verde) şi
modelează sursele sau destinaţiile fluxurilor (engleză: Data Sink Streams);
307
Severin Bumbaru
- clase care efectuează unele operaţii de transformare a datelor de pe flux (culoare violet) şi
reprezinta "fluxuri de prelucrare" (engleză: Processing Streams).
308
Programarea orientata pe obiecte în limbajul Java
În JDK 1.0 existau numai clasele de fluxuri de octeţi. Fluxurile de caractere au fost introduse
începand cu JDK 1.1. În prezent se folosesc ambele categorii de fluxuri. Pentru transmiterea
textelor sau a datelor reprezentate în format extern şi codificate în Unicode este preferabil,
evident, să se folosească fluxuri de caractere. Pentru transmiterea datelor codificate binar sau
a textelor în care caracterele sunt codificate pe un octet (de exemplu în codul ASCII) este
preferabilă folosirea fluxurilor de octeţi.
309
Severin Bumbaru
Fluxurile pot fi înlănţuite, astfel încât ieşirea unui flux poate fi intrare a altui flux. Astfel, în
Figura 6 este dată schema unei înlănţuiri de fluxuri de intrare: ieşirea fluxului InputStream
(sub forma de fluxului de octeţi flux1) este dată la intrarea fluxului InputStreamReader, care o
converteşte în fluxul de caracrtere flux2, iar acesta - la rândul lui - este preluat fe fluxul de
intrare de caractere cu zonă tampon BufferedReader.
Metode:
public int read() throws IOException - citeşte din fluxul de intrare un singur
caracter; întoarce caracterul citit (în domeniul 0 .. 16383) sau -1 dacă s-a ajuns la sfârşit de
fişier; metoda produce blocarea procesului în care este invocată, până când apare un caracter
în fluxul de intrare;
public int read(char[] cbuf) throws IOException - citeşte din flux o secvenţă
de caractere şi le depune într-o zonă tampon (buffer) constituită din tabloul de caractere cbuf;
întoarce numărul de caractere citite sau -1 dacă s-a atins sfârşitul de fişier; metoda produce
blocarea procesului până când apar caractere în fluxul de intrare, sau se ajunge la sfârşit de
fişier;
310
Programarea orientata pe obiecte în limbajul Java
public abstract int read(char[] cbuf, int off, int len) throws
IOException - acţionează asemănător cu metoda precedentă, dar depunerea caracterelor
citite în zona tampon de destinaţie se face începând de la poziţia off (offset), iar numărul
maxim de caractere citite este len;
public long skip(long n) throws IOException - se sare peste n caractere din
fluxul de intrare, care nu vor fi citite; procesul apelant este blocat pană când apare cel puţin
un caracter în fluxul de intrare; dacă se întâlneşte sfârşitul de fişier, se generează o excepţie
de intrare/ieşire; întoarce numărul de caractere sărite efectiv;
public boolean ready() - întoarce true dacă fluxul de intrare este gata pentru a
putea fi citit;
public void mark(int readAheadLimit) throws IOException - marchează
poziţia curentă în fluxul de intrare, pentru a se putea reveni la ea ulterior; argumentul
readAheadLimit indică numărul de caractere care vor putea fi ulterior citite din flux,fără ca
acest marcaj să se piardă; excepţia de intrare/ieşire apare dacă fluxul nu suportă marcarea sau
dacă se produce altă eroare de intrare/ieşire;
public boolean markSupported() - indică dacă fluxul suportă marcarea;
public void reset() throws IOException - dacă fluxul a fost marcat, este readus
la poziţia corespunzătoare ultimului marcaj; dacă fluxul nu a fost marcat sau nu suportă
resetarea, se generează o excepţie de intrare/ieşire;
public abstract void close() throws IOException - închide fluxul; din acest
moment, invocarea metodelor read(), ready(), mark() sau reset pentru acest flux va genera o
excepţie de intrare/ieşire.
Clasa Writer
Clasa abstractă java.io.Writer este rădăcina ierarhiei de clase pentru fluxuri de ieşire de
caractere.
Metode:
311
Severin Bumbaru
Clasa InputStream
Clasa java.io.InputStream este rădăcina ierarhiei de clase pentru fluxuri de intrare
organizate pe octeţi.
Metode:
public int read() throws IOException - citeşte din fluxul de intrare un singur
octet; întoarce octetul citit (in domeniul 0 .. 255) sau -1 dacă s-a ajuns la sfârşit de fişier;
metoda produce blocarea procesului în care este invocată, până când apare un octet în fluxul
de intrare;
public int read(byte[] buf) throws IOException - citeşte din flux o secvenţă
de octeţi şi îi depune într-o zonă tampon (buffer) constituită din tabloul de octeţi buf; întoarce
numărul de octeţi citiţi sau -1 dacă s-a atins sfârşitul de fişier; metoda produce blocarea
procesului până când apar octeţi în fluxul de intrare, sau se ajunge la sfârşit de fişier;
public abstract int read(byte[] buf, int off, int len) throws
IOException - acţionează asemănător cu metoda precedentă, dar depunerea octeţilor citiţi
în tabloul de destinaţie byte[] se face începând de la poziţia off (offset), iar numărul maxim
de octeţi citiţi este len;
public long skip(long n) throws IOException - se sare peste n octeţi din fluxul
de intrare, care nu vor fi citiţi; procesul apelant este blocat până când apare cel puţin un octet
în fluxul de intrare; dacă se întâlneşte sfârşitul de fişier se generează o eroare de intrare/ieşire;
întoarce numărul de octeţi săriţi efectiv;
public int available() throws IOException - întoarce numărul de octeţi
disponibili pentru citire în fluxul de intrare;
public void mark(int readAheadLimit) throws IOException - marchează
poziţia curenta în fluxul de intrare, pentru a se putea reveni la ea ulterior; argumentul
readAheadLimit indică numărul de octeţi care vor putea fi ulterior citiţi din flux, fără ca acest
marcaj să se piardă; excepţia de intrare/ieşire apare dacă fluxul nu suportă marcarea sau dacă
se produce altă eroare de intrare/ieşire;
public boolean markSupported() - indică dacă fluxul suportă marcarea;
public void reset() throws IOException - dacă fluxul a fost marcat, este readus
la poziţia corespunzătoare ultimului marcaj; dacă fluxul nu a fost marcat sau nu suporta
resetarea, se generează o excepţie de intrare/ieşire;
public abstract void close() throws IOException - închide fluxul; din acest
moment, invocarea metodelor read(), ready(), mark() sau reset() pentru acest flux va genera o
excepţie de intrare/ieşire.
Clasa OutputStream
Clasa java.io.OutputStream este rădăcina ierarhiei de clase pentru fluxuri de iesire de octeţi.
Metode:
312
Programarea orientata pe obiecte în limbajul Java
ieşire; dacă fluxul a salvat într-o zonă tampon anumiţi octeţi scrişi cu metodele write(), aceşti
octeţi sunt scrişi efectiv în fluxul de destinaţie; dacă această destinaţie este tot un flux, invocă
şi metoda flush() a acestuia, astfel că se "descarcă" întregul lanţ de fluxuri;
public abstract void close() throws IOException - se închide fluxul de ieşire;
invocarea ulterioară a metodelor write() sau flush() pentru acest flux va produce o excepţie de
intrare/ieşire.
Clasa PrintStream
Clasa PrintStream conţine doua feluri de metode de scriere a datelor: metodele cu numele
write() scriu întotdeauna octeţi (fără formatare), în timp ce cele cu numele print() sau
println() formatează datele, respectând convenţia de codificare (pe octeti sau pe caractere)
specifică platformei pe care ruleaza aplicaţia respectivă. Totuşi, se recomandă ca pentru a
obţine fluxuri de caractere să se folosească clasa PrintWriter.
Deosebirea dintre print() şi println() este că metodele cu numele println() adaugă, la
sfârşitul şirului de octeţi generat, un caracter de sfârşit de linie ('\n'). În consecinţă, dacă se
313
Severin Bumbaru
foloseşte metoda print(), afişarea se face fără a se trece la o linie nouă, în timp ce dacă se
foloseşte metoda println(), după afişare se trece la linie nouă. Dacă fluxul este cu
descărcare automată, metoda println() provoacă, de asemenea, descărcarea acestuia (flush).
Constructori:
public PrintStream(OutputStream out) - creează un nou flux de formatare pentru
tipărire, conectat la fluxul de ieşire out; acest flux nu se descarcă automat;
public PrintStream(OutputStream out, boolean autoFlush) - creeaza un nou
flux de formatare a datelor pentru tipărire, conectat la fluxul de ieşire out; al doilea argument
indică dacă fluxul se descarcă automat atunci când se întâlneste în flux caracterul '\n' (linie
nouă) sau se execută metoda println() sau se tipăreşte un tablou de octeţi.
Metode:
public void flush() - descarcă fluxul (goleşte zonele tampon, transmiţând
conţinutul lor la ieşire);
public void close() - închide fluxul;
public void write(int b) - scrie în fluxul de ieşire un singur octet (ultimul octet al
argumentului);
public void write(byte[] buf, int off, int len) - se scriu în fluxul de ieşire
len octeţi din tabloul de octeti buf (buffer), începând de la poziţia off (offset);
public void print(boolean b) - transmite în fluxul de ieşire forma externă a
valorii variabilei booleene b, respectiv cuvântul true sau false;
public void print(char c) - se afişează caracterul c (pe unul sau doi octeţi,
depinzând de platformă);
public void print(int i) - se afişează numărul întreg i;
public void print(long l) - se afişează numărul întreg lung l;
public void print(float f) - se afişează numărul real în simplă precizie f;
public void print(double d) - se afişează numărul real în dublă precizie d;
public void print(char[] s) - se afişează conţinutul tabloului de caractere s;
public void print(String s) - se afişează şirul de caractere s;
public void print(Object obj) - se afişeaza obiectul obj convertit în şir de
caractere prin aplicarea metodei String.valueOf(obj) care, la rândul ei, apeleaza metoda
obj.toString() din clasa căreia îi aparţine obiectul;
public void print() - introduce în fluxul de ieşire codul caracterului '\n' (linie
nouă);
public void println(boolean b) - transmit în fluxul de ieşire forma externă a
valorii variabilei booleene b, respectiv cuvântul true sau false;
public void println(char c) - se afişează caracterul c (pe unul sau doi octeţi,
depinzând de platformă);
public void println(int i) - se afişează numărul întreg i;
public void println(long l) - se afişează numărul întreg lung l;
public void println(float f) - se afişează numărul real în simplă precizie f;
public void println(double d) - se afişează numărul real în dublă precizie d;
public void println(char[] s) - se afişează conţinutul tabloului de caractere s;
public void println(String s) - se afişează şirul de caractere s;
public void println(Object obj) - se afişează obiectul obj convertit în şir de
caractere prin aplicarea metodei String.valueOf(obj);
314
Programarea orientata pe obiecte în limbajul Java
Clasa PrintWriter
Constructori:
public PrintWriter(Writer out) - creează un nou flux de formatare pentru afişare,
fără descărcare automată, conectându-l la fluxul de ieşire pe caractere out;
public PrintWriter(Writer out, boolean autoFlush) - la fel ca şi constructorul
precedent, dar al doilea argument specifică dacă are loc descărcarea automată a fluxului;
public PrintWriter(OutputStream out) - creează un nou flux de formatare pe
caractere, fără descărcare automată, conectându-l la fluxul de ieşire pe octeţi out; el creează şi
un OutputStreamWriter intermediar, care face conversia caracterelor pe unul sau doi octeţi,
dependent de platformă;
public PrintWriter(OutputStream out, boolean autoFlush) - la fel ca şi
constructorul precedent, dar al doilea argument indică dacă se face descărcare automată a
fluxului.
Metode:
Această clasa implementează aceleaşi metode ca şi clasa PrintStream, cu excepţia celor care
scriu octeţi bruti (write()).
Fişiere
În memoria externă a calculatorului, datele se păstrează sub forma de fişiere. Fişierul
(engleză: File) este o colecţie de înregistrări situată, de regulă, pe un suport extern şi
identificată printr-un nume. Fiecare înregistrare (engleză: Record) este o grupare de
informaţii sau de date care poate fi tratată în mod unitar.
315
Severin Bumbaru
După modul de exploatare, fişierele pot fi de intrare, de ieşire sau de intrare/ieşire (de
manevră). În cazul fişierelor de intrare, din momentul deschiderii fişierului şi până în
momentul închiderii acestuia se pot efectua numai operaţii de citire. În cazul fişierelor de
ieşire, între momentele de deschidere şi de închidere a fişierului respectiv se pot face numai
operaţii de scrieire. Desigur însă că, după ce s-a încheiat scrierea într-un anumit fişier de
ieşire şi acesta a fost închis, el poate fi deschis ca fişier de intrare. Acelaşi fişier poate fi citit
de mai multe ori. Fişierele de intrare/ieşire permit ca, după ce au fost deschise, să se
efectueze atât operaţii de scriere, cât şi de citire.
După modul de acces fişierele pot fi cu acces secvenţial sau cu acces direct. Fişierele cu
acces secvenţial se caracterizează prin faptul că înregistrările lor pot fi parcurse într-un singur
sens, în ordinea în care acestea sunt plasate în fişier. În cazul fişierelor cu acces direct,
numite şi fişiere cu acces aleator (engleză: random access file) ordinea de parcurgere a
înregistrărilor din fişier este arbitrară, în sensul că la fiecare operaţie de intrare/ieşire făcută
asupra fisierului respectiv se poate indica adresa sau numărul de ordine al înregistrării care va
fi citită sau scrisă.
În limbajul Java, fişierul este privit ca sursa sau destinaţia unui flux. În cazul citirii din
fişier, datele se transmit de la acesta către memoria internă sub forma unui flux de intrare. În
cazul operaţiei de scriere, datele se transmit de la memoria internă la fişier sub forma unui
flux de ieşire.
În principiu, comunicarea între memoria internă şi un fişier de text se face sub forma unui
flux de caractere. Totuşi, pe platformele pe care reprezentarea caracterelor se face pe un octet
(de exemplu în cod ASCII), acesta poate fi tratat şi ca un flux de octeţi.
În cazul fişierelor de date, comunicarea dintre memoria internă şi fişier se poate face prin
fluxuri de caractere numai dacă datele din fişier sunt reprezentate exclusiv în format extern
(deci sub forma de şiruri de caractere). Dacă însă există şi câmpuri de date în format binar,
legatura dintre memoria internă şi fişierul de date se face, de regula, prin fluxuri de octeţi.
Clasa File
Instanţele clasei java.io.File conţin informaţii privind numele fişierului şi calea pe care se
găseste acesta (engleza: Path). Clasa File oferă, de asemenea, metode prin care se pot face
unele operaţii legate de prezenţa fişierului respectiv: se poate afla dacă fişierul există, dacă el
poate fi citit sau scris, se poate crea un fişier nou, se poate şterge un fişier existent etc.
316
Programarea orientata pe obiecte în limbajul Java
Calea indică modul în care poate fi localizat fişierul de pe disc. Calea poate fi absolută sau
relativă. Calea absolută constă în indicarea unităţii de disc şi a succesiunii de directoare prin
care se ajunge de la rădacină la fişierul care ne interesează. Calea relativă, arată cum se
poate ajunge de la directorul curent la fişierul căutat.
În clasa File, reprezentarea căii se face sub o formă independentă de platformă. În acest scop,
în instanţele clasei File, calea se păstrează sub forma unui tablou de şiruri de caractere, în
care se memoreaza numele fiecărui director conţinut în cale. Separatorul se păstrează într-un
câmp separat şi este setat automat în funcţie de sistemul de operare al calculatorului pe care
se execută programul. În acest fel, portabilitatea programului creşte, întrucât nu trebuie
modificate căile fişierelor folosite în program atunci când se trece de pe o platformă pe alta.
ATENŢIE: pentru asigurarea portabilităţii programelor sursă, este recomandabil ca, în căile
date ca argumente ale metodelor clasei File, să se folosească numai varianta Unix de
separatori. Compilatorul javac de pe platformele Microsoft înlocuieşte automat separatorul '/'
prin ' \\' in timp ce pe platformele Unix (Linux) nu se face înlocuirea inversă.
317
Severin Bumbaru
Constructori:
public File(String pathname) - creează o instanţă a clasei File, care conţine calea
dată ca argument;
public File(String parent, String child) - creează o instanţă a clasei File, în
care calea este compusă dintr-o cale "părinte" (formată numai din directoare) şi o cale "copil"
(care poate fi director sau fişier);
public File(File parent, String child) - se deosebeşte de constructorul
precedent numai prin faptul că "părintele" este o instanţă a clasei File.
Metode:
Specificăm aici principalele metode ale clasei File.
public String getName() - întoarce numele fişierului (ultimul nume din cale); dacă
această cale este vidă, întoarce un şir vid;
public String getParent() - întoarce calea părinte;
public File getParentFile() - întoarce calea părinte sub forma de instanţă a
clasei File;
public String getPath() - întoarce calea conţinută în această instanţă; calea va
conţine separatorii impliciţi de pe platforma curentă;
public boolean isAbsolute() - intoarce true dacă această cale este absolută (dacă
este prefixată cu '/' pe platformele Unix sau cu '\\' pe platformele Microsoft);
public String getAbsolutePath() - întoarce calea absolută corespunzătoare căii
conţinute în această instanţă;
public File getAbsoluteFile() - întoarce o instanţă a clasei File care conţine
calea absolută corespunzatoare celei din instanţa curentă;
public URL toURL() - întoarce un URL (Uniform Resource Locator) corespunzător
căii din această instanţă; forma acestui URL este dependentă de sistem (Nota: URL-ul se
foloseşte pentru a localiza un fişier cu ajutorul unui browser de Web);
public boolean canRead() - testează dacă aplicaţia poate citi fişierul indicat de
această instanţă a clasei File;
public boolean canWrite() - testează dacă aplicaţia poate scrie în fişierul indicat;
public boolean exists() - testează dacă fişierul indicat există;
public boolean isDirectory() - testează dacă ultimul nume din această cale este
al unui director;
318
Programarea orientata pe obiecte în limbajul Java
public boolean isFile() - testează dacă ultimul nume din această cale este al unui
fişier;
public long lastModified() - întoarce timpul la care s-a făcut ultima modificare
în fişierul indicat de această cale (exprimat în milisecunde de la 1 ianuarie 1970 ora 00:00:00
GMT);
public long length() - întoarce lungimea fişierului indicat de această instanţă (în
octeţi);
public boolean createNewFile() throws IOException - dacă fişierul indicat în
această cale nu există, creează un fişier nou vid;
public boolean delete() - şterge fişierul; întoarce truedacă ştergerea a avut loc;
public void deleteOnExit() - cere ca fişierul să fie şters când se încheie
funcţionarea maşinii virtuale Java (are efect numai dacă funcţionarea se încheie normal);
public String[] list() - întoarce un tablou de şiruri, care conţine numele de
directoare şi de fişier din această cale;
public File[] listFile() - dacă această cale nu se încheie cu un nume de director,
întoarce null; altfel întoarce un tablou de instanţe ale clasei File, câte una pentru fiecare nume
de fişier din directorul indicat de această cale.
Exemplu:
În fişierul TestFile.java este dat un exemplu de aplicaţie, în care se testează utilizarea
metodelor clasei File. Metodele se aplică pentru un fişier situat în directorul curent, pentru un
director şi pentru un fişier situat în alt director.
Citirea fişierelor
Citirea fluxurilor de octeţi: clasa FileInputStream
Clasa java.io.FileInputStream permite citirea datelor din fişiere sub forma de fluxuri de
octeţi. Orice instanţă a acestei clase este un flux de intrare, care are ca sursă un fişier. La
crearea acestei instanţe se caută şi se deschide fişierul indicat ca argument al constructorului.
Dacă fişierul nu există, sau nu poate fi deschis pentru citire, se generează o excepţie.
319
Severin Bumbaru
Constructori:
public FileInputStream(String name) throws FileNotFoundException - se
creează un flux de intrare de octeţi având ca sursă fişierul name; (name este numele fişierului;
dacă acesta nu se află în directorul curent, atunci şirul name conţine şi calea);
public FileInputStream(File file) throws FileNotFoundException - se
creează un flux de intrare de octeţi având ca sursă fişierul indicat de instanţa file a clasei File;
public FileInputStream(FileDescriptor fdObj) - se creează un nou flux de
intrare de octeţi, care este conectat la fluxul de intrare fdObj deja existent.
Metode:
public int read() throws IOException - citeşte din fişier un singur octet; dacă
octetul nu este înca disponibil în fluxul de intrare, programul intră în aşteptare;
public int read(byte[] b) throws IOException - se citesc din fişier cel mult
b.length octeţi, care se pun în tabloul b; întoarce numărul de octeţi citit efectiv, sau zero dacă
s-a intâlnit sfârşitul de fisier fără să se citească nici un octet; dacă în fluxul de intrare nu mai
sunt octeţi de citit, dar nu s-a întâlnit sfârşitul de fişier, programul intră în aşteptare;
public int read(byte[] b, int off, int len) throws IOException - se
citesc din fluxul de intrare cel mult len octeţi, care se pun în tabloul b începând de la poziţia
off (offset); întoarce numărul de octeţi citit efectiv; dacă s-a întâlnit sfârşitul de fişier fără să
se citească nici un octet, intoarce -1; dacă nu s-a întâlnit sfârşitul de fişier şi nu sunt octeţi
disponibili pentru citire în fluxul de intrare, programul intră în aşteptare;
public long skip(long n) throws IOException - se sare peste n octeţi din
fişierul de intrare; se întoarce numărul de octeţi săriţi efectiv;
public int available() throws IOException - întoarce numărul de octeţi
disponibili pentru citire din fluxul de intrare;
public void close() throws IOException - închide fişierul;
public final FileDescriptor getFD() throws IOException - întoarce un
descriptor al acestui fişier.
NOTA: descriptorii de fişiere sunt obiecte care aparţin clasei java.io.FileDescriptor şi pot fi
folosiţi ca argument al constructorului pentru a crea un nou flux conectat la un fişier deja
deschis. Clasa FileDescriptor conţine, de asemenea, câmpurile statice in, out şi err, care sunt
descriptori ai fluxurilor de intrare/ieşire standard. În consecinţă, dacă se foloseşte ca
argument al constructorului clasei FileInputStream obiectul FileDescriptor.in, se creaza un
flux care citeşte datele de la tastatura.
Exemplu:
În fişierul TestFileInput.java este un exemplu de aplicaţie, în care se testeaza metodele clasei
FileInputStream. În aplicaţie se deschide fişierul f1, prin care se citesc octeţi dintr-un fişier de
text, al cărui nume se dă ca parametru în linia de comandă, după care octeţii respectivi se
afişează. Întrucât se ştie că f1 este un fişier de text, octeţii sunt afişati convertiţi în caractere.
Se deschide apoi un al doilea flux, f2, care este echivalent cu f1, deoarece au acelaşi
descriptor. În consecinţă, în continuare fluxurile f1 şi f2 citesc date din acelaşi fişier. În final,
se creează un nou flux (cu referinţa f1), care citeşte datele de la tastatura, deoarece la creare s-
320
Programarea orientata pe obiecte în limbajul Java
Constructori:
public FileReader(String fileName) throws FileNotFoundException -
deschide ca flux de caractere de intrare fişierul cu numele fileName (eventual acest şir
cuprinde şi calea);
public FileReader(File file) - throws FileNotFoundException - deschide
ca flux de intrare de caractere fişierul cu calea file;
public FileReader(FileDescriptor fd) - deschide un nou flux de intrare de
caractere şi îl conectează la fluxul deja existent, al cărui descriptor este fd.
Metode:
Clasa FileReader nu are metode proprii, dar moşteneşte următoarele metode ale clasei
InputStreamReader:
public int read() throws IOException - citeşte din fluxul de intrare un singur
caracter şi-l întoarce convertit în int;
public int read(char[] buf, int off, int len) throws IOException -
citeşte din fluxul de intrare cel mult len caractere, pe care le pune în tabloul buf începând de
la poziţia off (offset);
public boolean ready() throws IOException - verifică dacă fluxul de intrare
este gata pentru citire (dacă conţine caractere care pot fi citite);
public void close() throws IOException - închide fluxul de intrare;
public String getEncoding() - întoarce numele canonic al codului folosit pentru
caractere.
Exemplu:
În fişierul TestFileReader.java este dat un exemplu de aplicaţie în care se testează metodele
clasei FileReader. Fişierul care se citeşte poate fi acelaşi ca şi în cazul citirii cu un flux de
octeţi (din clasa FileInputStream). În această aplicaţie, citirea fişierului se face de trei ori,
folosind diverse metode.
321
Severin Bumbaru
Scrierea în fişiere
Clasa FileOutputStream
Fiecare instanţă a clasei java.io.FileOutputStream este un flux de octeţi de ieşire conectat la
un fişier, în care se sriu octeţii primiţi din flux. Fluxul se poate conecta şi la un flux de octeti
de ieşire deja existent.
Metode:
Exemplu:
În fişierul TestFileOutput.java se dă un exemplu de aplicaţie, în care se testează metodele
clasei FileOutputStream. Se deschide un fişier cu numele "ProbaScriere.txt" (dacă nu există,
se creează unul nou). În fişier se scrie textul dat în linia de comandă. În acest scop, lansarea
în execuţie se face sub forma
java TestFileOutput text_de_scris_în_fişier
După ce s-a scris textul, fişierul este închis, apoi este redeschis pentru citire şi conţinutul este
afişat pe ecran. Se închide şi se redeschide iarăşi, dar de data aceasta pentru scriere "în coada"
conţinutului existent. Se scriu cuvintele "Text adăugat", după care se închide, se redeschide
pentru citire şi se afişează. Metoda de scriere în fişier folosită de fiecare dată este cea în care
322
Programarea orientata pe obiecte în limbajul Java
se scrie un tablou de octeţi. Pentru afişare pe ecran, a doua oară s-a folosit tot un flux creat ca
instanţă a clasei FileOutputStream, dar care a fost conectat la fluxul standard de iesire
java.ioFileDescriptor.out, fiind astfel tratat acest flux ca un fişier.
Clasa FileWriter
Scrierea într-un fişier de text se poate face, de asemenea, folosind clasa FileWriter. Instanţele
acestei clase sunt fluxuri de ieşire de caractere, prin care se face scrierea într-un fişier. Clasa
FileWriter este derivată din java.io.OutputStreamWriter şi foloseşte metodele acesteia.
Constructori:
public FileWriter(String fileName) throws IOException - deschide fişierul
fileName pentru scriere şi creează un flux de caractere de ieşire conectat la acest fişier; dacă
fişierul nu există, îl creează;
public FileWriter(String fileName, boolean append) throws IOException
- la fel ca în cazul constructorului precedent, dar, dacă al doilea parametru este true,
scrierea în fişier se va face în coada celui deja existent;
public FileWriter(File file) throws IOException - deschide pentru scriere
fişierul indicat prin calea file şi creează un flux de caractere de ieşire conectat la acest fişier;
public FileWriter(FileDescriptor fd) - creează un flux de caractere de ieşire
şi îl conectează la fluxul deja existent, cu descriptorul fd;
Metode:
Metodele clasei FileWriter sunt moştenite de la clasa OutputStreamWriter.
Exemplu:
În fişierul TestFileWriter.java este dat un exemplu similar cu cel din fişierul
TestFileOutput.java, în care în locul claselor FileInputStream şi FileOutputStream s-au folosit
clasele FileReader şi, respectiv, FileWriter.
323
Severin Bumbaru
Clasa RandomAccessFile
Clasa java.io.RandomAccessFile este derivată direct din clasa Object, deci nu face parte
din niciuna din cele patru ierarhii de fluxuri de intrare/ieşire prezentate anterior, deşi face
parte, ca şi acestea, din pachetul java.io.
Fişierul cu acces direct este privit aici ca un tablou de octeţi memorat într-un sistem de fişiere
(de regulă în memoria externă). Există un cursor al fişierului (numit în engleză File Pointer),
care se comportă ca un indice al acestui tablou. Valoarea acestui indice (poziţia cursorului de
fişier relativ la începutul acestuia) poate fi "citită" cu metoda getFilePointer() şi poate fi
modificată cu metoda seek(). Orice citire sau scriere se face începând de la poziţia pe care
se găseşte acest cursor. La sfârşitul operaţiei, cursorul se deplasează pe o distanţă
corespunzatoare cu numarul de octeţi care au fost citiţi sau scrişi efectiv.
Fişierul cu acces direct poate fi deschis în modul read (numai pentru citire), write (numai
pentru scriere) sau read/write. În ultimul caz, este posibil să se efectueze atât citiri , cât şi
scrieri.
Scrierea în fişierele cu acces direct se poate face atât în mod text (succesiunea de octeţi
fiind privită ca o succesiune de caractere), cât şi în mod binar, la fel ca în fişierele de date, în
care scrierea s-a făcut folosind un DataOutputStream. În consecinţă, clasa RandomAccessFile
oferă atât metode de scriere şi de citire de octeţi, cât şi metode de scriere şi de citire pentru
toate tipurile de date primitive şi pentru şiruri de caractere, la fel ca în cazul claselor
DataOutputStream şi DataInputStream. Este evident că, la citirea din fişier, trebuie să se
respecte modul în care acesta a fost scris.
Dacă la citire se întâlneşte sfârşitul de fişier, se generează o excepţie de sfârşit de fişier din
clasa EOFException (End of File Exception), care este derivată din IOException. În toate
celelalte cazuri când nu se poate efectua o operaţie de citire sau de scriere se genereaza o
IOException (Input-Output Exception).
Constructori
public RandomAccessFile(String file, String mode) throws
FileNotFoundException - deschide fişierul cu acces direct cu numele file în modul de
exploatare mode. Modul poate fi "r" (numai citire) sau "rw" (citire si scriere); dacă este "rw"
324
Programarea orientata pe obiecte în limbajul Java
Metode
public final FileDescriptor getFD() throws IOException - întoarce
descriptorul de fişier;
public int read() throws IOException - citeşte din fişier un singur octet (sau
întoarce -1 dacă s-a atins sfârşitul fişierului); dacă în fluxul de intrare nu este disponibil nici
un octet, se intră în aşteptare;
public int read(byte[] b) throws IOException - citeşte maximum b.length
octeţi pe care îi pune în tabloul b; întoarce numărul de octeţi citit efectiv;
public int read(byte[] b, int offset, int length)throws IOException -
citeşte cel mult length octeţi pe care îi pune în tabloul b începând de la poziţia offset; întoarce
numărul de octeţi cititi efectiv sau -1;
public final void readFully(byte[] b) throws IOException - citeşte exact
b.length octeţi, pe care îi pune în tabloul b; se blochează dacă nu are sufucienţi octeţi, iar la
întâlnirea sfârşitului de fişier generează o EOFException;
public final void readFully(byte[] b, int offset, int length) throws
IOException - citeşte exact length octeţi, pe care îi depune în tabloul b începând de la
poziţia offset;
public int skipBytes(int n) - throws IOException - încearca să sară peste n
octeţi; întoarce numărul de octeţi săriţi efectiv;
public void write(int n) throws IOException - scrie în fişier un singur octet;
public void write(byte[] b) throws IOException - scrie în fişier conţinutul
tabloului b;
public void write(byte[] b, int offset, int length)throws IOException -
scrie length octeţi din tabloul b începând cu cel de pe poziţia offset;
public long getFilePointer() throws IOException - întoarce poziţia
cursorului de fişier;
public void seek(long pos) throws IOException - deplasează cursorul de fişier
pe poziţia pos;
public long length() throws IOException - întoarce lungimea fişierului
(exprimată în octeti);
public void setLength(long newLength) throws IOException - setează noua
lungime a fişierului;
public void close() throws IOException - închide fişierul;
public final boolean readBoolean() throws IOException - citeşte o valoare
booleană (un octet);
public final byte readByte() throws IOException - citeşte o valoare primitivă
de tip byte;
public final int readUnsignedByte() throws IOException - citeşte un singur
octet, pe care îl interpretează ca număr întreg fără semn (în intervalul 0 .. 255);
public final short readShort() throws IOException - citeşte un întreg scurt
(pe doi octeţi);
public final int readUnsignedShort() throws IOException - citeşte doi
octeţi, pe care îi interpretează ca un numar întreg scurt fără semn;
public final char readChar() throws IOException - citeşte doi octeţi, pe care
îi interpretează ca un caracter Unicode;
325
Severin Bumbaru
public final int readInt() throws IOException - citeşte un int (pe patru
octeţi);
public final long readLong() throws IOException - citeşte un long (pe opt
octeţi);
public final float readFloat() throws IOException - citeşte un float (pe
patru octeţi);
public final double readDouble()throws IOException - citeşte un double (pe
opt octeţi);
public final String readLine() throws IOException - citeşte din fişier o linie
de text, adică un şir de caractere încheiat prin marcajul de sfârşit de linie ('\n' pe platforme
Unix sau '\r' '\n' pe platforme Microsoft);
public final String readUTF() throws IOException - citeşte un şir de caractere
în format UTF-8;
public final void writeBoolean(boolean b) throws IOException - scrie o
valoare booleană (pe un octet);
public final void writeByte(byte v) throws IOException - scrie o valoare de
tip byte;
public final void writeShort(short v) throws IOException - scrie un short
(pe doi octeţi);
public final void writeChar(char v) throws IOException - scrie un caracter
Unicode (pe doi octeţi);
public final void writeInt(int v) throws IOException - scrie un int (pe
patru octeţi);
public final void writeLong(long v) throws IOException - scrie un long (pe
opt octeţi);
public final void writeFloat(float v) throws IOException - scrie un float
(pe patru octeţi);
public final void writeDouble(double v) throws IOException - scrie un
double (pe opt octeţi);
public final void writeBytes(String s) throws IOException - scrie un şir de
caractere convertit în secvenţă de octeţi (fiecare caracter este reprezentat pe un octet,
eliminându-se octetul superior al codului caracterului);
public final void writeChars(String s) throws IOException - scrie un şir de
caractere Unicode;
public final void writeUTF(String s) throws IOException - scrie un şir de
caractere în format UTF-8.
Exemplu
În fişierul AccesDirect.java este dat un exemplu de aplicaţie, în care se creează şi se
utilizează un fişier cu acces direct, cu numele "Proba.fad". Prima dată fişierul este deschis în
mod "rw" (read/write). Se fac două serii de scrieri de date, care apoi sunt citite şi afişate.
Înainte de fiecare citire a unei serii de date se poziţionează cursorul de fişier la începutul
seriei respective, iar datele se citesc respectând modul în care acestea au fost scrise. Fişierul
este apoi închis şi redeschis în mod "r" ("read"), după care se poziţionează cursorul la
începutul celei de a doua serii de date, care este citită şi afişată.
326
Programarea orientata pe obiecte în limbajul Java
Fluxuri de prelucrare
Fluxurile de prelucrare se conectează la alte fluxuri de intrare/ieşire pentru a face anumite
transformări asupra datelor din fluxul respectiv. Clasele de fluxuri de prelucrare din Java API
au fost prezentate pe scurt la descrierea pachetului java.io. În această secţiune ne vom ocupa
de două categorii de fluxuri de prelucrare: fluxurile de date şi fluxurile de obiecte.
Fluxuri de date
În unele aplicaţii se doreşte să se transmită într-un flux de ieşire sau să se recepţioneze dintr-
un flux de intrare date primitive reprezentate binar (de tip boolean, char, byte, short, int, long,
float sau double) şi şiruri de caractere. În acest scop, pot fi folosite clasele
java.io.DataOutputStream şi java.io.DataInputStream.Se pot citi cu un
DataInputStream numai date care au fost scrise cu un DataOutputStream.
Clasa DataOutputStream
Clasa java.io.DataOutputStream este derivată din clasa java.io.FilterOutputStream şi
implementează interfaţa java.io.DataOutput. Ea permite să se transmită date primitive către
un flux de ieşire într-o formă portabilă (independentă de platformă), cu condiţia ca aceste
date să fie citite ulterior printr-un DataInputStream.
Constructor:
public DataOutputStream(OutputStream out) - creează un nou flux de ieşire de
date, conectat la ieşire la un OutputStream (adică la o instanţă a unei clase derivate din clasa
OutputStream).
Metode:
Aceasta clasă conţine metodele clasei java.io.OutputStream, la care se adaugă următoarele
metode specifice, destinate scrierii în fluxul de ieşire a datelor primitive sau a unor şiruri de
caractere:
327
Severin Bumbaru
int pe 4 octeţi;
public final void writeLong(long v) throws IOException - scrie o valoare de
tip long pe 8 octeţi;
public final void writeFloat(float v) throws IOException - scrie o valoare
de tip float pe 4 octeţi;
public final void writeDouble(double v) throws IOException - scrie o
valoare de tip double pe opt octeţi;
public final void writeBytes(String v) throws IOException - scrie un şir de
caractere, convertit într-un şir de octeţi prin suprimarea octetului superior al fiecărui caracter;
public final void writeChars(String v) throws IOException - scrie în flux un
şir de caractere (în Unicode);
public final void writeUTF(String v) throws IOException - scrie un şir de
caractere codificat în formatul UTF-8 într-o formă independentă de maşină (se scrie mai întâi
lungimea şirului pe doi octeţi, apoi fiecare caracter din şir pe câte un singur octet);
public final int size() - întoarce numărul de octeţi scrişi în acest flux de ieşire.
Clasa DataInputStream
Un DataInputStream este un flux de intrare de octeţi care este conectat la intrare la un alt
InputStream şi citeste din acesta date primitive într-o formă independentă de platformă. Se
pot citi cu DataInputStream numai date care au fost scrise cu DataOutputStream.
Constructor:
Metode:
Aceasta clasă conţine toate metodele clasei java.io.InputStream, la care se adaugă
următoarele metode specifice, prin care se implementează interfaţa java.io.DataInput:
328
Programarea orientata pe obiecte în limbajul Java
un singur octet, pe care îl tratează ca un număr întreg cu semn în intervalul [-128, 127]; se
consideră că a fost scris cu writeByte(byte v);
public final int readUnsignedByte() throws IOException - citeşte din fluxul
de intrare un singur octet, pe care îl tratează ca număr întreg fără semn în intervalul [0, 255]
şi îl întoarce sub formă de int;
public final short readShort() throws IOException - citeşte doi octeţi din
fluxul de intrare şi îi interpretează ca un număr de tip short, scris anterior cu writeShort(short
v);
public final int readUnsignedShort() throws IOException - citeşte din fluxul
de intrare doi octeţi, pe care îi interpretează ca număr întreg fără semn şi-l întoarce sub formă
de int;
public final char readChar() throws IOException - citeşte din fluxul de intrare
un char (doi octeţi, format Unicode);
public final int readInt() throws IOException - citeşte patru octeţi si îi
interpretează ca un număr de tip int scris cu writeInt(int v);
public final long readLong() throws IOException - citeste opt octeţi si îi
interpretează ca un număr de tip long, scris cu writeLong(long v);
public final float readFloat() throws IOException - citeşte patru octeţi si îi
interpretează ca un număr de tip float, scris cu writeFloat(float v);
public final double readDouble() throws IOException - citeşte opt octeţi si îi
interpretează ca un număr de tip double, scris cu writeDouble(double v);
public final String readUTF() throws IOException - citeşte un şir de caractere
în format UTF (care a fost scris cu writeUTF(String str));
Exemplu
În fişierul FisierDate.java se dă un exemplu de aplicaţie, în care se creează un fişier de date şi
se scriu date în acest fişier folosind un DataOutputStream legat la un FileOutpusStream.
După ce au fost scrise datele, se închide fişierul pentru scriere şi se redeschide pentru citire,
după care se citesc datele în aceeaşi ordine în care au fost scrise. Datele scrise în fişier şi cele
citite sunt afişate pe ecran pe perechi, pentru comparaţie.
Fluxuri de obiecte
Fluxurile de obiecte sunt fluxuri de prelucrare care permit să se transmită obiecte. În acest
scop, obiectele transmise trebuie să fie serializate, înainte de a fi transmise, adică să fie puse
sub forma unei serii de octeti, într-un format care să permită la destinaţie reconstituirea în
memorie a obiectului respectiv. Astfel de obiecte se numesc serializabile.
Interfaţa Serializable
Orice obiect care se transmite pe un flux trebuie să aparţină unei clase care implementează
interfaţa java.io.Serializable. Aceasta inseamnă că trebuie îndeplinite următoarele condiţii:
329
Severin Bumbaru
Dacă, la transmiterea obiectelor pe flux, se constată că unul din acestea nu este serializabil, se
generează excepţia NotSerializableException.
Clasele care necesită o manipulare specială la serializare sau deserializare trebuie să conţina
metode cu signaturile următoare:
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
O prezentare mai amplă a acestei interfeţe se găseşte în documentaţia Java API.
Clasa ObjectOutputStream
Clasa ObjectInputStream
330
Programarea orientata pe obiecte în limbajul Java
Întrebări
Nivel 1
1. Ce este un stream?
2. Ce deosebire este între fluxurile de ieşire şi cele de intrare?
3. Care sunt etapele de utilizare a unui flux de iesire?
4. Care sunt etapele de utilizare a unui flux de intrare?
5. Ce conţine pachetul java.io?
6. Ce deosebire este între fluxurile de caractere şi cele de octeţi?
7. Care sunt rădăcinile ierarhiilor de clase pentru fluxuri de caractere?
8. Care sunt rădăcinile ierarhiilor de clase pentru fluxuri de octeţi?
9. Ce sunt fluxurile de prelucrare?
10. Ce este un fişier?
11. Care sunt clasele folosite pentru citirea fişierelor?
12. Care sunt clasele folosite pentru scrierea fişierelor?
13. Cum se deschide un fişier?
14. Cum se închide un fişier?
15. Ce clase se folosesc pentru a scrie date într-un fişier de octeţi şi pentru a citi date
331
Severin Bumbaru
Nivel 2
1. Dece fluxul este un obiect abstract?
2. Clasele Reader şi Writer sunt abstracte sau instanţiabile?
3. Ce se înţelege prin marcarea unui flux şi prin ce metodă se realizează?
4. Cum se poate reveni într-un flux la marcajul făcut anterior?
5. Clasele InputStream şi OutputStream sunt sau nu instanţiabile?
6. În ce situaţii este obligatorie folosirea fluxurilor de octeţi?
7. Ce clasă se foloseşte pentru a scrie date într-un fişier de caractere?
8. Ce clasă se foloseşte pentru a citi date dintr-un fişier de caractere?
9. Prin ce metode se scriu datele primitive într-un fişier cu acces direct?
10. Prin ce metode se citesc datele primitive dintr-un fişier cu acces direct?
11. Prin ce metode se scriu şirurile de caractere într-un fişier cu acces direct?
12. Ce este UTF?
13. Pot fi fluxurile de date conectate direct la un fişier?
14. La ce fluxuri se conectează fluxurile de date?
15. Ce se întamplă dacă se încearcă introducerea într-un flux de obiecte a unui obiect
neserializabil? Când se constată anomalia: la compilare sau la execuţie?
16. Un flux de obiecte poate conţine şi date primitive? dece?
17. Pot fi puse tablourile într-un flux de obiecte?
332
Programarea orientata pe obiecte în limbajul Java
Fire de execuţie
Conceptul de proces. Procese paralele şi 333
concurente
Fire de execuţie 335
Clasa Thread 335
Interfaţa Runnable 346
Sincronizarea firelor de execuţie 348
Întrebări. 354
Esenţial pentru un proces este caracterul său temporal: fiecare transformare elementară
necesită un anumit timp, iar procesul în ansamblu este o succesiune de astfel de transformări
elementare. Termenul este folosit în domenii diferite: procese fizice, procese chimice,
procese tehnologice, procese biologice, procese economice, procese sociale, procese juridice
etc.
Este posibil ca mai multe procese să se desfăşoare simultan, adică intervalele de timp
corespunzătoare acestor procese să se suprapună total sau parţial. Astfel de procese pot fi
paralele sau concurente.
333
Severin Bumbaru
Dacă procesele de calcul care au loc simultan folosesc resurse diferite (sunt executate de
procesoare diferite, folosesc zone de memorie internă şi externă diferite etc.), ele se numesc
procese paralele.
Dacă procesele care se desfăşoară în acelaşi interval de timp folosesc anumite resurse
comune, ele se numesc procese concurente. Cel mai frecvent se întâlneşte situaţia, în care
mai multe procese folosesc în comun acelaşi procesor. Întrucât, la un moment dat, procesorul
nu poate efectua decât o singură operaţie, înseamnă că este necesar să se adopte o anumită
strategie, conform căreia timpul de funcţionare al procesorului este partajat între diferitele
procese concurente. În principiu, aceste strategii se împart în două categorii:
1. Se acordă fiecăruia dintre procesele concurente un anumit nivel de prioritate. Dacă mai
multe procese solicită simultan aceeaşi resursă, ea este alocată procesului cu prioritate
superioară.
Conform acestei strategii, dacă, în timpul derularii unui proces A, procesorul este solicitat
de un alt proces B de prioritate superioară, procesul A este întrerupt, trecându-se la
executarea procesului B. După încheierea executării procesului prioritar B, se revine la
procesul A, care este continuat din starea în care a fost întrerupt. Este însa posibil ca, în timp
ce se deruleaza procesul B, procesorul sa fie solicitat de un alt proces C, de prioritate şi mai
înalta. În acest caz, şi procesul B va fi întrerupt, trecându-se la executarea procesului C şi
aşa mai departe.
Cele două strategii menţionate aici pot fi aplicate în diferite variante şi pot fi combinate în
diferite moduri. Cel mai frecvent, partajarea timpului este aplicată numai pentru procesele cu
acelaşi nivel de prioritate.
Pentru a pune în evidenţă distincţia între program şi proces, putem menţiona că acelaşi
program poate da naştere la procese diferite: dacă programul conţine ramificaţii şi/sau cicluri,
parcurgerea efectivă a acestora (ce ramuri vor fi executate efectiv şi de câte ori se vor
parcurge ciclurile) depinde de datele de intrare. În cadrul multitaskingului, este chiar posibil
ca doi utilizatori să utilizeze simultan acelaşi program folosind date diferite, astfel încât se
generează procese diferite.
334
Programarea orientata pe obiecte în limbajul Java
Fire de execuţie
Firul de execuţie (în engleză: Thread) este, în esenţă, un subproces strict secvenţial.
Menţinând definiţia procesului ca un program în curs de execuţie, putem considera acum că
procesul este format din mai multe fire de execuţie care se derulează în paralel, concurând la
utilizarea resurselor alocate procesului respectiv.
În limbajul Java, există posibilitatea de a se crea programe care conţin mai multe fire de
execuţie. Aceasta înseamnă că, la executarea lor, se vor crea mai multe subprocese care se
vor desfăşura simultan, folosind acelaşi procesor şi aceeaşi zonă de memorie. Acest mod de
funcţionare se numeşte în engleză multithreading.
Chiar şi în cazul programelor Java în care nu sunt prevăzute explicit mai multe fire de
execuţie, în timpul executării lor coexistă cel puţin două astfel de fire: cel al aplicaţiei
propriu-zise şi cel al colectorului de reziduuri (garbage collector). Colectorul de reziduuri
este un fir de execuţie cu nivel de prioritate coborât, care funcţionează atunci când apar
întreruperi în desfăşurarea aplicaţiei propriu-zise (de exemplu, când se aşteaptă efectuarea
unor operaţii de intrare/ieşire). În consecinţă, maşina virtuală Java are intrinsec o funcţionare
de tip multithreading.
Clasa Thread
Clasa java.lang.Thread realizează un fir de execuţie. Acesta poate fi un fir de execuţie
obişnuit, sau un demon.
Demonul (engleză: daemon thread) este un fir de execuţie de prioritate coborâtă, care nu este
invocat în mod explicit. El stă "adormit" şi intră automat în execuţie atunci când sunt
îndeplinite anumite condiţii.
Un exemplu tipic de demon este colectorul de reziduuri. După cum ştim deja, acesta este un
fir de execuţie de prioritate coborâtă, care intră în funcţiune atunci când în memorie apar
obiecte către care nu mai există referinţe.
Crearea unui fir de execuţie se face invocând unul dintre constructorii clasei Thread. Dintre
aceştia, îi menţionâm aici pe următorii:
public Thread() - creează un nou fir de execuţie cu numele implicit Thread-n, unde n
este un număr de ordine;
public Thread(String name) - creează un nou fir de execuţie cu numele name;
public Thread(Runnable target) - creează un nou fir de execuţie, care conţine
obiectul target cu interfaţa Runnable, iar numele firului este cel implicit;
335
Severin Bumbaru
Cele mai frecvent utilizate metode ale clasei Thread sunt run(), start() şi sleep(). Iată o
listă a principalelor metode ale acestei clase:
public void run() - conţine programul care trebuie executat de firul respectiv; Aşa
cum este ea în clasa Thread, metoda nu face nimic (are corpul vid). Această metodă trebuie
redefinită fie prin crearea unei subclase a clasei Thread, fie prin crearea unei obiect cu
interfaţa Runnable, care să fie înglobat în acest Thread (folosind constructorul
Thread(Runnable target));
public void start() - pune firul nou creat în starea "gata pentru execuţie";
public static void sleep(long millis) throws InterruptedException -
opreşte temporar execuţia acestui fir, pentru o durata de millis milisecunde;
public static void yield() - opreşte temporar execuţia acestui fir, permiţând
monitorului să dea controlul altui fir de aceeaşi prioritate;
public static Thread currentThread() - întoarce o referinţă la firul care este
executat în momentul curent;
public final void setPriority(int newPriority) - seteaza prioritatea firului de
execuţie; prioritatea trebuie sa fie cuprinsă în intervalul [Thread.MIN_PRIORITY,
Thread.MAX_PRIORITY] (valorile lor fiind, respectiv, 1 şi 10). Nerespectarea acestui
interval genereaza o IllegalArgumentException. Daca nu este stabilită explicit, prioritatea
este Thread.NORM_PRIORITY (are valoarea 5).
public final int getPriority() - întoarce prioritatea curentă a firului de execuţie.
public final void setName(String name) - pune firului de execuţie un nou nume;
public final String getName() - întoarce numele firului de execuţie;
public final void setDaemon(boolean on) - dă firului de execuţie calitatea de
demon (dacă argumentul este true) sau de fir obişnuit (dacă argumentul este false); Metoda
poate fi invocată numai dacă firul nu este activ, altfel se generează o
IllegalThreadStateException.
public final boolean isDaemon() - testează dacă firul de execuţie este demon.
Pentru redefinirea metodei Run este necesar sa creem o subclasă a clasei Thread sau sa
creem o clasă cu interfaţa Runnable, şi să dăm o instanţă a acestei clase ca argument
constructorului clasei Thread.
După ce firul de execuţie a fost creat (de exemplu prin expresia new Thread()), el există în
memorie, dar înca nu poate fi executat. Se găseşte, deci, în starea "nou creat" (engleza:
born). Pentru a-l face gata de execuţie, pentru firul respectiv este invocată metoda start().
După invocarea metodei start(), firul este executabil, dar aceasta nu înseamnă că el este
336
Programarea orientata pe obiecte în limbajul Java
pus în execuţie imediat: noul fir devine concurent cu cele deja existente (în primul rând cu cel
care l-a creat), şi va fi pus în execuţie în mod efectiv (i se va acorda acces la procesor) atunci
când îi vine rândul, conform cu strategia de alocare a resurselor adoptată.
Un fir în curs de execuţie poate fi oprit temporar ("adormit") invocând metoda statică
sleep(long millis). Argumentul acestei metode este intervalul de timp cât procesul se va
găsi în stare de "somn", exprimat în milisecunde.
Fiecărui fir de execuţie i se asociază o anumită prioritate. Aceasta este un numar întreg,
cuprins între valorile Thread.MIN_PRIORITY şi Thread.MAX_PRIORITY (având valorile 1 şi
respectiv 10). Dacă metoda setPriority(int newPriority) nu este invocată explicit, firului i se
dă prioritatea implicita Thread.NORM_PRIORITY, respectiv 5.
Daca firul de execuţie solicită o operaţie de intrare/ieşire, el rămîne blocat pe întreaga durată
a executării operaţiei respective.
În timpul în care firul de execuţie "doarme" (ca urmare a invocării metodei sleep()) sau este
blocat (deoarece asteapta terminarea unei operatii de intrare/iesire), se pot executa alte fire, de
prioritate egală sau inferioară. În schimb, firele de prioritate superioară pot întrerupe în orice
moment executarea celor de prioritate inferioară.
Un proces poate fi pus, de asemenea, în aşteptare prin invocarea metodei wait(). În acest
caz, firul aşteaptă confirmarea efectuării unei anumite acţiuni, prin invocarea metodei
notify() sau notifyAll(). Aceste trei metode există în clasa Object şi servesc la
sincronizarea firelor de execuţie.
Având în vedere cele de mai sus, punem în evidenţă urmatoarele stări în care se poate găsi un
fir de execuţie pe durata ciclului său de viaţă:
- nou creat (engleză: born) - este starea în care se găseşte imediat ce a fost creat prin
operatorul new; în această stare, firul nu poate fi executat;
- gata de execuţie (engleză: ready) - este starea în care firul poate fi pus în execuţie;
punerea efectivă în execuţie se face de către maşina virtuala Java atunci când procesorul este
liber şi nu sunt gata de execuţie fire de prioritate superioară;
- în execuţie (engleză: running) - este starea în care procesul se execută efectiv (ocupă
procesorul);
- adormit (engleză: sleeping) - este starea de oprire temporară, ca urmare a invocării
metodei sleep();
- blocat (engleză: blocked) - este starea în care aşteaptă incheierea unei operaţii de
intrare/ieşire;
- în aşteptare (engleză: waiting) - este starea în care firul se găseşte din momentul în care
337
Severin Bumbaru
se invocă metoda wait(), până când primeşte o confirmare dată prin invocarea metodei
notify();
- mort (engleză: dead) - este starea în care intră firul de execuţie dupa ce s-a încheiat
executarea metodei run().
Relaţiile dintre aceste stări sunt reprezentate grafic în figura de mai jos.
Denumirile stărilor firului de execuţie au fost date în limba engleză, pentru a putea fi urmarită
mai uşor documentaţia originală din Java API.
Trecerea de la starea Born la starea Ready (gata) se face prin invocarea metodei start().
Trecerea de la starea Ready la starea Running (în execuţie) se face de către maşina virtuală
Java (de către dispecerul firelor) atunci când sunt create condiţiile necesare: procesorul este
liber şi nici un fir de execuţie de prioritate superioară nu se găseşte în starea Ready.
Trecerea de la starea Running la starea Ready se face la executarea metodei yield(), sau
atunci când a expirat tranşa de timp alocată procesului (în cazul sistemelor cu diviziune a
timpului).
Trecerea de la starea Sleeping la starea Ready se face când a expirat intervalul de timp de
338
Programarea orientata pe obiecte în limbajul Java
Trecerea de la starea Running la starea Blocked are loc atunci când firul de execuţie
respectiv solicită efectuarea unei operaţii de intrare ieşire.
Trecerea de la starea Blocked la starea Ready are loc când s-a încheiat operaţia de
intrare/ieşire solicitată de acest fir.
Trecerea de la starea Waiting la starea Ready se face când se primeşte o confirmare prin
invocarea metodei notify() sau notifyAll().
Trecerea de la starea Running la starea Dead are loc atunci când se încheie executarea
metodei run() a firului de execuţie respectiv.
Se observă că orice fir de execuţie îşi începe ciclul de viaţă în starea Born şi îl încheie în
starea Dead. Punerea firului în execuţie efectivă (trecerea de la Ready la Running) se face
numai de către dispecerul firelor. În toate cazurile în care se încheie o stare de oprire
temporară a execuţiei (Sleeping, Waiting sau Blocked), firul de execuţie respectiv trece în
starea Ready, aşteptând ca dispecerul să-l pună efectiv în execuţie (să-l treacă în starea
Running). În timpul cât firul de execuţie se găseşte în orice altă stare decât Running,
procesorul calculatorului este liber, putând fi utilizat de alte fire de execuţie sau de alte
procese.
Exemplul 1
Fişierul DouaFireA.java conţine un exemplu de aplicaţie, în care se creează fire de execuţie
folosind o clasă care extinde clasa Thread.
class DouaFireA {
Fir(String numeFir) {
super(numeFir);
System.out.println("S-a creat firul "+getName());
}
339
Severin Bumbaru
Clasa Fir din această aplicaţie extinde clasa Thread, deci instanţele ei sunt fire de execuţie. În
clasa Fir s-a redefinit metoda run(), astfel încât să conţină programul firului respectiv. În
cazul nostru, firul execută un ciclu, în care afişează la terminal un mesaj, conţinând numele
firului şi indicele ciclului executat.
În metoda main() a aplicaţiei se creează două instanţe ale clasei Fir, dându-le, respectiv,
numele Alpha şi Beta, apoi se lansează în execuţie aceste fire. Iată un exemplu de rezultat al
executării acestei aplicaţii:
- În realitate, în afară de firele de execuţie Alpha şi Beta create explicit de către noi, mai
exista incă două fire de execuţie: cel al aplicaţiei propriu-zise (executarea metodei main()) şi
cel al colectorului de reziduuri. Întrucât nu s-au setat explicit priorităţile, firele Alpha, Beta şi
main() au toate prioritatea Thread.NORM_PRIORITY.
340
Programarea orientata pe obiecte în limbajul Java
- Întrucât toate firele au aceeaşi prioritate, durata executării fiecărui fir este mică şi nu s-au
folosit metode de suspendare a execuţiei (yield(), sleep(), wait()), în momentul în
care începe executarea unui fir acesta se execută până la capăt, după care procesorul
sistemului este preluat de firul următor. În consecinţă s-a executat mai întâi metoda main()
până la incheierea ei (confirmată prin mesajul "Sfârşitul metodei main()"), după care se
execută complet firul Alpha şi apoi firul Beta (în ordinea lansării).
- Aplicaţia se încheie în momentul în care s-a încheiat ultimul fir de execuţie.
Exemplul 2
În fişierul DouaFireA1.java se reia aplicaţia din exemplul precedent, dar la sfârşitul fiecărui
ciclu de indice i se invocă metoda yield() care suspendă firul în curs şi permite astfel să se
transmită controlul următorului fir de aceeaşi prioritate.
class DouaFireA1 {
Fir(String numeFir) {
super(numeFir);
System.out.println("S-a creat firul "+getName());
}
341
Severin Bumbaru
Se observă cu uşurinţă că, în acest caz, executarea celor două fire, Alpha şi Beta, are loc
intercalat, deoarece, după fiecare parcurgere a unui ciclu într-un fir, se invocă metoda
yield() , cedându-se procesorul sistemului în favoarea celuilalt fir. Aceasta nu s-ar fi
întâmplat, dacă cele doua fire ar fi avut priorităţi diferite.
Exemplul 3
În fişierul DouaFireB.java s-a reluat aplicaţia din Exemplul 1, aducându-i-se următoarele
modificări:
- în ciclul cu contorul i din metoda run() a clasei Fir s-a mai introdus un ciclu interior (cu
contorul j) care se parcurge de 100000000 ori, pentru a mări în mod artificial durata de
execuţie; în locul acestuia se putea pune, evident, orice alta secvenţa de instrucţiuni cu durata
de execuţie comparabilă;
- cele două cicluri (pe i şi pe j) au fost introduse şi în metoda main(), pentru a putea urmări
executarea acesteia după lansarea firelor Alpha şi Beta.
Fragmentele de program nou adăugate sunt puse în evidenţă în textul de mai jos cu caractere
aldine (îngroşate).
342
Programarea orientata pe obiecte în limbajul Java
class DouaFireB {
Fir(String numeFir) {
super(numeFir);
System.out.println("S-a creat firul "+getName());
}
343
Severin Bumbaru
Constatăm că, întrucat durata de execuţie a fiecărui fir a devenit mult mai mare decât în
exemplul precedent şi toate cele trei fire (Alpha, Beta si main()) au aceeaşi prioritate
(Thread.NORM_PRIORITY), se aplică o strategie de partajare a timpului, astfel încât timpul
de funcţionare al procesorului este alocat sub forma de tranşe succesive fiecăruia dintre
procese. În consecinţă, se creeaza impresia că cele trei procese se desfăşoară în paralel,
simultan în timp.
Exemplul 4
În fişierul DouaFireC.java se reia aplicaţia din exemplul precedent, dar s-a renunţat la
ciclurile interioare (cu contorul j), în schimb după parcurgerea fiecărui ciclu de indice i se
pune firul respectiv in aşteptare timp de 1000 milisecunde prin invocarea metodei sleep.
Această metodă aruncă excepţia InterruptedException care trebuie captată.
În mod similar s-a procedat şi în ciclul din metoda main. Având însă în vedere că metoda
main() nu se găseşte într-o clasa derivata din Thread, la invocarea metodei statice sleep a fost
necesar să se indice clasa căreia îi aparţine, deci invocarea s-a făcut sub forma
Thread.sleep(1000).
class DouaFireC {
Fir(String numeFir) {
super(numeFir);
System.out.println("S-a creat firul "+getName());
}
344
Programarea orientata pe obiecte în limbajul Java
}
}
} /* incheierea clasei Fir */
În timp ce un fir "doarme", se pot executa alte fire, chiar dacă ele sunt de prioritate
inferioară!
345
Severin Bumbaru
Interfaţa Runnable
Necesitatea implementării interfeţei Runnable apare atunci când dorim să creem o clasa de
fire de execuţie care nu extinde clasa Thread. Motivul ar putea fi, de exemplu, cel că clasa
nou creată, B, trebuie sa extindă o alta clasă, A, care nu este descendentă a clasei Thread Se
ştie că în Java nu există moştenire multiplă, deci clasa B nu poate avea ca superclase atât
clasa A, cât şi clasa Thread. În acest caz, vom crea o clasă B care extinde clasa A şi
implementeaza interfaţa Runnable, care conţine metoda run().
Exemplul 1
În fişierul DouaFireAR.java se dă un exemplu de aplicaţie similară cu cea din fişierul
DouaFireA1.java, cu deosebirea că clasa Fir nu extinde clasa Thread, ci implementează
interfaţa Runnable. Fragmentele de program modificate sunt puse în evidenţa în textul sursă
de mai jos prin caractere aldine (ingroşate).
class DouaFireAR {
346
Programarea orientata pe obiecte în limbajul Java
Dacă se compilează şi se execută această aplicaţie, se obţin exact aceleaţi rezultate ca în cazul
când s-a recurs la extinderea clasei Thread.
Exemplul 2
În fişierul Bondari.java este dat un exemplu de aplicaţie, în care se pot crea mai multe fire de
execuţie, fiecare având propria sa interfaţă grafică. Fiecare fir de execuţie este o instanţa a
clasei Bondar, care extinde clasa JFrame şi implementează interfaţa Runnable. Aplicaţia
propriu-zisă (care conţine metoda main()) este clasa Bondari, care extinde şi ea clasa
JFrame.
Clasa Bondar are propria ei interfaţă grafică, în care există două rigle pentru ajustarea
perioadei şi amplitudinii şi o fereastră în care evoluează "bondarul". Acesta este un mic
347
Severin Bumbaru
dreptunghi roşu, a cărui mişcare o simuleaza pe cea a unei insecte în zbor. Amplitudinea, cât
şi perioada deplasărilor "bondaruliui" sunt ajustate cu cele două rigle. Dacă se acţioneaza
butonul de închidere a ferestrei "bondarului" (cel marcat cu X din colţul dreapta-sus), se
încheie ciclul de viaţă al firului de execuţie respectiv. Aceasta are loc, întrucat ciclul din
metoda run() a firului continuă cât timp variabila booleana esteViu are valoarea true.
Când se acţioneaza butonul de închidere a ferestrei "bondarului", evenimentul este captat de
metoda windowClosing() a clasei SfarsitBondar, care pune variabila esteViu a
"bondarului" respectiv la valoarea false.
Dacă se acţionează butonul de închidere a ferestrei principale (cea care conţine butonul
"Creare nou bondar"), evenimentul este captat de metoda windowClosing() a clasei Iesire,
astfel că se execută metoda exit(0). Efectul este încheierea execuţiei tuturor firelor şi
oprirea maşinii virtuale Java.
Când un fir începe executarea uni metode sincronizate a unui monitor, el devine
"proprietarul" monitorului căruia îi aparţine această metodă (engleză: owner) şi deţine această
calitate până la încheierea executării metodei sincronizate respective, sau până când se
autosuspendă invocând metoda wait(), aşa cum vom explica ulterior. Pe toata durata cât un
fir de execuţie este proprietarul unui monitor, nici un alt fir nu poate invoca o metodă
sincronizată a monitorului respectiv. Aşa dar, orice fir care, în acest interval de timp, ar
încerca să invoce o metodă sincronizată a unui monitor al cărui proprietar este alt fir, trebuie
să aştepte până când monitorul respectiv este eliberat de proprietarul existent.
În acest fel, se evită situaţiile în care un fir de execuţie ar interveni să facă modificări asupra
unui obiect, în timp ce acesta se găseşte deja în curs de prelucrare de către un alt fir. De
exemplu, dacă un fir de execuţie efectuează ordonarea datelor dintr-un tablou, nu este corect
ca un alt fir, în acest timp, să modifice datele din acest tablou.
348
Programarea orientata pe obiecte în limbajul Java
Metoda
public final void wait()
throws InterruptedException
şi variantele ei
public final void wait(long timeout)
throws InterruptedException
public final void wait(long timeout, int nanos)
throws InterruptedException
au ca efect trecerea firului de execuţie activ din starea Running în starea Waiting (vezi
schema). Metoda wait() fără argumente face această trecere pentru un timp nedefinit, în
timp ce celelalte două metode primesc ca argument intervalul de timp maxim pentru care se
face suspendarea execuţiei firului.
Metodele
public final void notify()
public final void notifyAll()
au ca efect trecerea firului de execuţie din starea Waiting în starea Ready, astfel încât el
poate fi activat de către dispecer în momentul în care procesorul sistemului devine disponibil.
Deosebirea dintre ele este ca metoda notify() trece în starea Readyun singur fir de
execuţie dintre cele care se gasesc în momentul respectiv în starea Waiting provocată de
acest monitor (care din ele depinde de implementare), în timp ce notifyAll() le trece în
starea Ready pe toate.
class NumeClasaMonitor {
// declaraţii câmpuri de date ale monitorului
boolean variabilaConditie=false;
349
Severin Bumbaru
Dacă firul de execuţie producător a ajuns în starea, în care trebuie să pună în monitor date
noi, el invocă metoda sincronizată puneDate, devenind astfel proprietar (owner) al
monitorului. În această metodă, se verifică, în primul rând, valoarea variabilei
variabilaConditie. Dacă această variabilă are valoarea true, înseamnă că în monitor
există deja date noi, înca nefolosite de consumator. În consecinţă, se invocă metoda wait() şi
firul de execuţie monitor este suspendat, trecând în starea Waiting, astfel că el încetează să
mai fie proprietar (owner) al monitorului. Dacă un alt fir (în cazul de faţă consumatorul)
invocă o metodă sincronizată a aceluiaşi monitor care, la rândul ei, invocă metoda notify()
sau notifyAll(), firul pus în aşteptare anterior trece din starea Waiting în starea Ready şi,
deci, poate fi reactivat de către dispecer. Dacă variabila variabilaConditie are valoarea
false, firul producător nu mai intră în aşteptare, ci execută instrucţiunile prin care modifică
datele monitorului, după care pune variabilaConditie la valoarea true şi se invocă
metoda notify() pentru a notifica consumatorul că exista date noi, după care se încheie
executarea metodei puneDate.
Funcţionarea firului de execuţie consumator este asemănătoare, dar acesta invocă metoda
preiaDate. Executând această metodă, se verifică mai întâi dacă variabilaConditie are
valoarea false, ceeace înseamnă că nu există date noi. În această situaţie, firul consumator
intră în aşteptare, până va primi notificarea că s-au pus date noi. Dacă, însă,
variabilaConditie are valoarea true, se folosesc datele monitorului, după care se pune
variabilaConditie la valoarea false, pentru a permite producătorului să modifice datele şi
se invocă metoda notify() pentru a scoate producătorul din starea de aşteptare.
Este foarte important să avem în vedere că metoda puneDate este concepută pentru a fi
invocată de firul producător, în timp ce metoda preiaDate este concepută pentru a fi
invocată de către firul consumator. Numele folosite aici atât pentru clasa monitor, cât şi
pentru metodele acesteia şi variabila de condiţie sunt convenţionale şi se aleg de către
350
Programarea orientata pe obiecte în limbajul Java
programator, din care cauză au fost scrise în cele de mai sus cu roşu cursiv.
Exemplul 1
În fişierul Sincro.java se dă un exemplu de aplicaţie în care se sincronizeaza două fire de
execuţie folosind modelul producător/consumator. Pentru a uşura citirea programului, clasele
au fost denumite chiar Producator, Consumator şi Monitor, dar se puteau alege, evident, orice
alte nume. Firul de execuţie prod, care este instanţa clasei Producator, parcurge un număr de
cicluri impus (nrCicluri), iar la fiecare parcurgere genereaza un tablou de numere aleatoare,
pe care îl depune în monitor. Firul de execuţie cons, care este instanţă a clasei Consumator,
parcurge acelaşi număr de cicluri, în care foloseşte tablourile generate de firul prod.
Transmiterea datelor se face prin intermediul monitorului monit, care este instanţă a clasei
Monitor. Această clasă conţine tabloul de numere întregi tab şi variabila de condiţie
valoriNoi, care are iniţial valoarea false, dar primeşte valoarea true atunci când
producătorul a pus în monitor date noi şi primeşte valoarea false, atunci când aceste date au
fost folosite de consumator. Pentru punerea de date noi în monitor, producătorul invocă
metoda sincronizată puneTablou. Pentru a folosi datele din monitor, consumatorul invocă
metoda sincronizată preiaTablou. Fiecare din aceste metode testează valoarea variabilei de
condiţie valoriNoi şi pune firul de execuţie curent în aşteptare, dacă valoarea acestei
variabile nu este corespunzătoare. După ce s-a efectuat operaţia de punere/utilizare a datelor,
metoda sincronizată prin care s-a făcut această operaţie invocă metoda notify(). Dacă ar fi
putut exista mai multe fire în starea Waiting, era preferabil sa se invoce metoda
notifyAll().
class Sincro {
Producator prod;
Consumator cons;
Monitor monit;
int nrCicluri;
351
Severin Bumbaru
*/
class Consumator extends Thread {
public void run() {
System.out.println("Incepe executarea firului Consumator");
int tab[];
for(int i=0; i<nrCicluri; i++) {
tab=monit.preiaTablou();
System.out.print("Ciclul i="+i+
" s-a preluat tabloul ");
for(int j=0; j<tab.length; j++)
System.out.print(tab[j]+" ");
System.out.println();
}
System.out.println("Sfarsitul executiei firului Consumator");
}
} /* Sfarsitul clasei Consumator */
352
Programarea orientata pe obiecte în limbajul Java
Se observă că, deşi cele două fire se derulează în mod autonom, exista între ele o sincronizare
corectă, în sensul că datele puse de producător la un ciclu cu un anumit indice, sunt preluate
de consumator la ciclul cu acelaşi indice (de exemplu datele puse de producator în ciclul 3
sunt luate de consumator tot în ciclul 3). Aşa dar, nu există date pierdute sau preluate de două
ori.
353
Severin Bumbaru
Exemplul 2
În fişierul Bondari1.java se dă un exemplu de aplicaţie cu interfaţă grafică, în care există trei
fire de execuţie (în afară de cel al metodei main()): două fire din clasa Bondar, care
calculeaza fiecare mişcările unui "bondar", şi un fir de executie din clasa Fereastră, care
reprezintă grafic mişcările celor doi bondari. În acest caz, ambii "bondari" se mişca în aceeaşi
fereastră. Pentru că fiecare din acestea extinde câte o clasa de interfaţă grafică (respectiv
clasele JPanel şi Canvas), pentru realizarea firelor de execuţie s-a folosit interfaţa Runnable.
Cele două fire "bondar" (respectiv fir1 şi fir2) au rolul de producător, iar firul care conţine
fereastra de afişare (respectiv fir3) are rolul de consumator. Rolul monitorului este îndeplinit
de instanţa clasei CutiePoştală, care nu conţine decât variabila de condiţie a monitorului
valoareNoua şi două metode sincronizate amPus şi amLuat. Având în vedere că există
posibilitatea ca, la un moment dat, să existe în starea Waiting mai multe fire de aşteptare care
trebuie reactivate, în aceste metode s-a folosit invocarea notifyAll() în loc de notify().
Dacă se execută această aplicaţie se poate vedea cum cei doi "bondari" evoluează în mod
independent în aceeaşi fereastră, putându-se ajusta în mod independent perioada şi
amplitudinea fiecăruia.
Întrebări
Nivel 1
Nivel 2
354
Programarea orientata pe obiecte în limbajul Java
355
Severin Bumbaru
Pachetul java.lang
Clase
Byte - clasă acoperitoare pentru tipul byte - v.cap.4
Boolean - clasă acoperitoare pentru tipul boolean – v. cap.4
Character - clasă acoperitoare pentru tipul char – v.cap.4
Class - clasa claselor – v.cap.4
Double - clasă acoperitoare pentru tipul double – v.cap.4
Float - clasă acoperitoare pentru tipul float – v. cap.4
Integer - clasă acoperitoare pentru tipul int – v. cap.4
Long - clasă acoperitoare pentru tipul long – v. cap.4
Math - clasă care oferă metode pentru calcularea funcţiilor matematice uzuale – v.cap.4 şi
completarea din acest index, pag. 361
Number - clasă abstractă, rădăcina claselor acoperitoare numerice – v. cap.4
Object - rădăcina ierarhiei de clase Java (un obiect oarecare) – v.cap.4 şi completarea din
acest index pag. 363
Short - clasa acoperitoare pentru tipul short – v.cap.4
String - clasa şirurilor de caractere nemodificabile - v.cap.4 şi completarea din acest index
pag. 364
StringBuffer - clasa şirurilor de caractere modificabile (instanţa este o zonă tampon care
conţine un şir modificabil) – v. cap.4 şi completarea din acest index pag. 369
System - clasa sistemului de execuţie – v. cap.4
Thread - clasa firelor de execuţie – v.cap.12
Void - clasă acoperitoare pentru tipul void – v.cap.4
Interfeţe
Cloneable - implementată de clasele pentru care se poate folosi metoda clone() din clasa
Object. Nu contine metode.
Comparable - implementată de clasele ale căror instanţe sunt comparabile între ele
(formează o mulţime ordonată). – v. specificatia de la pag. 372
Runnable - implementată de clasele, ale căror instanţe pot fi rulate ca fire de execuţie. –
v.cap.12.
Pachetul java.io
Conţine clase şi interfeţe necesare pentru operaţiile de intrare/ieşire (input/output - io).
356
Programarea orientata pe obiecte în limbajul Java
Clase
(ierarhiile de clase de intrare/ieşire sunt prezentate în cap.11, secţiunea pachetul java.io din
acest manual)
Interfeţe
Serializable - interfaţă pe care trebuie să o aibă obiectele serializabile (care pot fi scrise cu
ObjectOutputStream şi citite cu ObjectInputStream) – v. cap.11
357
Severin Bumbaru
Pachetul java.util
Clase
EventObject - clasa obiectelor care conţin informaţii despre evenimente – v. cap. 7 şi pag.
379
Interfeţe
EventListener - interfaţă generică pentru ascultătoarele de evenimente. – v. cap.7 şi pag. 379
Pachetul java.awt
Clase
Interfeţe
358
Programarea orientata pe obiecte în limbajul Java
Pachetul java.awt.event
Clase
Interfeţe
ActionListener - interfaţă pentru ascultătoarele de evenimente de acţiune – v.cap.8 şi
pag.419
AdjustmentListener - interfaţă pentru ascultătoarele de evenimente de ajustare – v.cap.8 şi
pag.420.
ComponentListener - interfaţă pentru ascultătoarele de evenimente de componentă – v.cap.8
şi pag.420.
ItemListener - interfaţă pentru ascultătoarele de evenimente de articol – v.cap.8 şi pag.421.
KeyListener - interfaţă pentru ascultătoare de evenimente de tastă – v.cap.7 şi pag.421.
MouseListener - interfaţă pentru ascultătoare de evenimente de mouse v.cap.7 şi pag.421.
MouseMotionListener - interfaţă penteru ascultătoare de evenimente de mişcare a mouse-
ului – v.cap.7 şi pag.422.
TextListener - interfaţă pentru ascultătoare de evenimente de text – v.cap.8 şi pag.422.
WindowListener - interfaţă pentru ascultătoare de evenimente de fereastră – v.cap.7 şi
pag.423
Pachetul javax.swing
Clase
359
Severin Bumbaru
Pachetul javax.swing.event
Clase
360
Programarea orientata pe obiecte în limbajul Java
pag.454.
PopupMenuEvent - clasa evenimentelor generate de meniuri pop-up – v.cap.8 şi pag.454.
Interfeţe
361
Severin Bumbaru
Clasa Math
public final class Math
extends Object
Clasa Math contine metode statice pentru calcularea unor functii matematice uzuale, cum
sunt functiile trigonometrice, radacina patrata si altele.
Campuri:
Metode:
public static double sin(double a)- intoarce sinusul trigonometric sin a, unde unghiul
a este in radiani.
public static double cos(double a)- intoarce cosinusul trigonometric cos a, unde
unghiul a este in radiani;
public static double asin(double a)- intoarce arcsin a in intervalul [-pi/2, pi/2];
public static double acos(double a)- intoarce arccos a in intervalul [0.0, pi];
public static double atan(double a)- intoarce arctg a in intervalul [-pi/2, pi/2];
362
Programarea orientata pe obiecte în limbajul Java
valoarea expresiei f1-f2*n, unde n este numarul intreg cel mai apropiat de valoarea exacta a
raportului f1/f2. Daca restul este zero, el are semnul primului argument.
public static double ceil(double a)- intoarce, sub forma de valoare double, cel mai
mic numar intreg care nu este mai mic decat argumentul (rotunjire prin adaos);
public static double floor(double a)- intoarce, sub forma de valoare double, cel
mai mare numar intreg care nu este mai mare decat argumentul (rotunjire prin micsorare);
public static double rint(double a)- intoarce, sub forma de valoare double, numarul
intreg cel mai apropiat de argument;
public static int round(float a)- se intoarce numarul intreg cel mai apropiat de
argument. Daca acest intreg este mai mic decat marginea inferioara pentru tipul int, rezultatul
este Integer.MIN_VALUE. Daca acest intreg este mai mare decat marginea superioara pentru
tipul int, se obtine rezultatul Integer.MAX_VALUE.
public static long round(double a)- intoarce numarul intreg cel mai apropiat de
argument (cu aceleasi observatii ca la metoda precedenta).
public static double random()- intoarce un numar pseudoaleator in intervalul [0.0, 1.0],
avand o lege de repartitie (aproximativ) uniforma in acest interval.
public static int abs(int a)- intoarce modului argumentului a (valoarea absoluta);
public static long abs(long a)- intoarce modului argumentului a (valoarea absoluta);
public static int max(int a, int b)- intoarce cel mai mare din cele doua argumente;
public static long max(long a, long b)- intoarce cel mai mare din cele doua
argumente;
public static float max(float a, float b)- intoarce cel mai mare din cele doua
argumente;
363
Severin Bumbaru
public static double max(double a, double b)- intoarce cel mai mare din cele doua
argumente;
public static int min(int a, int b)- intoarce cel mai mic din cele doua argumente;
public static float min(float a, float b)intoarce cel mai mic din cele doua
argumente;
public static double min(double a, double b)intoarce cel mai mic din cele doua
argumente;
Class Object
java.lang.Object
--------------------------------------------------------------------------------
Clasa Object este radacina ierarhiei de clase Java. Orice alta clasa ore clasa Object ca
superclasa. Toate celelalte clase, inclusiv tablourile, implementeaza metodele acestei clase ---
-----------------------------------------------------------------------------
Constructor:
public Object()
Metode:
364
Programarea orientata pe obiecte în limbajul Java
Clasa String
public final class String
extends Object
implements Serializable, Comparable
Instantele clasei String sunt siruri de caractere. Orice literal sir in limbajul Java, de exemplu
"abc", este un astfel de obiect. Sirurile sunt constante, deci continutul lor nu poate fi
modificat. Pentru a obtine siruri modificabile se va folosi clasa StringBuffer.
Campuri:
public static Comparator CASE_INSENSITIVE_ORDER
Campul contine un Comparator care ordoneaza obiectele-sir cand ele sunt comparate
cu metoda comparetoIgnoreCase().
Constructori:
public String()
Creeaza un sir vid.
365
Severin Bumbaru
Metode:
366
Programarea orientata pe obiecte în limbajul Java
daca ii succede lui o intoarce o valoare pozitiva. Compararea sirurilor se face in ordine
lexicografica (in ordinea in care ar fi plasate intr-un dictionar).
public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
Copiaza caracterele din acest sir in tabloul de caractere de destinatie char[]. Copierea
incepe de la caracterul de pe pozitia srcBegin si se termina la caracterul de pe pozitia
srcEnd-1. Plasarea in tabloul de destinatie incepe cu pozitia dstBegin. Se poate genera
NullPointerException daca referinta la tablou este nula sau IndexOutOfBoundsException
daca indicii sunt gresiti..
367
Severin Bumbaru
publicint length()
Intoarce lungimea acestui sir (numarul de caractere continute).
368
Programarea orientata pe obiecte în limbajul Java
369
Severin Bumbaru
Clasa StringBuffer
public final class StringBuffer
extends Object
implements Serializable
Operatiile principale asupra unui StringBuffer sunt metodele append si insert. Fiecare
din ele converteste o data intr-un String, pe care apoi il adauga la sirul din StringBuffer
sau il insereaza in acesta pe o pozitie data.
Constructori:
Metode:
370
Programarea orientata pe obiecte în limbajul Java
public char charAt(int index)- intoarce caracterul de pe pozitia de indice index. Daca
indicele nu se incadreaza in lungimea sirului curent, se genereaza o
IndexOutOfBoundsException.
public void setCharAt(int index, char ch)- se pune caracterul ch pe pozitia index.
Daca index este incorect, se genereaza o IndexOutOfBoundsException.
public StringBuffer append(char[] str, int offset, int len)- se adauga la acest
StringBuffer len caractere din tabloul str, incepand de la pozitia offset din acest tablou.
public StringBuffer append(long l)- se adauga numarul intreg llung l convertit in sir.
371
Severin Bumbaru
public StringBuffer replace(int start, int end, String str)- caracterele situate
in StringBuffer pe pozitiile de la start la end-1 se inlocuiesc prin subsirul str. Daca indicii
start sau end nu sunt corecti se genereaza o StringIndexOutOfBoundsException.
public String substring(int start)- intoarce subsirul care incepe de la pozitia start
sau genereaza o StringIndexOutOfBoundsException.
public String substring(int start, int end)- intoarce subsirul de la pozitia start la
pozitia end-1 sau genereaza o StringIndexOutOfBoundsException.
372
Programarea orientata pe obiecte în limbajul Java
public String toString()- intoarce un String care are acelasi continut cu acest
StringBuffer.
--------------------------------------------------------------------------------
Interfaţa Comparable
java.lang.Comparable
Interfaţa este implementată de clasele ale căror instanţe pot fi comparate între ele, deci fac
parte dintr-o mulţime ordonată.
Fie a şi b două obiecte, aparţinând unei clase cu interfaţa Comparable. În acest caz:
a.compareTo(b)<0 dacă a<b (a precede lui b);
a.compareTo(b)==0 dacă a==b;
a.compareTo(b)>0 dacă a>b (a succede lui b).
Clasa BufferedInputStream
Face parte din pachetul java.IO şi extinde clasa FilterInputStream.
Instanţele acestei clase sunt fluxuri de intrare de octeţi, care conţin o zonă tampon (buffer).
Octeţii sunt citiţi unul câte unul din această zonă tampon. Când zona tampon se goleşte, ea se
373
Severin Bumbaru
umple din nou, în mod automat, prin citirea din fluxul de intrare căruia acest
BufferedInputStream îi este ataşat.
Constructori
public BufferedInputStream(InputStream in) - se creează un BufferedInputStream
ataşat fluxului de intrare in.
Metode
Clasa BufferedOutputStream
Face parte din pachetul java.io şi extinde clasa FilterOutputStream
Constructori
Metode
Aceleaşi ca în clasa OutputStream.
Clasa BufferedReader
Face parte din pachetul java.io şi extinde clasa Reader.
Constructori
public BufferedReader(Reader in) - creează un flux de intrare de caractere, cu zonă
tampon de lungime implicită, conectat la ieşirea fluxului de intrare de caractere in.
374
Programarea orientata pe obiecte în limbajul Java
Metode
Aceleaşi ca în clasa Reader.
Clasa BufferedWriter
Face parte din pachetul java.io şi extinde clasa Writer. Caracterele primite sunt acumulate
într-o zonă tampon şi sunt transmise la ieşire numai când această zonă tampon s-a umplut,
sau când se execută metoda flush().
Constructori
public BufferedWriter(Writer out) - creează un flux de ieşire de caractere, cu zonă
tampon de lungime implicită, conectat la ieşire la fluxul de caractere out.
Metode
Aceleaşi ca în clasa Writer.
Clasa ByteArrayInputStream
Face parte din pachetul java.io şi extinde clasa InputStream.
Acest flux conţine o zonă tampon (buffer) din care se citesc datele, situată în memoria
internă.
Câmpuri
protected byte[] buf - tabloul de octeţi din care se face citirea, situat în memoria internă.
protected int pos - indicele următorului octet care va fi citit din tabloul de octeţi de
intrare buf[].
protected int mark - poziţia marcată din flux (este iniţializată implicit la zero şi poate fi
modificată prin metoda mark()).
protected int count - numărul de octeţi existenţi efectiv în tabloul buf (este cel puţin 0 şi
cel mult egal cu lungimea tabloului)
Constructori
public ByteArrayInputStream(byte[] buf) - creează un flux de intrare de octeţi, care
citeşte din tabloul buf[] situat în memoria internă.
375
Severin Bumbaru
Metode
Aceleaşi ca la clasa InputStream.
Clasa ByteArrayOutputStream
Face parte din pachetul java.io şi extinde clasa OutputStream.
Flux de ieşire de octeţi, care scrie într-un tablou de octeţi situat în memoria internă. Zona
tampon (tabloul) în care se scrie are capacitate variabilă: capacitatea lui se mareşte automat
dacă prin o nouă scriere se depăşeşte capacitatea existentă. Referinţa la zona tampon pentru
scriere se obţine prin metoda toByteArray().
Câmpuri
protected byte[] buf - tabloul de octeţi în care se face scrierea, situat în memoria internă.
protected int count - numărul de octeţi valizi existenţi în tabloul buf[].
Constructori
public ByteArrayOutputStream() - creează un flux de ieşire de octeţi care scrie într-o
zonă tampon (tablou) care are iniţial capacitatea de 32 octeţi, dar capacitatea creşte automat,
dacă este necesar.
Metode
Metodele sunt cele din clasa OutputStream, la care se adaugă:
public byte[] toByteArray() - întoarce un nou tablou de octeţi, în care este copiat
conţinutul valid al yonei tampon buf.
public int size() - întoarce dimensiunea curentă a zonei tampon (valoarea câmpului count).
Clasa CharArrayReader
376
Programarea orientata pe obiecte în limbajul Java
Face parte din pachetul java.io şi extinde clasa Reader. Instanţele acestei clase sunt fluxuri de
intrare de carractere, care citesc dintr-un tablou de caractere situat in memoria internă.
Constructori
public CharArrayReader(char[] buf) - creează un flux de intrare de caractere, care
citeşte din tabloul de caractere buf[], situat ]n memoria internă.
Metode
Clasa CharArrayWriter
Face parte din pachetul java.io şi extinde clasa Writer. Instanţele sunt fluxuri de ieşire de
caractere, care scriu într-o zonă tampon sub formă de tablou de caractere extensibil, situat în
memoria internă. Datele din zona tampon de ieşire pot fi obţinute prin metodele
toCharArray() şi toString().
Constructori
public CharArrayWriter() - creează un flux de ieşire de caractere, care scrie într-o zonă
tampon situată în memoria internă, a cărei lungime este dată implicit.
Metode
Aceleaşi ca în clasa Writer, la care se adaugă:
public void writeTo(Writer out) throws IOException - scrie datele din zona tampon
în fluxul de ieşire de caractere out.
public char[] toCharArray() - întoarce un tablou de caractere, având acelaşi conţinut cu zona
tampon de ieşire.
public String toString() - întoarce un şir, care are acelaşi conţinut cu zona tampon.
Clasa FileDescriptor
377
Severin Bumbaru
Face parte din pachetul java.io. Instanţele clasei FileDescriptor servesc ca manipulatori ai
unor fişiere sau socluri deja deschise. Principala utilizare este la crearea unor fluxuri din
clasele FileInputStream sau FileOutputStream, conectate la fişiere deja deschise.
Câmpuri
public static final FileDescriptor in - descriptorul fişierului de intrare standard a
sistemului, System.in.
public static final FileDescriptor err - descriptorul fişierului de ieşire pentru erori
a sistemului, System.err
Constructori
public FileDescriptor() - creează o instanţă a clasei Filedescriptor, care nu este validă
(nu indică nici un fişier)
Metode
public boolean valid() - indică dacă acest descriptor de fişier este valid (se referă la un
fişier sau soclu deschis).
Clasa FilterInputStream
Face parte din pachetul java.io. Este superclasa claselor de fluxuri de intrare de octeţi, care
realizează anumite operaţii de filtrare (prelucrare) a datelor de intrare. De fapt, metodele
acestei clase sunt cele moştenite de la superclasa InputStream, deci nu fac nici o prelucrare.
Aceste metode pot fi însă redefinite în subclase, pentru a realiza astfel de prelucrări. În
pachetul java.io există subclasele DataInputStream, BufferedInputStream şi
PushbackInputStream. Putem crea şi propriile noastre subclase.
Constructor
protected FilterInputStream(InputStream in) - creează un flux de intrare de octeţi cu
filtru, care preia fluxul de intrare in.
Metode
Aceleaşi ca în clasa InputStream.
378
Programarea orientata pe obiecte în limbajul Java
Clasa FilterOutputStream
Face parte din pachetul java.io. Este superclasa claselor de fluxuri de ieşire de octeţi, care
realizează anumite operaţii de filtrare (prelucrare) a datelor de ieşire. De fapt, metodele
acestei clase sunt cele moştenite de la superclasa OutputStream, deci nu fac nici o prelucrare.
Aceste metode pot fi însă redefinite în subclase, pentru a realiza astfel de prelucrări. În
pachetul java.io există subclasele DataOutputStream, BufferedOutputStream, PrintStream şi
altele. Putem crea şi propriile noastre subclase.
Constructor
public FilterOutputStream(OutputStream out) - creează un flux de ieşire de octeţi cu
filtru, care transmite datele la fluxul de ieşire de octeţi out.
Metode
Metodele sunt aceleaşi ca în clasa OutputStream.
Clasa FilterReader
Face parte din pachetul java.io. Este superclasa claselor de fluxuri de intrare de caractere,
care realizează anumite operaţii de filtrare (prelucrare) a datelor de intrare. De fapt, metodele
acestei clase sunt cele moştenite de la superclasa Reader, deci nu fac nici o prelucrare. Aceste
metode pot fi însă redefinite în subclase, pentru a realiza astfel de prelucrări. În pachetul
java.io există subclasa PushbackReader. Putem crea şi propriile noastre subclase.
Constructor
protected FilterReader(Reader in) - creează un flux de intrare de caractere cu filtru,
care preia fluxul de intrare in.
Metode
metodele sunt la fel cele din superclasa Reader.
Clasa FilterWriter
Face parte din pachetul java.io. Este superclasa claselor de fluxuri de ieşire de caractere, care
realizează anumite operaţii de filtrare (prelucrare) a datelor de ieşire. De fapt, metodele
acestei clase sunt cele moştenite de la superclasa Writer, deci nu fac nici o prelucrare. Aceste
metode pot fi însă redefinite în subclase, pentru a realiza astfel de prelucrări.
379
Severin Bumbaru
Constructor
protected FilterWriter(Writer out) - creează un flux de ieşire de caractere cu filtru, care
transmite datele la fluxul de ieşire de caractere out.
Metode
Aceleaşi ca în superclasa Writer.
Clasa EventObject
public class EventObject extends Object implements Serializable
Face parte din pachetul java.util. Este rădăcina ierarhiei de clase de evenimente. Fiecare
eveniment este generat de o sursă (source). Fiecare instanţă a clasei EventObject conţine o
referinţă la această sursă şi o metodă prin care ea este obţinută.
Constructor
public EventObject(Object source) - se generează un eveniment, indicându-se sursa.
Metode
public Object getSource() - întoarce sursa evenimentului.
Interfaţa EventListener
public interface EventListener
Face parte din pachetul java.util. Este o interfaţă generică, pe care trebuie să o extindă -
direct sau indirect - orice interfaţă de ascultătoare de evenimente.
Clasa AWTEvent
public abstract class AWTEvent extends EventObject
Face parte din pachetul java.awt. Este superclasa tuturor claselor de evenimente AWT. Orice
eveniment AWT are un identificator, reprezentat prin câmpul id. Clasa conţine câmpuri
380
Programarea orientata pe obiecte în limbajul Java
statice, care au ca valori măşti pentru selectarea după id a diferitelor tipuri de evenimente.
Clasele de evenimente create în afara pachetelor din Java API trebuie să aibă valoarea
câmpului id superioară valorii maxime din câmpul RESERVED_ID_MAX.
Clasa AWTEvent înlocuieşte clasa Event din JDK 1.0, care a fost menţinută în pachet numai
pentru compatibilitate. Subclasele directe ale clasei AWTEvent sunt: ActionEvent,
AdjustmentEvent, AncestorEvent, ComponentEvent, HierarchyEvent, InputMethodEvent,
InternalFrameEvent, InvocationEvent, ItemEvent, TextEvent. Ele se găsesc în pachetul
java.awt.event, cu excepţia clasei InternalFrameEvent, care se găseşte în pachetul
javax.swing.event.
Câmpuri
protected int id - identificatorul tipului de eveniment.
381
Severin Bumbaru
public static final int RESERVED_ID_MAX - valoarea maximă rezervată pentru id.
Constructori
public AWTEvent(Event event) - construieşte un obiect din clasa AWTEvent, folosind
paramerii unei instanţe a clasei Event.
Metode principale
Metodele clasei EventObject, la care se adaugă:
Clasa BorderLayout
public class BorderLayout extends Object implements LayoutManager2, Serializable
Dăm aici un exemplu de utilizare a unui gestionar de poziţionare din clasa BorderLayout:
p.setLayout(new BorderLayout());
p.add(new Button("OK"), BorderLayout.SOUTH);
În prima instrucţiune, pentru containerul p se setează gestionarul de poziţionare
BorderLayout. În a doua instrucţiune, se pune butonul cu inscripţia "OK" în zona SOUTH a
aceluiaşi container. Dacă se omite zona, se consideră implicit că adăugarea componentei se
face în zona CENTER.
382
Programarea orientata pe obiecte în limbajul Java
Constructori
public BorderLayout() - se creează un gestionar de poziţionare BorderLayout, fără
distanţare între componente.
Metode
Metodele din interfeţele LayoutManager şi LayoutManager2, la care se adaugă următoarele:
Clasa Button
public class Button extends Component implements Accessible
Instanţele acestei clase sunt butoane care conţin un text numit "etichetă" (engl: label). Când
este apăsat, butonul generează un eveniment de acţiune, care poate fi ascultat cu un
ActionListener.
Constructori
public Button() - construieşte un buton care nu conţine text.
Metode
Metodele sunt cele ale clasei Component la care se adaugă următoarele:
383
Severin Bumbaru
Clasa Canvas
public class Canvas extends Component implements Accessible
Instanţele acestei clase sunt suprafeţe rectangulare albe, pe care se pot trasa desene (în
engleză, canvas este pânza pe care se pictează). Pentru a realiza un desen, se creează o
subclasă a clasei Canvas, pentru care se redefineşte metoda paint.
Constructori
public Canvas() - creează o nouă instanţă a clasei Canvas.
Medtode
Metodele clasei Component, dintre care cea mai utilizată este metoda:
Clasa CardLayout
public class CardLayout extends Object implements LayoutManager2, Serializable
Constructori
public CardLayout() - construieşte un CardLayout fără spaţiu liber în jurul componentelor.
384
Programarea orientata pe obiecte în limbajul Java
Metode
Metodele interfeţelor LayoutManager şi LayoutManager2, la care se adaugă următoarele:
Clasa Color
Clasa Color încapsulează culorile din spaţiul de culori implicit sRGB, sau culori dintrun
spqaţiu de culori arbitrar, identificat prin ColorSpace. Fiecare culoare conţine, în afară de
componentele de culoare fundamentale, şi o componentă numită alpha, care semnifică
opacitatea şi are valori reale în intervalul 0.0 ... 1.0 sau valori întregi în intervalul 0 .. 255.
Valoarea alpha=0 înseamnă opacitate nulă (culoare total transparentă), iar valoarea maximă a
lui alpha înseamnă opacitate maximă (culoare total netransparentă). În mod implicit se
admite opacitatea maximă.
Culorile fundamentale din sistemul sRGB (standard RGB) sunt următoarele: red (roşu),
green (verde) şi blue (albastru). Acestea pot fi date prin numere întregi în intervalul 0 .. 255
sau prin numere reale în intervalul 0.0 .. 1.0. Aceste valori reprezintă intensitatea culorii
fundamentale respective în culoarea dată. Orice culoare se obţine prin combinarea acestor trei
componente fundamentale, cu intensităţi diferite. Explicaţii suplimentare sunt date şi în
secţiunea "Clasa Color" din acest manual.
Valorile componentelor fundamentale ale culorii pot fi date şi împachetate într-un singur
număr de tip int, în care fiecare octet conţine una din componente (în intervalul 0 .. 255)
astfel: blue - biţii 0 ..7; green: biţii 8 .. 15; red: biţii 16 .. 23; alpha: biţii 24 .. 31.
385
Severin Bumbaru
Câmpuri
public static final Color white - culoarea albă.
Constructori
public Color(int r, int g, int b) - creează o instanţă a clasei Color pentru cu
culoarea cu componentele RGB specificate prin numere întregi cuprinse între 0 şi 255.
public Color(int r, int g, int b, int a) - creează o instanţă a clasei Color pentru
cu culoarea cu componentele RGB şi componenta a (alpha) specificate prin numere întregi
cuprinse între 0 şi 255.
public Color(int rgb) - creează o instanţă a clasei Color, dându-i ca argument un număr
de tip int (pe 4 octeţi), care în primii trei octeţi conţine cele trei componente fundamentale.
public Color(int rgba, boolean hasalpha) - creează o instanţă a clasei Color, dându-i ca
argument un număr de tip int (pe 4 octeţi), care în primii trei octeţi conţine cele trei
componente fundamentale, iar în ultimul octet componenta alpha. Al doilea argument indică
dacă există sau nu componenta alpha.
public Color(float r, float g, float b) - creează o instanţă a clasei Color pentru cu culoarea cu
componentele RGB specificate prin numere reale cuprinse între 0.0 şi 1.0.
public Color(float r, float g, float b, float a) - public Color(int r, int g, int b, int
a) - creează o instanţă a clasei Color pentru cu culoarea cu componentele RGB şi
componenta a (alpha) specificate prin numere reale cuprinse între 0.0 şi 1.0.
386
Programarea orientata pe obiecte în limbajul Java
Metode
public int getRed() - întoarce componenta roşie.
public int getRGB() - întoarce un int, care conţine sub formă împachetată cele patru
componente (r, g, b, alpha).
public static Color getColor(String nm, int v) - similar cu metoda precedentă, dar
culoarea implicită este dată printr-un număr întreg.
387
Severin Bumbaru
Clasa Component
public abstract class Component extends Object implements ImageObserver, MenuContainer,
Serializable
Face parte din pachetul java.awt. Este rădăcina ierarhiei tuturor claselor de componente ale
interfeţelor grafice AWT şi JFC/Swing.
Metode
public String getName() - întoarce numele componentei.
388
Programarea orientata pe obiecte în limbajul Java
public boolean isValid() - indică dacă această componentă este validă (este corect
dimensionată şi poziţionată în containerul său părinte).
public boolean isDisplayable() - indică dacă această componentă este afişabilă (este
conectată la o resursă de ecran de afişare nativă).
public boolean isVisible() - indică dacă această componentă este vizibilă. Toate
componentele sunt iniţial vizibile, cu excepţia celor de cel mai înalt nivel, cum sunt cele din
clasele Frame şi JFrame.
public boolean isShowing() - indică dacă această componentă apare efectiv pe ecran.
public boolean isEnabled() - arată dacă această componentă este activă (poate răspunde
la intrările date de utilizator şi să genereze evenimente).
389
Severin Bumbaru
public int getX() - întoarce coordonata x a colţului din stânga-sus (în sistemul de
coordonate al containerului părinte).
public int getY() - întoarce coordonata y a colţului din stânga-sus (în sistemul de
coordonate al containerului părinte).
public boolean isOpaque() - indică dacă această componentă este opacă (implicit, toate
componentele sunt opace).
390
Programarea orientata pe obiecte în limbajul Java
public Cursor getCursor() - întoarce cursorul setet pentru această componentă sau
pentru părintele ei.
public void paint(Graphics g) - desenează componenta. Această metodă este apelată automat
când trebuie desenată componenta.
public void repaint() - redesenează componentă (face ca metoda update pentru această
componentă să fie invocată cât mai rapid posibil).
public void repaint(long tm, int x, int y, int width, int height) -
redesenează dreptunghiul specificat al componentei după tm milisecunde.
391
Severin Bumbaru
392
Programarea orientata pe obiecte în limbajul Java
public String toString() - întoarce reprezentarea sub formă de şir a acestei componente.
public void list(PrintStream out) - scrie listingul acestei componente în fluxul out.
public void list(PrintStream out, int indent) - scrie listingul acestei componente
în fluxul out, indentat cu indent.
public void list(PrintWriter out) - scrie listingul acestei componente în fluxul out.
public void list(PrintWriter out, int indent) - scrie listingul acestei componente în fluxul out,
indentat cu indent.
393
Severin Bumbaru
Clasa Container
public class Container extends Component
Containerele sunt componente care pot conţine alte componente (inclusiv alte containere,
deoarece şi acestea sunt sunt tot componente).
Componentele adăugate în container sunt păstrate într-o listă. Ordinea din această listă este
cea în care componentele sunt luate în consideraţie de către gestionarul de poziţionare.
Constructor
public Container() - construieşte un nou container.
Metode
Metodele clasei Component, la care se adaugă următoarele:
public Insets getInsets() - întoarce lăţimea bordurii containerului (sub forma unui
obiect din clasa Insets).
394
Programarea orientata pe obiecte în limbajul Java
Clasa Dialog
public class Dialog extends Window
Fereastra de dialog este o fereastră de cel mai înalt nivel (care poate fi plasată direct pe
ecranul nativ). Ea are bordură şi este folosită în special pentru dialogul (schimbul de
informaţii interactiv) cu utilizatorul. Managerul de poziţionare implicit este BorderLayout.
Generează următoarele evenimente de fereastră: WindowOpened, WindowClosing,
WindowClosed, WindowActivated, WindowDeactivated.
Fereastra de dialog are ca proprietar o altă fereastră din clasa Frame sau Dialog. Când
proprietarul este ascuns sau minimizat, fereastra de dialog este de asemenea ascunsă.
Fereastra de dialog poate fi modală sau nemodală (implicit este nemodală). O fereastră
modală blochează toate intrările către orice altă fereastră de cel mai înalt nuvel şi
descendentele lor, cu excepţia celor care au ca proprietar chiar această fereastră de dialog.
395
Severin Bumbaru
Constructori
public Dialog(Frame owner) - construieşte o fereastră de dialog fără titlu, având ca
proprietar cadrul owner.
Metode
Metodele clasei Window, la care se adaugă următoarele:
public boolean isModal() - indică dacă fereastra de dialog este sau nu modală.
Clasa Dimension
public class Dimension extends Dimension2D implements Serializable
396
Programarea orientata pe obiecte în limbajul Java
Instanţele acestei clase conţin dimensiunile orizontală şi vericală ale unei componente,
exprimate ca numere întregi.
Câmpuri
public int width - lăţimea.
Constructori
public Dimension() - construieşte o instanţă a clasei Dimension, conţinând dimensiuni
nule.
Metode
public double getWidth() - întoarce lăţimea.
public boolean equals(Object obj) - redefineşte metoda equals din clasa Object.
Clasa Event
public class Event extends Object implements Serializable
Clasa Event este clasa evenimentelor generate de interfaţa utilizator grafică utilizată în JDK
1.0 (în prima versiune a Java API) şi menţinută în continuare pentru compatibilitate, deşi
începând cu JDK 1.1 ea a fost înlocuită prin clasa AWTEvent.
Clasa FlowLayout
397
Severin Bumbaru
Câmpuri
public static final int LEFT - aliniere la stânga.
public static final int LEADING - aliniere la muchia frontală pentru orientarea dată a
containerului (adică aliniere la stânga, în cazul când ordinea de plasare este de la stânga la
dreapta).
public static final int TRAILING - aliniere la muchia terminală, ţinând cont de
orientarea dată a containerului (deci aliniere la dreapta, dacă ordinea de plasare a
componentelor este de la stânga la dreapta).
Constructori
public FlowLayout() - construieşte un FlowLayout cu aliniere implicită a componentelor
(la centru) şi cu spaţii libere între componente de 5 unităţi.
Metode
metodele din interfaţa LayoutManager, la care se adaugă următoarele:
public void setAlignment(int align) - setează alinierea. Parametrul align poate fi unul
din câmpurile: FlowLayout.LEFT, FlowLayout.CENTER, FlowLayout.RIGHT.
public void setHgap(int hgap) - setează spaţiul liber între componente pe orizontală.
398
Programarea orientata pe obiecte în limbajul Java
public void setVgap(int vgap) - setează spaţiul liber între componente pe verticală.
Clasa Font
public class Font extends Object implements Serializable
Instanţele clasei Font reprezintă fonturi şi încorporează fiecare informaţii privind forma,
mărimea şi stilul unui caracter. Caracterele sunt simbolurile tipografice folosite în texte.
Stilurile sunt: plain (normal), bold, italic, bold+italic. Mărimea fontului (înălţimea literei
majuscule) se exprimă în puncte tipografice.
public static final int BOLD - stilul BOLD (caractere îngroşate, aldine).
Constructori
public Font(String name, int style, int size) - se construieşte un nou font, fiind
specificate numele fontului, stilul şi mărimea. Numele fontului poate fi "Serif", "SansSerif",
"Monospaced" sau alt stil existent pe maşina virtuală Java respectivă. Stiluol poate fi
Font.PLAIN, Font.BOLD, Font.ITALIC sau Font.BOLD|Font.ITALIC.
Metode principale
public static Font getFont(Map attributes) - întoarce fontul care corespunde cel mai
bine setului de atribute specificat ca argument.
399
Severin Bumbaru
public String getFamily() - întoarce numele familiei de fonturi din care face parte acest
font.
public boolean isPlain() - indică dacă stilul caracterului este PLAIN (normal).
public boolean isBold() - indică dacă stilul caracterului este BOLD (aldin, îngroşat).
public boolean isItalic() - indică dacă stilul caracterului este ITALIC (cursiv).
public static Font getFont(String nm) - întoarce fontul din lista de proprietăţi a
sistemului. Ca parametru se dă numele proprietăţii.
public static Font decode(String str) - întoarce fontul pe care îl descrie argumentul
str. Dacă argumentul este null, se întoarce un font implicit.
public Font deriveFont(int style, float size) - întoarce un nou font, care să aibă
aceeaşi formă cu acesta, dar să aibă stilul şi mărimea specificate.
public Font deriveFont(float size) - întoarce un nou font, similar acestuia, dar cu
mărimea specificată.
public Font deriveFont(int style) - întoarce un nou font, similar acestuia, dar cu stilul
specificat.
Clasa Frame
public class Frame extends Window implements MenuContainer
Face parte din pachetul java.awt. Instanţele acestei clase sau ale subclaselor ei sunt folosite ca
ferestre pr4incipale ale aplicaţiilor. Un Frame este o fereastră care are chenar şi bară de titlu.
Este o fereastră de cel mai înalt nivel (top-level window), deci poate fi plasată direct pe
ecranul fizic. Generează evenimente de fereastră (din clasa WindowEvent), care pot fi
ascultate cu un WindowListener sau WindowAdapter. La partea superioară i se poate pune o
bară de meniu (MenuBar). Iniţial, cadrul este invizibil, dar poate fi făcut vizibil invocând
setVisible(true), iar dimensiunile se stabilesc prin setSize(int, int), ambele fiind
metode ale clasei Component.
400
Programarea orientata pe obiecte în limbajul Java
Constructori
public Frame() - construieşte un cadru (Frame) fără titlu.
Metode
public String getTitle() - întoarce titlul cadrului (cel din bara de titlu).
public MenuBar getMenuBar() - îmtoarce bara de meniu a acestui cadru, sau null, dacă ea
nu există.
public void setMenuBar(MenuBar mb) - setează pentru acest cadru bara de meniu mb.
public boolean isResizable() - indică dacă acest cadru poate fi redimensionat de către
utilizator (prin tragere cu mouse-ul). Implicit, toate instanţele clasei Frame sunt
redimensionabile.
public void setResizable(boolean resizable) - setează dacă acest cadru poate sau nu
să fie redimensionat de către utilizator.
public static Frame[] getFrames() - întoarce tabloul tuturor cadrelor (Frame) deschise
de o aplicaţie sau accesibile unui applet.
Clasa Frame moşteneşte şi toate metodele clasei Window, deci şi pe cele ale claselor
Container şi Component.
401
Severin Bumbaru
Clasa Graphics2D
public abstract class Graphics2D extends Graphics
Este noul context grafic, care extinde clasa Graphics, pentru a putea realiza desene mult mai
complexe. Pentru utilizarea acestei clase şi, deci, a aplica în Java tehnicile de grafică 2D,
recomandăm să se consulte documentaţia originală Java API şi capitolul 2D Graphics din
Tutorialul Java.
Clasa GridBagLayout
public class GridBagLayout extends Object implements LayoutManager2, Serializable
Clasa GridLayout
public class GridLayout extends Object implements LayoutManager, Serializable
Constructori
public GridLayout() - construieşte un GridLayout cu o singură linie de componente.
public GridLayout(int rows, int cols, int hgap, int vgap) - construieşte un nou
GridLayout, specificându-se numărul de linii şi de coloane şi spaţiile libere între componente
pe orizontală şi pe verticală.
Metode
Metodele interfeţei LayoutManager, la care se adaugă următoarele:
402
Programarea orientata pe obiecte în limbajul Java
Clasa Insets
public class Insets extends Object implements Cloneable, Serializable
Instanţele acestei clase conţin lăţimile marginilor libere ale containerelor (care nu conţin
componente). Marginea respectivă poate fi complet liberă, sau poate conţine o bordură sau un
titlu.
Câmpuri
public int top - marginea de sus
Constructor
public Insets(int top, int left, int bottom, int right) - construieşte o instanţă
a clasei Insetws, fiind specificate marginile de sus, stânga, jos şi dreapta.
Metode
Class Panel
public class Panel extends Container implements Accessible
Panoul este cea mai simplă clasă de containere. Un panou este un dreptunghi fără bordură pe
suprafaţa căruia se pot plasa diferite componente ale interfeţei grafice, inclusiv alte panouri.
403
Severin Bumbaru
Constructori
public Panel() - construieşte un nou panou, având gestionarul de poziţionare implicit
FlowLayout.
Metode
Clasa Point
public class Point extends Point2D implements Serializable
Fiecare instanţă a acestei clase conţine coordonatele (x, y) ale unui punct, exprimate prin
numere întregi.
Constructori
public Point() - construieşte un punct de coordonate (0, 0).
Metode principale
public double getX() - întoarce coordonata x.
public void translate(int dx, int dy) - efectuează o translaţie a acestui punct, astfel
că noile coordonate vor fi x+dx, y+dy.
Clasa Rectangle
Instanţele acestei clase sunt suprafeţe de formă dreptunghiulară, cu laturile dispuse orizontal
şi vertical, pentru care se dau următoarele elemente:
x, y - coordonatele colţului din stânga sus;
width - lăţimea dreptunghiului;
height - înălţimea dreptunghiului.
Valorile acestor elemente sunt exprimate prin numere întregi.
404
Programarea orientata pe obiecte în limbajul Java
Constructori
public Rectangle() - construieşte un dreptunghi cu coordonatele (0, 0) şi dimensiunile
nule.
public void setBounds(int x, int y, int width, int height) - setează toate
elementele date ca parametri.
public void translate(int dx, int dy) - translatează dreptunghiul, astfel că noile
coordonate devin (x+dx, y+dy).
405
Severin Bumbaru
public boolean contains(int x, int y) - indică dacă acest dreptunghi conţine punctul
de coordonate (x, y).
public boolean contains(int x, int y, int width, int height) - indică dacă acest
dreptunghi conţine dreptunghiul ale cărui elemente sunt specificate.
public Rectangle union(Rectangle r) - întoarce dreptunghiul cel mai mic care conţine
în întregime acest dreptunghi şi dreptunghiul r.
Clasa Window
public class Window extends Container implements Accessible
Instanţele acestei clase sunt ferestre de cel mai înalt nivel (pot fi plasate direct pe eranul
nativ), fără bordură şi fără bară de meniu. Trebuie să aibă drept proprietar un cadru, o
fereastră de dialog sau o altă instanţă a clasei Window.
Constructori
public Window(Frame owner) - se construieşte o nouă fereastră, având ca proprietar o
instanţă a clasei Frame.
Metode
Metodele clasei Container, la care se adaugă următoarele:
406
Programarea orientata pe obiecte în limbajul Java
public Component getFocusOwner() - dacă fereastra este activă, întoarce fereastra copil
care este deţine focalizarea.
Interfaţa LayoutManager
Defineşte interfaţa pentru clasele care gestionează poziţionarea componentelor în container.
407
Severin Bumbaru
Metode
public void addLayoutComponent(String name, Component comp) - adaugă la
container componenta comp cu numele name.
Interfaţa LayoutManager2
public interface LayoutManager2 extends LayoutManager
Interfaţă pentru clasele de gestionare a poziţionării care folosesc un obiect de restricţii (care
stabileşte restricţiile pe care trebuie să le satisfacă poziţionarea).
Metode
Are toate metodele interfeţei LayoutManager la care se adaugă următoarele metode:
408
Programarea orientata pe obiecte în limbajul Java
Clasa ActionEvent
public class ActionEvent extends AWTEvent
Eveniment semantic, care indică faptul că asupra unei componente a fost exercitată o acţiune
specifică acelei componente (de exemplu, un buton a fost apăsat). Cu ajutorul măştilor date
drept câmpuri statice finale se pot determina anumite caracteristici ale evenimentului produs.
De exemplu, câmpul ALT_MASK permite sa se determine dacă, în momentul generării
evenimentului, era apăsată tasta alt. Aceasta se obţine intersectănd masca cu valoarea
întoarsă de metoda getModifiers().
Câmpuri
public static final int SHIFT_MASK - mască pentru a detecta dacă tasta shift era
apăsată.
public static final int CTRL_MASK - mască pentru a detecta dacă tasta ctrl era apăsată
public static final int META_MASK - mască pentru a detecta dacă tasta meta era apăsată
public static final int ALT_MASK - mască pentru a detecta dacă tasta alt era apăsată.
Metode
Metodele clasei AWTEvent, la care se adaugă următoarele:
Clasa AdjustmentEvent
public class AdjustmentEvent extends AWTEvent
Instanţele acestei clase sunt evenimente, generate atunci când este ajustată o mărime specifică
sursei de eveniment respective.
Metode
public Adjustable getAdjustable() - întoarce obiectul ajustabil care a generat acest
eveniment.
409
Severin Bumbaru
public int getAdjustmentType() - întoarce tipul de ajustare, care poate fi unul din
următoarele: UNIT_INCREMENT, UNIT_DECREMENT, BLOCK_INCREMENT,
BLOCK_DECREMENT, TRACK.
public String paramString() - întoarce un şir, care conţine parametri necesari, în special,
la depanare.
Clasa ComponentAdapter
public abstract class ComponentAdapter extends Object implements ComponentListener
Clasă abstractă, servind drept prototip pentru realizarea claselor ascultătoare de evenimente
de componentă. Clasa conţine toate metodele interfeţei ComponentListener, dar aceste
metode nu fac nimic (au corpul vid). Penteru a crea un ascultător de evenimente de
componentă, se crează o subclasă a clasei ComponentAdapter, în care se redefinesc numai
metodele efectiv necesare.
Constructor
public ComponentAdapter()
Metode
Clasa ComponentEvent
public class ComponentEvent extends AWTEvent
Metode
public Component getComponent() - întoarce componenta care a generat evenimentul.
410
Programarea orientata pe obiecte în limbajul Java
Clasa InputEvent
public abstract class InputEvent extends ComponentEvent
Clasă abstractă, care constituie rădăcina ierarhiei de clase pentru evenimente de intrare. Are
ca subclase directe KeyEvent şi MouseEvent.
Conţine câmpuri statice finale, ale căror valori sunt măşti pentru recunoaşterea tipului de
eveniment. Conţine, de asemenea, metode pentru recunoaşterea diferitelor situaţii existente la
generarea evenimentelor de intrare.
Metode
public boolean isShiftDown() - la generarea evenimentului, tasta SHIFT era apăsată.
public long getWhen() - întoarce momentul de timp când a avut loc evenimentul (dat de
ceasul sistemului).
public int getModifiers() - întoarce modificatorii (din care, folosind măştile) se pot
obţine caracteristicile evenimentului.
411
Severin Bumbaru
Clasa ItemEvent
public class ItemEvent extends AWTEvent
Eveniment semantic, care arată că un articol a fost selectat sau deselectat. Este generat de
către obiecte selectabile (de exemplu articolele de listă).
Metode
public ItemSelectable getItemSelectable() - întoarce articolul care a generat acest
eveniment.
Clasa KeyAdapter
public abstract class KeyAdapter extends Object implements KeyListener
Adaptor pentru ascultătoarele de evenimente de tastă. Clasa conţine toate metodele din
interfaţa KeyListener, dar aceste metode nu fac nimic (au corpul vid). Pentru a obţine un
ascultător de evenimente de tastă, se face o subclasă în care se redefinesc numai metodele
necesare în aplicaţia respectivă.
Constructor
public KeyAdapter()
Metode
public void keyTyped(KeyEvent e) - a avut loc apăsarea şi eliberarea imediată a unei
taste.
412
Programarea orientata pe obiecte în limbajul Java
Clasa KeyEvent
public class KeyEvent extends InputEvent
Eveniment care indică faptul că a fost acţionată o tastă. Evenimentul este generat de
componenta care este activă în momentul acţionării tastei.
Clasa conţine câmpuri statice finale, care permit identificarea tastei care a fost apăsate şi a
modificatorilor corespunzători (a tastelor auxiliare care erau apăsate în momentul când s-a
acţionar tasta generatoare de eveniment).
public static final int VK_CAPS_LOCK - tasta CapsLock (blocare pe litere majuscule)
413
Severin Bumbaru
public static final int VK_SLASH - tasta cu linia de fracţie (caracterul "-").
. . . . . . . . . . . . . similar VK_2, VK_3, VK_4, VK_5, VK_6, VK_7, VK_8, VK_9 respectiv
pentru cifrele 2, 3, 4, 5, 6, 7, 8, 9
public static final int VK_OPEN_BRACKET - tasta paranteză dreaptă deschisă (caracterul
"[" )
public static final int VK_BACK_SLASH - caracterul bară inversă (caracterul "\" )
414
Programarea orientata pe obiecte în limbajul Java
public static final int VK_NUM_LOCK - tasta NumLock (trecerea pe tastatura numerică)
. . . . . . . . . . . . . similar pentru tastele speciale F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
F13, F14, F15, F16, F17, F18, F19, F20, F21, F22, F23, F24
public static final int VK_KP_LEFT - tasta funcţională săgeată spre stânga
public static final int VK_LESS - tasta "mai mic" (caracterul "<" )
public static final int VK_GREATER - tasta "mai mare" (caracterul ">" )
public static final int VK_BRACELEFT - tasta "acolada stanga" (caracterul "{" )
public static final int VK_BRACERIGHT - tasta "acolada dreapta" (caracterul "}" )
415
Severin Bumbaru
public static final int VK_COLON - tasta "doua puncte" (caracterul ":" )
public static final int VK_DOLLAR - tasta cu simbolul valutar dolar (caracterul "$" )
public static final int VK_NUMBER_SIGN - tasta cu simbolul numeric (caracterul "#" )
Metode principale
public int getKeyCode() - întoarce codul tastei asociat cu acest eveniment de intrare (cel
care poate fi comparat cu unul din câmpurile statice finale de mai sus)
public static String getKeyText(int keyCode) - întoarce un şir, care conţine textul
de pe tasta al cărui cod este dat ca argument (un text cum ar fi "HOME", "F1" sau "A").
public boolean isActionKey() - indică dacă aceasta este o tastă de acţiune, adică o tastă
prin care utilizatorul cere o acţiune, cum ar fi tastele PageUp, PageDown, F1, F2 etc.
416
Programarea orientata pe obiecte în limbajul Java
Clasa MouseAdapter
public abstract class MouseAdapter extends Object implements MouseListener
Clasă abstractă, care poate fi extinsă pentru realizarea de ascultătoare de evenimente care
implementează interfaţa MouseListener. Clasa conţine toate metodele interfeţei
MouseListener, dar acestea nu fac nimic (au corpul vid). Extinzând această clasă,
programatorul poate redefini numai acele metode, de care are nevoie.
Constructor
public MouseAdapter()
Metode
Clasa MouseEvent
public class MouseEvent extends InputEvent
Clasa evenimentelor generate de o componentă, atunci când este acţionat mouse-ul, iar
cursorul de mouse se găseşte pe componenta respectivă. Există două categorii de astfel de
evenimente:
- evenimente de mouse propriu-zise: apăsarea sau eliberarea unui buton de mouse, click de
mouse, intrarea cursorului de mouse pe suprafaţa componentei sau ieşirea de pe aceasta.
- evenimente de mişcare a mouse-ului: tragerea mouse-ului pe suprafaţa componentei
(deplasarea cursorului acestuia în timp ce unul din butoane este ţinut apăsat), sau deplasarea
cursorului de mouse pe suprafaţa componentei (fără să fie apăsat nici unul din butoanele de
mouse).
Evenimentele de mouse sunt ascultate folosind interfaţa MouseListener, iar cele de mişcare
a mouse-ului sunt ascultate prin interfaţa MouseMotionListener.
Pentru a se afla care buton a fost apăsat, se folosesc modificatorii obţinuţi prin metoda
getModifiers() şi măştile corespunzătoare din superclasa InputEvent.
Metode
Metodele superclaselor AWTEvent şi InputEvent, la care se adaugă următoarele:
417
Severin Bumbaru
public int getX() - întoarce coordonata x a cursorului de mouse din momentul producerii
evenimentului, în sistemul de coordonate al componentei sursă.
public int getY() - întoarce coordonata y a cursorului de mouse din momentul producerii
evenimentului, în sistemul de coordonate al componentei sursă.
public void translatePoint(int dx, int dy) - tranlatează punctul în care se găsea
cursorul de mouse în momentul producerii evenimentului, adăugând la cele două coordonate
valorile dx şi dy specificate.
public int getClickCount() - întoarce numărul de clickuri (se are în vedere că este
posibil să se facă succesiv, la intervale mici de timp, mai multe clickuri de mouse).
public boolean isPopupTrigger() - indică dacă acest eveniment de mouse este un trigger
de menu pop-up pentru platforma dată.
Clasa MouseMotionAdapter
public abstract class MouseMotionAdapter extends Object implements MouseMotionListener
Constructor
public MouseMotionAdapter()
Metode
Clasa TextEvent
public class TextEvent extends AWTEvent
Eveniment semantic, care indică faptul că a avut loc o modificare a textului conţinut în
componenta care a generat evenimentul.
418
Programarea orientata pe obiecte în limbajul Java
Metodă
Clasa WindowAdapter
public abstract class WindowAdapter extends Object implements WindowListener
Constructor
public WindowAdapter()
Metode
Clasa WindowEvent
public class WindowEvent extends ComponentEvent
Clasă de evenimente de nivel coborât, care indică faptul că a avut loc o modificare a stării
unei ferestre: fereastra s-a deschis, urmează să se închidă, s-a închis, s-a activat, s-a
dezactivat, s-a iconificat sau deiconificat.
Metode
Metodele superclasei ComponentEvent, la care se adaugă următoarea:
Interfaţa ActionListener
public interface ActionListener extends EventListener
419
Severin Bumbaru
Metodă
Interfaţa AdjustmentListener
public interface AdjustmentListener extends EventListener
Metodă
Interfaţa ComponentListener
public interface ComponentListener extends EventListener
Metode
public void componentResized(ComponentEvent e) - componenta şi-a modificat
dimensiunile.
420
Programarea orientata pe obiecte în limbajul Java
Interfaţa ItemListener
public interface ItemListener extends EventListener
Metodă
Interfaţa KeyListener
public interface KeyListener extends EventListener
Metode
public void keyTyped(KeyEvent e) - a avut loc apăsarea şi eliberarea imediată a unei
taste.
Interfaţa MouseListener
public interface MouseListener extends EventListener
În loc de a se crea o clasă de ascultare care implementează direct această interfaţă, se poate
crea o subclasă a clasei MouseAdapter.
421
Severin Bumbaru
Metode
public void mouseClicked(MouseEvent e) - metodă invocată când s-a produs un click de
mouse.
public void mousePressed(MouseEvent e) - metodă invocată când este apăsat unul din
butoanele de mouse.
Interfaţa MouseMotionListener
public interface MouseMotionListener extends EventListener
Metode
public void mouseDragged(MouseEvent e) - mouse-ul a fost "tras" (adică a fost deplasat
menţinând unul din butoane apăsat).
public void mouseMoved(MouseEvent e) - mouse-ul a fost mişcat (fără să fie apăsat nici
unul din butoane).
Interfaţa TextListener
public interface TextListener extends EventListener
Metodă
public void textValueChanged(TextEvent e) - metodă invocată când a avut loc o
modificare a textului.
422
Programarea orientata pe obiecte în limbajul Java
Interfaţa WindowListener
public interface WindowListener extends EventListener
Metode
public void windowOpened(WindowEvent e) - metodă invocată când fereastra a fost
deschisă.
Clasa AbstractButton
public abstract class AbstractButton extends JComponent implements ItemSelectable,
SwingConstants
Când este apăsat (se pune pe buton cursorul de mouse şi se apasă unul din butoanele de la
mouse), butonul generează un eveniment de acţiune. Numele acestui eveniment
(ActionCommand) este implicit identic cu textul de pe buton, dar el poate fi modificat prin
metoda setActionCommand şi poate fi aflat prin metoda getActionCommand.
423
Severin Bumbaru
Clasa conţine foarte multe metode, pentru cunoaşterea cărora recomandăm consultarea
documentaţiei Java API.
Constructor
public AbstractButton()
Metode principale
public String getText() - întoarce textul butonului.
public boolean isSelected() - indică dacă acest buton sau articol de meniu este selectat.
public void setSelected(boolean b) - setează pentru ascest buton sau articol de meniu
starea selectat sau neselectat.
public int getMnemonic() - întoarce mnemonica ataşată acestui buton (sau null, dacă ea
nu există).
424
Programarea orientata pe obiecte în limbajul Java
Clasa Box.Filler
public static class Box.Filler extends Component implements Accessible
Constructor
public Box.Filler(Dimension min, Dimension pref, Dimension max) - se creează
un BoxFiller, fiind specificate dimensiunile minimă, preferată şi maximă.
Metode
public void changeShape(Dimension min, Dimension pref, Dimension max) - se
modifică dimensiunile, fiind specificate noile dimensiuni (minimă, preferată şi maximă).
425
Severin Bumbaru
Clasa ButtonGroup
public class ButtonGroup extends Object implements Serializable
Instanţele clasei ButtonGroup nu sunt componente grafice propriu-zise, rolul lor fiind de a
creea grupuri de butoane. Toate butoanele conţinute într-un grup sunt legate logic între ele,
astfel că numai un singur buton din grup se poate găsi la un moment dat în starea "apăsat",
celelalte fiind în starea "eliberat". Când se apasă un buton, se eliberează automat butonul din
acelaşi grup, care era apăsat anterior.Se foloseşte, de regulă, la gruparea butoanelor din
clasele JRadioButton şi JRadioButtonMenuItem.
Constructor
public ButtonGroup()
Metode principale
public void add(AbstractButton b) - se adaugă la grup butonul specificat.
Clasa JApplet
public class JApplet extends Applet implements Accessible, RootPaneContainer
Este o versiune extinsă a clasei Applet, introdusă în Swing. Principala deosebire faţă de clasa
Applet este că, la fel ca la clasa JFrame, adăugarea de componente nu se face direct la applet.
Fiecare instanţă a clasei JApplet conţine un container numit contentPane, la care se adaugă
toqate celelalte componente. Referinţa la acest container se obţine prin metoda
getContentPane. Tot ca la JFrame, un JApplet poate avea bară de meniu, pusă prin metoda
SetJMenuBar().
Explicatii despre crearea şi utilizarea appleturilor se dau în secţiunea Applet-uri din acest
curs. Explicaţii mai ample se gasesc în secţiunea Writing Applets din Tutorialul Java.
Descrierea tuturor metodelor este dată în Java API.
426
Programarea orientata pe obiecte în limbajul Java
Clasa JButton
public class JButton extends AbstractButton implements Accessible
Orice instanţă a acestei clase este un buton cu o singură stare stabilă (starea normală). Dacă
butonul este apăsat (punând pe el cursorul de mouse şi apăsând butonul mouse-ului), el îşi
schimbă temporar starea şi aspectul, dar revine la starea normală imediat ce este eliberat.
Butonul poate conţine un text şi/sau o imagine (o pictogramă). Butonului i se poate ataşa un
text volant (ToolTip) şi o mnemonică (o tastă, a cărei apăsare are acelaşi efect cu apăsarea
butonului respectiv).
Constructori
public JButton() - creează un buton, care nu conţine text sau pictogramă.
public JButton(String text, Icon icon) - creează un buton, care va conţine textul şui
acţiunea specificate.
Metode
Metodele principale sunt cele ale clasei AbstractButton. Pentru a cunoaşte toate metodele, se
va consulta documentaţia Java API.
Clasa JCheckBoxMenuItem
public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants,
Accessible
Este un articol de meniu care se comportă la fel ca o casetă de validare (JCheckBox). Are
aspectul unui patrat în care apare un simbol de validare (V) dacă este selectat şi nu apare
427
Severin Bumbaru
acest simbol dacă este deselectat. Caseta de validare poate fi însoţită de un text sau/şi o
pictogramă.
Constructori
public JCheckBoxMenuItem() - construieşte un JChecBoxMenuItem fără pictogramă şi
text.
Metode principale
public boolean getState() - întoarce starea.
Clasa JComponent
public abstract class JComponent extends Container implements Serializable
Clasa de bază pentru toate componentele Swing, cu excepţia containerelor de cel mai înalt
nivel (JFrame, JDialog, JApplet). Orice JComponentă poate avea o bordură. Oricărei
JComponente i se poate ataşa un "text volant" ("ToolTip"), adică un text explicativ, care
apare dacă se pune cursorul de mouse pe componenta respectivă.
Clasa JComponent conţine numeroase câmpuri şi metode. Aici se dau numai metodele cele
mai frecvent folosite. Pentru o documentaţie completă se va consulta Java API.
Constructor
public JComponent()
428
Programarea orientata pe obiecte în limbajul Java
public void setVisible(boolean aFlag) - setează dacă această componentă este sau nu
vizibilă.
public void setEnabled(boolean enabled) - setează dacă această componentă poate sau
nu primi intrări de la utilizator.
429
Severin Bumbaru
public boolean isOpaque() - indică dacă această componentă este complet opacă.
public void setOpaque(boolean isOpaque) - setează dacă această componentă este sau
nu opacă.
public void repaint(long tm, int x, int y, int width, int height) -
redesenează dreptunghiul specificat, după tm milisecunde.
Clasa JDialog
public class JDialog extends Dialog implements WindowConstants, Accessible,
RootPaneContainer
Instanţele acestei clase sunt ferestre de dialog. Componentele nu se adaugă direct la fereastra
JDialog, ci la un container numit contentPane, conţinut în aceasta şi care poate fi obţinut
prin metoda getContentPane(). Fereastra JDialog poate avea şi bară de meniu.
Constructori
public JDialog() - construieşte o fereastră de dialog fără titlu şi fără a se specifica
proprietarul.
430
Programarea orientata pe obiecte în limbajul Java
Metode principale
Metodele clasei Dialog, la care se adaugă:
431
Severin Bumbaru
Clasa JEditorPane
public class JEditorPane extends JTextComponent
Clasă pentru realizarea panourilor de editare care suportă diferite formate de text, cum ar fi
text simplu, text HTML sau text RTF. Are subclasa JTextPane.
Class JFileChooser
public class JFileChooser extends JComponent implements Accessible
Instanţele clasei JFileChooser sunt selectoare de fişiere. Ele vizualizează pe ecran arborele
directoarelor şi fişierelor de pe disc şi permit selectarea intercactivă (cu mouse-ul) a fişierului
dorit. Clasa conţine un număr mare de câmpuri, constructori şi metode. Pentru o mai bună
documentare se recomandă a se consulta Java API şi capitolul How to use FileChoosers din
Tutorialul Java.
Constructori principali
public JFileChooser() - construieşte un selector de fişiere cu pointer către directorul
utilizatorului (users home directory).
432
Programarea orientata pe obiecte în limbajul Java
Clasa JFrame
public class JFrame extends Frame implements WindowConstants, Accessible,
RootPaneContainer
Este varianta Swing a clasei Frame din AWT, fiind o subclasă a acesteia. O deosebire
importantă între cele două clase este că, în clasa JFrame, componentele nu se mai adaugă
direct la fereastra (la frame), ci la un panou conţinut de aceasta, numit contentPane. O
referinţă la contentPane se obţine prin metoda getContentPane(). Operaţiile cu acest
contentPane (adăugarea şi eliminarea de componente, setarea gestionarului de poziţionare
etc) se fac folosind metodele clasei Container.
433
Severin Bumbaru
Constructori
public JFrame() - construieşte un JFrame, iniţial invizibil şi fără titlu.
public JMenuBar getJMenuBar() - întoarce bara de meniu (sau null, dacă nu există)
434
Programarea orientata pe obiecte în limbajul Java
Clasa JLabel
public class JLabel extends JComponent implements SwingConstants, Accessible
Constructori
public JLabel(String text, Icon icon, int horizontalAlignment) - se
construieşte un JLabel, care conţine textul şi imaginea specificate. Al treilea parametru
specifică modul în care se face alinierea pe orizontală şi poate fi una din următoarele:
SwingConstants.LEFT - aliniere la stânga
SwingConstants.CENTER - aliniere la centru
SwingConstants.RIGHT - aliniere la dreapta.
Metode principale
public String getText() - întoarce textul.
435
Severin Bumbaru
Clasa JMenu
public class JMenu extends JMenuItem implements Accessible, MenuElement
Instanţele acestei clase sunt meniuri, adică ferestre verticale, care conţin articole de meniu.
Întrucât clasa JMenu este o subclasă a JMenuItem, înseamnă că un JMenu poate fi el insuşi
un articol al altui menu (un JMenuItem). În acest fel, se pot crea meniuri cu structură
ierarhică (arborescentă).
Clasa conţine multe metode, dintre care se dau aici câteva mai frecvent utilizate. Pentru
documentare completă se va folosi Java API.
Constructori
public JMenu() - construieşte un meniu fără text.
public void setSelected(boolean b) - setează dacă acest meniu este sau nu selectat.
436
Programarea orientata pe obiecte în limbajul Java
public void insert(String s, int pos) - înserează pe poziţia pos un nou srticol de
meniu cu inscripţia s.
public JMenuItem insert(JMenuItem mi, int pos) - înserează pe poziţia pos articolul
de meniu mi.
public int getItemCount() - întoarce numărul de articole din acest meniu, inclusiv
separatorii.
public boolean isTopLevelMenu() - indică dacă acest meniu este de cel mai înalt nivel,
adică este ataşat direct la bara de meniu.
Clasa JMenuBar
public class JMenuBar extends JComponent implements Accessible, MenuElement
Instanţele aceste clase sunt folosite ca bare de meniu. Pentru a obţine un meniu cu bară, la
bara de meniu se adaugă diferite meniuri (instanţe ale clasei JMenu).
Vezi şi explicaţiile din secţiunea Meniuri cu bară a acestui curs.
Indicăm aici numai câteva metode mai frecvent utilizate. Pentru o documentare completă se
va consulta Java API.
Constructor
public JMenuBar()
437
Severin Bumbaru
Metode principale
public JMenu add(JMenu c) - se adaugă la această bară un nou meniu.
Clasa JMenuItem
public class JMenuItem extends AbstractButton implements Accessible, MenuElement
Instanţele acestei clase sunt articole de meniu. Un astfel de obiect se comportă la fel ca un
buton, numai că este plasat într-un meniu. Din această cauză, clasa JMenuItem extinde clasa
AbstractButton. Are ca subclase JCheckBoxMenuItem, JMenu şi JRadioButtonMenuItem.
Instanţele acestei clase se comportă ca butoane obişnuite (au o singură stare stabilă, iar la
apăsare generează un eveniment de acţiune).
Constructori
public JMenuItem() - construieşte un articol de meniu fără text sau pictogramă.
Metode principale
Metodele din clasa AbstractButton, la care se adaugă:
438
Programarea orientata pe obiecte în limbajul Java
public void setArmed(boolean b) - setează dacă articolul de meniu este sau nu "armat"
)dacă butonul de mouse este eliberat când cursorul este pe acest articol, se va genera
evenimentul de acţiune).
Class JOptionPane
public class JOptionPane extends JComponent implements Accessible
Clasa JoptionPane permite să se creeze cu uşurinţă ferestre de dialog frecvent utilizate, având
un format predefinit. În acest scop, classa conţine metode satatice pentru realizarea fiecărui
tip de fereastră.
Clasa conţine un mare număr de câmpuri, constructori şi metode, dintre care vom da aici
numai câteva metode statice frecvent utilizate. Pentru documentare completă recomandăm să
se consulte Java API.
439
Severin Bumbaru
440
Programarea orientata pe obiecte în limbajul Java
Clasa JPanel
public class JPanel extends JComponent implements Accessible
Clasa JPanel din Swing are un rol similar cu clasa Panel din AWT. Instanţele acestei clase
sunt containere, care apar pe ecran ca nişte suprafeţe dreptunghiulare, pe care se pot plasa
diferite componente, aranjate cu ajutorul unui gestionar de poziţionare. Având în vedere că
clasa JPanel extinde clasa JComponent, panourile din această clasă pot avea şi bordură.
Constructori principali
public JPanel() - construieşte un JPanel cu gestionarul de poziţionare implicit
(FlowLayout).
Metode principale
Cele moştenite de la superclase
Class JPasswordField
public class JPasswordField extends JTextField
Instanţele acestei clase sunt câmpuri de text folosite la introducerea parolei. Se deosebesc de
JTextField prin faptul că parola introdusă în câmp de la tastatură nu este vizibilă pe ecran,
fiind substituită printr-un şir de caractere de înlocuire. Caracterul de înlocuire implicit este '*',
Constructori
public JPasswordField() - construieşte un câmp de parolă fără text şi cu lungimea 0
coloane.
Metode principale
public void setEchoChar(char c) - se setează caracterul de ecou (care va înlocui în
câmpul de pe ecran caracterele efective ale parolei).
441
Severin Bumbaru
Clasa JProgressBar
public class JProgressBar extends JComponent implements SwingConstants, Accessible
Instanţele acestei clase sunt bare de progres, folosite în special gradul de realizare a unui
eveniment în curs de desfăşurare. Pe ecran, apare ca o bară a cărei lungime este proporţională
cu un număr întreg, într-un interval de variaţie dat. Bara poate fi orientată orizontal sau
vertical.
Constructori
public JProgressBar() - creează o bară de progres orientată orizontal, cu intervalul de
variaţie 0 .. 100.
public JProgressBar(int min, int max) - creează o bară de progres orientată orizontal,
cu intervalul de variaţie specificat.
public JProgressBar(int orient, int min, int max) - creează o bară de progres cu
orientarea şi intervalul de variaţie specificate.
442
Programarea orientata pe obiecte în limbajul Java
public void setValue(int n) - setează valoarea (un număr întreg, cuprins în intervalul de
variaţie impus).
Clasa JRadioButtonMenuItem
public class JRadioButtonMenuItem extends JMenuItem implements Accessible
Articol de meniu care se comportă ca un buton radio (JRadioButton). Este un buton cu două
stări stabile (selectat şi deselectat). Mai multe butoane radio pot fi grupate folosind un
ButtonGroup, astfel încât, la un moment dat, un singur buton din grup se găseşte în starea
selectat.
Constructori
public JRadioButtonMenuItem() - construieşte un JRadioButtonMenuItem fără text şi fără
pictogramă.
443
Severin Bumbaru
Metode
Clasa JScrollBar
public class JScrollBar extends JComponent implements Adjustable, Accessible
Instanţele acestei clase sunt bare de defilare, care pot fi orientate orizontal sau vertical. Se
stabilesc orientarea, valorile minimă şi maximă, valoarea la care exte poziţionat cursorul şi
extensia. Orientarea poate fi una din valorile HORIZONTAL şi VERTICAL definite în
interfaţa Adjustable din pachetul java.awt.
Constructori
public JScrollBar(int orientation, int value, int extent, int min, int max)
- creează o bară de defilare, fiind specifiacte orientarea, valoarea, extensia, valoarea minimă
şi valoarea maximă.
444
Programarea orientata pe obiecte în limbajul Java
Clasa JScrollPane
public class JScrollPane extends JComponent implements ScrollPaneConstants, Accessible
Instanţele acestei clase sunt panouri cu bare de defilare orizontală şi/sau verticală. În Swing,
instanţele clasei JPanel nu au bare de defilare. Dacă dorim să-i punem bare de defilare, un
astfel de JPanel se pune ca singură componentă într-un JScrollPane. In loc de un panou, în
JScrollPane se poate pune orice altă componentă Swing, dar una singură.
Se pot adopta diferite "politici" privind modul de comportare al celor bare de defilare:
- pentru bara de defilare verticală:
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED - bara apare numai dacă este
necesară;
JScrollPane.VERTICAL_SCROLLBAR_NEVER - bara nu apare niciodată;
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS - bara apare întotdeauna;
- pentru bara de defilare orizontală:
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED - bara apare numai dacă
este necesară;
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER - bara nu apare niciodată;
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS - bara apare întotdeauna.
Aceste constante se folosesc ca parametri în constructori şi metode, unde se cer "politicile"
adoptate pentru comportamentul barelor de defilare.
Constructori
public JScrollPane(Component view, int vsbPolicy, int hsbPolicy) - se
construieşte un JScrollPane, fiind specificate componenta pe care o vizualizează şi politicile
adoptate pentru comportările barelor de defilare verticală şi orizontală.
445
Severin Bumbaru
Class JSeparator
public class JSeparator extends JComponent implements SwingConstants, Accessible
Instanţele acestei clase sunt separatori folosiţi în meniuri, pentru a grupa articolele de meniu
în grupuri logice. În loc de a folosi în mod explicit această clasă, se pot utiliza metodele
addSeparator(), existente în clasele JMenu şi JPopupMenu.
Constructori
public JSeparator() - construieşte un separator orizontal.
Metode principale
public int getOrientation() - întoarce orientarea separatorului.
446
Programarea orientata pe obiecte în limbajul Java
Clasa JSplitPane
public class JSplitPane extends JComponent implements Accessible
Instenţele clasei JSplitPane sunt panouri scindate în două zone, fiecare din aceste zone
conţinând câte o singură componentă. Scindarea se poate face vertical sau orizontal.
Orientarea scindării este specificată prin una constantele JSplitPane.HORIZONTAL_SPLIT
sau JSplitPane.VERTICAL_SPLIT .
Constructori
public JSplitPane() - creează un JSplitPane, în care cele două componente sunt puse una
lângă alta, orizontal.
public int getDividerSize() - întoarce lăţimea divizorului dintre cele două zone
(exprimată în pixeli).
447
Severin Bumbaru
Clasa JTabbedPane
public class JTabbedPane extends JComponent implements Serializable, Accessible,
SwingConstants
Fiecare instanţă a clasei JTabbedPane realizează metaforic un "clasor", care conţine fişe
plasate una în spatele celeilalte. Fiecare astfel de "fişă" este o componentă a panoului şi are
plasat pe ea un "călăreţ" (tab) care seamănă cu un buton. La apăsarea acestui "călăreţ", fişa
respectivă iese deasupra celorlalte.
Clasa conţine numeroase câmpuri şi metode, astfel că pentru o mai bună documentare se
recomandă consultarea documentaţiei Java API. Este utilă, de asemenea, consultarea
capitolului How to Use Tabbed Panes din Tutorialul Java.
448
Programarea orientata pe obiecte în limbajul Java
Constructori
public JTabbedPane() - construieşte un JTabbedPane cu "călăreţii" plasaţi vizibil
(JTabbedPane.WRAP_TAB_LAYOUT) la partea superioară.
449
Severin Bumbaru
Clasa JTable
public class JTable extends JComponent
implements TableModelListener, Scrollable, TableColumnModelListener,
ListSelectionListener, CellEditorListener, Accessible
Clasa JTable permite crearea de componente sub formă de tabele bidimensionale editabile.
Pentru utilizarea acestei clase se recomandă consultarea documentaţiei Java API şi a
capitolului How to Use Tables din Tutorialul Java.
JTextPane
public class JTextPane extends JEditorPane
Subclasă a clasei JEditorPane. Clasă pentru realizarea panourilor de editare care permit să se
folosească stiluri diferite pentru fiecare paragraf.
450
Programarea orientata pe obiecte în limbajul Java
Clasa JToggleButton
public class JToggleButton extends AbstractButton implements Accessible
Este clasa generică pentru butoane cu două stări stabile: starea acţionat (selectat, închis,
validat) şi starea eliberat (neselectat, deschis, nevalidat). Are ca subclase JCheckBox şi
JRadioButton. La fiecare acţionare cu mouse-ul asupra unei instanţe a clasei JToggleBox sau
a subclaselor sale, butonul respectiv trece, din starea în care se gaseşte, în cealaltă stare. Dacă
nu se specifică altfel, starea iniţială implicită este "eliberat".
Constructori
public JToggleButton() - creează un buton fără text sai pictogramă.
Metode principale
Metodele principale sunt cele din clasa AbstractButton. Pentru o documentare completă se va
consulta Java API.
Clasa JWindow
public class JWindow extends Window implements Accessible, RootPaneContainer
Instanţele clasei JWindow sunt ferestre fără bară de titlu, care pot fi plasate oriunde pe ecran
(sunt ferestre de cel mai înalt nivel). Adăugarea componentelor nu se face direct la fereastră,
ci la un container conţinut în aceasta numit contentPane, care poate fi obţinut prin metoda
getContentPane().
451
Severin Bumbaru
Constructori
public JWindow() - construieşte o fereastră fără un proprietar specificat.
Metode principale
Metodele clasei Window din pachetul java.awt, la care se adaugă:
Clasa ChangeEvent
public class ChangeEvent extends EventObject
Evenimentele din această clasă semnalează schimbarea stării componentei sursă. Ele sunt
ascultate cu un ChangeListener.
Constructor
Clasa ListDataEvent
public class ListDataEvent extends EventObject
Eveniment care indică apariţia unor modificări într-o listă. Tipul evenimentului are valoarea
unuia din următoare câmpuri statice finale:
ListDataEvent.CONTENTS_CHANGED - s-a modificat conţinutul listei;
ListDataEvent.INTERVAL_ADDED - s-au adăugat la listă unul sau mai multe articole
452
Programarea orientata pe obiecte în limbajul Java
Constructor
public ListDataEvent(Object source, int type, int index0, int index1) - se
creează un ListDataEvent fiind specificate: obiectul sursă, tipul evenimentului (unul din cele
menţionate mai sus), indicii marginilor intervalului de articole în care s-a produs modificarea.
Metode
public int getType() - întoarce tipul evenimentului.
public int getIndex0() - întoarce indicele primului articol al intervalului în care s-a produs
modificarea
public int getIndex1() - întoarce indicele ultimului articol al intervalului în care s-a produs
modificarea
Clasa ListSelectionEvent
public class ListSelectionEvent extends EventObject
Eveniment care indică modificarea selecţiei dintr-o listă. Se consideră că schimbarea selecţiei
este limitată la un interval de articole.
Constructor
public ListSelectionEvent(Object source, int firstIndex, int lastIndex,
boolean isAdjusting) - creează un ListSelectionEvent, fiind specificate: sursa
evenimentului, indicii inceputului şi sfârşitului intervalului şi dacă acest eveniment este sau
nu unul dintr-o serie rapidă de evenimente.
Metode
public int getFirstIndex() - întoarce indicele primului articol din intervalul selectat.
public int getLastIndex() - întoarce indicele ultimului articol din intervalul selectat.
public boolean getValueIsAdjusting() - indică dacă acest eveniment face parte dintr-o
serie de evenimente care au loc rapid.
Clasa MenuEvent
public class MenuEvent extends EventObject
453
Severin Bumbaru
Evenimentele din această clasă se produc atunci când a avut loc o modificare într-un meniu:
obiectul sursă a fost pus, selectat sau eliminat.
Constructor
Clasa MouseInputAdapter
public abstract class MouseInputAdapter extends Object implements MouseInputListener
Constructor
public MouseInputAdapter()
Metode
Clasa PopupMenuEvent
public class PopupMenuEvent extends EventObject
Constructor
Interfaţa ChangeListener
public interface ChangeListener extends EventListener
454
Programarea orientata pe obiecte în limbajul Java
Metodă
Interfaţa ListDataListener
public interface ListDataListener extends EventListener
Metode
public void intervalAdded(ListDataEvent e) - invocată când a avut loc o adăugare
sau înserare de articole în listă
Interfaţa ListSelectionListener
public interface ListSelectionListener extends EventListener
Metodă
Interfaţa MenuListener
public interface MenuListener extends EventListener
Metode
public void menuSelected(MenuEvent e) - invocată când meniul este selectat.
455
Severin Bumbaru
Interfaţa MouseInputListener
public interface MouseInputListener extends MouseListener, MouseMotionListener
În loc de a crea o clasă care extinde direct această interfaţă, se recomandă extinderea clasei
MouseInputAdapter.
Metode
public void mouseClicked(MouseEvent e) - metodă invocată când s-a produs un click de
mouse.
public void mousePressed(MouseEvent e) - metodă invocată când este apăsat unul din
butoanele de mouse.
public void mouseMoved(MouseEvent e) - mouse-ul a fost mişcat (fără să fie apăsat nici
unul din butoane).
Interfaţa PopupMenuListener
public interface PopupMenuListener extends EventListener
456
Programarea orientata pe obiecte în limbajul Java
Metode
public void popupMenuWillBecomeVisible(PopupMenuEvent e) - invocată când meniul
pop-up devine vizibil.
457
Severin Bumbaru
Surse de documentare
1. Documentaţie originală
Pagina Web de la care puteţi porni căutarea documentaţiei originale oferite de firma Sun
Microsystems pentru utilizatorii limbajului Java este java.sun.com.
La realizarea aplicaţiilor şi miniaplicaţiilor Java, din această documentaţie veti folosi cel mai
mult descrierea claselor din specificatia API (Application Program Interface) a platformei
Java 2, intitulată "Java 2 Platform, Standard Edition, v1.4 API Specification" pe care o găsiţi
la adresa următoare:
java.sun.com/products/j2se/1.4/docs/api/index.html - la firma Sun Microsystems;
Celor care sunt conectaţi la intranetul Catedrei de Calculatoare şi Informatică Aplicată de
la Universitatea "Dunărea de Jos" din Galaţi le recomandăm să caute aceeaşi
documentaţie la adresa
http://lib.cs.ugal.ro/java/jdk140/api/index.html
Pentru studierea mai profundă a limbajului Java, puteţi folosi specificatia riguroasa a
acestuia, dată în documentul "Java Language Specification", care se găseşte la adresele
urmatoare:
java.sun.com/docs/books/jls/second_edition/html/j.title.doc.html .
2. Prodan A., Prodan M. Mediul Java pentru Internet. Editura Promedia Plus, Cluj-Napoca,
1997.
3. Norton P., Stanek W. Ghid de programare in Java. Editura Teora, Bucureşti, 1997
458
Programarea orientata pe obiecte în limbajul Java
5. Fraizer C., Bond J. Java API. Pachetele API java.applet si java awt. Editura Teora,
Bucureşti, 1998.
6. Lemay L., Cadenhead R. Java2 Fără profesor în 21 zile. Editura Teora, Bucureşti
7. Chan M.C., Griffith S.W., Iasi A.F. Java - 1001 secrete pentru programatori. Editura
Teora, Bucureşti
NOTA: cu excepţia cărţilor de la poziţiile 1, 6 si 7, care se bazează pe Java2 (JDK 1.2 sau
ulterioare), toate celelalte au la bază JDK 1.0. La trecerea de la 1.0 la 1.1 s-au produs destul
de multe modificări, în special în pachetele API.
459
Severin Bumbaru
ANEXA
Capitolul 1 460
Capitolul 2 460
Capitolul 3 461
Capitolul 4 470
Capitolul 5 485
Capitolul 6 490
Capitolul 7 502
Capitolul 8 502
Capitolul 9 502
Capitolul 10 504
Capitolul 11 509
Capitolul 12 519
Capitolul 1.
Fişierul PrimaAplicaţie.java
class PrimaAplicatie {
public static void main(String args[]) {
System.out.println("Prima noastra aplicatie a reusit!");
}
}
Capitolul 2.
Fişierul AfisareSiruri.java
class AfisareSiruri {
public static void main(String args[]) {
System.out.println("sirul 1");
System.out.println("sirul 2"); // se afiseaza sub sirul 1
460
Programarea orientata pe obiecte în limbajul Java
Capitolul 3.
Fişierul Repetare1.java
class Repetare1 {
public static void main(String argv[]) {
/* Se declara si initializeaza variabilele */
int k, n=12;
Fişierul Suma1.java
class Suma1 {
public static void main(String argv[]) {
int n=8, k;
double a=0.72, b=-0.1735, S;
/* Se calculeaza suma S */
S=0; k=0; // Se dau valorile initiale lui S si k
while (k<=n) {
461
Severin Bumbaru
/* Se afiseaza S */
System.out.println("Suma calculata este "+S);
}
}
Fişierul Serie1.java
class Serie1 {
public static void main(String argv[]) {
/* Se declara si se initializeaza variabilele din program */
double x=1.30726, S=0, t=1;
int k=0;
/* Se calculeaza suma S a seriei */
while (S+t!=S){
S+=t; // Se adauga termenul curent la suma partiala anterioara
k++; // Se trece la indicele termenului urmator
t*=x/k; // Se calculeaza valoarea termenului urmator
} // sfarsit ciclu
/* Se afiseaza rezultatul */
System.out.println("Suma seriei este "+S);
/* Avand in vedere ca suma S calculata mai sus este dezvoltarea
in serie Taylor a functiei matematice exponentiale exp(x),
pentru comparatie afisam si valoarea acestei functii
*/
System.out.println("Pentru comparatie: "+Math.exp(x));
}
}
Fişierul DeosCicluri.java
class DeosCicluri {
public static void main(String argv[]) {
int k=7;
/* Situatia cand corpul ciclului cu test initial nu se executa
nici o data
*/
while (k<0)
System.out.println("Aceast text nu trebuie sa se afiseze!");
462
Programarea orientata pe obiecte în limbajul Java
Fişierul Repetare2.java
class Repetare2 {
public static void main(String argv[]) {
/* Se declara si initializeaza variabilele */
int k, n=12;
Fişierul Suma2.java
class Suma2 {
public static void main(String argv[]) {
463
Severin Bumbaru
int n=8, k;
double a=0.72, b=-0.1735, S;
/* Se calculeaza suma S */
S=0; k=0; // Se dau valorile initiale lui S si k
do {
/* Se calculeaza termenul curent al sumei si se adauga la
valoarea precedenta a lui S
*/
S+=Math.sqrt(Math.pow(Math.sin(k*a+b),2)+
Math.pow(Math.cos(k*b-a),2));
k++; // se trece la valoarea urmatoare a lui k
} while (k<=n);
/* Se afiseaza S */
System.out.println("Suma calculata este "+S);
}
}
Fişierul Serie2.java
class Serie2 {
public static void main(String argv[]) {
/* Se declara si se initializeaza variabilele din program */
double x=1.30726, S=0, t=1;
int k=0;
/* Se calculeaza suma S a seriei */
do {
S+=t; // Se adauga termenul curent la suma partiala anterioara
k++; // Se trece la indicele termenului urmator
t*=x/k; // Se calculeaza valoarea termenului urmator
} while (S+t!=S);
/* Se afiseaza rezultatul */
System.out.println("Suma seriei este "+S);
/* Avand in vedere ca suma S calculata mai sus este dezvoltarea
in serie Taylor a functiei matematice exponentiale exp(x),
pentru comparatie afisam si valoarea acestei functii
*/
System.out.println("Pentru comparatie: "+Math.exp(x));
}
}
Fişierul TestFor.java
class TestFor {
public static void main(String arg[]) {
int k, n=9;
double a=2.7465,P;
464
Programarea orientata pe obiecte în limbajul Java
/* Forma de baza */
for(P=1,k=5; k<=n; k++)
P*=a+k;
System.out.println("P="+P);
Fişierul TestFor1.java
class TestFor1 {
public static void main(String argv[]) {
int j=0, n=7;
double x=0, a=1.2073;
465
Severin Bumbaru
Fişierul TestBreak.java
class TestBreak {
public static void main(String argv[]) {
double x, y, a=3.27;
466
Programarea orientata pe obiecte în limbajul Java
467
Severin Bumbaru
Fişierul TestCont.java
class TestCont {
public static void main(String argv[]) {
double a=3.25, x=1.17, s, t;
int k;
System.out.println("\nTestul 2");
ciclu1: for(int i=0; i<6; i++) {
System.out.println("Control 1: i="+i);
for(int j=3; j>0; j--) {
System.out.println("Control 2: i="+i+" j="+j);
if(i>1 && i<5) continue ciclu1;
System.out.println("Control 3: i="+i+" j="+j);
} // Sfarsitul ciclului interior
System.out.println("Control 4: i="+i);
} // Sfarsitul ciclului exterior
}
}
Fişierul TestExcept1.java
class TestExcept1 {
public static void main(String argv[]) {
int a=7, b=0, x;
System.out.println("Urmeaza o exceptie de impartire la zero");
468
Programarea orientata pe obiecte în limbajul Java
x=a/b;
System.out.println("S-a depasit locul de aparitie a exceptiei");
}
}
Fişierul TestExcept2.java
class TestExcept2 {
public static void main(String argv[]) {
int a=7, b=0, x;
try {
System.out.println("Urmeaza o exceptie de impartire la zero");
x=a/b;
System.out.println("S-a depasit locul de aparitie a
exceptiei");
} catch (ArithmeticException e1) {
System.out.println("S-a captat exceptia aritmetica:\n"+e1);
} catch (Exception e2) {
System.out.println("S-a captat exceptia:\n"+e2);
}
System.out.println("S-a trecut de instructiunea try");
System.out.println("Sfarsit normal al programului");
}
}
Fişierul TestExcept3.java
class TestExcept3 {
public static void main(String argv[]) {
int a=7, b=0, x;
try {
System.out.println("Urmeaza o exceptie de impartire la zero");
x=a/b;
System.out.println("S-a depasit locul de aparitie a
exceptiei");
} catch (Exception e) {
System.out.println("S-a captat exceptia:\n"+e);
}
System.out.println("S-a trecut de instructiunea try");
System.out.println("Sfarsit normal al programului");
}
}
Fişierul TestExcept4.java
class TestExcept4 {
469
Severin Bumbaru
Fişierul TestExcept5.java
class TestExcept5 {
public static void main(String argv[]) {
int a=7, b=1, x;
try {
System.out.println("Urmeaza o impartire intreaga");
x=a/b;
System.out.println("S-a efectuat impartirea fara sa apara "+
"o exceptie");
} catch (ArithmeticException e1) {
System.out.println("S-a captat exceptia e1:\n"+e1);
} catch (Exception e2) {
System.out.println("S-a captat exceptia e2:\n"+e2);
}
finally {
System.out.println("S-a executat secventa din clauza
finally");
}
System.out.println("S-a trecut de instructiunea try");
System.out.println("Sfarsit normal al programului");
}
}
Capitolul 4
Fişierul TestObject.java
470
Programarea orientata pe obiecte în limbajul Java
class TestObject {
public static void main(String argv[]) {
Object ob1=new Object(), ob2=new String("ABCD"), ob3="abcd", ob4=ob2;
Object ob5=new Object(), ob6=ob5;
String str1="ABCD", str2=str1;
System.out.println("ob1="+ob1);
System.out.println("ob2="+ob2);
System.out.println("ob3="+ob3);
System.out.println("str1="+str1);
System.out.println("Coduri de dispersie:");
System.out.println("ob1: "+ob1.hashCode());
System.out.println("ob5: "+ob5.hashCode());
System.out.println("ob2: "+ob2.hashCode());
System.out.println("str1: "+str1.hashCode());
System.out.println("ob4: "+ob4.hashCode());
System.out.println("ob3: "+ob3.hashCode());
System.out.println("Testarea egalitatii prin == si prin equals():");
471
Severin Bumbaru
eroare de compilare
*/
ob1=str2;
Fişierul TestStr.java
class TestStr {
public static void main(String args[]) {
String s1="ABCdef#@&", s2, s3, s4;
char ch1[];
byte tb[];
s2=new String("Un sir nou");
System.out.println("s1="+s1+" s2="+s2);
System.out.println("Lungimile sirurilor: s1="+s1.length()+
" s2="+s2.length());
System.out.println("Conversie in litere mari: s1="+s1.toUpperCase()+
" s2="+s2.toUpperCase());
System.out.println("Conversie in litere mici: s1="+s1.toLowerCase()+
" s2="+s2.toLowerCase());
ch1=s1.toCharArray();
System.out.print("s1 convertit in tablou de caractere: ");
for(int i=0; i<ch1.length; i++) System.out.print(ch1[i]);
System.out.println(); // trecere la linie noua
tb=s1.getBytes();
System.out.print("s1 convertit in tablou de octeti: ");
for(int i=0; i<tb.length; i++) System.out.print(tb[i]+" ");
System.out.print("\nAcelasi tablou convertit in caractere: ");
for(int i=0; i<tb.length; i++) System.out.print((char)tb[i]);
System.out.println();
s3=String.copyValueOf(ch1);
System.out.println("Sirul obtinut din tabloul de caractere: "+s3);
System.out.println("Comparatii de siruri: "+s1.compareTo(s3)+" "+
472
Programarea orientata pe obiecte în limbajul Java
s1.compareTo(s2)+" "+s2.compareTo(s1));
System.out.println("s1==s3: "+(s1==s3)+" s1.equals(s3): "+
s1.equals(s3));
s4=s2.toUpperCase();
System.out.println("s2="+s2+" s4="+s4);
System.out.println("s2.equals(s4): "+s2.equals(s4)+
" s2.equalsIgnoreCase(s4): "+s2.equalsIgnoreCase(s4));
System.out.println("Subsirul lui s2 intre pozitiile 3 si 8: "+
s2.substring(3,8));
System.out.println("Subsirul lui s2 de la pozitia 3 la sfarsit: "+
s2.substring(3));
}
}
Fişierul TestStrB.java
class TestStrB {
public static void main(String args[]) {
StringBuffer stb1=new StringBuffer(), stb2;
String str1="abcdefg", str2;
double d=3.670283105E-3;
char tc1[]={'A','B','C'}, tc2[]=new char[10];
System.out.println("Lungimea si capacitatea initiala a stb1: "+
stb1.length()+" "+stb1.capacity());
stb1.append(str1);
stb1.append(d);
System.out.println("stb1="+stb1+"\nLungime: "+stb1.length()+
" Capacitate: "+stb1.capacity());
str2=stb1.toString(); // Este obligatorie folosirea metodei
toString()
System.out.println("Caracterul de indice 3: "+stb1.charAt(3));
stb2=stb1.insert(3, "PQRS");
System.out.println("Dupa insertia sirului PQRS:\nstb1="+stb1+
"\nstb2="+stb2);
stb1.append(tc1);
System.out.println("Dupa adaugarea tabloului tc1, stb1="+stb1);
System.out.println("Lungime: "+stb1.length()+" Capacitate: "+
stb1.capacity());
stb1.delete(3,25);
System.out.println("Dupa eliminarea zonei 3..25: "+stb1);
System.out.println("Lungime: "+stb1.length()+" Capacitate: "+
stb1.capacity());
stb1.reverse();
System.out.println("Dupa inversare stb1="+stb1);
stb1.getChars(1,4,tc2,3);
System.out.println("Dupa extragerea de caractere, lungimea "+
"tabloului tc2: "+tc2.length);
System.out.print("tc2=");
for(int i=0; i<tc2.length; i++) System.out.print(tc2[i]);
System.out.println();
}
}
473
Severin Bumbaru
Fişierul TestClass.java
class TestClass {
public static void main(String args[]) {
String s1="Primul sir", s2="al doilea sir";
Object obj=new Object();
Class cls1, cls2, cls3;
cls1=s1.getClass();
System.out.println(cls1.getName());
System.out.println(cls1.toString());
cls2=s2.getClass();
System.out.println(cls1.isAssignableFrom(cls2));
cls3=obj.getClass();
System.out.println(cls3);
System.out.println(cls1.isAssignableFrom(cls3));
System.out.println(cls3.isAssignableFrom(cls1));
System.out.println(cls1.getSuperclass());
System.out.println(cls3.getSuperclass());
System.out.println(cls1.isPrimitive());
}
}
Fişierul testBoolean.java
class TestBoolean {
public static void main(String args[]) {
boolean b1=true, b2=false, b3, b4;
Boolean bc1, bc2, bc3=Boolean.TRUE, bc4=Boolean.FALSE, bc5, bc6;
boolean tb1[]={true, false, true},
tb2[][]={{true,false},{false,true}};
Boolean tcb1[]={Boolean.TRUE,Boolean.FALSE};
Boolean tcb2[][]={{Boolean.FALSE,Boolean.TRUE},
{Boolean.TRUE,Boolean.FALSE}};
String str1, str2;
/* Se aplica constructorul Boolean(boolean value) */
bc1=new Boolean(b1);
bc2=new Boolean(b2);
/* Se afiseaza valorile variabilelor bc1 si bc2 convertite in siruri
(amintim ca in argumentele metodelor print() si println()
aplicarea
metodei toString() este implicita)
*/
System.out.println("bc1="+bc1+" bc2="+bc2);
/* Se afiseaza tablourile cu componente de tip primitiv boolean */
System.out.println("Tabloul tb1: ");
for (int i=0; i<tb1.length; i++) System.out.print(tb1[i]+" ");
System.out.println("\nTabloul bidimensional tb2: ");
for (int i=0; i<tb2.length; i++) {
for (int j=0; j<tb2[i].length; j++)
System.out.print(tb2[i][j]+" ");
System.out.println();
}
/* Se afiseaza tablourile cu componente din clasa Boolean */
474
Programarea orientata pe obiecte în limbajul Java
475
Severin Bumbaru
System.out.println("str1="+str1+" str2="+str2);
}
}
Fişierul TestInt.java
class TestInt {
public static void main(String args[]) {
Integer tI[]=new Integer[5], tI1[]=new Integer[5], a;
int tint[]=new int[5], i1;
String ts[]={"3298","-18765","+987692","-765b32","abcde"};
/* Testarea metodei statice parseInt(String) */
for(int i=0; i<5; i++) {
try {
tint[i]=Integer.parseInt(ts[i]);
}
catch(NumberFormatException e1) {
System.out.println("i="+i+" eroare in sirul: "+ts[i]);
}
catch(Exception e) {
System.out.println("i="+i+" Exceptie "+e);
}
}
System.out.print("tint=");
for(int i=0; i<tint.length; i++) System.out.print(tint[i]+" ");
System.out.println();
/* Incercam preluarea unui numar mai mare decat valoarea maxima
a unui numar de tip int, care este 2147483647
*/
try {
i1=Integer.parseInt("2147483648");
System.out.println("i1="+i1);
}
catch(Exception e) {
System.out.println("Numarul 2147483648 a produs exceptia:\n"+e);
}
/* Testarea metodei parseInt(String,int) */
System.out.println("38AB3FC7 din baza 16 este: "+
Integer.parseInt("38AB3FC7",16));
System.out.println("-38AB3FC7 din baza 16 este: "+
Integer.parseInt("-38AB3FC7",16));
System.out.println("6423651 din baza 7 este: "+
Integer.parseInt("6423651",7));
System.out.println("7GHA din baza 18 este: "+
Integer.parseInt("7GHA",18));
System.out.println("-AC7D din baza 16 este: "+
Integer.parseInt("-AC7D",16));
/* Incercam preluarea unui numar hexazecimal mai mare de 7FFFFFFF */
try {
i1=Integer.parseInt("80000000",16);
} catch (Exception e) {
System.out.println(e);
}
/* Afisarea aceluiasi numar in diferite baze de numeratie: */
i1=1986895879;
for(int i=2; i<=18; i+=4)
476
Programarea orientata pe obiecte în limbajul Java
Fişierul testDouble.java
class TestDouble {
public static void main(String args[]) {
Double tD[]=new Double[6], a;
double td[]=new double[6];
String ts[]={"3.2987","-187.659068","+9.87692413","8.0764382e-7",
"-238.0729456E8","4.687b53e7"};
477
Severin Bumbaru
long b;
/* Testarea metodei statice parseDouble(String) */
for(int i=0; i<6; i++) {
try {
td[i]=Double.parseDouble(ts[i]);
}
catch(NumberFormatException e1) {
System.out.println("i="+i+" eroare in sirul: "+ts[i]);
}
catch(Exception e) {
System.out.println("i="+i+" Exceptie "+e);
}
}
System.out.print("td=");
for(int i=0; i<td.length; i++) System.out.print(td[i]+" ");
System.out.println();
/* Repetam acelasi procedeu, testand acum constructorul
Double(String)
*/
for(int i=0; i<6; i++) {
try {
tD[i]=new Double(ts[i]);
}
catch(NumberFormatException e1) {
System.out.println("i="+i+" eroare in sirul: "+ts[i]);
}
catch(Exception e) {
System.out.println("i="+i+" Exceptie "+e);
}
}
System.out.print("tD=");
for(int i=0; i<tD.length; i++) System.out.print(tD[i]+" ");
System.out.println();
/* Pentru un singur obiect din clasa Integer aplicam toate metodele
de conversie in valoare de tip primitiv
*/
a=new Double(73416.853209);
System.out.println("byte: "+a.byteValue());
System.out.println("short: "+a.shortValue());
System.out.println("int: "+a.intValue());
System.out.println("long: "+a.longValue());
System.out.println("float: "+a.floatValue());
System.out.println("double: "+a.doubleValue());
/* Forma interna (binara) a numarului */
b=Double.doubleToLongBits(a.doubleValue());
System.out.println("Reprezentarea interna a numarului real
precedent:");
System.out.println("in binar: "+Long.toBinaryString(b));
System.out.println("in hexazecimal: "+Long.toHexString(b));
}
}
Fişierul TestChar.java
class TestChar {
public static void main(String args[]) {
478
Programarea orientata pe obiecte în limbajul Java
Fişierul InitTab1.java
class InitTab1 {
public static void main(String args[]) {
int a=27, b=-15, c[]={-3,72,-21}, d=-5, e[]={231,-98};
String s1="un sir", ts1[]={"sirul 0","sirul 1","sirul 2"},
s2="alt sir";
float[] u={-1.24076f, 0.03254f, 27.16f}, v={2.7698E-12f,-3.876e7f};
double aa[]=new double[3], bb[]={3.76,-2.879};
String str[]=new String[2], s3="abcdef1234";
long []k={187658954760876535L, 786432098764319051L},
m={598028476093172L, -9892865429740341L, 92765201};
Object tab1[]=new Object[2],tab2=new String[3],
tab3[]={"aaa","bbb","ccc"};
System.out.println("Valorile variabilelor simple:");
System.out.println("a="+a+" b="+b+" d="+d);
System.out.println("s1="+s1+" s2="+s2+" s3="+s3);
System.out.println("Lungimile tablourilor:");
System.out.println("c: "+c.length+" e: "+e.length+" ts1:
"+ts1.length);
System.out.println("u: "+u.length+" v: "+v.length+" str:
"+str.length);
System.out.println("aa: "+aa.length+" bb: "+bb.length);
479
Severin Bumbaru
Fişierul Tab1.java
class Tab1 {
public static void main(String args[]) {
double a[]={3.276, -5.83, 12.8}, b[];
String ts1[]={"aa", "bb"},ts2[];
/* Variabilelor b si ts1 li se atribuie ca valori referinte la
tablouri
existente, apoi se afiseaza
*/
b=a;
ts2=ts1;
System.out.println("Dupa atribuirile b=a si ts2=ts1:");
for (int i=0; i<a.length; i++)
System.out.println("i="+i+" a[i]="+a[i]+" b[i]="+b[i]);
for (int i=0; i<ts1.length; i++)
System.out.println("i="+i+" ts1[i]="+ts1[i]+" ts2[i]="+ts2[i]);
/* Atribuim o alte valori lui b[0] si b[1], apoi afisam tablourile a
si b
*/
b[0]=-12.7; b[1]=283.6;
System.out.println("Dupa ce s-au dat valori noi componentelor lui
b:");
for (int i=0; i<a.length; i++)
System.out.println("i="+i+" a[i]="+a[i]+" b[i]="+b[i]);
/* Atribuim variabilei b o referinta la un nou tablou. Constatam ca
valorile componentelor noului tablou sunt nule
*/
b=new double[4];
System.out.println("Dupa atribuirea unui nou tablou, b[] indica:");
for (int i=0; i<b.length; i++) System.out.print(b[i]+" ");
System.out.println();
/* Atribuim valori componentelor noului tablou indicat de b[] si
le afisam
*/
for (int i=0; i<b.length; i++) b[i]=Math.cos(0.7*i+0.23);
System.out.println("Noul tablou b dupa atribuirea de valori:");
for(int i=0; i<b.length; i++) System.out.print(b[i]+" ");
480
Programarea orientata pe obiecte în limbajul Java
Fişierul ConvTip1.java
class ConvTip1 {
public static void main(String args[]) {
int a[]={54, 23, -17}, b[];
double u[]={-6.237429, 83.17026}, v[];
String str1[]={"abc","def"}, str2[], str3[];
Object ob1, ob2, ob3, tob1[], tob2[];
/* Atribuire permisa: Object este superclasa pentru String */
tob1=str1;
/* Instructiunea urmatoare nu este permisa, deoarece String nu este
superclasa a lui Object (puteti verifica suprimand // din fata ei)
*/
// str2=tob1;
/* Deoarece stim ca tob1[] contine o referinta la un tablou String,
putem face o conversie explicita prin cast
*/
str2=(String[])tob1;
/* Tablourile a[] si u[] sunt considerate obiecte, deci variabilele
bsimple din clasa Object pot primi ca valori referinte la astfel
de
tablouri
*/
ob1=a;
ob2=u;
/* Chiar si un tablou de obiecte este considerat tot un obiect */
ob3=str1;
/* Atribuirile inverse nu sunt permise, deoarece clasele int[],
double[]
si String[] nu sunt superclase ale lui Object (puteti verifica!)
*/
// b=ob1;
// v=ob2;
// str3=ob3;
/* Avand in vedere ca stim ca atribuirile sunt corecte, ne putem
permite sa facem conversiile explicite prin cast
*/
b=(int[])ob1;
v=(double[])ob2;
str3=(String[])ob3;
481
Severin Bumbaru
Fişierul Tab2.java
class Tab2 {
public static void main(String args[]) {
double u=5.3127, v[]={1.25, -3.12, 2.6},
w[][]={{2.31, -4.15},{0.35,-12.6, -573.2},{10.9}};
int[] a={3,2,7}, b[]={{43,28,92,-6},{},{-1,17,29}};
char[][] h1={{'a','*'},{'+','$'}}, h2=new char[2][3], h3=new
char[4][];
String ts[][]={{"abc","defg"},{"AB","CD","EF"}};
System.out.println("Numarul de linii din tabloul w: "+w.length);
System.out.println("Numarul de componente din fiecare linie a "+
"tabloului w:");
for (int i=0; i<w.length; i++) System.out.print(w[i].length+" ");
System.out.println("\nComponentele tabloului w:");
for (int i=0; i<w.length; i++) { // Se afiseaza o linie
482
Programarea orientata pe obiecte în limbajul Java
Fişierul Tab2a.java
class Tab2a {
public static void main(String args[]) {
int a[][]={{-5,12,52},{8,-4},{},{105}}, b[]={31,-3,17,24};
System.out.println("Dupa initializare:\nTabloul bidimensional a:");
for(int i=0; i<a.length; i++) {
for(int j=0; j<a[i].length; j++) System.out.print(a[i][j]+" ");
System.out.println();
}
System.out.println("Tabloul unidimensional b:");
for(int i=0; i<b.length; i++) System.out.print(b[i]+" ");
System.out.println();
/* Se fac atribuiri de valori ale componentelor si de linii de tablou
*/
a[0][2]=2*b[1]-a[1][1];
a[2]=new int[3]; // s-a creat o noua linie pentru a[2]
a[2][0]=b[3];
a[2][2]=a[3][0];
a[3]=b; // S-a inlocuit vechea linie a[3] prin tabloul b
b=a[1]; // b indica acum spre linia a[1];
b[1]=88; // S-a modificat, de fapt, componenta a[1][1];
/* Se afiseaza noua stare */
System.out.println("Dupa atribuiri:\nTabloul bidimensional a:");
for(int i=0; i<a.length; i++) {
for(int j=0; j<a[i].length; j++) System.out.print(a[i][j]+" ");
System.out.println();
}
System.out.println("Tabloul unidimensional b:");
483
Severin Bumbaru
Fişierul Tab2b.java
class Tab2b {
public static void main(String args[]) {
Object ob1, ob2, tob2[][];
int ti1[][]={{0,1},{10,11,12,13},{20,21,22}},ti2[][];
String ts[][]={{"aa","ab"},{"ba","bb","bc"},{"ca","cb"}}, ts1[][];
/* Urmatoarele doua atribuiri sunt corecte */
ob1=ti1;
ob2=ts;
System.out.println("Tabloul indicat de ob1:");
for(int i=0; i<((int[][])ob1).length; i++) {
for(int j=0; j<((int[][])ob1)[i].length; j++)
System.out.print(((int[][])ob1)[i][j]+" ");
System.out.println();
}
/* Urmatoarele doua atribuiri nu sunt corecte, deoarece se trece de
la clasa la subclasa (incercati sa suprimati simbolul // )
*/
// ti2=ob1;
// ts1=ob2;
/* In urmatoarele doua atribuiri s-a recurs la conversii explicite,
stiind caror clase apartin efectiv obiectele referite de
variabilele
respective
*/
ti2=(int[][])ob1;
ts1=(String[][])ob2;
System.out.println("Tabloul ti2:");
for(int i=0; i<ti2.length; i++) {
for(int j=0; j<ti2[i].length; j++) System.out.print(ti2[i][j]+"
");
System.out.println();
}
System.out.println("Tabloul ts1:");
for(int i=0; i<ts1.length; i++) {
for(int j=0; j<ts1[i].length; j++) System.out.print(ts1[i][j]+"
");
System.out.println();
}
/* In atribuirea urmatoare, castul este aplicat corect sub aspect
sintactic, deci nu se semnaleaza o eroare la compilare, dar va
apare o exceptie la executie, deoarece in mod efectiv ob2 nu este
o referinta la int[][] ci la String[][]
*/
try {
ti2=(int[][])ob2;
}
catch(Exception e) {
System.out.println("S-a generat exceptia: "+e);
}
484
Programarea orientata pe obiecte în limbajul Java
tob2=ts;
ts1=(String[][])tob2;
}
}
Fişierul TabEterogen.java
class TabEterogen {
public static void main(String args[]) {
/* Construirea structurii */
Object tab[]=new Object[3];
tab[0]=new int[3];
((int[])tab[0])[0]=-87;
((int[])tab[0])[1]=23;
((int[])tab[0])[2]=164;
tab[1]=new double[2];
((double[])tab[1])[0]=32.164;
((double[])tab[1])[1]=-6.237;
tab[2]=new Object[3];
((Object[])tab[2])[0]=new String("abcd");
((Object[])tab[2])[1]=new String("PQRS");
((Object[])tab[2])[2]=new char[2];
((char[])((Object[])tab[2])[2])[0]='@';
((char[])((Object[])tab[2])[2])[1]='$';
/* Afisarea unor componente din structura */
System.out.println(((Object[])tab[2])[0]);
for(int i=0; i<3; i++)
System.out.print(((int[])tab[0])[i]+" ");
System.out.println();
for(int i=0; i<2; i++)
System.out.print(((char[])((Object[])tab[2])[2])[i]+" ");
System.out.println();
}
}
Capitolul 5
Fişierul TestRecursii.java
class Recursii {
/* Metode recursive */
public static long factorial(int n) throws Exception {
if(n<0) throw new Exception("factorial(n): n<0");
if(n==0) return 1;
return n*factorial(n-1);
}
public static long fibonacci(int n) throws Exception {
if(n<0) throw new Exception("fibonacci(n): n<0");
485
Severin Bumbaru
if(n==0) return 0;
if(n==1) return 1;
return fibonacci(n-1)+fibonacci(n-2);
}
class Iteratii {
public static long factorial(int n) throws Exception {
long fact;
if(n<0) throw new Exception("factorial(n): n<0");
fact=1;
for(int i=2; i<=n; i++) fact*=i;
return fact;
}
public static long fibonacci(int n) throws Exception {
if(n<0) throw new Exception("fibonacci(n): n<0");
if(n==0) return 0;
if(n==1) return 1;
long a=0, b=1;
for(int i=2; i<=n; i++) {
b+=a;
a=b-a;
}
return b;
}
}
class TestRecursii {
public static void main(String args[]) {
int n;
if(args.length==0) {
System.out.println("Nu ati dat ca parametru un numar intreg");
System.exit(1);
}
n=Integer.parseInt(args[0]);
try {
long fact1, fact2, fib1, fib2;
long time1, time2;
486
Programarea orientata pe obiecte în limbajul Java
Fişierul TestComplex.java
class TestComplex {
public static void main(String args[]) {
Complex u=new Complex(1,1), v=new Complex(1,-1),
w=new Complex(-1,1), z=new Complex(-1,-1);
Complex x=new Complex(1,1), y;
/* Testarea redefinirii metodei toString() din clasa Object */
System.out.println("u="+u+" v="+v);
487
Severin Bumbaru
488
Programarea orientata pe obiecte în limbajul Java
try {
/* Calcularea expresiei (2*u+w-3)/v */
a=(((Complex.inmultitCu(2,u)).plus(w)).minus(3)).impartitLa(v);
System.out.println("Valoarea expresiei finale: "+a);
Fişierul Referinţe.java
class Referinte {
public static void main(String argv[]) {
String a="un sir", b=new String(a), c, d, e;
c=new String(b);
d=b;
e=c;
System.out.println("a="+a+" b="+b+" c="+c);
System.out.println("d="+d+" e="+e);
489
Severin Bumbaru
Fişierul Finalizari.java
class ProbaFinaliz {
private int a;
public ProbaFinaliz(int m) {
a=m;
System.out.println("S-a construit obiectul "+a);
}
class Finalizari {
public static void main(String args[]) {
ProbaFinaliz pf1=new ProbaFinaliz(1), pf2=new ProbaFinaliz(2);
pf1=null;
System.out.println("S-a anulat referinta pf1");
pf2=null;
System.out.println("S-a anulat referinta pf2");
System.out.println("Se incheie executia aplicatiei");
}
}
Capitolul 6
Fişierul Cercuri.java
class Cerc1 {
static final double PI=3.141592653589793;
double r;
double arie() {
return PI*r*r;
}
double circumferinta() {
return 2*PI*r;
}
}
490
Programarea orientata pe obiecte în limbajul Java
class Cerc2 {
static final double PI=3.141592653589793;
class Cercuri {
public static void main(String args[]) {
double r1=1, r2=7.32;
Cerc1 c1=new Cerc1(), c2=new Cerc1();
c1.r=r1; c2.r=r2;
System.out.println("Folosind metodele de instanta din clasa Cerc1:");
System.out.println("Pentru cercul c1: aria="+c1.arie()+
" circumferinta="+c1.circumferinta());
System.out.println("Pentru cercul c2: aria="+c2.arie()+
" circumferinta="+c2.circumferinta());
System.out.println("Folosind metodele statice din clasa Cerc2:");
System.out.println("Pentru raza r1: aria="+Cerc2.arie(r1)+
" circumferinta="+Cerc2.circumferinta(r1));
System.out.println("Pentru raza r2: aria="+Cerc2.arie(r2)+
" circumferinta="+Cerc2.circumferinta(r2));
}
}
Fişierul Cercuri1.java
class Cerc {
public static final double PI=3.141592653589793;
private double r;
491
Severin Bumbaru
return PI*r*r;
}
class Cercuri1 {
public static void main(String args[]) {
double r1=1, r2=7.32;
/* Instantierea a doua cercuri */
Cerc c1=new Cerc(r1), c2=new Cerc(r2);
/* Utilizarea metodelor de instanta */
System.out.println("Utilizand metodele de instanta:");
System.out.println("Pentru cercul c1:\n aria="+c1.arie()+
" circumferinta="+c1.circumferinta()+" raza="+c1.raza());
System.out.println("Pentru cercul c2:\n aria="+c2.arie()+
" circumferinta="+c2.circumferinta()+" raza="+c2.raza());
System.out.println("Folosind metodele statice ale clasei Cerc:");
System.out.println("Pentru raza r1: aria="+Cerc.arie(r1)+
" circumferinta="+Cerc2.circumferinta(r1));
System.out.println("Pentru raza r2: aria="+Cerc.arie(r2)+
" circumferinta="+Cerc2.circumferinta(r2));
System.out.println("Pentru raza r=9.23 aria="+Cerc.arie(9.23)+
" circumferinta="+Cerc.circumferinta(9.23));
/* Acelasi calcul, facut calificand numele metodelor prin
referinte la instante ale clasei Cerc
*/
System.out.println("Aria cercului cu raza 9.23: "+
c1.arie(9.23)+" "+c2.arie(9.23));
}
}
Fişierul TestPers.java
class TestPers {
public static void main(String args[]) {
Pers p1=new Pers("Antoniu","Vasile",1981);
Pers p2, p3, p4, p5;
String s1="Vasilescu", s2="Mihai";
p2=new Pers(s1, s2, 1980);
p3=new Pers(s1, "Ion", 1982);
System.out.println("Numele persoanei p1: "+p1.nume());
System.out.println("Prenumele persoanei p2: "+p2.prenume());
System.out.println("Anul nasterii persoanei p3: "+p3.anNastere());
}
}
492
Programarea orientata pe obiecte în limbajul Java
Fişierul TestExceptie.java
class Fact {
public static double factorial(int n) throws ExceptieDomeniuFactorial {
if(n<0) throw new ExceptieDomeniuFactorial();
if(n==0) return 1;
return n*factorial(n-1);
}
}
class TestExceptie {
public static void main(String args[]) {
int n;
double fact;
if(args.length==0) {
System.out.println("Nu ati dat numarul intreg n ca parametru");
System.exit(1);
}
try {
n=Integer.parseInt(args[0]);
fact=Fact.factorial(n);
System.out.println("Factorialul este "+fact);
}
catch(NumberFormatException e1) {
System.out.println("Parametrul nu are format corect de numar
intreg");
}
catch(ExceptieDomeniuFactorial e2) { // se capteaza exceptia nou
creata
System.out.println("Nu se poate calcula factorial din numar
negativ");
}
catch(Exception e) {
System.out.println("S-a produs exceptia: "+e);
}
}
}
Fişierul Persoana1.java
493
Severin Bumbaru
Fişierul Student1.java
494
Programarea orientata pe obiecte în limbajul Java
Fişierul TestStud1.java
class TestStud1 {
public static void main(String args[]) {
Persoana1 p1=new Persoana1("Vasiliu","George",1981), p2;
Student1 s1=new Student1(p1, "NIE",2,"2221a"),
s2=new Student1("Ionescu","Maria", 1980, "NIE", 2, "2322b"), s3;
Object ob1, ob2, ob3;
Persoana1 tabPers[]=new Persoana1[3];
ob1=s1;
ob2=p2=s2;
ob3=p1;
System.out.println("p1="+p1);
System.out.println("s1="+s1);
System.out.println("s2="+s2);
System.out.println("ob1="+ob1);
System.out.println("p2="+p2);
System.out.println("ob2="+ob2);
System.out.println("ob3="+ob3);
System.out.println("Numele lui p1: "+p1.nume());
System.out.println("Numele lui s1: "+s1.nume());
System.out.println("Numele lui ob1: "+((Persoana1)ob1).nume());
s3=(Student1)ob2;
System.out.println("s3="+s3);
System.out.println("In anul 2000, s3 are "+s3.varsta(2000)+" ani");
System.out.println("p2.hashCode()="+p2.hashCode());
System.out.println("s1.hashCode()="+s1.hashCode());
tabPers[0]=p1; tabPers[1]=s1; tabPers[2]=s2;
System.out.println("Tabloul de persoane tabPers contine:");
System.out.println("Clasa lui p1: "+p1.getClass().getName());
System.out.println("Clasa lui ob2: "+ob2.getClass().getName());
for(int i=0; i<tabPers.length; i++)
System.out.println(tabPers[i]);
}
}
495
Severin Bumbaru
Fişierul FiguriPlane.java
class FiguriPlane {
public static void main(String args[]) {
FiguraPlana f;
Cerc c;
Patrat p;
496
Programarea orientata pe obiecte în limbajul Java
double a;
/* Instructiunea urmatoare nu este corecta, deoarece o clasa
abstracta
nu poate fi instantiata; daca se elimina din fata // apare eroare
la compilare
*/
// f=new FiguraPlana();
/* Se valideaza parametrul din linia de comnada */
if(args.length==0) {
System.out.println("Dati ca parametru un numar real pozitiv");
System.exit(1);
}
try {
a=Double.parseDouble(args[0]);
if(a<=0) {
System.out.println("Numarul nu este pozitiv");
System.exit(2);
}
/* Se instantiaza clasele Cerc si Patrat */
c=new Cerc(a);
p=new Patrat(a);
/* Variabilei f din clasa abstracta FiguraPlana i se poate da ca
valoare o referinta catre o subclasa instantiabila
*/
f=new Cerc(2*a);
/* Aplicarea metodelor */
System.out.println("Cerc: raza="+c.raza()+" perimetrul="+
c.perimetru()+"\naria="+c.arie()+" diametrul="+c.diametru());
System.out.println("Patrat: latura="+p.latura()+" perimetrul="+
p.perimetru()+"\naria="+p.arie()+" diagonala="+p.diagonala());
/* Daca spre un obiect din clasa Cerc indica o variabila din
superclasa FiguraPlana, este necesara conversia de clasa pentru
invocarea metodelor care nu exista in superclasa
*/
System.out.println("Al doilea cerc: raza="+((Cerc)f).raza()+
" perimetrul="+ f.perimetru()+"\naria="+f.arie()+
" diametrul="+((Cerc)f).diametru()); }
catch(NumberFormatException e1) {
System.out.println("Nu ati respectat formatul de numar real");
}
catch(Exception e) {
System.out.println("S-a produs exceptia: "+e);
}
}
}
Fişierul Integrare.java
interface FunctieReala {
double f(double x);
}
497
Severin Bumbaru
interface Integrator {
double integrala(double inf, double sup, FunctieReala func);
}
498
Programarea orientata pe obiecte în limbajul Java
class Integrare {
public static void main(String args[]) {
IntegrareTrapeze inTrap;
FunctieReala f1, f2;
double inf, sup; // marginile intervalului de integrare
int nrPasi; // numarul de pasi de integrare
if(args.length<3) {
System.out.println("Utilizare: java Integrare inf sup nrPasi");
System.exit(1);
}
try {
inf=Double.parseDouble(args[0]);
sup=Double.parseDouble(args[1]);
nrPasi=Integer.parseInt(args[2]);
f1=new Functie1(2.73, 0.15, -0.21, 2.45);
f2=new Functie1(-3.527, -0.43, 0.16, 1.03);
inTrap=new IntegrareTrapeze(nrPasi);
System.out.println("Prima integrala:
"+inTrap.integrala(inf,sup,f1));
System.out.println("A doua integrala:
"+inTrap.integrala(inf,sup,f2));
inTrap.puneNrPasi(2*nrPasi);
System.out.println("Dupa dublarea numarului de pasi de
integrare:");
System.out.println("Prima integrala:
"+inTrap.integrala(inf,sup,f1));
System.out.println("A doua integrala:
"+inTrap.integrala(inf,sup,f2));
}
catch(NumberFormatException e1) {
System.out.println("Nu ati introdus corect parametrii
aplicatiei");
}
catch(Exception e) {
System.out.println("S-a produs exceptia: "+e);
}
}
}
Fişierul Integrare1.java
interface FunctieReala {
double f(double x);
}
499
Severin Bumbaru
interface Integrator {
double integrala(double inf, double sup, FunctieReala func);
}
class Integrare1 {
public static void main(String args[]) {
Integrare1 int1=new Integrare1();// Instanta a acestei aplicatii
// necesara la utilizarea claselor interioare
IntegrareTrapeze inTrap;
FunctieReala f1, f2;
double inf, sup; // marginile intervalului de integrare
int nrPasi; // numarul de pasi de integrare
if(args.length<3) {
System.out.println("Utilizare: java Integrare inf sup nrPasi");
System.exit(1);
}
try {
inf=Double.parseDouble(args[0]);
sup=Double.parseDouble(args[1]);
nrPasi=Integer.parseInt(args[2]);
f1=int1.new Functie1(2.73, 0.15, -0.21, 2.45);
f2=int1.new Functie1(0.46, -1.23, 1.4, -0.78);
inTrap=new IntegrareTrapeze(nrPasi);
System.out.println("Prima integrala:
"+inTrap.integrala(inf,sup,f1));
500
Programarea orientata pe obiecte în limbajul Java
501
Severin Bumbaru
Capitolul 7
Capitolul 8
Capitolul 9
Fişierul TestRGB.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
class TestRGB {
static AF af=new AF();
static IUG iug=new IUG("Alegerea culorii in sistemul RGB"+
" (Red, Green, Blue)");
502
Programarea orientata pe obiecte în limbajul Java
AjustCuloare(String culoare) {
super(BoxLayout.X_AXIS);
add(new JLabel(culoare));
add(Box.createHorizontalGlue());
ajustare=new JSlider(JSlider.HORIZONTAL, 0, 255, 0);
ajustare.setMajorTickSpacing(50);
ajustare.setMinorTickSpacing(10);
ajustare.setPaintTicks(true);
ajustare.addChangeListener(this);
add(ajustare);
add(Box.createHorizontalStrut(5));
valoare=new JTextField("0",4);
valoare.setHorizontalAlignment(JTextField.RIGHT);
valoare.setEditable(false);
valoare.setBackground(Color.white);
valoare.setMaximumSize(valoare.getMinimumSize());
add(valoare);
add(Box.createHorizontalStrut(5));
}
503
Severin Bumbaru
Capitolul 10
Fişierul DouaTexte.java
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
Fişierul PrimApplet.java
/* Un prim applet */
import java.awt.*;
import java.applet.*;
504
Programarea orientata pe obiecte în limbajul Java
Fişierul PrimApplet.html
<html>
<head>
<title> Primul nostru applet </title>
</head>
<body>
</body>
</html>
Fişierul CBGroupAp.java
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
505
Severin Bumbaru
cb3.setBackground(Color.cyan);
lab.setBackground(Color.green);
lab.setAlignment(Label.CENTER);
setLayout(new BorderLayout());
add("North",lab);
add("West",cb1);
add("Center",cb2);
add("East",cb3);
setVisible(true);
}
Fişierul CBGroupAp.html
<html>
<head>
<title> Testarea claselor Checkbox si ChecboxGroup </title>
</head>
<body>
<applet CODE="CBGroupAp" WIDTH=180 HEIGHT=80>
Testarea claselor Checkbox si CheckboxGroup
</applet>
</body>
</html>
Fişierul TestFlowAp.java
506
Programarea orientata pe obiecte în limbajul Java
*/
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
507
Severin Bumbaru
ApasButon(TestFlowAp ap) {
this.ap=ap;
contor=0;
}
public void actionPerformed(ActionEvent e) {
String actiune=e.getActionCommand();
if(actiune.equals("Pune") && contor<20){
ap.add(ap.tlab[contor]);
contor++;
}
else if(actiune.equals("Elimina") && contor>0) {
contor--;
ap.remove(ap.tlab[contor]);
}
ap.fl.layoutContainer(ap);
ap.setVisible(true);
}
}
Fişierul TestFlow.html
<html>
<head>
<title> Testarea clasei FlowLayout </title>
</head>
<body>
<applet CODE="TestFlowAp" WIDTH=400 HEIGHT=250>
Testare FlowLayout
</applet>
508
Programarea orientata pe obiecte în limbajul Java
</body>
</html>
Capitolul 11
Fişierul TestFile.java
import java.io.*;
class TestFile {
public static void main(String args[]) throws Exception {
/* Fisier situat in directorul curent */
File fis1=new File("TestFile.java");
System.out.println("Calea absoluta: "+fis1.getAbsolutePath());
System.out.println("Fisierul exista: "+fis1.exists());
System.out.println("Pot citi fisier: "+fis1.canRead());
System.out.println("Pot scrie in fisier: "+fis1.canWrite());
System.out.println("Calea: "+fis1.getPath());
System.out.println("Parintele: "+fis1.getParent());
System.out.println("Este director: "+fis1.isDirectory());
System.out.println("Este fisier: "+fis1.isFile());
System.out.println("Ultima modificare: "+fis1.lastModified());
System.out.println("Lungimea fisierului: "+fis1.length());
System.out.println("Sub foorma de URL: "+fis1.toURL());
/* Director */
File dir1=new File("../surse");
System.out.println("Calea absoluta: "+dir1.getAbsolutePath());
System.out.println("Exista: "+dir1.exists());
System.out.println("Este director: "+dir1.isDirectory());
System.out.println("Se poate citi: "+dir1.canRead());
System.out.println("Se poate scrie: "+dir1.canWrite());
/* Fisier situat in alt director */
File fis2=new File("../surse/TestStudent.java");
System.out.println("Calea absoluta: "+fis2.getAbsolutePath());
System.out.println("Exista: "+fis2.exists());
System.out.println("Este director: "+fis2.isDirectory());
System.out.println("Se poate citi: "+fis2.canRead());
System.out.println("Se poate scrie: "+fis2.canWrite());
System.out.println("Parintele: "+fis2.getParent());
}
}
Fişierul TestFileInput.java
import java.io.*;
class TestFileInput {
public static void main(String args[]) {
byte b[]=new byte[30];
509
Severin Bumbaru
int n, n1;
long n2;
File fisier=null;
if(args.length==0) {
System.out.println("La lansarea in executie trebuie dat ca "+
"parametru numele unui fisier de text");
System.exit(0);
}
else {
fisier=new File(args[0]);
if(!fisier.exists()) {
System.out.println("Fisierul "+fisier+" nu exista!");
System.exit(0);
}
}
try {
/* Se deschide fisierul dat ca parametru si se conecteaza la
fluxul de intrare f1 */
FileInputStream f1=new FileInputStream(fisier);
System.out.println("S-a deschis fisierul "+fisier+
" conectat la fluxul f1");
System.out.println("Sunt disponibili "+f1.available()+
" octeti");
n1=f1.read();
System.out.println("Primul octet citit este "+n1+
" care este codul ASCII al caracterului "+(char)n1);
n2=f1.skip(3);
System.out.println("S-a sarit peste "+n2+" octeti");
n=f1.read(b);
System.out.println("S-au mai citit urmatorii "+n+" octeti:");
for(int i=0; i<n; i++) System.out.print(b[i]+" ");
System.out.println();
System.out.println("care sunt codurile ASCII ale urmatoarelor "+
" caractere:");
for(int i=0; i<n; i++) System.out.print((char)b[i]);
System.out.println();
System.out.println("In fluxul de intrare mai sunt disponibili "+
f1.available()+" octeti");
/* Descriptorul fluxului f1 este folosit pentru deschiderea
unui nou flux de intrare, f2. Acum fluxurile f1 si f2 sunt
conectate la acelasi fisier de intrare
*/
FileDescriptor fd=f1.getFD();
FileInputStream f2=new FileInputStream(fd);
System.out.println("S-a deschis fluxul f2, identic cu f1");
System.out.println("In fluxul f2 sunt disponibili "+
f2.available()+" octeti");
n2=f2.read(b,0,15);
System.out.println("S-au citit din f2 urmatorii "+n+" octeti:");
for(int i=0; i<n; i++) System.out.print((char)b[i]);
System.out.println();
System.out.println("In f1 sunt disponibili "+f1.available()+
" octeti, iar in f2 "+f2.available()+" octeti");
f2.close();
/* S-a inchis fisierul indicat de fluxul f2. In consecinta
nu mai pot fi folosite nici unul din fluxurile f2 si f1,
care au ca sursa acest fisier
*/
System.out.println("S-a inchis fisierul "+fisier);
510
Programarea orientata pe obiecte în limbajul Java
Fişierul TestFileReader.java
import java.io.*;
class TestFileReader {
public static void main(String args[]) {
try {
/* Se deschide fisierul si se citeste caracter cu caracter */
FileReader fin=new FileReader("TextProba.txt");
System.out.println("Numele canonic al codificarii textului: "+
fin.getEncoding());
int c;
while(fin.ready()) {
c=fin.read();
System.out.print((char)c);
}
System.out.println();
fin.close();
/* Se redeschide fisierul si se citeste intr-un singur tablou
de caractere
*/
fin=new FileReader("TextProba.txt");
char[] tab=new char[80];
int nrCar;
nrCar=fin.read(tab);
System.out.println("Numar caractere citite: "+nrCar);
for (int i=0; i<nrCar; i++) System.out.print(tab[i]);
System.out.println();
fin.close();
/* Se redeschide fisierul si se citeste pe portiuni, folosind
un tablou de lungime mai mica decat numarul de caractere
din fisier
*/
tab=new char[12];
fin=new FileReader("TextProba.txt");
while(fin.ready()) {
nrCar=fin.read(tab);
for(int i=0; i<nrCar; i++) System.out.print(tab[i]);
}
System.out.println();
fin.close();
}
511
Severin Bumbaru
catch(Exception e) {
System.out.println(e);
}
}
}
Fişierul TestFileOutput.java
import java.io.*;
class TestFileOutput {
public static void main(String args[]) {
FileOutputStream fout;
FileInputStream fin;
byte b[];
int k;
if(args.length==0) {
System.out.println("La lansare dati un text in linia de comanda");
System.exit(0);
}
try {
/* Se deschide fisierul pentru scriere */
fout=new FileOutputStream("ProbaScriere.txt");
for(int i=0; i<args.length; i++) {
/* parametrul args[i] se converteste in tablou de octeti */
b=(args[i]+" ").getBytes();
/* tabloul b astfel obtinut se scrie in fisier */
fout.write(b);
}
fout.close();
/* Dupa ce a fost inchis, acelasi fisier se deschide pentru citire
*/
fin=new FileInputStream("ProbaScriere.txt");
/* Se afiseaza continutul fisierului */
System.out.println("Fisierul contine:");
while((k=fin.read()) != -1) System.out.print((char)k);
System.out.println();
fin.close();
/* Se redeschide fisierul pentru scriere prin adaugare la coada */
fout=new FileOutputStream("ProbaScriere.txt",true);
/* Se scrie textul "\nText adaugat\n" */
fout.write(("\nText adaugat\n").getBytes());
fout.close();
/* Se deschide din nou fisierul pentru citire si se afiseaza.
De data aceasta, fin este fisierul din care se citeste, iar
fout este un nou flux de iesire, care se conecteaza la
iesirea standard al sistemului (la ecran) prin intermediul
descriptorului FileDescriptor.out
*/
fin=new FileInputStream("ProbaScriere.txt");
512
Programarea orientata pe obiecte în limbajul Java
fout=new FileOutputStream(FileDescriptor.out);
fout.write("\nA doua oara fisierul contine:\n".getBytes());
while((k=fin.read()) != -1) fout.write((byte)k);
fout.write('\n');
/* // Pentru comparatie, sub forma de comentariu se da
// programul de afisare obisnuit
System.out.println("\nA doua oara fisierul contine:");
while((k=fin.read()) != -1) System.out.print((char)k);
System.out.println();
*/
fin.close();
}
catch(Exception e) {
System.out.println("Exceptie: "+e);
}
}
}
Fişierul TestFileWriter.java
import java.io.*;
class TestFileWriter {
public static void main(String args[]) {
FileWriter fout;
FileReader fin;
String str;
int k;
if(args.length==0) {
System.out.println("La lansare dati un text in linia de comanda");
System.exit(0);
}
try {
/* Se deschide fisierul pentru scriere */
fout=new FileWriter("ProbaScriere.txt");
for(int i=0; i<args.length; i++) {
fout.write(args[i]);
fout.write(' ');
}
fout.close();
/* Dupa ce a fost inchis, acelasi fisier se deschide pentru citire
*/
fin=new FileReader("ProbaScriere.txt");
/* Se afiseaza continutul fisierului */
System.out.println("Fisierul contine:");
while((k=fin.read()) != -1) System.out.print((char)k);
System.out.println();
fin.close();
/* Se redeschide fisierul pentru scriere prin adaugare la coada */
fout=new FileWriter("ProbaScriere.txt",true);
513
Severin Bumbaru
Fişierul AccesDirect.java
import java.io.*;
class AccesDirect {
public static void main(String args[]) throws IOException {
/* Se deschide fisierul cu acces direct "Proba.fad"
in mod "rw" (citire si scriere)
*/
RandomAccessFile fisier=new RandomAccessFile("Proba.fad","rw");
long fp, fp1, fp2, k;
int i;
char c;
float f;
double d;
String s;
fp=fisier.getFilePointer();
System.out.println("Pozitia initiala a cursorului: "+fp);
/* Se scriu date in fisier incepand de la pozitia 0 */
fisier.writeInt(-176534);
fisier.writeDouble(176.5832987306);
fisier.writeChar('#');
fisier.writeUTF("Primul sir");
/* Se memoreaza si se afiseaza pozitia curenta a cursorului de
fisier
*/
fp1=fisier.getFilePointer();
System.out.println("Pozitia cursorului dupa prima scriere: "+fp1);
/* Se scriu in continuare date in fisier */
fisier.writeLong(157832490245L);
fisier.writeFloat(0.05682369543f);
fisier.writeBytes("Al doilea sir\n");
fp2=fisier.getFilePointer();
System.out.println("Pozitia cursorului dupa a doua scriere: "+fp2);
/* Se deplaseaza cursorul de fisier pe pozitia de la care a inceput
514
Programarea orientata pe obiecte în limbajul Java
Fişierul FisierDate.java
import java.io.*;
class FisierDate {
515
Severin Bumbaru
Fişierul FisierObiecte.java
516
Programarea orientata pe obiecte în limbajul Java
import java.io.*;
class FisierObiecte {
517
Severin Bumbaru
dout.writeObject(tabstr);
dout.writeObject(w);
518
Programarea orientata pe obiecte în limbajul Java
Capitolul 12
Fişierul Bondari.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public Bondari() {
super("Bondari");
nouBondar=new JButton("Creare nou bondar");
nouBondar.addActionListener(new CreareBondar());
getContentPane().add(nouBondar, BorderLayout.NORTH);
addWindowListener(new Iesire());
setSize(200,100);
setLocation(new Point(0,20));
setVisible(true);
}
public Bondar() {
perioada=new JSlider(JSlider.HORIZONTAL, 100, 500, 200);
perioada.setBorder(BorderFactory.createTitledBorder(
"Perioada saltului"));
perioada.setMajorTickSpacing(200);
perioada.setMinorTickSpacing(100);
perioada.setPaintTicks(true);
perioada.setPaintLabels(true);
amplitudine=new JSlider(JSlider.HORIZONTAL, 0, 40, 10);
amplitudine.setBorder(BorderFactory.createTitledBorder(
"Amplitudinea saltului"));
amplitudine.setMajorTickSpacing(20);
amplitudine.setMinorTickSpacing(2);
amplitudine.setPaintTicks(true);
amplitudine.setPaintLabels(true);
fereastra=new Fereastra();
fereastra.setSize(200,200);
fereastra.setBackground(Color.cyan);
fereastra.culoare=Color.red;
Box box=Box.createVerticalBox();
box.add(perioada);
519
Severin Bumbaru
box.add(amplitudine);
box.add(fereastra);
getContentPane().add(box);
addWindowListener(new SfarsitBondar(this));
setSize(200,350);
setVisible(true);
}
520
Programarea orientata pe obiecte în limbajul Java
bondar.setLocation(new Point(x,y));
Thread thread=new Thread(bondar);
thread.start();
}
} /* Sfarsitul clasei CreareBondar */
Fişierul Bondari1.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public Bondari1() {
super("Doi bondari in acelasi spatiu");
b1=new Bondar();
fir1=new Thread(b1);
b1.fir=fir1;
b2=new Bondar();
fir2=new Thread(b2);
b2.fir=fir2;
Box box1=Box.createHorizontalBox();
box1.add(b1);
box1.add(b2);
Box box2=Box.createVerticalBox();
fereastra=new Fereastra();
fir3=new Thread(fereastra);
fereastra.setSize(200,200);
fereastra.setBackground(Color.cyan);
box2.add(box1);
box2.add(fereastra);
Container cp1=getContentPane();
cp1.setLayout(new FlowLayout());
cp1.add(box2);
521
Severin Bumbaru
addWindowListener(new Iesire());
setSize(500,400);
setVisible(true);
cutiePostala=new CutiePostala();
fir1.start();
fir2.start();
fir3.start();
}
public Bondar() {
perioada=new JSlider(JSlider.HORIZONTAL, 100, 500, 200);
perioada.setBorder(BorderFactory.createTitledBorder(
"Perioada saltului"));
perioada.setMajorTickSpacing(200);
perioada.setMinorTickSpacing(100);
perioada.setPaintTicks(true);
perioada.setPaintLabels(true);
amplitudine=new JSlider(JSlider.HORIZONTAL, 0, 40, 10);
amplitudine.setBorder(BorderFactory.createTitledBorder(
"Amplitudinea saltului"));
amplitudine.setMajorTickSpacing(20);
amplitudine.setMinorTickSpacing(2);
amplitudine.setPaintTicks(true);
amplitudine.setPaintLabels(true);
Box box=Box.createVerticalBox();
box.add(perioada);
box.add(amplitudine);
getContentPane().add(box);
}
522
Programarea orientata pe obiecte în limbajul Java
g.fillRect(b1.x,b1.y,10,10);
g.setColor(Color.red);
g.drawRect(b1.x,b1.y,10,10);
g.setColor(Color.blue);
g.fillRect(b2.x,b2.y,10,10);
g.setColor(Color.red);
g.drawRect(b2.x,b2.y,10,10);
}
class CutiePostala {
boolean valoareNoua=false;
523