Sunteți pe pagina 1din 193

http://ms.sapientia.ro/~manyi/teaching/oop/oop_romanian/curs2/curs2.

html

Introducere
1. Limbaje orientate obiect - Dezvoltarea aplicatiilor in stil OO
2. Limbajul Java
    2.1. Generalitati
  2.1.1. Caracteristici
        2.1.2. Scrierea aplicatiilor Java
        2.1.3. Pachete Java
    2.2. Elementele limbajului
        2.2.1. Variablie
           2.2.1.1. Tipuri primitive
           2.2.1.2. Tipul referinta
           2.2.1.3. Tablouri
               2.2.1.3.1. Alocarea memoriei pentru tablouri
               2.2.1.3.2. Lungimea tablourilor
               2.2.1.3.3. Initializarea tablourilor
               2.2.1.3.4. Eliberarea memoriei alocate dinamic
        2.2.2. Expresii
        2.2.3. Exceptii
           2.2.3.1. Exceptii si tratarea acestora
           2.2.3.2. Aruncarea exceptiilor
           2.2.3.3. Aspecte legate de performanta utilizarii exceptiilor
    2.3. Tipuri de programe Java
1. Limbaje  OO
 
Anul  de
Limbaj Dezvoltare Variante
aparitie
AT&T Bell Laboratories; inceputul anilor Borland C++; Microsoft C++; Watcom C++;
C++
Bjarne Stroustrup 1980 GNU C++
inceputul anilor Sun JDK; Symantec Visual Cafe; Microsoft
Java SUN JavaSoft; James Gosling
1990 Visual Java; Borland JBuilder
inceputul anilor
Smalltalk XEROX PARC Smalltalk V.; Smalltalk-80; IBM Smalltalk
1970
inceputul anilor
Eiffel Bertrand Meyer
1980
Pascal Apple Computer, Niklaus Wirth 1986 Borland Turbo Pascal; Borland Delphi
Dezvoltarea aplicatiilor in stil OO
Pasii in dezvoltarea unei aplicatii indiferent de stilul de dezvoltare utilizat sunt:
1. analiza
2. proiectarea
3. implementarea
4. testarea.
Dupa aceea apare si o faza de intretinere a produsului, prin care se intelege corectarea unor mici erori care apar
cu timpul si adaugarea de noi facilitati  (mai mici) produsului. Cateodata la produse neproiectate in mod
corespunzator faza de intretinere poate fi mai costisitoare decat faza realizarii produsului. De aceea este foarte
important ca aplicatia sa fie proiectata modular astfel incat modulele independente sa se poata intretine si
separat.
Analiza:  Este facuta de catre un analist, care colaborand cu beneficiarul produsului identifica obiectele
principale ale aplicatiei. De exemplu in cazul unui sistem informatic pentru banci, obiectele principale ar fi:
Client, Factura, Tranzactie, Angajat_Bancar etc. Rezultatul acestei faze se va concretiza intr-un model realizat
de catre analist.  In aceasta faza se determina CE trebuie facut, dar nu si CUM  trebuie facut. Detaliile de
implementare apartin  altor faze. Se vor identifica subsistemele principale ale produsului si se va realiza un
dictionar de date continand descrierea principalelor obiecte. In ultima faza a analizei se identifica resursele
necesare pentru sistem (hardver si software).
Proiectarea:  Sarcina acestei faze este de a detalia modelul obiect rezultat din faza de analiza. Proiectantul va
determina sarcina exacta a obiectelor si structurile de date respectiv algoritmii necesari pentru faza de
implementare. Tot in aceasta faza se construiesc diagrame de clase si de obiecte precum si  diagrame de
colaborare intre obiecte.
Implementare, testare: Se implementeaza clasele pe baza diagramelor de clase. Daca proiectul este bine pus la
punct atunci munca din aceasta faza este doar munca de rutina. Tot in acesta faza se incearca utilizarea
componentelor gata facute astfel reutilizand codul existent.  Prima data se testeaza componente mai mici, dupa
aceea modulele si in ultima faza se testeaza produsul intreg.
 
Metode si instrumente de dezvoltare
Metode:
In faza de proiectare a produsului ( si nu numai in aceasta ) ne ajuta produsele CASE (Computer Aided Systems
Engineering ). Unele dintre aceste produse CASE stiu sa genereze si cod intr-un anumit limbaj sau in mai multe
limbaje.
OMT (Object Modeling Technique) 1991, James Rumbaugh
Metoda Booch, 1991, Grady Booch
OOSE (Object Oriented Systems Engineering) 1992, Ivar Jacobson
UML (Unified Modeling Language ) 1997, Rational Corporation, Santa Clara California. Grady Booch, James
Rumbaugh, Ivar Jacobson
UML este un standard, versiunile se gasesc la adresa http://www.rational.rose
Instrumente:
 
Denumire Metode Generare cod
OMT, Booch, OOSE, C++, Smalltalk, Visual Basic, Java,
Rational Rose (Rational Corporation)
UML Modula
OMTolol (General Electric Research and
OMT C++
Development Center )
Objectory (Objective Systems) OOSE
OMT, Booch, OOSE,
With Class (MicroGold Software Inc. ) C++, Pascal, Eiffel, Smalltalk
UML
2. Limbajul Java
Limbajul Java are radacinile in C. Java este asemanator cu C++, dar este mai flexibil, mai portabil si mai
simplu de invatat. Parintele limbajului a fost James Gosling, autorul celebrului editor de text emacs.  Java
incearca sa ramana un limbaj simplu, s-a preluat partea buna a limbajului C++ si au fost eliminate partile
derutante cum ar fi supraincarcarea operatorilor si mostenirea multipla. Limbajul este independent de
arhitectura calculatorului pe care lucreaza (portabil). Nu genereaza cod nativ pentru o platforma sau alta ci
genereaza o secventa de instructiuni pentru o masina virtuala, numita si Masina Virtuala Java (JVM). 
Aplicatiile Java vor fi interpretate. Deci Java este un limbaj compilat si interpretat. Dezavantajul principal al
limbajelor interpretate este viteza de executie.
2.1.1. Caracteristici:
     orientat obiect
     distribuit (are biblioteci pentru scrierea aplicatiilor ce lucreaza in retea: TCP/IP, URL)
     securitate ridicata (facilitati de protectie a obiectelor )
     permite lucrul cu mai multe fire de executie
2.1.2. Scrierea aplicatiilor Java
Scrierea unei aplicatii Java presupune existenta a amai multe faze. In prima faza se face editarea programului
sursa. Putem utiliza orice editor care stie sa salveze in format text sau putem utiliza medii care genereaza cod.
Rezultatul acestei faze este unul sau mai multe fisiere sursa Java.  Dupa faza de editare urmeaza faza de
compilare. Prin compilare se obtine unul sau mai multe fisiere bytescode (.class) cate unul pentru fiecare clasa,
clasa fiind unitatea de compilare. Dupa faza de compilare urmeaza faza de executie.
Faza de editare:
    Fiser sursa: Aplicatie.java
 
  // Un program care isi afiseaza argumentele primite la linia de comanda
  public class Aplicatie{
    public static void main( String args[] ){
    for( int i=0; i<args.length; i++)
                System.out.println(args[i]);
    }
}
Faza de compilare:
    javac Aplicatie.java  -------> Aplicatie.class
Faza de interpretare:
    java Aplicatie argument1 argument2 argument3 ...
In faza de interpretare se va crea un proces care contine in zona cod codul interpretorului, iar in zona de date
codul interpretat (BYTESCODE). Interpretorul este mic si poate fi implementat usor pentru diferite platforme.
Unitatea de compilare este clasa. Clasele sunt componentele unei aplicatii care contin date si cod. Clasele sunt
incarcate in mod dinamic de catre o aplicatie Java. Ele sunt incarcate in momentul in care sunt efectiv utilizate.
Java contine si cateva clase fundamentale care contin metode dependente de arhitectura. Aceste metode native
sunt implementate intr-un limbaj nativ platformei si permit accesul la sistemul de fisiere, sistemul de ferestre si
retea. Restul limbajului este scris in Java si este portabil. Aici sunt incluse aplicatiile ca: compilatorul Java,
browserul HotJava de la Sun etc.
 
2.1.3. Pachete Java

Pachetele Java sunt grupe formate din clase si interfete  Java. Principalele pachete sunt:
 
Contine clasa Applet si clase pentru comunicarea intre aplet si
 java.applet
contextul acestuia
Contine clase pentru crearea interfetelor grafice cat si pentru
 java.awt
desenare si lucrul cu imagini
 java.awt.color Permite lucrul cu culori
 java.awt.datatransfer Clase si interfete pentru transferul datelor intre aplicatii
 java.awt.dnd Clase si interfete pentru operatia drag and drop
 java.awt.event Clase si interfete pentru evenimente generate de componente AWT
 java.awt.font Clase si interfete pentru lucrul cu diferite tipuri de fonturi
 java.awt.image Clase si interfete pentru crearea si modificarea imaginilor
 java.awt.print Clase si interfete pentru tiparire
 java.beans Clase si interfete pentru Java Beans
 java.io Clase si interfete pentru intrare/iesire cu fluxuri de date
 java.lang Clase fundamentale Java
Clase pentru efectuarea operatiilor aritmetice de precizie
 java.math
oarecare
 java.net Clase pentru implementarea aplicatiilor de retea
 java.rmi Pachetul Remote Method Invocation
 java.sql Pachetul pentru JDBC
 java.util Clase utilitare ( Vector, Random, StringTokenizer, Date)
 javax.swing Clase pentru componente lightwight
2.2. Descrierea limbajul Java
2.2.1. Variabile
Toate variabilelel Java sunt initializate. Fiecare tip are o valoare implicita cu care se initializeaza variabilele de
tipul respectiv.
Declaratie: tip nume_variabila [ = expresie ];
2.2.1.1. Tipuri primitive
logic - boolean
caracter - char
intregi - byte, short, int, long
flotante - float, double
Tipul boolean are ca valori true si false. Variabilele boolean neinitializate explicit vor avea valorarea implicita
false.
Tipul caracter are ca valori coduri UNICODE reprezentate pe doi octeti, astfel permitand reprezentarea tuturor
caracterelor limbilor existente. Valoarea implicita este caracterul null cu codul '\u0000'.
Tipul byte este un tip intreg cu semn avand valorile intre -128 ..127. Valoarea implicita pentru variabilele de tip
intreg este 0. Celelalte tipuri intregi sunt reprezentate pe 16, 32 respectiv 64 biti.
Tipurile flotante sunt reprezentate pe 32 de biti, respectiv 64 de biti. Valoarea implicita pentru variabilele
flotante este 0.0f.
Reali speciali definiti de standardul IEEE
NaN - Not a Number. Este rezultatul unei impartiri cu 0. Ex. 0.0/0.0
Clasa java.lang.Float defineste aceasta constanta precum si altele cum ar fi: POSITIVE_INFINITY,
NEGATIVE_INFINITY
2.2.1.2. Tipul referinta
In C standard se pot defini tipuri noi, complexe cu ajutorul structurilor.  In Java sau alte limbaje  OO se
utilizeaza clase in locul structurilor. Clasa defineste un tip nou. Referinta este similara cu notiunea de pointer
din C cu exceptia ca referinta este un pointer sigur ("safe-pointer").  Referinta este un fel de alias, un alt nume
pentru un obiect. Se utilizeaza in definitia claselor pentru a referi un obiect din interiorul unui alt obiect.
Exemplu:
    String s1 = "Un sir"; // Se aloca memorie pentru referinta s1 si pentru sirul constant "Un sir". Se initializeaza
referinta s1 cu adresa sirului constant
    String s2 = s1; // Se aloca memorie pentru referinta s2 care este initializat cu  referinta s1
    String s3; // Se aloca memorie pentru referinta s3
In exemplele precedente am creat trei referinte toate de tipul String. Primul a fost initializat cu un sir constant,
al doilea cu o alta referinta, iar al treilea nu a fost deloc initializat. Valoarea implicita pentru variabile de tip
referinta este null (referinta neinitializata). La crearea referintelor  nu se aloca memorie pentru obiectul
referit, ci doar pentru referinta. Alocarea de memorie se face in mod dinamic cu cuvantul cheie new.
 
2.2.1.3. Tablouri
2.2.1.3.1. Alocarea dinamica a tablourilor
Tablourile sunt referinte speciale. Numele tabloului este o referinta cu ajutorul careia ne putem referi la
elementele tabloului.
Exemplu de referinta la un tablou de intregi:
int ti[];

Cu constructia de mai sus se creaza o referinta la un tablou de inregi. Nu se aloca memorie pentru inregii din
tablou, practic in acest moment nici nu se cunoaste dimensiunea acestuia. Alocarea de memorie pentru acest
tablou se va face cu urmatoarea constructie:
ti = new int[10];

Declaratia se poate face si mai compact: int ti[]= new int[10];


Initializarea tablourilor unidimensionale
int ti[]={1, 2, 3, 4, 5};

Tablourile bidimensionale sunt practic tablouri unidimensionale de referinte


 
int ti[][]=new int[5][]; // Se creaza un tablou de cinci referinte
ti[0]= new int[2]; // Se initializeaza prima referinta din tabloul ti, ti[0]
ti[1]= new int[3];// Se initializeaza a doua referinta din tabloul ti, ti[1]

2.2.1.3.2. Lungimea tablorilor


In Java tablourile sunt alocate dinamic, ele isi pot schimba dimensiunile pe parcurul executiei. Practic la
alocarea unui tablou se creaza un nou obiect instantiat dintr-o clasa ce permite stocarea a mai multor elemente
de acelasi tip. Astfel avem la dispozitie variabila de clasa length ce  ne furnizeaza lungimea tabloului. Pentru
exemplul precedent obtinem urmatoarele valori:
 
int dim_ti = ti.length; // se va obtine 5
int dim_ti_0 = ti[0].length; // se va obtine 2
int dim_ti_1 = ti[1].length; // se va obtine 3
2.2.1.3.3. Initializarea unui tablou bidimensional.
Varianta 1. Se initializeaza un tablou cu doua linii, fiecare linie avand acelasi numar de elemente.
int t[][] = new int[2][3];
int i,j;
for( i =0; i<t.length; i++)
{
        for( j=0; j<t[i].length; j++){
                t[i][j] = i*3+j;
                System.out.print(t[i][j]);
                System.out.print(" ");
        }
        System.out.println();
}
Rezultatul executiei:
0 1 2
3 4 5
Varianta 2. Se initializeaza un tablou cu doua linii, liniile avand lungime diferita.
int t[][] = new int[2][];
int i,j, c=0;
for( i =0; i<t.length; i++)
{
        t[i] = new int[(i+1)*2];
        for( j=0; j<t[i].length; j++){
                t[i][j] = c++;
                System.out.print(t[i][j]);
                System.out.print(" ");
        }
        System.out.println();
}
Rezultatul executiei:
0 1
2 3 4 5
2.2.1.3.4. Eliberarea memoriei  alocate dinamic
Eliberarea memoriei alocate dinamic se face automat in Java. Zonele de memorie nerefeite de nici o referinta
sunt colectate de asa zisa "colectorul de gunoi", care se ruleaza pe un fir de executie de tip demon (cu prioritate
mica). Colectorul de gunoi se poate invoca si explicit prin: System.gc(); Dereferentierea unei variabile se
poate face in urmatoarele moduri:
int x[] = new int[10];
...
x= null; sau x = new int[20];
2.2.2. Expresii
Expresia are un tip si o valoare.  Valoarea se obtine prin evaluarea expresiei. Tipul expresiei se cunoaste inca
din faza de compilare. Valoarea expresiei poate fi de un anumit tip primitiv (rezultatul unei expresii numerice
20+3-6) sau poate fi o referinta ( new OClasa() ) sau poate fi tipul void, ca rezultatul unei metode (functii) ce
returneaza acest tip.
Operatori - Diferente fata de C standard
Java nu suporta operatorii de adresare directa & si cel de adresare indirecta *. Operatorul de secventiere "," nu
este suportat de Java. Nici sizeof nu este suportat. In schimb avem operatori noi cum ar fiinstanceofsi
operatorul + se poate utiliza si la obiecte de tip String.
 
String s1 = new String("Un sir de caractere");
String s2 = "Un alt sir de caractere";
boolean b1= (s1 instanceof String);// true
b1 = ( s2 instanceof Object);// true
b1 = ( s1 instanceof Integer );// false

 Toate clasele deriva din clasa Object. Aceasta este superclasa tuturor claselor Java. Operatorul instanceof
verifica daca un obiect apartine unei clase. Orice obiect din subclasa unei clase apartine si superclasei. (Clasa
String este subclasa clasei Object, clasa Object este superclasa clasei String).
 
2.2.3. Exceptii
Cand vorbim despre exceptii trebuie sa facem diferenta intre instructiunea de generare (aruncare) a exceptiilor (
throw)  respectiv de instructiunea de tratarea acestora (try-catch-finally).
2.2.3.1. Exceptii si tratarea acestora
Proiectul Java a pornit cu scopul declarat de a dezvolta un software performant pentru aparatele electronice de
larg consum. Aceste echipamente se definesc ca mici, portabile, distribuite si lucrand in timp real. Pentru
controlul acestor echipamente  era absolut necesar ca erorile de tip software sa fie adecvat tratate. Nu se admite
ca un telefon sa nu functioneze din cauza unei erori software. Erorile software nu pot fi eliminate, dar putem
identifica secventele de cod care pot cauza acestea si le putem trata. In limbajul C standard cade in
responsabilitatea programatorului tratarea acestor erori. Functiile returneaza de obicei -1 in caz  ca a aparut vreo
eroare.  De exemplu la deschiderea unui fisier inexistent pentru citire.
Java ofera o solutie eleganta pentru aceste probleme prin mecanismul de tratarea exceptiilor. In cazul aparitiei
acestor exceptii controlul executiei programului se transfera la o sectiune de cod, scris in special  pentru tratarea
unei exceptii. Metodele Java sunt declarate specificand tipurile de exceptii ce pot fi generate de acestea.
Exceptiile sunt instante (obiecte) ale clasei java.lang.Exception si ale sublcaselor acesteia. Subclasele clasei
Exception pot contine informatii speciale despre un anumit tip de exceptie si un comportament adecvat.
Exceptia cea mai importanta este definita de clasa java.lang.IOException cu subclase speciale pentru
diferite probleme I/O  cum ar fi FileNotFoundException respectiv SocketException.
Un obiect  Exception este creat de catre cod in momentul cand au aparut conditiile pentru o eroare. Obiectul de
tip exceptie este transmis impreuna cu controlul executiei catre codul de tratare a exceptiei.
In continuare vom prezenta exceptiile di cadrul pachetului java.lang impreuna cu ierarhia de clase din care fac
parte. In general avem doua tipuri de exceptii si acestea sunt repezentate de subclasele clasei Throwable
Exception si Error.Cele de tip Error si RuntimeException sunt generate de mediul de executie. Ecxeptiile de tip
Error sunt generate de asemenea de catre mediul de executie, dar acestea nu pot fi prinse de program. De
exemplu masina virtuala Java nu are memorie suficienta.
java.lang.Exception
1. Object
    1.1. Throwable
            1.1.1. Exception
                    1.1.1.1. ClassNotFoundException
                    1.1.1.2. CloneNotSupportedException
                    1.1.1.3. IllegalAccessException
                    1.1.1.4. InstantiationException
                    1.1.1.5. InterruptedException
                    1.1.1.6. NoSuchMethodException
                    1.1.1.7. RuntimeException
                        1.1.1.7.1. ArithmeticException
                        1.1.1.7.2. ArrayStoreException
                        1.1.1.7.3. ClassCastException
                        1.1.1.7.4. IllegalArgumentException
                            1.1.1.7.4.1. IllegalThreadStateException
                            1.1.1.7.4.2. NumberFormatException
                        1.1.1.7.5. IllegalMonitorStateException
                        1.1.1.7.6. IllegalStateException
                        1.1.1.7.7. IndexOutOfBoundsException
                            1.1.1.7.7.1. ArrayIndexOutOfBoundsException
                            1.1.1.7.7.2. StringIndexOutOfBoundsException
                        1.1.1.7.8. NegativeArraySizeException
                        1.1.1.7.9. NullPointerException
                        1.1.1.7.10. SecurityException
java.lang.Error
1. Object
        1.1. Throwable
                1.1.1 Error
                        1.1.1.1. LinkageError
                                1.1.1.1.1. ClassCircularityError
                                1.1.1.1.2. ClassFormatError
                                1.1.1.1.3. ExceptionInitializerError
                                1.1.1.1.4. IncompatibleClassChangeError
                                        1.1.1.1.4.1. AbstractMethodError
                                        1.1.1.1.4.2. IllegalAccessError
                                        1.1.1.1.4.3. InstantiationError
                                        1.1.1.1.4.4. NoSuchFieldError
                                        1.1.1.1.4.5. NoSuchMethodError
                                1.1.1.1.5. NoClassDefFoundError
                                1.1.1.1.6. UnsatisfiedLinkError
                                1.1.1.1.7. VerifyError
                        1.1.1.2. ThreadDeath
                        1.1.1.3. VirtulaMachineError
Sintaxa instructiunii:
try{
        //bloc de instructiuni care pot cauza exceptii
}
catch( TipExceptie1 obiect1 ){
        // codul de tratare a exceptiei TipExceptie1
}
catch( TipExceptie2 obiect2 ){
        // codul de tratare a exceptiei TipExceptie2
}
finally{
        //bloc de instructiuni care se executa intotdeauna
}
 
Exemplu:
public class fin{
        public static void main( String args[] )
        {
                int i =0;
                try{
                        i = Integer.parseInt( args[ 0 ] );

                }               
                catch( ArrayIndexOutOfBoundsException e1 )
                {
                        System.out.println(e1.toString() );
                        i=10;
                }
                catch( NumberFormatException e2 ){
                        System.out.println( e2.toString() );
                        i = 20;
                }
                finally{
                        i++;

                }
                System.out.println(i);
        }
}
Exemple de executii: ( se compileaza javac fin.java ---> fin.class )
1. java fin
java.lang.ArrayIndexOutOfBoundsException
11
2.java fin 4
5
3. java fin qwerty
java.lang.NumberFormatException
21
Daca exceptia generata nu este prinsa de catre program atunci aceasta va fi prinsa de catre mediul de executie 
Java care de obicei suspenda executia programului afisand exceptia din cauza caruia s-a oprit programul. Daca
se trateaza exceptia atunci controlul executiei programului  se transfera la codul de tratare dupa care daca exista
si clauza finally se executa instructiunile de aici, dupa care se continua cu prima instructiune de dupa blocul try-
catch-finally. Daca intr-un bloc try avem mai multe clauze catch atunci se va cauta secvential intre aceste
clauze si numai primul potrivit va fi executat.
Exemplu:
try{
        //cod sursa exceptie de tip IOException
}
catch( Exception e1 ){
        // cod de tratare 1
}
catch( IOException e1 ){
        // cod de tratare 2 
}
In exemplul precedent in cazul generarii exceptiilor intotdeauna se va executa codul de tratare 1, deoarece orice
exceptie este implicit si de tip Exception, aceasta clasa fiind superclasa tuturor exceptiilor. Concluzia de aici
este ca trebuie sa fim foarte atenti  la ordinea in care sunt plasate clauzele catch.
2.2.3.2. Aruncarea exceptiilor
 fara a crea o clasa noua de exceptii: prin constructia throw new Exception("S-a intamplat ceva
rau"); Se creaza un obiect de tip Exception si controlul se transfera blocului de tratate a exceptiei cel
mai apropiat.
 prin extinderea clasei Exception
 
class MyException extends Exception{
        MyException(){
                super(); // Apel constructor superclasa
        }
        MyException( String text ){
                super( text ); // Apel constructor superclasa
        }
}

         Utilizarea exceptiei:


 
try{
        // operatii ce pot genera MyException
}
catch( MyException e ){
    // tratare
}
catch( IOException e ){
    //cod de tratare
}

 
2.2.3.3. Aspecte legate de performanta utilizarii tratarii exceptiilor
Exceptiile trebuie generate doar in imprejurari exceptionale. De exemplu la scrierea unui ciclu se recomanda
efectuarea unui test mic la fiecare reluare a ciclului, decat generarea unei exceptii, care conduce inevitabil la
marirea timpului de executie datorita faptului ca trebuie gasit contextul cel mai apropiat de try-catch, dupa care
trebuie transferat controlul executiei si revenit dupa aceea.
2.3. Tipuri de programe scrise in Java
Java permite scrierea a doua tipuri de programe:
 apleturi care se ruleaza in interiorul paginilor Web, incarcate de navigatoare. In acest caz navigatorul
are si un interpretor Java si interpreteaza codul octeti.
public class UnAplet extends java.awt.Applet{

        public void paint( java.awt.Graphics g ){


                g.drawString("Un aplet demonstrativ",10,10); 
        }
}
Fisierul HTML in care se incadreaza apletul:
<html>
<applet code="UnAplet.class" width=300 height=300>
</applet>
</html>
 
 aplicatii care vor fi interpretate de catre interpretorul Java
Exemplu de aplicatie Java: Un program care isi afiseaza argumentele primite la linia de comanda
 
public class OAplicatie{
 
    public static void main( String args[] ){
        int i;
        for( i=0; i<args.length; i++)
            System.out.println(args[i]);
    }
}

Conceptele programarii orientate obiect


1. Caracterizarea obiectului
2. Starea obiectului
3. Identificarea obiectului
4. Clasa, instanta
5. Starile unui obiect
6. Proprietatea de incapsulare;Ascunderea informatiei
7. Exemplu: Stiva deintregi
1.  Caracterizarea obiectului
Obiectul  stocheaza informatie si executa anumite sarcini la cerere. Un obiect este format din date (atribute) si
metode (operatii).
Obiect=Date+Cod
Metodele de fapt sunt rutine care lucreaza cu datele obiectului. Siacum sa consideram obiectul Om. Un om
poate fi caracterizat de nume, dedata nasterii, de inaltime, adresa. Sunt si alte caracteristici ale omului,dar
pentru a rezolva problema noastra e de ajuns sa le consideram doarpe acestea. Acum discutam cateva metode
prin care se manifesta obiectul nostru. De exemplu oamenii se muta din cand in cand. Odata cu mutarea seva
schimba si adresa omului. Omul poate sa creasca in inaltime pana lao anumita varsta. Deci obiectul om poate fi
caracterizat prin:
 Date: nume, data nasterii, inaltime, adresa
 Metode: se_muta( adresa noua ), creste( inaltime_noua )
Pana acum am descris o abstractizare a omului, care va fi un tip nou. Acum sa consideram un exemplu concret,
adica un obiect de tip om.
Un alt exemplu. Vom incerca sa caracterizam ferestrele de pe ecran. Unobiect de tip fereastra poate fi
caracterizat prin urmatoarele date simetode:
Date:
x0,y0: coordonatele coltului stanga-sus, intregi
latime, inaltime:  intregi
Metode:
afisare()
ascundere()
stergere()
2. Starea obiectului
Este formata din valorile actuale ale datelor. Dupa apelul anumitor metode (ale celor care modifica campurile
de date) starea obiectului se poatemodifica.  Doua obiecte se afla in aceeasi stare daca valorile campurilorde
date coincid.
Exemplu:
Om Om("Ioan",32) si
3. Identificarea obiectului
Doua obiecte pot fi in aceeasi stare, dar ele nu pot fi identice, deoarece adresa lor in memorie difera. Obiectele
sunt identificate prin referinte. Doua referinte pot referi acelasi obiect.
Exemple:
     String s1 ="Un sir de caractere";
    String s2 = s1;
 
    // Doua referinte la un singur obiect
    Om o1 = new Om("Pop Ioan",1960,182,"Tg-Mures");
    Om o2 = o1;
 
Obiectul "Un sir de caractere" exista intr-un singur exemplar si avemdoua referinte s1 si s2 prin care obiectul
poate fi accesat.
 
4. Clasa, instanta
Clasa unui obiect este un tip de data pe baza caruia se pot crea instante din clasa respectiva. Aceste instante sunt
obiecte.
Daca doua obiecte sunt in aceaasi stare atunci la aceaasi cerere reactioneaza la fel. Cererile se realizeaza prin
trimiterea anumitor mesaje obiectului. Mesajele au forma urmatoare:
referinta_obiect.nume_metoda()

Pentru fiecare obiect se aloca memorie pentru datele obiectului, darmetodele sunt utilizeaza in comun de catre
diferite instante.
Un obiect poate sa ceara de la un alt obiect realizarea unei sarcini.Mesajele sunt de fapt apeluri de metode
publice ale claselor. Metodelepublice sunt metode accesibile de catre celelalte obiecte. Un obiect poatesa
contina si metode private, metode care nu sunt accesibile din afara.In cazul clasei Fereastra metodele afiseaza()
si sterge() trebuie sa fiemetode publice, iar daca am dori desenarea unui chenar in jurul ferestreiaceasta am
putea-o defini ca o metoda privata.
Obiectele comunica prin mesaje si rolurile sunt distribuite in modulurmator:

client: cel care cere realizarea unei sarcini ( cel care trimitemesajul )
server: cel care executa sarcina (cel care receptioneaza mesajul)
Acelasi obiect poate fi odata client, iar dupa aceea server. Existatotusi obiecte tipice client cum ar fi obiectele
de tip control si obiectetipice server, obiecte de tip baza de date. In literatura de specialitateputem sa intalnim si
denumirile:
ACTOR: care cere numai
AGENT: care cere si executa
Mesajele au rolul de a activa obiecte si realizeaza schimb de informatieintre obiecte.  Raspunsul la un anumit
mesaj se obtine prin valoareareturnata de catre metoda sau prin parametrii transmisi ca referint

Pentru fiecare clasa utilizata in aplicatia noastra se creaza un obiectde tip Class(java.lang.Class) care tine
evidenta obiectelor instantiate din clasele respective. Un obiect poate sa spuna din ce clasa este instantiata.
Exemplu:
String s="Un sir de caractere";
Class c=s.getClass();    //getClass() - Returneazaun obiect de tip Class
System.out.println( c.getName()); //getName() - Returneaza un obiectde tip String
5. Starile unui obiect
Ca si variabile, obiectele au un ciclu de viata. Se nasc, sunt initializate, dupa care pot fi inactive sau pot fi
activate si dupa aceea mor. Numelemetodei care initializeaza obiectul difera de la un mediu de programarela
altul:
C++, Java: constructor,o metoda cu numele   clasei cu o lista de parametri si fara tipreturnat.
Constructorul are sarcina de a aloca memorie pentru campurilede date si de a le initializa acestea.
Turbo Pascal: Init()
Delphi: Create()
Un constructor de tip Java pentru o fereastra ar avea forma:
 
Fereastra( int Fx, int Fy, int FLatime, int FInaltime )
{
                    x0 = Fx; y0 = Fy;
                    Latime = FLatime; Inaltime = FInaltime;
}
Caracteristicile metodei constructor:
 poarta numele clasei
 nu returneaza nici un tip de date
 poate avea o lista de parametri
 o clasa poate avea mai multi constructori

De multe ori la distrugerea obiectelor avem nevoie de dezalocarea resurselor alocate pentru obiect. In C++
dezalocarile sunt facute de catre o metoda speciala numita si destructor. In Java dezalocarea memoriei (alocata
cu new) se face de catre Garbage Colector dupa ce nu mai exista nici o referinta a respectivul obiect. Exista insa
si metode native pentru care alocarea memoriei  nu s-a facut cu metoda new si in afara de memorie exista si alte
resurse alocabile obiectelor: fisiere, conexiuni retea. Pentru dezalocarea acestor resurse Java pune la dispozitie
metoda finalize, mostenita din clasa Object si care are urmatoarea signatura:
protected void finalize() throws Throwable{}

Aceasta metoda este apelata de runtime system inainte ca memoria alocata obiectului respectiv sa fie dezalocata
de Garbage Colector. Colectorul de gunoi de ruleaza pe un fir de executie demon si nu se stie de dinainte cand
va fi planificat pentru executie. Se stie ca metoda finalize se apeleaza inainte ca zona de memorie sa fie
reutilizata.
Clasa System insa are o metoda gc() care forteaza executia Garbage Collector si se foloseste impreuna cu
runFinalization(). Aceasta metoda forteaza executia metodelor finalize() a obiectelor care nu mai sunt
referintiate, dar inca n-au fost finalizate. Cu ajutorul unei alte metode runFinalizerOnExit()  putem seta  flag-ul
care indica masinii virtuale daca sa execute sau nu metodele finalize la exit, adica la terminarea executiei
masinii virtuale.

public static void gc()


public static void runFinalization()
public static void runFinalizerOnExit()
6. Proprietateade incapsulare. Ascunderea informatiei
Incapsularea inseamna ca datele se definesc impreuna cu codul care actioneaza asupra lor.
Ascunderea informatiei inseamna ca obiectul isi ascunde problemele interne.Datele din interiorul obiectelor
sunt accesibile doar prin metode publice.Astfel cei neautorizati nu pot patrunde in interiorul obiectului.

7. Exemplu
Sa se scrie o aplicatie care defineste o clasa stiva si instantieaza obiecte din aceasta clasa.
Diagrama de obiecte:

Diagrama de clase:

Sursa Java:
public class TControl{
        public TControl(){
                TStiva s1, s2;
                int i;
                s1 = new TStiva( 10 );
                s2 = new TStiva( );
                for( i=0; i<10; i++)
                        s1.push( i );
                while( ! s1.empty() )
                        System.out.println( Integer.toString( s1.pop()));
                for( i=10;i<15;i++){
                        s2.push( i );
                        System.out.println( Integer.toString( s2.top()));
                }
        }
        public static void main(String args[] ){
                TControl contro = new TControl();
        }
}
class TStiva{
        int st[];
        int sp;
        int dim;
        public TStiva( int pdim){
                dim = pdim;
                st = new int[ dim ];
                sp = -1;
        }
        public TStiva( ){
                dim = 100;
                st = new int[ dim ];
                sp = -1;
        }
        public void push( inte ){
                sp++;
                st[ sp ] = e;
        }
        public int pop(){
                sp--;
                return st[ sp+1 ];
        }
        public int top(){
                return st[ sp ];
        }
        public boolean empty(){
                return sp==-1;
        }
}

Modelare cu obiecte
1. Legatura dintre obiecte, diagrame de obiecte
2. Diagrama de clase
3. Mostenire. Legare statica.
4. Exemple
1. Legatura dintre obiecte, diagrame de obiecte
Obiectele pot conlucra doar daca exista legaturi intre ele. Legaturiledintre obiecte sunt de doua feluri:
 de cunoastere (aquaintance relationship)
 de continere(aggregation relationship)

Doua obiecte sunt in legatura de cunoastere, daca existaindependent si cel putin unul cunoaste pe celalalt.
Doua obiecte sunt in legatura de continere, daca unul dintreele contine fizic pe celalalt. (intreg+parte)
Exemple de legaturi de cunoastere:
Relatia de continere:
Daca se distruge obiectul container atunci automat se distrug si obiectele continute. Relatia de continere este o
relatie mai puternica decat relatia de cunoastere. Relatia de continere poate fi o relatie de continere slaba sau
una puternica. In cazul relatiei de continere slaba, intregul isi cunoaste partile, dar poate exista independent de
partea continuta. Ca exemplu vom considera un obiect Cutie, care poate sa contina un cadou. Cutia existasi fara
cadou. Pentru acest tip de relatie se utilizeaza un romb neumplutpe partea intregului.

In cazul relatiei de continere puternica utilizam un romb umplut tot pepartea intregului, dar in acest caz intregul
nu exista independent de partilelui. Ca exemplu consideram obiectele TCaine, TCap, TCorp, TPicior si
diagramaurmatoare pentru relatia de continere puternica.
 
Diagramele de mai sus se numesc diagrame de obiecte.
2. Diagrame de clase

Lagaturile de pe diagramele de mai sus sunt de doua tipuri


1. legaturi obligatorii (intre tara si captala), nu exista tara fara capitala si invers
2. legaturi optionale cum ar fi legatura de casnicie intre barbat si femeie
Legaturile dintre clase pot fi clasificate si dupa gradul legaturilor.Aici putem avea urmatioarele situatii:
 legatura unu la unu (one to one association)
 legatura unu la mai multe (one to many association)
 lagatura mai multe la mai multe(many to many association)
Exemplele de mai sus au fost din prima categorie, adica legaturi unu launu.
Exemple de legaturi unu la mai multe:
Exemple de legaturi "mai multe la mai multe"

In cazul diagramei de mai sus * inseamna ca numarul cursurilor poate fioricat de mare, iar cursurile se fac doar
daca avem cel putin 10 cursanti.Capaciatatea salilor fiind limitata, cursurile se tin maximum pentru 100de
studenti. In cazul celei de-a doua diagrame avem o legatura de cunoasterecu semnificatia ca orice student poate
sa cunoasca orice alt student.
 
Realizarea legaturilor
Obiectul de tip client intotdeauna trebuie sa cunoasca obiectul de tipserver. Cel care initiaza cererea trebuie sa
cunoasca pe cel caruia adreseazacererea. In cazul scrierii aplicatiilor acest lucru se realizeaza cu ajutorulunei 
referinte. Exemplu:
 
class TClient{
    TServer server;
    ...
}
class TServer
    ...
}

 
Scrierea unei aplicatii OO
Pas. 1. Specificarea detaliata a problemei astfel incat programul safie realizabil
Pas. 2. Faza de proiectare+implementare
      Proiectare:
o           Diagrame de obiecte
o           Diagrame  de clase
           Implementare:
         Implementarea claselor intr-unlimbaj
Pas. 3. Testare
 
Mostenire
Prima data definim doua concepte: specializarea si generalizarea.
Specializarea este procesul prin care se adauga noi caracteristiciunui obiect existent.
Generalizarea este procesul prin care din descrierea mai multorobiecte scoatem in evidenta cele comune.

In exemplul de mai sus rombul, dreptunghiul si patratul sunt paralelogramespeciale. De exemplu dreptunghiul
este un paralelogram cu un unghi dreptunghic.
Mostenirea este procesul de specializare. Clasa care se extinde senumeste clasa parinte, iar clasa derivata se
numeste clasa copil.

Prin mostenire se adauga noi proprietati unei clase existente si se potredefini anumite caracteristici ai clasei
existente.
Notatia UML pentru mostenire:

In exemplul de mai sus clasa TOm are doua campuri de date, numele si anul nasterii, respectiv doua metode
invata() si vorbeste(). Din aceasta clasa de baza derivam doua alte clase noi, clasa TStudent respectiv clasa
TProfesor. Aceste clase noi adauga alte campuri de date si metode respectiv specializeaza anumite metode ale
clasei de baza.
Astfel clasa TStudent va avea cinci campuri de date: nume, anul nasterii, numar matricol, an de studii, medie si
doua metode inavata() si vorbeste(). Metoda invata() a fost specializata (redefinita) deoarece un student invata
altfel decat un om obisnuit. Probabil ca invata mai mult si invata in anumite perioade ale anului. In cazul clasei
TProfesor apar doua date in plus fata de clasa parinte. Apare catedra la care apartine si salariul pe care primeste
pentru activitatea desfasurata. Clasa defineste o noua metoda preda(),care caracterizeaza numai obiectele de tip
TProfesor si redefineste metodevorbeste() deoarece un profesor ar trebui sa vorbeasca mai clar si maicoerent
decat un om obisnuit. In concluzie prin mostenire putem face urmatoarele:
 se adauga campuri de date noi
 se adauga metode noi
 se redefinesc anumite metode ale clasei parinte
Observatie: Daca se adauga campuri de date noi, atunci trebuie adaugatesi metode noi care realizeaza accesul la
aceste date.
Exista limbaje de programare care permit si mostenirea multipla cumar fi C++, iar altele care permit doar
derivarea de la o singura clasaparinte: Java, Smalltalk, Delphi.

Apelul constructorilor in cazul mostenirii


Consideram exemplul urmator:
class A{
   A(){
       System.out.println("Constructorul clasei A");
   }
}

class B extends A{
   B(){
       System.out.println("Constructorul clasei B");
   }
}

public class p4{


   public static void main( String args[] )
   {
       B b = new B();
   }
}
Rezultatul executiei va fi:
Constructorul clasei A
Constructorul clasei B
Deci putem afirma ca constructorul suprclasei se apeleaza intotdeauna automat, daca aceasta are un constructor
fara argumente. In caz contrar trebuie apelat explicit furnizandu-i parametrii corespunztori. Exemplul urmator
ilustreaza acest lucru:

class A{
   A( String a){
       System.out.println("Constructorul clasei A "+a);
   }
}

class B extends A{
   B( String b){
       super( b );
       System.out.println("Constructorul clasei B "+b);
   }
}

public class p5{


   public static void main( String args[] )
   {
       B b = new B("Hi");

   }
}
Legare statica
Legare statica inseamna ca adresa metodei se cunoaste inca din timpul compilarii. Deci in cazul unei constructii
nume_obiect.metoda() compilatorul stie exact care metoda va fi executata. Daca clasa careia apartine obiectul
nu areo asemenea metoda atunci compilatorul cauta  metoda in ierarhia declase. Se va apela metoda din clasa
cea mai apropiata cu acelasi nume    Aceasta cautare este facuta in faza compilarii si acest tip de legare
senumeste legare statica.
Exemplu 1: Sa se scrie o aplicatie care afiseaza pe ecran treiferestre cu titlurile: Unu, Doi, Trei si cu
dimensiunile 100x100.
Diagrama de clase:

Diagrama de colaborare:

Codul sursa Java:


//Source file: Aplicatie.java
import java.awt.Frame;
public class Aplicatie
{
   public Frame Frames[];
   public Aplicatie()
   {
      int i;
      String titles[]={"Unu", "Doi","Trei"};
      Frames = new Frame[ 3 ];
      for(i=0;i<3;i++)
      {
          Frames[i ] = new Frame(titles[ i ]);
          Frames[i ].setBounds(100*i,100*i,100,100);
          Frames[i ].setVisible( true );
      }
   }
   public static void main(String[] args)
   {
    Aplicatie a = new Aplicatie();
   }
}
 
Exemplu 2: Sa se scrie o aplicatie care se ruleaza intr-o fereastra.Fereastra va contine o eticheta si doua
butoane.

Sursa Java:
import java.awt.Frame;
import java.awt.Button;
import java.awt.Label;
public class MyFrame extends Frame
{
   private Button buttons[];
   private Label label;
   public MyFrame()
   {
    // Crearea butoanelor si adaugarea lor la container
    buttons = new Button[ 2 ];
    buttons[ 0 ]= new Button("Buton 1");
    add( buttons[ 0 ], "North" );
    buttons[ 1 ]= new Button("Buton 2");
    add( buttons[ 1 ],"South" );
    // Crearea etichetei si adaugarea ei la container
    label = new Label("Eticheta");
    add( label, "Center" );
   }
   public static void main(String[] args)
   {
     MyFrame f = new MyFrame();
     f.setBounds(10,10, 300, 300 );
     f.setVisible( true );
   }
}
Exemplu 3: Sa se scrie o aplicatie care afiseaza cinci ferestrede tip MyFrame astfel incat sa nu se suprapuna.
Sursa Java:
//Source file: MyFrames.java
 
public class MyFrames
{
   public MyFrame Frames[];
   public MyFrames()
   {
    int i;
    Frames = new MyFrame[5];
    for( i=0; i<5; i++)
    {
      Frames[ i ] = new MyFrame(Integer.toString( i ));
      Frames[ i ].setBounds(i*100,i*100,50,50);
      Frames[ i ].setVisible( true );
    }
   }
 
   public static void main(String[] args)
   {
    MyFrames f = new MyFrames();
   }
}

Vizibilitatea membrilor clasei si protectia acestora


1. Interfete, pachete, utilitarul jar, clase incuibate
2. Modificatori de acces
3. Polimorfism
4. Modificatori de tip
5. Instantele predefinite this si super
1. Interfete, pachete
O  interfata este o declaratie de tip care defineste de obicei un comportament. Este formata dintr-o serie de
constante si metode pentru care nu se specifica nici un fel de implementare. In Java se utilizeazainterfete pentru
a suplini lipsa mostenirii multiple. Si interfetele potavea modificatori. Din punctul de vedere al accesului la ele
pot fi publice(publicinterface .. ), accesibile si din alte pachete decat din cel care odefineste. Pot avea atasat si
modificatorul de tip abstract ( abstractinterface... ).
interface Scalabil{
    static final MARE = 2, MEDIU = 1, MIC =0;
    void scalare( int marime );
}
class Cutie implements Scalabil{
    public void scalare( int marime ){
        switch ( marime ){
           case MARE :{ ...; break; }.
           case MEDIU:{ ...; break; }
           case MIC     :{ ...; break; }
        }
    }
}
Deoarece interfata defineste un tip nou de aceea oriunde se pot utiliza clase se pot utiliza si interfte. De exemplu
in cazul exemplului de maisus putem avea urmatorul cod:
Scalabil s; // s-a declarat o referinta la interfata Scalabil
s = new Cutie(); // s-a instantiat un obiect din clasa Cutiecu care s-a initializat referinta s
Exemplu 1. Declaraea unei interfete si utilizarea acestuia
interface Scalabil{
    static final int MARE  = 2;
    static final int MEDIU = 1;
    static final int MIC   = 0;
    void scalare( int marime );
}
class Cutie implements Scalabil{
    public void scalare( int marime ){
        switch ( marime ){
           case MARE :System.out.println("MARE"); break;
           case MEDIU:System.out.println("MEDIU"); break;
           case MIC  :System.out.println("MIC"); break;
        }
    }
}
public class p_4_1{
    public static void main( String args[] )
    {
         Cutie c = newCutie();
         c.scalare(1);
 
         Scalabil s = newCutie();
         s.scalare( 2 );
    }
 
}
Exemplu 2.  Utilizarea interfetei List
Exemplul urmator afiseaza argumentelor liniei de comanda intr-o ordine aleatoare
import java.util.*;
public class Shuffle {
    public static void main(String args[]) {
 
        // l este o referintala o interfata List si poate fi initializata
        //cu orice obiect instantiat dintr-o implementarea a interfetei
 
        List l = Arrays.asList(args);
        Collections.shuffle(l);
        System.out.println(l);
    }
}
 
Crearea pachetelor
Un pachet este format dintr-un grup de clase si interfete. Clasele pe care le utilizam in aplicatiile noastre trebuie
sa fie cunoscute si de catrecompilator, respectiv de catre interpretor.
Codul sursa al unei clase  Java se numeste unitate de compilare.O unitate de compilare in mod normal este
format dintr-un singur fisiercontinand definitia unei singure clase (poate fi formata si din mai multeclase dar
numai una singura poate fi publica) si fisierul poarta numeleclasei. Clasa MyClass se defineste in fisierul
MyClass.java.Fiecare clasa Java apartine unui pachet. Daca nu se declara carui pachetii apartine clasa, atunci
va apartine pachetului implicit, altfel pachetuluispecificat.
Variabila mediului de executie CLASSPATH este utilizat atat de compilator cat si de interpretor pentru
localizarea unei resurse. Contine caile incare se afla pachetele Java. O cale poate specifica un catalog sau un
fisierdintr-un catalog dat. Pachetele pot fi arhivate si incarcatorul de claseva extrage din arhive clasele necesare.
Formatele de arhivare suportatede catre mediul de executie Java sunt JAR (Java Archive) si ZIP.Fisierele JAR
sunt create cu utilitarul jar care face partedin JDK. Intr-o arhiva JAR se poate pune absolut orice tip de fisier.
Decitoate clasele apartinand unei aplicatii pot fi impachetate intr-un singurfisier JAR, care se adauga pe urma la
CLASSPATH.
Utilizarea utilitarului jar (Similar cu utilitarul tar de sub UNIX):
 
Comanda Semnificatie
jar -cvf  <nume_arhiva> <catalog1> Se creaza arhiva cu numele specificat si care va contine
<catalog2> ... continutul cataloagelor specificate 
jar -tvf  <nume_arhiva> Afiseaza continutul arhivei
jar -xvf  <nume_arhiva> <catalog1> Extrage continutul arhivei, eventual doar continuturile
<catalog2> ... cataloagelor specificate.
c-create - creare arhiva
t-tell -afisare continut arhiva
x-extract - extragere din arhiva
v-verbose -cu mai multe informatii despre operatia curenta
f-file -primul argument este numele arhivei
$jar -cvf  Curs4.jar Curs4
added manifest
adding: Curs4/ (in=0) (out=0) (stored 0%)
adding: Curs4/Person.class (in=669) (out=372) (deflated 44%)
adding: Curs4/ppelda.java (in=665) (out=189) (deflated 71%)
adding: Curs4/classes/ (in=0) (out=0) (stored 0%)
adding: Curs4/classes/MyPackage/ (in=0) (out=0) (stored 0%)
adding: Curs4/classes/MyPackage/Person.class (in=692) (out=384)(deflated 44%)
adding: Curs4/classes/MyPackage/Student.class (in=488) (out=290)(deflated 40%)
adding: Curs4/classes/Test.class (in=888) (out=519) (deflated 41%)
adding: Curs4/Person.java (in=861) (out=300) (deflated 65%)
adding: Curs4/Test.java (in=587) (out=293) (deflated 50%)
adding: Curs4/Test.class (in=675) (out=405) (deflated 40%)
adding: Curs4/Curs4.jar (in=340) (out=183) (deflated 46%)
adding: Curs4/Student.java (in=522) (out=220) (deflated 57%)
$jar -xvf  Curs4.jar
   created: META-INF/
 extracted: META-INF/MANIFEST.MF
   created: Curs4/
 extracted: Curs4/Person.class
 extracted: Curs4/ppelda.java
   created: Curs4/classes/
   created: Curs4/classes/MyPackage/
 extracted: Curs4/classes/MyPackage/Person.class
 extracted: Curs4/classes/MyPackage/Student.class
 extracted: Curs4/classes/Test.class
 extracted: Curs4/Person.java
 extracted: Curs4/Test.java
 extracted: Curs4/Test.class
 extracted: Curs4/Curs4.jar
 extracted: Curs4/Student.java
$jar -tvf Curs4.jar
     0 Tue Mar 21 15:01:24 EET 2000 META-INF/
    66 Tue Mar 21 15:01:24 EET 2000 META-INF/MANIFEST.MF
     0 Thu Feb 10 13:18:26 EET 2000 Curs4/
   669 Mon Mar 20 21:15:04 EET 2000 Curs4/Person.class
   665 Mon Mar 20 20:51:52 EET 2000 Curs4/ppelda.java
     0 Tue Mar 21 08:01:02 EET 2000 Curs4/classes/
     0 Tue Mar 21 09:17:10 EET 2000 Curs4/classes/MyPackage/
   692 Tue Mar 21 12:57:50 EET 2000 Curs4/classes/MyPackage/Person.class
   488 Tue Mar 21 13:15:22 EET 2000 Curs4/classes/MyPackage/Student.class
   888 Tue Mar 21 13:23:40 EET 2000 Curs4/classes/Test.class
   861 Tue Mar 21 13:26:28 EET 2000 Curs4/Person.java
   587 Tue Mar 21 13:25:22 EET 2000 Curs4/Test.java
   675 Tue Mar 21 13:20:58 EET 2000 Curs4/Test.class
   340 Tue Mar 21 15:00:52 EET 2000 Curs4/Curs4.jar
   522 Tue Mar 21 13:38:18 EET 2000 Curs4/Student.java
 
Exemplu:
Fisierul: MyBaseClass.java
package MyPackage;
public class MyBaseClass{
..
}
Fisierul: MyDerivedClass.java
package MyPackage;
public class MyDerivedClass extends MyBaseClass{
..
}
Exemplul precedent este format din doua unitati de compilare care apartin aceluiasi pachet. Pentru compilarea
acestor fisiere se vor utiliza liniile de comanda:
1. mkdir  classes     //Se creaza catalogulclasses pentru fisierele .class
2. javac -d .\classes MyBaseClass.java   //Se compileaza unitatea
                                         //MyBaseClass.java, fisierul
                                         //rezultat se va pune in directorul
                                         //specificat dupa
                                         //optiunea -d
Rezultatul:  ---classes----MyPackage----MyBaseClass.class
3. javac -d .\classes -classpath .\classes;c:\jdk1.2\lib MyDerivedClass.java
    //Se compileaza unitatea MyDerivedClass.java, rezultatul se stocheaza tot
    //in directorul classes, iar pentru incarcarea claselor se utilizeaza caile
    //specificate
    //dupa optiunea -classpath. Aici s-au specificat doua cai de cautare:
    //subdirectorul classes al directorului curent si subdirectorul lib al
    //catalogului in
    //care s-a instalat JDK
Rezultatul: ---classes----MyPackage--------MyBaseClass.class
                                                                        |
                                                                        |-------MyDerivedClass.class
 
Utilizarea pachetelor
Pentru a utiliza clase dintr-un pachet putem sa alegem din 3 variante:
1. Se prefixeaza numele clasei cu numele pachetului
Exemple:
java.awt.Button b;
MyPackage.MyBaseClass ob;
MyPackage.MyDerivedClass od;
 2. Se importa clasele de care are nevoie aplicatia:
import java.awt.Button;
import MyPackage.MyBaseClass;
import MyPackage.MyDerivedClass
3. Se importa pachete intregi
import java.awt.*;
import MyPackage.*;
Exemplu complet:

// Se creaza catalogul classes


// Compilare: javac -d .\classes Person.java
// In catalogul  classes se creaza un subcatalog al acestuiaMyPackage si aici va fi
// pus si
// rezultatul compilarii, fisierul Person.class
package MyPackage;
public class Person{
        protected String name;
        protected int age;
        public Person( Stringname, int age){
                this.name = name;
                this.age  = age;
        }
        public String getName(){
                return name;
        }
        public int getAge(){
                return age;
        }
        public void setName(String name ){
                this.name = name;
        }
        public void setAge( intage ){
                this.age = age;
        }
}
 
//Compilare: javac -d ./classes -classpath c:\jdk1.2\lib;./classes
package MyPackage;
public class Student extends MyPackage.Person{
        protected String faculty;
        public  Student(String name, int age, String faculty){
                super( name, age );
                this.faculty = faculty;
        }
        public String getFaculty(){
                return faculty;
        }
        public void setFaculty(String faculty ){
                this.faculty = faculty;
        }
}
 
// Compilare: javac -d .\classes -classpath c:\jdk1.2\lib;.\classes Test.java
// Interpretare: java -classpath c:\jdk1.2\lib;.\classes Test
import MyPackage.Student;
import MyPackage.Person;
public class Test{
        public static void main(String args[] ){
                Person p1  = new Person("Peter",20);
                System.out.println("Name:"+p1.getName());
                Student s1 = new Student("Paul",24,"Engeneering");
                System.out.println("Name: "+s1.getName()+" Faculty: "+s1.getFaculty());
 
        }
}
 
Clase incuibate
Incepand cu versiunea 1.1 se pot declara clase in interiorull altorclase. In acest caz, obiecte din clasa incuibata
se pot declara doar inclasa care le contine. Se pot declara chiar si clase incuibate anonime.
Clase incuibate anonieme si instantierea acestora:
{
   ...
   addWindowListener( new WindowAdapter(){
       public void windowClosing( WindowEvent e ){
       {
           System.exit( 0 );
       }
       }
   );
   ...
}
Clase incuibate si instantierea acestora
class A{
   A(){}
   class B{
       B(){}
       public void doIt(){
           System.out.println("Hello");
       }
   };
}

public class p2{


   public static void main( String args[] )
   {
       A a = new A();
       A.B b = a. new B();
       b.doIt();

   }
}
Clase incuibate statice
class A{
   A(){}
   static class B{
       B(){}
       public static void doIt(){
           System.out.println("Hello");
       }
   };
}

public class p3{


   public static void main( String args[] )
   {
       A.B.doIt();

   }
}
Observatii:
1. Clasele incuibate nu pot contine metode statice, exceptie formand clasele incuibate statice
2. Clasele incuibate nestatice apartin claselor exterioare, de aceea instantierea obiectelor din aceste clase este
precedata de instantierea obiectelor din clasele exterioare.
In exemplul urmator vom utiliza interfata Enumeration:
//Interfata java.util.Enumeration
public interface Enumeration{
    boolean hasMoreElements();
    Object nextElement();
}
Un enumerator se utilizeaza pentru parcurgerea elementelor dintr-ostructura de date. In exemplul urmator vom
declara o lista de studentisi in interiorul acesteia o clasa care implementeaza interfata Enumeration.

 
2. Modificatori de acces
O clasa gata facuta se poate utiliza in  doua moduri:
 prin crearea de noi instante din clasa respectiva
 prin mostenire, derivarea de noi clase din clasa de baza
Accesul datelor din afara obiectului nu trebuie permis. De obicei clasatrebuia sa puna la dispozitia celor care o
utilizeaza o serie de metodepublice care permit manipularea acestor date. Exista si metode care suntmetode
ajutatoare si nu trebuie sa fie accesibile din afara. Pentru a putearealiza protectia membrilor clasei vom utiliza
asa zisele modificatoride acces. Modificatorii pot fi atasati atat claselor cat si membrilor acestora.Prima data ne
ocupam de modificatorii membrilor clasei (date+metode). InJava avem urmatoarele tipuri de modificatori
pentru membrii clasei:
 private -private
 public  -publice
 protected -protejate
 prietenosi
Membrii privati ai clasei nu sunt accesibili din afara clasei. Ei pot fiutilizati doar de catre celelalte metode ale
clasei.
Membrii publici sunt accesibili din afara clasei. Ei pot fi utilizatisi in clasele derivate. Nu exista nici o restrictie
in utilizarea acestora.
Membrii protejati sunt accesibili doar prin metodele clasei si ei vorfi accesibili si in clasele derivate.
Membrii prietenosi sunt declarate fara nici un fel de cuvant cheiesi ei vor fi accesibili din toate clasele din
pachetul caruia apartineclasa.
In continuare vizibilitatea datelor va fi notata in modul urmator:
+ ----> membru public
- ----> membru privat
# ----> membru protejat

In cazul mostenirii membrii privati ai clasei vor exista si in clasa derivata, dar acestia nu vor fi accesibili.
Din punctul de vedere al accesului la clasa avem doar doua tipuri declase:
 clase publice
 clase nepublice.
Clasele publice sunt accesibile pentru toate celelalte clase, iar celenepublice doar pentru clasele care fac parte
din acelasi pachet. Cele nepublicese declara normal fara vreun modificator, iar cele publice se declara
cumodificatorul public. (public class ...).
 
3. Polimorfism

Supraincarcarea metodelor (Methods overloading) inseamnadefinirea a doua sau a mai multe metode in cadrul
unei clase care diferaprin lista parametrilor si eventual prin tipul returnat. De exemplu inJava putem avea oricati
constructori pentru o clasa. Toti constructorii poarta numele clasei (au acelasi nume) ei difera doar prin
parametrii lor (Constructorii nu returneaza nici o valoare). Pentru exemplificare consideram clasa String din
pachetul java.lang si cativa din constructorii clasei:
    String()- creaza un obiect de tip string vid
    String( String) - creaza un obiect de tip string initializand cu un alt obiectde tip string (se copiaza
continutul parametrului)
    String( char []) - creaza un obiect de tip string initializand cu un tablou decaractere
    String( char s[],int indice_start, int numar_caractere ) -creaza un obiect de
tipstring initializand acesta cu un subtablou
In afara de constructor putem defini si alte metode cu acelasi nume.De exemplu:
    int max( int, int);
    float max( float,float );
Compilatorul poate decide despre care metoda este vorba din tipul sinumarul parametrilor. Deci adresa acestor
metode se cunoaste inca din fazade compilare.
Redefinirea metodelor (Methods overriding) inseamna caclasele derivate pot redefini metodele claselor parinte
utilizand aceasisintaxa ( tip returnat nume_metoda( lista parametrilor)). Un exemplu:
Metodele supraincarcate sunt legate in mod static, adica adresa lor secunoaste inca din faza compilarii.
Metodele redefinite sunt legate in moddinamic in timpul executiei. Acest lucru duce imediat la scaderea
vitezeide executie a programelor. In Java toate metodele pot fi redefinite, iardaca dorim interzicerea redefinirii
atunci putem utiliza modificatorulfinal (modificator de tip). Modificatorul final face ca un membru sa fieprivit
constant. Modificatorul se mai poate utiliza pentru campuri de date si pentru clase. Dintr-o clasa definita cu
modificatorul final nu putemderiva alte subclase.
 
4. Modificatori de tip
Acesti modificatori se pot defini pentru  clase, metode si variabilele clasei.
Modificatori pentru clase:
     final
     abstract
 O clasa se poate declara abstract sau final. Clasele finale nu potfi superclase pentru alte clase. Clasele abstracte
sunt clase care servescca superclase pentru clasele concrete care se vor deriva din aceasta. Dintr-o clasa
abstracta nu se pot crea instante.
Modificatori pentru metode:
 static
 abstract
 final
 native
 synchronized
Metodele statice apartin claselor (metode de tip clasa) si nu instantelor.Ele pot fi invocate fara existenta
instantelor, deci nu trebuie createobiecte din clasa respectiva ca se le putem utiliza aceste metode. Metodelede
conversie de la un tip de data la un alt tip de data sunt implementateca metode statice ale unor clase. Exemplu:
clasa Integer are o metodatoString(int ) care este definita ca o metoda statica. Aceste metode se apeleazain
modul urmator:
    nume_clasa.nume_metoda(listaparametrilor actuali)
Exemplu concret:
    int i;
    System.out.println(Integer.toString(i));
Metodele statice pot utiliza doar membrii statici ai clasei, ei nupot apela metode nestatice si nu pot utiliza
variabile nestatice. Dacaam dori sa contorizam numarul de instante create dintr-o anumita clasaam putea avea
urmatoarea declaratie:
 
class OClasa {
         static final numar_instante=0;
 
         public static incrementare(){
             numar_instante++;
         }
         public static get_numar_instante(){
             return numar_instante;
         }
         public OClasa(){
             incrementare();
         }
}
public class Control{
     public static void main( String args[]){
            System.out.println("Nr.instante:
            "+Integer.toString(OClasa.get_numar_instante()));
            OClasa v[]= new OClasa[5];
            for( int i=0; i<5; i++){
                 v[i]=new OClasa();
                 System.out.println("Nr. instante:"
                 +Integer.toString(OClasa.get_numar_instante()));
           }
    }
}

Metodele abstracte in Java sunt metode fara corp de implementare.Ele sunt declarate pentru a forta subclasele
care vor sa aiba instantesa implementeze metodele respective. Metodele abstracte se declara doarin clase
abstracte. Clasele derivate din clase abstracte care nu sunt declarateabstracte trebuie sa implementeze metodele
abstracte ale superclaselor.Metodele statice nu pot fi declarate si abstracte, deoarece o metoda staticaeste si
implicit metoda finala si nu poate fi rescrisa.
Metodele finale sunt metode care nu pot fi redefinite in subclaseleclasei in care au fost declarate. Declararea
metodelor cu clauza finaleste utila in primul rand pentru compilator, care poate sa faca legarestatica in cazul
acestor metode astfel crescand viteza de executie a programelorJava.
Metodele native sunt metode implementate intr-un alt limbaj deprogramare de obicei intr-un limaj nativ al
platformei (C, C++ sau asamblare).Metodele native nu au corp de implementare, ele fiind implementate intr-
unalt limbaj de programare. Aceste metode se comporta la fel ca toate celelaltemetode Java, pot fi mostenite,
redefinite etc.
Metodele sincronizate sunt utilizate pentru accesul unor resursecritice. In momentul apelarii unei asemenea
metode, resursei i se alocaun monitor, care blocheaza accesul altor metode la aceea resursa. Dupaterminarea
apelului se elibereaza monitorul acesta putand fi ocupat dealte metode sincronizate.
 
Modificatori pentru variabile
 static
 final
 transient
 volatile
Modificatorul static specifica ca variabila respectiva apartineclasei si nu ale instantelor si exista intr-un singur
exemplar. Toate instantele lucreaza cu aceeasi valoare. Daca o instanta modifica valoarea variabilei statice,
atunci aceasta modificare este vazuta si de celelalte instante.Variabilele statice sunt initializate la incarcarea
clasei si vor fi utilizatede catre metodele statice ale clasei. Vezi exemplul de la metode statice.
Modificatorul final se utilizeaza pentru declararea unor constante,deci variabile ale caror valori nu se modifica
pe parcursul executiei.Tocmai din aceasta cauza variabilele finale se initializeaza la declarare: finalint
numar = 10;
Modificatorul transient se utilizeaza in cazul obiectelor persistente pentru a marca datele care nu trebuie sa se
piarda la terminarea programului.
Modificatorul volatile specifica faptul ca variabila respectivapoate fi modificata asincron cu rularea aplicatiei.
Prin acest modificatorfortam masina vituala Java ca dupa modificarea variabilei sa scrie valoareainapoi in
memorie si operatie de citire se face tot din memorie., adicavariabila nu se poate stoca in cache sau in registrii
masinii virtuale.Singura problema cu acest modificator este ca in versiunea actuala incanu este implementata,
deci daca dorim sa protejam valoarea unei variabilela care au acces mai multe fire de executie, atunci utilizam
mecanismulde sincronizare.
 
 
5. Instantele predefinitethissi super
this este o referinta catre instanta curenta
super este o referinta la superclasa
Un exemplu de utilizare:
class Unu{
    String sir;
    public Unu( String sir ){
        this.sir = sir;
    }
}
class Doi extends Unu{
    int val;
    public Doi( String sir, int val ){
        super(sir); //apel constructor superclasa
        this.val = val;
    }
    public Doi( String sir, String altsir){
        super(sir);
        this.val = ( new Integer(altsir ) ).intValue();
    }
}

Crearea interfetelor grafice. Pachetul java.awt


1. Introducere
2. Componente grafice
3. Ierarhia claselor pentru componente
4. Obiecte peer
5. Conceptul Model/View/Controller (MVC)
6. Afisarea componentelor
7. Organizarea componentelor
Introducere
    Acest pachet ne ofera o coletie de clase pentru crearea interfetelor grafice. Aceste clase permit lucrul cu
ferestre, desenari, lucrul cu imagini si utilizarea componentelor ca butoane, liste, meniuri intr-un mod
independent de platforma. Pachetul java.awt contine clasele AWT (Abstract Window Toolkit) GUI iar
java.awt.image ne ofera niste facilitati in plus pentru lucrul cu imagini. Dupa cum numele sugereaza AWT este
o abstractie. Clasele si functionalitatile oferite sunt aceleasi pentru orice implementare Java. Pentru a obtine
independenta de platforma AWT utilizeaza toolkituri interschimbabile care actioneaza cu sistemul de ferestre
pe care se ruleaza aplicatia. De exemplu sa presupunem ca aplicatia noastra creaza un buton. Cand se ruleaza
aplicatia, toolkitul specific platformei afiseaza butonul conform platformei. Daca aplicatia se ruleaza sub
Windows vom obtine un buton de tip Windows, iar daca  se ruleaza sub Unix se va afisa un buton de tip X
Windows.
    Utilizarea componentelor din acest pachet este relativ usoara, pe cand toolkiturile GUI sunt foarte complexe,
dar aceasta este treaba celor care vor sa implementeze AWT-ul pe o noua platforma.
Realitatea este ca cele mai multe probleme despre Java se ridica la folosirea acestui pachet. Implementarea
toolkiturilor pentru acest pachet contin cele mai multe buguri Java. Tocmai din aceasta cauza JavaSoft a
introdus Swing care reprezinta noua generatie de componentele grafice si fac parte din JFC (Java Foundation
Classes).
Componente grafice
Componentele sunt elementele care se utilizeaza la crearea unei interfete grafice. Componentele sunt incluse in
containere, care la randul lor sunt componente speciale. Anumite componente pot fi containere pentru alte
componente: de exemplu o fereastra cu doua panouri. In acest caz fereastra este containerul, iar panelele sunt
componentele continute. Daca adaugam elemente ca butoane, liste, etichete, casete text etc. in cele doua
panouri atunci panoul va fi containerul iar elementele adaugate vor fi componentele.

Ierarhia claselor pentru componente

    Pentru simplitate impartim functionalitatea clasei Component in doua categorii: modul de afisare si
comportament. Aceasta  clasa contine metode si variabile care controleaza modul de afisare general al
componentei. Aici sunt incluse atributele pentru vizibilitate, marime, pozitie si atribute ca culoarea si fontul.
Clasa contine metode abstracte, care sunt implementate de subclasele pentru diferite tipuri de componente, si
care determina imaginea grafica a componentei. Prin comportamentul componentei intelegem actiunile
componentei la evenimentele primite de la  utilizator. In momentul cand utilizatorul actiuneaza asupra unei
componente (de exemplu apsa butonul mouse-ului) un fir de executie AWT informeaza toti receptorii
(componentele care vor sa primeasca acest eveniment) despre evenimentul produs. De exemplu la apasarea
unui buton se genereaza evenimentul ActionEvent. Cei care vor sa primeasca acest eveniment trebuie sa fie
inregistrati, aceasta insemnand ca trebuie sa fie obiecte care respecta interfata ActionListener.
    Evenimentele sunt receptionate de catre obiecte de tip Listener, care la primirea evenimentului apeleaza o
metoda de tratare a evenimetului. Obiectele care doresc sa primeasca evenimente trebuie sa implementeze
interfetele pentru tipurile de evenimente pe care doresc sa le primeasca. De exemplu evenimentele de tip
MouseEvents sunt primite de catre obiectele care respecta interfata MouseListener. Evenimentele reprezinta un
mecanism de comunicare intre obiecte (nu numai obiecte grafice). Evenimentele sunt importante mai alesin
cazul beanurilor Java.
    Containerele rezolva tratarea evenimentelor pentru toate componentele continute. De obicei containerul se
inregistreaza la sursa de evenimente pentru a primi evenimentele necesare pentru o anumita componenta.
 
Peer
In sectiunea precedenta tocmai am descris functionarea acestor componente si evenimentele cu ajutorul carora
aceste componente comunica intre ele. Asa sunt privite componentele de Java, dar nu si in lumea reala, adica pe
platforma pe care vor fi efectiv afisate componentele. Lumea reala este caracterizata de o anumita arhitectura si
de dispozitive fizice. La un anumit nivel obiectele noastre vor comunica cu obiecte care contin metode native
pentru mediul  in caare componentele trebuie sa interactioneze. Pentru a tine aceasta interactiune sub control
Java utilizeaza interfetele peer.  Aceasta interfata peer face posibila ca o componenta AWT Java  sa utilizeze
componenta corespunzatoare din lumea reala - obiectul peer  din mediul nativ. Programele noastre vor lucra cu
obiectele AWT Java, restul, partea grea este rezolvata de clasa Component.

    Este important sa intelegem aceasta arhitectura deoarece astfel putem intelege cam ce putem realiza cu
componentele. In figura preceedenta se poate vedea ce se intampla la crearea unui buton. Toolkitul este de fapt
o "fabrica de componente" native. Java utilizeaza aceasta fabrica pentru a separa funtionalitatea componentelor
de modalitatea de implementare pe un sistem de afisare nativ. Obiectul Toolkit contine metode pentru crearea
tuturor tipurilor de componente grafice. Daca nu ne place acest Toolkit oferit de mediul Java, putem sa scriem
toolkitul nostru propriu.
    Pachetul java.awt.peer contine o interfata pentru fiecare tip de componenta grafica. In acest pachet clasa de
baza este ComponentPeer de la care se deriva aproape toate celelalte clase. De exemplu clasei Button ii
corespunde clasa ButtonPeer. Apelul metodei setLabel() determina apelul metodei corespunzatoare al
obiectului nativ.

Conceptul Model/View/Controller (MVC)


    MVC este o metoda pentru producerea componentelor reutilizabile care sunt caracterizate prin structura,
reprezentare si comportament. MVC a fost introdus ca standard in primul rand pentru componentele grafice, dar
ideile de baza pot fi aplicate si la alte tipuri de componente. Ideea fundamentala este separarea modelului de
reprezentare. Pentru un singur model de date exista mai multe reprezentari diferite. De exemplu datelor dintr-un
spreadsheet putem atasa diferite tipuri de grafice. In cazul unei componente de tip Button modelul de date ar fi
o valoare logica pentru a reprezenta starea butonului, reprezentarea este modul de afisare, aspectul grafic al
butonului, iar evenimentele reprezinta partea de control.
    Comunicarea intre componentele grafice AWT respecta MVC. Receptorii evenimentelor sunt Observatori 
iar sursele evenimentelor Observabili. In momentul cand un obiect Observabil isi modifica starea informeaza
toti observatorii despre aceasta modificare.
    Conceptul "fabrica de componente" utilizat de catre obiectul Toolkit respecta tot MVC. Aceste "fabrici"
utilizeaza  interfetele Java pentru a separa implementarea obiectelor grafice de comportamentul acestora.
Componetele native trebuie sa respecte doar anumite interfete, nu trebuie sa se incadreze intr-o ierarhie de
clase. Singurul lucru pe care trebuie sa-l faca este implementarea metodelor din interfata.
 
Afisarea componentelor
Componetele pot fi rugate sa se autoafiseze. De exemplu daca fereastra care contine componentele este
acoperita de o alta fereastra, in momentul revenirii la fereastra initiala aceasta trebuie reafisata cel putin partial.
Cand AWT roaga componenta sa se autoafiseze, aceasta apeleaza metoda paint(). Metoda update() are aceasi
functionalitae, dar aceasta prima data sterge suprafata componentei si dupa aceea apeleaz metoda paint().
Componentele nu vor apela direct update(), aceasta metoda fiind apelata de catre repaint(). Aceasta metoda
roaga AWT sa programeze componenta in cauza pentru improspatare. Atat paint() cat si update() au un
parametru de tip Graphics care reprezinta contextul grafic a componentei in cauza. Corespunde suprafetei ecran
pe care componenta poate desena. Acesta este mecanismul utilizat de componente pentru autoafisare. Noi vom
utiliza aceste metode doar in cazul containerelor speciale, adica in cazul obiectelor de tip Panel, Canvas si
Applet.
 
Organizarea componentelor
Un organizator al componentelor este un obiect care stabileste marimea si pozitia componentelor in cadrul unui
container. Fiecare container are un organizator implicit care in caz de necesitate poate fi modificat.
Containerele principale sunt diferitele tipuri de ferestre, apleturi, panele etc. In cazul ferestrelor organizatorul
implicit este un obiect de tip BorderLayout, iar in cazul apleturilor si ale panourilor unul de tip FlowLayout.
Clasele principale organizatori de componente sunt: FlowLayot, GridLayout,CardLayout, BorderLayout si
GridBagLayout.

FlowLayout organizeaza componentele de la stanga la dreapta si daca se umple randul curent atunci
componetul urmator va fi plasat in randul urmator. Inaltimea randului este data de componenta de cea mai mare
inaltime pe randul respectiv.
 
FlowLayout fLayout = new FlowLayout();
setLayout( fLayout );
Button butonOk = new Button("Ok");
add(butonOk);
Button butonCancel = new Button("Cancel");
add(butonCancel);

sau scris mai compact:


setLayout( new FlowLayout());
add( new Button("Ok"));
add( new Button("Cancel"));

Organizatorul centreaza componentele adaugate, dar acest tip de aliniere poate fi schimbata prin metoda
setAlignment(). Metoda primeste ca parametru tipul alinierii. Tipurile de aliniere posibile sunt definite ca
constante in clasa FlowLayout (LEFT, RIGHT, CENTER)  si se utilizeaza in modul urmator:
FlowLayout fLayout = new FlowLayout();
fLayout.setAlignment(FlowLayout.LEFT)
setLayout( fLayout );

BorderLayout aranjeaza componentele in cinci regiuni: Nord, Sud, Vest, Est si Centru. La adaugarea
componentelor utilizand acest organizator trebuie specificat pe langa componenta adaugata si regiunea la care
se adauga. Atat BorderLayout cat si CardLayout permite specificarea spatiului intre componente indicand
numarul de pixeli pe orizontala respectiv pe verticala.
setLayout( new BorderLayout(), 10,6);
add( new Button("Unu"),"North");
add( new Button("Doi"),"East");
add( new Button("Trei"),"South");
add( new Button("Patru"),"West");

CardLayout (teanc de carti) se utilizeaza la organizarea componentelor care nu incap pe o anumita suprafata.
Componentele sunt organizate pe aceste carti dintre care unul singurul se va vedea pe ecran. Visual Caffe
introduce ferestrele cu mai multe pagini care pot inlocui acest organizator.
GridLayout imparte suprafata in randuri si coloane permitand aranjarea componentelor in aceste celule de
dimensiuni egale. Componentele sunt plasate in celule incepand cu primul rand dupa aceea urmatorul rand si
asa mai departe.
GridBagLayout este organizatorul cel mai avansat. Lucreaza asemanator ca GridLayout. Principala diferenta
fata de acesta este ca in caul acestui organizator o componenta poate ocupa mai mult decat o singura celula.
Un container poate sa contina la randul lui alte containere si fiecare dinntre aceste containere avand propriul
organizator. De exemplu o fereastra continand mai multe panele.

Fire de executie
Ce sunt firele de excutie si de ce avem nevoie de fire de executie?
Utilizarea firelor de executie in Java
1. Creare si lansare
2. Legarea firelor de executie
3. Fire de executie demon
4. Grupe de fire de executie
5. Starile unui fir de executie
6. Prioritatea firelor de executie
7. Sincronizarea firelor de executie
8. Problema producatorului si a consumatorului
9. Exemple
Ce sunt firele de executie?
O aplicatie Java ruleaza in interiorul unui proces al sistemului de operare. Procesul consta in segmente de cod si
segmente de date mapate intr-un spatiu virtual de adresare. Fiecare proces detine un numar de resurse alocate de
catre sistemul de operare, cum ar fi fisiere deschise, zone de memorie alocate dinamic sau fire de executie.
Resursele alocate procesului sunt eliberate la terminarea executiei procesului.
Un fir de executie este unitatea de executie a unui proces. Fiecarui fir de executie i se asociaza o secventa de
instructiuni, un set de registri CPU si o stiva. Procesul nu executa instructiuni, este un spatiu de adresare comun
pentru unul sau mai multe fire de executie. Firele de executie sunt cele care executa instructiunile.
Firele de executie seamana cu procesele, pot fi la fel planificate pentru executie. Principala diferenta este ca
firul se executa in spatiul de adresare al procesului caruia apartine si poate modifica valori care sunt vazute si
de celelalte fire care apartin aceluiasi proces.  Din aceasta cauza apare necesitatea ca firele sa comunice intre
ele, adica trebuie sincronizat accesul la datele utilizate in comun. Sincronizarea asigura siguranta datelor, adica
prin sincronizare se previn situatile ca un fir sa modifice o variabila care este tocmai utilazata de catre un alt fir
de executie.
Fir de executie = Secventa de instructiuni + un set de registi CPU + o stiva
Java fiind un limbaj interpretat, procesul detine codul interpretorului, iar codul binar Java (bytecode) este tratat
ca o zona de date de catre interpretor. Deci firele de excutie sunt create de fapt de catre interpretorul Java. La
lansarea in executie a unei aplicatii Java este creat si automat un prim fir de executie, numit firul principal.
Acesta poate sa creeze alte fire de executie, care la randul lor pot crea alte fire, si asa mai departe.
De ce avem nevoie de fire de executie?
Aplicatiile care utilizeaza mai multe fire de executie pot executa in paralel mai multe sarcini. De exemplu o
aplicatie care realizeaza o animatie intr-o fereastra, iar intr-o alta fereastra afiseaza rezultatul unei interogari de
baza de date.
Un program de navigare realizeaza cel putin doua lucruri in paralel: aduce date din retea si paralel afiseaza
aceste date.  Firele de executie faciliteaza ca programarea sa fie mai apropiata de gandirea umana. Omul de
obicei se ocupa de mai multe lucruri in acelasi timp. Un profesor in timp ce preda trebuie sa fie atent si la
studentii care il asculta.
Firele de executie Java ofera o serie de facilitati  programatorilor, dar exista si o serie de diferente fata de alte
pachete pentru fire de executie.  Comparand cu aceste pachete putem considera ca ne ofera mai putin, dar au si
un avantaj, simplitatea. Exista si un standard POSIX P10033.4a un pachet de fire de excutie, care poate fi
implementat in orice sistem de operare.
Orice pachet Java (de la Sun) este "Thread safe", adica este asigurat ca metodele continute in pachet pot fi
apelate simultan  de mai multe fire de executie.
 
Utilizarea firelor de executie in Java
1. Creare si lansare
In Java avem doua posibilitati de a crea un fir de executie:
 Prin definirea unei clase care mosteneste de la clasa predefinita Thread (deriva din clasa Thread)
 Prin implementarea interfetei Runnable
Clasa Thread este definita in pachetul java.lang avand o serie de metode. Principala metoda este metoda run().
Aceasta trebuie sa contina toate activitatile pe care firul trebuie sa le execute.
public class Thread extends Object implements Runnable
Clasa Thread este o clasa care are ca si superclasa clasa Object si implementeaza interfata Runnable. Interfata
Runnable declara o singura metoda run() si clasa Thread implementeaza aceasta. Masina virtuala Java permite
unei aplicatii sa ruleze mai multe fire de executie in paralel. Fiecare fir de executie are o prioritate si fiecare fir
poate fi marcat ca si fir demon. In momentul crearii unui fir de executie se seteaza si proprietatile acestea, adica
firul nou creat va avea aceasi prioritate ca si firul parinte si va fi de tip demon numai daca firul parinte este
demon.
Cand se interpreteaza codul octeti al unei aplicatii, masina virtuala Java creaza un prim fir de executie care
ruleaza metoda main. Masina virtuala Java continua sa execute acest fir sau alte fire noi create pana cand toate
firele de executie nedemon sunt distruse.
 
 
 
Thread
start()
run()
sleep()
interrupt()
join()
getName()
setName()
destroy()
isinterrupted()
Metoda  start() este utilizata pentru lansarea in executie a unui fir nou creat.  Metoda start() se utilizeaza o
singura data in ciclul de viata al unui fir . Firul se distruge cu metoda destroy(), si aceasta putand fi apelat o
singura data. Metoda sleep() se apeleaza cu un argument care reprezinta timpul in milisecunde in care firul de
executie asteapta(adoarme). sleep() este o metoda statica se apeleaza prefixat cu numele clasei. In caz ca firul
este intrerupt se genereaza exceptia InterruptedException. Deci apelul metodei trebuie facut intr-un context de
tratarea acestei exceptii.
try{
    Thread.sleep( 1000 )
}
catch ( InterruptedException e ){
    //Codul  de tratarea a exceptiei
}
Firul poate fi scos din starea de adormire prin apelul metodei interrupt(). Metoda se mai apeleaza pentru a
intrerupe executia unui fir care este blocat in asteptarea unei operatii lungi de intrare/iesire. Metoda join() se
utilizeza pentru legarea firelor de executie. Prin getName() si setName() putem obtine sau sa setam numele
firului.
Diagrama de clase:

Diagrama de colaborare:

Figura precedenta prezinta proiectul unei aplicatii, care creaza doua fire de executie si le starteaza, neasteptand
terminarea acestora. Firele vor afisa numele lor pana cand sunt oprite. Firele pot fi oprite cu combinatia de taste
Ctrl-C.
Crearea unui obiect din clasa MyThread nu inseamna si inceperea executiei firului creat. Firul poate fi lansat in
executie prin apelul metodei start(). Metoda start() apeleaza metoda run(), care executa instructiunile proprii
firului.
Sursa Java pentru exemplul precedent:
//Source file: MyThread.java
public class MyThread extends Thread
{
   public MyThread()
   {
   }
   public void run()
   {
    while( true )
      try{
           System.out.println(" Se ruleaza "+getName());
           sleep( 1000 );
      }
      catch( InterruptedException e ){}
   }
   public MyThread(String name)
   {
    super( name );
   }
}
//Source file: Control.java
public class Control
{
   public MyThread threads[];
   public Control()
   {
    threads = new MyThread[ 2 ];
    threads[ 0 ] = new MyThread( "Fir 1");
    threads[ 1 ] = new MyThread( "Fir 2");
    threads[ 0 ].start();
    threads[ 1 ].start();
   }
   public static void main(String[] args)
   {
    Control c = new Control();
   }
}
A doua modalitate de crearea unui fir de executie se utilizeaza cand clasa noua creata trebuie sa mosteneasca de
la doua clase. Deoarece Java nu permite mostenire multipla, se incearca simularea acestuia prin interfete.
Interfata este o declaratie care contine o serie de metode abstracte care trebuie implementate de catre clasa care
implementeaza interfata.
Interfata Runnable este:
 
public interface Runnable{
    public abstract void run();
}

 
 
 
 
 
In acest caz, clasa noua nu mosteneste metodele clasei Thread (start(), stop() etc. ) deci crearea firului se va face
prin crearea unui obiect de tip Thread care primeste ca parametru un obiect de tip Runnable. (Clasa Thread are
si un asemenea constructor).
Diagrama de clase:

Diagrama de colaborare:

Sursa Java:
//Source file: Control.java
public class Control
{
   public MyRunnable objects[];
   public Thread threads[];
   public Control()
   {
    objects = new MyRunnable[ 2 ];
    objects[ 0 ] = new MyRunnable();
    objects[ 1 ] = new MyRunnable();
    threads = new Thread[ 2 ];
    threads[ 0 ] = new Thread(objects[ 0 ]);
    threads[ 1 ] = new Thread(objects[ 1 ]);
    threads[ 0 ].start( );
    threads[ 1 ].start( );
   }
 
   public static void main(String[] args)
   {
    Control c = new Control();
   }
}
 
//Source file: MyRunnable.java
public class MyRunnable implements Runnable
{
   public MyRunnable()
   {
   }
   public void run()
   {
    while( true )
    {
      System.out.println( "Se ruleaza "+(Thread.currentThread()).getName() );
      try{
       Thread.sleep( 1000 );
      }
      catch( InterruptedException e ){}
     }
   }
}
 
2. Legarea firelor de executie
 
Legarea firelor de executie permite unui fir sa astepte ca un alt fir sa-si termine executia. De exemplu un fir
trebuie sa utilizeze date care sunt calculate de un alt fir de executie, atunci in momentul cand are nevoie de
aceste date isi intrerupe executia asteptand firul care ii va furniza acestea.  Pentru legarea firelor se utilizeaza
metoda join().  Metoda are doua forma de apel:
 
     join( int timp ) parametrul  reprezinta timpul de asteptare (in milisecunde), adica cat se asteapta
pentru terminarea firului,
     join() se asteapta terminarea firului indiferent cat trebuie asteptat

Exemplul urmator creaza doua fire de excutie. Unul dintre fire afiseaza un text si asteapta 500 de milisecunde
de cinci ori. Celalalt fir  isi afiseaza numele dupa care asteapta pe celalalt fir ca acesta sa-si termine executia.
Diagrama de secventiere:

Sursa Java:
//Source file: MyThread1.java
public class MyThread1 extends Thread
{
   public MyThread1(String name)
   {
    super( name);
   }
   public void run()
   {
     System.out.println(getName() + "is running");
        for(int i=0;i<5;i++){
         try{
                     sleep( 500 )  ;
            }
            catch( InterruptedException e ){}
            System.out.println(getName()+" writes "+Integer.toString(i));
       }
   }
}
//Source file: MyThread2.java
public class MyThread2 extends Thread
{
   public MyThread1 waitedThread;
 
   public MyThread2(String name, MyThread1 waitedThread)
   {
    super( name );
    this.waitedThread = waitedThread;
   }
   public void run()
   {
     System.out.println(getName()+" waits for Thread "+waitedThread.getName());
     try{
                 waitedThread.join();
     }
     catch( InterruptedException e ){}
     System.out.println(waitedThread.getName()+" has been finished");
     System.out.println(getName()+" has been finished");
   }
}
//Source file: Control.java
public class Control
{
   public static void main(java.lang.String args[])
   {
     MyThread1 thread1 = new MyThread1("First Thread");
     MyThread2 thread2 = new MyThread2("Second Thread",thread1);
     thread1.start();
     thread2.start();
   }
}
 3. Fire de executie demoni
Caracteristici:
 
 Sunt fire speciale similare cu procesele demoni.
 Realizeaza anumite activitati in fundal (background)
 Se distrug automat la terminarea celorlaltor fire de executie.
 Au prioritate de executie redusa, fiind planificate la CPU cand acesta nu ruleaza alte fire
Colectarea gunoiul in Java se relueza pe un fir demon. Firul poate fi transformat in fir demon prin apelul
metodei setDaemon().
Exemplul urmator ar trebui sa ne arate ca firele demoni sunt planificate pentru executie mai rar decat cele
normale.
 
class  MyThread extends Thread{
    public MyThread( String name, boolean is_Daemon ){
        super(name);
        setDaemon(is_Daemon);
    }
    public void run(){
      for(int i=0;i<5;i++){
            try{
                sleep( 500 )  ;
            }
            catch( InterruptedException e ){}
            System.out.println(getName() + "se ruleaza");
      }
    }

    }
    public class Control{
        public static void main( String args[] )
        {
            MyThread t1,t2;
            t1 = new MyThread("Normal", false );
            t2 = new MyThread("Demon ", true  );
            t1.start();
            t2.start();
        }
    }
 
 
4. Grupe de fire de executie
La crearea unui fir nou de executie, firul nou creat putem sa-l adaugam la un grup de fire de executie existent,
sau putem lasa in seama interpretorului Java la care grup sa-l adauge.
In exemplul demonstrativ vom utiliza metoda  yield() pentru a ceda unitatea centrala altui fir de executie
( astfel firul care cedeaza trece in coada de asteptarea a firelor gata de executie ).
    class MyThread extends Thread{
         MyThread( String nume ){
             super( nume );
         }
         public void run(){
             while( true ){
                 if (isInterrupted() )
                           break;
                 yield();
             }
         }
     }
 
     public class Control{
         static public void main( String args[] ){
             MyThread a, b, c;
             ThreadGroup group;
             // Obtinerea unei referinte la grup
             group = Thread.currentThread().getThreadGroup();
             System.out.println(" Nume grup: "+group.getName());
             System.out.println(" Nr. fire active: "+group.activeCount());
             a = new MyThread("Fir 1 ");
             b = new MyThread("Fir 2 ");
             c = new MyThread("Fir 3 ");
             a.start(); b.start(); c.start();
             System.out.println(" Nr. fire active: "+group.activeCount());
             a.interrupt();
             b.interrupt();
             c.interrupt();
         }
     }
 
5. Starile unui fir de executie

Fir "rulabil" (Runnable): se ajunge in aceasta stare prin apelul metodei start(). Firul trece in stare de
"nerulabil" (Not Runnable) prin apelul metodei sleep() , wait() sau daca firul se blocheaza intr-o operatie de
citire. Dupa terminarea asteptarii firul revine in starea "rulabil".
Firul ajunge in starea "mort", dupa ce se termina executia metodei run() sau daca se apeleaza metoda
destroy().
Firele sunt planificate pentru executie la fel ca si procesele, utilizand un algoritm adecvat pentru planificarea
lor. Daca masina virtuala se ruleaza pe un sistem de oprare care lucreaza cu fire de executie, atunci s-ar putea ca
fiecare fir de executie creat  de catre masina virtuala Java sa fie executat pe un fir nativ.
6. Prioritatea firelor de executie
Masina virtuala Java utilizeaza prioritatile firelor in planificarea firelor pentru executie. In Java sunt 10 niveluri
de prioritate. Avem si trei constante in clasa Thread ce  pot fi utilizate:
Thread.MIN_PRIORITY avand valoarea 1
Thread.MAX_PRIORITY  avand valoarea 10
Thread.NORM_PRIORITY avand valoarea 5
Prioritatea firului se seteaza cu metoda setPriority()
7. Sincronizarea firelor de executie.
    Fiecare fir de executie are o viata proprie si nu este interesat de ceea ce fac celelalte fire de executie. Daca
calculatorul este dotat cu mai multe procesoare, atunci diferitele fire de executie ale aceluiasi proces pot fi
planificate pentru executie pe diferite procesoare. Pentru cel care proiecteaza aplicatia, acest lucru este 
nesemnificativ, deoarece toate aceste lucruri cad in sarcina masinii virtuale si nu in sarcina programatorului. 
Totusi apare o problema in cazul aplicatiilor care se ruleaza pe mai multe fire si anume accesul resurselor
comune. Ca sa nu apara probleme, accesul la resursele utilizate in comun trebuie sincronizat. Sincronizarea se
bazeaza pe conceptul de monitor introdus de catre C.A. Hoare. Un monitor este de fapt un lacat atasat unei
resurse pentru a preveni utilizarea resursei in paralel.
    Un fir de executie ocupa un monitor daca apeleaza o metoda sincronizata. Daca un fir ocupa un monitor, un
alt fir care incearca ocuparea aceluiasi monitor, este blocat si asteapta pana la eliberarea monitorului.
Oricare obiect, care contine unul sau mai multe metode sincronizate, are si un monitor atasat. Metodele
sincronizate se definesc in modul urmator:
    public synchronized void my_method() {...}
    Orice clasa Java are un monitor atasat. Acest monitor se deosebeste de monitoarele atasate obiectelor, se
utilizeaza cand se apeleaza metodele statice sincronizate ale clasei.
    public static synchronized void my_static_method() {...}
 
wait() si notify()
    Cu ajutorul cuvantului-cheie synchronized putem serializa executia anumitor metode. Metodele
wait() si notify() ale clasei Object extind aceasta capabilitate. Utilizand wait() si notify(), un fir
de executie poate elibera monitorul ocupat, asteptand un semnal pentru reocuparea acestuia. Metodele pot fi
utilizate doar in metode sincronizate sau in blocuri de instructiuni sincronizate. Executand wait() intr-un bloc
sincronizat, firul elibereaza monitorul si adoarme. De obicei firul recurge la aceasta posibilitate cand trebuie sa
astepte aparitia unui eveniment intr-o alta parte a aplicatiei. Mai tarziu, cand apare evenimentul, firul, in care a
aparut evenimentul, apeleaza notify() pentru a trezi firul adormit. Firul trezit ocupa din nou monitorul si isi
continua activitatea din punctul de intrerupere.
class MyClass{
    public synchronized void method1(){}
    public synchronized void method2(){}
    public static synchronized void method3(){}
}
...
MyClass object1 = new MyClass(); // Se ataseaza un monitor obiect pentru sincronizarea
accesului la metodele method1() si method2() pentru firele de executie al obiectului
object1
MyClass object2 = new MyClass(); // Se ataseaza un monitor obiect pentru sincronizarea
accesului la metodele method1() si method2() pentru firele de executie al obiectului
object2
Pentru exemplul precedent se vor crea trei monitoare. Un monitor de clasa, care este responsabil pentru
sincronizarea accesului la metoda statica ( metoda clasei) method3() si doua monitoare obiect, cate unul pentru
fiecare obiect declarat pentru sincronizarea accesului la method1(0 si method2()
Metoda notify() anunta intotdeauna un singur fir de executie care asteapta pentru a accesa monitorul. Exista si o
alta metoda notifyAll() care se utilizeaza atunci cand mai multe fire de executie concureaza pentru  acelasi
monitor. In acest caz fiecare fir este instiintat si ele vor concura pentru obtinerea monitorului. Metodele wait(),
notify() si notifyAll() se utilizeaza doar in metode sincronizate, altfel mediul de executi arunca exceptia
IllegalMonitorStateException. Exemplul urmator se compileaza fara erori, dar se arunca aceasta exceptie pe
parcursul executiei.
public class test{
 public test(){
     try{
          wait();
     }
     catch( InterruptedException e ){}
 }
 public static void main( String args[] ){
  test t = new test();
 }
}
 
8. Problema producatorului si a consumatorului
Specificatia problemei

Intr-un birou lucreaza mai multi functionari, fiecare elaborand documente pe cate un calculator legate intr-o
retea. La aceeasi retea sunt legate si cateva imprimante, fiecare avand acces la toate imprimantele conectate. Sa
se simuleze functionarea biroului stiind ca nu toti functionarii elaboreaza documentele in acelasi ritm si
functionarii selecteaza aleator imprimantele pe care vor tipari documentele.
Rezolvarea problemei
Perspectiva conceptuala
Se vede clar ca problema se reduce la celebra problema a producatorului si a consumatorului, in acest caz avand
mai multi
producatori si mai multi consumatori. Fiecare functionar poate fi privit ca un producator si fiecare imprimanta
ca un consumator.
Fiecare imprimanta are atasat o coada de asteptare (buffer) unde sunt gestionate documentele inainte de a fi
tiparite.
Trecem la identificarea obiectelor (claselor). Principalele clase vor fi urmatoarele:
 
      Producer (Functionar)
      Consumer (Imprimanta)
      Buffer (Coada de asteptare a imprimantei)
Pentru a simula paralelismul vom lucra cu fire de executie, adica fiecare functionar si imprimanta va fi
implementat ca un fir de
executie. Evident apare si problema sincronizarii la cozile de asteptare atasate imprimantelor.

Figura de mai sus este diagrama de clasa din perspectiva conceptuala.


Clasele Producer si Consumer sunt derivate din clasa Thread (fir de executie).
Intre Producer si Buffer avem o asociere unidirectionala, numai Producer cunoaste Buffer si multiplicitatea
asocierii este 1:n,
adica un obiect producator cunoaste n cozi de asteptare.
Intre Consumer si Buffer exista tot o asociere unidirectionala, imprimanta cunascand coada de asteptare, dar
aici multiplicitatea
asocierii este 1:1 insemnand ca o imprimanta are atasata o singura coada de asteptare.
 
Perspectiva specificatiei

Continuam cu descrierea acestor clase, adica a atributelor si metodelor principale ale acestora.
Clasa Buffer
Atribute: Aceasta clasa trebuie sa aiba un atribut dimensiune prin care specificam dimensiunea maxima a unui
buffer si inca un
alt atribut de tip colectie pentru a permite stocarea produselor.
Metode: Avem nevoie de doua metode, una care adauga un produs la sfarsitul cozii de asteptare si inca una care
scoate
primul produs din coada de asteptare.
Clasa Producer
In cazul acestei clase nu avem nevoie de atribute, doar de cele care se nasc in urma asocierilor. Clasa va avea
un constructor
care initializeaza numele firului de executie (atribut mostenit de la clasa parinte Thread) si atributul theBuffer
creat in urma
asocierii cu clasa Buffer. Acest atribut este unul de tip array. Ca sa putem initializa acest atribut, trebuie sa
avem create in
prealabil obiectele de tip Buffer.
Clasa Consumer
Clasa va avea un constructor care initializeaza numele firului de executie (atribut mostenit de la clasa parinte
Thread) si atributul
theBuffer creat in urma asocierii cu clasa Buffer. Acest atribut este un atribut simplu, dar ca sa-l putem
initializa, trebuie sa avem
create in prealabil obiectele de tip Buffer si atunci fiecarui obiect Buffer ii corespunde un obiect de tip
Consumer.
Introducem si o clasa Product pentru identificarea documentelor produse. Fiecare document va avea un nume,
deci clasa
trebuie sa aiba un atribut tip sir de caractere reprezentand numele produsului. Acest atribut va fi initializat cu
constructorul clasei
si adaugam o metoda de tip query pentru returnarea acestui atribut.
Pentru numerotarea documentelor vom introduce si o clasa Counter. La pornirea aplicatiei se creaza o singura
instanta din
aceasta clasa si toti producatorii o vor folosi. Clasa va avea un atribut value de tip intreg lung si o operatie next,
pentru
returnarea urmatoarei valori a numaratorului.
 
Perspectiva de implementare

La diagrama din sectiunea precedenta vom adauga inca o clasa Control. Aceasta clasa va avea cate o asociere
cu calsele
Producer, Consumer si Buffer. Multiplicitatea acestor asocieri se vede pe diagrama alaturata. Constructorul
clasei face
instantierile obiectelor Buffer, Producer, Consumer. Fiecare asociere are multiplicitatea 1:n, un singur obiect de
tip control si
mai multi bufferi, producatori si consumatori. Trebuie sa vaem grija ca numarul bufferelor sa corespunda cu
numarul
imprimantelor.

Codul final:
//Source file: Control.java
 
public class Control
{
   private Producer theProducer[];
   private Consumer theConsumer[];
   private Buffer theBuffer[];
   public Control()
   {
   }
   public Control(int np, int nc)
   {
        int i;
      theProducer = new Producer[ np ];
      theBuffer   = new Buffer  [ nc ];
      theConsumer = new Consumer[ nc ];
      for( i=0; i<nc; i++ )
       theBuffer[ i ] = new Buffer( 3 );
      for( i=0; i<np; i++ ){
       theProducer[ i ] = new Producer( theBuffer , "Producer.
"+Integer.toString( i+1 ));
       theProducer[ i ].start();
      }
      for( i=0; i<nc; i++ ){
       theConsumer[ i ] = new Consumer( theBuffer[ i ], "Consumer. "+
Integer.toString( i+1 ));
       theConsumer[ i ].start();
       }
   }
   public static void main(String[] args)
   {
    Control c = new Control(4,3);
   }
}
 
//Source file: Producer.java
 
public class Producer extends Thread
{
   private Buffer theBuffer[];
 
   public void run()
   {
      try{
             while( true ) {
               Product p = new Product( Long.toString(Counter.next()) );
             // Choose a buffer
              int nr = ( int )Math.round( Math.random() * theBuffer.length );
              if( nr >= theBuffer.length ) nr = theBuffer.length -nr;
              System.out.println( getName() +": "+p.getName()+"...."+Integer.toString( nr
));
              theBuffer[ nr ].put( p );
              sleep( Math.round( Math.random()*1000 ));
             }
         }
         catch( InterruptedException e ){}
   }
 
   public Producer(Buffer[] buffer, String name)
   {
     super( name );
     theBuffer = buffer;
   }
}
//Source file: C:\Manyi\TempRose\Consumer.java
 
public class Consumer extends Thread
{
   private Buffer theBuffer;
   public Consumer()
   {
   }
 
   public void run()
   {
     try{
             while( true ) {
                  Product p= theBuffer.get();
                  System.out.println( getName()+" "+p.getName());
                  sleep(2000);
             }
         }
         catch( InterruptedException e ) {}
   }
 
   public Consumer(Buffer buffer, String name)
   {
    super( name );
    theBuffer = buffer;
   }
}
//Source file: C:\Manyi\TempRose\Buffer.java
import java.util.Vector;
public class Buffer
{
   private int maxBuffer;
   private Vector buffer = new Vector();
   public Buffer()
   {
   }
   public synchronized void put(Product p) throws InterruptedException
   {
       while( buffer.size() == maxBuffer )
             wait();
       buffer.addElement( p );
       notify();
   }
 
   public synchronized Product get() throws InterruptedException
   {
       
         while( buffer.size() == 0 )
             wait();
         Product p  = (Product) buffer.firstElement();
         buffer.removeElement( p );
         notify();  
         return p;
   }
   public Buffer(int maxBuffer)
   {
    this.maxBuffer = maxBuffer;
   }
}
//Source file: Counter.java
 
public class Counter
{
   private static long value = 0;
   public Counter()
   {
   }
   public static long next()
   {
    return value++;
   }
}
//Source file: Product.java
public class Product
{
   private String name;
   public Product()
   {
   }
   public Product(String name)
   {
    this.name = name;
   }
   public String getName()
   {
    return name;
   }
}
 
9. Exemple
9.1. Specificatie problema:
Intr-o gara sunt 3 case de bilete. Calatorii  se aseaza la coada la una dintre cozi ( de ex: la care e mai scurta ),
dar casieritele nu elibereaza biletele in acelasi ritm. Simulati functionarea caselor de bilete.
 
Diagrama de clase
Sursa Java:
Calator.java
package Gara;
import java.util.*;
public class Calator{
    private long cID;
    private Date tsosire;
    private Date tplecare;
    public Calator( long cID, Date tsosire, Date tplecare ){
         this.cID = cID;
         this.tsosire  = tsosire;
         this.tplecare = tplecare;
    }
    public long getCID(){
         return cID;
    }
    public Date getTsosire(){
         return tsosire;
    }
    public Date getTplecare(){
         return tplecare;
    }
    public String toString(){
         return ( Long.toString(cID)+" "+tsosire.toString()+" "+tplecare.toString());
    }
}
Casa.java
package Gara;
import java.util.*;
public class Casa extends Thread{
    private Vector calatori=new Vector();
        public Casa( String name ){
        setName( name );
    }
    public void run(){
        try{
         while( true ){
              sterge_calator();
              sleep( (int) (Math.random()*4000) );
         }
        }
        catch( InterruptedException e ){
             System.out.println("Intrerupere");
             System.out.println( e.toString());
         }
    }
    public synchronized void adauga_calator( Calator c ) throws InterruptedException
    {
         calatori.addElement(c);
         notifyAll();
    }
    public synchronized void sterge_calator() throws InterruptedException
    {
         while( calatori.size() == 0 )
             wait();
         Calator c = ( Calator )calatori.elementAt(0);
         calatori.removeElementAt(0);
         System.out.println(Long.toString( c.getCID())+" a fost deservit de casa
"+getName());
         notifyAll();
    }
    public synchronized long lungime_coada() throws InterruptedException{
         notifyAll();
         long size = calatori.size();
         return size;
    }
}
Producator.java
package Gara;
import java.util.*;
public class Producator extends Thread{
 
    private Casa casa[];
    private int nr_case;
    static long ID =0;
    private int nr_clienti;//Numar calatori care trebuie deserviti de catre casele de
bilete
    public Producator( int nr_case, Casa  casa[], String name, int nr_clienti ){
         setName( name );
         this.nr_case = nr_case;
         this.casa = new Casa[ nr_case ];
         this.nr_clienti = nr_clienti;
         for( int i=0; i<nr_case; i++){
             this.casa[ i ] =casa[ i ] ;
         }
    }
    private int min_index (){
      int index = 0;
      try
      {
         long min  = casa[0].lungime_coada();
         for( int i=1; i<nr_case; i++){
             long lung = casa[ i ].lungime_coada();
             if ( lung < min ){
                      min = lung;
                      index = i;
             }
         }
      }
      catch( InterruptedException e ){
           System.out.println( e.toString());
       }
      return index;
    }
 
    public void run(){
      try
      {
         int i=0;
         while( i<nr_clienti ){
             i++;
             Calator c = new Calator( ++ID, new Date(), new Date() );
             int m = min_index();
             System.out.println("Calator :" +Long.toString( ID )+" adaugat la CASA "+    
Integer.toString(m));
             casa[ m ].adauga_calator( c );
             sleep( (int) (Math.random()*1000) );
         }
      }
      catch( InterruptedException e ){
          System.out.println( e.toString());
      }
    }
}
TestGara.java
import Gara.*;
public class TestGara{
    public static void main( String args[] ){
     int i;
     int nr = 4;
     Casa c[] = new Casa[ nr ];
     for( i=0; i<nr; i++){
         c[ i ] = new Casa("Casa "+Integer.toString( i ));
         c[ i ].start();
     }
     Producator p = new Producator( nr , c, "Producator",30);
         p.start();
    }
}

Tratarea evenimentelor
1. Evenimente
2. Receptorii evenimentelor, interfete Listener
3. Sursele evenimentelor
4. Livrarea evenimentelor
5. Evenimente AWT
6. java.awt.event.InputEvent
7. Clase Adapter
8. Exemple
1. Evenimente
Daca dorim sa scriem aplicatii bazate pe evenimente in Java trebuie sa utilizam mecanismul de evenimente al
limbajul. In Java componentele GUI comunica intre ele cu ajutorul evenimentelor. Un eveniment este trimiterea
unui mesaj. De exemplu un buton informeaza aplicatia ca utilizatorul l-a apasat. Aceasta informare se
realizeaza cu ajutorul unui eveniment. Mesajele sosesc de la surse de evenimente iar destinatarii acestor
evenimente sunt receptionarii evenimentelor. La receptia evenimentului, in obiectul receptor se executa o
metoda. Evenimentul insusi este un obiect care contine informatii in legatura cu evenimentul produs. De
exemplu daca dorim sa cream evenimente noi atunci va trebui sa extindem clasa java.util.EventObject. Toate
tipurile de evenimente create de catre utilizatori trebuie sa fie subclasele acestei clase.
public class KeyboardEvent extends java.util.EventObject{
    private char key;
    KeyboardEvent ( java.awt.Component source, char key){
        super( source );
        this.key = key;
    }
}
Evenimentele sunt trimise    de la o singura sursa si pot fi receptionate de mai multi destinatari. Destinatarul
(obiectul receptor) trebuie sa implementeze metode de tratarea tuturor tipurilor de evenimente ai caror receptor
poate fi.
Exemple de tipuri de evenimnete:
ActionEvent  se genereaza la apasarea butoanelor, selectia din meniuri, terminarea editarii unei casete text
(apasarea tastei Enter in caseta).
MouseEvent se genereaza la operarea cu mouseul pe suprafata componenetei. Un eveniment de tip
ActionEvenet contine numele actiunii, iar un eveniment de tip MouseEvent  descrie pozitia mousului respectiv
starea butoanelor.
2. Receptorii evenimentelor, interfete Listener
Un eveniment  se livreaza destianatarului prin transmiterea ca parametru unei metode de tratare a
evenimentelor. Evenimentele de tip ActionEvent sunt livrate metodelor actionPerformed() cu urmatoarea
sintaxa:
//Receiver
public void actionPerformed(ActionEvent e){
                ...
}

Pentru fiecare tip de eveniment exista o interfata care trebuie implementata de catre toti cei care doresc sa
primeasca acel tip de eveniment. Orice obiect care doreste sa primeasca evenimente de tip ActionEvent trebuie
sa implementeze interfata ActionListener.
 
public interface ActionListener extends java.util.EventListener{
    public void actionPerformed( ActionEvent e );
}
Toate interfetele de tip "listener" sunt subinterfetele lui java.util.EventListener. Aceasta din urma este o
interfata vida. Interfetele ajuta la identificarea obiectelor capabile de o primi anumite tipuri de evenimente.
Interfata MouseListener descrie metodele necesare primirii evenimentelor MouseEvent ( intrarea mouseului
intr-o zana sau iesirea dintr-o zona, apasarea  sau eliberarea unui butom al mouseului. MouseMotionListener
este o interfata pentru a identifica evenimentele de tip miscarea(move) sau tarairea (drag) mouseului.
 
3. Sursele evenimentelor
In sectiunea precedenta am descris mecanismul de primire a evenimentelor in partea receptorului. In aceasta
sectiune vom descrie cum atentioneaza receptorul sursele de evenimente ca acestia sa porneasca trimiterea
evenimentelor daca acestia apar.  Pentru a primi un eveniment un obiect receptor trebuie sa se inregistreze la
sursa de evenimente transmitand acestuia o referinta catre el.  De exemplu clasa  AWT Button este o sursa de
evenimente ActionEvent. Pentru a fi recptor al acestui eveniment codul trebuie sa arate in modul urmator:
 
//sursa de eveniment de tip ActionEvent
Button buton = new Button("Ok");
//receptor de ActionEvenet
class Receptor implements ActionListener{
    setupReceiver(){
               ...
               buton.addActionLisener( this );
    }
    public void actionPerformed( ActionEvent e ){
        // Ce sa se faca la apasarea butonului
    }
}
Sursa de eveniment trebuie sa gestioneze toti receptorii, pentru acesta trebuie sa implementeze doua metode:
 
//sursa de ActionEvent
public void addActionListener( Actionlistener listener ){
    ...
}
public void removeActionListener( ActionListener listener ){
    ...
}
Metoda a doua removeActionListenner() se utilizeaza in cazul cand un receptor actual nu mai necesita serviciile
oferite de sursa si in continuare nu mai doreste sa primeasca evenimente.
 
4. Livrarea evenimentelor
Evenimentele AWT sunt difuzate, adica evenimentul are o singura sursa dar poate sa aiba mai multi receptori.
Evenimentele sunt difuzate utilizand modelul observator/observabil. In momentul inregistrarii unui receptor la
o sursa de evenimente, sursa adauga referinta primita la o lista. Cand apare un eveniment obiectul generat va fi
livrat tuturor obiectelor din lista.

Nu se stie care dintre acesti receptori va fi primul caruia se livreaza evenimentul. De altfel nu se ofera nici o
garantie pentru cazurile in care un obiect receptor se inregistreaza de mai multe ori.
 
5. Evenimente AWT
Toate evenimentele utilizate de componentele GUI al paachetului AWT sunt subclase ale clasei
java.awt.AWTEvent. Ierarhia acestor evenimente se prezinta in figura urmatoare:
ComponentEvent este clasa de baza. Aceasta clasa include evenimente de tip notificare in caz ca o componenta
isi schimba dimensiunea sau vizibilitatea sau daca se intampla evenimente cu mouseul sau tastatura care
afecteaza componenta respectiva. Apar evenimente ContainerEvent daca se adauga respectiv se sterg
componente dintr-un container.
Evenimente pentru componente  si containere AWT
 
 
Eveniment Cine produce? Interfete Listener Metode de tratare
componentResized()
componentMoved()
ComponentEvent Toate componentele ComponentListener
componentShown()
componentHidden()
focusGained()
FocusEvent Toate componentele FocusListener
focusLost()
keyTyped()
KeyEvent Toate componentele KeyListener keyPressed()
keyReleased()
mouseClicked()
MouseListener
mousePressed()
 
mouseReleased()
 
MouseEvent Toate componentele mouseEntered()
 
mouseExited()
MouseMotionListener
mouseDragged()
 
mouseMoved()
ContainerEvent Toti containerii ContainerListener componentAdded()
componentRemoved()
Evenimente specifice componentelor AWT
 
Eveniment Cine produce? Interfete Listener Metode de tratare
TextField
MenuItem
ActionEvent ActionListener actionPerformed()
List
Button
List
CheckBox
ItemEvent ItemListener itemStateChanged()
Choice
CheckboxMenuItem
ScrollPane
AdjustmentEvent AdjustmentListener adjustmentValueChanged()
Scrollbar
TextArea
TextEvent TextListener textValueChanged()
TextField
windowOpened()
windowClosing()
windowClosed()
Frame
WindowEvent WindowListener windowIconified()
Dialog
windowDeiconified()
windowActivated()
windowDeactivated()
6. java.awt.event.InputEvent
MouseEvent si Keyevent sunt evenimente de tip InputEvent. Daca utilizatorul misca mouseul sau apasa o tasta
se genereaza evenimente de acest tip avand ca sursa componenta pe suprafata caruia a avut loc evenimentul.
Aceste evenimente si alte cateva evenimente AWT sunt plasate intr-o coada de evenimente gestionata de  catre
AWT. Deci AWT gestioneaza aceste evenimente si tot AWT este responsabil pentru livrarea acestora catre
receptori.
Clasa InputEvent  defineste o serie de constante pentru a putea determina alte informatii despre evenimentul
produs. De exemplu daca este un eveniment cu tastatura ne intereseaza  daca concomitent cu o anumita tasta
obisnuita s-a mai apasat si o tasta de control Ctrl sau Alt, iar in cazul evenimentelor cu mouseul s-ar putea sa ne
intereseze si alte detalii,  care dintre butoanele mouseului a fost apasat.
Lista constantelor definitae:
SHIFT_MASK
CTRL_MASK
META_MASK
ALT_MASK
BUTTON1_MASK
BUTTON2_MASK
BUTTON#_MASK

Pentru  a  verifica aceste lucruri se va utiliza metoda getModifiers() a clasei InputEvent:


 
public void mousePressed( MouseEvent e){
    int mods = e.getModifiers();
    if( ( mods & InputEvent.SHIFT_MASK) != 0 ){
            // SHIFT apasat
            ...
    }
}
Evenimentele produse  pot fi putin modificate inainte ca acestea sa ajunga la receptorii lor. De exemplu
caracterul apsat de utilizator poate fi schimbat cu una dintre metodele: setKeyChar(), setKeyCode() sau
setKeyModifiers(). Schimband caracterul in evenument putem obtine ca pe ecran sa apara  deja caracterul
schimbat. De exemplu in caz ca dorim ca in campuri text sa apara doar majuscule indiferent de ce introduce
utilizatorul atunci se aplica aceasta metoda.
 
7. Clase Adapter
Nu este o idee prea buna ca componentele unei aplicatii sa primeasca evenimentele in direct. Cateodata nici nu
este posibil. Consideram exemplul urmator pentru simularea unui automat de bauturi. Automatul ne ofera trei
sortimente: cafea, ceai si suc. Proiectam pentru aceasta aplicatie o interfata grafica formata din trei butoane,
cate un buton pentru fiecare tip de bautura.  In prima implementare obiectul nostru de Bauturi_Adapter
primeste evenimentele direct de la butoane. Problema este ca obiectul trebuie sa stie mai mult decat servirea
bauturilor, stie si faptul ca este utilizat de trei butoane. Deci partea de GUI se suprapune cu logica aplicatiei.

O metoda mai buna este de a separa partea de GUI impreuna cu tratarea evenimentelor de logica aplicatiei.  
Pentru a realiza aceasta separare vom utiliza clase de tip adapter intre sursa si receptor . Un adaptor este un
obiect care in functie de un eveniment sosit apeleaza o anumita metoda. Implementarea primului adaptor ar fi:
 
class BauturiAdapter1 implements ActionListener{
    Bauturi b;
    BauturiAdapter1 ( Bauturi b ){
            this.b =  b;
    }
    public void actionPerformed( ActionEvent e ){
        b.Bautura_Cafea();
    }
}

Clasa care implementeza interfata grafica ar trebui sa  contina:


 
Bauturi b = ...;
Button buton_ceai, buton_cafea, buton_suc;
...
buton_ceai.addActionListener( new BauturiAdapter1(b));
buton_cafea.addActionListener( new BauturiAdapter2(b));
buton_suc.addActionListener( new BauturiAdapter3(b));

Astfel am separat logica aplicatiei de interfata la care este legata.


 
Multe din interfetele de tip Listener contin mai multe metode de tratarea evenimentelor. Din nefericire acesta
inseamna ca, clasa care este interesata in aceste tipuri de evenimente trebuie sa implementeze toate metodele
declarate in interfata. Spre ajutorul programatorilor au fost create niste clase de tip Adapter care implementeaza
aceste metode. Deci daca avem nevoie de o anumita interfata, vom deriva din aceasta clasa de tip Adapter
implementat special pentru interfata dorita de tip Listener. DE exemplu pentru evenimentele cu mouseul exista
clasa MouseAdapter care implementeza interfata MouseListener.
public void mouseClicked( MouseEvent e ) {};
public void mousePressed( MouseEvent e ){};
public void mouseReleased( MouseEvent e ) {};
public void mouseEntered( MouseEvent e ){};
public void mouseExited( MouseEvent e ) {};

Exemplu de utilizare:
 
oComponenta.addMouseListener( new MouseAdapter(){
                                     public void MousePressed( MouseEvent e ){
                                         obiect.show();
                                         ...
                                     }
                              }
                            );

In constructia de mai sus am utilizat o clasa incuibata.


 
8. Exemple
Exemplu 1. Sa se scrie o aplicatie care afiseaza o fereastra avand o caseta text si  trei butoane avand  etichetele
1,2,3. La apasarea butoanelor in caseta text sa apara eticheta butonului apasat.
Diagrama de clasa:
import java.awt.Frame;
import java.awt.event.ActionListener;
import java.awt.Button;
import java.awt.TextField;
import java.awt.event.ActionEvent;

public class MyFrame extends Frame implements ActionListener 


{
        private Button butoane[];
        private TextField tf;

        public MyFrame() 


        {
                setLayout( new java.awt.FlowLayout());
                tf = new TextField( 10 );
                add( tf );
                butoane = new Button[ 3 ];
                for( int i = 0; i< butoane.length; i++ )
                {
                        butoane[ i ] = new Button( Integer.toString( i ));
                        butoane[ i ].addActionListener( this );
                        add( butoane[ i ] );
                }
                // Tratarea evenimentelor cu fereastra
                addWindowListener( new java.awt.event.WindowAdapter()
                {
                        public void windowClosing( java.awt.event.WindowEvent e ){
                        setVisible( false );
                        System.exit( 0 );
                }
                });
        }

        public void actionPerformed(ActionEvent arg0) 


        {
                String s = arg0.getActionCommand();
                tf.setText( s );
        }

        public static void main(String[] args) 


        {
                MyFrame f = new MyFrame();
                f.setBounds(1,1,200,100);
                f.setVisible( true );
        }
}

Exemplu 2. Sa se scrie o aplicatie care se ruleaza intr-o fereastra si la apasarea mouseului in zona ferestrei sa
apara o eticheta care sa contina coordonatele apasarii mouseului.
 Diagrama de clasa:

 Sursa Java:
import java.awt.Frame;
import java.awt.Label;
public class MyFrame extends Frame
{
    private Label label;
    public MyFrame()
    {
        setLayout( new java.awt.FlowLayout() );
        label = new Label(" ");
        add( label );
        // Inregistrarea ferestrei ca si receptor pentru evenimentele cu mouse-ul
        addMouseListener( new java.awt.event.MouseAdapter(){
            public void mouseClicked( java.awt.event.MouseEvent e )
            {
                label.setText("("+Integer.toString(e.getX())
+","+Integer.toString(e.getY())+")");
            }
        });
        // Inregistrarea ferestrei ca si receptor pentru evenimnetele cu fereastra
        addWindowListener( new java.awt.event.WindowAdapter(){
            public void windowClosing( java.awt.event.WindowEvent e ){
                setVisible( false );
                System.exit( 0 );
            }
        });
    }
    public static void main(String[] args)
    {
        MyFrame f = new MyFrame();
        f.setBounds(1,1,300,300);
        f.setVisible( true );
    }
}
 
Exemplu 3. Sa se scrie o aplicatie care defineste o clasa Numarator care la schimbarea valorii numaratorul
genereaza un eveniment CounterEvent. Acest eveniment va fi receptat de o clasa Receptor care la aparitia
evenimentelor afiseaza valoarea numaratorului.
Pentru orice eveniment nou se creaza o clasa derivata din clasa EventObject, iar interfetele pentru receptionarea
evenimentelor trebuie sa extinda clasa EventListener din pachetul java.util. Clasa Counter este un numarator
care se ruleaza pe un fir de executie propriu si este capabil de gestionarea obiectelor de tip listener. Aceste
obiecte listener sunt stocate intr-un vector (java.util.Vector ) si la modificarea numaratorului sunt anuntati.
(notifyCounterChange).
Diagrama de clase:

Sursa Java:
 
class CounterEvent extends EventObject{
    private int count;
    CounterEvent ( Object source, int count ){
        super( source );
        this.count = count;
    }
    public int getCount(){
        return count;
    }
}
interface CounterChangeListener extends EventListener{
    void counterChange( CounterEvent e );
}
class Counter extends Thread{
    private int count = 0;
    private Vector listeners = new Vector();
    public Counter( string name, int count ){
        super( name );
        this.count = count;
    }
    public Counter( int count ){
         this.count = count;
    }
    public Counter(){
        this( 0 );
    }
    public void run(){
        while( true ){
            try{
                sleep( ( int ) Math.round( Math.random()*3000));
            }
            catch( InterruptedException e ){}
            count++;
            notifyCounterChange( count );
        }
    }
    public void startCounting(){
        this.start();
    }
    public notifyCounterChange( int count ){
        Vector tmpList;
        CounterEvent ce = new CounterEvent( this , count );
        synchronized( this ){
            tmpList = (Vector ) listeners.clone();
        }
        for( int i=0; i<tmpList.size(); i++){
                ((CounterChangeListener ) tmpList.elementAt(i)).counterChange( ce );
        }
    }
    public synchronized void addCounterChangeListener( CounterChangeListener ccl ){
        listeners.addElement( ccl );
    }
    public synchronized void removeCounterChangeListener( CounterChangeListener ccl ){
        listeners.removeElement( ccl );
    }
}
public class CounterControl implements CounterChangeListener 
{

        public CounterControl() 


        {
                Counter c = new Counter();
                c.addCounterChangeListener(this);
                c.startCounting();
        }

        public static void main(String[] args) 


        {
                CounterControl cc = new CounterControl();
        }

        public void counterChange(CounterEvent e) 


        {
                System.out.println("Counter value has changed: " + e.getCount());
        }
}

Facilitati I/E
1. Generalitati
2. I/E pe terminal
3. Filtre
4. Fisiere
o Implementarea comenzii cat din UNIX
o Copiere de fisiere
1. Conducte
1. Generalitati
In vederea tratarii unitare a interfetelor de comunicare intre entitatile unui sistem informatic( entitati
software+entitati hardware) se introduce conceptul de stream. Autorul acestui concept este Dennis Ritchie care
in 1984 implementeaza primul sistem I/O pe baza de stream  in cadrul s.o. Unix. Ideea de stream are la baza
creara unui canal de comunicatie intre doua entitati. Una dintre entitati este sursa, iar cealalta destinatia. Sursa
scrie informatii in canal, iar destinatia poata sa le citeasca aceste date.Canalul permite trecerea unui flux de date
intr-o singura directie.Datorita faptului ca exista doua directii de comunicare, exista doua tipuri mari de
streamuri pentru orice nod de comunicatie: input stream si output stream.  Tastatura ar fi un exemplu de
input stream, iar monitorul un output stream. Sursa si destinatia nu trebuie sa fie neaparat periferice, ele pot fi si
module soft. Un alt mare avantaj al streamurilor  este posibilitatea conectarii acestora, obtinand astfel un
mecanism pipe-line de comunicare interprocese.
Fluxurile de date sunt de doua tipuri fluxuri de octeti si fluxuri de caractere. Fluxurile de caractere au fost
introduse de versiunea 1.1. . Java API defineste o ierarhie intreaga de clase si interfete  pentru fluxuri.

InputStream/OutputStream
Sunt clase abstracte care definesc functionalitatile de baza pentru citirea respectiv scrierea unui bloc de octeti.
Toate celelalte tipuri de streamuri pentru octeti se deriva din aceste doua.
Reader/Writer
Sunt clase abstracte care definesc functionalitatile de baza pentru citirea respectiv scrierea unui bloc de
caractere. Toate celelalte tipuri de streamuri pentru caractere se deriva din aceste doua. Au fost introduse de
Java 1.1
InputStreamReader/InputStreamWriter
Sunt clase utilizate pentru conversia blocurilor de caractere la blocuri de octeti.
DataInputStream/DataOutputStream
Sunt clase de tip filtre care se pun peste clasele InputStream/OutputStream si permit citirea/scrierea a diferitelor
tipuri de date (logice, intregi, reale, siruri de caractere etc. ).
ObjectInputStream/ObjectOutputStream
Se utilizeaza pentru scrierea obiectelor serializabile respectiv reconstructia lor dupa citire.
BufferedInputStream/BufferedOutputStream/BufferedReader/BufferedWriter
Clase care realizeaza operatiile de citire scriere cu zone tampon pentru a mari eficienta acestor operatii.
PrintWriter
Un stream special de caractere care permite scrierea textelor. System.out este de acest tip.
PipedInputStream/PipedOutputStream/PipedReader/PipedWriter
Clase pentru realizarea mecanismului conducta (pipe).
FileInputStream/FileOutputStream/FileReader/FileWriter
Clase care ofera facilitati pentru accesul fisierelor din sistemul local de fisiere.
2. I/E pe terminal
Asemanator cu C standard si Java ne ofera facilitati pentru a lucra cu intrarea/iesirea respectiv eroarea
standard.  Clasa Systemdin pachetul java.lang contine trei referinte statice de urmatoarele tipuri:
InputStream    in
PrintWriter    out
PrintWriter    err
Aceste trei streamuri nu trebuie deschise, ele se deschid automat inaintea executiei aplicatiei. Celelalte
streamuri se deschid in momentul crearii lor prin apelul constructorului clasei corespunzatoare. Streamurile se
inchid cu apelul metodei close(). Streamurile in, out si err nu trebuie inchise.
Citirea si scrierea din si in aceste streamuri se realizeaza cu ajutorul metodelor read() si write() care au mai
multe forme supraincarcate. In cazul streamurilor de octeti metoda read() are urmatoarele trei forme:
int read() throws IOException;
int read( byte b[])throws IOException;
int read( byte b[], int offset, int length ) throws IOException;
Prima forma citeste un octet dar returneaza rezultatul sub forma unui intreg. In caz de eroare se genereaza
exceptia IOException, deci orice apel de metoda read obligatoriu trebuie inclus intr-un context de tratarea
exceptiilor. La sfarsitul fluxului read() returneaza -1.
try{
    int val = System.in.read();
    ...
}
catch (IOException e ){
    // Tratarea exceptiei
}
Prin a doua si a treia forma se pot citi deodata un bloc intreg de octeti. Octetii cititi vor fi stocati intr-un tablou
de octeti primit ca parametru. Aceste doua metode returneaza numarul octetilor efectiv cititi.
byte b[1024];
try{
    int bytesread=System.in.read( b );
}
catch( IOException e ){...}

Clasa InputStream defineste si o metoda available() care returneaza   numarul octetiilor disponibili din flux.
try{
    int disponibili = System.in.available();
    if( disponibili > 0){
        byte b[] = new byte[ disponibili ];
        System.in.read( b );
    }
}
catch( IOException e ){ ... }
Exemplul urmator copiaza intrarea standard la iesirea standard.
import java.io.*;
public class Copiere{
    public static void copiere ( InputStream sin, OutputStream sout) throws IOException{
      int b;
      while(( b =sin.read()) != -1) sout.write(b);
      sout.flush();
     }
    public static void main( String args[]){
      try{
       copiere(System.in, System.out );
      }
      catch( IOException e ){
          System.out.println("Eroare de copiere");
      }
   }
}
Functia skip( int ) se utilizeaza pentru a muta pozitia citirii peste un numar de octeti  specificat prin
parametru. Toate metodele de citire blocheaza firul de executie care doreste sa faca operatiile I/O pana cand
toate datele sunt disponibile, s-a ajuns la sfarsitul fluxului de date sau a aparut o exceptie.
Metodele mark() si reset()  se folosesc impreuna oferind posibilitatea reluarii citirii din stream. Prin metoda
mark( int readLimit ) se memoreaza pozitia curenta in stream si se creaza o zona tampon de dimensiunea
specificata prin readLimit. Prin reset() se repozitioneaza indicatorul in stream la pozitia marcata prin
mark(). Aceste doua metode pot fi folosite doar in cazul in care streamul suporta marcare. Verificarea acestui
lucru se poate face prin markSupported().
3. Filtre
Exista clase in  pachetul java.io care nu sunt utilizate pentru operatiile efective de citire si scriere, ci  ne ofera o
serie de metode pentru a scrie si citi diferite tipuri de date. Aceste clase alcatuiesc un nivel superior si
intotdeauna se combina cu cate o clasa care creaza canalul  de comunicatie . Clasele DataInputStream respectiv
DataOutputStream sunt clase care definesc o serie de astfel de metode. De exemplu clasa DataInputStream
contine si urmatoarele metode:
short readShort();
int readInt();
long  readLong();
float readFloat();
double readDouble();

4. Fisiere
Clasele FileInputStream, FileOutputStream, FileReader, FileWriter
La capetele unui canal de comunicatie putem sa avem si fisiere. In acest caz operatiile I/E vor afecta fisierele.
Daca dorim sa citim din fisiere atunci in functie de tipul fisierului vom utiliza clasele  FileInputStream respectiv
FileReader (fisier binar sau fisier text). Pentru scriere se vor utiliza clasele  FileOutputStream respectiv
FileWriter. Pentru a deschide un fisier pentru scriere sau citire se creaza un nou obiect din clasele amintite mai
in sus. La crearea obiectului se va apela unul din constructorii clasei corespunzatoare. Acest constructor poate
sa primeasca ca parametru numele fisierului sau un obiect de tip File sau unul de tip FileDescriptor.
Exemplul urmator afiseaza continutul  unui fisier text pe terminal. utilizand un obiect de tip FileReader.
Numele fisiereului de listat se primeste de la linia de comanda.
import java.io.*;
public class Cat{
 public static void main( String args[]){
     FileReader fr = null;
     int b = 0;
     if( args.length !=1 ){
          System.out.println("Utilizare: java Cat <nume_fisier_text>");
          System.exit(1);
     }
     try{
         fr = new FileReader( args[0] );
         while( ( b =fr.read()) != -1 )
              System.out.print( (char) b );
     }
     catch( FileNotFoundException e ){
       System.out.println("Fisier inexistent");
       System.exit(2);
     }
     catch( IOException e ){
       System.out.println("Eroare de citire");
       System.exit(3);
     }
 }
}
Daca dorim sa citim linii intregi din fisier in loc de caractere va trebui sa utilizam clasa BufferedReader care
defineste metoda readLine(). Constructorul clasei BufferedReader primeste ca parametru un obiect de tip
FileReader, care in prealabil trebuie creat.
        try{
          fr = new FileReader( args[0] );
          BufferedReader br = new BufferedReader( fr );
          String line;
          while( ( line =br.readLine()) != null )
              System.out.println( line );
     }
     catch( FileNotFoundException e ){
           System.out.println("Fisier inexistent");
           System.exit(2);
     }
     catch( IOException e ){
          System.out.println("Eroare de ccitire");
          System.exit(3);
     }
 }
}
Generalizare:
Modificam exemplul precedent a.i. daca nu primeste nici un argument de la linia de comanda atunci copieaza
intrarea standard la iesirea standard, iar daca primeste mai multe argumente, le afiseaza pe toate.
import java.io.*;
public class cat{
         String files[];
         // Constructor pentru cazul cand programului se transmit
         // argumente de la linia de comanda
        public cat( String files[] ){
                int i;
                this.files = files;
                for(i=0; i<files.length; i++)
                {
                   try
                   {
                        BufferedReader br = new BufferedReader( new FileReader(files[ i ]
));
                        print( br );
                        br.close();
                   }
                   catch( IOException e )
                   {
                        System.out.println("Error opening file: "+files[ i ] );
                   }
                }
         }
         // Constructor pentru cazul cand nu primeste nici un argument
       public cat(){
                try{
                        BufferedReader br = new BufferedReader(
                            new InputStreamReader( System.in ) );
                        print( br );
                        br.close();
                }
                catch( IOException e )
                {
                        System.out.println("Error " );
                }
         }
 
 
         public void print( BufferedReader br ) throws IOException {
                        String line;
                        while( ( line = br.readLine() ) != null )
                                System.out.println( line );
         }
       public static void main( String args[] ){
                if( args.length == 0 ){
                        System.out.println(" Copiing standard imput to standard output");
                        cat c = new cat();
                }
                else
                {
                        System.out.println(" Copiing files to standard output ");
                        cat c = new cat( args );
                }
         }
}
Forme de utilizare:
1. java cat
2. java cat fisier1 fisier2 ...
Copiere de fisiere:
 
import java.io.*;
public class CopiereFisiere{
 static FileInputStream  fis;
 static FileOutputStream fos;
 public static void copiere ( InputStream sin, OutputStream sout) throws IOException{
  byte b[]= new byte[1024];
  int bytesRead;
  while(( bytesRead =sin.read(b)) >0) sout.write(b);
  sout.flush();
 }
public static void main( String args[]){
  if( args.length !=2 ){
    System.out.println("Utilizare java CopiereFisiere sursa destinatie");
    System.exit(1);
  }
  try{
      fis = new FileInputStream( args[0]);
      fos = new FileOutputStream( args[1] );
      copiere(fis, fos );
  }
  catch( FileNotFoundException e ){
      System.out.println("Fisier inexistent "+args[0]);
      System.exit(1);
  }
  catch( IOException e ){
   System.out.println("Eroare de copiere");
  }
 }
}
 
Clasa File

Clasa File contine informatii despre fisiere si directoare si defineste o serie de metode pentru a obtine respectiv
a modifica aceste informatii.. Principalele metode  sunt urmatoarele:
 
Metoda Tipul returnat Descriere
canRead() boolean Are permisiunea de citire?
canWrite() boolean Are permisiunea de scriere?
delete() boolean Stergere
exists() boolean Exista fisierul?
getAbsolutePath() String Calea completa spre fisier
getCanonicalPath() String Calea completa spre fisier
getName() String Numele fisierului
getParent() String Numele catalogului parinte
getPath() String Calea spre fisier
isAbsolute() boolean Este un nume absolut?
isDirectory() boolean Este un director?
isFile() boolean Este un fisier ordinar?
lastModified() long Data ultimei modificari
length() long Lungimea fisierului
list() String[] Lista fisierelor din director
mkdir() booolean Crearea de director
mkdirs() boolean Crearea tuturor directoarelor
Programul urmator primeste de la linia de comanda un nume. Daca numele reprezinta numele unui director
atunci se listeaza continutul acestuia, altfel daca este fisier, atunci se afiseaza fisierul pe terminal..
import java.io.*;
public class Listare {
    public static void main( String args[] )
    {
         File file;
         if( args.length != 1 ){
             System.out.println("Utilizare: java Listare <nume> ");
             return;
         }
         try{
         file = new File( args[0]);
     }
     catch( Exception e ){
         System.out.println(" Fisier inexistent ");
         return;
     }
     if( ! file.exists() || !file.canRead() ){
         System.out.println("Eroare de citire fisier: "+ args[0]);
         return;
     }
     if( file.isDirectory() ){
         String files[] = file.list();
         for( int i =0; i<files.length; i++)
          System.out.println( files[i] );
     }
     else{
       try{
          FileReader fr = new FileReader( file );
          BufferedReader in = new BufferedReader( fr );
          String line;
          while( (line = in.readLine()) != null )
              System.out.println(line);
         }
       catch( FileNotFoundException e ){
          System.out.println("eroare la deschiderea fisierului");
       }
       catch( IOException e ){
          System.out.println("eroare de citire");
       }
 }
}
}
Clasa RandomAccessFile
Aceasta clasa implementeaza atat interfata DataInput cat si interfata DataOutput astfel permitand citirea si
scrierea datelor primitive Java cat si a sirurilor de caractere. Clasa permite accesul direct al fisierului. Clasele cu
care am lucrat pana acum au permis doar accesul secvential. Crearea unui obiect de tip RandomAccessFile se
face in modul urmator:
try{
    RandomaccessFile file = new randomAccessFile("unfisier","rw");
    ...
}
catch( IOException e){
}
Primul parametru al constructorului poate fi un sir de caractere care reprezinta calea si numele fisierului sau  un
obiect de tip File, iar al doilea parametru reprezinta modul de access ( r-citire, w-scriere). Si aceasta clasa
defineste metode de citire si scriere, dar pe langa aceste metode contine si una care permite accesul direct: seek(
long pozitie).
Exemplu:
Se creaza un fisier cu valori de tip double generate aleator si se afiseaza fisierul pe terminal. Dupa aceea se
modifica continutul aleator, modificand doar cateva elemente si se afiseaza inca o data fisierul pe terminal.
import java.io.*;
import java.util.*;
public class FisierAccesDirect{
        RandomAccessFile f;
        String  name;
        long    dim;
        public FisierAccesDirect( String name, long dim ) throws IOException
        {
                this.name = name;
                this.dim  = dim;
                f = new RandomAccessFile( name, "rw");
                Random r = new Random( 10000 );
                for( int i=0; i< dim; i++ )
                {
                        double d = r.nextDouble();
                        f.writeDouble( d );
                        System.out.print( d+"\t");
                }
                f.close();
                f = new RandomAccessFile( name, "rw");
        }
 
        public double getDouble( long poz ) throws IOException
        {
                f.seek( poz * 8 );
                return( f.readDouble());
        }
        public void putDouble( long poz, double d ) throws IOException
        {
                f.seek( poz * 8);
                f.writeDouble( d );
        }
        public long getDim()
        {
                return dim;
        }
 
 
        public static void main( String args[] )
        {
                try
                {
                        FisierAccesDirect c = new FisierAccesDirect("doublefile",10);
                        Random r = new Random();
                        long n = c.getDim();
                        long i;
                        System.out.println("Dimension : "+n);
                        for( i=0; i<5; i++ ){
                                long poz = r.nextLong();
                                if (poz<0) poz = -poz;
                                poz = poz % n;
                                System.out.println("Poz: "+poz);
                                c.putDouble( poz, 1.0 );
                        }
                        System.out.println("Fisierul modificat: ");
                        for( i=0; i< n; i++ )
                        {
                                System.out.print(c.getDouble(i)+"\t");
                        }
 
                }
                catch( IOException e ){
                        System.out.println("Eroare IO ");
                }
        }
  }
 
5. Conducte
In mod normal aplicatia utilizeaza un stream de scriere sau una de citire la un moment dat. Clasele
PipedInputStream si PipedOutputStream (respectiv PipedReader si PipedWriter) ne ajuta la crearea
mecanismului de conducta care se poate utiliza de catre o aplicatie care se ruleaza pe mai multe fire de executie.
Firele de executie comunica intre ele cu ajutorul acestui mecanism. Exemplul urmator va fi o aplicatie care se
ruleaza pe doua fire de executie. Primul fir produce mesaje pe care le scrie intr-o conducta, in cazul nostru
mesajul va fi format din data si ora curenta a producerii mesajului, iar celalalt fir de executie consuma mesajele,
afisandu-le pe terminal. Pentru a crea o conducta trebuie sa utilizam doua obiecte, unul pentru capatul de scriere
al conductei, iar celalalt pentru capatul de citire al conductei.  Crearea se face in modul urmator:
PipedInputStream pin = new PipedInputStream();
PipedOutputStream pout = new PipedOutputStream( pin );
sau invers
PipedOutputStream pout = new PipedOutputStream();
PipedInputStream pin = new PipedInputStream( pout );
Exemplu pentru utilizarea conductelor:
Diagrama de colaborare:
Diagrama de clase:

Sursa Java:
//Source file: Control.java
 
public class Control
{
   public Control()
   {
   }
   public static void main(String[] args)
   {
    ThreadA ta = new ThreadA("Pipe Writer Thread");
    ThreadB tb = new ThreadB("Pipe Reader Thread", ta.piperead );
    ta.start();
    tb.start();
   }
}
//Source file: ThreadA.java
import java.io.PipedWriter;
import java.io.PipedReader;
public class ThreadA extends Thread
{
   public PipedWriter pipewrite;
   public PipedReader piperead;
 
   public ThreadA(String name)
   {
      super( name );
      try{
        pipewrite = new PipedWriter();
        piperead  = new PipedReader( pipewrite );
      }
      catch( java.io.IOException e ) {}
   }
   public void run()
   {
      java.io.PrintWriter ptw = new java.io.PrintWriter( pipewrite );
      while( true ){
                 String s = ( new java.util.Date().toString());
                 ptw.println( s );
                 System.out.println(getName()+" "+s+": has been written in pipe");
                 try{
                     sleep(1000);
                 }
                 catch( InterruptedException e ){}
      }
   }
}
//Source file: ThreadB.java
import java.io.PipedReader;
public class ThreadB extends Thread
{
   public PipedReader piperead;
   public ThreadB(String name, PipedReader pr)
   {
    super( name );
    piperead = pr;
   }
   public void run()
   {
        String s=null;
        java.io.BufferedReader br = new java.io.BufferedReader ( piperead );
        while( true ){
            try{
                 s = br.readLine();
            }
            catch( java.io.IOException e ){
                 System.out.println("Error reading from pipe");
            }
            System.out.println(getName()+": "+s+" read from pipe");
            try{
                 sleep(1000);
            }
            catch( InterruptedException e ){}
        }
   }
}

Aplicatii de retea. Socluri. URL.


1. Protocoale si porturi. Modelul client server.
2. Modelul de comunicatie orientat pe conexiune. Clasele  Socket si ServerSocket
3. Modelul de comunicatie neorientat pe conexiune. Clasele DatagaramSocket si  DatagramPacket
4. URL
Clasele din pachetul java.net se impart in doua categorii, clase pentru socluri si clase care lucreaza cu URL
(Uniform Resource Locators). Soclurile Java faciliteaza accesul la protocoalele standard utilizate in
comunicarea intre calculatoarele gazda de pe Interent.
 
1. Protocoale si porturi. Modelul client server.
Protocoale
Inainte sa intram in detaliile posibilitatilor oferite de limbajul Java pentru scrierea aplicatiilor de retea trebuie sa
cunoastem niste lucruri despre reteaua Internet. Internetul functioneaza pe un sistem de protocoale numit
TCP/IP (Transport Control Protocol/Internet protocol)

Aceste protocoale stabilesc regulile cu ajutorul carora doua calculatoare comunica intre ele. Protocoalele sunt
standarde de care se ocupa IETF ( Internet Engineering Task Force). Java implementeaza protocoalele de nivel
superior al stivei de protocoale TCP/IP. Astfel faciliteaza utilizarea protocoalelor HTTP (HyperText Transfer
Protocol) si FTP( File Transfer Protocol). Astfel programatorul va utiliza niste clase si interfete predefinite, fara
a cunoaste detaliile de implementare a acestora. Nu trebuie sa cunoastem structurile de date utilizate de acest
sistem de protocoale, nici metodele utilizate pentru transmiterea si receptionarea secventelor de octeti.
Modelul client server
Serverul este o aplicatie care ofera servicii clientilor sositi prin retea. Serverele ofera o gama variata de servicii. 
Serverul cel mai cunoscut este serverul Web, care furnizeaza documentele cerute de catre clienti. Un alt
serviciu cunoscut este posta electronica, care utilizeaza protocoalele SMPT (Simple Mail Transfer Protocol) si
IMAP4 (Internet Mail Access Protocol). Pe principiul client-server funnctioneaza si protocoalele NFS
(Network File Service) si  FTP sau serviciul de nume de domenii  DNS (Domain Name Service) si serviciul de
informatii despre retea NIS (Network Information Services). Trebuie sa amintim si serviciul care permite
logarea la un calculator aflat la distanta: TELNET si RLOGIN. Putem trage concluzia ca arhitectura client-
server este instrumentul de baza in dezvoltarea aplicatiilor de retea.
Clientul  este o aplicatie care utilizeaza serviciile oferite de catre un server. Pentru a putea realiza acest lucru,
clientul trebuie sa cunoasca unde se afla serverul in retea si cum trebuie comunicat cu acesta si ce servicii ofera.
Deci daca un client doreste o comunicare cu serverul, trebuie sa cunosca trei lucruri:
 adresa server
 portul server utilizat pentru comunicare
 protocolul de comunicatie utilizat de server
Daca aceste date ne stau la dispozitie, putem realiza comunicatia cu serverul.
Porturi si socluri
Porturile si soclurile reprezinta mecanismul prin care se realizeaza legatura cu un server. Porturile reprezinta o
poarta la care sunt primiti clientii pentru un anumit serviciu. De exemplu la aceeasi adresa se pot oferi diferite
servicii, acestea oferindu-se la porturi diferite. Acelasi calculator (cu o singura adresa IP) poate sa ne ofere
oricate servicii doreste. Clientii care apeleaza la serviciile acestui calculator vor utiliza aceeasi adresa,
indiferent la care serviciu apeleaza, si toti clientii care doresc utilizarea unui anumit serviciu vor utiliza acelasi
port. Un numar de port este un numar inreg din intervalul 1-9999. IETF defineste  serviciile oferite pe porturile
1-1024. De obicei serviciile HTTP sunt oferite la portul 80, cele de FTP la portul 21, Telnet la 23 etc.
Un  soclu este de fapt un nod abstract de comunicatie. Soclurile reprezinta o interfata de nivel scazut pentru
comunicarea in retea. Soclurile permit comunicarea intre procese aflate pe acelasi calculator sau pe calculatoare
diferite din retea.  Mecanismul de socluri a fost definit prima data in BSD UNIX. Java suporta trei tipuri de
socluri. Clasa Socket  utilizeaza un protocol orientat pe conexiune (TCP), clasa DatagramSocket utilizeaza
protocolul UDP la nivelul transport, care este un protocol neorientat pe conexiune. O alta varianta a
DatagramSocket este MulticastSocket utilizat pentru a trimite date deodata la mai multi receptori. Soclurile
utilizeaza fluxuri de date (streamuri) pentru a trimite si a receptiona mesaje.
2. Modelul de comunicatie orientat pe conexiune.
Clasele  Socket si ServerSocket
Acest model se bazeaza pe protocolul TCP. Intr-o aplicatie retea intotdeauna avem doua parti: o parte client
care initializeaza conversatia si trimite cereri, si o parte server care primeste cererile si raspunde la acestea.
Clientul intotdeauna creaza un soclu pentru a initia conversatia si trebuie sa cunoasca serverul caruia adreseaza
cererea,  iar serverul trebuie sa fie pregatit pentru a receptiona aceste cereri. In momentul receptionarii
mesajului creaza un soclu pe partea serverului, soclu care va facilita deservirea clientului. Atat pe partea de
client cat si pe  partea de server se utilizeaza cate un obiect de tip Socket pentru comunicare. Pe partea de server
mai trebuie sa cream un obiect de tip ServerSocket, care are sarcina primirii conexiunilor si acceptarea acestora.

Clientul trebuie sa cunoasca doua lucruri despre server: numele serverului (utilizat pentru determinarea adresei
IP al serverului) si numarul portului la care acesta asculta cererile clientilor. Acelasi calculator gazda poate
oferi mai multe servicii, deci poate gazdui mai multe procese de tip server. De exemplu poate fi server Mail,
server FTP, server HTTP, dar aceste aplicatii lucreaza cu diferite porturi, deci cererile adresate acestor serveri
vor fi receptionate pe diferite porturi.
Privind modalitatea de deservire a clientilor serverul prin constructie poate fi server paralel (concurent) si
server secvential.
Serverul concurent permite deservirea in paralel a mai multor clienti. Aceasta paralelitate se poate realiza prin
crearea a mai multor procese fiu, cate unul pentru fiecare client sau prin crearea de fire de executie pentru
deservirea fiecarui clienti In Java noi vom utiliza cea de a doua modalitate. De obicei utilizam server cu
arhitectura paralela pentru servicii la care deservirea unui client dureaza in timp.
In general o aplicatie server trebuie sa execute urmatorii pasi:
1. Serverul aloca un port de comunicare care va fi cunoscut de
catre clienti
2. Cat timp ( mai sunt clienti la port )
    {
        stabilire conexiune cu client ( creare soclu )
        deservire client ( flux de intrare, flux de iesire )
        eliberare conexiune ( eliberare soclu )
    }
O aplicatie client executa urmatoarele:
1. Aloca un port de comunicare
2. Se conecteaza la server la portul cunoscut de dinainte
3. Se stabileste o conexiune prin care se trimit si se citesc date
(soclu + fluxuri)
Clasa Socket
Constructori:
          Socket(InetAddress address, int port)
                    Creaza un soclu si care se conecteaza la adresa IP specificat prin obiectul InetAddress si portul
specificat prin parametrul port.
         Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
Creaza un soclu si care se conecteaza la adresa IP specificat prin obiectul InetAddress si portul specificat prin
parametrul port. Ultimii doi parametri reprezinta adresa clientului si portul pe care acesta comunica .
          Socket(String host, int port)
                     Creaza un soclu si care se conecteaza la calculatorul host pe portul specificat prin parametrul port.
          Socket(String host, int port, InetAddress localAddr, int
localPort)
                     Creaza un soclu si care se conecteaza la calculatorul host pe portul specificat prin parametrul port.
Ultimii doi parametri reprezinta adresa clientului si portul pe care acesta comunica
Metode principale:
 
void close()
Inchide soclul.
InetAddress getInetAddress()
Returneaza adresa la care soclul este conectat
InputStream getInputStream()
Returneaza un stream de intrare pentru soclu.
InetAddress getLocalAddress()
Returneaza adresa pe care soclul este creat.
int getLocalPort()
Returneaza numarul portului local.
OutputStream getOutputStream()
Returneza un stream de iesire pentru soclu.
int getPort()
Returneaza portul la care soclul este conectat.
Clasa ServerSocket
Constructori:
ServerSocket(int port)
           Creaza un soclu server pe portul specificat.
ServerSocket(int port, int backlog)
     Creaza un soclu server pe portul specificat. Al doilea parametru indica lungimea cozii de asteptare.
Metode principale:
 Socket accept()
                       Asculta conexiunile si le accepta.
 void close()
                    Inchide soclul.
 InetAddress getInetAddress()
                       Returneaza adresa locala al soclului server.
int getLocalPort()
                       Returneaza portul la care serverul asteapta conexiunile.
 
Exemplu 1.
Urmatoarea aplicatie este formata dintr-o parte client si una server. Clientul va trimite un sir de caractere
serverului, iar serverul il va trimite inapoi convertit in majuscule.
Pentru rezolvarea acestei probleme construim doua pachete:

Incepen cu descrierea serverului. Serverul are o structura paralela, permite deservirea a mai multor clienti.
Pentru fiecare client se va crea un obiect de tip ClientHanedle, un obiect care se ruleaza pe un fir de executie
separat si realizeaza deservirea clientului.

//Source file: c:\Rose\ClientServer\ServerPackage\Server.java


package ServerPackage;
 
public class Server
{
   public static void main(String[] args)
   {
    int port;
    if( args.length <1 )
    {
      System.out.println( "Usage Server <portnumber>");
      System.exit( 1 );
    }
    try{
      port = Integer.parseInt( args[ 0 ] );
    }
    catch( NumberFormatException e )
    {
          port = 1024;
    }
    try{
      java.net.ServerSocket ss = new java.net.ServerSocket( port );
      while( true )
       ( new ClientHandle( ss.accept() )).start();
    }
    catch( java.io.IOException e )
    {
      System.out.println( "Server error ");
    }
   }
}
//Source file: c:\Rose\ClientServer\ServerPackage\ClientHandle.java
package ServerPackage;
import java.net.Socket;
import java.net.SocketException;
public class ClientHandle extends Thread
{
   Socket client;
   public ClientHandle(Socket client) throws SocketException
   {
     this.client = client;
   }
   public void run()
   {
    try{
      java.io.InputStream  in  = client.getInputStream();
      java.io.OutputStream out = client.getOutputStream();
      java.io.BufferedReader myin  = new java.io.BufferedReader(
                                        new java.io.InputStreamReader( in));
      java.io.PrintWriter    myout = new java.io.PrintWriter( new
                                       java.io.OutputStreamWriter( out ));
      String request = myin.readLine();
      System.out.println( "Request: "+request);
      myout.println( request.toUpperCase());
      myout.flush();
      myin.close();
      myout.close();
      client.close();
   }
   catch( java.io.IOException e )
   {
      System.out.println("I/O error "+e );
   }
  }
}
Partea de client:

 
//Source file: c:\Rose\ClientServer\ClientPackage\Client.java
package ClientPackage;
public class Client
{
   private String hostname;
   private int hostport;
   private String message;
   public Client(String hostname, int hostport, String message)
   {
     this.hostname = hostname;
     this.hostport = hostport;
     this.message = message;
     doIt();
   }
   public void doIt()
   {
    try{
      java.net.Socket s = new java.net.Socket( hostname, hostport );
      java.io.BufferedReader  in = new java.io.BufferedReader( new
                        java.io.InputStreamReader(s.getInputStream()));
      java.io.PrintWriter    out = new java.io.PrintWriter( s.getOutputStream() );
      out.println( message ); out.flush();
      System.out.println("Answer from the server: "+in.readLine() );
      s.close();
    }
    catch( java.io.IOException  e )
    {
      System.out.println( "I/O error "+e );
    }
   }
 
   public static void main(String[] args)
   {
    String hostname, message;
    int hostport;
    if( args.length < 3 )
    {
      System.out.println("Usage Client <hostname> <hostport> <message>");
      System.exit( 1 );
    }
    hostname = args[ 0 ];
    message  = args[ 2 ];
    try{
       hostport =Integer.parseInt( args[ 1 ] );
    }
    catch( NumberFormatException e )
    {
      hostport = 1024;
    }
    Client c = new Client( hostname, hostport, message );
   }
}
Exemplu 2
Urmatoarea aplicatie va fi un server web. Aplicatia se va rula pe un fir de executie cu prioritatea mai mica decat
cea normala si va rezolva cererile HTTP. Vom proiecta aplicatia a.i. sa aiba o arhitectura paralela si sa rezolve
cererile de tip GET nume_fisier.Arhitectura paralela permite deservirea concomitenta a mai multor
clienti.
import java.io.*;
import java.net.*;
import java.util.*;
public class TinyHttpd{
    public static void main ( String args[] ) throws IOException
    {
         ServerSocket ss = new ServerSocket( Integer.parseInt( args[0]));
         while( true )
         {
             Socket s = ss.accept();
             TinyHttpdConnection t = new TinyHttpdConnection( s );
             t.start();
         }
    }
}
 
class TinyHttpdConnection extends Thread{
    Socket client;
    TinyHttpdConnection ( Socket client) throws SocketException{
         this.client = client;
         setPriority(NORM_PRIORITY -1);
    }
    public void run(){
         try{
             BufferedReader in = new BufferedReader(
                new InputStreamReader(client.getInputStream(),"8859_1"));
             OutputStream out = client.getOutputStream();
             PrintWriter pout = new PrintWriter(
                new OutputStreamWriter(out,"8859_1"),true);
             String request = in.readLine();
             System.out.println("Request: "+request);
             StringTokenizer st = new StringTokenizer( request );
             if( (st.countTokens()>=2) && st.nextToken().equals("GET"))
             {
                  if( (request = st.nextToken()).startsWith("/"))
                      request=request.substring( 1 );
                  if( request.endsWith("/") || request.equals(""))
                      request=request+"index.html";
                  try{
                      FileInputStream fis = new FileInputStream( request );
                      byte[] data = new byte[ fis.available()];
                      fis.read(data);
                      out.write(data);
                      out.flush();
                  }
                  catch( FileNotFoundException e ){
                      pout.println(e.toString());
                  }
            }
            else{
                  pout.println("400 Bad request");
            }
            client.close();
         }
         catch( IOException e )
         {
             System.out.println(e.toString());
         }
   }
}
Utilizarea aplicatiei:
Aplicatia de mai sus se utilizeaza in modul urmator. Prima data se compileaza si dupa aceea se executa cu
comanda:
java TinyHttpd 1234.
Numarul 1234 reprezinta portul la care serverul asteapta cererile. Dupa ce serverul este lansat in executie, dintr-
un program de navigare (browser) se formuleaza o cerere de forma: http://127.0.0.1:1234/Index.html,
daca exista in catalogul aplicatiei TyniHttpd fisierul Index.html si daca clientul si serverul se ruleaza pe acelasi
calculator. 127.0.0.1 este adresa IP al localhost-ului. Daca aplicatia se ruleaza pe un calculator cu numele
unu.doi.com atunci in locul  adresei 127.0.0.1 se pune unu.doi.com.
Descrierea aplicatiei:
Aplicatia defineste doua clase. O clasa care rezolva o cerere pe un fir de executie, TinyHttpdConnection si o
clasa TinyHttpd care creaza soclul server si intra in asteptarea clientilor. In momentul aparitiei unui client (
accept()) se creaza un nou fir de executie caruia se transmite referinta la soclul returnat de catre accept().
Aceasta referinta la soclu permite firului de executie sa extraga de aici fluxurile de intrare si de iesire prin care
se realizeaza comunicarea efectiva. Firul de executie extrage din flux cu ajurorul unor operatii pe siruri de
caractere, numele fisierului cerut. Daca exista fisierul, acesta se deschide pentru citire si datele citite vor fi
scrise in soclu pentru a  putea fi extrase si de catre client.
3. Modelul de comunicatie neorientat pe conexiune. Clasele DatagaramSocket
si  DatagramPacket
Trimiterea unei datagrame este similara cu trimiterea unei scrisori prin serviciul postal. In cazul trimiterii unei
scrisori  avem nevoie de un plic pe care scriem adresa destinatarului si dupa aceea punem scrisoarea in plic si o
aruncam intr-o cutie postala. Analog la trimiterea unei datagrame trebuie sa cunoasten adresa si portul
calculatorului caruia ii este adresata datagrama, dupa care putem sa punem datele in datagrama si sa le
trimitem. Datagramele utilizeaza la nivelul transportului protocolul UDP. Acest protocol este unul nesigur,
neorientat pe conexiune. Nu se face confirmare in cazul receptionarii acestor datagrame. Nici calea urmata de
aceste datagrame nu se cunoaste de dinainte. De aceea daca trimitem la acelasi destinatar mai multe datagrame,
unul dupa altul, nu putem fi siguri nici in ordinea primirii acestor datagrame. Din cauza ca protocolul nu
necesita confirmarea sosirii datagramelor este un protocol rapid si se utilizeaza in cazul serviciilor unde nu este
nici o nenorocire daca se pierde un pachet-doua (DNS utilizeaza UDP).
 
Descrierea claselor principale:
Clasa DatagramPacket:
Constructori:
DatagramPacket(byte[] buf, int length)
    Construieste un obiect de tip DatagramPacket pentru primirea unui pachet de lungime length
DatagramPacket(byte[] buf, int length, InetAddress address, int
port)
     Construieste un obiect de tip DatagramPacket pentru trimiterea  unui pachet de lungime length pe un port
specificat, la un host specificat
DatagramPacket(byte[] buf, int offset, int length)
    Construieste un obiect de tip DatagramPacket pentru primirea unui pachet de lungime length, specificand un
offset in buffer
DatagramPacket(byte[] buf, int offset, int length, InetAddress
address, int port)
    Construieste un obiect de tip DatagramPacket pentru trimiterea  unui pachet de lungime length pe un port
specificat, la un host specificat, specificand si un offset
Metode:
InetAddress getAddress()
Returneaza adresa IP al calculatorului de la care se primiste sau la care se trimite datagrama
byte[] getData()
Returneaza sirul de octeti care se trimite sau care se primeste
int getLength()
Returneaza lungimea datelor care se trimit sau care se primesc
int getOffset()
Returneaza offsetul datelor care se trimit sau care se primesc
int getPort()
Returneaza numarul portului calculatorului la distanta la care se trimit datele sau de la care se primesc datele
void setAddress(InetAddress iaddr)
                            Seteaza adresa.
void setData(byte[] buf)
                       Seteaza bufferul pentru pachet
void setData(byte[] buf, int offset, int length)
                       Seteaza bufferyl pentru pachet
void setLength(int length)
                       Seteaza lungimea pachetului.
void setPort(int iport)
                    Seteaza portul..
Clasa DatagramSocket
Constructori:
 
DatagramSocket()
Construieste un soclu si leaga la un port liber pe calculatorul local.
DatagramSocket(int port)
           Construieste un soclu si leaga la un portul specificat pe calculatorul local.
DatagramSocket(int port, InetAddress laddr)
Construieste un soclu  si leaga la adresa si portul specificat

Metode:
 
void close()
                       Inchide soclul.
void connect(InetAddress address, int port)
                       Conecteaza soclul la o adresa si port la distanta.
void disconnect()
                       Deconecteaza soclul.
InetAddrress getInetAddress()
                       Returneaza adresa la distanta la care soclul este conectat.
InetAddress getLocalAddress()
                       Returneaza adresa locala la care soclul este conectat.
int getLocalPort()
                       Returneaza portul local la care soclul este legat.
int getPort()
                        Returneaza portul la distanta la care soclul este legat.
void receive(DatagramPacket p)
                       Primire de datagrama prin acest soclu.
void send(DatagramPacket p)
                       Trimitere de datagrama prin acest soclu
.
Exemplu:
Exemplul urmator va fi format  pe partea clientului dintr-un aplet, care instiinteaza aplicatia server ori de cate
ori este pornit. Apletul se include intotdeauna intr-o pagina HTML si programul navigator este cel care incarca
si ruleaza apletul. Astfel daca pagina HTML reprezinta un homepage vom putea contoriza numarul vizitatorilor
la pagina respectiva.
Aplicatia este formata dintr-o parte client si una server. Clientul este un aplet, iar serverul  o aplicatie.

Partea de client:

//Source file: c:\Rose\curs9\exemplu2\ClientPackage\MessageApplet.java


package ClientPackage;
import java.applet.Applet;
public class MessageApplet extends Applet
{
   private String myHost;
   private int myPort;
   public void init()
   {
    myHost = getCodeBase().getHost();
    myPort = Integer.parseInt( getParameter("PORT"));
   }
   public void sendMessage(String message)
   {
    try{
     byte data[] =message.getBytes();
        java.net.InetAddress addr = java.net.InetAddress.getByName( myHost );
        java.net.DatagramPacket packet = new java.net.DatagramPacket(
   data, data.length, addr, myPort );
        java.net.DatagramSocket ds = new java.net.DatagramSocket();
        ds.send( packet );
        ds.close();
    }
    catch( java.io.IOException e ){
     System.out.println(e );
    }
   }
 
   public void start()
   {
    sendMessage("Pagina incarcata");
   }
}

Fisierul HTML:
<applet HEIGHT=10 WIDTH=10 CODE=MessageApplet.class>
<param NAME="PORT" VALUE="1234"></applet>
Apletul utilizeaza metoda getCodeBase() si getHost() pentru aflarea numelui host-ului care il gazduieste, iar 
numarul portului se citeste din fisierul HTML. Astfel vor fi initializate datele obiectului MessageApplet,
myHost si myPort. Metodele start() si stop() respectiv init() sunt metode publice care vor fi utilizate de catre
navigator. init() se apeleaza o singura data la incarcarea paginii, iar start() ori de cate ori se revine la pagina
respectiva. Metoda init() initializeaza datele obiectului, iar start() si stop() apeleaza metoda  sendMessage()
pentru trimiterea datagramei. Prima data se creaza un obiect InetAddress, care este incarcat cu adresa hostului,
dupa care se va crea un obiect de tip DatagramPacket car va contine atat adresa cat mesajul de trimis.
byte data[] =message.getBytes();
//conversie String --> sir de octeti
InetAddress addr = InetAddress.getByName( myHost );
//obtinerea adresei IP al hostului
DatagramPacket packet = new DatagramPacket( data, data.length, addr, myPort );
//crearea pachetului
Dupa ce avem pachetul gata, pregatit pentru trimitere, mai trebuie sa cream un soclu prin care sa se trimita si
putem expedia pachetul cu metoda send(). Dupa epedierea pachetului se va inchide soclul.
DatagramSocket ds = new DatagramSocket();
//Crearea soclu
ds.send( packet );
//Trimitere pachet
ds. close();
//Inchidere soclu
Partea server:
import java.net.*;
import java.io.*;
 
public class AppletServer{
    public static void main( String args[]) throws IOException{
             DatagramSocket s = new DatagramSocket( Integer.parseInt(args[0]));
             while( true ){
                 byte data[] = new byte[1024];
                 DatagramPacket packet = new DatagramPacket( data,1024);
                 s.receive( packet );
                 String message = new String( packet.getData());
                 System.out.println("Hostname: "+packet.getAddress().getHostName()+"
"+message);
             }
    }
}
 
Partea de server:

//Source file: c:\Rose\curs9\exemplu2\ServerPackage\AppletServer.java


package ServerPackage;
public class AppletServer
{
   public static void main(String[] args)
   {
     try
     {
       java.net.DatagramSocket s = new
java.net.DatagramSocket( Integer.parseInt(args[0]));
       while( true ){
          //Serverul se pregateste pentru primirea unui pachet
          byte data[] = new byte[1024];
          java.net.DatagramPacket packet = new java.net.DatagramPacket( data,1024);
          //Serverul asteapta primirea unui pachet
          s.receive( packet );
          //Se scot datele din pachet si se afiseaza la iesirea standard
          String message = new String( packet.getData());
          System.out.println("Hostname: "+ packet.getAddress().getHostName()+"
"+message);
      }
     }
     catch( java.io.IOException e ){}
   }
}
Aplicatia server creaza un soclu de tip DatagramSocket si intra in asteptarea clientilor pe un port. Numarul
portului se primeste de la linia de comanda, fiind de tip String la primire se va converti la intreg prin apelul
metodei Integer.parseInt(args[0]). Dupa crearea soclului se creaza un pachet gol pentru receptionarea
datagramelor sosite. Acest pachet este un obiect de tip DatagramPacket care are si o variabila de tip sir de
octeti, care se utilizeaza pentru stocarea datelor sosite, insa acesta trebuie alocat inaintea sosirii datelor. Dupa ce
s-au creat aceste doua obiecte, soclul apeleaza metoda receive() si intra in astepatarea datagramelor. In
momentul sosirii unei datagrame, se extrage din aceasta mesajul si se va afisa pe terminal.
4. URL
URL (Uniform Resource Locator)  identifica o resursa(obiect) pe Internet. In mod uzual este format din trei sau
patru parti:
protocol://hostname/nume_resursa sau
protocol://hostname:port/nume_resursa
protocol:  poate fi http, ftp,  gopher etc.
hostname: numele calculatorului pe care se afla resursa, putem specifica si adresa IP, daca o stim in loc de
nume host
port: trebuie specificat doar daca serviciul este oferit pe un port nestandard.
nume_resursa: calea si numele resursei
Java ne ofera mai multi constructori pentru crearea unui obiect URL care pot genera exceptia
MalformedURLException in caz ca lipseste ceva din specificatia URL. In momentul construirii obiectului nu se
verifica existenta resursei, deci exceptia se genereaza daca un parametru este transmis incorect.
URL(String spec)
URL(String protocol, String host, int port, String file)
URL(String protocol, String host, String file)
Exemplu de utilizare:
try{
    URL url = new URL("http","www.uttgm.ro",3128, "index.html");
    System.out.println( url.toString());
}
catch( MalformedException e ){
//Se trateaza exceptia
}
  Un aplet poate comunica doar cu calculatorul pe care este stocat codul lui. Cu ajutorul metodei
getDocumentBase() a clasei Applet putem afla numele acestui calculator si dupa acea putem cere accesul la o
resursa de pe acel calculator:
try{
    URL url = new URL("http",getDocumentBase(),"index.html");
    System.out.println( url.toString());
}
catch( MalformedException e ){
//Se trateaza exceptia
}
Daca dorim sa aducem datele asociate unui URL trebuie sa cerem obiectului URL referinta la un stream atasat
acestuia. Referinta se obtine prin apelul metodei openStream(). Exemplul urmator afiseaza  pe terminal
continutul unui fisier HTML.
try{
    URL url = new URL("http://www.uttgm.ro/index.html");
    BufferedReader bin = new BufferedReader( new
                                InputStreamReader(url.openStream()));
    String line;
    while( ( line = bin.readLine()) != null )
        System.out.println( line );
}
catch (Exception e ){}
Exemplu cu URL:
Sa se scrie un aplet care permite utilizatorilor vizualizarea produselor unei firme. Din cauza ca preturile sunt in
continua schimbare acestea se stocheaza intr-un fisier "preturi.dat", aflat in acelasi catalog cu codul
apletului, precum si cu fisierul HTML. In caz ca se schimba preturile produselor, apletul ramane neschimat,
doar fisierul "preturi.dat" se va modifica.
Cand clientul viziteaza pagina Web, browserul incarca prima data fisierul HTML, pe urma codul apletului.
Apletul este rulat pe calculatorul clientului, acesta incarcand fisierul "preturi.dat". Pentru a incarca fisierul
"preturi.dat", apletul creaza un URL:
try{
    URL url = new URL( getDocumentBase(),"preturi.dat");
}
catch( Exception e ){}
Lista de preturi  este formatat a.i sa fie ptrivit pentru a fi incarcat intr-un obiect Properties:
Mere=7000
Pere=10000
Struguri=25000
Basnana=27000
Incarcarea fisierului intr-un obiect Properties se va realiza prin:
Properties preturi = new Properties();
try{
    preturi.load( url.openStream());
}
catch( Exception e ){}
Interfata apletului:

Diagrama de clasa:
Diagrama de secventiere:

Codul sursa:
//Source file: Linie_de_comanda.java
public class Linie_de_comanda
{
   private String nume;
   private int cantitate;
   private double pretunitar;
   public Linie_de_comanda(String nume, int cantitate, double pretunitar)
   {
    this.nume= nume;
    this.cantitate = cantitate;
    this.pretunitar = pretunitar;
   }
   public String toString()
   {
     String s2 = Integer.toString( cantitate );
     String s3 = Double.toString( pretunitar );
     return nume+" : "+s2+" : "+s3;
   }
}
//Source file: CanvasComanda.java
import java.awt.Canvas;
import java.util.Vector;
import java.awt.Graphics;
public class CanvasComanda extends Canvas
{
   private Vector a;
   public void redraw(java.util.Vector a)
   {
    this.a =a;
    repaint();
   }
   public void paint(Graphics g)
   {
     int i;
     int x=80, y=0;
     java.awt.Font f = new java.awt.Font("Monospaced",java.awt.Font.PLAIN, 12 );
     g.setFont( f );
     java.awt.FontMetrics fm = g.getFontMetrics( f );
     int height = fm.getHeight();
     y+=height;
     g.drawString("Your order: ",x,y);
     for( i = 0; i<a.size(); i++ )
     {
      y+=height;
      g.drawString( a.elementAt(i).toString(), x, y );
     }
   }
}
//Source file: ApletComanda.java
import java.applet.Applet;
import java.awt.event.ActionListener;
import java.awt.Choice;
import java.awt.TextField;
import java.awt.Button;
import java.util.Properties;
import java.util.Vector;
import java.awt.event.ActionEvent;
public class ApletComanda extends Applet implements ActionListener
{
   private Vector a = new java.util.Vector();
   public CanvasComanda comanda;
   public Choice nume;
   public TextField cantitate;
   public Button adaugare;
   public Button gata;
   public Properties preturi;
   public void init()
   {
    setLayout( new java.awt.BorderLayout() );
    java.awt.Panel p = new java.awt.Panel();
    p.setLayout( new java.awt.FlowLayout());
    nume = new Choice();
    preturi = new java.util.Properties();
    try{
      java.net.URL url= new java.net.URL( getDocumentBase(),"preturi.dat");
      preturi.load( url.openStream());
    }
    catch( Exception e ){}
    java.util.Enumeration e = preturi.propertyNames();
    while( e.hasMoreElements())
      nume.addItem( ( String ) e.nextElement());
    p.add( nume );
    cantitate = new TextField( 5);
    p.add( cantitate );
    adaugare = new Button("Adaugare");
    p.add( adaugare );
    adaugare.addActionListener( this );
    gata = new Button( "Gata");
    p.add(gata );
    gata.addActionListener( this );
    add( p, "North");
    comanda = new CanvasComanda();
    add( comanda,"Center");
    comanda.setSize(250,150);
    comanda.redraw( a );
   }
   public void actionPerformed(ActionEvent arg0)
   {
    String command = arg0.getActionCommand();
    if( command.equals("Adaugare")){
      String itemName = nume.getSelectedItem();
      int cant = 0;
      double pret = 0.0;
      try{
       cant = Integer.parseInt( cantitate.getText());
       pret = (new Double( preturi.getProperty( itemName ))).doubleValue();
      }
      catch( NumberFormatException e ){}
      a.addElement( new Linie_de_comanda(itemName, cant, pret ));
      comanda.redraw( a );
    }
   }
}

Accesul bazelor de date din Java.


JDBC-Java Database Connectivity
1. Limbajul Java, SQL, CLI
2. Arhitectura JDBC
3. Drivere si manageri de drivere
4. Utilizare JDBC-Exemple
5. Anatomia unei aplicatii JDBC
6. Relatia dintre tipuri SQL si tipuri Java
7. O aplicatie JDBC pe trei nivele
8. Instructiuni preparate
9. Tratarea campurilor speciale
10. Exemple
1. Limbajul Java, SQL si CLI
JDBC este bazat pe doua standarde: SQL (Structured Query Language) si CLI (X/Open Call Level Interface).
CLI este implementat in interfata Microsoft ODBC. ODBC a fost implementat in limbajul C, deci nu se putea 
utiliza direct din Java deoarece s-ar fi stricat portabilitatea acestor aplicatii. O alta problema cu ODBC era ca
fiind scris intr-un limbaj procedural in care caramida de constructie este functia era mai greu atasarea la Java,
care este un limbaj obiectual. Multe functii C din ODBC  returneaza si pointeri void care violeaza caracteristica
fundamentala a limbajului Java, siguranta tipului.
2. Arhitectura JDBC
Interfetele si clasele pentru JDBC se gasesc in pachetul java.sql. Clasele si interfetele utilizate intr-o aplicatie ce
utilizeaza JDBC se gasesc pe figura urmatoare:

O aplicatie JDBC utilizeaza unul sau mai multe drivere din pachetul java.sql care sunt utilizate de catre clasa
DriverManager. Driverele sunt specifice bazelor de date, deci pentru fiecare tip de baza de date se utilizeaza un
driver special. In aceeasi aplicatie putem lucra cu baze de date diferite, deci implicit si cu mai multe drivere.
Putem avea nevoie de un driver care comunica cu o baza de date Oracle aflata la distanta si de un altul care
reprezinta legatura spre driverul ODBC local si comunica cu un server SQL. In aplicatii comunicatia cu o baza
de date necesita urmatorii pasi:
1. Se apeleaza la DriverManager cerand un driver specific pentru baza de date
2. Driverul specific creaza legatura cu baza de date si returneaza un obiect de tip Connection
3. Cu ajutorul obiectului de tip Connection se creaza un obiect Statement care contine si o cerere SQL
catre baza de date
4. Obiectul Statement returneaza rezultatele intr-un obiect ResultSet
3. Drivere si manageri de drivere

4. Utilizare JDBC-Exemple
Exemplul urmator ilustreaza structura tipica a unei aplicatii JDBC.
In primul exemplu vom lucru cu o baza de date  Access. Numele bazei de date va fi Inventory, la fel si numele
singurei tabele continuta in baza de date.
Structura tabelei Inventory:
 
Nume_camp Tip_camp Lungime_camp
NAME Text 40
Numeric-
QUANTITY 20
Real
 Sursa Java:
import java.sql.*;
public class SimpleJDBC{
        public static void main( String args[]){
                try{
                        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                        String databaseName = "jdbc:odbc:Inventory";
                        Connection con    
=DriverManager.getConnection(databaseName,"username","password");
                        Statement stmt = con.createStatement();
                        ResultSet rs = stmt.executeQuery("select * from Inventory");
                        while( rs.next()){
                                System.out.println(rs.getString(1)+":"+rs.getFloat(2));
                        }
                }
                catch( Exception e ){
                        e.printStackTrace();
                }
       }
}
Inainte sa executam programul precedent trebuie sa informam ODBC despre existenta unei surse de date care
va fi Inventory.mdb. Sursa de date o vom denumi tot Inventory. Dupa ce s-a creat si s-a indexat baza de date se
selecteaza din Control Panel--ODBC32. De aici se trece la pagina System DSN unde se introduce in caseta
Data Source Name "Inventory". Dupa executarea acestor pasi tabela de date va fi atasata driverului permitand
operatii cu aceasta. ODBC este un protocol care asigura accesul la baze de date utilizate sub s.o. Windows. In
exemplul precedent aplicatia noastra acceseaza direct baza de date. Asemenea aplicatii se numesc aplicatii cu
doua nivele. Intr-o abordare eleganta aplicatiile sunt proiectate pe trei nivele. In cazul acestor aplicatii clientul
lucreaza cu un server care reprezinta un nivel intermediar intre client si baza de date.
Urmatorul exemplu va lucra cu o baza de date Access continand doua tabele.
Nume baza de date: Universitate.mdb
Nume tabel 1: Catedre
Structura:
 CODCATEDRA       Long
 DENUMIRE         Text
Indexari: CODCATEDRA (cheie primara)
Nume tabel 2: Profesori
Structura:
  CODPROFESOR   Long
 NUME          Text
 CODCATEDRA    Long
Indexari: CODPROFESOR (cheie primara); CODCATEDRA
 
import java.sql.*;
public class SimpleJDBC{
        public static void main( String args[]){
                try{
                        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                        String databaseName = "jdbc:odbc:Universitate";
                        Connection con =
DriverManager.getConnection(databaseName,"username","password");
                        Statement stmt = con.createStatement();
                        ResultSet rs = stmt.executeQuery("select
Profesori.codprofesor,Profesori.nume,Profesori.codcatedra,Catedre.nume  from
Catedre,Profesori where Catedre.codcatedra=Profesori.codcatedra order by catedre.nume");
                        while( rs.next()){
                               
System.out.println(rs.getLong(1)+":"+rs.getString(2)+":"+rs.getLong(3)+":"+rs.getString(4
));
                        }
                }
                catch( Exception e ){
                        e.printStackTrace();
                }
       }
}
5. Anatomia unei aplicatii JDBC
 
a. Instalarea unui driver
Primul lucru pe care trebuie sa faca o aplicatie care lucreaza cu baze de date este instalarea unui driver specific
bazei de date. Exemple:
Class.forName("com.sybase.jdbc.SybDriver");
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Prin apelul acestor metode se va incarca un driver JDBC. In clasa specifica driverului esista o metoda statica
care va inregistra existenta sa cu DriverManager.  Dupa incarcarea driverului trebuie creata legatura cu baza de
date. Se ridica o serie de probleme:
 unde se gaseste pe Internet calculatorul caruia apartine baza de date.?
 pe ce numar de port asculta RDBMS-ul cererile?
Internetul a rezolvat deja aceasta problema prin introducerea URL-urilor (Uniform resource Locator) care este
format din: protocol//nume_host:port//cale. Pentru JDBC s-a adaptat aceasta specificatie. Un URL pentru o
baza de date ar avea forma:
jdbc:<protocol_secundar>:<nume_secundar>//nume_host:port//nume_baza_de_date.
 
b.Crearea conexiunii
La crearea conexiunii pe langa numele bazei de date se vor transmite si numele utilizatorului precum si parola
acestuia. Daca sunt incarcate mai multe drivere pentru lucrul cu diferite baze de date, atunci se pune problema
cum se alege driverul pentru conexiunea curenta. Clasa DriverManager este responsabila pentru acesta
intreband fiecare driver daca poate realiza legatura cu baza de date specificata prin url. De exemplu un driver
Oracle ar observa imediat ca in exemplul prezentat se lucreaza cu un alt tip de baza de date si ar refuza cererea.
La realizarea conexiunii se creaza un obiect de tip Connection. De fapt Connection este o interfata, care asigura
transmiterea datelor spre baza de date si obtinerea datelor din baza de date. Interfata furnizeza metode pentru a
obtine informatii despre baza de date, inchid conexiunea cu baza de date si asculta mesajele sosite de la baza de
date.
 
c. Accesul la baza de date
Interfata Connection contine si metoda createStatement() care ne va furniza un obiect Statement printr-un apel
de urmatoarea forma:
Statement stmt = con.createStatement();
Metodele cele mai importante ale interfetei Statement sunt urmatoarele: executeQuery(String),
executeUpdate(String), execute(String). Aceste metode se utilizeaza pentru executia codului SQL. Metoda
executeQuery() executa comanda SQL si returneaza un  obiect de tip ResultSet si se utilizeaza pentru executia
comenziilor SQL de interogare  SELECT. Metoda executeUpdate() executa comanda SQL primita ca parametru
si returneaza numarul randurilor tabelei modificate (update count). Se utilizeaza pentru comenzile SQL de
manipularea datelor: INSERT, UPDATE, DELETE si pentru comenzi de definire a datelor CREATE/DROP
TABLE. In cazul comenzilor de definirea datelor valoarea returnata este 0. Ultima metoda execute() poate fi
privita ca fiind generalizarea celorlaltor doua metode. Se utilizeaza daca comanda SQL poate returna deodata
mai multe rezultate sau nu se cunoaste rezultatul executiei.
ResultSet rs = stmt.executeQuery("select * from Inventory");
d. Prelucrarea rezultatelor
ResultSet este tot o interfata. Obiectul ResultSet contine rezultatul interogarii bazei de date, insa pointerul
atasat acestei tabele puncteaza inaintea primului rand din tabela. Acest lucru ne este comod fiindca cu un singur
ciclu while putem parcurge rezultatele obtinute:
while( rs.next()){
 System.out.println( rs.getString(1)+":"+rs.getFloat(2));
}
Singurul pericol in asemenea constructii poate fi ca rezultatul interogarii nu produce nici o linie iar noi avem
ceva asemanator cu constructia de mai jos:
ResultSet rs = stmt.executeQuery("A Query...");
int i = rs.getInt(1);
Daca rezultatul interogarii este vid atunci constructia de mai sus va genera exceptie SQLException.
ResultSet contine metode pentru accesul datelor din rezultat. Astfel metodele getXXX(int) si getXXX(String)
returneaza valoarea dintr-o coloana specificata prin parametru si linia curenta. In locul XXX se pune un tip
predfinit Java (tip primitiv-ex. int sau clasa de baza-ex. String).
 
6. Relatia dintre tipuri SQL si tipuri Java
 
Tip SQL Tip JAVA Metoda
CHAR String getString()
VARCHAR String getString()
LONGVARCHAR String getString()
NUMERIC java.math.BigDecimal getBigDecimal()
DECIMAL java.math.BigDecimal getBigDecimal()
BIT boolean getBoolean()
TINYINT byte getByte()
SMALLINT short getShort()
INTEGER int getInt()
BIGINT lonng getLong()
REAL float getFloat()
DOUBLE double getDouble()
BINARY byte[] getBytes()
VARBINARY byte[] getBytes()
LONGVARBINARY byte[] getBytes()
DATE java.sql.Date getDate()
TIME java.sql.Time getTime()
TIMESTAMP java.sql.Timestamp getTimestamp()

 
7.  O aplicatie JDBC pe trei nivele
In exemplele precedente aplicatiile noastre contineau si codul SQL. Vom rescrie primul exemplu cu baza de
date inventory astfel incat vom crea clase speciale pentru toate operatiile posibile cu acesta tabela. Aplicatiile
care au nevoie de operatii cu aceasta  tabela vor utiliza clasele speciale, astfel aceste clase realizand un nivel
intermediar intre baza de date si aplicatia propriu zisa care lucreaza cu datele din tabela. Aceste clase
incapsuleaza logica aplicatiei.

Clasa InventoryItem lucreaza cu o inregistrare din tabela Inventory. Contine exact doua campuri, tipul acestor
campuri coincide cu tipul campurilor din tabela si contine metode de tip setXXX() care permit modificarea
acestor campuri precum si metode de tip getXXX() pentru returnarea valorilor din aceste campuri.
Clasa InventoryManager va fi cea care contine logica aplicatiei. Constructorul clasei incarca driverul pentru a
realiza legatura cu baza de date, iar celelalte metode permit operatiile necesare aplicatiilor si anume: ce
cantitate exista dintr-un anumit material, modificarea cantitatii dintr-o materie si verificarea daca dintr-o
anumita materie exista peste o anumita cantitate.
Diagrama de clase:
Sursa Java a aplicatiei:
Fisierul InventoryItem.java:
public class InventoryItem {
  String item;
  float amount;
  public InventoryItem() {
    item = "";
    amount = 0;
  }
  public InventoryItem(String s, float a) {
    item = s;
    amount =a ;
  }
  public void setAmount(float a) {
    amount = a;
  }
  public float getAmount() {
    return amount;
  }
  public void setItem(String s) {
    item = s;
  }
  public String getItem() {
    return item;
  }
}
Fisierul InventoryManager.java:
import java.sql.*;
import java.util.Vector;
import java.util.Enumeration;
public class InventoryManager {
  Statement stmt;
  String databaseName = "jdbc:odbc:Inventory";
  public InventoryManager() {
    try {
     Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    }
    catch( Exception e ) {
      e.printStackTrace();
    }
  }
  //Verifica daca din materia ingredient avem peste cantitatea amount
  public boolean checkInventory(String ingredient, float amount){
    try {
      Connection con = DriverManager.getConnection(databaseName, "", "");
      stmt = con.createStatement();
      ResultSet rs = stmt.executeQuery(
        "SELECT Quantity FROM Inventory WHERE Ingredient = '"
        + ingredient +"'");
      rs.next();
      if (amount > rs.getFloat(1))
        return false;
      else
        return true;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return false;
  }
 
  public boolean checkInventory(Vector ingredients) {
    try {
      Connection con = DriverManager.getConnection(databaseName, "", "");
      stmt = con.createStatement();
      Enumeration e = ingredients.elements();
      while (e.hasMoreElements()) {
        InventoryItem i = (InventoryItem) e.nextElement();
        ResultSet rs = stmt.executeQuery(
        "SELECT Quantity FROM Inventory WHERE Ingredient = '"
          + i.item + "'");
        rs.next();
        if (i.amount > rs.getFloat(1))
          return false;
      }
      return true;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return false;
  }
  //Returneaza cantitatea din materia primita ca parametru
  public float quantityOnHand(String ingredient)
    throws SQLException {
      Connection con =DriverManager.getConnection(databaseName, "", "");
      stmt = con.createStatement();
      ResultSet rs = stmt.executeQuery("SELECT Quantity FROM Inventory WHERE Ingredient =
'"
      + ingredient +"'");
    rs.next();
    return rs.getFloat(1);
  }
  //Actualizeaza cantitatea din materia ingredient la cantitatea quantity
  public void replenish(String ingredient, float quantity)
    throws SQLException {
      Connection con =
              DriverManager.getConnection(databaseName, "", "");
      stmt = con.createStatement();
    ResultSet rs = stmt.executeQuery(
      "SELECT Quantity FROM Inventory WHERE Ingredient = '"
      + ingredient +"'");
    rs.next();
    stmt.executeUpdate(
      "UPDATE Inventory SET Quantity = " +
      (rs.getFloat(1) + quantity) +
      " WHERE Ingredient = '" + ingredient +"'");
  }
}
Fisierul InventoryMain.java:
 
public class InventoryMain {
  public static void main(String args[]){
    InventoryManager im = new InventoryManager();
    try {
      System.out.println("Este cantitatea de zahar mai mare decat 90: " +
im.checkInventory("zahar", 90f));
      System.out.println("Cantitatea de zahar este:" + im.quantityOnHand("zahar"));
      im.replenish("zahar", 100f);
      System.out.println("Cantitatea de zahar este:" + im.quantityOnHand("zahar"));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
8. Instructiuni preparate

In cazul aplicatiilor care lucreaza cu baze de date este foarte frecventa utilizarea aceluiasi cod SQL de mai
multe ori. RDBMS-urile faciliteaza crearea instructiunilor preparate, care trebuie create, analizte si optimizate o
singura data in baza de date si dupa aceea pot fi utilizate in aplicatii. JDBC faciliteaza utilizarea instructiunilor
preparate prin clasa PreparedStatement. Aceasta clasa este subclasa clasei Statement. Clasa
PreparedStatement are un constructor care primeste ca parametru un string reprezentand instructiunea SQL.
PreparedStatement pstmt = con.preparedStatement("SELECT quantity
FROM Inventory WHERE ingredient=?";
Se poate observa ca instructiunea SQL contine si semne de intrebare. Aceste semne de intrebari reprezinta
parametrii interogarii. Inainte de executia propriu-zisa a codului acesti parametrii pot fi setati. In exemplul
precedent am utilizat o metoda checkInventory( Vector ingredients ) pentru a afla daca din fiecare materie
prima avem cantitatile dorite.
Enumeration e =ingredients.elements();
while( e.hasMoreElements()){
    InventoryItem i = (InventoryItem) e.nextElement();
    ResultSet rs = stmt.executeQuery( "SELECT quantity FROM Inventory WHERE ingredient ='
" + i.item+" ' ");
    rs.next();
    if( rs.getFloat(1) < i.amount )
        return false;
 }
return true;
In acest exemplu pentru fiecare materie prima din vectorul ingredients s-a executat cate o instructiune SQL,
timpul de executie astfel devenind foarte mare. Vom rescrie aceasta metoda utilizand un obiect de tip
PreparedStatement facand ca aplicatia sa devina mai eficienta.
PreparedStatement pStmt = con.prepareStatement("SELECT Quantity FROM Inventory WHERE
Ingredient =?");
Enumeration e =ingredients.elements();
while( e.hasMoreElements()){
    InventoryItem i = (InventoryItem) e.nextElement();
    pStmt.setString(1,i.item);
    ResultSet rs = stmt.executeQuery();
    rs.next();
    if( rs.getFloat(1) < i.amount )
        return false;
 }
return true;
Astfel codul SQL se compileaza o singura data pe partea de baza de date si la fiecare apel executeQuery() se
va executa acel cod precompilat cu parametrii inlocuiti.Parametrii interogarii sunt setati de catre metodele
setXXX() ale clasei ResultSet. Primul parametru al acestor metode este numarul de ordine al parametrului din
secventa SQL care se inlocuieste, iar al doilea parametru este valoarea cu care se inlocuieste.
9. Conversie SQL-Java
 
Tip Java Tip SQL Metoda
Java.math.BigDecimal NUMERIC setBigDecimal()
boolean BIT setBoolean()
byte TINYINT setByte()
short SMALLINT setShort()
int INTEGER setInt()
long BIGINT setLong()
float REAL setFloat()
double DOUBLE setDouble()
byte[] VARBINARY sau LONGVARBINARY setBytes()
java.sql.Date DATE setDate()
java.sql.Time TIME setTime()
java.sql.Timestamp TIMESTAMP setTIMESTAMP()
String VARCHAR sau LONGVARCHAR setString()
10. Tratarea campurilor speciale
Multe SGBD-uri faciliteaza folosirea campurilor de obicete speciale ( binary large object - BLOB ). Aceste
campuri sunt utilizate pentru stocarea datelor de lungime variabila care pot fi documente, foi de calcul, imagini,
pagini Web etc. JDBC faciliteaza utilizarea acestor campuri de date. Exemplul urmator va scrie un asemenea
camp de date intr-o baza de date, iar dupa aceea va citi un alt camp de date salvand rezultatul intr-un fisier.
Exemplul nostru va stoca imagini de tip .gif. Ca prim pas vom crea cu Microsoft Access o baza de date cu
urnatoarea structura:
Imagini.mdb cu tabela imagini:
 
Nume_Camp Tip_Camp
COD Numeric(10)
NUME  Text(30)
IMAGINE OLE Object
Dupa crearea bazei de date sursa de date trebuie sa fie facuta public prin intermediul ODBC.
Datele in aceasta tabela vor fi introduse de catre program.
Nume clasa:    JDBCImages
Date:
    -databaseName: String
    -con: Connection
    -pictures: String[5]
    -pStmt1, pStmt2: PreparedStatement
Metode:
    +JDBCImages()
    +writeRecord( int cod )
    +readRecord( int cod, String fileName)
Sursa Java:
import java.io.*;
import java.sql.*;
public class JDBCImages{
        String databaseName = "jdbc:odbc:Imagini";
        Connection con = null;
        String pictures[]={"curs2_fig1.gif","curs2_fig2.gif",
        "curs2_fig3.gif","curs2_fig4.gif", "curs2_fig5.gif"};
        PreparedStatement pStmt1 = null;
        PreparedStatement pStmt2 = null;
        public JDBCImages(){
          // Incarcare driver - Creare conexiune
          try{
                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                con = DriverManager.getConnection(databaseName,"","");
                pStmt1 = con.prepareStatement("INSERT INTO imagini
                        (cod,nume,imagine) VALUES  (?,?,?)");
                pStmt2 = con.prepareStatement("SELECT  imagine  FROM
                        imagini WHERE cod=?");
          }
          catch( Exception e ){
                System.out.println("Eroare constructor");
                e.printStackTrace();
          }
        }
        public void writeRecord( int cod )
        {
          try{
              File inFile = new File( pictures[ cod ] );
              int flength = (int) inFile.length();
              FileInputStream in = new FileInputStream( inFile );
              pStmt1.setInt(1,cod);
              pStmt1.setString(2,pictures[cod]);
              pStmt1.setBinaryStream(3,in,flength);
              pStmt1.executeUpdate();
          }
          catch( Exception e ){
                System.out.println("Eroare INSERT");
                e.printStackTrace();
          }
        }
        public void readRecord( int cod, String fileName )
        {
                byte [] picture = new byte[ 1024 ];
                try{
                     pStmt2.setInt(1,cod);
                     File outFile = new File(fileName);
                     FileOutputStream out = new FileOutputStream( outFile );
                     ResultSet rs = pStmt2.executeQuery();
                     rs.next();
                     InputStream ins = rs.getBinaryStream(1);
                     int n;
                     int s =0;
                     while( (n=ins.read(picture))>0)
                     {
                         out.write( picture,0,n);
                         s+=n;
                     }
                     System.out.println("Total bytes read: "+Integer.toString(s));
                     out.close();
                }
                catch( Exception e ){
                        System.out.println("eroare SELECT");
                        e.printStackTrace();
                }
          }
          public static void main( String args[]){
                int i;
                JDBCImages o = new JDBCImages();
                o.writeRecord( 1 );
                o.readRecord( 1, "picture1.gif");
          }
}
In exemplul precedent constructorul incarca driverul pentru baza de date si realizeaza conexiunea cu aceasta,
dupa care creaza doua instructiuni preparate: una pentru a insera o inregistrare in baza de date si una pentru a
obtine campul imagine din baza de date. Prima instructiune preparata va fi utilizata de catre metoda
writeRecord( int ), care primind indicele unui element din tabloul pictures insereaza in baza de date o
inregistrare. A doua instructiune preparata este folosita de catre readRecord( int, String ). Aici primul
parametru indica codul inregistrarii care trebuie selectata, iar al doilea parametru va fi utilizat pentru numele
fisierului in care se salveaza datele obtinute din baza de date.Pentru a scrie intr-un asemenea camp sau a citi din
aceasta se vor utiliza metodele setBinaryStream() respectiv getBinaryStream() ale clasei ResultSet.
 
Noutati aduse de JDBC 2.0:
 ResultSet-uri derulabile
Daca dorim sa lucram cu ResultSet-uri derulabile trebuie sa utilizam doi parametri in metodele clasei
Connection, createStatement respectiv prepareStatement:
    Statement stmt = con.createStatement( ResultSet.TYPE_SCROLL_SENSITIVE,
                                          ResultSet.CONCUR_READ_ONLY);
Intr-un ResultSet derulabil putem utiliza metodele:
 
Metoda Rezultat
boolean first() Pozitionarea indicatorului pe prima linie 
boolean previous() Pozitionarea indicatorului pe linia precedenta
boolean next() Pozitionarea indicatorului pe prima urmatoare
boolean last() Pozitionarea indicatorului pe ultima linie
boolean absolute( int poz ) Pozitionarea indicatorului pe poztia indicata 
boolean relative ( int pozitie_relativa ) Pozitionarea indicatorului relativ la pozitia curenta
 Toate actualizarile, inserarile si stergerile pot fi facute printr-o singura operatie cu baza de date utilizand
comenzile de tip batch.
           Pentru a realiza operatile de ti batch se va utiliza metoda clasei Statement addBatch. Exemplu de
utilizare:
//Creare Statement
Statement stmt = con.CreateStatement();
//Adaugare comenzi
stmt.addBatch("INSERT INTO Catedre (CodCatedra, Denumire) VALUES (5,'Stiinte
Economice')");
stmt.addBatch("INSERT INTO Profesori (CodProfesor, Codcatedra, Nume ) VALUES (10,2,'Pop
Ioan')");
//Executarea comenzilor
int [] updateCounts = stmt.executeBatch();
Metoda executeBatch() returneaza un tablou de elemente, continand randurile care au fost afectate prin
comanda data. Comenzile dintr-un batch se executa in ordinea in care au fost asezate in batch. Daca vreo
comanda este incorecta atunci se genereaza exceptia BatchUpdateException si  se opreste executarea
comenzilor din batch. Batch-urile pot fi utilizate si in cazul instructiunilor preparate.
PreparedStatement pstmt = con.prepareStatement("DELETE FROM Catedre WHERE Denumire=?");
Enumeration e =v.elements();
while( e.hasMoreElements()){
    pstmt.setString( 1, {String) e.nextElement());
    pstmt.addBatch();
}
int [] updateCounts = pstmt.executeBatch();
 Tipuri de date SQL 3.0. Obiectele Java pot fi stocate in baze de date.
11. Exemple:

1. Sa scrie o aplicatie care sa permita interogarea oricarei baze de date. Rezultatele selectiei se vor afisa intr-un
element de control TextArea. Interfata aplicatiei sa aiba urmatoarea forma:
 
 
Sursa Java:
import java.awt.*;
import java.awt.event.*;
import java.sql.*;
 
public class TestJdbc extends Frame implements ActionListener{
        String Driver=null;
        String DataBase = null;
        String UserName = null;
        String Password  = null;
        String SQL= null;
 
        TextField tfDriver;
        TextField tfDataBase;
        TextField tfUserName;
        TextField tfPassword;
        TextField tfSQL;
        Button b;
        TextArea  ta;
 
        // Constructorul creaza interfata aplicatiei si inregistreaza receptorii
        // evenimentelor la sursele acestora
        public TestJdbc(){
                setTitle("Jdbc Application");
                setLayout( new FlowLayout());
                Label l1 = new Label("Driver");
                add( l1 );
                tfDriver   =new TextField(60);
                add(tfDriver);
                Label l2 = new Label("DataBase URL");
                add( l2 );
                tfDataBase =new TextField(60);
                add(tfDataBase);
                Label l3 = new Label("User name:");
                add( l3 );
                tfUserName =new TextField(60);
                add(tfUserName);
                Label l4 = new Label("Password:");
                add( l4 );
                tfPassword =new TextField(60);
                add(tfPassword);
 
                Label l5 = new Label("SQL:");
                add( l5 );
                tfSQL      =new TextField(60);
                add(tfSQL);
                b= new Button( "Executa");
                add(b);
                b.addActionListener( this);
                ta= new TextArea( 10,60);
                add(ta);
                addWindowListener( new MyWindowAdapter());
        }
        //Prelucrarea datelor din formularul prezentat in interfata aplicatiei
        private void processData(){
                Driver = tfDriver.getText();
                if( Driver.equals(""))
                        Driver="sun.jdbc.odbc.JdbcOdbcDriver";
                DataBase =tfDataBase.getText();
                if( DataBase.equals(""))
                        DataBase="jdbc:odbc:Demo";
                UserName = tfUserName.getText();
                Password = tfPassword.getText();
                SQL = tfSQL.getText();
                if ( SQL.equals(""))
                        SQL="Select * From Catedre";
        }
 
        // Tratarea evenimentului generat prin apasarea butonului "Executa"
        // Se incarca driverul pt. baza de date
        // Se creaza conexiunea cu baza de date
        // Se creaza obiectul de tip Statement, care va executa codul SQL
        //
        public void actionPerformed( ActionEvent e ){
                ta.setText("");
                processData();
                try{
                       Class.forName( Driver );
                        Connection con =
                            DriverManager.getConnection(DataBase,UserName,Password);
                       Statement  stmt= con.createStatement();
                        // Se obtin rezultatele selectiei
                       ResultSet  rs = stmt.executeQuery( SQL );
                        // Informatii (metadatele) despre selectie
                        // Se poate obtine atat numarul coloanelor
                        // din selectie, cat si tipurile acestor
                        // coloane
                       ResultSetMetaData rsmd = rs.getMetaData();
                        // Prelucrarea rezultatelor
                        int numCols = rsmd.getColumnCount();
                        while( rs.next() )
                        {
                            String record="";
                            for (int i=1; i<=numCols; i++)
                            {
                                 int dataType = rsmd.getColumnType( i );
                                 record=record+getField(rs,dataType, i)+" ";
                            }
                            ta.append( record+"\n" );
                        }
                }
                catch( SQLException ex1 ){
                        System.out.println(ex1.toString());
                }
                catch( ClassNotFoundException ex2 ){
                        System.out.println(ex2.toString());
                }
        }
 
    private String getField(ResultSet rs, int dataType, int col)
                     throws SQLException
    {
     switch(dataType) {
     case Types.DATE:
       java.sql.Date date = rs.getDate(col);
       return date.toString();
     case Types.TIME:
       java.sql.Time time = rs.getTime(col);
       return time.toString();
     case Types.TIMESTAMP:
       java.sql.Timestamp timestamp = rs.getTimestamp(col);
       return timestamp.toString();
     case Types.CHAR:
     case Types.VARCHAR:
     case Types.LONGVARCHAR:
       String str = rs.getString(col);
       return str;
     case Types.NUMERIC:
     case Types.DECIMAL:
       java.math.BigDecimal numeric = rs.getBigDecimal(col, 10);
       return numeric.toString();
     case Types.BIT:
       boolean bit = rs.getBoolean(col);
       return (new Boolean(bit)).toString();
    case Types.TINYINT:
       byte tinyint = rs.getByte(col);
       return (new Integer(tinyint)).toString();
     case Types.SMALLINT:
       short smallint = rs.getShort(col);
       return (new Integer(smallint)).toString();
     case Types.INTEGER:
       int integer = rs.getInt(col);
       return (new Integer(integer)).toString();
     case Types.BIGINT:
       long bigint = rs.getLong(col);
       return (new Long(bigint)).toString();
     case Types.REAL:
       float real = rs.getFloat(col);
       return (new Float(real)).toString();
    case Types.FLOAT:
    case Types.DOUBLE:
       double longreal = rs.getDouble(col);
       return (new Double(longreal)).toString();
    case Types.BINARY:
    case Types.VARBINARY:
    case Types.LONGVARBINARY:
       byte[] binary = rs.getBytes(col);
       return new String(binary);
   }
     return "";
 }
 
        // Pt. tratarea evenimentului WindowClosing
        class MyWindowAdapter extends WindowAdapter{
                public void windowClosing( WindowEvent e ){
                        System.exit(0);
                }
        }
 
        public static void main( String args[]){
                TestJdbc frame = new TestJdbc();
                frame.setBounds(1,1,300,300);
                frame.setVisible( true );
        }
}

Servleturi si Java Webserver


1. Introducere
2. Anatomia unui servlet HTTP
3. Instalarea servletului in WebServer
4. Arhitectura pachetelor  javax.servlet si javax.servlet.http
5. Exemple
1. Introducere
Webul a trecut printr-o transformare rapida. Paginile statice HTML au fost schimbate cu pagini generate in mod
dinamic. Avantajul acestor pagini este ca informatia continuta in ele poate reflecta de exemplu continutul unei
magazii cu stocul in continua schimbare. Crearea acestor pagini necesita insa multa munca de programare.
    Java cu ajutorul apleturilor ajuta la crearea aplicatiilor pe partea clientilor. Pe partea serverelor s-au introdus
servleturile. Servletul este un mecanism de generarea acestor pagini dinamice HTML, oferind deservirea rapida
si eficienta a clientilor.
    Inaintea servleturilor partea de server a aplicatiilor client/server era ajutata de scripturi CGI (Common
Gateway Interface). CGI permitea si evaluarea formularelor trimise de catre clienti.
Comparatie CGI - Servlet
Scripturile CGI sunt mai putin eficiente decat servleturile. In cazul utilizarii CGI pentru deservirea unui client
se creaza un proces nou in care se executa scriptul CGI. Scriptul genereaza pagina HTML si trimite  clientului.
Crearea unui proces nou este o operatie costisitoare din punctul de vedere al sistemului de opearare.
Servletul face acelasi lucru ca un script CGI, insa servletul se ruleaza pe un fir de executie separat al procesului
webserver. Crearea unui fir de executie este mult mai eficienta decat crearea unui proces nou. Pe langa eficienta
servleturile mai au o caracteristica impotantanta, securitatea. Firele de executie sunt create de catre masina
virtuala Java si poseda toate facilitatiile oferite de aceasta din punctul de vedere al securitatii.
Exemplul urmator de servlet:genereaza in mod dinamic o pagina HTML care contine data si ora serverului si un
text aleator.

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.Date;
import java.lang.Math;
public class PhraseServlet extends HttpServlet {
  String [] adjective = {"fericit",
                          "nefericit",
                          "suparat",
                          "nervos",
                          "indurerat"};
  String [] adverbe = {"foarte",
                       "usor",
                       "putin",
                       "total"};
  public void doGet(HttpServletRequest req, HttpServletResponse res)
   throws ServletException, IOException {
    Date now = new Date();
    res.setContentType("text/html");
    PrintWriter out = res.getWriter();
    out.println("<HTML><HEAD><TITLE>Phrase Servlet</TITLE></HEAD>");
    out.println("<BODY BGCOLOR=#FFFFFF><CENTER><P>");
    out.println("Astazi ");
    out.println(now.toString());
    out.println(" acest webserver se simte  ");
    out.println(adverbe[(int) (Math.random() * adverbe.length)] + " " );
    out.println(adjective[(int) (Math.random() * adjective.length)] + ".");
    out.println("</P></CENTER></BODY></HTML>");
    out.flush();
    out.close();
 }
}
2. Anatomia unui servlet HTTP
Pachetul javax.servlet ne ofera un cadru generic pentru crearea serverelor. Contine API-uri pentru prelucrarea
cererilor si deservirea clientilor. Un alt pachet, javax.servlet.http contine implementarea servleturilor utilizate in
cazul webserverelor pentru generarea paginilor HTML.

Un servlet HTTP pentru a putea realiza o anumita sarcina trebuie sa redefineasca metoda doGet(). Metoda este
apelata de catre webserver ori de cate ori serverului ii este adresata o cerere HTTP care contine un GET <url>.
Metoda este definita in clasa HttpServlet pe langa metodele: doDelete(), doOptions(), doPost(), doTrace(). De
acum incolo ne vom referi la aceste metode in mod generic prin doXXX(). Aceste metode au doi parametri,
unul de tip ServletRequest si unul de tip ServletResponse. Cele doua clase contin metode si variabile care
permit comunicare servletului cu webserverul si pot genera exceptiile ServletException si IOException:
public void doGet( HttpServletRequest req, HttpServletResponse
res) throws ServletException, IOException{...}
Clasa ServletRequest contine toate informatiile necesare servletului pentru a intelege cererea si a putea
raspunde la aceasta. Contine informatii referitoare la calculatorul caruia trebuie sa raspunda.
Clasa ServletResponse contine metode care permit crearea si trimiterea raspunsului. Astfel cu ajutorul metodei
setContentType( String) se stabileste tipul datelor care se trimit inapoi. Dintre tipurile posibile amintim:
text/html, text/plain. O alta metoda importanta este getWriter(). Metoda returneaza o referinta de tip
PrintWriter, care este un flux de iesire prin care se trimite raspunsul. Aceasta metoda se utilizeaza daca
servletul trebuie sa trimita un continut de tip text si metoda getOutputStream() se utilizeaza daca se trimit date
binare.
 
3. Instalarea servletului in WebServer
Servletul trebuie instalat intr-un server care permite lucrul cu servleturi. Java Webserver de la Sun ne ofera
aceasta facilitate. Servletul poate fi instalat si fara repornirea serverului. In general un servlet poate fi incarcat
din trei surse:
 din CLASSPATH
 din catalogul servlets
 dintr-un URL
Pentru a putea instala servletul PhraseServlet va trebui sa copiem fisierul PhraseServlet.class in catalogul
servlets al softului JavaWebserver.
In JavaWebserver avem la dispozitie o serie de apleturi de administrare care se vor utiliza la instalarea
servletului nostru. Aceste apleturi se gasesc la adresa http://<nume_host>:9090/. La accesarea acestui URL
apare apletul admin:

Aici vom introduce atat la nume cat si la parola cuvantul "admin". Dupa logare  apare urmatoarea pagina:
Din lista se alege "Web Service" si se apasa butonul "Manage". Dupa alegerea acestui buton apare urmatoarea
pagina:

In aceasta pagina se completeaza casetele Description si Class Name asa cum se vede pe figura de dinainte.
Alegand optiunea Load at Startup servletul se va incarca la fiecare pornire al webserverului. Dupa completarea
casetelor se alege butonul Save pentru a salva optiunile selectate. Dupa aceste reglaje servletul nostru poate
accesat la adresa: http://<nume_calculator>:<port>/servlet/PhraseServlet.
Putem atasa si aliasuri servletului nostru. Aliasul se ataseaza prin alegerea butonului Setup din bara de
instrumente. Apare pagina urmatoare:
Completand casetele Alias si Servlet Invoked si salvand optiunile vom putea accesa servletul nostru la adresa:
http://<nume_calculator>:<port>/PhraseServlet.html.
Daca serverul rezolva concomitent doua cereri de acelasi tip, atunci trebuie apelat acelasi servlet. Servleturile
sunt rulate pe fire de executie diferite, fiecare avand cate o copie de ServletRequest si ServletResponse, astfel
neputand amesteca parametrii I/E. Serverele pot avea o arhitectura secventiala sau una paralela. Daca dorim sa
implementam una cu arhitectura secventiala atunci servletul trebuie definit a.i. sa implementeze interfata
SingleThreadModel.
public class MyServlet extends HttpServlet implements SingleThreadModel{
}
Java Webserver este un server de capacitate medie, deci se poate utiliza in cazul serverelor care nu sunt prea
frecvent utilizate. Exista si alte webservere care lucreaza cu servleturi: Netscape Enterprise Server, Microsoft
IIS, Apache Web Server
 
 
4. Arhitectura pachetelor javax.servlet si javax.servlet.http

    Pachetul javax.servlet ne ofera clase si interfete pentru scrierea servleturilor. Principala interfata definita in
acest pachet este interfata Servlet. Toate servleturile implementeaza aceasta interfata, cel mai des prin
extinderea clasei HttpServlet.
  Interfata Servlet declara metode care gestioneaza comunicarile cu toti clientii. In momentul cand un servlet
accepta conexiunea cu un client, primeste si doua obiecte:
 Un obiect ServletRequest, care este responsabil pentru comunicarea client--->server
 Un obiect ServletResponse, care este responsabil pentru comunicarea inversa server---> client
Interfata ServletRequest permite servletului urmatoarele:
 
 Informatii despre parametrii primiti de la client, protocolul utilizat de catre client, numele hostului de la
care s-a acceptat cererea.
 Un flux de intrarea ServletInputStream. Servletul utilizeaza acest flux de intrare pentru citirea datelor de
la clienti . Clientii  utilizeaza metode ca  PUT si POST ale protocolului HTTP.
Interfata ServletResponse declara metode prin care servletul poate trimite raspunsuri la cererile clientilor.
 
 Permite stabilirea lungimii raspunsului precum si  tipul MIME  al acestuia.
 Un flux de iesire ServletOutputStream si unul Writer prin care servletul va trimite raspunsul la cerere.
Interactiuni cu clientii
    Un servlet HTTP trateaza cererile clientilor cu ajutorul metodei service().  Metoda service() este cea care
apeleaza metoda doGet() pentru prelucrarea cererii.
    Cereri si raspunsuri
Tratarea cererilor GET si POST se face cu ajutorul urmatoarelor metode:
 
doGet, pentru tratarea cererilor GET, GET conditionat si HEAD
doPost, pentru tratarea cererilor POST
doPut,  pentru tratarea cererilor PUT
doDelete, pentru tratarea cererilor DELETE
Implicit aceste metode returneaza eroarea BAD_REQUEST (400) afisata de catre aplicatia client (programul de
navigare).
 
Deservirea clientilor
 
Servleturile sunt capabile pentru deservirea clientilor  in mod concurent. Daca deservirea clientilor necesita
accesul unor resurse partajate atunci accesul acestor resurse trebuie sincronizate.
Daca dorim deservirea unui singur client la un moment dat, atunci servletul nostru trebuie sa implementeze
interfata SingleThreadModel  pe langa extinderea clasei  HttpServlet. Implementarea acestei interfete nu
necesita scrierea unor metode suplimentare, asigura deservirea unui singur client la un moment dat.
         public class MyServlet extends HttpServlet
                                     implements SingleThreadModel {
             public void doPost(HttpServletRequest request,
                                HttpServletResponse response)
                 throws ServletException, IOException {
                     ...
             }
             ...
         }
Prelucrarea cererii
    Metode clasei HttpServletRequest
 
 Enumeration getParameterNames(), returneaza  numele tuturor parametrilor continute in pagina

Exemplu:
void doGet (HttpServletRequest req, HttpServletResponse res){
    String nume, valoare;
    String[] params = req.getParameterNames();
    while (params.hasMoreElements()) {
         nume   = (String) params.nextElement();
         valoare = req.getParameter(nume);
         //Prelucrarea parametrului
     }
      //
}
 String getParameter( String nume ), returneaza valoarea parametrului cu numele specificat
        Exemplu:
<INPUT NAME="Nume" SIZE=30></INPUT>, defineste un camp text care permite introducerea
unui text de lungime maxima 30. Obtinerea valorii introduse in acest camp text se poate obtine prin
req.getParameter("Nume").
        .
 String [] getParameterValues( String  nume), daca parametrul poate avea mai multe valori
 BufferedReader getReader(), pentru a obtine date de tip text de la client
 ServletInputStream getInputStream(), pentru a obtine date binare de la client
Observatie:
In prelucrarea unei cereri nu se poate utiliza concomitent metodele de tip getParameter si metodele getReader si
getInputStream.
 
Trimiterea raspunsului
          Un obiect HttpServletResponse pune la dispozitie doua cai pentru trimiterea datelor catre client:
 
 Writer getWriter()
 ServletOutputStream getOutputStream()
Odata cu inchiderea fluxului de date se trimit datele scrise in flux clientului.
Inainte de a scrie date in fluxul de iesire trebuie setat tipul de date pentru trimitere prin metoda
setContentType(String).
Exemplu:
    res.setContentType("text/html");
Ciclul de viata al unui servlet
 
Servleturi de baza
        Java Webserver este implementat cu ajutorul  servleturilor  de baza. In primul exemplu fara sa stim am
utilizat deja servletul de baza invoker la incaracarea servletului PhraseServlet. Un alt servlet de baza este
ssinclude. Acest tip de servlet se utilizeaza pentru pagini HTML cu includeri pe partea serverului (.shtml).
        Urmatorul exemplu servlet primeste intrarea dintr-o pagina HTML care contine si un formular si utlizeaza
aceste date pentru generarea raspunsului. Raspunsul va fi tot o pagina  HTML.
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class FormServlet extends HttpServlet
{
    public void doPost(HttpServletRequest req, HttpServletResponse res)
        throws ServletException
    {
        PrintWriter out = null;
        try {
            res.setContentType("text/html");
            out = res.getWriter();
            out.println(
                  "<HTML><HEAD><TITLE>FormServlet output</TITLE></HEAD>");
            out.println("<BODY BGCOLOR=#FFFFFF><P>");
            // Se prelucreaza parametrul starea emotionala
            String [] emot = req.getParameterValues("emotion");
            if (emot != null && emot[0].length() != 0) {
                out.println("Astazi va simtiti " + emot[0] + ".");
            } else {
                out.println("N-ati scris nimic. Va simtiti bine?");
            }
            out.println("<BR><BR>");
            // Se verifica care dintre zile
            // a fost bifat in formular
            String [] days = req.getParameterValues("days");
            if (days != null) {
                out.println("De obicei va simtiti bine in urmatoarele
                             zile:<BR><UL>");
                for (int i = 0 ; i < days.length; i++) {
                     out.println("<LI>" + days[i] + "</LI>");
                }
                out.println("</UL>");
            } else {
                out.println("N-ati ales nici o zi. Sunteti nefericita?");
            }
        } catch(Exception e) {
            e.printStackTrace();
            out.println(e);
        }
        out.println("</BODY></HTML>");
        out.flush();
        out.close();
    }
}

Pentru prelucrarea parametrilor primiti de la client se utilizeaza metodele: getParameterNames() si


getParameterValues( String ). A doua metoda returneaza un tablou de tip String astfel putand trata parametrii
cu o singura valoare returnata cat si parametrii cu mai multe valori returnate
Formularul pe care prelucreaza servletul arata in modul urmator:
Exemplul precedent trebuie facut public in Java Webserver, iar dupa aceea vom scrie un mic fisier HTML care
sa contina formularul si la apasarea butonului sa trimita rezultatele din formular servletului.
<HEAD>
<TITLE>sURVEY</TITLE>
</HEAD>
<BODY>
<FORM method=POST ACTION="/servlet/FormServlet" >
<BR><BR>Cum va simtiti astazi?<BR>
<BR><input type=text name=emotion>
<BR><BR>In care dintre urmatoarele zile sunteti fericit?<BR>
<BR>Luni<INPUT TYPE=checkbox NAME=days VALUE=Luni>
<BR>Marti<INPUT TYPE=checkbox NAME=days VALUE=Marti>
<BR>Miercuri<INPUT TYPE=checkbox NAME=days VALUE=Miercuri>
<BR>Joi<INPUT TYPE=checkbox NAME=days VALUE=Joi>
<BR>Vineri<INPUT TYPE=checkbox NAME=days VALUE=Vineri>
<BR>Sambata<INPUT TYPE=checkbox NAME=days VALUE=Sambata>
<BR>Duminica<INPUT TYPE=checkbox NAME=days VALUE=Duminica>
<BR><BR><INPUT TYPE=submit><INPUT TYPE=reset>
</FORM>
</BODY>
</HTML>

Apleturi
1. Crearea apleturilor si executia acestora
2. Structura clasei Applet. Tag-ul htmlAPPLET
3. Comunicarea intre aplet si cadrul de executie al acestuia
4. Mecanismul desecuritate Java referitor la apleturi
5. Lucrul cu imagini si sunete
1. Creare si executie
            Un apleteste un program mic care nu se executa sinestatator ci este nevoie de o altaaplicatie pentru a-l
executa. In general apleturile sunt executatede catrenavigatoare. Clasa Applet este definita in pachetul
java.applet impreuna cualte  interfete necesare pentru a realiza comunicatia intre aplet simediul sau de executie.
Un aplet este de fapt o fereastra cu o serie de metodeutilizate de catre contextul apletului pentru a initializa,a
starta respectiva opri apletul.
Contextul(cadrul) apletului este o aplicatie responsabila pentru a incarca si a rula apletul. De exemplu un
navigator poate fi un cadru de executie pentruun aplet.  Ierarhia de clase din care face parte clasaApplet este
redatain figura urmatoare. Totodata pe figura apare si clasa pentru primul nostruaplet, HelloWeb:
 
Pasii necesari pentru crearea unui aplet:
1. Editarea codului sursa
//Sursa: HelloWeb.java
public class HelloWeb extends java.applet.Applet{
    public void paint( java.awt.Graphics gc ){
        gc.drawString("HelloWeb!", 125, 95 );
    }
}
2. Compilarea codului sursa
javac HelloWeb.java
Compilarea produce fisierul HelloWeb.class. Pentru a-l executa avem nevoie de un fisier HTML care sa
contina tagul APPLET cu valoareaHelloWeb.class.
3. Executia apletului
 
3.1 Se creaza fisierul HTML HelloWeb.html
<html>
<body>
    <applet code=HelloWeb width=300 height=200></applet>
</body?
</html>
3.2 Se incarca intr-un navigator fisierul HelloWeb.html
2. Structura claseiApplet
void   destroy()
-Se apeleaza de catre programul care executa apletul pentru a distruge toate resursele alocate pentru executia
acestuia.
AppletContext getAppletContext()
-Returneaza o referinta la cadrul de executie, dandu-i posibilitatea apletului de a interoga si a afecta cadrul de
executie
String    getAppletInfo()
-Returneaza informatii despre aplet
URL    getCodeBase()
-Returneaza adresa la care se gaseste codul apletului (fisierul .class)
URL  getDocumentBase()
-Returneaza adresa la care se gaseste fisierul HTML in care este inclus codul apletului
String  getParameter(String name)
-Returneaza valoarea parametrului cu numele name
void  init()
-Navigatorul apeleaza imediat dupa incarcarea codului apletului. Deobicei in aceasta metoda se adauga
componentele grafice pe suprafata apletului.
boolean isActive()
-Determina daca apletul este sau nu activ
void  resize(Dimension d)
-Cere de la navigator redimensionarea suprafetei apletului
void  resize(int width, int height)
-Cere de la navigator redimensionarea suprafetei apletului
void  showStatus(String msg)
-Cere de la navigator afisarea unui text in bara de stare
void  start()
-Este apelat de catre navigator imediat dupa init() pentru startarea apletului, respectiv ori de cate ori se revine la
pagina in navigator.Metoda este folosita si pentru pornirea firului de executie propriu apletului.
void  stop()
-Este apelat de catre browser pentru oprirea executiei apletului. Se apeleaza ori de cate ori se trece la o alta
pagina HTML in navigator sause iconifica fereastra navigatorului.
Pe langa atributele CODE, WIDTH si HEIGHT tag-ul APPLET poate sacontina o serie de alte atribute:
< APPLET
    [ CODEBASE = codebaseURL ]
    CODE = appletFile
    [ ARCHIVE = archivesList ]
    [ ALT = alternateText         ]
    [ NAME = appletInstanceName ]
    WIDTH = pixels
    HEIGHT = pixels
    [ ALIGN = alignment ]
    [ VSPACE = pixels ]
    [ HSPACE =pixels ]
>
[<PARAM NAME = appletParameter1 VALUE = value >]
[<PARAM NAME = appletParameter1 VALUE = value >]
...
</APPLET>
CODEBASE: permite includerea unui aplet aflat intr-un alt catalog decatcel in care se afla fisierul HTML
respectiv a unui aplet aflat pe un althost
ARCHIVE: Permite utilizrea fisierelor comprimate. Inainte de executia apletului, navigatorul aduce codul
apletului. Daca pe parcursul executiei apletului este nevoie si de alte clase de pe server, atunci se vor incarca si
acestea astfel incetinind executia apletului. Pentru a evita acest lucru se poate utiliza un singur fisier comprimat
care sa contina toate clasele de care este nevoie. In acest caz inaintea incarcarii apletului JVM aduce arhiva si
numai dupa aceea se trece la executia programului. Pentru arhivarea claselor se poate utiliza utilitarul jar
ALT: Prin acest atribut se poate specifica un text pe care browserul sa-l afiseze in cazul in care navigatorul nu
este compatibil Java
NAME: Prin acest atribut se poate atribui un nume apletului
ALIGN, VSPACE, HSPACE: Sunt atribute pentru dispunerea apletului inpagina HTML
PARAM: Se utilizeaza pentru transmiterea argumentelor apletului. Sunt similare cu argumentele liniei de
comanda.
Exemple:
<PARAM NAME=nume  VALUE=valoare>
<PARAM NAME="dimensiune" VALUE="10">
 
3. Comunicarea intre aplet si cadrul de executie al acestuia
Apletul comunica cu browserul prin:
-parametrii apletului:
    definirea acestora se face  prin tagul HTML <PARAM  NAME="parametru"   VALUE="valoare">
    citirea de catre aplet se face prin metoda getParameter(String name )
-transmiterea unor mesaje browserului
Aici avem doua categorii de mesaje, cele definite in clasa Applet si celedefinite in interfata AppletContext
 Prin metoda showStatus( String status), cere de la browser afisareaunui mesaj in fereastra de stare
a browserului.
           Exemplu: showStatus("Un mesaj in bara de stare");
 Metoda getCodeBase()returneaza locatia la care segaseste apletul, iar metoda
getDocumentBase()returneaza adresa paginiiin care se afla apletul.
 Metoda showDocument( URL url ) cere browserului incarcarea unei noi pagini in pagina curenta, iar
metoda showDocument( URL url,Stringname ) specifica si cadrul de afisaj. Parametrul name poate
aveaunadin urmatoarele valori:

          "self": afisaeaza documentul in frame-ul curent


          "_parent": afiseza documentul in frame-ul parinte
          "_top": afiseaza documentul in frame-ul cel mai curent
            "_blank": afiseaza documentul intr-o fereastra noua
     Navigatorul poate sa ignore atat showStatus() cat si showDocument().
Exemple pentru apelul metodei showDocument( URLurl ):
getAppletContext().showDocument("http://www.uttgm.ro") ;
getAppletContext().showDocument(getCodeBase(),"Demo.html");
getAppletContext().showDocument(getDocumentBase(),"AltDemo.html");
Exemple pentru apelul metodei showDocument( URL url , name):
              getAppletContext().showDocument("http://www.uttgm.ro","self") ;
              getAppletContext().showDocument("http://www.uttgm.ro","_blank") ;
 
Primul exemplu afiseaza o pagina HTML specificat printr-un URL absolut, al doilea afiseaza un document
HTML aflat in acelasi catalog cu sursa apletului, iar al treilea exemplu incearca afisarea unei pagini HTML
aflat in acelasi catalog cu pagina care contine apletul
Urmatoarele doua metode se utilizeaza pentru comunicarea intre apleturile unei aceleasi pagini HTML.
Comunicarea intre apleturi se realizeaza la fel ca si comunicarea intre oricare tipuri de obiecte. Dupa ce un aplet
obtine referinta unui alt aplet, poate sa trimita mesaje acelui aplet.
 Metoda getApplets() returneaza o colectie detip Enumeration care contine toate apleturile
disponibiledin pagina in carese afla apletul curent. Astfel apleturile din cadrul aceleasi pagini pot
comunica.
 Metoda getApplet( String name ) returneaza o referinta la apletul cu numele name.

Exemplu de comunicare intre apleturi:


Construim o pagina HTML in care avem doua apleturi. Primul aplet asteapta introducerea unui text intr-o
caseta text si in momentul apasarii butonului Send trimite celui de al doilea aplet din pagina.
 
 
Diagrama de clasa:
Sursa Java:
//Source file: Applet1.java
import java.applet.Applet;
import java.awt.event.ActionListener;
import java.awt.TextField;
import java.awt.Button;
import java.awt.event.ActionEvent;
public class Applet1 extends Applet implements ActionListener
{
  public TextField tf;
  public Applet2 a;
  public Button b;
  public Applet1()
  {
  }
  public void init()
  {
      tf = new TextField( 30 );
      add( tf );
      b = new Button("Send");
      add( b );
      b.addActionListener( this );
      a = (Applet2)getAppletContext().getApplet("APPLET2");
  }
  public void actionPerformed(ActionEvent arg0)
  {
      a.write( tf.getText());
  }
}
//Source file: Applet2.java
import java.applet.Applet;
import java.awt.Graphics;
public class Applet2 extends Applet
{
  private String string = null;
  public Applet2()
  {
  }
  public void paint(Graphics g)
  {
      if( string != null )
       g.drawString(string, 10, 10 );
  }
  public void write(String s)
  {
      string = s;
      repaint();
  }
}
4. Mecanismulde securitate Java referitor la apleturi
Ce nu poate sa faca un aplet:
1. nu poate citi sau scrie fisiere de pe masina pe care se executa
2. nu poate sterge sau modifica fisiere de pe masina pe care se executa
3. nu poate citi anumite proprietati sistem
4. nu poate executa programe pe masina pe care se executa
5. nu poate deschide o conexiune de retea decat cu masina depe care a fost adus
6. nu poate incarca biblioteci sau metode native
Fiecare browser are un SecurityManager care implementeaza politica de securitate, iar in momentul in care se
incearca violarea drepturilor, acest SecurityManager arunca o exceptie SecurityException
 5. Lucrul cu imagini si sunete
Lucrul cu fisiere de tip sunet:
Pentru a incarca un fisier de tip sunet, apletul apeleaza metoda getAudioClip( URL url), unde fisierul sunet
este specificat prin parametrul URL.Metoda returneaza o referinta la clasa AudiClip din pachetul java.applet.
AudioClip sound = getAudioClip( getDocumentBase(),"spacemusic.au");
Clasa AudiClip contine trei metode:
  play(): executa clipul audio
  loop(): executa clipul audio intr-un ciclu infinit
  stop(): opreste executia clipului audio
Lucrul cu imagini:
Incarcarea unui fisier imagine se face cu unul dintre urmatoarele metode:
public Image getImage(URL url);
Returneaza un obiect de tip imagine. Parametrul url trebuie sa reprezinte unul absolut.
public Image getImage(URL url, String name);
Returneaza un obiect de tip imagine specificat printr-un URL absolut si numele unei resurse realativ la url-ul
specificat.
Exemplu demonstrativ:
In exemplul urmator veti putea utiliza la urmatoarele:
 afisarea unui text in bara de stare: se introduce mesajul in textbox si se selecteaza din lista showStatus
 afisarea unui alt document HTML: se introduce URL-ul in textbox si se selecteaza din lista
showDocument
 desenarea pe o tabla de desen ( cea cu culoarea verde)si stergerea acestuia cu butonul Clear. Desenarea
se face cu mouse-ul sieste "freehand".
Desenarea se realizeaza tratand doua tipuri de evenimente cu mouse-ul, MousePressed si MouseDragged.
Apletul este format din doua clase, o clasa pentru apletul propriu-zis DemoApplet si o clasa pentru tabla de
desenare DrawPad . Clasa DemoApplet trateaza evenimentele de tip ActinEvent, adica cele cu butoanele, iar
DrawPad-ul, care este de fapt un container derivat din Canvas, trateaza evenimentele cu mouse-ul. In clasa
DrawPad  se definesc dou clase incuibate, dar publice, de tip adaptor, pentru tratarea evenimentelor.
Inregistrarea receptorilor se face in modul urmator:
dp = new DrawPad();
panel.add("Center", dp );
dp.addMouseMotionListener( dp.new MyMouseMotionAdapter() );
dp.addMouseListener( dp.new MyMouseAdapter() );
Sursa Java:
//Source file:DrawPad.java
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Image;
public class DrawPad extends Canvas
{
  private int xpos;
  private int ypos;
  private int oxpos;
  private int oypos;
  private Graphics drawGr;
  private Image drawImg;
  public DrawPad()
  {
      setBackground( java.awt.Color.green );
  }
 
  public void paint(Graphics g)
  {
      if( drawImg == null )
   {
       drawImg = createImage( getWidth(),getHeight() );
       drawGr = drawImg.getGraphics();
   }
   g.drawImage(drawImg, 0, 0 , null );
  }
  public void update(Graphics g)
  {
      paint( g );
  }
  public void clear()
  {
      drawGr.clearRect(0,0, getWidth(), getHeight());
   repaint();
  }
  public class MyMouseAdapter extends MouseAdapter
  {
     public MyMouseAdapter()
     {
     }
     /**
     @roseuid 3A5C0B1D02A8
     */
     public void mousePressed(MouseEvent e)
     {
      oxpos= e.getX();
      oypos= e.getY();
     }
  }
  public class MyMouseMotionAdapter extends MouseMotionAdapter
  {
     public MyMouseMotionAdapter()
     {
     }
     public void mouseDragged(MouseEvent e)
     {
       xpos = e.getX();
       ypos = e.getY();
       if( drawGr != null )
       {
           drawGr.drawLine(oxpos, oypos, oxpos=xpos, oypos=ypos);
           repaint();
       }
     }
  }
}
//Source file: DemoApplet.java
import java.applet.Applet;
import java.awt.event.ActionListener;
import java.awt.Choice;
import java.awt.TextField;
import java.awt.Button;
import java.awt.Panel;
import java.awt.event.ActionEvent;
public class DemoApplet extends Applet implements ActionListener
{
  public Choice operation;
  public TextField parameter;
  public Button doIt;
  public Button audioButton;
  public Panel panel;
  public Button clearButton;
  public Panel p;
  public DrawPad dp;
 
  public void init()
  {
   String oplist[]={"showStatus", "showDocument"};
   setLayout( new java.awt.GridLayout( 2, 1 ));
   p = new Panel ();
   add( p );
   p.setLayout( new java.awt.FlowLayout());
   operation = new Choice();
   for(int i=0; i<oplist.length; i++ )
       operation.add( oplist[ i ] );
      p.add( operation );
   parameter = new TextField(50);
   p.add( parameter);
   doIt = new Button("Do It");
   p.add( doIt );
   doIt.addActionListener( this );
   audioButton = new Button("Play Audio");
   p.add( audioButton );
   audioButton.addActionListener( this );
   panel = new Panel();
   panel.setLayout( new java.awt.BorderLayout());
   add( panel );
   dp = new DrawPad();
   panel.add("Center", dp );
   dp.addMouseMotionListener( dp.new MyMouseMotionAdapter() );
   dp.addMouseListener( dp.new MyMouseAdapter() );
   clearButton = new Button("Clear");
   clearButton.addActionListener ( this );
   panel.add( "South",clearButton );
  }
  public void actionPerformed(ActionEvent arg0)
  {
   String command = arg0.getActionCommand();
   if( command.equalsIgnoreCase("Do It"))
   {
       String op  = operation.getSelectedItem();
       String par = parameter.getText();
       if( op == null )
           showStatus("Please select an operation and enter the proper parameter for
it");
          else
       {
           showStatus("Parameter: "+op+" "+"Option: "+par);
           if( op.equals("showStatus") )
               showStatus(parameter.getText());
           else
               if( op.equals("showDocument"))
               {
                   java.applet.AppletContext ctx = getAppletContext();
                   try{
                       ctx.showDocument( new java.net.URL(par));
                   }
                   catch( Exception e )
                   {
                       showStatus("The requested document doesn't exists");
                   }
               }
       }
   }
   else
       if( command.equalsIgnoreCase("Play Audio"))
       {
           java.applet.AudioClip sound =
getAudioClip( getDocumentBase(),"spacemusic.au");
           sound.play();
       }
       else
           if( command.equalsIgnoreCase("Clear") )
               dp.clear();
  }
}

Clase de baza
 
 java.lang.String
 java.lang.StringBuffer
 java.lang.StringTokenizer
 java.lang.Math
 Clase pentru tipuri primitive
 java.util.Random
 java.util.LinkedList
 java.util.Vector
 java.util.Enumeration
 java.util.Hashtable
 java.util.Properties
 
java.lang.String

Stringurile Java nu necesita un caracter terminator cum este  in limbajul C standard. Stringurile Java sunt
obiecte reale, caracterele sirului nu pot fi accesati direct doar cu ajutorul metodelor clasei, iar lungimea
stringului se poate obtine cu metoda length(). Operatorul + se utilizeaza pentru concatenare. Exemple:
String sir = "A fi sau a nu fi";
int len = sir.length();
String nume ="Pop"+ " Ioan"; sau String nume ="Pop".concat(" Ioan");
char primul = sir.charAt( 0 );// returneaza 'A'
Stringul poate fi construit si dintr-un array de caractere:
char [] data ={'u','n',' ','s','i','r'};
String datasir=new String( data );
Metode de conversie:
Cu metoda statica valueOf( ) putem obtine reprezentarea string al unui anumit obiect. Exemple de utilizare:
String intreg = String.valueOf( 100 );
String flotant= String.valueOf( 2.34f );
String logic  = String.valuOf( false );
Object o = null; String obiect =String.valueOf( o );
Cu metodele toUpperCase() respectiv toLowerCase() putem face conversia de la minuscule la majuscule si
invers.
Metode de comparare:
int compareTo( String ),  compara obiectul String cu parametrul primit si returneaza  o valoare  intreaga, care
este 0 in caz de egalitate, iar altfel pozitiv sau negativ, exact  ca functia strcmp() sin C standard.
boolean eguals( String )
boolean egualsIgnoreCase( String ), ambele metode returneaza true in caz de egalitate, iar a doua metoda nu
face distinctie intre majuscule si munuscule,
Metode de cautare
int indexOf(  char ),  returneaza prima pozitie in care apare caracterul primit ca si parametru:
int indexOf( String ), cauta  un subsir in string.
String sir="A fi sau a nu fi"; int primapozitie = sir.indexOf( "fi"); // returneaza 2
(indexarea incepe de la 0 )
int lastIndexOf( char ) sau int lastIndexOf( String ) returneaza pozitia ultimei aparitii al unui caracter sau al
unui sir de caractere.
 
java.lang.StringBuffer
Aceasta clasa reprezinta un buffer de caractere. Permite sa cream un sir care creste dinamic. Exemplu:
StringBuffer buffer = new StringBuffer();
buffer.append("Sirul ");
buffer.append("nostru ");
buffer.append("creste ");
buffer.append("dinamic.");
Pentru a efectua operatii cu un StringBuffer acesta trebuie convertit la String prin metoda toString(). Afisarea
unui StringBuffer se va face la fel ca al unui Strin obisnuit deoarece System.out.println() apeleaza automat
metoda toString() pentru obiecte diferite de tipul String.
java.lang.StringTokenizer
De multe ori trebuie sa prelucram elementele unui sir de caractere delimitate cu un caracter delimitator special.
Exemplu:
String text ="A fi sau a nu fi";
StringTokenizer st = new StringTokenizer( text );
while( st.hasMoreTokens() ){
    String cuvant = st.nextToken();
    System.out.println( cuvant );
}
Clasa StringTokenizer implementeaza interfata java.util.Enumeration, deci am putea utiliza metodele
hasMoreElements() respectiv nextElement(), dar este mai comod utilizarea metodeui nextToken() deoarece
aceasta returneaz String pe cand nextElement() returneaza Object.  Clasa StringTokenizer poseda si un
constructor cu un al doilea parametru de tip String prin care putem specifica sirul delimitator. Exemplu:
String text = "unu:doi:trei:patru:cinci";
StringTokenizer st = new StringTokenizer( text,":");
 
java.lang.Math
Clasa reprezinta biblioteca matematica a limbajului Java. Toate metodele clasei sunt metode statice si nu se pot
crea instante. Asemenea clase degenerate se utilizeaza pentru colectii de functii. Clasa contine metode pentru
functii trigonometrice, radacina, exponential, logaritm, totunjire, trunchiere respectiv si un generator de numere
aleatoare
 
Clase pentru tipuri primitive
 
Tip primitiv Clasa
void java.lang.Void
boolean java.lang.Boolean
char java.lang.Char
byte java.lang.Byte
short java.lang.Short
int java.lang.Integer
long java.lang.Long
float java.lang.Float
double java.lang.Double
Fiecare obiect incapsuleaza o singura data de tipul primitiv atasat si permite conversia acestuia la alte tipuri de
date.
 
java.util.Random

Se utilizeaza pentru generarea numerelor aleatoare. Un generator de numere aleatoare se creaza prin constructia
Random rnd = new Random(); sau Random rnd = new Random( seed ). Primul constructor utilizeaza timpul
curent,  convertit la un numar long pentru seed.  Dupa crearea unui generator de numere aleatoare putem utiliza
metodele specifice clasei pentru obtinerea valorilor aleatoare.
 protected
double    nextDouble()  :  returneaza o valoare double din intervalul 0.0 si 1.0.
float        nextFloat()      :  returneaza o valoare float din intervalul 0.0 si 1.0.
int           nextInt()           :  returneaza o valoare intreaga reprezentata pe 4 octeti
int             nextInt(int n) :  returneaza o valoare aleatoare intreaga din intervalul [0, n)
long           nextLong()   :  returneaza o valoare long reprezentata pe 8 octeti
 
java.util.LinkedList si java.util.ListIterator
Este  o clasa care implementeaza o lista inlantuita. Defineste toate metodele necesare pentru operarea cu aceasta
structura de date. Astfel avem la dispozitie urmatoarele metode:
 void add(int index, Object element) : insereaza un element in pozitia indicata
 boolean add(Object o) : adauga elementul la sfarsitul listei
 boolean addAll(Collection c) : adauga toate elementele colectiei la sfarsitul listei
 boolean addAll(int index, Collection c) : insereaza elementele colectiei in pozitia indicata de al doilea
parametru
 void addFirst(Object o) :  insereaza elementul la inceputul listei
 void addLast(Object o) : adauga elementul la sfarsitul listei
 void clear() : sterge toate elementele listei
 Object clone() : returneaza o copie (superficiala) a listei
 boolean contains(Object o) : cauta elementul specificat in lista, in caz de succes returneaza true
 Object get(int index) : returneaza elementul de pe pozitia specificata
 Object getFirst() : returneaza primul element din lista
 Object getLast() : returneaza ultimul element din lista
 int indexOf(Object o) : returneaza indicele elementului specificat, altfel -1
 int lastIndexOf(Object o) : returneaza indicele ultimei aparitii al elementului specificat, -1 altfel
 ListIterator listIterator(int index) : returneaza un iterator cu ajutorul caruia putem parcurge lista in ordinea
dorita
 Object remove(int index) : sterge elementul de pe pozitia specificata si il returneaza
 boolean remove(Object o) : sterge prima aparitie a elementului specificat
 Object removeFirst() : returneaza primul element din lista stergandu-l
 Object removeLast() : returneaza ultimul element din lista stergandu-l
 Object set(int index, Object element) : inlocuieste elementul de pe pozitia specificata cu elementul specificat
 int size() : returneaza numarul elementelor listei
 Object[] toArray() : returneaza un array care contine elementele listei
Pentru parcurgerea acestei structuri se utilizeaza metodele interfetei java.util.ListIterator
boolean  hasNext() : verifica daca colectia mai are elemente neparcurse in directia de la inceput spre sfarsit
boolean  hasPrevious() : verifica daca colectia mai are elemente neparcurse in directia de la sfarsit spre inceput

Object next() : returneaza urmatorul element din lista


int nextIndex() : returneaza indicele elementului la care s-ar ajunge cu inca un next()
Object previous() : returneaza elementul precedent din lista
int previousIndex() : returneaza indicele elementului la care s-ar ajunge cu inca un previous()
Un exemplu de utilizare:
import java.util.*;
public class MyList{
    public static void main( String args[] )
    {
         String strings[] = {"0","1","2","3","4","5","6","7","8","9"};
         // Crearea unei liste simplu inlantuite
         LinkedList l = new LinkedList();
         for( int i= 0; i<strings.length; i++ )
             l.add( strings[ i ] );
             // Parcurgerea listei de la inceput spre sfarsit
             System.out.print("\nLista: ");
             ListIterator it= l.listIterator();
             while ( it.hasNext() )
                 System.out.print( it.next()+" " );
             System.out.print("\nLista: ");
             // Parcurgerea listei de la sfarsit spre inceput
             while( it.hasPrevious() )
                 System.out.print( it.previous()+" " );
             System.out.println();
    }
}
 
 
java.util.Vector
Un vector este o structura de date dinamica in Java. Se pot adauga elemente in orice pozitie, se pot sterge
elemente si in plus este Thread-safe, acest lucru insemnand ca se sincronizeaza accesul la structura.  Vectorul
lucreaza cu elemente de tip Object, acesta insemnand ca permite stocarea oricaror tipuri de elemente.
Exemplu de utilizare:
Vector v = new Vector();
String  str[] ={"Unu","Doi","Trei","Patru","Cinci"};
for( int i=0 ; i<str.length; i++ )
    v.addElement( str[ i ] );
Metoda addElement( Object ) adauga un element la sfarsitul vectorului.
Pentru obtinerea elementelor putem utiliza metodele elementAt( int pozitie ), firstElement() si lastElement(),
toate cele trei metode returnand tipul Object, care se converteste explicit la tipul dorit.
Elementele vectorului se sterg cu metodele de tip . De exemplu removeElementAt (int index ) sterge un element
de pe pozitia specificata, removeAllElements() sterge toate elementele, iar removeElement( Object ) sterge
elementul specificat din vector.
Metoda size() returneaza numarul elementelor.
 
java.util.Enumeration
Aceasta interfata se poate utiliza pentru orice multime de date pentru a obtine secvential elementele multimii.
Orice clasa care implementeaza aceasta interfata defineste doua metode nextElement() pentru a obtine
urmatorul element din lista, respectiv hasMoreElements() pentru a verifica daca multimea mai are elemente
neparcurse.
Exemplu de utilizare (pentru afisarea vectorului creat la capitolul java.util.Vector ):
Enumeration e = v.elements();
while( e.hasMoreElements())
{
    String s = (String) e.nextElement();
    System.out.println( s );
}
 
 
java.util.Hashtable

Un  Hashtable este un dictionar in care fiecare element are o cheie asociata si se utilizeaza pentru baze de date
mici. Cand se adauga un element la un hashtable se asocieaza o cheie acestuia, iar cand dorim sa obtinem o
valoare putem obtine pe baza cheii asociate. Clasa Hashtable opereaza cu elemente de tip Object. Elementele se
adauga cu metoda put( Object cheie, Object element), iar se scot cu metoda get( Object cheie), iar stergerea  se
face tot pebaza cheii remove( Object cheie ). Putem verifica existenta unei chei cu metoda containsKey( Object
cheie ). Analog cu clasa Vector si aici lucram cu o multime de elemente. Metoda keys() returneaza un tip
Enumeration reprezentand multimea cheilor, iar metoda elements() returneaza tot un tip Enumeration, de
aceasta data reprezentand multimea elementelor.
import java.util.*;
public class hash
{
    public static void main( String args[] )
    {
         Hashtable date = new Hashtable();
         date.put("craciuni", new GregorianCalendar( 2001, Calendar.DECEMBER, 25 ));
         date.put("tata", new GregorianCalendar( 1942, Calendar.JULY, 20 ));
         date.put("mama", new GregorianCalendar( 1945, Calendar.APRIL, 10 ));
         Enumeration e1 = date.keys();
         while( e1.hasMoreElements() )
         {
             String cheie = ( String ) e1.nextElement();
             System.out.println( cheie );
         }
         Enumeration e2 = date.elements();
         while( e2.hasMoreElements() )
         {
             GregorianCalendar val = ( GregorianCalendar ) e2.nextElement();
             System.out.println( val.get( val.YEAR ) );
         }
    }
}
 
 
java.util.Properties
Clasa Properties este un tabel hash pentru siruri de caractere. Java utilizeaza proprietati pentru a inlocui
variabilele mediului de executie utilizate in alte medii de programare. Pentru configurarea unei aplicatii se
poate utiliza un tablou de proprietati.Obiectul Properties poate incarca proprietatile dintr-un fisier, respectiv
poate salva continutul intr-un fisier, utilizand fluxuri. Tabloul Properties are forma urmatoare:
 
Cheie  Valoare
Titlu Aplicatie 
Latime 400
Inaltime 300
De fapt este format din perechi (cheie, valoare). Tabelul proprietatilor se poate crea direct sau poate fi incarcat
dintr-un fisier.
Varianta 1. crearea directa a listei de proprietati
Properties props = new Properties();
props.put(Titlu,"Aplicatie");
props.put(Latime,"400");
props.put(Inaltime,"300");
Varianta 2. incarcarea dintr-un fisier
import java.io.*;
 
class Property1{
    public static void main( String args[] )
    {
         FileInputStream fin = null;
         java.util.Properties props = null;
         try{
             fin = new FileInputStream("props.dat");
             props = new java.util.Properties();
             props.load( fin );
             props.list( System.out );
         }
         catch( Exception e )
         {
             System.out.println( e );
         }
    }
}
 
Principalele metode ale clasei Properties
 
 
Metoda Semnificatie
String getProperty(String key)
 returneaza
valoarea cheii
 returneaza
valoarea cheii, in
String getProperty(String key, String defaultValue)
caz ca nu exista
returneaza valoarea
celui de-al doilea
parametru
 afiseaza lista
void list(PrintStream out) 
proprietatilor in
fluxul de iesire de
tip PrintStream
 afiseaza lista
void list(PrintWriter out) 
proprietatilor in
fluxul de iesire de
tip PrintWriter
 citeste lista
void load(InputStream inStream)  proprietatilor dintr-
un flux de intrare
Enumeration propertyNames()   returneaza lista
cheilor
 adauga o pereche
Object setProperty(String key, String value)  (cheie, valoare) la
lista proprietatilor
 salveaza lista
proprietatilor intr-
void store(OutputStream out, String header) 
un flux de iesire,
prin header
specificand capul
tabelului
Clasa System are o metoda statica getProperty() care returneaza  un obiect Properties continand tabloul
proprietatilor specifice sistemului.  Exemplul urmator afiseaza acest tablou:
public class TestProperty
{
   public static void main(String[] args)
   {
    java.util.Properties p = System.getProperties();
    java.util.Enumeration list = p.propertyNames();
     while( list.hasMoreElements() )
     {
          String e = ( String )list.nextElement();
          System.out.println( e +" | "+p.getProperty(e) );
     }
   }
}
Rezultatul executand pe un sistem Linux este:
java.runtime.name | Java(TM) 2 Runtime Environment, Standard Edition
sun.boot.library.path | /usr/java/jdk1.3/jre/lib/i386
java.vm.version | 1.3.0
java.vm.vendor | Sun Microsystems Inc.
java.vendor.url | http://java.sun.com/
path.separator | :
java.vm.name | Java HotSpot(TM) Client VM
file.encoding.pkg | sun.io
java.vm.specification.name | Java Virtual Machine Specification
user.dir | /dos/Manyi/Rose/system
java.runtime.version | 1.3.0
java.awt.graphicsenv | sun.awt.X11GraphicsEnvironment
os.arch | i386
java.io.tmpdir | /tmp
line.separator |
java.vm.specification.vendor | Sun Microsystems Inc.
java.awt.fonts |
os.name | Linux
java.library.path |
/usr/java/jdk1.3/jre/lib/i386:/usr/java/jdk1.3/jre/lib/i386/native_threads/:/usr/java/jdk1.3/jre/lib/i386/client:/usr/j
ava/jdk1.3/jre/../lib/i386
java.specification.name | Java Platform API Specification
java.class.version | 47.0
os.version | 2.2.16-22
user.home | /root
user.timezone |
java.awt.printerjob | sun.awt.motif.PSPrinterJob
file.encoding | ISO-8859-1
java.specification.version | 1.3
user.name | root
java.class.path | .:/usr/java/jdk1.3/lib
java.vm.specification.version | 1.0
java.home | /usr/java/jdk1.3/jre
user.language | en
java.specification.vendor | Sun Microsystems Inc.
java.vm.info | mixed mode
java.version | 1.3.0
java.ext.dirs | /usr/java/jdk1.3/jre/lib/ext
sun.boot.class.path |
/usr/java/jdk1.3/jre/lib/rt.jar:/usr/java/jdk1.3/jre/lib/i18n.jar:/usr/java/jdk1.3/jre/lib/sunrsasign.jar:/usr/java/jdk1.
3/jre/classes
java.vendor | Sun Microsystems Inc.
file.separator | /
java.vendor.url.bug | http://java.sun.com/cgi-bin/bugreport.cgi
sun.cpu.endian | little
sun.io.unicode.encoding | UnicodeLittle
user.region | US
sun.cpu.isalist |

Serializare
Utilizand fluxurile putem scrie aplicatii care salveaza si incarca datele in fisiere. Java permite si un mecanism
mai avansat si anume serializarea obiectelor. In forma cea mai simpla serializarea obiectelor inseamna salvarea
si restaurarea starii obiectelor. Obiectele oricarei clase care implementeaza interfata Serializable, pot fi salvate 
intr-un stream(fluxde date) si restaurate  din acesta.  Pachetul java.io continedoua clase
specialeObjectInputStream respectiv ObjectOutputStream pentru serializarea tipurilor primitive. Subclasele
claselor serializabile vor fi si ele serializabile. Mecanismul de serializare salveaza variabilele membri nestatice
si netransiente. Daca un obiect este serializat, atunciorice alt obiect care contine o referinta la obiectul serializat
va sisi el insusi serializat. Se poate serializa orice multime de obiecte interconectateintr-o structura de graf.
 
Utilizand serializarea putem salva obictele create pe o platforma si le putem transmite prin retea pe un alt
calculator care nu trebuie neaparat sa se ruleze pe aceasi platforma, totusi permite restauraea corecta a
obiectelor.

Pentru a intelege serializarea vom trata urmatoarele elemente Java:


1. interfata Serializable
2. interfata Externalizable
3. modificatorul transient
1. Interfata Serializable
public interface Serializable
Aceasta interfata nu declara nici metode, nici atribute, serveste doar pentru identificarea obiectelor serializabile.
Clasele care nu implementeaza aceasta interfata nu vor putea fi serializate sau deserializate. Operatia de
deserializare presupune ca clasa obievtului poseda si un constructor cu vizibilitate publica si fara argumente.
Atributele obiectului vor fi initializate dintr-un flux de date. In cazul serializarii unui obiect care apartine unui
graf de obiecte daca se ajunge la un obiect care nu implementeaza interfata Serializable, atunci se genereaza
exceptia NoSerializableException . Clasele care necesita o tratare speciala pe parcursul serializarii si a
deserializarii trebuie sa implementeze urmatoarele doua metode cu urmatoarele signaturi:
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;

Metoda writeObject()  este responsabila pentru salvarea starii obiectului, iar metoda readObject() are
responsabilitatea restaurarii starii obiectului. Salvarea starii, adica a atributelor intr-un flux ObjectOutputStream
se efectueaza cu metoda writeObject() respectiv cu metodele writeInt(), writeDouble() etc., metodele interfetei
DataOutput.
import java.util.*;
import java.io.*;
class AccountSer implements Serializable {
   private String username;
   private String password;

   public AccountSer( String username, String password ){


       System.out.println("Constructor Account( String, String )");
       this.username = username;
       this.password = password;
   }

   public AccountSer(){
       System.out.println("Default Constructor Account()");
      
   }

   public void readObject( ObjectInputStream in ) throws ClassNotFoundException


   {
       System.out.println("Method readObject, class Account");
       try{
           username = ( String )in.readObject();
           password = ( String )in.readObject();       
       }
       catch( IOException e ){
           System.out.println(e);
       }
   }

   public void writeObject( ObjectOutputStream out ) throws IOException


   {
       System.out.println("Method writeObject, class Account");
       out.writeObject( username );
       out.writeObject( password );
  
   }

   public String toString(){


       return username+password;
   }

   public static void main( String args[] )


   {
       System.out.println("Creating an object");
       AccountSer a = new AccountSer("MyName","MyPassword");
       try{
           System.out.println("Saving the object");
           ObjectOutputStream out = new ObjectOutputStream(
               new FileOutputStream("Account.ser"));
           out.writeObject( a );
           out.close();

           System.out.println("Restoring the object");


           ObjectInputStream in = new ObjectInputStream( new
FileInputStream("Account.ser"));
           Account b = (Account) in.readObject();
           in.close();
           System.out.println( b );           

       }
       catch( Exception e)
       {
           System.out.println( e );
       }
   }
}
2. Interfata Externalizable
Utilizand aceasta interfata avem posibilitatea de a controla cum se salveaza starea unui obiect. Adica putem sa
salvam doar valorile anumitor atribute. In exemplul precedent am salvat si atributul password, iar daca obiectul
este transmis prin retea si este interceptat de catre cineva, atunci oricine poate sa deserializeze, afland astfel
parola utilizatorului. Vom rescrie exemplul uttilizand doar salvarea atributului username.
Interfata Externalizable extinde interfata Serializable, adaugand doua metode:
void readExternal(ObjectInput in)
    Obiectul care implementeaza aceasta metoda poate s-o utilizeze pentru restaurarea starii obiectelor
cu  metodele readObject() pentru obiecte oarecare, siruri de caractere(String) si tablouri, iar metodele
din DataInput pentru tipuri de date primitive.          
 void writeExternal(ObjectOutput  out)
          Obiectul care implementeazaaceasta metoda poate s-o utilizeze pentru salvarea starii obiectelor
cu  metodele readObject() pentru obiecte oarecare, siruri de caractere(String)si tablouri, iar metodele
din DataInput pentru tipuri de date primitive.          

In exemplul urmator nu slavam atributul password, dar incercam sa restaram la deserializarea obiectului, astfel
obtinem valoarea null pentru acest atribut.
import java.util.*;
import java.io.*;

class Account implements Externalizable {


   private String username;
   private String password;

   public Account( String username, String password ){


       System.out.println("Constructor Account( String, String )");
       this.username = username;
       this.password = password;
   }

   public Account(){
       System.out.println("Default Constructor Account()");
      
   }

   public void readExternal( ObjectInput in ) throws ClassNotFoundException


   {
       System.out.println("Method readExternal, class Account");
       try{
           username = ( String )in.readObject();
           password = ( String )in.readObject();       
       }
       catch( IOException e ){
           System.out.println(e);
       }
   }

   public void writeExternal( ObjectOutput out ) throws IOException


   {
       System.out.println("Method writeExternal, class Account");
       out.writeObject( username );
      
  
   }

   public String toString(){


       return username+password;
   }

   public static void main( String args[] )


   {
       System.out.println("Creating an object");
       Account a = new Account("MyName","MyPassword");
       try{
           System.out.println("Saving the object");
           ObjectOutputStream out = new ObjectOutputStream(
               new FileOutputStream("Account.txt"));
           out.writeObject( a );
           out.close();

           System.out.println("Restoring the object");


           ObjectInputStream in = new ObjectInputStream( new
FileInputStream("Account.txt"));
           Account b = (Account) in.readObject();
           in.close();
           System.out.println( b );           

       }
       catch( Exception e)
       {
           System.out.println( e );
       }
   }
}
3. Modificatorul transient
Se utilizeaza pentru atribute care nu trebuie serializate. In exemplul precedent ar trebui sa declaram transient
atributul password.
  Un exemplu de program client-server care lucreaza cu obiecte
In exemplele de pana acum prin soclurile au fost utilizate pentru trimiterea unor date primitive. In scrierea si
citirea acestor date am utilizat fluxurile DataOutputStream respectiv DataInputStream. Daca dorim insa sa
trimitem obiecte intre aplicatii care se ruleaza pe diferite hosturiatunci va trebui sa utilizam mecanismul de
serializare. Fluxurile ObjectInputStream si ObjectOutputStream sunt utilizate ca fluxuri pentru obiecte
serializate. Inainte de scrierea obiectului cu metoda writeObject() obiectulva fi serializat, iar dupa citirea
obiectului cu metoda readObject(), obiectul se deserializeaza.
In exemplul urmator vom construi un server care rezolva cateva tipuri de cereri. Gama serviciilor oferite de
acest server se poate extinde prin adaugarea a noi clase la aplicatie.Pentru diferite tipuri de cereri seintroduce o
clasa de baza serializabila cu numele Cerere.
public class Cerere implements Serializable {
}
Aceasta clasa fiind serializabila toate clasele derivate din aceasta vorfi implicit serializabile. Vom deriva din
aceasta clasa o clasa CerereData .Clasa defineste o cerere pentru data calendaristica. Ori de cate ori serverul
primeste un asemenea obiect trimite ca si raspuns un obiect de tip java.util.Date. O alta clasa derivata din
aceasta esete CerereCalcul care va fi  superclasa tuturor claselor cereri de tip calcul. Aceasta clasa va avea o
metoda Objectexecute() pentru a executa calcului dorit. De fapt clasa CerereCalcul serveste ca clasa de baza si
de aceea la acest nivel nu efectueza niciun calcul, returneaza null. Clasele derivate vor redefini aceasta
metodacompletand cu efectuarea calculelor specifice acelor clase. Un exemplude clasa care efectueaza un
calcul va fi clasa CerereRidicareLaPutere.
Serverul va avea o arhitectura paralela, pentru fiecare client se creaza un fir de executie ( un obiect
DeservireClient ) si toate cererile suntrezolvate de catre acesta.
Diagrama de clasa a aplicatiei:
Sursele:
import java.io.Serializable;

public class Cerere implements Serializable 


{
}
public class CerereData extends Cerere 
{
}
public class CerereCalcul extends Cerere 
{

        public java.lang.Object execute() 


        {
                return null;
        }
}
public class CerereRidicareLaPatrat extends CerereCalcul 
{
        private int n;

        public CerereRidicareLaPatrat(int n) 


        {
                this.n = n;
        }
        public java.lang.Object execute() 
        {
                return new Integer( n* n );
        }
}
import java.io.IOException;

public class Server 


{

        public static void main(String[] args) throws IOException 


        {       
                if( args.length != 1 ){
                        System.out.println("Utilizare java Server <port>");
                        System.exit( 0 );
                }
                java.net.ServerSocket ss = new
java.net.ServerSocket( Integer.parseInt( args[ 0 ] )); 
                while( true )
                        new DeservireClient( ss.accept() ).start();
        }
}
import java.net.Socket;
import java.net.SocketException;

public class DeservireClient extends Thread 


{
        private Socket clientSocket;

        public DeservireClient(java.net.Socket clientsocket) throws SocketException 


        {
                this.clientSocket = clientsocket;
        }

        public void run() 


        {
                try{
                        java.io.ObjectInputStream in = new
java.io.ObjectInputStream( clientSocket.getInputStream());
                        java.io.ObjectOutputStream out = new
java.io.ObjectOutputStream( clientSocket.getOutputStream());
                        while( true ){
                                out.writeObject( executaCerere( in.readObject()));
                                out.flush();
                        }
                }
                // Clientul inchide conexiunea
                catch( java.io.EOFException e1 ){
                        try{
                                clientSocket.close();
                        }
                        catch( java.io.IOException e2 ){ System.out.println( e2 ); }
                }
                // Eroare I/O
                catch( java.io.IOException e2 ){ System.out.println( e2 ); }

                // Cerere necunoscuta


                catch( ClassNotFoundException e3 ){ System.out.println( e3 ); }
        }

        public java.lang.Object executaCerere(java.lang.Object cerere) 


        {
                if( cerere instanceof CerereData )
                        return new java.util.Date();
                else
                        if( cerere instanceof CerereCalcul )
                                return ((CerereCalcul) cerere).execute();
                        else
                                return null;

        }
}
public class Client 
{

        public static void main(String[] args) 


        {
                if( args.length != 2 )
                {
                        System.out.println("Utilizare java Client <numehost> <porthost>
");
                        System.exit( 0 );
                }
                try{
                        java.net.Socket socket = new java.net.Socket( args[ 0 ] ,
Integer.parseInt( args[ 1 ] ));
                        java.io.ObjectOutputStream out = new
java.io.ObjectOutputStream( socket.getOutputStream());
                        java.io.ObjectInputStream in = new
java.io.ObjectInputStream( socket.getInputStream());
                        // Se trimite prima cerere
                        out.writeObject( new CerereData() );
                        out.flush();
                        System.out.println( in.readObject());
                        // Se trimite a doua cerere
                        out.writeObject( new CerereRidicareLaPatrat( 5 ) );
                        out.flush();
                        System.out.println( in.readObject());

                        socket.close();
                }
                // Eroare I/O
                catch( java.io.IOException e ){
                        System.out.println( e );
                }
                // Tip raspuns necunoscut
                catch( ClassNotFoundException e1 ){
                        System.out.println( e1 );
                }

        }
}

Mecanismul de reflectie
1. Clasa Object
2. Clasa Class
3. Mecanismul de reflectie
 Securitate
 Accesul campurilor de date
 Accesul metodelor
 Accesul constructorilor

In acest capitol vom discuta de clasa Object, care este strabunica tuturor claselor si de clasa Class, care
reprezinta toate clasele Java in masina virtuala. Dupa aceea vom discuta despre un subiect mai general,
mecanismul de reflectie care ne permite inspectarea obiectelor.
 
Clasa Object

Fiecare clasa Java este derivata din clasa Object. Metodele acestet clase sunt impoertante deoarece ele sunt
mostenite de toate clasele Java si intre aceste metode Java sunt si o serie de metode publice. Dintre aceste
metode publice putem sa amintim metodele wait() si notify(), utilizate la sincronizarea firelor de executie.
Metoda toString() este utilia pentru conversia obiectelor la o valoare textuala, de aceea clasa PrintStream
foloseste aceasta metoda  pentru afisarea valorilor obiectelor. Exemplu:
try{
    ...
}
catch( Exception e )
{
    System.out.println( e );
}
In constructia de mai sus am afisat cu metoda println() un obiect de tip Exception. Acest lucru este posibil, din
cauza existentei metodei toString(). Prima data obiectul este convertit la un String, iar dupa ceea se afiseaza la
iesirea standard. Clasele derivate din Object redefinesc metoda toString(), fiecare convertind obiectul la o
valoare de tip String. Metoda equals() se utilizeaza pentru compararea obiectelor. in acest caz se compara
continutul obiectelor, adica tipul acestora si dupa aceea se compara valorile.
String nume1 ="Pop Ioan";
String nume2 = "Pop Ioan";
if( nume1.equals( nume2 ) ){
    System.out.println("Sirurile sunt egale");
}
Utilizand equals() se compara valori pe cand utilizand constructia if ( nume1 == nume2 ) se compara
referinte. In cazul referintelor egalitatea are loc numai daca cele doua referinta reprezinta acelasi obiect. In
exemplul de mai sus avem doua obiecte cu acealasi valoare deci comparand referintele vom obtine rezultatul
fals. Toate clasele care necesita compararea obiectelor trebuie sa redefineasca metoda equals pentru a permite
compararea obiectelor de tipul respectiv.  Un exemplu de redefinire:
class Student{
    private String nume;
    private  int varsta;
    public String getNume(){ return nume; }
    public int getVarsta(){ return varsta; }
    public boolean equals( Object arg ){
        if( (arg != null ) && ( arg instanceof Student) )
            if( nume.equals(arg.getNume()) &&  (varsta == arg.getVarsta()) )
                return true;
        return false;
    }
}
Metoda hashCode() returneaza un intreg care reprezinta hashcodul pentru obiectul respectiv. Un hashcode este
o valoare de control pentru un obiect, fiind o valoare aleatoare generata pe baza continutului obiectului. 
Hashcodul este diferit pentru instante  din aceasi clasa, dar cu continut diferit si trebuie sa fie acelasi pentru
obiecte cu acelasi continut. Este utilizat pentru stocarea obiectelor intr-un Hashtable Implementarea metodei
hashCode() din clasa Object ataseaza fiecarei instante o valoare unica.
public class hashcode{
 public static void main( String args[] )
 {
  String siruri[] = {"Un sir","Un alt sir","Un sir"};
  for(int i=0;i<siruri.length; i++)
   System.out.println( siruri[i].hashCode() );
 }
}
Obiectele utilizeaza metoda clone() pentru crearea copiilor. O copie este intotdeauna o noua instanta, diferit de
cel original. Java ne ofera un mecanism de copie bit cu bit al unui obiect, dar implicit aceasta capabilitate este
dezactivata. Daca dorim ca o clasa sa permita crearea  copiilor instantelor, clasa trebuie sa implementeze
interfata java.lang.Clonable. Daca utilizam metoda clone() pentru un obiect care nu respeca aceasta interfata,
metoda clone() genereaza CloneNotSupportedException.  Metoda clone()   este  o metoda protejata, daca dorim
s-o utilizam in orice context trebuie s-o  redefinim si s-o facem publica.
Exemple:
import java.util.Hashtable;
public class Colectie implements Cloneable{
    Hashtable htable = new Hashtable();
    public Object clone(){
        try{
            return super.clone();
        }
        catch( CloneNotSupportedException e ){
            throw new Error("Aici nu se ajunge niciodata");
        }
    }
}
Utilizare:
Colectie c1 = new Colectie();
Colectie c2 = ( Colectie ) c1.clone();
In exemplul precedent am creat o copie superficiala a obiectului c1 de tip Colectie. Rezultatul este exemplificat
pe imaginea urmatoare:

Daca dorim sa facem o copie adevarata, atunci trebuie sa rescriem metoda clone() si sa copiem  fiecare
variabila membru bit cu bit.
import java.util.Hashtable;
public class ColectieNoua implements Cloneable{
    Hashtable htable = new Hashtable();
    public Object clone(){
        try{
            ColectieNoua copie = new (ColectieNoua) super.clone();
            copie.htable = (Hashtable ) htable.clone();
            return copie;
        }
        catch( CloneNotSupportedException e ){
            throw new Error("Aici nu se ajunge niciodata");
        }
    }
}
Rezultatul copierii se vede in figura alaturata.

Clasa Class
Metoda getClass() a clasei Object returneaza o referinta de tip Class. Clasele din codul sursa Java sunt
reprezentate pe timpul executiei de instante ale clasei java.lang.Class.  Exista un obiect de tip Class pentru
fiecare clasa utilizata intr-o aplicatie. Acest obiect este responsabil pentru producerea instantelor din clasa
respectiva. Tot aceasta clasa Class serveste si pentru mecanismul de reflectie. Referinta Class asociata unui
obiect se poate obtine in doua moduri:
1. utilizand metoda getClass(0 a clasei Object
string sir = "Un sir";
Class c = sir.getClass();
2.  utilizand notatia nume_clasa.class care arata de fapt ca un membru static, dar de fapt este rezolvat de catre
compilator
Class c = String.class
Daca obtinem referinta de tip Class al unui obiect, putem sa cerem diferite lucruri de la aceasta referinta:
1. obtinerea numelui clasei  prin metoda getName()
String sir ="Un sir";
Class c = sir.getClass();
System.out.println( c.getName() );
2. Producerea unei noi instante prin metoda newInstance(). Aceasta metoda returneaza un tip Object care
trebuie convertit la tipul dorit
try{
    String unaltsir = ( String ) c.newInstance();
}
catch( InstantiationException e1 ){ ... }
catch( IllegalAccessException e2 ){ ... }
InstantiationException apare in cazul cand dorim sa cream instante dintr-o clasa abstracta sau dintr-o interfata.
IllegalAccessException apare cand  constructorul clasei nu este accesibil.
Cautarea si incarcarea unei clase dupa numele acestuia se face cu metoda statica forName(). Aceasta metoda
returneaza un obiect de tip Class pentru clasa indicata prin parametrul metodei.
try{
    Class oclasa = Class.forName("OClasa");
}
catch( ClassNotFoundException e ){ ... }
Daca nu se gaseste clasa indicata prin nume se genereaza exceptia ClassNotFound. Combinand metodele
prezentate putem  incarca clase dinamic, indicand nul\mele acestora si dupa aceea putem apela metodele acestor
clase. Exemplu:
interface Typewriter{
    void typeLine( String s );
}
class Printer implements Typewriter{
    ...
}
class MyApplication{
    ...
    String deviceName = "Printer";
    try{
        Class c = Class.forName( deviceName );
        Typewriter device = (Typewriter) c.newInstance();
        device.typeLine('Hello");
    }
    catch( Exception e ){...}
}
 
 
 
Reflectie

Acest mecanism ne este oferit de catre pachetul java.lang.reflect. Reflectia inseamna autoexaminare, adica
permite determinarea structurii clasei. In limitele managerului de securitate putem afla metodele clasei,
constructorii clasei si restul membrilor clasei. Cateodata putem sa modificam starea obiectlui prin apelul
metodelor specifice sau putem construi obiecte noi . Mecanismul de reflectie este utilizat de componentele Java
(Java beans) pentru determinarea  capabilitatilor obiectelor pe timpul executiei. Membrii clasei sunt atributele si
metodele. Metodele se impart la randul lor in doua categorii, constructori si metode obisnuite. Principalele clase
care definite in acest pachet sunt:
 java.lang.reflect.Field
 java.lang.reflect.Method
 java.lang.reflect.Constructor
Clasa Class ne ofere cate o pereche de metode pentru obtinerea atributelor, metodelor respectiv ai
contructorilor. Una dintre aceste metode permit accesul la metodele publice, incluzand si cele mostenite, iar
cealalalta metode permite accesul doar la metodele publice si nepublice declarate in cadrul clasei. De exemplu
getFields() va fi metoda care ne da lista tuturor campurilor de date publice, iar getDeclaredFields() returneaza
doar cele declarate in cadrul clasei. Tabelul urmator prezinta metodele cele mai importante pentru analiza unei
clase:
 
Metoda Descriere
Field[] getFields(); returneaza variabilele publice, incluzand si cele mostenite
Field getField( String name ); returneaza variabila publica specificata prin parametrul nume
Field[] getDeclaredFields(); returneaza variabilele clasei (publice+nepublice)
returneaza variabila declarata in cadrul clasei specificata prin
Field getDeclaredField( String name );
parametrul nume
Method [] getMethods(); returneaza metodele publice, incluzand si cele mostenite
Method getMethod( String name, Class[] returneaza metoda publica specificata prin parametrul nume si
argumentTypes ); tipul argumentelor
Method[] getDeclaredMethods( ); returneaza metodele clasei (publice+nepublice)
Method getDeclaredMethod( String name, returneaza metoda declarata in cadrul clasei specificata prin
Class[] argumentTypes ); parametrul nume si tipul argumentelor
Constructor[] getConstructors(); returneaza metodele publice, incluzand si cele mostenite
Constructor getConstructor( Class [] returneaza metoda publica specificata prin parametrul nume si
argumentTypes); tipul argumentelor
Constructor[] getDeclaredConstructors(); returneaza metodele clasei (publice+nepublice)
Constructor getDeclaredConstructor( Class[] returneaza metoda declarata in cadrul clasei specificata prin
argumentTypes);  parametrul nume si tipul argumentelor
Exemplul urmator va afisa metodele clasei java.util.Hashtable:
Method [] methods = Calendar.class.getMethods();
for( int i=0; i< methods.length;i++)
    System.out.println( methods[ i ] );
 
Securitate
Accesul la membrii clasei este controlat de catre managerul de securitate. In mod normal o aplicatie de
incredere poate accesa absolut tot ce este posibil prin API-ul reflectie, dar un aplet  care provine dintr-o sursa
nesigura va avea acces doar la membrii publici ai clasei.
Accesul campurilor de date
Clasa java.lang.reflection.Field este utilizat pentru accesul campurilor de date obisnuite si cele statice. Pentru
accesul acestora clasa contine  o serie de metode pentru a accesa diferite tipuri de campuri de date: getInt(),
setInt(), getBoolean(), setBoolean() etc. Normal ca metodele de tip setXXX()  si getXXX() functioneaza doar
pentru membrii publici ai clasei, altfel se genereaza exceptia IllegalAccessException.
class ContBancar{
    pblic int  depozit;
}
 
ContBancar cont;
try{
    Field fdepozit = ContBancar.class.getField("depozit");
     int depozit = fdepozit.getInt( cont );
    fdepozit.setInt( cont, 42 );
}
catch( NoSuchFieldException e 1){...}
catch( IllegalAccessException e2){...}
Accesul metodelor
Accesul metodelor se rezolva similar cu tehnica poinetrilor la functii in limbajul C standard. Clasa Method
invoke() se utilizeaza pentru apelul unei metod cu argumente specificate. Exemplul urmator va utiliza aceasta
facilitate prin apelul unei metode statice fara nici  un argument. Numele clasei si numele metodei se primesc de
la linia de comanda.
import java.lang.reflect.*;
public class ApelMetoda{
    public static void main( String args[] )
    {
        try{
            Class c = Class.forName( args[ 0 ] );
            Method m = c.getMethod( args[ 1 ], new Class[]{} );
            Object o = m.invoke( null, null );
            System.out.println("Apel metoda statica: "+args[ 1 ] + " din clasa: "+
args[ 0 ]+
               " fara argumente\nRezultate: "+ o );
        }
        catch( ClassNotFoundException e1 ){
            System.out.println( e1 );
        }
        catch( NoSuchMethodException e2 ){
            System.out.println( e2 );
        }
        catch( IllegalAccessException e3 ){
            System.out.println( e3 );
        }
        catch( InvocationTargetException e4 ){
            System.out.println( e4 );
        }
    }
}
Un exemplu de apel: java ApelMetoda java.lang.System currentTimeMillis
In exemplul precedent prima data s-a apelat metoda forName() cu numele clasei. Metoda returneaza o referinta
la clasa corespunzatoare. Cu ajutorul acestei referinte putem apela metoda getMethod(). Aceasta metoda
primeste ca prim argument numele metodei, iar al doilea argument este un array de tip Class specificand
signatura metodei.  In exemplul de mai sus am apelat o metoda fara parametri, deci al doilea argument este un
array vid. Daca am fi dorit sa apelam o metoda cu argumente, acest array ar fi trebuit initializat cu un array cu
clasele argumentelor. Pentru tipuri primitive se utilizeaza o constructie Clasa.TYPE. De exemplu pentru tipul
primitiv int se va utiliza Integer.TYPE. Fiecare tip primitiv are o clasa corespunzatoare in pachetul java.lang
(Void-void, Integer-int, Boolean-boolean, Long-long, Double-double, Float-float, Character-char, Byte-byte
etc.) si ceste clase au un atribut static TYPE. De fapt acesta se utilizeaza in loc de tipul primitiv. Dupa ce am
obtinut obiectul Method putem apela metoda dorita prin invoke() care returneaza un rezultat de tip Object.
Primul argument al metodei invoke() este obiectul care apeleaza metoda, daca este o metoda statica atunci null.
Al doilea argument este un array si reprezinta lista parametrilor. Tipul acestora trebuie sa coincida cu cel
specificat la getMethod(). Exceptia InvocationTargetException se genereaza daca metoda apelata genereaza ea
insai exceptii. Lista acestor exceptii putem obtine prin apelul getTargetException()
Accesul constructorilor
Incarcarea dinamica a claselor se realizeaza prin apelul metodelor statice Class.forName() respectiv
Class.newInstance(). Exista si un dezavantaj in utilizarea acestor metode, acestia nu pot primi argumente, deci
se apeleaza doar constructorii impliciti ai acelor clase.  Daca dorim apelul unui anumit  constructor, acesta se
rezolva cu metoda getConstructor(). Exemplul urmator ilustreaza acest lucru:
try{
    Constructor c = Date.class.getConstructor( new Class[] { String.class } );
    Object o = c.newInstance( new Object[] {"Jan 1,2001});
    Date d = ( Date )o;
    System.out.println( d );
}
catch( ClassNotFoundException e1 ){
System.out.println( e1 );
}
catch( NoSuchMethodException e2 ){
System.out.println( e2 );
}
catch( IllegalAccessException e3 ){
System.out.println( e3 );
}
catch( InvocationTargetException e4 ){
System.out.println( e4 );
}

Mecanismul de securitate
1. Incarcatori de clase
2. Managerul de securitate
3. Domenii de protectie
4. Semnaturi digitale
5. Clase pentru implementarea modelului de securitate
6. Extensie Java pentru criptografie (JCE - Java Criptography Extension)
Comparativ cu alte limbaje de programare, care au fost completate cu mecanisme de securitate, Java a fost
dezvoltat astfel incat  ca aceste mecanisme de securitate sa faca parte din limbaj. Trei mecanisme principale
garanteaza securitatea:
 Securitaea la nivelul limbajului:
o nu exista pointeri doar referinte
o permite doar conversii sigure, adica conversia explicita se poate face doar intre obiecte
compatibile
o se verifica automat depasirea dimensiunilor tablourilor in timpul executiei, tablourile isi cunosc
dimensiunile, astfel programul nu poate accesa o zona de memorie nepermisa
o prin modificatorii de acces se precizeaza exact pentru fiecare membru accesibilitatea acestuia
o varibilele de tip primiti si obiectele sunt initializate cu valori implicite conform tipului
 Existenta unui manager de securitate care sa controleze permisiunile aplicatilor la resursele sistemului
pe care se ruleaza aceasta.
 Domenii de protectie (doar versiunea Java 1.2 )
 Semnatura digitala. Autorii codului pot semna codul utilizand algoritmi de criptografie. Astfel
utilizatorii codului pot verifica daca codul provine sau nu dintr-o sursa sigura.
In Java securitatea nu se rezuma doar la securitatea oferita la nivelul limbajului. Dupa ce compilatorul produce
un cod octeti, acest cod este executat intr-un mediu de executie. Acest mediu utilizeaza clasa Verifier pentru a
verifica inca o data codul octeti. Se verifica conversiile, operatiile periculoase pentru a evita executia unor
coduri octeti produse de compilatoare nesigure. Clasa Verifier  este o componenta a masinii virtuale Java si face
urmatoarele verificari:
1. se verifica pentru fiecare operatie numarul parametrilor
2. se verifica conversiile pentru a detecta conversiile ilegale
3. se verifica tipul parametrilor la operatii
4. se verifica daca sunt respectate modificatorii de acces, adica fiecare membru al clasei sa fie accesat
conform acestor modificatori
5. se verifica daca registrii masinii virtuale sunt utilizate in mod adecvat
Incarcatori de clase (Class Loaders)

Un compilator Java traduce codul sursa Java intr-un limbaj pentru o masina virtuala, Masina Virtuala Java
(Java Virtual Machine). Acest cod intermediar se stocheaza intr-un fisier cu extensia  class. Aceste fisiere
contin codul tuturor metodelor unei clase. Aceste fisiere class sunt interpretate  de catre programe care
translateaza instructiunile masinii virtuale in instructiuni  ale microprocesorului. Interpretorul Java incarca doar
acele clase care sunt necesare executiei programului. Pentru a executa aplicatia MyProgram.class interpretorul
executa pasii:
1. Interpretorul Java are un mecanism pentru incarcarea claselor si il utilizeaza pentru incarcarea fisierului
MyProgram.class.
2. Interpretorul executa metoda main() ai clasei , aceasta fiind statica nu este necesara crearea unei instante
din clasa MyProgram.
3. Daca metoda main() necesita si alte clase, atunci se incarca si aceste clase.
4. Daca o clasa are atribute de tip neprimitiv sau superclase, atunci se incarca si acestea. ( Procesul
incarcarii tuturor claselor de care depinde o anumita clasa se numste rezolvarea clasei -resolving the
class).
Mecanismul incarcarii claselor cunoaste variabila de mediu CLASSPATH, si stie sa localizeze clasele din
aceste arhive. Asa sunt incarcate clasele sistem. Acest mecanism de incarcarea claselor este si mecanismul cel
mai nesigur. Prin acest mecanism se verifica doar daca clasele indeplinesc constrangerile de siguranta la nivelul
limbajului. Daca insa dorim o verificare mai adecvata, trebuie inlocuit acest incarcator de clase. Cel mai
cunoscut incarcator de clase este incarcatorul pentru apleturi. Acest incarcator de clase stie sa incarce clase de
pe alte hosturi, stie sa atutentifice arhive JAR semnate. Acest incarcator utilizeaza spatii de nume diferite pentru
clasele locale si pentru clase provenite de pe alte hosturi, pentru a evita conflictul acestora.
Implementarea unui incarcator de clase propriu
Pachetul java.lang contine o clasa abstracta ClassLoader. Orice incarcator propriu trebuie sa extinda aceasta
clasa. Clasa contine si o metoda abstracta loadClass() si  aceasta metoda trebuie redefinita. Restul metodelor
clasei ClassLoader sunt metode finale.
loadClass( String className, boolean resolve)
Metoda trebuie sa stie sa faca urmatoarele:
1. Sa verifice daca clasa indicata prin className este incarcata. pentru a putea face acest lucru neaparat
trebuie sa tina evidenta claselor incarcate.
2. Daca clasa nu este inca incarcata sa verifice daca este clasa sistem. In caz neafirmativ sa incarce codul
octeti din sistemul local de fisiere sau dintr-o alta sursa.
3. Sa apeleze metoda defineClass() a clasei ClassLoader pentru a prezenta codul octeti masinii virtuale.
Daca parametrul resolve are valoarea true se va apela metoda resolveClass() pentru a incarca si clasele referite
de clasa recent incarcata. Pana cand nu este rezolvata o clasa nu se creaza instante din aceasta si nu se pot apela
metodele clasei. Incarcatorii de clase utilizeaza tabele hash pentru a tine evidenta claselor incarcate. Exemplul
urmator este un schelet pentru  un incarcator de clase:
public class MyClassLoader extends ClassLoader
{
    private Hashtable classes = new Hashtable();
    protected synchronized Class loadClass( String name, boolean resolve ) throws
ClassNotFoundException
    {
        Class cl = (Class) classes.get( name );
        if( cl == null )
        {
            try{
                // Se verifica daca este clasa sistem
                return findSystemClass( name );
            }
            catch( ClassNotFoundException  e1){}
            catch( NoClassDefFoundError e1 ){}
            // se incarca codul octeti
            byte classBytes[] = loadClassBytes( name );
            if( classBytes == null ) throw new ClassNotFoundException( name );
            cl = defineClass( name, classBytes, o, classBytes.length );
            if( cl == null ) throw new ClassNotFoundException( name );
            classes.put( name, cl );
            if( resolve) resolveClass( cl );
        }
        private byte[] loadClassBytes( String name )
        {
            String cname =name.replace('.','/')+".class";
            FileInputStream in = null;
            try{
                in = new FileInputStream( cname );
                ByteArrayOutputStream buffer = new ByteArrayOutputStream();
                int ch;
                while( ( ch = in.read()) != -1 )
                    buffer.write( ch );
                return buffer.toByteArray();
            }
            catch( IOException e1)
            {
                if( in != null )
                try{
                    in.close();
                }
                catch( IOException e2 ){}
                return null;
            }
        }
 }
Utilizarea clasei:
ClassLoader loader = new MyClassLoader();
Class c = loader.loadClass("OClasa");
Metodele clasei java.lang.ClassLoader
 
Signatura metodei Descriere
adauga o clasa
noua la masina
virtuala name:
numele clasei 
data: un array
care contine
codul octeti
Class defineClas( String name, byte data[], int offset, int length )
offset: indicele
de inceput al
codului octeti
in array
length:
lungimea
codului octeti
void loadClass( String name, boolean resolve ) se
implementeaza
de catre clasele
care extind
clasa
ClassLoader
name: numele
clasei, se
utilizeaza . ca si
separator intre
pachete
resolve: true,
daca trebuie
apelat metoda
resolveClass
pentru
rezolvarea
clasei
se apeleaza de
catre loadClass,
daca resolve
void resolveClass( Class c ) este true
c: clasa care
trebuie
rezolvata
cauta clasa
intre clasele
sistem si o
incarca daca
Class findSystemClass( String name )
este necesar
name: numele
clasei ce se
cauta
Dupa ce se incarca o clasa nou, masina virtuala Java verifica aceasta cu un program numit verificator. De fapt
exista trei nivele de verificare:
1. se verifica toate clasele incarcate
2. nu se verifica clasele sistem, doar celelalte clase incarcate cu incarcatorul de clase (implicit)
3. nu se verifica nici o clasa
Nivelul de verificare se stabileste la pornirea interpretorului, dupa aceea nu se mai poate schimba. La pornirea
interpretorului putem specifica nivelul de verificare prin optiunile: -verify, -verifyremote, -noverify.
Exemplu: java -verify MyApplication
Verificatorul verifica urmatoarele:
-daca toate variabilele sunt initializate inainte de utilizare
-daca se respecta modifiactorii de acces
-daca se respecta domeniul de vizibilitate a  variabilelor locale
-daca nu cumva apare depasire de stiva
-daca metodele sunt apelate cu tipurile corespunzatoare de argumente
Daca clasa incarcata esueaza vreunul dintre aceste teste atunci se considera clasa corupta si nu se accepta
incarcarea ei. In mod normal si compilatoarele verifica aproape toate acestea, dar este destul de usor sa se scrie
( sau sa se modifice) un fisier .class cu un editor hexazeciamal. Astfel nu numai compilatoarele pot produce cod
octeti si de aceea si interpretorul efectueaza verificari.
Managerul de securitate. Clasa java.lang.SecurityManager
Accesul  unei aplicatii Java la resursele sistemului cum ar fi ecranul, sistemul de fisiere, procese, fire de
executie, retea sunt controlate de catre managerul de
securitate. Functonaliataea acestui manager este implementata in clasa  .  Mediul de executie instantieaza o
singura  data un asemenea
obiect, iar dupa aceea accesul la resursele sistemului este contolat de metodele specifice ale acestui obiect. 
Managerul de securitate implicit se poate schimba prin
metoda statica a clasei System, setsecurityManager(). De fapt putem sa nu utilizam deloc acest manager prin
apelul metodei System.setSecurityManager(null) sau  sa
utilizam propriul nostru manager de securitate System.setSecurityManager( new MySecuritymanager() ), unde 
MySecurityManager este o subclasa a clasei
SecurityManager.  Navigatoarele inaintea executiei apleturilor isi instaleaza propriul manager de securitate
Dupa ce s-a instantiat managerul de securitate, nu se mai
poate schimba politica de securitate a aplicatiei. Metodele cele mai importante ale clasei SecurityManager sunt:
 
 
  Metoda  Semnificatie
checkAccess( Thread g ) se poate accesa firul de executie?
checkExit( int status ) se poate executa System.exit()?
checkExec(  String cmd ) se poate executa procesul specificat prin comanda cmd?
checkRead( String file ) avem permisiunea de a citi fisierul?
checkWrite( String file ) avem permisiunea de a scrie fisierul?
checkDelete( String file ) avem dreptul de stergere pentru fisier?
checkConnect( String host, int port ) putem sa ne conectam la soclul specificat prin host si port?
checkListen( int port ) putem sa cream un soclu server?
checkAccept( String host, int port ) se poate accepta o anumita conexiune?
checkPropertyAccess( String key ) putem accesa o anumita proprietate?
checkTopLevelWindow( Object window ) putem crea o fereastra superioara?

 
 
 
Toate metodele daca se termina cu succes  inseamna ca operatia este permisa mai putin metoda
checkTopLevelWindow() care returneaza o valoare logica, true
insemnand ca accesul este permis.
Vom implementa un manager de securitate propriu care nu permite citirea fisierelor cu extensia .java.  Vom
incerca programul prin: java MyApplication
MyApplication.java
import java.io.*;
class MySecurityManager extends SecurityManager {
 public void checkRead( String s )
 {
     if( s.endsWith(".java") )
  throw new SecurityException("Access to "+s+" file not allowed");
 }
}
public class MyApplication{
    public static void typeFile( String filename ) throws Exception
    {
     int b;
     FileInputStream fin = new FileInputStream( filename );
     while( (b = fin.read()) != -1 )
         System.out.print( (char) b );
     fin.close();
    }
    public static void main( String args[] )
    {
         System.setSecurityManager( new MySecurityManager());
         if( args.length != 1 )
         {
             System.out.println("Utilizare: java MyApplication <nume_fisier>");
             System.exit( 1 );
         }
         try{
          typeFile( args[ 0 ]);
         }
         catch( Exception e )
         {
             System.out.println("Eroare citire fisier "+e );
         }
    }
}
Exemplul urmator executa  un fisier executabil.( sub un sistem Linux). Pentru executia acestuia se va crea un
proces nou. In exemplul urmator x reprezinta un fisier
executabil.
1. Se creaza un fiiser executabil prin compilarea urmatoarei sursa: cc x.c -o x
#include <stdio.h>
main()
{
 system("mkdir aaa"); // Creaza un director cu numele aaa
 exit( 0 );           // Termina executia programului
}
2. Se executa fisierul Java: java  system1
public class system1{
    public static void main( String args[] )
    {
         try{
          Process p = Runtime.getRuntime().exec("x");
         }
         catch( java.io.IOException e2 )
         {
             System.out.println(e2);
         }
    }
}
 
 
Domenii de protectie

Clasa Verifier este un element de securitate la nivel de executie. Insa la nivel de executie exista si alte elemente
de securitate cum ar fi domeniile de protectie cu care putem controla ca anumite clase ce resurse sistem pot
utiliza (fisiere, porturi etc.). Aceste domenii de protectie permit sau interzic accesul acestor resurse in functie de
originea acestor clase si in functie de semnatura atasata acestora. La incarcarea unei clase aceasta este asezat in
domeniul de protectie conform provenientei acestuia. De exemplu toate clasele  provenite de la  java.sun.com si
semnate de acesta sunt asezate in acelasi domeniu de protectie. Clasele provenite de la www.ibm.com sunt
asezate intr-un alt domeniu de protectie. Acestor domenii de protectie li se pot atasa drepturi, in functie de
gradul de incredere in aceste companii. Pana la urma dreprurile unei aplicatii depind de drepturile claselor
utilizate in aplicatie.
Domeniile de protectie au aparut in versiunea Java 1.2. In versiunile precedente exista doar SecurityManager,
care era responsabil pentru securitate. Slabiciunea modelului de securitate utilizand SecurityManager consta in
faptul, ca securitatea aplicatiei este de legat de codul sursa. Daca se modifica recomandarile pentru permisiuni
atunci trebuie rescrise codurile sursa pentru a le putea respecta. O alta slabiciune era ca securiataea unei
aplicatii era stabilit de catre dezvoltatorul acestuia, adica el era cel care decidea daca se utilizeaza sau nu acest
mecanism. Domeniile de protectie facand parte din masina virtuala se utilizeaza indiferent de programator si nu
face parte din codul sursa. Clasa SecurityManager a ramas si in versiunea 1.2, dar numai pentru compatibilitate.
Stabilirea optiunilor de securitate
Domeniile de protectie sunt responsabili pentru protectia sistemului.
grant signedBy "Sun", codeBase "http://java.sun.com"
{
    permission java.io.FilePermission "/tmp/*", "read, write";
    permission java.util.PropertyPermission "*", "read";
    permission java.net.SocketPermission "java.sun.com","connect";
};
Codul de mai sus stabileste urmatoarele:
1. de catre cine sunt semnate clasele:  Sun
2. de unde provin clasele:  "http://java.sun.com"
3. permisiuni :
o     clasele pot scrie si citi fisierele din catalogul /tmp (nu pot executa, nu pot sterge)
o     clasele pot citi proprietatile masinii virtuale
o     clasele pot stabili conexiuni cu java.sun.com

Optiunile de securitate pot fi stocate si in baze de date sau pe servere speciale. Caracterele joker se pot utiliza si
la fisiere pentru a permite accesul la toate fisierele dintr-un anumit catalog sau si in cazul conexiunilor, daca
dorim sa permitem accesul la toate hosturile de la sun, atunci ultima linie se modifica in modul urmator:
   permission java.net.SocketPermission "*.sun.com","connect";
Intr-un singur fisier putem specifica drepturi pentru mai multi utilizatori. Inexemplul urmator permitem
apleturilor provenite dintr-o anumita sursa sa scrie in catalog applet al utilizatorilor: bogdan, maria si ionel.
   permission java.io.FilePermission "/home/bogdan/applet/*", "read, write";
   permission java.io.FilePermission "/home/maria/applet/*", "read, write";
   permission java.io.FilePermission "/home/ionel/applet/*", "read, write";
 
 
 
Permisiunile de securitate se pot stabili in mai multe locuri. Fisierul global de configurare se afla in catalogul l
jdk1.3/lib. Putem specifica un asemenea fisier pentru executia unei aplicatii cu optiunea
-D java.security.policy=fisier. In acest caz aplicatia se ruleaza conform optiunilor specificate in acest fisier. In
mod normal aceste fisiere de configurare pentru securitate sunt aditive, adica daca exista unul si se mai adauga
inca unul, atunci nu va fi suprascris cel vechi , ci cel nou se adauga la continutul acestuia (se extinde cel vechi).
 
 
Apleturile din surse nesigure au permisiuni foarte restrictive, ele nu au drepturi la urmatoarele operatii:
 sa citeasca din sistemul de fisiere al calculatorului pe care se executa
 sa scrie sistemul de fisiere al calculatorului pe care se executa
 sa sterga  din sistemul de fisiere al calculatorului pe care se executa
 sa citeasca proprietatile sistem cu exceptia unora
 sa creeze conexiune cu alte hosturi decat cel de la care provine
 sa incarce biblioteci sau DLL-uri
 sa execute alte programe sau scripturi
 sa forteze terminarea executiei masinii virtuale
Semnaturi digitale
JAR (Java Archive Files) este  o colectie de fisiere HTML, GIF, CLASS si alte fisisre necesare pentru executia
unui aplet sau a unei aplicatii. Deci un fisier JAR este o arhiva. Daca navigatorul da de un asemenea fisier il
incaraca, dezarhiveaza si il executa in mod similar cu o aplicatie locala. De aceea JAR-urile pot fi semnate si
prin aceasta semnatura apletul poate sa fie incadrat intr-un alt domeniu de protectie decat cel implicit.
Proprietatile Codebase si SignedBy determina domeniul de protectie in care este incarcat apletul , astfel putem
permite apletului sa faca o serie de operatii dealtfel interzise apleturilor. Treaba verificarii este a mediului de
executie,.
Pasii pentru semnarea unei arhive JAR
 
1. Se creaza arhiva jar cu utiliatarul jar
2. Se genereaza cheile cu utilitarul  keytool, comanda -genkey
3. Se semneaza arhiva cu utilitarul jarsigner
4. Se exporta certificatul cu utilitarul keytool , comanda -export

1. Crearea arhivei : jar -cvf curs3.jar curs3


Optiuni:
c- creare
v-se afiseaza informatii despre fisierele care intra in arhiva
f-se specifica numele arhivei;
curs3.jar - numele arhivei
curs3 - catalogul care se arhiveaza
2.Generarea cheilor:  keytool -genkey -alias signPetruMaior -keystore manyistore
    -genkey: comanda
    -alias: numele aliasului
    -keystore: numele bazei de date unde se stocheaza cheile
Comanda genereaza o baza de date cu numele manyistore in catalogul curent ci o inregistrare care contine cheia
publica si cea privata insotita de informatii suplimentare despre semnatar. Comanda cere introducerea unor
informatii suplimentare si anume:
Enter keystore password:  This is the life.
What is your first and last name?
[Unknown]:  Margit Antal
What is the name of your organizational unit?
[Unknown]:  Petru-Maior University
What is the name of your organization?
[Unknown]:  Computer Science Department
What is the name of your City or Locality?
[Unknown]:  Tg-Mures
What is the name of your State or Province?
 [Unknown]:  Mures
What is the two-letter country code for this unit?
 [Unknown]:  RO
Is <CN=Margit Antal, OU=Petru-Maior University, O=Computer Science Department, L=Tg-Mures, 
ST=MURES, C=RO> correct?
[no]:  y
Enter key passwo rd for  <signPetruMaior>
 (RETURN if same as keystore password):  nec2001
3. Semnarea arhivei: jarsigner -keystore manyistore -signedjar scurs3.jar  curs3.jar
signPetruMaior
Enter PassPhrase for keystore: This is the life.
Enter key password for signPetruMaior: nec2001
 
4.  Avem o arhiva semnata scurs3.jar. Clientii care vor utiliza fiiserul vor dori autentificarea semnaturii. Pentru
a putea efectua aceasta verificare, au nevoie de cheia publica. Pentru aceasta se genereaza un certificat care
contine cheia publica. Certificatul se obtine din baza de date manyistore si se stocheaza intr-un fisier. Comanda
urmatoare face exact acest lucru:
keytool -export -keystore manyistore -alias signPetruMaior -file MargitAntal.cer
Clientii vor primi impreuna cu arhiva si acest certificat, autentificarea facandu-se ori de catre mediul de
executie ori manual cu comanda urmatoare:
jarsigner -verify scurs3.jar
Comanda precedenta verifica daca arhiva este semnata si daca a fost modificat dupa semnare. Astfel putem
avea urmatoarele rezultate:
1. jar verified - daca totul este in regula
2. jar is unsigned. (signatures missing or not parsable) - pentru un jar nesemnat
3. jarsigner: java.lang.SecurityException: invalid SHA1 ;signature file digest for
test/classes/Manifest.class -daca arhiva a fost modificata dupa ce s-a semnat
Semnarea arhivelor JAR se face cu acelasi utilitar. Pentru a semna o arhiva trebuie sa avem o cheie privata.
Cheia privata impreuna cu cheia publica sunt stocate intr-o baza de data protejata cu parola si cu numele
implicit keystores. O asemenea baza de date poate sa contina cheile mai multor semnatari. Fiecare cheie este
identificata printr-un alias, care este de obicei aliasul semnatarului.
 
Clase pentru implementarea modelului de securitate
 
Extensie Java pentru criptografie (JCE - Java Criptography Extension)
Programarea distribuita

Pana acum am studiat doua modele de client-server:


 utilizarea soclurilor pentru transmiterea informatiilor intre client si server
 utilizarea JDBC pentru interogarea si actualizarea bazelor de date
Aceste modele se pot aplica doar in situatii speciale. De exemplu soclurile presupun ca se transmit siruri de
octeti, iar JDBC-ul este potrivit doar pentru informatii ce pot fi stocate in baze de date relationale. De exemplu
bazele de date relationale nu sunt cele mai adecvate  pentru stocarea colectiilor eterogene, iar aceste colectii
eterogene sunt foarte importante pentru programarea orientata obiect. Programarea distribuita ne ofera un
model  de comunicare intre obiecte aflate pe diferite hosturi. In general aceste obiecte pot fi scrise in diferite
limbaje de programare. OMG (Object Management Group)  a dezvoltat o arhitectura numita CORBA
(Common Object Request Broker Architecture) care permite acest lucru. Obiectele nu vor comunica direct inte
ele ci utilizeaza un serviciu numit ORB (Object Request Broker) care face treaba urata. Obiectele ORB sunt
distribuite in retea si stiu sa comunice intre ele utilizand protocolul Internet inter-ORB (IIOP). Utilizand
CORBA clientii si serverele pot fi implementate in orice limbaj de programare, dar fiecare utilizeaza IDL
(Interface Definition Language) pentru a specifica signatura metodelor respectiv tipul obiectelor ce pot fi
trimise si receptionate de catre aceste obiecte.
 
RMI (Remote Method Invocations)

Acest mecanism se utilizeaza doar pentru comunicarea obiectelor Java. Java implementeaza obiectele aflate la
distanta, rezolva transportul datelor utilizand protocolul TCP/IP. Utilizatorul nu lucreaza la nivel de octeti.
Permite insa mai mult decat transportul obiectelor in retea si anume apelul  metodelor unor obiecte aflate la
distanta., fara transportul obiectelor corespunzatoare. Deci RMI inseamna ca codul care se ruleaza pe masina
clientului poate apela un cod aflat pe un server.
 
Stubs and Skeletons ( Ciot si schelet)

Cand clientul vrea sa apeleze o metoda aflata la distanta, o apeleaza ca si cum ar fi o metoda obisnuita. De fapt
clientul apeleaza o metoda incapsulata intr-un obiect numit  stub. Acest stub se afla pe masina clientului si este
responsabil pentru impachetarea  cererii, codificand intr-un mod independent de platforma parametrii metodei.
Pentru referinte se utilizeaza serializarea acestora. Codificarea parametrilor intr-un mod adecvat treansportului
pe retea se numeste "parameter marshalling". De fapt acest obiect stub impacheteaza urmatoarele informatii:
 identificator obiectului aflat la distanta
 identificatorul operatiei (metodei) invocate
 parametrii metodei
Dupa ce s-a creat pachetul, acesta este trimis la server. Pe partea de server pachetul este receptionat de un
obiect skelton si efectueaza urmatoarele operatii:
 decodificarea parametrilor
 apeleaza metoda ceruta de catre client
 captureaza valoarea returnata sau exceptiile generate de catre apelul metodei
 codifica rezultatul obtinut
 trimite inapoi pachetul care contine rezultatele apelului
Daca metoda apelata genereaza exceptii atunci acestea vor fi regenerate in spatiul procesului client. Acest
proces pare complicat, dar este complet transparent pentru programator. Obiectele aflate la distanta sunt
colectate de catre colectorul de gunoi automat, in mod similar cu cele locale. Sintaxa apelului unei metode este
similar cu apelul unei metode locale.
 
Incarcarea dinamica a claselor

Clasele pentru parametrii metodei, pentru valoarea returnata si exceptii trebuie incarcate. De exemplu daca se
declara o metoda cu un anumit tip returnat cunoscut de catre client, dar metoda returneaza unul compatibil ( o
clasa derivata) care nu este cunoscuta clientului. In acest caz si aceasta clasa noua trebuie incarcata. Daca 
aceste clase se gasesc in sistemul de fisiere local, atunci se incarca de aici, altfel sunt incarcate similar ca
clasele necesare apletului intr-un program navigator. Incarcarea codului apletului este treaba incarcatorului de
clase. Incarcatorul apleturilor este foarte restrictiv, permite incarcarea claselor doar de pe hostul de unde
provine apletul. Incarcatorul determina locatia de unde trebuie incarcata clasa, iar managerul de securitate
stabileste drepturile pentru aceste clase.
 
O aplicatie care utilizeaza RMI

In acest exemplu serverul creaza obiecte de tip Product, iar clientul incearca sa acceseze aceste obiecte.
Clientul incearca sa manipuleze niste obiecte aflate pe server, pentru a putea face acest lucru trebuie sa
cunoasca ce se pot face cu acele obiecte.  In cazul acestei aplicatii vom permite clientului sa obtina informatii
despre produse stocate pe server. Pentru aceasta se utilizeaza o interfata cunoscuta atat pe partea de server cat si
pe partea de client.
Fisierul Product.java
import java.rmi.*;
interface Product extends Remote{
    public String getDescription() throws RemoteException;
}
 
Toate interfetele pentru obiecte  aflate la distanta trebuie sa extinda interfata Remote din pachetul java.rmi.
Toate metodele declarate pot arunca exceptia RemoteException, deci acest lucru trebuie declarat. Cauza acestei
exceptii este ca metodele aflate la distanta sunt mai fragile, adica s-ar pute sa se apleleze corect o metoda, dar
hostul pe care se afla obiectul corespunzator sa nu fie accesibil temporar. In asemenea situatii se genereaza
exceptia Remoteexception si codul pe partea clientului trebuie pregatit sa faca fata unor asemenea situatii.
Clientul va acceseaza obiectul server prin intermediul obiectului stub.
Fiserul ProductImpl.java
import java.rmi.*;
import java.rmi.server.*;
public class ProductImpl extends UnicastRemoteObject implements Product{
    private String descr;
 
    public ProductImpl( String d ) throws RemoteException
    {
         descr = d;
    }
 
    public String getDescription(){
         return "I am a "+descr+" Buy me";
    }
}
Aceasta clasa  are o singura metoda getDescription() care poate fi apelat de catre un client. Este o clasa care se
afla pe partea de server si extinde clasa UnicastRemote Object si inzestreaza obiectele instantiate din aceasta
clasa cu proprietatea de a fi accesibile de la distanta.

Toate clasele pe partea de server trebuie derivate din clasa RemoteServer, clasa definita in pachetul java.rmi.
Clasa UnicastRemoteObject este derivat deja din aceasta clasa. Deci obiectele de acest tip vor fi instantiate pe
partea de server si ele trebuie sa fie accesibile printr-o conexiune TCP/IP cu serverul. Pentru ca clientul sa
obtina acces la un obiect de pe server se va apela o metoda aflata la distanta care returneaza o referinta la un
asemenea obiect.  Mecanismul RMI returneaza clientului o referinta la un obiect server, nu obiectul propriu zis.
De aceea trebuie sa exista un mecanism de localizarea unui obiect server. Biblioteca RMI ne furnizeaza un
asemenea serviciu care se numeste bootstrap registry service. Serverul inregistreaza obiecte cu ajutorul acestui
serviciu. Inregistrarea se face  pe partea de server cu metodele statice bind() sau rebind() ale clasei
java.rmi.Naming.
// server
ProductImpl p1 = new Product("Romanian-English Dictionary");
Naming.bind("redict",p1);
Clientul acceseaza obiectul server  cu metoda lookup() a clasei Naming:
//client
String url="rmi://127.0.0.1/"; // Se schimba cu adresa IP corespunzatoare sau cu numele
corespunzator Ex: 193.226.19.42 sau www.uttgm.ro
Product c1 = (Product) Naming.lookup( url+"redict");
Pornirea serviciului de inregistrarea numelor
Serviciul de inregistrare trebuie pornit inainte crearii obiectelor ce trebuie inregistrate, de aceea vom continua
cu descrierea posibilitatilor pornirii acestui serviciu. Numele acestui program este rmiregistry si se ruleaza pe
portul 1099. Daca acest port este intamplator alocat pentru un alt serviciu atunci acesta poate fi schimbat
indicand la pornirea serviciul portul ldorit.
Windows:
start rmiregistry  // se ruleaza pe portul 1099
start rmiregistry  1234 // se ruleaza pe portul 1234
Unix:
rmiregistry & // se ruleaza pe portul 1099
rmiregistry  1234  &// se ruleaza pe portul 1234
Efectul utilizarii start este ca comanda se executa intr-o fereastra noua, iar sub Unix comanda se lanseaza in
fundal (background ).
Generarea claselor pentru  skelton and stub se face cu utiliatrul rmic. Aceste clase  nu sunt utilizate direct de
catre programatorul Java si nici nu trebuie scrise de catre acesta. 
rmic ProductImpl
Se vor genera doua fisiere ProductImpl_Skel.class si ProductImpl_Stub.class. Daca clasa pentru care se
genereaza este intr-un pachet atunci se apeleaza rmic cu  numele pachetului.
import java.rmi.*;
import java.rmi.server.*;
public class ProductServer
{
    public static void main( String args[] ){
     //System.setSecurityManager( new RMISecurityManager());
     try{
         ProductImpl p1 = new ProductImpl("Romanian-English Dictionary");
         ProductImpl p2 = new ProductImpl("English-Roamanian Dictionary");
         Naming.rebind("redict",p1);
         Naming.rebind("erdict",p2);
     }
     catch( Exception e )
     {
         System.out.println("Error: "+e );
     }
    }
}
Programul creaza doua obiecte si ii inregistreaza, dar nu isi  termina executia. Masina virtuala cand creaza un
obiect care extinde clasa UnicastRemoteObject, se creaza un fir de executie care se ruleaza permanent.
Inregistrarile se pot verifica cu urmatorul program:
import java.rmi.*;
import java.rmi.server.*;
public class ShowBindings
{
    public static void main( String args[] ){
 //System.setSecurityManager( new RMISecurityManager());
 try{
     String[] bindings = Naming.list("");
     for( int i=0; i<bindings.length; i++ )
  System.out.println( bindings[ i ] );
 }
 catch( Exception e )
 {
     System.out.println( e );
 }
    }
}
In mod normal pentru exemplul nostru ar trebui sa afiseze:
rmi:/redict
rmi:/erdict
Programul client va ruga fiecare obiect inregistrat sa-si afiseze descrierea lor:
import java.rmi.*;
import java.rmi.server.*;
public class ProductClient
{
    public static void main( String args[] )
    {
     //System.setSecurityManager( new RMISecurityManager());
     String url ="rmi://localhost/";
     try{
         Product c1 = (Product) Naming.lookup(url+"redict");
         Product c2 = (Product) Naming.lookup(url+"erdict");
         System.out.println(c1.getDescription());
         System.out.println(c2.getDescription());
     }
     catch( Exception e ){
         System.out.println( e );
     }
     System.exit( 0 );
    }
}
Diagrama de clasa:

 Diagrama de secventiere:

Internationalizare
Clasa Locale
Text
Numar si valuta
 
1.  Clasa Locale

Crearea obiectelor  java.util.Locale se face prin invocarea constructorului clasei Locale si transmitand acestuia
codul limbii si codul tarii. Aceste coduri se gasesc la adresele:
ISO Language Code: http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt
ISO Country Code: http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html
Locale l1 = new Locale("en","US");
Locale l2 = new Locale("en","GB");
Obiectul  de tip Locale este doar un identificator care se transmite altor obiecte pentru a realiza o afisare
conform specificatiilor tarii. Putem afisa aceste specificatii pentru un anumit tip de obiect, de exemplu pentru
data calendaristica vom afisa toate formatele cunoscute de catre acesta. Lista formatelor se obtine de la clasa
DateFormat cu metoda getAvailableLocales().:
import java.util.*;
import java.text.*;
public class Available {
   static public void main(String[] args) {
          Locale list[] = DateFormat.getAvailableLocales();
          for (int i = 0; i < list.length; i++) {
              System.out.println(list[i].getLanguage() + " " + list[i].getCountry());
          }
    }
}
Putem seta valorile implicite pentru limba si tara in doua moduri:
 prin setarea unor valori corespunzatoare pentru proprietatile sistem user.language si user.region.
 prin crearea unui obiect Locale si invocand metoda setDefault a acestuia.

Exemplul urmator ilustreaza aceste tehnici:


import java.util.*;
public class DefaultLocale {
    static public void main(String[] args) {
               Properties props = System.getProperties();
               props.put("user.language", "ja");
               props.put("user.region", "JP");
               System.setProperties(props);
               Locale aLocale = Locale.getDefault();
               System.out.println(aLocale.toString());
               aLocale = new Locale("fr", "FR");
               Locale.setDefault(aLocale);
               System.out.println(aLocale.toString());
      }
}
In cadrul unei aplicatii putem seta aceste proprietati pentru fiecare obiect al aplicatiei.
 
2. Text
Sa presupunem va dorim sa scriem un program care afiseaza diferite tipuri de mesaje in limba materna a
utilizatorului. La inceput sa presupunem ca avem trei tipuri de mesaje: salutare de bun-venit, intrebare de
politete, salutare de ramas bun. Un program prntru engleza ar arata in modul urmator:
 
System.out.println("Hello.");
System.out.println("How are you?");
System.out.println("Goodbye.");

Si acum sa resriem codul intr-un format international.


import java.util.*;
public class LocaleSample {
    static public void main(String[] args) {
               String language;
               String country;
               if (args.length != 2) {
                   language = new String("en");
                   country = new String("US");
               } else {
                   language = new String(args[0]);
                   country = new String(args[1]);
               }
               Locale currentLocale;
               ResourceBundle messages;
               currentLocale = new Locale(language, country);
               messages =
                 ResourceBundle.getBundle("MessagesBundle",currentLocale);
               System.out.println(messages.getString("greetings"));
               System.out.println(messages.getString("inquiry"));
               System.out.println(messages.getString("farewell"));
     }
}
Utilizatorul acestui program va putea specifica la linia de comanda codul limbii si codul tarii. Pentru fiecare
pereche limba-tara va exista cate un fisier de proprietati cu aceste trei tipuri de mesaje. Daca se specifica o
pereche limba-tara pentru care nu exista fisier, atunci se va utiliza fisierul implicit pentru citirea datelor.
Exemple de fisiere:
MessagesBundle.properties
greetings = Hello.
farewell = Goodbye.
inquiry = How are you?
 
MessagesBundle_de_DE.properties
greetings = Hallo.
farewell = Tschüß.
inquiry = Wie geht's?
MessagesBundle_en_US.properties
greetings = Hello.
farewell = Goodbye.
inquiry = How are you?
MessagesBundle_fr_FR.properties
greetings = Bonjour.
farewell = Au revoir.
inquiry = Comment allez-vous?
Localizarea acestui fisier de proprietati se face cu urmatoarea constructie: ResourceBundle messages =
ResourceBundle.getBundle("MessagesBundle", currentLocale);
In acest caz se presupune ca numele familiei de fisiere este MessageBundle la care se adauga continutul
obiectului currentLocale. In exemplul nostru acesta contine doua lucruri: codul limbii si codul tarii. Pentru
limba se utilizeaza un cod format din dou litere(minuscule), iar pentru tara un cod format din doua majuscule.
Metoda getBundle() a clasei ResourceBundle incearca sa localizeze fisierul de proprietati. si va incarca in
obiectul messages.
 
3. Numar si valuta

Pentru afisarea numerelor si a valutei  se utilizeaza clasa NumberFormat. Aceasta clasa are metode pentru
obtinerea unor obiecte care se utilizeaza la formatarea numerelor respectiv a valutei. De exemplu formatarea
unui numar real conform specificatiilor locale se realizeaza in modul urmator:
Presupunem ca dorim sa afisam numarul double d = new Double( 123456.789 )
1. Se construieste un obiect Locale: Locale l = new Locale("en","GB");
2. Se obtine obiectul de formatare: NumberFormat formatter = NumberFormat.getNumberInstance( l );
3. Se formateaza numarul:  formatter.format( numar );
import java.util.*;
import java.text.*;
public class NumberFormatDemo {
   static public void displayNumber(Locale currentLocale) {
      Integer quantity = new Integer(123456);
      Double amount = new Double(345987.246);
      NumberFormat numberFormatter;
      String quantityOut;
      String amountOut;
      numberFormatter = NumberFormat.getNumberInstance(currentLocale);
      quantityOut = numberFormatter.format(quantity);
      amountOut = numberFormatter.format(amount);
      System.out.println(quantityOut + "   " + currentLocale.toString());
      System.out.println(amountOut + "   " + currentLocale.toString());
   }
   static public void displayCurrency(Locale currentLocale) {
      Double currency = new Double(9876543.21);
      NumberFormat currencyFormatter;
      String currencyOut;
      currencyFormatter = NumberFormat.getCurrencyInstance(currentLocale);
      currencyOut = currencyFormatter.format(currency);
      System.out.println(currencyOut + "   " + currentLocale.toString());
   }
   static public void displayPercent(Locale currentLocale) {
      Double percent = new Double(0.75);
      NumberFormat percentFormatter;
      String percentOut;
      percentFormatter = NumberFormat.getPercentInstance(currentLocale);
      percentOut = percentFormatter.format(percent);
      System.out.println(percentOut + "   " + currentLocale.toString());
   }
   static public void main(String[] args) {
      Locale[] locales = {
          new Locale("fr","FR"),
          new Locale("de","DE"),
          new Locale("en","US")
      };
      for (int i = 0; i < locales.length; i++) {
         System.out.println();
         displayNumber(locales[i]);
         displayCurrency(locales[i]);
         displayPercent(locales[i]);
      }
   }
}

Colectii
 Introducere
 Colectii de baza (interfete)
 Collection
 Set
 List
 Map
Introducere
O colectie este de fapt un obiect care grupeaza mai multe elemente intr-o singura unitatae. Colectiile se
utilizeaza pentru stocarea, regasirea si manupularea datelor. Deci colectiile reprezinta obiecte care in mod
natiral formeaza un grup. In JDK 1.1 am avut trei tipuri de colectii:
 Vector,
 Hashtable
 array
Descrierile acestor clase gasiti la capitolul Clase de baza.  Incepand cu JDK 1.2. s-a introdus un cadru nou
pentru lucrul cu colectii. Orice cadru contine urmatoarele trei lucruri esentiale:
 interfete: tipuri de date abstracte (TAD=ADT Abstract Data Type) pentru repreentarea diferitelor
colectii. Aceste interfete permit ca colectiile sa fie manipulabile intr-un mod unitar indiferent de
implementarii.
 implementari: implementarile concrete ale tipurilor de date abstracte din interfete
 algoritmi: o colectie de metode care permit operatii asupra colectiilor (cautare,  sortare). Acesti
algoritmi sunt polimorfici, adica aceasi metoda se utilizeaza pentru diferite tipuri de date (ex. metoda
sort(), ordoneaza orice colectie ale caror elemente implementeaza interfata Comparable).
Interfete
Asa cum se vede pe imaginea alaturata un Set este o colectie speciala, iar SortedSet este un Set special.
Interfata Map nu este specializata din interfata Collection, deci colectiile care realizeaza aceasta interfata nu vor
fi colectii adevarate.
 
Interfata Collection

Aceasta interfata este interfata de baza din care sunt specializate celelalte interfete. O colectie reprezinta un
grup de obiecte numite si elemente. Anumite colectii permit redundanta (acelasi element sa apara de doua ori),
altele nu. Unele sunt ordonate, altele nu. Nu extista nici o clasa care sa implementeze in mod direct aceasta
interfata, doar interfetele Set si List vor avea implementari.
Orice implementare indirecta a acestei interfete  poseda un constructor  cu un argument de tip Collection si care
initializeaza colectia respectiva cu cea specificata. De exemplu:
fie c o colectie (List, Set sau un alt tip), atunci
List  l = new ArrayList( c );
creaza un obiect de tip ArrayList initializat cu colectia c.
Definitia interfetei Collection:
 
           public interface Collection {
             // Basic Operations
             int size();
             boolean isEmpty();
             boolean contains(Object element);
             boolean add(Object element);    // Optional
             boolean remove(Object element); // Optional
             Iterator iterator();
             // Bulk Operations
             boolean containsAll(Collection c);
             boolean addAll(Collection c);    // Optional
             boolean removeAll(Collection c); // Optional
             boolean retainAll(Collection c); // Optional
             void clear();                    // Optional
             // Array Operations
             Object[] toArray();
             Object[] toArray(Object a[]);
         }
 
Operatii elementare:
 a afla numarul elementelor din colectie ( size )
 a verifica daca lista este vida( isEmpty )
 a verifica daca un anumit obiect apartine colectiei
 adaugarea si stergerea elementelor
 a obtine un iterator pentru parcurgerea elementelor colectiei
Iteratorul este un obiect similar cu Enumeration, dar pe langa parcurgerea colectiei poate sa permita si stergerea
elementelor. Interfata Iterator este declarat in java.util in modul urmator:
              public interface Iterator {
                  boolean hasNext();
                  Object next();
                  void remove();    // Optional
      }
Metoda hasNext() este similara cu metoda hasMoreElements() a interfetei Enumeration, iar metoda next() cu
nextElement() a aceleiasi interfete. Metoda remove() sterge elementul curent (elementul obtinut prin ultimul
apel next()).  Urmatoarea portiune de cod parcurge o colectie stergand elementele care respecta o anumita
conditie.
 
 
Collection c;
// se initializeaza colectia
....
Iterator i;
while( i.hasNext() )
    if( cond(i.next() )
        i.remove();

Exemplul precedent functioneaza pentru orice colectie, deci algoritmul de mai sus este unul polimorfic.  Ar fi
fost imposibil de implementat acest algoritm cu Enumeration deoarece interfata nu permite stergerea sigura a
elementelor pe parcursul traversarii.
Operatii in masa
Sunt operatii care executa o anumita operatie asupra mai multor elemente dintr-o colectie. Aceste operatii pot fi
executate si  altfel (prin repetarea aceleiasi operatii elementare), dar probabil  intr-un mod mai putin eficient.
 
 boolean containsAll( Collection c):  returneaza true, daca c este continuta in intregime in colectie
 boolean addAll( Collection c): adauga la colectie toate elementele colectiei c si returneaza true daca
colectia s-a schimbat dupa adaugare
 boolean removeAll( Collection c ): sterge toate elemntele care apartin colectiei c si returneaza true daca
colectia s-a schimbat dupa stergere
 boolean retainAll( Collection c ): sterge toate elementele care nu apartin colectiei c si returneaza true
daca colectia s-a schimbat dupa stergere
 void clear(): sterge toate elementele colectiei

Operatii cu array
Metoda toArray() s-a introdus pentru a pastra compatibilitatea cu versiunile precedente, permite
transformarea unei colectii intr-un array. Fara nici un argument se obtine un array format din elemente de tip
Object.
Exemple:
Collection c;
//Initializare c
...
Object[] a = c.toArray();
// Daca se stie ca colectia contine numai obiecte de tip String
String[] a = (String[]) c.toArray(new String[0]);

 
Interfata Set
Interfata Set reprezinta o colectie care nu permite duplicate. Modeleaza multimea din matematica. Nu contine
metode noi, doar cele mostenite de la interfata Collection, la care adauga restrictia cu duplicatele. Doua obiecte
de tip Set sunt egale daca ele contin aceleasi elemente.
Pachetul java.util contine doua implementari a interfetei Set:
 HashSet: stocheaza elementele colectiei intr-un tablou hash
 TreeSet: stocheaza elementele intr-un arbore "red-black"
Utilizare:
Collection c;
...
Collection c1 = new HashSet(c);
Exemplul urmator afiseaza duplicatele dintr-o colectie. (Colectia va fi formata din argumentele liniei de
comanda)
import java.util.*;
public class Duplicate {
    public static void main(String args[]) {
        Set s = new HashSet();
        for (int i=0; i<args.length; i++)
               if (!s.add(args[i]))
                    System.out.println("Duplicat: "+args[i]);
       System.out.println(s.size()+" cuvinte distincte: "+s);
  }
}
Executie: java Duplicate am venit am vazut am plecat
Rezultat:
Duplicat: am
Duplicat: am
4 cuvinte distincte: [ vazut, venit, plecat, am]
Schimband HashSet la TreeSet vom obtine cuvintele in ordine alfabetica, adica
Duplicat: am
Duplicat: am
4 cuvinte distincte: [ am, plecat, vazut, venit ]
In exemplele din acest capitol intotdeanu ne-am referit la o colectie printr-o referinta la o interfata in loc de o
referinta la o implementare. Este o tehnica foarte buna, astfel schimband implementarea nu va trebui sa
rescriem codul, deoarece nu contine referinte la implementare, doar la interfeta,
Operatii cu multimi:
Reuniune
Set union = new HashSet(s1);
union.addAll(s2);
 
Intersectie
Set intersection = new HashSet(s1);
intersection.retainAll(s2);

Diferenta
Set difference = new HashSet(s1);
difference.removeAll(s2);
In exemplul cu duplicatele am obtinut la sfarsit multimea care contine fiecare element o singura data, fara
duplicate. Vom modifica exemplul astfel incat sa afiseze doua multime, una care contine doar cuvintele care
apar o singura data, iar o alta multime care contine duplicatele. Pentru a realiza acest lucru vom lucra cu doua
multimi (doua obiecte de tip Set). La prima multime se adauga toate cuvintele, iar la a doua multime doar
duplicatele si la sfarsit se considera diferenta celor doua multimi astfel obtinand unicatele.
import java.util.*;
public class FindDups2 {
    public static void main(String args[]) {
        Set uniques = new HashSet();
        Set dups = new HashSet();
       for (int i=0; i<args.length; i++)
            if (!uniques.add(args[i]))
               dups.add(args[i]);
           uniques.removeAll(dups);  // Se modifica multimea uniques, se elimina
duplicatele
          System.out.println("Unicate:    " + uniques);
          System.out.println("Duplicate:  " + dups);
  }
}
 
Interfata List
Lista este o colectie ordonata (fiecare element este caracterizat prin pozitia sa in lista) si permite si duplicate.
Interfata List pe langa operatiile mostenite de la interfata Collection contine operatii pentru:
-accesul elementelor pe baza pozitiei lor in lista
-cautarea unui obiect specificat prin returnarea pozitiei
-obtinerea unui iterator pentru efectuarea parcurgerilor specifice listei, se extinde semantica interfetei Iterator
-obtinerea unei subliste
Declaratia interfetei List:
public interface List extends Collection {
    // Positional Access
    Object get(int index);
    Object set(int index, Object element);            // Optional
    void add(int index, Object element);              // Optional
    Object remove(int index);                         // Optional
    abstract boolean addAll(int index, Collection c); // Optional
    // Search
    int indexOf(Object o);
    int lastIndexOf(Object o);
    // Iteration
    ListIterator listIterator();
    ListIterator listIterator(int index);
    // Range-view
    List subList(int from, int to);
}
 
JDK contine doua implementari a intereftei List, ArrayList si LinkedList.
The JDK contains two general-purpose List implementations. ArrayList, which is generally the best-performing
implementation, and LinkedListwhich offers better performance under certain circumstances. Also,
 
Operatii:
Concatenarea a doua liste
Varianta A. (prin modificarea listei list1)
list1.addAll(list2);
Varianta B. (prin crearea listei care va contine listele concatenate)
List list3 = new ArrayList(list1);
list3.addAll(list2);

Interschimbarea a doua elemente din lista


 
private static void swap(List a, int i, int j) {
    Object tmp = a.get(i);
    a.set(i, a.get(j));
    a.set(j, tmp);
}
Metoda de mai sus este una polimorfica, se poate utiliza pentru interschimbarea elementelor in liste de diferite
tipuri, iar urmatoarea metoda va utiliza interschimbarea pentru a schimba ordinea elementelor din lista intr-un
mod aleator.
 
public static void shuffle(List list, Random rnd) {
    for (int i=list.size(); i>1; i--)
        swap(list, i-1, rnd.nextInt(i));
}
Urmatoarea aplicatie afiseaza argumentele liniei de comanda intr-o ordine aleatoare:
 
import java.util.*;
 
public class Shuffle {
    public static void main(String args[]) {
        List l = new ArrayList();
        for (int i=0; i<args.length; i++)
            l.add(args[i]);
        Collections.shuffle(l, new Random());
        System.out.println(l);
    }
}
 
O versiune mai eficienta ar fi urmatoarea:
Se utilizeaza metoda asList () a clasei Arrays pentru conversie array-lista si pentru lista astfel obtinuta se
apeleaza metoda shuffle().
 
import java.util.*;
public class Shuffle {
    public static void main(String args[]) {
        List l = Arrays.asList(args);
        Collections.shuffle(l);
        System.out.println(l);
    }
}
 
Iteratori
 
public interface ListIterator extends Iterator {
    boolean hasNext();
    Object next();
    boolean hasPrevious();
    Object previous();
 
    int nextIndex();
    int previousIndex();
    void remove();          // Optional
    void set(Object o);     // Optional
    void add(Object o);     // Optional
}
 
 
Metodele hasNext(), next() si remove() sunt mostenite de la interfata Iterator au aceasi functionalitate ca si in
Iterator.
Parcurgerea listei  "inainte"
for (ListIterator i=l.listIterator(); i.hasNext(); ) {
    Foo f = (Foo) i.next();
    ...
}
Parcurgerea listei  "inapoi"
for (ListIterator i=l.listIterator(l.size()); i.hasPrevious(); ) {
    Foo f = (Foo) i.previous();
    ...
}
 
Interfata List are doua metode listIterator, una care pozitioneaza pozitia curenta la inceputul listei listIterator(),
iar una care pozitioneaza intr-o pozitie specificata listIterator( int pozitie ).
 
Metoda subList( int fromIndex, int toIndex) returneaza o referinta la o sublista cu elementele incepand cu
indicele fromIndex (inclusiv) oana la indicele (toIndex) exclusiv. Atentie, dupa efectuarea acestet operatii nu
vom avea doua liste ci una singura, deci toate operatiile efectuate cu sublista vor afecta si cea originala. De
exemplu
 
list.subList(fromIndex, toIndex).clear(); //sterge toate elementele sublistei,
eliminandu-le si din list

Urmatoarele operatii de cautare sunt efectuate in sublista, deci pozitia returnata va fi o pozitie in sublista nu in
cea originala
 
int i = list.subList(fromIndex, toIndex).indexOf(o);
int j = list.subList(fromIndex, toIndex).lastIndexOf(o);
Algoritmi
sort(List): ordoneaza elementele listei cu algoritmul merge-sort
shuffle(List): efectueaza o permutatie a elementelor listei in mod aleator
reverse(List): inverseaza o lista
fill(List, Object):  suprascrie fiecare element al listei cu valoarea specificata
copy(List dest, List src): copieaza lista sursa in lisssta destinatie
binarySearch(List, Object): cauta un element in lista utilizand algoritmul de cautare binara
 
Interfata Map

Map este un obiect care asocieaza chei la valori si nu permite duplicate pentru cheie. Deci fiecare cheie are
asociata o singura valoare.
 
Cheie Valoare

public interface Map {


    // Basic Operations
    Object put(Object key, Object value);
    Object get(Object key);
    Object remove(Object key);
    boolean containsKey(Object key);
    boolean containsValue(Object value);
    int size();
    boolean isEmpty();
    // Bulk Operations
    void putAll(Map t);
    void clear();
    // Collection Views
    public Set keySet();
    public Collection values();
    public Set entrySet();
    // Interface for entrySet elements
    public interface Entry {
        Object getKey();
        Object getValue();
        Object setValue(Object value);
    }
}
In JDK avem doua implementari pentru aceasta interfata:
 HashMap, care stocheaza elementele intr-un tabel hash
 TreeMap, care stocheaza elementele intr-un arbore "red-black"

Interfata Map generalizeaza clasa Hashtable existenta si in versiunile precedente. Permite iterarea cheilor,
valorilor si a perechilor cheie-valoare. Mai mult permite si operatii de stergere pe parcursul iterarii. Programul
urmator calculeaza frecventa cuvinteleor in lista argumentelor liniei de comanda.
 
import java.util.*;
public class Freq {
 
    public static void main(String args[]) {
        Map m = new HashMap();
 
        // Initializeaza tabloul frecventelor
        for (int i=0; i<args.length; i++) {
            // Se verifica existenta cuvantului args[ i ], daca exista freq va fi null
            Integer freq = (Integer) m.get(args[i]);
 
            if( freq == null )
                m.put( args[ i ], new Integer(1) );
            else
                m.put( args[ i ], new Integer(freq.intValue()+1 ));
       }
       System.out.print(m.size()+" cuvinte distincte:");
       System.out.println(m);
  }
}

Metode pentru obtinerea unor view-uri din colectie


keySet: este o multime (Set) care contine toate cheile din colectie
values: colectia valorilor atasate cheilor, nu este o multime adevarata deoarece aceasi valoare poate fi atasata
mai multor chei
entrySet: multimea (set) perechilor cheie-valoare continute in Map.
Interfata Map contine si o interfata incuibata Entry pentru tipul elementelor din multime.
Iterarea peste elementele colectiei se poate efectua intr-un singur mod
Map m;
...
//Iterarea peste multimea cheilor
for (Iterator i=m.keySet().iterator(); i.hasNext(); )
    System.out.println(i.next());
//Iterarea peste multimea perechilor cheie-valoare
for (Iterator i=m.entrySet().iterator(); i.hasNext(); ) {
    Map.Entry e = (Map.Entry) i.next();
    System.out.println(e.getKey() + ": " + e.getValue());
}

Crearea interfetelor grafice cu  pachetul swing


1. Introducere
2. Arhitectura Model-View-Controller(MVC)
3. Look and Feel
4. Utilizarea componentelor Swing
5. JLabel
6. JButton
7. JTextComponent
8. JPasswordField
9. JScrollBar
10. JSlider
11. JProgressBar
12. JComboBox
13. JList
14. Timer
Introducere
Daca  ati incercat sa creati componente proprii  in AWT, probabil ca ati extins clasa Canvas sau Panel. Aceasta
metoda functioneaza insa ridica cateva probleme si anume atat Canvas cat si Panel sunt componente
netransparente si daca o componenta contine prea multe componente native atat scade viteza de executie a
programului. De exemplu sa consideram un checkbox, ori de cate ori modificam starea acestuia, acest obiect
comunica cu un obiect checkbox nativ si cele doua obiecte trebuie sa fie ibtotdeauna in aceasi stare, adica
starile lor se modifica in mod sincron. Acesasta soncronizare incetineste viteza de executie a aplicatiei. O alta
problema cu componentele AWT este ca modul de afisare a acestora depinde de platforma pe care se ruleaza
aplicatia. Deci cum arata ele nu depinde de programator ci de componentele native. Pentru aceste neajunsuri
JavaSoft introduce componentele "lightweight". Aceste componente sunt scrise 100% in Java si modul de
afisare astfel depinde doar de programator. In primele versiuni Java clasele Component si Container erau clase
finale, adica nu se puteau extinde. Incepand cu versiunea JDK 1.1 aceste clase nu mai sunt finale, devenind
posibila extinderea lor. Aceste componente nu vor acea perechea lor nativa, astfel nefiind necesara comunicarea
acestora cu perechile lor native si toate acestea conducand la programe mai rapide, iar ele arata pe orice
platforma la fel.  Intr-o aplicatie se poate combina utilizarea componentelor AWT cu cele "lightweight" (desi nu
se recomanda), dar trebuie tinut cont de faptul ca componentele AWT sunt netransparente, ele vor fi
intotdeauna in prim plan, acoperind cele "lightweight".
Exemplul urmator prezinta o componenta ligtweight, care este de fapt un Button, care arata ca si harta statului
Texas. Pentru implementarea acestei componente am utilizat doua clase. O clasa Texas care contine doar doua
variabile statice de tip tablou cu intregi si contin coordonatele punctelor pentru desenarea unui poligon si o alta
clasa care implementeaza componenta. exista si o atreia clasa care afiseaza o fereastra cu trei asemenea
butoane. La apasarea acestor butoane se afiseaza informatii despre buton la iesirea standard.
public class Texas {
  static int X[] = {
    150, 150, 150, 149, 149, 149, 150, 151, 152, 151,
    151, 151, 151, 151, 152, 152, 153, 153, 152, 152, 151, 151, 151, 151, 150,
    149, 150, 149, 147, 147, 147, 147, 145, 144, 143, 142,
    142, 141, 141, 140, 135, 134, 133, 131, 130, 129, 127, 125, 124, 123, 121,
    120, 120, 120, 119, 119, 118, 117, 117, 116, 115, 115, 115, 114, 114, 114,
    114, 113, 112, 111, 109, 108, 108, 106, 106, 105, 104, 104, 101, 101, 99,
    99, 98, 98, 97, 96, 93, 93, 93, 93, 92, 91, 90, 88, 88, 86, 86, 84, 85, 55,
    54, 50, 12, 11, 12, 12, 11, 12, 12, 14, 14, 15, 17, 23, 28, 28, 29, 29, 29,
    30, 30, 30, 32, 39, 45, 46, 47, 48, 49, 51, 52, 53, 55, 55, 56, 56, 57, 58,
    61, 62, 64, 64, 67, 67, 68, 68, 68, 69, 71, 73, 74, 74, 77, 78, 81, 82, 84,
    85, 86, 86, 86, 87, 87, 88, 90, 91, 92, 94, 97, 100, 105, 106, 109, 110, 111,
    112, 113, 113, 112, 112, 111, 109, 108, 110, 109, 109, 109, 109, 109, 109,
    110, 111, 111, 110, 111, 112, 114, 114, 114, 114, 114, 115, 115, 116, 117,
    117, 117, 117, 117, 118, 118, 119, 121, 121, 122, 121, 120, 120, 122, 123,
    124, 124, 124, 124, 124, 125, 129, 135, 135, 138, 139, 137, 138, 139, 140,
    140, 140, 142, 143, 147};
  static int Y[] = {
    98, 98, 98, 97, 96, 96, 95, 94, 91, 90,
    89, 89, 88, 86, 86, 85, 82, 81, 79, 78, 77, 76, 76, 75, 74,
    74, 73, 71, 69, 68, 56, 49, 48, 49, 49, 48,
    48, 48, 47, 47, 44, 45, 45, 45, 45, 45, 45, 46, 46, 47, 46, 46, 45, 45, 45,
    46, 46, 45, 44, 44, 45, 46, 47, 47, 46, 45, 45, 45, 46, 46, 44, 44, 45, 45,
    44, 43, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 40, 40, 39, 39, 38, 37, 38,
    38, 38, 36, 36, 35, 11, 9, 9, 66, 63, 63, 63, 64, 64, 65, 66, 67, 68, 70, 71,
    78, 82, 83, 84, 85, 86, 87, 91, 93, 96, 101, 104, 105, 104, 103, 102, 97, 95,
    95, 95, 95, 95, 94, 94, 95, 95, 96, 96, 96, 97, 98, 99, 99, 99, 100, 102, 104,
    105, 106, 114, 116, 120, 121, 123, 124, 125, 126, 128, 129, 132, 133, 138,
    140, 140, 141, 142, 144, 145, 145, 147, 147, 146, 146, 146, 145, 144, 144,
    143, 137, 134, 131, 130, 129, 129, 129, 129, 129, 128, 124, 124, 122, 121,
    122, 121, 119, 118, 118, 118, 118, 117, 118, 118, 118, 117, 116, 115, 115,
    115, 116, 115, 115, 114, 114, 113, 113, 112, 111, 111, 111, 112, 112, 112,
    112, 111, 107, 105, 102, 102, 100, 99, 98, 98, 99, 100, 100, 100, 98};
  }
import java.awt.*;
import java.awt.event.*;
public class texasButton extends Component {
  boolean inPoly = false;
  boolean mouseDown = false;
  ActionListener listener;
  Polygon poly = new Polygon(Texas.X, Texas.Y, Texas.X.length);
  public texasButton() {
    enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
    setBackground(Color.blue);
    }
  public synchronized void addActionListener(ActionListener l) {
    listener = AWTEventMulticaster.add(listener, l);
    }
  public synchronized void removeActionListener(ActionListener l) {
    listener = AWTEventMulticaster.remove(listener, l);
    }
  public void paint(Graphics g) {
    if (inPoly && mouseDown)
      g.setColor(getBackground().darker().darker());
    else
      g.setColor(getBackground());
    g.fillPolygon(poly);
    }
  public void processMouseMotionEvent(MouseEvent e) {
    if (poly.contains(e.getPoint())) {
      if (!inPoly && mouseDown)
        repaint();
      inPoly = true;
      }
    else {
      if (inPoly && mouseDown)
        repaint();
      inPoly = false;
      }
    }
 
  public void processMouseEvent(MouseEvent e) {
    switch (e.getID()) {
      case MouseEvent.MOUSE_PRESSED :
        if (inPoly) {
          mouseDown = true;
          repaint();
          }
        break;
      case MouseEvent.MOUSE_RELEASED :
        if (inPoly == true && mouseDown == true) {
          if (listener != null)
            listener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
""));
          repaint();
          }
        mouseDown = false;
        break;
      }
    super.processMouseEvent(e);
    }
  public Dimension getPreferredSize() {
    return getMinimumSize();
    }
  public Dimension getMinimumSize() {
    return new Dimension(160,150);
    }
 
  }
import java.awt.*;
import java.awt.event.*;
public class texasTest extends Frame implements ActionListener {
  public static void main(String args[]) {
    texasTest t = new texasTest();
    }
  public void actionPerformed(ActionEvent ae) {
    System.out.println(ae.getSource());
    }
  public texasTest() {
    setBackground(Color.lightGray);
    setLayout( new GridLayout(1, 3));
 
    texasButton cb[] = new texasButton[ 3 ];
    for( int i =0; i<cb.length; i++)
    {
         cb[ i ] = new texasButton();
         cb[ i ].addActionListener(this);
         add(cb[ i ]);
    }
    addWindowListener( new WindowAdapter(){
         public void windowClosing( WindowEvent e ){
             setVisible( false );
             System.exit( 0 );
         }
    });
    setBounds(1,1,500,200);
    setVisible( true );
    }
  }
 
 
 
Arhitectura Model-View-Controller(MVC)
Clasele din pachetul Swing sunt bazate pe arhitectura MVC. Intr-o interfata grafica MVC intotdeauna exista trei
obiecte model, view si controller (structura, reprezentare si comportament).  Modelul este reprezentarea logica,
view-ul este reprezentarea vizuala si controller-ul specifica comportamentul. Separarea acestor elemente
permite urmatoarele:
 Putem avea mai multe view-uri asociate aceluiasi model. De exemplu putem vizualiza aceleasi date sub
forma de tabel sau sub forma de grafic. Modificand datele se modifica ambele reprezentari.
 Modelul nespecificand reprezenarea, putem modifica view-ul fara sa-l afectam modelul atasat
Pentru modelul unui buton putem avea o variabila intreaga cu doua valori 0 si 1, sau o variabila logica cu
valorile false si true.
Look and Feel
In AWT fiecare componenta grafica are o clasa nativa ( clasa peer) care comunica cu componenta nativa a
sistemul de operare. De aceea un obiect Button apare ca si un buton Win95, daca este rulat sub Win95, iar daca
este rulat pe Solaris atunci arata ca un buton Motif. Componentele din pachetul Swing nu mai au aceste clase
peer, si pot arata in mod diferit pe aceasi platforma din cauza ca respecta arhitectura MVC.

Pentru a schimba modul de afisare a componentelor swing se utilizeaza metoda setLookAndFeel() a clasei
UIManager.
try {
    UIManager.setLookAndFeel( "javax.swing.plaf.metal.MetalLookAndFeel");
}
catch (Exception e) {
        System.err.println("Couldn't use the metal "+ "look and feel: " +
e);
}
Utilizarea componentelor Swing
Pachetul swing contine doua tipuri de componente:
 containere (JFrame, JApplet, JWindow, JDialog)
 componente "lightweight" (JButton, JPanel, JList etc. )
Containerele reprezinta cadrul in care aceste componente pot exista. Imaginea urmatoare prezinta ierarhia
componentelor unei aplicatii cu o fereastra, doua butoane, o caseta text si o lista.
 
 
Obiectul contentpane din figura precedenta este un obiect Container obisnuit si este sub containerul swing.
Codul pentru crearea suprafetei grafice:
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout());
panel.add(textField);
panel.add(list);

Container contentPane = this.getContentPane();


contentPane.setLayout(new FlowLayout());
contentPane.add(button1);
contentPane.add(button2);
contentPane.add(panel);
Observatie: Nu se pot adauga componentele direct la containerul swing.
Figura urmatoare contine cateva componente impreuna cu ierarhia de clase in care se incadreaza:

 
 
JLabel
Un JLabel este similar cu o eticehta java.awt.Label, avand o serie de functionalitati in plus:
 permite atasarea unei iconite
 permite pozitionarea verticala si orizontala a textului fata de iconita
 permite pozitionarea relativa a continutului in cadrul componentei
Exemplul urmator utilizeaza interfata Icon:
public interface Icon{
    void paintIcon( Component c, Graphics g, int x, int y );
    int getIconWidth();
    int getIconHeight();
}
Clasa ImageIcon este o implementare a clasei Icon, care creaza o iconita dintr-un obiect de tip Image.
Icon iconPicture = new ImageIcon("Apple.gif");
 
 
 
 
Un exemplu:

Sursa:
import javax.swing.*;
import java.awt.*;

public class SwingPanel extends JPanel


{

  public SwingPanel()
  {
                setLayout( new GridLayout( 3, 1 ));
                
                JLabel simplelabel = new JLabel("This is a simple label");
                add( simplelabel );
                
                JLabel iconlabel = new JLabel("This is a fancy label");
                Icon icon = new ImageIcon("icon.gif");
                iconlabel.setIcon( icon );
                Font font = new Font("Serif",Font.BOLD|Font.ITALIC,30);
                iconlabel.setFont( font );
                add( iconlabel );

                JLabel myiconlabel = new JLabel("This is my icon label");


                Icon myicon = new RedOval();
                myiconlabel.setIcon( myicon );
                add( myiconlabel );
  }
}

import javax.swing.JFrame;

public class SwingFrame extends JFrame


{
    private SwingPanel p;

    public SwingFrame()


    {
        p = new SwingPanel();
        getContentPane().add( p );
    }

    public static void main( String args[] )


    {
        SwingFrame f = new SwingFrame();
        f.addWindowListener( new java.awt.event.WindowAdapter(){
            public void windowClosing( java.awt.event.WindowEvent e )
            {
                System.exit( 0 );
            }
        });
        f.setBounds(1,1,400,300);
        f.setVisible( true );
    }
}
In toate exemplele demonstrative pentru componente se vor utiliza doua clase. O clasa care extinde clasa JPanel
si care contine componenta specifica si una de tip container care extinde clasa JFrame si afiseaza panelul cu
componentele specifice. De aceea in cazul urmatoarelor exemple vom descrie doar constructorul clasei
SwingPanel.
JButton
Un buton JButton se instantieaza analog ca si un butom java.awt.Button. Evenimentul care se produce la
apasarea acestuia este tot un ActionEvent si evenimentul se livreaza tuturor receptorilor inregistrati.Culoarea
butonului este identic cu culoarea containerului, deci trebuie modificat la SystemColor.control.
setLayout( new FlowLayout() );
JButton simplebutton = new JButton("Simple");
add( simplebutton );
simplebutton.setBackground(SystemColor.control);
Icon icon = new ImageIcon("icon.gif");
JButton iconbutton = new JButton("Icon", icon );
iconbutton.setBackground(SystemColor.control);
add( iconbutton );
JTextComponent
JTextComponent este o clasa generala cu toate functiunile unui editor de text simplu. Metodele cele mai
utilizate:
 copy()
 cut()
 paste()
 getSelectedText()
 setSelectionStart()
 setSelectionEnd()selectAll()
 replaceSelection()
 getText()
 setText()
 setEditable()
 setCaretPosition()
Subclasele clasei JTextComponent sunt: JTextField, JTextArea, JTextPane.
setLayout( new BorderLayout());
JTextPane tp = new JTextPane();
MutableAttributeSet attr = new SimpleAttributeSet();
StyleConstants.setFontFamily(attr,"Serif");
StyleConstants.setFontSize(attr,18);
StyleConstants.setBold(attr,true);
tp.setCharacterAttributes( attr, false );
add( tp, BorderLayout.CENTER );
JPasswordField
Este subclasa clasei JTextField. Cu metoda setEchoChar() se poate specifica caracterul care se afiseaza in locul
caracterelor introduse. Implicit este '*'.
setLayout( new FlowLayout() );
JPasswordField pf = new JPasswordField(20);
pf.setEchoChar('*');
add(pf );
JScrollBar
Este varianta "lightweight" a componentei java.awt.Scrollbar.
setLayout( new BorderLayout() );
JScrollBar vertical = new JScrollBar(JScrollBar.VERTICAL,0,5,0,100);
add(vertical, BorderLayout.EAST );
JScrollBar horizontal = new JScrollBar(JScrollBar.HORIZONTAL,0,5,0,100);
add(horizontal, BorderLayout.SOUTH );
JSlider
JSlider este similar cu JScrollBar, dar permite afisarea marcjelor minore respectiv a celor majore precum si 
desenarea unui chenar Border in jurul componentei.

setLayout( new BorderLayout() );

JSlider s1 = new JSlider( JSlider.VERTICAL, 0, 100, 50 );


s1.setPaintTicks( true );
s1.setMajorTickSpacing(10);
s1.setMinorTickSpacing(2);
add( s1, BorderLayout.EAST);

JSlider s2 = new JSlider( JSlider.VERTICAL, 0, 100, 50 );


s2.setPaintTicks( true );
s2.setMinorTickSpacing(5);
add( s2, BorderLayout.WEST);

JSlider s3 = new JSlider( JSlider.HORIZONTAL, 0, 100, 50 );


s3.setPaintTicks( true );
s3.setMajorTickSpacing(10);
add( s3, BorderLayout.SOUTH);

JSlider s4 = new JSlider( JSlider.HORIZONTAL, 0, 100, 50 );


s4.setPaintTicks( true );
s4.setBorder( LineBorder.createBlackLineBorder());
add( s4, BorderLayout.NORTH);
JProgressBar
Se utilizeaza pentru vizualizarea evolutiei unei operatii Pentru a lucra cu o asemenea componenta prima data
trebuie s-o intializam
JProgressBar p = new JProgressBar();
p.setMinimum( 0 );
setMaximum( numar_operatii );
 iar dupa aceea intr-un ciclu sa modificam starea componentei
p.setValue( p.getMinimum());
for( int i=0; i<numar_operatii; i++)
{
//se executa o operatie
p.setValue( i );
}
Exemplu:
import javax.swing.JFrame;

public class SwingFrame extends JFrame


{
        private SwingPanel p;

        public SwingFrame()


        {
                p = new SwingPanel();
                getContentPane().add( p );
                Thread t = new Thread( p );
                t.start();
        }

        public static void main( String args[] )


        {

                SwingFrame f = new SwingFrame();


                f.addWindowListener( new java.awt.event.WindowAdapter(){
                        public void windowClosing( java.awt.event.WindowEvent e )
                        {
                                System.exit( 0 );
                        }
                }
                );
                f.setBounds(1,1,400,300);
                f.setVisible( true );
        }
}
import javax.swing.*;
import java.awt.*;

public class SwingPanel extends JPanel implements Runnable


{

        JProgressBar pb;

        public SwingPanel()


        {
                setLayout( new FlowLayout() );
                pb = new JProgressBar();
                pb.setMinimum(0);
                pb.setMaximum(100);
                pb.setValue( pb.getMinimum());
                add( pb );
        }

        public void run()


        {
                for( int i=0; i<pb.getMaximum(); i++ )
                {
                        pb.setValue( i );
                        try{
                                Thread.sleep( 100 );
                        }
                        catch( InterruptedException e ){}
                }
        }

JComboBox
Componenta este asemanatoare cu Choice din AWT, dar permite si editarea, adica introducerea unei valori care
nu exista in lista.
Exemplu de utilizare:

Sursa:
import javax.swing.*;
import java.awt.*;
public class SwingPanel extends JPanel
{

        String elements[] ={"One","Two","Three","Four","Five"};

        public SwingPanel()


        {
                setLayout( new FlowLayout() );
                JComboBox c1 = new JComboBox();
                JComboBox c2 = new JComboBox();
                for( int i =0; i< elements.length; i++ )
                {
                        c1.addItem( elements[ i ] );
                        c2.addItem( elements[ i ] );
                }
                c1.setEditable( false );
                c2.setEditable( true );
                add( c1 );
                add( c2 );
        }
}

JList
Componenta are doua implementari, una care respecta arhitectura MVC si una care nu, se comporta la fel ca si
o lista AWT. Totusi si aceasta implementare mai simpla s-a imbunatatit. Putem adauga elementele prin
contructor transmitandu-i un tablou de stringuri. JList nu suporta  scroll, adica daca dorim aceasta facilitate
atunci trebuie  s-o punem intr-un ScrollPane.
import javax.swing.*;
import java.awt.*;

public class SwingPanel extends JPanel


{

        String elements[] = {"Orange","Lemon","Strawberry","Raspberry","Apple","Banana"};

        public SwingPanel()


        {
                setLayout( new BorderLayout() );
                JList list = new JList(elements);
                ScrollPane pane = new ScrollPane();
                pane.add( list );
                add( pane, BorderLayout.CENTER );

        }
}

Timer
Pe langa componente grafice, pachetul swing vine si cu o serie de utilitare. Un astfel de utilitar este clasa
Timer. Exemplul urmator este o fereastra cu trei butoane si cu trei obiecte de tip Timer. Fiecare obiect Timer
controleaza un buton, adica la trecerea intervalului specific fiecarui timer se incrementeaza numarul de pe
butonul corespunzator. La crearea  obiectelor de tip Timer specificam intervalul de timp la care actioneaza
respectiv si obiectul listener, adica componenta care este notificata. Timerul trimite mesajul actionPerformed()
dupa trecerea  intervalului de timp.Obiectul Timer trebuie pornit cu metoda start().
Diagrama de clasa
Diagrama de secventiere

Sursa
import javax.swing.JFrame;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.Timer;
import java.awt.event.ActionEvent;

public class MyWindow extends JFrame implements ActionListener 


{
        private JButton b[];
        private Timer t[];

        public MyWindow() 


        {
                final int n = 3;
                int i;
                getContentPane().setLayout( new java.awt.FlowLayout() );
                b = new JButton[ n ];
                for(i=0;i<n;i++)
                {
                        b[i] = new JButton(Integer.toString( i ));
                        getContentPane().add( b[ i ] );
                }
                addWindowListener( new java.awt.event.WindowAdapter(){
                        public void windowClosing( java.awt.event.WindowEvent e )
                        {
                                setVisible( false );
                                System.exit( 0 );
                        }
                });
                t = new Timer[ n ];
                for(i=0;i<n;i++)
                {
                        t[ i ] = new Timer( (i+1)*100, this );
                        t[ i ].start();
                }
        }

        /**
        @roseuid 3A72EE9202F8
        */
        public static void main(String[] args) 
        {
                MyWindow w = new MyWindow();
                w.setBounds(1,1,400,300);
                w.setVisible( true );
        }

        /**
        @roseuid 3A72F8A50122
        */
        public void actionPerformed(ActionEvent arg0) 
        {

                if( arg0.getSource() == t[0] )


                {
                        System.out.println( "Button: "+0);
                       
b[0].setLabel( Integer.toString( Integer.parseInt(b[0].getLabel())+1 ));

                }
                else
                        if( arg0.getSource() == t[1] )
                        {
                                System.out.println( "Button: "+1);
                               
b[1].setLabel( Integer.toString( Integer.parseInt(b[1].getLabel())+1 ));

                        }
                        else
                        if( arg0.getSource() == t[2] )
                        {
                                System.out.println( "Button: "+2);
                               
b[2].setLabel( Integer.toString( Integer.parseInt(b[2].getLabel())+1 ));

                        }
        }
}
Specificatie problema
Pe o tabla de dimensiune n*n avem asezate butoane cu etichetele de la 1 la n*n -1. Exista si o gaura. Jucatorul
trebuie sa aranjeze butoanele in ordine crescatoare. Se cere sa se scrie un program care afiseaza o configuratie
rezolvabila si sa permita jucatorului sa efectueze mutari pana cand reuseste sa aranjeze butoanele in ordine
crescatoare. Pentru n=4 o configurare initiala a tablei ar putea fi urmatoarea:
 
Executie aplet:

Perspectiva conceptuala
Pentru acest joc conceptele cu care vom lucra sunt urmatoarele:o tabla de joc care sta si pe post de container si
contine butoanele. Butoanele au ca etichete numerele intregi de la 1 la n*n -1, unde n reprezinta dimensiunea
tablei. Butoanele trebuie mutate, o mutare insemnand interschimbarea a doua butoane pe tabla. Jucatorul
interactioneaza cu tabla cu ajutorul unui mouse. Daca face clic pe un buton care are si un vecin gaura, atunci se
interschimba pozitia celor doua butoane, altfel nu se intampla nimic. Jocul se termina in momentul in care
jucatorul a reusit sa aseze in ordine crescatoare butoanele. Clasele identificate ar fi urmatoarele:
Board: este abstractizarea tablei de joc
responsabilitati:
 tratarea interactiunilor cu jucatorul
 identificarea butonului care a fost apasat
Square: este abstractizarea unui buton
responsabilitati:
 verifica daca butonul are sau nu vecin gaura
 identificarea gaurii
Move: abstractizarea unei mutari
 executa inetrschimbarea butoanelor
Diagrama de clasa din punct de vedere conceptual:
Un obiect de tip Board contine n*n butoane, adica obiecte de tip Square. Aceasta asociere este una de continere
si se va realiza ca si un tablou bidimensional. Un obiect Move ca sa faca interschimbarea a doua butoane
trebuie sa cunoasca atat obiectul tabla de joc (Board) cat si butonul apasat (Square).
 
Perspectiva specificativa
La aceasta perspectiva incercam sa identificam atributele si operatiile claselor. O prima versiune a diagramei ar
fi urmatoarea:

Un obiect Board trebuie sa stie cum afiseaza tabla de joc impreuna cu componentele. Responsabilitatea afisarii
componentelor ii revine metodei paint(). Pe langa faptul ca trebuie sa stie cum sa afiseze butoanele, trebuie sa
raspunda la interactiunile utilizatorului. Pentru a prelucra aceste interactiuni introducem metoda
eventHandler().
Un obiect Square poate fi intrebat daca are sau nu vecin gaura hasHoleNeighbour(), iar la nevoie trebuie sa
returneze acesta getHoleNeighbour(). Gaura va fi tot un buton, dar unul ascuns.
Obiectul Move trebuie sa stie doar sa interschimbe doua butoane de pe tabla, execute(). De aceea trebuie sa
cunoasca atat tabla cat si butonul implicat in actiune.
Diagrama de secventiere corespunzator apasarii unui buton cu vecin gaura ar fi urmatoarea:

Perspectiva implementarii

Vom implementa clasa Board ca un aplet, apletul fiind si un container, deci va putea sa contina si butoanele.
Suprafata apletului va contine tabla de joc si inca un buton pentru amestecarea celorlaltor butoane. Tratarea
evenimentelor legate de apasarea butoanelor se va face cu operatia actionPerformed(), operatie care se
adauga prin specificarea ca clasa Board implementeaza interfata ActionListener. Fiindca in Rational Rose in
cazul asocierii de tip continere cu multiplicitatea n, se genereaza o referinta de tip tablou unidimensional si
fiindca noi avem nevoie de tablou bidimensional, n-am mai pus aceasta asociere, in loc de aceasta am introdus
atributul squares in clasa Board. Metoda init() este o metoda mostenita de la clasa Applet si face initializarea
apletului. Metoda shuffle() face amestecarea butoanelor.
Clasa Square specializeaza clasa Button din pachetul java.awt.

Surse Java:
//Source file: c:\Rose\TiliToli\Board.java
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.applet.Applet;
import java.awt.event.ActionListener;
import java.awt.Button;
import java.awt.Panel;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
public class Board extends Applet implements ActionListener
{
   private int dim;
   public Square[][] squares;
   private int squareHeight;
   private int squareWidth;
   private Button shuffleButton;
   private Panel panel;
 
   public void init()
   {
   int i,j;
      String s;
      s = getParameter("dim");
      if (s != null) dim = Integer.parseInt(s);
      else
          dim =3;
 
     // Se aloca memorie pentru dim referinte
     squares = new Square[ dim ][];
     squareHeight = (getSize().height-50) / dim;
     squareWidth  = (getSize().width -50) / dim;
      setLayout( new java.awt.BorderLayout());
      panel = new Panel();
      add("Center",panel);
      panel.setLayout( new java.awt.GridLayout( dim, dim, 5, 5));
      for(i=0; i<dim; i++)
      {
      // Se aloca memorie pentru un rand
          squares[ i ] = new Square[ dim ];
          for(j=0; j<dim; j++)
          {
           // Se aloca memorie pentru un singur patratel
           squares[i][j]= new Square(i,j,squareWidth,squareHeight, Integer.toString(i*
dim+j),
            this);
           panel.add( squares[i][j]);
           squares[i][j].addActionListener( this );
           if( i== 0 && j==0 ) squares[i][j].setVisible( false );
      }
     }
     shuffleButton = new Button("Shuffle");
     add("South", shuffleButton );
     shuffleButton.addActionListener( this );
   }
   public void paint(Graphics g)
   {
     int i,j;
     for( i=0; i<dim; i++)
      for(j=0; j<dim; j++)
        if( (squares[i][j].getLabel()).equals("0") )
           squares[i][j].setVisible(false);
        else
            squares[i][j].setVisible(true);
   }
 
   public void actionPerformed(ActionEvent arg0)
   {
    String command = arg0.getActionCommand();
    if( command.equals("Shuffle") )
          shuffle();
    else
      // Button with number
    {
       Square s = ( Square )arg0.getSource();
       Move move = new Move( this , s );
       if( !move.execute() )
            showStatus( "Illegal move" );
       else
            showStatus( "Legal move" );
       repaint();
    }
   }
 
   public void shuffle()
   {
    int i, j, nr, n = dim*dim;
    int step[][] = { {-1,0} ,{ 0,1},{1,0}, {0,-1}};
    int temp[][]=new int[ dim ][];
    for(i=0; i<dim; i++)
      temp[ i ] = new int[ dim ];
    for(i=0;i<dim; i++)
      for(j=0;j<dim; j++)
       temp[ i ][ j ]= i* dim+j;
    java.util.Random generator = new java.util.Random();
    int newpoz0x, newpoz0y;
    int poz0x=0, poz0y=0;
    for(i=0; i<50 ; i++)
    {
      boolean ok = false;
      do{
       nr = generator.nextInt();
       if( nr < 0 ) nr = -nr;
       nr = nr % 4;
       newpoz0x = poz0x + step[ nr ][ 0 ];
       newpoz0y = poz0y + step[ nr ][ 1 ];
       if( newpoz0x >=0 && newpoz0x <dim &&
            newpoz0y >=0 && newpoz0y <dim)
            ok = true;
      } while( !ok );
      //Interchanges two elements of temp
      int c = temp[ poz0x ][ poz0y ];
      temp[ poz0x ][ poz0y ] = temp[ newpoz0x ][ newpoz0y ];
      temp[ newpoz0x ][ newpoz0y ] = c;
      poz0x = newpoz0x;
      poz0y = newpoz0y;
     }
     String rez="";
     for(i=0; i<dim; i++)
      for(j=0;j<dim; j++)
       rez=rez+Integer.toString(temp[ i ][ j ] )+' ';
     showStatus(rez);
     for( i=0; i<dim; i++ )
      for( j=0; j<dim; j++ )
       squares[ i ][ j ].setLabel( Integer.toString( temp[ i ][j ]));
     repaint();
   }
 
   public int getDim()
   {
    return dim;
   }
}
//Source file: c:\Rose\TiliToli\Square.java
import java.awt.Button;
public class Square extends Button
{
   private int i;
   private int j;
   private int height;
   private int width;
   private String label;
   public Board board;
   public Square(int i, int j, int height, int width, String label, Board board)
   {
     super( label );
     this.i = i;
     this.j = j;
     this.height = height;
     this.width  = width;
     this.label = label;
     this.board = board;
   }
 
   public int getRow()
   {
    return i;
   }
 
   public int getColumn()
   {
    return j;
   }
 
   public boolean hasHoleNeighbour(int i, int j)
   {
     if( (i-1)>=0)
       if ( (board.squares[i-1][j]).getLabel().equals("0" )) return true;
     if( (j+1)<board.getDim())
       if ( (board.squares[i][j+1]).getLabel().equals("0" )) return true;
     if( (i+1)<board.getDim())
       if ( (board.squares[i+1][j]).getLabel().equals("0" )) return true;
     if( (j-1)>=0)
       if ( (board.squares[i][j-1]).getLabel().equals("0" )) return true;
    return false;
   }
 
   public Square getHoleNeighbour(int i, int j)
   {
    if( (i-1)>=0)
       if ( (board.squares[i-1][j]).getLabel().equals("0" )) return board.squares[i-1]
[j];
    if( (j+1)<board.getDim())
       if ( (board.squares[i][j+1]).getLabel().equals("0" )) return board.squares[i]
[j+1];
    if( (i+1)<board.getDim())
       if ( (board.squares[i+1][j]).getLabel().equals("0" )) return board.squares[i+1]
[j];
    if( (j-1)>=0)
       if ( (board.squares[i][j-1]).getLabel().equals("0" )) return board.squares[i][j-
1];
    // Never gets here
    return board.squares[i][j];
   }
}
//Source file: c:\Rose\TiliToli\Move.java
 
public class Move
{
   public Square square;
   public Board board;
   public Move()
   {
   }
   public Move(Board board, Square square)
   {
    this.board = board;
    this.square = square;
   }
 
   public boolean execute()
   {
    int row = square.getRow();
    int col = square.getColumn();
    if( square.hasHoleNeighbour(row , col )){
          Square s = square.getHoleNeighbour( row , col );
          String label = s.getLabel();
          s.setLabel( square.getLabel());
          square.setLabel( label );
          return true;
     }
     else
          return false;
   }
}
 
Laborator 1.
1. Scrieti o aplicatie care afiseaza argumentele primite de lalinia de comanda numerotandu-le.
Exemplu:  ( public class p_1_1{...} )
java p_1_1 aaaa bbb ccccc
Rezultat:
0. aaaa
1. bbb
2. ccccc

2. Scrieti o aplicatie care calculeaza suma argumentelor.Parametrii neintregi sunt considerate ca au valoarea 0.
Indicatie: pentru conversia String --> int se utilizeaza metoda statica parseInt a clasei Integer: public static
int parseInt( String  s)
Exemplu:  ( public class p_1_2{...} )
java p_1_2 10  20  30
Rezultat:
s=60
Exemplu:
java p_1_2 100  21.6  abc  10
Rezultat:
s=110
3. Scrieti o aplicatie care calculeaza suma argumentelor. Parametriinenumericisunt considerate ca au valoarea
0.
Indicatie: pentru conversia String --> double se utilizeaza metoda statica parseDouble a clasei Double: public
static double parseDouble( String  s)
Exemplu:  ( public class p_1_3{...} )
java p_1_3 100  21.6  abc  10
Rezultat:
s=131.6
4. Scrieti o aplicatie care afiseaza suma argumentelor de ordinpar si de ordin impar. Parametrii nenumerici sunt
considerate caau valoarea 0.
Exemplu: ( public class p_1_4{...} )
java p_1_4 10  20  30 40 aaa 50 60 bbb
Rezultat:
s_par=100
s_impar=110
5. Scrieti o aplicatie care afiseaza argumentele converind minusculelela majuscule.
Indicatie: se utilizeaza metoda toUpperCase() a clasei String.Operatia inversa se poate realiza cu metoda
toLowerCase().
 
Laborator 2.
1.  Initializati si afisati un tablou bidimensional in modulurmator:
    1
    2    3
    4    5    6
    7    8    9    10
    ...
Numarul liniilor se primeste de la linia de comanda, in caz contrarsa fie 3. Tratati toate tipurile de exceptii
posibile (ArrayIndexOutOfBoundsException - daca nu avem argumente si ne referim lla primul argument prin
args[0],NumberFormatException, daca argumentul nu este de tip intreg si noi incercamsa-l convertim la acest
tip).
2. Numerotati cuvintele unei propozitii. Propozitia se declaraca si un sir constant.
Indicatie: se va utiliza clasa StringTokenizer dinpachetul java.util.
3.  Definiti o clasa Persoana cu doua atribute, nume sivarsta si cu metodele setNume(), getNume(), setVarsta(),
getVarsta(), toString()(pentruserializarea obiectului).
Definiti o clasa cu numele Operatii si efectuati urmatoarele operatii:
-creati o lista dublu inlantuita cu 5 obiecte de tip Persoanasi parcurgeti-o de la inceput spre sfarsit si invers.
-afisati lista in ordine crescatoare utilizand metoda sort() a claseiCollections
4. Scrieti o aplicatie care primeste de la linia de comanda o listade cuvinte si pentru fiecare cuvant afiseaza si
clasa de anagrame caruiaapartine.
Exemplu:
java p_2_4 corp amar porc rama mara
Rezultat:
corp: [ corp, porc]
amar: [ amar. mara, rama ]
porc: [ corp, porc]
rama: [ amar. mara, rama ]
mara: [ amar. mara, rama ]
Indicatie: Se pot utiliza clasele Map sau List pentru obtinereaclaselor de anagrame
5. Scrieti o aplicatie care primeste de la linia de comanda olista de cuvinte. Afisati lista unicatelor si lista
duplicatelor din listacuvintelor.
Indicatie: Se utilizeaza implementarea HashSet a interfeteiSet
Laborator 3.
1. Scrieti o aplicatie care sa contina doua metode statice, una pentrucalculul factorialului si una pentru
combinari din n luate cate k.
2. Scrieti o aplicatie care sa evalueze o expresie aritmetica in formapostfixata. Pentru ca prelucrarea sirului sa
fie mai simpla, se consideraca intre operanzi si operatori intotdeauna exista cate un spatiu. Astfelexpresia
postfixata pentru (20+30)*5 va avea urmatoarea forma: 20 30  + 5 *.
 
Laborator 4.
1.
2. Afisati argumentele liniei de comanda intr-o ordine aleatoare utilizand metoda shuffle() a clasei Collections.
Laborator 5.
1. Sa se scrie o aplicatie care afiseaza o eticheta care se misca aleator la intervale de 3 secunde.
Indicatie: se va utiliza clasa javax.swing.Timer
Problema 1.1.
public class p_1_1{
    public static void main( String args[] ){
        for( int i=0;i<args.length; i++ )
             System.out.println( i+". "+args[ i ] );
    }
}
Problema 1.2.
public class p_1_2{
    public static void main( String args[] ){
     long s=0;
     int nr;
     for( int i=0;i<args.length; i++ )
     {
         try{
          nr = Integer.parseInt( args[i] );
         }
         catch( NumberFormatException e )
         {
          nr = 0;
         }
         s += nr;
     }
     System.out.println( "Suma="+s );
    }
}
Problema 1.3.
public class p_1_3{
    public static void main( String args[] ){
         double s=0;
         double nr;
         for( int i=0;i<args.length; i++ )
         {
             try{
              nr = Double.parseDouble( args[i] );
             }
             catch( NumberFormatException e )
             {
              nr = 0;
             }
             s += nr;
         }
         System.out.println("Suma="+s );
    }
}
Problema 1.4.
public class p_1_4{
    public static void main( String args[] ){
         double s_par=0,s_impar=0;
         double nr;
         for( int i=0;i<args.length; i++ )
         {
             try{
                  nr = Double.parseDouble( args[i] );
             }
             catch( NumberFormatException e )
             {
                  nr = 0;
             }
             if( i % 2 == 0 )
                  s_par += nr;
             else
                  s_impar +=nr;
         }
         System.out.println("S_par="+s_par );
         System.out.println("S_impar="+s_impar);
    }
}
Problema 1.5.
public class p_1_5{
    public static void main( String args[] ){
     for( int i=0;i<args.length; i++ )
     {
         System.out.print(args[ i ].toUpperCase()+" " );
     }
    }
}
Problema 2.1.
public class p_2_1{
    public static void main( String args[] ){
         int tablou[][];
         int n;
         try{
             n = Integer.parseInt( args[ 0 ] );
         }
         // Daca sirulargumentelor este vid
         catch( ArrayIndexOutOfBoundsException e1 )
         {
             n=3;
         }
         // Daca primulargument nu este de tip intreg
         catch( NumberFormatException e2 )
         {
             n = 3;
         }
         int k =0;
         tablou = new int[n ][];
         for( int i=0;i<n; i++ )
         {
             tablou[ i ] = new int[ i+1 ];
             for( int j=0; j<=i; j++){
                  tablou[i][j] = ++k;
                  System.out.print( tablou[i][j]+" ");
             }
             System.out.println();
         }
    }
}
Problema 2.2.
import java.util.StringTokenizer;
public class p_2_2{
    public static void main( String args[] ){
     String line ="Aceasta este o propozitienormala.";
     StringTokenizer stk = new StringTokenizer(line);
     while( stk.hasMoreTokens() )
         System.out.println(stk.nextToken() );
    }
}
 
Problema 2.3.
import java.util.*;
public class p_2_3{
    public static void main( String args[] )
    {
         String tnume[]={"Maria","Sofia","Mircea","Ioan","Adrian"};
         int tvarsta[]={20,24,30,18,16};
         LinkedList list= new LinkedList();
         // Creare lista
         for(int i=0; i<tnume.length;i++)
             list.add( new Persoana( tnume[ i ],tvarsta[ i ] ));
         // Parcurgere lista inceput --> sfarsit
         ListIterator iterator = list.listIterator();
         while( iterator.hasNext() )
             System.out.println( iterator.next() );
         // Parcurgere lista sfarsit --> inceput
         while( iterator.hasPrevious() )
             System.out.println( iterator.previous() );
         // Afisarea listei in ordine crescatoare
         Collections.sort(list ) ;
         System.out.println(list );
 
    }
}
 
public class Persoana implements Comparable{
    private String nume;
    private int varsta;
    public Persoana( String nume, int varsta )
    {
         this.nume = nume;
         this.varsta =varsta;
    }
    public void setNume( String nume ){
         this.nume = nume;
    }
    public void setVarsta( int varsta ){
         this.varsta =varsta;
    }
    public String getNume(){
         return nume;
    }
    public int getVarsta(){
         return varsta;
    }
    public String toString(){
         return nume+""+varsta;
    }
    public int compareTo( Object o ){
         Persoana p = (Persoana) o;
         int nameComp =nume.compareTo( p.getNume() );
         if ( nameComp!= 0 ) return nameComp;
         else
             return (new Integer(varsta)).compareTo( new Integer(p.getVarsta()) );
    }
}
Problema  2.4.
Clasa Element este o clasa care contine doua atribute
     val: pentru stocarea unui cuvant
    cheie: caracterele din cuvant in ordine alfabetica,pe baza acestui atribut putem compara elementele
Aplicatia creaza un obiect de tip ElementList din sirul argumentelor  sortandu-le elementele listei, astfel
elementele claselor de anagrame vor fi consecutive in aceasta lista. Se pot ordona doar colectiile de elemente
ale caror elemente sunt obiecte comparabile. De aceea clasa Element implementeaza  interfata Comparable si
defineste metoda compareTo( Object ), care este metoda pe baza careia se compara obiectele de tip Element.
Metoda clasa( String ) a clasei ElementList returneaza o sublista care contine clase de anagrame careia apartine
sirul specificat
 
public class Element implements Comparable{
    String val;
    String cheie;
    public Element( String val ){ this.val = val;this.cheie = alfabetic( val ); }
    public static  String alfabetic( Strings ){
         char str[];
         str = s.toCharArray();
         int i,j,n=str.length;
         for(i=0;i<n;i++)
             for(j=i+1;j<n;j++)
                  if( str[i] > str[j] ){
                      char tmp = str[ i ];
                      str[ i ] = str[ j ];
                      str[ j ] = tmp;
                  }
         return new String( str );
    }
    public String getValoare(){ return val; }
    public String getCheie(){ return cheie; }
    public String toString(){
         return val;
    }
    public int compareTo( Object o ){
         Element e = (Element ) o;
         int cheiecomp= cheie.compareTo( e.getCheie());
         if( cheiecomp!= 0 ) return cheiecomp;
         else
             return val.compareTo( e.getValoare());
    }
}
 
import java.util.*;
public class ElementList{
    List l;
    public ElementList( String cuvinte[] ){
         l = new ArrayList();
         for( int i=0;i<cuvinte.length;i++ )
         {
             l.add( new Element( cuvinte[ i ] ));
         }
         Collections.sort(l );
    }
    public List clasa( String s ){
         String str = Element.alfabetic( s );
         int first =-1,last =-1;
         ListIterator i= l.listIterator();
         while( i.hasNext()){
             Element e = (Element)i.next();
             if( e.getCheie().equals( str ) )
             {
                  if (first == -1 ) first = l.indexOf( e );
                 last = l.indexOf( e );
             }
         }
         if( first == -1&& last == -1) return null;
         else
                 return l.subList(first, last+1);
        }
 
}
 
import java.util.*;
public class p_2_4{
    public static void main( String args[] )
    {
         ElementList list= new ElementList( args );
         for( int i =0;i<args.length; i++ )
         {
             List l =list.clasa( args[ i ] );
             System.out.println( l );
         }
    }
}
Problema 2.5.
import java.util.*;
public class FindDups2 {
    public static void main(String args[]) {
        Set uniques = new HashSet();
        Set dups = new HashSet();
       for (int i=0; i<args.length; i++)
           if (!uniques.add(args[i]))
               dups.add(args[i]);
           uniques.removeAll(dups);  // Se modifica multimea uniques, se elimina
duplicatele
          System.out.println("Unicate:    " + uniques);
          System.out.println("Duplicate:  " + dups);
  }
}
Problema 3.1.
public class p_3_1{
 
 public static long fact( int n ){
     if( n == 0 || n == 1 ) return 1;
     else{
      long p =1;
      for( int i=2; i<=n ; p=p*i,i++);
          return p;
     }
 }
 
 public static long comb ( int n, int k )
 {
     return fact(n)/(fact(n-k)*fact(k));
 }
 
 public static void main( String args[] )
 {
     for( int i=0;i <20; i++)
         System.out.println(i+"! = "+fact( i ));
     System.out.println("Triunghiul lui Pascal: ");
     for( int i =0; i<5; i++ )
     {
          for( intj =0; j<=i; j++ )
              System.out.print( comb(i,j)+" " );
          System.out.println();
     }
 
 }
}
Problema 3.2.
Atentie, aceasta rezolvare nu este nici pe de parte una generala.  Ne-am limitat doar la operatorii binari +, -, *, /
si la operanzi reale,in plus se lucreaza cu expresii in forma postfixata.
Exemple de executie:
java p_3_2    20    30     +    5    /
java p_3_2    10    20    +    10    5    -    /
Atentie!! Daca executati programul de la un terminal Unix sa nu utilizati * pentru inmultire, deoarece aceasta se
va expanda de catre interpretorul de comenzi la toate fisierele catalogului curent:
// Clasa pentru exceptie pentru expresii incorecte
import java.util.*;
class ExpressionException extends Exception{
    public ExpressionException( String messagge)
    {
         super( messagge);
    }
}
 
public class p_3_2{
    public static void main( String args[] )
    {
     if( args.length == 0 )
     {
          System.out.println("Utilizare : java p_3_2 expresie_postfixata_speciala");
          System.exit( 0 );
     }
     StringBuffer str = new StringBuffer();
     for( int i=0; i<args.length; i++ )
     {
         str.append( args[i ] );
         str.append(" ");
     }
     try{
         Expresie expr= new Expresie( str.toString());
         System.out.println("Valoarea expresiei: "+expr.eval());
     }
     catch( ExpressionException e )
     {
         System.out.println( "Expresie incorecta ");
     }
   }
 
}
 
class Expresie
{
    private String rpnexpr;
    private Stack operanzi;
 
    public Expresie( String rpnexpr ){
     this.rpnexpr = rpnexpr;
     operanzi  = new Stack();
 
    }
 
    private boolean eOperator( String token )
    {
         if( token.charAt(0 ) == '+' || token.charAt( 0 ) == '-' ||
         token.charAt(0 ) == '*' || token.charAt( 0 ) == '/' )
             return true;
         else
             return false;
    }
 
    private Double valOperand( String token )
    {
         Double rez = null;
         try{
             rez=new Double( token );
         }
         catch( NumberFormatException e )
         {
             System.out.println("Expresie incorecta");
         }
         return rez;
    }
 
    public double eval( ) throws ExpressionException
    {
         StringTokenizerstk = new StringTokenizer( rpnexpr );
 
         while( stk.hasMoreTokens() )
         {
             Double op1, op2;
             String token = stk.nextToken();
             System.out.println("Token: "+token);
             if ( eOperator( token ) )
             {
              try{
                  op2 = ( Double )operanzi.pop();
                  op1 = ( Double )operanzi.pop();
              }
              catch( EmptyStackException e )
              {
                  throw new ExpressionException("Expresie incorecta");
              }
 
              // Efectuarea operatiei
              Double rez=null;
              switch( token.charAt( 0 ) )
              {
                  case '+': rez = new Double( op1.doubleValue() + op2.doubleValue() );
                        break;
                  case '-': rez = new Double( op1.doubleValue() - op2.doubleValue() );
                        break;
                  case '*': rez = new Double( op1.doubleValue() * op2.doubleValue() );
                        break;
                  case '/': rez = new Double( op1.doubleValue() / op2.doubleValue() );
                        break;
              }
              operanzi.push( rez );
             }
             // e operand
             else
              operanzi.push( valOperand( token ) );
         }
         if ( operanzi.size() != 1 )
             throw new ExpressionException("Expresie incorecta");
         else
             return ((Double) operanzi.pop()).doubleValue();
    }
 
 
}
Problema 5.1.

import java.awt.Frame;
import java.awt.event.ActionListener;
import java.awt.Label;
import javax.swing.Timer;
import java.awt.event.ActionEvent;

public class Game extends Frame implements ActionListener


{
      Label label;
      Timer timer;

public Game()
{
          setLayout( new java.awt.FlowLayout() );
          label = new Label("Iti plac???");
          add( label );

          timer = new Timer( 300, this );


          timer.start();

          addWindowListener( new java.awt.event.WindowAdapter(){


              public void windowClosing( java.awt.event.WindowEvent e )
               {
                   setVisible( false );
                   System.exit( 0 );
               }
           });
          setBounds( 1, 1, 400,300);
          setVisible( true );
}

/**
@roseuid 3A93AB2901F4
*/
public static void main(String[] args)
{
           Game g = new Game();
}

/**
@roseuid 3A93AE6802B2
*/
public void actionPerformed(ActionEvent arg0)
{
           int x,y;
           java.awt.Point p = label.getLocation();
           java.util.Random rnd = new java.util.Random();
           x = ( int )p.getX(); y = ( int )p.getY();

           java.awt.Dimension d = getSize();
           int height = ( int )d.getHeight();
           int width  = ( int )d.getWidth();

           int dir = rnd.nextInt();


           if( dir % 2 == 0 )
           x = (x + rnd.nextInt()%10) % width;
           else
               x =( x - rnd.nextInt()%10) % width;

           dir = rnd.nextInt();
           if( dir % 2 == 0 )
               y = (y + rnd.nextInt()%10) % height;
           else
           {
               y = (y - rnd.nextInt()%10) % height;
               if ( y<20) y=20;
           }
          label.setLocation( new java.awt.Point( x, y ) );
}
}

Programarea multithreading
în limbajul Java
Pelin Nicoleta Corina şi Pelin Cristian Florentin
O noţiune fundamentală în înţelegerea corectă a thread-urilor este noţiunea de interfaţă. Cea mai folosită
interfaţă din Java este Runnable. Deoarece thread-urile implementează această interfaţă, am considerat necesară
o introducere a acestei noţiuni.
Interfeţe
Noţiuni introductive: În Java, o interfaţă încapsulează un set coerent de servicii şi atribute, fără a atribui aceste
funcţionalităţi la obiecte particulare sau la un cod particular. O interfaţă are membrii de tip constant şi metode
abstracte. Acest tip (interfaţă) nu are o implementare ci doar o �semnătură�. Într-o declaraţie de tip interface,
se specifică tipul de date întors şi parametri de intrare pentru fiecare metodă a interfeţei. O clasă poate fi
declarată ca o implementare directă a unei sau mai multor interfeţe, însemnând că toate metodele abstracte ale
interfeţei trebuie implementate.
Exemplu:
class Point{int x,y;}
interface Point {
void move(int dx, int dy);
}
În exemplu de mai sus, va apărea o eroare de compilare deoarece o clasă şi o interfaţă nu pot avea acelaşi
nume.
Iniţializarea câmpurilor din interfeţe: Toţi membrii interfeţei sunt implicit de tip public.
Fiecare câmp din corpul interfeţei trebuie să aibă o expresie de iniţializare, care nu trebuie să fie o expresie
constantă (adică declarată cu cuvântul cheie final). Variabila de iniţializare este evaluată şi asignată exact o
dată, când interfaţa este iniţializată.
O eroare de compilare apare dacă o expresie de iniţializare pentru un câmp conţine o referinţă la un nume de
câmp care este declarat mai târziu. Astfel:
interfaceTest{
float f=j;
int j=1;
int k=k+1;
}
provoacă două erori de compilare deoarece j este referit la iniţializarea lui f, înainte ca j să fie declarat şi
deoarece iniţializarea lui k se referă la însuşi k.
Câmpuri moştenite în mod ambiguu: O interfaţă poate extinde altă interfaţă predefinită, sau definită de
programator.
Exemplu:
interface BaseColors{
int RED=1,GREEN=2,
BLUE=4;
}
interface RainbowColors extends BaseColors{
int YELLOW=3,ORANGE=
5,INDIGO=6,VIOLET=7;
}
interface PrintColors extends BaseColors{
int YELLOW=8,CYAN=
16,MAGENTA=32;
}
interface LotsOfColors extends RainbowColors, PrintColors{
int FUCHSIA=17,
VERMILION=43,
CHARTREUSE=RED+90;
}
Interfaţa LotsOfColors moşteneşte 2 câmpuri numite YELLOW. Acest lucru este corect atâta timp cât interfaţa
nu conţine o referinţă la câmpul YELLOW.
Chiar dacă interfaţa PrintColors ar avea pentru YELLOW valoarea 3 în loc de 8, o referinţă la câmpul
YELLOW, din interfaţa LotsOfColors, tot ar fi fost considerată ambiguă.
Declaraţiile metodelor abstracte: Fiecare metodă declarată în corpul unei interfeţe este implicit de tipul
abstract. Pentru compatibilitate cu versiunile mai vechi de Java, este permisă dar nu este recomandată
specificarea cuvântului cheie abstract pentru metodele din interiorul unei interfeţe.
Fiecare metodă declarată, în corpul unei interfeţe, este implicit de tipul public. Este permisă dar nu este
recomandată specificarea cuvântului cheie public pentru metodele din interiorul unei interfeţe.
Dacă o metodă declarată într-o interfaţă este declarată de tipul native sau synchronized, atunci va apărea eroare
la compilare, deoarece aceste cuvinte cheie descriu proprietăţile unei implementări decât proprietăţile interfeţei.
Oricum o metodă declarată într-o interfaţă poate fi implementată ca o metodă de tip native sau synchronized.
Dacă o metodă dintr-o interfaţă este declarată de tip final, atunci va apărea o eroare de compilare. Oricum orice
metodă a interfeţei poate fi implementată ca o metodă de tip final.
Interfaţă de tip Runnable:
Una din cele mai folosite interfeţe din Java este interfaţa Runnable. Este foarte importantă deoarece este
implementată de clasa Thread (fir de execuţie, în sensul pe care îl are şi în C++)
Interfaţa Runnable asigură un protocol comun obiectelor care doresc să-şi execute codul, atâta timp cât sunt
active. Un obiect este activ dacă a fost lansat în execuţie şi nu a fost oprit. Inima unui obiect, care
implementează clasa Runnable, este metoda run(). Această metodă trebuie suprascrisă de obiectul de tip
Runnable. De altfel, este singura metodă a interfeţei Runnable.
Interfaţa se numeşte Runnable şi nu Running, deoarece ea nu se execută chiar tot timpul. Pe marea majoritate a
maşinilor se află un singur procesor care este ocupat şi cu alte sarcini. Doar o parte din timpul de lucru al
procesorului este alocată obiectului de tip Runnable. Exemplu:
import java.io.*;
// clasa Simple afiseaza message de number ori
class Simple implements Runnable{
protected String message;
protected int number;
// constructorul clasei
public Simple (String m, int n){
message = m;
number=n;
}
public void run(){
int i=0;
while(i++<number){
System.out.println(mes
sage);
}
}
}
public class prima_ora{
public static void main(String args[]){
Simple ob1=new Simple("par",10);
Simple ob2=new Simple("impar",9);
ob1.run();
ob2.run();
}
}
Rezultatul execuţiei programului precedent va fi afişarea mesajului par de 10 ori şi a mesajului impar de 9 ori.
Se poate atribui un obiect altui obiect. Exemplu:
import java.io.*;
// clasa Simple afisează message de number ori
class Simple implements Runnable{
protected String message;
protected int number;
// constructorul clasei
public Simple (String m, int n){
message = m;
number=n;
}
public void run(){
int i=0;
while(i++<number){
System.out.println(mes
sage);
}
}
}
public class atribuire{
public static void main(String args[]){
Simple ob1=new Simple("par",4);
Simple ob2=new Simple("impar",3);
ob1.run();
ob2.run();
ob1=ob2;
System.out.println("după atribuire");
ob1.run();
System.out.println("OK");
ob2.run();
}
}
Rezultatul execuţiei: afişarea mesajului par de 4 ori, afişarea mesajului impar de 3 ori, afişarea mesajului după
atribuire, afişarea mesajului impar de 3 ori, afişarea mesajului OK,afişarea mesajului impar de 3 ori.
Limitări ale paralelismului: Limbajul Java este conceput pentru a suporta programarea concurentă. Fără
îndoială, caracterul de multithreading al aplicaţiilor este dezvoltat tot mai mult în industria soft. Pe lângă
avantajele oferite de astfel de aplicaţii, apar şi unele puncte delicate care se cer cu grijă tratate de către
programatori.
Siguranţa: Când thread-urile nu sunt complet independente, în timpul execuţiei, fiecare poate influenţa celelalte
thread-uri. Pentru a evita acest lucru, obiectele de tip thread pot folosi mecanismele de sincronizare sau tehnici
de excluziune care să prevină intercalarea execuţiilor. Utilizarea thread-urilor multiple, implicând obiecte
proiectate pentru a lucra în mod secvenţial, conduce la programe greu de citit şi greu de depanat.
Timpul de viaţă: Activităţile din programele concurente se pot opri pur şi simplu, dintr-o varietate de motive.
De exemplu din cauză că alte activităţi consumă cicluri CPU sau din cauză că 2 activităţi diferite sunt blocate,
fiecare aşteptând pe cealaltă pentru a continua.
Nedeterminismul: Activităţile cu caracter multithread pot fi intercalate în mod arbitrar. Nici un program nu
rulează identic la 2 execuţii. Activităţile ce necesită un mare volum de calcule se pot întrerupe înainte ca aceste
calcule să fie satisfăcute. Acest lucru face ca programele multithreading să fie greu de înţeles, de depanat şi
greu predictibile. Construcţia unui thread, setarea lui şi utilizarea metodelor determină un consum de memorie
mai ridicat decât folosirea obiectelor normale.
Sincronizarea: Metodele Java implicate în sincronizare sunt mai lente decât cele care nu beneficiază de
protecţia oferită de sistemul de sincronizare.
Fire de execuţie (Thread-uri)
Thread-urile pot fi folosite în 2 moduri. Prima metodă constă în implementarea unei interfeţe Runnable.
Exemplu:
public class Simple implements Runnable{
protected String message;
protected TextArea text;
//constructorul clasei
public Simple (String m, TextArea t){
message = m;
text = t;
}
public void run(){
text.appendText(message);
}
}
Obiect de tip Runnable: Clasa Simple implementează o interfaţă de tip Runnable. Această interfaţă are doar o
singură metodă run(), nu are argumente la intrare şi nu întoarce rezultate.
public interface Runnable{
public void run();
}
Interfeţele sunt mai abstracte decât clasele, deoarece ele nu spun nimic despre reprezentare sau cod. Ele descriu
doar semnătura (nume, argumente şi tipuri de rezultate) ale operaţiilor publice. Clasele ce implementează
interfaţa Runnable nu au nimic în comun exceptând folosirea unei metode run(). Versiunea secvenţială a
programului:
public class Sapplet extends Applet{
protected TextArea text;
protected Simple hello;
protected Simple bye;
public Sapplet(){
text = new TextArea(4, 40); // 4 randuri si 40 coloane
hello = new Simple("Hello\n", text);
bye = new Simple("Bye\n", text);
}

public void init(){


add(text);
}
public void start(){
hello.run();
bye.run();
}
}
Clasa Sapplet va fi executată secvenţial. După invocarea metodei start(), se va continua cu rularea primului
obiect (hello) până la execuţia completă. După ce programul revine din execuţie prin instrucţiunea return din
metoda run(), se începe rularea celui de-al doilea obiect (bye). Prin urmare, în fereastra TextArea vor apărea
cele două mesaje succesiv şi complet separate. În acest exemplu, nu avem o aplicaţie multithreading, execuţia
celor două obiecte de tip Runnable fiind făcută în mod secvenţial.
Versiunea multithreading: A doua cale de folosire a interfeţei Runnable constă în crearea unui nou thread
folosind metoda new Thread(Runnable x) Exemplu:
public class TApplet extends Applet{
protected TextArea text;
protected Simple hello;
protected Simple bye;
public TApplet(){
text=new TextArea(4,40);
hello=new Simple("Hello\n", text);
bye=new Simple("Bye\n", text);
}
public void init(){
add(text);
}
public void start(){
new Thread(hello).start();
new Thread(bye).start();
}
}
Observaţie: Metoda care este apelată în thread este start(). Thread.start() determină execuţia metodei
Runnable.run() Se ştie că obiectul de tip applet deţine şi el o metodă start() care nu are nici o legătură cu
metoda cu aceaşi nume din Thread.
Sincronizarea: Când 2 sau mai multe thread-uri accesează acelaşi obiect, ele pot interfera.
Instrumentul principal, pentru evitarea acestei interferenţe, este mecanismul de sincronizare. Principalul merit
al sincronizării este asigurarea că un singur thread obţine accesul la un obiect într-un moment dat. Exemplu:
Dacă java.awt.TextArea nu ar fi fost implementat folosind sincronizarea, am fi putut face un mic program de
ajutor care asigură că un singur thread execută TextArea.appendText() la un moment dat.
Class Appender{
private TextArea text;

public Appender(TextArea t){


text=t;
}
synchronized void append(String s){
text.appendText(s);
}
}
public class ThreadApplet extends Applet{
protected TextArea text;
protected Appender appender;
protected Simple hello;
protected Simple bye;
public ThreadApplet(){
text=new TextArea(4,40);
appender=new Appender(text);
hello=new Simple("Hello\n",appender);
bye=new Simple("Bye\n",appen
der);
}
public void init(){
add(text);
}
public void start(){
new Thread(hello).start();
new Thread(bye).start();
}
}
public class Simple{
protected Appender appender;
protected String message;
public Simple (String m, Appen
der a){
message=m;
appender=a;
}
public void run(){
appender.append(message);
}
}
Metodele de control ale thread-ului:
� start(): determină apelarea metodei run() ca o activitate independentă. Fără o instrucţiune specială, cum ar fi
stop(), rularea thread-ului se termină când metoda run() întoarce return.
� isAlive(): întoarce true dacă un thread a fost startat dar nu a fost încă terminat.
� stop(): termină irevocabil activitatea unui thread. Nu are loc omorârea thread-ului, doar activitatea îi este
stopată. Deci metoda start() poate fi din nou apelată pentru acelaşi obiect de tip Thread.
� suspend(): opreşte temporar thread-ul ce va continua execuţia după invocarea metodei resume().
� sleep(): determină suspendarea execuţiei unui thread pentru un timp dat în milisecunde. Thread-ul poate să
nu continue imediat după timpul dat dacă există alt thread activ.
� interrupt(): determină întreruperea instrucţiunilor sleep(), wait() cu o InterruptException care poate fi prinsă,
captată şi prelucrată într-un mod specific aplicaţiei.
Priorităţi: Cazul fericit al rulării programelor multithreading este cazul multiprocesor. Majoritatea maşinilor
disponibile la ora actuală pe piaţă au un singur procesor, acesta prin capacitatea de efectuare a unui număr tot
mai mare de operaţii pe secundă putând face destul de bine fată aplicaţiilor multithreading. Un thread este
runnable, dacă a fost startat dar nu a fost terminat, suspendat , blocat şi nu este angajat într-o instrucţiune wait().
Când nu sunt în rulare, thread-urile sunt în aşteptare într-o coadă, aranjată în funcţie de priorităţi. Această coadă
este gestionată de sistemul de rulare Java. Priorităţile pot fi schimbate prin apelul instrucţiunii
Thread.setPriority cu un argument cuprins între Thread.MIN_PRIORITY şi MAX_PRIORITY. Instrucţiunea
Thread .yield recapătă controlul pentru un thread de prioritate egală cu celelalte. Dacă o metodă nu este marcată
ca synchronized, atunci poate fi executată imediat ce este apelată, chiar în timp ce altă metodă sincronizată
rulează. Calificativul de synchronized nu poate fi transferat în subclase. O subclasă trebuie declarată explicit
synchronized, altfel ea va fi tratată ca una obişnuită.
Metodele declarate în interfeţele Java nu pot fi înzestrate cu calificativul de syncronized. După cum se ştie,
metodele din interfeţe nu furnizează informaţii cu privire la cod, ele sunt propriu-zis o semnătură a metodei
(specifică tipul argumentelor de intrare şi tipul de date întors de metodă). Aceste metode trebuie suprascrise de
către programator. Un exemplu de metodă interfaţă este metoda run din interfaţa Runnable.
Wait şi Notification: Ca urmare a executării instrucţiunii wait():
� thread-ul curent este suspendat
� sistemul de rulare Java plasează thread-ul într-o coadă de aşteptare internă şi inaccesibilă programatorului.
Ca urmare a executării instrucţiunii notify():
� Din coada de aşteptare internă este scos în mod arbitrar un thread.
� Acest thread trebuie să obţină blocajul de sincronizare pentru obiectul ţintă, care întotdeauna va determina
blocarea cel puţin pană thread-ul va chema metoda notify .
� Thread-ul este atunci reluat din punctul unde apare metoda wait.
Invocarea metodei notifyAll:
� Invocarea metodei notifyAll lucrează în acelaşi mod ca şi notify numai că paşii de mai sus se aplica la toate
thread-urile ce aşteaptă în coada de aşteptare pentru obiectul ţintă.
� Două versiuni alternative ale metodei wait preia, argumente specificând timpul maxim de aşteptare în coadă.
Dacă timpul de aşteptare este depăşit, atunci metoda notify este invocată automat.
� Dacă o instrucţiune interrupt apare în timpul execuţiei unei instrucţiuni wait, acelaşi mecanism notify se
aplică exceptând controlul întors către clauza catch asociată cu invocarea lui wait.
Aplicaţie
Structura aplicaţiei: Ca o ilustrare a noţiunilor prezentate mai sus, este listat codul unei aplicaţii
multithreading. Interfaţa cu utilizatorul este dată de 3 butoane numite first, second, third. În spaţiul de deasupra
lor, 3 thread-uri care rulează concurent afişează numere consecutive de la 0 la 9. De observat că afişarea are un
caracter ciclic, după cifra 9 urmând cifra 0. Cifra de pornire nu este 0, ci este generată printr-un generator Java
de numere aleatoare şi este diferită în cazul fiecărui thread. Procesul de afişare este întrerupt prin apăsarea
butonului corespunzător fiecărui thread, caz în care este apelată metoda thread.stop(). Reîmprospătarea
periodică a ecranului şi captarea acţiunii asupra butoanelor sunt asigurate de un al patrulea fir de execuţie din
programul principal. Acest thread se va opri când toate celelalte thread-uri sunt oprite.
Ca şi structură, programul defineşte o clasă (Machine) ce extinde clasa Thread. Această clasă are următoarele
atribute:
� initial_value cifra iniţială generată aleator, de la care se începe afişarea cifrelor.
� counter variabila ce va fi afişată şi care ia valori de la 0 la 9, în mod ciclic.
� x_draw şi y_draw coordonatele la care o instantiere a acestei clase va începe să afişeze.
Metoda Thread.isAlive() testează starea firului de execuţie care a apelat metoda.
Consideraţii finale:
Pentru cei interesaţi câteva sugestii de îmbunătăţire a aplicaţiei. Ar fi utilă înzestrarea aplicaţiei cu un buton
start care să repornească întregul joc de la început. De asemenea, ar fi interesant dacă utilizatorul ar putea să
fixeze rapiditatea derulării cifrelor pe ecran pentru fiecare thread în parte, între nişte valori fixate de
programator.
Bibliografie:
Doug Lea
Concurrent Programming
în Java- Design Principles and Patterns
Addison-Wesley 1996
Codul sursă al aplicaţiei
import java.applet.*;
import java.io.*;
import java.awt.*;
import java.lang.Math;
import java.util.Random;
/*
Clasa Machine implementează comportamentul
dorit de programator pentru un Thread
*/
class Machine extends Thread{
protected int initial_value;
protected int counter=0;
protected int x_draw; //coordonatele de unde se începe
protected int y_draw; // scrierea textului
public Machine(int i, int x, int y){
initial_value=i;
x_draw=x;
y_draw=y;
}
public void run(){
while(true){
try {Thread.sleep(150);}
catch(InterruptedException e){
System.out.println("There is an Interrupt
edException");
}
counter=(initial_value++)%10;
}
}
}
public class game extends Applet implements Runnable{
protected Machine first_machine;
protected Machine second_machine;
protected Machine third_machine;
Thread engine=null;
protected Random r =new Random();
Button first_button = new Button("first");
Button second_button = new Button("second");
Button third_button = new Button("third");

public void init(){


resize(400,100);
add(first_button);
add(second_button);
add(third_button);
// generarea valorilor iniţiale de start pentru counter
int i1=Math.abs(r.nextInt()%10);
int i2=Math.abs(r.nextInt()%10);
int i3=Math.abs(r.nextInt()%10);
first_machine=new Machine(i1, 140,50);
second_machine=new Machine(i2,190,50);
third_machine=new Machine(i3,244,50);
}
public boolean action(Event e, Object o){
if(e.target instanceof Button){
if("first".equals((String)o)) {
first_machine.stop();
return true;
}
if( "second".equals((String)o)) {
second_machine.stop();
return true;
}
if("third".equals((String)o)) {
third_machine.stop();
return true;
}
}
return false;
}
public void start(){
if (engine = = null){
engine=new Thread(this);
first_machine.start();
second_machine.start();
third_machine.start();
engine.start();
}
}
public void run(){
while ((first_machine.isAlive()) ||
(second_machine.isAlive()) ||
(third_machine.isAlive()))
{
try{Thread.sleep(150);}
catch(InterruptedException e){}
repaint();
}
}
public void paint(Graphics g){
g.drawString(String.valueOf(first_machine.counter),
first_machine.x_draw,first_machine.y_draw);
g.drawString(String.valueOf(second_machine.counter),
second_machine.x_draw,second_machine.y_draw);
g.drawString(String.valueOf(third_machine.counter),
third_machine.x_draw,third_machine.y_draw);
}
}

S-ar putea să vă placă și