Documente Academic
Documente Profesional
Documente Cultură
* Limbajul Java *
* ABC-doar *
************************
*********************************
* autor RUSU MIRCEA AUREL VALER *
*********************************
********************************************
* MOTTO: *
* *
* "That's one small step for a man, *
* one giant leap for mankind." *
* Neil Armstrong *
********************************************
De ce Java ?
Prezentare generala . . . . . . . . . . . . . . . . 1
Elementele de limbaj . . . . . . . . . . . . . . . . 12
Clase si obiecte . . . . . . . . . . . . . . . . . . 16
Interfete grafice . . . . . . . . . . . . . . . . . . 19
Pachete . . . . . . . . . . . . . . . . . . . . . . . 22
Executia concurentiala . . . . . . . . . . . . . . . 57
Applet-uri Java . . . . . . . . . . . . . . . . . . . 79
Grafica 2D . . . . . . . . . . . . . . . . . . . . . 130
-2-
Acest mecanism inovativ,a produs o adevarata revolutie,atat pentru
tehnica de calcul,cat si pentru dispozitivele de automatizare si control.
Pentru prima data,se pot edita programe simple pe un calculator de birou
si apoi se pot transmite telefonic,pentru a fi implementate pe un robot,
sau orice alt tip de utilizator final.Exista o versiune de Java speciali-
zata pentru aplicatii de tip telefonie mobila.In prezent,exista nenumarate
solutii tehnologice similare,sau asemanatoare,dar Java este un mediu total
gratuit,este open source si este perfect compatibil,atat pentru scopuri
didactice,cat si pentru solutii prectice imediate.
Interpretorul de Java are 132 Kb.Ca rezultat,se poate crea un mediu
pentru executia aplicatiei Java in mai putin de 1 Mb de memorie (inclusiv
filele ce formeaza aplicatia).Ca rezultat,aplicatiile Java pot fi execu-
tate pe aproape orice tip de aparat electronic modern.
Limbajul Java nu este potivit pentru invatarea notiunilor elementare.
Este recomandabil sa utilizati alte limbaje pentru a invata ce este o
variabila sau o constanta,ce sunt instructiunile si comenzile,cum se
implementeaza functiile si procedurile sau ce este o clasa si un obiect.
Inainte de a incepe lucrul in Java,este bine sa cunoasteti cel putin un
limbaj de nivel superior,sa fiti familiarizati cu programarea orientata
spre obiect si cu modul de organizare al claselor de obiecte vizuale.In
plus,este bine sa aveti cel putin notiuni elementare despre reteaua Inter-
net si Web design (HTML,XML,XSL etc.).Principalele recomandari ar fi
limabjul C++ si Visual C++,limbajul Visual Basic sau Delphi.
In etapa de proiectare,limbajul Java a avut urmatoarele obiective:
1. -sa fie simplu,orientat spre obiect si familiar
2. -sa fie robust si sigur
3. -sa fie portabil si neutru fata de arhitectura intermediara
4. -sa fie cat mai performant (viteza/memorie consumata)
5. -sa fie dinamic,interpretat,multitasking (cu executie paralela)
Este bine sa aveti in vedere aceste obiective si pentru aplicatiile
create de d-voastra cu ajutorul platformei Java.
Spre deosebire de C++ si alte limbaje asemanatoare,Java ofera si alte
instrumente specializate de o importanta exceptionala.In primul rand,
trebuie mentionate instrumentele pentru gestiunea automata a memoriei
(Garbage collection) si cele pentru tratarea automata a eventualelor
erori de executie (Automated Exception Handling).Pe langa acestea,exista
un set intreg de instrumente (tools) auxiliare,ce permit conversia si
reconversia codurilor,arhivarea si dezarhivarea filelor,dezasamblarea
si asamblarea proiectelor,extragerea de documente Web cu comentariile
si referintele unui program etc.
Java este un mediu de programare suprastructurat.Toate datele trebuie
sa fie incluse in obiecte.Obiectele pot exista izolat,sau pot fi grupate
cu ajutorul unor obiecte de tip container.Fiecare obiect este creat cu
ajutorul unei clase (obiectul este o instanta a unei clase).Clasele pot
fi create si definite de catre programator,dar in principal se utilizeaza
clase standardizate,predefinite.Clasele predefinite sunt grupate in
pachete,incluse intr-o biblioteca denumita generic API.Spre deosebire de
alte limbaje vizuale,Java nu poate face apel la clasele incluse in Windows
sau in alte sisteme de operare.In schimb,Java contine un set complet de
clase proprii,ce inlocuiesc cu succes clasele API Windows si cele simi-
lare lor.
-3-
Platforma Java cuprinde un numar foarte mare de instrumente auxiliare,
editoare si programe de verificare si control.Ca rezultat,limbajul Java
se poate utiliza pentru foarte multe tipuri de aplicatii.Cele mai frec-
vente sunt: arhivarea si prelucrarea datelor,comunicatii in retea,actio-
nari si automatizari,prezentarea datelor in retea sub forma de interfete
grafice,componente auxiliare ale unei aplicatii Web (denumite applet-uri),
grafica si design,posta electronica etc.Acest manual,nu are decat titlul
de abecedar si se va axa mai ales pe aplicatiile de tip client,in care
se exploateaza o interfata grafica,formata din obiecte vizuale.Totusi se
vor prezenta succint si notiunile elementare ale limbajului,astfel incat
dupa terminarea acestui ciclu de instructie sa puteti migra spre oricare
dintre mijloacele de expresie ale limbajului Java.
Pentru inceput,trebuie sa va procurati setul de resurse software mini-
mal: setul cu instrumente de dezvoltare inclus intr-un pachet de tip JDK,
setul de instrumente pentru executie,inclus intr-un pachet de tip JRE,un
minim de documentatie tehnica (Exemplu: Java 2 SE 6 Documentation) si
eventual utilitarul NetBeans.Cel mai aimplu este sa descarcati de pe
Internet pachetul complet inclus in Java Standard Edition 6.0 (sau mai
recent).In plus,puteti cauta si descarca orice manuale,carti si tutoriale,
programe demonstrative sau exemple executabile,articole de specialitate,
comentarii si recenzii,etc.
Dupa instalarea programului,directorul Java va contine in folderul JDK
un set de subdirectoare: bin,db,demo,include,jre,lib,sample si src.Dintre
aceste,in aceasta etapa au importanta subdirectorul "bin" in care sunt
arhivate principalele utilitare si pachetul "src" in care sunt arhivate
pachetele de clase API (trebuie dezarhivat cu unzip sau cu JAR).
Daca deschideti "bin",puteti observa un set intreg de executabile mici,
si cateva file de tip DLL.Dintre acestea sunt esentiale: 1.cel denumit
"javac" (compilatorul) si 2. cel denumit "java" (interpretorul).Restul
de executabile implementeaza functii si operatii auxiliare:
javadoc -genereaza documentatie API
apt -instrument pentru addnotare
appletviewer -previzualizeaza un applet in web browser
jar -arhiveaza si dezarhiveaza filele
jdb -este programul debugger (depanator)
javah -genereaza file header de tip C++
javap -dezasambleaza filele .class
extcheck -detecteaza conflictele tip JAR (de arhivare)
Alte instrumente sunt specializate pentru securitatea aplicatiilor:
keytool -gestioneaza certificatele si setul de taste
jarsigner -genereaza sau verifica semnaturile JAR
policytool -administreaza filele de protocol (policy files)
Exista si un set experimental de instrumente destinate pentru depanare:
jinfo -prezinta informatii despre procesor sau despre server
jhat -permite verificarea stivei (din serverul Web)
jmap -prezinta o harta a memoriei (pentru un proces,o fila)
jsadebugd -ataseaza o fila destinata pentru depanarea server-ului
jstack -prezinta lista sirurilor de executie din proces
etc...
In etapa initiala,puteti sa eliminati instrumentele inutile.Este insa
obligatoriu sa pastrati compilatorul,interpretorul si filele DLL.
-4-
Trebuie remarcat de la prima vedere,ca intregul director "bin" nu ocupa
mai mult de 2Mb.Daca la alte platforme de programare toate utilitarele
sunt incluse intr-o interfata comuna (de obicei un meniu central cu seturi
de optiuni),Java a preferat sa ofere toate aceste utilitare separat.
Astfel,compilatorul java (javac) ocupa doar 25 Kb,iar interpretorul
(java) ocupa doar 132 Kb.Concluzia este evidenta: aplicatiile java se pot
crea si intr-un mediu foarte restrans de memorie (de exemplu pe un telefon
celular).Fiecare programator,are libertatea sa-si configureze mediul de
programare dupa bunul plac,sau dupa necesitatile de moment.Bineinteles ca
este recomandabil sa pastrati toate utilitarele din kit,la care puteti sa
adaugati orice alte instrumente de dezvoltare noi.
Pentru executia aplicatiilor Java,nu sunt suficiente doar utilitarele
din pachetul JDK,ci este strict necesar sa instalati si mediul de executie
denumit Java Runtime Enviroment,inclus in pachetul JRE.Acest pachet con-
tine procesorul virtual (Java virtual machine) si un set de biblioteci de
functii necesare pentru executie.Mediul de executie Java,este un mediu
complex,asemantor cu cel al sistemului de operare Windows,dar fara nici
un fel de interferenta cu acesta.In plus,sistemul de mesaje si controlul
evenimentelor sunt organizate complet diferit (nu are rost sa incercati sa
exploatati din Java mesajele de tip Windows).
Pentru a crea o fila executabila,sunt necesare urmatoarele etape:
1.-se editeaza o fila text ce contine codul in limbaj Java
2.-se salveaza fila cu extensia .java (numele filei trebuie sa corespunda
cu numele clasei implementate)
3.-se utilizeaza compilatorul javac pentru a converti fila in bytecode
(compilatorul genereaza o fila cu acelasi nume dar cu extensia .class)
4.-fila .class poate fi executata cu ajutorul interpretorului Java
La prima vedere,acest proces pare destul de complex si confuziv,dar cu
putin exercitiu,procesul de compilare si executie devine o rutina.Cei pre-
tentiosi,pot apela la un editor vizual,in care toate aceste operatii se
efectueaza automat.
Principalul avantaj il reprezinta faptul ca fila .class,generata in
cod simbolic (bytecode),este de dimensiuni mici,compacta,nu interfereaza
cu nici un sistem de operare,poate fi transmisa prin orice sistem de
comunicare (inclusiv telefonic) si poate fi executata pe orice dispozitiv
electronic dotat cu o masina virtuala Java si un interpretor.
Un alt avantaj major il reprezinta faptul ca intreaga platforma este
open source.Totul este la vedere,inclusiv bibliotecile de functii si
filele executabile.Mai mult decat atat,filele .class pot fi dezasamblate
pana la nivel de instructiuni de procesor,astfel incat profesionistii sa
poata verifica fiecare instructiune,registru sau operatie.
Nu in ultimul rand,Java este un limbaj cu un substantial potential de
dezvoltare viitoare,datorita faptului ca se poate adapta la aproape orice
tip de structura hardware.In etapa actuala,inca domina procesoarele cu
linie de executie seriala,bazata pe evenimente asincrone.Fiecare astfel
de eveniment,sau operatie,trebuie sa fie tratata la nivel de procesor
separat de restul evenimentelor.Chiar si procesarea de tip mutitasking,se
face tot serial: -se programaeaza mai multe fire de executie,apoi se
fragmenteaza aceste siruri de coduri si se executa pe rand cate un frag-
ment din fiecare sir (thread).Unele procese pot avea prioritate relativa
sau absoluta,in functie de un set de mark-eri.
-5-
Procesarea seriala a ajuns deja la limita fizica maxima,astfel incat
dezvoltarile viitoare nu se vor putea face decat utilizand metode de
procesare paralela,bazata pe evenimente sincrone.Exista deja calculatoare
de birou dotate cu procesoare duale,dar acest tip de solutie este rezervat
deocamdata doar pentru centralele mari si pentru supercalculatoare.Prin
dimensiunile mici si codul simbolic,Java este perfect compatibila si cu
aplicatiile in care se utilizeaza procese paralele (sistem distribuit).
Mai mult decat atat,Java este un mediu foarte interesant si pentru
iubitorii de hardware.Procesorul virtual ofera o platforma foarte simpla
si clara pentru verificare fizica a functionalitatii unui procesor.Cel
mai simplu exemplu il reprezinta verificarea registrilor cu ajutorul
celor din procesorul virtual (prin comparare).
In orice caz,Java nu este mediul cel mai potrivit pentru incepatori,
sau pentru amatori.Java este un limbaj modular,hiperstructurat sub forma
de clase,organizate arborescent.Clasa radacina este clasa Object.Toate
celelalte clase deriva din clasa Object si respectiv au clasa Object pe
post de superclasa(ancestor).Clasa Object este arhivata in pachetul
denumit "lang" si este implicita (intregul pachet "lang" este implicit).
Clasa Object are un constructor si un set de metode ce vor fi mostenite
de toate celelalte clase: clone(),equals(),finalize(),getClass(),hash-
Code(),notify(),notifyAll(),toString() si wait().Aceste metode pot fi
apelate din oricare dintre mostenitori.Restul claselor se dezvolta arbo-
rescent,spre clase din ce in ce mai specializate.Documentatia Java 2 SE 6
ofera pentru fiecare clasa prezentata si arborele genealogic:
EXEMPLU: java.lang.Object
|_ java.awt.Component
|_java.awt.Button
Urmarind arborele genealogic,puteti evalua toate proprietatile si meto-
dele mostenite de la ancestori.Mecanismul de mostenire este la fel ca si
cel al claselor C++,cu diferenta ca in Java nu exista mostenire multipla
(un obiect nu poate fi creat simultan din doua clase ancestoare).
In mod curent,aplicatiile Java nu vor contine o singura clasa,ci un
set intreg de clase,din care se vor crea obiecte ce interactioneaza prin
diverse apeluri reciproce.Din acest motiv,atunci cand se compileaza fila
sursa,este imperios necesar ca toate filele de resurse sa fie accesibile
pentru compilator.Inainte de a incepe procesul de compilare,trebuie sa
va asigurati ca directorul curent contine,sau are acces la toate pachetele
apelate si la toate filele de resurse incluse in aplicatie.
Si in mediul Java,este extrem de important cum gestionati conexiunile
dintre file si aplicatii (link-urile).Este recomandabil sa nu creati
link-uri spre file de resurse ce urmeaza sa fie actualizate,redenumite,
sau mutate la o alta adresa,deoarece riscati sa generati procese ce nu
pot fi executate prin lipsa unor resurse esentiale.
In linii mari,Java functioneaza la fel ca orice alt program orientat
spre obiect.In fiecare aplicatie se construieste un obiect,din clasa
principala si apoi se executa metodele obiectului.In cazul aplicatiilor
lipsite de obiecte vizuale,se implementeaza o functie principala (denu-
mita de obicei Main),cu ajutorul careia se organizeaza secventa de ope-
ratii,instructiuni si comenzi.Toate datele sunt incluse in obiecte (vizu-
ale sau nonvizuale),astfel incat programul este modular si incapsulat.
Daca a-ti inteles aceasta introducere,are rost sa cititi si restul cartii.
-6-
STRUCTURA GENERALA A PROGRAMULUI (Exemple)
-7-
Primul pas este sa deschideti directorul in care este arhivata fila nou
creata.Utilizati o comanda de genul:
cd C:\Program Files\Java\JDK1.6.0_03\BIN
Eventual,utilizati o comanda "dir" pentru a verifica daca sunt listate
toate filele din directorul BIN.
Pentru a compila fila nou creata utilizati o comanda:
javac HelloWorldApp.java
In mod normal se va genera o fila noua cu extensia .class.Verificati
cu "dir" daca fila HelloWorldApp.class apare in directorul BIN,apoi
puteti lansa fila in executie cu comanda:
java HelloWorld
Acesta este cel mai simplu exercitiu posibil.La inceput,este mai greu
sa va orientati in mediul DOS,dar va imaginati ca doriti sa transmiteti
un mesaj SMS,pe un telefon celular.
Exemplul este functional,deoarece utilizeaza stream-ul de iesire a
datelor System.out.La fel ca si in C++,stream-ul de iesire implicit este
un obiect gata creat si sta la dispozitie in orice moment.
In mod normal,clasele nu pot fi utilizate direct.Este necesar sa cre-
ati un obiect (o instanta a clasei respective),sa initializati obiectul
si abea apoi sa apelati una dintre metodele sale.
Mai observati si faptul ca nu trebuie inclusa nici o fila auxiliara.
Streamul System.out este inclus in pachetul System,care este inclus in
lang (pachetul lang este incarcat automat in orice aplicatie).Pentru o
imagine si mai clara,este bine sa deschideti fila de documentatie si sa
cautati in index pachetul System.Observati ca streamul "out" este inclus
in clasa System sub forma de camp de date static,conectat la clasa
PrintStream din pachetul java.io.Pentru scrierea propriu zisa a datelor
se va utiliza metoda println() a obiectului de tip PrintStream.Este bine
sa urmariti in pachetul de documentatie calea de executie pentru acest
exemplu simplu,pentru a putea intelege cum sunt apelate metodele unui
obiect in orice alta linie de comanda.
Pentru ca relatia dintre utilizator si program sa fie completa,este
necesar sa existe si o modalitate de a introduce date in program.Cea mai
simpla solutie este sa utilizati un stream (flux) de intrare a datelor.
Cel mai practic stream de intrare este inclus in clasa Scanner din pache-
tul denumit Util:
EXEMPLU:
import java.util.Scanner;
class Salut {
public static void main(String[] args) {
System.out.println("Hello World !");
System.out.println("Introduceti numele d-voastra:");
Scanner sc1=new Scanner(System.in);
String salut1 = sc1.next();
System.out.println("Hello: " + salut1 + " Wellcome to Java!");
}}
-salvati fila cu numele Salut.java
Apoi compilati fila (javac Salut.java) si executati fila (java Salut)
din Command Prompt.
Fata de exemplul precedent,am asociat stream-ului de intrare System.in
un obiect de tip Scanner (creat cu New) si apoi am apelat metoda next().
-8-
Pentru cei familiarizati cu C++,cele doua strem-uri sunt echivalente
cu "cin" si "cout".Acest gen de solutie este recomandabil doar pentru
aplicatii ce urmeaza sa ruleze sub DOS,intr-un mediu cu memorie extrem
de limitata.Pentru aplicatiile obisnuite,se recomanda fie crearea unei
interfete grafice cu obiecte vizuale,fie crearea unui applet,pentru o
fila de Web.
Pentru construirea interfetei grafice se utilizeaza un set de compo-
nente,asemantoare cu cele din mediul Windows,dar cu aspect putin diferit.
Trebuie remarcat faptul ca Java nu apeleaza resursele sistemului Windows
ci include un set separat de componente,incluse in pachetul denumit Swing.
EXEMPLU:
import javax.swing.*;
public class HelloWorldSwing {
public static void main(String[] args) {
JFrame frame = new JFrame("HelloWorldSwing");
frame.setDefaultCloseOperation(Jframe.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true); }
}
-salvati fila cu numele HelloWorldSwing.java,apoi compilati si execu-
tati fila.
Daca sunteti familiarizati cu obiectele vizuale,acest exemplu este
relativ usor de urmarit.Functia main() construieste initial un obiect de
tip JFrame si apoi o eticheta de tip jLabel (cu new) .Interfata trebuie
sa contina neaparat un obiect container (frame sau window) in care se
pot include restul de componente (label,combobox,buttons etc.) cu ajutorul
metodei add().Observati ca in acest caz,compilatorul a creat si o fila
auxiliara,denumita HelloWorldSwing$1.class in care arhiveaza informatii
suplimentare,necesare pentru executie (ordinea proceselor de executie).
Acest exemplu,afiseaza deja un obiect grafic.Daca nu va place sa ope-
rati din DOS,puteti sa utilizati un instrument oarecare prin care sa
transformati fila .class in executabil de tip Windows,sau cel putin sa
puteti lansa fila in executie cu un dublu click de mouse.Cel mai simplu
program de acest gen,se numeste Java Runner,are 55 Kb si poate fi descar-
cat de pe Internet,prin bunavointa autorului (Kevin Kuang).Instalati
utilitarul,apoi conectati in Windows Explorer filele de tip .class cu
utilitarul jr.exe (vezi fila readme din program).In continuare,puteti
lansa in executie orice fila .class,cu un simplu click dublu de mouse,
cu conditia sa existe un interpretor de java instalat si un pachet JRE
(continand Java Virtual Machine).In continuare,programarea in Java incepe
sa semene cu cea din oricare alt program vizual.
Este bine sa verificati in Documentatie si obiectele JFrame si respec-
tiv JLabel utilizate in exemplul de mai sus.In general,pentru interfetele
grafice se prefera un alt tip de solutie : se creaza mai intai o interfata
de tip runnable,apoi se lanseaza in executie prin metoda Run().In acest
caz,functiile prin care se implementeaza interfata se declara de tip
private,pentru a nu putea interfera cu restul aplicatiei.
-9-
EXEMPLU:
import javax.swing.*;
public class HelloWorldSwing {
private static void createAndShowGUI() {
JFrame frame = new JFrame("HelloWorldSwing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true); }
public static void main(String[] args){
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() { createAndShowGUI(); }
} );
}
}
-salvati,compilati si executati fila (atentie cum inchideti paranteza
invokeLater() dupa ultima acolada din new Runnable() ).
Observati ca in acest caz,functia prin care se creaza obiectele este
privata si nu poate fi apelata din exteriorul filei.In schimb,functia
main() este publica,creaza o interfata de tip Runnable() cu ajutorul
functiei private si apoi apeleaza metoda Run(),pentru a pune interfata
in executie.Acest procedeu este mai complicat,dat asigura o mult mai
buna securitate a datelor (interfata nu poate fi corupta din exterior).
O alta modalitate pentru prezentarea datelor o reprezinta applet-urile.
Spre deosebire de aplicatiile simple,un aplet nu trebuie sa contina o
functie main(),deoarece va fi pus in executie de motorul intern al filei
HTML in care va fi exportat.Pentru a putea fi recunoscut de browser,este
necesar ca applet-ul sa fie inclus intr-o clasa de tip public,creata prin
derivarea clasei standard JApplet din pachetul javax.swing.
EXEMPLU:
import javax.swing.JApplet;
import java.awt.Graphics;
public class Hello extends JApplet {
public void paint(Graphics g) {
g.drawRect(0,0,getSize().width - 1,getSize().height - 1);
g.drawString("Hello world!",5,15); }
}
-salvati fila cu numele Hello.java,apoi compilati fila
Includeti applet-ul intr-o fila HTML astfel:
<html>
<title> Applet </title>
<h1> APPLET </h1>
<body>
<applet code="Hello.class" width=100 height=20 >
</applet>
</body>
</html>
-salvati fila cu numele Applet1.htm si deschideti cu Internet Explorer
-10-
Un applet poate fi previzualizat si cu instrumentul denumit appletviewer
atunci cand nu doriti sa lansati in executie browser-ul de web.In acest
caz se va utiliza o linie simpla de comanda in care se specifica utili-
tarul si fila HTML ce contine applet-ul:
EXEMPLU: appletviewer applet1.htm
va lansa in executie applet-ul creat anterior.Trebuie remarcat faptul
ca utilitarul appletviewer nu are un interpretor pentru HTML si nu poate
prezenta intreaga fila HTML si doar continutul applet-ului extras intr-o
fereastra tip Java.
Limbajul java permite si redactarea unor file asemanatoare cu applet-
urile,dar destinate pentru a seta sau imbunatati functionalitatea unui
server.Aceste file se numesc servlet-uri si nu vor fi prezentate in acest
manual,deoarece depasesc interesul programatorului amator.
O alta modalitate de structurare a datelor,este sub forma de proiecte
complete.In aceasta etapa,este prematur sa construiti proiecte,dar este
recomandabil sa lansati in executie cateva dintre proiectele demonstrative
impachetate in directorul Demo,din pachetul JDK.
De exemplu,deschideti directorul "applets",apoi "Animator" si alegeti
unul dintre cele patru exemple (click pe fila HTML).Daca studiati putin
proiectul,observati ca exista un set de file pentru resurse grafice si
audio,si respectiv cate o fila sursa.
Un exemplu ceva mai complex,este inclus in directorul GraphicsTest.
Puteti observa ca pentru un singur applet s-au editat 15 file de cod Java.
Probabil ca cel mai simplu exemplu,este cel inclus in directorul numit
JumpingBox.Eventual,puteti incerca sa completati acest exemplu: fie sa
colorati dreptunghiul aleator,fie sa adaugati un tip ordonat de miscare.
Pentru reprezentari grafice elementare,puteti transforma exemplul din
directorul SimpleGraph (de exemplu pentru a reprezenta grafic o functie).
Exemplul SortDemo ofera trei algoritmi diferiti pentru sortarea date-
lor (bubble sort,quick sort si bi-directional sort).
Pentru a va face o idee despre cum arata un proiect complet,dar si
pentru a va sensibiliza cu componentele Swing,deschideti directorul JFC
si alegeti proiectul SwingSet.Lansati fila HTML si testati toate optiunile
apoi deschideti directorul src si studiati filele sursa.
Un alt proiect complex este Java2Demo,in care sunt prezentate princi-
palele mijloace de exprimare grafica.
Daca doriti sa scrieti un editor de tip Notepad,in java,studiati cu
atentie proiectul Notepad.
In general,este bine sa executati toate programele demonstrative si
apoi sa studiati structura fiecarui proiect.Pentru un alt tip de orientare
in proiect,puteti sa instalati si programul NetBeans IDE si apoi sa des-
chideti proiectele exemplificative cu aceasta interfata de programare.
De exemplu,din meniul File alegeti Open project si selectati grupul de
aplicatii de tip Netbeans din directorul "nbproject".Alegeti jfc si apoi
Notepad si apasati butonul OpenProject.Interfata NetBeans va ofera avan-
tajul de a putea deschide si compila fiecare fila separat.In plus puteti
studia structura arborescenta a proiectului si filele de resurse.Observati
ca filele de cod sunt arhivate in directorul "default package" in timp ce
resursele grafice au un director separat.Pentru executia proiectului,
alegeti din meniul Run,optiunea Run Project.
Incercati sa construiti un proiect elementar,cu o fila simpla Java.
-11-
O alta platforma software destinata pentru crearea proiectelor si apli-
catiilor Java,poarta numele de Eclipse si poate fi descarcata gratuit de
la adresa http://www.eclipse.org.Platforma este freeware si open source
si formeaza un instrument de lucru extrem de util.Contine si cateva tuto-
riale,inclusiv cel pentru crearea unei aplicatii simple.Urmariti acest
tutorial si creati o aplicatie simpla de tip HelloWorld.Observati ca spre
deosebire de fila simpla tip text,platforma a generat un folder special
pentru aplicatie,in care sunt incluse:o fila proiect,o fila .classpath
un director denumit "bin" in care se includ filele .class si un director
denumit "src",in care se includ filele sursa.
Platforma nu este utila doar pentru organizarea si impachetarea filelor
ci asigura si un set intreg de facilitati si operatii suplimentare.Des-
crierea acestei platforme,depaseste scopul acestui manual,dar este reco-
mandabil sa descarcati aceasta platforma,daca nu aveti un alt instrument
de lucru preferat.
In general,java lucreaza cu module,organizate ierarhic.Cel mai mic
modul recunoscut de limbaj,poarta numele de token si este echivalentul
unui "atom" lexical (Exemple: identificator,cuvant cheie,operator etc.).
Mai multi tokeni se organizeaza lexical,pentru a forma o expresie (de
obicei o linie de cod) concretizata printr-o comanda sau o instructiune
de procesor.Mai multe astfel de expresii,se grupeaza in blocuri de date,
denumite functii,metode sau proceduri,in functie de particularitatile din
definitie.Pentru a grupa grupuri mai mari de date,expresii,functii si me-
tode,exista structuri mai complexe denumite obiecte si clase.Mai multe
clase formeaza o fila,iar mai multe file se pot grupa in directoare spe-
ciale,pentru a forma pachete.Pachetele sunt continute pe o unitate de
memorie.Unitatile de memorie,pot fi coordonate de o centrala,sau un ser-
ver,pentru a forma o retea locala de calculatoare,iar retelele locale se
poat interconecta pentru a forma o super-retea,denumita Internet.
Limbajul Java permite interventia la nivelul oricarui astfel de modul.
Java este un limbaj de nivel inalt,adica utilizeaza o serie intrega de
conventii,prin care programatorul poate exprima foarte exact ce anume
doreste sa implementeze.Pentru executie,este necesar ca fiecare astfel de
conventie sa fie recunoscuta si retranscrisa simbolic in limbaj masina.
Pentru acest scop,programul compilator face o comparatie intre fila sursa
si un tabel in care sunt reprezentate conventiile de limbaj.Orice grup de
caractere ce nu poate fi interpretat de catre aceasta tabela de comparatie
va fi tratat ca si cand ar fi un simplu sir de caractere.Din acest motiv,
este imperios necesar sa respectati cu strictete conventiile de limbaj.
Acest manual nu poate face o prezentare exhaustiva a acestui set de
conventii,ci va face doar o trecere sumara in revista si se va axa mai
mult pe exemple practice.Pentru a putea verifica si depana aplicatiile
d-voastra este insa obligatoriu sa detineti si o referinta completa a
limbajului (standardul de limbaj).O astfel de referinta poate fi descar-
cata de pe Internet cu numele de "langspec-3.0".
Este inerent ca in etapa de invatare sa faceti o serie intrega de gre-
seli,semnalate si amendate de compilator.Nu va descurajati.Corectati cu
rabdare fiecare greseala,apoi recompilati fila sursa,pana cand obtineti
rezultatul dorit.Si din acest motiv,este recomandabil sa utilizati o plat-
forma de dezvoltare a aplicatiilor (depanarea este mai usoara).
-12-
ELEMENTELE DE LIMBAJ
-13-
Pentru a forma clase si respectiv obiecte,Java recunoaste un set intreg
de tipuri de data,ce pot fi utilizate pentru a forma campuri de date,arii,
constante si variabile,etc.Principalele astfel de tipuri de data sunt:
ARRAY,BIGINT,BINARY,BIT,BLOB,BOOLEAN,CHAR,CLOB,DATALINK,DATE,DECIMAL,DIS-
TINCT,DOUBLE,FLOAT,INTEGER,JAVA OBJECT,LONGNVARCHAR,LONGVARBINARY,LONG-
VARCHAR,NCHAR,NCLOB,NULL,NUMERIC,NVARCHAR,OTHER,REAL,REF,ROWID,SMALLINT,
SQLXML,STRING,STRUCT,TIME,TIMESTAMP,TINYINT,VARBINARY,VARCHAR
(vezi clasa Types din sql)
Pentru a forma expresii,Java recunoaste urmatorii 37 de operatori:
= > < ! ~ ? ? :
== <= >= != && || ++ --
+ - * / & | ^ % << >> >>>
+= -= *= /= &= |= ^= %= <<= >>= >>>=
Urmatoarele caractere au rol de separatori:
( ) { } [ ] ; , .
VARIABILE: -limbajul Java este un limbaj puternic tipizat,astfel fiecare
variabila si fiecare expresie va corespunde unui anumit tip de data ce
poate fi recunoscut de catre compilator.Orice eroare in sintaxa sau in
semantica unei expresii va transforma expresia respectiva intr-un sir de
caractere fara semnificatie pentru compilator.Tipurile de data Java pot
fi impartite in tipuri primitive:boolean si numeric (byte,short,int,long,
char,float,double) si tipuri de data de tip pointer (arii,obiecte si
clase etc.).Exista si un tip special -tipul NULL.Datele de tip sir de
caractere sunt de obicei arhivate in obiecte de tip String.Diferenta intre
tipurile de data primitive si cele de tip pointer o reprezinta faptul ca
tipurile primitive sunt prezente in momentul compilarii,in timp ce tipu-
rile de data prin referinta (pointer) pot fi absente in momentul compi-
larii,pentru a fi prezente in momentul executiei,dupa ce au fost incarcate
filele si fisierele sursa.
Tipurile de data,se utilizeaza pentru a crea variabile,cu declaratii
de genul: int i= 7; sau long nr1 = 2.71352;
In Java,toate declaratiile se fac in interiorul unei clase.In functie
de modificatorul utilizat,o astfel de variabila poate fi statica,caz in
care este prezenta in toate obiectele create din clasa respectiva,sau
poate fi declarata sub forma de camp de date nonstatic (instance varia-
bile) atunci cand nu este declarata cu "static".Variabilele de tip instan-
ta (non-statice) au valori unice in fiecare instanta (obiect creat din
clasa respectiva).Daca variabilele sunt declarate in interiorul unei me-
tode,vor fi variabile locale (adica nu vor avea vizibilitate in afara
metodei respective).Variabilele pot avea si calitatea de parametru,atunci
cand se utilizeaza ca argumente ale unei functii,sau metode.
Aceste amanunte,au o semnificatie mai mica in etapa de compilare a
programului,dar pot avea o importanta majora in etapa de executie.Astfel,
o variabila locala,apelata dintr-un spatiu fara vizibilitate,va genera un
mesaj de eroare.
Tipurile primitive de data asigura si o valoare implicita (nu este
necesar sa fie initializate).Aceste valori implicite sunt: byte = 0,
short = 0,int = 0,long = 0L,float = 0.0f,double = 0.0d,char = '\u0000',
String = null,boolean = null,orice obiect = null.
Constantele (literals) sunt asemantoare cu variabilele,dar au valoare
fixa: boolean result = true; sau char capitalC = 'C';
-14-
Atat variabilele cat si constantele ce contin valori de tip numeric
de tip "int",pot utiliza sistemul numeral decial,cel octal sau cel hexa-
zecimal.Pentru valorile exprimate octal se adauga un zero in fata valorii
numerice iar pentru cele exprimate hexazecimal se adauga 0x.
EXEMPLU: int decimal1 = 26; //are valoarea 26
int octal = 032; //are tot valoarea 26
int hexa1 = 0x1a; //are tot valoarea 26
In cazul numerelor in virgula mobila,se pot utiliza atat scrierea
stiintifica (cu E sau e) cat si conventiile F si f(32-bit float literal)
si respectiv D si d(64-bit double literal)
EXEMPLU: float f1 = 123.4f;
float f2 = 1.234e2;
double d1 = 123.4;
Atat pentru numere cat si pentru stringuri se pot utiliza si urmatoarele
caractere speciale:
\b (backspace), \t(tab), \n(line feed), \f(form feed), \r(carriage return)
\"(double quote), \'(single quote) si \\(backslash).
Un tip special de variabile sunt cele de tip arie de date.O arie este
de fapt un container ce contine un anumit numar de elemente,ordonate in
functie de un numar de indexare.Fiecare element din arie poate fi apelat
prin numarul sau de indexare.Numarul de elemente din arie se declara in
momentul in care se declara variabila.O arie de date,poate contine orice
tip de date:
EXEMPLE: int[] Arie1; // declara o arie de integri
Arie1 = new int[10]; //aloca 10 elemente pentru Arie1
Arie1[0] = 100; //initializeaza primul element
Arie1[1] = 200; //initializeaza al doilea element
....etc.
Pentru alte tipuri de date,se vor utiliza constructii similare:
byte[] aria1; // creaza o arie de bytes
short[] aria2; // creaza o arie de short int
boolean[] aria3; // creaza o arie de valori booleene
String[] aria4; // creeaza o arie de siruri
Exista si functii specializate pentru lucrul cu arii de date.De exemplu,
clasa System contine metoda arraycopy() specializata pentru a copia datele
dintr-o arie in alta.
Datele pot fi convertite de la un tip de data la altul.Pentru tipurile
primitive de date,exista functii standardizate,iar pentru cele de tip
pointer trebuie sa scrieti functii specializate pentru fiecare tip de
conversie.
Prin combinarea variabilelor cu operatori si semne de punctuatie se
pot forma expresii.O expresie este o combinatie de identificatori,date,
semne de punctuatie si apeluri la functii sau metode,in care se respecta
conventiile de limbaj,astfel incat prin evaluarea expresiei sa rezulte
o valoare acceptata de standardul de limbaj.In urma evaluarii expresiei,
procesorul trebuie sa poata inlocui expresia prin valoarea rezultata.In
caz contrar,expresia va fi interpretata a fi un simplu sir de caractere.
EXEMPLU: if(value1==value2) System.out.println("value1==value2");
Expresia va putea fi evaluata astfel: daca cele doua valori sunt egale,
va rezulta un sir de caractere("value1==value2"),iar in caz contrar
va rezulta o valoare nula.
-15-
Evaluarea unei expresii se face in ordinea prioritatilor,sau daca toate
elementele au acceasi prioritate,se va face in ordinea in care apar in
expresie.In cazul operatorilor,prioritatile poarta numele de precedenta
si respecta urmatoarea ordine descrescatoare:
OPERATORI PRECEDENTA
postfix expr++ expr--
unari ++expr --expr +expr -expr ~ !
multiplicare * / %
aditivi + -
salt binar << >> >>>
relationali < > <= >= instanceof
egalitate == !=
bitwise AND &
bitwise exclusiv OR ^
bitwise inclusiv OR |
AND logic &&
OR logic ||
ternar ? :
atribuire = += -= *= /= %= &= ^= |= <<=
>>= >>>=
Daca intr-o expresie se utilizeaza mai multi operatori,acestia vor fi
executati in ordinea precedentei:
EXEMPLU: x+y/100 executa y/100 apoi adauga x
O astfel de expresie se numeste ambigua.Pentru a specifica exact ordinea
de executie se pot utiliza parantezele rotunde:
EXEMPLU (x + y) / 100 executa x+y,apoi imparte la 100.O astfel de
expresie nu este ambigua si nu se preteaza la nici un fel de erori.
Pe cat posibil,este preferabil sa impartiti expresiile mari in seturi de
expresii mai mici,din care sa eliminati orice ambiguitati.In acest mod,
se evita orice eroare,iar aplicatia este mult mai usor de depanat.
Un tip special de expresii sunt cele care poarta umele de instructiuni.
Instructiunile sunt unitati complete de executie cu o semnificatie spe-
ciala pentru compilator.Compilatorul urmeaza sa transforme instructiunea
intr-un set de operatii simple.Exemple: atribuirea de valori,apelul unor
functii si metode,crearea unor variabile sau obiecte,etc.Un tip secial de
instructiuni sunt cele utilizate pentru controlul fluxului de executie si
sunt formate din bucle de operatii iterative (repetitive).
Mai multe expresii si instructiuni se pot grupa in blocuri de executie
denumite functii,metode sau proceduri.Functia este un grup bloc de execu-
tie in care se evalueaza toate expresiile si se executa toate instructiu-
nile dupa care se returneaza valoarea rezultata.Evaluarea unei functii
trebuie intotdeauna sa returneze o valoare.Procedura este asemantoare cu
functia,dar nu returneaza valori,decat daca se sepcifica explicit in
cadrul procedurii.Metoda,este o functie sau o procedura declarata in ca-
drul unei clase.Metoda nu poate fi apelata decat dupa ce se creaza un
obiect din clasa respectiva.Pentru apelul unei metode,se va utiliza o
expresie in care se precizeaza numele obiectului si apoi numele metodei,
separate prin punct.
EXEMPLU: obiect1.metoda1
Atat functiile cat si procedurile sau ,metodele pot avea unul sau mai
multi parametri,caz in care se spune ca sunt "paramterizate".
-16-
CLASE SI OBIECTE
Clasa este modulul structural de baza,in orice program orientat spre
obiect,iar in Java este "piatra de temelie" pentru orice tip de program.
Practic,o clasa este exchivalenta cu un miniprogram complet.O clasa incap-
suleaza un set oarecare de date,numite campuri de date sau proprietati si
un set oarecare de functii executabile,denumite metode.Fiecare metoda
grupeaza un set oarecare de expresii,instructiuni si comenzi.Mai mult
decat atat,o clasa contine de obicei si o functie specializata pentru
operatiile de initializare (creaza un obiect),denumita constructor si
o functie specializata pentru eliberarea memoriei,denumita functie des-
tructor.Clasa ca atare nu este un modul de program functional ci este
doar un set de declaratii si definitii cu ajutorul carora functia cons-
structor poate crea un obiect din clasa respectiva.Cu alte cuvinte,clasa
este un fel de matrita,sau sablon ce se poate utiliza pentru a forma se-
turi de obiecte identice.Fiecare obiect generat,va contine setul de date
si setul de metode declarate in clasa de origine.
Practic,informatia este organizata in felul urmator:
-definitia clasei este arhivata in memorie la o adresa oarecare
-se creaza un pointer (o referinta) spre adresa de memorie la care este
arhivata clasa respectiva.In program,clasa este reprezentata doar prin
pointerul sau,adica prin adresa sa de memorie.
-atunci cand se creaza obiecte,se creaza niste tampoane de memorie in
care se copiaza datele de la adresa clasei si in plus se construiesc si
restul de structuri de memorie necesare:variabile si constante,tampoane,
arii de date etc.Astfel,daca definitia clasei se rezuma la declararea
tipurilor de data,obiectul trebuie sa expandeze memoria astfel incat sa
permita operatii asupra datelor continute.Ca rezultat,obiectul va ocupa
de multe ori mai multa memorie decat clasa de origine.Pentru fiecare
obiect generat,sa va crea cate un astfel de tampon de memorie.In general,
obiectele sunt plasate in memorie la alta locatie decat clasa de origine,
astfel incat sa fie cat mai usor de accesat in program.Datele incluse
in obiect sunt deja date reale,ce pot fi accesate,prezentate sau pot fi
incluse in operatii (in clase toate datele sunt virtuale,sau abstracte).
Declararea unei clase noi,este echivalenta cu crearea unui nou tip de
data.O clasa poate fi declarata si in interiorul unei alte clase,caz in
care se spune ca este o clasa intricata (nested class).Mai frecvent,o
clasa se poate crea modificand o alta clasa.In acest caz,se spune ca este
o clasa derivata din clasa de origine si mosteneste toate campurile de
date si metodele clasei ancestoare.Exista si clase create special pentru
a fi utilizate pe post de sablon pentru alte clase.Aceste clase nu contin
declaratia completa a tipurilor de date,ci doar un schelet general al
modulului si poarta numele de clase abstracte.Clasele abstracte nu pot
fi utilizate pentru a crea obiecte.Clasele abstracte trebuie sa fie deri-
vate in clase mastenitoare la nivelul carora sa se faca completarea decla-
ratiilor si definitiilor necesare.Clasele declarate cu modificatorul "pu-
blic" au vizibilitate si in afara filei in care sunt declarate,adica sunt
un fel de module ubiquitare.Mai multe clase se pot grupa pentru a forma o
interfata.Atunci cand intr-o clasa mostenitoare se declara o metoda noua,
cu nume identic cu cel al unei metode mostenite de la ancestori,fenomenul
poarta numele de supraincarcare (overloading).Obiectul derivat va contine
metoda cu definitia noua (cea mostenita va fi ignorata).
-17-
Cu alte cuvinte,o clasa este oarecum asemanatoare cu o biblioteca DLL,
iar un obiect este asemanator cu un mic program executabil,independent.
O aplicatie formata din obiecte,este asemenatoare cu una formata din mai
multe programe executabile mici.
De ce nu se arhiveaza in memorie direct definitia obiectelor ?
Clasele prezinta urmatoarele avantaje:
-ocupa mai putin loc in memorie
-nu pot fi executate si ca urmare nu pot fi corupte in timpul executiei
-nu se modifica in timpul executiei
-clasa poate utiliza functii automate pentru a genera serii de obiecte
-clasele pot fi derivate si transformate succesiv pentru a forma serii de
clase (din care se obtin serii de obiecte)
-clasele genereaza obiectele automat (cu ajutorul constructorului)
-clasa se poate utiliza pentru a verifica sau depana un obiect in timpul
executiei programului
-clasele se pot arhiva in file independente pentru a putea fi utilizate
simultan de catre un numar mare de aplicatii
-clasele permit si definitii abstracte (fara instante reale)
-clasele sunt arhivate la alte adrese de memorie decat obiectele si permit
depanarea automata a programului,prin intermediul unor functii specia-
lizate pentru tratarea erorilor
In concluzie,obiectele sunt module de program complet independente,ce
pot fi create cu ajutorul unor matrite,numite clase.
Tipurile de data incluse intr-o clasa poarta numele de membri.O clasa
poate contine urmatoarele tipuri de membri:
-membri mosteniti de la o eventuala interfata (ce contine clasa)
-membri mosteniti de la ancestori
-membrii proprii (cei declarati in interiorul clasei)
Atunci cand se vorbeste despre tipul unei clase,aceasta expresie se
refera la:
-tipul de data al argumentelor functiei sau metodei
-tipul de data returnat de membrul respectiv
-clauza de exceptie throw (tipul de data al exceptiei tratate)
Spre deosebire de clasele C++,clasele Java sunt dotate cu un asa numit
mecanism de depanare automata.In ce consta acest mecanism ? Atunci cand se
executa o metoda oarecare,este posibil ca in timpul executiei sa fie
generata o eroare de executie.De exemplu,prin evaluarea unei expresii,
poate sa rezulte un alt tip de data decat cel pe care trebuie sa-l retur-
neze expresia.In acest caz,procesorul intalneste o eroare de executie si
in mod normal intrerupe executia si afiseaza eroarea.Pentru a evita intre-
ruperea programului,metoda poate include si o functie speciala,destinata
pentru a "trata" eventuala eroare in timpul executiei.O astfel de functie
este clauza "throw",prin care metoda poate renunta la o anumita valoare,
atunci cand nu corespunde cu tipul de data asteptat in urma evaluarii
expresiei.In acest caz,daca o expresie returneaza un tip de data eronat,
aceasta va fi "aruncata",adica se va renunta la ea,in loc sa se intrerupa
executia programului.Eroarea este totusi inregistrata si urmeaza sa fie
afisata dupa terminarea executiei (fara a intrerupe programul).Acest
mecanism,pare greoi,dar permite o serie de solutii tehnice inaccesibile
in alte limbaje de programare (decat daca se implementeaza functii auxi-
liare).Rutinele pentru autodepanare sunt facultative.
-18-
Pentru a declara o metoda,formula generala este:
identificator ( lista de paramatrii);
La aceasta formula,se pot adauga modificatorii,tipul parametrilor,tipul
de data rezultat si clauza throw.
Acest manual nu poate include recomandari complete pentru modul de
definitie al unei clase noi.In principiu,trebuie sa organizati datele
de asa maniera incat obiectul rezultat sa fie un modul de program ce
executa un anumit set de operatii.Constructorul trebuie astfel conceput
incat sa creeze si sa initializeze obiectul,iar destructorul trebuie sa
elibereze complet memoria.Daca nu stiti sa creati astfel de clase,este
recomandabil sa utilizati doar clase standard,fie ca atare,fie persona-
lizate prin adaugarea unor metode si campuri noi.Clasele standardizate
(cele incluse in pachetele Java) prezinta urmatoarele avanataje:
-sunt sigure si performante
-sunt deja in memorie,nu trebuie decat sa le apelati
-sunt organizate ierarhic si au o documentatie completa
-pot fi verificate si depanate de orice alt programator
-nu pot genera nici un fel de erori "accidentale"
In toate situatiile trebuie sa tineti cont de urmatorul postulat:
-in program,clasa nu este decat o adresa de memorie,in timp ce obiectul
este un tampon de memorie real,in care se pot face operatii.
In prima etapa,trebuie sa va familiarizati sa lucrati cu utilitarul
Help si cu documentatia de care dispuneti.Java contine zeci de pachete,
cu sute si mii de clase,constante,functii,variabile etc.Nu pot fi reti-
nute si nici nu este util.Pentru orice obiect si orice metoda apelata,
trebuie sa verificati si documentatia,pentru a va asigura ca respectati
standardul de limbaj.Pentru a putea edita programe performante,este obli-
gatoriu sa puteti verifica si depana orice expresie din program.Nu incer-
cati sa faceti "colaje" cu fragmente de program extrase din alte programe,
deoarece rezultatele pot fi imprevizibile.Acest gen de solutie este po-
sibil doar atunci cand cunosteti exact fiecare modul utilizat,impreuna cu
toate extensiile,link-urile,adresele de memorie referite,resursele si
filele de comanda si control,mecanismele de eliberare a memoriei,precum
si masurile de securitate si control.Clasele pot fi utilizate ca atare,
in orice situatie,in timp ce fragmentele arbitrare de program pot fi in
relatie cu diverse alte structuri de memorie,ce tin de mediul de operare,
sau de resurse importate de la diverse alte adrese de memorie.Java permite
lucrul si cu resurse situate la diverse adrese de memorie din reteaua
Internet.Pentru a avea acces la aceste resurse,de multe ori sunt necesare
instrumente si programe speciale,parole si coduri de acces sau structuri
specializate de memorie,programe de conversie etc.Nu incercati sa depa-
nati sau sa transformati un program in care exista si fragmente de cod
pe care nu le intelegeti,sau nu le puteti controla cu resursele locale.
Pe langa pachetele de clase standard incluse in kit-ul de dezvoltare,
puteti descarca din retea o serie de alte pachete auxiliare,specializate
pentru un anumit tip de aplicatie.Este recomandabil ca pentru astfel de
pachete sa obtineti si documentatia completa.Incercati fiecare clasa si
metoda in exemple izolate,inainte de a le include in program.Nu este ru-
sinos sa apelati la Help,sau sa cautati cu Google solutia unei anumite
probleme-dimpotriva,este un test de maturitate ( cu cat sunteti mai bine
informati,cu atat munca d-voastra va fi mai performanta).
-19-
INTERFETE GRAFICE
-20-
Ce avantaje prezinta utilizarea interfetelor ? In primul rand,se pas-
treaza in memorie un sablon,astfel incat interfata cu utilizatorul va
avea intotdeauna acelasi aspect.In al doilea rand,dintr-o interfata se
pot implementa clase diferite,astfel incat chiar daca aspectul va fi
acelasi,functionalitatea va fi diferita.Concret,atunci cand se implemen-
teaza clasa din care se va deriva obiectul,definitia metodelor poate fi
diferita de la o clasa de implementare la alta.Ca rezultat se pot obtine
aplicatii diferite,in care interfata are acelasi aspect,cu acelasi numar
de butoane si componente,dar butoanele vor functiona diferit,sau vor uti-
liza o solutie diferita de rezolvare a problemei.Cu alte cuvinte,functio-
nalitatea unei interfete poate fi actualizata sau transformata foarte
usor.In plus,interfata poate fi utilizata ca punct de plecare pentru
depanarea unei aplicatii.
Mai mult decat atat,o interfata se poate crea prin extinderea unei alte
interfete,sau chiar prin combinarea a doua sau mai multe interfete.
EXEMPLU:
public interface Grup1 extends Interface1,Interface2,Interface3
{ //declaratia constantelor
//declaratia metodelor
// declaratia claselor incluse
// ...etc.
}
In exemplul de mai sus,interfata Grup1 se va forma prin combinarea
celor trei interfete,la care se adauga metodele si clasele din declaratie.
Acest mecanism de mostenire multipla,permite formarea de structuri com-
plexe,pornind de la grupuri mici de componente.Exemplul cel mai simplu il
reprezinta o pagina de ziar electronic,in care interfata grafica grupeaza
pe coloane mai multe interfete simple.
Interfetele pot fi publicate in retea,pentru a putea fi utilizate in
comun,de doi sau mai multi programatori.
O interfata o data arhivata si publicata,nu mai poate fi schimbata,
deoarece va afecta toate aplicatiile create cu ajutorui ei.Din acest
motiv,ori de cate ori este necesar sa actualizati sau sa modernizati o
interfata,este recomandabil sa o extindeti pentru a crea o interfata
noua.
In particular,interfetele de tip grafic ofera o serie intreaga de
avantaje.In primul rand izoleaza utilizatorul de codul aplicatiei.Utili-
zatorul nu trebuie sa stie unde este codul aplicatiei si nici nu are
nevoie de nici o notiune de programare pentru a utiliza programul.In
unele situatii,nici programatorii avansati nu pot avea acces la codul
sursa.Pe langa faptul ca asigura un mecanism mult mai comod de lucru,o
interfata grafica trebuie si sa asigure securitatea programului,astfel
incat codurile sa nu poata fi modificate,iar utilizatorul sa nu poata
introduce date ce pot deranja functionalitatea programului.Pentru acest
scop,fiecare obiect grafic va avea si o functie de control,sau un filtru
de executie,prin care va refuza,sau va "arunca" (throw) eventualele date
introduse gresit.
Pentru o functionalitate cat mai simpla,o interfata poate fi implemen-
tata si cu ajutorul unei functii simple (atunci cand nu este necesar sa
fie declarate metode noi).In acest caz,se vor putea utiliza doar metodele
implicite (standard).
-21-
Cea mai simpla interfata Java este inclusa in pachetul "lang" cu
numele de Runnable.Aceasta interfata are o singura metoda si anume metoda
Run().Aceasta interfata se utilizeaza ori de cate ori doriti sa utilizati
un thread (un fir de exectie) pentru a executa interfata.Thread-urile
(firele de executie) prezinta avantajul ca permit un mecanism serial de
multiprocesare a datelor,asemanator cu executia paralela.Concret,daca
doua sau mai multe interfete se implementeaza in thread-uri diferite,
acestea vor putea fi executate simultan (procesare multitasking).Cu alte
cuvinte,procesorul va putea executa simultan mai multe sarcini,ca si cand
ar exista mai multe procesoare,in paralel.De fapt,procesorul fragmenteaza
thread-urile in fragmente si apoi executa serial cate un fragment din
fiecare thread,pana cand epuizeaza toate operatiile.La viteza de lucru a
procesorului,se creza senzatia de executie paralela.
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() { creazaInterfata(); }} );
}
}
-salvati fila cu numele FrameDemo.java si compilati fila
La executie,exemplul de mai sus va crea o fereastra de suport,denumita
FrameDemo,cu dimensiunile specificate,si cu eticheta amplasata in centrul
ferestrei.Fereastra de tip Frame este containerul de electie pentru orice
interfata grafica vizuala.Acest exemplu,poate reprezenta punctul de ple-
care spre orice interfata grafica de tip Swing.
Se remarca interfata Runnable() utilizata exclusiv pentru metoda Run().
Metoda invokeLater(),din SwingUtilities,are rostul de a apela metoda Run()
doar dupa ce au fost executate toate celelalte evenimente AWT.Cu alte
cuvinte,metoda asteapta pana cand sunt create toate obiectele si pana
cand se executa toate instructiunile de setare,initializare etc.Aceasta
metoda este recomandabila ori de cate ori se utilizeaza un thread,pentru
a implementa o interfata grafica,cu componente din pachetul Swing.
Daca a-ti inteles acest exemplu simplu,restul programarii cu componente
Java Swing este aproape o joaca de copil(se creaza obiecte,se seteaza pro-
prietatile,se organizeaza evenimentele,apoi se executa interfata).
-22-
PACHETE (packages)
Java contine deja sute de clase si limbajul este inca in continua dez-
voltare.Pentru a putea organiza aceste clase,dar mai ales pentru a evita
conflictele de identificator,clasele se grupeaza in containere mai mari,
fie sub forma de foldere,fie sub forma de baze de date.Organizarea in
foldere este mai usor de gestionat si executat de catre orice sistem de
operare,in timp ce organizarea in baze de date,permite operatii diverse
de sortare si selectie a claselor.
Clasele se grupeaza dupa un anumit criteriu,ales fie in functie de
functionalitatea clasei,fie in functie de modul de definitie.Un astfel
de grup de clase,poarta numele de pachet (package) si are rostul de a
asigura accesul la date si de a proteja datele (nu pot fi sterse automat).
Gruparea claselor in pachete,prezinta urmatoarele avantaje:
-programatorii pot observa cu usurinta clasele inrudite intre ele
-programatorii pot identifica usor fiecare tip de clase
-se elimina conflictele de identificator (pot exista mai multe clase cu
acelasi nume,cu conditia sa fie arhivate in alt pachet)
-se creaza un spatiu denumit suplimentar,ce poate fi utilizat pentru orga-
nizarea domeniilor de vizibilitate (Exemplu: clasele dintr-un pachet
oarecare pot avea acces la alte clase din pachet,dar nu si la clasele
arhivate in alt pachet).
Pentru a crea un pachet nou,trebuie sa alegeti un nume unic,diferit
de cele axistente.Numele trebuie sa fie cat mai sugestiv,usor de identi-
ficat si usor de inclus in aplicatii.Frecvent se utilizeaza denumiri
prescurtate,formate din initialele unor cuvinte.Pentru se deosebi de
numele claselor,se utilizeza de obieci doar litere mici.
Exemplu: awt este prescurtarea de la Abstract Window Toolkit
Un pachet,poate contine: interfete,clase,enumerari,exceptii,erori sau
tipuri adnotate.
Pentru fiecare clasa,se vor include in pachet cate doua file: fila
sursa cu extensia .java si fila compilata,cu extensia .class.In mod normal
nu se include decat o singura clasa,in fiecare fila.Fila sursa se va putea
utiliza pentru a dezvolta clasa spre o clasa mai complexa,sau pentru a
forma o clasa derivata (mostenitoare) iar fila compilata se va putea uti-
liza pentru a putea exploata clasa respectiva.
Java este un sistem de lucru "open source" in care totul trebuie sa fie
la vedere.Daca creati clase si pachete noi,trebuie sa oferiti si toate
datele necesare (fila sursa,explicatii si adnotatii,eventual un utilitar
de tip Help,etc.).Pentru a putea identifica pachetul din care face parte
fila sursa,se adauga pe primul rand numele pachetului:
EXEMPLU: package java.awt;
Daca este necesar,fila in care se defineste clasa poate importa alte
clase si pachete.In acest caz,ori de cate ori se va importa clasa res-
pectiva,se vor importa automat si restul de clase interdependente.
Dupa crearea si arhivarea unei clase intr-un pachet,clasa va putea fi
utilizata in programe in trei modalitati:
1.-se apeleaza clasa cu numele intreg (pachet.clasa)
2.-se importa clasa cu include
3.-se importa intregul pachet in care este arhivata clasa.
O clasa se importa prin: import java.numele pachetului.numele clasei
EXEMPLU: import.java.awt.peer.ButtonPeer;
-23-
Pentru a importa intregul pachet,se utilizeaza caracterul *;
EXEMPLU: import.awt.*;
Atentie,atunci cand pachetul respectiv contine la randul sau alte
pachete,acestea nu vor fi deschise automat cu .* ci trebuie sa utilizati
o instructiune import pentru fiecare pachet in parte.
EXEMPLU: import java.awt.*;
import java.awt.color.*;
Atunci cand pachetul contine clase,sau constante cu denumiri lungi,
pentru a nu trebui sa utilizati de fiecare data numele intreg,puteti
importa constanta respectiva cu modificatorul static;
EXEMPLU: pachetul java.Math include si constanta PI astfel
public static final double PI 3.141592653589793
in program se utilizeaza cu:
import java.lang.Math.*;
double numar = Math.PI;
daca PI se importa static,se va utiliza astfel:
import static java.lang.Math.PI;
double numar = PI;
In cazul pachetelor mici,filele sursa pot fi arhivate impreuna cu cele
compilate (in acelasi folder),dar in cazul pachetelor mari se obisnuieste
sa fie arhivate separat.Frecvent,pentru filele sursa se utilizeaza un
director denumit "src",pentru a permite motoarelor de cautare automata
sa identifice fila cat mai usor.
In general,pachetele trebuie arhivate in asa fel incat compilatorul sa
poata avea acces cat mai usor la fila sursa.
Atunci cand doriti sa recompilati o fila sursa,trebuie sa eliminati
eticheta din antet (linia cu instructiunea: package numele; ).
Pentru a asigura accesul cat mai usor la o anumita clasa,se poate
seta o variabila se sistem CLASSPATH:
EXEMPLU: in Windows - C:\> set CLASSPATH = C:\Program Files\bin\clasa1
in Unix - % CLASSPATH = /home/bin/clasa1; export CLASSPATH
Pentru scopuri temporare,sau pentru clase mici de uz restrans,se pot
utiliza file sursa in care nu includeti instructiunea package.In acest
caz,compilatorul va considera ca fila nu face parte din nici un pachet
(chiar daca fila este inclusa intr-un folder cu un nume oarecare).Acest
gen de solutie se poate aplica doar local,sau pentru aplicatii private.
Pentru pachetele oferite in retea,este obligatoriu ca pachetul ca fie
clar definit,printr-un identificator unic(sa fie un spatiu denumit unic).
Pentru a simplifica munca de cautare a unei clase,sau a unui anumit
membru,este bine ca fiecare pachet sa contina si o fila de indexare,in
care toate clasele si metodele sunt ordonate alfabetic.In plus,este bine
ca fiecare pachet sa contina si o nota explicativa: ce clase contine,cum
sunt implementate si pentru ce scop,cum se utilizeaza metodele specifice,
etc.Uneori,este util sa se includa si un arbore genealogic (atunci cand
clasele continute sunt inrudite).
In Java nu se obisnuieste sa se includa file de documentatie separate.
Toate explicatiile si comentariile sunt incluse in fila sursa,de unde
pot fi apoi extrase cu utilitarul special destinat.
Deschideti orice fila sursa.De exemplu: src/java/awt/Button.Observati
ca fila incepe cu un antet (comentariu) in care se specifica autorul.
Urmeaza instructiunea package si instructiunile de import.Apoi urmeaza
-24-
un lung sir de comentarii in care se explica scopul si modul de utilizare
al clasei respective.In continuare,intreaga fila este intesata cu nume-
roase comentarii pentru fiecare functionalitate implementata.
Acest gen de solutie permite altor programatori sa se orienteze cat
mai clar.Java este un minunat instrument didactic pentru programarea avan-
sata (este insa cam greoaie pentru incepatori).
Comentariile ocupa mai mult decat jumatate din fila.Daca urmeaza sa
utilizati fila intr-un mediu cu memorie restransa,puteti sa procedati la
optimizarea codului.Extrageti toata documentatia cu javadoc,arhivati o
copie de siguranta a filei originale,apoi puteti sa stergeti toate comen-
tariile din fila.Fila rezultata va avea aceeasi functionalitate dar va
ocupa mult mai putin spatiu.Puteti sa va personalizati instrumentele de
lucru,cu conditia sa nu alterati functionalitatea codurilor.
Structura pachetelor se rearanjeaza de la o versiune la alta.Pentru
kit-ul de dezvoltare oferit de Java Platform Standard Edition 6,principa-
lele pachete de clase standard sunt incluse in folderele:java si respectiv
javax din directorul "src".In pachetul documentar,gasiti referinte com-
plete in:Java 2 SE 6 Documentation/API (Aplication Proggraming Interface).
Folderul "java" contine urmatoarele pachete:
-applet = contine clasele necesare pentru a crea si utiliza un applet
-awt = contine clasele necesare pentru grafica si imagini si pentru
crearea de interfete
-beans = contine clase pentru aplicatii dezvoltate cu JavaBeans
-io = contine stream-ul de intrare si iesire (obiecte implicite)
-lang = contine clasele java fundamentale (este pachetul implicit)
-math = contine clase aritmetice (a nu se confunda cu lang.math)
-net = contine clase pentru aplicatiile de retea
-nio = contine tampoane,pe post de containere auxiliare pentru date
-rmi = contine pachetul Remote Method Invocation (pt.Virtual Machine)
-security= contine clase si interfete pentru masurile de securitate(acces)
-sql = contine clase ce permit exploatarea bazelor de date
-text = contine clase pentru prelucrare de text,numere,date si mesaje
-util = contine clase utile pt.timp,data,conventii internationale etc.
Folderul "javax" contine urmatoarele pachete:
-accessibility = clase pentru accesul la componentele din interfata
-activation = asigura operatiile de initializare (comenzi,adrese etc.)
-activity = contine exceptii aruncate in timpul pauzelor de executie
-annotatin = posibilele addnotari de constructie sau destructie
-crypto = clase pentru operatii cryptografice
-imageio = pachetul principal Java Image I/O API
-jws = clase pentru operatii Web Service
-lang.model = contine pachete de clase (class-use,element,Source-Ver-
sion,type,util) utile pentru modelarea limbajului Java
-management = contine clasele pentru Java Management Extensions
-naming = clase pentru accesul la servicii denumite
-net = clase pentru aplicatii de retea(altele decat java.net)
-print = clase pentru setarea operatiilor de imprimare
-rmi = contine pachete cu clase pentru aplicatii RMI-IIOP
-script = clase pentru Java Scripting Engine
-security = pachete pentru autorizari,certificate si autentificari
-sound = pachete pentru procesarea sunetelor (midi,sampled)
-25-
-sql = clase pentru accesul bazelor de date din server(server-side)
-swing = componente vizuale pentru interfete grafice
-tools = interfete pentru instrumentele invocate de un program
-transcation = exceptii eliberate in pauzele de executie
-xml = o clasa destinata pentru functionalitatea XML
Pe langa aceste pachete de date,puteti sa adaugati diverse alte pachete
cu clase specifice,create de d-voastra sau descarcate de pe Internet.In
cazul in care oferiti sau comercializati aplicatii scrise in Java,este
recomandabil sa utilizati pachetele standard,sau sa oferiti informatii
detaliate si complete despre pachetele "nonstandard".
Modul in care reusiti sa va orientati in documentatie,precum si modul
in care selectati si dezvoltati clasele si metodele cu care lucrati,este
si va fi definitoriu,pentru calitatea programelor generate de d-voastra.
Cu cat reusiti sa va documentati mai rapid si mai complet,cu atat pro-
gramele realizate vor fi mai clare si mai performante.Indiferent de
scopul propus,este probabil sa gasiti un obiect potrivit,sau cel putin
unul apropiat de cel necesar.Din acest motiv,este recomandabl sa studiati
cat mai amanuntit clasele standard,inainte de a trece la constructia de
clase noi.Daca nici una dintre clase nu va satisface,este bine sa creati
obiectul nou prin dezvoltarea obiectului radacina Object.In acest mod,
obiectul creat de d-voastra se va integra foarte bine cu restul de obiecte
din aplicatie si va puatea fi inclus in formulele automate de cautare,
realizate de asa numitii "roboti".Ca rezultat,aplicatia creata va putea
fi verificata si depanata automat,si va putea fi acceptata mult mai usor
pentru utilizare in sistem "shareware".Atunci cand creati clase noi,este
recomandabil sa le arhivati in pachete noi.Chiar daca o anumita clasa a
fost creata prin extinderea unei clase existente,daca includeti clasa nou
creata in acelasi pachet cu clasa ancestor si oferiti aplicatia in retea,
se vor crea confuzii intre pachetele cu acelasi nume.Din acest motiv,este
esential sa respectati structura pachetelor standard.In cazul in care
optimizati codul,pentru aplicatiile oferite in retea trebuie sa inlocuiti
pachetele "optimizate" cu cele standard.Pentru aplicatiile strict locale,
"optimizarea" poate merge chiar si mai departe.Puteti sa excludeti din
pachet clasele de care nu aveti nevoie,astfel incat sa nu includeti in
aplicatie (respectiv in memoria de executie),decat datele strict necesare.
Aceasta capacitate de modelare a resurselor in functie de memoria exis-
tenta,face din Java o tehnologie de varf,cu infinite posibilitati de
exprimare pentru micro-electronica si dispozitive de comanda si control.
O alta solutie,este sa utilizati pachete optimizate,dar sa le includeti
intr-un pachet nou,cu identificator unic.In acest caz,trebuie sa asigurati
si toata documentatia si sa specificati faptul ca aplicatia este indepen-
denta de pachetele standard.Pe cat posibil este bine sa oferiti si un
utilitar pentru instalarea automata a aplicatiei.
Exista si nenumarate situatii in care limbajul java va fi utilizat
doar pentru a completa o alta aplicatie.Cel mai clar exemplu il reprezinta
applet-urile.Si in acest caz,este recomandabil sa utilizati metoda "open
source" (majoritatea programatorilor si administratorilor de retea refuza
o aplicatie pe care nu o pot verifica si depana complet).
Nu incercati sa epatati prin formule complexe si alambicate.Calitatea
unui programator nu se evalueaza dupa aspectul codului,ci dupa modul de
rezovare a problemei propuse.Solutia cea mai simpla este cea mai buna.
-26-
COMPONENTELE VIZUALE (AWT si SWING)
frame.pack();
frame.SetVisible(true);
}
}
-salvati fila cu numele Buton1.java si apoi compilati se executati
fila
Observati principalele operatii: se creaza containerul de tip JFrame,
se seteaza destructorul (setDefaultCloseOperation) pentru a putea elibera
memoria la inchiderea ferestrei,se creaza butonul,se include butonul in
fereastra (cu add),se actualizeaza fereastra(cu pack) si apoi se seteaza
proprietatea setVisible,pentru a putea afisa fereastra (altfel va fi
activa dar in background).
Butonul creat insa nu este functional.Pentru ca butonul sa poata exe-
cuta ceva (operatii),trebuie sa existe o functie specializata,declansata
de apasarea butonului.Aici intervine cea mai radicala deosebire dintre
C++ si Java.Java nu exploateaza nimic din resursele sistemului de operare,
prin urmare nu poate utiliza nici sistemul de mesaje Windows.In locul
mesajelor Windows,Java utilizeaza un set propriu de mesaje.Aceste mesaje
nu sunt de fapt decat niste constante,incluse in fiecare obiect sub forma
de campuri de date.In momentul in care in aplicatie intervine un eveniment
oarecare,obiectul in care s-a petrecut evenimentul elibereaza in mediul
de operare un mesaj format din una dintre aceste constante.Pentru ca
acest mesaj sa poata fi interpretat,trebuie sa existe o functie speciali-
zata.Aceasta functie se numeste "functia de tratare a evenimentului" si
nu face decat sa verifice permanent daca in mediul de operare un anumit
mesaj este prezent,sau este nul.In momentul in care identifica mesajul,
functia executa un set oarecare de operatii,apoi sterge mesajul din mediul
de operare,pentru a evita operatiile redundante.Acest mecanism este mult
mai rapid si mai discriminativ decat cel din Windows ( in Windows,mai
multe evenimente pot elibera acelasi mesaj si respectiv acelasi mesaj
poate fi asteptat de mai multe functii).
Java contine un set intreg de structuri specializate pentru declan-
sarea si respectiv tratarea evenimentelor.Functiile(metodele) specializate
in "asteptarea" si respectiv "tratarea" evenimentelor poarta numele de
EventListeners("asculta" evenimentele) si pot fi de mai multe tipuri,in
functie de operatia ce a declansat evenimentul.Fiecare astfel de metoda
are un singur argument,format din tipul de data pe care il asteapta.
-28-
Evenimentele pot fi primare(low-level),cum sunt cele declansate prin
taste si dispozitivul mouse,sau pot fi evenimente semantice (toate cele-
lalte).Evenimentele primare sunt comune pentru toate componentele din
interfata,in timp ce cele semantice sunt specifice pentru fiecare obiect.
Cand se apasa un buton,se elibereaza un mesaj "actionPerformed" ce poate
declansa metoda EventListener(apartine unui obiect creat din clasa Event).
EXEMPLU:
import java.awt.*;
import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.*;
public class Data2 extends JPanel implements ActionListener {
JButton button;
public Data2() {
super(new BorderLayout());
button = new JButton("Click Me");
button.setPrefferedSize(new Dimension(400,100));
add(button,BorderLayout.CENTER);
button.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
button.setText("Data curenta: " + new Date());
}
public static void main(String[] args){
JFrame frame = new JFrame("Apasa butonul");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new Data2();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
}
-salvati fila cu numele Data2.java.Compilati si executati exemplul.
Observati ca pentru evaluarea evenimentului "click button" am utilizat:
o interfata ActionListener din care am implementat clasa Data2,un obiect
de tip ActionEvent necesar pentru identificarea mesajului si respectiv o
metoda (actionPerformed) declansata de aparitia mesajului.In plus,in
locul butonului clasic,am creat un component de tip JComponent,in care
am inclus un buton,am setat dimensiunile butonului si apoi am creat o
instanta a interfetei ActionListener (prin intermediul clasei Data2).
Acest exemplu,pare putin incurcat la prima vedere,dar include toate
etapele necesare pentru a exploata obiectele standard.Daca doriti,puteti
sa inlocuiti interfata ActionListener cu una creata de d-voastra,dar
exista riscul sa nu faceti conexiunea corecta dintre mesajul eliberat si
functia de tratare a evenimentului.In plus,trebuie sa codificati si un
timer,care sa citeasca permanent memoria.La orice eroare,fiecare apasare
de buton va crea in memorie diverse instante incomplete ale obiectelor
create,iar butonul va ramane nefunctional.
-29-
Daca doriti sa utilizati o interfata vizuala pentru generarea aplica-
tiei,puteti utiliza NetBeans IDE 6.5 astfel:
Din meniul File,alegeti New Project si alegeti un proiect Java de tip
Java Application,apoi alegeti numele proiectului si adresa de destinatie.
Deselectati optiunea Create main Class (controlul fluxului se va face
prin metoda main generata de obiectul container (JPanel).
Pentru a putea avea acces la paleta de componente vizuale,efectuati un
click drept de numele proiectului (JavaApplication1),alegeti New si apoi
JPanel Form (componentele trebuie incluse intr-un container).In continuare
se poate utiliza paleta de obiecte vizuale.Daca nu este afisata,alegeti
din meniul Window,optiunea Palette.Pentru a alterna intre fereastra de
cod si cea de design,puteti utiliza meniurile Source si Design.Alegeti
meniul Design,apoi deschideti fereastra Palette.Pentru a adauga un obiect
in fereastra de design,alegeti obiectul si trageti de el cu butonul mouse
apasat.De exemplu,trageti un buton OK in fereastra.Sa presupunem ca doriti
sa faceti un utilitar pentru conversia gradelor Celsius in Fahrenheit si
viceversa.Adaugati in fereastra si trei obiecte de tip TextField.In primul
se va introduce valoarea dorita,iar in celelalte doua se vor afisa cele
doua conversii posibile.Pentru a executa aceste operatii cu ajutorul
butonului OK,selectati butonul,executati un click drept,alegeti Events,
apoi Action si actionPerformed.Programul va crea automat functia necesara.
Completati functia astfel:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
double tempFahr = Double.parseDouble(jTextField1.getText())*1.8+32;
double tempCels =(Double.parseDouble(jTextField1.getText())-32)/1.8;
String y = Double.toString(tempFahr);
String z = Double.toString(tempCels);
jTextField2.setText(y);
jTextField3.setText(z);
}
In continuare,reveniti la meniul Design,asezati obiectele in fereastra
dupa bunul plac si apoi adaugati si cateva obiecte Label in care puteti
include explicatiile dorite.Daca doriti sa modificati fonturile si culo-
rile,utilizati din fereastra Palette proprietatile "font" si "background".
Pentru cele trei obiecte de tip TextField setati proprietatea Text la o
valoarea implicita oarecare,de exemplu zero.Schimbati si textul butonului.
Dupa ce fereastra are aspectul si dimensiunile dorite,executati aplicatia
cu Run Main Project.
Daca nu sunteti multumiti de aspect,faceti modificarile dorite.Apoi
puteti construi proiectul cu Clean and Build Main Project (apasand butonul
cu un ciocan si o matura).
Pentru a analiza proiectul in forma finala,deschideti directorul in
care a fost arhivat proiectul(cu Windows Commander).Proiectul va contine
urmatoarele foldere: build,dist,nbproject,src si test.Fila executabila
Windows se gaseste in directorul dist,cu extensia Executable Jar File,
iar fila tip java se gaseste in directorul "src".Fila de tip .class este
arhivata in "build/classes".Analizati structura intregului proiect.
Remarcati ca platforma NetBeans va permite sa realizati o interfata
grafica completa si eleganta,cu doar cateva ckick-uri de mouse si cateva
linii de cod.In continuare,incercati sa dezvoltati aplicatia adaugand
noi componente si functionalitati.
-30-
Fila .java,generata de NetBeans (din directorul src) nu poate fi uti-
lizata direct pentru a crea o fila de tip .class,deoarece nu contine
instructiunile pentru importul pachetelor de clase.Daca doriti sa dezvol-
tati acesata fila in afara platformei NetBeans,trebuie sa adaugati si
toate instructiunile de import.Platforma NetBeans are o tabela de pointeri
catre toate pachetele Java instalate si executa link-ul spre pachetele
necesare doar in momentul executiei (run time).Daca doriti sa executati
filele .class pe un calculator ce nu are instalata platforma NetBeans,fie
utilizati fila gata compilata (din classes),fie recompilati fila dupa ce
a-ti adaugat si pachetele necesare.
Intr-o interfata grafica,componentele pot executa secvente complexe de
operatii.Pentru a putea organiza aceste operatii,este necesar sa proiec-
tati si sa programati serii intregi de evenimente.Un singur component
poate raspunde la doua sau mai multe evenimente,iar acelasi eveniment
poate genera doua sau mai multe operatii simultane,sau consecutive.Cu
alte cuvinte,pentru acelasi obiect se pot conecta mai multe funcii de tip
EventListener si respectiv,pentru acelasi eveniment se pot utiliza mai
multe functii de tip EventHandler.
EXEMPLU: (pentru NetBeans)
-deschideti un nou proiect de tip Java Application (fara clasa Main)
-adaugati un component de tip JFrame Form.
-deschideti Palette si adaugati un buton de tip Button
-redimensionati butonul pentru a putea cuprinde un text mai lung
-click drept pe buton,alegeti Events -> Action -> actionPerformed
-completati metoda jButton1ActionPerformed() astfel:
jButton1.setText("Butonul a fost apasat");
-reveniti in fereastra de design
-click drept pe buton,alegeti Events -> Mouse -> mouseEntered
-completati metoda jButton1MouseEntered() astfel:
jButton1.setText("Mouse este deasupra butonului");
-reveniti in fereastra de design
-click drept pe buton,alegeti Events -> Mouse -> mouseExited
-completati metoda jButton1MouseExited() astfel:
jButton1.setText("Mouse a parasit butonul");
Executati exercitiul cu Run Main Project.Observati cum se modifica
textul in relatie cu miscarile indicatorului mouse pe obiect.
Pentru a conecta o functie noua de tratare a unui anumit eveniment,se
va completa lista Event Handler:
-in fereastra de design selectati butonul
-in fereastra Properties,alegeti Events,apoi apasati butonul cu trei
puncte situat la extremitatea optiunii actionPerformed
-se va deschide fereastra Handlers for actionPerformed in care este
deja inclusa metoda jButton1ActionPerformed
-pentru a adauga o metoda noua,apasati butonul Add,si completati numele
metodei dorite (Exemplu : Redimensionare)
-apoi deschideti fereastra Source si completati metoda astfel:
jButton1.setSize(300,200);
Executati exercitiul din nou.Observati ca la primul click pe buton se
va excuta prima metoda (jButton1ActionPerformed),iar la al doilea click
consecutiv (dublu click) se va executa si metoda a doua (Redimensionare).
Testati butonul cu click si cu dublu click.
-31-
Este foarte important cum alegeti evenimentele si ordinea in care
doriti sa fie executate.De exemplu: evenimentul actionPerformed si eveni-
mentul mouseClicked sunt declansate simultan in momentul in care butonul
este apasat cu un click de mouse.Cele doua evenimente utilizeaza un mesaj
diferit cu o functie Event Handler si o functie EventListener diferita.
Teoretic,cele doua functii se vor executa simultan.Intr-adevar,daca pro-
gramati cele doua evenimente simultan,nu se vor returna erori de compilare
sau de executie.Totusi executia celor doua metode nu se va face simultan
(decat daca utilizati thread-uri diferite).Pentru a evita executiile con-
fuzive,este preferabil sa evitati acest gen de solutii.Inainte de a incepe
programarea propriu zisa,este bine sa faceti o mica schema de executie,
in care sa stabiliti ce operatii doriti sa implementati,ordinea de execu-
tie si respectiv ordinea de prioritati.Apoi alegeti componentele cele mai
potrivite,selectati mesajele cele mai clare pentru fiecare component si
abea apoi incepeti sa introduceti codurile necesare.La un moment dat,
Internet-ul a fost inundat cu reclamatii de acest gen.Programul in sine
este foarte bun (chiar exceptional),dar se pot gasi solutii in care exe-
cutia sa fie confuziva (sau chiar imposibila),fara ca erorile sa fie
detectate in etapa de comiplare (daca sintaxa este corecta).Inainte de
a trece la interfete complexe,este bine sa faceti cat mai multe exercitii
cu obiecte simple si cu operatii simple,secventiale.Executia paralela si
multiprocesarea,vor fi abordate intr-o etapa de studiu mai avansata.
Daca analizati putin fila sursa pentru exemplul anterior,puteti observa
cum cele patru evenimente sunt organizate sub forma de metode.Doua dintre
evenimente sunt de tip ActionEvent si vor fi incluse intr-un obiect de
tip ActionListener,iar alte doua evenimente sunt declansate de catre
mouse si vor fi incluse intr-un obiect de tip MouseListener.
Intr-un program sunt posibile numeroase astfel de evenimente,dar numai
o parte dintre ele sunt utilizate frecvent in programare.Pentru cele mai
frecvente dintre ele,Java include cate o interfata specializata pentru
interceptarea ("ascultarea") evenimentului respectiv.Aceste interfete
standard sunt: ActionListener,AncestorListener,CaretListener,CellEditor-
Listener,ChangeListener,ComponentListener,ContainerListener,DocumentLis-
tener,ExceptionListener,FocusListener,HierarchyBoundsListener,Hierarchy-
Listener,HyperlinkListener,InputMethodListener,InternalFrameListener,
ItemListener,KeyListener,ListDataListener,ListSelectionListener,MenuDrag-
MouseListener,MenuKeyListener,MenuListener,MouseMotionListener,MouseListe-
ner,MouseMotionlistener,MouseWheelListener,PopupMenuListener,Property-
ChangeListener,TableColumnModeListener,TableModeListener,TreeExpansion-
Listener,TreeModeListener,TreeSelectionListener,TreeWillExpandListener,
UndoableEditListener,VetoableChangeListener,WindowFocusListener,Window-
Listener,WindowStatelistener.
Fiecare dintre aceste interfete,contine una sau mai multe metode spe-
cializate pentru evenimentul respectiv.Pentru o parte dintre aceste inter-
fete exista si clase de tip "adapter",create special pentru a implementa
o anumita functionalitate.Exemple: ComponentAdapter,ContainerAdapter,
FocusAdapter,KeyAdapter,MouseAdapter,WindowAdapter etc.Aceste clase contin
toate metodele necesare pentru "asteptarea" si inregistrarea unui anumit
tip de eveniment (Exemplu: evenimentele mouse pentru clasa MouseAdapter).
Clasele adaptoare pot fi apelate direct,interfetele necesita implementare.
-32-
Nu trebuie sa invatati pe de rost aceste interfete.Daca utilizati plat-
forma NetBeans,sau o alta interfata automata,toate obiectele si legaturile
vor fi create automat.Are rost sa intelegeti mecanismul lor,atunci cand
doriti sa depanati o aplicatie,sau atunci cand proiectati o functionali-
tate noua.In toate cazurile,trebuie sa va puteti orienta cat mai rapid in
sursa bibliografica.
Nu toate aceste interfete sunt utile pentru fiecare component.Interfe-
tele comune pentru toate componentele Swing sunt: ComponentListener,
FocusListener,KeyListener,MouseListener,MouseMotionListener,MouseWhell-
Listener,HierarchyListener si HierarchyBoundsListener.Restul pot fi apli-
cate doar pentru unul sau mai multe dintre componente.Pentru a va fami-
liariza cu toate tipurile de evenimente,este bine sa creati cel putin cate
un exemplu din fiecare tip (vezi si tutorialul oferit de firma Sun).
Pachetul Swing contine un obiect(o fereastra) special destinat pentru
selectarea si setarea culorilor preferate:
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.colorchooser.*;
public class ColorChooserDemo extends JPanel implements ChangeListener{
protected JColorChooser tcc;
protected JLabel banner;
public ColorChooserdemo(){
super(new Borderlayout());
banner = new JLabel("Obiectul ColorChooser",JLabel.CENTER);
banner.setForeground(Color.yellow);
banner.setBackground(Color.blue);
banner.setOpaque(true);
banner.setFont(new Font("SansSerif",Font.BOLD,24));
banner.setPrefferedSize(new Dimension(100,65));
JPanel bannerPanel = new JPanel(new BorderLayout());
bannerPanel.add(banner,BorderLayout.CENTER);
bannerPanel.setBorder(BorderFactory.createTitleBorder("Banner"));
tcc = new JColorChooser(banner.getForeground());
tcc.getSelectionModel().addChangeListener(this);
tcc.setBorder(BorderFactory.createTitleBorder("Alegeti culoarea"));
add(bannerPanel,BorderLayout.Center);
add(tcc,BorderLayout.PAGE_END); }
public void stateChanged(changeEvent e) {
Color newColor = tcc.getColor();
banner.setForeground(newColor); }
public static void main(String[] args) {
JFrame frame = new Jframe("ColorChooserDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new ColorChooserDemo();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane)
frame.pack();
frame.setVisible(true); } }
-33-
Salvati fila cu numele ColorChooserDemo.java.Compilati si executati.
Obiectul ColorChooser ofera mai multe metode de selectie pentru culoarea
sau nuanta dorita,precum si o fereastra Preview pentru previzualizare.
Oricat de riguros alegeti combinatia de culori pentru interfata gra-
fica creata de d-voastra,utilizatorul va dori alta combinatie de culori.
Cea mai simpla solutie este sa adaugati un astfel de obiect si apoi sa
conectati toate obiectele din interfata la obiectul ColorChooser (de
exemplu printr-o tasta de short-cut).
EXEMPLU: (pentru NetBeans)
-deschideti un nou proiect (fara functia main())
-adaugati o fereastra JFrame Form
-din Palette adaugati un obiect de tip Label
-setati dimensiunile,culorile si fonturile
-din Palette,alegeti Swing Windows si adaugati un ColorChooser
-selectati obiectul Label -> Events -> mouseClicked si adaugati metoda
Click1 astfel:
private void Click1(java.awt.event.MouseEvent evt) {
jLabel1.setText("Click de mouse");
jLabel1.setForeground(jColorChooser1.getColor()); }
Incercati sa conectati obiectul jColorChooser1 si la alte obiecte din
interfata.
Pentru a putea afisa sau ascunde obiectul jColorChooser1 se va putea
utiliza metoda setVisible(),mostenita de la ancestorul JComponent.
De exemplu,adaugati doua butoane denumite Deschide si Inchide.Pentru
butonul Deschide editati evenimentul actionPerformed:
jColorChooser1.setVisible(true);
iar pentru butonul Inchide editati evenimentul actionPerformed:
jColorChooser1.setVisible(false);
Cu un singur obiect ColorChooser,puteti permite utilizatorului sa
aleaga si sa seteze dupa bunul plac,intreaga paleta de culori din inter-
fata grafica.Eventual,puteti crea si o metoda prin care setarile alese
de utilizator sa ramana permanente (de exemplu,salvati setarile intr-o
fila independenta si rescrieti fila automat,ori de cate ori este necesar).
Obiectul are si un set de metode proprii,dar majoritatea metodelor sunt
cele mostenite de la JComponent.
Exista numeroase teorii si recomandari cu privire la modul de selectie
si combinare a culorilor dintr-o interfata.In principiu,este esential ca
sa asigurati un contrast cat mai bun intre culoarea de fond si cea rezer-
vata pentru text.Atunci cand interfata contine si imagini sau desene,este
recomandabil sa utilizati o culoare de fond cat mai mata,pentru a nu
estompa calitatea imaginilor.In general,culorile stralucitoare nu se vor
utiliza decat pentru elementele pe care doriti sa le evidentiati.Pentru
fond si pentru elementele mai putin importante,se aleg de obicei culori
pastelate,mai sterse.Este recomandabil sa nu utilizati mai mult de 4-5
culori/fereastra (pentru a nu obosi ochiul si simtul estetic).Daca doriti
sa atrageti neaparat atentia asupra unui element,puteti utiliza efecte de
clipire (blink) sau secvente animate,dar in general,aceste efecte nu numai
ca sunt obositoare,dar consuma inutil si din numarul de procese executate
de procesor/ciclu de operatii.Este recomandabil sa utilizati buclele de
repetitie doar pentru operatii utile: sortari,cautarea si selectia de
date,introducerea de date utile,setari automate,etc.
-34-
O alta operatie extrem de frecventa intr-o aplicatie,o reprezinta
identificarea si exploatarea unei file de resurse.In situatia in care
fila de resurse este fixa,se poate utiliza o cale directa de acces la
fila respectiva.In cazul in care doriti ca utilizatorul sa poata alege
singur fila din care extrage un anumit tip de date,trebuie creata o inter-
fata simpla pentru acces la fila.Java Swing include un obiect specializat
pentru aceasta operatie,cu numele de JFileChooser.Obiectul are un set
destul de bogat de metode proprii si un filtru,cu ajutorul caruia se poate
restrictiona accesul la un anumit tip de file.Obiectul are o interfata
vizuala si este foarte practic.De exemplu,pentru a deschide o fila de tip
text,puteti utiliza o aplicatie de genul:
EXEMPLU:
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.*;
-35-
//metoda Scrie() este necesara doar daca preluati textul din fila deschisa
-36-
Aceleasi transformari sunt necesare si pentru metoda Scrie():
public void Scrie(String[] args) {
try {
java.io.FileReader is1 = new Java.io.Filereader(nume);
char[] buffer1 = new char[32000];
int len;
while ((len = is1.read(buffer1)) != -1) {
String sir2 = new String(buffer1,0,len);
jTextArea1.append(sir2);
};
is1.close();
}catch (java.io.IOException ex1) {
System.err.println("Eroare: " + ex1.getMessage());
}finally {}
};
Pentru a depana eventualele erori,executati un click drept pe numele
proiectului din fereastra Inspector si alegeti Test.Daca nu se returneaza
nici un mesaj de eroare,puteti executa cu Run Main Project,apoi construiti
proiectul cu butonul Clean and Build Main Project.
Pentru exercitii,puteti personaliza obiectul FileChooser dupa bunul
plac.De exemplu,daca doriti sa utilizati obiectul doar pentru a deschide
file cu o anumita extensie,puteti adauga un filtru.
EXEMPLU:
JFileChooser chooser = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter("jpg");
chooser.setFileFilter(filter);
In acest caz,obiectul FileChooser nu va mai deschide decat filele cu
extensia .jpg (simplifica identificarea unei file).
Atentie.Obiectul FileChooser nu executa nici un fel de actiuni asupra
filelor.Daca doriti sa cititi date din fila,sau daca doriti sa puneti in
executie fila respectiva,trebuie sa editati o metoda speciala pe care sa
o atribuiti butonului Open.De exemplu,daca doriti sa deschideti o fila
de tip HTML in loc de o fila de tip text,inlocuiti obiectul TextArea cu
un obiect de tip JEditorPanel si modificati metoda butonului astfel:
public String nume;
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt){
javax.swing.JFileChooser fc = new javax.swing.jFileChooser();
int returnVal = fc.showOpenDialog(NewJFrame.this);
if( returnVal == javax.swing.JFileChooser.APPROVE_OPTION){
java.io.File fila1 = fc.getSelectedFile();
nume = fila1.getName();
jEditorPane1.setText(nume);
java.net.URL adresa = java.lang.ClassLoader.getSystemResource(nume);
try {
jEditorPane1.setPage(adresa);
} catch (java.io.IOException e) {
System.err.println("Eroare de preluare a filei:" + nume);
}
}
-metoda se va putea utiliza pentru file Web preluate din retea,sau
pentru file HTML locale,arhivate in acelasi director (src).
-37-
Exista foarte multe formate pentru datele de tip text.Incepand de la
seturile de caractere utilizate si pana la filele de text ce utilizeaza
un limbaj marcat de tip Hipertext,datele de tip text pot utiliza nenuma-
rate tipuri si stiluri de fonturi,culori,grafica si resurse audio-vizuale,
etc.Pentru a putea satisface toate gusturile,Java include un numar foarte
mare de obiecte,create special pentru a putea administra datele de tip
text.Astfel,doar pachetul swing,contine sase obiecte vizuale special des-
tinate(JTextField,JFormattedTextField,JPasswordField,JTextArea,JEditorPane
si JTextPane) precum si un pachet denumit "text",in care sunt arhivate
alte peste 120 de clase si interfete (Exemple: Caret,Document,EditorKit,
Element,html,NavigationFilter,Style,StyledDocument,TableView etc.).Este
imposibil de facut un ghid complet de utilizare a tuturor acestor resurse.
Este insa important sa stiti ca exista,pentru a putea alege atunci cand
este cazul,obiectul cel mai potrivit,sau cel mai usor de utilizat.
Dintre obiectele vizuale,cel mai simplu si probabil cel mai frecvent
utilizat este obiectul JTextField.Se creaza obiectul si se citesc datele
introduse de utilizator prin metoda getText().JPasswordField este destul
de asemanator cu JTextField dar a fost creat special pentru a solicita o
parola.Obiectul mascheaza caracterele introduse si preia parola cu aju-
torul metodei getPassword().Alt obiect simplu este JFormattedText,creat
pentru a lucra cu date in format tipizat.Acest obiect contine un conver-
tor automat pentru formatarea datelor,denumit "formatterFactory" ce per-
mite utilizarea directa a urmatoarelor tipuri de data: number,date,time,
percent,currency si mask.
Obiectul JTextArea este destinat pentru texte ample,iar JEditorPane si
JTextPane sunt create pentru a permite utilizarea textelor de tip hiper-
text.JEditorPane preia file intregi de tip HTML,in timp ce JTextPane
compune niste file asemenatoare cu cele HTML din siruri de caractere si
stiluri de prezentare,organizate sub forma de documente StyledDocument.
EXEMPLU:
-deschideti un proiect NetBeans,adaugati JForm si deschideti Design
-adaugati cate un obiect de tip JTextField,JPasswordField,JFormatted-
TextField,JTextArea,JEditorPane si JButton.
-pentru campul JFormattedTextField,alegeti din fereastra Properties
optiunea formatterFactory si selectati tipul de data cu care doriti sa
lucrati in obiectul respectiv.
-adaugati in proiect o fila HTML (Exemplu: Bv1.htm )
-selectati butonul,click drept -> Events -> Action -> actionPerformed
si editati urmatoarea metoda:
public String s1;
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt){
jTextArea1.append(jTextField1.getText() + "Parola= ");
jTextArea1.append(s1.valueof(jPasswordField1.getPassword()));
java.net.URL adresa = java.lang.ClassLoader.getSystemResource("Bv1.htm");
try {
jEditorPane1.setPage(adresa);
} catch (java.io.IOException e) {
System.err.println("Eroare de preluare a filei: " +"Bv1.htm");
}
}
Testati si apoi executati exemplul cu Run Project.
-38-
Obiectul JTextPane este putin mai greu de exploatat,deoarece nu are
metode executabile directe.Fila text se formeaza intr-un tampon de memo-
rie intermediar.Acest tampon intermediar se creaza cu ajutorul unui obiect
(document) de tip StyledDocument.In acest tampon se introduc sirurile de
tip text,cu ajutorul metodei insertString() ce permite ca fiecare sir de
caractere sa utilizeze un alt stil de prezentare (fonturi si culori dife-
rite etc.).Obiectul de tip StyledDocument ofera si un set propriu de meto-
de si proprietati.In plus,stilul de prezentare se poate compune cu aju-
torul unei functii speciale,in care se pot utiliza orice alt fel de clase
si obiecte de formatare a textului.Dupa ce documentul este complet redac-
tat (concatenat),acesta poate fi importat in obiectul de prezentare.
EXEMPLU:
-deschideti un nou proiect NetBeans
-adaugati JForm si deschideti fereastra de Design
-adaugati un obiect de tip JTextPane si un buton
-editati pentru buton urmatorul eveniment actionPerformed:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt){
java.swing.text.StyledDocument doc = jText1.getStyledDocument();
try {
doc.insertString(doc.getlength(),"text ",doc.getStyle("large"));
}catch (javax.swing.text.BadLocationException ble) {
System.err.println("Textul nu poate fi inserat !");
}
}
Pentru ca textul sa fie prezentat cu un stil oarecare,utilizati metoda
doc.getStyle() pentru fiecare sir in parte.
Pentru a simplifica munca de programare este bine sa aveti sabloane
gata pregatite pentru fiecare stil de prezentare dorit.Astfel in momentul
in care construiti documentul,nu va mai trebui decat sa importati textul
si respectiv sablonul de prezentare.
Obiectul ancestor pentru toate obiectele vizuale de tip text este
JTextComponent.Aceasta clasa radacina asigura urmatoarele functionalitati:
-un document pentru continutul obiectului
-o forma(fereastra) de afisare a obiectului pe ecran
-un kit de editare (citire/scriere) pentru operatiile I/O
-o bucla infinita pentru operatii tip "undo" si "redo"
-un cursor cu filtru de navigare si "caret change listener"
(ce vor fi mostentie in toate obiectele derivate).
Pentru a mosteni metode noi,sau pentru a personaliza aceste obiecte,
fie se pot deriva clase noi,fie se pot include obiectele standard in con-
tainere,ce pot contine si metode personalizate.Atunci cand se lucreaza
cu grupuri mari de astfel de obiecte,este chiar recomandabil sa separati
fiecare grup intr-un container.In acest mod,se creaza cate un spatiu
"namespace"(spatiu de vizibilitate),pentru fiecare grup.Prin aceasta me-
toda,obiectele de acelasi fel se izoleaza astfel incat sa se excluda orice
risc de corupere accidentala a datelor (buclele automate nu vor putea
citi decat obiectele incluse in acelasi container).Este bine ca fiecare
operatie sa fie controlata de un buton sau un meniu separat (pentru a fi
mai usor de verificat si depanat),dar este posibil ca un singur buton sa
execute operatii multiple,sau sa raspunda la mai multe tipuri de eveni-
mente posibile.
-39-
In exemplele precedente,metodele care contin cate o operatie de citire
a datelor contin si cate o bucla try-catch,destinata pentru identificarea
si tratarea exceptiilor.Acest mecanism face parte din mecanismul automat
prin care Java evita eventualele erori de citire a datelor.In general,o
exceptie este un eveniment ce intervine in momentul executiei si deter-
mina intreruperea fluxului de operatii (procesorul nu stie cum sa proce-
seze datele respective).Acest gen de situatii,nu pot fi depistate sau
depanate in etapa de compilare,ci trebuiesc anticipate si rezolvate cu
ajutorul unor functii speciale.Exemple: o exceptie intervine atunci cand
un obiect asteapta date de tip String iar utilizatorul introduce o data
calendaristica,sau atunci cand o variabila este de tip Integer si utili-
zatorul introduce o valoare in virgula mobila (float),etc.Acest gen de
situatii nu pot fi rezolvate de catre compilator si trebuiesc anticipate
si prevazute prin functii speciale.Si alte programe includ rutine pentru
depanarea automata,dar Java a ales sa includa aceste operatii in toate
functiile ce efectueaza operatii de tip I/O.Fie ca sunt Stream-uri,fie
ca sunt obiecte de tip File sau FileReader,toate operatiile de intrare/
iesire asteapta si o solutie de tratare a exceptiilor.Toate erorile si
exceptiile din Java au ca superclasa,clasa Throwable.Din aceasta clasa se
deriva cate o clasa mostenitoare,din care se formeaza obiecte.Fiecare
exceptie sau eroare posibila formeaza un astfel de obiect.Obiectele pot
fi recunoscute si apoi tratate in functie de specificatiile programato-
rului.Pentru fiecare functie,exista una sau mai multe astfel de exceptii
ce pot fi tratate in mod automat.Cele doua metode principlae de tratare a
exceptiilor sunt "catch" (capturarea exceptiei) si "throw" (eliminarea
exceptiei).Se utilizeaza metoda de capturare a exceptiei atunci cand se
considera ca datele respective sunt utile si trebuie sa fie prelucrate
prin alt mecanism.De exemplu,daca utilizatorul introduce datele intr-un
format gresit,metoda "catch" va prelua datele respective,va face operatia
de conversie necesara si va returna datele in formatul acceptat (adica va
corecta eroarea automat si eventual va afisa un mesaj de avertizare).Daca
datele respective nu sunt utile in program,pot fi eliberate fara nici o
altfel de prelucrare.In acest caz,se utilizeaza metoda "throw".Pentru
fiecare metoda I/O,documentatia contine si tipul de exceptii ce pot fi
tratate automat:
EXEMPLE: public int read() throws IOException
public void throwException() throws CharacterCodingException
Pentru a putea alege exceptia capturata sau eliminata in timpul unei
operatii de preluare a datelor,trebuie sa consultati documentatia Java,
sau sa utilizati depanatorul automat vizual.De exemplu,in NetBeans,in
momentul in care introduceti in cod numele unui obiect oarecare,apare o
fereastra interactiva in care sunt incluse toate constantele,variabilele,
proprietatile sau metodele obiectului (proprii sau mostenite).
In momentul in care apare in timpul executiei o eroare oarecare,Java
creaza un obiect specializat,denumit "exception object",in care include
informatii despre tipul de eroare si momentul din program in care a inter-
venit.Mecanismul prin care eroarea se transforma in obiect poarta numele
de "throwing an exception".Dupa ce eroarea a fost transformata in obiect,
procesorul asteapta indicatii asupra modului de prelucrare a obiectului.
Modul de prelucrare a obiectului poarta numele de "tratare a exceptiei".
Functiile si metodele specializate pentru tratarea exceptiilor pot fi
-40-
incluse in: functia care a generat eroarea,in functia main(),intr-o fun-
ctie specializata dar fara executie automata,sau intr-o functie speciali-
zata ce contine si o rutina automata denumita "exception handler"(tratarea
exceptiei).Procesorul verifica toate aceste variante.Daca exista o astfel
de functie,prelucreaza obiectul conform indicatiilor,iar in caz contrar,
obiectul este ignorat.Cu alte cuvinte,mecansimul "throw" este echivalent
cu cel de eliminare a datelor din program (acestea nu vor avea vizibili-
tate in program,dar nici nu vor intrerupe executia).Sau altfel spus,eroa-
rea se transforma intr-un obiect inert.
In Java,in cazul functiilor I/O,functia de tratare a exceptiei este
inclusa direct in functia ce a generat eroarea (functia contine si un
"exception handler").Ca rezultat,are prioritate absoluta si se executa
imediat.In alte limbaje,procesorul trebuie sa parcurga toate functiile
din program,pana cand identifica metoda specializata,cu un numar foarte
mare de operatii in plus,si cu o intarziere considerabila in cazul pro-
gramelor mari.
Pentru a edita o functie de tratare a exceptiilor,se utilizeaza de
obicei o bucla de tip try-catch.Primul bloc al acestei bucle il formeaza
blocul try { ...} in care se includ toate operatiile in care suspectati
ca utilizatorul poate introduce erori (toate operatiile ce pot genera o
exceptie).Cel de al doilea bloc,denumit "catch (tipul exceptiei nume){..}"
trebuie sa identifice exceptia si apoi sa utilizeze un set de operatii
pentru corectarea erorii.Daca nu puteti anticipa metoda cea mai buna de
corectare a erorii,atunci blocul catch trebuie sa contina cel putin un
mesaj de avertizare,prin care sa semnalizati tipul de eroare si momentul
in care a intervenit,astfel incat utilizatorul sa-si poata corecta singur
gresala.Exemplu de mesaj: "Atentie- introduceti o valoare de tip Integer".
Cu ajutorul acestor functii,se vor putea rezolva toate situatiile
anticipate de programator.Bineinteles ca exista si situatii ce nu pot fi
anticipate.Acest gen de situatii genereaza erori simple ce nu pot fi re-
zolvate in mod automat.Aceste erori vor intrerupe executia programului.
Erorile ce nu pot fi tratate automat,sunt fie erori de executie de tip
"runttime error" ce tin de o greseala de programare (ce nu a fost identi-
ficata de compilator),fie erori de executie generate de utilizator prin
operatii incorecte sau introducerea de date cu format incompatibil.Pro-
gramele ce contin astfel de erori iremediabile,poarta numele de programe
defective,"virusate",sau "buggs".Exista si o categorie de programatori
malitiosi,care cauta intentionat limitele de executie ale unui program
oarecare,tocmai cu scopul de a exploata aceste limite pentru a crea asa
numitii "virusi informatici".Acest gen de programatori poarta si numele
de "hack-eri".Hack-erii au si un rol pozitv: atrag atentia asupra limite-
lor sau viciilor de constructie si contribuie implicit la munca de dez-
voltare a platformelor software.Nu are rost sa incercati sa anticipati
toate exceptiile posibile,dar este bine sa tratati toate exceptiile ce
intervin frecvent in timpul executiei (apasarea unei taste gresite,apa-
sarea unui bloc de taste,click de mouse in zonele "inerte" etc.).Exista
un aforism prin care se postuleaza ca: oricat de ingenios si meticulos
este programatorul,"prostul" va gasi o modalitate prin care sa "sparga"
toate mijloacele de protectie a codului si sa "ocoleasca" toate proto-
coalele de securitate.Pentru situatiile exceptionale,puteti utiliza si
blocul "finally { }",ce se executa in cazul exceptiilor nescontate.
-41-
Pentru organizarea grupurilor de obiecte vizuale se pot utiliza diverse
obiecte de tip container.Pe langa fereastra JFrame,Swing ofera si urma-
toarele containere vizuale: Panel,Scroll Pane,Internal Frame,Tabbed Pane,
Tool Bar,Layered Pane,Split Pane si Desktop Pane iar pachetul AWT contine
containerele vizuale Panel,Canvas si Scroll Pane.In prima etapa,configu-
rati containerul de la baza.In mediul vizual,este suficient sa alegeti si
sa setati proprietatea potrivita iar in fila .java trebuie sa setati
fiecare proprietate printr-o linie de cod.
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
-42-
EXEMPLU:
import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.KeyEvent;
public class TabbedPaneDemo extends JPanel {
public TabbedPaneDemo() {
super(new GridLayout(1,1);
JTabbedPane tabbedPane = new JTabbedPane();
JComponent panel1 = makeTextPanel("Paleta 1 - inactiva");
tabbedPane.addTab("Tab 1",panel1);
JComponent panel2 = makeTextPanel("Paleta 2 ");
tabbedPane.addTab("Tab 2",panel2);
JComponent panel3 = maketextPanel("Paleta 3 ");
tabbedPane.addTab("Tab 3",panel3);
JComponent panel4 = makeTextPanel("Paleta 4");
panel4.setPreferredSize(new Dimension(400,250));
tabbedPane.addTab("Tab 4",panel4);
add(tabbedPane);
tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
}
protected JComponent makeTextPanel(String text) {
JPanel panel = new JPanel(false);
JLabel filler = new JLabel(text);
filler.setHorizontalAlignement(JLabel.CENTER);
panel.setLayout(new GridLayout(1,1));
panel.add(filler);
return panel;
}
public static void main(String[] args) {
JFrame frame = new JFrame("TabbedPaneDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TabbedPaneDemo(),BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
-salvati fila cu numele TabbedPaneDemo.java.Compilati si executati.
Daca utilizati o platforma vizuala,cum este NetBeans,nu trebuie decat
sa adaugati obiectele si apoi sa setati proprietatile.Fereastra de design
nu numai ca permite o evaluare cat mai exacta a aspectului final,dar
ofera si un set de indicatori (coordonate axiale) pentru alinierea si
dimensionarea obiectelor.In plus,pentru a putea previzualiza si functio-
nalitatea obiectelor,puteti utiliza butonul Preview Design.In continuare
puteti sa adaugati obiecte in fiecare optiune: butoane,casete de dialog,
campuri de text de tip Text Area,etc.Nu este insa bine sa exagerati cu
numarul de optiuni.Fiecare astfel de optiune introduce in program un
spatiu denumit suplimentar si implicit un set intreg de operatii pentru
reglementarea domeniului de vizibilitate.Prea multe obiecte de acest gen
pot incetini sau chiar bloca executia (suprascriu memoria de operare).
-43-
Uneori,obiectul contatiner poate influenta activ comportamentul sau
aspectul obiectelor continute.De exemplu,daca mai multe butoane de tip
radio se includ intr-un obiect de tip ButtonGroup,atunci daor unul dintre
butoane va putea fi selectat,iar restul de butoane vor fi deselectate
automat.ButtonGroup este special destinat pentru a reglementa statutul
selectat/deselectat al obiectelor continute.Se pot include in ButtonGroup
orice tip de obiecte derivate din AbstractButton,dar nu are rost sa uti-
lizati acest container decat pentru: JRadioButton,JRadioButtonMenuItem si
JToggleButton.
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class RadioButtonDemo extends JPanel implements ActionListener {
JLabel picture;
public RadioButtonDemo() {
super(new BorderLayout());
JRadioButton Buton1 = new JRadioButton("Buton1");
Buton1.setSelected(true);
Buton1.setActionCommand("Buton1");
JRadioButton Buton2 = new JRadioButton("Buton2");
ButtonGroup group = new Buttongroup();
group.add(Buton1);
group.add(Bouton2);
Buton1.addActionListener(this);
Buton2.addActionListener(this);
picture = new JLabel("text initial");
JPanel radioPanel = new JLabel(new GridLayout(0,1));
radioPanel.add(Buton1);
radioPanel.add(Buton2);
add(radioPanel,BorderLayout.LINE_START);
add(picture,BorderLayout.CENTER);
}
public void actionPerformed(ActionEvent e) {
picture.setText(e.getActionCommand() );
}
public static void main(String[] args) {
JFrame frame = new JFrame("RadioButtonDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent new ContentPane = new RadioButtonDemo();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
}
-salvati fila cu numele RadioButtonDemo.java.Compilati si executati.
Pentru ca bitoanele sa functioneze strict asincron,trebuie ca butoanele
sa fie incluse intr-un obiect de tip ButtonGroup,care sa fie inclus la
randul sau intr-un obiect de tip Panel,ce va fi inclus in containerul
de suport (in JFrame).
-44-
Daca analizati putin exercitiul anterior,puteti observa urmatoarea
ordine de operatii:
-intregul program este de fapt o clasa (RadioButtonDemo)
-clasa contine o functie main(),pe care o executa implicit
-functia main creaza fereastra de suport (new JFrame)
-se seteaza proprietatile containerului de suport
-se creaza un nou component de tip Pane utilizand functia RadioButtonDemo
-functia RadioButtonDemo() executa urmatoarele operatii:
-creaza un obiect JLabel
-creaza si seteaza doua obiecte JRadioButton
-creaza un obiect JButtonGroup
-include obiectele JRadioButton in JButtonGroup
-adauga cate un Listener la fiecare buton
-creaza un obiect de tip Panel
-include butoanele in panel
-defineste functia pentru tratarea evenimentului capturat (action)
-se seteaza proprietatile obiectului Panel
-se actualizeaza fereastra (pentru a crea toate obiectele si a actualiza
toate setarile)
-se afiseaza fereastra
Exercitiul contine obiecte structurate pe patru nivele diferite si
este putin confuz pentru incepatori.Pana cand va familiarizati cu acest
gen implementari,puteti incerca sa faceti scheme si diagrame,pe hartie,
inainte de a incepe codificarea instructiunilor.
Daca lucrati in mediul vizual,lucrurile se simplifica foarte mult:
EXEMPLU (pentru NetBeans):
-deschideti un proiect nou
-adaugati fereastra JFrame
-in fereastra de design adaugati un obiect de tip LayeredPane
-bifati pentru jLayeredPane1 proprietatea "opaque"
-alegeti o culoare de fond oarecare (cu proprietatea background)
-includeti in jLayeredPane1 doua obiecte RadioButton
-adaugati si un obiect de tip Label
-adaugati un obiect de tip ButtonGroup (Obiectul buttonGroup1 nu poate
fi vazut in fereastra de design,dar poate fi identificat in fereastra
Source,in blocul variabilelor declarate,sau in fereastra Navigator)
-pentru a include butoanele in ButtonGroup,selectati Frame,alegeti din
fereastra Properties optiunea Events,selectati windowOpen si adaugati
urmatoarea metoda Start:
private void Start(java.awt.event.WindowEvent evt){
buttonGroup1.add(jRadioButton1);
buttonGroup1.add(jRadioButton2);
}
-acum butoanele vor fi incluse automat in ButtonGroup in momentul in
care se deschide fereastra principala
-in continuare puteti adauga cate un eveniment actionPerformed pentru
fiecare dintre cele doua butoane:
private void jRadioButton1ActionPerformed(java.awt.event.ActionEvent evt){
jLabel1.setText("Butonul 1 a fost activat");
}
si respectiv...
-45-
private void jRadioButton2ActionPerformed(java.awt.event.ActionEvent evt){
jLabel1.setText("Butonul 2 a fost activat");
}
Din fereastra Design puteti previzualiza si apoi testati,sau executati cu
Run Main Project.
Esential este cum includeti obiectele in containere.Este indicat sa
utilizati unul dintre evenimentele automate de initializare a ferestrei.
Dupa ce identificati mecanismul de lucru,incercati sa includeti doua sau
mai multe astfel de grupuri de optiuni functionale.Pentru a personaliza
aspectul,puteti utiliza orice combinatie de fonturi si culori,etc.De
exemplu,incercati sa creati o aplicatie cu mai multe optiuni,prin care
sa puteti converti automat o valoare numerica introdusa de la tastatura,
in diverse monede de circulatie.
Exista situatii in care doriti sa faceti un schimb oarecare de infor-
matii intre program si utilizator,fara sa complicati interfata grafica cu
prea multe obiecte suplimentare.In aceste cazuri,cea mai simpla solutie
este sa adaugati o fereastra de dialog,adica o fereastra independenta in
care afisati sau preluati un anumit set de date simple.Cea mai simpla
fereastra de dialog,este fereastra de tip mesaj si se poate crea cu aju-
torul obiectului JOptionPane.
EXEMPLU:
import javax.swing.*;
public class Mesaj {
public static void main(String[] args) {
JOptionPane.showMessageDialog(null,"Mesajul dorit !"); }
}
-salvati fila fila cu numele Mesaj.java.Compilati si executati.
Obiectul JOptionPane este special conceput pentru urmatoarele tipuri
de ferestre de dialog:
1.Fereastra de confirmare,in care se asteapta un raspuns pozitiv la o anu-
mita intrebare.Se creaza cu: JOptionPane.showConfirmDialog().
2.Fereastra de input,in care utilizatorul trebuie sa introduca un anumit
tip de date.Se creaza cu: JOptionPane.showInputDialog().
3.Fereastra de tip mesaj,in care utilizatorul primeste un scurt mesaj in-
formativ.Se creaza cu: JOptionPane.showMessageDialog(). (vezi exemplul)
4.Fereastra de tip optiune,in care utilizatorul poate alege dintr-un set
de optiuni una singura.Se creaza cu: JoptionPane.showOptionDialog.
Toate aceste ferestre de dialog se creaza cu ajutorul unei ferestre
interne standard,de tip JInternalFrame.Optional se poate include si un
desen standard,sub forma de icon.Java include urmatoarele icons create
special pentru acest obiect: question,information,error,warning,custom.
Pentru a desemna tipul de mesaj si respectiv desenul afisat se pot utiliza
urmatoarele constante: JOptionPane.WARNING_MESSAGE,JOptionPane.ERROR_MESS-
AGE,JOptionPane.PLAIN_MESSAGE,JOptionPane.INFORMATION_MESSAGE si respectiv
JOptionPane.QUESTION_MESSAGE.
Asadar,obiectul JOptionPane are mai multi constructori.In functie de
constructorul apelat,se va crea o fereastra de dialog din tipul respectiv.
Daca doriti sa inlocuiti desenul standard cu un icon personalizat,trebuie
sa includeti numele icon-ului in locul constantei standard.
Pentru a nu complica prea mult programul,si pentru a fi cat mai usor
de depanat,ferestrele de dialog e bine sa fie cat mai simple.Este mai bine
-46-
sa utilizati mai multe ferestre de dialog succesive,decat sa incercati sa
aglomerati prea multe date intr-o singura fereastra.
EXEMPLU (recapitulativ):
import javax.swing.*;
public class Mesaj2 {
public static void main(String[] args){
JOptionPane.showMessageDialog(null,"Atentie!","Alerta",
JOptionPane.ERROR_MESSAGE);
JOptionPane.showConfirmDialog(null,
"Alegeti Yes sau No","Selectie",JOptionPane.YES_NO_OPTION);
Object[] optiuni = { "OK","CANCEL" };
JOptionPane.showOptionDialog(null,"Confirmati cu OK","Warning",
JOptionPane.DEFAULT_OPTION,JOptionPane.WARNING_MESSAGE,
null,optiuni,optiuni[0]);
String valoare1 = JOptionPane.showInputDialog("Introduceti o valoare:");
Object[] valori = {"Primul","Al doilea","Al treilea"};
Object selectedValue = JOptionPane.showInputDialog(null,
"Selectati optiunea:","Selectie",JOptionPane.INFORMATION_MESSAGE,
null,valori,valori[0];
}
}
-salvati fila cu numele Mesaj2.java.Compilati si executati.
Este bine sa faceti cat mai multe exercitii cu fiecare tip de fereastra
de dialog si apoi sa pastrati cate un sablon,pentru cele mai utile dintre
ele.Apoi,ori de cate ori este necesar,copiati sablonul si efectuati doar
modificarile necesare.
Daca lucrati intr-un mediu vizual,obiectul JOptionPane nu este vizual,
asa ca nu poate fi vizualizat in fereastra de Design,nu se pot atribui
evenimente (decat prin linii de cod scrise manual) si nu se poate utiliza
nici fereastra Properties.Pentru a utiliza acest obiect,este bine sa aveti
sabloane preformate,pe care le conectati la unul dintre obiectele vizuale.
EXEMPLU: (NetBeans)
-deschideti un proiect nou
-adaugati JFrame
-deschideti fereastra Design
-adaugati un buton
-selectati pentru buton evenimentul Action -> action Performed si adaugati
o metoda de genul:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt){
javax.swing.JOptionPane.showMessageDialog(this,"Mesajul dorit!");
}
-testati si executati cu Run Main project.
Ferestrele de dialog prezinta un avantaj major.Se creaza si se elibe-
reaza automat din memorie,imediat dupa executie.Astfel,memoria de operare
nu va mai fi incarcata cu un pointer inutil.Se utilizeaza foarte frecvent
pentru a semanliza eventualele erori de executie.Puteti utiliza aceasta
solutie pentru oricare dintre valorile capturate cu catch,pentru a semna-
liza daca o operatie a fost executata corect,sau eronat.Puteti utiliza
mesaje si pentru a crea un mic program "Help" interactiv,pentru a comu-
nica sau cere informatii suplimentare,sau pentru a descrie unele dintre
obiecte,sau o anumita functionalitate a programului,etc.
-47-
Atunci cand doriti sa includeti in program un numar mare de optiuni,cea
mai comoda solutie o ofera meniurile.Acelasi rezultat se poate obtine si
cu un grup de butoane radio,spinner,sau casete Listox si ComboBox,dar
meniurile ocupa mult mai putin spatiu,au un cod compact,usor de editat,
se executa mai usor si exclud orice fel de erori de executie.Spre deose-
bire de celelalte componente,meniurile nu se includ in interfata grafica,
ci sunt obiecte separate.Clasic se pot utiliza doua tipuri de meniuri:
bara de meniuri si meniul popup.O bara de meniu contine unul sau mai
multe meniuri,ce pot contine la rindul lor submeniuri.Pentru un meniu
complet se utilizeaza trei tipuri de obiecte Swing: JMenuBar,JMenu si
respectiv JMenuItem.Ca rezultat,datele sunt compartimentate modular si nu
exista riscul ca un anumit tip de data sa poata bloca intregul meniu.In
cazul in care intervine o eroare intr-un submeniu,restul meniului va fi
functional,astfel ca utilizatorul va putea avea o solutie de iesire din
impas.Meniurile se declara,se creaza,apoi se conecteaza la evenimente:
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MenuLayoutDemo {
public JMenuBar createMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenuBar.setLayout(new BoxLayout(menuBar,BoxLayout.X_AXIS));
menuBar.add(createMenu("Meniu1");
menuBar.setBorder(BorderFactory.createMatteBorder(3,3,3,5,Color.RED));
return menuBar;
}
public JMenu createMenu(String title) {
JMenu m = new Jmenu(title);
m.add("Optiunea 1 din " + title);
m.add("Optiunea 2 din " + title);
m.add("Optiunea 3 din " + title);
JMenu submenu = new JMenu("Submenu");
submenu.add("Submeniu optiunea 1");
submenu.add("Submeniu optiunea 2");
m.add(submenu);
return m;
}
public satic void main(String[] args){
JFrame frame = new JFrame("Exemplu de meniu simplu");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MenuLayoutDemo demo = new MenuLayoutDemo();
Container contentPane = frame.getContentPane();
contentPane.setBackground(Color.WHITE);
contentPane.add(demo.createMenuBar(),BorderLayout.LINE_START);
frame.setSize(300,80);
frame.setVisible(true);
}
}
-salvati fila cu numele MenuLayoutDemo.java.Compilati si executati.
Exemplul contine o bara de meniu,un meniu,trei optiuni si un submeniu.
-48-
Meniurile sunt laborios de editat manual.Pentru a verifica fiecare
meniu si apoi pentru a conecta cate un eveniment la fiecare optiune din
meniu,sunt necesare o serie intreaga de operatii.Este mult mai usor,daca
utilizati un editor vizual.
EXEMPLU: (NetBeans)
-deschideti un proiect nou
-adaugati JFrame
-deschideti fereastra de design
-adaugati un obiect MenuBar
-adaugati un obiect Label
-pentru a adauga meniuri,click drept pe MenuBar si alegeti Add Menu
-pentru a adauga optiuni,click drept pe Meniu si alegeti Add From Palette
si apoi MenuItem ( sau MenuItem/RadioButton)
-pentru a adauga evenimente,click drept pe meniu,alegeti Events si apoi
evenimentul dorit
-editati metoda pentru tratarea evenimentului
JLabel1.setText("A fost activat meniul 1 ");
-testati si executati cu Run Main Project
Editorul vizual este extrem de usor de utilizat si foarte eficient.Nu
numai ca simplifica foarte mult munca de editare,dar permite sa visuali-
zati in permanenta aspectul interfetei.Alegeti culorile si fonturile
preferate,setati dimensiunile si incadrati fiecare obiect,adaugati bor-
duri,chenare sau linii de subliniere,short-cut-uri etc.
In cazul in care nu aveti un editor vizual,este bine sa lucreti cu
sabloane.Construiti un model simplu de meniu,la care conectati cate un
eveniment simplu si o metoda de tratare a evenimentului.Apoi,ori de cate
ori este necesar,nu va trebui decat sa schimbati numele optiunilor si sa
editati metodele de tratare a evenimentului.Eventual,puteti gasi pe In-
ternet seturi intregi de sabloane,pentru majoritatea tipurilor de apli-
catii posibile.Nu trebuie decat sa alegeti sablonul potrivit si apoi
sa implementati metodele dorite.Daca nu aveti preferinte gata formate,
puteti sa pastrati aspectul general si combinatia de culori din meniul
standard.Daca schimbati grafica,este bine sa existe un contrast cat mai
bun,dar fara sa utilizati culori stralucitoare (obosesc privirea).
Meniurile nu trebuie sa fie colorate ca un papagal.Rostul lor este sa
controleze cat mai simplu o anumita actiune.Meniurile cu derulare econo-
misesc foarte mult din spatiul rezervat pentru interfata grafica.Pentru
a obtine acelasi efect cu alte tipuri de obiecte,trebuie sa creati o
functie speciala prin care obiectele sa fie afisate doar atunci cand sunt
necesare,sau respectiv sa proiectati o fereastra externa destinata special
acestui scop.
Meniurile ofera si un alt avantaj major,prin faptul ca pot grupa seturi
de operatii secventiale,sau seturi de operatii asemenatoare,astfel incat
utilizatorul sa vada dintr-o singura privire intregul set de operatii.
Fiecare meniu si submeniu este un obiect separat.In cazul in care,
dintr-un motiv sau altul apar erori de executie la nivelul unui anumit
meniu,cea mai simpla metoda este sa inlocuiti obiectul cu totul.Pentru
programele complexe,este bine sa arhivati cate o copie de siguranta a
fiecarui obiect,astfel incat sa fie cat mai simplu sa depanati programul
modular (inlocuiti doar obiectul defect cu clona sa functionala).Este
bine sa nu exagerati totusi,cu numarul de optiuni dintr-un meniu.
-49-
Dupa ce a-ti setat containerul de baza si eventual a-ti adaugat un me-
niu de optiuni,puteti introduce in interfata obiectele vizuale dorite.Pe
langa etichete(Label),butoane si campuri de date (prezentate deja in
exemplele anterioare),un obiect vizual extrem de util il reprezinta JList.
JList este creat special pentru a putea prezenta grupuri de date,sub
forma de lista fixa,sau derulabila.Utilizatorul poate sa aleaga unul sau
mai multe elemente din lista,cu care sa execute apoi o serie oarecare de
operatii.Cea mai simpla lista se creaza cu ajutorul unei arii de siruri:
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class Lista extends JPanel {
public JList list;
String[] sir1 = {"Optiunea 1","Optiunea 2","Optiunea 3","Optiunea 4"};
public JList list;
list = new JList(sir1);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setSelectedIndex(0);
list.setVisibleRowCount(2);
JScrollPane s1 = new JScrollPane(list);
add(s1,BorderLayout.CENTER); };
public static void main(String[] args){
JFrame frame = new JFrame("Lista de optiuni");
frame.setDefaultCloseOperation(Jframe.EXIT_ON_CLOSE);
JComponent newContentPane = new Lista();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
}
-salvati fila cu numele Lista.java.Compilati si executati.
Puteti observa in exercitiu urmatoarele:
-pentru a crea lista am declarat si definit functia constructor Lista()
-elementele din lista se creaza cu ajutorul ariei de siruri
-lista a fost inclusa intr-un panou de derulare (ScrollPane)
In rest,depinde de modul in care doriti sa utilizati elementele din
lista.Daca doriti sa afisati permanent toate elementele,nu mai este nece-
sar sa adaugati un scroller (panou de derulare),dar lista va ocupa o mare
parte din interfata grafica cu elemente inerte.
Obiectul are un numar mare de metode si proprietati ce permit personali-
zarea aspectului sau a stilului de executie,si respectiv garnitura normala
de evenimente.Pentru a programa operatii,trebuie sa conectati evenimentul
ales la metoda dorita.Editarea sub forma de linii de cod este destul de
laborioasa,asa ca este preferabil sa utilizati un editor vizual.Acest
obiect prezinta un foarte mare avantaj: -optiunile prezentate in lista au
un format fix,ce exclude orice eroare de introducere a datelor.Aplicatia
nu va avea nevoie de nici un fel de filtru I/O,sau de functii pentru
tratarea erorilor de format.
-50-
Daca doriti sa lucrati modular,sau daca aveti deja un sablon anume
pentru lista dorita,obiectul poate fi creat si cu ajutorul unui model
preformat,sau utilizand un sablon:
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class Lista1 extends JPanel {
private JList list;
private DefaultListModel model1;
public Lista1() {
model1 = new DefaultListModel();
model1.addElement("Primul element din lista");
model1.addElement("Al doilea element din lista");
model1.addElement("Al treilea element din Lista");
list = new JList(model1)
list.setSelectionMode(ListselectionModel.SINGLE_SELECTION);
list.setSelectedIndex(0);
list.setVisibleRowCount(3);
add(list,BorderLayout.CENTER);
};
public static void main(String[] args) {
JFrame frame = new JFrame("Lista de optiuni");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new Lista1();
newContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
}
-salvati fila cu numele Lista1.java.Compilati si executati.
Acest gen de solutie ofera urmatoarele avantaje:
-se pot crea mai multe liste cu ajutorul unui singur sablon
-sablonul poate fi configurat automat,pentru a genera liste diferite in
functie de un criteriu oarecare
-sablonul poate fi arhivat intr-o fila externa si utilizat in multiple
aplicatii
Pentru exercitiu,incercati sa creati o aplicatie in care lista se va
construi diferit,in functie de o parola oarecare introdusa de utilizator
inainte de initializarea interfetei grafice.
In mediu vizual,JLabel este mult mai usor de exploatat:
EXEMPLU: (NetBeans)
-deschideti un proiect nou si adaugati JFrame,deschideti Designer
-adaugati un obiect List si un obiect Label
-selectati jList1 si setati proprietatea model,apoi setati proprietatea
selectionMode = SINGLE_INTERVAL
-alegeti evenimentul ListSelection -> valueChanged si editati metoda:
jLabel1.setText("Selectie = " + jList1.getSelectedItem() );
-testati si executati exemplul cu Run Main Project
-incercati sa dezvoltati exemplul cu cateva metode suplimentare
-51-
Un obiect asemanator cu JList este JComboBox.Acest obiect este de fapt
o combinatie intre jList si un buton cu un camp de editare.Utilizatorul
poate alege o valoare predefinita din lista de derulare,sau poate edita
o valoare noua.Obiectul nu ridica probleme deosebite si nu necesita o
prezentare suplimentara.
Java contine insa si un obiect vizual mai particular,ce permite prezen-
tarea unor grupuri de siruri sau obiecte,sub forma de tabel bidimensional.
Nu este o grila pentru exploatarea bazelor de date,ci este un tabel simplu
creat cu date locale (sau preluate dintr-o fila sursa).Acest obiect nu se
intilneste in alte medii de programare,dar este foarte asemanator cu o
grila simpla.Obiectul poarta numele de JTable si are 7 constructori(adica
7 metode diferite de a crea un tabel).Cei sapte constructori recunosc
parametri diferiti.Astfel,cel mai simplu creaza un tabel pe baza unui
model standard implicit,iar cel mai complex creaza tabelul pe baza unui
set de obiecte de tip sablon.De exemplu,pentru a prezenta un set de siruri
de caractere sub forma de tabel,se va apela constuctorul ce are ca para-
metrii o arie bidimensionala de siruri si o arie simpla de siruri:
JTable(Object [] [] rowData,Object [] columnNames);
EXEMPLU:
import javax.swing.*;
import java.awt.Dimension;
import java.awt.GridLayout;
public class Tabel1 extends JPanel {
public Tabel1() {
super(new GridLayout(1,0));
String[] coloane={"Nume","Prenume","Adresa","Vechime(ani)","Confirmare");
Object [][] date = {
{"Barbu","Ion","Republicii nr.7",new Integer(5),new Boolean(false)},
{"Albu","Mihai","Primaverii nr.5",new Integer(3),new Boolean(true)},
{"Marin","Petre",B-dul Muncii nr.3",new Integer(2),new Boolean(true)},
{"Pop","Vasile","Piata Pacii nr.10",new Integer(7),new Boolean(false)},
};
final JTable table = new JTable(date,coloane);
table.setPrefferedScrollableViewportSize(new Dimension(500,100));
table.setFillsViewportHeight(true);
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Tabel simplu");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Tabel1 new ContentPane = new Tabel1();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
}
-salvati fila cu numele Tabel1.java.Compilati si executati fila.
Tableul contine: o arie de siruri pentru definitia titlurilor si o arie
bidimensionala de siruri in care sunt incluse datele propriu zise.
-52-
Atunci cand structura unui tabel urmeaza sa fie utilizata de mai multe
ori,fie in aceeasi aplicatie,fie in aplicatii diferite,este comod sa con-
struiti tabelul cu ajutorul unui sablon (model preformatat).Construiti
sablonul,apoi se pot utiliza rutine automate,pentru a crea seturi intregi
de obiecte,sau serii de obiecte ce mostenesc acelasi ancestor.
EXEMPLU:
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import java.awt.Dimension;
import java.awt.GridLayout;
public class Tabel2 extends JPanel {
public Tabel2() {
super(new GridLayout(1,0));
class TabelulModel extends AbstractTableModel {
String[] coloane={"Nume","Prenume","Adresa","Vechime(ani)","Confirmare");
Object [][] date = {
{"Barbu","Ion","Republicii nr.7",new Integer(5),new Boolean(false)},
{"Albu","Mihai","Primaverii nr.5",new Integer(3),new Boolean(true)},
{"Marin","Petre",B-dul Muncii nr.3",new Integer(2),new Boolean(true)},
{"Pop","Vasile","Piata Pacii nr.10",new Integer(7),new Boolean(false)},
};
public int getColumnCount() { return coloane.length; }
public int getRowCount() { return date.length; }
public String getColumnName(int col) { return coloane[col]; }
public Object getValueAt(int row,int col){ return date[row][col]; }
}
JTable table = new JTable(new TabelulModel());
table.setPrefferedScrollableViewportSize(new Dimension(500,100));
table.setFillsViewportHeight(true);
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Tabel simplu");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Tabel2 new ContentPane = new Tabel2();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
}
-salvati fila cu numele Tabel2.java.Compilati si executati.
Exercitiul este similar cu cel precedent,dar in locul celor doua obiecte
de tip arie de siruri,constructorul utilizeaza un tabel sablon creat cu
ajutorul clasei TabelulModel.Acest gen de solutie prezinta urmatorul avan-
taj: modelul poate contine si setarile dorite.Ori de cate ori doriti sa
clonati tabelul respectiv,acesta va fi gata setat (culori,fonturi,borduri,
stil de prezentare etc.).Exista si un constructor ce utilizeaza vectori
(adica arii de date ce pot creste progresiv).Acest constructor se va apela
atunci cand doriti sa adaugati periodic inregistrari noi.
-53-
Daca utilizati un editor vizual,etapa de design este mult mai simpla.
EXEMPLU:
-deschideti un proiect nou si adaugati JFrame.
-in fereastra Design,adaugati un obiect Table (din grupul Beans)
-selectati obiectul jTable1 si utilizati fereastra properties
-pentru a specifica sablonul tabelului utilizati proprietatea "model"
-alegeti fonturile,culorile,bordurile preferate
-adaugati si un obiect Label
-pentru a testa un eveniment oarecare,selectati din nou obiectul jTable1
-click drept pe tabel,alegeti Events -> Mouse si mouseClicked
-editati o metoda de genul:
private void jTableMouseClicked(java.awt.event.MouseEvent evt){
int x = 0 ;
int y = 0 ;
x = jTable1.getSelectedColumn() + 1;
y = jTable1.getSelectedRow() + 1;
jLabel1.setText("Selectie = coloana: " + x + " randul: " + y);
}
-testati si executati exemplul
Obiectul JTable are un set foarte bogat de metode,la care se pot adauga
si metode personalizate incluse in clasa utilizata ca sablon.Modul in care
combinati aceste metode pentru a obtine o anumita solutie,depinde atat de
necesitatile de moment,cat si de imaginatia d-voasta.Daca nu reusiti sa
obtineti o combinatie lucrativa,este bine sa consultati si oferta de pe
Internet.Exista o paleta foarte variata de sabloane si modele preformate
ce acopera aproape toate necesitatile uzuale de programare.Nu trebuie
decat sa descarcati un astfel de sablon si apoi sa-l transformati pentru
a corespunde cu cerintele aplicatiei curente.
O alta facilitate simpla si practica,oferita de Java este posibilitatea
de a formata sirurile de caractere cu ajutorul limbajului HTML.Majoritatea
componentelor Swing,utilizeaza si fragmente de text ca parte componenta a
inerfetei grafice.Pentru formatarea acestui text se pot utiliza proprie-
tatile obiectului:
EXEMPLU: label = new JLabel("textul dorit");
label.setFont(new Font("Serif",Font.PLAIN,14));
label.setForeground(new Color(0xffffdd));
sau se poate utiliza limbajul HTML:
EXEMPLU:
import javax.swing.*;
public static void main(String[] args) {
JFrame frame = new jFrame("Text redactat in limbaj HTML");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new Jlabel
("<html><h1><font color=red> Text HTML </font></h1></html>");
frame.getContentPane().add(label);
frame.pack()
frame.setVisible(true);
}
}
-salvati fila cu numele TextHTML.java.Compilati si executati
Experimentati mai multe obiecte si formate de tip HTML.
-54-
Daca lucrati in mediu vizual,designul si formatul sunt mult mai usor
de ales.
EXEMPLU:
-deschideti un nou proiect si adaugati JFrame
-deschideti fereastra de Design
-adaugati doua butoane si un camp Label
-editati pentru primul buton evenimentul Action -> actionPerformed:
jLabel1.setText(
"<html><h1><font color=red> Text HTML </font></h1></html>");
-editati pentru cel de al doilea buton Action -> action{erformed:
jLabel1.setText("Text normal");
-testati si executati,apoi apasati alternativ cele doua butoane.
Acest tip de solutie,nu numai ca simplifica alegerea formatului,dar
permite si sa aveti niste sabloane prestabilite pentru format,editate in
limbaj HTML si importate dintr-o fila sursa.Daca doriti ca un numar oare-
care de file si obiecte sa respecte un format particular de text,este
suficient sa editati sablonul o singura data si apoi sa-l importati sub
forma de sir de caractere,pentru toate obiectele dorite.
Tot in paleta Swing se gaseste si controlul Tree,creat cu ajutorul
clasei JTree,pentru a putea organiza si prezenta datele ierarhic,sub forma
de arbore binar.Obiectul de tip Tree nu va contine datele propriu zise,ci
doar va prezenta ierarhia lor.Pentru a putea avea acces la date cu ajuto-
rul acestui obiect,va trebui sa creati o conexiune intre fiecare element
din arbore si fila sursa.Conexiunea se va activa automat in momentul in
care se va identifica evenimentul ales pentru crearea link-ului.
In obiectul de tip jTree,datele se vor prezenta pe verticala,incepand
cu elementul radacina.Fiecare rand din diagrama va reprezenta un nod.
Pentru fiecare nod se va utiliza un obiect de tip TreeNode ce se poate
construi relativ simplu cu ajutorul unui obiect de tip DefaultMutable-
TreeNode.
Se observa de la prima vedere,ca este vorba de fapt despre un grup de
obiecte,organizate modular.Fiecare data,este reprezentata cu ajutorul
unui obiect distinct.Ca rezultat,operatiile de sortare,cautare sau navi-
gare in cadrul grupului sunt mult mai usor de realizat (se utilizeaza
pointerii spre fiecare adresa).
Pentru economie de spatiu,fiecare nod poate fi expandat sau plisat
(collapsed) dupa necesitati.Atunci cand este expandat,afiseaza toti des-
cendentii,iar cand este plisat (colabat),afiseaza doar directorul parinte.
Toate aceste operatii,se pot executa si automat,prin functii specifice:
EXEMPLE: isRootVisible(),setRootVisible(),getVisibleRowCount() etc...
Fiecare nod din arbore poate fi identificat fie cu ajutorul unui obiect
special destinat de tip TreePath,fie prin textul afisat in dreptul sau (
sau o pictograma de tip icon).
Pentru a identifica si exploata evenimentele de la nivelul obiectului,
este necesar sa conectati cate o functie de tip Event Listenr pentru
fiecare dintre evenimentele urmarite,de genul:
if (node.isLeaf()) { functia pentru afisarea datelor }
else { functia alternativa }
Obiectele de tip Tree sunt greu de editat fara ajutorul unei interfete
vizuale,deoarece contin numeroase obiecte si functii de tip constructor,
sau metode pentru setarea obiectelor.
-55-
EXEMPLU:
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import java.awt.*;
public class Arbore extends JPanel {
public Arbore() {
DemoArea demoArea;
demoArea = new DemoArea();
add(demoArea); }
public class DemoArea extends JScrollPane {
Dimension minSize = new Dimension(100, 100);
JTree tree;
public DemoArea() {
TreeNode rootNode = createNodes();
tree = new JTree(rootNode);
setViewportView(tree); }
private TreeNode createNodes() {
DefaultMutableTreeNode root;
DefaultMutableTreeNode grandparent;
DefaultMutableTreeNode parent;
DefaultMutableTreeNode child;
root = new DefaultMutableTreeNode("Directorul radacina ");
grandparent = new DefaultMutableTreeNode("Primul nod");
root.add(grandparent);
parent = new DefaultMutableTreeNode("A doua ramificatie (parinte
1)");
grandparent.add(parent);
child = new DefaultMutableTreeNode("Primul descendent");
parent.add(child);
child = new DefaultMutableTreeNode("Al doilea descendent");
parent.add(child);
parent = new DefaultMutableTreeNode("A doua ramificatie (parinte
2)");
grandparent.add(parent);
child = new DefaultMutableTreeNode("Primul urmas");
parent.add(child);
child = new DefaultMutableTreeNode("Al doilea urmas");
parent.add(child);
return root;
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Arbore de directoare");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new Arbore();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
}
-salvati fila cu numele Arbore.java.Compilati si executati.
-56-
Daca analizati putin exemplul,observati ca nu este chiar atat de greu
cum pare la prima vedere.Obiectul Tree se formeaza din unul sau mai multe
obiecte de tip TreeNode,ce se creaza utilizand ca sablon un obiect de tip
DefaultMutableTreeNode.Se definesc constructorii,apoi se grupeaza obiecte-
le cu ajutorul metodei add().Totusi,editarea manuala este foarte laborioa-
sa in cazul arborilor cu structura complexa.Daca utilizati un editor vi-
zual,munca de programare se simplifica foarte mult:
EXEMPLU: (NetBeans)
-deschideti un proiect nou si adaugati JFrame
-deschideti fereastra de Design
-adaugati un obiect de tip Tree si un obiect de tip Label
-obiectul jTree1 va contine un model implicit format din trei directoare
si cateva subdirectoare.Pentru a schimba continutul arborelui,selectati
obiectul jTree1,deschideti fereastra Properties si alegeti "model".Se va
deschide fereastra jTree1 - model in care elementele sunt prezentate in
doua casete de dialog.Stergeti elementele din prima caseta si introduceti
datele dorite.In cea de a doua caseta se va afisa aspectul arborelui con-
struit.Pentru a crea subdirectoare,trebuie sa lasati spatii goale,cores-
punzator cu pozitia din arbore: fara nici un spatiu va fi director rada-
cina,cu un spatiu gol va fi subdirector...etc.Cand arborele are aspectul
dorit,confirmati cu OK.
-inchideti fereastra Properties
-selectati din nou obiectul jTree1,click drept de mouse si alegeti din
Events -> TreeSelection -> valueChanged si editati urmatoarea metoda:
private void jTree1ValueChanged(javax.swing.event.TreeSelectionEvent evt){
javax.swing.tree.DefaultMutableTreeNode node =
(javax.swing.tree.DefaultMutableTreeNode)
jTree1.getLastSelectedPathComponent();
Object nodeInfo = node.getUserObject();
jLabel1.setText(nodeInfo.toString());
}
-testati si executati exercitiul cu Run Main Project
Intr-o aplicatie reala,textul returnat in jLabel1 poate fi numele unei
file ce urmeaza sa fie deschisa cu ajutorul unui alt obiect,sau respectiv
o comanda executabila,sau un link etc.
Observati ca si in acest caz,fiecare element din arbore este tot un
obiect de tip TreeNode,construit cu un sablon de tip DefaultMutableTree-
Node.Pentru a putea lucra cu elementele din arbore,acestea trebuie sa fie
referite prin metodele obiectelor de rigoare.In exemplul de mai sus,pentru
a putea identifica nodul selectat de utilizator,am creat un obiect de tip
DefaultMutableTreeNode in care am preluat valoarea curenta a nodului se-
lectat.
Structura arborelui poate fi modificata si interactiv in timpul exe-
cutiei,fie cu ajutorul unui algoritm prestabilit,fie cu ajutorul unor
operatii de tip drag and drop.Acest gen de solutii,depasesc nivelul ele-
mentar la manualului,dar puteti sa incercati sa dezvoltati exercitii de
acest gen.In plus,arborele poate contine pictograme si imagini,sau o
combinatie de text si pictograme.
Pentru orice aplicatie,trebuie sa aveti in vedere clasele necesare
pentru constructia obiectului: JTree si DefaultMutableTreeNode si res-
pectiv interfata TreeNode (analizati proprietatile si metodele lor).
-57-
Paleta de componente Swing este mult mai bogata,dar nu este necesara
prezentarea fiecarui obiect.Pentru a putea evalua utilitatea fiecarui
astfel de obiect,este necesar putin studiu individual.Creati exemple
simple,asemanatoare cu cele precedente si apoi explorati metodele si pro-
prieatatile fiecarui obiect.La nevoie,studiati si exemplele oferite in
diversele tutoriale din reteaua Internet.
EXECUTIA CONCURENTIALA
-58-
de executie).Impartirea unui proces in segmente,nu se poate face oricum.
Pentru a nu crea situatii incompatibile,segmentele trebuie sa contina
blocuri executabile,pe cat posibil independente de restul segmentelor din
proces (aplicatie).Cu alte cuvinte,pentru ca un program complex sa poata
fi executat in regim de multiprocesare seriala,trebuie sa poata fi sub-
impartit in mai multe module executabile.Aceste segmente executabile
poarta numele de fire de executie (thread-uri) si se reglementeaza in
limbaj Java cu ajutorul unui obiect special destinat,cu numele de Thread.
Astfel,programul va fi impertit in mai multe module.Fiecare modul va fi
inclus intr-un obiect de tip Thread.Fiecare obiect de tip Thread contine
o interfata de tip Runnable si prin urmare poate avea o executie indepen-
denta de restul modulelor.
Obiectul Thread are opt constructori diferiti,adica exista opt solutii
diferite de a crea un fir de executie.Cea mai simpla solutie este sa cre-
ati un obiect de tip Thread,in care sa definiti metoda Run().In acest
caz,firul de executie va utiliza propria metoda Run() pentru executie,dar
apelul nu se va face prin metoda Run(),ci se va apela o metoda special
destinata,numita start().
EXEMPLU:
public class FirExecutabil extends Thread {
public void run() {
System.out.println("Mesajul returnat !");
}
public static void main(String[] args) {
(new FirExecutabil()).start();
}
}
-salvati fila cu numele FirExecutabil.java.Compilati si executati.
Clasa Thread este importata implicit o data cu pachetul lang.Obser-
vati ca in metoda main(),obiectul se creaza prin apelul recursiv al
constructorului (clasa isi autoapeleaza constructorul).
Daca doriti ca firul executabil creat sa contina metoda Run() definita
in alt obiect (adica sa importe pentru executie datele dintr-o alta clasa)
trebuie sa utilizati al doilea tip de constructor,in care obiectul se
creaza pe baza unei interfete de tip Runnable.
EXEMPLU:
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Hello from a thread !");
}
public static void main(String[] args) {
(new Thread(new HelloRunnable())).start();
}
}
-salvati fila cu numele HelloRunnable.java.Compilati si executati.
Cele doua metode sunt practic identice,dar utilizeaza constructori dife-
riti.Cea de a doua metoda se utilizeaza atunci cand exista deja o inter-
fata oarecare,creata cu ajutorul unei interfete de tip Runnable,pe care
doriti sa o includeti intr-un thread (pentru a putea fi executata apoi
concurential).Ceilati 6 constructori,permit denumirea firului de executie
si respectiv formarea de grupuri de astfel de fire executabile.
-59-
Imediat dupa crearea unui obiect de tip Thread,se pot seta proprieta-
tile,sau se pot apela metodele acestui obiect.De exemplu,pentru a deter-
mina o intirziere de executie a unui fir executabil,se poate apela metoda
sleep().
EXEMPLU:
public class FirExecutabil2 extends Thread {
public void run() {
System.out.println("Mesajul returnat !");
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
try {
(new FirExecutabil2()).sleep(2000);
} catch (InterruptedException e) {
return;
}
(new FirExecutabil2()).start();
}}}
-salvati fila cu numele FirExecutabil2.java.Compilati si executati.
Atunci cand un proces (o aplicatie) contine mai multe fire executabile,
procesorul va alege si executa aceste fire,in functie de o serie intreaga
de criterii.Daca nu evaluati corect aceste criterii,executia va parea
uneori absolut paradoxala.
EXEMPLU:
public class FirExecutabil3 extends Thread {
public void run() {
System.out.println("Mesajul returnat !");
}
public static void main(String[] args) {
FirExecutabil3 fir_1 = new FirExecutabil3();
fir_1.setName("Firul1");
FirExecutabil3 fir_2 = new FirExecutabil3();
fir_2.setName("Firul2");
try {
System.out.println(fir_1.getName());
fir_1.sleep(2000);
fir_1.start();
System.out.println(fir_2.getName());
fir_2.sleep(5000);
fir_2.start();
} catch (InterruptedException e) { return ; }
}
}
-salvati fila cu numele FirExecutabil3.java.Compilati si executati.
Pentru a putea reglementa ordinea de executie,fiecare Thread are si o
anumita prioritate de executie.Aceasta prioritate se poate seta prin
metoda setPriority si accepta valori de tip int cuprinse intre 1 si 10.
Valoarea implicita este 5 si este reglementata prin constanta NORM_PRIO-
RITY,minima este MIN_PRIORITY = 1 iar maxima este MAX_PRIORITY = 10.In
exemplul de mai sus,ambele fire de executie au acceasi prioritate(normala)
asa ca vor fi executate in ordinea aparitiei in program).
-60-
Daca prioritatile nu sunt egale,se va executa preferential cel cu
prioritatea mai mare.
EXEMPLU:
public class FirExecutabil4 extends Thread {
public void run() {
System.out.println("Firul curent este: " + Thread.currentThread());
System.out.println("Mesajul returnat !");
}
public static void main(String args[]){
FirExecutabil4 fir_1 = new FirExecutabil4();
fir_1.setName("Firul1");
FirExecutabil4 fir_2 = new FirExecutabil4();
fir_2.setName("Firul2");
try {
fir_1.setPriority(2);
fir_2.setPriority(7);
fir_1.sleep(5500);
fir_2.sleep(1500);
fir_2.start();
fir_1.start();
-61-
EXEMPLU:
public class FirExecutabil5 extends Thread {
public void run() {
System.out.println("Mesajul returnat !");
try {
System.out.println("Metoda RUN-firul curent este: "
+ thread.currentThread());
while (true) {
try { Thread.sleep(1500);
} catch (InterruptedException x) { }
System.out.println("Firul DAEMON este activat din nou !");
System.out.println("Firul actual= " +Thread.currentThread());
}
} finally { System.out.println("Iesire din metoda RUN"); }
}
public static void main(String[] args) {
FirExecutabil5 fir_1 = new FirExecutabil5();
fir_1.setName("Firul1");
fir_1.setPriority(7);
FirExecutabil5 fir_2 = new FirExecutabil5();
fir_2.setName("Firul2");
fir_2.setPriority(1);
FirExecutabil5 fir_3 = new FirExecutabil5();
fir_3.setName("Firul3");
fir_3.setPriority(9);
fir_1.setDaemon(true);
fir_2.setDaemon(true);
fir_3.setDaemon(true);
fir_3.start();
fir_2.start();
fir_1.start()
System.out.println(" Status Firul1= " + fir_1.getState());
System.out.println(" Status Firul2= " + fir_2.getState());
System.out.println(" Status Firul3= " + fir_3.getState());
try { Thread.sleep(3000);
} catch (InterruptedException x) { } ;
System.out.println(" Status Firul1= " + fir_1.getState());
System.out.println(" Status Firul2= " + fir_2.getState());
System.out.println(" Status Firul3= " + fir_3.getState());
}
}
-salvati fila cu numele FirExecutabil5.java.Compilati si executati.
Observati cu atentie exercitiul.Ori de cate ori doriti sa depanati,
sau sa organizati ordinea de executie a thread-urilor este bine sa cititi
statusul pentru fiecare fir,in momentul respectiv.
ATENTIE ! -sirurile daemonice creaza foarte frecvent bucle de executie
infinite,fie direct,fie prin combinarea unui grup oarecare de circumstante
sau bucle de control.Ca rezultat,trebuie sa verificati cu maximum de aten-
tie toate variantele posibile de executie,inclusiv cele improbabile sau
cele care genereaza exceptii.Buclele infinite,blocheaza executia si sunt
extrem de stinjenitoare pentru "creditul" unui programator.
-62-
Atunci cand exista mai multe fire executabile,unul dintre ele ofera
suport pentru executia celorlalte.Firele executabile pot executa operatii
in background,sau pot crea interfete vizuale.De exemplu,o interfata vizu-
ala poate include mai multe module independente,fiecare modul fiind inclus
intr-un obiect de tip Thread.
EXEMPLU:
import javax.swing.*;
class Fir1 extends Thread {
public void run() {
JFrame frame = new JFrame("Fir1");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("Prima interfata grafica");
frame.setBounds(100,100,200,200);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
}
}
class Fir2 extends Thread {
public void run() {
JFrame frame = new JFrame("Fir2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("A doua interfata grafica");
frame.setBounds(300,100,200,200);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
}
}
public class FirExecutabil6 extends Thread {
public static void main(String[] args) {
(new Fir1()).start();
(new Fir2()).start();
}
}
-salvati fila cu numele FirExecutabil6.java.Compilati si executati.
Exemplul contine trei fire executabile.Cel din metoda main() se exe-
cuta in background si ofera suport pentru cele doua fire executabile ce
contin cate o interfata vizuala.Observati ca firele secundare sunt incluse
in clase simple (private) in timp ce firul implicit este inclus in clasa
declarata "public".In mod similar,se pot crea module complexe,cu executie
independenta.Aceste module sunt usor de importat in orice program si pre-
zinta avantajul ca nu ocupa memoria de operare decat in timpul executiei.
Un pachet importat,este prezent in mediul de memorie pe tot parcursul
executiei,in timp ce un fir executabil poate avea si o existenta efemera,
strict pe durata in care se executa firul respectiv.
Daca se aglomereaza prea multe fire executabile,se produce acelasi
fenomen ca si in cazul mesajelor Windows.Se produce fenomenul denumit si
"pool-ing",adica firele de executie se aduna in stiva (sau in tamponul)
de memorie) iar o parte dintre ele nu ajung sa fie executate decat la
inchiderea programului (cele cu prioritate minima).
-63-
Aceleasi criterii trebuiesc respectate si atunci cand editati programul
cu ajutorul unei interfete vizuale.
EXEMPLU: (NetBeans)
-deschideti un proiect nou si adaugati JFrame
-deschideti fereastra de Design
-adaugati doua butoane,alegeti pentru fiecare evenimentul Action si apoi
actionPerformed si editati metodele:
private void JButton1ActionPerformed(java.awt.event.ActionEvent evt){
class Fir1 extends Thread {
public void run() {
javax.swing.JFrame frame = new javax.swing.JFrame("Fir1");
frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
javax.swing.JLabel label = new javax.swing.JLabel("Prima interfata");
frame.setBounds(100,100,200,200);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
}
}
(new Fir1()).start();
private void jButton2ActionPerformed(java.awt.event.ActionPerformed evt){
class Fir2 extends Thread {
public void run() {
javax.swing.JFrame frame = new javax.swing.JFrame("Fir2");
frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
javax.swing.JLabel label = new javax.swing.JLabel("A doua interfata");
frame.setBounds(300,100,200,200);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
}
}
(new Fir2()).start();
-testati si executati exemplul.Este similar cu cel precedent,dar cu
deosebirea ca fiecare fir executabil este controlat cu ajutorul unui
buton.Nu mai este necesar un fir de suport.La fiecare apasare de buton,
firul respectiv va fi recreat si functional(butonul are un fir propriu).
Observati ca in ambele exemple,obiectul Thread a fost creat cu "new",
doar in momentul executiei.Acest gen de fire poarta si numele de fire
"dinamice",deoarece se pot adapta la un anumit context de memorie,spre
deosebire de cele statice (cum sunt cele daemonice) ce sunt prezente in
memorie pe toata durata de executie a programului (exemplu: firul ce
controleaza functia main).
Fiind obiecte,firele de executie pot interactiona cu toate celelalte
obiecte din acelasi spatiu de vizibilitate: -pot avea acces comun la date,
si la resurse,pot utiliza aceleasi variabile sau arii de memorie pentru
a organiza datele,pot parataja acelasi tampon de memorie pentru a executa
unele operatii etc.Mai mult decat atat,firele de executie se pot bloca si
debloca reciproc,isi pot schimba temporar prioritatile sau comenzile de
intirziere,se pot activa sau inactiva reciproc etc.Atunci cand sunt mai
multe,este bine sa fie organizate intr-un obiect de tip container.
-64-
EXEMPLU:
import javax.swing.*;
class Fir1 extends Thread {
public void run() {
JFrame frame = new JFrame("Fir1");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("Prima interfata grafica");
frame.setBounds(100,100,200,200);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
String sir1 = "";
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) { return; };
FirExecutabil7.numar1 = FirExecutabil7.numar1 + 1 ;
label.setText(sir1.valueOf(FirExecutabil7.numar1));
}}}
class Fir2 extends Thread {
public void run() {
JFrame frame = new JFrame("Fir2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("A doua interfata grafica");
frame.setBounds(300,100,200,200);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
String sir1 = "";
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) { return; };
FirExecutabil7.numar1 = FirExecutabil7.numar1 + 5;
label.setText(sir1.valueOf(Firexecutabil7.numar1));
}}}
public class FirExecutabil7 extends Thread {
public static int numar1 = 0;
public static void main(String args[]) {
(new Fir1()).start();
(new Fir2()).start();
}}
-salvati fila cu numele FirExecutabil7.java.Compilati si executati.
In exercitiul de mai sus,se pot observa urmatoarele:
-fiecare thread executa un set de operatii,dupa un algoritm propriu.
-ambele thread-uri modifica valoarea variabilei publice "numar1".
-actiunea unui thread influenteaza actiunea celuilat thread,sau altfel
spus,cele doua siruri prezinta fenomenul de inteferenta (thread interfe-
rence).Aceasta interferenta poate avea un rol pozitiv (adica cele doua
fire se potenteaza reciproc),sau poate avea un rol negativ (daca cele
doua fire se anuleaza reciproc).
-65-
Pentru a putea observa interferenta negativa,este suficient ca in
exemplul de mai sus sa fixati acelasi interval de timp pentru functia
sleep(),in ambele siruri.Apoi setati primul fir sa incrementeze cu unu
valoarea variabilei numar1,la fiecare bucla,si respectiv setati cel de
al doilea fir sa decrementeze cu unu valoarea variabilei numar1,la fiecare
bucla.La executie,puteti observa ca valoarea variabilei va oscila intre
1 si 0.
Un alt fenomen ce trebuie evitat,este fenomenul de "inconsistenta a
memoriei",adica situatiile in care datele preluate din memorie nu mai
sunt actuale in momentul interpretarii lor.Exemplu: daca se utilizeaza
un fir pentru a seta valoarea unei variabile,si un alt fir pentru citirea
valorii arhivate in variabila respectiva,exista posibilitatea ca intre
cele doua operatii sa se interpuna un al treilea fir de executie,cu prio-
ritate superioara,ce modifica valoarea variabilei respective.In acest caz,
chiar daca cele doua fire de executie sunt cuplate in aceeasi aplicatie,
exista riscul ca valoarea preluata sa fie "inconsistenta" cu valoarea
setata de primul fir.Rezultatul obtinut va fi eronat.
Pentru a evita acest gen de situatii,trebuie ca executia firelor sa se
ordoneze intr-un anumit fel,astfel incat sa nu se deranjeze reciproc.Cea
mai simpla solutie este sa intrerupeti firele de executie inutile si sa
lasati doar un singur fir,dar in acest caz,se vor pierde toate operatiile
executate de firele mai putin importante.Pentru reglementarea acestor
intreruperi,se poate utiliza metoda asociata cu bucla try-catch (pentru
InterruptedException).O modalitate mai eleganta de a ordona executia fire-
lor,va permite ca o parte dintre acestea sa fie doar intirziate,sau tre-
cute pe "linie de asteptare" si apoi sa fie reluate in ordinea prioritatii
pana cand se epuizeaza toate operatiile din program.Acest gen de solutie
poarta numele de sincronizare si se realizeaza cu ajutorul unor obiecte
denumite "monitoare".Un monitor se creaza cu ajutorul cuvintului cheie
"syncronized" si are proprietatea de a putea bloca sau debloca un fir de
executie.Pentru a simplifica lucrurile,Java Virtual machine recunoaste ca
monitor orice obiect derivat din clasa Object,adica orice obiect standard
din Java.Pentru a putea bloca si respectiv elibera firele de executie,se
vor aplea metodele wait() si respectiv notify() si notifyall().Asadar,
orice obiect care include aceste metode,poate fi utilizat si pe post de
monitor,pentru a putea organiza executia thread-urilor.De cele mai multe
ori,se utilizeaza un singur obiect,atat pentru a contine variabilele in
care se opereaza,cat si pentru a sincroniza executia.In exemplul de mai
sus,variabila "numar1" este inclusa in obiectul FirExecutabil7,care este
un obiect de tip Thread (derivat din Object) si poate fi utilizat si
pentru a sincroniza cele doua fire executabile.Este suficient sa apelati
metodele: FirExecutabil7.wait() sau respectiv FirExecutabil7.notify().
Pentru ca executia unei metode sa poata fi sincronizata,este necesar
ca metoda sa fie declarata utilizand si cuvantul cheie "SYNCHRONIZED".Nu
se pot sincroniza constructorii claselor si nici nu ar avea rost,deoarece
metodele de tip constructor nu pot fi accesate simultan de mai multe fire
de executie.Este bine insa sa utilizati metode sincronizate,ori de cate
ori un obiect este vizibil pentru doua sau mai multe fire de executie.In
acest caz,cuvantul cheie va asigura ca la un anumit moment dat,doar unul
singur dintre firele de executie va putea avea acces la obiectul respec-
tiv.Acest mecanism poarta numele de blocare intrinseca (intrinsec lock).
-66-
EXEMPLU:
import javax.swing.*;
class Fir1 extends Thread {
public synchronized void scrie() {
JFrame frame = new JFrame("Fir1");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("Prima interfata grafica");
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
String sir1 = "";
for (int i = 0;i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) { return; };
FirExecutabil8.numar1 = FirExecutabil8.numar1 + 1;
label.setText(sir1.valueOf(FirExecutabil8.numar1));
}}}
class Fir2 extends Thread {
public synchronized void scrie() {
JFrame frame = new JFrame("Fir2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("A doua interfata grafica");
frame.setBounds(300,100,200,200);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
String sir1 = "";
try {
Thread.sleep(3000);
} catch (InterruptedException e) { return; };
FirExecutabil8.numar1 = FirExecutabil8.numar1 + 5;
label.setText(sir1.valueOf(FirExecutabil8.numar1));
}}
public class FirExecutabil8 extends Thread {
public static int numar1 = 0;
public static void main(String args[]) {
Fir1 FirNou1 = new Fir1();
Fir2 FirNou2 = new Fir2();
FirNou2.scrie();
try {
FirNou1.scrie();
if (numar1 < 10) { FirNou1.wait(); };
} catch (InterruptedException e) { return; };
if ( numar1 <10) { FirNou2.notifyAll(); };
}}
-salvati fila cu numele FirExecutabil8.java.Compilati si executati.
Observati ca FirExecutabil8,nu numai ca ofera firul executabil de
suport,pentru cele doua fire executabile,dar este si obiect de tip "mo-
nitor".Metoda wait() pune primul fir pe linie de asteptare,pana cand se
deruleaza cel de al doilea fir,dupa care se activeaza cu notifyAll().
-67-
Orice obiect de tip Thread este derivat din Object si poate fi utili-
zat pe post de monitor.Pentru a putea utiliza blocajul intrinsec,trebuie
ca firul de executie sa dobindeasca acces exclusiv la acel obiect.Accesul
exclusiv se obtine apeland metoda declarata cu "SYNCHRONIZED".In momentul
in care obiectul incepe sa execute metoda sincronizata,obiectul nu mai
poate fi accesat de alte fire de executie,adica este blocat intrinsec.
Imediat dupa terminarea executiei,obiectul este deblocat,tot automat,
adica va putea fi accesat de alte fire de executie.Din acest motiv,este
esential ca monitorul sa obtina accesul exclusiv la obiect,inainte de a
apela metoda wait() sau notify().Cu alte cuvinte,inainte sa incercati sa
blocati un fir de executie,trebuie sa apelati metoda sincronizata.In
plus,este bine ca metodele wait() si notify(),sa fie apelate cu ajutorul
unei conditii oarecare de tip boolean,pentru a evita mecanismele reintran-
te(daca se incearca reblocarea obiectului fara apelul metodei sincroniza-
te,se va returna un mesaj de eroare de tip IllegalMonitorStateException).
Acest concept este ceva mai greu de inteles la inceput,dar este esen-
tial pentru a putea sistematiza ordinea de executie a thread-urilor.Nu
numai ca pot avea prioritati diferite,ci se pot crea algoritmi prin care
firele de executie se asteapta unul pe altul,pana cand exista conditiile
necesare pentru a executa o anumita operatie.Exemplu: firul efector va
astepta pana cind un alt fir executabil va deschide o anumita adresa de
Internet si va prelua un anumit set de date.Operatiile in retea nu pot fi
temporizate,deoarece depind de o serie de factori variabili (cum este
de exemplu viteza de transfer).Este esential ca firele de executie sa
poata astepta atat timp cat este necesar,pentru preluarea datelor.
La fel ca si metodele sincronizate,se pot utiliza instructiunile sin-
cronizate.Diferenta consta in faptul ca metoda sincronizata poate fi
apelata din orice monitor,in timp ce instructiunea sincronizata trebuie
sa specifice exact obiectul ce obtine blocajul intrinsec (monitorul).
EXEMPLU: public void addName(String name) {
synchronized(this) { lastName = name;
nameCount ++ ; }
nameList.add(name);
}
va putea fi apelata astfel:
public class MsLunch {
private long c1 = 0;
private Object lock1 = new Object();
public void inc1() {
synchronized(lock1) { c1++; }
}
}
-observati ca metoda synchronized se apeleaza explicit pentru obiectul
lock1,creat special pentru a fi "monitor".
Atunci cand lucrati cu un numar mare de fire executabile,este recoman-
dabil sa nu va repeziti direct la buclele automate pentru crearea algo-
ritmilor.Mai bine,construiti algoritmul cu doua sau trei fire si doar
apoi extindeti algoritmul pentru toate firele.Altfel,riscati sa mascati
o serie de operatii nedorite,sau dimpotriva,sa pierdeti din vedere o
serie de etape importante din constructia algoritmului.Verificati atent
orice mesaj de eroare si metodele pentru tratarea exceptiilor.
-68-
Atunci cand se proiecteaza sincronizarea unui numar oarecare de fire
executabile,trebuie sa se tina cont de perioada in care un astfel de fir
este activ,sau "in viata"(liveness).Exista o serie de situatii ce trebuie
sa fie evitate.De exemplu,atunci cand doua fire executabile sunt blocate
simultan si fiecare dintre ele asteapta sa fie deblocat de catre celalalt
fir,se creaza un blocaj permanent,denumit "deadlock".O situate mai putin
dramatica se intilneste atunci cand un thread asteapta executia unui alt
thread,care la rindul sau asteapta un alt thread (uneori timp destul de
idelungat).Acest gen de situatie poarta numele de "Livelock",deoarece
blocul nu este definitiv,ci mai devreme sau mai tarziu,va fi totusi exe-
cutat.Prin intirzierea executiei unor astfel de fire,se pot crea si si-
tuatii in care o parte dintre firele executabile nu mai ajung sa fie
executate,deoarece apar in permanenta thread-uri noi,cu prioritate mai
mare in executie.Acest gen de situatie poarta si numele de infometare
(starvation).Pentru a grupa thread-urile cu prioritate egala in functie
de o anumita conditie,se pot crea blocuri protejate (guarded blocks),in
care firele de executie vor fi incluse intr-o bulca conditionala de tip
if sau while.
Pentru a limita la maximum erorile de executie,este recomandabil sa
utilizati doar obiecte constante (immutable).Un obiect este constant,
atunci cand nu-si modifica statusul in timpul executiei.Nu exista o regula
fixa pentru a crea astfel de obiecte,dar se pot formula cateva recomandari
generale: -nu incudeti in obiect metode de tip set() si get().
-declarati variabilele de tip "final" si "private".
-nu supraincarcati definitiile si operatorii.
-nu amestecati obiectele constante cu cele variabile.
Un obiect este variabil,atunci cand in timpul executiei isi modifica
statusul,proprietatile,sau chiar structura interna.Obiectele cu structura
variabila sunt foarte utile in aplicatiile mici si directe,dar in cazul
aplicatiilor complexe de tip multithreading,cresc riscul de a genera
situatii de incompatibilitate.Exemplu: un thread asteapta niste date
preluate de alt thread,dar intre timp,obiectul sursa a fost reformatat si
cererea formulata este inconsistenta cu datele actuale.Pentru a evita
aceste situatii,se recomanda pe cat posibil ca thread-urile sa execute
mai ales operatii simple si directe.Acest gen de operatii,poarta si numele
de operatii "atomice",adica nu pot fi subimpartite in seturi de operatii
subsidiare.
EXEMPLU: -un thread poate deschide o fila oarecare si poate citi primul
byte din fila intr-o variabila,sau poate crea un tampon special de memorie
in care citeste primul byte din fila,apoi executa o serie intreaga de
operatii de verificare si control,reformateaza data sub forma de sir de
caractere si abea apoi arhiveaza data intr-o variabila de tip String.
Evident ca se va prefera prima varianta.Astfel de simplificari nu sunt
intotdeauna posibile,dar asigura robustetea si siguranta executiei.
Pentru a organiza si simplifica solutiile de sincronizare a thread-
urilor,java contine un pachet de obiecte special destinate,denumit:
java.util.concurent.Acest pachet contine obiecte specializate de tip
"monitor" denumite "Locks",interfete pentru executie denumite "executors"
si containere pentru colectii de fire (ThreadPools),precum si un set de
variabile de tip "atomic".Toate aceste facilitati au scopul de a simpli-
fica munca de programare si depanare a thread-urilor.
-69-
EXEMPLU:
import javax.swing.*;
import java.util.concurrent.locks.*;
class Fir1 extends Thread {
public synchronized void run() {
JFrame frame = new JFrame("Fir1");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jLabel label = new JLabel("Interfata grafica");
frame.setBounds(100,100,300,300);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
String sir1 = "";
final ReentrantLock blocaj1 = new ReentrantLock();
try { if(FirExecutabil9.numar1 < 1) {
Thread.sleep(2000);
blocaj1.tryLock();
label.setText(sir1.valueOf("Este blocat? =" +
blocaj1.isLocked()));
Thread.sleep(2000);
label.setText("de firul curent? =" +
sir1.valueOf(blocaj1.isHeldByCurrentThread())); };
FirExecutabil9.numar1 = 5;
} catch (InterruptedException e) {
return; } finally { blocaj1.unlock(); };
}}
public class FirExecutabil9 extends Thread {
public static int numar1 = 0;
public static void main(String args[]) {
(new Fir1()).start();
}}
-salvati cu numele FirExecutabil9.java.Compilati si executati.
In exemplul de mai sus am utilizat pe post de monitor un obiect de
tip ReentrantLock,deoarece are o serie intreaga de metode interesante.Nu
numai ca puteti verifica daca thread-ul a obtinut accesul exclusiv la
obiect(cu lock() sau tryLock()),dar puteti verifica daca exista si alte
thread-uri in stiva,pe linie de asteptare,cate sau cum se numesc,etc.
Exista si monitoare de tip ReentrantReadWriteLock.In acest caz,blocarea
si deblocarea trebuie sa se faca in tandem: daca este deblocat pentru
citire trebuie sa fie blocat pentru scriere si viceversa.Nu se va putea
scrie si citi simultan din obiect,ci operatiile se vor executa strict
secvential.Mai mult decat atat,obiectul ReentrantLock contine si o
metoda denumita newCondition() prin care se poate modifica in timpul
executiei conditia prin care s-a construit un blocul protejat (conditia
necesara pentru a obtine accesul exclusiv la obiect).Cu ajutorul acestei
metode,blocul poate fi adaptat interactiv la necesitatile de moment ale
utilizatorului.
Obiectele din acest pachet,nu numai ca simplifica munca de programare,
dar sunt sigure si robuste si evita o serie intreaga de posibile erori
de executie.In plus,aceste obiecte contribuie si la structura modulara
a programului,sau la rutinele de depanare si autodepanare rapida.
-70-
Pachetul Concurrent contine si alte clase extrem de utile,cum sunt cele
de tip container,cu ajutorul carora se pot organiza firele de executie,sau
orice alt fel de executabile(de exemplu intrefetele grafice).Acest tip de
obiecte este util atunci cand se lucreaza cu seturi mari de thread-uri,
cum este cazul serverelor de retea,sau al centralelor telefonice,etc.In
aceste situatii,pentru a organiza si stoca temporar modulele executabile
(firele de executie),se pot utiliza tampoane de memorie simple,dar este
mult mai eficient daca se utilizeaza obiecte special concepute.Obiectele
specializate sunt de doua tipuri: Executor Interfaces si Thread Pools.
Interfetele de tip Executor au rostul de a putea organiza seturi mari
de thread-uri create cu ajutorul unei interfete Runnable (adica thread-uri
ce implementeaza intefete grafice).
EXEMPLU:
import javax.swing.*;
import java.util.concurrent.Executor;
class Exe1 implements Runnable {
public void run() {
JFrame frame = new JFrame("Fir1");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new Jlabel("Prima interfata grafica");
frame.setBounds(100,100,200,200);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
}
}
class Exe2 implements Runnable {
public void run() {
JFrame frame = new JFrame("Fir2");
frame.setDefaultCloseOperation(Jframe.EXIT_ON_CLOSE);
JLabel label = new JLabel("A doua interfata grafica");
frame.setBounds(300,100,200,200);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
}
}
class DirectExecutor implements Executor {
public void execute(Runnable r) {
r.run(); }
}
public class Executor2 {
public static void main(String[] args) {
DirectExecutor Executabil = new DirectExecutor();
Executabil.execute(new Exe1());
Executabil.execute(new Exe2());
}
}
-salvati fila cu numele Executor2.java.Compilati si executati.
Interfata de tip Executor,utilizeaza in loc de run(),sau start(),metoda
execute(),pe care am redefinit-o in clasa DirectExecutor.Se poate observa
-71-
cu usurinta ca obiectul "Executabil" poate lansa in executie cele doua
interfete runnable cu ajutorul metodei execute().Atunci cand numarul de
interfete este redus,acelasi rezultat se poate obtine si creand cate un
thread nou,pentru fiecare interfata:
(new Thread(new Exe1())).start();
(new Thread(new Exe2())).start();
etc...
Care este diferenta ? In cazul aplicatiilor mari,cu un numar foarte mare
de thread-uri,obiectul Executor nu numai ca incapsuleaza modulele intr-un
spatiu de vizibilitate mai restrans,in care limiteaza interferentele cu
restul aplicatiei,dar prezinta avantajul ca este gata construit.Daca se
construieste cate un thread nou,pentru fiecare punere in executie a unei
interfete runnable oarecare,exista riscul ca sistemul de operare sa nu
detina suficienti pointeri spre adresele de memorie (sistemul se poate
bloca prin suprasolicitare).Cu ajutorul obiectului Executor,aceeasi inter-
fata runnable poate fi pusa in executie ori de cate ori,fara a se crea
redundanta (firele de executie sunt "muritoare",iar la un nou apel vor
returna un mesaj de eroare).In exemplul de mai sus,puteti incerca sa
apelati cele doua interfete,in mod repetat,dupa un algoritm oarecare.
Daca in loc de Executor,utilizati o interfata de tip ExecutorService,
beneficiati si de setul complet de metode al acestei interfete.De exemplu,
pentru a inchide simultan toate executabilele din obiect,se va putea apela
metoda shutdown(),sau pentru a activa simultan o colectie intreaga de
executabile,se va putea apela metoda invokeAll().ExecutorService este
derivata din Executor si adauga un set intreg de metode utile pentru
controlul si monitorizarea executiei.Detaliile referitoare la acest
subiect nu fac obiectul acestui manual,dar este bine sa retineti faptul
ca aceste obiecte,va pot simplifica foarte mult problemele ridicate de
programarea multitasking in regim concurential.In plus,functionalitatea
interfetelor de tip Executor si ExecutorService poate fi extinsa cu aju-
torul metodelor clasei Executors( callable(),newCachedThreadPool(),new-
SingleThreadExecutor(),privilegedCallable(),defaultThreadfactory(),etc.).
De exemplu interfata ThreadFactory,sau metoda Executors.defaultThreadFac-
tory(),se pot utiliza atunci cand doriti sa creati in permanenta fire noi
de executie.Firele nou create vor fi toate in acelasi grup (ThreadGroup)
si vor putea fi organizate cu acelasi SecurityManager.In plus,firele de
executie create cu ThreadFactory nu mai apeleaza la poinerii sistemului
de operare,ci utilizeaza adrese de memorie oferite de obiectul construc-
tor.Ca rezultat se evita eventualele "erori de executie",ce nu pot fi
evidentiate decat in situatiile extreme ale mediului de memorie(cum este
cazul pointerilor spre memorie).
Obiectele de tip container compartimenteaza spatiul de memorie si sim-
plifica munca de depanare si control.Containerele de tip ThreadPool,dupa
cum le spune si numele,au rolul de a adaposti temporar thread-urile cu
prioritate mai redusa.In locul unei stive simple,se vor utiliza aceste
obiecte specializate,care simplifica operatiile de sortate a thread-urilor
aflate "pe linie de asteptare".Cu alte cuvinte,obiectele de tip Thread-
PoolExecutor ofera o serie intreaga de metode,prin care arhiveaza tempo-
rar,activeaza sau dezactiveaza thread-urile,ajusteaza dimensiunea stivei,
controleaza numarul de thread-uri din stiva,anticipeaza si rezolva even-
tualele suprasolicitari,etc.
-72-
EXEMPLU:
import javax.swing.*;
import java.util.concurrent.ThreadFactory;
-73-
EXEMPLU:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import.java.util.concurrent.TimeUnit;
import.java.util.concurrent.BlockingQueue;
import.javax.swing.*;
class Exe1 implements Runnable {
public void run() {
JFrame frame = new JFrame("Fir1");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jLabel label = new JLabel("Interfata grafica");
frame.setBounds(100,100,200,200);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
try {
int x=0;
String sir1 = "";
ThreadPoolExecutor pool = new ThreadPoolExecutor (
10,20,60,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(20));
x= pool.getMaximumPoolSize();
label.setText("max Stiva = " + sir1.valueOf(x));
} catch (NullPointerException n) { return; };
}
}
public class Test1 {
-74-
In cazul interfetelor grafice realizate cu ajutorul componentelor din
pachetul SWING,organizarea firelor de executie trebuie sa tina cont si de
alti factori.Astfel,pentru a actualiza datele dintr-un obiect(component),
este esential ca apelul sa se faca din interiorul thread-ului in care
este definit obiectul Event Listener,adica din firul executabil in care
se "asteapta" evenimentul respectiv.Firul de executie in care se fac toate
operatiile de actualizare,poarta numele de "fir pentru tratarea evenimente-
lor" (event dispatch thread) si contine toate obiectele vizuale,impreuna
cu functiile pentru tratarea evenimentelor.Fiecare thread este un obiect
separat si izoleaza un anumit spatiu de vizibilitate.Daca se incearca
actualizarea unui obiect dintr-un thread,cu ajutorul unui alt thread,este
posibil sa apara erori de executie ce nu pot fi depistate de compilator si
nici nu se manifesta decat in anumite circumstante ale mediului de memorie.
Aceste erori,se pot produce fie prin interferenta (thread interference),fie
prin inconsistenta (memory consistency errors).Din acest motiv,majoritatea
obiectelor din pachetul Swing sunt descrise ca "not thread safe".Exista
si un numar limitat de obiecte,specificate ca "thread safe".Acestea pot
fi apelate din orice thread si in orice moment al executiei.Pentru a
evita aceste situatii,este bine sa includeti toate operatiile de actuali-
zare,doar in firul "event dispatch thread",sau sa organizati toate ope-
ratiile de asa maniera incat sa excludeti orice interferenta asupra date-
lor publice.
O alta situatie ce trebuie evitata,este cea a interfetelor "inghetate".
Atunci cand se utilizeaza un singur fir de executie,daca programul trebuie
sa execute un sir de operatii mai complex (de exemplu sa copieze o anumita
fila sau un program din reteaua Internet),atunci intreaga interfata gra-
fica va fi blocata ("inghetata") pentru intregul interval de timp,pana
cand se epuizeaza lantul de operatii.Pentru a evita acest gen de situatii,
este recomandabil sa utilizati mai multe thread-uri,dintre care unul va
tine evidenta interfetei grafice (event dispatch thread),iar altul va
lucra independent in background si va executa sirul de operatii necesare.
Acest ultim fir de executie,poarta si numele de "fir de lucru" (worker).
In pachetul Swing este inclus si un obiect special destinat pentru aceste
operatii,cu numele de SwingWorker.
Pe langa aceste fire de executie,pot fi necesare si altele,cu functii,
sau cu atributii specifice,pe care doriti sa le separati spatial de restul
operatiilor din program.De exemplu,daca aplicatia d-voastra necesita si o
serie de operatii preliminare (formatare,conversia datelor,crearea unor
obiecte specializate,preluarea datelor de la o anumita adresa,etc),atunci
se poate utiliza un thread special pentru initializare (initial thread).
Atunci cand thread-urile nu au o pondere egala,este bine sa nu fie
create cu ajutorul obiectelor de tip ThreadFactory si sa nu fie incluse
in containere de tip ThreadPool,ci sa fie organizate temporal,astfel incat
fiecare thread sa exploateze eficient procesorul.Daca aplicatia este mai
complexa,intotdeauna este util sa desenati si o schema simpla de executie,
asemanatoare cu un circuit electric,pe care sa sistematizati ordinea de
executie.O parte dintre operatii trebuie sa poata fi executate simultan,
sau cel putin "aparent simultan",in timp ce alte operatii vor trebui sa
stea pe linie de asteptare,pana cand configuratia de memorie va putea
permite executarea lor.Spre deosebire de thread-urile simple,SwingWorker
detine si un set de metode proprii,ce pot fi eventual redefinite.
-75-
Pe langa constructor,obiectele de tip SwingWorker detin si medtode ca:
doInBackground(),done(),execute(),get(),process(),publish(),run() etc.
Un obiect de tip SwingWorker se pune in executie prin metoda execute().
Nu este obligatoriu ca SwingWorker sa execute doar operatii in back-
ground.SwingWorker poate fi utilizat ca si un thread obisnuit,pentru orice
fel de operatii.De exemplu,poate include obiectele vizuale,pentru a fi un
thread de tip "event dispatch thread".
EXEMPLU:
import javax.swing.*;
class Procese extends SwingWorker<String,Object> {
@Override
public String doInBackground(){
return "text";
}
@Override
public void done() {
try {
JFrame frame = new JFrame("Fir1");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("Interfata grafica");
frame.setBounds(100,100,300,300);
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
label.settext(get());
} catch (Exception ignore) {}
}}
public class FirExecutabil10 extends Thread {
public static void main(String args[]) {
(new Procese()).execute();
}}
-salvati fila cu numele FirExecutabil10.java.Compilati si executati.
In exemplul,de mai sus,se pot observa urmatoarele:
-thread-ul SwingWorker se initializeaza cu execute()
-thread-ul SwingWorker <T,V> are si urmatorii parametri:
T - este tipul de data returnat de metodele doInBackground si Get
V - este tipul de data intermediara (tamponul de memorie) pentru
metodele publish si process
-metodele clasei pot fi redefinite cu ajutorul operatorului @Override
-in exemplu,am redefinit metoda doInBackground(),pentru a returna un
anumit sir de caractere si am redefinit metoda done(),pentru a crea
o mica interfata grafica,in care sa afiseze textul returnat
-metoda done() este apelata automat in momentul in care se termina
executia metodei doInBackground.Prin redefinirea lor,se poate crea un
duplex,pentru organizarea unui set oarecare de operatii.
In exemplul de mai sus,cele doua metode se executa simultan.Pentru
a ilustra mai bine secventa de executie a celor doua metode,exemplul se
poate dezvolta,astfel incat metoda doInBackground se execute o serie de
operatii preliminare,necesare pentru initializarea aplicatiei (de tip
initialize thread),iar metoda done() sa lanseze interfata grafica si
apoi sa o actualizeze (ca un event dispatch thread).
-76-
EXEMPLU:
import javax.swing.*;
-77-
EXEMPLU:
import javax.swing.*;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.awt.event.ActionEvent;
import java.awt.BorderLayout;
-78-
O situatie putin deosebita,este in cazul applet-urilor.In mod normal,
este recomandabil ca fiecare applet sa fie lansat in executie cu un thread
separat,destinat sa organizeze toate operatiile din applet.Nu se va lansa
in executie un applet din interiorul unui thread de tip event dispatch,
deoarece se pot crea interferente nedorite.Este de dorit ca fiecare applet
sa reprezinte un modul independent,fara legatura cu restul aplicatiei.Din
acest motiv,nu se vor utiliza functii I/O in interiorul unui applet,decat
pentru operatii ce se excuta local.
EXEMPLU:
import javax.swing.*;
import java.awt.*;
-79-
APPLET-URI JAVA
-80-
Pentru ca obiectele vizuale sa fie interective,trebuie sa utilizati
evenimente si functii EventListener,la fel ca si incazul aplicatiilor
simple.Cel mai simplu buton functional se programeaza cam asa:
EXEMPLU:
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
import java.applet.Applet;
-81-
EXEMPLU:
import java.awt.Graphics;
import javax.swing.*;
-82-
Executati exemplul si observati fiecare dintre metode.Metoda stop(),
apeleaza automat metoda destroy,astfel incat,desi are o intarziere de
o secunda,mesajul de avertizare nu poate fi vizualizat (desi apare),
deoarece se executa imediat metoda destroy().
In principiu,metoda init() contine codurile pe care in mod normal le
contine constructorul clasei.Se pot include in aceasta metoda si alte
operatii,dar este de dorit sa fie cat mai scurte si sa nu intarzie exe-
cutia aplicatiei.Pentru operatiile laborioase,este bine sa rezervati un
thread separat,ce se va executa in background-ul aplicatiei,fara sa afec-
teze etapa de constructie a obiectului.Metoda init(),apeleaza automat
metoda start().
In metoda start(),se includ operatiile principale ale applet-ului.
Daca exista seturi de operatii ce urmeaza sa fie executate in paralel,
atunci din metoda start() se vor lansa in executie mai multe thread-uri,
fiecare dintre ele cu setul sau de operatii: in background,sau vizuale.
Cand redefiniti metoda start(),este bine sa redefiniti si metoda stop().
In mod normal,metoda stop() se utilizeaza,fie pentru a intrerupe temporar
un set oarecare de operatii (un fel de pauza),fie pentru a defini des-
tructorul,inainte de a parasi applet-ul.Metoda stop(),se apeleaza atunci
cand executia paraseste applet-ul,de exemplu atunci cand se apeleaza un
link spre o alta fila.In momentul in care se revine la fila initiala,
appletul va fi reconstruit automat prin apelul functiei init().In exemplul
de mai sus,navigati de cateva ori intre cele doua file si observati
modul automat in care sunt apelate metodele applet-ului.
Metoda destroy() este destructorul applet-ului.In mod normal,nu este
bine sa redefiniti aceasta metoda.Se poate utiliza atunci cand doriti ca
destroy() sa elibereze din memorie si alte structuri de date,decat cele
eliberate implicit (adica sa fie destructor si pentru alte obiecte).
Clasa Applet,mosteneste o serie intreaga de facilitati de la superclasa
sa Container.Asfel sunt: metoda paint() sau processKeyEvent si process-
MouseEvent...etc.
Un applet se poate edita si cu ajutorul unei platforme vizuale.Daca
utilizati NetBeans,deschideti un proiect oarecare si apoi creati o fila
noua de tip JApplet,fie cu File->New File fie cu un click drept pe numele
proiectului,apoi alegeti New si JApplet.NetBeans va crea automat capul
de fila cu declaratia clasei si metoda init().Platforma vizuala prezinta
si o serie intreaga de avantaje.De exemplu,puteti compila si previzualiza
applet-ul fara sa fie necesara o fila HTML sau un browser.Pur si simplu,
executati un click drept pe numele filei (in Projects) si alegeti optiunea
Debug.Fila de tip applet se poate salva in interiorul proiectului,sau la
orice alta adresa.Compilarea filei nu genereaza automat fila .class,ci
doar verifica daca nu exista erori de sintaxa.Pentru a obtine fila de tip
.class,trebuie sa compilati fila prin procedeul obisnuit.Daca insa con-
struiti intregul proiect,fila .class pentru applet-ul respectiv va fi
creata automat in directorul build/classes din proiectul final.
Platforma vizuala este foarte practica atunci cand creati doua sau mai
multe applet-uri,sau atunci cand transformati un applet mai vechi,prin
adaugarea de noi functionalitati.In plus,daca utilizati optiunea Debug,
puteti apasa meniul Applet pentru a reseta unele dintre proprietati:
Http proxy server,Httpproxy port,Class access etc.In rest,toate etapele
de editare sunt identice,ca pentru editarea cu NotePad.
-83-
Pentru ca applet-ul sa raspunda la evenimente,la fel ca orice interfata
grafica,se va adauga un eventListener si functia sau functiile necesare
pentru tratarea evenimentului respectiv.De exemplu,pentru a exploata eve-
nimentele indicatorului mouse,se va adauga un MouseListener.
EXEMPLU:
import javax.swing.*;
import java.awt.*;
import.java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
-86-
Cele mai simple operatii sunt cele prin care se deseneaza un cerc sau
un patrat,o linie sau un arc.La acestea se adauga operatiile de setare a
fonturilor si culorilor si cele pentru decuparea si copierea unui fragment
oarecare din imagine.
EXEMPLU:
import javax.swing.*;
import java.awt.*;
import javax.swing.*;
import java.awt.*;
import java.text.*;
import java.awt.font.TextAttribute;
-87-
Un efect special foarte usor de realizat se poate obtine cu ajutorul
clasei GradientPaint,din pachetul java.awt.GradientPaint.Se poate utiliza
pentru a umple orice poligon (inclusiv caracterele grafice) cu o culoare
ce se modifica in gradient pe o suprafata delimitata din cea grafica.
Obiectul are patru variante de constructori si se poate apela direct.
EXEMPLU:
import javax.swing.*;
import java.awt.*;
import java.text.*;
import java.awt.font.TextAttribute;
-88-
Interfata grafica din Swing are o functionalitate destul de limitata.
Fiecare interfata de tip AudioClip,va putea reda la un anumit moment dat,
un singur sunet,preluat dintr-o fila sursa de tip Sun,cu extensia .au,in
care sunetele sunt codificate in format de 8 biti,pe un singur canal cu
o frecventa de 8000 Hz.Puteti crea aceste file,sau puteti converti orice
fel de file audio,cu ajutorul unui program specializat de conversie.Un
program de conversie foarte bun si gratuit,este de exemplu: Switch Sound
File Converter oferit de firma NCH Software (vezi adresa www.nch.com).
Aplicatiile pentru redarea sunetelor pot fi si extrem de complexe.Se
pot crea mai multe benzi de sunet,ce pot fi executate simultan,pot fi
mixate sau filtrate dupa un anumit algoritm etc.Pentru simplificarea
acestor operatii,exista si alte obiecte java specialiate,dar prezentarea
acestor obiecte va face obiectul unui alt capitol.Pentru applet-uri este
suficient sa puteti include una sau mai multe file de sunet,ce pot fi apoi
executate simultan sau serial.Nici nu este indicat sa creati o aplicatie
de tip media-player intr-un applet,deoarece o astfel de aplicatie ocupa
un volum mare de memorie,cu multe thread-uri si niveluri de intrerupere,
ce pot dreanja sau interfera cu operatiile de navigare de la o fila la
alta (fiecare incarcare va fi mult prelungita si exista riscul de a
epuiza memoria de operare,doar pentru aplicatia audio).
Din acelasi motiv,nu este recomandabil nici sa creati o baza prea
larga de file sursa,conectata la applet.Daca doriti sa existe totusi o
gama oarecare de optiuni,adaugati un folder cu file sursa si un obiect de
tip ComboBox,sau FileChooser in care se va selecta fila dorita.Cu cat
applet-ul va fi mai complex,cu atat va ocupa mai mult din memoria de ope-
rare rezervata pentru fila HTML gazda.Cu cat applet-urile sunt mai mici,
cu atat fila gazda va putea incarca si executa simultan mai multe astfel
de applet-uri.Din acest motiv,decat sa faceti o singura fila complexa,cu
foarte multe functionalitati,este mai bine sa realizati seturi intregi
de applet-uri simple,pe care sa le puteti apoi combina in configuratia
dorita.Este bine sa va construiti o mica arhiva de astfel de applet-uri.
Exista si unele situatii,in care doriti sa utilizati o fila locala de
tip text,ca sursa de date pentru applet.Fie doriti sa afisati un simplu
text,fie doriti sa preluati un set oarecare de valori numerice ce urmeaza
sa fie apoi procesate intr-un algoritm oarecare.In retea,applet-urile nu
au acces la filele locale,nici pentru server,nici pentru client.Aceasta
masura de securitate are rolul de a proteja fisierele importante ale
sistemului de operare de asa numitele "atacuri ale hack-erilor" din retea.
Cu alte cuvinte,face parte din politica de tip "antivirus".Restrictia de
acces nu se aplica si in cazul filelor locale.Cu alte cuvinte,nu puteti
edita un applet care sa citeasca filele de tip text din alt calculator,
dar puteti sa editati applet-uri care preiau date din filele de tip text
arhivate in propriul calculator.Tehnologia de lucru este la fel ca pentru
orice operatie de tip I/O.Se utilizeaza un stream pentru citirea datelor,
si o bucla de tip try...catch pentru interceptarea si tratarea exceptiilor
de tip I/O.In mod normal,este mai bine sa evitati acest gen de solutie,
cu exceptia cazurilor in care applet-ul este doar o extensie a unui pro-
gram mai vechi,creat prin acest mecanism.Este recomandabil sa utilizati
file sursa fixe,dar se poate utiliza si un obiect de tip FileDialog sau
JFileChooser,pentru a putea naviga prin arhiva locala.Este bine sa evitati
interfetele prea incarcate,cu prea multe evenimente si operatii.
-89-
EXEMPLU:
import java.applet.*;
import java.awt.*;
import java.io.*;
import java.net.*;
-90-
Un applet poate fi utilizat si pentru a crea un modul de lucru intr-o
baza de date,dar aceasta functionalitate va fi prezentata intr-un capitol
separat.Trebui mentionat si faptul ca Applet si JApplet sunt doua clase
absolut obisnuite.Nu este obligatoriu sa fie incluse doar in aplicatii de
tip applet,ci se pot utiliza in aplicatiile obisnuite,la fel ca orice
clasa.Daca se utilizeaza insa intr-o aplicatie obisnuita,interfata grafica
trebui sa fie inclusa intr-o fereastra (Form).
In tutorialul firmei Sun se specifica si urmatoarele recomandari gene-
rale,inainte de a pune in circulatie un applet:
1.Inactivati toate rutinele de depanare.De exemplu,toate comenzile de gen
System.out.println("mesajul dorit") incluse in buclele try-catch.Aceste
comenzi sunt foarte utile in etapa de editare sau depanare a programu-
lui,dar nu au nici un rost in timpul executiei normale.Daca preluati
exemple gata facute,este bine sa inactivati aceste comenzi.In plus,
este bine sa verificati daca nu exista si alte date care incarca inutil
memoria de operare (variabile neutilizate,constante inutile,obiecte
inactive,link-uri nefunctionale etc.)
2.Verificati daca applet-ul este inactiv atunci cand fila HTML gazda este
nefunctionala.Cu alte cuvinte,atunci cand applet-ul lanseaza in executie
unul sau mai multe thread-uri,trebuie sa va asigurati ca fiecare thread
contine si o metoda stop() pentru intreruperea executiei.In caz contrar,
exista riscul ca thread-ul respectiv sa ramana functional in background
si sa consume inutil din resursele procesorului.
3.Atunci cand applet-ul executa o operatie oarecare iterativa: animatie,
un sunet repetat,un spot publicitar sau o reclama...etc,trebuie sa va
asigurati ca utilizatorul are posibilitatea de a intrerupe in orice
moment functionalitatea respectiva (pentru a nu deveni agasanta).
La fel ca si fielele de tip Web,applet-urile sunt niste module de pro-
gram destinate sa execute operatii,fie local,fie intr-o retea oarecare de
calculatoare.In cazul aplicatiilor locale,nu exista restrictii speciale,
diferite de cele impuse de standardul de limbaj.Insa,in cazul in care un
applet este introdus in retea,exista si o serie de restricti impuse de
masurile de securitate ale retelei respective.Politica si masurile de se-
curitate pot fi usor diferite de la o retea la alta,dar in principu se vor
aplica urmatoarele restrictii:
-un applet nu poate adauga in sistem biblioteci noi de functii ( le
poate utiliza doar pe cele existente) si nici nu poate adauga metode noi
apelabile din programele externe (metodele sunt valabile doar in applet).
-in mod normal,un applet nu poate citi sau scrie filele native ale
unitatii gazda (maxim poate exploata un set de file atasate applet-ului).
-un applet nu poate face conexiuni in retea,decat cu unitatea sursa
(cea in care este gazduit applet-ul din retea).
-un applet nu poate pune in executie programele unitatii gazda
-un applet nu poate citi proprietatile sistemului de operare (setari,
constante,coduri,parole de acces,optiuni preferentiale...etc.).
-daca applet-ul deschide o fereastra noua,aceasta va avea un aspect
diferit de cel al ferestrelor din unitatea gazda,pentru a se putea
face o distinctie cat mai clara intre sistemul de operare si modul.
La aceste masuri generale,se adauga si cele specifice pentru fiecare
browser utilizat si cele ale programelor de tip anti-virus instalate in
calculatorul gazda.
-91-
Aceste restrictii nu sunt aplicabile si pentru administratorii de retea.
Daca aveti o problema ce nu poate fi rezolvata decat ignorand una dintre
restrictii,este recomandabil sa luati legatura cu administratorul de
retea.Mai ales in cazul reteleor locale,se pot gasi solutii fiabile,pentru
orice astfel de problema.
In principiu,in cadrul unei retele de calculatoare exista doua tipuri
de aplicatii.Aplicatii de tip server,destinate unitatilor ce asigura
serviciile de comunicatie din retea si aplicatii de tip client,adica
cele destinate pentru utilizatori.Pentru informatii suplimentare despre
aplicatiile de tip net-work,va trebui insa sa consultati alte surse de
informatie.
JAVA LOOK AND FEEL
Arhitectura Swing din Java,permite ca interfata grafica sa-si schimbe
interactiv aspectul grafic.Prin "LOOK" se intelege in termeni generali
modul de prezentare a componentelor vizuale iar prin "FEEL" se va intelege
modul de reactie la evenimente.Pentru acest scop,java separa fiecare
component in doua subclase.Una dintre subclase va contine obiectul de tip
JComponent(respectiv descendentul sau) iar cea de a doua subclasa va
contine un obiect de tip ComponentUI.Cea de a doua subclasa,este prezenta
in literatura de specialitate si cu denumirile: UI,UI component,UI dele-
gate,look and feel delegate etc.In mod normal,programatorul nu are nici o
interactiune directa cu aceasta subclasa.Ea exsita doar pentru a permite
ca obiectul sa poata avea forme diferite de prezentare.Toate operatiile se
executa automat,in momentul compilarii.
Cu ajutorul acestui mecanism,este posibil ca interfata grafica creata
de d-voastra sa-si schimbe aspectul cameleonic,in functie de sistemul de
operare pe care ruleaza aplicatia,sau la o comanda oarecare data de un
buton.Exista cateva aspecte standard implicite,sau puteti sa creati un
sablon oarecare pe care sa-l aplicati apoi la doua sau mai multe interfete
grafice.Daca sunteti un programator profesionist,puteti sa utilizati
acelasi sablon,pentru toate programele create de d-voastra,pentru a va
crea un "stil propriu".Mai mult decat atat.Java poate imprumuta stilul de
prezentare instalat in momentul respectiv in calculatorul userului.De
exemplu,daca aplicatia d-voastra este o extensie a sistemului de operare
si doriti sa utilizatorul sa vada obiectele cu acelasi aspect ca si cele
din sistemul sau de operare,puteti alege aceasta optiune automata.
Resursele pentru "Look and Feel" sunt situate in doua pachete diferite.
In pachetul implicit javax.swing.plaf.metal se gasesc resursele necesare
pentru aspectul implicit al obiectelor Java,pentru cele care imprumuta
aspectul de la sistemul de operare si pentru cele definite explicit de
catre utilizator.Aceste aspecte implicite sunt:
1.-CrossPlatformLookAndFeel -este denumit si "Metal" si contine setarile
implicite.Acesta este aspectul utilizat,daca nu formulati nici o alta
cerere specifica.Acest aspect va fi utilizat implicit,indiferent de
sistemul de operare pe care va rula aplicatia Java.Cu alte cuvinte,o
aplicatie Java obisnuita va avea acelasi aspect grafic,atat sub Windows,
cat si sub Linux sau Solaris.
2.-SystemLookAndFeel -este setul de resurse prin care se imprumuta de
la sistemul de operare,modul de prezentare grafica.Valorile sunt preluate
in timpul executie,astfel incat interfata grafica va respecta setarile
actuale ale sistemului de operare.
-92-
3.-Synth -contine resursele necesare prin care aspectul grafic poate fi
importat dintr-o fila de tip XML (locala sau din retea).Cu o singura
astfel de fila,se poate asigura aspectul grafic pentru un set intreg de
aplicatii create de aceeasi firma.
4.-Multiplexing -contine resursele pentru ca intefata sa poata sa-si
modifice aspectul interactiv,in timpul executiei.Pentru acest scop,cea
de a doua subclasa functionaza ca un tampon de memorie in care se retine
definitia initial.Subclasa poate fi rescrisa ori de cate ori,astfel ca
intefata se poate modifica cameleonic,chiar si in timpul executiei.
Pe langa aceste variante implicite,firma Sun ofera si alte seturi de
resurse,dar acestea sunt arhivate in directorul "com/sun/java/swing/plaf"
Acest pachet este arhivat in directorul "src" in paralel cu pachetul
java obisnuit.Pentru a se face distinctie intre cele doua pachete "java",
trebuie sa utilizati calea completa de acces,ori de cate ori apelati
aceste resurse (adica trebuie sa includeti si "com/sun/java....").
Acest pachet auxiliar contine seturile GTK,Motif si Windows.Primele doua
sunt destinate pentru sistemele de operare Linux si UNIX iar ultimul
pentru Windows.
Pentru a seta apectul grafic se va utiliza un obiect derivat din clasa
UIManager si metoda setLookAndFeel().
EXEMPLU: -pentru ca interfata sa primeasca apectul de tip Windows:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.metal.*;
-93-
Pachetul de resurse implicit,permite schimbarea culorilor prin mai
multe mecanisme.In primul rand,exista trei seturi implicite denumite
"themes"(teme).Aceste seturi sunt: DefaultMetal,Ocean si Test si se pot
inlocui reciproc cu o singura comanda simpla:
EXEMPLU: MetalLookAndfeel.seturrentTheme(new OceanTheme());
Daca doriti sa utilizati un set de culori personalizate,trebuie sa
definiti o clasa in care sa specificati aceste seturi,sau sa asociati o
fila de tip XML in care sunt specificate toate setarile grafice.
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.metal.*;
-96-
Din acest motiv,am preferat termenul de "manageri de design",chiar daca
in structura lor primara obiectele de tip Layout Manager nu asigura decat
pozitionarea componentelor intr-un container (layout = pozitionare).
Cea mai simpla pozitionare,se realizeaza fara nici un manager:
EXEMPLU:
import javax.swing.JButton;
import javax.swing.JFrame;
-97-
EXEMPLU:
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.Jframe;
import java.awt.Dimension;
import java.awt.Contaniner;
-98-
Daca se utilizeza Box,nu mai este necesar sa setati managerul cu o
comanda de tip setLayout().Daca se utilizeaza alt tip de container,este
obligatoriu sa setati managerul dorit.
EXEMPLU:
import javax.swing.JButton;
import javax.swing.JFrame;
import java.awt.Component;
import java.awt.Container;
import javax.swing.BoxLayout;
public class Design3 {
public frame = new JFrame("Exemplu cu BoxLayout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,300);
adaugaComponente(frame.getContentPane());
frame.setVisible(true);
}
public static void adaugaComponente(Container pane) {
pane.setLayout(new BoxLayout(pane,BoxLayout.Y_AXIS));
adaugaButon("Buton 1",pane);
adaugaButon("Buton 2",pane);
adaugaButon("Buton 3",pane);
adaugaButon("Buton 4 (text lung)",pane);
adaugaButon("5",pane);
}
private static void adaugaButon(String text,Container container) {
JButton button = new JButton(text);
button.SetAlignementX(Component.CENTER_ALIGNEMENT);
container.add(button);
}
}
-salvati fila cu numele Design3.java si compilati
In exercitiul de mai sus,trebuie observat ca s-a utilizat o functie
speciala pentru crearea fiecarui buton,cu doi parametri: textul dorit si
containerul tinta.Aceasta solutie este necesara,deoarece clasa BoxLayout
implementeaza o interfata de tip LayoutManager2 in loc de LayoutManager.
LayoutManager2 este o extensie a interfetei LayoutManager si se utili-
zeaza atunci cand programatorul doreste sa forteze utilizarea unui anumit
obiect tinta,sau a unui anumit obiect de tip manager (din acest motiv,
constructorul include si containerul tinta).
Pentru a simplifica utilizarea managerului de tip BoxLayout,este mult
mai simplu sa utilizati obiecte de tip Box.Grupati componentele in unul
sau mai multe obiecte de tip Box,apoi includeti obiectele Box intr-o
fereastra,sau in containerul dorit.Obiectul Box,ofera si o serie de faci-
litati suplimentare.Exemple:
-daca doriti ca obiectele sa nu poata fi redimensionate (sa fie "lipite"
de interfata) puteti utliza metoda createGlue()
-daca doriti ca spatiul dintre obiecte sa fie constant si dupa redimen-
sionare interactiva,puteti utiliza metoda createHorizontalStrut()
-daca doriti sa creati un component invizibil,dar care ocupa permanent
acelasi spatiu din interfata,puteti utiliza metoda createRidgidArea()
In plus,nu mai este necesar sa construiti un obiect de tip BoxLayout.
-99-
EXEMPLU:
import javax.swing.*;
import java.awt.Component;
import java.awt.Container;
public class Test4 {
public static void main(String[] args) {
JFrame frame = new JFrame("Exemplu cu Box");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,300);
Box caseta1 = new Box(BoxLayout.Y_AXIS);
JButton b1 = new JButton("text1");
JButton b2 = new JButton("text2");
caseta1.add(b1);
caseta1.add(b2);
caseta1.setVisible(true);
frame.add(caseta1);
frame.setVisible(true);
}
}
-salvati fila cu numele Test4.java si compilati.
CARDLAYOUT MANAGER
Un alt tip de manager pentru design,este CardLayout manager.Acest tip
de obiect trateaza fiecare component din container ca si cand ar fi o
carte de joc,dintr-un pachet de carti.Primul obiect adaugat in container
va fi cel vizibil implicit,iar restul obiectelor adaugate se vor aseza
ca intr-un pachet,sub componentul implicit.La un anumit moment dat,nu se
poate vizualiza decat unul singur dintre componentele din container,asa
cum din pachetul de carti nu se poate vedea decat una singura.Ordinea in
care se vor afisa restul de componente se poate organiza prin diversi
algoritmi si combinatii de evenimente.
Acest tip de manager,este foarte util atunci cand doriti ca interfata
grafica sa poata fi modificata interactiv,in functie de un anumit criteriu
sau in functie de un anumit eveniment.Pentru acest scop,grupurile de
obiecte vizuale necesare se pot introduce in containere de tip JPanel,
iar containerele JPanel pot fi organizate cu ajutorul unui obiect de tip
CardLayout manager.
Exemple: -se pot crea doua sau mai multe variante de interfata grafica,
iar utilizatorul va putea avea acces la una dintre variante,in functie
de o parola,sau un cod de acces.
-se pot crea grupuri de componente,incluse in containere diferite
iar in timpul executiei,containerele vor fi afisate succesiv,pe masura
ce sunt necesare pentru a controla operatiile aflate in curs.
In esenta,se pot crea solutii grafice prin care se elimina din inter-
fata grafica toate obiectele inutile si se inlocuiesc cu butoane,sau
cu casete de dialog.In momentul in care este necesar,interfata grafica
se va reconfigura,astfel incat sa afisese controalele necesare in acel
moment.
Pentru a naviga printre componentele containerului,obiectul manager
ofera metode specializate ca: first(),next() si last() sau previous().
Alte metode organizeaza dimensiunea si pozitionarea containerului.
-100-
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Design4 implements ItemListener {
JPanel cards;
public void Componente(Container pane) {
JPanel comboBoxPane = new JPanel();
String comboBoxItems[] = { "Butoane","Textbox","Eticheta" };
JComboBox cb = new JComboBox(comboBoxItems);
cb.addItemListener(this);
comboBoxPane.add(cb);
JPanel card1 = new JPanel();
card1.add(new JButton("Buton 1"));
card1.add(new JButton("Buton 2"));
card1.add(new JButton("Buton 3"));
JPanel card2 = new JPanel();
card2.add(new JTextField("Introduceti datele: ",20));
JPanel card3 = new JPanel();
card3.add(new JLabel("Eticheta");
pane.add(comboBoxPane,BorderLayout.PAGE_START);
pane.add(cards,BorderLayout.CENTER);
}
public void itemStateChanged(ItemEvent evt) {
CardLayout cl = (CardLayout)(cards.getLayout());
cl.show(cards,(String)evt.getItem());
}
private static void Interfata() {
JFrame frame = new JFrame("CardLayoutDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Design4 demo = new Design4();
demo.Componente(frame.getContentPane());
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) { Interfata(); }
}
-salvati fila cu numele de Design4.java si compilati
In exemplul de mai sus,prelucrat dupa exemplul din tutorialul oferit de
firma Sun,se pot observa etapele de lucru:
-se creaza mai multe obiecte de tip JPanel
-in fiecare container JPanel se includ obiectele vizuale dorite
-se creaza un obiect tip ComboBox cu pointeri spre fiecare JPanel
-se creaza un container de tip JPanel administrat de un manager de tip
CardLayout [new JPanel(new CardLayout())] in care se includ apoi toate
obiectele de tip Jpanel ce contin componente
-se include obiectul ComboBox intr-un alt panel si apoi impreuna cu
containerul principal se includ in containerul de baza
-se conecteaza evenimentul itemStateChanged la pointerii din ComboBox
Executati exercitiul si utilizati caseta ComboBox,pentru a schimba
interactiv interfata grafica (schimbati obiectul JPanel afisat).
-101-
FLOWLAYOUT MANAGER
FlowLayout manager organizeaza componentele unui container in ordinea
declararii,ca si cuvintele dintr-o propozitie.Acest manager este asociat
automat cu obiectele de tip JPanel.Obiectele sunt adaugate in container
pe linie orizontala,pana la epuizarea spatiului disponibil,apoi se trece
la linia urmatoare,la fel ca si cuvintele dintr-un text.Alinierea este
determinata prin proprietatea ALIGN si poate avea urmatoarele valori:
LEFT,RIGHT,CENTER,LEADING sau TRAILING.Obiectul FlowLayout are trei con-
structori diferiti.Cel mai complex dintre acestia permite specificarea
spatiilor libere dintre componenete.Acest tip de manager este usor de
utilizat si pentru rutine automate.
EXEMPLU:
import javax.swing.*;
import java.awt.*;
-102-
GRIDBAGLAYOUT MANAGER
GridBagLayout manager organizeaza componentele unui container sub forma
unei grile,cu celule orizontale si verticale,asemanator cu un tabel de
date preformatate.Fiecare celula poate include un singur obiect,dar un
obiect oarecare se poate intinde si pe doua sau mai multe celule.Nu este
obligatoriu ca obiectele sa fie egale intre ele,sau simetrice.Pentru a
reglementa pozitia si caracteristicile fiecarei celule se utilizeaza un
obiect de tip GridBagConstraints in care se vor seta proprietatile:east,
center,fill,gridx,gridy,gridwidth,gridheight,weightx,wieghty...etc.
EXEMPLU:
import javax.swing.*;
import java.awt.*;
GROUPLAYOUT MANAGER
Acest obiect manager este ceva mai complex si permite organizarea unor
seturi mai largi de componente,atat izolat cat si in grupuri fixe.Cu acest
manager,obiectele din interfata vor fi organizate ierarhic si pot fi
grupate cu ajutorul unor obiecte de tip Group.Pentru spatierea obiectelor
se utilizeaza spatii goale (gaps).Aceste spatii,pot fi considerate ca si
cand ar fi niste obiecte distincte,ce trebuiesc intercalate intre obiecte-
le vizibile.
Pentru organizarea componentelor,acest manager utilizeaza cele doua
axe: orizontala si verticala.Pentru fiecare dintre cele doua axe,trebuie
declarata metoda specifica: setHorizontalGroup() si setVerticalGroup().
Fiecare obiect trebuie sa fie inclus in ambele metode,pentru ca managerul
sa poata evalua pozitia sa relativ la axa respectiva.In caz contrar,se va
returna un mesaj de eroare.Cu alte cuvinte,fiecare obiect va fi raportat
atat la cele doua axe,cat si la celelalte obiecte din acelasi grup.In
fiecare dintre cele doua metode,se vor grupa obiectele care urmeaza sa
fie aliniate orizontal,respectiv vertical.Este posibil ca in cele doua
functii,obiectul sa fie inclus in grupuri diferite (dupa cum doriti sa
fie aliniat),dar este esential sa fie declarat in ambele metode.Acest
manager a fost conceput mai ales pentru editoarele cu interfata vizuala,
(cum este NetBeans),dar poate fi utilizat cu succes si in cod direct.
-104-
EXEMPLU:
import javax.swing.*;
import java.awt.*;
import static javax.swing.GroupLayout.Alignment.*;
import java.awt.Component;
public class Design8 {
public static void main(String[] args) {
JLabel label = new JLabel("Open File:");
JTextField textField = new JTextField();
JButton findButton = new JButton("Find");
JButton cancelButton = new JButton("Cancel");
JFrame frame = new JFrame("Exemplu cu GroupLayout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600,500);
GroupLayout layout = new GroupLayout(frame.getContentPane());
frame.getContentPane().setLayout(layout);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
layout.setHorizontalGroup(layout.createSequentialGroup()
.addComponent(label)
.addComponent(textField)
.add(layout.createParallelGroup(LEADING)
.addComponent(findButton)
.addComponent(cancelButton))
);
layout.setVerticalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(LEADING)
.addComponent(label)
.addComponent(textField)
.addComponent(findButton))
.addComponent(cancelButton)
);
frame.setVisible(true);
}
}
-salvati fila cu numele design8.java si compilati
In mod normal,nu are rost sa utilizati acest manager in programarea
manuala,linie cu linie,dar este esential sa intelegeti modul in care se
organizeaza componentele,pentru a putea moderniza sau depana o aplicatie
creata cu NetBeans in care se utilizeaza acest manager.Pornind de la
exemplul de mai sus,incercati sa adaugati progresiv obiecte izolate,apoi
grupuri de obiecte aliniate orizontal sau vertical,pana cand stapiniti
cu siguranta modul de lucru.
In exemplul de mai sus,se pot identifica usor constructorul si cele
doua metode : setHorizontalGroup() si setVerticalGroup().
Pentru construirea obiectelor de tip Group se pot utiliza cele doua
metode : createParallelGroup() si respectiv createSequentialGroup().
Atentie la modul de inchidere a parantezelor,pentru fiecare constructor.
Pentru ca spatiile goale sa fie create automat,este suficient sa setati
metoda setAutoCreateGaps(),daca nu doriti sa creati spatii personalizate
cu o anumita configuratie.
-105-
SPRINGLAYOUT MANAGER
SpringLayout manager,este destinat pentru a permite reconfigurarea unei
interfete grafice,atunci cand este redimensionata.Termenul de spring,vine
de la "elastic" sau "extensibil" si atrage atentia asupra faptului ca
permite o pozitinare mai laxa a obiectelor.Pentru acest scop,acest manager
permite ca obiectele sa fie pozitionate in functie de niste repere fixe
din interfata.Aceste repere pot fi marginile unui alt obiect din container
(obiectele se pozitioneaza unul fata de altul),sau marginile containerului
(obiectele se pozitioneaza fata de container).Ca rezultat,de exemplu,daca
obiectele se pozitioneaza fata de marginea inferioara a ferestrei,atunci
obiectele se vor deplasa impreuna cu marginea inferioara,atunci cand
fereastra este redimensionata.Pentru stabilirea acestor repere,se utili-
zeaza niste obiecte specializate de tip Spring.Fiecare reper este repre-
zentat printr-un astfel de obiect de tip Spring.Fiecare obiect de tip
Spring are proprietati prin care se seteaza dimensiunea minima,maxima sau
dimensiunea preferata.Prin combinarea acestor proprietati se va putea
determina si dimensiunea actuala a obiectului,salvata in proprietatea
"value".Pentru setarea obiectelor de tip String,managerul apeleaza la
metoda specializata denumita putConstrains().
EXEMPLU:
import javax.swing.*;
import java.awt.*;
-106-
Ca rezultat,obiectul textField se va redimensiona o data cu fereastra,
dar va mentine o distanta fixa fata de obiectul JLabel.Daca obiectele se
pozitioneaza doar prin raportare unul fata de altul,nu se vor deplasa
o data cu containerul.
EXEMPLU:
import javax.swing.*;
import java.awt.*;
-107-
MANAGER PERSONALIZAT
In mod normal,obiectele standard ofera o gama destul de larga de
optiuni,pentru a putea satisface majoritatea necesitatilor de programare
curenta.Exista insa situatii in care doriti sa utilizati un obiect per-
sonalizat pentru designul interfetei grafice.De exemplu,daca lucrati in
cadrul unei firme de software si doriti ca toate aplicatiile sa respecte
un anumit tipar.In acest caz,puteti crea o clasa noua de tip Layout Mana-
ger,in care setati toate caracteristicile principale.Un astfel de manager
personalizat ofera urmatoarele avantaje:
-clasa respectiva poate fi utilizata ori de cate ori este cazul
-utilizatorii nu pot modifica aspectul interfetei grafice,dacat daca
oferiti si fila sursa (se protejeaza codul managerului)
-managerul poate contine un set intreg de setari de tip design,nu doar
cele necesare pentru pozitionarea obiectelor
Cea mai simpla metoda pentru a crea un astfel de manager este sa
extindeti una dintre clasele standard,adaugand functionalitati noi.
EXEMPLU:
import java.awt.*;
import java.awt.Color;
-108-
Exista si posibilitatea de a crea un Layout manager complet nou,dar
aceasta solutie este mult mai laborioasa.In acest caz,este esential ca
managerul nou creat sa implementeze o interfata de tip LayoutManager,fie
direct,fie prin descendentul sau,LayoutManager2.In plus,pe langa construc-
tor si destructor,este obligatoriu ca noul mamager sa declare si sa defi-
neasca urmatoarele metode esentiale:
void addLayoutComponent(String,Component)
void removeLayoutComponent(component)
Dimension preferredLayoutSize(Container)
Dimension minimalLayoutSize(Container)
void layoutContainer(Container)
In cazul in care managerul d-voastra implementeaza o interfata de tip
LayoutManager2,se vor adauga si umatoarele cinci metode esentiale:
addLayoutComponent(Component,Object)
getLayoutAlignmentX(Container)
getLayoutAlignement(Container)
invalidateLayout(Container)
maximumLayoutSize(Container)
O etapa esentiala o constitue modul in care eliberati memoria.Din acest
motiv,destructorul va trebui sa fie testat pentru orice fel de situatie
posibila,pentru a elimina orice resturi de date sau thread-uri restante
in "background".Programarea unui astfel de destructor poate fi extrem de
dificila si nu se recomanda incepatorilor.Din acest motiv,este preferabil
sa programati managerul personalizat doar prin extinderea unei clase
standard,in care beneficiati atat de constructor si destructor autorizat,
cat si de metodele esentiale,predefinite.
Notiunile din acest capitol sunt aproape inutile daca lucrati cu
NetBeans,sau cu orice alt editor vizual integrat.Totusi,este esential sa
intelegeti modul in care se organizeaza si se administreaza obiectele
din interfata grafica.Acest sistem de organizare poate fi utilizat si
in alte scopuri.Nu este obligatoriu ca managerul sa administreze doar
obiecte vizuale,ci poate fi utilizat pentru orice alt tip de obiect.De
exemplu,interfata grafica poate contine si o serie intrega de obiecte
ce nu sunt afisate in timpul executiei,dar interactioneaza cu evenimente
din interfata grafica.Cel mai cunoscut exemplu il reprezinta jocurile
pentru calculator,in care se creaza adevarate retele de astfel de obiecte
invizibile,ce asteapta un anumit eveniment.In locul unei astfel de retele
se poate utiliza cu succes un obiect de tip GridLayout..etc.
In plus,este util sa intelegeti modul de evidenta al obiectelor din
interfata,pentru a putea crea aplicatii asemanatoare.De exemplu daca
doriti sa programati un sistem nou de operare,va trebui sa alegeti una
sau mai multe solutii pentru gestionarea unitatilor de lucru,pentru
gestionarea partitiilor de memorie etc.Practic,orice tip de operatie de
gestiune se poate face asemanator cu cea pentru pozitinarea obiectelor.
Din acest motiv,am preferat termenul de "design manager",in locul celui
de "gestionar de pozitie".
Nu se pot face recomandari generale referitoare la design,sau la
gestionarea operatiilor necesare pentru design.Fiecare utilizator va alege
solutia ce corespunde cel mai bine cu experienta sa personala,sau cu
necesitatile aplicatiei proiectate.Totusi,exista o recomandare utila:
-solutia cea mai buna,este intotdeauna solutia cea mai simpla !
-109-
TABELE SI BAZE DE DATE
Mediul Java nu a fost conceput pentru operatii cu baze de date,dar
prin adaugarea unor programe auxiliare,se pot face orice fel de operatii
cu tabele si baze de date,inclusiv in retea.Pentru a face conexiunea cu
un tabel sau o baza de date,este necesar un pachet special de clase si
componente API denumit JDBC (Java Database Connectivity).In plus,este
obligatoriu ca in unitatea de lucru sa fie instalat si un driver special
(driver = program destinat pentru functiile sistemului de operare).Daca
utilizati platforma NetBeans,beneficiati atat de pachetul JDBC,cat si de
driverele necesare (sunt instalate automat).
Cea mai simpla operatie posibila este cea prin care se creaza si apoi
se afiseaza un tabel.Pentru aceste operatii simple,nu este ncesar nici un
program auxiliar,ci se poate utiliza doar clasa JTable din javax.swing.
In locul bazei de date,se va crea o clasa ce extinde o interfata de tip
TableModel,apoi se va construi obiectul JTable pe baza acestui sablon.
EXEMPLU:
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
-110-
EXEMPLU:
import javax.swing.table.*;
import javax.swing.event.*;
class ModelTabel implements TableModel {
private final Object[][] GRADES = {
{"Albu","Mihai",8,7,5,9},
{"Barbu","Emil",9,6,8,7},
{"Ionescu","Ion",6,5,8,9},
{"Popescu","Daniela",5,6,5,8},
{"Stanescu","Ronald",8,5,5,7}, };
public void setValueAt(Object aValue,int rowIndex,int columnIndex){}
public void addTableModeListener(TableModeListener l) {}
public void removeTableModeListener(TableModeListener l) {}
public boolean isCellEditable(int rowIndex,int columnIndex) {
return false; }
public Object getValueAt(int row,int col) {
switch(col) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5: return GRADES[row][col];
case 6:
case 7: int fin = 0;
fin += (Integer) GRADES [row][2];
fin += (Integer) GRADES [row][3];
fin += (Integer) GRADES [row][4];
fin += (Integer) GRADES [row][5];
fin = Math.round((fin / 4.0f));
if (col == 6 ) { return fin;
} else { return fin > 7; }
} throw new AssertionError("invalid column"); }
public Class<?> getColumnClass(int col) {
switch(col) {
case 0:
case 1: return String.class;
case 2:
case 3:
case 4:
case 5:
case 6: return Integer.class;
case 7: return Boolean.class;
} throw new AssertionError("invalid column"); }
public String getColumnName(int col) { return "text: " ; }
public int getRowCount(){ return GRADES.length; }
public int getColumnCount(){ return 8; }
}
-salvati fila cu numele ModelTabel si compilati,apoi compilati si
fila precedenta si executati exercitiul.
Eventual,incercati sa dezvoltati tabelul cu noi randuri si coloane.
-111-
Acest gen de solutie prezinta urmatoarele avantaje:
-nu necesita nici un driver sau pachet suplimentar
-baza de date este inlocuita de o arie bidimensionala
-se elimina operatiile necesare pentru realizarea conexiunii
-modulul poate fi si monobloc,cu executie mult mai rapida
-pentru actualizarea datelor se va actualiza doar clasa Model Tabel
Principalele dezavantaje sunt:
-este o solutie foarte laborioasa,mai ales atunci cand se lucreaza cu
volume mari de date
-conectarea datelor din tabel la componentele din interfata grafica este
dificila si laborioasa
-este dificil de lucrat cu date arhivate in retea (site-uri de Internet)
-este dificil de organizat si sistematizat atunci cand se lucreaza cu un
numar mare de tabele
-comenzile de tip SQL sunt mult mai greu de implementat decat atunci cand
se lucreaza cu editoare vizuale
-nu exista un control vizual al operatiilor efectuate,motiv pentru care
operatiile de design si/sau depanare sunt mult mai greoaie
In sinteza,acest gen de solutie directa,este recomandabil doar atunci
cand se lucreaza cu volume mici de date,ce urmeaza sa fie prezentate cu
o structura fixa,fara operatii de actualizare sau reorganizare a datelor.
Pentru orice gen de aplicatie complexa,se recomanda sa utilizati exclusiv
editoare moderne,cu interfata grafica vizuala,gen NetBeans.Teoretic este
posibil sa realizati toate operatiile necesare si prin comenzi editate
manual,dar acest gen de solutie implica un volum nerezonabil de munca si
se preteaza la numeroase erori de conceptie,sau editare.
In principiu,programele ce opereaza cu baze de date si tabele de date
preformatate se pot realiza doar in urmatoarele conditii:
-trebuie sa existe cel putin un driver specializat,adica un program ce
faciliteaza operatiile de tip I/O (citirea sau scrierea bazelor de date)
-trebuie sa existe un program specializat cu care se editeaza bazele de
date si tabelele
-trebuie sa existe o solutie pentru organizarea tabelelor (file de index-
are,tabele de adevar,obiect de tip manager etc.)
-trebuie sa existe o solutie pentru a crea o conexiune intre baza de date
si aplicatia utilizatorului
-trebuie sa existe o compatibilitate perfecta intre tabelul utilizat si
comenzile de tip SQL prin care se solicita citirea sau reorganizarea
datelor
-trebuie sa existe un obiect compatibil,in care sa poata fi afisate datele
preluate
Programele denumite "driver" au un rol esential,deoarece determina
limitarile "fizice" ale sistemului de operare.Aceste drivere se pot
descarca din reteaua Internet si pot fi clasificate in urmatoarele grupe
mari:
-Tipul 1 -sunt cele care depind de o anumita biblioteca,ce trebuie sa
fie preinstalata in unitatea de lucru.Programele sunt functionale,dar pot
aparea o serie intrega de incompatibilitati atunci cand sunt transferate
pe alta unitate de lucru (portabilitatea este mai redusa).
-Tipul 2 -sunt specifice pentru un anumit format al bazei de date si
sunt dependente de o biblioteca specializata pentru acel tip de date.
-112-
-Tipul 3 -sunt cele care realizeaza conexiunea la baza de date,exclusiv
prin intermediul unui server,utilizand un protocol independent de tipul
de baza de date.
-Tipul 4 -sunt cele care utilizeaza cate un protocol specific pentru
fiecare tip de baza de date si realizeaza conexiunea direct cu baza de
date respectiva.
Exista un numar mare de programe ce permit editarea bazelor de date.
Dintre acestea,cele mai frecvent utilizate sunt FoxPro,Visual Basic,Oracle
dBase sau NetBeans.Fiecare program va genera un anumit tip de baze de date
cu o extensie specifica: .dbf,.mdb etc.Programele driver asigura compati-
bilitatea dintre aceste tipuri diferite de baza de date.Fiecare driver
este insotit de o documentatie tehnica.Este esential sa cititi cu atentie
aceasta documentatie inainte de a alege driver-ul dorit,pentru a va asi-
gura ca va fi compatibil cu datele pe care doriti sa le exploatati.In
principiu,nu trebuie sa alegeti driver-ul cel mai "la moda",ci este bine
sa alegeti cel care este competibil cu programul editor cu care sunteti
familiarizati.Exemplu : daca detineti tabele in format .dbf,este uneori
indicat sa utilizati un driver de tip 1 sau 2,chiar daca sunt mai vechi
si nu mai sunt "actuale".
Daca nu aveti nici un fel de experienta anterioara si nu detineti nici
un fel de baze de date,este recomandabil sa utilizati exclusiv platforma
NetBeans.Platforma NetBeans,nu numai ca instaleaza automat cinci programe
driver,ce acopera majoritatea necesitatilor posibile,dar permite si edi-
tarea tabelelor si bazelor de date,comod si simplu.
EXEMPLU:
-deschideti platforma NetBeans
-deschideti fereasta Services si extindeti nodul Databases
-selectati nodul Java DB si executati un click drept
-alegeti optiunea CreateDatabase
-completati fereastra de dialog : nume,user si parola
-confirmati cu OK
-deschideti din nou fereastra Services si selectati baza de date nou
creata,apoi executati un click drept si alegeti optiunea Connect...
-extindeti nodul (apasand pe semnul +) si selectati folderul Tables
-executati un click drept pe Tables si alegeti Create Table
-completati numele tabelului,apoi introduceti datele
-pentru fiecare coloana,alegeti tipul de data si dimensiunea
-este esential sa existe o cheie de indexare a tabelului (cel putin
una dintre coloane va avea selectata si optiunea key)
-daca doriti sa indexati tabelul in functie de o anumita coloana
selectati si optiunea Index (nu are rost sa indexati tabelul decat
daca urmeaza sa executati operatii de actualizare si sortare)
-in final,confirmati cu OK
-extindeti nodul Tables pentru a vedea tabelul nou creat
-selectati tabelul,executati un click drept de mouse si alegeti
optiunea View Data
-tabelul va fi afisat in planseta de design
-pentru a introduce date,executati un click drept in tabel si apoi
alegeti optiunea Insert Record
-completati caseta de dialog si confirmati cu OK
-daca doriti,puteti reedita comanda SQL (din caseta suprajacenta)
-113-
In exemplul precedent,s-a creat o baza de date si un tabel indexat.In
continuare,puteti adauga alte tabele,sau puteti testa diferite formule
de prezentare a datelor si diverse comenzi SQL.
De exemplu,pentru a crea o selectie (view) selectati folderul View,apoi
executati un click drept si selectati optiunea Create View.Alegeti un
nume si editati comanda sql dorita.
EXEMPLU:
select * FROM (numele tabelului) ...WHERE ...conditia dorita
Daca doriti sa previzualizati,expandati nodul Views,selectati nodul
dorit,executati un click drept si alegeti optiunea View Data
In continuare,baza de date,tabelele si selectiile dorite pot fi utili-
zate in aplicatii.Cea mai simpla forma de implementare a lor,este prin
utilizarea unui proiect de tip Java Desktop Application.
EXEMPLU:
-din meniul File alegeti New Project si apoi Java Desktop Application
-apasati butonul Next
-in caseta Choose Application Shell alegeti Database Application
-apasati butonul Next
-in caseta Database Connection selectati baza de date dorita
-daca exista mai multe tabele,alegeti tabelul dorit
-apasati butonul Finish
Aplicatia generata va contine o bara de meniuri cu meniurile File si
Help,un obiect de tip JTable gata conectat la tabelul ales,un set de
obiecte de tip TextArea conectate la fiecare coloana din tabel (pentru
actualizarea datelor) si un set de butoane functionale,gata setate pentru
principalele operatii posibile :
New - adauga o inregistrare noua
Delete -sterge o inregistrare
Refresh - actualizeaza datele
Save -salveaza modificarile efectuate
Acest schelet minimal,este suficient pentru majoritatea situatiilor.
Daca doriti sa executati operatii mai complexe,adaugati componentele
dorite si apoi implementati algoritmii doriti.Pentru a crea o conexiune
intre un component Swing si tabel,este suficient sa selectati obiectul,
apoi sa executati un click drept si sa alegeti optiunea Bind,iar apoi sa
selectati tabelul sursa (binding Source) si respectiv coloana dorita
(binding Expression).Operatiile se vor putea controla cu butoane simple
si evenimente mouseClicked sau actionPerformed,keyPresed ...etc.
Daca doriti pur si simplu sa afisati un tabel,sau o anumita selectie
dintre cele realizate anterior (altul decat cel conectat implicit),este
suficient sa deschideti fereastra Services,sa selectati nodul dorit si
apoi sa trageti (cu butonul mouse apasat) nodul dorit,in fereastra apli-
catiei.
Daca doriti sa adaugati un driver oarecare,deschideti fereastra Servi-
ces,selectati Drivers,executati un click drept si alegeti New Driver.
Expandati nodul Drivers pentru a putea observa driverele instalate.Daca
doriti sa stabiliti o conexiune,utilizand un anumit driver,selectati
driver-ul respectiv,executati un click drept si alegeti Conect Using...
Este necesar sa utilizati un driver special,atunci cand doriti sa va
conectati la un anumit tip de baza de date (de exemplu de tip FoxPro).
Pentru operatiile uzuale se pot utiliza metodele obiectului JTable.
-114-
Pentru aplicatii elementare,nu este necezsar sa adaugati nici un fel
de operatii suplimentare.Pur si simplu,selectati butonul Clean and Build
Main Project si construiti proiectul,apoi executati cu Run Main Project.
Pentru exercitiu,adaugati noi inregistrati folosind butonul New si
apoi salvati datele cu Save.Daca apasati butonul Refresh,se vor reseta
datele initiale.Pentru ca datele introduse sa ramana definitive,apsati
Save si inchideti aplicatia.
Daca sunteti incepatori,este recomandabil sa nu incercati sa dezvol-
tati operatii noi,sau sa utilizati doar obiecte standard conectate la
tabel cu Bind si seturi de operatii elementare.
Daca sunteti avansati,puteti dezvolta aplicatia progresiv.Cea mai
buna solutie este sa utilizati aceasi tip de solutie ca si cel oferit
de wizard.Pentru acest scop,deschideti fereastra DesktopApplicationView
si alegeti optiunea Source (in loc de Design).Studiati metodele utilizate
pentru newRecord(),deleteRecord(),SaveTask()...etc.Este recomandabil ca
metodele nou implementate sa apeleze componentele interfetei in mod
asemanator.
Exista insa si o solutie mult mai simpla.Pentru operatii elementare,
este mult mai usor sa utilizati componente swing simple,conectate la
tabelul dorit (cu bind).Executati operatiile necesare in aceste compo-
nente si apoi salvati rezultatul cu butonul Save implicit.
EXEMPLU:
-daca tabelul conectat contine o coloana denumita VALOARE,in care sunt
arhivate date de tip Double.
-presupunem ca doriti sa adaugati un procent de 18 % la o parte dintre
valorile din aceasta coloana (un impozit)
-coloana va fi conectata in interfata grafica la un obiect JTextField,
denumit valoareField.
-pentru a executa operatia automat,adaugati un buton simplu.Click drept
pe buton si alegeti Events -> Action -> ActionPerformed
-editati urmatoarea metoda
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
java.lang.Double xxx = 0.00;
xxx = java.lang.Double.valueOf(valoareField.getText()) +
java.lang.Double.valueOf(valoareField.getText()) * 18/100;
valoareField.setText(java.lang.String.valueOf(xxx);
}
Acest gen de solutie este extrem de simpla si nu implica utilizarea
unor alte obiecte.Conexiunea cu tabelul este gata realizata si nu sunt
necesare comenzi de tip SQL,sau tranzactii de date.Combinand mai multe
obiecte vizuale,se pot obtine operatii destul de complexe.
Pentru operatii mai complexe insa,trebuie sa utilizati pachetul
javax.persistence si obiecte de tip entityManager (la fel ca si in
metodele implicite).
Un alt tip de abordare posibila,este prin utilizarea pachetului
java.sql si a obiectelor de tip Statement si preparedStatement.
Nu exista o solutie unica.Fiecare programator va alege metoda prefe-
rata,in functie de experienta sa anterioara,sau in functie de necesita-
tile de moment.In orice caz,pentru incepatori este recomandabil sa nu
utilizeze decat metodele implicite si combinatii de operatii simple,
realizate cu ajutorul componentelor swing.
-115-
CLASE GENERICE SI COLECTII
-116-
Mai mult decat atat,tipul generic poate fi utilizat atat pentru decla-
rarea constructorului clasei,cat si pentru unele dintre metodele sale.Ca
rezultat,metoda respectiva va putea accepta tipuri diferite de data.
EXEMPLU:
public class Box<T> {
private T t;
public void sdd(T,t) { this.t = t; }
public T get() { return t; }
public <U> void inspect(U u) {
System.out.println("T: " + t.getClass().getName());
System.out.println("U: " + u.getClass().getName());
}
public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
integerBox.add(new Integer(10));
integerBox.inspect("some text");
Box<Double> doubleBox = new Box<Double>();
doubleBox.add( new Double(3.1415));
doubleBox.inspect(2.71);
}
}
-salvati fila cu numele Box.java si compilati.Executati fila.
In exemplul de mai sus,se declara o clasa de tip generic,iar in functia
main se apeleaza constructorul,recursiv,pentru a crea doua obiecte,dintre
care primul va fi de tip Integer,iar cel de al doilea va fi de tip Double.
Apoi se utilizeaza metodele fiecarui obiect,pentru a putea evalua rezulta-
tul returnat,in contextul fiecarui tip de data.
Trebuie remarcat faptul ca in obiectele derivate dintr-o clasa generica
parametrul precizat la declarare poate fi orice tip de data,orice varia-
bila sau obiect,cu exceptia tipurilor primare (Exemplu: nu se pot utiliza
valori numerice directe).
In exemplul de mai sus,daca incercati o formula de genul"
integerBox.add(10) in loc de integerBox.add(new Integer(10)),se va
returna un mesaj de eroare in momentul compilarii.Cu alte cuvinte,para-
metrul precizat in momentul declaratiei trebuie sa fie un obiect.In cazul
variabilelor se construieste un obiect din tipul respectiv (colectia
trebuie sa contina obiecte,nu poate grupa valori directe).
Se observa cu usurinta avantajele claselor generice.Nu numai ca se
pot adapta la orice tip de data,dar pot decela inca din etapa de compilare
erorile de format,ce nu sunt depistate in mod curent decat in etapa
de executie.De exemplu,daca utilizati o clasa obisnuita,compilatorul nu
va putea evalua ca eroare,tentativa de a adauga in colectie un numar
direct.
In exemplul de mai sus,clasa generica este exploatata direct in functia
main(),dar in mod normal aceste clase se declara si se compileaza separat,
pentru a putea fi apelate si utilizate din cat mai multe aplicatii.
Nu este obligatoriu ca o clasa generica sa accepte orice tip de data.
Pentru a restrange valorile acceptate doar la un anumit subgrup de tipuri
de data,se poate utiliza un parametru limitat (bounded parameter),declarat
prin extends.
EXEMPLU: public <U extends Number> void inspect(U u) { ....
-117-
In acest caz,obiectele derivate din acest tip generic nu vor putea
avea ca parametru decat unul dintre tipurile de data derivate din Number,
adica: AtomicInteger,AtomicLong,BigDecimal,BigInteger,Byte,Double,Float,
Integer,Long,Short.Tentativa de a apela metoda cu un parametru de tip
Strig (de exemplu),va returna un mesaj de eroare.
Cu alte cuvinte,cu cat tipul declarat se afla in fruntea unei ierarhii
mai mari de subtipuri derivate,cu atat clasa generica respectiva,si meto-
dele sale,vor putea accepta mai multe tipuri de date(respectiv toate sub-
tipurile).Forma cea mai generala de clasa generica utilizeaza tipul Object
deoarece toate tipurile din java au acest ancestor comun.
Pentru declararea unui tip,se pot utiliza si caracterele cu sens gene-
ral (wildcards),cum sunt ?,*...etc.In acest caz,caracterul respectiv va
tine locul unei litere,oricarui caracter alfa-numeric...etc.
EXEMPLU: Box<? extends Number> box = .....
inseamna ca tipul clasei este necunoscut,dar ca este un subtip al
tipului Number,eventual chiar tipul Number.
Box<*> box =.....
inseamna ca tipul este complet necunoscut si urmeaza sa fie specificat
in momentul apelarii constructorului.
Dupa ce se creaza o instanta a unei clase generice,compilatorul face
o serie de conversii interne pentru a premite compatibilitatea cu diverse
alte structuri din program.Ca rezultat,nu se poate utiliza tipul clasei
generice pentru a crea obiecte sau instante ale unei interfete,deoarece
compilatorul a stres din memorie informatia despre tipul generic (Type
Erasure).Mai exact,a inlocuit informatia despre tipul de data cu o struc-
tura noua,ce nu poate fi apelata in timpul executiei.Din acest motiv,orice
tentativa de a apela tipul generic va returna un mesaj de eroare.
EXEMPLE: public class MyClass<E> {
public static void myMethod(Object item) {
if (item instanceof E) { -returneaza eroare !
.....
E item2 = new E(); -returneaza eroare !
E[] iArray = new E[10]; -returneaza eroare !
E obj = (E)new Object(); -returneaza eroare !
....etc.
Tipurile generice reprezinta piatra de temelie pentru structurile mai
complexe de tip colectie.Pentru o mai buna intelegere a acestor notiuni,
este utila si putina istorie.In epoca de pionierat a tehnicii de calcul,
toate datele se arhivau sub forma de stiva.Pentru ca sa poata fi incluse
in stive,datele trebuiau sa fie formatate intr-un anumit fel (de obicei
binar in format de 4,8,16,32 de biti etc.).Ca rezultat,pentru fiecare tip
de data,trebuia sa existe un anumit tip de stiva.La fel se facea si orga-
nizarea interna a datelor.Memoria de operare,era fragmentata in numeroase
astfel de stive (tampoane de memorie) in care erau arhivate datele tempo-
rare.Colectiile,nu sunt decat niste structuri asemanatoare,menite sa arhi-
veze datele temporar,dar spre deosebire de stive,au o structura interna
elastica,ce se "muleaza" pe tipul de data utilizat.Astfel,in loc sa existe
cate un tampon de memorie specializat pentru fiecare tip de data,cu aju-
torul tipurilor generice se pot crea tampoane de memorie capabile sa arhi-
veze orice tip de data (inclusiv obiecte mari sau interfete grafice).Se
face astfel economie de memorie si se reduce numarul de operatii/proces.
-118-
Colectiile sunt de fapt obiecte de tip container,proiectate sa poata
grupa si organiza mai multe structuri de date,intr-o singura unitate.Se
utilizeaza pentru a arhiva datele temporar,dar si pentru a executa o serie
intreaga de operatii de sortare si filtrare,sau agregare,etc.In mod tipic
se utilizeaza pentru a grupa date de acelasi fel,sau tipuri compatibile,
astfel incat sa se poata opera asupra lor prin metode globale.Sub o forma
sau alta,structurile de tip colectie exista in toate limbajele de progra-
mare (stive,arii de date,vectori,tabele de pointeri etc.),dar limbajul
Java implementeaza un numar foarte mare de astfel de structuri,cu functio-
nalitati complexe,menite sa simplifice orice tip de operatii posibile.In
Java,toate aceste structuri formeaza o arhitectura denumita "retea de
colectii" (collections frameworks),in care sunt incluse o serie intreaga
de interfete,clasele in care sunt implementate aceste interfete,precum si
o serie intreaga de algoritmi specifici.Interfetele sunt tipul abstract,
adica sablonul initial utilizat pentru a crea structurile necesare.Imple-
mentarile,sunt clasele in care au fost dezvoltate aceste interfete,iar
algoritmii sunt metodele prin care se executa operatiile de nivel inferior
(sortare,filtrare,agregare,eradicare etc.).
Colectiile nu numai ca simplifica munca de programare,dar au si rolul
de a compartimenta structurile din program in module distincte.Ca rezultat
orice eroare de executie sau operatie defectiva,va fi limitata la un anu-
mit modul,fara sa afecteze restul programului.Programele vor fi mult mai
usor de conceput (se programeaza fiecare modul separat) si de depanat.In
plus,structurile de tip colectie pot asigura compatibilitatea cu structuri
de date mai vechi (inclusiv cu stivele binare),pentru a permite utilizarea
unor arhive de date mai vechi,fara reformatarea datelor.
Pentru a facilita compatibilitatea dintre structuri,colectiile sunt
organizate si dezvoltate ierarhic,la fel ca si restul obiectelor din Java.
In capul listei,ancestorul absolut il reprezinta interfata Collection<E>
si respectiv obiectele ce implementeaza aceasta interfata: AbstractCollec-
tion,AbstractList,AbstractQueue,AbstractSequentialList,AbstractSet,Array-
BlockingQueue,ArrayDeque,ArrayList,AttributeList,BeanContextServices-
Support,ConcurrentLinkedQueue,ConcurentSkipListSet,CopyOnWriteArrayList,
CopyOnWriteArraySet,DelayQueue,EnumSet,HashSet,JobStateReasons,LinkedBlo-
ckingDeque,LinkedBlockingQueue,LinkedHashSet,LinkedList,PriorityBlocking-
Queue,PriorityQueue,RoleList,RoleUnresolvedList,Stack,SynchronousQueue,
TrySet,Vector.
Din interfata Collection au fost derivate urmatoarele sub-interfete:
BeanContext,BeanContextServices,BlockingQueue<E>,BlockingDeque<E>,Deque<E>
List<E>,NavigableSet<E>,Queue<E>,Set<E>,SortedSet<E>.
Fiecare dintre clasele de mai sus,implementeaza una sau mai multe
dintre aceste interfete,simultan.Ca rezultat,obiectele derivate din aceste
clase vor putea mosteni functionalitatea acestor interfete.In functie de
interfetele implementate,exista o serie de deosebiri de la un obiect la
altul: unele nu accepta decat obiecte unice,in timp ce altele accepta si
clone identice,unele nu permit operatii de sortare in timp ce altele sunt
concepute special pentru acest scop...etc.Nu se poate face o descriere
exhaustiva a tuturor acestor clase.Este insa esential sa intelegeti modul
in care sunt organizate,pentru a putea naviga cu usurinta prin documenta-
tia tehnica (vezi manualul Help).In mod curent,nu se utilizeaza decat
clasele in care interfetele au fost deja implementate.
-119-
Dintre interfetele de tip Collection,cele mai frecvent utilizate sunt:
Set si SortedSet,List si Queue.
Set este un tip de colectie ce nu accepta elemente duplicate si maxim
un singur element nul.Pe langa metodele mostenite de la Collection contine
si metode proprii pentru a introduce si extrage elemente,pentru a verifica
prezenta unui element oarecare si pentru a numara elementele continute.
Doua colectii de tip Set sunt egale,daca contin exact aceleasi elemente.
Se pot utiliza pentru a copia date de la o adresa la alta.Pentru simplifi-
carea implementarii,este recomandabil sa utilizati una dintre clasele ce
implementeaza aceasta interfata: EnumSet,HashSet,TreeSet...etc.
EXEMPLU:
import java.util.*;
public class Set1 {
public static void main(String[] args) {
Set<Integer> s = new HashSet<Integer>();
for (Integer x=0;x<20;x++)
s.add(new Integer(x));
System.out.println(s);
}
}
-salvati fila cu numele Set1.java.Compilati si executati.
In exemplul se mai sus,se construieste colectia,se introduc elemente,
apoi se afiseaza continutul colectiei.
Pentru a verifica daca un anumit element este continut in colectie se
poate utiliza un algoritm de genul:
EXEMPLU:
import java.util.*;
public class Cauta {
public static void main(String[] args) {
Set<Integer> s = new HashSet<Integer>();
for (Integer x=0;x<20;x++);
s.add(new Integer(x));
System.out.println(s.contains(new Integer(7)));
}
}
-salvati cu numele Cauta.java,compilati si executati exercitiul
Pentru a verifica daca un element oarecare este duplicat,se poate
exploata si parameterul String[] din functia main().
EXEMPLU:
import java.util.*;
public class Duplicate {
public static void main(String[] args) {
Set<String> s = new HashSet<String>();
for (String a : args)
if (!s.add(a))
System.out.println("Cuvantul: " + a + " este preexistent !");
System.out.println(s.size()+" cuvinte distincte: " + s);
}
}
-slavati fila cu numele Duplicate.java si compliati fila.
Executati fila cu comanda: java Duplicate am venit am vazut am invins
-120-
Exemplul de mai sus,speculeaza faptul ca Set nu accepta elemente iden-
tice si utilizeaza parametrul de tip String al functiei main(),pentru a
accepta valorile ce urmeaza sa fie incluse in colectie.Repetati exemplul
pentru orice propozitie sau grup de caractere,redundante sau unice.
Intr-o colectie,metodele pot fi subimpartite in trei grupe:
-operatii de baza cum sunt: isEmpty(),contains(),add() si remove()
-operatii brute (bulk): containsAll(),addAll(),removeAll(),retainAll()
-operatii asupra ariilor: toArray()
Operatiile de baza sunt cele obligatorii pentru orice colectie.Cele
brute sunt cele ce actioneaza global asupra tuturor elementelor din co-
lectie,iar cele de tip arie permit ca elementele unei colectii sa poata
fi transferate intr-o variabila de tip arie(pentru a asigura compatibi-
litatea cu datele editate in format mai vechi).
Pentru a putea executa operatii asupra unui element oarecare dintr-o
colectie,este important modul in care se navigheaza in interiorul unei
colectii.Nu exista o solutie unica,ci exista grupuri de solutii posibile.
Cea mai simpla solutie,utilizeaza o bucla de tip For...sau For...each.
EXEMPLU:
import java.util.*;
public class Set2 {
public static void main(String[] args) {
Set<Integer> s = new HashSet<Integer>();
for (Integer x=0;x<20;x++);
s.add(new Integer(x));
for (Object o: s)
System.out.println(o);
}
}
-salvati fila cu numele Set2.java.Compilati si executati.
Prima bucla FOR introduce datele in colectie,iar cea de a doua citeste
colectia element cu element.
Exista si o interfata specializata pentru navigarea prin colectii,cu
numele de Iterator<E>.Pentru comoditate,se poate utiliza una dintre cla-
sele ce implementeaza aceasta interfata,cum este de exemplu,clasa Scanner.
EXEMPLU:
import java.util.*;
public class Scaner {
public static void main(String[] args) {
String sir1 = "unu,doi,trei,patru,cinci,sase,sapte";
Scanner sc = new Scanner(sir1);
for (Integer x=0;x<6;x++)
System.out.println(sc.next());
}
}
-salvati fila cu numele Scaner.java.compilati si executati.
Interfata de tip Iterator contine metodele hasNext(),next() si remove()
iar clasa Scanner adauga si un set complet de metode pentru navigatie.In
exemplul de mai sus,am utilizat doar metoda next(),dar obiectul contine
un set intreg de metode ce permit solutii estrem de personalizate sau
discriminative de navigare in colectie.Este esential insa sa alegeti
metodele cele mai potrivite pentru tipul de data din colectie.
-121-
import java.util.*;
-124-
EXEMPLU:
import java.util.*;
-125-
O alta interfata derivata din Collection<E>,este interfata List<E>.Nu
trebuie confundata cu clasa List din pachetul AWT,sau cu clasa JList din
pachetul Swing,chiar daca implementeaza metode destul de asemanatoare.
Interfata List<E> se utilizeaza pentru a implementa obiecte de tip
colectie in care elementele sunt arhivate intr-o lista ordonata.Fiecare
element este insotit de un numar de indexare,astfel incat sa poata fi
manipulat cu ajutorul acestui numar de indexare.Programatorul poate con-
trola cu precizie locatia la care va fi arhivat fiecare element din lista,
sau poate apela orice element,in functie de pozitia sa.Spre deosebire de
seturi,listele pot contine si elemente perfect identice (identificate doar
prin numarul de indexare).Deasemenea,listele pot contine un numar nelimi-
tat de elemente nule.Pentru navigare,interfata List ofera si un iterator
specializat,denumit ListIterator.
Exista mai multe clase ce implementeaza interfata List.Dintre acestea
cel mai comod se lucreaza cu clasa Vector.
EXEMPLU:
import java.util.*;
-126-
Asemanatoare cu Vector este si clasa ArrayList,care include cam ace-
leasi metode,dar nu este sincronizata (fenomenul de sincronizare are
importanta doar atunci cand obiectul primeste informatii simultane de la
mai multe thread-uri ce sunt procesate paralel).
Obiectele de acest gen,se pot utiliza pentru a arhiva temporar grupuri
mai mici sau mai mari de date.De exemplu,in procesul de depanare a unei
aplicatii,poate fi util sa cititi o fila de tip text,rand cu rand,pana
cand identificati sediul erorii.
EXEMPLU:
import java.util.*;
import java.io.*;
-127-
O lista poate fi generata si cu ajutorul unei arii de date.De exemplu,
metoda asList() din clasa Arrays,genereaza o interfata de tip List<E>,in
care include automat elementele din aria specificata ca parametru.
EXEMPLU:
import java.util.*;
-128-
Toate colectiile prezentate pana acum,au ca ancestor interfata Collec-
tion.Exista si un al doilea tip de colectii,denumite Map (map = a impere-
chea).Aceste colectii,au ca ancestor primordial Interfata Map si accepta
cate doi parametri interconectati intre ei.Primul parametru poarta numele
de cheie (Key) iar cel de al doilea poarta numele de valoare (Value).
Pentru fiecare cheie poate exista o singura valoare.Pot exista insa
mai multe chei,cu aceeasi valoare.Fiecare dintre cei doi parametrii este
de tip generic,astfel ca poate accepta orice tip de data.Ca rezultat,se
poate face conexiunea dintre orice tip de obiect,cu orice tip de data.
Din Map<K,V> au fost dezvoltate urmatoarele interfete specializate:
Bindings,ConcurrentMap<K,V>,ConcurrentNavigableMap<K,V>,LogicalMessage-
Context,MessageContext,NavigableMap<K,V>,SOAPMessageContext,SortedMap<K,V>
si urmatoarele clase,in care se implementeaza una sau mai multe dintre
interfete:AbstractMap,Attributes,AuthProvider,ConcurrentHashMap,Concurrent
SkipListMap,EnumMap,HashMap,HashTable,IdentityHashMap,LinkedHashMap,Prin-
terStateReasons,Properties,Provider,RenderingHints,SimpleBindings,Tabular-
DataSupport,TreeMap,UIDefaults,WeakHashMap.
Dintre acestea,cele mai uzitate sunt:HashMap,TreeMap si LinkedHashMap.
EXEMPLU:
import java.util.*;
-129-
Daca doriti ca elementele din colectie sa fie sortate automat,inlocuiti
clasa HashMap prin TreeMap.Elementele vor fi sortate in functie de crite-
riul lor natural.Daca doriti sa utilizati un alt criteriu pentru sortarea
datelor,va trebui sa definiti un Comparator.Este insa mult mai simplu sa
alegeti pentru cheie tipul de data cel mai convenabil.De exmplu,pentru
a organiza un set de date in ordine numerica,puteti utiliza pe post de
cheie o valoare de tip Integer.
EXEMPLU:
import java.util.*;
-130-
GRAFICA 2D
Mediul Java permite toate operatiile grafice posibile: desen,text,
preluare de imagini,filtrarea imaginilor,proiectii,imprimare etc.Pentru
a simplifica munca programatorului,Java include toate operatiile necesare
pentru identificarea dispozitivului grafic in clase speciale.Aceste clase
se gasesc in pachetul Abstract Windowing Toolkit(AWT),cu numele de:
Graphics si Graphics2D.
In general,grafica computerizata este ingreunata de urmatorul aspect:
fiecare dispozitiv grafic (imprimanta,monitor,scanner,aparat foto digital)
are un sistem diferit de reprezentare a imaginilor.Unele au un numar mai
mic sau mai mare de pixeli/cm patrat,altele organizeaza pixelii sub forma
de matrice,sau arii de date etc.Ca rezultat,atunci cand se vorbeste despre
spatiul grafic,se disting doua notiuni:
-spatiul clientului (user space) -este spatiul grafic utilizat de clientul
respectiv (este un spatiu logic format din coordonate logice)
-spatiul de dispozitiv(device space) -este spatiul grafic utilizat de dis-
pozitivul grafic (este spatiul real format din coordonate reale)
Nu intotdeauna exista o corespondenta perfecta intre modul in care
a fost programata o imagine si modul in care este reprezentata in dispo-
zitivul grafic periferic.Exemplu : imprimantele utilizeaza alt sistem de
organizare al pixelilor,decat monitoarele.Pentru ca imaginile sa poata fi
reprezentate,este necesar sa existe niste programe speciale in care se
face conversia datelor de la un format la altul.Aceste programe,poarta
numele de drivere si trebuie sa fie preinstalate(driver pentru imprimanta,
driver pentru camera foto,driver pentru monitor,etc.).
In majoritatea programelor anterioare,pentru a putea incepe o proiectie
grafica,era necesar sa fie precizat contextul de dispozitiv grafic.In Java
toate aceste operatii se executa automat.Clasele grafice identifica auto-
mat contextul de dispozitiv grafic corespunzator cu fereastra activa.Nu
mai este necesara nici o operatie pregatitoare.Este totusi necesar ca
proiectiile grafice sa fie facute intr-un spatiu compatibil : o fereastra
sau un alt obiect specializat.
In rest,grafica din Java este asemanatoare cu cea din orice alt limbaj
de programare.Trebuie remarcat insa faptul ca Java ofera un numar foarte
mare de interfete si clase specializate,astfel incat munca programatorului
se rezuma doar la alegerea clasei si a metodei dorite.
Nu exista o compatibilitate perfecta cu grafica din alte limbaje de
programare,decat in cazul componentelor de tip ActiveX,dar exista o
compatibilitate completa cu orice tip de resursa standardizata: file de
tip .bmp,.gif,.jpeg...etc.
Programatorul,utilizeaza spatiul de tip client (user space),pentru a
specifica operatiile dorite,cu ajutorul unui sistem de coordonate logice.
Acest sistem de coordonate,este independent de cel utilizat de fiecare
dispozitiv grafic.Pentru a realiza compatibilitatea cu orice tip de dis-
pozitiv,coordonalele logice,urmeaza sa fie proiectate in momentul execu-
tiei in spatiul de dispozitiv,pentru a forma coordonale reale,adica cele
utilizate efectiv.Din acest motiv,imaginea creata de programator va avea
un aspect diferit in functie de dispozitivul grafic utilizat pentru pre-
zentare (in functie de rezolutie,numar de culori si nuante,etc.).Pentru
a controla modul in care se face aceasta proiectie,Java utilizeaza o serie
de atribute: pen,fill,compositing,transform,clip,font etc.
-131-
Pentru reprezentarea imaginilor,trebuie utilizat un obiect de tip Gra-
phics,sau extensia sa Graphics2D.Obiectul Graphics ofera urmatoarele fa-
cilitati:
-detecteaza automat dispozitivul grafic
-are proprietati specifice pentru informatiile absolut necesare: obiectul
pe care se deseneaza,punctul de origine al translatiilor,culoarea curenta,
fontul,operatiile curente "pixel cu pixel" (gen XOR),daca exista operatii
de decupare (tip Clip),etc.
Clasa Graphics2D,este o extensie perfectionata ce permite un control
mult mai sofisticat al operatiilor de transformare a coordonatelor logice
in coordonate reale.Ori de cate ori este posibil,este preferabil sa utili-
zati aceasta clasa,atunci cand sunt necesare proiectii si operatii mai
complexe.Pentru reprezentarile foarte simple,este recomandabila clasa
Graphics (pentru a face economie de memorie).
Reprezentarea propriu zisa se face cu ajutorul unei metode paint().
Practic,se declara o fereastra,in care se include un obiect de tip Gra-
phics,apoi se apeleaza metodele acestui obiect si se prezinta cu paint().
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
-134-
Pentru a umple continutul unui poligon se poate utiliza si o textura
importata dintr-o fila de resurse,sau chiar dintr-o imagine.Pentru acest
scop,se va crea un obiect de tip TexturePaint,cu ajutorul unui tampon de
tip BufferedImage,apoi se va apela metoda setPaint(),cu acest obiect.
EXEMPLU: -desenati in Paint modelul.Salvati fila cu numele "Model1.gif".
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.event.*;
import java.io.*;
import javax.imageio.*;
public class Grafica6 {
public static void main(String[] args) {
Frame f = new Frame("Exercitii grafice"){
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
BufferedImage img = null;
try { img = OmageIO.read(new File("Model1.gif"));
} catch (IOException e) {}
TexturePaint t1 = new TexturePaint(
img,new Rectangle2D.Double(0,0,100,100));
g2d.setPaint(t1);
g2d.fill( new Ellipse2D.Double(50,50,400,300)); }};
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); }});
f.setSize(new Dimension(600,400));
f.setVisible(true); }}
-salvati fila cu numele Grafica6.java.Compilati si executati.
Puteti repeta exercitiul cu orice fila .jpg (fotografie,tablou,imagine).
Pentru a determina stilul penitei de desen se utilizeaza un obiect de
tip BasicStroke.
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
public class Grafica7 {
public static void main(String[] args) {
Frame f = new Frame("Exercitii grafice"){
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Float f1 = new Float(11.55);
BasicStroke bs1 = new BasicStroke(f1);
g2d.setStroke(bs1);
g2d.drawLine(70,70,150,250);
g2d.drawLine(150,250,220,70); }};
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); }});
f.setSize(new Dimension(600,400));
f.setVisible(true); }}
-salvati fila cu numele Grafica7.java.Compilati si executati.
In BasicStroke puteti seta atributele: caps,joins,dash sau miter limit.
-135-
Reprezentarile grafice pot fi inlocuite cu imagini digitale (fotografii
grafice,desene,imagini etc.) preluate dintr-o fila de resurse.Cea mai
simpla modalitate este sa creati o clasa derivata din Component in care
sa includeti imaginea.In continuare construiti un obiect din clasa respec-
tiva si utilizati obiectul ca pe un component oarecare.
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import javax.swing.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
-136-
Pentru operatii grafice mai complicate,se poate utiliza un obiect de
tip AffineTransform.Acest obiect permite transformarea unei imagini grafi-
ce,in alta imagine grafica cu alt sistem de coordonate.Obiectul actioneaza
asupra unui container de tip GeneralPath si translateaza fiecare punct,
dintr-un sistem de coordonate in altul.
EXEMPLU:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class Grafica9 {
public static void main(String[] args) {
JFrame frame = new JFrame("Design");
JPanel panel1 = new JPanel(){
GeneralPath makeBox(int x,int y) {
GeneralPath p = new GeneralPath();
p.moveTo(100,100);
p.lineTo(150,150);
p.lineTo(250,100);
p.lineTo(100,100);
p.closePath();
return p; }
public void paint(Graphics g) {
Dimension sz = getSize();
g.setColor(Color.red);
g.drawRect(10,10,sz.width-20,sz.height-20);
Graphics2D g2; g2=(Graphics2D) g;
GeneralPath p = makeBox(200,100);
p.setWindingRule(generalPath.WIND_EVEN_ODD);
AffineTransform tfm = new AffineTransform();
tfm.translate(175.5,27.5); g2.fill(p);
tfm = new AffineTransform();
p = makeBox(200,100);
tfm.translate(10.5,90.5); g2.setTransform(tfm);
g2.setColor(Color.green); g2.fill(p);
g2.draw();
Shape s = p.createTransformedShape(tfm);
tfm = new AffineTransform();
tfm.translate(75.7,150.5); tfm.rotate(-Math.PI/3);
g2.settransform(tfm);
g2.setColor(Color.red); g2.fill(s);
g2.draw(s);
}};
frame.setDefaultCloseOperation(Jframe.EXIT_ON_CLOSE);
frame.add(panel1);
frame.setSize(500,300);
frame.setVisible(true);
}}
-salvati fila cu numele Grafica9.java.Compilati si executati.
Practic,obiectul AffineTransform utilizeaza doua matrice tridimensio-
nale,si translateaza fiecare punct,dintr-o matrice in alta,apoi redese-
neaza obiectul.Desenul poate fi rotit,inversat,deplasat,deformat etc.
-137-
Pentru a decupa fragmente dintr-un desen,sau dintr-o imagine,se pot
utiliza metodele setClip() si Clip().Prin setClip() se specifica tipul
de suprafata utilizata pentru decuparea imaginii,iar prin clip() se exe-
cuta operatia propriu-zisa.
EXEMPLU:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
-138-
EXEMPLU:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
-139-
Pentru ca un obiect grafic sa poata fi redesenat interactiv,trebuie
creata o bucla,intre functia care trateaza evenimentul si cea care con-
struieste obiectul grafic,astfel incat sa poata fi apelata de fiecare
data metoda repaint().
EXEMPLU:
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class Grafica12 extends JApplet implements ChangeListener {
Desen desen1;
JSpinner sizes;
static int sizeChoice = 12;
public void init(){
JPanel panel1 = new JPanel();
panel1.add(new Jlabel("Schimbati coordonata y:"));
sizes = new JSpinner(new SpinnerNumberModel(50,10,300,10));
sizes.addChangeListener(this);
panel1.add(sizes);
desen1 = new Desen();
add(BorderLayout.NORTH,panel1);
add(BorderLayout.CENTER,desen1);
}
public void stateChanged(ChangeEvent e) {
try {
String size = sizes.getModel().getValue().toString();
sizeChoice = Integer.parseInt(size);
desen1.Actualizeaza();
} catch (NumberFormatException nfe) { }
}
public static void main(String args[]) {
JFrame f = new JFrame("Grafica interactiva");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e){ System.exit(0); }});
JApplet grafica1 = new Grafica12();
f.add(grafica1,BorderLayout.CENTER);
grafica1.init();
f.pack();
f.setVisible(true);
}}
class Desen extends JComponent {
public Dimension getPreferredSize(){ return new Dimension(500,200); }
public void Actualizeaza() { repaint(); }
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.drawLine(10,10,300,Grafica12.sizeChoice);
Actualizeaza();
}}
-salvati fila cu numele Grafica12.java.Compilati si executati.
-140-
Daca analizati putin exercitiul de mai sus,puteti observa ca la orice
modificare a spinner-ului este apelata metoda Actualizeaza() prin care
se redeseneaza obiectul cu repaint().O alta metoda posibila construieste
un obiect nou la fiecare eveniment.Solutiile mai complexe,implica mai
multe thread-uri,cate unul pentru fiecare eveniment.
Acest manual nu poate epuiza toate mijloacele de exprimare grafica
din Java.Pentru o documentare suplimentara,este util sa studiati si
exemplele similare oferite pe Internet.Fiecare astfel de exemplu,poate
fi punctul de plecare pentru un anumit gen de aplicatie.In majoritatea
cazurilor,puteti gasi solutia directa pentru problema d-voastra,fara sa
mai fie necesar sa proiectati o aplicatie noua.
Daca activitatea d-voastra implica un numar mare de aplicatii grafice,
si este important sa gasiti in permanenta solutii noi,este recomandabil
sa va organizati putin.Extrageti din sursa bibliografica toate clasele
utile pentru aplicatiile grafice si construiti scheme,sau sabloane,in
functie de tipul de solutie.
EXEMPLU:
-daca din pachetul awt sunt necesare clasele: ActiveEvent,Canvas,Color,
Component,Container,Event,geom,Graphics2D,Image,LayoutManager,Paint,
MultipleGradientPaint si Transparency.
Extrageti din sursa de documentatie fiecare dintre clase,cu metodele
si constructorii (inclusiv cele mostenite) si apoi construiti o planse
in care afisati relatiile cele mai frecvente.In acest mod,in loc sa
navigati prin Help,va fi suficient sa aruncati o privire pe diagrama de
ansamblu.
O alta abordare posibila,este prin crearea unor module de tip schelet,
pentru fiecare tip de aplicatie grafica.Construiti fereastra de baza si
obiectele necesare pentru afisarea sau prelucrarea datelor,proiectati
si definiti evenimentele si conectati obiectele intre ele.De cele mai
multe ori,este practic sa preluati datele dintr-o fila sursa,sau dintr-o
clasa auxiliara externa (usor de actualizat).Cu alte cuvinte,creati seturi
de interfete grafice.In continuare,va fi suficient sa inlocuiti fila de
resurse sau sa actualizati clasa externa.Cele mai frecvente file de tip
resursa sunt tabelele si bazele de date,filele XML si filele de tip text.
Orice aplicatie noua,se va putea crea combinand doua sau mai multe
astfel de module,sau interfete.Java este un sistem "open-source".Cu alte
cuvinte,este permis si chiar recomandabil sa utilizati fragmente de cod
preluate din surse sigure,cu exceptia situatiilor in care aceste coduri
au fost protejate expres,prin drepturi de autor.La randul d-voastra,este
recomandabil sa insotiti aplicatiile create de codul sursa.In acest mod,
programele vor fi mult mai usor de verificat si depanat si vor putea fi
preluate fara rezerva de catre alti programatori.
In cazul in care combinati module realizate cu versiuni diferite de
Java,pot sa apara unele mici incompatibilitati.In aceste situatii,este
recomandabil sa modificati aplicatia spre versiunea cea mai recenta,adica
sa inlocuiti clasele vechi,prin cele moderne.Fiecare versiune a imbunata-
tit mijloacele de expresie,sau a rezolvat micile neajunsuri ale versiunii
precedente.Situatia este ceva mai complexa in cazul in care doriti sa
combinati module create cu platforme vizuale diferite (NetBeans,Eclipse,
etc.).In acest caz,este recomandabil sa aveti o experienta anterioara cu
fiecare dintre aceste platforme,inainte de a incerca sa le mixati.
-141-
APLICATII AUDIO
Aplicatiile moderne sunt de cele mai multe ori audio-vizuale.Cu alte
cuvinte,fiecare program nou contine o serie de file sursa,atat de tip
grafic cat si de tip audio,precum si un set de obiecte in care se proce-
seaza aceste date,pentru a obtine efecte vizuale sau sonore.Operatiile de
tip audio,implica urmatoarele etape:
1.-crearea unor file de resurse audio,sau stocarea informatiei in tampoane
de memorie temporare (streamuri,linii de date,etc.)
2.-arhivarea si sau transmiterea acestor file de la un calculator la
altul
3.-citirea filelor de resurse si decodificarea informatiei
4.-prelucrarea informatiei (filtre,analiza,transformari,insertii etc.)
5.-conversia datelor intr-un format acceptat de utilizatorul final
6.-distributia datelor la unitatea de executie (difuzor)
In majoritatea limbajelor de programare,toate aceste operatii se fac
automat.Exista obiecte specializate,iar codurile sursa sunt protejate sau
chiar secrete.Platforma Java,este insa creata in baza conceptului de "open
source" si ofera un acces nelimitat la informatia din filele audio.Pro-
gramatorii pot descompune semnalul,bit cu bit,pot executa orice tip de
operatie posibila,pot crea orice tip de aplicatie software posibila: audio
playere,mixere audio,filtre,dispozitive de supraveghere si control,pro-
grame de analiza automata a informatiei,programe pentru depanare automata,
motoare de cautare in retea,motoare de ditributie in retea...etc.Mai mult
decat atat,Java ofera un set foarte bogat de obiecte specializate si de
interfete grafice,ce contin numeroase metode predefinite.In esenta,toate
aceste obiecte si interfete sunt grupate in pachetul "javax.sound".
Pachetul "javax.sound" este subimpartit in doua pachete: "sampled" ce
contine obiectele si interfetele destinate sa actioneze asupra unor file
de resurse preexistente (file sursa in format audio) si pachetul "midi" ce
contine mijloacele necesare pentru a crea file sursa de tip audio,sinte-
tizatoare de sunet si operatii de tip "midi"(receiver,sequencer,synthe-
sizer,transmitter etc.).
Aplicatiile audio create vor putea fi de tip "applet",pentru a putea
fi incluse in file de tip HTML,sau vor putea fi aplicatii independente,
menite sa ruleze pe un calculator user.Alte aplicatii,vor putea inter-
actiona cu diverse structuri software (mixere,sintetizatoare,filtre,ana-
lizoare,distribuitoare etc.) sau hardware (keyboard digital,orga electro-
nica,instrumente muzicale cu comanda numerica,CD-playere,DVD-uri,video
si audioplayere,televizoare,telefoane mobile...etc.).Practic,platforma
Java ofera mijloace de expresie,pentru orice tip de aplicatie audio ima-
ginabila.
Exista si o serie de limitari.Pachetul original nu include toate for-
matele audio posibile.De exemplu,pentru a putea utiliza file in format
MP3,este necesar sa descarcati pachete auxiliare de pe Internet,sau sa
creati codurile necesare pentru decomprimarea datelor.Mai exact,Java
nu recunoaste implicit de tipurile necomprimate de format audio: AIFC,
AIFF,AU,SND si WAVE.Pentru toate celelalte tipuri,sunt necesare operatii
auxiliare de conversie a datelor.Exista doua solutii posibile:
-convertiti file la formatul .wav (cu ajutorul unui program de conversie)
-extindeti paleta de optiuni a platformei Java cu alte tipuri de format
(adaugand pachete suplimentare,obiecte si interfete specializate etc.).
-142-
Este necesara o prezentare putin mai ampla a notiunii de format audio.
Unda sonora este o vibratie (oscilatie),caracterizata prin frecventa,
amplitudine si intensitate sonora (volum sau energie purtatoare).Undele
sonore sunt cuprinse in gama de frecvente situata intre 16 si 16000 Hz.
Pentru a caracteriza complet si explicit o unda sonora se pot imagina
numeroase metode matematice: descrierea undei punct cu punct,aproximarea
undei prin linii de amplitudine diferita,descrierea undei in sistem grafic
de tip "pixel cu pixel",descrierea undei prin obiecte vizuale sau matema-
tice,arii de date,matrite,tabele de pointeri,baze de date etc.Pentru ca
toate aceste mijloace de expresie sa se incadreze intr-un sistem coherent
este necesar sa existe un standard comun,respectat cu strictete de toti
programatorii.Acest standard,poarta numele de format audio.Exista mai
multe solutii general acceptate,iar ca rezultat exista mai multe formate
de tip audio.Fila de format audio contine: un header (antetul cu infor-
matii generale),informatii utile referitoare la formatul utilizat(Exemplu:
1 = 8-bit G.711 u-law sau 5=32-bit linear PCM),informatii referitoare
la fila in ansamblu (autor,data,lungime,versiune,etc.) precum si o struc-
tura ce codifica si decodifica informatia bruta,cunoscuta sub numele de
audio codec.In general,fiecare format audio contine un anumit audio codec,
dar pot exista si formate audio ce recunosc mai multe astfel de structuri
(de exemplu filele de tip AVI).
Principalele tipuri de format audio sunt: WAV,AIFF,AU,FLAC,Monkey's
Audio,WavPack,Shorten,Tom's,Audio Kompressor,TTA,ATRAC,Apple Loosless,
WindowsMedia Audio(WMA),MP3,Vorbis,Musepack,ATRAC,AAC...etc.
Pentru a reprezenta cat mai fidel informatia,se utilizeaza sisteme
extensive,consumatoare de mari volume de memorie.Astfel,pentru a repre-
zenta sunetele bit-cu bit,se va utiliza cate un byte pentru fiecare Hz
(oscilatie).Astfel,un sunet de 16 Hz va putea fi reprezentat cu 16 bytes,
dar un sunet de 3000 Hz va fi reprezentat prin 3000 de Bytes.Fiecare
valoare de tip byte,va fi reprezentata prin valori numerice de tip SIGNED,
pentru a putea codifica amplitudinea.Rezulta ca pentru reprezentarea
sunetelor tamponul minim de memorie trebuie sa contina cel putin 16000
de bytes (16Kb).Dar aceasta codificare nu poate include toate informatiile
dorite.De exemplu,pentru a putea regla volumul sonor,cresteri sau scaderi
ale volumului,sau pentru a putea adauga diverse efecte speciale (forte,
dolce,stereo,quadro)este necesar ca fiecare oscilatie sa fie caracterizata
prin doi sau mai multi bytes.Sunetele percepute de urechea umana nu de-
pasesc freceventa de 12000 de Hz.Prin conventie,s-a stabilit ca sunt
suficienti cate 4 bytes de informatie pentru fiecare oscilatie,astfel ca
tamponul minimal de memorie este cel de 48 Kb (12000x4).In concluzie,
pentru ca aplicatia software si respectiv unitatea hardware sa poata
procesa sunete,este necesar sa poata citi calupuri de cel putin 48 kb
pentru fiecare sunet (chiar daca majoritatea sunetelor vor ocupa mai
putina memorie).
Formatele acceptate de Java sunt exclusiv cele necomprimate.Adica fila
sursa descrie complet fiecare sunet,inclusiv pauzele audio totale.Cu
alte cuvinte,o fila necomprimata va ocupa pentru un minut de muzica exact
acelasi spatiu de memorie ca si pentru un minut de liniste absoluta.
Practic,intr-o melodie exista mai multe pauze decat momente sonore.Ca
rezultat,filele necomprimate sunt incarcate cu volume foarte mari de
informatie inutila,sau dispensabila (dar cu pierderea calitatii).
-143-
In plus,exista situatii in care se poate renunta la codificarea pe
patru bytes a fiecarei oscilatii.In acest caz,se va utiliza cate un singur
bytes/oscilatie,reducand consumul de memorie de 4 ori.Filele rezultate
contin toate sunetele,dar pierd o serie intreaga de efecte speciale.Cali-
tatea inregistrarii va fi compromisa ireversibil.Din acest motiv,atunci
cand doriti calitate maxima,sau doriti sa analizati un fragment audio,
este esential sa utilizati formatul audio necomprimat.
Principalul format audio necomprimat poarta numele de PCM (Pulse-code
modulation) si reprezinta oscilatiile prin variatia amplitudinii,spre
deosebire de formatul PPM (pulse position modulation) in care oscilatiile
sunt diferentiate intre ele prin pozitia pe axa orizontala.
Majoritatea formatelor audio utilizeaza insa o formula oarecare de
compresie a datelor,astfel incat sa reduca cat mai mult din spatiul de
memorie consumat,dar sa pastreze o calitate audio acceptabila.Gradul de
compresie depinde de sunetul inregistrat,dar si de solutia tehnica imple-
mentata: stereofonie,quadrofonie...etc.In esenta,tipurile de formate cu
compresie se impart in doua categorii: lossy si lossless in functie de
volumul de informatie utila ce se pierde din inregistrare.Detaliile pentru
fiecare solutie de compresie nu fac obiectul acestui manual,dar principiul
general este acelasi: -toate spatiile din inregistrare in care sunetele
sunt absente,sau practic nesemnificative se exclud din inregistrare si
se inlocuiesc cu o valoare numerica,in care se specifica durata pauzei
respective.La redare,valoarea numerica va fi transformata in sunet minim
si va fi reinserata in tamponul de memorie inainte de a fi transmisa la
utilizator.Prin astfel de operatii,spatiul de memorie poate fi redus de
patru pana la zece ori.Solutiile matematice difera de la un format la
altul si sunt determinate de tipul inregistrarii: mono sau stereo...etc.
Exista formule extrem de complexe,prin care se poate face o aproximare
destul de exacta a informatiei codificate prescurtat.Puteti studia aceste
solutii,in literatura de specialitate.In principiu,nu este util sa creati
un format audio nou,decat daca aveti deja un numar destul de mare de uti-
lizatori si exista un scop precis (protejarea unor informatii valoroase).
Pentru restul situatiilor,sunt suficiente formatele deja existente,
dintre care cele mai reprezentative sunt:
FREE OPEN FORMATS:
wav -este formatul audio standard necomprimat de tip PCM.Calitatea audio
este maxima,dar ocupa foarte multa memorie (circa 10 Mb/minut).Filele
wav au o structura de tip RIFF si se utilizeaza mai ales pentru inre-
gistrarile facute in studio (CD-urile comerciale).
ogg -este un format comprimat,asemanator cu Mp3,dar mai putin popular
mpc -Musepack este un format comprimat,stereo,utilizat de Apple pentru Mac
raw -(raw=brut) poate contine orice codec audio,dar se utilizeza mai ales
pentru formate necomprimate (PCM).Se utilizeaza pentru testarea unor
aparate audio,sau pentru proiectarea formatelor audio comerciale.
au -este formatul standard utilizat de firma Sun pentru Java.Formatul au
poate contine datele necomprimate(PCM),sau comprimate cu un audio
codec de tip u-law,a-law sau G729.
mid -este standardul industrial utilizat pentru a permite compatibilitatea
dintre computere si instrumentele muzicale sau echipamentele de veri-
ficare,control si comunicatie.
Toate aceste formate respecta legislatia referitoare la free-ware.
-144-
OPEN FILE FORMATS:
gsm -este destinat pentru telefonie.Este utilizat in Europa si asigura un
raport destul de bun de compresie/calitate.Poate codifica file wav.
dct -este destinat pentru dictare.Contine un header,ce poate fi criptat,
pentru a garanta confidentialitatea inregistrarilor.
vox -contine un codec de tip ADPCM (Adaptive Differential Pulse Code
Modulation).Comprima formatele mai mari de 4 biti la 4 biti/oscilatie
si este asemanator cu wav,dar nu include nr.de canale si rata/sec.
aac -este un format comprimat,bazat pe standardele MPEG2 si MPEG4
mp4/m4a -este un format comprimat cu un codec MPEG-4
mmf -este un format audio utilizat de firma Samsung
Aceste formate sunt de tip Open file,adica permit accesul la date,dar nu
sunt gratuite.Achizitionarea lor se face contra cost.
PROPRIETARY FORMATS:
mp3 -este cel mai popular format pentru arhivarea melodiilor comerciale.
Este comprimat cu un codec MPEG-3 pentru o compresie de circa 10 ori
a filelor de tip Wav.Calitatea audio este rezonabila.Se utilizeaza
pentru muzica ascultata pe PC (exista numeroase arhive freeware).
ATENTIE: melodiile sunt freeware dar fromatul este cu drept de autor.
wma -este formatul Microsoft Windows.
atrac -este dezvoltat de firma Sony.Are intotdeauna si o extensie .wav.
ra -Real Audio este creat pentru transportul datelor via Internet.In
acest format se includ datele (pentru a fi protejate la acces).
ram -este o fila de tip text ce contine link-ul spre filele de tip .ra
dss -Digital Speech Standard este un format produs de firma Olympus
msv -este un format comprimat,produs de firma Sony pentru Memory Stick
dvf -este un format comprimat utilizat de firma Sony pentru dictafoane
IVS -este dezvoltat de 3D Solar UK pentru muzica si video playere
m4p -este dezvoltat de firma Apple pentru iTunes Music Store
iKlax -este un format de tip multi-track,dezvolatat de iKlax Media pentru
operatii complexe asupra datelor: mixaje,rearanjari,compozitie etc.
mxp4 -este un format dezvoltat de firma Musinaut.Permite redarea unei
melodii in mai multe versiuni (skins = inregistrari paralele).Acest
format permite scenarii interactive intre artist si utilizator.
Toate aceste formate sunt protejate prin drepturi de autor.Chair daca
majoritatea inregistrarilor oferite cu aceste formate sunt de tip free-
ware,formatul propriu-zis este protejat.Cu alte cuvinte,pentru a putea
prelua datele brute din acest format,este necesara obtinerea unei licente
si a codec-ului audio.Puteti utiliza aceste formate pentru a arhiva sau
reda inregistrarile,dar nu puteti transforma sau reinregistra filele.Daca
este imperios necesar sa "deparazitati" o melodie,convertiti datele in
format .wav,efectuati operatiile dorite si apoi reconvertiti filele la
formatul original.
Este esential sa intelegeti formatul audio,inainte de a incepe orice
fel de operatii asupra datelor.Pentru a putea lucra cu formate diferite,
este important sa detineti un program bun de conversie.Exista numeroase
astfel de programe.Pot fi descarcate gratuit,sau contra cost,din reteaua
Internet.Un astfel de program,gratuit este de exemplu: Switch Sound File
Converter.Acest program lucreaza cu file de tip: aac,aiff,aif,amr,ape,au,
flac,gsm,m3u,m4a,mp3,mpc,ogg,pls,raw,rss,spx,vox,wav,wma,wpl.Puteti insa
sa alegeti orice alt program,in functie de preferintele d-voastra.
-145-
Prima operatie esentiala inainte de a incepe orice operatie asupra
datelor,o reprezinta obtinerea de informatii despre formatul audio.Pentru
acest scop,exista o clasa special destinata,denumita AudioSystem.Clasa
AudioSystem pemite identificarea si accesarea mixerelor instalate,conver-
sia dintre diferite formate audio,includerea resurselor audio in tampoane
de memorie si stream-uri...etc.Pentru a obtine formatul audio,clasa con-
tine trei metode getAudioFileFormat() supraincarcate,astfel incat se poate
obtine formatul audio fie dintr-o fila locala,fie de la o adresa URL,fie
dintr-un stream.Formatul audio obtinut va fi o instanta a clasei Audio-
FileFormat si va contine informatii despre: tipul de format audio,lungimea
in bytes,numarul de masuri (frames),tipul de compresie,numarul de canale
audio (mono,stereo),numarul de masuri/secunda,lungimea fiecarei masuri.
EXEMPLU:
import java.applet.*;
import javax.swing.*;
import java.awt.event.*;
import java.net.URL;
import java.awt.FlowLayout;
import javax.sound.sampled.*;
import java.io.IOException;
public class Sunet2 extends JApplet implements ActionListener {
Applet applet;
private AudioClip onceClip;
String sir1 = "bark.au";
JLabel text1,text2;
JButton buton1;
AudioFileFormat fila1;
public void init() {
setLayout(new FlowLayout());
buton1 = new JButton("SUNET");
text1 = new JLabel("Textul initial");
buton1.addActionListener(this);
add(buton1);
add(text1);
try { URL url = new URL(getCodeBase(),sir1);
try { try{
fila1 = AudioSystem.getAudioFileFormat(url);
} catch (IOException e){}
} catch (UnsupportedAudioFileException e) {}
onceClip = newAudioClip(url);
} catch (java.net.malformedURLException e) {}
}
public void actionPerformed(ActionEvent event){
onceClip.play();
text1.setText(fila1.toString());
}
}
-salvati fila cu numele Sunet2.java si compilati.
Pentru executie,includeti applet-ul intr-o fila HTML.Acest exercitiu
utilizeaza pentru redarea sunetelor un obiect de tip AudioClip (din pache-
tul applet) si metoda AudioFileFormat.toString() pentru formatul audio.
-146-
Interfata AudioClip este simplista.Se recomanda doar pentru efecte
sonore,salvate in file de tip .au.Interfata permite repetarea sunetelor
cu ajutorul metodei loop() sau chiar mixarea a doua sau mai multe sunete.
EXEMPLU:
import java.applet.*;
import javax.swing.*;
import java.awt.event.*;
import java.net.URL;
import java.awt.FlowLayout;
import javax.sound.sampled.*;
import java.io.*;
public class Sunet4 extends JApplet implements ActionListener {
Applet applet; private AudioClip onceClip;
String sir1 = "bong.au"; JLabel text1,text2;
JButton buton1,buton2,buton3;
JTextField t1; AudioFileFormat fila1;
public void init() {
setLayout(new FlowLayout());
t1 = new JTextField("bong.au");
buton1 = new JButton("Start");
buton2 = new JButton("Stop");
buton3 = new JButton("Loop");
text1 = new JLabel("Textul initial");
buton1.addActionListener(this);
buton2.addActionListener(this);
buton3.addActionListener(this);
add(t1); add(buton1); add(buton2);
add(buton3); add(text1);
try{ URL url = new URL(getCodeBase(),sir1);
try{ try{
fila1 = AudioSystem.getAudioFileFormat(url);
} catch (IOException e) {}
} catch (UnsupportedAudioFileException e) {}
onceClip = newAudioClip(url);
} catch (java.net.MalformedURLException e) {} }
public void actionPerformed(ActionEvent event) {
String s1 = event.getActionCommand();
if (s1 == "Start") { sir1 = t1.getText();
try{ URL url = new URL(getCodeBase(),sir1);
try{ try{ fila1 = AudioSystem.getAudioFileFormat(url);
} catch (IOException e){}
} catch (UnsupportedAudioFileException e) {}
onceClip = newAudioClip(url);
} catch (java.net.MalformedURLException e) {}
onceClip.play(); }
if (s1 == "Stop") { onceClip.stop(); }
if (s1 == "Loop") { onceClip.loop(); }
text1.settext(fila1.toString());
}}
Salvati fila cu numele Sunet4.java si compilati.Includeti applet-ul intr-o
fila HTML si executati exercitiul.Puteti mixa doua sau mai multe file.
-147-
Interfata AudioClip este destinata doar pentru efecte sonore intr-o
fila de tip web.Pentru a putea asculta o fila muzicala de tip wav (ce
poate contine 30 - 300 Mb de informatie,codificata pe 8 sau 16 biti,este
necesar un player sau un mixer audio.O astfel de structura,contine mai
multe linii de date,alimentate de catre un stream specializat si obiecte
vizuale pentru operatii asupra datelor.Scheletul unei singure linii de
date,se poate configura ca in exemplul de mai jos.
EXEMPLU:
import java.io.File;
import java.io.IOException;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.swing.SwingUtilities;
import javax.swing.filechooser.*;
import javax.sound.sampled.*;
-148-
public void actionPerformed(ActionEvent event) {
if (event.getSource() == openButton) {
int returnVal = fc.showOpenDialog(Audio1.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
strFilename = file.getAbsolutePath();
text1.append(strFileName);
text1.append("\n"); }}
File soundFile = new File(strFilename);
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(soundFile);
} catch (Exception e) { e.printStackTrace() ;
System.exit(1);
}
AudioFormat audioFormat = audioInputStream.getFormat();
SourceDataLine line = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class,audioFormat);
try { line = (SourceDataLine) AudioSystem.getLine(info);
line.open(audioFormat);
} catch (LineUnavailableException e)
{ e.printStackTrace();
System.exit(1);
} catch (Exception e) { e.printStackTrace();
System.exit(1);
}
if (event.getSource() == Start1) {
text1.append(audioFormat.toString());
text1.append("\n");
line.start();
int nBytesRead = 0;
byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];
while (nBytesRead != -1) {
try {
nBytesRead = audioInputStream.read(abData,0,abData.length);
} catch (IOException e) { e.printStackTrace(); }
if (nBytesRead >= 0)
{ int nBytesWritten = line.write(abData,0,nBytesRead); }
}
line.drain();
line.close(); }
}
if (event.getSource() == Stop1) {
line.stop();
line.close();
System.exit(0);
}
}}
-salvati fila cu numele Audio1.java.Compilati si executati.Apasati butonul
OpenFile si alegeti orice fila cu extensia .wav,apoi apasati butonul Start
pentru redare.Butonul Stop,este mai mult formal.
-149-
Exemplul de mai sus contine urmatoarele etape:
-designul pentru o interfata grafica minimala,cu cateva butoane
-un obiect JFileChoser pentru alegerea filei dorite
-continutul filei este copiat intr-un stream (audioInputStream)
-se citese formatul filei audio (in audioFormat)
-se creaza o linie de date de tip SourceDataLine
-se includ informatiile despre formatul filei audio
-se deschide linia de date (cu open())
-se copiaza datele din stream intr-un tampon intermediar de memorie (o
arie de bytes de dimensiunea tamponului de memorie- abData)
-eventual se executa operatiile dorite asupra datelor (filtre,insertii,
decupaje,selectii,resetari ...etc)
-se scriu datele din tamponul intermediar in linia de date
-se lanseaza linia in executie cu start()
Pentru a simplifica exercitiul,se poate renunta la stream si la tam-
ponul intermediar.In acest caz,datele se pot scrie direct din fila in
linia de date,pentru a fi executate cu start.In acest caz,insa,nu se va
putea face nici o operatie asupra datelor (player rudimentar).
Un player competitiv,sau un mixer,va trebui sa contina mai multe
astfel de linii de date,ce pot fi executate secvential sau simultan.
Pentru acest scop,este esential ca fiecare linie sa fie inclusa intr-un
thread separat.In exercitiul de mai sus,puteti observa ca melodia lansata
in executie nu poate fi intrerupta pana la citirea completa a datelor.
Pentru a remedia acest inconvenient,includeti intreaga linie de date
intr-un thread ce poate fi initializat sau oprit,ori de cate ori este
necesar.
Exista o oferta extrem de larga pentru mixere,playere,programe de
conversie a formatului,etc.Inainte de a proiecta o astfel de aplicatie,
este bine sa consultati oferta din reteaua Internet.Majoritatea progra-
melor sunt in regim shareware,sau chiar cu licenta,dar exista si o oferta
destul de bogata in regim freeware.De exemplu,un mixer functional si
destul de performat este oferit sub numele de Sound in Depth.Daca doriti
si codurile sursa,este bine sa verificati si oferta de la adresa:
http://svn.sourceforge.net
Are rost sa proiectati o aplicatie noua,doar daca sunteti extrem de
pasionati,sau daca aveti o conceptie radical diferita de cele existente.
Nu este suficient sa executati operatii matematice asupra sirului de
bytes.Pentru ca modificarile efectuate sa fie relevante,trebuie sa res-
pectati codec-ului fiecarui format audio.
Exemplu: sa presupunem ca este un format de 4 bytes/oscilatie.Daca este
vorba de un semnal stereo,cei patru bytes pot fi utilizati astfel: primul
pentru canalul LEFT,al doilea pentru canalul RIGHT,al treilea pentru
volum si al patrulea pentru timbru sau efecte speciale...etc.Nu se poate
ghici codul intern,ci trebuie sa cunoasteti exact aceste informatii,
pentru fiecare tip de format audio posibil (licenta codec-ului).
Nu exista reguli fixe.Puteti executa orice operatii matematice posi-
bile,cu conditia sa respectati toate reglementarile referitaore la for-
matul respectiv.In principiu,cele mai simple operatii sunt cele prin
care se extrag fragmente dintr-o inregistrare.Aceste fregmante,se pot
exclude (pentru a elimina "parazitii"),sau se pot salva in file izolate,
pentru efecte speciale : mixaje,waw-waw,polifonie etc.
-150-
EXEMPLU:
import java.io.*;
import java.io.IOException;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.*;
import javax.sound.sampled.*;
if (event.getSource() == Start1) {
line.start();
int nBytesRead = 0;
byte[] abData = new byte[tampon];
try {
nBytesRead = audioInputStream.read(abData,0,abData.length);
} catch (IOException e) {}
byte[] selectie = new byte[tampon];
for (int x = p1;x<p2;x++) {
selectie[x-p1] = abData[x];
}
int nBytesWritten = linie.write(abData,0,nBytesRead);
File salvare1 = new File("Salvare1.wav");
ByteArrayInputStream sursa1 = new ByteArrayInputStream(selectie);
AudioInputStream sursa2 = new AudioInputStream(sursa1,audioFormat,tampon);
try {
AudioSystem.write(sursa2,tip1,salvare1);
} catch (IOException e) {}
catch (IllegalArgumentException e1) {};
-continuare pe pagina urmatoare
-152-
text1.append(sursa1.toString());
text1.append("\n");
line.drain();
line.close();
}}}
-salvati exercitiul cu numele Audio3.java.Compilati si executati
Pentru a deschide fila dorita,utilizati butonul OpenFile.In prima
caseta JTextField (lungime) puteti seta lungimea fragmentului interpretat,
iar in urmatoarele doua: offset-ul de start si offset-ul final al frag-
mentului selectat.Apasand butonul,se va reda fragmentul cu lungimea setata
si se va salva in fila "Salvare1.wav",fragmentul cuprins intre: pornire
si oprire.
Un astfel de algoritm,reprezinta doar o mica parte dintr-un mixer.In
continuare,mixerul trebuie sa poata decupa mai multe astfel de fragmente
si apoi sa le alipeasca,sau sa le combine,pentru a rezulta un fragment
muzical nou.
Exista doua tipuri principale de operatii asupra datelor de tip audio:
-operatii asupra unor fragmente audio preinregistrate (selectare,mixare,
filtrare,transformare ...etc.)
-operatii asupra fenomenului fizic prin care se genereaza sunetele(modi-
ficarea frecventei,a amplitudinii,generarea de sunete noi...etc.).
Primul tip de operatii respecta formatul existent al datelor si lucrea-
za doar cu fragmente audio,ce pot fi salvate in file separate,sau in tam-
poane de memorie.
Cel de al doilea tip de operatii,necesita o pregatire speciala si no-
tiuni avansate despre structura semnalului audio.Fiecare sunet,este un
fenomen fizic complex,ce poate fi codificat doar cu ajutorul unui volum
destul de mare de date.Un sunet mediu,este format din sute sau mii de
oscilatii,cu amplitudine diferita.Rezultanta lor,este un singur sunet
scurt,dar descompunerea liniara a acestor oscilatii ofera o imagine destul
de realista a fenomenului de ansamblu.Se spune ca fiecare sunet are o
"amprenta",adica o anumita secventa logica,unica si repetabila.Aceasta
"amprenta" poate fi pusa in evidenta,fie prin valori numerice,fie sub
forma unei reprezentari grafice.Codificarea fiecarui sunet,nu se poate
face arbitrar,sau dupa reguli aleatorii.Din acest motiv,forul European de
specialitate,denumit European Broadcasting Union (EBU),a dezvoltat impre-
una cu Audio Engineering Society (AES),un standard pentru codificarea
semnalului audio,cunoscut sub numele de AES/EBU (AES3).Acest standard a
intrat in vigoare in anul 1985 si a fost actualizat in anul 1992 si 2003.
Inainte de a incerca orice operatii asupra semnalului audio,este bine sa
consultati si acest standard.In plus,trebuie sa cunoasteti algoritmul
exact al fiecarui codec audio,pentru a putea respecta formatul respectiv.
In principiu,este recomandabil sa uitlizati doar instrumente specia-
lizate (exista o gama foarte larga,oferita sub licenta freeware).Daca
totusi doriti sa faceti analize ale semnalului audio,puteti dezvolta
aplicatii in care prelucrati matematic valorile audio din stream-ul audio,
sau reprezentati grafic semnalul si apoi dezvoltati algoritmi pentru
analiza si interpretarea datelor obtinute.Trebuie insa sa tineti cont
de faptul ca doar unul dintre bytes contine informatia audio propriu zisa,
in timp ce restul sunt doar bytes "de masca",sau contin informatii auxi-
liare despre semnal.
-153-
EXEMPLU:
import java.io.File;
import java.io.IOException;
import java.awt.*;
import java.awt.Robot;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.Timer;
import.javax.swing.SwingUtilities;
import javax.swing.filechooser.*;
import javax.sound.sampled.*;
import java.awt.geom.Line2D.Double;
-154-
JLabel et1 = new Jlabel("bytes: ");
JLabel et2 = new JLabel("offset1: ");
JLabel et3 = new JLabel("offset2: ");
JLabel et4 = new JLabel("delay:" );
panou1.add(et1);
panou1.add(lungime);
panou1.add(openButton);
panou1.add(et2);
panou1.add(pornire);
panou1.add(Start1);
panou1.add(et3);
panou1.add(oprire);
panou1.add(Stop1);
panou1.add(et4);
panou1.add(delay1);
panou1.add(Stop2);
fc = new JFileChooser();
text1 = new JTextArea(10,80);
text1.setEditable(false);
JScrollPane scrol1 = new JScrollPane(text1);
panou2 = new Desen1();
add(panou1,BorderLayout.PAGE_START);
add(scrol1,BorderLayout.CENTER);
add(panou2,BorderLayout.SOUTH);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Mixer audio");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(new Audio6());
frame.pack();
frame.setSize(1000,600);
frame.setVisible(true);
frame.repaint();
}
public void actionPerformed(ActionEvent event) {
tampon = Integer.valueOf(lungime.getText());
p1 = Integer.valueOf(pornire.getText());
p2 = Integer.valueOf(oprire.getText());
if(event.getSource() == Stop1) { System.exit(0); }
if(event.getSource() == Stop2) { timer1.stop(); }
if(event.getSource() == openButton) {
int (returnVal = fc.showOpenDialog(Audio6.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
strFilename = file.getAbsolutePath();
text1.append(strFilename);
text1.append("\n");
}
}
-continuare pe pagina urmatoare
-155-
File soundFile = new File(strFilename);
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(soundFile);
} catch (Exception e){ text1.append("Exceptie I/O ! \n"); }
AudioFormat audioFormat = audioInputStream.getFormat();
SourceDataLine line = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class,audioFormat);
try { line = (SourceDataLine) AudioSystem.getLine(info);
line.open(audioFormat);
} catch (LineUnavailableException e)
{ text1.append("Linia nu este accesibila ! \n");
} catch (Exception e){ text1.append("Format necunoscut ! \n");}
if(event.getSource() == Start1) {
line.start();
int nBytesRead = 0;
int nAdresa = 0;
byte[] abData = new byte[tampon];
try{ nBytesRead = audioInputStream.read(abData,0,tampon-1);
} catch (IOException e) {}
sunet = new byte[tampon];
for (int x=0;4*x<tampon-1;x++) {
sunet[4*x+3] = abData[4*x+3];
sunet[4*x+1] = 0;
sunet[4*x+2] = 0;
sunet[4*x] = 0;
}
int nBytesWritten = linie.write(abdata,0,nBytesRead);
for (int x = p1;x<p2;x++) {
text1.append(String.valueOf(abData[x]));
text1.append("|");
ActionListener ceas1 = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if(Audio6.adresa < Audio6.tampon) {
Audio6.panou2.Actualizeaza(); }}};
timp = Integer.valueOf(delay1.getText());
timer1 = new Timer(timp,ceas1);
timer1.start();
};
line.drain();
line.close();
text1.append(audioFormat.toString());
text1.append(" \n");
}}}
-156-
Graphics2D g2d = (Graphics2D) g;
super.paintComponent(g);
g2d.setColor(Color.BLUE);
g2d.draw(new Rectangle(882,300));
g2D.setColor(Color.RED);
g2d.drawLine(0,150,850,150);
for int x=1;x<8800;x++) {
Audio6.adresa = Audio6.adresa + 1;
g2d.draw(new java.awt.geom.Line2D.Double(
x*0.1,150+0.1,x*0.1,150+Integer.valueOf(
Audio6.sunet[Audio6.adresa])+0.1)); };
g2d.drawString(String.valueOf(
Audio6.sunet[Audio6.adresa]),700,300);
g2d.drawString(String.valueOf(Audio6.adresa),600,300);
}}
-salvati fila cu numele Audio6.java.Compilati si executati.
In exercitiul de mai sus,puteti studia stream-ul audio,fie sub forma
de valori numerice,fie prin reprezentarea grafica (simplista) din Desen1.
Observati ca datele au fost copiate intr-un tampon separat,inainte de a
fi prelucrate grafic,sau matematic,pentru a evita orice tip de corupere
a datelor.
Exemplul de mai sus,ofera o reprezentare grafica cat de cat corecta,
doar in cazul formatului .wav cu patru bytes/frame.Observati ca la
copierea datelor am exclus primii trei bytes din fiecare frame si am
salvat valoarea numerica doar pentru cel de al patrulea byte.Aceasta
formula nu se poate aplica pentru restul formatelor ( nici pentru formatul
.wav cu 8 bytes/frame).
Exercitiul de mai sus,este foarte simplist,dar ofera o imagine destul
de intuitiva asupra modului de analiza a unui semnal audio.Pentru o abor-
dare profesionista,va trebui sa consultati literatura de specialitate.
Deoarece utilizeaza un singur thread,toate operatiile se executa secven-
tial.Astfel,reprezentarea grafica nu apare decat dupa redarea fragmentului
muzical.Pentru a putea executa mai multe operatii simultan,trebuie sa
organizati evenimentele din interfata sub forma de thread-uri separate si
apoi sa lansati in executie doua sau mai multe thread-uri simultan.
Pachetul javax.sound.sampled contine urmatoarele interfete: Clip,Line,
DataLine,LineListener,Mixer,Port,SourceDataLine,TargetDataLine si urma-
toarele clase: AudioFileFormat,AudioFileFormat.Type,AudioFormat,AudioFor-
mat.Encoding,AudioInputStream,AudioPermission,AudioSystem,BooleanControl,
BooleanControl.Type,CompoundControl,CompoundControlType,Control,Control.
Type,DataLine.Info,EnumControl,EnumControl.Type,FloatControl,FloatControl.
Type,Line.Info,LineEvent,LineEvent.Type,Mixer.Info,Port.Info,ReverbType.
Fiecare dintre ele ofera un set de metode specializate pentru un anumit
gen de operatii.O parte dintre aceste instrumente software sunt de tip
abstract,adica nu contin decat declaratia metodelor.Pentru a putea utili-
za aceste structuri,exista doua solutii:
1.-editati definitia pentru fiecare metoda abstracta
2.-importati de pe Internet implementari in care metodele abstracte au
fost redefinite pentru un anumit scop.
Exista si pachete suplimentare,complet noi,ce pot fi descarcate si
incluse in aplicatiile d-voastra.
-157-
Cel de al doilea pachet din javax.sound,poarta numele de "midi" si este
destinat pentru muzica electronica instrumentala (midi este prescurtarea
de la Musical Instrument Digital Interface).In termeni mai generali,prin
MIDI se intelege standardul industrial definit in anul 1982 de catre
Audio Engineering Society,pentru a asigura compatibilitatea dintre toate
instrumentele muzicale electronice si echipamentele de comanda si control
computerizate (pentru arhivarea,prelucrarea,redarea sau transmisia date-
lor in retea).Cu ajutorul acestui protocol,este posibil ca un fragment
muzical executat la o orga electronica din Europa sa poata fi inregistrat,
impachetat si transmis in Australia,pentru a fi redat pe un computer,sau
pe o alta orga electronica conectata la un calculator.
Aplicatiile de tip MIDI utilizeaza un sistem total diferit de codifi-
care a informatiilor.Prin urmare,atat structurile software,cat si cele
de tip hardware sunt complet diferite de cele de tip audio(incompatibile).
Informatiile MIDI sunt arhivate in file speciale,cu extensia .midi.
Exista trei tipuri posibile de file MIDI:
MIDI 0 - single track (cu o singura linie de date)
MIDI 1 - multiple tracks,synchronous (mai multe linii executate sincron)
MIDI 2 - multiple tracks,asynchronous(mai multe linii de date ce pot fi
redate fie sincron,fie asincron).
Fiecare fila MIDI contine un header,in care se specifica tipul filei,
dimensiunea header-ului,numarul de linii de date (tracks) si tactul/nota.
In continuare,datele sunt fragmentate in segmente (chunks).Fiecare linie
de date trebuie sa contina un header si unul sau mai multe segmente de
date (chunks).In segmente,informatiile sunt structurate sub forma de eve-
nimente MIDI.Fiecare byte,este un astfel de eveniment MIDI si contine de
fapt o anumita comanda.La executia filei MIDI,se va citi si interpreta
fiecare byte,apoi se va executa comanda respectiva.De exemplu,daca eveni-
mentul MIDI este "Note on" se va executa nota muzicala respectiva.
In format midi,sunetele sunt prearhivate si codificate cu un singur
byte/nota muzicala.Exista un tabel de corespondenta,cu ajutorul caruia
fiecare tasta poate fi transformata intr-o nota muzicala.Ca rezultat,un
sunet poate fi codificat cu un singur byte (fata de 48 kb in formatul
audio),dar posibilitatile de exprimare se rezuma doar suntele preinregis-
trate.Acest format nu este destinat pentru inregistrarile muzicale de
calitate,dar este extrem de util pentru scopuri didactice: editarea si
redarea unei partituri muzicale simple,abc-muzical ...etc.
Pentru a putea largi paleta de exprimare,sunetele preinregistrate se
arhiveaza sub forma de Instrumente.Fiecare instrument,va contine toata
gama de note muzicale acceptate.Ca rezultat,o anumita valoare numerica
(un Byte),va putea genera sunete diferite,in functie de Instrumentul
muzical in care este arhivat.Intr-o fila MIDI,se accepta pana la 16 linii
de date paralele,astfel ca un fragment muzical va putea cuprinde pana la
16 instrumente separate.Termenul de Instrument,nu implica neaparat un
instrument muzical,ci poate fi o voce umana,un cor,sau orice alt tip de
sunet (organizate sub forma de octave).De exemplu,un astfel de instrument
poate contine sunete sintetizate complet electronic (sunete care nu exista
in natura).Din acest motiv,obiectul software care genereaza sunetele este
denumit Synthesizer(sintetizator) iar cel care citeste secventele poarta
numele de Sequencer (secventiator).
Cel mai simplu MidiPlayer poate fi realizat astfel:
-158-
EXEMPLU:
import java.io.IOException;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.*;
import javax.sound.midi.*;
public class MidiPlayer1 extends JPanel implements ActionListener {
private static Sequencer sm_sequencer = null;
private static Synthesizer sm_synthesizer = null;
String strFilename = "music.midi";
JButton openButton,Start1,Stop1;
JTextArea text1;
JFileChooser fc;
public MidiPlayer() {
super(new BorderLayout());
JPanel panou1 = new JPanel();
panou1.setLayout(new FlowLayout());
openButton = new JButton("Open File");
openButton.addActionListener(this);
Start1 = new JButton("Start");
Start1.addActionListener(this);
Stop1 = new JButton("Stop");
Stop1.addActionListener(this);
panou1.add(openButton);
panou1.add(Start1);
panou1.add(Stop1);
fc = new JFileChooser();
text1 = new JTextArea(5,60);
text1.setEditable(false);
JScrollPane scrol1 = new JScrollPane(text1);
add(panou1,BorderLayout.PAGE_START);
add(scrol1,BorderLayout.CENTER); }
public static void main(String[] args) {
JFrame frame = new JFrame("Midi Player");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(new MidiPlayer1());
frame.setSize(300,200);
frame.pack();
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
if (event.getSource() == Stop1) { System.exit(0); };
if (event.getSource() == openButton) {
int returnVal = fc.showOpenDialog(MidiPlayer1.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
-continuare pe pagina urmatoare
-159-
strFilename = file.getAbsolutePath();
text1.append(strFilename);
text1.append("\n"); }}
if (event.getSource() == Start1) {
File midiFile = new File(strFilename);
Sequence sequence = null;
try {
sequence = MidiSystem.getSequence(midiFile);
} catch (InvalidMidiDataException e)
{ text1.append("Exceptie Midi Data ! \n");}
catch (IOException e)
{ text1.append("Exceptie I/O ! \n"); }
try {
sm_sequencer = MidiSystem.getSequencer();
} catch (MidiUnavailableException e)
{ text1.append("Fila midi este inaccesibila ! \n"); }
if (sm_sequencer == null)
{ text1.append(" Nu exista Sequencer ! \n"); }
sm_sequencer.addMetaEventListener(new MetaEventListener() {
public void meta(MetaMessage event) {
if (event.getType() == 47) {
sm_sequencer.close();
if (sm_synthesizer != null) {
sm_synthesizer.close(); }
text1.append("Sfarsitul fragmentului ! \n"); }
}});
try { sm_sequencer.open();
} catch (MidiUnavailableException e)
{ text1,append("Fila midi nu a fost deschisa ! \n");}
try { sm_sequencer.setSequence(sequence);
} catch (InvalidMidiDataException e)
{ text1.append("Nu a fost setata secventa ! \n"); }
if ( ! (sm_sequencer instanceof Synthesizer)) {
try { sm_synthesizer = MidiSystem.getSynthesizer();
sm_synthesizer.open();
Receiver synthReceiver = sm_synthesizer.getReceiver();
Transmitter seqTransmitter = sm_sequencer.getTransmitter();
seqTransmitter.setReceiver(synthReceiver); }
catch (MidiUnavailableException e)
{ text1.append("Eroare de instanta ! \n"); }}
sm_sequencer.start(); }}
}
-salvati fila cu numele MidiPlayer1.java.Compilati si executati.Alegeti
orice fila in format .midi.
In exemplul de mai sus,se observa obiectele centrale : sequencer si
synthesizer,accesul la resurse cu ajutorul obiectelor JFileChooser si
respectiv MidiSystem,precum si cele doua obiecte necesare pentru trans-
portul si interpretarea datelor: Transmitter si Receiver.
Si acest exemplu este structurat intr-un singur thread.Intr-o aplicatie
reala,este de dorit ca fiecare eveniment sa detina un thread propriu,
pentru a permite multiprocesarea paralela.
-160-
Pachetul MIDI include urmatoarele interfete: ControllerEventListener,
MetaEventListener,MidiDevice,Receiver,Sequencer,Soundbank,Synthesizer si
Transmitter,precum si urmatoarele clase predefinite:Instrument,MetaMessage
MidiDevice.Info,MidiEvent,MidiFileFormat,MidiMessage,MidiSystem,Patch,
Sequence,Sequencer.SyncMode,ShortMessage,SoundbankResource,SysexMessage,
Track,VoiceStatus.
Pentru a exploata sau dezvolta aceste instrumente software,este bine
sa consultati si fila sursa (situata in src/javax/sound/midi).Daca doriti
sa proiectati mixere si filtre midi,va trebui sa studiati cu atentie
formatul de tip midi.In principiu,formatul include mai multe secvente,
structurate astfel: MIDI Header,Track Header,Track data si Track out.
Fragmentul muzical propriu zis,este inclus in segmentul Track data,sub
forma de evenimente MIDI.Fiecare eveniment MIDI este definit prin cate
patru bytes astfel: Byte1 = time-stamp (adica masoara tactul)
Byte2 = status-byte (exemplu W,X si Y = Note On)
Byte3 = note pitch (inaltimea notei)
Byte4 = volume (volumul notei respective)
EXEMPLU:
7F 90 3E 60 se traduce astfel: asteapta 7F unitati de timp,apoi
executa pe canalul 0,nota muzicala C,la volum de 60 de unitati.
Pentru status,se utilizeaza urmatoarele conventii: 8=Note Off,9=Note On,
A=AfterTouch(apasarea unei taste),B=Control Change,C=Program Change,
D=Channel Pressure,E=Pitch Wheel.
Cu alte cuvinte,programul d-voastra va trebui sa ruleze fiecare sec-
venta,sa extraga evenimentele MIDI si sa le interpreteze.Pentru a edita
fragmente muzicale,se procedeaza exact invers: se editeaza evenimentele
MIDI,se includ in Tracks si apoi in secvente si se salveaza in file de
tip MIDI.Pentru a putea edita fragmente muzicale,trebuie sa detineti si
un tabel de corespondenta intre nota muzicala si tasta ce defineste nota
respectiva(se poate descarca de pe Internet):
EXEMPLE:
Tasta numarul Codul octavei(englez) Codul octavei(german) Frecventa
-----------------------------------------------------------------------
88 C8 c5 4186.01 Hz
87 B7 h4 3951.07 Hz
86 A7/B7 ais4/b4 3729.31 Hz
85 A7 a4 3520.00 Hz
...............
3 B0 H2 30.8677 Hz
2 A0/B0 Ais2/B2 29.1353 Hz
1 A0 A2 27.5000 Hz
------------------------------------------------------------------------
Cele 9 octave sunt structurate astfel:
Englez: A0 A1 A2 A3 A4 A5 A6 A7 A8
German: A2 A1 A a a1 a2 a3 a4 a5
Frecventa: 27.5 55 110 220 440 880 1760 3520 7040
Exista si numeroase mixere si playere MIDI,cu licenta freeware ce pot
fi descarcate gratuit de pe Internet: MidiSwing,FruityLoops ...etc.
Formatul MIDI,ofera o calitate limitata pentru sunet,dar este extrem de
util pentru educatia muzicala primara: solfegii,compozitie,ritm si tact,
descifrarea unor partituri,backing instrumental,muzica techno...etc.
-161-
JAVA MANAGEMENT
In orice limbaj de programare,una dintre activitatile prioritare ale
programatorului este legata de gestionarea resurselor: unitati de memorie,
unitati de procesare,aplicatii,file de resurse...etc.Orice program sau
aplicatie,nu se poate dezvolta si nu poate rula decat intr-un anumit
context hardware/software,denumit generic "mediul de operare".Metodele
primitive,nu implica structuri specializate:
EXEMPLU:
import java.io.*;
import java.awt.*;
import java.awt.event;
import javax.swing.*;
-162-
Platforma Java,implementeaza insa un set destul de bogat de interfete
si clase specializate,concepute special pentru a permite adminstrarea
cat mai judicioasa a resurselor si aplicatiilor,inclusiv in cazul unei
retele de calculatoare interconectate.Initial,aceste resurse au fost
incluse in pachetul java.lang.management,iar apoi s-a adaugat si pachetul
javax.management.Impreuna,cele doua pachete sunt cunoscute sub numele de
JMX Technology (Java Management Extensions technology) si reprezinta un
standard pentru platforma Java,incepand cu versiunea 5.0.Scopul principal
al acestei tehnologii este cel de a simplifica operatiile de administrare
si gestionare a retelelor,cu ajutorul unui standard comun.Respectand
acest standard,cu o singura aplicatie de control se pot supraveghea si
gestiona toate aplicatiile de tip Java din retea (cu conditia sa contina
un modul specializat denumit MBean).
Pentru supravegherea operatiilor executate de Java Virtual Machine,
pachetul Java contine si un executabil specializat,denumit "jconsole".
Inainte de a explora cele doua pachete destinate pentru management,este
bine sa explorati putin acest utilitar.
EXEMPLU:
-lansati in executie orice aplicatie Java -de exemplu Aplicatie1
-executati un dublu click pe jconsole.exe
-se va deschide fereastra Java Monitoring & Management Console in care
este deschisa fereastra de dialog: JConsole: New Connection
-pentru a va conecta la aplicatia dorita,alegeti din lista Local Process
aplicatia dorita si apoi apasati butonul Connect
-in fereastra se va afisa o analiza grafica a principalelor elemente
de management: memoria de stiva,numarul de thread-uri active,numarul
de clase active (in tot computerul),procentul de activitate a proceso-
rului principal.
-cu ajutorul meniului,puteti explora in profunzime oricare dintre
optiuni.De exemplu,alegeti optiunea Threads
-se va afisa o analiza grafica,dar si lista completa a firelor aflate
in executie la un anumit moment dat.Executati un click pe oricare
dintre ele,pentru a afisa o descriere mai detailiata.
-o alta viziune de ansamblu o ofera aptiunea VM Summary,in care este
prezentata sintetic Java Virtual Machine actuala.
-ultima optiune,MBeans include interfetele specializate pentru manage-
ment.De exemplu,puteti sa alegeti aceasta optiune si apoi sa selectati
JMImplementation.Deschideti folderul pentru a gasi MBeanServerDelegat,
adica serverul dedicat operatiilor administrative de tip MBean.
-in continuare,explorati dupa bunul plac acest utilitar.
Este usor de observat ca "jconsole" poate fi utilizata cu succes pentru
a verifica oricare dintre aplicatiile realizate de d-voastra.Lansati in
executie aplicatia,apoi deschideti jconsole pentru a verifica cata memorie
consuma,ce volum de procesare solicita,cate thread-uri si clase sunt
active...etc.Mai mult decat ata,puteti lansa in executie mai multe apli-
catii in paralel,pentru a putea estima volumul total de memorie si randa-
mentul procesorului instalat.Acest utilitar este mai complex decat Task
Manager din Windows si ofera o imagine de ansamblu asupra tuturor opera-
tiilor din calculator (nu doar cele din mediul Java).In plus,acest utili-
tar va familiarizeaza cu principalele aspecte ce trebuiesc urmarite in
munca de administrare a sistemului,sau a unei retele locale.
-163-
Utilitarul jconsole este realizat cu tehnologia JMX.In principiu,prin
tehnologia JMX se poate face legatura dintre o anumita resursa locala si
o aplicatie de control,situata in acelasi calculator,sau la o locatie
oarecare din retea.Pentru acest scop,exista mai multe nivele de actiune:
-primul nivel este in unitatea de lucru ce contine resursa ce urmeaza sa
fie administrata (Exemplu: Unitatea de memorie C in care este arhivata
o anumita aplicatie).Pentru legatura cu aceasta resursa,se utilizeaza o
interfata specializata ce contine in numele sau si sufixul MBean.
-urmatorul nivel il reprezinta obiectul ce implementeaza aceasta inter-
fata.Acest obiect va purta numele de Managed Beans (administratorul de
elemente),sau pe scurt MBean.In acest obiect vor fi definite toate ope-
ratiile ce se pot executa asupra resursei administrate.
-urmatorul nivel este un server local de tip MBean,prin care obiectul de
tip MBean poate comunica cu restul retelei.Este esential ca obiectul de
tip MBean sa fie inregistrat in acest server (cu ajutorul unor functii
specializate).
-nivelul urmator este reprezentat prin conectoarele si protocoalele de
comunicatie specifice pentru reteaua respectiva (Exemple: protocolul de
tip SNMP-Simple Network Management Protocol,sau protocoale private)
-ultimul nivel este reprezentat prin aplicatia din retea cu ajutorul
careia se opereaza asupra resursei administrate (Exemplu: un utilitar de
genul "jconsole.exe").
In alti termeni,tehnologia JMX poate fi rezumata astfel:
1.Instrumentation (instrumentarea) -este reprezentata prin obiectele Java
ce fac conexiunea cu resursa administrata (interfata si clasa MBean).
2.JMX Agent (agentul administrator) -este reprezentat prin serverul local
si protocoalele de cominicatie ce asigura legatura intre obiectul MBean
si server.
3.Remote management(administrarea de la distanta) - este reprezentat prin
programul sau programele utilizate pentru a opera asupra resursei.Acest
program poate fi situat in acelasi calculator,sau undeva in retea.
Interventia propriu zisa asupra resursei se face cu ajutorul obiectului
MBean.Pentru acest scop,obiectul MBean poate sa contina:
-un set de atribute fixe sau reconfigurabile
-un set de operatii (functii predefinite)
-un set de informatii prin care obiectul se autodescrie (atribute,operatii
posibile,autor sau licenta,modul de utilizare etc.)
Pentru a realiza un obiect de tip MBean,se va defini o interfata MBean,
sau se va putea utiliza una dintre interfetele predefinite (DynamicMBean).
Aceasta interfata va contine toate atributele si operatiile ce vor fi
vizibile in aplicatia de control.In mod curent este mai usor sa definiti
o interfata simpla,dar daca doriti sa utilizati interfetele predefinite,
va trebui ca in clasa de implementare sa definiti toate metodele din
interfata sablon.
Prin conventie,numele intefetei MBean se formeaza prin adaugarea sufix-
ului MBean la numele clasei de implementare.
De exemplu: daca dorim sa transformam clasa Aplicatie1 intr-o clasa
de tip MBean,vom denumi aceasta aplicatie: Aplicatie2,iar interfata de
tip MBean se va numi Aplicatie2MBean.
Cu ajutorul unui modul de tip MBean,aplicatia va putea fi controlata
si de la distanta,de exemplu din "jconsole.exe".
-164-
EXEMPLU: interfata MBean poate fi urmatoarea:
-165-
Prin conventie,server-ul local contabilizeaza clasele inregistrate cu
ajutorul unei clase specializate,denumita Main.Daca exsita aceasta clasa,
va trebui sa adaugati si inregistrarea noului obiect MBean,iar daca nu
exista,o puteti defini astfel:
import java.lang.management.ManagementFactory;
import java.util.Queue;
import java.util.concurent.ArrayBlockingQueue;
import javax.management.MBeanServer;
import javax.management.ObjectName;
-166-
Pentru ca aplicatia sa poata fi deschisa cu jconsole de pe un alt
terminal din retea,este necesar ca serverul sa fie conectat la retea prin
un port de comunicatie.Serverul MBeans include conectorul JMX automat si
asculta solicitarile primite de la clienti.Pentru ca protocolul sa fie
complet este insa necesar ca in clasa Main sa fie inregistrat si autenti-
ficat portul de comunicatie prin care se va face schimbul de date.
Portul se poate inregistra de exemplu astfel:
java -Dcom.sun.management.jmxremote.port=9999 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
Main
(-D seteaza respectiva proprietate a sistemului -vezi java.exe Options)
Cand doriti sa accesati o aplicatie situata pe alt calculator,lansati
jconsole,alegeti optiunea Remote Process si introduceti numele unitatii
de lucru si portul de cominicatie:
EXEMPLU: hostname:9999
Detaliile referitoare la comunicatiile din retea nu fac obiectul
acestui manual.Java contine un set de structuri software dedicate special
pentru operatiile in retea (Networking),dar nu este recomandabil sa inva-
tati aceste protocoale in sistem "autodidact".Pentru comunicatii in retea,
si aplicatii de retea,este recomandabil sa urmati un curs specializat,fie
prin Internet,fie intr-o unitate specializata.
Atunci cand datele cu care se opereaza in serverul MBean nu sunt din
tipurile standard (Exemplu: operati cu obiecte definite intr-o clasa lo-
cala),trebuie sa utilizati obiecte de tip MXBean.MXBean este un obiect
identic cu MBean,dar contine si o referinta (pointer) spre clasa locala
in care este definit tipul de date cu care se opereaza.
Exista si clase MBean dinamice,in care atributele si operatiile expuse
prin interfata MBean nu sunt fixe ci se pot modifica interactiv.In acest
caz,atributele si operatiile MBean vor fi definite cu ajutorul unor clase
locale,denumite "metadata classes".In serverul MBean se va inregistra
obiectul MBean,pentru a asigura conexiunea cu interfata MBean,dar conti-
nutul efectiv al acestei interfete va putea fi modificat dinamic prin
inlocuirea claselor locale (metadata classes).Nici acest mecanism nu face
obiectul acestui manual,deoarece necesita notiuni de programare cu clase
de tip generic,ce depasesc nivelul programatorului incepator.
Cele doua pachete contin clase si interfete ce permit automatizarea
operatiilor.De exemplu,clasa ManagementFactory poate genera automat seturi
de obiecte MXBean.Alte clase sunt destinate pentru administrarea memoriei
si a mediului de operare din Java Virtual Machine.Fiecare obiect MBean,
poate contine si un fel de utilitar "Help",in care sunt descrise principa-
lele caracteristici ale obiectului,incluse intr-un obiect de tip MBeanInfo
MBeanNotificationInfo,MBeanOperationInfo sau MBeanParameterInfo.
Nu se pot face recomandari referitor la modul de exploatare al acestor
resurse.Fiecare programator va apela la aceste clase,in functie de nece-
sitatile de moment,sau in functie de experienta sa personala.Este bine
sa studiati initial aplicatiile de tip management oferite in reteaua
Internet,apoi cautati clasele si metodele prin care se rezolva fiecare
dintre operatii.Cele mai frecvente aplicatii sunt cele care administreaza
sau supravegheaza memoria de operare (intrerup executia atunci cand apare
riscul de supraincarcare).
-167-
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
-168-
In orice alt limbaj de programare,acest tip de solutie ar fi necesitat
si o operatie oarecare de eliberare a memoriei.Limbajul Java,contine insa
o structura specializata tocmai pentru acest gen de operatii,denumita
Grabage Collection (colectorul de gunoi).Garbage Collection este procesul
prin care se elibereaza din memorie toate obiectele ramase fara referinta.
Pentru acest scop,Java Virtual Machine contine o serie de tampoane de
memorie cu ajutorul carora poate gestiona toate structurile de date din
program.Mai exact,memoria de operare este sub forma de stiva si este
impartita in trei sectiuni mari: Young Generation (datele recente),Old
Generation (datele vechi) si Permanent Generation (datele permanente).
Pentru datele recente,sectiunea Young generation este formata la randul
sau din trei stive mai mici: Eden Space (2 Mb),From Space (64 Kb) si res-
pectiv To Space(64 Kb).
Orice obiect nou creat,va fi salvat initial in Eden Space.Daca obiectul
urmeaza sa fie apelat din nou,este trecut in From si apoi in To Space,iar
daca urmeaza sa persiste in program,este trecut in stiva numita Old gene-
ration.Obiectele ce sunt necesare pe intreaga durata de executie a progra-
mului,vor fi pastrate in stiva denumita Permanent Space.
PREZENTARE SCHEMATICA:
________________________________________________________________
| Eden Space=2 Mb | From=64 Kb | To=64 Kb |
-----------------------------------------------------------------
|
_________________________________________________________________
| Old Generation = 5 Mb maxim 44 Mb |
-----------------------------------------------------------------
|
_________________________________________________________________
| Permanent Space = 4 Mb |
-----------------------------------------------------------------
-169-
In exemplul precedent,incercati sa adaugati in bucla timer-ului si
comanda: Runtime.getRuntime.gc();
In acest mod,se va forta eliberarea memoriei la fiecare secunda.Acest
tip de solutie este sigur ("safe") in ceeea ce priveste memoria.Totusi
in aplicatiile reale nu are rost sa eliberati memoria pentru cativa bytes
ce vor fi oricum eliberati automat la nevoie,dar este practic se efectuati
o operatie de eliberare a memoriei inainte de a lansa un program nou,sau
inainte de a crea un set oarecare de obiecte.Pentru anumite tipuri de
aplicatii,exista si tendinta de a executa o operatie de Garbage Collection
inainte de a incepe executia propriu-zisa.
Trebuie mentionat faptul ca Java Virtual machine nu partajeaza spatiul
de memorie decat cu celelalte aplicatii Java.Are rost se eliberati memoria
doar daca exista si alte aplicatii Java ce ruleaza simultan.
Exista situatii in care doriti sa stiti exact cum este gestionata me-
moria intr-o aplicatie oarecare (De exemplu in aplicatiile editate de
d-voastra).Pentru acest scop,platforma Java va pune la dispozitie doua
utilitare extrem de valoroase: jps.exe si jstat.exe.
Primul dintre ele,jps.exe,detecteaza toate aplicatiile java aflate in
executie la un anumit moment dat si returneaza un cod numeric ce repre-
zinta identificatorul Virtual Machine pentru aplicatia respectiva (vmid).
EXEMPLU: lansati in executie exemplul precedent ( java Memorie1)
deschideti alta fereastra Command Prompt,setati calea de acces si
apoi lansati in executie jps.exe (jps).Se va returna o diagrama de genul:
304 Jps (304 este vmid pentru Jps)
1756 Memorie1 (1756 este vmid pentru Memorie1)
In continuare puteti obtine o schema completa a memorie cu ajutorul
utilitarului jstat (vezi si documentatia tehnica).
EXEMPLU: tastati: jstat -gcutil 1756 250 3
unde: 1756 este codul vmid (utilizati codul returnat de Jps)
250 este intervalul intre doua citiri in milisecunde
3 este numarul de citiri
Se vor afisa urmatoarele tampoane de memorie:
S0 - Survivor space zero (procent din spatiul total)
S1 - Survivor space 1 (procent din spatiul total)
E - Eden space (procent din spatiul total)
O - Old space (procent din total)
P -Permanent space (procent din total)
YGC - numarul de evenimente Garbage Colection in Young generation
YGCCT -timpul ultimei operatii Garbage Collection
FGC - numarul de operatii GC complete (toate tampoanele)
FGCT - timpul/operatie GC completa
GCT - timpul total pentru Grabage Collection
Optiunea gcutil este cea mai frecvent utilizata,dar jstat ofera o
paleta mult mai larga de optiuni: class,compiler,gc,gccapacity,gccause,
gcnew,gcnewcapacity,gcold,gcoldcapacity,gcpermcapacity,printcompilation
si gcutil.Fiecare dintre ele ofera o configuratie diferita de date.
EXEMPLU: jstat -class 1756 1000 5
va returna: Loaded - numarul total de clase active,Bytes - numarul total
de Kbytes incarcati,Unloaded - numarul de clase descarcate,Bytes-numarul
total de Kbytes descarcati si Time-timpul necesar pentru toate aceste
operatii.
-170-
Cele doua utilitare pot fi extrem de utile in etapa de proiectare si
in cea de depanare a programelor.Compilati programul,lansati programul in
executie,detectati codul vmid cu jps.exe,apoi lansati jstat cu un numar
mare de repetitii,la un interval oarecare.Executati in program operatiile
cele mai critice si studiati aspectul tampoanelor de memorie.Atunci cand
memoria consumata se apropie de limita critica,este bine sa inserati in
program si cate o operatie de eliberare a memoriei.
Limita critica va fi stabilita in functie de destinatia programului.
Daca programul urmeaza sa ruleze strict independent,nu este necesar sa
faceti nici un fel de operatii asupra memoriei.Java va elibera automat
tampoanele atunci cand este cazul.Insa,in cazul aplicatiilor ce urmeaza
sa fie executate concurential,aceasta limita critica va trebui sa fie
astfel aleasa incat toate programele sa poata rula concomitent.
O solutie alternativa este sa construiti fiecare aplicatie sub forma
de thread-uri.Fiecare thread va citi automat memoria libera si va executa
o pauza,atunci cand memoria libera este deficitara.Cu un astfel de algo-
ritm,programul d-voastra se va putea adapta la situatia de moment,fara
interventia programatorului.
Se pot imagina numaroase scenarii.Are rost sa implementati solutii
spaciale pentru administrarea memoriei,doar in situatiile in care stiti
sigur ca programul d-voastra va rula intr-un mediu multiprocesare concu-
rentiala in care exista riscul de a supraincarca memoria de operare.In
acest caz,vor trebui selectate procesele prioritare,fata de cele cu im-
portanta secundara.Solutia ce mai simpla este sa pastrati activ un singur
thread,cel ce administreaza executia celorlalte thread-uri.In acest thread
cititi periodic memoria libera si implementati secventa de prioritati.Pe
masura ce memoria se elibereaza,thread-ul administrator va putea lansa in
executie noi si noi thread-uri.
Pentru a crea aplicatii personalizate de administrare a memoriei se
pot utiliza metodele claselor din cele doua pachete de management.
Pachetul java.long.management contine urmatoarele clase: LockInfo,Monitor-
Info,ManagementFactory,MemoryNotificationInfo,MemoryUsage,ManagementPer-
mision si ThreadInfo,iar la acestea se adauga si urmatoarele interfete:
ClassLoadingMXBean,CompilationMXBean,MemoryManagerMXBean,GarbageCollector-
MXBean,MemoryMXBean,MemoryPoolMXBean,OperatingSystemMXBean,RuntimeMXBean,
RuntimeMXBean,ThreadMXBean.
Nici una dintre aceste clase nu contine metode cu acces direct la
memorie.Din acest motiv,pentru a putea beneficia de avantajele oferite
de aceste structuri software,trebuie sa cititi cu foarte mare atentie
documentatia tehnica (sau tutoriale specializate) si apoi sa combinati
cate doua sau mai multe clase si interfete.
De exemplu: -interfata MemoryMXBean administreaza toata memoria din
Java Virtual Machine.Ca urmare,permite prin metodele sale detectarea
memoriei de stiva,a memoriei non stiva,activarea rutinei de Garbage
Colector sau detectarea numarului de obiecte aflate inca in stare de
asteptare (referite dar neprocesate).Prin metodele getHeapMemoryUsage() si
getNonHeapMemoryUsage() se poate obtine un obiect de tip MemoryUsage,din
care se pot determina: memoria alocata (cu getComitted() ),memoria maxima
alocabila (cu getMax() ),memoria utilizata actual (cu getUsed())...etc.
Pentru acest scop se va edita cate o functie specializata in care un
obiect de tip MemoryUsage returneaza valoarea de tip long dorita.
-171-
Pentru a obtine o instanta a interfetei MemoryMXBean se poate utiliza
metoda ManagementFactory.getMemoryMXBean().
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
-172-
In mod similar,se poate utiliza interfata MemoryPoolMXBean,pentru a
putea analiza tampoanele de memorie din Java Virtual Machine.
EXEMPLU:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.lang.management.*;
import java.util.*;
-173-
Cel de al doilea pachet administrativ,denumit javax.management,contine
un alt set de interfete si clase,destinate mai ales pentru schimb de date
intre doua instante de Java Virtual Machine,respectiv intre doua calcula-
toare diferite.Acest pachet depaseste nivelul de interes al incepatorilor
si este destinat mai ales adminstratorilor de retea.Totusi,daca doriti
sa instalati aplicatii Java pe mai multe calculatoare din retea si apoi
sa le monitorizati cu un program unic,este recomandabil sa studiati si
acest pachet.Descrierea exhaustiva se gaseste in documentul editat de
firma Sun,cu numele "Java Management Extensions (JMX) Remote API 1.0 Spe-
cification".Principalele clase si interfete se gasesc in pachetul javax.-
management.remote si definesc impreuna un standard de comunicatie ce are
urmatoarele caracteristici: flexibilitate,securitate,transparenta si
interoperabilitate:
-este flexibil,pentru ca permite adaugarea de noi protocoale si de solutii
pentru implementarea acestor protocoale noi
-este securizat prin urmatoarele standarde de securitate: Java Secure
Socket Extension (JSSE),Simple Authentification,Security Layer(SASL) si
Java Authentification and Authorization Service
-este transparent,deoarece expune catre client o interfata aproape iden-
tica cu cea arhivata in server-ul ce asigura comunicatia
-este interoperabil,deoarece clientul poate comunica cu server-ul prin
protocoale diferite,cu conditia sa respecte acest standard (clientul si
serverul pot utiliza implementari ale unor protocoale diferite)
In esenta,sistemul este format dintr-un server,unul sau mai multi
clienti si conectoarele dintre server si clienti.Aceste structuri se
gasesc atat la nivel de hardware (calculatoare si cabluri),cat si la
nivel de software (tampoane de memorie in care se organizeaza clasele si
interfetele ce administreaza operatiile dorite).Conexiunile fizice din
sistem (cele hardware) sunt permanente,in timp ce conexiunile software
se organizeaza prin algoritmii doriti (se deschid si se inchid atunci
cand este necesar).Exista un conector server si un conector client.In
fiecare dintre conectoare,se vor include clasele si interfetele necesare
pentru setul de operatii dorit.Ca rezultat al unei conexiuni,trebuie ca
terminalul de tip client sa expuna pentru operatii o interfata identica
cu cea arhivata in serverul MBean.Evident ca sistemul opereaza doar cu
clase de tip MBean.Protocolul definit pentru acest standard poarta numele
de Remote Method Invocation (RMI).
Comunicatia dintre server si client se face in sesiuni (sessions).O
sesiune este creata in momentul in care se defineste conectorul de tip
client.Sesiunea este prezenta si atunci cand conectorul software este
inchis.Astfel,in timpul unei sesiuni,conectorul poate fi deschis si apoi
inchis de mai multe ori,pentru a realiza conexiuni software repetate.O
conexiune este deschisa,atunci cand conectorul client a accesat serverul
pe unul dintre porturile de comunicatie posibile (portul de comunicatie
solicitat trebuie sa existe si sa fie liber).
EXEMPLU: connect "service:jmx:rmi://host:9876"
Dupa ce este creat,serverul software poate fi deschis,pentru a astepta
conexiunile de tip client.Daca serverul este deschis si se creaza un co-
nector de tip client pe care se realizeaza o conexiune,se deschide o noua
sesiune de comunicatie.Pentru a semnaliza aceste operatii,serverul MBean
poate adauga si Remote Listeners ( cu addNotificationListener).
-174-
Asadar,pentru un sistem complet sunt necesare urmatoarele programe:
o interfata MBean,o clasa MBean,un server MBean si un program client.
EXEMPLU: INTERFATA MBEAN
CLASA MBEAN
import javax.management.*;
public class Sample implements SampleMBean {
private String hello = "Hello World(s)!";
public Sample() {}
public String getHello() { return hello; }
}
-salvati fila cu numele Sample.java si compilati fila
import javax.management.MbeanServer;
import java.lang.management.ManagementFactory;
import javax.management.ObjectName;
import javax.management.remote.*;
-175-
Aplicatia client,ruleaza pe unul sau mai multe terminale din retea,de
obicei altele decat cel pe care ruleaza aplicatia server.Pentru a exem-
plifica insa modul de lucru,in exempul urmator vom include atat serverul
cat si clientul in acelasi exemplu (pentru a garanta conexiunea software).
APLICATIA CLIENT
import javax.naming.*;
import javax.management.*;
import java.lang.management.ManagementFactory;
import javax.management.remote.*;
import java.util.*;
import java.io.IOException;
-176-
Pachetul javax.management include si un mecanism complex de anunturi,
prin care orice modificari ale atributelor din clasa MBean pot fi tran-
smise automat catre aplicatia de comanda.Acest gen de anunturi poarta
numele de notificari si necesita un set intreg de clase si interfete.
Astfel,pentru a putea crea anunturi,clasa MBean trebuie sa implementeze
si o interfata de tip NotificationEmitter sau NotificationBroadcasterSup-
port,iar pentru a putea transmite aceste anunturi trebuie sa includa si
un obiect de tip Notification.La randul sau,aplicatia ce primeste aceste
anunturi,trebuie sa contina un obiect de tip Listener special creat pentru
a receptiona aceste anunturi.
EXEMPLU: INTERFATA MBEAN
public interface HelloMBean {
public int getCacheSize();
public void setCacheSize(int size);
}
-salvati fila cu numele HelloMBean.java si compilati fila.
CLASA MBEAN
import javax.management.*;
-177-
APLICATIA DE CONTROL (managerul clasei MBean)
import java.util.*;
import java.io.IOException;
import javax.management.*;
import javax.management.remote.*;
import javax.naming.*;
import java.lang.management.ManagementFactory;
-178-
echo("\n Se asteapta notificarea....");
try {
Thread.sleep(2000);
} catch (InterruptedException e){};
waitForEnterPressed();
echo("\n Se inchide conexiunea cu serverul...");
jmxc.close();
server.stop();
echo("\n Bye! Bye!");
waitForEnterPressed();
}
private static void echo(String msg) {
System.out.println(msg);
}
private static void waitForEnterPressed() {
try {
echo("\n Press <Enter> to continue...");
System.in.read();
} catch (IOException e) {}
}
-salvati fila cu numele Monitor1.java.Compilati si executati.
In exemplul de mai sus,executia este fragmentata in mai multe etape,
cu ajutorul functiei waitForEnterPressed.Puteti verifica modul in care
se executa anunturile (notificarile) in mai multe moduri:
1. rulati exercitiul pana la capat.Valoarea pentru cacheSize va fi rese-
tata in cadrul exercitiului si dupa o usoara intarziere va veni si
notificarea emisa de clasa MBean
2. porniti in executie Monitor1,pana la pornirea serverului si stabilirea
conexiunii.Apoi,deschideti in alta fereastra aplicatia JConsole si
faceti conexiunea cu Monitor1.Alegeti MBeans,apoi DefaultDomain si
din nodul Hello alegeti Attributes,apoi CacheSize.Modificati de cateva
ori valoarea si confirmati cu butonul Refresh.Observati in Monitor1
anuntul inregistrat.Eventual studiati si nodul Notifications.
3. Creati o clasa noua in care construiti un obiect de tip Hello si un
algoritm pentru resetarea valorii cacheSize.Lansati in executie in
ferestre diferite,noua clasa si clasa Monitor1.Resetati valoarea pentru
cacheSize in noua clasa si observati notificarile din calsa Monitor1.
La prima vedere acest mecanism pare destul de greoi,dar cu putin exerci-
tiu anunturile vor fi din ce in ce mai usor de controlat.Prin acest meca-
nism simplu se pot realiza algoritmi pentru diverse operatii automate,
roboti,motoare de cautare,sisteme de avertizare si control,rutine pentru
actualizare automata,aplicatii pentru automatizarea unor procese,manage-
ment de tip "remote control"...etc.Cu alte cuvinte,numarul de aplicatii
posibile este foarte mare si ofera o intreaga paleta de operatii greu de
realizat in alte limbaje de programare.
Pentru a controla si mai strict modul in care se fac anunturile,se
pot utiliza obiecte de tip Monitor.Aceste obiecte monitorizeaza un anumit
atribut si nu trimit notificarile decat in situatia in care se indepli-
neste o anumita conditie (actioneaza ca un fel de filtru software,prin
care se limiteaza notificarile,doar la situatiile semnificative).Exista
trei tipuri de monitoare : numarator(int),contor(int),comparator de text.
-179-
Pachetul javax.management.monitor contine trei astfel de monitoare
specializate: CounterMonitor,GaugeMonitor si StringMonitor.Aceste clase
sunt insa mai putin recomandabile,din urmatoarele motive:
-nu au o interfata grafica facila
-este necesar cate un obiect separat pentru fiecare atribut MBeans
-mecanismul de setare este greoi si ineficient
-consuma nejustificat de multa memorie de operare
Acelasi rezultat poate fi obtinut cu o simpla bucla IF...si o notifi-
care simpla.
EXEMPLU: in exemplul precedent,modificati in clasa Hello,functia prin
care se seteaza valoarea atributului astfel:
public synchronized void setCacheSize(int size) {
this.cacheSize = size;
System.out.println("Valoarea actuala CacheSize: "+this.cacheSize);
Notification n = new Notification("Notificare: ",this,
sequenceNumber,"CacheSize a depasit valoarea limita !");
if (this.cacheSize > 400) {
sendNotification(n);
try { Thread.sleep(2000) } catch (InterruptedException e){};
}}
Cu ajutorul unor astfel de algoritmi simpli,un singur obiect poate
monitoriza mai multe atribute simultan,sau poate utiliza mai multe seturi
succesive de filtrare a datelor.In majoritatea situatiilor,este mai comod
sa programati obiectelele necesare pentru monitorizare,decat sa utilizati
cele trei clase predefinite.
Pentru ca mecanismul sa fie complet,trebuie ca la receptia unei astfel
de notificari sa fie declansata o functie oarecare de raspuns la notifi-
care.Astfel,notificarea nu va fi emisa decat daca atributul urmarit inde-
plineste o anumita conditie,iar in momentul in care o astfel de notificare
a fost receptionata in aplicatia client,trebuie sa fie decalansata o ruti-
na automata prin care sa fie corectata situatia creata.De exemplu,se poate
declansa o functie prin care atributul este resetat automat la o valoare
acceptabila,se poate declansa un sistem de alarma,sau se poate pune in
actiune un set oarecare de operatii de raspuns la notificare.
Nu este necesar sa programati singuri toate rutinele necesare pentru
monitorizarea aplicatiilor java sau pentru supravegherea Virtual Machine.
Exista sute de astfel de aplicatii,oferite in sistem freeware sau share-
ware.Puteti alege oricare dintre acestea,in functie de necesitati sau in
functie de bugetul disponibil.De exemplu,in locul utilitarului jconsole,
se poate utiliza cu succes aplicatia denumita VisualVM 1.1.1.Aceasta este
realizata cu NetBeans si ofera o interfata grafica pentru monitorizarea
parametrilor Java VM,a sistemului de operare instalat,a memoriei de ope-
rare sau a thread-urilor active din sistem.Programul este oferit in regim
freeware si este ideal pentru verificarea aplicatiilor create cu NetBeans.
In general,este recomandabil sa utilizati un astfel de program,inainte
de a pune in circulatie programele realizate de d-voastra.Prima operatie
simpla,este controlul memoriei consumate.Lansati in executie aplicatia
creata de d-voastra,apoi executati toate operatiile posibile si urmariti
consumul de memorie/operatie.Este recomandabil ca memoria consumata,sa nu
depaseasca niciodata mai mult de 50 din cea accesibila.Restul de operatii
de control depind de complexitatea aplicatiei verificate.
-180-
JAVA NETWORKING (operatii in retea)
-181-
Protocolul de tip TCP realizeaza o conexiune de tip telefonic,in care
ambele unitati trebuie sa fie prezente si active,pentru a putea realiza o
conexiune.Acest protocol este rapid,sigur si eficient,dar nu poate trans-
mite date spre o unitate inactiva.
Protocolul UDP,este asemanator cu sistemul postal.Transmite datele
spre o adresa oarecare,unde acestea vor fi plasate pe o linie de asteptare
pana cand se poate realiza conexiunea neceasara.In acest caz,nu se garan-
teaza ordinea si rapiditatea transferului,dar pachetul de date poate fi
transmis chiar daca unitatea de destinatie este inactiva.
Clasele din pachetul java.net sunt independente de protocolul de comu-
nicatie,asa ca nu este necesar sa analizati prea amanuntit acest aspect.
Fiecare calculator conectat la retea are o singura conexiune fizica,dar
poate comunica simultan cu doua sau mai multe calculatoare din retea.
Pentru a putea realiza acest tip de comunicatie multipla,intre conexiunea
fizica si unitatea de lucru se interpun un numar oarecare de tampoane de
memorie,denumite "porturi de comunicatie".Fiecare astfel de port,poate
sa organizeze comunicatia dintre o anumita aplicatie si o anumita resursa
din retea.Astfel,fiecare calcualtor sa va identifica printr-o adresa de
tip IP (de 32 de biti) si un numar de porturi (codificate cu 16 biti).
Daca mai multe porturi sunt activate simultan,datele primite sau emise
vor fi transmise succesiv (serial).Practic,portul este utilizat pentru a
mentine datele pe "linie de asteptare",pana cand se poate efectua trans-
ferul.Exista 65535 de astfel de porturi programabile,dintre care primele
1024 (de la 0 la 1023) sunt rezervate pentru serviciile HTTP si FTP.
Din reteaua Internet,cea mai frecvent accesata de marele public este
reteaua Web ce utilizeaza protocolul de transfer denumit HTTP (Hipertext
Transfer Protocol).In aceasta retea,se exploateaza predominent file edi-
tate in format hipertext de tip HTML si XML.Fiecare protocol de comunica-
tie utilizeaza un anumit sablon pentru adresa resurselor din retea.
Adresa resursei poarta numele de URL (Uniform Resource Locator) iar
clasa java utilizata pentru identificarea unei astfel de adrese poarta
acelasi nume.O astfel de adresa se formeaza din doua parti:
1. identificatorul pentru protocol
2. numele resursei
EXEMPLU: http://java.sun.com unde:
http -este identificatorul tipului de protocol
//java.sun.com -este numele resursei
La randul sau,numele resursei se poate forma din:
1.Host Name - numele unitatii de lucru ce arhiveaza resursa
2.Filename - calea software spre fila ce contine resursa
3.Port Number - numarul portului utilizat pentru conexiune
4.Reference - un pointer spre adresa resursei in cadrul filei
In majoritatea protocoalelor sunt necesare doar primele doua (unitatea
si calea software spre resursa) in timp ce ultimele doua sunt optionale
(portul si pointerul spre memorie).
Pentru a crea o astfel de adresa URL,se va apela constructorul clasei
URL in care adresa propriu zisa se reprezinta sub forma de String.
EXEMPLU: pentru adresa http://www.gamelan.com
se va utiliza o linie de cod de genul:
URL gamelan = new URL("http://www.gamelan.com/");
(Site-ul gamelan.com contine informatii utile pentru programatorii Java.)
-182-
Clasa URL detine 6 constructori diferiti,ce permit diferite solutii de
formare a unei adrese de tip URL.Standardul de sintaxa pentru adresele URL,
este continut in documentele: RFC 2396 si respectiv 2732.
EXEMPLU: pentru a include in adresa si portul de comunicatie,se poate
utiliza un constructor de tipul:
URL(String protocol,String host,int port,String file);
EXEMPLU: URL url = new URL("http","gamelan.com",80,"game.html");
Este posibil chiar ca adresa sa fie creata prin referire la o adresa
relativa creata anterior:
EXEMPLU: adresa http://www.gamelan.com/pages/Gamelan.game.html
se poate crea si astfel:
URL gamelan = new URL("http://www.gamelan.com/pages/");
URL gamelanGames = new URL(gamelan,"Gamelan.game.html");
In acest caz,s-a apelat constructorul de tip:
URL(String context,String spec);
Exista si situatii in care adresa URL contine caractere speciale.In
acest caz,este necesar ca aceste caractere sa fie recodificate conform cu
standardul din RFC 2396.Pentru a simplifica acest proces,este recomandabil
sa utilizati clasa URI (Uniform Resource Identifier),special creata pentru
acest scop.
EXEMPLU: adresa: http://foo.com/hello world/
contine un spatiu gol ce trebuie recodificat (adaugand %20).Adresa se
va putea crea astfel:
URL url = new URL("http://foo.com/hello%20world");
sau se va putea apela clasa URI astfel:
URI uri = new URI("http","foo.com","/hello world/","");
URL url = uri.toURL();
Clasa URI are la randul sau 5 constructori diferiti si metode speciali-
zate pentru a putea compune sau decompune si analiza orice adresa.
Toti constructorii elibereaza o exeptie de tip MalformedURLException in
cazul in care a intervenit o eroare de sintaxa.Aceasta exceptie va putea
fi exploatata pentru functiile de depanare automata.
Analiza unei adrese URL se poate face cu ajutorul metodelor speciali-
zate: getProtocol,getAuthority,getHost,getPort,getPath,getQuery,getFile,
getRef...etc.
Exista o serie intreaga de obiecte Java ale caror metode solicita o
adresa de tip URL,chiar si in cazul in care resursele apelate sunt arhi-
vate local.In paginile anterioare au fost deja prezentate cateva exemple
de acest fel.
Dupa crearea unei adrese URL,aceasta poate fi utilizata pentru ope-
ratii de tip I/O.Clasic se va utiliza un stream specializat in care se
vor citi datele cu un BufferedReader (un tampon de memorie dedicat).
Dupa deschiderea streamului,datele pot fi preluate in tamponul de memorie,
fie integrale,fie filtrate cu ajutorul unor obiecte speciale sau al unor
algoritmi special realizati.Cu un astfel de mecanism elementar se poate
crea un browser simplu de Internet,sau se pot crea utilitare simple ce
extrag doar un anumit tip de data,dintr-o pagina de tip HTML.
Datele preluate pot fi prelucrate direct in aplicatii,sau pot fi uti-
lizate pentru a crea alte file de tip HTML,sau pentru a declansa o serie
de rutine de comanda automata...etc.Cel mai simplu exemplu,extrage pur si
simplu toate datele dintr-o pagina de web.
-183-
EXEMPLU:
import java.net.*;
import java.io.*;
-184-
Pentru a realiza o conexiune permanenta intre doua calculatoare din
retea se utilizeaza obiecte specializate.Cea mai simpla conexiune se poate
realiza cu ajutorul unui obiuect de tip URLConnection:
EXEMPLU:
try {
URL yahoo = new URL("http://yahoo.com/");
URLConnection yahooConnection = yahoo.openConnection();
yahooConnection.connect();
} catch (MalformedURLException e) {}
catch (IOException e) {}
In cazul protoculului TCP/IP,obiectele pentru conexiune poarta numele
de socket-uri.In cazul unitatilor de tip server,obiectele utilizate pentru
conexiune poarta numele de ServerSocket.
Clasa Socket are 9 constructori diferiti.In principiu,obiectul Socket
face conexiunea software dintre cele doua calculatoare (fata de conexiunea
fizica realizata prin cabluri sau dispozitive wireless).La apelarea unuia
dintre constructori se va preciza explicit adresa unitatii in retea,sau
adresa resursei cautate si portul de comunicatie dorit,iar pentru Server-
Socket poate fi suficient doar numarul portului de comunicatie:
EXEMPLU:
try{
Socket socket1 = new Socket("Unitatea1",2221);
Socket socket2 = new Socket(127.0.0.1,2222);
ServerSocket socket3 = new ServerSocket(4444);
} catch (UnknownHostException e1) { ...}
catch (SecurityException e2) { ...}
catch (IOException e3) { ... }
In cazul in care se lucreaza cu un numar foarte mare de conexiuni ce
trebuiesc formate si inchise automat,in mod dinamic,se pot utiliza clasele
SocketFactory si ServerSocketFactory din pachetul javax.net.
In mod normal,nu trebuie sa va complicati viata cu aceste obiecte.Orice
browser de Internet va crea si/sau inchide automat socket-urile sau
obiectele necesare pentru realizarea conexiunii.Are rost sa utilizati
aceste obiecte,doar atunci cand doriti sa creati aplicatii independente,ce
se pot conecta la retea fara intermediul unui browser,sau atunci cand
doriti sa programati un browser nou.Platforma Java nu include obiecte
vizuale de tip Socket,dar puteti consulta oferta de pe Internet,pentru
astfel de pachete auxiliare.
In cazul protocolului UDP,obiectele pentru conexiune sunt:DatagramSocket
DatagramPacket si MulticastSocket.Si aceste obiecte au un numar destul de
mare de constructori,pentru a permite formule destul de personalizate de
conectare la retea.Detaliile referitoare la modul de conectare si la ope-
ratiile I/O din retea,nu fac obiectul acestui manual,deoarece autorul nu
poate sa-si asume raspunderea pentru eventualele solutii oferite.Politica
locala pentru operatiile din retea,poata sa fie diferita de la o retea la
alta,sau chiar de la o luna la alta.Pentru detalii suplimentare este bine
sa va consultati si cu administratorul de retea.
In principiu,protocolul TCP/IP este de preferat in cazul unui schimb
activ de date,intre doua unitati (date transmise + date receptionate) in
timp ce protocolul UDP este de preferat atunci cand doriti doar sa trimi-
teti date,catre mai multi receptori(fara sa asteptati un raspuns imediat).
-185-
SECURITATEA APLICATIILOR JAVA
-186-
EXEMPLU:
import java.awt.*;
import java.io.*;
import java.lang.*;
import java.applet.*;
-187-
Pentru verificare,fereastra Policy Entry trebuie sa contina urmatorul
text: java.io.FilePermission writetest,"write"
java.security.SecurityPermission writetest
Confirmati cu butonul Done.Pentru a salva fila,alegeti in fereastra
Policy Tool meniul File,apoi Save As si navigati in browser.In mod normal
filele de tip policy se vor arhiva in directorul jre/lib/security
EXEMPLU:
C:\Program Files\Java\jre1.6.0_03\lib\security\AppletSecurity1Policy
Pentru ca applet-ul sa poata fi lansat,este necesar ca la prima lansare
sa specificati si numele filei de tip policy ce garanteaza operatiile.
Pentru acest scop,editati in Command Prompt o comanda de genul:
appletviewer -J-Djava.security.policy=AppletSecurity1Policy Security1.html
Acum applet-ul este executat.Puteti verifica fila locala writetest.
In continuare,fila HTML va putea fi executata cu o comanda simpla:
appletviewer Security1.html
Macanismul pare destul de complicat,dar permite utilizarea unui applet,
pentru a putea actualiza de exemplu,o fila locala,cu date preluate dintr-o
fila Web arhivata in reteaua Internet.
Daca descarcati din reteaua Internet aplicatii Java,puteti sa intalniti
programe sau aplicatii ce utilizeaza astfel de applet-uri si este bine sa
stiti cum sa editati certificatul pentru garantarea accesului.O astfel de
aplicatie,prezinta urmatorul avantaj: puteti utiliza applet-ul in mediul
din retea,fara ca alt utilizator din retea sa poata copia sau utiliza
fila HTML ce contine applet-ul.Cu alte cuvinte,un alt utilizator din
retea nu va putea avea acces la filele locale din calculatorul d-voastra.
In exemplul de mai sus,am omis o serie intreaga de etape.Astfel,in
fereastra Policy Entry puteti utiliza caseta CodeBase pentru a garanta
doar applet-urile ce provin de la o anumita adresa din retea,sau puteti
utiliza caseta SignedBy,pentru a garanta doar applet-urile ce poarta
semnatura digitala a autorului respectiv.Daca lasati ambele casete necom-
pletate,polita va avea efect,indiferent de locatia applet-ului si fara sa
se mai verifice semnatura digitala.
Daca doriti,puteti deschide fila policy cu un editor de text,pentru
a verifica ce contine.Eventual,puteti chiar sa editati astfel de file
manual.
Masurile de securitate pentru applet-uri sunt utile si bine gandite.
Nu are rost sa evitati aceste masuri,decat in situatii exceptionale,si
doar pentru aplicatii destinate uzului personal.Garantarea accesului la
operatii de tip read/write reprezinta un hazard pe care vi-l asumati pe
risc propriu.Un utilizator banal nu va putea abuza de applet-ul d-voastra,
dar un "hacker" experimentat va putea obtine o cale de acces spre sistemul
d-voastra de operare.In plus,in cazul in care doriti sa oferiti aplicatia
d-voastra si pentru alti utilizatori,determinati expunerea lor al aceeasi
tip de hazard.
Toate operatiile executate cu ajutorul applet-urilor pot fi programate
si cu obiecte de tip API.Nu exista situatii care sa impuna utilizarea
de applet-uri.Applet-urile sunt extrem de apreciate pentru portabilitatea
lor (pot fi preluate facil in orice site de web),dar tocmai aceasta cali-
tate,reprezinta un impediment in cazul operatiilor ce prezinta un risc
oarecare pentru securitatea sistemului.Opinia autorului este sa nu apelati
la acest gen de solutii,decat in situatii evaluate foarte atent.
-188-
Aceleasi reguli se pot extrapola si in cazul aplicatiilor simple.In
mod normal (implicit),aplicatiile simple nu sunt supuse la nici un fel de
restrictii de executie.Pentru a introduce un set oarecare de astfel de
restrictii,trebuie ca aplicatia sa fie executata intr-un anumit context
de memorie,impreuna cu o fila denumita "security manager",prin care sa
fie explicitate restrictiile dorite.Fila de tip security manager,va deter-
mina citirea unei file de tip "policy",in care sunt specificate drepturile
garantate pentru aplicatia respectiva.De exemplu,pentru a utiliza in cazul
unei aplicatii simple aceleasi restrictii ca si in cazul applet-urilor,se
poate utiliza managerul de securitate implicit si fila policy implicita
din pachetul: jre1.6.0_03\lib\security (java.security si java.policy)
EXEMPLU: Lansati aplicatia cu o comanda de genul:
java -Djava.security.manager NumeleAplicatiei
in continuare se procedeaza exact la fel ca in cazul applet-urilor.Se
editeaza cate o fila de tip policy,in care se garanteaza drepturile de
acces.Fiecare client,va putea avea o astfel de fila policy personalizata,
astfel incat sa utilizeze din aplicatie doar atat cat este strict necesar.
Cu acest gen de solutie,se poate personaliza dreptul de acces la o anumita
fila,la o anumita proprietate a sistemului de operare sau la o anumita
resursa din retea.
O alta modalitate de abordare,o reprezinta semnaturile digitale.Exista
situatii in care doriti ca un anumit client sa poata verifica identitatea
unei anumite surse de informatii,inainte de a avea acces la date.Daca
datele provin de la o anumita sursa,clientul va putea citi datele,iar in
caz contrar se va aplica o anumita masura de securitate.Acest gen de so-
lutie se poate crea si cu ajutorul unor parole simple,dar este mult mai
eficient daca se utilizeaza obiecte complexe.O parola de tip string,poate
fi decodificata de un program performant in cateva secunde,in timp ce un
obiect complex este practic imposibil de decodificat.Pentru situatiile
obisnuite,obiectul contine tot o parola de tip String,dar asociaza la
acest sir de caractere si un cod unic,ce identifica o anumita masina din
retea,sau o anumita instanta a programului ce a creat obiectul.In acest
mod,doua obiecte similare,create pe calculatoare diferite,sau cu instante
diferite ale aceluiasi program,vor fi diferite.
Semnatura digitala,este de fapt un obiect de sine statator.Pentru a
autentifica originea unui anumit pachet de date,este necesar ca in acest
pachet sa existe si obiectul utilizat ca semnatura.Acest obiect poate fi
creat de d-voastra,sau puteti utiliza unul dintre obiectele standardizate
oferite de pachetul java.security,sau respectiv javax.security.
EXEMPLU:
import javax.security.auth.AuthPermission;
class Sursa1 {
public static AuthPermission Semnatura = new AuthPermission("Parola");
public static String Date = new String("Datele din sursa1...");
}
-salvati fila cu numele Sursa1.java si apoi compilati fila.
In acest exemplu,semnatura digitala este reprezentata de obiectul de
tip AuthPermission,iar datele propriu zise sunt incluse in string-ul Date.
Fila Sursa1.class poate fi expediata unui client oarecare,ce va putea
verifica semnatura,cu o aplicatie de genul:
-189-
import javax.security.auth.AuthPermission;
class VerificaSemnatura {
public static void main(String[] args) {
AuthPermission Semnatura2 = new AuthPermission("Parola");
if(new Sursa1().Semnatura.getName() == Semnatura2.getName())
{
System.out.println("Se citeste sursa...");
System.out.println(new Sursa1().Date);
}
else
{ System.out.println("Semnatura este incorecta !"); }
System.out.println(" ...s-au citit datele solicitate !");
}}
-salvati fila cu numele VerificaSemnatura.java si compilati fila.
Pentru a citi datele din Sursa1.class,executati fila VerificaSemnatura.
La prima vedere,obiectul Semnatura este identic cu un String obisnuit.
Insa,daca incercati sa utilizati un string simplu,fila nu va fi recunos-
cuta ca valabila.Mai mult decat atat.Presupunand ca stiti parola si in-
cercati sa creati un obiect ce contine semnatura digitala pe un alt cal-
culator decat cel sursa,semnatura nu va fi validata.
Pentru a intelege acest mecanism,deschideti fila sursa pentru obiectul
AuthPermission (din pachetul src/javax/security/auth).Observati ca in
constructorul clasei se include si un cod denumit serialVersionUID.Acest
cod identifica originea semnaturii digitale.Exista cate un astfel de cod,
pentru fiecare obiect standard din java.security,astfel ca semnaturile
digitale sunt foarte greu de falsificat.
Mecanismele standard pentru java.security sunt mult mai complexe decat
atat.In mod curent se utilizeaza seturi intregi de obiecte ce interactio-
neaza dupa un algoritm oarecare.In locul unei simple semnaturi,se creaza
cate un obiect complex,de tip Subject,pentru fiecare client.In acest
obiect de tip Subject,se includ toate datele de identificare ale fiecarui
client,precum si restrictiile sau drepturile garantate.
Mai mult decat atat,in obiectul Subject clientul poate sa figureze cu
mai multe nume,sau denumiri generice de tip alias.Pentru fiecare astfel
de nume,va avea o interfata de tip Principal.Astfel,un singur client poate
avea mai multe seturi de depturi de acces,in functie de numele utilizat
pentru conectare (logg-are).Drepturile de acces,se vor putea reglementa
fie prin file de tip policy,fie prin algoritmi special conceputi.
Acest gen de solutii software,fac obiectul firmelor specializate in
securizarea datelor.Atunci cand aveti nevoie de o astfel de solutie,este
preferabil sa apelati la o astfel de firma.Nu trebuie uitat ca in ultima
instanta,comutatia se executa fizic,intr-o centrala din retea.
Nu puteti implementa nici un fel de masura de securitate,care sa elimine
complet responsabilitatea administratorului de retea.Obiectele din cele
doua pachete "security",nu fac decat sa simplifice operatiile de comutatie
automata (sau manuala).Nici una dintre masurile de securitate software nu
poate avea eficienta,decat daca este implementata si prin hardware.
In concluzie,pentru situatiile simple este recomandabil sa utilizati
solutii cat mai simple,iar pentru situatiile complexe este mai bine sa
apelati la (sau sa colaborati cu ) firme specializate.
-190-
FINALIZAREA APLICATIILOR
-191-
Daca alegeti varianta "open source",este recomandabil sa adaugati si
filele sursa (.java) si indicatii minimale pentru depanare si control.
In mod normal,codul vorbeste de la sine si este suficient sa adaugati o
fila in care precizati ca programul este distribuit in regim "freeware".
Chiar daca programul este "open source",nu inseamna ca sunteti degrevati
de orice responsabilitate (chiar daca adaugati o fila prin care declarati
ca nu va asumati nici o raspundere).Daca aveti cele mai mici dubii refe-
ritor la calitatea programului,este mai bina sa nu-l puneti in circulatie.
2.Depanare
Primele operatii de depanare sunt efectuate de catre compilator.La
acestea se poate adauga si utilitarul specializat (debugg-er).Daca apli-
catia este functionala si compilatorul nu identifica nici o eroare,nu
inseamna insa ca programul este perfect.Pot exista o serie intreaga de
situatii limita,ce vor genera erori de executie,sau chiar vor bloca exe-
cutia programului,dar numai in momentul in care se indeplinesc anumite
conditii.Cele mai frecvente situatii de acest gen sunt:
-se depaseste capacitatea unui tampon de memorie
-se apeleaza un tip de data incompatibil
-se depaseste domeniul de valori al unei anumite variabile
-se introduc date eronate,incomplete,defective,nule...etc
-se apeleaza actioneaza concurential asupra unei resurse determinand modi-
ficarea datelor sau a tipului de data prezentat
-se pierde sau se modifica pointerul spre o resursa
-se pierde sau se degradeaza conexiunea (link-ul) cu una dintre resurse
-se declanseaza o bucla de control infinita (fara conditie de stop)
-se depaseste capacitatea de lucru a procesorului,sau a sistemului de
operare
-se declanseaza seturi de operatii ce se anuleaza reciproc
-se cauta la infinit o resursa apelata grasit (sau relocata)
-se comuta executia intr-un spatiu de vizibilitate gresit
-se supraincarca memoria de operare
Toate aceste situatii nu pot fi identificate de compilator si nu se vor
exprima decat in conditii extreme.Pentru a elimina orice situatie de acest
gen,este bine sa testati programul utilizand valorile maximale.Pentru
situatiile ce se pot preta la confuzii,este recomandabil sa utilizati
bucle conditionale.Exemplu: programul asteapta date de tip numeric,dar
utilizatorul introduce din greseala date de tip text.In mod normal in
acest caz,se va declansa o eroare de tip si programul se va intrerupe.Cu
o bucla conditionala,introducerea textului va declansa doar un mesaj de
avertizare catre client.
Daca este vorba despre un program important,este bine sa-l oferiti
pentru testare spre cat mai multi utilizatori neavizati,sau chiar sa
apelati la o firma specializata in testarea si depanarea aplicatiilor.
3.Optimizare
Dupa primele doua etape,programul este functional si corect.Nu inseamna
insa ca este finalizat.In etapa urmatoare,se pot face o serie intreaga de
modificari,astfel incat executia sa fie mai rapida,memoria consumata sa
fie mai redusa si sa fie eliberata mai frecvent,expresiile sa fie mai
clare si mai precise,codurile sa fie mai condensate,modulele sa fie mai
performante...etc.Optimizarile pot interesa module intregi,sau se pot
aplica doar la nivel de linie de cod,sau expresie.
-192-
Primele modificari se pot face la nivel de expresie si implica utili-
zarea unor cuvinte cheie,denumite modificatori.Cu ajutorul acestora se
va putea determina modul in care obiectul generat se va comporta in unele
situatii mai speciale: datele sunt apelate dintr-o clasa externa,executia
se face concurential,in sistem multi-threading,se lucreaza cu serii de
obiecte...etc.
In general,modificatorii sunt cuvinte cheie prin care compilatorul
primeste informatii suplimentare despre coduri,sau obiecte si clase.
O parte dintre modificatori reglementeaza accesul la date: public,private
si protected,in timp ce alti modificatori descriu un anumit atribut sau
o anumita caracteristica: final,abstract,static,native,transient,volatile,
synchronized.
Modificatorii de acces (public,private si protected) reglementeaza
vizibilitatea datelor fata de restul spatiilor de memorie.Au aceeasi sem-
nificatie ca si in celelalte limbaje de programare:
-public : clasele,variabilele si metodele de acest fel pot fi utilizate
de catre orice program Java,fara restrictii.Metodele main() se declara
public,pentru a putea fi apelate din orice mediu Java.
-private : clasele principale nu pot fi declarate private.Variabilele si
metodele declarate private nu pot fi apelate decat din clasa respectiva.
-protected : nu se poate utiliza pentru clase.Metodele si variabilele de-
clarate protected sunt vizibile pentru toate clasele din acelasi pachet.
Mai mult decat atat,sun vizibile si pentru sub-clasele derivate din acea
clasa,chiar daca sunt declarate in alte pachete.In acest caz insa,datele
de tip protected nu pot fi utilizate decat in urmatoarele situatii:
-clasa descendenta poate redefini metoda de tip protected
-clasa descendenta poate scrie si citi datele protected din instanta
respectiva,dar nu poate scrie sau citi datele protected dintr-o alta
instanta a clasei respective
-clasa descendenta poate apela datele protected mostenite,in cadrul
clasei,dar nu poate apela datele protected din alta instanta a clasei
Acest mecanism,genereaza foarte frecvent nelamuriri,sau erori de exe-
cutie.Este bine sa nu apelati datele protected,decat din interiorul
clasei,la fel ca pe cele private.
Modificatorii de atribut au urmatoarea seminificatie:
-final : insemana ca datele respective nu mai pot fi schimbate.Se poate
utiliza atat pentru clase,cat si pentru metode si variabile.Datele
rezultate vor fi de tip constanta.De exemplu,o clasa declarata final,
nu mai poate fi extinsa sau redefinita,sau o variabila declarat final
nu mai poate schimba valoarea alocata.
-abstract : se utilizeaza pentru clase si metode la care nu se specifica
si definitia.Clasele abstracte nu pot crea instante (obiecte),decat
dupa ce au fost extinse cu definitiile necesare.Daca o clasa contine o
singura metoda abstracta,compilatorul va solicita sa fie declarata tot
abstracta.Clasele abstracte sunt destinate exclusiv ca sablon.
-static : se aplica pentru metode si variabile,atunci cand doriti sa
precizati ca apartin unei anumite clase,indiferent de instanta din care
vor fi apleate.Pentru o anumita clasa,exista o singura variabila sau
metoda de tip static,indifernt cate obiecte se vor deriva din clasa
respectiva.Atunci cand se apeleaza din instante diferite,datele se vor
suprascrie in aceeasi variabila.
-193-
EXEMPLU: class Clasa1 { static int = 0;
Clasa1(){x++;};
}
apoi in alt program....
Clasa1 c1 = new Clasa1();
Clasa1 c2 = new Clasa1();
c1.x = 100;
c2.x = 200;
int numar = c1.x;
...............
Valoarea pentru numar,va fi 200,deoarece exista o singura variabila x
si ambele instante suprascriu aceeasi variabila.
Metodele declarate static nu pot fi apelate cu this.Este esential sa
fie apelate specificand si instanta (obiectul).In mod similar,atunci cand
o metoda statica trebuie sa apeleze o variabila nonstatica,este necesar
sa se specifice si instanta variabilei respective.Si acest mecanism poate
genera numeroase erori de executie (instanta neprecizata).
In ce priveste importul din alta fila: datele private nu pot fi impor-
tate static,cele protected pot fi importate doar in acelasi pachet,iar
cele publice pot fi importate static si din alt pachet.
-native : se poate utiliza doar pentru metode.Este asemantor cu abstract
si specifica faptul ca definitia metodei se gaseste in alta parte,intr-o
biblioteca externa de Java Virtual Machine.Se utilizeaza pentru coduri
scrise in alt limbaj decat Java (de obicei C si C++).
EXEMPLU: class NativeExample {
native void doSomethingLocal(int i);
static { System.loadLibrary("MyNativeLib"); }
}
-transient : se aplica numai pentru variabile,in cazul obiectelor ce
au persistenta in cadrul seriilor(Serializable objects).O variabila
de tip transient nu va fi arhivata in statusul obiectului persistent.
(vezi mecanismul de eliberare a memoriei Garbage Recycle Bin)
-volatile : se aplica doar pentru variabile si indica faptul ca pot fi
modificate asincron.Se utilizeaza doar in cazul mediilor de operare
cu mai multe procesoare ce opereaza asincron (uz limitat).
-synchronized : se utilizeaza pentru a controla modul de acces la unele
secvente critice de cod,in cazul programelor cu thread-uri concurente.
-194-
Expresiile pot fi simplificate si in cazul in care contin formule
matematice si calcule complexe.Pentru a evita erorile generate de pre-
cedenta operatorilor,sau de incompatibilitatea tampoanelor de memorie,
este recomandabil sa impartiti orice expresie complexa in expresii mai
mici,ce se executa succesiv.In acest caz,nu numai ca va creste viteza de
procesare,dar puteti extrage si valori intermediare,pentru verificarea pe
etape a rezultatului obtinut.
Atentie la unitatile de masura.Daca cuplati module preluate din pro-
grame diferite,este posibil ca unele sa utilizeze m si Kg,iar altele sa
lucreze cu pounds si iarzi.
O alta etapa de optimizare se refera la modul de editare al codului
propriu zis.Se pot elimina variabilele inutile,se pot elimima spatiile
goale inutile,iar codurile se pot aglomera in spatii mai restranse.Daca
in cazul programelor educative este bine sa lucrati cat mai aerisit,sa
explicati fiecare etapa si operatie si sa includeti in cod toata documen-
tatia necesara,in cazul programelor de lucru,este recomandabil sa limitati
comentariile la minimum necesar.Preferabil sa nu includeti decat datele
strict necesare pentru depanare,verificare si control.
In orice program mai mare,este recomadabil sa lucrati cu thread-uri.
Thread-urile vor fi astfel proiectate incat sa fie cat mai echilibrate.
Verificati cu atentie inchiderea fiecarui thread,asfel incat sa nu existe
fire de executie trenante,ce raman active in background fara sa mai exe-
cute nici o operatie.Astfel de thread-uri,blocheaza o parte din memoria
de operare cu referinte inutile.In mod similar,este bine sa eliberati din
memorie toate obiectele si resursele ce nu mai sunt utile pentru desfasu-
rarea programului.Cu cat exista mai putini pointeri spre memorie,cu atat
procesorul va executa orice operatie mai rapid.
In cazul in care utilizati seturi mai mari de evenimente si ascultatori,
este recomandabil sa alegeti cu atentie evenimentele,astfel incat sa se
evite orice confuzie.Exemplu: daca utilizati mai multe butoane,evenimentul
Click este discriminativ pentru fiecare dintre ele,dar daca utilizati
pentru un singur buton atat evenimentul MouseClick cat si MouseMove,pot
apare si situatii confuzive.
O atentie deosebita trebuie acordata containerelor,colectiilor si
seriilor de date (arii,liste,tabele etc.).In toate aceste situatii,in mod
curent se lucreaza cu rutine automate pentru crearea,sortarea,actualizarea
si eliberarea obiectelor.Orice eroare,poate afecta sute sau mii de obiecte
cu implicatii majore asupra executiei programului.
Nu in ultimul rand,trebuiesc verificate cu atentie buclele de control.
Atunci cand parametrul ce controleaza inchiderea ciclului nu este destul
de clar si discriminativ,este bine sa implementati doua sau mai multe
solutii alternative de iesire din ciclu (pentru a evita buclele cu exe-
cutie infinita).
Nu ezitati sa consultati manuale si articole de specialitate,sau sa va
consultati cu alti programatori.Pentru fiecare tip de problema,exista deja
seturi standardizate de solutii.Nu trebuie decat sa alegeti solutia cea
mai buna pentru aplicatia d-voastra.O serie de tutoriale,sfaturi utile si
solutii concrete puteti gasi la adresele: www.java-tips.org si respectiv
www.1001javatips.com.Mai mult decat atat,puteti apela online la sfatul
expertilor Java,apeland site-ul: www.LivePerson.com.Intotdeauna este bine
sa cereti si o opinie externa,obiectiva ("second opinion").
-195-
4. Modernizare
Platforma Java se modernizeaza in permanenta.La intervale mai mici sau
mai mari,apare o versiune noua,un pachet nou,sau instrumente de lucru noi
(tools).Cele cateva sute de milioane de utilizatori,produc in permaneta
ceva nou.Spre deosebire de alte limbaje de programare,Java este inca in
permanenta dezvoltare.Daca programul d-voastra este conceput modular,este
destul de usor sa actualizati cate un astfel de modul,fie cu un modul
gata editat,preluat din retea,fie cu un modul rescris de d-voastra.Daca
a aparut o versiune noua de Java,verificati daca nu exista incompatibili-
tati.In mod normal,versiunile noi accepta orice solutie anterioara,dar
ofera si solutii noi,mai performante.Acelasi mecanism,trebuie sa fie apli-
cabil si pentru programele d-voastra.Pe masura ce introduceti module sau
resurse noi,programul d-voastra va upgrada spre o versiune superioara,dar
versiunile initiale trebuie sa ramana functionale,chiar daca vor avea o
functionalitate mai limitata(atat timp cat mai exista utilizatori activi).
5.Verificare
Etapa de verificare a programului,este mai complexa decat pare la prima
vedere.In cazul in care programul este strict pentru uz personal,nu este
necesar sa adaugati nici un fel de masuri suplimentare.Insa,in cazul in
care programul este destinat unor clienti necunoscuti,trebuie sa va asi-
gurati ca programul va fi suficient de robust incat sa reziste la orice
ce fel de operatii gresite,accidentale sau voluntare.Pentru acest scop,
trebuie sa implementati cate o solutie de depanare automata a oricarei
situatii de acest gen.Exemplu: utilizatorul altereaza accidental conti-
nutul unei file de resurse.In momentul in care programul apeleaza resursa
respectiva,in loc sa se blocheze,va trebui sa afiseze un mesaj de eroare
si sa revina la etapa anterioara de lucru (executia trebuie sa poata con-
tinua,pentru a putea inchide programul).
Pe cat posibil,programul va executa automat toate operatiile de iden-
tificare si incarcare a resurselor,astfel incat clientul sa execute doar
cateva click-uri de mouse.Exista insa si situatii in care este indispensa-
bil ca utilizatorul sa execute singur unele dintre operatii.In toate
aceste cazuri se vor utiliza bucle de tip TRY....CATCH si metode de prelu-
crare si tratare automata a erorii.In acest manual nu au fost prezentate
decat mesajele de eroare receptionate si afisate de bucla CATCH.Insa,in
situatiile reale,bucla de tratare a erorii va contine codul necesar,fie
pentru corectare erorii,fie pentru revenirea la o etapa anteroara din
program,fie pentru inchiderea automata a programului in conditii de sigu-
ranta.Nu se pot preintampina chiar toate tentativele de fraudare sau for-
tare a programului,dar se pot prevedea toate erorile ce pot intervenii
accidental (se apasa o tasta gresita,se executa un click de mouse in alt
loc decat cel dorit,se executa click-uri accidentale,se introduc date
gresite,se copiaza o resursa gresita,se solicita o adresa gresita,se exe-
cuta concomitent rutine ce anuleaza o parte din date...etc.).
In mod normal,este bine ca verificarea sa fie facuta de alta persoana,
sau alt colectiv,decat cel care a editat programul.Daca exista o eroare
oarecare,este probabil ca programatorul initial sa o repete,fie pentru ca
are tabiet-uri gresite,fie pentru ca nu este sensibilizat sa o observe.
Verificarea se poate face manual,sau cu ajutorul unor programe speciali-
zate.In cazul programelor comerciale,este recomandabil sa apelati la firme
specializate.
-196-
6.Analiza finala
Dupa ce programul a trecut prin toate etapele de verificare,se poate
face o analiza finala(viteza de executie,memorie consumata,intarziere/ope-
ratie,volum de date prelucrate,numar de operatii executate,procese sau
thread-uri active,metode apelate,numar de click-uri sau taste apasate,
numar de obiecte utilizate,date rescrise sau actualizate...etc.).Pentru
acest gen de analiza,fie se introduc in program module specializate ce
semnalizeaza automat toate aceste date,fie se utilizeaza concomitent
programe ce analizeaza automat aplicatia aflata in executie ( cum este
de exemplu jconsole).
Exista cateva solutii ce pot grabi executia programului:
-resursele pot fi impachetate intr-o singura clasa (pentru acces facil)
-thread-urile pot fi grupate in "pools" (pentru control facil)
-filele pot fi comprimate in file JAR executabile
Daca doriti o analiza amanuntita a mediului de executie,puteti utiliza
optiunea Xprof a comenzii java.Pur si simplu,executati aplicatia cu o
astfel de optiune:
EXEMPLU: java -Xrunhprof Memorie1
Aplicatia se va executa normal,dar se va afisa si textul:
Dumping Java heap...allocation sites....done
Pentru a vedea rezultatul analizei,deschideti directorul curent al apli-
catiei si observati fila generata cu numele java.hprof.Fila are circa 3 Mb
si contine toate clasele si obiectele,sau locusurile (sites) alocate.In
plus,o descrie statistica a executiei la nivel de CPU si Monitor.
Exista o serie intreaga de programe,specializate pentru analiza si
managementul memoriei,al claselor si obiectelor,al thread-urilor sau
chiar al codurilor masina de la nivel de procesor.Descrierea acestor
programe nu face obiectul acestui manual,dar este bine sa stiti ca exista.
In situatii critice,sau litigioase,este recomandabil sa apelati la firme
specializate (nu incercati sa va faceti "dreptate" singuri).
7.Legalizare
Pentru a putea obtine "drepturi de autor" trebuie sa va organizati
legal sub forma de societate comerciala,inscrisa la Registrul Comertului.
Apoi,inregistrati fiecare program la OSIM si obtineti timbrul legal ce
trebuie anexat la fiecare copie vanduta.In mod normal,se apeleaza la
firme specializate,care se ocupa de distributie si de asigurarea dreptu-
rilor aferente (puteti cauta o astfel de firma prin reteaua Internet).
Pentru masurile de securizare a programului,este recomandabil sa va
adresati la o firma specializata.In acest fel,va puneti la adapost de
eventualele litigii,generate de masurile de securizare a programelor (cele
mai multe situatii litigioase sunt generate de "drepturile de acces").
Trebuie sa fiti sensibilizati ca raspundeti material si moral,pentru
orice program pus in circulatie,fie ca este comercial,fie ca este in
regim freeware.Totusi,programele oferite in regim "open source",au fost
expuse la litigii doar in ocazii extrem de rare,in majoritatea cazurilor
doar prin acuzatii de "furt intelectual ?".
Acest manual se adreseaza exclusiv incepatorilor.Pentru primele pro-
grame editate de d-voastra,este recomandabil sa nu va lansati la varianta
comerciala,decat daca va asociati cu persoane mai experimentate,sau cu
firme specializate.Indiferent de valoarea programului,se vor gasi clienti
si utilizatori abili,capabili sa va solicite diverse tipuri de "daune".
-197-
8.Impachetare
Pentru arhivarea corecta,dar si pentru operatiile de upload si down-
load din retea,este recomandabil ca aplicatiile sa fie cat mai compacte.
Se poate utiliza si un director simplu,dar este recomandabil sa compri-
mati toate filele intr-o fila de tip JAR.O astfel de fila,nu numai ca este
mult mai usor de manevrat,dar poate fi si functionala.Pentru a crea o
astfel de fila se utilizeaza comanda JAR.Pentru a consulta optiunile este
suficient sa introduceti comanda JAR.Pentru a crea o fila noua,se va uti-
liza optiunea c,la care se pot adauga optional si alte optiuni.Toate
filele ce trebuiesc incluse in pachet,se vor adauga in lista,intr-o singura
linie de comanda.
EXEMPLU: -daca doriti sa impachetati filele necesare pentru exemplul de
applet
Cele trei file sunt: applet1.html,Applet1.class.Applet1.java
in fereastra Command Prompt (in directorul filelor) se va utilza comanda:
jar cvf Applet1.jar applet1.htm Applet1.class Applet1.java
Se va crea automat fila Applet1.jar.Observati ca am impachetat trei
tipuri diferite cu aceeasi comanda.Daca pachetul contine mai multe file
de tip .class,acestea pot fi impachetate simultan cu o comanda generica
de tip /*.class.
Pentru despachetarea filelor din fila JAR se va utiliza o comanda ce
include optiunile x si f.De exemplu,pentru a despacheta fila Applet1.jar
se va utiliza o comanda : jar xf Applet1.jar
In continuare,filele JAR pot fi comprimate cu utilitarul pack200,pentru
a crea o fila extrem de mica.In exemplul de mai sus,se poate utiliza o
comanda de genul: pack200 arhiva.pack.gz Applet1.jar
Se va crea automat o arhiva de tip WinRAR (la fel ca si cu WinZipp).
Pentru a despacheta arhiva,se va putea utiliza utilitarul unpack200 cu o
comanda : unpack200 arhiva.pack.gz Applet2.jar
Am utilizat un alt nume pentru fila JAR,pentru a putea observa fila nou
creata.
Impachetarea filelor nu numai ca va creste foarte mult viteza pentru
transferul in retea,dar asigura si un grad crescut de securitate.Un astfel
de pachet nu poate fi "atacat" accidental",si nu exista riscul de a pierde
file in cursul operatiilor de transfer (prin supraincarcarea tamponului
de memorie).Fila comprimata,inlocuieste zeci sau sute de pointeri prin
unul singur.
Cu aceste recomandari se incheie si acest manual.Platforma Java are
insa o arie de acoperire mult mai mare si in continua dezvoltare.Pentru
a stapani acest domeniu,este necesar un efort permanet de instruire si
reactualizare sau restructurare a notiunilor.Cand proiectati o aplicatie
Java sau JavaCE,este bine sa aveti in vedere si proverbul:
***********
* SFARSIT *
***********