Java
Java
secventiale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clase
dictionar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Colectii
ordonate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clase
iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Definirea de
noi clase colectie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clase
sablon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.
Reutilizarea codului n POO Reutilizarea codului prin
compunere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reutilizarea codului prin
derivare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Comparatie ntre compozitie
si derivare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mostenire multipl prin
compozitie si derivare . . . . . . . . . . . . . . . . . . . . . . . . . . Combinarea compozitiei cu
derivarea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9. Clase incluse Clase
incluse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clase
interioare cu nume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Simplificarea comunicrii ntre clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Clase interioare cu date comune . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Clase interioare anonime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Probleme asociate claselor incluse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.
Clase pentru o interfat grafic Programarea unei interfete grafice . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . Clase JFC pentru interfata
grafic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dispunerea componentelor
ntr-un panou . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Componente vizuale cu
text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Panouri multiple . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Apleti
Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.
Programare bazat pe evenimente Evenimente Swing . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . Tratarea evenimentelopr Swing . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . Evenimente de mouse si de tastatur . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . Evenimente asociate componentelor
JFC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . Evenimente produse de componente cu
text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mecanismul de generare a
evenimentelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Structura programelor dirijate
de evenimente . . . . . . . . . . . . . . . . . . . . . . . . . . . . Utilizarea de clase
interioare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clase generator si
receptor de evenimente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reducerea
cuplajului dintre clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.
Componente Swing cu model Comunicarea prin evenimente si clase "model" . . . . . .
. . . . . . . . . . . . . . . . . . . . Arhitectura MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . Utilizarea unui model de
list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Familii deschise de clase n
JFC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Utilizarea unui model de tabel .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Utilizarea unui model de
arbore. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13. Java si XML Fisiere XML
n aplicatii Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XML si
este acum de tip boolean si nu de tip int). Asadar, instructiunile urmtoare sunt
gresite sintactic n Java, desi sunt corecte n C si C++. while ( d++ = s++) ; //
corect este : while ( (d++=s++) !=0) ; return x ? 1:0 ; // corect este: return x !=0 ?
1:0 ; cu x de tip int if ( ! n) { ... } // corect este: if (n==0) { ... } do { nf=nf *n--;}
while (n) ; // corect este: do { nf=nf*n--;} while ( n>0); Variabilele declarate n
functii nu primesc valori implicite iar compilatorul semnaleaz utilizarea de variabile
neinitializate explicit de programator. 2 In Java, se fac automat la atribuire numai
conversiile de promovare de la un tip numeric inferior la un tip aritmetic
superior, care nu implic o trunchiere. Exemple: int n=3; float f; double d;
d=f=n; // corect f=3.0, d=3.0 n=f; // gresit sintactic f=d; // gresit sintactic
Ierarhizarea tipurilor aritmetice, de la inferior la superior este: byte, short, int,
long, float, double Tipul char nu este un tip aritmetic dar se pot face conversii prin
operatorul (tip) ntre tipul char si orice tip aritmetic ntreg. Exemplu: byte b=65;
char ch; ch =(char)b; // ch este 'A' ch='\n'; b =(byte)ch; // b este 10 Aceleasi reguli
de conversie ntre tipuri numerice se aplic si ntre argumentele efective si
argumentele formale, deoarece compilatorul face automat o atribuire a valorii
argumentului efectiv la argumentul formal corespunztor. Exemplu: double r =
Math.sqrt(2); // promovare de la int la double ptr. 2 O alt conversie automat, de
promovare se face pentru rezultatul unei functii, dac tipul expresiei din
instructiunea return difer de tipul declarat al functiei. Exemplu: static float rest
(float a, float b) { int r = (int)a % (int) b; return r; } Conversia de la un tip numeric
superior la un tip aritmetic inferior trebuie cerut explicit prin folosirea
operatorului cast de fortare a tipului si nu se face automat ca n C. Exemple : f=
(float)d; // cu pierdere de precizie n=(int)f; // cu trunchiere int r = (int)
Math.sqrt(4); // conversie necesara de la double la int // functie de rotunjire din clasa
Math public static int round (float a) { return (int)floor(a + 0.5f); // "floor" are
rezultat double } Compilatorul Java verific dac este specificat un rezultat la orice
iesire posibil dintr-o functie si nu permite instructiuni if fr else n functii cu tip
diferit de void. 3 In Java nu exist operatorul sizeof din C, pentru determinarea
memoriei ocupate de un tip sau de o variabil, pentru c nu este necesar acest
operator. Cea mai important diferent dintre Java, pe de o parte, si limbajele C, C+
+ pe de alt parte, este absenta tipurilor pointer din Java. Deci nu exist
posibilitatea de a declara explicit variabile pointer si nici operatorii unari & (pentru
obtinerea adresei unei variabile) si * (indirectare printr-un pointer). Operatorul new
pentru alocare dinamic din C++ exist n Java, dar are ca rezultat o referint si nu
un pointer. Supradefinirea functiilor Supradefinirea sau suprancrcarea functiilor
(Function Overloading) a fost introdus n C++ pentru a permite definirea mai
multor functii cu acelasi nume si cu acelasi tip dar cu argumente diferite ntr-o
aceeasi clas. Pot exista functii cu acelasi nume (eventual si cu acelasi argumente
si tip) n clase diferite, dar acesta nu este un caz de supradefinire, fiindc ele se afl
n spatii de nume diferite. In Java, ca si n C++, o functie este deosebit de alte
functii din aceeasi clas (de ctre compilator) prin "semntura" sa (prin "amprenta"
functiei), care este format din numele, tipul si argumentele functiei. Un exemplu
uzual de functii supradefinite este cel al functiilor de afisare la consol n mod text
print si println, care au mai multe definitii, pentru fiecare tip de date primitiv si
pentru tipurile clas String si Object : // din pachetul java.io public class
PrintStream ...{ // este o clas derivat public void print (int i) { // scrie un ntreg
write (String.valueOf(i)); } public void print (float f) { // scrie un numr real write
(String.valueOf(f)); } public void print (boolean b) { // scrie un boolean write (b ?
true : false); } public void print (String s) { // scrie un sir de caractere if (s==
null) s= null; write (s); } Functia String.valueOf este si ea supradefinit pentru
diferite argumente. Declaratii de variabile O declaratie de variabil poate s apar
fie ntr-o functie, fie n afara functiilor, dar ntr-o clas; nu exist variabile externe
claselor. Locul declaratiei este important : o variabil dintr-o functie este local
acelei functii, iar o variabil declarat la nivel de clas este utilizabil de orice
functie din clas (si chiar de functii din alte clase). In C toate declaratiile dintr-un
bloc trebuie s precead prima instructiune executabil din acel bloc. In C++ si n
Java o declaratie poate apare oriunde ntr-un 4 bloc, ntre alte instructiuni sau
declaratii. Domeniul de valabilitate al unei variabile ncepe n momentul declarrii si
se termin la sfrsitul blocului ce contine declaratia. Instructiunea for constituie un
caz special: variabila contor se declar de obicei n instructiunea for, iar
valabilitatea acestei declaratii este limitat la instructiunile repetate prin
instructiunea for . Exemplu: public static boolean este ( int x[ ], int y) { int
n=x.length; // lungime vector x for (int k=0; k<=10;i++) // scrie 10 numere
aleatoare System.out.println ( rand.nextFloat()); } Variabila cu numele "rand" este
de tipul Random, iar clasa Random este definit n pachetul "java.util". Notatia
"rand.nextFloat()" exprim apelul metodei "nextFloat" din clasa "Random" pentru
obiectul adresat de variabila "rand". Instructiunea import permite simplificarea
referirilor la clase din alte pachete si poate avea mai multe forme. Cea mai folosit
form este: import pachet.* ; Instructiunea anterioar permite folosirea numelor
tuturor claselor dintr-un pachet cu numele "pachet", fr a mai fi precedate de
numele pachetului. Exemplul urmtor ilustreaz folosirea instructiunii "import":
import java.util.*; // sau import java.util.Random; class R { public static void main
(String arg[]) { Random rand =new Random(); for (int i=1;i<=10;i++) // scrie 10
numere aleatoare System.out.println ( rand.nextFloat()); } } Uneori se prefer
importul de clase individuale, att pentru documentare ct si pentru evitarea
ambiguittilor create de clase cu acelasi nume din pachete diferite. Exemplu care
arat riscurile importului tuturor claselor dintr-un pachet: import java.util.*; import
java.awt.*; class test { public static void main (String av[ ]) { List list; . . . // clasa
java.awt.List sau interfata java.util.List ? } } Definirea si utilizarea de vectori n Java
10 Cuvntul vector este folosit aici ca echivalent pentru array din limba englez
si se refer la un tip de date implicit limbajelor C, C++ si Java. Acest tip este diferit
de tipul definit de clasa JDK Vector (vectori ce se pot extinde automat) si de aceea
vom folosi si denumirea de vector intrinsec (limbajului) pentru vectori ca cei din C.
In Java, declararea unei variabile (sau unui parametru formal) de un tip vector se
poate face n dou moduri, echivalente: tip nume [ ]; // la fel ca in C si C++ tip [ ]
nume; // specific Java Declararea matricelor (vectori de vectori) poate avea si ea
dou forme. Exemplu: int a[ ][ ] ; // o matrice de ntregi int [ ][ ] b; // alt matrice de
biti: // matrice cu elemente de tip boolean public class BoolMatrix { private boolean
a[ ][ ]; // o matrice patratica private int n; // nr de linii si coloane // constructor de
obiecte public BoolMatrix (int n) { this.n=n; a= new boolean[n][n]; // aloca memorie
ptr matrice } // modifica valoare element public void setElement (int i,int j, boolean
b) { a[i][j]=b; } // citire valoare element public boolean getElement (int i,int j)
{ return a[i][j]; } // sir cu elementele din matrice public String toString () { String
s=""; 8 for (int i=0;i to || from < 0 || to > s.length() ) return s; // s nemodificat !
return s.substring(0,2) + s.substring(5); } // variant cu StringBuffer static String
delete2 (String s, int from, int to ) { StringBuffer sb = new StringBuffer(s); sb.delete
(from,to); // exceptie daca argumente incorecte ! return sb.toString(); } De observat
c trecerea de la tipul String la tipul StringBuffer se poate face numai printr-un
constructor, dar trecerea invers se poate face prin metoda toString, iar aceste
transformri pot fi necesare pentru c n clasa StringBuffer nu se regsesc toate
metodele din clasa String. De exemplu, metodele indexOf si lastIndexOf pentru
determinarea pozitiei unui caracter sau unui subsir ntr-un sir (supradefinite n clasa
String) nu exist n clasa StringBuffer. Metoda toString exist n toate clasele ce
contin date si produce un sir cu datele din obiectul pentru care se apeleaz (face
conversia de la tipul datelor din obiect la tipul String). Operatii cu siruri de caractere
Operatiile cu siruri sunt prezente n multe aplicatii Java si pot ilustra utilizarea de
metode ale obiectelor si de metode ale claselor (statice). O problem uzual n
programare este extragerea de cuvinte (tokens), ce pot fi separate ntre ele prin
unul sau mai multe caractere cu rol de separator, dintr-un text dat. Solutia uzual
creeaz un obiect analizor lexical (din clasa StringTokenizer) si apeleaz metode ale
acestui obiect ("nextToken" = urmtorul cuvnt): import java.util.* ; class Tokens
{ public static void main ( String[ ] args) { String text = new String ("unu doi, trei.
patru; cinci"); // sirul analizat String tokens[ ] = new String[100]; // vector de cuvinte
StringTokenizer st = new StringTokenizer (text, " ,;.\t\n"); // separatori int k=0; //
numara cuvinte while (st.hasMoreTokens()) { // daca mai sunt cuvinte in sirul
analizat String token = st.nextToken(); // extrage urmatorul cuvant din linie
tokens[k++]=token; // memoreaza cuvant } } } 10 10 Solutia Java 1.4 foloseste
expresii regulate (metoda "split") pentru analiza textului: class Tokens { public static
void main ( String[ ] args) { String text = new String ("unu doi, trei. patru; cinci"); //
sirul analizat String tokens[] = text.split ("[,;.\t\n]+"); // argument expresie
regulat } } O expresie regulat ("regular expression") este un sir de caractere cu
rol de sablon ("pattern") pentru o multime de siruri care se "potrivesc" cu acel
sablon. Expresiile regulate permit cutarea de siruri, nlocuirea de siruri si
extragerea de subsiruri dintrun text (textul este obiect de tip "String"). Majoritatea
operatiilor care folosesc expresii regulate se pot realiza n dou moduri: a) Folosind
metode noi din clasa "String". b) Folosind metode ale claselor "Pattern" si "Matcher"
din "java.util.regex". Principalele metode din clasa "String" pentru lucrul cu expresii
regulate sunt: public boolean matches(String regex): public String[] split(String
regex) public String replaceFirst(String regex, String replacement) public String
replaceAll(String regex, String replacement) Metoda "matches" are rezultat "true"
daca sirul pentru care se aplic metoda se potriveste cu sablonul "regex". Metoda
"split" creeaz un vector cu toate sirurile extrase din textul pentru care se aplic
metoda, siruri separate ntre ele prin siruri care se potrivesc cu sablonul "regex".
Metode mai importante din clasa Pattern: static Pattern Pattern.compile(String
regex); boolean matches(String regex, String text); String[] split(String text);
Metoda "compile" construieste un obiect de tip "Pattern" si retine sablonul primit
ntr-un format intern (compilat). Metodele "matches" si "split" din clasa "Pattern" au
acelasi efect ca si metodele cu acelasi nume din clasa "String". Exemplu: String text
= new String ("unu doi, trei. patru; cinci"); // sirul analizat Pattern p =
Pattern.compile ("[,;.\t \n]+"); String tokens[] = p.split (text); // argument expresie
regulat Clasa Matcher contine metode pentru cutare (find), potrivire (match),
nlocuire (replaceAll, replaceFirst) s.a. un obiect de tipul Matcher se obtine apelnd
metoda matcher pentru un obiect Pattern. Exemplu de eliminare a comentariilor
care ncep cu caracterele // dintr-un text: String regex="//.+\\n" // o expresie
regulat 11 11 String text="linia1 // unu \nlinia2 // doi "; Matcher m =
Pattern.compile(regex).matcher(text); String text2= m.replaceAll("\n"); // eliminare
comentarii In general, operatiile pe siruri se poate realiza mai compact cu expresii
regulate, dar trebuie stpnite regulile de formare ale acestor sabloane. Exemplu de
eliminare a marcajelor HTML sau XML (tags) dintr-un text: String text=" 111 222
333 " // sir cu marcaje // fara expresii regulate textb = new StringBuffer(text); while
((p1=text.indexOf('<',p2)) >=0 ) { // p1=pozitia car. 0) // p2 = pozitia car. >
textb.delete(p1,p2+1); } // cu expresii regulate String regex="<[^<>]*>"; // orice
sir incadrat de < si > String text2= text.replaceAll(regex,""); Clase si obiecte Java n
faza de executie Pentru fiecare clas ncrcat n masina virtual Java este creat
automat cte un obiect de tip Class, cu informatii despre clasa asociat (metadate).
Obiecte Class sunt create automat si pentru interfete, clase abstracte si vectori
intrinseci Java. Prin reflectie (reflection) se ntelege obtinerea de informatii
despre o clas sau despre un obiect n faza de executie (este reflectat starea
masinii virtuale). In plus, se pot crea si modifica dinamic obiecte n faza de executie.
Reflectia este asigurat n principal de clasa numit Class, dar si de alte clase din
pachetul java.lang.reflect: Constructor, Method, Field, s.a. O variabil de tip Class
contine o referint la un obiect descriptor de clas; ea poate fi initializat n mai
multe feluri: - Folosind cuvntul cheie class (literalul class) ca si cum ar fi un
membru public si static al clasei sau tipului primitiv : Class cF = Float.class, cS =
Stiva.class, // clase predefinite sau proprii cf = float.class, cv =void.class, // tipuri
primitive cN= Number.class, cI=Iterator.class ; // clase abstracte si interfete Folosind metoda static forName cu argument nume de clas (ca sir de
caractere): Class cF = Class.forName(java.util.Float), cf = Class.forName (float);
- Folosind metoda getClass pentru o variabil de orice tip clas (metoda
getClass este mostenit de la clasa Object, deci exist n orice clas): Float f =
new Float (3.14); Class cF = f.getClass(); 12 12 Clasa Class contine metode care au
ca rezultat numele clasei, tipul clasei (clas sau interfat sau vector), tipul
superclasei, clasa extern, interfete implementate, tipul obiectelor declarate n
clas, numele cmpurilor (variabilelor clasei), numele metodelor clasei, formele
functiilor constructor s.a. Metoda getName are ca rezultat un sir ce reprezint
numele clasei al crui tip este continut ntr-un obiect Class. Exemplul urmtor arat
cum se poate afisa tipul real al unui obiect primit ca argument de tipul generic
Object: void printClassName (Object obj) { System.out.println
( obj.getClass().getName()); } Crearea de obiecte de un tip aflat n cursul executiei
dar necunoscut la compilare (cu conditia ca tipul respectiv s fi fost definit printr-o
clas, iar fisierul class s fie accesibil la executie) se poate face simplu dac n
clas exist numai un constructor fr argumente . Exemplu: public static Object
getInstance (String clsname) throws Exception { Class cl = Class.forName(clsname);
// clsname = nume clasa (complet) return cl.newInstance(); } Deoarece putem
obtine toti constructorii unei clase, cunoscnd tipul argumentelor, se pot fabrica
obiecte si apelnd constructori cu argumente. Exemplu: public static Object
newObject (Constructor constructor, Object [ ] args) { Object obj = null; try { obj =
constructor.newInstance(args); } catch (Exception e) { System.out.println(e); }
return obj; } // utilizare Float x; Class cls = Float.class; Class[] argsCls = new Class[ ]
{float.class}; // tip argumente constructor Constructor constr =
cls.getConstructor(argsCls); // obtinere constructor Object[] args = new Object[ ]
{ new Float(3.5) }; // argument efectiv ptr instantiere x =(Float) newObject
(constr,args); Prin reflectie un asamblor de componente dintr-un mediu vizual poate
s determine propriettile si metodele proprii unor obiecte, s modifice propriettile
acestor obiecte si s genereze apeluri de metode ntre obiecte. In rezumat, reflectia
permite operatii cu clase si cu obiecte necunoscute la scrierea programului, dar care
pot fi determinate dinamic, n cursul executiei. 4. Definirea de noi clase Definirea
unei clase n Java Definirea unei clase se face prin definirea variabilelor si functiilor
clasei. In Java toate metodele trebuie definite (si nu doar declarate) n cadrul clasei.
Ordinea n care sunt definiti membrii unei clase (date si functii) este indiferent.
Este posibil ca o metod s apeleze o alt metod definit ulterior, n aceeasi clas.
Datele clasei se declar n afara metodelor clasei si vor fi prezente n fiecare obiect
al clasei. Ele sunt necesare mai multor metode si sunt de obicei putine. Majoritatea
claselor instantiabile grupeaz mai multe functii n jurul unor date comune. Ca
exemplu vom schita o definitie posibil pentru o clas ale crei obiecte sunt numere
complexe (nu exist o astfel de clas predefinit n bibliotecile JDK): public class
Complex { // datele clasei private double re,im; // parte real si parte imaginar //
metode ale clasei // adunare de numere complexe public void add ( Complex cpx)
{ re += cpx.re; im += cpx.im; } // scadere de numere complexe public void sub
( Complex cpx) { re = re - cpx.re; im = im - cpx.im; } } Se observ c metodele
unei clase au putine argumente, iar aceste argumente nu se modific, ceea ce est
tipic pentru multe clase cu date: metodele clasei au ca efect modificarea datelor
clasei, iar argumentele sunt de obicei date initiale necesare pentru operatiile cu
variabilele clasei. Acesta este si un avantaj al programrii cu clase fat de
programarea procedural: functii cu argumente putine si nemodificabile. Clasele de
uz general se declar public pentru a fi accesibile din orice alt pachet. Toate
variabilele numerice ale unei clase sunt initializate implicit cu zerouri si toate
variabile referint sunt initializate cu null. Variabilele de interes numai pentru
anumite metode vor fi definite ca variabile locale n functii si nu ca variabile ale
clasei. Exemplu de variabil local unei metode: public Complex conj ( ) { Complex
c = new Complex(); // c este o variabil local c.re= re; c.im= -im; return c; } Pentru
utilizarea comod a obiectelor clasei "Complex" mai sunt necesare functii pentru
initializarea datelor din aceste obiecte (numite "constructori") si pentru afisarea
datelor continute n aceste obiecte. In Java clasele cu date nu contin metode de
afisare pe ecran a datelor din obiectele clasei, dar contin o metod cu numele
toString care produce un sir de caractere ce reprezint datele clasei si care poate
fi scris pe ecran (cu metoda System.out.println) sau introdus ntr-un alt flux de
date sau folosit de alte metode. Functia urmtoare (metod a clasei "Complex" )
trebuie inclus n definitia clasei: // conversie n sir, pentru afisare public String
toString ( ) { return ( "(" + re+ "," + im+ ")"); } Existenta metodei "toString" ne
permite s afism direct continutul unui obiect din clasa "Complex" astfel: Complex
c = new Complex(); System.out.println (c); // scrie (0,0) In plus, se poate afisa
simplu continutul unei colectii de obiecte "Complex", pentru c metoda "toString" a
colectiei apeleaz metoda "toString" a obiectelor din colectie. Redefinirea metodei
"equals" n clasa "Complex" permite cutarea unui obiect dat ntr-o colectie si alte
operatii care necesit comparatia la egalitate. Exemplu: public boolean equals
(Object obj) { Complex cobj = (Complex) obj; return re==cobj.re &&
im==cobj.im; } Functii constructor Orice clas instantiabil are cel putin un
constructor public, definit implicit sau explicit si apelat de operatorul new la crearea
de noi obiecte. Un constructor este o functie fr tip si care are obligatoriu numele
clasei din care face parte. Constructorii nu sunt considerati metode, deoarece nu
pot fi apelati explicit (prin nume). Variabilele oricrei clase sunt initializate automat
la ncrcarea clasei (cu valori zero pentru variabile numerice si null pentru variabile
de orice tip clas). Aceste valori sunt preluate de fiecare obiect creat, dac nu se fac
alte initializri prin constructori. Pentru a permite initializarea variabilelor clasei n
mod diferit pentru fiecare obiect creat exist n clasele ce contin date unul sau mai
multi constructori. Exemplu: public class Complex { private double re, im; //
re=parte real ,im= parte imaginar public Complex ( double x, double y) { re=x;
im=y; } . . . // metode ale clasei } Exemple de creare a unor obiecte de tipul
Complex : Complex c1 = new Complex (2,3) , c2= c1.conj(); // c2 =(2,-3) Dac nu
se defineste nici un constructor atunci compilatorul genereaz automat un
constructor implicit, fr argumente si fr efect asupra variabilelor clasei (dar care
apeleaz constructorul superclasei din care este derivat clasa respectiv). Este
uzual s existe mai multi constructori ntr-o clas, care difer prin argumente, dar
au acelasi nume (un caz de supradefinire a unor functii). Exemplu: public Complex
( Complex c) { re=c.re; im=c.im; } Este posibil si supradefinirea unor metode ale
claselor. De exemplu, putem defini dou metode de adunare la un complex, cu
acelasi nume dar cu argumente diferite: // adunare cu un alt complex public void
add ( Complex c) { re += c.re; im += c.im; } // adunare cu un numar real public
void add ( double x) { re = re + x; } Variabilele unei clase pot fi initializate la
declararea lor, ceea ce are ca efect initializarea la ncrcarea clasei, aceeasi pentru
toate obiectele clasei. Acest fel de initializare se practic pentru variabile membru
de tip clas si pentru clase cu un singur obiect. Exemplu de initializare a unei
Exemple: // un obiect observat isi transmite adresa sa unui observator prin metoda
update for (int i = obs.length-1; i>=0; i--) // din clasa "Observable" ((Observer)
obs[i]).update(this, arg); // apel metoda "Observer.update" // un vector isi transmite
adresa sa metodei statice de sortare class SortedVec extends Vector { public void
addElement (Object obj) { // adaugare element si ordonare super.addElement (obj);
Collections.sort(this); // ordonare obiect ptr care s-a apelat metoda add } }
Variabila this nu poate fi folosit n metode statice. Atribute ale membrilor claselor
Membrii unei clase, variabile si metode, au n Java mai multe atribute: tipul
variabilei sau metodei ( tip primitiv sau tip clas ), un atribut de accesibilitate
(public, protected, private) plus atributele static, final, abstract (numai pentru
metode). Cu exceptia tipului, fiecare din aceste atribute are o valoare implicit,
folosit atunci cnd nu se declar explicit o alt valoare. Cuvintele cheie folosite
pentru modificarea atributelor implicite se numesc "modificatori". De exemplu orice
membru este nestatic si nefinal atunci cnd nu se folosesc modificatorii static sau
final. O variabil static a clasei declarat final este de fapt o constant,
nemodificabil prin operatii ulterioare primei initializri. Exemple de constante
definite n clasa Byte: public class Byte { public static final byte MIN_VALUE = -128;
public static final byte MAX_VALUE = 127; public static final Class TYPE =
Class.getPrimitiveClass(byte); ... // constructori si metode clasa Byte } Se vede din
exemplul anterior c o variabil static final poate fi initializat si cu rezultatul unei
functii, pentru c initializarea se face la ncrcarea clasei. Metodele unei clase
instantiabile sunt de obicei nestatice, dar pot exista si cteva metode statice n
fiecare clas cu date. Exemplu din clasa String: public static String valueOf(Object
obj) { // sir echivalent unui obiect return (obj == null) ? "null" : obj.toString(); } O
metod static poate fi apelat de o metod nestatic. Exemplu: public String
toString() { // metoda din clasa "Integer" return String.valueOf(value); // metoda
statica din clasa String } O metod static (inclusiv functia main) nu poate apela o
metod nestatic, pentru c folosirea unei metode nesatice este conditionat de
existenta unui obiect, dar metoda static se poate folosi si n absenta unor obiecte.
O metod static poate apela un constructor. Exemplu: public static Integer valueOf
(String s) throws NumberFormatException { return new Integer(parseInt(s)); // static
int parseInt(String,int); } Parte din definitia unei metode este si clauza throws, care
specific ce fel de exceptii se pot produce n metoda respectiv si care permite
compilatorului s verifice dac exceptiile produse sunt sau nu tratate acolo unde se
apeleaz metoda. Exemplu de metod din clasa Integer, pentru conversia unui sir
de caractere ntr-un ntreg : public static int parseInt (String s) throws
NumberFormatException { if (s == null) throw new NumberFormatException("null");
. . . } Anumite situatii speciale aprute n executia unei metode sunt uneori
raportate prin rezultatul functiei (boolean) si nu prin exceptii. De exemplu, anumite
functii de citire a unui octet dintr-un fisier au rezultat negativ la sfrsit de fisier, iar
functia de citire a unei linii (ntr-un obiect String) are rezultat null la sfrsit de fisier.
Uneori este posibil anticiparea si prevenirea unor exceptii. Exemplul urmtor arat
cum se poate evita aparitia unei exceptii de iesire din limitele unui vector intrinsec,
din cauza utilizrii gresite a unui program Java: // program cu doua argumente n
linia de comanda public static void main ( String arg[ ]) { if ( arg.length < 2)
{ System.out.println (usage: java copy input output); return; } . . . // prelucrare
argumente primite } O metod declarat final nu mai poate fi redefinit ntr-o
subclas si ar putea fi implementat mai eficient de ctre compilator. O metod
abstract este o metod cu atributul abstract, declarat dar nedefinit n clasa unde
apare pentru prima dat. O clas care contine cel putin o metod abstract este o
clas abstract si nu poate fi instantiat, deci nu se pot crea obiecte pentru aceast
clas (dar se pot defini variabile de un tip clas abstract). Metode declarate dar
nedefinite n Java sunt si metodele "native" (cu atributul native), care se
implementeaz ntr-un alt limbaj dect Java (de obicei limbajul C). Definitia lor
depinde de particularitatile sistemului de calcul pe care se implementeaz
metodele. Exemplu: // metoda System.arraycopy ptr copiere partiala vectori public
static native void arraycopy (Object s, int si, Object d, int di, int length);
Incapsularea datelor n clase De obicei datele unei clase nu sunt direct accesibile
utilizatorilor clasei, deci nu sunt accesibile functiilor din alte clase. Pe de o parte
utilizatorii nu sunt interesati de aceste date (de exemplu, cel care foloseste o stiv
nu are nevoie s vad vectorul stiv si alte detalii), iar pe de alt parte este mai
sigur ca aceste date s fie modificate numai de metodele clasei si nu de orice
utilizator. O clas expune utilizatorilor si o interfat public, care const din
totalitatea metodelor publice (accesibile) ale clasei si care arat ce se poate face cu
obiecte ale clasei respective. Documentatia unei clase prezint numai interfata sa
public, adic constructorii publici, metodele publice (tip, nume, argumente) si
variabilele public accesibile. Este posibil modificarea implementrii unei clase, cu
mentinerea interfetei sale. De exemplu, putem folosi o stiv realizat ca list
nlntuit n locul unei stive vector, fr ca programele ce folosesc stiva s necesite
vreo modificare. Atributul de accesibilitate se refer la drepturile unei clase asupra
membrilor unei alte clase, si este legat de ceea ce se numeste "ncapsulare" sau
"ascunderea datelor". Toate obiectele unei clase au aceleasi drepturi, stabilite la
definitia clasei. Metodele unei clase pot folosi variabilele clasei, indiferent de
atributul de accesibilitate al acestor variabile (private, protected, public). Metodele
unei clase se pot apela unele pe altele, indiferent de ordinea definirii lor si de
atributul de accesibilitate. Atributul public se foloseste pentru functiile si/sau
variabilele clasei ce urmeaz a fi folosite din alte clase. In general, metodele si
constructorii unei clase se declar public. Variabilele public accesibile se mai
numesc si proprietti ale obiectelor. Datele unei clase au n general atributul private
sau protected, iar accesul la aceste date este permis numai prin intermediul
metodelor, publice, ale clasei respective. Exista si cazuri, mai rare, n care nu se
respect principiul ncapsulrii iar variabilele unei clase sunt public accesibile. Este
vorba de clase cu mai multe variabile, folosite relativ frecvent n alte clase si pentru
care accesul prin intermediul metodelor ar fi incomod si ineficient. Exemplu din
pachetul java.awt: public class Rectangle { // dreptunghi de incadrare figura
geometrica public int x, y, width, height; // coordonate: colt, ltime, nltime public
Rectangle (int x0, int y0, int w, int h) { x0=x; y0=y; width=w; height=h; } ... // alte
metode } Variabilele cu atributul protected dintr-o clas sunt accesibile pentru toate
clasele derivate direct din A (indiferent de pachetul unde sunt definite) si pentru
clasele din acelasi pachet cu ea. Dac nu se specific explicit modul de acces la un
membru al unei clase A atunci se consider implicit c el este accesibil pentru toate
clasele definite n acelasi pachet cu A, numite uneori clase prietene. De exemplu,
clasele VectorEnumerator si Vector sunt definite (n Java 1.0) n acelasi pachet si n
acelasi fisier surs, iar variabile cu atributul protected din clasa Vector sunt folosite
direct n clasa VectorEnumerator (n Java 1.2 clasa enumerator este inclus n clasa
vector). In continuare vom schita definitia unei clase pentru o list nlntuit de
numere ntregi. Mai nti trebuie definit o clas pentru un nod de list, o clas care
poate s nu aib nici o metod si eventual nici constructor explicit: class Node { //
nod de lista inlantuita int val; // date din nod Node leg; // legatura la nodul urmator
public Node (int v) { val=v; leg=null;} // constructor } Metodele clasei list trebuie
s aib acces direct la variabilele clasei Node, ceea ce se poate realiza n dou
moduri: - Clasele Node si MyList se afl n acelasi pachet. - Clasa Node este
interioar clasei MyList. In ambele cazuri variabilele clasei Node pot s nu aib
nici un atribut explicit de accesibilitate Clasa MyList contine o variabil de tip
Node (adresa primului element din list), care poate fi private sau protected,
dac anticipm eventuale subclase derivate. public class MyList { // lista inlantuita
simpla (cu santinela) protected Node prim; // adresa primului element public MyList
( ) { prim = new Node(0); // creare nod sentinela (cu orice date) } // adaugare la
sfirsit de lista public void add (int v) { // adaugare intreg v la lista Node nou= new
Node(v); // creare nod nou cu valoarea v Node p=prim; // inaintare spre sfarsit de
lista while (p.leg != null) p=p.leg; p.leg=nou; // nou urmeaza ultimului element } //
sir ptr. continut lista public String toString () { String aux=""; // rezultatul functiei
toString Node p=prim.leg; // se pleaca de la primul nod din lista while ( p != null)
{ // cat timp mai exista un element urmator aux=aux + p.val + " "; // intregul p.val
se converteste automat in String p=p.leg; // avans la nodul urmator } return
aux; // sirul cu toate valorile din lista } } O metod a unei clase nu poate fi
recursiv, cu argument modificabil de tipul clasei; solutia problemei este o functie
static recursiv, apelat de metod. Exemplu de functii din clasa MyList: //
cautare in lista public boolean contains (int x) { return find (prim,x); // apel fct
recursiva } // fct recursiva de cautare private boolean find (Node crt, int x) { if
(crt==null) // daca sfarsit de lista return false; // atunci x negasit if ( x==crt.val)
return true; // x gasit return find (crt.leg,x); // continua cautarea in sublista } In
exemplul anterior recursivitatea nu este justificat, dar pentru alte clase (cu arbori,
de exemplu) forma recursiv este preferabil. Structura unei clase Java O clas care
poate genera obiecte contine n mod normal si date, deci variabile ale clasei,
definite n afara metodelor clasei (de obicei naintea functiilor). Majoritatea claselor
instantiabile au unul sau mai multi constructori publici folositi pentru initializarea
obiectelor si apelati de operatorul new. Metodele unei clase pot fi clasificate n
cteva grupe: - Metode care permit citirea datelor clasei ( metode accesor). Metode care permit modificarea datelor clasei ( si care lipsesc din anumite clase ). Metode pentru operatii specifice clasei respective. - Metode mostenite de la clasa
Object si redefinite, pentru operatii generale, aplicabile oricrui obiect Java
( equals, toString, hashCode). Conform unor cerinte mai noi ale standardului
Java Beans, metodele de acces la variabile private au un nume care ncepe cu "get"
si continu cu numele variabilei, iar metodele de modificare a variabilelor au un
nume care ncepe cu "set". Exemplu: public class Complex { private double re, im; //
parte reala si imaginara // metode de citire a datelor public float getReal ( ) { return
re; } // extrage parte reale public float getImag ( ) { return im; } // extrage parte
imaginara // metode de modificare a datelor public void setReal (float x) { re =x; } //
modifica parte reala public void setImag (float y) { im=y; } // modifica parte
imaginara // complex conjugat public Complex conj () { return new Complex(re,-im);
} // alte operatii cu obiecte de tip Complex . . . // metode din Object redefinite
public boolean equals (Object obj) {. . . } public String toString () { . . . } } Clasa
String contine un vector de caractere, dimensiunea sa si mai multe functii care
realizeaz diverse operatii asupra acestui vector: cutarea unui caracter dat n
vector, extragerea unui subsir din ntregul sir, compararea cu un alt sir etc.
Exemplu: public class String { // variabilele clasei private char value[ ]; // un vector
de caractere private int count; // numar de caractere folosite // un constructor public
String( char [ ] str) { count= str.length; value= new char[count]; System.arraycopy
(str,0,value,0,count); } // metodele clasei public int length () { // lungime sir return
count; } public String substring (int start, int end) { // extrage subsir dintre start si
end int n=end-start+1; // nr de caractere intre start si end char b[ ] = new char[n];
System.arraycopy (value,start,b,0,n); return new String (b); } public String substring
(int pos) { // extrage subsir din pozitia pos return substring (pos, length()-1); //
return this.substring (pos,length()-1); } // ... alte metode } A se observa existenta
unor metode cu acelasi nume dar cu argumente diferite si modul n care o metod
(nestatic) apeleaz o alt metod nestatic din clas: nu se mai specific obiectul
pentru care se apeleaz metoda substring cu doi parametri, deoarece este acelasi
cu obiectul pentru care s-a apelat metoda substring cu un singur parametru
(obiectul this = chiar acest obiect). O clas mai poate contine constante si chiar alte
clase incluse. In limbajul C o declaratie de structur este o definitie de tip care nu
aloc memorie si de aceea nu este posibil initializarea membrilor structurii. In Java
o definitie de clas creeaz anumite structuri de date si aloc memorie, ceea ce
justific afirmatia c o clas este un fel de sablon pentru crearea de obiecte de tipul
respectiv. Definitia unei clase Java nu trebuie terminat cu ; ca n C++. Metode
care pot genera exceptii In antetul unor metode trebuie s apar clauza throws,
dac n aceste metode pot apare exceptii, a cror tratare este verificat de
compilator. Intr-o metod poate apare o exceptie fie datorit unei instructiuni throw,
fie pentru c se apeleaz o metod n care pot apare exceptii. In cursul executiei
unei metode sau unui constructor este posibil s apar o situatie de exceptie, iar
metoda trebuie s anunte mai departe aceast exceptie. Semnalarea unei exceptii
program nseamn n Java crearea unui obiect de un anumit tip clas (derivat din
clasa Exception) si transmiterea lui n afara functiei, ctre sistemul care asist
executia ("Runtime system"). Exemplu de metod care poate produce exceptii: //
metoda din clasa String public char charAt (int index) { if ((index < 0) || (index >=
count)) throw new StringIndexOutOfBoundsException(index); return value[index +
{return v+"-"+w; } public int compareTo (Object obj) { Arc a = (Arc) obj; return cost
a.cost; // arce egale dac au costuri egale ! } } 8 Metoda equals este folosit la
cutarea ntr-o colectie neordonat (contains, add pentru multimi, s.a.), iar
metoda compareTo este folosit la cutarea ntr-o colectie ordonat
(Collections.binarySearch), ceea ce poate conduce la rezultate diferite pentru cele
dou metode de cutare ntr-o list de obiecte Arc. Pentru comparare de obiecte
dup alte criterii dect cel natural s-a introdus o alt functie cu numele
compare, parte din interfata Comparator: public interface Comparator { int
compare (Object t1, Object t2); // implicit abstracta, ne-statica ! boolean equals
(Object); // comparare de obiecte comparator ! } Interfata Comparator ar fi putut fi
si o clas abstract cu o singur metod, pentru c este improbabil ca o clas
comparator s mai mosteneasc si de la o alt clas. Functia Arrays.sort are si o
form cu dou argumente; al doilea argument este de tipul Comparator si
precizeaz functia de comparare (alta dect compareTo). Functia care determin
maximul dintr-un vector de obiecte poate fi scris si astfel: public static Object max
(Object a[ ], Comparator c) { Object maxim = a[0]; for (int k=1; k< 0) // c este un
obiect comparator maxim = a[k]; return maxim; } Functia max cu dou
argumente este mai general si poate fi folosit pentru a ordona un acelasi vector
dup diferite criterii (dup diverse proprietti ale obiectelor). De exemplu, pentru
ordonarea unui vector de siruri dup lungimea sirurilor, vom defini urmtoarea clas
comparator: class LengthComparator implements Comparator { public int compare
(Object t1, Object t2) { return ((String)t1).length() - ((String)t2).length(); } } . . . //
ordonare dupa lungime String [ ] a = {patru,trei,unu}; Arrays.sort( a, new
LengthComparator()); Pentru ordonarea unei matrice de obiecte dup valorile dintro coloan dat putem defini o clas comparator cu un constructor care primeste
numrul coloanei: class CompCol implements Comparator { int c; // numar coloana
ce determina ordinea public CompCol (int col) { c=col; } // constructor cu un
argument 9 public int compare(Object o1, Object o2) { Comparable c1
=(Comparable) ((Object[ ])o1)[c]; // linia o1, coloana c Comparable c2
=(Comparable) ((Object[ ])o2)[c]; // linia o2, coloana c return c1.compareTo(c2); //
compara valorile din coloana c (liniile o1 si o2) } } Exemplu de utilizare a acestui
comparator n functia de sortare : Object a[ ][ ] = { {"3","1","6"}, {"1","5","2"},
{"7","3","4"} }; for (int i=0;i< files.length;i++) System.out.println (files[i]); } } In
clasa File exist si metode de extragere selectiv de fisiere dintr-un director: metoda list cu argument de tip FilenameFilter si rezultat String[] - metoda
listFiles cu argument de tip FileFilter sau FilenameFilter si rezultat File[] FileFilter si
FilenameFilter sunt interfete cu o singur metoda accept. 10 In SDK 2 (pachetul
java.io) sunt prevzute dou interfete pentru clase de filtrare a continutului unui
director : public interface FilenameFilter { // prezenta din jdk 1.0 boolean accept
(File path, String filename); } public interface FileFilter { // prezenta din jdk 1.2
boolean accept (File path); } Utilizatorul are sarcina de a defini o clas care
implementeaz una din aceste interfete si de a transmite o referint la un obiect al
acestei clase fie metodei list fie metodei listFiles. Exemplu de listare selectiv cu
masc a directorului curent: // clasa pentru obiecte filtru class FileTypeFilter
implements FileFilter { String ext; // extensie nume fisier public FileTypeFilter (String
ext) { this.ext = ext; } public boolean accept (File f) { String fname = f.getName();
int pp = fname.indexOf('.'); if (pp==0) return false; return
ext.equals(fname.substring(pp+1)) ; } } // Listare fisiere de un anumit tip class Dir {
public static void main (String arg[ ]) throws IOException { File d =new File(.); //
din directorul curent File [ ] files = d.listFiles(new FileTypeFilter("java")); for (int i=0;
i< files.length;i++) System.out.println (files[i]); } } Metoda accept definit de
utilizator este apelat de metoda list (listFiles), care contine si ciclul n care se
verific fiecare fisier dac satisface sau nu conditia continut de functia accept.
Functia list primeste adresa functiei accept prin intermediul obiectului argument
de tip FileFilter. Pentru filtrare dup orice sablon (masc) putem proceda astfel: a)
Se defineste o clas pentru lucrul cu siruri sablon : class Pattern { protected String
mask; public Pattern ( String mask) { this.mask=mask; 11 } public boolean match
( String str) { // ... compara str cu mask } } b) Se defineste o clas fltru dup
orice masc, derivat din clasa Pattern si care implementeaz o interfat filtru din
JDK : class MaskFilter extends Pattern implements FileFilter { public MaskFilter
(String mask) { super(mask); } public boolean accept (File f) { super.mask=
super.mask.toUpperCase(); String fname = f.getName().toUpperCase(); return
match(fname); } } c) Se foloseste un obiect din noua clas ntr-o aplicatie: class Dir
{ public static void main (String argv[ ]) throws IOException { File d =new
File(argv[0]); // nume director in argv[0] File[ ] files = d.listFiles(new
MaskFilter(argv[1])); // masca in argv[1] for (int i=0; i< files.length;i++)
System.out.println (files[i]); } } Acest exemplu poate explica de ce FileFilter este o
interfat si nu o clas abstract: clasa filtru poate prelua prin mostenire metoda
match de la o alt clas, pentru c nu este obligat s extind clasa abstract
(posibil) FileFilter. Functii callback O functie pentru ordonare natural n ordine
descresctoare poate fi scris astfel: public static void sortd (List list)
{ Collections.sort (a,new DComp()); } static class DComp implements Comparator
{ // folosit intr-o metoda statica ! public int compare (Object a,Object b)
{ Comparable ca=(Comparable)a; return - ca.compareTo(b); } } 12 Functia
"compare" poart numele de functie "callback". Clasa care contine functia sortd si
clasa DComp transmite functiei sort adresa functiei compare (printrun obiect
DComp) pentru a permite functie sort s se refere "napoi" la functia de
comparare. Practic, se transmite un pointer printr-un obiect ce contine o singur
functie; adresa obiectului comparator este transmis de la sortd la sort, iar
sort foloseste adresa continut n obiectul comparator pentru a se referi napoi la
functia "compare" (n cadrul functiei sort se apeleaz functia compare). In
limbajul C se transmite efectiv un pointer la o functie (un pointer declarat explicit).
Situatia mai poate fi schematizat si astfel: o functie A ("main", de ex.) apeleaz o
functie B ("sort", de ex.), iar B apeleaz o functie X ("compare") dintr-un grup de
functii posibile. Adresa functiei X este primit de B de la functia A. De fiecare dat
cnd A apeleaz pe B i transmite si adresa functiei X, care va fi apelat napoi de B.
La scrierea functiei de sortare (Collections.sort) nu se cunostea exact definitia
functiei de comparare (pentru c pot fi folosite diverse functii de comparare), dar s-
a elementelor unei colectii pentru orice fel de colectie a condus la aparitia claselor
cu rol de iterator fat de o alt clas colectie. Orice clas colectie Java 2 poate
avea o clas iterator asociat. Pentru un acelasi obiect colectie (de ex. un vector)
pot exista mai multi iteratori, care progreseaz n mod diferit n cadrul colectiei,
pentru c fiecare obiect iterator are o variabil cursor proprie. Toate clasele iterator
rebuie s includ urmtoarele operatii: - Pozitionarea pe primul element din colectie
10 - Pozitionarea pe urmtorul element din colectie - Obtinerea elementului curent
din colectie - Detectarea sfrsitului colectiei (test de terminare a enumerrii).
Interfata Iterator contine metode generale pentru orice iterator: public interface
Iterator { boolean hasNext(); // daca exista un element urmator in colectie Object
next(); // extrage element curent si avans la urmtorul void remove(); // elimina
element curent din colectie (optional) } De observat c modificarea continutului
unei colectii se poate face fie prin metode ale clasei colectie, fie prin metoda
remove a clasei iterator, dar nu ar trebui folosite simultan ambele moduri de
modificare. In exemplul urmtor apare o exceptie de tip
ConcurrentModificationException: ArrayList a = new ArrayList(); Iterator it =
a.iterator(); while (it.hasNext()) { it.next(); it.remove(); a.add( "x"); } Pentru fiecare
clas concret de tip colectie exist o clas iterator. Un obiect iterator este singura
posibilitate de enumerare a elementelor unei multimi si o alternativ pentru
adresarea prin indici a elementelor unei liste. Un dictionar nu poate avea un
iterator, dar multimea perechilor si multimea cheilor din dictionar au iteratori.
Clasele iterator nu sunt direct instantiabile (nu au constructor public), iar obiectele
iterator se obtin prin apelarea unei metode a clasei colectie (metoda iterator). In
felul acesta, programatorul este obligat s creeze nti obiectul colectie si numai
dup aceea obiectul iterator. Mai multi algoritmi generici realizati ca metode statice
(n clasa Collections) sau ca metode ne-statice din clasele abstracte folosesc un
obiect iterator pentru parcurgerea colectiei. Exemplu: public static void sort (List
list) { Object a[] = list.toArray(); // transforma lista in vector intrinsec Arrays.sort(a);
// ordonare vector intrinsec (mai eficienta) ListIterator i = list.listIterator(); for (int
j=0; j< entries.size();} public Object next() { Object e= (MEntry)entries.get(i); i++;
return e; } public void remove () { entries.remove(i-1); } } Clasa MEntry
implementeaz interfata Map.Entry si redefineste metoda equals (eventual si
metoda toString): class MEntry implements Map.Entry { private Object key,val;
public MEntry (Object k, Object v) { key=k; val=v; } public Object getKey() { return
key; } public Object getValue() { return val;} public Object setValue (Object v)
{ val=v; return v;} public boolean equals (Object obj) { return
((MEntry)obj).getKey().equals(key); 15 } public String toString() { return
key+":"+val;} } Clase colectie sablon Clasele colectie cu tipuri parametrizate au
fost introduse n versiunea 1.5, ca solutii alternative pentru colectiile de obiecte
deja existente. Exemple de declarare a unor vectori cu elemente de diferite tipuri :
ArrayList a = new ArrayList(); ArrayList b = ArrayList (100); ArrayList c = ArrayList
(n); ArrayList<LinkedList> graf; // initializata ulterior Avantajele sunt acelea c se
poate verifica la compilare dac se introduc n colectie numai obiecte de tipul
declarat pentru elementele colectiei, iar la extragerea din colectie nu mai este
1.2}, {2.0, 2.1, 2.2} }; System.out.println(Arrays.deepToString(b)); De asemenea, sau mai adaugat ctiva algoritmi generici pentru colectii: int frequency(Collection c,
Object o); // numara aparitiile obiectului o in colectie boolean disjoint(Collection c1,
Collection c2); // daca colectiile sunt disjuncte 18 Comparator
reverseOrder(Comparator cmp); // comp. pentru ordine inversa Din versiunea 1.5 sa introdus tipul de date definit prin enumerare (si cuvntul cheie enum), precum si
dou colectii performante pentru elemente de un tip enumerat: EnumSet si
EnumMap (implementate prin vectori de biti). 1 8. Reutilizarea codului n POO
Reutilizarea codului prin compunere Unul din avantajele programrii orientate pe
obiecte este posibilitatea de reutilizare simpl si sigur a unor clase existente n
definirea altor clase, fr a modifica clasele initiale. Metodele de reutilizare a
codului sunt compozitia si derivarea. O clas compus contine ca membri referinte
la obiecte din alte clase. Agregarea unor obiecte de tipuri deja definite ntr-un nou
tip de obiect se impune de la sine acolo unde, n programarea clasic, se definesc
tipuri structur (nregistrare). In exemplul urmtor, clasa "Pers" corespunde unei
persoane, pentru care se memoreaz numele si data nasterii. Fiecare obiect de tip
"Pers" va contine (pointeri la) un obiect de tip String si un obiect de tip Date. public
class Pers { private String nume; // nume persoana private Date nascut; // data
nasterii Pers (String nume, int zi, int luna, int an) { this.nume= nume; this.nascut=
new Date(luna,zi,an); } public String toString () { return nume + +
nascut.toString();} public Date getBDate () { return nascut;} } Obiectele de tip
Pers pot fi memorate ntr-o colectie ca orice alte obiecte derivate din Object: class
VecPers { // creare si afisare vector de persoane public static void main (String[ ] a)
{ Vector lista = new Vector(); lista.addElement ( new Pers ("unu",24,11,89));
lista.addElement ( new Pers ("doi",1,11,99)) ; System.out.println (lista); } } Pentru
clase ale cror obiecte se memoreaz n colectii trebuie redefinite metodele
"equals", "hashCode" si, eventual, "compareTo" pentru colectii ordonate (sortabile).
Uneori clasa agregat A contine un singur obiect dintr-o alt clas B, iar motivul
acestei relatii este reutilizarea functionalittii clasei B n noua clas A, adic
folosirea unor metode ale clasei B si pentru obiecte ale clasei A. Interfetele publice
ale claselor A si B trebuie s fie destul de diferite, pentru a justifica compozitia n
locul derivrii. In exemplul urmtor se defineste o clas pentru stive realizate ca
liste nlntuite de obiecte, cu preluarea functiilor de la obiectul continut (de tip
LinkedList): 2 public class LinkedStack { private LinkedList stack; // stiva lista (obiect
continut) public LinkedStack () { // constructor stack= new LinkedList(); } public
Object push (Object obj) { // metoda clasei LinkedStack stack.addFirst (obj); return
obj; // foloseste metoda clasei LinkedList } public Object pop () { return
stack.removeFirst(); } public boolean isEmpty () { // metode cu acelasi nume si tip
return stack.isEmpty(); } public String toString () { return stack.toString(); } } De
observat c tipurile LinkedStack si LinkedList nu sunt compatibile si nu se pot face
atribuiri ntre ele, desi continutul claselor este att de asemntor. Varianta definirii
clasei "LinkedStack" ca o subclas a clasei LinkedList este preferabil aici, mai ales
c dou dintre metode pot fi mostenite ca atare ("isEmpty" si "toString"). In
exemplul urmtor se defineste o clasa dictionar cu valori multiple, n care fiecare
cheie are asociat o lista de valori. Clasa preia o mare parte din functionalitatea
clasei HashMap, prin delegarea unor operatii ctre metode din clasa HashMap.
public class MultiMap { HashMap m = new HashMap(); public void put (Object key,
List val) { m.put (key, val); // apel HashMap.put } public List get (Object key)
{ return (List) m.get(key); // apel HashMap.get } public String toString () { String
str=""; Iterator ik=m.keySet().iterator(); // apel HashMap.keySet while
(ik.hasNext() ) { List lst = get(ik.next()); // lista de valori a unei chei str = str+ key +
" : " + lst.toString() + "\n"; } return str; } public Set keySet () { return
m.keySet(); } } 3 Reutilizarea codului prin derivare Solutia specific POO de
adaptare a unei clase A la alte cerinte este definirea unei clase D, derivat din clasa
A, si care modific functionalitatea clasei A. Nu se accept modificarea codului
clasei A de ctre fiecare
ilizator care constat necesitatea unor modificri, chiar dac acest cod surs este
disponibil. Prin derivare se face o adaptare sau o specializare a unei clase mai
generale la anumite cerinte particulare fr a opera modificri n clasa initial.
Extinderea unei clase permite reutilizarea unor metode din superclas, fie direct, fie
dup "ajustri" si "adaptri" cerute de rolul subclasei. Superclasa transmite
subclasei o mare parte din functiile sale, nefiind necesar rescrierea sau apelarea
metodelor mostenite. Vom relua exemplul clasei pentru stive realizate ca liste
nlntuite. Operatiile cu o stiv se numesc traditional push si pop, dar clasa
LinkedList foloseste alte nume pentru operatiile respective. De aceea, vom defini
dou metode noi: public class LinkedStack extends LinkedList { public Object push
(Object obj) { addFirst (obj); return obj; } public Object pop () { return removeFirst();
} } De observat c subclasa LinkedStack mosteneste metodele "toString",
"isEmpty" si altele din clasa LinkedList. De asemenea, exist un constructor implicit
care apeleaz constructorul superclasei si care initializeaz lista stiv. O problem
n acest caz ar putea fi posibilitatea utilizatorilor de a folosi pentru obiecte
"LinkedStack" metode mostenite de la superclas, dar interzise pentru stive: citire si
modificare orice element din stiv, cutare n stiv s.a. Solutia este de a redefini
aceste metode n subclas, cu efectul de aruncare a unor exceptii. Exemplu: public
Object remove (int index) { throw new NoSuchMethodError(); } Iat si o alt solutie
de definire clasei "MultiMap" (dictionar cu valori multiple), pe baza observatiei c
lista de valori asociat unei chei (de tip List) este tot un obiect compatibil cu tipul
Object si deci se poate pstra interfata public a clasei HashMap: public class
MultiMap extends HashMap { public Object put (Object key, Object val) { List lst =
(List) get(key); // extrage lista de valori asociata cheii key List result=lst; // rezultatul
va fi lista anterioara if (lst==null) // daca cheia key nu exista in dictionar super.put
(key, lst=new ArrayList()); // se introduce o pereche cheie-lista 4 lst.add (val); //
adauga prima valoare la lista return result; // lista anterioara de valori } } Toate
celelalte metode sunt mostenite ca atare de la superclasa HashMap : get, iterator,
toString. Dac dorim s modificam modul de afisare a unui dictionar atunci putem
redefini metoda toString din clasa MultiMap, ca n exemplul anterior.
Comparatie ntre compozitie si derivare Compozitia (un B contine un A) se
Legtura dintre un obiect flux si obiectul de calcul a sumei este stabilit la executie
si asigur o flexibilitate sporit. Exemplu: 6 public class CheckedOutputStream
extends FilterOutputStream { private Checksum cksum; // adresa obiect cu metoda
de calcul suma control public CheckedOutputStream(OutputStream out, Checksum
cksum) { super(out); this.cksum = cksum; } public void write(int b) throws
IOException { out.write(b); cksum.update(b); // metoda din interfata
Checksum } . . . } Exemplu de utilizare a clasei anterioare: CRC32 Checker = new
CRC32(); // clasa care implem. interfata Checksum CheckedOutputStream out; out =
new CheckedOutputStream (new FileOutputStream("date"), Checker); while
(in.available() > 0) { int c = in.read(); out.write(c); } Mostenire multipl prin
derivare si compozitie In Java o subclas nu poate avea dect o singur superclas,
dar uneori este necesar ca o clas s preia functii de la dou sau mai multe clase
diferite. Implementarea mai multor interfete de ctre o clas nu este o solutie
pentru mostenirea multipl de functii. O clas M poate prelua metode de la dou
clase A si B astfel: clasa M extinde pe A si contine o variabil de tip B; metodele din
M fie apeleaz metode din clasa B, fie sunt mostenite de la clasa A. Clasa A este de
multe ori o clas abstract, iar clasa B este instantiabil sau abstract. Exemplu de
mostenire functii de la 3 clase: class A { void f1 () { System.out.println ("A.f1"); } }
class B { void f2 () { System.out.println ("B.f2"); } } class C { void f3 ()
{ System.out.println ("C.f3"); } } class M extends C { A a = new A (); // initializarea
var. a si b se poate face intr-un constructor B b = new B (); void f1 () { a.f1();} //
delegare obiect a pentru operatia f1 void f2 () { b.f2();} // delegare obiect b pentru
operatia f2 } class X { 7 public static void main (String arg[]) { M m = new M();
m.f1(); m.f2(); m.f3(); } } Un exemplu real de mostenire multipl poate fi o clas
pentru o multime realizat ca vector, care extinde clasa AbstractSet si contine o
variabil de tip ArrayList : public class ArraySet extends AbstractSet { private
ArrayList set; public ArraySet() { set = new ArrayList(); } public boolean add (Object
obj) { if (! set.contains(obj) ) return set.add(obj); // delegare pentru operatia de
adaugare return false; } public Iterator iterator() { return set.iterator(); // delegare
pentru creare obiect iterator } public int size() { return set.size(); } } Pentru multimi
de tipul "ArraySet" se pot folosi toate metodele mostenite de la clasa AbstractSet:
toString, contains, containsAll, addAll, removeAll, retainAll s.a. Aceeasi solutie de
mostenire multipl este folosit n cteva clase JFC (Swing) de tip model; de
exemplu, clasa DefaultListModel preia metode de la superclasa AbstractListModel si
deleag unei variabile interne de tip Vector operatii cu un vector de obiecte. Un
model de list este un vector cu posibilitti de generare evenimente (de apelare
receptori) la modificri operate n vector. Combinarea compozitiei cu derivarea
Derivarea pstreaz interfata clasei de baz, iar agregarea permite mai mult
flexibilitate la executie. Combinarea agregrii cu derivarea mbin avantajele
ambelor metode de reutilizare: mostenirea interfetei si posibilitatea modificrii
obiectului interior din obiectul unei clase agregat. Uneori variabila din subclas este
chiar de tipul superclasei. Clasele ce contin un obiect de acelasi tip sau de un tip
compatibil cu al superclasei sale se mai numesc si clase anvelop (wrapper),
deoarece adaug functii obiectului continut, ca un ambalaj pentru acel obiect. O
clasa anvelop este numit si clas decorator, deoarece "decoreaz" cu noi functii
o clas existent. 8 De exemplu, putem defini o clas stiv mai general, care s
poat folosi fie un vector, fie o list nlntuit, dup cum doreste programatorul.
Clasa anvelop care urmeaz este compatibil cu tipul List si, n acelasi timp,
foloseste metode definite n clasa AbstractList : class StackList extends AbstractList
{ private AbstractList stack; // adresa obiect stiva public StackList (List list) { //
constructor stack=(AbstractList)list; // retine adresa obiect stiva } public Object
push (Object obj) { stack.add (0,obj); return obj; } public Object pop () { Object
obj= get(0); stack.remove(obj); return obj; } public int size() { return
stack.size(); } } Exemple de combinare a derivrii si compozitiei se gsesc n clasa
Collections, pentru definirea de clase colectie cu functionalitate putin modificat
fat de colectiile uzuale, dar compatibile cu acestea ca tip. Primul grup de clase
este cel al colectiilor nemodificabile, care difer de colectiile generale prin
interzicerea operatiilor ce pot modifica continutul colectiei. Exemplu: class
UnmodifiableCollection implements Collection, Serializable { Collection c; // metode
care nu modifica continutul colectiei public int size() {return c.size();} public
boolean isEmpty() {return c.isEmpty();} public boolean contains(Object o) {return
c.contains(o);} public String toString() {return c.toString();} . . . // metode care ar
putea modifica continutul colectiei public boolean add (Object o){ throw new
UnsupportedOperationException(); } public boolean remove (Object o) { throw new
UnsupportedOperationException(); } . . . } Incercarea de adugare a unui nou obiect
la o colectie nemodificabil produce o exceptie la executie; dac nu se defineau
metodele "add", "remove" s.a. n clasa 9 derivat, atunci apelarea metodei "add"
era semnalat ca eroare la compilare. Exceptia poate fi tratat fr a mpiedica
executia programului. Al doilea grup de clase sunt clasele pentru colectii
sincronizate. Exemplu: class SynchronizedCollection implements Collection,
Serializable { Collection c; // colectia de baza public int size() { synchronized(this)
{return c.size();} } public boolean add(Object o) { synchronized(this) {return
c.add(o);} } . . . // alte metode } In realitate, clasele prezentate sunt clase incluse
statice si sunt instantiate n metode statice din clasa Collections: static class
UnmodifiableList extends UnmodifiableCollection implements List { private List list;
UnmodifiableList(List list) { super(list); this.list = list; } . . . // alte metode } public
static List unmodifiableList (List list) { return new UnmodifiableList(list); } Exemple
de utilizare a claselor colectie "speciale" : String kw[] ={"if", "else", "do","while",
"for"}; List list = Collections.unmodifiableList (Arrays.asList(kw)); Set s =
Collections.synchronizedSet (new HashSet()); Clase decorator de intrare-iesire
Combinarea derivrii cu delegarea a fost folosit la proiectarea claselor din pachetul
java.io. Exist dou familii de clase paralele : familia claselor flux (Stream) cu
citire-scriere de octeti si familia claselor Reader-Writer, cu citirescriere de
caractere. Numrul de clase instantiabile de I/E este relativ mare deoarece sunt
posibile diverse combinatii ntre suportul fizic al fluxului de date si facilittile oferite
de fluxul respectiv. Toate clasele flux de intrare sunt subtipuri ale tipului
InputStream (Reader) si toate clasele flux de iesire sunt subtipuri ale tipului
OutputStream (Writer). Clasele Reader, Writer si celelalte sunt clase abstracte. 10
Dup suportul fizic al fluxului de date se poate alege ntre: - Fisiere disc :
FileInputStream, FileOutputStream, FileReader, FileWriter s.a. - Vector de octeti sau
de caractere : ByteArrayInputStream, ByteArrayOutputStream CharArrayReader,
CharArrayWriter. - Buffer de siruri (n memorie): StringBufferInputStream,
StringBufferOutputStream. - Canal pentru comunicarea sincronizat ntre fire de
executie : PipedInputStream, PipedOutputStream, PipedReader, PipedWriter. Dup
facilittile oferite avem de ales ntre: - Citire-scriere la nivel de octet sau bloc de
octeti (metode read, write). - Citire-scriere pe octeti dar cu zon buffer:
BufferedInputStream, BufferedReader, BufferedOutputStream, BufferedWriter. Citire-scriere la nivel de linie si pentru numere de diferite tipuri (fr conversie):
DataInputStream, DataOutputStream.. - Citire cu punere napoi n flux a ultimului
octet citit (PushBackInputStream). - Citire nsotit de numerotare automat a liniilor
citite (LineNumberInputStream). Combinarea celor 8 clase surs/destinatie cu
optiunile de prelucrare asociate transferului de date se face prin intermediul claselor
anvelop , care sunt numite si clase filtru de intrare-iesire. Dac s-ar fi utilizat
derivarea pentru obtinerea claselor direct utilizabile atunci ar fi trebuit generate,
prin derivare, combinatii ale celor 8 clase cu cele 4 optiuni, deci 32 de clase
(practic, mai putine, deoarece unele optiuni nu au sens pentru orice flux). Pentru a
folosi mai multe optiuni cu acelasi flux ar fi trebuit mai multe niveluri de derivare si
deci ar fi rezultat un numr si mai mare de clase. Solutia claselor anvelop permite
s se adauge unor clase de baz diverse functii n diferite combinatii. Principalele
clase filtru de intrare-iesire sunt: FilterInputStream, FilterOutputStream si
FilterReader, FilterWriter. Clasele de tip filtru sunt clase intermediare, din care sunt
derivate clase care adaug operatii specifice (de prelucrare): citire de linii de text
de lungime variabil, citire-scriere de numere n format intern, scriere numere cu
conversie de format s.a. O clas anvelop de I/E contine o variabil de tipul abstract
OutputStream sau InputStream, care va fi nlocuit cu o variabil de un tip flux
concret (FileOutputStream, ...), la construirea unui obiect de un tip flux direct
utilizabil. Clasa decorator FilterInputStream este derivat din InputStream si, n
acelasi timp, contine o variabil de tip InputStream: public class FilterInputStream
extends InputStream { protected InputStream in; protected FilterInputStream
(InputStream in) { // constructor (in clasa abstracta) this.in=in; // adresa obiectului
"flux" } // citirea unui octet public int read () throws IOException { return in.read
(); // citirea depinde de tipul fluxului } ... // alte metode } 11 Metoda "read" este o
metod polimorfic, iar selectarea metodei necesare se face n functie de tipul
concret al variabilei "in" (transmis ca argument constructorului). Nu se pot crea
obiecte de tipul FilterInputStream deoarece constructorul clasei este de tip
protected. In schimb, se pot crea obiecte din subclase ale clasei FilterInputStream.
Clasele DataInputStream, BufferedInputStream, LineNumberInputStream si
PushbackInputStream sunt derivate din clasa FilterInputStream si sunt clasele de
prelucrare a datelor citite. Cea mai folosit este clasa DataInputStream care adaug
metodelor de citire de octeti mostenite si metode de citire a tuturor tipurilor
primitive de date: "readInt", "readBoolean", readFloat", "readLine", etc. La crearea
unui obiect de tipul DataInputStream constructorul primeste un argument de tipul
InputStream, sau un tip derivat direct din InputStream, sau de un tip derivat din
FilterInputStream. Pentru a citi linii dintr-un fisier folosind o zon tampon, cu
numerotare de linii vom folosi urmtoarea secvent de instructiuni: public static
void main (String arg[]) throws IOException { FileInputStream fis= new
FileInputStream (arg[0]); BufferedInputStream bis = new BufferedInputStream (fis);
LineNumberInputStream lnis= new LineNumberInputStream (bis); DataInputStream
dis = new DataInputStream (lnis); String linie; while ( (linie=dis.readLine()) != null)
System.out.println (lnis.getLineNumber()+" "+linie); } De obicei nu se mai folosesc
variabile intermediare la construirea unui obiect flux. Exemplu de citire linii , cu
buffer, dintr-un fisier disc: public static void main (String arg[ ]) throws IOException
{ DataInputStream dis = new DataInputStream ( new BufferedInputStream (new
FileInputStream (arg[0]))); String linie; while ( (linie=dis.readLine()) != null)
System.out.println ( linie); } Ordinea n care sunt create obiectele de tip
InputStream este important : ultimul obiect trebuie s fie de tipul
DataInputStream, pentru a putea folosi metode ca "readLine" si altele. Si familia
claselor Reader-Writer foloseste clase decorator: public abstract class FilterReader
extends Reader { protected Reader in; protected FilterReader(Reader in) { super(in);
this.in = in; } public int read() throws IOException { return in.read(); } public int
read(char cbuf[ ], int off, int len) throws IOException { return in.read(cbuf, off,
len); } public void close() throws IOException { 12 in.close(); } } Clasele PrintStream
si PrintWriter adaug claselor filtru metode pentru scriere cu format (cu conversie)
ntr-un flux de iesire, metode cu numele print sau println. Constanta
System.out este de tipul PrintStream si corespunde ecranului, iar constanta
System.in este de un subtip al tipului InputStream si corespunde tastaturii. Clasa
BufferedReader adaug clasei Reader o metod readLine pentru a permite citirea
de linii din fisiere text. Clasele InputStreamReader si OutputStreamWriter sunt clase
adaptor ntre clasele Stream si clasele Reader-Writer. Clasele adaptor extind o
clas Reader (Writer) si contin o variabil de tip InputStream (OutputStream); o
parte din operatiile impuse de superclas sunt realizate prin apelarea operatiilor
pentru variabila flux (prin delegare). Este deci un alt caz de combinare ntre
extindere si agregare. Un obiect InputStreamReader poate fi folosit la fel ca un
obiect Reader pentru citirea de caractere, dar n interior se citesc octeti si se
convertesc octeti la caractere, folosind metode de conversie ale unei clase
convertor. Codul urmtor ilustreaz esenta clasei adaptor, dar sursa clasei prevede
posibilitatea ca un caracter s fie format din doi sau mai multi octeti (conversia se
face pe un bloc de octeti): public class InputStreamReader extends Reader { private
ByteToCharConverter btc; private InputStream in; private
InputStreamReader(InputStream in, ByteToCharConverter btc) { super(in); this.in =
in; this.btc = btc; } public InputStreamReader(InputStream in) { this(in,
ByteToCharConverter.getDefault()); } public int read() throws IOException { int byte
= in.read(); // citire octet char ch = btc.convert(byte); // conversie din byte in char
return ch; // caracter coresp. octetului citit } . . . // alte metode } Exemplu de
utilizare a unei clase adaptor pentru citire de linii de la tastatur : public static void
main (String arg[ ]) throws IOException { BufferedReader stdin = new
clas s poat mosteni functii de la cteva clase (mostenire multipl). Clase incluse
cu nume Clasele incluse cu nume primesc de la compilator un nume compus din
numele clasei exterioare, caracterul $ si numele clasei interioare. Clasele care nu
sunt incluse n alte clase se numesc clase de nivel superior (top-level classes). In
exemplul urmtor o clas pentru un vector ordonat ("SortedArray") contine o
variabil comparator ("cmp"), care poate fi initializat de un constructor al clasei.
Dac se foloseste constructorul fr argumente, atunci variabila comparator
primeste o valoare implicit, ca referint la un obiect comparator dintr-o clas
interioar. Clasa "DefaultComp" nu mai trebuie definit de utilizatori si transmis
din afar, ea este util numai clasei n care este definit (clasa exterioar
"SortedArray"): public class SortedArray extends ArrayList { // clasa interioara
private class DefaultComp implements Comparator { 3 public int compare (Object
e1, Object e2) { Comparable c1=(Comparable)e1; Comparable c2=(Comparable)e2;
return c1.compareTo(c2); } } // alti membri ai clasei SortedArray Comparator
cmp=new DefaultComp(); // comparator implicit public SortedArray () { super();}
public SortedArray (Comparator comp) { super(); cmp=comp; } public boolean add
(Object obj) { int k=indexOf(obj); if (k < 0) k= -k-1; super.add(k, obj); return true; }
public int indexOf (Object obj) { return Collections.binarySearch (this,obj,cmp); } In
exemplul urmtor clasa inclus Entry este folosit numai n definitia clasei
ArrayMap. Clasa inclus Entryimplementeaz o interfat inclus (interfata Entry
inclus n interfata Map este public si deci utilizabil din orice alt clas): public
class ArrayMap extends AbstractMap { // clasa interioara static class Entry
implements Map.Entry { private Object key,val; public Entry (Object k, Object v)
{ key=k; val=v; } public String toString() { return key+"="+val;} public Object
getKey() { return key; } public Object getValue() { return val;} public Object
setValue (Object v) { val=v; return v;} } // alti membri ai clasei ArrayMap private
Vector entries ; // perechi cheie-valoare public ArrayMap (int n) { // constructor
entries = new Vector(n); } public Object put ( Object key, Object value)
{ entries.addElement (new Entry(key,value)); return key; } public Set entrySet ()
{ HashSet set = new HashSet(); 4 for (int i=0;i n) return null; Node p=head.next; //
var. din clasa inclusa for (int i=0;i< 21) { r= ((int)(Math.random() * 2)); 7 switch (r)
{ case 0: p.put(new Integer(k)); k++; break; // activare producator case 1:
System.out.println(c.get()); break; // activare consumator } } Pentru comparatie
urmeaz solutia cu clase interioare. Clasele incluse au fost declarate statice pentru
a putea fi instantiate din metoda static main. class Simulare { static class
Producer { // clasa inclusa public void run() { q.add(new Byte(k)); k++; } } static
class Consumer { // clasa inclusa public void run() { if ( ! q.isEmpty())
System.out.println("Consumator " + " scoate " + q.del()); } } static Queue q ; //
obiectul coada static Producer p ; // proces producator static Consumer c ; // proces
consumator static byte k=1; // simulare procese producator-consumator public
static void main(String[] args) { q= new Queue(); p=new Producer(); c=new
Consumer(); while ( k <=20) switch ((int)(Math.random()*2)) { case 0: p.run();
break; case 1: c.run(); break; } } } Clase interioare anonime Uneori numele unei
clase incluse apare o singur dat, pentru a crea un singur obiect de acest tip. In
plus, clasa inclus implementeaz o interfat sau extinde o alt clas si contine
numai cteva metode scurte. Pentru astfel de situatii se admite definirea ad-hoc de
clase anonime, printr-un bloc care urmeaz operatorului new cu un nume de
interfat sau de clas abstract. Sintaxa definirii unei clase anonime este
urmtoarea: new Interf ( ) { ... // definitie clasa inclusa } ; 8 unde "Interf" este un
nume de interfat (sau de clas abstract sau neabstract) din care este derivat
(implicit) clasa inclus anonim. O astfel de clas nu poate avea un constructor
explicit si deci nu poate primi date la construirea unui obiect din clasa anonim. O
situatie tipic pentru folosirea unei clase anonime definit simultan cu crearea unui
obiect de tipul respectiv este cea n care transmitem unei functii un obiect de un
subtip al interfetei Comparator (adresa unei functii de comparare). Exemplu de
sortare a unei liste de obiecte n ordine descresctoare : Collections.sort (list, new
Comparator( ) { // ordonare descrescatoare public int compare (Object t1, Object t2)
{ // incepe definitia clasei anonime Comparable c1=(Comparable)t1,
c2=(Comparable)t2; return - c1.compareTo(c2); // rezultat invers metodei
compareTo } }); // aici se termina definitia clasei si instructiunea Alt exemplu de
clas comparator definit ca o clas interioar anonim: class SortedArray extends
ArrayList { private Comparator cmp=new Comparator () { // comparator implicit
public int compare (Object e1, Object e2) { Comparable c1=(Comparable)e1;
Comparable c2=(Comparable)e2; return c1.compareTo(c2); } }; public SortedArray
() { super();} public SortedArray (Comparator comp) { super(); cmp=comp; } public
int indexOf (Object obj) { return Collections.binarySearch (this,obj,cmp); } ... // alte
metode } Iat si o alt definitie a metodei iterator din clasa SimpleList, n care
clasa iterator pentru liste este anonim si este definit n expresia care urmeaz lui
new. public Iterator iterator () { return new Iterator() { // definirea clasei anonime
iterator ca o clasa inclusa private Node pos=head.next; public boolean hasNext ()
{ return pos != null; } public Object next() { Object obj =pos.val; pos=pos.next;
return obj; } 9 public void remove () { } }; // sfrsit instructiune return new Iterator
( ) } // sfarsit metoda iterator Prin definirea de clase anonime codul surs devine
mai compact iar definitia clasei apare chiar acolo unde este folosit, dar pot apare
dificultti la ntelegerea codului si erori de utilizare a acoladelor si parantezelor.
Intre clasa interioar si clasa exterioar exist un "cuplaj" foarte strns; acest cuplaj
poate fi un dezavantaj la restructurarea (refactorizarea) unei aplicatii, dar poate fi
exploatat n definirea unor clase de bibliotec (care nu se mai modific). Exemplu:
public abstract class AbstractMap implements Map { public Collection values() { // o
colectie a valorilor din dictionar if (values == null) { // ptr apeluri repetate ale
metodei values values = new AbstractCollection() { // subclasa interioara anonima
public Iterator iterator() { return new Iterator() { // alta clasa interioara anonima
private Iterator i = entrySet().iterator(); public boolean hasNext() { return
i.hasNext(); } public Object next() { return ((Entry)i.next()).getValue(); } public void
remove() { i.remove(); } }; // aici se termina instr. return new Iterator } public int
size() { // din subclasa lui AbstractCollection return AbstractMap.this.size(); // this =
obiect din clasa anonima } public boolean contains(Object v) { return
AbstractMap.this.containsValue(v); } }; // aici se termina instr. values= new }
return values; } } Mostenire multipl prin clase incluse O clas interioar poate
mosteni de la o implementare (nu de la o interfat) n mod independent de
mostenirea clasei exterioare si de mostenirile altor clase incluse. Aceste mosteniri
se pot combina n obiecte ale clasei exterioare, pentru care se pot folosi metode
mostenite de toate clasele incluse. Exemplu: class A { void f1 ()
{ System.out.println ("A.f1"); } } 10 class B { void f2 () { System.out.println ("B.f2");
} } class C { void f3 () { System.out.println ("C.f3"); } } class M extends C { class
AltA extends A { } class AltB extends B { } void f1 () { new AltA().f1(); } void f2 ()
{ new AltB().f2(); } } class X { public static void main (String arg[]) { M m = new
M(); m.f1(); m.f2(); m.f3(); } } Pentru clasa M se pot apela functii mostenite de la
clasele A, B si C la care se pot aduga si alte functii suplimentare. Clasele "AltA" si
"AltB" nu sunt folosite dect n functiile "f1" si "f2", deci am putea folosi clase
incluse anonime astfel: class M extends C { A makeA() { return new A() {}; } void f1
() { makeA().f1(); } B makeB() { return new B() {}; } void f2 () { makeB().f2(); } }
Acelasi efect se poate obtine si prin combinarea derivrii cu compozitia: class M
extends C { A a = new A (); B b = new B (); void f1 () { a.f1();} void f2 () { b.f2();} }
Solutia claselor incluse ofer n plus posibilitatea folosirii unor obiecte de tip M la
apelarea unor functii cu argumente de tip A sau B (si C, superclasa lui M): class X
{ static void useA (A a) { a.f1(); } // functie cu argument de tip A static void useB (B
b) { b.f2(); } // functie cu argument d tip B static void useC (C c) { c.f3(); } // functie
cu argument d tip B public static void main (String arg[ ]) { M m = new M(); m.f1();
m.f2(); m.f3(); useA (m.makeA()); useB (m.makeB()); useC (m); 11 } } Probleme
asociate claselor incluse O clas inclus ntr-un bloc poate folosi variabile locale din
acel bloc, inclusiv argumente formale ale functiei definite prin blocul respectiv. Orice
variabil sau parametru formal folosit ntr-o clas inclus trebuie declarat cu
atributul final, deoarece aceast variabil este copiat n fiecare instant a clasei
incluse si toate aceste copii trebuie s aib aceeasi valoare (nemodificat n cursul
executiei). Exemplul urmtor este o functie similar functiei iterator dintr-o clas
colectie, dar itereaz pe un vector intrinsec de obiecte. In prima variant a acestei
functii se defineste o clas cu nume interioar unui bloc si care foloseste un
argument al functiei care contine definitia clasei. // functie care produce un iterator
pentru vectori intrinseci public static Iterator arrayIterator (final Object a[] ) { //
clasa interioara functiei class AI implements Iterator { int i=0; public boolean
hasNext() { return i < a.length; } public Object next() { return a[i++]; } public void
remove() { } // neimplementata } return new AI(); } Clasa AI poate s fie definit ca
o clas inclus anonim deoarece acest nume nu este folosit n afara functiei
arrayIterator. O clas definit ntr-un bloc nu este membr a clasei ce contine acel
bloc si nu este vizibil din afara blocului. Exemplu de clas anonim definit ntr-un
bloc: // iterator pentru vectori intrinseci public static Iterator arrayIterator (final
Object a[] ) { return new Iterator () { // urmeaza definitia clasei anonime int i=0;
public boolean hasNext() { return i < a.length; } public Object next() { return a[i+
+]; } public void remove() { throw new UnsupportedOperationException();} 12 }; //
aici se termina instr. return } O clas anonim nu poate avea un constructor explicit
si poate fie s extind o alt clas, fie s implementeze o interfat (ca n exemplul
anterior) cu aceeasi sintax. Un nume de interfat poate urma cuvntului cheie new
numai la definirea unei clase anonime care implementeaz acea interfat, iar lista
de argumente trebuie s fie vid. O clas anonim nu poate simultan s extind o
alt clas si s implementeze o interfat, sau s implementeze mai multe interfete.
Numele claselor incluse anonime sunt generate de compilator prin adugarea la
numele clasei exterioare a caracterului $si a unui numr (a cta clas inclus
este). Variabilele locale din clasa exterioar sau din blocul exterior sunt copiate de
compilator n cmpuri private ale clasei interioare, cu nume generate automat de
compilator si transmise ca argumente constructorului clasei interioare. Pentru
functia anterioar compilatorul Java genereaz un cod echivalent de forma
urmtoare: public static Iterator arrayIterator (final Object a[]) { return new
C$1(a); // C este numele clasei ce contine metoda } class C$1 implements Iterator {
private Object val$a[]; int i; OuterClass$1 (Object val$a[]) { // constructor this.val$a
= val$a; i=0; } public boolean hasNext () { return i < val$a.length; } public Object
next() { return val$a[i++]; } public void remove() { throw new
UnsupportedOperationException();} } 10. Clase pentru o interfat grafic
Programarea unei interfete grafice (GUI) Comunicarea dintre un program de
aplicatie si operatorul (beneficiarul) aplicatiei poate folosi ecranul n modul text sau
n modul grafic. Majoritatea aplicatiilor actuale preiau datele de la operatorul uman
n mod interactiv, printr-o interfat grafic, pus la dispozitie de sistemul gazd
(Microsoft Windows, Linux cu X-Windows etc). Interfata grafic cu utilizatorul (GUI =
Graphical User Interface) este mai sigur si mai "prietenoas", folosind att
tastatura ct si mouse-ul pentru introducere sau selectare de date afisate pe ecran.
O interfat grafic simpl const dintr-o singur fereastr ecran a aplicatiei pe care
se plaseaz diverse componente vizuale interactive, numite si controale pentru c
permit operatorului s controleze evolutia programului prin introducerea unor date
sau optiuni de lucru (care, n mod text, se transmit programului prin linia de
comand). Uneori, pe parcursul programului se deschid si alte ferestre, dar exist o
fereastr initiala cu care ncepe aplicatia. Programele cu interfat grafic sunt
controlate prin evenimente produse fie de apsarea unei taste fie de apsarea unui
buton de mouse. Un eveniment des folosit este cel produs de pozitionarea
cursorului pe suprafata unui buton desenat pe ecran si clic pe butonul din stnga
de pe mouse. Tipul evenimentelor este determinat de componenta vizual implicat
dar si de operatia efectuat. De exemplu, ntr-un cmp cu text terminarea unei linii
de text (cu Enter) genereaz un tip de eveniment, iar modificarea unor caractere
din text genereaz un alt tip de eveniment. Limbajul Java permite, fat de alte
limbaje, programarea mai simpl si mai versatil a interfetei grafice prin numrul
mare de clase si de facilitti de care dispune. De exemplu, aspectul (Look and
Feel) componentelor vizuale poate fi ales dintre trei (patru, mai nou) variante
(Windows, MacOS sau Java), indiferent de sistemul de operare gazd. In termenii
specifici Java, componentele vizuale sunt de dou categorii: - Componente
atomice, folosite ca atare si care nu pot contine alte componente (un buton este
un exemplu de component atomic); - Componente container, care grupeaz
mai multe componente atomice si/sau containere. Componentele container sunt si
clasele GUI constructorul este cea mai important functie si uneori singura functie
din clas. Dup executia metodei show (sau setVisible) nu se vor mai crea alte
obiecte JFC (de exemplu, ca rspuns la evenimente generate de operatorul uman),
deoarece ele nu vor mai fi vizibile pe ecran. In schimb, se practic modificarea
continutului afisat n componentele deja existente; de exemplu, metoda setText
din clasele JTextField si JLabel poate modifica textul afisat n astfel de componente
JFC. Dispunerea componentelor ntr-un panou Plasarea componentelor grafice pe un
panou se poate face si prin pozitionare n coordonate absolute de ctre
programator, dar este mult mai simplu s apelm la un obiect de control al asezrii
n panou (Layout Manager), obiect selectat prin metoda setLayout si care
stabileste automat dimensiunile si pozitia fiecrei componente ntr-un panou. Pentru
panoul de continut al ferestrei JFrame este activ implicit BorderLayout, mod care
foloseste un al doilea parametru n metoda add. Dac nu se specific pozitia la
adugare, atunci componenta este centrat n fereastr, iar dac sunt mai multe
componente, atunci ele sunt suprapuse pe centrul ferestrei. Exemplu de plasare a
trei butoane: class DefaultLayout { // exemplu de asezare butoane in fereastra
public static void main (String args[ ]) { JFrame frame = new JFrame(); Container cp
= frame.getContentPane(); cp.add(new JButton( 1 ),BorderLayout.EAST); // cp.add
(...,East) cp.add(new JButton( 2 ),BorderLayout.CENTER); // cp.add (...,Center)
cp.add(new JButton( 3 ), BorderLayout.WEST); // cp.add (..., West)
frame.setVisible(true); } } De observat c obiectul extras cu "getContentPane" are
tipul Container. O solutie mai simpl este alegerea modului de dispunere
FlowLayout, care aseaz componentele una dup alta de la stnga la dreapta si de
sus n jos n functie de dimensiunile componentelor si ale ferestrei principale.
Exemplu: class FlowLayoutDemo { // Asezare butoane in fereastra public static void
main (String args[ ]) { JFrame frame = new JFrame(); Container cp =
frame.getContentPane(); cp.setLayout(new FlowLayout()); // sau new GridLayout()
for (int i=1;i. Exemplu de fisier html necesar pentru executia apletului precedent:
In comanda appletviewer este specificat numele fisierului html si nu apare
direct numele fisierului class. Dimensiunile ferestrei folosite de aplet se dau n
fisierul de tip "html" si nu n codul Java. Este posibil ca anumite programe de
navigare mai vechi (Browser) s nu recunoasc clase JFC si din acest motiv s-a dat
si varianta AWT pentru aplet. De remarcat c o clas care corespunde unui aplet
trebuie s aib atributul public si nu contine o metod main. Clasa aplet
mosteneste si redefineste de obicei metodele init, start, "paint" si alte cteva
metode, apelate de programul gazd la producerea anumitor evenimente. O clas
aplet poate fi transformat ntr-o aplicatie prin adugarea unei functii main n care
se construieste un obiect JFrame, la care se adaug un obiect aplet si se apeleaz
metoda init: public static void main (String args[ ]) { // se adauga la clasa JAplet
JFrame f = new JFrame(); JAplet aplet = new JAplet(); 14 f.getContentPane().add
(aplet); aplet.init(); f.setVisible (true); } O alt posibilitate este crearea unei clase
separate n care se preia codul din aplet si se adaug crearea si afisarea ferestrei
principale a aplicatiei (de tip JFrame). Functia init este nlocuit cu functia main
la trecerea de la un aplet la o aplicatie. Din punct de vedere functional un aplet