Appleturi Java (I)
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).
Odata 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 odata 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:
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);
}
}