Anda di halaman 1dari 26

Appleturi Java (I)

Mirela Stefania Andronescu

Articolul de fata face parte dintr-un set de articole care se adreseaza atat celor familiarizati cu
programarea orientata pe obiecte cat si celor care nu cunosc limbaje OO. Voi aminti pe scurt in
1.1. caracteristicile unui limbaj orientat pe obiecte. Pentru mai multe detalii despre obiecte, recititi
articolele despre Java aparute in numerele trecute ale revistei noastre. In 1.2. voi prezenta
caracteristicile programarii orientate pe obiecte in Java si apoi vom trece la construirea de
exemple (un prim exemplu este 1.3.). Astfel veti invata din exemple Daca o anumita notiune nu
este clara la un moment dat, nu-i nimic, vom reveni asupra ei. Totul se va clarifica folosind
exemplele.
Exemplele prezentate pot fi compilate folosind Java Development Kit (JDK) care poate fi obtinut
free de la adresa www.sun.com, Microsoft Vijual J++ sau orice alt mediu de dezvoltare Java.
1.1. Introducere. Programarea orientata pe obiecte
Programarea orientata pe obiecte este procesul de construire a aplicatiilor cu obiecte. Pentru a
intelege cum lucreaza un limbaj orientat pe obiecte, iata cateva notiuni de baza:
O clasa defineste un tip abstract de date. Ea are o definitie al carei
format simplificat este:
class nume {lista_elementelor_membru}
Lista_elementelor_membru cuprinde date membru si functii membru (metode).
Un obiect este o data de un tip definit printr-o clasa. Se spune ca un obiect este o instantiere a
clasei respective.
Un limbaj orientat pe obiecte are urmatoarele caracteristici: incapsularea, mostenirea si
polimorfismul. Incapsularea este un mecanism care leaga impreuna functii si date si le pastreaza
in siguranta fata de interventii din afara si de utilizari gresite. Asadar un obiect este o entitate
logica ce incapsuleaza atat date cat si functii care manevreaza aceste date.
Mostenirea este procesul prin care un obiect poate sa preia prototipul altui obiect. Acest lucru
introduce conceptul de clasificare. De exemplu, un trandafir face parte din clasa floare care face
parte din clasa plante etc. Mecanismul mostenirii este acela care face posibil ca un obiect sa fie
un exemplar specific al unui caz mai general. Clasa initiala se numeste clasa de baza, clasa
parinte sau superclasa si clasa care deriva din clasa de baza se numeste clasa derivata, clasa
copil sau subclasa.
Polimorfismul. Uneori o metoda a clasei parinte nu se potriveste si clasei copil. Atunci functia va fi
suprascrisa in clasa derivata. Alteori aceeasi functie trebuie sa execute altceva in functie de
situatie. Acest proces se numeste polimorfism. 1.2. Programarea orientata pe obiecte in Java
Limbajul Java a fost construit de firma Sun pornind de la limbajul C++, dar au fost indepartate
unele aspecte din C++, precum supraincarcarea operatorilor si mostenirea multipla. De
asemenea, pointerii lipsesc complet din limbajul Java, eliminandu-se astfel una din principalele
surse de erori. In plus, eliberarea memoriei ocupate de obiecte si masive se face automat, printr-
un "mecanism de colectare de gunoaie". Limbajul Java mai are inclus un suport pentru aplicatii
care lucreaza cu mai multe fire de executie, inclusiv primitive de sincronizare intre firele de
executie (vom vedea acest lucru mai tarziu).
Limbajul Java lucreaza folosind setul de caractere Unicode. Acesta este un standard international
al carui set de caractere este reprezentat pe 16 biti. Vechiul standard ASCII este un subset al
standardului Unicode, adica vom regasi aici caracterele ASCII cu exact aceleasi coduri. Diferenta
majora este ca o variabila Java de tip caracter, de exemplu, este reprezentata pe 16 biti, iar un sir
de caractere va ocupa de doua ori mai multa memorie decat numarul caracterelor. Analog, o
variabila de tip intreg, care in C era reprezentata pe 16 biti, acum va fi reprezentata pe 32 de biti.
Avantajul setului Unicode este ca, fiind reprezentat pe 16 biti, are posibilitati mult mai mari.
Spre deosebire de alte limbaje orientate pe obiecte (de ex. C++), in Java fiecare linie de cod
apartine unei clase. Alfel spus, un program sursa Java este format numai din clase si interfete
(veti vedea in curand ce inseamna toate acestea).
Pentru ca un program scris in Java sa devina functionabil, trebuie sa fie mai intai compilat si apoi
interpretat. Un program sursa are extensia .java. Numele sau trebuie sa fie identic cu numele
clasei sau interfetei publice declarate in interiorul sursei. In urma compilarii rezulta fisiere cu
nume identice cu numele claselor, dar cu extensia .class, indiferent daca este vorba de o clasa
sau o interfata (vor fi create atatea fisiere .class cate clase si interfete sunt definite in fisierul
sursa).
O data fisierele compilate, acestea pot rula ca aplicatii sau apleturi. O aplicatie este un program
care va fi rulat de sine-statator, cu ajutorul unui interpretor (java sau jview). Un aplet Java ruleaza
intr-un document HTML si fisierele compilate .class sunt interpretate de Java Virtual Machine.
Acesta este un software care sta pe masina unde fisierele compilate .class vor fi rulate (JVM
este instalat o data cu instalarea browserului de web).
Scopul acestui set de articole este crearea de apleturi Java (de cele mai multe ori codul pentru
apleturi difera intr-o oarecare masura de codul pentru aplicatii).

Sintaxa folosita pentru a crea o noua clasa in Java este:


modificator class numeclasa [extends clasaparinte] [implements
interfete]
modificator poate fi unul din urmatoarele cuvinte cheie:
public – o clasa declarata public poate fi folosita de orice obiect. De retinut ca un fisier
sursa .java poate avea o singura clasa publica. Numele fisierului sursa trebuie sa fie identic cu
numele clasei publice;
private – o clasa declarata private poate fi folosita numai de clase din acelasi fisier sursa. O
sursa .java poate contine numai o clasa publica, dar mai multe clase private;
<nimic> - daca nu este specificat nici un modificator, clasa va fi considerata "friendly"
(prietenoasa) (a nu se confunda cu clasele sau functiile friend din C++!), ceea ce inseamna ca
toate celelalte clase din acelasi pachet (veti vedea mai jos ce este un pachet) pot accesa clasa
respectiva;
synchronizable – este folosit pentru a sincroniza elementele (vom vedea mai tarziu);
abstract – o clasa abstracta contine numai metode fara implementare (corpul functiei nu este
definit aici) si este folosita drept clasa de baza pentru eventuale clase care deriva din ea;
final – o clasa finala nu poate fi mostenita (alte clase pot accesa clasa finala, dar nici o alta
clasa nu poate deriva din ea).
Urmatorul exemplu arata o clasa publica si una privata care pot fi create in acelasi fisier sursa:
Public class clasa1 {
    int x;
    MetodaX(int x);
}
private class clasa2 {
    int y;
    MetodaY(int y);
}
Asa cum clasele pot fi declarate folosind modificatorii descrisi mai sus, Java permite si ca
variabilele si metodele sa fie prefixate de unul din urmatorii modificatori:
public – o metoda sau variabila declarata public poate fi accesata de orice clasa;
protected – poate fi accesata numai de subclase;
private – poate fi accesata numai de metode din aceeasi clasa;
<nimic> - daca nu este specificat nici un modificator, metoda sau variabila poate fi accesata de
orice alta clasa din acelasi pachet;
static – specifica o metoda sau variabila valabila global pentru toate instantele unei clase;
native – denota o metoda implementata in alt limbaj de programare (de ex. C++)
final – specifica o variabila constanta (valoarea ei nu poate fi schimbata in momentul rularii)
sau o metoda care nu poate fi supraincarcata de o clasa derivata.
synchronized – acest cuvant cheie blocheaza obiectul atunci cand aceasta metoda este
apelata si il deblocheaza atunci cand se iese din metoda.

In Java mostenirea se realizeaza prin folosirea cuvantului cheie extends. Astfel, clasa derivata
va "mosteni" toate variabilele si metodele definite in clasa de baza si va adauga variabile si
metode specifice (veti vedea putin mai jos un exemplu).
Spre deosebire de limbajul C++, in Java o clasa poate mosteni numai o singura clasa. Java
permite extinderea acestei functionalitati prin intermediul interfetelor.
O interfata este o declaratie a unui set de metode care nu sunt implementate. O clasa
implemeteaza o interfata prin cuvantul cheie implements si este responsabila pentru scrierea
codului metodelor definite in interfata. O interfata poate deriva din una sau mai multe interfete.
Desi o clasa poate mosteni o singura clasa, ea poate implementa oricate interfete.
Exemplu de derivare a unei clase folosind mostenirea si implementarea:
Sa presupunem clasa de baza om si interfata comportament, definite astfel:
public class om {
   int anul_nasterii;
   char sex;
   int returneaza_varsta(int anul_nasterii) {
      cod…
   };
}

public interface comportament {


   void citeste();
   void mananca();
   void danseaza();
}

Sa derivam clasa student din clasa om si sa implementam interfata comportament :


public class student extends om implements comportament{
   String facultatea;
   int anul;
   int note[20];
   int calculeaza_media(int note[]){cod…};
   void citeste(){
      corpul_functiei_citeste;
   }
   void mananca(){
      corpul_functiei_mananca;
   }
   void danseaza();{
      corpul_functiei_danseaza;
   }
}

Mai multe clase si interfete care au ceva in comun pot fi grupate intr-un pachet (package)
adaugand urmatoarea linie la inceputul fiecarui fisier sursa:
package nume_pachet;
Sa presupunem ca am creat clase si interfete care reprezinta persoane:
class om;
class student;
interface comportament;
class profesor;
class muncitor;
Putem grupa aceste clase si interfete intr-un pachet numit persoana scriind linia package
persoana la inceputul fiecarui fisier sursa care contine clasele de mai sus.
Realizatorii limbajului Java au creat si un numar mare de pachete (dintre care enumar:
java.applet, java.awt, java.io, java.lang, java.math, java.net,
java.security, java.sql, java.text, java.util). Fiecare contine clase si interfete
pentru a fi reutilizate de programator. Daca aveti JDK, in directorul unde l-ati instalat se afla un
fisier \lib\classes.zip. Daca ati instalat Vijual J++, in directorul c:\windows\java\classes\java\ veti
gasi mai multe directoare. Daca nu ati instalat inca un mediu de dezvoltare Java, dar aveti un
browser cu capacitati Java, de exemplu Internet Explorer pe un mediu Windows 95/98, uitati-va
prin fisierele .zip din directorul c:\windows\java\packages\. Exista o conventie conform careia
numele pachetelor au fost create pe structura directoarelor. Astfel, pentru a crea subpachetul cu
numele java.applet, a fost creat un director cu numele java, apoi un subdirector al acestuia
cu numele applet. Toate subpachetele lui applet se vor afla in directorul applet. Va
recomand ca pe masura ce invatati sa programati in Java, sa va uitati prin fisierele acestor
directoare pentru a intelege structura ierarhica a claselor si functiile si variabilele definite de
fiecare clasa.
Asadar, iata, pe scurt, cate ceva despre cateva dintre aceste pachete:
1. Pachetul java.applet contine clase folosite pentru a crea apleturi Java si pentru a
lucra cu ele. Fiecare aplet este derivat direct din clasa java.applet.Applet din acest
pachet.
2. Pachetul java.awt contine clasele si subpachetele care alcatuiesc Java Abstract
Windowing Toolkit (AWT). Acest pachet furnizeaza principalele clase care permit unei
aplicatii GUI (Graphical User Interface) scrise in Java sa ruleze pe orice platforma.
Cateva exemple de clase incluse in pachetul java.awt sunt: Button, List, Menu,
CheckBox, Color, Font, Graphics, Image, Point, Rectangle, Polygon,
Window etc.
3. Pachetul java.io contine clase cu capacitati de intrare/iesire. Programatorul poate citi
de la intrarea standard sau scrie la iesirea standard: File, FileInputStream,
FileOutputStream, DataInputStream etc.
4. Pachetul java.lang contine clasele care corespund tipurilor de date de baza
(Integer, Boolean, Float, Character, Long, Double). In acelasi pachet
sunt incluse si clasele System ( furnizeaza informatii ale sistemului unde ruleaza) si
Thread (furnizeaza posibilitatea crearii mai multor fire de executie);
5. Pachetul java.net include clase pentru comunicatii intre procese pe masini diferite
(dintr-o retea de calculatoare). Clasele URL, URLConnection, Socket,
InetAddress sunt cateva exemple.

Pachetul java.util ofera un mare numar de clase utile ca: Date, Dictionary,
Hashtable, Stack, Vector etc.
Clasele cuprinse in aceste pachete si functionalitatile lor vor fi explicate pe masura ce voi
prezenta exemple. Fiecare exemplu va folosi clase din aceste pachete.

1.3. Exemplul 1
In primul exemplu vom invata sa scriem un text, sa desenam diferite forme geometrice etc.

Clasele din pachetele despre care tocmai am vorbit sunt folosite intr-un program prin includerea
la inceput a unei linii de forma: import numeClasa;
Clasa java.applet.Applet este clasa de baza din care deriva orice aplet, deci ea trebuie
inclusa intotdeauna cand construim un aplet. Pentru a putea folosi elemente grafice, vom mai
include clase din pachetul java.awt.

Iata un prim exemplu de aplet:


import java.applet.Applet;
import java.awt.Graphics;

public class PrimulAplet extends Applet{


    public void paint(Graphics g){
        g.drawString("Primul aplet",50,25);
    }
}
Metoda paint este mostenita de clasa Applet (de la clasa Component) si se foloseste pentru
a desena obiecte. Ea are antetul public void paint(Graphics g). Aici am suprascris-o
(iata deci un exemplu de polimorfism!) pentru a executa ceea ce dorim noi.
Functia care realizeaza afisarea unui text este drawString (functie membra a clasei
Graphics), ce primeste ca parametri textul de afisat si coordonatele x si y unde textul respectiv
va fi scris pe ecran.

Pentru a vedea rezultatele rularii acestui aplet, trebuie ca fisierul HTML sa contina urmatoarea
linie:
<applet code="PrimulAplet.class" width=150 height=25></applet>
Putem introduce in sursa HTML parametrii care vor fi transmisi apletului, astfel:
<applet  ...>
<param name=titlu value="Acesta este primul meu aplet">
</applet>

Atunci modificam codul apletului folosind functia getParameter(String nume), membra a


clasei Applet:
public class PrimulAplet extends Applet{
    private String sir;
    public void init(){
        sir=getParameter("titlu");
        if(sir==null) sir="Primul aplet";
    }
    public void paint(Graphics g){
        g.drawString(sir,50,25);
    }
}

Intr-un aplet java nu exista o functie main (cum este in C++ sau in aplicatiile java), care
determina apelul celorlalte functii. Atunci care este ordinea in care vor fi executate functiile?
Executia unui aplet este marcata de cateva evenimente importante generate de catre browser. La
intalnirea etichetei <applet>, browserul incarca fisierul necesar rularii apletului (fisierele
.class). Apletul nu poate rula pana cand codul nu a ajuns pe calculatorul client.
Dupa incarcarea codului, apletul este apelat automat pentru initializare prin functia init(). Este
momentul in care apletul isi pregateste parametrii si obtine de la sistem resursele necesare
rularii. In exemplul nostru functia init citeste parametrul titlu transmis apletului de fisierul
HTML.
Dupa ce initializarea a fost terminata, browserul trimite catre aplet o comanda de pornire (functia
start()). Un aplet ruleaza atat timp cat navigatorul este activ. La schimbarea paginii curente,
apleturile din vechea pagina primesc o comanda de oprire temporara (functia stop()). La
inchiderea browserului este lansata functia destroy() care distruge toate variabilele alocate,
eliberand memoria. Toate aceste functii sunt definite in clasa Applet si sunt apelate automat de
sistem. Daca sunteti atenti, atunci cand Internet Explorer incarca un aplet java, scrie pe bara de
jos mai intai "Applet initialized", apoi "Applet started", care va sta scris atata vreme cat apletul
este activ. Daca apasati butonul "Refresh", va scrie "Applet stopped", si apoi va trece iar la
initializare (cu init()) si va porni din nou apletul (cu start()).
Orice aplet Java reprezinta deci un nou tip de obiect, derivat din obiectul standard Applet. Sa
desenam acum pe ecran diferite obiecte grafice. Pentru aceasta, sa folosim urmatoarele functii,
care au fost definite in clasa java.awt.Graphics astfel:
- public abstract void drawLine(int x1, int y1, int x2, int y2); deseneaza
o linie de la punctul (x1,y1) la punctul (x2,y2), cu culoarea curenta (vom vedea cum o putem
schimba);
- public void drawRect(int x, int y, int width, int height); deseneaza un
patrat cu coltul din stanga sus (x,y) si de latimea width si inaltimea height;
- public abstract void fillRect(int x, int y, int width, int height);
deseneaza un patrat plin;
- public abstract void drawRoundRect(int x, int y, int width, int
height, int arcWidth, int arcHeight); deseneaza un patrat cu colturile rotunde;
- public abstract void fillRoundRect(int x, int y, int width, int
height, int arcWidth, int arcHeight); deseneaza un patrat plin cu colturile rotunde;
- public void draw3DRect(int x, int y, int width, int height, boolean
raised); daca parametrul raised are valoarea true, deseneaza un patrat cu umbra (asa
cum sunt butoanele); iar daca este false, deseneaza un patrat ca un buton apasat;
- public void fill3DRect(int x, int y, int width, int height, boolean
raised); un patrat plin cu umbra;
- public abstract void drawOval(int x, int y, int width, int height);
deseneaza un oval cu parametri specificati (desigur ca daca width si height sunt egale, va fi
desenat un cerc);
- public abstract void fillOval(int x, int y, int width, int height); un oval
(cerc) plin;
- public abstract void drawPolygon(int xPoints[], int yPoints[], int
nPoints); deseneaza un poligon cu nPoints colturi definite de vectorii xPoints si yPoints;
- public abstract void fillPolygon(int xPoints[], int yPoints[], int
nPoints); un poligon plin.
Pentru alte functii definite in clasa Graphics, cititi fisierul Graphics.java (din directorul
java\awt\). Putem schimba culoarea curenta cu ajutorul functiei definita tot in clasa Graphics
astfel:
public abstract void setColor(Color c);
Pentru aceasta trebuie sa cunoastem mai intai clasa Color, care face parte tot din pachetul
java.awt.
Clasa Color defineste constante corespunzatoare culorilor uzuale: white, lightGray,
gray, darkGray, black, red, pink, orange, yellow, green, magenta, cyan si
blue. Astfel, de exemplu, pentru a seta culoarea curenta pe rosu vom scrie linia
g.setColor(Color.red), unde g este un obiect de tipul Graphics. Putem seta culoarea
curenta pe o culoare care nu a fost definita ca o constanta apeland unul dintre constructori (vezi
fisierul Color.java), ca exemplu: Color c = new Color(123,200,255).
Culoarea de background a apletului se seteaza cu ajutorul functiei setBackground(Color c),
mostenita de clasa Applet. Acesta linie trebuie scrisa in corpul metodei init() pentru ca
apletul trebuie sa stie inca de la initializare care este culoarea de background (nu poate fi
schimbata dupa aceea); Acum aveti destule cunostinte pentru a putea desena cu orice culoare
obiecte grafice pe ecran. Va recomand sa construiti apleturi folosind functiile prezentate si
eventual si alte functii definite in clasele Graphics si Color. Iata un exemplu! Aici puteti vedea
apletul ruland.

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

public class Desene extends Applet {

 private String mesaj;

 public void init() {


     mesaj = getParameter("titlu");
     if(mesaj==null) mesaj="Uite ce-am invatat sa desenez in
Java! :)) :";
     setBackground(Color.cyan);
 }

 public void paint(Graphics g) {


     g.setColor(Color.black);
g.drawString(mesaj,10,15);
     g.setColor(Color.blue);
     g.drawLine(10,50,30,100);
     g.drawRect(50,50,50,50);
     g.setColor(Color.red);
     g.fillRoundRect(120,50,50,50,20,20);
     g.setColor(Color.magenta);
     g.fill3DRect(190,50,50,50,true);
     g.setColor(Color.pink);
     int xpoli[]={250,300,320,380,320,250,250};
     int ypoli[]={50,50,80,80,120,100,50};
     g.fillPolygon(xpoli,ypoli,7);
     Color c = new Color(100,20,200);
     g.setColor(c);
     g.drawOval(400,50,50,50);
 }
}

Appleturi Java (2)


Asa cum am promis in episodul trecut al serialului "Apleturi Java", in acest episod, ca si in cele ce
vor urma, voi prezenta cate un exemplu. Din acest exemplu veti invata cum puteti crea
interactiune cu utilizatorul folosind butoane, campuri de text etc.
Exemplul din acest episod se numeste Triunghiul lui Sierpinski. Dati un click aici pentru a
vedea apletul ruland, enuntul problemei si cateva alte informatii. Deoarece apleturile java trebuie
sa fie portabile relativ la sistemul de operare, orice mediu de dezvoltare Java furnizeaza clase
pentru crearea elementelor de baza necesare unei interfete grafice. Aceste elemente includ:
etichete (labels), campuri de text (text fields), butoane (buttons), radio butoane (radio buttons),
cutii de selectie (check boxes), meniuri de selectie (choice menus) etc. Toate aceste elemente
sunt incluse, asa cum va spuneam si data trecuta, in pachetul java.awt.
Etichete
O eticheta se creeaza apeland constructorul clasei Label, in felul urmator:
Label eticheta = new Label(str, align); unde str este o variabila de tip String
care defineste sirul de caractere care va fi scris pe aceasta eticheta, iar align este una dintre
valorile Label.LEFT, Label.CENTER sau Label.RIGHT, reprezentand alinierea etichetei la
stanga, in centru sau la dreapta.
Dupa crearea etichetei, aceasta trebuie adaugata apletului scriind in cadrul functiei init()
urmatoarea linie: add(eticheta).
Dupa cum puteti vedea, eticheta creata mai sus va fi afisata pe suprafata apletului in stanga, in
centru sau in dreapta. Acest lucru nu va da prea multa libertate. De exemplu, cum puteti afisa o
eticheta la anumite coordonate si cum puteti seta latimea si inaltimea etichetei dupa anumite
valori? Atunci cand creati un aplet, Java creeaza si asigneaza in mod automat un layout implicit.
Puteti seta layout-ul ca avand diferite valori, printre care enumar FlowLayout, GridLayout,
BorderLayout, CardLayout, GridBagLayout. Acestea sunt clase care fac parte din
pachetul java.awt. Prin urmare le puteti gasi acolo. Eu nu imi propun sa explic aceste clase
pentru ca personal nu le folosesc, preferand sa am libertatea de a seta coordonatele si
dimensiunile. Cum se face acest lucru? Pentru aceasta, trebuie sa nu fie setat nici un layout,
ceea ce se face prin comanda setLayout(null). Apelati aceasta functie prima in init() si
va va permite sa dati ce coordonate si dimensiuni doriti etichetelor. Haideti incetul cu incetul sa
construim apletul pe care vi l-am propus: import java.applet.Applet;
import java.awt.*; public class proba extends Applet {
     Label labelxa = new Label();
     Label labelya = new Label();      public void init() {
          setLayout(null);
 
          labelxa.setText("xa");
          labelxa.reshape(52,12,24,24);
          add(labelxa);
 
          labelya.setText("ya");
          labelya.reshape(88,12,24,24);
          add(labelya);
         }
} Am notat in acest exemplu si voi nota si de acum incolo conventional instantele claselor de tip
Label, Button etc. dupa clasa din care fac parte. Astfel, un obiect de tip Label il voi nota de
exemplu cu labelxa sau labelya.
Deci in exemplul de mai sus am creat mai intai doua obiecte de tip Label. Apoi, in cadrul
metodei init() am setat layout-ul pe null. Dintre metodele clasei Label, mai folosite sunt:
public void setText(String str); seteaza sirul de scris pe str, public String
getText(); returneaza sirul afisat.
Clasa Label mosteneste clasa Component, deci si toate metodele implementate de aceasta.
Clasa Component reprezinta o componenta generica ce are ca atribute: coordonata x,
coordonata y, latime, inaltime, culoare de foreground, culoare de background, font, vizibilitate etc.

Printre metodele implementate de clasa Component, mai folosite sunt:


public void move(int x, int y); muta obiectul la coordonatele x si y specificate;
public void resize(int latime, int inaltime); redimensioneaza latimea si
inaltimea cu valorile specificate;
public synchronized void reshape(int x, int y, int latime, int
inaltime); muta si redimensioneaza obiectul la valorile specificate.
Prin urmare, clasa Component ofera mai multe atribute necesare claselor de tip Label,
Button etc, si chiar si clasa Applet mosteneste aceasta clasa, chiar daca nu direct (clasa
Applet mosteneste clasa Panel, care mosteneste clasa Container, care mosteneste clasa
Component). Daca cititi fisierele applet.java, component.java, label.java etc. va veti
clarifica in ceea ce priveste mostenirile si metodele acestor clase. Revenind la exemplul nostru,
dupa ce am stabilit textul de scris si pozitia si dimensiunile obiectului, acesta trebuie adaugat
apletului prin add(labelxa). Adaugati in acelasi fel etichetele labelxb, labelyb,
labelxc, labelyc, labelIteratii, corespunzand celorlalte etichete pe care le-ati vazut
in aplet. Apoi compilati programul si rulati-l, apelandu-l dintr-un fisier .html in care sa aveti linia:
<applet code=triunghi.class width=500 height=500></applet>

Campuri de text
Obiectele de tip TextField sunt asemanatoare cu cele de tip Label. Deci le veti crea astfel:
 TextField textFieldxa = new textField();
 TextField textFieldya = new textField();
In corpul functiei init() veti seta textul implicit, coordonatele si dimensiunile, si apoi le veti
adauga apletului astfel:
  textFieldxa.setText("250");
  textFieldxa.reshape(42,36,36,24);
  add(textFieldxa);
 
  textFieldya.setText("100");
  textFieldya.reshape(88,36,36,24);
  add(textFieldya); Creati si celelalte campuri de text, si anume textFieldxb,
textFieldyb, textFieldxc, textFieldyc si textFieldIteratii.

Butoane
Asa cum va asteptati, butoanele pot fi create in mod asemanator cu etichetele si campurile de
text:
 Button buttonRedeseneaza = new Button();  
buttonRedeseneaza.setLabel("Redeseneaza");
  buttonRedeseneaza.reshape(356,36,96,24);
  add(buttonRedeseneaza); Sa vedem acum partea cea mai
interesanta, si anume: interactiunea cu utilizatorul: cum putem citi datele introduse in campurile
de text? Pentru a executa o actiune de gen apasarea unui buton, se apeleaza metoda action,
astfel:
public boolean action(Event evt, Object arg);
Primul parametru, evt, este un obiect de tip Event, care desemneaza un eveniment primit de
aplet, de exemplu poate fi vorba de un eveniment de la tastatura sau de la mouse. Campul
target al unui obiect de tip Event indica tipul obiectului care a generat evenimentul. Pentru a
determina acest obiect, folosim cuvantul cheie instanceof astfel:
if (evt.target instanceof Button). Daca acesta este adevarat, pentru a vedea exact
care buton a fost apasat, de exemplu se scrie linia:
if (arg == "Redeseneaza") pentru a vedea daca a fost apasat butonul cu textul
"Redeseneaza".
In exemplul prezentat nu avem mai multe butoane, deci nu e nevoie sa folosim aceasta din urma
linie, dar o puteti exersa construind mai multe butoane si adaugand evenimente diferite pentru
fiecare dintre ele. Sa revenim la exemplul nostru. Dupa ce ati construit etichetele, campurile de
text si un buton, sa citim datele introduse.
Pentru aceasta, mai declarati ca variabila a apletului variabila sir de tip String pentru a citi in
ea datele introduse intr-un camp de text:
String sir;
Apoi adaugati urmatoarele doua metode apletului: public boolean action(Event evt,
Object arg) {
   if(evt.target instanceof Button) {
       sir=textFieldxa.getText();
       repaint();
       return true;
   }
   return false;
} public void paint(Graphics g) {
    g.drawString(sir,10,100);
}
Functia action citeste sirul de caractere introdus in campul textFieldxa si il introduce in
variabila sir declarata anterior. Apoi apeleaza functia repaint(). Aceasta este definita in clasa
Component si executa metoda paint(Graphics g). Nu mai ramane deci decat sa apelam
functia drawString (pe care am explicat-o luna trecuta) in corpul functiei paint. Sa citim acum
datele introduse in campurile corespunzatoare coordonatelor si sa desenam un triunghi.
Trebuie sa declaram mai intai 6 variabile pentru cele 6 coordonate:
int xa, ya, xb, yb, xc, yc;
In locul variabilei declarate anterior String sir; vom declara Integer temp; pentru a citi
datele introduse. Metoda action va deveni:
public boolean action(Event evt, Object arg) {
    if(evt.target instanceof Button) {
        temp=Integer.valueOf(textFieldxa.getText());
        xa=temp.intValue();
        temp=Integer.valueOf(TextFieldya.getText());
        ya=temp.intValue();
        temp=Integer.valueOf(TextFieldxb.getText());
        xb=temp.intValue();
        temp=Integer.valueOf(TextFieldyb.getText());
        yb=temp.intValue();
        temp=Integer.valueOf(TextFieldxc.getText());
        xc=temp.intValue();
        temp=Integer.valueOf(TextFieldyc.getText());
        yc=temp.intValue();
        repaint();
        return true;
    }
    return false;
}
Am citit in temp valoarea introdusa in campul textFieldxa ca Integer, si apoi am trecut-o in
variabila xa transformand-o mai intai in int. Transformand textul introdus intr-o valoare de tip
int, realizam si o validare, in sensul ca daca introduceti in vreunul din campuri o alta valoare
decat un numar intreg, ea va fi ignorata. Functia paint devine:
public void paint(Graphics g) {
    g.drawLine(xa,ya,xb,yb);
    g.drawLine(xb,yb,xc,yc);
    g.drawLine(xc,yc,xa,ya);
} Rulati tot ce am facut pana acum si veti vedea ca apasand butonul veti desena un triunghi, iar
daca modificati valorile campurilor si apasati din nou butonul, triunghiul va avea alta forma.
Pentru ca triunghiul sa apara de prima data (pentru niste valori initiale ale coordonatelor), fara sa
mai fie nevoie sa mai apasati o data butonul, trebuie sa copiati liniile de tipul:
   temp=Integer.valueOf(textFieldxa.getText());
   xa=temp.intValue();
astfel incat ele sa apara si la sfarsitul functiei init(). Initial valorile xa, ya, xb, yb, xc,
yc au valoarea zero ( la declararea unei variabile, acesteia i se asigneaza valoarea 0 sau null)
si de aceea nu apare nimic desenat. In sfarsit, sa desenam figura care reprezinta Triunghiul lui
Sierpinski. Pentru aceasta trebuie definita o functie recursiva care imparte triunghiul initial in 4
triunghiuri egale si care se autoapeleaza pentru toate triunghiurile, exceptand triunghiul din
mijloc. Aceasta este o problema de programare si nu mi-am propus sa o explic in acest exemplu.
Daca nu ati mai facut functii recursive sau nu sunteti obisnuiti cu programarea, luati-o ca atare si
incercati sa o intelegeti. Nu este atat de grea precum pare.
Aceasta functie este data mai jos: void Sierpinski(Graphics g,int xa,int ya,int
xb,int yb,int xc,int yc,int iteratii) {
    int x1,y1,x2,y2,x3,y3; //coordonatele mijloacelor laturilor
x1=(xa+xb)/2;
    y1=(ya+yb)/2;
    x2=(xb+xc)/2;
    y2=(yb+yc)/2;
    x3=(xa+xc)/2;
    y3=(ya+yc)/2;
    g.drawLine(x1,y1,x2,y2);
    g.drawLine(x1,y1,x3,y3);
    g.drawLine(x2,y2,x3,y3);
    int xpoly[]={x1,x2,x3};
    int ypoly[]={y1,y2,y3};
    g.fillPolygon(xpoly,ypoly,3); //umplerea triunghiurilor exterioare cu culoarea
implicita
    if(iteratii>1) {
        Sierpinski(g,xa,ya,x1,y1,x3,y3,iteratii-1);
        Sierpinski(g,x1,y1,xb,yb,x2,y2,iteratii-1);
        Sierpinski(g,x3,y3,x2,y2,xc,yc,iteratii-1);
    }
} Aceasta functie trebuie apelata din functia paint, astfel incat ea devine:
public void paint(Graphics g) {
    g.drawLine(xa,ya,xb,yb);
    g.drawLine(xb,yb,xc,yc);
    g.drawLine(xc,yc,xa,ya);
    Sierpinski(g,xa,ya,xb,yb,xc,yc,nr_iteratii);
}
unde nr_iteratii este o variabila de tip int a carei valoare o citim la fel ca si xa, ya etc.
Alte clase inrudite cu cele prezentate mai sunt: CheckBox, Choice, TextArea, Menu,
MenuItem, MenuBar, ScrollBar etc. Unele sunt foarte mult asemanatoare cu cele din acest
episod, altele au elemente specifice.  Voi prezenta detalii asupra lor atunci cand le vom folosi in
alte exemple .

Apleturi Java (III): Fire de executie

In episodul de luna aceasta vom studia ceva specific limbajului Java, si anume crearea si
folosirea firelor de executie.
Apletul deseneaza niste cercuri rosii in miscare la fiecare click pe suprafata lui. Care este
legatura lui cu firele de executie? Raspunsul este urmatorul: la fiecare click pe suprafata apletului
se genereaza un nou fir de executie. Dar haideti sa aflam mai multe despre firele de executie:

Un fir de executie (thread) executa o serie de instructiuni. Un fir poate trai pe tot parcursul
apletului sau doar cateva milisecunde. De asemenea un fir poate da nastere altui fir sau poate
omori un fir existent.

Chiar si in apleturile din episoadele trecute exista unul sau mai multe fire de executie "ascunse",
create de browser. Atunci cand are loc un eveniment de gen click de mouse, sau cand se
apeleaza o functie ca repaint(), este executat un anumit fir ascuns. Firele ascunse sunt create
si executate automat de limbaj si sunt transparente pentru programator. Daca rulati orice aplet in
modul debugger al mediului de dezvoltare Java pe care il folositi, veti vedea cateva fire de
executie create automat.
Exista si situatii in care programatorul are nevoie sa creeze el insusi propriile fire de executie.
Trebuie stiut ca in timp ce un fir ruleaza in cadrul apletului, respectivul fir nu poate face altceva.
Daca apletul tine un fir prea mult, poate bloca browserul. De aceea daca apletul are nevoie sa
ruleze o metoda a sa pentru mai mult timp, este necesar ca un nou fir sa fie creat. Iata care sunt
situatiile cele mai frecvent intalnite in care este nevoie de un nou fir de executie:
- Timp indelungat pentru initializare (functia init() a apletului). De exemplu, apletul are nevoie
de ceva timp pentru a incarca o imagine. Un fir este folosit pentru ca sistemul sa poata primi alte
evenimente.
- Task-uri repetitive sau care trebuie sa dureze o anumita perioada de timp. De exemplu pentru
animatii. La fiecare cateva milisecunde este afisat un nou frame. Firul deseneaza frame-ul, sta o
perioada de timp, apoi reia procesul.
- Evenimente asincrone. De exemplu un click de mouse. Daca utilizatorul apasa butonul mouse-
ului, un nou fir este creat pentru a reda un nou frame dintr-o imagine.
- Mai multe task-uri. Firele sunt folosite pentru a face mai mult de un lucru o data. De exemplu un
fir controleaza o animatie in timp ce altul face un calcul.

Pentru a crea si controla firele de executie se folosesc clasa Thread si interfata Runnable din
pachetul java.lang.
Exista doua metode pentru a crea un fir:
1. Implementand interfata Runnable. In acest caz nu este nevoie sa creati o noua clasa. De
exemplu daca vreti sa creati un fir chiar in cadrul apletului, faceti apletul sa implementeze
aceasta interfata (amintiti-va ca o clasa nu poate mosteni mai multe clase, deci nu poate mosteni
si clasa Applet, si clasa Thread).
2. Mostenind clasa Thread. O clasa nou creata mosteneste clasa Thread si firul va incepe sa
ruleze conform functiei run() (veti vedea imediat in exemplu).

Nu este aproape nici o diferenta intre cele doua metode. In general metoda a doua se foloseste
atunci cand doriti ca firul sa ruleze din clasa-aplet. Daca firul va rula in propria lui clasa, va fi mai
convenabila prima metoda.

Dar pentru a intelege mai bine, sa vedem ce contin clasa Thread si interfata Runnable (pentru
aceasta cititi si fisierele Thread.java si Runnable.java din pachetul java.lang), si pe urma
voi prezenta exemple concrete.

Interfata Runnable are o singura metoda: public abstract void run(). Ea trebuie
definita obligatoriu de clasa care implementeaza interfata Runnable. Functia run() va contine
codul care va fi executat de firul de executie respectiv.
Clasa Thread implementeaza interfata Runnable si contine (printre altele) urmatoarele metode:
- mai multi constructori;
- start() - face ca firul de executie sa inceapa sa ruleze. In acest moment Java Virtual Machine
va apela functia run();
- functia run() care suprascrie functia run() a interfetei Runnable; acesta functie trebuie
suprascrisa cu codul ce se doreste a fi executat de firul respectiv;
- stop() - forteaza oprirea firului de executie.

Crearea si controlul unui fir de executie folosind prima metoda se face parcurgand urmatorii
pasi:
1. se declara clasa-aplet ca implementand interfata Runnable;
2. se declara obiectul de tip Thread;
3. se scrie functia init() in care se construieste firul;
4. se scrie functia de pornire a firului (start);
5. se scrie functia de oprire a firului (stop);
6. se suprascrie metoda run();
Exemplul urmator creeaza un fir pentru rularea unui contor care numara de la 1 la 100:

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

public class RunnableExample extends Applet


    implements Runnable { //pasul 1

  Thread thread; //pasul 2


  int count; //variabila contor
  String displayStr; //sirul care va fi afisat in functia paint
  Font font;

  public void init() { //pasul 3


    font = new Font("TimesRoman", Font.PLAIN, 72);
    setFont(font);
    count = 0;
    displayStr = "";
    thread = new Thread(this); //se creeaza firul apeland un
constructor
 }
  public void start() { //pasul 4
    thread.start(); //se porneste firul
 }

  public void stop() { //pasul 5


    thread.stop();
 }

  public void run() { //pasul 6


    while (count < 100) { //executa firul atata vreme cat count<100
      ++count;
      displayStr = String.valueOf(count); //transforma variabila int in String
      repaint(); //redeseneaza
      try {
        thread.sleep(100); //firul sta 100 milisecunde, dupa care reia corpul lui while
      }
      catch (InterruptedException e) {
      }
    }
 }

  public void paint(Graphics g) {


    g.drawString(displayStr, 50, 130);
  }
}

(Nu va bateti capul cu secventa try - catch de mai sus, va voi explica mai pe larg intr-un
episod in care voi trata exceptiile in Java.)
Nota. Nu este obligatoriu ca metoda stop() (pasul 5) sau linia thread.stop() sa existe, Java
va opri automat firul atunci cand nu va mai exista nici o referinta catre el. Totusi este recomandat
sa o scrieti. Apelul thread.start() este obligatoriu, fara el firul nu va porni,adica nu se va
executa functia run(). Insa nu este obligatoriu ca el sa fie in functia public void start().
Putea sa fie in alta parte unde se stie ca se executa inaintea apelului celorlalte functii care
apeleaza firul, de exemplu putea fi ultima linie a functiei public void init().
Daca ati urmarit cu atentie ce am explicat pana acum, acest exemplu ar trebui sa vi se para chiar
usor. Pentru intrebari, scrieti la mirela.andronescu@er.dntis.ro.

Haideti acum sa vedem cum se face acelasi lucru folosind cea de a doua metoda. Vom parcurge
urmatorii pasi:
1. cream clasa ThreadExample care mosteneste clasa Applet;
2. scriem functia init() care va crea un fir prin apelul constructorului clasei MyThread;
3. cream clasa MyThread care mosteneste clasa Thread;
4. declaram campurile clasei MyThread, printre care obligatoriu trebuie sa fie un obiect de tipul
ThreadExample sau Applet;
5. cream constructorul clasei MyThread care are ca parametru o clasa-aplet;
6. in acest constructor apelam functia start() pentru a porni firul de executie;
7. suprascriem functia run().
Iata mai jos codul acestui exemplu:

import java.awt.*;
import java.applet.*;
public class ThreadExample extends Applet { //pasul 1
  String displayStr;
  Font font;

  public void init() { //pasul 2


    font = new Font("TimesRoman", Font.PLAIN, 72);
    setFont(font);
    displayStr = "";
    new MyThread(this);   
  }

  public void paint(Graphics g) {


    g.drawString(displayStr, 50, 150);
  }

class MyThread extends Thread { //pasul 3


  ThreadExample applet; //pasul 4
  int count;
 
  MyThread(ThreadExample applet) { //pasul 5
    this.applet = applet;
start(); //pasul 6
  }

  public void run() { //pasul 7


    count = 0;
    while (count < 100) {
      ++count;
      applet.displayStr = String.valueOf(count);
      applet.repaint();
      try {
        sleep(100);
      }
      catch (InterruptedException e) {
      }
    }
  }

Nota1: Scrieti intreaga sursa de mai sus intr-un singur fisier. Puteti crea doua fisiere, cate unul
pentru fiecare clasa, dar pentru aceasta trebuie sa mai adaugati in fisierul-aplet linia: import
MyThread;si trebuie sa declarati clasa MyThread ca publica.
Nota2: Se mai obisnuieste ca in clasa ThreadExample sa existe linia MyThread thread;
(declararea unei variabile de tip MyThread). Astfel, apelul constructorului (pasul 2) va fi
thread=new MyThread(this);. De asemenea metodele public void start() si
public void stop() pot exista (la fel ca la prima metoda), dar nu este obligatoriu. Va
recomand sa rulati totusi codul si intr-un fel si in altul, pentru a intelege mai bine functionarea
firelor de executie in Java.

Este de remarcat urmatorul lucru: constructorul clasei MyThread are ca argument un obiect de
tip ThreadExample, care este apletul de unde firul de executie va fi rulat. Acest lucru este
necesar pentru ca firul sa poata comunica cu apletul.
Apoi, priviti functia run(). Clasa-fir acceseaza acum obiectul de tip aplet pentru a-i transmite
noua valoare a sirului si pentru a apela functia repaint(). In prima versiune, firul era asociat cu
clasa-aplet, altfel spus facea parte din ea, iar acum este un proces complet separat.
Acum aveti destule cunostinte despre firele de executie pentru a putea intelege sursa apletului pe
care l-ati vazut ruland la inceputul articolului. Vom folosi a doua modalitate deoarece, asa cum va
spuneam, la fiecare click se va genera un nou fir de executie, deci la un moment dat pot rula mai
multe fire de executie, depinzand de cate click-uri au fost date in ultimul timp. Vom parcurge
urmatorii pasi:
1. cream clasa OvalThreadExample care mosteneste clasa Applet;
2. declaram variabila Image offImage;
3. declaram variabila Graphics offGraphics;
Vom folosi aceste variabile pentru a evita licarirea ecranului in timpul animatiei. O animatie se
realizeaza prin redesenarea cadrelor. Daca acest lucru se face direct pe ecran, se va produce o
licarire a ecranului (puteti incerca!). Acest lucru poate fi inlaturat prin tehnica descrisa in urmatorii
pasi:
4. se creeaza o imagine (careia i se mai spune buffer si care la noi este variabila offImage) care
contine imaginea curenta a apletului (sau numai o parte a suprafetei sale).
5. se ia apoi contextul grafic al acestei imagini (in variabila offGraphics), care se adapteaza
pentru fiecare cadru in parte;
6. se apeleaza in metoda paint() functia drawImage (care este din clasa Graphics la fel ca
si drawLine, drawOval etc.) avand ca parametru imaginea offImage.
7. se scrie functia update(Graphics g). Aceasta functie este apelata automat la executia
functiei repaint(). (repaint() apeleaza automat update si aceasta apeleaza paint. Pentru
mai multe detalii cititi fisierul Component.java). Functia update va fi deci apelata cu noul
context grafic, adica offGraphics, dupa ce acesta a fost modificat.
Pasii 3-7 sunt folositi deci pentru a inlatura licarirea imaginii.
8. se apeleaza functia public boolean mouseDown(Event e, int x, int y). Aceasta
functie face urmatorul lucru: atunci cand se constata o apasare a butonului mouse-ului, se
executa corpul acestei functii. x si y sunt coordonatele mouse-ului unde a avut loc evenimentul.
Functia returneaza true daca s-a inregistrat evenimentul respectiv. Alte functii care reactioneaza
la anumite evenimente mai sunt: mouseDrag, mouseEnter, mouseExit, mouseUp, cu aceiasi
parametri. Ele sunt metode tot ale clasei Component. (Pentru exersarea acestor metode va
propun urmatorul exercitiu: modificati apletul din episodul trecut: Triunghiul lui Sierpinski astfel
incat cand utilizatorul da click undeva pe suprafata apletului, varful cel mai apropiat sa se
deplaseze in acel punct. Astept sa-mi trimiteti prin mail raspunsul! :))
9. in corpul metodei de la pasul 8 se scrie deci codul ce se va executa atunci cand se da un click,
si anume se creeaza un nou fir, deci se apeleaza constructorul clasei-fir;
10. se creeaza clasa DrawTarget care mosteneste clasa Thread si care are acelasi rol ca si
clasa MyThread din exemplul anterior.
11. printre variabilele sale trebuie sa fie si una de tip Applet sau clasa-aplet;
12. se scrie constructorul clasei DrawTarget, care ca avea ca parametri clasa Applet,
coordonatele mouse-ului unde s-a dat click si contextul grafic care urmeaza sa fie modificat;
13. in constructor se apeleaza si functia start() pentru pornirea firului de executie;
14. se scrie functia run();
15. in functia run() se seteaza culoarea de foreground pe alb pentru contexul offGraphics;
16. se apeleaza functia setXORMode, care seteaza modul de desenare al acestui context grafic
sa alterneze intre culoarea setata la pasul 15 (alb) si culoarea setata aici (rosu). (vezi clasa
Graphics);
17. in doua cicluri for realizeaza desenarea cercurilor in 8 pasi cand se maresc si tot 8 cand se
micsoreaza;
18. se apeleaza functia repaint() a apletului;
19. se opreste executia firului pentru 200 de milisecunde.
import java.applet.Applet;
import java.awt.*;

public class OvalThreadExample extends Applet { //pasul 1


  Image offImage; //pasul 2
  Graphics offGraphics; //pasul 3

public void init() {


    offImage=createImage(size().width,size().height); //pasul 4;
    offGraphics=offImage.getGraphics(); //pasul 5
 }

  public void paint(Graphics g) {


    g.drawImage(offImage,0,0,null); //pasul 6
 }

  public void update(Graphics g) { //pasul 7


    paint(g);
 }

  public boolean mouseDown(Event e, int x, int y) { //pasul 8


    new DrawTarget(this,x,y,offGraphics); //pasul 9
    return true;
 }

class DrawTarget extends Thread { //pasul 10


  int xPos,yPos; //pozitia unde se vor desena cercurile
  Applet applet; //pasul 11
  Graphics offGraphics;

  public DrawTarget(Applet a, int x, int y, Graphics g) { //pasul 12


    xPos=x;
    yPos=y;

applet=a;
    offGraphics=g;
    start(); //pasul 13
 }

  public void run() { //pasul 14


    int i;
    int r;
    offGraphics.setColor(Color.white); //pasul 15
    offGraphics.setXORMode(Color.red); //pasul 16
    for(r=0,i=10;i>-20;i-=20) // i=(10,-10) //pasul 17
      for(r+=i;(r<90)&&(r>0);r+=i) { // r=(10,20...80,80,70...10)
        offGraphics.fillOval(xPos-r,yPos-r,2*r,2*r);
        applet.repaint(); //pasul 18
        try {
          sleep(200); //pasul 19
    }
        catch (InterruptedException e) {
    }
  }
 }

Ei, de data aceasta nu a mai fost asa usor, nu-i asa? (m-am gandit ca ar fi bine sa incepeti anul
cu mult avant, ca sa o tineti tot asa pana la sfarsitul lui... glumesc :)))
Asupra tehnicii de buffering voi reveni cand voi mai prezenta apleturi cu animatii, pentru ca vom
folosi acelasi procedeu, iar asupra firelor de executie voi reveni luna viitoare, cand vom intra mai
amanuntit in sincronizarea firelor de executie.

Apleturi Java (IV): Exceptii


In episodul din numarul trecut v-am promis ca acum voi prezenta sincronizarea firelor de
executie. Dar fiindca pentru aceasta voi folosi si exceptii, cu care v-ati mai intalnit, de altfel, si in
episoadele trecute, m-am gandit sa lamuresc in acest numar ce sunt exceptiile si cum se trateaza
ele, urmand ca in numarul urmator sa revin asupra sincronizarii firelor de executie.

In programare in general, indiferent de limbaj, pot aparea erori, fie din cauza ca programatorul nu
a anticipat chiar toate situatiile care pot aparea, fie ca o conexiune de retea nu s-a realizat cu
succes, fie ca un fisier din care se citesc niste date nu poate fi accesat etc. Cand spun "erori" ma
refer la erorile care apar la momentul interpretarii, deci al executiei, nu la erorile are apar la
compilare! In Java, aceste evenimente care pot cauza esuarea programului se numesc exceptii.
Exceptiile nu apar pur si simplu, ele sunt "aruncate". Exceptiile pot fi aruncate de sistem sau de
programator. Pentru a controla executia programului atunci cand apare o exceptie, aceasta
trebuie "prinsa". Echivalentul expresiei "a aparut o eroare" in terminologia Java este "o exceptie a
fost aruncata".
Asadar Java arunca o exceptie ca raspuns la aparitia unei situatii neobisnuite. Programatorul
poate fie sa arunce o exceptie, fie sa o prinda.
Exceptiile in Java sunt de fapt niste obiecte, instante ale claselor care mostenesc clasa
Throwable. Figura de mai jos arata o ierarhie partiala a exceptiilor:

Clasa Throwable are doua subclase: Error si Exception. Instantele clasei Error sunt erorile interne
ale masinii virtuale. Aceste erori sunt rare, dar de cele mai multe ori atunci cand apar, sunt fatale.
Programatorul nu le poate influenta cu nimic (nu le poate prinde sau arunca), dar ele exista astfel
incat Java sa le poata folosi daca are nevoie de ele.
Felul in care programatorul poate influenta cursul programului in functie de erorile care apar este
prin intermediul clasei Exception.
Facand o clasificare a exceptiilor dupa modul in care programatorul este obligat sa le prinda sau
nu, le putem imparti in doua categorii:
I. Exceptii care nu trebuie prinse neaparat, adica Java permite

compilarea sursei fara prinderea lor. Acestea sunt generate de sistem atunci cand se intampla lucruri de
genul: alocarea memoriei esueaza, se incearca o impartire la 0, o valoare nula este folosita necorespunzator
etc. In cazul acestora, programatorul alege daca le trateaza sau nu.
II. Exceptii care trebuie prinse neaparat, compilatorul dand eroare in cazul neprinderii lor.

I.
In tabelul de mai jos am explicat cateva dintre cele mai intalnite exceptii din prima categorie:
Exceptii Descriere
ArithmeticException
Cauzata de erori matematice ca impartirea la zero
ArrayIndexOutOfBoundsException Cauzata de incercarea de alocare a unui element dintr-un
vector in afara spatiului alocat
ArrayStoreException Apare cand un program incearca sa stocheze un tip de
data necorespunzator intr-un vector (tablou)
FileNotFoundException Cauzata de incercarea de accesare a unui fisier
inexistent
Exceptii Descriere
IOException Cauzata de esecuri generale de input/output, ca esuarea de a citi dintr-un fisi
NullPointerException Cauzata de referirea unui obiect null
NumberFormatException Apare atunci cand o conversie intre siruri si numere esueaza
OutOfMemoryException Apare cand nu este destula memorie necesara alocarii unui obiect
SecurityException Apare cand un aplet incearca sa execute o actiune care nu este permisa de
setarile de securitate ale browserului
StackOverflowException Apare cand sistemul nu mai are loc pe stiva
StringIndexOutOfBoundsException Apare cand un program incearca sa acceseze un caracter dintr-un sir (String)
carui pozitie nu exista.

Urmatorul exemplu realizeaza impartirea a doua numere intregi introduse de utilizator. Rulati
acest exemplu cu AppletViewer:

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

public class ExceptionApplet extends Applet {


  TextField textField1 = new TextField();
  TextField textField2 = new TextField();
  String rezultat;

  public void init() {


    setLayout(null);
    textField1.reshape(30,30,50,20);
    add(textField1);
    textField2.reshape(100,30,50,20);
    add(textField2);
    rezultat = "Nedefinit";
  }
  
  public void paint(Graphics g) {
    g.drawString(rezultat, 30, 100);
  }

  public boolean action(Event evt, Object arg) {


    String str1 = textField1.getText();
    String str2 = textField2.getText();
    int int1 = Integer.parseInt(str1);
    int int2 = Integer.parseInt(str2);
int answer = int1 / int2;
    rezultat = String.valueOf(answer);
    repaint();
    return true;
  }
}

Mai intai sa analizam putin sursa de mai sus:


- daca in metoda action nu se testeaza evt sau arg, atunci codul din aceasta functie se executa la
apasarea tasei Enter;
- linia int int1 = Integer.parseInt(str1); face conversia sirului str1 la int folosind functia parseInt a
clasei java.lang.Integer care primeste ca parametru un sir de caractere si returneaza un int (sirul
convertit la int);
- linia rezultat = String.valueOf(answer); face conversia int-String; o alta modalitate, mai simpla,
este urmatoarea: rezultat = "" + answer;
- restul liniilor le cunoasteti din episodul din decembrie, acela cu Triunghiul lui Sierpinski.

Daca introduceti in campurile de text numere intregi obisnuite, atunci apletul functioneaza bine.
Dar ce se intampla daca introduceti in loc de numere cateva litere, sau orice altceva care nu este
numar intreg? Pe fereastra tip DOS a lui AppletViewer va aparea o eroare, si anume:
java.lang.NumberFormatException. De asemenea, daca introduceti in al doilea camp valoarea 0,
va aparea alta eroare, si anume: java.lang.ArithmeticException. Iata cum putem prinde aceste
exceptii: urmand pasii urmatori:
1. Identificam liniile de cod care ar putea cauza exceptia: in cazul nostru aceste linii sunt acelea
din metoda action care fac conversia textului introdus in int si calculeaza sirul de transmis ca
rezultat:
    int int1 = Integer.parseInt(str1);
    int int2 = Integer.parseInt(str2);
    int answer = int1 / int2;
    rezultat = String.valueOf(answer);
2. Plasam aceste linii intr-un bloc
try {
//liniile de cod identificate la pasul 1
}
3. In continuarea blocului try scriem atatea blocuri catch cate exceptii vrem sa prindem, iar ca
parametru la fiecare bloc catch trecem tipul exceptiei de prins;
4. in corpul instructiunii catch scriem liniile de cod care se vor executa in cazul ca exceptia
respectiva a aparut.
Asadar inlocuiti metoda action de mai sus cu urmatoarea:

  public boolean action(Event evt, Object arg) {


    String str1 = textField1.getText();
    String str2 = textField2.getText();
    try {
      int int1 = Integer.parseInt(str1);
      int int2 = Integer.parseInt(str2);
      int answer = int1 / int2;
      rezultat = String.valueOf(answer);
    }
    catch (NumberFormatException e) {
       rezultat = "Numar prost!";
}
     catch (ArithmeticException e) {
       rezultat = "Impartire la 0!";
     }
    repaint();
    return true;
  }

Acum exceptiile sunt prinse si sunt tratate ca atare.

Exista cazuri insa cand apar exceptii care nu trebuiesc prinse, ci rezolvate in alt fel.
Sa luam un mic exemplu care incearca sa afiseze un buton:

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

public class mic extends Applet {


  Button buton;

  public void init() {


    buton.setLabel("Buton");
    add(buton);
  }
}

Daca rulati acest aplet, nu o sa va dea eroare la compilare, dar pe fereastra AppletViewer va
scrie java.lang.NullPointerException si nici nu va rula nimic. Aceasta eroare a aparut din cauza
faptului ca in metoda init se apeleaza functia setLabel a instantei buton care nu a fost initializata
(creata). Prin urmare trebuie sa rezolvati eroarea prin initializarea obiectului: Button buton = new
Button();
Atentie! Toate obiectele trebuiesc create prin apelul unui constructor al clasei respective!
Exceptie de la aceasta regula il fac obiectele care instantiaza o clasa abstracta (care nici nu are
constructori). Aceste obiecte sunt de obicei create prin apeluri de functii (voi reveni). Daca un
obiect al unei clase care nu este abstracta nu este creat apeland un constructor, interpretorul va
da intotdeauna exceptia NullPointerException si nu va rula programul!

II. Haideti sa vedem cum si cand prindem exceptiile pe care le-am clasat in categoria a doua si
care trebuiesc prinse obligatoriu.
In exemplul cu firele de executie de luna trecuta ne-am intalnit cu urmatoarea secventa de
instructiuni:
  try {
    thread.sleep(100);
 }
  catch (InterruptedException e) {
 }
Daca in loc de aceste linii scriam doar thread.sleep(100); atunci ne dadea eroare la compilare.
De ce? Pentru ca metoda sleep a clasei Thread este declarata astfel:
public static native void sleep(long millis) throws InterruptedException
Cuvintele throws InterruptedException spun compilatorului ca aceasta metoda poate arunca
exceptia specificata si de aceea ea trebuie neaparat prinsa. Apoi in corpul functiei sleep exista o
linie de genul if(conditie) throw new Exceptie(); adica aceasta exceptie este aruncata atunci cand
este intalnita conditia.
Bineinteles ca in corpul catch trebuie scris codul care se va executa atunci cand are loc exceptia
respectiva. Dar chiar daca nu avem ceva anume de executat, blocul try-catch trebuie scris in
toate cazurile din aceasta categorie.
Atentie! Cuvantul cheie try nu are sens fara cuvantul catch si nici catch fara try; ele merg
intotdeauna impreuna! (este ca in cazul switch - case).

Iata un exemplu in care vom crea propriile noastre exceptii. Exemplul creeaza o stiva la care
utilizatorul poate adauga sau scoate elemente prin apasarea unor butoane: Push pentru a
adauga, Pop pentru a scoate. Stiva are o capacitate maxima data prin program (la noi 10). Atunci
cand stiva se umple este aruncata o exceptie, cand este goala, este aruncata alta exceptie.
Urmam pasii:
Pas 1. Cream doua clase care mostenesc clasa Exception pentru a arunca exceptii in caz de
stiva plina si goala;
Pas 2. Cream clasa TestStack pentru stiva;
Pas 3. Definim constructorul clasei TestStack pe care il vom apela din aplet;
Pas 4. Definim metoda push care adauga elemente in stiva si arunca exceptia
StackFullException;
Pas 5. In metoda push, daca stiva este plina, este creata si aruncata o noua exceptie, dupa care
cuvantul throw determina si iesirea din metoda;
Pas 6. La fel, metoda pop scoate elemente din stiva si daca stiva este goala, arunca exceptia
StackEmptyException.
Pas 7. Metoda draw deseneaza stiva.
import java.applet.*;
import java.awt.*;

class StackFullException extends Exception { } //pas 1


class StackEmptyException extends Exception { } //pas 1

class TestStack { //pas 2


  Applet parent; //apletul parinte
  int capacitate; //capacitatea maxima a stivei
  int top; //varful curent al stivei

  public TestStack(Applet p,int cap) { //pas 3


    parent=p;
    capacitate=cap;
    top = -1; //initial stiva este goala
 }

  public void push() throws StackFullException { //pas 4


    if (top+1 == capacitate) throw new StackFullException(); //pas 5
    top++;
 }

  public void pop() throws StackEmptyException { //pas 6


    if (top < 0) throw new stackemptyexception();
top--;
 }

  public void draw(Graphics g) { //pas 7


    g.setColor(Color.blue);
    for (int i = 0; i <= top; ++i) {
      int latime = (capacitate-i)*20;
      g.fill3DRect((parent.size().width- latime)/2,parent.size().height-70- i*20,latime,20,true);
  }
 }

Si iata si clasa care mosteneste Applet, care parcurge urmatorii pasi:


Pas 1. Creeaza butoanele Push si Pop si declara obiectul stack de tip TestStack;
Pas 2. In functia init cream o noua instanta a clasei mai sus descrise TestStack;
Pas 3. In metoda paint apelam functia draw a obiectului stack;
Pas 4. In metoda action: daca se apasa butonul Push, se apeleaza metoda push a obiectului
stack folosind blocul try-catch. La prinderea exceptiei, se afiseaza pe fereastra AppletViewer un
mesaj. Daca se apasa butonul Pop se apeleaza metoda pop, iar la prinderea exceptiei se
afiseaza alt mesaj.

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

public class StackApplet extends Applet {


  TestStack stack; //pas 1
  Button pushButton = new Button(); //pas 1
  Button popButton = new Button(); //pas 1

 public void init() {


   setLayout(null);
   pushButton.setLabel("Push");
   pushButton.reshape((size().width-110)/2,size().height-25,50,20);
   add(pushButton);
   popButton.setLabel("Pop");
   popButton.reshape((size().width-110)/2+60,size().height-25,50,20); add(popButton);
   stack = new TestStack(this,10); //pas 2
 } 

 public void paint(Graphics g) {


   stack.draw(g); //pas 3
 }

 public boolean action(Event evt, Object arg) {


   if(evt.target instanceof Button)
     if(arg=="Push") //pas 4
       try {
         stack.push();
         repaint();
       }
       catch (StackFullException e) {
System.out.println("Stiva plina!");
       }
     else if(arg=="Pop") //pas 4
       try {
         stack.pop();
         repaint();
       }
       catch(StackEmptyException e) {
         System.out.println("Stiva goala!");
       }
   return true;
 }

Appleturi Java V: Ferestre si meniuri


          In episodul de astazi veti invata cum sa creati ferestre, meniuri, cutii de dialog etc. intr-un
applet Java. Si in Java exista functii specializate pentru a crea astfel de obiecte, sau elemente
API, ca in orice limbaj de programare sau mediu de programare vizual (Delphi, Visual C, Visual
Basic etc.). Daca ati urmarit episoadele despre Java din numerele trecute, inseamna ca stiti deja
in ce pachet (package) se afla clasele care creeaza astfel de obiecte vizuale: in pachetul
java.awt.

Clasele Window si Frame


          Clasa Window se foloseste pentru a crea ferestre care pot aparea in afara spatiului alocat
pentru applet din cadrul browserului. Clasa Window mosteneste clasa Container si astfel poate
contine si alte componente. Spre deosebire de un aplet care este legat in mod direct de pagina
browserului, ferestrele create cu clasa Window nu sunt restrictionate la o suprafata specificata din
ecran. Obiectele de tip Window astfel create pot fi redimensionate dupa dorinta utilizatorului.
Acest lucru se realizeaza cu ajutorul metodei pack(), care este apelata inainte ca fereastra
respectiva sa fie afisata. Pentru ca ea sa fie apoi vizibila, trebuie apelata metoda show().
Fereastra va fi stearsa de pe ecran si din memoria calculatorului la invocarea metodei dispose().
          Clasa Frame extinde clasa Window prin adaugarea unei bare de titlu, unei margini pentru
redimensionare, suport pentru meniu si abilitatea de a modifica forma cursorului mouse-ului.
          Felul cum va arata fereastra respectiva depinde de platforma GUI (Graphical User
Interface) deoarece va imprumuta din sistem butoanele de tip minimize, maximize, restore sau
close.
          Dupa cum vedeti, clasa Frame are tot ce ii trebuie pentru a face un applet sa arate ca o
adevarata aplicatie, cu meniuri si bara de titlu cu butoane de control Clasele de formare a
meniurilor
          Clasa Frame si clasele prin care se construiesc meniurile sunt intr-o stransa legatura
deoarece clasa Frame este singura clasa din pachetul awt care are suport pentru meniuri.
          Primul pas este crearea unui obiect de tip MenuBar. Acest obiect este legat de obiectul de
tip Frame cu ajutorul metodei setMenuBar() a clasei Frame.
          Al doilea pas este adaugarea de meniuri individuale obiectului MenuBar creat anterior.
Aceste meniuri individuale sunt obiecte ale clasei Menu. Constructorul clasei Menu primeste ca
parametru textul care va fi asociat meniului respectiv. Meniurile astfel create vor fi adaugate
obiectului de tip MenuBar prin metoda acestei din urma clase, add(). Metoda remove() poate fi
folosita pentru a sterge un obiect Menu dintr-un MenuBar.
          Ultimul pas in crearea meniurilor este crearea meniurilor "atom" individuale cu ajutorul
clasei MenuItem. Aceasta clasa este o subclasa a clasei MenuComponent si furnizeaza operatii
suplimentare pentru obiectele "atom", de exemplu de a activa sau dezactiva o optiune.
Constructorul clasei MenuItem va primi ca parametru textul care va fi afisat pentru atomul
respectiv. Metoda add() a clasei Menu este folosita pentru a adauga unui meniu submeniuri. Un
atom poate fi sters prin metoda remove(), asa cum este si in cazul clasei MenuBar. Un separator
care sa desparta doua grupuri de atomi poate fi adaugat cu metoda addSeparator() a clasei
Menu. Acesta este folositor pentru meniuri care au categorii diferite de optiuni.

Clasa Dialog
          Ca si clasa Frame, clasa Dialog este o subclasa a clasei Window. Diferenta cea mai
importanta dintre Dialog si Frame este ca un obiect Dialog poate fi modal. Acest lucru inseamna
ca atunci cand un obiect Dialog modal este afisat, alte ferestre ale apletului nu pot fi editate pana
cand obiectul Dialog respectiv nu este eliberat. Acest lucru este folosit atunci cand se doreste
informarea utilizatorului cu un mesaj de avertisment sau se cere o decizie fara de care programul
nu poate continua.
          Clasa Dialog are doi constructori: ambii iau ca parametru un obiect de tip Frame, adica
fereastra parinte, si un parametru de tip boolean. Daca acesta este true, fereastra va fi de tip
modal, daca nu, nu. Unul dintre constructori mai are un parametru care specifica titlul ferestrei.
          Sa luam acum un exemplu, si anume un applet care construieste o fereastra cu o bara de
meniu. Pentru aceasta parcurgem urmatorii pasi:
1. Cream clasa FrameMenuApplet care mosteneste clasa Applet si care contine un buton la
apasarea caruia se va deschide o fereastra;
2. Cream clasa FrameMenu careia ii asociem o bara de meniuri cu mai multe meniuri
(constructorul);
3. In clasa FrameMenu cream functia handleEvent(Event e), cu ajutorul careia vom seta
actiunile aferente fiecarei optiuni selectate din meniu. Clasa Event are variabila id, care
indica ce tip de eveniment a fost selectat. Astfel, daca a fost apasat butonul de close din
coltul din dreapta sus al ferestrei, variabila id are valoarea WINDOW_DESTROY.
Aceasta este o componenta a clasei Event declarata astfel: public static final int
WINDOW_DESTROY. Altele de acest tip sunt:ACTION_EVENT (un eveniment a aparut),
DELETE ( tasta Delete a fost apasata ), DOWN, UP, ENTER, ESCAPE,
MOUSE_ENTER, MOUSE_EXIT, MOUSE_MOVE, WINDOW_ICONIFY,
WINDOW_MOVED etc. Pentru alte detalii cititi sursa clasa Event.

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

public class FrameMenuApplet extends Applet {


Button button;

public void init() {


setBackground(Color.white);
button = new Button("Deschide fereastra!");
add(button);
}

public boolean action(Event evt, Object arg) {


new FrameMenu("Fereastra cu meniu");
return true;
}
}

class FrameMenu extends Frame {


public FrameMenu(String title) {
super(title);
MenuBar mbar = new MenuBar();
setMenuBar(mbar);
Menu menu1 = new Menu("Fisier");
Menu menu2 = new Menu("Culoare fundal");
Menu menu3 = new Menu("Cursoare");
mbar.add(menu1);
mbar.add(menu2);
mbar.add(menu3);
menu1.add(new MenuItem("Iesire"));

menu2.add(new MenuItem("Alb"));
menu2.add(new MenuItem("Galben"));
menu2.add(new MenuItem("Portocaliu"));
menu2.add(new MenuItem("Rosu"));
menu2.addSeparator();
menu2.add(new MenuItem("Albastru"));
menu2.add(new MenuItem("Verde"));
menu2.add(new MenuItem("Negru"));

menu3.add(new MenuItem("Implicit"));
menu3.add(new MenuItem("Wait"));
menu3.add(new MenuItem("Hand"));
menu3.add(new MenuItem("Move"));
menu3.add(new MenuItem("Text"));
menu3.add(new MenuItem("SE Resize"));

pack();
resize(300,200);
show();
}

public boolean handleEvent(Event e) {


switch(e.id) {
case e.WINDOW_DESTROY:
dispose();
return true;
case e.ACTION_EVENT:
if (e.target instanceof MenuItem) {
String menuName = e.arg.toString();
if (menuName.equals("Iesire"))
dispose();
if (menuName.equals("Alb"))
setBackground(Color.white);
if (menuName.equals("Galben"))
setBackground(Color.yellow);
if (menuName.equals("Portocaliu"))
setBackground(Color.orange);
if (menuName.equals("Rosu"))
setBackground(Color.red);
if (menuName.equals("Albastru"))
setBackground(Color.blue);
if (menuName.equals("Verde"))
setBackground(Color.green);
if (menuName.equals("Negru"))
setBackground(Color.black);

if (menuName.equals("Implicit"))
setCursor(Frame.DEFAULT_CURSOR);
if (menuName.equals("Wait"))
setCursor(Frame.WAIT_CURSOR);
if (menuName.equals("Hand"))
setCursor(Frame.HAND_CURSOR);
if (menuName.equals("Move"))
setCursor(Frame.MOVE_CURSOR);
if (menuName.equals("Text"))
setCursor(Frame.TEXT_CURSOR);
if (menuName.equals("SE Resize"))
setCursor(Frame.SE_RESIZE_CURSOR);
return true;
} // end if
return true;
default:
return false;
}
}
}

Anda mungkin juga menyukai